mirror of
https://github.com/chatmail/core.git
synced 2026-04-05 23:22:11 +03:00
Compare commits
45 Commits
v1.151.1
...
add-webxdc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e8b5fe3d2 | ||
|
|
70fe196220 | ||
|
|
3ad9cf3c74 | ||
|
|
8ffe864812 | ||
|
|
df8c4cc3e9 | ||
|
|
5a353a206b | ||
|
|
8ddd28d08c | ||
|
|
e07e9aec17 | ||
|
|
0c35360b9f | ||
|
|
c356dbff06 | ||
|
|
d4a6484b0c | ||
|
|
5aa8ffaf5e | ||
|
|
85de1ad538 | ||
|
|
913203fbad | ||
|
|
a42cd5450b | ||
|
|
92a68ceb48 | ||
|
|
ada5368b9c | ||
|
|
f3332fa7a6 | ||
|
|
f03d56143c | ||
|
|
d21756812b | ||
|
|
cbe5c38705 | ||
|
|
755b245495 | ||
|
|
dc5fcdf425 | ||
|
|
45e55c963e | ||
|
|
8967d7748c | ||
|
|
948cefa3ef | ||
|
|
9ec1401a37 | ||
|
|
170b7e2ded | ||
|
|
d63a2b39aa | ||
|
|
167948e62a | ||
|
|
4edade225c | ||
|
|
da546d3526 | ||
|
|
6be96d3eba | ||
|
|
d1537095e4 | ||
|
|
ba68b87c58 | ||
|
|
b5f899540c | ||
|
|
c6dd03590c | ||
|
|
ff3efafcfc | ||
|
|
717c18ed0f | ||
|
|
4026c827be | ||
|
|
cd8cff7efb | ||
|
|
a319c1ea27 | ||
|
|
5db574b44f | ||
|
|
8af90a1299 | ||
|
|
a6db7ba1e3 |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
name: Lint Rust
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: 1.82.0
|
||||
RUSTUP_TOOLCHAIN: 1.83.0
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -95,11 +95,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
rust: 1.82.0
|
||||
rust: 1.83.0
|
||||
- os: windows-latest
|
||||
rust: 1.82.0
|
||||
rust: 1.83.0
|
||||
- os: macos-latest
|
||||
rust: 1.82.0
|
||||
rust: 1.83.0
|
||||
|
||||
# Minimum Supported Rust Version = 1.77.0
|
||||
- os: ubuntu-latest
|
||||
|
||||
27
CHANGELOG.md
27
CHANGELOG.md
@@ -1,5 +1,31 @@
|
||||
# Changelog
|
||||
|
||||
## [1.151.2] - 2024-11-26
|
||||
|
||||
### API-Changes
|
||||
|
||||
- Deprecate webxdc `descr` parameter ([#6255](https://github.com/deltachat/deltachat-core-rust/pull/6255)).
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- AEAP: Check that the old peerstate verified key fingerprint hasn't changed when removing it.
|
||||
- Add `AccountsChanged` and `AccountsItemChanged` events ([#6118](https://github.com/deltachat/deltachat-core-rust/pull/6118)).
|
||||
- Do not use format=flowed in outgoing messages ([#6256](https://github.com/deltachat/deltachat-core-rust/pull/6256)).
|
||||
- Add webxdc limits api.
|
||||
- Add href to IncomingWebxdcNotify event ([#6266](https://github.com/deltachat/deltachat-core-rust/pull/6266)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- Revert treating some transient SMTP errors as permanent.
|
||||
|
||||
### Refactor
|
||||
|
||||
- Create_status_update_record: Get rid of `notify` var.
|
||||
|
||||
### Tests
|
||||
|
||||
- Check that IncomingMsg isn't emitted for reactions.
|
||||
|
||||
## [1.151.1] - 2024-11-24
|
||||
|
||||
### Build system
|
||||
@@ -5342,3 +5368,4 @@ https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed
|
||||
[1.150.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.149.0..v1.150.0
|
||||
[1.151.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.150.0..v1.151.0
|
||||
[1.151.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.151.0..v1.151.1
|
||||
[1.151.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.151.1..v1.151.2
|
||||
|
||||
160
Cargo.lock
generated
160
Cargo.lock
generated
@@ -169,9 +169,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.92"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
@@ -230,7 +230,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -242,7 +242,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -347,7 +347,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -375,7 +375,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -386,7 +386,7 @@ checksum = "00b9f7252833d5ed4b00aa9604b563529dd5e11de9c23615de2dcdf91eb87b52"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"crc32fast",
|
||||
"futures-lite 2.4.0",
|
||||
"futures-lite 2.5.0",
|
||||
"pin-project",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@@ -688,9 +688,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -1238,7 +1238,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1262,7 +1262,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1273,7 +1273,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1306,7 +1306,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-broadcast",
|
||||
@@ -1330,7 +1330,7 @@ dependencies = [
|
||||
"fd-lock",
|
||||
"format-flowed",
|
||||
"futures",
|
||||
"futures-lite 2.4.0",
|
||||
"futures-lite 2.5.0",
|
||||
"hex",
|
||||
"hickory-resolver",
|
||||
"http-body-util",
|
||||
@@ -1407,7 +1407,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-channel 2.3.1",
|
||||
@@ -1432,7 +1432,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-repl"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -1448,12 +1448,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
"deltachat-jsonrpc",
|
||||
"futures-lite 2.4.0",
|
||||
"futures-lite 2.5.0",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -1472,12 +1472,12 @@ name = "deltachat_derive"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -1527,7 +1527,7 @@ checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1558,7 +1558,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1568,7 +1568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1588,7 +1588,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
@@ -1654,7 +1654,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1936,7 +1936,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1956,7 +1956,7 @@ checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2324,9 +2324,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f1fa2f9765705486b33fd2acf1577f8ec449c2ba1f318ae5447697b7c08d210"
|
||||
checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1"
|
||||
dependencies = [
|
||||
"fastrand 2.1.1",
|
||||
"futures-core",
|
||||
@@ -2343,7 +2343,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2911,9 +2911,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.4"
|
||||
version = "0.25.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae"
|
||||
checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
@@ -3043,7 +3043,7 @@ dependencies = [
|
||||
"derive_more",
|
||||
"ed25519-dalek",
|
||||
"futures-concurrency",
|
||||
"futures-lite 2.4.0",
|
||||
"futures-lite 2.5.0",
|
||||
"futures-util",
|
||||
"indexmap",
|
||||
"iroh-base",
|
||||
@@ -3097,7 +3097,7 @@ dependencies = [
|
||||
"duct",
|
||||
"futures-buffered",
|
||||
"futures-concurrency",
|
||||
"futures-lite 2.4.0",
|
||||
"futures-lite 2.5.0",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"genawaiter",
|
||||
@@ -3218,7 +3218,7 @@ checksum = "c1fd18ec6325dd3f01625f12c01acff50a4374ee1ab708e7b2078885fd63ad30"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-buffered",
|
||||
"futures-lite 2.4.0",
|
||||
"futures-lite 2.5.0",
|
||||
"futures-util",
|
||||
"iroh-net",
|
||||
"tokio",
|
||||
@@ -3303,9 +3303,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kamadak-exif"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99e7b00ff45df279c3e40f7fee99fad4f7eddbf9ed2d24e99133e8683330f0c7"
|
||||
checksum = "1130d80c7374efad55a117d715a3af9368f0fa7a2c54573afc15a188cd984837"
|
||||
dependencies = [
|
||||
"mutate_once",
|
||||
]
|
||||
@@ -3693,7 +3693,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"derive_more",
|
||||
"futures-lite 2.4.0",
|
||||
"futures-lite 2.5.0",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"libc",
|
||||
@@ -3834,7 +3834,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3916,7 +3916,7 @@ dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3990,7 +3990,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4194,7 +4194,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4292,7 +4292,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4406,7 +4406,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4482,7 +4482,7 @@ dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"derive_more",
|
||||
"futures-lite 2.4.0",
|
||||
"futures-lite 2.5.0",
|
||||
"futures-util",
|
||||
"igd-next",
|
||||
"iroh-metrics",
|
||||
@@ -4654,7 +4654,7 @@ dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4692,7 +4692,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4762,9 +4762,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.37.0"
|
||||
version = "0.37.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffbfb3ddf5364c9cfcd65549a1e7b801d0e8d1b14c1a1590a6408aa93cfbfa84"
|
||||
checksum = "f22f29bdff3987b4d8632ef95fd6424ec7e4e0a57e2f4fc63e489e75357f6a03"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -5257,9 +5257,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.37"
|
||||
version = "0.38.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
|
||||
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"errno",
|
||||
@@ -5270,9 +5270,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.18"
|
||||
version = "0.23.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f"
|
||||
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -5442,7 +5442,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5565,7 +5565,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5576,7 +5576,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5943,7 +5943,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"struct_iterable_internal",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5971,7 +5971,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6033,9 +6033,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.86"
|
||||
version = "2.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6073,7 +6073,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6119,9 +6119,9 @@ checksum = "094c9f64d6de9a8506b1e49b63a29333b37ed9e821ee04be694d431b3264c3c5"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.13.0"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
|
||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand 2.1.1",
|
||||
@@ -6157,22 +6157,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.66"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.66"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6288,7 +6288,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6484,7 +6484,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6596,7 +6596,7 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6818,7 +6818,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -6852,7 +6852,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -6887,9 +6887,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.6"
|
||||
version = "0.26.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958"
|
||||
checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
@@ -7005,7 +7005,7 @@ checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7016,7 +7016,7 @@ checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7333,7 +7333,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7359,7 +7359,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7379,7 +7379,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.86",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
14
Cargo.toml
14
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
rust-version = "1.77"
|
||||
@@ -62,10 +62,10 @@ http-body-util = "0.1.2"
|
||||
humansize = "2"
|
||||
hyper = "1"
|
||||
hyper-util = "0.1.10"
|
||||
image = { version = "0.25.4", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] }
|
||||
image = { version = "0.25.5", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] }
|
||||
iroh-gossip = { version = "0.28.1", default-features = false, features = ["net"] }
|
||||
iroh-net = { version = "0.28.1", default-features = false }
|
||||
kamadak-exif = "0.6.0"
|
||||
kamadak-exif = "0.6.1"
|
||||
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
|
||||
libc = { workspace = true }
|
||||
mailparse = "0.15"
|
||||
@@ -86,7 +86,7 @@ regex = { workspace = true }
|
||||
rusqlite = { workspace = true, features = ["sqlcipher"] }
|
||||
rust-hsluv = "0.1"
|
||||
rustls-pki-types = "1.10.0"
|
||||
rustls = { version = "0.23.14", default-features = false }
|
||||
rustls = { version = "0.23.19", default-features = false }
|
||||
sanitize-filename = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
serde_urlencoded = "0.7.1"
|
||||
@@ -109,7 +109,7 @@ tokio = { workspace = true, features = ["fs", "rt-multi-thread", "macros"] }
|
||||
toml = "0.8"
|
||||
url = "2"
|
||||
uuid = { version = "1", features = ["serde", "v4"] }
|
||||
webpki-roots = "0.26.6"
|
||||
webpki-roots = "0.26.7"
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true, features = ["backtrace"] } # Enable `backtrace` feature in tests.
|
||||
@@ -173,7 +173,7 @@ deltachat-contact-tools = { path = "deltachat-contact-tools" }
|
||||
deltachat-jsonrpc = { path = "deltachat-jsonrpc", default-features = false }
|
||||
deltachat = { path = ".", default-features = false }
|
||||
futures = "0.3.31"
|
||||
futures-lite = "2.4.0"
|
||||
futures-lite = "2.5.0"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
nu-ansi-term = "0.46"
|
||||
@@ -185,7 +185,7 @@ rusqlite = "0.32"
|
||||
sanitize-filename = "0.5"
|
||||
serde = "1.0"
|
||||
serde_json = "1"
|
||||
tempfile = "3.13.0"
|
||||
tempfile = "3.14.0"
|
||||
thiserror = "1"
|
||||
tokio = "1"
|
||||
tokio-util = "0.7.11"
|
||||
|
||||
@@ -14,8 +14,8 @@ For example, to release version 1.116.0 of the core, do the following steps.
|
||||
5. Commit the changes as `chore(release): prepare for 1.116.0`.
|
||||
Optionally, use a separate branch like `prep-1.116.0` for this commit and open a PR for review.
|
||||
|
||||
6. Tag the release: `git tag -a v1.116.0`.
|
||||
6. Tag the release: `git tag --annotate v1.116.0`.
|
||||
|
||||
7. Push the release tag: `git push origin v1.116.0`.
|
||||
|
||||
8. Create a GitHub release: `gh release create v1.116.0 -n ''`.
|
||||
8. Create a GitHub release: `gh release create v1.116.0 --notes ''`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
description = "Deltachat FFI"
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -1161,8 +1161,7 @@ uint32_t dc_send_videochat_invitation (dc_context_t* context, uint32_t chat_id);
|
||||
* - `document`: optional document name. shown eg. in title bar.
|
||||
* - `summary`: optional summary. shown beside app icon.
|
||||
* - `notify`: optional array of other users `selfAddr` to be notified e.g. by a sound about `info` or `summary`.
|
||||
* @param descr The user-visible description of JSON data,
|
||||
* in case of a chess game, e.g. the move.
|
||||
* @param descr Deprecated, set to NULL
|
||||
* @return 1=success, 0=error
|
||||
*/
|
||||
int dc_send_webxdc_status_update (dc_context_t* context, uint32_t msg_id, const char* json, const char* descr);
|
||||
@@ -2598,13 +2597,15 @@ dc_lot_t* dc_check_qr (dc_context_t* context, const char*
|
||||
|
||||
/**
|
||||
* Get QR code text that will offer an Setup-Contact or Verified-Group invitation.
|
||||
* The QR code is compatible to the OPENPGP4FPR format
|
||||
* so that a basic fingerprint comparison also works e.g. with OpenKeychain.
|
||||
*
|
||||
* The scanning device will pass the scanned content to dc_check_qr() then;
|
||||
* if dc_check_qr() returns DC_QR_ASK_VERIFYCONTACT or DC_QR_ASK_VERIFYGROUP
|
||||
* an out-of-band-verification can be joined using dc_join_securejoin()
|
||||
*
|
||||
* The returned text will also work as a normal https:-link,
|
||||
* so that the QR code is useful also without Delta Chat being installed
|
||||
* or can be passed to contacts through other channels.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
* @param chat_id If set to a group-chat-id,
|
||||
@@ -4204,6 +4205,10 @@ char* dc_msg_get_webxdc_blob (const dc_msg_t* msg, const char*
|
||||
* currently, this is only true for encrypted Webxdc's in the self chat
|
||||
* that have requested internet access in the manifest.
|
||||
* - self_addr: address to be used for `window.webxdc.selfAddr` in JS land.
|
||||
* - send_update_interval: Milliseconds to wait before calling `sendUpdate()` again since the last call.
|
||||
* Should be exposed to `webxdc.sendUpdateInterval` in JS land.
|
||||
* - send_update_max_size: Maximum number of bytes accepted for a serialized update object.
|
||||
+ Should be exposed to `webxdc.sendUpdateMaxSize` in JS land.
|
||||
*
|
||||
* @memberof dc_msg_t
|
||||
* @param msg The webxdc instance.
|
||||
@@ -4489,6 +4494,7 @@ int dc_msg_is_info (const dc_msg_t* msg);
|
||||
* - DC_INFO_INVALID_UNENCRYPTED_MAIL (13) - Info-message for "Provider requires end-to-end encryption which is not setup yet",
|
||||
* the UI should change the corresponding string using #DC_STR_INVALID_UNENCRYPTED_MAIL
|
||||
* and also offer a way to fix the encryption, eg. by a button offering a QR scan
|
||||
* - DC_INFO_WEBXDC_INFO_MESSAGE (32) - Info-message created by webxdc app sending `update.info`
|
||||
*
|
||||
* Even when you display an icon,
|
||||
* you should still display the text of the informational message using dc_msg_get_text()
|
||||
@@ -5898,15 +5904,26 @@ int dc_event_get_data2_int(dc_event_t* event);
|
||||
|
||||
/**
|
||||
* Get data associated with an event object.
|
||||
* The meaning of the data depends on the event ID
|
||||
* returned as @ref DC_EVENT constants by dc_event_get_id().
|
||||
* See also dc_event_get_data1_int() and dc_event_get_data2_int().
|
||||
* The meaning of the data depends on the event ID returned as @ref DC_EVENT constants.
|
||||
*
|
||||
* @memberof dc_event_t
|
||||
* @param event Event object as returned from dc_get_next_event().
|
||||
* @return "data2" as a string or NULL.
|
||||
* the meaning depends on the event type associated with this event.
|
||||
* Once you're done with the string, you have to unref it using dc_unref_str().
|
||||
* @return "data1" string or NULL.
|
||||
* The meaning depends on the event type associated with this event.
|
||||
* Must be freed using dc_str_unref().
|
||||
*/
|
||||
char* dc_event_get_data1_str(dc_event_t* event);
|
||||
|
||||
|
||||
/**
|
||||
* Get data associated with an event object.
|
||||
* The meaning of the data depends on the event ID returned as @ref DC_EVENT constants.
|
||||
*
|
||||
* @memberof dc_event_t
|
||||
* @param event Event object as returned from dc_get_next_event().
|
||||
* @return "data2" string or NULL.
|
||||
* The meaning depends on the event type associated with this event.
|
||||
* Must be freed using dc_str_unref().
|
||||
*/
|
||||
char* dc_event_get_data2_str(dc_event_t* event);
|
||||
|
||||
@@ -6108,7 +6125,9 @@ void dc_event_unref(dc_event_t* event);
|
||||
/**
|
||||
* A webxdc wants an info message or a changed summary to be notified.
|
||||
*
|
||||
* @param data1 contact_id ID of the contact sending.
|
||||
* @param data1 (int) contact_id ID _and_ (char*) href.
|
||||
* - dc_event_get_data1_int() returns contact_id of the sending contact.
|
||||
* - dc_event_get_data1_str() returns the href as set to `update.href`.
|
||||
* @param data2 (int) msg_id _and_ (char*) text_to_notify.
|
||||
* - dc_event_get_data2_int() returns the msg_id,
|
||||
* referring to the webxdc-info-message, if there is any.
|
||||
@@ -6414,6 +6433,25 @@ void dc_event_unref(dc_event_t* event);
|
||||
|
||||
#define DC_EVENT_CHATLIST_ITEM_CHANGED 2301
|
||||
|
||||
/**
|
||||
* Inform that the list of accounts has changed (an account removed or added or (not yet implemented) the account order changes)
|
||||
*
|
||||
* This event is only emitted by the account manager.
|
||||
*/
|
||||
|
||||
#define DC_EVENT_ACCOUNTS_CHANGED 2302
|
||||
|
||||
/**
|
||||
* Inform that an account property that might be shown in the account list changed, namely:
|
||||
* - is_configured (see dc_is_configured())
|
||||
* - displayname
|
||||
* - selfavatar
|
||||
* - private_tag
|
||||
*
|
||||
* This event is emitted from the account whose property changed.
|
||||
*/
|
||||
|
||||
#define DC_EVENT_ACCOUNTS_ITEM_CHANGED 2303
|
||||
|
||||
/**
|
||||
* Inform that some events have been skipped due to event channel overflow.
|
||||
|
||||
@@ -569,6 +569,8 @@ pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int
|
||||
EventType::AccountsBackgroundFetchDone => 2200,
|
||||
EventType::ChatlistChanged => 2300,
|
||||
EventType::ChatlistItemChanged { .. } => 2301,
|
||||
EventType::AccountsChanged => 2302,
|
||||
EventType::AccountsItemChanged => 2303,
|
||||
EventType::EventChannelOverflow { .. } => 2400,
|
||||
#[allow(unreachable_patterns)]
|
||||
#[cfg(test)]
|
||||
@@ -601,8 +603,10 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
|
||||
| EventType::ConfigSynced { .. }
|
||||
| EventType::IncomingMsgBunch { .. }
|
||||
| EventType::ErrorSelfNotInGroup(_)
|
||||
| EventType::AccountsBackgroundFetchDone => 0,
|
||||
EventType::ChatlistChanged => 0,
|
||||
| EventType::AccountsBackgroundFetchDone
|
||||
| EventType::ChatlistChanged
|
||||
| EventType::AccountsChanged
|
||||
| EventType::AccountsItemChanged => 0,
|
||||
EventType::IncomingReaction { contact_id, .. }
|
||||
| EventType::IncomingWebxdcNotify { contact_id, .. } => contact_id.to_u32() as libc::c_int,
|
||||
EventType::MsgsChanged { chat_id, .. }
|
||||
@@ -676,6 +680,8 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
|
||||
| EventType::AccountsBackgroundFetchDone
|
||||
| EventType::ChatlistChanged
|
||||
| EventType::ChatlistItemChanged { .. }
|
||||
| EventType::AccountsChanged
|
||||
| EventType::AccountsItemChanged
|
||||
| EventType::ConfigSynced { .. }
|
||||
| EventType::ChatModified(_)
|
||||
| EventType::WebxdcRealtimeAdvertisementReceived { .. }
|
||||
@@ -703,6 +709,27 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_event_get_data1_str(event: *mut dc_event_t) -> *mut libc::c_char {
|
||||
if event.is_null() {
|
||||
eprintln!("ignoring careless call to dc_event_get_data1_str()");
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let event = &(*event).typ;
|
||||
|
||||
match event {
|
||||
EventType::IncomingWebxdcNotify { href, .. } => {
|
||||
if let Some(href) = href {
|
||||
href.to_c_string().unwrap_or_default().into_raw()
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
_ => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut libc::c_char {
|
||||
if event.is_null() {
|
||||
@@ -751,6 +778,8 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
|
||||
| EventType::IncomingMsgBunch { .. }
|
||||
| EventType::ChatlistItemChanged { .. }
|
||||
| EventType::ChatlistChanged
|
||||
| EventType::AccountsChanged
|
||||
| EventType::AccountsItemChanged
|
||||
| EventType::WebxdcRealtimeAdvertisementReceived { .. }
|
||||
| EventType::EventChannelOverflow { .. } => ptr::null_mut(),
|
||||
EventType::ConfigureProgress { comment, .. } => {
|
||||
@@ -1065,7 +1094,7 @@ pub unsafe extern "C" fn dc_send_webxdc_status_update(
|
||||
context: *mut dc_context_t,
|
||||
msg_id: u32,
|
||||
json: *const libc::c_char,
|
||||
descr: *const libc::c_char,
|
||||
_descr: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_send_webxdc_status_update()");
|
||||
@@ -1073,14 +1102,10 @@ pub unsafe extern "C" fn dc_send_webxdc_status_update(
|
||||
}
|
||||
let ctx = &*context;
|
||||
|
||||
block_on(ctx.send_webxdc_status_update(
|
||||
MsgId::new(msg_id),
|
||||
&to_string_lossy(json),
|
||||
&to_string_lossy(descr),
|
||||
))
|
||||
.context("Failed to send webxdc update")
|
||||
.log_err(ctx)
|
||||
.is_ok() as libc::c_int
|
||||
block_on(ctx.send_webxdc_status_update(MsgId::new(msg_id), &to_string_lossy(json)))
|
||||
.context("Failed to send webxdc update")
|
||||
.log_err(ctx)
|
||||
.is_ok() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
description = "DeltaChat JSON-RPC API"
|
||||
edition = "2021"
|
||||
default-run = "deltachat-jsonrpc-server"
|
||||
|
||||
@@ -1767,10 +1767,10 @@ impl CommandApi {
|
||||
account_id: u32,
|
||||
instance_msg_id: u32,
|
||||
update_str: String,
|
||||
description: String,
|
||||
_descr: Option<String>,
|
||||
) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
ctx.send_webxdc_status_update(MsgId::new(instance_msg_id), &update_str, &description)
|
||||
ctx.send_webxdc_status_update(MsgId::new(instance_msg_id), &update_str)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -1829,6 +1829,18 @@ impl CommandApi {
|
||||
WebxdcMessageInfo::get_for_message(&ctx, MsgId::new(instance_msg_id)).await
|
||||
}
|
||||
|
||||
/// Get href from a WebxdcInfoMessage which might include a hash holding
|
||||
/// information about a specific position or state in a webxdc app (optional)
|
||||
async fn get_webxdc_href(
|
||||
&self,
|
||||
account_id: u32,
|
||||
instance_msg_id: u32,
|
||||
) -> Result<Option<String>> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let message = Message::load_from_db(&ctx, MsgId::new(instance_msg_id)).await?;
|
||||
Ok(message.get_webxdc_href())
|
||||
}
|
||||
|
||||
/// Get blob encoded as base64 from a webxdc message
|
||||
///
|
||||
/// path is the path of the file within webxdc archive
|
||||
|
||||
@@ -112,6 +112,7 @@ pub enum EventType {
|
||||
contact_id: u32,
|
||||
msg_id: u32,
|
||||
text: String,
|
||||
href: Option<String>,
|
||||
},
|
||||
|
||||
/// There is a fresh message. Typically, the user will show an notification
|
||||
@@ -285,6 +286,20 @@ pub enum EventType {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
ChatlistItemChanged { chat_id: Option<u32> },
|
||||
|
||||
/// Inform that the list of accounts has changed (an account removed or added or (not yet implemented) the account order changes)
|
||||
///
|
||||
/// This event is only emitted by the account manager
|
||||
AccountsChanged,
|
||||
|
||||
/// Inform that an account property that might be shown in the account list changed, namely:
|
||||
/// - is_configured (see is_configured())
|
||||
/// - displayname
|
||||
/// - selfavatar
|
||||
/// - private_tag
|
||||
///
|
||||
/// This event is emitted from the account whose property changed.
|
||||
AccountsItemChanged,
|
||||
|
||||
/// Inform than some events have been skipped due to event channel overflow.
|
||||
EventChannelOverflow { n: u64 },
|
||||
}
|
||||
@@ -331,10 +346,12 @@ impl From<CoreEventType> for EventType {
|
||||
contact_id,
|
||||
msg_id,
|
||||
text,
|
||||
href,
|
||||
} => IncomingWebxdcNotify {
|
||||
contact_id: contact_id.to_u32(),
|
||||
msg_id: msg_id.to_u32(),
|
||||
text,
|
||||
href,
|
||||
},
|
||||
CoreEventType::IncomingMsg { chat_id, msg_id } => IncomingMsg {
|
||||
chat_id: chat_id.to_u32(),
|
||||
@@ -426,6 +443,8 @@ impl From<CoreEventType> for EventType {
|
||||
},
|
||||
CoreEventType::ChatlistChanged => ChatlistChanged,
|
||||
CoreEventType::EventChannelOverflow { n } => EventChannelOverflow { n },
|
||||
CoreEventType::AccountsChanged => AccountsChanged,
|
||||
CoreEventType::AccountsItemChanged => AccountsItemChanged,
|
||||
#[allow(unreachable_patterns)]
|
||||
#[cfg(test)]
|
||||
_ => unreachable!("This is just to silence a rust_analyzer false-positive"),
|
||||
|
||||
@@ -85,6 +85,8 @@ pub struct MessageObject {
|
||||
|
||||
webxdc_info: Option<WebxdcMessageInfo>,
|
||||
|
||||
webxdc_href: Option<String>,
|
||||
|
||||
download_state: DownloadState,
|
||||
|
||||
reactions: Option<JSONRPCReactions>,
|
||||
@@ -128,6 +130,11 @@ impl MessageObject {
|
||||
|
||||
let webxdc_info = if message.get_viewtype() == Viewtype::Webxdc {
|
||||
Some(WebxdcMessageInfo::get_for_message(context, msg_id).await?)
|
||||
} else if let (deltachat::mimeparser::SystemMessage::WebxdcInfoMessage, Some(parent_msg)) =
|
||||
(message.get_info_type(), message.parent(context).await?)
|
||||
{
|
||||
// get webcdx info from parent message
|
||||
Some(WebxdcMessageInfo::get_for_message(context, parent_msg.get_id()).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -241,6 +248,10 @@ impl MessageObject {
|
||||
file_name: message.get_filename(),
|
||||
webxdc_info,
|
||||
|
||||
// On a WebxdcInfoMessage this might include a hash holding
|
||||
// information about a specific position or state in a webxdc app
|
||||
webxdc_href: message.get_webxdc_href(),
|
||||
|
||||
download_state,
|
||||
|
||||
reactions,
|
||||
|
||||
@@ -37,6 +37,12 @@ pub struct WebxdcMessageInfo {
|
||||
internet_access: bool,
|
||||
/// Address to be used for `window.webxdc.selfAddr` in JS land.
|
||||
self_addr: String,
|
||||
/// Milliseconds to wait before calling `sendUpdate()` again since the last call.
|
||||
/// Should be exposed to `window.sendUpdateInterval` in JS land.
|
||||
send_update_interval: usize,
|
||||
/// Maximum number of bytes accepted for a serialized update object.
|
||||
/// Should be exposed to `window.sendUpdateMaxSize` in JS land.
|
||||
send_update_max_size: usize,
|
||||
}
|
||||
|
||||
impl WebxdcMessageInfo {
|
||||
@@ -51,8 +57,11 @@ impl WebxdcMessageInfo {
|
||||
document,
|
||||
summary,
|
||||
source_code_url,
|
||||
request_integration: _,
|
||||
internet_access,
|
||||
self_addr,
|
||||
send_update_interval,
|
||||
send_update_max_size,
|
||||
} = message.get_webxdc_info(context).await?;
|
||||
|
||||
Ok(Self {
|
||||
@@ -63,6 +72,8 @@ impl WebxdcMessageInfo {
|
||||
source_code_url: maybe_empty_string_to_option(source_code_url),
|
||||
internet_access,
|
||||
self_addr,
|
||||
send_update_interval,
|
||||
send_update_max_size,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,5 +58,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "dist/deltachat.d.ts",
|
||||
"version": "1.151.1"
|
||||
"version": "1.151.2"
|
||||
}
|
||||
|
||||
@@ -90,6 +90,11 @@ impl Ratelimit {
|
||||
pub fn until_can_send(&self) -> Duration {
|
||||
self.until_can_send_at(SystemTime::now())
|
||||
}
|
||||
|
||||
/// Returns minimum possible update interval in milliseconds.
|
||||
pub fn update_interval(&self) -> usize {
|
||||
(self.window.as_millis() as f64 / self.quota) as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -102,6 +107,7 @@ mod tests {
|
||||
|
||||
let mut ratelimit = Ratelimit::new_at(Duration::new(60, 0), 3.0, now);
|
||||
assert!(ratelimit.can_send_at(now));
|
||||
assert_eq!(ratelimit.update_interval(), 20_000);
|
||||
|
||||
// Send burst of 3 messages.
|
||||
ratelimit.send_at(now);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-repl"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/deltachat/deltachat-core-rust"
|
||||
|
||||
@@ -969,9 +969,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
"Arguments <msg-id> <json status update> expected"
|
||||
);
|
||||
let msg_id = MsgId::new(arg1.parse()?);
|
||||
context
|
||||
.send_webxdc_status_update(msg_id, arg2, "this is a webxdc status update")
|
||||
.await?;
|
||||
context.send_webxdc_status_update(msg_id, arg2).await?;
|
||||
}
|
||||
"videochat" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deltachat-rpc-client"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
description = "Python client for Delta Chat core JSON-RPC interface"
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
|
||||
@@ -61,6 +61,8 @@ class EventType(str, Enum):
|
||||
WEBXDC_INSTANCE_DELETED = "WebxdcInstanceDeleted"
|
||||
CHATLIST_CHANGED = "ChatlistChanged"
|
||||
CHATLIST_ITEM_CHANGED = "ChatlistItemChanged"
|
||||
ACCOUNTS_CHANGED = "AccountsChanged"
|
||||
ACCOUNTS_ITEM_CHANGED = "AccountsItemChanged"
|
||||
CONFIG_SYNCED = "ConfigSynced"
|
||||
WEBXDC_REALTIME_DATA = "WebxdcRealtimeData"
|
||||
WEBXDC_REALTIME_ADVERTISEMENT_RECEIVED = "WebxdcRealtimeAdvertisementReceived"
|
||||
|
||||
29
deltachat-rpc-client/tests/test_account_events.py
Normal file
29
deltachat-rpc-client/tests/test_account_events.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from deltachat_rpc_client import EventType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from deltachat_rpc_client.pytestplugin import ACFactory
|
||||
|
||||
|
||||
def test_event_on_configuration(acfactory: ACFactory) -> None:
|
||||
"""
|
||||
Test if ACCOUNTS_ITEM_CHANGED event is emitted on configure
|
||||
"""
|
||||
|
||||
account = acfactory.new_preconfigured_account()
|
||||
account.clear_all_events()
|
||||
assert not account.is_configured()
|
||||
future = account.configure.future()
|
||||
while True:
|
||||
event = account.wait_for_event()
|
||||
if event.kind == EventType.ACCOUNTS_ITEM_CHANGED:
|
||||
break
|
||||
assert account.is_configured()
|
||||
|
||||
future()
|
||||
|
||||
|
||||
# other tests are written in rust: src/tests/account_events.rs
|
||||
@@ -25,6 +25,8 @@ def test_webxdc(acfactory) -> None:
|
||||
"sourceCodeUrl": None,
|
||||
"summary": None,
|
||||
"selfAddr": webxdc_info["selfAddr"],
|
||||
"sendUpdateInterval": 1000,
|
||||
"sendUpdateMaxSize": 18874368,
|
||||
}
|
||||
|
||||
status_updates = message.get_webxdc_status_updates()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
description = "DeltaChat JSON-RPC server"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "index.d.ts",
|
||||
"version": "1.151.1"
|
||||
"version": "1.151.2"
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ module.exports = {
|
||||
DC_DOWNLOAD_IN_PROGRESS: 1000,
|
||||
DC_DOWNLOAD_UNDECIPHERABLE: 30,
|
||||
DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE: 2200,
|
||||
DC_EVENT_ACCOUNTS_CHANGED: 2302,
|
||||
DC_EVENT_ACCOUNTS_ITEM_CHANGED: 2303,
|
||||
DC_EVENT_CHANNEL_OVERFLOW: 2400,
|
||||
DC_EVENT_CHATLIST_CHANGED: 2300,
|
||||
DC_EVENT_CHATLIST_ITEM_CHANGED: 2301,
|
||||
|
||||
@@ -44,5 +44,7 @@ module.exports = {
|
||||
2200: 'DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE',
|
||||
2300: 'DC_EVENT_CHATLIST_CHANGED',
|
||||
2301: 'DC_EVENT_CHATLIST_ITEM_CHANGED',
|
||||
2302: 'DC_EVENT_ACCOUNTS_CHANGED',
|
||||
2303: 'DC_EVENT_ACCOUNTS_ITEM_CHANGED',
|
||||
2400: 'DC_EVENT_CHANNEL_OVERFLOW'
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ export enum C {
|
||||
DC_DOWNLOAD_IN_PROGRESS = 1000,
|
||||
DC_DOWNLOAD_UNDECIPHERABLE = 30,
|
||||
DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE = 2200,
|
||||
DC_EVENT_ACCOUNTS_CHANGED = 2302,
|
||||
DC_EVENT_ACCOUNTS_ITEM_CHANGED = 2303,
|
||||
DC_EVENT_CHANNEL_OVERFLOW = 2400,
|
||||
DC_EVENT_CHATLIST_CHANGED = 2300,
|
||||
DC_EVENT_CHATLIST_ITEM_CHANGED = 2301,
|
||||
@@ -353,5 +355,7 @@ export const EventId2EventName: { [key: number]: string } = {
|
||||
2200: 'DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE',
|
||||
2300: 'DC_EVENT_CHATLIST_CHANGED',
|
||||
2301: 'DC_EVENT_CHATLIST_ITEM_CHANGED',
|
||||
2302: 'DC_EVENT_ACCOUNTS_CHANGED',
|
||||
2303: 'DC_EVENT_ACCOUNTS_ITEM_CHANGED',
|
||||
2400: 'DC_EVENT_CHANNEL_OVERFLOW',
|
||||
}
|
||||
|
||||
@@ -55,5 +55,5 @@
|
||||
"test:mocha": "mocha node/test/test.mjs --growl --reporter=spec --bail --exit"
|
||||
},
|
||||
"types": "node/dist/index.d.ts",
|
||||
"version": "1.151.1"
|
||||
"version": "1.151.2"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deltachat"
|
||||
version = "1.151.1"
|
||||
version = "1.151.2"
|
||||
description = "Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat"
|
||||
readme = "README.rst"
|
||||
requires-python = ">=3.7"
|
||||
|
||||
@@ -1 +1 @@
|
||||
2024-11-24
|
||||
2024-11-26
|
||||
@@ -7,7 +7,7 @@ set -euo pipefail
|
||||
#
|
||||
# Avoid using rustup here as it depends on reading /proc/self/exe and
|
||||
# has problems running under QEMU.
|
||||
RUST_VERSION=1.82.0
|
||||
RUST_VERSION=1.83.0
|
||||
|
||||
ARCH="$(uname -m)"
|
||||
test -f "/lib/libc.musl-$ARCH.so.1" && LIBC=musl || LIBC=gnu
|
||||
|
||||
@@ -139,6 +139,7 @@ impl Accounts {
|
||||
ctx.open("".to_string()).await?;
|
||||
|
||||
self.accounts.insert(account_config.id, ctx);
|
||||
self.emit_event(EventType::AccountsChanged);
|
||||
|
||||
Ok(account_config.id)
|
||||
}
|
||||
@@ -156,6 +157,7 @@ impl Accounts {
|
||||
.build()
|
||||
.await?;
|
||||
self.accounts.insert(account_config.id, ctx);
|
||||
self.emit_event(EventType::AccountsChanged);
|
||||
|
||||
Ok(account_config.id)
|
||||
}
|
||||
@@ -190,6 +192,7 @@ impl Accounts {
|
||||
.context("failed to remove account data")?;
|
||||
}
|
||||
self.config.remove_account(id).await?;
|
||||
self.emit_event(EventType::AccountsChanged);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
102
src/chat.rs
102
src/chat.rs
@@ -567,6 +567,14 @@ impl ChatId {
|
||||
contact_id: Option<ContactId>,
|
||||
timestamp_sort: i64,
|
||||
) -> Result<()> {
|
||||
if contact_id == Some(ContactId::SELF) {
|
||||
// Do not add protection messages to Saved Messages chat.
|
||||
// This chat never gets protected and unprotected,
|
||||
// we do not want the first message
|
||||
// to be a protection message with an arbitrary timestamp.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let text = context.stock_protection_msg(protect, contact_id).await;
|
||||
let cmd = match protect {
|
||||
ProtectionStatus::Protected => SystemMessage::ChatProtectionEnabled,
|
||||
@@ -2202,7 +2210,9 @@ impl Chat {
|
||||
msg.id = MsgId::new(u32::try_from(raw_id)?);
|
||||
|
||||
maybe_set_logging_xdc(context, msg, self.id).await?;
|
||||
context.update_webxdc_integration_database(msg).await?;
|
||||
context
|
||||
.update_webxdc_integration_database(msg, context)
|
||||
.await?;
|
||||
}
|
||||
context.scheduler.interrupt_ephemeral_task().await;
|
||||
Ok(msg.id)
|
||||
@@ -2591,10 +2601,12 @@ impl ChatIdBlocked {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let peerstate = Peerstate::from_addr(context, contact.get_addr()).await?;
|
||||
let protected = peerstate.map_or(false, |p| {
|
||||
p.is_using_verified_key() && p.prefer_encrypt == EncryptPreference::Mutual
|
||||
});
|
||||
let protected = contact_id == ContactId::SELF || {
|
||||
let peerstate = Peerstate::from_addr(context, contact.get_addr()).await?;
|
||||
peerstate.is_some_and(|p| {
|
||||
p.is_using_verified_key() && p.prefer_encrypt == EncryptPreference::Mutual
|
||||
})
|
||||
};
|
||||
let smeared_time = create_smeared_timestamp(context);
|
||||
|
||||
let chat_id = context
|
||||
@@ -2977,8 +2989,8 @@ pub(crate) async fn create_send_msg_jobs(context: &Context, msg: &mut Message) -
|
||||
recipients.push(from);
|
||||
}
|
||||
|
||||
// Webxdc integrations are messages, however, shipped with main app and must not be sent out
|
||||
if msg.param.get_int(Param::WebxdcIntegration).is_some() {
|
||||
// Default Webxdc integrations are hidden messages and must not be sent out
|
||||
if msg.param.get_int(Param::WebxdcIntegration).is_some() && msg.hidden {
|
||||
recipients.clear();
|
||||
}
|
||||
|
||||
@@ -4645,9 +4657,9 @@ impl Context {
|
||||
Contact::create_ex(self, Nosync, to, addr).await?;
|
||||
return Ok(());
|
||||
}
|
||||
let contact_id = Contact::lookup_id_by_addr_ex(self, addr, Origin::Unknown, None)
|
||||
.await?
|
||||
.with_context(|| format!("No contact for addr '{addr}'"))?;
|
||||
let addr = ContactAddress::new(addr).context("Invalid address")?;
|
||||
let (contact_id, _) =
|
||||
Contact::add_or_lookup(self, "", &addr, Origin::Hidden).await?;
|
||||
match action {
|
||||
SyncAction::Block => {
|
||||
return contact::set_blocked(self, Nosync, contact_id, true).await
|
||||
@@ -4657,9 +4669,10 @@ impl Context {
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
ChatIdBlocked::lookup_by_contact(self, contact_id)
|
||||
// Use `Request` so that even if the program crashes, the user doesn't have to look
|
||||
// into the blocked contacts.
|
||||
ChatIdBlocked::get_for_contact(self, contact_id, Blocked::Request)
|
||||
.await?
|
||||
.with_context(|| format!("No chat for addr '{addr}'"))?
|
||||
.id
|
||||
}
|
||||
SyncId::Grpid(grpid) => {
|
||||
@@ -7465,6 +7478,71 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sync_accept_before_first_msg() -> Result<()> {
|
||||
let alice0 = &TestContext::new_alice().await;
|
||||
let alice1 = &TestContext::new_alice().await;
|
||||
for a in [alice0, alice1] {
|
||||
a.set_config_bool(Config::SyncMsgs, true).await?;
|
||||
}
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
let ba_chat = bob.create_chat(alice0).await;
|
||||
let sent_msg = bob.send_text(ba_chat.id, "hi").await;
|
||||
let a0b_chat_id = alice0.recv_msg(&sent_msg).await.chat_id;
|
||||
assert_eq!(alice0.get_chat(&bob).await.blocked, Blocked::Request);
|
||||
a0b_chat_id.accept(alice0).await?;
|
||||
let a0b_contact = alice0.add_or_lookup_contact(&bob).await;
|
||||
assert_eq!(a0b_contact.origin, Origin::CreateChat);
|
||||
assert_eq!(alice0.get_chat(&bob).await.blocked, Blocked::Not);
|
||||
|
||||
sync(alice0, alice1).await;
|
||||
let a1b_contact = alice1.add_or_lookup_contact(&bob).await;
|
||||
assert_eq!(a1b_contact.origin, Origin::CreateChat);
|
||||
let a1b_chat = alice1.get_chat(&bob).await;
|
||||
assert_eq!(a1b_chat.blocked, Blocked::Not);
|
||||
let chats = Chatlist::try_load(alice1, 0, None, None).await?;
|
||||
assert_eq!(chats.len(), 1);
|
||||
|
||||
let rcvd_msg = alice1.recv_msg(&sent_msg).await;
|
||||
assert_eq!(rcvd_msg.chat_id, a1b_chat.id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sync_block_before_first_msg() -> Result<()> {
|
||||
let alice0 = &TestContext::new_alice().await;
|
||||
let alice1 = &TestContext::new_alice().await;
|
||||
for a in [alice0, alice1] {
|
||||
a.set_config_bool(Config::SyncMsgs, true).await?;
|
||||
}
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
let ba_chat = bob.create_chat(alice0).await;
|
||||
let sent_msg = bob.send_text(ba_chat.id, "hi").await;
|
||||
let a0b_chat_id = alice0.recv_msg(&sent_msg).await.chat_id;
|
||||
assert_eq!(alice0.get_chat(&bob).await.blocked, Blocked::Request);
|
||||
a0b_chat_id.block(alice0).await?;
|
||||
let a0b_contact = alice0.add_or_lookup_contact(&bob).await;
|
||||
assert_eq!(a0b_contact.origin, Origin::IncomingUnknownFrom);
|
||||
assert_eq!(alice0.get_chat(&bob).await.blocked, Blocked::Yes);
|
||||
|
||||
sync(alice0, alice1).await;
|
||||
let a1b_contact = alice1.add_or_lookup_contact(&bob).await;
|
||||
assert_eq!(a1b_contact.origin, Origin::Hidden);
|
||||
assert!(ChatIdBlocked::lookup_by_contact(alice1, a1b_contact.id)
|
||||
.await?
|
||||
.is_none());
|
||||
|
||||
let rcvd_msg = alice1.recv_msg(&sent_msg).await;
|
||||
let a1b_contact = alice1.add_or_lookup_contact(&bob).await;
|
||||
assert_eq!(a1b_contact.origin, Origin::IncomingUnknownFrom);
|
||||
let a1b_chat = alice1.get_chat(&bob).await;
|
||||
assert_eq!(a1b_chat.blocked, Blocked::Yes);
|
||||
assert_eq!(rcvd_msg.chat_id, a1b_chat.id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sync_adhoc_grp() -> Result<()> {
|
||||
let alice0 = &TestContext::new_alice().await;
|
||||
|
||||
@@ -791,6 +791,12 @@ impl Context {
|
||||
self.sql.set_raw_config(key.as_ref(), value).await?;
|
||||
}
|
||||
}
|
||||
if matches!(
|
||||
key,
|
||||
Config::Displayname | Config::Selfavatar | Config::PrivateTag
|
||||
) {
|
||||
self.emit_event(EventType::AccountsItemChanged);
|
||||
}
|
||||
if key.is_synced() {
|
||||
self.emit_event(EventType::ConfigSynced { key });
|
||||
}
|
||||
|
||||
@@ -36,10 +36,10 @@ use crate::message::Message;
|
||||
use crate::oauth2::get_oauth2_addr;
|
||||
use crate::provider::{Protocol, Socket, UsernamePattern};
|
||||
use crate::smtp::Smtp;
|
||||
use crate::stock_str;
|
||||
use crate::sync::Sync::*;
|
||||
use crate::tools::time;
|
||||
use crate::{chat, e2ee, provider};
|
||||
use crate::{stock_str, EventType};
|
||||
use deltachat_contact_tools::addr_cmp;
|
||||
|
||||
macro_rules! progress {
|
||||
@@ -486,6 +486,7 @@ async fn configure(ctx: &Context, param: &EnteredLoginParam) -> Result<Configure
|
||||
update_device_chats_handle.await??;
|
||||
|
||||
ctx.sql.set_raw_config_bool("configured", true).await?;
|
||||
ctx.emit_event(EventType::AccountsItemChanged);
|
||||
|
||||
Ok(configured_param)
|
||||
}
|
||||
|
||||
@@ -67,19 +67,15 @@ fn parse_server<B: BufRead>(
|
||||
|
||||
let typ = server_event
|
||||
.attributes()
|
||||
.find(|attr| {
|
||||
attr.as_ref()
|
||||
.map(|a| {
|
||||
String::from_utf8_lossy(a.key.as_ref())
|
||||
.trim()
|
||||
.to_lowercase()
|
||||
== "type"
|
||||
})
|
||||
.unwrap_or_default()
|
||||
.find_map(|attr| {
|
||||
attr.ok().filter(|a| {
|
||||
String::from_utf8_lossy(a.key.as_ref())
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("type")
|
||||
})
|
||||
})
|
||||
.map(|typ| {
|
||||
typ.unwrap()
|
||||
.decode_and_unescape_value(reader.decoder())
|
||||
typ.decode_and_unescape_value(reader.decoder())
|
||||
.unwrap_or_default()
|
||||
.to_lowercase()
|
||||
})
|
||||
|
||||
@@ -3244,4 +3244,20 @@ Until the false-positive is fixed:
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_self_is_verified() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
|
||||
let contact = Contact::get_by_id(&alice, ContactId::SELF).await?;
|
||||
assert_eq!(contact.is_verified(&alice).await?, true);
|
||||
assert!(contact.is_profile_verified(&alice).await?);
|
||||
assert!(contact.get_verifier_id(&alice).await?.is_none());
|
||||
|
||||
let chat_id = ChatId::get_for_contact(&alice, ContactId::SELF).await?;
|
||||
assert!(chat_id.is_protected(&alice).await.unwrap() == ProtectionStatus::Protected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use async_channel::{self as channel, Receiver, Sender};
|
||||
use pgp::types::PublicKeyTrait;
|
||||
use pgp::SignedPublicKey;
|
||||
use ratelimit::Ratelimit;
|
||||
use tokio::sync::{Mutex, Notify, OnceCell, RwLock};
|
||||
use tokio::sync::{Mutex, Notify, RwLock};
|
||||
|
||||
use crate::aheader::EncryptPreference;
|
||||
use crate::chat::{get_chat_cnt, ChatId, ProtectionStatus};
|
||||
@@ -276,7 +276,7 @@ pub struct InnerContext {
|
||||
/// The text of the last error logged and emitted as an event.
|
||||
/// If the ui wants to display an error after a failure,
|
||||
/// `last_error` should be used to avoid races with the event thread.
|
||||
pub(crate) last_error: std::sync::RwLock<String>,
|
||||
pub(crate) last_error: parking_lot::RwLock<String>,
|
||||
|
||||
/// If debug logging is enabled, this contains all necessary information
|
||||
///
|
||||
@@ -292,7 +292,7 @@ pub struct InnerContext {
|
||||
pub(crate) push_subscribed: AtomicBool,
|
||||
|
||||
/// Iroh for realtime peer channels.
|
||||
pub(crate) iroh: OnceCell<Iroh>,
|
||||
pub(crate) iroh: Arc<RwLock<Option<Iroh>>>,
|
||||
}
|
||||
|
||||
/// The state of ongoing process.
|
||||
@@ -446,11 +446,11 @@ impl Context {
|
||||
metadata: RwLock::new(None),
|
||||
creation_time: tools::Time::now(),
|
||||
last_full_folder_scan: Mutex::new(None),
|
||||
last_error: std::sync::RwLock::new("".to_string()),
|
||||
last_error: parking_lot::RwLock::new("".to_string()),
|
||||
debug_logging: std::sync::RwLock::new(None),
|
||||
push_subscriber,
|
||||
push_subscribed: AtomicBool::new(false),
|
||||
iroh: OnceCell::new(),
|
||||
iroh: Arc::new(RwLock::new(None)),
|
||||
};
|
||||
|
||||
let ctx = Context {
|
||||
@@ -486,6 +486,19 @@ impl Context {
|
||||
/// Stops the IO scheduler.
|
||||
pub async fn stop_io(&self) {
|
||||
self.scheduler.stop(self).await;
|
||||
if let Some(iroh) = self.iroh.write().await.take() {
|
||||
// Close all QUIC connections.
|
||||
|
||||
// Spawn into a separate task,
|
||||
// because Iroh calls `wait_idle()` internally
|
||||
// and it may take time, especially if the network
|
||||
// has become unavailable.
|
||||
tokio::spawn(async move {
|
||||
// We do not log the error because we do not want the task
|
||||
// to hold the reference to Context.
|
||||
let _ = tokio::time::timeout(Duration::from_secs(60), iroh.close()).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Restarts the IO scheduler if it was running before
|
||||
@@ -496,7 +509,7 @@ impl Context {
|
||||
|
||||
/// Indicate that the network likely has come back.
|
||||
pub async fn maybe_network(&self) {
|
||||
if let Some(iroh) = self.iroh.get() {
|
||||
if let Some(ref iroh) = *self.iroh.read().await {
|
||||
iroh.network_change().await;
|
||||
}
|
||||
self.scheduler.maybe_network().await;
|
||||
|
||||
@@ -440,7 +440,7 @@ mod tests {
|
||||
let _sent1 = alice.send_msg(chat_id, &mut instance).await;
|
||||
|
||||
alice
|
||||
.send_webxdc_status_update(instance.id, r#"{"payload":7}"#, "d")
|
||||
.send_webxdc_status_update(instance.id, r#"{"payload":7}"#)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent2 = alice.pop_sent_msg().await;
|
||||
|
||||
@@ -117,6 +117,9 @@ pub enum EventType {
|
||||
|
||||
/// Text to notify.
|
||||
text: String,
|
||||
|
||||
/// Link assigned to this notification, if any.
|
||||
href: Option<String>,
|
||||
},
|
||||
|
||||
/// There is a fresh message. Typically, the user will show an notification
|
||||
@@ -344,6 +347,20 @@ pub enum EventType {
|
||||
chat_id: Option<ChatId>,
|
||||
},
|
||||
|
||||
/// Inform that the list of accounts has changed (an account removed or added or (not yet implemented) the account order changes)
|
||||
///
|
||||
/// This event is only emitted by the account manager
|
||||
AccountsChanged,
|
||||
|
||||
/// Inform that an account property that might be shown in the account list changed, namely:
|
||||
/// - is_configured (see [crate::context::Context::is_configured])
|
||||
/// - displayname
|
||||
/// - selfavatar
|
||||
/// - private_tag
|
||||
///
|
||||
/// This event is emitted from the account whose property changed.
|
||||
AccountsItemChanged,
|
||||
|
||||
/// Event for using in tests, e.g. as a fence between normally generated events.
|
||||
#[cfg(test)]
|
||||
Test,
|
||||
|
||||
@@ -426,6 +426,7 @@ async fn import_backup_stream_inner<R: tokio::io::AsyncRead + Unpin>(
|
||||
if res.is_ok() {
|
||||
context.emit_event(EventType::ImexProgress(999));
|
||||
res = context.sql.run_migrations(context).await;
|
||||
context.emit_event(EventType::AccountsItemChanged);
|
||||
}
|
||||
if res.is_ok() {
|
||||
delete_and_reset_all_device_msgs(context)
|
||||
|
||||
@@ -244,18 +244,14 @@ impl Kml {
|
||||
self.tag = KmlTag::PlacemarkPoint;
|
||||
} else if tag == "coordinates" && self.tag == KmlTag::PlacemarkPoint {
|
||||
self.tag = KmlTag::PlacemarkPointCoordinates;
|
||||
if let Some(acc) = event.attributes().find(|attr| {
|
||||
attr.as_ref()
|
||||
.map(|a| {
|
||||
String::from_utf8_lossy(a.key.as_ref())
|
||||
.trim()
|
||||
.to_lowercase()
|
||||
== "accuracy"
|
||||
})
|
||||
.unwrap_or_default()
|
||||
if let Some(acc) = event.attributes().find_map(|attr| {
|
||||
attr.ok().filter(|a| {
|
||||
String::from_utf8_lossy(a.key.as_ref())
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("accuracy")
|
||||
})
|
||||
}) {
|
||||
let v = acc
|
||||
.unwrap()
|
||||
.decode_and_unescape_value(reader.decoder())
|
||||
.unwrap_or_default();
|
||||
|
||||
|
||||
@@ -50,13 +50,13 @@ impl Context {
|
||||
/// Set last error string.
|
||||
/// Implemented as blocking as used from macros in different, not always async blocks.
|
||||
pub fn set_last_error(&self, error: &str) {
|
||||
let mut last_error = self.last_error.write().unwrap();
|
||||
let mut last_error = self.last_error.write();
|
||||
*last_error = error.to_string();
|
||||
}
|
||||
|
||||
/// Get last error string.
|
||||
pub fn get_last_error(&self) -> String {
|
||||
let last_error = &*self.last_error.read().unwrap();
|
||||
let last_error = &*self.last_error.read();
|
||||
last_error.clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use anyhow::{bail, Context as _, Result};
|
||||
use base64::Engine as _;
|
||||
use chrono::TimeZone;
|
||||
use email::Mailbox;
|
||||
use format_flowed::{format_flowed, format_flowed_quote};
|
||||
use lettre_email::{Address, Header, MimeMultipartType, PartBuilder};
|
||||
use tokio::fs;
|
||||
|
||||
@@ -1300,9 +1299,18 @@ impl MimeFactory {
|
||||
|
||||
let final_text = placeholdertext.as_deref().unwrap_or(&msg.text);
|
||||
|
||||
let mut quoted_text = msg
|
||||
.quoted_text()
|
||||
.map(|quote| format_flowed_quote("e) + "\r\n\r\n");
|
||||
let mut quoted_text = None;
|
||||
if let Some(msg_quoted_text) = msg.quoted_text() {
|
||||
let mut some_quoted_text = String::new();
|
||||
for quoted_line in msg_quoted_text.split('\n') {
|
||||
some_quoted_text += "> ";
|
||||
some_quoted_text += quoted_line;
|
||||
some_quoted_text += "\r\n";
|
||||
}
|
||||
some_quoted_text += "\r\n";
|
||||
quoted_text = Some(some_quoted_text)
|
||||
}
|
||||
|
||||
if !is_encrypted && msg.param.get_bool(Param::ProtectQuote).unwrap_or_default() {
|
||||
// Message is not encrypted but quotes encrypted message.
|
||||
quoted_text = Some("> ...\r\n\r\n".to_string());
|
||||
@@ -1312,7 +1320,6 @@ impl MimeFactory {
|
||||
// Delta Chat.
|
||||
quoted_text = Some("\r\n".to_string());
|
||||
}
|
||||
let flowed_text = format_flowed(final_text);
|
||||
|
||||
let is_reaction = msg.param.get_int(Param::Reaction).unwrap_or_default() != 0;
|
||||
|
||||
@@ -1322,7 +1329,7 @@ impl MimeFactory {
|
||||
"{}{}{}{}{}{}",
|
||||
fwdhint.unwrap_or_default(),
|
||||
quoted_text.unwrap_or_default(),
|
||||
escape_message_footer_marks(&flowed_text),
|
||||
escape_message_footer_marks(final_text),
|
||||
if !final_text.is_empty() && !footer.is_empty() {
|
||||
"\r\n\r\n"
|
||||
} else {
|
||||
@@ -1332,10 +1339,8 @@ impl MimeFactory {
|
||||
footer
|
||||
);
|
||||
|
||||
let mut main_part = PartBuilder::new().header((
|
||||
"Content-Type",
|
||||
"text/plain; charset=utf-8; format=flowed; delsp=no",
|
||||
));
|
||||
let mut main_part =
|
||||
PartBuilder::new().header(("Content-Type", "text/plain; charset=utf-8"));
|
||||
main_part = self.add_message_text(main_part, message_text);
|
||||
|
||||
if is_reaction {
|
||||
|
||||
@@ -1667,10 +1667,11 @@ impl MimeMessage {
|
||||
{
|
||||
let mut to_list =
|
||||
get_all_addresses_from_header(&report.headers, "x-failed-recipients");
|
||||
let to = if to_list.len() == 1 {
|
||||
Some(to_list.pop().unwrap())
|
||||
let to = if to_list.len() != 1 {
|
||||
// We do not know which recipient failed
|
||||
None
|
||||
} else {
|
||||
None // We do not know which recipient failed
|
||||
to_list.pop()
|
||||
};
|
||||
|
||||
return Ok(Some(DeliveryReport {
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
//! Adding peer channels to webxdc needs upfront negotation of a topic and sharing of public keys so that
|
||||
//! nodes can connect to each other. The explicit approach is as follows:
|
||||
//!
|
||||
//! 1. We introduce a new [GossipTopic](crate::headerdef::HeaderDef::IrohGossipTopic) message header with a random 32-byte TopicId,
|
||||
//! 1. We introduce a new [`IrohGossipTopic`](crate::headerdef::HeaderDef::IrohGossipTopic) message header with a random 32-byte TopicId,
|
||||
//! securely generated on the initial webxdc sender's device. This message header is encrypted
|
||||
//! and sent in the same message as the webxdc application.
|
||||
//! 2. Whenever `joinRealtimeChannel().setListener()` or `joinRealtimeChannel().send()` is called by the webxdc application,
|
||||
//! we start a routine to establish p2p connectivity and join the gossip swarm with Iroh.
|
||||
//! 3. The first step of this routine is to introduce yourself with a regular message containing the `IrohPublicKey`.
|
||||
//! 3. The first step of this routine is to introduce yourself with a regular message containing the [`IrohNodeAddr`](crate::headerdef::HeaderDef::IrohNodeAddr).
|
||||
//! This message contains the users relay-server and public key.
|
||||
//! Direct IP address is not included as this information can be persisted by email providers.
|
||||
//! 4. After the announcement, the sending peer joins the gossip swarm with an empty list of peer IDs (as they don't know anyone yet).
|
||||
@@ -78,6 +78,14 @@ impl Iroh {
|
||||
self.endpoint.network_change().await
|
||||
}
|
||||
|
||||
/// Closes the QUIC endpoint.
|
||||
pub(crate) async fn close(self) -> Result<()> {
|
||||
self.endpoint
|
||||
.close(0u32.into(), b"")
|
||||
.await
|
||||
.context("Closing iroh endpoint failed")
|
||||
}
|
||||
|
||||
/// Join a topic and create the subscriber loop for it.
|
||||
///
|
||||
/// If there is no gossip, create it.
|
||||
@@ -285,15 +293,36 @@ impl Context {
|
||||
}
|
||||
|
||||
/// Get or initialize the iroh peer channel.
|
||||
pub async fn get_or_try_init_peer_channel(&self) -> Result<&Iroh> {
|
||||
pub async fn get_or_try_init_peer_channel(
|
||||
&self,
|
||||
) -> Result<tokio::sync::RwLockReadGuard<'_, Iroh>> {
|
||||
if !self.get_config_bool(Config::WebxdcRealtimeEnabled).await? {
|
||||
bail!("Attempt to get Iroh when realtime is disabled");
|
||||
}
|
||||
|
||||
let ctx = self.clone();
|
||||
self.iroh
|
||||
.get_or_try_init(|| async { ctx.init_peer_channels().await })
|
||||
.await
|
||||
if let Ok(lock) = tokio::sync::RwLockReadGuard::<'_, std::option::Option<Iroh>>::try_map(
|
||||
self.iroh.read().await,
|
||||
|opt_iroh| opt_iroh.as_ref(),
|
||||
) {
|
||||
return Ok(lock);
|
||||
}
|
||||
|
||||
let lock = self.iroh.write().await;
|
||||
match tokio::sync::RwLockWriteGuard::<'_, std::option::Option<Iroh>>::try_downgrade_map(
|
||||
lock,
|
||||
|opt_iroh| opt_iroh.as_ref(),
|
||||
) {
|
||||
Ok(lock) => Ok(lock),
|
||||
Err(mut lock) => {
|
||||
let iroh = self.init_peer_channels().await?;
|
||||
*lock = Some(iroh);
|
||||
tokio::sync::RwLockWriteGuard::<'_, std::option::Option<Iroh>>::try_downgrade_map(
|
||||
lock,
|
||||
|opt_iroh| opt_iroh.as_ref(),
|
||||
)
|
||||
.map_err(|_| anyhow!("Downgrade should succeed as we just stored `Some` value"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,7 +655,6 @@ mod tests {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let bob_iroh = bob.get_or_try_init_peer_channel().await.unwrap();
|
||||
|
||||
// Bob adds alice to gossip peers.
|
||||
let members = get_iroh_gossip_peers(bob, bob_webxdc.id)
|
||||
@@ -636,13 +664,23 @@ mod tests {
|
||||
.map(|addr| addr.node_id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let alice_iroh = alice.get_or_try_init_peer_channel().await.unwrap();
|
||||
assert_eq!(
|
||||
members,
|
||||
vec![alice_iroh.get_node_addr().await.unwrap().node_id]
|
||||
vec![
|
||||
alice
|
||||
.get_or_try_init_peer_channel()
|
||||
.await
|
||||
.unwrap()
|
||||
.get_node_addr()
|
||||
.await
|
||||
.unwrap()
|
||||
.node_id
|
||||
]
|
||||
);
|
||||
|
||||
bob_iroh
|
||||
bob.get_or_try_init_peer_channel()
|
||||
.await
|
||||
.unwrap()
|
||||
.join_and_subscribe_gossip(bob, bob_webxdc.id)
|
||||
.await
|
||||
.unwrap()
|
||||
@@ -651,7 +689,10 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
// Alice sends ephemeral message
|
||||
alice_iroh
|
||||
alice
|
||||
.get_or_try_init_peer_channel()
|
||||
.await
|
||||
.unwrap()
|
||||
.send_webxdc_realtime_data(alice, alice_webxdc.id, "alice -> bob".as_bytes().to_vec())
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -670,7 +711,9 @@ mod tests {
|
||||
}
|
||||
}
|
||||
// Bob sends ephemeral message
|
||||
bob_iroh
|
||||
bob.get_or_try_init_peer_channel()
|
||||
.await
|
||||
.unwrap()
|
||||
.send_webxdc_realtime_data(bob, bob_webxdc.id, "bob -> alice".as_bytes().to_vec())
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -699,10 +742,20 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
members,
|
||||
vec![bob_iroh.get_node_addr().await.unwrap().node_id]
|
||||
vec![
|
||||
bob.get_or_try_init_peer_channel()
|
||||
.await
|
||||
.unwrap()
|
||||
.get_node_addr()
|
||||
.await
|
||||
.unwrap()
|
||||
.node_id
|
||||
]
|
||||
);
|
||||
|
||||
bob_iroh
|
||||
bob.get_or_try_init_peer_channel()
|
||||
.await
|
||||
.unwrap()
|
||||
.send_webxdc_realtime_data(bob, bob_webxdc.id, "bob -> alice 2".as_bytes().to_vec())
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -720,6 +773,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calling stop_io() closes iroh endpoint as well,
|
||||
// even though I/O was not started in this test.
|
||||
assert!(alice.iroh.read().await.is_some());
|
||||
alice.stop_io().await;
|
||||
assert!(alice.iroh.read().await.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
@@ -761,7 +820,6 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
bob.recv_msg_trash(&alice.pop_sent_msg().await).await;
|
||||
let bob_iroh = bob.get_or_try_init_peer_channel().await.unwrap();
|
||||
|
||||
// Bob adds alice to gossip peers.
|
||||
let members = get_iroh_gossip_peers(bob, bob_webxdc.id)
|
||||
@@ -771,13 +829,23 @@ mod tests {
|
||||
.map(|addr| addr.node_id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let alice_iroh = alice.get_or_try_init_peer_channel().await.unwrap();
|
||||
assert_eq!(
|
||||
members,
|
||||
vec![alice_iroh.get_node_addr().await.unwrap().node_id]
|
||||
vec![
|
||||
alice
|
||||
.get_or_try_init_peer_channel()
|
||||
.await
|
||||
.unwrap()
|
||||
.get_node_addr()
|
||||
.await
|
||||
.unwrap()
|
||||
.node_id
|
||||
]
|
||||
);
|
||||
|
||||
bob_iroh
|
||||
bob.get_or_try_init_peer_channel()
|
||||
.await
|
||||
.unwrap()
|
||||
.join_and_subscribe_gossip(bob, bob_webxdc.id)
|
||||
.await
|
||||
.unwrap()
|
||||
@@ -786,7 +854,10 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
// Alice sends ephemeral message
|
||||
alice_iroh
|
||||
alice
|
||||
.get_or_try_init_peer_channel()
|
||||
.await
|
||||
.unwrap()
|
||||
.send_webxdc_realtime_data(alice, alice_webxdc.id, "alice -> bob".as_bytes().to_vec())
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -811,7 +882,9 @@ mod tests {
|
||||
.unwrap();
|
||||
let bob_sequence_number = bob
|
||||
.iroh
|
||||
.get()
|
||||
.read()
|
||||
.await
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.sequence_numbers
|
||||
.lock()
|
||||
@@ -820,7 +893,9 @@ mod tests {
|
||||
leave_webxdc_realtime(bob, bob_webxdc.id).await.unwrap();
|
||||
let bob_sequence_number_after = bob
|
||||
.iroh
|
||||
.get()
|
||||
.read()
|
||||
.await
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.sequence_numbers
|
||||
.lock()
|
||||
@@ -829,7 +904,9 @@ mod tests {
|
||||
// Check that sequence number is persisted when leaving the channel.
|
||||
assert_eq!(bob_sequence_number, bob_sequence_number_after);
|
||||
|
||||
bob_iroh
|
||||
bob.get_or_try_init_peer_channel()
|
||||
.await
|
||||
.unwrap()
|
||||
.join_and_subscribe_gossip(bob, bob_webxdc.id)
|
||||
.await
|
||||
.unwrap()
|
||||
@@ -837,7 +914,9 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
bob_iroh
|
||||
bob.get_or_try_init_peer_channel()
|
||||
.await
|
||||
.unwrap()
|
||||
.send_webxdc_realtime_data(bob, bob_webxdc.id, "bob -> alice".as_bytes().to_vec())
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -860,7 +939,16 @@ mod tests {
|
||||
// bob for example does not change the channels because he never sends an
|
||||
// advertisement
|
||||
assert_eq!(
|
||||
alice.iroh.get().unwrap().iroh_channels.read().await.len(),
|
||||
alice
|
||||
.iroh
|
||||
.read()
|
||||
.await
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.iroh_channels
|
||||
.read()
|
||||
.await
|
||||
.len(),
|
||||
1
|
||||
);
|
||||
leave_webxdc_realtime(alice, alice_webxdc.id).await.unwrap();
|
||||
@@ -870,7 +958,9 @@ mod tests {
|
||||
.unwrap();
|
||||
assert!(alice
|
||||
.iroh
|
||||
.get()
|
||||
.read()
|
||||
.await
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.iroh_channels
|
||||
.read()
|
||||
@@ -963,19 +1053,19 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(alice.ctx.iroh.get().is_none());
|
||||
assert!(alice.ctx.iroh.read().await.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());
|
||||
assert!(alice.ctx.iroh.read().await.is_none());
|
||||
|
||||
// creates iroh endpoint as side effect
|
||||
leave_webxdc_realtime(alice, MsgId::new(1)).await.unwrap();
|
||||
|
||||
assert!(alice.ctx.iroh.get().is_none());
|
||||
assert!(alice.ctx.iroh.read().await.is_none());
|
||||
|
||||
// This internal function should return error
|
||||
// if accidentally called with the setting disabled.
|
||||
|
||||
@@ -542,6 +542,8 @@ impl Peerstate {
|
||||
/// * `old_addr`: Old address of the peerstate in case of an AEAP transition.
|
||||
pub(crate) async fn save_to_db_ex(&self, sql: &Sql, old_addr: Option<&str>) -> Result<()> {
|
||||
let trans_fn = |t: &mut rusqlite::Transaction| {
|
||||
let verified_key_fingerprint =
|
||||
self.verified_key_fingerprint.as_ref().map(|fp| fp.hex());
|
||||
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
|
||||
@@ -551,11 +553,14 @@ impl Peerstate {
|
||||
// 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).
|
||||
//
|
||||
// NB: We check that `verified_key_fingerprint` hasn't changed to protect from
|
||||
// possible races.
|
||||
t.execute(
|
||||
"UPDATE acpeerstates \
|
||||
SET verified_key=NULL, verified_key_fingerprint='', verifier='' \
|
||||
WHERE addr=?",
|
||||
(old_addr,),
|
||||
"UPDATE acpeerstates
|
||||
SET verified_key=NULL, verified_key_fingerprint='', verifier=''
|
||||
WHERE addr=? AND verified_key_fingerprint=?",
|
||||
(old_addr, &verified_key_fingerprint),
|
||||
)?;
|
||||
}
|
||||
t.execute(
|
||||
@@ -604,7 +609,7 @@ impl Peerstate {
|
||||
self.public_key_fingerprint.as_ref().map(|fp| fp.hex()),
|
||||
self.gossip_key_fingerprint.as_ref().map(|fp| fp.hex()),
|
||||
self.verified_key.as_ref().map(|k| k.to_bytes()),
|
||||
self.verified_key_fingerprint.as_ref().map(|fp| fp.hex()),
|
||||
&verified_key_fingerprint,
|
||||
self.verifier.as_deref().unwrap_or(""),
|
||||
self.secondary_verified_key.as_ref().map(|k| k.to_bytes()),
|
||||
self.secondary_verified_key_fingerprint
|
||||
|
||||
@@ -558,7 +558,12 @@ Here's my footer -- bob@example.net"
|
||||
) -> Result<()> {
|
||||
let event = t
|
||||
.evtracker
|
||||
.get_matching(|evt| matches!(evt, EventType::ReactionsChanged { .. }))
|
||||
.get_matching(|evt| {
|
||||
matches!(
|
||||
evt,
|
||||
EventType::ReactionsChanged { .. } | EventType::IncomingMsg { .. }
|
||||
)
|
||||
})
|
||||
.await;
|
||||
match event {
|
||||
EventType::ReactionsChanged {
|
||||
@@ -570,7 +575,7 @@ Here's my footer -- bob@example.net"
|
||||
assert_eq!(msg_id, expected_msg_id);
|
||||
assert_eq!(contact_id, expected_contact_id);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
_ => panic!("Unexpected event {event:?}."),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -583,7 +588,14 @@ Here's my footer -- bob@example.net"
|
||||
) -> Result<()> {
|
||||
let event = t
|
||||
.evtracker
|
||||
.get_matching(|evt| matches!(evt, EventType::IncomingReaction { .. }))
|
||||
// Check for absence of `IncomingMsg` events -- it appeared that it's quite easy to make
|
||||
// bugs when `IncomingMsg` is issued for reactions.
|
||||
.get_matching(|evt| {
|
||||
matches!(
|
||||
evt,
|
||||
EventType::IncomingReaction { .. } | EventType::IncomingMsg { .. }
|
||||
)
|
||||
})
|
||||
.await;
|
||||
match event {
|
||||
EventType::IncomingReaction {
|
||||
@@ -595,16 +607,25 @@ Here's my footer -- bob@example.net"
|
||||
assert_eq!(contact_id, expected_contact_id);
|
||||
assert_eq!(reaction, Reaction::from(expected_reaction));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
_ => panic!("Unexpected event {event:?}."),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn has_incoming_reactions_event(t: &TestContext) -> bool {
|
||||
t.evtracker
|
||||
.get_matching_opt(t, |evt| matches!(evt, EventType::IncomingReaction { .. }))
|
||||
.await
|
||||
.is_some()
|
||||
/// Checks that no unwanted events remain after expecting "wanted" reaction events.
|
||||
async fn expect_no_unwanted_events(t: &TestContext) {
|
||||
let ev = t
|
||||
.evtracker
|
||||
.get_matching_opt(t, |evt| {
|
||||
matches!(
|
||||
evt,
|
||||
EventType::IncomingReaction { .. } | EventType::IncomingMsg { .. }
|
||||
)
|
||||
})
|
||||
.await;
|
||||
if let Some(ev) = ev {
|
||||
panic!("Unwanted event {ev:?}.")
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
@@ -635,9 +656,10 @@ Here's my footer -- bob@example.net"
|
||||
|
||||
bob_msg.chat_id.accept(&bob).await?;
|
||||
|
||||
bob.evtracker.clear_events();
|
||||
send_reaction(&bob, bob_msg.id, "👍").await.unwrap();
|
||||
expect_reactions_changed_event(&bob, bob_msg.chat_id, bob_msg.id, ContactId::SELF).await?;
|
||||
assert!(!has_incoming_reactions_event(&bob).await);
|
||||
expect_no_unwanted_events(&bob).await;
|
||||
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 2);
|
||||
|
||||
let bob_reaction_msg = bob.pop_sent_msg().await;
|
||||
@@ -656,6 +678,7 @@ Here's my footer -- bob@example.net"
|
||||
expect_reactions_changed_event(&alice, chat_alice.id, alice_msg.sender_msg_id, *bob_id)
|
||||
.await?;
|
||||
expect_incoming_reactions_event(&alice, alice_msg.sender_msg_id, *bob_id, "👍").await?;
|
||||
expect_no_unwanted_events(&alice).await;
|
||||
|
||||
// Alice reacts to own message.
|
||||
send_reaction(&alice, alice_msg.sender_msg_id, "👍 😀")
|
||||
@@ -684,6 +707,7 @@ Here's my footer -- bob@example.net"
|
||||
let bob = TestContext::new_bob().await;
|
||||
alice.set_config(Config::Displayname, Some("ALICE")).await?;
|
||||
bob.set_config(Config::Displayname, Some("BOB")).await?;
|
||||
let alice_bob_id = alice.add_or_lookup_contact_id(&bob).await;
|
||||
|
||||
// Alice sends message to Bob
|
||||
let alice_chat = alice.create_chat(&bob).await;
|
||||
@@ -696,7 +720,9 @@ Here's my footer -- bob@example.net"
|
||||
send_reaction(&bob, bob_msg1.id, "👍").await?;
|
||||
let bob_send_reaction = bob.pop_sent_msg().await;
|
||||
alice.recv_msg_trash(&bob_send_reaction).await;
|
||||
assert!(has_incoming_reactions_event(&alice).await);
|
||||
expect_incoming_reactions_event(&alice, alice_msg1.sender_msg_id, alice_bob_id, "👍")
|
||||
.await?;
|
||||
expect_no_unwanted_events(&alice).await;
|
||||
|
||||
let chatlist = Chatlist::try_load(&bob, 0, None, None).await?;
|
||||
let summary = chatlist.get_summary(&bob, 0, None).await?;
|
||||
@@ -711,8 +737,9 @@ Here's my footer -- bob@example.net"
|
||||
SystemTime::shift(Duration::from_secs(10));
|
||||
send_reaction(&alice, alice_msg1.sender_msg_id, "🍿").await?;
|
||||
let alice_send_reaction = alice.pop_sent_msg().await;
|
||||
bob.evtracker.clear_events();
|
||||
bob.recv_msg_opt(&alice_send_reaction).await;
|
||||
assert!(!has_incoming_reactions_event(&bob).await);
|
||||
expect_no_unwanted_events(&bob).await;
|
||||
|
||||
assert_summary(&alice, "You reacted 🍿 to \"Party?\"").await;
|
||||
assert_summary(&bob, "ALICE reacted 🍿 to \"Party?\"").await;
|
||||
@@ -934,7 +961,9 @@ Here's my footer -- bob@example.net"
|
||||
expect_reactions_changed_event(&alice0, chat_id, alice0_msg_id, ContactId::SELF).await?;
|
||||
expect_reactions_changed_event(&alice1, alice1_msg.chat_id, alice1_msg.id, ContactId::SELF)
|
||||
.await?;
|
||||
|
||||
for a in [&alice0, &alice1] {
|
||||
expect_no_unwanted_events(a).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1068,7 +1068,7 @@ async fn test_classic_mailing_list() -> Result<()> {
|
||||
let mime = sent.payload();
|
||||
|
||||
println!("Sent mime message is:\n\n{mime}\n\n");
|
||||
assert!(mime.contains("Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no\r\n"));
|
||||
assert!(mime.contains("Content-Type: text/plain; charset=utf-8\r\n"));
|
||||
assert!(mime.contains("Subject: =?utf-8?q?Re=3A_=5Bdelta-dev=5D_What=27s_up=3F?=\r\n"));
|
||||
assert!(mime.contains("MIME-Version: 1.0\r\n"));
|
||||
assert!(mime.contains("In-Reply-To: <38942@posteo.org>\r\n"));
|
||||
|
||||
47
src/smtp.rs
47
src/smtp.rs
@@ -244,32 +244,27 @@ pub(crate) async fn smtp_send(
|
||||
async_smtp::error::Error::Transient(ref response) => {
|
||||
// We got a transient 4xx response from SMTP server.
|
||||
// Give some time until the server-side error maybe goes away.
|
||||
|
||||
if let Some(first_word) = response.first_word() {
|
||||
if first_word.ends_with(".1.1")
|
||||
|| first_word.ends_with(".1.2")
|
||||
|| first_word.ends_with(".1.3")
|
||||
{
|
||||
// Sometimes we receive transient errors that should be permanent.
|
||||
// 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.");
|
||||
SendResult::Failure(format_err!("Permanent SMTP error: {}", err))
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"Transient error with status code {first_word}, postponing retry for later."
|
||||
);
|
||||
SendResult::Retry
|
||||
}
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"Transient error without status code, postponing retry for later."
|
||||
);
|
||||
SendResult::Retry
|
||||
}
|
||||
//
|
||||
// One particular case is
|
||||
// `450 4.1.2 <alice@example.org>: Recipient address rejected: Domain not found`.
|
||||
// known to be returned by Postfix.
|
||||
//
|
||||
// [RFC 3463](https://tools.ietf.org/html/rfc3463#section-3.2)
|
||||
// says "This code is only useful for permanent failures."
|
||||
// in X.1.1, X.1.2 and X.1.3 descriptions.
|
||||
//
|
||||
// Previous Delta Chat core versions
|
||||
// from 1.51.0 to 1.151.1
|
||||
// were treating such errors as permanent.
|
||||
//
|
||||
// This was later reverted because such errors were observed
|
||||
// for existing domains and turned out to be actually transient,
|
||||
// likely caused by nameserver downtime.
|
||||
info!(
|
||||
context,
|
||||
"Transient error {response:?}, postponing retry for later."
|
||||
);
|
||||
SendResult::Retry
|
||||
}
|
||||
_ => {
|
||||
info!(
|
||||
|
||||
@@ -1070,6 +1070,24 @@ CREATE INDEX msgs_status_updates_index2 ON msgs_status_updates (uid);
|
||||
.await?;
|
||||
}
|
||||
|
||||
inc_and_check(&mut migration_version, 124)?;
|
||||
if dbversion < migration_version {
|
||||
// Mark Saved Messages chat as protected if it already exists.
|
||||
sql.execute_migration(
|
||||
"UPDATE chats
|
||||
SET protected=1 -- ProtectionStatus::Protected
|
||||
WHERE type==100 -- Chattype::Single
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM chats_contacts cc
|
||||
WHERE cc.chat_id==chats.id
|
||||
AND cc.contact_id=1
|
||||
)
|
||||
",
|
||||
migration_version,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let new_version = sql
|
||||
.get_raw_config_int(VERSION_CFG)
|
||||
.await?
|
||||
|
||||
@@ -391,7 +391,7 @@ impl TestContext {
|
||||
Self {
|
||||
ctx,
|
||||
dir,
|
||||
evtracker: EventTracker(evtracker_receiver),
|
||||
evtracker: EventTracker::new(evtracker_receiver),
|
||||
_log_sink,
|
||||
}
|
||||
}
|
||||
@@ -655,8 +655,8 @@ impl TestContext {
|
||||
.expect("failed to load msg")
|
||||
}
|
||||
|
||||
/// Returns the [`Contact`] for the other [`TestContext`], creating it if necessary.
|
||||
pub async fn add_or_lookup_contact(&self, other: &TestContext) -> Contact {
|
||||
/// Returns the [`ContactId`] for the other [`TestContext`], creating a contact if necessary.
|
||||
pub async fn add_or_lookup_contact_id(&self, other: &TestContext) -> ContactId {
|
||||
let primary_self_addr = other.ctx.get_primary_self_addr().await.unwrap();
|
||||
let addr = ContactAddress::new(&primary_self_addr).unwrap();
|
||||
// MailinglistAddress is the lowest allowed origin, we'd prefer to not modify the
|
||||
@@ -670,6 +670,12 @@ impl TestContext {
|
||||
Modifier::Modified => warn!(&self.ctx, "Contact {} modified by TestContext", &addr),
|
||||
Modifier::Created => warn!(&self.ctx, "Contact {} created by TestContext", &addr),
|
||||
}
|
||||
contact_id
|
||||
}
|
||||
|
||||
/// Returns the [`Contact`] for the other [`TestContext`], creating it if necessary.
|
||||
pub async fn add_or_lookup_contact(&self, other: &TestContext) -> Contact {
|
||||
let contact_id = self.add_or_lookup_contact_id(other).await;
|
||||
Contact::get_by_id(&self.ctx, contact_id).await.unwrap()
|
||||
}
|
||||
|
||||
@@ -1087,6 +1093,10 @@ impl DerefMut for EventTracker {
|
||||
}
|
||||
|
||||
impl EventTracker {
|
||||
pub fn new(emitter: EventEmitter) -> Self {
|
||||
Self(emitter)
|
||||
}
|
||||
|
||||
/// Consumes emitted events returning the first matching one.
|
||||
///
|
||||
/// If no matching events are ready this will wait for new events to arrive and time out
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
mod account_events;
|
||||
mod aeap;
|
||||
mod verified_chats;
|
||||
|
||||
170
src/tests/account_events.rs
Normal file
170
src/tests/account_events.rs
Normal file
@@ -0,0 +1,170 @@
|
||||
//! contains tests for account (list) events
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use crate::accounts::Accounts;
|
||||
use crate::config::Config;
|
||||
use crate::imex::{get_backup, has_backup, imex, BackupProvider, ImexMode};
|
||||
use crate::test_utils::{sync, EventTracker, TestContext, TestContextManager};
|
||||
use crate::EventType;
|
||||
|
||||
async fn wait_for_item_changed(context: &TestContext) {
|
||||
context
|
||||
.evtracker
|
||||
.get_matching(|evt| matches!(evt, EventType::AccountsItemChanged))
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_account_event() -> Result<()> {
|
||||
let dir = tempdir().unwrap();
|
||||
let mut manager = Accounts::new(dir.path().join("accounts"), true).await?;
|
||||
let tracker = EventTracker::new(manager.get_event_emitter());
|
||||
|
||||
// create account
|
||||
tracker.clear_events();
|
||||
let account_id = manager.add_account().await?;
|
||||
tracker
|
||||
.get_matching(|evt| matches!(evt, EventType::AccountsChanged))
|
||||
.await;
|
||||
|
||||
// remove account
|
||||
tracker.clear_events();
|
||||
manager.remove_account(account_id).await?;
|
||||
tracker
|
||||
.get_matching(|evt| matches!(evt, EventType::AccountsChanged))
|
||||
.await;
|
||||
|
||||
// create closed account
|
||||
tracker.clear_events();
|
||||
manager.add_closed_account().await?;
|
||||
tracker
|
||||
.get_matching(|evt| matches!(evt, EventType::AccountsChanged))
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// configuration is tested by python tests in deltachat-rpc-client/tests/test_account_events.py
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_displayname() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let context = tcm.alice().await;
|
||||
context.evtracker.clear_events();
|
||||
context
|
||||
.set_config(crate::config::Config::Displayname, Some("🐰 Alice"))
|
||||
.await?;
|
||||
wait_for_item_changed(&context).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_selfavatar() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let context = tcm.alice().await;
|
||||
let file = context.dir.path().join("avatar.jpg");
|
||||
let bytes = include_bytes!("../../test-data/image/avatar1000x1000.jpg");
|
||||
tokio::fs::write(&file, bytes).await?;
|
||||
context.evtracker.clear_events();
|
||||
context
|
||||
.set_config(
|
||||
crate::config::Config::Selfavatar,
|
||||
Some(file.to_str().unwrap()),
|
||||
)
|
||||
.await?;
|
||||
wait_for_item_changed(&context).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_set_private_tag() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let context = tcm.alice().await;
|
||||
context.evtracker.clear_events();
|
||||
context
|
||||
.set_config(crate::config::Config::PrivateTag, Some("Wonderland"))
|
||||
.await?;
|
||||
wait_for_item_changed(&context).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_import_backup() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let context1 = tcm.alice().await;
|
||||
let backup_dir = tempfile::tempdir().unwrap();
|
||||
assert!(
|
||||
imex(&context1, ImexMode::ExportBackup, backup_dir.path(), None)
|
||||
.await
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
let context2 = TestContext::new().await;
|
||||
assert!(!context2.is_configured().await?);
|
||||
context2.evtracker.clear_events();
|
||||
let backup = has_backup(&context2, backup_dir.path()).await?;
|
||||
imex(&context2, ImexMode::ImportBackup, backup.as_ref(), None).await?;
|
||||
assert!(context2.is_configured().await?);
|
||||
wait_for_item_changed(&context2).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_receive_backup() {
|
||||
let mut tcm = TestContextManager::new();
|
||||
// Create first device.
|
||||
let ctx0 = tcm.alice().await;
|
||||
// Prepare to transfer backup.
|
||||
let provider = BackupProvider::prepare(&ctx0).await.unwrap();
|
||||
// Set up second device.
|
||||
let ctx1 = tcm.unconfigured().await;
|
||||
|
||||
ctx1.evtracker.clear_events();
|
||||
get_backup(&ctx1, provider.qr()).await.unwrap();
|
||||
|
||||
// Make sure the provider finishes without an error.
|
||||
tokio::time::timeout(Duration::from_secs(30), provider)
|
||||
.await
|
||||
.expect("timed out")
|
||||
.expect("error in provider");
|
||||
|
||||
wait_for_item_changed(&ctx1).await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_sync() -> Result<()> {
|
||||
let alice0 = TestContext::new_alice().await;
|
||||
let alice1 = TestContext::new_alice().await;
|
||||
for a in [&alice0, &alice1] {
|
||||
a.set_config_bool(Config::SyncMsgs, true).await?;
|
||||
}
|
||||
|
||||
let new_name = "new name";
|
||||
alice0
|
||||
.set_config(Config::Displayname, Some(new_name))
|
||||
.await?;
|
||||
alice1.evtracker.clear_events();
|
||||
sync(&alice0, &alice1).await;
|
||||
wait_for_item_changed(&alice1).await;
|
||||
assert_eq!(
|
||||
alice1.get_config(Config::Displayname).await?,
|
||||
Some(new_name.to_owned())
|
||||
);
|
||||
|
||||
assert!(alice0.get_config(Config::Selfavatar).await?.is_none());
|
||||
let file = alice0.dir.path().join("avatar.png");
|
||||
let bytes = include_bytes!("../../test-data/image/avatar64x64.png");
|
||||
tokio::fs::write(&file, bytes).await?;
|
||||
alice0
|
||||
.set_config(Config::Selfavatar, Some(file.to_str().unwrap()))
|
||||
.await?;
|
||||
alice1.evtracker.clear_events();
|
||||
sync(&alice0, &alice1).await;
|
||||
wait_for_item_changed(&alice1).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
374
src/webxdc.rs
374
src/webxdc.rs
@@ -13,12 +13,13 @@
|
||||
//! - `msg_id` - ID of the message in the `msgs` table
|
||||
//! - `first_serial` - serial number of the first status update to send
|
||||
//! - `last_serial` - serial number of the last status update to send
|
||||
//! - `descr` - text to send along with the updates
|
||||
//! - `descr` - not used, set to empty string
|
||||
|
||||
mod integration;
|
||||
mod maps_integration;
|
||||
|
||||
use std::cmp::max;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, format_err, Context as _, Result};
|
||||
@@ -40,6 +41,7 @@ use crate::events::EventType;
|
||||
use crate::key::{load_self_public_key, DcKey};
|
||||
use crate::message::{Message, MessageState, MsgId, Viewtype};
|
||||
use crate::mimefactory::wrapped_base64_encode;
|
||||
use crate::mimefactory::RECOMMENDED_FILE_SIZE;
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::param::Param;
|
||||
use crate::param::Params;
|
||||
@@ -56,6 +58,9 @@ const WEBXDC_API_VERSION: u32 = 1;
|
||||
pub const WEBXDC_SUFFIX: &str = "xdc";
|
||||
const WEBXDC_DEFAULT_ICON: &str = "__webxdc__/default-icon.png";
|
||||
|
||||
/// Text shown to classic e-mail users in the visible e-mail body.
|
||||
const BODY_DESCR: &str = "Webxdc Status Update";
|
||||
|
||||
/// Raw information read from manifest.toml
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
#[non_exhaustive]
|
||||
@@ -69,8 +74,8 @@ pub struct WebxdcManifest {
|
||||
/// Optional URL of webxdc source code.
|
||||
pub source_code_url: Option<String>,
|
||||
|
||||
/// If the webxdc requests network access.
|
||||
pub request_internet_access: Option<bool>,
|
||||
/// Set to "map" to request integration.
|
||||
pub request_integration: Option<String>,
|
||||
}
|
||||
|
||||
/// Parsed information from WebxdcManifest and fallbacks.
|
||||
@@ -95,6 +100,9 @@ pub struct WebxdcInfo {
|
||||
/// URL of webxdc source code or an empty string.
|
||||
pub source_code_url: String,
|
||||
|
||||
/// Set to "map" to request integration, otherwise an empty string.
|
||||
pub request_integration: String,
|
||||
|
||||
/// If the webxdc is allowed to access the network.
|
||||
/// It should request access, be encrypted
|
||||
/// and sent to self for this.
|
||||
@@ -102,6 +110,14 @@ pub struct WebxdcInfo {
|
||||
|
||||
/// Address to be used for `window.webxdc.selfAddr` in JS land.
|
||||
pub self_addr: String,
|
||||
|
||||
/// Milliseconds to wait before calling `sendUpdate()` again since the last call.
|
||||
/// Should be exposed to `window.sendUpdateInterval` in JS land.
|
||||
pub send_update_interval: usize,
|
||||
|
||||
/// Maximum number of bytes accepted for a serialized update object.
|
||||
/// Should be exposed to `window.sendUpdateMaxSize` in JS land.
|
||||
pub send_update_max_size: usize,
|
||||
}
|
||||
|
||||
/// Status Update ID.
|
||||
@@ -190,7 +206,7 @@ pub struct StatusUpdateItem {
|
||||
|
||||
/// Array of other users `selfAddr` that should be notified about this update.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub notify: Option<Vec<String>>,
|
||||
pub notify: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
/// Update items as passed to the UIs.
|
||||
@@ -323,14 +339,7 @@ impl Context {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let notify = if let Some(notify_list) = status_update_item.notify {
|
||||
let self_addr = instance.get_webxdc_self_addr(self).await?;
|
||||
notify_list.contains(&self_addr)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let mut notify_msg_id = instance.id;
|
||||
let mut notify_text = "".to_string();
|
||||
let mut param_changed = false;
|
||||
|
||||
let mut instance = instance.clone();
|
||||
@@ -352,7 +361,6 @@ impl Context {
|
||||
let summary = sanitize_bidi_characters(summary);
|
||||
instance.param.set(Param::WebxdcSummary, summary.clone());
|
||||
param_changed = true;
|
||||
notify_text = summary;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,18 +370,16 @@ impl Context {
|
||||
.get_overwritable_info_msg_id(&instance, from_id)
|
||||
.await?;
|
||||
|
||||
if info_msg_id.is_some() && status_update_item.href.is_none() {
|
||||
if let Some(info_msg_id) = info_msg_id {
|
||||
chat::update_msg_text_and_timestamp(
|
||||
self,
|
||||
instance.chat_id,
|
||||
info_msg_id,
|
||||
info.as_str(),
|
||||
timestamp,
|
||||
)
|
||||
.await?;
|
||||
notify_msg_id = info_msg_id;
|
||||
}
|
||||
if let (Some(info_msg_id), None) = (info_msg_id, &status_update_item.href) {
|
||||
chat::update_msg_text_and_timestamp(
|
||||
self,
|
||||
instance.chat_id,
|
||||
info_msg_id,
|
||||
info.as_str(),
|
||||
timestamp,
|
||||
)
|
||||
.await?;
|
||||
notify_msg_id = info_msg_id;
|
||||
} else {
|
||||
notify_msg_id = chat::add_info_msg_with_cmd(
|
||||
self,
|
||||
@@ -387,9 +393,8 @@ impl Context {
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
notify_text = info.to_string();
|
||||
|
||||
if let Some(href) = status_update_item.href {
|
||||
if let Some(ref href) = status_update_item.href {
|
||||
let mut notify_msg = Message::load_from_db(self, notify_msg_id).await?;
|
||||
notify_msg.param.set(Param::Arg, href);
|
||||
notify_msg.update_param(self).await?;
|
||||
@@ -409,12 +414,20 @@ impl Context {
|
||||
});
|
||||
}
|
||||
|
||||
if notify && !notify_text.is_empty() && from_id != ContactId::SELF {
|
||||
self.emit_event(EventType::IncomingWebxdcNotify {
|
||||
contact_id: from_id,
|
||||
msg_id: notify_msg_id,
|
||||
text: notify_text,
|
||||
});
|
||||
if from_id != ContactId::SELF {
|
||||
if let Some(notify_list) = status_update_item.notify {
|
||||
let self_addr = instance.get_webxdc_self_addr(self).await?;
|
||||
if let Some(notify_text) =
|
||||
notify_list.get(&self_addr).or_else(|| notify_list.get("*"))
|
||||
{
|
||||
self.emit_event(EventType::IncomingWebxdcNotify {
|
||||
contact_id: from_id,
|
||||
msg_id: notify_msg_id,
|
||||
text: notify_text.clone(),
|
||||
href: status_update_item.href,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(status_update_serial))
|
||||
@@ -483,11 +496,10 @@ impl Context {
|
||||
&self,
|
||||
instance_msg_id: MsgId,
|
||||
update_str: &str,
|
||||
descr: &str,
|
||||
) -> Result<()> {
|
||||
let status_update_item: StatusUpdateItem = serde_json::from_str(update_str)
|
||||
.with_context(|| format!("Failed to parse webxdc update item from {update_str:?}"))?;
|
||||
self.send_webxdc_status_update_struct(instance_msg_id, status_update_item, descr)
|
||||
self.send_webxdc_status_update_struct(instance_msg_id, status_update_item)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -498,7 +510,6 @@ impl Context {
|
||||
&self,
|
||||
instance_msg_id: MsgId,
|
||||
mut status_update: StatusUpdateItem,
|
||||
descr: &str,
|
||||
) -> Result<()> {
|
||||
let instance = Message::load_from_db(self, instance_msg_id)
|
||||
.await
|
||||
@@ -546,10 +557,10 @@ impl Context {
|
||||
|
||||
if send_now {
|
||||
self.sql.insert(
|
||||
"INSERT INTO smtp_status_updates (msg_id, first_serial, last_serial, descr) VALUES(?, ?, ?, ?)
|
||||
"INSERT INTO smtp_status_updates (msg_id, first_serial, last_serial, descr) VALUES(?, ?, ?, '')
|
||||
ON CONFLICT(msg_id)
|
||||
DO UPDATE SET last_serial=excluded.last_serial, descr=excluded.descr",
|
||||
(instance.id, status_update_serial, status_update_serial, descr),
|
||||
DO UPDATE SET last_serial=excluded.last_serial",
|
||||
(instance.id, status_update_serial, status_update_serial),
|
||||
).await.context("Failed to insert webxdc update into SMTP queue")?;
|
||||
self.scheduler.interrupt_smtp().await;
|
||||
}
|
||||
@@ -557,21 +568,18 @@ impl Context {
|
||||
}
|
||||
|
||||
/// Returns one record of the queued webxdc status updates.
|
||||
async fn smtp_status_update_get(
|
||||
&self,
|
||||
) -> Result<Option<(MsgId, i64, StatusUpdateSerial, String)>> {
|
||||
async fn smtp_status_update_get(&self) -> Result<Option<(MsgId, i64, StatusUpdateSerial)>> {
|
||||
let res = self
|
||||
.sql
|
||||
.query_row_optional(
|
||||
"SELECT msg_id, first_serial, last_serial, descr \
|
||||
"SELECT msg_id, first_serial, last_serial \
|
||||
FROM smtp_status_updates LIMIT 1",
|
||||
(),
|
||||
|row| {
|
||||
let instance_id: MsgId = row.get(0)?;
|
||||
let first_serial: i64 = row.get(1)?;
|
||||
let last_serial: StatusUpdateSerial = row.get(2)?;
|
||||
let descr: String = row.get(3)?;
|
||||
Ok((instance_id, first_serial, last_serial, descr))
|
||||
Ok((instance_id, first_serial, last_serial))
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
@@ -609,7 +617,7 @@ impl Context {
|
||||
/// Attempts to send queued webxdc status updates.
|
||||
pub(crate) async fn flush_status_updates(&self) -> Result<()> {
|
||||
loop {
|
||||
let (instance_id, first, last, descr) = match self.smtp_status_update_get().await? {
|
||||
let (instance_id, first, last) = match self.smtp_status_update_get().await? {
|
||||
Some(res) => res,
|
||||
None => return Ok(()),
|
||||
};
|
||||
@@ -626,7 +634,7 @@ impl Context {
|
||||
let mut status_update = Message {
|
||||
chat_id: instance.chat_id,
|
||||
viewtype: Viewtype::Text,
|
||||
text: descr.to_string(),
|
||||
text: BODY_DESCR.to_string(),
|
||||
hidden: true,
|
||||
..Default::default()
|
||||
};
|
||||
@@ -910,9 +918,9 @@ impl Message {
|
||||
}
|
||||
}
|
||||
|
||||
let internet_access = manifest.request_internet_access.unwrap_or_default()
|
||||
&& self.chat_id.is_self_talk(context).await.unwrap_or_default()
|
||||
&& self.get_showpadlock();
|
||||
let request_integration = manifest.request_integration.unwrap_or_default();
|
||||
let is_integrated = self.is_set_as_webxdc_integration(context).await?;
|
||||
let internet_access = is_integrated;
|
||||
|
||||
let self_addr = self.get_webxdc_self_addr(context).await?;
|
||||
|
||||
@@ -934,8 +942,11 @@ impl Message {
|
||||
.get(Param::WebxdcDocument)
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
summary: if internet_access {
|
||||
"Dev Mode: Do not enter sensitive data!".to_string()
|
||||
summary: if is_integrated {
|
||||
"🌍 Used as map. Delete to use default. Do not enter sensitive data".to_string()
|
||||
} else if request_integration == "map" {
|
||||
"🌏 To use as map, forward to \"Saved Messages\" again. Do not enter sensitive data"
|
||||
.to_string()
|
||||
} else {
|
||||
self.param
|
||||
.get(Param::WebxdcSummary)
|
||||
@@ -947,8 +958,11 @@ impl Message {
|
||||
} else {
|
||||
"".to_string()
|
||||
},
|
||||
request_integration,
|
||||
internet_access,
|
||||
self_addr,
|
||||
send_update_interval: context.ratelimit.read().await.update_interval(),
|
||||
send_update_max_size: RECOMMENDED_FILE_SIZE as usize,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1133,7 +1147,6 @@ mod tests {
|
||||
t.send_webxdc_status_update(
|
||||
instance.id,
|
||||
r#"{"info": "foo", "summary":"bar", "document":"doc", "payload": 42}"#,
|
||||
"descr",
|
||||
)
|
||||
.await?;
|
||||
assert!(!instance.is_forwarded());
|
||||
@@ -1184,7 +1197,6 @@ mod tests {
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
r#"{"payload":7,"info": "i","summary":"s"}"#,
|
||||
"d",
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(alice_grp.get_msg_cnt(&alice).await?, 2);
|
||||
@@ -1267,7 +1279,7 @@ mod tests {
|
||||
.await
|
||||
.is_ok());
|
||||
assert!(bob
|
||||
.send_webxdc_status_update(bob_instance.id, r#"{"payload":42}"#, "descr")
|
||||
.send_webxdc_status_update(bob_instance.id, r#"{"payload":42}"#)
|
||||
.await
|
||||
.is_err());
|
||||
assert_eq!(
|
||||
@@ -1279,7 +1291,7 @@ mod tests {
|
||||
// Once the contact request is accepted, Bob can send updates
|
||||
bob_chat.id.accept(&bob).await?;
|
||||
assert!(bob
|
||||
.send_webxdc_status_update(bob_instance.id, r#"{"payload":42}"#, "descr")
|
||||
.send_webxdc_status_update(bob_instance.id, r#"{"payload":42}"#)
|
||||
.await
|
||||
.is_ok());
|
||||
assert_eq!(
|
||||
@@ -1310,7 +1322,6 @@ mod tests {
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
r#"{"payload": 7, "summary":"sum", "document":"doc"}"#,
|
||||
"bla",
|
||||
)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
@@ -1437,7 +1448,7 @@ mod tests {
|
||||
.await?;
|
||||
chat_id.set_draft(&t, Some(&mut instance)).await?;
|
||||
let instance = chat_id.get_draft(&t).await?.unwrap();
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 42}"#, "descr")
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 42}"#)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
|
||||
@@ -1520,12 +1531,12 @@ mod tests {
|
||||
assert_eq!(update_id1_duplicate, None);
|
||||
|
||||
assert!(t
|
||||
.send_webxdc_status_update(instance.id, "\n\n\n", "")
|
||||
.send_webxdc_status_update(instance.id, "\n\n\n")
|
||||
.await
|
||||
.is_err());
|
||||
|
||||
assert!(t
|
||||
.send_webxdc_status_update(instance.id, "bad json", "")
|
||||
.send_webxdc_status_update(instance.id, "bad json")
|
||||
.await
|
||||
.is_err());
|
||||
|
||||
@@ -1584,7 +1595,6 @@ mod tests {
|
||||
t.send_webxdc_status_update(
|
||||
instance.id,
|
||||
r#"{"payload" : 1, "sender": "that is not used"}"#,
|
||||
"",
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
@@ -1719,11 +1729,7 @@ mod tests {
|
||||
assert!(!sent1.payload().contains("report-type=status-update"));
|
||||
|
||||
alice
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
r#"{"payload" : {"foo":"bar"}}"#,
|
||||
"descr text",
|
||||
)
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"payload" : {"foo":"bar"}}"#)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
expect_status_update_event(&alice, alice_instance.id).await?;
|
||||
@@ -1732,7 +1738,7 @@ mod tests {
|
||||
assert!(alice_update.hidden);
|
||||
assert_eq!(alice_update.viewtype, Viewtype::Text);
|
||||
assert_eq!(alice_update.get_filename(), None);
|
||||
assert_eq!(alice_update.text, "descr text".to_string());
|
||||
assert_eq!(alice_update.text, BODY_DESCR.to_string());
|
||||
assert_eq!(alice_update.chat_id, alice_instance.chat_id);
|
||||
assert_eq!(
|
||||
alice_update.parent(&alice).await?.unwrap().id,
|
||||
@@ -1740,7 +1746,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 1);
|
||||
assert!(sent2.payload().contains("report-type=status-update"));
|
||||
assert!(sent2.payload().contains("descr text"));
|
||||
assert!(sent2.payload().contains(BODY_DESCR));
|
||||
assert_eq!(
|
||||
alice
|
||||
.get_webxdc_status_updates(alice_instance.id, StatusUpdateSerial(0))
|
||||
@@ -1749,11 +1755,7 @@ mod tests {
|
||||
);
|
||||
|
||||
alice
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
r#"{"payload":{"snipp":"snapp"}}"#,
|
||||
"bla text",
|
||||
)
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"payload":{"snipp":"snapp"}}"#)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
alice
|
||||
@@ -1822,31 +1824,23 @@ mod tests {
|
||||
+ &String::from_utf8(vec![b'a'; STATUS_UPDATE_SIZE_MAX])?
|
||||
+ r#""}"#;
|
||||
alice
|
||||
.send_webxdc_status_update(alice_instance.id, &(update1_str.clone() + "}"), "descr1")
|
||||
.send_webxdc_status_update(alice_instance.id, &(update1_str.clone() + "}"))
|
||||
.await?;
|
||||
alice
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
r#"{"payload" : {"foo":"bar2"}}"#,
|
||||
"descr2",
|
||||
)
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"payload" : {"foo":"bar2"}}"#)
|
||||
.await?;
|
||||
alice
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
r#"{"payload" : {"foo":"bar3"}}"#,
|
||||
"descr3",
|
||||
)
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"payload" : {"foo":"bar3"}}"#)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
|
||||
// There's the message stack, so we pop messages in the reverse order.
|
||||
let sent3 = &alice.pop_sent_msg().await;
|
||||
let alice_update = sent3.load_from_db().await;
|
||||
assert_eq!(alice_update.text, "descr3".to_string());
|
||||
assert_eq!(alice_update.text, BODY_DESCR.to_string());
|
||||
let sent2 = &alice.pop_sent_msg().await;
|
||||
let alice_update = sent2.load_from_db().await;
|
||||
assert_eq!(alice_update.text, "descr3".to_string());
|
||||
assert_eq!(alice_update.text, BODY_DESCR.to_string());
|
||||
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 1);
|
||||
|
||||
// Bob receives the instance.
|
||||
@@ -1897,7 +1891,7 @@ mod tests {
|
||||
(None, StatusUpdateSerial(u32::MAX))
|
||||
);
|
||||
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 1}"#, "bla")
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 1}"#)
|
||||
.await?;
|
||||
let (object, first_new) = t
|
||||
.render_webxdc_status_update_object(instance.id, first, last, None)
|
||||
@@ -1913,13 +1907,13 @@ mod tests {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "a chat").await?;
|
||||
let instance = send_webxdc_instance(&t, chat_id).await?;
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 1}"#, "d")
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 1}"#)
|
||||
.await?;
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 2}"#, "d")
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 2}"#)
|
||||
.await?;
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 3}"#, "d")
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 3}"#)
|
||||
.await?;
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 4}"#, "d")
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 4}"#)
|
||||
.await?;
|
||||
let (json, first_new) = t
|
||||
.render_webxdc_status_update_object(
|
||||
@@ -1964,17 +1958,17 @@ mod tests {
|
||||
let instance3 = send_webxdc_instance(&t, chat_id).await?;
|
||||
assert!(t.smtp_status_update_get().await?.is_none());
|
||||
|
||||
t.send_webxdc_status_update(instance1.id, r#"{"payload": "1a"}"#, "descr1a")
|
||||
t.send_webxdc_status_update(instance1.id, r#"{"payload": "1a"}"#)
|
||||
.await?;
|
||||
t.send_webxdc_status_update(instance2.id, r#"{"payload": "2a"}"#, "descr2a")
|
||||
t.send_webxdc_status_update(instance2.id, r#"{"payload": "2a"}"#)
|
||||
.await?;
|
||||
t.send_webxdc_status_update(instance2.id, r#"{"payload": "2b"}"#, "descr2b")
|
||||
t.send_webxdc_status_update(instance2.id, r#"{"payload": "2b"}"#)
|
||||
.await?;
|
||||
t.send_webxdc_status_update(instance3.id, r#"{"payload": "3a"}"#, "descr3a")
|
||||
t.send_webxdc_status_update(instance3.id, r#"{"payload": "3a"}"#)
|
||||
.await?;
|
||||
t.send_webxdc_status_update(instance3.id, r#"{"payload": "3b"}"#, "descr3b")
|
||||
t.send_webxdc_status_update(instance3.id, r#"{"payload": "3b"}"#)
|
||||
.await?;
|
||||
t.send_webxdc_status_update(instance3.id, r#"{"payload": "3c"}"#, "descr3c")
|
||||
t.send_webxdc_status_update(instance3.id, r#"{"payload": "3c"}"#)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
t.sql
|
||||
@@ -1986,7 +1980,7 @@ mod tests {
|
||||
// order of smtp_status_update_get() is not defined, therefore the more complicated test
|
||||
let mut instances_checked = 0;
|
||||
for i in 0..3 {
|
||||
let (instance, min_ser, max_ser, descr) = t.smtp_status_update_get().await?.unwrap();
|
||||
let (instance, min_ser, max_ser) = t.smtp_status_update_get().await?.unwrap();
|
||||
t.smtp_status_update_pop_serials(
|
||||
instance,
|
||||
min_ser,
|
||||
@@ -1996,15 +1990,14 @@ mod tests {
|
||||
let min_ser: u32 = min_ser.try_into()?;
|
||||
if instance == instance1.id {
|
||||
assert_eq!(min_ser, max_ser.to_u32());
|
||||
assert_eq!(descr, "descr1a");
|
||||
|
||||
instances_checked += 1;
|
||||
} else if instance == instance2.id {
|
||||
assert_eq!(min_ser, max_ser.to_u32() - 1);
|
||||
assert_eq!(descr, "descr2b");
|
||||
|
||||
instances_checked += 1;
|
||||
} else if instance == instance3.id {
|
||||
assert_eq!(min_ser, max_ser.to_u32() - 2);
|
||||
assert_eq!(descr, "descr3c");
|
||||
instances_checked += 1;
|
||||
} else {
|
||||
bail!("unexpected instance");
|
||||
@@ -2042,11 +2035,11 @@ mod tests {
|
||||
let mut alice_instance = alice_chat_id.get_draft(&alice).await?.unwrap();
|
||||
|
||||
alice
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"payload": {"foo":"bar"}}"#, "descr")
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"payload": {"foo":"bar"}}"#)
|
||||
.await?;
|
||||
expect_status_update_event(&alice, alice_instance.id).await?;
|
||||
alice
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"payload":42, "info":"i"}"#, "descr")
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"payload":42, "info":"i"}"#)
|
||||
.await?;
|
||||
expect_status_update_event(&alice, alice_instance.id).await?;
|
||||
assert_eq!(
|
||||
@@ -2093,7 +2086,7 @@ mod tests {
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
let msg_id = send_text_msg(&t, chat_id, "ho!".to_string()).await?;
|
||||
assert!(t
|
||||
.send_webxdc_status_update(msg_id, r#"{"foo":"bar"}"#, "descr")
|
||||
.send_webxdc_status_update(msg_id, r#"{"foo":"bar"}"#)
|
||||
.await
|
||||
.is_err());
|
||||
Ok(())
|
||||
@@ -2241,19 +2234,6 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_webxdc_manifest_request_internet_access() -> Result<()> {
|
||||
let result = parse_webxdc_manifest(r#"request_internet_access = 3"#.as_bytes());
|
||||
assert!(result.is_err());
|
||||
let manifest = parse_webxdc_manifest(r#" source_code_url="https://foo.org""#.as_bytes())?;
|
||||
assert_eq!(manifest.request_internet_access, None);
|
||||
let manifest = parse_webxdc_manifest(r#" request_internet_access=false"#.as_bytes())?;
|
||||
assert_eq!(manifest.request_internet_access, Some(false));
|
||||
let manifest = parse_webxdc_manifest(r#"request_internet_access = true"#.as_bytes())?;
|
||||
assert_eq!(manifest.request_internet_access, Some(true));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_min_api_too_large() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -2282,6 +2262,8 @@ sth_for_the = "future""#
|
||||
let info = instance.get_webxdc_info(&t).await?;
|
||||
assert_eq!(info.name, "minimal.xdc");
|
||||
assert_eq!(info.icon, WEBXDC_DEFAULT_ICON.to_string());
|
||||
assert_eq!(info.send_update_interval, 10000);
|
||||
assert_eq!(info.send_update_max_size, RECOMMENDED_FILE_SIZE as usize);
|
||||
|
||||
let mut instance = create_webxdc_instance(
|
||||
&t,
|
||||
@@ -2387,11 +2369,7 @@ sth_for_the = "future""#
|
||||
assert_eq!(info.summary, "".to_string());
|
||||
|
||||
alice
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
r#"{"summary":"sum: 1", "payload":1}"#,
|
||||
"descr",
|
||||
)
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"summary":"sum: 1", "payload":1}"#)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent_update1 = &alice.pop_sent_msg().await;
|
||||
@@ -2402,11 +2380,7 @@ sth_for_the = "future""#
|
||||
assert_eq!(info.summary, "sum: 1".to_string());
|
||||
|
||||
alice
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
r#"{"summary":"sum: 2", "payload":2}"#,
|
||||
"descr",
|
||||
)
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"summary":"sum: 2", "payload":2}"#)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent_update2 = &alice.pop_sent_msg().await;
|
||||
@@ -2457,7 +2431,6 @@ sth_for_the = "future""#
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
r#"{"document":"my file", "payload":1337}"#,
|
||||
"descr",
|
||||
)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
@@ -2497,7 +2470,6 @@ sth_for_the = "future""#
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
r#"{"info":"this appears in-chat", "payload":"sth. else"}"#,
|
||||
"descr text",
|
||||
)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
@@ -2575,13 +2547,13 @@ sth_for_the = "future""#
|
||||
// Alice sends two info messages in a row;
|
||||
// the second one removes the first one as there is nothing in between
|
||||
alice
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"info":"i1", "payload":1}"#, "d")
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"info":"i1", "payload":1}"#)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent2 = &alice.pop_sent_msg().await;
|
||||
assert_eq!(alice_chat.id.get_msg_cnt(&alice).await?, 2);
|
||||
alice
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"info":"i2", "payload":2}"#, "d")
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"info":"i2", "payload":2}"#)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent3 = &alice.pop_sent_msg().await;
|
||||
@@ -2608,12 +2580,12 @@ sth_for_the = "future""#
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "c").await?;
|
||||
let instance = send_webxdc_instance(&t, chat_id).await?;
|
||||
|
||||
t.send_webxdc_status_update(instance.id, r#"{"info":"i1", "payload":1}"#, "d")
|
||||
t.send_webxdc_status_update(instance.id, r#"{"info":"i1", "payload":1}"#)
|
||||
.await?;
|
||||
assert_eq!(chat_id.get_msg_cnt(&t).await?, 2);
|
||||
send_text_msg(&t, chat_id, "msg between info".to_string()).await?;
|
||||
assert_eq!(chat_id.get_msg_cnt(&t).await?, 3);
|
||||
t.send_webxdc_status_update(instance.id, r#"{"info":"i2", "payload":2}"#, "d")
|
||||
t.send_webxdc_status_update(instance.id, r#"{"info":"i2", "payload":2}"#)
|
||||
.await?;
|
||||
assert_eq!(chat_id.get_msg_cnt(&t).await?, 4);
|
||||
|
||||
@@ -2642,7 +2614,7 @@ sth_for_the = "future""#
|
||||
let alice_instance = send_webxdc_instance(&alice, alice_chat_id).await?;
|
||||
let sent1 = &alice.pop_sent_msg().await;
|
||||
alice
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"payload":42}"#, "descr")
|
||||
.send_webxdc_status_update(alice_instance.id, r#"{"payload":42}"#)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent2 = &alice.pop_sent_msg().await;
|
||||
@@ -2662,7 +2634,7 @@ sth_for_the = "future""#
|
||||
Contact::create(&bob, "", "claire@example.org").await?,
|
||||
)
|
||||
.await?;
|
||||
bob.send_webxdc_status_update(bob_instance.id, r#"{"payload":43}"#, "descr")
|
||||
bob.send_webxdc_status_update(bob_instance.id, r#"{"payload":43}"#)
|
||||
.await?;
|
||||
bob.flush_status_updates().await?;
|
||||
let sent3 = bob.pop_sent_msg().await;
|
||||
@@ -2672,16 +2644,16 @@ sth_for_the = "future""#
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// check that `info.internet_access` is not set for normal, non-integrated webxdc -
|
||||
// even if they use the deprecated option `request_internet_access` in manifest.toml
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_internet_access() -> Result<()> {
|
||||
async fn test_webxdc_no_internet_access() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let self_id = t.get_self_chat().await.id;
|
||||
let single_id = t.create_chat_with_contact("bob", "bob@e.com").await.id;
|
||||
let group_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?;
|
||||
let broadcast_id = create_broadcast_list(&t).await?;
|
||||
|
||||
let mut first_test = true; // only the first test has all conditions for internet access
|
||||
|
||||
for e2ee in ["1", "0"] {
|
||||
t.set_config(Config::E2eeEnabled, Some(e2ee)).await?;
|
||||
for chat_id in [self_id, single_id, group_id, broadcast_id] {
|
||||
@@ -2700,16 +2672,11 @@ sth_for_the = "future""#
|
||||
t.send_webxdc_status_update(
|
||||
instance_id,
|
||||
r#"{"summary":"real summary", "payload": 42}"#,
|
||||
"descr",
|
||||
)
|
||||
.await?;
|
||||
let instance = Message::load_from_db(&t, instance_id).await?;
|
||||
let info = instance.get_webxdc_info(&t).await?;
|
||||
assert_eq!(info.internet_access, first_test);
|
||||
assert_eq!(info.summary.contains("Do not enter sensitive"), first_test);
|
||||
assert_eq!(info.summary.contains("real summary"), !first_test);
|
||||
|
||||
first_test = false;
|
||||
assert_eq!(info.internet_access, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2778,7 +2745,6 @@ sth_for_the = "future""#
|
||||
bob.send_webxdc_status_update(
|
||||
bob_instance.id,
|
||||
r#"{"payload":7,"info": "i","summary":"s"}"#,
|
||||
"",
|
||||
)
|
||||
.await?;
|
||||
bob.flush_status_updates().await?;
|
||||
@@ -2898,7 +2864,6 @@ sth_for_the = "future""#
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
r#"{"payload":"p","info":"i","aNewUnknownProperty":"x","max_serial":123}"#,
|
||||
"Some description",
|
||||
)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
@@ -2994,27 +2959,29 @@ sth_for_the = "future""#
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
&format!(
|
||||
"{{\"payload\":7,\"info\": \"Alice moved\",\"notify\":[\"{}\"]}}",
|
||||
"{{\"payload\":7,\"info\": \"Alice moved\",\"notify\":{{\"{}\": \"Your move!\"}} }}",
|
||||
bob_instance.get_webxdc_self_addr(&bob).await?
|
||||
),
|
||||
"d",
|
||||
)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent2 = alice.pop_sent_msg().await;
|
||||
let info_msg = alice.get_last_msg().await;
|
||||
assert!(info_msg.is_info());
|
||||
assert!(!has_incoming_webxdc_event(&alice, info_msg, "Alice moved").await);
|
||||
assert_eq!(info_msg.text, "Alice moved");
|
||||
assert!(!has_incoming_webxdc_event(&alice, info_msg, "").await);
|
||||
|
||||
bob.recv_msg_trash(&sent2).await;
|
||||
let info_msg = bob.get_last_msg().await;
|
||||
assert!(info_msg.is_info());
|
||||
assert!(has_incoming_webxdc_event(&bob, info_msg, "Alice moved").await);
|
||||
assert_eq!(info_msg.text, "Alice moved");
|
||||
assert!(has_incoming_webxdc_event(&bob, info_msg, "Your move!").await);
|
||||
|
||||
fiona.recv_msg_trash(&sent2).await;
|
||||
let info_msg = fiona.get_last_msg().await;
|
||||
assert!(info_msg.is_info());
|
||||
assert!(!has_incoming_webxdc_event(&fiona, info_msg, "Alice moved").await);
|
||||
assert_eq!(info_msg.text, "Alice moved");
|
||||
assert!(!has_incoming_webxdc_event(&fiona, info_msg, "").await);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -3038,28 +3005,28 @@ sth_for_the = "future""#
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
&format!(
|
||||
"{{\"payload\":7,\"info\": \"moved\", \"summary\": \"ignored for notify as info is set\", \"notify\":[\"{}\",\"{}\"]}}",
|
||||
"{{\"payload\":7,\"info\": \"moved\", \"summary\": \"move summary\", \"notify\":{{\"{}\":\"move, Bob\",\"{}\":\"move, Fiona\"}} }}",
|
||||
bob_instance.get_webxdc_self_addr(&bob).await?,
|
||||
fiona_instance.get_webxdc_self_addr(&fiona).await?
|
||||
),
|
||||
"d",
|
||||
|
||||
)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent2 = alice.pop_sent_msg().await;
|
||||
let info_msg = alice.get_last_msg().await;
|
||||
assert!(info_msg.is_info());
|
||||
assert!(!has_incoming_webxdc_event(&alice, info_msg, "moved").await);
|
||||
assert!(!has_incoming_webxdc_event(&alice, info_msg, "").await);
|
||||
|
||||
bob.recv_msg_trash(&sent2).await;
|
||||
let info_msg = bob.get_last_msg().await;
|
||||
assert!(info_msg.is_info());
|
||||
assert!(has_incoming_webxdc_event(&bob, info_msg, "moved").await);
|
||||
assert!(has_incoming_webxdc_event(&bob, info_msg, "move, Bob").await);
|
||||
|
||||
fiona.recv_msg_trash(&sent2).await;
|
||||
let info_msg = fiona.get_last_msg().await;
|
||||
assert!(info_msg.is_info());
|
||||
assert!(has_incoming_webxdc_event(&fiona, info_msg, "moved").await);
|
||||
assert!(has_incoming_webxdc_event(&fiona, info_msg, "move, Fiona").await);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -3085,55 +3052,129 @@ sth_for_the = "future""#
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
&format!(
|
||||
"{{\"payload\":7,\"info\": \"moved\", \"notify\":[\"{}\"]}}",
|
||||
"{{\"payload\":7,\"info\": \"moved\", \"notify\":{{\"{}\": \"bla\"}} }}",
|
||||
alice2_instance.get_webxdc_self_addr(&alice2).await?
|
||||
),
|
||||
"d",
|
||||
)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent2 = alice.pop_sent_msg().await;
|
||||
let info_msg = alice.get_last_msg().await;
|
||||
assert!(info_msg.is_info());
|
||||
assert!(!has_incoming_webxdc_event(&alice, info_msg, "moved").await);
|
||||
assert!(!has_incoming_webxdc_event(&alice, info_msg, "").await);
|
||||
|
||||
alice2.recv_msg_trash(&sent2).await;
|
||||
let info_msg = alice2.get_last_msg().await;
|
||||
assert!(info_msg.is_info());
|
||||
assert!(!has_incoming_webxdc_event(&alice2, info_msg, "moved").await);
|
||||
assert!(!has_incoming_webxdc_event(&alice2, info_msg, "").await);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_notify_summary() -> Result<()> {
|
||||
async fn test_webxdc_notify_all() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
let fiona = tcm.fiona().await;
|
||||
|
||||
let grp_id = alice
|
||||
.create_group_with_members(ProtectionStatus::Unprotected, "grp", &[&bob])
|
||||
.create_group_with_members(ProtectionStatus::Unprotected, "grp", &[&bob, &fiona])
|
||||
.await;
|
||||
let alice_instance = send_webxdc_instance(&alice, grp_id).await?;
|
||||
let sent1 = alice.pop_sent_msg().await;
|
||||
bob.recv_msg(&sent1).await;
|
||||
fiona.recv_msg(&sent1).await;
|
||||
|
||||
alice
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
"{\"payload\":7,\"info\": \"go\", \"notify\":{\"*\":\"notify all\"} }",
|
||||
)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent2 = alice.pop_sent_msg().await;
|
||||
let info_msg = alice.get_last_msg().await;
|
||||
assert_eq!(info_msg.text, "go");
|
||||
assert!(!has_incoming_webxdc_event(&alice, info_msg, "").await);
|
||||
|
||||
bob.recv_msg_trash(&sent2).await;
|
||||
let info_msg = bob.get_last_msg().await;
|
||||
assert_eq!(info_msg.text, "go");
|
||||
assert!(has_incoming_webxdc_event(&bob, info_msg, "notify all").await);
|
||||
|
||||
fiona.recv_msg_trash(&sent2).await;
|
||||
let info_msg = fiona.get_last_msg().await;
|
||||
assert_eq!(info_msg.text, "go");
|
||||
assert!(has_incoming_webxdc_event(&fiona, info_msg, "notify all").await);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_notify_bob_and_all() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
let fiona = tcm.fiona().await;
|
||||
|
||||
let grp_id = alice
|
||||
.create_group_with_members(ProtectionStatus::Unprotected, "grp", &[&bob, &fiona])
|
||||
.await;
|
||||
let alice_instance = send_webxdc_instance(&alice, grp_id).await?;
|
||||
let sent1 = alice.pop_sent_msg().await;
|
||||
let bob_instance = bob.recv_msg(&sent1).await;
|
||||
let fiona_instance = fiona.recv_msg(&sent1).await;
|
||||
|
||||
alice
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
&format!(
|
||||
"{{\"payload\":7,\"summary\": \"4 moves done\",\"notify\":[\"{}\"]}}",
|
||||
"{{\"payload\":7, \"notify\":{{\"{}\": \"notify bob\",\"*\": \"notify all\"}} }}",
|
||||
bob_instance.get_webxdc_self_addr(&bob).await?
|
||||
),
|
||||
"d",
|
||||
)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent2 = alice.pop_sent_msg().await;
|
||||
assert!(!has_incoming_webxdc_event(&alice, alice_instance, "4 moves done").await);
|
||||
|
||||
bob.recv_msg_trash(&sent2).await;
|
||||
assert!(has_incoming_webxdc_event(&bob, bob_instance, "4 moves done").await);
|
||||
fiona.recv_msg_trash(&sent2).await;
|
||||
assert!(has_incoming_webxdc_event(&bob, bob_instance, "notify bob").await);
|
||||
assert!(has_incoming_webxdc_event(&fiona, fiona_instance, "notify all").await);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_webxdc_notify_all_and_bob() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
let fiona = tcm.fiona().await;
|
||||
|
||||
let grp_id = alice
|
||||
.create_group_with_members(ProtectionStatus::Unprotected, "grp", &[&bob, &fiona])
|
||||
.await;
|
||||
let alice_instance = send_webxdc_instance(&alice, grp_id).await?;
|
||||
let sent1 = alice.pop_sent_msg().await;
|
||||
let bob_instance = bob.recv_msg(&sent1).await;
|
||||
let fiona_instance = fiona.recv_msg(&sent1).await;
|
||||
|
||||
alice
|
||||
.send_webxdc_status_update(
|
||||
alice_instance.id,
|
||||
&format!(
|
||||
"{{\"payload\":7, \"notify\":{{\"*\": \"notify all\", \"{}\": \"notify bob\"}} }}",
|
||||
bob_instance.get_webxdc_self_addr(&bob).await?
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
let sent2 = alice.pop_sent_msg().await;
|
||||
bob.recv_msg_trash(&sent2).await;
|
||||
fiona.recv_msg_trash(&sent2).await;
|
||||
assert!(has_incoming_webxdc_event(&bob, bob_instance, "notify bob").await);
|
||||
assert!(has_incoming_webxdc_event(&fiona, fiona_instance, "notify all").await);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -3154,7 +3195,6 @@ sth_for_the = "future""#
|
||||
.send_webxdc_status_update(
|
||||
instance.id,
|
||||
r##"{"payload": "my deeplink data", "info": "my move!", "href": "#foobar"}"##,
|
||||
"d",
|
||||
)
|
||||
.await?;
|
||||
alice.flush_status_updates().await?;
|
||||
|
||||
@@ -57,14 +57,34 @@ impl Context {
|
||||
}
|
||||
|
||||
// Check if a Webxdc shall be used as an integration and remember that.
|
||||
pub(crate) async fn update_webxdc_integration_database(&self, msg: &Message) -> Result<()> {
|
||||
if msg.viewtype == Viewtype::Webxdc && msg.param.get_int(Param::WebxdcIntegration).is_some()
|
||||
{
|
||||
self.set_config_internal(
|
||||
Config::WebxdcIntegration,
|
||||
Some(&msg.id.to_u32().to_string()),
|
||||
)
|
||||
.await?;
|
||||
pub(crate) async fn update_webxdc_integration_database(
|
||||
&self,
|
||||
msg: &mut Message,
|
||||
context: &Context,
|
||||
) -> Result<()> {
|
||||
if msg.viewtype == Viewtype::Webxdc {
|
||||
let is_integration = if msg.param.get_int(Param::WebxdcIntegration).is_some() {
|
||||
true
|
||||
} else if msg.chat_id.is_self_talk(context).await? {
|
||||
let info = msg.get_webxdc_info(context).await?;
|
||||
if info.request_integration == "map" {
|
||||
msg.param.set_int(Param::WebxdcIntegration, 1);
|
||||
msg.update_param(context).await?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if is_integration {
|
||||
self.set_config_internal(
|
||||
Config::WebxdcIntegration,
|
||||
Some(&msg.id.to_u32().to_string()),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -101,11 +121,26 @@ impl Message {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the message is an actually used as Webxdc integration.
|
||||
pub(crate) async fn is_set_as_webxdc_integration(&self, context: &Context) -> Result<bool> {
|
||||
if let Some(integration_id) = context
|
||||
.get_config_parsed::<u32>(Config::WebxdcIntegration)
|
||||
.await?
|
||||
{
|
||||
Ok(integration_id == self.id.to_u32())
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::message;
|
||||
use crate::message::{Message, Viewtype};
|
||||
use crate::test_utils::TestContext;
|
||||
use anyhow::Result;
|
||||
use std::time::Duration;
|
||||
@@ -126,4 +161,65 @@ 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;
|
||||
assert!(t.init_webxdc_integration(None).await?.is_none());
|
||||
|
||||
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?;
|
||||
t.send_msg(self_chat.id, &mut msg).await;
|
||||
assert_integration(&t, "with some icon").await?; // still the default integration
|
||||
|
||||
// send a maps.xdc with manifest including the line `request_integration = "map"`
|
||||
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?;
|
||||
let sent = t.send_msg(self_chat.id, &mut msg).await;
|
||||
let info = msg.get_webxdc_info(&t).await?;
|
||||
assert!(info.summary.contains("Used as map"));
|
||||
assert_integration(&t, "Maps Test 2").await?;
|
||||
|
||||
// when maps.xdc is received on another device, the integration is not accepted (needs to be forwarded again)
|
||||
let t2 = TestContext::new_alice().await;
|
||||
let msg2 = t2.recv_msg(&sent).await;
|
||||
let info = msg2.get_webxdc_info(&t2).await?;
|
||||
assert!(info.summary.contains("To use as map,"));
|
||||
assert!(t2.init_webxdc_integration(None).await?.is_none());
|
||||
|
||||
// deleting maps.xdc removes the user's integration - the UI will go back to default calling set_webxdc_integration() then
|
||||
message::delete_msgs(&t, &[msg.id]).await?;
|
||||
assert!(t.init_webxdc_integration(None).await?.is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ mod tests {
|
||||
async fn test_maps_integration() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
let bytes = include_bytes!("../../test-data/webxdc/mapstest.xdc");
|
||||
let bytes = include_bytes!("../../test-data/webxdc/mapstest-integration-set.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?;
|
||||
@@ -199,13 +199,12 @@ 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");
|
||||
assert_eq!(info.name, "Maps Test 2");
|
||||
assert_eq!(info.internet_access, true);
|
||||
|
||||
t.send_webxdc_status_update(
|
||||
integration_id,
|
||||
r#"{"payload": {"action": "pos", "lat": 11.0, "lng": 12.0, "label": "poi #1"}}"#,
|
||||
"descr",
|
||||
)
|
||||
.await?;
|
||||
t.evtracker
|
||||
@@ -239,7 +238,6 @@ mod tests {
|
||||
t.send_webxdc_status_update(
|
||||
integration_id,
|
||||
r#"{"payload": {"action": "pos", "lat": 22.0, "lng": 23.0, "label": "poi #2"}}"#,
|
||||
"descr",
|
||||
)
|
||||
.await?;
|
||||
let updates = t
|
||||
|
||||
BIN
test-data/webxdc/mapstest-integration-set.xdc
Normal file
BIN
test-data/webxdc/mapstest-integration-set.xdc
Normal file
Binary file not shown.
Reference in New Issue
Block a user