mirror of
https://github.com/chatmail/core.git
synced 2026-04-08 00:22:12 +03:00
Compare commits
12 Commits
fix1623
...
http-uploa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be4d91fcb3 | ||
|
|
1350495574 | ||
|
|
401a0fd37f | ||
|
|
a860fb15e5 | ||
|
|
311fffcfa4 | ||
|
|
7d2105dbc9 | ||
|
|
060492afe8 | ||
|
|
b0330f5c0a | ||
|
|
4da6177219 | ||
|
|
fa159cde3d | ||
|
|
194970a164 | ||
|
|
1208de7c92 |
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@@ -5,6 +5,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- staging
|
||||
- trying
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -18,12 +20,25 @@ jobs:
|
||||
profile: minimal
|
||||
toolchain: 1.43.1
|
||||
override: true
|
||||
- run: rustup component add rustfmt
|
||||
- run: rustup component add rustfmt
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
run_clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.43.1
|
||||
components: clippy
|
||||
override: true
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
build_and_test:
|
||||
name: Build and test
|
||||
@@ -60,6 +75,12 @@ jobs:
|
||||
path: target
|
||||
key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-build-target-${{ hashFiles('**/Cargo.toml') }}
|
||||
|
||||
- name: check
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --workspace --all --bins --examples --tests
|
||||
|
||||
- name: tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
|
||||
233
Cargo.lock
generated
233
Cargo.lock
generated
@@ -11,9 +11,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.1.0"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d"
|
||||
checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
@@ -187,7 +187,7 @@ checksum = "2a397614997df8e3a8b81ff8d882c0fdc100568d3162fadbd1b1410f95c40277"
|
||||
dependencies = [
|
||||
"async-native-tls",
|
||||
"async-std",
|
||||
"base64 0.12.2",
|
||||
"base64 0.12.1",
|
||||
"byte-pool",
|
||||
"chrono",
|
||||
"futures 0.3.5",
|
||||
@@ -215,17 +215,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-smtp"
|
||||
version = "0.3.2"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9720181a7d56bf3b4d0cfdcb6353df975125996bdd2958b4e639c8317fcaa68"
|
||||
checksum = "81598a96675097ba5df18eec536a0c1d669244aeb4170f2dfa5ef12e1b2ee4b1"
|
||||
dependencies = [
|
||||
"async-native-tls",
|
||||
"async-std",
|
||||
"async-trait",
|
||||
"base64 0.12.2",
|
||||
"base64 0.12.1",
|
||||
"bufstream",
|
||||
"fast_chemail",
|
||||
"hostname 0.1.5",
|
||||
"hostname",
|
||||
"log",
|
||||
"nom 5.1.2",
|
||||
"pin-project",
|
||||
@@ -261,18 +261,6 @@ dependencies = [
|
||||
"wasm-bindgen-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-std-resolver"
|
||||
version = "0.19.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d113439234775ae3e43d4e7589c5cc64fa3565996ae82dfe26b4ed78c02165be"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
"futures 0.3.5",
|
||||
"trust-dns-resolver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "3.0.0"
|
||||
@@ -315,14 +303,13 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.49"
|
||||
version = "0.3.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c"
|
||||
checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
@@ -350,9 +337,9 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.12.2"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67"
|
||||
checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
@@ -646,7 +633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca761767cf3fa9068cc893ec8c247a22d0fd0535848e65640c0548bd1f8bbb36"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"base64 0.12.2",
|
||||
"base64 0.12.1",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"percent-encoding",
|
||||
@@ -822,10 +809,9 @@ dependencies = [
|
||||
"async-native-tls",
|
||||
"async-smtp",
|
||||
"async-std",
|
||||
"async-std-resolver",
|
||||
"async-trait",
|
||||
"backtrace",
|
||||
"base64 0.12.2",
|
||||
"base64 0.12.1",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"charset",
|
||||
@@ -840,7 +826,6 @@ dependencies = [
|
||||
"image-meta",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"kamadak-exif",
|
||||
"lazy_static",
|
||||
"lettre_email",
|
||||
"libc",
|
||||
@@ -849,6 +834,7 @@ dependencies = [
|
||||
"native-tls",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"open",
|
||||
"percent-encoding",
|
||||
"pgp",
|
||||
"pretty_assertions",
|
||||
@@ -1107,18 +1093,6 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca"
|
||||
|
||||
[[package]]
|
||||
name = "enum-as-inner"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc4bfcfacb61d231109d1d55202c1f33263319668b168843e02ad4652725ec9c"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
@@ -1415,9 +1389,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.14"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909"
|
||||
checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -1458,17 +1432,6 @@ dependencies = [
|
||||
"winutil",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hostname"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"match_cfg",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-client"
|
||||
version = "3.0.0"
|
||||
@@ -1606,6 +1569,15 @@ dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inflate"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iovec"
|
||||
version = "0.1.4"
|
||||
@@ -1615,18 +1587,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipconfig"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7"
|
||||
dependencies = [
|
||||
"socket2",
|
||||
"widestring",
|
||||
"winapi",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.8.2"
|
||||
@@ -1660,15 +1620,6 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kamadak-exif"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a5e66d5b5469321038611f7f0e845a48989e4fd54987b6e5bb4c8ae3adbace7"
|
||||
dependencies = [
|
||||
"mutate_once",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.0"
|
||||
@@ -1803,12 +1754,6 @@ dependencies = [
|
||||
"quoted_printable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "match_cfg"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.8"
|
||||
@@ -1872,12 +1817,6 @@ dependencies = [
|
||||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mutate_once"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b"
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.4"
|
||||
@@ -2024,9 +1963,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.20.0"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
|
||||
checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
@@ -2040,6 +1979,15 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "open"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c283bf0114efea9e42f1a60edea9859e8c47528eae09d01df4b29c1e489cc48"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.29"
|
||||
@@ -2140,7 +2088,7 @@ version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59698ea79df9bf77104aefd39cc3ec990cb9693fb59c3b0a70ddf2646fdffb4b"
|
||||
dependencies = [
|
||||
"base64 0.12.2",
|
||||
"base64 0.12.1",
|
||||
"once_cell",
|
||||
"regex",
|
||||
]
|
||||
@@ -2158,7 +2106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d3fd4e0b2b7a8195b22e196a947c9ceb0d0f722d060e38e0641f48f56e195b"
|
||||
dependencies = [
|
||||
"aes 0.4.0",
|
||||
"base64 0.12.2",
|
||||
"base64 0.12.1",
|
||||
"bitfield",
|
||||
"block-modes",
|
||||
"block-padding 0.2.0",
|
||||
@@ -2201,18 +2149,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.22"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17"
|
||||
checksum = "e75373ff9037d112bb19bc61333a06a159eaeb217660dcfbea7d88e1db823919"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "0.4.22"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7"
|
||||
checksum = "10b4b44893d3c370407a1d6a5cfde7c41ae0478e31c516c85f67eb3adc51be6d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2251,14 +2199,14 @@ checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.16.5"
|
||||
version = "0.16.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ccdd66f6fe4b2433b07e4728e9a013e43233120427046e93ceb709c3a439bf"
|
||||
checksum = "12faa637ed9ae3d3c881332e54b5ae2dba81cda9fc4bbce0faa1ba53abcead50"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"deflate",
|
||||
"miniz_oxide",
|
||||
"inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2307,9 +2255,9 @@ checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-nested"
|
||||
version = "0.1.6"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
|
||||
checksum = "0afe1bd463b9e9ed51d0e0f0b50b6b146aec855c56fd182bb242388710a9b6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
@@ -2525,9 +2473,9 @@ checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
@@ -2553,16 +2501,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "resolv-conf"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11834e137f3b14e309437a8276714eed3a80d1ef894869e510f2c0c0b98b9f4a"
|
||||
dependencies = [
|
||||
"hostname 0.3.1",
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ripemd160"
|
||||
version = "0.9.0"
|
||||
@@ -2777,18 +2715,18 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.112"
|
||||
version = "1.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "736aac72d1eafe8e5962d1d1c3d99b0df526015ba40915cb3c49d042e92ec243"
|
||||
checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.112"
|
||||
version = "1.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf0343ce212ac0d3d6afd9391ac8e9c9efe06b533c8d33f660f6390cc4093f57"
|
||||
checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2914,9 +2852,9 @@ checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
|
||||
|
||||
[[package]]
|
||||
name = "smol"
|
||||
version = "0.1.12"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afcf8beb4aa23cc616e3351e49585153b462637c8fec929163b54d88e522b3b0"
|
||||
checksum = "845f5e7db6b614a8f4f59e565c3035f0fab2f71c9537ff0119eddb60d866cae3"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"crossbeam-deque",
|
||||
@@ -3154,18 +3092,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.20"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
|
||||
checksum = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.20"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
|
||||
checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3249,44 +3187,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trust-dns-proto"
|
||||
version = "0.19.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdd7061ba6f4d4d9721afedffbfd403f20f39a4301fee1b70d6fcd09cca69f28"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"backtrace",
|
||||
"enum-as-inner",
|
||||
"futures 0.3.5",
|
||||
"idna",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trust-dns-resolver"
|
||||
version = "0.19.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f23cdfdc3d8300b3c50c9e84302d3bd6d860fb9529af84ace6cf9665f181b77"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"cfg-if",
|
||||
"futures 0.3.5",
|
||||
"ipconfig",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"lru-cache",
|
||||
"resolv-conf",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"trust-dns-proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try_from"
|
||||
version = "0.3.2"
|
||||
@@ -3539,12 +3439,6 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a763e303c0e0f23b0da40888724762e802a8ffefbc22de4127ef42493c2ea68c"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.8"
|
||||
@@ -3576,15 +3470,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winutil"
|
||||
version = "0.1.1"
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
# lto = true
|
||||
|
||||
[dependencies]
|
||||
deltachat_derive = { path = "./deltachat_derive" }
|
||||
@@ -33,7 +33,6 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
chrono = "0.4.6"
|
||||
indexmap = "1.3.0"
|
||||
kamadak-exif = "0.5"
|
||||
lazy_static = "1.4.0"
|
||||
regex = "1.1.6"
|
||||
rusqlite = { version = "0.23", features = ["bundled"] }
|
||||
@@ -59,12 +58,12 @@ thiserror = "1.0.14"
|
||||
anyhow = "1.0.28"
|
||||
async-trait = "0.1.31"
|
||||
url = "2.1.1"
|
||||
async-std-resolver = "0.19.5"
|
||||
|
||||
pretty_env_logger = { version = "0.4.0", optional = true }
|
||||
log = {version = "0.4.8", optional = true }
|
||||
rustyline = { version = "4.1.0", optional = true }
|
||||
ansi_term = { version = "0.12.1", optional = true }
|
||||
open = { version = "1.4.0", optional = true }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -95,7 +94,7 @@ required-features = ["repl"]
|
||||
[features]
|
||||
default = []
|
||||
internals = []
|
||||
repl = ["internals", "rustyline", "log", "pretty_env_logger", "ansi_term"]
|
||||
repl = ["internals", "rustyline", "log", "pretty_env_logger", "ansi_term", "open"]
|
||||
vendored = ["async-native-tls/vendored", "async-smtp/native-tls-vendored"]
|
||||
nightly = ["pgp/nightly"]
|
||||
|
||||
|
||||
@@ -307,9 +307,9 @@ char* dc_get_blobdir (const dc_context_t* context);
|
||||
* DC_MEDIA_QUALITY_WORSE (1)
|
||||
* allow worse images/videos/voice quality to gain smaller sizes,
|
||||
* suitable for providers or areas known to have a bad connection.
|
||||
* The library uses the `media_quality` setting to use different defaults
|
||||
* for recoding images sent with type DC_MSG_IMAGE.
|
||||
* If needed, recoding other file types is up to the UI.
|
||||
* In contrast to other options, the implementation of this option is currently up to the UIs;
|
||||
* this may change in future, however,
|
||||
* having the option in the core allows provider-specific-defaults already today.
|
||||
*
|
||||
* If you want to retrieve a value, use dc_get_config().
|
||||
*
|
||||
@@ -528,20 +528,9 @@ int dc_is_io_running(const dc_context_t* context);
|
||||
void dc_stop_io(dc_context_t* context);
|
||||
|
||||
/**
|
||||
* This function should be called when there is a hint
|
||||
* that the network is available again,
|
||||
* eg. as a response to system event reporting network availability.
|
||||
* The library will try to send pending messages out immediately.
|
||||
*
|
||||
* Moreover, to have a reliable state
|
||||
* when the app comes to foreground with network available,
|
||||
* it may be reasonable to call the function also at that moment.
|
||||
*
|
||||
* It is okay to call the function unconditionally when there is
|
||||
* network available, however, calling the function
|
||||
* _without_ having network may interfere with the backoff algorithm
|
||||
* and will led to let the jobs fail faster, with fewer retries
|
||||
* and may avoid messages being sent out.
|
||||
* This function can be called whenever there is a hint
|
||||
* that the network is available again.
|
||||
* The library will try to send pending messages out.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
@@ -765,15 +754,6 @@ uint32_t dc_prepare_msg (dc_context_t* context, uint32_t ch
|
||||
* dc_msg_unref(msg);
|
||||
* ~~~
|
||||
*
|
||||
* If you send images with the DC_MSG_IMAGE type,
|
||||
* they will be recoded to a reasonable size before sending, if possible
|
||||
* (cmp the dc_set_config()-option `media_quality`).
|
||||
* If that fails, is not possible, or the image is already small enough, the image is sent as original.
|
||||
* If you want images to be always sent as the original file, use the DC_MSG_FILE type.
|
||||
*
|
||||
* Videos and other file types are currently not recoded by the library,
|
||||
* with dc_prepare_msg(), however, you can do that from the UI.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object as returned from dc_context_new().
|
||||
* @param chat_id Chat ID to send the message to.
|
||||
@@ -2802,9 +2782,6 @@ int dc_msg_get_viewtype (const dc_msg_t* msg);
|
||||
* If a sent message changes to this state, you'll receive the event #DC_EVENT_MSG_DELIVERED.
|
||||
* - DC_STATE_OUT_MDN_RCVD (28) - Outgoing message read by the recipient (two checkmarks; this requires goodwill on the receiver's side)
|
||||
* If a sent message changes to this state, you'll receive the event #DC_EVENT_MSG_READ.
|
||||
* Also messages already read by some recipients
|
||||
* may get into the state DC_STATE_OUT_FAILED at a later point,
|
||||
* eg. when in a group, delivery fails for some recipients.
|
||||
*
|
||||
* If you just want to check if a message is sent or not, please use dc_msg_is_sent() which regards all states accordingly.
|
||||
*
|
||||
@@ -3186,6 +3163,54 @@ int dc_msg_is_setupmessage (const dc_msg_t* msg);
|
||||
char* dc_msg_get_setupcodebegin (const dc_msg_t* msg);
|
||||
|
||||
|
||||
/**
|
||||
* Check if the message is completely downloaded
|
||||
* or if some further action is needed.
|
||||
*
|
||||
* The function returns one of:
|
||||
* - @ref DC_DOWNLOAD_NO_URL - The message does not need any further download action
|
||||
* and should be rendered as usual.
|
||||
* - @ref DC_DOWNLOAD_AVAILABLE - There is additional content to download.
|
||||
* Tn addition to the usual message rendering,
|
||||
* the UI shall show a download button that starts dc_schedule_download()
|
||||
* - @ref DC_DOWNLOAD_IN_PROGRESS - Download was started with dc_schedule_download() and is still in progress.
|
||||
* On progress changes and if the download fails or succeeds,
|
||||
* the event @ref DC_EVENT_DOWNLOAD_PROGRESS will be emitted.
|
||||
* - @ref DC_DOWNLOAD_DONE - Download finished successfully
|
||||
* - @ref DC_DOWNLOAD_FAILURE - Download error, the user may start over calling dc_schedule_download() again.
|
||||
*
|
||||
* @memberof dc_msg_t
|
||||
* @param msg The message object.
|
||||
* @return One of the @ref DC_DOWNLOAD values
|
||||
*/
|
||||
int dc_msg_download_status(const dc_msg_t* msg);
|
||||
|
||||
|
||||
/**
|
||||
* Advices the core to start downloading a message.
|
||||
* This function is typically called when the user hits the "Download" button
|
||||
* that is shown by the UI in case dc_msg_download_status()
|
||||
* returns @ref DC_DOWNLOAD_AVAILABLE or @ref DC_DOWNLOAD_FAILURE.
|
||||
*
|
||||
* The UI may want to show a file selector and let the user chose a download location.
|
||||
* The file name in the file selector may be prefilled using dc_msg_get_filename().
|
||||
*
|
||||
* During the download, the progress, errors and success
|
||||
* are reported using @ref DC_EVENT_DOWNLOAD_PROGRESS.
|
||||
*
|
||||
* Once the @ref DC_EVENT_DOWNLOAD_PROGRESS reports success,
|
||||
* The file can be accessed as usual using dc_msg_get_file().
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
* @param path Path to the destination file.
|
||||
* You can specify NULL here to download
|
||||
* to a reasonable file name in the internal blob-directory.
|
||||
* @param msg_id Message-ID to download the content for.
|
||||
*/
|
||||
void dc_schedule_download(dc_context_t* context, int msg_id, const char* path);
|
||||
|
||||
|
||||
/**
|
||||
* Set the text of a message object.
|
||||
* This does not alter any information in the database; this may be done by dc_send_msg() later.
|
||||
@@ -4140,9 +4165,8 @@ void dc_event_unref(dc_event_t* event);
|
||||
|
||||
|
||||
/**
|
||||
* A single message could not be sent.
|
||||
* State changed from DC_STATE_OUT_PENDING, DC_STATE_OUT_DELIVERED or DC_STATE_OUT_MDN_RCVD
|
||||
* to DC_STATE_OUT_FAILED, see dc_msg_get_state().
|
||||
* A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to
|
||||
* DC_STATE_OUT_FAILED, see dc_msg_get_state().
|
||||
*
|
||||
* @param data1 (int) chat_id
|
||||
* @param data2 (int) msg_id
|
||||
@@ -4255,6 +4279,16 @@ void dc_event_unref(dc_event_t* event);
|
||||
*/
|
||||
#define DC_EVENT_SECUREJOIN_JOINER_PROGRESS 2061
|
||||
|
||||
|
||||
/**
|
||||
* Inform about the progress of a download started by dc_schedule_download().
|
||||
*
|
||||
* @param data1 (int) Message-ID the progress is reported for.
|
||||
* @param data2 (int) 0=error, 1-999=progress in permille, 1000=success and done
|
||||
*/
|
||||
#define DC_EVENT_DOWNLOAD_PROGRESS 2070
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -4394,6 +4428,29 @@ void dc_event_unref(dc_event_t* event);
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup DC_DOWNLOAD DC_DOWNLOAD
|
||||
*
|
||||
* These constants describe the download state of a message.
|
||||
* The download state can be retrieved using dc_msg_download_status()
|
||||
* and usually changes after calling dc_schedule_download().
|
||||
*
|
||||
* @addtogroup DC_DOWNLOAD
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define DC_DOWNLOAD_NO_URL 10 ///< Download not needed, see dc_msg_download_status() for details.
|
||||
#define DC_DOWNLOAD_AVAILABLE 20 ///< Download available, see dc_msg_download_status() for details.
|
||||
#define DC_DOWNLOAD_IN_PROGRESS 30 ///< Download in progress, see dc_msg_download_status() for details.
|
||||
#define DC_DOWNLOAD_DONE 40 ///< Download done, see dc_msg_download_status() for details.
|
||||
#define DC_DOWNLOAD_FAILURE 50 ///< Download failed, see dc_msg_download_status() for details.
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* TODO: Strings need some doumentation about used placeholders.
|
||||
*
|
||||
@@ -4452,9 +4509,7 @@ void dc_event_unref(dc_event_t* event);
|
||||
#define DC_STR_WELCOME_MESSAGE 71
|
||||
#define DC_STR_UNKNOWN_SENDER_FOR_CHAT 72
|
||||
#define DC_STR_SUBJECT_FOR_NEW_CONTACT 73
|
||||
#define DC_STR_FAILED_SENDING_TO 74
|
||||
|
||||
#define DC_STR_COUNT 74
|
||||
#define DC_STR_COUNT 73
|
||||
|
||||
/*
|
||||
* @}
|
||||
|
||||
@@ -370,6 +370,8 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
===========================Message commands==\n\
|
||||
listmsgs <query>\n\
|
||||
msginfo <msg-id>\n\
|
||||
openfile <msg-id>\n\
|
||||
download <msg-id>\n\
|
||||
listfresh\n\
|
||||
forward <msg-id> <chat-id>\n\
|
||||
markseen <msg-id>\n\
|
||||
@@ -890,6 +892,25 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
let res = message::get_msg_info(&context, id).await;
|
||||
println!("{}", res);
|
||||
}
|
||||
"openfile" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let id = MsgId::new(arg1.parse()?);
|
||||
let msg = Message::load_from_db(&context, id).await?;
|
||||
let filepath = msg.get_file(&context);
|
||||
ensure!(filepath.is_some(), "Message has no file.");
|
||||
let filepath = filepath.unwrap();
|
||||
open::that(filepath)?;
|
||||
}
|
||||
"download" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let id = MsgId::new(arg1.parse()?);
|
||||
let path = if !arg2.is_empty() {
|
||||
Some(arg2.into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
message::schedule_download(&context, id, path).await?;
|
||||
}
|
||||
"listfresh" => {
|
||||
let msglist = context.get_fresh_msgs().await;
|
||||
|
||||
|
||||
@@ -186,9 +186,11 @@ const CHAT_COMMANDS: [&str; 26] = [
|
||||
"unpin",
|
||||
"delchat",
|
||||
];
|
||||
const MESSAGE_COMMANDS: [&str; 8] = [
|
||||
const MESSAGE_COMMANDS: [&str; 10] = [
|
||||
"listmsgs",
|
||||
"msginfo",
|
||||
"openfile",
|
||||
"download",
|
||||
"listfresh",
|
||||
"forward",
|
||||
"markseen",
|
||||
|
||||
@@ -187,9 +187,7 @@ class DirectImap:
|
||||
""" (blocking) wait for next idle message from server. """
|
||||
assert self._idling
|
||||
self.account.log("imap-direct: calling idle_check")
|
||||
res = self.conn.idle_check(timeout=30)
|
||||
if len(res) == 0:
|
||||
raise TimeoutError
|
||||
res = self.conn.idle_check()
|
||||
if terminate:
|
||||
self.idle_done()
|
||||
return res
|
||||
|
||||
@@ -850,7 +850,6 @@ class TestOnlineAccount:
|
||||
|
||||
lp.sec("mark messages as seen on ac2, wait for changes on ac1")
|
||||
ac2.direct_imap.idle_start()
|
||||
ac1.direct_imap.idle_start()
|
||||
ac2.mark_seen_messages([msg2, msg4])
|
||||
ac2.direct_imap.idle_check(terminate=True)
|
||||
lp.step("1")
|
||||
@@ -859,8 +858,6 @@ class TestOnlineAccount:
|
||||
assert ev.data1 > const.DC_CHAT_ID_LAST_SPECIAL
|
||||
assert ev.data2 > const.DC_MSG_ID_LAST_SPECIAL
|
||||
lp.step("2")
|
||||
ac1.direct_imap.idle_wait_for_seen() # Check that ac1 marks the read receipt as read
|
||||
|
||||
assert msg1.is_out_mdn_received()
|
||||
assert msg3.is_out_mdn_received()
|
||||
|
||||
|
||||
27
src/blob.rs
27
src/blob.rs
@@ -14,7 +14,6 @@ use thiserror::Error;
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::error::Error;
|
||||
use crate::events::Event;
|
||||
use crate::message;
|
||||
|
||||
@@ -409,13 +408,7 @@ impl<'a> BlobObject<'a> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut img = img.thumbnail(img_wh, img_wh);
|
||||
match self.get_exif_orientation(context) {
|
||||
Ok(90) => img = img.rotate90(),
|
||||
Ok(180) => img = img.rotate180(),
|
||||
Ok(270) => img = img.rotate270(),
|
||||
_ => {}
|
||||
}
|
||||
let img = img.thumbnail(img_wh, img_wh);
|
||||
|
||||
img.save(&blob_abs).map_err(|err| BlobError::WriteFailure {
|
||||
blobdir: context.get_blobdir().to_path_buf(),
|
||||
@@ -425,24 +418,6 @@ impl<'a> BlobObject<'a> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_exif_orientation(&self, context: &Context) -> Result<i32, Error> {
|
||||
let file = std::fs::File::open(self.to_abs_path())?;
|
||||
let mut bufreader = std::io::BufReader::new(&file);
|
||||
let exifreader = exif::Reader::new();
|
||||
let exif = exifreader.read_from_container(&mut bufreader)?;
|
||||
if let Some(orientation) = exif.get_field(exif::Tag::Orientation, exif::In::PRIMARY) {
|
||||
// possible orientation values are described at http://sylvana.net/jpegcrop/exif_orientation.html
|
||||
// we only use rotation, in practise, flipping is not used.
|
||||
match orientation.value.get_uint(0) {
|
||||
Some(3) => return Ok(180),
|
||||
Some(6) => return Ok(90),
|
||||
Some(8) => return Ok(270),
|
||||
other => warn!(context, "exif orientation value ignored: {:?}", other),
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for BlobObject<'a> {
|
||||
|
||||
25
src/chat.rs
25
src/chat.rs
@@ -424,7 +424,7 @@ impl ChatId {
|
||||
async fn get_parent_mime_headers(self, context: &Context) -> Option<(String, String, String)> {
|
||||
let collect =
|
||||
|row: &rusqlite::Row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?));
|
||||
let (rfc724_mid, mime_in_reply_to, mime_references, error): (
|
||||
let (packed, rfc724_mid, mime_in_reply_to, mime_references): (
|
||||
String,
|
||||
String,
|
||||
String,
|
||||
@@ -432,14 +432,15 @@ impl ChatId {
|
||||
) = self
|
||||
.parent_query(
|
||||
context,
|
||||
"rfc724_mid, mime_in_reply_to, mime_references, error",
|
||||
"param, rfc724_mid, mime_in_reply_to, mime_references",
|
||||
collect,
|
||||
)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()?;
|
||||
|
||||
if !error.is_empty() {
|
||||
let param = packed.parse::<Params>().ok()?;
|
||||
if param.exists(Param::Error) {
|
||||
// Do not reply to error messages.
|
||||
//
|
||||
// An error message could be a group chat message that we failed to decrypt and
|
||||
@@ -453,13 +454,12 @@ impl ChatId {
|
||||
}
|
||||
|
||||
async fn parent_is_encrypted(self, context: &Context) -> Result<bool, Error> {
|
||||
let collect = |row: &rusqlite::Row| Ok((row.get(0)?, row.get(1)?));
|
||||
let res: Option<(String, String)> =
|
||||
self.parent_query(context, "param, error", collect).await?;
|
||||
let collect = |row: &rusqlite::Row| Ok(row.get(0)?);
|
||||
let packed: Option<String> = self.parent_query(context, "param", collect).await?;
|
||||
|
||||
if let Some((ref packed, ref error)) = res {
|
||||
if let Some(ref packed) = packed {
|
||||
let param = packed.parse::<Params>()?;
|
||||
Ok(error.is_empty() && param.exists(Param::GuaranteeE2ee))
|
||||
Ok(!param.exists(Param::Error) && param.exists(Param::GuaranteeE2ee))
|
||||
} else {
|
||||
// No messages
|
||||
Ok(false)
|
||||
@@ -1464,7 +1464,7 @@ pub async fn send_msg(
|
||||
}
|
||||
}
|
||||
msg.param.remove(Param::PrepForwards);
|
||||
msg.update_param(context).await;
|
||||
msg.save_param_to_disk(context).await;
|
||||
}
|
||||
return send_msg_inner(context, chat_id, msg).await;
|
||||
}
|
||||
@@ -2596,7 +2596,7 @@ pub async fn forward_msgs(
|
||||
.set(Param::PrepForwards, new_msg_id.to_u32().to_string());
|
||||
}
|
||||
|
||||
msg.update_param(context).await;
|
||||
msg.save_param_to_disk(context).await;
|
||||
msg.param = save_param;
|
||||
} else {
|
||||
msg.state = MessageState::OutPending;
|
||||
@@ -2784,7 +2784,7 @@ pub(crate) async fn delete_and_reset_all_device_msgs(context: &Context) -> Resul
|
||||
pub(crate) async fn add_info_msg(context: &Context, chat_id: ChatId, text: impl AsRef<str>) {
|
||||
let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device");
|
||||
|
||||
if let Err(e) = context.sql.execute(
|
||||
if context.sql.execute(
|
||||
"INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,rfc724_mid) VALUES (?,?,?, ?,?,?, ?,?);",
|
||||
paramsv![
|
||||
chat_id,
|
||||
@@ -2796,8 +2796,7 @@ pub(crate) async fn add_info_msg(context: &Context, chat_id: ChatId, text: impl
|
||||
text.as_ref().to_string(),
|
||||
rfc724_mid,
|
||||
]
|
||||
).await {
|
||||
warn!(context, "Could not add info msg: {}", e);
|
||||
).await.is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -227,19 +227,6 @@ pub async fn dc_receive_imf(
|
||||
context
|
||||
.do_heuristics_moves(server_folder.as_ref(), insert_msg_id)
|
||||
.await;
|
||||
if !mime_parser.mdn_reports.is_empty() && mime_parser.has_chat_version() {
|
||||
// This is a Delta Chat MDN. Mark as read.
|
||||
job::add(
|
||||
context,
|
||||
job::Job::new(
|
||||
Action::MarkseenMsgOnImap,
|
||||
insert_msg_id.to_u32(),
|
||||
Params::new(),
|
||||
0,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +238,7 @@ pub async fn dc_receive_imf(
|
||||
cleanup(context, &create_event_to_send, created_db_entries);
|
||||
|
||||
mime_parser
|
||||
.handle_reports(context, from_id, sent_timestamp, &mime_parser.parts)
|
||||
.handle_reports(context, from_id, sent_timestamp)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
@@ -345,7 +332,7 @@ async fn add_parts(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut is_dc_message = if mime_parser.has_chat_version() {
|
||||
let mut msgrmsg = if mime_parser.has_chat_version() {
|
||||
MessengerMessage::Yes
|
||||
} else if is_reply_to_messenger_message(context, mime_parser).await {
|
||||
MessengerMessage::Reply
|
||||
@@ -357,7 +344,7 @@ async fn add_parts(
|
||||
let show_emails =
|
||||
ShowEmails::from_i32(context.get_config_int(Config::ShowEmails).await).unwrap_or_default();
|
||||
if mime_parser.is_system_message != SystemMessage::AutocryptSetupMessage
|
||||
&& is_dc_message == MessengerMessage::No
|
||||
&& msgrmsg == MessengerMessage::No
|
||||
{
|
||||
// this message is a classic email not a chat-message nor a reply to one
|
||||
match show_emails {
|
||||
@@ -386,7 +373,7 @@ async fn add_parts(
|
||||
|
||||
// handshake may mark contacts as verified and must be processed before chats are created
|
||||
if mime_parser.get(HeaderDef::SecureJoin).is_some() {
|
||||
is_dc_message = MessengerMessage::Yes; // avoid discarding by show_emails setting
|
||||
msgrmsg = MessengerMessage::Yes; // avoid discarding by show_emails setting
|
||||
*chat_id = ChatId::new(0);
|
||||
allow_creation = true;
|
||||
match handle_securejoin_handshake(context, mime_parser, from_id).await {
|
||||
@@ -418,14 +405,6 @@ async fn add_parts(
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
if chat_id.is_unset() && mime_parser.failure_report.is_some() {
|
||||
*chat_id = ChatId::new(DC_CHAT_ID_TRASH);
|
||||
info!(
|
||||
context,
|
||||
"Message belongs to an NDN and is not shown in a chat.",
|
||||
);
|
||||
}
|
||||
|
||||
// get the chat_id - a chat_id here is no indicator that the chat is displayed in the normal list,
|
||||
// it might also be blocked and displayed in the deaddrop as a result
|
||||
if chat_id.is_unset() {
|
||||
@@ -519,7 +498,7 @@ async fn add_parts(
|
||||
if Blocked::Not != chat_id_blocked
|
||||
&& state == MessageState::InFresh
|
||||
&& !incoming_origin.is_known()
|
||||
&& is_dc_message == MessengerMessage::No
|
||||
&& msgrmsg == MessengerMessage::No
|
||||
&& show_emails != ShowEmails::All
|
||||
{
|
||||
state = MessageState::InNoticed;
|
||||
@@ -534,7 +513,7 @@ async fn add_parts(
|
||||
|
||||
// handshake may mark contacts as verified and must be processed before chats are created
|
||||
if mime_parser.get(HeaderDef::SecureJoin).is_some() {
|
||||
is_dc_message = MessengerMessage::Yes; // avoid discarding by show_emails setting
|
||||
msgrmsg = MessengerMessage::Yes; // avoid discarding by show_emails setting
|
||||
*chat_id = ChatId::new(0);
|
||||
allow_creation = true;
|
||||
match observe_securejoin_on_other_device(context, mime_parser, to_id).await {
|
||||
@@ -573,7 +552,7 @@ async fn add_parts(
|
||||
}
|
||||
}
|
||||
if chat_id.is_unset() && allow_creation {
|
||||
let create_blocked = if MessengerMessage::No != is_dc_message
|
||||
let create_blocked = if MessengerMessage::No != msgrmsg
|
||||
&& !Contact::is_blocked_load(context, to_id).await
|
||||
{
|
||||
Blocked::Not
|
||||
@@ -658,7 +637,7 @@ async fn add_parts(
|
||||
let sent_timestamp = *sent_timestamp;
|
||||
let is_hidden = *hidden;
|
||||
let chat_id = *chat_id;
|
||||
let is_mdn = !mime_parser.mdn_reports.is_empty();
|
||||
let is_mdn = !mime_parser.reports.is_empty();
|
||||
|
||||
// TODO: can this clone be avoided?
|
||||
let rfc724_mid = rfc724_mid.to_string();
|
||||
@@ -675,8 +654,8 @@ async fn add_parts(
|
||||
"INSERT INTO msgs \
|
||||
(rfc724_mid, server_folder, server_uid, chat_id, from_id, to_id, timestamp, \
|
||||
timestamp_sent, timestamp_rcvd, type, state, msgrmsg, txt, txt_raw, param, \
|
||||
bytes, hidden, mime_headers, mime_in_reply_to, mime_references, error) \
|
||||
VALUES (?,?,?,?,?,?, ?,?,?,?,?,?, ?,?,?,?,?,?, ?,?, ?);",
|
||||
bytes, hidden, mime_headers, mime_in_reply_to, mime_references) \
|
||||
VALUES (?,?,?,?,?,?, ?,?,?,?,?,?, ?,?,?,?,?,?, ?,?);",
|
||||
)?;
|
||||
|
||||
let is_location_kml = location_kml_is
|
||||
@@ -710,7 +689,7 @@ async fn add_parts(
|
||||
rcvd_timestamp,
|
||||
part.typ,
|
||||
state,
|
||||
is_dc_message,
|
||||
msgrmsg,
|
||||
part.msg,
|
||||
// txt_raw might contain invalid utf8
|
||||
txt_raw,
|
||||
@@ -720,7 +699,6 @@ async fn add_parts(
|
||||
mime_headers,
|
||||
mime_in_reply_to,
|
||||
mime_references,
|
||||
part.error,
|
||||
])?;
|
||||
|
||||
drop(stmt);
|
||||
@@ -1789,7 +1767,7 @@ mod tests {
|
||||
use crate::chat::ChatVisibility;
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::message::Message;
|
||||
use crate::test_utils::*;
|
||||
use crate::test_utils::{configured_offline_context, dummy_context};
|
||||
|
||||
#[test]
|
||||
fn test_hex_hash() {
|
||||
@@ -2346,182 +2324,4 @@ mod tests {
|
||||
"Carl"
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_parse_ndn_tiscali() {
|
||||
test_parse_ndn(
|
||||
"alice@tiscali.it",
|
||||
"shenauithz@testrun.org",
|
||||
"Mr.un2NYERi1RM.lbQ5F9q-QyJ@tiscali.it",
|
||||
include_bytes!("../test-data/message/tiscali_ndn.eml"),
|
||||
"",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_parse_ndn_testrun() {
|
||||
test_parse_ndn(
|
||||
"alice@testrun.org",
|
||||
"hcksocnsofoejx@five.chat",
|
||||
"Mr.A7pTA5IgrUA.q4bP41vAJOp@testrun.org",
|
||||
include_bytes!("../test-data/message/testrun_ndn.eml"),
|
||||
"Undelivered Mail Returned to Sender – This is the mail system at host hq5.merlinux.eu.\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<hcksocnsofoejx@five.chat>: host mail.five.chat[195.62.125.103] said: 550 5.1.1\n <hcksocnsofoejx@five.chat>: Recipient address rejected: User unknown in\n virtual mailbox table (in reply to RCPT TO command)"
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_parse_ndn_yahoo() {
|
||||
test_parse_ndn(
|
||||
"alice@yahoo.com",
|
||||
"haeclirth.sinoenrat@yahoo.com",
|
||||
"1680295672.3657931.1591783872936@mail.yahoo.com",
|
||||
include_bytes!("../test-data/message/yahoo_ndn.eml"),
|
||||
"Failure Notice – Sorry, we were unable to deliver your message to the following address.\n\n<haeclirth.sinoenrat@yahoo.com>:\n554: delivery error: dd Not a valid recipient - atlas117.free.mail.ne1.yahoo.com"
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_parse_ndn_gmail() {
|
||||
test_parse_ndn(
|
||||
"alice@gmail.com",
|
||||
"assidhfaaspocwaeofi@gmail.com",
|
||||
"CABXKi8zruXJc_6e4Dr087H5wE7sLp+u250o0N2q5DdjF_r-8wg@mail.gmail.com",
|
||||
include_bytes!("../test-data/message/gmail_ndn.eml"),
|
||||
"Delivery Status Notification (Failure) – ** Die Adresse wurde nicht gefunden **\n\nIhre Nachricht wurde nicht an assidhfaaspocwaeofi@gmail.com zugestellt, weil die Adresse nicht gefunden wurde oder keine E-Mails empfangen kann.\n\nHier erfahren Sie mehr: https://support.google.com/mail/?p=NoSuchUser\n\nAntwort:\n\n550 5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient\'s email address for typos or unnecessary spaces. Learn more at https://support.google.com/mail/?p=NoSuchUser i18sor6261697wrs.38 - gsmtp",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_parse_ndn_gmx() {
|
||||
test_parse_ndn(
|
||||
"alice@gmx.com",
|
||||
"snaerituhaeirns@gmail.com",
|
||||
"9c9c2a32-056b-3592-c372-d7e8f0bd4bc2@gmx.de",
|
||||
include_bytes!("../test-data/message/gmx_ndn.eml"),
|
||||
"Mail delivery failed: returning message to sender – This message was created automatically by mail delivery software.\n\nA message that you sent could not be delivered to one or more of\nits recipients. This is a permanent error. The following address(es)\nfailed:\n\nsnaerituhaeirns@gmail.com:\nSMTP error from remote server for RCPT TO command, host: gmail-smtp-in.l.google.com (66.102.1.27) reason: 550-5.1.1 The email account that you tried to reach does not exist. Please\n try\n550-5.1.1 double-checking the recipient\'s email address for typos or\n550-5.1.1 unnecessary spaces. Learn more at\n550 5.1.1 https://support.google.com/mail/?p=NoSuchUser f6si2517766wmc.21\n9 - gsmtp"
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_parse_ndn_posteo() {
|
||||
test_parse_ndn(
|
||||
"alice@posteo.org",
|
||||
"hanerthaertidiuea@gmx.de",
|
||||
"04422840-f884-3e37-5778-8192fe22d8e1@posteo.de",
|
||||
include_bytes!("../test-data/message/posteo_ndn.eml"),
|
||||
"Undelivered Mail Returned to Sender – This is the mail system at host mout01.posteo.de.\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<hanerthaertidiuea@gmx.de>: host mx01.emig.gmx.net[212.227.17.5] said: 550\n Requested action not taken: mailbox unavailable (in reply to RCPT TO\n command)",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
// ndn = Non Delivery Notification
|
||||
async fn test_parse_ndn(
|
||||
self_addr: &str,
|
||||
foreign_addr: &str,
|
||||
rfc724_mid_outgoing: &str,
|
||||
raw_ndn: &[u8],
|
||||
error_msg: &str,
|
||||
) {
|
||||
let t = configured_offline_context_with_addr(self_addr).await;
|
||||
|
||||
dc_receive_imf(
|
||||
&t.ctx,
|
||||
format!(
|
||||
"From: {}\n\
|
||||
To: {}\n\
|
||||
Subject: foo\n\
|
||||
Message-ID: <{}>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
|
||||
\n\
|
||||
hello\n",
|
||||
self_addr, foreign_addr, rfc724_mid_outgoing
|
||||
)
|
||||
.as_bytes(),
|
||||
"INBOX",
|
||||
1,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
let msg_id = chats.get_msg_id(0).unwrap();
|
||||
|
||||
// Check that the ndn would be downloaded:
|
||||
let headers = mailparse::parse_mail(raw_ndn).unwrap().headers;
|
||||
assert!(
|
||||
crate::imap::prefetch_should_download(&t.ctx, &headers, ShowEmails::Off)
|
||||
.await
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
dc_receive_imf(&t.ctx, raw_ndn, "INBOX", 1, false)
|
||||
.await
|
||||
.unwrap();
|
||||
let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap();
|
||||
|
||||
assert_eq!(msg.state, MessageState::OutFailed);
|
||||
assert_eq!(msg.error, error_msg);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_parse_ndn_group_msg() {
|
||||
let t = configured_offline_context_with_addr("alice@gmail.com").await;
|
||||
|
||||
dc_receive_imf(
|
||||
&t.ctx,
|
||||
b"From: alice@gmail.com\n\
|
||||
To: bob@example.org, assidhfaaspocwaeofi@gmail.com\n\
|
||||
Subject: foo\n\
|
||||
Message-ID: <CADWx9Cs32Wa7Gy-gM0bvbq54P_FEHe7UcsAV=yW7sVVW=fiMYQ@mail.gmail.com>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Chat-Group-ID: abcde\n\
|
||||
Chat-Group-Name: foo\n\
|
||||
Chat-Disposition-Notification-To: alice@example.org\n\
|
||||
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
|
||||
\n\
|
||||
hello\n",
|
||||
"INBOX",
|
||||
1,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
let msg_id = chats.get_msg_id(0).unwrap();
|
||||
|
||||
let raw = include_bytes!("../test-data/message/gmail_ndn_group.eml");
|
||||
dc_receive_imf(&t.ctx, raw, "INBOX", 1, false)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap();
|
||||
|
||||
assert_eq!(msg.state, MessageState::OutFailed);
|
||||
|
||||
let msgs = chat::get_chat_msgs(&t.ctx, msg.chat_id, 0, None).await;
|
||||
let last_msg = Message::load_from_db(&t.ctx, *msgs.last().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
last_msg.text,
|
||||
Some(
|
||||
t.ctx
|
||||
.stock_string_repl_str(
|
||||
StockMessage::FailedSendingTo,
|
||||
"assidhfaaspocwaeofi@gmail.com",
|
||||
)
|
||||
.await,
|
||||
)
|
||||
);
|
||||
assert_eq!(last_msg.from_id, DC_CONTACT_ID_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ pub enum HeaderDef {
|
||||
References,
|
||||
InReplyTo,
|
||||
Precedence,
|
||||
ContentType,
|
||||
ChatVersion,
|
||||
ChatGroupId,
|
||||
ChatGroupName,
|
||||
@@ -35,6 +34,7 @@ pub enum HeaderDef {
|
||||
ChatContent,
|
||||
ChatDuration,
|
||||
ChatDispositionNotificationTo,
|
||||
ChatUploadUrl,
|
||||
Autocrypt,
|
||||
AutocryptSetupMessage,
|
||||
SecureJoin,
|
||||
|
||||
@@ -1449,7 +1449,7 @@ async fn prefetch_is_reply_to_chat_message(
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) async fn prefetch_should_download(
|
||||
async fn prefetch_should_download(
|
||||
context: &Context,
|
||||
headers: &[mailparse::MailHeader<'_>],
|
||||
show_emails: ShowEmails,
|
||||
@@ -1457,13 +1457,6 @@ pub(crate) async fn prefetch_should_download(
|
||||
let is_chat_message = headers.get_header_value(HeaderDef::ChatVersion).is_some();
|
||||
let is_reply_to_chat_message = prefetch_is_reply_to_chat_message(context, &headers).await;
|
||||
|
||||
let maybe_ndn = if let Some(from) = headers.get_header_value(HeaderDef::From_) {
|
||||
let from = from.to_ascii_lowercase();
|
||||
from.contains("mailer-daemon") || from.contains("mail-daemon")
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// Autocrypt Setup Message should be shown even if it is from non-chat client.
|
||||
let is_autocrypt_setup_message = headers
|
||||
.get_header_value(HeaderDef::AutocryptSetupMessage)
|
||||
@@ -1474,7 +1467,6 @@ pub(crate) async fn prefetch_should_download(
|
||||
let accepted_contact = origin.is_known();
|
||||
|
||||
let show = is_autocrypt_setup_message
|
||||
|| maybe_ndn
|
||||
|| match show_emails {
|
||||
ShowEmails::Off => is_chat_message || is_reply_to_chat_message,
|
||||
ShowEmails::AcceptedContacts => {
|
||||
|
||||
51
src/job.rs
51
src/job.rs
@@ -3,6 +3,7 @@
|
||||
//! This module implements a job queue maintained in the SQLite database
|
||||
//! and job types.
|
||||
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
|
||||
@@ -31,6 +32,7 @@ use crate::message::{self, Message, MessageState};
|
||||
use crate::mimefactory::MimeFactory;
|
||||
use crate::param::*;
|
||||
use crate::smtp::Smtp;
|
||||
use crate::upload::{download_message_file, generate_upload_url, upload_file};
|
||||
use crate::{scheduler::InterruptInfo, sql};
|
||||
|
||||
// results in ~3 weeks for the last backoff timespan
|
||||
@@ -107,6 +109,8 @@ pub enum Action {
|
||||
MaybeSendLocationsEnded = 5007,
|
||||
SendMdn = 5010,
|
||||
SendMsgToSmtp = 5901, // ... high priority
|
||||
|
||||
DownloadMessageFile = 7000,
|
||||
}
|
||||
|
||||
impl Default for Action {
|
||||
@@ -133,6 +137,9 @@ impl From<Action> for Thread {
|
||||
MaybeSendLocationsEnded => Thread::Smtp,
|
||||
SendMdn => Thread::Smtp,
|
||||
SendMsgToSmtp => Thread::Smtp,
|
||||
|
||||
// TODO: Where does downloading fit in the thread architecture?
|
||||
DownloadMessageFile => Thread::Imap,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,6 +337,14 @@ impl Job {
|
||||
}
|
||||
|
||||
pub(crate) async fn send_msg_to_smtp(&mut self, context: &Context, smtp: &mut Smtp) -> Status {
|
||||
// Upload file to HTTP if set in params.
|
||||
if let (Some(upload_url), Ok(Some(upload_path))) = (
|
||||
self.param.get_upload_url(),
|
||||
self.param.get_upload_path(context),
|
||||
) {
|
||||
job_try!(upload_file(context, upload_url.to_string(), upload_path).await);
|
||||
}
|
||||
|
||||
// SMTP server, if not yet done
|
||||
if !smtp.is_connected().await {
|
||||
let loginparam = LoginParam::from_database(context, "configured_").await;
|
||||
@@ -658,6 +673,13 @@ impl Job {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn download_message_file(&mut self, context: &Context) -> Status {
|
||||
let msg_id = MsgId::new(self.foreign_id);
|
||||
let download_path = job_try!(self.param.get_upload_path(context));
|
||||
job_try!(download_message_file(context, msg_id, download_path).await);
|
||||
Status::Finished(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete all pending jobs with the given action.
|
||||
@@ -726,7 +748,23 @@ pub async fn send_msg_job(context: &Context, msg_id: MsgId) -> Result<Option<Job
|
||||
}
|
||||
};
|
||||
|
||||
let mimefactory = MimeFactory::from_msg(context, &msg, attach_selfavatar).await?;
|
||||
let mut mimefactory = MimeFactory::from_msg(context, &msg, attach_selfavatar).await?;
|
||||
|
||||
// Prepare file upload if DCC_UPLOAD_URL env variable is set.
|
||||
// See upload-server folder for an example server impl.
|
||||
// Here a new URL is generated, which the mimefactory includes in the message instead of the
|
||||
// actual attachement. The upload then happens in the smtp send job.
|
||||
let upload = if let Some(file) = msg.get_file(context) {
|
||||
if let Ok(endpoint) = env::var("DCC_UPLOAD_URL") {
|
||||
let upload_url = generate_upload_url(context, endpoint);
|
||||
mimefactory.set_upload_url(upload_url.clone());
|
||||
Some((upload_url, file))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut recipients = mimefactory.recipients();
|
||||
|
||||
@@ -806,7 +844,7 @@ pub async fn send_msg_job(context: &Context, msg_id: MsgId) -> Result<Option<Job
|
||||
|
||||
if rendered_msg.is_encrypted && !needs_encryption {
|
||||
msg.param.set_int(Param::GuaranteeE2ee, 1);
|
||||
msg.update_param(context).await;
|
||||
msg.save_param_to_disk(context).await;
|
||||
}
|
||||
|
||||
ensure!(!recipients.is_empty(), "no recipients for smtp job set");
|
||||
@@ -818,6 +856,11 @@ pub async fn send_msg_job(context: &Context, msg_id: MsgId) -> Result<Option<Job
|
||||
param.set(Param::File, blob.as_name());
|
||||
param.set(Param::Recipients, &recipients);
|
||||
|
||||
if let Some((upload_url, upload_path)) = upload {
|
||||
param.set_upload_url(upload_url);
|
||||
param.set_upload_path(upload_path);
|
||||
}
|
||||
|
||||
let job = create(Action::SendMsgToSmtp, msg_id.to_u32() as i32, param, 0)?;
|
||||
|
||||
Ok(Some(job))
|
||||
@@ -978,6 +1021,7 @@ async fn perform_job_action(
|
||||
sql::housekeeping(context).await;
|
||||
Status::Finished(Ok(()))
|
||||
}
|
||||
Action::DownloadMessageFile => job.download_message_file(context).await,
|
||||
};
|
||||
|
||||
info!(
|
||||
@@ -1034,7 +1078,8 @@ pub async fn add(context: &Context, job: Job) {
|
||||
| Action::OldDeleteMsgOnImap
|
||||
| Action::DeleteMsgOnImap
|
||||
| Action::MarkseenMsgOnImap
|
||||
| Action::MoveMsg => {
|
||||
| Action::MoveMsg
|
||||
| Action::DownloadMessageFile => {
|
||||
info!(context, "interrupt: imap");
|
||||
context
|
||||
.interrupt_inbox(InterruptInfo::new(false, None))
|
||||
|
||||
@@ -67,6 +67,7 @@ mod simplify;
|
||||
mod smtp;
|
||||
pub mod stock;
|
||||
mod token;
|
||||
pub(crate) mod upload;
|
||||
#[macro_use]
|
||||
mod dehtml;
|
||||
|
||||
|
||||
148
src/message.rs
148
src/message.rs
@@ -14,7 +14,7 @@ use crate::error::{ensure, Error};
|
||||
use crate::events::Event;
|
||||
use crate::job::{self, Action};
|
||||
use crate::lot::{Lot, LotState, Meaning};
|
||||
use crate::mimeparser::{FailureReport, SystemMessage};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::param::*;
|
||||
use crate::pgp::*;
|
||||
use crate::stock::StockMessage;
|
||||
@@ -255,7 +255,6 @@ pub struct Message {
|
||||
pub(crate) starred: bool,
|
||||
pub(crate) chat_blocked: Blocked,
|
||||
pub(crate) location_id: u32,
|
||||
pub(crate) error: String,
|
||||
pub(crate) param: Params,
|
||||
}
|
||||
|
||||
@@ -290,7 +289,6 @@ impl Message {
|
||||
" m.timestamp_rcvd AS timestamp_rcvd,",
|
||||
" m.type AS type,",
|
||||
" m.state AS state,",
|
||||
" m.error AS error,",
|
||||
" m.msgrmsg AS msgrmsg,",
|
||||
" m.txt AS txt,",
|
||||
" m.param AS param,",
|
||||
@@ -318,7 +316,6 @@ impl Message {
|
||||
msg.timestamp_rcvd = row.get("timestamp_rcvd")?;
|
||||
msg.viewtype = row.get("type")?;
|
||||
msg.state = row.get("state")?;
|
||||
msg.error = row.get("error")?;
|
||||
msg.is_dc_message = row.get("msgrmsg")?;
|
||||
|
||||
let text;
|
||||
@@ -393,7 +390,7 @@ impl Message {
|
||||
}
|
||||
|
||||
if !self.id.is_unset() {
|
||||
self.update_param(context).await;
|
||||
self.save_param_to_disk(context).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -646,10 +643,10 @@ impl Message {
|
||||
if duration > 0 {
|
||||
self.param.set_int(Param::Duration, duration);
|
||||
}
|
||||
self.update_param(context).await;
|
||||
self.save_param_to_disk(context).await;
|
||||
}
|
||||
|
||||
pub async fn update_param(&mut self, context: &Context) -> bool {
|
||||
pub async fn save_param_to_disk(&mut self, context: &Context) -> bool {
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
@@ -766,10 +763,9 @@ impl From<MessageState> for LotState {
|
||||
impl MessageState {
|
||||
pub fn can_fail(self) -> bool {
|
||||
match self {
|
||||
MessageState::OutPreparing
|
||||
| MessageState::OutPending
|
||||
| MessageState::OutDelivered
|
||||
| MessageState::OutMdnRcvd => true, // OutMdnRcvd can still fail because it could be a group message and only some recipients failed.
|
||||
MessageState::OutPreparing | MessageState::OutPending | MessageState::OutDelivered => {
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -941,9 +937,8 @@ pub async fn get_msg_info(context: &Context, msg_id: MsgId) -> String {
|
||||
}
|
||||
|
||||
ret += "\n";
|
||||
|
||||
if !msg.error.is_empty() {
|
||||
ret += &format!("Error: {}", msg.error);
|
||||
if let Some(err) = msg.param.get(Param::Error) {
|
||||
ret += &format!("Error: {}", err)
|
||||
}
|
||||
|
||||
if let Some(path) = msg.get_file(context) {
|
||||
@@ -1256,38 +1251,33 @@ pub async fn exists(context: &Context, msg_id: MsgId) -> bool {
|
||||
|
||||
pub async fn set_msg_failed(context: &Context, msg_id: MsgId, error: Option<impl AsRef<str>>) {
|
||||
if let Ok(mut msg) = Message::load_from_db(context, msg_id).await {
|
||||
let error = error.map(|e| e.as_ref().to_string()).unwrap_or_default();
|
||||
if msg.state.can_fail() {
|
||||
msg.state = MessageState::OutFailed;
|
||||
warn!(context, "{} failed: {}", msg_id, error);
|
||||
} else {
|
||||
warn!(
|
||||
context,
|
||||
"{} seems to have failed ({}), but state is {}", msg_id, error, msg.state
|
||||
)
|
||||
}
|
||||
if let Some(error) = error {
|
||||
msg.param.set(Param::Error, error.as_ref());
|
||||
warn!(context, "Message failed: {}", error.as_ref());
|
||||
}
|
||||
|
||||
match context
|
||||
if context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE msgs SET state=?, error=? WHERE id=?;",
|
||||
paramsv![msg.state, error, msg_id],
|
||||
"UPDATE msgs SET state=?, param=? WHERE id=?;",
|
||||
paramsv![msg.state, msg.param.to_string(), msg_id],
|
||||
)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
Ok(_) => context.emit_event(Event::MsgFailed {
|
||||
context.emit_event(Event::MsgFailed {
|
||||
chat_id: msg.chat_id,
|
||||
msg_id,
|
||||
}),
|
||||
Err(e) => {
|
||||
warn!(context, "{:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// returns Some if an event should be send
|
||||
pub async fn handle_mdn(
|
||||
pub async fn mdn_from_ext(
|
||||
context: &Context,
|
||||
from_id: u32,
|
||||
rfc724_mid: &str,
|
||||
@@ -1328,10 +1318,10 @@ pub async fn handle_mdn(
|
||||
if let Ok((msg_id, chat_id, chat_type, msg_state)) = res {
|
||||
let mut read_by_all = false;
|
||||
|
||||
if msg_state == MessageState::OutPreparing
|
||||
|| msg_state == MessageState::OutPending
|
||||
|| msg_state == MessageState::OutDelivered
|
||||
{
|
||||
// if already marked as MDNS_RCVD msgstate_can_fail() returns false.
|
||||
// however, it is important, that ret_msg_id is set above as this
|
||||
// will allow the caller eg. to move the message away
|
||||
if msg_state.can_fail() {
|
||||
let mdn_already_in_table = context
|
||||
.sql
|
||||
.exists(
|
||||
@@ -1394,69 +1384,6 @@ pub async fn handle_mdn(
|
||||
None
|
||||
}
|
||||
|
||||
/// Marks a message as failed after an ndn (non-delivery-notification) arrived.
|
||||
/// 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,
|
||||
error: Option<impl AsRef<str>>,
|
||||
) {
|
||||
if failed.rfc724_mid.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let res = context
|
||||
.sql
|
||||
.query_row(
|
||||
concat!(
|
||||
"SELECT",
|
||||
" m.id AS msg_id,",
|
||||
" c.id AS chat_id,",
|
||||
" c.type AS type",
|
||||
" FROM msgs m LEFT JOIN chats c ON m.chat_id=c.id",
|
||||
" WHERE rfc724_mid=? AND from_id=1",
|
||||
),
|
||||
paramsv![failed.rfc724_mid],
|
||||
|row| {
|
||||
Ok((
|
||||
row.get::<_, MsgId>("msg_id")?,
|
||||
row.get::<_, ChatId>("chat_id")?,
|
||||
row.get::<_, Chattype>("type")?,
|
||||
))
|
||||
},
|
||||
)
|
||||
.await;
|
||||
if let Err(ref err) = res {
|
||||
info!(context, "Failed to select NDN {:?}", err);
|
||||
}
|
||||
|
||||
if let Ok((msg_id, chat_id, chat_type)) = res {
|
||||
set_msg_failed(context, msg_id, error).await;
|
||||
|
||||
if chat_type == Chattype::Group || chat_type == Chattype::VerifiedGroup {
|
||||
if let Some(failed_recipient) = &failed.failed_recipient {
|
||||
let contact_id =
|
||||
Contact::lookup_id_by_addr(context, failed_recipient, Origin::Unknown).await;
|
||||
if let Ok(contact) = Contact::load_from_db(context, contact_id).await {
|
||||
// Tell the user which of the recipients failed if we know that (because in a group, this might otherwise be unclear)
|
||||
chat::add_info_msg(
|
||||
context,
|
||||
chat_id,
|
||||
context
|
||||
.stock_string_repl_str(
|
||||
StockMessage::FailedSendingTo,
|
||||
contact.get_display_name(),
|
||||
)
|
||||
.await,
|
||||
)
|
||||
.await;
|
||||
context.emit_event(Event::ChatModified(chat_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The number of messages assigned to real chat (!=deaddrop, !=trash)
|
||||
pub async fn get_real_msg_cnt(context: &Context) -> i32 {
|
||||
match context
|
||||
@@ -1618,6 +1545,33 @@ pub async fn update_server_uid(
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedule attachement download for a message.
|
||||
pub async fn schedule_download(
|
||||
context: &Context,
|
||||
msg_id: MsgId,
|
||||
path: Option<PathBuf>,
|
||||
) -> Result<(), Error> {
|
||||
let msg = Message::load_from_db(context, msg_id).await?;
|
||||
if let Some(_upload_url) = msg.param.get_upload_url() {
|
||||
// TODO: Check if message was already downloaded.
|
||||
let mut params = Params::new();
|
||||
if let Some(path) = path {
|
||||
params.set_upload_path(path);
|
||||
}
|
||||
job::add(
|
||||
context,
|
||||
job::Job::new(Action::DownloadMessageFile, msg_id.to_u32(), params, 0),
|
||||
)
|
||||
.await;
|
||||
} else {
|
||||
warn!(
|
||||
context,
|
||||
"Tried to schedule download for message {} which has no uploads", msg_id
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn dc_empty_server(context: &Context, flags: u32) {
|
||||
job::kill_action(context, Action::EmptyServer).await;
|
||||
|
||||
@@ -50,6 +50,7 @@ pub struct MimeFactory<'a, 'b> {
|
||||
context: &'a Context,
|
||||
last_added_location_id: u32,
|
||||
attach_selfavatar: bool,
|
||||
upload_url: Option<String>,
|
||||
}
|
||||
|
||||
/// Result of rendering a message, ready to be submitted to a send job.
|
||||
@@ -159,6 +160,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
last_added_location_id: 0,
|
||||
attach_selfavatar,
|
||||
context,
|
||||
upload_url: None,
|
||||
};
|
||||
Ok(factory)
|
||||
}
|
||||
@@ -206,6 +208,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
req_mdn: false,
|
||||
last_added_location_id: 0,
|
||||
attach_selfavatar: false,
|
||||
upload_url: None,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
@@ -409,6 +412,10 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn set_upload_url(&mut self, upload_url: String) {
|
||||
self.upload_url = Some(upload_url)
|
||||
}
|
||||
|
||||
pub async fn render(mut self) -> Result<RenderedEmail, Error> {
|
||||
// Headers that are encrypted
|
||||
// - Chat-*, except Chat-Version
|
||||
@@ -879,11 +886,21 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
}
|
||||
};
|
||||
|
||||
// if upload url is present: add as header and to message text
|
||||
// TODO: make text part translatable (or remove)
|
||||
let upload_url_text = if let Some(ref upload_url) = self.upload_url {
|
||||
protected_headers.push(Header::new("Chat-Upload-Url".into(), upload_url.clone()));
|
||||
Some(format!("\n\nFile attachement: {}", upload_url.clone()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let footer = &self.selfstatus;
|
||||
let message_text = format!(
|
||||
"{}{}{}{}{}",
|
||||
"{}{}{}{}{}{}",
|
||||
fwdhint.unwrap_or_default(),
|
||||
escape_message_footer_marks(final_text),
|
||||
upload_url_text.unwrap_or_default(),
|
||||
if !final_text.is_empty() && !footer.is_empty() {
|
||||
"\r\n\r\n"
|
||||
} else {
|
||||
@@ -899,8 +916,8 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
.body(message_text);
|
||||
let mut parts = Vec::new();
|
||||
|
||||
// add attachment part
|
||||
if chat::msgtype_has_file(self.msg.viewtype) {
|
||||
// add attachment part, skip if upload url was provided
|
||||
if chat::msgtype_has_file(self.msg.viewtype) && self.upload_url.is_none() {
|
||||
if !is_file_size_okay(context, &self.msg).await {
|
||||
bail!(
|
||||
"Message exceeds the recommended {} MB.",
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use lazy_static::lazy_static;
|
||||
use lettre_email::mime::{self, Mime};
|
||||
use mailparse::{addrparse_header, DispositionType, MailHeader, MailHeaderMap, SingleInfo};
|
||||
|
||||
@@ -54,8 +53,7 @@ pub struct MimeMessage {
|
||||
pub message_kml: Option<location::Kml>,
|
||||
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) reports: Vec<Report>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -178,16 +176,14 @@ impl MimeMessage {
|
||||
signatures,
|
||||
gossipped_addr,
|
||||
is_forwarded: false,
|
||||
mdn_reports: Vec::new(),
|
||||
reports: Vec::new(),
|
||||
is_system_message: SystemMessage::Unknown,
|
||||
location_kml: None,
|
||||
message_kml: None,
|
||||
user_avatar: None,
|
||||
group_avatar: None,
|
||||
failure_report: None,
|
||||
};
|
||||
parser.parse_mime_recursive(context, &mail).await?;
|
||||
parser.heuristically_parse_ndn(context).await;
|
||||
parser.parse_headers(context)?;
|
||||
|
||||
Ok(parser)
|
||||
@@ -338,6 +334,13 @@ impl MimeMessage {
|
||||
}
|
||||
}
|
||||
|
||||
let upload_url = self.get(HeaderDef::ChatUploadUrl).map(|v| v.to_string());
|
||||
if let Some(upload_url) = upload_url {
|
||||
for part in self.parts.iter_mut() {
|
||||
part.param.set_upload_url(upload_url.clone());
|
||||
}
|
||||
}
|
||||
|
||||
self.parse_attachments();
|
||||
|
||||
// See if an MDN is requested from the other side
|
||||
@@ -357,7 +360,7 @@ impl MimeMessage {
|
||||
// just have send a message in the subject with an empty body.
|
||||
// Besides, we want to show something in case our incoming-processing
|
||||
// failed to properly handle an incoming message.
|
||||
if self.parts.is_empty() && self.mdn_reports.is_empty() {
|
||||
if self.parts.is_empty() && self.reports.is_empty() {
|
||||
let mut part = Part::default();
|
||||
part.typ = Viewtype::Text;
|
||||
|
||||
@@ -531,7 +534,7 @@ impl MimeMessage {
|
||||
part.typ = Viewtype::Text;
|
||||
part.msg_raw = Some(txt.clone());
|
||||
part.msg = txt;
|
||||
part.error = "Decryption failed".to_string();
|
||||
part.param.set(Param::Error, "Decryption failed");
|
||||
|
||||
self.parts.push(part);
|
||||
|
||||
@@ -554,10 +557,10 @@ impl MimeMessage {
|
||||
(mime::MULTIPART, "report") => {
|
||||
/* RFC 6522: the first part is for humans, the second for machines */
|
||||
if mail.subparts.len() >= 2 {
|
||||
match mail.ctype.params.get("report-type").map(|s| s as &str) {
|
||||
Some("disposition-notification") => {
|
||||
if let Some(report_type) = mail.ctype.params.get("report-type") {
|
||||
if report_type == "disposition-notification" {
|
||||
if let Some(report) = self.process_report(context, mail)? {
|
||||
self.mdn_reports.push(report);
|
||||
self.reports.push(report);
|
||||
}
|
||||
|
||||
// Add MDN part so we can track it, avoid
|
||||
@@ -569,21 +572,9 @@ impl MimeMessage {
|
||||
self.parts.push(part);
|
||||
|
||||
any_part_added = true;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Add all parts (we need another part, preferrably text/plain, to show as an error message)
|
||||
for cur_data in mail.subparts.iter() {
|
||||
if self.parse_mime_recursive(context, cur_data).await? {
|
||||
any_part_added = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
} else {
|
||||
/* eg. `report-type=delivery-status`;
|
||||
maybe we should show them as a little error icon */
|
||||
if let Some(first) = mail.subparts.iter().next() {
|
||||
any_part_added = self.parse_mime_recursive(context, first).await?;
|
||||
}
|
||||
@@ -856,121 +847,24 @@ impl MimeMessage {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn process_delivery_status(
|
||||
&self,
|
||||
context: &Context,
|
||||
report: &mailparse::ParsedMail<'_>,
|
||||
) -> Result<Option<FailureReport>> {
|
||||
// parse as mailheaders
|
||||
if let Some(original_msg) = report
|
||||
.subparts
|
||||
.iter()
|
||||
.find(|p| p.ctype.mimetype.contains("rfc822") || p.ctype.mimetype == "message/global")
|
||||
{
|
||||
let report_body = original_msg.get_body_raw()?;
|
||||
let (report_fields, _) = mailparse::parse_headers(&report_body)?;
|
||||
|
||||
if let Some(original_message_id) = report_fields
|
||||
.get_header_value(HeaderDef::MessageId)
|
||||
.and_then(|v| parse_message_id(&v).ok())
|
||||
{
|
||||
let mut to_list = get_all_addresses_from_header(&report.headers, |header_key| {
|
||||
header_key == "x-failed-recipients"
|
||||
});
|
||||
let to = if to_list.len() == 1 {
|
||||
Some(to_list.pop().unwrap())
|
||||
} else {
|
||||
None // We do not know which recipient failed
|
||||
};
|
||||
|
||||
return Ok(Some(FailureReport {
|
||||
rfc724_mid: original_message_id,
|
||||
failed_recipient: to.map(|s| s.addr),
|
||||
}));
|
||||
}
|
||||
|
||||
warn!(
|
||||
context,
|
||||
"ignoring unknown ndn-notification, Message-Id: {:?}",
|
||||
report_fields.get_header_value(HeaderDef::MessageId)
|
||||
);
|
||||
/// Handle reports (only MDNs for now)
|
||||
pub async fn handle_reports(&self, context: &Context, from_id: u32, sent_timestamp: i64) {
|
||||
if self.reports.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Some providers like GMX and Yahoo do not send standard NDNs (Non Delivery notifications).
|
||||
/// If you improve heuristics here you might also have to change prefetch_should_download() in imap/mod.rs.
|
||||
/// Also you should add a test in dc_receive_imf.rs (there already are lots of test_parse_ndn_* tests).
|
||||
async fn heuristically_parse_ndn(&mut self, context: &Context) -> Option<()> {
|
||||
if self
|
||||
.get(HeaderDef::Subject)?
|
||||
.to_ascii_lowercase()
|
||||
.contains("fail")
|
||||
&& self
|
||||
.get(HeaderDef::From_)?
|
||||
.to_ascii_lowercase()
|
||||
.contains("mailer-daemon")
|
||||
&& self.failure_report.is_none()
|
||||
{
|
||||
lazy_static! {
|
||||
static ref RE: regex::Regex = regex::Regex::new(r"Message-ID:(.*)").unwrap();
|
||||
}
|
||||
for captures in self
|
||||
.parts
|
||||
.iter()
|
||||
.filter_map(|part| part.msg_raw.as_ref())
|
||||
.flat_map(|part| part.lines())
|
||||
.filter_map(|line| RE.captures(line))
|
||||
{
|
||||
if let Ok(original_message_id) = parse_message_id(&captures[1]) {
|
||||
if let Ok(Some(_)) =
|
||||
message::rfc724_mid_exists(context, &original_message_id).await
|
||||
{
|
||||
self.failure_report = Some(FailureReport {
|
||||
rfc724_mid: original_message_id,
|
||||
failed_recipient: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None // Always return None, we just return anything so that we can use the '?' operator.
|
||||
}
|
||||
|
||||
/// Handle reports
|
||||
/// (MDNs = Message Disposition Notification, the message was read
|
||||
/// and NDNs = Non delivery notification, the message could not be delivered)
|
||||
pub async fn handle_reports(
|
||||
&self,
|
||||
context: &Context,
|
||||
from_id: u32,
|
||||
sent_timestamp: i64,
|
||||
parts: &[Part],
|
||||
) {
|
||||
for report in &self.mdn_reports {
|
||||
for report in &self.reports {
|
||||
for original_message_id in
|
||||
std::iter::once(&report.original_message_id).chain(&report.additional_message_ids)
|
||||
{
|
||||
if let Some((chat_id, msg_id)) =
|
||||
message::handle_mdn(context, from_id, original_message_id, sent_timestamp).await
|
||||
message::mdn_from_ext(context, from_id, original_message_id, sent_timestamp)
|
||||
.await
|
||||
{
|
||||
context.emit_event(Event::MsgRead { chat_id, msg_id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(failure_report) = &self.failure_report {
|
||||
let error = parts.iter().find(|p| p.typ == Viewtype::Text).map(|p| {
|
||||
let msg = &p.msg;
|
||||
match msg.find("\n--- ") {
|
||||
Some(footer_start) => &msg[..footer_start],
|
||||
None => msg,
|
||||
}
|
||||
.trim()
|
||||
});
|
||||
message::handle_ndn(context, failure_report, error).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1027,12 +921,6 @@ pub(crate) struct Report {
|
||||
additional_message_ids: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FailureReport {
|
||||
pub rfc724_mid: String,
|
||||
pub failed_recipient: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) fn parse_message_ids(ids: &str) -> Result<Vec<String>> {
|
||||
// take care with mailparse::msgidparse() that is pretty untolerant eg. wrt missing `<` or `>`
|
||||
let mut msgids = Vec::new();
|
||||
@@ -1076,7 +964,6 @@ pub struct Part {
|
||||
pub bytes: usize,
|
||||
pub param: Params,
|
||||
org_filename: Option<String>,
|
||||
pub error: String,
|
||||
}
|
||||
|
||||
/// return mimetype and viewtype for a parsed mail
|
||||
@@ -1523,7 +1410,7 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
);
|
||||
|
||||
assert_eq!(message.parts.len(), 1);
|
||||
assert_eq!(message.mdn_reports.len(), 1);
|
||||
assert_eq!(message.reports.len(), 1);
|
||||
}
|
||||
|
||||
/// Test parsing multiple MDNs combined in a single message.
|
||||
@@ -1603,7 +1490,7 @@ Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
);
|
||||
|
||||
assert_eq!(message.parts.len(), 2);
|
||||
assert_eq!(message.mdn_reports.len(), 2);
|
||||
assert_eq!(message.reports.len(), 2);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
@@ -1650,13 +1537,10 @@ Additional-Message-IDs: <foo@example.com> <foo@example.net>\n\
|
||||
);
|
||||
|
||||
assert_eq!(message.parts.len(), 1);
|
||||
assert_eq!(message.mdn_reports.len(), 1);
|
||||
assert_eq!(message.reports.len(), 1);
|
||||
assert_eq!(message.reports[0].original_message_id, "foo@example.org");
|
||||
assert_eq!(
|
||||
message.mdn_reports[0].original_message_id,
|
||||
"foo@example.org"
|
||||
);
|
||||
assert_eq!(
|
||||
&message.mdn_reports[0].additional_message_ids,
|
||||
&message.reports[0].additional_message_ids,
|
||||
&["foo@example.com", "foo@example.net"]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
//! OAuth 2 module
|
||||
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use async_std_resolver::{config, resolver};
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -17,7 +15,6 @@ const OAUTH2_GMAIL: Oauth2 = Oauth2 {
|
||||
init_token: "https://accounts.google.com/o/oauth2/token?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&code=$CODE&grant_type=authorization_code",
|
||||
refresh_token: "https://accounts.google.com/o/oauth2/token?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&refresh_token=$REFRESH_TOKEN&grant_type=refresh_token",
|
||||
get_userinfo: Some("https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=$ACCESS_TOKEN"),
|
||||
mx_pattern: Some(r"^aspmx\.l\.google\.com\.$"),
|
||||
};
|
||||
|
||||
const OAUTH2_YANDEX: Oauth2 = Oauth2 {
|
||||
@@ -27,11 +24,8 @@ const OAUTH2_YANDEX: Oauth2 = Oauth2 {
|
||||
init_token: "https://oauth.yandex.com/token?grant_type=authorization_code&code=$CODE&client_id=$CLIENT_ID&client_secret=58b8c6e94cf44fbe952da8511955dacf",
|
||||
refresh_token: "https://oauth.yandex.com/token?grant_type=refresh_token&refresh_token=$REFRESH_TOKEN&client_id=$CLIENT_ID&client_secret=58b8c6e94cf44fbe952da8511955dacf",
|
||||
get_userinfo: None,
|
||||
mx_pattern: None,
|
||||
};
|
||||
|
||||
const OAUTH2_PROVIDERS: [Oauth2; 1] = [OAUTH2_GMAIL];
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct Oauth2 {
|
||||
client_id: &'static str,
|
||||
@@ -39,7 +33,6 @@ struct Oauth2 {
|
||||
init_token: &'static str,
|
||||
refresh_token: &'static str,
|
||||
get_userinfo: Option<&'static str>,
|
||||
mx_pattern: Option<&'static str>,
|
||||
}
|
||||
|
||||
/// OAuth 2 Access Token Response
|
||||
@@ -60,7 +53,7 @@ pub async fn dc_get_oauth2_url(
|
||||
addr: impl AsRef<str>,
|
||||
redirect_uri: impl AsRef<str>,
|
||||
) -> Option<String> {
|
||||
if let Some(oauth2) = Oauth2::from_address(addr).await {
|
||||
if let Some(oauth2) = Oauth2::from_address(addr) {
|
||||
if context
|
||||
.sql
|
||||
.set_raw_config(
|
||||
@@ -88,7 +81,7 @@ pub async fn dc_get_oauth2_access_token(
|
||||
code: impl AsRef<str>,
|
||||
regenerate: bool,
|
||||
) -> Option<String> {
|
||||
if let Some(oauth2) = Oauth2::from_address(addr).await {
|
||||
if let Some(oauth2) = Oauth2::from_address(addr) {
|
||||
let lock = context.oauth2_mutex.lock().await;
|
||||
|
||||
// read generated token
|
||||
@@ -246,7 +239,7 @@ pub async fn dc_get_oauth2_addr(
|
||||
addr: impl AsRef<str>,
|
||||
code: impl AsRef<str>,
|
||||
) -> Option<String> {
|
||||
let oauth2 = Oauth2::from_address(addr.as_ref()).await?;
|
||||
let oauth2 = Oauth2::from_address(addr.as_ref())?;
|
||||
oauth2.get_userinfo?;
|
||||
|
||||
if let Some(access_token) =
|
||||
@@ -270,62 +263,23 @@ pub async fn dc_get_oauth2_addr(
|
||||
}
|
||||
|
||||
impl Oauth2 {
|
||||
async fn from_address(addr: impl AsRef<str>) -> Option<Self> {
|
||||
fn from_address(addr: impl AsRef<str>) -> Option<Self> {
|
||||
let addr_normalized = normalize_addr(addr.as_ref());
|
||||
if let Some(domain) = addr_normalized
|
||||
.find('@')
|
||||
.map(|index| addr_normalized.split_at(index + 1).1)
|
||||
{
|
||||
if let Some(provider) = Oauth2::lookup_whitelist(domain) {
|
||||
Some(provider)
|
||||
} else {
|
||||
Oauth2::lookup_mx(domain).await
|
||||
match domain {
|
||||
"gmail.com" | "googlemail.com" => Some(OAUTH2_GMAIL),
|
||||
"yandex.com" | "yandex.by" | "yandex.kz" | "yandex.ru" | "yandex.ua" | "ya.ru"
|
||||
| "narod.ru" => Some(OAUTH2_YANDEX),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_whitelist(domain: impl AsRef<str>) -> Option<Self> {
|
||||
let domain = domain.as_ref();
|
||||
match domain {
|
||||
"gmail.com" | "googlemail.com" => Some(OAUTH2_GMAIL),
|
||||
"yandex.com" | "yandex.by" | "yandex.kz" | "yandex.ru" | "yandex.ua" | "ya.ru"
|
||||
| "narod.ru" => Some(OAUTH2_YANDEX),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
async fn lookup_mx(domain: impl AsRef<str>) -> Option<Self> {
|
||||
if let Ok(resolver) = resolver(
|
||||
config::ResolverConfig::default(),
|
||||
config::ResolverOpts::default(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
for provider in OAUTH2_PROVIDERS.iter() {
|
||||
if let Some(pattern) = provider.mx_pattern {
|
||||
let re = Regex::new(pattern).unwrap();
|
||||
|
||||
let mut fqdn: String = String::from(domain.as_ref());
|
||||
if !fqdn.ends_with('.') {
|
||||
fqdn.push_str(".");
|
||||
}
|
||||
|
||||
if let Ok(res) = resolver.mx_lookup(fqdn).await {
|
||||
for rr in res.iter() {
|
||||
if re.is_match(&rr.exchange().to_lowercase().to_utf8()) {
|
||||
return Some(provider.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
async fn get_addr(&self, context: &Context, access_token: impl AsRef<str>) -> Option<String> {
|
||||
let userinfo_url = self.get_userinfo.unwrap_or_else(|| "");
|
||||
let userinfo_url = replace_in_uri(&userinfo_url, "$ACCESS_TOKEN", access_token);
|
||||
@@ -408,34 +362,20 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_oauth_from_address() {
|
||||
#[test]
|
||||
fn test_oauth_from_address() {
|
||||
assert_eq!(Oauth2::from_address("hello@gmail.com"), Some(OAUTH2_GMAIL));
|
||||
assert_eq!(
|
||||
Oauth2::from_address("hello@gmail.com").await,
|
||||
Oauth2::from_address("hello@googlemail.com"),
|
||||
Some(OAUTH2_GMAIL)
|
||||
);
|
||||
assert_eq!(
|
||||
Oauth2::from_address("hello@googlemail.com").await,
|
||||
Some(OAUTH2_GMAIL)
|
||||
);
|
||||
assert_eq!(
|
||||
Oauth2::from_address("hello@yandex.com").await,
|
||||
Some(OAUTH2_YANDEX)
|
||||
);
|
||||
assert_eq!(
|
||||
Oauth2::from_address("hello@yandex.ru").await,
|
||||
Oauth2::from_address("hello@yandex.com"),
|
||||
Some(OAUTH2_YANDEX)
|
||||
);
|
||||
assert_eq!(Oauth2::from_address("hello@yandex.ru"), Some(OAUTH2_YANDEX));
|
||||
|
||||
assert_eq!(Oauth2::from_address("hello@web.de").await, None);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_oauth_from_mx() {
|
||||
assert_eq!(
|
||||
Oauth2::from_address("hello@google.com").await,
|
||||
Some(OAUTH2_GMAIL)
|
||||
);
|
||||
assert_eq!(Oauth2::from_address("hello@web.de"), None);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
|
||||
26
src/param.rs
26
src/param.rs
@@ -65,6 +65,9 @@ pub enum Param {
|
||||
/// For Messages
|
||||
Arg4 = b'H',
|
||||
|
||||
/// For Messages
|
||||
Error = b'L',
|
||||
|
||||
/// For Messages
|
||||
AttachGroupImage = b'A',
|
||||
|
||||
@@ -117,6 +120,12 @@ pub enum Param {
|
||||
|
||||
/// For MDN-sending job
|
||||
MsgId = b'I',
|
||||
|
||||
/// For messages that have a HTTP file upload instead of attachement
|
||||
UploadUrl = b'y',
|
||||
|
||||
/// For messages that have a HTTP file upload instead of attachement: Path to local file
|
||||
UploadPath = b'Y',
|
||||
}
|
||||
|
||||
/// Possible values for `Param::ForcePlaintext`.
|
||||
@@ -314,6 +323,23 @@ impl Params {
|
||||
Ok(Some(path))
|
||||
}
|
||||
|
||||
pub fn get_upload_url(&self) -> Option<&str> {
|
||||
self.get(Param::UploadUrl)
|
||||
}
|
||||
|
||||
pub fn get_upload_path(&self, context: &Context) -> Result<Option<PathBuf>, BlobError> {
|
||||
self.get_path(Param::UploadPath, context)
|
||||
}
|
||||
|
||||
pub fn set_upload_path(&mut self, path: PathBuf) {
|
||||
// TODO: Remove unwrap? May panic for invalid UTF8 in path.
|
||||
self.set(Param::UploadPath, path.to_str().unwrap());
|
||||
}
|
||||
|
||||
pub fn set_upload_url(&mut self, url: impl AsRef<str>) {
|
||||
self.set(Param::UploadUrl, url);
|
||||
}
|
||||
|
||||
pub fn get_msg_id(&self) -> Option<MsgId> {
|
||||
self.get(Param::MsgId)
|
||||
.and_then(|x| x.parse::<u32>().ok())
|
||||
|
||||
@@ -450,11 +450,14 @@ impl<'a> Peerstate<'a> {
|
||||
}
|
||||
|
||||
pub fn has_verified_key(&self, fingerprints: &HashSet<Fingerprint>) -> bool {
|
||||
if let Some(vkc) = &self.verified_key_fingerprint {
|
||||
fingerprints.contains(vkc) && self.verified_key.is_some()
|
||||
} else {
|
||||
false
|
||||
if self.verified_key.is_some() && self.verified_key_fingerprint.is_some() {
|
||||
let vkc = self.verified_key_fingerprint.as_ref().unwrap();
|
||||
if fingerprints.contains(vkc) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
97
src/pgp.rs
97
src/pgp.rs
@@ -10,6 +10,7 @@ use pgp::composed::{
|
||||
SignedPublicSubKey, SignedSecretKey, SubkeyParamsBuilder,
|
||||
};
|
||||
use pgp::crypto::{HashAlgorithm, SymmetricKeyAlgorithm};
|
||||
use pgp::ser::Serialize;
|
||||
use pgp::types::{
|
||||
CompressionAlgorithm, KeyTrait, Mpi, PublicKeyTrait, SecretKeyTrait, StringToKey,
|
||||
};
|
||||
@@ -272,14 +273,6 @@ pub async fn pk_encrypt(
|
||||
.await
|
||||
}
|
||||
|
||||
/// Decrypts the message with keys from the private key keyring.
|
||||
///
|
||||
/// Receiver private keys are provided in
|
||||
/// `private_keys_for_decryption`.
|
||||
///
|
||||
/// If `ret_signature_fingerprints` is not `None`, stores fingerprints
|
||||
/// of all keys from the `public_keys_for_validation` keyring that
|
||||
/// have valid signatures there.
|
||||
#[allow(clippy::implicit_hasher)]
|
||||
pub async fn pk_decrypt(
|
||||
ctext: Vec<u8>,
|
||||
@@ -298,45 +291,54 @@ pub async fn pk_decrypt(
|
||||
})
|
||||
.await?;
|
||||
|
||||
if let Some(msg) = msgs.into_iter().next() {
|
||||
// get_content() will decompress the message if needed,
|
||||
// but this avoids decompressing it again to check signatures
|
||||
let msg = msg.decompress()?;
|
||||
ensure!(!msgs.is_empty(), "No valid messages found");
|
||||
|
||||
let content = match msg.get_content()? {
|
||||
Some(content) => content,
|
||||
None => bail!("The decrypted message is empty"),
|
||||
};
|
||||
let content = match msgs[0].get_content()? {
|
||||
Some(content) => content,
|
||||
None => bail!("Decrypted message is empty"),
|
||||
};
|
||||
|
||||
if let Some(ret_signature_fingerprints) = ret_signature_fingerprints {
|
||||
if !public_keys_for_validation.is_empty() {
|
||||
let fingerprints = async_std::task::spawn_blocking(move || {
|
||||
let pkeys = public_keys_for_validation.keys();
|
||||
if let Some(ret_signature_fingerprints) = ret_signature_fingerprints {
|
||||
if !public_keys_for_validation.is_empty() {
|
||||
let fingerprints = async_std::task::spawn_blocking(move || {
|
||||
let dec_msg = &msgs[0];
|
||||
|
||||
let mut fingerprints: Vec<Fingerprint> = Vec::new();
|
||||
if let signed_msg @ pgp::composed::Message::Signed { .. } = msg {
|
||||
for pkey in pkeys {
|
||||
if signed_msg.verify(&pkey.primary_key).is_ok() {
|
||||
let fp = DcKey::fingerprint(pkey);
|
||||
fingerprints.push(fp);
|
||||
}
|
||||
}
|
||||
let pkeys = public_keys_for_validation.keys();
|
||||
|
||||
let mut fingerprints: Vec<Fingerprint> = Vec::new();
|
||||
for pkey in pkeys {
|
||||
if dec_msg.verify(&pkey.primary_key).is_ok() {
|
||||
let fp = DcKey::fingerprint(pkey);
|
||||
fingerprints.push(fp);
|
||||
}
|
||||
fingerprints
|
||||
})
|
||||
.await;
|
||||
}
|
||||
fingerprints
|
||||
})
|
||||
.await;
|
||||
|
||||
ret_signature_fingerprints.extend(fingerprints);
|
||||
}
|
||||
ret_signature_fingerprints.extend(fingerprints);
|
||||
}
|
||||
Ok(content)
|
||||
} else {
|
||||
bail!("No valid messages found");
|
||||
}
|
||||
|
||||
Ok(content)
|
||||
}
|
||||
|
||||
/// Symmetric encryption.
|
||||
/// Symmetric encryption with armored base64 text output.
|
||||
pub async fn symm_encrypt(passphrase: &str, plain: &[u8]) -> Result<String> {
|
||||
let message = symm_encrypt_to_message(passphrase, plain).await?;
|
||||
let encoded_msg = message.to_armored_string(None)?;
|
||||
Ok(encoded_msg)
|
||||
}
|
||||
|
||||
/// Symmetric encryption with binary output.
|
||||
pub async fn symm_encrypt_bytes(passphrase: &str, plain: &[u8]) -> Result<Vec<u8>> {
|
||||
let message = symm_encrypt_to_message(passphrase, plain).await?;
|
||||
let mut buf = Vec::new();
|
||||
message.to_writer(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
async fn symm_encrypt_to_message(passphrase: &str, plain: &[u8]) -> Result<Message> {
|
||||
let lit_msg = Message::new_literal_bytes("", plain);
|
||||
let passphrase = passphrase.to_string();
|
||||
|
||||
@@ -345,24 +347,33 @@ pub async fn symm_encrypt(passphrase: &str, plain: &[u8]) -> Result<String> {
|
||||
let s2k = StringToKey::new_default(&mut rng);
|
||||
let msg =
|
||||
lit_msg.encrypt_with_password(&mut rng, s2k, Default::default(), || passphrase)?;
|
||||
|
||||
let encoded_msg = msg.to_armored_string(None)?;
|
||||
|
||||
Ok(encoded_msg)
|
||||
Ok(msg)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Symmetric decryption.
|
||||
/// Symmetric decryption from armored text.
|
||||
pub async fn symm_decrypt<T: std::io::Read + std::io::Seek>(
|
||||
passphrase: &str,
|
||||
ctext: T,
|
||||
) -> Result<Vec<u8>> {
|
||||
let (enc_msg, _) = Message::from_armor_single(ctext)?;
|
||||
symm_decrypt_from_message(enc_msg, passphrase).await
|
||||
}
|
||||
|
||||
/// Symmetric decryption from bytes.
|
||||
pub async fn symm_decrypt_bytes<T: std::io::Read + std::io::Seek>(
|
||||
passphrase: &str,
|
||||
cbytes: T,
|
||||
) -> Result<Vec<u8>> {
|
||||
let enc_msg = Message::from_bytes(cbytes)?;
|
||||
symm_decrypt_from_message(enc_msg, passphrase).await
|
||||
}
|
||||
|
||||
async fn symm_decrypt_from_message(message: Message, passphrase: &str) -> Result<Vec<u8>> {
|
||||
let passphrase = passphrase.to_string();
|
||||
async_std::task::spawn_blocking(move || {
|
||||
let decryptor = enc_msg.decrypt_with_password(|| passphrase)?;
|
||||
let decryptor = message.decrypt_with_password(|| passphrase)?;
|
||||
|
||||
let msgs = decryptor.collect::<pgp::errors::Result<Vec<_>>>()?;
|
||||
ensure!(!msgs.is_empty(), "No valid messages found");
|
||||
|
||||
@@ -1241,15 +1241,6 @@ async fn open(
|
||||
.await?;
|
||||
sql.set_raw_config_int(context, "dbversion", 63).await?;
|
||||
}
|
||||
if dbversion < 64 {
|
||||
info!(context, "[migration] v64");
|
||||
sql.execute(
|
||||
"ALTER TABLE msgs ADD COLUMN error TEXT DEFAULT '';",
|
||||
paramsv![],
|
||||
)
|
||||
.await?;
|
||||
sql.set_raw_config_int(context, "dbversion", 64).await?;
|
||||
}
|
||||
|
||||
// (2) updates that require high-level objects
|
||||
// (the structure is complete now and all objects are usable)
|
||||
|
||||
@@ -182,9 +182,6 @@ pub enum StockMessage {
|
||||
|
||||
#[strum(props(fallback = "Message from %1$s"))]
|
||||
SubjectForNewContact = 73,
|
||||
|
||||
#[strum(props(fallback = "Failed to send message to %1$s."))]
|
||||
FailedSendingTo = 74,
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -41,14 +41,13 @@ pub(crate) async fn dummy_context() -> TestContext {
|
||||
}
|
||||
|
||||
pub(crate) async fn configured_offline_context() -> TestContext {
|
||||
configured_offline_context_with_addr("alice@example.org").await
|
||||
}
|
||||
|
||||
pub(crate) async fn configured_offline_context_with_addr(addr: &str) -> TestContext {
|
||||
let t = dummy_context().await;
|
||||
t.ctx.set_config(Config::Addr, Some(addr)).await.unwrap();
|
||||
t.ctx
|
||||
.set_config(Config::ConfiguredAddr, Some(addr))
|
||||
.set_config(Config::Addr, Some("alice@example.org"))
|
||||
.await
|
||||
.unwrap();
|
||||
t.ctx
|
||||
.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
|
||||
.await
|
||||
.unwrap();
|
||||
t.ctx
|
||||
|
||||
142
src/upload.rs
Normal file
142
src/upload.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use crate::blob::BlobObject;
|
||||
// use crate::constants::Viewtype;
|
||||
use crate::context::Context;
|
||||
use crate::error::{bail, format_err, Result};
|
||||
use crate::message::{Message, MsgId};
|
||||
use crate::pgp::{symm_decrypt_bytes, symm_encrypt_bytes};
|
||||
use async_std::fs;
|
||||
use async_std::path::PathBuf;
|
||||
use rand::Rng;
|
||||
use std::io::Cursor;
|
||||
use url::Url;
|
||||
|
||||
/// Upload file to a HTTP upload endpoint.
|
||||
pub async fn upload_file(
|
||||
context: &Context,
|
||||
url: impl AsRef<str>,
|
||||
filepath: PathBuf,
|
||||
) -> Result<String> {
|
||||
let (passphrase, url) = parse_upload_url(url)?;
|
||||
|
||||
let content = fs::read(filepath).await?;
|
||||
let encrypted = symm_encrypt_bytes(&passphrase, &content).await?;
|
||||
|
||||
// TODO: Use tokens for upload.
|
||||
info!(context, "uploading encrypted file to {}", &url);
|
||||
let response = surf::put(url).body_bytes(encrypted).await;
|
||||
if let Err(err) = response {
|
||||
bail!("Upload failed: {}", err);
|
||||
}
|
||||
let mut response = response.unwrap();
|
||||
match response.body_string().await {
|
||||
Ok(string) => Ok(string),
|
||||
Err(err) => bail!("Invalid response from upload: {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn download_message_file(
|
||||
context: &Context,
|
||||
msg_id: MsgId,
|
||||
download_path: Option<PathBuf>,
|
||||
) -> Result<()> {
|
||||
let mut message = Message::load_from_db(context, msg_id).await?;
|
||||
let upload_url = message
|
||||
.param
|
||||
.get_upload_url()
|
||||
.ok_or_else(|| format_err!("Message has no upload URL"))?;
|
||||
|
||||
let (passphrase, url) = parse_upload_url(upload_url)?;
|
||||
|
||||
let filename: String = url
|
||||
.path_segments()
|
||||
.ok_or_else(|| format_err!("Invalid upload URL"))?
|
||||
.last()
|
||||
.ok_or_else(|| format_err!("Invalid upload URL"))?
|
||||
.to_string();
|
||||
|
||||
let data = download_file(context, url, passphrase).await?;
|
||||
let saved_path = if let Some(download_path) = download_path {
|
||||
fs::write(&download_path, data).await?;
|
||||
download_path.to_string_lossy().to_string()
|
||||
} else {
|
||||
let blob = BlobObject::create(context, filename.clone(), &data)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
format_err!(
|
||||
"Could not add blob for file download {}, error {}",
|
||||
filename,
|
||||
err
|
||||
)
|
||||
})?;
|
||||
blob.as_name().to_string()
|
||||
};
|
||||
info!(context, "saved download to: {:?}", saved_path);
|
||||
|
||||
// TODO: Support getting the mime type.
|
||||
let filemime = None;
|
||||
|
||||
message.set_file(saved_path, filemime);
|
||||
message.save_param_to_disk(context).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Download and decrypt a file from a HTTP endpoint.
|
||||
pub async fn download_file(
|
||||
context: &Context,
|
||||
url: impl AsRef<str>,
|
||||
passphrase: String,
|
||||
) -> Result<Vec<u8>> {
|
||||
info!(context, "downloading file from {}", &url.as_ref());
|
||||
let response = surf::get(url).recv_bytes().await;
|
||||
if let Err(err) = response {
|
||||
bail!("Download failed: {}", err);
|
||||
}
|
||||
let bytes = response.unwrap();
|
||||
info!(context, "download complete, len: {}", bytes.len());
|
||||
let reader = Cursor::new(bytes);
|
||||
let decrypted = symm_decrypt_bytes(&passphrase, reader).await?;
|
||||
Ok(decrypted)
|
||||
}
|
||||
|
||||
/// Parse a URL from a string and take out the hash fragment.
|
||||
fn parse_upload_url(url: impl AsRef<str>) -> Result<(String, Url)> {
|
||||
let mut url = url::Url::parse(url.as_ref())?;
|
||||
let passphrase = url.fragment();
|
||||
if passphrase.is_none() {
|
||||
bail!("Missing passphrase for upload URL");
|
||||
}
|
||||
let passphrase = passphrase.unwrap().to_string();
|
||||
url.set_fragment(None);
|
||||
Ok((passphrase, url))
|
||||
}
|
||||
|
||||
/// Generate a random URL based on the provided endpoint.
|
||||
pub fn generate_upload_url(_context: &Context, mut endpoint: String) -> String {
|
||||
// equals at least 16 random bytes (base32 takes 160% of binary size).
|
||||
const FILENAME_LEN: usize = 26;
|
||||
// equals at least 32 random bytes.
|
||||
const PASSPHRASE_LEN: usize = 52;
|
||||
|
||||
if endpoint.ends_with('/') {
|
||||
endpoint.pop();
|
||||
}
|
||||
let passphrase = generate_token_string(PASSPHRASE_LEN);
|
||||
let filename = generate_token_string(FILENAME_LEN);
|
||||
format!("{}/{}#{}", endpoint, filename, passphrase)
|
||||
}
|
||||
|
||||
/// Generate a random string encoded in base32.
|
||||
/// Len is the desired string length of the result.
|
||||
/// TODO: There's likely better methods to create random tokens.
|
||||
pub fn generate_token_string(len: usize) -> String {
|
||||
const CROCKFORD_ALPHABET: &[u8] = b"0123456789abcdefghjkmnpqrstvwxyz";
|
||||
let mut rng = rand::thread_rng();
|
||||
let token: String = (0..len)
|
||||
.map(|_| {
|
||||
let idx = rng.gen_range(0, CROCKFORD_ALPHABET.len());
|
||||
CROCKFORD_ALPHABET[idx] as char
|
||||
})
|
||||
.collect();
|
||||
token
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
Delivered-To: alice@gmail.com
|
||||
Received: by 2002:a1c:b4d7:0:0:0:0:0 with SMTP id d206csp3026053wmf;
|
||||
Mon, 18 May 2020 09:23:25 -0700 (PDT)
|
||||
X-Received: by 2002:a5d:4651:: with SMTP id j17mr19532177wrs.50.1589819005555;
|
||||
Mon, 18 May 2020 09:23:25 -0700 (PDT)
|
||||
ARC-Seal: i=1; a=rsa-sha256; t=1589819005; cv=none;
|
||||
d=google.com; s=arc-20160816;
|
||||
b=IZbNnzzuYzTFuqfuZwpd3ehqpYYGpn31c8DsfGbQ8rpbS0OTTROkVYvihQl8Ne/8X/
|
||||
brEWsrcmaCh9WpFMzpI+cp/TY39uusnI6qdp5rcgrFmFgoANtwf3TBBj1+f7wBPn46BP
|
||||
dQOUsg/J8KVfvzVgvL1x4uyJ0m9QirDgJeJ/BvrswbTleRQK7oY3fIireUCDxj6r2lCB
|
||||
1Z0TKw1mgIb1LiFMZz8kvCNn3R4KSFnwS8rIju0hYwnsioNiExVQgumXL+RVkEZ9BMzf
|
||||
UdoWIAw3VW+MOZFTpfLCEfgIPtLg/gtE0Q1P+a3KKpi8dkPiV2n6DGMecy9lTLtdhCXt
|
||||
pnaA==
|
||||
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
|
||||
h=in-reply-to:references:subject:from:date:message-id:auto-submitted
|
||||
:to:dkim-signature;
|
||||
bh=5xjZvcHbEGbMY0K2QB+3U6tpm1L1LAVv5h1pd4YXDEE=;
|
||||
b=nNP0DktrSjdBaFfhhoDi2O9KVKM0iXE5ZgubQ0q0ff68Z6Ke7c8dDBXEsZoToI0s4Y
|
||||
w90KyJFpgMJLFmP3iVDRqCfohi2y1HGdWg5VXQPTvzM7+YozZRlbNNV9UsuyRY91CXrJ
|
||||
a2XREBgB+LPMGQivwcHtUMZfyNv/4uiwWivk+92ySNDhxqOiDt4R5Jak/7RkZMFwQpsE
|
||||
JGwk6asM6VqZlihkF24lKv3pPaob6feyX3wD5N0+Mqiy1kQTj2JkpQk6nkTmdf0gapZe
|
||||
fOhU1NkbNfbuS3U7m2gEUiyktE+MhV/MgAzgBhm9bgNt2gQLVWju8rHkPndfv1PDmEkC
|
||||
FsYQ==
|
||||
ARC-Authentication-Results: i=1; mx.google.com;
|
||||
dkim=pass header.i=@googlemail.com header.s=20161025 header.b=dPisws+O;
|
||||
spf=pass (google.com: best guess record for domain of postmaster@mail-sor-f69.google.com designates 209.85.220.69 as permitted sender) smtp.helo=mail-sor-f69.google.com;
|
||||
dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com
|
||||
Return-Path: <>
|
||||
Received: from mail-sor-f69.google.com (mail-sor-f69.google.com. [209.85.220.69])
|
||||
by mx.google.com with SMTPS id s18sor5584435wrb.25.2020.05.18.09.23.25
|
||||
for <alice@gmail.com>
|
||||
(Google Transport Security);
|
||||
Mon, 18 May 2020 09:23:25 -0700 (PDT)
|
||||
Received-SPF: pass (google.com: best guess record for domain of postmaster@mail-sor-f69.google.com designates 209.85.220.69 as permitted sender) client-ip=209.85.220.69;
|
||||
Authentication-Results: mx.google.com;
|
||||
dkim=pass header.i=@googlemail.com header.s=20161025 header.b=dPisws+O;
|
||||
spf=pass (google.com: best guess record for domain of postmaster@mail-sor-f69.google.com designates 209.85.220.69 as permitted sender) smtp.helo=mail-sor-f69.google.com;
|
||||
dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
|
||||
d=googlemail.com; s=20161025;
|
||||
h=to:auto-submitted:message-id:date:from:subject:references
|
||||
:in-reply-to;
|
||||
bh=5xjZvcHbEGbMY0K2QB+3U6tpm1L1LAVv5h1pd4YXDEE=;
|
||||
b=dPisws+OwGFyOy0a612XYZgvz5T71GcJRJtU068/Tce8vN/+ggIQtUsZnZtsphe71v
|
||||
2NvfP9ULxR4cXvomTvhrYAk19KdxN/S7SeyBbmXv3x/tg+DBVCmmPS/6RXrcl6Ms3Hkw
|
||||
uPFQ9S3KcvHe/2bcb5LSTA/stIP4tuxxAXvsX2j+MjPYPWKAl50jkSbWK98U0Q0U+MTl
|
||||
pKaaC9s9iEBafac8BFZCy4DfpumKlemNEyRa3cSV2hw+DYHKA5peModrK1A2tcsfstFF
|
||||
rZi8yF/D90RIFbE04DI2QCxB3trsChNF1aYF06aSzI//wsfM1+lb+uGPi0YVkw3n4HrX
|
||||
Xw4w==
|
||||
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
|
||||
d=1e100.net; s=20161025;
|
||||
h=x-gm-message-state:to:auto-submitted:message-id:date:from:subject
|
||||
:references:in-reply-to;
|
||||
bh=5xjZvcHbEGbMY0K2QB+3U6tpm1L1LAVv5h1pd4YXDEE=;
|
||||
b=A/NCOtgbpA7VzB1G7ZFo8TA2FfrjuqjGdwMrJr3yXe21FrBFwzssprJwOkynqoVLkK
|
||||
iJU7uMF/KTcQPDEmOLFThzFfe5GCx7eJtZPhwY+FbBlC5sq4I55/xaQLd0gOZ1BYXwMn
|
||||
2bk169d2aoukbaLbGSQZF3d9atd+/e48YzkRxpmUoLcrWk2LcHAeQIG7SgT9pfX5DKPr
|
||||
VpxM5/GMVEBbTRhBIWCeVSfpYCs80l0xEeTC3/B5lzpzMVDE8QCW6Dwh75b4Tb2K6yru
|
||||
Zsy5ZpRmwv0wrkrb2vM+pl4IMkaF7s8XosIvlIT++fQV5xDFItT4atpykZvSDB92RKV0
|
||||
8lEA==
|
||||
X-Gm-Message-State: AOAM532RG/PT3ChZHBCDORGLtAjKvX8TGBuOy+AxrnEaJT6v1ieb+VV1
|
||||
+ejly+/6UthxHYlkOJYAszCSgL4dKVFotoVaN7LhEA==
|
||||
X-Google-Smtp-Source: ABdhPJz6veVKWhomCL4gK+whrybuMzHCDCq8AowgQvi7sobpMoM/k9CDw79jo1j3OUcTz6MEeUYLxEXuNIuu4zyoS7kVtsUYryGFHAI=
|
||||
X-Received: by 2002:a5d:5183:: with SMTP id k3mr20545185wrv.159.1589819005394;
|
||||
Mon, 18 May 2020 09:23:25 -0700 (PDT)
|
||||
Content-Type: multipart/report; boundary="00000000000012d63005a5ee9520"; report-type=delivery-status
|
||||
To: alice@gmail.com
|
||||
Received: by 2002:a5d:5183:: with SMTP id k3mr13704211wrv.159; Mon, 18 May
|
||||
2020 09:23:25 -0700 (PDT)
|
||||
Return-Path: <>
|
||||
Auto-Submitted: auto-replied
|
||||
Message-ID: <5ec2b67d.1c69fb81.213af.67a5.GMR@mx.google.com>
|
||||
Date: Mon, 18 May 2020 09:23:25 -0700 (PDT)
|
||||
From: Mail Delivery Subsystem <mailer-daemon@googlemail.com>
|
||||
Subject: Delivery Status Notification (Failure)
|
||||
References: <CABXKi8zruXJc_6e4Dr087H5wE7sLp+u250o0N2q5DdjF_r-8wg@mail.gmail.com>
|
||||
In-Reply-To: <CABXKi8zruXJc_6e4Dr087H5wE7sLp+u250o0N2q5DdjF_r-8wg@mail.gmail.com>
|
||||
X-Failed-Recipients: assidhfaaspocwaeofi@gmail.com
|
||||
|
||||
--00000000000012d63005a5ee9520
|
||||
Content-Type: multipart/related; boundary="00000000000012dc0005a5ee952f"
|
||||
|
||||
--00000000000012dc0005a5ee952f
|
||||
Content-Type: multipart/alternative; boundary="00000000000012dc0705a5ee9530"
|
||||
|
||||
--00000000000012dc0705a5ee9530
|
||||
Content-Type: text/plain; charset="UTF-8"
|
||||
|
||||
|
||||
** Die Adresse wurde nicht gefunden **
|
||||
|
||||
Ihre Nachricht wurde nicht an assidhfaaspocwaeofi@gmail.com zugestellt, weil die Adresse nicht gefunden wurde oder keine E-Mails empfangen kann.
|
||||
|
||||
Hier erfahren Sie mehr: https://support.google.com/mail/?p=NoSuchUser
|
||||
|
||||
Antwort:
|
||||
|
||||
550 5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient's email address for typos or unnecessary spaces. Learn more at https://support.google.com/mail/?p=NoSuchUser i18sor6261697wrs.38 - gsmtp
|
||||
|
||||
--00000000000012dc0705a5ee9530
|
||||
Content-Type: text/html; charset="UTF-8"
|
||||
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
* {
|
||||
font-family:Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table cellpadding="0" cellspacing="0" class="email-wrapper" style="padding-top:32px;background-color:#ffffff;"><tbody>
|
||||
<tr><td>
|
||||
<table cellpadding=0 cellspacing=0><tbody>
|
||||
<tr><td style="max-width:560px;padding:24px 24px 32px;background-color:#fafafa;border:1px solid #e0e0e0;border-radius:2px">
|
||||
<img style="padding:0 24px 16px 0;float:left" width=72 height=72 alt="Fehlersymbol" src="cid:icon.png">
|
||||
<table style="min-width:272px;padding-top:8px"><tbody>
|
||||
<tr><td><h2 style="font-size:20px;color:#212121;font-weight:bold;margin:0">
|
||||
Die Adresse wurde nicht gefunden
|
||||
</h2></td></tr>
|
||||
<tr><td style="padding-top:20px;color:#757575;font-size:16px;font-weight:normal;text-align:left">
|
||||
Ihre Nachricht wurde nicht an <a style='color:#212121;text-decoration:none'><b>assidhfaaspocwaeofi@gmail.com</b></a> zugestellt, weil die Adresse nicht gefunden wurde oder keine E-Mails empfangen kann.
|
||||
</td></tr>
|
||||
<tr><td style="padding-top:24px;color:#4285F4;font-size:14px;font-weight:bold;text-align:left">
|
||||
<a style="text-decoration:none" href="https://support.google.com/mail/?p=NoSuchUser">WEITERE INFORMATIONEN</a>
|
||||
</td></tr>
|
||||
</tbody></table>
|
||||
</td></tr>
|
||||
</tbody></table>
|
||||
</td></tr>
|
||||
<tr style="border:none;background-color:#fff;font-size:12.8px;width:90%">
|
||||
<td align="left" style="padding:48px 10px">
|
||||
Antwort:<br/>
|
||||
<p style="font-family:monospace">
|
||||
550 5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient's email address for typos or unnecessary spaces. Learn more at https://support.google.com/mail/?p=NoSuchUser i18sor6261697wrs.38 - gsmtp
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
--00000000000012dc0705a5ee9530--
|
||||
--00000000000012dc0005a5ee952f
|
||||
Content-Type: image/png; name="icon.png"
|
||||
Content-Disposition: attachment; filename="icon.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-ID: <icon.png>
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAAJAAAACQCAYAAADnRuK4AAAACXBIWXMAABYlAAAWJQFJUiTwAAAA
|
||||
GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABTdJREFUeNrsnD9sFEcUh5+PRMqZ
|
||||
yA0SPhAUQAQFUkyTgiBASARo6QApqVIkfdxGFJFSgGhJAUIiBaQB0ZIOKVCkwUgURjIg2fxL4kS+
|
||||
YDvkbC/388bi8N16Z4/d7J/5PsniuD3fyePP772ZeTsDQRAYQL/UGAJAIEAgQCBAIAAEAgQCBAIE
|
||||
AkAgyJT3Mv+Eq7vYK8mTE+MDRCAghQECAeRQA5V2ZOpmg5vDx3NPzRbmGRMEcmTrEbNNB8zWfRD+
|
||||
f/Efs2e3zCZvMjaksBg27TfbcuSNPEKP9ZyuAQKtHX2O9ncNgWC57umMPKvRNb0GEKgnLoUyxTQC
|
||||
rcns0/6uIRAs8/hGf9cQCJZpTpjdO2f25/03z+mxntM1eLtsZAgiUtX4JcaBCAQIBAgECARQ8CJa
|
||||
G5jab4J4pm4WZmO3OALVh802fIwcLkyPkcKAGggAgQCBAIEAgQCBABAIEAjKA/1AnahhbO5FdOOY
|
||||
VsrrDbPBYcYKgf5D2wLaV3p+22xh1u17tO3S+DTcvxvagUDeivPgx/a/95J/73w7Sj26Hn4pKo2M
|
||||
ehuV/KyBJM6d0f7k6RKx/R63vvL2tmf/ItDdM2ZTP6f7nkp9Y2fDx1v9akmpIU+KSCLVUghUQfSL
|
||||
zVKeTklbLxGoctw/nzC5rw8L5KRNbkpnKq6pgSqEClzNnFzY+XnYWrt6VpVk1vbwWvg+RKCKMOUw
|
||||
Q1LEOXA+/MX3mpJvGDHb265xtnzmFoUK1HaKQGlMtePYM+q2KKjXuaS1NJYIEKgI8jhEgqHt4cqy
|
||||
Ky53j3hyHz2bqSLp2o2LbJ7MxKovkGqXteoWpaOk96O9/yF/dF7NwlS36AuIQIBA5celQK4PIxBE
|
||||
4LLzrtoLgaALdSy6CJRkWQCBPGLsTHznomZ9nszUECgJ2ml3WWHe+QVFNPSQx6UdZNtxr9pbEShN
|
||||
eTTz8mQXHoHSlke7+Z+c9m6VGoHSkEfs/trLW3wQKApN1V3lGfnGu2Z6BFoLtYCs3GWBPAiUCLVh
|
||||
/HoaeRCoT9R873KLM/IgUBfapnCpe5AHgXry4pf412ihEHkQqCdxd5VqrcezhUIESsJMTJ+Pdthp
|
||||
Z0WgyNlXXPHc2Mc4IVAELl2Gnh8mhUDvCkfbIVAkcbf/aOoO3fMKhqAD3frTa4quwpn0hUDOkQhI
|
||||
YYBAgECAQAAU0QlYObl+5Ug8NcprZkZxjUCxRPVA6zmtEXHCBykskrhjgHXN09PoEcgFl4M4H11j
|
||||
nBAoApcj6ZoPGScEAgTKApcDoTw5sgWB+sGlz1n90IBAPdE6j1o21PfcC11jLagL1oFWRyGlKU3p
|
||||
OxcSJQ7NZAjkhHp/uG2HFAYIBAgECASAQIBAgECAQAAIBOkxEARBtp9wdVfAMOfIifEBIhCQwgCB
|
||||
ABAI0oV2jhxZ+nfBatuPZfgBCy0Eqqo8c01b+uu51XZvzOgDWoHNTGR+pCwpLEd5svuAZXlO2uEr
|
||||
PyEQ8hRWHgRCHmqg0sjTnLalv6crJQ8C/U8stqNO0I4+VZOHFIY8COS1PGL2ybd5yUMKK7s8zYmL
|
||||
dujyd3n+nESgcsvzZd4/KwIhDwIhT35QA6UyE1qyxZnfvJMHgdKS549JC1qvvJOHFIY8CFR5eV5O
|
||||
XimqPAhUdHnmfx+zgxdOFXkoqIGKKs/cswnb/8Oeog8HEai48nxUhiFBIORBIOShBioskkbySCLk
|
||||
IQIhDwIhj28p7FApR6b1qlEbHGpkO/rr6215vi/zH1r2x7tApSGFAQIBAgECAQIBIBAgECAQIBBA
|
||||
LK8FGADCTxYrr+EVJgAAAABJRU5ErkJggg==
|
||||
--00000000000012dc0005a5ee952f--
|
||||
--00000000000012d63005a5ee9520
|
||||
Content-Type: message/delivery-status
|
||||
|
||||
Reporting-MTA: dns; googlemail.com
|
||||
Arrival-Date: Mon, 18 May 2020 09:23:25 -0700 (PDT)
|
||||
X-Original-Message-ID: <CABXKi8zruXJc_6e4Dr087H5wE7sLp+u250o0N2q5DdjF_r-8wg@mail.gmail.com>
|
||||
|
||||
Final-Recipient: rfc822; assidhfaaspocwaeofi@gmail.com
|
||||
Action: failed
|
||||
Status: 5.1.1
|
||||
Diagnostic-Code: smtp; 550-5.1.1 The email account that you tried to reach does not exist. Please try
|
||||
550-5.1.1 double-checking the recipient's email address for typos or
|
||||
550-5.1.1 unnecessary spaces. Learn more at
|
||||
550 5.1.1 https://support.google.com/mail/?p=NoSuchUser i18sor6261697wrs.38 - gsmtp
|
||||
Last-Attempt-Date: Mon, 18 May 2020 09:23:25 -0700 (PDT)
|
||||
|
||||
--00000000000012d63005a5ee9520
|
||||
Content-Type: message/rfc822
|
||||
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
|
||||
d=gmail.com; s=20161025;
|
||||
h=mime-version:from:date:message-id:subject:to;
|
||||
bh=gtlm3j0shCgZYOVxUt74zkQ69Zq+GTQeHeXLfMlrhlk=;
|
||||
b=a185ogBcMzF9whNVWvuyUoUunNZk3Vc1kEIFmPkX0IxLpAFcI+fOQajOSromGl7Oyi
|
||||
yecLwQevpww2Xd0XjZ3UkZvrI9m9koRmh0QeoHvgTRORiVwj08+PVc3N4F9bCO4w9i0J
|
||||
ir7SSsJqBCDovoIFSFDyNa64vs6Nxno0cH/DaPG7pVTdD+3jfB7nLXIsMQYeX+1eP6rB
|
||||
UhKxH82r7Mh9CI2PWDQpVtGj63AMUEyHgE9Ou08PWbbKjrQOasoG3Tw8tB1GoN1XYssM
|
||||
rxOTgWEoTiduZ35AUH6h+eChOn9OHuI3SPECcVob70Qndayia3dMKfHMO6sEx9J0Wpic
|
||||
29vg==
|
||||
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
|
||||
d=1e100.net; s=20161025;
|
||||
h=x-gm-message-state:mime-version:from:date:message-id:subject:to;
|
||||
bh=gtlm3j0shCgZYOVxUt74zkQ69Zq+GTQeHeXLfMlrhlk=;
|
||||
b=miGIfL5BgnkD3wQvS34RtGwRRoh+8gJT5sFFfdX/hVyG/dvjXfdwP4yyNWr8ox8iY2
|
||||
BLlahS4y4VGcbG1e2aYjurnWNytGu6utQcZax/uUngJ0bTOwXW1VaIiEZtqd6gTV+8d/
|
||||
rrfQ459+4vXqIoQf0+Oi/U6dWwgJvPPjjRiToWdF3vIJE8R1iTRdZbW4lkgxSADbmskg
|
||||
noT/gWGWblHtR6uuGuKGJ3bkhJKCBnjavKh0LlbWEeFBZfmVNPRvzEFWHjBDdu5wvSL5
|
||||
0QJ+Qn0Orfn5CJuN3xPfzT1S2rI2iYZx37KX9zyMnZEx0ilkTYqCtBPWkrXRYDSXcxYS
|
||||
Y1ag==
|
||||
X-Gm-Message-State: AOAM531vhwpXiK8M12286dOJx0Q5fBl9ZaH6BJKts93GoxvPv0xdryP0
|
||||
jg9wYmoP5MUHudsxAMCYDFsCUMVx2PEywyIsaQqklw==
|
||||
X-Google-Smtp-Source: ABdhPJxlVJtTODM3pZZSTbbpAAAQRU8XbmuosDF9fgQZmVwxGZSzRWl22o+moppVRU/r8xMAyf0r3+qXwEBe1vZfjZo=
|
||||
X-Received: by 2002:a5d:5183:: with SMTP id k3mr20545162wrv.159.1589819005034;
|
||||
Mon, 18 May 2020 09:23:25 -0700 (PDT)
|
||||
MIME-Version: 1.0
|
||||
From: <alice@gmail.com>
|
||||
Date: Mon, 18 May 2020 18:23:39 +0200
|
||||
Message-ID: <CABXKi8zruXJc_6e4Dr087H5wE7sLp+u250o0N2q5DdjF_r-8wg@mail.gmail.com>
|
||||
Subject: Kommt sowieso nicht an
|
||||
To: assidhfaaspocwaeofi@gmail.com
|
||||
Content-Type: multipart/alternative; boundary="0000000000000d652a05a5ee95df"
|
||||
|
||||
--0000000000000d652a05a5ee95df
|
||||
Content-Type: text/plain; charset="UTF-8"
|
||||
|
||||
Wollte nur was testen
|
||||
|
||||
--0000000000000d652a05a5ee95df
|
||||
Content-Type: text/html; charset="UTF-8"
|
||||
|
||||
<div dir="ltr">Wollte nur was testen<br></div>
|
||||
|
||||
--0000000000000d652a05a5ee95df--
|
||||
|
||||
--00000000000012d63005a5ee9520--
|
||||
@@ -1,242 +0,0 @@
|
||||
Delivered-To: alice@gmail.com
|
||||
Received: by 2002:a02:6629:0:0:0:0:0 with SMTP id k41csp368502jac;
|
||||
Wed, 10 Jun 2020 05:17:57 -0700 (PDT)
|
||||
X-Received: by 2002:a6b:1448:: with SMTP id 69mr2898530iou.83.1591791475733;
|
||||
Wed, 10 Jun 2020 05:17:55 -0700 (PDT)
|
||||
ARC-Seal: i=1; a=rsa-sha256; t=1591791475; cv=none;
|
||||
d=google.com; s=arc-20160816;
|
||||
b=a0vSKJPbMtGYFnuk1ye/gnnV00Zvva4OOJTMOyfm13xMJD0YAhzGVfa7Z+wn5sQ8dw
|
||||
VAxpmDHCkjp4jol0C1iutiq2Nl0qma819oFPuuoMLLatKQXHpo8Jt+sL3MnwNR7J5bZC
|
||||
1c6Fjk75EIsRWhJd1HCkm44A6UYHxqqsTnzQCaNiHbjsRsvbggxwlMGSrZ4silxqSDvo
|
||||
Pzd/YDLCvsnZNSNIjIckKAwtGmY6sXctZ+JnOTykXAyL32Milfwy1vRL9xm10Q14biTR
|
||||
+qaIQp4E6WE63g1WHvfAjs0Dru7DalUh4GGl/NAwqVhY1gVyRD5E9/nODyHAfxjvaxDD
|
||||
4sMw==
|
||||
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
|
||||
h=in-reply-to:references:subject:from:date:message-id:auto-submitted
|
||||
:to:dkim-signature;
|
||||
bh=XaR1H4XeD+InO7mULPJn53omDGmxN+KG6DbSxyyErPM=;
|
||||
b=OJbgbrktMKyczw25z/ib7lSdRX80PEK3Myh9fj4q6mDlXmPPv//Gv069znRQ4QbadM
|
||||
HUXZH0WLMZcGyqI6SvGL/noxQ1O8yP0FYJJKTkoX0Gk2hHzfaE3x1scOP/o2FMMQXIFm
|
||||
S4CgGBD6HHzBJYj/rSL3gzqLzx1Id/z5kTeDvH2cn8JJAcCE2q/nhjTyWUb87geoNlDJ
|
||||
A1HRrLHK/0JOyRjHfg2zZCqIvSi1xmpiHStMyL9mfVyrQs98tsPxaOkJHjLplFARoPlr
|
||||
mmmDvsFg7MPvFqkkANzz4JDHidnfKRULCgnrVj1yTU66UagUpQEGjZqz8/99YuU6nt1t
|
||||
81sQ==
|
||||
ARC-Authentication-Results: i=1; mx.google.com;
|
||||
dkim=pass header.i=@googlemail.com header.s=20161025 header.b=aO4aNy7C;
|
||||
spf=pass (google.com: best guess record for domain of postmaster@mail-sor-f69.google.com designates 209.85.220.69 as permitted sender) smtp.helo=mail-sor-f69.google.com;
|
||||
dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com
|
||||
Return-Path: <>
|
||||
Received: from mail-sor-f69.google.com (mail-sor-f69.google.com. [209.85.220.69])
|
||||
by mx.google.com with SMTPS id w14sor16686480iow.23.2020.06.10.05.17.55
|
||||
for <alice@gmail.com>
|
||||
(Google Transport Security);
|
||||
Wed, 10 Jun 2020 05:17:55 -0700 (PDT)
|
||||
Received-SPF: pass (google.com: best guess record for domain of postmaster@mail-sor-f69.google.com designates 209.85.220.69 as permitted sender) client-ip=209.85.220.69;
|
||||
Authentication-Results: mx.google.com;
|
||||
dkim=pass header.i=@googlemail.com header.s=20161025 header.b=aO4aNy7C;
|
||||
spf=pass (google.com: best guess record for domain of postmaster@mail-sor-f69.google.com designates 209.85.220.69 as permitted sender) smtp.helo=mail-sor-f69.google.com;
|
||||
dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
|
||||
d=googlemail.com; s=20161025;
|
||||
h=to:auto-submitted:message-id:date:from:subject:references
|
||||
:in-reply-to;
|
||||
bh=XaR1H4XeD+InO7mULPJn53omDGmxN+KG6DbSxyyErPM=;
|
||||
b=aO4aNy7CUOk9O4Jnsue/DvMFY6Ph0C34AbpoxJH+mLZpOmt/KYGCGYWgunZgamF15U
|
||||
Vm8JY5yLKGwkTz2m3abDnKNP4fpl6zeZ5fyk5LvXH2Jema0iocHai6pJZBoFGPnonNmd
|
||||
MscTf1sEltbOxwfOmM1BRHX34c1jW0+8Yd2+Nhg2DPvzuq1brOVin6bUV4VX5EeeuNqT
|
||||
ZTewjJVPmO/B5NQhdpG81FO5w4hKSQ/VzZXnap2thMf3gOmnaoR+tbsnOIAiklcLdJ7b
|
||||
57SKUwI041pwSmh9dffs0STl2GvMRSJyGCtBqMnzXgflqoGTcnPflWgR3LXHM/MIA0q8
|
||||
WqRQ==
|
||||
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
|
||||
d=1e100.net; s=20161025;
|
||||
h=x-gm-message-state:to:auto-submitted:message-id:date:from:subject
|
||||
:references:in-reply-to;
|
||||
bh=XaR1H4XeD+InO7mULPJn53omDGmxN+KG6DbSxyyErPM=;
|
||||
b=iORAzNvXegQ8oSp4RYb/S168muAiBox769seMk49kDBIvXwI+N8P4mUZq/zDi8DmQd
|
||||
+wlLzVzowQq6EofiSpjOJWT9IC/k8otk15PMGtgHE4BGSSeKn7L30d3ocQS93HzYnLmA
|
||||
VBlHBdFTKrsfKhe2+CQyCosTDGRpbkQLuRRyhxChEq8ltvaOHgbu1+eCeb9PsPuh6OxH
|
||||
kvTHJZeA9A+eLOl26pBmqGIWkr7FlYW0wI6YPoEs9WXX5LSFOQs6fm/9l366eIR7IFFI
|
||||
ihX5LrZl/Cf0lwwYX7fqIMgnHy1K+QnKuEb+dRQGqLbxdIEls9bXIF98iPQVkEWzgSZy
|
||||
ip8Q==
|
||||
X-Gm-Message-State: AOAM531ahfHE6oS9/nuni8pNf9bwC+DXAcaLV0owBwNCj9kcTPLCCNhX
|
||||
W1JNciK0ivEIVB4dgiyLE/5K7iKbEznQhqyG9Bi1QA==
|
||||
X-Google-Smtp-Source: ABdhPJygljUXswH0ycJyHmXVthi5IjlDvP8QdYlMdHUPKEtgIZeUk69Acti5LnswGhg63T9/L0PuGZGBM5XE5BsP0mMNNDRZyt+DgnE=
|
||||
X-Received: by 2002:a05:6638:101c:: with SMTP id r28mr2990163jab.84.1591791475516;
|
||||
Wed, 10 Jun 2020 05:17:55 -0700 (PDT)
|
||||
Content-Type: multipart/report; boundary="00000000000074432a05a7b9d512"; report-type=delivery-status
|
||||
To: alice@gmail.com
|
||||
Received: by 2002:a05:6638:101c:: with SMTP id r28mr3059870jab.84; Wed, 10 Jun
|
||||
2020 05:17:55 -0700 (PDT)
|
||||
Return-Path: <>
|
||||
Auto-Submitted: auto-replied
|
||||
Message-ID: <5ee0cf73.1c69fb81.6888.c2f4.GMR@mx.google.com>
|
||||
Date: Wed, 10 Jun 2020 05:17:55 -0700 (PDT)
|
||||
From: Mail Delivery Subsystem <mailer-daemon@googlemail.com>
|
||||
Subject: Delivery Status Notification (Failure)
|
||||
References: <CADWx9Cs32Wa7Gy-gM0bvbq54P_FEHe7UcsAV=yW7sVVW=fiMYQ@mail.gmail.com>
|
||||
In-Reply-To: <CADWx9Cs32Wa7Gy-gM0bvbq54P_FEHe7UcsAV=yW7sVVW=fiMYQ@mail.gmail.com>
|
||||
X-Failed-Recipients: assidhfaaspocwaeofi@gmail.com
|
||||
|
||||
--00000000000074432a05a7b9d512
|
||||
Content-Type: multipart/related; boundary="000000000000745e0805a7b9d51b"
|
||||
|
||||
--000000000000745e0805a7b9d51b
|
||||
Content-Type: multipart/alternative; boundary="000000000000745e1705a7b9d51c"
|
||||
|
||||
--000000000000745e1705a7b9d51c
|
||||
Content-Type: text/plain; charset="UTF-8"
|
||||
|
||||
|
||||
** Die Adresse wurde nicht gefunden **
|
||||
|
||||
Ihre Nachricht wurde nicht an assidhfaaspocwaeofi@gmail.com zugestellt, weil die Adresse nicht gefunden wurde oder keine E-Mails empfangen kann.
|
||||
|
||||
Hier erfahren Sie mehr: https://support.google.com/mail/?p=NoSuchUser
|
||||
|
||||
Antwort:
|
||||
|
||||
550 5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient's email address for typos or unnecessary spaces. Learn more at https://support.google.com/mail/?p=NoSuchUser h20sor9401601jar.6 - gsmtp
|
||||
|
||||
--000000000000745e1705a7b9d51c
|
||||
Content-Type: text/html; charset="UTF-8"
|
||||
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
* {
|
||||
font-family:Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table cellpadding="0" cellspacing="0" class="email-wrapper" style="padding-top:32px;background-color:#ffffff;"><tbody>
|
||||
<tr><td>
|
||||
<table cellpadding=0 cellspacing=0><tbody>
|
||||
<tr><td style="max-width:560px;padding:24px 24px 32px;background-color:#fafafa;border:1px solid #e0e0e0;border-radius:2px">
|
||||
<img style="padding:0 24px 16px 0;float:left" width=72 height=72 alt="Fehlersymbol" src="cid:icon.png">
|
||||
<table style="min-width:272px;padding-top:8px"><tbody>
|
||||
<tr><td><h2 style="font-size:20px;color:#212121;font-weight:bold;margin:0">
|
||||
Die Adresse wurde nicht gefunden
|
||||
</h2></td></tr>
|
||||
<tr><td style="padding-top:20px;color:#757575;font-size:16px;font-weight:normal;text-align:left">
|
||||
Ihre Nachricht wurde nicht an <a style='color:#212121;text-decoration:none'><b>assidhfaaspocwaeofi@gmail.com</b></a> zugestellt, weil die Adresse nicht gefunden wurde oder keine E-Mails empfangen kann.
|
||||
</td></tr>
|
||||
<tr><td style="padding-top:24px;color:#4285F4;font-size:14px;font-weight:bold;text-align:left">
|
||||
<a style="text-decoration:none" href="https://support.google.com/mail/?p=NoSuchUser">WEITERE INFORMATIONEN</a>
|
||||
</td></tr>
|
||||
</tbody></table>
|
||||
</td></tr>
|
||||
</tbody></table>
|
||||
</td></tr>
|
||||
<tr style="border:none;background-color:#fff;font-size:12.8px;width:90%">
|
||||
<td align="left" style="padding:48px 10px">
|
||||
Antwort:<br/>
|
||||
<p style="font-family:monospace">
|
||||
550 5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient's email address for typos or unnecessary spaces. Learn more at https://support.google.com/mail/?p=NoSuchUser h20sor9401601jar.6 - gsmtp
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
--000000000000745e1705a7b9d51c--
|
||||
--000000000000745e0805a7b9d51b
|
||||
Content-Type: image/png; name="icon.png"
|
||||
Content-Disposition: attachment; filename="icon.png"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-ID: <icon.png>
|
||||
|
||||
iVBORw0KGgoAAAANSUhEUgAAAJAAAACQCAYAAADnRuK4AAAACXBIWXMAABYlAAAWJQFJUiTwAAAA
|
||||
GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABTdJREFUeNrsnD9sFEcUh5+PRMqZ
|
||||
yA0SPhAUQAQFUkyTgiBASARo6QApqVIkfdxGFJFSgGhJAUIiBaQB0ZIOKVCkwUgURjIg2fxL4kS+
|
||||
YDvkbC/388bi8N16Z4/d7J/5PsniuD3fyePP772ZeTsDQRAYQL/UGAJAIEAgQCBAIAAEAgQCBAIE
|
||||
AkAgyJT3Mv+Eq7vYK8mTE+MDRCAghQECAeRQA5V2ZOpmg5vDx3NPzRbmGRMEcmTrEbNNB8zWfRD+
|
||||
f/Efs2e3zCZvMjaksBg27TfbcuSNPEKP9ZyuAQKtHX2O9ncNgWC57umMPKvRNb0GEKgnLoUyxTQC
|
||||
rcns0/6uIRAs8/hGf9cQCJZpTpjdO2f25/03z+mxntM1eLtsZAgiUtX4JcaBCAQIBAgECARQ8CJa
|
||||
G5jab4J4pm4WZmO3OALVh802fIwcLkyPkcKAGggAgQCBAIEAgQCBABAIEAjKA/1AnahhbO5FdOOY
|
||||
VsrrDbPBYcYKgf5D2wLaV3p+22xh1u17tO3S+DTcvxvagUDeivPgx/a/95J/73w7Sj26Hn4pKo2M
|
||||
ehuV/KyBJM6d0f7k6RKx/R63vvL2tmf/ItDdM2ZTP6f7nkp9Y2fDx1v9akmpIU+KSCLVUghUQfSL
|
||||
zVKeTklbLxGoctw/nzC5rw8L5KRNbkpnKq6pgSqEClzNnFzY+XnYWrt6VpVk1vbwWvg+RKCKMOUw
|
||||
Q1LEOXA+/MX3mpJvGDHb265xtnzmFoUK1HaKQGlMtePYM+q2KKjXuaS1NJYIEKgI8jhEgqHt4cqy
|
||||
Ky53j3hyHz2bqSLp2o2LbJ7MxKovkGqXteoWpaOk96O9/yF/dF7NwlS36AuIQIBA5celQK4PIxBE
|
||||
4LLzrtoLgaALdSy6CJRkWQCBPGLsTHznomZ9nszUECgJ2ml3WWHe+QVFNPSQx6UdZNtxr9pbEShN
|
||||
eTTz8mQXHoHSlke7+Z+c9m6VGoHSkEfs/trLW3wQKApN1V3lGfnGu2Z6BFoLtYCs3GWBPAiUCLVh
|
||||
/HoaeRCoT9R873KLM/IgUBfapnCpe5AHgXry4pf412ihEHkQqCdxd5VqrcezhUIESsJMTJ+Pdthp
|
||||
Z0WgyNlXXPHc2Mc4IVAELl2Gnh8mhUDvCkfbIVAkcbf/aOoO3fMKhqAD3frTa4quwpn0hUDOkQhI
|
||||
YYBAgECAQAAU0QlYObl+5Ug8NcprZkZxjUCxRPVA6zmtEXHCBykskrhjgHXN09PoEcgFl4M4H11j
|
||||
nBAoApcj6ZoPGScEAgTKApcDoTw5sgWB+sGlz1n90IBAPdE6j1o21PfcC11jLagL1oFWRyGlKU3p
|
||||
OxcSJQ7NZAjkhHp/uG2HFAYIBAgECASAQIBAgECAQAAIBOkxEARBtp9wdVfAMOfIifEBIhCQwgCB
|
||||
ABAI0oV2jhxZ+nfBatuPZfgBCy0Eqqo8c01b+uu51XZvzOgDWoHNTGR+pCwpLEd5svuAZXlO2uEr
|
||||
PyEQ8hRWHgRCHmqg0sjTnLalv6crJQ8C/U8stqNO0I4+VZOHFIY8COS1PGL2ybd5yUMKK7s8zYmL
|
||||
dujyd3n+nESgcsvzZd4/KwIhDwIhT35QA6UyE1qyxZnfvJMHgdKS549JC1qvvJOHFIY8CFR5eV5O
|
||||
XimqPAhUdHnmfx+zgxdOFXkoqIGKKs/cswnb/8Oeog8HEai48nxUhiFBIORBIOShBioskkbySCLk
|
||||
IQIhDwIhj28p7FApR6b1qlEbHGpkO/rr6215vi/zH1r2x7tApSGFAQIBAgECAQIBIBAgECAQIBBA
|
||||
LK8FGADCTxYrr+EVJgAAAABJRU5ErkJggg==
|
||||
--000000000000745e0805a7b9d51b--
|
||||
--00000000000074432a05a7b9d512
|
||||
Content-Type: message/delivery-status
|
||||
|
||||
Reporting-MTA: dns; googlemail.com
|
||||
Arrival-Date: Wed, 10 Jun 2020 05:17:55 -0700 (PDT)
|
||||
X-Original-Message-ID: <CADWx9Cs32Wa7Gy-gM0bvbq54P_FEHe7UcsAV=yW7sVVW=fiMYQ@mail.gmail.com>
|
||||
|
||||
Final-Recipient: rfc822; assidhfaaspocwaeofi@gmail.com
|
||||
Action: failed
|
||||
Status: 5.1.1
|
||||
Diagnostic-Code: smtp; 550-5.1.1 The email account that you tried to reach does not exist. Please try
|
||||
550-5.1.1 double-checking the recipient's email address for typos or
|
||||
550-5.1.1 unnecessary spaces. Learn more at
|
||||
550 5.1.1 https://support.google.com/mail/?p=NoSuchUser h20sor9401601jar.6 - gsmtp
|
||||
Last-Attempt-Date: Wed, 10 Jun 2020 05:17:55 -0700 (PDT)
|
||||
|
||||
--00000000000074432a05a7b9d512
|
||||
Content-Type: message/rfc822
|
||||
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
|
||||
d=gmail.com; s=20161025;
|
||||
h=mime-version:from:date:message-id:subject:to;
|
||||
bh=Y1ylbv3YC5frF/LtF2it4tQQ0OVZstDdWqivvggIOB0=;
|
||||
b=eyr60XbgOrgHoZFpRYzw9WQIR7aEBaYKWhiEcqdnugB+hn0W2KVcTkKiL2C6zSF+jh
|
||||
l+lM+dNZZTUcMqWx4kVgTVtqwUNea8OUqe+WLqx04ULwdKZn1okbKYovaiavCLKOKDnf
|
||||
ZP5mNz3Ka/ywpCGoq8rdgnXc7NunnkWeaBpYY/BWOmLU4WNXX8zS7etXXhQE4YPQEJT4
|
||||
Sh2o/YIIjDLncJFMyE+25n3tbd2mIoLt4sjaCHE5ibm9w7zojyHM+LDCQ37cM74FEAAa
|
||||
88KTn0gSnCFBCfojhfxOH78CpySHG3FFfTlpCefwP2A5J9MQlb6QdSVa9STYSx3IntJ4
|
||||
L7Tg==
|
||||
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
|
||||
d=1e100.net; s=20161025;
|
||||
h=x-gm-message-state:mime-version:from:date:message-id:subject:to;
|
||||
bh=Y1ylbv3YC5frF/LtF2it4tQQ0OVZstDdWqivvggIOB0=;
|
||||
b=pBL4/bKUDw5E2zo1uR2Tl69h2iTlMgIAcnzQgodPCbU4jZ9kH+F5H9rfbzXCjT06J7
|
||||
L72SYpdfgc5fOwM4GhRcdYnyK3wiXQ8ugpL19nbYt2iWo/vRF3GidawXXDGb2GUYpkzX
|
||||
1Mz531cy2/HOsmQbUQ7304KV+OUghtcg8eLNnFuhQch7n12Kk3yy3AOzjrLoktcdgIsy
|
||||
/HxBjyut0Au+A2t6si+PVwTHvC647a0BioeV0tUYLigzu3/jgP9Hb8eRZaXTX5VC6iZi
|
||||
9QMH/+rXp05IK7OpGWh22xDpeV8CDkQ2sLFaBhKxtJ+nYoerM64t8EJXBBsVQb18ojGz
|
||||
pW/A==
|
||||
X-Gm-Message-State: AOAM5330q6kn/TKataMNEVigNfNdr/xii/PQgHXzJyMbwLvsETlNfLoy
|
||||
1rM9JBIGrcHeEDRx4qhZfl5S4bircceU7c3i6Fyn2fRO
|
||||
X-Google-Smtp-Source: ABdhPJwysG+S90b/g+9mK7LgeHhmJTBowst6JMhL16+a0coTi7P1NVp9jjaNHJfhvhLodYG6eHIvWdbQGJnAP2brEzI=
|
||||
X-Received: by 2002:a05:6638:101c:: with SMTP id r28mr2990137jab.84.1591791475066;
|
||||
Wed, 10 Jun 2020 05:17:55 -0700 (PDT)
|
||||
MIME-Version: 1.0
|
||||
From: Deltachat Test <alice@gmail.com>
|
||||
Date: Wed, 10 Jun 2020 14:18:26 +0200
|
||||
Message-ID: <CADWx9Cs32Wa7Gy-gM0bvbq54P_FEHe7UcsAV=yW7sVVW=fiMYQ@mail.gmail.com>
|
||||
Subject: test
|
||||
To: bob@example.org, assidhfaaspocwaeofi@gmail.com
|
||||
Content-Type: multipart/alternative; boundary="0000000000006d8d7d05a7b9d5b3"
|
||||
|
||||
--0000000000006d8d7d05a7b9d5b3
|
||||
Content-Type: text/plain; charset="UTF-8"
|
||||
|
||||
test
|
||||
|
||||
--0000000000006d8d7d05a7b9d5b3
|
||||
Content-Type: text/html; charset="UTF-8"
|
||||
|
||||
<div dir="ltr">test<br></div>
|
||||
|
||||
--0000000000006d8d7d05a7b9d5b3--
|
||||
|
||||
--00000000000074432a05a7b9d512--
|
||||
@@ -1,113 +0,0 @@
|
||||
Return-Path: <>
|
||||
Received: from mout-bounce.gmx.net ([212.227.15.44]) by mx-ha.gmx.net
|
||||
(mxgmx101 [212.227.17.5]) with ESMTPS (Nemesis) id 1Mr97m-1jC6Y01o86-00oEqk
|
||||
for <alice@gmx.de>; Tue, 09 Jun 2020 14:35:30 +0200
|
||||
Received: from localhost by mout-bounce.gmx.net id 0LhiZF-1jDTj11ZoH-00msO3
|
||||
Tue, 09 Jun 2020 14:35:30 +0200
|
||||
Date: Tue, 09 Jun 2020 14:35:30 +0200
|
||||
From: "GMX Mailer Daemon" <mailer-daemon@gmx.de>
|
||||
To: alice@gmx.de
|
||||
Subject: Mail delivery failed: returning message to sender
|
||||
Auto-Submitted: auto-replied
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
X-UI-Out-Filterresults: unknown:0;V03:K0:O8yx6kuPaGQ=:0wIDPNXEr0wX2oNsLnXaWA
|
||||
==
|
||||
Envelope-To: <alice@gmx.de>
|
||||
X-GMX-Antispam: 0 (Mail was not recognized as spam); Detail=V3;
|
||||
X-Spam-Flag: NO
|
||||
X-UI-Filterresults: notjunk:1;V03:K0:QcE43EBhMmU=:IC5vvzi9jhPS/698Wuubzw1Q4N
|
||||
h87X9j9B3CBN0ZKXB67KepwyNHmh9pxmFIUOMimylv7UK9np+j3X55roOd0nX9BmaaZ3Twvqf
|
||||
UaSsxmyU+cNr6m3+oOb3udJBLe2pJEZDk1cOwACb5NXzYPSaIj4APfGCyvrzIx3FGkNuScNBb
|
||||
tCbbKUJ0GB/VmJLB34XfF6dNN+Iwv9IQ9Yrvw/VXv9vWKsi3qRGGUt3yRw5jUKhQlBY21Pnoq
|
||||
m0LqoMbAKfH1tKEQ/5TymH1ei50YKyWzZ89ISkQwkbYLaqN+6meGACpY18j43VCU9Fk4WQR7y
|
||||
3XvBYh2CO0CnCn+M9VsnasYag2sNrySe9nzyKfRTaxEg8qlJtl7kS4GX/FsxhHPavkqnU62Gl
|
||||
9V5TxIG7tmIR0Bf11sPzG/WGegoOHxrfz+qYR81llLMOHznpdDRKjsYDtO/rFBGZzYTiCZsrW
|
||||
dZPVXV25SVcrDGZOaop3JoCbglmXLcSLLhmfE5MzyJEGte3I+6EiZJNeIe8qN3wMDTsRtJL9S
|
||||
J6b2F/5/kTGVAWnXtNlf69BholCrxvjC4Snt3Xjc+7WIO8iw2c5YjmWy+4bAwd4uWll529hZd
|
||||
6pUYGwjFRnKleivCaJIt7DqbvbE7GZSbQH8fXm3zYqYTrrxiWdlykdoOA1OGbeM06RHJt3mJB
|
||||
osZPU8BZKt0OiBOW64vg6gyAsNC0f02EA7dvRWYgFYqlSogfWZQIOKDKibMVHpIaA0foXg4BG
|
||||
TEQDlsTIL0n2WC9WVqkMdm6xUXHgpArCrAsUhw3mEqPywEfJeBHn60tP2vQ9+pDIQAj5dQCDV
|
||||
y96qSiCX4p31HfrWwAXB9mHfl4OO/tPcKUGBclj2rZ/NMc4O+7yDedLWXQnRzQExfOJLBbBh3
|
||||
xgiNlWFHvDLn0pKG9EI1+3wJ7m2GF2jzDtbQTBv9z26DuAq5WbHZHupzeyfP7VCVXcKuB6sG1
|
||||
3+LWcdYtcXfqT58HwcvDLwowC4uJpiHfHwtVdiGMtHnmYLysp0V425g+vofQfNzBgR3d9JC15
|
||||
G+HS44o6x6Legm6KnHYH3k0KhR7fgcgswJv/S+I/ryppUhGb2jezVZIUzgvAplzIUDAWnrHdF
|
||||
KVqZ5wBJ0acShIfgMlsIxnBmcnIQ4R4jq3zAyj4XTFxVUFanU8ySiXubxV5PzJqj+GsVa2sjx
|
||||
9n/xQRJLwgMC4BYqzP6lEPwg/g5AneDAnl7ZlcQPC4SCMblC8N1KZyyIDTXPOI/o4lfdMYb9P
|
||||
7DmBp2S8aA2yuDe5XT20OmX3kVWeBOsBaAGvVFpIn7gwIDqnFh9WSMP6mkCwfChN3D1yLquYB
|
||||
KAODgRZV5lVNmK+eOjW8m2oiRxmfxrjXLtw5PEhn3RkiRN4HnoePJeoYC7SG4EUwg+wYPu3M6
|
||||
exP/YigoE6bjuBS5d0imUTRDMiwg469GyrFo1J1GkRVvj3lXSF4Nt11j6waqu1l0ReDYfU+QM
|
||||
EMPGLEh1vRChCaqz4L7YI5FlSAVXxfmst0JRyE4k5r9CToEbZuYlPQ+jbcvptwBSaqzMb/YfT
|
||||
cCrU2RWHUfmYIy8x8A/t8ScRbYPzs1lTK3yn1hYeXpw8Fgkip6DIIXAJwUUp+2SLcELICIo+p
|
||||
uumMN0P/OZHH3V/hZ0dPr9xsYi7gdd/vyRIRPUwiL1rSp2WJGi+w4atun8kQBgnbAznRObDh1
|
||||
4zzKhApX9jo5gtFN6640QDEI5KpsMuPoty4rj9OK163ntKWGR451n+5ZX2FilTlpZYlIPO9Hy
|
||||
SrjHzBog4texceR1OLh4pb/WFB0XFSjQchPAltXCYQFs82aDDk/A0nOPk=
|
||||
|
||||
This message was created automatically by mail delivery software.
|
||||
|
||||
A message that you sent could not be delivered to one or more of
|
||||
its recipients. This is a permanent error. The following address(es)
|
||||
failed:
|
||||
|
||||
snaerituhaeirns@gmail.com:
|
||||
SMTP error from remote server for RCPT TO command, host: gmail-smtp-in.l.google.com (66.102.1.27) reason: 550-5.1.1 The email account that you tried to reach does not exist. Please
|
||||
try
|
||||
550-5.1.1 double-checking the recipient's email address for typos or
|
||||
550-5.1.1 unnecessary spaces. Learn more at
|
||||
550 5.1.1 https://support.google.com/mail/?p=NoSuchUser f6si2517766wmc.21
|
||||
9 - gsmtp
|
||||
|
||||
|
||||
|
||||
--- The header of the original message is following. ---
|
||||
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net;
|
||||
s=badeba3b8450; t=1591706130;
|
||||
bh=g3zLYH4xKxcPrHOD18z9YfpQcnk/GaJedfustWU5uGs=;
|
||||
h=X-UI-Sender-Class:To:From:Subject:Date;
|
||||
b=NwY6W33mI1bAq6lpr6kbY+LD2hO9cDJBItTgY3NRIT94A6rKTVlSmhFM3AxYgFnj0
|
||||
Db0hncsNRDqcdtRoOo8Emcah5NJURvEQohG37lkug3GqneB4+FNTdYCeQbOKlZn6on
|
||||
pYYD/T9CmeL2HG3+8voeBjZIUenyXrF2WXG37hFY=
|
||||
X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c
|
||||
Received: from [192.168.178.30] ([84.57.126.154]) by mail.gmx.com (mrgmx005
|
||||
[212.227.17.190]) with ESMTPSA (Nemesis) id 1MKbkM-1jNoq60HKm-00KyL2 for
|
||||
<snaerituhaeirns@gmail.com>; Tue, 09 Jun 2020 14:35:30 +0200
|
||||
To: snaerituhaeirns@gmail.com
|
||||
From: Alice <alice@gmx.de>
|
||||
Subject: test
|
||||
Message-ID: <9c9c2a32-056b-3592-c372-d7e8f0bd4bc2@gmx.de>
|
||||
Date: Tue, 9 Jun 2020 14:36:10 +0200
|
||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101
|
||||
Thunderbird/68.8.1
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Language: de-DE
|
||||
X-Provags-ID: V03:K1:7awoptmynaF5MqxCAincdsa7zFCwQFNkFb6xRxigK06uEiGN00b
|
||||
Mv2wJU91CEd4mvCCWzrTtaWLZDLH8pjAWaT4+HvYIUbpwNx2jC6WHTppkYYRMVJnm4lG9pr
|
||||
SUx1OlIcp0kbsnl3mB9xYNFwm9jzpR9Kx8QEHwIbZiiSFBcH56498UGQi//kKXVMos8C14o
|
||||
I7cmwYmr8xB09DwLMKXfg==
|
||||
X-Spam-Flag: NO
|
||||
X-UI-Out-Filterresults: notjunk:1;V03:K0:NWQAUbhAkBc=:yAaolOnVCDWEZhgUwwvtEs
|
||||
wXbSJ/GfMvDRpCkYpFBvHXOTpGm6hjdjQ0vLK2hvu/Hz22UdlWbIdc1J2oO9S5U20mIdc+1bS
|
||||
TPSSpqPFc7ICPx4Wbvv2SEp9ZqH2q7ORC52UvUWfI6OjAJEPDNrXQFdUiZAa72hLj1NPeG6Qi
|
||||
4AbL2HwLfJ8s6TeOCm6TXRRuD+w1o/ASFOqQmoao2dFyZ2BaoAgOKPKxXYfwVGceuUygpchyS
|
||||
0d2bZYOXSLR+6rUYevjZAq1OCi9AIC6/wlkOe5yIRk4gJFMfPauaICsdnq3uZ9ikCAX83VWun
|
||||
PJVMxTLTP54lgo2h0jMBX3uKk10+/wzXWplllxX9NnSa3x1V28n6raslNF0IoC6Pm72kC6Jzr
|
||||
GkC22viCm3/Y4uHlPMOXbY5WFrQe/D9GKeJeXBLoGciNwIFkUG12a+iqWtoT+h5HVObTW8LxM
|
||||
+UtEl97nAwxYSM+sGfIpasRpZc7r/SgN3JWGO9R9WaXpW4Cc1dH7RI+hzuZDsDUBEGTUTVPDo
|
||||
0SvjKHiJ6sUqGDyfv4HUgVutus6EYP27LALND4ekfom2DPRFopZhbtV5fZT7CL1Q1NogU7tYf
|
||||
/FdmR1T1J1zCAZSFvyR5LBkfglZlHzgdnTF2heuxyqKq4dm0hnLFSULB1+CVWsg8hzrruvO5q
|
||||
XzA6qIhBQUZmWo7wBpqpkBPxzjgTGGtXc5y5e6+crxYbbuQdnUWEnyw2xI4d6pJPqtHDA2/vT
|
||||
ZgvNDUGceavTR5Rtyb14hhX4Q6dWK3ATy16j4hs9Aq+q/IKyVAX3A5nFYbJRIz+2YnoLr2YOa
|
||||
IrScEorXjvTxjw+aBy73SZBe2REPzJ+O6k7chVrYjV9Q28FiGVuRYJYxWw/59Pes7IAbmfQBV
|
||||
4vqGCQQr4eG78gVwjw+SNdp4/6jdNkIHDqR4XW9id/r5wYxKKj4UUkSor3/+h9Rd9srh+GApy
|
||||
uOxw/ejFvbRcxFIjvadpq1KLnO7nM27nJ4lp44ul3i7VUGefLM/45TCsuds2HM1iQWhPFQ54y
|
||||
SA5sYjf73EUJdkHchaf5i+4uSOmbOWQ4Yvmd8+IoyoXAxvEzY2Xh53nWi8ZPY1Tu4Bw8GRrz7
|
||||
L+VK0QiWCg3/hM7wRlFFyshmMrScMk5fOf9ynqd0JbHB7u+n4/GUwx3im/w8+NgSd3YOz7wNU
|
||||
KD1snDWoMUO8f23Ik1Osym688OLWNwKYT+mZbMIMXcz1fB+olRZn4czMhN5DiSb8hyOxRI8NE
|
||||
PNfaoN87CXiRkgazV6U1eiRkfcK2AvI7zOJF1tclUHZ9awyYoXtxfEzZ+J/2TCXiC7V2iSkUF
|
||||
EjwgPxlJmccccjsxc46v1ajnTxLo0tJbZ0+DJXWkCgQ0d/iiScQ=
|
||||
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
Return-Path: <>
|
||||
Delivered-To: alice@posteo.org
|
||||
Received: from proxy02.posteo.name ([127.0.0.1])
|
||||
by dovecot03.posteo.local (Dovecot) with LMTP id zvCFJRzX317LGQIA+3EWog
|
||||
for <alice@posteo.org>; Tue, 09 Jun 2020 20:44:24 +0200
|
||||
Received: from proxy02.posteo.de ([127.0.0.1])
|
||||
by proxy02.posteo.name (Dovecot) with LMTP id mhNkNAnR316xBQMAGFAyLg
|
||||
; Tue, 09 Jun 2020 20:44:23 +0200
|
||||
Received: from mailin06.posteo.de (unknown [10.0.1.6])
|
||||
by proxy02.posteo.de (Postfix) with ESMTPS id 49hJtv3RRcz11m7
|
||||
for <alice@posteo.org>; Tue, 9 Jun 2020 20:44:23 +0200 (CEST)
|
||||
Received: from mx04.posteo.de (mailin06.posteo.de [127.0.0.1])
|
||||
by mailin06.posteo.de (Postfix) with ESMTPS id 6935920DD2
|
||||
for <alice@posteo.org>; Tue, 9 Jun 2020 20:44:23 +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=8
|
||||
tests=[ALL_TRUSTED=-1] autolearn=disabled
|
||||
Received: from mout01.posteo.de (mout01.posteo.de [185.67.36.65])
|
||||
by mx04.posteo.de (Postfix) with ESMTPS id 49hJtv001Vz10kT
|
||||
for <alice@posteo.org>; Tue, 9 Jun 2020 20:44:22 +0200 (CEST)
|
||||
Authentication-Results: mx04.posteo.de; dmarc=none (p=none dis=none) header.from=mout01.posteo.de
|
||||
Received: by mout01.posteo.de (Postfix)
|
||||
id DCB6B1200DD; Tue, 9 Jun 2020 20:44:22 +0200 (CEST)
|
||||
Date: Tue, 9 Jun 2020 20:44:22 +0200 (CEST)
|
||||
From: MAILER-DAEMON@mout01.posteo.de (Mail Delivery System)
|
||||
Subject: Undelivered Mail Returned to Sender
|
||||
To: alice@posteo.org
|
||||
Auto-Submitted: auto-replied
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/report; report-type=delivery-status;
|
||||
boundary="B39111200B9.1591728262/mout01.posteo.de"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Message-Id: <20200609184422.DCB6B1200DD@mout01.posteo.de>
|
||||
|
||||
This is a MIME-encapsulated message.
|
||||
|
||||
--B39111200B9.1591728262/mout01.posteo.de
|
||||
Content-Description: Notification
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
|
||||
This is the mail system at host mout01.posteo.de.
|
||||
|
||||
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
|
||||
|
||||
<hanerthaertidiuea@gmx.de>: host mx01.emig.gmx.net[212.227.17.5] said: 550
|
||||
Requested action not taken: mailbox unavailable (in reply to RCPT TO
|
||||
command)
|
||||
|
||||
--B39111200B9.1591728262/mout01.posteo.de
|
||||
Content-Description: Delivery report
|
||||
Content-Type: message/delivery-status
|
||||
|
||||
Reporting-MTA: dns; mout01.posteo.de
|
||||
X-Postfix-Queue-ID: B39111200B9
|
||||
X-Postfix-Sender: rfc822; alice@posteo.org
|
||||
Arrival-Date: Tue, 9 Jun 2020 20:44:22 +0200 (CEST)
|
||||
|
||||
Final-Recipient: rfc822; hanerthaertidiuea@gmx.de
|
||||
Original-Recipient: rfc822;hanerthaertidiuea@gmx.de
|
||||
Action: failed
|
||||
Status: 5.0.0
|
||||
Remote-MTA: dns; mx01.emig.gmx.net
|
||||
Diagnostic-Code: smtp; 550 Requested action not taken: mailbox unavailable
|
||||
|
||||
--B39111200B9.1591728262/mout01.posteo.de
|
||||
Content-Description: Undelivered Message Headers
|
||||
Content-Type: text/rfc822-headers
|
||||
|
||||
Return-Path: <alice@posteo.org>
|
||||
Received: from mout01.posteo.de (unknown [10.0.0.65])
|
||||
by mout01.posteo.de (Postfix) with ESMTPS id B39111200B9
|
||||
for <hanerthaertidiuea@gmx.de>; Tue, 9 Jun 2020 20:44:22 +0200 (CEST)
|
||||
Received: from submission-encrypt01.posteo.de (unknown [10.0.0.75])
|
||||
by mout01.posteo.de (Postfix) with ESMTPS id 8A684160060
|
||||
for <hanerthaertidiuea@gmx.de>; Tue, 9 Jun 2020 20:44:22 +0200 (CEST)
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.de; s=2017;
|
||||
t=1591728262; bh=g3zLYH4xKxcPrHOD18z9YfpQcnk/GaJedfustWU5uGs=;
|
||||
h=To:From:Subject:Date:From;
|
||||
b=brJnt4PLAX3Tda1RHCo91aB1kMAL/Ku9dmO7D2DD41Zu5ShNsyqqyDkyxb1DsDn3O
|
||||
6KuBZe3/8gemBuCJ/mxzwd9v8sBnlrV+5afIk0Ye9VvthZsc4HoG79+FiVOi9F38o0
|
||||
DtJJFYFw/X7mAc5Xyt0B0JvtiTPpBdRAkluUQm+QW6cW6GGlwicVW19qvebzq+sHyP
|
||||
X2bZ8wpo78yVgvjPBK3DLaXa+pKFMBjLdDUcIE2bZnY6u6F1x8SXGKGBoxVwdJipJx
|
||||
v14so5IejNsf4LYJjH3Qb8xgK1aAi6e6nQn4YXV0INL6ahzgALiT9N6vwunNKYVJNi
|
||||
fPPKvBWDfUS4Q==
|
||||
Received: from customer (localhost [127.0.0.1])
|
||||
by submission (posteo.de) with ESMTPSA id 49hJtt1WPbz6tmV
|
||||
for <hanerthaertidiuea@gmx.de>; Tue, 9 Jun 2020 20:44:22 +0200 (CEST)
|
||||
To: hanerthaertidiuea@gmx.de
|
||||
From: deltachat <alice@posteo.org>
|
||||
Subject: test
|
||||
Message-ID: <04422840-f884-3e37-5778-8192fe22d8e1@posteo.de>
|
||||
Date: Tue, 9 Jun 2020 20:45:02 +0200
|
||||
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101
|
||||
Thunderbird/68.8.1
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Language: de-DE
|
||||
Posteo-User: alice@posteo.org
|
||||
Posteo-Dkim: ok
|
||||
|
||||
--B39111200B9.1591728262/mout01.posteo.de--
|
||||
@@ -1,107 +0,0 @@
|
||||
Return-Path: <>
|
||||
Delivered-To: alice@testrun.org
|
||||
Received: from hq5.merlinux.eu
|
||||
by hq5.merlinux.eu (Dovecot) with LMTP id Ye02K6PB5F43cQAAPzvFDg
|
||||
for <alice@testrun.org>; Sat, 13 Jun 2020 14:08:03 +0200
|
||||
Received: by hq5.merlinux.eu (Postfix)
|
||||
id 9EBE627A0B2E; Sat, 13 Jun 2020 14:08:03 +0200 (CEST)
|
||||
Date: Sat, 13 Jun 2020 14:08:03 +0200 (CEST)
|
||||
From: MAILER-DAEMON@hq5.merlinux.eu (Mail Delivery System)
|
||||
Subject: Undelivered Mail Returned to Sender
|
||||
To: alice@testrun.org
|
||||
Auto-Submitted: auto-replied
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/report; report-type=delivery-status;
|
||||
boundary="CDB8D27A0B2C.1592050083/hq5.merlinux.eu"
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Message-Id: <20200613120803.9EBE627A0B2E@hq5.merlinux.eu>
|
||||
|
||||
This is a MIME-encapsulated message.
|
||||
|
||||
--CDB8D27A0B2C.1592050083/hq5.merlinux.eu
|
||||
Content-Description: Notification
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This is the mail system at host hq5.merlinux.eu.
|
||||
|
||||
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
|
||||
|
||||
<hcksocnsofoejx@five.chat>: host mail.five.chat[195.62.125.103] said: 550 5.1.1
|
||||
<hcksocnsofoejx@five.chat>: Recipient address rejected: User unknown in
|
||||
virtual mailbox table (in reply to RCPT TO command)
|
||||
|
||||
--CDB8D27A0B2C.1592050083/hq5.merlinux.eu
|
||||
Content-Description: Delivery report
|
||||
Content-Type: message/global-delivery-status
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Reporting-MTA: dns; hq5.merlinux.eu
|
||||
X-Postfix-Queue-ID: CDB8D27A0B2C
|
||||
X-Postfix-Sender: rfc822; alice@testrun.org
|
||||
Arrival-Date: Sat, 13 Jun 2020 14:08:01 +0200 (CEST)
|
||||
|
||||
Final-Recipient: rfc822; hcksocnsofoejx@five.chat
|
||||
Original-Recipient: rfc822;hcksocnsofoejx@five.chat
|
||||
Action: failed
|
||||
Status: 5.1.1
|
||||
Remote-MTA: dns; mail.five.chat
|
||||
Diagnostic-Code: smtp; 550 5.1.1 <hcksocnsofoejx@five.chat>: Recipient address
|
||||
rejected: User unknown in virtual mailbox table
|
||||
|
||||
--CDB8D27A0B2C.1592050083/hq5.merlinux.eu
|
||||
Content-Description: Undelivered Message
|
||||
Content-Type: message/global
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Return-Path: <alice@testrun.org>
|
||||
Received: from localhost (p200300edb723070079835ce22985a199.dip0.t-ipconnect.de [IPv6:2003:ed:b723:700:7983:5ce2:2985:a199])
|
||||
by hq5.merlinux.eu (Postfix) with UTF8SMTPSA id CDB8D27A0B2C;
|
||||
Sat, 13 Jun 2020 14:08:01 +0200 (CEST)
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=testrun.org;
|
||||
s=testrun; t=1592050082;
|
||||
bh=Kvhta0OMsTRVC7OlaAqo68TBE0KuGBv4vUBp6Ez/7VY=;
|
||||
h=Subject:References:In-Reply-To:Date:To:From:From;
|
||||
b=Ql60JEGFXLNvjsyihATw2z34ct++8xZvTPNw0snXe6+oqdqsRZJ9tWNDTxOgx8Iqf
|
||||
HQ4puBVGcWjIlszYQVLlq3APi04o2ep3GrD8EF0J0GpDdW8yw6wCos6Q8r+TWmXwET
|
||||
kGXHTRPVaUIqZF2i/utypxMfd1ua0S3jBDnIXTe/p2+XvfC3Cf3hZGW+FQ/Zd7G8Vh
|
||||
/U2rgX5BTIGf26ZCbmcMaXWkftgv6+yns0AmzorV9yB+EhTkWIUjk+C25bRtMbJ5mZ
|
||||
93dwdr+sXrrSZLSi+LBqc57Dv9j4p/SUmB4zPlvfUv7/bqLi36pypvtCJ5Ul8UEXSb
|
||||
XNFZPaEl+mwjA==
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Chat-Disposition-Notification-To: alice@testrun.org
|
||||
Subject: =?utf-8?q?Message_from_hocuri1=40testrun=2Eorg?=
|
||||
MIME-Version: 1.0
|
||||
References: <Mr.VSg3KXFUOTG.9sn7JBxZn1W@testrun.org>
|
||||
<Mr.F4nR4LnXb6v.gqVbCJRgsmn@testrun.org>
|
||||
In-Reply-To: <Mr.F4nR4LnXb6v.gqVbCJRgsmn@testrun.org>
|
||||
Date: Sat, 13 Jun 2020 12:08:01 +0000
|
||||
X-Mailer: Delta Chat Core 1.35.0/Android 1.9.5
|
||||
Chat-Version: 1.0
|
||||
Autocrypt: addr=alice@testrun.org; prefer-encrypt=mutual;
|
||||
keydata=xjMEXt3z1xYJKwYBBAHaRw8BAQdAf6MctU/8cmEqwEN9VFZ3gHBFIxKiEaARZl1DFUkI7e
|
||||
rNFTxob2N1cmkxQHRlc3RydW4ub3JnPsKLBBAWCAAzAhkBBQJe3fPXAhsDBAsJCAcGFQgJCgsCAxYC
|
||||
ARYhBIZ3ajRUkki89+04sgctAtaIXygAAAoJEActAtaIXygAG2IA/1nTmmmkHAc1Bjtx2FOstbaS+N
|
||||
XHjxaK+hkoWllsyhz0AQDJJ1++u7jVZPRn/j1LlByrT3Jv/D1aY14J5rjj+ADVBM44BF7d89cSCisG
|
||||
AQQBl1UBBQEBB0DpSTaZ30dAVwM9PkBe2h+gFyxn9HSorP4XCHJu/lIdPAMBCAfCeAQYFggAIAUCXt
|
||||
3z1wIbDBYhBIZ3ajRUkki89+04sgctAtaIXygAAAoJEActAtaIXygA2QkA/16toWCtseYKw8G1X2j7
|
||||
xYR3Cyabq37hgbesDOThIIzNAP0UCUS8mnunmkS5adEbftRaDi2JZoGxDw46jtJJ2+13Cw==
|
||||
Message-ID: <Mr.A7pTA5IgrUA.q4bP41vAJOp@testrun.org>
|
||||
To: <hcksocnsofoejx@five.chat>
|
||||
From: <alice@testrun.org>
|
||||
|
||||
F
|
||||
|
||||
--
|
||||
Sent with my Delta Chat Messenger: https://delta.chat
|
||||
|
||||
|
||||
--CDB8D27A0B2C.1592050083/hq5.merlinux.eu--
|
||||
@@ -1,91 +0,0 @@
|
||||
Return-Path: <>
|
||||
Delivered-To: alice@tiscali.it
|
||||
Received: from director-5.mail.tiscali.sys ([10.39.80.174])
|
||||
by dovecot-08.mail.tiscali.sys with LMTP id SBRfEpGb517VAgAAd2fHbg
|
||||
for <alice@tiscali.it>; Mon, 15 Jun 2020 16:02:25 +0000
|
||||
Received: from cmgw-4.mail.tiscali.it ([10.39.80.174])
|
||||
by director-5.mail.tiscali.sys with LMTP id MFUPL5Cb516tawAArQJVuQ
|
||||
; Mon, 15 Jun 2020 16:02:25 +0000
|
||||
Received: from michael.mail.tiscali.it ([213.205.33.246])
|
||||
by cmgw-4.mail.tiscali.it with
|
||||
id rTtS2200V5JdeUd01U2RlV; Mon, 15 Jun 2020 16:02:25 +0000
|
||||
x-cnfs-analysis: v=2.3 cv=ZdPMyfdA c=1 sm=1 tr=0 cx=a_idp_d
|
||||
a=AfTPebshMYb+aQOCLa9q3Q==:117 a=HpEJnUlJZJkA:10 a=jmdcTMp_Gj4A:10
|
||||
a=r77TgQKjGQsHNAKrUKIA:9 a=b8iBRs35AAAA:8 a=NMdB-582e605uxHDr_AA:9
|
||||
a=QEXdDO2ut3YA:10 a=MhhPCb74-dYA:10 a=HXlsH_Kov2KnitTn7A4A:9
|
||||
a=BkuCPOF3BOzethIN9HQA:9 a=qG5HpJ6ZyD35YNEB:21 a=kvHihYffoorsyJbA:21
|
||||
a=xD8EQi6zkreDqSNPYj5l:22
|
||||
Date: Mon, 15 Jun 2020 16:02:25 +0000
|
||||
From: Mail Delivery System <mail-daemon@smtp.tiscali.it>
|
||||
To: alice@tiscali.it
|
||||
Subject: Delivery status notification
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/report; boundary="------------I305M09060309060P_896715922369450"
|
||||
|
||||
This is a multi-part message in MIME format.
|
||||
|
||||
--------------I305M09060309060P_896715922369450
|
||||
Content-Type: text/plain; charset=UTF-8;
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This is an automatically generated Delivery Status Notification.
|
||||
|
||||
Delivery to the following recipients was aborted after 2 second(s):
|
||||
|
||||
* shenauithz@testrun.org
|
||||
|
||||
|
||||
|
||||
--------------I305M09060309060P_896715922369450
|
||||
Content-Type: message/delivery-status; charset=UTF-8;
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Reporting-MTA: dns; michael.mail.tiscali.it [213.205.33.13]
|
||||
Received-From-MTA: dns; localhost [146.241.100.150]
|
||||
Arrival-Date: Mon, 15 Jun 2020 16:02:23 +0000
|
||||
|
||||
|
||||
Final-recipient: rfc822; shenauithz@testrun.org
|
||||
Action: failed
|
||||
Status: 5.1.1
|
||||
Diagnostic-Code: smtp; 550 5.1.1 <shenauithz@testrun.org>: Recipient address rejected: User unknown in virtual mailbox table
|
||||
Last-attempt-Date: Mon, 15 Jun 2020 16:02:25 +0000
|
||||
|
||||
|
||||
|
||||
--------------I305M09060309060P_896715922369450
|
||||
Content-Type: text/rfc822-headers; Content-Transfer-Encoding: 8bit
|
||||
Content-Disposition: attachment
|
||||
|
||||
x-auth-user: alice@tiscali.it
|
||||
Chat-Disposition-Notification-To: alice@tiscali.it
|
||||
Chat-User-Avatar: avatar.jpg
|
||||
Subject: =?utf-8?q?Message_from_=F0=9F=8F=9E=EF=B8=8F_Mefiscali?=
|
||||
MIME-Version: 1.0
|
||||
Date: Mon, 15 Jun 2020 16:02:22 +0000
|
||||
X-Mailer: Delta Chat Core 1.35.0/Android 1.9.5
|
||||
Chat-Version: 1.0
|
||||
Autocrypt: addr=alice@tiscali.it; prefer-encrypt=mutual;
|
||||
keydata=xjMEXtFRUBYJKwYBBAHaRw8BAQdA5sqHJqkWlveCgsNd0rtwtZrT1mmo1gwaGC5+WheYk5
|
||||
nNHTxhbmRyZWFzLmxhdHRtYW5uQHRpc2NhbGkuaXQ+wosEEBYIADMCGQEFAl7RUXoCGwMECwkIBwYV
|
||||
CAkKCwIDFgIBFiEEJUsbRIjZEaRNAs1p1Z5M1vshkrAACgkQ1Z5M1vshkrAAaAEA4wssXeU2IXnowv
|
||||
iu3zmcNzDgE4HdmW4RFyqJC6bgxXQA/3aTfE/PhQgZvi6RrKMvP4zygXpD9y+3ydIZP88Bp8kIzjgE
|
||||
XtFRUBIKKwYBBAGXVQEFAQEHQKaAwlP0j9m0aYsCtO+qD9+foH0kiTN5BWDe5YcZrckVAwEIB8J4BB
|
||||
gWCAAgBQJe0VF6AhsMFiEEJUsbRIjZEaRNAs1p1Z5M1vshkrAACgkQ1Z5M1vshkrA1JgEAkscCQlps
|
||||
h3ZxlLqBlbf2+85f4S4aGQfFPtIYEkKKhYEBAJbQulNNp9UarvhfyBiIdvkBVDcCnJZwzbORqp8RM0 gC
|
||||
Message-ID: <Mr.un2NYERi1RM.lbQ5F9q-QyJ@tiscali.it>
|
||||
To: <shenauithz@testrun.org>
|
||||
From: =?utf-8?b?8J+Pnu+4jyBNZWZpc2NhbGk=?= <alice@tiscali.it>
|
||||
Content-Type: multipart/mixed; boundary="5uAmYQux1HZxxriijTjjKSp4DMoJwq"
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tiscali.it; s=smtp;
|
||||
t=1592236943; bh=C3taz+zuSre1ko5Q5CzGPmbyrgegKYBClx/3Dv7t/Xw=;
|
||||
h=Subject:Date:To:From;
|
||||
b=LrcfLfrQoemOkHTQsqR8MExqNlx5KPYNFWhwlBWylvVc5GlmlhzqM6SAVKd0NVsKE
|
||||
gVRlBId5FvnlwoJ2WZnXaw/+3lWKilMTuzzQ1oFGvLnZ1XUaUEfuliIv+9NI79dJWX
|
||||
+S3jsSgzJMJc9+fO6s9bJsX1EHQ2a8GXNbwDtLXs=
|
||||
|
||||
|
||||
|
||||
--------------I305M09060309060P_896715922369450--
|
||||
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
X-Atlas-Received: from 10.218.250.153 by atlas222.free.mail.ne1.yahoo.com with http; Wed, 10 Jun 2020 10:11:23 +0000
|
||||
Return-Path: <>
|
||||
Received: from 77.238.177.145 (EHLO sonic314-19.consmr.mail.ir2.yahoo.com)
|
||||
by atlas222.free.mail.ne1.yahoo.com with SMTPs; Wed, 10 Jun 2020 10:11:23 +0000
|
||||
X-Originating-Ip: [77.238.177.145]
|
||||
Received-SPF: none (domain of sonic314-19.consmr.mail.ir2.yahoo.com does not designate permitted sender hosts)
|
||||
Authentication-Results: atlas222.free.mail.ne1.yahoo.com;
|
||||
dkim=pass header.i=@yahoo.com header.s=@bounce;
|
||||
spf=none smtp.mailfrom=sonic314-19.consmr.mail.ir2.yahoo.com;
|
||||
dmarc=success(p=REJECT) header.from=yahoo.com;
|
||||
X-Apparently-To: alice@yahoo.com; Wed, 10 Jun 2020 10:11:23 +0000
|
||||
X-YMailISG: RT5ZnycWLDvIW52uqHS_EWNgl31NdJPyLLB2F4SYb1GCAoo9
|
||||
pcninuVU5GDMBZykeMT4cSUt4ZqXxS5FdEeWJqtGIAtbEGbIL8Uhcoszqm4m
|
||||
JuMJiQZwEE7W_fsS_9MUK5gZtMkhKkSnAuaeaOLKNYAwFZdBqA0uEYA5EmVf
|
||||
EC9J4RGQ4hZvrMqMj_W.cj4pvbEC.pyirLxTfkICuUkZVguYoxG16y1EOJPw
|
||||
B48fhXvF5ErU7WAHKxyRM3bMOg7b5pXHKn1dtRSVAXEuqBAQrWig1pePpYH1
|
||||
wO54sYT7cgmdiFvfLY5rR7YcBzopmKJBycKzBVoRLCY4gvoNyTLPKx9o3AAz
|
||||
WU4B7TGejDBElYSLpfnyvQg8wU27zzo2IVBZWUNztP0Ca8CQ07Y7TxUZAO.f
|
||||
DNO5c7nd81PHMRDbSeaw1BTV2Yd9vlBc7syYmwGvtVBJQwRU7qPN.DpFO2jC
|
||||
9j9DytVhm5231gdBBRSzW78yG.VvaIdJgq_YViKNM9VxFseTz3Sjt3TaYznP
|
||||
gAVq.MxpopNsSZf_tedwAhXDWyrjKsRPK.v2ANivmuWGPednniEaMYhxJ05M
|
||||
_5SnJ.hAU.l6h3HCEfiU.SH390_3tZgYNfxCo4GPPFMfnNPmKa3.rgpChBCz
|
||||
9CRexJ8BSFyCEeAhuqQ8vSJfSuittJmXvS6Tk8Rxd9HUJAtKzZ.xCWZQ4tA6
|
||||
Yp2aRG23_rK_C6hH8ArkkvbG.uVQTt6DltSX6avJLObBfIhBH0x64RoFjGee
|
||||
vYXxM741Okm0jH7r79c8GhnAwas_bwfkaTW9e1nhYP0eyI36z_QwLYgOH3Mm
|
||||
LrUcejpOMDR60QWDuDyRbWXOJdr3Q2K0ERhuAy6YnINq0sL3HX7t5wjsFLvp
|
||||
_7Ri_eruTfIst4C7DZwERwui6aDSEAdF1Z8oZukBVmiyZsHmhJQCUik646iy
|
||||
3ASMR3lX7R3q2PBHQo2oC3qte8Fzz1FhKoMtfCGtIpeCazlkhDEJ6eTBSQ3R
|
||||
Pe7M_GPiv3QNp7qu5CWHlzy6hWEKIkNwx.WRGYzfxkyJMmJm4UrhQYUfa4lG
|
||||
Wb8n.mfYnS_KGYtzyRFNqAL0IGo.1MB9aG6qQk456Fz9GJgbHLWrMXVtyfrr
|
||||
Uo7mKih8FCrdUKv5X6KBnpY0vvyoH5jrWyrvo3DW0bq_JvZ9U51JwUhoGY5U
|
||||
c1t.yCSJbs8tnrGZHuUTOvouWzpCAJsk34AqRyH0wDJZQsAwBW5UZ3jx8ARA
|
||||
FicoSqZCa4wEP9WaaXvfzFbmLW0-
|
||||
X-Originating-IP: [77.238.177.145]
|
||||
Received: from 10.217.135.165 (EHLO sonic314-19.consmr.mail.ir2.yahoo.com) (77.238.177.145)
|
||||
by mta4277.mail.ne1.yahoo.com with SMTPS; Wed, 10 Jun 2020 10:11:21 +0000
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=bounce; t=1591783879; bh=4BmZzBC/nu0AJ9r0i0xNuCENks2KZcuXCbjSHdzbg9Y=; h=Date:From:To:Subject:From:Subject; b=lPxu8goOGOLVgnwbndfdptZ7zI5VEo0lSSr+ONGxwdtuhrySKDU6Sp41/g6jWbAiVPT1947j/B5wOlPfa5tv4XkWrGf0JCbT1I20ZJIkNfNwt4F0qPnbJAiHFIDPxcY68utjC9IgPWJd0cGqJNXbFwbJBu88rtrbMoInzLakh5I=
|
||||
Received: from sonic.gate.mail.ne1.yahoo.com by sonic314.consmr.mail.ir2.yahoo.com with HTTP; Wed, 10 Jun 2020 10:11:19 +0000
|
||||
Date: Wed, 10 Jun 2020 10:11:19 +0000
|
||||
From: MAILER-DAEMON@yahoo.com
|
||||
To: alice@yahoo.com
|
||||
Message-ID: <1713051795.39992.1591783879940@sonic314.consmr.mail.ir2.yahoo.com>
|
||||
Subject: Failure Notice
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Length: 3347
|
||||
|
||||
Sorry, we were unable to deliver your message to the following address.
|
||||
|
||||
<haeclirth.sinoenrat@yahoo.com>:
|
||||
554: delivery error: dd Not a valid recipient - atlas117.free.mail.ne1.yahoo.com
|
||||
|
||||
--- Below this line is a copy of the message.
|
||||
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1591783876; bh=kXD6TZuQDjqZf/AEAJ2HCX32Titkj3IywytG6GSm4yQ=; h=Date:From:To:Subject:References:From:Subject; b=PexnIBxnVSkyutv/jVn+Wlt5QPuVHnWleP3baWlvqkXaKR51pCaZIoGJggMEonitEeJkcYpgplBawEBz0hlGf63NqOHHkxUk6U1olwc0y9kj5kDH5lrORgXrf7U4z5t+i5n0II36MxbG9n/5tDRXoiabLFoWx//3O9x/ZJvVWPlq8RBFzVG8aoL2TkBQAVcqX/vW1f3WJuopaUYWB4AzR3TyuC2kVQPFqbPMk+G/VmyuZFmZesh1bSBva5hdKYLxES5v8hvTIDRqSYZrnZ4V67MqicZ8m229Xf4Za8qOE+a2Z+Vv5VrQ+CjjPZwRAcmKHkLY80VCSkpeL2R2YG4APg==
|
||||
X-YMail-OSG: 4spnw98VM1k_g51CM6oepdNEMiPFRtZ0ZG_zOBGIlhcNvS1mkzr8l8VTB0CY_Q0
|
||||
dR52ikl7QVYESerRQgcGqBfLwKpem7i1XSrCl2HuyHeWzF6Gu5MqFPMCak.v8GyDXNO075NEwNt1
|
||||
i18CJ29cEjiHthoamgmj0oqerAglglKRhTuuAFy4wUmZZm7VyvaW4wHUD1g7DeWGijQsCglSYMUK
|
||||
CmoFcKsWOZBSYPMkp7iRwUp52pXHFin3qf4uQ27K_Sh.6s7KLAfWVkV7L_5AR3MyCPAVzm71.1yG
|
||||
G7Vy5HSBgGMQ90B7VbcjOkCg3F4JNl4Z_P2ejV1KZ.tNoPLgO.FmsfFy1OXBGf3m2mDmRcuEO4K2
|
||||
mTRhsjZf.2iiWpx02b3tY.oUtYrIBXBVIFPbTB9sBMn_9Z_qdVmO3gjD6gCPEBzuVvEO0eZIrgaw
|
||||
EDTZt8Z9tSRDm1.4gV8LWQBYShF7XuMV0togiLYIO8s_iTHcTbhKhPlwxP.mxr06xcx_9kzReVTL
|
||||
9lB1FkB5Jm0WccWHGhLBqeMjGDoaNqPxLqJ.1tI58tLXsPoR6m1NFVEdzI1G.4AVBeXZ_9BjgUhm
|
||||
KY33sEg.GwIjUlWWWuSyRZ1q1K2nqi1z29wH2R1Glmdmx0lyqfMg9Xe8HV7YZu2CuZ8SlDLLB.rX
|
||||
NU4PwMsNfU6pK2HejQPsJuyOlI5Q824rXRF5xTLYKsYcQptoFXyLe6MXKW1ThBLQV2nWYDRs_V.e
|
||||
SBmt22TfuOwu4Y5ju0sXmztZ8zpiIC8_rnAa5bVBEHxzkic64UZdukDX9V12Pk3G2sGYRyPTH472
|
||||
wBX33JpDuq6BtrKr4FXjCLeVppARTHpiKM0jHMjmNf1bF0TvrcCsC9zAYtitAqgcGZNFETNuV3KM
|
||||
57XifdDEwUPOuww0ApSWO.iwP2POvIRBVlrxdgA8MbLmuuX4UxNCw23z1f7MVY6F3L60LUrX5GZO
|
||||
aKaMmD1XTzx32J6c_TUmyuViT5vphqpEooTzHG2X7ALb4xC8yHlE4wDKyaEDARZ.8P2lO9T18oCz
|
||||
OQvJjwDaLOkeAmo23yRMn70bYJK3tP9Z5cS1C0TE8PEtz4sd1syQUIZZ2g8JG_AQcE4lUZSZlIKN
|
||||
AHjB8h8Uin35zKe0Le1DBjdQUmpgAETlmYE7V0nJDEmagB3dtpbokgRBuuBfhXlFpxHcnAmBFFFm
|
||||
XOSLWEPnmxu2o8CCjjz3QUBy2fr3EI_D2VFpy..MuZgRwtES.l24m_95xtQxI28R4SWZN6LsS_rr
|
||||
1S33BJCCCAfXtCAFzCfz5.qSzHRYbLdY5do6yKj0pPLQTUTjlMwmCUGPcSJhsyxkkEVIK1W_Z16R
|
||||
ZRls-
|
||||
Received: from sonic.gate.mail.ne1.yahoo.com by sonic314.consmr.mail.ir2.yahoo.com with HTTP; Wed, 10 Jun 2020 10:11:16 +0000
|
||||
Date: Wed, 10 Jun 2020 10:11:12 +0000 (UTC)
|
||||
From: Delta Chat Test <alice@yahoo.com>
|
||||
To: "haeclirth.sinoenrat@yahoo.com" <haeclirth.sinoenrat@yahoo.com>
|
||||
Message-ID: <1680295672.3657931.1591783872936@mail.yahoo.com>
|
||||
Subject: test
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/alternative;
|
||||
boundary="----=_Part_3657930_145367320.1591783872935"
|
||||
References: <1680295672.3657931.1591783872936.ref@mail.yahoo.com>
|
||||
X-Mailer: WebService/1.1.16072 YMailNorrin Mozilla/5.0 (X11; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0
|
||||
Content-Length: 494
|
||||
|
||||
------=_Part_3657930_145367320.1591783872935
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
test
|
||||
|
||||
------=_Part_3657930_145367320.1591783872935
|
||||
Content-Type: text/html; charset=UTF-8
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
<html><head></head><body><div class="yahoo-style-wrap" style="font-family:Helvetica Neue, Helvetica, Arial, sans-serif;font-size:16px;"><div dir="ltr" data-setdir="false">test<br></div></div></body></html>
|
||||
------=_Part_3657930_145367320.1591783872935--
|
||||
4
upload-server/.gitignore
vendored
Normal file
4
upload-server/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
uploads
|
||||
node_modules
|
||||
yarn*
|
||||
.gitfoo
|
||||
17
upload-server/README.md
Normal file
17
upload-server/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# deltachat-upload-server
|
||||
|
||||
Demo server for the HTTP file upload feature.
|
||||
|
||||
### Usage
|
||||
|
||||
```
|
||||
npm install
|
||||
node server.js
|
||||
```
|
||||
|
||||
Configure with environment variables:
|
||||
* `UPLOAD_PATH`: Path to upload files to (default: `./uploads`)
|
||||
* `PORT`: Port to listen on (default: `8080`)
|
||||
* `HOSTNAME`: Hostname to listen on (default: `0.0.0.0`)
|
||||
* `BASEURL`: Base URL for generated links (default: `http://[hostname]:[port]/`)
|
||||
|
||||
13
upload-server/package.json
Normal file
13
upload-server/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "deltachat-upload-server",
|
||||
"version": "1.0.0",
|
||||
"main": "server.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"base32": "^0.0.6",
|
||||
"express": "^4.17.1"
|
||||
}
|
||||
}
|
||||
73
upload-server/server.js
Normal file
73
upload-server/server.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const p = require('path')
|
||||
const express = require('express')
|
||||
const fs = require('fs')
|
||||
const { pipeline } = require('stream')
|
||||
|
||||
const app = express()
|
||||
|
||||
const config = {
|
||||
path: process.env.UPLOAD_PATH || p.resolve('./uploads'),
|
||||
port: process.env.PORT || 8080,
|
||||
hostname: process.env.HOSTNAME || '0.0.0.0',
|
||||
baseurl: process.env.BASE_URL
|
||||
}
|
||||
|
||||
if (!config.baseurl) config.baseurl = `http://${config.hostname}:${config.port}/`
|
||||
if (!config.baseurl.endsWith('/')) config.baseurl = config.baseurl + '/'
|
||||
|
||||
if (!fs.existsSync(config.path)) {
|
||||
fs.mkdirSync(config.path, { recursive: true })
|
||||
}
|
||||
|
||||
app.use('/:filename', checkFilenameMiddleware)
|
||||
app.put('/:filename', (req, res) => {
|
||||
const uploadpath = req.uploadpath
|
||||
const filename = req.params.filename
|
||||
fs.stat(uploadpath, (err, stat) => {
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
console.error('error', err.message)
|
||||
return res.code(500).send('internal server error')
|
||||
}
|
||||
if (stat) return res.status(500).send('filename in use')
|
||||
|
||||
const ws = fs.createWriteStream(uploadpath)
|
||||
pipeline(req, ws, err => {
|
||||
if (err) {
|
||||
console.error('error', err.message)
|
||||
return res.status(500).send('internal server error')
|
||||
}
|
||||
console.log('file uploaded: ' + uploadpath)
|
||||
const url = config.baseurl + filename
|
||||
res.end(url)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
app.get('/:filename', (req, res) => {
|
||||
const uploadpath = req.uploadpath
|
||||
const rs = fs.createReadStream(uploadpath)
|
||||
res.setHeader('content-type', 'application/octet-stream')
|
||||
pipeline(rs, res, err => {
|
||||
if (err) console.error('error', err.message)
|
||||
if (err) return res.status(500).send(err.message)
|
||||
})
|
||||
})
|
||||
|
||||
function checkFilenameMiddleware (req, res, next) {
|
||||
const filename = req.params.filename
|
||||
if (!filename) return res.status(500).send('missing filename')
|
||||
if (!filename.match(/^[a-zA-Z0-9]{26,32}$/)) {
|
||||
return res.status(500).send('illegal filename')
|
||||
}
|
||||
const uploadpath = p.normalize(p.join(config.path, req.params.filename))
|
||||
if (!uploadpath.startsWith(config.path)) {
|
||||
return res.code(500).send('bad request')
|
||||
}
|
||||
req.uploadpath = uploadpath
|
||||
next()
|
||||
}
|
||||
|
||||
app.listen(config.port, err => {
|
||||
if (err) console.error(err)
|
||||
else console.log(`Listening on ${config.baseurl}`)
|
||||
})
|
||||
Reference in New Issue
Block a user