mirror of
https://github.com/chatmail/core.git
synced 2026-04-04 14:32:11 +03:00
Compare commits
1 Commits
configured
...
fix_sql_or
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dac320a810 |
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -3,7 +3,7 @@ updates:
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
interval: "daily"
|
||||
commit-message:
|
||||
prefix: "cargo"
|
||||
open-pull-requests-limit: 50
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -77,10 +77,10 @@ jobs:
|
||||
include:
|
||||
# Currently used Rust version, same as in `rust-toolchain` file.
|
||||
- os: ubuntu-latest
|
||||
rust: 1.60.0
|
||||
rust: 1.59.0
|
||||
python: 3.9
|
||||
- os: windows-latest
|
||||
rust: 1.60.0
|
||||
rust: 1.59.0
|
||||
python: false # Python bindings compilation on Windows is not supported.
|
||||
|
||||
# Minimum Supported Rust Version = 1.56.0
|
||||
|
||||
21
.github/workflows/dependabot.yml
vendored
21
.github/workflows/dependabot.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: Dependabot auto-approve
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
steps:
|
||||
- name: Dependabot metadata
|
||||
id: metadata
|
||||
uses: dependabot/fetch-metadata@v1.1.1
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Approve a PR
|
||||
run: gh pr review --approve "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{github.event.pull_request.html_url}}
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
46
CHANGELOG.md
46
CHANGELOG.md
@@ -2,22 +2,18 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Fixes
|
||||
|
||||
- Take `delete_device_after` into account when calculating ephemeral loop timeout #3211
|
||||
|
||||
### Changes
|
||||
|
||||
- Further improve finding the correct server after logging in #3208
|
||||
- `get_connectivity_html()` returns HTML as non-scalable #3213
|
||||
|
||||
|
||||
## 1.77.0
|
||||
|
||||
### API changes
|
||||
- change semantics of `dc_get_webxdc_status_updates()` second parameter
|
||||
and remove update-id from `DC_EVENT_WEBXDC_STATUS_UPDATE` #3081
|
||||
|
||||
### Fixes
|
||||
- Hopefully fix a bug where outgoing messages appear twice with Amazon SES #3077
|
||||
- do not delete messages without Message-IDs as duplicates #3095
|
||||
- Assign replies from a different email address to the correct chat #3119
|
||||
- start ephemeral timer when seen status is synchronized via IMAP #3122
|
||||
- do not delete duplicate messages on IMAP immediately to accidentally deleting
|
||||
the last copy #3138
|
||||
|
||||
### Changes
|
||||
- add more SMTP logging #3093
|
||||
- place common headers like `From:` before the large `Autocrypt:` header #3079
|
||||
@@ -26,32 +22,12 @@
|
||||
- improve speed by caching config values #3131 #3145
|
||||
- optimize `markseen_msgs` #3141
|
||||
- automatically accept chats with outgoing messages #3143
|
||||
- `dc_receive_imf` refactorings #3154 #3156 #3159
|
||||
- `dc_receive_imf` refactorings #3154 #3156
|
||||
- add index to speedup deletion of expired ephemeral messages #3155
|
||||
- muted chats stay archived on new messages #3184
|
||||
- support `min_api` from Webxdc manifests #3206
|
||||
- do not read whole webxdc file into memory #3109
|
||||
- improve tests, refactorings #3073 #3096 #3102 #3108 #3139 #3128 #3133 #3142 #3153 #3151 #3174 #3170 #3148 #3179 #3185
|
||||
- improve documentation #2983 #3112 #3103 #3118 #3120
|
||||
|
||||
|
||||
### Fixes
|
||||
- speed up loading of chat messages by a factor of 20 #3171 #3194 #3173
|
||||
- fix an issue where the app crashes when trying to export a backup #3195
|
||||
- hopefully fix a bug where outgoing messages appear twice with Amazon SES #3077
|
||||
- do not delete messages without Message-IDs as duplicates #3095
|
||||
- assign replies from a different email address to the correct chat #3119
|
||||
- assing outgoing private replies to the correct chat #3177
|
||||
- start ephemeral timer when seen status is synchronized via IMAP #3122
|
||||
- do not create empty contact requests with "setup changed" messages;
|
||||
instead, send a "setup changed" message into all chats we share with the peer #3187
|
||||
- do not delete duplicate messages on IMAP immediately to accidentally deleting
|
||||
the last copy #3138
|
||||
- clear more columns when message expires due to `delete_device_after` setting #3181
|
||||
- do not try to use stale SMTP connections #3180
|
||||
- slightly improve finding the correct server after logging in #3207
|
||||
- retry message sending automatically if loop is not interrupted #3183
|
||||
- fix a bug where sometimes the file extension of a long filename containing a dot was cropped #3098
|
||||
|
||||
- Fix a bug where sometimes the file extension of a long filename containing a dot was cropped #3098
|
||||
|
||||
## 1.76.0
|
||||
|
||||
|
||||
290
Cargo.lock
generated
290
Cargo.lock
generated
@@ -89,7 +89,7 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom 0.2.6",
|
||||
"getrandom 0.2.4",
|
||||
"once_cell",
|
||||
"version_check 0.9.4",
|
||||
]
|
||||
@@ -177,9 +177,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-global-executor"
|
||||
version = "2.0.4"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c290043c9a95b05d45e952fb6383c67bcb61471f60cfa21e890dba6654234f43"
|
||||
checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-executor",
|
||||
@@ -249,9 +249,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "2.5.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
|
||||
checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
]
|
||||
@@ -310,6 +310,9 @@ dependencies = [
|
||||
"nom 5.1.2",
|
||||
"pin-project",
|
||||
"pin-utils",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -344,9 +347,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-std-resolver"
|
||||
version = "0.21.2"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f2f8a4a203be3325981310ab243a28e6e4ea55b6519bffce05d41ab60e09ad8"
|
||||
checksum = "e97652c63a409ec955622c07244fea8511197767f1ab362a78ddb8a15b8a00a5"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
@@ -372,9 +375,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.2.0"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9"
|
||||
checksum = "677d306121baf53310a3fd342d88dc0824f6bbeace68347593658525565abee8"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
@@ -528,9 +531,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||
|
||||
[[package]]
|
||||
name = "blocking"
|
||||
version = "1.2.0"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
|
||||
checksum = "046e47d4b2d391b1f6f8b407b1deb8dee56c1852ccd868becf2710f601b5f427"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-task",
|
||||
@@ -597,9 +600,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.9.1"
|
||||
version = "1.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc"
|
||||
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -635,9 +638,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||
|
||||
[[package]]
|
||||
name = "cfb-mode"
|
||||
@@ -772,7 +775,7 @@ dependencies = [
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"percent-encoding",
|
||||
"rand 0.8.5",
|
||||
"rand 0.8.4",
|
||||
"sha2 0.9.9",
|
||||
"time 0.2.27",
|
||||
"version_check 0.9.4",
|
||||
@@ -796,9 +799,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.2"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
|
||||
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -864,9 +867,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.4"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
|
||||
checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
@@ -885,11 +888,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.8"
|
||||
version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
|
||||
checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
@@ -899,9 +901,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.5"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
|
||||
checksum = "4dd435b205a4842da59efd07628f921c096bc1cc0a156835b4fa0bcb9a19bcce"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
@@ -909,9 +911,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.8"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
|
||||
checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"lazy_static",
|
||||
@@ -961,9 +963,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.22"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
|
||||
checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -980,9 +982,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "3.2.1"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0"
|
||||
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest 0.9.0",
|
||||
@@ -1067,7 +1069,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.77.0"
|
||||
version = "1.76.0"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"anyhow",
|
||||
@@ -1147,7 +1149,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.77.0"
|
||||
version = "1.76.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-std",
|
||||
@@ -1236,9 +1238,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
@@ -1264,9 +1266,9 @@ checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "1.4.1"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d5c4b5e5959dc2c2b89918d8e2cc40fcdd623cef026ed09d2f0ee05199dc8e4"
|
||||
checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816"
|
||||
dependencies = [
|
||||
"signature",
|
||||
]
|
||||
@@ -1386,9 +1388,9 @@ checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.31"
|
||||
version = "0.8.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
|
||||
checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
@@ -1521,9 +1523,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
version = "3.0.5"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46e245f4c8ec30c6415c56cb132c07e69e74f1942f6b4a4061da748b49f486ca"
|
||||
checksum = "fcef756dea9cf3db5ce73759cf0467330427a786b47711b8d6c97620d718ceb9"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"rustix",
|
||||
@@ -1718,9 +1720,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.6"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||
checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
@@ -1987,9 +1989,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "0.6.1"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9448015e586b611e5d322f6703812bbca2f1e709d5773ecd38ddb4e3bb649504"
|
||||
checksum = "f6ef6787e7f0faedc040f95716bdd0e62bcfcf4ba93da053b62dea2691c13864"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipconfig"
|
||||
@@ -2005,9 +2010,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.4.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c"
|
||||
checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
@@ -2032,15 +2037,15 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.2.4"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "744c24117572563a98a7e9168a5ac1ee4a1ca7f702211258797bbe0ed0346c3c"
|
||||
checksum = "105fb082d64e2100074587f59a74231f771750c664af903f1f9f76c9dedfc6f1"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.57"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
|
||||
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -2117,9 +2122,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.122"
|
||||
version = "0.2.121"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259"
|
||||
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -2129,9 +2134,9 @@ checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.24.2"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
|
||||
checksum = "4b8f2caab464860c64cdf0a7435d36906e25894b67e774b5dd44146ef1cd0420"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"openssl-sys",
|
||||
@@ -2147,17 +2152,16 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.0.42"
|
||||
version = "0.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7"
|
||||
checksum = "95f5690fef754d905294c56f7ac815836f2513af966aa47f2e07ac79be07827f"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.7"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
@@ -2237,9 +2241,9 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.4"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
|
||||
checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
@@ -2485,9 +2489,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.18.0+1.1.1n"
|
||||
version = "111.17.0+1.1.1m"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7897a926e1e8d00219127dc020130eca4292e5ca666dd592480d72c3eca2ff6c"
|
||||
checksum = "05d6a336abd10814198f66e2a91ccd7336611f30334119ca8ce300536666fcf4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
@@ -2571,7 +2575,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.2",
|
||||
"parking_lot_core 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2590,15 +2594,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.2"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37"
|
||||
checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys 0.34.0",
|
||||
"windows-sys 0.32.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2702,9 +2706,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.25"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
@@ -2818,9 +2822,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.37"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
@@ -2836,7 +2840,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"quick-error 2.0.1",
|
||||
"rand 0.8.5",
|
||||
"rand 0.8.4",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_xorshift",
|
||||
"regex-syntax",
|
||||
@@ -2932,18 +2936,19 @@ dependencies = [
|
||||
"packed_simd",
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc",
|
||||
"rand_hc 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.3",
|
||||
"rand_hc 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2981,7 +2986,7 @@ version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom 0.2.6",
|
||||
"getrandom 0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2993,6 +2998,15 @@ dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.3.0"
|
||||
@@ -3029,22 +3043,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.13"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
||||
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.3"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
dependencies = [
|
||||
"getrandom 0.2.6",
|
||||
"getrandom 0.2.4",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3164,14 +3177,14 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver 1.0.7",
|
||||
"semver 1.0.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.34.2"
|
||||
version = "0.32.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96619609a54d638872db136f56941d34e2a00bb0acf3fa783a90d6b96a093ba2"
|
||||
checksum = "7cee647393af53c750e15dcbf7781cdd2e550b246bde76e46c326e7ea3c73773"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -3301,9 +3314,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.7"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
|
||||
checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
@@ -3497,9 +3510,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.6"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
|
||||
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
@@ -3669,7 +3682,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"cfg-if 1.0.0",
|
||||
"futures-util",
|
||||
"getrandom 0.2.6",
|
||||
"getrandom 0.2.4",
|
||||
"http-client",
|
||||
"http-types",
|
||||
"log",
|
||||
@@ -3682,9 +3695,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.91"
|
||||
version = "1.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
|
||||
checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3731,9 +3744,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
@@ -3854,9 +3867,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.17.0"
|
||||
version = "1.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
|
||||
checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
@@ -3872,9 +3885,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "trust-dns-proto"
|
||||
version = "0.21.2"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c31f240f59877c3d4bb3b3ea0ec5a6a0cff07323580ff8c7a605cd7d08b255d"
|
||||
checksum = "2861b3ed517888174d13909e675c4e94b3291867512068be59d76533e4d1270c"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"cfg-if 1.0.0",
|
||||
@@ -3887,7 +3900,7 @@ dependencies = [
|
||||
"ipnet",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"rand 0.8.4",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
@@ -3896,9 +3909,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "trust-dns-resolver"
|
||||
version = "0.21.2"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4ba72c2ea84515690c9fcef4c6c660bb9df3036ed1051686de84605b74fd558"
|
||||
checksum = "d9e737a252a617bd4774649e245dbf705e207275db0893b9fa824d49f074fc1c"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"futures-util",
|
||||
@@ -4025,7 +4038,7 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom 0.2.6",
|
||||
"getrandom 0.2.4",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -4088,9 +4101,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.80"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
|
||||
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -4098,9 +4111,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.80"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
|
||||
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
@@ -4113,9 +4126,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.30"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2"
|
||||
checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
@@ -4125,9 +4138,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.80"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
|
||||
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -4135,9 +4148,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.80"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
|
||||
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4148,15 +4161,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.80"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
|
||||
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.57"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
|
||||
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -4229,15 +4242,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.34.0"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825"
|
||||
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
|
||||
dependencies = [
|
||||
"windows_aarch64_msvc 0.34.0",
|
||||
"windows_i686_gnu 0.34.0",
|
||||
"windows_i686_msvc 0.34.0",
|
||||
"windows_x86_64_gnu 0.34.0",
|
||||
"windows_x86_64_msvc 0.34.0",
|
||||
"windows_aarch64_msvc 0.32.0",
|
||||
"windows_i686_gnu 0.32.0",
|
||||
"windows_i686_msvc 0.32.0",
|
||||
"windows_x86_64_gnu 0.32.0",
|
||||
"windows_x86_64_msvc 0.32.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4248,9 +4261,9 @@ checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.34.0"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d"
|
||||
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@@ -4260,9 +4273,9 @@ checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.34.0"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed"
|
||||
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@@ -4272,9 +4285,9 @@ checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.34.0"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956"
|
||||
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@@ -4284,9 +4297,9 @@ checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.34.0"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4"
|
||||
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@@ -4296,9 +4309,9 @@ checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.34.0"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
|
||||
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
@@ -4346,9 +4359,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.3.2"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17"
|
||||
checksum = "81e8f13fef10b63c06356d65d416b070798ddabcadc10d3ece0c5be9b3c7eddb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4358,12 +4371,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "0.6.2"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d"
|
||||
checksum = "e6fa4aa90e99fb8d701bda16fb040d8ed2f9c7176fb44de750e880a74b580315"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crc32fast",
|
||||
"crossbeam-utils",
|
||||
"flate2",
|
||||
]
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.77.0"
|
||||
version = "1.76.0"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
@@ -19,7 +19,7 @@ ansi_term = { version = "0.12.1", optional = true }
|
||||
anyhow = "1"
|
||||
async-imap = { git = "https://github.com/async-email/async-imap" }
|
||||
async-native-tls = { version = "0.3" }
|
||||
async-smtp = { git = "https://github.com/async-email/async-smtp", branch="master", default-features=false, features = ["smtp-transport", "socks5"] }
|
||||
async-smtp = { git = "https://github.com/async-email/async-smtp", branch="master", features = ["socks5"] }
|
||||
async-std-resolver = "0.21"
|
||||
async-std = { version = "1" }
|
||||
async-tar = { version = "0.4", default-features=false }
|
||||
@@ -75,7 +75,7 @@ humansize = "1"
|
||||
qrcodegen = "1.7.0"
|
||||
tagger = "4.3.3"
|
||||
textwrap = "0.15.0"
|
||||
zip = { version = "0.6.2", default-features = false, features = ["deflate"] }
|
||||
zip = { version = "0.6.0", default-features = false, features = ["deflate"] }
|
||||
|
||||
[dev-dependencies]
|
||||
ansi_term = "0.12.0"
|
||||
@@ -124,10 +124,6 @@ harness = false
|
||||
name = "get_chat_msgs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "get_chatlist"
|
||||
harness = false
|
||||
|
||||
[features]
|
||||
default = ["vendored"]
|
||||
internals = []
|
||||
|
||||
@@ -27,12 +27,10 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
(0..len).map(|i| chatlist.get_chat_id(i).unwrap()).collect()
|
||||
});
|
||||
|
||||
c.bench_function("chat::get_chat_msgs (load messages from 10 chats)", |b| {
|
||||
c.bench_function("Load all chats", |b| {
|
||||
b.to_async(AsyncStdExecutor)
|
||||
.iter(|| get_chat_msgs_benchmark(black_box(&path.as_ref()), black_box(&chats)))
|
||||
});
|
||||
} else {
|
||||
println!("env var not set: DELTACHAT_BENCHMARK_DATABASE");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
use criterion::async_executor::AsyncStdExecutor;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
use deltachat::chatlist::Chatlist;
|
||||
use deltachat::context::Context;
|
||||
|
||||
async fn get_chat_list_benchmark(context: &Context) {
|
||||
Chatlist::try_load(&context, 0, None, None).await.unwrap();
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
// To enable this benchmark, set `DELTACHAT_BENCHMARK_DATABASE` to some large database with many
|
||||
// messages, such as your primary account.
|
||||
if let Ok(path) = std::env::var("DELTACHAT_BENCHMARK_DATABASE") {
|
||||
let context =
|
||||
async_std::task::block_on(async { Context::new(path.into(), 100).await.unwrap() });
|
||||
c.bench_function("chatlist:try_load (Get Chatlist)", |b| {
|
||||
b.to_async(AsyncStdExecutor)
|
||||
.iter(|| get_chat_list_benchmark(black_box(&context)))
|
||||
});
|
||||
} else {
|
||||
println!("env var not set: DELTACHAT_BENCHMARK_DATABASE");
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
||||
@@ -20,8 +20,6 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
c.bench_function("search hello", |b| {
|
||||
b.iter(|| block_on(async { search_benchmark(black_box(&path)).await }))
|
||||
});
|
||||
} else {
|
||||
println!("env var not set: DELTACHAT_BENCHMARK_DATABASE");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.77.0"
|
||||
version = "1.76.0"
|
||||
description = "Deltachat FFI"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2018"
|
||||
|
||||
@@ -1949,19 +1949,18 @@ pub unsafe extern "C" fn dc_block_contact(
|
||||
contact_id: u32,
|
||||
block: libc::c_int,
|
||||
) {
|
||||
let contact_id = ContactId::new(contact_id);
|
||||
if context.is_null() || contact_id.is_special() {
|
||||
if context.is_null() || contact_id <= constants::DC_CONTACT_ID_LAST_SPECIAL.to_u32() {
|
||||
eprintln!("ignoring careless call to dc_block_contact()");
|
||||
return;
|
||||
}
|
||||
let ctx = &*context;
|
||||
block_on(async move {
|
||||
if block == 0 {
|
||||
Contact::unblock(ctx, contact_id)
|
||||
Contact::unblock(ctx, ContactId::new(contact_id))
|
||||
.await
|
||||
.ok_or_log_msg(ctx, "Can't unblock contact");
|
||||
} else {
|
||||
Contact::block(ctx, contact_id)
|
||||
Contact::block(ctx, ContactId::new(contact_id))
|
||||
.await
|
||||
.ok_or_log_msg(ctx, "Can't block contact");
|
||||
}
|
||||
@@ -1995,15 +1994,14 @@ pub unsafe extern "C" fn dc_delete_contact(
|
||||
context: *mut dc_context_t,
|
||||
contact_id: u32,
|
||||
) -> libc::c_int {
|
||||
let contact_id = ContactId::new(contact_id);
|
||||
if context.is_null() || contact_id.is_special() {
|
||||
if context.is_null() || contact_id <= constants::DC_CONTACT_ID_LAST_SPECIAL.to_u32() {
|
||||
eprintln!("ignoring careless call to dc_delete_contact()");
|
||||
return 0;
|
||||
}
|
||||
let ctx = &*context;
|
||||
|
||||
block_on(async move {
|
||||
match Contact::delete(ctx, contact_id).await {
|
||||
match Contact::delete(ctx, ContactId::new(contact_id)).await {
|
||||
Ok(_) => 1,
|
||||
Err(_) => 0,
|
||||
}
|
||||
|
||||
@@ -176,9 +176,6 @@ just clone and start adapting things to your need.
|
||||
|
||||
- older devices might not have the newest js features in their webview,
|
||||
you may want to transpile your code down to an older js version eg. with https://babeljs.io
|
||||
- viewport and scaling features are implementation specific,
|
||||
if you want to have an explicit behavior, you can add eg.
|
||||
`<meta name="viewport" content="initial-scale=1; user-scalable=no">` to your Webxdc
|
||||
- there are tons of ideas for enhancements of the API and the file format,
|
||||
eg. in the future, we will may define icon- and manifest-files,
|
||||
allow to aggregate the state or add metadata.
|
||||
|
||||
@@ -210,7 +210,7 @@ async fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
contact_id,
|
||||
msgtext.unwrap_or_default(),
|
||||
if msg.has_html() { "[HAS-HTML]️" } else { "" },
|
||||
if msg.get_from_id() == ContactId::SELF {
|
||||
if msg.get_from_id() == DC_CONTACT_ID_SELF {
|
||||
""
|
||||
} else if msg.get_state() == MessageState::InSeen {
|
||||
"[SEEN]"
|
||||
@@ -297,7 +297,7 @@ async fn log_contactlist(context: &Context, contacts: &[ContactId]) -> Result<()
|
||||
let peerstate = Peerstate::from_addr(context, addr)
|
||||
.await
|
||||
.expect("peerstate error");
|
||||
if peerstate.is_some() && *contact_id != ContactId::SELF {
|
||||
if peerstate.is_some() && *contact_id != DC_CONTACT_ID_SELF {
|
||||
line2 = format!(
|
||||
", prefer-encrypt={}",
|
||||
peerstate.as_ref().unwrap().prefer_encrypt
|
||||
@@ -932,24 +932,13 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
"listmsgs" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <query> missing.");
|
||||
|
||||
let query = format!("{} {}", arg1, arg2).trim().to_string();
|
||||
let chat = sel_chat.as_ref().map(|sel_chat| sel_chat.get_id());
|
||||
let time_start = std::time::SystemTime::now();
|
||||
let msglist = context.search_msgs(chat, &query).await?;
|
||||
let msglist = context.search_msgs(chat, arg1).await?;
|
||||
let time_needed = time_start.elapsed().unwrap_or_default();
|
||||
|
||||
log_msglist(&context, &msglist).await?;
|
||||
println!(
|
||||
"{}{} messages for {}search of \"{}\"",
|
||||
msglist.len(),
|
||||
if msglist.len() == 1000 { "+" } else { "" },
|
||||
if chat.is_none() {
|
||||
"global "
|
||||
} else {
|
||||
"in-chat-"
|
||||
},
|
||||
query,
|
||||
);
|
||||
println!("{} messages.", msglist.len());
|
||||
println!("{:?} to create this list", time_needed);
|
||||
}
|
||||
"draft" => {
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.60.0
|
||||
1.59.0
|
||||
|
||||
@@ -8,7 +8,7 @@ set -e -x
|
||||
#
|
||||
# Avoid using rustup here as it depends on reading /proc/self/exe and
|
||||
# has problems running under QEMU.
|
||||
RUST_VERSION=1.60.0
|
||||
RUST_VERSION=1.59.0
|
||||
|
||||
curl "https://static.rust-lang.org/dist/rust-${RUST_VERSION}-$(uname -m)-unknown-linux-gnu.tar.gz" | tar xz
|
||||
cd "rust-${RUST_VERSION}-$(uname -m)-unknown-linux-gnu"
|
||||
|
||||
@@ -8,7 +8,7 @@ set -e -x
|
||||
#
|
||||
# Avoid using rustup here as it depends on reading /proc/self/exe and
|
||||
# has problems running under QEMU.
|
||||
RUST_VERSION=1.60.0
|
||||
RUST_VERSION=1.59.0
|
||||
|
||||
curl "https://static.rust-lang.org/dist/rust-${RUST_VERSION}-$(uname -m)-unknown-linux-gnu.tar.gz" | tar xz
|
||||
cd "rust-${RUST_VERSION}-$(uname -m)-unknown-linux-gnu"
|
||||
|
||||
330
src/chat.rs
330
src/chat.rs
@@ -16,7 +16,8 @@ use crate::color::str_to_color;
|
||||
use crate::config::Config;
|
||||
use crate::constants::{
|
||||
Blocked, Chattype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK, DC_CHAT_ID_LAST_SPECIAL,
|
||||
DC_CHAT_ID_TRASH, DC_GCM_ADDDAYMARKER, DC_GCM_INFO_ONLY, DC_RESEND_USER_AVATAR_DAYS,
|
||||
DC_CHAT_ID_TRASH, DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_INFO, DC_CONTACT_ID_LAST_SPECIAL,
|
||||
DC_CONTACT_ID_SELF, DC_GCM_ADDDAYMARKER, DC_GCM_INFO_ONLY, DC_RESEND_USER_AVATAR_DAYS,
|
||||
};
|
||||
use crate::contact::{addr_cmp, Contact, ContactId, Origin, VerifiedStatus};
|
||||
use crate::context::Context;
|
||||
@@ -26,7 +27,7 @@ use crate::dc_tools::{
|
||||
dc_create_smeared_timestamps, dc_get_abs_path, dc_gm2local_offset, improve_single_line_input,
|
||||
time, IsNoneOrEmpty,
|
||||
};
|
||||
use crate::ephemeral::Timer as EphemeralTimer;
|
||||
use crate::ephemeral::{delete_expired_messages, schedule_ephemeral_task, Timer as EphemeralTimer};
|
||||
use crate::events::EventType;
|
||||
use crate::html::new_html_mimepart;
|
||||
use crate::job::{self, Action};
|
||||
@@ -198,7 +199,7 @@ impl ChatId {
|
||||
}
|
||||
None => {
|
||||
if Contact::real_exists_by_id(context, contact_id).await?
|
||||
|| contact_id == ContactId::SELF
|
||||
|| contact_id == DC_CONTACT_ID_SELF
|
||||
{
|
||||
let chat_id =
|
||||
ChatIdBlocked::get_for_contact(context, contact_id, create_blocked)
|
||||
@@ -296,7 +297,7 @@ impl ChatId {
|
||||
}
|
||||
Chattype::Single => {
|
||||
for contact_id in get_chat_contacts(context, self).await? {
|
||||
if contact_id != ContactId::SELF {
|
||||
if contact_id != DC_CONTACT_ID_SELF {
|
||||
info!(
|
||||
context,
|
||||
"Blocking the contact {} to block 1:1 chat", contact_id
|
||||
@@ -339,7 +340,7 @@ impl ChatId {
|
||||
// Previously accepting a chat literally created a chat because unaccepted chats
|
||||
// went to "contact requests" list rather than normal chatlist.
|
||||
for contact_id in get_chat_contacts(context, self).await? {
|
||||
if contact_id != ContactId::SELF {
|
||||
if contact_id != DC_CONTACT_ID_SELF {
|
||||
Contact::scaleup_origin_by_id(context, contact_id, Origin::CreateChat)
|
||||
.await?;
|
||||
}
|
||||
@@ -445,7 +446,6 @@ impl ChatId {
|
||||
cmd,
|
||||
dc_create_smeared_timestamp(context).await,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@@ -464,7 +464,7 @@ impl ChatId {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
self.add_protection_msg(context, protect, chat.is_promoted(), ContactId::SELF)
|
||||
self.add_protection_msg(context, protect, chat.is_promoted(), DC_CONTACT_ID_SELF)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -501,15 +501,14 @@ impl ChatId {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Unarchives a chat that is archived and not muted.
|
||||
// Needed when a message is added to a chat so that the chat gets a normal visibility again.
|
||||
// Sending an appropriate event is up to the caller.
|
||||
pub async fn unarchive_if_not_muted(self, context: &Context) -> Result<()> {
|
||||
// note that unarchive() is not the same as set_visibility(Normal) -
|
||||
// eg. unarchive() does not modify pinned chats and does not send events.
|
||||
pub async fn unarchive(self, context: &Context) -> Result<()> {
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE chats SET archived=0 WHERE id=? AND archived=1 AND NOT(muted_until=-1 OR muted_until>?)",
|
||||
paramsv![self, time()],
|
||||
"UPDATE chats SET archived=0 WHERE id=? and archived=1",
|
||||
paramsv![self],
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
@@ -715,7 +714,7 @@ impl ChatId {
|
||||
VALUES (?,?,?, ?,?,?,?,?,?);",
|
||||
paramsv![
|
||||
self,
|
||||
ContactId::SELF,
|
||||
DC_CONTACT_ID_SELF,
|
||||
time(),
|
||||
msg.viewtype,
|
||||
MessageState::OutDraft,
|
||||
@@ -862,7 +861,7 @@ impl ChatId {
|
||||
for contact_id in get_chat_contacts(context, self)
|
||||
.await?
|
||||
.iter()
|
||||
.filter(|&contact_id| !contact_id.is_special())
|
||||
.filter(|&contact_id| *contact_id > DC_CONTACT_ID_LAST_SPECIAL)
|
||||
{
|
||||
let contact = Contact::load_from_db(context, *contact_id).await?;
|
||||
let addr = contact.get_addr();
|
||||
@@ -1088,7 +1087,7 @@ impl Chat {
|
||||
pub(crate) async fn is_self_in_chat(&self, context: &Context) -> Result<bool> {
|
||||
match self.typ {
|
||||
Chattype::Single | Chattype::Broadcast | Chattype::Mailinglist => Ok(true),
|
||||
Chattype::Group => is_contact_in_chat(context, self.id, ContactId::SELF).await,
|
||||
Chattype::Group => is_contact_in_chat(context, self.id, DC_CONTACT_ID_SELF).await,
|
||||
Chattype::Undefined => Ok(false),
|
||||
}
|
||||
}
|
||||
@@ -1242,7 +1241,7 @@ impl Chat {
|
||||
|
||||
if !self.can_send(context).await? {
|
||||
if self.typ == Chattype::Group
|
||||
&& !is_contact_in_chat(context, self.id, ContactId::SELF).await?
|
||||
&& !is_contact_in_chat(context, self.id, DC_CONTACT_ID_SELF).await?
|
||||
{
|
||||
context.emit_event(EventType::ErrorSelfNotInGroup(
|
||||
"Cannot send message; self not in group.".into(),
|
||||
@@ -1254,7 +1253,11 @@ impl Chat {
|
||||
}
|
||||
}
|
||||
|
||||
let from = context.get_primary_addr().await?;
|
||||
let from = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.context("Cannot prepare message for sending, address is not configured.")?;
|
||||
|
||||
let new_rfc724_mid = {
|
||||
let grpid = match self.typ {
|
||||
Chattype::Group => Some(self.grpid.as_str()),
|
||||
@@ -1352,7 +1355,7 @@ impl Chat {
|
||||
VALUES (?,?,?, ?,?,1);",
|
||||
paramsv![
|
||||
timestamp,
|
||||
ContactId::SELF,
|
||||
DC_CONTACT_ID_SELF,
|
||||
self.id,
|
||||
msg.param.get_float(Param::SetLatitude).unwrap_or_default(),
|
||||
msg.param.get_float(Param::SetLongitude).unwrap_or_default(),
|
||||
@@ -1404,7 +1407,7 @@ impl Chat {
|
||||
paramsv![
|
||||
new_rfc724_mid,
|
||||
self.id,
|
||||
ContactId::SELF,
|
||||
DC_CONTACT_ID_SELF,
|
||||
to_id as i32,
|
||||
timestamp,
|
||||
msg.viewtype,
|
||||
@@ -1424,6 +1427,7 @@ impl Chat {
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
schedule_ephemeral_task(context).await;
|
||||
msg.id = update_msg_id;
|
||||
} else {
|
||||
let raw_id = context
|
||||
@@ -1452,7 +1456,7 @@ impl Chat {
|
||||
paramsv![
|
||||
new_rfc724_mid,
|
||||
self.id,
|
||||
ContactId::SELF,
|
||||
DC_CONTACT_ID_SELF,
|
||||
to_id as i32,
|
||||
timestamp,
|
||||
msg.viewtype,
|
||||
@@ -1473,7 +1477,7 @@ impl Chat {
|
||||
.await?;
|
||||
msg.id = MsgId::new(u32::try_from(raw_id)?);
|
||||
}
|
||||
context.interrupt_ephemeral_task().await;
|
||||
schedule_ephemeral_task(context).await;
|
||||
Ok(msg.id)
|
||||
}
|
||||
}
|
||||
@@ -1581,7 +1585,7 @@ pub struct ChatInfo {
|
||||
|
||||
pub(crate) async fn update_saved_messages_icon(context: &Context) -> Result<()> {
|
||||
// if there is no saved-messages chat, there is nothing to update. this is no error.
|
||||
if let Some(chat_id) = ChatId::lookup_by_contact(context, ContactId::SELF).await? {
|
||||
if let Some(chat_id) = ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF).await? {
|
||||
let icon = include_bytes!("../assets/icon-saved-messages.png");
|
||||
let blob = BlobObject::create(context, "icon-saved-messages.png", icon).await?;
|
||||
let icon = blob.as_name().to_string();
|
||||
@@ -1595,7 +1599,7 @@ pub(crate) async fn update_saved_messages_icon(context: &Context) -> Result<()>
|
||||
|
||||
pub(crate) async fn update_device_icon(context: &Context) -> Result<()> {
|
||||
// if there is no device-chat, there is nothing to update. this is no error.
|
||||
if let Some(chat_id) = ChatId::lookup_by_contact(context, ContactId::DEVICE).await? {
|
||||
if let Some(chat_id) = ChatId::lookup_by_contact(context, DC_CONTACT_ID_DEVICE).await? {
|
||||
let icon = include_bytes!("../assets/icon-device.png");
|
||||
let blob = BlobObject::create(context, "icon-device.png", icon).await?;
|
||||
let icon = blob.as_name().to_string();
|
||||
@@ -1604,7 +1608,7 @@ pub(crate) async fn update_device_icon(context: &Context) -> Result<()> {
|
||||
chat.param.set(Param::ProfileImage, &icon);
|
||||
chat.update_param(context).await?;
|
||||
|
||||
let mut contact = Contact::load_from_db(context, ContactId::DEVICE).await?;
|
||||
let mut contact = Contact::load_from_db(context, DC_CONTACT_ID_DEVICE).await?;
|
||||
contact.param.set(Param::ProfileImage, icon);
|
||||
contact.update_param(context).await?;
|
||||
}
|
||||
@@ -1647,13 +1651,13 @@ async fn update_special_chat_name(
|
||||
pub(crate) async fn update_special_chat_names(context: &Context) -> Result<()> {
|
||||
update_special_chat_name(
|
||||
context,
|
||||
ContactId::DEVICE,
|
||||
DC_CONTACT_ID_DEVICE,
|
||||
stock_str::device_messages(context).await,
|
||||
)
|
||||
.await?;
|
||||
update_special_chat_name(
|
||||
context,
|
||||
ContactId::SELF,
|
||||
DC_CONTACT_ID_SELF,
|
||||
stock_str::saved_messages(context).await,
|
||||
)
|
||||
.await?;
|
||||
@@ -1683,7 +1687,7 @@ impl ChatIdBlocked {
|
||||
) -> Result<Option<Self>> {
|
||||
ensure!(context.sql.is_open().await, "Database not available");
|
||||
ensure!(
|
||||
contact_id != ContactId::UNDEFINED,
|
||||
contact_id > ContactId::new(0),
|
||||
"Invalid contact id requested"
|
||||
);
|
||||
|
||||
@@ -1719,7 +1723,7 @@ impl ChatIdBlocked {
|
||||
) -> Result<Self> {
|
||||
ensure!(context.sql.is_open().await, "Database not available");
|
||||
ensure!(
|
||||
contact_id != ContactId::UNDEFINED,
|
||||
contact_id > ContactId::new(0),
|
||||
"Invalid contact id requested"
|
||||
);
|
||||
|
||||
@@ -1732,10 +1736,10 @@ impl ChatIdBlocked {
|
||||
let chat_name = contact.get_display_name().to_string();
|
||||
let mut params = Params::new();
|
||||
match contact_id {
|
||||
ContactId::SELF => {
|
||||
DC_CONTACT_ID_SELF => {
|
||||
params.set_int(Param::Selftalk, 1);
|
||||
}
|
||||
ContactId::DEVICE => {
|
||||
DC_CONTACT_ID_DEVICE => {
|
||||
params.set_int(Param::Devicetalk, 1);
|
||||
}
|
||||
_ => (),
|
||||
@@ -1776,8 +1780,8 @@ impl ChatIdBlocked {
|
||||
.await?;
|
||||
|
||||
match contact_id {
|
||||
ContactId::SELF => update_saved_messages_icon(context).await?,
|
||||
ContactId::DEVICE => update_device_icon(context).await?,
|
||||
DC_CONTACT_ID_SELF => update_saved_messages_icon(context).await?,
|
||||
DC_CONTACT_ID_DEVICE => update_device_icon(context).await?,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -1891,7 +1895,7 @@ async fn prepare_msg_common(
|
||||
msg.state = change_state_to;
|
||||
|
||||
prepare_msg_blob(context, msg).await?;
|
||||
chat_id.unarchive_if_not_muted(context).await?;
|
||||
chat_id.unarchive(context).await?;
|
||||
msg.id = chat
|
||||
.prepare_msg_raw(
|
||||
context,
|
||||
@@ -1913,8 +1917,8 @@ pub async fn is_contact_in_chat(
|
||||
) -> Result<bool> {
|
||||
// this function works for group and for normal chats, however, it is more useful
|
||||
// for group chats.
|
||||
// ContactId::SELF may be used to check, if the user itself is in a group
|
||||
// chat (ContactId::SELF is not added to normal chats)
|
||||
// DC_CONTACT_ID_SELF may be used to check, if the user itself is in a group
|
||||
// chat (DC_CONTACT_ID_SELF is not added to normal chats)
|
||||
|
||||
let exists = context
|
||||
.sql
|
||||
@@ -1982,7 +1986,7 @@ async fn send_msg_inner(context: &Context, chat_id: ChatId, msg: &mut Message) -
|
||||
});
|
||||
|
||||
if msg.param.exists(Param::SetLatitude) {
|
||||
context.emit_event(EventType::LocationChanged(Some(ContactId::SELF)));
|
||||
context.emit_event(EventType::LocationChanged(Some(DC_CONTACT_ID_SELF)));
|
||||
}
|
||||
|
||||
context.interrupt_smtp(InterruptInfo::new(false)).await;
|
||||
@@ -2043,7 +2047,10 @@ async fn create_send_msg_job(context: &Context, msg_id: MsgId) -> Result<Option<
|
||||
|
||||
let mut recipients = mimefactory.recipients();
|
||||
|
||||
let from = context.get_primary_addr().await.unwrap_or_default();
|
||||
let from = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
let lowercase_from = from.to_lowercase();
|
||||
|
||||
// Send BCC to self if it is enabled and we are not going to
|
||||
@@ -2198,6 +2205,23 @@ pub async fn get_chat_msgs(
|
||||
flags: u32,
|
||||
marker1before: Option<MsgId>,
|
||||
) -> Result<Vec<ChatItem>> {
|
||||
match delete_expired_messages(context).await {
|
||||
Err(err) => warn!(context, "Failed to delete expired messages: {}", err),
|
||||
Ok(messages_deleted) => {
|
||||
if messages_deleted {
|
||||
// Trigger reload of chatlist.
|
||||
//
|
||||
// On desktop chatlist is always shown on the side,
|
||||
// and it is important to update the last message shown
|
||||
// there.
|
||||
context.emit_event(EventType::MsgsChanged {
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let process_row = if (flags & DC_GCM_INFO_ONLY) != 0 {
|
||||
|row: &rusqlite::Row| {
|
||||
// is_info logic taken from Message.is_info()
|
||||
@@ -2206,8 +2230,8 @@ pub async fn get_chat_msgs(
|
||||
row.get::<_, ContactId>("from_id")?,
|
||||
row.get::<_, ContactId>("to_id")?,
|
||||
);
|
||||
let is_info_msg: bool = from_id == ContactId::INFO
|
||||
|| to_id == ContactId::INFO
|
||||
let is_info_msg: bool = from_id == DC_CONTACT_ID_INFO
|
||||
|| to_id == DC_CONTACT_ID_INFO
|
||||
|| match Params::from_str(¶ms) {
|
||||
Ok(p) => {
|
||||
let cmd = p.get_cmd();
|
||||
@@ -2217,40 +2241,30 @@ pub async fn get_chat_msgs(
|
||||
};
|
||||
|
||||
Ok((
|
||||
row.get::<_, i64>("timestamp")?,
|
||||
row.get::<_, MsgId>("id")?,
|
||||
row.get::<_, i64>("timestamp")?,
|
||||
!is_info_msg,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
|row: &rusqlite::Row| {
|
||||
Ok((
|
||||
row.get::<_, i64>("timestamp")?,
|
||||
row.get::<_, MsgId>("id")?,
|
||||
row.get::<_, i64>("timestamp")?,
|
||||
false,
|
||||
))
|
||||
}
|
||||
};
|
||||
let process_rows = |rows: rusqlite::MappedRows<_>| {
|
||||
// It is faster to sort here rather than
|
||||
// let sqlite execute an ORDER BY clause.
|
||||
let mut sorted_rows = Vec::new();
|
||||
for row in rows {
|
||||
let (ts, curr_id, exclude_message): (i64, MsgId, bool) = row?;
|
||||
if !exclude_message {
|
||||
sorted_rows.push((ts, curr_id));
|
||||
}
|
||||
}
|
||||
sorted_rows.sort_unstable();
|
||||
|
||||
let mut ret = Vec::new();
|
||||
let mut last_day = 0;
|
||||
let cnv_to_local = dc_gm2local_offset();
|
||||
let marker1 = marker1before.unwrap_or_else(MsgId::new_unset);
|
||||
|
||||
for (ts, curr_id) in sorted_rows {
|
||||
if curr_id == marker1 {
|
||||
ret.push(ChatItem::Marker1);
|
||||
for row in rows {
|
||||
let (curr_id, ts, exclude_message): (MsgId, i64, bool) = row?;
|
||||
if let Some(marker_id) = marker1before {
|
||||
if curr_id == marker_id {
|
||||
ret.push(ChatItem::Marker1);
|
||||
}
|
||||
}
|
||||
if (flags & DC_GCM_ADDDAYMARKER) != 0 {
|
||||
let curr_local_timestamp = ts + cnv_to_local;
|
||||
@@ -2262,7 +2276,9 @@ pub async fn get_chat_msgs(
|
||||
last_day = curr_day;
|
||||
}
|
||||
}
|
||||
ret.push(ChatItem::Message { msg_id: curr_id });
|
||||
if !exclude_message {
|
||||
ret.push(ChatItem::Message { msg_id: curr_id });
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
};
|
||||
@@ -2280,8 +2296,9 @@ pub async fn get_chat_msgs(
|
||||
m.param GLOB \"*S=*\"
|
||||
OR m.from_id == ?
|
||||
OR m.to_id == ?
|
||||
);",
|
||||
paramsv![chat_id, ContactId::INFO, ContactId::INFO],
|
||||
)
|
||||
ORDER BY m.timestamp, m.id;",
|
||||
paramsv![chat_id, DC_CONTACT_ID_INFO, DC_CONTACT_ID_INFO],
|
||||
process_row,
|
||||
process_rows,
|
||||
)
|
||||
@@ -2290,10 +2307,11 @@ pub async fn get_chat_msgs(
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT m.id AS id, m.timestamp AS timestamp
|
||||
"SELECT (m.id+1) AS id, (m.timestamp+1) AS timestamp
|
||||
FROM msgs m
|
||||
WHERE m.chat_id=?
|
||||
AND m.hidden=0;",
|
||||
AND m.hidden=0
|
||||
ORDER BY timestamp, id;",
|
||||
paramsv![chat_id],
|
||||
process_row,
|
||||
process_rows,
|
||||
@@ -2563,8 +2581,8 @@ pub async fn create_group_chat(
|
||||
.await?;
|
||||
|
||||
let chat_id = ChatId::new(u32::try_from(row_id)?);
|
||||
if !is_contact_in_chat(context, chat_id, ContactId::SELF).await? {
|
||||
add_to_chat_contacts_table(context, chat_id, ContactId::SELF).await?;
|
||||
if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await? {
|
||||
add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await?;
|
||||
}
|
||||
|
||||
context.emit_event(EventType::MsgsChanged {
|
||||
@@ -2693,13 +2711,13 @@ pub(crate) async fn add_contact_to_chat_ex(
|
||||
chat_id
|
||||
);
|
||||
ensure!(
|
||||
Contact::real_exists_by_id(context, contact_id).await? || contact_id == ContactId::SELF,
|
||||
Contact::real_exists_by_id(context, contact_id).await? || contact_id == DC_CONTACT_ID_SELF,
|
||||
"invalid contact_id {} for adding to group",
|
||||
contact_id
|
||||
);
|
||||
ensure!(!chat.is_mailing_list(), "Mailing lists can't be changed");
|
||||
ensure!(
|
||||
chat.typ != Chattype::Broadcast || contact_id != ContactId::SELF,
|
||||
chat.typ != Chattype::Broadcast || contact_id != DC_CONTACT_ID_SELF,
|
||||
"Cannot add SELF to broadcast."
|
||||
);
|
||||
|
||||
@@ -2716,9 +2734,12 @@ pub(crate) async fn add_contact_to_chat_ex(
|
||||
context.sync_qr_code_tokens(Some(chat_id)).await?;
|
||||
context.send_sync_msg().await?;
|
||||
}
|
||||
let self_addr = context.get_primary_addr().await.unwrap_or_default();
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
if addr_cmp(contact.get_addr(), &self_addr) {
|
||||
// ourself is added using ContactId::SELF, do not add this address explicitly.
|
||||
// ourself is added using DC_CONTACT_ID_SELF, do not add this address explicitly.
|
||||
// if SELF is not in the group, members cannot be added at all.
|
||||
warn!(
|
||||
context,
|
||||
@@ -2751,7 +2772,7 @@ pub(crate) async fn add_contact_to_chat_ex(
|
||||
msg.viewtype = Viewtype::Text;
|
||||
|
||||
msg.text =
|
||||
Some(stock_str::msg_add_member(context, contact.get_addr(), ContactId::SELF).await);
|
||||
Some(stock_str::msg_add_member(context, contact.get_addr(), DC_CONTACT_ID_SELF).await);
|
||||
msg.param.set_cmd(SystemMessage::MemberAddedToGroup);
|
||||
msg.param.set(Param::Arg, contact.get_addr());
|
||||
msg.param.set_int(Param::Arg2, from_handshake.into());
|
||||
@@ -2777,7 +2798,7 @@ pub(crate) async fn shall_attach_selfavatar(context: &Context, chat_id: ChatId)
|
||||
FROM chats_contacts cc
|
||||
LEFT JOIN contacts c ON c.id=cc.contact_id
|
||||
WHERE cc.chat_id=? AND cc.contact_id!=?;",
|
||||
paramsv![chat_id, ContactId::SELF],
|
||||
paramsv![chat_id, DC_CONTACT_ID_SELF],
|
||||
|row| Ok(row.get::<_, i64>(0)),
|
||||
|rows| {
|
||||
let mut needs_attach = false;
|
||||
@@ -2862,7 +2883,7 @@ pub async fn remove_contact_from_chat(
|
||||
chat_id
|
||||
);
|
||||
ensure!(
|
||||
!contact_id.is_special() || contact_id == ContactId::SELF,
|
||||
contact_id > DC_CONTACT_ID_LAST_SPECIAL || contact_id == DC_CONTACT_ID_SELF,
|
||||
"Cannot remove special contact"
|
||||
);
|
||||
|
||||
@@ -2881,16 +2902,16 @@ pub async fn remove_contact_from_chat(
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id).await {
|
||||
if chat.typ == Chattype::Group && chat.is_promoted() {
|
||||
msg.viewtype = Viewtype::Text;
|
||||
if contact.id == ContactId::SELF {
|
||||
if contact.id == DC_CONTACT_ID_SELF {
|
||||
set_group_explicitly_left(context, &chat.grpid).await?;
|
||||
msg.text =
|
||||
Some(stock_str::msg_group_left(context, ContactId::SELF).await);
|
||||
Some(stock_str::msg_group_left(context, DC_CONTACT_ID_SELF).await);
|
||||
} else {
|
||||
msg.text = Some(
|
||||
stock_str::msg_del_member(
|
||||
context,
|
||||
contact.get_addr(),
|
||||
ContactId::SELF,
|
||||
DC_CONTACT_ID_SELF,
|
||||
)
|
||||
.await,
|
||||
);
|
||||
@@ -2983,7 +3004,8 @@ pub async fn set_chat_name(context: &Context, chat_id: ChatId, new_name: &str) -
|
||||
if chat.is_promoted() && !chat.is_mailing_list() && chat.typ != Chattype::Broadcast {
|
||||
msg.viewtype = Viewtype::Text;
|
||||
msg.text = Some(
|
||||
stock_str::msg_grp_name(context, &chat.name, &new_name, ContactId::SELF).await,
|
||||
stock_str::msg_grp_name(context, &chat.name, &new_name, DC_CONTACT_ID_SELF)
|
||||
.await,
|
||||
);
|
||||
msg.param.set_cmd(SystemMessage::GroupNameChanged);
|
||||
if !chat.name.is_empty() {
|
||||
@@ -3024,7 +3046,7 @@ pub async fn set_chat_profile_image(
|
||||
"Failed to set profile image; group does not exist"
|
||||
);
|
||||
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
|
||||
if !is_contact_in_chat(context, chat_id, ContactId::SELF).await? {
|
||||
if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await? {
|
||||
context.emit_event(EventType::ErrorSelfNotInGroup(
|
||||
"Cannot set chat profile image; self not in group.".into(),
|
||||
));
|
||||
@@ -3036,7 +3058,7 @@ pub async fn set_chat_profile_image(
|
||||
if new_image.as_ref().is_empty() {
|
||||
chat.param.remove(Param::ProfileImage);
|
||||
msg.param.remove(Param::Arg);
|
||||
msg.text = Some(stock_str::msg_grp_img_deleted(context, ContactId::SELF).await);
|
||||
msg.text = Some(stock_str::msg_grp_img_deleted(context, DC_CONTACT_ID_SELF).await);
|
||||
} else {
|
||||
let mut image_blob = match BlobObject::from_path(context, Path::new(new_image.as_ref())) {
|
||||
Ok(blob) => Ok(blob),
|
||||
@@ -3050,7 +3072,7 @@ pub async fn set_chat_profile_image(
|
||||
image_blob.recode_to_avatar_size(context).await?;
|
||||
chat.param.set(Param::ProfileImage, image_blob.as_name());
|
||||
msg.param.set(Param::Arg, image_blob.as_name());
|
||||
msg.text = Some(stock_str::msg_grp_img_changed(context, ContactId::SELF).await);
|
||||
msg.text = Some(stock_str::msg_grp_img_changed(context, DC_CONTACT_ID_SELF).await);
|
||||
}
|
||||
chat.update_param(context).await?;
|
||||
if chat.is_promoted() && !chat.is_mailing_list() {
|
||||
@@ -3072,7 +3094,7 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
|
||||
let mut created_msgs: Vec<MsgId> = Vec::new();
|
||||
let mut curr_timestamp: i64;
|
||||
|
||||
chat_id.unarchive_if_not_muted(context).await?;
|
||||
chat_id.unarchive(context).await?;
|
||||
if let Ok(mut chat) = Chat::load_from_db(context, chat_id).await {
|
||||
ensure!(chat.can_send(context).await?, "cannot send to {}", chat_id);
|
||||
curr_timestamp = dc_create_smeared_timestamps(context, msg_ids.len()).await;
|
||||
@@ -3225,12 +3247,12 @@ pub async fn add_device_msg_with_importance(
|
||||
}
|
||||
|
||||
if let Some(msg) = msg {
|
||||
chat_id = ChatId::get_for_contact(context, ContactId::DEVICE).await?;
|
||||
chat_id = ChatId::get_for_contact(context, DC_CONTACT_ID_DEVICE).await?;
|
||||
|
||||
let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device");
|
||||
msg.try_calc_and_set_dimensions(context).await.ok();
|
||||
prepare_msg_blob(context, msg).await?;
|
||||
chat_id.unarchive_if_not_muted(context).await?;
|
||||
chat_id.unarchive(context).await?;
|
||||
|
||||
let timestamp_sent = dc_create_smeared_timestamp(context).await;
|
||||
|
||||
@@ -3267,8 +3289,8 @@ pub async fn add_device_msg_with_importance(
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?);",
|
||||
paramsv![
|
||||
chat_id,
|
||||
ContactId::DEVICE,
|
||||
ContactId::SELF,
|
||||
DC_CONTACT_ID_DEVICE,
|
||||
DC_CONTACT_ID_SELF,
|
||||
timestamp_sort,
|
||||
timestamp_sent,
|
||||
timestamp_sent, // timestamp_sent equals timestamp_rcvd
|
||||
@@ -3328,7 +3350,7 @@ pub async fn was_device_msg_ever_added(context: &Context, label: &str) -> Result
|
||||
}
|
||||
|
||||
// needed on device-switches during export/import;
|
||||
// - deletion in `msgs` with `ContactId::DEVICE` makes sure,
|
||||
// - deletion in `msgs` with `DC_CONTACT_ID_DEVICE` makes sure,
|
||||
// no wrong information are shown in the device chat
|
||||
// - deletion in `devmsglabels` makes sure,
|
||||
// deleted messages are resetted and useful messages can be added again
|
||||
@@ -3339,7 +3361,7 @@ pub(crate) async fn delete_and_reset_all_device_msgs(context: &Context) -> Resul
|
||||
.sql
|
||||
.execute(
|
||||
"DELETE FROM msgs WHERE from_id=?;",
|
||||
paramsv![ContactId::DEVICE],
|
||||
paramsv![DC_CONTACT_ID_DEVICE],
|
||||
)
|
||||
.await?;
|
||||
context
|
||||
@@ -3358,9 +3380,7 @@ pub(crate) async fn add_info_msg_with_cmd(
|
||||
chat_id: ChatId,
|
||||
text: &str,
|
||||
cmd: SystemMessage,
|
||||
timestamp_sort: i64,
|
||||
// Timestamp to show to the user (if this is None, `timestamp_sort` will be shown to the user)
|
||||
timestamp_sent_rcvd: Option<i64>,
|
||||
timestamp: i64,
|
||||
parent: Option<&Message>,
|
||||
) -> Result<MsgId> {
|
||||
let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device");
|
||||
@@ -3373,15 +3393,12 @@ pub(crate) async fn add_info_msg_with_cmd(
|
||||
|
||||
let row_id =
|
||||
context.sql.insert(
|
||||
"INSERT INTO msgs (chat_id,from_id,to_id,timestamp,timestamp_sent,timestamp_rcvd,type,state,txt,rfc724_mid,ephemeral_timer, param,mime_in_reply_to)
|
||||
VALUES (?,?,?, ?,?,?,?,?, ?,?,?, ?,?);",
|
||||
"INSERT INTO msgs (chat_id,from_id,to_id,timestamp,type,state,txt,rfc724_mid,ephemeral_timer, param,mime_in_reply_to) VALUES (?,?,?, ?,?,?, ?,?,?, ?,?);",
|
||||
paramsv![
|
||||
chat_id,
|
||||
ContactId::INFO,
|
||||
ContactId::INFO,
|
||||
timestamp_sort,
|
||||
timestamp_sent_rcvd.unwrap_or(0),
|
||||
timestamp_sent_rcvd.unwrap_or(0),
|
||||
DC_CONTACT_ID_INFO,
|
||||
DC_CONTACT_ID_INFO,
|
||||
timestamp,
|
||||
Viewtype::Text,
|
||||
MessageState::InNoticed,
|
||||
text,
|
||||
@@ -3411,7 +3428,6 @@ pub(crate) async fn add_info_msg(
|
||||
SystemMessage::Unknown,
|
||||
timestamp,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -3636,7 +3652,7 @@ mod tests {
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo")
|
||||
.await
|
||||
.unwrap();
|
||||
let added = add_contact_to_chat_ex(&t, chat_id, ContactId::SELF, false)
|
||||
let added = add_contact_to_chat_ex(&t, chat_id, DC_CONTACT_ID_SELF, false)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(added, false);
|
||||
@@ -3846,7 +3862,7 @@ mod tests {
|
||||
let bob_msg = bob.get_last_msg().await;
|
||||
let bob_chat_id = bob_msg.chat_id;
|
||||
bob_chat_id.accept(&bob).await?;
|
||||
remove_contact_from_chat(&bob, bob_chat_id, ContactId::SELF).await?;
|
||||
remove_contact_from_chat(&bob, bob_chat_id, DC_CONTACT_ID_SELF).await?;
|
||||
|
||||
let leave_msg = bob.pop_sent_msg().await;
|
||||
alice.recv_msg(&leave_msg).await;
|
||||
@@ -3875,7 +3891,7 @@ mod tests {
|
||||
assert!(removed.is_err());
|
||||
assert_eq!(get_chat_contacts(&ctx, chat.id).await.unwrap().len(), 1);
|
||||
|
||||
let removed = remove_contact_from_chat(&ctx, chat.id, ContactId::SELF).await;
|
||||
let removed = remove_contact_from_chat(&ctx, chat.id, DC_CONTACT_ID_SELF).await;
|
||||
assert!(removed.is_err());
|
||||
assert_eq!(get_chat_contacts(&ctx, chat.id).await.unwrap().len(), 1);
|
||||
}
|
||||
@@ -3884,6 +3900,7 @@ mod tests {
|
||||
async fn test_self_talk() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat = &t.get_self_chat().await;
|
||||
assert_eq!(DC_CONTACT_ID_SELF, ContactId::new(1));
|
||||
assert!(!chat.id.is_special());
|
||||
assert!(chat.is_self_talk());
|
||||
assert!(chat.visibility == ChatVisibility::Normal);
|
||||
@@ -3894,8 +3911,8 @@ mod tests {
|
||||
|
||||
let msg_id = send_text_msg(&t, chat.id, "foo self".to_string()).await?;
|
||||
let msg = Message::load_from_db(&t, msg_id).await?;
|
||||
assert_eq!(msg.from_id, ContactId::SELF);
|
||||
assert_eq!(msg.to_id, ContactId::SELF);
|
||||
assert_eq!(msg.from_id, DC_CONTACT_ID_SELF);
|
||||
assert_eq!(msg.to_id, DC_CONTACT_ID_SELF);
|
||||
assert!(msg.get_showpadlock());
|
||||
|
||||
let sent_msg = t.pop_sent_msg().await;
|
||||
@@ -3904,8 +3921,8 @@ mod tests {
|
||||
let chat = &t2.get_self_chat().await;
|
||||
let msg = t2.get_last_msg_in(chat.id).await;
|
||||
assert_eq!(msg.text, Some("foo self".to_string()));
|
||||
assert_eq!(msg.from_id, ContactId::SELF);
|
||||
assert_eq!(msg.to_id, ContactId::SELF);
|
||||
assert_eq!(msg.from_id, DC_CONTACT_ID_SELF);
|
||||
assert_eq!(msg.to_id, DC_CONTACT_ID_SELF);
|
||||
assert!(msg.get_showpadlock());
|
||||
|
||||
Ok(())
|
||||
@@ -3932,8 +3949,8 @@ mod tests {
|
||||
assert!(msg1.is_ok());
|
||||
let msg1 = msg1.unwrap();
|
||||
assert_eq!(msg1.text.as_ref().unwrap(), "first message");
|
||||
assert_eq!(msg1.from_id, ContactId::DEVICE);
|
||||
assert_eq!(msg1.to_id, ContactId::SELF);
|
||||
assert_eq!(msg1.from_id, DC_CONTACT_ID_DEVICE);
|
||||
assert_eq!(msg1.to_id, DC_CONTACT_ID_SELF);
|
||||
assert!(!msg1.is_info());
|
||||
assert!(!msg1.is_setupmessage());
|
||||
|
||||
@@ -3967,8 +3984,8 @@ mod tests {
|
||||
let msg1 = message::Message::load_from_db(&t, *msg1_id.as_ref().unwrap()).await?;
|
||||
assert_eq!(msg1_id.as_ref().unwrap(), &msg1.id);
|
||||
assert_eq!(msg1.text.as_ref().unwrap(), "first message");
|
||||
assert_eq!(msg1.from_id, ContactId::DEVICE);
|
||||
assert_eq!(msg1.to_id, ContactId::SELF);
|
||||
assert_eq!(msg1.from_id, DC_CONTACT_ID_DEVICE);
|
||||
assert_eq!(msg1.to_id, DC_CONTACT_ID_SELF);
|
||||
assert!(!msg1.is_info());
|
||||
assert!(!msg1.is_setupmessage());
|
||||
|
||||
@@ -4060,7 +4077,7 @@ mod tests {
|
||||
async fn test_device_chat_cannot_sent() {
|
||||
let t = TestContext::new().await;
|
||||
t.update_device_chats().await.unwrap();
|
||||
let device_chat_id = ChatId::get_for_contact(&t, ContactId::DEVICE)
|
||||
let device_chat_id = ChatId::get_for_contact(&t, DC_CONTACT_ID_DEVICE)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -4208,90 +4225,6 @@ mod tests {
|
||||
assert_eq!(chatlist_len(&t, DC_GCL_ARCHIVED_ONLY).await, 1);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_unarchive_if_muted() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
async fn msg_from_bob(t: &TestContext, num: u32) -> Result<()> {
|
||||
dc_receive_imf(
|
||||
t,
|
||||
format!(
|
||||
"From: bob@example.net\n\
|
||||
To: alice@example.org\n\
|
||||
Message-ID: <{}@example.org>\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Date: Sun, 22 Mar 2022 19:37:57 +0000\n\
|
||||
\n\
|
||||
hello\n",
|
||||
num
|
||||
)
|
||||
.as_bytes(),
|
||||
"INBOX",
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
msg_from_bob(&t, 1).await?;
|
||||
let chat_id = t.get_last_msg().await.get_chat_id();
|
||||
chat_id.accept(&t).await?;
|
||||
chat_id.set_visibility(&t, ChatVisibility::Archived).await?;
|
||||
assert_eq!(dc_get_archived_cnt(&t).await?, 1);
|
||||
|
||||
// not muted chat is unarchived on receiving a message
|
||||
msg_from_bob(&t, 2).await?;
|
||||
assert_eq!(dc_get_archived_cnt(&t).await?, 0);
|
||||
|
||||
// forever muted chat is not unarchived on receiving a message
|
||||
chat_id.set_visibility(&t, ChatVisibility::Archived).await?;
|
||||
set_muted(&t, chat_id, MuteDuration::Forever).await?;
|
||||
msg_from_bob(&t, 3).await?;
|
||||
assert_eq!(dc_get_archived_cnt(&t).await?, 1);
|
||||
|
||||
// otherwise muted chat is not unarchived on receiving a message
|
||||
set_muted(
|
||||
&t,
|
||||
chat_id,
|
||||
MuteDuration::Until(
|
||||
SystemTime::now()
|
||||
.checked_add(Duration::from_secs(1000))
|
||||
.unwrap(),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
msg_from_bob(&t, 4).await?;
|
||||
assert_eq!(dc_get_archived_cnt(&t).await?, 1);
|
||||
|
||||
// expired mute will unarchive the chat
|
||||
set_muted(
|
||||
&t,
|
||||
chat_id,
|
||||
MuteDuration::Until(
|
||||
SystemTime::now()
|
||||
.checked_sub(Duration::from_secs(1000))
|
||||
.unwrap(),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
msg_from_bob(&t, 5).await?;
|
||||
assert_eq!(dc_get_archived_cnt(&t).await?, 0);
|
||||
|
||||
// no unarchiving on sending to muted chat or on adding info messages to muted chat
|
||||
chat_id.set_visibility(&t, ChatVisibility::Archived).await?;
|
||||
set_muted(&t, chat_id, MuteDuration::Forever).await?;
|
||||
send_text_msg(&t, chat_id, "out".to_string()).await?;
|
||||
add_info_msg(&t, chat_id, "info", time()).await?;
|
||||
assert_eq!(dc_get_archived_cnt(&t).await?, 1);
|
||||
|
||||
// finally, unarchive on sending to not muted chat
|
||||
set_muted(&t, chat_id, MuteDuration::NotMuted).await?;
|
||||
send_text_msg(&t, chat_id, "out2".to_string()).await?;
|
||||
assert_eq!(dc_get_archived_cnt(&t).await?, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_chats_from_chat_list(ctx: &Context, listflags: usize) -> Vec<ChatId> {
|
||||
let chatlist = Chatlist::try_load(ctx, listflags, None, None)
|
||||
.await
|
||||
@@ -4384,7 +4317,7 @@ mod tests {
|
||||
let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de")
|
||||
.await
|
||||
.unwrap();
|
||||
assert_ne!(contact1, ContactId::UNDEFINED);
|
||||
assert_ne!(contact1, ContactId::new(0));
|
||||
|
||||
let chat_id = ChatId::create_for_contact(&context.ctx, contact1)
|
||||
.await
|
||||
@@ -4498,7 +4431,6 @@ mod tests {
|
||||
SystemMessage::EphemeralTimerChanged,
|
||||
10000,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -4590,7 +4522,7 @@ mod tests {
|
||||
|
||||
// create contact, then unblocked chat
|
||||
let contact_id = Contact::create(&ctx, "", "bob@foo.de").await.unwrap();
|
||||
assert_ne!(contact_id, ContactId::UNDEFINED);
|
||||
assert_ne!(contact_id, ContactId::new(0));
|
||||
let found = ChatId::lookup_by_contact(&ctx, contact_id).await.unwrap();
|
||||
assert!(found.is_none());
|
||||
|
||||
@@ -4631,13 +4563,13 @@ mod tests {
|
||||
async fn test_lookup_self_by_contact_id() {
|
||||
let ctx = TestContext::new_alice().await;
|
||||
|
||||
let chat = ChatId::lookup_by_contact(&ctx, ContactId::SELF)
|
||||
let chat = ChatId::lookup_by_contact(&ctx, DC_CONTACT_ID_SELF)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(chat.is_none());
|
||||
|
||||
ctx.update_device_chats().await.unwrap();
|
||||
let chat = ChatIdBlocked::lookup_by_contact(&ctx, ContactId::SELF)
|
||||
let chat = ChatIdBlocked::lookup_by_contact(&ctx, DC_CONTACT_ID_SELF)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
@@ -5162,7 +5094,7 @@ mod tests {
|
||||
.await?,
|
||||
true
|
||||
);
|
||||
remove_contact_from_chat(&alice, chat_id, ContactId::SELF).await?;
|
||||
remove_contact_from_chat(&alice, chat_id, DC_CONTACT_ID_SELF).await?;
|
||||
assert_eq!(
|
||||
Chat::load_from_db(&alice, chat_id)
|
||||
.await?
|
||||
|
||||
@@ -4,11 +4,13 @@ use anyhow::{ensure, Context as _, Result};
|
||||
|
||||
use crate::chat::{update_special_chat_names, Chat, ChatId, ChatVisibility};
|
||||
use crate::constants::{
|
||||
Blocked, Chattype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK, DC_GCL_ADD_ALLDONE_HINT,
|
||||
DC_GCL_ARCHIVED_ONLY, DC_GCL_FOR_FORWARDING, DC_GCL_NO_SPECIALS,
|
||||
Blocked, Chattype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK, DC_CONTACT_ID_DEVICE,
|
||||
DC_CONTACT_ID_SELF, DC_CONTACT_ID_UNDEFINED, DC_GCL_ADD_ALLDONE_HINT, DC_GCL_ARCHIVED_ONLY,
|
||||
DC_GCL_FOR_FORWARDING, DC_GCL_NO_SPECIALS,
|
||||
};
|
||||
use crate::contact::{Contact, ContactId};
|
||||
use crate::context::Context;
|
||||
use crate::ephemeral::delete_expired_messages;
|
||||
use crate::message::{Message, MessageState, MsgId};
|
||||
use crate::stock_str;
|
||||
use crate::summary::Summary;
|
||||
@@ -90,6 +92,12 @@ impl Chatlist {
|
||||
let flag_no_specials = 0 != listflags & DC_GCL_NO_SPECIALS;
|
||||
let flag_add_alldone_hint = 0 != listflags & DC_GCL_ADD_ALLDONE_HINT;
|
||||
|
||||
// Note that we do not emit DC_EVENT_MSGS_MODIFIED here even if some
|
||||
// messages get deleted to avoid reloading the same chatlist.
|
||||
if let Err(err) = delete_expired_messages(context).await {
|
||||
warn!(context, "Failed to hide expired messages: {}", err);
|
||||
}
|
||||
|
||||
let mut add_archived_link_item = false;
|
||||
|
||||
let process_row = |row: &rusqlite::Row| {
|
||||
@@ -104,7 +112,7 @@ impl Chatlist {
|
||||
};
|
||||
|
||||
let skip_id = if flag_for_forwarding {
|
||||
ChatId::lookup_by_contact(context, ContactId::DEVICE)
|
||||
ChatId::lookup_by_contact(context, DC_CONTACT_ID_DEVICE)
|
||||
.await?
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
@@ -208,7 +216,7 @@ impl Chatlist {
|
||||
} else {
|
||||
// show normal chatlist
|
||||
let sort_id_up = if flag_for_forwarding {
|
||||
ChatId::lookup_by_contact(context, ContactId::SELF)
|
||||
ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF)
|
||||
.await?
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
@@ -318,7 +326,7 @@ impl Chatlist {
|
||||
|
||||
let (lastmsg, lastcontact) = if let Some(lastmsg_id) = lastmsg_id {
|
||||
let lastmsg = Message::load_from_db(context, lastmsg_id).await?;
|
||||
if lastmsg.from_id == ContactId::SELF {
|
||||
if lastmsg.from_id == DC_CONTACT_ID_SELF {
|
||||
(Some(lastmsg), None)
|
||||
} else {
|
||||
match chat.typ {
|
||||
@@ -335,7 +343,7 @@ impl Chatlist {
|
||||
|
||||
if chat.id.is_archived_link() {
|
||||
Ok(Default::default())
|
||||
} else if let Some(lastmsg) = lastmsg.filter(|msg| msg.from_id != ContactId::UNDEFINED) {
|
||||
} else if let Some(lastmsg) = lastmsg.filter(|msg| msg.from_id != DC_CONTACT_ID_UNDEFINED) {
|
||||
Ok(Summary::new(context, &lastmsg, chat, lastcontact.as_ref()).await)
|
||||
} else {
|
||||
Ok(Summary {
|
||||
@@ -348,10 +356,6 @@ impl Chatlist {
|
||||
pub fn get_index_for_id(&self, id: ChatId) -> Option<usize> {
|
||||
self.ids.iter().position(|(chat_id, _)| chat_id == &id)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &(ChatId, Option<MsgId>)> {
|
||||
self.ids.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of archived chats
|
||||
|
||||
@@ -5,10 +5,12 @@ use strum::{EnumProperty, IntoEnumIterator};
|
||||
use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
|
||||
|
||||
use crate::blob::BlobObject;
|
||||
use crate::chat::ChatId;
|
||||
use crate::constants::DC_VERSION_STR;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::{dc_get_abs_path, improve_single_line_input};
|
||||
use crate::events::EventType;
|
||||
use crate::message::MsgId;
|
||||
use crate::mimefactory::RECOMMENDED_FILE_SIZE;
|
||||
use crate::provider::{get_provider_by_id, Provider};
|
||||
|
||||
@@ -227,14 +229,6 @@ impl Context {
|
||||
Ok(self.get_config_int(key).await? != 0)
|
||||
}
|
||||
|
||||
/// Returns the primary configured address for this account.
|
||||
pub(crate) async fn get_primary_addr(&self) -> Result<String> {
|
||||
use anyhow::Context;
|
||||
self.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.context("no address configured")
|
||||
}
|
||||
|
||||
pub(crate) async fn should_watch_mvbox(&self) -> Result<bool> {
|
||||
Ok(self.get_config_bool(Config::MvboxMove).await?
|
||||
|| self.get_config_bool(Config::OnlyFetchMvbox).await?)
|
||||
@@ -299,8 +293,11 @@ impl Context {
|
||||
}
|
||||
Config::DeleteDeviceAfter => {
|
||||
let ret = self.sql.set_raw_config(key, value).await;
|
||||
// Interrupt ephemeral loop to delete old messages immediately.
|
||||
self.interrupt_ephemeral_task().await;
|
||||
// Force chatlist reload to delete old messages immediately.
|
||||
self.emit_event(EventType::MsgsChanged {
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
});
|
||||
ret?
|
||||
}
|
||||
Config::Displayname => {
|
||||
|
||||
@@ -85,7 +85,7 @@ impl Context {
|
||||
async fn inner_configure(&self) -> Result<()> {
|
||||
info!(self, "Configure ...");
|
||||
|
||||
let mut param = LoginParam::load_candidate_params(self).await?;
|
||||
let mut param = LoginParam::from_database(self, "").await?;
|
||||
let success = configure(self, &mut param).await;
|
||||
self.set_config(Config::NotifyAboutWrongPw, None).await?;
|
||||
|
||||
@@ -454,7 +454,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
|
||||
progress!(ctx, 910);
|
||||
// the trailing underscore is correct
|
||||
param.save_as_configured_params(ctx).await?;
|
||||
param.save_to_database(ctx, "configured_").await?;
|
||||
ctx.set_config(Config::ConfiguredTimestamp, Some(&time().to_string()))
|
||||
.await?;
|
||||
|
||||
@@ -699,11 +699,11 @@ pub enum Error {
|
||||
error: quick_xml::Error,
|
||||
},
|
||||
|
||||
#[error("Failed to get URL: {0}")]
|
||||
ReadUrl(#[from] self::read_url::Error),
|
||||
|
||||
#[error("Number of redirection is exceeded")]
|
||||
Redirection,
|
||||
|
||||
#[error("{0:#}")]
|
||||
Other(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,40 +1,20 @@
|
||||
use crate::context::Context;
|
||||
|
||||
use anyhow::format_err;
|
||||
use anyhow::Context as _;
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("URL request error")]
|
||||
GetError(surf::Error),
|
||||
}
|
||||
|
||||
pub async fn read_url(context: &Context, url: &str) -> anyhow::Result<String> {
|
||||
match read_url_inner(context, url).await {
|
||||
Ok(s) => {
|
||||
info!(context, "Successfully read url {}", url);
|
||||
Ok(s)
|
||||
}
|
||||
Err(e) => {
|
||||
info!(context, "Can't read URL {}: {:#}", url, e);
|
||||
Err(format_err!("Can't read URL {}: {:#}", url, e))
|
||||
pub async fn read_url(context: &Context, url: &str) -> Result<String, Error> {
|
||||
info!(context, "Requesting URL {}", url);
|
||||
|
||||
match surf::get(url).recv_string().await {
|
||||
Ok(res) => Ok(res),
|
||||
Err(err) => {
|
||||
info!(context, "Can\'t read URL {}: {}", url, err);
|
||||
|
||||
Err(Error::GetError(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_url_inner(context: &Context, mut url: &str) -> anyhow::Result<String> {
|
||||
let mut _temp; // For the borrow checker
|
||||
|
||||
// Follow up to 10 http-redirects
|
||||
for _i in 0..10 {
|
||||
let mut response = surf::get(url).send().await.map_err(|e| e.into_inner())?;
|
||||
if response.status().is_redirection() {
|
||||
_temp = response
|
||||
.header("location")
|
||||
.context("Redirection doesn't have a target location")?
|
||||
.last()
|
||||
.to_string();
|
||||
info!(context, "Following redirect to {}", _temp);
|
||||
url = &_temp;
|
||||
continue;
|
||||
}
|
||||
|
||||
return response.body_string().await.map_err(|e| e.into_inner());
|
||||
}
|
||||
|
||||
Err(format_err!("Followed 10 redirections"))
|
||||
}
|
||||
|
||||
@@ -52,8 +52,10 @@ impl ServerParams {
|
||||
fn expand_hostnames(self, param_domain: &str) -> Vec<ServerParams> {
|
||||
if self.hostname.is_empty() {
|
||||
vec![
|
||||
// Try "imap.ex.org"/"smtp.ex.org" and "mail.ex.org" first because if a server exists
|
||||
// under this address, it's likely the correct one.
|
||||
Self {
|
||||
hostname: param_domain.to_string(),
|
||||
..self.clone()
|
||||
},
|
||||
Self {
|
||||
hostname: match self.protocol {
|
||||
Protocol::Imap => "imap.".to_string() + param_domain,
|
||||
@@ -63,12 +65,6 @@ impl ServerParams {
|
||||
},
|
||||
Self {
|
||||
hostname: "mail.".to_string() + param_domain,
|
||||
..self.clone()
|
||||
},
|
||||
// Try "ex.org" last because if it's wrong and the server is configured to
|
||||
// not answer at all, configuration may be stuck for several minutes.
|
||||
Self {
|
||||
hostname: param_domain.to_string(),
|
||||
..self
|
||||
},
|
||||
]
|
||||
@@ -300,48 +296,5 @@ mod tests {
|
||||
strict_tls: Some(true)
|
||||
}],
|
||||
);
|
||||
|
||||
// Test that "example.net" is tried after "*.example.net".
|
||||
let v = expand_param_vector(
|
||||
vec![ServerParams {
|
||||
protocol: Protocol::Imap,
|
||||
hostname: "".to_string(),
|
||||
port: 10480,
|
||||
socket: Socket::Ssl,
|
||||
username: "foobar".to_string(),
|
||||
strict_tls: Some(true),
|
||||
}],
|
||||
"foobar@example.net",
|
||||
"example.net",
|
||||
);
|
||||
assert_eq!(
|
||||
v,
|
||||
vec![
|
||||
ServerParams {
|
||||
protocol: Protocol::Imap,
|
||||
hostname: "imap.example.net".to_string(),
|
||||
port: 10480,
|
||||
socket: Socket::Ssl,
|
||||
username: "foobar".to_string(),
|
||||
strict_tls: Some(true)
|
||||
},
|
||||
ServerParams {
|
||||
protocol: Protocol::Imap,
|
||||
hostname: "mail.example.net".to_string(),
|
||||
port: 10480,
|
||||
socket: Socket::Ssl,
|
||||
username: "foobar".to_string(),
|
||||
strict_tls: Some(true)
|
||||
},
|
||||
ServerParams {
|
||||
protocol: Protocol::Imap,
|
||||
hostname: "example.net".to_string(),
|
||||
port: 10480,
|
||||
socket: Socket::Ssl,
|
||||
username: "foobar".to_string(),
|
||||
strict_tls: Some(true)
|
||||
}
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::chat::ChatId;
|
||||
use crate::contact::ContactId;
|
||||
|
||||
pub static DC_VERSION_STR: Lazy<String> = Lazy::new(|| env!("CARGO_PKG_VERSION").to_string());
|
||||
|
||||
@@ -179,6 +180,16 @@ pub const DC_ELLIPSIS: &str = "[...]";
|
||||
/// `char`s), not Unicode Grapheme Clusters.
|
||||
pub const DC_DESIRED_TEXT_LEN: usize = 5000;
|
||||
|
||||
pub const DC_CONTACT_ID_UNDEFINED: ContactId = ContactId::new(0);
|
||||
pub const DC_CONTACT_ID_SELF: ContactId = ContactId::new(1);
|
||||
pub const DC_CONTACT_ID_INFO: ContactId = ContactId::new(2);
|
||||
pub const DC_CONTACT_ID_DEVICE: ContactId = ContactId::new(5);
|
||||
pub const DC_CONTACT_ID_LAST_SPECIAL: ContactId = ContactId::new(9);
|
||||
|
||||
// decorative address that is used for DC_CONTACT_ID_DEVICE
|
||||
// when an api that returns an email is called.
|
||||
pub const DC_CONTACT_ID_DEVICE_ADDR: &str = "device@localhost";
|
||||
|
||||
// Flags for empty server job
|
||||
|
||||
pub const DC_EMPTY_MVBOX: u32 = 0x01;
|
||||
|
||||
193
src/contact.rs
193
src/contact.rs
@@ -14,7 +14,10 @@ use crate::aheader::EncryptPreference;
|
||||
use crate::chat::ChatId;
|
||||
use crate::color::str_to_color;
|
||||
use crate::config::Config;
|
||||
use crate::constants::{Blocked, Chattype, DC_GCL_ADD_SELF, DC_GCL_VERIFIED_ONLY};
|
||||
use crate::constants::{
|
||||
Blocked, Chattype, DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_DEVICE_ADDR, DC_CONTACT_ID_LAST_SPECIAL,
|
||||
DC_CONTACT_ID_SELF, DC_GCL_ADD_SELF, DC_GCL_VERIFIED_ONLY,
|
||||
};
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::{dc_get_abs_path, improve_single_line_input, EmailAddress};
|
||||
use crate::events::EventType;
|
||||
@@ -30,44 +33,18 @@ use crate::{chat, stock_str};
|
||||
///
|
||||
/// Some contact IDs are reserved to identify special contacts. This
|
||||
/// type can represent both the special as well as normal contacts.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[derive(
|
||||
Debug, Copy, Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
|
||||
)]
|
||||
pub struct ContactId(u32);
|
||||
|
||||
impl ContactId {
|
||||
pub const UNDEFINED: ContactId = ContactId::new(0);
|
||||
/// The owner of the account.
|
||||
///
|
||||
/// The email-address is set by `dc_set_config` using "addr".
|
||||
pub const SELF: ContactId = ContactId::new(1);
|
||||
pub const INFO: ContactId = ContactId::new(2);
|
||||
pub const DEVICE: ContactId = ContactId::new(5);
|
||||
const LAST_SPECIAL: ContactId = ContactId::new(9);
|
||||
|
||||
/// Address to go with [`ContactId::DEVICE`].
|
||||
///
|
||||
/// This is used by APIs which need to return an email address for this contact.
|
||||
pub const DEVICE_ADDR: &'static str = "device@localhost";
|
||||
|
||||
/// Creates a new [`ContactId`].
|
||||
pub const fn new(id: u32) -> ContactId {
|
||||
ContactId(id)
|
||||
}
|
||||
|
||||
/// Whether this is a special [`ContactId`].
|
||||
///
|
||||
/// Some [`ContactId`]s are reserved for special contacts like [`ContactId::SELF`],
|
||||
/// [`ContactId::INFO`] and [`ContactId::DEVICE`]. This function indicates whether this
|
||||
/// [`ContactId`] is any of the reserved special [`ContactId`]s (`true`) or whether it
|
||||
/// is the [`ContactId`] of a real contact (`false`).
|
||||
pub fn is_special(&self) -> bool {
|
||||
self.0 <= Self::LAST_SPECIAL.0
|
||||
}
|
||||
|
||||
/// Numerical representation of the [`ContactId`].
|
||||
///
|
||||
/// Each contact ID has a unique numerical representation which is used in the database
|
||||
/// (via [`rusqlite::ToSql`]) and also for FFI purposes. In Rust code you should never
|
||||
/// need to use this directly.
|
||||
/// Bad evil escape hatch, do not use.
|
||||
pub const fn to_u32(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
@@ -75,19 +52,20 @@ impl ContactId {
|
||||
|
||||
impl fmt::Display for ContactId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if *self == ContactId::UNDEFINED {
|
||||
write!(f, "Contact#Undefined")
|
||||
} else if *self == ContactId::SELF {
|
||||
write!(f, "Contact#Self")
|
||||
} else if *self == ContactId::INFO {
|
||||
write!(f, "Contact#Info")
|
||||
} else if *self == ContactId::DEVICE {
|
||||
write!(f, "Contact#Device")
|
||||
} else if self.is_special() {
|
||||
write!(f, "Contact#Special{}", self.0)
|
||||
} else {
|
||||
write!(f, "Contact#{}", self.0)
|
||||
}
|
||||
write!(f, "{}", self.0)
|
||||
// if *self == DC_CONTACT_ID_UNDEFINED {
|
||||
// write!(f, "Contact#Undefined")
|
||||
// } else if *self == DC_CONTACT_ID_SELF {
|
||||
// write!(f, "Contact#Self")
|
||||
// } else if *self == DC_CONTACT_ID_INFO {
|
||||
// write!(f, "Contact#Info")
|
||||
// } else if *self == DC_CONTACT_ID_DEVICE {
|
||||
// write!(f, "Contact#Device")
|
||||
// } else if *self <= DC_CONTACT_ID_LAST_SPECIAL {
|
||||
// write!(f, "Contact#Special{}", self.0)
|
||||
// } else {
|
||||
// write!(f, "Contact#{}", self.0)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +104,12 @@ impl rusqlite::types::FromSql for ContactId {
|
||||
#[derive(Debug)]
|
||||
pub struct Contact {
|
||||
/// The contact ID.
|
||||
///
|
||||
/// Special message IDs:
|
||||
/// - DC_CONTACT_ID_SELF (1) - this is the owner of the account with the email-address set by
|
||||
/// `dc_set_config` using "addr".
|
||||
///
|
||||
/// Normal contact IDs are larger than these special ones (larger than DC_CONTACT_ID_LAST_SPECIAL).
|
||||
pub id: ContactId,
|
||||
|
||||
/// Contact name. It is recommended to use `Contact::get_name`,
|
||||
@@ -294,16 +278,19 @@ impl Contact {
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
if contact_id == ContactId::SELF {
|
||||
if contact_id == DC_CONTACT_ID_SELF {
|
||||
contact.name = stock_str::self_msg(context).await;
|
||||
contact.addr = context.get_primary_addr().await.unwrap_or_default();
|
||||
contact.addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
contact.status = context
|
||||
.get_config(Config::Selfstatus)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
} else if contact_id == ContactId::DEVICE {
|
||||
} else if contact_id == DC_CONTACT_ID_DEVICE {
|
||||
contact.name = stock_str::device_messages(context).await;
|
||||
contact.addr = ContactId::DEVICE_ADDR.to_string();
|
||||
contact.addr = DC_CONTACT_ID_DEVICE_ADDR.to_string();
|
||||
contact.status = stock_str::device_messages_hint(context).await;
|
||||
}
|
||||
Ok(contact)
|
||||
@@ -395,8 +382,10 @@ impl Contact {
|
||||
|
||||
let addr_normalized = addr_normalize(addr);
|
||||
|
||||
if context.is_self_addr(addr_normalized).await? {
|
||||
return Ok(Some(ContactId::SELF));
|
||||
if let Some(addr_self) = context.get_config(Config::ConfiguredAddr).await? {
|
||||
if addr_cmp(addr_normalized, &addr_self) {
|
||||
return Ok(Some(DC_CONTACT_ID_SELF));
|
||||
}
|
||||
}
|
||||
let id = context
|
||||
.sql
|
||||
@@ -404,7 +393,11 @@ impl Contact {
|
||||
"SELECT id FROM contacts \
|
||||
WHERE addr=?1 COLLATE NOCASE \
|
||||
AND id>?2 AND origin>=?3 AND blocked=0;",
|
||||
paramsv![addr_normalized, ContactId::LAST_SPECIAL, min_origin as u32,],
|
||||
paramsv![
|
||||
addr_normalized,
|
||||
DC_CONTACT_ID_LAST_SPECIAL,
|
||||
min_origin as u32,
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
Ok(id)
|
||||
@@ -447,12 +440,13 @@ impl Contact {
|
||||
ensure!(origin != Origin::Unknown, "Missing valid origin");
|
||||
|
||||
let addr = addr_normalize(addr).to_string();
|
||||
let addr_self = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
|
||||
// during configuration process some chats are added and addr
|
||||
// might not be configured yet
|
||||
let addr_self = context.get_primary_addr().await.unwrap_or_default();
|
||||
if addr_cmp(&addr, &addr_self) {
|
||||
return Ok((ContactId::SELF, sth_modified));
|
||||
return Ok((DC_CONTACT_ID_SELF, sth_modified));
|
||||
}
|
||||
|
||||
if !may_be_valid_addr(&addr) {
|
||||
@@ -686,7 +680,11 @@ impl Contact {
|
||||
listflags: u32,
|
||||
query: Option<impl AsRef<str>>,
|
||||
) -> Result<Vec<ContactId>> {
|
||||
let self_addr = context.get_primary_addr().await.unwrap_or_default();
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut add_self = false;
|
||||
let mut ret = Vec::new();
|
||||
let flag_verified_only = (listflags & DC_GCL_VERIFIED_ONLY) != 0;
|
||||
@@ -708,7 +706,7 @@ impl Contact {
|
||||
ORDER BY LOWER(iif(c.name='',c.authname,c.name)||c.addr),c.id;",
|
||||
paramsv![
|
||||
self_addr,
|
||||
ContactId::LAST_SPECIAL,
|
||||
DC_CONTACT_ID_LAST_SPECIAL,
|
||||
Origin::IncomingReplyTo,
|
||||
s3str_like_cmd,
|
||||
s3str_like_cmd,
|
||||
@@ -752,7 +750,11 @@ impl Contact {
|
||||
AND origin>=?3
|
||||
AND blocked=0
|
||||
ORDER BY LOWER(iif(name='',authname,name)||addr),id;",
|
||||
paramsv![self_addr, ContactId::LAST_SPECIAL, Origin::IncomingReplyTo],
|
||||
paramsv![
|
||||
self_addr,
|
||||
DC_CONTACT_ID_LAST_SPECIAL,
|
||||
Origin::IncomingReplyTo
|
||||
],
|
||||
|row| row.get::<_, ContactId>(0),
|
||||
|ids| {
|
||||
for id in ids {
|
||||
@@ -765,7 +767,7 @@ impl Contact {
|
||||
}
|
||||
|
||||
if flag_add_self && add_self {
|
||||
ret.push(ContactId::SELF);
|
||||
ret.push(DC_CONTACT_ID_SELF);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
@@ -820,7 +822,7 @@ impl Contact {
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0",
|
||||
paramsv![ContactId::LAST_SPECIAL],
|
||||
paramsv![DC_CONTACT_ID_LAST_SPECIAL],
|
||||
)
|
||||
.await?;
|
||||
Ok(count as usize)
|
||||
@@ -836,7 +838,7 @@ impl Contact {
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(iif(name='',authname,name)||addr),id;",
|
||||
paramsv![ContactId::LAST_SPECIAL],
|
||||
paramsv![DC_CONTACT_ID_LAST_SPECIAL],
|
||||
|row| row.get::<_, ContactId>(0),
|
||||
|ids| {
|
||||
ids.collect::<std::result::Result<Vec<_>, _>>()
|
||||
@@ -854,13 +856,13 @@ impl Contact {
|
||||
/// fingerprints of the keys involved.
|
||||
pub async fn get_encrinfo(context: &Context, contact_id: ContactId) -> Result<String> {
|
||||
ensure!(
|
||||
!contact_id.is_special(),
|
||||
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
|
||||
"Can not provide encryption info for special contact"
|
||||
);
|
||||
|
||||
let mut ret = String::new();
|
||||
if let Ok(contact) = Contact::load_from_db(context, contact_id).await {
|
||||
let loginparam = LoginParam::load_configured_params(context).await?;
|
||||
let loginparam = LoginParam::from_database(context, "configured_").await?;
|
||||
let peerstate = Peerstate::from_addr(context, &contact.addr).await?;
|
||||
|
||||
if let Some(peerstate) = peerstate.filter(|peerstate| {
|
||||
@@ -922,7 +924,10 @@ impl Contact {
|
||||
///
|
||||
/// May result in a `#DC_EVENT_CONTACTS_CHANGED` event.
|
||||
pub async fn delete(context: &Context, contact_id: ContactId) -> Result<()> {
|
||||
ensure!(!contact_id.is_special(), "Can not delete special contact");
|
||||
ensure!(
|
||||
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
|
||||
"Can not delete special contact"
|
||||
);
|
||||
|
||||
let count_chats = context
|
||||
.sql
|
||||
@@ -958,7 +963,7 @@ impl Contact {
|
||||
|
||||
/// Get a single contact object. For a list, see eg. dc_get_contacts().
|
||||
///
|
||||
/// For contact ContactId::SELF (1), the function returns sth.
|
||||
/// For contact DC_CONTACT_ID_SELF (1), the function returns sth.
|
||||
/// like "Me" in the selected language and the email address
|
||||
/// defined by dc_set_config().
|
||||
pub async fn get_by_id(context: &Context, contact_id: ContactId) -> Result<Contact> {
|
||||
@@ -1051,7 +1056,7 @@ impl Contact {
|
||||
/// This is the image set by each remote user on their own
|
||||
/// using dc_set_config(context, "selfavatar", image).
|
||||
pub async fn get_profile_image(&self, context: &Context) -> Result<Option<PathBuf>> {
|
||||
if self.id == ContactId::SELF {
|
||||
if self.id == DC_CONTACT_ID_SELF {
|
||||
if let Some(p) = context.get_config(Config::Selfavatar).await? {
|
||||
return Ok(Some(PathBuf::from(p)));
|
||||
}
|
||||
@@ -1097,7 +1102,7 @@ impl Contact {
|
||||
) -> Result<VerifiedStatus> {
|
||||
// We're always sort of secured-verified as we could verify the key on this device any time with the key
|
||||
// on this device
|
||||
if self.id == ContactId::SELF {
|
||||
if self.id == DC_CONTACT_ID_SELF {
|
||||
return Ok(VerifiedStatus::BidirectVerified);
|
||||
}
|
||||
|
||||
@@ -1145,14 +1150,14 @@ impl Contact {
|
||||
.sql
|
||||
.count(
|
||||
"SELECT COUNT(*) FROM contacts WHERE id>?;",
|
||||
paramsv![ContactId::LAST_SPECIAL],
|
||||
paramsv![DC_CONTACT_ID_LAST_SPECIAL],
|
||||
)
|
||||
.await?;
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
pub async fn real_exists_by_id(context: &Context, contact_id: ContactId) -> Result<bool> {
|
||||
if contact_id.is_special() {
|
||||
if contact_id <= DC_CONTACT_ID_LAST_SPECIAL {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@@ -1225,7 +1230,7 @@ async fn set_block_contact(
|
||||
new_blocking: bool,
|
||||
) -> Result<()> {
|
||||
ensure!(
|
||||
!contact_id.is_special(),
|
||||
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
|
||||
"Can't block special contact {}",
|
||||
contact_id
|
||||
);
|
||||
@@ -1295,7 +1300,7 @@ pub(crate) async fn set_profile_image(
|
||||
let mut contact = Contact::load_from_db(context, contact_id).await?;
|
||||
let changed = match profile_image {
|
||||
AvatarAction::Change(profile_image) => {
|
||||
if contact_id == ContactId::SELF {
|
||||
if contact_id == DC_CONTACT_ID_SELF {
|
||||
if was_encrypted {
|
||||
context
|
||||
.set_config(Config::Selfavatar, Some(profile_image))
|
||||
@@ -1309,7 +1314,7 @@ pub(crate) async fn set_profile_image(
|
||||
true
|
||||
}
|
||||
AvatarAction::Delete => {
|
||||
if contact_id == ContactId::SELF {
|
||||
if contact_id == DC_CONTACT_ID_SELF {
|
||||
if was_encrypted {
|
||||
context.set_config(Config::Selfavatar, None).await?;
|
||||
} else {
|
||||
@@ -1340,7 +1345,7 @@ pub(crate) async fn set_status(
|
||||
encrypted: bool,
|
||||
has_chat_version: bool,
|
||||
) -> Result<()> {
|
||||
if contact_id == ContactId::SELF {
|
||||
if contact_id == DC_CONTACT_ID_SELF {
|
||||
if encrypted && has_chat_version {
|
||||
context
|
||||
.set_config(Config::Selfstatus, Some(&status))
|
||||
@@ -1365,7 +1370,7 @@ pub(crate) async fn update_last_seen(
|
||||
timestamp: i64,
|
||||
) -> Result<()> {
|
||||
ensure!(
|
||||
!contact_id.is_special(),
|
||||
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
|
||||
"Can not update special contact last seen timestamp"
|
||||
);
|
||||
|
||||
@@ -1469,17 +1474,6 @@ mod tests {
|
||||
use crate::message::Message;
|
||||
use crate::test_utils::{self, TestContext};
|
||||
|
||||
#[test]
|
||||
fn test_contact_id_values() {
|
||||
// Some FFI users need to have the values of these fixed, how naughty. But let's
|
||||
// make sure we don't modify them anyway.
|
||||
assert_eq!(ContactId::UNDEFINED.to_u32(), 0);
|
||||
assert_eq!(ContactId::SELF.to_u32(), 1);
|
||||
assert_eq!(ContactId::INFO.to_u32(), 2);
|
||||
assert_eq!(ContactId::DEVICE.to_u32(), 5);
|
||||
assert_eq!(ContactId::LAST_SPECIAL.to_u32(), 9);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_may_be_valid_addr() {
|
||||
assert_eq!(may_be_valid_addr(""), false);
|
||||
@@ -1543,7 +1537,7 @@ mod tests {
|
||||
Origin::IncomingReplyTo,
|
||||
)
|
||||
.await?;
|
||||
assert_ne!(id, ContactId::UNDEFINED);
|
||||
assert_ne!(id, ContactId::new(0));
|
||||
|
||||
let contact = Contact::load_from_db(&context.ctx, id).await.unwrap();
|
||||
assert_eq!(contact.get_name(), "");
|
||||
@@ -1621,7 +1615,7 @@ mod tests {
|
||||
Contact::add_or_lookup(&t, "bla foo", "one@eins.org", Origin::IncomingUnknownTo)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!contact_id.is_special());
|
||||
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
|
||||
assert_eq!(sth_modified, Modifier::Modified);
|
||||
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
|
||||
assert_eq!(contact.get_id(), contact_id);
|
||||
@@ -1648,7 +1642,7 @@ mod tests {
|
||||
Contact::add_or_lookup(&t, "", "three@drei.sam", Origin::IncomingUnknownTo)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!contact_id.is_special());
|
||||
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
|
||||
assert_eq!(sth_modified, Modifier::None);
|
||||
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
|
||||
assert_eq!(contact.get_name(), "");
|
||||
@@ -1688,7 +1682,7 @@ mod tests {
|
||||
Contact::add_or_lookup(&t, "", "alice@w.de", Origin::IncomingUnknownTo)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!contact_id.is_special());
|
||||
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
|
||||
assert_eq!(sth_modified, Modifier::None);
|
||||
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
|
||||
assert_eq!(contact.get_name(), "Wonderland, Alice");
|
||||
@@ -1697,7 +1691,8 @@ mod tests {
|
||||
assert_eq!(contact.get_name_n_addr(), "Wonderland, Alice (alice@w.de)");
|
||||
|
||||
// check SELF
|
||||
let contact = Contact::load_from_db(&t, ContactId::SELF).await.unwrap();
|
||||
let contact = Contact::load_from_db(&t, DC_CONTACT_ID_SELF).await.unwrap();
|
||||
assert_eq!(DC_CONTACT_ID_SELF, ContactId::new(1));
|
||||
assert_eq!(contact.get_name(), stock_str::self_msg(&t).await);
|
||||
assert_eq!(contact.get_addr(), ""); // we're not configured
|
||||
assert!(!contact.is_blocked());
|
||||
@@ -1707,7 +1702,7 @@ mod tests {
|
||||
async fn test_delete() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
assert!(Contact::delete(&alice, ContactId::SELF).await.is_err());
|
||||
assert!(Contact::delete(&alice, DC_CONTACT_ID_SELF).await.is_err());
|
||||
|
||||
// Create Bob contact
|
||||
let (contact_id, _) =
|
||||
@@ -1740,7 +1735,7 @@ mod tests {
|
||||
Contact::add_or_lookup(&t, "bob1", "bob@example.org", Origin::IncomingUnknownFrom)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!contact_id.is_special());
|
||||
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
|
||||
assert_eq!(sth_modified, Modifier::Created);
|
||||
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
|
||||
assert_eq!(contact.get_authname(), "bob1");
|
||||
@@ -1752,7 +1747,7 @@ mod tests {
|
||||
Contact::add_or_lookup(&t, "bob2", "bob@example.org", Origin::IncomingUnknownFrom)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!contact_id.is_special());
|
||||
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
|
||||
assert_eq!(sth_modified, Modifier::Modified);
|
||||
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
|
||||
assert_eq!(contact.get_authname(), "bob2");
|
||||
@@ -1763,7 +1758,7 @@ mod tests {
|
||||
let contact_id = Contact::create(&t, "bob3", "bob@example.org")
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!contact_id.is_special());
|
||||
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
|
||||
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
|
||||
assert_eq!(contact.get_authname(), "bob2");
|
||||
assert_eq!(contact.get_name(), "bob3");
|
||||
@@ -1774,7 +1769,7 @@ mod tests {
|
||||
Contact::add_or_lookup(&t, "bob4", "bob@example.org", Origin::IncomingUnknownFrom)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!contact_id.is_special());
|
||||
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
|
||||
assert_eq!(sth_modified, Modifier::Modified);
|
||||
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
|
||||
assert_eq!(contact.get_authname(), "bob4");
|
||||
@@ -1788,7 +1783,7 @@ mod tests {
|
||||
|
||||
// manually create "claire@example.org" without a given name
|
||||
let contact_id = Contact::create(&t, "", "claire@example.org").await.unwrap();
|
||||
assert!(!contact_id.is_special());
|
||||
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
|
||||
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
|
||||
assert_eq!(contact.get_authname(), "");
|
||||
assert_eq!(contact.get_name(), "");
|
||||
@@ -1962,7 +1957,7 @@ mod tests {
|
||||
let id = Contact::lookup_id_by_addr(&alice.ctx, "alice@example.org", Origin::Unknown)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(id, Some(ContactId::SELF));
|
||||
assert_eq!(id, Some(DC_CONTACT_ID_SELF));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
@@ -1970,9 +1965,9 @@ mod tests {
|
||||
let alice = TestContext::new_alice().await;
|
||||
|
||||
// Return error for special IDs
|
||||
let encrinfo = Contact::get_encrinfo(&alice, ContactId::SELF).await;
|
||||
let encrinfo = Contact::get_encrinfo(&alice, DC_CONTACT_ID_SELF).await;
|
||||
assert!(encrinfo.is_err());
|
||||
let encrinfo = Contact::get_encrinfo(&alice, ContactId::DEVICE).await;
|
||||
let encrinfo = Contact::get_encrinfo(&alice, DC_CONTACT_ID_DEVICE).await;
|
||||
assert!(encrinfo.is_err());
|
||||
|
||||
let (contact_bob_id, _modified) =
|
||||
|
||||
@@ -10,6 +10,7 @@ use async_std::{
|
||||
channel::{self, Receiver, Sender},
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
task,
|
||||
};
|
||||
|
||||
use crate::chat::{get_chat_cnt, ChatId};
|
||||
@@ -55,6 +56,7 @@ pub struct InnerContext {
|
||||
pub(crate) events: Events,
|
||||
|
||||
pub(crate) scheduler: RwLock<Scheduler>,
|
||||
pub(crate) ephemeral_task: RwLock<Option<task::JoinHandle<()>>>,
|
||||
|
||||
/// Recently loaded quota information, if any.
|
||||
/// Set to `None` if quota was never tried to load.
|
||||
@@ -174,6 +176,7 @@ impl Context {
|
||||
translated_stockstrings: RwLock::new(HashMap::new()),
|
||||
events: Events::default(),
|
||||
scheduler: RwLock::new(Scheduler::Stopped),
|
||||
ephemeral_task: RwLock::new(None),
|
||||
quota: RwLock::new(None),
|
||||
creation_time: std::time::SystemTime::now(),
|
||||
last_full_folder_scan: Mutex::new(None),
|
||||
@@ -311,8 +314,8 @@ impl Context {
|
||||
|
||||
pub async fn get_info(&self) -> Result<BTreeMap<&'static str, String>> {
|
||||
let unset = "0";
|
||||
let l = LoginParam::load_candidate_params(self).await?;
|
||||
let l2 = LoginParam::load_configured_params(self).await?;
|
||||
let l = LoginParam::from_database(self, "").await?;
|
||||
let l2 = LoginParam::from_database(self, "configured_").await?;
|
||||
let displayname = self.get_config(Config::Displayname).await?;
|
||||
let chats = get_chat_cnt(self).await? as usize;
|
||||
let unblocked_msgs = message::get_unblocked_msg_cnt(self).await as usize;
|
||||
@@ -639,6 +642,10 @@ impl InnerContext {
|
||||
lock.stop(token).await;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ephemeral_task) = self.ephemeral_task.write().await.take() {
|
||||
ephemeral_task.cancel().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,7 +670,7 @@ mod tests {
|
||||
use crate::chat::{
|
||||
get_chat_contacts, get_chat_msgs, send_msg, set_muted, Chat, ChatId, MuteDuration,
|
||||
};
|
||||
use crate::contact::ContactId;
|
||||
use crate::constants::DC_CONTACT_ID_SELF;
|
||||
use crate::dc_receive_imf::dc_receive_imf;
|
||||
use crate::dc_tools::dc_create_outgoing_rfc724_mid;
|
||||
use crate::message::{Message, Viewtype};
|
||||
@@ -949,7 +956,7 @@ mod tests {
|
||||
#[async_std::test]
|
||||
async fn test_search_msgs() -> Result<()> {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let self_talk = ChatId::create_for_contact(&alice, ContactId::SELF).await?;
|
||||
let self_talk = ChatId::create_for_contact(&alice, DC_CONTACT_ID_SELF).await?;
|
||||
let chat = alice
|
||||
.create_chat_with_contact("Bob", "bob@example.org")
|
||||
.await;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Internet Message Format reception pipeline.
|
||||
|
||||
use std::cmp::min;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::BTreeSet;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use anyhow::{bail, ensure, Context as _, Result};
|
||||
@@ -13,7 +13,9 @@ use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::chat::{self, Chat, ChatId, ChatIdBlocked, ProtectionStatus};
|
||||
use crate::config::Config;
|
||||
use crate::constants::{Blocked, Chattype, ShowEmails, DC_CHAT_ID_TRASH};
|
||||
use crate::constants::{
|
||||
Blocked, Chattype, ShowEmails, DC_CHAT_ID_TRASH, DC_CONTACT_ID_LAST_SPECIAL, DC_CONTACT_ID_SELF,
|
||||
};
|
||||
use crate::contact;
|
||||
use crate::contact::{
|
||||
addr_cmp, may_be_valid_addr, normalize_name, Contact, ContactId, Origin, VerifiedStatus,
|
||||
@@ -178,7 +180,7 @@ pub(crate) async fn dc_receive_imf_inner(
|
||||
let (from_id, _from_id_blocked, incoming_origin) =
|
||||
from_field_to_contact_id(context, &mime_parser.from, prevent_rename).await?;
|
||||
|
||||
let incoming = from_id != ContactId::SELF;
|
||||
let incoming = from_id != DC_CONTACT_ID_SELF;
|
||||
|
||||
let to_ids = dc_add_or_lookup_contacts_by_address_list(
|
||||
context,
|
||||
@@ -220,7 +222,7 @@ pub(crate) async fn dc_receive_imf_inner(
|
||||
.await
|
||||
.context("add_parts error")?;
|
||||
|
||||
if !from_id.is_special() {
|
||||
if from_id > DC_CONTACT_ID_LAST_SPECIAL {
|
||||
contact::update_last_seen(context, from_id, sent_timestamp).await?;
|
||||
}
|
||||
|
||||
@@ -254,7 +256,7 @@ pub(crate) async fn dc_receive_imf_inner(
|
||||
save_locations(context, &mime_parser, chat_id, from_id, insert_msg_id).await?;
|
||||
|
||||
if let Some(ref sync_items) = mime_parser.sync_items {
|
||||
if from_id == ContactId::SELF {
|
||||
if from_id == DC_CONTACT_ID_SELF {
|
||||
if mime_parser.was_encrypted() {
|
||||
if let Err(err) = context.execute_sync_items(sync_items).await {
|
||||
warn!(context, "receive_imf cannot execute sync items: {}", err);
|
||||
@@ -277,7 +279,7 @@ pub(crate) async fn dc_receive_imf_inner(
|
||||
}
|
||||
|
||||
if let Some(avatar_action) = &mime_parser.user_avatar {
|
||||
if from_id != ContactId::UNDEFINED
|
||||
if from_id != ContactId::new(0)
|
||||
&& context
|
||||
.update_contacts_timestamp(from_id, Param::AvatarTimestamp, sent_timestamp)
|
||||
.await?
|
||||
@@ -305,7 +307,7 @@ pub(crate) async fn dc_receive_imf_inner(
|
||||
// Ignore MDNs though, as they never contain the signature even if user has set it.
|
||||
if mime_parser.mdn_reports.is_empty()
|
||||
&& is_partial_download.is_none()
|
||||
&& from_id != ContactId::UNDEFINED
|
||||
&& from_id != ContactId::new(0)
|
||||
&& context
|
||||
.update_contacts_timestamp(from_id, Param::StatusTimestamp, sent_timestamp)
|
||||
.await?
|
||||
@@ -393,8 +395,8 @@ pub async fn from_field_to_contact_id(
|
||||
)
|
||||
.await?;
|
||||
|
||||
if from_ids.contains(&ContactId::SELF) {
|
||||
Ok((ContactId::SELF, false, Origin::OutgoingBcc))
|
||||
if from_ids.contains(&DC_CONTACT_ID_SELF) {
|
||||
Ok((DC_CONTACT_ID_SELF, false, Origin::OutgoingBcc))
|
||||
} else if !from_ids.is_empty() {
|
||||
if from_ids.len() > 1 {
|
||||
warn!(
|
||||
@@ -417,7 +419,7 @@ pub async fn from_field_to_contact_id(
|
||||
"mail has an empty From header: {:?}", from_address_list
|
||||
);
|
||||
|
||||
Ok((ContactId::UNDEFINED, false, Origin::Unknown))
|
||||
Ok((ContactId::new(0), false, Origin::Unknown))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +494,7 @@ async fn add_parts(
|
||||
let state: MessageState;
|
||||
let mut needs_delete_job = false;
|
||||
if incoming {
|
||||
to_id = ContactId::SELF;
|
||||
to_id = DC_CONTACT_ID_SELF;
|
||||
|
||||
// Whether the message is a part of securejoin handshake that should be marked as seen
|
||||
// automatically.
|
||||
@@ -524,7 +526,7 @@ async fn add_parts(
|
||||
securejoin_seen = false;
|
||||
}
|
||||
|
||||
let test_normal_chat = if from_id == ContactId::UNDEFINED {
|
||||
let test_normal_chat = if from_id == ContactId::new(0) {
|
||||
Default::default()
|
||||
} else {
|
||||
ChatIdBlocked::lookup_by_contact(context, from_id).await?
|
||||
@@ -539,7 +541,7 @@ async fn add_parts(
|
||||
// try to assign to a chat based on In-Reply-To/References:
|
||||
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) =
|
||||
lookup_chat_by_reply(context, mime_parser, &parent, to_ids, from_id).await?
|
||||
lookup_chat_by_reply(context, mime_parser, &parent, to_ids).await?
|
||||
{
|
||||
chat_id = Some(new_chat_id);
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
@@ -666,7 +668,7 @@ async fn add_parts(
|
||||
|
||||
if chat_id.is_none() {
|
||||
// try to create a normal chat
|
||||
let create_blocked = if from_id == ContactId::SELF {
|
||||
let create_blocked = if from_id == DC_CONTACT_ID_SELF {
|
||||
Blocked::Not
|
||||
} else {
|
||||
Blocked::Request
|
||||
@@ -718,8 +720,9 @@ async fn add_parts(
|
||||
state = MessageState::OutDelivered;
|
||||
to_id = to_ids.get(0).cloned().unwrap_or_default();
|
||||
|
||||
let self_sent =
|
||||
from_id == ContactId::SELF && to_ids.len() == 1 && to_ids.contains(&ContactId::SELF);
|
||||
let self_sent = from_id == DC_CONTACT_ID_SELF
|
||||
&& to_ids.len() == 1
|
||||
&& to_ids.contains(&DC_CONTACT_ID_SELF);
|
||||
|
||||
// handshake may mark contacts as verified and must be processed before chats are created
|
||||
if mime_parser.get_header(HeaderDef::SecureJoin).is_some() {
|
||||
@@ -771,7 +774,7 @@ async fn add_parts(
|
||||
// try to assign to a chat based on In-Reply-To/References:
|
||||
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) =
|
||||
lookup_chat_by_reply(context, mime_parser, &parent, to_ids, from_id).await?
|
||||
lookup_chat_by_reply(context, mime_parser, &parent, to_ids).await?
|
||||
{
|
||||
chat_id = Some(new_chat_id);
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
@@ -833,11 +836,12 @@ async fn add_parts(
|
||||
}
|
||||
|
||||
if chat_id.is_none() && self_sent {
|
||||
// from_id==to_id==ContactId::SELF - this is a self-sent messages,
|
||||
// from_id==to_id==DC_CONTACT_ID_SELF - this is a self-sent messages,
|
||||
// maybe an Autocrypt Setup Message
|
||||
if let Ok(chat) = ChatIdBlocked::get_for_contact(context, ContactId::SELF, Blocked::Not)
|
||||
.await
|
||||
.log_err(context, "Failed to get (new) chat for contact")
|
||||
if let Ok(chat) =
|
||||
ChatIdBlocked::get_for_contact(context, DC_CONTACT_ID_SELF, Blocked::Not)
|
||||
.await
|
||||
.log_err(context, "Failed to get (new) chat for contact")
|
||||
{
|
||||
chat_id = Some(chat.id);
|
||||
chat_id_blocked = chat.blocked;
|
||||
@@ -1144,8 +1148,8 @@ INSERT INTO msgs
|
||||
stmt.execute(paramsv![
|
||||
rfc724_mid,
|
||||
chat_id,
|
||||
if trash { ContactId::UNDEFINED } else { from_id },
|
||||
if trash { ContactId::UNDEFINED } else { to_id },
|
||||
if trash { ContactId::new(0) } else { from_id },
|
||||
if trash { ContactId::new(0) } else { to_id },
|
||||
sort_timestamp,
|
||||
sent_timestamp,
|
||||
rcvd_timestamp,
|
||||
@@ -1187,7 +1191,7 @@ INSERT INTO msgs
|
||||
}
|
||||
drop(conn);
|
||||
|
||||
chat_id.unarchive_if_not_muted(context).await?;
|
||||
chat_id.unarchive(context).await?;
|
||||
|
||||
info!(
|
||||
context,
|
||||
@@ -1318,7 +1322,6 @@ async fn lookup_chat_by_reply(
|
||||
mime_parser: &MimeMessage,
|
||||
parent: &Option<Message>,
|
||||
to_ids: &[ContactId],
|
||||
from_id: ContactId,
|
||||
) -> Result<Option<(ChatId, Blocked)>> {
|
||||
// Try to assign message to the same chat as the parent message.
|
||||
|
||||
@@ -1340,7 +1343,7 @@ async fn lookup_chat_by_reply(
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if is_probably_private_reply(context, to_ids, from_id, mime_parser, parent_chat.id).await? {
|
||||
if is_probably_private_reply(context, to_ids, mime_parser, parent_chat.id).await? {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -1359,7 +1362,6 @@ async fn lookup_chat_by_reply(
|
||||
async fn is_probably_private_reply(
|
||||
context: &Context,
|
||||
to_ids: &[ContactId],
|
||||
from_id: ContactId,
|
||||
mime_parser: &MimeMessage,
|
||||
parent_chat_id: ChatId,
|
||||
) -> Result<bool> {
|
||||
@@ -1370,15 +1372,14 @@ async fn is_probably_private_reply(
|
||||
// should be assigned to the group chat. We restrict this exception to classical emails, as chat-group-messages
|
||||
// contain a Chat-Group-Id header and can be sorted into the correct chat this way.
|
||||
|
||||
let private_message =
|
||||
(to_ids == [ContactId::SELF]) || (from_id == ContactId::SELF && to_ids.len() == 1);
|
||||
let private_message = to_ids == [DC_CONTACT_ID_SELF];
|
||||
if !private_message {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !mime_parser.has_chat_version() {
|
||||
let chat_contacts = chat::get_chat_contacts(context, parent_chat_id).await?;
|
||||
if chat_contacts.len() == 2 && chat_contacts.contains(&ContactId::SELF) {
|
||||
if chat_contacts.len() == 2 && chat_contacts.contains(&DC_CONTACT_ID_SELF) {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
@@ -1406,8 +1407,8 @@ async fn create_or_lookup_group(
|
||||
if !member_ids.contains(&(from_id)) {
|
||||
member_ids.push(from_id);
|
||||
}
|
||||
if !member_ids.contains(&(ContactId::SELF)) {
|
||||
member_ids.push(ContactId::SELF);
|
||||
if !member_ids.contains(&(DC_CONTACT_ID_SELF)) {
|
||||
member_ids.push(DC_CONTACT_ID_SELF);
|
||||
}
|
||||
|
||||
let res = create_adhoc_group(context, mime_parser, create_blocked, &member_ids)
|
||||
@@ -1434,7 +1435,7 @@ async fn create_or_lookup_group(
|
||||
// they belong to the group because of the Chat-Group-Id or Message-Id header
|
||||
if let Some(chat_id) = chat_id {
|
||||
if !mime_parser.has_chat_version()
|
||||
&& is_probably_private_reply(context, to_ids, from_id, mime_parser, chat_id).await?
|
||||
&& is_probably_private_reply(context, to_ids, mime_parser, chat_id).await?
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -1451,7 +1452,10 @@ async fn create_or_lookup_group(
|
||||
ProtectionStatus::Unprotected
|
||||
};
|
||||
|
||||
let self_addr = context.get_primary_addr().await?;
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.context("no address configured")?;
|
||||
if chat_id.is_none()
|
||||
&& !mime_parser.is_mailinglist_message()
|
||||
&& !grpid.is_empty()
|
||||
@@ -1485,8 +1489,9 @@ async fn create_or_lookup_group(
|
||||
chat_id_blocked = create_blocked;
|
||||
|
||||
// Create initial member list.
|
||||
chat::add_to_chat_contacts_table(context, new_chat_id, ContactId::SELF).await?;
|
||||
if !from_id.is_special() && !chat::is_contact_in_chat(context, new_chat_id, from_id).await?
|
||||
chat::add_to_chat_contacts_table(context, new_chat_id, DC_CONTACT_ID_SELF).await?;
|
||||
if from_id > DC_CONTACT_ID_LAST_SPECIAL
|
||||
&& !chat::is_contact_in_chat(context, new_chat_id, from_id).await?
|
||||
{
|
||||
chat::add_to_chat_contacts_table(context, new_chat_id, from_id).await?;
|
||||
}
|
||||
@@ -1547,7 +1552,11 @@ async fn apply_group_changes(
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let self_addr = context.get_primary_addr().await?;
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.context("no address configured")?;
|
||||
|
||||
let mut recreate_member_list = false;
|
||||
let mut send_event_chat_modified = false;
|
||||
|
||||
@@ -1635,7 +1644,7 @@ async fn apply_group_changes(
|
||||
|
||||
// add members to group/check members
|
||||
if recreate_member_list {
|
||||
if chat::is_contact_in_chat(context, chat_id, ContactId::SELF).await?
|
||||
if chat::is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await?
|
||||
&& !chat::is_contact_in_chat(context, chat_id, from_id).await?
|
||||
{
|
||||
warn!(
|
||||
@@ -1649,7 +1658,7 @@ async fn apply_group_changes(
|
||||
.await?
|
||||
{
|
||||
if removed_id.is_some()
|
||||
|| !chat::is_contact_in_chat(context, chat_id, ContactId::SELF).await?
|
||||
|| !chat::is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await?
|
||||
{
|
||||
// Members could have been removed while we were
|
||||
// absent. We can't use existing member list and need to
|
||||
@@ -1662,11 +1671,11 @@ async fn apply_group_changes(
|
||||
)
|
||||
.await?;
|
||||
|
||||
if removed_id != Some(ContactId::SELF) {
|
||||
chat::add_to_chat_contacts_table(context, chat_id, ContactId::SELF).await?;
|
||||
if removed_id != Some(DC_CONTACT_ID_SELF) {
|
||||
chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await?;
|
||||
}
|
||||
}
|
||||
if !from_id.is_special()
|
||||
if from_id > DC_CONTACT_ID_LAST_SPECIAL
|
||||
&& !Contact::addr_equals_contact(context, &self_addr, from_id).await?
|
||||
&& !chat::is_contact_in_chat(context, chat_id, from_id).await?
|
||||
&& removed_id != Some(from_id)
|
||||
@@ -1687,7 +1696,7 @@ async fn apply_group_changes(
|
||||
}
|
||||
|
||||
if let Some(avatar_action) = &mime_parser.group_avatar {
|
||||
if !chat::is_contact_in_chat(context, chat_id, ContactId::SELF).await? {
|
||||
if !chat::is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await? {
|
||||
warn!(
|
||||
context,
|
||||
"Received group avatar update for group chat {} we are not a member of.", chat_id
|
||||
@@ -1837,7 +1846,7 @@ async fn create_or_lookup_mailinglist(
|
||||
)
|
||||
})?;
|
||||
|
||||
chat::add_to_chat_contacts_table(context, chat_id, ContactId::SELF).await?;
|
||||
chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await?;
|
||||
Ok(Some((chat_id, Blocked::Request)))
|
||||
} else {
|
||||
info!(context, "creating list forbidden by caller");
|
||||
@@ -1991,14 +2000,19 @@ async fn create_adhoc_group(
|
||||
/// are hidden in BCC. This group ID is sent by DC in the messages sent to this chat,
|
||||
/// so having the same ID prevents group split.
|
||||
async fn create_adhoc_grp_id(context: &Context, member_ids: &[ContactId]) -> Result<String> {
|
||||
let member_cs = context.get_primary_addr().await?.to_lowercase();
|
||||
let member_cs = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.unwrap_or_else(|| "no-self".to_string())
|
||||
.to_lowercase();
|
||||
|
||||
let query = format!(
|
||||
"SELECT addr FROM contacts WHERE id IN({}) AND id!=?",
|
||||
sql::repeat_vars(member_ids.len())?
|
||||
);
|
||||
let mut params = Vec::new();
|
||||
params.extend_from_slice(member_ids);
|
||||
params.push(ContactId::SELF);
|
||||
params.push(DC_CONTACT_ID_SELF);
|
||||
|
||||
let members = context
|
||||
.sql
|
||||
@@ -2054,7 +2068,7 @@ async fn check_verified_properties(
|
||||
// and the message is signed with a verified key of the sender.
|
||||
// this check is skipped for SELF as there is no proper SELF-peerstate
|
||||
// and results in group-splits otherwise.
|
||||
if from_id != ContactId::SELF {
|
||||
if from_id != DC_CONTACT_ID_SELF {
|
||||
let peerstate = Peerstate::from_addr(context, contact.get_addr()).await?;
|
||||
|
||||
if peerstate.is_none()
|
||||
@@ -2079,12 +2093,17 @@ async fn check_verified_properties(
|
||||
let to_ids = to_ids
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|id| *id != ContactId::SELF)
|
||||
.filter(|id| *id != DC_CONTACT_ID_SELF)
|
||||
.collect::<Vec<ContactId>>();
|
||||
|
||||
if to_ids.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let to_ids_str = to_ids
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(",");
|
||||
|
||||
let rows = context
|
||||
.sql
|
||||
@@ -2092,9 +2111,9 @@ async fn check_verified_properties(
|
||||
format!(
|
||||
"SELECT c.addr, LENGTH(ps.verified_key_fingerprint) FROM contacts c \
|
||||
LEFT JOIN acpeerstates ps ON c.addr=ps.addr WHERE c.id IN({}) ",
|
||||
sql::repeat_vars(to_ids.len())?
|
||||
to_ids_str
|
||||
),
|
||||
rusqlite::params_from_iter(to_ids),
|
||||
paramsv![],
|
||||
|row| {
|
||||
let to_addr: String = row.get(0)?;
|
||||
let is_verified: i32 = row.get(1).unwrap_or(0);
|
||||
@@ -2248,7 +2267,7 @@ async fn dc_add_or_lookup_contacts_by_address_list(
|
||||
origin: Origin,
|
||||
prevent_rename: bool,
|
||||
) -> Result<Vec<ContactId>> {
|
||||
let mut contact_ids = HashSet::new();
|
||||
let mut contact_ids = BTreeSet::new();
|
||||
for info in address_list.iter() {
|
||||
let addr = &info.addr;
|
||||
if !may_be_valid_addr(addr) {
|
||||
@@ -2274,7 +2293,7 @@ async fn add_or_lookup_contact_by_addr(
|
||||
origin: Origin,
|
||||
) -> Result<ContactId> {
|
||||
if context.is_self_addr(addr).await? {
|
||||
return Ok(ContactId::SELF);
|
||||
return Ok(DC_CONTACT_ID_SELF);
|
||||
}
|
||||
let display_name_normalized = display_name.map(normalize_name).unwrap_or_default();
|
||||
|
||||
@@ -2294,7 +2313,7 @@ mod tests {
|
||||
use crate::chat::get_chat_contacts;
|
||||
use crate::chat::{get_chat_msgs, ChatItem, ChatVisibility};
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::constants::DC_GCL_NO_SPECIALS;
|
||||
use crate::constants::{DC_CONTACT_ID_INFO, DC_GCL_NO_SPECIALS};
|
||||
use crate::message::Message;
|
||||
use crate::test_utils::{get_chat_msg, TestContext, TestContextManager};
|
||||
|
||||
@@ -2942,7 +2961,7 @@ mod tests {
|
||||
last_msg.text,
|
||||
Some(stock_str::failed_sending_to(&t, "assidhfaaspocwaeofi@gmail.com").await,)
|
||||
);
|
||||
assert_eq!(last_msg.from_id, ContactId::INFO);
|
||||
assert_eq!(last_msg.from_id, DC_CONTACT_ID_INFO);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4884,7 +4903,7 @@ Hi, I created a group"#,
|
||||
)
|
||||
.await?;
|
||||
let msg_out = t.get_last_msg().await;
|
||||
assert_eq!(msg_out.from_id, ContactId::SELF);
|
||||
assert_eq!(msg_out.from_id, DC_CONTACT_ID_SELF);
|
||||
assert_eq!(msg_out.text.unwrap(), "Hi, I created a group");
|
||||
assert_eq!(msg_out.in_reply_to, None);
|
||||
|
||||
@@ -4909,7 +4928,7 @@ Reply from different address
|
||||
)
|
||||
.await?;
|
||||
let msg_in = t.get_last_msg().await;
|
||||
assert_eq!(msg_in.to_id, ContactId::SELF);
|
||||
assert_eq!(msg_in.to_id, DC_CONTACT_ID_SELF);
|
||||
assert_eq!(msg_in.text.unwrap(), "Reply from different address");
|
||||
assert_eq!(
|
||||
msg_in.in_reply_to.unwrap(),
|
||||
@@ -5027,81 +5046,4 @@ Reply from different address
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_outgoing_private_reply_multidevice() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new().await;
|
||||
let alice1 = tcm.alice().await;
|
||||
let alice2 = tcm.alice().await;
|
||||
let bob = tcm.bob().await;
|
||||
|
||||
// =============== Bob creates a group ===============
|
||||
let group_id =
|
||||
chat::create_group_chat(&bob, ProtectionStatus::Unprotected, "Group").await?;
|
||||
chat::add_to_chat_contacts_table(
|
||||
&bob,
|
||||
group_id,
|
||||
bob.add_or_lookup_contact(&alice1).await.id,
|
||||
)
|
||||
.await?;
|
||||
chat::add_to_chat_contacts_table(
|
||||
&bob,
|
||||
group_id,
|
||||
Contact::create(&bob, "", "charlie@example.org").await?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// =============== Bob sends the first message to the group ===============
|
||||
let sent = bob.send_text(group_id, "Hello all!").await;
|
||||
alice1.recv_msg(&sent).await;
|
||||
alice2.recv_msg(&sent).await;
|
||||
|
||||
// =============== Alice answers privately with device 1 ===============
|
||||
let received = alice1.get_last_msg().await;
|
||||
let alice1_bob_contact = alice1.add_or_lookup_contact(&bob).await;
|
||||
assert_eq!(received.from_id, alice1_bob_contact.id);
|
||||
assert_eq!(received.to_id, ContactId::SELF);
|
||||
assert!(!received.hidden);
|
||||
assert_eq!(received.text, Some("Hello all!".to_string()));
|
||||
assert_eq!(received.in_reply_to, None);
|
||||
assert_eq!(received.chat_blocked, Blocked::Request);
|
||||
|
||||
let received_group = Chat::load_from_db(&alice1, received.chat_id).await?;
|
||||
assert_eq!(received_group.typ, Chattype::Group);
|
||||
assert_eq!(received_group.name, "Group");
|
||||
assert_eq!(received_group.can_send(&alice1).await?, false); // Can't send because it's Blocked::Request
|
||||
|
||||
let mut msg_out = Message::new(Viewtype::Text);
|
||||
msg_out.set_text(Some("Private reply".to_string()));
|
||||
|
||||
assert_eq!(received_group.blocked, Blocked::Request);
|
||||
msg_out.set_quote(&alice1, Some(&received)).await?;
|
||||
let alice1_bob_chat = alice1.create_chat(&bob).await;
|
||||
let sent2 = alice1.send_msg(alice1_bob_chat.id, &mut msg_out).await;
|
||||
alice2.recv_msg(&sent2).await;
|
||||
|
||||
// =============== Alice's second device receives the message ===============
|
||||
let received = alice2.get_last_msg().await;
|
||||
|
||||
// That's a regression test for https://github.com/deltachat/deltachat-core-rust/issues/2949:
|
||||
assert_eq!(received.chat_id, alice2.get_chat(&bob).await.unwrap().id);
|
||||
|
||||
let alice2_bob_contact = alice2.add_or_lookup_contact(&bob).await;
|
||||
assert_eq!(received.from_id, ContactId::SELF);
|
||||
assert_eq!(received.to_id, alice2_bob_contact.id);
|
||||
assert!(!received.hidden);
|
||||
assert_eq!(received.text, Some("Private reply".to_string()));
|
||||
assert_eq!(
|
||||
received.parent(&alice2).await?.unwrap().text,
|
||||
Some("Hello all!".to_string())
|
||||
);
|
||||
assert_eq!(received.chat_blocked, Blocked::Not);
|
||||
|
||||
let received_chat = Chat::load_from_db(&alice2, received.chat_id).await?;
|
||||
assert_eq!(received_chat.typ, Chattype::Single);
|
||||
assert_eq!(received_chat.name, "bob@example.net");
|
||||
assert_eq!(received_chat.can_send(&alice2).await?, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ pub(crate) fn dc_gm2local_offset() -> i64 {
|
||||
// `last_smeared_timestamp` is again in sync with the normal time.
|
||||
// - however, we do not do all this for the far future,
|
||||
// but at max `MAX_SECONDS_TO_LEND_FROM_FUTURE`
|
||||
pub(crate) const MAX_SECONDS_TO_LEND_FROM_FUTURE: i64 = 5;
|
||||
const MAX_SECONDS_TO_LEND_FROM_FUTURE: i64 = 5;
|
||||
|
||||
/// Returns the current smeared timestamp,
|
||||
///
|
||||
|
||||
18
src/e2ee.rs
18
src/e2ee.rs
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::{format_err, Context as _, Result};
|
||||
use anyhow::{bail, format_err, Context as _, Result};
|
||||
use mailparse::ParsedMail;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
@@ -28,7 +28,13 @@ impl EncryptHelper {
|
||||
let prefer_encrypt =
|
||||
EncryptPreference::from_i32(context.get_config_int(Config::E2eeEnabled).await?)
|
||||
.unwrap_or_default();
|
||||
let addr = context.get_primary_addr().await?;
|
||||
let addr = match context.get_config(Config::ConfiguredAddr).await? {
|
||||
None => {
|
||||
bail!("addr not configured!");
|
||||
}
|
||||
Some(addr) => addr,
|
||||
};
|
||||
|
||||
let public_key = SignedPublicKey::load_self(context).await?;
|
||||
|
||||
Ok(EncryptHelper {
|
||||
@@ -381,7 +387,13 @@ fn contains_report(mail: &ParsedMail<'_>) -> bool {
|
||||
/// [Config::ConfiguredAddr] is configured, this address is returned.
|
||||
// TODO, remove this once deltachat::key::Key no longer exists.
|
||||
pub async fn ensure_secret_key_exists(context: &Context) -> Result<String> {
|
||||
let self_addr = context.get_primary_addr().await?;
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.context(concat!(
|
||||
"Failed to get self address, ",
|
||||
"cannot ensure secret key if not configured."
|
||||
))?;
|
||||
SignedPublicKey::load_self(context).await?;
|
||||
Ok(self_addr)
|
||||
}
|
||||
|
||||
300
src/ephemeral.rs
300
src/ephemeral.rs
@@ -48,9 +48,9 @@
|
||||
//!
|
||||
//! ## When messages are deleted
|
||||
//!
|
||||
//! The `ephemeral_loop` task schedules the next due running of
|
||||
//! `delete_expired_messages` which in turn emits `MsgsChanged` events
|
||||
//! when deleting local messages to make UIs reload displayed messages.
|
||||
//! Local deletion happens when the chatlist or chat is loaded. A
|
||||
//! `MsgsChanged` event is emitted when a message deletion is due, to
|
||||
//! make UI reload displayed messages and cause actual deletion.
|
||||
//!
|
||||
//! Server deletion happens by updating the `imap` table based on
|
||||
//! the database entries which are expired either according to their
|
||||
@@ -62,18 +62,18 @@ use std::str::FromStr;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use anyhow::{ensure, Context as _, Result};
|
||||
use async_std::channel::Receiver;
|
||||
use async_std::future::timeout;
|
||||
use async_std::task;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::chat::{send_msg, ChatId};
|
||||
use crate::constants::{DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH};
|
||||
use crate::constants::{
|
||||
DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH, DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_SELF,
|
||||
};
|
||||
use crate::contact::ContactId;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::{duration_to_str, time};
|
||||
use crate::dc_tools::time;
|
||||
use crate::download::MIN_DELETE_SERVER_AFTER;
|
||||
use crate::events::EventType;
|
||||
use crate::log::LogExt;
|
||||
use crate::message::{Message, MessageState, MsgId, Viewtype};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::sql;
|
||||
@@ -198,7 +198,7 @@ impl ChatId {
|
||||
}
|
||||
self.inner_set_ephemeral_timer(context, timer).await?;
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.text = Some(stock_ephemeral_timer_changed(context, timer, ContactId::SELF).await);
|
||||
msg.text = Some(stock_ephemeral_timer_changed(context, timer, DC_CONTACT_ID_SELF).await);
|
||||
msg.param.set_cmd(SystemMessage::EphemeralTimerChanged);
|
||||
if let Err(err) = send_msg(context, self, &mut msg).await {
|
||||
error!(
|
||||
@@ -293,7 +293,7 @@ impl MsgId {
|
||||
paramsv![ephemeral_timestamp, ephemeral_timestamp, self],
|
||||
)
|
||||
.await?;
|
||||
context.interrupt_ephemeral_task().await;
|
||||
schedule_ephemeral_task(context).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -325,7 +325,7 @@ pub(crate) async fn start_ephemeral_timers_msgids(
|
||||
)
|
||||
.await?;
|
||||
if count > 0 {
|
||||
context.interrupt_ephemeral_task().await;
|
||||
schedule_ephemeral_task(context).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -338,7 +338,7 @@ pub(crate) async fn start_ephemeral_timers_msgids(
|
||||
/// false. This function does not emit the MsgsChanged event itself,
|
||||
/// because it is also called when chatlist is reloaded, and emitting
|
||||
/// MsgsChanged there will cause infinite reload loop.
|
||||
pub(crate) async fn delete_expired_messages(context: &Context, now: i64) -> Result<()> {
|
||||
pub(crate) async fn delete_expired_messages(context: &Context) -> Result<bool> {
|
||||
let mut updated = context
|
||||
.sql
|
||||
.execute(
|
||||
@@ -354,21 +354,21 @@ WHERE
|
||||
AND ephemeral_timestamp <= ?
|
||||
AND chat_id != ?
|
||||
"#,
|
||||
paramsv![DC_CHAT_ID_TRASH, now, DC_CHAT_ID_TRASH],
|
||||
paramsv![DC_CHAT_ID_TRASH, time(), DC_CHAT_ID_TRASH],
|
||||
)
|
||||
.await
|
||||
.context("update failed")?
|
||||
> 0;
|
||||
|
||||
if let Some(delete_device_after) = context.get_config_delete_device_after().await? {
|
||||
let self_chat_id = ChatId::lookup_by_contact(context, ContactId::SELF)
|
||||
let self_chat_id = ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
let device_chat_id = ChatId::lookup_by_contact(context, ContactId::DEVICE)
|
||||
let device_chat_id = ChatId::lookup_by_contact(context, DC_CONTACT_ID_DEVICE)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
|
||||
let threshold_timestamp = now.saturating_sub(delete_device_after);
|
||||
let threshold_timestamp = time() - delete_device_after;
|
||||
|
||||
// Delete expired messages
|
||||
//
|
||||
@@ -378,8 +378,7 @@ WHERE
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE msgs \
|
||||
SET chat_id = ?, txt = '', subject='', txt_raw='', \
|
||||
mime_headers='', from_id=0, to_id=0, param='' \
|
||||
SET txt = 'DELETED', chat_id = ? \
|
||||
WHERE timestamp < ? \
|
||||
AND chat_id > ? \
|
||||
AND chat_id != ? \
|
||||
@@ -398,119 +397,72 @@ WHERE
|
||||
updated |= rows_modified > 0;
|
||||
}
|
||||
|
||||
if updated {
|
||||
context.emit_event(EventType::MsgsChanged {
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
schedule_ephemeral_task(context).await;
|
||||
Ok(updated)
|
||||
}
|
||||
|
||||
/// Calculates the next timestamp when a message will be deleted due to
|
||||
/// `delete_device_after` setting being set.
|
||||
async fn next_delete_device_after_timestamp(context: &Context) -> Result<Option<i64>> {
|
||||
if let Some(delete_device_after) = context.get_config_delete_device_after().await? {
|
||||
let self_chat_id = ChatId::lookup_by_contact(context, ContactId::SELF)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
let device_chat_id = ChatId::lookup_by_contact(context, ContactId::DEVICE)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
|
||||
let oldest_message_timestamp: Option<i64> = context
|
||||
.sql
|
||||
.query_get_value(
|
||||
r#"
|
||||
SELECT min(timestamp)
|
||||
FROM msgs
|
||||
WHERE chat_id > ?
|
||||
AND chat_id != ?
|
||||
AND chat_id != ?;
|
||||
"#,
|
||||
paramsv![DC_CHAT_ID_TRASH, self_chat_id, device_chat_id],
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(oldest_message_timestamp.map(|x| x.saturating_add(delete_device_after)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates next timestamp when expiration of some message will happen.
|
||||
/// Schedule a task to emit MsgsChanged event when the next local
|
||||
/// deletion happens. Existing task is cancelled to make sure at most
|
||||
/// one such task is scheduled at a time.
|
||||
///
|
||||
/// Expiration can happen either because user has set `delete_device_after` setting or because the
|
||||
/// message itself has an ephemeral timer.
|
||||
async fn next_expiration_timestamp(context: &Context) -> Option<i64> {
|
||||
/// UI is expected to reload the chatlist or the chat in response to
|
||||
/// MsgsChanged event, this will trigger actual deletion.
|
||||
///
|
||||
/// This takes into account only per-chat timeouts, because global device
|
||||
/// timeouts are at least one hour long and deletion is triggered often enough
|
||||
/// by user actions.
|
||||
pub async fn schedule_ephemeral_task(context: &Context) {
|
||||
let ephemeral_timestamp: Option<i64> = match context
|
||||
.sql
|
||||
.query_get_value(
|
||||
r#"
|
||||
SELECT min(ephemeral_timestamp)
|
||||
FROM msgs
|
||||
WHERE ephemeral_timestamp != 0
|
||||
AND chat_id != ?;
|
||||
"#,
|
||||
SELECT ephemeral_timestamp
|
||||
FROM msgs
|
||||
WHERE ephemeral_timestamp != 0
|
||||
AND chat_id != ?
|
||||
ORDER BY ephemeral_timestamp ASC
|
||||
LIMIT 1;
|
||||
"#,
|
||||
paramsv![DC_CHAT_ID_TRASH], // Trash contains already deleted messages, skip them
|
||||
)
|
||||
.await
|
||||
{
|
||||
Err(err) => {
|
||||
warn!(context, "Can't calculate next ephemeral timeout: {}", err);
|
||||
None
|
||||
return;
|
||||
}
|
||||
Ok(ephemeral_timestamp) => ephemeral_timestamp,
|
||||
};
|
||||
|
||||
let delete_device_after_timestamp: Option<i64> =
|
||||
match next_delete_device_after_timestamp(context).await {
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
"Can't calculate timestamp of the next message expiration: {}", err
|
||||
);
|
||||
None
|
||||
}
|
||||
Ok(timestamp) => timestamp,
|
||||
};
|
||||
|
||||
ephemeral_timestamp
|
||||
.into_iter()
|
||||
.chain(delete_device_after_timestamp.into_iter())
|
||||
.min()
|
||||
}
|
||||
|
||||
pub(crate) async fn ephemeral_loop(context: &Context, interrupt_receiver: Receiver<()>) {
|
||||
loop {
|
||||
let ephemeral_timestamp = next_expiration_timestamp(context).await;
|
||||
// Cancel existing task, if any
|
||||
if let Some(ephemeral_task) = context.ephemeral_task.write().await.take() {
|
||||
ephemeral_task.cancel().await;
|
||||
}
|
||||
|
||||
if let Some(ephemeral_timestamp) = ephemeral_timestamp {
|
||||
let now = SystemTime::now();
|
||||
let until = if let Some(ephemeral_timestamp) = ephemeral_timestamp {
|
||||
UNIX_EPOCH
|
||||
+ Duration::from_secs(ephemeral_timestamp.try_into().unwrap_or(u64::MAX))
|
||||
+ Duration::from_secs(1)
|
||||
} else {
|
||||
// no messages to be deleted for now, wait long for one to occur
|
||||
now + Duration::from_secs(86400)
|
||||
};
|
||||
let until = UNIX_EPOCH
|
||||
+ Duration::from_secs(ephemeral_timestamp.try_into().unwrap_or(u64::MAX))
|
||||
+ Duration::from_secs(1);
|
||||
|
||||
if let Ok(duration) = until.duration_since(now) {
|
||||
info!(
|
||||
context,
|
||||
"Ephemeral loop waiting for deletion in {} or interrupt",
|
||||
duration_to_str(duration)
|
||||
);
|
||||
if timeout(duration, interrupt_receiver.recv()).await.is_ok() {
|
||||
// received an interruption signal, recompute waiting time (if any)
|
||||
continue;
|
||||
}
|
||||
// Schedule a task, ephemeral_timestamp is in the future
|
||||
let context1 = context.clone();
|
||||
let ephemeral_task = task::spawn(async move {
|
||||
async_std::task::sleep(duration).await;
|
||||
context1.emit_event(EventType::MsgsChanged {
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
});
|
||||
*context.ephemeral_task.write().await = Some(ephemeral_task);
|
||||
} else {
|
||||
// Emit event immediately
|
||||
context.emit_event(EventType::MsgsChanged {
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
|
||||
delete_expired_messages(context, time())
|
||||
.await
|
||||
.ok_or_log(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,7 +532,6 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::config::Config;
|
||||
use crate::dc_receive_imf::dc_receive_imf;
|
||||
use crate::dc_tools::MAX_SECONDS_TO_LEND_FROM_FUTURE;
|
||||
use crate::download::DownloadState;
|
||||
use crate::test_utils::TestContext;
|
||||
use crate::{
|
||||
@@ -593,7 +544,7 @@ mod tests {
|
||||
let context = TestContext::new().await;
|
||||
|
||||
assert_eq!(
|
||||
stock_ephemeral_timer_changed(&context, Timer::Disabled, ContactId::SELF).await,
|
||||
stock_ephemeral_timer_changed(&context, Timer::Disabled, DC_CONTACT_ID_SELF).await,
|
||||
"Message deletion timer is disabled by me."
|
||||
);
|
||||
|
||||
@@ -601,7 +552,7 @@ mod tests {
|
||||
stock_ephemeral_timer_changed(
|
||||
&context,
|
||||
Timer::Enabled { duration: 1 },
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 1 s by me."
|
||||
@@ -610,7 +561,7 @@ mod tests {
|
||||
stock_ephemeral_timer_changed(
|
||||
&context,
|
||||
Timer::Enabled { duration: 30 },
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 30 s by me."
|
||||
@@ -619,7 +570,7 @@ mod tests {
|
||||
stock_ephemeral_timer_changed(
|
||||
&context,
|
||||
Timer::Enabled { duration: 60 },
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 1 minute by me."
|
||||
@@ -628,7 +579,7 @@ mod tests {
|
||||
stock_ephemeral_timer_changed(
|
||||
&context,
|
||||
Timer::Enabled { duration: 90 },
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 1.5 minutes by me."
|
||||
@@ -637,7 +588,7 @@ mod tests {
|
||||
stock_ephemeral_timer_changed(
|
||||
&context,
|
||||
Timer::Enabled { duration: 30 * 60 },
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 30 minutes by me."
|
||||
@@ -646,7 +597,7 @@ mod tests {
|
||||
stock_ephemeral_timer_changed(
|
||||
&context,
|
||||
Timer::Enabled { duration: 60 * 60 },
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 1 hour by me."
|
||||
@@ -655,7 +606,7 @@ mod tests {
|
||||
stock_ephemeral_timer_changed(
|
||||
&context,
|
||||
Timer::Enabled { duration: 5400 },
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 1.5 hours by me."
|
||||
@@ -666,7 +617,7 @@ mod tests {
|
||||
Timer::Enabled {
|
||||
duration: 2 * 60 * 60
|
||||
},
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 2 hours by me."
|
||||
@@ -677,7 +628,7 @@ mod tests {
|
||||
Timer::Enabled {
|
||||
duration: 24 * 60 * 60
|
||||
},
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 1 day by me."
|
||||
@@ -688,7 +639,7 @@ mod tests {
|
||||
Timer::Enabled {
|
||||
duration: 2 * 24 * 60 * 60
|
||||
},
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 2 days by me."
|
||||
@@ -699,7 +650,7 @@ mod tests {
|
||||
Timer::Enabled {
|
||||
duration: 7 * 24 * 60 * 60
|
||||
},
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 1 week by me."
|
||||
@@ -710,7 +661,7 @@ mod tests {
|
||||
Timer::Enabled {
|
||||
duration: 4 * 7 * 24 * 60 * 60
|
||||
},
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
)
|
||||
.await,
|
||||
"Message deletion timer is set to 4 weeks by me."
|
||||
@@ -869,106 +820,31 @@ mod tests {
|
||||
#[async_std::test]
|
||||
async fn test_ephemeral_delete_msgs() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let self_chat = t.get_self_chat().await;
|
||||
let chat = t.get_self_chat().await;
|
||||
|
||||
assert_eq!(next_expiration_timestamp(&t).await, None);
|
||||
|
||||
t.send_text(self_chat.id, "Saved message, which we delete manually")
|
||||
t.send_text(chat.id, "Saved message, which we delete manually")
|
||||
.await;
|
||||
let msg = t.get_last_msg_in(self_chat.id).await;
|
||||
let msg = t.get_last_msg_in(chat.id).await;
|
||||
msg.id.delete_from_db(&t).await?;
|
||||
check_msg_is_deleted(&t, &self_chat, msg.id).await;
|
||||
check_msg_was_deleted(&t, &chat, msg.id).await;
|
||||
|
||||
self_chat
|
||||
.id
|
||||
.set_ephemeral_timer(&t, Timer::Enabled { duration: 3600 })
|
||||
chat.id
|
||||
.set_ephemeral_timer(&t, Timer::Enabled { duration: 1 })
|
||||
.await
|
||||
.unwrap();
|
||||
let msg = t
|
||||
.send_text(chat.id, "Saved message, disappearing after 1s")
|
||||
.await;
|
||||
|
||||
// Send a saved message which will be deleted after 3600s
|
||||
let now = time();
|
||||
let msg = t.send_text(self_chat.id, "Message text").await;
|
||||
|
||||
check_msg_will_be_deleted(&t, msg.sender_msg_id, &self_chat, now + 3599, time() + 3601)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Set DeleteDeviceAfter to 1800s. Thend send a saved message which will
|
||||
// still be deleted after 3600s because DeleteDeviceAfter doesn't apply to saved messages.
|
||||
t.set_config(Config::DeleteDeviceAfter, Some("1800"))
|
||||
.await?;
|
||||
|
||||
let now = time();
|
||||
let msg = t.send_text(self_chat.id, "Message text").await;
|
||||
|
||||
check_msg_will_be_deleted(&t, msg.sender_msg_id, &self_chat, now + 3559, time() + 3601)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Send a message to Bob which will be deleted after 1800s because of DeleteDeviceAfter.
|
||||
let bob_chat = t.create_chat_with_contact("", "bob@example.net").await;
|
||||
let now = time();
|
||||
let msg = t.send_text(bob_chat.id, "Message text").await;
|
||||
|
||||
check_msg_will_be_deleted(
|
||||
&t,
|
||||
msg.sender_msg_id,
|
||||
&bob_chat,
|
||||
now + 1799,
|
||||
// The message may appear to be sent MAX_SECONDS_TO_LEND_FROM_FUTURE later and
|
||||
// therefore be deleted MAX_SECONDS_TO_LEND_FROM_FUTURE later.
|
||||
time() + 1801 + MAX_SECONDS_TO_LEND_FROM_FUTURE,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Enable ephemeral messages with Bob -> message will be deleted after 60s.
|
||||
// This tests that the message is deleted at min(ephemeral deletion time, DeleteDeviceAfter deletion time).
|
||||
bob_chat
|
||||
.id
|
||||
.set_ephemeral_timer(&t, Timer::Enabled { duration: 60 })
|
||||
.await?;
|
||||
|
||||
let now = time();
|
||||
let msg = t.send_text(bob_chat.id, "Message text").await;
|
||||
|
||||
check_msg_will_be_deleted(&t, msg.sender_msg_id, &bob_chat, now + 59, time() + 61)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn check_msg_will_be_deleted(
|
||||
t: &TestContext,
|
||||
msg_id: MsgId,
|
||||
chat: &Chat,
|
||||
not_deleted_at: i64,
|
||||
deleted_at: i64,
|
||||
) -> Result<()> {
|
||||
let next_expiration = next_expiration_timestamp(t).await.unwrap();
|
||||
|
||||
assert!(next_expiration > not_deleted_at);
|
||||
delete_expired_messages(t, not_deleted_at).await?;
|
||||
|
||||
let loaded = Message::load_from_db(t, msg_id).await?;
|
||||
assert_eq!(loaded.text.unwrap(), "Message text");
|
||||
assert_eq!(loaded.chat_id, chat.id);
|
||||
|
||||
assert!(next_expiration < deleted_at);
|
||||
delete_expired_messages(t, deleted_at).await?;
|
||||
|
||||
let loaded = Message::load_from_db(t, msg_id).await?;
|
||||
assert_eq!(loaded.text.unwrap(), "");
|
||||
assert_eq!(loaded.chat_id, DC_CHAT_ID_TRASH);
|
||||
async_std::task::sleep(Duration::from_millis(1100)).await;
|
||||
|
||||
// Check that the msg was deleted locally.
|
||||
check_msg_is_deleted(t, chat, msg_id).await;
|
||||
check_msg_was_deleted(&t, &chat, msg.sender_msg_id).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn check_msg_is_deleted(t: &TestContext, chat: &Chat, msg_id: MsgId) {
|
||||
async fn check_msg_was_deleted(t: &TestContext, chat: &Chat, msg_id: MsgId) {
|
||||
let chat_items = chat::get_chat_msgs(t, chat.id, 0, None).await.unwrap();
|
||||
// Check that the chat is empty except for possibly info messages:
|
||||
for item in &chat_items {
|
||||
@@ -980,8 +856,8 @@ mod tests {
|
||||
|
||||
// Check that if there is a message left, the text and metadata are gone
|
||||
if let Ok(msg) = Message::load_from_db(t, msg_id).await {
|
||||
assert_eq!(msg.from_id, ContactId::UNDEFINED);
|
||||
assert_eq!(msg.to_id, ContactId::UNDEFINED);
|
||||
assert_eq!(msg.from_id, ContactId::new(0));
|
||||
assert_eq!(msg.to_id, ContactId::new(0));
|
||||
assert!(msg.text.is_none_or_empty(), "{:?}", msg.text);
|
||||
let rawtxt: Option<String> = t
|
||||
.sql
|
||||
|
||||
10
src/html.rs
10
src/html.rs
@@ -279,7 +279,7 @@ mod tests {
|
||||
use crate::chat;
|
||||
use crate::chat::forward_msgs;
|
||||
use crate::config::Config;
|
||||
use crate::contact::ContactId;
|
||||
use crate::constants::DC_CONTACT_ID_SELF;
|
||||
use crate::dc_receive_imf::dc_receive_imf;
|
||||
use crate::message::{MessengerMessage, Viewtype};
|
||||
use crate::test_utils::TestContext;
|
||||
@@ -442,7 +442,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
let raw = include_bytes!("../test-data/message/text_alt_plain_html.eml");
|
||||
dc_receive_imf(&alice, raw, "INBOX", false).await.unwrap();
|
||||
let msg = alice.get_last_msg_in(chat.get_id()).await;
|
||||
assert_ne!(msg.get_from_id(), ContactId::SELF);
|
||||
assert_ne!(msg.get_from_id(), DC_CONTACT_ID_SELF);
|
||||
assert_eq!(msg.is_dc_message, MessengerMessage::No);
|
||||
assert!(!msg.is_forwarded());
|
||||
assert!(msg.get_text().unwrap().contains("this is plain"));
|
||||
@@ -456,7 +456,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
.await
|
||||
.unwrap();
|
||||
let msg = alice.get_last_msg_in(chat.get_id()).await;
|
||||
assert_eq!(msg.get_from_id(), ContactId::SELF);
|
||||
assert_eq!(msg.get_from_id(), DC_CONTACT_ID_SELF);
|
||||
assert_eq!(msg.is_dc_message, MessengerMessage::Yes);
|
||||
assert!(msg.is_forwarded());
|
||||
assert!(msg.get_text().unwrap().contains("this is plain"));
|
||||
@@ -469,7 +469,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
let chat = bob.create_chat_with_contact("", "alice@example.org").await;
|
||||
bob.recv_msg(&alice.pop_sent_msg().await).await;
|
||||
let msg = bob.get_last_msg_in(chat.get_id()).await;
|
||||
assert_ne!(msg.get_from_id(), ContactId::SELF);
|
||||
assert_ne!(msg.get_from_id(), DC_CONTACT_ID_SELF);
|
||||
assert_eq!(msg.is_dc_message, MessengerMessage::Yes);
|
||||
assert!(msg.is_forwarded());
|
||||
assert!(msg.get_text().unwrap().contains("this is plain"));
|
||||
@@ -506,7 +506,7 @@ test some special html-characters as < > and & but also " and &#x
|
||||
alice.recv_msg(&msg).await;
|
||||
let chat = alice.get_self_chat().await;
|
||||
let msg = alice.get_last_msg_in(chat.get_id()).await;
|
||||
assert_eq!(msg.get_from_id(), ContactId::SELF);
|
||||
assert_eq!(msg.get_from_id(), DC_CONTACT_ID_SELF);
|
||||
assert_eq!(msg.is_dc_message, MessengerMessage::Yes);
|
||||
assert!(msg.get_showpadlock());
|
||||
assert!(msg.is_forwarded());
|
||||
|
||||
28
src/imap.rs
28
src/imap.rs
@@ -20,10 +20,9 @@ use num_traits::FromPrimitive;
|
||||
use crate::chat::{self, ChatId, ChatIdBlocked};
|
||||
use crate::config::Config;
|
||||
use crate::constants::{
|
||||
Blocked, Chattype, ShowEmails, DC_FETCH_EXISTING_MSGS_COUNT, DC_FOLDERS_CONFIGURED_VERSION,
|
||||
DC_LP_AUTH_OAUTH2,
|
||||
Blocked, Chattype, ShowEmails, DC_CONTACT_ID_SELF, DC_FETCH_EXISTING_MSGS_COUNT,
|
||||
DC_FOLDERS_CONFIGURED_VERSION, DC_LP_AUTH_OAUTH2,
|
||||
};
|
||||
use crate::contact::ContactId;
|
||||
use crate::context::Context;
|
||||
use crate::dc_receive_imf::{
|
||||
dc_receive_imf_inner, from_field_to_contact_id, get_prefetch_parent_message, ReceivedMsg,
|
||||
@@ -224,7 +223,7 @@ impl Imap {
|
||||
bail!("IMAP Connect without configured params");
|
||||
}
|
||||
|
||||
let param = LoginParam::load_configured_params(context).await?;
|
||||
let param = LoginParam::from_database(context, "configured_").await?;
|
||||
// the trailing underscore is correct
|
||||
|
||||
let imap = Self::new(
|
||||
@@ -458,15 +457,9 @@ impl Imap {
|
||||
}
|
||||
self.prepare(context).await?;
|
||||
|
||||
if self
|
||||
.fetch_new_messages(context, watch_folder, false)
|
||||
self.fetch_new_messages(context, watch_folder, false)
|
||||
.await
|
||||
.context("fetch_new_messages")?
|
||||
{
|
||||
// New messages were fetched, restart ephemeral loop in case these messages will expire
|
||||
// later.
|
||||
context.interrupt_ephemeral_task().await;
|
||||
}
|
||||
.context("fetch_new_messages")?;
|
||||
|
||||
self.move_delete_messages(context, watch_folder)
|
||||
.await
|
||||
@@ -664,9 +657,6 @@ impl Imap {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Fetches new messages.
|
||||
///
|
||||
/// Returns true if at least one message was fetched.
|
||||
pub(crate) async fn fetch_new_messages(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
@@ -1117,7 +1107,11 @@ impl Imap {
|
||||
.session
|
||||
.as_mut()
|
||||
.context("IMAP No Connection established")?;
|
||||
let self_addr = context.get_primary_addr().await?;
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.context("not configured")?;
|
||||
|
||||
let search_command = format!("FROM \"{}\"", self_addr);
|
||||
let uids = session
|
||||
.uid_search(search_command)
|
||||
@@ -1645,7 +1639,7 @@ async fn should_move_out_of_spam(
|
||||
if chat_id_blocked.blocked != Blocked::Not {
|
||||
return Ok(false);
|
||||
}
|
||||
} else if from_id != ContactId::SELF {
|
||||
} else if from_id != DC_CONTACT_ID_SELF {
|
||||
// No chat with this contact found.
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use rand::{thread_rng, Rng};
|
||||
use crate::blob::BlobObject;
|
||||
use crate::chat::{self, delete_and_reset_all_device_msgs, ChatId};
|
||||
use crate::config::Config;
|
||||
use crate::contact::ContactId;
|
||||
use crate::constants::DC_CONTACT_ID_SELF;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::{
|
||||
dc_create_folder, dc_delete_file, dc_delete_files_in_dir, dc_get_filesuffix_lc,
|
||||
@@ -176,7 +176,7 @@ async fn do_initiate_key_transfer(context: &Context) -> Result<String> {
|
||||
)
|
||||
.await?;
|
||||
|
||||
let chat_id = ChatId::create_for_contact(context, ContactId::SELF).await?;
|
||||
let chat_id = ChatId::create_for_contact(context, DC_CONTACT_ID_SELF).await?;
|
||||
let mut msg = Message {
|
||||
viewtype: Viewtype::File,
|
||||
..Default::default()
|
||||
@@ -351,8 +351,9 @@ async fn set_self_key(
|
||||
}
|
||||
};
|
||||
|
||||
let self_addr = context.get_primary_addr().await?;
|
||||
let addr = EmailAddress::new(&self_addr)?;
|
||||
let self_addr = context.get_config(Config::ConfiguredAddr).await?;
|
||||
ensure!(self_addr.is_some(), "Missing self addr");
|
||||
let addr = EmailAddress::new(&self_addr.unwrap_or_default())?;
|
||||
let keypair = pgp::KeyPair {
|
||||
addr,
|
||||
public: public_key,
|
||||
|
||||
23
src/job.rs
23
src/job.rs
@@ -4,7 +4,7 @@
|
||||
//! and job types.
|
||||
use std::fmt;
|
||||
|
||||
use anyhow::{bail, format_err, Context as _, Result};
|
||||
use anyhow::{bail, format_err, Context as _, Error, Result};
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::message::{Message, MsgId};
|
||||
use crate::mimefactory::MimeFactory;
|
||||
use crate::param::{Param, Params};
|
||||
use crate::scheduler::InterruptInfo;
|
||||
use crate::smtp::{smtp_send, SendResult, Smtp};
|
||||
use crate::smtp::{smtp_send, Smtp};
|
||||
use crate::sql;
|
||||
|
||||
// results in ~3 weeks for the last backoff timespan
|
||||
@@ -40,7 +40,7 @@ pub(crate) enum Thread {
|
||||
/// Job try result.
|
||||
#[derive(Debug, Display)]
|
||||
pub enum Status {
|
||||
Finished(Result<()>),
|
||||
Finished(std::result::Result<(), Error>),
|
||||
RetryNow,
|
||||
RetryLater,
|
||||
}
|
||||
@@ -318,18 +318,13 @@ impl Job {
|
||||
return Status::RetryLater;
|
||||
}
|
||||
|
||||
match smtp_send(context, &recipients, &body, smtp, msg_id, 0).await {
|
||||
SendResult::Success => {
|
||||
// Remove additional SendMdn jobs we have aggregated into this one.
|
||||
job_try!(kill_ids(context, &additional_job_ids).await);
|
||||
Status::Finished(Ok(()))
|
||||
}
|
||||
SendResult::Retry => {
|
||||
info!(context, "Temporary SMTP failure while sending an MDN");
|
||||
Status::RetryLater
|
||||
}
|
||||
SendResult::Failure(err) => Status::Finished(Err(err)),
|
||||
let status = smtp_send(context, &recipients, &body, smtp, msg_id, 0).await;
|
||||
if matches!(status, Status::Finished(Ok(_))) {
|
||||
// Remove additional SendMdn jobs we have aggregated into this one.
|
||||
job_try!(kill_ids(context, &additional_job_ids).await);
|
||||
}
|
||||
|
||||
status
|
||||
}
|
||||
|
||||
/// Read the recipients from old emails sent by the user and add them as contacts.
|
||||
|
||||
@@ -198,7 +198,10 @@ impl DcSecretKey for SignedSecretKey {
|
||||
}
|
||||
|
||||
async fn generate_keypair(context: &Context) -> Result<KeyPair> {
|
||||
let addr = context.get_primary_addr().await?;
|
||||
let addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.context("no address configured")?;
|
||||
let addr = EmailAddress::new(&addr)?;
|
||||
let _guard = context.generating_key_mutex.lock().await;
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ use bitflags::bitflags;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::chat::{self, ChatId};
|
||||
use crate::config::Config;
|
||||
use crate::constants::DC_CONTACT_ID_SELF;
|
||||
use crate::contact::ContactId;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::time;
|
||||
@@ -313,7 +315,7 @@ pub async fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64
|
||||
accuracy,
|
||||
time(),
|
||||
chat_id,
|
||||
ContactId::SELF,
|
||||
DC_CONTACT_ID_SELF,
|
||||
]
|
||||
).await {
|
||||
warn!(context, "failed to store location {:?}", err);
|
||||
@@ -322,7 +324,7 @@ pub async fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64
|
||||
}
|
||||
}
|
||||
if continue_streaming {
|
||||
context.emit_event(EventType::LocationChanged(Some(ContactId::SELF)));
|
||||
context.emit_event(EventType::LocationChanged(Some(DC_CONTACT_ID_SELF)));
|
||||
};
|
||||
schedule_maybe_send_locations(context, false).await.ok();
|
||||
}
|
||||
@@ -423,7 +425,10 @@ pub async fn delete_all(context: &Context) -> Result<()> {
|
||||
pub async fn get_kml(context: &Context, chat_id: ChatId) -> Result<(String, u32)> {
|
||||
let mut last_added_location_id = 0;
|
||||
|
||||
let self_addr = context.get_primary_addr().await?;
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
|
||||
let (locations_send_begin, locations_send_until, locations_last_sent) = context.sql.query_row(
|
||||
"SELECT locations_send_begin, locations_send_until, locations_last_sent FROM chats WHERE id=?;",
|
||||
@@ -458,10 +463,10 @@ pub async fn get_kml(context: &Context, chat_id: ChatId) -> Result<(String, u32)
|
||||
GROUP BY timestamp \
|
||||
ORDER BY timestamp;",
|
||||
paramsv![
|
||||
ContactId::SELF,
|
||||
DC_CONTACT_ID_SELF,
|
||||
locations_send_begin,
|
||||
locations_last_sent,
|
||||
ContactId::SELF
|
||||
DC_CONTACT_ID_SELF
|
||||
],
|
||||
|row| {
|
||||
let location_id: i32 = row.get(0)?;
|
||||
@@ -662,7 +667,7 @@ pub(crate) async fn job_maybe_send_locations(context: &Context, _job: &Job) -> j
|
||||
for (chat_id, locations_send_begin, locations_last_sent) in &rows {
|
||||
if !stmt_locations
|
||||
.exists(paramsv![
|
||||
ContactId::SELF,
|
||||
DC_CONTACT_ID_SELF,
|
||||
*locations_send_begin,
|
||||
*locations_last_sent,
|
||||
])
|
||||
|
||||
@@ -139,18 +139,8 @@ pub struct LoginParam {
|
||||
}
|
||||
|
||||
impl LoginParam {
|
||||
/// Load entered (candidate) account settings
|
||||
pub async fn load_candidate_params(context: &Context) -> Result<Self> {
|
||||
LoginParam::from_database(context, "").await
|
||||
}
|
||||
|
||||
/// Load configured (working) account settings
|
||||
pub async fn load_configured_params(context: &Context) -> Result<Self> {
|
||||
LoginParam::from_database(context, "configured_").await
|
||||
}
|
||||
|
||||
/// Read the login parameters from the database.
|
||||
async fn from_database(context: &Context, prefix: impl AsRef<str>) -> Result<Self> {
|
||||
pub async fn from_database(context: &Context, prefix: impl AsRef<str>) -> Result<Self> {
|
||||
let prefix = prefix.as_ref();
|
||||
let sql = &context.sql;
|
||||
|
||||
@@ -252,8 +242,8 @@ impl LoginParam {
|
||||
}
|
||||
|
||||
/// Save this loginparam to the database.
|
||||
pub async fn save_as_configured_params(&self, context: &Context) -> Result<()> {
|
||||
let prefix = "configured_";
|
||||
pub async fn save_to_database(&self, context: &Context, prefix: impl AsRef<str>) -> Result<()> {
|
||||
let prefix = prefix.as_ref();
|
||||
let sql = &context.sql;
|
||||
|
||||
let key = format!("{}addr", prefix);
|
||||
@@ -448,8 +438,8 @@ mod tests {
|
||||
socks5_config: None,
|
||||
};
|
||||
|
||||
param.save_as_configured_params(&t).await?;
|
||||
let loaded = LoginParam::load_configured_params(&t).await?;
|
||||
param.save_to_database(&t, "foobar_").await?;
|
||||
let loaded = LoginParam::from_database(&t, "foobar_").await?;
|
||||
|
||||
assert_eq!(param, loaded);
|
||||
Ok(())
|
||||
|
||||
@@ -10,7 +10,8 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::chat::{self, Chat, ChatId};
|
||||
use crate::constants::{
|
||||
Blocked, Chattype, VideochatType, DC_CHAT_ID_TRASH, DC_DESIRED_TEXT_LEN, DC_MSG_ID_LAST_SPECIAL,
|
||||
Blocked, Chattype, VideochatType, DC_CHAT_ID_TRASH, DC_CONTACT_ID_INFO, DC_CONTACT_ID_SELF,
|
||||
DC_DESIRED_TEXT_LEN, DC_MSG_ID_LAST_SPECIAL,
|
||||
};
|
||||
use crate::contact::{Contact, ContactId, Origin};
|
||||
use crate::context::Context;
|
||||
@@ -430,7 +431,7 @@ impl Message {
|
||||
/// this is done by dc_set_location() and dc_send_locations_to_chat().
|
||||
///
|
||||
/// Typically results in the event #DC_EVENT_LOCATION_CHANGED with
|
||||
/// contact_id set to ContactId::SELF.
|
||||
/// contact_id set to DC_CONTACT_ID_SELF.
|
||||
///
|
||||
/// @param latitude North-south position of the location.
|
||||
/// @param longitude East-west position of the location.
|
||||
@@ -543,7 +544,7 @@ impl Message {
|
||||
&chat_loaded
|
||||
};
|
||||
|
||||
let contact = if self.from_id != ContactId::SELF {
|
||||
let contact = if self.from_id != DC_CONTACT_ID_SELF {
|
||||
match chat.typ {
|
||||
Chattype::Group | Chattype::Broadcast | Chattype::Mailinglist => {
|
||||
Some(Contact::get_by_id(context, self.from_id).await?)
|
||||
@@ -596,8 +597,8 @@ impl Message {
|
||||
|
||||
pub fn is_info(&self) -> bool {
|
||||
let cmd = self.param.get_cmd();
|
||||
self.from_id == ContactId::INFO
|
||||
|| self.to_id == ContactId::INFO
|
||||
self.from_id == DC_CONTACT_ID_INFO
|
||||
|| self.to_id == DC_CONTACT_ID_INFO
|
||||
|| cmd != SystemMessage::Unknown && cmd != SystemMessage::AutocryptSetupMessage
|
||||
}
|
||||
|
||||
@@ -1016,7 +1017,7 @@ pub async fn get_msg_info(context: &Context, msg_id: MsgId) -> Result<String> {
|
||||
ret += &format!(" by {}", name);
|
||||
ret += "\n";
|
||||
|
||||
if msg.from_id != ContactId::SELF {
|
||||
if msg.from_id != DC_CONTACT_ID_SELF {
|
||||
let s = dc_timestamp_to_str(if 0 != msg.timestamp_rcvd {
|
||||
msg.timestamp_rcvd
|
||||
} else {
|
||||
@@ -1037,7 +1038,7 @@ pub async fn get_msg_info(context: &Context, msg_id: MsgId) -> Result<String> {
|
||||
);
|
||||
}
|
||||
|
||||
if msg.from_id == ContactId::INFO || msg.to_id == ContactId::INFO {
|
||||
if msg.from_id == DC_CONTACT_ID_INFO || msg.to_id == DC_CONTACT_ID_INFO {
|
||||
// device-internal message, no further details needed
|
||||
return Ok(ret);
|
||||
}
|
||||
@@ -1431,7 +1432,7 @@ pub async fn handle_mdn(
|
||||
rfc724_mid: &str,
|
||||
timestamp_sent: i64,
|
||||
) -> Result<Option<(ChatId, MsgId)>> {
|
||||
if from_id == ContactId::SELF {
|
||||
if from_id == DC_CONTACT_ID_SELF {
|
||||
warn!(
|
||||
context,
|
||||
"ignoring MDN sent to self, this is a bug on the sender device"
|
||||
@@ -1636,7 +1637,7 @@ pub async fn estimate_deletion_cnt(
|
||||
from_server: bool,
|
||||
seconds: i64,
|
||||
) -> Result<usize> {
|
||||
let self_chat_id = ChatId::lookup_by_contact(context, ContactId::SELF)
|
||||
let self_chat_id = ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
let threshold_timestamp = time() - seconds;
|
||||
@@ -1804,6 +1805,7 @@ mod tests {
|
||||
|
||||
use crate::chat::{marknoticed_chat, ChatItem};
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::constants::DC_CONTACT_ID_DEVICE;
|
||||
use crate::dc_receive_imf::dc_receive_imf;
|
||||
use crate::test_utils as test;
|
||||
use crate::test_utils::TestContext;
|
||||
@@ -1943,7 +1945,7 @@ mod tests {
|
||||
// test that get_width() and get_height() are returning some dimensions for images;
|
||||
// (as the device-chat contains a welcome-images, we check that)
|
||||
t.update_device_chats().await.ok();
|
||||
let device_chat_id = ChatId::get_for_contact(&t, ContactId::DEVICE)
|
||||
let device_chat_id = ChatId::get_for_contact(&t, DC_CONTACT_ID_DEVICE)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -133,7 +133,11 @@ impl<'a> MimeFactory<'a> {
|
||||
) -> Result<MimeFactory<'a>> {
|
||||
let chat = Chat::load_from_db(context, msg.chat_id).await?;
|
||||
|
||||
let from_addr = context.get_primary_addr().await.unwrap_or_default();
|
||||
let from_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
|
||||
let config_displayname = context
|
||||
.get_config(Config::Displayname)
|
||||
.await?
|
||||
@@ -233,7 +237,10 @@ impl<'a> MimeFactory<'a> {
|
||||
ensure!(!msg.chat_id.is_special(), "Invalid chat id");
|
||||
|
||||
let contact = Contact::load_from_db(context, msg.from_id).await?;
|
||||
let from_addr = context.get_primary_addr().await?;
|
||||
let from_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
let from_displayname = context
|
||||
.get_config(Config::Displayname)
|
||||
.await?
|
||||
@@ -271,7 +278,11 @@ impl<'a> MimeFactory<'a> {
|
||||
&self,
|
||||
context: &Context,
|
||||
) -> Result<Vec<(Option<Peerstate>, &str)>> {
|
||||
let self_addr = context.get_primary_addr().await?;
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.context("not configured")?;
|
||||
|
||||
let mut res = Vec::new();
|
||||
for (_, addr) in self
|
||||
.recipients
|
||||
|
||||
@@ -4,13 +4,11 @@ use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
|
||||
use crate::aheader::{Aheader, EncryptPreference};
|
||||
use crate::chat::{self};
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::chat::{self, ChatIdBlocked};
|
||||
use crate::constants::Blocked;
|
||||
use crate::context::Context;
|
||||
use crate::events::EventType;
|
||||
use crate::key::{DcKey, Fingerprint, SignedPublicKey};
|
||||
use crate::message::Message;
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::sql::Sql;
|
||||
use crate::stock_str;
|
||||
use anyhow::{bail, Result};
|
||||
@@ -273,34 +271,14 @@ impl Peerstate {
|
||||
.query_get_value("SELECT id FROM contacts WHERE addr=?;", paramsv![self.addr])
|
||||
.await?
|
||||
{
|
||||
let chats = Chatlist::try_load(context, 0, None, contact_id).await?;
|
||||
let chat_id = ChatIdBlocked::get_for_contact(context, contact_id, Blocked::Request)
|
||||
.await?
|
||||
.id;
|
||||
|
||||
let msg = stock_str::contact_setup_changed(context, self.addr.clone()).await;
|
||||
for (chat_id, msg_id) in chats.iter() {
|
||||
let timestamp_sort = if let Some(msg_id) = msg_id {
|
||||
let lastmsg = Message::load_from_db(context, *msg_id).await?;
|
||||
lastmsg.timestamp_sort
|
||||
} else {
|
||||
context
|
||||
.sql
|
||||
.query_get_value(
|
||||
"SELECT created_timestamp FROM chats WHERE id=?;",
|
||||
paramsv![chat_id],
|
||||
)
|
||||
.await?
|
||||
.unwrap_or(0)
|
||||
};
|
||||
chat::add_info_msg_with_cmd(
|
||||
context,
|
||||
*chat_id,
|
||||
&msg,
|
||||
SystemMessage::Unknown,
|
||||
timestamp_sort,
|
||||
Some(timestamp),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
context.emit_event(EventType::ChatModified(*chat_id));
|
||||
}
|
||||
|
||||
chat::add_info_msg(context, chat_id, &msg, timestamp).await?;
|
||||
context.emit_event(EventType::ChatModified(chat_id));
|
||||
} else {
|
||||
bail!("contact with peerstate.addr {:?} not found", &self.addr);
|
||||
}
|
||||
|
||||
@@ -711,7 +711,7 @@ mod tests {
|
||||
..
|
||||
} = qr
|
||||
{
|
||||
assert_ne!(contact_id, ContactId::UNDEFINED);
|
||||
assert_ne!(contact_id, ContactId::new(0));
|
||||
assert_eq!(grpname, "test ? test !");
|
||||
} else {
|
||||
bail!("Wrong QR code type");
|
||||
@@ -729,7 +729,7 @@ mod tests {
|
||||
..
|
||||
} = qr
|
||||
{
|
||||
assert_ne!(contact_id, ContactId::UNDEFINED);
|
||||
assert_ne!(contact_id, ContactId::new(0));
|
||||
assert_eq!(grpname, "test ? test !");
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, contact_id).await?;
|
||||
@@ -751,7 +751,7 @@ mod tests {
|
||||
).await?;
|
||||
|
||||
if let Qr::AskVerifyContact { contact_id, .. } = qr {
|
||||
assert_ne!(contact_id, ContactId::UNDEFINED);
|
||||
assert_ne!(contact_id, ContactId::new(0));
|
||||
} else {
|
||||
bail!("Wrong QR code type");
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ use crate::{
|
||||
chat::{Chat, ChatId},
|
||||
color::color_int_to_hex_string,
|
||||
config::Config,
|
||||
contact::{Contact, ContactId},
|
||||
constants::DC_CONTACT_ID_SELF,
|
||||
contact::Contact,
|
||||
context::Context,
|
||||
securejoin, stock_str,
|
||||
};
|
||||
@@ -40,7 +41,7 @@ async fn generate_join_group_qr_code(context: &Context, chat_id: ChatId) -> Resu
|
||||
}
|
||||
|
||||
async fn generate_verification_qr(context: &Context) -> Result<String> {
|
||||
let contact = Contact::get_by_id(context, ContactId::SELF).await?;
|
||||
let contact = Contact::get_by_id(context, DC_CONTACT_ID_SELF).await?;
|
||||
|
||||
let avatar = match contact.get_profile_image(context).await? {
|
||||
Some(path) => {
|
||||
|
||||
@@ -8,7 +8,7 @@ use async_std::{
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::maybe_add_time_based_warnings;
|
||||
use crate::ephemeral::{self, delete_expired_imap_messages};
|
||||
use crate::ephemeral::delete_expired_imap_messages;
|
||||
use crate::imap::Imap;
|
||||
use crate::job::{self, Thread};
|
||||
use crate::log::LogExt;
|
||||
@@ -34,8 +34,6 @@ pub(crate) enum Scheduler {
|
||||
sentbox_handle: Option<task::JoinHandle<()>>,
|
||||
smtp: SmtpConnectionState,
|
||||
smtp_handle: Option<task::JoinHandle<()>>,
|
||||
ephemeral_handle: Option<task::JoinHandle<()>>,
|
||||
ephemeral_interrupt_send: Sender<()>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -61,10 +59,6 @@ impl Context {
|
||||
pub(crate) async fn interrupt_smtp(&self, info: InterruptInfo) {
|
||||
self.scheduler.read().await.interrupt_smtp(info).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn interrupt_ephemeral_task(&self) {
|
||||
self.scheduler.read().await.interrupt_ephemeral_task().await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConnectionHandlers) {
|
||||
@@ -311,7 +305,6 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect
|
||||
.expect("smtp loop, missing started receiver");
|
||||
let ctx = ctx1;
|
||||
|
||||
let mut timeout = None;
|
||||
let mut interrupt_info = Default::default();
|
||||
loop {
|
||||
let job = match job::load_next(&ctx, Thread::Smtp, &interrupt_info).await {
|
||||
@@ -329,16 +322,9 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect
|
||||
interrupt_info = Default::default();
|
||||
}
|
||||
None => {
|
||||
let res = send_smtp_messages(&ctx, &mut connection).await;
|
||||
if let Err(err) = &res {
|
||||
if let Err(err) = send_smtp_messages(&ctx, &mut connection).await {
|
||||
warn!(ctx, "send_smtp_messages failed: {:#}", err);
|
||||
}
|
||||
let success = res.unwrap_or(false);
|
||||
timeout = if success {
|
||||
None
|
||||
} else {
|
||||
Some(timeout.map_or(30, |timeout: u64| timeout.saturating_mul(3)))
|
||||
};
|
||||
|
||||
// Fake Idle
|
||||
info!(ctx, "smtp fake idle - started");
|
||||
@@ -347,27 +333,7 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect
|
||||
Some(err) => connection.connectivity.set_err(&ctx, err).await,
|
||||
}
|
||||
|
||||
// If send_smtp_messages() failed, we set a timeout for the fake-idle so that
|
||||
// sending is retried (at the latest) after the timeout. If sending fails
|
||||
// again, we increase the timeout exponentially, in order not to do lots of
|
||||
// unnecessary retries.
|
||||
if let Some(timeout) = timeout {
|
||||
info!(
|
||||
ctx,
|
||||
"smtp has messages to retry, planning to retry {} seconds later",
|
||||
timeout
|
||||
);
|
||||
let duration = std::time::Duration::from_secs(timeout);
|
||||
interrupt_info = async_std::future::timeout(duration, async {
|
||||
idle_interrupt_receiver.recv().await.unwrap_or_default()
|
||||
})
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
} else {
|
||||
info!(ctx, "smtp has no messages to retry, waiting for interrupt");
|
||||
interrupt_info = idle_interrupt_receiver.recv().await.unwrap_or_default();
|
||||
};
|
||||
|
||||
interrupt_info = idle_interrupt_receiver.recv().await.unwrap_or_default();
|
||||
info!(ctx, "smtp fake idle - interrupted")
|
||||
}
|
||||
}
|
||||
@@ -401,7 +367,6 @@ impl Scheduler {
|
||||
let (sentbox_start_send, sentbox_start_recv) = channel::bounded(1);
|
||||
let mut sentbox_handle = None;
|
||||
let (smtp_start_send, smtp_start_recv) = channel::bounded(1);
|
||||
let (ephemeral_interrupt_send, ephemeral_interrupt_recv) = channel::bounded(1);
|
||||
|
||||
let inbox_handle = {
|
||||
let ctx = ctx.clone();
|
||||
@@ -463,13 +428,6 @@ impl Scheduler {
|
||||
}))
|
||||
};
|
||||
|
||||
let ephemeral_handle = {
|
||||
let ctx = ctx.clone();
|
||||
Some(task::spawn(async move {
|
||||
ephemeral::ephemeral_loop(&ctx, ephemeral_interrupt_recv).await;
|
||||
}))
|
||||
};
|
||||
|
||||
*self = Scheduler::Running {
|
||||
inbox,
|
||||
mvbox,
|
||||
@@ -479,8 +437,6 @@ impl Scheduler {
|
||||
mvbox_handle,
|
||||
sentbox_handle,
|
||||
smtp_handle,
|
||||
ephemeral_handle,
|
||||
ephemeral_interrupt_send,
|
||||
};
|
||||
|
||||
// wait for all loops to be started
|
||||
@@ -546,16 +502,6 @@ impl Scheduler {
|
||||
}
|
||||
}
|
||||
|
||||
async fn interrupt_ephemeral_task(&self) {
|
||||
if let Scheduler::Running {
|
||||
ref ephemeral_interrupt_send,
|
||||
..
|
||||
} = self
|
||||
{
|
||||
ephemeral_interrupt_send.try_send(()).ok();
|
||||
}
|
||||
}
|
||||
|
||||
/// Halts the scheduler, must be called first, and then `stop`.
|
||||
pub(crate) async fn pre_stop(&self) -> StopToken {
|
||||
match self {
|
||||
@@ -602,7 +548,6 @@ impl Scheduler {
|
||||
mvbox_handle,
|
||||
sentbox_handle,
|
||||
smtp_handle,
|
||||
ephemeral_handle,
|
||||
..
|
||||
} => {
|
||||
if let Some(handle) = inbox_handle.take() {
|
||||
@@ -617,9 +562,6 @@ impl Scheduler {
|
||||
if let Some(handle) = smtp_handle.take() {
|
||||
handle.await;
|
||||
}
|
||||
if let Some(handle) = ephemeral_handle.take() {
|
||||
handle.cancel().await;
|
||||
}
|
||||
|
||||
*self = Scheduler::Stopped;
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ impl Context {
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="initial-scale=1.0; user-scalable=no" />
|
||||
<meta name="viewport" content="initial-scale=1.0" />
|
||||
<style>
|
||||
ul {
|
||||
list-style-type: none;
|
||||
@@ -449,7 +449,13 @@ impl Context {
|
||||
// [======67%===== ]
|
||||
// =============================================================================================
|
||||
|
||||
let domain = dc_tools::EmailAddress::new(&self.get_primary_addr().await?)?.domain;
|
||||
let domain = dc_tools::EmailAddress::new(
|
||||
&self
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.unwrap_or_default(),
|
||||
)?
|
||||
.domain;
|
||||
ret += &format!(
|
||||
"<h3>{}</h3><ul>",
|
||||
stock_str::storage_on_domain(self, domain).await
|
||||
|
||||
@@ -8,7 +8,7 @@ use percent_encoding::{utf8_percent_encode, AsciiSet, NON_ALPHANUMERIC};
|
||||
use crate::aheader::EncryptPreference;
|
||||
use crate::chat::{self, Chat, ChatId, ChatIdBlocked};
|
||||
use crate::config::Config;
|
||||
use crate::constants::Blocked;
|
||||
use crate::constants::{Blocked, DC_CONTACT_ID_LAST_SPECIAL};
|
||||
use crate::contact::{Contact, ContactId, Origin, VerifiedStatus};
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::time;
|
||||
@@ -66,7 +66,19 @@ pub async fn dc_get_securejoin_qr(context: &Context, group: Option<ChatId>) -> R
|
||||
.is_none();
|
||||
let invitenumber = token::lookup_or_new(context, Namespace::InviteNumber, group).await;
|
||||
let auth = token::lookup_or_new(context, Namespace::Auth, group).await;
|
||||
let self_addr = context.get_primary_addr().await?;
|
||||
let self_addr = match context.get_config(Config::ConfiguredAddr).await {
|
||||
Ok(Some(addr)) => addr,
|
||||
Ok(None) => {
|
||||
bail!("Not configured, cannot generate QR code.");
|
||||
}
|
||||
Err(err) => {
|
||||
bail!(
|
||||
"Unable to retrieve configuration, cannot generate QR code: {:?}",
|
||||
err
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let self_name = context
|
||||
.get_config(Config::Displayname)
|
||||
.await?
|
||||
@@ -298,7 +310,7 @@ pub(crate) async fn handle_securejoin_handshake(
|
||||
mime_message: &MimeMessage,
|
||||
contact_id: ContactId,
|
||||
) -> Result<HandshakeMessage> {
|
||||
if contact_id.is_special() {
|
||||
if contact_id <= DC_CONTACT_ID_LAST_SPECIAL {
|
||||
return Err(Error::msg("Can not be called with special contact ID"));
|
||||
}
|
||||
let step = mime_message
|
||||
@@ -561,7 +573,7 @@ pub(crate) async fn observe_securejoin_on_other_device(
|
||||
mime_message: &MimeMessage,
|
||||
contact_id: ContactId,
|
||||
) -> Result<HandshakeMessage> {
|
||||
if contact_id.is_special() {
|
||||
if contact_id <= DC_CONTACT_ID_LAST_SPECIAL {
|
||||
return Err(Error::msg("Can not be called with special contact ID"));
|
||||
}
|
||||
let step = mime_message
|
||||
|
||||
132
src/smtp.rs
132
src/smtp.rs
@@ -4,14 +4,14 @@ pub mod send;
|
||||
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use anyhow::{bail, format_err, Context as _, Error, Result};
|
||||
use anyhow::{bail, format_err, Context as _, Result};
|
||||
use async_smtp::smtp::client::net::ClientTlsParameters;
|
||||
use async_smtp::smtp::response::{Category, Code, Detail};
|
||||
use async_smtp::{smtp, EmailAddress, ServerAddress};
|
||||
use async_std::task;
|
||||
|
||||
use crate::constants::DC_LP_AUTH_OAUTH2;
|
||||
use crate::events::EventType;
|
||||
use crate::job::Status;
|
||||
use crate::login_param::{
|
||||
dc_build_tls, CertificateChecks, LoginParam, ServerLoginParam, Socks5Config,
|
||||
};
|
||||
@@ -50,10 +50,7 @@ impl Smtp {
|
||||
/// Disconnect the SMTP transport and drop it entirely.
|
||||
pub async fn disconnect(&mut self) {
|
||||
if let Some(mut transport) = self.transport.take() {
|
||||
// Closing connection with a QUIT command may take some time, especially if it's a
|
||||
// stale connection and an attempt to send the command times out. Send a command in a
|
||||
// separate task to avoid waiting for reply or timeout.
|
||||
task::spawn(async move { transport.close().await });
|
||||
transport.close().await.ok();
|
||||
}
|
||||
self.last_success = None;
|
||||
}
|
||||
@@ -87,7 +84,7 @@ impl Smtp {
|
||||
}
|
||||
|
||||
self.connectivity.set_connecting(context).await;
|
||||
let lp = LoginParam::load_configured_params(context).await?;
|
||||
let lp = LoginParam::from_database(context, "configured_").await?;
|
||||
self.connect(
|
||||
context,
|
||||
&lp.smtp,
|
||||
@@ -199,18 +196,12 @@ impl Smtp {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum SendResult {
|
||||
/// Message was sent successfully.
|
||||
Success,
|
||||
|
||||
/// Permanent error, message sending has failed.
|
||||
Failure(Error),
|
||||
|
||||
/// Temporary error, the message should be retried later.
|
||||
Retry,
|
||||
}
|
||||
|
||||
/// Tries to send a message.
|
||||
///
|
||||
/// Returns Status::Finished if sending the message should not be retried anymore,
|
||||
/// Status::RetryLater if sending should be postponed and Status::RetryNow if it is suspected that
|
||||
/// temporary failure is caused by stale connection, in which case a second attempt to send the
|
||||
/// same message may be done immediately.
|
||||
pub(crate) async fn smtp_send(
|
||||
context: &Context,
|
||||
recipients: &[async_smtp::EmailAddress],
|
||||
@@ -218,7 +209,7 @@ pub(crate) async fn smtp_send(
|
||||
smtp: &mut Smtp,
|
||||
msg_id: MsgId,
|
||||
rowid: i64,
|
||||
) -> SendResult {
|
||||
) -> Status {
|
||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||
info!(context, "smtp-sending out mime message:");
|
||||
println!("{}", message);
|
||||
@@ -226,20 +217,6 @@ pub(crate) async fn smtp_send(
|
||||
|
||||
smtp.connectivity.set_working(context).await;
|
||||
|
||||
if smtp.has_maybe_stale_connection().await {
|
||||
info!(context, "Closing stale connection");
|
||||
smtp.disconnect().await;
|
||||
|
||||
if let Err(err) = smtp
|
||||
.connect_configured(context)
|
||||
.await
|
||||
.context("failed to reopen stale SMTP connection")
|
||||
{
|
||||
smtp.last_send_error = Some(format!("{:#}", err));
|
||||
return SendResult::Retry;
|
||||
}
|
||||
}
|
||||
|
||||
let send_result = smtp
|
||||
.send(context, recipients, message.as_bytes(), rowid)
|
||||
.await;
|
||||
@@ -276,14 +253,14 @@ pub(crate) async fn smtp_send(
|
||||
|
||||
if maybe_transient {
|
||||
info!(context, "Permanent error that is likely to actually be transient, postponing retry for later");
|
||||
SendResult::Retry
|
||||
Status::RetryLater
|
||||
} else {
|
||||
info!(context, "Permanent error, message sending failed");
|
||||
// If we do not retry, add an info message to the chat.
|
||||
// Yandex error "554 5.7.1 [2] Message rejected under suspicion of SPAM; https://ya.cc/..."
|
||||
// should definitely go here, because user has to open the link to
|
||||
// resume message sending.
|
||||
SendResult::Failure(format_err!("Permanent SMTP error: {}", err))
|
||||
Status::Finished(Err(format_err!("Permanent SMTP error: {}", err)))
|
||||
}
|
||||
}
|
||||
async_smtp::smtp::error::Error::Transient(ref response) => {
|
||||
@@ -300,29 +277,35 @@ pub(crate) async fn smtp_send(
|
||||
// receive as a transient error are misconfigurations of the smtp server.
|
||||
// See <https://tools.ietf.org/html/rfc3463#section-3.2>
|
||||
info!(context, "Received extended status code {} for a transient error. This looks like a misconfigured smtp server, let's fail immediatly", first_word);
|
||||
SendResult::Failure(format_err!("Permanent SMTP error: {}", err))
|
||||
Status::Finished(Err(format_err!("Permanent SMTP error: {}", err)))
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"Transient error with status code {}, postponing retry for later",
|
||||
first_word
|
||||
);
|
||||
SendResult::Retry
|
||||
Status::RetryLater
|
||||
}
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"Transient error without status code, postponing retry for later"
|
||||
);
|
||||
SendResult::Retry
|
||||
Status::RetryLater
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
info!(
|
||||
context,
|
||||
"Message sending failed without error returned by the server, retry later"
|
||||
"Message sending failed without error returned by the server"
|
||||
);
|
||||
SendResult::Retry
|
||||
if smtp.has_maybe_stale_connection().await {
|
||||
info!(context, "Connection is probably stale, retry immediately");
|
||||
Status::RetryNow
|
||||
} else {
|
||||
info!(context, "Connection is not stale, retry later");
|
||||
Status::RetryLater
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -336,24 +319,24 @@ pub(crate) async fn smtp_send(
|
||||
// Local error, job is invalid, do not retry.
|
||||
smtp.disconnect().await;
|
||||
warn!(context, "SMTP job is invalid: {}", err);
|
||||
SendResult::Failure(err.into())
|
||||
Status::Finished(Err(err.into()))
|
||||
}
|
||||
Err(crate::smtp::send::Error::NoTransport) => {
|
||||
// Should never happen.
|
||||
// It does not even make sense to disconnect here.
|
||||
error!(context, "SMTP job failed because SMTP has no transport");
|
||||
SendResult::Failure(format_err!("SMTP has not transport"))
|
||||
Status::Finished(Err(format_err!("SMTP has not transport")))
|
||||
}
|
||||
Err(crate::smtp::send::Error::Other(err)) => {
|
||||
// Local error, job is invalid, do not retry.
|
||||
smtp.disconnect().await;
|
||||
warn!(context, "unable to load job: {}", err);
|
||||
SendResult::Failure(err)
|
||||
Status::Finished(Err(err))
|
||||
}
|
||||
Ok(()) => SendResult::Success,
|
||||
Ok(()) => Status::Finished(Ok(())),
|
||||
};
|
||||
|
||||
if let SendResult::Failure(err) = &status {
|
||||
if let Status::Finished(Err(err)) = &status {
|
||||
// We couldn't send the message, so mark it as failed
|
||||
message::set_msg_failed(context, msg_id, Some(err.to_string())).await;
|
||||
}
|
||||
@@ -449,7 +432,7 @@ pub(crate) async fn send_msg_to_smtp(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let status = smtp_send(
|
||||
let status = match smtp_send(
|
||||
context,
|
||||
&recipients_list,
|
||||
body.as_str(),
|
||||
@@ -457,25 +440,47 @@ pub(crate) async fn send_msg_to_smtp(
|
||||
msg_id,
|
||||
rowid,
|
||||
)
|
||||
.await;
|
||||
.await
|
||||
{
|
||||
Status::RetryNow => {
|
||||
// Do a single retry immediately without increasing retry counter in case of stale
|
||||
// connection.
|
||||
info!(context, "Doing immediate retry to send message.");
|
||||
|
||||
// smtp_send just closed stale SMTP connection, reconnect and try again.
|
||||
if let Err(err) = smtp
|
||||
.connect_configured(context)
|
||||
.await
|
||||
.context("failed to reopen stale SMTP connection")
|
||||
{
|
||||
smtp.last_send_error = Some(format!("{:#}", err));
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
smtp_send(
|
||||
context,
|
||||
&recipients_list,
|
||||
body.as_str(),
|
||||
smtp,
|
||||
msg_id,
|
||||
rowid,
|
||||
)
|
||||
.await
|
||||
}
|
||||
status => status,
|
||||
};
|
||||
match status {
|
||||
SendResult::Retry => {}
|
||||
SendResult::Success | SendResult::Failure(_) => {
|
||||
Status::Finished(res) => {
|
||||
context
|
||||
.sql
|
||||
.execute("DELETE FROM smtp WHERE id=?", paramsv![rowid])
|
||||
.await?;
|
||||
if res.is_ok() {
|
||||
msg_id.set_delivered(context).await?;
|
||||
}
|
||||
res
|
||||
}
|
||||
};
|
||||
|
||||
match status {
|
||||
SendResult::Retry => Err(format_err!("Retry")),
|
||||
SendResult::Success => {
|
||||
msg_id.set_delivered(context).await?;
|
||||
Ok(())
|
||||
}
|
||||
SendResult::Failure(err) => Err(format_err!("{}", err)),
|
||||
Status::RetryNow | Status::RetryLater => Err(format_err!("Retry")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,9 +488,10 @@ pub(crate) async fn send_msg_to_smtp(
|
||||
///
|
||||
/// Logs and ignores SMTP errors to ensure that a single SMTP message constantly failing to be sent
|
||||
/// does not block other messages in the queue from being sent.
|
||||
///
|
||||
/// Returns true if all messages were sent successfully, false otherwise.
|
||||
pub(crate) async fn send_smtp_messages(context: &Context, connection: &mut Smtp) -> Result<bool> {
|
||||
pub(crate) async fn send_smtp_messages(
|
||||
context: &Context,
|
||||
connection: &mut Smtp,
|
||||
) -> anyhow::Result<()> {
|
||||
context.send_sync_msg().await?; // Add sync message to the end of the queue if needed.
|
||||
let rowids = context
|
||||
.sql
|
||||
@@ -503,12 +509,10 @@ pub(crate) async fn send_smtp_messages(context: &Context, connection: &mut Smtp)
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let mut success = true;
|
||||
for rowid in rowids {
|
||||
if let Err(err) = send_msg_to_smtp(context, connection, rowid).await {
|
||||
info!(context, "Failed to send message over SMTP: {:#}.", err);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
Ok(success)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
87
src/sql.rs
87
src/sql.rs
@@ -599,55 +599,10 @@ impl Sql {
|
||||
}
|
||||
|
||||
pub async fn housekeeping(context: &Context) -> Result<()> {
|
||||
if let Err(err) = remove_unused_files(context).await {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: cannot remove unusued files: {}", err
|
||||
);
|
||||
if let Err(err) = crate::ephemeral::delete_expired_messages(context).await {
|
||||
warn!(context, "Failed to delete expired messages: {}", err);
|
||||
}
|
||||
|
||||
if let Err(err) = start_ephemeral_timers(context).await {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: cannot start ephemeral timers: {}", err
|
||||
);
|
||||
}
|
||||
|
||||
if let Err(err) = prune_tombstones(&context.sql).await {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: Cannot prune message tombstones: {}", err
|
||||
);
|
||||
}
|
||||
|
||||
if let Err(err) = deduplicate_peerstates(&context.sql).await {
|
||||
warn!(context, "Failed to deduplicate peerstates: {}", err)
|
||||
}
|
||||
|
||||
context.schedule_quota_update().await?;
|
||||
|
||||
// Try to clear the freelist to free some space on the disk. This
|
||||
// only works if auto_vacuum is enabled.
|
||||
if let Err(err) = context
|
||||
.sql
|
||||
.execute("PRAGMA incremental_vacuum", paramsv![])
|
||||
.await
|
||||
{
|
||||
warn!(context, "Failed to run incremental vacuum: {}", err);
|
||||
}
|
||||
|
||||
if let Err(e) = context
|
||||
.set_config(Config::LastHousekeeping, Some(&time().to_string()))
|
||||
.await
|
||||
{
|
||||
warn!(context, "Can't set config: {}", e);
|
||||
}
|
||||
|
||||
info!(context, "Housekeeping done.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
||||
let mut files_in_use = HashSet::new();
|
||||
let mut unreferenced_count = 0;
|
||||
|
||||
@@ -762,6 +717,44 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = start_ephemeral_timers(context).await {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: cannot start ephemeral timers: {}", err
|
||||
);
|
||||
}
|
||||
|
||||
if let Err(err) = prune_tombstones(&context.sql).await {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: Cannot prune message tombstones: {}", err
|
||||
);
|
||||
}
|
||||
|
||||
if let Err(err) = deduplicate_peerstates(&context.sql).await {
|
||||
warn!(context, "Failed to deduplicate peerstates: {}", err)
|
||||
}
|
||||
|
||||
context.schedule_quota_update().await?;
|
||||
|
||||
// Try to clear the freelist to free some space on the disk. This
|
||||
// only works if auto_vacuum is enabled.
|
||||
if let Err(err) = context
|
||||
.sql
|
||||
.execute("PRAGMA incremental_vacuum", paramsv![])
|
||||
.await
|
||||
{
|
||||
warn!(context, "Failed to run incremental vacuum: {}", err);
|
||||
}
|
||||
|
||||
if let Err(e) = context
|
||||
.set_config(Config::LastHousekeeping, Some(&time().to_string()))
|
||||
.await
|
||||
{
|
||||
warn!(context, "Can't set config: {}", e);
|
||||
}
|
||||
|
||||
info!(context, "Housekeeping done.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -395,7 +395,7 @@ UPDATE chats SET protected=1, type=120 WHERE type=130;"#,
|
||||
|
||||
if dbversion < 71 {
|
||||
info!(context, "[migration] v71");
|
||||
if let Ok(addr) = context.get_primary_addr().await {
|
||||
if let Some(addr) = context.get_config(Config::ConfiguredAddr).await? {
|
||||
if let Ok(domain) = addr.parse::<EmailAddress>().map(|email| email.domain) {
|
||||
context
|
||||
.set_config(
|
||||
@@ -608,11 +608,6 @@ CREATE INDEX smtp_messageid ON imap(rfc724_mid);
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
if dbversion < 88 {
|
||||
info!(context, "[migration] v88");
|
||||
sql.execute_migration("DROP TABLE IF EXISTS backup_blobs;", 88)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok((
|
||||
recalc_fingerprints,
|
||||
|
||||
@@ -10,6 +10,7 @@ use strum_macros::EnumProperty;
|
||||
use crate::blob::BlobObject;
|
||||
use crate::chat::{self, Chat, ChatId, ProtectionStatus};
|
||||
use crate::config::Config;
|
||||
use crate::constants::DC_CONTACT_ID_SELF;
|
||||
use crate::contact::{Contact, ContactId, Origin};
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::dc_timestamp_to_str;
|
||||
@@ -396,7 +397,7 @@ trait StockStringMods: AsRef<str> + Sized {
|
||||
Box::pin(async move {
|
||||
let message = self.as_ref().trim_end_matches('.');
|
||||
match contact_id {
|
||||
ContactId::SELF => msg_action_by_me(context, message).await,
|
||||
DC_CONTACT_ID_SELF => msg_action_by_me(context, message).await,
|
||||
_ => {
|
||||
let displayname = Contact::get_by_id(context, contact_id)
|
||||
.await
|
||||
@@ -1128,7 +1129,7 @@ impl Context {
|
||||
self.sql
|
||||
.set_raw_config_bool("self-chat-added", true)
|
||||
.await?;
|
||||
ChatId::create_for_contact(self, ContactId::SELF).await?;
|
||||
ChatId::create_for_contact(self, DC_CONTACT_ID_SELF).await?;
|
||||
}
|
||||
|
||||
// add welcome-messages. by the label, this is done only once,
|
||||
@@ -1148,13 +1149,14 @@ impl Context {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use num_traits::ToPrimitive;
|
||||
use super::*;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
use crate::constants::DC_CONTACT_ID_SELF;
|
||||
|
||||
use crate::chat::Chat;
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
use super::*;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
#[test]
|
||||
fn test_enum_mapping() {
|
||||
@@ -1231,7 +1233,7 @@ mod tests {
|
||||
async fn test_stock_system_msg_add_member_by_me() {
|
||||
let t = TestContext::new().await;
|
||||
assert_eq!(
|
||||
msg_add_member(&t, "alice@example.org", ContactId::SELF).await,
|
||||
msg_add_member(&t, "alice@example.org", DC_CONTACT_ID_SELF).await,
|
||||
"Member alice@example.org added by me."
|
||||
)
|
||||
}
|
||||
@@ -1243,7 +1245,7 @@ mod tests {
|
||||
.await
|
||||
.expect("failed to create contact");
|
||||
assert_eq!(
|
||||
msg_add_member(&t, "alice@example.org", ContactId::SELF).await,
|
||||
msg_add_member(&t, "alice@example.org", DC_CONTACT_ID_SELF).await,
|
||||
"Member Alice (alice@example.org) added by me."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! # Message summary for chatlist.
|
||||
|
||||
use crate::chat::Chat;
|
||||
use crate::constants::Chattype;
|
||||
use crate::contact::{Contact, ContactId};
|
||||
use crate::constants::{Chattype, DC_CONTACT_ID_SELF};
|
||||
use crate::contact::Contact;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::dc_truncate;
|
||||
use crate::message::{Message, MessageState, Viewtype};
|
||||
@@ -60,7 +60,7 @@ impl Summary {
|
||||
) -> Self {
|
||||
let prefix = if msg.state == MessageState::OutDraft {
|
||||
Some(SummaryPrefix::Draft(stock_str::draft(context).await))
|
||||
} else if msg.from_id == ContactId::SELF {
|
||||
} else if msg.from_id == DC_CONTACT_ID_SELF {
|
||||
if msg.is_info() || chat.is_self_talk() {
|
||||
None
|
||||
} else {
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
use crate::chat::{Chat, ChatId};
|
||||
use crate::config::Config;
|
||||
use crate::constants::Blocked;
|
||||
use crate::contact::ContactId;
|
||||
use crate::constants::{Blocked, DC_CONTACT_ID_SELF};
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::time;
|
||||
use crate::message::{Message, MsgId, Viewtype};
|
||||
@@ -127,7 +126,7 @@ impl Context {
|
||||
pub async fn send_sync_msg(&self) -> Result<Option<MsgId>> {
|
||||
if let Some((json, ids)) = self.build_sync_json().await? {
|
||||
let chat_id =
|
||||
ChatId::create_for_contact_with_blocked(self, ContactId::SELF, Blocked::Yes)
|
||||
ChatId::create_for_contact_with_blocked(self, DC_CONTACT_ID_SELF, Blocked::Yes)
|
||||
.await?;
|
||||
let mut msg = Message {
|
||||
chat_id,
|
||||
@@ -484,7 +483,7 @@ mod tests {
|
||||
// check that the used self-talk is not visible to the user
|
||||
// but that creation will still work (in this case, the chat is empty)
|
||||
assert_eq!(Chatlist::try_load(&alice, 0, None, None).await?.len(), 0);
|
||||
let chat_id = ChatId::create_for_contact(&alice, ContactId::SELF).await?;
|
||||
let chat_id = ChatId::create_for_contact(&alice, DC_CONTACT_ID_SELF).await?;
|
||||
let chat = Chat::load_from_db(&alice, chat_id).await?;
|
||||
assert!(chat.is_self_talk());
|
||||
assert_eq!(Chatlist::try_load(&alice, 0, None, None).await?.len(), 1);
|
||||
|
||||
@@ -22,8 +22,8 @@ use crate::chat::{self, Chat, ChatId};
|
||||
use crate::chatlist::Chatlist;
|
||||
use crate::config::Config;
|
||||
use crate::constants::Chattype;
|
||||
use crate::constants::{DC_MSG_ID_DAYMARKER, DC_MSG_ID_MARKER1};
|
||||
use crate::contact::{Contact, ContactId, Modifier, Origin};
|
||||
use crate::constants::{DC_CONTACT_ID_SELF, DC_MSG_ID_DAYMARKER, DC_MSG_ID_MARKER1};
|
||||
use crate::contact::{Contact, Modifier, Origin};
|
||||
use crate::context::Context;
|
||||
use crate::dc_receive_imf::dc_receive_imf;
|
||||
use crate::dc_tools::EmailAddress;
|
||||
@@ -407,7 +407,12 @@ impl TestContext {
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.unwrap_or_default();
|
||||
let addr = other.ctx.get_primary_addr().await.unwrap();
|
||||
let addr = other
|
||||
.ctx
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
// MailinglistAddress is the lowest allowed origin, we'd prefer to not modify the
|
||||
// origin when creating this contact.
|
||||
let (contact_id, modified) =
|
||||
@@ -461,7 +466,7 @@ impl TestContext {
|
||||
|
||||
/// Retrieves the "self" chat.
|
||||
pub async fn get_self_chat(&self) -> Chat {
|
||||
let chat_id = ChatId::create_for_contact(self, ContactId::SELF)
|
||||
let chat_id = ChatId::create_for_contact(self, DC_CONTACT_ID_SELF)
|
||||
.await
|
||||
.unwrap();
|
||||
Chat::load_from_db(self, chat_id).await.unwrap()
|
||||
@@ -846,7 +851,7 @@ async fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
&contact_name,
|
||||
contact_id,
|
||||
msgtext.unwrap_or_default(),
|
||||
if msg.get_from_id() == ContactId::SELF {
|
||||
if msg.get_from_id() == DC_CONTACT_ID_SELF {
|
||||
""
|
||||
} else if msg.get_state() == MessageState::InSeen {
|
||||
"[SEEN]"
|
||||
|
||||
@@ -19,12 +19,6 @@ use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use zip::ZipArchive;
|
||||
|
||||
/// The current API version.
|
||||
/// If `min_api` in manifest.toml is set to a larger value,
|
||||
/// the Webxdc's index.html is replaced by an error message.
|
||||
/// In the future, that may be useful to avoid new Webxdc being loaded on old Delta Chats.
|
||||
const WEBXDC_API_VERSION: u32 = 1;
|
||||
|
||||
pub const WEBXDC_SUFFIX: &str = "xdc";
|
||||
const WEBXDC_DEFAULT_ICON: &str = "__webxdc__/default-icon.png";
|
||||
|
||||
@@ -50,7 +44,6 @@ const WEBXDC_RECEIVING_LIMIT: u64 = 4194304;
|
||||
#[non_exhaustive]
|
||||
struct WebxdcManifest {
|
||||
name: Option<String>,
|
||||
min_api: Option<u32>,
|
||||
}
|
||||
|
||||
/// Parsed information from WebxdcManifest and fallbacks.
|
||||
@@ -226,7 +219,6 @@ impl Context {
|
||||
info.as_str(),
|
||||
SystemMessage::Unknown,
|
||||
timestamp,
|
||||
None,
|
||||
Some(instance),
|
||||
)
|
||||
.await?;
|
||||
@@ -506,21 +498,6 @@ impl Message {
|
||||
};
|
||||
|
||||
let mut archive = self.get_webxdc_archive(context).await?;
|
||||
|
||||
if name == "index.html" {
|
||||
if let Ok(bytes) = get_blob(&mut archive, "manifest.toml").await {
|
||||
if let Ok(manifest) = parse_webxdc_manifest(&bytes).await {
|
||||
if let Some(min_api) = manifest.min_api {
|
||||
if min_api > WEBXDC_API_VERSION {
|
||||
return Ok(Vec::from(
|
||||
"<!DOCTYPE html>This Webxdc requires a newer Delta Chat version.",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_blob(&mut archive, name).await
|
||||
}
|
||||
|
||||
@@ -533,16 +510,10 @@ impl Message {
|
||||
if let Ok(manifest) = parse_webxdc_manifest(&bytes).await {
|
||||
manifest
|
||||
} else {
|
||||
WebxdcManifest {
|
||||
name: None,
|
||||
min_api: None,
|
||||
}
|
||||
WebxdcManifest { name: None }
|
||||
}
|
||||
} else {
|
||||
WebxdcManifest {
|
||||
name: None,
|
||||
min_api: None,
|
||||
}
|
||||
WebxdcManifest { name: None }
|
||||
};
|
||||
|
||||
if let Some(ref name) = manifest.name {
|
||||
@@ -1323,38 +1294,6 @@ sth_for_the = "future""#
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(manifest.name, Some("foz".to_string()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_parse_webxdc_manifest_min_api() -> Result<()> {
|
||||
let manifest = parse_webxdc_manifest(r#"min_api = 3"#.as_bytes()).await?;
|
||||
assert_eq!(manifest.min_api, Some(3));
|
||||
|
||||
let result = parse_webxdc_manifest(r#"min_api = "1""#.as_bytes()).await;
|
||||
assert!(result.is_err());
|
||||
|
||||
let result = parse_webxdc_manifest(r#"min_api = 1.2"#.as_bytes()).await;
|
||||
assert!(result.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_webxdc_min_api_too_large() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?;
|
||||
let mut instance = create_webxdc_instance(
|
||||
&t,
|
||||
"with-min-api-1001.xdc",
|
||||
include_bytes!("../test-data/webxdc/with-min-api-1001.xdc"),
|
||||
)
|
||||
.await?;
|
||||
send_msg(&t, chat_id, &mut instance).await?;
|
||||
|
||||
let instance = t.get_last_msg().await;
|
||||
let html = instance.get_webxdc_blob(&t, "index.html").await?;
|
||||
assert!(String::from_utf8_lossy(&*html).contains("requires a newer Delta Chat version"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user