mirror of
https://github.com/chatmail/core.git
synced 2026-04-05 15:02:11 +03:00
Compare commits
77 Commits
should_do_
...
1.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2773b89815 | ||
|
|
949d93fdaa | ||
|
|
90a4303c8e | ||
|
|
d3b1972505 | ||
|
|
3807d5fbd0 | ||
|
|
e49e2021e5 | ||
|
|
84a5276ab0 | ||
|
|
1732c3b350 | ||
|
|
0f52f63863 | ||
|
|
0043e95ba7 | ||
|
|
a3f2088046 | ||
|
|
186f5553b8 | ||
|
|
d8454d9da5 | ||
|
|
318194a216 | ||
|
|
b50d2358d3 | ||
|
|
763587ffb4 | ||
|
|
8a375c12e9 | ||
|
|
11afdb51f3 | ||
|
|
e4353f4650 | ||
|
|
9d4437a7f5 | ||
|
|
493bf5ed08 | ||
|
|
93800fd834 | ||
|
|
79bc34ed76 | ||
|
|
b40ad7e87e | ||
|
|
1ab3fba212 | ||
|
|
3cd0bbc0f4 | ||
|
|
a806728e43 | ||
|
|
683037ca69 | ||
|
|
a113127df1 | ||
|
|
fecfaaf812 | ||
|
|
8df8f1f6f7 | ||
|
|
5502ca5f58 | ||
|
|
75b8cfa92e | ||
|
|
f7b23fb0e2 | ||
|
|
42f7abf2f5 | ||
|
|
08dd0e34d1 | ||
|
|
7540770dec | ||
|
|
213c5df706 | ||
|
|
578e4b2785 | ||
|
|
8f8db0c431 | ||
|
|
df7253b13e | ||
|
|
fcafd63f41 | ||
|
|
c82fdb9fa1 | ||
|
|
454c52f4ab | ||
|
|
73c69c3f93 | ||
|
|
614ec30e05 | ||
|
|
d7c07e0ed3 | ||
|
|
08bd16c1b6 | ||
|
|
4f06c72798 | ||
|
|
166fe2a4e5 | ||
|
|
b7635a71c8 | ||
|
|
6ad4bdea83 | ||
|
|
efb9a11d22 | ||
|
|
047d09bcb1 | ||
|
|
e1ca6b5181 | ||
|
|
36a2569537 | ||
|
|
10ceddfa67 | ||
|
|
929677afe0 | ||
|
|
ee4adc1363 | ||
|
|
c7b2bdfaac | ||
|
|
fde8fb960b | ||
|
|
9a43d26c60 | ||
|
|
742f603b3b | ||
|
|
a82f2a5df3 | ||
|
|
06bc5513ae | ||
|
|
47b937f880 | ||
|
|
775d27b6a9 | ||
|
|
6c838ab57c | ||
|
|
8eebd2aa67 | ||
|
|
fc78a08657 | ||
|
|
7b3cc95ab7 | ||
|
|
bd70765b7d | ||
|
|
5b424aec22 | ||
|
|
d6cc0694f0 | ||
|
|
9738515129 | ||
|
|
b05ee10f41 | ||
|
|
67d85f0f86 |
@@ -120,32 +120,19 @@ jobs:
|
||||
paths:
|
||||
- c-docs
|
||||
|
||||
# build_test_docs_wheel:
|
||||
# docker:
|
||||
# - image: deltachat/coredeps
|
||||
# environment:
|
||||
# TESTS: 1
|
||||
# DOCS: 1
|
||||
# working_directory: /mnt/crate
|
||||
# steps:
|
||||
# - *restore-workspace
|
||||
# - *restore-cache
|
||||
# - run:
|
||||
# name: build docs, run tests and build wheels
|
||||
# command: ci_scripts/run-python.sh
|
||||
# - run:
|
||||
# name: copying docs and wheels to workspace
|
||||
# command: |
|
||||
# mkdir -p workspace/python
|
||||
# # cp -av docs workspace/c-docs
|
||||
# cp -av python/.docker-tox/wheelhouse workspace/
|
||||
# cp -av python/doc/_build/ workspace/py-docs
|
||||
# - persist_to_workspace:
|
||||
# root: workspace
|
||||
# paths:
|
||||
# # - c-docs
|
||||
# - py-docs
|
||||
# - wheelhouse
|
||||
remote_python_packaging:
|
||||
machine: true
|
||||
steps:
|
||||
- checkout
|
||||
# the following commands on success produces
|
||||
# workspace/{wheelhouse,py-docs} as artefact directories
|
||||
- run: bash ci_scripts/remote_python_packaging.sh
|
||||
- persist_to_workspace:
|
||||
root: workspace
|
||||
paths:
|
||||
# - c-docs
|
||||
- py-docs
|
||||
- wheelhouse
|
||||
|
||||
remote_tests_rust:
|
||||
machine: true
|
||||
@@ -157,10 +144,7 @@ jobs:
|
||||
machine: true
|
||||
steps:
|
||||
- checkout
|
||||
#- attach_workspace:
|
||||
# at: workspace
|
||||
- run: ci_scripts/remote_tests_python.sh
|
||||
# workspace/py-docs workspace/wheelhouse workspace/c-docs
|
||||
|
||||
upload_docs_wheels:
|
||||
machine: true
|
||||
@@ -168,6 +152,7 @@ jobs:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: workspace
|
||||
- run: pyenv versions
|
||||
- run: pyenv global 3.5.2
|
||||
- run: ls -laR workspace
|
||||
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse workspace/c-docs
|
||||
@@ -193,10 +178,15 @@ workflows:
|
||||
|
||||
- remote_tests_python
|
||||
|
||||
# - upload_docs_wheels:
|
||||
# requires:
|
||||
# - build_test_docs_wheel
|
||||
# - build_doxygen
|
||||
- remote_python_packaging:
|
||||
requires:
|
||||
- remote_tests_python
|
||||
- remote_tests_rust
|
||||
|
||||
- upload_docs_wheels:
|
||||
requires:
|
||||
- remote_python_packaging
|
||||
- build_doxygen
|
||||
# - rustfmt:
|
||||
# requires:
|
||||
# - cargo_fetch
|
||||
|
||||
22
CHANGELOG.md
22
CHANGELOG.md
@@ -1,5 +1,27 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0-beta.23
|
||||
|
||||
- #1197 fix imap-deletion of messages
|
||||
|
||||
- #1171 Combine multiple MDNs into a single mail, reducing traffic
|
||||
|
||||
- #1155 fix to not send out gossip always, reducing traffic
|
||||
|
||||
- #1160 fix reply-to-encrypted determination
|
||||
|
||||
- #1182 Add "Auto-Submitted: auto-replied" header to MDNs
|
||||
|
||||
- #1194 produce python wheels again, fix c/py.delta.chat
|
||||
master-deployment
|
||||
|
||||
- rust-level housekeeping and improvements #1161 #1186 #1185 #1190 #1194 #1199 #1191 #1190 #1184 and more
|
||||
|
||||
- #1063 clarify licensing
|
||||
|
||||
- #1147 use mailparse 0.10.2
|
||||
|
||||
|
||||
## 1.0.0-beta.22
|
||||
|
||||
- #1095 normalize email lineends to CRLF
|
||||
|
||||
245
Cargo.lock
generated
245
Cargo.lock
generated
@@ -84,7 +84,7 @@ version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -179,9 +179,9 @@ name = "async-trait"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -528,14 +528,6 @@ dependencies = [
|
||||
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.6.6"
|
||||
@@ -561,7 +553,7 @@ version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -592,10 +584,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -605,7 +597,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -628,7 +620,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.0.0-beta.22"
|
||||
version = "1.0.0-beta.23"
|
||||
dependencies = [
|
||||
"async-imap 0.1.1 (git+https://github.com/async-email/async-imap?branch=dcc-stable)",
|
||||
"async-native-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -641,14 +633,14 @@ dependencies = [
|
||||
"charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"debug_stub_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"deltachat_derive 0.1.0",
|
||||
"deltachat_derive 1.0.0-beta.22",
|
||||
"email 0.0.21 (git+https://github.com/deltachat/rust-email)",
|
||||
"encoded-words 0.1.0 (git+https://github.com/async-email/encoded-words)",
|
||||
"escaper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"image 0.22.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"image-meta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -666,11 +658,11 @@ dependencies = [
|
||||
"proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quick-xml 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"r2d2 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"r2d2_sqlite 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"r2d2_sqlite 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rusqlite 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sanitize-filename 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -697,17 +689,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_derive"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0-beta.22"
|
||||
dependencies = [
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.0.0-beta.22"
|
||||
version = "1.0.0-beta.23"
|
||||
dependencies = [
|
||||
"deltachat 1.0.0-beta.22",
|
||||
"deltachat 1.0.0-beta.23",
|
||||
"deltachat-provider-database 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -723,9 +715,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"derive_builder_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -734,9 +726,9 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -940,9 +932,9 @@ name = "failure_derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -1080,9 +1072,9 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1329,7 +1321,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.22.3"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1339,8 +1331,6 @@ dependencies = [
|
||||
"num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"png 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tiff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1404,7 +1394,6 @@ version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1493,7 +1482,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.16.0"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1728,24 +1717,14 @@ dependencies = [
|
||||
"zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1978,9 +1957,9 @@ name = "pin-project-internal"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2040,9 +2019,9 @@ name = "proc-macro-hack"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2052,15 +2031,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.6"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2111,20 +2082,12 @@ name = "quote"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2144,11 +2107,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "r2d2_sqlite"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"r2d2 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rusqlite 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2297,28 +2260,6 @@ dependencies = [
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rdrand"
|
||||
version = "0.4.0"
|
||||
@@ -2381,9 +2322,9 @@ name = "rental-impl"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2450,13 +2391,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.20.0"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libsqlite3-sys 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2556,11 +2497,6 @@ dependencies = [
|
||||
"parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped_threadpool"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.0.0"
|
||||
@@ -2612,9 +2548,9 @@ name = "serde_derive"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2729,9 +2665,9 @@ name = "snafu-derive"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2787,9 +2723,9 @@ version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2809,30 +2745,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.14.9"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -2850,9 +2766,9 @@ name = "synstructure"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -2907,9 +2823,9 @@ name = "thiserror-impl"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2928,17 +2844,6 @@ dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.42"
|
||||
@@ -3072,11 +2977,6 @@ name = "unicode-xid"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
@@ -3193,9 +3093,9 @@ dependencies = [
|
||||
"bumpalo 3.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -3224,9 +3124,9 @@ name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -3244,9 +3144,9 @@ dependencies = [
|
||||
"anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -3381,9 +3281,9 @@ name = "zeroize_derive"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -3451,7 +3351,6 @@ dependencies = [
|
||||
"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c"
|
||||
"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca"
|
||||
"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac"
|
||||
"checksum crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfd6515864a82d2f877b42813d4553292c6659498c9a2aa31bab5a15243c2700"
|
||||
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
|
||||
"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
|
||||
"checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc"
|
||||
@@ -3533,7 +3432,7 @@ dependencies = [
|
||||
"checksum hyper-tls 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab58a31960b2f78c5c24cf255216789863754438a1e48849a956846f899e762e"
|
||||
"checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
|
||||
"checksum image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4be8aaefbe7545dc42ae925afb55a0098f226a3fe5ef721872806f44f57826"
|
||||
"checksum image 0.22.4 (registry+https://github.com/rust-lang/crates.io-index)" = "53cb19c4e35102e5c6fb9ade5e0e236c5588424dc171a849af3141bf0b47768a"
|
||||
"checksum image-meta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b00861cbbb254a627d8acc0cec786b484297d896ab8f20fdc8e28536a3e918ef"
|
||||
"checksum imap-proto 0.9.1 (git+https://github.com/djc/tokio-imap)" = "<none>"
|
||||
"checksum indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2"
|
||||
@@ -3552,7 +3451,7 @@ dependencies = [
|
||||
"checksum lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2304bccb228c4b020f3a4835d247df0a02a7c4686098d4167762cfbbe4c5cb14"
|
||||
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
|
||||
"checksum libm 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
|
||||
"checksum libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b95e89c330291768dc840238db7f9e204fd208511ab6319b56193a7f2ae25"
|
||||
"checksum libsqlite3-sys 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "266eb8c361198e8d1f682bc974e5d9e2ae90049fb1943890904d11dad7d4a77d"
|
||||
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
|
||||
"checksum lock_api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e57b3997725d2b60dbec1297f6c2e2957cc383db1cebd6be812163f969c7d586"
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
@@ -3578,7 +3477,6 @@ dependencies = [
|
||||
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||
"checksum nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c618b63422da4401283884e6668d39f819a106ef51f5f59b81add00075da35ca"
|
||||
"checksum num-bigint-dig 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3d03c330f9f7a2c19e3c0b42698e48141d0809c78cd9b6219f85bd7d7e892aa"
|
||||
"checksum num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2"
|
||||
"checksum num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746"
|
||||
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||
"checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e"
|
||||
@@ -3611,18 +3509,16 @@ dependencies = [
|
||||
"checksum pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "717ee476b1690853d222af4634056d830b5197ffd747726a9a1eee6da9f49074"
|
||||
"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
|
||||
"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e"
|
||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
|
||||
"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548"
|
||||
"checksum proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cf147e022eacf0c8a054ab864914a7602618adba841d800a9a9868a5237a529f"
|
||||
"checksum pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eef52fac62d0ea7b9b4dc7da092aa64ea7ec3d90af6679422d3d7e0e14b6ee15"
|
||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||
"checksum quick-xml 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fe1e430bdcf30c9fdc25053b9c459bb1a4672af4617b6c783d7d91dc17c6bbb0"
|
||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
"checksum quoted_printable 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "86cedf331228892e747bb85beb130b6bb23fc628c40dde9ea01eb6becea3c798"
|
||||
"checksum r2d2 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "22b5c5fc5fba064373f03887337b412e0e1562d63023393db77251146cb75553"
|
||||
"checksum r2d2_sqlite 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "806e268035ce9e5a604bf617ac8a073ef28b59ef2e48e8338db0baf530caef33"
|
||||
"checksum r2d2_sqlite 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b061f5b16692bbe81eeb260f92e6fc7d13aea455c4cbe67f5c4aa20aa92d1d9e"
|
||||
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
|
||||
@@ -3638,8 +3534,6 @@ dependencies = [
|
||||
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
||||
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
||||
"checksum rayon 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43739f8831493b276363637423d3622d4bd6394ab6f0a9c4a552e208aeb7fddd"
|
||||
"checksum rayon-core 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8bf17de6f23b05473c437eb958b9c850bfc8af0961fe17b4cc92d5a627b4791"
|
||||
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
"checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d"
|
||||
@@ -3651,7 +3545,7 @@ dependencies = [
|
||||
"checksum reqwest 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03c6cbd2bc1c1cb7052dbe30f4a70cf65811967c800f2dfbb2e6036dc9ee2553"
|
||||
"checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a"
|
||||
"checksum rsa 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6ed8692d8e0ea3baae03f0f32ecfc13a6c6f1f85fcd6d9fdefcdf364e70f4df9"
|
||||
"checksum rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051"
|
||||
"checksum rusqlite 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "64a656821bb6317a84b257737b7934f79c0dbb7eb694710475908280ebad3e64"
|
||||
"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf"
|
||||
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
@@ -3663,7 +3557,6 @@ dependencies = [
|
||||
"checksum sanitize-filename 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23fd0fec94ec480abfd86bb8f4f6c57e0efb36dac5c852add176ea7b04c74801"
|
||||
"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021"
|
||||
"checksum scheduled-thread-pool 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f5de7bc31f28f8e6c28df5e1bf3d10610f5fdc14cc95f272853512c70a2bd779"
|
||||
"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
|
||||
"checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df"
|
||||
"checksum security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895"
|
||||
@@ -3694,9 +3587,7 @@ dependencies = [
|
||||
"checksum strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81"
|
||||
"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941"
|
||||
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
|
||||
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||
"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238"
|
||||
"checksum syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8"
|
||||
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
||||
"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
|
||||
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||
@@ -3707,7 +3598,6 @@ dependencies = [
|
||||
"checksum thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef"
|
||||
"checksum thread-local-object 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7da3caa820d0308c84c8654f6cafd81cc3195d45433311cbe22fcf44fc8be071"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum tiff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b7c2cfc4742bd8a32f2e614339dd8ce30dbcf676bb262bd63a2327bc5df57d"
|
||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||
"checksum tokio 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "a9d5acfe1b1130d50ac2286a2f1f8cf49309680366ceb7609ce369b75c9058d4"
|
||||
"checksum tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828"
|
||||
@@ -3725,7 +3615,6 @@ dependencies = [
|
||||
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
||||
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f"
|
||||
"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61"
|
||||
|
||||
14
Cargo.toml
14
Cargo.toml
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.0.0-beta.22"
|
||||
version = "1.0.0-beta.23"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2018"
|
||||
license = "MPL"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
@@ -17,7 +17,7 @@ hex = "0.4.0"
|
||||
sha2 = "0.8.0"
|
||||
rand = "0.7.0"
|
||||
smallvec = "1.0.0"
|
||||
reqwest = "0.10.0"
|
||||
reqwest = { version = "0.10.0", features = ["blocking", "json"] }
|
||||
num-derive = "0.3.0"
|
||||
num-traits = "0.2.6"
|
||||
async-smtp = { git = "https://github.com/async-email/async-smtp" }
|
||||
@@ -42,8 +42,8 @@ indexmap = "1.3.0"
|
||||
rustyline = "4.1.0"
|
||||
lazy_static = "1.4.0"
|
||||
regex = "1.1.6"
|
||||
rusqlite = { version = "0.20", features = ["bundled"] }
|
||||
r2d2_sqlite = "0.12.0"
|
||||
rusqlite = { version = "0.21", features = ["bundled"] }
|
||||
r2d2_sqlite = "0.13.0"
|
||||
r2d2 = "0.8.5"
|
||||
strum = "0.16.0"
|
||||
strum_macros = "0.16.0"
|
||||
@@ -61,7 +61,7 @@ stop-token = { version = "0.1.1", features = ["unstable"] }
|
||||
mailparse = "0.10.2"
|
||||
encoded-words = { git = "https://github.com/async-email/encoded-words", branch="master" }
|
||||
native-tls = "0.2.3"
|
||||
image = "0.22.3"
|
||||
image = { version = "0.22.4", default-features=false, features = ["gif_codec", "jpeg", "ico", "png_codec", "pnm", "webp", "bmp"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
@@ -85,7 +85,7 @@ path = "examples/repl/main.rs"
|
||||
|
||||
|
||||
[features]
|
||||
default = ["nightly", "ringbuf", "reqwest/blocking", "reqwest/json"]
|
||||
default = ["nightly", "ringbuf"]
|
||||
vendored = ["native-tls/vendored", "reqwest/native-tls-vendored"]
|
||||
nightly = ["pgp/nightly"]
|
||||
ringbuf = ["pgp/ringbuf"]
|
||||
|
||||
3
LICENSE
3
LICENSE
@@ -2,9 +2,6 @@ The files in this directory and under its subdirectories
|
||||
are (c) 2019 by Bjoern Petersen and contributors and released under the
|
||||
Mozilla Public License Version 2.0, see below for a copy.
|
||||
|
||||
NOTE that the files in the "libs" directory are copyrighted by third parties
|
||||
and come with their own respective licenses.
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
|
||||
@@ -14,24 +14,22 @@ DOXYDOCDIR=${3:?directory where doxygen docs to be found}
|
||||
export BRANCH=${CIRCLE_BRANCH:?specify branch for uploading purposes}
|
||||
|
||||
|
||||
# DISABLED: python docs to py.delta.chat
|
||||
#ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@py.delta.chat mkdir -p build/${BRANCH}
|
||||
#rsync -avz \
|
||||
# -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
|
||||
# "$PYDOCDIR/html/" \
|
||||
# delta@py.delta.chat:build/${BRANCH}
|
||||
# python docs to py.delta.chat
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@py.delta.chat mkdir -p build/${BRANCH}
|
||||
rsync -avz \
|
||||
--delete \
|
||||
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
|
||||
"$PYDOCDIR/html/" \
|
||||
delta@py.delta.chat:build/${BRANCH}
|
||||
|
||||
# C docs to c.delta.chat
|
||||
ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@c.delta.chat mkdir -p build-c/${BRANCH}
|
||||
rsync -avz \
|
||||
--delete \
|
||||
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
|
||||
"$DOXYDOCDIR/html/" \
|
||||
delta@c.delta.chat:build-c/${BRANCH}
|
||||
|
||||
exit 0
|
||||
|
||||
# OUTDATED -- for re-use from python release-scripts
|
||||
|
||||
echo -----------------------
|
||||
echo upload wheels
|
||||
echo -----------------------
|
||||
@@ -39,6 +37,7 @@ echo -----------------------
|
||||
# Bundle external shared libraries into the wheels
|
||||
pushd $WHEELHOUSEDIR
|
||||
|
||||
pip3 install -U pip
|
||||
pip3 install devpi-client
|
||||
devpi use https://m.devpi.net
|
||||
devpi login dc --password $DEVPI_LOGIN
|
||||
@@ -50,6 +49,9 @@ devpi use dc/$N_BRANCH || {
|
||||
devpi use dc/$N_BRANCH
|
||||
}
|
||||
devpi index $N_BRANCH bases=/root/pypi
|
||||
devpi upload deltachat*.whl
|
||||
devpi upload deltachat*
|
||||
|
||||
popd
|
||||
|
||||
# remove devpi non-master dc indices if thy are too old
|
||||
python ci_scripts/cleanup_devpi_indices.py
|
||||
|
||||
69
ci_scripts/cleanup_devpi_indices.py
Normal file
69
ci_scripts/cleanup_devpi_indices.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
Remove old "dc" indices except for master which always stays.
|
||||
|
||||
"""
|
||||
from requests import Session
|
||||
import datetime
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
MAXDAYS=7
|
||||
|
||||
session = Session()
|
||||
session.headers["Accept"] = "application/json"
|
||||
|
||||
|
||||
def get_indexes(baseurl, username):
|
||||
response = session.get(baseurl + username)
|
||||
assert response.status_code == 200
|
||||
result = response.json()["result"]
|
||||
return result["indexes"]
|
||||
|
||||
|
||||
def get_projectnames(baseurl, username, indexname):
|
||||
response = session.get(baseurl + username + "/" + indexname)
|
||||
assert response.status_code == 200
|
||||
result = response.json()["result"]
|
||||
return result["projects"]
|
||||
|
||||
|
||||
def get_release_dates(baseurl, username, indexname, projectname):
|
||||
response = session.get(baseurl + username + "/" + indexname + "/" + projectname)
|
||||
assert response.status_code == 200
|
||||
result = response.json()["result"]
|
||||
dates = set()
|
||||
for value in result.values():
|
||||
if "+links" not in value:
|
||||
continue
|
||||
for link in value["+links"]:
|
||||
for log in link["log"]:
|
||||
dates.add(tuple(log["when"]))
|
||||
return dates
|
||||
|
||||
|
||||
def run():
|
||||
baseurl = "https://m.devpi.net/"
|
||||
username = "dc"
|
||||
for indexname in get_indexes(baseurl, username):
|
||||
projectnames = get_projectnames(baseurl, username, indexname)
|
||||
if indexname == "master" or not indexname:
|
||||
continue
|
||||
assert projectnames == ["deltachat"]
|
||||
for projectname in projectnames:
|
||||
dates = get_release_dates(baseurl, username, indexname, projectname)
|
||||
if not dates:
|
||||
print(
|
||||
"%s has no releases" % (baseurl + username + "/" + indexname),
|
||||
file=sys.stderr)
|
||||
date = datetime.datetime.now()
|
||||
else:
|
||||
date = datetime.datetime(*max(dates))
|
||||
if (datetime.datetime.now() - date) > datetime.timedelta(days=MAXDAYS):
|
||||
assert username and indexname
|
||||
url = baseurl + username + "/" + indexname
|
||||
subprocess.check_call(["devpi", "index", "-y", "--delete", url])
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -5,14 +5,11 @@ RUN echo /usr/local/lib64 > /etc/ld.so.conf.d/local.conf && \
|
||||
echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf
|
||||
ENV PKG_CONFIG_PATH /usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig
|
||||
|
||||
# Install a recent Perl, needed to install OpenSSL
|
||||
# Install a recent Perl, needed to install the openssl crate
|
||||
ADD deps/build_perl.sh /builder/build_perl.sh
|
||||
RUN rm /usr/bin/perl
|
||||
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1
|
||||
|
||||
# Install OpenSSL
|
||||
ADD deps/build_openssl.sh /builder/build_openssl.sh
|
||||
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_openssl.sh && cd .. && rm -r tmp1
|
||||
|
||||
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
|
||||
|
||||
# Install python tools (auditwheels,tox, ...)
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
set -e -x
|
||||
|
||||
# Install Rust
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-09-12 -y
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-11-06 -y
|
||||
export PATH=/root/.cargo/bin:$PATH
|
||||
rustc --version
|
||||
|
||||
# remove some 300-400 MB that we don't need for automated builds
|
||||
rm -rf /root/.rustup/toolchains/nightly-2019-11-06-x86_64-unknown-linux-gnu/share/
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Build the Delta Chat C/Rust library
|
||||
#
|
||||
set -e -x
|
||||
|
||||
# perform clean build of core and install
|
||||
export TOXWORKDIR=.docker-tox
|
||||
|
||||
# build core library
|
||||
|
||||
cargo build --release -p deltachat_ffi
|
||||
|
||||
# configure access to a base python and
|
||||
# to several python interpreters needed by tox below
|
||||
export PATH=$PATH:/opt/python/cp35-cp35m/bin
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
pushd /bin
|
||||
ln -s /opt/python/cp27-cp27m/bin/python2.7
|
||||
ln -s /opt/python/cp36-cp36m/bin/python3.6
|
||||
ln -s /opt/python/cp37-cp37m/bin/python3.7
|
||||
popd
|
||||
|
||||
#
|
||||
# run python tests
|
||||
#
|
||||
|
||||
if [ -n "$TESTS" ]; then
|
||||
|
||||
echo ----------------
|
||||
echo run python tests
|
||||
echo ----------------
|
||||
|
||||
pushd python
|
||||
# first run all tests ...
|
||||
rm -rf tests/__pycache__
|
||||
rm -rf src/deltachat/__pycache__
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
tox --workdir "$TOXWORKDIR" -e py27,py35,py36,py37
|
||||
popd
|
||||
fi
|
||||
|
||||
|
||||
if [ -n "$DOCS" ]; then
|
||||
echo -----------------------
|
||||
echo generating python docs
|
||||
echo -----------------------
|
||||
(cd python && tox --workdir "$TOXWORKDIR" -e doc)
|
||||
fi
|
||||
51
ci_scripts/remote_python_packaging.sh
Executable file
51
ci_scripts/remote_python_packaging.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
|
||||
export BRANCH=${CIRCLE_BRANCH:?branch to build}
|
||||
export REPONAME=${CIRCLE_PROJECT_REPONAME:?repository name}
|
||||
export SSHTARGET=${SSHTARGET-ci@b1.delta.chat}
|
||||
|
||||
# we construct the BUILDDIR such that we can easily share the
|
||||
# CARGO_TARGET_DIR between runs ("..")
|
||||
export BUILDDIR=ci_builds/$REPONAME/$BRANCH/${CIRCLE_JOB:?jobname}/${CIRCLE_BUILD_NUM:?circle-build-number}
|
||||
|
||||
echo "--- Copying files to $SSHTARGET:$BUILDDIR"
|
||||
|
||||
set -xe
|
||||
|
||||
ssh -oBatchMode=yes -oStrictHostKeyChecking=no $SSHTARGET mkdir -p "$BUILDDIR"
|
||||
git ls-files >.rsynclist
|
||||
# we seem to need .git for setuptools_scm versioning
|
||||
find .git >>.rsynclist
|
||||
rsync --delete --files-from=.rsynclist -az ./ "$SSHTARGET:$BUILDDIR"
|
||||
|
||||
set +x
|
||||
|
||||
# we have to create a remote file for the remote-docker run
|
||||
# so we can do a simple ssh command with a TTY
|
||||
# so that when our job dies, all container-runs are aborted.
|
||||
# sidenote: the circle-ci machinery will kill ongoing jobs
|
||||
# if there are new commits and we want to ensure that
|
||||
# everything is terminated/cleaned up and we have no orphaned
|
||||
# useless still-running docker-containers consuming resources.
|
||||
|
||||
ssh $SSHTARGET bash -c "cat >$BUILDDIR/exec_docker_run" <<_HERE
|
||||
set +x -e
|
||||
cd $BUILDDIR
|
||||
export DCC_PY_LIVECONFIG=$DCC_PY_LIVECONFIG
|
||||
|
||||
set -x
|
||||
|
||||
# run everything else inside docker
|
||||
docker run -e DCC_PY_LIVECONFIG \
|
||||
--rm -it -v \$(pwd):/mnt -w /mnt \
|
||||
deltachat/coredeps ci_scripts/run_all.sh
|
||||
|
||||
_HERE
|
||||
|
||||
echo "--- Running $CIRCLE_JOB remotely"
|
||||
|
||||
ssh -t $SSHTARGET bash "$BUILDDIR/exec_docker_run"
|
||||
mkdir -p workspace
|
||||
rsync -avz "$SSHTARGET:$BUILDDIR/python/.docker-tox/wheelhouse" workspace/
|
||||
rsync -avz "$SSHTARGET:$BUILDDIR/python/.docker-tox/dist/*" workspace/wheelhouse/
|
||||
rsync -avz "$SSHTARGET:$BUILDDIR/python/doc/_build/" workspace/py-docs
|
||||
@@ -3,5 +3,4 @@
|
||||
set -ex
|
||||
|
||||
cd deltachat-ffi
|
||||
doxygen
|
||||
|
||||
PROJECT_NUMBER=$(git log -1 --format "%h (%cd)") doxygen
|
||||
|
||||
48
ci_scripts/run_all.sh
Executable file
48
ci_scripts/run_all.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Build the Delta Chat Core Rust library, Python wheels and docs
|
||||
|
||||
set -e -x
|
||||
|
||||
# Perform clean build of core and install.
|
||||
export TOXWORKDIR=.docker-tox
|
||||
|
||||
# compile core lib
|
||||
|
||||
export PATH=/root/.cargo/bin:$PATH
|
||||
cargo build --release -p deltachat_ffi
|
||||
# cargo test --all --all-features
|
||||
|
||||
# Statically link against libdeltachat.a.
|
||||
export DCC_RS_DEV=$(pwd)
|
||||
|
||||
# Configure access to a base python and to several python interpreters
|
||||
# needed by tox below.
|
||||
export PATH=$PATH:/opt/python/cp35-cp35m/bin
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
pushd /bin
|
||||
ln -s /opt/python/cp27-cp27m/bin/python2.7
|
||||
ln -s /opt/python/cp36-cp36m/bin/python3.6
|
||||
ln -s /opt/python/cp37-cp37m/bin/python3.7
|
||||
ln -s /opt/python/cp38-cp38/bin/python3.8
|
||||
popd
|
||||
|
||||
pushd python
|
||||
# prepare a clean tox run
|
||||
rm -rf tests/__pycache__
|
||||
rm -rf src/deltachat/__pycache__
|
||||
mkdir -p $TOXWORKDIR
|
||||
|
||||
# disable live-account testing to speed up test runs and wheel building
|
||||
# XXX we may switch on some live-tests on for better ensurances
|
||||
# Note that the independent remote_tests_python step does all kinds of
|
||||
# live-testing already.
|
||||
unset DCC_PY_LIVECONFIG
|
||||
tox --workdir "$TOXWORKDIR" -e py35,py36,py37,py38,auditwheels
|
||||
popd
|
||||
|
||||
|
||||
echo -----------------------
|
||||
echo generating python docs
|
||||
echo -----------------------
|
||||
(cd python && tox --workdir "$TOXWORKDIR" -e doc)
|
||||
@@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.0.0-beta.22"
|
||||
version = "1.0.0-beta.23"
|
||||
description = "Deltachat FFI"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
license = "MPL-2.0"
|
||||
|
||||
keywords = ["deltachat", "chat", "openpgp", "email", "encryption"]
|
||||
categories = ["cryptography", "std", "email"]
|
||||
|
||||
@@ -2989,15 +2989,15 @@ int dc_msg_get_viewtype (const dc_msg_t* msg);
|
||||
* Get the state of a message.
|
||||
*
|
||||
* Incoming message states:
|
||||
* - DC_STATE_IN_FRESH (10) - Incoming _fresh_ message. Fresh messages are not noticed nor seen and are typically shown in notifications. Use dc_get_fresh_msgs() to get all fresh messages.
|
||||
* - DC_STATE_IN_NOTICED (13) - Incoming _noticed_ message. Eg. chat opened but message not yet read - noticed messages are not counted as unread but did not marked as read nor resulted in MDNs. Use dc_marknoticed_chat() or dc_marknoticed_contact() to mark messages as being noticed.
|
||||
* - DC_STATE_IN_SEEN (16) - Incoming message, really _seen_ by the user. Marked as read on IMAP and MDN may be send. Use dc_markseen_msgs() to mark messages as being seen.
|
||||
* - DC_STATE_IN_FRESH (10) - Incoming _fresh_ message. Fresh messages are neither noticed nor seen and are typically shown in notifications. Use dc_get_fresh_msgs() to get all fresh messages.
|
||||
* - DC_STATE_IN_NOTICED (13) - Incoming _noticed_ message. E.g. chat opened but message not yet read - noticed messages are not counted as unread but did not marked as read nor resulted in MDNs. Use dc_marknoticed_chat() or dc_marknoticed_contact() to mark messages as being noticed.
|
||||
* - DC_STATE_IN_SEEN (16) - Incoming message, really _seen_ by the user. Marked as read on IMAP and MDN may be sent. Use dc_markseen_msgs() to mark messages as being seen.
|
||||
*
|
||||
* Outgoing message states:
|
||||
* - DC_STATE_OUT_PREPARING (18) - For files which need time to be prepared before they can be sent,
|
||||
* the message enters this state before DC_STATE_OUT_PENDING.
|
||||
* - DC_STATE_OUT_DRAFT (19) - Message saved as draft using dc_set_draft()
|
||||
* - DC_STATE_OUT_PENDING (20) - The user has send the "send" button but the
|
||||
* - DC_STATE_OUT_PENDING (20) - The user has pressed the "send" button but the
|
||||
* message is not yet sent and is pending in some way. Maybe we're offline (no checkmark).
|
||||
* - DC_STATE_OUT_FAILED (24) - _Unrecoverable_ error (_recoverable_ errors result in pending messages), you'll receive the event #DC_EVENT_MSG_FAILED.
|
||||
* - DC_STATE_OUT_DELIVERED (26) - Outgoing message successfully delivered to server (one checkmark). Note, that already delivered messages may get into the state DC_STATE_OUT_FAILED if we get such a hint from the server.
|
||||
|
||||
@@ -24,6 +24,7 @@ use std::sync::RwLock;
|
||||
use libc::uintptr_t;
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
|
||||
use deltachat::chat::ChatId;
|
||||
use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
|
||||
use deltachat::contact::Contact;
|
||||
use deltachat::context::Context;
|
||||
@@ -151,12 +152,12 @@ impl ContextWrapper {
|
||||
ffi_cb(
|
||||
self,
|
||||
event_id,
|
||||
chat_id as uintptr_t,
|
||||
chat_id.to_u32() as uintptr_t,
|
||||
msg_id.to_u32() as uintptr_t,
|
||||
);
|
||||
}
|
||||
Event::ChatModified(chat_id) => {
|
||||
ffi_cb(self, event_id, chat_id as uintptr_t, 0);
|
||||
ffi_cb(self, event_id, chat_id.to_u32() as uintptr_t, 0);
|
||||
}
|
||||
Event::ContactsChanged(id) | Event::LocationChanged(id) => {
|
||||
let id = id.unwrap_or_default();
|
||||
@@ -191,7 +192,7 @@ impl ContextWrapper {
|
||||
ffi_cb(
|
||||
self,
|
||||
event_id,
|
||||
chat_id as uintptr_t,
|
||||
chat_id.to_u32() as uintptr_t,
|
||||
contact_id as uintptr_t,
|
||||
);
|
||||
}
|
||||
@@ -707,7 +708,9 @@ pub unsafe extern "C" fn dc_create_chat_by_msg_id(context: *mut dc_context_t, ms
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::create_by_msg_id(ctx, MsgId::new(msg_id))
|
||||
.unwrap_or_log_default(ctx, "Failed to create chat")
|
||||
.log_err(ctx, "Failed to create chat from msg_id")
|
||||
.map(|id| id.to_u32())
|
||||
.unwrap_or(0)
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
@@ -725,7 +728,9 @@ pub unsafe extern "C" fn dc_create_chat_by_contact_id(
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::create_by_contact_id(ctx, contact_id)
|
||||
.unwrap_or_log_default(ctx, "Failed to create chat")
|
||||
.log_err(ctx, "Failed to create chat from contact_id")
|
||||
.map(|id| id.to_u32())
|
||||
.unwrap_or(0)
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
@@ -741,7 +746,12 @@ pub unsafe extern "C" fn dc_get_chat_id_by_contact_id(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| chat::get_by_contact_id(ctx, contact_id).unwrap_or(0))
|
||||
.with_inner(|ctx| {
|
||||
chat::get_by_contact_id(ctx, contact_id)
|
||||
.log_err(ctx, "Failed to get chat for contact_id")
|
||||
.map(|id| id.to_u32())
|
||||
.unwrap_or(0)
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
@@ -759,7 +769,7 @@ pub unsafe extern "C" fn dc_prepare_msg(
|
||||
let ffi_msg: &mut MessageWrapper = &mut *msg;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::prepare_msg(ctx, chat_id, &mut ffi_msg.message)
|
||||
chat::prepare_msg(ctx, ChatId::new(chat_id), &mut ffi_msg.message)
|
||||
.unwrap_or_log_default(ctx, "Failed to prepare message")
|
||||
})
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
@@ -780,7 +790,7 @@ pub unsafe extern "C" fn dc_send_msg(
|
||||
let ffi_msg = &mut *msg;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::send_msg(ctx, chat_id, &mut ffi_msg.message)
|
||||
chat::send_msg(ctx, ChatId::new(chat_id), &mut ffi_msg.message)
|
||||
.unwrap_or_log_default(ctx, "Failed to send message")
|
||||
})
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
@@ -801,7 +811,7 @@ pub unsafe extern "C" fn dc_send_text_msg(
|
||||
let text_to_send = to_string_lossy(text_to_send);
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::send_text_msg(ctx, chat_id, text_to_send)
|
||||
chat::send_text_msg(ctx, ChatId::new(chat_id), text_to_send)
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.unwrap_or_log_default(ctx, "Failed to send text message")
|
||||
})
|
||||
@@ -826,7 +836,7 @@ pub unsafe extern "C" fn dc_set_draft(
|
||||
Some(&mut ffi_msg.message)
|
||||
};
|
||||
ffi_context
|
||||
.with_inner(|ctx| chat::set_draft(ctx, chat_id, msg))
|
||||
.with_inner(|ctx| chat::set_draft(ctx, ChatId::new(chat_id), msg))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
@@ -901,7 +911,7 @@ pub unsafe extern "C" fn dc_get_draft(context: *mut dc_context_t, chat_id: u32)
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| match chat::get_draft(ctx, chat_id) {
|
||||
.with_inner(|ctx| match chat::get_draft(ctx, ChatId::new(chat_id)) {
|
||||
Ok(Some(draft)) => {
|
||||
let ffi_msg = MessageWrapper {
|
||||
context,
|
||||
@@ -938,7 +948,7 @@ pub unsafe extern "C" fn dc_get_chat_msgs(
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
let arr = dc_array_t::from(
|
||||
chat::get_chat_msgs(ctx, chat_id, flags, marker_flag)
|
||||
chat::get_chat_msgs(ctx, ChatId::new(chat_id), flags, marker_flag)
|
||||
.iter()
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.collect::<Vec<u32>>(),
|
||||
@@ -956,7 +966,7 @@ pub unsafe extern "C" fn dc_get_msg_cnt(context: *mut dc_context_t, chat_id: u32
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| chat::get_msg_cnt(ctx, chat_id) as libc::c_int)
|
||||
.with_inner(|ctx| chat::get_msg_cnt(ctx, ChatId::new(chat_id)) as libc::c_int)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
@@ -971,7 +981,7 @@ pub unsafe extern "C" fn dc_get_fresh_msg_cnt(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| chat::get_fresh_msg_cnt(ctx, chat_id) as libc::c_int)
|
||||
.with_inner(|ctx| chat::get_fresh_msg_cnt(ctx, ChatId::new(chat_id)) as libc::c_int)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
@@ -1006,7 +1016,9 @@ pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::marknoticed_chat(ctx, chat_id).log_err(ctx, "Failed marknoticed chat")
|
||||
chat::marknoticed_chat(ctx, ChatId::new(chat_id))
|
||||
.log_err(ctx, "Failed marknoticed chat")
|
||||
.unwrap_or(())
|
||||
})
|
||||
.unwrap_or(())
|
||||
}
|
||||
@@ -1020,7 +1032,9 @@ pub unsafe extern "C" fn dc_marknoticed_all_chats(context: *mut dc_context_t) {
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::marknoticed_all_chats(ctx).log_err(ctx, "Failed marknoticed all chats")
|
||||
chat::marknoticed_all_chats(ctx)
|
||||
.log_err(ctx, "Failed marknoticed all chats")
|
||||
.unwrap_or(())
|
||||
})
|
||||
.unwrap_or(())
|
||||
}
|
||||
@@ -1054,10 +1068,16 @@ pub unsafe extern "C" fn dc_get_chat_media(
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
let arr = dc_array_t::from(
|
||||
chat::get_chat_media(ctx, chat_id, msg_type, or_msg_type2, or_msg_type3)
|
||||
.iter()
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.collect::<Vec<u32>>(),
|
||||
chat::get_chat_media(
|
||||
ctx,
|
||||
ChatId::new(chat_id),
|
||||
msg_type,
|
||||
or_msg_type2,
|
||||
or_msg_type3,
|
||||
)
|
||||
.iter()
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.collect::<Vec<u32>>(),
|
||||
);
|
||||
Box::into_raw(Box::new(arr))
|
||||
})
|
||||
@@ -1124,7 +1144,11 @@ pub unsafe extern "C" fn dc_archive_chat(
|
||||
return;
|
||||
};
|
||||
ffi_context
|
||||
.with_inner(|ctx| chat::archive(ctx, chat_id, archive).log_err(ctx, "Failed archive chat"))
|
||||
.with_inner(|ctx| {
|
||||
chat::archive(ctx, ChatId::new(chat_id), archive)
|
||||
.log_err(ctx, "Failed archive chat")
|
||||
.unwrap_or(())
|
||||
})
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
@@ -1136,7 +1160,11 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| chat::delete(ctx, chat_id).log_err(ctx, "Failed chat delete"))
|
||||
.with_inner(|ctx| {
|
||||
chat::delete(ctx, ChatId::new(chat_id))
|
||||
.log_err(ctx, "Failed chat delete")
|
||||
.unwrap_or(())
|
||||
})
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
@@ -1152,7 +1180,7 @@ pub unsafe extern "C" fn dc_get_chat_contacts(
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
let arr = dc_array_t::from(chat::get_chat_contacts(ctx, chat_id));
|
||||
let arr = dc_array_t::from(chat::get_chat_contacts(ctx, ChatId::new(chat_id)));
|
||||
Box::into_raw(Box::new(arr))
|
||||
})
|
||||
.unwrap_or_else(|_| ptr::null_mut())
|
||||
@@ -1172,7 +1200,7 @@ pub unsafe extern "C" fn dc_search_msgs(
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
let arr = dc_array_t::from(
|
||||
ctx.search_msgs(chat_id, to_string_lossy(query))
|
||||
ctx.search_msgs(ChatId::new(chat_id), to_string_lossy(query))
|
||||
.iter()
|
||||
.map(|msg_id| msg_id.to_u32())
|
||||
.collect::<Vec<u32>>(),
|
||||
@@ -1190,13 +1218,15 @@ pub unsafe extern "C" fn dc_get_chat(context: *mut dc_context_t, chat_id: u32) -
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| match chat::Chat::load_from_db(ctx, chat_id) {
|
||||
Ok(chat) => {
|
||||
let ffi_chat = ChatWrapper { context, chat };
|
||||
Box::into_raw(Box::new(ffi_chat))
|
||||
}
|
||||
Err(_) => ptr::null_mut(),
|
||||
})
|
||||
.with_inner(
|
||||
|ctx| match chat::Chat::load_from_db(ctx, ChatId::new(chat_id)) {
|
||||
Ok(chat) => {
|
||||
let ffi_chat = ChatWrapper { context, chat };
|
||||
Box::into_raw(Box::new(ffi_chat))
|
||||
}
|
||||
Err(_) => ptr::null_mut(),
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|_| ptr::null_mut())
|
||||
}
|
||||
|
||||
@@ -1219,7 +1249,9 @@ pub unsafe extern "C" fn dc_create_group_chat(
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::create_group_chat(ctx, verified, to_string_lossy(name))
|
||||
.unwrap_or_log_default(ctx, "Failed to create group chat")
|
||||
.log_err(ctx, "Failed to create group chat")
|
||||
.map(|id| id.to_u32())
|
||||
.unwrap_or(0)
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
@@ -1236,7 +1268,7 @@ pub unsafe extern "C" fn dc_is_contact_in_chat(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| chat::is_contact_in_chat(ctx, chat_id, contact_id))
|
||||
.with_inner(|ctx| chat::is_contact_in_chat(ctx, ChatId::new(chat_id), contact_id))
|
||||
.unwrap_or_default()
|
||||
.into()
|
||||
}
|
||||
@@ -1253,7 +1285,9 @@ pub unsafe extern "C" fn dc_add_contact_to_chat(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| chat::add_contact_to_chat(ctx, chat_id, contact_id) as libc::c_int)
|
||||
.with_inner(|ctx| {
|
||||
chat::add_contact_to_chat(ctx, ChatId::new(chat_id), contact_id) as libc::c_int
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
@@ -1270,7 +1304,7 @@ pub unsafe extern "C" fn dc_remove_contact_from_chat(
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::remove_contact_from_chat(ctx, chat_id, contact_id)
|
||||
chat::remove_contact_from_chat(ctx, ChatId::new(chat_id), contact_id)
|
||||
.map(|_| 1)
|
||||
.unwrap_or_log_default(ctx, "Failed to remove contact")
|
||||
})
|
||||
@@ -1290,7 +1324,7 @@ pub unsafe extern "C" fn dc_set_chat_name(
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::set_chat_name(ctx, chat_id, to_string_lossy(name))
|
||||
chat::set_chat_name(ctx, ChatId::new(chat_id), to_string_lossy(name))
|
||||
.map(|_| 1)
|
||||
.unwrap_or_log_default(ctx, "Failed to set chat name")
|
||||
})
|
||||
@@ -1310,7 +1344,7 @@ pub unsafe extern "C" fn dc_set_chat_profile_image(
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::set_chat_profile_image(ctx, chat_id, to_string_lossy(image))
|
||||
chat::set_chat_profile_image(ctx, ChatId::new(chat_id), to_string_lossy(image))
|
||||
.map(|_| 1)
|
||||
.unwrap_or_log_default(ctx, "Failed to set profile image")
|
||||
})
|
||||
@@ -1399,7 +1433,7 @@ pub unsafe extern "C" fn dc_forward_msgs(
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::forward_msgs(ctx, &msg_ids[..], chat_id)
|
||||
chat::forward_msgs(ctx, &msg_ids[..], ChatId::new(chat_id))
|
||||
.unwrap_or_log_default(ctx, "Failed to forward message")
|
||||
})
|
||||
.unwrap_or_default()
|
||||
@@ -1812,7 +1846,7 @@ pub unsafe extern "C" fn dc_get_securejoin_qr(
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
securejoin::dc_get_securejoin_qr(ctx, chat_id)
|
||||
securejoin::dc_get_securejoin_qr(ctx, ChatId::new(chat_id))
|
||||
.unwrap_or_else(|| "".to_string())
|
||||
.strdup()
|
||||
})
|
||||
@@ -1830,7 +1864,7 @@ pub unsafe extern "C" fn dc_join_securejoin(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| securejoin::dc_join_securejoin(ctx, &to_string_lossy(qr)))
|
||||
.with_inner(|ctx| securejoin::dc_join_securejoin(ctx, &to_string_lossy(qr)).to_u32())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
@@ -1846,7 +1880,9 @@ pub unsafe extern "C" fn dc_send_locations_to_chat(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| location::send_locations_to_chat(ctx, chat_id, seconds as i64))
|
||||
.with_inner(|ctx| {
|
||||
location::send_locations_to_chat(ctx, ChatId::new(chat_id), seconds as i64)
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
@@ -1861,7 +1897,9 @@ pub unsafe extern "C" fn dc_is_sending_locations_to_chat(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| location::is_sending_locations_to_chat(ctx, chat_id) as libc::c_int)
|
||||
.with_inner(|ctx| {
|
||||
location::is_sending_locations_to_chat(ctx, ChatId::new(chat_id)) as libc::c_int
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
@@ -1899,7 +1937,7 @@ pub unsafe extern "C" fn dc_get_locations(
|
||||
.with_inner(|ctx| {
|
||||
let res = location::get_range(
|
||||
ctx,
|
||||
chat_id,
|
||||
ChatId::new(chat_id),
|
||||
contact_id,
|
||||
timestamp_begin as i64,
|
||||
timestamp_end as i64,
|
||||
@@ -2021,8 +2059,7 @@ pub unsafe extern "C" fn dc_array_get_chat_id(
|
||||
eprintln!("ignoring careless call to dc_array_get_chat_id()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
(*array).get_location(index).chat_id
|
||||
(*array).get_location(index).chat_id.to_u32()
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_contact_id(
|
||||
@@ -2159,7 +2196,7 @@ pub unsafe extern "C" fn dc_chatlist_get_chat_id(
|
||||
return 0;
|
||||
}
|
||||
let ffi_list = &*chatlist;
|
||||
ffi_list.list.get_chat_id(index as usize)
|
||||
ffi_list.list.get_chat_id(index as usize).to_u32()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -2251,7 +2288,7 @@ pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 {
|
||||
return 0;
|
||||
}
|
||||
let ffi_chat = &*chat;
|
||||
ffi_chat.chat.get_id()
|
||||
ffi_chat.chat.get_id().to_u32()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -2398,7 +2435,7 @@ pub unsafe extern "C" fn dc_chat_get_info_json(
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
let chat = match chat::Chat::load_from_db(ctx, chat_id) {
|
||||
let chat = match chat::Chat::load_from_db(ctx, ChatId::new(chat_id)) {
|
||||
Ok(chat) => chat,
|
||||
Err(err) => {
|
||||
error!(ctx, "dc_get_chat_info_json() failed to load chat: {}", err);
|
||||
@@ -2494,7 +2531,7 @@ pub unsafe extern "C" fn dc_msg_get_chat_id(msg: *mut dc_msg_t) -> u32 {
|
||||
return 0;
|
||||
}
|
||||
let ffi_msg = &*msg;
|
||||
ffi_msg.message.get_chat_id()
|
||||
ffi_msg.message.get_chat_id().to_u32()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -3119,12 +3156,12 @@ pub unsafe extern "C" fn dc_str_unref(s: *mut libc::c_char) {
|
||||
|
||||
pub mod providers;
|
||||
|
||||
pub trait ResultExt<T> {
|
||||
pub trait ResultExt<T, E> {
|
||||
fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T;
|
||||
fn log_err(&self, context: &context::Context, message: &str);
|
||||
fn log_err(self, context: &context::Context, message: &str) -> Result<T, E>;
|
||||
}
|
||||
|
||||
impl<T: Default, E: std::fmt::Display> ResultExt<T> for Result<T, E> {
|
||||
impl<T: Default, E: std::fmt::Display> ResultExt<T, E> for Result<T, E> {
|
||||
fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T {
|
||||
match self {
|
||||
Ok(t) => t,
|
||||
@@ -3135,10 +3172,11 @@ impl<T: Default, E: std::fmt::Display> ResultExt<T> for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
fn log_err(&self, context: &context::Context, message: &str) {
|
||||
if let Err(err) = self {
|
||||
error!(context, "{}: {}", message, err);
|
||||
}
|
||||
fn log_err(self, context: &context::Context, message: &str) -> Result<T, E> {
|
||||
self.map_err(|err| {
|
||||
warn!(context, "{}: {}", message, err);
|
||||
err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
[package]
|
||||
name = "deltachat_derive"
|
||||
version = "0.1.0"
|
||||
authors = ["Dmitry Bogatov <KAction@debian.org>"]
|
||||
version = "1.0.0-beta.22"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "0.14.4"
|
||||
quote = "0.6.3"
|
||||
syn = "1.0.13"
|
||||
quote = "1.0.2"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use deltachat::chat::{self, Chat};
|
||||
use deltachat::chat::{self, Chat, ChatId};
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::config;
|
||||
use deltachat::constants::*;
|
||||
@@ -84,7 +84,7 @@ fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
||||
}
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
|
||||
@@ -166,7 +166,7 @@ fn poke_spec(context: &Context, spec: Option<&str>) -> libc::c_int {
|
||||
println!("Import: {} items read from \"{}\".", read_cnt, &real_spec);
|
||||
if read_cnt > 0 {
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
@@ -299,7 +299,7 @@ fn chat_prefix(chat: &Chat) -> &'static str {
|
||||
|
||||
pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
let chat_id = *context.cmdline_sel_chat_id.read().unwrap();
|
||||
let mut sel_chat = if chat_id > 0 {
|
||||
let mut sel_chat = if !chat_id.is_unset() {
|
||||
Chat::load_from_db(context, chat_id).ok()
|
||||
} else {
|
||||
None
|
||||
@@ -549,7 +549,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
);
|
||||
}
|
||||
}
|
||||
if location::is_sending_locations_to_chat(context, 0) {
|
||||
if location::is_sending_locations_to_chat(context, ChatId::new(0)) {
|
||||
println!("Location streaming enabled.");
|
||||
}
|
||||
println!("{} chats", cnt);
|
||||
@@ -559,8 +559,8 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
bail!("Argument [chat-id] is missing.");
|
||||
}
|
||||
if !arg1.is_empty() {
|
||||
let chat_id = arg1.parse()?;
|
||||
println!("Selecting chat #{}", chat_id);
|
||||
let chat_id = ChatId::new(arg1.parse()?);
|
||||
println!("Selecting chat {}", chat_id);
|
||||
sel_chat = Some(Chat::load_from_db(context, chat_id)?);
|
||||
*context.cmdline_sel_chat_id.write().unwrap() = chat_id;
|
||||
}
|
||||
@@ -787,7 +787,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
let chat = if let Some(ref sel_chat) = sel_chat {
|
||||
sel_chat.get_id()
|
||||
} else {
|
||||
0 as libc::c_uint
|
||||
ChatId::new(0)
|
||||
};
|
||||
|
||||
let msglist = context.search_msgs(chat, arg1);
|
||||
@@ -846,12 +846,12 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
}
|
||||
"archive" | "unarchive" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||
let chat_id = arg1.parse()?;
|
||||
let chat_id = ChatId::new(arg1.parse()?);
|
||||
chat::archive(context, chat_id, arg0 == "archive")?;
|
||||
}
|
||||
"delchat" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||
let chat_id = arg1.parse()?;
|
||||
let chat_id = ChatId::new(arg1.parse()?);
|
||||
chat::delete(context, chat_id)?;
|
||||
}
|
||||
"msginfo" => {
|
||||
@@ -873,7 +873,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
);
|
||||
|
||||
let mut msg_ids = [MsgId::new(0); 1];
|
||||
let chat_id = arg2.parse()?;
|
||||
let chat_id = ChatId::new(arg2.parse()?);
|
||||
msg_ids[0] = MsgId::new(arg1.parse()?);
|
||||
chat::forward_msgs(context, &msg_ids, chat_id)?;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use std::process::Command;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use deltachat::chat::ChatId;
|
||||
use deltachat::config;
|
||||
use deltachat::configure::*;
|
||||
use deltachat::context::*;
|
||||
@@ -484,9 +485,10 @@ fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, failu
|
||||
}
|
||||
"getqr" | "getbadqr" => {
|
||||
start_threads(ctx.clone());
|
||||
if let Some(mut qr) =
|
||||
dc_get_securejoin_qr(&ctx.read().unwrap(), arg1.parse().unwrap_or_default())
|
||||
{
|
||||
if let Some(mut qr) = dc_get_securejoin_qr(
|
||||
&ctx.read().unwrap(),
|
||||
ChatId::new(arg1.parse().unwrap_or_default()),
|
||||
) {
|
||||
if !qr.is_empty() {
|
||||
if arg0 == "getbadqr" && qr.len() > 40 {
|
||||
qr.replace_range(12..22, "0000000000")
|
||||
|
||||
@@ -75,42 +75,31 @@ With ``DCC_PY_LIVECONFIG`` set pytest invocations will use real
|
||||
e-mail accounts and run through all functional "liveconfig" tests.
|
||||
|
||||
|
||||
============================================================================================================================
|
||||
(21-Dec-2019) THE BELOW WHEELS ARE CURRENTLY NOT WORKING/BROKEN, COMPILE FROM SOURCE USING ABOVE INSTRUCTIONS INSTEAD
|
||||
============================================================================================================================
|
||||
|
||||
Installing pre-built packages (linux-only) (OUTDATED)
|
||||
Installing pre-built packages (Linux-only)
|
||||
========================================================
|
||||
|
||||
If you have a linux system you may install the ``deltachat`` binary "wheel" package
|
||||
If you have a Linux system you may try to install the ``deltachat`` binary "wheel" packages
|
||||
without any "build-from-source" steps.
|
||||
|
||||
1. `Install virtualenv <https://virtualenv.pypa.io/en/stable/installation/>`_,
|
||||
First of all we suggest to `Install virtualenv <https://virtualenv.pypa.io/en/stable/installation/>`_,
|
||||
then create a fresh python environment and activate it in your shell::
|
||||
|
||||
virtualenv venv # or: python -m venv
|
||||
source venv/bin/activate
|
||||
|
||||
Afterwards, invoking ``python`` or ``pip install`` will only
|
||||
modify files in your ``venv`` directory and leave your system installation
|
||||
alone.
|
||||
|
||||
2. Install the wheel for linux::
|
||||
|
||||
pip install deltachat
|
||||
|
||||
Verify it worked by typing::
|
||||
|
||||
python -c "import deltachat"
|
||||
|
||||
|
||||
Installing a wheel from a PR/branch (OUTDATED)
|
||||
-------------------------------------------------
|
||||
Afterwards, invoking ``python`` or ``pip install`` will only
|
||||
modify files in your ``venv`` directory and leave your system installation
|
||||
alone.
|
||||
|
||||
For Linux, we automatically build wheels for all github PR branches
|
||||
and push them to a python package index. To install the latest github ``master`` branch::
|
||||
and push them to a python package index. To install the latest
|
||||
github ``master`` branch::
|
||||
|
||||
pip install -i https://m.devpi.net/dc/master deltachat
|
||||
pip install --pre -i https://m.devpi.net/dc/master deltachat
|
||||
|
||||
To verify it worked::
|
||||
|
||||
python -c "import deltachat"
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -119,10 +108,6 @@ and push them to a python package index. To install the latest github ``master``
|
||||
`in contact with us <https://delta.chat/en/contribute>`_.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Code examples
|
||||
=============
|
||||
|
||||
@@ -133,15 +118,11 @@ You may look at `examples <https://py.delta.chat/examples.html>`_.
|
||||
.. _`deltachat-core`: https://github.com/deltachat/deltachat-core-rust
|
||||
|
||||
|
||||
Building manylinux1 wheels
|
||||
==========================
|
||||
|
||||
.. note::
|
||||
|
||||
This section may not fully work.
|
||||
Building manylinux1 based wheels
|
||||
================================
|
||||
|
||||
Building portable manylinux1 wheels which come with libdeltachat.so
|
||||
and all it's dependencies is easy using the provided docker tooling.
|
||||
can be done with docker-tooling.
|
||||
|
||||
using docker pull / premade images
|
||||
------------------------------------
|
||||
@@ -154,9 +135,9 @@ organization::
|
||||
|
||||
This docker image can be used to run tests and build Python wheels for all interpreters::
|
||||
|
||||
$ bash ci_scripts/ci_run.sh
|
||||
|
||||
This command runs tests and build-wheel scripts in a docker container.
|
||||
$ docker run -e DCC_PY_LIVECONFIG \
|
||||
--rm -it -v \$(pwd):/mnt -w /mnt \
|
||||
deltachat/coredeps ci_scripts/run_all.sh
|
||||
|
||||
|
||||
Optionally build your own docker image
|
||||
|
||||
@@ -678,14 +678,26 @@ class TestOnlineAccount:
|
||||
chat2b.mark_noticed()
|
||||
assert chat2b.count_fresh_messages() == 0
|
||||
|
||||
lp.sec("mark message as seen on ac2, wait for changes on ac1")
|
||||
ac2.mark_seen_messages([msg_in])
|
||||
ac2._evlogger.consume_events()
|
||||
|
||||
lp.sec("sending a second message from ac1 to ac2")
|
||||
msg_out2 = chat.send_text("message2")
|
||||
|
||||
lp.sec("wait for ac2 to receive second message")
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
|
||||
assert ev[2] == msg_out2.id
|
||||
msg_in2 = ac2.get_message_by_id(msg_out2.id)
|
||||
|
||||
lp.sec("mark messages as seen on ac2, wait for changes on ac1")
|
||||
ac2.mark_seen_messages([msg_in, msg_in2])
|
||||
lp.step("1")
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_READ")
|
||||
assert ev[1] > const.DC_CHAT_ID_LAST_SPECIAL
|
||||
assert ev[2] > const.DC_MSG_ID_LAST_SPECIAL
|
||||
for i in range(2):
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_READ")
|
||||
assert ev[1] > const.DC_CHAT_ID_LAST_SPECIAL
|
||||
assert ev[2] > const.DC_MSG_ID_LAST_SPECIAL
|
||||
lp.step("2")
|
||||
assert msg_out.is_out_mdn_received()
|
||||
assert msg_out2.is_out_mdn_received()
|
||||
|
||||
lp.sec("check that a second call to mark_seen does not create change or smtp job")
|
||||
ac2._evlogger.consume_events()
|
||||
@@ -829,7 +841,21 @@ class TestOnlineAccount:
|
||||
print("ac1: e2ee_enabled={}".format(ac1.get_config("e2ee_enabled")))
|
||||
print("ac2: e2ee_enabled={}".format(ac2.get_config("e2ee_enabled")))
|
||||
ac1.set_config("e2ee_enabled", "0")
|
||||
chat.send_text("message2 -- should be encrypted")
|
||||
|
||||
# Set unprepared and unencrypted draft to test that it is not
|
||||
# taken into account when determining whether last message is
|
||||
# encrypted.
|
||||
msg_draft = Message.new_empty(ac1, "text")
|
||||
msg_draft.set_text("message2 -- should be encrypted")
|
||||
chat.set_draft(msg_draft)
|
||||
|
||||
# Get the draft, prepare and send it.
|
||||
msg_draft = chat.get_draft()
|
||||
msg_out = chat.prepare_message(msg_draft)
|
||||
chat.send_prepared(msg_out)
|
||||
|
||||
chat.set_draft(None)
|
||||
assert chat.get_draft() is None
|
||||
|
||||
lp.sec("wait for ac2 to receive message")
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
|
||||
@@ -1177,29 +1203,46 @@ class TestOnlineAccount:
|
||||
class TestGroupStressTests:
|
||||
def test_group_many_members_add_leave_remove(self, acfactory, lp):
|
||||
lp.sec("creating and configuring five accounts")
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
accounts = [acfactory.get_online_configuring_account() for i in range(3)]
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
accounts = [acfactory.get_online_configuring_account() for i in range(5)]
|
||||
for acc in accounts:
|
||||
wait_configuration_progress(acc, 1000)
|
||||
ac1 = accounts.pop()
|
||||
|
||||
lp.sec("ac1: setting up contacts with 4 other members")
|
||||
contacts = []
|
||||
for acc, name in zip(accounts, list("äöüsr")):
|
||||
contact = ac1.create_contact(acc.get_config("addr"), name=name)
|
||||
contacts.append(contact)
|
||||
|
||||
# make sure we accept the "hi" message
|
||||
ac1.create_chat_by_contact(contact)
|
||||
|
||||
# make sure the other side accepts our messages
|
||||
c1 = acc.create_contact(ac1.get_config("addr"), "ä member")
|
||||
chat1 = acc.create_chat_by_contact(c1)
|
||||
|
||||
# send a message to get the contact key via autocrypt header
|
||||
chat1.send_text("hi")
|
||||
msg = ac1.wait_next_incoming_message()
|
||||
assert msg.text == "hi"
|
||||
|
||||
# Save fifth account for later
|
||||
ac5 = accounts.pop()
|
||||
contact5 = contacts.pop()
|
||||
|
||||
lp.sec("ac1: creating group chat with 3 other members")
|
||||
chat = ac1.create_group_chat("title1")
|
||||
contacts = []
|
||||
chars = list("äöüsr")
|
||||
for acc in accounts:
|
||||
contact = ac1.create_contact(acc.get_config("addr"), name=chars.pop())
|
||||
contacts.append(contact)
|
||||
for contact in contacts:
|
||||
chat.add_contact(contact)
|
||||
# make sure the other side accepts our messages
|
||||
c1 = acc.create_contact(ac1.get_config("addr"), "ä member")
|
||||
acc.create_chat_by_contact(c1)
|
||||
|
||||
assert not chat.is_promoted()
|
||||
|
||||
lp.sec("ac1: send mesage to new group chat")
|
||||
chat.send_text("hello")
|
||||
msg = chat.send_text("hello")
|
||||
assert chat.is_promoted()
|
||||
assert msg.is_encrypted()
|
||||
|
||||
gossiped_timestamp = chat.get_summary()["gossiped_timestamp"]
|
||||
assert gossiped_timestamp > 0
|
||||
|
||||
num_contacts = len(chat.get_contacts())
|
||||
assert num_contacts == 3 + 1
|
||||
@@ -1215,19 +1258,45 @@ class TestGroupStressTests:
|
||||
ac3 = accounts[1]
|
||||
msg3 = ac3.wait_next_incoming_message()
|
||||
assert msg3.text == "hello"
|
||||
contacts = ac3.get_contacts()
|
||||
assert len(contacts) == 3
|
||||
ac3_contacts = ac3.get_contacts()
|
||||
assert len(ac3_contacts) == 3
|
||||
ac4_contacts = ac3.get_contacts(query=accounts[2].get_config("addr"))
|
||||
assert len(ac4_contacts) == 1
|
||||
|
||||
lp.sec("ac1: removing one contacts and checking things are right")
|
||||
to_remove = msg.chat.get_contacts()[-1]
|
||||
lp.sec("ac2: removing one contact")
|
||||
to_remove = contacts[-1]
|
||||
msg.chat.remove_contact(to_remove)
|
||||
|
||||
lp.sec("ac1: receiving system message about contact removal")
|
||||
sysmsg = ac1.wait_next_incoming_message()
|
||||
assert to_remove.addr in sysmsg.text
|
||||
assert len(sysmsg.chat.get_contacts()) == 3
|
||||
|
||||
# Receiving message about removed contact does not reset gossip
|
||||
assert chat.get_summary()["gossiped_timestamp"] == gossiped_timestamp
|
||||
|
||||
lp.sec("ac1: sending another message to the chat")
|
||||
chat.send_text("hello2")
|
||||
msg = ac2.wait_next_incoming_message()
|
||||
assert msg.text == "hello2"
|
||||
assert chat.get_summary()["gossiped_timestamp"] == gossiped_timestamp
|
||||
|
||||
lp.sec("ac1: adding fifth member to the chat")
|
||||
chat.add_contact(contact5)
|
||||
# Additng contact to chat resets gossiped_timestamp
|
||||
assert chat.get_summary()["gossiped_timestamp"] >= gossiped_timestamp
|
||||
|
||||
lp.sec("ac2: receiving system message about contact addition")
|
||||
sysmsg = ac2.wait_next_incoming_message()
|
||||
assert contact5.addr in sysmsg.text
|
||||
assert len(sysmsg.chat.get_contacts()) == 4
|
||||
|
||||
lp.sec("ac5: waiting for message about addition to the chat")
|
||||
sysmsg = ac5.wait_next_incoming_message()
|
||||
msg = sysmsg.chat.send_text("hello!")
|
||||
# Message should be encrypted because keys of other members are gossiped
|
||||
assert msg.is_encrypted()
|
||||
|
||||
|
||||
class TestOnlineConfigureFails:
|
||||
def test_invalid_password(self, acfactory):
|
||||
|
||||
@@ -8,7 +8,7 @@ envlist =
|
||||
[testenv]
|
||||
commands =
|
||||
pytest -n6 --reruns 2 --reruns-delay 5 -v -rsXx {posargs:tests}
|
||||
# python tests/package_wheels.py {toxworkdir}/wheelhouse
|
||||
python tests/package_wheels.py {toxworkdir}/wheelhouse
|
||||
passenv =
|
||||
TRAVIS
|
||||
DCC_RS_DEV
|
||||
|
||||
8
spec.md
8
spec.md
@@ -153,8 +153,8 @@ The message-id MUST have the format `Gr.<group-id>.<unique data>`.
|
||||
Hello group - this group contains three members
|
||||
|
||||
Messengers adding the member list in the form `Name <email-address>`
|
||||
MUST take care only to spread the names authorized by the contacts themselves.
|
||||
Otherwise, names as _Daddy_ or _Honey_ may be spread
|
||||
MUST take care only to distribute the names authorized by the contacts themselves.
|
||||
Otherwise, names as _Daddy_ or _Honey_ may be distributed
|
||||
(this issue is also true for normal MUAs, however,
|
||||
for more contact- and chat-centralized apps
|
||||
such situations happen more frequently).
|
||||
@@ -288,7 +288,7 @@ to add a `Chat-Group-Avatar` only on image changes.
|
||||
|
||||
# Set profile image
|
||||
|
||||
A user MAY have a profile-image that MAY be spread to their contacts.
|
||||
A user MAY have a profile-image that MAY be distributed to their contacts.
|
||||
To change or set the profile-image,
|
||||
the messenger MUST attach an image file to a message
|
||||
and MUST add the header `Chat-User-Avatar`
|
||||
@@ -297,7 +297,7 @@ with the value set to the image name.
|
||||
To remove the profile-image,
|
||||
the messenger MUST add the header `Chat-User-Avatar: 0`.
|
||||
|
||||
To spread the image,
|
||||
To distribute the image,
|
||||
the messenger MAY send the profile image
|
||||
together with the next mail to a given contact
|
||||
(to do this only once,
|
||||
|
||||
793
src/chat.rs
793
src/chat.rs
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@ use crate::stock::StockMessage;
|
||||
#[derive(Debug)]
|
||||
pub struct Chatlist {
|
||||
/// Stores pairs of `chat_id, message_id`
|
||||
ids: Vec<(u32, MsgId)>,
|
||||
ids: Vec<(ChatId, MsgId)>,
|
||||
}
|
||||
|
||||
impl Chatlist {
|
||||
@@ -91,7 +91,7 @@ impl Chatlist {
|
||||
let mut add_archived_link_item = false;
|
||||
|
||||
let process_row = |row: &rusqlite::Row| {
|
||||
let chat_id: u32 = row.get(0)?;
|
||||
let chat_id: ChatId = row.get(0)?;
|
||||
let msg_id: MsgId = row.get(1).unwrap_or_default();
|
||||
Ok((chat_id, msg_id))
|
||||
};
|
||||
@@ -211,7 +211,10 @@ impl Chatlist {
|
||||
)?;
|
||||
if 0 == listflags & DC_GCL_NO_SPECIALS {
|
||||
if let Some(last_deaddrop_fresh_msg_id) = get_last_deaddrop_fresh_msg(context) {
|
||||
ids.insert(0, (DC_CHAT_ID_DEADDROP, last_deaddrop_fresh_msg_id));
|
||||
ids.insert(
|
||||
0,
|
||||
(ChatId::new(DC_CHAT_ID_DEADDROP), last_deaddrop_fresh_msg_id),
|
||||
);
|
||||
}
|
||||
add_archived_link_item = true;
|
||||
}
|
||||
@@ -220,9 +223,9 @@ impl Chatlist {
|
||||
|
||||
if add_archived_link_item && dc_get_archived_cnt(context) > 0 {
|
||||
if ids.is_empty() && 0 != listflags & DC_GCL_ADD_ALLDONE_HINT {
|
||||
ids.push((DC_CHAT_ID_ALLDONE_HINT, MsgId::new(0)));
|
||||
ids.push((ChatId::new(DC_CHAT_ID_ALLDONE_HINT), MsgId::new(0)));
|
||||
}
|
||||
ids.push((DC_CHAT_ID_ARCHIVED_LINK, MsgId::new(0)));
|
||||
ids.push((ChatId::new(DC_CHAT_ID_ARCHIVED_LINK), MsgId::new(0)));
|
||||
}
|
||||
|
||||
Ok(Chatlist { ids })
|
||||
@@ -241,9 +244,9 @@ impl Chatlist {
|
||||
/// Get a single chat ID of a chatlist.
|
||||
///
|
||||
/// To get the message object from the message ID, use dc_get_chat().
|
||||
pub fn get_chat_id(&self, index: usize) -> u32 {
|
||||
pub fn get_chat_id(&self, index: usize) -> ChatId {
|
||||
if index >= self.ids.len() {
|
||||
return 0;
|
||||
return ChatId::new(0);
|
||||
}
|
||||
self.ids[index].0
|
||||
}
|
||||
@@ -307,7 +310,7 @@ impl Chatlist {
|
||||
None
|
||||
};
|
||||
|
||||
if chat.id == DC_CHAT_ID_ARCHIVED_LINK {
|
||||
if chat.id.is_archived_link() {
|
||||
ret.text2 = None;
|
||||
} else if lastmsg.is_none() || lastmsg.as_ref().unwrap().from_id == DC_CONTACT_ID_UNDEFINED
|
||||
{
|
||||
|
||||
@@ -190,7 +190,9 @@ mod tests {
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
|
||||
use crate::constants::AVATAR_SIZE;
|
||||
use crate::test_utils::*;
|
||||
use image::GenericImageView;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
@@ -212,32 +214,55 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_selfavatar_outside_blobdir() -> failure::Fallible<()> {
|
||||
fn test_selfavatar_outside_blobdir() {
|
||||
let t = dummy_context();
|
||||
let avatar_src = t.dir.path().join("avatar.jpg");
|
||||
let avatar_bytes = include_bytes!("../test-data/image/avatar1000x1000.jpg");
|
||||
File::create(&avatar_src)?.write_all(avatar_bytes)?;
|
||||
File::create(&avatar_src)
|
||||
.unwrap()
|
||||
.write_all(avatar_bytes)
|
||||
.unwrap();
|
||||
let avatar_blob = t.ctx.get_blobdir().join("avatar.jpg");
|
||||
assert!(!avatar_blob.exists());
|
||||
t.ctx
|
||||
.set_config(Config::Selfavatar, Some(&avatar_src.to_str().unwrap()))?;
|
||||
.set_config(Config::Selfavatar, Some(&avatar_src.to_str().unwrap()))
|
||||
.unwrap();
|
||||
assert!(avatar_blob.exists());
|
||||
assert!(std::fs::metadata(&avatar_blob).unwrap().len() < avatar_bytes.len() as u64);
|
||||
let avatar_cfg = t.ctx.get_config(Config::Selfavatar);
|
||||
assert_eq!(avatar_cfg, avatar_blob.to_str().map(|s| s.to_string()));
|
||||
Ok(())
|
||||
|
||||
let img = image::open(avatar_src).unwrap();
|
||||
assert_eq!(img.width(), 1000);
|
||||
assert_eq!(img.height(), 1000);
|
||||
|
||||
let img = image::open(avatar_blob).unwrap();
|
||||
assert_eq!(img.width(), AVATAR_SIZE);
|
||||
assert_eq!(img.height(), AVATAR_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_selfavatar_in_blobdir() -> failure::Fallible<()> {
|
||||
fn test_selfavatar_in_blobdir() {
|
||||
let t = dummy_context();
|
||||
let avatar_src = t.ctx.get_blobdir().join("avatar.jpg");
|
||||
let avatar_bytes = include_bytes!("../test-data/image/avatar1000x1000.jpg");
|
||||
File::create(&avatar_src)?.write_all(avatar_bytes)?;
|
||||
let avatar_src = t.ctx.get_blobdir().join("avatar.png");
|
||||
let avatar_bytes = include_bytes!("../test-data/image/avatar900x900.png");
|
||||
File::create(&avatar_src)
|
||||
.unwrap()
|
||||
.write_all(avatar_bytes)
|
||||
.unwrap();
|
||||
|
||||
let img = image::open(&avatar_src).unwrap();
|
||||
assert_eq!(img.width(), 900);
|
||||
assert_eq!(img.height(), 900);
|
||||
|
||||
t.ctx
|
||||
.set_config(Config::Selfavatar, Some(&avatar_src.to_str().unwrap()))?;
|
||||
.set_config(Config::Selfavatar, Some(&avatar_src.to_str().unwrap()))
|
||||
.unwrap();
|
||||
let avatar_cfg = t.ctx.get_config(Config::Selfavatar);
|
||||
assert_eq!(avatar_cfg, avatar_src.to_str().map(|s| s.to_string()));
|
||||
Ok(())
|
||||
|
||||
let img = image::open(avatar_src).unwrap();
|
||||
assert_eq!(img.width(), AVATAR_SIZE);
|
||||
assert_eq!(img.height(), AVATAR_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ pub fn dc_is_configured(context: &Context) -> bool {
|
||||
/*******************************************************************************
|
||||
* Configure JOB
|
||||
******************************************************************************/
|
||||
#[allow(non_snake_case, unused_must_use)]
|
||||
#[allow(non_snake_case, unused_must_use, clippy::cognitive_complexity)]
|
||||
pub fn JobConfigureImap(context: &Context) -> job::Status {
|
||||
if !context.sql.is_open() {
|
||||
error!(context, "Cannot configure, database not opened.",);
|
||||
|
||||
@@ -49,6 +49,8 @@ pub const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
|
||||
pub const DC_HANDSHAKE_STOP_NORMAL_PROCESSING: i32 = 0x02;
|
||||
pub const DC_HANDSHAKE_ADD_DELETE_JOB: i32 = 0x04;
|
||||
|
||||
pub(crate) const DC_FROM_HANDSHAKE: i32 = 0x01;
|
||||
|
||||
pub const DC_GCL_ARCHIVED_ONLY: usize = 0x01;
|
||||
pub const DC_GCL_NO_SPECIALS: usize = 0x02;
|
||||
pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
|
||||
|
||||
@@ -7,6 +7,7 @@ use itertools::Itertools;
|
||||
use rusqlite;
|
||||
|
||||
use crate::aheader::EncryptPreference;
|
||||
use crate::chat::ChatId;
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
@@ -262,7 +263,7 @@ impl Contact {
|
||||
.is_ok()
|
||||
{
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ pub struct Context {
|
||||
#[debug_stub = "Callback"]
|
||||
cb: Box<ContextCallback>,
|
||||
pub os_name: Option<String>,
|
||||
pub cmdline_sel_chat_id: Arc<RwLock<u32>>,
|
||||
pub cmdline_sel_chat_id: Arc<RwLock<ChatId>>,
|
||||
pub bob: Arc<RwLock<BobStatus>>,
|
||||
pub last_smeared_timestamp: RwLock<i64>,
|
||||
pub running_state: Arc<RwLock<RunningState>>,
|
||||
@@ -75,10 +75,6 @@ pub fn get_info() -> HashMap<&'static str, String> {
|
||||
let mut res = HashMap::new();
|
||||
res.insert("deltachat_core_version", format!("v{}", &*DC_VERSION_STR));
|
||||
res.insert("sqlite_version", rusqlite::version().to_string());
|
||||
res.insert(
|
||||
"sqlite_thread_safe",
|
||||
unsafe { rusqlite::ffi::sqlite3_threadsafe() }.to_string(),
|
||||
);
|
||||
res.insert("arch", (std::mem::size_of::<usize>() * 8).to_string());
|
||||
res.insert("level", "awesome".into());
|
||||
res
|
||||
@@ -120,7 +116,7 @@ impl Context {
|
||||
oauth2_critical: Arc::new(Mutex::new(())),
|
||||
bob: Arc::new(RwLock::new(Default::default())),
|
||||
last_smeared_timestamp: RwLock::new(0),
|
||||
cmdline_sel_chat_id: Arc::new(RwLock::new(0)),
|
||||
cmdline_sel_chat_id: Arc::new(RwLock::new(ChatId::new(0))),
|
||||
inbox_thread: Arc::new(RwLock::new(JobThread::new(
|
||||
"INBOX",
|
||||
"configured_inbox_folder",
|
||||
@@ -349,7 +345,7 @@ impl Context {
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn search_msgs(&self, chat_id: u32, query: impl AsRef<str>) -> Vec<MsgId> {
|
||||
pub fn search_msgs(&self, chat_id: ChatId, query: impl AsRef<str>) -> Vec<MsgId> {
|
||||
let real_query = query.as_ref().trim();
|
||||
if real_query.is_empty() {
|
||||
return Vec::new();
|
||||
@@ -357,7 +353,7 @@ impl Context {
|
||||
let strLikeInText = format!("%{}%", real_query);
|
||||
let strLikeBeg = format!("{}%", real_query);
|
||||
|
||||
let query = if 0 != chat_id {
|
||||
let query = if !chat_id.is_unset() {
|
||||
concat!(
|
||||
"SELECT m.id AS id, m.timestamp AS timestamp",
|
||||
" FROM msgs m",
|
||||
@@ -389,7 +385,7 @@ impl Context {
|
||||
self.sql
|
||||
.query_map(
|
||||
query,
|
||||
params![chat_id as i32, &strLikeInText, &strLikeBeg],
|
||||
params![chat_id, &strLikeInText, &strLikeBeg],
|
||||
|row| row.get::<_, MsgId>("id"),
|
||||
|rows| {
|
||||
let mut ret = Vec::new();
|
||||
|
||||
@@ -3,7 +3,7 @@ use sha2::{Digest, Sha256};
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::chat::{self, Chat};
|
||||
use crate::chat::{self, Chat, ChatId};
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
@@ -17,7 +17,7 @@ use crate::message::{self, MessageState, MessengerMessage, MsgId};
|
||||
use crate::mimeparser::*;
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
use crate::securejoin::handle_securejoin_handshake;
|
||||
use crate::securejoin::{self, handle_securejoin_handshake};
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
use crate::{contact, location};
|
||||
@@ -61,7 +61,7 @@ pub fn dc_receive_imf(
|
||||
ensure!(mime_parser.has_headers(), "No Headers Found");
|
||||
|
||||
// the function returns the number of created messages in the database
|
||||
let mut chat_id = 0;
|
||||
let mut chat_id = ChatId::new(0);
|
||||
let mut hidden = false;
|
||||
|
||||
let mut needs_delete_job = false;
|
||||
@@ -74,17 +74,17 @@ pub fn dc_receive_imf(
|
||||
// helper method to handle early exit and memory cleanup
|
||||
let cleanup = |context: &Context,
|
||||
create_event_to_send: &Option<CreateEvent>,
|
||||
created_db_entries: &Vec<(usize, MsgId)>| {
|
||||
created_db_entries: &Vec<(ChatId, MsgId)>| {
|
||||
if let Some(create_event_to_send) = create_event_to_send {
|
||||
for (chat_id, msg_id) in created_db_entries {
|
||||
let event = match create_event_to_send {
|
||||
CreateEvent::MsgsChanged => Event::MsgsChanged {
|
||||
msg_id: *msg_id,
|
||||
chat_id: *chat_id as u32,
|
||||
chat_id: *chat_id,
|
||||
},
|
||||
CreateEvent::IncomingMsg => Event::IncomingMsg {
|
||||
msg_id: *msg_id,
|
||||
chat_id: *chat_id as u32,
|
||||
chat_id: *chat_id,
|
||||
},
|
||||
};
|
||||
context.call_cb(event);
|
||||
@@ -249,6 +249,7 @@ pub fn dc_receive_imf(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments, clippy::cognitive_complexity)]
|
||||
fn add_parts(
|
||||
context: &Context,
|
||||
mut mime_parser: &mut MimeMessage,
|
||||
@@ -263,11 +264,11 @@ fn add_parts(
|
||||
from_id: u32,
|
||||
from_id_blocked: bool,
|
||||
hidden: &mut bool,
|
||||
chat_id: &mut u32,
|
||||
chat_id: &mut ChatId,
|
||||
flags: u32,
|
||||
needs_delete_job: &mut bool,
|
||||
insert_msg_id: &mut MsgId,
|
||||
created_db_entries: &mut Vec<(usize, MsgId)>,
|
||||
created_db_entries: &mut Vec<(ChatId, MsgId)>,
|
||||
create_event_to_send: &mut Option<CreateEvent>,
|
||||
) -> Result<()> {
|
||||
let mut state: MessageState;
|
||||
@@ -308,7 +309,7 @@ fn add_parts(
|
||||
{
|
||||
// this message is a classic email not a chat-message nor a reply to one
|
||||
if show_emails == ShowEmails::Off {
|
||||
*chat_id = DC_CHAT_ID_TRASH;
|
||||
*chat_id = ChatId::new(DC_CHAT_ID_TRASH);
|
||||
allow_creation = false
|
||||
} else if show_emails == ShowEmails::AcceptedContacts {
|
||||
allow_creation = false
|
||||
@@ -328,36 +329,33 @@ fn add_parts(
|
||||
MessageState::InFresh
|
||||
};
|
||||
to_id = DC_CONTACT_ID_SELF;
|
||||
let mut needs_stop_ongoing_process = false;
|
||||
|
||||
// handshake messages must be processed _before_ chats are created
|
||||
// (eg. contacs may be marked as verified)
|
||||
if mime_parser.get(HeaderDef::SecureJoin).is_some() {
|
||||
// avoid discarding by show_emails setting
|
||||
msgrmsg = MessengerMessage::Yes;
|
||||
*chat_id = 0;
|
||||
*chat_id = ChatId::new(0);
|
||||
allow_creation = true;
|
||||
match handle_securejoin_handshake(context, mime_parser, from_id) {
|
||||
Ok(ret) => {
|
||||
if ret.hide_this_msg {
|
||||
*hidden = true;
|
||||
*needs_delete_job = ret.delete_this_msg;
|
||||
state = MessageState::InSeen;
|
||||
}
|
||||
if let Some(status) = ret.bob_securejoin_success {
|
||||
context.bob.write().unwrap().status = status as i32;
|
||||
}
|
||||
needs_stop_ongoing_process = ret.stop_ongoing_process;
|
||||
Ok(securejoin::HandshakeMessage::Done) => {
|
||||
*hidden = true;
|
||||
*needs_delete_job = true;
|
||||
state = MessageState::InSeen;
|
||||
}
|
||||
Ok(securejoin::HandshakeMessage::Ignore) => {
|
||||
*hidden = true;
|
||||
state = MessageState::InSeen;
|
||||
}
|
||||
Ok(securejoin::HandshakeMessage::Propagate) => {
|
||||
// Message will still be processed as "member
|
||||
// added" or similar system message.
|
||||
}
|
||||
Err(err) => {
|
||||
// maybe this message belongs to an aborted scan,
|
||||
// however, by the explicit header check we really know
|
||||
// that it is a Secure-Join message that should be hidden in the chat view.
|
||||
*hidden = true;
|
||||
warn!(
|
||||
context,
|
||||
"Unexpected messaged passed to Secure-Join handshake protocol: {}", err
|
||||
);
|
||||
context.bob.write().unwrap().status = 0; // secure-join failed
|
||||
context.stop_ongoing();
|
||||
error!(context, "Error in Secure-Join message handling: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,12 +365,12 @@ fn add_parts(
|
||||
|
||||
// get the chat_id - a chat_id here is no indicator that the chat is displayed in the normal list,
|
||||
// it might also be blocked and displayed in the deaddrop as a result
|
||||
if *chat_id == 0 {
|
||||
if chat_id.is_unset() {
|
||||
// try to create a group
|
||||
// (groups appear automatically only if the _sender_ is known, see core issue #54)
|
||||
|
||||
let create_blocked =
|
||||
if 0 != test_normal_chat_id && test_normal_chat_id_blocked == Blocked::Not {
|
||||
if !test_normal_chat_id.is_unset() && test_normal_chat_id_blocked == Blocked::Not {
|
||||
Blocked::Not
|
||||
} else {
|
||||
Blocked::Deaddrop
|
||||
@@ -388,21 +386,24 @@ fn add_parts(
|
||||
)?;
|
||||
*chat_id = new_chat_id;
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
if *chat_id != 0 && chat_id_blocked != Blocked::Not && create_blocked == Blocked::Not {
|
||||
if !chat_id.is_unset()
|
||||
&& chat_id_blocked != Blocked::Not
|
||||
&& create_blocked == Blocked::Not
|
||||
{
|
||||
chat::unblock(context, new_chat_id);
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
|
||||
if *chat_id == 0 {
|
||||
if chat_id.is_unset() {
|
||||
// check if the message belongs to a mailing list
|
||||
if mime_parser.is_mailinglist_message() {
|
||||
*chat_id = DC_CHAT_ID_TRASH;
|
||||
*chat_id = ChatId::new(DC_CHAT_ID_TRASH);
|
||||
info!(context, "Message belongs to a mailing list and is ignored.",);
|
||||
}
|
||||
}
|
||||
|
||||
if *chat_id == 0 {
|
||||
if chat_id.is_unset() {
|
||||
// try to create a normal chat
|
||||
let create_blocked = if from_id == to_id {
|
||||
Blocked::Not
|
||||
@@ -410,7 +411,7 @@ fn add_parts(
|
||||
Blocked::Deaddrop
|
||||
};
|
||||
|
||||
if 0 != test_normal_chat_id {
|
||||
if !test_normal_chat_id.is_unset() {
|
||||
*chat_id = test_normal_chat_id;
|
||||
chat_id_blocked = test_normal_chat_id_blocked;
|
||||
} else if allow_creation {
|
||||
@@ -420,7 +421,7 @@ fn add_parts(
|
||||
*chat_id = id;
|
||||
chat_id_blocked = bl;
|
||||
}
|
||||
if 0 != *chat_id && Blocked::Not != chat_id_blocked {
|
||||
if !chat_id.is_unset() && Blocked::Not != chat_id_blocked {
|
||||
if Blocked::Not == create_blocked {
|
||||
chat::unblock(context, *chat_id);
|
||||
chat_id_blocked = Blocked::Not;
|
||||
@@ -438,9 +439,9 @@ fn add_parts(
|
||||
}
|
||||
}
|
||||
}
|
||||
if *chat_id == 0 {
|
||||
if chat_id.is_unset() {
|
||||
// maybe from_id is null or sth. else is suspicious, move message to trash
|
||||
*chat_id = DC_CHAT_ID_TRASH;
|
||||
*chat_id = ChatId::new(DC_CHAT_ID_TRASH);
|
||||
}
|
||||
|
||||
// if the chat_id is blocked,
|
||||
@@ -454,13 +455,6 @@ fn add_parts(
|
||||
{
|
||||
state = MessageState::InNoticed;
|
||||
}
|
||||
|
||||
if needs_stop_ongoing_process {
|
||||
// The Secure-Join protocol finished and the group
|
||||
// creation handling is done. Stopping the ongoing
|
||||
// process will let dc_join_securejoin() return.
|
||||
context.stop_ongoing();
|
||||
}
|
||||
} else {
|
||||
// Outgoing
|
||||
|
||||
@@ -469,7 +463,7 @@ fn add_parts(
|
||||
state = MessageState::OutDelivered;
|
||||
to_id = to_ids.get_index(0).cloned().unwrap_or_default();
|
||||
if !to_ids.is_empty() {
|
||||
if *chat_id == 0 {
|
||||
if chat_id.is_unset() {
|
||||
let (new_chat_id, new_chat_id_blocked) = create_or_lookup_group(
|
||||
context,
|
||||
&mut mime_parser,
|
||||
@@ -481,12 +475,12 @@ fn add_parts(
|
||||
*chat_id = new_chat_id;
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
// automatically unblock chat when the user sends a message
|
||||
if *chat_id != 0 && chat_id_blocked != Blocked::Not {
|
||||
if !chat_id.is_unset() && chat_id_blocked != Blocked::Not {
|
||||
chat::unblock(context, new_chat_id);
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
if *chat_id == 0 && allow_creation {
|
||||
if chat_id.is_unset() && allow_creation {
|
||||
let create_blocked = if MessengerMessage::No != msgrmsg
|
||||
&& !Contact::is_blocked_load(context, to_id)
|
||||
{
|
||||
@@ -499,7 +493,7 @@ fn add_parts(
|
||||
*chat_id = id;
|
||||
chat_id_blocked = bl;
|
||||
|
||||
if 0 != *chat_id
|
||||
if !chat_id.is_unset()
|
||||
&& Blocked::Not != chat_id_blocked
|
||||
&& Blocked::Not == create_blocked
|
||||
{
|
||||
@@ -512,7 +506,7 @@ fn add_parts(
|
||||
&& to_ids.len() == 1
|
||||
&& to_ids.contains(&DC_CONTACT_ID_SELF);
|
||||
|
||||
if *chat_id == 0 && self_sent {
|
||||
if chat_id.is_unset() && self_sent {
|
||||
// from_id==to_id==DC_CONTACT_ID_SELF - this is a self-sent messages,
|
||||
// maybe an Autocrypt Setup Messag
|
||||
let (id, bl) =
|
||||
@@ -521,13 +515,13 @@ fn add_parts(
|
||||
*chat_id = id;
|
||||
chat_id_blocked = bl;
|
||||
|
||||
if 0 != *chat_id && Blocked::Not != chat_id_blocked {
|
||||
if !chat_id.is_unset() && Blocked::Not != chat_id_blocked {
|
||||
chat::unblock(context, *chat_id);
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
if *chat_id == 0 {
|
||||
*chat_id = DC_CHAT_ID_TRASH;
|
||||
if chat_id.is_unset() {
|
||||
*chat_id = ChatId::new(DC_CHAT_ID_TRASH);
|
||||
}
|
||||
}
|
||||
// correct message_timestamp, it should not be used before,
|
||||
@@ -598,7 +592,7 @@ fn add_parts(
|
||||
rfc724_mid,
|
||||
server_folder.as_ref(),
|
||||
server_uid as i32,
|
||||
*chat_id as i32,
|
||||
*chat_id,
|
||||
from_id as i32,
|
||||
to_id as i32,
|
||||
sort_timestamp,
|
||||
@@ -626,7 +620,7 @@ fn add_parts(
|
||||
let row_id =
|
||||
sql::get_rowid_with_conn(context, conn, "msgs", "rfc724_mid", &rfc724_mid);
|
||||
*insert_msg_id = MsgId::new(row_id);
|
||||
created_db_entries.push((*chat_id as usize, *insert_msg_id));
|
||||
created_db_entries.push((*chat_id, *insert_msg_id));
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
@@ -638,7 +632,7 @@ fn add_parts(
|
||||
);
|
||||
|
||||
// check event to send
|
||||
if *chat_id == DC_CHAT_ID_TRASH {
|
||||
if chat_id.is_trash() {
|
||||
*create_event_to_send = None;
|
||||
} else if incoming && state == MessageState::InFresh {
|
||||
if from_id_blocked {
|
||||
@@ -656,12 +650,12 @@ fn add_parts(
|
||||
fn save_locations(
|
||||
context: &Context,
|
||||
mime_parser: &MimeMessage,
|
||||
chat_id: u32,
|
||||
chat_id: ChatId,
|
||||
from_id: u32,
|
||||
insert_msg_id: MsgId,
|
||||
hidden: bool,
|
||||
) {
|
||||
if chat_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
if chat_id.is_special() {
|
||||
return;
|
||||
}
|
||||
let mut location_id_written = false;
|
||||
@@ -707,9 +701,10 @@ fn save_locations(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn calc_timestamps(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
chat_id: ChatId,
|
||||
from_id: u32,
|
||||
message_timestamp: i64,
|
||||
is_fresh_msg: bool,
|
||||
@@ -727,7 +722,7 @@ fn calc_timestamps(
|
||||
let last_msg_time: Option<i64> = context.sql.query_get_value(
|
||||
context,
|
||||
"SELECT MAX(timestamp) FROM msgs WHERE chat_id=? and from_id!=? AND timestamp>=?",
|
||||
params![chat_id as i32, from_id as i32, *sort_timestamp],
|
||||
params![chat_id, from_id as i32, *sort_timestamp],
|
||||
);
|
||||
if let Some(last_msg_time) = last_msg_time {
|
||||
if last_msg_time > 0 && *sort_timestamp <= last_msg_time {
|
||||
@@ -751,7 +746,7 @@ fn calc_timestamps(
|
||||
/// - create an ad-hoc group based on the recipient list
|
||||
///
|
||||
/// on success the function returns the found/created (chat_id, chat_blocked) tuple .
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(non_snake_case, clippy::cognitive_complexity)]
|
||||
fn create_or_lookup_group(
|
||||
context: &Context,
|
||||
mime_parser: &mut MimeMessage,
|
||||
@@ -759,7 +754,7 @@ fn create_or_lookup_group(
|
||||
create_blocked: Blocked,
|
||||
from_id: u32,
|
||||
to_ids: &ContactIds,
|
||||
) -> Result<(u32, Blocked)> {
|
||||
) -> Result<(ChatId, Blocked)> {
|
||||
let mut chat_id_blocked = Blocked::Not;
|
||||
let mut recreate_member_list = false;
|
||||
let mut send_EVENT_CHAT_MODIFIED = false;
|
||||
@@ -787,10 +782,10 @@ fn create_or_lookup_group(
|
||||
}
|
||||
if grpid.is_empty() {
|
||||
if let Some(extracted_grpid) = extract_grpid(mime_parser, HeaderDef::InReplyTo) {
|
||||
grpid = extracted_grpid;
|
||||
grpid = extracted_grpid.to_string();
|
||||
} else if let Some(extracted_grpid) = extract_grpid(mime_parser, HeaderDef::References)
|
||||
{
|
||||
grpid = extracted_grpid;
|
||||
grpid = extracted_grpid.to_string();
|
||||
} else {
|
||||
return create_or_lookup_adhoc_group(
|
||||
context,
|
||||
@@ -830,14 +825,14 @@ fn create_or_lookup_group(
|
||||
} else {
|
||||
let field = mime_parser.get(HeaderDef::ChatGroupMemberAdded).cloned();
|
||||
if let Some(optional_field) = field {
|
||||
X_MrAddToGrp = Some(optional_field);
|
||||
mime_parser.is_system_message = SystemMessage::MemberAddedToGroup;
|
||||
better_msg = context.stock_system_msg(
|
||||
StockMessage::MsgAddMember,
|
||||
X_MrAddToGrp.as_ref().unwrap(),
|
||||
&optional_field,
|
||||
"",
|
||||
from_id as u32,
|
||||
)
|
||||
);
|
||||
X_MrAddToGrp = Some(optional_field);
|
||||
} else {
|
||||
let field = mime_parser.get(HeaderDef::ChatGroupNameChanged);
|
||||
if let Some(field) = field {
|
||||
@@ -877,8 +872,9 @@ 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, chat_id_verified, _blocked) = chat::get_chat_id_by_grpid(context, &grpid);
|
||||
if chat_id != 0 {
|
||||
let (mut chat_id, chat_id_verified, _blocked) = chat::get_chat_id_by_grpid(context, &grpid)
|
||||
.unwrap_or((ChatId::new(0), false, Blocked::Not));
|
||||
if !chat_id.is_error() {
|
||||
if chat_id_verified {
|
||||
if let Err(err) =
|
||||
check_verified_properties(context, mime_parser, from_id as u32, to_ids)
|
||||
@@ -906,7 +902,7 @@ fn create_or_lookup_group(
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.unwrap_or_default();
|
||||
|
||||
if chat_id == 0
|
||||
if chat_id.is_error()
|
||||
&& !mime_parser.is_mailinglist_message()
|
||||
&& !grpid.is_empty()
|
||||
&& grpname.is_some()
|
||||
@@ -932,7 +928,7 @@ fn create_or_lookup_group(
|
||||
|
||||
if !allow_creation {
|
||||
info!(context, "creating group forbidden by caller");
|
||||
return Ok((0, Blocked::Not));
|
||||
return Ok((ChatId::new(0), Blocked::Not));
|
||||
}
|
||||
|
||||
chat_id = create_group_record(
|
||||
@@ -947,9 +943,9 @@ fn create_or_lookup_group(
|
||||
}
|
||||
|
||||
// again, check chat_id
|
||||
if chat_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
if chat_id.is_special() {
|
||||
return if group_explicitly_left {
|
||||
Ok((DC_CHAT_ID_TRASH, chat_id_blocked))
|
||||
Ok((ChatId::new(DC_CHAT_ID_TRASH), chat_id_blocked))
|
||||
} else {
|
||||
create_or_lookup_adhoc_group(
|
||||
context,
|
||||
@@ -989,7 +985,7 @@ fn create_or_lookup_group(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET name=? WHERE id=?;",
|
||||
params![grpname, chat_id as i32],
|
||||
params![grpname, chat_id],
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
@@ -1027,7 +1023,7 @@ fn create_or_lookup_group(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM chats_contacts WHERE chat_id=?;",
|
||||
params![chat_id as i32],
|
||||
params![chat_id],
|
||||
)
|
||||
.ok();
|
||||
if skip.is_none() || !addr_cmp(&self_addr, skip.unwrap()) {
|
||||
@@ -1049,7 +1045,6 @@ fn create_or_lookup_group(
|
||||
}
|
||||
}
|
||||
send_EVENT_CHAT_MODIFIED = true;
|
||||
chat::reset_gossiped_timestamp(context, chat_id)?;
|
||||
}
|
||||
|
||||
if send_EVENT_CHAT_MODIFIED {
|
||||
@@ -1059,17 +1054,13 @@ fn create_or_lookup_group(
|
||||
}
|
||||
|
||||
/// try extract a grpid from a message-id list header value
|
||||
fn extract_grpid(mime_parser: &MimeMessage, headerdef: HeaderDef) -> Option<String> {
|
||||
if let Some(value) = mime_parser.get(headerdef) {
|
||||
for part in value.split(',').map(str::trim) {
|
||||
if !part.is_empty() {
|
||||
if let Some(extracted_grpid) = dc_extract_grpid_from_rfc724_mid(part) {
|
||||
return Some(extracted_grpid.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
fn extract_grpid<'a>(mime_parser: &'a MimeMessage, headerdef: HeaderDef) -> Option<&'a str> {
|
||||
let header = mime_parser.get(headerdef)?;
|
||||
let parts = header
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.filter(|part| !part.is_empty());
|
||||
parts.filter_map(dc_extract_grpid_from_rfc724_mid).next()
|
||||
}
|
||||
|
||||
/// Handle groups for received messages, return chat_id/Blocked status on success
|
||||
@@ -1080,7 +1071,7 @@ fn create_or_lookup_adhoc_group(
|
||||
create_blocked: Blocked,
|
||||
from_id: u32,
|
||||
to_ids: &ContactIds,
|
||||
) -> Result<(u32, Blocked)> {
|
||||
) -> Result<(ChatId, Blocked)> {
|
||||
// if we're here, no grpid was found, check if there is an existing
|
||||
// ad-hoc group matching the to-list or if we should and can create one
|
||||
// (we do not want to heuristically look at the likely mangled Subject)
|
||||
@@ -1092,7 +1083,7 @@ fn create_or_lookup_adhoc_group(
|
||||
context,
|
||||
"not creating ad-hoc group for mailing list message"
|
||||
);
|
||||
return Ok((0, Blocked::Not));
|
||||
return Ok((ChatId::new(0), Blocked::Not));
|
||||
}
|
||||
|
||||
let mut member_ids: Vec<u32> = to_ids.iter().copied().collect();
|
||||
@@ -1105,7 +1096,7 @@ fn create_or_lookup_adhoc_group(
|
||||
|
||||
if member_ids.len() < 3 {
|
||||
info!(context, "not creating ad-hoc group: too few contacts");
|
||||
return Ok((0, Blocked::Not));
|
||||
return Ok((ChatId::new(0), Blocked::Not));
|
||||
}
|
||||
|
||||
let chat_ids = search_chat_ids_by_contact_ids(context, &member_ids)?;
|
||||
@@ -1113,25 +1104,35 @@ fn create_or_lookup_adhoc_group(
|
||||
let chat_ids_str = join(chat_ids.iter().map(|x| x.to_string()), ",");
|
||||
let res = context.sql.query_row(
|
||||
format!(
|
||||
"SELECT c.id, c.blocked FROM chats c \
|
||||
LEFT JOIN msgs m ON m.chat_id=c.id WHERE c.id IN({}) ORDER BY m.timestamp DESC, m.id DESC LIMIT 1;",
|
||||
"SELECT c.id,
|
||||
c.blocked
|
||||
FROM chats c
|
||||
LEFT JOIN msgs m
|
||||
ON m.chat_id=c.id
|
||||
WHERE c.id IN({})
|
||||
ORDER BY m.timestamp DESC,
|
||||
m.id DESC
|
||||
LIMIT 1;",
|
||||
chat_ids_str
|
||||
),
|
||||
params![],
|
||||
|row| {
|
||||
Ok((row.get::<_, i32>(0)?, row.get::<_, Option<Blocked>>(1)?.unwrap_or_default()))
|
||||
}
|
||||
Ok((
|
||||
row.get::<_, ChatId>(0)?,
|
||||
row.get::<_, Option<Blocked>>(1)?.unwrap_or_default(),
|
||||
))
|
||||
},
|
||||
);
|
||||
|
||||
if let Ok((id, id_blocked)) = res {
|
||||
/* success, chat found */
|
||||
return Ok((id as u32, id_blocked));
|
||||
return Ok((id, id_blocked));
|
||||
}
|
||||
}
|
||||
|
||||
if !allow_creation {
|
||||
info!(context, "creating ad-hoc group prevented from caller");
|
||||
return Ok((0, Blocked::Not));
|
||||
return Ok((ChatId::new(0), Blocked::Not));
|
||||
}
|
||||
|
||||
// we do not check if the message is a reply to another group, this may result in
|
||||
@@ -1145,7 +1146,7 @@ fn create_or_lookup_adhoc_group(
|
||||
context,
|
||||
"failed to create ad-hoc grpid for {:?}", member_ids
|
||||
);
|
||||
return Ok((0, Blocked::Not));
|
||||
return Ok((ChatId::new(0), Blocked::Not));
|
||||
}
|
||||
// use subject as initial chat name
|
||||
let grpname = mime_parser.get_subject().unwrap_or_else(|| {
|
||||
@@ -1153,7 +1154,7 @@ fn create_or_lookup_adhoc_group(
|
||||
});
|
||||
|
||||
// create group record
|
||||
let new_chat_id = create_group_record(
|
||||
let new_chat_id: ChatId = create_group_record(
|
||||
context,
|
||||
&grpid,
|
||||
grpname,
|
||||
@@ -1175,7 +1176,7 @@ fn create_group_record(
|
||||
grpname: impl AsRef<str>,
|
||||
create_blocked: Blocked,
|
||||
create_verified: VerifiedStatus,
|
||||
) -> u32 {
|
||||
) -> ChatId {
|
||||
if sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
@@ -1194,10 +1195,24 @@ fn create_group_record(
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return 0;
|
||||
warn!(
|
||||
context,
|
||||
"Failed to create group '{}' for grpid={}",
|
||||
grpname.as_ref(),
|
||||
grpid.as_ref()
|
||||
);
|
||||
return ChatId::new(0);
|
||||
}
|
||||
|
||||
sql::get_rowid(context, &context.sql, "chats", "grpid", grpid.as_ref())
|
||||
let row_id = sql::get_rowid(context, &context.sql, "chats", "grpid", grpid.as_ref());
|
||||
let chat_id = ChatId::new(row_id);
|
||||
info!(
|
||||
context,
|
||||
"Created group '{}' grpid={} as {}",
|
||||
grpname.as_ref(),
|
||||
grpid.as_ref(),
|
||||
chat_id
|
||||
);
|
||||
chat_id
|
||||
}
|
||||
|
||||
fn create_adhoc_grp_id(context: &Context, member_ids: &[u32]) -> String {
|
||||
@@ -1247,7 +1262,7 @@ fn hex_hash(s: impl AsRef<str>) -> String {
|
||||
fn search_chat_ids_by_contact_ids(
|
||||
context: &Context,
|
||||
unsorted_contact_ids: &[u32],
|
||||
) -> Result<Vec<u32>> {
|
||||
) -> Result<Vec<ChatId>> {
|
||||
/* searches chat_id's by the given contact IDs, may return zero, one or more chat_id's */
|
||||
let mut contact_ids = Vec::with_capacity(23);
|
||||
let mut chat_ids = Vec::with_capacity(23);
|
||||
@@ -1264,19 +1279,19 @@ fn search_chat_ids_by_contact_ids(
|
||||
let contact_ids_str = join(contact_ids.iter().map(|x| x.to_string()), ",");
|
||||
context.sql.query_map(
|
||||
format!(
|
||||
"SELECT DISTINCT cc.chat_id, cc.contact_id \
|
||||
FROM chats_contacts cc \
|
||||
LEFT JOIN chats c ON c.id=cc.chat_id \
|
||||
WHERE cc.chat_id IN(SELECT chat_id FROM chats_contacts WHERE contact_id IN({})) \
|
||||
AND c.type=120 \
|
||||
AND cc.contact_id!=1 \
|
||||
ORDER BY cc.chat_id, cc.contact_id;", // 1=DC_CONTACT_ID_SELF
|
||||
"SELECT DISTINCT cc.chat_id, cc.contact_id
|
||||
FROM chats_contacts cc
|
||||
LEFT JOIN chats c ON c.id=cc.chat_id
|
||||
WHERE cc.chat_id IN(SELECT chat_id FROM chats_contacts WHERE contact_id IN({}))
|
||||
AND c.type=120
|
||||
AND cc.contact_id!=1
|
||||
ORDER BY cc.chat_id, cc.contact_id;", // 1=DC_CONTACT_ID_SELF
|
||||
contact_ids_str
|
||||
),
|
||||
params![],
|
||||
|row| Ok((row.get::<_, u32>(0)?, row.get::<_, u32>(1)?)),
|
||||
|row| Ok((row.get::<_, ChatId>(0)?, row.get::<_, u32>(1)?)),
|
||||
|rows| {
|
||||
let mut last_chat_id = 0;
|
||||
let mut last_chat_id = ChatId::new(0);
|
||||
let mut matches = 0;
|
||||
let mut mismatches = 0;
|
||||
|
||||
@@ -1375,31 +1390,31 @@ fn check_verified_properties(
|
||||
context.is_self_addr(&to_addr)
|
||||
);
|
||||
let mut is_verified = _is_verified != 0;
|
||||
let mut peerstate = Peerstate::from_addr(context, &context.sql, &to_addr);
|
||||
let peerstate = Peerstate::from_addr(context, &context.sql, &to_addr);
|
||||
|
||||
// mark gossiped keys (if any) as verified
|
||||
if mimeparser.gossipped_addr.contains(&to_addr) && peerstate.is_some() {
|
||||
let peerstate = peerstate.as_mut().unwrap();
|
||||
|
||||
// if we're here, we know the gossip key is verified:
|
||||
// - use the gossip-key as verified-key if there is no verified-key
|
||||
// - OR if the verified-key does not match public-key or gossip-key
|
||||
// (otherwise a verified key can _only_ be updated through QR scan which might be annoying,
|
||||
// see https://github.com/nextleap-project/countermitm/issues/46 for a discussion about this point)
|
||||
if !is_verified
|
||||
|| peerstate.verified_key_fingerprint != peerstate.public_key_fingerprint
|
||||
&& peerstate.verified_key_fingerprint != peerstate.gossip_key_fingerprint
|
||||
{
|
||||
info!(context, "{} has verified {}.", contact.get_addr(), to_addr,);
|
||||
let fp = peerstate.gossip_key_fingerprint.clone();
|
||||
if let Some(fp) = fp {
|
||||
peerstate.set_verified(
|
||||
PeerstateKeyType::GossipKey,
|
||||
&fp,
|
||||
PeerstateVerifiedStatus::BidirectVerified,
|
||||
);
|
||||
peerstate.save_to_db(&context.sql, false)?;
|
||||
is_verified = true;
|
||||
if mimeparser.gossipped_addr.contains(&to_addr) {
|
||||
if let Some(mut peerstate) = peerstate {
|
||||
// if we're here, we know the gossip key is verified:
|
||||
// - use the gossip-key as verified-key if there is no verified-key
|
||||
// - OR if the verified-key does not match public-key or gossip-key
|
||||
// (otherwise a verified key can _only_ be updated through QR scan which might be annoying,
|
||||
// see https://github.com/nextleap-project/countermitm/issues/46 for a discussion about this point)
|
||||
if !is_verified
|
||||
|| peerstate.verified_key_fingerprint != peerstate.public_key_fingerprint
|
||||
&& peerstate.verified_key_fingerprint != peerstate.gossip_key_fingerprint
|
||||
{
|
||||
info!(context, "{} has verified {}.", contact.get_addr(), to_addr,);
|
||||
let fp = peerstate.gossip_key_fingerprint.clone();
|
||||
if let Some(fp) = fp {
|
||||
peerstate.set_verified(
|
||||
PeerstateKeyType::GossipKey,
|
||||
&fp,
|
||||
PeerstateVerifiedStatus::BidirectVerified,
|
||||
);
|
||||
peerstate.save_to_db(&context.sql, false)?;
|
||||
is_verified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1625,7 +1640,7 @@ mod tests {
|
||||
hello\x00";
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap();
|
||||
assert_eq!(extract_grpid(&mimeparser, HeaderDef::InReplyTo), None);
|
||||
let grpid = Some("HcxyMARjyJy".to_string());
|
||||
let grpid = Some("HcxyMARjyJy");
|
||||
assert_eq!(extract_grpid(&mimeparser, HeaderDef::References), grpid);
|
||||
}
|
||||
|
||||
@@ -1639,7 +1654,7 @@ mod tests {
|
||||
\n\
|
||||
hello\x00";
|
||||
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap();
|
||||
let grpid = Some("HcxyMARjyJy".to_string());
|
||||
let grpid = Some("HcxyMARjyJy");
|
||||
assert_eq!(extract_grpid(&mimeparser, HeaderDef::InReplyTo), grpid);
|
||||
assert_eq!(extract_grpid(&mimeparser, HeaderDef::References), grpid);
|
||||
}
|
||||
|
||||
@@ -653,7 +653,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[test]
|
||||
fn test_dc_extract_grpid_from_rfc724_mid() {
|
||||
// Should return None if we pass invalid mid
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::path::PathBuf;
|
||||
|
||||
use strum::EnumProperty;
|
||||
|
||||
use crate::chat::ChatId;
|
||||
use crate::message::MsgId;
|
||||
|
||||
impl Event {
|
||||
@@ -107,36 +108,36 @@ pub enum Event {
|
||||
/// - Chats created, deleted or archived
|
||||
/// - A draft has been set
|
||||
#[strum(props(id = "2000"))]
|
||||
MsgsChanged { chat_id: u32, msg_id: MsgId },
|
||||
MsgsChanged { chat_id: ChatId, msg_id: MsgId },
|
||||
|
||||
/// There is a fresh message. Typically, the user will show an notification
|
||||
/// when receiving this message.
|
||||
///
|
||||
/// There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event.
|
||||
#[strum(props(id = "2005"))]
|
||||
IncomingMsg { chat_id: u32, msg_id: MsgId },
|
||||
IncomingMsg { chat_id: ChatId, msg_id: MsgId },
|
||||
|
||||
/// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to
|
||||
/// DC_STATE_OUT_DELIVERED, see dc_msg_get_state().
|
||||
#[strum(props(id = "2010"))]
|
||||
MsgDelivered { chat_id: u32, msg_id: MsgId },
|
||||
MsgDelivered { chat_id: ChatId, msg_id: MsgId },
|
||||
|
||||
/// A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to
|
||||
/// DC_STATE_OUT_FAILED, see dc_msg_get_state().
|
||||
#[strum(props(id = "2012"))]
|
||||
MsgFailed { chat_id: u32, msg_id: MsgId },
|
||||
MsgFailed { chat_id: ChatId, msg_id: MsgId },
|
||||
|
||||
/// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to
|
||||
/// DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state().
|
||||
#[strum(props(id = "2015"))]
|
||||
MsgRead { chat_id: u32, msg_id: MsgId },
|
||||
MsgRead { chat_id: ChatId, msg_id: MsgId },
|
||||
|
||||
/// Chat changed. The name or the image of a chat group was changed or members were added or removed.
|
||||
/// Or the verify state of a chat has changed.
|
||||
/// See dc_set_chat_name(), dc_set_chat_profile_image(), dc_add_contact_to_chat()
|
||||
/// and dc_remove_contact_from_chat().
|
||||
#[strum(props(id = "2020"))]
|
||||
ChatModified(u32),
|
||||
ChatModified(ChatId),
|
||||
|
||||
/// Contact(s) created, renamed, blocked or deleted.
|
||||
///
|
||||
@@ -205,5 +206,5 @@ pub enum Event {
|
||||
/// @param data1 (int) chat_id
|
||||
/// @param data2 (int) contact_id
|
||||
#[strum(props(id = "2062"))]
|
||||
SecurejoinMemberAdded { chat_id: u32, contact_id: u32 },
|
||||
SecurejoinMemberAdded { chat_id: ChatId, contact_id: u32 },
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ pub enum HeaderDef {
|
||||
Cc,
|
||||
Disposition,
|
||||
OriginalMessageId,
|
||||
|
||||
/// Delta Chat extension for message IDs in combined MDNs
|
||||
AdditionalMessageIds,
|
||||
|
||||
ListId,
|
||||
References,
|
||||
InReplyTo,
|
||||
|
||||
@@ -957,8 +957,8 @@ impl Imap {
|
||||
remote_message_id,
|
||||
message_id,
|
||||
);
|
||||
*uid = 0;
|
||||
}
|
||||
*uid = 0;
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
|
||||
291
src/job.rs
291
src/job.rs
@@ -6,15 +6,17 @@
|
||||
use std::{fmt, time};
|
||||
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use itertools::Itertools;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use async_std::task;
|
||||
|
||||
use crate::blob::BlobObject;
|
||||
use crate::chat;
|
||||
use crate::chat::{self, ChatId};
|
||||
use crate::config::Config;
|
||||
use crate::configure::*;
|
||||
use crate::constants::*;
|
||||
use crate::contact::Contact;
|
||||
use crate::context::{Context, PerformJobsNeeded};
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::{Error, Result};
|
||||
@@ -25,7 +27,7 @@ use crate::location;
|
||||
use crate::login_param::LoginParam;
|
||||
use crate::message::MsgId;
|
||||
use crate::message::{self, Message, MessageState};
|
||||
use crate::mimefactory::{vec_contains_lowercase, MimeFactory, RenderedEmail};
|
||||
use crate::mimefactory::{MimeFactory, RenderedEmail};
|
||||
use crate::param::*;
|
||||
use crate::sql;
|
||||
|
||||
@@ -88,9 +90,7 @@ pub enum Action {
|
||||
// Jobs in the SMTP-thread, range from DC_SMTP_THREAD..DC_SMTP_THREAD+999
|
||||
MaybeSendLocations = 5005, // low priority ...
|
||||
MaybeSendLocationsEnded = 5007,
|
||||
SendMdnOld = 5010,
|
||||
SendMdn = 5011,
|
||||
SendMsgToSmtpOld = 5900,
|
||||
SendMdn = 5010,
|
||||
SendMsgToSmtp = 5901, // ... high priority
|
||||
}
|
||||
|
||||
@@ -118,9 +118,7 @@ impl From<Action> for Thread {
|
||||
|
||||
MaybeSendLocations => Thread::Smtp,
|
||||
MaybeSendLocationsEnded => Thread::Smtp,
|
||||
SendMdnOld => Thread::Smtp,
|
||||
SendMdn => Thread::Smtp,
|
||||
SendMsgToSmtpOld => Thread::Smtp,
|
||||
SendMsgToSmtp => Thread::Smtp,
|
||||
}
|
||||
}
|
||||
@@ -171,6 +169,53 @@ impl Job {
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
fn smtp_send<F>(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
recipients: Vec<async_smtp::EmailAddress>,
|
||||
message: Vec<u8>,
|
||||
job_id: u32,
|
||||
success_cb: F,
|
||||
) -> Status
|
||||
where
|
||||
F: FnOnce() -> Result<()>,
|
||||
{
|
||||
// hold the smtp lock during sending of a job and
|
||||
// its ok/error response processing. Note that if a message
|
||||
// was sent we need to mark it in the database ASAP as we
|
||||
// otherwise might send it twice.
|
||||
let mut smtp = context.smtp.lock().unwrap();
|
||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||
info!(context, "smtp-sending out mime message:");
|
||||
println!("{}", String::from_utf8_lossy(&message));
|
||||
}
|
||||
match task::block_on(smtp.send(context, recipients, message, job_id)) {
|
||||
Err(crate::smtp::send::Error::SendError(err)) => {
|
||||
// Remote error, retry later.
|
||||
warn!(context, "SMTP failed to send: {}", err);
|
||||
smtp.disconnect();
|
||||
self.pending_error = Some(err.to_string());
|
||||
Status::RetryLater
|
||||
}
|
||||
Err(crate::smtp::send::Error::EnvelopeError(err)) => {
|
||||
// Local error, job is invalid, do not retry.
|
||||
smtp.disconnect();
|
||||
warn!(context, "SMTP job is invalid: {}", err);
|
||||
Status::Finished(Err(Error::SmtpError(err)))
|
||||
}
|
||||
Err(crate::smtp::send::Error::NoTransport) => {
|
||||
// Should never happen.
|
||||
// It does not even make sense to disconnect here.
|
||||
error!(context, "SMTP job failed because SMTP has no transport");
|
||||
Status::Finished(Err(format_err!("SMTP has not transport")))
|
||||
}
|
||||
Ok(()) => {
|
||||
job_try!(success_cb());
|
||||
Status::Finished(Ok(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn SendMsgToSmtp(&mut self, context: &Context) -> Status {
|
||||
/* connect to SMTP server, if not yet done */
|
||||
@@ -216,45 +261,118 @@ impl Job {
|
||||
)));
|
||||
};
|
||||
|
||||
// hold the smtp lock during sending of a job and
|
||||
// its ok/error response processing. Note that if a message
|
||||
// was sent we need to mark it in the database ASAP as we
|
||||
// otherwise might send it twice.
|
||||
let mut smtp = context.smtp.lock().unwrap();
|
||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||
info!(context, "smtp-sending out mime message:");
|
||||
println!("{}", String::from_utf8_lossy(&body));
|
||||
}
|
||||
match task::block_on(smtp.send(context, recipients_list, body, self.job_id)) {
|
||||
Err(crate::smtp::send::Error::SendError(err)) => {
|
||||
// Remote error, retry later.
|
||||
warn!(context, "SMTP failed to send: {}", err);
|
||||
smtp.disconnect();
|
||||
self.pending_error = Some(err.to_string());
|
||||
Status::RetryLater
|
||||
let foreign_id = self.foreign_id;
|
||||
self.smtp_send(context, recipients_list, body, self.job_id, || {
|
||||
// smtp success, update db ASAP, then delete smtp file
|
||||
if 0 != foreign_id {
|
||||
set_delivered(context, MsgId::new(foreign_id));
|
||||
}
|
||||
Err(crate::smtp::send::Error::EnvelopeError(err)) => {
|
||||
// Local error, job is invalid, do not retry.
|
||||
smtp.disconnect();
|
||||
warn!(context, "SMTP job is invalid: {}", err);
|
||||
Status::Finished(Err(Error::SmtpError(err)))
|
||||
}
|
||||
Err(crate::smtp::send::Error::NoTransport) => {
|
||||
// Should never happen.
|
||||
// It does not even make sense to disconnect here.
|
||||
error!(context, "SMTP job failed because SMTP has no transport");
|
||||
Status::Finished(Err(format_err!("SMTP has not transport")))
|
||||
}
|
||||
Ok(()) => {
|
||||
// smtp success, update db ASAP, then delete smtp file
|
||||
if 0 != self.foreign_id {
|
||||
set_delivered(context, MsgId::new(self.foreign_id));
|
||||
}
|
||||
// now also delete the generated file
|
||||
dc_delete_file(context, filename);
|
||||
Status::Finished(Ok(()))
|
||||
// now also delete the generated file
|
||||
dc_delete_file(context, filename);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Get `SendMdn` jobs with foreign_id equal to `contact_id` excluding the `job_id` job.
|
||||
fn get_additional_mdn_jobs(
|
||||
&self,
|
||||
context: &Context,
|
||||
contact_id: u32,
|
||||
) -> sql::Result<(Vec<u32>, Vec<String>)> {
|
||||
// Extract message IDs from job parameters
|
||||
let res: Vec<(u32, MsgId)> = context.sql.query_map(
|
||||
"SELECT id, param FROM jobs WHERE foreign_id=? AND id!=?",
|
||||
params![contact_id, self.job_id],
|
||||
|row| {
|
||||
let job_id: u32 = row.get(0)?;
|
||||
let params_str: String = row.get(1)?;
|
||||
let params: Params = params_str.parse().unwrap_or_default();
|
||||
Ok((job_id, params))
|
||||
},
|
||||
|jobs| {
|
||||
let res = jobs
|
||||
.filter_map(|row| {
|
||||
let (job_id, params) = row.ok()?;
|
||||
let msg_id = params.get_msg_id()?;
|
||||
Some((job_id, msg_id))
|
||||
})
|
||||
.collect();
|
||||
Ok(res)
|
||||
},
|
||||
)?;
|
||||
|
||||
// Load corresponding RFC724 message IDs
|
||||
let mut job_ids = Vec::new();
|
||||
let mut rfc724_mids = Vec::new();
|
||||
for (job_id, msg_id) in res {
|
||||
if let Ok(Message { rfc724_mid, .. }) = Message::load_from_db(context, msg_id) {
|
||||
job_ids.push(job_id);
|
||||
rfc724_mids.push(rfc724_mid);
|
||||
}
|
||||
}
|
||||
Ok((job_ids, rfc724_mids))
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn SendMdn(&mut self, context: &Context) -> Status {
|
||||
if !context.get_config_bool(Config::MdnsEnabled) {
|
||||
// User has disabled MDNs after job scheduling but before
|
||||
// execution.
|
||||
return Status::Finished(Err(format_err!("MDNs are disabled")));
|
||||
}
|
||||
|
||||
let contact_id = self.foreign_id;
|
||||
let contact = job_try!(Contact::load_from_db(context, contact_id));
|
||||
if contact.is_blocked() {
|
||||
return Status::Finished(Err(format_err!("Contact is blocked")));
|
||||
}
|
||||
|
||||
let msg_id = if let Some(msg_id) = self.param.get_msg_id() {
|
||||
msg_id
|
||||
} else {
|
||||
return Status::Finished(Err(format_err!(
|
||||
"SendMdn job has invalid parameters: {}",
|
||||
self.param
|
||||
)));
|
||||
};
|
||||
|
||||
// Try to aggregate other SendMdn jobs and send a combined MDN.
|
||||
let (additional_job_ids, additional_rfc724_mids) = self
|
||||
.get_additional_mdn_jobs(context, contact_id)
|
||||
.unwrap_or_default();
|
||||
|
||||
if !additional_rfc724_mids.is_empty() {
|
||||
info!(
|
||||
context,
|
||||
"SendMdn job: aggregating {} additional MDNs",
|
||||
additional_rfc724_mids.len()
|
||||
)
|
||||
}
|
||||
|
||||
let msg = job_try!(Message::load_from_db(context, msg_id));
|
||||
let mimefactory = job_try!(MimeFactory::from_mdn(context, &msg, additional_rfc724_mids));
|
||||
let rendered_msg = job_try!(mimefactory.render());
|
||||
let body = rendered_msg.message;
|
||||
|
||||
let addr = contact.get_addr();
|
||||
let recipient = job_try!(async_smtp::EmailAddress::new(addr.to_string())
|
||||
.map_err(|err| format_err!("invalid recipient: {} {:?}", addr, err)));
|
||||
let recipients = vec![recipient];
|
||||
|
||||
/* connect to SMTP server, if not yet done */
|
||||
if !context.smtp.lock().unwrap().is_connected() {
|
||||
let loginparam = LoginParam::from_database(context, "configured_");
|
||||
if let Err(err) = context.smtp.lock().unwrap().connect(context, &loginparam) {
|
||||
warn!(context, "SMTP connection failure: {:?}", err);
|
||||
return Status::RetryLater;
|
||||
}
|
||||
}
|
||||
|
||||
self.smtp_send(context, recipients, body, self.job_id, || {
|
||||
// Remove additional SendMdn jobs we have aggretated into this one.
|
||||
job_kill_ids(context, &additional_job_ids)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
@@ -363,7 +481,7 @@ impl Job {
|
||||
if msg.param.get_bool(Param::WantsMdn).unwrap_or_default()
|
||||
&& context.get_config_bool(Config::MdnsEnabled)
|
||||
{
|
||||
if let Err(err) = send_mdn(context, msg.id) {
|
||||
if let Err(err) = send_mdn(context, &msg) {
|
||||
warn!(context, "could not send out mdn for {}: {}", msg.id, err);
|
||||
return Status::Finished(Err(err));
|
||||
}
|
||||
@@ -422,6 +540,19 @@ pub fn job_kill_action(context: &Context, action: Action) -> bool {
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// Remove jobs with specified IDs.
|
||||
pub fn job_kill_ids(context: &Context, job_ids: &[u32]) -> sql::Result<()> {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
format!(
|
||||
"DELETE FROM jobs WHERE id IN({})",
|
||||
job_ids.iter().map(|_| "?").join(",")
|
||||
),
|
||||
job_ids,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn perform_inbox_fetch(context: &Context) {
|
||||
let use_network = context.get_config_bool(Config::InboxWatch);
|
||||
|
||||
@@ -633,7 +764,7 @@ pub fn job_action_exists(context: &Context, action: Action) -> bool {
|
||||
|
||||
fn set_delivered(context: &Context, msg_id: MsgId) {
|
||||
message::update_msg_state(context, msg_id, MessageState::OutDelivered);
|
||||
let chat_id: i32 = context
|
||||
let chat_id: ChatId = context
|
||||
.sql
|
||||
.query_get_value(
|
||||
context,
|
||||
@@ -641,10 +772,7 @@ fn set_delivered(context: &Context, msg_id: MsgId) {
|
||||
params![msg_id],
|
||||
)
|
||||
.unwrap_or_default();
|
||||
context.call_cb(Event::MsgDelivered {
|
||||
chat_id: chat_id as u32,
|
||||
msg_id,
|
||||
});
|
||||
context.call_cb(Event::MsgDelivered { chat_id, msg_id });
|
||||
}
|
||||
|
||||
/* special case for DC_JOB_SEND_MSG_TO_SMTP */
|
||||
@@ -683,8 +811,12 @@ pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<()> {
|
||||
);
|
||||
}
|
||||
|
||||
let lowercase_from = rendered_msg.from.to_lowercase();
|
||||
if context.get_config_bool(Config::BccSelf)
|
||||
&& !vec_contains_lowercase(&rendered_msg.recipients, &rendered_msg.from)
|
||||
&& !rendered_msg
|
||||
.recipients
|
||||
.iter()
|
||||
.any(|x| x.to_lowercase() == lowercase_from)
|
||||
{
|
||||
rendered_msg.recipients.push(rendered_msg.from.clone());
|
||||
}
|
||||
@@ -727,7 +859,7 @@ pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<()> {
|
||||
msg.save_param_to_disk(context);
|
||||
}
|
||||
|
||||
add_smtp_job(context, Action::SendMsgToSmtp, &rendered_msg)?;
|
||||
add_smtp_job(context, Action::SendMsgToSmtp, msg.id, &rendered_msg)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -752,9 +884,7 @@ pub fn perform_sentbox_jobs(context: &Context) {
|
||||
}
|
||||
|
||||
fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
let jobs: Vec<Job> = load_jobs(context, thread, probe_network);
|
||||
|
||||
for mut job in jobs {
|
||||
while let Some(mut job) = load_next_job(context, thread, probe_network) {
|
||||
info!(context, "{}-job {} started...", thread, job);
|
||||
|
||||
// some configuration jobs are "exclusive":
|
||||
@@ -793,7 +923,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
Action::MarkseenMsgOnImap => job.MarkseenMsgOnImap(context),
|
||||
Action::MarkseenMdnOnImap => job.MarkseenMdnOnImap(context),
|
||||
Action::MoveMsg => job.MoveMsg(context),
|
||||
Action::SendMdn => job.SendMsgToSmtp(context),
|
||||
Action::SendMdn => job.SendMdn(context),
|
||||
Action::ConfigureImap => JobConfigureImap(context),
|
||||
Action::ImexImap => match JobImexImap(context, &job) {
|
||||
Ok(()) => Status::Finished(Ok(())),
|
||||
@@ -810,8 +940,6 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
sql::housekeeping(context);
|
||||
Status::Finished(Ok(()))
|
||||
}
|
||||
Action::SendMdnOld => Status::Finished(Ok(())),
|
||||
Action::SendMsgToSmtpOld => Status::Finished(Ok(())),
|
||||
};
|
||||
|
||||
info!(
|
||||
@@ -907,10 +1035,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
"{} removes job {} as it failed with error {:?}", thread, job, err
|
||||
);
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"{} removes job {} as it cannot be retried", thread, job
|
||||
);
|
||||
info!(context, "{} removes job {} as it succeeded", thread, job);
|
||||
}
|
||||
|
||||
job.delete(context);
|
||||
@@ -942,17 +1067,21 @@ fn suspend_smtp_thread(context: &Context, suspend: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_mdn(context: &Context, msg_id: MsgId) -> Result<()> {
|
||||
let msg = Message::load_from_db(context, msg_id)?;
|
||||
let mimefactory = MimeFactory::from_mdn(context, &msg)?;
|
||||
let rendered_msg = mimefactory.render()?;
|
||||
fn send_mdn(context: &Context, msg: &Message) -> Result<()> {
|
||||
let mut param = Params::new();
|
||||
param.set(Param::MsgId, msg.id.to_u32().to_string());
|
||||
|
||||
add_smtp_job(context, Action::SendMdn, &rendered_msg)?;
|
||||
job_add(context, Action::SendMdn, msg.from_id as i32, param, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_smtp_job(context: &Context, action: Action, rendered_msg: &RenderedEmail) -> Result<()> {
|
||||
fn add_smtp_job(
|
||||
context: &Context,
|
||||
action: Action,
|
||||
msg_id: MsgId,
|
||||
rendered_msg: &RenderedEmail,
|
||||
) -> Result<()> {
|
||||
ensure!(
|
||||
!rendered_msg.recipients.is_empty(),
|
||||
"no recipients for smtp job set"
|
||||
@@ -965,16 +1094,7 @@ fn add_smtp_job(context: &Context, action: Action, rendered_msg: &RenderedEmail)
|
||||
param.set(Param::File, blob.as_name());
|
||||
param.set(Param::Recipients, &recipients);
|
||||
|
||||
job_add(
|
||||
context,
|
||||
action,
|
||||
rendered_msg
|
||||
.foreign_id
|
||||
.map(|v| v.to_u32() as i32)
|
||||
.unwrap_or_default(),
|
||||
param,
|
||||
0,
|
||||
);
|
||||
job_add(context, action, msg_id.to_u32() as i32, param, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1035,7 +1155,7 @@ pub fn interrupt_smtp_idle(context: &Context) {
|
||||
/// IMAP jobs. The `probe_network` parameter decides how to query
|
||||
/// jobs, this is tricky and probably wrong currently. Look at the
|
||||
/// SQL queries for details.
|
||||
fn load_jobs(context: &Context, thread: Thread, probe_network: bool) -> Vec<Job> {
|
||||
fn load_next_job(context: &Context, thread: Thread, probe_network: bool) -> Option<Job> {
|
||||
let query = if !probe_network {
|
||||
// processing for first-try and after backoff-timeouts:
|
||||
// process jobs in the order they were added.
|
||||
@@ -1077,14 +1197,13 @@ fn load_jobs(context: &Context, thread: Thread, probe_network: bool) -> Vec<Job>
|
||||
Ok(job)
|
||||
},
|
||||
|jobs| {
|
||||
let mut ret: Vec<Job> = Vec::new();
|
||||
for job in jobs {
|
||||
match job {
|
||||
Ok(j) => ret.push(j),
|
||||
Ok(j) => return Ok(Some(j)),
|
||||
Err(e) => warn!(context, "Bad job from the database: {}", e),
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
Ok(None)
|
||||
},
|
||||
)
|
||||
.unwrap_or_default()
|
||||
@@ -1117,15 +1236,17 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_jobs() {
|
||||
fn test_load_next_job() {
|
||||
// We want to ensure that loading jobs skips over jobs which
|
||||
// fails to load from the database instead of failing to load
|
||||
// all jobs.
|
||||
let t = dummy_context();
|
||||
insert_job(&t.ctx, 0);
|
||||
insert_job(&t.ctx, -1); // This can not be loaded into Job struct.
|
||||
let jobs = load_next_job(&t.ctx, Thread::from(Action::MoveMsg), false);
|
||||
assert!(jobs.is_none());
|
||||
|
||||
insert_job(&t.ctx, 1);
|
||||
let jobs = load_jobs(&t.ctx, Thread::from(Action::MoveMsg), false);
|
||||
assert_eq!(jobs.len(), 2);
|
||||
let jobs = load_next_job(&t.ctx, Thread::from(Action::MoveMsg), false);
|
||||
assert!(jobs.is_some());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::correctness, missing_debug_implementations, clippy::all)]
|
||||
// for now we hide warnings to not clutter/hide errors during "cargo clippy"
|
||||
#![allow(clippy::cognitive_complexity, clippy::too_many_arguments)]
|
||||
#![allow(clippy::match_bool)]
|
||||
#![feature(ptr_wrapping_offset_from)]
|
||||
#![feature(drain_filter)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate failure_derive;
|
||||
|
||||
@@ -4,7 +4,7 @@ use bitflags::bitflags;
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::chat;
|
||||
use crate::chat::{self, ChatId};
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::context::*;
|
||||
@@ -28,7 +28,7 @@ pub struct Location {
|
||||
pub timestamp: i64,
|
||||
pub contact_id: u32,
|
||||
pub msg_id: u32,
|
||||
pub chat_id: u32,
|
||||
pub chat_id: ChatId,
|
||||
pub marker: Option<String>,
|
||||
pub independent: u32,
|
||||
}
|
||||
@@ -193,9 +193,9 @@ impl Kml {
|
||||
}
|
||||
|
||||
// location streaming
|
||||
pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) {
|
||||
pub fn send_locations_to_chat(context: &Context, chat_id: ChatId, seconds: i64) {
|
||||
let now = time();
|
||||
if !(seconds < 0 || chat_id <= DC_CHAT_ID_LAST_SPECIAL) {
|
||||
if !(seconds < 0 || chat_id.is_special()) {
|
||||
let is_sending_locations_before = is_sending_locations_to_chat(context, chat_id);
|
||||
if sql::execute(
|
||||
context,
|
||||
@@ -207,7 +207,7 @@ pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) {
|
||||
params![
|
||||
if 0 != seconds { now } else { 0 },
|
||||
if 0 != seconds { now + seconds } else { 0 },
|
||||
chat_id as i32,
|
||||
chat_id,
|
||||
],
|
||||
)
|
||||
.is_ok()
|
||||
@@ -229,7 +229,7 @@ pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) {
|
||||
job_add(
|
||||
context,
|
||||
job::Action::MaybeSendLocationsEnded,
|
||||
chat_id as i32,
|
||||
chat_id.to_u32() as i32,
|
||||
Params::new(),
|
||||
seconds + 1,
|
||||
);
|
||||
@@ -251,12 +251,12 @@ fn schedule_MAYBE_SEND_LOCATIONS(context: &Context, force_schedule: bool) {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn is_sending_locations_to_chat(context: &Context, chat_id: u32) -> bool {
|
||||
pub fn is_sending_locations_to_chat(context: &Context, chat_id: ChatId) -> bool {
|
||||
context
|
||||
.sql
|
||||
.exists(
|
||||
"SELECT id FROM chats WHERE (? OR id=?) AND locations_send_until>?;",
|
||||
params![if chat_id == 0 { 1 } else { 0 }, chat_id as i32, time()],
|
||||
params![if chat_id.is_unset() { 1 } else { 0 }, chat_id, time()],
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
@@ -302,7 +302,7 @@ pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> b
|
||||
|
||||
pub fn get_range(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
chat_id: ChatId,
|
||||
contact_id: u32,
|
||||
timestamp_from: i64,
|
||||
mut timestamp_to: i64,
|
||||
@@ -320,8 +320,8 @@ pub fn get_range(
|
||||
AND (l.independent=1 OR (l.timestamp>=? AND l.timestamp<=?)) \
|
||||
ORDER BY l.timestamp DESC, l.id DESC, msg_id DESC;",
|
||||
params![
|
||||
if chat_id == 0 { 1 } else { 0 },
|
||||
chat_id as i32,
|
||||
if chat_id.is_unset() { 1 } else { 0 },
|
||||
chat_id,
|
||||
if contact_id == 0 { 1 } else { 0 },
|
||||
contact_id as i32,
|
||||
timestamp_from,
|
||||
@@ -371,7 +371,7 @@ pub fn delete_all(context: &Context) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_kml(context: &Context, chat_id: u32) -> Result<(String, u32), Error> {
|
||||
pub fn get_kml(context: &Context, chat_id: ChatId) -> Result<(String, u32), Error> {
|
||||
let mut last_added_location_id = 0;
|
||||
|
||||
let self_addr = context
|
||||
@@ -380,7 +380,7 @@ pub fn get_kml(context: &Context, chat_id: u32) -> Result<(String, u32), Error>
|
||||
|
||||
let (locations_send_begin, locations_send_until, locations_last_sent) = context.sql.query_row(
|
||||
"SELECT locations_send_begin, locations_send_until, locations_last_sent FROM chats WHERE id=?;",
|
||||
params![chat_id as i32], |row| {
|
||||
params![chat_id], |row| {
|
||||
let send_begin: i64 = row.get(0)?;
|
||||
let send_until: i64 = row.get(1)?;
|
||||
let last_sent: i64 = row.get(2)?;
|
||||
@@ -465,14 +465,14 @@ pub fn get_message_kml(timestamp: i64, latitude: f64, longitude: f64) -> String
|
||||
|
||||
pub fn set_kml_sent_timestamp(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
chat_id: ChatId,
|
||||
timestamp: i64,
|
||||
) -> Result<(), Error> {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET locations_last_sent=? WHERE id=?;",
|
||||
params![timestamp, chat_id as i32],
|
||||
params![timestamp, chat_id],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -495,12 +495,12 @@ pub fn set_msg_location_id(
|
||||
|
||||
pub fn save(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
chat_id: ChatId,
|
||||
contact_id: u32,
|
||||
locations: &[Location],
|
||||
independent: bool,
|
||||
) -> Result<u32, Error> {
|
||||
ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat id");
|
||||
ensure!(!chat_id.is_special(), "Invalid chat id");
|
||||
context
|
||||
.sql
|
||||
.prepare2(
|
||||
@@ -520,7 +520,7 @@ pub fn save(
|
||||
stmt_insert.execute(params![
|
||||
location.timestamp,
|
||||
contact_id as i32,
|
||||
chat_id as i32,
|
||||
chat_id,
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
location.accuracy,
|
||||
@@ -562,7 +562,7 @@ pub fn JobMaybeSendLocations(context: &Context, _job: &Job) -> job::Status {
|
||||
WHERE locations_send_until>?;",
|
||||
params![now],
|
||||
|row| {
|
||||
let chat_id: i32 = row.get(0)?;
|
||||
let chat_id: ChatId = row.get(0)?;
|
||||
let locations_send_begin: i64 = row.get(1)?;
|
||||
let locations_last_sent: i64 = row.get(2)?;
|
||||
continue_streaming = true;
|
||||
@@ -629,7 +629,7 @@ pub fn JobMaybeSendLocations(context: &Context, _job: &Job) -> job::Status {
|
||||
|
||||
for (chat_id, mut msg) in msgs.into_iter() {
|
||||
// TODO: better error handling
|
||||
chat::send_msg(context, chat_id as u32, &mut msg).unwrap_or_default();
|
||||
chat::send_msg(context, chat_id, &mut msg).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
if continue_streaming {
|
||||
@@ -644,11 +644,11 @@ pub fn JobMaybeSendLocationsEnded(context: &Context, job: &mut Job) -> job::Stat
|
||||
// the function checks, if location-streaming is really ended;
|
||||
// if so, a device-message is added if not yet done.
|
||||
|
||||
let chat_id = job.foreign_id;
|
||||
let chat_id = ChatId::new(job.foreign_id);
|
||||
|
||||
let (send_begin, send_until) = job_try!(context.sql.query_row(
|
||||
"SELECT locations_send_begin, locations_send_until FROM chats WHERE id=?",
|
||||
params![chat_id as i32],
|
||||
params![chat_id],
|
||||
|row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?)),
|
||||
));
|
||||
|
||||
@@ -660,7 +660,7 @@ pub fn JobMaybeSendLocationsEnded(context: &Context, job: &mut Job) -> job::Stat
|
||||
// not streaming, device-message already sent
|
||||
job_try!(context.sql.execute(
|
||||
"UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?",
|
||||
params![chat_id as i32],
|
||||
params![chat_id],
|
||||
));
|
||||
|
||||
let stock_str = context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0);
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use failure::Fail;
|
||||
|
||||
use crate::chat::{self, Chat};
|
||||
use crate::chat::{self, Chat, ChatId};
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::*;
|
||||
@@ -176,7 +176,7 @@ pub struct Message {
|
||||
pub(crate) id: MsgId,
|
||||
pub(crate) from_id: u32,
|
||||
pub(crate) to_id: u32,
|
||||
pub(crate) chat_id: u32,
|
||||
pub(crate) chat_id: ChatId,
|
||||
pub(crate) viewtype: Viewtype,
|
||||
pub(crate) state: MessageState,
|
||||
pub(crate) hidden: bool,
|
||||
@@ -400,9 +400,9 @@ impl Message {
|
||||
self.from_id
|
||||
}
|
||||
|
||||
pub fn get_chat_id(&self) -> u32 {
|
||||
pub fn get_chat_id(&self) -> ChatId {
|
||||
if self.chat_blocked != Blocked::Not {
|
||||
1
|
||||
ChatId::new(DC_CHAT_ID_DEADDROP)
|
||||
} else {
|
||||
self.chat_id
|
||||
}
|
||||
@@ -610,18 +610,48 @@ impl Message {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
|
||||
#[repr(i32)]
|
||||
pub enum MessageState {
|
||||
Undefined = 0,
|
||||
|
||||
/// Incoming *fresh* message. Fresh messages are neither noticed
|
||||
/// nor seen and are typically shown in notifications.
|
||||
InFresh = 10,
|
||||
|
||||
/// Incoming *noticed* message. E.g. chat opened but message not
|
||||
/// yet read - noticed messages are not counted as unread but did
|
||||
/// not marked as read nor resulted in MDNs.
|
||||
InNoticed = 13,
|
||||
|
||||
/// Incoming message, really *seen* by the user. Marked as read on
|
||||
/// IMAP and MDN may be sent.
|
||||
InSeen = 16,
|
||||
|
||||
/// For files which need time to be prepared before they can be
|
||||
/// sent, the message enters this state before
|
||||
/// OutPending.
|
||||
OutPreparing = 18,
|
||||
|
||||
/// Message saved as draft.
|
||||
OutDraft = 19,
|
||||
|
||||
/// The user has pressed the "send" button but the message is not
|
||||
/// yet sent and is pending in some way. Maybe we're offline (no
|
||||
/// checkmark).
|
||||
OutPending = 20,
|
||||
|
||||
/// *Unrecoverable* error (*recoverable* errors result in pending
|
||||
/// messages).
|
||||
OutFailed = 24,
|
||||
|
||||
/// Outgoing message successfully delivered to server (one
|
||||
/// checkmark). Note, that already delivered messages may get into
|
||||
/// the OutFailed state if we get such a hint from the server.
|
||||
OutDelivered = 26,
|
||||
|
||||
/// Outgoing message read by the recipient (two checkmarks; this
|
||||
/// requires goodwill on the receiver's side)
|
||||
OutMdnRcvd = 28,
|
||||
}
|
||||
|
||||
@@ -631,6 +661,27 @@ impl Default for MessageState {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MessageState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::Undefined => "Undefined",
|
||||
Self::InFresh => "Fresh",
|
||||
Self::InNoticed => "Noticed",
|
||||
Self::InSeen => "Seen",
|
||||
Self::OutPreparing => "Preparing",
|
||||
Self::OutDraft => "Draft",
|
||||
Self::OutPending => "Pending",
|
||||
Self::OutFailed => "Failed",
|
||||
Self::OutDelivered => "Delivered",
|
||||
Self::OutMdnRcvd => "Read",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MessageState> for LotState {
|
||||
fn from(s: MessageState) -> Self {
|
||||
use MessageState::*;
|
||||
@@ -686,7 +737,7 @@ impl Lot {
|
||||
self.text1 = None;
|
||||
self.text1_meaning = Meaning::None;
|
||||
} else {
|
||||
if chat.id == DC_CHAT_ID_DEADDROP {
|
||||
if chat.id.is_deaddrop() {
|
||||
if let Some(contact) = contact {
|
||||
self.text1 = Some(contact.get_display_name().into());
|
||||
} else {
|
||||
@@ -785,19 +836,7 @@ pub fn get_msg_info(context: &Context, msg_id: MsgId) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
ret += "State: ";
|
||||
use MessageState::*;
|
||||
match msg.state {
|
||||
InFresh => ret += "Fresh",
|
||||
InNoticed => ret += "Noticed",
|
||||
InSeen => ret += "Seen",
|
||||
OutDelivered => ret += "Delivered",
|
||||
OutFailed => ret += "Failed",
|
||||
OutMdnRcvd => ret += "Read",
|
||||
OutPending => ret += "Pending",
|
||||
OutPreparing => ret += "Preparing",
|
||||
_ => ret += &format!("{}", msg.state),
|
||||
}
|
||||
ret += &format!("State: {}", msg.state);
|
||||
|
||||
if msg.has_location() {
|
||||
ret += ", Location sent";
|
||||
@@ -890,7 +929,7 @@ pub fn delete_msgs(context: &Context, msg_ids: &[MsgId]) {
|
||||
delete_poi_location(context, msg.location_id);
|
||||
}
|
||||
}
|
||||
update_msg_chat_id(context, *msg_id, DC_CHAT_ID_TRASH);
|
||||
update_msg_chat_id(context, *msg_id, ChatId::new(DC_CHAT_ID_TRASH));
|
||||
job_add(
|
||||
context,
|
||||
Action::DeleteMsgOnImap,
|
||||
@@ -902,7 +941,7 @@ pub fn delete_msgs(context: &Context, msg_ids: &[MsgId]) {
|
||||
|
||||
if !msg_ids.is_empty() {
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
job_kill_action(context, Action::Housekeeping);
|
||||
@@ -910,12 +949,12 @@ pub fn delete_msgs(context: &Context, msg_ids: &[MsgId]) {
|
||||
};
|
||||
}
|
||||
|
||||
fn update_msg_chat_id(context: &Context, msg_id: MsgId, chat_id: u32) -> bool {
|
||||
fn update_msg_chat_id(context: &Context, msg_id: MsgId, chat_id: ChatId) -> bool {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET chat_id=? WHERE id=?;",
|
||||
params![chat_id as i32, msg_id],
|
||||
params![chat_id, msg_id],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
@@ -994,7 +1033,7 @@ pub fn markseen_msgs(context: &Context, msg_ids: &[MsgId]) -> bool {
|
||||
|
||||
if send_event {
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
chat_id: ChatId::new(0),
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
@@ -1105,14 +1144,14 @@ pub fn exists(context: &Context, msg_id: MsgId) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
let chat_id: Option<u32> = context.sql.query_get_value(
|
||||
let chat_id: Option<ChatId> = context.sql.query_get_value(
|
||||
context,
|
||||
"SELECT chat_id FROM msgs WHERE id=?;",
|
||||
params![msg_id],
|
||||
);
|
||||
|
||||
if let Some(chat_id) = chat_id {
|
||||
chat_id != DC_CHAT_ID_TRASH
|
||||
!chat_id.is_trash()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -1150,7 +1189,7 @@ pub fn mdn_from_ext(
|
||||
from_id: u32,
|
||||
rfc724_mid: &str,
|
||||
timestamp_sent: i64,
|
||||
) -> Option<(u32, MsgId)> {
|
||||
) -> Option<(ChatId, MsgId)> {
|
||||
if from_id <= DC_MSG_ID_LAST_SPECIAL || rfc724_mid.is_empty() {
|
||||
return None;
|
||||
}
|
||||
@@ -1170,7 +1209,7 @@ pub fn mdn_from_ext(
|
||||
|row| {
|
||||
Ok((
|
||||
row.get::<_, MsgId>("msg_id")?,
|
||||
row.get::<_, u32>("chat_id")?,
|
||||
row.get::<_, ChatId>("chat_id")?,
|
||||
row.get::<_, Chattype>("type")?,
|
||||
row.get::<_, MessageState>("state")?,
|
||||
))
|
||||
|
||||
@@ -11,36 +11,35 @@ use crate::dc_tools::*;
|
||||
use crate::e2ee::*;
|
||||
use crate::error::Error;
|
||||
use crate::location;
|
||||
use crate::message::MsgId;
|
||||
use crate::message::{self, Message};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::param::*;
|
||||
use crate::peerstate::{Peerstate, PeerstateVerifiedStatus};
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Loaded {
|
||||
Message,
|
||||
MDN,
|
||||
Message { chat: Chat },
|
||||
MDN { additional_msg_ids: Vec<String> },
|
||||
}
|
||||
|
||||
/// Helper to construct mime messages.
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MimeFactory<'a, 'b> {
|
||||
pub from_addr: String,
|
||||
pub from_displayname: String,
|
||||
pub selfstatus: String,
|
||||
pub recipients_names: Vec<String>,
|
||||
pub recipients_addr: Vec<String>,
|
||||
pub timestamp: i64,
|
||||
pub loaded: Loaded,
|
||||
pub msg: &'b Message,
|
||||
pub chat: Option<Chat>,
|
||||
pub increation: bool,
|
||||
pub in_reply_to: String,
|
||||
pub references: String,
|
||||
pub req_mdn: bool,
|
||||
pub context: &'a Context,
|
||||
from_addr: String,
|
||||
from_displayname: String,
|
||||
selfstatus: String,
|
||||
|
||||
/// Vector of pairs of recipient name and address
|
||||
recipients: Vec<(String, String)>,
|
||||
|
||||
timestamp: i64,
|
||||
loaded: Loaded,
|
||||
msg: &'b Message,
|
||||
in_reply_to: String,
|
||||
references: String,
|
||||
req_mdn: bool,
|
||||
context: &'a Context,
|
||||
last_added_location_id: u32,
|
||||
attach_selfavatar: bool,
|
||||
}
|
||||
@@ -53,8 +52,6 @@ pub struct RenderedEmail {
|
||||
pub is_encrypted: bool,
|
||||
pub is_gossiped: bool,
|
||||
pub last_added_location_id: u32,
|
||||
/// None for MDN, the message id otherwise
|
||||
pub foreign_id: Option<MsgId>,
|
||||
|
||||
pub from: String,
|
||||
pub recipients: Vec<String>,
|
||||
@@ -71,44 +68,22 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
) -> Result<MimeFactory<'a, 'b>, Error> {
|
||||
let chat = Chat::load_from_db(context, msg.chat_id)?;
|
||||
|
||||
let mut factory = MimeFactory {
|
||||
from_addr: context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.unwrap_or_default(),
|
||||
from_displayname: context.get_config(Config::Displayname).unwrap_or_default(),
|
||||
selfstatus: context
|
||||
.get_config(Config::Selfstatus)
|
||||
.unwrap_or_else(|| context.stock_str(StockMessage::StatusLine).to_string()),
|
||||
recipients_names: Vec::with_capacity(5),
|
||||
recipients_addr: Vec::with_capacity(5),
|
||||
timestamp: msg.timestamp_sort,
|
||||
loaded: Loaded::Message,
|
||||
msg,
|
||||
chat: Some(chat),
|
||||
increation: msg.is_increation(),
|
||||
in_reply_to: String::default(),
|
||||
references: String::default(),
|
||||
req_mdn: false,
|
||||
last_added_location_id: 0,
|
||||
attach_selfavatar: add_selfavatar,
|
||||
context,
|
||||
};
|
||||
|
||||
// just set the chat above
|
||||
let chat = factory.chat.as_ref().unwrap();
|
||||
let from_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.unwrap_or_default();
|
||||
let from_displayname = context.get_config(Config::Displayname).unwrap_or_default();
|
||||
let mut recipients = Vec::with_capacity(5);
|
||||
let mut req_mdn = false;
|
||||
|
||||
if chat.is_self_talk() {
|
||||
factory
|
||||
.recipients_names
|
||||
.push(factory.from_displayname.to_string());
|
||||
factory.recipients_addr.push(factory.from_addr.to_string());
|
||||
recipients.push((from_displayname.to_string(), from_addr.to_string()));
|
||||
} else {
|
||||
context.sql.query_map(
|
||||
"SELECT c.authname, c.addr \
|
||||
FROM chats_contacts cc \
|
||||
LEFT JOIN contacts c ON cc.contact_id=c.id \
|
||||
WHERE cc.chat_id=? AND cc.contact_id>9;",
|
||||
params![factory.msg.chat_id as i32],
|
||||
params![msg.chat_id],
|
||||
|row| {
|
||||
let authname: String = row.get(0)?;
|
||||
let addr: String = row.get(1)?;
|
||||
@@ -117,17 +92,15 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
|rows| {
|
||||
for row in rows {
|
||||
let (authname, addr) = row?;
|
||||
if !vec_contains_lowercase(&factory.recipients_addr, &addr) {
|
||||
factory.recipients_addr.push(addr);
|
||||
factory.recipients_names.push(authname);
|
||||
if !recipients_contain_addr(&recipients, &addr) {
|
||||
recipients.push((authname, addr));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
let command = factory.msg.param.get_cmd();
|
||||
let msg = &factory.msg;
|
||||
let command = msg.param.get_cmd();
|
||||
|
||||
/* for added members, the list is just fine */
|
||||
if command == SystemMessage::MemberRemovedFromGroup {
|
||||
@@ -139,20 +112,19 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
|
||||
if !email_to_remove.is_empty()
|
||||
&& !addr_cmp(email_to_remove, self_addr)
|
||||
&& !vec_contains_lowercase(&factory.recipients_addr, &email_to_remove)
|
||||
&& !recipients_contain_addr(&recipients, &email_to_remove)
|
||||
{
|
||||
factory.recipients_names.push("".to_string());
|
||||
factory.recipients_addr.push(email_to_remove.to_string());
|
||||
recipients.push(("".to_string(), email_to_remove.to_string()));
|
||||
}
|
||||
}
|
||||
if command != SystemMessage::AutocryptSetupMessage
|
||||
&& command != SystemMessage::SecurejoinMessage
|
||||
&& context.get_config_bool(Config::MdnsEnabled)
|
||||
{
|
||||
factory.req_mdn = true;
|
||||
req_mdn = true;
|
||||
}
|
||||
}
|
||||
let row = context.sql.query_row(
|
||||
let (in_reply_to, references) = context.sql.query_row(
|
||||
"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?",
|
||||
params![msg.id],
|
||||
|row| {
|
||||
@@ -164,39 +136,37 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
render_rfc724_mid_list(&references),
|
||||
))
|
||||
},
|
||||
);
|
||||
|
||||
match row {
|
||||
Ok((in_reply_to, references)) => {
|
||||
factory.in_reply_to = in_reply_to;
|
||||
factory.references = references;
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
context,
|
||||
"mimefactory: failed to load mime_in_reply_to: {:?}", err
|
||||
);
|
||||
}
|
||||
}
|
||||
)?;
|
||||
|
||||
let factory = MimeFactory {
|
||||
from_addr,
|
||||
from_displayname,
|
||||
selfstatus: context
|
||||
.get_config(Config::Selfstatus)
|
||||
.unwrap_or_else(|| context.stock_str(StockMessage::StatusLine).to_string()),
|
||||
recipients,
|
||||
timestamp: msg.timestamp_sort,
|
||||
loaded: Loaded::Message { chat },
|
||||
msg,
|
||||
in_reply_to,
|
||||
references,
|
||||
req_mdn,
|
||||
last_added_location_id: 0,
|
||||
attach_selfavatar: add_selfavatar,
|
||||
context,
|
||||
};
|
||||
Ok(factory)
|
||||
}
|
||||
|
||||
pub fn from_mdn(context: &'a Context, msg: &'b Message) -> Result<Self, Error> {
|
||||
// MDNs not enabled - check this is late, in the job. the
|
||||
// user may have changed its choice while offline ...
|
||||
ensure!(
|
||||
context.get_config_bool(Config::MdnsEnabled),
|
||||
"MDNs meanwhile disabled"
|
||||
);
|
||||
pub fn from_mdn(
|
||||
context: &'a Context,
|
||||
msg: &'b Message,
|
||||
additional_msg_ids: Vec<String>,
|
||||
) -> Result<Self, Error> {
|
||||
ensure!(!msg.chat_id.is_special(), "Invalid chat id");
|
||||
|
||||
let contact = Contact::load_from_db(context, msg.from_id)?;
|
||||
|
||||
// Do not send MDNs trash etc.; chats.blocked is already checked by the caller
|
||||
// in dc_markseen_msgs()
|
||||
ensure!(!contact.is_blocked(), "Contact blocked");
|
||||
ensure!(msg.chat_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat id");
|
||||
|
||||
Ok(MimeFactory {
|
||||
context,
|
||||
from_addr: context
|
||||
@@ -206,13 +176,13 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
selfstatus: context
|
||||
.get_config(Config::Selfstatus)
|
||||
.unwrap_or_else(|| context.stock_str(StockMessage::StatusLine).to_string()),
|
||||
recipients_names: vec![contact.get_authname().to_string()],
|
||||
recipients_addr: vec![contact.get_addr().to_string()],
|
||||
recipients: vec![(
|
||||
contact.get_authname().to_string(),
|
||||
contact.get_addr().to_string(),
|
||||
)],
|
||||
timestamp: dc_create_smeared_timestamp(context),
|
||||
loaded: Loaded::MDN,
|
||||
loaded: Loaded::MDN { additional_msg_ids },
|
||||
msg,
|
||||
chat: None,
|
||||
increation: false,
|
||||
in_reply_to: String::default(),
|
||||
references: String::default(),
|
||||
req_mdn: false,
|
||||
@@ -228,10 +198,10 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
.ok_or_else(|| format_err!("Not configured"))?;
|
||||
|
||||
Ok(self
|
||||
.recipients_addr
|
||||
.recipients
|
||||
.iter()
|
||||
.filter(|addr| *addr != &self_addr)
|
||||
.map(|addr| {
|
||||
.filter(|(_, addr)| addr != &self_addr)
|
||||
.map(|(_, addr)| {
|
||||
(
|
||||
Peerstate::from_addr(self.context, &self.context.sql, addr),
|
||||
addr.as_str(),
|
||||
@@ -240,10 +210,10 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn is_e2ee_guranteed(&self) -> bool {
|
||||
match self.loaded {
|
||||
Loaded::Message => {
|
||||
if self.chat.as_ref().unwrap().typ == Chattype::VerifiedGroup {
|
||||
fn is_e2ee_guaranteed(&self) -> bool {
|
||||
match &self.loaded {
|
||||
Loaded::Message { chat } => {
|
||||
if chat.typ == Chattype::VerifiedGroup {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -264,28 +234,26 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
|
||||
false
|
||||
}
|
||||
Loaded::MDN => false,
|
||||
Loaded::MDN { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn min_verified(&self) -> PeerstateVerifiedStatus {
|
||||
match self.loaded {
|
||||
Loaded::Message => {
|
||||
let chat = self.chat.as_ref().unwrap();
|
||||
match &self.loaded {
|
||||
Loaded::Message { chat } => {
|
||||
if chat.typ == Chattype::VerifiedGroup {
|
||||
PeerstateVerifiedStatus::BidirectVerified
|
||||
} else {
|
||||
PeerstateVerifiedStatus::Unverified
|
||||
}
|
||||
}
|
||||
Loaded::MDN => PeerstateVerifiedStatus::Unverified,
|
||||
Loaded::MDN { .. } => PeerstateVerifiedStatus::Unverified,
|
||||
}
|
||||
}
|
||||
|
||||
fn should_force_plaintext(&self) -> i32 {
|
||||
match self.loaded {
|
||||
Loaded::Message => {
|
||||
let chat = self.chat.as_ref().unwrap();
|
||||
match &self.loaded {
|
||||
Loaded::Message { chat } => {
|
||||
if chat.typ == Chattype::VerifiedGroup {
|
||||
0
|
||||
} else {
|
||||
@@ -295,30 +263,28 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
Loaded::MDN => ForcePlaintext::NoAutocryptHeader as i32,
|
||||
Loaded::MDN { .. } => ForcePlaintext::NoAutocryptHeader as i32,
|
||||
}
|
||||
}
|
||||
|
||||
fn should_do_gossip(&self) -> bool {
|
||||
match self.loaded {
|
||||
Loaded::Message => {
|
||||
let chat = self.chat.as_ref().unwrap();
|
||||
match &self.loaded {
|
||||
Loaded::Message { chat } => {
|
||||
// beside key- and member-changes, force re-gossip every 48 hours
|
||||
let gossiped_timestamp = chat.get_gossiped_timestamp(self.context);
|
||||
if gossiped_timestamp == 0 || (gossiped_timestamp + (2 * 24 * 60 * 60)) > time() {
|
||||
if time() > gossiped_timestamp + (2 * 24 * 60 * 60) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.msg.param.get_cmd() == SystemMessage::MemberAddedToGroup
|
||||
}
|
||||
Loaded::MDN => false,
|
||||
Loaded::MDN { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn grpimage(&self) -> Option<String> {
|
||||
match self.loaded {
|
||||
Loaded::Message => {
|
||||
let chat = self.chat.as_ref().unwrap();
|
||||
match &self.loaded {
|
||||
Loaded::Message { chat } => {
|
||||
let cmd = self.msg.param.get_cmd();
|
||||
|
||||
match cmd {
|
||||
@@ -342,51 +308,37 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
|
||||
None
|
||||
}
|
||||
Loaded::MDN => None,
|
||||
Loaded::MDN { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn subject_str(&self) -> String {
|
||||
match self.loaded {
|
||||
Loaded::Message => {
|
||||
match self.chat {
|
||||
Some(ref chat) => {
|
||||
let raw = message::get_summarytext_by_raw(
|
||||
self.msg.viewtype,
|
||||
self.msg.text.as_ref(),
|
||||
&self.msg.param,
|
||||
32,
|
||||
self.context,
|
||||
);
|
||||
let mut lines = raw.lines();
|
||||
let raw_subject = if let Some(line) = lines.next() {
|
||||
line
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let afwd_email = self.msg.param.exists(Param::Forwarded);
|
||||
let fwd = if afwd_email { "Fwd: " } else { "" };
|
||||
|
||||
if self.msg.param.get_cmd() == SystemMessage::AutocryptSetupMessage {
|
||||
// do not add the "Chat:" prefix for setup messages
|
||||
self.context
|
||||
.stock_str(StockMessage::AcSetupMsgSubject)
|
||||
.into_owned()
|
||||
} else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup
|
||||
{
|
||||
format!("Chat: {}: {}{}", chat.name, fwd, raw_subject)
|
||||
} else {
|
||||
format!("Chat: {}{}", fwd, raw_subject)
|
||||
}
|
||||
}
|
||||
None => String::new(),
|
||||
Loaded::Message { ref chat } => {
|
||||
if self.msg.param.get_cmd() == SystemMessage::AutocryptSetupMessage {
|
||||
self.context
|
||||
.stock_str(StockMessage::AcSetupMsgSubject)
|
||||
.into_owned()
|
||||
} else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
|
||||
let re = if self.in_reply_to.is_empty() {
|
||||
""
|
||||
} else {
|
||||
"Re: "
|
||||
};
|
||||
format!("{}{}", re, chat.name)
|
||||
} else {
|
||||
let raw = message::get_summarytext_by_raw(
|
||||
self.msg.viewtype,
|
||||
self.msg.text.as_ref(),
|
||||
&self.msg.param,
|
||||
32,
|
||||
self.context,
|
||||
);
|
||||
let raw_subject = raw.lines().next().unwrap_or_default();
|
||||
format!("Chat: {}", raw_subject)
|
||||
}
|
||||
}
|
||||
Loaded::MDN => {
|
||||
let e = self.context.stock_str(StockMessage::ReadRcpt);
|
||||
format!("Chat: {}", e)
|
||||
}
|
||||
Loaded::MDN { .. } => self.context.stock_str(StockMessage::ReadRcpt).into_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,10 +357,8 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
self.from_addr.clone(),
|
||||
);
|
||||
|
||||
let mut to = Vec::with_capacity(self.recipients_names.len());
|
||||
let name_iter = self.recipients_names.iter();
|
||||
let addr_iter = self.recipients_addr.iter();
|
||||
for (name, addr) in name_iter.zip(addr_iter) {
|
||||
let mut to = Vec::with_capacity(self.recipients.len());
|
||||
for (name, addr) in self.recipients.iter() {
|
||||
if name.is_empty() {
|
||||
to.push(Address::new_mailbox(addr.clone()));
|
||||
} else {
|
||||
@@ -451,6 +401,13 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
));
|
||||
unprotected_headers.push(Header::new("Chat-Version".to_string(), "1.0".to_string()));
|
||||
|
||||
if let Loaded::MDN { .. } = self.loaded {
|
||||
unprotected_headers.push(Header::new(
|
||||
"Auto-Submitted".to_string(),
|
||||
"auto-replied".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if self.req_mdn {
|
||||
// we use "Chat-Disposition-Notification-To"
|
||||
// because replies to "Disposition-Notification-To" are weird in many cases
|
||||
@@ -465,16 +422,16 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
let grpimage = self.grpimage();
|
||||
let force_plaintext = self.should_force_plaintext();
|
||||
let subject_str = self.subject_str();
|
||||
let e2ee_guranteed = self.is_e2ee_guranteed();
|
||||
let e2ee_guaranteed = self.is_e2ee_guaranteed();
|
||||
let mut encrypt_helper = EncryptHelper::new(self.context)?;
|
||||
|
||||
let subject = encode_words(&subject_str);
|
||||
|
||||
let mut message = match self.loaded {
|
||||
Loaded::Message => {
|
||||
Loaded::Message { .. } => {
|
||||
self.render_message(&mut protected_headers, &mut unprotected_headers, &grpimage)?
|
||||
}
|
||||
Loaded::MDN => self.render_mdn()?,
|
||||
Loaded::MDN { .. } => self.render_mdn()?,
|
||||
};
|
||||
|
||||
if force_plaintext != ForcePlaintext::NoAutocryptHeader as i32 {
|
||||
@@ -487,12 +444,12 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
|
||||
let peerstates = self.peerstates_for_recipients()?;
|
||||
let should_encrypt =
|
||||
encrypt_helper.should_encrypt(self.context, e2ee_guranteed, &peerstates)?;
|
||||
encrypt_helper.should_encrypt(self.context, e2ee_guaranteed, &peerstates)?;
|
||||
let is_encrypted = should_encrypt && force_plaintext == 0;
|
||||
|
||||
let rfc724_mid = match self.loaded {
|
||||
Loaded::Message => self.msg.rfc724_mid.clone(),
|
||||
Loaded::MDN => dc_create_outgoing_rfc724_mid(None, &self.from_addr),
|
||||
Loaded::Message { .. } => self.msg.rfc724_mid.clone(),
|
||||
Loaded::MDN { .. } => dc_create_outgoing_rfc724_mid(None, &self.from_addr),
|
||||
};
|
||||
|
||||
// we could also store the message-id in the protected headers
|
||||
@@ -601,11 +558,9 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
};
|
||||
|
||||
let MimeFactory {
|
||||
recipients_addr,
|
||||
recipients,
|
||||
from_addr,
|
||||
last_added_location_id,
|
||||
msg,
|
||||
loaded,
|
||||
..
|
||||
} = self;
|
||||
|
||||
@@ -615,16 +570,13 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
is_encrypted,
|
||||
is_gossiped,
|
||||
last_added_location_id,
|
||||
foreign_id: match loaded {
|
||||
Loaded::Message => Some(msg.id),
|
||||
Loaded::MDN => None,
|
||||
},
|
||||
recipients: recipients_addr,
|
||||
recipients: recipients.into_iter().map(|(_, addr)| addr).collect(),
|
||||
from: from_addr,
|
||||
rfc724_mid,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn render_message(
|
||||
&mut self,
|
||||
protected_headers: &mut Vec<Header>,
|
||||
@@ -632,7 +584,10 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
grpimage: &Option<String>,
|
||||
) -> Result<PartBuilder, Error> {
|
||||
let context = self.context;
|
||||
let chat = self.chat.as_ref().unwrap();
|
||||
let chat = match &self.loaded {
|
||||
Loaded::Message { chat } => chat,
|
||||
Loaded::MDN { .. } => bail!("Attempt to render MDN as a message"),
|
||||
};
|
||||
let command = self.msg.param.get_cmd();
|
||||
let mut placeholdertext = None;
|
||||
let mut meta_part = None;
|
||||
@@ -666,7 +621,9 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
email_to_add.into(),
|
||||
));
|
||||
}
|
||||
if 0 != self.msg.param.get_int(Param::Arg2).unwrap_or_default() & 0x1 {
|
||||
if 0 != self.msg.param.get_int(Param::Arg2).unwrap_or_default()
|
||||
& DC_FROM_HANDSHAKE
|
||||
{
|
||||
info!(
|
||||
context,
|
||||
"sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>",
|
||||
@@ -950,6 +907,13 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
// are forwarded for any reasons (eg. gmail always forwards to IMAP), we have no chance to decrypt them;
|
||||
// this issue is fixed with 0.9.4
|
||||
|
||||
let additional_msg_ids = match &self.loaded {
|
||||
Loaded::Message { .. } => bail!("Attempt to render a message as MDN"),
|
||||
Loaded::MDN {
|
||||
additional_msg_ids, ..
|
||||
} => additional_msg_ids,
|
||||
};
|
||||
|
||||
let mut message = PartBuilder::new().header((
|
||||
"Content-Type".to_string(),
|
||||
"multipart/report; report-type=disposition-notification".to_string(),
|
||||
@@ -991,10 +955,22 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
version, self.from_addr, self.from_addr, self.msg.rfc724_mid
|
||||
);
|
||||
|
||||
let extension_fields = if additional_msg_ids.is_empty() {
|
||||
"".to_string()
|
||||
} else {
|
||||
"Additional-Message-IDs: ".to_string()
|
||||
+ &additional_msg_ids
|
||||
.iter()
|
||||
.map(|mid| render_rfc724_mid(&mid))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
+ "\r\n"
|
||||
};
|
||||
|
||||
message = message.child(
|
||||
PartBuilder::new()
|
||||
.content_type(&"message/disposition-notification".parse().unwrap())
|
||||
.body(message_text2)
|
||||
.body(message_text2 + &extension_fields)
|
||||
.build(),
|
||||
);
|
||||
|
||||
@@ -1112,14 +1088,11 @@ fn build_selfavatar_file(context: &Context, path: String) -> Result<(PartBuilder
|
||||
Ok((part, filename_to_send))
|
||||
}
|
||||
|
||||
pub(crate) fn vec_contains_lowercase(vec: &[String], part: &str) -> bool {
|
||||
let partlc = part.to_lowercase();
|
||||
for cur in vec.iter() {
|
||||
if cur.to_lowercase() == partlc {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
fn recipients_contain_addr(recipients: &[(String, String)], addr: &str) -> bool {
|
||||
let addr_lc = addr.to_lowercase();
|
||||
recipients
|
||||
.iter()
|
||||
.any(|(_, cur)| cur.to_lowercase() == addr_lc)
|
||||
}
|
||||
|
||||
fn is_file_size_okay(context: &Context, msg: &Message) -> bool {
|
||||
@@ -1160,14 +1133,8 @@ fn encode_words(word: &str) -> String {
|
||||
}
|
||||
|
||||
pub fn needs_encoding(to_check: impl AsRef<str>) -> bool {
|
||||
let to_check = to_check.as_ref();
|
||||
|
||||
if to_check.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
to_check.chars().any(|c| {
|
||||
!c.is_ascii_alphanumeric() && c != '-' && c != '_' && c != '.' && c != '~' && c != '%'
|
||||
!to_check.as_ref().chars().all(|c| {
|
||||
c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.' || c == '~' || c == '%'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1226,4 +1193,12 @@ mod tests {
|
||||
FBQUFBQUFBQQ==";
|
||||
assert_eq!(wrapped_base64_encode(input), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_needs_encoding() {
|
||||
assert!(!needs_encoding(""));
|
||||
assert!(!needs_encoding("foobar"));
|
||||
assert!(needs_encoding(" "));
|
||||
assert!(needs_encoding("foo bar"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ pub struct MimeMessage<'a> {
|
||||
pub message_kml: Option<location::Kml>,
|
||||
pub user_avatar: AvatarAction,
|
||||
pub group_avatar: AvatarAction,
|
||||
reports: Vec<Report>,
|
||||
pub(crate) reports: Vec<Report>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -175,12 +175,19 @@ impl<'a> MimeMessage<'a> {
|
||||
Ok(parser)
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn parse_headers(&mut self) -> Result<()> {
|
||||
if self.get(HeaderDef::AutocryptSetupMessage).is_some() {
|
||||
self.parts.drain_filter(|part| {
|
||||
part.mimetype.is_some()
|
||||
&& part.mimetype.as_ref().unwrap().as_ref() != MIME_AC_SETUP_FILE
|
||||
});
|
||||
self.parts = self
|
||||
.parts
|
||||
.iter()
|
||||
.filter(|part| {
|
||||
part.mimetype.is_none()
|
||||
|| part.mimetype.as_ref().unwrap().as_ref() == MIME_AC_SETUP_FILE
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if self.parts.len() == 1 {
|
||||
self.is_system_message = SystemMessage::AutocryptSetupMessage;
|
||||
} else {
|
||||
@@ -367,14 +374,9 @@ impl<'a> MimeMessage<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn get_subject(&self) -> Option<String> {
|
||||
if let Some(s) = self.get(HeaderDef::Subject) {
|
||||
if s.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(s.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
self.get(HeaderDef::Subject)
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.to_string())
|
||||
}
|
||||
|
||||
pub fn get(&self, headerdef: HeaderDef) -> Option<&String> {
|
||||
@@ -713,11 +715,8 @@ impl<'a> MimeMessage<'a> {
|
||||
}
|
||||
|
||||
pub fn get_rfc724_mid(&self) -> Option<String> {
|
||||
if let Some(msgid) = self.get(HeaderDef::MessageId) {
|
||||
parse_message_id(msgid)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
self.get(HeaderDef::MessageId)
|
||||
.and_then(|msgid| parse_message_id(msgid))
|
||||
}
|
||||
|
||||
fn merge_headers(&mut self, fields: &[mailparse::MailHeader<'_>]) {
|
||||
@@ -750,8 +749,17 @@ impl<'a> MimeMessage<'a> {
|
||||
.flatten()
|
||||
.and_then(|v| parse_message_id(&v))
|
||||
{
|
||||
let additional_message_ids = report_fields
|
||||
.get_first_value(&HeaderDef::AdditionalMessageIds.get_headername())
|
||||
.ok()
|
||||
.flatten()
|
||||
.map_or_else(Vec::new, |v| {
|
||||
v.split(' ').filter_map(parse_message_id).collect()
|
||||
});
|
||||
|
||||
return Ok(Some(Report {
|
||||
original_message_id,
|
||||
additional_message_ids,
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -778,14 +786,18 @@ impl<'a> MimeMessage<'a> {
|
||||
|
||||
let mut mdn_recognized = false;
|
||||
for report in &self.reports {
|
||||
if let Some((chat_id, msg_id)) = message::mdn_from_ext(
|
||||
self.context,
|
||||
from_id,
|
||||
&report.original_message_id,
|
||||
sent_timestamp,
|
||||
) {
|
||||
self.context.call_cb(Event::MsgRead { chat_id, msg_id });
|
||||
mdn_recognized = true;
|
||||
for original_message_id in
|
||||
std::iter::once(&report.original_message_id).chain(&report.additional_message_ids)
|
||||
{
|
||||
if let Some((chat_id, msg_id)) = message::mdn_from_ext(
|
||||
self.context,
|
||||
from_id,
|
||||
original_message_id,
|
||||
sent_timestamp,
|
||||
) {
|
||||
self.context.call_cb(Event::MsgRead { chat_id, msg_id });
|
||||
mdn_recognized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -860,8 +872,11 @@ fn update_gossip_peerstates(
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Report {
|
||||
pub(crate) struct Report {
|
||||
/// Original-Message-ID header
|
||||
original_message_id: String,
|
||||
/// Additional-Message-IDs
|
||||
additional_message_ids: Vec<String>,
|
||||
}
|
||||
|
||||
fn parse_message_id(field: &str) -> Option<String> {
|
||||
@@ -1036,7 +1051,6 @@ fn compare_addrs(a: &mailparse::MailAddr, b: &mailparse::MailAddr) -> bool {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::*;
|
||||
use proptest::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_dc_mimeparser_crash() {
|
||||
@@ -1048,17 +1062,6 @@ mod tests {
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_dc_mailmime_parse_crash_fuzzy(data in "[!-~\t ]{2000,}") {
|
||||
let context = dummy_context();
|
||||
|
||||
// just don't crash
|
||||
let _ = MimeMessage::from_bytes(&context.ctx, data.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_rfc724_mid_exists() {
|
||||
let context = dummy_context();
|
||||
@@ -1270,4 +1273,176 @@ Content-Disposition: attachment; filename=\"message.kml\"\n\
|
||||
// and only goes into message_kml.
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_mdn() {
|
||||
let context = dummy_context();
|
||||
let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\
|
||||
Date: Mon, 10 Jan 2020 00:00:00 +0000\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: <bar@example.org>\n\
|
||||
To: Alice <alice@example.org>\n\
|
||||
From: Bob <bob@example.org>\n\
|
||||
Content-Type: multipart/report; report-type=disposition-notification;\n\t\
|
||||
boundary=\"kJBbU58X1xeWNHgBtTbMk80M5qnV4N\"\n\
|
||||
\n\
|
||||
\n\
|
||||
--kJBbU58X1xeWNHgBtTbMk80M5qnV4N\n\
|
||||
Content-Type: text/plain; charset=utf-8\n\
|
||||
\n\
|
||||
The \"Encrypted message\" message you sent was displayed on the screen of the recipient.\n\
|
||||
\n\
|
||||
This is no guarantee the content was read.\n\
|
||||
\n\
|
||||
\n\
|
||||
--kJBbU58X1xeWNHgBtTbMk80M5qnV4N\n\
|
||||
Content-Type: message/disposition-notification\n\
|
||||
\n\
|
||||
Reporting-UA: Delta Chat 1.0.0-beta.22\n\
|
||||
Original-Recipient: rfc822;bob@example.org\n\
|
||||
Final-Recipient: rfc822;bob@example.org\n\
|
||||
Original-Message-ID: <foo@example.org>\n\
|
||||
Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
\n\
|
||||
\n\
|
||||
--kJBbU58X1xeWNHgBtTbMk80M5qnV4N--\n\
|
||||
";
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap();
|
||||
assert_eq!(
|
||||
message.get_subject(),
|
||||
Some("Chat: Message opened".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(message.parts.len(), 0);
|
||||
assert_eq!(message.reports.len(), 1);
|
||||
}
|
||||
|
||||
/// Test parsing multiple MDNs combined in a single message.
|
||||
///
|
||||
/// RFC 6522 specifically allows MDNs to be nested inside
|
||||
/// multipart MIME messages.
|
||||
#[test]
|
||||
fn test_parse_multiple_mdns() {
|
||||
let context = dummy_context();
|
||||
let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\
|
||||
Date: Mon, 10 Jan 2020 00:00:00 +0000\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: <foo@example.org>\n\
|
||||
To: Alice <alice@example.org>\n\
|
||||
From: Bob <bob@example.org>\n\
|
||||
Content-Type: multipart/parallel; boundary=outer\n\
|
||||
\n\
|
||||
This is a multipart MDN.\n\
|
||||
\n\
|
||||
--outer\n\
|
||||
Content-Type: multipart/report; report-type=disposition-notification;\n\t\
|
||||
boundary=kJBbU58X1xeWNHgBtTbMk80M5qnV4N\n\
|
||||
\n\
|
||||
\n\
|
||||
--kJBbU58X1xeWNHgBtTbMk80M5qnV4N\n\
|
||||
Content-Type: text/plain; charset=utf-8\n\
|
||||
\n\
|
||||
The \"Encrypted message\" message you sent was displayed on the screen of the recipient.\n\
|
||||
\n\
|
||||
This is no guarantee the content was read.\n\
|
||||
\n\
|
||||
\n\
|
||||
--kJBbU58X1xeWNHgBtTbMk80M5qnV4N\n\
|
||||
Content-Type: message/disposition-notification\n\
|
||||
\n\
|
||||
Reporting-UA: Delta Chat 1.0.0-beta.22\n\
|
||||
Original-Recipient: rfc822;bob@example.org\n\
|
||||
Final-Recipient: rfc822;bob@example.org\n\
|
||||
Original-Message-ID: <bar@example.org>\n\
|
||||
Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
\n\
|
||||
\n\
|
||||
--kJBbU58X1xeWNHgBtTbMk80M5qnV4N--\n\
|
||||
--outer\n\
|
||||
Content-Type: multipart/report; report-type=disposition-notification;\n\t\
|
||||
boundary=zuOJlsTfZAukyawEPVdIgqWjaM9w2W\n\
|
||||
\n\
|
||||
\n\
|
||||
--zuOJlsTfZAukyawEPVdIgqWjaM9w2W\n\
|
||||
Content-Type: text/plain; charset=utf-8\n\
|
||||
\n\
|
||||
The \"Encrypted message\" message you sent was displayed on the screen of the recipient.\n\
|
||||
\n\
|
||||
This is no guarantee the content was read.\n\
|
||||
\n\
|
||||
\n\
|
||||
--zuOJlsTfZAukyawEPVdIgqWjaM9w2W\n\
|
||||
Content-Type: message/disposition-notification\n\
|
||||
\n\
|
||||
Reporting-UA: Delta Chat 1.0.0-beta.22\n\
|
||||
Original-Recipient: rfc822;bob@example.org\n\
|
||||
Final-Recipient: rfc822;bob@example.org\n\
|
||||
Original-Message-ID: <baz@example.org>\n\
|
||||
Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
\n\
|
||||
\n\
|
||||
--zuOJlsTfZAukyawEPVdIgqWjaM9w2W--\n\
|
||||
--outer--\n\
|
||||
";
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap();
|
||||
assert_eq!(
|
||||
message.get_subject(),
|
||||
Some("Chat: Message opened".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(message.parts.len(), 0);
|
||||
assert_eq!(message.reports.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_mdn_with_additional_message_ids() {
|
||||
let context = dummy_context();
|
||||
let raw = b"Subject: =?utf-8?q?Chat=3A_Message_opened?=\n\
|
||||
Date: Mon, 10 Jan 2020 00:00:00 +0000\n\
|
||||
Chat-Version: 1.0\n\
|
||||
Message-ID: <bar@example.org>\n\
|
||||
To: Alice <alice@example.org>\n\
|
||||
From: Bob <bob@example.org>\n\
|
||||
Content-Type: multipart/report; report-type=disposition-notification;\n\t\
|
||||
boundary=\"kJBbU58X1xeWNHgBtTbMk80M5qnV4N\"\n\
|
||||
\n\
|
||||
\n\
|
||||
--kJBbU58X1xeWNHgBtTbMk80M5qnV4N\n\
|
||||
Content-Type: text/plain; charset=utf-8\n\
|
||||
\n\
|
||||
The \"Encrypted message\" message you sent was displayed on the screen of the recipient.\n\
|
||||
\n\
|
||||
This is no guarantee the content was read.\n\
|
||||
\n\
|
||||
\n\
|
||||
--kJBbU58X1xeWNHgBtTbMk80M5qnV4N\n\
|
||||
Content-Type: message/disposition-notification\n\
|
||||
\n\
|
||||
Reporting-UA: Delta Chat 1.0.0-beta.22\n\
|
||||
Original-Recipient: rfc822;bob@example.org\n\
|
||||
Final-Recipient: rfc822;bob@example.org\n\
|
||||
Original-Message-ID: <foo@example.org>\n\
|
||||
Disposition: manual-action/MDN-sent-automatically; displayed\n\
|
||||
Additional-Message-IDs: <foo@example.com> <foo@example.net>\n\
|
||||
\n\
|
||||
\n\
|
||||
--kJBbU58X1xeWNHgBtTbMk80M5qnV4N--\n\
|
||||
";
|
||||
|
||||
let message = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap();
|
||||
assert_eq!(
|
||||
message.get_subject(),
|
||||
Some("Chat: Message opened".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(message.parts.len(), 0);
|
||||
assert_eq!(message.reports.len(), 1);
|
||||
assert_eq!(message.reports[0].original_message_id, "foo@example.org");
|
||||
assert_eq!(
|
||||
&message.reports[0].additional_message_ids,
|
||||
&["foo@example.com", "foo@example.net"]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,13 +312,12 @@ impl Oauth2 {
|
||||
}
|
||||
if let Ok(response) = parsed {
|
||||
// serde_json::Value.as_str() removes the quotes of json-strings
|
||||
let addr = response.get("email");
|
||||
if addr.is_none() {
|
||||
if let Some(addr) = response.get("email") {
|
||||
Some(addr.to_string())
|
||||
} else {
|
||||
warn!(context, "E-mail missing in userinfo.");
|
||||
return None;
|
||||
None
|
||||
}
|
||||
let addr = addr.unwrap().as_str();
|
||||
addr.map(|addr| addr.to_string())
|
||||
} else {
|
||||
warn!(context, "Failed to parse userinfo.");
|
||||
None
|
||||
|
||||
24
src/param.rs
24
src/param.rs
@@ -8,6 +8,7 @@ use num_traits::FromPrimitive;
|
||||
use crate::blob::{BlobError, BlobObject};
|
||||
use crate::context::Context;
|
||||
use crate::error;
|
||||
use crate::message::MsgId;
|
||||
use crate::mimeparser::SystemMessage;
|
||||
|
||||
/// Available param keys.
|
||||
@@ -96,26 +97,29 @@ pub enum Param {
|
||||
/// For Jobs: space-separated list of message recipients
|
||||
Recipients = b'R',
|
||||
|
||||
// For Groups
|
||||
/// For Groups
|
||||
Unpromoted = b'U',
|
||||
|
||||
// For Groups and Contacts
|
||||
/// For Groups and Contacts
|
||||
ProfileImage = b'i',
|
||||
|
||||
// For Chats
|
||||
/// For Chats
|
||||
Selftalk = b'K',
|
||||
|
||||
// For Chats
|
||||
/// For Chats
|
||||
Devicetalk = b'D',
|
||||
|
||||
// For QR
|
||||
/// For QR
|
||||
Auth = b's',
|
||||
|
||||
// For QR
|
||||
/// For QR
|
||||
GroupId = b'x',
|
||||
|
||||
// For QR
|
||||
/// For QR
|
||||
GroupName = b'g',
|
||||
|
||||
/// For MDN-sending job
|
||||
MsgId = b'I',
|
||||
}
|
||||
|
||||
/// Possible values for `Param::ForcePlaintext`.
|
||||
@@ -312,6 +316,12 @@ impl Params {
|
||||
Ok(Some(path))
|
||||
}
|
||||
|
||||
pub fn get_msg_id(&self) -> Option<MsgId> {
|
||||
self.get(Param::MsgId)
|
||||
.and_then(|x| x.parse::<u32>().ok())
|
||||
.map(MsgId::new)
|
||||
}
|
||||
|
||||
/// Set the given paramter to the passed in `i32`.
|
||||
pub fn set_int(&mut self, key: Param, value: i32) -> &mut Self {
|
||||
self.set(key, format!("{}", value));
|
||||
|
||||
@@ -5,7 +5,6 @@ use std::fmt;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::aheader::*;
|
||||
use crate::chat::*;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::key::*;
|
||||
@@ -418,7 +417,6 @@ impl<'a> Peerstate<'a> {
|
||||
&self.addr,
|
||||
],
|
||||
)?;
|
||||
reset_gossiped_timestamp(self.context, 0)?;
|
||||
} else if self.to_save == Some(ToSave::Timestamps) {
|
||||
sql::execute(
|
||||
self.context,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use percent_encoding::{utf8_percent_encode, AsciiSet, NON_ALPHANUMERIC};
|
||||
|
||||
use crate::aheader::EncryptPreference;
|
||||
use crate::chat::{self, Chat};
|
||||
use crate::chat::{self, Chat, ChatId};
|
||||
use crate::config::*;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
@@ -65,15 +65,18 @@ macro_rules! get_qr_attr {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: u32) -> Option<String> {
|
||||
/* =========================================================
|
||||
pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: ChatId) -> Option<String> {
|
||||
/*=======================================================
|
||||
==== Alice - the inviter side ====
|
||||
==== Step 1 in "Setup verified contact" protocol ====
|
||||
========================================================= */
|
||||
=======================================================*/
|
||||
|
||||
let fingerprint: String;
|
||||
|
||||
ensure_secret_key_exists(context).ok();
|
||||
|
||||
// invitenumber will be used to allow starting the handshake,
|
||||
// auth will be used to verify the fingerprint
|
||||
let invitenumber = token::lookup_or_new(context, token::Namespace::InviteNumber, group_chat_id);
|
||||
let auth = token::lookup_or_new(context, token::Namespace::Auth, group_chat_id);
|
||||
let self_addr = match context.get_config(Config::ConfiguredAddr) {
|
||||
@@ -98,7 +101,8 @@ pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: u32) -> Option<Str
|
||||
let self_name_urlencoded =
|
||||
utf8_percent_encode(&self_name, NON_ALPHANUMERIC_WITHOUT_DOT).to_string();
|
||||
|
||||
let qr = if 0 != group_chat_id {
|
||||
let qr = if !group_chat_id.is_unset() {
|
||||
// parameters used: a=g=x=i=s=
|
||||
if let Ok(chat) = Chat::load_from_db(context, group_chat_id) {
|
||||
let group_name = chat.get_name();
|
||||
let group_name_urlencoded =
|
||||
@@ -118,6 +122,7 @@ pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: u32) -> Option<Str
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
// parameters used: a=n=i=s=
|
||||
Some(format!(
|
||||
"OPENPGP4FPR:{}#a={}&n={}&i={}&s={}",
|
||||
fingerprint, self_addr_urlencoded, self_name_urlencoded, &invitenumber, &auth,
|
||||
@@ -138,36 +143,39 @@ fn get_self_fingerprint(context: &Context) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn dc_join_securejoin(context: &Context, qr: &str) -> u32 {
|
||||
pub fn dc_join_securejoin(context: &Context, qr: &str) -> ChatId {
|
||||
let cleanup =
|
||||
|context: &Context, contact_chat_id: u32, ongoing_allocated: bool, join_vg: bool| {
|
||||
|context: &Context, contact_chat_id: ChatId, ongoing_allocated: bool, join_vg: bool| {
|
||||
let mut bob = context.bob.write().unwrap();
|
||||
bob.expects = 0;
|
||||
let ret_chat_id = if bob.status == DC_BOB_SUCCESS {
|
||||
let ret_chat_id: ChatId = if bob.status == DC_BOB_SUCCESS {
|
||||
if join_vg {
|
||||
chat::get_chat_id_by_grpid(
|
||||
context,
|
||||
bob.qr_scan.as_ref().unwrap().text2.as_ref().unwrap(),
|
||||
)
|
||||
.unwrap_or((ChatId::new(0), false, Blocked::Not))
|
||||
.0
|
||||
} else {
|
||||
contact_chat_id
|
||||
}
|
||||
} else {
|
||||
0
|
||||
ChatId::new(0)
|
||||
};
|
||||
bob.qr_scan = None;
|
||||
|
||||
if ongoing_allocated {
|
||||
context.free_ongoing();
|
||||
}
|
||||
ret_chat_id as u32
|
||||
ret_chat_id
|
||||
};
|
||||
/* ==========================================================
|
||||
|
||||
/*========================================================
|
||||
==== Bob - the joiner's side =====
|
||||
==== Step 2 in "Setup verified contact" protocol =====
|
||||
========================================================== */
|
||||
let mut contact_chat_id: u32 = 0;
|
||||
========================================================*/
|
||||
|
||||
let mut contact_chat_id = ChatId::new(0);
|
||||
let mut join_vg: bool = false;
|
||||
|
||||
info!(context, "Requesting secure-join ...",);
|
||||
@@ -181,11 +189,13 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> u32 {
|
||||
error!(context, "Unknown QR code.",);
|
||||
return cleanup(&context, contact_chat_id, true, join_vg);
|
||||
}
|
||||
contact_chat_id = chat::create_by_contact_id(context, qr_scan.id).unwrap_or_default();
|
||||
if contact_chat_id == 0 {
|
||||
error!(context, "Unknown contact.",);
|
||||
return cleanup(&context, contact_chat_id, true, join_vg);
|
||||
}
|
||||
contact_chat_id = match chat::create_by_contact_id(context, qr_scan.id) {
|
||||
Ok(chat_id) => chat_id,
|
||||
Err(_) => {
|
||||
error!(context, "Unknown contact.");
|
||||
return cleanup(&context, contact_chat_id, true, join_vg);
|
||||
}
|
||||
};
|
||||
if context.shall_stop_ongoing() {
|
||||
return cleanup(&context, contact_chat_id, true, join_vg);
|
||||
}
|
||||
@@ -209,10 +219,14 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> u32 {
|
||||
.unwrap(),
|
||||
contact_chat_id,
|
||||
) {
|
||||
// the scanned fingerprint matches Alice's key,
|
||||
// we can proceed to step 4b) directly and save two mails
|
||||
info!(context, "Taking protocol shortcut.");
|
||||
context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM;
|
||||
joiner_progress!(context, chat_id_2_contact_id(context, contact_chat_id), 400);
|
||||
let own_fingerprint = get_self_fingerprint(context).unwrap_or_default();
|
||||
|
||||
// Bob -> Alice
|
||||
send_handshake_msg(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -231,6 +245,8 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> u32 {
|
||||
);
|
||||
} else {
|
||||
context.bob.write().unwrap().expects = DC_VC_AUTH_REQUIRED;
|
||||
|
||||
// Bob -> Alice
|
||||
send_handshake_msg(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -241,7 +257,6 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> u32 {
|
||||
);
|
||||
}
|
||||
|
||||
// Bob -> Alice
|
||||
while !context.shall_stop_ongoing() {
|
||||
// Don't sleep too long, the user is waiting.
|
||||
std::thread::sleep(std::time::Duration::from_millis(200));
|
||||
@@ -251,7 +266,7 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> u32 {
|
||||
|
||||
fn send_handshake_msg(
|
||||
context: &Context,
|
||||
contact_chat_id: u32,
|
||||
contact_chat_id: ChatId,
|
||||
step: &str,
|
||||
param2: impl AsRef<str>,
|
||||
fingerprint: Option<String>,
|
||||
@@ -288,7 +303,7 @@ fn send_handshake_msg(
|
||||
chat::send_msg(context, contact_chat_id, &mut msg).unwrap_or_default();
|
||||
}
|
||||
|
||||
fn chat_id_2_contact_id(context: &Context, contact_chat_id: u32) -> u32 {
|
||||
fn chat_id_2_contact_id(context: &Context, contact_chat_id: ChatId) -> u32 {
|
||||
let contacts = chat::get_chat_contacts(context, contact_chat_id);
|
||||
if contacts.len() == 1 {
|
||||
contacts[0]
|
||||
@@ -300,7 +315,7 @@ fn chat_id_2_contact_id(context: &Context, contact_chat_id: u32) -> u32 {
|
||||
fn fingerprint_equals_sender(
|
||||
context: &Context,
|
||||
fingerprint: impl AsRef<str>,
|
||||
contact_chat_id: u32,
|
||||
contact_chat_id: ChatId,
|
||||
) -> bool {
|
||||
let contacts = chat::get_chat_contacts(context, contact_chat_id);
|
||||
|
||||
@@ -319,80 +334,115 @@ fn fingerprint_equals_sender(
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) struct HandshakeMessageStatus {
|
||||
pub(crate) hide_this_msg: bool,
|
||||
pub(crate) delete_this_msg: bool,
|
||||
pub(crate) stop_ongoing_process: bool,
|
||||
pub(crate) bob_securejoin_success: Option<bool>,
|
||||
#[derive(Fail, Debug)]
|
||||
pub(crate) enum HandshakeError {
|
||||
#[fail(display = "Can not be called with special contact ID")]
|
||||
SpecialContactId,
|
||||
#[fail(display = "Not a Secure-Join message")]
|
||||
NotSecureJoinMsg,
|
||||
#[fail(
|
||||
display = "Failed to look up or create chat for contact #{}",
|
||||
contact_id
|
||||
)]
|
||||
NoChat {
|
||||
contact_id: u32,
|
||||
#[cause]
|
||||
cause: Error,
|
||||
},
|
||||
#[fail(display = "Chat for group {} not found", group)]
|
||||
ChatNotFound { group: String },
|
||||
#[fail(display = "No configured self address found")]
|
||||
NoSelfAddr,
|
||||
}
|
||||
|
||||
impl Default for HandshakeMessageStatus {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hide_this_msg: true,
|
||||
delete_this_msg: false,
|
||||
stop_ongoing_process: false,
|
||||
bob_securejoin_success: None,
|
||||
}
|
||||
}
|
||||
/// What to do with a Secure-Join handshake message after it was handled.
|
||||
pub(crate) enum HandshakeMessage {
|
||||
/// The message has been fully handled and should be removed/delete.
|
||||
Done,
|
||||
/// The message should be ignored/hidden, but not removed/deleted.
|
||||
Ignore,
|
||||
/// The message should be further processed by incoming message handling.
|
||||
Propagate,
|
||||
}
|
||||
|
||||
/// Handle incoming secure-join handshake.
|
||||
///
|
||||
/// This function will update the securejoin state in [Context::bob]
|
||||
/// and also terminate the ongoing process using
|
||||
/// [Context::stop_ongoing] as required by the protocol.
|
||||
///
|
||||
/// A message which results in [Err] will be hidden from the user but
|
||||
/// not deleted, it may be a valid message for something else we are
|
||||
/// not aware off. E.g. it could be part of a handshake performed by
|
||||
/// another DC app on the same account.
|
||||
///
|
||||
/// When handle_securejoin_handshake() is called,
|
||||
/// the message is not yet filed in the database;
|
||||
/// this is done by receive_imf() later on as needed.
|
||||
pub(crate) fn handle_securejoin_handshake(
|
||||
context: &Context,
|
||||
mimeparser: &MimeMessage,
|
||||
mime_message: &MimeMessage,
|
||||
contact_id: u32,
|
||||
) -> Result<HandshakeMessageStatus, Error> {
|
||||
) -> Result<HandshakeMessage, HandshakeError> {
|
||||
let own_fingerprint: String;
|
||||
|
||||
ensure!(
|
||||
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
|
||||
"handle_securejoin_handshake(): called with special contact id"
|
||||
);
|
||||
let step = mimeparser
|
||||
if contact_id <= DC_CONTACT_ID_LAST_SPECIAL {
|
||||
return Err(HandshakeError::SpecialContactId);
|
||||
}
|
||||
let step = mime_message
|
||||
.get(HeaderDef::SecureJoin)
|
||||
.ok_or_else(|| format_err!("This message is not a Secure-Join message"))?;
|
||||
.ok_or(HandshakeError::NotSecureJoinMsg)?;
|
||||
|
||||
info!(
|
||||
context,
|
||||
">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received", step,
|
||||
);
|
||||
|
||||
let (contact_chat_id, contact_chat_id_blocked) =
|
||||
chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not).unwrap_or_default();
|
||||
|
||||
if contact_chat_id_blocked != Blocked::Not {
|
||||
chat::unblock(context, contact_chat_id);
|
||||
}
|
||||
let contact_chat_id =
|
||||
match chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not) {
|
||||
Ok((chat_id, blocked)) => {
|
||||
if blocked != Blocked::Not {
|
||||
chat::unblock(context, chat_id);
|
||||
}
|
||||
chat_id
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(HandshakeError::NoChat {
|
||||
contact_id,
|
||||
cause: err,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let join_vg = step.starts_with("vg-");
|
||||
let mut ret = HandshakeMessageStatus::default();
|
||||
|
||||
match step.as_str() {
|
||||
"vg-request" | "vc-request" => {
|
||||
/* =========================================================
|
||||
/*=======================================================
|
||||
==== Alice - the inviter side ====
|
||||
==== Step 3 in "Setup verified contact" protocol ====
|
||||
========================================================= */
|
||||
// this message may be unencrypted (Bob, the joinder and the sender, might not have Alice's key yet)
|
||||
=======================================================*/
|
||||
|
||||
// this message may be unencrypted (Bob, the joiner and the sender, might not have Alice's key yet)
|
||||
// it just ensures, we have Bobs key now. If we do _not_ have the key because eg. MitM has removed it,
|
||||
// send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here.
|
||||
// verify that the `Secure-Join-Invitenumber:`-header matches invitenumber written to the QR code
|
||||
let invitenumber = match mimeparser.get(HeaderDef::SecureJoinInvitenumber) {
|
||||
let invitenumber = match mime_message.get(HeaderDef::SecureJoinInvitenumber) {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
warn!(context, "Secure-join denied (invitenumber missing).",);
|
||||
return Ok(ret);
|
||||
warn!(context, "Secure-join denied (invitenumber missing)");
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
};
|
||||
if !token::exists(context, token::Namespace::InviteNumber, &invitenumber) {
|
||||
warn!(context, "Secure-join denied (bad invitenumber).",);
|
||||
return Ok(ret);
|
||||
warn!(context, "Secure-join denied (bad invitenumber).");
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
info!(context, "Secure-join requested.",);
|
||||
|
||||
inviter_progress!(context, contact_id, 300);
|
||||
|
||||
// Alice -> Bob
|
||||
send_handshake_msg(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -401,8 +451,15 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
None,
|
||||
"",
|
||||
);
|
||||
Ok(HandshakeMessage::Done)
|
||||
}
|
||||
"vg-auth-required" | "vc-auth-required" => {
|
||||
/*========================================================
|
||||
==== Bob - the joiner's side =====
|
||||
==== Step 4 in "Setup verified contact" protocol =====
|
||||
========================================================*/
|
||||
|
||||
// verify that Alice's Autocrypt key and fingerprint matches the QR-code
|
||||
let cond = {
|
||||
let bob = context.bob.read().unwrap();
|
||||
let scan = bob.qr_scan.as_ref();
|
||||
@@ -412,26 +469,26 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
};
|
||||
|
||||
if cond {
|
||||
warn!(context, "auth-required message out of sync.",);
|
||||
warn!(context, "auth-required message out of sync.");
|
||||
// no error, just aborted somehow or a mail from another handshake
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string();
|
||||
let auth = get_qr_attr!(context, auth).to_string();
|
||||
|
||||
if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) {
|
||||
if !encrypted_and_signed(mime_message, &scanned_fingerprint_of_alice) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
if mimeparser.was_encrypted() {
|
||||
if mime_message.was_encrypted() {
|
||||
"No valid signature."
|
||||
} else {
|
||||
"Not encrypted."
|
||||
},
|
||||
);
|
||||
ret.stop_ongoing_process = true;
|
||||
ret.bob_securejoin_success = Some(false);
|
||||
return Ok(ret);
|
||||
context.bob.write().unwrap().status = 0; // secure-join failed
|
||||
context.stop_ongoing();
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
if !fingerprint_equals_sender(context, &scanned_fingerprint_of_alice, contact_chat_id) {
|
||||
could_not_establish_secure_connection(
|
||||
@@ -439,15 +496,16 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
contact_chat_id,
|
||||
"Fingerprint mismatch on joiner-side.",
|
||||
);
|
||||
ret.stop_ongoing_process = true;
|
||||
ret.bob_securejoin_success = Some(false);
|
||||
return Ok(ret);
|
||||
context.bob.write().unwrap().status = 0; // secure-join failed
|
||||
context.stop_ongoing();
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
info!(context, "Fingerprint verified.",);
|
||||
own_fingerprint = get_self_fingerprint(context).unwrap();
|
||||
joiner_progress!(context, contact_id, 400);
|
||||
context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM;
|
||||
|
||||
// Bob -> Alice
|
||||
send_handshake_msg(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -460,15 +518,17 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
"".to_string()
|
||||
},
|
||||
);
|
||||
Ok(HandshakeMessage::Done)
|
||||
}
|
||||
"vg-request-with-auth" | "vc-request-with-auth" => {
|
||||
/* ============================================================
|
||||
/*==========================================================
|
||||
==== Alice - the inviter side ====
|
||||
==== Steps 5+6 in "Setup verified contact" protocol ====
|
||||
==== Step 6 in "Out-of-band verified groups" protocol ====
|
||||
============================================================ */
|
||||
==========================================================*/
|
||||
|
||||
// verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob
|
||||
let fingerprint = match mimeparser.get(HeaderDef::SecureJoinFingerprint) {
|
||||
let fingerprint = match mime_message.get(HeaderDef::SecureJoinFingerprint) {
|
||||
Some(fp) => fp,
|
||||
None => {
|
||||
could_not_establish_secure_connection(
|
||||
@@ -476,16 +536,16 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
contact_chat_id,
|
||||
"Fingerprint not provided.",
|
||||
);
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
};
|
||||
if !encrypted_and_signed(mimeparser, &fingerprint) {
|
||||
if !encrypted_and_signed(mime_message, &fingerprint) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
"Auth not encrypted.",
|
||||
);
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
if !fingerprint_equals_sender(context, &fingerprint, contact_chat_id) {
|
||||
could_not_establish_secure_connection(
|
||||
@@ -493,11 +553,11 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
contact_chat_id,
|
||||
"Fingerprint mismatch on inviter-side.",
|
||||
);
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
info!(context, "Fingerprint verified.",);
|
||||
// verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code
|
||||
let auth_0 = match mimeparser.get(HeaderDef::SecureJoinAuth) {
|
||||
let auth_0 = match mime_message.get(HeaderDef::SecureJoinAuth) {
|
||||
Some(auth) => auth,
|
||||
None => {
|
||||
could_not_establish_secure_connection(
|
||||
@@ -505,12 +565,12 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
contact_chat_id,
|
||||
"Auth not provided.",
|
||||
);
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
};
|
||||
if !token::exists(context, token::Namespace::Auth, &auth_0) {
|
||||
could_not_establish_secure_connection(context, contact_chat_id, "Auth invalid.");
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
if mark_peer_as_verified(context, fingerprint).is_err() {
|
||||
could_not_establish_secure_connection(
|
||||
@@ -518,7 +578,7 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
contact_chat_id,
|
||||
"Fingerprint mismatch on inviter-side.",
|
||||
);
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited);
|
||||
info!(context, "Auth verified.",);
|
||||
@@ -526,31 +586,47 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
emit_event!(context, Event::ContactsChanged(Some(contact_id)));
|
||||
inviter_progress!(context, contact_id, 600);
|
||||
if join_vg {
|
||||
let field_grpid = mimeparser
|
||||
.get(HeaderDef::SecureJoinGroup)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or_else(|| "");
|
||||
let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, field_grpid);
|
||||
if group_chat_id == 0 {
|
||||
error!(context, "Chat {} not found.", &field_grpid);
|
||||
return Ok(ret);
|
||||
} else if let Err(err) =
|
||||
chat::add_contact_to_chat_ex(context, group_chat_id, contact_id, true)
|
||||
{
|
||||
error!(context, "failed to add contact: {}", err);
|
||||
// the vg-member-added message is special:
|
||||
// this is a normal Chat-Group-Member-Added message
|
||||
// with an additional Secure-Join header
|
||||
let field_grpid = match mime_message.get(HeaderDef::SecureJoinGroup) {
|
||||
Some(s) => s.as_str(),
|
||||
None => {
|
||||
warn!(context, "Missing Secure-Join-Group header");
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
};
|
||||
match chat::get_chat_id_by_grpid(context, field_grpid) {
|
||||
Ok((group_chat_id, _, _)) => {
|
||||
if let Err(err) =
|
||||
chat::add_contact_to_chat_ex(context, group_chat_id, contact_id, true)
|
||||
{
|
||||
error!(context, "failed to add contact: {}", err);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!(context, "Chat {} not found: {}", &field_grpid, err);
|
||||
return Err(HandshakeError::ChatNotFound {
|
||||
group: field_grpid.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Alice -> Bob
|
||||
send_handshake_msg(context, contact_chat_id, "vc-contact-confirm", "", None, "");
|
||||
inviter_progress!(context, contact_id, 1000);
|
||||
}
|
||||
Ok(HandshakeMessage::Done)
|
||||
}
|
||||
"vg-member-added" | "vc-contact-confirm" => {
|
||||
if join_vg {
|
||||
ret.hide_this_msg = false;
|
||||
}
|
||||
/*=======================================================
|
||||
==== Bob - the joiner's side ====
|
||||
==== Step 7 in "Setup verified contact" protocol ====
|
||||
=======================================================*/
|
||||
|
||||
if context.bob.read().unwrap().expects != DC_VC_CONTACT_CONFIRM {
|
||||
info!(context, "Message belongs to a different handshake.",);
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Propagate);
|
||||
}
|
||||
let cond = {
|
||||
let bob = context.bob.read().unwrap();
|
||||
@@ -562,13 +638,19 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
context,
|
||||
"Message out of sync or belongs to a different handshake.",
|
||||
);
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Propagate);
|
||||
}
|
||||
let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string();
|
||||
|
||||
let vg_expect_encrypted = if join_vg {
|
||||
let group_id = get_qr_attr!(context, text2).to_string();
|
||||
let (_, is_verified_group, _) = chat::get_chat_id_by_grpid(context, group_id);
|
||||
// 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_verified_group, _) = chat::get_chat_id_by_grpid(context, &group_id)
|
||||
.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.
|
||||
@@ -579,15 +661,15 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
true
|
||||
};
|
||||
if vg_expect_encrypted
|
||||
&& !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice)
|
||||
&& !encrypted_and_signed(mime_message, &scanned_fingerprint_of_alice)
|
||||
{
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
"Contact confirm message not encrypted.",
|
||||
);
|
||||
ret.bob_securejoin_success = Some(false);
|
||||
return Ok(ret);
|
||||
context.bob.write().unwrap().status = 0;
|
||||
return Ok(HandshakeMessage::Propagate);
|
||||
}
|
||||
|
||||
if mark_peer_as_verified(context, &scanned_fingerprint_of_alice).is_err() {
|
||||
@@ -596,21 +678,26 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
contact_chat_id,
|
||||
"Fingerprint mismatch on joiner-side.",
|
||||
);
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Propagate);
|
||||
}
|
||||
Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined);
|
||||
emit_event!(context, Event::ContactsChanged(None));
|
||||
let cg_member_added = mimeparser
|
||||
let cg_member_added = mime_message
|
||||
.get(HeaderDef::ChatGroupMemberAdded)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or_else(|| "");
|
||||
if join_vg && !context.is_self_addr(cg_member_added)? {
|
||||
if join_vg
|
||||
&& !context
|
||||
.is_self_addr(cg_member_added)
|
||||
.map_err(|_| HandshakeError::NoSelfAddr)?
|
||||
{
|
||||
info!(context, "Message belongs to a different handshake (scaled up contact anyway to allow creation of group).");
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Propagate);
|
||||
}
|
||||
secure_connection_established(context, contact_chat_id);
|
||||
context.bob.write().unwrap().expects = 0;
|
||||
if join_vg {
|
||||
// Bob -> Alice
|
||||
send_handshake_msg(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -620,48 +707,52 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
"",
|
||||
);
|
||||
}
|
||||
ret.stop_ongoing_process = true;
|
||||
ret.bob_securejoin_success = Some(true);
|
||||
context.bob.write().unwrap().status = 1;
|
||||
context.stop_ongoing();
|
||||
Ok(HandshakeMessage::Propagate)
|
||||
}
|
||||
"vg-member-added-received" => {
|
||||
/* ============================================================
|
||||
/*==========================================================
|
||||
==== Alice - the inviter side ====
|
||||
==== Step 8 in "Out-of-band verified groups" protocol ====
|
||||
============================================================ */
|
||||
==========================================================*/
|
||||
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||
if contact.is_verified(context) == VerifiedStatus::Unverified {
|
||||
warn!(context, "vg-member-added-received invalid.",);
|
||||
return Ok(ret);
|
||||
return Ok(HandshakeMessage::Ignore);
|
||||
}
|
||||
inviter_progress!(context, contact_id, 800);
|
||||
inviter_progress!(context, contact_id, 1000);
|
||||
let field_grpid = mimeparser
|
||||
let field_grpid = mime_message
|
||||
.get(HeaderDef::SecureJoinGroup)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or_else(|| "");
|
||||
let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, &field_grpid);
|
||||
let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, &field_grpid)
|
||||
.map_err(|err| {
|
||||
warn!(context, "Failed to lookup chat_id from grpid: {}", err);
|
||||
HandshakeError::ChatNotFound {
|
||||
group: field_grpid.to_string(),
|
||||
}
|
||||
})?;
|
||||
context.call_cb(Event::SecurejoinMemberAdded {
|
||||
chat_id: group_chat_id,
|
||||
contact_id,
|
||||
});
|
||||
Ok(HandshakeMessage::Done)
|
||||
} else {
|
||||
warn!(context, "vg-member-added-received invalid.",);
|
||||
return Ok(ret);
|
||||
Ok(HandshakeMessage::Ignore)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
warn!(context, "invalid step: {}", step);
|
||||
Ok(HandshakeMessage::Ignore)
|
||||
}
|
||||
}
|
||||
|
||||
if ret.hide_this_msg {
|
||||
ret.delete_this_msg = true;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn secure_connection_established(context: &Context, contact_chat_id: u32) {
|
||||
fn secure_connection_established(context: &Context, contact_chat_id: ChatId) {
|
||||
let contact_id: u32 = chat_id_2_contact_id(context, contact_chat_id);
|
||||
let contact = Contact::get_by_id(context, contact_id);
|
||||
let addr = if let Ok(ref contact) = contact {
|
||||
@@ -674,7 +765,11 @@ fn secure_connection_established(context: &Context, contact_chat_id: u32) {
|
||||
emit_event!(context, Event::ChatModified(contact_chat_id));
|
||||
}
|
||||
|
||||
fn could_not_establish_secure_connection(context: &Context, contact_chat_id: u32, details: &str) {
|
||||
fn could_not_establish_secure_connection(
|
||||
context: &Context,
|
||||
contact_chat_id: ChatId,
|
||||
details: &str,
|
||||
) {
|
||||
let contact_id = chat_id_2_contact_id(context, contact_chat_id);
|
||||
let contact = Contact::get_by_id(context, contact_id);
|
||||
let msg = context.stock_string_repl_str(
|
||||
|
||||
@@ -365,6 +365,7 @@ fn table_exists(conn: &Connection, name: impl AsRef<str>) -> Result<bool> {
|
||||
Ok(exists)
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn open(
|
||||
context: &Context,
|
||||
sql: &Sql,
|
||||
|
||||
11
src/token.rs
11
src/token.rs
@@ -6,6 +6,7 @@
|
||||
|
||||
use deltachat_derive::*;
|
||||
|
||||
use crate::chat::ChatId;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::*;
|
||||
use crate::sql;
|
||||
@@ -27,28 +28,28 @@ impl Default for Namespace {
|
||||
|
||||
/// Creates a new token and saves it into the database.
|
||||
/// Returns created token.
|
||||
pub fn save(context: &Context, namespace: Namespace, foreign_id: u32) -> String {
|
||||
pub fn save(context: &Context, namespace: Namespace, foreign_id: ChatId) -> String {
|
||||
// foreign_id may be 0
|
||||
let token = dc_create_id();
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"INSERT INTO tokens (namespc, foreign_id, token, timestamp) VALUES (?, ?, ?, ?);",
|
||||
params![namespace, foreign_id as i32, &token, time()],
|
||||
params![namespace, foreign_id, &token, time()],
|
||||
)
|
||||
.ok();
|
||||
token
|
||||
}
|
||||
|
||||
pub fn lookup(context: &Context, namespace: Namespace, foreign_id: u32) -> Option<String> {
|
||||
pub fn lookup(context: &Context, namespace: Namespace, foreign_id: ChatId) -> Option<String> {
|
||||
context.sql.query_get_value::<_, String>(
|
||||
context,
|
||||
"SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;",
|
||||
params![namespace, foreign_id as i32],
|
||||
params![namespace, foreign_id],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn lookup_or_new(context: &Context, namespace: Namespace, foreign_id: u32) -> String {
|
||||
pub fn lookup_or_new(context: &Context, namespace: Namespace, foreign_id: ChatId) -> String {
|
||||
lookup(context, namespace, foreign_id).unwrap_or_else(|| save(context, namespace, foreign_id))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
import re
|
||||
|
||||
if __name__ == "__main__":
|
||||
if Path('src/top_evil_rs.py').exists():
|
||||
os.chdir('src')
|
||||
filestats = []
|
||||
for fn in Path(".").glob("**/*.rs"):
|
||||
s = fn.read_text()
|
||||
s = re.sub(r"(?m)///.*$", "", s) # remove comments
|
||||
unsafe = s.count("unsafe")
|
||||
free = s.count("free(")
|
||||
unsafe_fn = s.count("unsafe fn")
|
||||
chars = s.count("c_char") + s.count("CStr")
|
||||
libc = s.count("libc")
|
||||
filestats.append((fn, unsafe, free, unsafe_fn, chars, libc))
|
||||
|
||||
sum_unsafe, sum_free, sum_unsafe_fn, sum_chars, sum_libc = 0, 0, 0, 0, 0
|
||||
|
||||
for fn, unsafe, free, unsafe_fn, chars, libc in reversed(sorted(filestats, key=lambda x: sum(x[1:]))):
|
||||
if unsafe + free + unsafe_fn + chars + libc == 0:
|
||||
continue
|
||||
print("{0: <25} unsafe: {1: >3} free: {2: >3} unsafe-fn: {3: >3} chars: {4: >3} libc: {5: >3}".format(str(fn), unsafe, free, unsafe_fn, chars, libc))
|
||||
sum_unsafe += unsafe
|
||||
sum_free += free
|
||||
sum_unsafe_fn += unsafe_fn
|
||||
sum_chars += chars
|
||||
sum_libc += libc
|
||||
|
||||
|
||||
print()
|
||||
print("total unsafe:", sum_unsafe)
|
||||
print("total free:", sum_free)
|
||||
print("total unsafe-fn:", sum_unsafe_fn)
|
||||
print("total c_chars:", sum_chars)
|
||||
print("total libc:", sum_libc)
|
||||
BIN
test-data/image/avatar900x900.png
Normal file
BIN
test-data/image/avatar900x900.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user