Compare commits

...

472 Commits

Author SHA1 Message Date
link2xt
5820c4ce95 Serialize mime_compressed 2024-04-06 16:37:51 +00:00
link2xt
12ba33d9d4 Serialize uid 2024-04-06 15:54:12 +00:00
link2xt
60a7bbc9b5 Do not serialize is_default
It is only stored for compatibility with old versions
2024-04-06 15:27:11 +00:00
link2xt
e9f434b562 Serialize backward_verified_key_id 2024-04-06 02:48:27 +00:00
link2xt
2423cb8175 feat: add support for dumping the database to stream 2024-04-06 01:45:14 +00:00
link2xt
65c9e72bf4 test: test withdrawing group join QR codes 2024-04-05 22:34:02 +00:00
link2xt
ea4d954c77 fix: do not emit MSGS_CHANGED event for outgoing hidden messages
This includes synchronization messages.
2024-04-05 22:34:02 +00:00
link2xt
43523a96a2 api(deltachat-rpc-client): add check_qr and set_config_from_qr APIs 2024-04-05 22:34:02 +00:00
link2xt
2e2fa9e74f chore: update lockfile in /fuzz 2024-04-05 19:44:51 +00:00
B. Petersen
e43ffb20a1 chore(release): prepare for 1.137.2 2024-04-05 14:21:15 +00:00
link2xt
2f0f247e70 refactor: use Rust 1.77.0 support for recursion in async functions 2024-04-04 17:01:15 +00:00
Simon Laux
5bda4f0c26 update node constants for #5387 (#5429) 2024-04-04 15:31:02 +02:00
iequidoo
d39c8a3a19 refactor: is_probably_private_reply: Remove reaction-specific code
Instead, look up the 1:1 chat in `receive_imf::add_parts()`. This is a more generic approach to fix
assigning outgoing reactions to 1:1 chats in the multi-device setup. Although currently both
approaches give the same result, this way we can even implement a "react privately"
functionality. Maybe it sounds useless, but it seems better to have less reaction-specific code.
2024-04-03 21:29:27 -03:00
link2xt
e465415039 fix: do not ignore Message::load_from_db errors 2024-04-04 01:44:50 +02:00
B. Petersen
5cef77b8e6 fix: do not show empty summary if message reacted to is deleted
we checked for tombstones already using `is_trash()`,
however, we've overseen that tombstones get deleted at some point :)

therefore, just do not treat loading failures of the weak msg_id as errors -
usually, they are not - and if, just the normal summary is shown.
in theory, we could check for existance explicitly before tryong load_from_db,
however, that would be additional code (and maybe another database call)
and not worth the effort.

anyways, this commit also adds an explicit test
for physical deletion after housekeeping.
2024-04-04 01:44:50 +02:00
dependabot[bot]
60e733c30c chore(cargo): bump fast-socks5 from 0.9.5 to 0.9.6
Bumps [fast-socks5](https://github.com/dizda/fast-socks5) from 0.9.5 to 0.9.6.
- [Release notes](https://github.com/dizda/fast-socks5/releases)
- [Commits](https://github.com/dizda/fast-socks5/commits/v0.9.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-03 21:51:57 +00:00
dependabot[bot]
8b98816eb9 Merge pull request #5404 from deltachat/dependabot/cargo/rustyline-14.0.0 2024-04-03 21:19:38 +00:00
link2xt
50165b3e35 chore: upgrade image from 0.24.9 to 0.25.1 2024-04-03 20:59:06 +00:00
link2xt
0be8b5a5c4 chore: upgrade h2 from 0.4.3 to 0.4.4
This upgrade contains a fix for RUSTSEC-2024-0332
2024-04-03 20:34:14 +00:00
B. Petersen
451bb6e9db add tests for get_summary_text_without_prefix() 2024-04-03 20:55:22 +02:00
B. Petersen
83196d4cb5 add get_summary_text_without_prefix()
use get_summary_text_without_prefix() to get raw summaries without prefixes,
this is needed for reaction summaries,
where we also do not show the name, so we do not want to show "Forwarded" as well.
2024-04-03 20:55:22 +02:00
B. Petersen
0003e55ad5 test reactions for forwarded messages 2024-04-03 20:55:22 +02:00
link2xt
02014eda6c chore: update from brotli 3.4.0 to brotli 4.0.0 2024-04-03 17:25:28 +00:00
link2xt
f1c6cd69e9 chore: update chrono from 0.4.34 to 0.4.37 2024-04-03 16:49:19 +00:00
bjoern
ace281ff6c feat: show reactions in summaries (#5387)
shows the last reaction in chatlist's summaries if there is no
newer message.

the reason to show reactions in the summary, is to make them a _little_
more visible when one is not in the chat. esp. in not-so-chatty or in
one-to-ones chats this becomes handy: imaging a question and someone
"answers" with "thumbs up" ... 

otoh, reactions are still tuned down on purpose: no notifications, chats
are opend as usual, the chatlist is not sorted by reactions and also the
date in the summary refer to the last message - i thought quite a bit
about that, this seems to be good compromise and will raise the fewest
questions. it is somehow clear to the users that reactions are not the
same as a real message. also, it is comparable easy to implement - no
UI changes required :)

all that is very close to what whatsapp is doing (figured that out by
quite some testing ... to cite @adbenitez: if in doubt, we can blame
whatsapp :)

technically, i first wanted to go for the "big solution" and add two
more columns, chat_id and timestamp, however, it seemed a bit bloated if
we really only need the last one. therefore, i just added the last
reaction information to the chat's param, which seems more performant
but also easier to code :)
2024-04-03 08:50:05 +00:00
dependabot[bot]
c9edd525e0 chore(cargo): bump rustyline from 13.0.0 to 14.0.0
Bumps [rustyline](https://github.com/kkawakam/rustyline) from 13.0.0 to 14.0.0.
- [Release notes](https://github.com/kkawakam/rustyline/releases)
- [Changelog](https://github.com/kkawakam/rustyline/blob/master/History.md)
- [Commits](https://github.com/kkawakam/rustyline/compare/v13.0.0...v14.0.0)

---
updated-dependencies:
- dependency-name: rustyline
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-03 06:12:01 +00:00
link2xt
3f35b442c3 chore(release): prepare for 1.137.1 2024-04-03 01:28:13 +00:00
link2xt
87e9365016 ci: remove android builds for x86 and x86_64
They are failing to build.
2024-04-03 01:17:21 +00:00
link2xt
9806509f4a chore(release): prepare for 1.137.0 2024-04-02 21:22:58 +00:00
link2xt
91600a34b6 chore(cargo): update aho-corasick from 1.1.2 to 1.1.3 2024-04-02 21:11:04 +00:00
dependabot[bot]
d16351d207 chore(cargo): bump regex from 1.10.3 to 1.10.4
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.3 to 1.10.4.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.10.3...1.10.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 21:03:12 +00:00
dependabot[bot]
4caf638201 chore(cargo): bump backtrace from 0.3.69 to 0.3.71
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.69 to 0.3.71.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.69...0.3.71)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 20:59:21 +00:00
B. Petersen
375fcbd63c reactions are not a 'probably private reply'
`is_probably_private_reply()` checks
if a message should better go to the one to one chat
instead of the chat already identified by the `In-Reply-To`.
this functionality is needed to make "Reply Privately" work.

however, this functionality is never true for reactions.
if we would return `true` here, own reactions seen by a second device
would not get the correct chat assiged.
2024-04-02 22:33:36 +02:00
B. Petersen
6ff3a2cf7c add failing test for #5418 (wrong DC_EVENT_REACTIONS_CHANGED) 2024-04-02 22:33:36 +02:00
link2xt
a890fe3a9a chore(cargo): bump reqwest from 0.11.24 to 0.12.2
This also removes duplicate dependencies
for `http-body`, `http` and `hyper`.
2024-04-02 18:58:13 +00:00
link2xt
2b8bf29fce api(deltachat-rpc-client): add futures
futures allow to call multiple methods in parallel
without threads.

This introduces RpcFuture class and futuremethod decorator.
2024-04-02 16:54:25 +00:00
link2xt
26400a9e4e chore: update deny.toml 2024-04-02 16:39:45 +00:00
dependabot[bot]
f8b9bb9083 chore(cargo): bump axum from 0.7.4 to 0.7.5
Bumps [axum](https://github.com/tokio-rs/axum) from 0.7.4 to 0.7.5.
- [Release notes](https://github.com/tokio-rs/axum/releases)
- [Changelog](https://github.com/tokio-rs/axum/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/axum/compare/axum-v0.7.4...axum-v0.7.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 16:35:51 +00:00
dependabot[bot]
42f9047a54 chore(cargo): bump futures-lite from 2.2.0 to 2.3.0
Bumps [futures-lite](https://github.com/smol-rs/futures-lite) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/smol-rs/futures-lite/releases)
- [Changelog](https://github.com/smol-rs/futures-lite/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/futures-lite/compare/v2.2.0...v2.3.0)

---
updated-dependencies:
- dependency-name: futures-lite
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 16:33:31 +00:00
dependabot[bot]
6433a3a5f3 chore(cargo): bump syn from 2.0.52 to 2.0.57
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.52 to 2.0.57.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.52...2.0.57)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 16:32:58 +00:00
dependabot[bot]
4b6a03c904 chore(cargo): bump anyhow from 1.0.80 to 1.0.81
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.80 to 1.0.81.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.80...1.0.81)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 16:31:50 +00:00
dependabot[bot]
ff3df01d98 chore(cargo): bump serde_json from 1.0.114 to 1.0.115
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.114 to 1.0.115.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.114...v1.0.115)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 16:30:56 +00:00
dependabot[bot]
cdc99854b2 chore(cargo): bump strum_macros from 0.26.1 to 0.26.2
Bumps [strum_macros](https://github.com/Peternator7/strum) from 0.26.1 to 0.26.2.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/compare/v0.26.1...v0.26.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 16:29:31 +00:00
dependabot[bot]
e7072bcb75 chore(cargo): bump async-smtp from 0.9.0 to 0.9.1
Bumps [async-smtp](https://github.com/async-email/async-smtp) from 0.9.0 to 0.9.1.
- [Commits](https://github.com/async-email/async-smtp/compare/v0.9.0...v0.9.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 14:46:20 +00:00
dependabot[bot]
7950bde3c6 chore(cargo): bump smallvec from 1.13.1 to 1.13.2
Bumps [smallvec](https://github.com/servo/rust-smallvec) from 1.13.1 to 1.13.2.
- [Release notes](https://github.com/servo/rust-smallvec/releases)
- [Commits](https://github.com/servo/rust-smallvec/compare/v1.13.1...v1.13.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 13:18:17 +00:00
dependabot[bot]
a259669c98 chore(cargo): bump tokio-stream from 0.1.14 to 0.1.15
Bumps [tokio-stream](https://github.com/tokio-rs/tokio) from 0.1.14 to 0.1.15.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-stream-0.1.14...tokio-stream-0.1.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 13:11:11 +00:00
dependabot[bot]
603e6be9b4 chore(cargo): bump toml from 0.8.10 to 0.8.12
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.10 to 0.8.12.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.10...toml-v0.8.12)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 13:00:15 +00:00
dependabot[bot]
a78c484467 chore(cargo): bump uuid from 1.7.0 to 1.8.0
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.7.0...1.8.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 12:58:52 +00:00
dependabot[bot]
e78f07b343 chore(cargo): bump strum from 0.26.1 to 0.26.2
Bumps [strum](https://github.com/Peternator7/strum) from 0.26.1 to 0.26.2.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/compare/v0.26.1...v0.26.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 12:55:39 +00:00
dependabot[bot]
8abf10aacb chore(cargo): bump pin-project from 1.1.4 to 1.1.5
Bumps [pin-project](https://github.com/taiki-e/pin-project) from 1.1.4 to 1.1.5.
- [Release notes](https://github.com/taiki-e/pin-project/releases)
- [Changelog](https://github.com/taiki-e/pin-project/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/pin-project/compare/v1.1.4...v1.1.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 12:53:42 +00:00
dependabot[bot]
2fef4acdd6 chore(cargo): bump tokio from 1.36.0 to 1.37.0
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.36.0 to 1.37.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.36.0...tokio-1.37.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 12:49:13 +00:00
Sebastian Klähn
de27be3a36 build: add development shell (#5390)
Add nix development shell to flake.nix.

---------

Co-authored-by: Septias <scoreplayer2000@gmail.comclear>
2024-04-02 11:20:34 +02:00
dependabot[bot]
c62e8539a1 chore(cargo): bump thiserror from 1.0.57 to 1.0.58
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.57 to 1.0.58.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.57...1.0.58)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 04:49:18 +00:00
link2xt
22c0aef9c0 build(python): remove setuptools_scm dependency
We update version in several .toml and .json files
on every release anyway, so updating pyproject.toml is easy.

setuptools_scm makes it more difficult to build
python packages for software distributions
because it requires full git checkout
with tags rather than just a worktree.
It is also possible to remove or move tags
after the release, so git revision no longer
pins python package version if setuptools_scm is used.
2024-04-02 04:43:39 +00:00
link2xt
87805bc36d build: add repository to Cargo.toml 2024-03-31 22:40:01 +00:00
B. Petersen
99c4d24eab cleanup jobs and Params relicts
- the `jobs` table is no longer in use,
  no need to track files on housekeeping,
  no need to clear it from repl tool

- some `Params` were used for jobs table only,
  they can be used freely for other purposes on other tables.
  param 'protection settings timestamp' was never used in practise,
  its code is removed as well, so we can free the Param as well.
2024-03-30 08:10:57 +01:00
bjoern
7bf9c4a2d9 api: remove unused dc_accounts_all_work_done() (#5384)
it was used by iOS to know when a background fetch was complete;
meanwhile the superiour `dc_accounts_background_fetch()` is used for
that.

there is still the corresponding context function `dc_all_work_done()`,
this not used by any UI as well, however, it is in use by a python
tests.

not sure, what to do with it, at a first glance, the test still seems
useful.
2024-03-30 01:18:17 +01:00
iequidoo
304e902fce fix: Don't send selfavatar in SecureJoin messages before contact verification (#5354)
Don't attach selfavatar in "v{c,g}-request" and "v{c,g}-auth-required" messages:
- These messages are deleted right after processing, so other devices won't see the avatar.
- It's also good for privacy because the contact isn't yet verified and these messages are auto-sent
  unlike usual unencrypted messages.
2024-03-28 21:29:05 -03:00
B. Petersen
0155d93622 test: Remove flaky time check from test_list_from()
the rendered time output seems different on different systems and timezomes,
eg. on my local machine, it is
`Sent: 2024.03.20 10:00:01 ` and not `Sent: 2024.03.20 09:00:01`,
maybe because of winter/summer time, idk.

as the gist of the test is to check the name,
however, i just removed the whole time check.
2024-03-28 16:40:41 +01:00
link2xt
ebd097bdbe ci: shorter names for deltachat-rpc-server jobs 2024-03-28 06:57:44 +00:00
link2xt
a11d01f8a3 ci: build deltachat-rpc-server for Android 2024-03-28 06:57:44 +00:00
link2xt
38491b694b build(nix): add outputs for Android binaries
Using NDK 24 because NDK 23 does not have getauxval() function.
2024-03-28 06:57:44 +00:00
link2xt
e702c1a8ca feat: include more entries into DNS fallback cache 2024-03-25 10:30:08 +00:00
link2xt
1adea3c678 fix: put overridden sender name into message info 2024-03-25 05:19:04 +00:00
link2xt
9af812a3e7 refactor(jsonrpc): add msg_id and account_id to get_message() errors 2024-03-24 18:18:34 +00:00
link2xt
36bdf8a67e fix: do not ignore Contact::get_by_id() error in from_field_to_contact_id() 2024-03-24 18:18:34 +00:00
link2xt
20b30fc70a refactor: remove MessageObject::from_message_id()
It accepted u32, not Message-ID.
There is a properly typed from_msg_id function
that accepts MsgId next to it.
2024-03-24 18:18:34 +00:00
link2xt
e59ff6ca74 feat: include 3 recent Message-IDs in References header
Do not include oldest reference, because chat members
which have been added later and have not seen the first message
do not have referenced message in the database.

Instead, include up to 3 recent Message-IDs.
2024-03-23 02:06:24 +00:00
iequidoo
0e5db36205 fix: Rescan folders after changing Config::SentboxWatch
If `Config::SentboxWatch` changes, the sentbox needs to be [un]configured which is done by
`Imap::scan_folders()`.
2024-03-22 19:08:05 -03:00
iequidoo
7960944b14 test: test_mvbox_sentbox_threads: Check that sentbox gets configured after setting sentbox_watch (#5105) 2024-03-22 19:08:05 -03:00
link2xt
71c2383cbe ci: update to Rust 1.77.0 2024-03-22 19:44:15 +00:00
link2xt
5f5b272726 chore: add result to .gitignore 2024-03-22 18:42:08 +00:00
link2xt
b34fe8f118 feat: do not include provider hostname in Message-ID
It is leaked by anonymous mailing lists,
making it possible to tell which provider the sender is using.
Use `localhost` as the hostname instead.
2024-03-20 00:36:15 +00:00
bjoern
810be4f6c7 fix: preserve upper-/lowercase of links parsed by dehtml() (#5362)
this PR fixes a bug that lowercases all links handleld by `dehtml()`,
which is wrong.

closes #5361
2024-03-19 16:38:23 +01:00
link2xt
1ebbe26ebb api!: remove data from DC_EVENT_INCOMING_MSG_BUNCH
It is not used by existing clients
and incorrectly included all downloaded messages,
including outgoing messages and MDNs.
2024-03-19 14:21:35 +00:00
link2xt
0f5d5dd2b2 build(cmake): build outside the source tree
If user specifies build directory, respect this
instead of building inside the target/ directory.

Also remove outdated comment about Rust 1.50.0 support.
Current MSRV is 1.70.0 and we use 2021 edition
which implies version 2 resolver.
2024-03-19 12:14:33 +00:00
link2xt
473dbe01af chore(release): prepare for 1.136.6 2024-03-19 03:57:44 +00:00
link2xt
069ed7afa6 chore: nix flake update 2024-03-19 03:56:11 +00:00
link2xt
9313ece3cd ci: automate publishing of deltachat-rpc-server to PyPI 2024-03-19 03:25:39 +00:00
link2xt
900168c68c build: read version from Cargo.toml in wheel-rpc-server.py 2024-03-19 03:25:39 +00:00
link2xt
0bd137b4e5 build: add description to deltachat-rpc-server wheels
This is required to pass `twine check`.
2024-03-19 03:04:40 +00:00
link2xt
75da205ff6 docs(deltachat-rpc-server): update deltachat-rpc-client URL 2024-03-19 03:04:40 +00:00
link2xt
67e5fbbfe3 ci: update actions/cache from v3 to v4 2024-03-19 02:17:04 +00:00
link2xt
570daf42ec chore(release): prepare for 1.136.5 2024-03-19 00:01:25 +00:00
link2xt
fcbbb91cde docs(deltachat-rpc-client): document that 0 is a special value of set_ephemeral_timer() 2024-03-18 20:15:54 +00:00
link2xt
c3a7fc4c8d test: test that reordering of Member added message results in square bracket error
This is a test reproducing the problem
in <https://github.com/deltachat/deltachat-core-rust/issues/5339>.
Fix would be to avoid reordering on the server side,
so the test checks that the unverified message
is replaced with a square bracket error
as expected if messages arrive in the wrong order.
2024-03-18 18:14:06 +00:00
iequidoo
4b4c57a480 fix: Add white background to recoded avatars (#3787)
Add white background instead of the default black one to avatars when recoding to JPEG. But not for
"usual" images to spare the CPU. The motivation is to handle correctly
"black-on-transparent-background" avatars which are quite common.
2024-03-17 22:14:08 -03:00
bjoern
b95d58208c add save_mime_headers to debug info (#5350)
as turned out on recent researches wrt a slow database, setting
`save_mime_headers` will result in 25x larger databases.

this is definetely something we want to know at least in the debug info.

(the option cannot be enabled in current UIs,
however, esp. devs find options to set this.
apart from that, save_mime_headers is only used in some python tests)

ftr: the huge database was more than 10 times slower, leading to missing
notifications on ios (as things do not finish within the few seconds iOS
gave us).
moreover, the hugeness also avoids exporting; this is fixed by
https://github.com/deltachat/deltachat-core-rust/pull/5349
2024-03-17 08:27:08 +01:00
bjoern
c468eb088e fix: on iOS, use FILE (default) instead of MEMORY (#5349)
this PR fixes one of the issues we had with an (honestly accidentally)
huge database of >2gb.

this database could not be exported on iOS, as ram memory is limited
there, leading to the app just crashing.

it is unclear if that would be better on eg. Android, however,
temp_store=FILE is not working as well there, this is the smaller
drawback there.

before merging, this PR should be tested on time with export/import on
iOS (export/import uses VACUUM which uses /tmp) EDIT: did so, works on
iphone7 with ios15
2024-03-17 08:25:55 +01:00
B. Petersen
de37135ed6 nicer summaries: prefer emoji over names 2024-03-15 19:20:33 +01:00
iequidoo
33777d8759 fix: Update MemberListTimestamp when sending a group message
`Param::MemberListTimestamp` was updated only from `receive_imf::apply_group_changes()` i.e. for
received messages. If we sent a message, that timestamp wasn't updated, so remote group membership
changes always overrode local ones. Especially that was a problem when a message is sent offline so
that it doesn't incorporate recent group membership changes.
2024-03-15 06:14:38 -03:00
link2xt
8cc348bfa4 fix: terminate ephemeral and location loop immediately on channel close
When scheduler is destroyed, e.g. during a key import,
there is some time between destroying the interrupt channel
and the loop task.

To avoid busy looping, tasks should terminate if
receiving from the interrupt loop fails
instead of treating it as the interrupt.
2024-03-15 01:26:23 +00:00
link2xt
76bbd5fd72 build: add README to deltachat-rpc-client Python packages 2024-03-11 14:42:32 +01:00
link2xt
eaed2381e7 chore(release): prepare for 1.136.4 2024-03-11 12:52:46 +00:00
link2xt
6198ed0ef5 ci: add workflow for automatic publishing of deltachat-rpc-client 2024-03-11 11:45:02 +00:00
link2xt
9f4af679a3 build: build deltachat-rpc-server wheels with nix 2024-03-10 20:22:47 +00:00
B. Petersen
e158b889c9 fix: remove duplicate CHANGELOG entries for 1.135.1 2024-03-10 01:49:58 +01:00
link2xt
9f7defa8da build(nix): make .#libdeltachat buildable on macOS 2024-03-09 19:10:11 +00:00
link2xt
e9d7fe0561 chore(release): prepare for 1.136.3 2024-03-09 16:07:15 +00:00
iequidoo
7d7289bd51 feat: Start IMAP loop for sentbox only if it is configured (#5105) 2024-03-09 15:06:49 +01:00
iequidoo
ebdc52247c chore: RPC client: Add missing constants (#5110) 2024-03-08 19:23:47 -03:00
Simon Laux
36bb4a7a32 ci: remove artefacts from npm package 2024-03-08 08:08:39 +00:00
iequidoo
c0832af634 refactor: Remove deduplicate_peerstates()
There's the `UNIQUE (acpeerstates.addr)` constraint since db v94.
2024-03-08 00:42:39 -03:00
iequidoo
b6db0152b0 fix: Create new Peerstate for unencrypted message with already known Autocrypt key, but a new address
An unencrypted message with already known Autocrypt key, but sent from another address, means that
it's rather a new contact sharing the same key than the existing one changed its address, otherwise
it would already have our key to encrypt.
2024-03-08 00:42:39 -03:00
iequidoo
bc7fd4495b fix: Remove leading whitespace from Subject (#5106)
If Subject is multiline-formatted, `mailparse` adds the leading whitespace to it. The solution is to
always remove the leading whitespace, because if Subject isn't multiline-formatted, it never
contains the leading whitespace anyway. But as for the trailing whitespace -- i checked -- it's
never removed, so let's keep this as is.
2024-03-08 00:08:52 -03:00
dependabot[bot]
e67e86422f chore(deps): bump mio from 0.8.8 to 0.8.11 in /fuzz
Bumps [mio](https://github.com/tokio-rs/mio) from 0.8.8 to 0.8.11.
- [Release notes](https://github.com/tokio-rs/mio/releases)
- [Changelog](https://github.com/tokio-rs/mio/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/mio/compare/v0.8.8...v0.8.11)

---
updated-dependencies:
- dependency-name: mio
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-08 02:54:57 +00:00
link2xt
2030de11d9 chore: fix 2024-03-05 nightly clippy warnings 2024-03-08 02:53:47 +00:00
link2xt
2c5a0cac5f build(nix): include SystemConfiguration framework on darwin systems 2024-03-08 00:28:51 +00:00
link2xt
251917e602 build(nix): cleanup cross-compilation code 2024-03-08 00:28:51 +00:00
link2xt
273719ae7c ci: wait for build_windows task before trying to publish it 2024-03-07 23:45:06 +00:00
iequidoo
e639b58c6f refactor: Don't even parse Autocrypt header for outgoing messages (#5259)
Accordingly, there's no need in `Peerstate` for self addresses (and in the db too).
2024-03-06 19:32:37 -03:00
link2xt
5addfa8d1d chore(release): prepare for 1.136.2 2024-03-05 22:42:36 +00:00
link2xt
02d68332c7 build: downgrade cc to 1.0.83
1.0.84 and 1.0.85 are yanked.
With 1.0.86 and 1.0.89 Delta Chat for Android fails to build.

Fixes <https://github.com/deltachat/deltachat-android/issues/2972>.
2024-03-05 22:21:28 +00:00
link2xt
97abb9a0a9 ci: update setup-node action 2024-03-05 03:19:46 +00:00
link2xt
d0e0cfafef chore(release): prepare for 1.136.1 2024-03-05 01:23:12 +00:00
link2xt
f630b5fb39 chore: update node constants 2024-03-05 01:18:22 +00:00
link2xt
d9bab938d5 build: restore MSRV 1.70.0 2024-03-05 01:04:49 +00:00
link2xt
215ec14b20 build: revert to OpenSSL 3.1
OpenSSL 3.2 build currently fails under Nix
so we cannot build deltachat-rpc-server releases:
<https://github.com/alexcrichton/openssl-src-rs/issues/235>
2024-03-05 00:14:52 +00:00
link2xt
ea728e9b62 docs: add missing 1.136.0 link to changelog 2024-03-04 21:33:20 +00:00
link2xt
2af9ff1d01 chore(release): prepare for 1.136.0 2024-03-04 21:10:04 +00:00
link2xt
7502234686 api: dc_accounts_set_push_device_token and dc_get_push_state APIs 2024-03-04 21:10:04 +00:00
link2xt
863a386d0f test: test that ASM "encrypted" with plaintext algorithm is not accepted 2024-03-04 21:10:04 +00:00
link2xt
e4b49dfdef fix: validate Group IDs and SecureJoin tokens 2024-03-04 21:10:04 +00:00
iequidoo
612aa1431e fix: Check that peer SecureJoin messages (except vc/vg-request) gossip our addr+pubkey
This fixes the following identity-misbinding attack:

It appears that Bob’s messages in the SecureJoin protocol do not properly “bind” to Alice’s public
key or fingerprint. Even though Bob’s messages carry Alice’s public key and address as a gossip in
the protected payload, Alice does not reject the message if the gossiped key is different from her
own key. As a result, Mallory could perform an identity-misbinding attack. If Mallory obtained
Alice’s QR invite code, she could change her own QR code to contain the same tokens as in Alice’s QR
code, and convince Bob to scan the modified QR code, possibly as an insider attacker. Mallory would
forward messages from Bob to Alice and craft appropriate responses for Bob on his own. In the end,
Bob would believe he is talking to Mallory, but Alice would believe she is talking to Bob.
2024-03-04 21:10:04 +00:00
link2xt
781d3abdb9 fix: make should_do_gossip() return true even if we send securejoin only to Alice 2024-03-04 21:10:04 +00:00
link2xt
78d01933ad fix: don't leak Group-ID in Message-ID
Chat assignment based on In-Reply-To and References works good enough
even if the message cannot be decrypted.
2024-03-04 21:07:10 +00:00
iequidoo
1a1467f7cf fix: Remove unsigned Chat-Group-* headers from Autocrypt-encrypted messages
These headers are opportunistically protected, so if they appear in the unencrypted part, they are
probably added by a malicious server.
2024-03-04 21:07:10 +00:00
link2xt
8d09291d1e fix: do not send Secure-Join-Group in vg-request
Secure-Join-Group is only expected by old core in vg-request-with-auth.
There is no reason to leak group ID in unencrypted vg-request.
Besides that, Secure-Join-Group is deprecated
as Alice knows Group ID corresponding to the auth code,
so the header can be removed completely eventually.
2024-03-04 21:07:10 +00:00
link2xt
4ccd2b8d02 fix: require that Autocrypt Setup Message is self-sent 2024-03-04 21:07:10 +00:00
iequidoo
794596ec69 fix: Don't log SecureJoin QRs
Delta Chat mustn't write sensitive information to unencrypted log files in local storage.
2024-03-04 21:07:10 +00:00
link2xt
3a787519b3 test: test that encrypted Message-ID overwrites X-Microsoft-Original-Message-ID 2024-03-04 21:07:10 +00:00
link2xt
c03e163ed2 fix: reject messages with protected From not corresponding to outer From 2024-03-04 21:07:10 +00:00
iequidoo
6cee295a5d fix: Don't treat forged outgoing messages as Autocrypt-encrypted 2024-03-04 21:07:10 +00:00
iequidoo
f0be7daae9 test: Add failing test on outgoing message forgery
If a message is sent from SELF, but signed with a foreign key, it mustn't be considered
Autocrypt-encrypted and shown with a padlock. Currently this is broken.
2024-03-04 21:07:10 +00:00
link2xt
0b279ec84e fix: make protected Message-ID take precedence over X-Microsoft-Original-Message-ID 2024-03-04 21:07:10 +00:00
link2xt
e919de78a3 fix: do not take Secure-Join-Auth from unprotected headers 2024-03-04 21:07:10 +00:00
link2xt
6ea675a12f fix: do not use Secure-Join-Group header
Alice already knows which auth token corresponds to which group.
There is no need to trust Bob on sending the correct group ID.
2024-03-04 21:07:10 +00:00
link2xt
b970ebe67a fix: do not compress SecureJoin messages 2024-03-04 21:07:10 +00:00
link2xt
3c4c701f9b fix: protect Secure-Join header
Secure-Join header must come from protected headers
unless it is a "vc-request" or "vg-request".
2024-03-04 21:07:10 +00:00
link2xt
01ac9c8b90 fix: ensure Autocrypt-Gossip is not taken from insecure headers 2024-03-04 21:07:10 +00:00
link2xt
f6de23738d build: increase MSRV to 1.74.0
This is what updated dependencies require.

Also update Rust used to build manylinux wheels
from 1.72.0 to 1.76.0.
2024-03-04 21:07:10 +00:00
link2xt
ddc2704278 chore: cargo update 2024-03-04 21:07:10 +00:00
link2xt
3d2b164c05 chore(deps): update mio to fix RUSTSEC-2024-0019 2024-03-04 21:07:09 +00:00
link2xt
2094bc3135 chore(deps): update buffer-redux to remove unmaintained safemem 2024-03-04 21:07:09 +00:00
holger krekel
acff8205e2 test: fix pytest compat (#5317)
seems pytest_report_header has changed with pytest incompatible but we
don't need it anyway so we can just leave it out.
2024-03-04 21:07:09 +00:00
link2xt
255400028a build: do not vendor OpenSSL when cross-compiling (#5316)
Compilation of vendored OpenSSL inside Nix is broken since
<https://github.com/alexcrichton/openssl-src-rs/pull/229> due to build
script changes.

There is anyway no need to compile vendored OpenSSL as nixpkgs already
contains OpenSSL package.

This fixes `nix build .#deltachat-rpc-server-x86_64-linux` and similar
commands which are used during releases.
2024-03-04 21:07:09 +00:00
Hocuri
d7615b223f feat(Self-Reporting): Report number of protected/encrypted/unencrypted chats (#5292) 2024-03-04 21:07:09 +00:00
dependabot[bot]
00fbf540c4 chore(cargo): bump tempfile from 3.10.0 to 3.10.1
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.10.0 to 3.10.1.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.10.0...v3.10.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 21:07:09 +00:00
dependabot[bot]
288eccf722 chore(cargo): bump image from 0.24.8 to 0.24.9
Bumps [image](https://github.com/image-rs/image) from 0.24.8 to 0.24.9.
- [Changelog](https://github.com/image-rs/image/blob/master/CHANGES.md)
- [Commits](https://github.com/image-rs/image/compare/v0.24.8...v0.24.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-03 14:46:53 +00:00
dependabot[bot]
99ee769580 chore(cargo): bump textwrap from 0.16.0 to 0.16.1
Bumps [textwrap](https://github.com/mgeisler/textwrap) from 0.16.0 to 0.16.1.
- [Release notes](https://github.com/mgeisler/textwrap/releases)
- [Changelog](https://github.com/mgeisler/textwrap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mgeisler/textwrap/compare/0.16.0...0.16.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-03 08:01:51 +00:00
dependabot[bot]
345759d653 chore(cargo): bump syn from 2.0.48 to 2.0.52
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.48 to 2.0.52.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.48...2.0.52)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-03 04:15:51 +00:00
link2xt
db0143f01a build: remove deprecated unmaintained field from deny.toml 2024-03-03 03:49:27 +00:00
link2xt
4da0c19766 test: fixup tests/test_3_offline.py::TestOfflineAccountBasic::test_wrong_db 2024-03-03 03:28:47 +00:00
link2xt
08247a5d37 refactor: build contexts using ContextBuilder 2024-03-02 17:19:50 +00:00
link2xt
ceadd8928e api: add ContextBuilder.build() to build Context without opening 2024-03-02 17:19:50 +00:00
dependabot[bot]
c3d96814ca chore(cargo): bump walkdir from 2.4.0 to 2.5.0
Bumps [walkdir](https://github.com/BurntSushi/walkdir) from 2.4.0 to 2.5.0.
- [Commits](https://github.com/BurntSushi/walkdir/compare/2.4.0...2.5.0)

---
updated-dependencies:
- dependency-name: walkdir
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-02 17:08:47 +00:00
dependabot[bot]
c2953623b9 chore(cargo): bump serde from 1.0.196 to 1.0.197
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.196 to 1.0.197.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.196...v1.0.197)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-02 17:07:36 +00:00
dependabot[bot]
1907d1859e chore(cargo): bump anyhow from 1.0.79 to 1.0.80
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.79 to 1.0.80.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.79...1.0.80)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-02 16:00:57 +00:00
dependabot[bot]
a1970e998f chore(cargo): bump log from 0.4.20 to 0.4.21
Bumps [log](https://github.com/rust-lang/log) from 0.4.20 to 0.4.21.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.20...0.4.21)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-02 16:00:33 +00:00
dependabot[bot]
1e9baefca0 chore(cargo): bump serde_json from 1.0.113 to 1.0.114
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.113 to 1.0.114.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.113...v1.0.114)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-02 15:59:55 +00:00
iequidoo
e16322d99d test: get_protected_chat(): Use FFIEventTracker instead of dc_wait_next_msgs() (#5207)
The way it was implemented it threw out all remaining messages after finding the next incoming
message. Better use FFIEventTracker functions, they are used in all the tests anyway.
2024-03-02 12:12:26 -03:00
dependabot[bot]
ecfe3898c6 Merge pull request #5311 from deltachat/dependabot/cargo/rusqlite-0.31.0 2024-03-02 03:12:56 +00:00
link2xt
5499ca52bf refactor: get rid of ImapActionResult 2024-03-02 01:31:29 +00:00
link2xt
4e8979f7c8 refactor: merge ImapConfig into Imap 2024-03-01 21:12:21 +00:00
dependabot[bot]
417db31098 chore(cargo): bump rusqlite from 0.30.0 to 0.31.0
Bumps [rusqlite](https://github.com/rusqlite/rusqlite) from 0.30.0 to 0.31.0.
- [Release notes](https://github.com/rusqlite/rusqlite/releases)
- [Changelog](https://github.com/rusqlite/rusqlite/blob/master/Changelog.md)
- [Commits](https://github.com/rusqlite/rusqlite/compare/v0.30.0...v0.31.0)

---
updated-dependencies:
- dependency-name: rusqlite
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-01 21:11:09 +00:00
link2xt
cd9f6c3d5b ci: build c.delta.chat docs with nix 2024-03-01 20:28:20 +00:00
link2xt
07870a6d69 refactor(imap): remove Session from Imap structure
Connection establishment now happens only in one place in each IMAP loop.
Now all connection establishment happens in one place
and is limited by the ratelimit.

Backoff was removed from fake_idle
as it does not establish connections anymore.
If connection fails, fake_idle will return an error.
We then drop the connection and get back to the beginning of IMAP
loop.

Backoff may be still nice to have to delay retries
in case of constant connection failures
so we don't immediately hit ratelimit if the network is unusable
and returns immediate error on each connection attempt
(e.g. ICMP network unreachable error),
but adding backoff for connection failures is out of scope for this change.
2024-03-01 18:36:03 +00:00
link2xt
b08a4d6fcf ci: upload cffi docs without GH actions 2024-03-01 02:30:55 +00:00
link2xt
b3a82b416f ci: upload python docs without GH actions 2024-03-01 02:21:35 +00:00
link2xt
4e5d7fb821 ci: build Python docs with Nix 2024-03-01 02:00:53 +00:00
link2xt
1d73f97ef3 nix: add deltachat-time to sources 2024-03-01 01:58:11 +00:00
link2xt
f5601e7683 Merge pull request #5296 from deltachat/link2xt/imap-session
refactor: move more methods from Imap into Session
2024-02-29 02:29:52 +00:00
link2xt
0000c09ad3 fix(imap): allow maybe_network to interrupt connection ratelimit
ratelimit can be exhausted quickly if the network is not available,
i.e. if every connection attempt returns "network unreachable" error.
When the network becomes available, we want to retry connecting
as soon as maybe_network is called without waiting for ratelimiter.
2024-02-29 02:29:18 +00:00
link2xt
a83884d7e9 refactor(imap): require watch_folder for fake_idle() 2024-02-28 23:18:30 +00:00
link2xt
9e00e8627f refactor(imap): pass Session to add_all_recipients_as_contacts() 2024-02-28 22:51:07 +00:00
link2xt
85c9622675 refactor(imap): move fetch_many_msgs() into Session 2024-02-28 22:48:23 +00:00
link2xt
30432d8fa5 refactor(imap): move fetch_metadata() to Session 2024-02-28 22:42:54 +00:00
link2xt
8b9f19be70 refactor(imap): move get_all_recipients() to Session 2024-02-28 22:40:37 +00:00
link2xt
39c317e211 refator(imap): move sync_seen_flags() to Session 2024-02-28 22:39:06 +00:00
link2xt
36ab7bdf47 refactor(imap): do not get Session twice in fetch_new_messages() 2024-02-28 22:32:38 +00:00
link2xt
f8f0ca08da refactor(imap): pass Imap Session to update_recent_quota() 2024-02-28 22:30:19 +00:00
link2xt
2a0a05d03c refactor(imap): move resync_folders() to Session 2024-02-28 22:26:25 +00:00
link2xt
7bc2f0cb6b refactor(imap): move select_with_uidvalidity() to Session 2024-02-28 22:15:33 +00:00
link2xt
4355bd77a9 refactor(imap): move resync_folder_uids() to Session 2024-02-28 22:02:36 +00:00
link2xt
f0091696c2 refactor(imap): move prefetch() to Session 2024-02-28 21:56:28 +00:00
link2xt
d2e86c5852 refactor(imap): move prefetch_existing_msgs to Session 2024-02-28 21:49:44 +00:00
link2xt
d4a505b52e refactor(imap): move list_folders() to Session 2024-02-28 21:43:25 +00:00
iequidoo
08a30031eb fix: Don't send sync messages on self-{status,avatar} update from self-sent messages (#5289)
Sync messages should only be sent in response to user actions.
2024-02-26 12:28:03 -03:00
link2xt
44686d6caa ci: update to Rust 1.76 and fix clippy warnings 2024-02-25 10:43:01 +00:00
iequidoo
9862d40f89 feat: Send Chat-Group-Avatar as inline base64 (#5253)
Before group avatar was sent as an attachment. Let's do the same as with user avatar and send group
avatar as base64. Receiver code uses the same functions for user and chat avatars, so base64 avatars
are supported for most receivers already.
2024-02-24 20:24:04 -03:00
link2xt
256c8c13f1 build: unpin OpenSSL
`deltachat-rpc-server` releases are built
with Nix and LLVM/clang toolchains now
that fully support atomics.
Zig toolchain that required disabling atomics
and resulted in problems with OpenSSL 3.2 releases
is not used anymore.
2024-02-24 13:32:42 +00:00
link2xt
0b3a56c3c4 api: make store_self_keypair private
It is not useful as public API because input argument types
are not public.
Use `imex` instead.
2024-02-23 19:29:45 +00:00
iequidoo
89024bbf37 test: Fix test_verified_oneonone_chat_broken_by_device_change() (#5280)
It was broken completely and before "fix: apply Autocrypt headers if timestamp is unchanged" that
didn't show up because the message from the second Bob's device never had "Date" greater than one
from the message sent before from the first device.
2024-02-23 15:23:02 -03:00
link2xt
cf16671d8d fix(imap): set connectivity to "connecting" only after ratelimit 2024-02-22 13:08:44 +00:00
link2xt
671feb68a4 fix: do not fake idle after trigger_reconnect()
In this case connection failure
may be a connection timeout (currently 1 minute),
so it does not make sense to fake idle for another minute immediately after.

However, failure may be immediate if the port is closed
and the server refuses connection every time.
To prevent busy loop in this case
we apply ratelimit to connection attempts rather than login attempts.
This partially reverts ccec26ffa7
2024-02-22 13:08:44 +00:00
link2xt
ccd5158109 ci: upgrade setup-python GitHub Action 2024-02-22 06:18:34 +00:00
link2xt
0a18e32d62 chore(cargo): update rpgp to 0.11
<https://github.com/rpgp/rpgp/releases/tag/v0.11.0>
2024-02-22 05:08:00 +00:00
iequidoo
e9fadc0785 feat: Recognise Trash folder by name (#5275)
If a folder is named "Trash" or like this, it should be recognised as such even if it does not have
a \Trash attribute.
2024-02-20 18:29:04 -03:00
link2xt
cfa13f0669 build: tag armv6 wheels with tags accepted by PyPI
See
<https://github.com/pypi/warehouse/blob/main/warehouse/forklift/legacy.py>
for the tag checking code.

linux_armv6l and linux_armv7l are accepted,
but {many,musl}linux_*_armv6l are not.
2024-02-20 18:10:52 +00:00
link2xt
89e43c6678 chore(release): prepare for 1.135.1 2024-02-20 17:21:21 +00:00
link2xt
8a67797cb1 build: add footer template for git-cliff 2024-02-20 17:05:36 +00:00
link2xt
b29bc19ef4 ci: try to upload deltachat-rpc-server only on release 2024-02-20 15:59:53 +00:00
link2xt
e765066f05 ci: build deltachat-rpc-server with nix 2024-02-20 15:59:53 +00:00
link2xt
67aa785a9e ci: build deltachat-repl for Windows with nix 2024-02-20 15:59:53 +00:00
link2xt
c88c26426d build: add flake.nix 2024-02-20 15:59:53 +00:00
link2xt
b4e9a9764f fix: apply Autocrypt headers if timestamp is unchanged
If two messages arrive with the same timestamp,
the one that arrived later should be preferred.
2024-02-20 12:46:41 +00:00
gerryfrancis
06e79e8926 Correct typo in imap.rs 2024-02-20 12:46:30 +00:00
link2xt
9427f7b587 fix: never encrypt {vc,vg}-request
Even if 1:1 chat with alice is protected,
we should send vc-request unencrypted.
This happens if Alice changed the key
and QR-code Bob scans contains fingerprint
that is different from the verified fingerprint.
Sending vc-request encrypted to the old key
does not help because Alice is not able
to decrypt it in this case.
2024-02-19 15:32:50 +00:00
iequidoo
bce22edfe3 feat: Sync Config::Selfstatus across devices (#4893)
Use sync messages for that as it is done for e.g. Config::Displayname. Maybe we need to remove
self-status synchronisation via usual messages then, but let's think of it a bit.
2024-02-19 12:18:13 -03:00
iequidoo
656d4ed506 feat: Sync self-avatar across devices (#4893)
Use sync messages for that as it is done for e.g. Config::Displayname. Maybe we need to remove
avatar synchronisation via usual messages then, but let's think of it a bit.
2024-02-19 12:18:13 -03:00
iequidoo
5e3fcafb3a fix: Context::get_info: Report displayname as "displayname" (w/o underscore)
Otherwise the user thinks that the config key is "display_name" and can't change it using `set
display_name` command.
2024-02-19 12:18:13 -03:00
link2xt
660cfd4f01 refactor: rename incorrectly named variables in create_keypair
Encryption subkey is incorrectly referred to as public key
in variable names.
This is incorrect because generated encryption key
is secret too just as the signing primary key.
Generated OpenPGP secret key consists of primary signing key
and encryption subkey.
Then OpenPGP public key consisting of
the primary signing public key
and encryption public key is generated.
Keypair consists of the secret OpenPGP key and public OpenPGP key,
each of them has a primary key and subkey inside.
2024-02-17 17:50:33 +00:00
link2xt
7a1270f861 refactor: return error with a cause when failing to export keys 2024-02-17 17:50:33 +00:00
link2xt
b35b893351 refactor(create_keypair): remove unnecessary map_err 2024-02-17 17:50:33 +00:00
link2xt
f45f9263db ci: fixup node-package.yml after artifact actions upgrade 2024-02-17 13:49:40 +00:00
link2xt
8289dc92ed ci: update to actions/checkout@v4
Also disable --progress.
It is not disabled by default for backward compatibility,
but solves the problem of lots of progress lines
in the downloadable raw output:
https://github.com/actions/checkout/pull/1067
2024-02-17 13:49:01 +00:00
link2xt
862107c708 feat: remove webxdc sending limit
The limit is better enforced by webxdc distributors,
e.g. xdc store bots or actually email providers
to allow for experimentation with large frameworks
or porting existing apps and testing them
before reducing their size.

Besides that, the comment on WEBXDC_SENDING_LIMIT was outdated,
it was not updated when the limit was increased to 640 kB.
2024-02-17 00:09:21 +00:00
iequidoo
778660a8c9 test: Add a test on protection message sort timestamp (#5088)
Even if `vc-request-with-auth` is received with a delay, the protection message must have the sort
timestamp equal to the Sent timestamp of `vc-request-with-auth`, otherwise subsequent chat messages
would also have greater sort timestamps and while it doesn't affect the chat itself (because Sent
timestamps are shown to a user), it affects the chat position in the chatlist because chats there
are sorted by sort timestamps of the last messages, so the user sees chats sorted out of
order. That's what happened in #5088 where a user restores the backup made before setting up a
verified chat with their contact and fetches new messages, including `vc-request-with-auth` and also
messages from other chats, after that.
2024-02-15 14:24:46 -03:00
iequidoo
6e55f0c6e3 feat: Mock SystemTime::now() for the tests
Add a new crate `deltachat_time` with a fake `struct SystemTimeTools` for mocking
`SystemTime::now()` for test purposes. One still needs to use `std::time::SystemTime` as a struct
representing a system time. I think such a minimalistic approach is ok -- even if somebody uses the
original `SystemTime::now()` instead of the mock by mistake, that could break only tests but not the
program itself. The worst thing that can happen is that tests using `SystemTime::shift()` and
checking messages timestamps f.e. wouldn't catch the corresponding bugs, but now we don't have such
tests at all which is much worse.
2024-02-15 14:24:46 -03:00
link2xt
3b0e740c17 ci: replace download-artifact v1 with v4 2024-02-15 11:31:58 +00:00
link2xt
2dd87b6b5e ci: use actions/download-artifact@v4
download-artifact@v3 does not download artifacts uploaded with upload-artifact@v4
2024-02-15 04:32:14 +00:00
link2xt
cdcacf2f83 ci: update actions/upload-artifact
There are no breaking changes listed in the README that affect our usage.
2024-02-15 02:41:49 +00:00
bjoern
51aaaf2e8d cleanup changelog (#5265)
somehow a whole issue sneaked in :)
2024-02-14 18:02:16 +01:00
bjoern
e6438f9981 chore(release): prepare for 1.135.0 (#5264)
after merging:

6. Tag the release: `git tag -a v1.135.0`.

7. Push the release tag: `git push origin v1.135.0`.

8. Create a GitHub release: `gh release create v1.135.0 -n ''`.

---------

Co-authored-by: link2xt <link2xt@testrun.org>
2024-02-14 11:04:02 +01:00
bjoern
9135cffaa4 do not change db schema in an incompatible way (#5254)
PR #5099 removed some columns in the database that were actually in use.

usually, to not worsen UX unnecessarily
(releases take time - in between, "Add Second Device", "Backup" etc.
would fail), we try to avoid such schema changes (checking for
db-version would avoid import etc. but would still worse UX),
see discussion at #2294.

these are the errors, the user will be confronted with otherwise:

<img width=400
src=https://github.com/deltachat/deltachat-core-rust/assets/9800740/e3f0fd6e-a7a9-43f6-9023-0ae003985425>

it is not great to maintain the old columns, but well :)

as no official releases with newer cores are rolled out yet, i think, it
is fine to change the "107" migration
and not copy things a second time in a newer migration.

(this issue happens to me during testing, and is probably also the issue
reported by @lk108 for ubuntu-touch)
2024-02-13 23:00:47 +01:00
link2xt
73492ca4bc chore(scripts): do not install deltachat-rpc-client twice 2024-02-13 11:20:11 +00:00
iequidoo
fe3c1f69c3 feat: Cache system time instead of looking at the clock several times in a row
The system clock may be adjusted and even go back, so caching system time in code sections where
it's not supposed to change may even protect from races/bugs.
2024-02-12 21:13:36 -03:00
iequidoo
31ee3feb57 fix: Use SystemTime instead of Instant everywhere
If a time value doesn't need to be sent to another host, saved to the db or otherwise used across
program restarts, a monotonically nondecreasing clock (`Instant`) should be used. But as `Instant`
may use `libc::clock_gettime(CLOCK_MONOTONIC)`, e.g. on Android, and does not advance while being in
deep sleep mode, get rid of `Instant` in favor of using `SystemTime`, but add `tools::Time` as an
alias for it with the appropriate comment so that it's clear why `Instant` isn't used in those
places and to protect from unwanted usages of `Instant` in the future. Also this can help to switch
to another clock impl if we find any.
2024-02-12 21:13:36 -03:00
link2xt
f4ed63c54c chore: cargo update 2024-02-12 23:16:39 +00:00
link2xt
8f88cdd826 chore(cargo): update to strum 0.26 2024-02-12 18:54:14 +00:00
iequidoo
9933a4268f fix: Create mvbox on setting mvbox_move 2024-02-12 15:41:11 -03:00
iequidoo
8a54c228fd feat: server_sent_unsolicited_exists(): Log folder name 2024-02-12 15:41:11 -03:00
iequidoo
b5f2c747e0 feat: Context::set_config(): Restart IO scheduler if needed (#5111)
Restart the IO scheduler if needed to make the new config value effective (for `MvboxMove,
OnlyFetchMvbox, SentboxWatch` currently). Also add `set_config_internal()` which doesn't affect
running the IO scheduler. The reason is that `Scheduler::start()` itself calls `set_config()`,
although not for the mentioned keys, but still, and also Rust complains about recursive async calls.
2024-02-12 15:41:11 -03:00
iequidoo
ba35e83db2 feat: Add device message about outgoing undecryptable messages (#5164)
Currently when a user sets up another device by logging in, a new key is created. If a message is
sent from either device outside, it cannot be decrypted by the other device.

The message is replaced with square bracket error like this:
```
<string name="systemmsg_cannot_decrypt">This message cannot be decrypted.\n\n• It might already help to simply reply to this message and ask the sender to send the message again.\n\n• If you just re-installed Delta Chat then it is best if you re-setup Delta Chat now and choose "Add as second device" or import a backup.</string>
```
(taken from Android repo `res/values/strings.xml`)

If the message is outgoing, it does not help to "simply reply to this message". Instead, we should
add a translatable device message of a special type so UI can link to the FAQ entry about second
device. But let's limit such notifications to 1 per day. And as for the undecryptable message
itself, let it go to Trash if it can't be assigned to a chat by its references.
2024-02-11 23:22:52 -03:00
dependabot[bot]
61a2c551fc chore(cargo): bump toml from 0.8.8 to 0.8.10
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.8 to 0.8.10.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.8...toml-v0.8.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-11 03:48:58 +00:00
dependabot[bot]
20c91ba2fa chore(cargo): bump serde from 1.0.194 to 1.0.196
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.194 to 1.0.196.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.194...v1.0.196)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 21:39:05 +00:00
dependabot[bot]
969f8b916b chore(cargo): bump syn from 2.0.46 to 2.0.48
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.46 to 2.0.48.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.46...2.0.48)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 21:38:55 +00:00
dependabot[bot]
b7b7a7e95d chore(cargo): bump serde_json from 1.0.111 to 1.0.113
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.111 to 1.0.113.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.111...v1.0.113)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 18:50:12 +00:00
dependabot[bot]
455b108a6c chore(cargo): bump fast-socks5 from 0.9.2 to 0.9.5
Bumps [fast-socks5](https://github.com/dizda/fast-socks5) from 0.9.2 to 0.9.5.
- [Release notes](https://github.com/dizda/fast-socks5/releases)
- [Commits](https://github.com/dizda/fast-socks5/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 18:50:01 +00:00
dependabot[bot]
645ca7741b chore(cargo): bump uuid from 1.6.1 to 1.7.0
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.6.1...1.7.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 06:13:42 +00:00
dependabot[bot]
36643c551d chore(cargo): bump axum from 0.7.3 to 0.7.4
Bumps [axum](https://github.com/tokio-rs/axum) from 0.7.3 to 0.7.4.
- [Release notes](https://github.com/tokio-rs/axum/releases)
- [Changelog](https://github.com/tokio-rs/axum/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/axum/compare/axum-v0.7.3...axum-v0.7.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 04:36:49 +00:00
link2xt
0fcdee8857 refactor: resultify token::exists 2024-02-09 04:36:32 +00:00
dependabot[bot]
26ae686687 chore(cargo): bump reqwest from 0.11.23 to 0.11.24
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.23 to 0.11.24.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.23...v0.11.24)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 04:02:42 +00:00
iequidoo
b94bd9a659 fix: Emit ConfigSynced event on the second device
Before it was emitted only on the source device and `test_sync()` didn't catch the bug because of
the interference of the previous call to `set_config_bool()`. Now there's a separate test that does
its job well.
2024-02-09 00:41:01 -03:00
iequidoo
f15e7d43e3 fix: ImapSession::select_or_create_folder(): Don't fail if folder is created in parallel 2024-02-08 23:39:09 -03:00
iequidoo
05c256dd5b fix: Imap::configure_mvbox: Do select_with_uidvalidity() before return
Before moving emails to the mvbox we need to remember its UIDVALIDITY, otherwise emails moved before
that wouldn't be fetched but considered "old" instead.

Also:
- Don't use `session.create()` to create mvbox as `select_with_uidvalidity()` already creates mvbox
  on its own.
- Don't try to create compat folders like "INBOX.DeltaChat", but only look for them.
2024-02-08 23:39:09 -03:00
dependabot[bot]
37295f6967 chore(cargo): bump libc from 0.2.151 to 0.2.153
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.151 to 0.2.153.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.151...0.2.153)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 02:34:41 +00:00
dependabot[bot]
dfdbb91f0a chore(cargo): bump regex from 1.10.2 to 1.10.3
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.2 to 1.10.3.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.10.2...1.10.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 02:32:50 +00:00
dependabot[bot]
72f93dca7a chore(cargo): bump base64 from 0.21.5 to 0.21.7
Bumps [base64](https://github.com/marshallpierce/rust-base64) from 0.21.5 to 0.21.7.
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.21.5...v0.21.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 02:31:48 +00:00
dependabot[bot]
ec2cf31cfa chore(cargo): bump smallvec from 1.11.2 to 1.13.1
Bumps [smallvec](https://github.com/servo/rust-smallvec) from 1.11.2 to 1.13.1.
- [Release notes](https://github.com/servo/rust-smallvec/releases)
- [Commits](https://github.com/servo/rust-smallvec/compare/v1.11.2...v1.13.1)

---
updated-dependencies:
- dependency-name: smallvec
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-09 02:31:24 +00:00
iequidoo
ecd4d2afe0 test: delete_server_after="1" should cause immediate message deletion (#5201) 2024-02-08 12:18:11 -03:00
Hocuri
ec9d104cf3 Basic self-reporting, core part (#5129)
Part of https://github.com/deltachat/deltachat-android/issues/2909

For now, this is only sending a few basic metrics.
2024-02-07 20:23:11 +01:00
Hocuri
11214c7d1f fix: Never allow a message timestamp to be a lot in the future (#5249)
We must finish what was started in
https://github.com/deltachat/deltachat-core-rust/issues/5088.
2024-02-07 20:17:46 +01:00
iequidoo
fba27ff884 fix: Guarantee immediate message deletion if delete_server_after == 0 (#5201) 2024-02-07 11:43:11 -03:00
link2xt
f8907e3c83 feat: add wildcard pattern support to provider database 2024-02-06 21:02:28 +00:00
Hocuri
f1688d2b3f fix: Mark the gossip keys from the message as verified, not the ones from the db (#5247) 2024-02-06 10:22:38 +01:00
iequidoo
693045b542 chore(cargo): bump iana-time-zone from yanked 0.1.59 to 0.1.60 2024-02-04 10:42:13 +01:00
dependabot[bot]
14dfb9abec Merge pull request #5235 from deltachat/dependabot/cargo/image-0.24.8 2024-02-03 17:09:30 +00:00
dependabot[bot]
c8ed3ed73b Merge pull request #5236 from deltachat/dependabot/cargo/chrono-0.4.33 2024-02-03 17:04:12 +00:00
dependabot[bot]
bce5203eeb Merge pull request #5241 from deltachat/dependabot/cargo/futures-lite-2.2.0 2024-02-03 16:53:21 +00:00
dependabot[bot]
74c0c2cc38 Merge pull request #5243 from deltachat/dependabot/cargo/pin-project-1.1.4 2024-02-03 16:51:59 +00:00
iequidoo
4f25072352 fix: dehtml: Don't just truncate text when trying to decode (#5223)
If `escaper::decode_html_buf_sloppy()` just truncates the text (which happens when it fails to
html-decode it at some position), then it's probably not HTML at all and should be left as
is. That's what happens with hyperlinks f.e. and there was even a test on this wrong behaviour which
is fixed now. So, now hyperlinks are not truncated in messages and should work as expected.
2024-02-02 14:55:52 -03:00
B. Petersen
91c3a39134 update welcome image, thanks @paulaluap 2024-02-01 23:16:47 +01:00
dependabot[bot]
ae94b2a7b3 chore(cargo): bump pin-project from 1.1.3 to 1.1.4
Bumps [pin-project](https://github.com/taiki-e/pin-project) from 1.1.3 to 1.1.4.
- [Release notes](https://github.com/taiki-e/pin-project/releases)
- [Changelog](https://github.com/taiki-e/pin-project/blob/main/CHANGELOG.md)
- [Commits](https://github.com/taiki-e/pin-project/compare/v1.1.3...v1.1.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 21:27:59 +00:00
dependabot[bot]
3b013a1017 chore(cargo): bump futures-lite from 2.1.0 to 2.2.0
Bumps [futures-lite](https://github.com/smol-rs/futures-lite) from 2.1.0 to 2.2.0.
- [Release notes](https://github.com/smol-rs/futures-lite/releases)
- [Changelog](https://github.com/smol-rs/futures-lite/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/futures-lite/compare/v2.1.0...v2.2.0)

---
updated-dependencies:
- dependency-name: futures-lite
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 21:27:11 +00:00
dependabot[bot]
80aab220b6 chore(cargo): bump chrono from 0.4.31 to 0.4.33
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.31 to 0.4.33.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.31...v0.4.33)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 21:25:01 +00:00
dependabot[bot]
34c3e44b9d chore(cargo): bump image from 0.24.7 to 0.24.8
Bumps [image](https://github.com/image-rs/image) from 0.24.7 to 0.24.8.
- [Changelog](https://github.com/image-rs/image/blob/master/CHANGES.md)
- [Commits](https://github.com/image-rs/image/compare/v0.24.7...v0.24.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-01 21:24:34 +00:00
B. Petersen
78d304443a chore(release): prepare for 1.134.0 2024-01-31 14:52:40 +00:00
link2xt
d6c24eb9f6 Make Accounts::background_fetch() not return Result 2024-01-31 14:04:03 +01:00
link2xt
f7fd1ef2bf Emit DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE even on timeout
Otherwise if there is a timeout,
UI will wait for DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE forever.
2024-01-31 14:04:03 +01:00
link2xt
af7bf5bd2b s/forgeting/forgetting/ 2024-01-31 14:04:03 +01:00
link2xt
ea666f1098 Hide background_fetch_without_timeout from public API 2024-01-31 14:04:03 +01:00
Simon Laux
5bb80f94c7 Apply suggestions from code review
Co-authored-by: bjoern <r10s@b44t.com>
2024-01-31 14:04:03 +01:00
link2xt
2f29c56a36 fix: do not log error if watched folder is not configured
This may happen if Sent folder does not exist
but configuration option to watch it is enabled.
2024-01-31 14:04:03 +01:00
Simon Laux
de86b8a96e rename event also in core 2024-01-31 14:04:03 +01:00
Simon Laux
060c9c8aa1 cargo fmt 2024-01-31 14:04:03 +01:00
Simon Laux
727428a965 rename event and mention event in method documentation 2024-01-31 14:04:03 +01:00
Simon Laux
df455bbcf5 BackgroundFetchCompletedForAllAccounts event 2024-01-31 14:04:03 +01:00
Simon Laux
946eea4c9e add rate limit for quota check in background fetch (12h for now) 2024-01-31 14:04:03 +01:00
Simon Laux
5cbc87369e rename cffi function 2024-01-31 14:04:03 +01:00
Simon Laux
5cdd5e0564 Apply suggestions from code review
Co-authored-by: link2xt <link2xt@testrun.org>
2024-01-31 14:04:03 +01:00
Simon Laux
f493d6bb40 don't hold write lock in cffi (this blocked events) 2024-01-31 14:04:03 +01:00
Simon Laux
8e073b9c3e log time that the function took 2024-01-31 14:04:03 +01:00
Simon Laux
ea2a692d18 fix iOS build issue 2024-01-31 14:04:03 +01:00
Simon Laux
1b7c5be9c5 Test server has no sentbox folder 2024-01-31 14:04:03 +01:00
Simon Laux
f7903df805 api: cffi: add dc_accounts_background_fetch_with_timeout 2024-01-31 14:04:03 +01:00
Simon Laux
d2c61dc90e api: jsonrpc: add background_fetch_for_all_accounts 2024-01-31 14:04:03 +01:00
Simon Laux
7b68098785 feat: add background fetch method 2024-01-31 14:04:03 +01:00
Simon Laux
48f2ea717e refactor: move convert folder meaning logic in own method
also unify the error handling for the cases where it can go wrong.
2024-01-31 14:04:03 +01:00
link2xt
cb3f03fd39 feat: add support for IMAP METADATA 2024-01-31 04:16:04 +00:00
iequidoo
06f1fe18d6 fix: Delete resent messages on receiver side (#5155)
If a Delta Chat message has the Message-ID already existing in the db, but a greater "Date", it's a
resent message that can be deleted. Messages having the same "Date" mustn't be deleted because they
can be already seen messages moved back to INBOX. Also don't delete messages having lesser "Date" to
avoid deleting both messages in a multi-device setting.
2024-01-30 22:01:45 -03:00
iequidoo
1dbf924c6a feat: chat::resend_msgs: Guarantee strictly increasing time in the Date header
Use `create_smeared_timestamp()` for this. This allows to dedup messages on the receiver -- if it
sees the same Message-ID, but a different timestamp, then it's a resent message that can be deleted.
2024-01-30 22:01:45 -03:00
link2xt
3f6814f421 chore: remove unnecessary exception from deny.toml 2024-01-30 20:27:07 +00:00
link2xt
782828ac4f chore(deps): update imap-proto 2024-01-30 20:12:07 +00:00
iequidoo
bd3759d55e test: test_import_export_online_all: Send the message to the existing address to avoid errors (#5220) 2024-01-29 20:34:03 -03:00
iequidoo
672993e69e feat: qr::check_qr(): Accept i.delta.chat invite links (#5217)
Accepts invite links with `https://i.delta.chat[/]#FINGERPRINT&a=ADDR[&OPTIONAL_PARAMS]`
scheme. Only `i.delta.chat` domain is supported now not to intersect with the common HTTPS scheme.
2024-01-28 20:33:56 -03:00
iequidoo
987bdaf237 ci: Add/remove necessary newlines to fix Python lint 2024-01-26 14:46:46 -03:00
iequidoo
7cf382a3b8 fix: Treat only "Auto-Submitted: auto-generated" messages as bot-sent (#5213)
"Auto-Submitted: auto-replied" messages mustn't be considered as sent by either bots or non-bots,
e.g. MDNs have this header value and it's the same for bots and non-bots.
2024-01-26 13:03:19 -03:00
Simon Laux
19dce9ddfa api!: jsonrpc: device message api now requires Option<MessageData> instead of String for the message (#5211)
api: jsonrpc: device message api now setting empty device messages to
block adding a message for a specific label

required for https://github.com/deltachat/deltachat-desktop/pull/3639

---------

Co-authored-by: iequidoo <117991069+iequidoo@users.noreply.github.com>
2024-01-25 11:26:11 +01:00
Simon Laux
0afc0dd65a fix: add tolerance for macOS and iOS changing # to %23
fixes #1969

Bug description:
macOS and iOS sometimes replace the # with %23 (uri encode it), we should be able to parse this wrong format too, see issue https://github.com/deltachat/deltachat-core-rust/issues/1969 for more info
2024-01-24 23:58:49 +01:00
link2xt
73d612a07d feat: do not drop unknown report attachments
In particular TLSRPT reports
contain files that may be interesting for admins.
Currently Delta Chat drops the attachment
so message appears as a text message without actual payload.
2024-01-24 16:13:20 +00:00
link2xt
3b1529ef81 chore(release): prepare for 1.133.2 2024-01-24 01:57:45 +00:00
link2xt
15187c0adb fix: downgrade OpenSSL from 3.2.0 to 3.1.4 2024-01-24 01:48:23 +00:00
Sebastian Klähn
c5f31c3d03 fix: No new chats for MDNs with alias (#5196) (#5199)
close #5196
2024-01-22 16:51:37 +01:00
link2xt
2c17e78347 chore(release): prepare for 1.133.1 2024-01-21 04:18:13 +00:00
Sebastian Klähn
4ee646ce0b feat(api): Add is_bot to cffi and jsonrpc (#5197)
@adbenitez wants this feature on Deltalab to display a bot tag. 
Other UIs might also want to adopt this feature :)

---------
Co-authored-by: link2xt <link2xt@testrun.org>
2024-01-20 15:00:10 +00:00
B. Petersen
1f7b4a74fa add missing 'unencrypted message' defines
in #5161, it was forgotten to adapt deltachat.h;
moreover, this PR tweaks some other minor things
2024-01-20 15:00:23 +01:00
Sebastian Klähn
4bc90701cc feat: Add system message when provider does not allow unencrypted messages (#5161) (#5195)
close #5161

![Screenshot from 2024-01-19
19-56-09](https://github.com/deltachat/deltachat-core-rust/assets/39526136/27ecdd9b-1739-410b-bb26-80d5bdbbc39a)

---------

Co-authored-by: bjoern <r10s@b44t.com>
2024-01-20 11:47:23 +00:00
dependabot[bot]
490deb9347 chore(deps): bump h2 from 0.3.17 to 0.3.24 in /fuzz
Bumps [h2](https://github.com/hyperium/h2) from 0.3.17 to 0.3.24.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/v0.3.24/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.17...v0.3.24)

---
updated-dependencies:
- dependency-name: h2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-19 23:58:52 +00:00
Sebastian Klähn
28d9484a13 fix(node): run tests with native ESM modules instead of esm (#5194)
close #5156

---------

Co-authored-by: Septias <scoreplayer2000@gmail.comclear>
2024-01-19 18:09:19 +01:00
link2xt
e67e684ee0 test: wait for joiner success in test_verified_group_[member_added]_recovery
If we wait for inviter success,
vg-member-added message may be still in flight
and reach ac2 after device resetup.

Making ac2 wait for joining the group ensures that old
device receives vg-member-added message
and new device will not receive it and fail to decrypt.

Other instances of wait_for_securejoin_inviter_success()
in the same tests are also replaced for reliability.
2024-01-18 17:00:21 +00:00
link2xt
6cfe3e6a97 chore(deps): update h2 0.4.0 -> 0.4.2 2024-01-18 13:40:38 +00:00
Sebastian Klähn
99ac524905 chore(deps): update h2 from 0.3.22 -> 0.3.24 2024-01-18 14:21:58 +01:00
link2xt
2faf7fdb78 fix: BCC-to-self even if server deletion is set to "at once" 2024-01-18 10:20:01 +00:00
link2xt
6a8ea8a083 fix: set message download state to Failure on IMAP errors
Previously the message was removed from `download` table,
but message bubble was stuck in InProgress state.

Now download state is updated by the caller,
so it cannot be accidentally skipped.
2024-01-18 10:18:57 +00:00
link2xt
e0e56cd831 chore: update quoted_printable to 0.5
And update mailparse to 0.14.1 so there is no duplicate dependency.
2024-01-18 09:35:05 +00:00
missytake
bbc6febb72 test: no timeout in SetupPlugin 2024-01-17 14:20:29 +01:00
missytake
7f7f42d721 test: Ensure that member is added before yielding chat 2024-01-17 14:20:29 +01:00
iequidoo
589236c27b fix: chat::send_msg: Remove encryption-related params from already sent message
This allows to send existing messages (incoming and outgoing) taken from encrypted chats, to
unencrypted ones. `Param::ForcePlaintext` is removed as well -- if a message can be sent encrypted
this time, nothing bad with this.
2024-01-17 14:20:29 +01:00
iequidoo
c16c5e0802 test: Bring test_forward_encrypted_to_unencrypted into line with current API
Currently `Chat.send_msg()` modifies the source message and returns another message object
equivalent to the source one. That's how it works in the core and thus in Python bindings too.
2024-01-17 14:20:29 +01:00
missytake
36cab40ac1 test: add get_protected_chat to testplugin.py 2024-01-17 14:20:29 +01:00
missytake
4186d78305 test: add python test for message forwarding from encrypted to unencrypted chat 2024-01-17 14:20:29 +01:00
iequidoo
06cccb77f8 feat: Use Quoted-Printable for the text part (#3986)
This is needed to protect from ESPs (such as gmx.at) doing their own Quoted-Printable encoding and
thus breaking messages and signatures. It's unlikely that the reader uses a MUA not supporting
Quoted-Printable encoding. And RFC 2646 "4.6" also recommends it for encrypted messages.
2024-01-16 23:46:24 -03:00
link2xt
1895f4c556 chore(release): prepare for 1.133.0 2024-01-15 22:55:26 +00:00
link2xt
849a873e61 feat: only try to configure non-strict TLS checks if explicitly set
Trying non-strict TLS checks is not necessary
for most servers with proper TLS setup,
but doubles the time needed to fail configuration
when the server is not responding, e.g.
when all connection attempts time out.

There is also a risk of accidentally
configuring non-strict TLS checks in a rare case
that strict TLS check configuration spuriously failed,
e.g. on a bad network.

If the server has a known broken TLS setup,
it can still be added to the provider database
or configured with non-strict TLS check manually.
User can also configure another email provider,
such as chatmail servers, instead of using the server
with invalid TLS hostname.

This change does not affect exising setups.
2024-01-15 22:54:31 +00:00
link2xt
b5c0372c99 docs: restore "Constants" page in Doxygen >=1.9.8
deltachat.h uses `@defgroup` commands to create topics
for groups of constants. Prior to Doxygen 1.9.8
defining a group created a "module"
and all constants were visible from the modules.html page.
In Doxygen 1.9.8 "modules" were renamed into "topics"
as C++20 modules have taken their place,
so Delta Chat documentation does not have modules
in Doxygen sense anymore.

The change is to replace "modules.html" with "topics.html"
in the DoxygenLayout.xml.

See <https://www.doxygen.nl/manual/grouping.html> for
Doxygen documentation about groups and their relation to topics.
2024-01-14 12:17:54 +00:00
link2xt
1ba9b69849 chore: npm run build:core:constants 2024-01-13 22:51:24 +00:00
holger krekel
6345a4f5b3 fix link for securejoin 2024-01-13 12:50:16 +01:00
Sebastian Klähn
382fc75b1e Add more docs (#5174)
Add some docs to smtp functions
2024-01-12 11:14:05 +01:00
Sebastian Klähn
92fc9ea971 feat: Encrypt MDNs #5168 (#5175)
This PR stops MDNs from being forced to be sent unencrypted. 
If no encryption is possible (by `should_encrypt`), the fix #5152 still
applies.

close #5168
2024-01-12 10:54:54 +01:00
Sebastian Klähn
de7ac2a240 fix: emit events more reliable when starting and stopping io #5097 (#5101)
Send `EventType::ConnectivityChanged` when using the context methods
`start_io` and `stop_io`.

close #5097

---------

Co-authored-by: Septias <scoreplayer2000@gmail.comclear>
2024-01-12 09:45:34 +01:00
link2xt
7b0e5adaee chore(deps): update rustyline from 12 to 13 2024-01-12 02:45:53 +00:00
iequidoo
406b59501b chore: deltachat-jsonrpc/src/api/types/events.rs: Apply rustfmt 2024-01-11 21:53:26 -03:00
iequidoo
d5da2bed75 feat: Add ConfigSynced event
Add an event for a case if a multi-device synced config value changed. Maybe the app needs to
refresh smth on such an event. For uniformity it is emitted on the source device too. The value is
omitted, otherwise it would be logged which might not be good for privacy.
2024-01-11 21:53:26 -03:00
iequidoo
924d5b9377 feat: Sync contact creation/rename across devices (#5163)
Use `chat::SyncAction::Rename` for that. Anyway 1:1-s can't be renamed and a separate sync action
would only complicate the code.
2024-01-10 16:46:54 -03:00
iequidoo
bb47299ee4 fix: contact::set_blocked(): Don't fail on sync errors, just log them
Multi-device synchronisation is not critical and should not fail the local operation, in other
places sync errors are already ignored.
2024-01-10 16:46:54 -03:00
link2xt
20065d3daa docs: add a NOTE comment about KeyId backward verification race 2024-01-09 21:46:37 +00:00
link2xt
ccb267beab refactor: rename notify_peer_verified() into set_peer_verified()
It was named notify_peer_verified()
because it added info message,
but this is no longer true since
https://github.com/deltachat/deltachat-core-rust/pull/4998
(commit c6ea4e389a)
is merged.
2024-01-09 21:46:37 +00:00
link2xt
32bcb59601 refactor: do not emit ChatModified event in notify_peer_verified()
The chat is not modified at least since
c6ea4e389a
(PR https://github.com/deltachat/deltachat-core-rust/pull/4998),
even the info message is not posted there.
2024-01-09 21:46:37 +00:00
link2xt
c708c44f0a feat: mark 1:1 chat as verified for Bob early
Mark 1:1 chat as verified as soon as Alice is forward-verified
so Bob can already start sending Chat-Verified headers.
This way Alice and Bob can scan each other's QR codes
and even if all Secure-Join headers are dropped from the network,
still get forward verifications via QR-code scans
and backward verifications via Chat-Verified messages in 1:1 chat.
2024-01-09 21:46:37 +00:00
link2xt
9415a71f9d refactor: rename fingerprint_equals_sender to verify_sender_by_fingerprint 2024-01-09 21:46:37 +00:00
link2xt
1fd42f2c53 test: test recovery from lost vc-contact-confirm 2024-01-09 21:46:37 +00:00
link2xt
1e52502ab3 refactor: send Secure-Join-Fingerprint only in *-request-with-auth 2024-01-09 21:46:37 +00:00
link2xt
a144d7e4f3 test: test that changing default private key breaks backward verification 2024-01-09 21:46:37 +00:00
link2xt
e855b79f9c feat: add backward_verified_key_id column to acpeerstates 2024-01-09 21:46:37 +00:00
link2xt
2f8a8f9f50 ci: update to Rust 1.75.0 and fix clippy 2024-01-08 20:01:40 +00:00
link2xt
b9a58bf625 docs: add a link to autoconfig RFC draft
This will hopefully replace deleted Mozilla documentation page
in the future.
2024-01-07 22:55:16 +00:00
iequidoo
c8075e53d2 fix: Reset message error when scheduling resending (#5119)
Before, while a message is in OutPending state after resending is requested, the user still sees the
red marker with error and it is confusing, so the user don't know the sending state of the message.
2024-01-07 15:02:31 -03:00
iequidoo
ff54cf24a1 fix: message::update_msg_state(): Reset error if message is delivered (#5119) 2024-01-05 20:43:55 -03:00
link2xt
af0833e821 ci: downgrade chai from 4.4.0 to 4.3.10
4.4.0 fails with a syntax error in CI currently.
2024-01-05 23:36:57 +00:00
link2xt
da11542322 fix: do not remove contents from Schleuder ML messages
Before this fix actual contents of the message
reposted by Schleuder is considered a mailing list footer and removed,
not visible even in the "Show Full Message..." view.

With this change there will be two message bubbles,
one for header and one for the contents,
but it is still better than losing the contents completely.

Attempting to parse header part is out of scope for this change.
2024-01-05 15:42:56 +00:00
link2xt
3bcdd1770a test: test that read receipts don't degrade encryption
This is broken since 44227d7b86
mimeparser only recognizes read receipts
by the Content-Type being "multipart/report".
If multipart/report is hidden inside multipart/mixed
and the message is not encrypted,
it degrades encryption.
2024-01-05 15:34:48 +00:00
link2xt
4dc596e646 fix(mimefactory): do not wrap MDNs into multipart/mixed part 2024-01-05 15:34:48 +00:00
link2xt
2e69210825 refactor: use wait_for_incoming_msg_event() more 2024-01-05 15:34:48 +00:00
iequidoo
625887d249 fix: Split SMTP jobs already in chat::create_send_msg_jobs() (#5115)
a27e84ad89 "fix: Delete received outgoing messages from SMTP queue"
can break sending messages sent as several SMTP messages because they have a lot of recipients:
`pub(crate) const DEFAULT_MAX_SMTP_RCPT_TO: usize = 50;`

We should not cancel sending if it is such a message and we received BCC-self because it does not
mean the other part was sent successfully. For this, split such messages into separate jobs in the
`smtp` table so that only a job containing BCC-self is canceled from `receive_imf_inner()`. Although
this doesn't solve the initial problem with timed-out SMTP requests for such messages completely,
this enables fine-grained SMTP retries so we don't need to resend all SMTP messages if only some of
them failed to be sent.
2024-01-05 01:53:41 -03:00
link2xt
b7c34b7794 chore: remove minor version from serde_json spec 2024-01-04 16:06:33 +00:00
link2xt
941cf38a3e chore(deps): cargo update 2024-01-04 16:03:28 +00:00
dependabot[bot]
7f61896ec8 Merge pull request #5136 from deltachat/dependabot/cargo/futures-0.3.30 2024-01-03 17:19:18 +00:00
dependabot[bot]
b14b49cbf0 Merge pull request #5146 from deltachat/dependabot/cargo/anyhow-1.0.79 2024-01-03 17:17:21 +00:00
dependabot[bot]
6de3510a5d Merge pull request #5131 from deltachat/dependabot/cargo/reqwest-0.11.23 2024-01-03 04:53:00 +00:00
dependabot[bot]
dea519095c chore(cargo): bump futures from 0.3.29 to 0.3.30
Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.29 to 0.3.30.
- [Release notes](https://github.com/rust-lang/futures-rs/releases)
- [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.29...0.3.30)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-03 04:48:48 +00:00
dependabot[bot]
3f8ca0cee9 Merge pull request #5133 from deltachat/dependabot/cargo/tempfile-3.9.0 2024-01-03 04:47:42 +00:00
dependabot[bot]
1b998da57a chore(cargo): bump anyhow from 1.0.75 to 1.0.79
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.75 to 1.0.79.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.75...1.0.79)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-03 04:46:17 +00:00
dependabot[bot]
772747d42d Merge pull request #5135 from deltachat/dependabot/cargo/syn-2.0.43 2024-01-03 04:43:06 +00:00
dependabot[bot]
3998258afb Merge pull request #5141 from deltachat/dependabot/cargo/quote-1.0.34 2024-01-03 04:40:31 +00:00
dependabot[bot]
4e86de98c4 chore(cargo): bump quote from 1.0.33 to 1.0.34
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.33 to 1.0.34.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.33...1.0.34)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 21:33:28 +00:00
dependabot[bot]
2a497989e9 chore(cargo): bump syn from 2.0.41 to 2.0.43
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.41 to 2.0.43.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.41...2.0.43)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 21:32:04 +00:00
dependabot[bot]
361b19e455 chore(cargo): bump tempfile from 3.8.1 to 3.9.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.8.1 to 3.9.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.8.1...v3.9.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 21:31:38 +00:00
dependabot[bot]
c036b26ae5 chore(cargo): bump reqwest from 0.11.22 to 0.11.23
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.22 to 0.11.23.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.22...v0.11.23)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 21:31:13 +00:00
link2xt
dcf6ffef12 fix(imap): fail fast on LIST errors
async-imap returns infinite stream of errors
in case of EOF or timeout on the input stream,
so attempting to skip and log errors results in busy loop
similar to this:

   2023-12-22T13:07:35.751Z core/event WARNING "" 2 "/__w/deltachat-core-rust/deltachat-core-rust/src/imap/scan_folders.rs:112: list_folders() can't get folder: io: timed out: timed out"
   2023-12-22T13:07:35.751Z core/event WARNING "" 2 "/__w/deltachat-core-rust/deltachat-core-rust/src/imap/scan_folders.rs:112: list_folders() can't get folder: io: timed out: timed out"
   2023-12-22T13:07:35.751Z core/event WARNING "" 2 "/__w/deltachat-core-rust/deltachat-core-rust/src/imap/scan_folders.rs:112: list_folders() can't get folder: io: timed out: timed out"
   2023-12-22T13:07:35.751Z core/event WARNING "" 2 "/__w/deltachat-core-rust/deltachat-core-rust/src/imap/scan_folders.rs:112: list_folders() can't get folder: io: timed out: timed out"
   2023-12-22T13:07:35.752Z core/event WARNING "" 2 "/__w/deltachat-core-rust/deltachat-core-rust/src/imap/scan_folders.rs:112: list_folders() can't get folder: io: timed out: timed out"
   2023-12-22T13:07:35.752Z core/event WARNING "" 2 "/__w/deltachat-core-rust/deltachat-core-rust/src/imap/scan_folders.rs:112: list_folders() can't get folder: io: timed out: timed out"
   2023-12-22T13:07:35.752Z core/event WARNING "" 2 "/__w/deltachat-core-rust/deltachat-core-rust/src/imap/scan_folders.rs:112: list_folders() can't get folder: io: timed out: timed out"
   2023-12-22T13:07:35.752Z core/event WARNING "" 2 "/__w/deltachat-core-rust/deltachat-core-rust/src/imap/scan_folders.rs:112: list_folders() can't get folder: io: timed out: timed out"
   2023-12-22T13:07:35.753Z core/event WARNING "" 2 "/__w/deltachat-core-rust/deltachat-core-rust/src/imap/scan_folders.rs:112: list_folders() can't get folder: io: timed out: timed out"
   2023-12-22T13:07:35.754Z core/event WARNING "" 2 "/__w/deltachat-core-rust/deltachat-core-rust/src/imap/scan_folders.rs:112: list_folders() can't get folder: io: timed out: timed out"
   2023-12-22T13:07:35.754Z core/event WARNING "" 2 "/__w/deltachat-core-rust/deltachat-core-rust/src/imap/scan_folders.rs:112: list_folders() can't get folder: io: timed out: timed out"

To avoid busy loop, fail fast on first error
and bubble it up instead of trying to recover.
2023-12-28 15:20:15 +00:00
iequidoo
865ede39fe fix: Properly escape target in receive_imf_inner()
The bug was made in 44227d7b86. Sql::execute() with placeholders must
be used to escape strings, one never should escape them manually as strings themselves can contain
escape symbols. Thanks to @link2xt for noticing.
2023-12-22 17:42:25 -03:00
iequidoo
a27e84ad89 fix: Delete received outgoing messages from SMTP queue (#5115)
Some SMTP servers are running slow before-queue filters, most commonly Postfix with `rspamd` filter
which is implemented as a [before-queue Milter](https://www.postfix.org/MILTER_README.html). Some of
`rspamd` plugin filters are slow on large mails.

We previously had problems with timing out during waiting for SMTP response:
https://github.com/deltachat/deltachat-core-rust/issues/1383. This is largely fixed by
https://github.com/async-email/async-smtp/pull/29 and currently we have 60-second timeout just for
reading a response but apparently it is not sufficient -- maybe connection gets killed by NAT while
we are waiting for response or `rspamd` takes more than 60 seconds for large messages.

As a result a message is resent multiple times and eventually fails with "too many retries" while
multiple BCC-self messages are received.

We should remove the message from the SMTP queue as soon as we receive it via IMAP as it is clear
the message was sent even if we did not manage to get actual SMTP server response.
2023-12-21 16:57:23 -03:00
iequidoo
b83bd26325 refactor: Don't pass seen=true to add_parts() when replacing existing message
First of all, it's just downloaded and hasn't been seen yet by the user. Also this changes nothing
as `msgs.state` isn't changed when replacing a message anyway.
2023-12-21 16:14:52 -03:00
iequidoo
44227d7b86 fix: Put Message-ID into hidden headers and take it from there on receiver (#4798)
Put a copy of Message-ID into hidden headers and prefer it over the one in the IMF header section
that servers mess up with.

This also reverts "Set X-Microsoft-Original-Message-ID on outgoing emails for amazonaws (#3077)".
2023-12-21 16:14:52 -03:00
iequidoo
6bcf022523 refactor: receive_imf_inner: Rename replace_partial_download to replace_msg_id
It's more consistent with `replace_chat_id` and the same as the corresponding parameter name of
`add_parts()`.
2023-12-21 16:14:52 -03:00
link2xt
ccec26ffa7 fix(imap): limit the rate of LOGIN attempts rather than connection attempts
As ratelimit was introduced to avoid reconnecting immediately after disconnecting
in case of bugs in IMAP protocol handling,
connection attempts should only be counted when IMAP is actually used,
i.e. when the first command (LOGIN) is sent.
2023-12-21 08:07:34 +00:00
link2xt
83e159e42f refactor: better log at the start of imex_inner() 2023-12-18 21:03:09 +00:00
link2xt
cbabd4219e refactor: improve error message when non-verified contact is added to protected chat 2023-12-18 21:03:09 +00:00
link2xt
548afe3153 refactor: derive Debug, PartialEq and Eq for VerifiedEncryption 2023-12-18 21:03:09 +00:00
link2xt
35c5f42b35 refactor: use let-else in *-request-with-auth handler 2023-12-18 21:03:09 +00:00
link2xt
b9ff8b1d6c refactor: flatten peerstate::maybe_do_aeap_transition 2023-12-18 21:03:09 +00:00
link2xt
bb6a20dc11 test: test joining non-protected group 2023-12-18 21:03:09 +00:00
link2xt
e97955f5a0 refactor: flatten handle_auth_require() with let..else 2023-12-18 21:03:09 +00:00
iequidoo
35bd56ffea fix: Securejoin: Mark 1:1s as protected regardless of the Config::VerifiedOneOnOneChats
As per the comment in `receive_imf.rs`, `chat.protected` must be maintained regardless of the
`Config::VerifiedOneOnOneChats`. The only thing that mustn't be done if `VerifiedOneOnOneChats` is
unset (i.e. for non-supporting UIs) is marking chats as "protection broken" because this needs
showing the corresponding dialog to a user.
2023-12-18 16:32:09 -03:00
link2xt
78affb766e fix: do not ignore peerstate.save_to_db() errors 2023-12-18 11:57:16 +00:00
link2xt
9b1704e3b2 feat(deltachat-repl): enable INFO logging by default and add timestamps 2023-12-17 15:10:19 +00:00
link2xt
55cdbdc085 refactor(sql): recreate keypairs table
Removed unused `addr` and `created` field.
`is_default` boolean flag is moved into `config` row
pointing to the current default key.
2023-12-17 14:13:54 +00:00
link2xt
58620988d7 refactor(sql): recreate config table with UNIQUE constraint 2023-12-17 14:13:54 +00:00
link2xt
467f313091 chore: cargo update 2023-12-17 13:31:20 +00:00
dependabot[bot]
091578573a chore(cargo): bump zerocopy from 0.7.29 to 0.7.31
Bumps [zerocopy](https://github.com/google/zerocopy) from 0.7.29 to 0.7.31.
- [Release notes](https://github.com/google/zerocopy/releases)
- [Changelog](https://github.com/google/zerocopy/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google/zerocopy/compare/v0.7.29...v0.7.31)

---
updated-dependencies:
- dependency-name: zerocopy
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-15 16:17:23 -03:00
iequidoo
62c1237024 refactor: Move calc_{protection_msg_,}sort_timestamp() to impl ChatId 2023-12-13 20:29:47 -03:00
iequidoo
8d41d02397 fix: calc_sort_timestamp: Skip messages that mustn't affect sorting of a new message (#5088)
Drafts mustn't affect sorting of any other messages, they aren't even displayed in the chat
window. Also hidden messages mustn't affect sorting of usual messages. But let hidden messages sort
together with protection messages because hidden messages also can be or not be verified, so let's
preserve this information -- even it's not useful currently, it can be useful in the future
versions.
2023-12-13 20:29:47 -03:00
iequidoo
fce3f80654 fix: Always pass the correct sort timestamp to ChatId::set_protection() (#5088)
Before in some places it was correctly calculated by passing the "sent" timestamp to
`calc_sort_timestamp()`, but in other places just the system time was used. In some complex
scenarios like #5088 (restoration of a backup made before a contact verification) it led to wrong
sort timestamps of protection messages and also messages following by them.

But to reduce number of args passed to functions needing to calculate the sort timestamp, add
message timestamps to `struct MimeMessage` which is anyway passed everywhere.
2023-12-13 20:29:47 -03:00
link2xt
2a0a51bea0 chore: remove n0-computer from deny.toml 2023-12-12 19:57:24 +00:00
link2xt
91d94d5920 build: use released version of iroh 0.4.2 2023-12-12 14:56:38 +00:00
link2xt
c59f21230d chore(release): prepare for 1.132.1 2023-12-12 02:58:29 +00:00
link2xt
828cc1fbd1 fix(connectivity): return false from all_work_done() immediately after connecting
We do not want all_work_done() to return true immediately
after calling start_io(), but only when connection goes idle.

"Connected" state is set immediately after connecting to the server,
but it does not mean there is nothing to do.

This change make all_work_done() return false
from the Connected state and introduces a new Idle
connectivity state that is only set before connection
actually goes idle. For idle state all_work_done() returns true.

From the user point of view both old Connected state
and new Idle state look the same.
2023-12-12 02:19:51 +00:00
link2xt
57f4958fc6 test(deltachat-rpc-client): test Account.{import,export}_self_keys 2023-12-11 06:43:10 +00:00
link2xt
3aeb57b4df api(deltachat-jsonrpc-client): add Account.{import,export}_self_keys 2023-12-11 06:43:10 +00:00
link2xt
1b85614db9 fix: renew IDLE timeout on keepalives and reduce it to 5 minutes
This change depends on async-imap update that resets the timeout
every time an `* OK Still here` is received.

Reducing timeout allows to detect lost connections
not later than 6 minutes
because Delta Chat will attempt to finish IDLE with DONE
after 5 minutes without keepalives
and will either get TCP RST directly
or, worst case, wait another minute for TCP socket read timeout.
2023-12-11 06:32:13 +00:00
link2xt
57ecf49eb1 chore: fix typo ("Bot" instead of "Bob") 2023-12-11 04:08:45 +00:00
iequidoo
f279b0d1e5 feat: Sync user actions for ad-hoc groups across devices (#5065)
Ad-hoc groups don't have grpid-s that can be used to identify them across devices and thus wasn't
synced until now.

The same problem already exists for assigning messages to ad-hoc groups and this assignment is done
by `get_parent_message()` and `lookup_chat_by_reply()`. Let's reuse this logic for the
synchronisation, it works well enough and this way we have less surprises than if we try to
implement grpids for ad-hoc groups. I.e. add an `Msgids` variant to `chat::SyncId` analogous to the
"References" header in messages and put two following Message-IDs to a sync message:
- The latest message A having `DownloadState::Done` and the state to be one of `InFresh, InNoticed,
  InSeen, OutDelivered, OutMdnRcvd`.
- The message that A references in `In-Reply-To`.

This way the logic is almost the same to what we have in `Chat::prepare_msg_raw()` (the difference
is that we don't use the oldest Message-ID) and it's easier to reuse the existing code.

NOTE: If a chat has only an OutPending message f.e., the synchronisation wouldn't work, but trying
to work in such a corner case has no significant value and isn't worth complicating the code.
2023-12-09 01:41:07 -03:00
iequidoo
32071297e6 feat: Add "From:" to protected headers for signed-only messages 2023-12-09 00:57:35 -03:00
link2xt
1d98c38ff3 ci: update to Rust 1.74.1 2023-12-08 23:52:36 +00:00
link2xt
c09e0e2b65 refactor: move AEAP and peerstate save from mimeparser to receive_imf()
Ideally mimeparser should be functional
and have no side effects such as modifying a peerstate in the database.
2023-12-08 23:32:03 +00:00
link2xt
0c8f967391 test: refine test_encrypted_no_autocrypt()
- Use TestContextManager
- Actually run receive_imf rather than only mimeparser on "received" messages
- Check that received message parts actually have a padlock
2023-12-08 23:32:03 +00:00
link2xt
aca34379e0 fix: add padlock to empty part if the whole message is empty
parse_mime_recursive() skips empty text parts,
so there may be no parts as the result of parsing.
In this case an empty part is added.
However, because it is added with parts.push()
rather than add_single_part(),
it is added without a padlock even if the message is encrypted.
`do_add_single_part()` adds padlock (GuaranteeE2EE param)
and should be used to add parts instead.
2023-12-07 03:38:20 +00:00
link2xt
1edd7045be chore(release): prepare for 1.132.0 2023-12-06 17:52:46 +00:00
B. Petersen
c784c499c2 fix: do not check lock_task on iOS before syncing
`lock_task` is anyways always `None` on iOS
to avoid lock files held open and cause 0xdead10cc crashes.
2023-12-06 18:17:49 +01:00
link2xt
36c751bcc3 chore: cargo update 2023-12-06 17:02:22 +00:00
link2xt
8a14a84bec test: check that ac2 gets a verified chat in test_securejoin_after_contact_resetup 2023-12-06 16:44:18 +00:00
link2xt
b00703cec2 fix: protect groups even if some members are not verified 2023-12-06 16:44:18 +00:00
link2xt
05e783564f refactor: log if the group is created as protected or not 2023-12-06 16:44:18 +00:00
link2xt
330fb02486 test: add test_securejoin_after_contact_resetup test
This test reproduces a bug preventing joining the group with a QR code
if the group already has a contact with inconsistent key state,
which means both Autocrypt and verified key exist,
but don't match.
This can happen when an invite QR code created by this contact
is scanned as scanning an invite code creates unprotected group
with the inviter for info messages.
If securejoin process never finishes because the inviter is offline,
group remains in this unprotected state with added inviter.

Normally the group becomes verified when a "Member added" (vg-member-added)
message is received in the chat.
However, current code checks that all members
of the chat are verified
(have a green checkmark, use verified key in 1:1 chat)
before marking the group as verified and fails otherwise.
2023-12-06 16:44:18 +00:00
link2xt
1447ab8dac refactor: clean up the logs and reduce noise
- Remove "Detected Autocrypt-mime message" logs printed for every incoming Autocrypt message.
- Print only a single line at the beginning of receive_imf with both the Message-ID and seen flag.
- Print Securejoin step only once, inside handle_securejoin_handshake or observe_securejoin_on_other_device.
- Do not log "Not creating ad-hoc group" every time ad-hoc group is not created, log when it is created instead.
- Log ID of the chat where Autocrypt-Gossip for all members is received.
- Do not print "Secure-join requested." for {vg,vc}-request, we already log the step.
- Remove ">>>>>>>>>>>>>>>>>>>>>>>>>" noise from securejoin logs.
2023-12-06 01:56:35 +00:00
link2xt
d574ee4edb chore: update zerocopy from 0.7.28 to 0.7.29
0.7.28 is yanked.
2023-12-05 19:25:27 +00:00
link2xt
814fe953a9 chore(cargo): update filetime 2023-12-05 16:45:24 +00:00
link2xt
280f13b8cf fix: do not lock accounts.toml on iOS
This results in 0xdead10cc crashes on suspend.
iOS itself ensures that multiple instances of Delta Chat are not running.
2023-12-04 21:51:17 +00:00
link2xt
a96b44a482 fix: do not mark recipients as verified if there is no Chat-Verified header 2023-12-04 15:34:09 +00:00
link2xt
4286d248e9 feat: increase TCP timeouts from 30 to 60 seconds
GitHub Action tests sometimes fail with TCP connection
timeouts, especially for macOS.
2023-12-04 12:50:07 +00:00
dependabot[bot]
116537019b chore(deps): bump self_cell from 1.0.1 to 1.0.2 in /fuzz
Bumps [self_cell](https://github.com/Voultapher/self_cell) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/Voultapher/self_cell/releases)
- [Commits](https://github.com/Voultapher/self_cell/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: self_cell
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-03 16:05:27 -03:00
iequidoo
8b37b8c1fd fix: Don't sort message creating a protected group over a protection message (#4963)
Otherwise it looks like the message creating a protected group is not verified. For this, use
`sent_timestamp` of the received message as an upper limit of the sort timestamp (`msgs.timestamp`)
of the protection message. As the protection message is added to the chat earlier, this way its
timestamp is always less or eq than the received message's timestamp.
2023-12-03 15:10:54 -03:00
iequidoo
63b4339ca0 test: Message order in a just created protected group on the second device (#4963)
Test that on the second device of a protected group creator the first message is
`SystemMessage::ChatProtectionEnabled` and the second one is the message populating the group.
2023-12-03 15:10:54 -03:00
link2xt
fdd239f61f fix: narrow the scope of verification exception to 1:1 chats
Allowing outgoing unencrypted messages in groups with 2 members
breaks the test
`python/tests/test_0_complex_or_slow.py::test_verified_group_vs_delete_server_after`
2023-12-03 15:46:56 +00:00
link2xt
5ca5d95c5e refactor: call has_verified_encryption() in a single place
This centralizes all Securejoin/verification checks and updates in one
place right before add_parts() even before we assign the message to
the chat, so we can decouple chat logic from verification logic.
2023-12-03 15:46:56 +00:00
link2xt
3fcad50924 refactor: move to_ids.is_empty() check into mark_recepients_as_verified() 2023-12-03 15:46:56 +00:00
link2xt
8e40540d24 refactor: add debug assertion where we expect a 1:1 chat 2023-12-03 15:46:56 +00:00
link2xt
04d22bb84d refactor: remove chattype argument from has_verified_encryption() 2023-12-03 15:46:56 +00:00
link2xt
5415f1bfa1 docs: has_verified_encryption() does not check that all members are verified 2023-12-03 15:46:56 +00:00
link2xt
ff3bf4791a chore: update dependencies 2023-12-03 00:43:21 +00:00
dependabot[bot]
eebea216cb chore(cargo): bump testdir from 0.8.1 to 0.9.0
Bumps [testdir](https://github.com/flub/testdir) from 0.8.1 to 0.9.0.
- [Changelog](https://github.com/flub/testdir/blob/main/CHANGELOG.md)
- [Commits](https://github.com/flub/testdir/compare/v0.8.1...v0.9.0)

---
updated-dependencies:
- dependency-name: testdir
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-02 15:50:51 +00:00
link2xt
fbcd7f46b8 chore(release): prepare for 1.131.9 2023-12-02 01:18:34 +00:00
iequidoo
846278b18e feat: chat::rename_ex: Don't send sync message if usual message is sent
It's not necessary and in other places like add_contact_to_chat_ex() sync messages are also sent
only if there are no system messages sent like MemberAddedToGroup.
2023-12-01 21:41:58 -03:00
iequidoo
2f2b1e18bf test: Split test_sync_alter_chat() into smaller tests 2023-12-01 21:41:58 -03:00
iequidoo
073c250fa4 refactor: Add test_utils::sync()
Add a function that pops a sync message from one Alice's device and receives it on another.
2023-12-01 21:41:58 -03:00
iequidoo
1f336f89a6 feat: Sync Config::Displayname across devices (#4893)
We already synchronise status/footer when we see a self-sent message with a Chat-Version
header. Would be nice to do the same for display name.

But let's do it the same way as for `Config::{MdnsEnabled,ShowEmails}`. Otherwise, if we sync the
display name using the "From" header, smth like `Param::StatusTimestamp` is needed then to reject
outdated display names. Also this timestamp needs to be updated when `Config::Displayname` is set
locally. Also this wouldn't work if system time isn't synchronised on devices. Also using multiple
approaches to sync different config values would lead to more code and bugs while having almost no
value -- using "From" only saves some bytes and allows to sync some things w/o the synchronisation
itself to be enabled. But the latter also can be a downside -- if it's usual synchonisation, you can
(potentially) disable it and share the same email account across people in some organisation
allowing them to have different display names. With using "From" for synchronisation such a
capability definitely requires a new config option.
2023-12-01 21:41:58 -03:00
iequidoo
a47fec7f6c feat: Sync Config::{MdnsEnabled,ShowEmails} across devices (#4954)
Motivation: Syncing these options will improve UX in very most cases and should be done. Other
candidates are less clear or are advanced options, we can reconsider that at some point later.

Approach:
- Sync options one-by-one when the corresponding option is set (even if to the same value).
- Don't sync when an option is reset to a default as defaults may differ across client versions.
- Check on both sides that the option should be synced so that if there are different client
  versions, the synchronisation of the option is either done or not done in both directions.
  Moreover, receivers of a config value need to check if a key can be synced because some settings
  (e.g. Avatar path) could otherwise lead to exfiltration of files from a receiver's device if we
  assume an attacker to have control of a device in a multi-device setting or if multiple users are
  sharing an account.
- Don't sync `SyncMsgs` itself.
2023-12-01 21:41:58 -03:00
iequidoo
084434d3b4 feat: receive_imf_inner: Add missing initialisation of ReceivedMsg::from_is_signed 2023-12-01 21:41:58 -03:00
iequidoo
ebfbc11973 feat: Don't affect MimeMessage with "From" and secured headers from encrypted unsigned messages
If a message is encrypted, but unsigned:
- Don't set `MimeMessage::from_is_signed`.
- Remove "secure-join-fingerprint" and "chat-verified" headers from `MimeMessage`.
- Minor: Preserve "Subject" from the unencrypted top level if there's no "Subject" in the encrypted
  part, this message is displayed w/o a padlock anyway.

Apparently it didn't lead to any vulnerabilities because there are checks for
`MimeMessage::signatures.is_empty()` in all necessary places, but still the code looked dangerous,
especially because `from_is_singed` var name didn't correspond to its actual value (it was rather
`from_is_encrypted_maybe_signed`).
2023-12-01 19:06:11 -03:00
link2xt
9cc9579b2d feat: remove receiver limit on .xdc size
If we have downloaded the file anyway,
might as well allow to open it.
2023-12-01 15:53:38 +00:00
link2xt
7beccd9dbc refactor: better error context in send_webxdc_status_update_struct() 2023-12-01 15:19:23 +00:00
link2xt
0e195bc7a2 fix: lock the database when INSERTing a webxdc update
`query_row_optional` does not hold the write lock
and may fail with "database is locked" error
or cause the other task such as SMTP loop to fail.
2023-12-01 15:19:23 +00:00
link2xt
f89efd5fce test: test inserting lots of webxdc updates
Currently this leads to
DEBUG    root:rpc.py:136 account_id=1 got an event {'kind': 'Warning', 'msg': 'src/scheduler.rs:711: send_smtp_messages failed: failed to send message: failed to update retries count: database is locked: Error code 5: The database file is locked'}
and
FAILED tests/test_webxdc.py::test_webxdc_insert_lots_of_updates - deltachat_rpc_client.rpc.JsonRpcError: {'code': -1, 'message': 'database is locked\n\nCaused by:\n    Error code 5: The database file is locked'}
2023-12-01 15:19:23 +00:00
link2xt
48d278fca9 chore: update dependencies 2023-12-01 02:41:48 +00:00
link2xt
c84effdaa1 refactor: add more error context to send_webxdc_status_update()
This is a follow-up to b9fa05c3bb
2023-12-01 02:32:21 +00:00
link2xt
e9601ef138 test: make Result-returning tests produce a line number
Without this change
when the test returns a `Result`, `cargo test` does not show
the line number.

To make the tests as easy to debug as the panicking tests,
enable `backtrace` feature on `anyhow` and add debug information
to add source line numbers to backtraces.

Now running `RUST_BACKTRACE=1 cargo test` produces backtraces
with the line numbers. For example:

Error: near ",": syntax error in SELECT COUNT(,,*) FROM msgs_status_updates; at offset 13

Caused by:
    Error code 1: SQL error or missing database

Stack backtrace:
   0: <core::result::Result<T,F> as core::ops::try_trait::FromResidual<core::result::Result<core::convert::Infallible,E>>>::from_residual
             at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/core/src/result.rs:1962:27
   1: deltachat::sql::Sql::query_row::{{closure}}::{{closure}}
             at ./src/sql.rs:466:23
   2: deltachat::sql::Sql::call::{{closure}}::{{closure}}
             at ./src/sql.rs:379:55
   3: tokio::runtime::context::runtime_mt::exit_runtime
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.34.0/src/runtime/context/runtime_mt.rs:35:5
   4: tokio::runtime::scheduler::multi_thread::worker::block_in_place
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.34.0/src/runtime/scheduler/multi_thread/worker.rs:438:9
   5: tokio::runtime::scheduler::block_in_place::block_in_place
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.34.0/src/runtime/scheduler/block_in_place.rs:20:5
   6: tokio::task::blocking::block_in_place
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.34.0/src/task/blocking.rs:78:9
   7: deltachat::sql::Sql::call::{{closure}}
             at ./src/sql.rs:379:19
   8: deltachat::sql::Sql::query_row::{{closure}}
             at ./src/sql.rs:469:10
   9: deltachat::sql::Sql::count::{{closure}}
             at ./src/sql.rs:443:76
  10: deltachat::webxdc::tests::change_logging_webxdc::{{closure}}
             at ./src/webxdc.rs:2644:18
  11: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /rustc/79e9716c980570bfd1f666e3b16ac583f0168962/library/core/src/future/future.rs:125:9
  12: tokio::runtime::park::CachedParkThread::block_on::{{closure}}
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.34.0/src/runtime/park.rs:282:63
  13: tokio::runtime::coop::with_budget
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.34.0/src/runtime/coop.rs:107:5
      tokio::runtime::coop::budget
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.34.0/src/runtime/coop.rs:73:5
      tokio::runtime::park::CachedParkThread::block_on
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.34.0/src/runtime/park.rs:282:31
  14: tokio::runtime::context::blocking::BlockingRegionGuard::block_on
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.34.0/src/runtime/context/blocking.rs:66:9
...

Line 10 of the backtrace contains the line number in the test (2644).
2023-11-30 22:27:38 +00:00
iequidoo
44c5cd5526 feat: Ratelimit IMAP connections (#4940)
Limit the number of IMAP connections to 1 per minute regardless of the reason of reconnection, but
allow one immediate retry. This is more reliable than ratelimiting only in error conditions because
ratelimiting can't be skipped by mistake. Anyway connections shouldn't be frequent in normal
operation mode.
2023-11-30 19:22:01 -03:00
link2xt
1c9662a8f2 refactor: rename min_verified into verified 2023-11-30 12:04:03 +00:00
link2xt
5d08b2ce33 refactor: remove unused PeerstateVerifiedStatus 2023-11-30 12:04:03 +00:00
link2xt
bb9d7d7ef3 feat: send Chat-Verified headers in 1:1 chats
Chat-Verified is going to be useful to upgrade one-way verification
to bidirectional verification.
2023-11-30 12:04:03 +00:00
link2xt
766bb5c8aa refactor: factor securejoin processing out of add_parts 2023-11-30 12:04:03 +00:00
link2xt
84144659cf refactor: remove {vc-contact-confirm,vg-member-added}-received steps 2023-11-30 12:04:03 +00:00
link2xt
1394137436 refactor: make min_verified a boolean
We either need a securejoin or autocrypt key,
there are no intermediate states.
2023-11-30 12:04:03 +00:00
link2xt
998614b923 api: make Contact.is_verified() return bool 2023-11-30 12:04:03 +00:00
B. Petersen
5b346397b8 api: deprecate CFFI APIs dc_send_reaction(), dc_get_msg_reactions(), dc_reactions_get_contacts(), dc_reactions_get_by_contact_id(), dc_reactions_unref and dc_reactions_t
this is now done with jsonrpc via
`dc_jsonrpc_request()` or `dc_jsonrpc_blocking_call()`
using the methods `send_reaction` and `get_message_reactions`
2023-11-29 11:29:29 +01:00
B. Petersen
1f99269002 api: remove dc_get_http_response(), dc_http_response_get_mimetype(), dc_http_response_get_encoding(), dc_http_response_get_blob(), dc_http_response_get_size(), dc_http_response_unref() and dc_http_response_t from cffi
this is now done with jsonrpc via
`dc_jsonrpc_request()` or `dc_jsonrpc_blocking_call()`
using the method `get_http_response`
2023-11-29 11:29:29 +01:00
iequidoo
160cbe8125 fix: Use keyring with all private keys when decrypting a message (#5046)
Before a keyring with the only default key was used, i.e. the key used for signing and encrypting to
self.
2023-11-29 02:20:19 -03:00
link2xt
b9fa05c3bb refactor: improve logging of send_webxdc_status_update errors
send_webxdc_status_update JSON-RPC call
and corresponding Rust call sometimes fail in CI with
---
database is locked

Caused by:
    Error code 5: The database file is locked
---

Adding more context to send_webxdc_status_update() errors
to better localize the error origin.
2023-11-28 22:48:55 +00:00
link2xt
4287a4d3ad refactor: factor out insert_tombstone 2023-11-28 21:57:41 +00:00
link2xt
37d2aafb26 fix: return correct MsgId for malformed message tombstone
.execute() returns the number of affected rows,
in this case it is always 1 and MsgId(1) is returned
instead of the actual tombstone row ID.
2023-11-28 21:57:41 +00:00
link2xt
4332170691 ci: add exception for RUSTSEC-2023-0071 to cargo-deny config
See
<https://rustsec.org/advisories/RUSTSEC-2023-0071>
and discussion at
<https://github.com/RustCrypto/RSA/issues/19>
for details.
2023-11-28 16:26:11 +00:00
link2xt
9a7c0f4737 chore: update OpenSSL 2023-11-28 15:05:34 +00:00
link2xt
9e7e172a7b build: switch from fork of iroh to iroh 0.4.2 pre-release 2023-11-28 02:59:42 +00:00
link2xt
71fbaf572a chore(release): prepare for 1.131.8 2023-11-28 00:01:17 +00:00
link2xt
2ab29e5bfa fix: allow IMAP servers not returning UIDNEXT on SELECT and STATUS 2023-11-27 23:50:43 +00:00
link2xt
85f8f910b9 chore: update wasm-bindgen from 0.2.88 to 0.2.89
0.2.88 is yanked: https://github.com/rustwasm/wasm-bindgen/issues/3685
2023-11-27 21:41:23 +00:00
link2xt
b779d08d7f test: check that receive_status_update has forward compatibility
This ensures old version of Delta Chat will be fine with a new "uid" field.
2023-11-27 13:49:41 +00:00
link2xt
3b5634f14b fix: do not emit events about webxdc update events logged into debug log webxdc 2023-11-27 13:49:41 +00:00
link2xt
f91ba357cf feat(webxdc): add unique IDs to status updates sent outside
This allows for deduplication
if status updates are sent over multiple transports.
2023-11-27 13:49:41 +00:00
Hocuri
616faff96b fix: Use the correct securejoin strings used in the UI, remove old TODO (#5047) 2023-11-26 15:54:11 +01:00
link2xt
5e6869403e chore(release): prepare for 1.131.7 2023-11-24 18:26:25 +00:00
link2xt
7ff7d82959 Revert "fix: check UIDNEXT with a STATUS command before going IDLE"
This reverts commit 2e50abedaa.

STATUS is broken on mail.163.com.
It returns `STATUS "INBOX" ()` reply
when `STATUS "INBOX" (UIDNEXT)` is requested.
2023-11-24 18:19:02 +00:00
167 changed files with 14793 additions and 6257 deletions

View File

@@ -24,9 +24,11 @@ jobs:
name: Lint Rust
runs-on: ubuntu-latest
env:
RUSTUP_TOOLCHAIN: 1.74.0
RUSTUP_TOOLCHAIN: 1.77.1
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Install rustfmt and clippy
run: rustup toolchain install $RUSTUP_TOOLCHAIN --profile minimal --component rustfmt --component clippy
- name: Cache rust cargo artifacts
@@ -42,7 +44,9 @@ jobs:
name: cargo deny
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- uses: EmbarkStudios/cargo-deny-action@v1
with:
arguments: --all-features --workspace
@@ -53,7 +57,9 @@ jobs:
name: Check provider database
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Check provider database
run: scripts/update-provider-database.sh
@@ -63,8 +69,9 @@ jobs:
env:
RUSTDOCFLAGS: -Dwarnings
steps:
- name: Checkout sources
uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Cache rust cargo artifacts
uses: swatinem/rust-cache@v2
- name: Rustdoc
@@ -76,18 +83,20 @@ jobs:
matrix:
include:
- os: ubuntu-latest
rust: 1.74.0
rust: 1.77.1
- os: windows-latest
rust: 1.74.0
rust: 1.77.1
- os: macos-latest
rust: 1.74.0
rust: 1.77.1
# Minimum Supported Rust Version = 1.70.0
# Minimum Supported Rust Version = 1.77.0
- os: ubuntu-latest
rust: 1.70.0
rust: 1.77.0
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Install Rust ${{ matrix.rust }}
run: rustup toolchain install --profile minimal ${{ matrix.rust }}
@@ -111,7 +120,9 @@ jobs:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Cache rust cargo artifacts
uses: swatinem/rust-cache@v2
@@ -120,7 +131,7 @@ jobs:
run: cargo build -p deltachat_ffi --features jsonrpc
- name: Upload C library
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}-libdeltachat.a
path: target/debug/libdeltachat.a
@@ -133,7 +144,9 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Cache rust cargo artifacts
uses: swatinem/rust-cache@v2
@@ -142,7 +155,7 @@ jobs:
run: cargo build -p deltachat-rpc-server
- name: Upload deltachat-rpc-server
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}-deltachat-rpc-server
path: ${{ matrix.os == 'windows-latest' && 'target/debug/deltachat-rpc-server.exe' || 'target/debug/deltachat-rpc-server' }}
@@ -152,8 +165,9 @@ jobs:
name: Python lint
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Install tox
run: pip install tox
@@ -193,16 +207,18 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Download libdeltachat.a
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: ${{ matrix.os }}-libdeltachat.a
path: target/debug
- name: Install python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
@@ -243,10 +259,12 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Install python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
@@ -254,7 +272,7 @@ jobs:
run: pip install tox
- name: Download deltachat-rpc-server
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: ${{ matrix.os }}-deltachat-rpc-server
path: target/debug

View File

@@ -21,69 +21,65 @@ jobs:
# Build a version statically linked against musl libc
# to avoid problems with glibc version incompatibility.
build_linux:
name: Cross-compile deltachat-rpc-server for x86_64, i686, aarch64 and armv7 Linux
runs-on: ubuntu-22.04
name: Linux
strategy:
fail-fast: false
matrix:
arch: [aarch64, armv7l, armv6l, i686, x86_64]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install ziglang
run: pip install wheel ziglang==0.11.0
- uses: actions/checkout@v4
with:
show-progress: false
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Build deltachat-rpc-server binaries
run: sh scripts/zig-rpc-server.sh
run: nix build .#deltachat-rpc-server-${{ matrix.arch }}-linux
- name: Upload dist directory with Linux binaries
uses: actions/upload-artifact@v3
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: linux
path: dist/
name: deltachat-rpc-server-${{ matrix.arch }}-linux
path: result/bin/deltachat-rpc-server
if-no-files-found: error
build_windows:
name: Build deltachat-rpc-server for Windows
name: Windows
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
artifact: win32.exe
path: deltachat-rpc-server.exe
target: i686-pc-windows-msvc
- os: windows-latest
artifact: win64.exe
path: deltachat-rpc-server.exe
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.os }}
arch: [win32, win64]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Setup rust target
run: rustup target add ${{ matrix.target }}
- name: Build
run: cargo build --release --package deltachat-rpc-server --target ${{ matrix.target }} --features vendored
- name: Build deltachat-rpc-server binaries
run: nix build .#deltachat-rpc-server-${{ matrix.arch }}
- name: Upload binary
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: deltachat-rpc-server-${{ matrix.artifact }}
path: target/${{ matrix.target}}/release/${{ matrix.path }}
name: deltachat-rpc-server-${{ matrix.arch }}
path: result/bin/deltachat-rpc-server.exe
if-no-files-found: error
build_macos:
name: Build deltachat-rpc-server for macOS
name: macOS
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
- arch: aarch64
arch: [x86_64, aarch64]
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Setup rust target
run: rustup target add ${{ matrix.arch }}-apple-darwin
@@ -92,61 +88,140 @@ jobs:
run: cargo build --release --package deltachat-rpc-server --target ${{ matrix.arch }}-apple-darwin --features vendored
- name: Upload binary
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: deltachat-rpc-server-${{ matrix.arch }}-macos
path: target/${{ matrix.arch }}-apple-darwin/release/deltachat-rpc-server
if-no-files-found: error
build_android:
name: Android
strategy:
fail-fast: false
matrix:
arch: [arm64-v8a, armeabi-v7a]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Build deltachat-rpc-server binaries
run: nix build .#deltachat-rpc-server-${{ matrix.arch }}-android
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: deltachat-rpc-server-${{ matrix.arch }}-android
path: result/bin/deltachat-rpc-server
if-no-files-found: error
publish:
name: Build wheels and upload binaries to the release
needs: ["build_linux", "build_windows", "build_macos"]
environment:
name: pypi
url: https://pypi.org/p/deltachat-rpc-server
permissions:
id-token: write
contents: write
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v3
- name: Download Linux binaries
uses: actions/download-artifact@v3
- uses: actions/checkout@v4
with:
name: linux
path: dist/
show-progress: false
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Download win32 binary
uses: actions/download-artifact@v3
- name: Download Linux aarch64 binary
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-win32.exe
path: deltachat-rpc-server-win32.exe.d
name: deltachat-rpc-server-aarch64-linux
path: deltachat-rpc-server-aarch64-linux.d
- name: Download win64 binary
uses: actions/download-artifact@v3
- name: Download Linux armv7l binary
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-win64.exe
path: deltachat-rpc-server-win64.exe.d
name: deltachat-rpc-server-armv7l-linux
path: deltachat-rpc-server-armv7l-linux.d
- name: Download Linux armv6l binary
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-armv6l-linux
path: deltachat-rpc-server-armv6l-linux.d
- name: Download Linux i686 binary
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-i686-linux
path: deltachat-rpc-server-i686-linux.d
- name: Download Linux x86_64 binary
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-x86_64-linux
path: deltachat-rpc-server-x86_64-linux.d
- name: Download Win32 binary
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-win32
path: deltachat-rpc-server-win32.d
- name: Download Win64 binary
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-win64
path: deltachat-rpc-server-win64.d
- name: Download macOS binary for x86_64
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-x86_64-macos
path: deltachat-rpc-server-x86_64-macos.d
- name: Download macOS binary for aarch64
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-aarch64-macos
path: deltachat-rpc-server-aarch64-macos.d
- name: Flatten dist/ directory
- name: Download Android binary for arm64-v8a
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-arm64-v8a-android
path: deltachat-rpc-server-arm64-v8a-android.d
- name: Download Android binary for armeabi-v7a
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-armeabi-v7a-android
path: deltachat-rpc-server-armeabi-v7a-android.d
- name: Create bin/ directory
run: |
mv deltachat-rpc-server-win32.exe.d/deltachat-rpc-server.exe dist/deltachat-rpc-server-win32.exe
mv deltachat-rpc-server-win64.exe.d/deltachat-rpc-server.exe dist/deltachat-rpc-server-win64.exe
mv deltachat-rpc-server-x86_64-macos.d/deltachat-rpc-server dist/deltachat-rpc-server-x86_64-macos
mv deltachat-rpc-server-aarch64-macos.d/deltachat-rpc-server dist/deltachat-rpc-server-aarch64-macos
mkdir -p bin
mv deltachat-rpc-server-aarch64-linux.d/deltachat-rpc-server bin/deltachat-rpc-server-aarch64-linux
mv deltachat-rpc-server-armv7l-linux.d/deltachat-rpc-server bin/deltachat-rpc-server-armv7l-linux
mv deltachat-rpc-server-armv6l-linux.d/deltachat-rpc-server bin/deltachat-rpc-server-armv6l-linux
mv deltachat-rpc-server-i686-linux.d/deltachat-rpc-server bin/deltachat-rpc-server-i686-linux
mv deltachat-rpc-server-x86_64-linux.d/deltachat-rpc-server bin/deltachat-rpc-server-x86_64-linux
mv deltachat-rpc-server-win32.d/deltachat-rpc-server.exe bin/deltachat-rpc-server-win32.exe
mv deltachat-rpc-server-win64.d/deltachat-rpc-server.exe bin/deltachat-rpc-server-win64.exe
mv deltachat-rpc-server-x86_64-macos.d/deltachat-rpc-server bin/deltachat-rpc-server-x86_64-macos
mv deltachat-rpc-server-aarch64-macos.d/deltachat-rpc-server bin/deltachat-rpc-server-aarch64-macos
mv deltachat-rpc-server-arm64-v8a-android.d/deltachat-rpc-server bin/deltachat-rpc-server-arm64-v8a-android
mv deltachat-rpc-server-armeabi-v7a-android.d/deltachat-rpc-server bin/deltachat-rpc-server-armeabi-v7a-android
- name: List binaries
run: ls -l bin/
# Python 3.11 is needed for tomllib used in scripts/wheel-rpc-server.py
- name: Install python 3.12
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.12
@@ -154,15 +229,40 @@ jobs:
run: pip install wheel
- name: Build deltachat-rpc-server Python wheels and source package
run: scripts/wheel-rpc-server.py
run: |
mkdir -p dist
nix build .#deltachat-rpc-server-x86_64-linux-wheel
cp result/*.whl dist/
nix build .#deltachat-rpc-server-armv7l-linux-wheel
cp result/*.whl dist/
nix build .#deltachat-rpc-server-armv6l-linux-wheel
cp result/*.whl dist/
nix build .#deltachat-rpc-server-aarch64-linux-wheel
cp result/*.whl dist/
nix build .#deltachat-rpc-server-i686-linux-wheel
cp result/*.whl dist/
nix build .#deltachat-rpc-server-win64-wheel
cp result/*.whl dist/
nix build .#deltachat-rpc-server-win32-wheel
cp result/*.whl dist/
nix build .#deltachat-rpc-server-source
cp result/*.tar.gz dist/
python3 scripts/wheel-rpc-server.py x86_64-darwin bin/deltachat-rpc-server-x86_64-macos
python3 scripts/wheel-rpc-server.py aarch64-darwin bin/deltachat-rpc-server-aarch64-macos
mv *.whl dist/
- name: List downloaded artifacts
- name: List artifacts
run: ls -l dist/
- name: Upload binaries to the GitHub release
if: github.event_name == 'release'
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
run: |
gh release upload ${{ github.ref_name }} \
--repo ${{ github.repository }} \
dist/*
bin/* dist/*
- name: Publish deltachat-rpc-client to PyPI
if: github.event_name == 'release'
uses: pypa/gh-action-pypi-publish@release/v1

View File

@@ -13,9 +13,10 @@ jobs:
steps:
- name: Install tree
run: sudo apt install tree
- name: Checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/setup-node@v4
with:
node-version: "18"
- name: Get tag
@@ -49,7 +50,7 @@ jobs:
ls -lah
mv $(find deltachat-jsonrpc-client-*) $DELTACHAT_JSONRPC_TAR_GZ
- name: Upload Prebuild
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: deltachat-jsonrpc-client.tgz
path: deltachat-jsonrpc/typescript/${{ env.DELTACHAT_JSONRPC_TAR_GZ }}

View File

@@ -14,9 +14,11 @@ jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Use Node.js 18.x
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18.x
- name: Add Rust cache

View File

@@ -14,10 +14,12 @@ jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Use Node.js 18.x
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18.x

View File

@@ -14,9 +14,10 @@ jobs:
matrix:
os: [macos-latest, windows-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/setup-node@v4
with:
node-version: "18"
- name: System info
@@ -28,7 +29,7 @@ jobs:
node --version
- name: Cache node modules
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{ env.APPDATA }}/npm-cache
@@ -36,7 +37,7 @@ jobs:
key: ${{ matrix.os }}-node-${{ hashFiles('**/package.json') }}
- name: Cache cargo index
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/
@@ -56,7 +57,7 @@ jobs:
tar -zcvf "${{ matrix.os }}.tar.gz" -C prebuilds .
- name: Upload Prebuild
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}
path: node/${{ matrix.os }}.tar.gz
@@ -74,9 +75,10 @@ jobs:
- name: Change working directory owner
run: chown root:root .
- name: Checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/setup-node@v4
with:
node-version: "18"
- run: apt-get update
@@ -97,7 +99,7 @@ jobs:
node --version
- name: Cache node modules
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{ env.APPDATA }}/npm-cache
@@ -105,7 +107,7 @@ jobs:
key: ${{ matrix.os }}-node-${{ hashFiles('**/package.json') }}
- name: Cache cargo index
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/
@@ -125,7 +127,7 @@ jobs:
tar -zcvf "linux.tar.gz" -C prebuilds .
- name: Upload Prebuild
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: linux
path: node/linux.tar.gz
@@ -137,9 +139,10 @@ jobs:
steps:
- name: Install tree
run: sudo apt install tree
- name: Checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v2
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/setup-node@v4
with:
node-version: "18"
- name: Get tag
@@ -165,25 +168,25 @@ jobs:
node --version
echo $DELTACHAT_NODE_TAR_GZ
- name: Download Linux prebuild
uses: actions/download-artifact@v1
uses: actions/download-artifact@v4
with:
name: linux
- name: Download macOS prebuild
uses: actions/download-artifact@v1
uses: actions/download-artifact@v4
with:
name: macos-latest
- name: Download Windows prebuild
uses: actions/download-artifact@v1
uses: actions/download-artifact@v4
with:
name: windows-latest
- shell: bash
run: |
mkdir node/prebuilds
tar -xvzf linux/linux.tar.gz -C node/prebuilds
tar -xvzf macos-latest/macos-latest.tar.gz -C node/prebuilds
tar -xvzf windows-latest/windows-latest.tar.gz -C node/prebuilds
tar -xvzf linux.tar.gz -C node/prebuilds
tar -xvzf macos-latest.tar.gz -C node/prebuilds
tar -xvzf windows-latest.tar.gz -C node/prebuilds
tree node/prebuilds
rm -rf linux macos-latest windows-latest
rm -f linux.tar.gz macos-latest.tar.gz windows-latest.tar.gz
- name: Install dependencies without running scripts
run: |
npm install --ignore-scripts
@@ -201,7 +204,7 @@ jobs:
ls -lah
mv $(find deltachat-node-*) $DELTACHAT_NODE_TAR_GZ
- name: Upload prebuild
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: deltachat-node.tgz
path: ${{ env.DELTACHAT_NODE_TAR_GZ }}

View File

@@ -23,9 +23,10 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/setup-node@v4
with:
node-version: "18"
- name: System info
@@ -37,7 +38,7 @@ jobs:
node --version
- name: Cache node modules
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
${{ env.APPDATA }}/npm-cache
@@ -45,7 +46,7 @@ jobs:
key: ${{ matrix.os }}-node-${{ hashFiles('**/package.json') }}
- name: Cache cargo index
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/

View File

@@ -0,0 +1,47 @@
name: Publish deltachat-rpc-client to PyPI
on:
workflow_dispatch:
release:
types: [published]
jobs:
build:
name: Build distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- name: Install pypa/build
run: python3 -m pip install build
- name: Build a binary wheel and a source tarball
working-directory: deltachat-rpc-client
run: python3 -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: deltachat-rpc-client/dist/
publish-to-pypi:
name: Publish Python distribution to PyPI
if: github.event_name == 'release'
needs:
- build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/deltachat-rpc-client
permissions:
id-token: write
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish deltachat-rpc-client to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

View File

@@ -10,15 +10,17 @@ on:
jobs:
build_repl:
name: Build REPL example
runs-on: windows-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Build
run: cargo build -p deltachat-repl --features vendored
run: nix build .#deltachat-repl-win64
- name: Upload binary
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: repl.exe
path: "target/debug/deltachat-repl.exe"
path: "result/bin/deltachat-repl.exe"

View File

@@ -1,4 +1,4 @@
name: Build & Deploy Documentation on rs.delta.chat
name: Build & Deploy Documentation on rs.delta.chat, c.delta.chat, py.delta.chat
on:
push:
@@ -6,11 +6,13 @@ on:
- main
jobs:
build:
build-rs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Build the documentation with cargo
run: |
cargo doc --package deltachat --no-deps --document-private-items
@@ -22,3 +24,41 @@ jobs:
HOST: "delta.chat"
SOURCE: "target/doc"
TARGET: "/var/www/html/rs/"
build-python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
show-progress: false
fetch-depth: 0 # Fetch history to calculate VCS version number.
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Build Python documentation
run: nix build .#python-docs
- name: Upload to py.delta.chat
run: |
mkdir -p "$HOME/.ssh"
echo "${{ secrets.CODESPEAK_KEY }}" > "$HOME/.ssh/key"
chmod 600 "$HOME/.ssh/key"
rsync -avzh -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no" $GITHUB_WORKSPACE/result/html/ "delta@py.delta.chat:/home/delta/build/master"
build-c:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
show-progress: false
fetch-depth: 0 # Fetch history to calculate VCS version number.
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Build C documentation
run: nix build .#docs
- name: Upload to py.delta.chat
run: |
mkdir -p "$HOME/.ssh"
echo "${{ secrets.CODESPEAK_KEY }}" > "$HOME/.ssh/key"
chmod 600 "$HOME/.ssh/key"
rsync -avzh -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no" $GITHUB_WORKSPACE/result/html/ "delta@c.delta.chat:/home/delta/build-c/master"

View File

@@ -14,15 +14,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
show-progress: false
- name: Build the documentation with cargo
run: |
cargo doc --package deltachat_ffi --no-deps
- name: Upload to cffi.delta.chat
uses: up9cloud/action-rsync@v1.3
env:
USER: ${{ secrets.USERNAME }}
KEY: ${{ secrets.KEY }}
HOST: "delta.chat"
SOURCE: "target/doc"
TARGET: "/var/www/html/cffi/"
run: |
mkdir -p "$HOME/.ssh"
echo "${{ secrets.KEY }}" > "$HOME/.ssh/key"
chmod 600 "$HOME/.ssh/key"
rsync -avzh -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no" $GITHUB_WORKSPACE/target/doc/ "${{ secrets.USERNAME }}@delta.chat:/var/www/html/cffi/"

View File

@@ -1,25 +0,0 @@
name: Build & Deploy Documentation on py.delta.chat
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Fetch history to calculate VCS version number.
- name: Build Python documentation
run: scripts/build-python-docs.sh
- name: Upload to py.delta.chat
uses: up9cloud/action-rsync@v1.3
env:
USER: delta
KEY: ${{ secrets.CODESPEAK_KEY }}
HOST: "lists.codespeak.net"
SOURCE: "dist/html/"
TARGET: "/home/delta/build/master"

7
.gitignore vendored
View File

@@ -44,3 +44,10 @@ node/build/
node/dist/
node/prebuilds/
node/.nyc_output/
# Nix symlink.
result
# direnv
.envrc
.direnv

View File

@@ -1,5 +1,593 @@
# Changelog
## [1.137.2] - 2024-04-05
### API-Changes
- [**breaking**] Increase Minimum Supported Rust Version to 1.77.0.
### Features / Changes
- Show reactions in summaries ([#5387](https://github.com/deltachat/deltachat-core-rust/pull/5387)).
### Tests
- Test reactions for forwarded messages
### Refactor
- `is_probably_private_reply`: Remove reaction-specific code.
- Use Rust 1.77.0 support for recursion in async functions.
### Miscellaneous Tasks
- cargo: Bump rustyline from 13.0.0 to 14.0.0.
- Update chrono from 0.4.34 to 0.4.37.
- Update from brotli 3.4.0 to brotli 4.0.0.
- Upgrade `h2` from 0.4.3 to 0.4.4.
- Upgrade `image` from 0.24.9 to 0.25.1.
- cargo: Bump fast-socks5 from 0.9.5 to 0.9.6.
## [1.137.1] - 2024-04-03
### CI
- Remove android builds for `x86` and `x86_64`.
## [1.137.0] - 2024-04-02
### API-Changes
- [**breaking**] Remove data from `DC_EVENT_INCOMING_MSG_BUNCH`.
- [**breaking**] Remove unused `dc_accounts_all_work_done()` ([#5384](https://github.com/deltachat/deltachat-core-rust/pull/5384)).
- deltachat-rpc-client: Add futures.
### Build system
- cmake: Build outside the source tree.
- nix: Add outputs for Android binaries.
- Add `repository` to Cargo.toml.
- python: Remove `setuptools_scm` dependency.
- Add development shell ([#5390](https://github.com/deltachat/deltachat-core-rust/pull/5390)).
### CI
- Update to Rust 1.77.0.
- Build deltachat-rpc-server for Android.
- Shorter names for deltachat-rpc-server jobs.
### Features / Changes
- Do not include provider hostname in `Message-ID`.
- Include 3 recent Message-IDs in `References` header.
- Include more entries into DNS fallback cache.
### Fixes
- Preserve upper-/lowercase of links parsed by `dehtml()` ([#5362](https://github.com/deltachat/deltachat-core-rust/pull/5362)).
- Rescan folders after changing `Config::SentboxWatch`.
- Do not ignore `Contact::get_by_id()` error in `from_field_to_contact_id()`.
- Put overridden sender name into message info.
- Don't send selfavatar in `SecureJoin` messages before contact verification ([#5354](https://github.com/deltachat/deltachat-core-rust/pull/5354)).
- Always set correct `chat_id` for `DC_EVENT_REACTIONS_CHANGED` ([#5419](https://github.com/deltachat/deltachat-core-rust/pull/5419)).
### Refactor
- Remove `MessageObject::from_message_id()`.
- jsonrpc: Add `msg_id` and `account_id` to `get_message()` errors.
- Cleanup `jobs` and `Params` relicts.
### Tests
- `Test_mvbox_sentbox_threads`: Check that sentbox gets configured after setting `sentbox_watch` ([#5105](https://github.com/deltachat/deltachat-core-rust/pull/5105)).
- Remove flaky time check from `test_list_from()`.
- Add failing test for #5418 (wrong `DC_EVENT_REACTIONS_CHANGED`)
### Miscellaneous Tasks
- Add `result` to .gitignore.
- cargo: Bump thiserror from 1.0.57 to 1.0.58.
- cargo: Bump tokio from 1.36.0 to 1.37.0.
- cargo: Bump pin-project from 1.1.4 to 1.1.5.
- cargo: Bump strum from 0.26.1 to 0.26.2.
- cargo: Bump uuid from 1.7.0 to 1.8.0.
- cargo: Bump toml from 0.8.10 to 0.8.12.
- cargo: Bump tokio-stream from 0.1.14 to 0.1.15.
- cargo: Bump smallvec from 1.13.1 to 1.13.2.
- cargo: Bump async-smtp from 0.9.0 to 0.9.1.
- cargo: Bump strum_macros from 0.26.1 to 0.26.2.
- cargo: Bump serde_json from 1.0.114 to 1.0.115.
- cargo: Bump anyhow from 1.0.80 to 1.0.81.
- cargo: Bump syn from 2.0.52 to 2.0.57.
- cargo: Bump futures-lite from 2.2.0 to 2.3.0.
- cargo: Bump axum from 0.7.4 to 0.7.5.
- cargo: Bump reqwest from 0.11.24 to 0.12.2.
- cargo: Bump backtrace from 0.3.69 to 0.3.71.
- cargo: Bump regex from 1.10.3 to 1.10.4.
- cargo: Update aho-corasick from 1.1.2 to 1.1.3.
- Update deny.toml.
## [1.136.6] - 2024-03-19
### Build system
- Add description to deltachat-rpc-server wheels.
- Read version from Cargo.toml in wheel-rpc-server.py.
### CI
- Update actions/cache from v3 to v4.
- Automate publishing of deltachat-rpc-server to PyPI.
### Documentation
- deltachat-rpc-server: Update deltachat-rpc-client URL.
### Miscellaneous Tasks
- Nix flake update.
## [1.136.5] - 2024-03-18
### Features / Changes
- Nicer summaries: prefer emoji over names
- Add `save_mime_headers` to debug info ([#5350](https://github.com/deltachat/deltachat-core-rust/pull/5350))
### Fixes
- Terminate ephemeral and location loop immediately on channel close.
- Update MemberListTimestamp when sending a group message.
- On iOS, use FILE (default) instead of MEMORY ([#5349](https://github.com/deltachat/deltachat-core-rust/pull/5349)).
- Add white background to recoded avatars ([#3787](https://github.com/deltachat/deltachat-core-rust/pull/3787)).
### Build system
- Add README to deltachat-rpc-client Python packages.
### Documentation
- deltachat-rpc-client: Document that 0 is a special value of `set_ephemeral_timer()`.
### Tests
- Test that reordering of Member added message results in square bracket error.
## [1.136.4] - 2024-03-11
### Build system
- nix: Make .#libdeltachat buildable on macOS.
- Build deltachat-rpc-server wheels with nix.
### CI
- Add workflow for automatic publishing of deltachat-rpc-client.
### Fixes
- Remove duplicate CHANGELOG entries for 1.135.1.
## [1.136.3] - 2024-03-09
### Features / Changes
- Start IMAP loop for sentbox only if it is configured ([#5105](https://github.com/deltachat/deltachat-core-rust/pull/5105)).
### Fixes
- Remove leading whitespace from Subject ([#5106](https://github.com/deltachat/deltachat-core-rust/pull/5106)).
- Create new Peerstate for unencrypted message with already known Autocrypt key, but a new address.
### Build system
- nix: Cleanup cross-compilation code.
- nix: Include SystemConfiguration framework on darwin systems.
### CI
- Wait for `build_windows` task before trying to publish it.
- Remove artifacts from npm package.
### Refactor
- Don't parse Autocrypt header for outgoing messages ([#5259](https://github.com/deltachat/deltachat-core-rust/pull/5259)).
- Remove `deduplicate_peerstates()`.
- Fix 2024-03-05 nightly clippy warnings.
### Miscellaneous Tasks
- deps: Bump mio from 0.8.8 to 0.8.11 in /fuzz.
- RPC client: Add missing constants ([#5110](https://github.com/deltachat/deltachat-core-rust/pull/5110)).
## [1.136.2] - 2024-03-05
### Build system
- Downgrade `cc` to 1.0.83 to fix build for Android.
### CI
- Update setup-node action.
## [1.136.1] - 2024-03-05
### Build system
- Revert to OpenSSL 3.1.
- Restore MSRV 1.70.0.
### Miscellaneous Tasks
- Update node constants.
## [1.136.0] - 2024-03-04
### Features / Changes
- Recognise Trash folder by name ([#5275](https://github.com/deltachat/deltachat-core-rust/pull/5275)).
- Send Chat-Group-Avatar as inline base64 ([#5253](https://github.com/deltachat/deltachat-core-rust/pull/5253)).
- Self-Reporting: Report number of protected/encrypted/unencrypted chats ([#5292](https://github.com/deltachat/deltachat-core-rust/pull/5292)).
### Fixes
- Don't send sync messages on self-{status,avatar} update from self-sent messages ([#5289](https://github.com/deltachat/deltachat-core-rust/pull/5289)).
- imap: Allow `maybe_network` to interrupt connection ratelimit.
- imap: Set connectivity to "connecting" only after ratelimit.
- Remove `Group-ID` from `Message-ID`.
- Prioritize protected `Message-ID` over `X-Microsoft-Original-Message-ID`.
### API-Changes
- Make `store_self_keypair` private.
- Add `ContextBuilder.build()` to build Context without opening.
- `dc_accounts_set_push_device_token` and `dc_get_push_state` APIs for iOS push notifications.
### Build system
- Tag armv6 wheels with tags accepted by PyPI.
- Unpin OpenSSL.
- Remove deprecated `unmaintained` field from deny.toml.
- Do not vendor OpenSSL when cross-compiling ([#5316](https://github.com/deltachat/deltachat-core-rust/pull/5316)).
- Increase MSRV to 1.74.0.
### CI
- Upgrade setup-python GitHub Action.
- Update to Rust 1.76 and fix clippy warnings.
- Build Python docs with Nix.
- Upload python docs without GH actions.
- Upload cffi docs without GH actions.
- Build c.delta.chat docs with nix.
### Other
- refactor: move more methods from Imap into Session.
- Add deltachat-time to sources.
### Refactor
- Remove Session from Imap structure.
- Merge ImapConfig into Imap.
- Get rid of ImapActionResult.
- Build contexts using ContextBuilder.
- Do not send `Secure-Join-Group` in `vg-request`.
### Tests
- Fix `test_verified_oneonone_chat_broken_by_device_change()` ([#5280](https://github.com/deltachat/deltachat-core-rust/pull/5280)).
- `get_protected_chat()`: Use FFIEventTracker instead of `dc_wait_next_msgs()` ([#5207](https://github.com/deltachat/deltachat-core-rust/pull/5207)).
- Fixup `tests/test_3_offline.py::TestOfflineAccountBasic::test_wrong_db`.
- Fix pytest compat ([#5317](https://github.com/deltachat/deltachat-core-rust/pull/5317)).
## [1.135.1] - 2024-02-20
### Features / Changes
- Sync self-avatar across devices ([#4893](https://github.com/deltachat/deltachat-core-rust/pull/4893)).
- Sync Config::Selfstatus across devices ([#4893](https://github.com/deltachat/deltachat-core-rust/pull/4893)).
- Remove webxdc sending limit.
### Fixes
- Never encrypt `{vc,vg}-request` SecureJoin messages.
- Apply Autocrypt headers if timestamp is unchanged.
- `Context::get_info`: Report displayname as "displayname" (w/o underscore).
### Tests
- Mock `SystemTime::now()` for the tests.
- Add a test on protection message sort timestamp ([#5088](https://github.com/deltachat/deltachat-core-rust/pull/5088)).
### Build system
- Add flake.nix.
- Add footer template for git-cliff.
### CI
- Update GitHub Actions `actions/upload-artifact`, `actions/download-artifact`, `actions/checkout`.
- Build deltachat-repl for Windows with nix.
- Build deltachat-rpc-server with nix.
- Try to upload deltachat-rpc-server only on release.
- Fixup node-package.yml after artifact actions upgrade.
- Update to actions/checkout@v4.
- Replace download-artifact v1 with v4.
### Refactor
- `create_keypair`: Remove unnecessary `map_err`.
- Return error with a cause when failing to export keys.
- Rename incorrectly named variables in `create_keypair`.
## [1.135.0] - 2024-02-13
### Features / Changes
- Add wildcard pattern support to provider database.
- Add device message about outgoing undecryptable messages ([#5164](https://github.com/deltachat/deltachat-core-rust/pull/5164)).
- Context::set_config(): Restart IO scheduler if needed ([#5111](https://github.com/deltachat/deltachat-core-rust/pull/5111)).
- Server_sent_unsolicited_exists(): Log folder name.
- Cache system time instead of looking at the clock several times in a row.
- Basic self-reporting ([#5129](https://github.com/deltachat/deltachat-core-rust/pull/5129)).
### Fixes
- Dehtml: Don't just truncate text when trying to decode ([#5223](https://github.com/deltachat/deltachat-core-rust/pull/5223)).
- Mark the gossip keys from the message as verified, not the ones from the db ([#5247](https://github.com/deltachat/deltachat-core-rust/pull/5247)).
- Guarantee immediate message deletion if delete_server_after == 0 ([#5201](https://github.com/deltachat/deltachat-core-rust/pull/5201)).
- Never allow a message timestamp to be a lot in the future ([#5249](https://github.com/deltachat/deltachat-core-rust/pull/5249)).
- Imap::configure_mvbox: Do select_with_uidvalidity() before return.
- ImapSession::select_or_create_folder(): Don't fail if folder is created in parallel.
- Emit ConfigSynced event on the second device.
- Create mvbox on setting mvbox_move.
- Use SystemTime instead of Instant everywhere.
- Restore database rows removed in previous release; this ensures compatibility when adding second device or importing backup and not all devices run the new core ([#5254](https://github.com/deltachat/deltachat-core-rust/pull/5254))
### Miscellaneous Tasks
- cargo: Bump image from 0.24.7 to 0.24.8.
- cargo: Bump chrono from 0.4.31 to 0.4.33.
- cargo: Bump futures-lite from 2.1.0 to 2.2.0.
- cargo: Bump pin-project from 1.1.3 to 1.1.4.
- cargo: Bump iana-time-zone from yanked 0.1.59 to 0.1.60.
- cargo: Bump smallvec from 1.11.2 to 1.13.1.
- cargo: Bump base64 from 0.21.5 to 0.21.7.
- cargo: Bump regex from 1.10.2 to 1.10.3.
- cargo: Bump libc from 0.2.151 to 0.2.153.
- cargo: Bump reqwest from 0.11.23 to 0.11.24.
- cargo: Bump axum from 0.7.3 to 0.7.4.
- cargo: Bump uuid from 1.6.1 to 1.7.0.
- cargo: Bump fast-socks5 from 0.9.2 to 0.9.5.
- cargo: Bump serde_json from 1.0.111 to 1.0.113.
- cargo: Bump syn from 2.0.46 to 2.0.48.
- cargo: Bump serde from 1.0.194 to 1.0.196.
- cargo: Bump toml from 0.8.8 to 0.8.10.
- cargo: Update to strum 0.26.
- Cargo update.
- scripts: Do not install deltachat-rpc-client twice.
### Other
- Update welcome image, thanks @paulaluap
- Merge pull request #5243 from deltachat/dependabot/cargo/pin-project-1.1.4
- Merge pull request #5241 from deltachat/dependabot/cargo/futures-lite-2.2.0
- Merge pull request #5236 from deltachat/dependabot/cargo/chrono-0.4.33
- Merge pull request #5235 from deltachat/dependabot/cargo/image-0.24.8
### Refactor
- Resultify token::exists.
### Tests
- Delete_server_after="1" should cause immediate message deletion ([#5201](https://github.com/deltachat/deltachat-core-rust/pull/5201)).
## [1.134.0] - 2024-01-31
### API-Changes
- [**breaking**] JSON-RPC: device message api now requires `Option<MessageData>` instead of `String` for the message ([#5211](https://github.com/deltachat/deltachat-core-rust/pull/5211)).
- CFFI: add `dc_accounts_background_fetch` and event `DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE`.
- JSON-RPC: add `accounts_background_fetch`.
### Features / Changes
- `Qr::check_qr()`: Accept i.delta.chat invite links ([#5217](https://github.com/deltachat/deltachat-core-rust/pull/5217)).
- Add support for IMAP METADATA, fetching `/shared/comment` and `/shared/admin` and displaying it in account info.
### Fixes
- Add tolerance for macOS and iOS changing `#` to `%23`.
- Do not drop unknown report attachments, such as TLS reports.
- Treat only "Auto-Submitted: auto-generated" messages as bot-sent ([#5213](https://github.com/deltachat/deltachat-core-rust/pull/5213)).
- `Chat::resend_msgs`: Guarantee strictly increasing time in the `Date` header.
- Delete resent messages on receiver side ([#5155](https://github.com/deltachat/deltachat-core-rust/pull/5155)).
- Fix iOS build issue.
### CI
- Add/remove necessary newlines to fix Python lint.
### Tests
- `test_import_export_online_all`: Send the message to the existing address to avoid errors ([#5220](https://github.com/deltachat/deltachat-core-rust/pull/5220)).
## [1.133.2] - 2024-01-24
### Fixes
- Downgrade OpenSSL from 3.2.0 to 3.1.4 ([#5206](https://github.com/deltachat/deltachat-core-rust/issues/5206))
- No new chats for MDNs with alias ([#5196](https://github.com/deltachat/deltachat-core-rust/issues/5196)) ([#5199](https://github.com/deltachat/deltachat-core-rust/pull/5199)).
## [1.133.1] - 2024-01-21
### API-Changes
- Add `is_bot` to cffi and jsonrpc ([#5197](https://github.com/deltachat/deltachat-core-rust/pull/5197)).
### Features / Changes
- Add system message when provider does not allow unencrypted messages ([#5195](https://github.com/deltachat/deltachat-core-rust/pull/5195)).
### Fixes
- `Chat::send_msg`: Remove encryption-related params from already sent message. This allows to send received encrypted `dc_msg_t` object to unencrypted chat, e.g. in a Python bot.
- Set message download state to Failure on IMAP errors. This avoids partially downloaded messages getting stuck in "Downloading..." state without actually being in a download queue.
- BCC-to-self even if server deletion is set to "at once". This is a workaround for SMTP servers which do not return response in time, BCC-self works as a confirmation that message was sent out successfully and does not need more retries.
- node: Run tests with native ESM modules instead of `esm` ([#5194](https://github.com/deltachat/deltachat-core-rust/pull/5194)).
- Use Quoted-Printable MIME encoding for the text part ([#3986](https://github.com/deltachat/deltachat-core-rust/pull/3986)).
### Tests
- python: Add `get_protected_chat` to testplugin.py.
## [1.133.0] - 2024-01-14
### Features / Changes
- Securejoin protocol implementation refinements
- Track forward and backward verification separately ([#5089](https://github.com/deltachat/deltachat-core-rust/pull/5089)) to avoid inconsistent states.
- Mark 1:1 chat as verified for Bob early. 1:1 chat with Alice is verified as soon as Alice's key is verified rather than at the end of the protocol.
- Put Message-ID into hidden headers and take it from there on receiver ([#4798](https://github.com/deltachat/deltachat-core-rust/pull/4798)). This works around servers which generate their own Message-ID and overwrite the one generated by Delta Chat.
- deltachat-repl: Enable INFO logging by default and add timestamps.
- Add `ConfigSynced` (`DC_EVENT_CONFIG_SYNCED`) event which is emitted when configuration is changed via synchronization message or synchronization message for configuration is sent. UI may refresh elments based on the configuration key which is a part of the event.
- Sync contact creation/rename across devices ([#5163](https://github.com/deltachat/deltachat-core-rust/pull/5163)).
- Encrypt MDNs ([#5175](https://github.com/deltachat/deltachat-core-rust/pull/5175)).
- Only try to configure non-strict TLS checks if explicitly set ([#5181](https://github.com/deltachat/deltachat-core-rust/pull/5181)).
### Build system
- Use released version of iroh 0.4.2 for "setup second device" feature.
### CI
- Update to Rust 1.75.0.
- Downgrade `chai` from 4.4.0 to 4.3.10.
### Documentation
- Add a link <https://www.ietf.org/archive/id/draft-bucksch-autoconfig-00.html> to autoconfig RFC draft.
- Update securejoin link in `standards.md` from <https://countermitm.readthedocs.io/> to <https://securejoin.readthedocs.io>.
- Restore "Constants" page in Doxygen >=1.9.8
### Fixes
- imap: Limit the rate of LOGIN attempts rather than connection attempts. This is to avoid having to wait for rate limiter right after switching from a bad or offline network to a working network while still guarding against reconnection loop.
- Do not ignore `peerstate.save_to_db()` errors.
- securejoin: Mark 1:1s as protected regardless of the Config::VerifiedOneOnOneChats.
- Delete received outgoing messages from SMTP queue ([#5115](https://github.com/deltachat/deltachat-core-rust/pull/5115)).
- imap: Fail fast on `LIST` errors to avoid busy loop when connection is lost.
- Split SMTP jobs already in `chat::create_send_msg_jobs()` ([#5115](https://github.com/deltachat/deltachat-core-rust/pull/5115)).
- Do not remove contents from unencrypted [Schleuder](https://schleuder.org/) mailing lists messages.
- Reset message error when scheduling resending ([#5119](https://github.com/deltachat/deltachat-core-rust/pull/5119)).
- Emit events more reliably when starting and stopping I/O ([#5101](https://github.com/deltachat/deltachat-core-rust/pull/5101)).
- Fix timestamp of chat protection info message for correct message ordering after restoring a backup ([#5088](https://github.com/deltachat/deltachat-core-rust/pull/5088)).
### Refactor
- sql: Recreate `config` table with UNIQUE constraint.
- sql: Recreate `keypairs` table to remove unused `addr` and `created` fields and move `is_default` flag to `config` table.
- Send `Secure-Join-Fingerprint` only in `*-request-with-auth`.
### Tests
- Test joining non-protected group.
- Test that read receipts don't degrade encryption.
- Test that changing default private key breaks backward verification.
- Test recovery from lost vc-contact-confirm.
- Use `wait_for_incoming_msg_event()` more.
## [1.132.1] - 2023-12-12
### Features / Changes
- Add "From:" to protected headers for signed-only messages.
- Sync user actions for ad-hoc groups across devices ([#5065](https://github.com/deltachat/deltachat-core-rust/pull/5065)).
### Fixes
- Add padlock to empty part if the whole message is empty.
- Renew IDLE timeout on keepalives and reduce it to 5 minutes.
- connectivity: Return false from `all_work_done()` immediately after connecting (iOS notification fix).
### API-Changes
- deltachat-jsonrpc-client: add `Account.{import,export}_self_keys`.
### CI
- Update to Rust 1.74.1.
## [1.132.0] - 2023-12-06
### Features / Changes
- Increase TCP timeouts from 30 to 60 seconds.
### Fixes
- Don't sort message creating a protected group over a protection message ([#4963](https://github.com/deltachat/deltachat-core-rust/pull/4963)).
- Do not lock accounts.toml on iOS.
- Protect groups even if some members are not verified and add `test_securejoin_after_contact_resetup` regression test.
## [1.131.9] - 2023-12-02
### API-Changes
- Remove `dc_get_http_response()`, `dc_http_response_get_mimetype()`, `dc_http_response_get_encoding()`, `dc_http_response_get_blob()`, `dc_http_response_get_size()`, `dc_http_response_unref()` and `dc_http_response_t` from cffi.
- Deprecate CFFI APIs `dc_send_reaction()`, `dc_get_msg_reactions()`, `dc_reactions_get_contacts()`, `dc_reactions_get_by_contact_id()`, `dc_reactions_unref` and `dc_reactions_t`.
- Make `Contact.is_verified()` return bool.
### Build system
- Switch from fork of iroh to iroh 0.4.2 pre-release.
### Features / Changes
- Send `Chat-Verified` headers in 1:1 chats.
- Ratelimit IMAP connections ([#4940](https://github.com/deltachat/deltachat-core-rust/pull/4940)).
- Remove receiver limit on `.xdc` size.
- Don't affect MimeMessage with "From" and secured headers from encrypted unsigned messages.
- Sync `Config::{MdnsEnabled,ShowEmails}` across devices ([#4954](https://github.com/deltachat/deltachat-core-rust/pull/4954)).
- Sync `Config::Displayname` across devices ([#4893](https://github.com/deltachat/deltachat-core-rust/pull/4893)).
- `Chat::rename_ex`: Don't send sync message if usual message is sent.
### Fixes
- Lock the database when INSERTing a webxdc update, avoid "Database is locked" errors.
- Use keyring with all private keys when decrypting a message ([#5046](https://github.com/deltachat/deltachat-core-rust/pull/5046)).
### Tests
- Make Result-returning tests produce a line number.
- Add `test_utils::sync()`.
- Test inserting lots of webxdc updates.
- Split `test_sync_alter_chat()` into smaller tests.
## [1.131.8] - 2023-11-27
### Features / Changes
- webxdc: Add unique IDs to status updates sent outside and deduplicate based on IDs.
### Fixes
- Allow IMAP servers not returning UIDNEXT on SELECT and STATUS such as mail.163.com.
- Use the correct securejoin strings used in the UI, remove old TODO ([#5047](https://github.com/deltachat/deltachat-core-rust/pull/5047)).
- Do not emit events about webxdc update events logged into debug log webxdc.
### Tests
- Check that `receive_status_update` has forward compatibility and unique webxdc IDs will be ignored by previous Delta Chat versions.
## [1.131.7] - 2023-11-24
### Fixes
- Revert "fix: check UIDNEXT with a STATUS command before going IDLE". This attempts to fix mail.163.com which has broken STATUS command.
## [1.131.6] - 2023-11-21
### Fixes
@@ -3263,3 +3851,24 @@ https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed
[1.131.4]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.3...v1.131.4
[1.131.5]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.4...v1.131.5
[1.131.6]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.5...v1.131.6
[1.131.7]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.6...v1.131.7
[1.131.8]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.7...v1.131.8
[1.131.9]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.8...v1.131.9
[1.132.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.131.9...v1.132.0
[1.132.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.132.0...v1.132.1
[1.133.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.132.1...v1.133.0
[1.133.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.133.0...v1.133.1
[1.133.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.133.1...v1.133.2
[1.134.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.133.2...v1.134.0
[1.135.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.134.0...v1.135.0
[1.135.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.135.0...v1.135.1
[1.136.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.135.1...v1.136.0
[1.136.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.0...v1.136.1
[1.136.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.1...v1.136.2
[1.136.3]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.2...v1.136.3
[1.136.4]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.3...v1.136.4
[1.136.5]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.4...v1.136.5
[1.136.6]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.5...v1.136.6
[1.137.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.136.6...v1.137.0
[1.137.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.137.0...v1.137.1
[1.137.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.137.1...v1.137.2

View File

@@ -14,24 +14,14 @@ endif()
add_custom_command(
OUTPUT
"target/release/libdeltachat.a"
"target/release/libdeltachat.${DYNAMIC_EXT}"
"target/release/pkgconfig/deltachat.pc"
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.a"
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.${DYNAMIC_EXT}"
"${CMAKE_BINARY_DIR}/target/release/pkgconfig/deltachat.pc"
COMMAND
PREFIX=${CMAKE_INSTALL_PREFIX}
LIBDIR=${CMAKE_INSTALL_FULL_LIBDIR}
INCLUDEDIR=${CMAKE_INSTALL_FULL_INCLUDEDIR}
${CARGO} build --release --no-default-features --features jsonrpc
# Build in `deltachat-ffi` directory instead of using
# `--package deltachat_ffi` to avoid feature resolver version
# "1" bug which makes `--no-default-features` affect only
# `deltachat`, but not `deltachat-ffi` package.
#
# We can't enable version "2" resolver [1] because it is not
# stable yet on rust 1.50.0.
#
# [1] https://doc.rust-lang.org/nightly/cargo/reference/features.html#resolver-version-2-command-line-flags
${CARGO} build --target-dir=${CMAKE_BINARY_DIR}/target --release --no-default-features --features jsonrpc
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/deltachat-ffi
)
@@ -39,12 +29,12 @@ add_custom_target(
lib_deltachat
ALL
DEPENDS
"target/release/libdeltachat.a"
"target/release/libdeltachat.${DYNAMIC_EXT}"
"target/release/pkgconfig/deltachat.pc"
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.a"
"${CMAKE_BINARY_DIR}/target/release/libdeltachat.${DYNAMIC_EXT}"
"${CMAKE_BINARY_DIR}/target/release/pkgconfig/deltachat.pc"
)
install(FILES "deltachat-ffi/deltachat.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES "target/release/libdeltachat.a" DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES "target/release/libdeltachat.${DYNAMIC_EXT}" DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES "target/release/pkgconfig/deltachat.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
install(FILES "${CMAKE_BINARY_DIR}/target/release/libdeltachat.a" DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES "${CMAKE_BINARY_DIR}/target/release/libdeltachat.${DYNAMIC_EXT}" DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES "${CMAKE_BINARY_DIR}/target/release/pkgconfig/deltachat.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)

View File

@@ -91,6 +91,12 @@ All errors should be handled in one of these ways:
- With `.log_err().ok()`.
- Bubbled up with `?`.
`backtrace` feature is enabled for `anyhow` crate
and `debug = 1` option is set in the test profile.
This allows to run `RUST_BACKTRACE=1 cargo test`
and get a backtrace with line numbers in resultified tests
which return `anyhow::Result`.
### Logging
For logging, use `info!`, `warn!` and `error!` macros.

1342
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,10 @@
[package]
name = "deltachat"
version = "1.131.6"
version = "1.137.2"
edition = "2021"
license = "MPL-2.0"
rust-version = "1.70"
rust-version = "1.77"
repository = "https://github.com/deltachat/deltachat-core-rust"
[profile.dev]
debug = 0
@@ -11,6 +12,10 @@ panic = 'abort'
opt-level = 1
[profile.test]
# Make anyhow `backtrace` feature useful.
# With `debug = 0` there are no line numbers in the backtrace
# produced with RUST_BACKTRACE=1.
debug = 1
opt-level = 0
# Always optimize dependencies.
@@ -28,31 +33,34 @@ strip = true
[dependencies]
deltachat_derive = { path = "./deltachat_derive" }
deltachat-time = { path = "./deltachat-time" }
format-flowed = { path = "./format-flowed" }
ratelimit = { path = "./deltachat-ratelimit" }
anyhow = "1"
async-channel = "2.0.0"
async-imap = { version = "0.9.1", default-features = false, features = ["runtime-tokio"] }
async-imap = { version = "0.9.7", default-features = false, features = ["runtime-tokio"] }
async-native-tls = { version = "0.5", default-features = false, features = ["runtime-tokio"] }
async-smtp = { version = "0.9", default-features = false, features = ["runtime-tokio"] }
async_zip = { version = "0.0.12", default-features = false, features = ["deflate", "fs"] }
backtrace = "0.3"
base64 = "0.21"
brotli = { version = "3.4", default-features=false, features = ["std"] }
brotli = { version = "4", default-features=false, features = ["std"] }
bitflags = "1.3"
bstr = { version = "1.4.0", default-features=false, features = ["std", "alloc"] }
chrono = { version = "0.4", default-features=false, features = ["clock", "std"] }
email = { git = "https://github.com/deltachat/rust-email", branch = "master" }
encoded-words = { git = "https://github.com/async-email/encoded-words", branch = "master" }
escaper = "0.1"
fast-socks5 = "0.8"
fd-lock = "3.0.11"
fast-socks5 = "0.9"
fd-lock = "4"
futures = "0.3"
futures-lite = "2.0.0"
futures-lite = "2.3.0"
hex = "0.4.0"
hickory-resolver = "0.24"
humansize = "2"
image = { version = "0.24.7", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] }
iroh = { git = "https://github.com/deltachat/iroh", branch = "0.4-update-quic", default-features = false }
image = { version = "0.25.1", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] }
iroh = { version = "0.4.2", default-features = false }
kamadak-exif = "0.5"
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
libc = "0.2"
@@ -64,45 +72,55 @@ num-traits = "0.2"
once_cell = "1.18.0"
percent-encoding = "2.3"
parking_lot = "0.12"
pgp = { version = "0.10", default-features = false }
pgp = { version = "0.11", default-features = false }
pin-project = "1"
pretty_env_logger = { version = "0.5", optional = true }
qrcodegen = "1.7.0"
quick-xml = "0.31"
quoted_printable = "0.5"
rand = "0.8"
regex = "1.9"
reqwest = { version = "0.11.20", features = ["json"] }
rusqlite = { version = "0.30", features = ["sqlcipher"] }
regex = "1.10"
reqwest = { version = "0.12.2", features = ["json"] }
rusqlite = { version = "0.31", features = ["sqlcipher"] }
rust-hsluv = "0.1"
sanitize-filename = "0.5"
serde_json = "1.0"
serde_json = "1"
serde = { version = "1.0", features = ["derive"] }
sha-1 = "0.10"
sha2 = "0.10"
smallvec = "1"
strum = "0.25"
strum_macros = "0.25"
strum = "0.26"
strum_macros = "0.26"
tagger = "4.3.4"
textwrap = "0.16.0"
textwrap = "0.16.1"
thiserror = "1"
tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] }
tokio-io-timeout = "1.2.0"
tokio-stream = { version = "0.1.14", features = ["fs"] }
tokio-stream = { version = "0.1.15", features = ["fs"] }
tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar
tokio-util = "0.7.9"
toml = "0.8"
url = "2"
uuid = { version = "1", features = ["serde", "v4"] }
# Pin OpenSSL to 3.1 releases.
# OpenSSL 3.2 has a regression tracked at <https://github.com/openssl/openssl/issues/23376>
# which results in broken `deltachat-rpc-server` binaries when cross-compiled using Zig toolchain.
# See <https://github.com/deltachat/deltachat-core-rust/issues/5206> for Delta Chat issue.
# According to <https://www.openssl.org/policies/releasestrat.html>
# 3.1 branch will be supported until 2025-03-14.
openssl-src = "~300.1"
[dev-dependencies]
ansi_term = "0.12.0"
anyhow = { version = "1", features = ["backtrace"] } # Enable `backtrace` feature in tests.
criterion = { version = "0.5.1", features = ["async_tokio"] }
futures-lite = "2.0.0"
futures-lite = "2.3.0"
log = "0.4"
pretty_env_logger = "0.5"
proptest = { version = "1", default-features = false, features = ["std"] }
tempfile = "3"
testdir = "0.8.0"
testdir = "0.9.0"
tokio = { version = "1", features = ["parking_lot", "rt-multi-thread", "macros"] }
pretty_assertions = "1.3.0"
@@ -114,6 +132,7 @@ members = [
"deltachat-rpc-server",
"deltachat-ratelimit",
"deltachat-repl",
"deltachat-time",
"format-flowed",
]

View File

@@ -27,7 +27,7 @@ $ curl https://sh.rustup.rs -sSf | sh
Compile and run Delta Chat Core command line utility, using `cargo`:
```
$ RUST_LOG=deltachat_repl=info cargo run -p deltachat-repl -- ~/deltachat-db
$ cargo run -p deltachat-repl -- ~/deltachat-db
```
where ~/deltachat-db is the database file. Delta Chat will create it if it does not exist.
@@ -121,7 +121,7 @@ $ cargo build -p deltachat_ffi --release
- `DCC_MIME_DEBUG`: if set outgoing and incoming message will be printed
- `RUST_LOG=deltachat_repl=info,async_imap=trace,async_smtp=trace`: enable IMAP and
- `RUST_LOG=async_imap=trace,async_smtp=trace`: enable IMAP and
SMTP tracing in addition to info messages.
### Expensive tests

View File

@@ -9,13 +9,16 @@ For example, to release version 1.116.0 of the core, do the following steps.
3. Update the changelog: `git cliff --unreleased --tag 1.116.0 --prepend CHANGELOG.md` or `git cliff -u -t 1.116.0 -p CHANGELOG.md`.
4. Update the version by running `scripts/set_core_version.py 1.116.0`.
4. add a link to compare previous with current version to the end of CHANGELOG.md:
`[1.116.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.115.2...v1.116.0`
5. Commit the changes as `chore(release): prepare for 1.116.0`.
5. Update the version by running `scripts/set_core_version.py 1.116.0`.
6. Commit the changes as `chore(release): prepare for 1.116.0`.
Optionally, use a separate branch like `prep-1.116.0` for this commit and open a PR for review.
6. Tag the release: `git tag -a v1.116.0`.
7. Tag the release: `git tag -a v1.116.0`.
7. Push the release tag: `git push origin v1.116.0`.
8. Push the release tag: `git push origin v1.116.0`.
8. Create a GitHub release: `gh release create v1.116.0 -n ''`.
9. Create a GitHub release: `gh release create v1.116.0 -n ''`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View File

@@ -77,3 +77,17 @@ body = """
"""
# remove the leading and trailing whitespace from the template
trim = true
footer = """
{% for release in releases -%}
{% if release.version -%}
{% if release.previous.version -%}
[{{ release.version | trim_start_matches(pat="v") }}]: \
https://github.com/deltachat/deltachat-core-rust\
/compare/{{ release.previous.version }}..{{ release.version }}
{% endif -%}
{% else -%}
[unreleased]: https://github.com/deltachat/deltachat-core-rust\
/compare/{{ release.previous.version }}..HEAD
{% endif -%}
{% endfor %}
"""

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat_ffi"
version = "1.131.6"
version = "1.137.2"
description = "Deltachat FFI"
edition = "2018"
readme = "README.md"

View File

@@ -9,7 +9,7 @@
<tab type="hierarchy" visible="no" title="" intro=""/>
<tab type="classmembers" visible="no" title="" intro=""/>
</tab>
<tab type="modules" visible="yes" title="Constants" intro="Here is a list of constants:"/>
<tab type="topics" visible="yes" title="Constants" intro="Here is a list of constants:"/>
<tab type="pages" visible="yes" title="" intro=""/>
<tab type="namespaces" visible="yes" title="">
<tab type="namespacelist" visible="yes" title="" intro=""/>

View File

@@ -25,7 +25,6 @@ typedef struct _dc_event dc_event_t;
typedef struct _dc_event_emitter dc_event_emitter_t;
typedef struct _dc_jsonrpc_instance dc_jsonrpc_instance_t;
typedef struct _dc_backup_provider dc_backup_provider_t;
typedef struct _dc_http_response dc_http_response_t;
// Alias for backwards compatibility, use dc_event_emitter_t instead.
typedef struct _dc_event_emitter dc_accounts_event_emitter_t;
@@ -424,19 +423,16 @@ char* dc_get_blobdir (const dc_context_t* context);
* Sending messages to self is needed for a proper multi-account setup,
* however, on the other hand, may lead to unwanted notifications in non-delta clients.
* - `sentbox_watch`= 1=watch `Sent`-folder for changes,
* 0=do not watch the `Sent`-folder (default),
* changes require restarting IO by calling dc_stop_io() and then dc_start_io().
* 0=do not watch the `Sent`-folder (default).
* - `mvbox_move` = 1=detect chat messages,
* move them to the `DeltaChat` folder,
* and watch the `DeltaChat` folder for updates (default),
* 0=do not move chat-messages
* changes require restarting IO by calling dc_stop_io() and then dc_start_io().
* - `only_fetch_mvbox` = 1=Do not fetch messages from folders other than the
* `DeltaChat` folder. Messages will still be fetched from the
* spam folder and `sendbox_watch` will also still be respected
* if enabled.
* 0=watch all folders normally (default)
* changes require restarting IO by calling dc_stop_io() and then dc_start_io().
* - `show_emails` = DC_SHOW_EMAILS_OFF (0)=
* show direct replies to chats only,
* DC_SHOW_EMAILS_ACCEPTED_CONTACTS (1)=
@@ -690,8 +686,25 @@ int dc_get_connectivity (dc_context_t* context);
char* dc_get_connectivity_html (dc_context_t* context);
#define DC_PUSH_NOT_CONNECTED 0
#define DC_PUSH_HEARTBEAT 1
#define DC_PUSH_CONNECTED 2
/**
* Get the current push notification state.
* One of:
* - DC_PUSH_NOT_CONNECTED
* - DC_PUSH_HEARTBEAT
* - DC_PUSH_CONNECTED
*
* @memberof dc_context_t
* @param context The context object.
* @return Push notification state.
*/
int dc_get_push_state (dc_context_t* context);
/**
* Standalone version of dc_accounts_all_work_done().
* Only used by the python tests.
*/
int dc_all_work_done (dc_context_t* context);
@@ -1112,6 +1125,7 @@ uint32_t dc_send_videochat_invitation (dc_context_t* context, uint32_t chat_id);
* received overrides all previously received reactions. It is
* possible to remove all reactions by sending an empty string.
*
* @deprecated 2023-11-27, use jsonrpc method `send_reaction` instead
* @memberof dc_context_t
* @param context The context object.
* @param msg_id ID of the message you react to.
@@ -1124,6 +1138,7 @@ uint32_t dc_send_reaction (dc_context_t* context, uint32_t msg_id, char *reactio
/**
* Get a structure with reactions to the message.
*
* @deprecated 2023-11-27, use jsonrpc method `get_message_reactions` instead
* @memberof dc_context_t
* @param context The context object.
* @param msg_id The message ID to get reactions for.
@@ -3083,23 +3098,6 @@ dc_context_t* dc_accounts_get_selected_account (dc_accounts_t* accounts);
int dc_accounts_select_account (dc_accounts_t* accounts, uint32_t account_id);
/**
* This is meant especially for iOS, because iOS needs to tell the system when its background work is done.
*
* iOS can:
* - call dc_start_io() (in case IO was not running)
* - call dc_maybe_network()
* - while dc_accounts_all_work_done() returns false:
* - Wait for #DC_EVENT_CONNECTIVITY_CHANGED
*
* @memberof dc_accounts_t
* @param accounts The account manager as created by dc_accounts_new().
* @return Whether all accounts finished their background work.
* #DC_EVENT_CONNECTIVITY_CHANGED will be sent when this turns to true.
*/
int dc_accounts_all_work_done (dc_accounts_t* accounts);
/**
* Start job and IMAP/SMTP tasks for all accounts managed by the account manager.
* If IO is already running, nothing happens.
@@ -3150,6 +3148,33 @@ void dc_accounts_maybe_network (dc_accounts_t* accounts);
void dc_accounts_maybe_network_lost (dc_accounts_t* accounts);
/**
* Perform a background fetch for all accounts in parallel with a timeout.
* Pauses the scheduler, fetches messages from imap and then resumes the scheduler.
*
* dc_accounts_background_fetch() was created for the iOS Background fetch.
*
* The `DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE` event is emitted at the end
* even in case of timeout, unless the function fails and returns 0.
* Process all events until you get this one and you can safely return to the background
* without forgetting to create notifications caused by timing race conditions.
*
* @memberof dc_accounts_t
* @param timeout The timeout in seconds
* @return Return 1 if DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE was emitted and 0 otherwise.
*/
int dc_accounts_background_fetch (dc_accounts_t* accounts, uint64_t timeout);
/**
* Sets device token for Apple Push Notification service.
* Returns immediately.
*
* @memberof dc_accounts_t
* @param token Hexadecimal device token
*/
void dc_accounts_set_push_device_token (dc_accounts_t* accounts, const char *token);
/**
* Create the event emitter that is used to receive events.
*
@@ -4396,6 +4421,9 @@ int dc_msg_is_info (const dc_msg_t* msg);
* Currently, the following types are defined:
* - DC_INFO_PROTECTION_ENABLED (11) - Info-message for "Chat is now protected"
* - DC_INFO_PROTECTION_DISABLED (12) - Info-message for "Chat is no longer protected"
* - DC_INFO_INVALID_UNENCRYPTED_MAIL (13) - Info-message for "Provider requires end-to-end encryption which is not setup yet",
* the UI should change the corresponding string using #DC_STR_INVALID_UNENCRYPTED_MAIL
* and also offer a way to fix the encryption, eg. by a button offering a QR scan
*
* Even when you display an icon,
* you should still display the text of the informational message using dc_msg_get_text()
@@ -4422,6 +4450,7 @@ int dc_msg_get_info_type (const dc_msg_t* msg);
#define DC_INFO_EPHEMERAL_TIMER_CHANGED 10
#define DC_INFO_PROTECTION_ENABLED 11
#define DC_INFO_PROTECTION_DISABLED 12
#define DC_INFO_INVALID_UNENCRYPTED_MAIL 13
#define DC_INFO_WEBXDC_INFO_MESSAGE 32
/**
@@ -5067,6 +5096,15 @@ int dc_contact_is_blocked (const dc_contact_t* contact);
*/
int dc_contact_is_verified (dc_contact_t* contact);
/**
* Returns whether contact is a bot.
*
* @memberof dc_contact_t
* @param contact The contact object.
* @return 0 if the contact is not a bot, 1 otherwise.
*/
int dc_contact_is_bot (dc_contact_t* contact);
/**
* Return the contact ID that verified a contact.
@@ -5185,72 +5223,6 @@ int dc_provider_get_status (const dc_provider_t* prov
void dc_provider_unref (dc_provider_t* provider);
/**
* Return an HTTP(S) GET response.
* This function can be used to download remote content for HTML emails.
*
* @memberof dc_context_t
* @param context The context object to take proxy settings from.
* @param url HTTP or HTTPS URL.
* @return The response must be released using dc_http_response_unref() after usage.
* NULL is returned on errors.
*/
dc_http_response_t* dc_get_http_response (const dc_context_t* context, const char* url);
/**
* @class dc_http_response_t
*
* An object containing an HTTP(S) GET response.
* Created by dc_get_http_response().
*/
/**
* Returns HTTP response MIME type as a string, e.g. "text/plain" or "text/html".
*
* @memberof dc_http_response_t
* @param response HTTP response as returned by dc_get_http_response().
* @return The string which must be released using dc_str_unref() after usage. May be NULL.
*/
char* dc_http_response_get_mimetype (const dc_http_response_t* response);
/**
* Returns HTTP response encoding, e.g. "utf-8".
*
* @memberof dc_http_response_t
* @param response HTTP response as returned by dc_get_http_response().
* @return The string which must be released using dc_str_unref() after usage. May be NULL.
*/
char* dc_http_response_get_encoding (const dc_http_response_t* response);
/**
* Returns HTTP response contents.
*
* @memberof dc_http_response_t
* @param response HTTP response as returned by dc_get_http_response().
* @return The blob which must be released using dc_str_unref() after usage. NULL is never returned.
*/
uint8_t* dc_http_response_get_blob (const dc_http_response_t* response);
/**
* Returns HTTP response content size.
*
* @memberof dc_http_response_t
* @param response HTTP response as returned by dc_get_http_response().
* @return The blob size.
*/
size_t dc_http_response_get_size (const dc_http_response_t* response);
/**
* Free an HTTP response object.
*
* @memberof dc_http_response_t
* @param response HTTP response as returned by dc_get_http_response().
*/
void dc_http_response_unref (const dc_http_response_t* response);
/**
* @class dc_lot_t
*
@@ -5350,6 +5322,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
/**
* @class dc_reactions_t
* @deprecated 2023-11-27, use jsonrpc method `get_message_reactions` instead
*
* An object representing all reactions for a single message.
*/
@@ -5357,6 +5330,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
/**
* Returns array of contacts which reacted to the given message.
*
* @deprecated 2023-11-27, use jsonrpc method `get_message_reactions` instead
* @memberof dc_reactions_t
* @param reactions The object containing message reactions.
* @return array of contact IDs. Use dc_array_get_cnt() to get array length and
@@ -5368,6 +5342,7 @@ dc_array_t* dc_reactions_get_contacts(dc_reactions_t* reactions);
/**
* Returns a string containing space-separated reactions of a single contact.
*
* @deprecated 2023-11-27, use jsonrpc method `get_message_reactions` instead
* @memberof dc_reactions_t
* @param reactions The object containing message reactions.
* @param contact_id ID of the contact.
@@ -5383,6 +5358,7 @@ char* dc_reactions_get_by_contact_id(dc_reactions_t* reactions, uint32
*
* Reactions objects are created by dc_get_msg_reactions().
*
* @deprecated 2023-11-27
* @memberof dc_reactions_t
* @param reactions The object to free.
* If NULL is given, nothing is done.
@@ -6080,10 +6056,12 @@ void dc_event_unref(dc_event_t* event);
* Downloading a bunch of messages just finished. This is an
* event to allow the UI to only show one notification per message bunch,
* instead of cluttering the user with many notifications.
* For each of the msg_ids, an additional #DC_EVENT_INCOMING_MSG event was emitted before.
* UI may store #DC_EVENT_INCOMING_MSG events
* and display notifications for all messages at once
* when this event arrives.
*
* @param data1 0
* @param data2 (char*) msg_ids, a json object with the message ids.
* @param data2 0
*/
#define DC_EVENT_INCOMING_MSG_BUNCH 2006
@@ -6267,6 +6245,18 @@ void dc_event_unref(dc_event_t* event);
#define DC_EVENT_SELFAVATAR_CHANGED 2110
/**
* A multi-device synced config value changed. Maybe the app needs to refresh smth. For uniformity
* this is emitted on the source device too. The value isn't reported, otherwise it would be logged
* which might not be good for privacy. You can get the new value with
* `dc_get_config(context, data2)`.
*
* @param data1 0
* @param data2 (char*) Configuration key.
*/
#define DC_EVENT_CONFIG_SYNCED 2111
/**
* webxdc status update received.
* To get the received status update, use dc_get_webxdc_status_updates() with
@@ -6291,6 +6281,16 @@ void dc_event_unref(dc_event_t* event);
#define DC_EVENT_WEBXDC_INSTANCE_DELETED 2121
/**
* Tells that the Background fetch was completed (or timed out).
*
* This event acts as a marker, when you reach this event you can be sure
* that all events emitted during the background fetch were processed.
*
* This event is only emitted by the account manager
*/
#define DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE 2200
/**
* @}
@@ -6621,7 +6621,7 @@ void dc_event_unref(dc_event_t* event);
/// - %1$s will be replaced by the name of the verified contact
#define DC_STR_CONTACT_VERIFIED 35
/// "Cannot verify %1$s."
/// "Cannot establish guaranteed end-to-end encryption with %1$s."
///
/// Used in status messages.
/// - %1$s will be replaced by the name of the contact that cannot be verified
@@ -7059,6 +7059,8 @@ void dc_event_unref(dc_event_t* event);
/// "You added member %1$s."
///
/// Used in status messages.
///
/// `%1$s` will be replaced by the added member's name.
#define DC_STR_ADD_MEMBER_BY_YOU 128
/// "Member %1$s added by %2$s."
@@ -7280,6 +7282,37 @@ void dc_event_unref(dc_event_t* event);
/// Used as the first info messages in newly created groups.
#define DC_STR_NEW_GROUP_SEND_FIRST_MESSAGE 172
/// "Member %1$s added."
///
/// Used as info messages.
///
/// `%1$s` will be replaced by the added member's name.
#define DC_STR_MESSAGE_ADD_MEMBER 173
/// "Your email provider %1$s requires end-to-end encryption which is not setup yet."
///
/// Used as info messages when a message cannot be sent because it cannot be encrypted.
///
/// `%1$s` will be replaced by the provider's domain.
#define DC_STR_INVALID_UNENCRYPTED_MAIL 174
/// "You reacted %1$s to '%2$s'"
///
/// `%1$s` will be replaced by the reaction, usually an emoji
/// `%2$s` will be replaced by the summary of the message the reaction refers to
///
/// Used in summaries.
#define DC_STR_YOU_REACTED 176
/// "%1$s reacted %2$s to '%3$s'"
///
/// `%1$s` will be replaced by the name the contact who reacted
/// `%2$s` will be replaced by the reaction, usually an emoji
/// `%3$s` will be replaced by the summary of the message the reaction refers to
///
/// Used in summaries.
#define DC_STR_REACTED_BY 177
/**
* @}
*/

View File

@@ -26,16 +26,14 @@ use anyhow::Context as _;
use deltachat::chat::{ChatId, ChatVisibility, MessageListOptions, MuteDuration, ProtectionStatus};
use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
use deltachat::contact::{Contact, ContactId, Origin};
use deltachat::context::Context;
use deltachat::context::{Context, ContextBuilder};
use deltachat::ephemeral::Timer as EphemeralTimer;
use deltachat::imex::BackupProvider;
use deltachat::key::preconfigure_keypair;
use deltachat::message::MsgId;
use deltachat::net::read_url_blob;
use deltachat::qr_code_generator::{generate_backup_qr, get_securejoin_qr_svg};
use deltachat::reaction::{get_msg_reactions, send_reaction, Reactions};
use deltachat::stock_str::StockMessage;
use deltachat::stock_str::StockStrings;
use deltachat::webxdc::StatusUpdateSerial;
use deltachat::*;
use deltachat::{accounts::Accounts, log::LogExt};
@@ -105,12 +103,11 @@ pub unsafe extern "C" fn dc_context_new(
let ctx = if blobdir.is_null() || *blobdir == 0 {
// generate random ID as this functionality is not yet available on the C-api.
let id = rand::thread_rng().gen();
block_on(Context::new(
as_path(dbfile),
id,
Events::new(),
StockStrings::new(),
))
block_on(
ContextBuilder::new(as_path(dbfile).to_path_buf())
.with_id(id)
.open(),
)
} else {
eprintln!("blobdir can not be defined explicitly anymore");
return ptr::null_mut();
@@ -134,12 +131,11 @@ pub unsafe extern "C" fn dc_context_new_closed(dbfile: *const libc::c_char) -> *
}
let id = rand::thread_rng().gen();
match block_on(Context::new_closed(
as_path(dbfile),
id,
Events::new(),
StockStrings::new(),
)) {
match block_on(
ContextBuilder::new(as_path(dbfile).to_path_buf())
.with_id(id)
.build(),
) {
Ok(context) => Box::into_raw(Box::new(context)),
Err(err) => {
eprintln!("failed to create context: {err:#}");
@@ -388,7 +384,7 @@ pub unsafe extern "C" fn dc_get_connectivity(context: *const dc_context_t) -> li
return 0;
}
let ctx = &*context;
block_on(async move { ctx.get_connectivity().await as u32 as libc::c_int })
block_on(ctx.get_connectivity()) as u32 as libc::c_int
}
#[no_mangle]
@@ -411,6 +407,16 @@ pub unsafe extern "C" fn dc_get_connectivity_html(
})
}
#[no_mangle]
pub unsafe extern "C" fn dc_get_push_state(context: *const dc_context_t) -> libc::c_int {
if context.is_null() {
eprintln!("ignoring careless call to dc_get_push_state()");
return 0;
}
let ctx = &*context;
block_on(ctx.push_state()) as libc::c_int
}
#[no_mangle]
pub unsafe extern "C" fn dc_all_work_done(context: *mut dc_context_t) -> libc::c_int {
if context.is_null() {
@@ -557,8 +563,10 @@ pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int
EventType::SecurejoinJoinerProgress { .. } => 2061,
EventType::ConnectivityChanged => 2100,
EventType::SelfavatarChanged => 2110,
EventType::ConfigSynced { .. } => 2111,
EventType::WebxdcStatusUpdate { .. } => 2120,
EventType::WebxdcInstanceDeleted { .. } => 2121,
EventType::AccountsBackgroundFetchDone => 2200,
}
}
@@ -584,8 +592,10 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
| EventType::Error(_)
| EventType::ConnectivityChanged
| EventType::SelfavatarChanged
| EventType::ConfigSynced { .. }
| EventType::IncomingMsgBunch { .. }
| EventType::ErrorSelfNotInGroup(_) => 0,
| EventType::ErrorSelfNotInGroup(_)
| EventType::AccountsBackgroundFetchDone => 0,
EventType::MsgsChanged { chat_id, .. }
| EventType::ReactionsChanged { chat_id, .. }
| EventType::IncomingMsg { chat_id, .. }
@@ -644,7 +654,9 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
| EventType::ConnectivityChanged
| EventType::WebxdcInstanceDeleted { .. }
| EventType::IncomingMsgBunch { .. }
| EventType::SelfavatarChanged => 0,
| EventType::SelfavatarChanged
| EventType::AccountsBackgroundFetchDone
| EventType::ConfigSynced { .. } => 0,
EventType::ChatModified(_) => 0,
EventType::MsgsChanged { msg_id, .. }
| EventType::ReactionsChanged { msg_id, .. }
@@ -706,7 +718,9 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
| EventType::SelfavatarChanged
| EventType::WebxdcStatusUpdate { .. }
| EventType::WebxdcInstanceDeleted { .. }
| EventType::ChatEphemeralTimerModified { .. } => ptr::null_mut(),
| EventType::AccountsBackgroundFetchDone
| EventType::ChatEphemeralTimerModified { .. }
| EventType::IncomingMsgBunch { .. } => ptr::null_mut(),
EventType::ConfigureProgress { comment, .. } => {
if let Some(comment) = comment {
comment.to_c_string().unwrap_or_default().into_raw()
@@ -718,11 +732,10 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
let data2 = file.to_c_string().unwrap_or_default();
data2.into_raw()
}
EventType::IncomingMsgBunch { msg_ids } => serde_json::to_string(msg_ids)
.unwrap_or_default()
.to_c_string()
.unwrap_or_default()
.into_raw(),
EventType::ConfigSynced { key } => {
let data2 = key.to_string().to_c_string().unwrap_or_default();
data2.into_raw()
}
}
}
@@ -4113,10 +4126,26 @@ pub unsafe extern "C" fn dc_contact_is_verified(contact: *mut dc_contact_t) -> l
let ffi_contact = &*contact;
let ctx = &*ffi_contact.context;
block_on(ffi_contact.contact.is_verified(ctx))
if block_on(ffi_contact.contact.is_verified(ctx))
.context("is_verified failed")
.log_err(ctx)
.unwrap_or_default() as libc::c_int
.unwrap_or_default()
{
// Return value is essentially a boolean,
// but we return 2 for true for backwards compatibility.
2
} else {
0
}
}
#[no_mangle]
pub unsafe extern "C" fn dc_contact_is_bot(contact: *mut dc_contact_t) -> libc::c_int {
if contact.is_null() {
eprintln!("ignoring careless call to dc_contact_is_bot()");
return 0;
}
(*contact).contact.is_bot() as libc::c_int
}
#[no_mangle]
@@ -4590,96 +4619,6 @@ pub unsafe extern "C" fn dc_provider_unref(provider: *mut dc_provider_t) {
// this may change once we start localizing string.
}
// dc_http_response_t
pub type dc_http_response_t = net::HttpResponse;
#[no_mangle]
pub unsafe extern "C" fn dc_get_http_response(
context: *const dc_context_t,
url: *const libc::c_char,
) -> *mut dc_http_response_t {
if context.is_null() || url.is_null() {
eprintln!("ignoring careless call to dc_get_http_response()");
return ptr::null_mut();
}
let context = &*context;
let url = to_string_lossy(url);
if let Ok(response) = block_on(read_url_blob(context, &url))
.context("read_url_blob")
.log_err(context)
{
Box::into_raw(Box::new(response))
} else {
ptr::null_mut()
}
}
#[no_mangle]
pub unsafe extern "C" fn dc_http_response_get_mimetype(
response: *const dc_http_response_t,
) -> *mut libc::c_char {
if response.is_null() {
eprintln!("ignoring careless call to dc_http_response_get_mimetype()");
return ptr::null_mut();
}
let response = &*response;
response.mimetype.strdup()
}
#[no_mangle]
pub unsafe extern "C" fn dc_http_response_get_encoding(
response: *const dc_http_response_t,
) -> *mut libc::c_char {
if response.is_null() {
eprintln!("ignoring careless call to dc_http_response_get_encoding()");
return ptr::null_mut();
}
let response = &*response;
response.encoding.strdup()
}
#[no_mangle]
pub unsafe extern "C" fn dc_http_response_get_blob(
response: *const dc_http_response_t,
) -> *mut libc::c_char {
if response.is_null() {
eprintln!("ignoring careless call to dc_http_response_get_blob()");
return ptr::null_mut();
}
let response = &*response;
let blob_len = response.blob.len();
let ptr = libc::malloc(blob_len);
libc::memcpy(ptr, response.blob.as_ptr() as *mut libc::c_void, blob_len);
ptr as *mut libc::c_char
}
#[no_mangle]
pub unsafe extern "C" fn dc_http_response_get_size(
response: *const dc_http_response_t,
) -> libc::size_t {
if response.is_null() {
eprintln!("ignoring careless call to dc_http_response_get_size()");
return 0;
}
let response = &*response;
response.blob.len()
}
#[no_mangle]
pub unsafe extern "C" fn dc_http_response_unref(response: *mut dc_http_response_t) {
if response.is_null() {
eprintln!("ignoring careless call to dc_http_response_unref()");
return;
}
drop(Box::from_raw(response));
}
// -- Accounts
/// Reader-writer lock wrapper for accounts manager to guarantee thread safety when using
@@ -4912,16 +4851,6 @@ pub unsafe extern "C" fn dc_accounts_get_all(accounts: *mut dc_accounts_t) -> *m
Box::into_raw(Box::new(array))
}
#[no_mangle]
pub unsafe extern "C" fn dc_accounts_all_work_done(accounts: *mut dc_accounts_t) -> libc::c_int {
if accounts.is_null() {
eprintln!("ignoring careless call to dc_accounts_all_work_done()");
return 0;
}
let accounts = &*accounts;
block_on(async move { accounts.read().await.all_work_done().await as libc::c_int })
}
#[no_mangle]
pub unsafe extern "C" fn dc_accounts_start_io(accounts: *mut dc_accounts_t) {
if accounts.is_null() {
@@ -4966,6 +4895,49 @@ pub unsafe extern "C" fn dc_accounts_maybe_network_lost(accounts: *mut dc_accoun
block_on(async move { accounts.write().await.maybe_network_lost().await });
}
#[no_mangle]
pub unsafe extern "C" fn dc_accounts_background_fetch(
accounts: *mut dc_accounts_t,
timeout_in_seconds: u64,
) -> libc::c_int {
if accounts.is_null() || timeout_in_seconds <= 2 {
eprintln!("ignoring careless call to dc_accounts_background_fetch()");
return 0;
}
let accounts = &*accounts;
block_on(async move {
let accounts = accounts.read().await;
accounts
.background_fetch(Duration::from_secs(timeout_in_seconds))
.await;
});
1
}
#[no_mangle]
pub unsafe extern "C" fn dc_accounts_set_push_device_token(
accounts: *mut dc_accounts_t,
token: *const libc::c_char,
) {
if accounts.is_null() {
eprintln!("ignoring careless call to dc_accounts_set_push_device_token()");
return;
}
let accounts = &*accounts;
let token = to_string_lossy(token);
block_on(async move {
let mut accounts = accounts.write().await;
if let Err(err) = accounts.set_push_device_token(&token).await {
accounts.emit_event(EventType::Error(format!(
"Failed to set notify token: {err:#}."
)));
}
})
}
#[no_mangle]
pub unsafe extern "C" fn dc_accounts_get_event_emitter(
accounts: *mut dc_accounts_t,

View File

@@ -1,10 +1,11 @@
[package]
name = "deltachat-jsonrpc"
version = "1.131.6"
version = "1.137.2"
description = "DeltaChat JSON-RPC API"
edition = "2021"
default-run = "deltachat-jsonrpc-server"
license = "MPL-2.0"
repository = "https://github.com/deltachat/deltachat-core-rust"
[[bin]]
name = "deltachat-jsonrpc-server"
@@ -17,24 +18,24 @@ deltachat = { path = ".." }
num-traits = "0.2"
schemars = "0.8.13"
serde = { version = "1.0", features = ["derive"] }
tempfile = "3.8.0"
tempfile = "3.10.1"
log = "0.4"
async-channel = { version = "2.0.0" }
futures = { version = "0.3.28" }
serde_json = "1.0.105"
futures = { version = "0.3.30" }
serde_json = "1"
yerpc = { version = "0.5.2", features = ["anyhow_expose", "openrpc"] }
typescript-type-def = { version = "0.5.8", features = ["json_value"] }
tokio = { version = "1.33.0" }
tokio = { version = "1.37.0" }
sanitize-filename = "0.5"
walkdir = "2.3.3"
walkdir = "2.5.0"
base64 = "0.21"
# optional dependencies
axum = { version = "0.6.20", optional = true, features = ["ws"] }
axum = { version = "0.7", optional = true, features = ["ws"] }
env_logger = { version = "0.10.0", optional = true }
[dev-dependencies]
tokio = { version = "1.33.0", features = ["full", "rt-multi-thread"] }
tokio = { version = "1.37.0", features = ["full", "rt-multi-thread"] }
[features]

View File

@@ -231,13 +231,27 @@ impl CommandApi {
Ok(())
}
/// Performs a background fetch for all accounts in parallel with a timeout.
///
/// The `AccountsBackgroundFetchDone` event is emitted at the end even in case of timeout.
/// Process all events until you get this one and you can safely return to the background
/// without forgetting to create notifications caused by timing race conditions.
async fn accounts_background_fetch(&self, timeout_in_seconds: f64) -> Result<()> {
self.accounts
.write()
.await
.background_fetch(std::time::Duration::from_secs_f64(timeout_in_seconds))
.await;
Ok(())
}
// ---------------------------------------------
// Methods that work on individual accounts
// ---------------------------------------------
/// Starts background tasks for a single account.
async fn start_io(&self, account_id: u32) -> Result<()> {
let mut ctx = self.get_context(account_id).await?;
let ctx = self.get_context(account_id).await?;
ctx.start_io().await;
Ok(())
}
@@ -311,6 +325,11 @@ impl CommandApi {
ctx.get_info().await
}
async fn draft_self_report(&self, account_id: u32) -> Result<u32> {
let ctx = self.get_context(account_id).await?;
Ok(ctx.draft_self_report().await?.to_u32())
}
/// Sets the given configuration key.
async fn set_config(&self, account_id: u32, key: String, value: Option<String>) -> Result<()> {
let ctx = self.get_context(account_id).await?;
@@ -383,7 +402,7 @@ impl CommandApi {
/// Configures this account with the currently set parameters.
/// Setup the credential config before calling this.
async fn configure(&self, account_id: u32) -> Result<()> {
let mut ctx = self.get_context(account_id).await?;
let ctx = self.get_context(account_id).await?;
ctx.stop_io().await;
let result = ctx.configure().await;
if result.is_err() {
@@ -896,19 +915,35 @@ impl CommandApi {
.to_u32())
}
// for now only text messages, because we only used text messages in desktop thusfar
/// Add a message to the device-chat.
/// Device-messages usually contain update information
/// and some hints that are added during the program runs, multi-device etc.
/// The device-message may be defined by a label;
/// if a message with the same label was added or skipped before,
/// the message is not added again, even if the message was deleted in between.
/// If needed, the device-chat is created before.
///
/// Sends the `MsgsChanged` event on success.
///
/// Setting msg to None will prevent the device message with this label from being added in the future.
async fn add_device_message(
&self,
account_id: u32,
label: String,
text: String,
) -> Result<u32> {
msg: Option<MessageData>,
) -> Result<Option<u32>> {
let ctx = self.get_context(account_id).await?;
let mut msg = Message::new(Viewtype::Text);
msg.set_text(text);
let message_id =
deltachat::chat::add_device_msg(&ctx, Some(&label), Some(&mut msg)).await?;
Ok(message_id.to_u32())
if let Some(msg) = msg {
let mut message = msg.create_message(&ctx).await?;
let message_id =
deltachat::chat::add_device_msg(&ctx, Some(&label), Some(&mut message)).await?;
if !message_id.is_unset() {
return Ok(Some(message_id.to_u32()));
}
} else {
deltachat::chat::add_device_msg(&ctx, Some(&label), None).await?;
}
Ok(None)
}
/// Mark all messages in a chat as _noticed_.
@@ -1062,9 +1097,12 @@ impl CommandApi {
.collect::<Vec<JSONRPCMessageListItem>>())
}
async fn get_message(&self, account_id: u32, message_id: u32) -> Result<MessageObject> {
async fn get_message(&self, account_id: u32, msg_id: u32) -> Result<MessageObject> {
let ctx = self.get_context(account_id).await?;
MessageObject::from_message_id(&ctx, message_id).await
let msg_id = MsgId::new(msg_id);
MessageObject::from_msg_id(&ctx, msg_id)
.await
.with_context(|| format!("Failed to load message {msg_id} for account {account_id}"))
}
async fn get_message_html(&self, account_id: u32, message_id: u32) -> Result<Option<String>> {
@@ -1084,7 +1122,7 @@ impl CommandApi {
let ctx = self.get_context(account_id).await?;
let mut messages: HashMap<u32, MessageLoadResult> = HashMap::new();
for message_id in message_ids {
let message_result = MessageObject::from_message_id(&ctx, message_id).await;
let message_result = MessageObject::from_msg_id(&ctx, MsgId::new(message_id)).await;
messages.insert(
message_id,
match message_result {
@@ -1808,38 +1846,7 @@ impl CommandApi {
async fn send_msg(&self, account_id: u32, chat_id: u32, data: MessageData) -> Result<u32> {
let ctx = self.get_context(account_id).await?;
let mut message = Message::new(if let Some(viewtype) = data.viewtype {
viewtype.into()
} else if data.file.is_some() {
Viewtype::File
} else {
Viewtype::Text
});
message.set_text(data.text.unwrap_or_default());
if data.html.is_some() {
message.set_html(data.html);
}
if data.override_sender_name.is_some() {
message.set_override_sender_name(data.override_sender_name);
}
if let Some(file) = data.file {
message.set_file(file, None);
}
if let Some((latitude, longitude)) = data.location {
message.set_location(latitude, longitude);
}
if let Some(id) = data.quoted_message_id {
message
.set_quote(
&ctx,
Some(
&Message::load_from_db(&ctx, MsgId::new(id))
.await
.context("message to quote could not be loaded")?,
),
)
.await?;
}
let mut message = data.create_message(&ctx).await?;
let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message)
.await?
.to_u32();
@@ -2038,11 +2045,9 @@ impl CommandApi {
)
.await?;
}
let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message)
.await?
.to_u32();
let message = MessageObject::from_message_id(&ctx, msg_id).await?;
Ok((msg_id, message))
let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message).await?;
let message = MessageObject::from_msg_id(&ctx, msg_id).await?;
Ok((msg_id.to_u32(), message))
}
// mimics the old desktop call, will get replaced with something better in the composer rewrite,
@@ -2121,13 +2126,6 @@ async fn set_config(
value,
)
.await?;
match key {
"sentbox_watch" | "mvbox_move" | "only_fetch_mvbox" => {
ctx.restart_io_if_running().await;
}
_ => {}
}
}
Ok(())
}

View File

@@ -85,7 +85,7 @@ impl FullChat {
let can_send = chat.can_send(context).await?;
let was_seen_recently = if chat.get_type() == Chattype::Single {
match contact_ids.get(0) {
match contact_ids.first() {
Some(contact) => Contact::get_by_id(context, *contact)
.await
.context("failed to load contact for was_seen_recently")?

View File

@@ -102,7 +102,7 @@ pub(crate) async fn get_chat_list_item_by_id(
let self_in_group = chat_contacts.contains(&ContactId::SELF);
let (dm_chat_contact, was_seen_recently) = if chat.get_type() == Chattype::Single {
let contact = chat_contacts.get(0);
let contact = chat_contacts.first();
let was_seen_recently = match contact {
Some(contact) => Contact::get_by_id(ctx, *contact)
.await

View File

@@ -1,5 +1,4 @@
use anyhow::Result;
use deltachat::contact::VerifiedStatus;
use deltachat::context::Context;
use serde::Serialize;
use typescript_type_def::TypeDef;
@@ -46,6 +45,9 @@ pub struct ContactObject {
/// the contact's last seen timestamp
last_seen: i64,
was_seen_recently: bool,
/// If the contact is a bot.
is_bot: bool,
}
impl ContactObject {
@@ -57,7 +59,7 @@ impl ContactObject {
Some(path_buf) => path_buf.to_str().map(|s| s.to_owned()),
None => None,
};
let is_verified = contact.is_verified(context).await? == VerifiedStatus::BidirectVerified;
let is_verified = contact.is_verified(context).await?;
let is_profile_verified = contact.is_profile_verified(context).await?;
let verifier_id = contact
@@ -81,6 +83,7 @@ impl ContactObject {
verifier_id,
last_seen: contact.last_seen(),
was_seen_recently: contact.was_seen_recently(),
is_bot: contact.is_bot(),
})
}
}

View File

@@ -28,55 +28,37 @@ pub enum EventType {
///
/// This event should *not* be reported to the end-user using a popup or something like
/// that.
Info {
msg: String,
},
Info { msg: String },
/// Emitted when SMTP connection is established and login was successful.
SmtpConnected {
msg: String,
},
SmtpConnected { msg: String },
/// Emitted when IMAP connection is established and login was successful.
ImapConnected {
msg: String,
},
ImapConnected { msg: String },
/// Emitted when a message was successfully sent to the SMTP server.
SmtpMessageSent {
msg: String,
},
SmtpMessageSent { msg: String },
/// Emitted when an IMAP message has been marked as deleted
ImapMessageDeleted {
msg: String,
},
ImapMessageDeleted { msg: String },
/// Emitted when an IMAP message has been moved
ImapMessageMoved {
msg: String,
},
ImapMessageMoved { msg: String },
/// Emitted before going into IDLE on the Inbox folder.
ImapInboxIdle,
/// Emitted when an new file in the $BLOBDIR was created
NewBlobFile {
file: String,
},
NewBlobFile { file: String },
/// Emitted when an file in the $BLOBDIR was deleted
DeletedBlobFile {
file: String,
},
DeletedBlobFile { file: String },
/// The library-user should write a warning string to the log.
///
/// This event should *not* be reported to the end-user using a popup or something like
/// that.
Warning {
msg: String,
},
Warning { msg: String },
/// The library-user should report an error to the end-user.
///
@@ -88,18 +70,14 @@ pub enum EventType {
/// it might be better to delay showing these events until the function has really
/// failed (returned false). It should be sufficient to report only the *last* error
/// in a messasge box then.
Error {
msg: String,
},
Error { msg: String },
/// An action cannot be performed because the user is not in the group.
/// Reported eg. after a call to
/// setChatName(), setChatProfileImage(),
/// addContactToChat(), removeContactFromChat(),
/// and messages sending functions.
ErrorSelfNotInGroup {
msg: String,
},
ErrorSelfNotInGroup { msg: String },
/// Messages or chats changed. One or more messages or chats changed for various
/// reasons in the database:
@@ -110,10 +88,7 @@ pub enum EventType {
/// `chatId` is set if only a single chat is affected by the changes, otherwise 0.
/// `msgId` is set if only a single message is affected by the changes, otherwise 0.
#[serde(rename_all = "camelCase")]
MsgsChanged {
chat_id: u32,
msg_id: u32,
},
MsgsChanged { chat_id: u32, msg_id: u32 },
/// Reactions for the message changed.
#[serde(rename_all = "camelCase")]
@@ -126,60 +101,39 @@ pub enum EventType {
/// 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.
/// There is no extra #DC_EVENT_MSGS_CHANGED event sent together with this event.
#[serde(rename_all = "camelCase")]
IncomingMsg {
chat_id: u32,
msg_id: u32,
},
IncomingMsg { chat_id: u32, msg_id: u32 },
/// Downloading a bunch of messages just finished. This is an experimental
/// Downloading a bunch of messages just finished. This is an
/// event to allow the UI to only show one notification per message bunch,
/// instead of cluttering the user with many notifications.
///
/// msg_ids contains the message ids.
#[serde(rename_all = "camelCase")]
IncomingMsgBunch {
msg_ids: Vec<u32>,
},
IncomingMsgBunch,
/// Messages were seen or noticed.
/// chat id is always set.
#[serde(rename_all = "camelCase")]
MsgsNoticed {
chat_id: u32,
},
MsgsNoticed { chat_id: u32 },
/// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to
/// DC_STATE_OUT_DELIVERED, see `Message.state`.
#[serde(rename_all = "camelCase")]
MsgDelivered {
chat_id: u32,
msg_id: u32,
},
MsgDelivered { chat_id: u32, msg_id: u32 },
/// 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 `Message.state`.
#[serde(rename_all = "camelCase")]
MsgFailed {
chat_id: u32,
msg_id: u32,
},
MsgFailed { chat_id: u32, msg_id: u32 },
/// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to
/// DC_STATE_OUT_MDN_RCVD, see `Message.state`.
#[serde(rename_all = "camelCase")]
MsgRead {
chat_id: u32,
msg_id: u32,
},
MsgRead { chat_id: u32, msg_id: u32 },
/// A single message is deleted.
#[serde(rename_all = "camelCase")]
MsgDeleted {
chat_id: u32,
msg_id: u32,
},
MsgDeleted { chat_id: u32, msg_id: u32 },
/// 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.
@@ -189,24 +143,17 @@ pub enum EventType {
/// This event does not include ephemeral timer modification, which
/// is a separate event.
#[serde(rename_all = "camelCase")]
ChatModified {
chat_id: u32,
},
ChatModified { chat_id: u32 },
/// Chat ephemeral timer changed.
#[serde(rename_all = "camelCase")]
ChatEphemeralTimerModified {
chat_id: u32,
timer: u32,
},
ChatEphemeralTimerModified { chat_id: u32, timer: u32 },
/// Contact(s) created, renamed, blocked or deleted.
///
/// @param data1 (int) If set, this is the contact_id of an added contact that should be selected.
#[serde(rename_all = "camelCase")]
ContactsChanged {
contact_id: Option<u32>,
},
ContactsChanged { contact_id: Option<u32> },
/// Location of one or more contact has changed.
///
@@ -214,9 +161,7 @@ pub enum EventType {
/// If the locations of several contacts have been changed,
/// this parameter is set to `None`.
#[serde(rename_all = "camelCase")]
LocationChanged {
contact_id: Option<u32>,
},
LocationChanged { contact_id: Option<u32> },
/// Inform about the configuration progress started by configure().
ConfigureProgress {
@@ -234,9 +179,7 @@ pub enum EventType {
/// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done
/// @param data2 0
#[serde(rename_all = "camelCase")]
ImexProgress {
progress: usize,
},
ImexProgress { progress: usize },
/// A file has been exported. A file has been written by imex().
/// This event may be sent multiple times by a single call to imex().
@@ -246,9 +189,7 @@ pub enum EventType {
///
/// @param data2 0
#[serde(rename_all = "camelCase")]
ImexFileWritten {
path: String,
},
ImexFileWritten { path: String },
/// Progress information of a secure-join handshake from the view of the inviter
/// (Alice, the person who shows the QR code).
@@ -263,10 +204,7 @@ pub enum EventType {
/// 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol.
/// 1000=Protocol finished for this contact.
#[serde(rename_all = "camelCase")]
SecurejoinInviterProgress {
contact_id: u32,
progress: usize,
},
SecurejoinInviterProgress { contact_id: u32, progress: usize },
/// Progress information of a secure-join handshake from the view of the joiner
/// (Bob, the person who scans the QR code).
@@ -277,10 +215,7 @@ pub enum EventType {
/// 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself."
/// (Bob has verified alice and waits until Alice does the same for him)
#[serde(rename_all = "camelCase")]
SecurejoinJoinerProgress {
contact_id: u32,
progress: usize,
},
SecurejoinJoinerProgress { contact_id: u32, progress: usize },
/// The connectivity to the server changed.
/// This means that you should refresh the connectivity view
@@ -288,8 +223,17 @@ pub enum EventType {
/// getConnectivityHtml() for details.
ConnectivityChanged,
/// Deprecated by `ConfigSynced`.
SelfavatarChanged,
/// A multi-device synced config value changed. Maybe the app needs to refresh smth. For
/// uniformity this is emitted on the source device too. The value isn't here, otherwise it
/// would be logged which might not be good for privacy.
ConfigSynced {
/// Configuration key.
key: String,
},
#[serde(rename_all = "camelCase")]
WebxdcStatusUpdate {
msg_id: u32,
@@ -298,9 +242,14 @@ pub enum EventType {
/// Inform that a message containing a webxdc instance has been deleted
#[serde(rename_all = "camelCase")]
WebxdcInstanceDeleted {
msg_id: u32,
},
WebxdcInstanceDeleted { msg_id: u32 },
/// Tells that the Background fetch was completed (or timed out).
/// This event acts as a marker, when you reach this event you can be sure
/// that all events emitted during the background fetch were processed.
///
/// This event is only emitted by the account manager
AccountsBackgroundFetchDone,
}
impl From<CoreEventType> for EventType {
@@ -336,9 +285,7 @@ impl From<CoreEventType> for EventType {
chat_id: chat_id.to_u32(),
msg_id: msg_id.to_u32(),
},
CoreEventType::IncomingMsgBunch { msg_ids } => IncomingMsgBunch {
msg_ids: msg_ids.into_iter().map(|id| id.to_u32()).collect(),
},
CoreEventType::IncomingMsgBunch => IncomingMsgBunch,
CoreEventType::MsgsNoticed(chat_id) => MsgsNoticed {
chat_id: chat_id.to_u32(),
},
@@ -396,6 +343,9 @@ impl From<CoreEventType> for EventType {
},
CoreEventType::ConnectivityChanged => ConnectivityChanged,
CoreEventType::SelfavatarChanged => SelfavatarChanged,
CoreEventType::ConfigSynced { key } => ConfigSynced {
key: key.to_string(),
},
CoreEventType::WebxdcStatusUpdate {
msg_id,
status_update_serial,
@@ -406,6 +356,7 @@ impl From<CoreEventType> for EventType {
CoreEventType::WebxdcInstanceDeleted { msg_id } => WebxdcInstanceDeleted {
msg_id: msg_id.to_u32(),
},
CoreEventType::AccountsBackgroundFetchDone => AccountsBackgroundFetchDone,
}
}
}

View File

@@ -105,11 +105,6 @@ enum MessageQuote {
}
impl MessageObject {
pub async fn from_message_id(context: &Context, message_id: u32) -> Result<Self> {
let msg_id = MsgId::new(message_id);
Self::from_msg_id(context, msg_id).await
}
pub async fn from_msg_id(context: &Context, msg_id: MsgId) -> Result<Self> {
let message = Message::load_from_db(context, msg_id).await?;
@@ -345,6 +340,7 @@ pub enum SystemMessageType {
SecurejoinMessage,
LocationStreamingEnabled,
LocationOnly,
InvalidUnencryptedMail,
/// Chat ephemeral message timer is changed.
EphemeralTimerChanged,
@@ -385,6 +381,7 @@ impl From<deltachat::mimeparser::SystemMessage> for SystemMessageType {
SystemMessage::MultiDeviceSync => SystemMessageType::MultiDeviceSync,
SystemMessage::WebxdcStatusUpdate => SystemMessageType::WebxdcStatusUpdate,
SystemMessage::WebxdcInfoMessage => SystemMessageType::WebxdcInfoMessage,
SystemMessage::InvalidUnencryptedMail => SystemMessageType::InvalidUnencryptedMail,
}
}
}
@@ -546,6 +543,44 @@ pub struct MessageData {
pub quoted_message_id: Option<u32>,
}
impl MessageData {
pub(crate) async fn create_message(self, context: &Context) -> Result<Message> {
let mut message = Message::new(if let Some(viewtype) = self.viewtype {
viewtype.into()
} else if self.file.is_some() {
Viewtype::File
} else {
Viewtype::Text
});
message.set_text(self.text.unwrap_or_default());
if self.html.is_some() {
message.set_html(self.html);
}
if self.override_sender_name.is_some() {
message.set_override_sender_name(self.override_sender_name);
}
if let Some(file) = self.file {
message.set_file(file, None);
}
if let Some((latitude, longitude)) = self.location {
message.set_location(latitude, longitude);
}
if let Some(id) = self.quoted_message_id {
message
.set_quote(
context,
Some(
&Message::load_from_db(context, MsgId::new(id))
.await
.context("message to quote could not be loaded")?,
),
)
.await?;
}
Ok(message)
}
}
#[derive(Serialize, TypeDef, schemars::JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct MessageReadReceipt {

View File

@@ -33,10 +33,8 @@ async fn main() -> Result<(), std::io::Error> {
let addr = SocketAddr::from(([127, 0, 0, 1], port));
log::info!("JSON-RPC WebSocket server listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
Ok(())
}

View File

@@ -53,5 +53,5 @@
},
"type": "module",
"types": "dist/deltachat.d.ts",
"version": "1.131.6"
"version": "1.137.2"
}

View File

@@ -1,18 +1,19 @@
[package]
name = "deltachat-repl"
version = "1.131.6"
version = "1.137.2"
license = "MPL-2.0"
edition = "2021"
repository = "https://github.com/deltachat/deltachat-core-rust"
[dependencies]
ansi_term = "0.12.1"
anyhow = "1"
deltachat = { path = "..", features = ["internals"]}
dirs = "5"
log = "0.4.20"
log = "0.4.21"
pretty_env_logger = "0.5"
rusqlite = "0.30"
rustyline = "12"
rusqlite = "0.31"
rustyline = "14"
tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] }
[features]

View File

@@ -3,7 +3,7 @@ extern crate dirs;
use std::path::Path;
use std::str::FromStr;
use std::time::{Duration, SystemTime};
use std::time::Duration;
use anyhow::{bail, ensure, Result};
use deltachat::chat::{
@@ -33,14 +33,6 @@ use tokio::fs;
/// e.g. bitmask 7 triggers actions defined with bits 1, 2 and 4.
async fn reset_tables(context: &Context, bits: i32) {
println!("Resetting tables ({bits})...");
if 0 != bits & 1 {
context
.sql()
.execute("DELETE FROM jobs;", ())
.await
.unwrap();
println!("(1) Jobs reset.");
}
if 0 != bits & 2 {
context
.sql()
@@ -284,13 +276,8 @@ async fn log_contactlist(context: &Context, contacts: &[ContactId]) -> Result<()
let contact = Contact::get_by_id(context, *contact_id).await?;
let name = contact.get_display_name();
let addr = contact.get_addr();
let verified_state = contact.is_verified(context).await?;
let verified_str = if VerifiedStatus::Unverified != verified_state {
if verified_state == VerifiedStatus::BidirectVerified {
" √√"
} else {
""
}
let verified_str = if contact.is_verified(context).await? {
""
} else {
""
};
@@ -352,6 +339,8 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
export-keys\n\
import-keys\n\
export-setup\n\
dump <filename>\n\n
read <filename>\n\n
poke [<eml-file>|<folder>|<addr> <key-file>]\n\
reset <flags>\n\
stop\n\
@@ -527,6 +516,14 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
&setup_code,
);
}
"dump" => {
ensure!(!arg1.is_empty(), "Argument <filename> missing.");
serialize_database(&context, arg1).await?;
}
"read" => {
ensure!(!arg1.is_empty(), "Argument <filename> missing.");
deserialize_database(&context, arg1).await?;
}
"poke" => {
ensure!(poke_spec(&context, Some(arg1)).await, "Poke failed");
}

View File

@@ -9,7 +9,6 @@ extern crate deltachat;
use std::borrow::Cow::{self, Borrowed, Owned};
use std::io::{self, Write};
use std::path::Path;
use std::process::Command;
use ansi_term::Color;
@@ -20,8 +19,7 @@ use deltachat::context::*;
use deltachat::oauth2::*;
use deltachat::qr_code_generator::get_securejoin_qr_svg;
use deltachat::securejoin::*;
use deltachat::stock_str::StockStrings;
use deltachat::{EventType, Events};
use deltachat::EventType;
use log::{error, info, warn};
use rustyline::completion::{Completer, FilenameCompleter, Pair};
use rustyline::error::ReadlineError;
@@ -299,8 +297,8 @@ impl Highlighter for DcHelper {
self.highlighter.highlight(line, pos)
}
fn highlight_char(&self, line: &str, pos: usize) -> bool {
self.highlighter.highlight_char(line, pos)
fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool {
self.highlighter.highlight_char(line, pos, forced)
}
}
@@ -312,7 +310,10 @@ async fn start(args: Vec<String>) -> Result<(), Error> {
println!("Error: Bad arguments, expected [db-name].");
bail!("No db-name specified");
}
let context = Context::new(Path::new(&args[1]), 0, Events::new(), StockStrings::new()).await?;
let context = ContextBuilder::new(args[1].clone().into())
.with_id(1)
.open()
.await?;
let events = context.get_event_emitter();
tokio::task::spawn(async move {
@@ -401,7 +402,7 @@ enum ExitResult {
async fn handle_cmd(
line: &str,
mut ctx: Context,
ctx: Context,
selected_chat: &mut ChatId,
) -> Result<ExitResult, Error> {
let mut args = line.splitn(2, ' ');
@@ -481,7 +482,10 @@ async fn handle_cmd(
#[tokio::main]
async fn main() -> Result<(), Error> {
let _ = pretty_env_logger::try_init();
pretty_env_logger::formatted_timed_builder()
.parse_default_env()
.filter_module("deltachat_repl", log::LevelFilter::Info)
.init();
let args = std::env::args().collect();
start(args).await?;

View File

@@ -1,9 +1,10 @@
[build-system]
requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"]
requires = ["setuptools>=45"]
build-backend = "setuptools.build_meta"
[project]
name = "deltachat-rpc-client"
version = "1.137.2"
description = "Python client for Delta Chat core JSON-RPC interface"
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -19,9 +20,7 @@ classifiers = [
"Topic :: Communications :: Chat",
"Topic :: Communications :: Email"
]
dynamic = [
"version"
]
readme = "README.md"
[tool.setuptools.package-data]
deltachat_rpc_client = [
@@ -31,9 +30,6 @@ deltachat_rpc_client = [
[project.entry-points.pytest11]
"deltachat_rpc_client.pytestplugin" = "deltachat_rpc_client.pytestplugin"
[tool.setuptools_scm]
root = ".."
[tool.black]
line-length = 120

View File

@@ -1,4 +1,5 @@
"""Delta Chat JSON-RPC high-level API"""
from ._utils import AttrDict, run_bot_cli, run_client_cli
from .account import Account
from .chat import Chat

View File

@@ -168,3 +168,33 @@ def parse_system_add_remove(text: str) -> Optional[Tuple[str, str, str]]:
return "removed", addr, addr
return None
class futuremethod: # noqa: N801
"""Decorator for async methods."""
def __init__(self, func):
self._func = func
def __get__(self, instance, owner=None):
if instance is None:
return self
def future(*args):
generator = self._func(instance, *args)
res = next(generator)
def f():
try:
generator.send(res())
except StopIteration as e:
return e.value
return f
def wrapper(*args):
f = future(*args)
return f()
wrapper.future = future
return wrapper

View File

@@ -2,7 +2,7 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING, List, Optional, Tuple, Union
from warnings import warn
from ._utils import AttrDict
from ._utils import AttrDict, futuremethod
from .chat import Chat
from .const import ChatlistFlag, ContactFlag, EventType, SpecialContactId
from .contact import Contact
@@ -76,9 +76,16 @@ class Account:
"""Get self avatar."""
return self.get_config("selfavatar")
def configure(self) -> None:
def check_qr(self, qr):
return self._rpc.check_qr(self.id, qr)
def set_config_from_qr(self, qr: str):
self._rpc.set_config_from_qr(self.id, qr)
@futuremethod
def configure(self):
"""Configure an account."""
self._rpc.configure(self.id)
yield self._rpc.configure.future(self.id)
def create_contact(self, obj: Union[int, str, Contact], name: Optional[str] = None) -> Contact:
"""Create a new Contact or return an existing one.
@@ -300,3 +307,13 @@ class Account:
def import_backup(self, path, passphrase: str = "") -> None:
"""Import backup."""
self._rpc.import_backup(self.id, str(path), passphrase)
def export_self_keys(self, path) -> None:
"""Export keys."""
passphrase = "" # Setting passphrase is currently not supported.
self._rpc.export_self_keys(self.id, str(path), passphrase)
def import_self_keys(self, path) -> None:
"""Import keys."""
passphrase = "" # Importing passphrase-protected keys is currently not supported.
self._rpc.import_self_keys(self.id, str(path), passphrase)

View File

@@ -84,7 +84,9 @@ class Chat:
self._rpc.set_chat_name(self.account.id, self.id, name)
def set_ephemeral_timer(self, timer: int) -> None:
"""Set ephemeral timer of this chat."""
"""Set ephemeral timer of this chat in seconds.
0 means the timer is disabled, use 1 for immediate deletion."""
self._rpc.set_chat_ephemeral_timer(self.account.id, self.id, timer)
def get_encryption_info(self) -> str:

View File

@@ -1,4 +1,5 @@
"""Event loop implementations offering high level event handling/hooking."""
import logging
from typing import (
TYPE_CHECKING,

View File

@@ -61,6 +61,15 @@ class EventType(str, Enum):
WEBXDC_INSTANCE_DELETED = "WebxdcInstanceDeleted"
class ChatId(IntEnum):
"""Special chat ids"""
TRASH = 3
ARCHIVED_LINK = 6
ALLDONE_HINT = 7
LAST_SPECIAL = 9
class ChatType(IntEnum):
"""Chat types"""
@@ -122,3 +131,107 @@ class SystemMessageType(str, Enum):
EPHEMERAL_TIMER_CHANGED = "EphemeralTimerChanged"
MULTI_DEVICE_SYNC = "MultiDeviceSync"
WEBXDC_INFO_MESSAGE = "WebxdcInfoMessage"
class MessageState(IntEnum):
"""State of the message."""
UNDEFINED = 0
IN_FRESH = 10
IN_NOTICED = 13
IN_SEEN = 16
OUT_PREPARING = 18
OUT_DRAFT = 19
OUT_PENDING = 20
OUT_FAILED = 24
OUT_DELIVERED = 26
OUT_MDN_RCVD = 28
class MessageId(IntEnum):
"""Special message ids"""
DAYMARKER = 9
LAST_SPECIAL = 9
class CertificateChecks(IntEnum):
"""Certificate checks mode"""
AUTOMATIC = 0
STRICT = 1
ACCEPT_INVALID_CERTIFICATES = 3
class Connectivity(IntEnum):
"""Connectivity states"""
NOT_CONNECTED = 1000
CONNECTING = 2000
WORKING = 3000
CONNECTED = 4000
class KeyGenType(IntEnum):
"""Type of the key to generate"""
DEFAULT = 0
RSA2048 = 1
ED25519 = 2
RSA4096 = 3
# "Lp" means "login parameters"
class LpAuthFlag(IntEnum):
"""Authorization flags"""
OAUTH2 = 0x2
NORMAL = 0x4
class MediaQuality(IntEnum):
"""Media quality setting"""
BALANCED = 0
WORSE = 1
class ProviderStatus(IntEnum):
"""Provider status according to manual testing"""
OK = 1
PREPARATION = 2
BROKEN = 3
class PushNotifyState(IntEnum):
"""Push notifications state"""
NOT_CONNECTED = 0
HEARTBEAT = 1
CONNECTED = 2
class ShowEmails(IntEnum):
"""Show emails mode"""
OFF = 0
ACCEPTED_CONTACTS = 1
ALL = 2
class SocketSecurity(IntEnum):
"""Socket security"""
AUTOMATIC = 0
SSL = 1
STARTTLS = 2
PLAIN = 3
class VideochatType(IntEnum):
"""Video chat URL type"""
UNKNOWN = 0
BASICWEBRTC = 1
JITSI = 2

View File

@@ -1,4 +1,5 @@
"""High-level classes for event processing and filtering."""
import re
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Callable, Iterable, Iterator, Optional, Set, Tuple, Union

View File

@@ -5,6 +5,7 @@ from typing import AsyncGenerator, List, Optional
import pytest
from . import Account, AttrDict, Bot, Client, DeltaChat, EventType, Message
from ._utils import futuremethod
from .rpc import Rpc
@@ -37,9 +38,10 @@ class ACFactory:
assert not account.is_configured()
return account
def new_configured_account(self) -> Account:
@futuremethod
def new_configured_account(self):
account = self.new_preconfigured_account()
account.configure()
yield account.configure.future()
assert account.is_configured()
return account
@@ -49,8 +51,9 @@ class ACFactory:
bot.configure(credentials["email"], credentials["password"])
return bot
def get_online_account(self) -> Account:
account = self.new_configured_account()
@futuremethod
def get_online_account(self):
account = yield self.new_configured_account.future()
account.start_io()
while True:
event = account.wait_for_event()
@@ -59,7 +62,8 @@ class ACFactory:
return account
def get_online_accounts(self, num: int) -> List[Account]:
return [self.get_online_account() for _ in range(num)]
futures = [self.get_online_account.future() for _ in range(num)]
return [f() for f in futures]
def resetup_account(self, ac: Account) -> Account:
"""Resetup account from scratch, losing the encryption key."""

View File

@@ -13,6 +13,48 @@ class JsonRpcError(Exception):
pass
class RpcFuture:
def __init__(self, rpc: "Rpc", request_id: int, event: Event):
self.rpc = rpc
self.request_id = request_id
self.event = event
def __call__(self):
self.event.wait()
response = self.rpc.request_results.pop(self.request_id)
if "error" in response:
raise JsonRpcError(response["error"])
if "result" in response:
return response["result"]
return None
class RpcMethod:
def __init__(self, rpc: "Rpc", name: str):
self.rpc = rpc
self.name = name
def __call__(self, *args) -> Any:
"""Synchronously calls JSON-RPC method."""
future = self.future(*args)
return future()
def future(self, *args) -> Any:
"""Asynchronously calls JSON-RPC method."""
request_id = next(self.rpc.id_iterator)
request = {
"jsonrpc": "2.0",
"method": self.name,
"params": args,
"id": request_id,
}
event = Event()
self.rpc.request_events[request_id] = event
self.rpc.request_queue.put(request)
return RpcFuture(self.rpc, request_id, event)
class Rpc:
def __init__(self, accounts_dir: Optional[str] = None, **kwargs):
"""The given arguments will be passed to subprocess.Popen()"""
@@ -145,24 +187,4 @@ class Rpc:
return queue.get()
def __getattr__(self, attr: str):
def method(*args) -> Any:
request_id = next(self.id_iterator)
request = {
"jsonrpc": "2.0",
"method": attr,
"params": args,
"id": request_id,
}
event = Event()
self.request_events[request_id] = event
self.request_queue.put(request)
event.wait()
response = self.request_results.pop(request_id)
if "error" in response:
raise JsonRpcError(response["error"])
if "result" in response:
return response["result"]
return None
return method
return RpcMethod(self, attr)

View File

@@ -1,9 +1,10 @@
import logging
from deltachat_rpc_client import Chat, SpecialContactId
import pytest
from deltachat_rpc_client import Chat, EventType, SpecialContactId
def test_qr_setup_contact(acfactory) -> None:
def test_qr_setup_contact(acfactory, tmp_path) -> None:
alice, bob = acfactory.get_online_accounts(2)
qr_code, _svg = alice.get_qr_code()
@@ -23,13 +24,26 @@ def test_qr_setup_contact(acfactory) -> None:
bob_contact_alice_snapshot = bob_contact_alice.get_snapshot()
assert bob_contact_alice_snapshot.is_verified
# Test that if Bob changes the key, backwards verification is lost.
logging.info("Bob 2 is created")
bob2 = acfactory.new_configured_account()
bob2.export_self_keys(tmp_path)
def test_qr_securejoin(acfactory):
logging.info("Bob imports a key")
bob.import_self_keys(tmp_path / "private-key-default.asc")
assert bob.get_config("key_id") == "2"
bob_contact_alice_snapshot = bob_contact_alice.get_snapshot()
assert not bob_contact_alice_snapshot.is_verified
@pytest.mark.parametrize("protect", [True, False])
def test_qr_securejoin(acfactory, protect):
alice, bob = acfactory.get_online_accounts(2)
logging.info("Alice creates a verified group")
alice_chat = alice.create_group("Verified group", protect=True)
assert alice_chat.get_basic_snapshot().is_protected
alice_chat = alice.create_group("Verified group", protect=protect)
assert alice_chat.get_basic_snapshot().is_protected == protect
logging.info("Bob joins verified group")
qr_code, _svg = alice_chat.get_qr_code()
@@ -53,7 +67,7 @@ def test_qr_securejoin(acfactory):
snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
assert snapshot.text == "Member Me ({}) added by {}.".format(bob.get_config("addr"), alice.get_config("addr"))
assert snapshot.chat.get_basic_snapshot().is_protected
assert snapshot.chat.get_basic_snapshot().is_protected == protect
# Test that Bob verified Alice's profile.
bob_contact_alice = bob.get_contact_by_addr(alice.get_config("addr"))
@@ -150,6 +164,21 @@ def test_qr_readreceipt(acfactory) -> None:
assert not bob.get_chat_by_contact(bob_contact_charlie)
def test_setup_contact_resetup(acfactory) -> None:
"""Tests that setup contact works after Alice resets the device and changes the key."""
alice, bob = acfactory.get_online_accounts(2)
qr_code, _svg = alice.get_qr_code()
bob.secure_join(qr_code)
bob.wait_for_securejoin_joiner_success()
alice = acfactory.resetup_account(alice)
qr_code, _svg = alice.get_qr_code()
bob.secure_join(qr_code)
bob.wait_for_securejoin_joiner_success()
def test_verified_group_recovery(acfactory) -> None:
"""Tests verified group recovery by reverifying a member and sending a message in a group."""
ac1, ac2, ac3 = acfactory.get_online_accounts(3)
@@ -161,7 +190,7 @@ def test_verified_group_recovery(acfactory) -> None:
logging.info("ac2 joins verified group")
qr_code, _svg = chat.get_qr_code()
ac2.secure_join(qr_code)
ac1.wait_for_securejoin_inviter_success()
ac2.wait_for_securejoin_joiner_success()
# ac1 has ac2 directly verified.
ac1_contact_ac2 = ac1.get_contact_by_addr(ac2.get_config("addr"))
@@ -169,7 +198,8 @@ def test_verified_group_recovery(acfactory) -> None:
logging.info("ac3 joins verified group")
ac3_chat = ac3.secure_join(qr_code)
ac1.wait_for_securejoin_inviter_success()
ac3.wait_for_securejoin_joiner_success()
ac3.wait_for_incoming_msg_event() # Member added
logging.info("ac2 logs in on a new device")
ac2 = acfactory.resetup_account(ac2)
@@ -177,8 +207,7 @@ def test_verified_group_recovery(acfactory) -> None:
logging.info("ac2 reverifies with ac3")
qr_code, _svg = ac3.get_qr_code()
ac2.secure_join(qr_code)
ac3.wait_for_securejoin_inviter_success()
ac2.wait_for_securejoin_joiner_success()
logging.info("ac3 sends a message to the group")
assert len(ac3_chat.get_contacts()) == 3
@@ -225,7 +254,7 @@ def test_verified_group_member_added_recovery(acfactory) -> None:
logging.info("ac2 joins verified group")
qr_code, _svg = chat.get_qr_code()
ac2.secure_join(qr_code)
ac1.wait_for_securejoin_inviter_success()
ac2.wait_for_securejoin_joiner_success()
# ac1 has ac2 directly verified.
ac1_contact_ac2 = ac1.get_contact_by_addr(ac2.get_config("addr"))
@@ -233,7 +262,8 @@ def test_verified_group_member_added_recovery(acfactory) -> None:
logging.info("ac3 joins verified group")
ac3_chat = ac3.secure_join(qr_code)
ac1.wait_for_securejoin_inviter_success()
ac3.wait_for_securejoin_joiner_success()
ac3.wait_for_incoming_msg_event() # Member added
logging.info("ac2 logs in on a new device")
ac2 = acfactory.resetup_account(ac2)
@@ -241,8 +271,7 @@ def test_verified_group_member_added_recovery(acfactory) -> None:
logging.info("ac2 reverifies with ac3")
qr_code, _svg = ac3.get_qr_code()
ac2.secure_join(qr_code)
ac3.wait_for_securejoin_inviter_success()
ac2.wait_for_securejoin_joiner_success()
logging.info("ac3 sends a message to the group")
assert len(ac3_chat.get_contacts()) == 3
@@ -429,3 +458,161 @@ def test_aeap_flow_verified(acfactory):
assert ac1new.get_config("addr") in [
contact.get_snapshot().address for contact in msg_in_2_snapshot.chat.get_contacts()
]
def test_gossip_verification(acfactory) -> None:
alice, bob, carol = acfactory.get_online_accounts(3)
# Bob verifies Alice.
qr_code, _svg = alice.get_qr_code()
bob.secure_join(qr_code)
bob.wait_for_securejoin_joiner_success()
# Bob verifies Carol.
qr_code, _svg = carol.get_qr_code()
bob.secure_join(qr_code)
bob.wait_for_securejoin_joiner_success()
bob_contact_alice = bob.create_contact(alice.get_config("addr"), "Alice")
bob_contact_carol = bob.create_contact(carol.get_config("addr"), "Carol")
carol_contact_alice = carol.create_contact(alice.get_config("addr"), "Alice")
logging.info("Bob creates an Autocrypt group")
bob_group_chat = bob.create_group("Autocrypt Group")
assert not bob_group_chat.get_basic_snapshot().is_protected
bob_group_chat.add_contact(bob_contact_alice)
bob_group_chat.add_contact(bob_contact_carol)
bob_group_chat.send_message(text="Hello Autocrypt group")
snapshot = carol.get_message_by_id(carol.wait_for_incoming_msg_event().msg_id).get_snapshot()
assert snapshot.text == "Hello Autocrypt group"
assert snapshot.show_padlock
# Autocrypt group does not propagate verification.
carol_contact_alice_snapshot = carol_contact_alice.get_snapshot()
assert not carol_contact_alice_snapshot.is_verified
logging.info("Bob creates a Securejoin group")
bob_group_chat = bob.create_group("Securejoin Group", protect=True)
assert bob_group_chat.get_basic_snapshot().is_protected
bob_group_chat.add_contact(bob_contact_alice)
bob_group_chat.add_contact(bob_contact_carol)
bob_group_chat.send_message(text="Hello Securejoin group")
snapshot = carol.get_message_by_id(carol.wait_for_incoming_msg_event().msg_id).get_snapshot()
assert snapshot.text == "Hello Securejoin group"
assert snapshot.show_padlock
# Securejoin propagates verification.
carol_contact_alice_snapshot = carol_contact_alice.get_snapshot()
assert carol_contact_alice_snapshot.is_verified
def test_securejoin_after_contact_resetup(acfactory) -> None:
"""
Regression test for a bug that prevented joining verified group with a QR code
if the group is already created and contains
a contact with inconsistent (Autocrypt and verified keys exist but don't match) key state.
"""
ac1, ac2, ac3 = acfactory.get_online_accounts(3)
# ac3 creates protected group with ac1.
ac3_chat = ac3.create_group("Verified group", protect=True)
# ac1 joins ac3 group.
ac3_qr_code, _svg = ac3_chat.get_qr_code()
ac1.secure_join(ac3_qr_code)
ac1.wait_for_securejoin_joiner_success()
# ac1 waits for member added message and creates a QR code.
snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
ac1_qr_code, _svg = snapshot.chat.get_qr_code()
# ac2 verifies ac1
qr_code, _svg = ac1.get_qr_code()
ac2.secure_join(qr_code)
ac2.wait_for_securejoin_joiner_success()
# ac1 is verified for ac2.
ac2_contact_ac1 = ac2.create_contact(ac1.get_config("addr"), "")
assert ac2_contact_ac1.get_snapshot().is_verified
# ac1 resetups the account.
ac1 = acfactory.resetup_account(ac1)
# ac1 sends a message to ac2.
ac1_contact_ac2 = ac1.create_contact(ac2.get_config("addr"), "")
ac1_chat_ac2 = ac1_contact_ac2.create_chat()
ac1_chat_ac2.send_text("Hello!")
# ac2 receives a message.
snapshot = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id).get_snapshot()
assert snapshot.text == "Hello!"
# ac1 is no longer verified for ac2 as new Autocrypt key is not the same as old verified key.
assert not ac2_contact_ac1.get_snapshot().is_verified
# ac1 goes offline.
ac1.remove()
# Scanning a QR code results in creating an unprotected group with an inviter.
# In this case inviter is ac1 which has an inconsistent key state.
# Normally inviter becomes verified as a result of Securejoin protocol
# and then the group chat becomes verified when "Member added" is received,
# but in this case ac1 is offline and this Securejoin process will never finish.
logging.info("ac2 scans ac1 QR code, this is not expected to finish")
ac2.secure_join(ac1_qr_code)
logging.info("ac2 scans ac3 QR code")
ac2.secure_join(ac3_qr_code)
logging.info("ac2 waits for joiner success")
ac2.wait_for_securejoin_joiner_success()
# Wait for member added.
logging.info("ac2 waits for member added message")
snapshot = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id).get_snapshot()
assert snapshot.is_info
ac2_chat = snapshot.chat
assert ac2_chat.get_basic_snapshot().is_protected
assert len(ac2_chat.get_contacts()) == 3
# ac1 is still "not verified" for ac2 due to inconsistent state.
assert not ac2_contact_ac1.get_snapshot().is_verified
def test_withdraw_securejoin_qr(acfactory):
alice, bob = acfactory.get_online_accounts(2)
logging.info("Alice creates a verified group")
alice_chat = alice.create_group("Verified group", protect=True)
assert alice_chat.get_basic_snapshot().is_protected
logging.info("Bob joins verified group")
qr_code, _svg = alice_chat.get_qr_code()
bob_chat = bob.secure_join(qr_code)
bob.wait_for_securejoin_joiner_success()
snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
assert snapshot.text == "Member Me ({}) added by {}.".format(bob.get_config("addr"), alice.get_config("addr"))
assert snapshot.chat.get_basic_snapshot().is_protected
bob_chat.leave()
snapshot = alice.get_message_by_id(alice.wait_for_incoming_msg_event().msg_id).get_snapshot()
assert snapshot.text == "Group left by {}.".format(bob.get_config("addr"))
logging.info("Alice withdraws QR code.")
qr = alice.check_qr(qr_code)
assert qr["kind"] == "withdrawVerifyGroup"
alice.set_config_from_qr(qr_code)
logging.info("Bob scans withdrawn QR code.")
bob_chat = bob.secure_join(qr_code)
logging.info("Bob scanned withdrawn QR code")
while True:
event = alice.wait_for_event()
if event.kind == EventType.MSGS_CHANGED and event.chat_id != 0:
break
snapshot = alice.get_message_by_id(event.msg_id).get_snapshot()
assert snapshot.text == "Cannot establish guaranteed end-to-end encryption with {}".format(bob.get_config("addr"))

View File

@@ -140,12 +140,9 @@ def test_chat(acfactory) -> None:
alice_chat_bob = alice_contact_bob.create_chat()
alice_chat_bob.send_text("Hello!")
while True:
event = bob.wait_for_event()
if event.kind == EventType.INCOMING_MSG:
chat_id = event.chat_id
msg_id = event.msg_id
break
event = bob.wait_for_incoming_msg_event()
chat_id = event.chat_id
msg_id = event.msg_id
message = bob.get_message_by_id(msg_id)
snapshot = message.get_snapshot()
assert snapshot.chat_id == chat_id
@@ -224,12 +221,9 @@ def test_message(acfactory) -> None:
alice_chat_bob = alice_contact_bob.create_chat()
alice_chat_bob.send_text("Hello!")
while True:
event = bob.wait_for_event()
if event.kind == EventType.INCOMING_MSG:
chat_id = event.chat_id
msg_id = event.msg_id
break
event = bob.wait_for_incoming_msg_event()
chat_id = event.chat_id
msg_id = event.msg_id
message = bob.get_message_by_id(msg_id)
snapshot = message.get_snapshot()
@@ -331,7 +325,7 @@ def test_wait_next_messages(acfactory) -> None:
next_messages_task = executor.submit(bot.wait_next_messages)
bot_addr = bot.get_config("addr")
alice_contact_bot = alice.create_contact(bot_addr, "Bob")
alice_contact_bot = alice.create_contact(bot_addr, "Bot")
alice_chat_bot = alice_contact_bot.create_chat()
alice_chat_bot.send_text("Hello!")
@@ -341,7 +335,7 @@ def test_wait_next_messages(acfactory) -> None:
assert snapshot.text == "Hello!"
def test_import_export(acfactory, tmp_path) -> None:
def test_import_export_backup(acfactory, tmp_path) -> None:
alice = acfactory.new_configured_account()
alice.export_backup(tmp_path)
@@ -352,6 +346,31 @@ def test_import_export(acfactory, tmp_path) -> None:
assert alice2.manager.get_system_info()
def test_import_export_keys(acfactory, tmp_path) -> None:
alice, bob = acfactory.get_online_accounts(2)
bob_addr = bob.get_config("addr")
alice_contact_bob = alice.create_contact(bob_addr, "Bob")
alice_chat_bob = alice_contact_bob.create_chat()
alice_chat_bob.send_text("Hello Bob!")
snapshot = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id).get_snapshot()
assert snapshot.text == "Hello Bob!"
# Alice resetups account, but keeps the key.
alice_keys_path = tmp_path / "alice_keys"
alice_keys_path.mkdir()
alice.export_self_keys(alice_keys_path)
alice = acfactory.resetup_account(alice)
alice.import_self_keys(alice_keys_path)
snapshot.chat.accept()
snapshot.chat.send_text("Hello Alice!")
snapshot = alice.get_message_by_id(alice.wait_for_incoming_msg_event().msg_id).get_snapshot()
assert snapshot.text == "Hello Alice!"
assert snapshot.show_padlock
def test_openrpc_command_line() -> None:
"""Test that "deltachat-rpc-server --openrpc" command returns an OpenRPC specification."""
out = subprocess.run(["deltachat-rpc-server", "--openrpc"], capture_output=True, check=True).stdout
@@ -377,3 +396,46 @@ def test_provider_info(rpc) -> None:
rpc.set_config(account_id, "socks5_enabled", "1")
provider_info = rpc.get_provider_info(account_id, "github.com")
assert provider_info is None
def test_mdn_doesnt_break_autocrypt(acfactory) -> None:
alice, bob = acfactory.get_online_accounts(2)
bob_addr = bob.get_config("addr")
alice_contact_bob = alice.create_contact(bob_addr, "Bob")
# Bob creates chat manually so chat with Alice is accepted.
alice_chat_bob = alice_contact_bob.create_chat()
# Alice sends a message to Bob.
alice_chat_bob.send_text("Hello Bob!")
event = bob.wait_for_incoming_msg_event()
msg_id = event.msg_id
message = bob.get_message_by_id(msg_id)
snapshot = message.get_snapshot()
# Bob sends a message to Alice.
bob_chat_alice = snapshot.chat
bob_chat_alice.accept()
bob_chat_alice.send_text("Hello Alice!")
event = alice.wait_for_incoming_msg_event()
msg_id = event.msg_id
message = alice.get_message_by_id(msg_id)
snapshot = message.get_snapshot()
assert snapshot.show_padlock
# Alice reads Bob's message.
message.mark_seen()
while True:
event = bob.wait_for_event()
if event.kind == EventType.MSG_READ:
break
# Bob sends a message to Alice, it should also be encrypted.
bob_chat_alice.send_text("Hi Alice!")
event = alice.wait_for_incoming_msg_event()
msg_id = event.msg_id
message = alice.get_message_by_id(msg_id)
snapshot = message.get_snapshot()
assert snapshot.show_padlock

View File

@@ -43,3 +43,15 @@ def test_webxdc(acfactory) -> None:
assert status_updates == [
{"payload": "Second update", "serial": 2, "max_serial": 2},
]
def test_webxdc_insert_lots_of_updates(acfactory) -> None:
alice, bob = acfactory.get_online_accounts(2)
bob_addr = bob.get_config("addr")
alice_contact_bob = alice.create_contact(bob_addr, "Bob")
alice_chat_bob = alice_contact_bob.create_chat()
message = alice_chat_bob.send_message(text="Let's play chess!", file="../test-data/webxdc/chess.xdc")
for i in range(2000):
message.send_webxdc_status_update({"payload": str(i)}, "description")

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat-rpc-server"
version = "1.131.6"
version = "1.137.2"
description = "DeltaChat JSON-RPC server"
edition = "2021"
readme = "README.md"
@@ -15,11 +15,11 @@ deltachat = { path = "..", default-features = false }
anyhow = "1"
env_logger = { version = "0.10.0" }
futures-lite = "2.0.0"
futures-lite = "2.3.0"
log = "0.4"
serde_json = "1.0.105"
serde_json = "1"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.33.0", features = ["io-std"] }
tokio = { version = "1.37.0", features = ["io-std"] }
tokio-util = "0.7.9"
yerpc = { version = "0.5.2", features = ["anyhow_expose", "openrpc"] }

View File

@@ -30,7 +30,7 @@ deltachat-rpc-server
The common use case for this program is to create bindings to use Delta Chat core from programming
languages other than Rust, for example:
1. Python: https://github.com/deltachat/deltachat-core-rust/tree/master/deltachat-rpc-client/
1. Python: https://pypi.org/project/deltachat-rpc-client/
2. Go: https://github.com/deltachat/deltachat-rpc-client-go/
Run `deltachat-rpc-server --version` to check the version of the server.

View File

@@ -0,0 +1,8 @@
[package]
name = "deltachat-time"
version = "1.0.0"
description = "Time-related tools"
edition = "2021"
license = "MPL-2.0"
[dependencies]

35
deltachat-time/src/lib.rs Normal file
View File

@@ -0,0 +1,35 @@
#![allow(missing_docs)]
use std::sync::RwLock;
use std::time::{Duration, SystemTime};
static SYSTEM_TIME_SHIFT: RwLock<Duration> = RwLock::new(Duration::new(0, 0));
/// Fake struct for mocking `SystemTime::now()` for test purposes. You still need to use
/// `SystemTime` as a struct representing a system time.
pub struct SystemTimeTools();
impl SystemTimeTools {
pub const UNIX_EPOCH: SystemTime = SystemTime::UNIX_EPOCH;
pub fn now() -> SystemTime {
return SystemTime::now() + *SYSTEM_TIME_SHIFT.read().unwrap();
}
/// Simulates a system clock forward adjustment by `duration`.
pub fn shift(duration: Duration) {
*SYSTEM_TIME_SHIFT.write().unwrap() += duration;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
SystemTimeTools::shift(Duration::from_secs(60));
let t = SystemTimeTools::now();
assert!(t > SystemTime::now());
}
}

View File

@@ -1,8 +1,20 @@
[advisories]
unmaintained = "allow"
ignore = [
"RUSTSEC-2020-0071",
"RUSTSEC-2022-0093",
# Timing attack on RSA.
# Delta Chat does not use RSA for new keys
# and this requires precise measurement of the decryption time by the attacker.
# There is no fix at the time of writing this (2023-11-28).
# <https://rustsec.org/advisories/RUSTSEC-2023-0071>
"RUSTSEC-2023-0071",
# Unmaintained ansi_term
"RUSTSEC-2021-0139",
# Unmaintained encoding
"RUSTSEC-2021-0153",
]
[bans]
@@ -27,6 +39,7 @@ skip = [
{ name = "ed25519", version = "1.5.3" },
{ name = "event-listener", version = "2.5.3" },
{ name = "getrandom", version = "<0.2" },
{ name = "idna", version = "0.4.0" },
{ name = "pem-rfc7468", version = "0.6.0" },
{ name = "pkcs8", version = "0.9.0" },
{ name = "quick-error", version = "<2.0" },
@@ -40,19 +53,25 @@ skip = [
{ name = "sec1", version = "0.3.0" },
{ name = "sha2", version = "<0.10" },
{ name = "signature", version = "1.6.4" },
{ name = "socket2", version = "0.4.9" },
{ name = "spin", version = "<0.9.6" },
{ name = "spki", version = "0.6.0" },
{ name = "sync_wrapper", version = "0.1.2" },
{ name = "syn", version = "1.0.109" },
{ name = "time", version = "<0.3" },
{ name = "toml_edit", version = "0.21.1" },
{ name = "untrusted", version = "0.7.1" },
{ name = "wasi", version = "<0.11" },
{ name = "windows_aarch64_msvc", version = "<0.48" },
{ name = "windows_i686_gnu", version = "<0.48" },
{ name = "windows_i686_msvc", version = "<0.48" },
{ name = "windows_aarch64_gnullvm", version = "<0.52" },
{ name = "windows_aarch64_msvc", version = "<0.52" },
{ name = "windows_i686_gnu", version = "<0.52" },
{ name = "windows_i686_msvc", version = "<0.52" },
{ name = "windows-sys", version = "<0.52" },
{ name = "windows-targets", version = "<0.52" },
{ name = "windows", version = "0.32.0" },
{ name = "windows_x86_64_gnu", version = "<0.48" },
{ name = "windows_x86_64_msvc", version = "<0.48" },
{ name = "windows_x86_64_gnullvm", version = "<0.52" },
{ name = "windows_x86_64_gnu", version = "<0.52" },
{ name = "windows_x86_64_msvc", version = "<0.52" },
{ name = "winnow", version = "0.5.40" },
]

288
flake.lock generated Normal file
View File

@@ -0,0 +1,288 @@
{
"nodes": {
"android": {
"inputs": {
"devshell": "devshell",
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1710633978,
"narHash": "sha256-yemnwSvW7cdWtXGpivFA5jDO35rGPs6fqxlQ4l6ODXs=",
"owner": "tadfisher",
"repo": "android-nixpkgs",
"rev": "e91fb3d8517538e5ad9b422c9a4f604b56008a9e",
"type": "github"
},
"original": {
"owner": "tadfisher",
"repo": "android-nixpkgs",
"type": "github"
}
},
"devshell": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"android",
"nixpkgs"
]
},
"locked": {
"lastModified": 1708939976,
"narHash": "sha256-O5+nFozxz2Vubpdl1YZtPrilcIXPcRAjqNdNE8oCRoA=",
"owner": "numtide",
"repo": "devshell",
"rev": "5ddecd67edbd568ebe0a55905273e56cc82aabe3",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"fenix": {
"inputs": {
"nixpkgs": "nixpkgs_2",
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1710742993,
"narHash": "sha256-W0PQCe0bW3hKF5lHawXrKynBcdSP18Qa4sb8DcUfOqI=",
"owner": "nix-community",
"repo": "fenix",
"rev": "6f2fec850f569d61562d3a47dc263f19e9c7d825",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1709126324,
"narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "d465f4819400de7c8d874d50b982301f28a84605",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_3": {
"inputs": {
"systems": "systems_3"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"naersk": {
"inputs": {
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1698420672,
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
"owner": "nix-community",
"repo": "naersk",
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "naersk",
"type": "github"
}
},
"nix-filter": {
"locked": {
"lastModified": 1710156097,
"narHash": "sha256-1Wvk8UP7PXdf8bCCaEoMnOT1qe5/Duqgj+rL8sRQsSM=",
"owner": "numtide",
"repo": "nix-filter",
"rev": "3342559a24e85fc164b295c3444e8a139924675b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "nix-filter",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1709237383,
"narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1710631334,
"narHash": "sha256-rL5LSYd85kplL5othxK5lmAtjyMOBg390sGBTb3LRMM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "c75037bbf9093a2acb617804ee46320d6d1fea5a",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1710765496,
"narHash": "sha256-p7ryWEeQfMwTB6E0wIUd5V2cFTgq+DRRBz2hYGnJZyA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e367f7a1fb93137af22a3908f00b9a35e2d286a7",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1710631334,
"narHash": "sha256-rL5LSYd85kplL5othxK5lmAtjyMOBg390sGBTb3LRMM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "c75037bbf9093a2acb617804ee46320d6d1fea5a",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"android": "android",
"fenix": "fenix",
"flake-utils": "flake-utils_3",
"naersk": "naersk",
"nix-filter": "nix-filter",
"nixpkgs": "nixpkgs_4"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1710708100,
"narHash": "sha256-Jd6pmXlwKk5uYcjyO/8BfbUVmx8g31Qfk7auI2IG66A=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "b6d1887bc4f9543b6c6bf098179a62446f34a6c3",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

542
flake.nix Normal file
View File

@@ -0,0 +1,542 @@
{
description = "Delta Chat core";
inputs = {
fenix.url = "github:nix-community/fenix";
flake-utils.url = "github:numtide/flake-utils";
naersk.url = "github:nix-community/naersk";
nix-filter.url = "github:numtide/nix-filter";
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
android.url = "github:tadfisher/android-nixpkgs";
};
outputs = { self, nixpkgs, flake-utils, nix-filter, naersk, fenix, android }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
inherit (pkgs.stdenv) isDarwin;
fenixPkgs = fenix.packages.${system};
naersk' = pkgs.callPackage naersk { };
manifest = (pkgs.lib.importTOML ./Cargo.toml).package;
androidSdk = android.sdk.${system} (sdkPkgs:
builtins.attrValues {
inherit (sdkPkgs) ndk-24-0-8215888 cmdline-tools-latest;
});
androidNdkRoot = "${androidSdk}/share/android-sdk/ndk/24.0.8215888";
rustSrc = nix-filter.lib {
root = ./.;
# Include only necessary files
# to avoid rebuilds e.g. when README.md or flake.nix changes.
include = [
./benches
./assets
./Cargo.lock
./Cargo.toml
./CMakeLists.txt
./CONTRIBUTING.md
./deltachat_derive
./deltachat-ffi
./deltachat-jsonrpc
./deltachat-ratelimit
./deltachat-repl
./deltachat-rpc-client
./deltachat-time
./deltachat-rpc-server
./format-flowed
./release-date.in
./src
];
exclude = [
(nix-filter.lib.matchExt "nix")
"flake.lock"
];
};
# Map from architecture name to rust targets and nixpkgs targets.
arch2targets = {
"x86_64-linux" = {
rustTarget = "x86_64-unknown-linux-musl";
crossTarget = "x86_64-unknown-linux-musl";
};
"armv7l-linux" = {
rustTarget = "armv7-unknown-linux-musleabihf";
crossTarget = "armv7l-unknown-linux-musleabihf";
};
"armv6l-linux" = {
rustTarget = "arm-unknown-linux-musleabihf";
crossTarget = "armv6l-unknown-linux-musleabihf";
};
"aarch64-linux" = {
rustTarget = "aarch64-unknown-linux-musl";
crossTarget = "aarch64-unknown-linux-musl";
};
"i686-linux" = {
rustTarget = "i686-unknown-linux-musl";
crossTarget = "i686-unknown-linux-musl";
};
"x86_64-darwin" = {
rustTarget = "x86_64-apple-darwin";
crossTarget = "x86_64-darwin";
};
"aarch64-darwin" = {
rustTarget = "aarch64-apple-darwin";
crossTarget = "aarch64-darwin";
};
};
cargoLock = {
lockFile = ./Cargo.lock;
outputHashes = {
"email-0.0.20" = "sha256-rV4Uzqt2Qdrfi5Ti1r+Si1c2iW1kKyWLwOgLkQ5JGGw=";
"encoded-words-0.2.0" = "sha256-KK9st0hLFh4dsrnLd6D8lC6pRFFs8W+WpZSGMGJcosk=";
"lettre-0.9.2" = "sha256-+hU1cFacyyeC9UGVBpS14BWlJjHy90i/3ynMkKAzclk=";
};
};
mkRustPackage = packageName:
naersk'.buildPackage {
pname = packageName;
cargoBuildOptions = x: x ++ [ "--package" packageName ];
version = manifest.version;
src = pkgs.lib.cleanSource ./.;
nativeBuildInputs = [
pkgs.perl # Needed to build vendored OpenSSL.
];
buildInputs = pkgs.lib.optionals isDarwin [
pkgs.darwin.apple_sdk.frameworks.SystemConfiguration
];
auditable = false; # Avoid cargo-auditable failures.
doCheck = false; # Disable test as it requires network access.
};
pkgsWin64 = pkgs.pkgsCross.mingwW64;
mkWin64RustPackage = packageName:
let
rustTarget = "x86_64-pc-windows-gnu";
toolchainWin = fenixPkgs.combine [
fenixPkgs.stable.rustc
fenixPkgs.stable.cargo
fenixPkgs.targets.${rustTarget}.stable.rust-std
];
naerskWin = pkgs.callPackage naersk {
cargo = toolchainWin;
rustc = toolchainWin;
};
in
naerskWin.buildPackage rec {
pname = packageName;
cargoBuildOptions = x: x ++ [ "--package" packageName ];
version = manifest.version;
strictDeps = true;
src = pkgs.lib.cleanSource ./.;
nativeBuildInputs = [
pkgs.perl # Needed to build vendored OpenSSL.
];
depsBuildBuild = [
pkgsWin64.stdenv.cc
pkgsWin64.windows.pthreads
];
auditable = false; # Avoid cargo-auditable failures.
doCheck = false; # Disable test as it requires network access.
CARGO_BUILD_TARGET = rustTarget;
TARGET_CC = "${pkgsWin64.stdenv.cc}/bin/${pkgsWin64.stdenv.cc.targetPrefix}cc";
CARGO_BUILD_RUSTFLAGS = [
"-C"
"linker=${TARGET_CC}"
];
CC = "${pkgsWin64.stdenv.cc}/bin/${pkgsWin64.stdenv.cc.targetPrefix}cc";
LD = "${pkgsWin64.stdenv.cc}/bin/${pkgsWin64.stdenv.cc.targetPrefix}cc";
};
pkgsWin32 = pkgs.pkgsCross.mingw32;
mkWin32RustPackage = packageName:
let
rustTarget = "i686-pc-windows-gnu";
in
let
toolchainWin = fenixPkgs.combine [
fenixPkgs.stable.rustc
fenixPkgs.stable.cargo
fenixPkgs.targets.${rustTarget}.stable.rust-std
];
naerskWin = pkgs.callPackage naersk {
cargo = toolchainWin;
rustc = toolchainWin;
};
# Get rid of MCF Gthread library.
# See <https://github.com/NixOS/nixpkgs/issues/156343>
# and <https://discourse.nixos.org/t/statically-linked-mingw-binaries/38395>
# for details.
#
# Use DWARF-2 instead of SJLJ for exception handling.
winCC = pkgsWin32.buildPackages.wrapCC (
(pkgsWin32.buildPackages.gcc-unwrapped.override
({
threadsCross = {
model = "win32";
package = null;
};
})).overrideAttrs (oldAttr: {
configureFlags = oldAttr.configureFlags ++ [
"--disable-sjlj-exceptions --with-dwarf2"
];
})
);
in
naerskWin.buildPackage rec {
pname = packageName;
cargoBuildOptions = x: x ++ [ "--package" packageName ];
version = manifest.version;
strictDeps = true;
src = pkgs.lib.cleanSource ./.;
nativeBuildInputs = [
pkgs.perl # Needed to build vendored OpenSSL.
];
depsBuildBuild = [
winCC
pkgsWin32.windows.pthreads
];
auditable = false; # Avoid cargo-auditable failures.
doCheck = false; # Disable test as it requires network access.
CARGO_BUILD_TARGET = rustTarget;
TARGET_CC = "${winCC}/bin/${winCC.targetPrefix}cc";
CARGO_BUILD_RUSTFLAGS = [
"-C"
"linker=${TARGET_CC}"
];
CC = "${winCC}/bin/${winCC.targetPrefix}cc";
LD = "${winCC}/bin/${winCC.targetPrefix}cc";
};
mkCrossRustPackage = arch: packageName:
let
rustTarget = arch2targets."${arch}".rustTarget;
crossTarget = arch2targets."${arch}".crossTarget;
pkgsCross = import nixpkgs {
system = system;
crossSystem.config = crossTarget;
};
in
let
toolchain = fenixPkgs.combine [
fenixPkgs.stable.rustc
fenixPkgs.stable.cargo
fenixPkgs.targets.${rustTarget}.stable.rust-std
];
naersk-lib = pkgs.callPackage naersk {
cargo = toolchain;
rustc = toolchain;
};
in
naersk-lib.buildPackage rec {
pname = packageName;
cargoBuildOptions = x: x ++ [ "--package" packageName ];
version = manifest.version;
strictDeps = true;
src = rustSrc;
nativeBuildInputs = [
pkgs.perl # Needed to build vendored OpenSSL.
];
auditable = false; # Avoid cargo-auditable failures.
doCheck = false; # Disable test as it requires network access.
CARGO_BUILD_TARGET = rustTarget;
TARGET_CC = "${pkgsCross.stdenv.cc}/bin/${pkgsCross.stdenv.cc.targetPrefix}cc";
CARGO_BUILD_RUSTFLAGS = [
"-C"
"linker=${TARGET_CC}"
];
CC = "${pkgsCross.stdenv.cc}/bin/${pkgsCross.stdenv.cc.targetPrefix}cc";
LD = "${pkgsCross.stdenv.cc}/bin/${pkgsCross.stdenv.cc.targetPrefix}cc";
};
androidAttrs = {
armeabi-v7a = {
cc = "armv7a-linux-androideabi19-clang";
rustTarget = "armv7-linux-androideabi";
};
arm64-v8a = {
cc = "aarch64-linux-android21-clang";
rustTarget = "aarch64-linux-android";
};
};
mkAndroidRustPackage = arch: packageName:
let
rustTarget = androidAttrs.${arch}.rustTarget;
toolchain = fenixPkgs.combine [
fenixPkgs.stable.rustc
fenixPkgs.stable.cargo
fenixPkgs.targets.${rustTarget}.stable.rust-std
];
naersk-lib = pkgs.callPackage naersk {
cargo = toolchain;
rustc = toolchain;
};
targetToolchain = "${androidNdkRoot}/toolchains/llvm/prebuilt/linux-x86_64";
targetCcName = androidAttrs.${arch}.cc;
targetCc = "${targetToolchain}/bin/${targetCcName}";
in
naersk-lib.buildPackage rec {
pname = packageName;
cargoBuildOptions = x: x ++ [ "--package" packageName ];
version = manifest.version;
strictDeps = true;
src = rustSrc;
nativeBuildInputs = [
pkgs.perl # Needed to build vendored OpenSSL.
];
auditable = false; # Avoid cargo-auditable failures.
doCheck = false; # Disable test as it requires network access.
CARGO_BUILD_TARGET = rustTarget;
TARGET_CC = "${targetCc}";
CARGO_BUILD_RUSTFLAGS = [
"-C"
"linker=${TARGET_CC}"
];
CC = "${targetCc}";
LD = "${targetCc}";
};
mkAndroidPackages = arch: {
"deltachat-rpc-server-${arch}-android" = mkAndroidRustPackage arch "deltachat-rpc-server";
"deltachat-repl-${arch}-android" = mkAndroidRustPackage arch "deltachat-repl";
};
mkRustPackages = arch:
let
rpc-server = mkCrossRustPackage arch "deltachat-rpc-server";
in
{
"deltachat-repl-${arch}" = mkCrossRustPackage arch "deltachat-repl";
"deltachat-rpc-server-${arch}" = rpc-server;
"deltachat-rpc-server-${arch}-wheel" =
pkgs.stdenv.mkDerivation {
pname = "deltachat-rpc-server-${arch}-wheel";
version = manifest.version;
src = nix-filter.lib {
root = ./.;
include = [
"scripts/wheel-rpc-server.py"
"deltachat-rpc-server/README.md"
"LICENSE"
"Cargo.toml"
];
};
nativeBuildInputs = [
pkgs.python3
pkgs.python3Packages.wheel
];
buildInputs = [
rpc-server
];
buildPhase = ''
mkdir tmp
cp ${rpc-server}/bin/deltachat-rpc-server tmp/deltachat-rpc-server
python3 scripts/wheel-rpc-server.py ${arch} tmp/deltachat-rpc-server
'';
installPhase = ''mkdir -p $out; cp -av deltachat_rpc_server-*.whl $out'';
};
};
in
{
formatter = pkgs.nixpkgs-fmt;
packages =
mkRustPackages "aarch64-linux" //
mkRustPackages "i686-linux" //
mkRustPackages "x86_64-linux" //
mkRustPackages "armv7l-linux" //
mkRustPackages "armv6l-linux" //
mkAndroidPackages "armeabi-v7a" //
mkAndroidPackages "arm64-v8a" //
mkAndroidPackages "x86" //
mkAndroidPackages "x86_64" // rec {
# Run with `nix run .#deltachat-repl foo.db`.
deltachat-repl = mkRustPackage "deltachat-repl";
deltachat-rpc-server = mkRustPackage "deltachat-rpc-server";
deltachat-repl-win64 = mkWin64RustPackage "deltachat-repl";
deltachat-rpc-server-win64 = mkWin64RustPackage "deltachat-rpc-server";
deltachat-rpc-server-win64-wheel =
pkgs.stdenv.mkDerivation {
pname = "deltachat-rpc-server-win64-wheel";
version = manifest.version;
src = nix-filter.lib {
root = ./.;
include = [
"scripts/wheel-rpc-server.py"
"deltachat-rpc-server/README.md"
"LICENSE"
"Cargo.toml"
];
};
nativeBuildInputs = [
pkgs.python3
pkgs.python3Packages.wheel
];
buildInputs = [
deltachat-rpc-server-win64
];
buildPhase = ''
mkdir tmp
cp ${deltachat-rpc-server-win64}/bin/deltachat-rpc-server.exe tmp/deltachat-rpc-server.exe
python3 scripts/wheel-rpc-server.py win64 tmp/deltachat-rpc-server.exe
'';
installPhase = ''mkdir -p $out; cp -av deltachat_rpc_server-*.whl $out'';
};
deltachat-repl-win32 = mkWin32RustPackage "deltachat-repl";
deltachat-rpc-server-win32 = mkWin32RustPackage "deltachat-rpc-server";
deltachat-rpc-server-win32-wheel =
pkgs.stdenv.mkDerivation {
pname = "deltachat-rpc-server-win32-wheel";
version = manifest.version;
src = nix-filter.lib {
root = ./.;
include = [
"scripts/wheel-rpc-server.py"
"deltachat-rpc-server/README.md"
"LICENSE"
"Cargo.toml"
];
};
nativeBuildInputs = [
pkgs.python3
pkgs.python3Packages.wheel
];
buildInputs = [
deltachat-rpc-server-win32
];
buildPhase = ''
mkdir tmp
cp ${deltachat-rpc-server-win32}/bin/deltachat-rpc-server.exe tmp/deltachat-rpc-server.exe
python3 scripts/wheel-rpc-server.py win32 tmp/deltachat-rpc-server.exe
'';
installPhase = ''mkdir -p $out; cp -av deltachat_rpc_server-*.whl $out'';
};
# Run `nix build .#docs` to get C docs generated in `./result/`.
docs =
pkgs.stdenv.mkDerivation {
pname = "docs";
version = manifest.version;
src = pkgs.lib.cleanSource ./.;
nativeBuildInputs = [ pkgs.doxygen ];
buildPhase = ''scripts/run-doxygen.sh'';
installPhase = ''mkdir -p $out; cp -av deltachat-ffi/html deltachat-ffi/xml $out'';
};
libdeltachat =
pkgs.stdenv.mkDerivation {
pname = "libdeltachat";
version = manifest.version;
src = rustSrc;
cargoDeps = pkgs.rustPlatform.importCargoLock cargoLock;
nativeBuildInputs = [
pkgs.perl # Needed to build vendored OpenSSL.
pkgs.cmake
pkgs.rustPlatform.cargoSetupHook
pkgs.cargo
];
buildInputs = pkgs.lib.optionals isDarwin [
pkgs.darwin.apple_sdk.frameworks.CoreFoundation
pkgs.darwin.apple_sdk.frameworks.Security
pkgs.darwin.apple_sdk.frameworks.SystemConfiguration
pkgs.libiconv
];
postInstall = ''
substituteInPlace $out/include/deltachat.h \
--replace __FILE__ '"${placeholder "out"}/include/deltachat.h"'
'';
};
# Source package for deltachat-rpc-server.
# Fake package that downloads Linux version,
# needed to install deltachat-rpc-server on Android with `pip`.
deltachat-rpc-server-source =
pkgs.stdenv.mkDerivation {
pname = "deltachat-rpc-server-source";
version = manifest.version;
src = pkgs.lib.cleanSource ./.;
nativeBuildInputs = [
pkgs.python3
pkgs.python3Packages.wheel
];
buildPhase = ''python3 scripts/wheel-rpc-server.py source deltachat-rpc-server-${manifest.version}.tar.gz'';
installPhase = ''mkdir -p $out; cp -av deltachat-rpc-server-${manifest.version}.tar.gz $out'';
};
deltachat-rpc-client =
pkgs.python3Packages.buildPythonPackage {
pname = "deltachat-rpc-client";
version = manifest.version;
src = pkgs.lib.cleanSource ./deltachat-rpc-client;
format = "pyproject";
propagatedBuildInputs = [
pkgs.python3Packages.setuptools
];
};
deltachat-python =
pkgs.python3Packages.buildPythonPackage {
pname = "deltachat-python";
version = manifest.version;
src = pkgs.lib.cleanSource ./python;
format = "pyproject";
buildInputs = [
libdeltachat
];
nativeBuildInputs = [
pkgs.pkg-config
];
propagatedBuildInputs = [
pkgs.python3Packages.setuptools
pkgs.python3Packages.pkgconfig
pkgs.python3Packages.cffi
pkgs.python3Packages.imap-tools
pkgs.python3Packages.pluggy
pkgs.python3Packages.requests
];
};
python-docs =
pkgs.stdenv.mkDerivation {
pname = "docs";
version = manifest.version;
src = pkgs.lib.cleanSource ./.;
buildInputs = [
deltachat-python
deltachat-rpc-client
pkgs.python3Packages.breathe
pkgs.python3Packages.sphinx_rtd_theme
];
nativeBuildInputs = [ pkgs.sphinx ];
buildPhase = ''sphinx-build -b html -a python/doc/ dist/html'';
installPhase = ''mkdir -p $out; cp -av dist/html $out'';
};
};
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
(fenixPkgs.complete.withComponents [
"cargo"
"clippy"
"rust-src"
"rustc"
"rustfmt"
])
cargo-deny
fenixPkgs.rust-analyzer
perl # needed to build vendored OpenSSL
];
};
}
);
}

992
fuzz/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -29,9 +29,11 @@ module.exports = {
DC_DOWNLOAD_FAILURE: 20,
DC_DOWNLOAD_IN_PROGRESS: 1000,
DC_DOWNLOAD_UNDECIPHERABLE: 30,
DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE: 2200,
DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED: 2021,
DC_EVENT_CHAT_MODIFIED: 2020,
DC_EVENT_CONFIGURE_PROGRESS: 2041,
DC_EVENT_CONFIG_SYNCED: 2111,
DC_EVENT_CONNECTIVITY_CHANGED: 2100,
DC_EVENT_CONTACTS_CHANGED: 2030,
DC_EVENT_DELETED_BLOB_FILE: 151,
@@ -79,6 +81,7 @@ module.exports = {
DC_INFO_EPHEMERAL_TIMER_CHANGED: 10,
DC_INFO_GROUP_IMAGE_CHANGED: 3,
DC_INFO_GROUP_NAME_CHANGED: 2,
DC_INFO_INVALID_UNENCRYPTED_MAIL: 13,
DC_INFO_LOCATIONSTREAMING_ENABLED: 8,
DC_INFO_LOCATION_ONLY: 9,
DC_INFO_MEMBER_ADDED_TO_GROUP: 4,
@@ -112,6 +115,9 @@ module.exports = {
DC_PROVIDER_STATUS_BROKEN: 3,
DC_PROVIDER_STATUS_OK: 1,
DC_PROVIDER_STATUS_PREPARATION: 2,
DC_PUSH_CONNECTED: 2,
DC_PUSH_HEARTBEAT: 1,
DC_PUSH_NOT_CONNECTED: 0,
DC_QR_ACCOUNT: 250,
DC_QR_ADDR: 320,
DC_QR_ASK_VERIFYCONTACT: 200,
@@ -225,11 +231,13 @@ module.exports = {
DC_STR_GROUP_NAME_CHANGED_BY_YOU: 124,
DC_STR_IMAGE: 9,
DC_STR_INCOMING_MESSAGES: 103,
DC_STR_INVALID_UNENCRYPTED_MAIL: 174,
DC_STR_LAST_MSG_SENT_SUCCESSFULLY: 111,
DC_STR_LOCATION: 66,
DC_STR_LOCATION_ENABLED_BY_OTHER: 137,
DC_STR_LOCATION_ENABLED_BY_YOU: 136,
DC_STR_MESSAGES: 114,
DC_STR_MESSAGE_ADD_MEMBER: 173,
DC_STR_MSGACTIONBYME: 63,
DC_STR_MSGACTIONBYUSER: 62,
DC_STR_MSGADDMEMBER: 17,
@@ -249,6 +257,7 @@ module.exports = {
DC_STR_PARTIAL_DOWNLOAD_MSG_BODY: 99,
DC_STR_PART_OF_TOTAL_USED: 116,
DC_STR_QUOTA_EXCEEDING_MSG_BODY: 98,
DC_STR_REACTED_BY: 177,
DC_STR_READRCPT: 31,
DC_STR_READRCPT_MAILBODY: 32,
DC_STR_REMOVE_MEMBER_BY_OTHER: 131,
@@ -276,6 +285,7 @@ module.exports = {
DC_STR_VIDEOCHAT_INVITE_MSG_BODY: 83,
DC_STR_VOICEMESSAGE: 7,
DC_STR_WELCOME_MESSAGE: 71,
DC_STR_YOU_REACTED: 176,
DC_TEXT1_DRAFT: 1,
DC_TEXT1_SELF: 3,
DC_TEXT1_USERNAME: 2,

View File

@@ -34,6 +34,8 @@ module.exports = {
2061: 'DC_EVENT_SECUREJOIN_JOINER_PROGRESS',
2100: 'DC_EVENT_CONNECTIVITY_CHANGED',
2110: 'DC_EVENT_SELFAVATAR_CHANGED',
2111: 'DC_EVENT_CONFIG_SYNCED',
2120: 'DC_EVENT_WEBXDC_STATUS_UPDATE',
2121: 'DC_EVENT_WEBXDC_INSTANCE_DELETED'
2121: 'DC_EVENT_WEBXDC_INSTANCE_DELETED',
2200: 'DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE'
}

View File

@@ -29,9 +29,11 @@ export enum C {
DC_DOWNLOAD_FAILURE = 20,
DC_DOWNLOAD_IN_PROGRESS = 1000,
DC_DOWNLOAD_UNDECIPHERABLE = 30,
DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE = 2200,
DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED = 2021,
DC_EVENT_CHAT_MODIFIED = 2020,
DC_EVENT_CONFIGURE_PROGRESS = 2041,
DC_EVENT_CONFIG_SYNCED = 2111,
DC_EVENT_CONNECTIVITY_CHANGED = 2100,
DC_EVENT_CONTACTS_CHANGED = 2030,
DC_EVENT_DELETED_BLOB_FILE = 151,
@@ -79,6 +81,7 @@ export enum C {
DC_INFO_EPHEMERAL_TIMER_CHANGED = 10,
DC_INFO_GROUP_IMAGE_CHANGED = 3,
DC_INFO_GROUP_NAME_CHANGED = 2,
DC_INFO_INVALID_UNENCRYPTED_MAIL = 13,
DC_INFO_LOCATIONSTREAMING_ENABLED = 8,
DC_INFO_LOCATION_ONLY = 9,
DC_INFO_MEMBER_ADDED_TO_GROUP = 4,
@@ -112,6 +115,9 @@ export enum C {
DC_PROVIDER_STATUS_BROKEN = 3,
DC_PROVIDER_STATUS_OK = 1,
DC_PROVIDER_STATUS_PREPARATION = 2,
DC_PUSH_CONNECTED = 2,
DC_PUSH_HEARTBEAT = 1,
DC_PUSH_NOT_CONNECTED = 0,
DC_QR_ACCOUNT = 250,
DC_QR_ADDR = 320,
DC_QR_ASK_VERIFYCONTACT = 200,
@@ -225,11 +231,13 @@ export enum C {
DC_STR_GROUP_NAME_CHANGED_BY_YOU = 124,
DC_STR_IMAGE = 9,
DC_STR_INCOMING_MESSAGES = 103,
DC_STR_INVALID_UNENCRYPTED_MAIL = 174,
DC_STR_LAST_MSG_SENT_SUCCESSFULLY = 111,
DC_STR_LOCATION = 66,
DC_STR_LOCATION_ENABLED_BY_OTHER = 137,
DC_STR_LOCATION_ENABLED_BY_YOU = 136,
DC_STR_MESSAGES = 114,
DC_STR_MESSAGE_ADD_MEMBER = 173,
DC_STR_MSGACTIONBYME = 63,
DC_STR_MSGACTIONBYUSER = 62,
DC_STR_MSGADDMEMBER = 17,
@@ -249,6 +257,7 @@ export enum C {
DC_STR_PARTIAL_DOWNLOAD_MSG_BODY = 99,
DC_STR_PART_OF_TOTAL_USED = 116,
DC_STR_QUOTA_EXCEEDING_MSG_BODY = 98,
DC_STR_REACTED_BY = 177,
DC_STR_READRCPT = 31,
DC_STR_READRCPT_MAILBODY = 32,
DC_STR_REMOVE_MEMBER_BY_OTHER = 131,
@@ -276,6 +285,7 @@ export enum C {
DC_STR_VIDEOCHAT_INVITE_MSG_BODY = 83,
DC_STR_VOICEMESSAGE = 7,
DC_STR_WELCOME_MESSAGE = 71,
DC_STR_YOU_REACTED = 176,
DC_TEXT1_DRAFT = 1,
DC_TEXT1_SELF = 3,
DC_TEXT1_USERNAME = 2,
@@ -319,6 +329,8 @@ export const EventId2EventName: { [key: number]: string } = {
2061: 'DC_EVENT_SECUREJOIN_JOINER_PROGRESS',
2100: 'DC_EVENT_CONNECTIVITY_CHANGED',
2110: 'DC_EVENT_SELFAVATAR_CHANGED',
2111: 'DC_EVENT_CONFIG_SYNCED',
2120: 'DC_EVENT_WEBXDC_STATUS_UPDATE',
2121: 'DC_EVENT_WEBXDC_INSTANCE_DELETED',
2200: 'DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE',
}

View File

@@ -178,7 +178,7 @@ export class AccountManager extends EventEmitter {
static newTemporary() {
let directory = null
while (true) {
const randomString = Math.random().toString(36).substr(2, 5)
const randomString = Math.random().toString(36).substring(2, 5)
directory = join(tmpdir(), 'deltachat-' + randomString)
if (!existsSync(directory)) break
}

View File

@@ -3048,14 +3048,6 @@ NAPI_METHOD(dcn_accounts_select_account) {
NAPI_RETURN_UINT32(result);
}
NAPI_METHOD(dcn_accounts_all_work_done) {
NAPI_ARGV(1);
NAPI_DCN_ACCOUNTS();
int result = dc_accounts_all_work_done(dcn_accounts->dc_accounts);
NAPI_RETURN_INT32(result);
}
NAPI_METHOD(dcn_accounts_start_io) {
NAPI_ARGV(1);
NAPI_DCN_ACCOUNTS();
@@ -3382,7 +3374,6 @@ NAPI_INIT() {
NAPI_EXPORT_FUNCTION(dcn_accounts_get_account);
NAPI_EXPORT_FUNCTION(dcn_accounts_get_selected_account);
NAPI_EXPORT_FUNCTION(dcn_accounts_select_account);
NAPI_EXPORT_FUNCTION(dcn_accounts_all_work_done);
NAPI_EXPORT_FUNCTION(dcn_accounts_start_io);
NAPI_EXPORT_FUNCTION(dcn_accounts_stop_io);
NAPI_EXPORT_FUNCTION(dcn_accounts_maybe_network);

View File

@@ -1,13 +1,17 @@
// @ts-check
import DeltaChat from '../dist'
import { DeltaChat } from '../dist/index.js'
import { deepStrictEqual, strictEqual } from 'assert'
import chai, { expect } from 'chai'
import chaiAsPromised from 'chai-as-promised'
import { EventId2EventName, C } from '../dist/constants'
import { EventId2EventName, C } from '../dist/constants.js'
import { join } from 'path'
import { statSync } from 'fs'
import { Context } from '../dist/context'
import { Context } from '../dist/context.js'
import {fileURLToPath} from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
chai.use(chaiAsPromised)
chai.config.truncateThreshold = 0 // Do not truncate assertion errors.
@@ -235,7 +239,7 @@ describe('Basic offline Tests', function () {
'delete_device_after',
'delete_server_after',
'deltachat_core_version',
'display_name',
'displayname',
'download_limit',
'e2ee_enabled',
'entered_account_settings',
@@ -246,6 +250,7 @@ describe('Basic offline Tests', function () {
'journal_mode',
'key_gen_type',
'last_housekeeping',
'last_cant_decrypt_outgoing_msgs',
'level',
'mdns_enabled',
'media_quality',

View File

@@ -8,9 +8,8 @@
"devDependencies": {
"@types/debug": "^4.1.7",
"@types/node": "^20.8.10",
"chai": "^4.2.0",
"chai": "~4.3.10",
"chai-as-promised": "^7.1.1",
"esm": "^3.2.25",
"mocha": "^8.2.1",
"node-gyp": "^10.0.0",
"prebuildify": "^5.0.1",
@@ -53,8 +52,8 @@
"prebuildify": "cd node && prebuildify -t 18.0.0 --napi --strip --postinstall \"node scripts/postinstall.js --prebuild\"",
"test": "npm run test:lint && npm run test:mocha",
"test:lint": "npm run lint",
"test:mocha": "mocha -r esm node/test/test.js --growl --reporter=spec --bail --exit"
"test:mocha": "mocha node/test/test.mjs --growl --reporter=spec --bail --exit"
},
"types": "node/dist/index.d.ts",
"version": "1.131.6"
"version": "1.137.2"
}

View File

@@ -1,9 +1,10 @@
[build-system]
requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2", "cffi>=1.0.0", "pkgconfig"]
requires = ["setuptools>=45", "wheel", "cffi>=1.0.0", "pkgconfig"]
build-backend = "setuptools.build_meta"
[project]
name = "deltachat"
version = "1.137.2"
description = "Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat"
readme = "README.rst"
requires-python = ">=3.7"
@@ -26,9 +27,6 @@ dependencies = [
"pluggy",
"requests",
]
dynamic = [
"version"
]
[project.urls]
"Home" = "https://github.com/deltachat/deltachat-core-rust/"
@@ -44,11 +42,6 @@ deltachat = [
"py.typed"
]
[tool.setuptools_scm]
root = ".."
tag_regex = '^(?P<prefix>v)?(?P<version>[^\+]+)(?P<suffix>.*)?$'
git_describe_command = "git describe --dirty --tags --long --match v*.*"
[tool.black]
line-length = 120

View File

@@ -1,6 +1,5 @@
"""Account class implementation."""
import os
from array import array
from contextlib import contextmanager
@@ -376,22 +375,6 @@ class Account:
dc_array = ffi.gc(lib.dc_get_fresh_msgs(self._dc_context), lib.dc_array_unref)
return (x for x in iter_array(dc_array, lambda x: Message.from_db(self, x)) if x is not None)
def _wait_next_message_ids(self) -> List[int]:
"""Return IDs of all next messages from all chats."""
dc_array = ffi.gc(lib.dc_wait_next_msgs(self._dc_context), lib.dc_array_unref)
return [lib.dc_array_get_id(dc_array, i) for i in range(lib.dc_array_get_cnt(dc_array))]
def wait_next_incoming_message(self) -> Message:
"""Waits until the next incoming message
with ID higher than given is received and returns it."""
while True:
message_ids = self._wait_next_message_ids()
for msg_id in message_ids:
message = Message.from_db(self, msg_id)
if message and not message.is_from_self() and not message.is_from_device():
self.set_config("last_msg_id", str(msg_id))
return message
def create_chat(self, obj) -> Chat:
"""Create a 1:1 chat with Account, Contact or e-mail address."""
return self.create_contact(obj).create_chat()
@@ -478,6 +461,16 @@ class Account:
msg_ids = [msg.id for msg in messages]
lib.dc_forward_msgs(self._dc_context, msg_ids, len(msg_ids), chat.id)
def resend_messages(self, messages: List[Message]) -> None:
"""Resend list of messages.
:param messages: list of :class:`deltachat.message.Message` object.
:returns: None
"""
msg_ids = [msg.id for msg in messages]
if lib.dc_resend_msgs(self._dc_context, msg_ids, len(msg_ids)) != 1:
raise ValueError(f"could not resend messages {msg_ids}")
def delete_messages(self, messages: List[Message]) -> None:
"""delete messages (local and remote).

View File

@@ -182,6 +182,12 @@ class FFIEventTracker:
print(f"** SECUREJOINT-INVITER PROGRESS {target}", self.account)
break
def wait_securejoin_joiner_progress(self, target):
while True:
event = self.get_matching("DC_EVENT_SECUREJOIN_JOINER_PROGRESS")
if event.data2 >= target:
break
def wait_idle_inbox_ready(self):
"""Has to be called after start_io() to wait for fetch_existing_msgs to run
so that new messages are not mistaken for old ones:

View File

@@ -115,7 +115,7 @@ def pytest_configure(config):
deltachat.register_global_plugin(la)
def pytest_report_header(config, startdir):
def pytest_report_header(config):
info = get_core_info()
summary = [
"Deltachat core={} sqlite={} journal_mode={}".format(
@@ -590,6 +590,20 @@ class ACFactory:
ac2.create_chat(ac1)
return ac1.create_chat(ac2)
def get_protected_chat(self, ac1: Account, ac2: Account):
chat = ac1.create_group_chat("Protected Group", verified=True)
qr = chat.get_join_qr()
ac2.qr_join_chat(qr)
ac2._evtracker.wait_securejoin_joiner_progress(1000)
ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED")
msg = ac2.get_message_by_id(ev.data2)
assert msg is not None
assert msg.text == "Messages are guaranteed to be end-to-end encrypted from now on."
msg = ac2._evtracker.wait_next_incoming_message()
assert msg is not None
assert "Member Me " in msg.text and " added by " in msg.text
return chat
def introduce_each_other(self, accounts, sending=True):
to_wait = []
for i, acc in enumerate(accounts):

View File

@@ -547,13 +547,14 @@ def test_see_new_verified_member_after_going_online(acfactory, tmp_path, lp):
assert msg_in.get_sender_contact().addr == ac2_addr
def test_use_new_verified_group_after_going_online(acfactory, tmp_path, lp):
def test_use_new_verified_group_after_going_online(acfactory, data, tmp_path, lp):
"""Another test for the bug #3836:
- Bob has two devices, the second is offline.
- Alice creates a verified group and sends a QR invitation to Bob.
- Bob joins the group.
- Bob's second devices goes online, but sees a contact request instead of the verified group.
- The "member added" message is not a system message but a plain text message.
- Bob's second device doesn't display the Alice's avatar (bug #5354).
- Sending a message fails as the key is missing -- message info says "proper enc-key for <Alice>
missing, cannot encrypt".
"""
@@ -568,6 +569,10 @@ def test_use_new_verified_group_after_going_online(acfactory, tmp_path, lp):
ac2_offl.import_self_keys(str(dir))
ac2_offl.stop_io()
lp.sec("ac1: set avatar")
avatar_path = data.get_path("d.png")
ac1.set_avatar(avatar_path)
lp.sec("ac1: create verified-group QR, ac2 scans and joins")
chat = ac1.create_group_chat("hello", verified=True)
assert chat.is_protected()
@@ -580,11 +585,13 @@ def test_use_new_verified_group_after_going_online(acfactory, tmp_path, lp):
ac2_offl.start_io()
# Receive "Member Me (<addr>) added by <addr>." message.
msg_in = ac2_offl._evtracker.wait_next_incoming_message()
contact = msg_in.get_sender_contact()
assert msg_in.is_system_message()
assert msg_in.get_sender_contact().addr == ac1.get_config("addr")
assert contact.addr == ac1.get_config("addr")
chat2 = msg_in.chat
assert chat2.is_protected()
assert chat2.get_messages()[0].text == "Messages are guaranteed to be end-to-end encrypted from now on."
assert open(contact.get_profile_image(), "rb").read() == open(avatar_path, "rb").read()
lp.sec("ac2_offl: sending message")
msg_out = chat2.send_text("hello")

View File

@@ -44,21 +44,21 @@ def test_configure_generate_key(acfactory, lp):
lp.sec("ac1: send unencrypted message to ac2")
chat.send_text("message1")
lp.sec("ac2: waiting for message from ac1")
msg_in = ac2.wait_next_incoming_message()
msg_in = ac2._evtracker.wait_next_incoming_message()
assert msg_in.text == "message1"
assert not msg_in.is_encrypted()
lp.sec("ac2: send encrypted message to ac1")
msg_in.chat.send_text("message2")
lp.sec("ac1: waiting for message from ac2")
msg2_in = ac1.wait_next_incoming_message()
msg2_in = ac1._evtracker.wait_next_incoming_message()
assert msg2_in.text == "message2"
assert msg2_in.is_encrypted()
lp.sec("ac1: send encrypted message to ac2")
msg2_in.chat.send_text("message3")
lp.sec("ac2: waiting for message from ac1")
msg3_in = ac2.wait_next_incoming_message()
msg3_in = ac2._evtracker.wait_next_incoming_message()
assert msg3_in.text == "message3"
assert msg3_in.is_encrypted()
@@ -382,9 +382,24 @@ def test_webxdc_download_on_demand(acfactory, data, lp):
assert msgs_changed_event.data2 == 0
def test_enable_mvbox_move(acfactory, lp):
(ac1,) = acfactory.get_online_accounts(1)
lp.sec("ac2: start without mvbox thread")
ac2 = acfactory.new_online_configuring_account(mvbox_move=False)
acfactory.bring_accounts_online()
lp.sec("ac2: configuring mvbox")
ac2.set_config("mvbox_move", "1")
lp.sec("ac1: send message and wait for ac2 to receive it")
acfactory.get_accepted_chat(ac1, ac2).send_text("message1")
assert ac2._evtracker.wait_next_incoming_message().text == "message1"
def test_mvbox_sentbox_threads(acfactory, lp):
lp.sec("ac1: start with mvbox thread")
ac1 = acfactory.new_online_configuring_account(mvbox_move=True, sentbox_watch=True)
ac1 = acfactory.new_online_configuring_account(mvbox_move=True, sentbox_watch=False)
lp.sec("ac2: start without mvbox/sentbox threads")
ac2 = acfactory.new_online_configuring_account(mvbox_move=False, sentbox_watch=False)
@@ -392,10 +407,18 @@ def test_mvbox_sentbox_threads(acfactory, lp):
lp.sec("ac2 and ac1: waiting for configuration")
acfactory.bring_accounts_online()
lp.sec("ac1: create and configure sentbox")
ac1.direct_imap.create_folder("Sent")
ac1.set_config("sentbox_watch", "1")
lp.sec("ac1: send message and wait for ac2 to receive it")
acfactory.get_accepted_chat(ac1, ac2).send_text("message1")
assert ac2._evtracker.wait_next_incoming_message().text == "message1"
assert ac1.get_config("configured_mvbox_folder") == "DeltaChat"
while ac1.get_config("configured_sentbox_folder") != "Sent":
ac1._evtracker.get_matching("DC_EVENT_CONNECTIVITY_CHANGED")
def test_move_works(acfactory):
ac1 = acfactory.new_online_configuring_account()
@@ -498,6 +521,26 @@ def test_forward_messages(acfactory, lp):
assert not chat3.get_messages()
def test_forward_encrypted_to_unencrypted(acfactory, lp):
ac1, ac2, ac3 = acfactory.get_online_accounts(3)
chat = acfactory.get_protected_chat(ac1, ac2)
lp.sec("ac1: send encrypted message to ac2")
txt = "This should be encrypted"
chat.send_text(txt)
msg = ac2._evtracker.wait_next_incoming_message()
assert msg.text == txt
assert msg.is_encrypted()
lp.sec("ac2: forward message to ac3 unencrypted")
unencrypted_chat = ac2.create_chat(ac3)
msg_id = msg.id
msg2 = unencrypted_chat.send_msg(msg)
assert msg2 == msg
assert msg.id != msg_id
assert not msg.is_encrypted()
def test_forward_own_message(acfactory, lp):
ac1, ac2 = acfactory.get_online_accounts(2)
chat = acfactory.get_accepted_chat(ac1, ac2)
@@ -523,6 +566,27 @@ def test_forward_own_message(acfactory, lp):
assert msg_in.is_forwarded()
def test_resend_message(acfactory, lp):
ac1, ac2 = acfactory.get_online_accounts(2)
chat1 = ac1.create_chat(ac2)
lp.sec("ac1: send message to ac2")
chat1.send_text("message")
lp.sec("ac2: receive message")
msg_in = ac2._evtracker.wait_next_incoming_message()
assert msg_in.text == "message"
chat2 = msg_in.chat
chat2_msg_cnt = len(chat2.get_messages())
lp.sec("ac1: resend message")
ac1.resend_messages([msg_in])
lp.sec("ac2: check that message is deleted")
ac2._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED")
assert len(chat2.get_messages()) == chat2_msg_cnt
def test_long_group_name(acfactory, lp):
"""See bug https://github.com/deltachat/deltachat-core-rust/issues/3650 "Space added before long
group names after MIME serialization/deserialization".
@@ -1531,10 +1595,11 @@ def test_reactions_for_a_reordering_move(acfactory, lp):
def test_import_export_online_all(acfactory, tmp_path, data, lp):
(ac1,) = acfactory.get_online_accounts(1)
(ac1, some1) = acfactory.get_online_accounts(2)
lp.sec("create some chat content")
chat1 = ac1.create_contact("some1@example.org", name="some1").create_chat()
some1_addr = some1.get_config("addr")
chat1 = ac1.create_contact(some1_addr, name="some1").create_chat()
chat1.send_text("msg1")
assert len(ac1.get_contacts(query="some1")) == 1
@@ -1551,7 +1616,7 @@ def test_import_export_online_all(acfactory, tmp_path, data, lp):
contacts = ac.get_contacts(query="some1")
assert len(contacts) == 1
contact2 = contacts[0]
assert contact2.addr == "some1@example.org"
assert contact2.addr == some1_addr
chat2 = contact2.create_chat()
messages = chat2.get_messages()
assert len(messages) == 3
@@ -1979,6 +2044,32 @@ def test_connectivity(acfactory, lp):
ac1._evtracker.wait_for_connectivity(dc.const.DC_CONNECTIVITY_NOT_CONNECTED)
def test_all_work_done(acfactory, lp):
"""
Tests that calling start_io() immediately followed by maybe_network()
and then waiting for all_work_done() reliably fetches the messages
delivered while account was offline.
In other words, connectivity should not change to a state
where all_work_done() returns true until IMAP connection goes idle.
"""
ac1, ac2 = acfactory.get_online_accounts(2)
ac1.stop_io()
ac1._evtracker.wait_for_connectivity(dc.const.DC_CONNECTIVITY_NOT_CONNECTED)
ac1.direct_imap.select_config_folder("inbox")
with ac1.direct_imap.idle() as idle1:
ac2.create_chat(ac1).send_text("Hi")
idle1.wait_for_new_message()
ac1.start_io()
ac1.maybe_network()
ac1._evtracker.wait_for_all_work_done()
msgs = ac1.create_chat(ac2).get_messages()
assert len(msgs) == 1
assert msgs[0].text == "Hi"
def test_fetch_deleted_msg(acfactory, lp):
"""This is a regression test: Messages with \\Deleted flag were downloaded again and again,
hundreds of times, because uid_next was not updated.

View File

@@ -52,8 +52,8 @@ class TestOfflineAccountBasic:
def test_wrong_db(self, tmp_path):
p = tmp_path / "hello.db"
p.write_text("123")
account = Account(str(p))
assert not account.is_open()
with pytest.raises(ValueError):
_account = Account(str(p))
def test_os_name(self, tmp_path):
p = tmp_path / "hello.db"

View File

@@ -156,6 +156,8 @@ def test_markseen_invalid_message_ids(acfactory):
chat = contact1.create_chat()
chat.send_text("one message")
ac1._evtracker.get_matching("DC_EVENT_MSGS_CHANGED")
# Skip configuration-related warnings, but not errors.
ac1._evtracker.ensure_event_not_queued("DC_EVENT_ERROR")
msg_ids = [9]
lib.dc_markseen_msgs(ac1._dc_context, msg_ids, len(msg_ids))
ac1._evtracker.ensure_event_not_queued("DC_EVENT_WARNING|DC_EVENT_ERROR")

View File

@@ -1 +1 @@
2023-11-21
2024-04-05

View File

@@ -35,12 +35,8 @@ and an own build machine.
- `run_all.sh` builds Python wheels
- `zig-rpc-server.sh` compiles binaries of `deltachat-rpc-server` using Zig toolchain statically linked against musl libc.
- `android-rpc-server.sh` compiles binaries of `deltachat-rpc-server` using Android NDK.
- `build-python-docs.sh` builds Python documentation into `dist/html/`.
## Triggering runs on the build machine locally (fast!)
There is experimental support for triggering a remote Python or Rust test run

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
export DCC_RS_TARGET=debug
export DCC_RS_DEV="$PWD"
cargo build -p deltachat_ffi --features jsonrpc
python3 -m venv venv
venv/bin/pip install ./python
venv/bin/pip install ./deltachat-rpc-client
venv/bin/pip install sphinx breathe sphinx_rtd_theme
venv/bin/pip install ./deltachat-rpc-client
venv/bin/sphinx-build -b html -a python/doc/ dist/html

View File

@@ -15,55 +15,6 @@ resources:
tag_filter: "v*"
jobs:
- name: doxygen
plan:
- get: deltachat-core-rust
trigger: true
# Build Doxygen documentation
- task: build-doxygen
config:
inputs:
- name: deltachat-core-rust
outputs:
- name: c-docs
image_resource:
source:
repository: alpine
type: registry-image
platform: linux
run:
path: sh
args:
- -ec
- |
apk add --no-cache doxygen git
cd deltachat-core-rust
scripts/run-doxygen.sh
cd ..
cp -av deltachat-core-rust/deltachat-ffi/html deltachat-core-rust/deltachat-ffi/xml c-docs/
- task: upload-c-docs
config:
inputs:
- name: c-docs
image_resource:
type: registry-image
source:
repository: alpine
platform: linux
run:
path: sh
args:
- -ec
- |
apk add --no-cache rsync openssh-client
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "(("c.delta.chat".private_key))" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
rsync -e "ssh -o StrictHostKeyChecking=no" -avz --delete c-docs/html/ delta@c.delta.chat:build-c/master
- name: python-x86_64
plan:
- get: deltachat-core-rust

View File

@@ -7,7 +7,7 @@ set -euo pipefail
#
# Avoid using rustup here as it depends on reading /proc/self/exe and
# has problems running under QEMU.
RUST_VERSION=1.72.0
RUST_VERSION=1.77.1
ARCH="$(uname -m)"
test -f "/lib/libc.musl-$ARCH.so.1" && LIBC=musl || LIBC=gnu

View File

@@ -220,9 +220,9 @@ if __name__ == "__main__":
process_dir(Path(sys.argv[1]))
out_all += "pub(crate) static PROVIDER_DATA: Lazy<HashMap<&'static str, &'static Provider>> = Lazy::new(|| HashMap::from([\n"
out_all += "pub(crate) static PROVIDER_DATA: [(&str, &Provider); " + str(len(domains_set)) + "] = [\n";
out_all += out_domains
out_all += "]));\n\n"
out_all += "];\n\n"
out_all += "pub(crate) static PROVIDER_IDS: Lazy<HashMap<&'static str, &'static Provider>> = Lazy::new(|| HashMap::from([\n"
out_all += out_ids

View File

@@ -73,6 +73,8 @@ def main():
"deltachat-jsonrpc/Cargo.toml",
"deltachat-rpc-server/Cargo.toml",
"deltachat-repl/Cargo.toml",
"python/pyproject.toml",
"deltachat-rpc-client/pyproject.toml",
]
try:
opts = parser.parse_args()
@@ -102,7 +104,7 @@ def main():
found = True
if not found:
raise SystemExit(
f"{changelog_name} contains no entry for version: {newversion}"
f"CHANGELOG.md contains no entry for version: {newversion}"
)
for toml_filename in toml_list:

View File

@@ -6,7 +6,7 @@ set -euo pipefail
export TZ=UTC
# Provider database revision.
REV=18f714cf73d0bdfb8b013fa344494ab80c92b477
REV=2f3db24107e4802c2df0aa0a40f0e144006c0a9b
CORE_ROOT="$PWD"
TMP="$(mktemp -d)"

View File

@@ -1,24 +1,26 @@
#!/usr/bin/env python3
"""Build Python wheels for deltachat-rpc-server.
Run scripts/zig-rpc-server.sh first."""
"""Build Python wheels for deltachat-rpc-server."""
from pathlib import Path
from wheel.wheelfile import WheelFile
import tomllib
import tarfile
import sys
from io import BytesIO
def metadata_contents(version):
readme_text = (Path("deltachat-rpc-server") / "README.md").read_text()
return f"""Metadata-Version: 2.1
Name: deltachat-rpc-server
Version: {version}
Summary: Delta Chat JSON-RPC server
Description-Content-Type: text/markdown
{readme_text}
"""
def build_source_package(version):
filename = f"dist/deltachat-rpc-server-{version}.tar.gz"
def build_source_package(version, filename):
with tarfile.open(filename, "w:gz") as pkg:
def pack(name, contents):
@@ -99,7 +101,7 @@ setup(
def build_wheel(version, binary, tag, windows=False):
filename = f"dist/deltachat_rpc_server-{version}-{tag}.whl"
filename = f"deltachat_rpc_server-{version}-{tag}.whl"
with WheelFile(filename, "w") as wheel:
wheel.write("LICENSE", "deltachat_rpc_server/LICENSE")
@@ -126,9 +128,11 @@ def main():
Path(binary).chmod(0o755)
wheel.write(
binary,
"deltachat_rpc_server/deltachat-rpc-server.exe"
if windows
else "deltachat_rpc_server/deltachat-rpc-server",
(
"deltachat_rpc_server/deltachat-rpc-server.exe"
if windows
else "deltachat_rpc_server/deltachat-rpc-server"
),
)
wheel.writestr(
f"deltachat_rpc_server-{version}.dist-info/METADATA",
@@ -144,54 +148,41 @@ def main():
)
def main():
with open("deltachat-rpc-server/Cargo.toml", "rb") as f:
cargo_toml = tomllib.load(f)
version = cargo_toml["package"]["version"]
Path("dist").mkdir(exist_ok=True)
build_source_package(version)
build_wheel(
version,
"dist/deltachat-rpc-server-x86_64-linux",
"py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.musllinux_1_1_x86_64",
)
build_wheel(
version,
"dist/deltachat-rpc-server-armv7-linux",
"py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l",
)
build_wheel(
version,
"dist/deltachat-rpc-server-aarch64-linux",
"py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64",
)
build_wheel(
version,
"dist/deltachat-rpc-server-i686-linux",
"py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686",
)
arch2tags = {
"x86_64-linux": "manylinux_2_17_x86_64.manylinux2014_x86_64.musllinux_1_1_x86_64",
"armv7l-linux": "linux_armv7l.manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l",
"armv6l-linux": "linux_armv6l",
"aarch64-linux": "manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64",
"i686-linux": "manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686",
"win64": "win_amd64",
"win32": "win32",
# macOS versions for platform compatibility tags are taken from https://doc.rust-lang.org/rustc/platform-support.html
build_wheel(
version,
"dist/deltachat-rpc-server-x86_64-macos",
"py3-none-macosx_10_7_x86_64",
)
build_wheel(
version,
"dist/deltachat-rpc-server-aarch64-macos",
"py3-none-macosx_11_0_arm64",
)
"x86_64-darwin": "macosx_10_7_x86_64",
"aarch64-darwin": "macosx_11_0_arm64",
}
build_wheel(
version, "dist/deltachat-rpc-server-win32.exe", "py3-none-win32", windows=True
)
build_wheel(
version,
"dist/deltachat-rpc-server-win64.exe",
"py3-none-win_amd64",
windows=True,
)
def main():
with Path("Cargo.toml").open("rb") as fp:
cargo_manifest = tomllib.load(fp)
version = cargo_manifest["package"]["version"]
if sys.argv[1] == "source":
filename = f"deltachat-rpc-server-{version}.tar.gz"
build_source_package(version, filename)
else:
arch = sys.argv[1]
executable = sys.argv[2]
tags = arch2tags[arch]
if arch in ["win32", "win64"]:
build_wheel(
version,
executable,
f"py3-none-{tags}",
windows=True,
)
else:
build_wheel(version, executable, f"py3-none-{tags}")
main()

View File

@@ -1,57 +0,0 @@
#!/usr/bin/env python
# /// pyproject
# [run]
# dependencies = [
# "ziglang==0.11.0"
# ]
# ///
import os
import subprocess
import sys
def flag_filter(flag: str) -> bool:
# Workaround for <https://github.com/sfackler/rust-openssl/issues/2043>.
if flag == "-latomic":
return False
if flag == "-lc":
return False
if flag == "-Wl,-melf_i386":
return False
if "self-contained" in flag and "crt" in flag:
return False
return True
def main():
args = [flag for flag in sys.argv[1:] if flag_filter(flag)]
zig_target = os.environ["ZIG_TARGET"]
zig_cpu = os.environ.get("ZIG_CPU")
if zig_cpu:
zig_cpu_args = ["-mcpu=" + zig_cpu]
args = [x for x in args if not x.startswith("-march")]
else:
zig_cpu_args = []
# Disable atomics and use locks instead in OpenSSL.
# Zig toolchains do not provide atomics.
# This is a workaround for <https://github.com/deltachat/deltachat-core-rust/issues/4799>
args += ["-DBROKEN_CLANG_ATOMICS"]
subprocess.run(
[
sys.executable,
"-m",
"ziglang",
"cc",
"-target",
zig_target,
*zig_cpu_args,
*args,
],
check=True,
)
main()

View File

@@ -1,50 +0,0 @@
#!/bin/sh
#
# Build statically linked deltachat-rpc-server using zig.
set -x
set -e
unset RUSTFLAGS
# Pin Rust version to avoid uncontrolled changes in the compiler and linker flags.
export RUSTUP_TOOLCHAIN=1.72.0
rustup target add i686-unknown-linux-musl
CC="$PWD/scripts/zig-cc" \
TARGET_CC="$PWD/scripts/zig-cc" \
CARGO_TARGET_I686_UNKNOWN_LINUX_MUSL_LINKER="$PWD/scripts/zig-cc" \
LD="$PWD/scripts/zig-cc" \
ZIG_TARGET="x86-linux-musl" \
cargo build --release --target i686-unknown-linux-musl -p deltachat-rpc-server --features vendored
rustup target add armv7-unknown-linux-musleabihf
CC="$PWD/scripts/zig-cc" \
TARGET_CC="$PWD/scripts/zig-cc" \
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABIHF_LINKER="$PWD/scripts/zig-cc" \
LD="$PWD/scripts/zig-cc" \
ZIG_TARGET="arm-linux-musleabihf" \
ZIG_CPU="generic+v7a+vfp3-d32+thumb2-neon" \
cargo build --release --target armv7-unknown-linux-musleabihf -p deltachat-rpc-server --features vendored
rustup target add x86_64-unknown-linux-musl
CC="$PWD/scripts/zig-cc" \
TARGET_CC="$PWD/scripts/zig-cc" \
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER="$PWD/scripts/zig-cc" \
LD="$PWD/scripts/zig-cc" \
ZIG_TARGET="x86_64-linux-musl" \
cargo build --release --target x86_64-unknown-linux-musl -p deltachat-rpc-server --features vendored
rustup target add aarch64-unknown-linux-musl
CC="$PWD/scripts/zig-cc" \
TARGET_CC="$PWD/scripts/zig-cc" \
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER="$PWD/scripts/zig-cc" \
LD="$PWD/scripts/zig-cc" \
ZIG_TARGET="aarch64-linux-musl" \
cargo build --release --target aarch64-unknown-linux-musl -p deltachat-rpc-server --features vendored
mkdir -p dist
cp target/x86_64-unknown-linux-musl/release/deltachat-rpc-server dist/deltachat-rpc-server-x86_64-linux
cp target/i686-unknown-linux-musl/release/deltachat-rpc-server dist/deltachat-rpc-server-i686-linux
cp target/aarch64-unknown-linux-musl/release/deltachat-rpc-server dist/deltachat-rpc-server-aarch64-linux
cp target/armv7-unknown-linux-musleabihf/release/deltachat-rpc-server dist/deltachat-rpc-server-armv7-linux

View File

@@ -119,8 +119,9 @@ All group members form the member list.
To allow different groups with the same members,
groups are identified by a group-id.
The group-id MUST be created only from the characters
`0`-`9`, `A`-`Z`, `a`-`z` `_` and `-`
and MUST have a length of at least 11 characters.
`0`-`9`, `A`-`Z`, `a`-`z` `_` and `-`,
MUST have a length of at least 11 characters
and no more than 32 characters.
Groups MUST have a group-name.
The group-name is any non-zero-length UTF-8 string.

View File

@@ -5,16 +5,21 @@ use std::future::Future;
use std::path::{Path, PathBuf};
use anyhow::{ensure, Context as _, Result};
use futures::future::join_all;
use serde::{Deserialize, Serialize};
use tokio::fs;
use tokio::io::AsyncWriteExt;
use tokio::sync::oneshot;
use tokio::task::JoinHandle;
use tokio::time::{sleep, Duration};
use uuid::Uuid;
use crate::context::Context;
#[cfg(not(target_os = "ios"))]
use tokio::sync::oneshot;
#[cfg(not(target_os = "ios"))]
use tokio::time::{sleep, Duration};
use crate::context::{Context, ContextBuilder};
use crate::events::{Event, EventEmitter, EventType, Events};
use crate::push::PushSubscriber;
use crate::stock_str::StockStrings;
/// Account manager, that can handle multiple accounts in a single place.
@@ -33,6 +38,9 @@ pub struct Accounts {
/// This way changing a translation for one context automatically
/// changes it for all other contexts.
pub(crate) stockstrings: StockStrings,
/// Push notification subscriber shared between accounts.
push_subscriber: PushSubscriber,
}
impl Accounts {
@@ -69,8 +77,9 @@ impl Accounts {
.context("failed to load accounts config")?;
let events = Events::new();
let stockstrings = StockStrings::new();
let push_subscriber = PushSubscriber::new();
let accounts = config
.load_accounts(&events, &stockstrings, &dir)
.load_accounts(&events, &stockstrings, push_subscriber.clone(), &dir)
.await
.context("failed to load accounts")?;
@@ -80,6 +89,7 @@ impl Accounts {
accounts,
events,
stockstrings,
push_subscriber,
})
}
@@ -116,13 +126,17 @@ impl Accounts {
let account_config = self.config.new_account().await?;
let dbfile = account_config.dbfile(&self.dir);
let ctx = Context::new(
&dbfile,
account_config.id,
self.events.clone(),
self.stockstrings.clone(),
)
.await?;
let ctx = ContextBuilder::new(dbfile)
.with_id(account_config.id)
.with_events(self.events.clone())
.with_stock_strings(self.stockstrings.clone())
.with_push_subscriber(self.push_subscriber.clone())
.build()
.await?;
// Try to open without a passphrase,
// but do not return an error if account is passphare-protected.
ctx.open("".to_string()).await?;
self.accounts.insert(account_config.id, ctx);
Ok(account_config.id)
@@ -131,14 +145,15 @@ impl Accounts {
/// Adds a new closed account.
pub async fn add_closed_account(&mut self) -> Result<u32> {
let account_config = self.config.new_account().await?;
let dbfile = account_config.dbfile(&self.dir);
let ctx = Context::new_closed(
&account_config.dbfile(&self.dir),
account_config.id,
self.events.clone(),
self.stockstrings.clone(),
)
.await?;
let ctx = ContextBuilder::new(dbfile)
.with_id(account_config.id)
.with_events(self.events.clone())
.with_stock_strings(self.stockstrings.clone())
.with_push_subscriber(self.push_subscriber.clone())
.build()
.await?;
self.accounts.insert(account_config.id, ctx);
Ok(account_config.id)
@@ -238,25 +253,6 @@ impl Accounts {
self.accounts.keys().copied().collect()
}
/// This is meant especially for iOS, because iOS needs to tell the system when its background work is done.
///
/// Returns whether all accounts finished their background work.
/// DC_EVENT_CONNECTIVITY_CHANGED will be sent when this turns to true.
///
/// iOS can:
/// - call dc_start_io() (in case IO was not running)
/// - call dc_maybe_network()
/// - while dc_accounts_all_work_done() returns false:
/// - Wait for DC_EVENT_CONNECTIVITY_CHANGED
pub async fn all_work_done(&self) -> bool {
for account in self.accounts.values() {
if !account.all_work_done().await {
return false;
}
}
true
}
/// Starts background tasks such as IMAP and SMTP loops for all accounts.
pub async fn start_io(&mut self) {
for account in self.accounts.values_mut() {
@@ -288,6 +284,42 @@ impl Accounts {
}
}
/// Performs a background fetch for all accounts in parallel.
///
/// This is an auxiliary function and not part of public API.
/// Use [Accounts::background_fetch] instead.
async fn background_fetch_without_timeout(&self) {
async fn background_fetch_and_log_error(account: Context) {
if let Err(error) = account.background_fetch().await {
warn!(account, "{error:#}");
}
}
join_all(
self.accounts
.values()
.cloned()
.map(background_fetch_and_log_error),
)
.await;
}
/// Performs a background fetch for all accounts in parallel with a timeout.
///
/// The `AccountsBackgroundFetchDone` event is emitted at the end,
/// process all events until you get this one and you can safely return to the background
/// without forgetting to create notifications caused by timing race conditions.
pub async fn background_fetch(&self, timeout: std::time::Duration) {
if let Err(_err) =
tokio::time::timeout(timeout, self.background_fetch_without_timeout()).await
{
self.emit_event(EventType::Warning(
"Background fetch timed out.".to_string(),
));
}
self.emit_event(EventType::AccountsBackgroundFetchDone);
}
/// Emits a single event.
pub fn emit_event(&self, event: EventType) {
self.events.emit(Event { id: 0, typ: event })
@@ -297,12 +329,19 @@ impl Accounts {
pub fn get_event_emitter(&self) -> EventEmitter {
self.events.get_emitter()
}
/// Sets notification token for Apple Push Notification service.
pub async fn set_push_device_token(&mut self, token: &str) -> Result<()> {
self.push_subscriber.set_device_token(token).await;
Ok(())
}
}
/// Configuration file name.
const CONFIG_NAME: &str = "accounts.toml";
/// Lockfile name.
#[cfg(not(target_os = "ios"))]
const LOCKFILE_NAME: &str = "accounts.lock";
/// Database file name.
@@ -338,22 +377,16 @@ impl Drop for Config {
}
impl Config {
/// Creates a new Config for `file`, but doesn't open/sync it.
async fn new_nosync(file: PathBuf, lock: bool) -> Result<Self> {
let dir = file.parent().context("Cannot get config file directory")?;
let inner = InnerConfig {
accounts: Vec::new(),
selected_account: 0,
next_id: 1,
};
if !lock {
let cfg = Self {
file,
inner,
lock_task: None,
};
return Ok(cfg);
}
#[cfg(target_os = "ios")]
async fn create_lock_task(_dir: PathBuf) -> Result<Option<JoinHandle<anyhow::Result<()>>>> {
// Do not lock accounts.toml on iOS.
// This results in 0xdead10cc crashes on suspend.
// iOS itself ensures that multiple instances of Delta Chat are not running.
Ok(None)
}
#[cfg(not(target_os = "ios"))]
async fn create_lock_task(dir: PathBuf) -> Result<Option<JoinHandle<anyhow::Result<()>>>> {
let lockfile = dir.join(LOCKFILE_NAME);
let mut lock = fd_lock::RwLock::new(fs::File::create(lockfile).await?);
let (locked_tx, locked_rx) = oneshot::channel();
@@ -384,12 +417,32 @@ impl Config {
rx.await?;
Ok(())
});
locked_rx.await?;
Ok(Some(lock_task))
}
/// Creates a new Config for `file`, but doesn't open/sync it.
async fn new_nosync(file: PathBuf, lock: bool) -> Result<Self> {
let dir = file.parent().context("Cannot get config file directory")?;
let inner = InnerConfig {
accounts: Vec::new(),
selected_account: 0,
next_id: 1,
};
if !lock {
let cfg = Self {
file,
inner,
lock_task: None,
};
return Ok(cfg);
}
let lock_task = Self::create_lock_task(dir.to_path_buf()).await?;
let cfg = Self {
file,
inner,
lock_task: Some(lock_task),
lock_task,
};
locked_rx.await?;
Ok(cfg)
}
@@ -406,11 +459,13 @@ impl Config {
/// Takes a mutable reference because the saved file is a part of the `Config` state. This
/// protects from parallel calls resulting to a wrong file contents.
async fn sync(&mut self) -> Result<()> {
#[cfg(not(target_os = "ios"))]
ensure!(!self
.lock_task
.as_ref()
.context("Config is read-only")?
.is_finished());
let tmp_path = self.file.with_extension("toml.tmp");
let mut file = fs::File::create(&tmp_path)
.await
@@ -465,24 +520,24 @@ impl Config {
&self,
events: &Events,
stockstrings: &StockStrings,
push_subscriber: PushSubscriber,
dir: &Path,
) -> Result<BTreeMap<u32, Context>> {
let mut accounts = BTreeMap::new();
for account_config in &self.inner.accounts {
let ctx = Context::new(
&account_config.dbfile(dir),
account_config.id,
events.clone(),
stockstrings.clone(),
)
.await
.with_context(|| {
format!(
"failed to create context from file {:?}",
account_config.dbfile(dir)
)
})?;
let dbfile = account_config.dbfile(dir);
let ctx = ContextBuilder::new(dbfile.clone())
.with_id(account_config.id)
.with_events(events.clone())
.with_stock_strings(stockstrings.clone())
.with_push_subscriber(push_subscriber.clone())
.build()
.await
.with_context(|| format!("failed to create context from file {:?}", &dbfile))?;
// Try to open without a passphrase,
// but do not return an error if account is passphare-protected.
ctx.open("".to_string()).await?;
accounts.insert(account_config.id, ctx);
}
@@ -526,8 +581,12 @@ impl Config {
}
if self.inner.selected_account == id {
// reset selected account
self.inner.selected_account =
self.inner.accounts.get(0).map(|e| e.id).unwrap_or_default();
self.inner.selected_account = self
.inner
.accounts
.first()
.map(|e| e.id)
.unwrap_or_default();
}
}

View File

@@ -216,7 +216,7 @@ async fn update_authservid_candidates(
if old_ids != new_ids {
let new_config = new_ids.into_iter().collect::<Vec<_>>().join(" ");
context
.set_config(Config::AuthservIdCandidates, Some(&new_config))
.set_config_internal(Config::AuthservIdCandidates, Some(&new_config))
.await?;
// Updating the authservid candidates may mean that we now consider
// emails as "failed" which "passed" previously, so we need to

View File

@@ -5,11 +5,14 @@ use std::ffi::OsStr;
use std::fmt;
use std::io::Cursor;
use std::iter::FusedIterator;
use std::mem;
use std::path::{Path, PathBuf};
use anyhow::{format_err, Context as _, Result};
use base64::Engine as _;
use futures::StreamExt;
use image::{DynamicImage, GenericImageView, ImageFormat, ImageOutputFormat};
use image::codecs::jpeg::JpegEncoder;
use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat, Pixel, Rgba};
use num_traits::FromPrimitive;
use tokio::io::AsyncWriteExt;
use tokio::{fs, io};
@@ -33,6 +36,12 @@ pub struct BlobObject<'a> {
name: String,
}
#[derive(Debug, Clone)]
enum ImageOutputFormat {
Png,
Jpeg { quality: u8 },
}
impl<'a> BlobObject<'a> {
/// Creates a new blob object with a unique name.
///
@@ -312,6 +321,30 @@ impl<'a> BlobObject<'a> {
true
}
/// Returns path to the stored Base64-decoded blob.
///
/// If `data` represents an image of known format, this adds the corresponding extension to
/// `suggested_file_stem`.
pub(crate) async fn store_from_base64(
context: &Context,
data: &str,
suggested_file_stem: &str,
) -> Result<String> {
let buf = base64::engine::general_purpose::STANDARD.decode(data)?;
let ext = if let Ok(format) = image::guess_format(&buf) {
if let Some(ext) = format.extensions_str().first() {
format!(".{ext}")
} else {
String::new()
}
} else {
String::new()
};
let blob =
BlobObject::create(context, &format!("{suggested_file_stem}{ext}"), &buf).await?;
Ok(blob.as_name().to_string())
}
pub async fn recode_to_avatar_size(&mut self, context: &Context) -> Result<()> {
let blob_abs = self.to_abs_path();
@@ -388,6 +421,8 @@ impl<'a> BlobObject<'a> {
max_bytes: usize,
strict_limits: bool,
) -> Result<Option<String>> {
// Add white background only to avatars to spare the CPU.
let mut add_white_bg = img_wh <= constants::BALANCED_AVATAR_SIZE;
let mut no_exif = false;
let no_exif_ref = &mut no_exif;
let res = tokio::task::block_in_place(move || {
@@ -421,13 +456,19 @@ impl<'a> BlobObject<'a> {
let exceeds_wh = img.width() > img_wh || img.height() > img_wh;
let exceeds_max_bytes = nr_bytes > max_bytes as u64;
let jpeg_quality = 75;
let fmt = ImageFormat::from_path(&blob_abs);
let ofmt = match fmt {
Ok(ImageFormat::Png) if !exceeds_max_bytes => ImageOutputFormat::Png,
_ => {
let jpeg_quality = 75;
ImageOutputFormat::Jpeg(jpeg_quality)
Ok(ImageFormat::Jpeg) => {
add_white_bg = false;
ImageOutputFormat::Jpeg {
quality: jpeg_quality,
}
}
_ => ImageOutputFormat::Jpeg {
quality: jpeg_quality,
},
};
// We need to rewrite images with Exif to remove metadata such as location,
// camera model, etc.
@@ -438,14 +479,18 @@ impl<'a> BlobObject<'a> {
let do_scale = exceeds_max_bytes
|| strict_limits
&& (exceeds_wh
|| exif.is_some()
&& encoded_img_exceeds_bytes(
|| exif.is_some() && {
if mem::take(&mut add_white_bg) {
self::add_white_bg(&mut img);
}
encoded_img_exceeds_bytes(
context,
&img,
ofmt.clone(),
max_bytes,
&mut encoded,
)?);
)?
});
if do_scale {
if !exceeds_wh {
@@ -458,6 +503,9 @@ impl<'a> BlobObject<'a> {
}
loop {
if mem::take(&mut add_white_bg) {
self::add_white_bg(&mut img);
}
let new_img = img.thumbnail(img_wh, img_wh);
if encoded_img_exceeds_bytes(
@@ -491,7 +539,7 @@ impl<'a> BlobObject<'a> {
if do_scale || exif.is_some() {
// The file format is JPEG/PNG now, we may have to change the file extension
if !matches!(fmt, Ok(ImageFormat::Jpeg))
&& matches!(ofmt, ImageOutputFormat::Jpeg(_))
&& matches!(ofmt, ImageOutputFormat::Jpeg { .. })
{
blob_abs = blob_abs.with_extension("jpg");
let file_name = blob_abs.file_name().context("No image file name (???)")?;
@@ -500,6 +548,9 @@ impl<'a> BlobObject<'a> {
}
if encoded.is_empty() {
if mem::take(&mut add_white_bg) {
self::add_white_bg(&mut img);
}
encode_img(&img, ofmt, &mut encoded)?;
}
@@ -643,7 +694,13 @@ fn encode_img(
) -> anyhow::Result<()> {
encoded.clear();
let mut buf = Cursor::new(encoded);
img.write_to(&mut buf, fmt)?;
match fmt {
ImageOutputFormat::Png => img.write_to(&mut buf, ImageFormat::Png)?,
ImageOutputFormat::Jpeg { quality } => {
let encoder = JpegEncoder::new_with_quality(&mut buf, quality);
img.write_with_encoder(encoder)?;
}
}
Ok(())
}
@@ -669,11 +726,21 @@ fn encoded_img_exceeds_bytes(
Ok(false)
}
/// Removes transparency from an image using a white background.
fn add_white_bg(img: &mut DynamicImage) {
for y in 0..img.height() {
for x in 0..img.width() {
let mut p = Rgba([255u8, 255, 255, 255]);
p.blend(&img.get_pixel(x, y));
img.put_pixel(x, y, p);
}
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use fs::File;
use image::{GenericImageView, Pixel};
use image::Pixel;
use super::*;
use crate::chat::{self, create_group_chat, ProtectionStatus};
@@ -885,6 +952,40 @@ mod tests {
assert!(!stem.contains('?'));
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_white_bg() {
let t = TestContext::new().await;
let bytes0 = include_bytes!("../test-data/image/logo.png").as_slice();
let bytes1 = include_bytes!("../test-data/image/avatar900x900.png").as_slice();
for (bytes, color) in [
(bytes0, [255u8, 255, 255, 255]),
(bytes1, [253u8, 198, 0, 255]),
] {
let avatar_src = t.dir.path().join("avatar.png");
fs::write(&avatar_src, bytes).await.unwrap();
let mut blob = BlobObject::new_from_path(&t, &avatar_src).await.unwrap();
let img_wh = 128;
let maybe_sticker = &mut false;
let strict_limits = true;
blob.recode_to_size(
&t,
blob.to_abs_path(),
maybe_sticker,
img_wh,
20_000,
strict_limits,
)
.unwrap();
tokio::task::block_in_place(move || {
let img = image::open(blob.to_abs_path()).unwrap();
assert!(img.width() == img_wh);
assert!(img.height() == img_wh);
assert_eq!(img.get_pixel(0, 0), Rgba(color));
});
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_selfavatar_outside_blobdir() {
let t = TestContext::new().await;

File diff suppressed because it is too large Load Diff

View File

@@ -416,7 +416,7 @@ impl Chatlist {
if chat.id.is_archived_link() {
Ok(Default::default())
} else if let Some(lastmsg) = lastmsg.filter(|msg| msg.from_id != ContactId::UNDEFINED) {
Ok(Summary::new(context, &lastmsg, chat, lastcontact.as_ref()).await)
Summary::new(context, &lastmsg, chat, lastcontact.as_ref()).await
} else {
Ok(Summary {
text: stock_str::no_messages(context).await,

View File

@@ -5,17 +5,22 @@ use std::path::Path;
use std::str::FromStr;
use anyhow::{ensure, Context as _, Result};
use base64::Engine as _;
use serde::{Deserialize, Serialize};
use strum::{EnumProperty, IntoEnumIterator};
use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
use tokio::fs;
use crate::blob::BlobObject;
use crate::constants::DC_VERSION_STR;
use crate::constants::{self, DC_VERSION_STR};
use crate::contact::addr_cmp;
use crate::context::Context;
use crate::events::EventType;
use crate::log::LogExt;
use crate::mimefactory::RECOMMENDED_FILE_SIZE;
use crate::provider::{get_provider_by_id, Provider};
use crate::tools::{get_abs_path, improve_single_line_input, EmailAddress};
use crate::sync::{self, Sync::*, SyncData};
use crate::tools::{get_abs_path, improve_single_line_input};
/// The available configuration keys.
#[derive(
@@ -31,6 +36,8 @@ use crate::tools::{get_abs_path, improve_single_line_input, EmailAddress};
EnumProperty,
PartialOrd,
Ord,
Serialize,
Deserialize,
)]
#[strum(serialize_all = "snake_case")]
pub enum Config {
@@ -286,6 +293,9 @@ pub enum Config {
/// Timestamp of the last time housekeeping was run
LastHousekeeping,
/// Timestamp of the last `CantDecryptOutgoingMsgs` notification.
LastCantDecryptOutgoingMsgs,
/// To how many seconds to debounce scan_all_folders. Used mainly in tests, to disable debouncing completely.
#[strum(props(default = "60"))]
ScanAllFoldersDebounceSecs,
@@ -338,6 +348,48 @@ pub enum Config {
/// until `chat_id.accept()` is called.
#[strum(props(default = "0"))]
VerifiedOneOnOneChats,
/// Row ID of the key in the `keypairs` table
/// used for signatures, encryption to self and included in `Autocrypt` header.
KeyId,
/// This key is sent to the self_reporting bot so that the bot can recognize the user
/// without storing the email address
SelfReportingId,
}
impl Config {
/// Whether the config option is synced across devices.
///
/// This must be checked on both sides so that if there are different client versions, the
/// synchronisation of a particular option is either done or not done in both directions.
/// Moreover, receivers of a config value need to check if a key can be synced because if it is
/// a file path, it could otherwise lead to exfiltration of files from a receiver's
/// device if we assume an attacker to have control of a device in a multi-device setting or if
/// multiple users are sharing an account. Another example is `Self::SyncMsgs` itself which
/// mustn't be controlled by other devices.
pub(crate) fn is_synced(&self) -> bool {
// We don't restart IO from the synchronisation code, so this is to be on the safe side.
if self.needs_io_restart() {
return false;
}
matches!(
self,
Self::Displayname
| Self::MdnsEnabled
| Self::ShowEmails
| Self::Selfavatar
| Self::Selfstatus,
)
}
/// Whether the config option needs an IO scheduler restart to take effect.
pub(crate) fn needs_io_restart(&self) -> bool {
matches!(
self,
Config::MvboxMove | Config::OnlyFetchMvbox | Config::SentboxWatch
)
}
}
impl Context {
@@ -423,6 +475,15 @@ impl Context {
|| self.get_config_bool(Config::OnlyFetchMvbox).await?)
}
/// Returns true if sentbox ("Sent" folder) should be watched.
pub(crate) async fn should_watch_sentbox(&self) -> Result<bool> {
Ok(self.get_config_bool(Config::SentboxWatch).await?
&& self
.get_config(Config::ConfiguredSentboxFolder)
.await?
.is_some())
}
/// Gets configured "delete_server_after" value.
///
/// `None` means never delete the message, `Some(0)` means delete
@@ -457,40 +518,25 @@ impl Context {
}
}
/// Set the given config key.
/// If `None` is passed as a value the value is cleared and set to the default if there is one.
pub async fn set_config(&self, key: Config, value: Option<&str>) -> Result<()> {
match key {
/// Executes [`SyncData::Config`] item sent by other device.
pub(crate) async fn sync_config(&self, key: &Config, value: &str) -> Result<()> {
let config_value;
let value = match key {
Config::Selfavatar if value.is_empty() => None,
Config::Selfavatar => {
self.sql
.execute("UPDATE contacts SET selfavatar_sent=0;", ())
.await?;
match value {
Some(value) => {
let mut blob = BlobObject::new_from_path(self, value.as_ref()).await?;
blob.recode_to_avatar_size(self).await?;
self.sql
.set_raw_config(key.as_ref(), Some(blob.as_name()))
.await?;
}
None => {
self.sql.set_raw_config(key.as_ref(), None).await?;
}
}
self.emit_event(EventType::SelfavatarChanged);
}
Config::DeleteDeviceAfter => {
let ret = self.sql.set_raw_config(key.as_ref(), value).await;
// Interrupt ephemeral loop to delete old messages immediately.
self.scheduler.interrupt_ephemeral_task().await;
ret?
}
Config::Displayname => {
let value = value.map(improve_single_line_input);
self.sql
.set_raw_config(key.as_ref(), value.as_deref())
.await?;
config_value = BlobObject::store_from_base64(self, value, "avatar").await?;
Some(config_value.as_str())
}
_ => Some(value),
};
match key.is_synced() {
true => self.set_config_ex(Nosync, *key, value).await,
false => Ok(()),
}
}
fn check_config(key: Config, value: Option<&str>) -> Result<()> {
match key {
Config::Socks5Enabled
| Config::BccSelf
| Config::E2eeEnabled
@@ -511,6 +557,82 @@ impl Context {
matches!(value, None | Some("0") | Some("1")),
"Boolean value must be either 0 or 1"
);
}
_ => (),
}
Ok(())
}
/// Set the given config key and make it effective.
/// This may restart the IO scheduler. If `None` is passed as a value the value is cleared and
/// set to the default if there is one.
pub async fn set_config(&self, key: Config, value: Option<&str>) -> Result<()> {
Self::check_config(key, value)?;
let _pause = match key.needs_io_restart() {
true => self.scheduler.pause(self.clone()).await?,
_ => Default::default(),
};
self.set_config_internal(key, value).await?;
if key == Config::SentboxWatch {
self.last_full_folder_scan.lock().await.take();
}
Ok(())
}
pub(crate) async fn set_config_internal(&self, key: Config, value: Option<&str>) -> Result<()> {
self.set_config_ex(Sync, key, value).await
}
pub(crate) async fn set_config_ex(
&self,
sync: sync::Sync,
key: Config,
mut value: Option<&str>,
) -> Result<()> {
Self::check_config(key, value)?;
let sync = sync == Sync && key.is_synced();
let better_value;
match key {
Config::Selfavatar => {
self.sql
.execute("UPDATE contacts SET selfavatar_sent=0;", ())
.await?;
match value {
Some(path) => {
let mut blob = BlobObject::new_from_path(self, path.as_ref()).await?;
blob.recode_to_avatar_size(self).await?;
self.sql
.set_raw_config(key.as_ref(), Some(blob.as_name()))
.await?;
if sync {
let buf = fs::read(blob.to_abs_path()).await?;
better_value = base64::engine::general_purpose::STANDARD.encode(buf);
value = Some(&better_value);
}
}
None => {
self.sql.set_raw_config(key.as_ref(), None).await?;
if sync {
better_value = String::new();
value = Some(&better_value);
}
}
}
self.emit_event(EventType::SelfavatarChanged);
}
Config::DeleteDeviceAfter => {
let ret = self.sql.set_raw_config(key.as_ref(), value).await;
// Interrupt ephemeral loop to delete old messages immediately.
self.scheduler.interrupt_ephemeral_task().await;
ret?
}
Config::Displayname => {
if let Some(v) = value {
better_value = improve_single_line_input(v);
value = Some(&better_value);
}
self.sql.set_raw_config(key.as_ref(), value).await?;
}
Config::Addr => {
@@ -518,10 +640,35 @@ impl Context {
.set_raw_config(key.as_ref(), value.map(|s| s.to_lowercase()).as_deref())
.await?;
}
Config::MvboxMove => {
self.sql.set_raw_config(key.as_ref(), value).await?;
self.sql
.set_raw_config(constants::DC_FOLDERS_CONFIGURED_KEY, None)
.await?;
}
_ => {
self.sql.set_raw_config(key.as_ref(), value).await?;
}
}
if key.is_synced() {
self.emit_event(EventType::ConfigSynced { key });
}
if !sync {
return Ok(());
}
let Some(val) = value else {
return Ok(());
};
let val = val.to_string();
if self
.add_sync_item(SyncData::Config { key, val })
.await
.log_err(self)
.is_err()
{
return Ok(());
}
self.send_sync_msg().await.log_err(self).ok();
Ok(())
}
@@ -533,8 +680,7 @@ impl Context {
/// Set the given config to a boolean value.
pub async fn set_config_bool(&self, key: Config, value: bool) -> Result<()> {
self.set_config(key, if value { Some("1") } else { Some("0") })
.await?;
self.set_config(key, from_bool(value)).await?;
Ok(())
}
@@ -554,6 +700,11 @@ impl Context {
}
}
/// Returns a value for use in `Context::set_config_*()` for the given `bool`.
pub(crate) fn from_bool(val: bool) -> Option<&'static str> {
Some(if val { "1" } else { "0" })
}
// Separate impl block for self address handling
impl Context {
/// Determine whether the specified addr maps to the/a self addr.
@@ -576,32 +727,19 @@ impl Context {
///
/// This should only be used by test code and during configure.
pub(crate) async fn set_primary_self_addr(&self, primary_new: &str) -> Result<()> {
let old_addr = self.get_config(Config::ConfiguredAddr).await?;
// add old primary address (if exists) to secondary addresses
let mut secondary_addrs = self.get_all_self_addrs().await?;
// never store a primary address also as a secondary
secondary_addrs.retain(|a| !addr_cmp(a, primary_new));
self.set_config(
self.set_config_internal(
Config::SecondaryAddrs,
Some(secondary_addrs.join(" ").as_str()),
)
.await?;
self.set_config(Config::ConfiguredAddr, Some(primary_new))
self.set_config_internal(Config::ConfiguredAddr, Some(primary_new))
.await?;
if let Some(old_addr) = old_addr {
let old_addr = EmailAddress::new(&old_addr)?;
let old_keypair = crate::key::load_keypair(self, &old_addr).await?;
if let Some(mut old_keypair) = old_keypair {
old_keypair.addr = EmailAddress::new(primary_new)?;
crate::key::store_self_keypair(self, &old_keypair, crate::key::KeyPairUse::Default)
.await?;
}
}
Ok(())
}
@@ -647,13 +785,10 @@ fn get_config_keys_string() -> String {
#[cfg(test)]
mod tests {
use std::string::ToString;
use num_traits::FromPrimitive;
use super::*;
use crate::constants;
use crate::test_utils::TestContext;
use crate::test_utils::{sync, TestContext, TestContextManager};
#[test]
fn test_to_string() {
@@ -797,4 +932,201 @@ mod tests {
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_sync() -> Result<()> {
let alice0 = TestContext::new_alice().await;
let alice1 = TestContext::new_alice().await;
for a in [&alice0, &alice1] {
a.set_config_bool(Config::SyncMsgs, true).await?;
}
let mdns_enabled = alice0.get_config_bool(Config::MdnsEnabled).await?;
// Alice1 has a different config value.
alice1
.set_config_bool(Config::MdnsEnabled, !mdns_enabled)
.await?;
// This changes nothing, but still sends a sync message.
alice0
.set_config_bool(Config::MdnsEnabled, mdns_enabled)
.await?;
sync(&alice0, &alice1).await;
assert_eq!(
alice1.get_config_bool(Config::MdnsEnabled).await?,
mdns_enabled
);
// Reset to default. Test that it's not synced because defaults may differ across client
// versions.
alice0.set_config(Config::MdnsEnabled, None).await?;
assert_eq!(alice0.get_config_bool(Config::MdnsEnabled).await?, true);
alice0.set_config_bool(Config::MdnsEnabled, false).await?;
sync(&alice0, &alice1).await;
assert_eq!(alice1.get_config_bool(Config::MdnsEnabled).await?, false);
let show_emails = alice0.get_config_bool(Config::ShowEmails).await?;
alice0
.set_config_bool(Config::ShowEmails, !show_emails)
.await?;
sync(&alice0, &alice1).await;
assert_eq!(
alice1.get_config_bool(Config::ShowEmails).await?,
!show_emails
);
// `Config::SyncMsgs` mustn't be synced.
alice0.set_config_bool(Config::SyncMsgs, false).await?;
alice0.set_config_bool(Config::SyncMsgs, true).await?;
alice0.set_config_bool(Config::MdnsEnabled, true).await?;
sync(&alice0, &alice1).await;
assert!(alice1.get_config_bool(Config::MdnsEnabled).await?);
// Usual sync scenario.
async fn test_config_str(
alice0: &TestContext,
alice1: &TestContext,
key: Config,
val: &str,
) -> Result<()> {
alice0.set_config(key, Some(val)).await?;
sync(alice0, alice1).await;
assert_eq!(alice1.get_config(key).await?, Some(val.to_string()));
Ok(())
}
test_config_str(&alice0, &alice1, Config::Displayname, "Alice Sync").await?;
test_config_str(&alice0, &alice1, Config::Selfstatus, "My status").await?;
assert!(alice0.get_config(Config::Selfavatar).await?.is_none());
let file = alice0.dir.path().join("avatar.png");
let bytes = include_bytes!("../test-data/image/avatar64x64.png");
tokio::fs::write(&file, bytes).await?;
alice0
.set_config(Config::Selfavatar, Some(file.to_str().unwrap()))
.await?;
sync(&alice0, &alice1).await;
assert!(alice1
.get_config(Config::Selfavatar)
.await?
.filter(|path| path.ends_with(".png"))
.is_some());
alice0.set_config(Config::Selfavatar, None).await?;
sync(&alice0, &alice1).await;
assert!(alice1.get_config(Config::Selfavatar).await?.is_none());
Ok(())
}
/// Sync message mustn't be sent if self-{status,avatar} is changed by a self-sent message.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_no_sync_on_self_sent_msg() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice0 = &tcm.alice().await;
let alice1 = &tcm.alice().await;
for a in [alice0, alice1] {
a.set_config_bool(Config::SyncMsgs, true).await?;
}
let status = "Synced via usual message";
alice0.set_config(Config::Selfstatus, Some(status)).await?;
alice0.pop_sent_msg().await; // Sync message
let status1 = "Synced via sync message";
alice1.set_config(Config::Selfstatus, Some(status1)).await?;
tcm.send_recv(alice0, alice1, "hi Alice!").await;
assert_eq!(
alice1.get_config(Config::Selfstatus).await?,
Some(status.to_string())
);
sync(alice1, alice0).await;
assert_eq!(
alice0.get_config(Config::Selfstatus).await?,
Some(status1.to_string())
);
// Need a chat with another contact to send self-avatar.
let bob = &tcm.bob().await;
let a0b_chat_id = tcm.send_recv_accept(bob, alice0, "hi").await.chat_id;
let file = alice0.dir.path().join("avatar.png");
let bytes = include_bytes!("../test-data/image/avatar64x64.png");
tokio::fs::write(&file, bytes).await?;
alice0
.set_config(Config::Selfavatar, Some(file.to_str().unwrap()))
.await?;
alice0.pop_sent_msg().await; // Sync message
let file = alice1.dir.path().join("avatar.jpg");
let bytes = include_bytes!("../test-data/image/avatar1000x1000.jpg");
tokio::fs::write(&file, bytes).await?;
alice1
.set_config(Config::Selfavatar, Some(file.to_str().unwrap()))
.await?;
let sent_msg = alice0.send_text(a0b_chat_id, "hi").await;
alice1.recv_msg(&sent_msg).await;
assert!(alice1
.get_config(Config::Selfavatar)
.await?
.filter(|path| path.ends_with(".png"))
.is_some());
sync(alice1, alice0).await;
assert!(alice0
.get_config(Config::Selfavatar)
.await?
.filter(|path| path.ends_with(".jpg"))
.is_some());
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_event_config_synced() -> Result<()> {
let alice0 = TestContext::new_alice().await;
let alice1 = TestContext::new_alice().await;
for a in [&alice0, &alice1] {
a.set_config_bool(Config::SyncMsgs, true).await?;
}
alice0
.set_config(Config::Displayname, Some("Alice Sync"))
.await?;
alice0
.evtracker
.get_matching(|e| {
matches!(
e,
EventType::ConfigSynced {
key: Config::Displayname
}
)
})
.await;
sync(&alice0, &alice1).await;
assert_eq!(
alice1.get_config(Config::Displayname).await?,
Some("Alice Sync".to_string())
);
alice1
.evtracker
.get_matching(|e| {
matches!(
e,
EventType::ConfigSynced {
key: Config::Displayname
}
)
})
.await;
alice0.set_config(Config::Displayname, None).await?;
alice0
.evtracker
.get_matching(|e| {
matches!(
e,
EventType::ConfigSynced {
key: Config::Displayname
}
)
})
.await;
Ok(())
}
}

View File

@@ -22,10 +22,10 @@ use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use server_params::{expand_param_vector, ServerParams};
use tokio::task;
use crate::config::Config;
use crate::config::{self, Config};
use crate::contact::addr_cmp;
use crate::context::Context;
use crate::imap::Imap;
use crate::imap::{session::Session as ImapSession, Imap};
use crate::log::LogExt;
use crate::login_param::{CertificateChecks, LoginParam, ServerLoginParam};
use crate::message::{Message, Viewtype};
@@ -34,6 +34,7 @@ use crate::provider::{Protocol, Socket, UsernamePattern};
use crate::smtp::Smtp;
use crate::socks::Socks5Config;
use crate::stock_str;
use crate::sync::Sync::*;
use crate::tools::{time, EmailAddress};
use crate::{chat, e2ee, provider};
@@ -111,12 +112,13 @@ impl Context {
let mut param = LoginParam::load_candidate_params(self).await?;
let old_addr = self.get_config(Config::ConfiguredAddr).await?;
let success = configure(self, &mut param).await;
self.set_config(Config::NotifyAboutWrongPw, None).await?;
self.set_config_internal(Config::NotifyAboutWrongPw, None)
.await?;
on_configure_completed(self, param, old_addr).await?;
success?;
self.set_config(Config::NotifyAboutWrongPw, Some("1"))
self.set_config_internal(Config::NotifyAboutWrongPw, Some("1"))
.await?;
Ok(())
}
@@ -132,7 +134,9 @@ async fn on_configure_completed(
for def in config_defaults {
if !context.config_exists(def.key).await? {
info!(context, "apply config_defaults {}={}", def.key, def.value);
context.set_config(def.key, Some(def.value)).await?;
context
.set_config_ex(Nosync, def.key, Some(def.value))
.await?;
} else {
info!(
context,
@@ -391,7 +395,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
// Configure IMAP
let mut imap: Option<Imap> = None;
let mut imap: Option<(Imap, ImapSession)> = None;
let imap_servers: Vec<&ServerParams> = servers
.iter()
.filter(|params| params.protocol == Protocol::Imap)
@@ -429,7 +433,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
600 + (800 - 600) * (1 + imap_server_index) / imap_servers_count
);
}
let mut imap = match imap {
let (mut imap, mut imap_session) = match imap {
Some(imap) => imap,
None => bail!(nicer_configuration_error(ctx, errors).await),
};
@@ -450,9 +454,11 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
let create_mvbox = ctx.should_watch_mvbox().await?;
imap.configure_folders(ctx, create_mvbox).await?;
imap.configure_folders(ctx, &mut imap_session, create_mvbox)
.await?;
imap.select_with_uidvalidity(ctx, "INBOX")
imap_session
.select_with_uidvalidity(ctx, "INBOX")
.await
.context("could not read INBOX status")?;
@@ -470,7 +476,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
// the trailing underscore is correct
param.save_as_configured_params(ctx).await?;
ctx.set_config(Config::ConfiguredTimestamp, Some(&time().to_string()))
ctx.set_config_internal(Config::ConfiguredTimestamp, Some(&time().to_string()))
.await?;
progress!(ctx, 920);
@@ -478,7 +484,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
e2ee::ensure_secret_key_exists(ctx).await?;
info!(ctx, "key generation completed");
ctx.set_config_bool(Config::FetchedExistingMsgs, false)
ctx.set_config_internal(Config::FetchedExistingMsgs, config::from_bool(false))
.await?;
ctx.scheduler.interrupt_inbox().await;
@@ -572,7 +578,7 @@ async fn try_imap_one_param(
socks5_config: &Option<Socks5Config>,
addr: &str,
provider_strict_tls: bool,
) -> Result<Imap, ConfigurationError> {
) -> Result<(Imap, ImapSession), ConfigurationError> {
let inf = format!(
"imap: {}@{}:{} security={} certificate_checks={} oauth2={} socks5_config={}",
param.user,
@@ -610,9 +616,9 @@ async fn try_imap_one_param(
msg: format!("{err:#}"),
})
}
Ok(()) => {
Ok(session) => {
info!(context, "success: {}", inf);
Ok(imap)
Ok((imap, session))
}
}
}

View File

@@ -1,6 +1,7 @@
//! # Thunderbird's Autoconfiguration implementation
//!
//! Documentation: <https://web.archive.org/web/20210624004729/https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration>
//! RFC draft: <https://www.ietf.org/archive/id/draft-bucksch-autoconfig-00.html>
//! Archived original documentation: <https://web.archive.org/web/20210624004729/https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration>
use std::io::BufRead;
use std::str::FromStr;

View File

@@ -137,20 +137,11 @@ impl ServerParams {
}
fn expand_strict_tls(self) -> Vec<ServerParams> {
if self.strict_tls.is_none() {
vec![
Self {
strict_tls: Some(true), // Strict.
..self.clone()
},
Self {
strict_tls: None, // Automatic.
..self
},
]
} else {
vec![self]
}
vec![Self {
// Strict if not set by the user or provider database.
strict_tls: Some(self.strict_tls.unwrap_or(true)),
..self
}]
}
}
@@ -162,31 +153,10 @@ pub(crate) fn expand_param_vector(
domain: &str,
) -> Vec<ServerParams> {
v.into_iter()
.map(|params| {
if params.socket == Socket::Plain {
ServerParams {
// Avoid expanding plaintext configuration into configuration with and without
// `strict_tls` if `strict_tls` is set to `None` as `strict_tls` is not used for
// plaintext connections. Always setting it to "enabled", just in case.
strict_tls: Some(true),
..params
}
} else {
params
}
})
// The order of expansion is important.
//
// Ports are expanded the last, so they are changed the first. Username is only changed if
// default value (address with domain) didn't work for all available hosts and ports.
//
// Strict TLS must be expanded first, so we try all configurations with strict TLS first
// and only then try again without strict TLS. Otherwise we may lock to wrong hostname
// without strict TLS when another hostname with strict TLS is available. For example, if
// both smtp.example.net and mail.example.net are running an SMTP server, but both use a
// certificate that is only valid for mail.example.net, we want to skip smtp.example.net
// and use mail.example.net with strict TLS instead of using smtp.example.net without
// strict TLS.
.flat_map(|params| params.expand_strict_tls().into_iter())
.flat_map(|params| params.expand_usernames(addr).into_iter())
.flat_map(|params| params.expand_hostnames(domain).into_iter())
@@ -257,22 +227,6 @@ mod tests {
username: "foobar".to_string(),
strict_tls: Some(true)
},
ServerParams {
protocol: Protocol::Smtp,
hostname: "example.net".to_string(),
port: 123,
socket: Socket::Ssl,
username: "foobar".to_string(),
strict_tls: None,
},
ServerParams {
protocol: Protocol::Smtp,
hostname: "example.net".to_string(),
port: 123,
socket: Socket::Starttls,
username: "foobar".to_string(),
strict_tls: None
}
],
);
@@ -284,7 +238,7 @@ mod tests {
port: 123,
socket: Socket::Plain,
username: "foobar".to_string(),
strict_tls: None,
strict_tls: Some(true),
}],
"foobar@example.net",
"example.net",

View File

@@ -206,9 +206,19 @@ pub(crate) const WORSE_AVATAR_SIZE: u32 = 128;
pub const BALANCED_IMAGE_SIZE: u32 = 1280;
pub const WORSE_IMAGE_SIZE: u32 = 640;
// Key for the folder configuration version (see below).
pub(crate) const DC_FOLDERS_CONFIGURED_KEY: &str = "folders_configured";
// this value can be increased if the folder configuration is changed and must be redone on next program start
pub(crate) const DC_FOLDERS_CONFIGURED_VERSION: i32 = 4;
// If more recipients are needed in SMTP's `RCPT TO:` header, the recipient list is split into
// chunks. This does not affect MIME's `To:` header. Can be overwritten by setting
// `max_smtp_rcpt_to` in the provider db.
pub(crate) const DEFAULT_MAX_SMTP_RCPT_TO: usize = 50;
/// How far the last quota check needs to be in the past to be checked by the background function (in seconds).
pub(crate) const DC_BACKGROUND_FETCH_QUOTA_CHECK_RATELIMIT: u64 = 12 * 60 * 60; // 12 hours
#[cfg(test)]
mod tests {
use num_traits::FromPrimitive;

View File

@@ -2,11 +2,10 @@
use std::cmp::Reverse;
use std::collections::BinaryHeap;
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
use std::time::UNIX_EPOCH;
use anyhow::{bail, ensure, Context as _, Result};
use async_channel::{self as channel, Receiver, Sender};
@@ -26,16 +25,17 @@ use crate::constants::{Blocked, Chattype, DC_GCL_ADD_SELF, DC_GCL_VERIFIED_ONLY}
use crate::context::Context;
use crate::events::EventType;
use crate::key::{load_self_public_key, DcKey};
use crate::log::LogExt;
use crate::login_param::LoginParam;
use crate::message::MessageState;
use crate::mimeparser::AvatarAction;
use crate::param::{Param, Params};
use crate::peerstate::{Peerstate, PeerstateVerifiedStatus};
use crate::peerstate::Peerstate;
use crate::sql::{self, params_iter};
use crate::sync::{self, Sync::*, SyncData};
use crate::sync::{self, Sync::*};
use crate::tools::{
duration_to_str, get_abs_path, improve_single_line_input, strip_rtlo_characters, time,
EmailAddress,
EmailAddress, SystemTime,
};
use crate::{chat, stock_str};
@@ -348,24 +348,6 @@ pub(crate) enum Modifier {
Created,
}
/// Verification status of the contact.
#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive)]
#[repr(u8)]
pub enum VerifiedStatus {
/// Contact is not verified.
Unverified = 0,
/// SELF has verified the fingerprint of a contact. Currently unused.
Verified = 1,
/// SELF and contact have verified their fingerprints in both directions; in the UI typically checkmarks are shown.
BidirectVerified = 2,
}
impl Default for VerifiedStatus {
fn default() -> Self {
Self::Unverified
}
}
impl Contact {
/// Loads a single contact object from the database.
///
@@ -494,6 +476,15 @@ impl Contact {
///
/// May result in a `#DC_EVENT_CONTACTS_CHANGED` event.
pub async fn create(context: &Context, name: &str, addr: &str) -> Result<ContactId> {
Self::create_ex(context, Sync, name, addr).await
}
pub(crate) async fn create_ex(
context: &Context,
sync: sync::Sync,
name: &str,
addr: &str,
) -> Result<ContactId> {
let name = improve_single_line_input(name);
let (name, addr) = sanitize_name_and_addr(&name, addr);
@@ -514,6 +505,16 @@ impl Contact {
set_blocked(context, Nosync, contact_id, false).await?;
}
if sync.into() {
chat::sync(
context,
chat::SyncId::ContactAddr(addr.to_string()),
chat::SyncAction::Rename(name.to_string()),
)
.await
.log_err(context)
.ok();
}
Ok(contact_id)
}
@@ -790,7 +791,7 @@ impl Contact {
sth_modified = Modifier::Created;
row_id = u32::try_from(transaction.last_insert_rowid())?;
info!(context, "added contact id={} addr={}", row_id, &addr);
info!(context, "Added contact id={row_id} addr={addr}.");
}
Ok(row_id)
}).await?;
@@ -1055,11 +1056,9 @@ impl Contact {
let loginparam = LoginParam::load_configured_params(context).await?;
let peerstate = Peerstate::from_addr(context, &contact.addr).await?;
if let Some(peerstate) = peerstate.filter(|peerstate| {
peerstate
.peek_key(PeerstateVerifiedStatus::Unverified)
.is_some()
}) {
if let Some(peerstate) =
peerstate.filter(|peerstate| peerstate.peek_key(false).is_some())
{
let stock_message = match peerstate.prefer_encrypt {
EncryptPreference::Mutual => stock_str::e2e_preferred(context).await,
EncryptPreference::NoPreference => stock_str::e2e_available(context).await,
@@ -1074,11 +1073,11 @@ impl Contact {
.fingerprint()
.to_string();
let fingerprint_other_verified = peerstate
.peek_key(PeerstateVerifiedStatus::BidirectVerified)
.peek_key(true)
.map(|k| k.fingerprint().to_string())
.unwrap_or_default();
let fingerprint_other_unverified = peerstate
.peek_key(PeerstateVerifiedStatus::Unverified)
.peek_key(false)
.map(|k| k.fingerprint().to_string())
.unwrap_or_default();
if loginparam.addr < peerstate.addr {
@@ -1281,20 +1280,37 @@ impl Contact {
/// otherwise use is_chat_protected().
/// Use [Self::get_verifier_id] to display the verifier contact
/// in the info section of the contact profile.
pub async fn is_verified(&self, context: &Context) -> Result<VerifiedStatus> {
pub async fn is_verified(&self, context: &Context) -> Result<bool> {
// We're always sort of secured-verified as we could verify the key on this device any time with the key
// on this device
if self.id == ContactId::SELF {
return Ok(VerifiedStatus::BidirectVerified);
return Ok(true);
}
if let Some(peerstate) = Peerstate::from_addr(context, &self.addr).await? {
if peerstate.is_using_verified_key() {
return Ok(VerifiedStatus::BidirectVerified);
}
let Some(peerstate) = Peerstate::from_addr(context, &self.addr).await? else {
return Ok(false);
};
let forward_verified = peerstate.is_using_verified_key();
let backward_verified = peerstate.is_backward_verified(context).await?;
Ok(forward_verified && backward_verified)
}
/// Returns true if we have a verified key for the contact
/// and it is the same as Autocrypt key.
/// This is enough to send messages to the contact in verified chat
/// and verify received messages, but not enough to display green checkmark
/// or add the contact to verified groups.
pub async fn is_forward_verified(&self, context: &Context) -> Result<bool> {
if self.id == ContactId::SELF {
return Ok(true);
}
Ok(VerifiedStatus::Unverified)
let Some(peerstate) = Peerstate::from_addr(context, &self.addr).await? else {
return Ok(false);
};
Ok(peerstate.is_using_verified_key())
}
/// Returns the `ContactId` that verified the contact.
@@ -1349,7 +1365,7 @@ impl Contact {
Ok(chat_id.is_protected(context).await? == ProtectionStatus::Protected)
} else {
// 1:1 chat does not exist.
Ok(self.is_verified(context).await? == VerifiedStatus::BidirectVerified)
Ok(self.is_verified(context).await?)
}
}
@@ -1500,13 +1516,14 @@ WHERE type=? AND id IN (
true => chat::SyncAction::Block,
false => chat::SyncAction::Unblock,
};
context
.add_sync_item(SyncData::AlterChat {
id: chat::SyncId::ContactAddr(contact.addr.clone()),
action,
})
.await?;
context.send_sync_msg().await?;
chat::sync(
context,
chat::SyncId::ContactAddr(contact.addr.clone()),
action,
)
.await
.log_err(context)
.ok();
}
}
@@ -1519,7 +1536,7 @@ WHERE type=? AND id IN (
/// as profile images can be set only by receiving messages, this should be always the case, however.
///
/// For contact SELF, the image is not saved in the contact-database but as Config::Selfavatar;
/// this typically happens if we see message with our own profile image, sent from another device.
/// this typically happens if we see message with our own profile image.
pub(crate) async fn set_profile_image(
context: &Context,
contact_id: ContactId,
@@ -1532,7 +1549,7 @@ pub(crate) async fn set_profile_image(
if contact_id == ContactId::SELF {
if was_encrypted {
context
.set_config(Config::Selfavatar, Some(profile_image))
.set_config_ex(Nosync, Config::Selfavatar, Some(profile_image))
.await?;
} else {
info!(context, "Do not use unencrypted selfavatar.");
@@ -1545,7 +1562,9 @@ pub(crate) async fn set_profile_image(
AvatarAction::Delete => {
if contact_id == ContactId::SELF {
if was_encrypted {
context.set_config(Config::Selfavatar, None).await?;
context
.set_config_ex(Nosync, Config::Selfavatar, None)
.await?;
} else {
info!(context, "Do not use unencrypted selfavatar deletion.");
}
@@ -1577,7 +1596,7 @@ pub(crate) async fn set_status(
if contact_id == ContactId::SELF {
if encrypted && has_chat_version {
context
.set_config(Config::Selfstatus, Some(&status))
.set_config_ex(Nosync, Config::Selfstatus, Some(&status))
.await?;
}
} else {
@@ -1712,6 +1731,12 @@ impl RecentlySeenLoop {
async fn run(context: Context, interrupt: Receiver<RecentlySeenInterrupt>) {
type MyHeapElem = (Reverse<i64>, ContactId);
let now = SystemTime::now();
let now_ts = now
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as i64;
// Priority contains all recently seen sorted by the timestamp
// when they become not recently seen.
//
@@ -1722,7 +1747,7 @@ impl RecentlySeenLoop {
.query_map(
"SELECT id, last_seen FROM contacts
WHERE last_seen > ?",
(time() - SEEN_RECENTLY_SECONDS,),
(now_ts - SEEN_RECENTLY_SECONDS,),
|row| {
let contact_id: ContactId = row.get("id")?;
let last_seen: i64 = row.get("last_seen")?;
@@ -1737,8 +1762,6 @@ impl RecentlySeenLoop {
.unwrap_or_default();
loop {
let now = SystemTime::now();
let (until, contact_id) =
if let Some((Reverse(timestamp), contact_id)) = unseen_queue.peek() {
(
@@ -1905,12 +1928,12 @@ mod tests {
// Search by name.
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.get(0), Some(&id));
assert_eq!(contacts.first(), Some(&id));
// Search by address.
let contacts = Contact::get_all(&context.ctx, 0, Some("user")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.get(0), Some(&id));
assert_eq!(contacts.first(), Some(&id));
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).await?;
assert_eq!(contacts.len(), 0);
@@ -1937,7 +1960,7 @@ mod tests {
// Search by display name (same as manually set name).
let contacts = Contact::get_all(&context.ctx, 0, Some("someone")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.get(0), Some(&id));
assert_eq!(contacts.first(), Some(&id));
Ok(())
}
@@ -2809,4 +2832,33 @@ Hi."#;
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_sync_create() -> Result<()> {
let alice0 = &TestContext::new_alice().await;
let alice1 = &TestContext::new_alice().await;
for a in [alice0, alice1] {
a.set_config_bool(Config::SyncMsgs, true).await?;
}
Contact::create(alice0, "Bob", "bob@example.net").await?;
test_utils::sync(alice0, alice1).await;
let a1b_contact_id =
Contact::lookup_id_by_addr(alice1, "bob@example.net", Origin::ManuallyCreated)
.await?
.unwrap();
let a1b_contact = Contact::get_by_id(alice1, a1b_contact_id).await?;
assert_eq!(a1b_contact.name, "Bob");
Contact::create(alice0, "Bob Renamed", "bob@example.net").await?;
test_utils::sync(alice0, alice1).await;
let id = Contact::lookup_id_by_addr(alice1, "bob@example.net", Origin::ManuallyCreated)
.await?
.unwrap();
assert_eq!(id, a1b_contact_id);
let a1b_contact = Contact::get_by_id(alice1, a1b_contact_id).await?;
assert_eq!(a1b_contact.name, "Bob Renamed");
Ok(())
}
}

View File

@@ -6,28 +6,37 @@ use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime};
use std::time::Duration;
use anyhow::{bail, ensure, Context as _, Result};
use async_channel::{self as channel, Receiver, Sender};
use pgp::SignedPublicKey;
use ratelimit::Ratelimit;
use tokio::sync::{Mutex, Notify, RwLock};
use crate::chat::{get_chat_cnt, ChatId};
use crate::aheader::EncryptPreference;
use crate::chat::{get_chat_cnt, ChatId, ProtectionStatus};
use crate::config::Config;
use crate::constants::DC_VERSION_STR;
use crate::contact::Contact;
use crate::constants::{
self, DC_BACKGROUND_FETCH_QUOTA_CHECK_RATELIMIT, DC_CHAT_ID_TRASH, DC_VERSION_STR,
};
use crate::contact::{Contact, ContactId};
use crate::debug_logging::DebugLogging;
use crate::download::DownloadState;
use crate::events::{Event, EventEmitter, EventType, Events};
use crate::key::{load_self_public_key, DcKey as _};
use crate::imap::{FolderMeaning, Imap, ServerMetadata};
use crate::key::{load_self_public_key, load_self_secret_key, DcKey as _};
use crate::login_param::LoginParam;
use crate::message::{self, MessageState, MsgId};
use crate::message::{self, Message, MessageState, MsgId, Viewtype};
use crate::param::{Param, Params};
use crate::peerstate::Peerstate;
use crate::push::PushSubscriber;
use crate::quota::QuotaInfo;
use crate::scheduler::SchedulerState;
use crate::scheduler::{convert_folder_meaning, SchedulerState};
use crate::sql::Sql;
use crate::stock_str::StockStrings;
use crate::timesmearing::SmearedTimestamp;
use crate::tools::{duration_to_str, time};
use crate::tools::{self, create_id, duration_to_str, time, time_elapsed};
/// Builder for the [`Context`].
///
@@ -78,6 +87,8 @@ pub struct ContextBuilder {
events: Events,
stock_strings: StockStrings,
password: Option<String>,
push_subscriber: Option<PushSubscriber>,
}
impl ContextBuilder {
@@ -93,6 +104,7 @@ impl ContextBuilder {
events: Events::new(),
stock_strings: StockStrings::new(),
password: None,
push_subscriber: None,
}
}
@@ -147,11 +159,32 @@ impl ContextBuilder {
self
}
/// Opens the [`Context`].
/// Sets push subscriber.
pub(crate) fn with_push_subscriber(mut self, push_subscriber: PushSubscriber) -> Self {
self.push_subscriber = Some(push_subscriber);
self
}
/// Builds the [`Context`] without opening it.
pub async fn build(self) -> Result<Context> {
let push_subscriber = self.push_subscriber.unwrap_or_default();
let context = Context::new_closed(
&self.dbfile,
self.id,
self.events,
self.stock_strings,
push_subscriber,
)
.await?;
Ok(context)
}
/// Builds the [`Context`] and opens it.
///
/// Returns error if context cannot be opened with the given passphrase.
pub async fn open(self) -> Result<Context> {
let context =
Context::new_closed(&self.dbfile, self.id, self.events, self.stock_strings).await?;
let password = self.password.unwrap_or_default();
let password = self.password.clone().unwrap_or_default();
let context = self.build().await?;
match context.open(password).await? {
true => Ok(context),
false => bail!("database could not be decrypted, incorrect or missing password"),
@@ -224,7 +257,10 @@ pub struct InnerContext {
/// <https://datatracker.ietf.org/doc/html/rfc2971>
pub(crate) server_id: RwLock<Option<HashMap<String, String>>>,
pub(crate) last_full_folder_scan: Mutex<Option<Instant>>,
/// IMAP METADATA.
pub(crate) metadata: RwLock<Option<ServerMetadata>>,
pub(crate) last_full_folder_scan: Mutex<Option<tools::Time>>,
/// ID for this `Context` in the current process.
///
@@ -232,7 +268,7 @@ pub struct InnerContext {
/// be identified by this ID.
pub(crate) id: u32,
creation_time: SystemTime,
creation_time: tools::Time,
/// The text of the last error logged and emitted as an event.
/// If the ui wants to display an error after a failure,
@@ -244,6 +280,13 @@ pub struct InnerContext {
/// Standard RwLock instead of [`tokio::sync::RwLock`] is used
/// because the lock is used from synchronous [`Context::emit_event`].
pub(crate) debug_logging: std::sync::RwLock<Option<DebugLogging>>,
/// Push subscriber to store device token
/// and register for heartbeat notifications.
pub(crate) push_subscriber: PushSubscriber,
/// True if account has subscribed to push notifications via IMAP.
pub(crate) push_subscribed: AtomicBool,
}
/// The state of ongoing process.
@@ -253,7 +296,7 @@ enum RunningState {
Running { cancel_sender: Sender<()> },
/// Cancel signal has been sent, waiting for ongoing process to be freed.
ShallStop { request: Instant },
ShallStop { request: tools::Time },
/// There is no ongoing process, a new one can be allocated.
Stopped,
@@ -289,7 +332,8 @@ impl Context {
events: Events,
stock_strings: StockStrings,
) -> Result<Context> {
let context = Self::new_closed(dbfile, id, events, stock_strings).await?;
let context =
Self::new_closed(dbfile, id, events, stock_strings, Default::default()).await?;
// Open the database if is not encrypted.
if context.check_passphrase("".to_string()).await? {
@@ -304,6 +348,7 @@ impl Context {
id: u32,
events: Events,
stockstrings: StockStrings,
push_subscriber: PushSubscriber,
) -> Result<Context> {
let mut blob_fname = OsString::new();
blob_fname.push(dbfile.file_name().unwrap_or_default());
@@ -312,7 +357,14 @@ impl Context {
if !blobdir.exists() {
tokio::fs::create_dir_all(&blobdir).await?;
}
let context = Context::with_blobdir(dbfile.into(), blobdir, id, events, stockstrings)?;
let context = Context::with_blobdir(
dbfile.into(),
blobdir,
id,
events,
stockstrings,
push_subscriber,
)?;
Ok(context)
}
@@ -355,6 +407,7 @@ impl Context {
id: u32,
events: Events,
stockstrings: StockStrings,
push_subscriber: PushSubscriber,
) -> Result<Context> {
ensure!(
blobdir.is_dir(),
@@ -384,10 +437,13 @@ impl Context {
resync_request: AtomicBool::new(false),
new_msgs_notify,
server_id: RwLock::new(None),
creation_time: std::time::SystemTime::now(),
metadata: RwLock::new(None),
creation_time: tools::Time::now(),
last_full_folder_scan: Mutex::new(None),
last_error: std::sync::RwLock::new("".to_string()),
debug_logging: std::sync::RwLock::new(None),
push_subscriber,
push_subscribed: AtomicBool::new(false),
};
let ctx = Context {
@@ -398,7 +454,7 @@ impl Context {
}
/// Starts the IO scheduler.
pub async fn start_io(&mut self) {
pub async fn start_io(&self) {
if !self.is_configured().await.unwrap_or_default() {
warn!(self, "can not start io on a context that is not configured");
return;
@@ -436,6 +492,58 @@ impl Context {
self.scheduler.maybe_network().await;
}
/// Does a background fetch
/// pauses the scheduler and does one imap fetch, then unpauses and returns
pub async fn background_fetch(&self) -> Result<()> {
if !(self.is_configured().await?) {
return Ok(());
}
let address = self.get_primary_self_addr().await?;
let time_start = tools::Time::now();
info!(self, "background_fetch started fetching {address}");
let _pause_guard = self.scheduler.pause(self.clone()).await?;
// connection
let mut connection = Imap::new_configured(self, channel::bounded(1).1).await?;
let mut session = connection.prepare(self).await?;
// fetch imap folders
for folder_meaning in [FolderMeaning::Inbox, FolderMeaning::Mvbox] {
let (_, watch_folder) = convert_folder_meaning(self, folder_meaning).await?;
connection
.fetch_move_delete(self, &mut session, &watch_folder, folder_meaning)
.await?;
}
// update quota (to send warning if full) - but only check it once in a while
let quota_needs_update = {
let quota = self.quota.read().await;
quota
.as_ref()
.filter(|quota| {
time_elapsed(&quota.modified)
> Duration::from_secs(DC_BACKGROUND_FETCH_QUOTA_CHECK_RATELIMIT)
})
.is_none()
};
if quota_needs_update {
if let Err(err) = self.update_recent_quota(&mut session).await {
warn!(self, "Failed to update quota: {err:#}.");
}
}
info!(
self,
"background_fetch done for {address} took {:?}",
time_elapsed(&time_start),
);
Ok(())
}
pub(crate) async fn schedule_resync(&self) -> Result<()> {
self.resync_request.store(true, Ordering::Relaxed);
self.scheduler.interrupt_inbox().await;
@@ -532,7 +640,7 @@ impl Context {
pub(crate) async fn free_ongoing(&self) {
let mut s = self.running_state.write().await;
if let RunningState::ShallStop { request } = *s {
info!(self, "Ongoing stopped in {:?}", request.elapsed());
info!(self, "Ongoing stopped in {:?}", time_elapsed(&request));
}
*s = RunningState::Stopped;
}
@@ -547,7 +655,7 @@ impl Context {
}
info!(self, "Signaling the ongoing process to stop ASAP.",);
*s = RunningState::ShallStop {
request: Instant::now(),
request: tools::Time::now(),
};
}
RunningState::ShallStop { .. } | RunningState::Stopped => {
@@ -613,7 +721,7 @@ impl Context {
let only_fetch_mvbox = self.get_config_int(Config::OnlyFetchMvbox).await?;
let folders_configured = self
.sql
.get_raw_config_int("folders_configured")
.get_raw_config_int(constants::DC_FOLDERS_CONFIGURED_KEY)
.await?
.unwrap_or_default();
@@ -653,7 +761,7 @@ impl Context {
);
res.insert("journal_mode", journal_mode);
res.insert("blobdir", self.get_blobdir().display().to_string());
res.insert("display_name", displayname.unwrap_or_else(|| unset.into()));
res.insert("displayname", displayname.unwrap_or_else(|| unset.into()));
res.insert(
"selfavatar",
self.get_config(Config::Selfavatar)
@@ -669,6 +777,16 @@ impl Context {
res.insert("imap_server_id", format!("{server_id:?}"));
}
if let Some(metadata) = &*self.metadata.read().await {
if let Some(comment) = &metadata.comment {
res.insert("imap_server_comment", format!("{comment:?}"));
}
if let Some(admin) = &metadata.admin {
res.insert("imap_server_admin", format!("{admin:?}"));
}
}
res.insert("secondary_addrs", secondary_addrs);
res.insert(
"fetch_existing_msgs",
@@ -686,6 +804,12 @@ impl Context {
"show_emails",
self.get_config_int(Config::ShowEmails).await?.to_string(),
);
res.insert(
"save_mime_headers",
self.get_config_bool(Config::SaveMimeHeaders)
.await?
.to_string(),
);
res.insert(
"download_limit",
self.get_config_int(Config::DownloadLimit)
@@ -695,7 +819,10 @@ impl Context {
res.insert("sentbox_watch", sentbox_watch.to_string());
res.insert("mvbox_move", mvbox_move.to_string());
res.insert("only_fetch_mvbox", only_fetch_mvbox.to_string());
res.insert("folders_configured", folders_configured.to_string());
res.insert(
constants::DC_FOLDERS_CONFIGURED_KEY,
folders_configured.to_string(),
);
res.insert("configured_inbox_folder", configured_inbox_folder);
res.insert("configured_sentbox_folder", configured_sentbox_folder);
res.insert("configured_mvbox_folder", configured_mvbox_folder);
@@ -746,6 +873,12 @@ impl Context {
.await?
.to_string(),
);
res.insert(
"last_cant_decrypt_outgoing_msgs",
self.get_config_int(Config::LastCantDecryptOutgoingMsgs)
.await?
.to_string(),
);
res.insert(
"scan_all_folders_debounce_secs",
self.get_config_int(Config::ScanAllFoldersDebounceSecs)
@@ -789,12 +922,177 @@ impl Context {
.to_string(),
);
let elapsed = self.creation_time.elapsed();
res.insert("uptime", duration_to_str(elapsed.unwrap_or_default()));
let elapsed = time_elapsed(&self.creation_time);
res.insert("uptime", duration_to_str(elapsed));
Ok(res)
}
async fn get_self_report(&self) -> Result<String> {
#[derive(Default)]
struct ChatNumbers {
protected: u32,
protection_broken: u32,
opportunistic_dc: u32,
opportunistic_mua: u32,
unencrypted_dc: u32,
unencrypted_mua: u32,
}
let mut res = String::new();
res += &format!("core_version {}\n", get_version_str());
let num_msgs: u32 = self
.sql
.query_get_value(
"SELECT COUNT(*) FROM msgs WHERE hidden=0 AND chat_id!=?",
(DC_CHAT_ID_TRASH,),
)
.await?
.unwrap_or_default();
res += &format!("num_msgs {}\n", num_msgs);
let num_chats: u32 = self
.sql
.query_get_value("SELECT COUNT(*) FROM chats WHERE id>9 AND blocked!=1", ())
.await?
.unwrap_or_default();
res += &format!("num_chats {}\n", num_chats);
let db_size = tokio::fs::metadata(&self.sql.dbfile).await?.len();
res += &format!("db_size_bytes {}\n", db_size);
let secret_key = &load_self_secret_key(self).await?.primary_key;
let key_created = secret_key.created_at().timestamp();
res += &format!("key_created {}\n", key_created);
// how many of the chats active in the last months are:
// - protected
// - protection-broken
// - opportunistic-encrypted and the contact uses Delta Chat
// - opportunistic-encrypted and the contact uses a classical MUA
// - unencrypted and the contact uses Delta Chat
// - unencrypted and the contact uses a classical MUA
let three_months_ago = time().saturating_sub(3600 * 24 * 30 * 3);
let chats = self
.sql
.query_map(
"SELECT c.protected, m.param, m.msgrmsg
FROM chats c
JOIN msgs m
ON c.id=m.chat_id
AND m.id=(
SELECT id
FROM msgs
WHERE chat_id=c.id
AND hidden=0
AND download_state=?
AND to_id!=?
ORDER BY timestamp DESC, id DESC LIMIT 1)
WHERE c.id>9
AND (c.blocked=0 OR c.blocked=2)
AND IFNULL(m.timestamp,c.created_timestamp) > ?
GROUP BY c.id",
(DownloadState::Done, ContactId::INFO, three_months_ago),
|row| {
let protected: ProtectionStatus = row.get(0)?;
let message_param: Params =
row.get::<_, String>(1)?.parse().unwrap_or_default();
let is_dc_message: bool = row.get(2)?;
Ok((protected, message_param, is_dc_message))
},
|rows| {
let mut chats = ChatNumbers::default();
for row in rows {
let (protected, message_param, is_dc_message) = row?;
let encrypted = message_param
.get_bool(Param::GuaranteeE2ee)
.unwrap_or(false);
if protected == ProtectionStatus::Protected {
chats.protected += 1;
} else if protected == ProtectionStatus::ProtectionBroken {
chats.protection_broken += 1;
} else if encrypted {
if is_dc_message {
chats.opportunistic_dc += 1;
} else {
chats.opportunistic_mua += 1;
}
} else if is_dc_message {
chats.unencrypted_dc += 1;
} else {
chats.unencrypted_mua += 1;
}
}
Ok(chats)
},
)
.await?;
res += &format!("chats_protected {}\n", chats.protected);
res += &format!("chats_protection_broken {}\n", chats.protection_broken);
res += &format!("chats_opportunistic_dc {}\n", chats.opportunistic_dc);
res += &format!("chats_opportunistic_mua {}\n", chats.opportunistic_mua);
res += &format!("chats_unencrypted_dc {}\n", chats.unencrypted_dc);
res += &format!("chats_unencrypted_mua {}\n", chats.unencrypted_mua);
let self_reporting_id = match self.get_config(Config::SelfReportingId).await? {
Some(id) => id,
None => {
let id = create_id();
self.set_config(Config::SelfReportingId, Some(&id)).await?;
id
}
};
res += &format!("self_reporting_id {}", self_reporting_id);
Ok(res)
}
/// Drafts a message with statistics about the usage of Delta Chat.
/// The user can inspect the message if they want, and then hit "Send".
///
/// On the other end, a bot will receive the message and make it available
/// to Delta Chat's developers.
pub async fn draft_self_report(&self) -> Result<ChatId> {
const SELF_REPORTING_BOT: &str = "self_reporting@testrun.org";
let contact_id = Contact::create(self, "Statistics bot", SELF_REPORTING_BOT).await?;
let chat_id = ChatId::create_for_contact(self, contact_id).await?;
// We're including the bot's public key in Delta Chat
// so that the first message to the bot can directly be encrypted:
let public_key = SignedPublicKey::from_base64(
"xjMEZbfBlBYJKwYBBAHaRw8BAQdABpLWS2PUIGGo4pslVt4R8sylP5wZihmhf1DTDr3oCM\
PNHDxzZWxmX3JlcG9ydGluZ0B0ZXN0cnVuLm9yZz7CiwQQFggAMwIZAQUCZbfBlAIbAwQLCQgHBhUI\
CQoLAgMWAgEWIQTS2i16sHeYTckGn284K3M5Z4oohAAKCRA4K3M5Z4oohD8dAQCQV7CoH6UP4PD+Nq\
I4kW5tbbqdh2AnDROg60qotmLExAEAxDfd3QHAK9f8b9qQUbLmHIztCLxhEuVbWPBEYeVW0gvOOARl\
t8GUEgorBgEEAZdVAQUBAQdAMBUhYoAAcI625vGZqnM5maPX4sGJ7qvJxPAFILPy6AcDAQgHwngEGB\
YIACAFAmW3wZQCGwwWIQTS2i16sHeYTckGn284K3M5Z4oohAAKCRA4K3M5Z4oohPwCAQCvzk1ObIkj\
2GqsuIfaULlgdnfdZY8LNary425CEfHZDQD5AblXVrlMO1frdlc/Vo9z3pEeCrfYdD7ITD3/OeVoiQ\
4=",
)?;
let mut peerstate = Peerstate::from_public_key(
SELF_REPORTING_BOT,
0,
EncryptPreference::Mutual,
&public_key,
);
let fingerprint = public_key.fingerprint();
peerstate.set_verified(public_key, fingerprint, "".to_string())?;
peerstate.save_to_db(&self.sql).await?;
chat_id
.set_protection(self, ProtectionStatus::Protected, time(), Some(contact_id))
.await?;
let mut msg = Message::new(Viewtype::Text);
msg.text = self.get_self_report().await?;
chat_id.set_draft(self, Some(&mut msg)).await?;
Ok(chat_id)
}
/// Get a list of fresh, unmuted messages in unblocked chats.
///
/// The list starts with the most recent message
@@ -1051,23 +1349,18 @@ pub fn get_version_str() -> &'static str {
#[cfg(test)]
mod tests {
use std::time::Duration;
use anyhow::Context as _;
use strum::IntoEnumIterator;
use tempfile::tempdir;
use super::*;
use crate::chat::{
get_chat_contacts, get_chat_msgs, send_msg, set_muted, Chat, ChatId, MuteDuration,
};
use crate::chat::{get_chat_contacts, get_chat_msgs, send_msg, set_muted, Chat, MuteDuration};
use crate::chatlist::Chatlist;
use crate::constants::Chattype;
use crate::contact::ContactId;
use crate::message::{Message, Viewtype};
use crate::mimeparser::SystemMessage;
use crate::receive_imf::receive_imf;
use crate::test_utils::TestContext;
use crate::tools::create_outgoing_rfc724_mid;
use crate::test_utils::{get_chat_msg, TestContext};
use crate::tools::{create_outgoing_rfc724_mid, SystemTime};
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_wrong_db() -> Result<()> {
@@ -1102,7 +1395,7 @@ mod tests {
\n\
hello\n",
contact.get_addr(),
create_outgoing_rfc724_mid(None, contact.get_addr())
create_outgoing_rfc724_mid()
);
println!("{msg}");
receive_imf(t, msg.as_bytes(), false).await.unwrap();
@@ -1252,7 +1545,14 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = PathBuf::new();
let res = Context::with_blobdir(dbfile, blobdir, 1, Events::new(), StockStrings::new());
let res = Context::with_blobdir(
dbfile,
blobdir,
1,
Events::new(),
StockStrings::new(),
Default::default(),
);
assert!(res.is_err());
}
@@ -1261,7 +1561,14 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("blobs");
let res = Context::with_blobdir(dbfile, blobdir, 1, Events::new(), StockStrings::new());
let res = Context::with_blobdir(
dbfile,
blobdir,
1,
Events::new(),
StockStrings::new(),
Default::default(),
);
assert!(res.is_err());
}
@@ -1276,14 +1583,14 @@ mod tests {
let t = TestContext::new().await;
let info = t.get_info().await.unwrap();
assert!(info.get("database_dir").is_some());
assert!(info.contains_key("database_dir"));
}
#[test]
fn test_get_info_no_context() {
let info = get_info();
assert!(info.get("deltachat_core_version").is_some());
assert!(info.get("database_dir").is_none());
assert!(info.contains_key("deltachat_core_version"));
assert!(!info.contains_key("database_dir"));
assert_eq!(info.get("level").unwrap(), "awesome");
}
@@ -1304,7 +1611,7 @@ mod tests {
"mail_port",
"mail_security",
"notify_about_wrong_pw",
"save_mime_headers",
"self_reporting_id",
"selfstatus",
"send_server",
"send_user",
@@ -1318,6 +1625,7 @@ mod tests {
"socks5_port",
"socks5_user",
"socks5_password",
"key_id",
];
let t = TestContext::new().await;
let info = t.get_info().await.unwrap();
@@ -1369,7 +1677,7 @@ mod tests {
assert_eq!(res.len(), 2);
// Message added later is returned first.
assert_eq!(res.get(0), Some(&msg2.id));
assert_eq!(res.first(), Some(&msg2.id));
assert_eq!(res.get(1), Some(&msg1.id));
// Global search with longer text does not find any message.
@@ -1482,16 +1790,18 @@ mod tests {
let dir = tempdir()?;
let dbfile = dir.path().join("db.sqlite");
let id = 1;
let context = Context::new_closed(&dbfile, id, Events::new(), StockStrings::new())
let context = ContextBuilder::new(dbfile.clone())
.with_id(1)
.build()
.await
.context("failed to create context")?;
assert_eq!(context.open("foo".to_string()).await?, true);
assert_eq!(context.is_open().await, true);
drop(context);
let id = 2;
let context = Context::new(&dbfile, id, Events::new(), StockStrings::new())
let context = ContextBuilder::new(dbfile)
.with_id(2)
.build()
.await
.context("failed to create context")?;
assert_eq!(context.is_open().await, false);
@@ -1507,8 +1817,9 @@ mod tests {
let dir = tempdir()?;
let dbfile = dir.path().join("db.sqlite");
let id = 1;
let context = Context::new_closed(&dbfile, id, Events::new(), StockStrings::new())
let context = ContextBuilder::new(dbfile)
.with_id(1)
.build()
.await
.context("failed to create context")?;
assert_eq!(context.open("foo".to_string()).await?, true);
@@ -1586,7 +1897,7 @@ mod tests {
let bob_next_msg_ids = bob.get_next_msgs().await?;
assert_eq!(bob_next_msg_ids.len(), 1);
assert_eq!(bob_next_msg_ids.get(0), Some(&received_msg.id));
assert_eq!(bob_next_msg_ids.first(), Some(&received_msg.id));
bob.set_config_u32(Config::LastMsgId, received_msg.id.to_u32())
.await?;
@@ -1595,7 +1906,7 @@ mod tests {
// Next messages include self-sent messages.
let alice_next_msg_ids = alice.get_next_msgs().await?;
assert_eq!(alice_next_msg_ids.len(), 1);
assert_eq!(alice_next_msg_ids.get(0), Some(&sent_msg.sender_msg_id));
assert_eq!(alice_next_msg_ids.first(), Some(&sent_msg.sender_msg_id));
alice
.set_config_u32(Config::LastMsgId, sent_msg.sender_msg_id.to_u32())
@@ -1604,4 +1915,24 @@ mod tests {
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_draft_self_report() -> Result<()> {
let alice = TestContext::new_alice().await;
let chat_id = alice.draft_self_report().await?;
let msg = get_chat_msg(&alice, chat_id, 0, 1).await;
assert_eq!(msg.get_info_type(), SystemMessage::ChatProtectionEnabled);
let chat = Chat::load_from_db(&alice, chat_id).await?;
assert!(chat.is_protected());
let mut draft = chat_id.get_draft(&alice).await?.unwrap();
assert!(draft.text.starts_with("core_version"));
// Test that sending into the protected chat works:
let _sent = alice.send_msg(chat_id, &mut draft).await;
Ok(())
}
}

View File

@@ -54,7 +54,7 @@ pub async fn debug_logging_loop(context: &Context, events: Receiver<DebugEventLo
match context
.write_status_update_inner(
&msg_id,
StatusUpdateItem {
&StatusUpdateItem {
payload: json!({
"event": event,
"time": time,
@@ -62,6 +62,7 @@ pub async fn debug_logging_loop(context: &Context, events: Receiver<DebugEventLo
info: None,
summary: None,
document: None,
uid: None,
},
)
.await
@@ -70,10 +71,17 @@ pub async fn debug_logging_loop(context: &Context, events: Receiver<DebugEventLo
eprintln!("Can't log event to webxdc status update: {err:#}");
}
Ok(serial) => {
context.emit_event(EventType::WebxdcStatusUpdate {
msg_id,
status_update_serial: serial,
});
if let Some(serial) = serial {
if !matches!(event, EventType::WebxdcStatusUpdate { .. }) {
context.emit_event(EventType::WebxdcStatusUpdate {
msg_id,
status_update_serial: serial,
});
}
} else {
// This should not happen as the update has no `uid`.
error!(context, "Debug logging update is not created.");
};
}
}
}

View File

@@ -23,34 +23,12 @@ use crate::pgp;
///
/// If the message is wrongly signed, HashSet will be empty.
pub fn try_decrypt(
context: &Context,
mail: &ParsedMail<'_>,
private_keyring: &[SignedSecretKey],
public_keyring_for_validate: &[SignedPublicKey],
) -> Result<Option<(Vec<u8>, HashSet<Fingerprint>)>> {
let encrypted_data_part = match {
let mime = get_autocrypt_mime(mail);
if mime.is_some() {
info!(context, "Detected Autocrypt-mime message.");
}
mime
}
.or_else(|| {
let mime = get_mixed_up_mime(mail);
if mime.is_some() {
info!(context, "Detected mixed-up mime message.");
}
mime
})
.or_else(|| {
let mime = get_attachment_mime(mail);
if mime.is_some() {
info!(context, "Detected attached Autocrypt-mime message.");
}
mime
}) {
None => return Ok(None),
Some(res) => res,
let Some(encrypted_data_part) = get_encrypted_mime(mail) else {
return Ok(None);
};
decrypt_part(
@@ -87,28 +65,29 @@ pub(crate) async fn prepare_decryption(
});
}
let autocrypt_header =
if let Some(autocrypt_header_value) = mail.headers.get_header_value(HeaderDef::Autocrypt) {
match Aheader::from_str(&autocrypt_header_value) {
Ok(header) if addr_cmp(&header.addr, from) => Some(header),
Ok(header) => {
warn!(
context,
"Autocrypt header address {:?} is not {:?}.", header.addr, from
);
None
}
Err(err) => {
warn!(context, "Failed to parse Autocrypt header: {:#}.", err);
None
}
let autocrypt_header = if context.is_self_addr(from).await? {
None
} else if let Some(aheader_value) = mail.headers.get_header_value(HeaderDef::Autocrypt) {
match Aheader::from_str(&aheader_value) {
Ok(header) if addr_cmp(&header.addr, from) => Some(header),
Ok(header) => {
warn!(
context,
"Autocrypt header address {:?} is not {:?}.", header.addr, from
);
None
}
} else {
None
};
Err(err) => {
warn!(context, "Failed to parse Autocrypt header: {:#}.", err);
None
}
}
} else {
None
};
let dkim_results = handle_authres(context, mail, from, message_time).await?;
let allow_aeap = get_encrypted_mime(mail).is_some();
let peerstate = get_autocrypt_peerstate(
context,
from,
@@ -116,6 +95,7 @@ pub(crate) async fn prepare_decryption(
message_time,
// Disallowing keychanges is disabled for now:
true, // dkim_results.allow_keychange,
allow_aeap,
)
.await?;
@@ -144,6 +124,13 @@ pub struct DecryptionInfo {
pub(crate) dkim_results: authres::DkimResults,
}
/// Returns a reference to the encrypted payload of a message.
fn get_encrypted_mime<'a, 'b>(mail: &'a ParsedMail<'b>) -> Option<&'a ParsedMail<'b>> {
get_autocrypt_mime(mail)
.or_else(|| get_mixed_up_mime(mail))
.or_else(|| get_attachment_mime(mail))
}
/// Returns a reference to the encrypted payload of a ["Mixed
/// Up"][pgpmime-message-mangling] message.
///
@@ -282,6 +269,7 @@ pub(crate) fn validate_detached_signature<'a, 'b>(
}
}
/// Returns public keyring for `peerstate`.
pub(crate) fn keyring_from_peerstate(peerstate: Option<&Peerstate>) -> Vec<SignedPublicKey> {
let mut public_keyring_for_validate = Vec::new();
if let Some(peerstate) = peerstate {
@@ -309,23 +297,28 @@ pub(crate) async fn get_autocrypt_peerstate(
autocrypt_header: Option<&Aheader>,
message_time: i64,
allow_change: bool,
allow_aeap: bool,
) -> Result<Option<Peerstate>> {
let allow_change = allow_change && !context.is_self_addr(from).await?;
let mut peerstate;
// Apply Autocrypt header
if let Some(header) = autocrypt_header {
// The "from_verified_fingerprint" part is for AEAP:
// If we know this fingerprint from another addr,
// we may want to do a transition from this other addr
// (and keep its peerstate)
// For security reasons, for now, we only do a transition
// if the fingerprint is verified.
peerstate = Peerstate::from_verified_fingerprint_or_addr(
context,
&header.public_key.fingerprint(),
from,
)
.await?;
if allow_aeap {
// If we know this fingerprint from another addr,
// we may want to do a transition from this other addr
// (and keep its peerstate)
// For security reasons, for now, we only do a transition
// if the fingerprint is verified.
peerstate = Peerstate::from_verified_fingerprint_or_addr(
context,
&header.public_key.fingerprint(),
from,
)
.await?;
} else {
peerstate = Peerstate::from_addr(context, from).await?;
}
if let Some(ref mut peerstate) = peerstate {
if addr_cmp(&peerstate.addr, from) {

Some files were not shown because too many files have changed in this diff Show More