mirror of
https://github.com/chatmail/core.git
synced 2026-07-10 21:13:03 +03:00
Compare commits
22 Commits
py-1.89.0
...
broadcast_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d09a5adb03 | ||
|
|
9926804f1b | ||
|
|
294d8862e4 | ||
|
|
d09be1f7e3 | ||
|
|
ed5fc820c2 | ||
|
|
248d9600c5 | ||
|
|
cfadf20d08 | ||
|
|
32eb016ee7 | ||
|
|
2e009d1327 | ||
|
|
797b9fb087 | ||
|
|
e5c255e011 | ||
|
|
39ae44dbf0 | ||
|
|
c9a1ebf257 | ||
|
|
f5c5429fe8 | ||
|
|
8c70393c90 | ||
|
|
37cb16b95c | ||
|
|
50e1866572 | ||
|
|
68a15725d2 | ||
|
|
6ae278f735 | ||
|
|
0b2c3ee163 | ||
|
|
62a0ce29e9 | ||
|
|
6b9aac5234 |
@@ -3,8 +3,12 @@
|
||||
## Unreleased
|
||||
|
||||
### Changes
|
||||
- handle drafts from mailto links in scanned QR #3492
|
||||
- do not overflow ratelimiter leaky bucket #3496
|
||||
|
||||
### Fixes
|
||||
- don't squash text parts of NDN into attachments #3497
|
||||
- do not treat non-failed DSNs as NDNs #3506
|
||||
|
||||
|
||||
## 1.89.0
|
||||
|
||||
377
Cargo.lock
generated
377
Cargo.lock
generated
@@ -134,6 +134,15 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-mutex"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-native-tls"
|
||||
version = "0.4.0"
|
||||
@@ -216,6 +225,54 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2cc6e8e8c993cb61a005fab8c1e5093a29199b7253b05a6883999312935c1ff"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"base64 0.13.0",
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"itoa 1.0.2",
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sha-1 0.10.0",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"mime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.65"
|
||||
@@ -512,6 +569,12 @@ version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
@@ -706,8 +769,28 @@ version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
"darling_core 0.10.2",
|
||||
"darling_macro 0.10.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
||||
dependencies = [
|
||||
"darling_core 0.13.4",
|
||||
"darling_macro 0.13.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
||||
dependencies = [
|
||||
"darling_core 0.14.1",
|
||||
"darling_macro 0.14.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -720,7 +803,35 @@ dependencies = [
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"strsim 0.9.3",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn",
|
||||
]
|
||||
|
||||
@@ -730,7 +841,29 @@ version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_core 0.10.2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||
dependencies = [
|
||||
"darling_core 0.13.4",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
||||
dependencies = [
|
||||
"darling_core 0.14.1",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@@ -752,7 +885,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.89.0"
|
||||
version = "1.87.0"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"anyhow",
|
||||
@@ -822,6 +955,26 @@ dependencies = [
|
||||
"uuid 1.1.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.86.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-channel",
|
||||
"axum",
|
||||
"deltachat",
|
||||
"env_logger 0.9.0",
|
||||
"futures",
|
||||
"log",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"typescript-type-def",
|
||||
"yerpc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_derive"
|
||||
version = "2.0.0"
|
||||
@@ -832,10 +985,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.89.0"
|
||||
version = "1.87.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
"deltachat-jsonrpc",
|
||||
"human-panic",
|
||||
"libc",
|
||||
"num-traits",
|
||||
@@ -863,7 +1017,7 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"darling 0.10.2",
|
||||
"derive_builder_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -876,7 +1030,7 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"darling 0.10.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -1130,7 +1284,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"humantime 1.3.0",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime 2.1.0",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
@@ -1544,6 +1711,12 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range-header"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.7.1"
|
||||
@@ -1586,6 +1759,12 @@ dependencies = [
|
||||
"quick-error 1.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.19"
|
||||
@@ -1880,6 +2059,12 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb"
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.9.1"
|
||||
@@ -2406,7 +2591,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"env_logger 0.7.1",
|
||||
"log",
|
||||
]
|
||||
|
||||
@@ -3045,6 +3230,15 @@ dependencies = [
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "1.5.0"
|
||||
@@ -3125,6 +3319,12 @@ version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.24.1"
|
||||
@@ -3161,6 +3361,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.6"
|
||||
@@ -3292,6 +3498,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"winapi",
|
||||
@@ -3344,6 +3551,18 @@ dependencies = [
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"tokio",
|
||||
"tungstenite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.3"
|
||||
@@ -3367,6 +3586,47 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-range-header",
|
||||
"pin-project-lite",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
@@ -3380,10 +3640,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.28"
|
||||
@@ -3444,6 +3717,25 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"sha-1 0.10.0",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "twofish"
|
||||
version = "0.6.0"
|
||||
@@ -3461,6 +3753,30 @@ version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||
|
||||
[[package]]
|
||||
name = "typescript-type-def"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b4d7f4a1cf8923b722bd56456e0dd560869a37fb849169f18b848f261f01b96"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
"typescript-type-def-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typescript-type-def-derive"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8a8b246d662eb07a8627513dc78bad0b79da6d05e8224ecd559963efc597af0"
|
||||
dependencies = [
|
||||
"darling 0.13.4",
|
||||
"ident_case",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.8"
|
||||
@@ -3521,6 +3837,12 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.0"
|
||||
@@ -3809,6 +4131,41 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yerpc"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1baa6fce4cf16d1cff91b557baceac3e363106f66e555fff906a7f82dce8153"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-channel",
|
||||
"async-mutex",
|
||||
"async-trait",
|
||||
"axum",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"typescript-type-def",
|
||||
"yerpc_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yerpc_derive"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e70f944ca6789bc55ddc86839478f6d49c9d2a66e130f69fd1f8d171b3108990"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"darling 0.14.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.5.6"
|
||||
|
||||
@@ -2292,7 +2292,7 @@ void dc_stop_ongoing_process (dc_context_t* context);
|
||||
* - DC_QR_FPR_MISMATCH with dc_lot_t::id=Contact ID:
|
||||
* scanned fingerprint does not match last seen fingerprint.
|
||||
*
|
||||
* - DC_QR_FPR_WITHOUT_ADDR with dc_lot_t::test1=Formatted fingerprint
|
||||
* - DC_QR_FPR_WITHOUT_ADDR with dc_lot_t::text1=Formatted fingerprint
|
||||
* the scanned QR code contains a fingerprint but no e-mail address;
|
||||
* suggest the user to establish an encrypted connection first.
|
||||
*
|
||||
@@ -2305,7 +2305,8 @@ void dc_stop_ongoing_process (dc_context_t* context);
|
||||
* if so, call dc_set_config_from_qr().
|
||||
*
|
||||
* - DC_QR_ADDR with dc_lot_t::id=Contact ID:
|
||||
* e-mail address scanned,
|
||||
* e-mail address scanned, optionally, a draft message could be set in
|
||||
* dc_lot_t::text1 in which case dc_lot_t::text1_meaning will be DC_TEXT1_DRAFT;
|
||||
* ask the user if they want to start chatting;
|
||||
* if so, call dc_create_chat_by_contact_id().
|
||||
*
|
||||
|
||||
@@ -51,7 +51,7 @@ impl Lot {
|
||||
Qr::FprWithoutAddr { fingerprint, .. } => Some(fingerprint),
|
||||
Qr::Account { domain } => Some(domain),
|
||||
Qr::WebrtcInstance { domain, .. } => Some(domain),
|
||||
Qr::Addr { .. } => None,
|
||||
Qr::Addr { draft, .. } => draft.as_deref(),
|
||||
Qr::Url { url } => Some(url),
|
||||
Qr::Text { text } => Some(text),
|
||||
Qr::WithdrawVerifyContact { .. } => None,
|
||||
@@ -79,7 +79,13 @@ impl Lot {
|
||||
Some(SummaryPrefix::Username(_username)) => Meaning::Text1Username,
|
||||
Some(SummaryPrefix::Me(_text)) => Meaning::Text1Self,
|
||||
},
|
||||
Self::Qr(_qr) => Meaning::None,
|
||||
Self::Qr(qr) => match qr {
|
||||
Qr::Addr {
|
||||
draft: Some(_draft),
|
||||
..
|
||||
} => Meaning::Text1Draft,
|
||||
_ => Meaning::None,
|
||||
},
|
||||
Self::Error(_err) => Meaning::None,
|
||||
}
|
||||
}
|
||||
@@ -118,7 +124,7 @@ impl Lot {
|
||||
Qr::FprWithoutAddr { .. } => Default::default(),
|
||||
Qr::Account { .. } => Default::default(),
|
||||
Qr::WebrtcInstance { .. } => Default::default(),
|
||||
Qr::Addr { contact_id } => contact_id.to_u32(),
|
||||
Qr::Addr { contact_id, .. } => contact_id.to_u32(),
|
||||
Qr::Url { .. } => Default::default(),
|
||||
Qr::Text { .. } => Default::default(),
|
||||
Qr::WithdrawVerifyContact { contact_id, .. } => contact_id.to_u32(),
|
||||
|
||||
@@ -83,7 +83,6 @@ module.exports = {
|
||||
DC_MSG_GIF: 21,
|
||||
DC_MSG_ID_DAYMARKER: 9,
|
||||
DC_MSG_ID_LAST_SPECIAL: 9,
|
||||
DC_MSG_ID_MARKER1: 1,
|
||||
DC_MSG_IMAGE: 20,
|
||||
DC_MSG_STICKER: 23,
|
||||
DC_MSG_TEXT: 10,
|
||||
|
||||
@@ -79,6 +79,10 @@ export class Chat {
|
||||
return this.getType() === C.DC_CHAT_TYPE_GROUP
|
||||
}
|
||||
|
||||
isBroadcast(): boolean {
|
||||
return this.getType() === C.DC_CHAT_TYPE_BROADCAST
|
||||
}
|
||||
|
||||
isMuted(): boolean {
|
||||
return Boolean(binding.dcn_chat_is_muted(this.dc_chat))
|
||||
}
|
||||
|
||||
@@ -83,7 +83,6 @@ export enum C {
|
||||
DC_MSG_GIF = 21,
|
||||
DC_MSG_ID_DAYMARKER = 9,
|
||||
DC_MSG_ID_LAST_SPECIAL = 9,
|
||||
DC_MSG_ID_MARKER1 = 1,
|
||||
DC_MSG_IMAGE = 20,
|
||||
DC_MSG_STICKER = 23,
|
||||
DC_MSG_TEXT = 10,
|
||||
|
||||
@@ -5,6 +5,7 @@ export type ChatTypes =
|
||||
| C.DC_CHAT_TYPE_MAILINGLIST
|
||||
| C.DC_CHAT_TYPE_SINGLE
|
||||
| C.DC_CHAT_TYPE_UNDEFINED
|
||||
| C.DC_CHAT_TYPE_BROADCAST
|
||||
|
||||
export interface ChatJSON {
|
||||
archived: boolean
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
from __future__ import print_function
|
||||
from deltachat import capi
|
||||
from deltachat.capi import ffi, lib
|
||||
|
||||
if __name__ == "__main__":
|
||||
ctx = capi.lib.dc_context_new(ffi.NULL, ffi.NULL)
|
||||
lib.dc_stop_io(ctx)
|
||||
@@ -1,7 +1,41 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2", "cffi>=1.0.0", "pkgconfig"]
|
||||
requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2", "cffi>=1.0.0", "pkgconfig"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deltachat"
|
||||
description = "Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat"
|
||||
readme = "README.rst"
|
||||
requires-python = ">=3.7"
|
||||
authors = [
|
||||
{ name = "holger krekel, Floris Bruynooghe, Bjoern Petersen and contributors" },
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Communications :: Email",
|
||||
"Topic :: Software Development :: Libraries",
|
||||
]
|
||||
dependencies = [
|
||||
"cffi>=1.0.0",
|
||||
"imap-tools",
|
||||
"pluggy",
|
||||
"requests",
|
||||
]
|
||||
dynamic = [
|
||||
"version"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
"Home" = "https://github.com/deltachat/deltachat-core-rust/"
|
||||
"Bug Tracker" = "https://github.com/deltachat/deltachat-core-rust/issues"
|
||||
"Documentation" = "https://py.delta.chat/"
|
||||
|
||||
[project.entry-points.pytest11]
|
||||
"deltachat.testplugin" = "deltachat.testplugin"
|
||||
|
||||
[tool.setuptools_scm]
|
||||
root = ".."
|
||||
tag_regex = '^(?P<prefix>py-)?(?P<version>[^\+]+)(?P<suffix>.*)?$'
|
||||
|
||||
@@ -1,40 +1,4 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
import setuptools
|
||||
|
||||
|
||||
def main():
|
||||
with open("README.rst") as f:
|
||||
long_description = f.read()
|
||||
setuptools.setup(
|
||||
name="deltachat",
|
||||
description="Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat",
|
||||
long_description=long_description,
|
||||
author="holger krekel, Floris Bruynooghe, Bjoern Petersen and contributors",
|
||||
install_requires=["cffi>=1.0.0", "pluggy", "imap-tools", "requests"],
|
||||
setup_requires=[
|
||||
"setuptools_scm", # required for compatibility with `python3 setup.py sdist`
|
||||
"pkgconfig",
|
||||
],
|
||||
packages=setuptools.find_packages("src"),
|
||||
package_dir={"": "src"},
|
||||
cffi_modules=["src/deltachat/_build.py:ffibuilder"],
|
||||
entry_points={
|
||||
"pytest11": [
|
||||
"deltachat.testplugin = deltachat.testplugin",
|
||||
],
|
||||
},
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Communications :: Email",
|
||||
"Topic :: Software Development :: Libraries",
|
||||
],
|
||||
)
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
setup(cffi_modules=["src/deltachat/_build.py:ffibuilder"])
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
assert len(sys.argv) == 2
|
||||
wheelhousedir = sys.argv[1]
|
||||
# pip wheel will build in an isolated tmp dir that does not have git
|
||||
# history so setuptools_scm can not automatically determine a
|
||||
# version there. So pass in the version through an env var.
|
||||
version = subprocess.check_output(["python", "setup.py", "--version"]).strip().split(b"\n")[-1]
|
||||
os.environ["SETUPTOOLS_SCM_PRETEND_VERSION"] = version.decode("ascii")
|
||||
subprocess.check_call(("pip wheel . -w %s" % wheelhousedir).split())
|
||||
@@ -9,7 +9,7 @@ envlist =
|
||||
[testenv]
|
||||
commands =
|
||||
pytest -n6 --extra-info --reruns 2 --reruns-delay 5 -v -rsXx --ignored --strict-tls {posargs: tests examples}
|
||||
python tests/package_wheels.py {toxworkdir}/wheelhouse
|
||||
pip wheel . -w {toxworkdir}/wheelhouse --no-deps
|
||||
passenv =
|
||||
DCC_RS_DEV
|
||||
DCC_RS_TARGET
|
||||
|
||||
@@ -33,7 +33,10 @@ mkdir -p $TOXWORKDIR
|
||||
# Note that the independent remote_tests_python step does all kinds of
|
||||
# live-testing already.
|
||||
unset DCC_NEW_TMP_EMAIL
|
||||
tox --workdir "$TOXWORKDIR" -e py37,py38,py39,py310,pypy37,pypy38,pypy39,auditwheels
|
||||
|
||||
# Try to build wheels for a range of interpreters, but don't fail if they are not available.
|
||||
# E.g. musllinux_1_1 does not have PyPy interpreters as of 2022-07-10
|
||||
tox --workdir "$TOXWORKDIR" -e py37,py38,py39,py310,pypy37,pypy38,pypy39,auditwheels --skip-missing-interpreters true
|
||||
popd
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::download::DownloadState;
|
||||
use crate::ephemeral::{start_ephemeral_timers_msgids, Timer as EphemeralTimer};
|
||||
use crate::events::EventType;
|
||||
use crate::imap::markseen_on_imap_table;
|
||||
use crate::mimeparser::{parse_message_id, FailureReport, SystemMessage};
|
||||
use crate::mimeparser::{parse_message_id, DeliveryReport, SystemMessage};
|
||||
use crate::param::{Param, Params};
|
||||
use crate::pgp::split_armored_data;
|
||||
use crate::scheduler::InterruptInfo;
|
||||
@@ -1532,7 +1532,7 @@ pub async fn handle_mdn(
|
||||
/// Where appropriate, also adds an info message telling the user which of the recipients of a group message failed.
|
||||
pub(crate) async fn handle_ndn(
|
||||
context: &Context,
|
||||
failed: &FailureReport,
|
||||
failed: &DeliveryReport,
|
||||
error: Option<String>,
|
||||
) -> Result<()> {
|
||||
if failed.rfc724_mid.is_empty() {
|
||||
@@ -1588,7 +1588,7 @@ pub(crate) async fn handle_ndn(
|
||||
|
||||
async fn ndn_maybe_add_info_msg(
|
||||
context: &Context,
|
||||
failed: &FailureReport,
|
||||
failed: &DeliveryReport,
|
||||
chat_id: ChatId,
|
||||
chat_type: Chattype,
|
||||
) -> Result<()> {
|
||||
|
||||
@@ -73,7 +73,7 @@ pub struct MimeMessage {
|
||||
pub(crate) user_avatar: Option<AvatarAction>,
|
||||
pub(crate) group_avatar: Option<AvatarAction>,
|
||||
pub(crate) mdn_reports: Vec<Report>,
|
||||
pub(crate) failure_report: Option<FailureReport>,
|
||||
pub(crate) delivery_report: Option<DeliveryReport>,
|
||||
|
||||
/// Standard USENET signature, if any.
|
||||
pub(crate) footer: Option<String>,
|
||||
@@ -332,7 +332,7 @@ impl MimeMessage {
|
||||
webxdc_status_update: None,
|
||||
user_avatar: None,
|
||||
group_avatar: None,
|
||||
failure_report: None,
|
||||
delivery_report: None,
|
||||
footer: None,
|
||||
is_mime_modified: false,
|
||||
decoded_data: Vec::new(),
|
||||
@@ -535,7 +535,9 @@ impl MimeMessage {
|
||||
self.parse_system_message_headers(context);
|
||||
self.parse_avatar_headers(context).await;
|
||||
self.parse_videochat_headers();
|
||||
self.squash_attachment_parts();
|
||||
if self.delivery_report.is_none() {
|
||||
self.squash_attachment_parts();
|
||||
}
|
||||
|
||||
if let Some(ref subject) = self.get_subject() {
|
||||
let mut prepend_subject = true;
|
||||
@@ -867,7 +869,7 @@ impl MimeMessage {
|
||||
// Some providers, e.g. Tiscali, forget to set the report-type. So, if it's None, assume that it might be delivery-status
|
||||
Some("delivery-status") | None => {
|
||||
if let Some(report) = self.process_delivery_status(context, mail)? {
|
||||
self.failure_report = Some(report);
|
||||
self.delivery_report = Some(report);
|
||||
}
|
||||
|
||||
// Add all parts (we need another part, preferably text/plain, to show as an error message)
|
||||
@@ -1278,9 +1280,46 @@ impl MimeMessage {
|
||||
&self,
|
||||
context: &Context,
|
||||
report: &mailparse::ParsedMail<'_>,
|
||||
) -> Result<Option<FailureReport>> {
|
||||
) -> Result<Option<DeliveryReport>> {
|
||||
// Assume failure.
|
||||
let mut failure = true;
|
||||
|
||||
if let Some(status_part) = report.subparts.get(1) {
|
||||
// RFC 3464 defines `message/delivery-status`
|
||||
// RFC 6533 defines `message/global-delivery-status`
|
||||
if status_part.ctype.mimetype != "message/delivery-status"
|
||||
&& status_part.ctype.mimetype != "message/global-delivery-status"
|
||||
{
|
||||
warn!(context, "Second part of Delivery Status Notification is not message/delivery-status or message/global-delivery-status, ignoring");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let status_body = status_part.get_body_raw()?;
|
||||
|
||||
// Skip per-message fields.
|
||||
let (_, sz) = mailparse::parse_headers(&status_body)?;
|
||||
|
||||
// Parse first set of per-recipient fields
|
||||
if let Some(status_body) = status_body.get(sz..) {
|
||||
let (status_fields, _) = mailparse::parse_headers(status_body)?;
|
||||
if let Some(action) = status_fields.get_first_value("action") {
|
||||
if action != "failed" {
|
||||
info!(context, "DSN with {:?} action", action);
|
||||
failure = false;
|
||||
}
|
||||
} else {
|
||||
warn!(context, "DSN without action");
|
||||
}
|
||||
} else {
|
||||
warn!(context, "DSN without per-recipient fields");
|
||||
}
|
||||
} else {
|
||||
// No message/delivery-status part.
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// parse as mailheaders
|
||||
if let Some(original_msg) = report.subparts.iter().find(|p| {
|
||||
if let Some(original_msg) = report.subparts.get(2).filter(|p| {
|
||||
p.ctype.mimetype.contains("rfc822")
|
||||
|| p.ctype.mimetype == "message/global"
|
||||
|| p.ctype.mimetype == "message/global-headers"
|
||||
@@ -1301,9 +1340,10 @@ impl MimeMessage {
|
||||
None // We do not know which recipient failed
|
||||
};
|
||||
|
||||
return Ok(Some(FailureReport {
|
||||
return Ok(Some(DeliveryReport {
|
||||
rfc724_mid: original_message_id,
|
||||
failed_recipient: to.map(|s| s.addr),
|
||||
failure,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1384,7 +1424,7 @@ impl MimeMessage {
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if maybe_ndn && self.failure_report.is_none() {
|
||||
if maybe_ndn && self.delivery_report.is_none() {
|
||||
static RE: Lazy<regex::Regex> =
|
||||
Lazy::new(|| regex::Regex::new(r"Message-ID:(.*)").unwrap());
|
||||
for captures in self
|
||||
@@ -1398,9 +1438,10 @@ impl MimeMessage {
|
||||
if let Ok(Some(_)) =
|
||||
message::rfc724_mid_exists(context, &original_message_id).await
|
||||
{
|
||||
self.failure_report = Some(FailureReport {
|
||||
self.delivery_report = Some(DeliveryReport {
|
||||
rfc724_mid: original_message_id,
|
||||
failed_recipient: None,
|
||||
failure: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1438,13 +1479,15 @@ impl MimeMessage {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(failure_report) = &self.failure_report {
|
||||
let error = parts
|
||||
.iter()
|
||||
.find(|p| p.typ == Viewtype::Text)
|
||||
.map(|p| p.msg.clone());
|
||||
if let Err(e) = message::handle_ndn(context, failure_report, error).await {
|
||||
warn!(context, "Could not handle ndn: {}", e);
|
||||
if let Some(delivery_report) = &self.delivery_report {
|
||||
if delivery_report.failure {
|
||||
let error = parts
|
||||
.iter()
|
||||
.find(|p| p.typ == Viewtype::Text)
|
||||
.map(|p| p.msg.clone());
|
||||
if let Err(e) = message::handle_ndn(context, delivery_report, error).await {
|
||||
warn!(context, "Could not handle ndn: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1524,6 +1567,7 @@ async fn update_gossip_peerstates(
|
||||
Ok(gossiped_addr)
|
||||
}
|
||||
|
||||
/// Message Disposition Notification (RFC 8098)
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Report {
|
||||
/// Original-Message-ID header
|
||||
@@ -1535,10 +1579,12 @@ pub(crate) struct Report {
|
||||
additional_message_ids: Vec<String>,
|
||||
}
|
||||
|
||||
/// Delivery Status Notification (RFC 3464, RFC 6533)
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FailureReport {
|
||||
pub(crate) struct DeliveryReport {
|
||||
pub rfc724_mid: String,
|
||||
pub failed_recipient: Option<String>,
|
||||
pub failure: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
|
||||
78
src/qr.rs
78
src/qr.rs
@@ -61,6 +61,7 @@ pub enum Qr {
|
||||
},
|
||||
Addr {
|
||||
contact_id: ContactId,
|
||||
draft: Option<String>,
|
||||
},
|
||||
Url {
|
||||
url: String,
|
||||
@@ -451,15 +452,52 @@ pub async fn set_config_from_qr(context: &Context, qr: &str) -> Result<()> {
|
||||
async fn decode_mailto(context: &Context, qr: &str) -> Result<Qr> {
|
||||
let payload = &qr[MAILTO_SCHEME.len()..];
|
||||
|
||||
let addr = if let Some(query_index) = payload.find('?') {
|
||||
&payload[..query_index]
|
||||
let (addr, query) = if let Some(query_index) = payload.find('?') {
|
||||
(&payload[..query_index], &payload[query_index + 1..])
|
||||
} else {
|
||||
payload
|
||||
(payload, "")
|
||||
};
|
||||
|
||||
let param: BTreeMap<&str, &str> = query
|
||||
.split('&')
|
||||
.filter_map(|s| {
|
||||
if let [key, value] = s.splitn(2, '=').collect::<Vec<_>>()[..] {
|
||||
Some((key, value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let subject = if let Some(subject) = param.get("subject") {
|
||||
subject.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let draft = if let Some(body) = param.get("body") {
|
||||
if subject.is_empty() {
|
||||
body.to_string()
|
||||
} else {
|
||||
subject + "\n" + body
|
||||
}
|
||||
} else {
|
||||
subject
|
||||
};
|
||||
let draft = draft.replace('+', "%20"); // sometimes spaces are encoded as `+`
|
||||
let draft = match percent_decode_str(&draft).decode_utf8() {
|
||||
Ok(decoded_draft) => decoded_draft.to_string(),
|
||||
Err(_err) => draft,
|
||||
};
|
||||
|
||||
let addr = normalize_address(addr)?;
|
||||
let name = "".to_string();
|
||||
Qr::from_address(context, name, addr).await
|
||||
Qr::from_address(
|
||||
context,
|
||||
name,
|
||||
addr,
|
||||
if draft.is_empty() { None } else { Some(draft) },
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Extract address for the smtp scheme.
|
||||
@@ -477,7 +515,7 @@ async fn decode_smtp(context: &Context, qr: &str) -> Result<Qr> {
|
||||
|
||||
let addr = normalize_address(addr)?;
|
||||
let name = "".to_string();
|
||||
Qr::from_address(context, name, addr).await
|
||||
Qr::from_address(context, name, addr, None).await
|
||||
}
|
||||
|
||||
/// Extract address for the matmsg scheme.
|
||||
@@ -502,7 +540,7 @@ async fn decode_matmsg(context: &Context, qr: &str) -> Result<Qr> {
|
||||
|
||||
let addr = normalize_address(addr)?;
|
||||
let name = "".to_string();
|
||||
Qr::from_address(context, name, addr).await
|
||||
Qr::from_address(context, name, addr, None).await
|
||||
}
|
||||
|
||||
static VCARD_NAME_RE: Lazy<regex::Regex> =
|
||||
@@ -531,14 +569,19 @@ async fn decode_vcard(context: &Context, qr: &str) -> Result<Qr> {
|
||||
bail!("Bad e-mail address");
|
||||
};
|
||||
|
||||
Qr::from_address(context, name, addr).await
|
||||
Qr::from_address(context, name, addr, None).await
|
||||
}
|
||||
|
||||
impl Qr {
|
||||
pub async fn from_address(context: &Context, name: String, addr: String) -> Result<Self> {
|
||||
pub async fn from_address(
|
||||
context: &Context,
|
||||
name: String,
|
||||
addr: String,
|
||||
draft: Option<String>,
|
||||
) -> Result<Self> {
|
||||
let (contact_id, _) =
|
||||
Contact::add_or_lookup(context, &name, &addr, Origin::UnhandledQrScan).await?;
|
||||
Ok(Qr::Addr { contact_id })
|
||||
Ok(Qr::Addr { contact_id, draft })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,12 +662,13 @@ mod tests {
|
||||
"BEGIN:VCARD\nVERSION:3.0\nN:Last;First\nEMAIL;TYPE=INTERNET:stress@test.local\nEND:VCARD"
|
||||
).await?;
|
||||
|
||||
if let Qr::Addr { contact_id } = qr {
|
||||
if let Qr::Addr { contact_id, draft } = qr {
|
||||
let contact = Contact::get_by_id(&ctx.ctx, contact_id).await?;
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
assert_eq!(contact.get_name(), "First Last");
|
||||
assert_eq!(contact.get_authname(), "");
|
||||
assert_eq!(contact.get_display_name(), "First Last");
|
||||
assert!(draft.is_none());
|
||||
} else {
|
||||
bail!("Wrong QR code type");
|
||||
}
|
||||
@@ -642,9 +686,10 @@ mod tests {
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Qr::Addr { contact_id } = qr {
|
||||
if let Qr::Addr { contact_id, draft } = qr {
|
||||
let contact = Contact::get_by_id(&ctx.ctx, contact_id).await?;
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
assert!(draft.is_none());
|
||||
} else {
|
||||
bail!("Wrong QR code type");
|
||||
}
|
||||
@@ -658,20 +703,22 @@ mod tests {
|
||||
|
||||
let qr = check_qr(
|
||||
&ctx.ctx,
|
||||
"mailto:stress@test.local?subject=hello&body=world",
|
||||
"mailto:stress@test.local?subject=hello&body=beautiful+world",
|
||||
)
|
||||
.await?;
|
||||
if let Qr::Addr { contact_id } = qr {
|
||||
if let Qr::Addr { contact_id, draft } = qr {
|
||||
let contact = Contact::get_by_id(&ctx.ctx, contact_id).await?;
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
assert_eq!(draft.unwrap(), "hello\nbeautiful world");
|
||||
} else {
|
||||
bail!("Wrong QR code type");
|
||||
}
|
||||
|
||||
let res = check_qr(&ctx.ctx, "mailto:no-questionmark@example.org").await?;
|
||||
if let Qr::Addr { contact_id } = res {
|
||||
if let Qr::Addr { contact_id, draft } = res {
|
||||
let contact = Contact::get_by_id(&ctx.ctx, contact_id).await?;
|
||||
assert_eq!(contact.get_addr(), "no-questionmark@example.org");
|
||||
assert!(draft.is_none());
|
||||
} else {
|
||||
bail!("Wrong QR code type");
|
||||
}
|
||||
@@ -686,11 +733,12 @@ mod tests {
|
||||
async fn test_decode_smtp() -> Result<()> {
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
if let Qr::Addr { contact_id } =
|
||||
if let Qr::Addr { contact_id, draft } =
|
||||
check_qr(&ctx.ctx, "SMTP:stress@test.local:subjecthello:bodyworld").await?
|
||||
{
|
||||
let contact = Contact::get_by_id(&ctx.ctx, contact_id).await?;
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
assert!(draft.is_none());
|
||||
} else {
|
||||
bail!("Wrong QR code type");
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ impl Ratelimit {
|
||||
|
||||
/// Returns true if it is allowed to send a message.
|
||||
fn can_send_at(&self, now: SystemTime) -> bool {
|
||||
self.current_value_at(now) <= self.quota
|
||||
self.current_value_at(now) + 1.0 <= self.quota
|
||||
}
|
||||
|
||||
/// Returns true if can send another message now.
|
||||
@@ -62,7 +62,7 @@ impl Ratelimit {
|
||||
}
|
||||
|
||||
fn send_at(&mut self, now: SystemTime) {
|
||||
self.current_value = self.current_value_at(now) + 1.0;
|
||||
self.current_value = f64::min(self.quota, self.current_value_at(now) + 1.0);
|
||||
self.last_update = now;
|
||||
}
|
||||
|
||||
@@ -77,10 +77,10 @@ impl Ratelimit {
|
||||
|
||||
fn until_can_send_at(&self, now: SystemTime) -> Duration {
|
||||
let current_value = self.current_value_at(now);
|
||||
if current_value <= self.quota {
|
||||
if current_value + 1.0 <= self.quota {
|
||||
Duration::ZERO
|
||||
} else {
|
||||
let requirement = current_value - self.quota;
|
||||
let requirement = current_value + 1.0 - self.quota;
|
||||
let rate = self.quota / self.window.as_secs_f64();
|
||||
Duration::from_secs_f64(requirement / rate)
|
||||
}
|
||||
@@ -109,8 +109,6 @@ mod tests {
|
||||
ratelimit.send_at(now);
|
||||
assert!(ratelimit.can_send_at(now));
|
||||
ratelimit.send_at(now);
|
||||
assert!(ratelimit.can_send_at(now));
|
||||
ratelimit.send_at(now);
|
||||
|
||||
// Can't send more messages now.
|
||||
assert!(!ratelimit.can_send_at(now));
|
||||
@@ -125,11 +123,8 @@ mod tests {
|
||||
// Send one more message anyway, over quota.
|
||||
ratelimit.send_at(now);
|
||||
|
||||
// Waiting 20 seconds is not enough.
|
||||
let now = now + Duration::from_secs(20);
|
||||
assert!(!ratelimit.can_send_at(now));
|
||||
|
||||
// Can send another message after 40 seconds.
|
||||
// Always can send another message after 20 seconds,
|
||||
// leaky bucket never overflows.
|
||||
let now = now + Duration::from_secs(20);
|
||||
assert!(ratelimit.can_send_at(now));
|
||||
|
||||
|
||||
@@ -497,9 +497,9 @@ async fn add_parts(
|
||||
ChatIdBlocked::lookup_by_contact(context, from_id).await?
|
||||
};
|
||||
|
||||
if chat_id.is_none() && mime_parser.failure_report.is_some() {
|
||||
if chat_id.is_none() && mime_parser.delivery_report.is_some() {
|
||||
chat_id = Some(DC_CHAT_ID_TRASH);
|
||||
info!(context, "Message belongs to an NDN (TRASH)",);
|
||||
info!(context, "Message is a DSN (TRASH)",);
|
||||
}
|
||||
|
||||
if chat_id.is_none() {
|
||||
@@ -2659,7 +2659,7 @@ mod tests {
|
||||
"shenauithz@testrun.org",
|
||||
"Mr.un2NYERi1RM.lbQ5F9q-QyJ@tiscali.it",
|
||||
include_bytes!("../test-data/message/tiscali_ndn.eml"),
|
||||
Some("Delivery to at least one recipient failed."),
|
||||
Some("Delivery status notification – This is an automatically generated Delivery Status Notification. \n\nDelivery to the following recipients was aborted after 2 second(s):\n\n * shenauithz@testrun.org"),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -2736,6 +2736,32 @@ mod tests {
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Tests that text part is not squashed into OpenPGP attachment.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_ndn_with_attachment() {
|
||||
test_parse_ndn(
|
||||
"alice@example.org",
|
||||
"bob@example.net",
|
||||
"Mr.I6Da6dXcTel.TroC5J3uSDH@example.org",
|
||||
include_bytes!("../test-data/message/ndn_with_attachment.eml"),
|
||||
Some("Undelivered Mail Returned to Sender – This is the mail system at host relay01.example.org.\n\nI'm sorry to have to inform you that your message could not\nbe delivered to one or more recipients. It's attached below.\n\nFor further assistance, please send mail to postmaster.\n\nIf you do so, please include this problem report. You can\ndelete your own text from the attached returned message.\n\n The mail system\n\n<bob@example.net>: host mx2.example.net[80.241.60.215] said: 552 5.2.2\n <bob@example.net>: Recipient address rejected: Mailbox quota exceeded (in\n reply to RCPT TO command)\n\n<bob2@example.net>: host mx1.example.net[80.241.60.212] said: 552 5.2.2\n <bob2@example.net>: Recipient address rejected: Mailbox quota\n exceeded (in reply to RCPT TO command)")
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Test that DSN is not treated as NDN if Action: is not "failed"
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_dsn_relayed() {
|
||||
test_parse_ndn(
|
||||
"anon_1@posteo.de",
|
||||
"anon_2@gmx.at",
|
||||
"8b7b1a9d0c8cc588c7bcac47f5687634@posteo.de",
|
||||
include_bytes!("../test-data/message/dsn_relayed.eml"),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
// ndn = Non Delivery Notification
|
||||
async fn test_parse_ndn(
|
||||
self_addr: &str,
|
||||
@@ -2785,7 +2811,14 @@ mod tests {
|
||||
receive_imf(&t, raw_ndn, false).await.unwrap();
|
||||
let msg = Message::load_from_db(&t, msg_id).await.unwrap();
|
||||
|
||||
assert_eq!(msg.state, MessageState::OutFailed);
|
||||
assert_eq!(
|
||||
msg.state,
|
||||
if error_msg.is_some() {
|
||||
MessageState::OutFailed
|
||||
} else {
|
||||
MessageState::OutDelivered
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(msg.error(), error_msg.map(|error| error.to_string()));
|
||||
}
|
||||
|
||||
109
test-data/message/dsn_relayed.eml
Normal file
109
test-data/message/dsn_relayed.eml
Normal file
@@ -0,0 +1,109 @@
|
||||
Return-Path: <>
|
||||
Delivered-To: anon_1@posteo.de
|
||||
Received: from proxy02.posteo.name ([127.0.0.1])
|
||||
by dovecot16.posteo.name (Dovecot) with LMTP id 4LJTJKBpxGClSAAAchYRkQ
|
||||
for <anon_1@posteo.de>; Sat, 12 Jun 2021 10:42:09 +0200
|
||||
Received: from proxy02.posteo.de ([127.0.0.1])
|
||||
by proxy02.posteo.name (Dovecot) with LMTP id 0NENMHNXxGDI4AIAGFAyLg
|
||||
; Sat, 12 Jun 2021 10:42:09 +0200
|
||||
Received: from mailin02.posteo.de (unknown [10.0.0.62])
|
||||
by proxy02.posteo.de (Postfix) with ESMTPS id 4G2B686dbVz11xc
|
||||
for <anon_1@posteo.de>; Sat, 12 Jun 2021 10:42:08 +0200 (CEST)
|
||||
Received: from mx01.posteo.de (mailin02.posteo.de [127.0.0.1])
|
||||
by mailin02.posteo.de (Postfix) with ESMTPS id AC2472152F
|
||||
for <anon_1@posteo.de>; Sat, 12 Jun 2021 10:42:08 +0200 (CEST)
|
||||
X-Virus-Scanned: amavisd-new at posteo.de
|
||||
X-Spam-Flag: NO
|
||||
X-Spam-Score: -1
|
||||
X-Spam-Level:
|
||||
X-Spam-Status: No, score=-1 tagged_above=-1000 required=7
|
||||
tests=[ALL_TRUSTED=-1] autolearn=disabled
|
||||
X-Posteo-Antispam-Signature: v=1; e=base64; a=aes-256-gcm; d=7/8PYiypR3F6lmk8rQGNxZgmuPRJI9wU2IwnCWX1fg/nFdbPrDu9pCFSVsnrK1SjAWJJ9HtJVYECbeMxMhq9tOMxZf1nSN2cM/XXzeH6ELaaQfOWfQbBff3ZIe+rix/CF1uWX164
|
||||
Authentication-Results: posteo.de; dmarc=none (p=none dis=none) header.from=mout02.posteo.de
|
||||
X-Posteo-TLS-Received-Status: TLSv1.3
|
||||
Received: from mout02.posteo.de (mout02.posteo.de [185.67.36.66])
|
||||
by mx01.posteo.de (Postfix) with ESMTPS id 4G2B676wGBz10Wt
|
||||
for <anon_1@posteo.at>; Sat, 12 Jun 2021 10:42:07 +0200 (CEST)
|
||||
Received: by mout02.posteo.de (Postfix)
|
||||
id A9F481A0089; Sat, 12 Jun 2021 10:42:07 +0200 (CEST)
|
||||
Date: Sat, 12 Jun 2021 10:42:07 +0200 (CEST)
|
||||
From: MAILER-DAEMON@mout02.posteo.de (Mail Delivery System)
|
||||
Subject: Successful Mail Delivery Report
|
||||
To: anon_1@posteo.at
|
||||
Auto-Submitted: auto-replied
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/report; report-type=delivery-status;
|
||||
boundary="56E6D1A007F.1623487327/mout02.posteo.de"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Message-Id: <20210612084207.A9F481A0089@mout02.posteo.de>
|
||||
|
||||
This is a MIME-encapsulated message.
|
||||
|
||||
--56E6D1A007F.1623487327/mout02.posteo.de
|
||||
Content-Description: Notification
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
|
||||
This is the mail system at host mout02.posteo.de.
|
||||
|
||||
Your message was successfully delivered to the destination(s)
|
||||
listed below. If the message was delivered to mailbox you will
|
||||
receive no further notifications. Otherwise you may still receive
|
||||
notifications of mail delivery errors from other systems.
|
||||
|
||||
The mail system
|
||||
|
||||
<anon_2@gmx.at>: delivery via mx00.emig.gmx.net[212.227.15.9]:25: 250
|
||||
Requested mail action okay, completed: id=1M9ohD-1lvXys2NFd-005r3O
|
||||
|
||||
--56E6D1A007F.1623487327/mout02.posteo.de
|
||||
Content-Description: Delivery report
|
||||
Content-Type: message/delivery-status
|
||||
|
||||
Reporting-MTA: dns; mout02.posteo.de
|
||||
X-Postfix-Queue-ID: 56E6D1A007F
|
||||
X-Postfix-Sender: rfc822; anon_1@posteo.at
|
||||
Arrival-Date: Sat, 12 Jun 2021 10:42:07 +0200 (CEST)
|
||||
|
||||
Final-Recipient: rfc822; anon_2@gmx.at
|
||||
Original-Recipient: rfc822;anon_2@gmx.at
|
||||
Action: relayed
|
||||
Status: 2.0.0
|
||||
Remote-MTA: dns; mx00.emig.gmx.net
|
||||
Diagnostic-Code: smtp; 250 Requested mail action okay, completed:
|
||||
id=1M9ohD-1lvXys2NFd-005r3O
|
||||
|
||||
--56E6D1A007F.1623487327/mout02.posteo.de
|
||||
Content-Description: Message Headers
|
||||
Content-Type: text/rfc822-headers
|
||||
|
||||
Return-Path: <anon_1@posteo.at>
|
||||
Received: from mout02.posteo.de (unknown [10.0.0.66])
|
||||
by mout02.posteo.de (Postfix) with ESMTPS id 56E6D1A007F
|
||||
for <anon_2@gmx.at>; Sat, 12 Jun 2021 10:42:07 +0200 (CEST)
|
||||
Received: from submission-encrypt01.posteo.de (unknown [10.0.0.75])
|
||||
by mout02.posteo.de (Postfix) with ESMTPS id 1C39E2400FD
|
||||
for <anon_2@gmx.at>; Sat, 12 Jun 2021 10:42:07 +0200 (CEST)
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.at; s=2017;
|
||||
t=1623487327; bh=+ZIKEoFCh8N5xYBj6tMbfqiyHmay76uM4H4bfme6VyU=;
|
||||
h=Date:From:To:Subject:From;
|
||||
b=QK6HwDU2YEzzTgHN2PRT2lPaf5uwC7ZJ1Y0QMSUrEyvJxwPj6+z6OoEqRDcgQcGVo
|
||||
biAO2aKyBX+YCFwM5a6CaJotv8DaL+hn/XLk3RKqxGKTu5cBLQXJc0gjfRMel7LnBg
|
||||
i0UxTeOqoTw2anWTonH2GnseUPtVAhi23UICVD6gC6DchuNYF/YloMltns5HMGthQh
|
||||
z279J05txneSKgpbU/R3fN2v5ACEve7X6GoxM0hDZRNmAur0HAxAREc9xIaHwQ3zXM
|
||||
dEGFyO53s+UzLlOFnY4vhGVI3AiyOZUProq6vX40g9e4TkrIJMGd1pyKG4NdajauuY
|
||||
KTIwbUiR5Y2Xw==
|
||||
Received: from customer (localhost [127.0.0.1])
|
||||
by submission (posteo.de) with ESMTPSA id 4G2B665xBPz6tmH
|
||||
for <anon_2@gmx.at>; Sat, 12 Jun 2021 10:42:06 +0200 (CEST)
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/alternative;
|
||||
boundary="=_d0190a7dc3b70a1dcf12785779aad292"
|
||||
Date: Sat, 12 Jun 2021 08:42:06 +0000
|
||||
From: Anon_1 <anon_1@posteo.at>
|
||||
To: Anon_2 <anon_2@gmx.at>
|
||||
Subject: Hallo
|
||||
Message-ID: <8b7b1a9d0c8cc588c7bcac47f5687634@posteo.de>
|
||||
Posteo-User: anon_1@posteo.de
|
||||
Posteo-Dkim: ok
|
||||
|
||||
--56E6D1A007F.1623487327/mout02.posteo.de--
|
||||
123
test-data/message/ndn_with_attachment.eml
Normal file
123
test-data/message/ndn_with_attachment.eml
Normal file
@@ -0,0 +1,123 @@
|
||||
Return-Path: <>
|
||||
Delivered-To: <alice@example.org>
|
||||
Date: Sat, 25 Jun 2022 21:35:33 -0400 (CDT)
|
||||
From: MAILER-DAEMON@example.org (Mail Delivery System)
|
||||
Subject: Undelivered Mail Returned to Sender
|
||||
To: alice@example.org
|
||||
Auto-Submitted: auto-replied
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/report; report-type=delivery-status;
|
||||
boundary="C06EAE01B0.1656207333/relay01.example.org"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Message-Id: <20220626013533.5F0E5E0180@relay01.example.org>
|
||||
|
||||
This is a MIME-encapsulated message.
|
||||
|
||||
--C06EAE01B0.1656207333/relay01.example.org
|
||||
Content-Description: Notification
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
|
||||
This is the mail system at host relay01.example.org.
|
||||
|
||||
I'm sorry to have to inform you that your message could not
|
||||
be delivered to one or more recipients. It's attached below.
|
||||
|
||||
For further assistance, please send mail to postmaster.
|
||||
|
||||
If you do so, please include this problem report. You can
|
||||
delete your own text from the attached returned message.
|
||||
|
||||
The mail system
|
||||
|
||||
<bob@example.net>: host mx2.example.net[80.241.60.215] said: 552 5.2.2
|
||||
<bob@example.net>: Recipient address rejected: Mailbox quota exceeded (in
|
||||
reply to RCPT TO command)
|
||||
|
||||
<bob2@example.net>: host mx1.example.net[80.241.60.212] said: 552 5.2.2
|
||||
<bob2@example.net>: Recipient address rejected: Mailbox quota
|
||||
exceeded (in reply to RCPT TO command)
|
||||
|
||||
--C06EAE01B0.1656207333/relay01.example.org
|
||||
Content-Description: Delivery report
|
||||
Content-Type: message/delivery-status
|
||||
|
||||
Reporting-MTA: dns; relay01.example.org
|
||||
X-Postfix-Queue-ID: C06EAE01B0
|
||||
X-Postfix-Sender: rfc822; alice@example.org
|
||||
Arrival-Date: Sat, 25 Jun 2022 21:35:23 -0400 (CDT)
|
||||
|
||||
Final-Recipient: rfc822; bob@example.org
|
||||
Original-Recipient: rfc822;bob@example.org
|
||||
Action: failed
|
||||
Status: 5.2.2
|
||||
Remote-MTA: dns; mx2.example.net
|
||||
Diagnostic-Code: smtp; 552 5.2.2 <bob@example.org>: Recipient address
|
||||
rejected: Mailbox quota exceeded
|
||||
|
||||
Final-Recipient: rfc822; bob2@example.net
|
||||
Original-Recipient: rfc822;bob2@example.net
|
||||
Action: failed
|
||||
Status: 5.2.2
|
||||
Remote-MTA: dns; mx1.example.net
|
||||
Diagnostic-Code: smtp; 552 5.2.2 <bob2@example.net>: Recipient address
|
||||
rejected: Mailbox quota exceeded
|
||||
|
||||
--C06EAE01B0.1656207333/relay01.example.org
|
||||
Content-Description: Undelivered Message
|
||||
Content-Type: message/rfc822
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Return-Path: <alice@example.org>
|
||||
Received: from smtp-gw01.enet.cu (unknown [172.29.8.31])
|
||||
by relay01.example.org (Postfix) with ESMTP id C06EAE01B0;
|
||||
Sat, 25 Jun 2022 21:35:23 -0400 (CDT)
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.org; s=nauta;
|
||||
t=1656207323; bh=c0t8muX/2spIAQ/MRxSXsZ/ljSWP8uTxPcM5fqYC6qg=;
|
||||
h=Subject:From:To:Date:In-Reply-To:References;
|
||||
b=fggUmxnR3RjNfxO4S460fvxfenu3eUPvsGjldIGQeApxT508qGctJZ0tzYiFelOAu
|
||||
4zL8+VhlX0KdnrvRXo2/00O9U2kM8gpRLE9tIR0Rn6FyWM4nU5rcvAJp3oiUWxYzPt
|
||||
NMlzFeUsBvwFCJCEmZllSHaAyt/HTvWe68sGQsu8=
|
||||
Received: from amavis4.example.org (unknown [172.29.8.235])
|
||||
by smtp-gw01.enet.cu (Postfix) with ESMTP id ECFE9180004D;
|
||||
Sat, 25 Jun 2022 21:35:18 -0400 (CDT)
|
||||
Received: from smtp.example.org ([172.29.8.248])
|
||||
by amavis4.example.org with ESMTP id 25Q1ZNDW016368-25Q1ZNDX016368;
|
||||
Sat, 25 Jun 2022 21:35:23 -0400
|
||||
Received: from [127.0.0.1] (unknown [10.59.196.114])
|
||||
by smtp.example.org (Postfix) with ESMTPSA id E07E79F110;
|
||||
Sat, 25 Jun 2022 21:35:21 -0400 (CDT)
|
||||
Subject: ...
|
||||
From: Alice <alice@example.org>
|
||||
To: <bob@example.net>, <bob2@example.net>
|
||||
Date: Sun, 26 Jun 2022 01:35:20 +0000
|
||||
Message-ID: <Mr.I6Da6dXcTel.TroC5J3uSDH@example.org>
|
||||
In-Reply-To: <Mr.I6Da6dXcTel.-qoK1IWpqby@example.org>
|
||||
References: <Mr.I6Da6dXcTel.TroC5J3uSDH@example.org>
|
||||
Chat-Version: 1.0
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
|
||||
boundary="OOGjcgxCkv3w8d1thY6jg0b8O0NFJE"
|
||||
|
||||
|
||||
--OOGjcgxCkv3w8d1thY6jg0b8O0NFJE
|
||||
Content-Type: application/pgp-encrypted
|
||||
Content-Description: PGP/MIME version identification
|
||||
|
||||
Version: 1
|
||||
|
||||
|
||||
--OOGjcgxCkv3w8d1thY6jg0b8O0NFJE
|
||||
Content-Type: application/octet-stream; name="encrypted.asc"
|
||||
Content-Description: OpenPGP encrypted message
|
||||
Content-Disposition: inline; filename="encrypted.asc";
|
||||
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
REMOVED
|
||||
-----END PGP MESSAGE-----
|
||||
|
||||
|
||||
--OOGjcgxCkv3w8d1thY6jg0b8O0NFJE--
|
||||
|
||||
|
||||
--C06EAE01B0.1656207333/relay01.example.org--
|
||||
Reference in New Issue
Block a user