Compare commits

..

25 Commits

Author SHA1 Message Date
Hocuri
9cb446981d save work 2022-04-07 11:21:27 +02:00
dependabot[bot]
7bfdf2e2f5 Merge pull request #3189 from deltachat/dependabot/cargo/zip-0.6.2 2022-04-05 08:27:49 +00:00
dependabot[bot]
d9ac5d88e9 cargo: bump zip from 0.6.1 to 0.6.2
Bumps [zip](https://github.com/zip-rs/zip) from 0.6.1 to 0.6.2.
- [Release notes](https://github.com/zip-rs/zip/releases)
- [Commits](https://github.com/zip-rs/zip/commits)

---
updated-dependencies:
- dependency-name: zip
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-04 21:13:27 +00:00
bjoern
2cf11bb2ea muted chats stay archived (#3184)
* do not unarchive muted chats on sending/receving messages

* add a test for unarchive muted chats

* update CHANGELOG

* improve test_unarchive_if_muted
2022-04-04 11:02:36 +02:00
link2xt
de91063fbe scheduler: add comment about fake-idle timeout 2022-04-03 19:30:56 +00:00
link2xt
3d95272707 smtp: retry message sending automatically if loop is not interrupted 2022-04-03 18:55:29 +00:00
Floris Bruynooghe
3c20d0902e Add explicit tests for special ContactId values
Turns out some FFI users don't use the constants.  Scary.
2022-04-03 20:35:09 +02:00
Floris Bruynooghe
f2c1e5c6e5 Replace some ContactId::new() calls with constants 2022-04-03 20:35:09 +02:00
Floris Bruynooghe
feb354725a Make ContactId::LAST_SPECIAL private
Also remove the Ord implementations, this makes ContactId more opaque
by no longer letting people deal with the fact this is ordered.
2022-04-03 20:35:09 +02:00
Floris Bruynooghe
35c0434dc7 Move ContactId constants to struct.
This makes the APIs much more Rust-like and keep contact IDs clearer
and in one place.
2022-04-03 20:35:09 +02:00
Hocuri
918ee47c79 Consider outgoing messages to just one receiver as "private message" (#3177) 2022-04-03 19:19:10 +02:00
link2xt
a8ab7c9c04 smtp: do not try to use stale connections
Previously first try used an old connection and. If using stale
connection, an immediate retry was performed using a new connection.

Now if connection is stale, it is closed immediately without trying to
use it.

This should reduce the delay in cases when old connection is unusable.
2022-04-03 13:11:27 +00:00
link2xt
332892b468 ephemeral: clear more fields in delete_expired_messages
These fields are cleared in other places,
but in the case of delete_device_after setting
only txt column was cleared previously.
2022-04-02 17:10:09 +00:00
link2xt
6392300311 job: remove unnecessary anyhow::Error import 2022-04-02 17:06:22 +00:00
Floris Bruynooghe
16d201faca Re-enable custom Display for ContactId
Caught another case of using Display instead of ToSql, now all tests
pass again.
2022-04-02 17:19:00 +02:00
dependabot[bot]
ea0cf67f98 cargo: bump zip from 0.6.0 to 0.6.1
Bumps [zip](https://github.com/zip-rs/zip) from 0.6.0 to 0.6.1.
- [Release notes](https://github.com/zip-rs/zip/releases)
- [Commits](https://github.com/zip-rs/zip/commits)

---
updated-dependencies:
- dependency-name: zip
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-02 16:09:44 +02:00
holger krekel
612132b7c8 move invariant out of loop, less LOC and 1.5% faster 2022-04-01 14:33:57 +02:00
holger krekel
26470c6047 apply hocuri's niceifcation 2022-04-01 14:33:57 +02:00
holger krekel
93d3522f67 stylistic changes 2022-04-01 14:33:57 +02:00
holger krekel
c6d901d799 first iteration of faster sorting 2022-04-01 14:33:57 +02:00
B. Petersen
4880f9ff32 improve repl message search
- allow spaces in queries
- show query and scope below result
2022-04-01 11:46:26 +02:00
holger krekel
aaa42a3412 feedback if missing env var 2022-03-31 17:05:33 +02:00
holger krekel
3e5e852e20 1.30 is imminent so i think it makes sense to do a cargo-update now for all deps, to detect issues as early as possible before releases go to stores. 2022-03-31 16:53:38 +02:00
holger krekel
0a3f44bd73 housekeeping cleanup: factor out remove-unused-files logic 2022-03-31 16:45:58 +02:00
holger krekel
d4fed5f5f7 add chatlist loading benchmark 2022-03-31 16:45:45 +02:00
31 changed files with 857 additions and 627 deletions

View File

@@ -10,9 +10,14 @@
- 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 delete duplicate messages on IMAP immediately to accidentally deleting
the last copy #3138
- speed up loading of chat messages #3171
- clear more columns when message expires due to `delete_device_after` setting #3181
- do not try to use stale SMTP connections #3180
- retry message sending automatically if loop is not interrupted #3183
### Changes
- add more SMTP logging #3093
@@ -24,6 +29,7 @@
- automatically accept chats with outgoing messages #3143
- `dc_receive_imf` refactorings #3154 #3156
- add index to speedup deletion of expired ephemeral messages #3155
- muted chats stay archived on new messages #3184
### Fixes

227
Cargo.lock generated
View File

@@ -89,7 +89,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom 0.2.4",
"getrandom 0.2.6",
"once_cell",
"version_check 0.9.4",
]
@@ -177,9 +177,9 @@ dependencies = [
[[package]]
name = "async-global-executor"
version = "2.0.2"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6"
checksum = "c026b7e44f1316b567ee750fea85103f87fcb80792b860e979f221259796ca0a"
dependencies = [
"async-channel",
"async-executor",
@@ -249,9 +249,9 @@ dependencies = [
[[package]]
name = "async-lock"
version = "2.4.0"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b"
checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
dependencies = [
"event-listener",
]
@@ -347,9 +347,9 @@ dependencies = [
[[package]]
name = "async-std-resolver"
version = "0.21.1"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e97652c63a409ec955622c07244fea8511197767f1ab362a78ddb8a15b8a00a5"
checksum = "0f2f8a4a203be3325981310ab243a28e6e4ea55b6519bffce05d41ab60e09ad8"
dependencies = [
"async-std",
"async-trait",
@@ -375,9 +375,9 @@ dependencies = [
[[package]]
name = "async-task"
version = "4.1.0"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d306121baf53310a3fd342d88dc0824f6bbeace68347593658525565abee8"
checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9"
[[package]]
name = "async-trait"
@@ -531,9 +531,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
[[package]]
name = "blocking"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046e47d4b2d391b1f6f8b407b1deb8dee56c1852ccd868becf2710f601b5f427"
checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
dependencies = [
"async-channel",
"async-task",
@@ -600,9 +600,9 @@ dependencies = [
[[package]]
name = "bytemuck"
version = "1.7.3"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f"
checksum = "ee1e0e2125faccb856bf10b0a9dfa89c4c718d05ef85580dfefbdf1c422ef801"
[[package]]
name = "byteorder"
@@ -638,9 +638,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.72"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfb-mode"
@@ -775,7 +775,7 @@ dependencies = [
"hkdf",
"hmac",
"percent-encoding",
"rand 0.8.4",
"rand 0.8.5",
"sha2 0.9.9",
"time 0.2.27",
"version_check 0.9.4",
@@ -799,9 +799,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
dependencies = [
"libc",
]
@@ -867,9 +867,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.2"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
@@ -888,10 +888,11 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.7"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
dependencies = [
"autocfg 1.1.0",
"cfg-if 1.0.0",
"crossbeam-utils",
"lazy_static",
@@ -901,9 +902,9 @@ dependencies = [
[[package]]
name = "crossbeam-queue"
version = "0.3.4"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dd435b205a4842da59efd07628f921c096bc1cc0a156835b4fa0bcb9a19bcce"
checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
@@ -911,9 +912,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.7"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [
"cfg-if 1.0.0",
"lazy_static",
@@ -963,9 +964,9 @@ dependencies = [
[[package]]
name = "ctor"
version = "0.1.21"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa"
checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
dependencies = [
"quote",
"syn",
@@ -982,9 +983,9 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
version = "3.2.0"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0"
dependencies = [
"byteorder",
"digest 0.9.0",
@@ -1238,9 +1239,9 @@ dependencies = [
[[package]]
name = "dirs-sys"
version = "0.3.6"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users",
@@ -1266,9 +1267,9 @@ checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]]
name = "ed25519"
version = "1.3.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816"
checksum = "3d5c4b5e5959dc2c2b89918d8e2cc40fcdd623cef026ed09d2f0ee05199dc8e4"
dependencies = [
"signature",
]
@@ -1523,9 +1524,9 @@ dependencies = [
[[package]]
name = "fd-lock"
version = "3.0.3"
version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcef756dea9cf3db5ce73759cf0467330427a786b47711b8d6c97620d718ceb9"
checksum = "46e245f4c8ec30c6415c56cb132c07e69e74f1942f6b4a4061da748b49f486ca"
dependencies = [
"cfg-if 1.0.0",
"rustix",
@@ -1720,9 +1721,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.4"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
dependencies = [
"cfg-if 1.0.0",
"libc",
@@ -1989,12 +1990,9 @@ dependencies = [
[[package]]
name = "io-lifetimes"
version = "0.4.4"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ef6787e7f0faedc040f95716bdd0e62bcfcf4ba93da053b62dea2691c13864"
dependencies = [
"winapi",
]
checksum = "9448015e586b611e5d322f6703812bbca2f1e709d5773ecd38ddb4e3bb649504"
[[package]]
name = "ipconfig"
@@ -2010,9 +2008,9 @@ dependencies = [
[[package]]
name = "ipnet"
version = "2.3.1"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c"
[[package]]
name = "itertools"
@@ -2037,9 +2035,9 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "jpeg-decoder"
version = "0.2.2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "105fb082d64e2100074587f59a74231f771750c664af903f1f9f76c9dedfc6f1"
checksum = "a55ad40a2d27923f63b6fbde5934926176c69ac5e23da6ac05ebfaca984e163f"
[[package]]
name = "js-sys"
@@ -2134,9 +2132,9 @@ checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db"
[[package]]
name = "libsqlite3-sys"
version = "0.24.0"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b8f2caab464860c64cdf0a7435d36906e25894b67e774b5dd44146ef1cd0420"
checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
dependencies = [
"cc",
"openssl-sys",
@@ -2152,16 +2150,17 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "linux-raw-sys"
version = "0.0.37"
version = "0.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95f5690fef754d905294c56f7ac815836f2513af966aa47f2e07ac79be07827f"
checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7"
[[package]]
name = "lock_api"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [
"autocfg 1.1.0",
"scopeguard",
]
@@ -2241,9 +2240,9 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mime_guess"
version = "2.0.3"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
@@ -2489,9 +2488,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "111.17.0+1.1.1m"
version = "111.18.0+1.1.1n"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d6a336abd10814198f66e2a91ccd7336611f30334119ca8ce300536666fcf4"
checksum = "7897a926e1e8d00219127dc020130eca4292e5ca666dd592480d72c3eca2ff6c"
dependencies = [
"cc",
]
@@ -2575,7 +2574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [
"lock_api",
"parking_lot_core 0.9.1",
"parking_lot_core 0.9.2",
]
[[package]]
@@ -2594,15 +2593,15 @@ dependencies = [
[[package]]
name = "parking_lot_core"
version = "0.9.1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
"smallvec",
"windows-sys 0.32.0",
"windows-sys 0.34.0",
]
[[package]]
@@ -2840,7 +2839,7 @@ dependencies = [
"lazy_static",
"num-traits",
"quick-error 2.0.1",
"rand 0.8.4",
"rand 0.8.5",
"rand_chacha 0.3.1",
"rand_xorshift",
"regex-syntax",
@@ -2936,19 +2935,18 @@ dependencies = [
"packed_simd",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc 0.2.0",
"rand_hc",
]
[[package]]
name = "rand"
version = "0.8.4"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.3",
"rand_hc 0.3.1",
]
[[package]]
@@ -2986,7 +2984,7 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom 0.2.4",
"getrandom 0.2.6",
]
[[package]]
@@ -2998,15 +2996,6 @@ 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"
@@ -3043,21 +3032,22 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.2.10"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.4.0"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom 0.2.4",
"getrandom 0.2.6",
"redox_syscall",
"thiserror",
]
[[package]]
@@ -3177,14 +3167,14 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver 1.0.5",
"semver 1.0.7",
]
[[package]]
name = "rustix"
version = "0.32.1"
version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cee647393af53c750e15dcbf7781cdd2e550b246bde76e46c326e7ea3c73773"
checksum = "cd3cc851a13d30a34cb747ba2a0c5101a4b2e8b1677a29b213ee465365ea495e"
dependencies = [
"bitflags",
"errno",
@@ -3314,9 +3304,9 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.5"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7"
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
[[package]]
name = "semver-parser"
@@ -3682,7 +3672,7 @@ dependencies = [
"async-trait",
"cfg-if 1.0.0",
"futures-util",
"getrandom 0.2.4",
"getrandom 0.2.6",
"http-client",
"http-types",
"log",
@@ -3744,9 +3734,9 @@ dependencies = [
[[package]]
name = "termcolor"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
@@ -3867,9 +3857,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.16.1"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a"
checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
dependencies = [
"pin-project-lite",
]
@@ -3885,9 +3875,9 @@ dependencies = [
[[package]]
name = "trust-dns-proto"
version = "0.21.1"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2861b3ed517888174d13909e675c4e94b3291867512068be59d76533e4d1270c"
checksum = "9c31f240f59877c3d4bb3b3ea0ec5a6a0cff07323580ff8c7a605cd7d08b255d"
dependencies = [
"async-trait",
"cfg-if 1.0.0",
@@ -3900,7 +3890,7 @@ dependencies = [
"ipnet",
"lazy_static",
"log",
"rand 0.8.4",
"rand 0.8.5",
"smallvec",
"thiserror",
"tinyvec",
@@ -3909,9 +3899,9 @@ dependencies = [
[[package]]
name = "trust-dns-resolver"
version = "0.21.1"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9e737a252a617bd4774649e245dbf705e207275db0893b9fa824d49f074fc1c"
checksum = "e4ba72c2ea84515690c9fcef4c6c660bb9df3036ed1051686de84605b74fd558"
dependencies = [
"cfg-if 1.0.0",
"futures-util",
@@ -4038,7 +4028,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
"getrandom 0.2.4",
"getrandom 0.2.6",
"serde",
]
@@ -4242,15 +4232,15 @@ dependencies = [
[[package]]
name = "windows-sys"
version = "0.32.0"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825"
dependencies = [
"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",
"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",
]
[[package]]
@@ -4261,9 +4251,9 @@ checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
[[package]]
name = "windows_aarch64_msvc"
version = "0.32.0"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d"
[[package]]
name = "windows_i686_gnu"
@@ -4273,9 +4263,9 @@ checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
[[package]]
name = "windows_i686_gnu"
version = "0.32.0"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed"
[[package]]
name = "windows_i686_msvc"
@@ -4285,9 +4275,9 @@ checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
[[package]]
name = "windows_i686_msvc"
version = "0.32.0"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956"
[[package]]
name = "windows_x86_64_gnu"
@@ -4297,9 +4287,9 @@ checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.32.0"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4"
[[package]]
name = "windows_x86_64_msvc"
@@ -4309,9 +4299,9 @@ checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.32.0"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
[[package]]
name = "winreg"
@@ -4359,9 +4349,9 @@ dependencies = [
[[package]]
name = "zeroize_derive"
version = "1.3.1"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81e8f13fef10b63c06356d65d416b070798ddabcadc10d3ece0c5be9b3c7eddb"
checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17"
dependencies = [
"proc-macro2",
"quote",
@@ -4371,11 +4361,12 @@ dependencies = [
[[package]]
name = "zip"
version = "0.6.0"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6fa4aa90e99fb8d701bda16fb040d8ed2f9c7176fb44de750e880a74b580315"
checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d"
dependencies = [
"byteorder",
"crc32fast",
"crossbeam-utils",
"flate2",
]

View File

@@ -75,7 +75,7 @@ humansize = "1"
qrcodegen = "1.7.0"
tagger = "4.3.3"
textwrap = "0.15.0"
zip = { version = "0.6.0", default-features = false, features = ["deflate"] }
zip = { version = "0.6.2", default-features = false, features = ["deflate"] }
[dev-dependencies]
ansi_term = "0.12.0"
@@ -124,6 +124,10 @@ harness = false
name = "get_chat_msgs"
harness = false
[[bench]]
name = "get_chatlist"
harness = false
[features]
default = ["vendored"]
internals = []

View File

@@ -27,10 +27,12 @@ fn criterion_benchmark(c: &mut Criterion) {
(0..len).map(|i| chatlist.get_chat_id(i).unwrap()).collect()
});
c.bench_function("Load all chats", |b| {
c.bench_function("chat::get_chat_msgs (load messages from 10 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");
}
}

27
benches/get_chatlist.rs Normal file
View File

@@ -0,0 +1,27 @@
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);

View File

@@ -20,6 +20,8 @@ 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");
}
}

View File

@@ -1949,18 +1949,19 @@ pub unsafe extern "C" fn dc_block_contact(
contact_id: u32,
block: libc::c_int,
) {
if context.is_null() || contact_id <= constants::DC_CONTACT_ID_LAST_SPECIAL.to_u32() {
let contact_id = ContactId::new(contact_id);
if context.is_null() || contact_id.is_special() {
eprintln!("ignoring careless call to dc_block_contact()");
return;
}
let ctx = &*context;
block_on(async move {
if block == 0 {
Contact::unblock(ctx, ContactId::new(contact_id))
Contact::unblock(ctx, contact_id)
.await
.ok_or_log_msg(ctx, "Can't unblock contact");
} else {
Contact::block(ctx, ContactId::new(contact_id))
Contact::block(ctx, contact_id)
.await
.ok_or_log_msg(ctx, "Can't block contact");
}
@@ -1994,14 +1995,15 @@ pub unsafe extern "C" fn dc_delete_contact(
context: *mut dc_context_t,
contact_id: u32,
) -> libc::c_int {
if context.is_null() || contact_id <= constants::DC_CONTACT_ID_LAST_SPECIAL.to_u32() {
let contact_id = ContactId::new(contact_id);
if context.is_null() || contact_id.is_special() {
eprintln!("ignoring careless call to dc_delete_contact()");
return 0;
}
let ctx = &*context;
block_on(async move {
match Contact::delete(ctx, ContactId::new(contact_id)).await {
match Contact::delete(ctx, contact_id).await {
Ok(_) => 1,
Err(_) => 0,
}

View File

@@ -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() == DC_CONTACT_ID_SELF {
if msg.get_from_id() == ContactId::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 != DC_CONTACT_ID_SELF {
if peerstate.is_some() && *contact_id != ContactId::SELF {
line2 = format!(
", prefer-encrypt={}",
peerstate.as_ref().unwrap().prefer_encrypt
@@ -932,13 +932,24 @@ 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, arg1).await?;
let msglist = context.search_msgs(chat, &query).await?;
let time_needed = time_start.elapsed().unwrap_or_default();
log_msglist(&context, &msglist).await?;
println!("{} messages.", msglist.len());
println!(
"{}{} messages for {}search of \"{}\"",
msglist.len(),
if msglist.len() == 1000 { "+" } else { "" },
if chat.is_none() {
"global "
} else {
"in-chat-"
},
query,
);
println!("{:?} to create this list", time_needed);
}
"draft" => {

View File

@@ -16,8 +16,7 @@ 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_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,
DC_CHAT_ID_TRASH, 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;
@@ -199,7 +198,7 @@ impl ChatId {
}
None => {
if Contact::real_exists_by_id(context, contact_id).await?
|| contact_id == DC_CONTACT_ID_SELF
|| contact_id == ContactId::SELF
{
let chat_id =
ChatIdBlocked::get_for_contact(context, contact_id, create_blocked)
@@ -297,7 +296,7 @@ impl ChatId {
}
Chattype::Single => {
for contact_id in get_chat_contacts(context, self).await? {
if contact_id != DC_CONTACT_ID_SELF {
if contact_id != ContactId::SELF {
info!(
context,
"Blocking the contact {} to block 1:1 chat", contact_id
@@ -340,7 +339,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 != DC_CONTACT_ID_SELF {
if contact_id != ContactId::SELF {
Contact::scaleup_origin_by_id(context, contact_id, Origin::CreateChat)
.await?;
}
@@ -464,7 +463,7 @@ impl ChatId {
return Err(e);
}
self.add_protection_msg(context, protect, chat.is_promoted(), DC_CONTACT_ID_SELF)
self.add_protection_msg(context, protect, chat.is_promoted(), ContactId::SELF)
.await
}
@@ -501,14 +500,15 @@ impl ChatId {
Ok(())
}
// 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<()> {
// 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<()> {
context
.sql
.execute(
"UPDATE chats SET archived=0 WHERE id=? and archived=1",
paramsv![self],
"UPDATE chats SET archived=0 WHERE id=? AND archived=1 AND NOT(muted_until=-1 OR muted_until>?)",
paramsv![self, time()],
)
.await?;
Ok(())
@@ -714,7 +714,7 @@ impl ChatId {
VALUES (?,?,?, ?,?,?,?,?,?);",
paramsv![
self,
DC_CONTACT_ID_SELF,
ContactId::SELF,
time(),
msg.viewtype,
MessageState::OutDraft,
@@ -861,7 +861,7 @@ impl ChatId {
for contact_id in get_chat_contacts(context, self)
.await?
.iter()
.filter(|&contact_id| *contact_id > DC_CONTACT_ID_LAST_SPECIAL)
.filter(|&contact_id| !contact_id.is_special())
{
let contact = Contact::load_from_db(context, *contact_id).await?;
let addr = contact.get_addr();
@@ -1087,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, DC_CONTACT_ID_SELF).await,
Chattype::Group => is_contact_in_chat(context, self.id, ContactId::SELF).await,
Chattype::Undefined => Ok(false),
}
}
@@ -1241,7 +1241,7 @@ impl Chat {
if !self.can_send(context).await? {
if self.typ == Chattype::Group
&& !is_contact_in_chat(context, self.id, DC_CONTACT_ID_SELF).await?
&& !is_contact_in_chat(context, self.id, ContactId::SELF).await?
{
context.emit_event(EventType::ErrorSelfNotInGroup(
"Cannot send message; self not in group.".into(),
@@ -1355,7 +1355,7 @@ impl Chat {
VALUES (?,?,?, ?,?,1);",
paramsv![
timestamp,
DC_CONTACT_ID_SELF,
ContactId::SELF,
self.id,
msg.param.get_float(Param::SetLatitude).unwrap_or_default(),
msg.param.get_float(Param::SetLongitude).unwrap_or_default(),
@@ -1407,7 +1407,7 @@ impl Chat {
paramsv![
new_rfc724_mid,
self.id,
DC_CONTACT_ID_SELF,
ContactId::SELF,
to_id as i32,
timestamp,
msg.viewtype,
@@ -1427,7 +1427,6 @@ impl Chat {
],
)
.await?;
schedule_ephemeral_task(context).await;
msg.id = update_msg_id;
} else {
let raw_id = context
@@ -1456,7 +1455,7 @@ impl Chat {
paramsv![
new_rfc724_mid,
self.id,
DC_CONTACT_ID_SELF,
ContactId::SELF,
to_id as i32,
timestamp,
msg.viewtype,
@@ -1585,7 +1584,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, DC_CONTACT_ID_SELF).await? {
if let Some(chat_id) = ChatId::lookup_by_contact(context, ContactId::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();
@@ -1599,7 +1598,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, DC_CONTACT_ID_DEVICE).await? {
if let Some(chat_id) = ChatId::lookup_by_contact(context, ContactId::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();
@@ -1608,7 +1607,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, DC_CONTACT_ID_DEVICE).await?;
let mut contact = Contact::load_from_db(context, ContactId::DEVICE).await?;
contact.param.set(Param::ProfileImage, icon);
contact.update_param(context).await?;
}
@@ -1651,13 +1650,13 @@ async fn update_special_chat_name(
pub(crate) async fn update_special_chat_names(context: &Context) -> Result<()> {
update_special_chat_name(
context,
DC_CONTACT_ID_DEVICE,
ContactId::DEVICE,
stock_str::device_messages(context).await,
)
.await?;
update_special_chat_name(
context,
DC_CONTACT_ID_SELF,
ContactId::SELF,
stock_str::saved_messages(context).await,
)
.await?;
@@ -1687,7 +1686,7 @@ impl ChatIdBlocked {
) -> Result<Option<Self>> {
ensure!(context.sql.is_open().await, "Database not available");
ensure!(
contact_id > ContactId::new(0),
contact_id != ContactId::UNDEFINED,
"Invalid contact id requested"
);
@@ -1723,7 +1722,7 @@ impl ChatIdBlocked {
) -> Result<Self> {
ensure!(context.sql.is_open().await, "Database not available");
ensure!(
contact_id > ContactId::new(0),
contact_id != ContactId::UNDEFINED,
"Invalid contact id requested"
);
@@ -1736,10 +1735,10 @@ impl ChatIdBlocked {
let chat_name = contact.get_display_name().to_string();
let mut params = Params::new();
match contact_id {
DC_CONTACT_ID_SELF => {
ContactId::SELF => {
params.set_int(Param::Selftalk, 1);
}
DC_CONTACT_ID_DEVICE => {
ContactId::DEVICE => {
params.set_int(Param::Devicetalk, 1);
}
_ => (),
@@ -1780,8 +1779,8 @@ impl ChatIdBlocked {
.await?;
match contact_id {
DC_CONTACT_ID_SELF => update_saved_messages_icon(context).await?,
DC_CONTACT_ID_DEVICE => update_device_icon(context).await?,
ContactId::SELF => update_saved_messages_icon(context).await?,
ContactId::DEVICE => update_device_icon(context).await?,
_ => (),
}
@@ -1895,7 +1894,7 @@ async fn prepare_msg_common(
msg.state = change_state_to;
prepare_msg_blob(context, msg).await?;
chat_id.unarchive(context).await?;
chat_id.unarchive_if_not_muted(context).await?;
msg.id = chat
.prepare_msg_raw(
context,
@@ -1917,8 +1916,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.
// 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)
// ContactId::SELF may be used to check, if the user itself is in a group
// chat (ContactId::SELF is not added to normal chats)
let exists = context
.sql
@@ -1986,7 +1985,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(DC_CONTACT_ID_SELF)));
context.emit_event(EventType::LocationChanged(Some(ContactId::SELF)));
}
context.interrupt_smtp(InterruptInfo::new(false)).await;
@@ -2205,23 +2204,6 @@ 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()
@@ -2230,8 +2212,8 @@ pub async fn get_chat_msgs(
row.get::<_, ContactId>("from_id")?,
row.get::<_, ContactId>("to_id")?,
);
let is_info_msg: bool = from_id == DC_CONTACT_ID_INFO
|| to_id == DC_CONTACT_ID_INFO
let is_info_msg: bool = from_id == ContactId::INFO
|| to_id == ContactId::INFO
|| match Params::from_str(&params) {
Ok(p) => {
let cmd = p.get_cmd();
@@ -2241,30 +2223,40 @@ pub async fn get_chat_msgs(
};
Ok((
row.get::<_, MsgId>("id")?,
row.get::<_, i64>("timestamp")?,
row.get::<_, MsgId>("id")?,
!is_info_msg,
))
}
} else {
|row: &rusqlite::Row| {
Ok((
row.get::<_, MsgId>("id")?,
row.get::<_, i64>("timestamp")?,
row.get::<_, MsgId>("id")?,
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();
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);
}
let marker1 = marker1before.unwrap_or_else(MsgId::new_unset);
for (ts, curr_id) in sorted_rows {
if curr_id == marker1 {
ret.push(ChatItem::Marker1);
}
if (flags & DC_GCM_ADDDAYMARKER) != 0 {
let curr_local_timestamp = ts + cnv_to_local;
@@ -2276,9 +2268,7 @@ pub async fn get_chat_msgs(
last_day = curr_day;
}
}
if !exclude_message {
ret.push(ChatItem::Message { msg_id: curr_id });
}
ret.push(ChatItem::Message { msg_id: curr_id });
}
Ok(ret)
};
@@ -2296,9 +2286,8 @@ pub async fn get_chat_msgs(
m.param GLOB \"*S=*\"
OR m.from_id == ?
OR m.to_id == ?
)
ORDER BY m.timestamp, m.id;",
paramsv![chat_id, DC_CONTACT_ID_INFO, DC_CONTACT_ID_INFO],
);",
paramsv![chat_id, ContactId::INFO, ContactId::INFO],
process_row,
process_rows,
)
@@ -2307,11 +2296,10 @@ pub async fn get_chat_msgs(
context
.sql
.query_map(
"SELECT (m.id+1) AS id, (m.timestamp+1) AS timestamp
"SELECT m.id AS id, m.timestamp AS timestamp
FROM msgs m
WHERE m.chat_id=?
AND m.hidden=0
ORDER BY timestamp, id;",
AND m.hidden=0;",
paramsv![chat_id],
process_row,
process_rows,
@@ -2581,8 +2569,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, DC_CONTACT_ID_SELF).await? {
add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await?;
if !is_contact_in_chat(context, chat_id, ContactId::SELF).await? {
add_to_chat_contacts_table(context, chat_id, ContactId::SELF).await?;
}
context.emit_event(EventType::MsgsChanged {
@@ -2711,13 +2699,13 @@ pub(crate) async fn add_contact_to_chat_ex(
chat_id
);
ensure!(
Contact::real_exists_by_id(context, contact_id).await? || contact_id == DC_CONTACT_ID_SELF,
Contact::real_exists_by_id(context, contact_id).await? || contact_id == ContactId::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 != DC_CONTACT_ID_SELF,
chat.typ != Chattype::Broadcast || contact_id != ContactId::SELF,
"Cannot add SELF to broadcast."
);
@@ -2739,7 +2727,7 @@ pub(crate) async fn add_contact_to_chat_ex(
.await?
.unwrap_or_default();
if addr_cmp(contact.get_addr(), &self_addr) {
// ourself is added using DC_CONTACT_ID_SELF, do not add this address explicitly.
// ourself is added using ContactId::SELF, do not add this address explicitly.
// if SELF is not in the group, members cannot be added at all.
warn!(
context,
@@ -2772,7 +2760,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(), DC_CONTACT_ID_SELF).await);
Some(stock_str::msg_add_member(context, contact.get_addr(), ContactId::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());
@@ -2798,7 +2786,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, DC_CONTACT_ID_SELF],
paramsv![chat_id, ContactId::SELF],
|row| Ok(row.get::<_, i64>(0)),
|rows| {
let mut needs_attach = false;
@@ -2883,7 +2871,7 @@ pub async fn remove_contact_from_chat(
chat_id
);
ensure!(
contact_id > DC_CONTACT_ID_LAST_SPECIAL || contact_id == DC_CONTACT_ID_SELF,
!contact_id.is_special() || contact_id == ContactId::SELF,
"Cannot remove special contact"
);
@@ -2902,16 +2890,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 == DC_CONTACT_ID_SELF {
if contact.id == ContactId::SELF {
set_group_explicitly_left(context, &chat.grpid).await?;
msg.text =
Some(stock_str::msg_group_left(context, DC_CONTACT_ID_SELF).await);
Some(stock_str::msg_group_left(context, ContactId::SELF).await);
} else {
msg.text = Some(
stock_str::msg_del_member(
context,
contact.get_addr(),
DC_CONTACT_ID_SELF,
ContactId::SELF,
)
.await,
);
@@ -3004,8 +2992,7 @@ 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, DC_CONTACT_ID_SELF)
.await,
stock_str::msg_grp_name(context, &chat.name, &new_name, ContactId::SELF).await,
);
msg.param.set_cmd(SystemMessage::GroupNameChanged);
if !chat.name.is_empty() {
@@ -3046,7 +3033,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, DC_CONTACT_ID_SELF).await? {
if !is_contact_in_chat(context, chat_id, ContactId::SELF).await? {
context.emit_event(EventType::ErrorSelfNotInGroup(
"Cannot set chat profile image; self not in group.".into(),
));
@@ -3058,7 +3045,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, DC_CONTACT_ID_SELF).await);
msg.text = Some(stock_str::msg_grp_img_deleted(context, ContactId::SELF).await);
} else {
let mut image_blob = match BlobObject::from_path(context, Path::new(new_image.as_ref())) {
Ok(blob) => Ok(blob),
@@ -3072,7 +3059,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, DC_CONTACT_ID_SELF).await);
msg.text = Some(stock_str::msg_grp_img_changed(context, ContactId::SELF).await);
}
chat.update_param(context).await?;
if chat.is_promoted() && !chat.is_mailing_list() {
@@ -3094,7 +3081,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(context).await?;
chat_id.unarchive_if_not_muted(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;
@@ -3247,12 +3234,12 @@ pub async fn add_device_msg_with_importance(
}
if let Some(msg) = msg {
chat_id = ChatId::get_for_contact(context, DC_CONTACT_ID_DEVICE).await?;
chat_id = ChatId::get_for_contact(context, ContactId::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(context).await?;
chat_id.unarchive_if_not_muted(context).await?;
let timestamp_sent = dc_create_smeared_timestamp(context).await;
@@ -3289,8 +3276,8 @@ pub async fn add_device_msg_with_importance(
VALUES (?,?,?,?,?,?,?,?,?,?,?);",
paramsv![
chat_id,
DC_CONTACT_ID_DEVICE,
DC_CONTACT_ID_SELF,
ContactId::DEVICE,
ContactId::SELF,
timestamp_sort,
timestamp_sent,
timestamp_sent, // timestamp_sent equals timestamp_rcvd
@@ -3350,7 +3337,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 `DC_CONTACT_ID_DEVICE` makes sure,
// - deletion in `msgs` with `ContactId::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
@@ -3361,7 +3348,7 @@ pub(crate) async fn delete_and_reset_all_device_msgs(context: &Context) -> Resul
.sql
.execute(
"DELETE FROM msgs WHERE from_id=?;",
paramsv![DC_CONTACT_ID_DEVICE],
paramsv![ContactId::DEVICE],
)
.await?;
context
@@ -3396,8 +3383,8 @@ pub(crate) async fn add_info_msg_with_cmd(
"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,
DC_CONTACT_ID_INFO,
DC_CONTACT_ID_INFO,
ContactId::INFO,
ContactId::INFO,
timestamp,
Viewtype::Text,
MessageState::InNoticed,
@@ -3652,7 +3639,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, DC_CONTACT_ID_SELF, false)
let added = add_contact_to_chat_ex(&t, chat_id, ContactId::SELF, false)
.await
.unwrap();
assert_eq!(added, false);
@@ -3862,7 +3849,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, DC_CONTACT_ID_SELF).await?;
remove_contact_from_chat(&bob, bob_chat_id, ContactId::SELF).await?;
let leave_msg = bob.pop_sent_msg().await;
alice.recv_msg(&leave_msg).await;
@@ -3891,7 +3878,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, DC_CONTACT_ID_SELF).await;
let removed = remove_contact_from_chat(&ctx, chat.id, ContactId::SELF).await;
assert!(removed.is_err());
assert_eq!(get_chat_contacts(&ctx, chat.id).await.unwrap().len(), 1);
}
@@ -3900,7 +3887,6 @@ 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);
@@ -3911,8 +3897,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, DC_CONTACT_ID_SELF);
assert_eq!(msg.to_id, DC_CONTACT_ID_SELF);
assert_eq!(msg.from_id, ContactId::SELF);
assert_eq!(msg.to_id, ContactId::SELF);
assert!(msg.get_showpadlock());
let sent_msg = t.pop_sent_msg().await;
@@ -3921,8 +3907,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, DC_CONTACT_ID_SELF);
assert_eq!(msg.to_id, DC_CONTACT_ID_SELF);
assert_eq!(msg.from_id, ContactId::SELF);
assert_eq!(msg.to_id, ContactId::SELF);
assert!(msg.get_showpadlock());
Ok(())
@@ -3949,8 +3935,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, DC_CONTACT_ID_DEVICE);
assert_eq!(msg1.to_id, DC_CONTACT_ID_SELF);
assert_eq!(msg1.from_id, ContactId::DEVICE);
assert_eq!(msg1.to_id, ContactId::SELF);
assert!(!msg1.is_info());
assert!(!msg1.is_setupmessage());
@@ -3984,8 +3970,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, DC_CONTACT_ID_DEVICE);
assert_eq!(msg1.to_id, DC_CONTACT_ID_SELF);
assert_eq!(msg1.from_id, ContactId::DEVICE);
assert_eq!(msg1.to_id, ContactId::SELF);
assert!(!msg1.is_info());
assert!(!msg1.is_setupmessage());
@@ -4077,7 +4063,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, DC_CONTACT_ID_DEVICE)
let device_chat_id = ChatId::get_for_contact(&t, ContactId::DEVICE)
.await
.unwrap();
@@ -4225,6 +4211,90 @@ 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
@@ -4317,7 +4387,7 @@ mod tests {
let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de")
.await
.unwrap();
assert_ne!(contact1, ContactId::new(0));
assert_ne!(contact1, ContactId::UNDEFINED);
let chat_id = ChatId::create_for_contact(&context.ctx, contact1)
.await
@@ -4522,7 +4592,7 @@ mod tests {
// create contact, then unblocked chat
let contact_id = Contact::create(&ctx, "", "bob@foo.de").await.unwrap();
assert_ne!(contact_id, ContactId::new(0));
assert_ne!(contact_id, ContactId::UNDEFINED);
let found = ChatId::lookup_by_contact(&ctx, contact_id).await.unwrap();
assert!(found.is_none());
@@ -4563,13 +4633,13 @@ mod tests {
async fn test_lookup_self_by_contact_id() {
let ctx = TestContext::new_alice().await;
let chat = ChatId::lookup_by_contact(&ctx, DC_CONTACT_ID_SELF)
let chat = ChatId::lookup_by_contact(&ctx, ContactId::SELF)
.await
.unwrap();
assert!(chat.is_none());
ctx.update_device_chats().await.unwrap();
let chat = ChatIdBlocked::lookup_by_contact(&ctx, DC_CONTACT_ID_SELF)
let chat = ChatIdBlocked::lookup_by_contact(&ctx, ContactId::SELF)
.await
.unwrap()
.unwrap();
@@ -5094,7 +5164,7 @@ mod tests {
.await?,
true
);
remove_contact_from_chat(&alice, chat_id, DC_CONTACT_ID_SELF).await?;
remove_contact_from_chat(&alice, chat_id, ContactId::SELF).await?;
assert_eq!(
Chat::load_from_db(&alice, chat_id)
.await?

View File

@@ -4,9 +4,8 @@ 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_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,
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,
};
use crate::contact::{Contact, ContactId};
use crate::context::Context;
@@ -92,12 +91,6 @@ 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| {
@@ -112,7 +105,7 @@ impl Chatlist {
};
let skip_id = if flag_for_forwarding {
ChatId::lookup_by_contact(context, DC_CONTACT_ID_DEVICE)
ChatId::lookup_by_contact(context, ContactId::DEVICE)
.await?
.unwrap_or_default()
} else {
@@ -216,7 +209,7 @@ impl Chatlist {
} else {
// show normal chatlist
let sort_id_up = if flag_for_forwarding {
ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF)
ChatId::lookup_by_contact(context, ContactId::SELF)
.await?
.unwrap_or_default()
} else {
@@ -326,7 +319,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 == DC_CONTACT_ID_SELF {
if lastmsg.from_id == ContactId::SELF {
(Some(lastmsg), None)
} else {
match chat.typ {
@@ -343,7 +336,7 @@ impl Chatlist {
if chat.id.is_archived_link() {
Ok(Default::default())
} else if let Some(lastmsg) = lastmsg.filter(|msg| msg.from_id != DC_CONTACT_ID_UNDEFINED) {
} else if let Some(lastmsg) = lastmsg.filter(|msg| msg.from_id != ContactId::UNDEFINED) {
Ok(Summary::new(context, &lastmsg, chat, lastcontact.as_ref()).await)
} else {
Ok(Summary {

View File

@@ -4,7 +4,6 @@ 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());
@@ -180,16 +179,6 @@ 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;

View File

@@ -14,10 +14,7 @@ use crate::aheader::EncryptPreference;
use crate::chat::ChatId;
use crate::color::str_to_color;
use crate::config::Config;
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::constants::{Blocked, Chattype, 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;
@@ -33,18 +30,44 @@ 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, PartialOrd, Ord, Serialize, Deserialize,
)]
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash, 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)
}
/// Bad evil escape hatch, do not use.
/// 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.
pub const fn to_u32(&self) -> u32 {
self.0
}
@@ -52,20 +75,19 @@ impl ContactId {
impl fmt::Display for ContactId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
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)
// }
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)
}
}
}
@@ -104,12 +126,6 @@ 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`,
@@ -278,7 +294,7 @@ impl Contact {
},
)
.await?;
if contact_id == DC_CONTACT_ID_SELF {
if contact_id == ContactId::SELF {
contact.name = stock_str::self_msg(context).await;
contact.addr = context
.get_config(Config::ConfiguredAddr)
@@ -288,9 +304,9 @@ impl Contact {
.get_config(Config::Selfstatus)
.await?
.unwrap_or_default();
} else if contact_id == DC_CONTACT_ID_DEVICE {
} else if contact_id == ContactId::DEVICE {
contact.name = stock_str::device_messages(context).await;
contact.addr = DC_CONTACT_ID_DEVICE_ADDR.to_string();
contact.addr = ContactId::DEVICE_ADDR.to_string();
contact.status = stock_str::device_messages_hint(context).await;
}
Ok(contact)
@@ -384,7 +400,7 @@ impl Contact {
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));
return Ok(Some(ContactId::SELF));
}
}
let id = context
@@ -393,11 +409,7 @@ impl Contact {
"SELECT id FROM contacts \
WHERE addr=?1 COLLATE NOCASE \
AND id>?2 AND origin>=?3 AND blocked=0;",
paramsv![
addr_normalized,
DC_CONTACT_ID_LAST_SPECIAL,
min_origin as u32,
],
paramsv![addr_normalized, ContactId::LAST_SPECIAL, min_origin as u32,],
)
.await?;
Ok(id)
@@ -446,7 +458,7 @@ impl Contact {
.unwrap_or_default();
if addr_cmp(&addr, &addr_self) {
return Ok((DC_CONTACT_ID_SELF, sth_modified));
return Ok((ContactId::SELF, sth_modified));
}
if !may_be_valid_addr(&addr) {
@@ -706,7 +718,7 @@ impl Contact {
ORDER BY LOWER(iif(c.name='',c.authname,c.name)||c.addr),c.id;",
paramsv![
self_addr,
DC_CONTACT_ID_LAST_SPECIAL,
ContactId::LAST_SPECIAL,
Origin::IncomingReplyTo,
s3str_like_cmd,
s3str_like_cmd,
@@ -750,11 +762,7 @@ impl Contact {
AND origin>=?3
AND blocked=0
ORDER BY LOWER(iif(name='',authname,name)||addr),id;",
paramsv![
self_addr,
DC_CONTACT_ID_LAST_SPECIAL,
Origin::IncomingReplyTo
],
paramsv![self_addr, ContactId::LAST_SPECIAL, Origin::IncomingReplyTo],
|row| row.get::<_, ContactId>(0),
|ids| {
for id in ids {
@@ -767,7 +775,7 @@ impl Contact {
}
if flag_add_self && add_self {
ret.push(DC_CONTACT_ID_SELF);
ret.push(ContactId::SELF);
}
Ok(ret)
@@ -822,7 +830,7 @@ impl Contact {
.sql
.count(
"SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0",
paramsv![DC_CONTACT_ID_LAST_SPECIAL],
paramsv![ContactId::LAST_SPECIAL],
)
.await?;
Ok(count as usize)
@@ -838,7 +846,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![DC_CONTACT_ID_LAST_SPECIAL],
paramsv![ContactId::LAST_SPECIAL],
|row| row.get::<_, ContactId>(0),
|ids| {
ids.collect::<std::result::Result<Vec<_>, _>>()
@@ -856,7 +864,7 @@ impl Contact {
/// fingerprints of the keys involved.
pub async fn get_encrinfo(context: &Context, contact_id: ContactId) -> Result<String> {
ensure!(
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
!contact_id.is_special(),
"Can not provide encryption info for special contact"
);
@@ -924,10 +932,7 @@ impl Contact {
///
/// May result in a `#DC_EVENT_CONTACTS_CHANGED` event.
pub async fn delete(context: &Context, contact_id: ContactId) -> Result<()> {
ensure!(
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
"Can not delete special contact"
);
ensure!(!contact_id.is_special(), "Can not delete special contact");
let count_chats = context
.sql
@@ -963,7 +968,7 @@ impl Contact {
/// Get a single contact object. For a list, see eg. dc_get_contacts().
///
/// For contact DC_CONTACT_ID_SELF (1), the function returns sth.
/// For contact ContactId::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> {
@@ -1056,7 +1061,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 == DC_CONTACT_ID_SELF {
if self.id == ContactId::SELF {
if let Some(p) = context.get_config(Config::Selfavatar).await? {
return Ok(Some(PathBuf::from(p)));
}
@@ -1102,7 +1107,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 == DC_CONTACT_ID_SELF {
if self.id == ContactId::SELF {
return Ok(VerifiedStatus::BidirectVerified);
}
@@ -1150,14 +1155,14 @@ impl Contact {
.sql
.count(
"SELECT COUNT(*) FROM contacts WHERE id>?;",
paramsv![DC_CONTACT_ID_LAST_SPECIAL],
paramsv![ContactId::LAST_SPECIAL],
)
.await?;
Ok(count)
}
pub async fn real_exists_by_id(context: &Context, contact_id: ContactId) -> Result<bool> {
if contact_id <= DC_CONTACT_ID_LAST_SPECIAL {
if contact_id.is_special() {
return Ok(false);
}
@@ -1230,7 +1235,7 @@ async fn set_block_contact(
new_blocking: bool,
) -> Result<()> {
ensure!(
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
!contact_id.is_special(),
"Can't block special contact {}",
contact_id
);
@@ -1300,7 +1305,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 == DC_CONTACT_ID_SELF {
if contact_id == ContactId::SELF {
if was_encrypted {
context
.set_config(Config::Selfavatar, Some(profile_image))
@@ -1314,7 +1319,7 @@ pub(crate) async fn set_profile_image(
true
}
AvatarAction::Delete => {
if contact_id == DC_CONTACT_ID_SELF {
if contact_id == ContactId::SELF {
if was_encrypted {
context.set_config(Config::Selfavatar, None).await?;
} else {
@@ -1345,7 +1350,7 @@ pub(crate) async fn set_status(
encrypted: bool,
has_chat_version: bool,
) -> Result<()> {
if contact_id == DC_CONTACT_ID_SELF {
if contact_id == ContactId::SELF {
if encrypted && has_chat_version {
context
.set_config(Config::Selfstatus, Some(&status))
@@ -1370,7 +1375,7 @@ pub(crate) async fn update_last_seen(
timestamp: i64,
) -> Result<()> {
ensure!(
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
!contact_id.is_special(),
"Can not update special contact last seen timestamp"
);
@@ -1474,6 +1479,17 @@ 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);
@@ -1537,7 +1553,7 @@ mod tests {
Origin::IncomingReplyTo,
)
.await?;
assert_ne!(id, ContactId::new(0));
assert_ne!(id, ContactId::UNDEFINED);
let contact = Contact::load_from_db(&context.ctx, id).await.unwrap();
assert_eq!(contact.get_name(), "");
@@ -1615,7 +1631,7 @@ mod tests {
Contact::add_or_lookup(&t, "bla foo", "one@eins.org", Origin::IncomingUnknownTo)
.await
.unwrap();
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
assert!(!contact_id.is_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);
@@ -1642,7 +1658,7 @@ mod tests {
Contact::add_or_lookup(&t, "", "three@drei.sam", Origin::IncomingUnknownTo)
.await
.unwrap();
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
assert!(!contact_id.is_special());
assert_eq!(sth_modified, Modifier::None);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
assert_eq!(contact.get_name(), "");
@@ -1682,7 +1698,7 @@ mod tests {
Contact::add_or_lookup(&t, "", "alice@w.de", Origin::IncomingUnknownTo)
.await
.unwrap();
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
assert!(!contact_id.is_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");
@@ -1691,8 +1707,7 @@ mod tests {
assert_eq!(contact.get_name_n_addr(), "Wonderland, Alice (alice@w.de)");
// check SELF
let contact = Contact::load_from_db(&t, DC_CONTACT_ID_SELF).await.unwrap();
assert_eq!(DC_CONTACT_ID_SELF, ContactId::new(1));
let contact = Contact::load_from_db(&t, ContactId::SELF).await.unwrap();
assert_eq!(contact.get_name(), stock_str::self_msg(&t).await);
assert_eq!(contact.get_addr(), ""); // we're not configured
assert!(!contact.is_blocked());
@@ -1702,7 +1717,7 @@ mod tests {
async fn test_delete() -> Result<()> {
let alice = TestContext::new_alice().await;
assert!(Contact::delete(&alice, DC_CONTACT_ID_SELF).await.is_err());
assert!(Contact::delete(&alice, ContactId::SELF).await.is_err());
// Create Bob contact
let (contact_id, _) =
@@ -1735,7 +1750,7 @@ mod tests {
Contact::add_or_lookup(&t, "bob1", "bob@example.org", Origin::IncomingUnknownFrom)
.await
.unwrap();
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
assert!(!contact_id.is_special());
assert_eq!(sth_modified, Modifier::Created);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
assert_eq!(contact.get_authname(), "bob1");
@@ -1747,7 +1762,7 @@ mod tests {
Contact::add_or_lookup(&t, "bob2", "bob@example.org", Origin::IncomingUnknownFrom)
.await
.unwrap();
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
assert!(!contact_id.is_special());
assert_eq!(sth_modified, Modifier::Modified);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
assert_eq!(contact.get_authname(), "bob2");
@@ -1758,7 +1773,7 @@ mod tests {
let contact_id = Contact::create(&t, "bob3", "bob@example.org")
.await
.unwrap();
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
assert!(!contact_id.is_special());
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
assert_eq!(contact.get_authname(), "bob2");
assert_eq!(contact.get_name(), "bob3");
@@ -1769,7 +1784,7 @@ mod tests {
Contact::add_or_lookup(&t, "bob4", "bob@example.org", Origin::IncomingUnknownFrom)
.await
.unwrap();
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
assert!(!contact_id.is_special());
assert_eq!(sth_modified, Modifier::Modified);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
assert_eq!(contact.get_authname(), "bob4");
@@ -1783,7 +1798,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 > DC_CONTACT_ID_LAST_SPECIAL);
assert!(!contact_id.is_special());
let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
assert_eq!(contact.get_authname(), "");
assert_eq!(contact.get_name(), "");
@@ -1957,7 +1972,7 @@ mod tests {
let id = Contact::lookup_id_by_addr(&alice.ctx, "alice@example.org", Origin::Unknown)
.await
.unwrap();
assert_eq!(id, Some(DC_CONTACT_ID_SELF));
assert_eq!(id, Some(ContactId::SELF));
}
#[async_std::test]
@@ -1965,9 +1980,9 @@ mod tests {
let alice = TestContext::new_alice().await;
// Return error for special IDs
let encrinfo = Contact::get_encrinfo(&alice, DC_CONTACT_ID_SELF).await;
let encrinfo = Contact::get_encrinfo(&alice, ContactId::SELF).await;
assert!(encrinfo.is_err());
let encrinfo = Contact::get_encrinfo(&alice, DC_CONTACT_ID_DEVICE).await;
let encrinfo = Contact::get_encrinfo(&alice, ContactId::DEVICE).await;
assert!(encrinfo.is_err());
let (contact_bob_id, _modified) =

View File

@@ -56,7 +56,7 @@ pub struct InnerContext {
pub(crate) events: Events,
pub(crate) scheduler: RwLock<Scheduler>,
pub(crate) ephemeral_task: RwLock<Option<task::JoinHandle<()>>>,
pub(crate) ephemeral_task: RwLock<Option<(task::JoinHandle<()>, Sender<()>)>>,
/// Recently loaded quota information, if any.
/// Set to `None` if quota was never tried to load.
@@ -670,7 +670,7 @@ mod tests {
use crate::chat::{
get_chat_contacts, get_chat_msgs, send_msg, set_muted, Chat, ChatId, MuteDuration,
};
use crate::constants::DC_CONTACT_ID_SELF;
use crate::contact::ContactId;
use crate::dc_receive_imf::dc_receive_imf;
use crate::dc_tools::dc_create_outgoing_rfc724_mid;
use crate::message::{Message, Viewtype};
@@ -956,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, DC_CONTACT_ID_SELF).await?;
let self_talk = ChatId::create_for_contact(&alice, ContactId::SELF).await?;
let chat = alice
.create_chat_with_contact("Bob", "bob@example.org")
.await;

View File

@@ -1,7 +1,7 @@
//! Internet Message Format reception pipeline.
use std::cmp::min;
use std::collections::BTreeSet;
use std::collections::HashSet;
use std::convert::TryFrom;
use anyhow::{bail, ensure, Context as _, Result};
@@ -13,9 +13,7 @@ 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, DC_CONTACT_ID_LAST_SPECIAL, DC_CONTACT_ID_SELF,
};
use crate::constants::{Blocked, Chattype, ShowEmails, DC_CHAT_ID_TRASH};
use crate::contact;
use crate::contact::{
addr_cmp, may_be_valid_addr, normalize_name, Contact, ContactId, Origin, VerifiedStatus,
@@ -180,7 +178,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 != DC_CONTACT_ID_SELF;
let incoming = from_id != ContactId::SELF;
let to_ids = dc_add_or_lookup_contacts_by_address_list(
context,
@@ -222,7 +220,7 @@ pub(crate) async fn dc_receive_imf_inner(
.await
.context("add_parts error")?;
if from_id > DC_CONTACT_ID_LAST_SPECIAL {
if !from_id.is_special() {
contact::update_last_seen(context, from_id, sent_timestamp).await?;
}
@@ -256,7 +254,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 == DC_CONTACT_ID_SELF {
if from_id == ContactId::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);
@@ -279,7 +277,7 @@ pub(crate) async fn dc_receive_imf_inner(
}
if let Some(avatar_action) = &mime_parser.user_avatar {
if from_id != ContactId::new(0)
if from_id != ContactId::UNDEFINED
&& context
.update_contacts_timestamp(from_id, Param::AvatarTimestamp, sent_timestamp)
.await?
@@ -307,7 +305,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::new(0)
&& from_id != ContactId::UNDEFINED
&& context
.update_contacts_timestamp(from_id, Param::StatusTimestamp, sent_timestamp)
.await?
@@ -395,8 +393,8 @@ pub async fn from_field_to_contact_id(
)
.await?;
if from_ids.contains(&DC_CONTACT_ID_SELF) {
Ok((DC_CONTACT_ID_SELF, false, Origin::OutgoingBcc))
if from_ids.contains(&ContactId::SELF) {
Ok((ContactId::SELF, false, Origin::OutgoingBcc))
} else if !from_ids.is_empty() {
if from_ids.len() > 1 {
warn!(
@@ -419,7 +417,7 @@ pub async fn from_field_to_contact_id(
"mail has an empty From header: {:?}", from_address_list
);
Ok((ContactId::new(0), false, Origin::Unknown))
Ok((ContactId::UNDEFINED, false, Origin::Unknown))
}
}
@@ -494,7 +492,7 @@ async fn add_parts(
let state: MessageState;
let mut needs_delete_job = false;
if incoming {
to_id = DC_CONTACT_ID_SELF;
to_id = ContactId::SELF;
// Whether the message is a part of securejoin handshake that should be marked as seen
// automatically.
@@ -526,7 +524,7 @@ async fn add_parts(
securejoin_seen = false;
}
let test_normal_chat = if from_id == ContactId::new(0) {
let test_normal_chat = if from_id == ContactId::UNDEFINED {
Default::default()
} else {
ChatIdBlocked::lookup_by_contact(context, from_id).await?
@@ -541,7 +539,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).await?
lookup_chat_by_reply(context, mime_parser, &parent, to_ids, from_id).await?
{
chat_id = Some(new_chat_id);
chat_id_blocked = new_chat_id_blocked;
@@ -668,7 +666,7 @@ async fn add_parts(
if chat_id.is_none() {
// try to create a normal chat
let create_blocked = if from_id == DC_CONTACT_ID_SELF {
let create_blocked = if from_id == ContactId::SELF {
Blocked::Not
} else {
Blocked::Request
@@ -720,9 +718,8 @@ async fn add_parts(
state = MessageState::OutDelivered;
to_id = to_ids.get(0).cloned().unwrap_or_default();
let self_sent = from_id == DC_CONTACT_ID_SELF
&& to_ids.len() == 1
&& to_ids.contains(&DC_CONTACT_ID_SELF);
let self_sent =
from_id == ContactId::SELF && to_ids.len() == 1 && to_ids.contains(&ContactId::SELF);
// handshake may mark contacts as verified and must be processed before chats are created
if mime_parser.get_header(HeaderDef::SecureJoin).is_some() {
@@ -774,7 +771,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).await?
lookup_chat_by_reply(context, mime_parser, &parent, to_ids, from_id).await?
{
chat_id = Some(new_chat_id);
chat_id_blocked = new_chat_id_blocked;
@@ -836,12 +833,11 @@ async fn add_parts(
}
if chat_id.is_none() && self_sent {
// from_id==to_id==DC_CONTACT_ID_SELF - this is a self-sent messages,
// from_id==to_id==ContactId::SELF - this is a self-sent messages,
// maybe an Autocrypt Setup Message
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")
if let Ok(chat) = ChatIdBlocked::get_for_contact(context, ContactId::SELF, Blocked::Not)
.await
.log_err(context, "Failed to get (new) chat for contact")
{
chat_id = Some(chat.id);
chat_id_blocked = chat.blocked;
@@ -1148,8 +1144,8 @@ INSERT INTO msgs
stmt.execute(paramsv![
rfc724_mid,
chat_id,
if trash { ContactId::new(0) } else { from_id },
if trash { ContactId::new(0) } else { to_id },
if trash { ContactId::UNDEFINED } else { from_id },
if trash { ContactId::UNDEFINED } else { to_id },
sort_timestamp,
sent_timestamp,
rcvd_timestamp,
@@ -1191,7 +1187,7 @@ INSERT INTO msgs
}
drop(conn);
chat_id.unarchive(context).await?;
chat_id.unarchive_if_not_muted(context).await?;
info!(
context,
@@ -1322,6 +1318,7 @@ 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.
@@ -1343,7 +1340,7 @@ async fn lookup_chat_by_reply(
return Ok(None);
}
if is_probably_private_reply(context, to_ids, mime_parser, parent_chat.id).await? {
if is_probably_private_reply(context, to_ids, from_id, mime_parser, parent_chat.id).await? {
return Ok(None);
}
@@ -1362,6 +1359,7 @@ 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> {
@@ -1372,14 +1370,15 @@ 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 == [DC_CONTACT_ID_SELF];
let private_message =
(to_ids == [ContactId::SELF]) || (from_id == ContactId::SELF && to_ids.len() == 1);
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(&DC_CONTACT_ID_SELF) {
if chat_contacts.len() == 2 && chat_contacts.contains(&ContactId::SELF) {
return Ok(false);
}
}
@@ -1407,8 +1406,8 @@ async fn create_or_lookup_group(
if !member_ids.contains(&(from_id)) {
member_ids.push(from_id);
}
if !member_ids.contains(&(DC_CONTACT_ID_SELF)) {
member_ids.push(DC_CONTACT_ID_SELF);
if !member_ids.contains(&(ContactId::SELF)) {
member_ids.push(ContactId::SELF);
}
let res = create_adhoc_group(context, mime_parser, create_blocked, &member_ids)
@@ -1435,7 +1434,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, mime_parser, chat_id).await?
&& is_probably_private_reply(context, to_ids, from_id, mime_parser, chat_id).await?
{
return Ok(None);
}
@@ -1489,9 +1488,8 @@ 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, 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, 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, from_id).await?;
}
@@ -1644,7 +1642,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, DC_CONTACT_ID_SELF).await?
if chat::is_contact_in_chat(context, chat_id, ContactId::SELF).await?
&& !chat::is_contact_in_chat(context, chat_id, from_id).await?
{
warn!(
@@ -1658,7 +1656,7 @@ async fn apply_group_changes(
.await?
{
if removed_id.is_some()
|| !chat::is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await?
|| !chat::is_contact_in_chat(context, chat_id, ContactId::SELF).await?
{
// Members could have been removed while we were
// absent. We can't use existing member list and need to
@@ -1671,11 +1669,11 @@ async fn apply_group_changes(
)
.await?;
if removed_id != Some(DC_CONTACT_ID_SELF) {
chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await?;
if removed_id != Some(ContactId::SELF) {
chat::add_to_chat_contacts_table(context, chat_id, ContactId::SELF).await?;
}
}
if from_id > DC_CONTACT_ID_LAST_SPECIAL
if !from_id.is_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)
@@ -1696,7 +1694,7 @@ async fn apply_group_changes(
}
if let Some(avatar_action) = &mime_parser.group_avatar {
if !chat::is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF).await? {
if !chat::is_contact_in_chat(context, chat_id, ContactId::SELF).await? {
warn!(
context,
"Received group avatar update for group chat {} we are not a member of.", chat_id
@@ -1846,7 +1844,7 @@ async fn create_or_lookup_mailinglist(
)
})?;
chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF).await?;
chat::add_to_chat_contacts_table(context, chat_id, ContactId::SELF).await?;
Ok(Some((chat_id, Blocked::Request)))
} else {
info!(context, "creating list forbidden by caller");
@@ -2012,7 +2010,7 @@ async fn create_adhoc_grp_id(context: &Context, member_ids: &[ContactId]) -> Res
);
let mut params = Vec::new();
params.extend_from_slice(member_ids);
params.push(DC_CONTACT_ID_SELF);
params.push(ContactId::SELF);
let members = context
.sql
@@ -2068,7 +2066,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 != DC_CONTACT_ID_SELF {
if from_id != ContactId::SELF {
let peerstate = Peerstate::from_addr(context, contact.get_addr()).await?;
if peerstate.is_none()
@@ -2093,17 +2091,12 @@ async fn check_verified_properties(
let to_ids = to_ids
.iter()
.copied()
.filter(|id| *id != DC_CONTACT_ID_SELF)
.filter(|id| *id != ContactId::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
@@ -2111,9 +2104,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({}) ",
to_ids_str
sql::repeat_vars(to_ids.len())?
),
paramsv![],
rusqlite::params_from_iter(to_ids),
|row| {
let to_addr: String = row.get(0)?;
let is_verified: i32 = row.get(1).unwrap_or(0);
@@ -2267,7 +2260,7 @@ async fn dc_add_or_lookup_contacts_by_address_list(
origin: Origin,
prevent_rename: bool,
) -> Result<Vec<ContactId>> {
let mut contact_ids = BTreeSet::new();
let mut contact_ids = HashSet::new();
for info in address_list.iter() {
let addr = &info.addr;
if !may_be_valid_addr(addr) {
@@ -2293,7 +2286,7 @@ async fn add_or_lookup_contact_by_addr(
origin: Origin,
) -> Result<ContactId> {
if context.is_self_addr(addr).await? {
return Ok(DC_CONTACT_ID_SELF);
return Ok(ContactId::SELF);
}
let display_name_normalized = display_name.map(normalize_name).unwrap_or_default();
@@ -2313,7 +2306,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_CONTACT_ID_INFO, DC_GCL_NO_SPECIALS};
use crate::constants::DC_GCL_NO_SPECIALS;
use crate::message::Message;
use crate::test_utils::{get_chat_msg, TestContext, TestContextManager};
@@ -2961,7 +2954,7 @@ mod tests {
last_msg.text,
Some(stock_str::failed_sending_to(&t, "assidhfaaspocwaeofi@gmail.com").await,)
);
assert_eq!(last_msg.from_id, DC_CONTACT_ID_INFO);
assert_eq!(last_msg.from_id, ContactId::INFO);
Ok(())
}
@@ -4903,7 +4896,7 @@ Hi, I created a group"#,
)
.await?;
let msg_out = t.get_last_msg().await;
assert_eq!(msg_out.from_id, DC_CONTACT_ID_SELF);
assert_eq!(msg_out.from_id, ContactId::SELF);
assert_eq!(msg_out.text.unwrap(), "Hi, I created a group");
assert_eq!(msg_out.in_reply_to, None);
@@ -4928,7 +4921,7 @@ Reply from different address
)
.await?;
let msg_in = t.get_last_msg().await;
assert_eq!(msg_in.to_id, DC_CONTACT_ID_SELF);
assert_eq!(msg_in.to_id, ContactId::SELF);
assert_eq!(msg_in.text.unwrap(), "Reply from different address");
assert_eq!(
msg_in.in_reply_to.unwrap(),
@@ -5046,4 +5039,81 @@ 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(())
}
}

View File

@@ -62,18 +62,18 @@ use std::str::FromStr;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use anyhow::{ensure, Context as _, Result};
use async_std::task;
use async_std::future::timeout;
use async_std::{channel, task};
use serde::{Deserialize, Serialize};
use crate::chat::{send_msg, ChatId};
use crate::constants::{
DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH, DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_SELF,
};
use crate::constants::{DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH};
use crate::contact::ContactId;
use crate::context::Context;
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, DC_CONTACT_ID_SELF).await);
msg.text = Some(stock_ephemeral_timer_changed(context, timer, ContactId::SELF).await);
msg.param.set_cmd(SystemMessage::EphemeralTimerChanged);
if let Err(err) = send_msg(context, self, &mut msg).await {
error!(
@@ -346,8 +346,8 @@ pub(crate) async fn delete_expired_messages(context: &Context) -> Result<bool> {
// which information dc_receive_imf::add_parts() still adds to the db if the chat_id is TRASH
r#"
UPDATE msgs
SET
chat_id=?, txt='', subject='', txt_raw='',
SET
chat_id=?, txt='', subject='', txt_raw='',
mime_headers='', from_id=0, to_id=0, param=''
WHERE
ephemeral_timestamp != 0
@@ -361,10 +361,10 @@ WHERE
> 0;
if let Some(delete_device_after) = context.get_config_delete_device_after().await? {
let self_chat_id = ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF)
let self_chat_id = ChatId::lookup_by_contact(context, ContactId::SELF)
.await?
.unwrap_or_default();
let device_chat_id = ChatId::lookup_by_contact(context, DC_CONTACT_ID_DEVICE)
let device_chat_id = ChatId::lookup_by_contact(context, ContactId::DEVICE)
.await?
.unwrap_or_default();
@@ -378,7 +378,8 @@ WHERE
.sql
.execute(
"UPDATE msgs \
SET txt = 'DELETED', chat_id = ? \
SET chat_id = ?, txt = '', subject='', txt_raw='', \
mime_headers='', from_id=0, to_id=0, param='' \
WHERE timestamp < ? \
AND chat_id > ? \
AND chat_id != ? \
@@ -398,6 +399,14 @@ WHERE
}
schedule_ephemeral_task(context).await;
if updated {
context.emit_event(EventType::MsgsChanged {
msg_id: MsgId::new(0),
chat_id: ChatId::new(0),
})
}
Ok(updated)
}
@@ -435,8 +444,9 @@ pub async fn schedule_ephemeral_task(context: &Context) {
};
// Cancel existing task, if any
if let Some(ephemeral_task) = context.ephemeral_task.write().await.take() {
ephemeral_task.cancel().await;
let old_task = context.ephemeral_task.write().await.take();
if let Some((_, stop_sender)) = old_task {
stop_sender.try_send(()).ok();
}
if let Some(ephemeral_timestamp) = ephemeral_timestamp {
@@ -445,27 +455,27 @@ pub async fn schedule_ephemeral_task(context: &Context) {
+ Duration::from_secs(ephemeral_timestamp.try_into().unwrap_or(u64::MAX))
+ Duration::from_secs(1);
if let Ok(duration) = until.duration_since(now) {
// 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),
});
}
let (stop_sender, stop_receiver) = channel::bounded(1);
let context1 = context.clone();
let ephemeral_task = task::spawn(async move {
if let Some((join_handle, _)) = old_task {
join_handle.await; // First of all, join the old task.
}
if let Ok(duration) = until.duration_since(now) {
timeout(duration, stop_receiver.recv()).await;
} else {
stop_receiver.try_recv();
}
delete_expired_messages(&context).await.ok_or_log(&context);
});
// Schedule a task, ephemeral_timestamp is in the future
*context.ephemeral_task.write().await = Some((ephemeral_task, stop_sender));
}
}
pub(crate) async fn ephemeral_deletion_loop(context: &Context) -> Result {}
/// Schedules expired IMAP messages for deletion.
pub(crate) async fn delete_expired_imap_messages(context: &Context) -> Result<()> {
let now = time();
@@ -544,7 +554,7 @@ mod tests {
let context = TestContext::new().await;
assert_eq!(
stock_ephemeral_timer_changed(&context, Timer::Disabled, DC_CONTACT_ID_SELF).await,
stock_ephemeral_timer_changed(&context, Timer::Disabled, ContactId::SELF).await,
"Message deletion timer is disabled by me."
);
@@ -552,7 +562,7 @@ mod tests {
stock_ephemeral_timer_changed(
&context,
Timer::Enabled { duration: 1 },
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 1 s by me."
@@ -561,7 +571,7 @@ mod tests {
stock_ephemeral_timer_changed(
&context,
Timer::Enabled { duration: 30 },
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 30 s by me."
@@ -570,7 +580,7 @@ mod tests {
stock_ephemeral_timer_changed(
&context,
Timer::Enabled { duration: 60 },
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 1 minute by me."
@@ -579,7 +589,7 @@ mod tests {
stock_ephemeral_timer_changed(
&context,
Timer::Enabled { duration: 90 },
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 1.5 minutes by me."
@@ -588,7 +598,7 @@ mod tests {
stock_ephemeral_timer_changed(
&context,
Timer::Enabled { duration: 30 * 60 },
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 30 minutes by me."
@@ -597,7 +607,7 @@ mod tests {
stock_ephemeral_timer_changed(
&context,
Timer::Enabled { duration: 60 * 60 },
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 1 hour by me."
@@ -606,7 +616,7 @@ mod tests {
stock_ephemeral_timer_changed(
&context,
Timer::Enabled { duration: 5400 },
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 1.5 hours by me."
@@ -617,7 +627,7 @@ mod tests {
Timer::Enabled {
duration: 2 * 60 * 60
},
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 2 hours by me."
@@ -628,7 +638,7 @@ mod tests {
Timer::Enabled {
duration: 24 * 60 * 60
},
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 1 day by me."
@@ -639,7 +649,7 @@ mod tests {
Timer::Enabled {
duration: 2 * 24 * 60 * 60
},
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 2 days by me."
@@ -650,7 +660,7 @@ mod tests {
Timer::Enabled {
duration: 7 * 24 * 60 * 60
},
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 1 week by me."
@@ -661,7 +671,7 @@ mod tests {
Timer::Enabled {
duration: 4 * 7 * 24 * 60 * 60
},
DC_CONTACT_ID_SELF
ContactId::SELF
)
.await,
"Message deletion timer is set to 4 weeks by me."
@@ -845,6 +855,7 @@ mod tests {
}
async fn check_msg_was_deleted(t: &TestContext, chat: &Chat, msg_id: MsgId) {
// trigger deletion?
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 {
@@ -856,8 +867,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::new(0));
assert_eq!(msg.to_id, ContactId::new(0));
assert_eq!(msg.from_id, ContactId::UNDEFINED);
assert_eq!(msg.to_id, ContactId::UNDEFINED);
assert!(msg.text.is_none_or_empty(), "{:?}", msg.text);
let rawtxt: Option<String> = t
.sql

View File

@@ -279,7 +279,7 @@ mod tests {
use crate::chat;
use crate::chat::forward_msgs;
use crate::config::Config;
use crate::constants::DC_CONTACT_ID_SELF;
use crate::contact::ContactId;
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 &lt; &gt; and &amp; but also &quot; 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(), DC_CONTACT_ID_SELF);
assert_ne!(msg.get_from_id(), ContactId::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 &lt; &gt; and &amp; but also &quot; and &#x
.await
.unwrap();
let msg = alice.get_last_msg_in(chat.get_id()).await;
assert_eq!(msg.get_from_id(), DC_CONTACT_ID_SELF);
assert_eq!(msg.get_from_id(), ContactId::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 &lt; &gt; and &amp; but also &quot; 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(), DC_CONTACT_ID_SELF);
assert_ne!(msg.get_from_id(), ContactId::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 &lt; &gt; and &amp; but also &quot; 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(), DC_CONTACT_ID_SELF);
assert_eq!(msg.get_from_id(), ContactId::SELF);
assert_eq!(msg.is_dc_message, MessengerMessage::Yes);
assert!(msg.get_showpadlock());
assert!(msg.is_forwarded());

View File

@@ -20,9 +20,10 @@ use num_traits::FromPrimitive;
use crate::chat::{self, ChatId, ChatIdBlocked};
use crate::config::Config;
use crate::constants::{
Blocked, Chattype, ShowEmails, DC_CONTACT_ID_SELF, DC_FETCH_EXISTING_MSGS_COUNT,
DC_FOLDERS_CONFIGURED_VERSION, DC_LP_AUTH_OAUTH2,
Blocked, Chattype, ShowEmails, 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,
@@ -1639,7 +1640,7 @@ async fn should_move_out_of_spam(
if chat_id_blocked.blocked != Blocked::Not {
return Ok(false);
}
} else if from_id != DC_CONTACT_ID_SELF {
} else if from_id != ContactId::SELF {
// No chat with this contact found.
return Ok(false);
}

View File

@@ -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::constants::DC_CONTACT_ID_SELF;
use crate::contact::ContactId;
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, DC_CONTACT_ID_SELF).await?;
let chat_id = ChatId::create_for_contact(context, ContactId::SELF).await?;
let mut msg = Message {
viewtype: Viewtype::File,
..Default::default()

View File

@@ -4,7 +4,7 @@
//! and job types.
use std::fmt;
use anyhow::{bail, format_err, Context as _, Error, Result};
use anyhow::{bail, format_err, Context as _, 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, Smtp};
use crate::smtp::{smtp_send, SendResult, 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(std::result::Result<(), Error>),
Finished(Result<()>),
RetryNow,
RetryLater,
}
@@ -318,13 +318,18 @@ impl Job {
return Status::RetryLater;
}
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);
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)),
}
status
}
/// Read the recipients from old emails sent by the user and add them as contacts.

View File

@@ -7,7 +7,6 @@ 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;
@@ -315,7 +314,7 @@ pub async fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64
accuracy,
time(),
chat_id,
DC_CONTACT_ID_SELF,
ContactId::SELF,
]
).await {
warn!(context, "failed to store location {:?}", err);
@@ -324,7 +323,7 @@ pub async fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64
}
}
if continue_streaming {
context.emit_event(EventType::LocationChanged(Some(DC_CONTACT_ID_SELF)));
context.emit_event(EventType::LocationChanged(Some(ContactId::SELF)));
};
schedule_maybe_send_locations(context, false).await.ok();
}
@@ -463,10 +462,10 @@ pub async fn get_kml(context: &Context, chat_id: ChatId) -> Result<(String, u32)
GROUP BY timestamp \
ORDER BY timestamp;",
paramsv![
DC_CONTACT_ID_SELF,
ContactId::SELF,
locations_send_begin,
locations_last_sent,
DC_CONTACT_ID_SELF
ContactId::SELF
],
|row| {
let location_id: i32 = row.get(0)?;
@@ -667,7 +666,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![
DC_CONTACT_ID_SELF,
ContactId::SELF,
*locations_send_begin,
*locations_last_sent,
])

View File

@@ -10,8 +10,7 @@ use serde::{Deserialize, Serialize};
use crate::chat::{self, Chat, ChatId};
use crate::constants::{
Blocked, Chattype, VideochatType, DC_CHAT_ID_TRASH, DC_CONTACT_ID_INFO, DC_CONTACT_ID_SELF,
DC_DESIRED_TEXT_LEN, DC_MSG_ID_LAST_SPECIAL,
Blocked, Chattype, VideochatType, DC_CHAT_ID_TRASH, DC_DESIRED_TEXT_LEN, DC_MSG_ID_LAST_SPECIAL,
};
use crate::contact::{Contact, ContactId, Origin};
use crate::context::Context;
@@ -431,7 +430,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 DC_CONTACT_ID_SELF.
/// contact_id set to ContactId::SELF.
///
/// @param latitude North-south position of the location.
/// @param longitude East-west position of the location.
@@ -544,7 +543,7 @@ impl Message {
&chat_loaded
};
let contact = if self.from_id != DC_CONTACT_ID_SELF {
let contact = if self.from_id != ContactId::SELF {
match chat.typ {
Chattype::Group | Chattype::Broadcast | Chattype::Mailinglist => {
Some(Contact::get_by_id(context, self.from_id).await?)
@@ -597,8 +596,8 @@ impl Message {
pub fn is_info(&self) -> bool {
let cmd = self.param.get_cmd();
self.from_id == DC_CONTACT_ID_INFO
|| self.to_id == DC_CONTACT_ID_INFO
self.from_id == ContactId::INFO
|| self.to_id == ContactId::INFO
|| cmd != SystemMessage::Unknown && cmd != SystemMessage::AutocryptSetupMessage
}
@@ -1017,7 +1016,7 @@ pub async fn get_msg_info(context: &Context, msg_id: MsgId) -> Result<String> {
ret += &format!(" by {}", name);
ret += "\n";
if msg.from_id != DC_CONTACT_ID_SELF {
if msg.from_id != ContactId::SELF {
let s = dc_timestamp_to_str(if 0 != msg.timestamp_rcvd {
msg.timestamp_rcvd
} else {
@@ -1038,7 +1037,7 @@ pub async fn get_msg_info(context: &Context, msg_id: MsgId) -> Result<String> {
);
}
if msg.from_id == DC_CONTACT_ID_INFO || msg.to_id == DC_CONTACT_ID_INFO {
if msg.from_id == ContactId::INFO || msg.to_id == ContactId::INFO {
// device-internal message, no further details needed
return Ok(ret);
}
@@ -1432,7 +1431,7 @@ pub async fn handle_mdn(
rfc724_mid: &str,
timestamp_sent: i64,
) -> Result<Option<(ChatId, MsgId)>> {
if from_id == DC_CONTACT_ID_SELF {
if from_id == ContactId::SELF {
warn!(
context,
"ignoring MDN sent to self, this is a bug on the sender device"
@@ -1637,7 +1636,7 @@ pub async fn estimate_deletion_cnt(
from_server: bool,
seconds: i64,
) -> Result<usize> {
let self_chat_id = ChatId::lookup_by_contact(context, DC_CONTACT_ID_SELF)
let self_chat_id = ChatId::lookup_by_contact(context, ContactId::SELF)
.await?
.unwrap_or_default();
let threshold_timestamp = time() - seconds;
@@ -1805,7 +1804,6 @@ 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;
@@ -1945,7 +1943,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, DC_CONTACT_ID_DEVICE)
let device_chat_id = ChatId::get_for_contact(&t, ContactId::DEVICE)
.await
.unwrap();

View File

@@ -711,7 +711,7 @@ mod tests {
..
} = qr
{
assert_ne!(contact_id, ContactId::new(0));
assert_ne!(contact_id, ContactId::UNDEFINED);
assert_eq!(grpname, "test ? test !");
} else {
bail!("Wrong QR code type");
@@ -729,7 +729,7 @@ mod tests {
..
} = qr
{
assert_ne!(contact_id, ContactId::new(0));
assert_ne!(contact_id, ContactId::UNDEFINED);
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::new(0));
assert_ne!(contact_id, ContactId::UNDEFINED);
} else {
bail!("Wrong QR code type");
}

View File

@@ -6,8 +6,7 @@ use crate::{
chat::{Chat, ChatId},
color::color_int_to_hex_string,
config::Config,
constants::DC_CONTACT_ID_SELF,
contact::Contact,
contact::{Contact, ContactId},
context::Context,
securejoin, stock_str,
};
@@ -41,7 +40,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, DC_CONTACT_ID_SELF).await?;
let contact = Contact::get_by_id(context, ContactId::SELF).await?;
let avatar = match contact.get_profile_image(context).await? {
Some(path) => {

View File

@@ -305,6 +305,7 @@ 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 {
@@ -322,9 +323,16 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect
interrupt_info = Default::default();
}
None => {
if let Err(err) = send_smtp_messages(&ctx, &mut connection).await {
let res = send_smtp_messages(&ctx, &mut connection).await;
if let Err(err) = &res {
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");
@@ -333,7 +341,27 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect
Some(err) => connection.connectivity.set_err(&ctx, err).await,
}
interrupt_info = idle_interrupt_receiver.recv().await.unwrap_or_default();
// 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();
};
info!(ctx, "smtp fake idle - interrupted")
}
}
@@ -370,9 +398,10 @@ impl Scheduler {
let inbox_handle = {
let ctx = ctx.clone();
Some(task::spawn(async move {
inbox_loop(ctx, inbox_start_send, inbox_handlers).await
}))
Some(
task::spawn(async move { inbox_loop(ctx, inbox_start_send, inbox_handlers).await })
.cancel(),
)
};
if ctx.should_watch_mvbox().await? {

View File

@@ -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, DC_CONTACT_ID_LAST_SPECIAL};
use crate::constants::Blocked;
use crate::contact::{Contact, ContactId, Origin, VerifiedStatus};
use crate::context::Context;
use crate::dc_tools::time;
@@ -310,7 +310,7 @@ pub(crate) async fn handle_securejoin_handshake(
mime_message: &MimeMessage,
contact_id: ContactId,
) -> Result<HandshakeMessage> {
if contact_id <= DC_CONTACT_ID_LAST_SPECIAL {
if contact_id.is_special() {
return Err(Error::msg("Can not be called with special contact ID"));
}
let step = mime_message
@@ -573,7 +573,7 @@ pub(crate) async fn observe_securejoin_on_other_device(
mime_message: &MimeMessage,
contact_id: ContactId,
) -> Result<HandshakeMessage> {
if contact_id <= DC_CONTACT_ID_LAST_SPECIAL {
if contact_id.is_special() {
return Err(Error::msg("Can not be called with special contact ID"));
}
let step = mime_message

View File

@@ -4,14 +4,14 @@ pub mod send;
use std::time::{Duration, SystemTime};
use anyhow::{bail, format_err, Context as _, Result};
use anyhow::{bail, format_err, Context as _, Error, 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,7 +50,10 @@ impl Smtp {
/// Disconnect the SMTP transport and drop it entirely.
pub async fn disconnect(&mut self) {
if let Some(mut transport) = self.transport.take() {
transport.close().await.ok();
// 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 });
}
self.last_success = None;
}
@@ -196,12 +199,18 @@ 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],
@@ -209,7 +218,7 @@ pub(crate) async fn smtp_send(
smtp: &mut Smtp,
msg_id: MsgId,
rowid: i64,
) -> Status {
) -> SendResult {
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
info!(context, "smtp-sending out mime message:");
println!("{}", message);
@@ -217,6 +226,20 @@ 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;
@@ -253,14 +276,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");
Status::RetryLater
SendResult::Retry
} 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.
Status::Finished(Err(format_err!("Permanent SMTP error: {}", err)))
SendResult::Failure(format_err!("Permanent SMTP error: {}", err))
}
}
async_smtp::smtp::error::Error::Transient(ref response) => {
@@ -277,35 +300,29 @@ 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);
Status::Finished(Err(format_err!("Permanent SMTP error: {}", err)))
SendResult::Failure(format_err!("Permanent SMTP error: {}", err))
} else {
info!(
context,
"Transient error with status code {}, postponing retry for later",
first_word
);
Status::RetryLater
SendResult::Retry
}
} else {
info!(
context,
"Transient error without status code, postponing retry for later"
);
Status::RetryLater
SendResult::Retry
}
}
_ => {
info!(
context,
"Message sending failed without error returned by the server"
"Message sending failed without error returned by the server, retry later"
);
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
}
SendResult::Retry
}
};
@@ -319,24 +336,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);
Status::Finished(Err(err.into()))
SendResult::Failure(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");
Status::Finished(Err(format_err!("SMTP has not transport")))
SendResult::Failure(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);
Status::Finished(Err(err))
SendResult::Failure(err)
}
Ok(()) => Status::Finished(Ok(())),
Ok(()) => SendResult::Success,
};
if let Status::Finished(Err(err)) = &status {
if let SendResult::Failure(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;
}
@@ -432,7 +449,7 @@ pub(crate) async fn send_msg_to_smtp(
return Ok(());
}
let status = match smtp_send(
let status = smtp_send(
context,
&recipients_list,
body.as_str(),
@@ -440,47 +457,25 @@ pub(crate) async fn send_msg_to_smtp(
msg_id,
rowid,
)
.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.");
.await;
// 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 {
Status::Finished(res) => {
SendResult::Retry => {}
SendResult::Success | SendResult::Failure(_) => {
context
.sql
.execute("DELETE FROM smtp WHERE id=?", paramsv![rowid])
.await?;
if res.is_ok() {
msg_id.set_delivered(context).await?;
}
res
}
Status::RetryNow | Status::RetryLater => Err(format_err!("Retry")),
};
match status {
SendResult::Retry => Err(format_err!("Retry")),
SendResult::Success => {
msg_id.set_delivered(context).await?;
Ok(())
}
SendResult::Failure(err) => Err(format_err!("{}", err)),
}
}
@@ -488,10 +483,9 @@ 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.
pub(crate) async fn send_smtp_messages(
context: &Context,
connection: &mut Smtp,
) -> anyhow::Result<()> {
///
/// Returns true if all messages were sent successfully, false otherwise.
pub(crate) async fn send_smtp_messages(context: &Context, connection: &mut Smtp) -> Result<bool> {
context.send_sync_msg().await?; // Add sync message to the end of the queue if needed.
let rowids = context
.sql
@@ -509,10 +503,12 @@ pub(crate) async fn send_smtp_messages(
},
)
.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(())
Ok(success)
}

View File

@@ -603,6 +603,55 @@ pub async fn housekeeping(context: &Context) -> Result<()> {
warn!(context, "Failed to delete expired messages: {}", err);
}
if let Err(err) = remove_unused_files(context).await {
warn!(
context,
"Housekeeping: cannot remove unusued files: {}", 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;
@@ -717,44 +766,6 @@ pub async fn housekeeping(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(())
}

View File

@@ -10,7 +10,6 @@ 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;
@@ -397,7 +396,7 @@ trait StockStringMods: AsRef<str> + Sized {
Box::pin(async move {
let message = self.as_ref().trim_end_matches('.');
match contact_id {
DC_CONTACT_ID_SELF => msg_action_by_me(context, message).await,
ContactId::SELF => msg_action_by_me(context, message).await,
_ => {
let displayname = Contact::get_by_id(context, contact_id)
.await
@@ -1129,7 +1128,7 @@ impl Context {
self.sql
.set_raw_config_bool("self-chat-added", true)
.await?;
ChatId::create_for_contact(self, DC_CONTACT_ID_SELF).await?;
ChatId::create_for_contact(self, ContactId::SELF).await?;
}
// add welcome-messages. by the label, this is done only once,
@@ -1149,14 +1148,13 @@ impl Context {
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::TestContext;
use crate::constants::DC_CONTACT_ID_SELF;
use num_traits::ToPrimitive;
use crate::chat::Chat;
use crate::chatlist::Chatlist;
use num_traits::ToPrimitive;
use crate::test_utils::TestContext;
use super::*;
#[test]
fn test_enum_mapping() {
@@ -1233,7 +1231,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", DC_CONTACT_ID_SELF).await,
msg_add_member(&t, "alice@example.org", ContactId::SELF).await,
"Member alice@example.org added by me."
)
}
@@ -1245,7 +1243,7 @@ mod tests {
.await
.expect("failed to create contact");
assert_eq!(
msg_add_member(&t, "alice@example.org", DC_CONTACT_ID_SELF).await,
msg_add_member(&t, "alice@example.org", ContactId::SELF).await,
"Member Alice (alice@example.org) added by me."
);
}

View File

@@ -1,8 +1,8 @@
//! # Message summary for chatlist.
use crate::chat::Chat;
use crate::constants::{Chattype, DC_CONTACT_ID_SELF};
use crate::contact::Contact;
use crate::constants::Chattype;
use crate::contact::{Contact, ContactId};
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 == DC_CONTACT_ID_SELF {
} else if msg.from_id == ContactId::SELF {
if msg.is_info() || chat.is_self_talk() {
None
} else {

View File

@@ -2,7 +2,8 @@
use crate::chat::{Chat, ChatId};
use crate::config::Config;
use crate::constants::{Blocked, DC_CONTACT_ID_SELF};
use crate::constants::Blocked;
use crate::contact::ContactId;
use crate::context::Context;
use crate::dc_tools::time;
use crate::message::{Message, MsgId, Viewtype};
@@ -126,7 +127,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, DC_CONTACT_ID_SELF, Blocked::Yes)
ChatId::create_for_contact_with_blocked(self, ContactId::SELF, Blocked::Yes)
.await?;
let mut msg = Message {
chat_id,
@@ -483,7 +484,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, DC_CONTACT_ID_SELF).await?;
let chat_id = ChatId::create_for_contact(&alice, ContactId::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);

View File

@@ -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_CONTACT_ID_SELF, DC_MSG_ID_DAYMARKER, DC_MSG_ID_MARKER1};
use crate::contact::{Contact, Modifier, Origin};
use crate::constants::{DC_MSG_ID_DAYMARKER, DC_MSG_ID_MARKER1};
use crate::contact::{Contact, ContactId, Modifier, Origin};
use crate::context::Context;
use crate::dc_receive_imf::dc_receive_imf;
use crate::dc_tools::EmailAddress;
@@ -466,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, DC_CONTACT_ID_SELF)
let chat_id = ChatId::create_for_contact(self, ContactId::SELF)
.await
.unwrap();
Chat::load_from_db(self, chat_id).await.unwrap()
@@ -851,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() == DC_CONTACT_ID_SELF {
if msg.get_from_id() == ContactId::SELF {
""
} else if msg.get_state() == MessageState::InSeen {
"[SEEN]"