mirror of
https://github.com/chatmail/core.git
synced 2026-04-14 03:57:19 +03:00
Compare commits
31 Commits
simon/impr
...
sk/aliases
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f29d98333f | ||
|
|
2c802516cf | ||
|
|
51276e9320 | ||
|
|
800edc6fce | ||
|
|
4e5e9f6006 | ||
|
|
d9d694ead0 | ||
|
|
faad576d10 | ||
|
|
b96593ed10 | ||
|
|
d2324a8fc4 | ||
|
|
10a05fa6d9 | ||
|
|
97d2119028 | ||
|
|
a510d5f3c2 | ||
|
|
678f1b305c | ||
|
|
dface33699 | ||
|
|
92c6dd483c | ||
|
|
c627d2fcc8 | ||
|
|
429c14ae0b | ||
|
|
ce40c04e63 | ||
|
|
b89eec8bbb | ||
|
|
7175ee8587 | ||
|
|
c12a972abd | ||
|
|
145b91c2de | ||
|
|
a49c25bbee | ||
|
|
a439224f9e | ||
|
|
64cd7f8d31 | ||
|
|
48ab5d4089 | ||
|
|
cd2394c31e | ||
|
|
c972d7b6ef | ||
|
|
170023f1c8 | ||
|
|
5dc746d691 | ||
|
|
91acf0708a |
40
CHANGELOG.md
40
CHANGELOG.md
@@ -1,5 +1,43 @@
|
||||
# Changelog
|
||||
|
||||
## [1.149.0] - 2024-11-05
|
||||
|
||||
### Build system
|
||||
|
||||
- Update tokio to 1.41 and Android NDK to r27.
|
||||
- `nix flake update android`.
|
||||
|
||||
### Fixes
|
||||
|
||||
- cargo: Update iroh to 0.28.1.
|
||||
This fixes the problem with iroh not sending the `Host:` header and not being able to connect to relays behind nginx reverse proxy.
|
||||
|
||||
## [1.148.7] - 2024-11-03
|
||||
|
||||
### API-Changes
|
||||
|
||||
- Add API to reset contact encryption.
|
||||
|
||||
### Features / Changes
|
||||
|
||||
- Emit chatlist events only if message still exists.
|
||||
|
||||
### Fixes
|
||||
|
||||
- send_msg_to_smtp: Do not fail if the message does not exist anymore.
|
||||
- Do not percent-encode dot when passing to autoconfig server.
|
||||
- Save contact name from SecureJoin QR to `authname`, not to `name` ([#6115](https://github.com/deltachat/deltachat-core-rust/pull/6115)).
|
||||
- Always exit fake IDLE after at most 60 seconds.
|
||||
- Concat NDNs ([#6129](https://github.com/deltachat/deltachat-core-rust/pull/6129)).
|
||||
|
||||
### Refactor
|
||||
|
||||
- Remove `has_decrypted_pgp_armor()`.
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Update dependencies.
|
||||
|
||||
## [1.148.6] - 2024-10-31
|
||||
|
||||
### API-Changes
|
||||
@@ -5193,3 +5231,5 @@ https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed
|
||||
[1.148.4]: https://github.com/deltachat/deltachat-core-rust/compare/v1.148.3..v1.148.4
|
||||
[1.148.5]: https://github.com/deltachat/deltachat-core-rust/compare/v1.148.4..v1.148.5
|
||||
[1.148.6]: https://github.com/deltachat/deltachat-core-rust/compare/v1.148.5..v1.148.6
|
||||
[1.148.7]: https://github.com/deltachat/deltachat-core-rust/compare/v1.148.6..v1.148.7
|
||||
[1.149.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.148.7..v1.149.0
|
||||
|
||||
421
Cargo.lock
generated
421
Cargo.lock
generated
@@ -160,9 +160,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.89"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
||||
checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
@@ -384,6 +384,12 @@ dependencies = [
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "attohttpc"
|
||||
version = "0.24.1"
|
||||
@@ -413,9 +419,9 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit",
|
||||
@@ -448,7 +454,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
@@ -615,9 +621,9 @@ checksum = "78a6932c88f1d2c29533a3b8a5f5a2f84cc19c3339b431677c3160c5c2e6ca85"
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "6.0.0"
|
||||
version = "7.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
|
||||
checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
@@ -1303,7 +1309,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-broadcast",
|
||||
@@ -1332,7 +1338,7 @@ dependencies = [
|
||||
"hickory-resolver",
|
||||
"http-body-util",
|
||||
"humansize",
|
||||
"hyper 1.4.1",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"image",
|
||||
"iroh-gossip",
|
||||
@@ -1403,7 +1409,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-channel 2.3.1",
|
||||
@@ -1428,7 +1434,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-repl"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -1444,7 +1450,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -1473,7 +1479,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -2213,9 +2219,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -2271,9 +2277,9 @@ checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -2361,6 +2367,37 @@ dependencies = [
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "genawaiter"
|
||||
version = "0.99.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"genawaiter-macro",
|
||||
"genawaiter-proc-macro",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "genawaiter-macro"
|
||||
version = "0.99.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc"
|
||||
|
||||
[[package]]
|
||||
name = "genawaiter-proc-macro"
|
||||
version = "0.99.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784f84eebc366e15251c4a8c3acee82a6a6f427949776ecb88377362a9621738"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generator"
|
||||
version = "0.7.5"
|
||||
@@ -2474,16 +2511,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.26"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
||||
checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"http 1.1.0",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
@@ -2666,17 +2703,6 @@ dependencies = [
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 0.2.12",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.0"
|
||||
@@ -2696,7 +2722,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
@@ -2714,9 +2740,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "human-panic"
|
||||
version = "2.0.1"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c5a08ed290eac04006e21e63d32e90086b6182c7cd0452d10f4264def1fec9a"
|
||||
checksum = "80b84a66a325082740043a6c28bbea400c129eac0d3a27673a1de971e44bf1f7"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"os_info",
|
||||
@@ -2743,39 +2769,16 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.28"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
|
||||
checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http 0.2.12",
|
||||
"http-body 0.4.6",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
@@ -2793,7 +2796,7 @@ checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
@@ -2813,8 +2816,8 @@ dependencies = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"hyper 1.4.1",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
@@ -2872,16 +2875,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "igd-next"
|
||||
version = "0.14.3"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "064d90fec10d541084e7b39ead8875a5a80d9114a2b18791565253bae25f49e4"
|
||||
checksum = "76b0d7d4541def58a37bf8efc559683f21edce7c82f0d866c93ac21f7e098f93"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"attohttpc",
|
||||
"bytes",
|
||||
"futures",
|
||||
"http 0.2.12",
|
||||
"hyper 0.14.28",
|
||||
"http 1.1.0",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"tokio",
|
||||
@@ -2891,9 +2896,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.2"
|
||||
version = "0.25.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10"
|
||||
checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
@@ -2908,12 +2913,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "image-webp"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a84a25dcae3ac487bc24ef280f9e20c79c9b1a3e5e32cbed3041d1c514aa87c"
|
||||
checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"thiserror",
|
||||
"byteorder-lite",
|
||||
"quick-error 2.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2973,9 +2978,9 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "iroh-base"
|
||||
version = "0.26.0"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "973c0b3c7851fa2e8e6cf4cb81c9f4cab1373848828fafa43dfe25b123a89ff2"
|
||||
checksum = "1c21fd8eb71f166a172a9779c2244db992218e9a9bd929b9df6fc355d2b630c9"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"anyhow",
|
||||
@@ -2991,7 +2996,6 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"rand_core 0.6.4",
|
||||
"serde",
|
||||
"serde-error",
|
||||
"ssh-key",
|
||||
"thiserror",
|
||||
"ttl_cache",
|
||||
@@ -3014,9 +3018,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-gossip"
|
||||
version = "0.26.0"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b44749f361aca7cddcb357b62ef84cad1023771aee03d427c8922991fc4a9561"
|
||||
checksum = "c078057037f0e741c5ef285c67fd9cfdb928163dd046fb547089898bdb02990e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-channel 2.3.1",
|
||||
@@ -3031,10 +3035,12 @@ dependencies = [
|
||||
"iroh-blake3",
|
||||
"iroh-metrics",
|
||||
"iroh-net",
|
||||
"iroh-router",
|
||||
"postcard",
|
||||
"rand 0.8.5",
|
||||
"rand_core 0.6.4",
|
||||
"serde",
|
||||
"serde-error",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
@@ -3042,14 +3048,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-metrics"
|
||||
version = "0.26.0"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02edfa7ca1aa89b0b9793d94671e32b79c97d61e9022f082c5eb2b95b64e90c0"
|
||||
checksum = "e0d40f2ee3997489d47403d204a06514ed65373d224b5b43a8ea133f543e5db1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"erased_set",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"once_cell",
|
||||
"prometheus-client",
|
||||
@@ -3063,9 +3069,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-net"
|
||||
version = "0.26.0"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "329553be056a92eec7802e1c087eed951eec6e60f0f0740491ef16a295be4ca2"
|
||||
checksum = "b40e1f1f9029e198c6d05bd232d3239814b0a66ac4668978729b709aeb6a44e2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"backoff",
|
||||
@@ -3079,6 +3085,7 @@ dependencies = [
|
||||
"futures-lite 2.4.0",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"genawaiter",
|
||||
"governor",
|
||||
"hex",
|
||||
"hickory-proto",
|
||||
@@ -3086,7 +3093,7 @@ dependencies = [
|
||||
"hostname",
|
||||
"http 1.1.0",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"igd-next",
|
||||
"iroh-base",
|
||||
@@ -3099,11 +3106,13 @@ dependencies = [
|
||||
"netlink-packet-core",
|
||||
"netlink-packet-route",
|
||||
"netlink-sys",
|
||||
"netwatch",
|
||||
"num_enum",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"pin-project",
|
||||
"pkarr",
|
||||
"portmapper",
|
||||
"postcard",
|
||||
"rand 0.8.5",
|
||||
"rcgen",
|
||||
@@ -3139,9 +3148,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-quinn"
|
||||
version = "0.11.3"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fd590a39a14cfc168efa4d894de5039d65641e62d8da4a80733018ababe3c33"
|
||||
checksum = "35ba75a5c57cff299d2d7ca1ddee053f66339d1756bd79ec637bcad5aa61100e"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"iroh-quinn-proto",
|
||||
@@ -3157,9 +3166,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-quinn-proto"
|
||||
version = "0.11.6"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fd0538ff12efe3d61ea1deda2d7913f4270873a519d43e6995c6e87a1558538"
|
||||
checksum = "e2c869ba52683d3d067c83ab4c00a2fda18eaf13b1434d4c1352f428674d4a5d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"rand 0.8.5",
|
||||
@@ -3175,9 +3184,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iroh-quinn-udp"
|
||||
version = "0.5.4"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0619b59471fdd393ac8a6c047f640171119c1c8b41f7d2927db91776dcdbc5f"
|
||||
checksum = "bfcfc0abc2fdf8cf18a6c72893b7cbebeac2274a3b1306c1760c48c0e10ac5e0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"once_cell",
|
||||
@@ -3186,6 +3195,22 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iroh-router"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1fd18ec6325dd3f01625f12c01acff50a4374ee1ab708e7b2078885fd63ad30"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-buffered",
|
||||
"futures-lite 2.4.0",
|
||||
"futures-util",
|
||||
"iroh-net",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.12"
|
||||
@@ -3263,9 +3288,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kamadak-exif"
|
||||
version = "0.5.5"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077"
|
||||
checksum = "99e7b00ff45df279c3e40f7fee99fad4f7eddbf9ed2d24e99133e8683330f0c7"
|
||||
dependencies = [
|
||||
"mutate_once",
|
||||
]
|
||||
@@ -3314,9 +3339,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.159"
|
||||
version = "0.2.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -3426,6 +3451,26 @@ dependencies = [
|
||||
"quoted_printable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mainline"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b751ffb57303217bcae8f490eee6044a5b40eadf6ca05ff476cad37e7b7970d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"crc",
|
||||
"ed25519-dalek",
|
||||
"flume",
|
||||
"lru",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_bencode",
|
||||
"serde_bytes",
|
||||
"sha1_smol",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "match_cfg"
|
||||
version = "0.1.0"
|
||||
@@ -3499,13 +3544,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3623,6 +3669,35 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "netwatch"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a639d52c0996ac640e2a7052a5265c8f71efdbdadc83188435ffc358b7ca931"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"derive_more",
|
||||
"futures-lite 2.4.0",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"libc",
|
||||
"netdev",
|
||||
"netlink-packet-core",
|
||||
"netlink-packet-route",
|
||||
"netlink-sys",
|
||||
"once_cell",
|
||||
"rtnetlink",
|
||||
"serde",
|
||||
"socket2",
|
||||
"thiserror",
|
||||
"time 0.3.36",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"windows 0.51.1",
|
||||
"wmi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nibble_vec"
|
||||
version = "0.1.0"
|
||||
@@ -3840,9 +3915,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
@@ -4201,11 +4276,13 @@ checksum = "89f9e12544b00f5561253bbd3cb72a85ff3bc398483dc1bf82bdf095c774136b"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"document-features",
|
||||
"dyn-clone",
|
||||
"ed25519-dalek",
|
||||
"flume",
|
||||
"futures",
|
||||
"js-sys",
|
||||
"lru",
|
||||
"mainline",
|
||||
"self_cell",
|
||||
"simple-dns",
|
||||
"thiserror",
|
||||
@@ -4356,6 +4433,35 @@ version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
||||
|
||||
[[package]]
|
||||
name = "portmapper"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d60045fdcfe8ff6b781cf1027fdbb08ed319d93aff7da4bedc018e3bc92226"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"derive_more",
|
||||
"futures-lite 2.4.0",
|
||||
"futures-util",
|
||||
"igd-next",
|
||||
"iroh-metrics",
|
||||
"libc",
|
||||
"netwatch",
|
||||
"num_enum",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"socket2",
|
||||
"thiserror",
|
||||
"time 0.3.36",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.0.8"
|
||||
@@ -4454,6 +4560,32 @@ dependencies = [
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn-mid",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
@@ -4476,6 +4608,12 @@ dependencies = [
|
||||
"syn 2.0.86",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.20+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
@@ -4567,6 +4705,12 @@ version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.37.0"
|
||||
@@ -4879,9 +5023,9 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
@@ -4917,7 +5061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00"
|
||||
dependencies = [
|
||||
"hostname",
|
||||
"quick-error",
|
||||
"quick-error 1.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5121,9 +5265,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55"
|
||||
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-platform-verifier"
|
||||
@@ -5344,9 +5488,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde-error"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e988182713aeed6a619a88bca186f6d6407483485ffe44c869ee264f8eabd13f"
|
||||
checksum = "342110fb7a5d801060c885da03bf91bfa7c7ca936deafcc64bb6706375605d47"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_bencode"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a70dfc7b7438b99896e7f8992363ab8e2c4ba26aa5ec675d32d1c3c2c33d413e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -5375,9 +5538,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.128"
|
||||
version = "1.0.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@@ -5458,6 +5621,12 @@ dependencies = [
|
||||
"sha1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
@@ -5830,6 +5999,17 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn-mid"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea305d57546cc8cd04feb14b62ec84bf17f50e3f7b12560d7bfa9265f39d9ed"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
@@ -6037,21 +6217,20 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.38.1"
|
||||
version = "1.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
|
||||
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6066,9 +6245,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6350,9 +6529,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "typescript-type-def"
|
||||
version = "0.5.12"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c9a1ec7a0e59e03c7ab74e924abef19d2669aa9255b9821e55854b08453eb71"
|
||||
checksum = "233ee5e596f41dbaf8c3e48a60b128eadf89395ee9e45fcedc3281c077c5a014"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
"typescript-type-def-derive",
|
||||
@@ -6360,9 +6539,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "typescript-type-def-derive"
|
||||
version = "0.5.12"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34c1213de2386875250474a109f0985ce83333038c885d2fec4de79d9dfcee8c"
|
||||
checksum = "912e6d6fed61bac90cd957093b11b5330d756ad4e7b8f309f71ae04b546a8513"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"ident_case",
|
||||
@@ -7152,9 +7331,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.4.11"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448"
|
||||
checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768"
|
||||
dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
|
||||
27
Cargo.toml
27
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
rust-version = "1.77"
|
||||
@@ -46,7 +46,7 @@ async-native-tls = { version = "0.5", default-features = false, features = ["run
|
||||
async-smtp = { version = "0.9", default-features = false, features = ["runtime-tokio"] }
|
||||
async_zip = { version = "0.0.17", default-features = false, features = ["deflate", "tokio-fs"] }
|
||||
base64 = { workspace = true }
|
||||
brotli = { version = "6", default-features=false, features = ["std"] }
|
||||
brotli = { version = "7", default-features=false, features = ["std"] }
|
||||
bytes = "1"
|
||||
chrono = { workspace = true, features = ["alloc", "clock", "std"] }
|
||||
email = { git = "https://github.com/deltachat/rust-email", branch = "master" }
|
||||
@@ -62,10 +62,10 @@ http-body-util = "0.1.2"
|
||||
humansize = "2"
|
||||
hyper = "1"
|
||||
hyper-util = "0.1.10"
|
||||
image = { version = "0.25.1", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] }
|
||||
iroh-gossip = { version = "0.26.0", default-features = false, features = ["net"] }
|
||||
iroh-net = { version = "0.26.0", default-features = false }
|
||||
kamadak-exif = "0.5.3"
|
||||
image = { version = "0.25.4", 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"
|
||||
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
|
||||
libc = { workspace = true }
|
||||
mailparse = "0.15"
|
||||
@@ -85,7 +85,7 @@ rand = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
rusqlite = { workspace = true, features = ["sqlcipher"] }
|
||||
rust-hsluv = "0.1"
|
||||
rustls-pki-types = "1.9.0"
|
||||
rustls-pki-types = "1.10.0"
|
||||
rustls = { version = "0.23.14", default-features = false }
|
||||
sanitize-filename = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
@@ -171,13 +171,13 @@ chrono = { version = "0.4.38", default-features = false }
|
||||
deltachat-contact-tools = { path = "deltachat-contact-tools" }
|
||||
deltachat-jsonrpc = { path = "deltachat-jsonrpc", default-features = false }
|
||||
deltachat = { path = ".", default-features = false }
|
||||
futures = "0.3.30"
|
||||
futures = "0.3.31"
|
||||
futures-lite = "2.4.0"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
nu-ansi-term = "0.46"
|
||||
num-traits = "0.2"
|
||||
once_cell = "1.18.0"
|
||||
once_cell = "1.20.2"
|
||||
rand = "0.8"
|
||||
regex = "1.10"
|
||||
rusqlite = "0.32"
|
||||
@@ -186,14 +186,7 @@ serde = "1.0"
|
||||
serde_json = "1"
|
||||
tempfile = "3.13.0"
|
||||
thiserror = "1"
|
||||
|
||||
# 1.38 is the latest version before `mio` dependency update
|
||||
# that broke compilation with Android NDK r23c and r24.
|
||||
# Version 1.39.0 cannot be compiled using these NDKs,
|
||||
# see issue <https://github.com/tokio-rs/tokio/issues/6748>
|
||||
# for details.
|
||||
tokio = "~1.38.1"
|
||||
|
||||
tokio = "1"
|
||||
tokio-util = "0.7.11"
|
||||
tracing-subscriber = "0.3"
|
||||
yerpc = "0.6.2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
description = "Deltachat FFI"
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -506,6 +506,11 @@ char* dc_get_blobdir (const dc_context_t* context);
|
||||
* to not mess up with non-delivery-reports or read-receipts.
|
||||
* 0=no limit (default).
|
||||
* Changes affect future messages only.
|
||||
* - `protect_autocrypt` = Enable Header Protection for Autocrypt header.
|
||||
* This is an experimental option not compatible to other MUAs
|
||||
* and older Delta Chat versions.
|
||||
* 1 = enable.
|
||||
* 0 = disable (default).
|
||||
* - `gossip_period` = How often to gossip Autocrypt keys in chats with multiple recipients, in
|
||||
* seconds. 2 days by default.
|
||||
* This is not supposed to be changed by UIs and only used for testing.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
description = "DeltaChat JSON-RPC API"
|
||||
edition = "2021"
|
||||
default-run = "deltachat-jsonrpc-server"
|
||||
@@ -25,7 +25,7 @@ async-channel = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
yerpc = { workspace = true, features = ["anyhow_expose", "openrpc"] }
|
||||
typescript-type-def = { version = "0.5.12", features = ["json_value"] }
|
||||
typescript-type-def = { version = "0.5.13", features = ["json_value"] }
|
||||
tokio = { workspace = true }
|
||||
sanitize-filename = { workspace = true }
|
||||
walkdir = "2.5.0"
|
||||
|
||||
@@ -4,8 +4,8 @@ This crate provides a [JSON-RPC 2.0](https://www.jsonrpc.org/specification) inte
|
||||
|
||||
The JSON-RPC API is exposed in two fashions:
|
||||
|
||||
- A executable that exposes the JSON-RPC API through a [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) server running on localhost.
|
||||
- The JSON-RPC API can also be called through the [C FFI](../deltachat-ffi). The C FFI needs to be built with the `jsonrpc` feature. It will then expose the functions `dc_jsonrpc_init`, `dc_jsonrpc_request`, `dc_jsonrpc_next_response` and `dc_jsonrpc_unref`. See the docs in the [header file](../deltachat-ffi/deltachat.h) for details.
|
||||
* A executable that exposes the JSON-RPC API through a [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) server running on localhost.
|
||||
* The JSON-RPC API can also be called through the [C FFI](../deltachat-ffi). The C FFI needs to be built with the `jsonrpc` feature. It will then expose the functions `dc_jsonrpc_init`, `dc_jsonrpc_request`, `dc_jsonrpc_next_response` and `dc_jsonrpc_unref`. See the docs in the [header file](../deltachat-ffi/deltachat.h) for details.
|
||||
|
||||
We also include a JavaScript and TypeScript client for the JSON-RPC API. The source for this is in the [`typescript`](typescript) folder. The client can easily be used with the WebSocket server to build DeltaChat apps for web browsers or Node.js. See the [examples](typescript/example) for details.
|
||||
|
||||
@@ -24,17 +24,16 @@ If you want to use the server in a production setup, first build it in release m
|
||||
```sh
|
||||
cargo build --features webserver --release
|
||||
```
|
||||
|
||||
You will then find the `deltachat-jsonrpc-server` executable in your `target/release` folder.
|
||||
|
||||
The executable currently does not support any command-line arguments. By default, once started it will accept WebSocket connections on `ws://localhost:20808/ws`. It will store the persistent configuration and databases in a `./accounts` folder relative to the directory from where it is started.
|
||||
|
||||
The server can be configured with environment variables:
|
||||
|
||||
| variable | default | description |
|
||||
| ------------------ | ------------ | ------------------------- |
|
||||
| `DC_PORT` | `20808` | port to listen on |
|
||||
| `DC_ACCOUNTS_PATH` | `./accounts` | path to storage directory |
|
||||
|variable|default|description|
|
||||
|-|-|-|
|
||||
|`DC_PORT`|`20808`|port to listen on|
|
||||
|`DC_ACCOUNTS_PATH`|`./accounts`|path to storage directory|
|
||||
|
||||
If you are targeting other architectures (like KaiOS or Android), the webserver binary can be cross-compiled easily with [rust-cross](https://github.com/cross-rs/cross):
|
||||
|
||||
@@ -44,53 +43,30 @@ cross build --features=webserver --target armv7-linux-androideabi --release
|
||||
|
||||
#### Using the TypeScript/JavaScript client
|
||||
|
||||
The package includes a JavaScript/TypeScript client which is partially auto-generated through the JSON-RPC library used by this crate ([yerpc](https://github.com/Frando/yerpc/)). Find the source in the [`typescript`](typescript) folder.
|
||||
The package includes a JavaScript/TypeScript client which is partially auto-generated through the JSON-RPC library used by this crate ([yerpc](https://github.com/Frando/yerpc/)). Find the source in the [`typescript`](typescript) folder.
|
||||
|
||||
To use it locally, first install the dependencies and compile the TypeScript code to JavaScript:
|
||||
|
||||
```sh
|
||||
cd typescript
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
The package is also published on npm under the name [`@deltachat/jsonrpc-client`](https://www.npmjs.com/package/@deltachat/jsonrpc-client).
|
||||
The JavaScript client is not yet published on NPM (but will likely be soon). Currently, it is recommended to vendor the bundled build. After running `npm run build` as documented above, there will be a file `dist/deltachat.bundle.js`. This is an ESM module containing all dependencies. Copy this file to your project and import the DeltaChat class.
|
||||
|
||||
###### Usage
|
||||
|
||||
Stdio server (recommended):
|
||||
|
||||
```typescript
|
||||
import { startDeltaChat } from "@deltachat/stdio-rpc-server";
|
||||
import { C } from "@deltachat/jsonrpc-client";
|
||||
|
||||
const dc = await startDeltaChat("deltachat-data");
|
||||
console.log(await dc.rpc.getSystemInfo());
|
||||
const accounts = await dc.rpc.getAllAccounts();
|
||||
console.log("accounts", accounts);
|
||||
dc.close();
|
||||
```
|
||||
|
||||
Websocket:
|
||||
|
||||
```typescript
|
||||
import { WebsocketDeltaChat as DeltaChat } from '@deltachat/jsonrpc-client''=
|
||||
|
||||
import { DeltaChat } from './deltachat.bundle.js'
|
||||
const dc = new DeltaChat('ws://localhost:20808/ws')
|
||||
console.log(await dc.rpc.getSystemInfo());
|
||||
const accounts = await dc.rpc.getAllAccounts()
|
||||
console.log('accounts', accounts)
|
||||
```
|
||||
|
||||
##### Generate TypeScript/JavaScript documentation
|
||||
|
||||
A script is included to build autogenerated documentation, which includes all RPC methods:
|
||||
|
||||
```sh
|
||||
cd typescript
|
||||
npm run docs
|
||||
```
|
||||
|
||||
Then open the [`typescript/docs`](typescript/docs) folder in a web browser.
|
||||
|
||||
## Development
|
||||
@@ -105,7 +81,6 @@ npm run build
|
||||
npm run example:build
|
||||
npm run example:start
|
||||
```
|
||||
|
||||
Then, open [`http://localhost:8080/example.html`](http://localhost:8080/example.html) in a web browser.
|
||||
|
||||
Run `npm run example:dev` to live-rebuild the example app when files changes.
|
||||
@@ -129,7 +104,7 @@ cd typescript
|
||||
npm run test
|
||||
```
|
||||
|
||||
This will build the `deltachat-rpc-server` binary and then run a test suite against the deltachat-rpc-server (stdio).
|
||||
This will build the `deltachat-jsonrpc-server` binary and then run a test suite against the WebSocket server.
|
||||
|
||||
The test suite includes some tests that need online connectivity and a way to create test email accounts. To run these tests, talk to DeltaChat developers to get a token for the `testrun.org` service, or use a local instance of [`mailadm`](https://github.com/deltachat/docker-mailadm).
|
||||
|
||||
|
||||
@@ -1419,6 +1419,15 @@ impl CommandApi {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resets contact encryption.
|
||||
async fn reset_contact_encryption(&self, account_id: u32, contact_id: u32) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let contact_id = ContactId::new(contact_id);
|
||||
|
||||
contact_id.reset_encryption(&ctx).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn change_contact_name(
|
||||
&self,
|
||||
account_id: u32,
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
# @deltachat/jsonrpc-client
|
||||
|
||||
This package is a client for the jsonrpc server.
|
||||
|
||||
> If you are looking for the functions in the documentation, they are under [`RawClient`](https://js.jsonrpc.delta.chat/classes/RawClient.html).
|
||||
|
||||
### Important Terms
|
||||
|
||||
- [delta chat core](https://github.com/deltachat/deltachat-core-rust/) the heart of all Delta Chat clients. Handels all the heavy lifting (email, encryption, ...) and provides an easy api for bots and clients (`getChatlist`, `getChat`, `getContact`, ...).
|
||||
- [jsonrpc](https://www.jsonrpc.org/specification) is a json based protocol
|
||||
for applications to speak to each other by [remote procedure calls](https://en.wikipedia.org/wiki/Remote_procedure_call) (short RPC),
|
||||
which basically means that the client can call methods on the server by sending a json messages.
|
||||
- [`deltachat-rpc-server`](https://github.com/deltachat/deltachat-core-rust/tree/main/deltachat-rpc-server) provides the jsonrpc api over stdio (stdin/stdout)
|
||||
- [`@deltachat/stdio-rpc-server`](https://www.npmjs.com/package/@deltachat/stdio-rpc-server) is an easy way to install `deltachat-rpc-server` from npm and use it from nodejs.
|
||||
|
||||
#### Transport
|
||||
|
||||
You need to connect this client to an instance of deltachat core via a transport.
|
||||
|
||||
Currently there are 2 transports available:
|
||||
|
||||
- (recomended) `StdioTransport` usable from `StdioDeltaChat` - speak to `deltachat-rpc-server` directly
|
||||
- `WebsocketTransport` usable from `WebsocketDeltaChat`
|
||||
|
||||
You can also make your own transport, for example deltachat desktop uses a custom transport that sends the json messages over electron ipc.
|
||||
Just implement your transport based on the `Transport` interface - look at how the [stdio transport is implemented](https://github.com/deltachat/deltachat-core-rust/blob/7121675d226e69fd85d0194d4b9c4442e4dd8299/deltachat-jsonrpc/typescript/src/client.ts#L113) for an example, it's not hard.
|
||||
|
||||
## Usage
|
||||
|
||||
> The **minimum** nodejs version for `@deltachat/stdio-rpc-server` is `16`
|
||||
|
||||
```
|
||||
npm i @deltachat/stdio-rpc-server @deltachat/jsonrpc-client
|
||||
```
|
||||
|
||||
```js
|
||||
import { startDeltaChat } from "@deltachat/stdio-rpc-server";
|
||||
// Import constants you might need later
|
||||
import { C } from "@deltachat/jsonrpc-client";
|
||||
|
||||
async function main() {
|
||||
const dc = await startDeltaChat("deltachat-data");
|
||||
console.log(await dc.rpc.getSystemInfo());
|
||||
dc.close();
|
||||
}
|
||||
main();
|
||||
```
|
||||
|
||||
For a more complete example refer to <https://github.com/deltachat-bot/echo/tree/master/nodejs_stdio_jsonrpc>.
|
||||
|
||||
### Listening for events
|
||||
|
||||
```ts
|
||||
dc.on("Info", (accountId, { msg }) =>
|
||||
console.info(accountId, "[core:info]", msg)
|
||||
);
|
||||
// Or get an event emitter for only one account
|
||||
const emitter = dc.getContextEvents(accountId);
|
||||
emitter.on("IncomingMsg", async ({ chatId, msgId }) => {
|
||||
const message = await dc.rpc.getMessage(accountId, msgId);
|
||||
console.log("got message in chat " + chatId + " : ", message.text);
|
||||
});
|
||||
```
|
||||
|
||||
### Getting Started
|
||||
|
||||
This section describes how to handle the Delta Chat core library over the jsonrpc bindings.
|
||||
For general information about Delta Chat itself,
|
||||
see <https://delta.chat> and <https://github.com/deltachat>.
|
||||
|
||||
Let's start.
|
||||
|
||||
First of all, you have to start the deltachat-rpc-server process.
|
||||
|
||||
```js
|
||||
import { startDeltaChat } from "@deltachat/stdio-rpc-server";
|
||||
const dc = await startDeltaChat("deltachat-data");
|
||||
```
|
||||
|
||||
Then we have to create an Account (also called Context or profile) that is bound to a database.
|
||||
The database is a normal SQLite file with a "blob directory" beside it.
|
||||
But these details are handled by deltachat's account manager.
|
||||
So you just have to tell the account manager to create a new account:
|
||||
|
||||
```js
|
||||
const accountId = await dc.rpc.addAccount();
|
||||
```
|
||||
|
||||
After that, register event listeners so you can see what core is doing:
|
||||
Intenally `@deltachat/jsonrpc-client` implments a loop that waits for new events and then emits them to javascript land.
|
||||
```js
|
||||
dc.on("Info", (accountId, { msg }) =>
|
||||
console.info(accountId, "[core:info]", msg)
|
||||
);
|
||||
```
|
||||
|
||||
Now you can **configure the account:**
|
||||
```js
|
||||
// use some real test credentials here
|
||||
await dc.rpc.setConfig(accountId, "addr", "alice@example.org")
|
||||
await dc.rpc.setConfig(accountId, "mail_pw", "***")
|
||||
// you can also set multiple config options in one call
|
||||
await dc.rpc.batchSetConfig(accountId, {
|
||||
"addr": "alice@example.org",
|
||||
"mail_pw": "***"
|
||||
})
|
||||
|
||||
// after setting the credentials attempt to login
|
||||
await dc.rpc.configure(accountId)
|
||||
```
|
||||
|
||||
`configure()` returns a promise that is rejected on error (with await is is thrown).
|
||||
The configuration itself may take a while. You can monitor it's progress like this:
|
||||
```js
|
||||
dc.on("ConfigureProgress", (accountId, { progress, comment }) => {
|
||||
console.log(accountId, "ConfigureProgress", progress, comment);
|
||||
});
|
||||
// make sure to register this event handler before calling `dc.rpc.configure()`
|
||||
```
|
||||
|
||||
The configuration result is saved in the database.
|
||||
On subsequent starts it is not needed to call `dc.rpc.configure(accountId)`
|
||||
(you can check this using `dc.rpc.isConfigured(accountId)`).
|
||||
|
||||
On a successfully configuration delta chat core automatically connects to the server, however subsequent starts you **need to do that manually** by calling `dc.rpc.startIo(accountId)` or `dc.rpc.startIoForAllAccounts()`.
|
||||
|
||||
```js
|
||||
if (!await dc.rpc.isConfigured(accountId)) {
|
||||
// use some real test credentials here
|
||||
await dc.rpc.batchSetConfig(accountId, {
|
||||
"addr": "alice@example.org",
|
||||
"mail_pw": "***"
|
||||
})
|
||||
await dc.rpc.configure(accountId)
|
||||
} else {
|
||||
await dc.rpc.startIo(accountId)
|
||||
}
|
||||
```
|
||||
|
||||
Now you can **send the first message:**
|
||||
|
||||
```js
|
||||
const contactId = await dc.rpc.createContact(accountId, "bob@example.org", null /* optional name */)
|
||||
const chatId = await dc.rpc.createChatByContactId(accountId, contactId)
|
||||
|
||||
await dc.rpc.miscSendTextMessage(accountId, chatId, "Hi, here is my first message!")
|
||||
```
|
||||
|
||||
`dc.rpc.miscSendTextMessage()` returns immediately;
|
||||
the sending itself is done in the background.
|
||||
If you check the testing address (bob),
|
||||
you should receive a normal e-mail.
|
||||
Answer this e-mail in any e-mail program with "Got it!",
|
||||
and the IO you started above will **receive the message**.
|
||||
|
||||
You can then **list all messages** of a chat as follows:
|
||||
|
||||
```js
|
||||
let i = 0;
|
||||
for (const msgId of await exp.rpc.getMessageIds(120, 12, false, false)) {
|
||||
i++;
|
||||
console.log(`Message: ${i}`, (await dc.rpc.getMessage(120, msgId)).text);
|
||||
}
|
||||
```
|
||||
|
||||
This will output the following two lines:
|
||||
```
|
||||
Message 1: Hi, here is my first message!
|
||||
Message 2: Got it!
|
||||
```
|
||||
|
||||
<!-- TODO: ### Clean shutdown? - seems to be more advanced to call async functions on exit, also is this needed in this usecase? -->
|
||||
|
||||
## Further information
|
||||
|
||||
- `@deltachat/stdio-rpc-server`
|
||||
- [package on npm](https://www.npmjs.com/package/@deltachat/stdio-rpc-server)
|
||||
- [source code on github](https://github.com/deltachat/deltachat-core-rust/tree/main/deltachat-rpc-server/npm-package)
|
||||
- [use `@deltachat/stdio-rpc-server` on an usuported platform](https://github.com/deltachat/deltachat-core-rust/tree/main/deltachat-rpc-server/npm-package#how-to-use-on-an-unsupported-platform)
|
||||
- The issue-tracker for the core library is here: <https://github.com/deltachat/deltachat-core-rust/issues>
|
||||
|
||||
If you need further assistance,
|
||||
please do not hesitate to contact us
|
||||
through the channels shown at https://delta.chat/en/contribute
|
||||
|
||||
Please keep in mind, that your derived work
|
||||
must respect the Mozilla Public License 2.0 of deltachat-rpc-server
|
||||
and the respective licenses of the libraries deltachat-rpc-server links with.
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DcEvent, WebsocketDeltaChat as DeltaChat } from "../deltachat.js";
|
||||
import { DcEvent, DeltaChat } from "../deltachat.js";
|
||||
|
||||
var SELECTED_ACCOUNT = 0;
|
||||
|
||||
|
||||
@@ -58,5 +58,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "dist/deltachat.d.ts",
|
||||
"version": "1.148.6"
|
||||
"version": "1.149.0"
|
||||
}
|
||||
|
||||
@@ -5,14 +5,14 @@ import { RawClient } from "../generated/client.js";
|
||||
import { WebsocketTransport, BaseTransport, Request } from "yerpc";
|
||||
import { TinyEmitter } from "@deltachat/tiny-emitter";
|
||||
|
||||
export type Events = { ALL: (accountId: number, event: EventType) => void } & {
|
||||
type Events = { ALL: (accountId: number, event: EventType) => void } & {
|
||||
[Property in EventType["kind"]]: (
|
||||
accountId: number,
|
||||
event: Extract<EventType, { kind: Property }>
|
||||
) => void;
|
||||
};
|
||||
|
||||
export type ContextEvents = { ALL: (event: EventType) => void } & {
|
||||
type ContextEvents = { ALL: (event: EventType) => void } & {
|
||||
[Property in EventType["kind"]]: (
|
||||
event: Extract<EventType, { kind: Property }>
|
||||
) => void;
|
||||
@@ -83,7 +83,7 @@ export const DEFAULT_OPTS: Opts = {
|
||||
url: "ws://localhost:20808/ws",
|
||||
startEventLoop: true,
|
||||
};
|
||||
export class WebsocketDeltaChat extends BaseDeltaChat<WebsocketTransport> {
|
||||
export class DeltaChat extends BaseDeltaChat<WebsocketTransport> {
|
||||
opts: Opts;
|
||||
close() {
|
||||
this.transport.close();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-repl"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/deltachat/deltachat-core-rust"
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deltachat-rpc-client"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
description = "Python client for Delta Chat core JSON-RPC interface"
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
|
||||
@@ -36,6 +36,10 @@ class Contact:
|
||||
"""Delete contact."""
|
||||
self._rpc.delete_contact(self.account.id, self.id)
|
||||
|
||||
def reset_encryption(self) -> None:
|
||||
"""Reset contact encryption."""
|
||||
self._rpc.reset_contact_encryption(self.account.id, self.id)
|
||||
|
||||
def set_name(self, name: str) -> None:
|
||||
"""Change the name of this contact."""
|
||||
self._rpc.change_contact_name(self.account.id, self.id, name)
|
||||
|
||||
@@ -7,8 +7,8 @@ If you want to debug iroh at rust-trace/log level set
|
||||
RUST_LOG=iroh_net=trace,iroh_gossip=trace
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
@@ -25,9 +25,7 @@ def path_to_webxdc(request):
|
||||
|
||||
|
||||
def log(msg):
|
||||
print()
|
||||
print("*" * 80 + "\n" + msg + "\n", file=sys.stderr)
|
||||
print()
|
||||
logging.info(msg)
|
||||
|
||||
|
||||
def setup_realtime_webxdc(ac1, ac2, path_to_webxdc):
|
||||
|
||||
@@ -57,8 +57,8 @@ def test_acfactory(acfactory) -> None:
|
||||
if event.progress == 1000: # Success
|
||||
break
|
||||
else:
|
||||
print(event)
|
||||
print("Successful configuration")
|
||||
logging.info(event)
|
||||
logging.info("Successful configuration")
|
||||
|
||||
|
||||
def test_configure_starttls(acfactory) -> None:
|
||||
@@ -246,6 +246,7 @@ def test_contact(acfactory) -> None:
|
||||
assert repr(alice_contact_bob)
|
||||
alice_contact_bob.block()
|
||||
alice_contact_bob.unblock()
|
||||
alice_contact_bob.reset_encryption()
|
||||
alice_contact_bob.set_name("new name")
|
||||
alice_contact_bob.get_encryption_info()
|
||||
snapshot = alice_contact_bob.get_snapshot()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
description = "DeltaChat JSON-RPC server"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -18,46 +18,20 @@ import { startDeltaChat } from "@deltachat/stdio-rpc-server";
|
||||
import { C } from "@deltachat/jsonrpc-client";
|
||||
|
||||
async function main() {
|
||||
const dc = await startDeltaChat("deltachat-data");
|
||||
console.log(await dc.rpc.getSystemInfo());
|
||||
dc.close();
|
||||
const dc = await startDeltaChat("deltachat-data");
|
||||
console.log(await dc.rpc.getSystemInfo());
|
||||
dc.close()
|
||||
}
|
||||
main();
|
||||
main()
|
||||
```
|
||||
|
||||
For a more complete example refer to https://github.com/deltachat-bot/echo/tree/master/nodejs_stdio_jsonrpc.
|
||||
For a more complete example refer to https://github.com/deltachat-bot/echo/pull/69/files (TODO change link when pr is merged).
|
||||
|
||||
## How to use on an unsupported platform
|
||||
|
||||
You need to have rust installed to compile deltachat core for your platform and cpu architecture.
|
||||
<https://rustup.rs/> is the recommended way to install rust.
|
||||
Also your system probably needs more than 4gb ram to compile core, alternatively your could try to build the debug build, that might take less ram to build.
|
||||
<!-- todo instructions, will uses an env var for pointing to `deltachat-rpc-server` binary -->
|
||||
|
||||
1. clone the core repo, right next to your project folder: `git clone git@github.com:deltachat/deltachat-core-rust.git`
|
||||
2. go into your core checkout and run `git pull` and `git checkout <version>` to point it to the correct version (needs to be the same version the `@deltachat/jsonrpc-client` package has)
|
||||
3. run `cargo build --release --package deltachat-rpc-server --bin deltachat-rpc-server`
|
||||
|
||||
Then you have 2 options:
|
||||
|
||||
### point to deltachat-rpc-server via direct path:
|
||||
|
||||
```sh
|
||||
# start your app with the DELTA_CHAT_RPC_SERVER env var
|
||||
DELTA_CHAT_RPC_SERVER="../deltachat-core-rust/target/release/deltachat-rpc-server" node myapp.js
|
||||
```
|
||||
|
||||
### install deltachat-rpc-server in your $PATH:
|
||||
|
||||
```sh
|
||||
# use this to install to ~/.cargo/bin
|
||||
cargo install --release --package deltachat-rpc-server --bin deltachat-rpc-server
|
||||
# or manually move deltachat-core-rust/target/release/deltachat-rpc-server
|
||||
# to a location that is included in your $PATH Environment variable.
|
||||
```
|
||||
|
||||
```js
|
||||
startDeltaChat("data-dir", { takeVersionFromPATH: true });
|
||||
```
|
||||
<!-- todo copy parts from https://github.com/deltachat/deltachat-desktop/blob/7045c6f549e4b9d5caa0709d5bd314bbd9fd53db/docs/UPDATE_CORE.md -->
|
||||
|
||||
## How does it work when you install it
|
||||
|
||||
@@ -72,7 +46,7 @@ references:
|
||||
When you import this package it searches for the rpc server in the following locations and order:
|
||||
|
||||
1. `DELTA_CHAT_RPC_SERVER` environment variable
|
||||
2. use the PATH when `{takeVersionFromPATH: true}` is supplied in the options.
|
||||
2. use the PATH when `{takeVersionFromPATH: true}` is supplied in the options.
|
||||
3. prebuilds in npm packages
|
||||
|
||||
so by default it uses the prebuilds.
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "index.d.ts",
|
||||
"version": "1.148.6"
|
||||
"version": "1.149.0"
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ ignore = [
|
||||
|
||||
# Unmaintained encoding
|
||||
"RUSTSEC-2021-0153",
|
||||
|
||||
# Unmaintained proc-macro-error
|
||||
# <https://rustsec.org/advisories/RUSTSEC-2024-0370>
|
||||
"RUSTSEC-2024-0370",
|
||||
]
|
||||
|
||||
[bans]
|
||||
@@ -28,10 +32,7 @@ skip = [
|
||||
{ name = "fastrand", version = "1.9.0" },
|
||||
{ name = "futures-lite", version = "1.13.0" },
|
||||
{ name = "getrandom", version = "<0.2" },
|
||||
{ name = "h2", version = "0.3.26" },
|
||||
{ name = "http-body", version = "0.4.6" },
|
||||
{ name = "http", version = "0.2.12" },
|
||||
{ name = "hyper", version = "0.14.28" },
|
||||
{ name = "nix", version = "0.26.4" },
|
||||
{ name = "quick-error", version = "<2.0" },
|
||||
{ name = "rand_chacha", version = "<0.3" },
|
||||
|
||||
62
flake.lock
generated
62
flake.lock
generated
@@ -3,15 +3,15 @@
|
||||
"android": {
|
||||
"inputs": {
|
||||
"devshell": "devshell",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712088936,
|
||||
"narHash": "sha256-mVjeSWQiR/t4UZ9fUawY9OEPAhY1R3meYG+0oh8DUBs=",
|
||||
"lastModified": 1729628358,
|
||||
"narHash": "sha256-2HDSc6BL+bE3S1l3Gn0Z8wWvvfBEUEjvXkNIQ11Aifk=",
|
||||
"owner": "tadfisher",
|
||||
"repo": "android-nixpkgs",
|
||||
"rev": "2d8181caef279f19c4a33dc694723f89ffc195d4",
|
||||
"rev": "52b9e0c0f9cff887d2bb4932f8be4e062ba0802d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -22,18 +22,17 @@
|
||||
},
|
||||
"devshell": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
"android",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1711099426,
|
||||
"narHash": "sha256-HzpgM/wc3aqpnHJJ2oDqPBkNsqWbW0WfWUO8lKu8nGk=",
|
||||
"lastModified": 1728330715,
|
||||
"narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "2d45b54ca4a183f2fdcf4b19c895b64fbf620ee8",
|
||||
"rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -66,11 +65,11 @@
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"lastModified": 1726560853,
|
||||
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -97,24 +96,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_3": {
|
||||
"inputs": {
|
||||
"systems": "systems_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"naersk": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
@@ -150,11 +131,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1711703276,
|
||||
"narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=",
|
||||
"lastModified": 1729413321,
|
||||
"narHash": "sha256-I4tuhRpZFa6Fu6dcH9Dlo5LlH17peT79vx1y1SpeKt0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d8fe5e6c92d0d190646fb9f1056741a229980089",
|
||||
"rev": "1997e4aa514312c1af7e2bda7fad1644e778ff26",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -213,7 +194,7 @@
|
||||
"inputs": {
|
||||
"android": "android",
|
||||
"fenix": "fenix",
|
||||
"flake-utils": "flake-utils_3",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"naersk": "naersk",
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs_4"
|
||||
@@ -265,21 +246,6 @@
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_3": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
14
flake.nix
14
flake.nix
@@ -18,9 +18,9 @@
|
||||
manifest = (pkgs.lib.importTOML ./Cargo.toml).package;
|
||||
androidSdk = android.sdk.${system} (sdkPkgs:
|
||||
builtins.attrValues {
|
||||
inherit (sdkPkgs) ndk-24-0-8215888 cmdline-tools-latest;
|
||||
inherit (sdkPkgs) ndk-27-0-11902837 cmdline-tools-latest;
|
||||
});
|
||||
androidNdkRoot = "${androidSdk}/share/android-sdk/ndk/24.0.8215888";
|
||||
androidNdkRoot = "${androidSdk}/share/android-sdk/ndk/27.0.11902837";
|
||||
|
||||
rustSrc = nix-filter.lib {
|
||||
root = ./.;
|
||||
@@ -257,13 +257,21 @@
|
||||
|
||||
androidAttrs = {
|
||||
armeabi-v7a = {
|
||||
cc = "armv7a-linux-androideabi19-clang";
|
||||
cc = "armv7a-linux-androideabi21-clang";
|
||||
rustTarget = "armv7-linux-androideabi";
|
||||
};
|
||||
arm64-v8a = {
|
||||
cc = "aarch64-linux-android21-clang";
|
||||
rustTarget = "aarch64-linux-android";
|
||||
};
|
||||
x86 = {
|
||||
cc = "i686-linux-android21-clang";
|
||||
rustTarget = "i686-linux-android";
|
||||
};
|
||||
x86_64 = {
|
||||
cc = "x86_64-linux-android21-clang";
|
||||
rustTarget = "x86_64-linux-android";
|
||||
};
|
||||
};
|
||||
|
||||
mkAndroidRustPackage = arch: packageName:
|
||||
|
||||
@@ -55,5 +55,5 @@
|
||||
"test:mocha": "mocha node/test/test.mjs --growl --reporter=spec --bail --exit"
|
||||
},
|
||||
"types": "node/dist/index.d.ts",
|
||||
"version": "1.148.6"
|
||||
"version": "1.149.0"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deltachat"
|
||||
version = "1.148.6"
|
||||
version = "1.149.0"
|
||||
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-10-31
|
||||
2024-11-05
|
||||
@@ -520,8 +520,13 @@ Authentication-Results: dkim=";
|
||||
handle_authres(&t, &mail, "invalid@rom.com").await.unwrap();
|
||||
}
|
||||
|
||||
// Test that Autocrypt works with mailing list.
|
||||
//
|
||||
// Previous versions of Delta Chat ignored Autocrypt based on the List-Post header.
|
||||
// This is not needed: comparing of the From address to Autocrypt header address is enough.
|
||||
// If the mailing list is not rewriting the From header, Autocrypt should be applied.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_autocrypt_in_mailinglist_ignored() -> Result<()> {
|
||||
async fn test_autocrypt_in_mailinglist_not_ignored() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
@@ -533,28 +538,18 @@ Authentication-Results: dkim=";
|
||||
.insert_str(0, "List-Post: <mailto:deltachat-community.example.net>\n");
|
||||
bob.recv_msg(&sent).await;
|
||||
let peerstate = Peerstate::from_addr(&bob, "alice@example.org").await?;
|
||||
assert!(peerstate.is_none());
|
||||
|
||||
// Do the same without the mailing list header, this time the peerstate should be accepted
|
||||
let sent = alice
|
||||
.send_text(alice_bob_chat.id, "hellooo without mailing list")
|
||||
.await;
|
||||
bob.recv_msg(&sent).await;
|
||||
let peerstate = Peerstate::from_addr(&bob, "alice@example.org").await?;
|
||||
assert!(peerstate.is_some());
|
||||
|
||||
// This also means that Bob can now write encrypted to Alice:
|
||||
// Bob can now write encrypted to Alice:
|
||||
let mut sent = bob
|
||||
.send_text(bob_alice_chat.id, "hellooo in the mailinglist again")
|
||||
.await;
|
||||
assert!(sent.load_from_db().await.get_showpadlock());
|
||||
|
||||
// But if Bob writes to a mailing list, Alice doesn't show a padlock
|
||||
// since she can't verify the signature without accepting Bob's key:
|
||||
sent.payload
|
||||
.insert_str(0, "List-Post: <mailto:deltachat-community.example.net>\n");
|
||||
let rcvd = alice.recv_msg(&sent).await;
|
||||
assert!(!rcvd.get_showpadlock());
|
||||
assert!(rcvd.get_showpadlock());
|
||||
assert_eq!(&rcvd.text, "hellooo in the mailinglist again");
|
||||
|
||||
Ok(())
|
||||
|
||||
82
src/chat.rs
82
src/chat.rs
@@ -3481,29 +3481,6 @@ pub async fn get_chat_contacts(context: &Context, chat_id: ChatId) -> Result<Vec
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
/// Returns a vector of contact IDs for given chat ID where the contact is not SELF.
|
||||
pub async fn get_other_chat_contacts(context: &Context, chat_id: ChatId) -> Result<Vec<ContactId>> {
|
||||
// Normal chats do not include SELF. Group chats do (as it may happen that one is deleted from a
|
||||
// groupchat but the chats stays visible, moreover, this makes displaying lists easier)
|
||||
|
||||
let list = context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT cc.contact_id
|
||||
FROM chats_contacts cc
|
||||
LEFT JOIN contacts c
|
||||
ON c.id=cc.contact_id
|
||||
WHERE cc.chat_id=? AND c.id!=1
|
||||
ORDER BY c.id=1, c.last_seen DESC, c.id DESC;",
|
||||
(chat_id,),
|
||||
|row| row.get::<_, ContactId>(0),
|
||||
|ids| ids.collect::<Result<Vec<_>, _>>().map_err(Into::into),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
/// Creates a group chat with a given `name`.
|
||||
pub async fn create_group_chat(
|
||||
context: &Context,
|
||||
@@ -4528,6 +4505,7 @@ pub(crate) async fn delete_and_reset_all_device_msgs(context: &Context) -> Resul
|
||||
/// Adds an informational message to chat.
|
||||
///
|
||||
/// For example, it can be a message showing that a member was added to a group.
|
||||
/// Doesn't fail if the chat doesn't exist.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn add_info_msg_with_cmd(
|
||||
context: &Context,
|
||||
@@ -4733,6 +4711,7 @@ impl Context {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::chat;
|
||||
use crate::chatlist::get_archived_cnt;
|
||||
use crate::constants::{DC_GCL_ARCHIVED_ONLY, DC_GCL_NO_SPECIALS};
|
||||
use crate::headerdef::HeaderDef;
|
||||
@@ -7718,4 +7697,61 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_reply_with_alias() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
|
||||
// Alice sends a message to Bob
|
||||
let alice_bob_chat = alice.create_chat(&bob).await;
|
||||
let alice_msg = alice.send_text(alice_bob_chat.id, "Hello Bob!").await;
|
||||
let msg = alice_msg.load_from_db().await;
|
||||
bob.recv_msg(&alice_msg).await;
|
||||
|
||||
// Bob replies using an alias email address
|
||||
let alias_email = "bob.alias@example.org";
|
||||
let reply_imf = format!(
|
||||
"From: Bob Alias <{}>\r\n\
|
||||
To: {}\r\n\
|
||||
In-Reply-To: <{}>\r\n\
|
||||
Message-ID: <reply1@alias.example.org>\r\n\
|
||||
\r\n\
|
||||
Hi Alice, this is Bob's alias.",
|
||||
alias_email,
|
||||
alice.get_primary_self_addr().await?,
|
||||
msg.rfc724_mid
|
||||
);
|
||||
|
||||
// Alice receives the raw message
|
||||
let received = receive_imf(&alice, reply_imf.as_bytes(), false)
|
||||
.await?
|
||||
.unwrap();
|
||||
|
||||
// The message should appear in the existing 1:1 chat between Alice and Bob
|
||||
assert_eq!(received.chat_id, alice_bob_chat.id);
|
||||
|
||||
let msg = Message::load_from_db(&alice, received.msg_ids[0]).await?;
|
||||
assert_eq!(msg.text, "Hi Alice, this is Bob's alias.");
|
||||
|
||||
// Alias is displayed in chat
|
||||
assert_eq!(
|
||||
msg.param.get(Param::OverrideSenderDisplayname).unwrap(),
|
||||
"Bob Alias"
|
||||
);
|
||||
|
||||
// Chat remains 1:1
|
||||
let contacts = chat::get_chat_contacts(&alice, alice_bob_chat.id)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(contacts, vec![ContactId::new(10)]);
|
||||
|
||||
// Following messages still go to 1:1 chat
|
||||
let sent = alice.send_text(alice_bob_chat.id, "Hello again!").await;
|
||||
let msg = Message::load_from_db(&alice, sent.sender_msg_id).await?;
|
||||
assert_eq!(msg.chat_id, alice_bob_chat.id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,6 +396,12 @@ pub enum Config {
|
||||
/// Make all outgoing messages with Autocrypt header "multipart/signed".
|
||||
SignUnencrypted,
|
||||
|
||||
/// Enable header protection for `Autocrypt` header.
|
||||
///
|
||||
/// This is an experimental setting not compatible to other MUAs
|
||||
/// and older Delta Chat versions (core version <= v1.149.0).
|
||||
ProtectAutocrypt,
|
||||
|
||||
/// Let the core save all events to the database.
|
||||
/// This value is used internally to remember the MsgId of the logging xdc
|
||||
#[strum(props(default = "0"))]
|
||||
|
||||
@@ -143,6 +143,43 @@ impl ContactId {
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns contact adress.
|
||||
pub async fn addr(&self, context: &Context) -> Result<String> {
|
||||
let addr = context
|
||||
.sql
|
||||
.query_row("SELECT addr FROM contacts WHERE id=?", (self,), |row| {
|
||||
let addr: String = row.get(0)?;
|
||||
Ok(addr)
|
||||
})
|
||||
.await?;
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
/// Resets encryption with the contact.
|
||||
///
|
||||
/// Effect is similar to receiving a message without Autocrypt header
|
||||
/// from the contact, but this action is triggered manually by the user.
|
||||
///
|
||||
/// For example, this will result in sending the next message
|
||||
/// to 1:1 chat unencrypted, but will not remove existing verified keys.
|
||||
pub async fn reset_encryption(self, context: &Context) -> Result<()> {
|
||||
let now = time();
|
||||
|
||||
let addr = self.addr(context).await?;
|
||||
if let Some(mut peerstate) = Peerstate::from_addr(context, &addr).await? {
|
||||
peerstate.degrade_encryption(now);
|
||||
peerstate.save_to_db(&context.sql).await?;
|
||||
}
|
||||
|
||||
// Reset 1:1 chat protection.
|
||||
if let Some(chat_id) = ChatId::lookup_by_contact(context, self).await? {
|
||||
chat_id
|
||||
.set_protection(context, ProtectionStatus::Unprotected, now, Some(self))
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ContactId {
|
||||
@@ -3152,4 +3189,59 @@ Until the false-positive is fixed:
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_reset_encryption() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
let msg = tcm.send_recv_accept(alice, bob, "Hello!").await;
|
||||
assert_eq!(msg.get_showpadlock(), false);
|
||||
|
||||
let msg = tcm.send_recv(bob, alice, "Hi!").await;
|
||||
assert_eq!(msg.get_showpadlock(), true);
|
||||
let alice_bob_contact_id = msg.from_id;
|
||||
|
||||
alice_bob_contact_id.reset_encryption(alice).await?;
|
||||
|
||||
let msg = tcm.send_recv(alice, bob, "Unencrypted").await;
|
||||
assert_eq!(msg.get_showpadlock(), false);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_reset_verified_encryption() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
tcm.execute_securejoin(bob, alice).await;
|
||||
|
||||
let msg = tcm.send_recv(bob, alice, "Encrypted").await;
|
||||
assert_eq!(msg.get_showpadlock(), true);
|
||||
|
||||
let alice_bob_chat_id = msg.chat_id;
|
||||
let alice_bob_contact_id = msg.from_id;
|
||||
alice_bob_contact_id.reset_encryption(alice).await?;
|
||||
|
||||
// Check that the contact is still verified after resetting encryption.
|
||||
let alice_bob_contact = Contact::get_by_id(alice, alice_bob_contact_id).await?;
|
||||
assert_eq!(alice_bob_contact.is_verified(alice).await?, true);
|
||||
|
||||
// 1:1 chat and profile is no longer verified.
|
||||
assert_eq!(alice_bob_contact.is_profile_verified(alice).await?, false);
|
||||
|
||||
let info_msg = alice.get_last_msg_in(alice_bob_chat_id).await;
|
||||
assert_eq!(
|
||||
info_msg.text,
|
||||
"bob@example.net sent a message from another device."
|
||||
);
|
||||
|
||||
let msg = tcm.send_recv(alice, bob, "Unencrypted").await;
|
||||
assert_eq!(msg.get_showpadlock(), false);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -990,6 +990,12 @@ impl Context {
|
||||
.await?
|
||||
.to_string(),
|
||||
);
|
||||
res.insert(
|
||||
"protect_autocrypt",
|
||||
self.get_config_int(Config::ProtectAutocrypt)
|
||||
.await?
|
||||
.to_string(),
|
||||
);
|
||||
res.insert(
|
||||
"debug_logging",
|
||||
self.get_config_int(Config::DebugLogging).await?.to_string(),
|
||||
|
||||
150
src/decrypt.rs
150
src/decrypt.rs
@@ -1,125 +1,36 @@
|
||||
//! End-to-end decryption support.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Result;
|
||||
use deltachat_contact_tools::addr_cmp;
|
||||
use mailparse::ParsedMail;
|
||||
|
||||
use crate::aheader::Aheader;
|
||||
use crate::authres::handle_authres;
|
||||
use crate::authres::{self, DkimResults};
|
||||
use crate::context::Context;
|
||||
use crate::headerdef::{HeaderDef, HeaderDefMap};
|
||||
use crate::key::{DcKey, Fingerprint, SignedPublicKey, SignedSecretKey};
|
||||
use crate::peerstate::Peerstate;
|
||||
use crate::pgp;
|
||||
|
||||
/// Tries to decrypt a message, but only if it is structured as an Autocrypt message.
|
||||
///
|
||||
/// If successful and the message is encrypted, returns decrypted body and a set of valid
|
||||
/// signature fingerprints.
|
||||
///
|
||||
/// If the message is wrongly signed, HashSet will be empty.
|
||||
/// If successful and the message is encrypted, returns decrypted body.
|
||||
pub fn try_decrypt(
|
||||
mail: &ParsedMail<'_>,
|
||||
private_keyring: &[SignedSecretKey],
|
||||
public_keyring_for_validate: &[SignedPublicKey],
|
||||
) -> Result<Option<(Vec<u8>, HashSet<Fingerprint>)>> {
|
||||
) -> Result<Option<::pgp::composed::Message>> {
|
||||
let Some(encrypted_data_part) = get_encrypted_mime(mail) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
decrypt_part(
|
||||
encrypted_data_part,
|
||||
private_keyring,
|
||||
public_keyring_for_validate,
|
||||
)
|
||||
}
|
||||
let data = encrypted_data_part.get_body_raw()?;
|
||||
let msg = pgp::pk_decrypt(data, private_keyring)?;
|
||||
|
||||
pub(crate) async fn prepare_decryption(
|
||||
context: &Context,
|
||||
mail: &ParsedMail<'_>,
|
||||
from: &str,
|
||||
message_time: i64,
|
||||
) -> Result<DecryptionInfo> {
|
||||
if mail.headers.get_header(HeaderDef::ListPost).is_some() {
|
||||
if mail.headers.get_header(HeaderDef::Autocrypt).is_some() {
|
||||
info!(
|
||||
context,
|
||||
"Ignoring autocrypt header since this is a mailing list message. \
|
||||
NOTE: For privacy reasons, the mailing list software should remove Autocrypt headers."
|
||||
);
|
||||
}
|
||||
return Ok(DecryptionInfo {
|
||||
from: from.to_string(),
|
||||
autocrypt_header: None,
|
||||
peerstate: None,
|
||||
message_time,
|
||||
dkim_results: DkimResults { dkim_passed: false },
|
||||
});
|
||||
}
|
||||
|
||||
let autocrypt_header = if context.is_self_addr(from).await? {
|
||||
None
|
||||
} else if let Some(aheader_value) = mail.headers.get_header_value(HeaderDef::Autocrypt) {
|
||||
match Aheader::from_str(&aheader_value) {
|
||||
Ok(header) if addr_cmp(&header.addr, from) => Some(header),
|
||||
Ok(header) => {
|
||||
warn!(
|
||||
context,
|
||||
"Autocrypt header address {:?} is not {:?}.", header.addr, from
|
||||
);
|
||||
None
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, "Failed to parse Autocrypt header: {:#}.", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let dkim_results = handle_authres(context, mail, from).await?;
|
||||
let allow_aeap = get_encrypted_mime(mail).is_some();
|
||||
let peerstate = get_autocrypt_peerstate(
|
||||
context,
|
||||
from,
|
||||
autocrypt_header.as_ref(),
|
||||
message_time,
|
||||
allow_aeap,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(DecryptionInfo {
|
||||
from: from.to_string(),
|
||||
autocrypt_header,
|
||||
peerstate,
|
||||
message_time,
|
||||
dkim_results,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DecryptionInfo {
|
||||
/// The From address. This is the address from the unnencrypted, outer
|
||||
/// From header.
|
||||
pub from: String,
|
||||
pub autocrypt_header: Option<Aheader>,
|
||||
/// The peerstate that will be used to validate the signatures
|
||||
pub peerstate: Option<Peerstate>,
|
||||
/// The timestamp when the message was sent.
|
||||
/// If this is older than the peerstate's last_seen, this probably
|
||||
/// means out-of-order message arrival, We don't modify the
|
||||
/// peerstate in this case.
|
||||
pub message_time: i64,
|
||||
pub(crate) dkim_results: authres::DkimResults,
|
||||
Ok(Some(msg))
|
||||
}
|
||||
|
||||
/// Returns a reference to the encrypted payload of a message.
|
||||
fn get_encrypted_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail<'b>> {
|
||||
pub(crate) fn get_encrypted_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail<'b>> {
|
||||
get_autocrypt_mime(mail)
|
||||
.or_else(|| get_mixed_up_mime(mail))
|
||||
.or_else(|| get_attachment_mime(mail))
|
||||
@@ -204,37 +115,6 @@ fn get_autocrypt_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns Ok(None) if nothing encrypted was found.
|
||||
fn decrypt_part(
|
||||
mail: &ParsedMail<'_>,
|
||||
private_keyring: &[SignedSecretKey],
|
||||
public_keyring_for_validate: &[SignedPublicKey],
|
||||
) -> Result<Option<(Vec<u8>, HashSet<Fingerprint>)>> {
|
||||
let data = mail.get_body_raw()?;
|
||||
|
||||
if has_decrypted_pgp_armor(&data) {
|
||||
let (plain, ret_valid_signatures) =
|
||||
pgp::pk_decrypt(data, private_keyring, public_keyring_for_validate)?;
|
||||
return Ok(Some((plain, ret_valid_signatures)));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
fn has_decrypted_pgp_armor(input: &[u8]) -> bool {
|
||||
if let Some(index) = input.iter().position(|b| *b > b' ') {
|
||||
if input.len() - index > 26 {
|
||||
let start = index;
|
||||
let end = start + 27;
|
||||
|
||||
return &input[start..end] == b"-----BEGIN PGP MESSAGE-----";
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Validates signatures of Multipart/Signed message part, as defined in RFC 1847.
|
||||
///
|
||||
/// Returns the signed part and the set of key
|
||||
@@ -346,24 +226,6 @@ mod tests {
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
#[test]
|
||||
fn test_has_decrypted_pgp_armor() {
|
||||
let data = b" -----BEGIN PGP MESSAGE-----";
|
||||
assert_eq!(has_decrypted_pgp_armor(data), true);
|
||||
|
||||
let data = b" \n-----BEGIN PGP MESSAGE-----";
|
||||
assert_eq!(has_decrypted_pgp_armor(data), true);
|
||||
|
||||
let data = b" -----BEGIN PGP MESSAGE---";
|
||||
assert_eq!(has_decrypted_pgp_armor(data), false);
|
||||
|
||||
let data = b" -----BEGIN PGP MESSAGE-----";
|
||||
assert_eq!(has_decrypted_pgp_armor(data), true);
|
||||
|
||||
let data = b"blas";
|
||||
assert_eq!(has_decrypted_pgp_armor(data), false);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_mixed_up_mime() -> Result<()> {
|
||||
// "Mixed Up" mail as received when sending an encrypted
|
||||
|
||||
@@ -144,12 +144,12 @@ impl HtmlMsgParser {
|
||||
self.plain = Some(PlainText {
|
||||
text: decoded_data,
|
||||
flowed: if let Some(format) = mail.ctype.params.get("format") {
|
||||
format.as_str().to_ascii_lowercase() == "flowed"
|
||||
format.as_str().eq_ignore_ascii_case("flowed")
|
||||
} else {
|
||||
false
|
||||
},
|
||||
delsp: if let Some(delsp) = mail.ctype.params.get("delsp") {
|
||||
delsp.as_str().to_ascii_lowercase() == "yes"
|
||||
delsp.as_str().eq_ignore_ascii_case("yes")
|
||||
} else {
|
||||
false
|
||||
},
|
||||
@@ -283,7 +283,6 @@ mod tests {
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
</head><body>
|
||||
This message does not have Content-Type nor Subject.<br/>
|
||||
<br/>
|
||||
</body></html>
|
||||
"#
|
||||
);
|
||||
@@ -302,7 +301,6 @@ This message does not have Content-Type nor Subject.<br/>
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
</head><body>
|
||||
message with a non-UTF-8 encoding: äöüßÄÖÜ<br/>
|
||||
<br/>
|
||||
</body></html>
|
||||
"#
|
||||
);
|
||||
@@ -325,7 +323,6 @@ This line ends with a space and will be merged with the next one due to format=f
|
||||
<br/>
|
||||
This line does not end with a space<br/>
|
||||
and will be wrapped as usual.<br/>
|
||||
<br/>
|
||||
</body></html>
|
||||
"#
|
||||
);
|
||||
@@ -347,7 +344,6 @@ mime-modified should not be set set as there is no html and no special stuff;<br
|
||||
although not being a delta-message.<br/>
|
||||
test some special html-characters as < > and & but also " and ' :)<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
</body></html>
|
||||
"#
|
||||
);
|
||||
|
||||
53
src/imap.rs
53
src/imap.rs
@@ -89,7 +89,7 @@ pub(crate) struct Imap {
|
||||
|
||||
oauth2: bool,
|
||||
|
||||
login_failed_once: bool,
|
||||
authentication_failed_once: bool,
|
||||
|
||||
pub(crate) connectivity: ConnectivityStore,
|
||||
|
||||
@@ -254,7 +254,7 @@ impl Imap {
|
||||
proxy_config,
|
||||
strict_tls,
|
||||
oauth2,
|
||||
login_failed_once: false,
|
||||
authentication_failed_once: false,
|
||||
connectivity: Default::default(),
|
||||
conn_last_try: UNIX_EPOCH,
|
||||
conn_backoff_ms: 0,
|
||||
@@ -402,7 +402,7 @@ impl Imap {
|
||||
let mut lock = context.server_id.write().await;
|
||||
lock.clone_from(&session.capabilities.server_id);
|
||||
|
||||
self.login_failed_once = false;
|
||||
self.authentication_failed_once = false;
|
||||
context.emit_event(EventType::ImapConnected(format!(
|
||||
"IMAP-LOGIN as {}",
|
||||
lp.user
|
||||
@@ -416,35 +416,38 @@ impl Imap {
|
||||
let imap_user = lp.user.to_owned();
|
||||
let message = stock_str::cannot_login(context, &imap_user).await;
|
||||
|
||||
let err_str = err.to_string();
|
||||
warn!(context, "IMAP failed to login: {err:#}.");
|
||||
first_error.get_or_insert(format_err!("{message} ({err:#})"));
|
||||
|
||||
// If it looks like the password is wrong, send a notification:
|
||||
let _lock = context.wrong_pw_warning_mutex.lock().await;
|
||||
if !configuring
|
||||
&& self.login_failed_once
|
||||
&& err_str.to_lowercase().contains("authentication")
|
||||
&& context.get_config_bool(Config::NotifyAboutWrongPw).await?
|
||||
{
|
||||
let mut msg = Message::new_text(message);
|
||||
if let Err(e) = chat::add_device_msg_with_importance(
|
||||
context,
|
||||
None,
|
||||
Some(&mut msg),
|
||||
true,
|
||||
)
|
||||
.await
|
||||
if err.to_string().to_lowercase().contains("authentication") {
|
||||
if self.authentication_failed_once
|
||||
&& !configuring
|
||||
&& context.get_config_bool(Config::NotifyAboutWrongPw).await?
|
||||
{
|
||||
warn!(context, "Failed to add device message: {e:#}.");
|
||||
let mut msg = Message::new_text(message);
|
||||
if let Err(e) = chat::add_device_msg_with_importance(
|
||||
context,
|
||||
None,
|
||||
Some(&mut msg),
|
||||
true,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!(context, "Failed to add device message: {e:#}.");
|
||||
} else {
|
||||
context
|
||||
.set_config_internal(Config::NotifyAboutWrongPw, None)
|
||||
.await
|
||||
.log_err(context)
|
||||
.ok();
|
||||
}
|
||||
} else {
|
||||
context
|
||||
.set_config_internal(Config::NotifyAboutWrongPw, None)
|
||||
.await
|
||||
.log_err(context)
|
||||
.ok();
|
||||
self.authentication_failed_once = true;
|
||||
}
|
||||
} else {
|
||||
self.login_failed_once = true;
|
||||
self.authentication_failed_once = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1913,7 +1916,7 @@ async fn needs_move_to_mvbox(
|
||||
&& has_chat_version
|
||||
&& headers
|
||||
.get_header_value(HeaderDef::AutoSubmitted)
|
||||
.filter(|val| val.to_ascii_lowercase() == "auto-generated")
|
||||
.filter(|val| val.eq_ignore_ascii_case("auto-generated"))
|
||||
.is_some()
|
||||
{
|
||||
if let Some(from) = mimeparser::get_from(headers) {
|
||||
|
||||
@@ -150,16 +150,17 @@ impl MsgId {
|
||||
|
||||
pub(crate) async fn set_delivered(self, context: &Context) -> Result<()> {
|
||||
update_msg_state(context, self, MessageState::OutDelivered).await?;
|
||||
let chat_id: ChatId = context
|
||||
let chat_id: Option<ChatId> = context
|
||||
.sql
|
||||
.query_get_value("SELECT chat_id FROM msgs WHERE id=?", (self,))
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
.await?;
|
||||
context.emit_event(EventType::MsgDelivered {
|
||||
chat_id,
|
||||
chat_id: chat_id.unwrap_or_default(),
|
||||
msg_id: self,
|
||||
});
|
||||
chatlist_events::emit_chatlist_item_changed(context, chat_id);
|
||||
if let Some(chat_id) = chat_id {
|
||||
chatlist_events::emit_chatlist_item_changed(context, chat_id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1850,20 +1851,21 @@ pub(crate) async fn set_msg_failed(
|
||||
}
|
||||
msg.error = Some(error.to_string());
|
||||
|
||||
context
|
||||
let exists = context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE msgs SET state=?, error=? WHERE id=?;",
|
||||
(msg.state, error, msg.id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
.await?
|
||||
> 0;
|
||||
context.emit_event(EventType::MsgFailed {
|
||||
chat_id: msg.chat_id,
|
||||
msg_id: msg.id,
|
||||
});
|
||||
chatlist_events::emit_chatlist_item_changed(context, msg.chat_id);
|
||||
|
||||
if exists {
|
||||
chatlist_events::emit_chatlist_item_changed(context, msg.chat_id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -743,7 +743,9 @@ impl MimeFactory {
|
||||
hidden_headers.push(header);
|
||||
} else if header_name == "chat-user-avatar" {
|
||||
hidden_headers.push(header);
|
||||
} else if header_name == "autocrypt" {
|
||||
} else if header_name == "autocrypt"
|
||||
&& !context.get_config_bool(Config::ProtectAutocrypt).await?
|
||||
{
|
||||
unprotected_headers.push(header.clone());
|
||||
} else if header_name == "from" {
|
||||
// Unencrypted securejoin messages should _not_ include the display name:
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::cmp::min;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{bail, Context as _, Result};
|
||||
use deltachat_contact_tools::{addr_cmp, addr_normalize, sanitize_bidi_characters};
|
||||
@@ -14,6 +15,7 @@ use mailparse::{addrparse_header, DispositionType, MailHeader, MailHeaderMap, Si
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
|
||||
use crate::aheader::{Aheader, EncryptPreference};
|
||||
use crate::authres::handle_authres;
|
||||
use crate::blob::BlobObject;
|
||||
use crate::chat::{add_info_msg, ChatId};
|
||||
use crate::config::Config;
|
||||
@@ -21,8 +23,8 @@ use crate::constants::{self, Chattype};
|
||||
use crate::contact::{Contact, ContactId, Origin};
|
||||
use crate::context::Context;
|
||||
use crate::decrypt::{
|
||||
keyring_from_peerstate, prepare_decryption, try_decrypt, validate_detached_signature,
|
||||
DecryptionInfo,
|
||||
get_autocrypt_peerstate, get_encrypted_mime, keyring_from_peerstate, try_decrypt,
|
||||
validate_detached_signature,
|
||||
};
|
||||
use crate::dehtml::dehtml;
|
||||
use crate::events::EventType;
|
||||
@@ -71,7 +73,8 @@ pub(crate) struct MimeMessage {
|
||||
/// messages to this address to post them to the list.
|
||||
pub list_post: Option<String>,
|
||||
pub chat_disposition_notification_to: Option<SingleInfo>,
|
||||
pub decryption_info: DecryptionInfo,
|
||||
pub autocrypt_header: Option<Aheader>,
|
||||
pub peerstate: Option<Peerstate>,
|
||||
pub decrypting_failed: bool,
|
||||
|
||||
/// Set of valid signature fingerprints if a message is an
|
||||
@@ -301,42 +304,101 @@ impl MimeMessage {
|
||||
let mut from = from.context("No from in message")?;
|
||||
let private_keyring = load_self_secret_keyring(context).await?;
|
||||
|
||||
let mut decryption_info =
|
||||
prepare_decryption(context, &mail, &from.addr, timestamp_sent).await?;
|
||||
let allow_aeap = get_encrypted_mime(&mail).is_some();
|
||||
|
||||
let dkim_results = handle_authres(context, &mail, &from.addr).await?;
|
||||
|
||||
// Memory location for a possible decrypted message.
|
||||
let mut mail_raw = Vec::new();
|
||||
let mut gossiped_keys = Default::default();
|
||||
let mut from_is_signed = false;
|
||||
hop_info += "\n\n";
|
||||
hop_info += &decryption_info.dkim_results.to_string();
|
||||
hop_info += &dkim_results.to_string();
|
||||
|
||||
let incoming = !context.is_self_addr(&from.addr).await?;
|
||||
let public_keyring = match decryption_info.peerstate.is_none() && !incoming {
|
||||
true => key::load_self_public_keyring(context).await?,
|
||||
false => keyring_from_peerstate(decryption_info.peerstate.as_ref()),
|
||||
};
|
||||
let (mail, mut signatures, encrypted) = match tokio::task::block_in_place(|| {
|
||||
try_decrypt(&mail, &private_keyring, &public_keyring)
|
||||
}) {
|
||||
Ok(Some((raw, signatures))) => {
|
||||
mail_raw = raw;
|
||||
let decrypted_mail = mailparse::parse_mail(&mail_raw)?;
|
||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||
info!(
|
||||
context,
|
||||
"decrypted message mime-body:\n{}",
|
||||
String::from_utf8_lossy(&mail_raw),
|
||||
);
|
||||
|
||||
let mut aheader_value: Option<String> = mail.headers.get_header_value(HeaderDef::Autocrypt);
|
||||
|
||||
let mail_raw; // Memory location for a possible decrypted message.
|
||||
let decrypted_msg; // Decrypted signed OpenPGP message.
|
||||
|
||||
let (mail, encrypted) =
|
||||
match tokio::task::block_in_place(|| try_decrypt(&mail, &private_keyring)) {
|
||||
Ok(Some(msg)) => {
|
||||
mail_raw = msg.get_content()?.unwrap_or_default();
|
||||
|
||||
let decrypted_mail = mailparse::parse_mail(&mail_raw)?;
|
||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||
info!(
|
||||
context,
|
||||
"decrypted message mime-body:\n{}",
|
||||
String::from_utf8_lossy(&mail_raw),
|
||||
);
|
||||
}
|
||||
|
||||
decrypted_msg = Some(msg);
|
||||
if let Some(protected_aheader_value) = decrypted_mail
|
||||
.headers
|
||||
.get_header_value(HeaderDef::Autocrypt)
|
||||
{
|
||||
aheader_value = Some(protected_aheader_value);
|
||||
}
|
||||
|
||||
(Ok(decrypted_mail), true)
|
||||
}
|
||||
Ok(None) => {
|
||||
mail_raw = Vec::new();
|
||||
decrypted_msg = None;
|
||||
(Ok(mail), false)
|
||||
}
|
||||
Err(err) => {
|
||||
mail_raw = Vec::new();
|
||||
decrypted_msg = None;
|
||||
warn!(context, "decryption failed: {:#}", err);
|
||||
(Err(err), false)
|
||||
}
|
||||
};
|
||||
|
||||
let autocrypt_header = if !incoming {
|
||||
None
|
||||
} else if let Some(aheader_value) = aheader_value {
|
||||
match Aheader::from_str(&aheader_value) {
|
||||
Ok(header) if addr_cmp(&header.addr, &from.addr) => Some(header),
|
||||
Ok(header) => {
|
||||
warn!(
|
||||
context,
|
||||
"Autocrypt header address {:?} is not {:?}.", header.addr, from.addr
|
||||
);
|
||||
None
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, "Failed to parse Autocrypt header: {:#}.", err);
|
||||
None
|
||||
}
|
||||
(Ok(decrypted_mail), signatures, true)
|
||||
}
|
||||
Ok(None) => (Ok(mail), HashSet::new(), false),
|
||||
Err(err) => {
|
||||
warn!(context, "decryption failed: {:#}", err);
|
||||
(Err(err), HashSet::new(), false)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// The peerstate that will be used to validate the signatures.
|
||||
let mut peerstate = get_autocrypt_peerstate(
|
||||
context,
|
||||
&from.addr,
|
||||
autocrypt_header.as_ref(),
|
||||
timestamp_sent,
|
||||
allow_aeap,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let public_keyring = match peerstate.is_none() && !incoming {
|
||||
true => key::load_self_public_keyring(context).await?,
|
||||
false => keyring_from_peerstate(peerstate.as_ref()),
|
||||
};
|
||||
|
||||
let mut signatures = if let Some(ref decrypted_msg) = decrypted_msg {
|
||||
crate::pgp::valid_signature_fingerprints(decrypted_msg, &public_keyring)?
|
||||
} else {
|
||||
HashSet::new()
|
||||
};
|
||||
|
||||
let mail = mail.as_ref().map(|mail| {
|
||||
let (content, signatures_detached) = validate_detached_signature(mail, &public_keyring)
|
||||
.unwrap_or((mail, Default::default()));
|
||||
@@ -422,7 +484,7 @@ impl MimeMessage {
|
||||
Self::remove_secured_headers(&mut headers);
|
||||
|
||||
// If it is not a read receipt, degrade encryption.
|
||||
if let (Some(peerstate), Ok(mail)) = (&mut decryption_info.peerstate, mail) {
|
||||
if let (Some(peerstate), Ok(mail)) = (&mut peerstate, mail) {
|
||||
if timestamp_sent > peerstate.last_seen_autocrypt
|
||||
&& mail.ctype.mimetype != "multipart/report"
|
||||
{
|
||||
@@ -433,7 +495,7 @@ impl MimeMessage {
|
||||
if !encrypted {
|
||||
signatures.clear();
|
||||
}
|
||||
if let Some(peerstate) = &mut decryption_info.peerstate {
|
||||
if let Some(peerstate) = &mut peerstate {
|
||||
if peerstate.prefer_encrypt != EncryptPreference::Mutual && !signatures.is_empty() {
|
||||
peerstate.prefer_encrypt = EncryptPreference::Mutual;
|
||||
peerstate.save_to_db(&context.sql).await?;
|
||||
@@ -449,7 +511,8 @@ impl MimeMessage {
|
||||
from_is_signed,
|
||||
incoming,
|
||||
chat_disposition_notification_to,
|
||||
decryption_info,
|
||||
autocrypt_header,
|
||||
peerstate,
|
||||
decrypting_failed: mail.is_err(),
|
||||
|
||||
// only non-empty if it was a valid autocrypt message
|
||||
@@ -770,15 +833,6 @@ impl MimeMessage {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set different sender name for a message.
|
||||
/// This overrides the name set by the `set_config()`-option `displayname`.
|
||||
pub fn set_override_sender_name(&mut self, name: Option<String>) {
|
||||
self.parts.iter_mut().for_each(|part| {
|
||||
part.param
|
||||
.set_optional(Param::OverrideSenderDisplayname, name.clone());
|
||||
});
|
||||
}
|
||||
|
||||
async fn avatar_action_from_header(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
@@ -1167,7 +1221,7 @@ impl MimeMessage {
|
||||
|
||||
let is_format_flowed = if let Some(format) = mail.ctype.params.get("format")
|
||||
{
|
||||
format.as_str().to_ascii_lowercase() == "flowed"
|
||||
format.as_str().eq_ignore_ascii_case("flowed")
|
||||
} else {
|
||||
false
|
||||
};
|
||||
@@ -1177,7 +1231,7 @@ impl MimeMessage {
|
||||
&& is_format_flowed
|
||||
{
|
||||
let delsp = if let Some(delsp) = mail.ctype.params.get("delsp") {
|
||||
delsp.as_str().to_ascii_lowercase() == "yes"
|
||||
delsp.as_str().eq_ignore_ascii_case("yes")
|
||||
} else {
|
||||
false
|
||||
};
|
||||
@@ -1240,7 +1294,7 @@ impl MimeMessage {
|
||||
if decoded_data.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(peerstate) = &mut self.decryption_info.peerstate {
|
||||
if let Some(peerstate) = &mut self.peerstate {
|
||||
if peerstate.prefer_encrypt != EncryptPreference::Mutual
|
||||
&& mime_type.type_() == mime::APPLICATION
|
||||
&& mime_type.subtype().as_str() == "pgp-keys"
|
||||
@@ -3654,6 +3708,28 @@ On 2020-10-25, Bob wrote:
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests that sender status (signature) does not appear
|
||||
/// in HTML view of a long message.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_large_message_no_signature() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
alice
|
||||
.set_config(Config::Selfstatus, Some("Some signature"))
|
||||
.await?;
|
||||
let chat = alice.create_chat(bob).await;
|
||||
let txt = "Hello!\n".repeat(500);
|
||||
let sent = alice.send_text(chat.id, &txt).await;
|
||||
let msg = bob.recv_msg(&sent).await;
|
||||
|
||||
assert_eq!(msg.has_html(), true);
|
||||
let html = msg.id.get_html(bob).await?.unwrap();
|
||||
assert_eq!(html.contains("Some signature"), false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_x_microsoft_original_message_id() {
|
||||
let t = TestContext::new_alice().await;
|
||||
@@ -4021,12 +4097,8 @@ Content-Disposition: reaction\n\
|
||||
|
||||
// We do allow the time to be in the future a bit (because of unsynchronized clocks),
|
||||
// but only 60 seconds:
|
||||
assert!(mime_message.decryption_info.message_time <= time() + 60);
|
||||
assert!(mime_message.decryption_info.message_time >= beginning_time + 60);
|
||||
assert_eq!(
|
||||
mime_message.decryption_info.message_time,
|
||||
mime_message.timestamp_sent
|
||||
);
|
||||
assert!(mime_message.timestamp_sent <= time() + 60);
|
||||
assert!(mime_message.timestamp_sent >= beginning_time + 60);
|
||||
assert!(mime_message.timestamp_rcvd <= time());
|
||||
|
||||
Ok(())
|
||||
@@ -4097,4 +4169,24 @@ Content-Type: text/plain; charset=utf-8
|
||||
"alice@example.org"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_protect_autocrypt() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let alice = &tcm.alice().await;
|
||||
let bob = &tcm.bob().await;
|
||||
|
||||
alice
|
||||
.set_config_bool(Config::ProtectAutocrypt, true)
|
||||
.await?;
|
||||
bob.set_config_bool(Config::ProtectAutocrypt, true).await?;
|
||||
|
||||
let msg = tcm.send_recv_accept(alice, bob, "Hello!").await;
|
||||
assert_eq!(msg.get_showpadlock(), false);
|
||||
|
||||
let msg = tcm.send_recv(bob, alice, "Hi!").await;
|
||||
assert_eq!(msg.get_showpadlock(), true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -766,8 +766,7 @@ pub(crate) async fn maybe_do_aeap_transition(
|
||||
context: &Context,
|
||||
mime_parser: &mut crate::mimeparser::MimeMessage,
|
||||
) -> Result<()> {
|
||||
let info = &mime_parser.decryption_info;
|
||||
let Some(peerstate) = &info.peerstate else {
|
||||
let Some(peerstate) = &mime_parser.peerstate else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@@ -815,13 +814,13 @@ pub(crate) async fn maybe_do_aeap_transition(
|
||||
|
||||
// DC avoids sending messages with the same timestamp, that's why messages
|
||||
// with equal timestamps are ignored here unlike in `Peerstate::apply_header()`.
|
||||
if info.message_time <= peerstate.last_seen {
|
||||
if mime_parser.timestamp_sent <= peerstate.last_seen {
|
||||
info!(
|
||||
context,
|
||||
"Not doing AEAP from {} to {} because {} < {}.",
|
||||
&peerstate.addr,
|
||||
&mime_parser.from.addr,
|
||||
info.message_time,
|
||||
mime_parser.timestamp_sent,
|
||||
peerstate.last_seen
|
||||
);
|
||||
return Ok(());
|
||||
@@ -832,24 +831,23 @@ pub(crate) async fn maybe_do_aeap_transition(
|
||||
"Doing AEAP transition from {} to {}.", &peerstate.addr, &mime_parser.from.addr
|
||||
);
|
||||
|
||||
let info = &mut mime_parser.decryption_info;
|
||||
let peerstate = info.peerstate.as_mut().context("no peerstate??")?;
|
||||
let peerstate = mime_parser.peerstate.as_mut().context("no peerstate??")?;
|
||||
// Add info messages to chats with this (verified) contact
|
||||
//
|
||||
peerstate
|
||||
.handle_setup_change(
|
||||
context,
|
||||
info.message_time,
|
||||
PeerstateChange::Aeap(info.from.clone()),
|
||||
mime_parser.timestamp_sent,
|
||||
PeerstateChange::Aeap(mime_parser.from.addr.clone()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let old_addr = mem::take(&mut peerstate.addr);
|
||||
peerstate.addr.clone_from(&info.from);
|
||||
let header = info.autocrypt_header.as_ref().context(
|
||||
peerstate.addr.clone_from(&mime_parser.from.addr);
|
||||
let header = mime_parser.autocrypt_header.as_ref().context(
|
||||
"Internal error: Tried to do an AEAP transition without an autocrypt header??",
|
||||
)?;
|
||||
peerstate.apply_header(context, header, info.message_time);
|
||||
peerstate.apply_header(context, header, mime_parser.timestamp_sent);
|
||||
|
||||
peerstate
|
||||
.save_to_db_ex(&context.sql, Some(&old_addr))
|
||||
|
||||
67
src/pgp.rs
67
src/pgp.rs
@@ -297,34 +297,34 @@ pub fn pk_calc_signature(
|
||||
///
|
||||
/// Receiver private keys are provided in
|
||||
/// `private_keys_for_decryption`.
|
||||
///
|
||||
/// Returns decrypted message and fingerprints
|
||||
/// of all keys from the `public_keys_for_validation` keyring that
|
||||
/// have valid signatures there.
|
||||
#[allow(clippy::implicit_hasher)]
|
||||
pub fn pk_decrypt(
|
||||
ctext: Vec<u8>,
|
||||
private_keys_for_decryption: &[SignedSecretKey],
|
||||
public_keys_for_validation: &[SignedPublicKey],
|
||||
) -> Result<(Vec<u8>, HashSet<Fingerprint>)> {
|
||||
let mut ret_signature_fingerprints: HashSet<Fingerprint> = Default::default();
|
||||
|
||||
) -> Result<pgp::composed::Message> {
|
||||
let cursor = Cursor::new(ctext);
|
||||
let (msg, _) = Message::from_armor_single(cursor)?;
|
||||
let (msg, _headers) = Message::from_armor_single(cursor)?;
|
||||
|
||||
let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption.iter().collect();
|
||||
|
||||
let (msg, _) = msg.decrypt(|| "".into(), &skeys[..])?;
|
||||
let (msg, _key_ids) = msg.decrypt(|| "".into(), &skeys[..])?;
|
||||
|
||||
// get_content() will decompress the message if needed,
|
||||
// but this avoids decompressing it again to check signatures
|
||||
let msg = msg.decompress()?;
|
||||
|
||||
let content = match msg.get_content()? {
|
||||
Some(content) => content,
|
||||
None => bail!("The decrypted message is empty"),
|
||||
};
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Returns fingerprints
|
||||
/// of all keys from the `public_keys_for_validation` keyring that
|
||||
/// have valid signatures there.
|
||||
///
|
||||
/// If the message is wrongly signed, HashSet will be empty.
|
||||
pub fn valid_signature_fingerprints(
|
||||
msg: &pgp::composed::Message,
|
||||
public_keys_for_validation: &[SignedPublicKey],
|
||||
) -> Result<HashSet<Fingerprint>> {
|
||||
let mut ret_signature_fingerprints: HashSet<Fingerprint> = Default::default();
|
||||
if let signed_msg @ pgp::composed::Message::Signed { .. } = msg {
|
||||
for pkey in public_keys_for_validation {
|
||||
if signed_msg.verify(&pkey.primary_key).is_ok() {
|
||||
@@ -333,7 +333,7 @@ pub fn pk_decrypt(
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((content, ret_signature_fingerprints))
|
||||
Ok(ret_signature_fingerprints)
|
||||
}
|
||||
|
||||
/// Validates detached signature.
|
||||
@@ -407,6 +407,18 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::{alice_keypair, bob_keypair};
|
||||
|
||||
fn pk_decrypt_and_validate(
|
||||
ctext: Vec<u8>,
|
||||
private_keys_for_decryption: &[SignedSecretKey],
|
||||
public_keys_for_validation: &[SignedPublicKey],
|
||||
) -> Result<(pgp::composed::Message, HashSet<Fingerprint>)> {
|
||||
let msg = pk_decrypt(ctext, private_keys_for_decryption)?;
|
||||
let ret_signature_fingerprints =
|
||||
valid_signature_fingerprints(&msg, public_keys_for_validation)?;
|
||||
|
||||
Ok((msg, ret_signature_fingerprints))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_armored_data_1() {
|
||||
let (typ, _headers, base64) = split_armored_data(
|
||||
@@ -534,34 +546,35 @@ mod tests {
|
||||
// Check decrypting as Alice
|
||||
let decrypt_keyring = vec![KEYS.alice_secret.clone()];
|
||||
let sig_check_keyring = vec![KEYS.alice_public.clone()];
|
||||
let (plain, valid_signatures) = pk_decrypt(
|
||||
let (msg, valid_signatures) = pk_decrypt_and_validate(
|
||||
ctext_signed().await.as_bytes().to_vec(),
|
||||
&decrypt_keyring,
|
||||
&sig_check_keyring,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, CLEARTEXT);
|
||||
assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT);
|
||||
assert_eq!(valid_signatures.len(), 1);
|
||||
|
||||
// Check decrypting as Bob
|
||||
let decrypt_keyring = vec![KEYS.bob_secret.clone()];
|
||||
let sig_check_keyring = vec![KEYS.alice_public.clone()];
|
||||
let (plain, valid_signatures) = pk_decrypt(
|
||||
let (msg, valid_signatures) = pk_decrypt_and_validate(
|
||||
ctext_signed().await.as_bytes().to_vec(),
|
||||
&decrypt_keyring,
|
||||
&sig_check_keyring,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, CLEARTEXT);
|
||||
assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT);
|
||||
assert_eq!(valid_signatures.len(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decrypt_no_sig_check() {
|
||||
let keyring = vec![KEYS.alice_secret.clone()];
|
||||
let (plain, valid_signatures) =
|
||||
pk_decrypt(ctext_signed().await.as_bytes().to_vec(), &keyring, &[]).unwrap();
|
||||
assert_eq!(plain, CLEARTEXT);
|
||||
let (msg, valid_signatures) =
|
||||
pk_decrypt_and_validate(ctext_signed().await.as_bytes().to_vec(), &keyring, &[])
|
||||
.unwrap();
|
||||
assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT);
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
}
|
||||
|
||||
@@ -570,26 +583,26 @@ mod tests {
|
||||
// The validation does not have the public key of the signer.
|
||||
let decrypt_keyring = vec![KEYS.bob_secret.clone()];
|
||||
let sig_check_keyring = vec![KEYS.bob_public.clone()];
|
||||
let (plain, valid_signatures) = pk_decrypt(
|
||||
let (msg, valid_signatures) = pk_decrypt_and_validate(
|
||||
ctext_signed().await.as_bytes().to_vec(),
|
||||
&decrypt_keyring,
|
||||
&sig_check_keyring,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, CLEARTEXT);
|
||||
assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT);
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_decrypt_unsigned() {
|
||||
let decrypt_keyring = vec![KEYS.bob_secret.clone()];
|
||||
let (plain, valid_signatures) = pk_decrypt(
|
||||
let (msg, valid_signatures) = pk_decrypt_and_validate(
|
||||
ctext_unsigned().await.as_bytes().to_vec(),
|
||||
&decrypt_keyring,
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, CLEARTEXT);
|
||||
assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT);
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::simplify::split_lines;
|
||||
use crate::simplify::remove_message_footer;
|
||||
|
||||
/// Plaintext message body together with format=flowed attributes.
|
||||
#[derive(Debug)]
|
||||
@@ -32,7 +32,8 @@ impl PlainText {
|
||||
regex::Regex::new(r"\b((http|https|ftp|ftps):[\w.,:;$/@!?&%\-~=#+]+)").unwrap()
|
||||
});
|
||||
|
||||
let lines = split_lines(&self.text);
|
||||
let lines: Vec<&str> = self.text.lines().collect();
|
||||
let (lines, _footer) = remove_message_footer(&lines);
|
||||
|
||||
let mut ret = r#"<!DOCTYPE html>
|
||||
<html><head>
|
||||
@@ -136,7 +137,28 @@ line 1<br/>
|
||||
line 2<br/>
|
||||
line with <a href="https://link-mid-of-line.org">https://link-mid-of-line.org</a> and <a href="http://link-end-of-line.com/file?foo=bar%20">http://link-end-of-line.com/file?foo=bar%20</a><br/>
|
||||
<a href="http://link-at-start-of-line.org">http://link-at-start-of-line.org</a><br/>
|
||||
<br/>
|
||||
</body></html>
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_plain_remove_signature() {
|
||||
let html = PlainText {
|
||||
text: "Foo\nbar\n-- \nSignature here".to_string(),
|
||||
flowed: false,
|
||||
delsp: false,
|
||||
}
|
||||
.to_html();
|
||||
assert_eq!(
|
||||
html,
|
||||
r#"<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
</head><body>
|
||||
Foo<br/>
|
||||
bar<br/>
|
||||
</body></html>
|
||||
"#
|
||||
);
|
||||
|
||||
@@ -201,7 +201,7 @@ pub(crate) async fn receive_imf_inner(
|
||||
};
|
||||
|
||||
crate::peerstate::maybe_do_aeap_transition(context, &mut mime_parser).await?;
|
||||
if let Some(peerstate) = &mime_parser.decryption_info.peerstate {
|
||||
if let Some(peerstate) = &mime_parser.peerstate {
|
||||
peerstate
|
||||
.handle_fingerprint_change(context, mime_parser.timestamp_sent)
|
||||
.await?;
|
||||
@@ -356,8 +356,7 @@ pub(crate) async fn receive_imf_inner(
|
||||
|
||||
// Peerstate could be updated by handling the Securejoin handshake.
|
||||
let contact = Contact::get_by_id(context, from_id).await?;
|
||||
mime_parser.decryption_info.peerstate =
|
||||
Peerstate::from_addr(context, contact.get_addr()).await?;
|
||||
mime_parser.peerstate = Peerstate::from_addr(context, contact.get_addr()).await?;
|
||||
} else {
|
||||
let to_id = to_ids.first().copied().unwrap_or_default();
|
||||
// handshake may mark contacts as verified and must be processed before chats are created
|
||||
@@ -393,7 +392,7 @@ pub(crate) async fn receive_imf_inner(
|
||||
if verified_encryption == VerifiedEncryption::Verified
|
||||
&& mime_parser.get_header(HeaderDef::ChatVerified).is_some()
|
||||
{
|
||||
if let Some(peerstate) = &mut mime_parser.decryption_info.peerstate {
|
||||
if let Some(peerstate) = &mut mime_parser.peerstate {
|
||||
// NOTE: it might be better to remember ID of the key
|
||||
// that we used to decrypt the message, but
|
||||
// it is unlikely that default key ever changes
|
||||
@@ -967,52 +966,55 @@ async fn add_parts(
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the message was sent with verified encryption and set the protection of
|
||||
// the 1:1 chat accordingly.
|
||||
let chat = match is_partial_download.is_none()
|
||||
&& mime_parser.get_header(HeaderDef::SecureJoin).is_none()
|
||||
&& !is_mdn
|
||||
{
|
||||
true => Some(Chat::load_from_db(context, chat_id).await?)
|
||||
.filter(|chat| chat.typ == Chattype::Single),
|
||||
false => None,
|
||||
if let Some(chat_id) = chat_id {
|
||||
let contact = Contact::get_by_id(context, from_id).await?;
|
||||
// Check if the message was sent with verified encryption and set the protection of
|
||||
// the 1:1 chat accordingly.
|
||||
let chat = match is_partial_download.is_none()
|
||||
&& mime_parser.get_header(HeaderDef::SecureJoin).is_none()
|
||||
&& !is_mdn
|
||||
{
|
||||
true => Some(Chat::load_from_db(context, chat_id).await?)
|
||||
.filter(|chat| chat.typ == Chattype::Single),
|
||||
false => None,
|
||||
};
|
||||
if let Some(chat) = chat {
|
||||
debug_assert!(chat.typ == Chattype::Single);
|
||||
let mut new_protection = match verified_encryption {
|
||||
VerifiedEncryption::Verified => ProtectionStatus::Protected,
|
||||
VerifiedEncryption::NotVerified(_) => ProtectionStatus::Unprotected,
|
||||
};
|
||||
if let Some(chat) = chat {
|
||||
debug_assert!(chat.typ == Chattype::Single);
|
||||
let mut new_protection = match verified_encryption {
|
||||
VerifiedEncryption::Verified => ProtectionStatus::Protected,
|
||||
VerifiedEncryption::NotVerified(_) => ProtectionStatus::Unprotected,
|
||||
};
|
||||
|
||||
if chat.protected != ProtectionStatus::Unprotected
|
||||
&& new_protection == ProtectionStatus::Unprotected
|
||||
// `chat.protected` must be maintained regardless of the `Config::VerifiedOneOnOneChats`.
|
||||
// That's why the config is checked here, and not above.
|
||||
&& context.get_config_bool(Config::VerifiedOneOnOneChats).await?
|
||||
{
|
||||
new_protection = ProtectionStatus::ProtectionBroken;
|
||||
}
|
||||
if chat.protected != new_protection {
|
||||
// The message itself will be sorted under the device message since the device
|
||||
// message is `MessageState::InNoticed`, which means that all following
|
||||
// messages are sorted under it.
|
||||
chat_id
|
||||
.set_protection(
|
||||
context,
|
||||
new_protection,
|
||||
mime_parser.timestamp_sent,
|
||||
Some(from_id),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
if let Some(peerstate) = &mime_parser.decryption_info.peerstate {
|
||||
restore_protection = new_protection != ProtectionStatus::Protected
|
||||
&& peerstate.prefer_encrypt == EncryptPreference::Mutual
|
||||
// Check that the contact still has the Autocrypt key same as the
|
||||
// verified key, see also `Peerstate::is_using_verified_key()`.
|
||||
&& contact.is_verified(context).await?;
|
||||
}
|
||||
if chat.protected != ProtectionStatus::Unprotected
|
||||
&& new_protection == ProtectionStatus::Unprotected
|
||||
// `chat.protected` must be maintained regardless of the `Config::VerifiedOneOnOneChats`.
|
||||
// That's why the config is checked here, and not above.
|
||||
&& context.get_config_bool(Config::VerifiedOneOnOneChats).await?
|
||||
{
|
||||
new_protection = ProtectionStatus::ProtectionBroken;
|
||||
}
|
||||
if chat.protected != new_protection {
|
||||
// The message itself will be sorted under the device message since the device
|
||||
// message is `MessageState::InNoticed`, which means that all following
|
||||
// messages are sorted under it.
|
||||
chat_id
|
||||
.set_protection(
|
||||
context,
|
||||
new_protection,
|
||||
mime_parser.timestamp_sent,
|
||||
Some(from_id),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
if let Some(peerstate) = &mime_parser.peerstate {
|
||||
restore_protection = new_protection != ProtectionStatus::Protected
|
||||
&& peerstate.prefer_encrypt == EncryptPreference::Mutual
|
||||
// Check that the contact still has the Autocrypt key same as the
|
||||
// verified key, see also `Peerstate::is_using_verified_key()`.
|
||||
&& contact.is_verified(context).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1806,15 +1808,13 @@ async fn lookup_chat_by_reply(
|
||||
|
||||
// If this was a private message just to self, it was probably a private reply.
|
||||
// It should not go into the group then, but into the private chat.
|
||||
if is_probably_private_reply(context, to_ids, from_id, mime_parser, parent_chat.id).await? {
|
||||
// If the parent chat is a 1:1 chat, then the message should go to the 1:1 chat.
|
||||
if to_ids.len() == 1 {
|
||||
let name = chat::get_other_chat_contacts(context, parent_chat_id).await?[0];
|
||||
if from_id != *name {
|
||||
mime_parser.set_override_sender_name(Some(name.get_stock_name(context).await));
|
||||
}
|
||||
return Ok(Some((parent_chat.id, parent_chat.blocked)));
|
||||
}
|
||||
if parent_chat.get_type() != Chattype::Single
|
||||
&& is_probably_private_reply(context, to_ids, from_id, mime_parser, parent_chat.id).await?
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if parent_chat.is_protected() || mime_parser.decrypting_failed {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -2670,7 +2670,7 @@ async fn update_verified_keys(
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Some(peerstate) = &mut mimeparser.decryption_info.peerstate else {
|
||||
let Some(peerstate) = &mut mimeparser.peerstate else {
|
||||
// No peerstate means no verified keys.
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -2743,7 +2743,7 @@ async fn has_verified_encryption(
|
||||
// this check is skipped for SELF as there is no proper SELF-peerstate
|
||||
// and results in group-splits otherwise.
|
||||
if from_id != ContactId::SELF {
|
||||
let Some(peerstate) = &mimeparser.decryption_info.peerstate else {
|
||||
let Some(peerstate) = &mimeparser.peerstate else {
|
||||
return Ok(NotVerified(
|
||||
"No peerstate, the contact isn't verified".to_string(),
|
||||
));
|
||||
|
||||
@@ -135,7 +135,7 @@ impl SchedulerState {
|
||||
/// If in the meantime [`SchedulerState::start`] or [`SchedulerState::stop`] is called
|
||||
/// resume will do the right thing and restore the scheduler to the state requested by
|
||||
/// the last call.
|
||||
pub(crate) async fn pause<'a>(&'_ self, context: Context) -> Result<IoPausedGuard> {
|
||||
pub(crate) async fn pause(&'_ self, context: Context) -> Result<IoPausedGuard> {
|
||||
{
|
||||
let mut inner = self.inner.write().await;
|
||||
match *inner {
|
||||
|
||||
@@ -21,7 +21,9 @@ pub fn escape_message_footer_marks(text: &str) -> String {
|
||||
/// `footer_lines` is set to `Some` if the footer was actually removed from `lines`
|
||||
/// (which is equal to the input array otherwise).
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
fn remove_message_footer<'a>(lines: &'a [&str]) -> (&'a [&'a str], Option<&'a [&'a str]>) {
|
||||
pub(crate) fn remove_message_footer<'a>(
|
||||
lines: &'a [&str],
|
||||
) -> (&'a [&'a str], Option<&'a [&'a str]>) {
|
||||
let mut nearly_standard_footer = None;
|
||||
for (ix, &line) in lines.iter().enumerate() {
|
||||
match line {
|
||||
|
||||
@@ -357,9 +357,9 @@ pub(crate) async fn send_msg_to_smtp(
|
||||
.await
|
||||
.context("failed to update retries count")?;
|
||||
|
||||
let (body, recipients, msg_id, retries) = context
|
||||
let Some((body, recipients, msg_id, retries)) = context
|
||||
.sql
|
||||
.query_row(
|
||||
.query_row_optional(
|
||||
"SELECT mime, recipients, msg_id, retries FROM smtp WHERE id=?",
|
||||
(rowid,),
|
||||
|row| {
|
||||
@@ -370,7 +370,10 @@ pub(crate) async fn send_msg_to_smtp(
|
||||
Ok((mime, recipients, msg_id, retries))
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
.await?
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
if retries > 6 {
|
||||
if let Some(mut msg) = Message::load_from_db_optional(context, msg_id).await? {
|
||||
message::set_msg_failed(context, &mut msg, "Number of retries exceeded the limit.")
|
||||
|
||||
@@ -542,7 +542,7 @@ impl ContactId {
|
||||
}
|
||||
|
||||
/// Get contact name, e.g. `Bob`, or `bob@exmple.net` if no name is set.
|
||||
pub async fn get_stock_name(self, context: &Context) -> String {
|
||||
async fn get_stock_name(self, context: &Context) -> String {
|
||||
Contact::get_by_id(context, self)
|
||||
.await
|
||||
.map(|contact| contact.get_display_name().to_string())
|
||||
|
||||
Reference in New Issue
Block a user