mirror of
https://github.com/chatmail/core.git
synced 2026-06-28 02:26:35 +03:00
Compare commits
1 Commits
search-bug
...
update_dep
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca0672e718 |
70
CHANGELOG.md
70
CHANGELOG.md
@@ -1,74 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 1.47.0
|
||||
|
||||
- breaking change: `dc_update_device_chats()` removed;
|
||||
this is now done automatically during configure
|
||||
unless the new config-option `bot` is set #1957
|
||||
|
||||
- breaking change: split `DC_EVENT_MSGS_NOTICED` off `DC_EVENT_MSGS_CHANGED`
|
||||
and remove `dc_marknoticed_all_chats()` #1942 #1981
|
||||
|
||||
- breaking change: remove unused starring options #1965
|
||||
|
||||
- breaking change: `DC_CHAT_TYPE_VERIFIED_GROUP` replaced by
|
||||
`dc_chat_is_protected()`; also single-chats may be protected now, this may
|
||||
happen over the wire even if the UI do not offer an option for that #1968
|
||||
|
||||
- breaking change: split quotes off message text,
|
||||
UIs should use at least `dc_msg_get_quoted_text()` to show quotes now #1975
|
||||
|
||||
- new api for quote handling: `dc_msg_set_quote()`, `dc_msg_get_quoted_text()`,
|
||||
`dc_msg_get_quoted_msg()` #1975 #1984 #1985 #1987 #1989 #2004
|
||||
|
||||
- require quorum to enable encryption #1946
|
||||
|
||||
- speed up and clean up account creation #1912 #1927 #1960 #1961
|
||||
|
||||
- configure now collects recent contacts and fetches last messages
|
||||
unless disabled by `fetch_existing` config-option #1913 #2003
|
||||
|
||||
- emit `DC_EVENT_CHAT_MODIFIED` on contact rename
|
||||
and set contact-id on `DC_EVENT_CONTACTS_CHANGED` #1935 #1936 #1937
|
||||
|
||||
- add `dc_set_chat_protection()`; the `protect` parameter in
|
||||
`dc_create_group_chat()` will be removed in an upcoming release;
|
||||
up to then, UIs using the "verified group" paradigm
|
||||
should not use `dc_set_chat_protection()` #1968 #2014 #2001 #2012 #2007
|
||||
|
||||
- remove unneeded `DC_STR_COUNT` #1991
|
||||
|
||||
- mark all failed messages as failed when receiving an NDN #1993
|
||||
|
||||
- fix import temporary directory usage #1929
|
||||
|
||||
- fix forcing encryption for reset peers #1998
|
||||
|
||||
- fix: do not allow to save drafts in non-writeable chats #1997
|
||||
|
||||
- fix: do not show HTML if there is no content and there is an attachment #1988
|
||||
|
||||
- fix recovering offline/lost connections, fixes background receive bug #1983
|
||||
|
||||
- fix ordering of accounts returned by `dc_accounts_get_all()` #1909
|
||||
|
||||
- fix whitespace for summaries #1938
|
||||
|
||||
- fix: improve sentbox name guessing #1941
|
||||
|
||||
- fix: avoid manual poll impl for accounts events #1944
|
||||
|
||||
- fix encoding newlines in param as a preparation for storing quotes #1945
|
||||
|
||||
- fix: internal and ffi error handling #1967 #1966 #1959 #1911 #1916 #1917 #1915
|
||||
|
||||
- fix ci #1928 #1931 #1932 #1933 #1934 #1943
|
||||
|
||||
- update provider-database #1940 #2005 #2006
|
||||
|
||||
- update dependencies #1919 #1908 #1950 #1963 #1996 #2010 #2013
|
||||
|
||||
|
||||
## 1.46.0
|
||||
|
||||
- breaking change: `dc_configure()` report errors in
|
||||
@@ -814,3 +745,4 @@
|
||||
For a full list of changes, please see our closed Pull Requests:
|
||||
|
||||
https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed
|
||||
|
||||
|
||||
333
Cargo.lock
generated
333
Cargo.lock
generated
@@ -108,10 +108,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.13"
|
||||
name = "ahash"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
|
||||
checksum = "f6789e291be47ace86a60303502173d84af8327e3627ecf334356ee0f87a164c"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -136,9 +142,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.32"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
|
||||
checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
@@ -170,9 +176,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "1.4.2"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21279cfaa4f47df10b1816007e738ca3747ef2ee53ffc51cdbf57a8bb266fee3"
|
||||
checksum = "59740d83946db6a5af71ae25ddf9562c2b176b2ca42cf99a455f09f4a220d6b9"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener",
|
||||
@@ -224,13 +230,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-imap"
|
||||
version = "0.4.1"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bdc2bb91844456110d27da8f712f4cbc693f3538521a43cb79c6b2e6f717f3e"
|
||||
checksum = "caadebe62f5ad78d6418a47a86b5b84439a52bdd5c893e55a5c81974da111455"
|
||||
dependencies = [
|
||||
"async-native-tls",
|
||||
"async-std",
|
||||
"base64 0.13.0",
|
||||
"base64 0.12.3",
|
||||
"byte-pool",
|
||||
"chrono",
|
||||
"futures",
|
||||
@@ -246,13 +252,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "1.1.6"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5bfd63f6fc8fd2925473a147d3f4d252c712291efdde0d7057b25146563402c"
|
||||
checksum = "d54bc4c1c7292475efb2253227dbcfad8fe1ca4c02bc62c510cc2f3da5c4704e"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"libc",
|
||||
"log",
|
||||
"nb-connect",
|
||||
"once_cell",
|
||||
@@ -260,6 +267,7 @@ dependencies = [
|
||||
"polling",
|
||||
"vec-arena",
|
||||
"waker-fn",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -407,14 +415,14 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.51"
|
||||
version = "0.3.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec1931848a574faa8f7c71a12ea00453ff5effbb5f51afe7f77d7a48cace6ac1"
|
||||
checksum = "707b586e0e2f247cbde68cdd2c3ce69ea7b7be43e1c5b426e37c9319c4b9838e"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"miniz_oxide 0.4.2",
|
||||
"miniz_oxide 0.4.3",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
@@ -446,12 +454,6 @@ version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.2"
|
||||
@@ -536,16 +538,16 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||
|
||||
[[package]]
|
||||
name = "blocking"
|
||||
version = "1.0.0"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2640778f8053e72c11f621b0a5175a0560a269282aa98ed85107773ab8e2a556"
|
||||
checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-task",
|
||||
"atomic-waker",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
"waker-fn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -643,9 +645,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.60"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c"
|
||||
checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
|
||||
|
||||
[[package]]
|
||||
name = "cfb-mode"
|
||||
@@ -662,6 +664,12 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "charset"
|
||||
version = "0.1.2"
|
||||
@@ -722,9 +730,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.0.1"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
@@ -798,7 +806,7 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -865,7 +873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"maybe-uninit",
|
||||
@@ -879,7 +887,7 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"crossbeam-utils",
|
||||
"maybe-uninit",
|
||||
]
|
||||
@@ -891,7 +899,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
@@ -1003,7 +1011,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.47.0"
|
||||
version = "1.46.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"anyhow",
|
||||
@@ -1033,6 +1041,7 @@ dependencies = [
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"kamadak-exif",
|
||||
"lazy_static",
|
||||
"lettre_email",
|
||||
"libc",
|
||||
"log",
|
||||
@@ -1040,7 +1049,6 @@ dependencies = [
|
||||
"native-tls",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pgp",
|
||||
"pretty_assertions",
|
||||
@@ -1079,7 +1087,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.47.0"
|
||||
version = "1.46.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-std",
|
||||
@@ -1180,17 +1188,11 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07dfc993ea376e864fe29a4099a61ca0bb994c6d7745a61bf60ddb3d64e05237"
|
||||
checksum = "37c66a534cbb46ab4ea03477eae19d5c22c01da8258030280b7bd9d8433fb6ef"
|
||||
dependencies = [
|
||||
"signature",
|
||||
]
|
||||
@@ -1314,7 +1316,7 @@ version = "0.8.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a51b8cf747471cb9499b6d59e59b0444f4c90eba8968c4e44874e92b5b64ace2"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1386,9 +1388,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.3.5"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c85295147490b8fcf2ea3d104080a105a8b2c63f9c319e82c02d8e952388919"
|
||||
checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
@@ -1396,7 +1401,7 @@ version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"winapi",
|
||||
@@ -1408,10 +1413,10 @@ version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da80be589a72651dcda34d8b35bcdc9b7254ad06325611074d9cc0fbb19f60ee"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"crc32fast",
|
||||
"libc",
|
||||
"miniz_oxide 0.4.2",
|
||||
"miniz_oxide 0.4.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1447,9 +1452,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613"
|
||||
checksum = "5d8e3078b7b2a8a671cb7a3d17b4760e4181ea243227776ba83fd043b4ca034e"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1462,9 +1467,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
|
||||
checksum = "a7a4d35f7401e948629c9c3d6638fb9bf94e0b2121e96c3b428cc4e631f3eb74"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -1472,15 +1477,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
|
||||
checksum = "d674eaa0056896d5ada519900dbf97ead2e46a7b6621e8160d79e2f2e1e2784b"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314"
|
||||
checksum = "cc709ca1da6f66143b8c9bec8e6260181869893714e9b5a490b169b0414144ab"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -1489,15 +1494,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
|
||||
checksum = "5fc94b64bb39543b4e432f1790b6bf18e3ee3b74653c5449f63310e9a74b123c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "1.8.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0db18c5f58083b54b0c416638ea73066722c2815c1e54dd8ba85ee3def593c3a"
|
||||
checksum = "381a7ad57b1bad34693f63f6f377e1abded7a9c85c9d3eb6771e11c60aaadab9"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
@@ -1510,9 +1515,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
|
||||
checksum = "f57ed14da4603b2554682e9f2ff3c65d7567b53188db96cb71538217fc64581b"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
@@ -1522,24 +1527,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
|
||||
checksum = "0d8764258ed64ebc5d9ed185cf86a95db5cac810269c5d20ececb32e0088abbd"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
|
||||
checksum = "4dd26820a9f3637f1302da8bceba3ff33adbe53464b54ca24d4e2d4f1db30f94"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
|
||||
checksum = "8a894a0acddba51a2d49a6f4263b1e64b8c579ece8af50fa86503d52cd1eea34"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1571,7 +1576,7 @@ version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
@@ -1625,6 +1630,18 @@ name = "hashbrown"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@@ -1637,9 +1654,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.16"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151"
|
||||
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -1693,9 +1710,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "http-client"
|
||||
version = "6.0.0"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a519fb1e28c8aa09b1291c17f814cd0efb239c509f76d5acf75089e606b93e8"
|
||||
checksum = "9cc054d9c24096dde2dc73c0d37fa3f9269443dbf4a7ba47598ec9cffe71220e"
|
||||
dependencies = [
|
||||
"async-h1",
|
||||
"async-native-tls",
|
||||
@@ -1720,7 +1737,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_qs",
|
||||
"serde_urlencoded 0.7.0",
|
||||
"serde_urlencoded",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -1789,9 +1806,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "imap-proto"
|
||||
version = "0.11.0"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3091b99ee5b80f9b010eb6f962af9495ad06561bf662126b077e8ca30e463182"
|
||||
checksum = "16a6def1d5ac8975d70b3fd101d57953fe3278ef2ee5d7816cba54b1d1dfc22f"
|
||||
dependencies = [
|
||||
"nom 5.1.2",
|
||||
]
|
||||
@@ -1818,7 +1835,7 @@ version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63312a18f7ea8760cdd0a7c5aac1a619752a246b833545e3e36d1f81f7cd9e66"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1931,16 +1948,16 @@ checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.78"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98"
|
||||
checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -1950,9 +1967,9 @@ checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.20.0"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3a245984b1b06c291f46e27ebda9f369a94a1ab8461d0e845e23f9ced01f5db"
|
||||
checksum = "64d31059f22935e6c31830db5249ba2b7ecd54fd73a9909286f0a67aa55c2fbd"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
@@ -1980,7 +1997,7 @@ version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2074,9 +2091,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c60c0dfe32c10b43a144bad8fc83538c52f58302c92300ea7ec7bf7b38d5a7b9"
|
||||
checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg 1.0.1",
|
||||
@@ -2108,9 +2125,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nb-connect"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "701f47aeb98466d0a7fea67e2c2f667c33efa1f2e4fd7f76743aac1153196f72"
|
||||
checksum = "8123a81538e457d44b933a02faf885d3fe8408806b23fa700e8f01c6c3a98998"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
@@ -2124,7 +2141,7 @@ checksum = "4dbdc256eaac2e3bd236d93ad999d3479ef775c863dbda3068c4006a92eec51b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"void",
|
||||
]
|
||||
@@ -2244,9 +2261,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.20.0"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
|
||||
checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
@@ -2279,7 +2296,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"foreign-types",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
@@ -2294,9 +2311,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.11.0+1.1.1h"
|
||||
version = "111.12.0+1.1.1h"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380fe324132bea01f45239fadfec9343adb044615f29930d039bec1ae7b9fa5b"
|
||||
checksum = "858a4132194f8570a7ee9eb8629e85b23cbc4565f2d4a162e87556e5956abf61"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
@@ -2339,7 +2356,7 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a85ea9fc0d4ac0deb6fe7911d38786b32fc11119afd9e9d38b84ff691ce64220"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2365,7 +2382,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"cloudabi",
|
||||
"instant",
|
||||
"libc",
|
||||
@@ -2442,18 +2459,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.25"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b9e280448854bd91559252582173b3bd1f8e094a0e644791c0628ca9b1f144f"
|
||||
checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "0.4.25"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8c8b352676bc6a4c3d71970560b913cea444a7a921cc2e2d920225e4b91edaa"
|
||||
checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2474,9 +2491,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.18"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
|
||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
@@ -2504,14 +2521,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "1.1.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0720e0b9ea9d52451cf29d3413ba8a9303f8815d9d9653ef70e03ff73e65566"
|
||||
checksum = "ab773feb154f12c49ffcfd66ab8bdcf9a1843f950db48b0d8be9d4393783b058"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"log",
|
||||
"wepoll-sys-stjepang",
|
||||
"wepoll-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -2521,7 +2538,7 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5884790f1ce3553ad55fec37b5aaac5882e0e845a2612df744d6c85c9bf046c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
@@ -2740,9 +2757,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.9"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
|
||||
checksum = "36f45b719a674bf4b828ff318906d6c133264c793eff7a41e30074a45b5099e2"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -2761,9 +2778,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.18"
|
||||
version = "0.6.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
|
||||
checksum = "c17be88d9eaa858870aa5e48cc406c206e4600e983fc4f06bbe5750d93d09761"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
@@ -2840,15 +2857,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.24.0"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c78c3275d9d6eb684d2db4b2388546b32fdae0586c20a82f3905d21ea78b9ef"
|
||||
checksum = "7e3d4791ab5517217f51216a84a688b53c1ebf7988736469c538d02f46ddba68"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"lru-cache",
|
||||
"memchr",
|
||||
"smallvec",
|
||||
]
|
||||
@@ -2867,9 +2884,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.16"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
checksum = "b2610b7f643d18c87dff3b489950269617e6601a51f1f05aa5daefee36f64f0b"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
@@ -2932,9 +2949,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sanitize-filename"
|
||||
version = "0.3.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf18934a12018228c5b55a6dae9df5d0641e3566b3630cb46cc55564068e7c2f"
|
||||
checksum = "23fd0fec94ec480abfd86bb8f4f6c57e0efb36dac5c852add176ea7b04c74801"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
@@ -3035,9 +3052,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.58"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4"
|
||||
checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -3056,18 +3073,6 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
|
||||
dependencies = [
|
||||
"dtoa",
|
||||
"itoa",
|
||||
"serde",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.0"
|
||||
@@ -3087,7 +3092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"cpuid-bool",
|
||||
"digest",
|
||||
"opaque-debug 0.3.0",
|
||||
@@ -3106,7 +3111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"cpuid-bool",
|
||||
"digest",
|
||||
"opaque-debug 0.3.0",
|
||||
@@ -3159,7 +3164,7 @@ version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"winapi",
|
||||
@@ -3179,9 +3184,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "standback"
|
||||
version = "0.2.10"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33a71ea1ea5f8747d1af1979bfb7e65c3a025a70609f04ceb78425bc5adad8e6"
|
||||
checksum = "f4e0831040d2cf2bdfd51b844be71885783d489898a192f254ae25d57cce725c"
|
||||
dependencies = [
|
||||
"version_check 0.9.2",
|
||||
]
|
||||
@@ -3269,15 +3274,15 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.19.2"
|
||||
version = "0.19.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3924a58d165da3b7b2922c667ab0673c7b5fd52b5c19ea3442747bcb3cd15abe"
|
||||
checksum = "b89a286a7e3b5720b9a477b23253bc50debac207c8d21505f8e70b36792f11b5"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.19.2"
|
||||
version = "0.19.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d2ab682ecdcae7f5f45ae85cd7c1e6c8e68ea42c8a612d47fedf831c037146a"
|
||||
checksum = "e61bb0be289045cb80bfce000512e32d09f8337e54c186725da381377ad1f8d5"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -3293,13 +3298,13 @@ checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd"
|
||||
|
||||
[[package]]
|
||||
name = "surf"
|
||||
version = "2.0.0-alpha.7"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "187577a5f02b0156cb5dcc69504fb57a3f8247361dca07864a30dd294ee9a698"
|
||||
checksum = "6d38339c05d2bc84595d56e27aa07566e7b59767ea5cb5c78991b499cd188060"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"futures-util",
|
||||
"http-client",
|
||||
"http-types",
|
||||
@@ -3308,15 +3313,13 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded 0.6.1",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.42"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228"
|
||||
checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3341,7 +3344,7 @@ version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"rand",
|
||||
"redox_syscall",
|
||||
@@ -3369,18 +3372,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
|
||||
checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
|
||||
checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3463,9 +3466,9 @@ checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.6"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
|
||||
checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -3496,7 +3499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f23cdfdc3d8300b3c50c9e84302d3bd6d860fb9529af84ace6cf9665f181b77"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"futures",
|
||||
"ipconfig",
|
||||
"lazy_static",
|
||||
@@ -3514,7 +3517,7 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3691,7 +3694,7 @@ version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
@@ -3716,7 +3719,7 @@ version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
@@ -3768,19 +3771,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0e26e7a4d998e3d7949c69444b8b4916bac810da0d3a82ae612c89e952782f4"
|
||||
|
||||
[[package]]
|
||||
name = "wepoll-sys-stjepang"
|
||||
version = "1.0.8"
|
||||
name = "wepoll-sys"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fdfbb03f290ca0b27922e8d48a0997b4ceea12df33269b9f75e713311eb178d"
|
||||
checksum = "142bc2cba3fe88be1a8fcb55c727fa4cd5b0cf2d7438722792e22f26f04bc1e0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a763e303c0e0f23b0da40888724762e802a8ffefbc22de4127ef42493c2ea68c"
|
||||
checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.47.0"
|
||||
version = "1.46.0"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
@@ -34,7 +34,7 @@ serde_json = "1.0"
|
||||
chrono = "0.4.6"
|
||||
indexmap = "1.3.0"
|
||||
kamadak-exif = "0.5"
|
||||
once_cell = "1.4.1"
|
||||
lazy_static = "1.4.0"
|
||||
regex = "1.1.6"
|
||||
rusqlite = { version = "0.24", features = ["bundled"] }
|
||||
r2d2_sqlite = "0.17.0"
|
||||
@@ -47,7 +47,7 @@ itertools = "0.9.0"
|
||||
quick-xml = "0.18.1"
|
||||
escaper = "0.1.0"
|
||||
bitflags = "1.1.0"
|
||||
sanitize-filename = "0.3.0"
|
||||
sanitize-filename = "0.2.1"
|
||||
stop-token = { version = "0.1.1", features = ["unstable"] }
|
||||
mailparse = "0.13.0"
|
||||
encoded-words = { git = "https://github.com/async-email/encoded-words", branch="master" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.47.0"
|
||||
version = "1.46.0"
|
||||
description = "Deltachat FFI"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2018"
|
||||
|
||||
@@ -349,10 +349,6 @@ char* dc_get_blobdir (const dc_context_t* context);
|
||||
* https://github.com/cracker0dks/basicwebrtc which some UIs have native support for.
|
||||
* The type `jitsi:` may be handled by external apps.
|
||||
* If no type is prefixed, the videochat is handled completely in a browser.
|
||||
* - `bot` = Set to "1" if this is a bot. E.g. prevents adding the "Device messages" and "Saved messages" chats.
|
||||
* - `fetch_existing` = 1=fetch most recent existing messages on configure (default),
|
||||
* 0=do not fetch existing messages on configure.
|
||||
* In both cases, existing recipients are added to the contact database.
|
||||
*
|
||||
* If you want to retrieve a value, use dc_get_config().
|
||||
*
|
||||
@@ -988,6 +984,24 @@ void dc_set_draft (dc_context_t* context, uint32_t ch
|
||||
*/
|
||||
uint32_t dc_add_device_msg (dc_context_t* context, const char* label, dc_msg_t* msg);
|
||||
|
||||
|
||||
/**
|
||||
* Init device-messages and saved-messages chat.
|
||||
* This function adds the device-chat and saved-messages chat
|
||||
* and adds one or more welcome or update-messages.
|
||||
* The ui can add messages on its own using dc_add_device_msg() -
|
||||
* for ordering, either before or after or even without calling this function.
|
||||
*
|
||||
* Chat and message creation is done only once.
|
||||
* So if the user has manually deleted things, they won't be re-created
|
||||
* (however, not seen device messages are added and may re-create the device-chat).
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
*/
|
||||
void dc_update_device_chats (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Check if a device-message with a given label was ever added.
|
||||
* Device-messages can be added dc_add_device_msg().
|
||||
@@ -1156,24 +1170,6 @@ dc_array_t* dc_get_chat_media (dc_context_t* context, uint32_t ch
|
||||
uint32_t dc_get_next_media (dc_context_t* context, uint32_t msg_id, int dir, int msg_type, int msg_type2, int msg_type3);
|
||||
|
||||
|
||||
/**
|
||||
* Enable or disable protection against active attacks.
|
||||
* To enable protection, it is needed that all members are verified;
|
||||
* if this condition is met, end-to-end-encryption is always enabled
|
||||
* and only the verified keys are used.
|
||||
*
|
||||
* Sends out #DC_EVENT_CHAT_MODIFIED on changes
|
||||
* and #DC_EVENT_MSGS_CHANGED if a status message was sent.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object as returned from dc_context_new().
|
||||
* @param chat_id The ID of the chat to change the protection for.
|
||||
* @param protect 1=protect chat, 0=unprotect chat
|
||||
* @return 1=success, 0=error, eg. some members may be unverified
|
||||
*/
|
||||
int dc_set_chat_protection (dc_context_t* context, uint32_t chat_id, int protect);
|
||||
|
||||
|
||||
/**
|
||||
* Set chat visibility to pinned, archived or normal.
|
||||
*
|
||||
@@ -1303,15 +1299,15 @@ dc_chat_t* dc_get_chat (dc_context_t* context, uint32_t ch
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
* @param protect If set to 1 the function creates group with protection initially enabled.
|
||||
* Only verified members are allowed in these groups
|
||||
* @param verified If set to 1 the function creates a secure verified group.
|
||||
* Only secure-verified members are allowed in these groups
|
||||
* and end-to-end-encryption is always enabled.
|
||||
* @param name The name of the group chat to create.
|
||||
* The name may be changed later using dc_set_chat_name().
|
||||
* To find out the name of a group later, see dc_chat_get_name()
|
||||
* @return The chat ID of the new group chat, 0 on errors.
|
||||
*/
|
||||
uint32_t dc_create_group_chat (dc_context_t* context, int protect, const char* name);
|
||||
uint32_t dc_create_group_chat (dc_context_t* context, int verified, const char* name);
|
||||
|
||||
|
||||
/**
|
||||
@@ -1333,7 +1329,7 @@ int dc_is_contact_in_chat (dc_context_t* context, uint32_t ch
|
||||
* If the group is already _promoted_ (any message was sent to the group),
|
||||
* all group members are informed by a special status message that is sent automatically by this function.
|
||||
*
|
||||
* If the group has group protection enabled, only verified contacts can be added to the group.
|
||||
* If the group is a verified group, only verified contacts can be added to the group.
|
||||
*
|
||||
* Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent.
|
||||
*
|
||||
@@ -1977,7 +1973,7 @@ dc_lot_t* dc_check_qr (dc_context_t* context, const char*
|
||||
* @param context The context object.
|
||||
* @param chat_id If set to a group-chat-id,
|
||||
* the Verified-Group-Invite protocol is offered in the QR code;
|
||||
* works for protected groups as well as for normal groups.
|
||||
* works for verified groups as well as for normal groups.
|
||||
* If set to 0, the Setup-Contact protocol is offered in the QR code.
|
||||
* See https://countermitm.readthedocs.io/en/latest/new.html
|
||||
* for details about both protocols.
|
||||
@@ -2007,7 +2003,7 @@ char* dc_get_securejoin_qr (dc_context_t* context, uint32_t ch
|
||||
* When the protocol has finished, an info-message is added to that chat.
|
||||
* - If the given QR code starts the Verified-Group-Invite protocol,
|
||||
* the function waits until the protocol has finished.
|
||||
* This is because the protected group is not opportunistic
|
||||
* This is because the verified group is not opportunistic
|
||||
* and can be created only when the contacts have verified each other.
|
||||
*
|
||||
* See https://countermitm.readthedocs.io/en/latest/new.html
|
||||
@@ -2019,8 +2015,8 @@ char* dc_get_securejoin_qr (dc_context_t* context, uint32_t ch
|
||||
* to dc_check_qr().
|
||||
* @return Chat-id of the joined chat, the UI may redirect to the this chat.
|
||||
* If the out-of-band verification failed or was aborted, 0 is returned.
|
||||
* A returned chat-id does not guarantee that the chat is protected or the belonging contact is verified.
|
||||
* If needed, this be checked with dc_chat_is_protected() and dc_contact_is_verified(),
|
||||
* A returned chat-id does not guarantee that the chat or the belonging contact is verified.
|
||||
* If needed, this be checked with dc_chat_is_verified() and dc_contact_is_verified(),
|
||||
* however, in practise, the UI will just listen to #DC_EVENT_CONTACTS_CHANGED unconditionally.
|
||||
*/
|
||||
uint32_t dc_join_securejoin (dc_context_t* context, const char* qr);
|
||||
@@ -2773,6 +2769,7 @@ char* dc_chat_get_info_json (dc_context_t* context, size_t chat
|
||||
#define DC_CHAT_TYPE_UNDEFINED 0
|
||||
#define DC_CHAT_TYPE_SINGLE 100
|
||||
#define DC_CHAT_TYPE_GROUP 120
|
||||
#define DC_CHAT_TYPE_VERIFIED_GROUP 130
|
||||
|
||||
|
||||
/**
|
||||
@@ -2813,6 +2810,9 @@ uint32_t dc_chat_get_id (const dc_chat_t* chat);
|
||||
* - DC_CHAT_TYPE_GROUP (120) - a group chat, chats_contacts contain all group
|
||||
* members, incl. DC_CONTACT_ID_SELF
|
||||
*
|
||||
* - DC_CHAT_TYPE_VERIFIED_GROUP (130) - a verified group chat. In verified groups,
|
||||
* all members are verified and encryption is always active and cannot be disabled.
|
||||
*
|
||||
* @memberof dc_chat_t
|
||||
* @param chat The chat object.
|
||||
* @return Chat type.
|
||||
@@ -2942,16 +2942,15 @@ int dc_chat_can_send (const dc_chat_t* chat);
|
||||
|
||||
|
||||
/**
|
||||
* Check if a chat is protected.
|
||||
* Protected chats contain only verified members and encryption is always enabled.
|
||||
* Protected chats are created using dc_create_group_chat() by setting the 'protect' parameter to 1.
|
||||
* The status can be changed using dc_set_chat_protection().
|
||||
* Check if a chat is verified. Verified chats contain only verified members
|
||||
* and encryption is alwasy enabled. Verified chats are created using
|
||||
* dc_create_group_chat() by setting the 'verified' parameter to true.
|
||||
*
|
||||
* @memberof dc_chat_t
|
||||
* @param chat The chat object.
|
||||
* @return 1=chat protected, 0=chat is not protected
|
||||
* @return 1=chat verified, 0=chat is not verified
|
||||
*/
|
||||
int dc_chat_is_protected (const dc_chat_t* chat);
|
||||
int dc_chat_is_verified (const dc_chat_t* chat);
|
||||
|
||||
|
||||
/**
|
||||
@@ -3445,8 +3444,7 @@ int dc_msg_is_forwarded (const dc_msg_t* msg);
|
||||
/**
|
||||
* Check if the message is an informational message, created by the
|
||||
* device or by another users. Such messages are not "typed" by the user but
|
||||
* created due to other actions,
|
||||
* eg. dc_set_chat_name(), dc_set_chat_profile_image(), dc_set_chat_protection()
|
||||
* created due to other actions, eg. dc_set_chat_name(), dc_set_chat_profile_image()
|
||||
* or dc_add_contact_to_chat().
|
||||
*
|
||||
* These messages are typically shown in the center of the chat view,
|
||||
@@ -3462,32 +3460,6 @@ int dc_msg_is_forwarded (const dc_msg_t* msg);
|
||||
int dc_msg_is_info (const dc_msg_t* msg);
|
||||
|
||||
|
||||
/**
|
||||
* Get the type of an informational message.
|
||||
* If dc_msg_is_info() returns 1, this function returns the type of the informational message.
|
||||
* UIs can display eg. an icon based upon the type.
|
||||
*
|
||||
* Currently, the following types are defined:
|
||||
* - DC_INFO_PROTECTION_ENABLED (11) - Info-message for "Chat is now protected"
|
||||
* - DC_INFO_PROTECTION_DISABLED (12) - Info-message for "Chat is no longer protected"
|
||||
*
|
||||
* Even when you display an icon,
|
||||
* you should still display the text of the informational message using dc_msg_get_text()
|
||||
*
|
||||
* @memberof dc_msg_t
|
||||
* @param msg The message object.
|
||||
* @return One of the DC_INFO* constants.
|
||||
* 0 or other values indicate unspecified types
|
||||
* or that the message is not an info-message.
|
||||
*/
|
||||
int dc_msg_get_info_type (const dc_msg_t* msg);
|
||||
|
||||
|
||||
// DC_INFO* uses the same values as SystemMessage in rust-land
|
||||
#define DC_INFO_PROTECTION_ENABLED 11
|
||||
#define DC_INFO_PROTECTION_DISABLED 12
|
||||
|
||||
|
||||
/**
|
||||
* Check if a message is still in creation. A message is in creation between
|
||||
* the calls to dc_prepare_msg() and dc_send_msg().
|
||||
@@ -4985,9 +4957,8 @@ void dc_event_unref(dc_event_t* event);
|
||||
#define DC_STR_BAD_TIME_MSG_BODY 85
|
||||
#define DC_STR_UPDATE_REMINDER_MSG_BODY 86
|
||||
#define DC_STR_ERROR_NO_NETWORK 87
|
||||
#define DC_STR_PROTECTION_ENABLED 88
|
||||
#define DC_STR_PROTECTION_DISABLED 89
|
||||
#define DC_STR_REPLY_NOUN 90 /* eg. "Reply", used in summaries, a noun, not a verb (not: "to reply") */
|
||||
|
||||
#define DC_STR_COUNT 87
|
||||
|
||||
/*
|
||||
* @}
|
||||
|
||||
@@ -25,7 +25,7 @@ use async_std::task::{block_on, spawn};
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
|
||||
use deltachat::accounts::Accounts;
|
||||
use deltachat::chat::{ChatId, ChatVisibility, MuteDuration, ProtectionStatus};
|
||||
use deltachat::chat::{ChatId, ChatVisibility, MuteDuration};
|
||||
use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
|
||||
use deltachat::contact::{Contact, Origin};
|
||||
use deltachat::context::Context;
|
||||
@@ -807,6 +807,21 @@ pub unsafe extern "C" fn dc_add_device_msg(
|
||||
.to_u32()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_update_device_chats(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_update_device_chats()");
|
||||
return;
|
||||
}
|
||||
let ctx = &mut *context;
|
||||
|
||||
block_on(async move {
|
||||
ctx.update_device_chats()
|
||||
.await
|
||||
.unwrap_or_log_default(&ctx, "Failed to add device message")
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_was_device_msg_ever_added(
|
||||
context: *mut dc_context_t,
|
||||
@@ -1042,32 +1057,6 @@ pub unsafe extern "C" fn dc_get_next_media(
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_set_chat_protection(
|
||||
context: *mut dc_context_t,
|
||||
chat_id: u32,
|
||||
protect: libc::c_int,
|
||||
) -> libc::c_int {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_set_chat_protection()");
|
||||
return 0;
|
||||
}
|
||||
let ctx = &*context;
|
||||
let protect = if let Some(s) = ProtectionStatus::from_i32(protect) {
|
||||
s
|
||||
} else {
|
||||
warn!(ctx, "bad protect-value for dc_set_chat_protection()");
|
||||
return 0;
|
||||
};
|
||||
|
||||
block_on(async move {
|
||||
match ChatId::new(chat_id).set_protection(&ctx, protect).await {
|
||||
Ok(()) => 1,
|
||||
Err(_) => 0,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_set_chat_visibility(
|
||||
context: *mut dc_context_t,
|
||||
@@ -1181,7 +1170,7 @@ pub unsafe extern "C" fn dc_get_chat(context: *mut dc_context_t, chat_id: u32) -
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_create_group_chat(
|
||||
context: *mut dc_context_t,
|
||||
protect: libc::c_int,
|
||||
verified: libc::c_int,
|
||||
name: *const libc::c_char,
|
||||
) -> u32 {
|
||||
if context.is_null() || name.is_null() {
|
||||
@@ -1189,15 +1178,14 @@ pub unsafe extern "C" fn dc_create_group_chat(
|
||||
return 0;
|
||||
}
|
||||
let ctx = &*context;
|
||||
let protect = if let Some(s) = ProtectionStatus::from_i32(protect) {
|
||||
let verified = if let Some(s) = contact::VerifiedStatus::from_i32(verified) {
|
||||
s
|
||||
} else {
|
||||
warn!(ctx, "bad protect-value for dc_create_group_chat()");
|
||||
return 0;
|
||||
};
|
||||
|
||||
block_on(async move {
|
||||
chat::create_group_chat(&ctx, protect, to_string_lossy(name))
|
||||
chat::create_group_chat(&ctx, verified, to_string_lossy(name))
|
||||
.await
|
||||
.log_err(ctx, "Failed to create group chat")
|
||||
.map(|id| id.to_u32())
|
||||
@@ -2401,13 +2389,13 @@ pub unsafe extern "C" fn dc_chat_can_send(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_is_protected(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
if chat.is_null() {
|
||||
eprintln!("ignoring careless call to dc_chat_is_protected()");
|
||||
eprintln!("ignoring careless call to dc_chat_is_verified()");
|
||||
return 0;
|
||||
}
|
||||
let ffi_chat = &*chat;
|
||||
ffi_chat.chat.is_protected() as libc::c_int
|
||||
ffi_chat.chat.is_verified() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -2829,16 +2817,6 @@ pub unsafe extern "C" fn dc_msg_is_info(msg: *mut dc_msg_t) -> libc::c_int {
|
||||
ffi_msg.message.is_info().into()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_msg_get_info_type(msg: *mut dc_msg_t) -> libc::c_int {
|
||||
if msg.is_null() {
|
||||
eprintln!("ignoring careless call to dc_msg_get_info_type()");
|
||||
return 0;
|
||||
}
|
||||
let ffi_msg = &*msg;
|
||||
ffi_msg.message.get_info_type() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_msg_is_increation(msg: *mut dc_msg_t) -> libc::c_int {
|
||||
if msg.is_null() {
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::str::FromStr;
|
||||
|
||||
use anyhow::{bail, ensure};
|
||||
use async_std::path::Path;
|
||||
use deltachat::chat::{self, Chat, ChatId, ChatItem, ChatVisibility, ProtectionStatus};
|
||||
use deltachat::chat::{self, Chat, ChatId, ChatItem, ChatVisibility};
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::contact::*;
|
||||
@@ -357,7 +357,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
createchat <contact-id>\n\
|
||||
createchatbymsg <msg-id>\n\
|
||||
creategroup <name>\n\
|
||||
createprotected <name>\n\
|
||||
createverified <name>\n\
|
||||
addmember <contact-id>\n\
|
||||
removemember <contact-id>\n\
|
||||
groupname <name>\n\
|
||||
@@ -379,8 +379,6 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
unarchive <chat-id>\n\
|
||||
pin <chat-id>\n\
|
||||
unpin <chat-id>\n\
|
||||
protect <chat-id>\n\
|
||||
unprotect <chat-id>\n\
|
||||
delchat <chat-id>\n\
|
||||
===========================Message commands==\n\
|
||||
listmsgs <query>\n\
|
||||
@@ -525,7 +523,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
for i in (0..cnt).rev() {
|
||||
let chat = Chat::load_from_db(&context, chatlist.get_chat_id(i)).await?;
|
||||
println!(
|
||||
"{}#{}: {} [{} fresh] {}{}",
|
||||
"{}#{}: {} [{} fresh] {}",
|
||||
chat_prefix(&chat),
|
||||
chat.get_id(),
|
||||
chat.get_name(),
|
||||
@@ -535,7 +533,6 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
ChatVisibility::Archived => "📦",
|
||||
ChatVisibility::Pinned => "📌",
|
||||
},
|
||||
if chat.is_protected() { "🛡️" } else { "" },
|
||||
);
|
||||
let lot = chatlist.get_summary(&context, i, Some(&chat)).await;
|
||||
let statestr = if chat.visibility == ChatVisibility::Archived {
|
||||
@@ -610,7 +607,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
format!("{} member(s)", members.len())
|
||||
};
|
||||
println!(
|
||||
"{}#{}: {} [{}]{}{} {}",
|
||||
"{}#{}: {} [{}]{}{}",
|
||||
chat_prefix(sel_chat),
|
||||
sel_chat.get_id(),
|
||||
sel_chat.get_name(),
|
||||
@@ -627,11 +624,6 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
},
|
||||
_ => "".to_string(),
|
||||
},
|
||||
if sel_chat.is_protected() {
|
||||
"🛡️"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
);
|
||||
log_msglist(&context, &msglist).await?;
|
||||
if let Some(draft) = sel_chat.get_id().get_draft(&context).await? {
|
||||
@@ -662,16 +654,15 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
"creategroup" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
||||
let chat_id =
|
||||
chat::create_group_chat(&context, ProtectionStatus::Unprotected, arg1).await?;
|
||||
chat::create_group_chat(&context, VerifiedStatus::Unverified, arg1).await?;
|
||||
|
||||
println!("Group#{} created successfully.", chat_id);
|
||||
}
|
||||
"createprotected" => {
|
||||
"createverified" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
||||
let chat_id =
|
||||
chat::create_group_chat(&context, ProtectionStatus::Protected, arg1).await?;
|
||||
let chat_id = chat::create_group_chat(&context, VerifiedStatus::Verified, arg1).await?;
|
||||
|
||||
println!("Group#{} created and protected successfully.", chat_id);
|
||||
println!("VerifiedGroup#{} created successfully.", chat_id);
|
||||
}
|
||||
"addmember" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected");
|
||||
@@ -881,6 +872,9 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
msg.set_text(Some(arg1.to_string()));
|
||||
chat::add_device_msg(&context, None, Some(&mut msg)).await?;
|
||||
}
|
||||
"updatedevicechats" => {
|
||||
context.update_device_chats().await?;
|
||||
}
|
||||
"listmedia" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
@@ -912,21 +906,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
"archive" => ChatVisibility::Archived,
|
||||
"unarchive" | "unpin" => ChatVisibility::Normal,
|
||||
"pin" => ChatVisibility::Pinned,
|
||||
_ => unreachable!("arg0={:?}", arg0),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
"protect" | "unprotect" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||
let chat_id = ChatId::new(arg1.parse()?);
|
||||
chat_id
|
||||
.set_protection(
|
||||
&context,
|
||||
match arg0 {
|
||||
"protect" => ProtectionStatus::Protected,
|
||||
"unprotect" => ProtectionStatus::Unprotected,
|
||||
_ => unreachable!("arg0={:?}", arg0),
|
||||
_ => panic!("Unexpected command (This should never happen)"),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -77,7 +77,6 @@ def run_cmdline(argv=None, account_plugins=None):
|
||||
ac.set_config("mvbox_move", "0")
|
||||
ac.set_config("mvbox_watch", "0")
|
||||
ac.set_config("sentbox_watch", "0")
|
||||
ac.set_config("bot", "1")
|
||||
configtracker = ac.configure()
|
||||
configtracker.wait_finish()
|
||||
|
||||
|
||||
@@ -263,17 +263,6 @@ class Account(object):
|
||||
"""
|
||||
return Contact(self, contact_id)
|
||||
|
||||
def get_blocked_contacts(self):
|
||||
""" return a list of all blocked contacts.
|
||||
|
||||
:returns: list of :class:`deltachat.contact.Contact` objects.
|
||||
"""
|
||||
dc_array = ffi.gc(
|
||||
lib.dc_get_blocked_contacts(self._dc_context),
|
||||
lib.dc_array_unref
|
||||
)
|
||||
return list(iter_array(dc_array, lambda x: Contact(self, x)))
|
||||
|
||||
def get_contacts(self, query=None, with_self=False, only_verified=False):
|
||||
""" get a (filtered) list of contacts.
|
||||
|
||||
@@ -347,9 +336,6 @@ class Account(object):
|
||||
def get_deaddrop_chat(self):
|
||||
return Chat(self, const.DC_CHAT_ID_DEADDROP)
|
||||
|
||||
def get_device_chat(self):
|
||||
return Contact(self, const.DC_CONTACT_ID_DEVICE).create_chat()
|
||||
|
||||
def get_message_by_id(self, msg_id):
|
||||
""" return Message instance.
|
||||
:param msg_id: integer id of this message.
|
||||
|
||||
@@ -57,7 +57,10 @@ class Chat(object):
|
||||
|
||||
:returns: True if chat is a group-chat, false if it's a contact 1:1 chat.
|
||||
"""
|
||||
return lib.dc_chat_get_type(self._dc_chat) == const.DC_CHAT_TYPE_GROUP
|
||||
return lib.dc_chat_get_type(self._dc_chat) in (
|
||||
const.DC_CHAT_TYPE_GROUP,
|
||||
const.DC_CHAT_TYPE_VERIFIED_GROUP
|
||||
)
|
||||
|
||||
def is_deaddrop(self):
|
||||
""" return true if this chat is a deaddrop chat.
|
||||
@@ -82,20 +85,12 @@ class Chat(object):
|
||||
"""
|
||||
return not lib.dc_chat_is_unpromoted(self._dc_chat)
|
||||
|
||||
def can_send(self):
|
||||
"""Check if messages can be sent to a give chat.
|
||||
This is not true eg. for the deaddrop or for the device-talk
|
||||
def is_verified(self):
|
||||
""" return True if this chat is a verified group.
|
||||
|
||||
:returns: True if the chat is writable, False otherwise
|
||||
:returns: True if chat is verified, False otherwise.
|
||||
"""
|
||||
return lib.dc_chat_can_send(self._dc_chat)
|
||||
|
||||
def is_protected(self):
|
||||
""" return True if this chat is a protected chat.
|
||||
|
||||
:returns: True if chat is protected, False otherwise.
|
||||
"""
|
||||
return lib.dc_chat_is_protected(self._dc_chat)
|
||||
return lib.dc_chat_is_verified(self._dc_chat)
|
||||
|
||||
def get_name(self):
|
||||
""" return name of this chat.
|
||||
|
||||
@@ -52,17 +52,9 @@ class Contact(object):
|
||||
return lib.dc_contact_is_blocked(self._dc_contact)
|
||||
|
||||
def set_blocked(self, block=True):
|
||||
""" [Deprecated, use block/unblock methods] Block or unblock a contact. """
|
||||
""" Block or unblock a contact. """
|
||||
return lib.dc_block_contact(self.account._dc_context, self.id, block)
|
||||
|
||||
def block(self):
|
||||
""" Block this contact. Message will not be seen/retrieved from this contact. """
|
||||
return lib.dc_block_contact(self.account._dc_context, self.id, True)
|
||||
|
||||
def unblock(self):
|
||||
""" Unblock this contact. Messages from this contact will be retrieved (again)."""
|
||||
return lib.dc_block_contact(self.account._dc_context, self.id, False)
|
||||
|
||||
def is_verified(self):
|
||||
""" Return True if the contact is verified. """
|
||||
return lib.dc_contact_is_verified(self._dc_contact)
|
||||
|
||||
@@ -354,8 +354,6 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig, data):
|
||||
for acc in self._accounts:
|
||||
if hasattr(acc, "_configtracker"):
|
||||
acc._configtracker.wait_finish()
|
||||
acc._evtracker.consume_events()
|
||||
acc.get_device_chat().mark_noticed()
|
||||
del acc._configtracker
|
||||
acc.set_config("bcc_self", "0")
|
||||
if acc.is_configured() and not acc.is_started():
|
||||
|
||||
@@ -129,20 +129,6 @@ class TestOfflineContact:
|
||||
assert not contact1.is_blocked()
|
||||
assert not contact1.is_verified()
|
||||
|
||||
def test_get_blocked(self, acfactory):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
contact1 = ac1.create_contact("some1@example.org", name="some1")
|
||||
contact2 = ac1.create_contact("some2@example.org", name="some2")
|
||||
ac1.create_contact("some3@example.org", name="some3")
|
||||
assert ac1.get_blocked_contacts() == []
|
||||
contact1.block()
|
||||
assert ac1.get_blocked_contacts() == [contact1]
|
||||
contact2.block()
|
||||
blocked = ac1.get_blocked_contacts()
|
||||
assert len(blocked) == 2 and contact1 in blocked and contact2 in blocked
|
||||
contact2.unblock()
|
||||
assert ac1.get_blocked_contacts() == [contact1]
|
||||
|
||||
def test_create_self_contact(self, acfactory):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
contact1 = ac1.create_contact(ac1.get_config("addr"))
|
||||
@@ -1046,16 +1032,6 @@ class TestOnlineAccount:
|
||||
assert msg_in.text == text2
|
||||
assert ac1.get_config("addr") in [x.addr for x in msg_in.chat.get_contacts()]
|
||||
|
||||
def test_no_draft_if_cant_send(self, acfactory):
|
||||
"""Tests that no quote can be set if the user can't send to this chat"""
|
||||
ac1 = acfactory.get_one_online_account()
|
||||
device_chat = ac1.get_device_chat()
|
||||
msg = Message.new_empty(ac1, "text")
|
||||
device_chat.set_draft(msg)
|
||||
|
||||
assert not device_chat.can_send()
|
||||
assert device_chat.get_draft() is None
|
||||
|
||||
def test_prefer_encrypt(self, acfactory, lp):
|
||||
"""Test quorum rule for encryption preference in 1:1 and group chat."""
|
||||
ac1, ac2, ac3 = acfactory.get_many_online_accounts(3)
|
||||
@@ -1367,7 +1343,7 @@ class TestOnlineAccount:
|
||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||
lp.sec("ac1: create verified-group QR, ac2 scans and joins")
|
||||
chat1 = ac1.create_group_chat("hello", verified=True)
|
||||
assert chat1.is_protected()
|
||||
assert chat1.is_verified()
|
||||
qr = chat1.get_join_qr()
|
||||
lp.sec("ac2: start QR-code based join-group protocol")
|
||||
chat2 = ac2.qr_join_chat(qr)
|
||||
@@ -1386,7 +1362,7 @@ class TestOnlineAccount:
|
||||
lp.sec("ac2: read message and check it's verified chat")
|
||||
msg = ac2._evtracker.wait_next_incoming_message()
|
||||
assert msg.text == "hello"
|
||||
assert msg.chat.is_protected()
|
||||
assert msg.chat.is_verified()
|
||||
assert msg.is_encrypted()
|
||||
|
||||
lp.sec("ac2: send message and let ac1 read it")
|
||||
|
||||
2
spec.md
2
spec.md
@@ -32,7 +32,7 @@ Messages SHOULD be encrypted by the
|
||||
`prefer-encrypt=mutual` MAY be set by default.
|
||||
|
||||
Meta data (at least the subject and all chat-headers) SHOULD be encrypted
|
||||
by the [Protected Headers](https://tools.ietf.org/id/draft-autocrypt-lamps-protected-headers-02.html) standard.
|
||||
by the [Protected Headers](https://www.ietf.org/id/draft-autocrypt-lamps-protected-headers-02.html) standard.
|
||||
|
||||
|
||||
# Outgoing messages
|
||||
|
||||
357
src/chat.rs
357
src/chat.rs
@@ -1,6 +1,5 @@
|
||||
//! # Chat module
|
||||
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use std::convert::TryFrom;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
@@ -46,33 +45,6 @@ pub enum ChatItem {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Display,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
FromPrimitive,
|
||||
ToPrimitive,
|
||||
FromSql,
|
||||
ToSql,
|
||||
IntoStaticStr,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum ProtectionStatus {
|
||||
Unprotected = 0,
|
||||
Protected = 1,
|
||||
}
|
||||
|
||||
impl Default for ProtectionStatus {
|
||||
fn default() -> Self {
|
||||
ProtectionStatus::Unprotected
|
||||
}
|
||||
}
|
||||
|
||||
/// Chat ID, including reserved IDs.
|
||||
///
|
||||
/// Some chat IDs are reserved to identify special chat types. This
|
||||
@@ -174,109 +146,6 @@ impl ChatId {
|
||||
self.set_blocked(context, Blocked::Not).await;
|
||||
}
|
||||
|
||||
/// Sets protection without sending a message.
|
||||
///
|
||||
/// Used when a message arrives indicating that someone else has
|
||||
/// changed the protection value for a chat.
|
||||
pub(crate) async fn inner_set_protection(
|
||||
self,
|
||||
context: &Context,
|
||||
protect: ProtectionStatus,
|
||||
) -> Result<(), Error> {
|
||||
ensure!(!self.is_special(), "Invalid chat-id.");
|
||||
|
||||
let chat = Chat::load_from_db(context, self).await?;
|
||||
|
||||
if protect == chat.protected {
|
||||
info!(context, "Protection status unchanged for {}.", self);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match protect {
|
||||
ProtectionStatus::Protected => match chat.typ {
|
||||
Chattype::Single | Chattype::Group => {
|
||||
let contact_ids = get_chat_contacts(context, self).await;
|
||||
for contact_id in contact_ids.into_iter() {
|
||||
let contact = Contact::get_by_id(context, contact_id).await?;
|
||||
if contact.is_verified(context).await != VerifiedStatus::BidirectVerified {
|
||||
bail!("{} is not verified.", contact.get_display_name());
|
||||
}
|
||||
}
|
||||
}
|
||||
Chattype::Undefined => bail!("Undefined group type"),
|
||||
},
|
||||
ProtectionStatus::Unprotected => {}
|
||||
};
|
||||
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE chats SET protected=? WHERE id=?;",
|
||||
paramsv![protect, self],
|
||||
)
|
||||
.await?;
|
||||
|
||||
context.emit_event(EventType::ChatModified(self));
|
||||
|
||||
// make sure, the receivers will get all keys
|
||||
reset_gossiped_timestamp(context, self).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send protected status message to the chat.
|
||||
///
|
||||
/// This sends the message with the protected status change to the chat,
|
||||
/// notifying the user on this device as well as the other users in the chat.
|
||||
///
|
||||
/// If `promote` is false this means, the message must not be sent out
|
||||
/// and only a local info message should be added to the chat.
|
||||
/// This is used when protection is enabled implicitly or when a chat is not yet promoted.
|
||||
pub(crate) async fn add_protection_msg(
|
||||
self,
|
||||
context: &Context,
|
||||
protect: ProtectionStatus,
|
||||
promote: bool,
|
||||
from_id: u32,
|
||||
) -> Result<(), Error> {
|
||||
let msg_text = context.stock_protection_msg(protect, from_id).await;
|
||||
let cmd = match protect {
|
||||
ProtectionStatus::Protected => SystemMessage::ChatProtectionEnabled,
|
||||
ProtectionStatus::Unprotected => SystemMessage::ChatProtectionDisabled,
|
||||
};
|
||||
|
||||
if promote {
|
||||
let mut msg = Message::default();
|
||||
msg.viewtype = Viewtype::Text;
|
||||
msg.text = Some(msg_text);
|
||||
msg.param.set_cmd(cmd);
|
||||
send_msg(context, self, &mut msg).await?;
|
||||
} else {
|
||||
add_info_msg_with_cmd(context, self, msg_text, cmd).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets protection and sends or adds a message.
|
||||
pub async fn set_protection(
|
||||
self,
|
||||
context: &Context,
|
||||
protect: ProtectionStatus,
|
||||
) -> Result<(), Error> {
|
||||
ensure!(!self.is_special(), "set protection: invalid chat-id.");
|
||||
|
||||
let chat = Chat::load_from_db(context, self).await?;
|
||||
|
||||
if let Err(e) = self.inner_set_protection(context, protect).await {
|
||||
error!(context, "Cannot set protection: {}", e); // make error user-visible
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
self.add_protection_msg(context, protect, chat.is_promoted(), DC_CONTACT_ID_SELF)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Archives or unarchives a chat.
|
||||
pub async fn set_visibility(
|
||||
self,
|
||||
@@ -460,12 +329,6 @@ impl ChatId {
|
||||
msg.param.set(Param::File, blob.as_name());
|
||||
}
|
||||
}
|
||||
|
||||
let chat = Chat::load_from_db(context, self).await?;
|
||||
if !chat.can_send() {
|
||||
bail!("Can't set a draft: Can't send");
|
||||
}
|
||||
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
@@ -675,7 +538,6 @@ pub struct Chat {
|
||||
pub param: Params,
|
||||
is_sending_locations: bool,
|
||||
pub mute_duration: MuteDuration,
|
||||
protected: ProtectionStatus,
|
||||
}
|
||||
|
||||
impl Chat {
|
||||
@@ -685,7 +547,7 @@ impl Chat {
|
||||
.sql
|
||||
.query_row(
|
||||
"SELECT c.type, c.name, c.grpid, c.param, c.archived,
|
||||
c.blocked, c.locations_send_until, c.muted_until, c.protected
|
||||
c.blocked, c.locations_send_until, c.muted_until
|
||||
FROM chats c
|
||||
WHERE c.id=?;",
|
||||
paramsv![chat_id],
|
||||
@@ -700,7 +562,6 @@ impl Chat {
|
||||
blocked: row.get::<_, Option<_>>(5)?.unwrap_or_default(),
|
||||
is_sending_locations: row.get(6)?,
|
||||
mute_duration: row.get(7)?,
|
||||
protected: row.get(8)?,
|
||||
};
|
||||
Ok(c)
|
||||
},
|
||||
@@ -866,9 +727,9 @@ impl Chat {
|
||||
!self.is_unpromoted()
|
||||
}
|
||||
|
||||
/// Returns true if chat protection is enabled.
|
||||
pub fn is_protected(&self) -> bool {
|
||||
self.protected == ProtectionStatus::Protected
|
||||
/// Returns true if chat is a verified group chat.
|
||||
pub fn is_verified(&self) -> bool {
|
||||
self.typ == Chattype::VerifiedGroup
|
||||
}
|
||||
|
||||
/// Returns true if location streaming is enabled in the chat.
|
||||
@@ -895,12 +756,15 @@ impl Chat {
|
||||
let mut to_id = 0;
|
||||
let mut location_id = 0;
|
||||
|
||||
if !(self.typ == Chattype::Single || self.typ == Chattype::Group) {
|
||||
if !(self.typ == Chattype::Single
|
||||
|| self.typ == Chattype::Group
|
||||
|| self.typ == Chattype::VerifiedGroup)
|
||||
{
|
||||
error!(context, "Cannot send to chat type #{}.", self.typ,);
|
||||
bail!("Cannot set to chat type #{}", self.typ);
|
||||
}
|
||||
|
||||
if self.typ == Chattype::Group
|
||||
if (self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup)
|
||||
&& !is_contact_in_chat(context, self.id, DC_CONTACT_ID_SELF).await
|
||||
{
|
||||
emit_event!(
|
||||
@@ -917,7 +781,7 @@ impl Chat {
|
||||
|
||||
let new_rfc724_mid = {
|
||||
let grpid = match self.typ {
|
||||
Chattype::Group => Some(self.grpid.as_str()),
|
||||
Chattype::Group | Chattype::VerifiedGroup => Some(self.grpid.as_str()),
|
||||
_ => None,
|
||||
};
|
||||
dc_create_outgoing_rfc724_mid(grpid, &from)
|
||||
@@ -941,7 +805,7 @@ impl Chat {
|
||||
);
|
||||
bail!("Cannot set message, contact for {} not found.", self.id);
|
||||
}
|
||||
} else if self.typ == Chattype::Group
|
||||
} else if (self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup)
|
||||
&& self.param.get_int(Param::Unpromoted).unwrap_or_default() == 1
|
||||
{
|
||||
msg.param.set_int(Param::AttachGroupImage, 1);
|
||||
@@ -1136,7 +1000,7 @@ pub struct ChatInfo {
|
||||
///
|
||||
/// On the C API this number is one of the
|
||||
/// `DC_CHAT_TYPE_UNDEFINED`, `DC_CHAT_TYPE_SINGLE`,
|
||||
/// or `DC_CHAT_TYPE_GROUP`
|
||||
/// `DC_CHAT_TYPE_GROUP` or `DC_CHAT_TYPE_VERIFIED_GROUP`
|
||||
/// constants.
|
||||
#[serde(rename = "type")]
|
||||
pub type_: u32,
|
||||
@@ -2001,7 +1865,7 @@ pub async fn get_chat_contacts(context: &Context, chat_id: ChatId) -> Vec<u32> {
|
||||
|
||||
pub async fn create_group_chat(
|
||||
context: &Context,
|
||||
protect: ProtectionStatus,
|
||||
verified: VerifiedStatus,
|
||||
chat_name: impl AsRef<str>,
|
||||
) -> Result<ChatId, Error> {
|
||||
let chat_name = improve_single_line_input(chat_name);
|
||||
@@ -2015,7 +1879,11 @@ pub async fn create_group_chat(
|
||||
context.sql.execute(
|
||||
"INSERT INTO chats (type, name, grpid, param, created_timestamp) VALUES(?, ?, ?, \'U=1\', ?);",
|
||||
paramsv![
|
||||
Chattype::Group,
|
||||
if verified != VerifiedStatus::Unverified {
|
||||
Chattype::VerifiedGroup
|
||||
} else {
|
||||
Chattype::Group
|
||||
},
|
||||
chat_name,
|
||||
grpid,
|
||||
time(),
|
||||
@@ -2039,12 +1907,6 @@ pub async fn create_group_chat(
|
||||
chat_id: ChatId::new(0),
|
||||
});
|
||||
|
||||
if protect == ProtectionStatus::Protected {
|
||||
// this part is to stay compatible to verified groups,
|
||||
// in some future, we will drop the "protect"-flag from create_group_chat()
|
||||
chat_id.inner_set_protection(context, protect).await?;
|
||||
}
|
||||
|
||||
Ok(chat_id)
|
||||
}
|
||||
|
||||
@@ -2170,12 +2032,12 @@ pub(crate) async fn add_contact_to_chat_ex(
|
||||
}
|
||||
} else {
|
||||
// else continue and send status mail
|
||||
if chat.is_protected()
|
||||
if chat.typ == Chattype::VerifiedGroup
|
||||
&& contact.is_verified(context).await != VerifiedStatus::BidirectVerified
|
||||
{
|
||||
error!(
|
||||
context,
|
||||
"Only bidirectional verified contacts can be added to protected chats."
|
||||
"Only bidirectional verified contacts can be added to verified groups."
|
||||
);
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -2213,7 +2075,7 @@ async fn real_group_exists(context: &Context, chat_id: ChatId) -> bool {
|
||||
context
|
||||
.sql
|
||||
.exists(
|
||||
"SELECT id FROM chats WHERE id=? AND type=120;",
|
||||
"SELECT id FROM chats WHERE id=? AND (type=120 OR type=130);",
|
||||
paramsv![chat_id],
|
||||
)
|
||||
.await
|
||||
@@ -2739,7 +2601,7 @@ pub(crate) async fn get_chat_cnt(context: &Context) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a tuple of `(chatid, is_protected, blocked)`.
|
||||
/// Returns a tuple of `(chatid, is_verified, blocked)`.
|
||||
pub(crate) async fn get_chat_id_by_grpid(
|
||||
context: &Context,
|
||||
grpid: impl AsRef<str>,
|
||||
@@ -2747,16 +2609,14 @@ pub(crate) async fn get_chat_id_by_grpid(
|
||||
context
|
||||
.sql
|
||||
.query_row(
|
||||
"SELECT id, blocked, protected FROM chats WHERE grpid=?;",
|
||||
"SELECT id, blocked, type FROM chats WHERE grpid=?;",
|
||||
paramsv![grpid.as_ref()],
|
||||
|row| {
|
||||
let chat_id = row.get::<_, ChatId>(0)?;
|
||||
|
||||
let b = row.get::<_, Option<Blocked>>(1)?.unwrap_or_default();
|
||||
let p = row
|
||||
.get::<_, Option<ProtectionStatus>>(2)?
|
||||
.unwrap_or_default();
|
||||
Ok((chat_id, p == ProtectionStatus::Protected, b))
|
||||
let v = row.get::<_, Option<Chattype>>(2)?.unwrap_or_default();
|
||||
Ok((chat_id, v == Chattype::VerifiedGroup, b))
|
||||
},
|
||||
)
|
||||
.await
|
||||
@@ -2909,22 +2769,18 @@ pub(crate) async fn delete_and_reset_all_device_msgs(context: &Context) -> Resul
|
||||
/// Adds an informational message to chat.
|
||||
///
|
||||
/// For example, it can be a message showing that a member was added to a group.
|
||||
pub(crate) async fn add_info_msg_with_cmd(
|
||||
context: &Context,
|
||||
chat_id: ChatId,
|
||||
text: impl AsRef<str>,
|
||||
cmd: SystemMessage,
|
||||
) -> Result<MsgId, Error> {
|
||||
pub(crate) async fn add_info_msg(context: &Context, chat_id: ChatId, text: impl AsRef<str>) {
|
||||
let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device");
|
||||
let ephemeral_timer = chat_id.get_ephemeral_timer(context).await?;
|
||||
let ephemeral_timer = match chat_id.get_ephemeral_timer(context).await {
|
||||
Err(e) => {
|
||||
warn!(context, "Could not get timer for info msg: {}", e);
|
||||
return;
|
||||
}
|
||||
Ok(ephemeral_timer) => ephemeral_timer,
|
||||
};
|
||||
|
||||
let mut param = Params::new();
|
||||
if cmd != SystemMessage::Unknown {
|
||||
param.set_cmd(cmd)
|
||||
}
|
||||
|
||||
context.sql.execute(
|
||||
"INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,rfc724_mid,ephemeral_timer, param) VALUES (?,?,?, ?,?,?, ?,?,?, ?);",
|
||||
if let Err(e) = context.sql.execute(
|
||||
"INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,rfc724_mid,ephemeral_timer) VALUES (?,?,?, ?,?,?, ?,?,?);",
|
||||
paramsv![
|
||||
chat_id,
|
||||
DC_CONTACT_ID_INFO,
|
||||
@@ -2934,25 +2790,22 @@ pub(crate) async fn add_info_msg_with_cmd(
|
||||
MessageState::InNoticed,
|
||||
text.as_ref().to_string(),
|
||||
rfc724_mid,
|
||||
ephemeral_timer,
|
||||
param.to_string(),
|
||||
ephemeral_timer
|
||||
]
|
||||
).await?;
|
||||
).await {
|
||||
warn!(context, "Could not add info msg: {}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
let row_id = context
|
||||
.sql
|
||||
.get_rowid(context, "msgs", "rfc724_mid", &rfc724_mid)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
let msg_id = MsgId::new(row_id);
|
||||
context.emit_event(EventType::MsgsChanged { chat_id, msg_id });
|
||||
Ok(msg_id)
|
||||
}
|
||||
|
||||
pub(crate) async fn add_info_msg(context: &Context, chat_id: ChatId, text: impl AsRef<str>) {
|
||||
if let Err(e) = add_info_msg_with_cmd(context, chat_id, text, SystemMessage::Unknown).await {
|
||||
warn!(context, "Could not add info msg: {}", e);
|
||||
}
|
||||
context.emit_event(EventType::MsgsChanged {
|
||||
chat_id,
|
||||
msg_id: MsgId::new(row_id),
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -3045,7 +2898,7 @@ mod tests {
|
||||
async fn test_add_contact_to_chat_ex_add_self() {
|
||||
// Adding self to a contact should succeed, even though it's pointless.
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "foo")
|
||||
let chat_id = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo")
|
||||
.await
|
||||
.unwrap();
|
||||
let added = add_contact_to_chat_ex(&t.ctx, chat_id, DC_CONTACT_ID_SELF, false)
|
||||
@@ -3431,7 +3284,7 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
async_std::task::sleep(std::time::Duration::from_millis(1000)).await;
|
||||
let chat_id3 = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "foo")
|
||||
let chat_id3 = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -3476,7 +3329,7 @@ mod tests {
|
||||
#[async_std::test]
|
||||
async fn test_set_chat_name() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "foo")
|
||||
let chat_id = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo")
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -3519,7 +3372,7 @@ mod tests {
|
||||
#[async_std::test]
|
||||
async fn test_shall_attach_selfavatar() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "foo")
|
||||
let chat_id = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo")
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!shall_attach_selfavatar(&t.ctx, chat_id).await.unwrap());
|
||||
@@ -3543,7 +3396,7 @@ mod tests {
|
||||
#[async_std::test]
|
||||
async fn test_set_mute_duration() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "foo")
|
||||
let chat_id = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo")
|
||||
.await
|
||||
.unwrap();
|
||||
// Initial
|
||||
@@ -3607,116 +3460,4 @@ mod tests {
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_add_info_msg() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "foo")
|
||||
.await
|
||||
.unwrap();
|
||||
add_info_msg(&t.ctx, chat_id, "foo info").await;
|
||||
|
||||
let msg = t.get_last_msg(chat_id).await;
|
||||
assert_eq!(msg.get_chat_id(), chat_id);
|
||||
assert_eq!(msg.get_viewtype(), Viewtype::Text);
|
||||
assert_eq!(msg.get_text().unwrap(), "foo info");
|
||||
assert!(msg.is_info());
|
||||
assert_eq!(msg.get_info_type(), SystemMessage::Unknown);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_add_info_msg_with_cmd() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "foo")
|
||||
.await
|
||||
.unwrap();
|
||||
let msg_id = add_info_msg_with_cmd(
|
||||
&t.ctx,
|
||||
chat_id,
|
||||
"foo bar info",
|
||||
SystemMessage::EphemeralTimerChanged,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap();
|
||||
assert_eq!(msg.get_chat_id(), chat_id);
|
||||
assert_eq!(msg.get_viewtype(), Viewtype::Text);
|
||||
assert_eq!(msg.get_text().unwrap(), "foo bar info");
|
||||
assert!(msg.is_info());
|
||||
assert_eq!(msg.get_info_type(), SystemMessage::EphemeralTimerChanged);
|
||||
|
||||
let msg2 = t.get_last_msg(chat_id).await;
|
||||
assert_eq!(msg.get_id(), msg2.get_id());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_set_protection() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let chat_id = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "foo")
|
||||
.await
|
||||
.unwrap();
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
|
||||
assert!(!chat.is_protected());
|
||||
assert!(chat.is_unpromoted());
|
||||
|
||||
// enable protection on unpromoted chat, the info-message is added via add_info_msg()
|
||||
chat_id
|
||||
.set_protection(&t.ctx, ProtectionStatus::Protected)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
|
||||
assert!(chat.is_protected());
|
||||
assert!(chat.is_unpromoted());
|
||||
|
||||
let msgs = get_chat_msgs(&t.ctx, chat_id, 0, None).await;
|
||||
assert_eq!(msgs.len(), 1);
|
||||
|
||||
let msg = t.get_last_msg(chat_id).await;
|
||||
assert!(msg.is_info());
|
||||
assert_eq!(msg.get_info_type(), SystemMessage::ChatProtectionEnabled);
|
||||
assert_eq!(msg.get_state(), MessageState::InNoticed);
|
||||
|
||||
// disable protection again, still unpromoted
|
||||
chat_id
|
||||
.set_protection(&t.ctx, ProtectionStatus::Unprotected)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
|
||||
assert!(!chat.is_protected());
|
||||
assert!(chat.is_unpromoted());
|
||||
|
||||
let msg = t.get_last_msg(chat_id).await;
|
||||
assert!(msg.is_info());
|
||||
assert_eq!(msg.get_info_type(), SystemMessage::ChatProtectionDisabled);
|
||||
assert_eq!(msg.get_state(), MessageState::InNoticed);
|
||||
|
||||
// send a message, this switches to promoted state
|
||||
send_text_msg(&t.ctx, chat_id, "hi!".to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
|
||||
assert!(!chat.is_protected());
|
||||
assert!(!chat.is_unpromoted());
|
||||
|
||||
let msgs = get_chat_msgs(&t.ctx, chat_id, 0, None).await;
|
||||
assert_eq!(msgs.len(), 3);
|
||||
|
||||
// enable protection on promoted chat, the info-message is sent via send_msg() this time
|
||||
chat_id
|
||||
.set_protection(&t.ctx, ProtectionStatus::Protected)
|
||||
.await
|
||||
.unwrap();
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id).await.unwrap();
|
||||
assert!(chat.is_protected());
|
||||
assert!(!chat.is_unpromoted());
|
||||
|
||||
let msg = t.get_last_msg(chat_id).await;
|
||||
assert!(msg.is_info());
|
||||
assert_eq!(msg.get_info_type(), SystemMessage::ChatProtectionEnabled);
|
||||
assert_eq!(msg.get_state(), MessageState::OutDelivered); // as bcc-self is disabled and there is nobody else in the chat
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,7 +362,9 @@ impl Chatlist {
|
||||
let mut lastcontact = None;
|
||||
|
||||
let lastmsg = if let Ok(lastmsg) = Message::load_from_db(context, lastmsg_id).await {
|
||||
if lastmsg.from_id != DC_CONTACT_ID_SELF && chat.typ == Chattype::Group {
|
||||
if lastmsg.from_id != DC_CONTACT_ID_SELF
|
||||
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
|
||||
{
|
||||
lastcontact = Contact::load_from_db(context, lastmsg.from_id).await.ok();
|
||||
}
|
||||
|
||||
@@ -438,13 +440,13 @@ mod tests {
|
||||
#[async_std::test]
|
||||
async fn test_try_load() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id1 = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "a chat")
|
||||
let chat_id1 = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "a chat")
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id2 = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "b chat")
|
||||
let chat_id2 = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "b chat")
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id3 = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "c chat")
|
||||
let chat_id3 = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "c chat")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -487,7 +489,7 @@ mod tests {
|
||||
async fn test_sort_self_talk_up_on_forward() {
|
||||
let t = TestContext::new().await;
|
||||
t.ctx.update_device_chats().await.unwrap();
|
||||
create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "a chat")
|
||||
create_group_chat(&t.ctx, VerifiedStatus::Unverified, "a chat")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -544,7 +546,7 @@ mod tests {
|
||||
#[async_std::test]
|
||||
async fn test_get_summary_unwrap() {
|
||||
let t = TestContext::new().await;
|
||||
let chat_id1 = create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "a chat")
|
||||
let chat_id1 = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "a chat")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -69,9 +69,6 @@ pub enum Config {
|
||||
#[strum(props(default = "0"))] // also change MediaQuality.default() on changes
|
||||
MediaQuality,
|
||||
|
||||
#[strum(props(default = "1"))]
|
||||
FetchExisting,
|
||||
|
||||
#[strum(props(default = "0"))]
|
||||
KeyGenType,
|
||||
|
||||
@@ -124,8 +121,6 @@ pub enum Config {
|
||||
#[strum(serialize = "sys.config_keys")]
|
||||
SysConfigKeys,
|
||||
|
||||
Bot,
|
||||
|
||||
/// Whether we send a warning if the password is wrong (set to false when we send a warning
|
||||
/// because we do not want to send a second warning)
|
||||
#[strum(props(default = "0"))]
|
||||
|
||||
@@ -163,9 +163,6 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
DC_LP_AUTH_NORMAL as i32
|
||||
};
|
||||
|
||||
//let ctx2 = ctx.clone();
|
||||
//let update_device_chats_handle = task::spawn(async move { ctx2.update_device_chats().await });
|
||||
|
||||
// Step 1: Load the parameters and check email-address and password
|
||||
|
||||
if oauth2 {
|
||||
@@ -217,32 +214,28 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
|
||||
progress!(ctx, 500);
|
||||
|
||||
let mut servers = param_autoconfig.unwrap_or_default();
|
||||
if !servers
|
||||
.iter()
|
||||
.any(|server| server.protocol == Protocol::IMAP)
|
||||
{
|
||||
servers.push(ServerParams {
|
||||
protocol: Protocol::IMAP,
|
||||
hostname: param.imap.server.clone(),
|
||||
port: param.imap.port,
|
||||
socket: param.imap.security,
|
||||
username: param.imap.user.clone(),
|
||||
})
|
||||
}
|
||||
if !servers
|
||||
.iter()
|
||||
.any(|server| server.protocol == Protocol::SMTP)
|
||||
{
|
||||
servers.push(ServerParams {
|
||||
protocol: Protocol::SMTP,
|
||||
hostname: param.smtp.server.clone(),
|
||||
port: param.smtp.port,
|
||||
socket: param.smtp.security,
|
||||
username: param.smtp.user.clone(),
|
||||
})
|
||||
}
|
||||
let servers = expand_param_vector(servers, ¶m.addr, ¶m_domain);
|
||||
let servers = expand_param_vector(
|
||||
param_autoconfig.unwrap_or_else(|| {
|
||||
vec![
|
||||
ServerParams {
|
||||
protocol: Protocol::IMAP,
|
||||
hostname: param.imap.server.clone(),
|
||||
port: param.imap.port,
|
||||
socket: param.imap.security,
|
||||
username: param.imap.user.clone(),
|
||||
},
|
||||
ServerParams {
|
||||
protocol: Protocol::SMTP,
|
||||
hostname: param.smtp.server.clone(),
|
||||
port: param.smtp.port,
|
||||
socket: param.smtp.security,
|
||||
username: param.smtp.user.clone(),
|
||||
},
|
||||
]
|
||||
}),
|
||||
¶m.addr,
|
||||
¶m_domain,
|
||||
);
|
||||
|
||||
progress!(ctx, 550);
|
||||
|
||||
@@ -364,7 +357,6 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
.await;
|
||||
|
||||
progress!(ctx, 940);
|
||||
//update_device_chats_handle.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -564,9 +556,7 @@ async fn nicer_configuration_error(context: &Context, errors: Vec<ConfigurationE
|
||||
let first_err = if let Some(f) = errors.first() {
|
||||
f
|
||||
} else {
|
||||
// This means configuration failed but no errors have been captured. This should never
|
||||
// happen, but if it does, the user will see classic "Error: no error".
|
||||
return "no error".to_string();
|
||||
return "".to_string();
|
||||
};
|
||||
|
||||
if errors
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
//! # Constants
|
||||
use deltachat_derive::*;
|
||||
use once_cell::sync::Lazy;
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub static DC_VERSION_STR: Lazy<String> = Lazy::new(|| env!("CARGO_PKG_VERSION").to_string());
|
||||
lazy_static! {
|
||||
pub static ref DC_VERSION_STR: String = env!("CARGO_PKG_VERSION").to_string();
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
@@ -143,6 +145,7 @@ pub enum Chattype {
|
||||
Undefined = 0,
|
||||
Single = 100,
|
||||
Group = 120,
|
||||
VerifiedGroup = 130,
|
||||
}
|
||||
|
||||
impl Default for Chattype {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use async_std::path::PathBuf;
|
||||
use deltachat_derive::*;
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::aheader::EncryptPreference;
|
||||
@@ -1053,7 +1053,9 @@ pub fn addr_normalize(addr: &str) -> &str {
|
||||
}
|
||||
|
||||
fn sanitize_name_and_addr(name: impl AsRef<str>, addr: impl AsRef<str>) -> (String, String) {
|
||||
static ADDR_WITH_NAME_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new("(.*)<(.*)>").unwrap());
|
||||
lazy_static! {
|
||||
static ref ADDR_WITH_NAME_REGEX: Regex = Regex::new("(.*)<(.*)>").unwrap();
|
||||
}
|
||||
if let Some(captures) = ADDR_WITH_NAME_REGEX.captures(addr.as_ref()) {
|
||||
(
|
||||
if name.as_ref().is_empty() {
|
||||
|
||||
@@ -4,7 +4,7 @@ use sha2::{Digest, Sha256};
|
||||
|
||||
use mailparse::SingleInfo;
|
||||
|
||||
use crate::chat::{self, Chat, ChatId, ProtectionStatus};
|
||||
use crate::chat::{self, Chat, ChatId};
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
@@ -714,43 +714,6 @@ async fn add_parts(
|
||||
ephemeral_timer = EphemeralTimer::Disabled;
|
||||
}
|
||||
|
||||
// if a chat is protected, check additional properties
|
||||
if !chat_id.is_special() {
|
||||
let chat = Chat::load_from_db(context, *chat_id).await?;
|
||||
let new_status = match mime_parser.is_system_message {
|
||||
SystemMessage::ChatProtectionEnabled => Some(ProtectionStatus::Protected),
|
||||
SystemMessage::ChatProtectionDisabled => Some(ProtectionStatus::Unprotected),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if chat.is_protected() || new_status.is_some() {
|
||||
if let Err(err) =
|
||||
check_verified_properties(context, mime_parser, from_id as u32, to_ids).await
|
||||
{
|
||||
warn!(context, "verification problem: {}", err);
|
||||
let s = format!("{}. See 'Info' for more details", err);
|
||||
mime_parser.repl_msg_by_error(s);
|
||||
} else {
|
||||
// change chat protection only when verification check passes
|
||||
if let Some(new_status) = new_status {
|
||||
if let Err(e) = chat_id.inner_set_protection(context, new_status).await {
|
||||
chat::add_info_msg(
|
||||
context,
|
||||
*chat_id,
|
||||
format!("Cannot set protection: {}", e),
|
||||
)
|
||||
.await;
|
||||
return Ok(()); // do not return an error as this would result in retrying the message
|
||||
}
|
||||
set_better_msg(
|
||||
mime_parser,
|
||||
context.stock_protection_msg(new_status, from_id).await,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// correct message_timestamp, it should not be used before,
|
||||
// however, we cannot do this earlier as we need from_id to be set
|
||||
let in_fresh = state == MessageState::InFresh;
|
||||
@@ -1191,18 +1154,29 @@ async fn create_or_lookup_group(
|
||||
set_better_msg(mime_parser, &better_msg);
|
||||
|
||||
// check, if we have a chat with this group ID
|
||||
let (mut chat_id, _, _blocked) = chat::get_chat_id_by_grpid(context, &grpid)
|
||||
let (mut chat_id, chat_id_verified, _blocked) = chat::get_chat_id_by_grpid(context, &grpid)
|
||||
.await
|
||||
.unwrap_or((ChatId::new(0), false, Blocked::Not));
|
||||
if !chat_id.is_unset() && !chat::is_contact_in_chat(context, chat_id, from_id as u32).await {
|
||||
// The From-address is not part of this group.
|
||||
// It could be a new user or a DSN from a mailer-daemon.
|
||||
// in any case we do not want to recreate the member list
|
||||
// but still show the message as part of the chat.
|
||||
// After all, the sender has a reference/in-reply-to that
|
||||
// points to this chat.
|
||||
let s = context.stock_str(StockMessage::UnknownSenderForChat).await;
|
||||
mime_parser.repl_msg_by_error(s.to_string());
|
||||
if !chat_id.is_unset() {
|
||||
if chat_id_verified {
|
||||
if let Err(err) =
|
||||
check_verified_properties(context, mime_parser, from_id as u32, to_ids).await
|
||||
{
|
||||
warn!(context, "verification problem: {}", err);
|
||||
let s = format!("{}. See 'Info' for more details", err);
|
||||
mime_parser.repl_msg_by_error(s);
|
||||
}
|
||||
}
|
||||
if !chat::is_contact_in_chat(context, chat_id, from_id as u32).await {
|
||||
// The From-address is not part of this group.
|
||||
// It could be a new user or a DSN from a mailer-daemon.
|
||||
// in any case we do not want to recreate the member list
|
||||
// but still show the message as part of the chat.
|
||||
// After all, the sender has a reference/in-reply-to that
|
||||
// points to this chat.
|
||||
let s = context.stock_str(StockMessage::UnknownSenderForChat).await;
|
||||
mime_parser.repl_msg_by_error(s.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// check if the group does not exist but should be created
|
||||
@@ -1225,7 +1199,7 @@ async fn create_or_lookup_group(
|
||||
|| X_MrAddToGrp.is_some() && addr_cmp(&self_addr, X_MrAddToGrp.as_ref().unwrap()))
|
||||
{
|
||||
// group does not exist but should be created
|
||||
let create_protected = if mime_parser.get(HeaderDef::ChatVerified).is_some() {
|
||||
let create_verified = if mime_parser.get(HeaderDef::ChatVerified).is_some() {
|
||||
if let Err(err) =
|
||||
check_verified_properties(context, mime_parser, from_id as u32, to_ids).await
|
||||
{
|
||||
@@ -1233,9 +1207,9 @@ async fn create_or_lookup_group(
|
||||
let s = format!("{}. See 'Info' for more details", err);
|
||||
mime_parser.repl_msg_by_error(&s);
|
||||
}
|
||||
ProtectionStatus::Protected
|
||||
VerifiedStatus::Verified
|
||||
} else {
|
||||
ProtectionStatus::Unprotected
|
||||
VerifiedStatus::Unverified
|
||||
};
|
||||
|
||||
if !allow_creation {
|
||||
@@ -1248,22 +1222,11 @@ async fn create_or_lookup_group(
|
||||
&grpid,
|
||||
grpname.as_ref().unwrap(),
|
||||
create_blocked,
|
||||
create_protected,
|
||||
create_verified,
|
||||
)
|
||||
.await;
|
||||
chat_id_blocked = create_blocked;
|
||||
recreate_member_list = true;
|
||||
|
||||
// once, we have protected-chats explained in UI, we can uncomment the following lines.
|
||||
// ("verified groups" did not add a message anyway)
|
||||
//
|
||||
//if create_protected == ProtectionStatus::Protected {
|
||||
// set from_id=0 as it is not clear that the sender of this random group message
|
||||
// actually really has enabled chat-protection at some point.
|
||||
//chat_id
|
||||
// .add_protection_msg(context, ProtectionStatus::Protected, false, 0)
|
||||
// .await?;
|
||||
//}
|
||||
}
|
||||
|
||||
// again, check chat_id
|
||||
@@ -1315,10 +1278,7 @@ async fn create_or_lookup_group(
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if mime_parser.is_system_message == SystemMessage::ChatProtectionEnabled {
|
||||
recreate_member_list = true;
|
||||
}
|
||||
|
||||
if let Some(avatar_action) = &mime_parser.group_avatar {
|
||||
info!(context, "group-avatar change for {}", chat_id);
|
||||
if let Ok(mut chat) = Chat::load_from_db(context, chat_id).await {
|
||||
@@ -1503,7 +1463,7 @@ async fn create_or_lookup_adhoc_group(
|
||||
&grpid,
|
||||
grpname,
|
||||
create_blocked,
|
||||
ProtectionStatus::Unprotected,
|
||||
VerifiedStatus::Unverified,
|
||||
)
|
||||
.await;
|
||||
for &member_id in &member_ids {
|
||||
@@ -1520,17 +1480,20 @@ async fn create_group_record(
|
||||
grpid: impl AsRef<str>,
|
||||
grpname: impl AsRef<str>,
|
||||
create_blocked: Blocked,
|
||||
create_protected: ProtectionStatus,
|
||||
create_verified: VerifiedStatus,
|
||||
) -> ChatId {
|
||||
if context.sql.execute(
|
||||
"INSERT INTO chats (type, name, grpid, blocked, created_timestamp, protected) VALUES(?, ?, ?, ?, ?, ?);",
|
||||
"INSERT INTO chats (type, name, grpid, blocked, created_timestamp) VALUES(?, ?, ?, ?, ?);",
|
||||
paramsv![
|
||||
Chattype::Group,
|
||||
if VerifiedStatus::Unverified != create_verified {
|
||||
Chattype::VerifiedGroup
|
||||
} else {
|
||||
Chattype::Group
|
||||
},
|
||||
grpname.as_ref(),
|
||||
grpid.as_ref(),
|
||||
create_blocked,
|
||||
time(),
|
||||
create_protected,
|
||||
],
|
||||
).await
|
||||
.is_err()
|
||||
@@ -1682,17 +1645,6 @@ async fn check_verified_properties(
|
||||
|
||||
ensure!(mimeparser.was_encrypted(), "This message is not encrypted.");
|
||||
|
||||
if mimeparser.get(HeaderDef::ChatVerified).is_none() {
|
||||
// we do not fail here currently, this would exclude (a) non-deltas
|
||||
// and (b) deltas with different protection views across multiple devices.
|
||||
// for group creation or protection enabled/disabled, however, Chat-Verified is respected.
|
||||
warn!(
|
||||
context,
|
||||
"{} did not mark message as protected.",
|
||||
contact.get_addr()
|
||||
);
|
||||
}
|
||||
|
||||
// ensure, the contact is verified
|
||||
// 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
|
||||
@@ -1782,7 +1734,7 @@ async fn check_verified_properties(
|
||||
}
|
||||
if !is_verified {
|
||||
bail!(
|
||||
"{} is not a member of this protected chat",
|
||||
"{} is not a member of this verified group",
|
||||
to_addr.to_string()
|
||||
);
|
||||
}
|
||||
@@ -2230,7 +2182,7 @@ mod tests {
|
||||
assert!(one2one.get_visibility() == ChatVisibility::Archived);
|
||||
|
||||
// create a group with bob, archive group
|
||||
let group_id = chat::create_group_chat(&t.ctx, ProtectionStatus::Unprotected, "foo")
|
||||
let group_id = chat::create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo")
|
||||
.await
|
||||
.unwrap();
|
||||
chat::add_contact_to_chat(&t.ctx, group_id, bob_id).await;
|
||||
|
||||
@@ -717,7 +717,10 @@ where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn is_none_or_empty(&self) -> bool {
|
||||
!matches!(self, Some(s) if !s.as_ref().is_empty())
|
||||
match self {
|
||||
Some(s) if !s.as_ref().is_empty() => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
//!
|
||||
//! A module to remove HTML tags from the email text
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use lazy_static::lazy_static;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
static LINE_RE: Lazy<regex::Regex> = Lazy::new(|| regex::Regex::new(r"(\r?\n)+").unwrap());
|
||||
lazy_static! {
|
||||
static ref LINE_RE: regex::Regex = regex::Regex::new(r"(\r?\n)+").unwrap();
|
||||
}
|
||||
|
||||
struct Dehtml {
|
||||
strbuilder: String,
|
||||
@@ -22,16 +24,16 @@ enum AddText {
|
||||
|
||||
// dehtml() returns way too many newlines; however, an optimisation on this issue is not needed as
|
||||
// the newlines are typically removed in further processing by the caller
|
||||
pub fn dehtml(buf: &str) -> Option<String> {
|
||||
pub fn dehtml(buf: &str) -> String {
|
||||
let s = dehtml_quick_xml(buf);
|
||||
if !s.trim().is_empty() {
|
||||
return Some(s);
|
||||
return s;
|
||||
}
|
||||
let s = dehtml_manually(buf);
|
||||
if !s.trim().is_empty() {
|
||||
return Some(s);
|
||||
return s;
|
||||
}
|
||||
None
|
||||
buf.to_string()
|
||||
}
|
||||
|
||||
pub fn dehtml_quick_xml(buf: &str) -> String {
|
||||
@@ -220,23 +222,21 @@ mod tests {
|
||||
"<a href='https://get.delta.chat/'/>",
|
||||
"[](https://get.delta.chat/)",
|
||||
),
|
||||
("", ""),
|
||||
("<!doctype html>\n<b>fat text</b>", "*fat text*"),
|
||||
// Invalid html (at least DC should show the text if the html is invalid):
|
||||
("<!some invalid html code>\n<b>some text</b>", "some text"),
|
||||
("<This text is in brackets>", "<This text is in brackets>"),
|
||||
];
|
||||
for (input, output) in cases {
|
||||
assert_eq!(simplify(dehtml(input).unwrap(), true).0, output);
|
||||
}
|
||||
let none_cases = vec!["<html> </html>", ""];
|
||||
for input in none_cases {
|
||||
assert_eq!(dehtml(input), None);
|
||||
assert_eq!(simplify(dehtml(input), true).0, output);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dehtml_parse_br() {
|
||||
let html = "\r\r\nline1<br>\r\n\r\n\r\rline2<br/>line3\n\r";
|
||||
let plain = dehtml(html).unwrap();
|
||||
let plain = dehtml(html);
|
||||
|
||||
assert_eq!(plain, "line1\n\r\r\rline2\nline3");
|
||||
}
|
||||
@@ -244,7 +244,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_dehtml_parse_href() {
|
||||
let html = "<a href=url>text</a";
|
||||
let plain = dehtml(html).unwrap();
|
||||
let plain = dehtml(html);
|
||||
|
||||
assert_eq!(plain, "[text](url)");
|
||||
}
|
||||
@@ -252,7 +252,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_dehtml_bold_text() {
|
||||
let html = "<!DOCTYPE name [<!DOCTYPE ...>]><!-- comment -->text <b><?php echo ... ?>bold</b><![CDATA[<>]]>";
|
||||
let plain = dehtml(html).unwrap();
|
||||
let plain = dehtml(html);
|
||||
|
||||
assert_eq!(plain, "text *bold*<>");
|
||||
}
|
||||
@@ -262,7 +262,7 @@ mod tests {
|
||||
let html =
|
||||
"<>"'& äÄöÖüÜß fooÆçÇ ♦‎‏‌&noent;‍";
|
||||
|
||||
let plain = dehtml(html).unwrap();
|
||||
let plain = dehtml(html);
|
||||
|
||||
assert_eq!(
|
||||
plain,
|
||||
@@ -285,7 +285,7 @@ mod tests {
|
||||
</body>
|
||||
</html>
|
||||
"##;
|
||||
let txt = dehtml(input).unwrap();
|
||||
let txt = dehtml(input);
|
||||
assert_eq!(txt.trim(), "lots of text");
|
||||
}
|
||||
}
|
||||
|
||||
70
src/e2ee.rs
70
src/e2ee.rs
@@ -57,9 +57,12 @@ impl EncryptHelper {
|
||||
/// preferences, even if message copy is not sent to self.
|
||||
///
|
||||
/// `e2ee_guaranteed` should be set to true for replies to encrypted messages (as required by
|
||||
/// Autocrypt Level 1, version 1.1) and for messages sent in protected groups.
|
||||
/// Autocrypt Level 1, version 1.1) and for messages sent in verified groups.
|
||||
///
|
||||
/// Returns an error if `e2ee_guaranteed` is true, but one or more keys are missing.
|
||||
///
|
||||
/// Always returns `false` if one of the peerstates does not support Autocrypt (is in "reset"
|
||||
/// state) or does not have a known key.
|
||||
pub fn should_encrypt(
|
||||
&self,
|
||||
context: &Context,
|
||||
@@ -81,11 +84,7 @@ impl EncryptHelper {
|
||||
match peerstate.prefer_encrypt {
|
||||
EncryptPreference::NoPreference => {}
|
||||
EncryptPreference::Mutual => prefer_encrypt_count += 1,
|
||||
EncryptPreference::Reset => {
|
||||
if !e2ee_guaranteed {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
EncryptPreference::Reset => return Ok(false),
|
||||
};
|
||||
}
|
||||
None => {
|
||||
@@ -235,9 +234,9 @@ fn get_autocrypt_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Result<&'a ParsedMail
|
||||
}
|
||||
}
|
||||
|
||||
async fn decrypt_if_autocrypt_message(
|
||||
async fn decrypt_if_autocrypt_message<'a>(
|
||||
context: &Context,
|
||||
mail: &ParsedMail<'_>,
|
||||
mail: &ParsedMail<'a>,
|
||||
private_keyring: Keyring<SignedSecretKey>,
|
||||
public_keyring_for_validate: Keyring<SignedPublicKey>,
|
||||
ret_valid_signatures: &mut HashSet<Fingerprint>,
|
||||
@@ -511,59 +510,4 @@ Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new_peerstates(
|
||||
ctx: &Context,
|
||||
prefer_encrypt: EncryptPreference,
|
||||
) -> Vec<(Option<Peerstate<'_>>, &str)> {
|
||||
let addr = "bob@foo.bar";
|
||||
let pub_key = bob_keypair().public;
|
||||
let peerstate = Peerstate {
|
||||
context: &ctx,
|
||||
addr: addr.into(),
|
||||
last_seen: 13,
|
||||
last_seen_autocrypt: 14,
|
||||
prefer_encrypt,
|
||||
public_key: Some(pub_key.clone()),
|
||||
public_key_fingerprint: Some(pub_key.fingerprint()),
|
||||
gossip_key: Some(pub_key.clone()),
|
||||
gossip_timestamp: 15,
|
||||
gossip_key_fingerprint: Some(pub_key.fingerprint()),
|
||||
verified_key: Some(pub_key.clone()),
|
||||
verified_key_fingerprint: Some(pub_key.fingerprint()),
|
||||
to_save: Some(ToSave::All),
|
||||
fingerprint_changed: false,
|
||||
};
|
||||
let mut peerstates = Vec::new();
|
||||
peerstates.push((Some(peerstate), addr));
|
||||
peerstates
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_should_encrypt() {
|
||||
let t = TestContext::new_alice().await;
|
||||
let encrypt_helper = EncryptHelper::new(&t.ctx).await.unwrap();
|
||||
|
||||
// test with EncryptPreference::NoPreference:
|
||||
// if e2ee_eguaranteed is unset, there is no encryption as not more than half of peers want encryption
|
||||
let ps = new_peerstates(&t.ctx, EncryptPreference::NoPreference);
|
||||
assert!(encrypt_helper.should_encrypt(&t.ctx, true, &ps).unwrap());
|
||||
assert!(!encrypt_helper.should_encrypt(&t.ctx, false, &ps).unwrap());
|
||||
|
||||
// test with EncryptPreference::Reset
|
||||
let ps = new_peerstates(&t.ctx, EncryptPreference::Reset);
|
||||
assert!(encrypt_helper.should_encrypt(&t.ctx, true, &ps).unwrap());
|
||||
assert!(!encrypt_helper.should_encrypt(&t.ctx, false, &ps).unwrap());
|
||||
|
||||
// test with EncryptPreference::Mutual (self is also Mutual)
|
||||
let ps = new_peerstates(&t.ctx, EncryptPreference::Mutual);
|
||||
assert!(encrypt_helper.should_encrypt(&t.ctx, true, &ps).unwrap());
|
||||
assert!(encrypt_helper.should_encrypt(&t.ctx, false, &ps).unwrap());
|
||||
|
||||
// test with missing peerstate
|
||||
let mut ps = Vec::new();
|
||||
ps.push((None, "bob@foo.bar"));
|
||||
assert!(encrypt_helper.should_encrypt(&t.ctx, true, &ps).is_err());
|
||||
assert!(!encrypt_helper.should_encrypt(&t.ctx, false, &ps).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
15
src/imex.rs
15
src/imex.rs
@@ -1054,21 +1054,6 @@ mod tests {
|
||||
assert_eq!(bytes, key.to_asc(None).into_bytes());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_export_and_import_key() {
|
||||
let context = TestContext::new().await;
|
||||
context.configure_alice().await;
|
||||
let blobdir = "$BLOBDIR";
|
||||
assert!(imex(&context.ctx, ImexMode::ExportSelfKeys, Some(blobdir))
|
||||
.await
|
||||
.is_ok());
|
||||
|
||||
let blobdir = context.ctx.get_blobdir().to_str().unwrap();
|
||||
assert!(imex(&context.ctx, ImexMode::ImportSelfKeys, Some(blobdir))
|
||||
.await
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_setup_code() {
|
||||
let norm = normalize_setup_code("123422343234423452346234723482349234");
|
||||
|
||||
27
src/job.rs
27
src/job.rs
@@ -627,9 +627,6 @@ impl Job {
|
||||
/// Then, Fetch the last messages DC_FETCH_EXISTING_MSGS_COUNT emails from the server
|
||||
/// and show them in the chat list.
|
||||
async fn fetch_existing_msgs(&mut self, context: &Context, imap: &mut Imap) -> Status {
|
||||
if context.get_config_bool(Config::Bot).await {
|
||||
return Status::Finished(Ok(())); // Bots don't want those messages
|
||||
}
|
||||
if let Err(err) = imap.connect_configured(context).await {
|
||||
warn!(context, "could not connect: {:?}", err);
|
||||
return Status::RetryLater;
|
||||
@@ -639,19 +636,17 @@ impl Job {
|
||||
add_all_recipients_as_contacts(context, imap, Config::ConfiguredMvboxFolder).await;
|
||||
add_all_recipients_as_contacts(context, imap, Config::ConfiguredInboxFolder).await;
|
||||
|
||||
if context.get_config_bool(Config::FetchExisting).await {
|
||||
for config in &[
|
||||
Config::ConfiguredMvboxFolder,
|
||||
Config::ConfiguredInboxFolder,
|
||||
Config::ConfiguredSentboxFolder,
|
||||
] {
|
||||
if let Some(folder) = context.get_config(*config).await {
|
||||
if let Err(e) = imap.fetch_new_messages(context, folder, true).await {
|
||||
// We are using Anyhow's .context() and to show the inner error, too, we need the {:#}:
|
||||
warn!(context, "Could not fetch messages, retrying: {:#}", e);
|
||||
return Status::RetryLater;
|
||||
};
|
||||
}
|
||||
for config in &[
|
||||
Config::ConfiguredMvboxFolder,
|
||||
Config::ConfiguredInboxFolder,
|
||||
Config::ConfiguredSentboxFolder,
|
||||
] {
|
||||
if let Some(folder) = context.get_config(*config).await {
|
||||
if let Err(e) = imap.fetch_new_messages(context, folder, true).await {
|
||||
// We are using Anyhow's .context() and to show the inner error, too, we need the {:#}:
|
||||
warn!(context, "Could not fetch messages, retrying: {:#}", e);
|
||||
return Status::RetryLater;
|
||||
};
|
||||
}
|
||||
}
|
||||
info!(context, "Done fetching existing messages.");
|
||||
|
||||
@@ -431,9 +431,11 @@ mod tests {
|
||||
use std::error::Error;
|
||||
|
||||
use async_std::sync::Arc;
|
||||
use once_cell::sync::Lazy;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
static KEYPAIR: Lazy<KeyPair> = Lazy::new(alice_keypair);
|
||||
lazy_static! {
|
||||
static ref KEYPAIR: KeyPair = alice_keypair();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_armored_string() {
|
||||
|
||||
103
src/message.rs
103
src/message.rs
@@ -544,7 +544,9 @@ impl Message {
|
||||
return ret;
|
||||
};
|
||||
|
||||
let contact = if self.from_id != DC_CONTACT_ID_SELF as u32 && chat.typ == Chattype::Group {
|
||||
let contact = if self.from_id != DC_CONTACT_ID_SELF as u32
|
||||
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
|
||||
{
|
||||
Contact::get_by_id(context, self.from_id).await.ok()
|
||||
} else {
|
||||
None
|
||||
@@ -589,10 +591,6 @@ impl Message {
|
||||
|| cmd != SystemMessage::Unknown && cmd != SystemMessage::AutocryptSetupMessage
|
||||
}
|
||||
|
||||
pub fn get_info_type(&self) -> SystemMessage {
|
||||
self.param.get_cmd()
|
||||
}
|
||||
|
||||
pub fn is_system_message(&self) -> bool {
|
||||
let cmd = self.param.get_cmd();
|
||||
cmd != SystemMessage::Unknown
|
||||
@@ -978,7 +976,7 @@ impl Lot {
|
||||
);
|
||||
self.text1_meaning = Meaning::Text1Self;
|
||||
}
|
||||
} else if chat.typ == Chattype::Group {
|
||||
} else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
|
||||
if msg.is_info() || contact.is_none() {
|
||||
self.text1 = None;
|
||||
self.text1_meaning = Meaning::None;
|
||||
@@ -998,23 +996,16 @@ impl Lot {
|
||||
}
|
||||
}
|
||||
|
||||
let mut text2 = get_summarytext_by_raw(
|
||||
msg.viewtype,
|
||||
msg.text.as_ref(),
|
||||
&msg.param,
|
||||
SUMMARY_CHARACTERS,
|
||||
context,
|
||||
)
|
||||
.await;
|
||||
|
||||
if text2.is_empty() && msg.quoted_text().is_some() {
|
||||
text2 = context
|
||||
.stock_str(StockMessage::ReplyNoun)
|
||||
.await
|
||||
.into_owned()
|
||||
}
|
||||
|
||||
self.text2 = Some(text2);
|
||||
self.text2 = Some(
|
||||
get_summarytext_by_raw(
|
||||
msg.viewtype,
|
||||
msg.text.as_ref(),
|
||||
&msg.param,
|
||||
SUMMARY_CHARACTERS,
|
||||
context,
|
||||
)
|
||||
.await,
|
||||
);
|
||||
|
||||
self.timestamp = msg.get_timestamp();
|
||||
self.state = msg.state.into();
|
||||
@@ -1196,7 +1187,6 @@ pub fn guess_msgtype_from_suffix(path: &Path) -> Option<(Viewtype, &str)> {
|
||||
"jpg" => (Viewtype::Image, "image/jpeg"),
|
||||
"json" => (Viewtype::File, "application/json"),
|
||||
"mov" => (Viewtype::Video, "video/quicktime"),
|
||||
"m4a" => (Viewtype::Audio, "audio/m4a"),
|
||||
"mp3" => (Viewtype::Audio, "audio/mpeg"),
|
||||
"mp4" => (Viewtype::Video, "video/mp4"),
|
||||
"odp" => (
|
||||
@@ -1638,16 +1628,14 @@ pub(crate) async fn handle_ndn(
|
||||
context: &Context,
|
||||
failed: &FailureReport,
|
||||
error: Option<impl AsRef<str>>,
|
||||
) -> anyhow::Result<()> {
|
||||
) {
|
||||
if failed.rfc724_mid.is_empty() {
|
||||
return Ok(());
|
||||
return;
|
||||
}
|
||||
|
||||
// The NDN might be for a message-id that had attachments and was sent from a non-Delta Chat client.
|
||||
// In this case we need to mark multiple "msgids" as failed that all refer to the same message-id.
|
||||
let msgs: Vec<_> = context
|
||||
let res = context
|
||||
.sql
|
||||
.query_map(
|
||||
.query_row(
|
||||
concat!(
|
||||
"SELECT",
|
||||
" m.id AS msg_id,",
|
||||
@@ -1664,42 +1652,37 @@ pub(crate) async fn handle_ndn(
|
||||
row.get::<_, Chattype>("type")?,
|
||||
))
|
||||
},
|
||||
|rows| Ok(rows.collect::<Vec<_>>()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
for (i, msg) in msgs.into_iter().enumerate() {
|
||||
let (msg_id, chat_id, chat_type) = msg?;
|
||||
set_msg_failed(context, msg_id, error.as_ref()).await;
|
||||
if i == 0 {
|
||||
// Add only one info msg for all failed messages
|
||||
ndn_maybe_add_info_msg(context, failed, chat_id, chat_type).await?;
|
||||
}
|
||||
.await;
|
||||
if let Err(ref err) = res {
|
||||
info!(context, "Failed to select NDN {:?}", err);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
if let Ok((msg_id, chat_id, chat_type)) = res {
|
||||
set_msg_failed(context, msg_id, error).await;
|
||||
|
||||
async fn ndn_maybe_add_info_msg(
|
||||
context: &Context,
|
||||
failed: &FailureReport,
|
||||
chat_id: ChatId,
|
||||
chat_type: Chattype,
|
||||
) -> anyhow::Result<()> {
|
||||
if chat_type == Chattype::Group {
|
||||
if let Some(failed_recipient) = &failed.failed_recipient {
|
||||
let contact_id =
|
||||
Contact::lookup_id_by_addr(context, failed_recipient, Origin::Unknown).await;
|
||||
let contact = Contact::load_from_db(context, contact_id).await?;
|
||||
// Tell the user which of the recipients failed if we know that (because in a group, this might otherwise be unclear)
|
||||
let text = context
|
||||
.stock_string_repl_str(StockMessage::FailedSendingTo, contact.get_display_name())
|
||||
.await;
|
||||
chat::add_info_msg(context, chat_id, text).await;
|
||||
context.emit_event(EventType::ChatModified(chat_id));
|
||||
if chat_type == Chattype::Group || chat_type == Chattype::VerifiedGroup {
|
||||
if let Some(failed_recipient) = &failed.failed_recipient {
|
||||
let contact_id =
|
||||
Contact::lookup_id_by_addr(context, failed_recipient, Origin::Unknown).await;
|
||||
if let Ok(contact) = Contact::load_from_db(context, contact_id).await {
|
||||
// Tell the user which of the recipients failed if we know that (because in a group, this might otherwise be unclear)
|
||||
chat::add_info_msg(
|
||||
context,
|
||||
chat_id,
|
||||
context
|
||||
.stock_string_repl_str(
|
||||
StockMessage::FailedSendingTo,
|
||||
contact.get_display_name(),
|
||||
)
|
||||
.await,
|
||||
)
|
||||
.await;
|
||||
context.emit_event(EventType::ChatModified(chat_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The number of messages assigned to real chat (!=deaddrop, !=trash)
|
||||
|
||||
@@ -233,7 +233,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
fn is_e2ee_guaranteed(&self) -> bool {
|
||||
match &self.loaded {
|
||||
Loaded::Message { chat } => {
|
||||
if chat.is_protected() {
|
||||
if chat.typ == Chattype::VerifiedGroup {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
fn min_verified(&self) -> PeerstateVerifiedStatus {
|
||||
match &self.loaded {
|
||||
Loaded::Message { chat } => {
|
||||
if chat.is_protected() {
|
||||
if chat.typ == Chattype::VerifiedGroup {
|
||||
PeerstateVerifiedStatus::BidirectVerified
|
||||
} else {
|
||||
PeerstateVerifiedStatus::Unverified
|
||||
@@ -268,7 +268,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
fn should_force_plaintext(&self) -> bool {
|
||||
match &self.loaded {
|
||||
Loaded::Message { chat } => {
|
||||
if chat.is_protected() {
|
||||
if chat.typ == Chattype::VerifiedGroup {
|
||||
false
|
||||
} else {
|
||||
self.msg
|
||||
@@ -345,7 +345,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
.stock_str(StockMessage::AcSetupMsgSubject)
|
||||
.await
|
||||
.into_owned()
|
||||
} else if chat.typ == Chattype::Group {
|
||||
} else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
|
||||
let re = if self.in_reply_to.is_empty() {
|
||||
""
|
||||
} else {
|
||||
@@ -708,11 +708,11 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
let mut placeholdertext = None;
|
||||
let mut meta_part = None;
|
||||
|
||||
if chat.is_protected() {
|
||||
if chat.typ == Chattype::VerifiedGroup {
|
||||
protected_headers.push(Header::new("Chat-Verified".to_string(), "1".to_string()));
|
||||
}
|
||||
|
||||
if chat.typ == Chattype::Group {
|
||||
if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
|
||||
protected_headers.push(Header::new("Chat-Group-ID".into(), chat.grpid.clone()));
|
||||
|
||||
let encoded = encode_words(&chat.name);
|
||||
@@ -846,18 +846,6 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
};
|
||||
}
|
||||
}
|
||||
SystemMessage::ChatProtectionEnabled => {
|
||||
protected_headers.push(Header::new(
|
||||
"Chat-Content".to_string(),
|
||||
"protection-enabled".to_string(),
|
||||
));
|
||||
}
|
||||
SystemMessage::ChatProtectionDisabled => {
|
||||
protected_headers.push(Header::new(
|
||||
"Chat-Content".to_string(),
|
||||
"protection-disabled".to_string(),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use lazy_static::lazy_static;
|
||||
use lettre_email::mime::{self, Mime};
|
||||
use mailparse::{addrparse_header, DispositionType, MailHeader, MailHeaderMap, SingleInfo};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::aheader::Aheader;
|
||||
use crate::blob::BlobObject;
|
||||
@@ -86,10 +86,6 @@ pub enum SystemMessage {
|
||||
|
||||
/// Chat ephemeral message timer is changed.
|
||||
EphemeralTimerChanged = 10,
|
||||
|
||||
// Chat protection state changed
|
||||
ChatProtectionEnabled = 11,
|
||||
ChatProtectionDisabled = 12,
|
||||
}
|
||||
|
||||
impl Default for SystemMessage {
|
||||
@@ -127,7 +123,6 @@ impl MimeMessage {
|
||||
|
||||
// remove headers that are allowed _only_ in the encrypted part
|
||||
headers.remove("secure-join-fingerprint");
|
||||
headers.remove("chat-verified");
|
||||
|
||||
// Memory location for a possible decrypted message.
|
||||
let mail_raw;
|
||||
@@ -223,7 +218,6 @@ impl MimeMessage {
|
||||
failure_report: None,
|
||||
};
|
||||
parser.parse_mime_recursive(context, &mail).await?;
|
||||
parser.maybe_remove_bad_parts().await;
|
||||
parser.heuristically_parse_ndn(context).await;
|
||||
parser.parse_headers(context)?;
|
||||
|
||||
@@ -259,10 +253,6 @@ impl MimeMessage {
|
||||
self.is_system_message = SystemMessage::LocationStreamingEnabled;
|
||||
} else if value == "ephemeral-timer-changed" {
|
||||
self.is_system_message = SystemMessage::EphemeralTimerChanged;
|
||||
} else if value == "protection-enabled" {
|
||||
self.is_system_message = SystemMessage::ChatProtectionEnabled;
|
||||
} else if value == "protection-disabled" {
|
||||
self.is_system_message = SystemMessage::ChatProtectionDisabled;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -714,17 +704,12 @@ impl MimeMessage {
|
||||
}
|
||||
};
|
||||
|
||||
let mut dehtml_failed = false;
|
||||
|
||||
let (simplified_txt, is_forwarded, top_quote) = if decoded_data.is_empty() {
|
||||
("".to_string(), false, None)
|
||||
} else {
|
||||
let is_html = mime_type == mime::TEXT_HTML;
|
||||
let out = if is_html {
|
||||
dehtml(&decoded_data).unwrap_or_else(|| {
|
||||
dehtml_failed = true;
|
||||
decoded_data.clone()
|
||||
})
|
||||
dehtml(&decoded_data)
|
||||
} else {
|
||||
decoded_data.clone()
|
||||
};
|
||||
@@ -754,9 +739,8 @@ impl MimeMessage {
|
||||
(simplified_txt, top_quote)
|
||||
};
|
||||
|
||||
if !simplified_txt.is_empty() || simplified_quote.is_some() {
|
||||
if !simplified_txt.is_empty() {
|
||||
let mut part = Part::default();
|
||||
part.dehtlm_failed = dehtml_failed;
|
||||
part.typ = Viewtype::Text;
|
||||
part.mimetype = Some(mime_type);
|
||||
part.msg = simplified_txt;
|
||||
@@ -864,7 +848,6 @@ impl MimeMessage {
|
||||
}
|
||||
|
||||
pub fn repl_msg_by_error(&mut self, error_msg: impl AsRef<str>) {
|
||||
self.is_system_message = SystemMessage::Unknown;
|
||||
if let Some(part) = self.parts.first_mut() {
|
||||
part.typ = Viewtype::Text;
|
||||
part.msg = format!("[{}]", error_msg.as_ref());
|
||||
@@ -999,21 +982,11 @@ impl MimeMessage {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn maybe_remove_bad_parts(&mut self) {
|
||||
let good_parts = self.parts.iter().filter(|p| !p.dehtlm_failed).count();
|
||||
if good_parts == 0 {
|
||||
// We have no good part but show at least one bad part in order to show anything at all
|
||||
self.parts.truncate(1);
|
||||
} else if good_parts < self.parts.len() {
|
||||
self.parts.retain(|p| !p.dehtlm_failed);
|
||||
}
|
||||
}
|
||||
|
||||
/// Some providers like GMX and Yahoo do not send standard NDNs (Non Delivery notifications).
|
||||
/// If you improve heuristics here you might also have to change prefetch_should_download() in imap/mod.rs.
|
||||
/// Also you should add a test in dc_receive_imf.rs (there already are lots of test_parse_ndn_* tests).
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
async fn heuristically_parse_ndn(&mut self, context: &Context) {
|
||||
async fn heuristically_parse_ndn(&mut self, context: &Context) -> Option<()> {
|
||||
let maybe_ndn = if let Some(from) = self.get(HeaderDef::From_) {
|
||||
let from = from.to_ascii_lowercase();
|
||||
from.contains("mailer-daemon") || from.contains("mail-daemon")
|
||||
@@ -1021,8 +994,9 @@ impl MimeMessage {
|
||||
false
|
||||
};
|
||||
if maybe_ndn && self.failure_report.is_none() {
|
||||
static RE: Lazy<regex::Regex> =
|
||||
Lazy::new(|| regex::Regex::new(r"Message-ID:(.*)").unwrap());
|
||||
lazy_static! {
|
||||
static ref RE: regex::Regex = regex::Regex::new(r"Message-ID:(.*)").unwrap();
|
||||
}
|
||||
for captures in self
|
||||
.parts
|
||||
.iter()
|
||||
@@ -1042,6 +1016,7 @@ impl MimeMessage {
|
||||
}
|
||||
}
|
||||
}
|
||||
None // Always return None, we just return anything so that we can use the '?' operator.
|
||||
}
|
||||
|
||||
/// Handle reports
|
||||
@@ -1071,9 +1046,7 @@ impl MimeMessage {
|
||||
.iter()
|
||||
.find(|p| p.typ == Viewtype::Text)
|
||||
.map(|p| p.msg.clone());
|
||||
if let Err(e) = message::handle_ndn(context, failure_report, error).await {
|
||||
warn!(context, "Could not handle ndn: {}", e);
|
||||
}
|
||||
message::handle_ndn(context, failure_report, error).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1212,7 +1185,6 @@ pub struct Part {
|
||||
pub param: Params,
|
||||
org_filename: Option<String>,
|
||||
pub error: Option<String>,
|
||||
dehtlm_failed: bool,
|
||||
}
|
||||
|
||||
/// return mimetype and viewtype for a parsed mail
|
||||
@@ -1894,55 +1866,6 @@ MDYyMDYxNTE1RTlDOEE4Cj4+CnN0YXJ0eHJlZgo4Mjc4CiUlRU9GCg==
|
||||
assert_eq!(message.parts[0].msg, "Hello!");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_hide_html_without_content() {
|
||||
let t = TestContext::new().await;
|
||||
let raw = br#"Date: Thu, 13 Feb 2020 22:41:20 +0000 (UTC)
|
||||
From: sender@example.com
|
||||
To: receiver@example.com
|
||||
Subject: Mail with inline attachment
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="----=_Part_25_46172632.1581201680436"
|
||||
|
||||
------=_Part_25_46172632.1581201680436
|
||||
Content-Type: text/html; charset=utf-8
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
|
||||
<meta name="GENERATOR" content="MSHTML 11.00.10570.1001"></head>
|
||||
<body><img align="baseline" alt="" src="cid:1712254131-1" border="0" hspace="0">
|
||||
</body>
|
||||
|
||||
------=_Part_25_46172632.1581201680436
|
||||
Content-Type: application/pdf; name="some_pdf.pdf"
|
||||
Content-Transfer-Encoding: base64
|
||||
Content-Disposition: inline; filename="some_pdf.pdf"
|
||||
|
||||
JVBERi0xLjUKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURl
|
||||
Y29kZT4+CnN0cmVhbQp4nGVOuwoCMRDs8xVbC8aZvC4Hx4Hno7ATAhZi56MTtPH33YtXiLKQ3ZnM
|
||||
MDYyMDYxNTE1RTlDOEE4Cj4+CnN0YXJ0eHJlZgo4Mjc4CiUlRU9GCg==
|
||||
------=_Part_25_46172632.1581201680436--
|
||||
"#;
|
||||
|
||||
let message = MimeMessage::from_bytes(&t.ctx, &raw[..]).await.unwrap();
|
||||
|
||||
assert_eq!(message.parts.len(), 1);
|
||||
assert_eq!(message.parts[0].typ, Viewtype::File);
|
||||
assert_eq!(message.parts[0].msg, "");
|
||||
|
||||
// Make sure the file is there even though the html is wrong:
|
||||
let param = &message.parts[0].param;
|
||||
let blob: BlobObject = param
|
||||
.get_blob(Param::File, &t.ctx, false)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let f = async_std::fs::File::open(blob.to_abs_path()).await.unwrap();
|
||||
let size = f.metadata().await.unwrap().len();
|
||||
assert_eq!(size, 154);
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn parse_inline_image() {
|
||||
let context = TestContext::new().await;
|
||||
@@ -2236,36 +2159,4 @@ Reply
|
||||
);
|
||||
assert_eq!(message.parts[0].msg, "Reply");
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn parse_quote_without_reply() {
|
||||
let context = TestContext::new().await;
|
||||
let raw = br##"Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
||||
Subject: Re: swipe-to-reply
|
||||
MIME-Version: 1.0
|
||||
In-Reply-To: <bar@example.org>
|
||||
Date: Tue, 06 Oct 2020 00:00:00 +0000
|
||||
Message-ID: <foo@example.org>
|
||||
To: bob <bob@example.org>
|
||||
From: alice <alice@example.org>
|
||||
|
||||
> Just a quote.
|
||||
"##;
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
message.get_subject(),
|
||||
Some("Re: swipe-to-reply".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(message.parts.len(), 1);
|
||||
assert_eq!(message.parts[0].typ, Viewtype::Text);
|
||||
assert_eq!(
|
||||
message.parts[0].param.get(Param::Quote).unwrap(),
|
||||
"Just a quote."
|
||||
);
|
||||
assert_eq!(message.parts[0].msg, "");
|
||||
}
|
||||
}
|
||||
|
||||
41
src/pgp.rs
41
src/pgp.rs
@@ -381,7 +381,7 @@ pub async fn symm_decrypt<T: std::io::Read + std::io::Seek>(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::*;
|
||||
use once_cell::sync::Lazy;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
#[test]
|
||||
fn test_split_armored_data_1() {
|
||||
@@ -449,29 +449,26 @@ mod tests {
|
||||
/// The original text of [CTEXT_SIGNED]
|
||||
static CLEARTEXT: &[u8] = b"This is a test";
|
||||
|
||||
/// Initialised [TestKeys] for tests.
|
||||
static KEYS: Lazy<TestKeys> = Lazy::new(TestKeys::new);
|
||||
lazy_static! {
|
||||
/// Initialised [TestKeys] for tests.
|
||||
static ref KEYS: TestKeys = TestKeys::new();
|
||||
|
||||
/// A cyphertext encrypted to Alice & Bob, signed by Alice.
|
||||
static CTEXT_SIGNED: Lazy<String> = Lazy::new(|| {
|
||||
let mut keyring = Keyring::new();
|
||||
keyring.add(KEYS.alice_public.clone());
|
||||
keyring.add(KEYS.bob_public.clone());
|
||||
futures_lite::future::block_on(pk_encrypt(
|
||||
CLEARTEXT,
|
||||
keyring,
|
||||
Some(KEYS.alice_secret.clone()),
|
||||
))
|
||||
.unwrap()
|
||||
});
|
||||
/// A cyphertext encrypted to Alice & Bob, signed by Alice.
|
||||
static ref CTEXT_SIGNED: String = {
|
||||
let mut keyring = Keyring::new();
|
||||
keyring.add(KEYS.alice_public.clone());
|
||||
keyring.add(KEYS.bob_public.clone());
|
||||
futures_lite::future::block_on(pk_encrypt(CLEARTEXT, keyring, Some(KEYS.alice_secret.clone()))).unwrap()
|
||||
};
|
||||
|
||||
/// A cyphertext encrypted to Alice & Bob, not signed.
|
||||
static CTEXT_UNSIGNED: Lazy<String> = Lazy::new(|| {
|
||||
let mut keyring = Keyring::new();
|
||||
keyring.add(KEYS.alice_public.clone());
|
||||
keyring.add(KEYS.bob_public.clone());
|
||||
futures_lite::future::block_on(pk_encrypt(CLEARTEXT, keyring, None)).unwrap()
|
||||
});
|
||||
/// A cyphertext encrypted to Alice & Bob, not signed.
|
||||
static ref CTEXT_UNSIGNED: String = {
|
||||
let mut keyring = Keyring::new();
|
||||
keyring.add(KEYS.alice_public.clone());
|
||||
keyring.add(KEYS.bob_public.clone());
|
||||
futures_lite::future::block_on(pk_encrypt(CLEARTEXT, keyring, None)).unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encrypt_signed() {
|
||||
|
||||
1509
src/provider/data.rs
1509
src/provider/data.rs
File diff suppressed because it is too large
Load Diff
@@ -42,8 +42,8 @@ def process_config_defaults(data):
|
||||
config_defaults = data.get("config_defaults", "")
|
||||
for key in config_defaults:
|
||||
value = str(config_defaults[key])
|
||||
defaults += " ConfigDefault { key: Config::" + camel(key) + ", value: \"" + value + "\" },\n"
|
||||
defaults += " ])"
|
||||
defaults += " ConfigDefault { key: Config::" + camel(key) + ", value: \"" + value + "\" },\n"
|
||||
defaults += " ])"
|
||||
return defaults
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ def process_data(data, file):
|
||||
raise TypeError("domain used twice: " + domain)
|
||||
domains_dict[domain] = True
|
||||
|
||||
domains += " (\"" + domain + "\", &*" + file2varname(file) + "),\n"
|
||||
domains += " (\"" + domain + "\", &*" + file2varname(file) + "),\n"
|
||||
comment += domain + ", "
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ def process_data(data, file):
|
||||
if username_pattern != "EMAIL" and username_pattern != "EMAILLOCALPART":
|
||||
raise TypeError("bad username pattern")
|
||||
|
||||
server += (" Server { protocol: " + protocol + ", socket: " + socket + ", hostname: \""
|
||||
server += (" Server { protocol: " + protocol + ", socket: " + socket + ", hostname: \""
|
||||
+ hostname + "\", port: " + str(port) + ", username_pattern: " + username_pattern + " },\n")
|
||||
|
||||
config_defaults = process_config_defaults(data)
|
||||
@@ -111,16 +111,16 @@ def process_data(data, file):
|
||||
before_login_hint = cleanstr(data.get("before_login_hint", ""))
|
||||
after_login_hint = cleanstr(data.get("after_login_hint", ""))
|
||||
if (not has_imap and not has_smtp) or (has_imap and has_smtp):
|
||||
provider += "static " + file2varname(file) + ": Lazy<Provider> = Lazy::new(|| Provider {\n"
|
||||
provider += " status: Status::" + status + ",\n"
|
||||
provider += " before_login_hint: \"" + before_login_hint + "\",\n"
|
||||
provider += " after_login_hint: \"" + after_login_hint + "\",\n"
|
||||
provider += " overview_page: \"" + file2url(file) + "\",\n"
|
||||
provider += " server: vec![\n" + server + " ],\n"
|
||||
provider += " config_defaults: " + config_defaults + ",\n"
|
||||
provider += " strict_tls: " + strict_tls + ",\n"
|
||||
provider += " oauth2_authorizer: " + oauth2 + ",\n"
|
||||
provider += "});\n\n"
|
||||
provider += " static ref " + file2varname(file) + ": Provider = Provider {\n"
|
||||
provider += " status: Status::" + status + ",\n"
|
||||
provider += " before_login_hint: \"" + before_login_hint + "\",\n"
|
||||
provider += " after_login_hint: \"" + after_login_hint + "\",\n"
|
||||
provider += " overview_page: \"" + file2url(file) + "\",\n"
|
||||
provider += " server: vec![\n" + server + " ],\n"
|
||||
provider += " config_defaults: " + config_defaults + ",\n"
|
||||
provider += " strict_tls: " + strict_tls + ",\n"
|
||||
provider += " oauth2_authorizer: " + oauth2 + ",\n"
|
||||
provider += " };\n\n"
|
||||
else:
|
||||
raise TypeError("SMTP and IMAP must be specified together or left out both")
|
||||
|
||||
@@ -129,7 +129,7 @@ def process_data(data, file):
|
||||
|
||||
# finally, add the provider
|
||||
global out_all, out_domains
|
||||
out_all += "// " + file[file.rindex("/")+1:] + ": " + comment.strip(", ") + "\n"
|
||||
out_all += " // " + file[file.rindex("/")+1:] + ": " + comment.strip(", ") + "\n"
|
||||
|
||||
# also add provider with no special things to do -
|
||||
# eg. _not_ supporting oauth2 is also an information and we can skip the mx-lookup in this case
|
||||
@@ -164,16 +164,18 @@ if __name__ == "__main__":
|
||||
"use crate::provider::UsernamePattern::*;\n"
|
||||
"use crate::provider::*;\n"
|
||||
"use std::collections::HashMap;\n\n"
|
||||
"use once_cell::sync::Lazy;\n\n")
|
||||
"lazy_static::lazy_static! {\n\n")
|
||||
|
||||
process_dir(sys.argv[1])
|
||||
|
||||
out_all += "pub static PROVIDER_DATA: Lazy<HashMap<&'static str, &'static Provider>> = Lazy::new(|| [\n"
|
||||
out_all += " pub static ref PROVIDER_DATA: HashMap<&'static str, &'static Provider> = [\n"
|
||||
out_all += out_domains;
|
||||
out_all += "].iter().copied().collect());\n\n"
|
||||
out_all += " ].iter().copied().collect();\n\n"
|
||||
|
||||
now = datetime.datetime.utcnow()
|
||||
out_all += "pub static PROVIDER_UPDATED: Lazy<chrono::NaiveDate> = "\
|
||||
"Lazy::new(|| chrono::NaiveDate::from_ymd("+str(now.year)+", "+str(now.month)+", "+str(now.day)+"));\n"
|
||||
out_all += " pub static ref PROVIDER_UPDATED: chrono::NaiveDate = "\
|
||||
"chrono::NaiveDate::from_ymd("+str(now.year)+", "+str(now.month)+", "+str(now.day)+");\n"
|
||||
|
||||
out_all += "}"
|
||||
|
||||
print(out_all)
|
||||
|
||||
12
src/qr.rs
12
src/qr.rs
@@ -1,6 +1,6 @@
|
||||
//! # QR code module
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use lazy_static::lazy_static;
|
||||
use percent_encoding::percent_decode_str;
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -358,10 +358,12 @@ async fn decode_matmsg(context: &Context, qr: &str) -> Lot {
|
||||
Lot::from_address(context, name, addr).await
|
||||
}
|
||||
|
||||
static VCARD_NAME_RE: Lazy<regex::Regex> =
|
||||
Lazy::new(|| regex::Regex::new(r"(?m)^N:([^;]*);([^;\n]*)").unwrap());
|
||||
static VCARD_EMAIL_RE: Lazy<regex::Regex> =
|
||||
Lazy::new(|| regex::Regex::new(r"(?m)^EMAIL([^:\n]*):([^;\n]*)").unwrap());
|
||||
lazy_static! {
|
||||
static ref VCARD_NAME_RE: regex::Regex =
|
||||
regex::Regex::new(r"(?m)^N:([^;]*);([^;\n]*)").unwrap();
|
||||
static ref VCARD_EMAIL_RE: regex::Regex =
|
||||
regex::Regex::new(r"(?m)^EMAIL([^:\n]*):([^;\n]*)").unwrap();
|
||||
}
|
||||
|
||||
/// Extract address for the matmsg scheme.
|
||||
///
|
||||
|
||||
@@ -348,7 +348,7 @@ async fn securejoin(context: &Context, qr: &str) -> Result<ChatId, JoinError> {
|
||||
let bob = context.bob.read().await;
|
||||
let grpid = bob.qr_scan.as_ref().unwrap().text2.as_ref().unwrap();
|
||||
match chat::get_chat_id_by_grpid(context, grpid).await {
|
||||
Ok((chatid, _is_protected, _blocked)) => break chatid,
|
||||
Ok((chatid, _is_verified, _blocked)) => break chatid,
|
||||
Err(err) => {
|
||||
if start.elapsed() > Duration::from_secs(7) {
|
||||
return Err(JoinError::MissingChat(err));
|
||||
@@ -791,19 +791,19 @@ pub(crate) async fn handle_securejoin_handshake(
|
||||
|
||||
let vg_expect_encrypted = if join_vg {
|
||||
let group_id = get_qr_attr!(context, text2).to_string();
|
||||
// This is buggy, is_protected_group will always be
|
||||
// This is buggy, is_verified_group will always be
|
||||
// false since the group is created by receive_imf by
|
||||
// the very handshake message we're handling now. But
|
||||
// only after we have returned. It does not impact
|
||||
// the security invariants of secure-join however.
|
||||
let (_, is_protected_group, _) = chat::get_chat_id_by_grpid(context, &group_id)
|
||||
let (_, is_verified_group, _) = chat::get_chat_id_by_grpid(context, &group_id)
|
||||
.await
|
||||
.unwrap_or((ChatId::new(0), false, Blocked::Not));
|
||||
// when joining a non-verified group
|
||||
// the vg-member-added message may be unencrypted
|
||||
// when not all group members have keys or prefer encryption.
|
||||
// So only expect encryption if this is a verified group
|
||||
is_protected_group
|
||||
is_verified_group
|
||||
} else {
|
||||
// setup contact is always encrypted
|
||||
true
|
||||
@@ -1102,7 +1102,6 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::chat;
|
||||
use crate::chat::ProtectionStatus;
|
||||
use crate::peerstate::Peerstate;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
@@ -1329,7 +1328,7 @@ mod tests {
|
||||
let alice = TestContext::new_alice().await;
|
||||
let bob = TestContext::new_bob().await;
|
||||
|
||||
let chatid = chat::create_group_chat(&alice.ctx, ProtectionStatus::Protected, "the chat")
|
||||
let chatid = chat::create_group_chat(&alice.ctx, VerifiedStatus::Verified, "the chat")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -1428,6 +1427,6 @@ mod tests {
|
||||
|
||||
let bob_chatid = joiner.await;
|
||||
let bob_chat = Chat::load_from_db(&bob.ctx, bob_chatid).await.unwrap();
|
||||
assert!(bob_chat.is_protected());
|
||||
assert!(bob_chat.is_verified());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,9 +71,10 @@ pub fn simplify(mut input: String, is_chat_message: bool) -> (String, bool, Opti
|
||||
let lines = split_lines(&input);
|
||||
let (lines, is_forwarded) = skip_forward_header(&lines);
|
||||
|
||||
let (lines, top_quote) = remove_top_quote(lines);
|
||||
let original_lines = &lines;
|
||||
|
||||
let lines = remove_message_footer(lines);
|
||||
let (lines, top_quote) = remove_top_quote(lines);
|
||||
|
||||
let text = if is_chat_message {
|
||||
render_message(lines, false)
|
||||
|
||||
14
src/sql.rs
14
src/sql.rs
@@ -1368,20 +1368,6 @@ CREATE INDEX devmsglabels_index1 ON devmsglabels (label);
|
||||
.await?;
|
||||
sql.set_raw_config_int(context, "dbversion", 68).await?;
|
||||
}
|
||||
if dbversion < 69 {
|
||||
info!(context, "[migration] v69");
|
||||
sql.execute(
|
||||
"ALTER TABLE chats ADD COLUMN protected INTEGER DEFAULT 0;",
|
||||
paramsv![],
|
||||
)
|
||||
.await?;
|
||||
sql.execute(
|
||||
"UPDATE chats SET protected=1, type=120 WHERE type=130;", // 120=group, 130=old verified group
|
||||
paramsv![],
|
||||
)
|
||||
.await?;
|
||||
sql.set_raw_config_int(context, "dbversion", 69).await?;
|
||||
}
|
||||
|
||||
// (2) updates that require high-level objects
|
||||
// (the structure is complete now and all objects are usable)
|
||||
|
||||
38
src/stock.rs
38
src/stock.rs
@@ -5,8 +5,8 @@ use std::borrow::Cow;
|
||||
use strum::EnumProperty;
|
||||
use strum_macros::EnumProperty;
|
||||
|
||||
use crate::blob::BlobObject;
|
||||
use crate::chat;
|
||||
use crate::chat::ProtectionStatus;
|
||||
use crate::constants::{Viewtype, DC_CONTACT_ID_SELF};
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
@@ -14,7 +14,6 @@ use crate::error::{bail, Error};
|
||||
use crate::message::Message;
|
||||
use crate::param::Param;
|
||||
use crate::stock::StockMessage::{DeviceMessagesHint, WelcomeMessage};
|
||||
use crate::{blob::BlobObject, config::Config};
|
||||
|
||||
/// Stock strings
|
||||
///
|
||||
@@ -234,16 +233,6 @@ pub enum StockMessage {
|
||||
fallback = "Could not find your mail server.\n\nPlease check your internet connection."
|
||||
))]
|
||||
ErrorNoNetwork = 87,
|
||||
|
||||
#[strum(props(fallback = "Chat protection enabled."))]
|
||||
ProtectionEnabled = 88,
|
||||
|
||||
#[strum(props(fallback = "Chat protection disabled."))]
|
||||
ProtectionDisabled = 89,
|
||||
|
||||
// used in summaries, a noun, not a verb (not: "to reply")
|
||||
#[strum(props(fallback = "Reply"))]
|
||||
ReplyNoun = 90,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -409,27 +398,16 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a stock message saying that protection status has changed.
|
||||
pub async fn stock_protection_msg(&self, protect: ProtectionStatus, from_id: u32) -> String {
|
||||
self.stock_system_msg(
|
||||
match protect {
|
||||
ProtectionStatus::Protected => StockMessage::ProtectionEnabled,
|
||||
ProtectionStatus::Unprotected => StockMessage::ProtectionDisabled,
|
||||
},
|
||||
"",
|
||||
"",
|
||||
from_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn update_device_chats(&self) -> Result<(), Error> {
|
||||
if self.get_config_bool(Config::Bot).await {
|
||||
pub async fn update_device_chats(&self) -> Result<(), Error> {
|
||||
// check for the LAST added device message - if it is present, we can skip message creation.
|
||||
// this is worthwhile as this function is typically called
|
||||
// by the UI on every program start or even on every opening of the chatlist.
|
||||
if chat::was_device_msg_ever_added(&self, "core-welcome").await? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// create saved-messages chat; we do this only once, if the user has deleted the chat,
|
||||
// he can recreate it manually (make sure we do not re-add it when configure() was called a second time)
|
||||
// create saved-messages chat;
|
||||
// we do this only once, if the user has deleted the chat, he can recreate it manually.
|
||||
if !self.sql.get_raw_config_bool(&self, "self-chat-added").await {
|
||||
self.sql
|
||||
.set_raw_config_bool(&self, "self-chat-added", true)
|
||||
|
||||
@@ -9,15 +9,13 @@ use async_std::path::PathBuf;
|
||||
use async_std::sync::RwLock;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
use crate::chat;
|
||||
use crate::chat::{ChatId, ChatItem};
|
||||
use crate::chat::ChatId;
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::dc_receive_imf::dc_receive_imf;
|
||||
use crate::dc_tools::EmailAddress;
|
||||
use crate::job::Action;
|
||||
use crate::key::{self, DcKey};
|
||||
use crate::message::Message;
|
||||
use crate::mimeparser::MimeMessage;
|
||||
use crate::param::{Param, Params};
|
||||
|
||||
@@ -189,19 +187,6 @@ impl TestContext {
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Get the most recent message of a chat.
|
||||
///
|
||||
/// Panics on errors or if the most recent message is a marker.
|
||||
pub async fn get_last_msg(&self, chat_id: ChatId) -> Message {
|
||||
let msgs = chat::get_chat_msgs(&self.ctx, chat_id, 0, None).await;
|
||||
let msg_id = if let ChatItem::Message { msg_id } = msgs.last().unwrap() {
|
||||
msg_id
|
||||
} else {
|
||||
panic!("Wrong item type");
|
||||
};
|
||||
Message::load_from_db(&self.ctx, *msg_id).await.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A raw message as it was scheduled to be sent.
|
||||
|
||||
110
tests/stress.rs
Normal file
110
tests/stress.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
//! Stress some functions for testing; if used as a lib, this file is obsolete.
|
||||
|
||||
use deltachat::config;
|
||||
use deltachat::context::*;
|
||||
use tempfile::tempdir;
|
||||
|
||||
/* some data used for testing
|
||||
******************************************************************************/
|
||||
|
||||
async fn stress_functions(context: &Context) {
|
||||
let res = context
|
||||
.get_config(config::Config::SysConfigKeys)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(!res.contains(" probably_never_a_key "));
|
||||
assert!(res.contains(" addr "));
|
||||
assert!(res.contains(" mail_server "));
|
||||
assert!(res.contains(" mail_user "));
|
||||
assert!(res.contains(" mail_pw "));
|
||||
assert!(res.contains(" mail_port "));
|
||||
assert!(res.contains(" send_server "));
|
||||
assert!(res.contains(" send_user "));
|
||||
assert!(res.contains(" send_pw "));
|
||||
assert!(res.contains(" send_port "));
|
||||
assert!(res.contains(" server_flags "));
|
||||
assert!(res.contains(" imap_folder "));
|
||||
assert!(res.contains(" displayname "));
|
||||
assert!(res.contains(" selfstatus "));
|
||||
assert!(res.contains(" selfavatar "));
|
||||
assert!(res.contains(" e2ee_enabled "));
|
||||
assert!(res.contains(" mdns_enabled "));
|
||||
assert!(res.contains(" save_mime_headers "));
|
||||
assert!(res.contains(" configured_addr "));
|
||||
assert!(res.contains(" configured_mail_server "));
|
||||
assert!(res.contains(" configured_mail_user "));
|
||||
assert!(res.contains(" configured_mail_pw "));
|
||||
assert!(res.contains(" configured_mail_port "));
|
||||
assert!(res.contains(" configured_send_server "));
|
||||
assert!(res.contains(" configured_send_user "));
|
||||
assert!(res.contains(" configured_send_pw "));
|
||||
assert!(res.contains(" configured_send_port "));
|
||||
assert!(res.contains(" configured_server_flags "));
|
||||
|
||||
// Cant check, no configured context
|
||||
// assert!(dc_is_configured(context) != 0, "Missing configured context");
|
||||
|
||||
// let setupcode = dc_create_setup_code(context);
|
||||
// let setupcode_c = CString::new(setupcode.clone()).unwrap();
|
||||
// let setupfile = dc_render_setup_file(context, &setupcode).unwrap();
|
||||
// let setupfile_c = CString::new(setupfile).unwrap();
|
||||
// let mut headerline_2: *const libc::c_char = ptr::null();
|
||||
// let payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr());
|
||||
|
||||
// assert!(payload.is_null());
|
||||
// assert!(!dc_split_armored_data(
|
||||
// payload,
|
||||
// &mut headerline_2,
|
||||
// ptr::null_mut(),
|
||||
// ptr::null_mut(),
|
||||
// ptr::null_mut(),
|
||||
// ));
|
||||
// assert!(!headerline_2.is_null());
|
||||
// assert_eq!(
|
||||
// strcmp(
|
||||
// headerline_2,
|
||||
// b"-----BEGIN PGP PRIVATE KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
|
||||
// ),
|
||||
// 0
|
||||
// );
|
||||
// free(payload as *mut libc::c_void);
|
||||
|
||||
// Cant check, no configured context
|
||||
// assert!(dc_is_configured(context) != 0, "missing configured context");
|
||||
|
||||
// let qr = dc_get_securejoin_qr(context, 0);
|
||||
// assert!(!qr.is_null(), "Invalid qr code generated");
|
||||
// let qr_r = as_str(qr);
|
||||
|
||||
// assert!(qr_r.len() > 55);
|
||||
// assert!(qr_r.starts_with("OPENPGP4FPR:"));
|
||||
|
||||
// let res = dc_check_qr(context, qr);
|
||||
// let s = res.get_state();
|
||||
|
||||
// assert!(
|
||||
// s == QrState::AskVerifyContact
|
||||
// || s == QrState::FprMissmatch
|
||||
// || s == QrState::FprWithoutAddr
|
||||
// );
|
||||
|
||||
// free(qr.cast());
|
||||
}
|
||||
|
||||
async fn create_test_context() -> Context {
|
||||
use rand::Rng;
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
let id = rand::thread_rng().gen();
|
||||
Context::new("FakeOs".into(), dbfile.into(), id)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_stress_tests() {
|
||||
let context = create_test_context().await;
|
||||
stress_functions(&context).await;
|
||||
}
|
||||
Reference in New Issue
Block a user