mirror of
https://github.com/chatmail/core.git
synced 2026-06-29 19:16:35 +03:00
Compare commits
3 Commits
r10s/repla
...
debug-iroh
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37c771d342 | ||
|
|
72df9504ad | ||
|
|
c68008aa04 |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -125,6 +125,9 @@ jobs:
|
||||
- name: Tests
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
# Workaround for <https://github.com/nextest-rs/nextest/issues/1493>.
|
||||
RUSTUP_WINDOWS_PATH_ADD_BIN: 1
|
||||
run: cargo nextest run --workspace
|
||||
|
||||
- name: Doc-Tests
|
||||
|
||||
95
CHANGELOG.md
95
CHANGELOG.md
@@ -1,97 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [1.140.1] - 2024-06-05
|
||||
|
||||
### Fixes
|
||||
|
||||
- Retry sending MDNs on temporary error.
|
||||
- Set Config::IsChatmail in configure().
|
||||
- Do not miss new messages while expunging the folder.
|
||||
- Log messages with `info!` instead of `println!`.
|
||||
|
||||
### Documentation
|
||||
|
||||
- imap: Document why CLOSE is faster than EXPUNGE.
|
||||
|
||||
### Refactor
|
||||
|
||||
- imap: Make select_folder() accept non-optional folder.
|
||||
- Improve SMTP logs and errors.
|
||||
- Remove unused `select_folder::Error` variants.
|
||||
|
||||
### Tests
|
||||
|
||||
- deltachat-rpc-client: reenable `log_cli`.
|
||||
|
||||
## [1.140.0] - 2024-06-04
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Remove limit on number of email recipients for chatmail clients ([#5598](https://github.com/deltachat/deltachat-core-rust/pull/5598)).
|
||||
- Add config option to enable iroh ([#5607](https://github.com/deltachat/deltachat-core-rust/pull/5607)).
|
||||
- Map `*.wav` to Viewtype::Audio ([#5633](https://github.com/deltachat/deltachat-core-rust/pull/5633)).
|
||||
- Add a db index for reactions by msg_id ([#5507](https://github.com/deltachat/deltachat-core-rust/pull/5507)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- Set Param::Bot for messages on the sender side as well ([#5615](https://github.com/deltachat/deltachat-core-rust/pull/5615)).
|
||||
- AEAP: Remove old peerstate verified_key instead of removing the whole peerstate ([#5535](https://github.com/deltachat/deltachat-core-rust/pull/5535)).
|
||||
- Allow creation of groups by outgoing messages without recipients.
|
||||
- Prefer `Chat-Group-ID` over references for new groups.
|
||||
- Do not fail to send images with wrong extensions.
|
||||
|
||||
### Build system
|
||||
|
||||
- Unpin OpenSSL version and update to OpenSSL 3.3.0.
|
||||
|
||||
### CI
|
||||
|
||||
- Remove cargo-nextest bug workaround.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Add vCard as supported standard.
|
||||
- Create_group() does not find chats, only creates them.
|
||||
- Fix a typo in test_partial_group_consistency().
|
||||
|
||||
### Refactor
|
||||
|
||||
- Factor create_adhoc_group() call out of create_group().
|
||||
- Put duplicate code into `lookup_chat_or_create_adhoc_group`.
|
||||
|
||||
### Tests
|
||||
|
||||
- Fix logging of TestContext created using TestContext::new_alice().
|
||||
- Refactor `test_alias_*` into 8 separate tests.
|
||||
|
||||
## [1.139.6] - 2024-05-25
|
||||
|
||||
### Build system
|
||||
|
||||
- Update `iroh` to the git version.
|
||||
- nix: Add iroh-base output hash.
|
||||
- Upgrade iroh to 0.17.0.
|
||||
|
||||
### Fixes
|
||||
|
||||
- @deltachat/stdio-rpc-server: Do not set RUST_LOG to "info" by default.
|
||||
- Acquire write lock on iroh_channels before checking for subscribe_loop.
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Fix python lint.
|
||||
- cargo-deny: Remove unused entry from deny.toml.
|
||||
|
||||
### Refactor
|
||||
|
||||
- Log IMAP connection type on connection failure.
|
||||
|
||||
### Tests
|
||||
|
||||
- Viewtype::File attachments are sent unchanged and preserve extensions.
|
||||
- deltachat-rpc-client: Add realtime channel tests.
|
||||
- deltachat-rpc-client: Regression test for double gossip subscription.
|
||||
|
||||
## [1.139.5] - 2024-05-23
|
||||
|
||||
### API-Changes
|
||||
@@ -4392,6 +4300,3 @@ https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed
|
||||
[1.139.3]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.2...v1.139.3
|
||||
[1.139.4]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.3...v1.139.4
|
||||
[1.139.5]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.4...v1.139.5
|
||||
[1.139.6]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.5...v1.139.6
|
||||
[1.140.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.6...v1.140.0
|
||||
[1.140.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.140.0...v1.140.1
|
||||
|
||||
241
Cargo.lock
generated
241
Cargo.lock
generated
@@ -174,9 +174,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.86"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
@@ -251,7 +251,7 @@ checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
"synstructure 0.13.1",
|
||||
]
|
||||
|
||||
@@ -274,7 +274,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -302,11 +302,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "2.3.1"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a"
|
||||
checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener 5.2.0",
|
||||
"event-listener-strategy",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
@@ -331,7 +332,7 @@ version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98892ebee4c05fc66757e600a7466f0d9bfcde338f645d64add323789f26cb36"
|
||||
dependencies = [
|
||||
"async-channel 2.3.1",
|
||||
"async-channel 2.2.1",
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"chrono",
|
||||
@@ -393,7 +394,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -932,9 +933,9 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
|
||||
checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
@@ -1212,7 +1213,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1323,12 +1324,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"anyhow",
|
||||
"async-broadcast",
|
||||
"async-channel 2.3.1",
|
||||
"async-channel 2.2.1",
|
||||
"async-imap",
|
||||
"async-native-tls",
|
||||
"async-smtp",
|
||||
@@ -1366,6 +1367,7 @@ dependencies = [
|
||||
"num-traits",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"openssl-src",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"pgp",
|
||||
@@ -1417,10 +1419,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-channel 2.3.1",
|
||||
"async-channel 2.2.1",
|
||||
"axum",
|
||||
"base64 0.22.1",
|
||||
"deltachat",
|
||||
@@ -1442,7 +1444,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-repl"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"anyhow",
|
||||
@@ -1457,7 +1459,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -1481,12 +1483,12 @@ name = "deltachat_derive"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -1574,7 +1576,7 @@ checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1648,7 +1650,7 @@ checksum = "2bba3e9872d7c58ce7ef0fcf1844fcc3e23ef2a58377b50df35dd98e42a5726e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
@@ -1738,7 +1740,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2078,7 +2080,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2091,7 +2093,7 @@ dependencies = [
|
||||
"num-traits",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2111,7 +2113,7 @@ checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2213,9 +2215,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "event-listener-strategy"
|
||||
version = "0.5.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
|
||||
checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291"
|
||||
dependencies = [
|
||||
"event-listener 5.2.0",
|
||||
"pin-project-lite",
|
||||
@@ -2500,7 +2502,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3235,9 +3237,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-base"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1be0b442ed44d20905cf77c673169906c883e05c829e3fb303b131e925139fc"
|
||||
version = "0.16.0"
|
||||
source = "git+https://github.com/n0-computer/iroh?rev=c8690a2c6eb5753c4ec6b7e44db72abf09df3c6e#c8690a2c6eb5753c4ec6b7e44db72abf09df3c6e"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"anyhow",
|
||||
@@ -3276,9 +3277,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-gossip"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "857aa77a0b29283edf99224bd12cd739684c5873e639b85adb38b8d1d777162d"
|
||||
version = "0.16.0"
|
||||
source = "git+https://github.com/n0-computer/iroh?rev=c8690a2c6eb5753c4ec6b7e44db72abf09df3c6e#c8690a2c6eb5753c4ec6b7e44db72abf09df3c6e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -3302,9 +3302,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-metrics"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd54b9cf342b2618efc8d3ff6cdcd083fa5a2cf6cc78bb473bd32e228eabb40e"
|
||||
version = "0.16.0"
|
||||
source = "git+https://github.com/n0-computer/iroh?rev=c8690a2c6eb5753c4ec6b7e44db72abf09df3c6e#c8690a2c6eb5753c4ec6b7e44db72abf09df3c6e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"erased_set",
|
||||
@@ -3323,9 +3322,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-net"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a744000e6c5704479eeb4751eb23b9b8e7e56a0fc484beb8831694fc93e378f"
|
||||
version = "0.16.0"
|
||||
source = "git+https://github.com/n0-computer/iroh?rev=c8690a2c6eb5753c4ec6b7e44db72abf09df3c6e#c8690a2c6eb5753c4ec6b7e44db72abf09df3c6e"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"anyhow",
|
||||
@@ -3370,7 +3368,7 @@ dependencies = [
|
||||
"postcard",
|
||||
"rand 0.8.5",
|
||||
"rand_core 0.6.4",
|
||||
"rcgen 0.12.1",
|
||||
"rcgen 0.11.3",
|
||||
"reqwest 0.12.4",
|
||||
"ring 0.17.8",
|
||||
"rtnetlink",
|
||||
@@ -3400,9 +3398,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-quinn"
|
||||
version = "0.10.5"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "906875956feb75d3d41d708ddaffeb11fdb10cd05f23efbcb17600037e411779"
|
||||
checksum = "b934380145fd5d53a583d01ae9500f4807efe6b0f0fe115c7be4afa2b35db99f"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"iroh-quinn-proto",
|
||||
@@ -3417,13 +3415,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-quinn-proto"
|
||||
version = "0.10.8"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6bf92478805e67f2320459285496e1137edf5171411001a0d4d85f9bbafb792"
|
||||
checksum = "16f2656b322c7f6cf3eb95e632d1c0f2fa546841915b0270da581f918c70c4be"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"rand 0.8.5",
|
||||
"ring 0.17.8",
|
||||
"ring 0.16.20",
|
||||
"rustc-hash",
|
||||
"rustls 0.21.11",
|
||||
"rustls-native-certs",
|
||||
@@ -3435,9 +3433,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-quinn-udp"
|
||||
version = "0.4.2"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc7915b3a31f08ee0bc02f73f4d61a5d5be146a1081ef7f70622a11627fd314"
|
||||
checksum = "6679979a7271c24f9dae9622c0b4a543881508aa3a7396f55dfbaaa56f01c063"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
@@ -3554,9 +3552,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -3980,7 +3978,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4005,9 +4003,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
@@ -4041,7 +4039,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4091,9 +4089,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.64"
|
||||
version = "0.10.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
|
||||
checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"cfg-if",
|
||||
@@ -4112,7 +4110,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4123,18 +4121,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.3.0+3.3.0"
|
||||
version = "300.1.6+3.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eba8804a1c5765b18c4b3f907e6897ebabeedebc9830e1a0046c4a4cf44663e1"
|
||||
checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.102"
|
||||
version = "0.9.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
|
||||
checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -4244,9 +4242,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.3"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||
checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
@@ -4345,7 +4343,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4432,7 +4430,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4565,7 +4563,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4743,9 +4741,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.85"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
||||
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -4770,7 +4768,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5058,6 +5056,18 @@ dependencies = [
|
||||
"yasna",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rcgen"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52c4f3084aa3bc7dfbba4eff4fab2a54db4324965d8872ab933565e6fbd83bc6"
|
||||
dependencies = [
|
||||
"pem 3.0.4",
|
||||
"ring 0.16.20",
|
||||
"time 0.3.34",
|
||||
"yasna",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rcgen"
|
||||
version = "0.12.1"
|
||||
@@ -5584,9 +5594,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.21"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92"
|
||||
checksum = "fc6e7ed6919cb46507fb01ff1654309219f62b4d603822501b0b80d42f6f21ef"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"schemars_derive",
|
||||
@@ -5596,14 +5606,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.21"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e"
|
||||
checksum = "185f2b7aa7e02d418e453790dde16890256bbd2bcd04b7dc5348811052b53f49"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5690,9 +5700,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.203"
|
||||
version = "1.0.200"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||
checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -5717,13 +5727,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.203"
|
||||
version = "1.0.200"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5734,14 +5744,14 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.117"
|
||||
version = "1.0.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -5760,9 +5770,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.6"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
|
||||
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -6092,7 +6102,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"struct_iterable_internal",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6120,7 +6130,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6182,9 +6192,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.66"
|
||||
version = "2.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
||||
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6223,7 +6233,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6333,22 +6343,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.61"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6430,9 +6440,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.38.0"
|
||||
version = "1.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
||||
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -6459,13 +6469,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.3.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6582,34 +6592,35 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.11"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
|
||||
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.13"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba"
|
||||
checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.22.13",
|
||||
"toml_edit 0.22.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.6"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -6627,9 +6638,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.13"
|
||||
version = "0.22.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c"
|
||||
checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
@@ -6686,7 +6697,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7021,7 +7032,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -7055,7 +7066,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -7238,7 +7249,7 @@ checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7249,7 +7260,7 @@ checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7655,7 +7666,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7675,7 +7686,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
24
Cargo.toml
24
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
rust-version = "1.77"
|
||||
@@ -40,7 +40,7 @@ ratelimit = { path = "./deltachat-ratelimit" }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
async-broadcast = "0.7.0"
|
||||
async-channel = "2.3.1"
|
||||
async-channel = "2.2.1"
|
||||
async-imap = { version = "0.9.7", default-features = false, features = ["runtime-tokio"] }
|
||||
async-native-tls = { version = "0.5", default-features = false, features = ["runtime-tokio"] }
|
||||
async-smtp = { version = "0.9", default-features = false, features = ["runtime-tokio"] }
|
||||
@@ -61,8 +61,8 @@ hickory-resolver = "0.24"
|
||||
humansize = "2"
|
||||
image = { version = "0.25.1", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] }
|
||||
iroh_old = { version = "0.4.2", default-features = false, package = "iroh"}
|
||||
iroh-net = "0.17.0"
|
||||
iroh-gossip = { version = "0.17.0", features = ["net"] }
|
||||
iroh-net = { git = "https://github.com/n0-computer/iroh", rev="c8690a2c6eb5753c4ec6b7e44db72abf09df3c6e" }
|
||||
iroh-gossip = { git = "https://github.com/n0-computer/iroh", rev="c8690a2c6eb5753c4ec6b7e44db72abf09df3c6e", features = ["net"] }
|
||||
quinn = "0.10.0"
|
||||
kamadak-exif = "0.5.3"
|
||||
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
|
||||
@@ -95,15 +95,23 @@ strum_macros = "0.26"
|
||||
tagger = "4.3.4"
|
||||
textwrap = "0.16.1"
|
||||
thiserror = "1"
|
||||
tokio = { version = "1.38.0", features = ["fs", "rt-multi-thread", "macros"] }
|
||||
tokio = { version = "1.37.0", features = ["fs", "rt-multi-thread", "macros"] }
|
||||
tokio-io-timeout = "1.2.0"
|
||||
tokio-stream = { version = "0.1.15", features = ["fs"] }
|
||||
tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar
|
||||
tokio-util = "0.7.11"
|
||||
tokio-util = "0.7.9"
|
||||
toml = "0.8"
|
||||
url = "2"
|
||||
uuid = { version = "1", features = ["serde", "v4"] }
|
||||
|
||||
# Pin OpenSSL to 3.1 releases.
|
||||
# OpenSSL 3.2 has a regression tracked at <https://github.com/openssl/openssl/issues/23376>
|
||||
# which results in broken `deltachat-rpc-server` binaries when cross-compiled using Zig toolchain.
|
||||
# See <https://github.com/deltachat/deltachat-core-rust/issues/5206> for Delta Chat issue.
|
||||
# According to <https://www.openssl.org/policies/releasestrat.html>
|
||||
# 3.1 branch will be supported until 2025-03-14.
|
||||
openssl-src = "~300.1"
|
||||
|
||||
[dev-dependencies]
|
||||
ansi_term = "0.12.0"
|
||||
anyhow = { version = "1", features = ["backtrace"] } # Enable `backtrace` feature in tests.
|
||||
@@ -113,7 +121,7 @@ log = "0.4"
|
||||
proptest = { version = "1", default-features = false, features = ["std"] }
|
||||
tempfile = "3"
|
||||
testdir = "0.9.0"
|
||||
tokio = { version = "1.38.0", features = ["parking_lot", "rt-multi-thread", "macros"] }
|
||||
tokio = { version = "1.37.0", features = ["parking_lot", "rt-multi-thread", "macros"] }
|
||||
pretty_assertions = "1.3.0"
|
||||
|
||||
[workspace]
|
||||
@@ -171,4 +179,4 @@ vendored = [
|
||||
"async-native-tls/vendored",
|
||||
"rusqlite/bundled-sqlcipher-vendored-openssl",
|
||||
"reqwest/native-tls-vendored"
|
||||
]
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
description = "Deltachat FFI"
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
@@ -20,7 +20,7 @@ libc = "0.2"
|
||||
human-panic = { version = "2", default-features = false }
|
||||
num-traits = "0.2"
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.38.0", features = ["rt-multi-thread"] }
|
||||
tokio = { version = "1.37.0", features = ["rt-multi-thread"] }
|
||||
anyhow = "1"
|
||||
thiserror = "1"
|
||||
rand = "0.8"
|
||||
|
||||
@@ -523,9 +523,6 @@ char* dc_get_blobdir (const dc_context_t* context);
|
||||
* e.g. `ui.desktop.foo`, `ui.desktop.linux.bar`, `ui.android.foo`, `ui.dc40.bar`, `ui.bot.simplebot.baz`.
|
||||
* These keys go to backups and allow easy per-account settings when using @ref dc_accounts_t,
|
||||
* however, are not handled by the core otherwise.
|
||||
* - `webxdc_realtime_enabled` = Whether the realtime APIs should be enabled.
|
||||
* 0 = WebXDC realtime API is disabled and behaves as noop (default).
|
||||
* 1 = WebXDC realtime API is enabled.
|
||||
*
|
||||
* If you want to retrieve a value, use dc_get_config().
|
||||
*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
description = "DeltaChat JSON-RPC API"
|
||||
edition = "2021"
|
||||
default-run = "deltachat-jsonrpc-server"
|
||||
@@ -17,16 +17,16 @@ anyhow = "1"
|
||||
deltachat = { path = ".." }
|
||||
deltachat-contact-tools = { path = "../deltachat-contact-tools" }
|
||||
num-traits = "0.2"
|
||||
schemars = "0.8.21"
|
||||
schemars = "0.8.19"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tempfile = "3.10.1"
|
||||
log = "0.4"
|
||||
async-channel = { version = "2.3.1" }
|
||||
async-channel = { version = "2.2.1" }
|
||||
futures = { version = "0.3.30" }
|
||||
serde_json = "1"
|
||||
yerpc = { version = "0.5.2", features = ["anyhow_expose", "openrpc"] }
|
||||
typescript-type-def = { version = "0.5.8", features = ["json_value"] }
|
||||
tokio = { version = "1.38.0" }
|
||||
tokio = { version = "1.37.0" }
|
||||
sanitize-filename = "0.5"
|
||||
walkdir = "2.5.0"
|
||||
base64 = "0.22"
|
||||
@@ -36,7 +36,7 @@ axum = { version = "0.7", optional = true, features = ["ws"] }
|
||||
env_logger = { version = "0.11.3", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.38.0", features = ["full", "rt-multi-thread"] }
|
||||
tokio = { version = "1.37.0", features = ["full", "rt-multi-thread"] }
|
||||
|
||||
|
||||
[features]
|
||||
|
||||
@@ -58,5 +58,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "dist/deltachat.d.ts",
|
||||
"version": "1.140.1"
|
||||
"version": "1.139.5"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-repl"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/deltachat/deltachat-core-rust"
|
||||
@@ -13,7 +13,7 @@ dirs = "5"
|
||||
log = "0.4.21"
|
||||
rusqlite = "0.31"
|
||||
rustyline = "14"
|
||||
tokio = { version = "1.38.0", features = ["fs", "rt-multi-thread", "macros"] }
|
||||
tokio = { version = "1.37.0", features = ["fs", "rt-multi-thread", "macros"] }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deltachat-rpc-client"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
description = "Python client for Delta Chat core JSON-RPC interface"
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
|
||||
@@ -177,7 +177,8 @@ class Rpc:
|
||||
account_id = event["contextId"]
|
||||
queue = self.get_queue(account_id)
|
||||
event = event["event"]
|
||||
logging.debug("account_id=%d got an event %s", account_id, event)
|
||||
# logging.debug("account_id=%d got an event %s", account_id, event)
|
||||
print("account_id=%d got an event %s" % (account_id, event))
|
||||
queue.put(event)
|
||||
except Exception:
|
||||
# Log an exception if the event loop dies.
|
||||
|
||||
@@ -29,8 +29,6 @@ def log(msg):
|
||||
|
||||
|
||||
def setup_realtime_webxdc(ac1, ac2, path_to_webxdc):
|
||||
assert ac1.get_config("webxdc_realtime_enabled") == "1"
|
||||
assert ac2.get_config("webxdc_realtime_enabled") == "1"
|
||||
ac1_ac2_chat = ac1.create_chat(ac2)
|
||||
ac2.create_chat(ac1)
|
||||
|
||||
@@ -77,8 +75,6 @@ def wait_receive_realtime_data(msg_data_list):
|
||||
def test_realtime_sequentially(acfactory, path_to_webxdc):
|
||||
"""Test two peers trying to establish connection sequentially."""
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
ac1.set_config("webxdc_realtime_enabled", "1")
|
||||
ac2.set_config("webxdc_realtime_enabled", "1")
|
||||
ac1.create_chat(ac2)
|
||||
ac2.create_chat(ac1)
|
||||
|
||||
@@ -119,8 +115,6 @@ def test_realtime_sequentially(acfactory, path_to_webxdc):
|
||||
def test_realtime_simultaneously(acfactory, path_to_webxdc):
|
||||
"""Test two peers trying to establish connection simultaneously."""
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
ac1.set_config("webxdc_realtime_enabled", "1")
|
||||
ac2.set_config("webxdc_realtime_enabled", "1")
|
||||
|
||||
ac1_webxdc_msg, ac2_webxdc_msg = setup_realtime_webxdc(ac1, ac2, path_to_webxdc)
|
||||
|
||||
@@ -131,8 +125,6 @@ def test_realtime_simultaneously(acfactory, path_to_webxdc):
|
||||
def test_two_parallel_realtime_simultaneously(acfactory, path_to_webxdc):
|
||||
"""Test two peers trying to establish connection simultaneously."""
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
ac1.set_config("webxdc_realtime_enabled", "1")
|
||||
ac2.set_config("webxdc_realtime_enabled", "1")
|
||||
|
||||
ac1_webxdc_msg, ac2_webxdc_msg = setup_realtime_webxdc(ac1, ac2, path_to_webxdc)
|
||||
ac1_webxdc_msg2, ac2_webxdc_msg2 = setup_realtime_webxdc(ac1, ac2, path_to_webxdc)
|
||||
@@ -149,8 +141,6 @@ def test_two_parallel_realtime_simultaneously(acfactory, path_to_webxdc):
|
||||
def test_no_duplicate_messages(acfactory, path_to_webxdc):
|
||||
"""Test that messages are received only once."""
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
ac1.set_config("webxdc_realtime_enabled", "1")
|
||||
ac2.set_config("webxdc_realtime_enabled", "1")
|
||||
|
||||
ac1_ac2_chat = ac1.create_chat(ac2)
|
||||
|
||||
|
||||
@@ -28,5 +28,5 @@ commands =
|
||||
|
||||
[pytest]
|
||||
timeout = 300
|
||||
log_cli = true
|
||||
#log_cli = true
|
||||
log_level = debug
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
description = "DeltaChat JSON-RPC server"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
@@ -18,8 +18,8 @@ futures-lite = "2.3.0"
|
||||
log = "0.4"
|
||||
serde_json = "1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tokio = { version = "1.38.0", features = ["io-std"] }
|
||||
tokio-util = "0.7.11"
|
||||
tokio = { version = "1.37.0", features = ["io-std"] }
|
||||
tokio-util = "0.7.9"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
yerpc = { version = "0.5.2", features = ["anyhow_expose", "openrpc"] }
|
||||
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "index.d.ts",
|
||||
"version": "1.140.1"
|
||||
"version": "1.139.5"
|
||||
}
|
||||
|
||||
@@ -139,4 +139,5 @@ license-files = [
|
||||
github = [
|
||||
"async-email",
|
||||
"deltachat",
|
||||
"n0-computer",
|
||||
]
|
||||
|
||||
@@ -55,5 +55,5 @@
|
||||
"test:mocha": "mocha node/test/test.mjs --growl --reporter=spec --bail --exit"
|
||||
},
|
||||
"types": "node/dist/index.d.ts",
|
||||
"version": "1.140.1"
|
||||
"version": "1.139.5"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deltachat"
|
||||
version = "1.140.1"
|
||||
version = "1.139.5"
|
||||
description = "Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat"
|
||||
readme = "README.rst"
|
||||
requires-python = ">=3.7"
|
||||
|
||||
@@ -551,9 +551,6 @@ class ACFactory:
|
||||
fn = module.__file__
|
||||
|
||||
bot_cfg = self.get_next_liveconfig()
|
||||
# The bot process is run asynchronously, so some messages can arrive before the bot is fully
|
||||
# initialised.
|
||||
bot_cfg["fetch_existing_msgs"] = "1"
|
||||
bot_ac = self.prepare_account_from_liveconfig(bot_cfg)
|
||||
|
||||
# Forget ac as it will be opened by the bot subprocess
|
||||
|
||||
@@ -1 +1 @@
|
||||
2024-06-05
|
||||
2024-05-23
|
||||
56
src/blob.rs
56
src/blob.rs
@@ -3,7 +3,7 @@
|
||||
use core::cmp::max;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::io::{Cursor, Seek};
|
||||
use std::io::Cursor;
|
||||
use std::iter::FusedIterator;
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -12,7 +12,6 @@ use anyhow::{format_err, Context as _, Result};
|
||||
use base64::Engine as _;
|
||||
use futures::StreamExt;
|
||||
use image::codecs::jpeg::JpegEncoder;
|
||||
use image::io::Reader as ImageReader;
|
||||
use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat, Pixel, Rgba};
|
||||
use num_traits::FromPrimitive;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
@@ -427,25 +426,9 @@ impl<'a> BlobObject<'a> {
|
||||
let mut no_exif = false;
|
||||
let no_exif_ref = &mut no_exif;
|
||||
let res = tokio::task::block_in_place(move || {
|
||||
let mut file = std::fs::File::open(self.to_abs_path())?;
|
||||
let (nr_bytes, exif) = image_metadata(&file)?;
|
||||
let (nr_bytes, exif) = self.metadata()?;
|
||||
*no_exif_ref = exif.is_none();
|
||||
// It's strange that BufReader modifies a file position while it takes a non-mut
|
||||
// reference. Ok, just rewind it.
|
||||
file.rewind()?;
|
||||
let imgreader = ImageReader::new(std::io::BufReader::new(&file)).with_guessed_format();
|
||||
let imgreader = match imgreader {
|
||||
Ok(ir) => ir,
|
||||
_ => {
|
||||
file.rewind()?;
|
||||
ImageReader::with_format(
|
||||
std::io::BufReader::new(&file),
|
||||
ImageFormat::from_path(&blob_abs)?,
|
||||
)
|
||||
}
|
||||
};
|
||||
let fmt = imgreader.format().context("No format??")?;
|
||||
let mut img = imgreader.decode().context("image decode failure")?;
|
||||
let mut img = image::open(&blob_abs).context("image decode failure")?;
|
||||
let orientation = exif.as_ref().map(|exif| exif_orientation(exif, context));
|
||||
let mut encoded = Vec::new();
|
||||
let mut changed_name = None;
|
||||
@@ -474,9 +457,10 @@ impl<'a> BlobObject<'a> {
|
||||
let exceeds_max_bytes = nr_bytes > max_bytes as u64;
|
||||
|
||||
let jpeg_quality = 75;
|
||||
let fmt = ImageFormat::from_path(&blob_abs);
|
||||
let ofmt = match fmt {
|
||||
ImageFormat::Png if !exceeds_max_bytes => ImageOutputFormat::Png,
|
||||
ImageFormat::Jpeg => {
|
||||
Ok(ImageFormat::Png) if !exceeds_max_bytes => ImageOutputFormat::Png,
|
||||
Ok(ImageFormat::Jpeg) => {
|
||||
add_white_bg = false;
|
||||
ImageOutputFormat::Jpeg {
|
||||
quality: jpeg_quality,
|
||||
@@ -513,7 +497,7 @@ impl<'a> BlobObject<'a> {
|
||||
img_wh = max(img.width(), img.height());
|
||||
// PNGs and WebPs may be huge because of animation, which is lost by the `image`
|
||||
// crate when recoding, so don't scale them down.
|
||||
if matches!(fmt, ImageFormat::Jpeg) || !encoded.is_empty() {
|
||||
if matches!(fmt, Ok(ImageFormat::Jpeg)) || !encoded.is_empty() {
|
||||
img_wh = img_wh * 2 / 3;
|
||||
}
|
||||
}
|
||||
@@ -554,7 +538,7 @@ impl<'a> BlobObject<'a> {
|
||||
|
||||
if do_scale || exif.is_some() {
|
||||
// The file format is JPEG/PNG now, we may have to change the file extension
|
||||
if !matches!(fmt, ImageFormat::Jpeg)
|
||||
if !matches!(fmt, Ok(ImageFormat::Jpeg))
|
||||
&& matches!(ofmt, ImageOutputFormat::Jpeg { .. })
|
||||
{
|
||||
blob_abs = blob_abs.with_extension("jpg");
|
||||
@@ -591,14 +575,15 @@ impl<'a> BlobObject<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns image file size and Exif.
|
||||
pub fn image_metadata(file: &std::fs::File) -> Result<(u64, Option<exif::Exif>)> {
|
||||
let len = file.metadata()?.len();
|
||||
let mut bufreader = std::io::BufReader::new(file);
|
||||
let exif = exif::Reader::new().read_from_container(&mut bufreader).ok();
|
||||
Ok((len, exif))
|
||||
/// Returns image file size and Exif.
|
||||
pub fn metadata(&self) -> Result<(u64, Option<exif::Exif>)> {
|
||||
let file = std::fs::File::open(self.to_abs_path())?;
|
||||
let len = file.metadata()?.len();
|
||||
let mut bufreader = std::io::BufReader::new(&file);
|
||||
let exif = exif::Reader::new().read_from_container(&mut bufreader).ok();
|
||||
Ok((len, exif))
|
||||
}
|
||||
}
|
||||
|
||||
fn exif_orientation(exif: &exif::Exif, context: &Context) -> i32 {
|
||||
@@ -1321,7 +1306,8 @@ mod tests {
|
||||
.context("failed to write file")?;
|
||||
check_image_size(&file, original_width, original_height);
|
||||
|
||||
let (_, exif) = image_metadata(&std::fs::File::open(&file)?)?;
|
||||
let blob = BlobObject::new_from_path(&alice, &file).await?;
|
||||
let (_, exif) = blob.metadata()?;
|
||||
if has_exif {
|
||||
let exif = exif.unwrap();
|
||||
assert_eq!(exif_orientation(&exif, &alice), orientation);
|
||||
@@ -1356,7 +1342,8 @@ mod tests {
|
||||
assert_eq!(&bytes1, bytes);
|
||||
}
|
||||
|
||||
let (_, exif) = image_metadata(&std::fs::File::open(&file_saved)?)?;
|
||||
let blob = BlobObject::new_from_path(&bob, &file_saved).await?;
|
||||
let (_, exif) = blob.metadata()?;
|
||||
assert!(exif.is_none());
|
||||
|
||||
let img = check_image_size(file_saved, compressed_width, compressed_height);
|
||||
@@ -1392,7 +1379,8 @@ mod tests {
|
||||
.get_blobdir()
|
||||
.join("saved-".to_string() + &bob_msg.get_filename().unwrap());
|
||||
bob_msg.save_file(&bob, &file_saved).await?;
|
||||
let (file_size, _) = image_metadata(&std::fs::File::open(&file_saved)?)?;
|
||||
let blob = BlobObject::new_from_path(&bob, &file_saved).await?;
|
||||
let (file_size, _) = blob.metadata()?;
|
||||
assert_eq!(file_size, bytes.len() as u64);
|
||||
check_image_size(file_saved, width, height);
|
||||
Ok(())
|
||||
|
||||
39
src/chat.rs
39
src/chat.rs
@@ -1932,10 +1932,6 @@ impl Chat {
|
||||
// reset encrypt error state eg. for forwarding
|
||||
msg.param.remove(Param::ErroneousE2ee);
|
||||
|
||||
let is_bot = context.get_config_bool(Config::Bot).await?;
|
||||
msg.param
|
||||
.set_optional(Param::Bot, Some("1").filter(|_| is_bot));
|
||||
|
||||
// Set "In-Reply-To:" to identify the message to which the composed message is a reply.
|
||||
// Set "References:" to identify the "thread" of the conversation.
|
||||
// Both according to [RFC 5322 3.6.4, page 25](https://www.rfc-editor.org/rfc/rfc5322#section-3.6.4).
|
||||
@@ -2636,7 +2632,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
|
||||
{
|
||||
if better_type != Viewtype::Webxdc
|
||||
|| context
|
||||
.prepare_webxdc_file(&blob.to_abs_path(), msg)
|
||||
.ensure_sendable_webxdc_file(&blob.to_abs_path())
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
@@ -2645,7 +2641,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
|
||||
}
|
||||
} else if msg.viewtype == Viewtype::Webxdc {
|
||||
context
|
||||
.prepare_webxdc_file(&blob.to_abs_path(), msg)
|
||||
.ensure_sendable_webxdc_file(&blob.to_abs_path())
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -2913,7 +2909,7 @@ pub(crate) async fn create_send_msg_jobs(context: &Context, msg: &mut Message) -
|
||||
recipients.push(from);
|
||||
}
|
||||
|
||||
// Webxdc integrations are local (shipped with app or added to "Saved Messages") and must not be sent out
|
||||
// Webxdc integrations are messages, however, shipped with main app and must not be sent out
|
||||
if msg.param.get_int(Param::WebxdcIntegration).is_some() {
|
||||
recipients.clear();
|
||||
}
|
||||
@@ -2995,7 +2991,11 @@ pub(crate) async fn create_send_msg_jobs(context: &Context, msg: &mut Message) -
|
||||
|
||||
msg.subject.clone_from(&rendered_msg.subject);
|
||||
msg.update_subject(context).await?;
|
||||
let chunk_size = context.get_max_smtp_rcpt_to().await?;
|
||||
let chunk_size = context
|
||||
.get_configured_provider()
|
||||
.await?
|
||||
.and_then(|provider| provider.opt.max_smtp_rcpt_to)
|
||||
.map_or(constants::DEFAULT_MAX_SMTP_RCPT_TO, usize::from);
|
||||
let trans_fn = |t: &mut rusqlite::Transaction| {
|
||||
let mut row_ids = Vec::<i64>::new();
|
||||
for recipients_chunk in recipients.chunks(chunk_size) {
|
||||
@@ -7528,27 +7528,4 @@ mod tests {
|
||||
assert_eq!(a1_broadcast_chat.get_name(), "Broadcast list 42");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests sending JPEG image with .png extension.
|
||||
///
|
||||
/// This is a regression test, previously sending failed
|
||||
/// because image was passed to PNG decoder
|
||||
/// and it failed to decode image.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_jpeg_with_png_ext() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
let bytes = include_bytes!("../test-data/image/screenshot.jpg");
|
||||
let file = alice.get_blobdir().join("screenshot.png");
|
||||
tokio::fs::write(&file, bytes).await?;
|
||||
let mut msg = Message::new(Viewtype::Image);
|
||||
msg.set_file(file.to_str().unwrap(), None);
|
||||
|
||||
let alice_chat = alice.create_chat(&bob).await;
|
||||
let sent_msg = alice.send_msg(alice_chat.get_id(), &mut msg).await;
|
||||
let _msg = bob.recv_msg(&sent_msg).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,8 +363,8 @@ pub enum Config {
|
||||
/// MsgId of webxdc map integration.
|
||||
WebxdcIntegration,
|
||||
|
||||
/// Enable webxdc realtime features.
|
||||
WebxdcRealtimeEnabled,
|
||||
/// Iroh secret key.
|
||||
IrohSecretKey,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
||||
@@ -459,7 +459,6 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
progress!(ctx, 900);
|
||||
|
||||
if imap_session.is_chatmail() {
|
||||
ctx.set_config(Config::IsChatmail, Some("1")).await?;
|
||||
ctx.set_config(Config::SentboxWatch, None).await?;
|
||||
ctx.set_config(Config::MvboxMove, Some("0")).await?;
|
||||
ctx.set_config(Config::OnlyFetchMvbox, None).await?;
|
||||
|
||||
@@ -498,23 +498,6 @@ impl Context {
|
||||
self.get_config_bool(Config::IsChatmail).await
|
||||
}
|
||||
|
||||
/// Returns maximum number of recipients the provider allows to send a single email to.
|
||||
pub(crate) async fn get_max_smtp_rcpt_to(&self) -> Result<usize> {
|
||||
let is_chatmail = self.is_chatmail().await?;
|
||||
let val = self
|
||||
.get_configured_provider()
|
||||
.await?
|
||||
.and_then(|provider| provider.opt.max_smtp_rcpt_to)
|
||||
.map_or_else(
|
||||
|| match is_chatmail {
|
||||
true => usize::MAX,
|
||||
false => constants::DEFAULT_MAX_SMTP_RCPT_TO,
|
||||
},
|
||||
usize::from,
|
||||
);
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
/// Does a background fetch
|
||||
/// pauses the scheduler and does one imap fetch, then unpauses and returns
|
||||
pub async fn background_fetch(&self) -> Result<()> {
|
||||
@@ -967,12 +950,6 @@ impl Context {
|
||||
.await?
|
||||
.to_string(),
|
||||
);
|
||||
res.insert(
|
||||
"webxdc_realtime_enabled",
|
||||
self.get_config_bool(Config::WebxdcRealtimeEnabled)
|
||||
.await?
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let elapsed = time_elapsed(&self.creation_time);
|
||||
res.insert("uptime", duration_to_str(elapsed));
|
||||
@@ -1679,6 +1656,7 @@ mod tests {
|
||||
"socks5_password",
|
||||
"key_id",
|
||||
"webxdc_integration",
|
||||
"iroh_secret_key",
|
||||
];
|
||||
let t = TestContext::new().await;
|
||||
let info = t.get_info().await.unwrap();
|
||||
|
||||
@@ -184,7 +184,7 @@ impl Session {
|
||||
bail!("Attempt to fetch UID 0");
|
||||
}
|
||||
|
||||
self.select_with_uidvalidity(context, folder).await?;
|
||||
self.select_folder(context, Some(folder)).await?;
|
||||
|
||||
// we are connected, and the folder is selected
|
||||
info!(context, "Downloading message {}/{} fully...", folder, uid);
|
||||
|
||||
17
src/imap.rs
17
src/imap.rs
@@ -313,7 +313,7 @@ impl Imap {
|
||||
if !ratelimit_duration.is_zero() {
|
||||
warn!(
|
||||
context,
|
||||
"IMAP got rate limited, waiting for {} until can connect.",
|
||||
"IMAP got rate limited, waiting for {} until can connect",
|
||||
duration_to_str(ratelimit_duration),
|
||||
);
|
||||
let interrupted = async {
|
||||
@@ -543,16 +543,15 @@ impl Imap {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
session
|
||||
let new_emails = session
|
||||
.select_with_uidvalidity(context, folder)
|
||||
.await
|
||||
.with_context(|| format!("Failed to select folder {folder:?}"))?;
|
||||
|
||||
if !session.new_mail && !fetch_existing_msgs {
|
||||
if !new_emails && !fetch_existing_msgs {
|
||||
info!(context, "No new emails in folder {folder:?}.");
|
||||
return Ok(false);
|
||||
}
|
||||
session.new_mail = false;
|
||||
|
||||
let uid_validity = get_uidvalidity(context, folder).await?;
|
||||
let old_uid_next = get_uid_next(context, folder).await?;
|
||||
@@ -839,7 +838,7 @@ impl Session {
|
||||
// Collect pairs of UID and Message-ID.
|
||||
let mut msgs = BTreeMap::new();
|
||||
|
||||
self.select_with_uidvalidity(context, folder).await?;
|
||||
self.select_folder(context, Some(folder)).await?;
|
||||
|
||||
let mut list = self
|
||||
.uid_fetch("1:*", RFC724MID_UID)
|
||||
@@ -1040,7 +1039,7 @@ impl Session {
|
||||
// MOVE/DELETE operations. This does not result in multiple SELECT commands
|
||||
// being sent because `select_folder()` does nothing if the folder is already
|
||||
// selected.
|
||||
self.select_with_uidvalidity(context, folder).await?;
|
||||
self.select_folder(context, Some(folder)).await?;
|
||||
|
||||
// Empty target folder name means messages should be deleted.
|
||||
if target.is_empty() {
|
||||
@@ -1088,7 +1087,7 @@ impl Session {
|
||||
.await?;
|
||||
|
||||
for (folder, rowid_set, uid_set) in UidGrouper::from(rows) {
|
||||
self.select_with_uidvalidity(context, &folder)
|
||||
self.select_folder(context, Some(&folder))
|
||||
.await
|
||||
.context("failed to select folder")?;
|
||||
|
||||
@@ -1132,7 +1131,7 @@ impl Session {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.select_with_uidvalidity(context, folder)
|
||||
self.select_folder(context, Some(folder))
|
||||
.await
|
||||
.context("failed to select folder")?;
|
||||
|
||||
@@ -1564,7 +1563,7 @@ impl Session {
|
||||
) -> Result<Option<&'a str>> {
|
||||
// Close currently selected folder if needed.
|
||||
// We are going to select folders using low-level EXAMINE operations below.
|
||||
self.maybe_close_folder(context).await?;
|
||||
self.select_folder(context, None).await?;
|
||||
|
||||
for folder in folders {
|
||||
info!(context, "Looking for MVBOX-folder \"{}\"...", &folder);
|
||||
|
||||
@@ -29,13 +29,9 @@ impl Session {
|
||||
) -> Result<Self> {
|
||||
use futures::future::FutureExt;
|
||||
|
||||
self.select_with_uidvalidity(context, folder).await?;
|
||||
self.select_folder(context, Some(folder)).await?;
|
||||
|
||||
if self.server_sent_unsolicited_exists(context)? {
|
||||
self.new_mail = true;
|
||||
}
|
||||
|
||||
if self.new_mail {
|
||||
return Ok(self);
|
||||
}
|
||||
|
||||
@@ -96,9 +92,6 @@ impl Session {
|
||||
session.as_mut().set_read_timeout(Some(IMAP_TIMEOUT));
|
||||
self.inner = session;
|
||||
|
||||
// Fetch mail once we exit IDLE.
|
||||
self.new_mail = true;
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,12 @@ type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("IMAP Connection Lost or no connection established")]
|
||||
ConnectionLost,
|
||||
|
||||
#[error("IMAP Folder name invalid: {0}")]
|
||||
BadFolderName(String),
|
||||
|
||||
#[error("Got a NO response when trying to select {0}, usually this means that it doesn't exist: {1}")]
|
||||
NoFolder(String, String),
|
||||
|
||||
@@ -27,8 +33,7 @@ impl ImapSession {
|
||||
/// Issues a CLOSE command if selected folder needs expunge,
|
||||
/// i.e. if Delta Chat marked a message there as deleted previously.
|
||||
///
|
||||
/// CLOSE is considerably faster than an EXPUNGE
|
||||
/// because no EXPUNGE responses are sent, see
|
||||
/// CLOSE is considerably faster than an EXPUNGE, see
|
||||
/// <https://tools.ietf.org/html/rfc3501#section-6.4.2>
|
||||
pub(super) async fn maybe_close_folder(&mut self, context: &Context) -> anyhow::Result<()> {
|
||||
if let Some(folder) = &self.selected_folder {
|
||||
@@ -39,7 +44,6 @@ impl ImapSession {
|
||||
info!(context, "close/expunge succeeded");
|
||||
self.selected_folder = None;
|
||||
self.selected_folder_needs_expunge = false;
|
||||
self.new_mail = false;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -48,12 +52,18 @@ impl ImapSession {
|
||||
/// Selects a folder, possibly updating uid_validity and, if needed,
|
||||
/// expunging the folder to remove delete-marked messages.
|
||||
/// Returns whether a new folder was selected.
|
||||
async fn select_folder(&mut self, context: &Context, folder: &str) -> Result<NewlySelected> {
|
||||
pub(crate) async fn select_folder(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
folder: Option<&str>,
|
||||
) -> Result<NewlySelected> {
|
||||
// if there is a new folder and the new folder is equal to the selected one, there's nothing to do.
|
||||
// if there is _no_ new folder, we continue as we might want to expunge below.
|
||||
if let Some(selected_folder) = &self.selected_folder {
|
||||
if folder == selected_folder {
|
||||
return Ok(NewlySelected::No);
|
||||
if let Some(folder) = folder {
|
||||
if let Some(selected_folder) = &self.selected_folder {
|
||||
if folder == selected_folder {
|
||||
return Ok(NewlySelected::No);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,26 +71,34 @@ impl ImapSession {
|
||||
self.maybe_close_folder(context).await?;
|
||||
|
||||
// select new folder
|
||||
let res = if self.can_condstore() {
|
||||
self.select_condstore(folder).await
|
||||
if let Some(folder) = folder {
|
||||
let res = if self.can_condstore() {
|
||||
self.select_condstore(folder).await
|
||||
} else {
|
||||
self.select(folder).await
|
||||
};
|
||||
|
||||
// <https://tools.ietf.org/html/rfc3501#section-6.3.1>
|
||||
// says that if the server reports select failure we are in
|
||||
// authenticated (not-select) state.
|
||||
|
||||
match res {
|
||||
Ok(mailbox) => {
|
||||
self.selected_folder = Some(folder.to_string());
|
||||
self.selected_mailbox = Some(mailbox);
|
||||
Ok(NewlySelected::Yes)
|
||||
}
|
||||
Err(async_imap::error::Error::ConnectionLost) => Err(Error::ConnectionLost),
|
||||
Err(async_imap::error::Error::Validate(_)) => {
|
||||
Err(Error::BadFolderName(folder.to_string()))
|
||||
}
|
||||
Err(async_imap::error::Error::No(response)) => {
|
||||
Err(Error::NoFolder(folder.to_string(), response))
|
||||
}
|
||||
Err(err) => Err(Error::Other(err.to_string())),
|
||||
}
|
||||
} else {
|
||||
self.select(folder).await
|
||||
};
|
||||
|
||||
// <https://tools.ietf.org/html/rfc3501#section-6.3.1>
|
||||
// says that if the server reports select failure we are in
|
||||
// authenticated (not-select) state.
|
||||
|
||||
match res {
|
||||
Ok(mailbox) => {
|
||||
self.selected_folder = Some(folder.to_string());
|
||||
self.selected_mailbox = Some(mailbox);
|
||||
Ok(NewlySelected::Yes)
|
||||
}
|
||||
Err(async_imap::error::Error::No(response)) => {
|
||||
Err(Error::NoFolder(folder.to_string(), response))
|
||||
}
|
||||
Err(err) => Err(Error::Other(err.to_string())),
|
||||
Ok(NewlySelected::No)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +108,7 @@ impl ImapSession {
|
||||
context: &Context,
|
||||
folder: &str,
|
||||
) -> anyhow::Result<NewlySelected> {
|
||||
match self.select_folder(context, folder).await {
|
||||
match self.select_folder(context, Some(folder)).await {
|
||||
Ok(newly_selected) => Ok(newly_selected),
|
||||
Err(err) => match err {
|
||||
Error::NoFolder(..) => {
|
||||
@@ -100,7 +118,7 @@ impl ImapSession {
|
||||
info!(context, "Couldn't select folder, then create() failed: {err:#}.");
|
||||
// Need to recheck, could have been created in parallel.
|
||||
}
|
||||
let select_res = self.select_folder(context, folder).await.with_context(|| format!("failed to select newely created folder {folder}"));
|
||||
let select_res = self.select_folder(context, Some(folder)).await.with_context(|| format!("failed to select newely created folder {folder}"));
|
||||
if select_res.is_err() {
|
||||
create_res?;
|
||||
}
|
||||
@@ -116,14 +134,13 @@ impl ImapSession {
|
||||
/// When selecting a folder for the first time, sets the uid_next to the current
|
||||
/// mailbox.uid_next so that no old emails are fetched.
|
||||
///
|
||||
/// Updates `self.new_mail` if folder was previously unselected
|
||||
/// and new mails are detected after selecting,
|
||||
/// i.e. UIDNEXT advanced while the folder was closed.
|
||||
/// Returns Result<new_emails> (i.e. whether new emails arrived),
|
||||
/// if in doubt, returns new_emails=true so emails are fetched.
|
||||
pub(crate) async fn select_with_uidvalidity(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
folder: &str,
|
||||
) -> Result<()> {
|
||||
) -> Result<bool> {
|
||||
let newly_selected = self
|
||||
.select_or_create_folder(context, folder)
|
||||
.await
|
||||
@@ -180,26 +197,28 @@ impl ImapSession {
|
||||
mailbox.uid_next = new_uid_next;
|
||||
|
||||
if new_uid_validity == old_uid_validity {
|
||||
if newly_selected == NewlySelected::Yes {
|
||||
if let Some(new_uid_next) = new_uid_next {
|
||||
if new_uid_next < old_uid_next {
|
||||
warn!(
|
||||
context,
|
||||
"The server illegally decreased the uid_next of folder {folder:?} from {old_uid_next} to {new_uid_next} without changing validity ({new_uid_validity}), resyncing UIDs...",
|
||||
);
|
||||
set_uid_next(context, folder, new_uid_next).await?;
|
||||
context.schedule_resync().await?;
|
||||
}
|
||||
|
||||
// If UIDNEXT changed, there are new emails.
|
||||
self.new_mail |= new_uid_next != old_uid_next;
|
||||
} else {
|
||||
warn!(context, "Folder {folder} was just selected but we failed to determine UIDNEXT, assume that it has new mail.");
|
||||
self.new_mail = true;
|
||||
let new_emails = if newly_selected == NewlySelected::No {
|
||||
// The folder was not newly selected i.e. no SELECT command was run. This means that mailbox.uid_next
|
||||
// was not updated and may contain an incorrect value. So, just return true so that
|
||||
// the caller tries to fetch new messages (we could of course run a SELECT command now, but trying to fetch
|
||||
// new messages is only one command, just as a SELECT command)
|
||||
true
|
||||
} else if let Some(new_uid_next) = new_uid_next {
|
||||
if new_uid_next < old_uid_next {
|
||||
warn!(
|
||||
context,
|
||||
"The server illegally decreased the uid_next of folder {folder:?} from {old_uid_next} to {new_uid_next} without changing validity ({new_uid_validity}), resyncing UIDs...",
|
||||
);
|
||||
set_uid_next(context, folder, new_uid_next).await?;
|
||||
context.schedule_resync().await?;
|
||||
}
|
||||
}
|
||||
new_uid_next != old_uid_next // If UIDNEXT changed, there are new emails
|
||||
} else {
|
||||
// We have no UIDNEXT and if in doubt, return true.
|
||||
true
|
||||
};
|
||||
|
||||
return Ok(());
|
||||
return Ok(new_emails);
|
||||
}
|
||||
|
||||
// UIDVALIDITY is modified, reset highest seen MODSEQ.
|
||||
@@ -210,7 +229,6 @@ impl ImapSession {
|
||||
let new_uid_next = new_uid_next.unwrap_or_default();
|
||||
set_uid_next(context, folder, new_uid_next).await?;
|
||||
set_uidvalidity(context, folder, new_uid_validity).await?;
|
||||
self.new_mail = true;
|
||||
|
||||
// Collect garbage entries in `imap` table.
|
||||
context
|
||||
@@ -233,7 +251,7 @@ impl ImapSession {
|
||||
old_uid_next,
|
||||
old_uid_validity,
|
||||
);
|
||||
Ok(())
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,11 +40,6 @@ pub(crate) struct Session {
|
||||
pub selected_mailbox: Option<Mailbox>,
|
||||
|
||||
pub selected_folder_needs_expunge: bool,
|
||||
|
||||
/// True if currently selected folder has new messages.
|
||||
///
|
||||
/// Should be false if no folder is currently selected.
|
||||
pub new_mail: bool,
|
||||
}
|
||||
|
||||
impl Deref for Session {
|
||||
@@ -72,7 +67,6 @@ impl Session {
|
||||
selected_folder: None,
|
||||
selected_mailbox: None,
|
||||
selected_folder_needs_expunge: false,
|
||||
new_mail: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1433,7 +1433,7 @@ pub(crate) fn guess_msgtype_from_suffix(path: &Path) -> Option<(Viewtype, &str)>
|
||||
"txt" => (Viewtype::File, "text/plain"),
|
||||
"vcard" => (Viewtype::Vcard, "text/vcard"),
|
||||
"vcf" => (Viewtype::Vcard, "text/vcard"),
|
||||
"wav" => (Viewtype::Audio, "audio/wav"),
|
||||
"wav" => (Viewtype::File, "audio/wav"),
|
||||
"weba" => (Viewtype::File, "audio/webm"),
|
||||
"webm" => (Viewtype::Video, "video/webm"),
|
||||
"webp" => (Viewtype::Image, "image/webp"), // iOS via SDWebImage, Android since 4.0
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use email::Header;
|
||||
use futures_lite::StreamExt;
|
||||
use iroh_gossip::net::{Gossip, JoinTopicFut, GOSSIP_ALPN};
|
||||
use iroh_gossip::proto::{Event as IrohEvent, TopicId};
|
||||
use iroh_net::relay::{RelayMap, RelayUrl};
|
||||
@@ -37,7 +38,6 @@ use tokio::task::JoinHandle;
|
||||
use url::Url;
|
||||
|
||||
use crate::chat::send_msg;
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::headerdef::HeaderDef;
|
||||
use crate::message::{Message, MsgId, Viewtype};
|
||||
@@ -158,10 +158,6 @@ impl Iroh {
|
||||
|
||||
self.gossip.broadcast(topic, data.into()).await?;
|
||||
|
||||
if env::var("REALTIME_DEBUG").is_ok() {
|
||||
info!(ctx, "Sent realtime data");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -247,6 +243,15 @@ impl Context {
|
||||
// Shuts down on deltachat shutdown
|
||||
tokio::spawn(endpoint_loop(context, endpoint.clone(), gossip.clone()));
|
||||
|
||||
let endp = endpoint.clone();
|
||||
let gsp = gossip.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut stream = endp.local_endpoints();
|
||||
while let Some(endpoints) = stream.next().await {
|
||||
gsp.update_endpoints(&endpoints)?;
|
||||
}
|
||||
anyhow::Ok(())
|
||||
});
|
||||
Ok(Iroh {
|
||||
endpoint,
|
||||
gossip,
|
||||
@@ -339,10 +344,6 @@ pub async fn send_webxdc_realtime_advertisement(
|
||||
ctx: &Context,
|
||||
msg_id: MsgId,
|
||||
) -> Result<Option<JoinTopicFut>> {
|
||||
if !ctx.get_config_bool(Config::WebxdcRealtimeEnabled).await? {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let iroh = ctx.get_or_try_init_peer_channel().await?;
|
||||
let conn = iroh.join_and_subscribe_gossip(ctx, msg_id).await?;
|
||||
|
||||
@@ -352,16 +353,14 @@ pub async fn send_webxdc_realtime_advertisement(
|
||||
msg.param.set_cmd(SystemMessage::IrohNodeAddr);
|
||||
msg.in_reply_to = Some(webxdc.rfc724_mid.clone());
|
||||
send_msg(ctx, webxdc.chat_id, &mut msg).await?;
|
||||
info!(ctx, "IROH_REALTIME: Sent realtime advertisement");
|
||||
if env::var("REALTIME_DEBUG").is_ok() {
|
||||
info!(ctx, "IROH_REALTIME: Sent realtime advertisement");
|
||||
}
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
/// Send realtime data to other peers using iroh.
|
||||
/// Send realtime data to the gossip swarm.
|
||||
pub async fn send_webxdc_realtime_data(ctx: &Context, msg_id: MsgId, data: Vec<u8>) -> Result<()> {
|
||||
if !ctx.get_config_bool(Config::WebxdcRealtimeEnabled).await? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let iroh = ctx.get_or_try_init_peer_channel().await?;
|
||||
iroh.send_webxdc_realtime_data(ctx, msg_id, data).await?;
|
||||
Ok(())
|
||||
@@ -369,13 +368,10 @@ pub async fn send_webxdc_realtime_data(ctx: &Context, msg_id: MsgId, data: Vec<u
|
||||
|
||||
/// Leave the gossip of the webxdc with given [MsgId].
|
||||
pub async fn leave_webxdc_realtime(ctx: &Context, msg_id: MsgId) -> Result<()> {
|
||||
if !ctx.get_config_bool(Config::WebxdcRealtimeEnabled).await? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let iroh = ctx.get_or_try_init_peer_channel().await?;
|
||||
iroh.leave_realtime(get_iroh_topic_for_msg(ctx, msg_id).await?)
|
||||
.await?;
|
||||
|
||||
info!(ctx, "IROH_REALTIME: Left gossip for message {msg_id}");
|
||||
|
||||
Ok(())
|
||||
@@ -443,11 +439,12 @@ async fn subscribe_loop(
|
||||
let event = stream.recv().await?;
|
||||
match event {
|
||||
IrohEvent::NeighborUp(node) => {
|
||||
info!(context, "IROH_REALTIME: NeighborUp: {}", node.to_string());
|
||||
iroh_add_peer_for_topic(context, msg_id, topic, node, None).await?;
|
||||
}
|
||||
IrohEvent::Received(event) => {
|
||||
info!(context, "IROH_REALTIME: Received realtime data");
|
||||
if env::var("REALTIME_DEBUG").is_ok() {
|
||||
info!(context, "IROH_REALTIME: Received realtime data");
|
||||
}
|
||||
context.emit_event(EventType::WebxdcRealtimeData {
|
||||
msg_id,
|
||||
data: event
|
||||
@@ -478,17 +475,6 @@ mod tests {
|
||||
let alice = &mut tcm.alice().await;
|
||||
let bob = &mut tcm.bob().await;
|
||||
|
||||
bob.ctx
|
||||
.set_config_bool(Config::WebxdcRealtimeEnabled, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
alice
|
||||
.ctx
|
||||
.set_config_bool(Config::WebxdcRealtimeEnabled, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Alice sends webxdc to bob
|
||||
let alice_chat = alice.create_chat(bob).await;
|
||||
let mut instance = Message::new(Viewtype::File);
|
||||
@@ -620,21 +606,6 @@ mod tests {
|
||||
let alice = &mut tcm.alice().await;
|
||||
let bob = &mut tcm.bob().await;
|
||||
|
||||
bob.ctx
|
||||
.set_config_bool(Config::WebxdcRealtimeEnabled, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
alice
|
||||
.ctx
|
||||
.set_config_bool(Config::WebxdcRealtimeEnabled, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(alice
|
||||
.get_config_bool(Config::WebxdcRealtimeEnabled)
|
||||
.await
|
||||
.unwrap());
|
||||
// Alice sends webxdc to bob
|
||||
let alice_chat = alice.create_chat(bob).await;
|
||||
let mut instance = Message::new(Viewtype::File);
|
||||
@@ -770,17 +741,6 @@ mod tests {
|
||||
let alice = &mut tcm.alice().await;
|
||||
let bob = &mut tcm.bob().await;
|
||||
|
||||
bob.ctx
|
||||
.set_config_bool(Config::WebxdcRealtimeEnabled, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
alice
|
||||
.ctx
|
||||
.set_config_bool(Config::WebxdcRealtimeEnabled, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Alice sends webxdc to bob
|
||||
let alice_chat = alice.create_chat(bob).await;
|
||||
let mut instance = Message::new(Viewtype::File);
|
||||
@@ -843,29 +803,4 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_peer_channels_disabled() {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &mut tcm.alice().await;
|
||||
|
||||
// creates iroh endpoint as side effect
|
||||
send_webxdc_realtime_advertisement(alice, MsgId::new(1))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(alice.ctx.iroh.get().is_none());
|
||||
|
||||
// creates iroh endpoint as side effect
|
||||
send_webxdc_realtime_data(alice, MsgId::new(1), vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(alice.ctx.iroh.get().is_none());
|
||||
|
||||
// creates iroh endpoint as side effect
|
||||
leave_webxdc_realtime(alice, MsgId::new(1)).await.unwrap();
|
||||
|
||||
assert!(alice.ctx.iroh.get().is_none())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,18 +538,10 @@ impl Peerstate {
|
||||
if let Some(old_addr) = old_addr {
|
||||
// We are doing an AEAP transition to the new address and the SQL INSERT below will
|
||||
// save the existing peerstate as belonging to this new address. We now need to
|
||||
// "unverify" the peerstate that belongs to the current address in case if the
|
||||
// contact later wants to move back to the current address. Otherwise the old entry
|
||||
// will be just found and updated instead of doing AEAP. We can't just delete the
|
||||
// existing peerstate as this would break encryption to it. This is critical for
|
||||
// non-verified groups -- if we can't encrypt to the old address, we can't securely
|
||||
// remove it from the group (to add the new one instead).
|
||||
t.execute(
|
||||
"UPDATE acpeerstates \
|
||||
SET verified_key=NULL, verified_key_fingerprint='', verifier='' \
|
||||
WHERE addr=?",
|
||||
(old_addr,),
|
||||
)?;
|
||||
// delete the peerstate that belongs to the current address in case if the contact
|
||||
// later wants to move back to the current address. Otherwise the old entry will be
|
||||
// just found and updated instead of doing AEAP.
|
||||
t.execute("DELETE FROM acpeerstates WHERE addr=?", (old_addr,))?;
|
||||
}
|
||||
t.execute(
|
||||
"INSERT INTO acpeerstates (
|
||||
|
||||
@@ -782,6 +782,29 @@ async fn add_parts(
|
||||
info!(context, "Message is an MDN (TRASH).",);
|
||||
}
|
||||
|
||||
// Try to assign to a chat based on Chat-Group-ID.
|
||||
if chat_id.is_none() {
|
||||
if let Some(grpid) = mime_parser.get_chat_group_id() {
|
||||
if let Some((id, _protected, blocked)) =
|
||||
chat::get_chat_id_by_grpid(context, grpid).await?
|
||||
{
|
||||
chat_id = Some(id);
|
||||
chat_id_blocked = blocked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if chat_id.is_none() {
|
||||
// try to assign to a chat based on In-Reply-To/References:
|
||||
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) =
|
||||
lookup_chat_by_reply(context, mime_parser, &parent, to_ids, from_id).await?
|
||||
{
|
||||
chat_id = Some(new_chat_id);
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
}
|
||||
}
|
||||
|
||||
// signals whether the current user is a bot
|
||||
let is_bot = context.get_config_bool(Config::Bot).await?;
|
||||
|
||||
@@ -811,44 +834,17 @@ async fn add_parts(
|
||||
create_blocked_default
|
||||
};
|
||||
|
||||
// Try to assign to a chat based on Chat-Group-ID.
|
||||
if chat_id.is_none() {
|
||||
if let Some(grpid) = mime_parser.get_chat_group_id().map(|s| s.to_string()) {
|
||||
if let Some((id, _protected, blocked)) =
|
||||
chat::get_chat_id_by_grpid(context, &grpid).await?
|
||||
{
|
||||
chat_id = Some(id);
|
||||
chat_id_blocked = blocked;
|
||||
} else if allow_creation || test_normal_chat.is_some() {
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) = create_group(
|
||||
context,
|
||||
mime_parser,
|
||||
is_partial_download.is_some(),
|
||||
create_blocked,
|
||||
from_id,
|
||||
to_ids,
|
||||
&verified_encryption,
|
||||
&grpid,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
chat_id = Some(new_chat_id);
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if chat_id.is_none() && (allow_creation || test_normal_chat.is_some()) {
|
||||
// try to create a group
|
||||
|
||||
if chat_id.is_none() {
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) = lookup_chat_or_create_adhoc_group(
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) = create_group(
|
||||
context,
|
||||
mime_parser,
|
||||
&parent,
|
||||
to_ids,
|
||||
from_id,
|
||||
allow_creation || test_normal_chat.is_some(),
|
||||
create_blocked,
|
||||
is_partial_download.is_some(),
|
||||
create_blocked,
|
||||
from_id,
|
||||
to_ids,
|
||||
&verified_encryption,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
@@ -1050,32 +1046,27 @@ async fn add_parts(
|
||||
|
||||
// Try to assign to a chat based on Chat-Group-ID.
|
||||
if chat_id.is_none() {
|
||||
if let Some(grpid) = mime_parser.get_chat_group_id().map(|s| s.to_string()) {
|
||||
if let Some(grpid) = mime_parser.get_chat_group_id() {
|
||||
if let Some((id, _protected, blocked)) =
|
||||
chat::get_chat_id_by_grpid(context, &grpid).await?
|
||||
chat::get_chat_id_by_grpid(context, grpid).await?
|
||||
{
|
||||
chat_id = Some(id);
|
||||
chat_id_blocked = blocked;
|
||||
} else if allow_creation {
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) = create_group(
|
||||
context,
|
||||
mime_parser,
|
||||
is_partial_download.is_some(),
|
||||
Blocked::Not,
|
||||
from_id,
|
||||
to_ids,
|
||||
&verified_encryption,
|
||||
&grpid,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
chat_id = Some(new_chat_id);
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if chat_id.is_none() {
|
||||
// try to assign to a chat based on In-Reply-To/References:
|
||||
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) =
|
||||
lookup_chat_by_reply(context, mime_parser, &parent, to_ids, from_id).await?
|
||||
{
|
||||
chat_id = Some(new_chat_id);
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
}
|
||||
}
|
||||
|
||||
if mime_parser.decrypting_failed && !fetching_existing_messages {
|
||||
if chat_id.is_none() {
|
||||
chat_id = Some(DC_CHAT_ID_TRASH);
|
||||
@@ -1107,25 +1098,23 @@ async fn add_parts(
|
||||
}
|
||||
}
|
||||
|
||||
if chat_id.is_none() {
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) = lookup_chat_or_create_adhoc_group(
|
||||
context,
|
||||
mime_parser,
|
||||
&parent,
|
||||
to_ids,
|
||||
from_id,
|
||||
allow_creation,
|
||||
Blocked::Not,
|
||||
is_partial_download.is_some(),
|
||||
)
|
||||
.await?
|
||||
{
|
||||
chat_id = Some(new_chat_id);
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
}
|
||||
}
|
||||
|
||||
if !to_ids.is_empty() {
|
||||
if chat_id.is_none() && allow_creation {
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) = create_group(
|
||||
context,
|
||||
mime_parser,
|
||||
is_partial_download.is_some(),
|
||||
Blocked::Not,
|
||||
from_id,
|
||||
to_ids,
|
||||
&verified_encryption,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
chat_id = Some(new_chat_id);
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
}
|
||||
}
|
||||
if chat_id.is_none() && allow_creation {
|
||||
let to_contact = Contact::get_by_id(context, to_id).await?;
|
||||
if let Some(list_id) = to_contact.param.get(Param::ListId) {
|
||||
@@ -1790,44 +1779,6 @@ async fn lookup_chat_by_reply(
|
||||
Ok(Some((parent_chat.id, parent_chat.blocked)))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn lookup_chat_or_create_adhoc_group(
|
||||
context: &Context,
|
||||
mime_parser: &MimeMessage,
|
||||
parent: &Option<Message>,
|
||||
to_ids: &[ContactId],
|
||||
from_id: ContactId,
|
||||
allow_creation: bool,
|
||||
create_blocked: Blocked,
|
||||
is_partial_download: bool,
|
||||
) -> Result<Option<(ChatId, Blocked)>> {
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) =
|
||||
// Try to assign to a chat based on In-Reply-To/References.
|
||||
lookup_chat_by_reply(context, mime_parser, parent, to_ids, from_id).await?
|
||||
{
|
||||
Ok(Some((new_chat_id, new_chat_id_blocked)))
|
||||
} else if allow_creation {
|
||||
// Try to create an ad hoc group.
|
||||
if let Some(new_chat_id) = create_adhoc_group(
|
||||
context,
|
||||
mime_parser,
|
||||
create_blocked,
|
||||
from_id,
|
||||
to_ids,
|
||||
is_partial_download,
|
||||
)
|
||||
.await
|
||||
.context("Could not create ad hoc group")?
|
||||
{
|
||||
Ok(Some((new_chat_id, create_blocked)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// If this method returns true, the message shall be assigned to the 1:1 chat with the sender.
|
||||
/// If it returns false, it shall be assigned to the parent chat.
|
||||
async fn is_probably_private_reply(
|
||||
@@ -1869,8 +1820,7 @@ async fn is_probably_private_reply(
|
||||
/// chat with this ID. If there is no group-id and there are more
|
||||
/// than two members, a new ad hoc group is created.
|
||||
///
|
||||
/// On success the function returns the created (chat_id, chat_blocked) tuple.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
/// On success the function returns the found/created (chat_id, chat_blocked) tuple.
|
||||
async fn create_group(
|
||||
context: &Context,
|
||||
mime_parser: &mut MimeMessage,
|
||||
@@ -1879,8 +1829,34 @@ async fn create_group(
|
||||
from_id: ContactId,
|
||||
to_ids: &[ContactId],
|
||||
verified_encryption: &VerifiedEncryption,
|
||||
grpid: &str,
|
||||
) -> Result<Option<(ChatId, Blocked)>> {
|
||||
let Some(grpid) = mime_parser.get_chat_group_id().map(|s| s.to_string()) else {
|
||||
if is_partial_download {
|
||||
// Partial download may be an encrypted message with protected Subject header.
|
||||
//
|
||||
// We do not want to create a group with "..." or "Encrypted message" as a subject.
|
||||
info!(
|
||||
context,
|
||||
"Ad-hoc group cannot be created from partial download."
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut member_ids: Vec<ContactId> = to_ids.to_vec();
|
||||
if !member_ids.contains(&(from_id)) {
|
||||
member_ids.push(from_id);
|
||||
}
|
||||
if !member_ids.contains(&(ContactId::SELF)) {
|
||||
member_ids.push(ContactId::SELF);
|
||||
}
|
||||
|
||||
let res = create_adhoc_group(context, mime_parser, create_blocked, &member_ids)
|
||||
.await
|
||||
.context("could not create ad hoc group")?
|
||||
.map(|chat_id| (chat_id, create_blocked));
|
||||
return Ok(res);
|
||||
};
|
||||
|
||||
let mut chat_id = None;
|
||||
let mut chat_id_blocked = Default::default();
|
||||
|
||||
@@ -1923,7 +1899,7 @@ async fn create_group(
|
||||
// otherwise, a pending "quit" message may pop up
|
||||
&& mime_parser.get_header(HeaderDef::ChatGroupMemberRemoved).is_none()
|
||||
// re-create explicitly left groups only if ourself is re-added
|
||||
&& (!chat::is_group_explicitly_left(context, grpid).await?
|
||||
&& (!chat::is_group_explicitly_left(context, &grpid).await?
|
||||
|| self_explicitly_added(context, &mime_parser).await?)
|
||||
{
|
||||
// Group does not exist but should be created.
|
||||
@@ -1936,7 +1912,7 @@ async fn create_group(
|
||||
let new_chat_id = ChatId::create_multiuser_record(
|
||||
context,
|
||||
Chattype::Group,
|
||||
grpid,
|
||||
&grpid,
|
||||
grpname,
|
||||
create_blocked,
|
||||
create_protected,
|
||||
@@ -2494,29 +2470,8 @@ async fn create_adhoc_group(
|
||||
context: &Context,
|
||||
mime_parser: &MimeMessage,
|
||||
create_blocked: Blocked,
|
||||
from_id: ContactId,
|
||||
to_ids: &[ContactId],
|
||||
is_partial_download: bool,
|
||||
member_ids: &[ContactId],
|
||||
) -> Result<Option<ChatId>> {
|
||||
if is_partial_download {
|
||||
// Partial download may be an encrypted message with protected Subject header.
|
||||
//
|
||||
// We do not want to create a group with "..." or "Encrypted message" as a subject.
|
||||
info!(
|
||||
context,
|
||||
"Ad-hoc group cannot be created from partial download."
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut member_ids: Vec<ContactId> = to_ids.to_vec();
|
||||
if !member_ids.contains(&(from_id)) {
|
||||
member_ids.push(from_id);
|
||||
}
|
||||
if !member_ids.contains(&(ContactId::SELF)) {
|
||||
member_ids.push(ContactId::SELF);
|
||||
}
|
||||
|
||||
if mime_parser.is_mailinglist_message() {
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -2562,7 +2517,7 @@ async fn create_adhoc_group(
|
||||
context,
|
||||
"Created ad-hoc group id={new_chat_id}, name={grpname:?}."
|
||||
);
|
||||
chat::add_to_chat_contacts_table(context, new_chat_id, &member_ids).await?;
|
||||
chat::add_to_chat_contacts_table(context, new_chat_id, member_ids).await?;
|
||||
|
||||
context.emit_event(EventType::ChatModified(new_chat_id));
|
||||
chatlist_events::emit_chatlist_changed(context);
|
||||
|
||||
@@ -1820,31 +1820,26 @@ async fn test_save_mime_headers_on() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: bool) {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
|
||||
async fn create_test_alias(chat_request: bool, group_request: bool) -> (TestContext, TestContext) {
|
||||
// Claire, a customer, sends a support request
|
||||
// to the alias address <support@example.org>.
|
||||
// If `chat_request` is true, Claire is using Delta Chat,
|
||||
// otherwise Claire sends the request from a classic MUA.
|
||||
// to the alias address <support@example.org> from a classic MUA.
|
||||
// The alias expands to the supporters Alice and Bob.
|
||||
// Check that Alice receives the message in a group chat.
|
||||
let claire_request = if group_request {
|
||||
format!(
|
||||
"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
To: support@example.org, ceo@example.org\n\
|
||||
From: claire@example.org\n\
|
||||
Subject: i have a question\n\
|
||||
Message-ID: <non-dc-1@example.org>\n\
|
||||
{}\
|
||||
Date: Sun, 14 Mar 2021 17:04:36 +0100\n\
|
||||
Content-Type: text/plain\n\
|
||||
\n\
|
||||
hi support! what is the current version?",
|
||||
To: support@example.org, ceo@example.org\n\
|
||||
From: claire@example.org\n\
|
||||
Subject: i have a question\n\
|
||||
Message-ID: <non-dc-1@example.org>\n\
|
||||
{}\
|
||||
Date: Sun, 14 Mar 2021 17:04:36 +0100\n\
|
||||
Content-Type: text/plain\n\
|
||||
\n\
|
||||
hi support! what is the current version?",
|
||||
if chat_request {
|
||||
"Chat-Version: 1.0\n\
|
||||
Chat-Group-ID: 8ud29aridt29arid\n\
|
||||
Chat-Group-Name: =?utf-8?q?i_have_a_question?=\n"
|
||||
"Chat-Group-ID: 8ud29aridt29arid\n\
|
||||
Chat-Group-Name: =?utf-8?q?i_have_a_question?=\n"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
@@ -1852,15 +1847,15 @@ async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: boo
|
||||
} else {
|
||||
format!(
|
||||
"Received: (Postfix, from userid 1000); Mon, 4 Dec 2006 14:51:39 +0100 (CET)\n\
|
||||
To: support@example.org\n\
|
||||
From: claire@example.org\n\
|
||||
Subject: i have a question\n\
|
||||
Message-ID: <non-dc-1@example.org>\n\
|
||||
{}\
|
||||
Date: Sun, 14 Mar 2021 17:04:36 +0100\n\
|
||||
Content-Type: text/plain\n\
|
||||
\n\
|
||||
hi support! what is the current version?",
|
||||
To: support@example.org\n\
|
||||
From: claire@example.org\n\
|
||||
Subject: i have a question\n\
|
||||
Message-ID: <non-dc-1@example.org>\n\
|
||||
{}\
|
||||
Date: Sun, 14 Mar 2021 17:04:36 +0100\n\
|
||||
Content-Type: text/plain\n\
|
||||
\n\
|
||||
hi support! what is the current version?",
|
||||
if chat_request {
|
||||
"Chat-Version: 1.0\n"
|
||||
} else {
|
||||
@@ -1869,11 +1864,11 @@ async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: boo
|
||||
)
|
||||
};
|
||||
|
||||
let alice = TestContext::new_alice().await;
|
||||
receive_imf(&alice, claire_request.as_bytes(), false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Check that Alice receives the message in a group chat.
|
||||
let msg = alice.get_last_msg().await;
|
||||
assert_eq!(msg.get_subject(), "i have a question");
|
||||
assert!(msg.get_text().contains("hi support!"));
|
||||
@@ -1887,7 +1882,7 @@ async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: boo
|
||||
}
|
||||
assert_eq!(msg.get_override_sender_name(), None);
|
||||
|
||||
let claire = tcm.unconfigured().await;
|
||||
let claire = TestContext::new().await;
|
||||
claire.configure_addr("claire@example.org").await;
|
||||
receive_imf(&claire, claire_request.as_bytes(), false)
|
||||
.await
|
||||
@@ -1911,48 +1906,15 @@ async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: boo
|
||||
assert_eq!(get_chat_msgs(&claire, chat.id).await.unwrap().len(), 1);
|
||||
assert_eq!(msg.get_override_sender_name(), None);
|
||||
|
||||
let reply = if from_dc {
|
||||
// Bob, the other supporter, answers with Delta Chat.
|
||||
format!(
|
||||
"To: support@example.org, claire@example.org\n\
|
||||
From: bob@example.net\n\
|
||||
Subject: =?utf-8?q?Re=3A_i_have_a_question?=\n\
|
||||
References: <bobreply@localhost>\n\
|
||||
In-Reply-To: <non-dc-1@example.org>\n\
|
||||
Message-ID: <bobreply@localhost>\n\
|
||||
Date: Sun, 14 Mar 2021 16:04:57 +0000\n\
|
||||
Chat-Version: 1.0\n\
|
||||
{}\
|
||||
Chat-Group-Name: =?utf-8?q?i_have_a_question?=\n\
|
||||
Chat-Disposition-Notification-To: bob@example.net\n\
|
||||
Content-Type: text/plain\n\
|
||||
\n\
|
||||
hi claire, the version is 1.0, cheers bob",
|
||||
if group_request && chat_request {
|
||||
"Chat-Group-ID: 8ud29aridt29arid\n"
|
||||
} else {
|
||||
// Ad-hoc group has no ID.
|
||||
""
|
||||
}
|
||||
)
|
||||
} else {
|
||||
// Bob, the other supporter, answers with a classic MUA.
|
||||
"To: support@example.org, claire@example.org\n\
|
||||
From: bob@example.net\n\
|
||||
Subject: =?utf-8?q?Re=3A_i_have_a_question?=\n\
|
||||
References: <non-dc-1@example.org>\n\
|
||||
In-Reply-To: <non-dc-1@example.org>\n\
|
||||
Message-ID: <non-dc-2@example.net>\n\
|
||||
Date: Sun, 14 Mar 2021 16:04:57 +0000\n\
|
||||
Content-Type: text/plain\n\
|
||||
\n\
|
||||
hi claire, the version is 1.0, cheers bob"
|
||||
.to_string()
|
||||
};
|
||||
(claire, alice)
|
||||
}
|
||||
|
||||
async fn check_alias_reply(reply: &[u8], chat_request: bool, group_request: bool) {
|
||||
let (claire, alice) = create_test_alias(chat_request, group_request).await;
|
||||
|
||||
// Check that Alice gets the message in the same chat.
|
||||
let request = alice.get_last_msg().await;
|
||||
receive_imf(&alice, reply.as_bytes(), false).await.unwrap();
|
||||
receive_imf(&alice, reply, false).await.unwrap();
|
||||
let answer = alice.get_last_msg().await;
|
||||
assert_eq!(answer.get_subject(), "Re: i have a question");
|
||||
assert!(answer.get_text().contains("the version is 1.0"));
|
||||
@@ -1975,7 +1937,7 @@ async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: boo
|
||||
|
||||
// Check that Claire also gets the message in the same chat.
|
||||
let request = claire.get_last_msg().await;
|
||||
receive_imf(&claire, reply.as_bytes(), false).await.unwrap();
|
||||
receive_imf(&claire, reply, false).await.unwrap();
|
||||
let answer = claire.get_last_msg().await;
|
||||
assert_eq!(answer.get_subject(), "Re: i have a question");
|
||||
assert!(answer.get_text().contains("the version is 1.0"));
|
||||
@@ -1987,36 +1949,47 @@ async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: boo
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_alias_support_answer_from_nondc_nonchat_nongroup() {
|
||||
check_alias_reply(false, false, false).await;
|
||||
async fn test_alias_support_answer_from_nondc() {
|
||||
// Bob, the other supporter, answers with a classic MUA.
|
||||
let bob_answer = b"To: support@example.org, claire@example.org\n\
|
||||
From: bob@example.net\n\
|
||||
Subject: =?utf-8?q?Re=3A_i_have_a_question?=\n\
|
||||
References: <non-dc-1@example.org>\n\
|
||||
In-Reply-To: <non-dc-1@example.org>\n\
|
||||
Message-ID: <non-dc-2@example.net>\n\
|
||||
Date: Sun, 14 Mar 2021 16:04:57 +0000\n\
|
||||
Content-Type: text/plain\n\
|
||||
\n\
|
||||
hi claire, the version is 1.0, cheers bob";
|
||||
|
||||
check_alias_reply(bob_answer, true, true).await;
|
||||
check_alias_reply(bob_answer, false, true).await;
|
||||
check_alias_reply(bob_answer, true, false).await;
|
||||
check_alias_reply(bob_answer, false, false).await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_alias_support_answer_from_nondc_nonchat_group() {
|
||||
check_alias_reply(false, false, true).await;
|
||||
}
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_alias_support_answer_from_nondc_chat_nongroup() {
|
||||
check_alias_reply(false, true, false).await;
|
||||
}
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_alias_support_answer_from_nondc_chat_group() {
|
||||
check_alias_reply(false, true, true).await;
|
||||
}
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_alias_support_answer_from_dc_nonchat_nongroup() {
|
||||
check_alias_reply(true, false, false).await;
|
||||
}
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_alias_support_answer_from_dc_nonchat_group() {
|
||||
check_alias_reply(true, false, true).await;
|
||||
}
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_alias_support_answer_from_dc_chat_nongroup() {
|
||||
check_alias_reply(true, true, false).await;
|
||||
}
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_alias_support_answer_from_dc_chat_group() {
|
||||
check_alias_reply(true, true, true).await;
|
||||
async fn test_alias_answer_from_dc() {
|
||||
// Bob, the other supporter, answers with Delta Chat.
|
||||
let bob_answer = b"To: support@example.org, claire@example.org\n\
|
||||
From: bob@example.net\n\
|
||||
Subject: =?utf-8?q?Re=3A_i_have_a_question?=\n\
|
||||
References: <Gr.af9e810c9b592927.gNm8dVdkZsH@example.net>\n\
|
||||
In-Reply-To: <non-dc-1@example.org>\n\
|
||||
Message-ID: <Gr.af9e810c9b592927.gNm8dVdkZsH@example.net>\n\
|
||||
Date: Sun, 14 Mar 2021 16:04:57 +0000\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Chat-Group-ID: af9e810c9b592927\n\
|
||||
Chat-Group-Name: =?utf-8?q?i_have_a_question?=\n\
|
||||
Chat-Disposition-Notification-To: bob@example.net\n\
|
||||
Content-Type: text/plain\n\
|
||||
\n\
|
||||
hi claire, the version is 1.0, cheers bob";
|
||||
|
||||
check_alias_reply(bob_answer, true, true).await;
|
||||
check_alias_reply(bob_answer, false, true).await;
|
||||
check_alias_reply(bob_answer, true, false).await;
|
||||
check_alias_reply(bob_answer, false, false).await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
@@ -3240,29 +3213,6 @@ async fn test_auto_accept_group_for_bots() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_send_as_bot() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
alice.set_config(Config::Bot, Some("1")).await.unwrap();
|
||||
let bob = &tcm.bob().await;
|
||||
let bob_addr = bob.get_config(Config::Addr).await?.unwrap();
|
||||
let alice_bob_id = Contact::create(alice, "", &bob_addr).await?;
|
||||
let bob_chat_id = tcm.send_recv_accept(alice, bob, "hi").await.chat_id;
|
||||
let alice_chat_id = ChatId::lookup_by_contact(alice, alice_bob_id)
|
||||
.await?
|
||||
.unwrap();
|
||||
let msg = alice.get_last_msg_in(alice_chat_id).await;
|
||||
assert!(msg.is_bot());
|
||||
let msg = bob.get_last_msg_in(bob_chat_id).await;
|
||||
assert!(msg.is_bot());
|
||||
chat::forward_msgs(bob, &[msg.id], bob_chat_id).await?;
|
||||
let msg = bob.get_last_msg_in(bob_chat_id).await;
|
||||
assert!(msg.is_forwarded());
|
||||
assert!(!msg.is_bot());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_no_private_reply_to_blocked_account() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
@@ -4430,7 +4380,7 @@ Chat-Group-Member-Added: charlie@example.com",
|
||||
let contacts = get_chat_contacts(&bob, bob_chat_id).await?;
|
||||
assert_eq!(contacts.len(), 3);
|
||||
|
||||
// Bob fully receives the partial message.
|
||||
// Bob fully reives the partial message.
|
||||
let msg_id = receive_imf_from_inbox(
|
||||
&bob,
|
||||
"first@example.org",
|
||||
@@ -4620,83 +4570,3 @@ async fn test_receive_vcard() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_group_no_recipients() -> Result<()> {
|
||||
let t = &TestContext::new_alice().await;
|
||||
let raw = b"From: alice@example.org\n\
|
||||
Subject: Group\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Chat-Group-Name: Group\n\
|
||||
Chat-Group-ID: GePFDkwEj2K\n\
|
||||
Message-ID: <foobar@localhost>\n\
|
||||
\n\
|
||||
Hello!";
|
||||
let received = receive_imf(t, raw, false).await?.unwrap();
|
||||
let msg = Message::load_from_db(t, *received.msg_ids.last().unwrap()).await?;
|
||||
let chat = Chat::load_from_db(t, msg.chat_id).await?;
|
||||
assert_eq!(chat.typ, Chattype::Group);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests that creating a group
|
||||
/// is preferred over assigning message to existing
|
||||
/// chat based on `In-Reply-To` and `References`.
|
||||
///
|
||||
/// Referenced message itself may be incorrectly assigned,
|
||||
/// but `Chat-Group-ID` uniquely identifies the chat.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_prefer_chat_group_id_over_references() -> Result<()> {
|
||||
let t = &TestContext::new_alice().await;
|
||||
|
||||
// Alice receives 1:1 message from Bob.
|
||||
let raw = b"From: bob@example.net\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: Hi\n\
|
||||
Message-ID: <oneoneone@localhost>\n\
|
||||
\n\
|
||||
Hello!";
|
||||
receive_imf(t, raw, false).await?.unwrap();
|
||||
|
||||
// Alice receives a group message from Bob.
|
||||
// This references 1:1 message,
|
||||
// but should create a group.
|
||||
let raw = b"From: bob@example.net\n\
|
||||
To: alice@example.org\n\
|
||||
Subject: Group\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Chat-Group-Name: Group 1\n\
|
||||
Chat-Group-ID: GePFDkwEj2K\n\
|
||||
Message-ID: <incoming@localhost>\n\
|
||||
References: <oneoneone@localhost>\n\
|
||||
In-Reply-To: <oneoneone@localhost>\n\
|
||||
\n\
|
||||
Group 1";
|
||||
let received1 = receive_imf(t, raw, false).await?.unwrap();
|
||||
let msg1 = Message::load_from_db(t, *received1.msg_ids.last().unwrap()).await?;
|
||||
let chat1 = Chat::load_from_db(t, msg1.chat_id).await?;
|
||||
assert_eq!(chat1.typ, Chattype::Group);
|
||||
|
||||
// Alice receives outgoing group message.
|
||||
// This references 1:1 message,
|
||||
// but should create another group.
|
||||
let raw = b"From: alice@example.org\n\
|
||||
To: bob@example.net
|
||||
Subject: Group\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Chat-Group-Name: Group 2\n\
|
||||
Chat-Group-ID: Abcdexyzfoo\n\
|
||||
Message-ID: <outgoing@localhost>\n\
|
||||
References: <oneoneone@localhost>\n\
|
||||
In-Reply-To: <oneoneone@localhost>\n\
|
||||
\n\
|
||||
Group 2";
|
||||
let received2 = receive_imf(t, raw, false).await?.unwrap();
|
||||
let msg2 = Message::load_from_db(t, *received2.msg_ids.last().unwrap()).await?;
|
||||
let chat2 = Chat::load_from_db(t, msg2.chat_id).await?;
|
||||
assert_eq!(chat2.typ, Chattype::Group);
|
||||
|
||||
assert_ne!(chat1.id, chat2.id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -749,7 +749,7 @@ async fn smtp_loop(
|
||||
) {
|
||||
use futures::future::FutureExt;
|
||||
|
||||
info!(ctx, "Starting SMTP loop.");
|
||||
info!(ctx, "starting smtp loop");
|
||||
let SmtpConnectionHandlers {
|
||||
mut connection,
|
||||
stop_receiver,
|
||||
@@ -760,14 +760,14 @@ async fn smtp_loop(
|
||||
let fut = async move {
|
||||
let ctx = ctx1;
|
||||
if let Err(()) = started.send(()) {
|
||||
warn!(&ctx, "SMTP loop, missing started receiver.");
|
||||
warn!(&ctx, "smtp loop, missing started receiver");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut timeout = None;
|
||||
loop {
|
||||
if let Err(err) = send_smtp_messages(&ctx, &mut connection).await {
|
||||
warn!(ctx, "send_smtp_messages failed: {:#}.", err);
|
||||
warn!(ctx, "send_smtp_messages failed: {:#}", err);
|
||||
timeout = Some(timeout.unwrap_or(30));
|
||||
} else {
|
||||
timeout = None;
|
||||
@@ -784,7 +784,7 @@ async fn smtp_loop(
|
||||
}
|
||||
|
||||
// Fake Idle
|
||||
info!(ctx, "SMTP fake idle started.");
|
||||
info!(ctx, "smtp fake idle - started");
|
||||
match &connection.last_send_error {
|
||||
None => connection.connectivity.set_idle(&ctx).await,
|
||||
Some(err) => connection.connectivity.set_err(&ctx, err).await,
|
||||
@@ -798,7 +798,7 @@ async fn smtp_loop(
|
||||
let now = tools::Time::now();
|
||||
info!(
|
||||
ctx,
|
||||
"SMTP has messages to retry, planning to retry {t} seconds later."
|
||||
"smtp has messages to retry, planning to retry {} seconds later", t,
|
||||
);
|
||||
let duration = std::time::Duration::from_secs(t);
|
||||
tokio::time::timeout(duration, async {
|
||||
@@ -812,18 +812,18 @@ async fn smtp_loop(
|
||||
slept.saturating_add(rand::thread_rng().gen_range((slept / 2)..=slept)),
|
||||
));
|
||||
} else {
|
||||
info!(ctx, "SMTP has no messages to retry, waiting for interrupt.");
|
||||
info!(ctx, "smtp has no messages to retry, waiting for interrupt");
|
||||
idle_interrupt_receiver.recv().await.unwrap_or_default();
|
||||
};
|
||||
|
||||
info!(ctx, "SMTP fake idle interrupted.")
|
||||
info!(ctx, "smtp fake idle - interrupted")
|
||||
}
|
||||
};
|
||||
|
||||
stop_receiver
|
||||
.recv()
|
||||
.map(|_| {
|
||||
info!(ctx, "Shutting down SMTP loop.");
|
||||
info!(ctx, "shutting down smtp loop");
|
||||
})
|
||||
.race(fut)
|
||||
.await;
|
||||
|
||||
84
src/smtp.rs
84
src/smtp.rs
@@ -87,7 +87,7 @@ impl Smtp {
|
||||
/// Connect using configured parameters.
|
||||
pub async fn connect_configured(&mut self, context: &Context) -> Result<()> {
|
||||
if self.has_maybe_stale_connection() {
|
||||
info!(context, "Closing stale connection.");
|
||||
info!(context, "Closing stale connection");
|
||||
self.disconnect();
|
||||
}
|
||||
|
||||
@@ -364,7 +364,8 @@ pub(crate) async fn smtp_send(
|
||||
msg_id: MsgId,
|
||||
) -> SendResult {
|
||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||
info!(context, "SMTP-sending out mime message:\n{message}");
|
||||
info!(context, "smtp-sending out mime message:");
|
||||
println!("{message}");
|
||||
}
|
||||
|
||||
smtp.connectivity.set_working(context).await;
|
||||
@@ -384,7 +385,7 @@ pub(crate) async fn smtp_send(
|
||||
let status = match send_result {
|
||||
Err(crate::smtp::send::Error::SmtpSend(err)) => {
|
||||
// Remote error, retry later.
|
||||
info!(context, "SMTP failed to send: {:?}.", &err);
|
||||
info!(context, "SMTP failed to send: {:?}", &err);
|
||||
|
||||
let res = match err {
|
||||
async_smtp::error::Error::Permanent(ref response) => {
|
||||
@@ -411,10 +412,10 @@ pub(crate) async fn smtp_send(
|
||||
};
|
||||
|
||||
if maybe_transient {
|
||||
info!(context, "Permanent error that is likely to actually be transient, postponing retry for later.");
|
||||
info!(context, "Permanent error that is likely to actually be transient, postponing retry for later");
|
||||
SendResult::Retry
|
||||
} else {
|
||||
info!(context, "Permanent error, message sending failed.");
|
||||
info!(context, "Permanent error, message sending failed");
|
||||
// If we do not retry, add an info message to the chat.
|
||||
// Yandex error "554 5.7.1 [2] Message rejected under suspicion of SPAM; https://ya.cc/..."
|
||||
// should definitely go here, because user has to open the link to
|
||||
@@ -435,19 +436,20 @@ pub(crate) async fn smtp_send(
|
||||
// Any extended smtp status codes like x.1.1, x.1.2 or x.1.3 that we
|
||||
// receive as a transient error are misconfigurations of the smtp server.
|
||||
// See <https://tools.ietf.org/html/rfc3463#section-3.2>
|
||||
info!(context, "Received extended status code {first_word} for a transient error. This looks like a misconfigured SMTP server, let's fail immediately.");
|
||||
info!(context, "Received extended status code {} for a transient error. This looks like a misconfigured SMTP server, let's fail immediately", first_word);
|
||||
SendResult::Failure(format_err!("Permanent SMTP error: {}", err))
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"Transient error with status code {first_word}, postponing retry for later."
|
||||
"Transient error with status code {}, postponing retry for later",
|
||||
first_word
|
||||
);
|
||||
SendResult::Retry
|
||||
}
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"Transient error without status code, postponing retry for later."
|
||||
"Transient error without status code, postponing retry for later"
|
||||
);
|
||||
SendResult::Retry
|
||||
}
|
||||
@@ -455,14 +457,14 @@ pub(crate) async fn smtp_send(
|
||||
_ => {
|
||||
info!(
|
||||
context,
|
||||
"Message sending failed without error returned by the server, retry later."
|
||||
"Message sending failed without error returned by the server, retry later"
|
||||
);
|
||||
SendResult::Retry
|
||||
}
|
||||
};
|
||||
|
||||
// this clears last_success info
|
||||
info!(context, "Failed to send message over SMTP, disconnecting.");
|
||||
info!(context, "Failed to send message over SMTP, disconnecting");
|
||||
smtp.disconnect();
|
||||
|
||||
res
|
||||
@@ -470,19 +472,19 @@ pub(crate) async fn smtp_send(
|
||||
Err(crate::smtp::send::Error::Envelope(err)) => {
|
||||
// Local error, job is invalid, do not retry.
|
||||
smtp.disconnect();
|
||||
warn!(context, "SMTP job is invalid: {err:#}.");
|
||||
warn!(context, "SMTP job is invalid: {}", err);
|
||||
SendResult::Failure(err)
|
||||
}
|
||||
Err(crate::smtp::send::Error::NoTransport) => {
|
||||
// Should never happen.
|
||||
// It does not even make sense to disconnect here.
|
||||
error!(context, "SMTP job failed because SMTP has no transport.");
|
||||
error!(context, "SMTP job failed because SMTP has no transport");
|
||||
SendResult::Failure(format_err!("SMTP has not transport"))
|
||||
}
|
||||
Err(crate::smtp::send::Error::Other(err)) => {
|
||||
// Local error, job is invalid, do not retry.
|
||||
smtp.disconnect();
|
||||
warn!(context, "Unable to load SMTP job: {err:#}.");
|
||||
warn!(context, "unable to load job: {}", err);
|
||||
SendResult::Failure(err)
|
||||
}
|
||||
Ok(()) => SendResult::Success,
|
||||
@@ -556,12 +558,12 @@ pub(crate) async fn send_msg_to_smtp(
|
||||
.sql
|
||||
.execute("DELETE FROM smtp WHERE id=?", (rowid,))
|
||||
.await
|
||||
.context("Failed to remove message with exceeded retry limit from smtp table")?;
|
||||
.context("failed to remove message with exceeded retry limit from smtp table")?;
|
||||
return Ok(());
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
"Try number {retries} to send message {msg_id} (entry {rowid}) over SMTP."
|
||||
"Try number {retries} to send message {msg_id} (entry {rowid}) over SMTP"
|
||||
);
|
||||
|
||||
let recipients_list = recipients
|
||||
@@ -570,7 +572,7 @@ pub(crate) async fn send_msg_to_smtp(
|
||||
|addr| match async_smtp::EmailAddress::new(addr.to_string()) {
|
||||
Ok(addr) => Some(addr),
|
||||
Err(err) => {
|
||||
warn!(context, "Invalid recipient: {} {:?}.", addr, err);
|
||||
warn!(context, "invalid recipient: {} {:?}", addr, err);
|
||||
None
|
||||
}
|
||||
},
|
||||
@@ -648,7 +650,7 @@ pub(crate) async fn send_msg_to_smtp(
|
||||
async fn send_mdns(context: &Context, connection: &mut Smtp) -> Result<()> {
|
||||
loop {
|
||||
if !context.ratelimit.read().await.can_send() {
|
||||
info!(context, "Ratelimiter does not allow sending MDNs now.");
|
||||
info!(context, "Ratelimiter does not allow sending MDNs now");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -695,7 +697,7 @@ pub(crate) async fn send_smtp_messages(context: &Context, connection: &mut Smtp)
|
||||
for rowid in rowids {
|
||||
send_msg_to_smtp(context, connection, rowid)
|
||||
.await
|
||||
.context("Failed to send message")?;
|
||||
.context("failed to send message")?;
|
||||
}
|
||||
|
||||
// although by slow sending, ratelimit may have been expired meanwhile,
|
||||
@@ -704,7 +706,7 @@ pub(crate) async fn send_smtp_messages(context: &Context, connection: &mut Smtp)
|
||||
if !ratelimited {
|
||||
send_mdns(context, connection)
|
||||
.await
|
||||
.context("Failed to send MDNs")?;
|
||||
.context("failed to send MDNs")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -760,7 +762,7 @@ async fn send_mdn_msg_id(
|
||||
|
||||
match smtp_send(context, &recipients, &body, smtp, msg_id).await {
|
||||
SendResult::Success => {
|
||||
info!(context, "Successfully sent MDN for {msg_id}.");
|
||||
info!(context, "Successfully sent MDN for {}", msg_id);
|
||||
context
|
||||
.sql
|
||||
.execute("DELETE FROM smtp_mdns WHERE msg_id = ?", (msg_id,))
|
||||
@@ -780,7 +782,7 @@ async fn send_mdn_msg_id(
|
||||
SendResult::Retry => {
|
||||
info!(
|
||||
context,
|
||||
"Temporary SMTP failure while sending an MDN for {msg_id}."
|
||||
"Temporary SMTP failure while sending an MDN for {}", msg_id
|
||||
);
|
||||
Ok(false)
|
||||
}
|
||||
@@ -796,7 +798,7 @@ async fn send_mdn(context: &Context, smtp: &mut Smtp) -> Result<bool> {
|
||||
context.sql.execute("DELETE FROM smtp_mdns", []).await?;
|
||||
return Ok(false);
|
||||
}
|
||||
info!(context, "Sending MDNs.");
|
||||
info!(context, "Sending MDNs");
|
||||
|
||||
context
|
||||
.sql
|
||||
@@ -826,28 +828,22 @@ async fn send_mdn(context: &Context, smtp: &mut Smtp) -> Result<bool> {
|
||||
(msg_id,),
|
||||
)
|
||||
.await
|
||||
.context("Failed to update MDN retries count")?;
|
||||
.context("failed to update MDN retries count")?;
|
||||
|
||||
match send_mdn_msg_id(context, msg_id, contact_id, smtp).await {
|
||||
Err(err) => {
|
||||
// If there is an error, for example there is no message corresponding to the msg_id in the
|
||||
// database, do not try to send this MDN again.
|
||||
warn!(
|
||||
context,
|
||||
"Error sending MDN for {msg_id}, removing it: {err:#}."
|
||||
);
|
||||
context
|
||||
.sql
|
||||
.execute("DELETE FROM smtp_mdns WHERE msg_id = ?", (msg_id,))
|
||||
.await?;
|
||||
Err(err)
|
||||
}
|
||||
Ok(false) => {
|
||||
bail!("Temporary error while sending an MDN");
|
||||
}
|
||||
Ok(true) => {
|
||||
// Successfully sent MDN.
|
||||
Ok(true)
|
||||
}
|
||||
let res = send_mdn_msg_id(context, msg_id, contact_id, smtp).await;
|
||||
if let Err(ref err) = res {
|
||||
// If there is an error, for example there is no message corresponding to the msg_id in the
|
||||
// database, do not try to send this MDN again.
|
||||
warn!(
|
||||
context,
|
||||
"Error sending MDN for {msg_id}, removing it: {err:#}."
|
||||
);
|
||||
context
|
||||
.sql
|
||||
.execute("DELETE FROM smtp_mdns WHERE msg_id = ?", (msg_id,))
|
||||
.await?;
|
||||
}
|
||||
// If there's a temporary error, pretend there are no more MDNs to send. It's unlikely that
|
||||
// other MDNs could be sent successfully in case of connectivity problems.
|
||||
res
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use deltachat_contact_tools::EmailAddress;
|
||||
use rusqlite::OptionalExtension;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::constants::ShowEmails;
|
||||
use crate::constants::{self, ShowEmails};
|
||||
use crate::context::Context;
|
||||
use crate::imap;
|
||||
use crate::message::MsgId;
|
||||
@@ -839,7 +839,11 @@ CREATE INDEX msgs_status_updates_index2 ON msgs_status_updates (uid);
|
||||
|
||||
if dbversion < 108 {
|
||||
let version = 108;
|
||||
let chunk_size = context.get_max_smtp_rcpt_to().await?;
|
||||
let chunk_size = context
|
||||
.get_configured_provider()
|
||||
.await?
|
||||
.and_then(|provider| provider.opt.max_smtp_rcpt_to)
|
||||
.map_or(constants::DEFAULT_MAX_SMTP_RCPT_TO, usize::from);
|
||||
sql.transaction(move |trans| {
|
||||
Sql::set_db_version_trans(trans, version)?;
|
||||
let id_max =
|
||||
@@ -932,11 +936,6 @@ CREATE INDEX msgs_status_updates_index2 ON msgs_status_updates (uid);
|
||||
.await?;
|
||||
}
|
||||
|
||||
if dbversion < 114 {
|
||||
sql.execute_migration("CREATE INDEX reactions_index1 ON reactions (msg_id)", 114)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let new_version = sql
|
||||
.get_raw_config_int(VERSION_CFG)
|
||||
.await?
|
||||
|
||||
@@ -193,18 +193,7 @@ impl TestContextManager {
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct TestContextBuilder {
|
||||
key_pair: Option<KeyPair>,
|
||||
|
||||
/// Log sink if set.
|
||||
///
|
||||
/// If log sink is not set,
|
||||
/// a new one will be created and stored
|
||||
/// inside the test context when it is built.
|
||||
/// If log sink is provided by the caller,
|
||||
/// it will be subscribed to the test context,
|
||||
/// but not stored inside of it,
|
||||
/// so the caller should store the LogSink elsewhere to
|
||||
/// prevent it from being dropped immediately.
|
||||
log_sink: Option<LogSink>,
|
||||
log_sink: LogSink,
|
||||
}
|
||||
|
||||
impl TestContextBuilder {
|
||||
@@ -245,7 +234,7 @@ impl TestContextBuilder {
|
||||
/// sequence as they occurred rather than all messages from each context in a single
|
||||
/// block.
|
||||
pub fn with_log_sink(mut self, sink: LogSink) -> Self {
|
||||
self.log_sink = Some(sink);
|
||||
self.log_sink = sink;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -253,7 +242,7 @@ impl TestContextBuilder {
|
||||
pub async fn build(self) -> TestContext {
|
||||
let name = self.key_pair.as_ref().map(|key| key.addr.local.clone());
|
||||
|
||||
let test_context = TestContext::new_internal(name, self.log_sink).await;
|
||||
let test_context = TestContext::new_internal(name, Some(self.log_sink.clone())).await;
|
||||
|
||||
if let Some(key_pair) = self.key_pair {
|
||||
test_context
|
||||
|
||||
@@ -327,12 +327,6 @@ async fn check_no_transition_done(groups: &[ChatId], old_alice_addr: &str, bob:
|
||||
last_info_msg.is_none(),
|
||||
"{last_info_msg:?} shouldn't be there (or it's an unrelated info msg)"
|
||||
);
|
||||
|
||||
let sent = bob.send_text(*group, "hi").await;
|
||||
let msg = Message::load_from_db(bob, sent.sender_msg_id)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(msg.get_showpadlock(), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,9 +66,6 @@ pub struct WebxdcManifest {
|
||||
/// Optional URL of webxdc source code.
|
||||
pub source_code_url: Option<String>,
|
||||
|
||||
/// If the webxdc is an integration.
|
||||
pub integration: Option<String>,
|
||||
|
||||
/// If the webxdc requests network access.
|
||||
pub request_internet_access: Option<bool>,
|
||||
}
|
||||
@@ -223,26 +220,29 @@ impl Context {
|
||||
}
|
||||
|
||||
/// Ensure that a file is an acceptable webxdc for sending.
|
||||
/// Moreover, check if the webxdc is an integration and add that information to the message object.
|
||||
pub(crate) async fn prepare_webxdc_file(&self, path: &Path, msg: &mut Message) -> Result<()> {
|
||||
pub(crate) async fn ensure_sendable_webxdc_file(&self, path: &Path) -> Result<()> {
|
||||
let filename = path.to_str().unwrap_or_default();
|
||||
if !filename.ends_with(WEBXDC_SUFFIX) {
|
||||
bail!("{} is not a valid webxdc file", filename);
|
||||
}
|
||||
|
||||
let archive = async_zip::read::fs::ZipFileReader::new(path).await?;
|
||||
if find_zip_entry(archive.file(), "index.html").is_none() {
|
||||
bail!("{} misses index.html", filename);
|
||||
}
|
||||
|
||||
if let Ok(bytes) = get_blob(&archive, "manifest.toml").await {
|
||||
let manifest = parse_webxdc_manifest(&bytes)?;
|
||||
if let Some(integration) = manifest.integration {
|
||||
if integration == "maps" && msg.chat_id.is_self_talk(&self).await? {
|
||||
info!(self, "{} set as maps integration", filename);
|
||||
msg.param.set_int(Param::WebxdcIntegration, 1);
|
||||
let valid = match async_zip::read::fs::ZipFileReader::new(path).await {
|
||||
Ok(archive) => {
|
||||
if find_zip_entry(archive.file(), "index.html").is_none() {
|
||||
warn!(self, "{} misses index.html", filename);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
warn!(self, "{} cannot be opened as zip-file", filename);
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if !valid {
|
||||
bail!("{} is not a valid webxdc file", filename);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -15,7 +15,7 @@ impl Context {
|
||||
let mut msg = Message::new(Viewtype::Webxdc);
|
||||
msg.set_file(file, None);
|
||||
msg.hidden = true;
|
||||
msg.param.set_int(Param::WebxdcIntegration, 1); // can be removed when all UI upgraded to maps.xdc having integration=maps in the manifest
|
||||
msg.param.set_int(Param::WebxdcIntegration, 1);
|
||||
msg.param.set_int(Param::GuaranteeE2ee, 1); // needed to pass `internet_access` requirements
|
||||
send_msg(self, chat_id, &mut msg).await?;
|
||||
Ok(())
|
||||
@@ -105,10 +105,7 @@ impl Message {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::chat;
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::message::{Message, Viewtype};
|
||||
use crate::test_utils::TestContext;
|
||||
use anyhow::Result;
|
||||
use std::time::Duration;
|
||||
@@ -129,55 +126,4 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_overwrite_default_integration() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let self_chat = &t.get_self_chat().await;
|
||||
|
||||
async fn assert_integration(t: &Context, name: &str) -> Result<()> {
|
||||
let integration_id = t.init_webxdc_integration(None).await?.unwrap();
|
||||
let integration = Message::load_from_db(&t, integration_id).await?;
|
||||
let integration_info = integration.get_webxdc_info(&t).await?;
|
||||
assert_eq!(integration_info.name, name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// set default integration
|
||||
let bytes = include_bytes!("../../test-data/webxdc/with-manifest-and-png-icon.xdc");
|
||||
let file = t.get_blobdir().join("maps.xdc");
|
||||
tokio::fs::write(&file, bytes).await.unwrap();
|
||||
t.set_webxdc_integration(file.to_str().unwrap()).await?;
|
||||
assert_integration(&t, "with some icon").await?;
|
||||
|
||||
// send a maps.xdc with insufficient manifest
|
||||
let mut msg = Message::new(Viewtype::Webxdc);
|
||||
msg.set_file_from_bytes(
|
||||
&t,
|
||||
"mapstest.xdc",
|
||||
include_bytes!("../../test-data/webxdc/mapstest-integration-unset.xdc"),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
chat::send_msg(&t, self_chat.id, &mut msg).await?;
|
||||
let sent = t.pop_sent_msg_opt(Duration::from_secs(1)).await;
|
||||
assert!(sent.is_some());
|
||||
assert_integration(&t, "with some icon").await?; // still the default integration
|
||||
|
||||
// send a maps.xdc with manifest including the line `integration = "maps"`
|
||||
let mut msg = Message::new(Viewtype::Webxdc);
|
||||
msg.set_file_from_bytes(
|
||||
&t,
|
||||
"mapstest.xdc",
|
||||
include_bytes!("../../test-data/webxdc/mapstest-integration-set.xdc"),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
chat::send_msg(&t, self_chat.id, &mut msg).await?;
|
||||
//let sent = t.pop_sent_msg_opt(Duration::from_secs(1)).await;
|
||||
//assert!(sent.is_some());
|
||||
assert_integration(&t, "Maps Test 2").await?; // still the default integration
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ mod tests {
|
||||
async fn test_maps_integration() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
let bytes = include_bytes!("../../test-data/webxdc/mapstest-integration-set.xdc");
|
||||
let bytes = include_bytes!("../../test-data/webxdc/mapstest.xdc");
|
||||
let file = t.get_blobdir().join("maps.xdc");
|
||||
tokio::fs::write(&file, bytes).await.unwrap();
|
||||
t.set_webxdc_integration(file.to_str().unwrap()).await?;
|
||||
@@ -198,7 +198,7 @@ mod tests {
|
||||
|
||||
let integration = Message::load_from_db(&t, integration_id).await?;
|
||||
let info = integration.get_webxdc_info(&t).await?;
|
||||
assert_eq!(info.name, "Maps Test 2");
|
||||
assert_eq!(info.name, "Maps Test");
|
||||
assert_eq!(info.internet_access, true);
|
||||
|
||||
t.send_webxdc_status_update(
|
||||
|
||||
@@ -25,7 +25,6 @@ Messenger functions | [Chat-over-Email](https://github.com/deltacha
|
||||
Detect mailing list | List-Id ([RFC 2919][]) and Precedence ([RFC 3834][])
|
||||
User and chat colors | [XEP-0392][]: Consistent Color Generation
|
||||
Send and receive system messages | Multipart/Report Media Type ([RFC 6522][])
|
||||
Send and receive contact files | vCard ([RFC 6350][])
|
||||
Return receipts | Message Disposition Notification (MDN, [RFC 8098][], [RFC 3503][]) using the Chat-Disposition-Notification-To header
|
||||
Locations | KML ([Open Geospatial Consortium](http://www.opengeospatial.org/standards/kml/), [Google Dev](https://developers.google.com/kml/))
|
||||
|
||||
@@ -54,7 +53,6 @@ Locations | KML ([Open Geospatial Consortium](http://www.
|
||||
[RFC 5321]: https://tools.ietf.org/html/rfc5321
|
||||
[RFC 5322]: https://tools.ietf.org/html/rfc5322
|
||||
[RFC 6154]: https://tools.ietf.org/html/rfc6154
|
||||
[RFC 6350]: https://tools.ietf.org/html/rfc6350
|
||||
[RFC 6522]: https://tools.ietf.org/html/rfc6522
|
||||
[RFC 6749]: https://tools.ietf.org/html/rfc6749
|
||||
[RFC 7162]: https://tools.ietf.org/html/rfc7162
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user