Compare commits

...

462 Commits

Author SHA1 Message Date
link2xt
8538a3c148 chore(release): prepare for 1.142.7 2024-08-17 16:00:42 +00:00
link2xt
cb4b992204 fix: do not request ALPN on standard ports and when using STARTTLS
Apparently some providers fail TLS connection
with "no_application_protocol" alert
even when requesting "imap" protocol for IMAP connection
and "smtp" protocol for SMTP connection.

Fixes <https://github.com/deltachat/deltachat-core-rust/issues/5892>.
2024-08-17 15:56:26 +00:00
link2xt
af4d54ab50 fix: do not save "Automatic" into configured_imap_certificate_checks
configured_imap_certificate_checks=0 means
accept invalid certificates unless provider database
says otherwise or SOCKS5 is enabled.
It should not be saved into the database anymore.

This bug was introduced in
<https://github.com/deltachat/deltachat-core-rust/pull/5854>
(commit 6b4532a08e)
and affects released core 1.142.4, 1.142.5 and 1.142.6.

Fix reverts faulty fix from
<https://github.com/deltachat/deltachat-core-rust/pull/5886>
(commit a268946f8d)
which changed the way configured_imap_certificate_checks=0
is interpreted and introduced problems
for existing setups with configured_imap_certificate_checks=0:
<https://github.com/deltachat/deltachat-core-rust/issues/5889>.

Existing test from previous fix is not reverted
and still applies.
Regression test is added to check that
configured_imap_certificate_checks
is not "0" for new accounts.
2024-08-17 15:18:06 +00:00
link2xt
1faff84905 build: update rpgp from 0.13.1 to 0.13.2
This fixes the problem with old core (<1.136.0)
not being able to decrypt messages
produced with the new core
when using Ed25519 keys.

The issue is described in
<https://github.com/deltachat/deltachat-core-rust/issues/5881>
2024-08-17 11:31:22 +00:00
iequidoo
62fde21d9a fix: Create a group unblocked for bot even if 1:1 chat is blocked (#5514) 2024-08-16 13:14:29 -03:00
iequidoo
6f3729a00f test: Protected group for bot is auto-accepted 2024-08-16 13:14:29 -03:00
iequidoo
fbf66ba02b feat(jsonrpc): Add ContactObject::e2ee_avail
This can be helpful for the chatmail case, the app can warn the user at least.
2024-08-15 14:41:09 -03:00
link2xt
ed74f4d1d9 chore(release): prepare for 1.142.6 2024-08-15 16:57:56 +00:00
link2xt
a268946f8d fix: default to strict TLS checks if not configured
If user has not set any settings manually
and provider is not configured,
default to strict TLS checks.

Bug was introduced in
<https://github.com/deltachat/deltachat-core-rust/pull/5854>
(commit 6b4532a08e)
and affects released core 1.142.4 and 1.142.5.

The problem only affects accounts configured
using these core versions with provider
not in the provider database or when using advanced settings.
2024-08-15 16:45:48 +00:00
link2xt
7432c6de84 chore(deltachat-rpc-client): fix ruff 0.6.0 warnings 2024-08-15 16:20:02 +00:00
link2xt
7fe9342d0d docs: tweak changelog 2024-08-15 02:16:11 +00:00
B. Petersen
a0e89e4d4e chore(release): prepare for 1.142.5 2024-08-15 02:13:59 +00:00
Hocuri
0c3a476449 fix: Increase timeout for QR generation to 60s (#5882)
On big accounts, it can take more than 10s, so that QR generation
failed.
2024-08-14 22:46:48 +02:00
B. Petersen
de517c15ff chore: update provider database 2024-08-14 21:58:37 +02:00
link2xt
b83d5b0dbf docs: document new mdns_enabled behavior 2024-08-12 20:47:27 +00:00
dependabot[bot]
27924a259f Merge pull request #5871 from deltachat/dependabot/github_actions/actions/setup-node-4 2024-08-11 18:11:13 +00:00
dependabot[bot]
530256b1bf chore(deps): bump actions/setup-node from 2 to 4
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2 to 4.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-11 02:29:57 +00:00
dependabot[bot]
23d15d7485 Merge pull request #5872 from deltachat/dependabot/github_actions/dependabot/fetch-metadata-2.2.0 2024-08-11 02:28:55 +00:00
dependabot[bot]
3c38d2e105 Merge pull request #5873 from deltachat/dependabot/github_actions/horochx/deploy-via-scp-1.1.0 2024-08-11 02:28:09 +00:00
iequidoo
a53ffcf5e3 fix: store_seen_flags_on_imap: Skip to next messages if couldn't select folder (#5870)
`imap::Session::store_seen_flags_on_imap()` handles messages from multiple folders, so not being
able to select one folder mustn't fail the whole function.
2024-08-10 17:39:24 -03:00
iequidoo
22366cf246 fix: Still try to create "INBOX.DeltaChat" if couldn't create "DeltaChat" (#5870)
It appeared that some servers require namespace-style names for folders created via IMAP, like
"INBOX.DeltaChat". This partially reverts 05c256dd5b.
2024-08-10 17:39:24 -03:00
dependabot[bot]
ddc2b86875 Merge pull request #5874 from deltachat/dependabot/cargo/serde-1.0.205 2024-08-09 20:51:14 +00:00
dependabot[bot]
9e966615f2 Merge pull request #5875 from deltachat/dependabot/cargo/regex-1.10.6 2024-08-09 20:50:40 +00:00
dependabot[bot]
3335fc727d chore(cargo): bump regex from 1.10.5 to 1.10.6
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.5 to 1.10.6.
- [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.5...1.10.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-09 19:14:31 +00:00
dependabot[bot]
00d7b38e02 chore(cargo): bump serde from 1.0.204 to 1.0.205
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.204 to 1.0.205.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.204...v1.0.205)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-09 19:14:15 +00:00
dependabot[bot]
2a8a98c432 chore(deps): bump horochx/deploy-via-scp from 1.0.1 to 1.1.0
Bumps [horochx/deploy-via-scp](https://github.com/horochx/deploy-via-scp) from 1.0.1 to 1.1.0.
- [Release notes](https://github.com/horochx/deploy-via-scp/releases)
- [Commits](https://github.com/horochx/deploy-via-scp/compare/v1.0.1...1.1.0)

---
updated-dependencies:
- dependency-name: horochx/deploy-via-scp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-09 19:14:02 +00:00
dependabot[bot]
13841491d4 chore(deps): bump dependabot/fetch-metadata from 1.1.1 to 2.2.0
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.1.1 to 2.2.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.1.1...v2.2.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-09 19:13:59 +00:00
link2xt
2137c05cd6 ci: configure Dependabot to update GitHub Actions 2024-08-09 19:13:31 +00:00
link2xt
6519630d46 chore(release): prepare for 1.142.4 2024-08-09 17:30:54 +00:00
iequidoo
7c6d6a4b12 fix: Still send MDNs from bots by default
Fixup for 5ce44ade1. It is good that read receipts are sent by bots to see the bot received the
message. Thanks to @adbenitez for pointing this out.
2024-08-09 13:58:29 -03:00
link2xt
745b33f174 build: use --locked with cargo install
`cargo install` ignores lockfile by default.
Without lockfile current build fails
due to iroh-net 0.21.0 depending on `derive_more` 1.0.0-beta.6
but failing to compile with `derive_more` 1.0.0.-beta.7.
This particular error will be fixed by upgrading to iroh 0.22.0,
but using lockfile will avoid similar problems in the future.
2024-08-09 16:22:19 +00:00
link2xt
153188db20 feat: allow autoconfig when SOCKS5 is enabled
Since HTTP module supports SOCKS5 now,
there is no reason not to request autoconfig XML
and outlook configuration anymore.
2024-08-09 15:06:27 +00:00
link2xt
4a2ebd0c81 feat: allow using OAuth 2 with SOCKS5
SOCKS5 for HTTP requests is supported since
fa198c3b5e
(PR <https://github.com/deltachat/deltachat-core-rust/pull/4017>)
2024-08-09 15:06:27 +00:00
link2xt
e701709645 chore(cargo): update iroh from 0.21 to 0.22 (#5860) 2024-08-09 14:06:22 +00:00
Daniel Kahn Gillmor
1ca835f34d Point to active Header Protection draft
The old draft was expired and abandoned, and the new draft should be
possible to generate cleanly without breaking compatibility with old
clients.
2024-08-09 15:53:24 +02:00
link2xt
1c021ae5ca ci: update Rust to 1.80.1 2024-08-09 07:49:05 +00:00
link2xt
479a4c2880 chore: update provider database 2024-08-09 03:34:58 +00:00
iequidoo
5ce44ade17 feat: Disable MDNs for bots by default
- To avoid receiving undecryptable MDNs by bots and replying to them if the bot's key changes.
- MDNs from bots don't look useful in general, usually the user expects some reply from the bot, not
  just that the message is read.
2024-08-09 00:11:47 -03:00
link2xt
f03ffa7641 refactor: pass address to moz_autoconfigure() instead of LoginParam 2024-08-08 00:15:08 +00:00
link2xt
b44185948d refactor: remove param_addr_urlencoded argument from get_autoconfig()
It can be calculated inside the function.
2024-08-08 00:15:08 +00:00
link2xt
6b4532a08e refactor: merge imap_certificate_checks and smtp_certificate_checks 2024-08-07 18:08:39 +00:00
iequidoo
86ad5506e3 feat: Always move outgoing auto-generated messages to the mvbox
Recently there are many questions on the Delta Chat forum why some unexpected encrypted messages
appear in Inbox. Seems they are mainly sync messages, though that also obviously happens to
SecureJoin messages. Anyway, regardless of the `MvboxMove` setting, auto-generated outgoing messages
should be moved to the DeltaChat folder so as not to complicate co-using Delta Chat with other MUAs.
2024-08-06 11:33:22 -03:00
iequidoo
6513349c09 feat: Add Config::FixIsChatmail
Add a config option preventing autoconfiguring `IsChatmail` for tests.
2024-08-06 11:33:22 -03:00
link2xt
92685189aa ci: update EmbarkStudios/cargo-deny-action action
v1 is not going to be updated to cargo-deny 0.16.0
because of breaking changes in cargo-deny.
2024-08-06 14:11:56 +00:00
link2xt
3b76622cf1 chore: fix typo s/webdxc/webxdc/ 2024-08-06 05:53:54 +00:00
link2xt
c5a524d3c6 refactor: derive Default for CertificateChecks 2024-08-06 02:32:55 +00:00
link2xt
17eb85b9cd build: downgrade Tokio to 1.38 to fix Android compilation 2024-08-05 17:10:11 +00:00
link2xt
3c688360fb chore(release): prepare for 1.142.3 2024-08-04 04:11:52 +00:00
link2xt
9f220768c2 build: do not disable "vendored" feature in the workspace
This fixes `nix build .#python-docs`
2024-08-04 03:17:42 +00:00
link2xt
fd183c6ee5 chore: remove direct "quinn" dependency 2024-08-04 02:31:42 +00:00
link2xt
9788fb16e8 chore(cargo): update rusqlite and libsqlite3-sys
SQLCipher does not allow passing empty key
since version v4.5.5,
so PRAGMA calls are wrapped into if's.
2024-08-03 23:02:08 +00:00
link2xt
39ed587959 Revert "chore(cargo): update rusqlite"
This reverts commit 1b92d18777.
2024-08-03 19:19:55 +00:00
link2xt
c4327a0558 Fix cargo warnings about default-features
Otherwise cargo emits these warnings:
warning: .../deltachat-core-rust/deltachat-ffi/Cargo.toml: `default-features` is ignored for deltachat, since `default-features` was not specified for `workspace.dependencies.deltachat`, this could become a hard error in the future
warning: .../deltachat-core-rust/deltachat-rpc-server/Cargo.toml: `default-features` is ignored for deltachat, since `default-features` was not specified for `workspace.dependencies.deltachat`, this could become a hard error in the future
warning: .../deltachat-core-rust/deltachat-rpc-server/Cargo.toml: `default-features` is ignored for deltachat-jsonrpc, since `default-features` was not specified for `workspace.dependencies.deltachat-jsonrpc`, this could become a hard error in the future
2024-08-03 19:08:47 +00:00
link2xt
1b92d18777 chore(cargo): update rusqlite 2024-08-03 19:08:29 +00:00
link2xt
a67503ae4a chore: remove backtrace dependency
It is not used directly by `deltachat` crate.
2024-08-02 23:06:30 +00:00
link2xt
c54f39bea0 chore: remove sha2 dependency
It is not used since ce6ec64069
2024-08-02 23:06:30 +00:00
dependabot[bot]
ff3138fa43 Merge pull request #5830 from deltachat/dependabot/cargo/env_logger-0.11.5 2024-08-02 21:39:16 +00:00
dependabot[bot]
09d46942ca Merge pull request #5832 from deltachat/dependabot/cargo/tokio-1.39.2 2024-08-02 19:51:34 +00:00
dependabot[bot]
84e365d263 Merge pull request #5833 from deltachat/dependabot/cargo/uuid-1.10.0 2024-08-02 19:50:28 +00:00
dependabot[bot]
b31bcf5561 Merge pull request #5836 from deltachat/dependabot/cargo/quick-xml-0.36.1 2024-08-02 19:43:48 +00:00
link2xt
da50d682e1 chore(release): prepare for 1.142.2 2024-08-02 17:05:43 +00:00
link2xt
094d310f5c feat: sort DNS results by successful connection timestamp (#5818) 2024-08-02 16:53:16 +00:00
dependabot[bot]
642eaf92d7 chore(cargo): bump serde from 1.0.203 to 1.0.204
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.203 to 1.0.204.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.203...v1.0.204)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 13:39:06 -03:00
link2xt
76c032a2c4 fix: reset configured_provider on reconfiguration 2024-08-02 16:31:07 +00:00
dependabot[bot]
a74b04d175 chore(cargo): bump quoted_printable from 0.5.0 to 0.5.1
Bumps [quoted_printable](https://github.com/staktrace/quoted-printable) from 0.5.0 to 0.5.1.
- [Commits](https://github.com/staktrace/quoted-printable/compare/v0.5.0...v0.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 13:14:21 -03:00
dependabot[bot]
c9448feafc chore(cargo): bump env_logger from 0.11.3 to 0.11.5
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.3 to 0.11.5.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.3...v0.11.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 15:51:02 +00:00
dependabot[bot]
8314f3e30c chore(cargo): bump syn from 2.0.68 to 2.0.72
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.68 to 2.0.72.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.68...2.0.72)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 12:45:02 -03:00
dependabot[bot]
935da2db49 Merge pull request #5838 from deltachat/dependabot/cargo/thiserror-1.0.63 2024-08-02 15:41:01 +00:00
dependabot[bot]
b5e95fa1ef chore(cargo): bump human-panic from 2.0.0 to 2.0.1
Bumps [human-panic](https://github.com/rust-cli/human-panic) from 2.0.0 to 2.0.1.
- [Changelog](https://github.com/rust-cli/human-panic/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/human-panic/compare/v2.0.0...v2.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 12:40:06 -03:00
dependabot[bot]
b60d8356cb chore(cargo): bump serde_json from 1.0.120 to 1.0.122
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.120 to 1.0.122.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.120...v1.0.122)

---
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-08-02 12:39:19 -03:00
link2xt
ee7a7a2f9d fix: fix compilation on iOS 2024-08-02 15:22:19 +00:00
dependabot[bot]
b5eb824346 Merge pull request #5835 from deltachat/dependabot/cargo/toml-0.8.15 2024-08-02 15:20:33 +00:00
dependabot[bot]
41867b89a0 chore(cargo): bump thiserror from 1.0.61 to 1.0.63
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.61 to 1.0.63.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.61...1.0.63)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 21:58:46 +00:00
dependabot[bot]
7e7aa7aba0 chore(cargo): bump quick-xml from 0.35.0 to 0.36.1
Bumps [quick-xml](https://github.com/tafia/quick-xml) from 0.35.0 to 0.36.1.
- [Release notes](https://github.com/tafia/quick-xml/releases)
- [Changelog](https://github.com/tafia/quick-xml/blob/master/Changelog.md)
- [Commits](https://github.com/tafia/quick-xml/compare/v0.35.0...v0.36.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 21:57:59 +00:00
dependabot[bot]
fd1dab7c7b chore(cargo): bump toml from 0.8.14 to 0.8.15
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.14 to 0.8.15.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.14...toml-v0.8.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 21:57:38 +00:00
dependabot[bot]
a69f9f01b3 chore(cargo): bump uuid from 1.9.1 to 1.10.0
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.9.1 to 1.10.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.9.1...1.10.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-08-01 21:56:35 +00:00
dependabot[bot]
c808ed1368 chore(cargo): bump tokio from 1.38.0 to 1.39.2
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.38.0 to 1.39.2.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.38.0...tokio-1.39.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 21:56:05 +00:00
link2xt
21be85071a feat: try only the full email address if username is unspecified
Previously Delta Chat tried to use local part of email address as well.
This configuration is very uncommon,
but trying it doubled the time of configuration try
in the worst case, e.g. when the password is typed in incorrectly.
2024-08-01 17:08:23 +00:00
iequidoo
a30c6ae1f7 refactor: Don't update message state to OutMdnRcvd anymore
This state can be computed from the `msgs_mdns` table without significant overhead as we have an
index by msg_id there.
2024-07-31 21:04:24 -03:00
link2xt
0324884124 build: use workspace dependencies to make cargo-deny 0.15.1 happy 2024-07-31 16:45:12 +00:00
link2xt
ad225b12c2 chore(cargo): update bytemuck from 0.14.3 to 0.16.3
Version 0.14.3 is yanked.
2024-07-31 05:20:09 +00:00
link2xt
0dd5e5ab7d fix: do not reset is_chatmail config on failed reconfiguration
Instead, always set it at the end of successful configuration.
2024-07-31 02:36:36 +00:00
link2xt
490f41cda8 fix: await the tasks after aborting them 2024-07-30 20:31:22 +00:00
link2xt
c163438eaf chore(release): prepare for 1.142.1 2024-07-30 15:40:06 +00:00
link2xt
ef925b0948 refactor: move DNS resolution into IMAP and SMTP connect code 2024-07-30 02:12:05 +00:00
link2xt
0fceb270ca fix: explicitly close the database on account removal 2024-07-30 00:22:03 +00:00
link2xt
4ec5d12213 refactor(imap): unify IMAP connection setup in Client::connect()
All functions like Client::connect_secure() are now private
and every new connection is established in Client::connect().
2024-07-29 15:16:40 +00:00
link2xt
d9c0e47581 refactor(smtp): unify SMTP connection setup between TLS and STARTTLS
Single function smtp::connect::connect_stream
returns a stream of a single `dyn` type
that can be a TLS, STARTTLS or plaintext
connection established over SOCKS5 or directly.
2024-07-29 15:16:40 +00:00
link2xt
8ec4a8ad46 refactor: replace {IMAP,SMTP,HTTP}_TIMEOUT with a single constant
This change also increases HTTP timeout from 30 seconds to 60 seconds.
2024-07-29 15:16:40 +00:00
link2xt
40d355209b refactor: pass single ALPN around instead of ALPN list
This way there is always exactly one ALPN ("imap" or "smtp").
2024-07-29 15:16:40 +00:00
iequidoo
354702fcab fix: imex::import_backup: Ignore errors from delete_and_reset_all_device_msgs()
They are not a good reason to fail the whole import. Anyway `delete_and_reset_all_device_msgs()`
isn't retried after restarting the program.
2024-07-28 12:51:25 -03:00
iequidoo
bfc7ae1eff fix: Sql::import: Detach backup db if any step of the import fails
Otherwise we continue to work with an incompletely imported db... but only until restart -- after
that all changes to the db are lost.
2024-07-28 12:51:25 -03:00
iequidoo
cccefe15b3 fix: import_backup_stream: Fix progress stucking at 0
Fix the progress calculation, before `total_size.checked_div(file_size)` was giving 0 if `total_size
< file_size`.
2024-07-28 12:51:25 -03:00
iequidoo
bb4236ffed fix: imex::import_backup: Unpack all blobs before importing a db (#4307)
This way we can't get an account with missing blobs if there's not enough disk space.

Also delete already unpacked files if all files weren't unpacked successfully. Still, there are some
minor problems remaining:
- If a db wasn't imported successfully, unpacked blobs aren't deleted because we don't know at which
  step the import failed and whether the db will reference the blobs after restart.
- If `delete_and_reset_all_device_msgs()` fails, the whole `import_backup()` fails also, but after a
  restart delete_and_reset_all_device_msgs() isn't retried. Probably errors from it should be
  ignored at all.
2024-07-28 12:51:25 -03:00
link2xt
14d57e780b feat: report first error instead of the last on connection failure
First result has higher priority
as it is the one prioritized by DNS
or used recently, while the last
tried server may never work at all.
2024-07-27 23:00:05 +00:00
link2xt
76a43c8de6 feat: try next DNS resolution result if TLS setup fails
Previously Delta Chat tried all DNS resolution results
in sequence until TCP connection is established successfully,
then tried to establish TLS on top of the TCP connection.
If establishing TLS fails, the whole
connection establishment procedure failed
without trying next DNS resolution results.

In particular, in a scenario
where DNS returns incorrect result
pointing e.g. to a server
that listens on the TCP port
but does not have correpsponding TLS certificate,
Delta Chat now will fall back to the cached result
and connect successfully.
2024-07-27 23:00:05 +00:00
link2xt
b807435c42 refactor: add net/dns submodule 2024-07-27 23:00:05 +00:00
link2xt
3b040fd4b5 ci: update Rust to 1.80.0 2024-07-26 23:57:21 +00:00
link2xt
b9b9ed197e chore(cargo): update iroh from 0.20.0 to 0.21.0 2024-07-26 23:07:59 +00:00
bjoern
03523ab589 feat: do not reveal sender's language in read receipts (#5802)
while adapting strings for the recent change about read receipts,
https://github.com/deltachat/deltachat-core-rust/pull/5712 , it turns
out in discussions eg. at
https://github.com/deltachat/deltachat-android/issues/3179 that
untranslated english for the read receipts seem to be sufficient or even
better:

- do not reveal the sender's language
- unexpected languages are confusing - even if you chat in english, you
may get Chinese read receipts
- many clients do not show the text anyways, iirc, eg. Outlook display
the read receipts in context, and Delta Chat of course as well
- afaik, we're leaving comparable `multipart/report` untranslated as
well (sync, but also webxdc updates are practically english only)
- less code, fewer translations needed :)
2024-07-26 21:07:30 +02:00
link2xt
c4efe59a12 chore(cargo): update time from 0.3.34 to 0.3.36 2024-07-26 16:21:20 +00:00
link2xt
d46f53a004 fix(smtp): use DNS cache for implicit TLS connections
load_cache argument to connect_tcp() should only be false
if strict TLS checks are disabled or TLS is not used.
2024-07-25 01:25:49 +00:00
link2xt
5fb5fd4318 chore(release): prepare for 1.142.0 2024-07-23 05:02:53 +00:00
link2xt
a3cb58484f feat: use [...] for protected subject
This subject is going to be standardized in
<https://datatracker.ietf.org/doc/draft-ietf-lamps-header-protection/>
and is already used in K-9 Mail:
<https://github.com/thunderbird/thunderbird-android/pull/8014>
2024-07-23 04:50:40 +00:00
iequidoo
04fd2cdcab fix: Reject message with forged From even if no valid signatures are found
There are many reasons why we may fail to find valid signatures in a message, e.g. we don't yet know
a public key attached in the same message, anyway, if From is forged, the message must be rejected.

Also always take the displayname from encrypted From, even if no valid signatures are found.
2024-07-22 20:22:46 -03:00
link2xt
a710c034e4 feat: do not show the address in invite QR code SVG
Addresses take space and sometimes
do not fit. We generally want
to deemphasize addresses in the UI,
especially randomized chatmail
addresses.
2024-07-22 22:39:48 +00:00
iequidoo
bd651d9ef3 feat: Set summary thumbnail path for WebXDCs to "webxdc-icon://last-msg-id" (#5782)
This is a hint for apps that a WebXDC icon should be shown in the summary, e.g. in the
chatlist. Otherwise it's not clear when it should be shown, e.g. it shouldn't be shown in a reaction
summary.
2024-07-22 18:25:15 -03:00
link2xt
7f3e8f9796 feat: promote fallback DNS results to cached on successful use
For hardcoded built-in DNS results
there is no cache entry in `dns_cache` table
so they cannot be prioritized if DNS resolution
never returned these results yet.
If there is no entry, a new one should be created.
SQL UPSERT does this.
2024-07-22 20:08:07 +00:00
link2xt
837311abce chore(cargo): update image crate to 0.25.2
This version deprecated `image::io::Reader`,
requires changes to avoid warnings.
2024-07-22 20:02:58 +00:00
iequidoo
c596ee0256 fix: Emit MsgsChanged if the number of unnoticed archived chats could decrease (#5768)
Follow-up to 3cf78749df "Emit DC_EVENT_MSGS_CHANGED for
DC_CHAT_ID_ARCHIVED_LINK when the number of archived chats with unread messages increases (#3940)".

In general we don't want to make an extra db query to know if a noticied chat is
archived. Emitting events should be cheap, better to allow false-positive `MsgsChanged` events.
2024-07-22 16:26:20 -03:00
dependabot[bot]
5815d8f1dd chore(deps): bump openssl from 0.10.60 to 0.10.66 in /fuzz
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.60 to 0.10.66.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.60...openssl-v0.10.66)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-22 16:06:18 -03:00
iequidoo
2675e7b2e1 chore(cargo): Update openssl to v0.10.66 2024-07-22 11:24:29 +00:00
link2xt
8f400dda85 feat: use custom DNS resolver for HTTP(S) 2024-07-21 23:04:53 +00:00
link2xt
2a605b93cd test: add test for get_http_response JSON-RPC call 2024-07-21 23:04:53 +00:00
iequidoo
e4d65b2f3b fix: Call send_sync_msg() only from the SMTP loop (#5780)
`Context::send_sync_msg()` mustn't be called from multiple tasks in parallel to avoid sending the
same sync items twice because sync items are removed from the db only after successful
sending. Let's guarantee this by calling `send_sync_msg()` only from the SMTP loop. Before
`send_sync_msg()` could be called in parallel from the SMTP loop and another task doing
e.g. `chat::sync()` which led to `test_multidevice_sync_chat` being flaky because of events
triggered by duplicated sync messages.
2024-07-21 12:10:06 -03:00
link2xt
87a45e88dc fix: correct copy-pasted DCACCOUNT parsing errors message
Apparently error message was copy-pasted from DCWEBRTC handling code.
2024-07-20 12:26:13 +00:00
link2xt
d6d90db957 feat: new BACKUP2 transfer protocol
New protocol streams .tar into iroh-net
stream without traversing all the files first.
Reception over old backup protocol
is still supported to allow
transferring backups from old devices
to new ones, but not vice versa.
2024-07-19 03:16:57 +00:00
iequidoo
eb669afb8f feat: Don't unarchive a group on a member removal except SELF (#5618) 2024-07-17 17:25:10 -03:00
iequidoo
d1cf80001e feat: Don't create ad-hoc group on a member removal message (#5618)
The "Chat-Group-Member-Removed" header is added to ad-hoc group messages as well, so we should check
for its presense before creating an ad-hoc group as we do for DC-style groups.
2024-07-17 17:25:10 -03:00
link2xt
307d11f503 api(deltachat-jsonrpc): add pinned property to FullChat and BasicChat 2024-07-17 10:12:39 +00:00
link2xt
73f527e772 fix: randomize avatar blob filenames to work around caching 2024-07-15 22:48:39 +00:00
iequidoo
5143ebece1 refactor: Reduce boilerplate for migration version increment 2024-07-15 15:39:10 -03:00
iequidoo
9996c2db80 feat: Limit the size of aggregated WebXDC update to 100 KiB (#4825)
Before, update sending might be delayed due to rate limits and later merged into large
messages. This is undesirable for apps that want to send large files over WebXDC updates because the
message with aggregated update may be too large for actual sending and hit the provider limit or
require multiple attempts on a flaky SMTP connection.

So, don't aggregate updates if the size of an aggregated update will exceed the limit of 100
KiB. This is a soft limit, so it may be exceeded if a single update is larger and it limits only the
update JSON size, so the message with all envelopes still may be larger. Also the limit may be
exceeded when updates are sent together with the WebXDC instance when resending it as the instance
size isn't accounted to not complicate the code. At least this is not worse than the previous
behaviour when all updates were attached.
2024-07-13 16:24:44 -03:00
link2xt
0f26da4028 feat(iroh): pass direct addresses from Endpoint to Gossip 2024-07-13 14:36:35 +00:00
iequidoo
a3dd37b011 feat(jsonrpc): Allow to set message quote text without referencing quoted message (#5695)
Bridge bots like matterdelta need to set a quoted text without referencing the quoted message, this
makes easier bridging messages from other platforms to Delta Chat or even bridging Delta Chat groups
in different accounts where you can not set a quoted message by the message id from another account.
2024-07-12 15:08:45 -03:00
iequidoo
6b11b0ea8d fix: Message::set_quote: Don't forget to remove Param::ProtectQuote 2024-07-12 15:08:45 -03:00
link2xt
faad7d5843 feat: request smtp ALPN for SMTP TLS connections
Even though SMTP ALPN is not officially registered (unlike IMAP),
it is an obvious choice that will allow
to multiplex SMTP and other protocols on the same TLS port.
2024-07-11 20:57:45 +00:00
link2xt
ef0d6d0c90 build(node): pin node-gyp to version 10.1
Newer node-gyp uses newer gyp
which requires newer python
that is not available in Debian 10
container that is
used for building node prebuilds
with old glibc.
2024-07-11 08:59:26 +00:00
link2xt
bd83fb3d38 feat: set imap ALPN when connecting to IMAP servers
IMAP has a registered protocol ID
listed at <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>

Requesting specific ALPN on the client
should allow the server to
multiplex multiple protocols on the same
port and dispatch
requests to the correct backend on the proxy such as HAProxy.
2024-07-11 05:28:32 +00:00
link2xt
f84e603318 refactor: return error from export_backup() without logging
The error is logged by `imex` caller.
2024-07-10 21:14:07 +00:00
link2xt
d77459e4fc refactor: move TempPathGuard into tools and use instead of DeleteOnDrop 2024-07-10 20:13:42 +00:00
link2xt
2c14bd353f refactor: move key transfer into its own submodule
`create_setup_code` and `render_setup_file`
are now hidden from public API,
so deltachat-repl does not have "export-setup"
debug command anymore.
2024-07-10 04:29:10 +00:00
iequidoo
0860508a1d feat: Contact::create_ex: Don't send sync message if nothing changed (#5705)
Follow-up to 5fa7cff46. Let's still not send a sync message if the contact wasn't modified. This is
not very important, but just for consistency with the `chat::rename_ex()` behaviour.
2024-07-10 00:14:23 -03:00
iequidoo
f81daa16b3 feat: Add email address and fingerprint to exported key file names (#5694)
This way it's clearer which key is which and also adding the key fingerprint to the file name avoids
overwriting another previously exported key. I think this is better than adding an incremental
number as we do for backups, there's no need to export a key several times to different files.
2024-07-10 00:13:02 -03:00
iequidoo
436b00e3cb feat: Report better error from DcKey::from_asc() (#5539)
If no matching key packet was found, report which key is needed to make it clear to the user.
2024-07-09 21:38:20 -03:00
link2xt
4d52aa8b7f chore(cargo): update hashlink to remove allocator-api2 dependency 2024-07-09 23:17:21 +00:00
dignifiedquire
c2d5488663 fix: only add node addrs with actual information 2024-07-09 22:11:30 +00:00
link2xt
cc51d51a78 chore(cargo): update iroh from 0.17 to 0.20 2024-07-09 22:11:30 +00:00
link2xt
7f1068e37e chore(release): prepare for 1.141.2 2024-07-09 17:12:59 +00:00
B. Petersen
81777fac47 feat: add is_muted config option 2024-07-09 17:04:14 +00:00
iequidoo
9a6147b643 fix: MimeFactory::verified: Return true for self-chat
For purposes of building a message it's better to consider the self-chat as verified. Particularly,
this removes unencrypted name from the "From" header.
2024-07-08 23:52:13 -03:00
link2xt
a2dacc333c fix: distinguish between database errors and no gossip topic 2024-07-09 02:37:48 +00:00
link2xt
088008a030 chore(cargo): update rPGP from 0.11 to 0.13 2024-07-09 01:32:38 +00:00
link2xt
a198e9fce8 chore(cargo): update yerpc to 0.6.2 2024-07-06 16:08:35 +00:00
iequidoo
3f087e5fb1 fix: Use and prefer Date from signed message part (#5716) 2024-07-04 15:38:23 -03:00
dependabot[bot]
5beb4a5f27 chore(cargo): bump quick-xml from 0.31.0 to 0.35.0
Bumps [quick-xml](https://github.com/tafia/quick-xml) from 0.31.0 to 0.35.0.
- [Release notes](https://github.com/tafia/quick-xml/releases)
- [Changelog](https://github.com/tafia/quick-xml/blob/master/Changelog.md)
- [Commits](https://github.com/tafia/quick-xml/compare/v0.31.0...v0.35.0)

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

Co-authored-by: iequidoo <dgreshilov@gmail.com>
2024-07-02 18:52:29 -03:00
dependabot[bot]
ba7eaca762 Merge pull request #5743 from deltachat/dependabot/cargo/backtrace-0.3.73 2024-07-02 03:08:39 +00:00
dependabot[bot]
d31f897f9e chore(cargo): bump uuid from 1.8.0 to 1.9.1
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.8.0 to 1.9.1.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.8.0...1.9.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-02 02:58:07 +00:00
dependabot[bot]
e60598bafd chore(cargo): bump backtrace from 0.3.72 to 0.3.73
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.72 to 0.3.73.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.72...0.3.73)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-02 02:46:39 +00:00
dependabot[bot]
df29767fc7 Merge pull request #5733 from deltachat/dependabot/cargo/proptest-1.5.0 2024-07-02 02:09:17 +00:00
dependabot[bot]
e58a1a2aad Merge pull request #5747 from deltachat/dependabot/cargo/regex-1.10.5 2024-07-02 01:44:39 +00:00
dependabot[bot]
74f98e2b79 Merge pull request #5735 from deltachat/dependabot/cargo/log-0.4.22 2024-07-02 01:44:14 +00:00
dependabot[bot]
c4cfde3c4c chore(cargo): bump url from 2.5.0 to 2.5.2
Bumps [url](https://github.com/servo/rust-url) from 2.5.0 to 2.5.2.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v2.5.0...v2.5.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-02 00:48:26 +00:00
link2xt
5792d7b18d fix(imap): reset new_mail if folder is ignored
This prevents skipping IDLE in infinite loop
if folder is not fetched.
This happens on the INBOX
when OnlyFetchMvbox setting is enabled.
2024-07-02 00:47:52 +00:00
iequidoo
5fa7cff468 feat: Disable sending sync messages for bots (#5705)
If currently there are no multi-device bots, let's disable sync messages for bots at all. Another
option is to auto-disable sync messages when `Config::Bot` is set, so sync messages can be reenabled
if needed. But let's leave this option for the future.
2024-07-01 21:30:02 -03:00
dependabot[bot]
a76a2715ad Merge pull request #5738 from deltachat/dependabot/cargo/async-broadcast-0.7.1 2024-07-02 00:29:04 +00:00
dependabot[bot]
2d2a61f7df chore(cargo): bump regex from 1.10.4 to 1.10.5
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.4 to 1.10.5.
- [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.4...1.10.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-02 00:26:15 +00:00
dependabot[bot]
9f963c0b61 Merge pull request #5740 from deltachat/dependabot/cargo/syn-2.0.68 2024-07-02 00:25:10 +00:00
dependabot[bot]
69595a6bb4 Merge pull request #5734 from deltachat/dependabot/cargo/serde_json-1.0.120 2024-07-02 00:20:54 +00:00
dependabot[bot]
bbac5a499a Merge pull request #5732 from deltachat/dependabot/cargo/toml-0.8.14 2024-07-02 00:19:30 +00:00
dependabot[bot]
1b241b62f3 chore(cargo): bump syn from 2.0.66 to 2.0.68
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.66 to 2.0.68.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.66...2.0.68)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 21:57:22 +00:00
dependabot[bot]
1f36595d19 chore(cargo): bump async-broadcast from 0.7.0 to 0.7.1
Bumps [async-broadcast](https://github.com/smol-rs/async-broadcast) from 0.7.0 to 0.7.1.
- [Release notes](https://github.com/smol-rs/async-broadcast/releases)
- [Changelog](https://github.com/smol-rs/async-broadcast/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-broadcast/compare/0.7.0...v0.7.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 21:56:32 +00:00
dependabot[bot]
e8c0f85016 chore(cargo): bump log from 0.4.21 to 0.4.22
Bumps [log](https://github.com/rust-lang/log) from 0.4.21 to 0.4.22.
- [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.21...0.4.22)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 21:55:24 +00:00
dependabot[bot]
2dbddef5e9 chore(cargo): bump serde_json from 1.0.117 to 1.0.120
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.117 to 1.0.120.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.117...v1.0.120)

---
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-07-01 21:55:07 +00:00
dependabot[bot]
4a34ae5cdc chore(cargo): bump proptest from 1.4.0 to 1.5.0
Bumps [proptest](https://github.com/proptest-rs/proptest) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/proptest-rs/proptest/releases)
- [Changelog](https://github.com/proptest-rs/proptest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/proptest-rs/proptest/compare/v1.4.0...v1.5.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 21:54:49 +00:00
dependabot[bot]
b2ad958340 chore(cargo): bump toml from 0.8.13 to 0.8.14
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.13 to 0.8.14.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.13...toml-v0.8.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 21:54:31 +00:00
Hocuri
53217d5eb8 chore: Remove two TODOs that are not worth fixing (#5726)
About the first TODO: I tried this out, but it didn't actually improve
things, for two reasons:
1. The trick with `#![cfg_attr(not(test),
warn(clippy::indexing_slicing))]` that enables the lint everywhere
except for tests doesn't work with workspace-wide lints. (Context: We
want to lint against indexing because it might panic, but in a test
panicking is fine, so we don't want to enable the lint in tests).
2. Most of our crates have different sets of lints right now, so it
would only be very few crates that use the workspace-wide list of lints.

About the second TODO:
It's not feasible right now to fully parse vCards, and for our
good-enough parser the current behavior is fine, I think. If we fail to
parse some realworld vCards because of this, we can still improve it.
2024-07-01 18:28:06 +00:00
link2xt
7a5dca2645 fix: do not try to register non-iOS tokens for heartbeats
Notification server uses APNS server
for heartbeat notifications,
so registering FCM tokens there
will result in failing to notify them
and unregistering them anyway.
2024-07-01 18:09:15 +00:00
iequidoo
170cbb6635 refactor: Move quota_needs_update calculation to a separate function (#5683)
And add a unit test for this function. At least this way we protect from the recently fixed bug when
a wrong comparison operator was used.
2024-06-30 11:37:42 -03:00
Hocuri
ee2fffb52b feat: Parse vcards exported by protonmail (#5723) 2024-06-29 09:45:51 +02:00
Hocuri
68b62392bf Document vCards in the specification (#5724)
Also, move the `Miscellaneous` section to the end again and update the
table of contents with https://derlin.github.io/bitdowntoc/.
2024-06-29 09:44:51 +02:00
iequidoo
222e1ce4a6 refactor: Protect from reusing migration versions (#5719)
It's possible that when rebasing a PR adding a migration a merge-conflict doesn't occur if another
migration was added in the target branch. Better to have at least runtime checks that the migration
version is correct. Looks like compile-time checks are not possible because Rust doesn't allow to
redefine constants, only vars.
2024-06-28 20:52:01 -03:00
Hocuri
ac198b17bf fix: Correctly sanitize input everywhere (#5697)
Best reviewed commit-by-commit; the commit messages explain what is
done.
2024-06-28 14:36:09 +02:00
iequidoo
4ed9c04e9b refactor: MimeFactory::is_e2ee_guaranteed(): always respect Param::ForcePlaintext
Even if a chat is protected, `Param::ForcePlaintext` in fact disables e2ee. Reflect this behaviour
in `MimeFactory::is_e2ee_guaranteed()`.
2024-06-27 15:41:55 -03:00
iequidoo
ce44312ac0 fix: Don't fail if going to send plaintext, but some peerstate is missing
F.e. this allows to reexecute Securejoin and fix the problem.
2024-06-27 15:41:55 -03:00
link2xt
71104e9312 chore(release): prepare for 1.141.1 2024-06-27 15:11:19 +00:00
link2xt
ced5f51482 refactor: improve logging during SMTP/IMAP configuration 2024-06-27 15:11:19 +00:00
link2xt
c400491c07 fix(sql): assign migration adding msgs.deleted a new number 2024-06-27 15:11:19 +00:00
iequidoo
72a1406b86 fix: Update quota if it's stale, not fresh (#5683) 2024-06-26 13:52:01 -03:00
link2xt
11e13d1873 refactor(mimefactory): factor out header confidentiality policy (#5715)
Instead of constructing lists of protected,
unprotected and hidden headers,
construct a single list of headers
and then sort them into separate lists
based on the well-defined policy.

This also fixes the bug
where Subject was not present in the IMF header
for signed-only messages.

Closes #5713
2024-06-26 16:39:04 +00:00
link2xt
6607b7fd62 chore(release): prepare for 1.141.0 2024-06-24 21:03:24 +00:00
link2xt
8d862b5ad3 chore: update provider database 2024-06-24 20:58:46 +00:00
iequidoo
d40ec88b94 test(python): Wait for bot's DC_EVENT_IMAP_INBOX_IDLE before sending messages to it (#5699)
Bot processes are run asynchronously, so we shouldn't send messages to a bot before it's fully
initialised and skipped existing messages for processing, i.e. before DC_EVENT_IMAP_INBOX_IDLE is
emitted.
2024-06-23 01:46:40 -03:00
link2xt
a82eb7def6 fix: do not require the Message to render MDN 2024-06-23 04:25:19 +00:00
B. Petersen
92e8b80da8 docs: remove misleading configuration comment
we're always checking the configuration encrypted.
saying it is 'preferred' encrypted is misleading,
therfore, just remove it.
i do not think, it is worth saying that we do not query 'http',
this is clear from the source code.

moreover, fix two typos.
2024-06-22 16:18:36 +02:00
link2xt
76a84ec9b1 refactor: store public key instead of secret key for peer channels
We only need public key, so there is no need to derive
it from secret key every time.
2024-06-21 22:31:21 +00:00
iequidoo
7109692791 feat: Don't reveal profile data in MDNs (#5166)
Looks like it has no sense to send any profile data (From/To names, self-status; self-avatar was
never sent even before) in MDNs, they aren't normal messages and aren't seen in a MUA. Better not to
reveal profile data to the network and even to contacts in MDNs and make them more lightweight.
2024-06-21 16:35:24 -03:00
iequidoo
7ad3c70b68 feat: Don't reveal profile data to a not yet verified contact (#5166)
Follow-up to b771311593. Since that commit names are not revealed in
verified chats, but during verification (i.e. SecureJoin) they are still sent unencrypted. Moreover,
all profile data mustn't be sent even encrypted before the contact verification, i.e. before
"v{c,g}-request-with-auth". That was done for the selfavatar in
304e902fce, now it's done for From/To names and the self-status as
well. Moreover, "v{c,g}-request" and "v{c,g}-auth-required" messages are deleted right after
processing, so other devices won't see the received profile data anyway.
2024-06-21 16:35:24 -03:00
iequidoo
0b20f69959 fix: Don't generate Config sync messages for unconfigured accounts
Probably sync messages generated for a not yet configured account are useless because there are no
other devices yet. And even if this is not true, we don't want to depend on the order of setting
`Config::SyncMsgs` and other keys. Also w/o this the Python tests don't work if we start syncing
`Config::MvboxMove` because they don't expect that sync messages are sent while configuring
accounts.
2024-06-21 12:53:40 -03:00
iequidoo
be0ebc7847 feat: Sync Config::MvboxMove across devices (#5680)
NB: We don't restart IO from the synchronisation code, so `MvboxMove` isn't effective immediately if
`ConfiguredMvboxFolder` is unset, but only after a reconnect to IMAP.
2024-06-21 12:53:40 -03:00
link2xt
b5e2ded47a Revert "test: Set fetch_existing_msgs for bots (#4976)"
This reverts commit 25b8a482bc.
2024-06-20 02:00:49 +00:00
link2xt
8953c2a7de fix: do not send sync messages if bcc_self is disabled 2024-06-19 22:11:56 +00:00
iequidoo
13f58e0ca5 fix: Delete user-deleted messages on the server even if they show up on IMAP later
Before, if the user deleted a message too quickly after sending, it was deleted only locally. The
fix is to remember for tombstones that the corresponding message should be deleted on the server
too.
2024-06-19 18:41:24 -03:00
iequidoo
f436e915d3 fix: housekeeping: Delete MDNs and webxdc status updates for tombstones 2024-06-19 18:41:24 -03:00
iequidoo
72bfae9448 fix: Keep tombstones for two days before deleting (#3685)
This is a way to prevent redownloading locally deleted messages. Otherwise if a message is deleted
quickly after sending and `bcc_self` is configured, the BCC copy is downloaded and appears as a new
message as it happens for messages sent from another device.
2024-06-19 18:41:24 -03:00
link2xt
6aaed3b524 chore: update curve25519-dalek 4.1.x and suppress 3.2.0 warning 2024-06-19 16:34:54 +00:00
iequidoo
501f41fca1 feat: Replace "Unnamed group" with "👥📧" to avoid translation 2024-06-19 13:14:09 -03:00
iequidoo
06d80e5da3 feat: Remove subject prefix from ad-hoc group names (#5385)
Delta Chat -style groups have names w/o prefixes like "Re: " even if the user is added to an already
existing group, so let's remove prefixes from ad-hoc group names too. Usually it's not very
important that the group is a classic email thread existed before, this info just eats up screen
space. Also this way a group name is likely to preserve if the first message was missed.
2024-06-19 13:14:09 -03:00
link2xt
8ddc05923b api!(deltachat-rpc-client): make {Account,Chat}.get_qr_code() return no SVG
This is a breaking change, old method is renamed into `get_qr_code_svg()`.
2024-06-19 13:29:44 +00:00
link2xt
9cbc9bf2bc api(deltachat-jsonrpc): add get_chat_securejoin_qr_code()
New method is the same as `get_chat_securejoin_qr_code_svg()`,
but does not generate SVG.
2024-06-19 13:29:44 +00:00
link2xt
5489b49cc1 test(deltachat-rpc-client): test that webxdc realtime data is not reordered on the sender 2024-06-18 18:06:44 +00:00
iequidoo
f6f4ccc6ea feat: Case-insensitive search for non-ASCII messages (#5052)
SQLite search with `LIKE` is case-insensitive only for ASCII chars. To make it case-insensitive for
all messages, create a new column `msgs.txt_normalized` defaulting to `NULL` (so we do not bump up
the database size in a migration) and storing lowercased/normalized text there when the row is
created/updated. When doing a search, search over `IFNULL(txt_normalized, txt)`.
2024-06-17 17:45:39 -03:00
Hocuri
a5d14b377d refactor: Deduplicate dependency versions (#5691)
Deduplicate dependency versions by specifying them only once in
Cargo.toml for the whole workspace under `[workspace.dependencies]`.
2024-06-17 07:51:54 +00:00
iequidoo
3b91815240 test(python): Set delete_server_after=1 ("delete immediately") for bots (#4976)
Test bots are run with `fetch_existing_msgs` set, so messages must be deleted immediately not to be
processed again after a bot redeployment.
2024-06-16 22:16:33 -03:00
iequidoo
aa30afbeda fix: Fetch existing messages for bots as InFresh (#4976)
Before, if `Config::FetchExistingMsgs` is set, existing messages were received with the `InSeen`
state set, but for bots they must be `InFresh` and also `IncomingMsg` events should be emitted for
them so that they are processed by bots as it happens with new messages.
2024-06-16 22:16:33 -03:00
link2xt
bdc2c8f456 ci: update Rust to 1.79.0 2024-06-13 20:36:31 +00:00
iequidoo
37831f82a4 feat: Display vCard contact name in the message summary 2024-06-12 13:10:34 -03:00
iequidoo
4049d3451a test: Image drafted as Viewtype::File is sent as is 2024-06-12 12:19:28 -03:00
link2xt
6614864d78 docs: remove outdated documentation comment from send_smtp_messages
Since commit c0a17df344
(PR https://github.com/deltachat/deltachat-core-rust/pull/3402)
`send_smtp_messages` returns an error
as soon as it encounters the first message it failed to send.

Since this worked like this for about 2 years
without any problems, there is no need to revert the change,
but outdated comment should be removed.
2024-06-11 19:14:56 +00:00
Septias
b771311593 feat: Protect From name for verified chats and To names for encrypted chats (#5166)
If a display name should be protected (i.e. opportunistically encrypted), only put the corresponding
address to the unprotected headers. We protect the From display name only for verified chats,
otherwise this would be incompatible with Thunderbird and K-9 who don't use display names from the
encrypted part. Still, we always protect To display names as compatibility seems less critical here.

When receiving a messge, overwrite the From display name but not the whole From field as that would
allow From forgery. For the To field we don't really care. Anyway as soon as we receive a message
from the user, the display name will be corrected.

Co-authored-by: iequidoo <dgreshilov@gmail.com>
2024-06-10 12:21:54 -03:00
iequidoo
78fe2beefb feat: Prefer references to fully downloaded messages for chat assignment (#5645) 2024-06-09 22:12:28 -03:00
link2xt
6a3902d90d chore(release): prepare for 1.140.2 2024-06-07 22:22:27 +00:00
Simon Laux
d412887bf4 refactor(@deltachat/stdio-rpc-server): use old school require instead of the experimental json import (#5628)
to get rid of warning. Should also make it possible to use nodejs versions older than 20.11.
2024-06-07 21:34:12 +00:00
Simon Laux
9c2526bbdd fix(@deltachat/stdio-rpc-server): make local non-symlinked installation possible by using absolute paths for local dev version (#5679)
this fixes the local non-symlinked (copied) instalation with `npm i
--install-links=true` possible

I probably need this for flatpak building.
2024-06-07 21:32:57 +00:00
iequidoo
889b947792 api(jsonrpc): Add set_draft_vcard(.., msg_id, contacts)
Add a function setting a vCard containing the given contacts to the message draft. This should
simplify sending contacts as vCards for apps.
2024-06-06 16:14:47 -03:00
iequidoo
0a0e7156e0 fix: Revert member addition if the corresponding message couldn't be sent (#5508) 2024-06-06 11:53:53 -03:00
iequidoo
24a06d175e fix: Remove group member locally even if send_msg() fails (#5508)
Otherwise it's impossible to remove a member with missing key from a protected group. In the worst
case a removed member will be added back due to the group membership consistency algo.
2024-06-06 11:53:53 -03:00
iequidoo
980bab3040 test: Don't leave protected group if some member's key is missing (#5508)
The "I left the group" message can't be sent to a protected group if some member's key is missing,
in this case we should remain in the group. The problem should be fixed first, then the user may
retry to leave the group.
2024-06-06 11:53:53 -03:00
dependabot[bot]
b6dceb4271 chore(cargo): bump backtrace from 0.3.71 to 0.3.72
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.71 to 0.3.72.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.71...0.3.72)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-06 00:14:10 +00:00
iequidoo
87a57cd63b fix: Allow fetch_existing_msgs for bots (#4976)
There was a comment in `fetch_existing_msgs()`: "Bots don't want those messages". If a bot doesn't
want this setting, why enable it? It's disabled by default anyway.
2024-06-05 21:11:50 -03:00
iequidoo
25b8a482bc test: Set fetch_existing_msgs for bots (#4976)
A bot process is run asynchronously, so some messages can arrive before the bot is fully
initialised.
2024-06-05 18:27:20 -03:00
dependabot[bot]
d7dd563df4 chore(cargo): bump schemars from 0.8.19 to 0.8.21
Bumps [schemars](https://github.com/GREsau/schemars) from 0.8.19 to 0.8.21.
- [Release notes](https://github.com/GREsau/schemars/releases)
- [Changelog](https://github.com/GREsau/schemars/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GREsau/schemars/compare/v0.8.19...v0.8.21)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 19:42:09 +00:00
link2xt
6d720b793d chore(release): prepare for 1.140.1 2024-06-05 19:07:26 +00:00
dependabot[bot]
6cc3e0a19a chore(cargo): bump libc from 0.2.153 to 0.2.155
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.153 to 0.2.155.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.153...0.2.155)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 18:40:19 +00:00
link2xt
380116d107 fix: do not miss new messages while expunging the folder
This should fix flaky `test_verified_group_vs_delete_server_after`.
2024-06-05 18:15:23 +00:00
link2xt
216b295f52 docs(imap): document why CLOSE is faster than EXPUNGE 2024-06-05 18:15:23 +00:00
link2xt
388980ed6c refactor: remove unused select_folder::Error variants 2024-06-05 18:15:23 +00:00
dependabot[bot]
2a2983ace0 chore(cargo): bump serde_json from 1.0.116 to 1.0.117
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.116 to 1.0.117.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.116...v1.0.117)

---
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-06-05 15:03:39 -03:00
dependabot[bot]
a7f56e164e chore(cargo): bump num-traits from 0.2.18 to 0.2.19
Bumps [num-traits](https://github.com/rust-num/num-traits) from 0.2.18 to 0.2.19.
- [Changelog](https://github.com/rust-num/num-traits/blob/master/RELEASES.md)
- [Commits](https://github.com/rust-num/num-traits/compare/num-traits-0.2.18...num-traits-0.2.19)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 17:01:23 +00:00
dependabot[bot]
db4183596c chore(cargo): bump tokio-util from 0.7.10 to 0.7.11
Bumps [tokio-util](https://github.com/tokio-rs/tokio) from 0.7.10 to 0.7.11.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-util-0.7.10...tokio-util-0.7.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 17:00:30 +00:00
dependabot[bot]
2b06e672de chore(cargo): bump serde from 1.0.200 to 1.0.203
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.200 to 1.0.203.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.200...v1.0.203)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 15:18:44 +00:00
link2xt
e596664753 fix: log messages with info! instead of println! 2024-06-05 13:16:21 +00:00
link2xt
79d1c96db4 refactor: improve SMTP logs and errors 2024-06-05 13:16:21 +00:00
dependabot[bot]
cc7c235556 chore(cargo): bump tokio from 1.37.0 to 1.38.0
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.37.0 to 1.38.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.37.0...tokio-1.38.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-06-05 11:24:10 +00:00
dependabot[bot]
56960882ce chore(cargo): bump async-channel from 2.2.1 to 2.3.1
Bumps [async-channel](https://github.com/smol-rs/async-channel) from 2.2.1 to 2.3.1.
- [Release notes](https://github.com/smol-rs/async-channel/releases)
- [Changelog](https://github.com/smol-rs/async-channel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-channel/compare/v2.2.1...v2.3.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 11:23:48 +00:00
dependabot[bot]
b11c2c6cc5 chore(cargo): bump thiserror from 1.0.59 to 1.0.61
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.59 to 1.0.61.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.59...1.0.61)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 11:22:17 +00:00
dependabot[bot]
12e0a1962d chore(cargo): bump parking_lot from 0.12.2 to 0.12.3
Bumps [parking_lot](https://github.com/Amanieu/parking_lot) from 0.12.2 to 0.12.3.
- [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Amanieu/parking_lot/compare/0.12.2...0.12.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 11:21:52 +00:00
dependabot[bot]
f379bea669 chore(cargo): bump toml from 0.8.12 to 0.8.13
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.12 to 0.8.13.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.12...toml-v0.8.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 11:21:23 +00:00
dependabot[bot]
bf674151cc chore(cargo): bump syn from 2.0.60 to 2.0.66
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.60 to 2.0.66.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.60...2.0.66)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 11:19:52 +00:00
dependabot[bot]
c11cb5fb3e chore(cargo): bump anyhow from 1.0.82 to 1.0.86
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.82 to 1.0.86.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.82...1.0.86)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 11:18:15 +00:00
link2xt
941208cc64 test(deltachat-rpc-client): reenable log_cli
It was accidentally disabled in f4dfc79808
2024-06-05 09:55:44 +00:00
iequidoo
9f3cbdc873 fix: Set Config::IsChatmail in configure()
`IsChatmail` is set also by `inbox_fetch_idle()`, but it isn't called during `configure()`. Setting
`IsChatmail` from `inbox_fetch_idle()` is necessary to handle client/server upgrades, but
`IsChatmail` also should be available for the app after configuring an account, e.g. DC Android
needs it to know whether to ask the user to disable battery optimisations.
2024-06-04 17:41:38 -03:00
link2xt
90c30879b1 refactor(imap): make select_folder() accept non-optional folder
If no folder should be selected,
`maybe_close_folder()` can be called directly.
2024-06-04 13:31:40 +00:00
link2xt
0ca1318118 fix: retry sending MDNs on temporary error
Postfix returns 421 4.4.2 Error: timeout exceeded
when overloaded by CI. If MDN is not retried in this case,
`test_qr_readreceipt` fails.
2024-06-04 12:54:47 +00:00
link2xt
0be639b244 chore(release): prepare for 1.140.0 2024-06-04 12:01:55 +00:00
Sebastian Klähn
48b4cfc247 feat: add config option to enable iroh (#5607)
Co-authored-by: link2xt <link2xt@testrun.org>
2024-06-03 22:59:29 +00:00
link2xt
a4037b8278 refactor: put duplicate code into lookup_chat_or_create_adhoc_group 2024-06-03 20:46:30 +00:00
link2xt
30405056e3 fix: Do not fail to send images with wrong extensions
Try to guess the image format based on its content first.

Co-authored-by: iequidoo <dgreshilov@gmail.com>
2024-06-03 14:56:10 +00:00
link2xt
0fbab7147a fix: prefer Chat-Group-ID over references for new groups 2024-06-03 14:10:32 +00:00
link2xt
de57ef5ac7 refactor: factor create_adhoc_group() call out of create_group() 2024-06-03 14:10:32 +00:00
link2xt
f48a047fe0 test: refactor test_alias_* into 8 separate tests 2024-06-03 14:10:32 +00:00
link2xt
8ba08432c5 docs: fix a typo in test_partial_group_consistency() 2024-06-03 14:10:32 +00:00
link2xt
bf34bd3a62 docs: create_group() does not find chats, only creates them 2024-06-03 14:10:32 +00:00
B. Petersen
21845ca5ea docs: add vCard as supported standard 2024-06-02 15:33:56 +02:00
iequidoo
768ef772bb feat: Add a db index for reactions by msg_id (#5507)
This should speed up `get_msg_reactions()` filtering reactions by `msg_id`, but also queries in
other places involving both `msg_id` and `contact_id`.
2024-06-01 11:43:15 -03:00
link2xt
69842c18f7 test: fix logging of TestContext created using TestContext::new_alice()
Before this fix LogSink was dropped immediately,
resulting in no logs printed for contexts created using
TextContext::new_alice(), but printed for contexts created
using TextContext::new().
2024-05-30 21:14:41 +00:00
link2xt
42a7cd3eea fix: allow creation of groups by outgoing messages without recipients
`!to_ids().is_empty()` check is needed in cases of 1:1 chat creation
because otherwise `to_id` is undefined,
but in case of outgoing group message without recipients
observed on a second device creating a group should be allowed.
2024-05-30 18:01:32 +00:00
link2xt
b7e5b906d1 build: unpin OpenSSL version and update to OpenSSL 3.3.0
Previously OpenSSL was pinned due to problems
with Zig toolchain used back then
and then not unpinned even after switching from Zig to Nix
due to <https://github.com/alexcrichton/openssl-src-rs/issues/235>.
The problem should be fixed now
and we can try to unpin OpenSSL again.
2024-05-30 16:56:10 +00:00
link2xt
ad271fac80 ci: remove cargo-nextest bug workaround
The problem should be fixed
since nextest 0.9.72.
2024-05-30 13:46:29 +00:00
iequidoo
70ad323c9a fix: AEAP: Remove old peerstate verified_key instead of removing the whole peerstate (#5535)
When doing an AEAP transition, we mustn't just delete the old peerstate as this would break
encryption to it. This is critical for non-verified groups -- if we can't encrypt to the old
address, we can't securely remove it from the group (to add the new one instead).
2024-05-30 10:38:39 -03:00
iequidoo
27bf4c37a7 feat: Map *.wav to Viewtype::Audio (#5633)
It seems there are no problems with playing WAV on all modern platforms, so such files should be
displayed in the "AUDIO", not "FILES" tab in the UIs.
2024-05-30 10:33:10 -03:00
iequidoo
1cc31c1038 feat: Remove limit on number of email recipients for chatmail clients (#5598) 2024-05-27 16:59:01 -03:00
iequidoo
adb0dd43a7 fix: Set Param::Bot for messages on the sender side as well (#5615) 2024-05-27 16:29:41 -03:00
link2xt
d29538beb0 chore(release): prepare for 1.139.6 2024-05-25 07:05:10 +00:00
link2xt
b99e4649a4 chore(cargo-deny): remove unused entry from deny.toml 2024-05-25 06:50:16 +00:00
link2xt
68daa3550e build: upgrade iroh to 0.17.0 2024-05-25 06:36:34 +00:00
link2xt
9d65282710 build(nix): add iroh-base output hash 2024-05-25 05:59:00 +00:00
link2xt
d8f3368b3c chore: fix python lint 2024-05-25 05:51:37 +00:00
link2xt
5755fe7bef test(deltachat-rpc-client): regression test for double gossip subscription 2024-05-25 05:46:55 +00:00
link2xt
4f071e3b31 fix: acquire write lock on iroh_channels before checking for subscribe_loop 2024-05-25 05:46:55 +00:00
holger krekel
f4dfc79808 test(deltachat-rpc-client): add realtime channel tests
Co-authored-by: link2xt <link2xt@testrun.org>
2024-05-25 05:46:55 +00:00
link2xt
518d5bc4c7 refactor: log IMAP connection type on connection failure
Otherwise it is impossible to distinguish between
failure to establish INBOX and DeltaChat folder connections
in the logs.
2024-05-24 00:13:56 +00:00
link2xt
0e1f62a38d build: update iroh to the git version
This fixes the problem with simultaneous connections in iroh-gossip:
<https://github.com/n0-computer/iroh/pull/2318>
2024-05-23 22:58:45 +00:00
iequidoo
af4b59fe0a test: Viewtype::File attachments are sent unchanged and preserve extensions 2024-05-23 17:10:52 -03:00
link2xt
8c3c0484ed fix(@deltachat/stdio-rpc-server): do not set RUST_LOG to "info" by default
`info` enables info level logging for all libraries,
e.g. iroh starts printing very verbose logs
to stderr this way.
2024-05-23 15:50:29 +00:00
link2xt
97828234dd chore(release): prepare for 1.139.5 2024-05-23 13:15:08 +00:00
iequidoo
20e64c71f8 test: "SecureJoin wait" state and info messages 2024-05-23 14:36:13 +02:00
iequidoo
2214d140c3 fix: Don't recode images in Viewtype::File messages (#5617) 2024-05-23 14:35:38 +02:00
link2xt
907d3efcd0 api(deltachat-rpc-client): add Message.send_webxdc_realtime_data() 2024-05-21 22:15:49 +00:00
link2xt
9573e02c32 api(deltachat-rpc-client): add Message.send_webxdc_realtime_advertisement() 2024-05-21 22:15:49 +00:00
Sebastian Klähn
8cb699290a fix: connect to peers that advertise to you 2024-05-21 22:12:23 +00:00
Friedel Ziegelmayer
31d7b4f9ce feat(deltachat-repl): add start-realtime and send-realtime commands 2024-05-21 22:11:22 +00:00
link2xt
2e5ad3f3a0 test(peer_channels): add test_parallel_connect() 2024-05-21 22:10:15 +00:00
holger krekel
5d3d5d23a1 api(deltachat-rpc-client): add EventType.WEBXDC_REALTIME_DATA constant 2024-05-21 22:10:15 +00:00
link2xt
469ff799ad api: add event channel overflow event 2024-05-21 22:05:48 +00:00
link2xt
18f2a09b35 api(deltachat-ffi): make WebXdcRealtimeData data usable in CFFI
Previously only msg_id was returned to CFFI
without any way to get to the actual received data.
2024-05-21 22:05:34 +00:00
link2xt
81f6aec1a0 chore(release): prepare for 1.139.4 2024-05-21 18:09:55 +00:00
iequidoo
ff60605a7f test: import_vcard() updates only the contact's gossip key 2024-05-21 17:40:07 +00:00
iequidoo
7010e80336 fix: make_vcard: Add authname and key for ContactId::SELF 2024-05-21 17:40:07 +00:00
iequidoo
5f790c1dbc fix(contact-tools): parse_vcard: Support \r\n newlines 2024-05-21 17:40:07 +00:00
iequidoo
8c5d8477fb feat: Add import_vcard() (#5202)
Add a function importing contacts from the given vCard.
2024-05-21 17:40:07 +00:00
iequidoo
10fe6929b0 feat: Scale up contact origins to OutgoingTo when sending a message 2024-05-21 17:40:07 +00:00
link2xt
6fc0000c8a fix: do not log warning if iroh relay metadata is NIL 2024-05-21 15:24:08 +00:00
Sebastian Klähn
e84a5589df nix: add nextest (#5610)
Has some unrelated change that I add the whole .vscode folder to
.gitignore which I also need.
2024-05-21 08:18:05 +00:00
link2xt
e7d9ff12ec chore(release): prepare for 1.139.3 2024-05-20 18:19:27 +00:00
link2xt
607f5959ab fix: always convert absolute paths to relative in accounts.toml
Even if the path does not start with
the directory containing the config,
we want to keep only the last component.
2024-05-20 17:49:21 +00:00
Simon Laux
11546a1ce9 api!(npm rpc server): change api: don't search in path unless options.takeVersionFromPATH is set to true
-remove `DELTA_CHAT_SKIP_PATH` environment variable
- remove version check / search for dc rpc server in $PATH
- remove `options.skipSearchInPath`
- add `options.takeVersionFromPATH`
2024-05-20 18:55:05 +02:00
Simon Laux
ee671836ca fix: npm rpc: set default options for startDeltaChat
this fixes an "undefined" error
2024-05-20 18:55:05 +02:00
Simon Laux
dd77d32446 fix: log/print exit error of deltachat-rpc-server (#5601)
see also #5599 (this logs that error atleast, but does not fix it yet)

---------

Co-authored-by: iequidoo <117991069+iequidoo@users.noreply.github.com>
2024-05-20 10:47:36 +00:00
link2xt
b32fb05ab8 fix: ignore event channel overflows
async-broadcast returns Overflowed error once
if channel overflow happened.
Public APIs such as get_next_event JSON-RPC method
are only expecting an error if the channel is closed,
so we should not propagate overflow error outside.
In particular, Delta Chat Desktop
stop receiving events completely if an error
is returned once.
If overflow happens, we should ignore it
and try again until we get an event or an error because
the channel is closed (in case of recv())
or empty (in case of try_recv()).
2024-05-20 10:44:35 +00:00
link2xt
918d87dcb6 refactor: do not try to lookup group in create_or_lookup_group() 2024-05-20 05:20:38 +00:00
link2xt
98ae05ee59 refactor: stop trying to extract chat ID from Message-IDs 2024-05-20 05:20:38 +00:00
link2xt
cff5c064a6 refactor: use let..else in create_or_lookup_group() 2024-05-20 05:20:38 +00:00
link2xt
e9cef4b0ba refactor(receive_imf): only call create_or_lookup_group() with allow_creation=true 2024-05-20 05:20:38 +00:00
link2xt
7f2c8ff53d refactor(receive_imf): remove unnecessary check for is_mdn
If message is an MDN, it is already assigned to the trash chat
by now, so it is enough to check if the message
is not assigned to chat yet.
2024-05-20 05:20:38 +00:00
link2xt
46d6b81058 refactor(receive_imf): do not check for ContactId::UNDEFINED
from_id should not be undefined,
we either should create a contact for it by then
or reject the message.
2024-05-20 05:20:38 +00:00
link2xt
6d59fb49aa feat: replace env_logger with tracing_subscriber
This allows to get iroh logs with
RUST_LOG=iroh_net=trace
2024-05-19 23:22:37 +00:00
Simon Laux
97602f3fd7 fix: sql syntax error in db migration 27 2024-05-19 20:15:22 +02:00
Simon Laux
f17987743e fix: db migration version 59, it contained an sql syntax error 2024-05-19 20:15:22 +02:00
link2xt
5767cce178 fix(mimeparser): take the last header of multiple ones with the same name
If multiple headers with the same name are present,
we must take the last one.
This is the one that is DKIM-signed if
this header is DKIM-signed at all.

Ideally servers should prevent adding
more From, To and Cc headers by oversigning
them, but unfortunately it is not common
and OpenDKIM in its default configuration
does not oversign any headers.
2024-05-18 22:24:39 +00:00
link2xt
20a4bb1a88 api(deltachat-rpc-client): add Account.wait_for_incoming_msg() 2024-05-18 22:24:17 +00:00
link2xt
578f29f215 chore(release): prepare for 1.139.2 2024-05-18 20:58:03 +00:00
link2xt
6c9643e39e build: add repository URL to @deltachat/jsonrpc-client 2024-05-18 20:56:11 +00:00
link2xt
502ae7fd9f chore(release): prepare for 1.139.1 2024-05-18 20:40:34 +00:00
link2xt
8cb527342a ci: set --access public when publishing to npm
Otherwise it fails to upload new packages with provenance.
2024-05-18 20:39:41 +00:00
link2xt
964c943dd9 chore(release): prepare for 1.139.0 2024-05-18 19:42:54 +00:00
Simon Laux
a971ad1f85 npm rpc server: fix example (#5580) 2024-05-18 19:58:44 +02:00
link2xt
e66b9de922 fix: do not mark the message as seen if it has location.kml 2024-05-18 16:09:55 +00:00
adbenitez
5db202169b fix: save override sender displayname for outgoing messages
Co-authored-by: link2xt <link2xt@testrun.org>
2024-05-18 16:08:53 +00:00
link2xt
b292b191ff test(deltachat-rpc-client): test sending vCard 2024-05-18 14:00:57 +00:00
link2xt
450ff411ec api(deltachat-rpc-client): add Chat.send_contact() 2024-05-18 14:00:57 +00:00
link2xt
8de92e54eb api(deltachat-rpc-client): add Contact.make_vcard() 2024-05-18 14:00:57 +00:00
link2xt
d0844c3e62 api(deltachat-rpc-client): add ViewType.VCARD constant 2024-05-18 14:00:57 +00:00
link2xt
37d61e41ca api(deltachat-jsonrpc): return vcard contact directly in MessageObject 2024-05-17 23:37:29 +00:00
Simon Laux
0c7dad961d npm rpc: fix convert_platform.py: 32bit i32 -> ia32 (#5589) 2024-05-17 23:35:50 +02:00
Sebastian Klähn
36f1fc4f9d feat: ephemeral peer channels (#5346)
Co-authored-by: link2xt <link2xt@testrun.org>
Co-authored-by: iequidoo <117991069+iequidoo@users.noreply.github.com>
2024-05-17 20:13:21 +00:00
Simon Laux
517cb821fb jsonrpc: add api migrate_account and get_blob_dir (#5584)
closes #5533

adds the functions that were still missing for migration to jsonrpc (the
ones that the cffi already had, so just should be quick to review ;)
2024-05-17 21:33:59 +02:00
Simon Laux
ef6c3f8476 fix: rpc npm: fix local desktop development (#5583)
typescript was complaining about missing `@deltachat/jsonrpc-client`
when it wasn't installed locally
2024-05-17 21:28:33 +02:00
link2xt
f84f0d5ad9 ci: check that constants are always up-to-date 2024-05-17 19:23:27 +00:00
Simon Laux
d8e98279c4 rpc npm: rename shutdown method to close and add muteStdErr option to mute the stderr output (#5588) 2024-05-17 21:14:38 +02:00
link2xt
424ac606d8 ci: publish @deltachat/jsonrpc-client directly to npm 2024-05-17 19:10:39 +00:00
Sebastian Klähn
2f35d9a013 use rust-analyzer nightly 2024-05-17 17:37:51 +00:00
Sebastian Klähn
e5259176c9 nix: add git-cliff to flake 2024-05-17 17:36:02 +00:00
link2xt
c370195698 chore(cargo): downgrade libc from 0.2.154 to 0.2.153
0.2.154 is yanked.
2024-05-17 13:52:19 +00:00
Simon Laux
0ba0bd3d77 fix(@deltachat/stdio-rpc-server): fix version check when deltachat-rpc-server is found in path (#5579) 2024-05-17 11:11:11 +02:00
link2xt
d23a7b8523 chore(release): prepare for 1.138.5 2024-05-16 15:07:46 +00:00
link2xt
935f503bc7 chore: rebuild node constants 2024-05-16 15:01:56 +00:00
link2xt
a0f0a8e021 build: add repository URL to deltachat-rpc-server packages 2024-05-16 14:59:45 +00:00
iequidoo
6290ed8752 api: Add make_vcard() (#5203)
Add a function returning a vCard containing contacts with the given ids.
2024-05-15 21:49:59 -03:00
iequidoo
a38f0ba09e refactor: VcardContact: Change timestamp type to i64
- u64 only adds unnecessary conversions.
- `Contact::last_seen` is also `i64`, so make timestamps such everywhere.
2024-05-15 21:07:24 -03:00
iequidoo
191624f334 refactor(contact-tools): VcardContact: rename display_name to authname
It's actually `deltachat::contact::Contact::authname` by semantics. `display_name`/`name` shouldn't
be shared, it's the name given by the user locally.
2024-05-15 21:07:24 -03:00
Hocuri
5292a49bb1 fix: parsing vCards with avatars exported by Android's "Contacts" app 2024-05-15 21:07:24 -03:00
iequidoo
22f01a2699 api: Add Viewtype::Vcard (#5202)
Co-authored-by: Hocuri <hocuri@gmx.de>
2024-05-15 21:07:24 -03:00
iequidoo
95238b6e17 api(jsonrpc): Add parse_vcard() (#5202)
Add a function parsing a vCard file at the given path.

Co-authored-by: Hocuri <hocuri@gmx.de>
Co-authored-by: Asiel Díaz Benítez <asieldbenitez@gmail.com>
2024-05-15 21:07:24 -03:00
link2xt
4a738ebd19 chore(release): prepare for 1.138.4 2024-05-15 21:59:28 +00:00
link2xt
d02eccd303 ci: run actions/setup-node before npm publish
Otherwise it does not try to use NODE_AUTH_TOKEN.
2024-05-15 21:57:24 +00:00
link2xt
f1fa053f9f chore(release): prepare for 1.138.3 2024-05-15 20:42:34 +00:00
link2xt
38c1caf180 ci: give CI job permission to publish binaries to the release
Otherwise it fails on `gh release upload` step.
2024-05-15 20:38:59 +00:00
link2xt
97d2812644 chore(release): prepare for 1.138.2 2024-05-15 18:34:06 +00:00
link2xt
2ab713d968 ci: add npm token to publish deltachat-rpc-server packages 2024-05-15 18:08:58 +00:00
link2xt
b7a25d5092 api(deltachat-rpc-client): add CONFIG_SYNCED constant 2024-05-15 17:20:06 +00:00
link2xt
8cd85fa7a4 feat: reset more settings when configuring a chatmail account 2024-05-15 06:30:17 +00:00
link2xt
7cfab9a931 test: set configuration after configure() finishes
This allows to overwrite settings changed
when XCHATMAIL capability is detected.
2024-05-15 06:27:57 +00:00
link2xt
30086038e6 chore(release): prepare for 1.138.1 2024-05-14 22:25:13 +00:00
link2xt
eec1062619 feat: detect XCHATMAIL capability and expose it as is_chatmail config 2024-05-14 22:17:46 +00:00
link2xt
07ceabdf85 refactor: resultify token::lookup_or_new() 2024-05-14 20:32:23 +00:00
Simon Laux
c349bf8e0c ci(deltachat-rpc-server): fix upload of npm packages to github releases (#5564) 2024-05-14 18:48:34 +00:00
link2xt
21eb4f6648 chore(cargo): bump brotli from 5.0.0 to 6.0.0 2024-05-14 17:27:33 +00:00
dependabot[bot]
10fed7d7de chore(cargo): bump human-panic from 1.2.3 to 2.0.0
Bumps [human-panic](https://github.com/rust-cli/human-panic) from 1.2.3 to 2.0.0.
- [Changelog](https://github.com/rust-cli/human-panic/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/human-panic/compare/v1.2.3...v2.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-14 16:29:22 +00:00
dependabot[bot]
b08a283fe5 chore(cargo): bump serde_json from 1.0.115 to 1.0.116
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.115 to 1.0.116.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.115...v1.0.116)

---
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-05-14 16:12:52 +00:00
dependabot[bot]
45a2805100 chore(cargo): bump hickory-resolver from 0.24.0 to 0.24.1
Bumps [hickory-resolver](https://github.com/hickory-dns/hickory-dns) from 0.24.0 to 0.24.1.
- [Release notes](https://github.com/hickory-dns/hickory-dns/releases)
- [Changelog](https://github.com/hickory-dns/hickory-dns/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hickory-dns/hickory-dns/compare/v0.24.0...v0.24.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-14 16:11:48 +00:00
dependabot[bot]
cc8157ecf1 chore(cargo): bump libc from 0.2.153 to 0.2.154
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.153 to 0.2.154.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.153...0.2.154)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-14 16:11:30 +00:00
dependabot[bot]
0c98aca5f0 chore(cargo): bump parking_lot from 0.12.1 to 0.12.2
Bumps [parking_lot](https://github.com/Amanieu/parking_lot) from 0.12.1 to 0.12.2.
- [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Amanieu/parking_lot/compare/0.12.1...0.12.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-14 16:11:12 +00:00
link2xt
170e4b3530 refactor(sql): make open flags immutable 2024-05-14 14:55:04 +00:00
link2xt
5ed91e9f6e refactor: make MimeMessage.get_header() return Option<&str> 2024-05-13 17:07:58 +00:00
link2xt
2779737c56 ci: set RUSTUP_WINDOWS_PATH_ADD_BIN
This is a workaround for
<https://github.com/nextest-rs/nextest/issues/1493>
2024-05-13 14:29:33 +00:00
link2xt
0d3c0a3d8f fix: ignore parent message if message references itself
When there are no parent references,
Delta Chat inserts Message-ID into References.
Such references should be ignored
because otherwise fully downloaded message
may be assigned to the same chat as previously incorrectly assigned
partially downloaded message.

Fixes receive_imf::tests::test_create_group_with_big_msg
2024-05-13 13:29:06 +00:00
link2xt
8e38e7220b fix: always prefer Chat-Group-ID over In-Reply-To and References
Chat-Group-ID always correctly identifies the chat
message was sent to, while In-Reply-To and References
may point to a message that has itself been incorrectly
assigned to a chat.
2024-05-13 13:29:06 +00:00
link2xt
acfde3cb7b fix: never treat message with Chat-Group-ID as a private reply 2024-05-13 13:29:06 +00:00
link2xt
b6a461e3b7 refactor: add MimeMessage.get_chat_group_id() 2024-05-13 13:29:06 +00:00
B. Petersen
0541ecf22c chore(release): prepare for 1.138.0 2024-05-13 12:47:11 +02:00
B. Petersen
77af0a2114 update node constants 2024-05-13 12:47:11 +02:00
B. Petersen
2f679bc21a add new securejoin strings to deltachat.h 2024-05-13 12:08:36 +02:00
iequidoo
518db9a20f feat: Make one-to-one chats read-only the first seconds of a SecureJoin (#5512)
This protects Bob (the joiner) of sending unexpected-unencrypted messages during an otherwise nicely
running SecureJoin.

If things get stuck, however, we do not want to block communication -- the chat is just
opportunistic as usual, but that needs to be communicated:
1. If Bob's chat with Alice is `Unprotected` and a SecureJoin is started, then add info-message
   "Establishing guaranteed end-to-end encryption, please wait..." and let `Chat::can_send()` return
   `false`.
2. Once the info-message "Messages are guaranteed to be e2ee from now on" is added, let
   `Chat::can_send()` return `true`.
3. If after SECUREJOIN_WAIT_TIMEOUT seconds `2.` did not happen, add another info-message "Could not
   yet establish guaranteed end-to-end encryption but you may already send a message" and also let
   `Chat::can_send()` return `true`.

Both `2.` and `3.` require the event `ChatModified` being sent out so that UI pick up the change wrt
`Chat::can_send()` (this is the same way how groups become updated wrt `can_send()` changes).

SECUREJOIN_WAIT_TIMEOUT should be 10-20 seconds so that we are reasonably sure that the app remains
active and receiving also on mobile devices. If the app is killed during this time then we may need
to do step 3 for any pending Bob-join chats (right now, Bob can only join one chat at a time).
2024-05-13 12:08:36 +02:00
link2xt
edf8aafbdc api!(jsonrpc): replace EphemeralTimer tag "variant" with "kind"
We use "kind" everywhere else.
2024-05-12 01:15:31 +00:00
iequidoo
ab1583eef9 fix: Use ChatIdBlocked::lookup_by_contact() instead of ChatId's method when applicable
`ChatId::lookup_by_contact()` returns `None` for blocked chats, so it should be only used if we need
to filter out blocked chats, e.g. when building a chatlist.
2024-05-11 17:37:12 -03:00
iequidoo
e3cb9b894b fix: Update special chats icons even if they are blocked (#5509)
E.g. the multi-device synchronisation creates the "Saved Messages" chat as blocked, in this case the
chat icon wasn't updated before and the user avatar was displayed instead.
2024-05-11 17:37:12 -03:00
Simon Laux
c375c03d8e stdio jsonrpc server npm package (#5332)
- [x] figgure out how to build the packages (that it installs native
optional package automatically)
- [X] Make the gluecode
- [x] expose both the lowerlevel api that desktop uses (~~send objects
and receive objects~~, getting path of rpc-server is enough)
  - [X] and the higher level api needed for bots (jsonrpc client)
  - [X] typescript types
- [x] automatically pick the right binary from npm or allow getting it
from env var, or give out an error (throw error)
- [x] find out how to dev locally (use local built core in dc desktop) -
there is the question of how to link the typescript client and the task
to add a search in the cargo target folder for a debug build or a
different way, find out some good flow that we can use and document for
dc desktop + locally built core development
- [x] build the packages in ci
- [x] fix that deltachat-rpc-server is not executable

postponed:
- [ ] publish from ci
   - [ ] add key/token to deploy to npm 

Closes #4694

## Related prs
- https://github.com/deltachat-bot/echo/pull/69
- https://github.com/deltachat/deltachat-desktop/pull/3567

---------

Co-authored-by: link2xt <link2xt@testrun.org>
2024-05-11 20:54:11 +02:00
bjoern
14aaab05b0 limit quote replies (#5543)
this PR checks if the quotes are used in a reasonable way:

- quoted messages should be send to the same chat
- or to one-to-one chats

if the quote's chat ID is not the same than the sending chat _and_ the
sending chat is not a one-to-one chat, sending is aborted.

usually, the UIs does not allow quoting in other ways, so, this check is
only a "last defence line" to avoid leaking data in case the UI has
bugs, as recently in
https://github.com/deltachat/deltachat-android/issues/3032

@iequidoo @link2xt @adbenitez i am not 100% sure about this PR, maybe
i've overseen a reasonable usecase where other quotes make sense

---------

Co-authored-by: link2xt <link2xt@testrun.org>
2024-05-10 16:43:49 +02:00
link2xt
72c09feb64 feat: do not add location markers to messages with non-POI location 2024-05-10 14:04:16 +00:00
Nico de Haen
8a4dff2212 Add webxdc internal integration commands in jsonrpc (#5541)
Adds 
_setWebxdcIntegration_ 
_initWebxdcIntegration_
2024-05-08 07:53:04 +02:00
dependabot[bot]
022f836d35 chore(cargo): bump schemars from 0.8.16 to 0.8.19
Bumps [schemars](https://github.com/GREsau/schemars) from 0.8.16 to 0.8.19.
- [Release notes](https://github.com/GREsau/schemars/releases)
- [Changelog](https://github.com/GREsau/schemars/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GREsau/schemars/compare/v0.8.16...v0.8.19)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-08 03:42:51 +00:00
link2xt
636ab4a9e5 fix: delete non-POI locations after delete_device_after, not immediately 2024-05-06 18:32:04 +00:00
link2xt
2bddefa1ab refactor: remove allow_keychange 2024-05-05 21:23:32 +00:00
dependabot[bot]
7d67100a3c Merge pull request #5523 from deltachat/dependabot/cargo/mailparse-0.15.0 2024-05-05 21:23:10 +00:00
dependabot[bot]
1043916411 chore(cargo): bump mailparse from 0.14.1 to 0.15.0
Bumps [mailparse](https://github.com/staktrace/mailparse) from 0.14.1 to 0.15.0.
- [Commits](https://github.com/staktrace/mailparse/compare/v0.14.1...v0.15.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-05 20:59:52 +00:00
dependabot[bot]
f4e58e90ae chore(cargo): bump syn from 2.0.57 to 2.0.60
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.57 to 2.0.60.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.57...2.0.60)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-05 13:50:07 +00:00
link2xt
e4f10b32dd chore(cargo): bump imap-proto from 0.16.4 to 0.16.5 2024-05-04 22:41:30 +00:00
dependabot[bot]
e9431888a6 Merge pull request #5522 from deltachat/dependabot/cargo/chrono-0.4.38 2024-05-04 22:40:17 +00:00
dependabot[bot]
1649073c0f chore(cargo): bump anyhow from 1.0.81 to 1.0.82
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.81 to 1.0.82.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.81...1.0.82)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-04 03:23:17 -03:00
dependabot[bot]
b2cf18d8b3 chore(cargo): bump chrono from 0.4.37 to 0.4.38
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.37 to 0.4.38.
- [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.37...v0.4.38)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-04 02:53:42 +00:00
iequidoo
2eceb4be29 feat(contact-tools): Add make_vcard()
Add a function making a vCard containing given `VcardContact`s.
2024-05-03 23:52:51 -03:00
iequidoo
ae7ff17ba2 feat(contact-tools): Support parsing profile images from "PHOTO:data:image/jpeg;base64,..." 2024-05-03 23:52:51 -03:00
Hocuri
026f678452 feat: Parsing vCards for contacts sharing (#5482)
Co-authored-by: iequidoo <dgreshilov@gmail.com>
2024-05-03 22:44:09 -03:00
link2xt
add8c0680f ci: update Rust to 1.78.0 2024-05-03 02:11:07 +00:00
dependabot[bot]
aee2b81c06 chore(cargo): bump thiserror from 1.0.58 to 1.0.59
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.58 to 1.0.59.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.58...1.0.59)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-02 20:35:31 -03:00
dependabot[bot]
3624aad1b5 chore(cargo): bump async-channel from 2.2.0 to 2.2.1
Bumps [async-channel](https://github.com/smol-rs/async-channel) from 2.2.0 to 2.2.1.
- [Release notes](https://github.com/smol-rs/async-channel/releases)
- [Changelog](https://github.com/smol-rs/async-channel/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smol-rs/async-channel/compare/v2.2.0...v2.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-02 20:24:27 -03:00
link2xt
299d994d4b ci: replace black with ruff format
We use `ruff` anyway, so it is one less dependency.
2024-05-02 14:44:11 +02:00
dependabot[bot]
5e0f5ec390 chore(cargo): bump serde from 1.0.197 to 1.0.200
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.197 to 1.0.200.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.197...v1.0.200)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-02 04:27:42 -03:00
dependabot[bot]
c318ca5d1a chore(cargo): bump base64 from 0.22.0 to 0.22.1
Bumps [base64](https://github.com/marshallpierce/rust-base64) from 0.22.0 to 0.22.1.
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.22.0...v0.22.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-02 04:18:18 -03:00
dependabot[bot]
38a2e07194 Merge pull request #5515 from deltachat/dependabot/cargo/quote-1.0.36 2024-05-02 06:22:56 +00:00
dependabot[bot]
1ff6740938 chore(cargo): bump quote from 1.0.35 to 1.0.36
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.35 to 1.0.36.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.35...1.0.36)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-01 21:52:09 +00:00
Christian Hagenest
402d5bed85 rm npm install 2024-04-30 21:10:36 +02:00
missytake
57bc046381 ci: use rsync instead of 3rd party github action 2024-04-30 21:10:36 +02:00
Christian Hagenest
0617236eb0 rm leading slash 2024-04-30 21:10:36 +02:00
Christian Hagenest
8c5ffe0237 upload-docs npm run => npm run build 2024-04-30 21:10:36 +02:00
Christian Hagenest
39f977c1e6 add : to upload-docs.yml 2024-04-30 21:10:36 +02:00
Christian Hagenest
ec03614cae add npm install to upload-docs.yml 2024-04-30 21:10:36 +02:00
Christian Hagenest
ea0b063c19 test ts build on branch 2024-04-30 21:10:36 +02:00
Christian Hagenest
98d7a93909 correct folder for js.jsonrpc docs 2024-04-30 21:10:36 +02:00
Christian Hagenest
49bf8414ed rm unneeded rust install from ts docs ci 2024-04-30 21:10:36 +02:00
Christian Hagenest
1e7dbea351 Implement jsonrpc-docs build in github action 2024-04-30 21:10:36 +02:00
Christian Hagenest
0412244646 Revert "WIP: build ts docs with ci + nix"
This reverts commit 00d486e58f.
2024-04-30 21:10:36 +02:00
Christian Hagenest
bbd854d7bc Revert "push docs to delta.chat instead of codespeak"
This reverts commit c04188ce56.
2024-04-30 21:10:36 +02:00
Christian Hagenest
ba2bb517f7 Revert "WIP npm2nix, error: no source/nix"
This reverts commit 83bfba53de.
2024-04-30 21:10:36 +02:00
Christian Hagenest
0ae831eca0 WIP npm2nix, error: no source/nix 2024-04-30 21:10:36 +02:00
Christian Hagenest
ab494ae786 push docs to delta.chat instead of codespeak 2024-04-30 21:10:36 +02:00
Christian Hagenest
8a58ae8a3a WIP: build ts docs with ci + nix 2024-04-30 21:10:36 +02:00
link2xt
cf84255e99 test: test that POIs are deleted when ephemeral message expires 2024-04-29 22:35:59 +00:00
link2xt
462bd63065 docs: add location module documentation 2024-04-29 22:35:59 +00:00
link2xt
6bfbf6547b feat: delete orphaned POI locations during housekeeping 2024-04-29 22:35:59 +00:00
link2xt
13802bab42 fix: delete POI location when disappearing message expires 2024-04-29 22:35:59 +00:00
link2xt
adb2e4ea32 refactor: move delete_poi_location to location module and document it 2024-04-29 22:35:59 +00:00
link2xt
421a7b277d docs: remove Doxygen markup from Message.has_location() 2024-04-29 22:35:59 +00:00
link2xt
14d8139883 feat: delete expired path locations in ephemeral loop 2024-04-29 22:35:59 +00:00
link2xt
062905924c docs: fix references in Message.set_location() documentation 2024-04-29 22:35:59 +00:00
iequidoo
20d79970a2 fix: Correct message viewtype before recoding image blob (#5496)
Otherwise, e.g. if a message is a large GIF, but its viewtype is set to `Image` by the app, this GIF
will be recoded to JPEG to reduce its size. GIFs and other special viewtypes must be always detected
and sent as is.
2024-04-27 23:44:50 -03:00
link2xt
f49588e64e fix: interrupt location loop when new location is stored
Otherwise location-only messages
that should be sent every 60 seconds
are never sent because location loop
waits until the end of location streaming
and is only interrupted by location streaming
ending in other chats or being enabled in other chats.
2024-04-27 20:55:05 +00:00
link2xt
496a8e3810 test: test that member is added even if "Member added" is lost
This is similar to `test_modify_chat_disordered`,
but tests that recovery works in the simplest case
where next message is not modifying group membership.
2024-04-27 07:51:08 +00:00
link2xt
94dc65c1a2 refactor(python): fix ruff 0.4.2 warnings 2024-04-25 20:45:54 +00:00
link2xt
4fe7fa3148 fix: never prepend subject to message text when bot receives it 2024-04-25 12:15:54 +00:00
link2xt
4cf923ccb9 fix: do not fail to send encrypted quotes to unencrypted chats
Replace quote text with "..." instead.
2024-04-25 09:00:49 +00:00
iequidoo
56b86adf18 api: Add dc_msg_save_file() which saves file copy at the provided path (#4309)
... and fails if file already exists. The UI should open the file saving dialog, defaulting to
Downloads and original filename, when asked to save the file. After confirmation it should call
dc_msg_save_file().
2024-04-24 16:38:25 -03:00
iequidoo
cfccee2ad4 fix: Message::set_file_from_bytes(): Set Param::Filename 2024-04-24 16:38:25 -03:00
Hocuri
37d92e3fa5 test: Explain test_was_seen_recently false-positive and give workaround instructions (#5474)
Until the issue is fixed, keep others from running into the same issue.
2024-04-24 14:55:15 +00:00
link2xt
a1ee2b463f chore(release): prepare for 1.137.4 2024-04-24 11:10:25 +00:00
link2xt
8df3b1bb1b fix: use only CRLF in Autocrypt Setup Message 2024-04-24 01:50:28 +00:00
iequidoo
22f240dd4d feat: Add progressive backoff for failing IMAP connection attempts (#5443)
This way we avoid an immediate retry if the network is not yet ready exhausting the ratelimiter's
quota of two connection attempts. Also notify the ratelimiter only after a successful connection so
that it only limits the server load, but not connection attempts.
2024-04-23 22:00:47 -03:00
iequidoo
ae10ed5c40 refactor: Imap: remove RwLock from ratelimit 2024-04-23 22:00:47 -03:00
link2xt
aff6bf9402 fix: convert images to RGB8 (without alpha) before encoding into JPEG
Otherwise an error
"The encoder or decoder for Jpeg does not support the color type `Rgba8`"
is returned if image has an alpha channel.

This is caused by the recent change of JPEG encoder
in `image` crate: <https://github.com/image-rs/image/issues/2211>
2024-04-23 23:37:58 +00:00
link2xt
43fc55e542 test: test recoding RGBA image 2024-04-23 23:37:58 +00:00
link2xt
7ea05cb8a0 test: add screenshot-rgba.png
Created by `convert -alpha deactivate screenshot.png screenshot-rbga.png`.
2024-04-23 23:37:58 +00:00
link2xt
d036ad5853 fix: do not fail if Autocrypt Setup Message has no encryption preference
According to Autocrypt specification
Autocrypt Setup Message SHOULD
contain Autocrypt-Prefer-Encrypt header,
but K-9 6.802 does not include it.
2024-04-23 22:16:54 +00:00
link2xt
e9280b8413 refactor: group use at the top of the test modules 2024-04-23 21:07:50 +00:00
link2xt
2108a8ba94 fix(node): undefine NAPI_EXPERIMENTAL
This fixes build with new clang
which treats -Wincompatible-function-pointer-types as an error.

Related upstream issue: <https://github.com/nodejs/node/issues/52229>
2024-04-23 11:34:03 +00:00
link2xt
34f4ec02f6 feat: replace event channel with broadcast channel
This makes `EventTracker` receive events immediately
instead of being moved from event emitter to event tracker
by a task spawned from `TestContext::new_internal`.

This makes `EventTracker.clear_events` reliable
as it is guaranteed to remove all events emitted
by the time it is called rather than only events
that have been moved already.
2024-04-22 07:44:21 +00:00
link2xt
72d5a387fb api!: remove Stream implementation for EventEmitter 2024-04-22 07:44:21 +00:00
link2xt
d17d89ea8f fix: mark contact request messages as seen on IMAP 2024-04-21 02:17:52 +00:00
link2xt
d2aa76c0ca refactor(deltachat-ffi): remove ResultNullableExt 2024-04-20 21:04:01 +00:00
link2xt
406031773b docs: update references to SecureJoin protocols 2024-04-20 20:17:56 +02:00
bjoern
242547f1e9 feat: experimental Webxdc Integration API, Maps Integration (#5461)
as discussed in several chats, this PR starts making it possible to use
Webxdc as integrations to the main app. In other word: selected parts of
the main app can be integrated as Webxdc, eg. Maps [^1]

this PR contains two parts:

- draft an Webxdc Integration API
- use the Webxdc Integration API to create a Maps Integration

to be clear: a Webxdc is not part of this PR. the PR is about marking a
Webxdc being used as a Map - and core then feeds the Webxdc with
location data. from the view of the Webxdc, the normal
`sendUpdate()`/`setUpdateListener()` is used.

things are still marked as "experimental", idea is to get that in to
allow @adbenitez and @nicodh to move forward on the integrations into
android and desktop, as well as improving the maps.xdc itself.
good news is that we currently can change the protocol between Webxdc
and core at any point :)


# Webxdc Integration API

see `dc_init_webxdc_integration()` in `deltachat.h` for overview and
documentation.

rust code is mostly in `webxdc/integration.rs` that is called by other
places as needed. current [user of the API is
deltachat-ios](https://github.com/deltachat/deltachat-ios/pull/1912),
android/desktop will probably follow.

the jsonrpc part is missing and can come in another PR when things are
settled and desktop is really starting [^2] (so we won't need to do all
iterations twice :) makes also sense, when this is done by someone
actually trying that out on desktop

while the API is prepared to allow other types of integrations (photo
editor, compose tools ...) internally, we currently ignore the type. if
that gets more crazy, we probably also need a dedicated table for the
integrations and not just a single param.

# Maps Integration

rust code is mostly in `webxdc/maps_integration.rs` that is called by
`webxdc/integration.rs` as needed.

EDIT: the idea of having a split here, is that
`webxdc/maps_integration.rs` really can focus on the json part, on the
communication with the .xdc, including tests

this PR is basic implementation, enabling to move forward on
integrations on iOS, but also on desktop and android.

the current implementation allows already the following:
- global and per-chat maps
- add and display POIs
- show positions and tracks of the last 24 hours

the current maps.xdc uses leaflet, and is in some regards better than
the current android/desktop implementations (much faster, show age of
positions, fade out positions, always show names of POIs, clearer UI).
however, we are also not bound to leaflet, it can be anything

> [**screenshots of the current
state**](https://github.com/deltachat/deltachat-ios/pull/1912)
> 👆

to move forward faster and to keep this PR small, the following will go
to a subsequent PR:

- consider allowing webxdc to use a different timewindow for the
location
- delete POIs
- jsonrpc 


[^1]: maps are a good example as anyways barely native (see android
app), did cause a lot of pain on many levels in the past (technically,
bureaucratically), and have a comparable simple api
[^2]: only going for jsonrpc would only make sense if large parts of
android/ios would use jsonrpc, we're not there

---------

Co-authored-by: link2xt <link2xt@testrun.org>
2024-04-20 18:09:35 +02:00
link2xt
f43f5c6c0f build(nix): add ./deltachat-contact-tools to sources 2024-04-20 06:01:53 +00:00
link2xt
910e4bfa37 docs: fix broken references in documentation comments 2024-04-20 05:57:26 +00:00
link2xt
ecf4e651ee bulid(nix): update nix flake 2024-04-20 05:56:34 +00:00
link2xt
7b724fa75a build(nix): add imap-tools as deltachat-rpc-client dependency 2024-04-20 05:56:26 +00:00
link2xt
09776ae71c chore(deps): update rustls to 0.21.11 2024-04-20 05:35:26 +00:00
iequidoo
47bea5f8fb feat(ffi): Warning instead of error if dc_get_msg() could not retrieve message
As trashed messages can't be loaded from the db now by `Message::load_from_db()` returning an error
for such messages, errors from `Message::load_from_db()` should be logged as warnings. If it's
really an error like a db failure, it should be logged internally.
2024-04-20 05:33:56 +00:00
iequidoo
99cd6d10da fix(python): _map_ffi_event: Always check if get_message_by_id() returned None
As trashed messages can't be loaded from the db now, `get_message_by_id()` returns None in some
tests e.g. in `test_deleted_msgs_dont_reappear()`. A `PerAccount` hook shouldn't be called if so.
2024-04-20 05:33:56 +00:00
iequidoo
fae4cb33bc fix(python): Message::is_outgoing: Don't reload message from db
There's no need to load an updated message state from the db to implement `is_outgoing()` and also
this function is implicitly called in some tests where a message is already trashed and a call to
`dc_get_msg()` generates an unexpected error.
2024-04-20 05:33:56 +00:00
iequidoo
7a3be74350 fix(python): Use cached message object if dc_get_msg() returns NULL
Particularly, in _msgstate() and download_state(). If the message has been trashed, dc_get_msg()
returns NULL now.
2024-04-20 05:33:56 +00:00
link2xt
20a64ec357 refactor: remove duplicate clippy exceptions 2024-04-20 03:04:24 +00:00
iequidoo
92bf48684a fix: Don't set is_bot for webxdc status updates (#5445)
Webxdc status update messages are always "Auto-Submitted: auto-generated", so they must be excluded
for is_bot flag calculation.
2024-04-18 23:19:34 -03:00
180 changed files with 17409 additions and 5147 deletions

View File

@@ -7,3 +7,10 @@ updates:
commit-message:
prefix: "chore(cargo)"
open-pull-requests-limit: 50
# Keep GitHub Actions up to date.
# <https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot>
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@@ -24,7 +24,7 @@ jobs:
name: Lint Rust
runs-on: ubuntu-latest
env:
RUSTUP_TOOLCHAIN: 1.77.1
RUSTUP_TOOLCHAIN: 1.80.1
steps:
- uses: actions/checkout@v4
with:
@@ -40,6 +40,18 @@ jobs:
- name: Check
run: cargo check --workspace --all-targets --all-features
npm_constants:
name: Check if node constants are up to date
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- name: Rebuild constants
run: npm run build:core:constants
- name: Check that constants are not changed
run: git diff --exit-code
cargo_deny:
name: cargo deny
runs-on: ubuntu-latest
@@ -47,7 +59,7 @@ jobs:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: EmbarkStudios/cargo-deny-action@v1
- uses: EmbarkStudios/cargo-deny-action@v2
with:
arguments: --all-features --workspace
command: check
@@ -83,11 +95,11 @@ jobs:
matrix:
include:
- os: ubuntu-latest
rust: 1.77.1
rust: 1.80.1
- os: windows-latest
rust: 1.77.1
rust: 1.80.1
- os: macos-latest
rust: 1.77.1
rust: 1.80.1
# Minimum Supported Rust Version = 1.77.0
- os: ubuntu-latest

View File

@@ -266,3 +266,141 @@ jobs:
- name: Publish deltachat-rpc-client to PyPI
if: github.event_name == 'release'
uses: pypa/gh-action-pypi-publish@release/v1
publish_npm_package:
name: Build & Publish npm prebuilds and deltachat-rpc-server
needs: ["build_linux", "build_windows", "build_macos"]
runs-on: "ubuntu-latest"
permissions:
id-token: write
# Needed to publish the binaries to the release.
contents: write
steps:
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Download Linux aarch64 binary
uses: actions/download-artifact@v4
with:
name: deltachat-rpc-server-aarch64-linux
path: deltachat-rpc-server-aarch64-linux.d
- name: Download Linux armv7l binary
uses: actions/download-artifact@v4
with:
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@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@v4
with:
name: deltachat-rpc-server-aarch64-macos
path: deltachat-rpc-server-aarch64-macos.d
- 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: make npm packets for prebuilds and `@deltachat/stdio-rpc-server`
run: |
cd deltachat-rpc-server/npm-package
python --version
python scripts/pack_binary_for_platform.py aarch64-unknown-linux-musl ../../deltachat-rpc-server-aarch64-linux.d/deltachat-rpc-server
python scripts/pack_binary_for_platform.py armv7-unknown-linux-musleabihf ../../deltachat-rpc-server-armv7l-linux.d/deltachat-rpc-server
python scripts/pack_binary_for_platform.py arm-unknown-linux-musleabihf ../../deltachat-rpc-server-armv6l-linux.d/deltachat-rpc-server
python scripts/pack_binary_for_platform.py i686-unknown-linux-musl ../../deltachat-rpc-server-i686-linux.d/deltachat-rpc-server
python scripts/pack_binary_for_platform.py x86_64-unknown-linux-musl ../../deltachat-rpc-server-x86_64-linux.d/deltachat-rpc-server
python scripts/pack_binary_for_platform.py i686-pc-windows-gnu ../../deltachat-rpc-server-win32.d/deltachat-rpc-server.exe
python scripts/pack_binary_for_platform.py x86_64-pc-windows-gnu ../../deltachat-rpc-server-win64.d/deltachat-rpc-server.exe
python scripts/pack_binary_for_platform.py x86_64-apple-darwin ../../deltachat-rpc-server-x86_64-macos.d/deltachat-rpc-server
python scripts/pack_binary_for_platform.py aarch64-apple-darwin ../../deltachat-rpc-server-aarch64-macos.d/deltachat-rpc-server
python scripts/pack_binary_for_platform.py aarch64-linux-android ../../deltachat-rpc-server-arm64-v8a-android.d/deltachat-rpc-server
python scripts/pack_binary_for_platform.py armv7-linux-androideabi ../../deltachat-rpc-server-armeabi-v7a-android.d/deltachat-rpc-server
ls -lah platform_package
for platform in ./platform_package/*; do npm pack "$platform"; done
npm pack
ls -lah
- name: Upload to artifacts
uses: actions/upload-artifact@v4
with:
name: deltachat-rpc-server-npm-package
path: deltachat-rpc-server/npm-package/*.tgz
if-no-files-found: error
- name: Upload npm packets 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 }} \
deltachat-rpc-server/npm-package/*.tgz
# Configure Node.js for publishing.
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: "https://registry.npmjs.org"
- name: Publish npm packets for prebuilds and `@deltachat/stdio-rpc-server`
if: github.event_name == 'release'
working-directory: deltachat-rpc-server/npm-package
run: |
ls -lah platform_package
for platform in *.tgz; do npm publish --provenance "$platform" --access public; done
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -14,7 +14,7 @@ jobs:
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v1.1.1
uses: dependabot/fetch-metadata@v2.2.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve a PR

View File

@@ -1,82 +1,38 @@
name: "jsonrpc js client build"
name: "Publish @deltachat/jsonrpc-client"
on:
pull_request:
push:
tags:
- "*"
- "!py-*"
workflow_dispatch:
release:
types: [published]
jobs:
pack-module:
name: "Package @deltachat/jsonrpc-client and upload to download.delta.chat"
name: "Publish @deltachat/jsonrpc-client"
runs-on: ubuntu-20.04
permissions:
id-token: write
contents: read
steps:
- name: Install tree
run: sudo apt install tree
- uses: actions/checkout@v4
with:
show-progress: false
- uses: actions/setup-node@v4
with:
node-version: "18"
- name: Get tag
id: tag
uses: dawidd6/action-get-tag@v1
continue-on-error: true
- name: Get Pull Request ID
id: prepare
run: |
tag=${{ steps.tag.outputs.tag }}
if [ -z "$tag" ]; then
node -e "console.log('DELTACHAT_JSONRPC_TAR_GZ=deltachat-jsonrpc-client-' + '${{ github.ref }}'.split('/')[2] + '.tar.gz')" >> $GITHUB_ENV
else
echo "DELTACHAT_JSONRPC_TAR_GZ=deltachat-jsonrpc-client-${{ steps.tag.outputs.tag }}.tar.gz" >> $GITHUB_ENV
echo "No preview will be uploaded this time, but the $tag release"
fi
- name: System info
run: |
npm --version
node --version
echo $DELTACHAT_JSONRPC_TAR_GZ
node-version: 20
registry-url: "https://registry.npmjs.org"
- name: Install dependencies without running scripts
working-directory: deltachat-jsonrpc/typescript
run: npm install --ignore-scripts
- name: Package
shell: bash
working-directory: deltachat-jsonrpc/typescript
run: |
npm run build
npm pack .
ls -lah
mv $(find deltachat-jsonrpc-client-*) $DELTACHAT_JSONRPC_TAR_GZ
- name: Upload Prebuild
uses: actions/upload-artifact@v4
with:
name: deltachat-jsonrpc-client.tgz
path: deltachat-jsonrpc/typescript/${{ env.DELTACHAT_JSONRPC_TAR_GZ }}
# Upload to download.delta.chat/node/preview/
- name: Upload deltachat-jsonrpc-client preview to download.delta.chat/node/preview/
if: ${{ ! steps.tag.outputs.tag }}
id: upload-preview
shell: bash
run: |
echo -e "${{ secrets.SSH_KEY }}" >__TEMP_INPUT_KEY_FILE
chmod 600 __TEMP_INPUT_KEY_FILE
scp -o StrictHostKeyChecking=no -v -i __TEMP_INPUT_KEY_FILE -P "22" -r deltachat-jsonrpc/typescript/$DELTACHAT_JSONRPC_TAR_GZ "${{ secrets.USERNAME }}"@"download.delta.chat":"/var/www/html/download/node/preview/"
continue-on-error: true
- name: Post links to details
if: steps.upload-preview.outcome == 'success'
run: node ./node/scripts/postLinksToDetails.js
- name: Publish
working-directory: deltachat-jsonrpc/typescript
run: npm publish --provenance deltachat-jsonrpc-client-* --access public
env:
URL: preview/${{ env.DELTACHAT_JSONRPC_TAR_GZ }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MSG_CONTEXT: Download the deltachat-jsonrpc-client.tgz
# Upload to download.delta.chat/node/
- name: Upload deltachat-jsonrpc-client build to download.delta.chat/node/
if: ${{ steps.tag.outputs.tag }}
id: upload
shell: bash
run: |
echo -e "${{ secrets.SSH_KEY }}" >__TEMP_INPUT_KEY_FILE
chmod 600 __TEMP_INPUT_KEY_FILE
scp -o StrictHostKeyChecking=no -v -i __TEMP_INPUT_KEY_FILE -P "22" -r deltachat-jsonrpc/typescript/$DELTACHAT_JSONRPC_TAR_GZ "${{ secrets.USERNAME }}"@"download.delta.chat":"/var/www/html/download/node/"
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -31,7 +31,7 @@ jobs:
mv docs js
- name: Upload
uses: horochx/deploy-via-scp@v1.0.1
uses: horochx/deploy-via-scp@1.1.0
with:
user: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}

View File

@@ -4,6 +4,7 @@ on:
push:
branches:
- main
- build_jsonrpc_docs_ci
jobs:
build-rs:
@@ -17,13 +18,11 @@ jobs:
run: |
cargo doc --package deltachat --no-deps --document-private-items
- name: Upload to rs.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/rs/"
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 }}@rs.delta.chat:/var/www/html/rs/"
build-python:
runs-on: ubuntu-latest
@@ -62,3 +61,31 @@ jobs:
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"
build-ts:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./deltachat-jsonrpc/typescript
steps:
- uses: actions/checkout@v4
with:
show-progress: false
fetch-depth: 0 # Fetch history to calculate VCS version number.
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: npm install
run: npm install
- name: npm run build
run: npm run build
- name: Run docs script
run: npm run docs
- name: Upload to js.jsonrpc.delta.chat
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/deltachat-jsonrpc/typescript/docs/ "${{ secrets.USERNAME }}@js.jsonrpc.delta.chat:/var/www/html/js-jsonrpc/"

4
.gitignore vendored
View File

@@ -33,7 +33,7 @@ deltachat-ffi/xml
coverage/
.DS_Store
.vscode/launch.json
.vscode
python/accounts.txt
python/all-testaccounts.txt
tmp/
@@ -50,4 +50,4 @@ result
# direnv
.envrc
.direnv
.direnv

View File

@@ -1,5 +1,805 @@
# Changelog
## [1.142.7] - 2024-08-17
### Fixes
- Do not save "Automatic" into configured_imap_certificate_checks. **This fixes regression introduced in core 1.142.4. Versions 1.142.4..1.142.6 should not be used in releases.**
- Create a group unblocked for bot even if 1:1 chat is blocked ([#5514](https://github.com/deltachat/deltachat-core-rust/pull/5514)).
- Update rpgp from 0.13.1 to 0.13.2 to fix "unable to decrypt" errors when sending messages to old Delta Chat clients and using Ed25519 keys to encrypt.
- Do not request ALPN on standard ports and when using STARTTLS.
### Features / Changes
- jsonrpc: Add ContactObject::e2ee_avail.
### Tests
- Protected group for bot is auto-accepted.
## [1.142.6] - 2024-08-15
### Fixes
- Default to strict TLS checks if not configured.
### Miscellaneous Tasks
- deltachat-rpc-client: Fix ruff 0.6.0 warnings.
## [1.142.5] - 2024-08-14
### Fixes
- Still try to create "INBOX.DeltaChat" if couldn't create "DeltaChat" ([#5870](https://github.com/deltachat/deltachat-core-rust/pull/5870)).
- `store_seen_flags_on_imap`: Skip to next messages if couldn't select folder ([#5870](https://github.com/deltachat/deltachat-core-rust/pull/5870)).
- Increase timeout for QR generation to 60s ([#5882](https://github.com/deltachat/deltachat-core-rust/pull/5882)).
### Documentation
- Document new `mdns_enabled` behavior (bots do not send MDNs by default).
### CI
- Configure Dependabot to update GitHub Actions.
### Miscellaneous Tasks
- cargo: Bump regex from 1.10.5 to 1.10.6.
- cargo: Bump serde from 1.0.204 to 1.0.205.
- deps: Bump horochx/deploy-via-scp from 1.0.1 to 1.1.0.
- deps: Bump dependabot/fetch-metadata from 1.1.1 to 2.2.0.
- deps: Bump actions/setup-node from 2 to 4.
- Update provider database.
## [1.142.4] - 2024-08-09
### Build system
- Downgrade Tokio to 1.38 to fix Android compilation.
- Use `--locked` with `cargo install`.
### Features / Changes
- Add Config::FixIsChatmail.
- Always move outgoing auto-generated messages to the mvbox.
- Disable requesting MDNs for bots by default.
- Allow using OAuth 2 with SOCKS5.
- Allow autoconfig when SOCKS5 is enabled.
- Update provider database.
- cargo: Update iroh from 0.21 to 0.22 ([#5860](https://github.com/deltachat/deltachat-core-rust/pull/5860)).
### CI
- Update Rust to 1.80.1.
- Update EmbarkStudios/cargo-deny-action.
### Documentation
- Point to active Header Protection draft
### Refactor
- Derive `Default` for `CertificateChecks`.
- Merge imap_certificate_checks and smtp_certificate_checks.
- Remove param_addr_urlencoded argument from get_autoconfig().
- Pass address to moz_autoconfigure() instead of LoginParam.
## [1.142.3] - 2024-08-04
### Build system
- cargo: Update rusqlite and libsqlite3-sys.
- Fix cargo warnings about default-features
- Do not disable "vendored" feature in the workspace.
- cargo: Bump quick-xml from 0.35.0 to 0.36.1.
- cargo: Bump uuid from 1.9.1 to 1.10.0.
- cargo: Bump tokio from 1.38.0 to 1.39.2.
- cargo: Bump env_logger from 0.11.3 to 0.11.5.
- Remove sha2 dependency.
- Remove `backtrace` dependency.
- Remove direct "quinn" dependency.
## [1.142.2] - 2024-08-02
### Features / Changes
- Try only the full email address if username is unspecified.
- Sort DNS results by successful connection timestamp ([#5818](https://github.com/deltachat/deltachat-core-rust/pull/5818)).
### Fixes
- Await the tasks after aborting them.
- Do not reset is_chatmail config on failed reconfiguration.
- Fix compilation on iOS.
- Reset configured_provider on reconfiguration.
### Refactor
- Don't update message state to `OutMdnRcvd` anymore.
### Build system
- Use workspace dependencies to make cargo-deny 0.15.1 happy.
- cargo: Update bytemuck from 0.14.3 to 0.16.3.
- cargo: Bump toml from 0.8.14 to 0.8.15.
- cargo: Bump serde_json from 1.0.120 to 1.0.122.
- cargo: Bump human-panic from 2.0.0 to 2.0.1.
- cargo: Bump thiserror from 1.0.61 to 1.0.63.
- cargo: Bump syn from 2.0.68 to 2.0.72.
- cargo: Bump quoted_printable from 0.5.0 to 0.5.1.
- cargo: Bump serde from 1.0.203 to 1.0.204.
## [1.142.1] - 2024-07-30
### Features / Changes
- Do not reveal sender's language in read receipts ([#5802](https://github.com/deltachat/deltachat-core-rust/pull/5802)).
- Try next DNS resolution result if TLS setup fails.
- Report first error instead of the last on connection failure.
### Fixes
- smtp: Use DNS cache for implicit TLS connections.
- Imex::import_backup: Unpack all blobs before importing a db ([#4307](https://github.com/deltachat/deltachat-core-rust/pull/4307)).
- Import_backup_stream: Fix progress stucking at 0.
- Sql::import: Detach backup db if any step of the import fails.
- Imex::import_backup: Ignore errors from delete_and_reset_all_device_msgs().
- Explicitly close the database on account removal.
### Miscellaneous Tasks
- cargo: Update time from 0.3.34 to 0.3.36.
- cargo: Update iroh from 0.20.0 to 0.21.0.
### Refactor
- Add net/dns submodule.
- Pass single ALPN around instead of ALPN list.
- Replace {IMAP,SMTP,HTTP}_TIMEOUT with a single constant.
- smtp: Unify SMTP connection setup between TLS and STARTTLS.
- imap: Unify IMAP connection setup in Client::connect().
- Move DNS resolution into IMAP and SMTP connect code.
### CI
- Update Rust to 1.80.0.
## [1.142.0] - 2024-07-23
### API-Changes
- deltachat-jsonrpc: Add `pinned` property to `FullChat` and `BasicChat`.
- deltachat-jsonrpc: Allow to set message quote text without referencing quoted message ([#5695](https://github.com/deltachat/deltachat-core-rust/pull/5695)).
### Features / Changes
- cargo: Update iroh from 0.17 to 0.20.
- iroh: Pass direct addresses from Endpoint to Gossip.
- New BACKUP2 transfer protocol.
- Use `[...]` instead of `...` for protected subject.
- Add email address and fingerprint to exported key file names ([#5694](https://github.com/deltachat/deltachat-core-rust/pull/5694)).
- Request `imap` ALPN for IMAP TLS connections and `smtp` ALPN for SMTP TLS connections.
- Limit the size of aggregated WebXDC update to 100 KiB ([#4825](https://github.com/deltachat/deltachat-core-rust/pull/4825)).
- Don't create ad-hoc group on a member removal message ([#5618](https://github.com/deltachat/deltachat-core-rust/pull/5618)).
- Don't unarchive a group on a member removal except SELF ([#5618](https://github.com/deltachat/deltachat-core-rust/pull/5618)).
- Use custom DNS resolver for HTTP(S).
- Promote fallback DNS results to cached on successful use.
- Set summary thumbnail path for WebXDCs to "webxdc-icon://last-msg-id" ([#5782](https://github.com/deltachat/deltachat-core-rust/pull/5782)).
- Do not show the address in invite QR code SVG.
- Report better error from DcKey::from_asc() ([#5539](https://github.com/deltachat/deltachat-core-rust/pull/5539)).
- Contact::create_ex: Don't send sync message if nothing changed ([#5705](https://github.com/deltachat/deltachat-core-rust/pull/5705)).
### Fixes
- `Message::set_quote`: Don't forget to remove `Param::ProtectQuote`.
- Randomize avatar blob filenames to work around caching.
- Correct copy-pasted DCACCOUNT parsing errors message.
- Call `send_sync_msg()` only from the SMTP loop ([#5780](https://github.com/deltachat/deltachat-core-rust/pull/5780)).
- Emit MsgsChanged if the number of unnoticed archived chats could decrease ([#5768](https://github.com/deltachat/deltachat-core-rust/pull/5768)).
- Reject message with forged From even if no valid signatures are found.
### Refactor
- Move key transfer into its own submodule.
- Move TempPathGuard into `tools` and use instead of `DeleteOnDrop`.
- Return error from export_backup() without logging.
- Reduce boilerplate for migration version increment.
### Tests
- Add test for `get_http_response` JSON-RPC call.
### Build system
- node: Pin node-gyp to version 10.1.
### Miscellaneous Tasks
- cargo: Update hashlink to remove allocator-api2 dependency.
- cargo: Update openssl to v0.10.66.
- deps: Bump openssl from 0.10.60 to 0.10.66 in /fuzz.
- cargo: Update `image` crate to 0.25.2.
## [1.141.2] - 2024-07-09
### Features / Changes
- Add `is_muted` config option.
- Parse vcards exported by protonmail ([#5723](https://github.com/deltachat/deltachat-core-rust/pull/5723)).
- Disable sending sync messages for bots ([#5705](https://github.com/deltachat/deltachat-core-rust/pull/5705)).
### Fixes
- Don't fail if going to send plaintext, but some peerstate is missing.
- Correctly sanitize input everywhere ([#5697](https://github.com/deltachat/deltachat-core-rust/pull/5697)).
- Do not try to register non-iOS tokens for heartbeats.
- imap: Reset new_mail if folder is ignored.
- Use and prefer Date from signed message part ([#5716](https://github.com/deltachat/deltachat-core-rust/pull/5716)).
- Distinguish between database errors and no gossip topic.
- MimeFactory::verified: Return true for self-chat.
### Refactor
- `MimeFactory::is_e2ee_guaranteed()`: always respect `Param::ForcePlaintext`.
- Protect from reusing migration versions ([#5719](https://github.com/deltachat/deltachat-core-rust/pull/5719)).
- Move `quota_needs_update` calculation to a separate function ([#5683](https://github.com/deltachat/deltachat-core-rust/pull/5683)).
### Documentation
- Document vCards in the specification ([#5724](https://github.com/deltachat/deltachat-core-rust/pull/5724))
### Miscellaneous Tasks
- cargo: Bump toml from 0.8.13 to 0.8.14.
- cargo: Bump serde_json from 1.0.117 to 1.0.120.
- cargo: Bump syn from 2.0.66 to 2.0.68.
- cargo: Bump async-broadcast from 0.7.0 to 0.7.1.
- cargo: Bump url from 2.5.0 to 2.5.2.
- cargo: Bump log from 0.4.21 to 0.4.22.
- cargo: Bump regex from 1.10.4 to 1.10.5.
- cargo: Bump proptest from 1.4.0 to 1.5.0.
- cargo: Bump uuid from 1.8.0 to 1.9.1.
- cargo: Bump backtrace from 0.3.72 to 0.3.73.
- cargo: Bump quick-xml from 0.31.0 to 0.35.0.
- cargo: Update yerpc to 0.6.2.
- cargo: Update rPGP from 0.11 to 0.13.
## [1.141.1] - 2024-06-27
### Fixes
- Update quota if it's stale, not fresh ([#5683](https://github.com/deltachat/deltachat-core-rust/pull/5683)).
- sql: Assign migration adding msgs.deleted a new number.
### Refactor
- mimefactory: Factor out header confidentiality policy ([#5715](https://github.com/deltachat/deltachat-core-rust/pull/5715)).
- Improve logging during SMTP/IMAP configuration.
## [1.141.0] - 2024-06-24
### API-Changes
- deltachat-jsonrpc: Add `get_chat_securejoin_qr_code()`.
- api!(deltachat-rpc-client): make {Account,Chat}.get_qr_code() return no SVG
This is a breaking change, old method is renamed into `get_qr_code_svg()`.
### Features / Changes
- Prefer references to fully downloaded messages for chat assignment ([#5645](https://github.com/deltachat/deltachat-core-rust/pull/5645)).
- Protect From name for verified chats and To names for encrypted chats ([#5166](https://github.com/deltachat/deltachat-core-rust/pull/5166)).
- Display vCard contact name in the message summary.
- Case-insensitive search for non-ASCII messages ([#5052](https://github.com/deltachat/deltachat-core-rust/pull/5052)).
- Remove subject prefix from ad-hoc group names ([#5385](https://github.com/deltachat/deltachat-core-rust/pull/5385)).
- Replace "Unnamed group" with "👥📧" to avoid translation.
- Sync `Config::MvboxMove` across devices ([#5680](https://github.com/deltachat/deltachat-core-rust/pull/5680)).
- Don't reveal profile data to a not yet verified contact ([#5166](https://github.com/deltachat/deltachat-core-rust/pull/5166)).
- Don't reveal profile data in MDNs ([#5166](https://github.com/deltachat/deltachat-core-rust/pull/5166)).
### Fixes
- Fetch existing messages for bots as `InFresh` ([#4976](https://github.com/deltachat/deltachat-core-rust/pull/4976)).
- Keep tombstones for two days before deleting ([#3685](https://github.com/deltachat/deltachat-core-rust/pull/3685)).
- Housekeeping: Delete MDNs and webxdc status updates for tombstones.
- Delete user-deleted messages on the server even if they show up on IMAP later.
- Do not send sync messages if bcc_self is disabled.
- Don't generate Config sync messages for unconfigured accounts.
- Do not require the Message to render MDN.
### CI
- Update Rust to 1.79.0.
### Documentation
- Remove outdated documentation comment from `send_smtp_messages`.
- Remove misleading configuration comment.
### Miscellaneous Tasks
- Update curve25519-dalek 4.1.x and suppress 3.2.0 warning.
- Update provider database.
### Refactor
- Deduplicate dependency versions ([#5691](https://github.com/deltachat/deltachat-core-rust/pull/5691)).
- Store public key instead of secret key for peer channels.
### Tests
- Image drafted as Viewtype::File is sent as is.
- python: Set delete_server_after=1 ("delete immediately") for bots ([#4976](https://github.com/deltachat/deltachat-core-rust/pull/4976)).
- deltachat-rpc-client: Test that webxdc realtime data is not reordered on the sender.
- python: Wait for bot's DC_EVENT_IMAP_INBOX_IDLE before sending messages to it ([#5699](https://github.com/deltachat/deltachat-core-rust/pull/5699)).
## [1.140.2] - 2024-06-07
### API-Changes
- jsonrpc: Add set_draft_vcard(.., msg_id, contacts).
### Fixes
- Allow fetch_existing_msgs for bots ([#4976](https://github.com/deltachat/deltachat-core-rust/pull/4976)).
- Remove group member locally even if send_msg() fails ([#5508](https://github.com/deltachat/deltachat-core-rust/pull/5508)).
- Revert member addition if the corresponding message couldn't be sent ([#5508](https://github.com/deltachat/deltachat-core-rust/pull/5508)).
- @deltachat/stdio-rpc-server: Make local non-symlinked installation possible by using absolute paths for local dev version ([#5679](https://github.com/deltachat/deltachat-core-rust/pull/5679)).
### Miscellaneous Tasks
- cargo: Bump schemars from 0.8.19 to 0.8.21.
- cargo: Bump backtrace from 0.3.71 to 0.3.72.
### Refactor
- @deltachat/stdio-rpc-server: Use old school require instead of the experimental json import ([#5628](https://github.com/deltachat/deltachat-core-rust/pull/5628)).
### Tests
- Set fetch_existing_msgs for bots ([#4976](https://github.com/deltachat/deltachat-core-rust/pull/4976)).
- Don't leave protected group if some member's key is missing ([#5508](https://github.com/deltachat/deltachat-core-rust/pull/5508)).
## [1.140.1] - 2024-06-05
### Fixes
- Retry sending MDNs on temporary error.
- Set Config::IsChatmail in configure().
- Do not miss new messages while expunging the folder.
- Log messages with `info!` instead of `println!`.
### Documentation
- imap: Document why CLOSE is faster than EXPUNGE.
### Refactor
- imap: Make select_folder() accept non-optional folder.
- Improve SMTP logs and errors.
- Remove unused `select_folder::Error` variants.
### Tests
- deltachat-rpc-client: reenable `log_cli`.
## [1.140.0] - 2024-06-04
### Features / Changes
- Remove limit on number of email recipients for chatmail clients ([#5598](https://github.com/deltachat/deltachat-core-rust/pull/5598)).
- Add config option to enable iroh ([#5607](https://github.com/deltachat/deltachat-core-rust/pull/5607)).
- Map `*.wav` to Viewtype::Audio ([#5633](https://github.com/deltachat/deltachat-core-rust/pull/5633)).
- Add a db index for reactions by msg_id ([#5507](https://github.com/deltachat/deltachat-core-rust/pull/5507)).
### Fixes
- Set Param::Bot for messages on the sender side as well ([#5615](https://github.com/deltachat/deltachat-core-rust/pull/5615)).
- AEAP: Remove old peerstate verified_key instead of removing the whole peerstate ([#5535](https://github.com/deltachat/deltachat-core-rust/pull/5535)).
- Allow creation of groups by outgoing messages without recipients.
- Prefer `Chat-Group-ID` over references for new groups.
- Do not fail to send images with wrong extensions.
### Build system
- Unpin OpenSSL version and update to OpenSSL 3.3.0.
### CI
- Remove cargo-nextest bug workaround.
### Documentation
- Add vCard as supported standard.
- Create_group() does not find chats, only creates them.
- Fix a typo in test_partial_group_consistency().
### Refactor
- Factor create_adhoc_group() call out of create_group().
- Put duplicate code into `lookup_chat_or_create_adhoc_group`.
### Tests
- Fix logging of TestContext created using TestContext::new_alice().
- Refactor `test_alias_*` into 8 separate tests.
## [1.139.6] - 2024-05-25
### Build system
- Update `iroh` to the git version.
- nix: Add iroh-base output hash.
- Upgrade iroh to 0.17.0.
### Fixes
- @deltachat/stdio-rpc-server: Do not set RUST_LOG to "info" by default.
- Acquire write lock on iroh_channels before checking for subscribe_loop.
### Miscellaneous Tasks
- Fix python lint.
- cargo-deny: Remove unused entry from deny.toml.
### Refactor
- Log IMAP connection type on connection failure.
### Tests
- Viewtype::File attachments are sent unchanged and preserve extensions.
- deltachat-rpc-client: Add realtime channel tests.
- deltachat-rpc-client: Regression test for double gossip subscription.
## [1.139.5] - 2024-05-23
### API-Changes
- deltachat-ffi: Make WebXdcRealtimeData data usable in CFFI.
- Add event channel overflow event.
- deltachat-rpc-client: Add EventType.WEBXDC_REALTIME_DATA constant.
- deltachat-rpc-client: Add Message.send_webxdc_realtime_advertisement().
- deltachat-rpc-client: Add Message.send_webxdc_realtime_data().
### Features / Changes
- deltachat-repl: Add start-realtime and send-realtime commands.
### Fixes
- peer_channels: Connect to peers that advertise to you.
- Don't recode images in `Viewtype::File` messages ([#5617](https://github.com/deltachat/deltachat-core-rust/pull/5617)).
### Tests
- peer_channels: Add test_parallel_connect().
- "SecureJoin wait" state and info messages.
## [1.139.4] - 2024-05-21
### Features / Changes
- Scale up contact origins to OutgoingTo when sending a message.
- Add import_vcard() ([#5202](https://github.com/deltachat/deltachat-core-rust/pull/5202)).
### Fixes
- Do not log warning if iroh relay metadata is NIL.
- contact-tools: Parse_vcard: Support `\r\n` newlines.
- Make_vcard: Add authname and key for ContactId::SELF.
### Other
- nix: Add nextest ([#5610](https://github.com/deltachat/deltachat-core-rust/pull/5610)).
## [1.139.3] - 2024-05-20
### API-Changes
- [**breaking**] @deltachat/stdio-rpc-server: change api: don't search in path unless `options.takeVersionFromPATH` is set to `true`
- @deltachat/stdio-rpc-server: remove `DELTA_CHAT_SKIP_PATH` environment variable
- @deltachat/stdio-rpc-server: remove version check / search for dc rpc server in $PATH
- @deltachat/stdio-rpc-server: remove `options.skipSearchInPath`
- @deltachat/stdio-rpc-server: add `options.takeVersionFromPATH`
- deltachat-rpc-client: Add Account.wait_for_incoming_msg().
### Features / Changes
- Replace env_logger with tracing_subscriber.
### Fixes
- Ignore event channel overflows.
- mimeparser: Take the last header of multiple ones with the same name.
- Db migration version 59, it contained an sql syntax error.
- Sql syntax error in db migration 27.
- Log/print exit error of deltachat-rpc-server ([#5601](https://github.com/deltachat/deltachat-core-rust/pull/5601)).
- @deltachat/stdio-rpc-server: set default options for `startDeltaChat`.
- Always convert absolute paths to relative in accounts.toml.
### Refactor
- receive_imf: Do not check for ContactId::UNDEFINED.
- receive_imf: Remove unnecessary check for is_mdn.
- receive_imf: Only call create_or_lookup_group() with allow_creation=true.
- Use let..else in create_or_lookup_group().
- Stop trying to extract chat ID from Message-IDs.
- Do not try to lookup group in create_or_lookup_group().
## [1.139.2] - 2024-05-18
### Build system
- Add repository URL to @deltachat/jsonrpc-client.
## [1.139.1] - 2024-05-18
### CI
- Set `--access public` when publishing to npm.
## [1.139.0] - 2024-05-18
### Features / Changes
- Ephemeral peer channels ([#5346](https://github.com/deltachat/deltachat-core-rust/pull/5346)).
### Fixes
- Save override sender displayname for outgoing messages.
- Do not mark the message as seen if it has `location.kml`.
- @deltachat/stdio-rpc-server: fix version check when deltachat-rpc-server is found in path ([#5579](https://github.com/deltachat/deltachat-core-rust/pull/5579)).
- @deltachat/stdio-rpc-server: fix local desktop development ([#5583](https://github.com/deltachat/deltachat-core-rust/pull/5583)).
- @deltachat/stdio-rpc-server: rename `shutdown` method to `close` and add `muteStdErr` option to mute the stderr output ([#5588](https://github.com/deltachat/deltachat-core-rust/pull/5588))
- @deltachat/stdio-rpc-server: fix `convert_platform.py`: 32bit `i32` -> `ia32` ([#5589](https://github.com/deltachat/deltachat-core-rust/pull/5589))
- @deltachat/stdio-rpc-server: fix example ([#5580](https://github.com/deltachat/deltachat-core-rust/pull/5580))
### API-Changes
- deltachat-jsonrpc: Return vcard contact directly in MessageObject.
- deltachat-jsonrpc: Add api `migrate_account` and `get_blob_dir` ([#5584](https://github.com/deltachat/deltachat-core-rust/pull/5584)).
- deltachat-rpc-client: Add ViewType.VCARD constant.
- deltachat-rpc-client: Add Contact.make_vcard().
- deltachat-rpc-client: Add Chat.send_contact().
### CI
- Publish @deltachat/jsonrpc-client directly to npm.
- Check that constants are always up-to-date.
### Build system
- nix: Add git-cliff to flake.
- nix: Use rust-analyzer nightly
### Miscellaneous Tasks
- cargo: Downgrade libc from 0.2.154 to 0.2.153.
### Tests
- deltachat-rpc-client: Test sending vCard.
## [1.138.5] - 2024-05-16
### API-Changes
- jsonrpc: Add parse_vcard() ([#5202](https://github.com/deltachat/deltachat-core-rust/pull/5202)).
- Add Viewtype::Vcard ([#5202](https://github.com/deltachat/deltachat-core-rust/pull/5202)).
- Add make_vcard() ([#5203](https://github.com/deltachat/deltachat-core-rust/pull/5203)).
### Build system
- Add repository URL to deltachat-rpc-server packages.
### Fixes
- Parsing vCards with avatars exported by Android's "Contacts" app.
### Miscellaneous Tasks
- Rebuild node constants.
### Refactor
- contact-tools: VcardContact: rename display_name to authname.
- VcardContact: Change timestamp type to i64.
## [1.138.4] - 2024-05-15
### CI
- Run actions/setup-node before npm publish.
## [1.138.3] - 2024-05-15
### CI
- Give CI job permission to publish binaries to the release.
## [1.138.2] - 2024-05-15
### API-Changes
- deltachat-rpc-client: Add CONFIG_SYNCED constant.
### CI
- Add npm token to publish deltachat-rpc-server packages.
### Features / Changes
- Reset more settings when configuring a chatmail account.
### Tests
- Set configuration after configure() finishes.
## [1.138.1] - 2024-05-14
### Features / Changes
- Detect XCHATMAIL capability and expose it as `is_chatmail` config.
### Fixes
- Never treat message with Chat-Group-ID as a private reply.
- Always prefer Chat-Group-ID over In-Reply-To and References.
- Ignore parent message if message references itself.
### CI
- Set RUSTUP_WINDOWS_PATH_ADD_BIN to work around `nextest` issue <https://github.com/nextest-rs/nextest/issues/1493>.
- deltachat-rpc-server: Fix upload of npm packages to github releases ([#5564](https://github.com/deltachat/deltachat-core-rust/pull/5564)).
### Refactor
- Add MimeMessage.get_chat_group_id().
- Make MimeMessage.get_header() return Option<&str>.
- sql: Make open flags immutable.
- Resultify token::lookup_or_new().
### Miscellaneous Tasks
- cargo: Bump parking_lot from 0.12.1 to 0.12.2.
- cargo: Bump libc from 0.2.153 to 0.2.154.
- cargo: Bump hickory-resolver from 0.24.0 to 0.24.1.
- cargo: Bump serde_json from 1.0.115 to 1.0.116.
- cargo: Bump human-panic from 1.2.3 to 2.0.0.
- cargo: Bump brotli from 5.0.0 to 6.0.0.
## [1.138.0] - 2024-05-13
### API-Changes
- Add dc_msg_save_file() which saves file copy at the provided path ([#4309](https://github.com/deltachat/deltachat-core-rust/pull/4309)).
- Api!(jsonrpc): replace EphemeralTimer tag "variant" with "kind"
### CI
- Use rsync instead of 3rd party github action.
- Replace `black` with `ruff format`.
- Update Rust to 1.78.0.
### Documentation
- Fix references in Message.set_location() documentation.
- Remove Doxygen markup from Message.has_location().
- Add `location` module documentation.
### Features / Changes
- Delete expired path locations in ephemeral loop.
- Delete orphaned POI locations during housekeeping.
- Parsing vCards for contacts sharing ([#5482](https://github.com/deltachat/deltachat-core-rust/pull/5482)).
- contact-tools: Support parsing profile images from "PHOTO:data:image/jpeg;base64,...".
- contact-tools: Add make_vcard().
- Do not add location markers to messages with non-POI location.
- Make one-to-one chats read-only the first seconds of a SecureJoin ([#5512](https://github.com/deltachat/deltachat-core-rust/pull/5512)).
### Fixes
- Message::set_file_from_bytes(): Set Param::Filename.
- Do not fail to send encrypted quotes to unencrypted chats.
- Never prepend subject to message text when bot receives it.
- Interrupt location loop when new location is stored.
- Correct message viewtype before recoding image blob ([#5496](https://github.com/deltachat/deltachat-core-rust/pull/5496)).
- Delete POI location when disappearing message expires.
- Delete non-POI locations after `delete_device_after`, not immediately.
- Update special chats icons even if they are blocked ([#5509](https://github.com/deltachat/deltachat-core-rust/pull/5509)).
- Use ChatIdBlocked::lookup_by_contact() instead of ChatId's method when applicable.
### Miscellaneous Tasks
- cargo: Bump quote from 1.0.35 to 1.0.36.
- cargo: Bump base64 from 0.22.0 to 0.22.1.
- cargo: Bump serde from 1.0.197 to 1.0.200.
- cargo: Bump async-channel from 2.2.0 to 2.2.1.
- cargo: Bump thiserror from 1.0.58 to 1.0.59.
- cargo: Bump anyhow from 1.0.81 to 1.0.82.
- cargo: Bump chrono from 0.4.37 to 0.4.38.
- cargo: Bump imap-proto from 0.16.4 to 0.16.5.
- cargo: Bump syn from 2.0.57 to 2.0.60.
- cargo: Bump mailparse from 0.14.1 to 0.15.0.
- cargo: Bump schemars from 0.8.16 to 0.8.19.
### Other
- Build ts docs with ci + nix.
- Push docs to delta.chat instead of codespeak
- Implement jsonrpc-docs build in github action
- Rm unneeded rust install from ts docs ci
- Correct folder for js.jsonrpc docs
- Add npm install to upload-docs.yml
- Add : to upload-docs.yml
- Upload-docs npm run => npm run build
- Rm leading slash
- Rm npm install
- Merge pull request #5515 from deltachat/dependabot/cargo/quote-1.0.36
- Merge pull request #5522 from deltachat/dependabot/cargo/chrono-0.4.38
- Merge pull request #5523 from deltachat/dependabot/cargo/mailparse-0.15.0
- Add webxdc internal integration commands in jsonrpc ([#5541](https://github.com/deltachat/deltachat-core-rust/pull/5541))
- Limit quote replies ([#5543](https://github.com/deltachat/deltachat-core-rust/pull/5543))
- Stdio jsonrpc server npm package ([#5332](https://github.com/deltachat/deltachat-core-rust/pull/5332))
### Refactor
- python: Fix ruff 0.4.2 warnings.
- Move `delete_poi_location` to location module and document it.
- Remove allow_keychange.
### Tests
- Explain test_was_seen_recently false-positive and give workaround instructions ([#5474](https://github.com/deltachat/deltachat-core-rust/pull/5474)).
- Test that member is added even if "Member added" is lost.
- Test that POIs are deleted when ephemeral message expires.
- Test ts build on branch
## [1.137.4] - 2024-04-24
### API-Changes
- [**breaking**] Remove `Stream` implementation for `EventEmitter`.
- Experimental Webxdc Integration API, Maps Integration ([#5461](https://github.com/deltachat/deltachat-core-rust/pull/5461)).
### Features / Changes
- Add progressive backoff for failing IMAP connection attempts ([#5443](https://github.com/deltachat/deltachat-core-rust/pull/5443)).
- Replace event channel with broadcast channel.
- Mark contact request messages as seen on IMAP.
### Fixes
- Convert images to RGB8 (without alpha) before encoding into JPEG to fix sending of large RGBA images.
- Don't set `is_bot` for webxdc status updates ([#5445](https://github.com/deltachat/deltachat-core-rust/pull/5445)).
- Do not fail if Autocrypt Setup Message has no encryption preference to fix key transfer from K-9 Mail to Delta Chat.
- Use only CRLF in Autocrypt Setup Message.
- python: Use cached message object if `dc_get_msg()` returns `NULL`.
- python: `Message::is_outgoing`: Don't reload message from db.
- python: `_map_ffi_event`: Always check if `get_message_by_id()` returned None.
- node: Undefine `NAPI_EXPERIMENTAL` to fix build with new clang.
### Build system
- nix: Add `imap-tools` as `deltachat-rpc-client` dependency.
- nix: Add `./deltachat-contact-tools` to sources.
- nix: Update nix flake.
- deps: Update rustls to 0.21.11.
### Documentation
- Update references to SecureJoin protocols.
- Fix broken references in documentation comments.
### Refactor
- imap: remove `RwLock` from `ratelimit`.
- deltachat-ffi: Remove unused `ResultNullableExt`.
- Remove duplicate clippy exceptions.
- Group `use` at the top of the test modules.
## [1.137.3] - 2024-04-16
### API-Changes
@@ -3939,3 +4739,31 @@ https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed
[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
[1.137.3]: https://github.com/deltachat/deltachat-core-rust/compare/v1.137.2...v1.137.3
[1.137.4]: https://github.com/deltachat/deltachat-core-rust/compare/v1.137.3...v1.137.4
[1.138.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.137.4...v1.138.0
[1.138.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.138.0...v1.138.1
[1.138.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.138.1...v1.138.2
[1.138.3]: https://github.com/deltachat/deltachat-core-rust/compare/v1.138.2...v1.138.3
[1.138.4]: https://github.com/deltachat/deltachat-core-rust/compare/v1.138.3...v1.138.4
[1.138.5]: https://github.com/deltachat/deltachat-core-rust/compare/v1.138.4...v1.138.5
[1.139.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.138.5...v1.139.0
[1.139.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.0...v1.139.1
[1.139.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.1...v1.139.2
[1.139.3]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.2...v1.139.3
[1.139.4]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.3...v1.139.4
[1.139.5]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.4...v1.139.5
[1.139.6]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.5...v1.139.6
[1.140.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.139.6...v1.140.0
[1.140.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.140.0...v1.140.1
[1.140.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.140.1...v1.140.2
[1.141.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.140.2...v1.141.0
[1.141.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.141.0...v1.141.1
[1.141.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.141.1...v1.141.2
[1.142.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.141.2...v1.142.0
[1.142.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.142.0...v1.142.1
[1.142.2]: https://github.com/deltachat/deltachat-core-rust/compare/v1.142.1...v1.142.2
[1.142.3]: https://github.com/deltachat/deltachat-core-rust/compare/v1.142.2...v1.142.3
[1.142.4]: https://github.com/deltachat/deltachat-core-rust/compare/v1.142.3...v1.142.4
[1.142.5]: https://github.com/deltachat/deltachat-core-rust/compare/v1.142.4...v1.142.5
[1.142.6]: https://github.com/deltachat/deltachat-core-rust/compare/v1.142.5...v1.142.6
[1.142.7]: https://github.com/deltachat/deltachat-core-rust/compare/v1.142.6...v1.142.7

2446
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat"
version = "1.137.3"
version = "1.142.7"
edition = "2021"
license = "MPL-2.0"
rust-version = "1.77"
@@ -34,93 +34,83 @@ strip = true
[dependencies]
deltachat_derive = { path = "./deltachat_derive" }
deltachat-time = { path = "./deltachat-time" }
deltachat-contact-tools = { path = "./deltachat-contact-tools" }
deltachat-contact-tools = { workspace = true }
format-flowed = { path = "./format-flowed" }
ratelimit = { path = "./deltachat-ratelimit" }
anyhow = { workspace = true }
async-channel = "2.0.0"
async-broadcast = "0.7.1"
async-channel = { workspace = true }
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.22"
brotli = { version = "5", default-features=false, features = ["std"] }
chrono = { version = "0.4.37", default-features=false, features = ["clock", "std"] }
base64 = { workspace = true }
brotli = { version = "6", default-features=false, features = ["std"] }
chrono = { workspace = true, features = ["alloc", "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.9"
fd-lock = "4"
futures = "0.3"
futures-lite = "2.3.0"
futures = { workspace = true }
futures-lite = { workspace = true }
hex = "0.4.0"
hickory-resolver = "0.24"
humansize = "2"
image = { version = "0.25.1", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] }
iroh = { version = "0.4.2", default-features = false }
iroh_old = { version = "0.4.2", default-features = false, package = "iroh"}
iroh-net = { version = "0.22.0", default-features = false }
iroh-gossip = { version = "0.22.0", default-features = false, features = ["net"] }
kamadak-exif = "0.5.3"
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
libc = "0.2"
mailparse = "0.14"
libc = { workspace = true }
mailparse = "0.15"
mime = "0.3.17"
num_cpus = "1.16"
num-derive = "0.4"
num-traits = "0.2"
num-traits = { workspace = true }
once_cell = { workspace = true }
percent-encoding = "2.3"
parking_lot = "0.12"
pgp = { version = "0.11", default-features = false }
pin-project = "1"
pretty_env_logger = { version = "0.5", optional = true }
pgp = { version = "0.13.2", default-features = false }
qrcodegen = "1.7.0"
quick-xml = "0.31"
quick-xml = "0.36"
quoted_printable = "0.5"
rand = "0.8"
rand = { workspace = true }
regex = { workspace = true }
reqwest = { version = "0.12.2", features = ["json"] }
reqwest = { version = "0.12.5", features = ["json"] }
rusqlite = { workspace = true, features = ["sqlcipher"] }
rust-hsluv = "0.1"
sanitize-filename = "0.5"
serde_json = "1"
serde = { version = "1.0", features = ["derive"] }
sanitize-filename = { workspace = true }
serde_json = { workspace = true }
serde = { workspace = true, features = ["derive"] }
sha-1 = "0.10"
sha2 = "0.10"
smallvec = "1.13.2"
strum = "0.26"
strum_macros = "0.26"
tagger = "4.3.4"
textwrap = "0.16.1"
thiserror = "1"
tokio = { version = "1.37.0", features = ["fs", "rt-multi-thread", "macros"] }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["fs", "rt-multi-thread", "macros"] }
tokio-io-timeout = "1.2.0"
tokio-stream = { version = "0.1.15", features = ["fs"] }
tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar
tokio-util = "0.7.9"
tokio-util = { workspace = true }
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.
ansi_term = { workspace = true }
anyhow = { workspace = true, features = ["backtrace"] } # Enable `backtrace` feature in tests.
criterion = { version = "0.5.1", features = ["async_tokio"] }
futures-lite = "2.3.0"
log = "0.4"
pretty_env_logger = "0.5"
futures-lite = { workspace = true }
log = { workspace = true }
proptest = { version = "1", default-features = false, features = ["std"] }
tempfile = "3"
tempfile = { workspace = true }
testdir = "0.9.0"
tokio = { version = "1.37.0", features = ["parking_lot", "rt-multi-thread", "macros"] }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
pretty_assertions = "1.3.0"
[workspace]
@@ -166,9 +156,38 @@ harness = false
[workspace.dependencies]
anyhow = "1"
ansi_term = "0.12.1"
async-channel = "2.3.1"
base64 = "0.22"
chrono = { version = "0.4.38", default-features = false }
deltachat-contact-tools = { path = "deltachat-contact-tools" }
deltachat-jsonrpc = { path = "deltachat-jsonrpc" }
deltachat = { path = "." }
futures = "0.3.30"
futures-lite = "2.3.0"
libc = "0.2"
log = "0.4"
num-traits = "0.2"
once_cell = "1.18.0"
rand = "0.8"
regex = "1.10"
rusqlite = { version = "0.31" }
rusqlite = "0.32"
sanitize-filename = "0.5"
serde_json = "1"
serde = "1.0"
tempfile = "3.10.1"
thiserror = "1"
# 1.38 is the latest version before `mio` dependency update
# that broke compilation with Android NDK r23c and r24.
# Version 1.39.0 cannot be compiled using these NDKs,
# see issue <https://github.com/tokio-rs/tokio/issues/6748>
# for details.
tokio = "~1.38.1"
tokio-util = "0.7.11"
tracing-subscriber = "0.3"
yerpc = "0.6.2"
[features]
default = ["vendored"]
@@ -178,3 +197,6 @@ vendored = [
"rusqlite/bundled-sqlcipher-vendored-openssl",
"reqwest/native-tls-vendored"
]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }

View File

@@ -30,13 +30,13 @@ $ curl https://sh.rustup.rs -sSf | sh
Compile and run Delta Chat Core command line utility, using `cargo`:
```
$ cargo run -p deltachat-repl -- ~/deltachat-db
$ cargo run --locked -p deltachat-repl -- ~/deltachat-db
```
where ~/deltachat-db is the database file. Delta Chat will create it if it does not exist.
Optionally, install `deltachat-repl` binary with
```
$ cargo install --path deltachat-repl/
$ cargo install --locked --path deltachat-repl/
```
and run as
```

View File

@@ -4,21 +4,18 @@ For example, to release version 1.116.0 of the core, do the following steps.
1. Resolve all [blocker issues](https://github.com/deltachat/deltachat-core-rust/labels/blocker).
2. Run `npm run build:core:constants` in the root of the repository
and commit generated `node/constants.js`, `node/events.js` and `node/lib/constants.js`.
2. Update the changelog: `git cliff --unreleased --tag 1.116.0 --prepend CHANGELOG.md` or `git cliff -u -t 1.116.0 -p CHANGELOG.md`.
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. add a link to compare previous with current version to the end of CHANGELOG.md:
3. 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. Update the version by running `scripts/set_core_version.py 1.116.0`.
4. 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`.
5. 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.
7. Tag the release: `git tag -a v1.116.0`.
6. Tag the release: `git tag -a v1.116.0`.
8. Push the release tag: `git push origin v1.116.0`.
7. Push the release tag: `git push origin v1.116.0`.
9. Create a GitHub release: `gh release create v1.116.0 -n ''`.
8. Create a GitHub release: `gh release create v1.116.0 -n ''`.

View File

@@ -1,3 +1,4 @@
#![recursion_limit = "256"]
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use deltachat::contact::Contact;
use deltachat::context::Context;

View File

@@ -1,3 +1,4 @@
#![recursion_limit = "256"]
use std::path::PathBuf;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

View File

@@ -1,3 +1,4 @@
#![recursion_limit = "256"]
use std::path::Path;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

View File

@@ -1,3 +1,4 @@
#![recursion_limit = "256"]
use std::path::Path;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

View File

@@ -1,3 +1,4 @@
#![recursion_limit = "256"]
use std::path::PathBuf;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

View File

@@ -1,3 +1,4 @@
#![recursion_limit = "256"]
use std::path::Path;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

View File

@@ -1,3 +1,4 @@
#![recursion_limit = "256"]
use criterion::{criterion_group, criterion_main, Criterion};
use deltachat::context::Context;

View File

@@ -1,10 +1,9 @@
[package]
name = "deltachat-contact-tools"
version = "0.1.0"
version = "0.0.0" # No semver-stable versioning
edition = "2021"
description = "Contact-related tools, like parsing vcards and sanitizing name and address"
description = "Contact-related tools, like parsing vcards and sanitizing name and address. Meant for internal use in the deltachat crate."
license = "MPL-2.0"
# TODO maybe it should be called "deltachat-text-utils" or similar?
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -13,6 +12,7 @@ anyhow = { workspace = true }
once_cell = { workspace = true }
regex = { workspace = true }
rusqlite = { workspace = true } # Needed in order to `impl rusqlite::types::ToSql for EmailAddress`. Could easily be put behind a feature.
chrono = { workspace = true, features = ["alloc", "clock", "std"] }
[dev-dependencies]
anyhow = { workspace = true, features = ["backtrace"] } # Enable `backtrace` feature in tests.

View File

@@ -22,17 +22,210 @@
clippy::bool_assert_comparison,
clippy::manual_split_once,
clippy::format_push_string,
clippy::bool_to_int_with_if
clippy::bool_to_int_with_if,
clippy::manual_range_contains
)]
use std::fmt;
use std::ops::Deref;
use anyhow::bail;
use anyhow::Context as _;
use anyhow::Result;
use chrono::{DateTime, NaiveDateTime};
use once_cell::sync::Lazy;
use regex::Regex;
#[derive(Debug)]
/// A Contact, as represented in a VCard.
pub struct VcardContact {
/// The email address, vcard property `email`
pub addr: String,
/// This must be the name authorized by the contact itself, not a locally given name. Vcard
/// property `fn`. Can be empty, one should use `display_name()` to obtain the display name.
pub authname: String,
/// The contact's public PGP key in Base64, vcard property `key`
pub key: Option<String>,
/// The contact's profile image (=avatar) in Base64, vcard property `photo`
pub profile_image: Option<String>,
/// The timestamp when the vcard was created / last updated, vcard property `rev`
pub timestamp: Result<i64>,
}
impl VcardContact {
/// Returns the contact's display name.
pub fn display_name(&self) -> &str {
match self.authname.is_empty() {
false => &self.authname,
true => &self.addr,
}
}
}
/// Returns a vCard containing given contacts.
///
/// Calling [`parse_vcard()`] on the returned result is a reverse operation.
pub fn make_vcard(contacts: &[VcardContact]) -> String {
fn format_timestamp(c: &VcardContact) -> Option<String> {
let timestamp = *c.timestamp.as_ref().ok()?;
let datetime = DateTime::from_timestamp(timestamp, 0)?;
Some(datetime.format("%Y%m%dT%H%M%SZ").to_string())
}
let mut res = "".to_string();
for c in contacts {
let addr = &c.addr;
let display_name = c.display_name();
res += &format!(
"BEGIN:VCARD\n\
VERSION:4.0\n\
EMAIL:{addr}\n\
FN:{display_name}\n"
);
if let Some(key) = &c.key {
res += &format!("KEY:data:application/pgp-keys;base64,{key}\n");
}
if let Some(profile_image) = &c.profile_image {
res += &format!("PHOTO:data:image/jpeg;base64,{profile_image}\n");
}
if let Some(timestamp) = format_timestamp(c) {
res += &format!("REV:{timestamp}\n");
}
res += "END:VCARD\n";
}
res
}
/// Parses `VcardContact`s from a given `&str`.
pub fn parse_vcard(vcard: &str) -> Vec<VcardContact> {
fn remove_prefix<'a>(s: &'a str, prefix: &str) -> Option<&'a str> {
let start_of_s = s.get(..prefix.len())?;
if start_of_s.eq_ignore_ascii_case(prefix) {
s.get(prefix.len()..)
} else {
None
}
}
fn vcard_property<'a>(s: &'a str, property: &str) -> Option<&'a str> {
let remainder = remove_prefix(s, property)?;
// If `s` is `EMAIL;TYPE=work:alice@example.com` and `property` is `EMAIL`,
// then `remainder` is now `;TYPE=work:alice@example.com`
// Note: This doesn't handle the case where there are quotes around a colon,
// like `NAME;Foo="Some quoted text: that contains a colon":value`.
// This could be improved in the future, but for now, the parsing is good enough.
let (params, value) = remainder.split_once(':')?;
// In the example from above, `params` is now `;TYPE=work`
// and `value` is now `alice@example.com`
if params
.chars()
.next()
.filter(|c| !c.is_ascii_punctuation() || *c == '_')
.is_some()
{
// `s` started with `property`, but the next character after it was not punctuation,
// so this line's property is actually something else
return None;
}
Some(value)
}
fn parse_datetime(datetime: &str) -> Result<i64> {
// According to https://www.rfc-editor.org/rfc/rfc6350#section-4.3.5, the timestamp
// is in ISO.8601.2004 format. DateTime::parse_from_rfc3339() apparently parses
// ISO.8601, but fails to parse any of the examples given.
// So, instead just parse using a format string.
// Parses 19961022T140000Z, 19961022T140000-05, or 19961022T140000-0500.
let timestamp = match DateTime::parse_from_str(datetime, "%Y%m%dT%H%M%S%#z") {
Ok(datetime) => datetime.timestamp(),
// Parses 19961022T140000.
Err(e) => match NaiveDateTime::parse_from_str(datetime, "%Y%m%dT%H%M%S") {
Ok(datetime) => datetime
.and_local_timezone(chrono::offset::Local)
.single()
.context("Could not apply local timezone to parsed date and time")?
.timestamp(),
Err(_) => return Err(e.into()),
},
};
Ok(timestamp)
}
// Remove line folding, see https://datatracker.ietf.org/doc/html/rfc6350#section-3.2
static NEWLINE_AND_SPACE_OR_TAB: Lazy<Regex> = Lazy::new(|| Regex::new("\r?\n[\t ]").unwrap());
let unfolded_lines = NEWLINE_AND_SPACE_OR_TAB.replace_all(vcard, "");
let mut lines = unfolded_lines.lines().peekable();
let mut contacts = Vec::new();
while lines.peek().is_some() {
// Skip to the start of the vcard:
for line in lines.by_ref() {
if line.eq_ignore_ascii_case("BEGIN:VCARD") {
break;
}
}
let mut display_name = None;
let mut addr = None;
let mut key = None;
let mut photo = None;
let mut datetime = None;
for mut line in lines.by_ref() {
if let Some(remainder) = remove_prefix(line, "item1.") {
// Remove the group name, if the group is called "item1".
// If necessary, we can improve this to also remove groups that are called something different that "item1".
//
// Search "group name" at https://datatracker.ietf.org/doc/html/rfc6350 for more infos.
line = remainder;
}
if let Some(email) = vcard_property(line, "email") {
addr.get_or_insert(email);
} else if let Some(name) = vcard_property(line, "fn") {
display_name.get_or_insert(name);
} else if let Some(k) = remove_prefix(line, "KEY;PGP;ENCODING=BASE64:")
.or_else(|| remove_prefix(line, "KEY;TYPE=PGP;ENCODING=b:"))
.or_else(|| remove_prefix(line, "KEY:data:application/pgp-keys;base64,"))
.or_else(|| remove_prefix(line, "KEY;PREF=1:data:application/pgp-keys;base64,"))
{
key.get_or_insert(k);
} else if let Some(p) = remove_prefix(line, "PHOTO;JPEG;ENCODING=BASE64:")
.or_else(|| remove_prefix(line, "PHOTO;ENCODING=BASE64;JPEG:"))
.or_else(|| remove_prefix(line, "PHOTO;TYPE=JPEG;ENCODING=b:"))
.or_else(|| remove_prefix(line, "PHOTO;ENCODING=b;TYPE=JPEG:"))
.or_else(|| remove_prefix(line, "PHOTO;ENCODING=BASE64;TYPE=JPEG:"))
.or_else(|| remove_prefix(line, "PHOTO;TYPE=JPEG;ENCODING=BASE64:"))
.or_else(|| remove_prefix(line, "PHOTO:data:image/jpeg;base64,"))
{
photo.get_or_insert(p);
} else if let Some(rev) = vcard_property(line, "rev") {
datetime.get_or_insert(rev);
} else if line.eq_ignore_ascii_case("END:VCARD") {
break;
}
}
let (authname, addr) =
sanitize_name_and_addr(display_name.unwrap_or(""), addr.unwrap_or(""));
contacts.push(VcardContact {
authname,
addr,
key: key.map(|s| s.to_string()),
profile_image: photo.map(|s| s.to_string()),
timestamp: datetime
.context("No timestamp in vcard")
.and_then(parse_datetime),
});
}
contacts
}
/// Valid contact address.
#[derive(Debug, Clone)]
pub struct ContactAddress(String);
@@ -78,54 +271,109 @@ impl rusqlite::types::ToSql for ContactAddress {
}
}
/// Make the name and address
/// Takes a name and an address and sanitizes them:
/// - Extracts a name from the addr if the addr is in form "Alice <alice@example.org>"
/// - Removes special characters from the name, see [`sanitize_name()`]
/// - Removes the name if it is equal to the address by setting it to ""
pub fn sanitize_name_and_addr(name: &str, addr: &str) -> (String, String) {
static ADDR_WITH_NAME_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new("(.*)<(.*)>").unwrap());
if let Some(captures) = ADDR_WITH_NAME_REGEX.captures(addr.as_ref()) {
let (name, addr) = if let Some(captures) = ADDR_WITH_NAME_REGEX.captures(addr.as_ref()) {
(
if name.is_empty() {
strip_rtlo_characters(
&captures
.get(1)
.map_or("".to_string(), |m| normalize_name(m.as_str())),
)
captures.get(1).map_or("", |m| m.as_str())
} else {
strip_rtlo_characters(name)
name
},
captures
.get(2)
.map_or("".to_string(), |m| m.as_str().to_string()),
)
} else {
(strip_rtlo_characters(name), addr.to_string())
(name, addr.to_string())
};
let mut name = sanitize_name(name);
// If the 'display name' is just the address, remove it:
// Otherwise, the contact would sometimes be shown as "alice@example.com (alice@example.com)" (see `get_name_n_addr()`).
// If the display name is empty, DC will just show the address when it needs a display name.
if name == addr {
name = "".to_string();
}
(name, addr)
}
/// Sanitizes a name.
///
/// - Removes newlines and trims the string
/// - Removes quotes (come from some bad MUA implementations)
/// - Removes potentially-malicious bidi characters
pub fn sanitize_name(name: &str) -> String {
let name = sanitize_single_line(name);
match name.as_bytes() {
[b'\'', .., b'\''] | [b'\"', .., b'\"'] | [b'<', .., b'>'] => name
.get(1..name.len() - 1)
.map_or("".to_string(), |s| s.trim().to_string()),
_ => name.to_string(),
}
}
/// Normalize a name.
/// Sanitizes user input
///
/// - Remove quotes (come from some bad MUA implementations)
/// - Trims the resulting string
///
/// Typically, this function is not needed as it is called implicitly by `Contact::add_address_book`.
pub fn normalize_name(full_name: &str) -> String {
let full_name = full_name.trim();
if full_name.is_empty() {
return full_name.into();
}
match full_name.as_bytes() {
[b'\'', .., b'\''] | [b'\"', .., b'\"'] | [b'<', .., b'>'] => full_name
.get(1..full_name.len() - 1)
.map_or("".to_string(), |s| s.trim().to_string()),
_ => full_name.to_string(),
}
/// - Removes newlines and trims the string
/// - Removes potentially-malicious bidi characters
pub fn sanitize_single_line(input: &str) -> String {
sanitize_bidi_characters(input.replace(['\n', '\r'], " ").trim())
}
const RTLO_CHARACTERS: [char; 5] = ['\u{202A}', '\u{202B}', '\u{202C}', '\u{202D}', '\u{202E}'];
/// This method strips all occurrences of the RTLO Unicode character.
/// [Why is this needed](https://github.com/deltachat/deltachat-core-rust/issues/3479)?
pub fn strip_rtlo_characters(input_str: &str) -> String {
input_str.replace(|char| RTLO_CHARACTERS.contains(&char), "")
const ISOLATE_CHARACTERS: [char; 3] = ['\u{2066}', '\u{2067}', '\u{2068}'];
const POP_ISOLATE_CHARACTER: char = '\u{2069}';
/// Some control unicode characters can influence whether adjacent text is shown from
/// left to right or from right to left.
///
/// Since user input is not supposed to influence how adjacent text looks,
/// this function removes some of these characters.
///
/// Also see https://github.com/deltachat/deltachat-core-rust/issues/3479.
pub fn sanitize_bidi_characters(input_str: &str) -> String {
// RTLO_CHARACTERS are apparently rarely used in practice.
// They can impact all following text, so, better remove them all:
let input_str = input_str.replace(|char| RTLO_CHARACTERS.contains(&char), "");
// If the ISOLATE characters are not ended with a POP DIRECTIONAL ISOLATE character,
// we regard the input as potentially malicious and simply remove all ISOLATE characters.
// See https://en.wikipedia.org/wiki/Bidirectional_text#Unicode_bidi_support
// and https://www.w3.org/International/questions/qa-bidi-unicode-controls.en
// for an explanation about ISOLATE characters.
fn isolate_characters_are_valid(input_str: &str) -> bool {
let mut isolate_character_nesting: i32 = 0;
for char in input_str.chars() {
if ISOLATE_CHARACTERS.contains(&char) {
isolate_character_nesting += 1;
} else if char == POP_ISOLATE_CHARACTER {
isolate_character_nesting -= 1;
}
// According to Wikipedia, 125 levels are allowed:
// https://en.wikipedia.org/wiki/Unicode_control_characters
// (although, in practice, we could also significantly lower this number)
if isolate_character_nesting < 0 || isolate_character_nesting > 125 {
return false;
}
}
isolate_character_nesting == 0
}
if isolate_characters_are_valid(&input_str) {
input_str
} else {
input_str.replace(
|char| ISOLATE_CHARACTERS.contains(&char) || POP_ISOLATE_CHARACTER == char,
"",
)
}
}
/// Returns false if addr is an invalid address, otherwise true.
@@ -230,8 +478,124 @@ impl rusqlite::types::ToSql for EmailAddress {
#[cfg(test)]
mod tests {
use chrono::TimeZone;
use super::*;
#[test]
fn test_vcard_thunderbird() {
let contacts = parse_vcard(
"BEGIN:VCARD
VERSION:4.0
FN:'Alice Mueller'
EMAIL;PREF=1:alice.mueller@posteo.de
UID:a8083264-ca47-4be7-98a8-8ec3db1447ca
END:VCARD
BEGIN:VCARD
VERSION:4.0
FN:'bobzzz@freenet.de'
EMAIL;PREF=1:bobzzz@freenet.de
UID:cac4fef4-6351-4854-bbe4-9b6df857eaed
END:VCARD
",
);
assert_eq!(contacts[0].addr, "alice.mueller@posteo.de".to_string());
assert_eq!(contacts[0].authname, "Alice Mueller".to_string());
assert_eq!(contacts[0].key, None);
assert_eq!(contacts[0].profile_image, None);
assert!(contacts[0].timestamp.is_err());
assert_eq!(contacts[1].addr, "bobzzz@freenet.de".to_string());
assert_eq!(contacts[1].authname, "".to_string());
assert_eq!(contacts[1].key, None);
assert_eq!(contacts[1].profile_image, None);
assert!(contacts[1].timestamp.is_err());
assert_eq!(contacts.len(), 2);
}
#[test]
fn test_vcard_simple_example() {
let contacts = parse_vcard(
"BEGIN:VCARD
VERSION:4.0
FN:Alice Wonderland
N:Wonderland;Alice;;;Ms.
GENDER:W
EMAIL;TYPE=work:alice@example.com
KEY;TYPE=PGP;ENCODING=b:[base64-data]
REV:20240418T184242Z
END:VCARD",
);
assert_eq!(contacts[0].addr, "alice@example.com".to_string());
assert_eq!(contacts[0].authname, "Alice Wonderland".to_string());
assert_eq!(contacts[0].key, Some("[base64-data]".to_string()));
assert_eq!(contacts[0].profile_image, None);
assert_eq!(*contacts[0].timestamp.as_ref().unwrap(), 1713465762);
assert_eq!(contacts.len(), 1);
}
#[test]
fn test_make_and_parse_vcard() {
let contacts = [
VcardContact {
addr: "alice@example.org".to_string(),
authname: "Alice Wonderland".to_string(),
key: Some("[base64-data]".to_string()),
profile_image: Some("image in Base64".to_string()),
timestamp: Ok(1713465762),
},
VcardContact {
addr: "bob@example.com".to_string(),
authname: "".to_string(),
key: None,
profile_image: None,
timestamp: Ok(0),
},
];
let items = [
"BEGIN:VCARD\n\
VERSION:4.0\n\
EMAIL:alice@example.org\n\
FN:Alice Wonderland\n\
KEY:data:application/pgp-keys;base64,[base64-data]\n\
PHOTO:data:image/jpeg;base64,image in Base64\n\
REV:20240418T184242Z\n\
END:VCARD\n",
"BEGIN:VCARD\n\
VERSION:4.0\n\
EMAIL:bob@example.com\n\
FN:bob@example.com\n\
REV:19700101T000000Z\n\
END:VCARD\n",
];
let mut expected = "".to_string();
for len in 0..=contacts.len() {
let contacts = &contacts[0..len];
let vcard = make_vcard(contacts);
if len > 0 {
expected += items[len - 1];
}
assert_eq!(vcard, expected);
let parsed = parse_vcard(&vcard);
assert_eq!(parsed.len(), contacts.len());
for i in 0..parsed.len() {
assert_eq!(parsed[i].addr, contacts[i].addr);
assert_eq!(parsed[i].authname, contacts[i].authname);
assert_eq!(parsed[i].key, contacts[i].key);
assert_eq!(parsed[i].profile_image, contacts[i].profile_image);
assert_eq!(
parsed[i].timestamp.as_ref().unwrap(),
contacts[i].timestamp.as_ref().unwrap()
);
}
}
}
#[test]
fn test_contact_address() -> Result<()> {
let alice_addr = "alice@example.org";
@@ -277,4 +641,170 @@ mod tests {
assert!(EmailAddress::new("u@tt").is_ok());
assert_eq!(EmailAddress::new("@d.tt").is_ok(), false);
}
#[test]
fn test_vcard_android() {
let contacts = parse_vcard(
"BEGIN:VCARD
VERSION:2.1
N:;Bob;;;
FN:Bob
TEL;CELL:+1-234-567-890
EMAIL;HOME:bob@example.org
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:;Alice;;;
FN:Alice
EMAIL;HOME:alice@example.org
END:VCARD
",
);
assert_eq!(contacts[0].addr, "bob@example.org".to_string());
assert_eq!(contacts[0].authname, "Bob".to_string());
assert_eq!(contacts[0].key, None);
assert_eq!(contacts[0].profile_image, None);
assert_eq!(contacts[1].addr, "alice@example.org".to_string());
assert_eq!(contacts[1].authname, "Alice".to_string());
assert_eq!(contacts[1].key, None);
assert_eq!(contacts[1].profile_image, None);
assert_eq!(contacts.len(), 2);
}
#[test]
fn test_vcard_local_datetime() {
let contacts = parse_vcard(
"BEGIN:VCARD\n\
VERSION:4.0\n\
FN:Alice Wonderland\n\
EMAIL;TYPE=work:alice@example.org\n\
REV:20240418T184242\n\
END:VCARD",
);
assert_eq!(contacts.len(), 1);
assert_eq!(contacts[0].addr, "alice@example.org".to_string());
assert_eq!(contacts[0].authname, "Alice Wonderland".to_string());
assert_eq!(
*contacts[0].timestamp.as_ref().unwrap(),
chrono::offset::Local
.with_ymd_and_hms(2024, 4, 18, 18, 42, 42)
.unwrap()
.timestamp()
);
}
#[test]
fn test_vcard_with_base64_avatar() {
// This is not an actual base64-encoded avatar, it's just to test the parsing.
// This one is Android-like.
let vcard0 = "BEGIN:VCARD
VERSION:2.1
N:;Bob;;;
FN:Bob
EMAIL;HOME:bob@example.org
PHOTO;ENCODING=BASE64;JPEG:/9j/4AAQSkZJRgABAQAAAQABAAD/4gIoSUNDX1BST0ZJTEU
AAQEAAAIYAAAAAAQwAABtbnRyUkdCIFhZWiAAAAAAAAAAAAAAAABhY3NwAAAAAAAAAAAAAAAA
L8bRuAJYoZUYrI4ZY3VWwxw4Ay28AAGBISScmf/2Q==
END:VCARD
";
// This one is DOS-like.
let vcard1 = vcard0.replace('\n', "\r\n");
for vcard in [vcard0, vcard1.as_str()] {
let contacts = parse_vcard(vcard);
assert_eq!(contacts.len(), 1);
assert_eq!(contacts[0].addr, "bob@example.org".to_string());
assert_eq!(contacts[0].authname, "Bob".to_string());
assert_eq!(contacts[0].key, None);
assert_eq!(contacts[0].profile_image.as_deref().unwrap(), "/9j/4AAQSkZJRgABAQAAAQABAAD/4gIoSUNDX1BST0ZJTEUAAQEAAAIYAAAAAAQwAABtbnRyUkdCIFhZWiAAAAAAAAAAAAAAAABhY3NwAAAAAAAAAAAAAAAAL8bRuAJYoZUYrI4ZY3VWwxw4Ay28AAGBISScmf/2Q==");
}
}
#[test]
fn test_protonmail_vcard() {
let contacts = parse_vcard(
"BEGIN:VCARD
VERSION:4.0
FN;PREF=1:Alice Wonderland
UID:proton-web-03747582-328d-38dc-5ddd-000000000000
ITEM1.EMAIL;PREF=1:alice@example.org
ITEM1.KEY;PREF=1:data:application/pgp-keys;base64,aaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
ITEM1.KEY;PREF=2:data:application/pgp-keys;base64,bbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
ITEM1.X-PM-ENCRYPT:true
ITEM1.X-PM-SIGN:true
END:VCARD",
);
assert_eq!(contacts.len(), 1);
assert_eq!(&contacts[0].addr, "alice@example.org");
assert_eq!(&contacts[0].authname, "Alice Wonderland");
assert_eq!(contacts[0].key.as_ref().unwrap(), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
assert!(contacts[0].timestamp.is_err());
assert_eq!(contacts[0].profile_image, None);
}
#[test]
fn test_sanitize_name() {
assert_eq!(&sanitize_name(" hello world "), "hello world");
assert_eq!(&sanitize_name("<"), "<");
assert_eq!(&sanitize_name(">"), ">");
assert_eq!(&sanitize_name("'"), "'");
assert_eq!(&sanitize_name("\""), "\"");
}
#[test]
fn test_sanitize_single_line() {
assert_eq!(sanitize_single_line("Hi\naiae "), "Hi aiae");
assert_eq!(sanitize_single_line("\r\nahte\n\r"), "ahte");
}
#[test]
fn test_sanitize_bidi_characters() {
// Legit inputs:
assert_eq!(
&sanitize_bidi_characters("Tes\u{2067}ting Delta Chat\u{2069}"),
"Tes\u{2067}ting Delta Chat\u{2069}"
);
assert_eq!(
&sanitize_bidi_characters("Tes\u{2067}ting \u{2068} Delta Chat\u{2069}\u{2069}"),
"Tes\u{2067}ting \u{2068} Delta Chat\u{2069}\u{2069}"
);
assert_eq!(
&sanitize_bidi_characters("Tes\u{2067}ting\u{2069} Delta Chat\u{2067}\u{2069}"),
"Tes\u{2067}ting\u{2069} Delta Chat\u{2067}\u{2069}"
);
// Potentially-malicious inputs:
assert_eq!(
&sanitize_bidi_characters("Tes\u{202C}ting Delta Chat"),
"Testing Delta Chat"
);
assert_eq!(
&sanitize_bidi_characters("Testing Delta Chat\u{2069}"),
"Testing Delta Chat"
);
assert_eq!(
&sanitize_bidi_characters("Tes\u{2067}ting Delta Chat"),
"Testing Delta Chat"
);
assert_eq!(
&sanitize_bidi_characters("Tes\u{2069}ting Delta Chat\u{2067}"),
"Testing Delta Chat"
);
assert_eq!(
&sanitize_bidi_characters("Tes\u{2068}ting Delta Chat"),
"Testing Delta Chat"
);
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat_ffi"
version = "1.137.3"
version = "1.142.7"
description = "Deltachat FFI"
edition = "2018"
readme = "README.md"
@@ -14,21 +14,21 @@ name = "deltachat"
crate-type = ["cdylib", "staticlib"]
[dependencies]
deltachat = { path = "../", default-features = false }
deltachat-jsonrpc = { path = "../deltachat-jsonrpc", optional = true }
libc = "0.2"
human-panic = { version = "1", default-features = false }
num-traits = "0.2"
serde_json = "1.0"
tokio = { version = "1.37.0", features = ["rt-multi-thread"] }
anyhow = "1"
thiserror = "1"
rand = "0.8"
once_cell = "1.18.0"
yerpc = { version = "0.5.1", features = ["anyhow_expose"] }
deltachat = { workspace = true, default-features = false }
deltachat-jsonrpc = { workspace = true, optional = true }
libc = { workspace = true }
human-panic = { version = "2", default-features = false }
num-traits = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread"] }
anyhow = { workspace = true }
thiserror = { workspace = true }
rand = { workspace = true }
once_cell = { workspace = true }
yerpc = { workspace = true, features = ["anyhow_expose"] }
[features]
default = ["vendored"]
vendored = ["deltachat/vendored"]
vendored = ["deltachat/vendored", "deltachat-jsonrpc/vendored"]
jsonrpc = ["dep:deltachat-jsonrpc"]

View File

@@ -362,8 +362,12 @@ uint32_t dc_get_id (dc_context_t* context);
* Must be freed using dc_event_emitter_unref() after usage.
*
* Note: Use only one event emitter per context.
* Having more than one event emitter running at the same time on the same context
* will result in events being randomly delivered to one of the emitters.
* The result of having multiple event emitters is unspecified.
* Currently events are broadcasted to all existing event emitters,
* but previous versions delivered events to only one event emitter
* and this behavior may change again in the future.
* Events emitted before creation of event emitter
* may or may not be available to event emitter.
*/
dc_event_emitter_t* dc_get_event_emitter(dc_context_t* context);
@@ -405,7 +409,7 @@ char* dc_get_blobdir (const dc_context_t* context);
* - `socks5_user` = SOCKS5 proxy username
* - `socks5_password` = SOCKS5 proxy password
* - `imap_certificate_checks` = how to check IMAP certificates, one of the @ref DC_CERTCK flags, defaults to #DC_CERTCK_AUTO (0)
* - `smtp_certificate_checks` = how to check SMTP certificates, one of the @ref DC_CERTCK flags, defaults to #DC_CERTCK_AUTO (0)
* - `smtp_certificate_checks` = deprecated option, should be set to the same value as `imap_certificate_checks` but ignored by the new core
* - `displayname` = Own name to use when sending messages. MUAs are allowed to spread this way e.g. using CC, defaults to empty
* - `selfstatus` = Own status to display, e.g. in e-mail footers, defaults to empty
* - `selfavatar` = File containing avatar. Will immediately be copied to the
@@ -416,7 +420,8 @@ char* dc_get_blobdir (const dc_context_t* context);
* and also recoded to a reasonable size.
* - `e2ee_enabled` = 0=no end-to-end-encryption, 1=prefer end-to-end-encryption (default)
* - `mdns_enabled` = 0=do not send or request read receipts,
* 1=send and request read receipts (default)
* 1=send and request read receipts
* default=send and request read receipts, only send but not reuqest if `bot` is set
* - `bcc_self` = 0=do not send a copy of outgoing messages to self (default),
* 1=send a copy of outgoing messages to self.
* Sending messages to self is needed for a proper multi-account setup,
@@ -477,8 +482,9 @@ char* dc_get_blobdir (const dc_context_t* context);
* - `bot` = Set to "1" if this is a bot.
* Prevents adding the "Device messages" and "Saved messages" chats,
* adds Auto-Submitted header to outgoing messages,
* accepts contact requests automatically (calling dc_accept_chat() is not needed for bots)
* and does not cut large incoming text messages.
* accepts contact requests automatically (calling dc_accept_chat() is not needed),
* does not cut large incoming text messages,
* handles existing messages the same way as new ones if `fetch_existing_msgs=1`.
* - `last_msg_id` = database ID of the last message processed by the bot.
* This ID and IDs below it are guaranteed not to be returned
* by dc_get_next_msgs() and dc_wait_next_msgs().
@@ -489,8 +495,8 @@ char* dc_get_blobdir (const dc_context_t* context);
* For most bots calling `dc_markseen_msgs()` is the
* recommended way to update this value
* even for self-sent messages.
* - `fetch_existing_msgs` = 1=fetch most recent existing messages on configure (default),
* 0=do not fetch existing messages on configure.
* - `fetch_existing_msgs` = 0=do not fetch existing messages on configure (default),
* 1=fetch most recent existing messages on configure.
* In both cases, existing recipients are added to the contact database.
* - `disable_idle` = 1=disable IMAP IDLE even if the server supports it,
* 0=use IMAP IDLE if the server supports it.
@@ -513,11 +519,20 @@ char* dc_get_blobdir (const dc_context_t* context);
* 0=Nothing else happens when the key changes.
* 1=After the key changed, `dc_chat_can_send()` returns false and `dc_chat_is_protection_broken()` returns true
* until `dc_accept_chat()` is called.
* - `is_chatmail` = 1 if the the server is a chatmail server, 0 otherwise.
* - `is_muted` = Whether a context is muted by the user.
* Muted contexts should not sound, vibrate or show notifications.
* In contrast to `dc_set_chat_mute_duration()`,
* fresh message and badge counters are not changed by this setting,
* but should be tuned down where appropriate.
* - `ui.*` = All keys prefixed by `ui.` can be used by the user-interfaces for system-specific purposes.
* The prefix should be followed by the system and maybe subsystem,
* e.g. `ui.desktop.foo`, `ui.desktop.linux.bar`, `ui.android.foo`, `ui.dc40.bar`, `ui.bot.simplebot.baz`.
* These keys go to backups and allow easy per-account settings when using @ref dc_accounts_t,
* however, are not handled by the core otherwise.
* - `webxdc_realtime_enabled` = Whether the realtime APIs should be enabled.
* 0 = WebXDC realtime API is disabled and behaves as noop (default).
* 1 = WebXDC realtime API is enabled.
*
* If you want to retrieve a value, use dc_get_config().
*
@@ -1178,6 +1193,65 @@ int dc_send_webxdc_status_update (dc_context_t* context, uint32_t msg_id, const
*/
char* dc_get_webxdc_status_updates (dc_context_t* context, uint32_t msg_id, uint32_t serial);
/**
* Set Webxdc file as integration.
* see dc_init_webxdc_integration() for more details about Webxdc integrations.
*
* @warning This is an experimental API which may change in the future
*
* @memberof dc_context_t
* @param context The context object.
* @param file The .xdc file to use as Webxdc integration.
*/
void dc_set_webxdc_integration (dc_context_t* context, const char* file);
/**
* Init a Webxdc integration.
*
* A Webxdc integration is
* a Webxdc showing a map, getting locations via setUpdateListener(), setting POIs via sendUpdate();
* core takes eg. care of feeding locations to the Webxdc or sending the data out.
*
* @warning This is an experimental API, esp. support of integration types (eg. image editor, tools) is left out for simplicity
*
* Currently, Webxdc integrations are .xdc files shipped together with the main app.
* Before dc_init_webxdc_integration() can be called,
* UI has to call dc_set_webxdc_integration() to define a .xdc file to be used as integration.
*
* dc_init_webxdc_integration() returns a Webxdc message ID that
* UI can open and use mostly as usual.
*
* Concrete behaviour and status updates depend on the integration, driven by UI needs.
*
* There is no need to de-initialize the integration,
* however, unless documented otherwise,
* the integration is valid only as long as not re-initialized
* In other words, UI must not have a Webxdc with the same integration open twice.
*
* Example:
*
* ~~~
* // Define a .xdc file to be used as maps integration
* dc_set_webxdc_integration(context, path_to_maps_xdc);
*
* // Integrate the map to a chat, the map will show locations for this chat then:
* uint32_t webxdc_instance = dc_init_webxdc_integration(context, any_chat_id);
*
* // Or use the Webxdc as a global map, showing locations of all chats:
* uint32_t webxdc_instance = dc_init_webxdc_integration(context, 0);
* ~~~
*
* @memberof dc_context_t
* @param context The context object.
* @param chat_id The chat to get the integration for.
* @return ID of the message that refers to the Webxdc instance.
* UI can open a Webxdc as usual with this instance.
*/
uint32_t dc_init_webxdc_integration (dc_context_t* context, uint32_t chat_id);
/**
* Save a draft for a chat in the database.
*
@@ -2431,6 +2505,7 @@ void dc_stop_ongoing_process (dc_context_t* context);
#define DC_QR_FPR_WITHOUT_ADDR 230 // test1=formatted fingerprint
#define DC_QR_ACCOUNT 250 // text1=domain
#define DC_QR_BACKUP 251
#define DC_QR_BACKUP2 252
#define DC_QR_WEBRTC_INSTANCE 260 // text1=domain, text2=instance pattern
#define DC_QR_ADDR 320 // id=contact
#define DC_QR_TEXT 330 // text1=text
@@ -2477,6 +2552,7 @@ void dc_stop_ongoing_process (dc_context_t* context);
* if so, call dc_set_config_from_qr() and then dc_configure().
*
* - DC_QR_BACKUP:
* - DC_QR_BACKUP2:
* ask the user if they want to set up a new device.
* If so, pass the qr-code to dc_receive_backup().
*
@@ -2545,7 +2621,7 @@ dc_lot_t* dc_check_qr (dc_context_t* context, const char*
* the Verified-Group-Invite protocol is offered in the QR code;
* works for protected groups as well as for normal groups.
* If set to 0, the Setup-Contact protocol is offered in the QR code.
* See https://securejoin.readthedocs.io/en/latest/new.html
* See https://securejoin.delta.chat/
* for details about both protocols.
* @return The text that should go to the QR code,
* On errors, an empty QR code is returned, NULL is never returned.
@@ -2581,8 +2657,7 @@ char* dc_get_securejoin_qr_svg (dc_context_t* context, uint32_
*
* Subsequent calls of dc_join_securejoin() will abort previous, unfinished handshakes.
*
* See https://securejoin.readthedocs.io/en/latest/new.html
* for details about both protocols.
* See https://securejoin.delta.chat/ for details about both protocols.
*
* @memberof dc_context_t
* @param context The context object.
@@ -4043,6 +4118,19 @@ char* dc_msg_get_subject (const dc_msg_t* msg);
char* dc_msg_get_file (const dc_msg_t* msg);
/**
* Save file copy at the user-provided path.
*
* Fails if file already exists at the provided path.
*
* @memberof dc_msg_t
* @param msg The message object.
* @param path Destination file path with filename and extension.
* @return 0 on failure, 1 on success.
*/
int dc_msg_save_file (const dc_msg_t* msg, const char* path);
/**
* Get an original attachment filename, with extension but without the path. To get the full path,
* use dc_msg_get_file().
@@ -4107,7 +4195,6 @@ char* dc_msg_get_webxdc_blob (const dc_msg_t* msg, const char*
* true if the Webxdc should get full internet access, including Webrtc.
* currently, this is only true for encrypted Webxdc's in the self chat
* that have requested internet access in the manifest.
* this is useful for development and maybe for internal integrations at some point.
*
* @memberof dc_msg_t
* @param msg The webxdc instance.
@@ -4316,9 +4403,9 @@ int dc_msg_has_deviating_timestamp(const dc_msg_t* msg);
/**
* Check if a message has a location bound to it.
* These messages are also returned by dc_get_locations()
* and the UI may decide to display a special icon beside such messages,
* Check if a message has a POI location bound to it.
* These locations are also returned by dc_get_locations()
* The UI may decide to display a special icon beside such messages.
*
* @memberof dc_msg_t
* @param msg The message object.
@@ -5405,6 +5492,11 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*/
#define DC_MSG_WEBXDC 80
/**
* Message containing shared contacts represented as a vCard (virtual contact file)
* with email addresses and possibly other fields.
*/
#define DC_MSG_VCARD 90
/**
* @}
@@ -6204,6 +6296,18 @@ void dc_event_unref(dc_event_t* event);
#define DC_EVENT_WEBXDC_INSTANCE_DELETED 2121
/**
* Data received over an ephemeral peer channel.
*
* @param data1 (int) msg_id
* @param data2 (int) + (char*) binary data.
* length is returned as integer with dc_event_get_data2_int()
* and binary data is returned as dc_event_get_data2_str().
* Binary data must be passed to dc_str_unref() afterwards.
*/
#define DC_EVENT_WEBXDC_REALTIME_DATA 2150
/**
* Tells that the Background fetch was completed (or timed out).
*
@@ -6232,6 +6336,14 @@ void dc_event_unref(dc_event_t* event);
#define DC_EVENT_CHATLIST_ITEM_CHANGED 2301
/**
* Inform that some events have been skipped due to event channel overflow.
*
* @param data1 (int) number of events that have been skipped
*/
#define DC_EVENT_CHANNEL_OVERFLOW 2400
/**
* @}
*/
@@ -6539,12 +6651,16 @@ void dc_event_unref(dc_event_t* event);
/// "Message opened"
///
/// Used in subjects of outgoing read receipts.
///
/// @deprecated Deprecated 2024-07-26
#define DC_STR_READRCPT 31
/// "The message '%1$s' you sent was displayed on the screen of the recipient."
///
/// Used as message text of outgoing read receipts.
/// - %1$s will be replaced by the subject of the displayed message
///
/// @deprecated Deprecated 2024-06-23
#define DC_STR_READRCPT_MAILBODY 32
/// @deprecated Deprecated, this string is no longer needed.
@@ -7253,6 +7369,19 @@ void dc_event_unref(dc_event_t* event);
/// Used in summaries.
#define DC_STR_REACTED_BY 177
/// "Establishing guaranteed end-to-end encryption, please wait…"
///
/// Used as info message.
#define DC_STR_SECUREJOIN_WAIT 190
/// "Could not yet establish guaranteed end-to-end encryption, but you may already send a message."
///
/// Used as info message.
#define DC_STR_SECUREJOIN_WAIT_TIMEOUT 191
/// "Contact". Deprecated, currently unused.
#define DC_STR_CONTACT 200
/**
* @}
*/

View File

@@ -1,10 +1,9 @@
#![recursion_limit = "256"]
#![warn(unused, clippy::all)]
#![allow(
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
non_upper_case_globals,
non_camel_case_types,
clippy::missing_safety_doc,
clippy::expect_fun_call
)]
@@ -563,9 +562,11 @@ pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int
EventType::ConfigSynced { .. } => 2111,
EventType::WebxdcStatusUpdate { .. } => 2120,
EventType::WebxdcInstanceDeleted { .. } => 2121,
EventType::WebxdcRealtimeData { .. } => 2150,
EventType::AccountsBackgroundFetchDone => 2200,
EventType::ChatlistChanged => 2300,
EventType::ChatlistItemChanged { .. } => 2301,
EventType::EventChannelOverflow { .. } => 2400,
}
}
@@ -618,11 +619,13 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
| EventType::SecurejoinJoinerProgress { contact_id, .. } => {
contact_id.to_u32() as libc::c_int
}
EventType::WebxdcStatusUpdate { msg_id, .. } => msg_id.to_u32() as libc::c_int,
EventType::WebxdcInstanceDeleted { msg_id, .. } => msg_id.to_u32() as libc::c_int,
EventType::WebxdcRealtimeData { msg_id, .. }
| EventType::WebxdcStatusUpdate { msg_id, .. }
| EventType::WebxdcInstanceDeleted { msg_id, .. } => msg_id.to_u32() as libc::c_int,
EventType::ChatlistItemChanged { chat_id } => {
chat_id.unwrap_or_default().to_u32() as libc::c_int
}
EventType::EventChannelOverflow { n } => *n as libc::c_int,
}
}
@@ -661,8 +664,9 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
| EventType::AccountsBackgroundFetchDone
| EventType::ChatlistChanged
| EventType::ChatlistItemChanged { .. }
| EventType::ConfigSynced { .. } => 0,
EventType::ChatModified(_) => 0,
| EventType::ConfigSynced { .. }
| EventType::ChatModified(_)
| EventType::EventChannelOverflow { .. } => 0,
EventType::MsgsChanged { msg_id, .. }
| EventType::ReactionsChanged { msg_id, .. }
| EventType::IncomingMsg { msg_id, .. }
@@ -677,6 +681,7 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
status_update_serial,
..
} => status_update_serial.to_u32() as libc::c_int,
EventType::WebxdcRealtimeData { data, .. } => data.len() as libc::c_int,
}
}
@@ -727,7 +732,8 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
| EventType::ChatEphemeralTimerModified { .. }
| EventType::IncomingMsgBunch { .. }
| EventType::ChatlistItemChanged { .. }
| EventType::ChatlistChanged => ptr::null_mut(),
| EventType::ChatlistChanged
| EventType::EventChannelOverflow { .. } => ptr::null_mut(),
EventType::ConfigureProgress { comment, .. } => {
if let Some(comment) = comment {
comment.to_c_string().unwrap_or_default().into_raw()
@@ -743,6 +749,11 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
let data2 = key.to_string().to_c_string().unwrap_or_default();
data2.into_raw()
}
EventType::WebxdcRealtimeData { data, .. } => {
let ptr = libc::malloc(data.len());
libc::memcpy(ptr, data.as_ptr() as *mut libc::c_void, data.len());
ptr as *mut libc::c_char
}
}
}
@@ -1065,6 +1076,43 @@ pub unsafe extern "C" fn dc_get_webxdc_status_updates(
.strdup()
}
#[no_mangle]
pub unsafe extern "C" fn dc_set_webxdc_integration(
context: *mut dc_context_t,
file: *const libc::c_char,
) {
if context.is_null() || file.is_null() {
eprintln!("ignoring careless call to dc_set_webxdc_integration()");
return;
}
let ctx = &*context;
block_on(ctx.set_webxdc_integration(&to_string_lossy(file)))
.log_err(ctx)
.unwrap_or_default();
}
#[no_mangle]
pub unsafe extern "C" fn dc_init_webxdc_integration(
context: *mut dc_context_t,
chat_id: u32,
) -> u32 {
if context.is_null() {
eprintln!("ignoring careless call to dc_init_webxdc_integration()");
return 0;
}
let ctx = &*context;
let chat_id = if chat_id == 0 {
None
} else {
Some(ChatId::new(chat_id))
};
block_on(ctx.init_webxdc_integration(chat_id))
.log_err(ctx)
.map(|msg_id| msg_id.map(|id| id.to_u32()).unwrap_or_default())
.unwrap_or(0)
}
#[no_mangle]
pub unsafe extern "C" fn dc_set_draft(
context: *mut dc_context_t,
@@ -2010,7 +2058,7 @@ pub unsafe extern "C" fn dc_get_msg(context: *mut dc_context_t, msg_id: u32) ->
);
message::Message::default()
} else {
error!(ctx, "dc_get_msg could not retrieve msg_id {msg_id}: {e:#}");
warn!(ctx, "dc_get_msg could not retrieve msg_id {msg_id}: {e:#}");
return ptr::null_mut();
}
}
@@ -3333,6 +3381,34 @@ pub unsafe extern "C" fn dc_msg_get_file(msg: *mut dc_msg_t) -> *mut libc::c_cha
.unwrap_or_else(|| "".strdup())
}
#[no_mangle]
pub unsafe extern "C" fn dc_msg_save_file(
msg: *mut dc_msg_t,
path: *const libc::c_char,
) -> libc::c_int {
if msg.is_null() || path.is_null() {
eprintln!("ignoring careless call to dc_msg_save_file()");
return 0;
}
let ffi_msg = &*msg;
let ctx = &*ffi_msg.context;
let path = to_string_lossy(path);
let r = block_on(
ffi_msg
.message
.save_file(ctx, &std::path::PathBuf::from(path)),
);
match r {
Ok(()) => 1,
Err(_) => {
r.context("Failed to save file from message")
.log_err(ctx)
.unwrap_or_default();
0
}
}
}
#[no_mangle]
pub unsafe extern "C" fn dc_msg_get_filename(msg: *mut dc_msg_t) -> *mut libc::c_char {
if msg.is_null() {
@@ -4288,7 +4364,7 @@ pub unsafe extern "C" fn dc_backup_provider_wait(provider: *mut dc_backup_provid
let ctx = &*ffi_provider.context;
let provider = &mut ffi_provider.provider;
block_on(provider)
.context("Failed to await BackupProvider")
.context("Failed to await backup provider")
.log_err(ctx)
.set_last_error(ctx)
.ok();
@@ -4342,7 +4418,7 @@ trait ResultExt<T, E> {
/// Like `log_err()`, but:
/// - returns the default value instead of an Err value.
/// - emits an error instead of a warning for an [Err] result. This means
/// that the error will be shown to the user in a small pop-up.
/// that the error will be shown to the user in a small pop-up.
fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T;
}
@@ -4408,19 +4484,6 @@ where
}
}
trait ResultNullableExt<T> {
fn into_raw(self) -> *mut T;
}
impl<T, E> ResultNullableExt<T> for Result<T, E> {
fn into_raw(self) -> *mut T {
match self {
Ok(t) => Box::into_raw(Box::new(t)),
Err(_) => ptr::null_mut(),
}
}
}
fn convert_and_prune_message_ids(msg_ids: *const u32, msg_cnt: libc::c_int) -> Vec<MsgId> {
let ids = unsafe { std::slice::from_raw_parts(msg_ids, msg_cnt as usize) };
let msg_ids: Vec<MsgId> = ids
@@ -4900,7 +4963,9 @@ mod jsonrpc {
}
let account_manager = &*account_manager;
let cmd_api = deltachat_jsonrpc::api::CommandApi::from_arc(account_manager.inner.clone());
let cmd_api = block_on(deltachat_jsonrpc::api::CommandApi::from_arc(
account_manager.inner.clone(),
));
let (request_handle, receiver) = RpcClient::new();
let handle = RpcSession::new(request_handle, cmd_api);

View File

@@ -50,6 +50,7 @@ impl Lot {
Qr::FprWithoutAddr { fingerprint, .. } => Some(fingerprint),
Qr::Account { domain } => Some(domain),
Qr::Backup { .. } => None,
Qr::Backup2 { .. } => None,
Qr::WebrtcInstance { domain, .. } => Some(domain),
Qr::Addr { draft, .. } => draft.as_deref(),
Qr::Url { url } => Some(url),
@@ -102,6 +103,7 @@ impl Lot {
Qr::FprWithoutAddr { .. } => LotState::QrFprWithoutAddr,
Qr::Account { .. } => LotState::QrAccount,
Qr::Backup { .. } => LotState::QrBackup,
Qr::Backup2 { .. } => LotState::QrBackup2,
Qr::WebrtcInstance { .. } => LotState::QrWebrtcInstance,
Qr::Addr { .. } => LotState::QrAddr,
Qr::Url { .. } => LotState::QrUrl,
@@ -127,6 +129,7 @@ impl Lot {
Qr::FprWithoutAddr { .. } => Default::default(),
Qr::Account { .. } => Default::default(),
Qr::Backup { .. } => Default::default(),
Qr::Backup2 { .. } => Default::default(),
Qr::WebrtcInstance { .. } => Default::default(),
Qr::Addr { contact_id, .. } => contact_id.to_u32(),
Qr::Url { .. } => Default::default(),
@@ -177,6 +180,8 @@ pub enum LotState {
QrBackup = 251,
QrBackup2 = 252,
/// text1=domain, text2=instance pattern
QrWebrtcInstance = 260,

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat-jsonrpc"
version = "1.137.3"
version = "1.142.7"
description = "DeltaChat JSON-RPC API"
edition = "2021"
default-run = "deltachat-jsonrpc-server"
@@ -13,29 +13,30 @@ path = "src/webserver.rs"
required-features = ["webserver"]
[dependencies]
anyhow = "1"
deltachat = { path = ".." }
num-traits = "0.2"
schemars = "0.8.13"
serde = { version = "1.0", features = ["derive"] }
tempfile = "3.10.1"
log = "0.4"
async-channel = { version = "2.0.0" }
futures = { version = "0.3.30" }
serde_json = "1"
yerpc = { version = "0.5.2", features = ["anyhow_expose", "openrpc"] }
anyhow = { workspace = true }
deltachat = { workspace = true }
deltachat-contact-tools = { workspace = true }
num-traits = { workspace = true }
schemars = "0.8.21"
serde = { workspace = true, features = ["derive"] }
tempfile = { workspace = true }
log = { workspace = true }
async-channel = { workspace = true }
futures = { workspace = true }
serde_json = { workspace = true }
yerpc = { workspace = true, features = ["anyhow_expose", "openrpc"] }
typescript-type-def = { version = "0.5.8", features = ["json_value"] }
tokio = { version = "1.37.0" }
sanitize-filename = "0.5"
tokio = { workspace = true }
sanitize-filename = { workspace = true }
walkdir = "2.5.0"
base64 = "0.22"
base64 = { workspace = true }
# optional dependencies
axum = { version = "0.7", optional = true, features = ["ws"] }
env_logger = { version = "0.11.3", optional = true }
env_logger = { version = "0.11.5", optional = true }
[dev-dependencies]
tokio = { version = "1.37.0", features = ["full", "rt-multi-thread"] }
tokio = { workspace = true, features = ["full", "rt-multi-thread"] }
[features]

View File

@@ -1,4 +1,6 @@
use std::collections::BTreeMap;
use std::path::Path;
use std::str;
use std::sync::Arc;
use std::time::Duration;
use std::{collections::HashMap, str::FromStr};
@@ -16,12 +18,14 @@ use deltachat::constants::DC_MSG_ID_DAYMARKER;
use deltachat::contact::{may_be_valid_addr, Contact, ContactId, Origin};
use deltachat::context::get_info;
use deltachat::ephemeral::Timer;
use deltachat::imex;
use deltachat::location;
use deltachat::message::get_msg_read_receipts;
use deltachat::message::{
self, delete_msgs, markseen_msgs, Message, MessageState, MsgId, Viewtype,
};
use deltachat::peer_channels::{
leave_webxdc_realtime, send_webxdc_realtime_advertisement, send_webxdc_realtime_data,
};
use deltachat::provider::get_provider_info;
use deltachat::qr::{self, Qr};
use deltachat::qr_code_generator::{generate_backup_qr, get_securejoin_qr_svg};
@@ -29,6 +33,8 @@ use deltachat::reaction::{get_msg_reactions, send_reaction};
use deltachat::securejoin;
use deltachat::stock_str::StockMessage;
use deltachat::webxdc::StatusUpdateSerial;
use deltachat::EventEmitter;
use deltachat::{imex, info};
use sanitize_filename::is_sanitized;
use tokio::fs;
use tokio::sync::{watch, Mutex, RwLock};
@@ -40,7 +46,7 @@ pub mod types;
use num_traits::FromPrimitive;
use types::account::Account;
use types::chat::FullChat;
use types::contact::ContactObject;
use types::contact::{ContactObject, VcardContact};
use types::events::Event;
use types::http::HttpResponse;
use types::message::{MessageData, MessageObject, MessageReadReceipt};
@@ -81,21 +87,30 @@ impl Default for AccountState {
pub struct CommandApi {
pub(crate) accounts: Arc<RwLock<Accounts>>,
/// Receiver side of the event channel.
///
/// Events from it can be received by calling `get_next_event` method.
event_emitter: Arc<EventEmitter>,
states: Arc<Mutex<BTreeMap<u32, AccountState>>>,
}
impl CommandApi {
pub fn new(accounts: Accounts) -> Self {
let event_emitter = Arc::new(accounts.get_event_emitter());
CommandApi {
accounts: Arc::new(RwLock::new(accounts)),
event_emitter,
states: Arc::new(Mutex::new(BTreeMap::new())),
}
}
#[allow(dead_code)]
pub fn from_arc(accounts: Arc<RwLock<Accounts>>) -> Self {
pub async fn from_arc(accounts: Arc<RwLock<Accounts>>) -> Self {
let event_emitter = Arc::new(accounts.read().await.get_event_emitter());
CommandApi {
accounts,
event_emitter,
states: Arc::new(Mutex::new(BTreeMap::new())),
}
}
@@ -158,8 +173,7 @@ impl CommandApi {
/// Get the next event.
async fn get_next_event(&self) -> Result<Event> {
let event_emitter = self.accounts.read().await.get_event_emitter();
event_emitter
self.event_emitter
.recv()
.await
.map(|event| event.into())
@@ -174,6 +188,16 @@ impl CommandApi {
self.accounts.write().await.add_account().await
}
/// Imports/migrated an existing account from a database path into this account manager.
/// Returns the ID of new account.
async fn migrate_account(&self, path_to_db: String) -> Result<u32> {
self.accounts
.write()
.await
.migrate_account(std::path::PathBuf::from(path_to_db))
.await
}
async fn remove_account(&self, account_id: u32) -> Result<()> {
self.accounts
.write()
@@ -318,6 +342,11 @@ impl CommandApi {
ctx.get_info().await
}
async fn get_blob_dir(&self, account_id: u32) -> Result<Option<String>> {
let ctx = self.get_context(account_id).await?;
Ok(ctx.get_blobdir().to_str().map(|s| s.to_owned()))
}
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())
@@ -678,7 +707,22 @@ impl CommandApi {
ChatId::new(chat_id).get_encryption_info(&ctx).await
}
/// Get QR code (text and SVG) that will offer an Setup-Contact or Verified-Group invitation.
/// Get QR code text that will offer a [SecureJoin](https://securejoin.delta.chat/) invitation.
///
/// If `chat_id` is a group chat ID, SecureJoin QR code for the group is returned.
/// If `chat_id` is unset, setup contact QR code is returned.
async fn get_chat_securejoin_qr_code(
&self,
account_id: u32,
chat_id: Option<u32>,
) -> Result<String> {
let ctx = self.get_context(account_id).await?;
let chat = chat_id.map(ChatId::new);
let qr = securejoin::get_securejoin_qr(&ctx, chat).await?;
Ok(qr)
}
/// Get QR code (text and SVG) that will offer a Setup-Contact or Verified-Group invitation.
/// The QR code is compatible to the OPENPGP4FPR format
/// so that a basic fingerprint comparison also works e.g. with OpenKeychain.
///
@@ -690,8 +734,7 @@ impl CommandApi {
/// the Verified-Group-Invite protocol is offered in the QR code;
/// works for protected groups as well as for normal groups.
/// If not set, the Setup-Contact protocol is offered in the QR code.
/// See https://securejoin.readthedocs.io/en/latest/new.html
/// for details about both protocols.
/// See https://securejoin.delta.chat/ for details about both protocols.
///
/// return format: `[code, svg]`
async fn get_chat_securejoin_qr_code_svg(
@@ -701,10 +744,9 @@ impl CommandApi {
) -> Result<(String, String)> {
let ctx = self.get_context(account_id).await?;
let chat = chat_id.map(ChatId::new);
Ok((
securejoin::get_securejoin_qr(&ctx, chat).await?,
get_securejoin_qr_svg(&ctx, chat).await?,
))
let qr = securejoin::get_securejoin_qr(&ctx, chat).await?;
let svg = get_securejoin_qr_svg(&ctx, chat).await?;
Ok((qr, svg))
}
/// Continue a Setup-Contact or Verified-Group-Invite protocol
@@ -719,8 +761,7 @@ impl CommandApi {
///
/// Subsequent calls of `secure_join()` will abort previous, unfinished handshakes.
///
/// See https://securejoin.readthedocs.io/en/latest/new.html
/// for details about both protocols.
/// See https://securejoin.delta.chat/ for details about both protocols.
///
/// **qr**: The text of the scanned QR code. Typically, the same string as given
/// to `check_qr()`.
@@ -1418,6 +1459,51 @@ impl CommandApi {
Ok(contact_id.map(|id| id.to_u32()))
}
/// Parses a vCard file located at the given path. Returns contacts in their original order.
async fn parse_vcard(&self, path: String) -> Result<Vec<VcardContact>> {
let vcard = fs::read(Path::new(&path)).await?;
let vcard = str::from_utf8(&vcard)?;
Ok(deltachat_contact_tools::parse_vcard(vcard)
.into_iter()
.map(|c| c.into())
.collect())
}
/// Imports contacts from a vCard file located at the given path.
///
/// Returns the ids of created/modified contacts in the order they appear in the vCard.
async fn import_vcard(&self, account_id: u32, path: String) -> Result<Vec<u32>> {
let ctx = self.get_context(account_id).await?;
let vcard = tokio::fs::read(Path::new(&path)).await?;
let vcard = str::from_utf8(&vcard)?;
Ok(deltachat::contact::import_vcard(&ctx, vcard)
.await?
.into_iter()
.map(|c| c.to_u32())
.collect())
}
/// Returns a vCard containing contacts with the given ids.
async fn make_vcard(&self, account_id: u32, contacts: Vec<u32>) -> Result<String> {
let ctx = self.get_context(account_id).await?;
let contacts: Vec<_> = contacts.iter().map(|&c| ContactId::new(c)).collect();
deltachat::contact::make_vcard(&ctx, &contacts).await
}
/// Sets vCard containing the given contacts to the message draft.
async fn set_draft_vcard(
&self,
account_id: u32,
msg_id: u32,
contacts: Vec<u32>,
) -> Result<()> {
let ctx = self.get_context(account_id).await?;
let contacts: Vec<_> = contacts.iter().map(|&c| ContactId::new(c)).collect();
let mut msg = Message::load_from_db(&ctx, MsgId::new(msg_id)).await?;
msg.make_vcard(&ctx, &contacts).await?;
msg.get_chat_id().set_draft(&ctx, Some(&mut msg)).await
}
// ---------------------------------------------
// chat
// ---------------------------------------------
@@ -1586,10 +1672,10 @@ impl CommandApi {
///
/// This call will block until the QR code is ready,
/// even if there is no concurrent call to [`CommandApi::provide_backup`],
/// but will fail after 10 seconds to avoid deadlocks.
/// but will fail after 60 seconds to avoid deadlocks.
async fn get_backup_qr(&self, account_id: u32) -> Result<String> {
let qr = tokio::time::timeout(
Duration::from_secs(10),
Duration::from_secs(60),
self.inner_get_backup_qr(account_id),
)
.await
@@ -1605,13 +1691,13 @@ impl CommandApi {
///
/// This call will block until the QR code is ready,
/// even if there is no concurrent call to [`CommandApi::provide_backup`],
/// but will fail after 10 seconds to avoid deadlocks.
/// but will fail after 60 seconds to avoid deadlocks.
///
/// Returns the QR code rendered as an SVG image.
async fn get_backup_qr_svg(&self, account_id: u32) -> Result<String> {
let ctx = self.get_context(account_id).await?;
let qr = tokio::time::timeout(
Duration::from_secs(10),
Duration::from_secs(60),
self.inner_get_backup_qr(account_id),
)
.await
@@ -1722,6 +1808,37 @@ impl CommandApi {
.await
}
async fn send_webxdc_realtime_data(
&self,
account_id: u32,
instance_msg_id: u32,
data: Vec<u8>,
) -> Result<()> {
let ctx = self.get_context(account_id).await?;
send_webxdc_realtime_data(&ctx, MsgId::new(instance_msg_id), data).await
}
async fn send_webxdc_realtime_advertisement(
&self,
account_id: u32,
instance_msg_id: u32,
) -> Result<()> {
let ctx = self.get_context(account_id).await?;
let fut = send_webxdc_realtime_advertisement(&ctx, MsgId::new(instance_msg_id)).await?;
if let Some(fut) = fut {
tokio::spawn(async move {
fut.await.ok();
info!(ctx, "send_webxdc_realtime_advertisement done")
});
}
Ok(())
}
async fn leave_webxdc_realtime(&self, account_id: u32, instance_message_id: u32) -> Result<()> {
let ctx = self.get_context(account_id).await?;
leave_webxdc_realtime(&ctx, MsgId::new(instance_message_id)).await
}
async fn get_webxdc_status_updates(
&self,
account_id: u32,
@@ -1763,6 +1880,29 @@ impl CommandApi {
Ok(general_purpose::STANDARD_NO_PAD.encode(blob))
}
/// Sets Webxdc file as integration.
/// `file` is the .xdc to use as Webxdc integration.
async fn set_webxdc_integration(&self, account_id: u32, file_path: String) -> Result<()> {
let ctx = self.get_context(account_id).await?;
ctx.set_webxdc_integration(&file_path).await
}
/// Returns Webxdc instance used for optional integrations.
/// UI can open the Webxdc as usual.
/// Returns `None` if there is no integration; the caller can add one using `set_webxdc_integration` then.
/// `integrate_for` is the chat to get the integration for.
async fn init_webxdc_integration(
&self,
account_id: u32,
chat_id: Option<u32>,
) -> Result<Option<u32>> {
let ctx = self.get_context(account_id).await?;
Ok(ctx
.init_webxdc_integration(chat_id.map(ChatId::new))
.await?
.map(|msg_id| msg_id.to_u32()))
}
/// Makes an HTTP GET request and returns a response.
///
/// `url` is the HTTP or HTTPS URL.
@@ -1871,6 +2011,15 @@ impl CommandApi {
Ok(can_send)
}
/// Saves a file copy at the user-provided path.
///
/// Fails if file already exists at the provided path.
async fn save_msg_file(&self, account_id: u32, msg_id: u32, path: String) -> Result<()> {
let ctx = self.get_context(account_id).await?;
let message = Message::load_from_db(&ctx, MsgId::new(msg_id)).await?;
message.save_file(&ctx, Path::new(&path)).await
}
// ---------------------------------------------
// functions for the composer
// the composer is the message input field
@@ -1943,19 +2092,21 @@ impl CommandApi {
);
let destination_path = account_folder.join("stickers").join(collection);
fs::create_dir_all(&destination_path).await?;
let file = message.get_file(&ctx).context("no file")?;
fs::copy(
&file,
destination_path.join(format!(
"{}.{}",
msg_id,
file.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default()
)),
)
.await?;
let file = message.get_filename().context("no file?")?;
message
.save_file(
&ctx,
&destination_path.join(format!(
"{}.{}",
msg_id,
Path::new(&file)
.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default()
)),
)
.await?;
Ok(())
}

View File

@@ -32,6 +32,7 @@ pub struct FullChat {
is_protected: bool,
profile_image: Option<String>, //BLOBS ?
archived: bool,
pinned: bool,
// subtitle - will be moved to frontend because it uses translation functions
chat_type: u32,
is_unpromoted: bool,
@@ -104,6 +105,7 @@ impl FullChat {
is_protected: chat.is_protected(),
profile_image, //BLOBS ?
archived: chat.get_visibility() == chat::ChatVisibility::Archived,
pinned: chat.get_visibility() == chat::ChatVisibility::Pinned,
chat_type: chat.get_type().to_u32().context("unknown chat type id")?,
is_unpromoted: chat.is_unpromoted(),
is_self_talk: chat.is_self_talk(),
@@ -153,6 +155,7 @@ pub struct BasicChat {
is_protected: bool,
profile_image: Option<String>, //BLOBS ?
archived: bool,
pinned: bool,
chat_type: u32,
is_unpromoted: bool,
is_self_talk: bool,
@@ -180,6 +183,7 @@ impl BasicChat {
is_protected: chat.is_protected(),
profile_image, //BLOBS ?
archived: chat.get_visibility() == chat::ChatVisibility::Archived,
pinned: chat.get_visibility() == chat::ChatVisibility::Pinned,
chat_type: chat.get_type().to_u32().context("unknown chat type id")?,
is_unpromoted: chat.is_unpromoted(),
is_self_talk: chat.is_self_talk(),

View File

@@ -1,4 +1,5 @@
use anyhow::Result;
use deltachat::color;
use deltachat::context::Context;
use serde::Serialize;
use typescript_type_def::TypeDef;
@@ -18,6 +19,7 @@ pub struct ContactObject {
profile_image: Option<String>, // BLOBS
name_and_addr: String,
is_blocked: bool,
e2ee_avail: bool,
/// True if the contact can be added to verified groups.
///
@@ -78,6 +80,7 @@ impl ContactObject {
profile_image, //BLOBS
name_and_addr: contact.get_name_n_addr(),
is_blocked: contact.is_blocked(),
e2ee_avail: contact.e2ee_avail(context).await?,
is_verified,
is_profile_verified,
verifier_id,
@@ -87,3 +90,35 @@ impl ContactObject {
})
}
}
#[derive(Clone, Serialize, TypeDef, schemars::JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct VcardContact {
/// Email address.
addr: String,
/// The contact's name, or the email address if no name was given.
display_name: String,
/// Public PGP key in Base64.
key: Option<String>,
/// Profile image in Base64.
profile_image: Option<String>,
/// Contact color as hex string.
color: String,
/// Last update timestamp.
timestamp: Option<i64>,
}
impl From<deltachat_contact_tools::VcardContact> for VcardContact {
fn from(vc: deltachat_contact_tools::VcardContact) -> Self {
let display_name = vc.display_name().to_string();
let color = color::str_to_color(&vc.addr.to_lowercase());
Self {
addr: vc.addr,
display_name,
key: vc.key,
profile_image: vc.profile_image,
color: color_int_to_hex_string(color),
timestamp: vc.timestamp.ok(),
}
}
}

View File

@@ -240,6 +240,10 @@ pub enum EventType {
status_update_serial: u32,
},
/// Data received over an ephemeral peer channel.
#[serde(rename_all = "camelCase")]
WebxdcRealtimeData { msg_id: u32, data: Vec<u8> },
/// Inform that a message containing a webxdc instance has been deleted
#[serde(rename_all = "camelCase")]
WebxdcInstanceDeleted { msg_id: u32 },
@@ -259,6 +263,9 @@ pub enum EventType {
/// If `chat_id` is set to None, then all currently visible chats need to be rerendered, and all not-visible items need to be cleared from cache if the UI has a cache.
#[serde(rename_all = "camelCase")]
ChatlistItemChanged { chat_id: Option<u32> },
/// Inform than some events have been skipped due to event channel overflow.
EventChannelOverflow { n: u64 },
}
impl From<CoreEventType> for EventType {
@@ -362,6 +369,10 @@ impl From<CoreEventType> for EventType {
msg_id: msg_id.to_u32(),
status_update_serial: status_update_serial.to_u32(),
},
CoreEventType::WebxdcRealtimeData { msg_id, data } => WebxdcRealtimeData {
msg_id: msg_id.to_u32(),
data,
},
CoreEventType::WebxdcInstanceDeleted { msg_id } => WebxdcInstanceDeleted {
msg_id: msg_id.to_u32(),
},
@@ -370,6 +381,7 @@ impl From<CoreEventType> for EventType {
chat_id: chat_id.map(|id| id.to_u32()),
},
CoreEventType::ChatlistChanged => ChatlistChanged,
CoreEventType::EventChannelOverflow { n } => EventChannelOverflow { n },
}
}
}

View File

@@ -1,3 +1,4 @@
use crate::api::VcardContact;
use anyhow::{Context as _, Result};
use deltachat::chat::Chat;
use deltachat::chat::ChatItem;
@@ -35,6 +36,10 @@ pub struct MessageObject {
parent_id: Option<u32>,
text: String,
/// Check if a message has a POI location bound to it.
/// These locations are also returned by `get_locations` method.
/// The UI may decide to display a special icon beside such messages.
has_location: bool,
has_html: bool,
view_type: MessageViewtype,
@@ -83,6 +88,8 @@ pub struct MessageObject {
download_state: DownloadState,
reactions: Option<JSONRPCReactions>,
vcard_contact: Option<VcardContact>,
}
#[derive(Serialize, TypeDef, schemars::JsonSchema)]
@@ -169,6 +176,13 @@ impl MessageObject {
Some(reactions.into())
};
let vcard_contacts: Vec<VcardContact> = message
.vcard_contacts(context)
.await?
.into_iter()
.map(Into::into)
.collect();
Ok(MessageObject {
id: msg_id.to_u32(),
chat_id: message.get_chat_id().to_u32(),
@@ -228,6 +242,8 @@ impl MessageObject {
download_state,
reactions,
vcard_contact: vcard_contacts.first().cloned(),
})
}
}
@@ -270,6 +286,11 @@ pub enum MessageViewtype {
/// Message is an webxdc instance.
Webxdc,
/// Message containing shared contacts represented as a vCard (virtual contact file)
/// with email addresses and possibly other fields.
/// Use `parse_vcard()` to retrieve them.
Vcard,
}
impl From<Viewtype> for MessageViewtype {
@@ -286,6 +307,7 @@ impl From<Viewtype> for MessageViewtype {
Viewtype::File => MessageViewtype::File,
Viewtype::VideochatInvitation => MessageViewtype::VideochatInvitation,
Viewtype::Webxdc => MessageViewtype::Webxdc,
Viewtype::Vcard => MessageViewtype::Vcard,
}
}
}
@@ -304,6 +326,7 @@ impl From<MessageViewtype> for Viewtype {
MessageViewtype::File => Viewtype::File,
MessageViewtype::VideochatInvitation => Viewtype::VideochatInvitation,
MessageViewtype::Webxdc => Viewtype::Webxdc,
MessageViewtype::Vcard => Viewtype::Vcard,
}
}
}
@@ -342,6 +365,14 @@ pub enum SystemMessageType {
LocationOnly,
InvalidUnencryptedMail,
/// 1:1 chats info message telling that SecureJoin has started and the user should wait for it
/// to complete.
SecurejoinWait,
/// 1:1 chats info message telling that SecureJoin is still running, but the user may already
/// send messages.
SecurejoinWaitTimeout,
/// Chat ephemeral message timer is changed.
EphemeralTimerChanged,
@@ -360,6 +391,9 @@ pub enum SystemMessageType {
/// Webxdc info added with `info` set in `send_webxdc_status_update()`.
WebxdcInfoMessage,
/// This message contains a users iroh node address.
IrohNodeAddr,
}
impl From<deltachat::mimeparser::SystemMessage> for SystemMessageType {
@@ -382,6 +416,9 @@ impl From<deltachat::mimeparser::SystemMessage> for SystemMessageType {
SystemMessage::WebxdcStatusUpdate => SystemMessageType::WebxdcStatusUpdate,
SystemMessage::WebxdcInfoMessage => SystemMessageType::WebxdcInfoMessage,
SystemMessage::InvalidUnencryptedMail => SystemMessageType::InvalidUnencryptedMail,
SystemMessage::IrohNodeAddr => SystemMessageType::IrohNodeAddr,
SystemMessage::SecurejoinWait => SystemMessageType::SecurejoinWait,
SystemMessage::SecurejoinWaitTimeout => SystemMessageType::SecurejoinWaitTimeout,
}
}
}
@@ -540,7 +577,9 @@ pub struct MessageData {
pub file: Option<String>,
pub location: Option<(f64, f64)>,
pub override_sender_name: Option<String>,
/// Quoted message id. Takes preference over `quoted_text` (see below).
pub quoted_message_id: Option<u32>,
pub quoted_text: Option<String>,
}
impl MessageData {
@@ -576,6 +615,9 @@ impl MessageData {
),
)
.await?;
} else if let Some(text) = self.quoted_text {
let protect = false;
message.set_quote_text(Some((text, protect)));
}
Ok(message)
}
@@ -631,7 +673,7 @@ impl MessageInfo {
#[derive(
Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize, TypeDef, schemars::JsonSchema,
)]
#[serde(rename_all = "camelCase", tag = "variant")]
#[serde(rename_all = "camelCase", tag = "kind")]
pub enum EphemeralTimer {
/// Timer is disabled.
Disabled,

View File

@@ -35,6 +35,11 @@ pub enum QrObject {
Backup {
ticket: String,
},
Backup2 {
auth_token: String,
node_addr: String,
},
WebrtcInstance {
domain: String,
instance_pattern: String,
@@ -132,6 +137,14 @@ impl From<Qr> for QrObject {
Qr::Backup { ticket } => QrObject::Backup {
ticket: ticket.to_string(),
},
Qr::Backup2 {
ref node_addr,
auth_token,
} => QrObject::Backup2 {
node_addr: serde_json::to_string(node_addr).unwrap_or_default(),
auth_token,
},
Qr::WebrtcInstance {
domain,
instance_pattern,

View File

@@ -1,3 +1,4 @@
#![recursion_limit = "256"]
pub mod api;
pub use yerpc;

View File

@@ -1,3 +1,4 @@
#![recursion_limit = "256"]
use std::net::SocketAddr;
use std::path::PathBuf;

View File

@@ -3,7 +3,7 @@
"dependencies": {
"@deltachat/tiny-emitter": "3.0.0",
"isomorphic-ws": "^4.0.1",
"yerpc": "^0.4.3"
"yerpc": "^0.6.2"
},
"devDependencies": {
"@types/chai": "^4.2.21",
@@ -25,12 +25,17 @@
"exports": {
".": {
"import": "./dist/deltachat.js",
"require": "./dist/deltachat.cjs"
"require": "./dist/deltachat.cjs",
"types": "./dist/deltachat.d.ts"
}
},
"license": "MPL-2.0",
"main": "dist/deltachat.js",
"name": "@deltachat/jsonrpc-client",
"repository": {
"type": "git",
"url": "https://github.com/deltachat/deltachat-core-rust.git"
},
"scripts": {
"build": "run-s generate-bindings extract-constants build:tsc build:bundle build:cjs",
"build:bundle": "esbuild --format=esm --bundle dist/deltachat.js --outfile=dist/deltachat.bundle.js",
@@ -53,5 +58,5 @@
},
"type": "module",
"types": "dist/deltachat.d.ts",
"version": "1.137.3"
"version": "1.142.7"
}

View File

@@ -1,20 +1,20 @@
[package]
name = "deltachat-repl"
version = "1.137.3"
version = "1.142.7"
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"]}
ansi_term = { workspace = true }
anyhow = { workspace = true }
deltachat = { workspace = true, features = ["internals"]}
dirs = "5"
log = "0.4.21"
pretty_env_logger = "0.5"
rusqlite = "0.31"
log = { workspace = true }
rusqlite = { workspace = true }
rustyline = "14"
tokio = { version = "1.37.0", features = ["fs", "rt-multi-thread", "macros"] }
tokio = { workspace = true, features = ["fs", "rt-multi-thread", "macros"] }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
[features]
default = ["vendored"]

View File

@@ -19,6 +19,7 @@ use deltachat::location;
use deltachat::log::LogExt;
use deltachat::message::{self, Message, MessageState, MsgId, Viewtype};
use deltachat::mimeparser::SystemMessage;
use deltachat::peer_channels::{send_webxdc_realtime_advertisement, send_webxdc_realtime_data};
use deltachat::peerstate::*;
use deltachat::qr::*;
use deltachat::reaction::send_reaction;
@@ -338,7 +339,6 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
receive-backup <qr>\n\
export-keys\n\
import-keys\n\
export-setup\n\
poke [<eml-file>|<folder>|<addr> <key-file>]\n\
reset <flags>\n\
stop\n\
@@ -503,17 +503,6 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
"import-keys" => {
imex(&context, ImexMode::ImportSelfKeys, arg1.as_ref(), None).await?;
}
"export-setup" => {
let setup_code = create_setup_code(&context);
let file_name = blobdir.join("autocrypt-setup-message.html");
let file_content = render_setup_file(&context, &setup_code).await?;
fs::write(&file_name, file_content).await?;
println!(
"Setup message written to: {}\nSetup code: {}",
file_name.display(),
&setup_code,
);
}
"poke" => {
ensure!(poke_spec(&context, Some(arg1)).await, "Poke failed");
}
@@ -642,6 +631,30 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
println!("{cnt} chats");
println!("{time_needed:?} to create this list");
}
"start-realtime" => {
if arg1.is_empty() {
bail!("missing msgid");
}
let msg_id = MsgId::new(arg1.parse()?);
let res = send_webxdc_realtime_advertisement(&context, msg_id).await?;
if let Some(res) = res {
println!("waiting for peer channel join");
res.await?;
}
println!("joined peer channel");
}
"send-realtime" => {
if arg1.is_empty() {
bail!("missing msgid");
}
if arg2.is_empty() {
bail!("no message");
}
let msg_id = MsgId::new(arg1.parse()?);
send_webxdc_realtime_data(&context, msg_id, arg2.as_bytes().to_vec()).await?;
println!("sent realtime message");
}
"chat" => {
if sel_chat.is_none() && arg1.is_empty() {
bail!("Argument [chat-id] is missing.");

View File

@@ -1,3 +1,4 @@
#![recursion_limit = "256"]
//! This is a CLI program and a little testing frame. This file must not be
//! included when using Delta Chat Core as a library.
//!
@@ -31,6 +32,7 @@ use rustyline::{
};
use tokio::fs;
use tokio::runtime::Handle;
use tracing_subscriber::EnvFilter;
mod cmdline;
use self::cmdline::*;
@@ -150,7 +152,7 @@ impl Completer for DcHelper {
}
}
const IMEX_COMMANDS: [&str; 14] = [
const IMEX_COMMANDS: [&str; 13] = [
"initiate-key-transfer",
"get-setupcodebegin",
"continue-key-transfer",
@@ -161,7 +163,6 @@ const IMEX_COMMANDS: [&str; 14] = [
"receive-backup",
"export-keys",
"import-keys",
"export-setup",
"poke",
"reset",
"stop",
@@ -482,9 +483,10 @@ async fn handle_cmd(
#[tokio::main]
async fn main() -> Result<(), Error> {
pretty_env_logger::formatted_timed_builder()
.parse_default_env()
.filter_module("deltachat_repl", log::LevelFilter::Info)
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::from_default_env().add_directive("deltachat_repl=info".parse()?),
)
.init();
let args = std::env::args().collect();

View File

@@ -4,6 +4,7 @@
it will echo back any text send to it, it also will print to console all Delta Chat core events.
Pass --help to the CLI to see available options.
"""
from deltachat_rpc_client import events, run_bot_cli
hooks = events.HookCollection()

View File

@@ -3,6 +3,7 @@
it will echo back any message that has non-empty text and also supports the /help command.
"""
import logging
import sys
from threading import Thread

View File

@@ -2,6 +2,7 @@
"""
Example echo bot without using hooks
"""
import logging
import sys

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "deltachat-rpc-client"
version = "1.137.3"
version = "1.142.7"
description = "Python client for Delta Chat core JSON-RPC interface"
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -37,7 +37,7 @@ deltachat_rpc_client = [
line-length = 120
[tool.ruff]
select = [
lint.select = [
"E", "W", # pycodestyle
"F", # Pyflakes
"N", # pep8-naming

View File

@@ -244,18 +244,22 @@ class Account:
The function returns immediately and the handshake runs in background, sending
and receiving several messages.
Subsequent calls of `secure_join()` will abort previous, unfinished handshakes.
See https://securejoin.readthedocs.io/en/latest/new.html for protocol details.
See https://securejoin.delta.chat/ for protocol details.
:param qrdata: The text of the scanned QR code.
"""
return Chat(self, self._rpc.secure_join(self.id, qrdata))
def get_qr_code(self) -> tuple[str, str]:
"""Get Setup-Contact QR Code text and SVG data.
def get_qr_code(self) -> str:
"""Get Setup-Contact QR Code text.
this data needs to be transferred to another Delta Chat account
This data needs to be transferred to another Delta Chat account
in a second channel, typically used by mobiles with QRcode-show + scan UX.
"""
return self._rpc.get_chat_securejoin_qr_code(self.id, None)
def get_qr_code_svg(self) -> tuple[str, str]:
"""Get Setup-Contact QR code text and SVG."""
return self._rpc.get_chat_securejoin_qr_code_svg(self.id, None)
def get_message_by_id(self, msg_id: int) -> Message:
@@ -297,6 +301,12 @@ class Account:
if event.kind == EventType.INCOMING_MSG:
return event
def wait_for_incoming_msg(self):
"""Wait for incoming message and return it.
Consumes all events before the next incoming message event."""
return self.get_message_by_id(self.wait_for_incoming_msg_event().msg_id)
def wait_for_securejoin_inviter_success(self):
while True:
event = self.wait_for_event()

View File

@@ -2,6 +2,7 @@ from __future__ import annotations
import calendar
from dataclasses import dataclass
from tempfile import NamedTemporaryFile
from typing import TYPE_CHECKING, Optional, Union
from ._utils import AttrDict
@@ -95,7 +96,11 @@ class Chat:
"""Return encryption info for this chat."""
return self._rpc.get_chat_encryption_info(self.account.id, self.id)
def get_qr_code(self) -> tuple[str, str]:
def get_qr_code(self) -> str:
"""Get Join-Group QR code text."""
return self._rpc.get_chat_securejoin_qr_code(self.account.id, self.id)
def get_qr_code_svg(self) -> tuple[str, str]:
"""Get Join-Group QR code text and SVG data."""
return self._rpc.get_chat_securejoin_qr_code_svg(self.account.id, self.id)
@@ -265,3 +270,11 @@ class Chat:
location["message"] = Message(self.account, location.msg_id)
locations.append(location)
return locations
def send_contact(self, contact: Contact):
"""Send contact to the chat."""
vcard = contact.make_vcard()
with NamedTemporaryFile(suffix=".vcard") as f:
f.write(vcard.encode())
f.flush()
self._rpc.send_msg(self.account.id, self.id, {"viewtype": ViewType.VCARD, "file": f.name})

View File

@@ -61,6 +61,8 @@ class EventType(str, Enum):
WEBXDC_INSTANCE_DELETED = "WebxdcInstanceDeleted"
CHATLIST_CHANGED = "ChatlistChanged"
CHATLIST_ITEM_CHANGED = "ChatlistItemChanged"
CONFIG_SYNCED = "ConfigSynced"
WEBXDC_REALTIME_DATA = "WebxdcRealtimeData"
class ChatId(IntEnum):
@@ -113,6 +115,7 @@ class ViewType(str, Enum):
FILE = "File"
VIDEOCHAT_INVITATION = "VideochatInvitation"
WEBXDC = "Webxdc"
VCARD = "Vcard"
class SystemMessageType(str, Enum):

View File

@@ -60,3 +60,6 @@ class Contact:
self.account,
self._rpc.create_chat_by_contact_id(self.account.id, self.id),
)
def make_vcard(self) -> str:
return self._rpc.make_vcard(self.account.id, [self.id])

View File

@@ -2,7 +2,7 @@ import json
from dataclasses import dataclass
from typing import TYPE_CHECKING, Optional, Union
from ._utils import AttrDict
from ._utils import AttrDict, futuremethod
from .const import EventType
from .contact import Contact
@@ -70,3 +70,11 @@ class Message:
event = self.account.wait_for_event()
if event.kind == EventType.MSG_DELIVERED and event.msg_id == self.id:
break
@futuremethod
def send_webxdc_realtime_advertisement(self):
yield self._rpc.send_webxdc_realtime_advertisement.future(self.account.id, self.id)
@futuremethod
def send_webxdc_realtime_data(self, data) -> None:
yield self._rpc.send_webxdc_realtime_data.future(self.account.id, self.id, list(data))

View File

@@ -114,13 +114,13 @@ class ACFactory:
return to_client.run_until(lambda e: e.kind == EventType.INCOMING_MSG)
@pytest.fixture()
@pytest.fixture
def rpc(tmp_path) -> AsyncGenerator:
rpc_server = Rpc(accounts_dir=str(tmp_path / "accounts"))
with rpc_server:
yield rpc_server
@pytest.fixture()
@pytest.fixture
def acfactory(rpc) -> AsyncGenerator:
return ACFactory(DeltaChat(rpc))

View File

@@ -126,8 +126,7 @@ def test_download_on_demand(acfactory: ACFactory) -> None:
alice.set_config("download_limit", "1")
event = bob.wait_for_incoming_msg_event()
msg = bob.get_message_by_id(event.msg_id)
msg = bob.wait_for_incoming_msg()
chat_id = msg.get_snapshot().chat_id
msg.get_snapshot().chat.accept()
bob.get_chat_by_id(chat_id).send_message(
@@ -135,13 +134,15 @@ def test_download_on_demand(acfactory: ACFactory) -> None:
html=base64.b64encode(os.urandom(300000)).decode("utf-8"),
)
msg_id = alice.wait_for_incoming_msg_event().msg_id
assert alice.get_message_by_id(msg_id).get_snapshot().download_state == const.DownloadState.AVAILABLE
message = alice.wait_for_incoming_msg()
snapshot = message.get_snapshot()
assert snapshot.download_state == const.DownloadState.AVAILABLE
alice.clear_all_events()
chat_id = alice.get_message_by_id(msg_id).get_snapshot().chat_id
alice._rpc.download_full_message(alice.id, msg_id)
snapshot = message.get_snapshot()
chat_id = snapshot.chat_id
alice._rpc.download_full_message(alice.id, message.id)
wait_for_chatlist_specific_item(alice, chat_id)
@@ -177,8 +178,7 @@ def test_imap_sync_seen_msgs(acfactory: ACFactory) -> None:
alice_chat_bob.send_text("hello")
event = bob.wait_for_incoming_msg_event()
msg = bob.get_message_by_id(event.msg_id)
msg = bob.wait_for_incoming_msg()
bob_chat_id = msg.get_snapshot().chat_id
msg.get_snapshot().chat.accept()
@@ -189,8 +189,7 @@ def test_imap_sync_seen_msgs(acfactory: ACFactory) -> None:
# make sure alice_second_device already received the message
alice_second_device.wait_for_incoming_msg_event()
event = alice.wait_for_incoming_msg_event()
msg = alice.get_message_by_id(event.msg_id)
msg = alice.wait_for_incoming_msg()
alice_second_device.clear_all_events()
msg.mark_seen()
@@ -211,6 +210,7 @@ def test_multidevice_sync_chat(acfactory: ACFactory) -> None:
alice_second_device.clear_all_events()
alice_chat_bob.pin()
wait_for_chatlist_specific_item(alice_second_device, alice_chat_bob.id)
assert alice_second_device.get_chat_by_id(alice_chat_bob.id).get_basic_snapshot().pinned
alice_second_device.clear_all_events()
alice_chat_bob.mute()

View File

@@ -0,0 +1,210 @@
#!/usr/bin/env python3
"""
Testing webxdc iroh connectivity
If you want to debug iroh at rust-trace/log level set
RUST_LOG=iroh_net=trace,iroh_gossip=trace
"""
import sys
import threading
import time
import pytest
from deltachat_rpc_client import EventType
@pytest.fixture
def path_to_webxdc(request):
p = request.path.parent.parent.parent.joinpath("test-data/webxdc/chess.xdc")
assert p.exists()
return str(p)
def log(msg):
print()
print("*" * 80 + "\n" + msg + "\n", file=sys.stderr)
print()
def setup_realtime_webxdc(ac1, ac2, path_to_webxdc):
assert ac1.get_config("webxdc_realtime_enabled") == "1"
assert ac2.get_config("webxdc_realtime_enabled") == "1"
ac1_ac2_chat = ac1.create_chat(ac2)
ac2.create_chat(ac1)
# share a webxdc app between ac1 and ac2
ac1_webxdc_msg = ac1_ac2_chat.send_message(text="play", file=path_to_webxdc)
ac2_webxdc_msg = ac2.wait_for_incoming_msg()
assert ac2_webxdc_msg.get_snapshot().text == "play"
# send iroh announcements simultaneously
log("sending ac1 -> ac2 realtime advertisement and additional message")
ac1_webxdc_msg.send_webxdc_realtime_advertisement()
log("sending ac2 -> ac1 realtime advertisement and additional message")
ac2_webxdc_msg.send_webxdc_realtime_advertisement()
return ac1_webxdc_msg, ac2_webxdc_msg
def setup_thread_send_realtime_data(msg, data):
def thread_run():
for _i in range(10):
msg.send_webxdc_realtime_data(data)
time.sleep(1)
threading.Thread(target=thread_run, daemon=True).start()
def wait_receive_realtime_data(msg_data_list):
account = msg_data_list[0][0].account
msg_data_list = msg_data_list[:]
log(f"account {account.id}: waiting for realtime data {msg_data_list}")
while msg_data_list:
event = account.wait_for_event()
if event.kind == EventType.WEBXDC_REALTIME_DATA:
for i, (msg, data) in enumerate(msg_data_list):
if msg.id == event.msg_id:
assert list(data) == event.data
log(f"msg {msg.id}: got correct realtime data {data}")
del msg_data_list[i]
break
def test_realtime_sequentially(acfactory, path_to_webxdc):
"""Test two peers trying to establish connection sequentially."""
ac1, ac2 = acfactory.get_online_accounts(2)
ac1.set_config("webxdc_realtime_enabled", "1")
ac2.set_config("webxdc_realtime_enabled", "1")
ac1.create_chat(ac2)
ac2.create_chat(ac1)
# share a webxdc app between ac1 and ac2
ac1_webxdc_msg = acfactory.send_message(from_account=ac1, to_account=ac2, text="play", file=path_to_webxdc)
ac2_webxdc_msg = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id)
snapshot = ac2_webxdc_msg.get_snapshot()
assert snapshot.text == "play"
# send iroh announcements sequentially
log("sending ac1 -> ac2 realtime advertisement and additional message")
ac1_webxdc_msg.send_webxdc_realtime_advertisement()
acfactory.send_message(from_account=ac1, to_account=ac2, text="ping1")
log("waiting for incoming message on ac2")
snapshot = ac2.get_message_by_id(ac2.wait_for_incoming_msg_event().msg_id).get_snapshot()
assert snapshot.text == "ping1"
log("sending ac2 -> ac1 realtime advertisement and additional message")
ac2_webxdc_msg.send_webxdc_realtime_advertisement()
acfactory.send_message(from_account=ac2, to_account=ac1, text="ping2")
log("waiting for incoming message on ac1")
snapshot = ac1.get_message_by_id(ac1.wait_for_incoming_msg_event().msg_id).get_snapshot()
assert snapshot.text == "ping2"
log("sending realtime data ac1 -> ac2")
ac1_webxdc_msg.send_webxdc_realtime_data(b"foo")
log("ac2: waiting for realtime data")
while 1:
event = ac2.wait_for_event()
if event.kind == EventType.WEBXDC_REALTIME_DATA:
assert event.data == list(b"foo")
break
def test_realtime_simultaneously(acfactory, path_to_webxdc):
"""Test two peers trying to establish connection simultaneously."""
ac1, ac2 = acfactory.get_online_accounts(2)
ac1.set_config("webxdc_realtime_enabled", "1")
ac2.set_config("webxdc_realtime_enabled", "1")
ac1_webxdc_msg, ac2_webxdc_msg = setup_realtime_webxdc(ac1, ac2, path_to_webxdc)
setup_thread_send_realtime_data(ac1_webxdc_msg, [10])
wait_receive_realtime_data([(ac2_webxdc_msg, [10])])
def test_two_parallel_realtime_simultaneously(acfactory, path_to_webxdc):
"""Test two peers trying to establish connection simultaneously."""
ac1, ac2 = acfactory.get_online_accounts(2)
ac1.set_config("webxdc_realtime_enabled", "1")
ac2.set_config("webxdc_realtime_enabled", "1")
ac1_webxdc_msg, ac2_webxdc_msg = setup_realtime_webxdc(ac1, ac2, path_to_webxdc)
ac1_webxdc_msg2, ac2_webxdc_msg2 = setup_realtime_webxdc(ac1, ac2, path_to_webxdc)
setup_thread_send_realtime_data(ac1_webxdc_msg, [10])
setup_thread_send_realtime_data(ac1_webxdc_msg2, [20])
setup_thread_send_realtime_data(ac2_webxdc_msg, [30])
setup_thread_send_realtime_data(ac2_webxdc_msg2, [40])
wait_receive_realtime_data([(ac1_webxdc_msg, [30]), (ac1_webxdc_msg2, [40])])
wait_receive_realtime_data([(ac2_webxdc_msg, [10]), (ac2_webxdc_msg2, [20])])
def test_no_duplicate_messages(acfactory, path_to_webxdc):
"""Test that messages are received only once."""
ac1, ac2 = acfactory.get_online_accounts(2)
ac1.set_config("webxdc_realtime_enabled", "1")
ac2.set_config("webxdc_realtime_enabled", "1")
ac1_ac2_chat = ac1.create_chat(ac2)
ac1_webxdc_msg = ac1_ac2_chat.send_message(text="webxdc", file=path_to_webxdc)
ac2_webxdc_msg = ac2.wait_for_incoming_msg()
ac2_webxdc_msg.get_snapshot().chat.accept()
assert ac2_webxdc_msg.get_snapshot().text == "webxdc"
# Issue a "send" call in parallel with sending advertisement.
# Previously due to a bug this caused subscribing to the channel twice.
ac2_webxdc_msg.send_webxdc_realtime_data.future(b"foobar")
ac2_webxdc_msg.send_webxdc_realtime_advertisement()
def thread_run():
for i in range(10):
data = str(i).encode()
ac1_webxdc_msg.send_webxdc_realtime_data(data)
time.sleep(1)
threading.Thread(target=thread_run, daemon=True).start()
while 1:
event = ac2.wait_for_event()
if event.kind == EventType.WEBXDC_REALTIME_DATA:
n = int(bytes(event.data).decode())
break
while 1:
event = ac2.wait_for_event()
if event.kind == EventType.WEBXDC_REALTIME_DATA:
assert int(bytes(event.data).decode()) > n
break
def test_no_reordering(acfactory, path_to_webxdc):
"""Test that sending a lot of realtime messages does not result in reordering."""
ac1, ac2 = acfactory.get_online_accounts(2)
ac1.set_config("webxdc_realtime_enabled", "1")
ac2.set_config("webxdc_realtime_enabled", "1")
ac1_webxdc_msg, ac2_webxdc_msg = setup_realtime_webxdc(ac1, ac2, path_to_webxdc)
setup_thread_send_realtime_data(ac1_webxdc_msg, b"hello")
wait_receive_realtime_data([(ac2_webxdc_msg, b"hello")])
for i in range(200):
ac1_webxdc_msg.send_webxdc_realtime_data([i])
for i in range(200):
while 1:
event = ac2.wait_for_event()
if event.kind == EventType.WEBXDC_REALTIME_DATA and bytes(event.data) != b"hello":
if event.data[0] == i:
break
pytest.fail("Reordering detected")

View File

@@ -1,13 +1,14 @@
import logging
import pytest
from deltachat_rpc_client import Chat, EventType, SpecialContactId
def test_qr_setup_contact(acfactory, tmp_path) -> None:
alice, bob = acfactory.get_online_accounts(2)
qr_code, _svg = alice.get_qr_code()
qr_code = alice.get_qr_code()
bob.secure_join(qr_code)
alice.wait_for_securejoin_inviter_success()
@@ -30,13 +31,35 @@ def test_qr_setup_contact(acfactory, tmp_path) -> None:
bob2.export_self_keys(tmp_path)
logging.info("Bob imports a key")
bob.import_self_keys(tmp_path / "private-key-default.asc")
bob.import_self_keys(tmp_path)
assert bob.get_config("key_id") == "2"
bob_contact_alice_snapshot = bob_contact_alice.get_snapshot()
assert not bob_contact_alice_snapshot.is_verified
def test_qr_setup_contact_svg(acfactory) -> None:
alice = acfactory.new_configured_account()
_, _, domain = alice.get_config("addr").rpartition("@")
_qr_code, svg = alice.get_qr_code_svg()
# Test that email address is in SVG
# when we have no display name.
# Check only the domain name, because
# long address may be split over multiple lines
# and not matched.
assert domain in svg
alice.set_config("displayname", "Alice")
# Test that display name is used
# in SVG and no address is visible.
_qr_code, svg = alice.get_qr_code_svg()
assert domain not in svg
assert "Alice" in svg
@pytest.mark.parametrize("protect", [True, False])
def test_qr_securejoin(acfactory, protect):
alice, bob = acfactory.get_online_accounts(2)
@@ -46,7 +69,7 @@ def test_qr_securejoin(acfactory, protect):
assert alice_chat.get_basic_snapshot().is_protected == protect
logging.info("Bob joins verified group")
qr_code, _svg = alice_chat.get_qr_code()
qr_code = alice_chat.get_qr_code()
bob.secure_join(qr_code)
# Check that at least some of the handshake messages are deleted.
@@ -91,7 +114,7 @@ def test_qr_securejoin_contact_request(acfactory) -> None:
alice_chat = alice.create_group("Verified group", protect=True)
logging.info("Bob joins verified group")
qr_code, _svg = alice_chat.get_qr_code()
qr_code = alice_chat.get_qr_code()
bob.secure_join(qr_code)
while True:
event = bob.wait_for_event()
@@ -106,7 +129,7 @@ def test_qr_readreceipt(acfactory) -> None:
alice, bob, charlie = acfactory.get_online_accounts(3)
logging.info("Bob and Charlie setup contact with Alice")
qr_code, _svg = alice.get_qr_code()
qr_code = alice.get_qr_code()
bob.secure_join(qr_code)
charlie.secure_join(qr_code)
@@ -168,13 +191,13 @@ 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()
qr_code = 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()
qr_code = alice.get_qr_code()
bob.secure_join(qr_code)
bob.wait_for_securejoin_joiner_success()
@@ -188,7 +211,7 @@ def test_verified_group_recovery(acfactory) -> None:
assert chat.get_basic_snapshot().is_protected
logging.info("ac2 joins verified group")
qr_code, _svg = chat.get_qr_code()
qr_code = chat.get_qr_code()
ac2.secure_join(qr_code)
ac2.wait_for_securejoin_joiner_success()
@@ -205,7 +228,7 @@ def test_verified_group_recovery(acfactory) -> None:
ac2 = acfactory.resetup_account(ac2)
logging.info("ac2 reverifies with ac3")
qr_code, _svg = ac3.get_qr_code()
qr_code = ac3.get_qr_code()
ac2.secure_join(qr_code)
ac2.wait_for_securejoin_joiner_success()
@@ -252,7 +275,7 @@ def test_verified_group_member_added_recovery(acfactory) -> None:
assert chat.get_basic_snapshot().is_protected
logging.info("ac2 joins verified group")
qr_code, _svg = chat.get_qr_code()
qr_code = chat.get_qr_code()
ac2.secure_join(qr_code)
ac2.wait_for_securejoin_joiner_success()
@@ -269,7 +292,7 @@ def test_verified_group_member_added_recovery(acfactory) -> None:
ac2 = acfactory.resetup_account(ac2)
logging.info("ac2 reverifies with ac3")
qr_code, _svg = ac3.get_qr_code()
qr_code = ac3.get_qr_code()
ac2.secure_join(qr_code)
ac2.wait_for_securejoin_joiner_success()
@@ -336,7 +359,7 @@ def test_qr_join_chat_with_pending_bobstate_issue4894(acfactory):
ac1, ac2, ac3, ac4 = acfactory.get_online_accounts(4)
logging.info("ac3: verify with ac2")
qr_code, _svg = ac2.get_qr_code()
qr_code = ac2.get_qr_code()
ac3.secure_join(qr_code)
ac2.wait_for_securejoin_inviter_success()
@@ -346,7 +369,7 @@ def test_qr_join_chat_with_pending_bobstate_issue4894(acfactory):
logging.info("ac1: create verified group that ac2 fully joins")
ch1 = ac1.create_group("Group", protect=True)
qr_code, _svg = ch1.get_qr_code()
qr_code = ch1.get_qr_code()
ac2.secure_join(qr_code)
ac1.wait_for_securejoin_inviter_success()
@@ -359,7 +382,7 @@ def test_qr_join_chat_with_pending_bobstate_issue4894(acfactory):
break
logging.info("ac1: let ac2 join again but shutoff ac1 in the middle of securejoin")
qr_code, _svg = ch1.get_qr_code()
qr_code = ch1.get_qr_code()
ac2.secure_join(qr_code)
ac1.remove()
logging.info("ac2 now has pending bobstate but ac1 is shutoff")
@@ -381,7 +404,7 @@ def test_qr_join_chat_with_pending_bobstate_issue4894(acfactory):
break
logging.info("ac3: create a join-code for group VG and let ac4 join, check that ac2 got it")
qr_code, _svg = vg.get_qr_code()
qr_code = vg.get_qr_code()
ac4.secure_join(qr_code)
ac3.wait_for_securejoin_inviter_success()
while 1:
@@ -402,7 +425,7 @@ def test_qr_new_group_unblocked(acfactory):
ac1, ac2 = acfactory.get_online_accounts(2)
ac1_chat = ac1.create_group("Group for joining", protect=True)
qr_code, _svg = ac1_chat.get_qr_code()
qr_code = ac1_chat.get_qr_code()
ac2.secure_join(qr_code)
ac1.wait_for_securejoin_inviter_success()
@@ -425,7 +448,7 @@ def test_aeap_flow_verified(acfactory):
logging.info("ac1: create verified-group QR, ac2 scans and joins")
chat = ac1.create_group("hello", protect=True)
assert chat.get_basic_snapshot().is_protected
qr_code, _svg = chat.get_qr_code()
qr_code = chat.get_qr_code()
logging.info("ac2: start QR-code based join-group protocol")
ac2.secure_join(qr_code)
ac1.wait_for_securejoin_inviter_success()
@@ -464,12 +487,12 @@ def test_gossip_verification(acfactory) -> None:
alice, bob, carol = acfactory.get_online_accounts(3)
# Bob verifies Alice.
qr_code, _svg = alice.get_qr_code()
qr_code = 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()
qr_code = carol.get_qr_code()
bob.secure_join(qr_code)
bob.wait_for_securejoin_joiner_success()
@@ -520,16 +543,16 @@ def test_securejoin_after_contact_resetup(acfactory) -> None:
ac3_chat = ac3.create_group("Verified group", protect=True)
# ac1 joins ac3 group.
ac3_qr_code, _svg = ac3_chat.get_qr_code()
ac3_qr_code = 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()
ac1_qr_code = snapshot.chat.get_qr_code()
# ac2 verifies ac1
qr_code, _svg = ac1.get_qr_code()
qr_code = ac1.get_qr_code()
ac2.secure_join(qr_code)
ac2.wait_for_securejoin_joiner_success()
@@ -589,7 +612,7 @@ def test_withdraw_securejoin_qr(acfactory):
assert alice_chat.get_basic_snapshot().is_protected
logging.info("Bob joins verified group")
qr_code, _svg = alice_chat.get_qr_code()
qr_code = alice_chat.get_qr_code()
bob_chat = bob.secure_join(qr_code)
bob.wait_for_securejoin_joiner_success()

View File

@@ -1,14 +1,17 @@
import base64
import concurrent.futures
import json
import logging
import os
import socket
import subprocess
import time
from unittest.mock import MagicMock
import pytest
from deltachat_rpc_client import Contact, EventType, Message, events
from deltachat_rpc_client.const import DownloadState
from deltachat_rpc_client.const import DownloadState, MessageState
from deltachat_rpc_client.direct_imap import DirectImap
from deltachat_rpc_client.rpc import JsonRpcError
@@ -68,6 +71,18 @@ def test_configure_starttls(acfactory) -> None:
assert account.is_configured()
def test_configure_ip(acfactory) -> None:
account = acfactory.new_preconfigured_account()
domain = account.get_config("addr").rsplit("@")[-1]
ip_address = socket.gethostbyname(domain)
# This should fail TLS check.
account.set_config("mail_server", ip_address)
with pytest.raises(JsonRpcError):
account.configure()
def test_account(acfactory) -> None:
alice, bob = acfactory.get_online_accounts(2)
@@ -103,12 +118,12 @@ def test_account(acfactory) -> None:
assert alice.get_chatlist(snapshot=True)
assert alice.get_qr_code()
assert alice.get_fresh_messages()
assert alice.get_next_messages()
# Test sending empty message.
assert len(bob.wait_next_messages()) == 0
alice_chat_bob.send_text("")
messages = bob.wait_next_messages()
assert bob.get_next_messages() == messages
assert len(messages) == 1
message = messages[0]
snapshot = message.get_snapshot()
@@ -508,8 +523,8 @@ def test_reactions_for_a_reordering_move(acfactory):
"""
(ac1,) = acfactory.get_online_accounts(1)
ac2 = acfactory.new_preconfigured_account()
ac2.set_config("mvbox_move", "1")
ac2.configure()
ac2.set_config("mvbox_move", "1")
ac2.bring_online()
chat1 = acfactory.get_accepted_chat(ac1, ac2)
ac2.stop_io()
@@ -584,3 +599,60 @@ def test_download_limit_chat_assignment(acfactory, tmp_path, n_accounts):
#
# Message may be a private reply, so we assign it to 1:1 chat with Alice.
assert snapshot.chat == bob_chat_alice
def test_markseen_contact_request(acfactory, tmp_path):
"""
Test that seen status is synchronized for contact request messages
even though read receipt is not sent.
"""
alice, bob = acfactory.get_online_accounts(2)
# Bob sets up a second device.
bob.export_backup(tmp_path)
files = list(tmp_path.glob("*.tar"))
bob2 = acfactory.get_unconfigured_account()
bob2.import_backup(files[0])
bob2.start_io()
alice_chat_bob = alice.create_chat(bob)
alice_chat_bob.send_text("Hello Bob!")
message = bob.get_message_by_id(bob.wait_for_incoming_msg_event().msg_id)
message2 = bob2.get_message_by_id(bob2.wait_for_incoming_msg_event().msg_id)
assert message2.get_snapshot().state == MessageState.IN_FRESH
message.mark_seen()
while True:
event = bob2.wait_for_event()
if event.kind == EventType.MSGS_NOTICED:
break
assert message2.get_snapshot().state == MessageState.IN_SEEN
def test_get_http_response(acfactory):
alice = acfactory.new_configured_account()
http_response = alice._rpc.get_http_response(alice.id, "https://example.org")
assert http_response["mimetype"] == "text/html"
assert b"<title>Example Domain</title>" in base64.b64decode((http_response["blob"] + "==").encode())
def test_configured_imap_certificate_checks(acfactory):
alice = acfactory.new_configured_account()
configured_certificate_checks = alice.get_config("configured_imap_certificate_checks")
# Certificate checks should be configured (not None)
assert configured_certificate_checks
# 0 is the value old Delta Chat core versions used
# to mean user entered "imap_certificate_checks=0" (Automatic)
# and configuration failed to use strict TLS checks
# so it switched strict TLS checks off.
#
# New versions of Delta Chat are not disabling TLS checks
# unless users explicitly disables them
# or provider database says provider has invalid certificates.
#
# Core 1.142.4, 1.142.5 and 1.142.6 saved this value due to bug.
# This test is a regression test to prevent this happening again.
assert configured_certificate_checks != "0"

View File

@@ -0,0 +1,15 @@
def test_vcard(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_contact_charlie = alice.create_contact("charlie@example.org", "Charlie")
alice_chat_bob = alice_contact_bob.create_chat()
alice_chat_bob.send_contact(alice_contact_charlie)
event = bob.wait_for_incoming_msg_event()
message = bob.get_message_by_id(event.msg_id)
snapshot = message.get_snapshot()
assert snapshot.vcard_contact
assert snapshot.vcard_contact.addr == "charlie@example.org"

View File

@@ -22,9 +22,8 @@ skipsdist = True
skip_install = True
deps =
ruff
black
commands =
black --quiet --check --diff src/ examples/ tests/
ruff format --quiet --diff src/ examples/ tests/
ruff check src/ examples/ tests/
[pytest]

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat-rpc-server"
version = "1.137.3"
version = "1.142.7"
description = "DeltaChat JSON-RPC server"
edition = "2021"
readme = "README.md"
@@ -10,18 +10,18 @@ keywords = ["deltachat", "chat", "openpgp", "email", "encryption"]
categories = ["cryptography", "std", "email"]
[dependencies]
deltachat-jsonrpc = { path = "../deltachat-jsonrpc", default-features = false }
deltachat = { path = "..", default-features = false }
deltachat-jsonrpc = { workspace = true }
deltachat = { workspace = true }
anyhow = "1"
env_logger = { version = "0.11.3" }
futures-lite = "2.3.0"
log = "0.4"
serde_json = "1"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.37.0", features = ["io-std"] }
tokio-util = "0.7.9"
yerpc = { version = "0.5.2", features = ["anyhow_expose", "openrpc"] }
anyhow = { workspace = true }
futures-lite = { workspace = true }
log = { workspace = true }
serde_json = { workspace = true }
serde = { workspace = true, features = ["derive"] }
tokio = { workspace = true, features = ["io-std"] }
tokio-util = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
yerpc = { workspace = true, features = ["anyhow_expose", "openrpc"] }
[features]
default = ["vendored"]

View File

@@ -0,0 +1,3 @@
platform_package
*.tgz
package-lock.json

View File

@@ -0,0 +1,3 @@
platform_package/*
scripts/
*.tgz

View File

@@ -0,0 +1,78 @@
## npm package for deltachat-rpc-server
This is the successor of `deltachat-node`,
it does not use NAPI bindings but instead uses stdio executables
to let you talk to core over jsonrpc over stdio.
This simplifies cross-compilation and even reduces binary size (no CFFI layer and no NAPI layer).
## Usage
> The **minimum** nodejs version for this package is `16`
```
npm i @deltachat/stdio-rpc-server @deltachat/jsonrpc-client
```
```js
import { startDeltaChat } from "@deltachat/stdio-rpc-server";
import { C } from "@deltachat/jsonrpc-client";
async function main() {
const dc = await startDeltaChat("deltachat-data");
console.log(await dc.rpc.getSystemInfo());
dc.close()
}
main()
```
For a more complete example refer to https://github.com/deltachat-bot/echo/pull/69/files (TODO change link when pr is merged).
## How to use on an unsupported platform
<!-- todo instructions, will uses an env var for pointing to `deltachat-rpc-server` binary -->
<!-- todo copy parts from https://github.com/deltachat/deltachat-desktop/blob/7045c6f549e4b9d5caa0709d5bd314bbd9fd53db/docs/UPDATE_CORE.md -->
## How does it work when you install it
NPM automatically installs platform dependent optional dependencies when `os` and `cpu` fields are set correctly.
references:
- https://napi.rs/docs/deep-dive/release#3-the-native-addon-for-different-platforms-is-distributed-through-different-npm-packages, [webarchive version](https://web.archive.org/web/20240309234250/https://napi.rs/docs/deep-dive/release#3-the-native-addon-for-different-platforms-is-distributed-through-different-npm-packages)
- https://docs.npmjs.com/cli/v6/configuring-npm/package-json#cpu
- https://docs.npmjs.com/cli/v6/configuring-npm/package-json#os
When you import this package it searches for the rpc server in the following locations and order:
1. `DELTA_CHAT_RPC_SERVER` environment variable
2. use the PATH when `{takeVersionFromPATH: true}` is supplied in the options.
3. prebuilds in npm packages
so by default it uses the prebuilds.
## How do you built this package in CI
- To build platform packages, run the `build_platform_package.py` script:
```
python3 build_platform_package.py <cargo-target>
# example
python3 build_platform_package.py x86_64-apple-darwin
```
- Then pass it as an artifact to the last CI action that publishes the main package.
- upload all packages from `deltachat-rpc-server/npm-package/platform_package`.
- then publish `deltachat-rpc-server/npm-package`,
- this will run `update_optional_dependencies_and_version.js` (in the `prepack` script),
which puts all platform packages into `optionalDependencies` and updates the `version` in `package.json`
## How to build a version you can use localy on your host machine for development
You can not install the npm packet from the previous section locally, unless you have a local npm registry set up where you upload it too. This is why we have seperate scripts for making it work for local installation.
- If you just need your host platform run `python scripts/make_local_dev_version.py`
- note: this clears the `platform_package` folder
- (advanced) If you need more than one platform for local install you can just run `node scripts/update_optional_dependencies_and_version.js` after building multiple plaftorms with `build_platform_package.py`
## Thanks to nlnet
The initial work on this package was funded by nlnet as part of the [Delta Tauri](https://nlnet.nl/project/DeltaTauri/) Project.

View File

@@ -0,0 +1,42 @@
import { StdioDeltaChat } from "@deltachat/jsonrpc-client";
export interface SearchOptions {
/** whether take deltachat-rpc-server inside of $PATH*/
takeVersionFromPATH: boolean;
/** whether to disable the DELTA_CHAT_RPC_SERVER environment variable */
disableEnvPath: boolean;
}
/**
*
* @returns absolute path to deltachat-rpc-server binary
* @throws when it is not found
*/
export function getRPCServerPath(
options?: Partial<SearchOptions>
): Promise<string>;
export type DeltaChatOverJsonRpcServer = StdioDeltaChat & {
readonly pathToServerBinary: string;
};
export interface StartOptions {
/** whether to disable outputting stderr to the parent process's stderr */
muteStdErr: boolean;
}
/**
*
* @param directory directory for accounts folder
* @param options
*/
export function startDeltaChat(directory: string, options?: Partial<SearchOptions & StartOptions> ): Promise<DeltaChatOverJsonRpcServer>
export namespace FnTypes {
export type getRPCServerPath = typeof getRPCServerPath
export type startDeltaChat = typeof startDeltaChat
}

View File

@@ -0,0 +1,108 @@
//@ts-check
import { spawn } from "node:child_process";
import { stat } from "node:fs/promises";
import os from "node:os";
import process from "node:process";
import { ENV_VAR_NAME, PATH_EXECUTABLE_NAME } from "./src/const.js";
import {
ENV_VAR_LOCATION_NOT_FOUND,
FAILED_TO_START_SERVER_EXECUTABLE,
NPM_NOT_FOUND_SUPPORTED_PLATFORM_ERROR,
NPM_NOT_FOUND_UNSUPPORTED_PLATFORM_ERROR,
} from "./src/errors.js";
import { createRequire } from "node:module";
function findRPCServerInNodeModules() {
const arch = os.arch();
const operating_system = process.platform;
const package_name = `@deltachat/stdio-rpc-server-${operating_system}-${arch}`;
try {
const { resolve } = createRequire(import.meta.url);
return resolve(package_name);
} catch (error) {
console.debug("findRpcServerInNodeModules", error);
const require = createRequire(import.meta.url);
if (
Object.keys(require("./package.json").optionalDependencies).includes(
package_name
)
) {
throw new Error(NPM_NOT_FOUND_SUPPORTED_PLATFORM_ERROR(package_name));
} else {
throw new Error(NPM_NOT_FOUND_UNSUPPORTED_PLATFORM_ERROR());
}
}
}
/** @type {import("./index").FnTypes.getRPCServerPath} */
export async function getRPCServerPath(options = {}) {
const { takeVersionFromPATH, disableEnvPath } = {
takeVersionFromPATH: false,
disableEnvPath: false,
...options,
};
// 1. check if it is set as env var
if (process.env[ENV_VAR_NAME] && !disableEnvPath) {
try {
if (!(await stat(process.env[ENV_VAR_NAME])).isFile()) {
throw new Error(
`expected ${ENV_VAR_NAME} to point to the deltachat-rpc-server executable`
);
}
} catch (error) {
throw new Error(ENV_VAR_LOCATION_NOT_FOUND());
}
return process.env[ENV_VAR_NAME];
}
// 2. check if PATH should be used
if (takeVersionFromPATH) {
return PATH_EXECUTABLE_NAME;
}
// 3. check for prebuilds
return findRPCServerInNodeModules();
}
import { StdioDeltaChat } from "@deltachat/jsonrpc-client";
/** @type {import("./index").FnTypes.startDeltaChat} */
export async function startDeltaChat(directory, options = {}) {
const pathToServerBinary = await getRPCServerPath(options);
const server = spawn(pathToServerBinary, {
env: {
RUST_LOG: process.env.RUST_LOG,
DC_ACCOUNTS_PATH: directory,
},
stdio: ["pipe", "pipe", options.muteStdErr ? "ignore" : "inherit"],
});
server.on("error", (err) => {
throw new Error(FAILED_TO_START_SERVER_EXECUTABLE(pathToServerBinary, err));
});
let shouldClose = false;
server.on("exit", () => {
if (shouldClose) {
return;
}
throw new Error("Server quit");
});
/** @type {import('./index').DeltaChatOverJsonRpcServer} */
//@ts-expect-error
const dc = new StdioDeltaChat(server.stdin, server.stdout, true);
dc.close = () => {
shouldClose = true;
if (!server.kill()) {
console.log("server termination failed");
}
};
//@ts-expect-error
dc.pathToServerBinary = pathToServerBinary;
return dc;
}

View File

@@ -0,0 +1,19 @@
{
"license": "MPL-2.0",
"main": "index.js",
"name": "@deltachat/stdio-rpc-server",
"optionalDependencies": {},
"peerDependencies": {
"@deltachat/jsonrpc-client": "*"
},
"repository": {
"type": "git",
"url": "https://github.com/deltachat/deltachat-core-rust.git"
},
"scripts": {
"prepack": "node scripts/update_optional_dependencies_and_version.js"
},
"type": "module",
"types": "index.d.ts",
"version": "1.142.7"
}

View File

@@ -0,0 +1,53 @@
#!/usr/bin/env python3
import subprocess
from sys import argv
from os import path, makedirs, chdir
from shutil import copy
from src.make_package import write_package_json
# ensure correct working directory
chdir(path.join(path.dirname(path.abspath(__file__)), "../"))
if len(argv) < 2:
print("First argument should be target architecture as required by cargo")
exit(1)
target = argv[1].strip()
subprocess.run(
["cargo", "build", "--release", "-p", "deltachat-rpc-server", "--target", target],
check=True,
)
newpath = "platform_package"
if not path.exists(newpath):
makedirs(newpath)
# make new folder
platform_path = "platform_package/" + target
if not path.exists(platform_path):
makedirs(platform_path)
# copy binary it over
def binary_path(binary_name):
return "../../target/" + target + "/release/" + binary_name
my_binary_name = "deltachat-rpc-server"
if not path.isfile(binary_path("deltachat-rpc-server")):
my_binary_name = "deltachat-rpc-server.exe"
if not path.isfile(binary_path("deltachat-rpc-server.exe")):
print("Did not find the build")
exit(1)
my_binary_path = binary_path(my_binary_name)
copy(my_binary_path, platform_path + "/" + my_binary_name)
# make a package.json for it
write_package_json(platform_path, target, my_binary_name)

View File

@@ -0,0 +1,34 @@
# This script is for making a version of the npm packet that you can install locally
import subprocess
from sys import argv
from os import path, makedirs, chdir
import re
import json
import tomllib
from shutil import copy, rmtree
# ensure correct working directory
chdir(path.join(path.dirname(path.abspath(__file__)), "../"))
# get host target with "rustc -vV"
output = subprocess.run(["rustc", "-vV"], capture_output=True)
host_target = re.search('host: ([-\\w]*)', output.stdout.decode("utf-8")).group(1)
print("host target to build for is:", host_target)
# clean platform_package folder
newpath = r'platform_package'
if not path.exists(newpath):
makedirs(newpath)
else:
rmtree(path.join(path.dirname(path.abspath(__file__)), "../platform_package/"))
makedirs(newpath)
# run build_platform_package.py with the host's target to build it
subprocess.run(["python", "scripts/build_platform_package.py", host_target], capture_output=False, check=True)
# run update_optional_dependencies_and_version.js to adjust the package / make it installable locally
subprocess.run(["node", "scripts/update_optional_dependencies_and_version.js", "--local"], capture_output=False, check=True)
# typescript / npm local package installing/linking needs that this package has it's own node_modules folder
subprocess.run(["npm", "i"], capture_output=False, check=True)

View File

@@ -0,0 +1,46 @@
import subprocess
from sys import argv
from os import path, makedirs, chdir, chmod, stat
import json
from shutil import copy
from src.make_package import write_package_json
# ensure correct working directory
chdir(path.join(path.dirname(path.abspath(__file__)), "../"))
if len(argv) < 3:
print("First argument should be target architecture as required by cargo")
print("Second argument should be the location of th built binary (binary_path)")
exit(1)
target = argv[1].strip()
binary_path = argv[2].strip()
output = subprocess.run(["rustc","--print","target-list"], capture_output=True, check=True)
available_targets = output.stdout.decode("utf-8")
if available_targets.find(target) == -1:
print("target", target, "is not known / not valid")
exit(1)
newpath = r'platform_package'
if not path.exists(newpath):
makedirs(newpath)
# make new folder
platform_path = 'platform_package/' + target
if not path.exists(platform_path):
makedirs(platform_path)
# copy binary it over
my_binary_name = path.basename(binary_path)
new_binary_path = platform_path + "/" + my_binary_name
copy(binary_path, new_binary_path)
chmod(new_binary_path, 0o555) # everyone can read & execute, nobody can write
# make a package.json for it
write_package_json(platform_path, target, my_binary_name)

View File

@@ -0,0 +1,21 @@
def convert_cpu_arch_to_npm_cpu_arch(arch):
if arch == "x86_64":
return "x64"
if arch == "i686":
return "ia32"
if arch == "aarch64":
return "arm64"
if arch == "armv7" or arch == "arm":
return "arm"
print("architecture might not be known by nodejs, please make sure it can be returned by 'process.arch':", arch)
return arch
def convert_os_to_npm_os(os):
if os == "windows":
return "win32"
if os == "darwin" or os == "linux":
return os
if os.startswith("android"):
return "android"
print("architecture might not be known by nodejs, please make sure it can be returned by 'process.platform':", os)
return os

View File

@@ -0,0 +1,34 @@
import tomllib
import json
from .convert_platform import convert_cpu_arch_to_npm_cpu_arch, convert_os_to_npm_os
def write_package_json(platform_path, rust_target, my_binary_name):
if len(rust_target.split("-")) == 3:
[cpu_arch, vendor, os] = rust_target.split("-")
else:
[cpu_arch, vendor, os, _env] = rust_target.split("-")
# read version
tomlfile = open("../../Cargo.toml", 'rb')
version = tomllib.load(tomlfile)['package']['version']
package_json = {
"name": "@deltachat/stdio-rpc-server-"
+ convert_os_to_npm_os(os)
+ "-"
+ convert_cpu_arch_to_npm_cpu_arch(cpu_arch),
"version": version,
"os": [convert_os_to_npm_os(os)],
"cpu": [convert_cpu_arch_to_npm_cpu_arch(cpu_arch)],
"main": my_binary_name,
"license": "MPL-2.0",
"repository": {
"type": "git",
"url": "https://github.com/deltachat/deltachat-core-rust.git",
},
}
file = open(platform_path + "/package.json", 'w')
file.write(json.dumps(package_json, indent=4))

View File

@@ -0,0 +1,64 @@
import fs from "node:fs/promises";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const expected_cwd = join(dirname(fileURLToPath(import.meta.url)), "..");
if (process.cwd() !== expected_cwd) {
console.error(
"CWD missmatch: this script needs to be run from " + expected_cwd,
{ actual: process.cwd(), expected: expected_cwd }
);
process.exit(1);
}
// whether to use local paths instead of npm registry version number for the prebuilds in optionalDependencies
// useful for local development
const is_local = process.argv.includes("--local");
const package_json = JSON.parse(await fs.readFile("./package.json", "utf8"));
const cargo_toml = await fs.readFile("../Cargo.toml", "utf8");
const version = cargo_toml
.split("\n")
.find((line) => line.includes("version"))
.split('"')[1];
const platform_packages_dir = "./platform_package";
const platform_package_names = await Promise.all(
(await fs.readdir(platform_packages_dir)).map(async (name) => {
const p = JSON.parse(
await fs.readFile(
join(platform_packages_dir, name, "package.json"),
"utf8"
)
);
if (p.version !== version) {
console.error(
name,
"has a different version than the version of the rpc server.",
{ rpc_server: version, platform_package: p.version }
);
throw new Error("version missmatch");
}
return { folder_name: name, package_name: p.name };
})
);
package_json.version = version;
package_json.optionalDependencies = {};
for (const { folder_name, package_name } of platform_package_names) {
package_json.optionalDependencies[package_name] = is_local
? `file:${expected_cwd}/platform_package/${folder_name}` // npm seems to work better with an absolute path here
: version;
}
if (is_local) {
package_json.peerDependencies["@deltachat/jsonrpc-client"] =
`file:${join(expected_cwd, "/../../deltachat-jsonrpc/typescript")}`;
} else {
package_json.peerDependencies["@deltachat/jsonrpc-client"] = "*";
}
await fs.writeFile("./package.json", JSON.stringify(package_json, null, 4));

View File

@@ -0,0 +1,5 @@
//@ts-check
export const PATH_EXECUTABLE_NAME = 'deltachat-rpc-server'
export const ENV_VAR_NAME = "DELTA_CHAT_RPC_SERVER"

View File

@@ -0,0 +1,41 @@
//@ts-check
import { ENV_VAR_NAME } from "./const.js";
const cargoInstallCommand =
"cargo install --git https://github.com/deltachat/deltachat-core-rust deltachat-rpc-server";
export function NPM_NOT_FOUND_SUPPORTED_PLATFORM_ERROR(package_name) {
return `deltachat-rpc-server not found:
- Install it with "npm i ${package_name}"
- or download/compile deltachat-rpc-server for your platform and
- either put it into your PATH (for example with "${cargoInstallCommand}")
- or set the "${ENV_VAR_NAME}" env var to the path to deltachat-rpc-server"`;
}
export function NPM_NOT_FOUND_UNSUPPORTED_PLATFORM_ERROR() {
return `deltachat-rpc-server not found:
Unfortunately no prebuild is available for your system, so you need to provide deltachat-rpc-server yourself.
- Download or Compile deltachat-rpc-server for your platform and
- either put it into your PATH (for example with "${cargoInstallCommand}")
- or set the "${ENV_VAR_NAME}" env var to the path to deltachat-rpc-server"`;
}
export function ENV_VAR_LOCATION_NOT_FOUND(error) {
return `deltachat-rpc-server not found in ${ENV_VAR_NAME}:
Error: ${error}
Content of ${ENV_VAR_NAME}: "${process.env[ENV_VAR_NAME]}"`;
}
export function FAILED_TO_START_SERVER_EXECUTABLE(pathToServerBinary, error) {
return `Failed to start server executable at '${pathToServerBinary}',
Error: ${error}
Make sure the deltachat-rpc-server binary exists at this location
and you can start it with \`${pathToServerBinary} --version\``;
}

View File

@@ -1,3 +1,4 @@
#![recursion_limit = "256"]
//! Delta Chat core RPC server.
//!
//! It speaks JSON Lines over stdio.
@@ -10,6 +11,7 @@ use deltachat::constants::DC_VERSION_STR;
use deltachat_jsonrpc::api::{Accounts, CommandApi};
use futures_lite::stream::StreamExt;
use tokio::io::{self, AsyncBufReadExt, BufReader};
use tracing_subscriber::EnvFilter;
use yerpc::RpcServer as _;
#[cfg(target_family = "unix")]
@@ -27,6 +29,9 @@ async fn main() {
// "For technical reasons, stdin is implemented by using an ordinary blocking read on a separate
// thread, and it is impossible to cancel that read. This can make shutdown of the runtime hang
// until the user presses enter."
if let Err(error) = &r {
log::error!("Fatal error: {error:#}.")
}
std::process::exit(if r.is_ok() { 0 } else { 1 });
}
@@ -59,7 +64,13 @@ async fn main_impl() -> Result<()> {
#[cfg(target_family = "unix")]
let mut sigterm = signal_unix::signal(signal_unix::SignalKind::terminate())?;
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
// Logs from `log` crate and traces from `tracing` crate
// are configurable with `RUST_LOG` environment variable
// and go to stderr to avoid interferring with JSON-RPC using stdout.
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.with_writer(std::io::stderr)
.init();
let path = std::env::var("DC_ACCOUNTS_PATH").unwrap_or_else(|_| "accounts".to_string());
log::info!("Starting with accounts directory `{}`.", path);
@@ -68,7 +79,7 @@ async fn main_impl() -> Result<()> {
log::info!("Creating JSON-RPC API.");
let accounts = Arc::new(RwLock::new(accounts));
let state = CommandApi::from_arc(accounts.clone());
let state = CommandApi::from_arc(accounts.clone()).await;
let (client, mut out_receiver) = RpcClient::new();
let session = RpcSession::new(client.clone(), state.clone());

View File

@@ -15,6 +15,10 @@ ignore = [
# Unmaintained encoding
"RUSTSEC-2021-0153",
# Problem in curve25519-dalek 3.2.0 used by iroh 0.4.
# curve25519-dalek 4.1.3 has the problem fixed.
"RUSTSEC-2024-0344",
]
[bans]
@@ -23,6 +27,9 @@ ignore = [
# when upgrading.
# Please keep this list alphabetically sorted.
skip = [
{ name = "asn1-rs-derive", version = "0.4.0" },
{ name = "asn1-rs-impl", version = "0.1.0" },
{ name = "asn1-rs", version = "0.5.2" },
{ name = "async-channel", version = "1.9.0" },
{ name = "base16ct", version = "0.1.1" },
{ name = "base64", version = "<0.21" },
@@ -34,46 +41,77 @@ skip = [
{ name = "darling_core", version = "<0.14" },
{ name = "darling_macro", version = "<0.14" },
{ name = "darling", version = "<0.14" },
{ name = "der_derive", version = "0.6.1" },
{ name = "derive_more", version = "0.99.17" },
{ name = "der-parser", version = "8.2.0" },
{ name = "der", version = "0.6.1" },
{ name = "digest", version = "<0.10" },
{ name = "dlopen2", version = "0.4.1" },
{ name = "ed25519-dalek", version = "1.0.1" },
{ name = "ed25519", version = "1.5.3" },
{ name = "env_logger", version = "0.10.2" },
{ name = "event-listener", version = "2.5.3" },
{ name = "event-listener", version = "4.0.3" },
{ name = "fastrand", version = "1.9.0" },
{ name = "futures-lite", version = "1.13.0" },
{ name = "getrandom", version = "<0.2" },
{ name = "h2", version = "0.3.26" },
{ name = "http-body", version = "0.4.6" },
{ name = "http", version = "0.2.12" },
{ name = "hyper", version = "0.14.28" },
{ name = "idna", version = "0.4.0" },
{ name = "netlink-packet-core", version = "0.5.0" },
{ name = "netlink-packet-route", version = "0.15.0" },
{ name = "nix", version = "0.26.4" },
{ name = "oid-registry", version = "0.6.1" },
{ name = "pem-rfc7468", version = "0.6.0" },
{ name = "pem", version = "1.1.1" },
{ name = "pkcs8", version = "0.9.0" },
{ name = "quick-error", version = "<2.0" },
{ name = "rand_chacha", version = "<0.3" },
{ name = "rand_core", version = "<0.6" },
{ name = "rand", version = "<0.8" },
{ name = "rcgen", version = "<0.12.1" },
{ name = "redox_syscall", version = "0.3.5" },
{ name = "regex-automata", version = "0.1.10" },
{ name = "regex-syntax", version = "0.6.29" },
{ name = "ring", version = "0.16.20" },
{ name = "rustls-pemfile", version = "1.0.4" },
{ name = "rustls", version = "0.21.11" },
{ name = "rustls-webpki", version = "0.101.7" },
{ name = "sec1", version = "0.3.0" },
{ name = "sha2", version = "<0.10" },
{ name = "signature", version = "1.6.4" },
{ name = "spin", version = "<0.9.6" },
{ name = "spki", version = "0.6.0" },
{ name = "ssh-encoding", version = "0.1.0" },
{ name = "ssh-key", version = "0.5.1" },
{ name = "strsim", version = "0.10.0" },
{ name = "sync_wrapper", version = "0.1.2" },
{ name = "synstructure", version = "0.12.6" },
{ name = "syn", version = "1.0.109" },
{ name = "system-configuration-sys", version = "0.5.0" },
{ name = "system-configuration", version = "0.5.1" },
{ name = "time", version = "<0.3" },
{ name = "tokio-rustls", version = "0.24.1" },
{ name = "toml_edit", version = "0.21.1" },
{ name = "untrusted", version = "0.7.1" },
{ name = "wasi", version = "<0.11" },
{ name = "webpki-roots", version ="0.25.4" },
{ name = "windows_aarch64_gnullvm", version = "<0.52" },
{ name = "windows_aarch64_msvc", version = "<0.52" },
{ name = "windows-core", version = "<0.54.0" },
{ 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", version = "<0.54.0" },
{ 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" },
{ name = "winreg", version = "0.50.0" },
{ name = "x509-parser", version = "<0.16.0" },
]

65
flake.lock generated
View File

@@ -7,11 +7,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1710633978,
"narHash": "sha256-yemnwSvW7cdWtXGpivFA5jDO35rGPs6fqxlQ4l6ODXs=",
"lastModified": 1712088936,
"narHash": "sha256-mVjeSWQiR/t4UZ9fUawY9OEPAhY1R3meYG+0oh8DUBs=",
"owner": "tadfisher",
"repo": "android-nixpkgs",
"rev": "e91fb3d8517538e5ad9b422c9a4f604b56008a9e",
"rev": "2d8181caef279f19c4a33dc694723f89ffc195d4",
"type": "github"
},
"original": {
@@ -29,11 +29,11 @@
]
},
"locked": {
"lastModified": 1708939976,
"narHash": "sha256-O5+nFozxz2Vubpdl1YZtPrilcIXPcRAjqNdNE8oCRoA=",
"lastModified": 1711099426,
"narHash": "sha256-HzpgM/wc3aqpnHJJ2oDqPBkNsqWbW0WfWUO8lKu8nGk=",
"owner": "numtide",
"repo": "devshell",
"rev": "5ddecd67edbd568ebe0a55905273e56cc82aabe3",
"rev": "2d45b54ca4a183f2fdcf4b19c895b64fbf620ee8",
"type": "github"
},
"original": {
@@ -48,11 +48,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1710742993,
"narHash": "sha256-W0PQCe0bW3hKF5lHawXrKynBcdSP18Qa4sb8DcUfOqI=",
"lastModified": 1714112748,
"narHash": "sha256-jq6Cpf/pQH85p+uTwPPrGG8Ky/zUOTwMJ7mcqc5M4So=",
"owner": "nix-community",
"repo": "fenix",
"rev": "6f2fec850f569d61562d3a47dc263f19e9c7d825",
"rev": "3ae4b908a795b6a3824d401a0702e11a7157d7e1",
"type": "github"
},
"original": {
@@ -84,11 +84,11 @@
"systems": "systems_2"
},
"locked": {
"lastModified": 1709126324,
"narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=",
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "d465f4819400de7c8d874d50b982301f28a84605",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
@@ -120,11 +120,11 @@
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1698420672,
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
"lastModified": 1713520724,
"narHash": "sha256-CO8MmVDmqZX2FovL75pu5BvwhW+Vugc7Q6ze7Hj8heI=",
"owner": "nix-community",
"repo": "naersk",
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
"rev": "c5037590290c6c7dae2e42e7da1e247e54ed2d49",
"type": "github"
},
"original": {
@@ -150,11 +150,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1709237383,
"narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=",
"lastModified": 1711703276,
"narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8",
"rev": "d8fe5e6c92d0d190646fb9f1056741a229980089",
"type": "github"
},
"original": {
@@ -166,11 +166,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1710631334,
"narHash": "sha256-rL5LSYd85kplL5othxK5lmAtjyMOBg390sGBTb3LRMM=",
"lastModified": 1713895582,
"narHash": "sha256-cfh1hi+6muQMbi9acOlju3V1gl8BEaZBXBR9jQfQi4U=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "c75037bbf9093a2acb617804ee46320d6d1fea5a",
"rev": "572af610f6151fd41c212f897c71f7056e3fb518",
"type": "github"
},
"original": {
@@ -182,12 +182,11 @@
},
"nixpkgs_3": {
"locked": {
"lastModified": 1710765496,
"narHash": "sha256-p7ryWEeQfMwTB6E0wIUd5V2cFTgq+DRRBz2hYGnJZyA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e367f7a1fb93137af22a3908f00b9a35e2d286a7",
"type": "github"
"lastModified": 1711668574,
"narHash": "sha256-u1dfs0ASQIEr1icTVrsKwg2xToIpn7ZXxW3RHfHxshg=",
"path": "/nix/store/9fpv0kjq9a80isa1wkkvrdqsh9dpcn05-source",
"rev": "219951b495fc2eac67b1456824cc1ec1fd2ee659",
"type": "path"
},
"original": {
"id": "nixpkgs",
@@ -196,11 +195,11 @@
},
"nixpkgs_4": {
"locked": {
"lastModified": 1710631334,
"narHash": "sha256-rL5LSYd85kplL5othxK5lmAtjyMOBg390sGBTb3LRMM=",
"lastModified": 1714076141,
"narHash": "sha256-Drmja/f5MRHZCskS6mvzFqxEaZMeciScCTFxWVLqWEY=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "c75037bbf9093a2acb617804ee46320d6d1fea5a",
"rev": "7bb2ccd8cdc44c91edba16c48d2c8f331fb3d856",
"type": "github"
},
"original": {
@@ -223,11 +222,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1710708100,
"narHash": "sha256-Jd6pmXlwKk5uYcjyO/8BfbUVmx8g31Qfk7auI2IG66A=",
"lastModified": 1714031783,
"narHash": "sha256-xS/niQsq1CQPOe4M4jvVPO2cnXS/EIeRG5gIopUbk+Q=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "b6d1887bc4f9543b6c6bf098179a62446f34a6c3",
"rev": "56bee2ddafa6177b19c631eedc88d43366553223",
"type": "github"
},
"original": {

View File

@@ -35,6 +35,7 @@
./CMakeLists.txt
./CONTRIBUTING.md
./deltachat_derive
./deltachat-contact-tools
./deltachat-ffi
./deltachat-jsonrpc
./deltachat-ratelimit
@@ -482,6 +483,7 @@
format = "pyproject";
propagatedBuildInputs = [
pkgs.python3Packages.setuptools
pkgs.python3Packages.imap-tools
];
};
@@ -523,15 +525,26 @@
};
};
devShells.default = pkgs.mkShell {
devShells.default = let
pkgs = import nixpkgs {
system = system;
overlays = [ fenix.overlays.default ];
};
in pkgs.mkShell {
buildInputs = with pkgs; [
cargo
clippy
rustc
rustfmt
rust-analyzer
(fenix.packages.${system}.complete.withComponents [
"cargo"
"clippy"
"rust-src"
"rustc"
"rustfmt"
])
cargo-deny
rust-analyzer-nightly
cargo-nextest
perl # needed to build vendored OpenSSL
git-cliff
];
};
}

2719
fuzz/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -30,6 +30,7 @@ module.exports = {
DC_DOWNLOAD_IN_PROGRESS: 1000,
DC_DOWNLOAD_UNDECIPHERABLE: 30,
DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE: 2200,
DC_EVENT_CHANNEL_OVERFLOW: 2400,
DC_EVENT_CHATLIST_CHANGED: 2300,
DC_EVENT_CHATLIST_ITEM_CHANGED: 2301,
DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED: 2021,
@@ -66,6 +67,7 @@ module.exports = {
DC_EVENT_SMTP_MESSAGE_SENT: 103,
DC_EVENT_WARNING: 300,
DC_EVENT_WEBXDC_INSTANCE_DELETED: 2121,
DC_EVENT_WEBXDC_REALTIME_DATA: 2150,
DC_EVENT_WEBXDC_STATUS_UPDATE: 2120,
DC_GCL_ADD_ALLDONE_HINT: 4,
DC_GCL_ADD_SELF: 2,
@@ -110,6 +112,7 @@ module.exports = {
DC_MSG_IMAGE: 20,
DC_MSG_STICKER: 23,
DC_MSG_TEXT: 10,
DC_MSG_VCARD: 90,
DC_MSG_VIDEO: 50,
DC_MSG_VIDEOCHAT_INVITATION: 70,
DC_MSG_VOICE: 41,
@@ -125,6 +128,7 @@ module.exports = {
DC_QR_ASK_VERIFYCONTACT: 200,
DC_QR_ASK_VERIFYGROUP: 202,
DC_QR_BACKUP: 251,
DC_QR_BACKUP2: 252,
DC_QR_ERROR: 400,
DC_QR_FPR_MISMATCH: 220,
DC_QR_FPR_OK: 210,
@@ -173,6 +177,7 @@ module.exports = {
DC_STR_CONFIGURATION_FAILED: 84,
DC_STR_CONNECTED: 107,
DC_STR_CONNTECTING: 108,
DC_STR_CONTACT: 200,
DC_STR_CONTACT_NOT_VERIFIED: 36,
DC_STR_CONTACT_SETUP_CHANGED: 37,
DC_STR_CONTACT_VERIFIED: 35,
@@ -266,6 +271,8 @@ module.exports = {
DC_STR_REMOVE_MEMBER_BY_YOU: 130,
DC_STR_REPLY_NOUN: 90,
DC_STR_SAVED_MESSAGES: 69,
DC_STR_SECUREJOIN_WAIT: 190,
DC_STR_SECUREJOIN_WAIT_TIMEOUT: 191,
DC_STR_SECURE_JOIN_GROUP_QR_DESC: 120,
DC_STR_SECURE_JOIN_REPLIES: 118,
DC_STR_SECURE_JOIN_STARTED: 117,

View File

@@ -37,7 +37,9 @@ module.exports = {
2111: 'DC_EVENT_CONFIG_SYNCED',
2120: 'DC_EVENT_WEBXDC_STATUS_UPDATE',
2121: 'DC_EVENT_WEBXDC_INSTANCE_DELETED',
2150: 'DC_EVENT_WEBXDC_REALTIME_DATA',
2200: 'DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE',
2300: 'DC_EVENT_CHATLIST_CHANGED',
2301: 'DC_EVENT_CHATLIST_ITEM_CHANGED'
2301: 'DC_EVENT_CHATLIST_ITEM_CHANGED',
2400: 'DC_EVENT_CHANNEL_OVERFLOW'
}

View File

@@ -30,6 +30,7 @@ export enum C {
DC_DOWNLOAD_IN_PROGRESS = 1000,
DC_DOWNLOAD_UNDECIPHERABLE = 30,
DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE = 2200,
DC_EVENT_CHANNEL_OVERFLOW = 2400,
DC_EVENT_CHATLIST_CHANGED = 2300,
DC_EVENT_CHATLIST_ITEM_CHANGED = 2301,
DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED = 2021,
@@ -66,6 +67,7 @@ export enum C {
DC_EVENT_SMTP_MESSAGE_SENT = 103,
DC_EVENT_WARNING = 300,
DC_EVENT_WEBXDC_INSTANCE_DELETED = 2121,
DC_EVENT_WEBXDC_REALTIME_DATA = 2150,
DC_EVENT_WEBXDC_STATUS_UPDATE = 2120,
DC_GCL_ADD_ALLDONE_HINT = 4,
DC_GCL_ADD_SELF = 2,
@@ -110,6 +112,7 @@ export enum C {
DC_MSG_IMAGE = 20,
DC_MSG_STICKER = 23,
DC_MSG_TEXT = 10,
DC_MSG_VCARD = 90,
DC_MSG_VIDEO = 50,
DC_MSG_VIDEOCHAT_INVITATION = 70,
DC_MSG_VOICE = 41,
@@ -125,6 +128,7 @@ export enum C {
DC_QR_ASK_VERIFYCONTACT = 200,
DC_QR_ASK_VERIFYGROUP = 202,
DC_QR_BACKUP = 251,
DC_QR_BACKUP2 = 252,
DC_QR_ERROR = 400,
DC_QR_FPR_MISMATCH = 220,
DC_QR_FPR_OK = 210,
@@ -173,6 +177,7 @@ export enum C {
DC_STR_CONFIGURATION_FAILED = 84,
DC_STR_CONNECTED = 107,
DC_STR_CONNTECTING = 108,
DC_STR_CONTACT = 200,
DC_STR_CONTACT_NOT_VERIFIED = 36,
DC_STR_CONTACT_SETUP_CHANGED = 37,
DC_STR_CONTACT_VERIFIED = 35,
@@ -266,6 +271,8 @@ export enum C {
DC_STR_REMOVE_MEMBER_BY_YOU = 130,
DC_STR_REPLY_NOUN = 90,
DC_STR_SAVED_MESSAGES = 69,
DC_STR_SECUREJOIN_WAIT = 190,
DC_STR_SECUREJOIN_WAIT_TIMEOUT = 191,
DC_STR_SECURE_JOIN_GROUP_QR_DESC = 120,
DC_STR_SECURE_JOIN_REPLIES = 118,
DC_STR_SECURE_JOIN_STARTED = 117,
@@ -334,7 +341,9 @@ export const EventId2EventName: { [key: number]: string } = {
2111: 'DC_EVENT_CONFIG_SYNCED',
2120: 'DC_EVENT_WEBXDC_STATUS_UPDATE',
2121: 'DC_EVENT_WEBXDC_INSTANCE_DELETED',
2150: 'DC_EVENT_WEBXDC_REALTIME_DATA',
2200: 'DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE',
2300: 'DC_EVENT_CHATLIST_CHANGED',
2301: 'DC_EVENT_CHATLIST_ITEM_CHANGED',
2400: 'DC_EVENT_CHANNEL_OVERFLOW',
}

View File

@@ -1,5 +1,4 @@
#define NAPI_VERSION 4
#define NAPI_EXPERIMENTAL
#include <stdlib.h>
#include <stdio.h>

View File

@@ -11,7 +11,7 @@
"chai": "~4.3.10",
"chai-as-promised": "^7.1.1",
"mocha": "^8.2.1",
"node-gyp": "^10.0.0",
"node-gyp": "~10.1.0",
"prebuildify": "^5.0.1",
"prebuildify-ci": "^1.0.5",
"prettier": "^3.0.3",
@@ -55,5 +55,5 @@
"test:mocha": "mocha node/test/test.mjs --growl --reporter=spec --bail --exit"
},
"types": "node/dist/index.d.ts",
"version": "1.137.3"
"version": "1.142.7"
}

View File

@@ -44,11 +44,6 @@ def test_group_tracking_plugin(acfactory, lp):
ac1, ac2 = acfactory.get_online_accounts(2)
botproc.fnmatch_lines(
"""
*ac_configure_completed*
""",
)
ac1.add_account_plugin(FFIEventLogger(ac1))
ac2.add_account_plugin(FFIEventLogger(ac2))

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "deltachat"
version = "1.137.3"
version = "1.142.7"
description = "Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat"
readme = "README.rst"
requires-python = ">=3.7"
@@ -46,7 +46,7 @@ deltachat = [
line-length = 120
[tool.ruff]
select = ["E", "F", "W", "YTT", "C4", "ISC", "ICN", "TID", "DTZ", "PLC", "PLE", "PLW", "PIE", "COM", "UP004", "UP010", "UP031", "UP032", "ANN204"]
lint.select = ["E", "F", "W", "YTT", "C4", "ISC", "ICN", "TID", "DTZ", "PLC", "PLE", "PLW", "PIE", "COM", "UP004", "UP010", "UP031", "UP032", "ANN204"]
line-length = 120
[tool.isort]

View File

@@ -328,10 +328,12 @@ class EventThread(threading.Thread):
elif name == "DC_EVENT_REACTIONS_CHANGED":
assert ffi_event.data1 > 0
msg = account.get_message_by_id(ffi_event.data2)
yield "ac_reactions_changed", {"message": msg}
if msg is not None:
yield "ac_reactions_changed", {"message": msg}
elif name == "DC_EVENT_MSG_DELIVERED":
msg = account.get_message_by_id(ffi_event.data2)
yield "ac_message_delivered", {"message": msg}
if msg is not None:
yield "ac_message_delivered", {"message": msg}
elif name == "DC_EVENT_CHAT_MODIFIED":
chat = account.get_chat_by_id(ffi_event.data1)
yield "ac_chat_modified", {"chat": chat}

View File

@@ -364,6 +364,9 @@ class Message:
else:
# load message from db to get a fresh/current state
dc_msg = ffi.gc(lib.dc_get_msg(self.account._dc_context, self.id), lib.dc_msg_unref)
# Message could be trashed, use the cached object if so.
if dc_msg == ffi.NULL:
dc_msg = self._dc_msg
return lib.dc_msg_get_state(dc_msg)
def is_in_fresh(self):
@@ -392,7 +395,7 @@ class Message:
def is_outgoing(self):
"""Return True if Message is outgoing."""
return self._msgstate in (
return lib.dc_msg_get_state(self._dc_msg) in (
const.DC_STATE_OUT_PREPARING,
const.DC_STATE_OUT_PENDING,
const.DC_STATE_OUT_FAILED,
@@ -484,6 +487,9 @@ class Message:
# load message from db to get a fresh/current state
dc_msg = ffi.gc(lib.dc_get_msg(self.account._dc_context, self.id), lib.dc_msg_unref)
# Message could be trashed, use the cached object if so.
if dc_msg == ffi.NULL:
dc_msg = self._dc_msg
return lib.dc_msg_get_download_state(dc_msg)
def download_full(self) -> None:

View File

@@ -275,6 +275,7 @@ class ACSetup:
def __init__(self, testprocess, init_time) -> None:
self._configured_events = Queue()
self._account2state: Dict[Account, str] = {}
self._account2config: Dict[Account, Dict[str, str]] = {}
self._imap_cleaned: Set[str] = set()
self.testprocess = testprocess
self.init_time = init_time
@@ -336,6 +337,8 @@ class ACSetup:
if not success:
pytest.fail(f"configuring online account {acc} failed: {comment}")
self._account2state[acc] = self.CONFIGURED
if acc in self._account2config:
acc.update_config(self._account2config[acc])
return acc
def _onconfigure_start_io(self, acc):
@@ -523,6 +526,7 @@ class ACFactory:
configdict.setdefault("sentbox_watch", False)
configdict.setdefault("sync_msgs", False)
ac.update_config(configdict)
self._acsetup._account2config[ac] = configdict
self._preconfigure_key(ac, configdict["addr"])
return ac
@@ -548,6 +552,15 @@ class ACFactory:
bot_cfg = self.get_next_liveconfig()
bot_ac = self.prepare_account_from_liveconfig(bot_cfg)
self._acsetup.start_configure(bot_ac)
self.wait_configured(bot_ac)
bot_ac.start_io()
# Wait for DC_EVENT_IMAP_INBOX_IDLE so that all emails appeared in the bot's Inbox later are
# considered new and not existing ones, and thus processed by the bot.
print(bot_ac._logid, "waiting for inbox IDLE to become ready")
bot_ac._evtracker.wait_idle_inbox_ready()
bot_ac.stop_io()
self._acsetup._account2state[bot_ac] = self._acsetup.IDLEREADY
# Forget ac as it will be opened by the bot subprocess
# but keep something in the list to not confuse account generation

View File

@@ -18,14 +18,14 @@ def test_db_busy_error(acfactory):
# make a number of accounts
accounts = acfactory.get_many_online_accounts(3)
log("created %s accounts" % len(accounts))
log(f"created {len(accounts)} accounts")
# put a bigfile into each account
for acc in accounts:
acc.bigfile = os.path.join(acc.get_blobdir(), "bigfile")
with open(acc.bigfile, "wb") as f:
f.write(b"01234567890" * 1000_000)
log("created %s bigfiles" % len(accounts))
log(f"created {len(accounts)} bigfiles")
contact_addrs = [acc.get_self_contact().addr for acc in accounts]
chat = accounts[0].create_group_chat("stress-group")

View File

@@ -1,4 +1,5 @@
import sys
import time
import pytest
import deltachat as dc
@@ -675,3 +676,17 @@ def test_verified_group_vs_delete_server_after(acfactory, tmp_path, lp):
assert msg_in.chat == chat2_offl
assert msg_in.get_sender_contact().addr == ac2.get_config("addr")
assert ac2_offl_ac1_contact.is_verified()
def test_deleted_msgs_dont_reappear(acfactory):
ac1 = acfactory.new_online_configuring_account()
acfactory.bring_accounts_online()
ac1.set_config("bcc_self", "1")
chat = ac1.get_self_contact().create_chat()
msg = chat.send_text("hello")
ac1._evtracker.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
ac1.delete_messages([msg])
ac1._evtracker.get_matching("DC_EVENT_MSG_DELETED")
ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED")
time.sleep(5)
assert len(chat.get_messages()) == 0

View File

@@ -484,6 +484,16 @@ def test_move_works_on_self_sent(acfactory):
ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
def test_move_sync_msgs(acfactory):
ac1 = acfactory.new_online_configuring_account(bcc_self=True, sync_msgs=True, fix_is_chatmail=True)
acfactory.bring_accounts_online()
ac1.set_config("displayname", "Alice")
ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
ac1.set_config("displayname", "Bob")
ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
def test_forward_messages(acfactory, lp):
ac1, ac2 = acfactory.get_online_accounts(2)
chat = ac1.create_chat(ac2)
@@ -1343,7 +1353,6 @@ def test_quote_encrypted(acfactory, lp):
for quoted_msg in msg1, msg3:
# Save the draft with a quote.
# It should be encrypted if quoted message is encrypted.
msg_draft = Message.new_empty(ac1, "text")
msg_draft.set_text("message reply")
msg_draft.quote = quoted_msg
@@ -1357,10 +1366,14 @@ def test_quote_encrypted(acfactory, lp):
chat.set_draft(None)
assert chat.get_draft() is None
# Quote should be replaced with "..." if quoted message is encrypted.
msg_in = ac2._evtracker.wait_next_incoming_message()
assert msg_in.text == "message reply"
assert msg_in.quoted_text == quoted_msg.text
assert msg_in.is_encrypted() == quoted_msg.is_encrypted()
assert not msg_in.is_encrypted()
if quoted_msg.is_encrypted():
assert msg_in.quoted_text == "..."
else:
assert msg_in.quoted_text == quoted_msg.text
def test_quote_attachment(tmp_path, acfactory, lp):
@@ -1559,8 +1572,6 @@ def test_import_export_online_all(acfactory, tmp_path, data, lp):
# check progress events for import
assert imex_tracker.wait_progress(1, progress_upper_limit=249)
assert imex_tracker.wait_progress(500, progress_upper_limit=749)
assert imex_tracker.wait_progress(750, progress_upper_limit=999)
assert imex_tracker.wait_progress(1000)
assert_account_is_proper(ac1)
@@ -2033,14 +2044,15 @@ def test_send_receive_locations(acfactory, lp):
assert chat1.is_sending_locations()
ac1._evtracker.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
# Wait for "enabled location streaming" message.
ac2._evtracker.wait_next_incoming_message()
# First location is sent immediately as a location-only message.
ac1.set_location(latitude=2.0, longitude=3.0, accuracy=0.5)
ac1._evtracker.get_matching("DC_EVENT_LOCATION_CHANGED")
chat1.send_text("🍞")
ac1._evtracker.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
lp.sec("ac2: wait for incoming location message")
# currently core emits location changed before event_incoming message
ac2._evtracker.get_matching("DC_EVENT_LOCATION_CHANGED")
locations = chat2.get_locations()
@@ -2049,7 +2061,7 @@ def test_send_receive_locations(acfactory, lp):
assert locations[0].longitude == 3.0
assert locations[0].accuracy == 0.5
assert locations[0].timestamp > now
assert locations[0].marker == "🍞"
assert locations[0].marker is None
contact = ac2.create_contact(ac1)
locations2 = chat2.get_locations(contact=contact)

View File

@@ -41,12 +41,11 @@ skipsdist = True
skip_install = True
deps =
ruff
black
# pygments required by rst-lint
pygments
restructuredtext_lint
commands =
black --quiet --check --diff setup.py src/deltachat examples/ tests/
ruff format --quiet --diff setup.py src/deltachat examples/ tests/
ruff check src/deltachat tests/ examples/
rst-lint --encoding 'utf-8' README.rst

View File

@@ -1 +1 @@
2024-04-16
2024-08-17

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.77.1
RUST_VERSION=1.80.1
ARCH="$(uname -m)"
test -f "/lib/libc.musl-$ARCH.so.1" && LIBC=musl || LIBC=gnu

View File

@@ -149,7 +149,7 @@ def process_data(data, file):
oauth2 = "Some(Oauth2Authorizer::" + camel(oauth2) + ")" if oauth2 != "" else "None"
provider = ""
before_login_hint = cleanstr(data.get("before_login_hint", ""))
before_login_hint = cleanstr(data.get("before_login_hint", "") or "")
after_login_hint = cleanstr(data.get("after_login_hint", ""))
if (not has_imap and not has_smtp) or (has_imap and has_smtp):
provider += (

View File

@@ -3,4 +3,4 @@ set -euo pipefail
tox -c deltachat-rpc-client -e py --devenv venv
venv/bin/pip install --upgrade pip
cargo install --path deltachat-rpc-server/ --root "$PWD/venv" --debug
cargo install --locked --path deltachat-rpc-server/ --root "$PWD/venv" --debug

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
cargo install --path deltachat-rpc-server/ --root "$PWD/venv" --debug
cargo install --locked --path deltachat-rpc-server/ --root "$PWD/venv" --debug
PATH="$PWD/venv/bin:$PATH" tox -c deltachat-rpc-client

View File

@@ -66,7 +66,11 @@ def main():
parser = ArgumentParser(prog="set_core_version")
parser.add_argument("newversion")
json_list = ["package.json", "deltachat-jsonrpc/typescript/package.json"]
json_list = [
"package.json",
"deltachat-jsonrpc/typescript/package.json",
"deltachat-rpc-server/npm-package/package.json",
]
toml_list = [
"Cargo.toml",
"deltachat-ffi/Cargo.toml",

View File

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

77
spec.md
View File

@@ -1,6 +1,6 @@
# chat-mail specification
Version: 0.34.0
Version: 0.35.0
Status: In-progress
Format: [Semantic Line Breaks](https://sembr.org/)
@@ -22,7 +22,13 @@ to implement typical messenger functions.
- [Locations](#locations)
- [User locations](#user-locations)
- [Points of interest](#points-of-interest)
- [Stickers](#stickers)
- [Voice messages](#voice-messages)
- [Reactions](#reactions)
- [Attaching a contact to a message](#attaching-a-contact-to-a-message)
- [Transitioning to a new e-mail address (AEAP)](#transitioning-to-a-new-e-mail-address-aeap)
- [Miscellaneous](#miscellaneous)
- [Sync messages](#sync-messages)
# Encryption
@@ -461,6 +467,58 @@ As an extension to RFC 9078, it is allowed to send empty reaction message,
in which case all previously sent reactions are retracted.
# Attaching a contact to a message
Messengers MAY allow the user to attach a contact to a message
in order to share it with the chat partner.
The contact MUST be sent as a [vCard](https://datatracker.ietf.org/doc/html/rfc6350).
The vCard MUST contain `EMAIL`,
`FN` (display name),
and `VERSION` (which version of the vCard standard you're using).
If available, it SHOULD contain
`REV` (current timestamp),
`PHOTO` (avatar), and
`KEY` (OpenPGP public key,
in binary format,
encoded with vanilla base64;
note that this is different from the OpenPGP 'ASCII Armor' format).
Example vCard:
```
BEGIN:VCARD
VERSION:4.0
EMAIL:alice@example.org
FN:Alice Wonderland
KEY:data:application/pgp-keys;base64,[Base64-data]
PHOTO:data:image/jpeg;base64,[image in Base64]
REV:20240418T184242Z
END:VCARD
```
It is fine if messengers do include a full vCard parser
and e.g. simply search for the line starting with `EMAIL`
in order to get the email address.
# Transitioning to a new e-mail address (AEAP)
When receiving a message:
- If the key exists, but belongs to another address
- AND there is a `Chat-Version` header
- AND the message is signed correctly
- AND the From address is (also) in the encrypted (and therefore signed) headers
- AND the message timestamp is newer than the contact's `lastseen`
(to prevent changing the address back when messages arrive out of order)
(this condition is not that important
since we will have eventual consistency even without it):
Replace the contact in _all_ groups,
possibly deduplicate the members list,
and add a system message to all of these chats.
# Miscellaneous
Messengers SHOULD use the header `In-Reply-To` as usual.
@@ -484,21 +542,4 @@ We define the effective date of a message
as the sending time of the message as indicated by its Date header,
or the time of first receipt if that date is in the future or unavailable.
# Transitioning to a new e-mail address (AEAP)
When receiving a message:
- If the key exists, but belongs to another address
- AND there is a `Chat-Version` header
- AND the message is signed correctly
- AND the From address is (also) in the encrypted (and therefore signed) headers
- AND the message timestamp is newer than the contact's `lastseen`
(to prevent changing the address back when messages arrive out of order)
(this condition is not that important
since we will have eventual consistency even without it):
Replace the contact in _all_ groups,
possibly deduplicate the members list,
and add a system message to all of these chats.
Copyright © 2017-2021 Delta Chat contributors.

View File

@@ -166,6 +166,19 @@ impl Accounts {
.remove(&id)
.with_context(|| format!("no account with id {id}"))?;
ctx.stop_io().await;
// Explicitly close the database
// to make sure the database file is closed
// and can be removed on Windows.
// If some spawned task tries to use the database afterwards,
// it will fail.
//
// Previously `stop_io()` aborted the tasks without awaiting them
// and this resulted in keeping `Context` clones inside
// `Future`s that were not dropped. This bug is fixed now,
// but explicitly closing the database ensures that file is freed
// even if not all `Context` references are dropped.
ctx.sql.close().await;
drop(ctx);
if let Some(cfg) = self.config.get_account(id) {
@@ -485,10 +498,6 @@ impl Config {
/// Read a configuration from the given file into memory.
pub async fn from_file(file: PathBuf, writable: bool) -> Result<Self> {
let dir = file
.parent()
.context("Cannot get config file directory")?
.to_path_buf();
let mut config = Self::new_nosync(file, writable).await?;
let bytes = fs::read(&config.file)
.await
@@ -500,9 +509,13 @@ impl Config {
// Convert them to relative paths.
let mut modified = false;
for account in &mut config.inner.accounts {
if let Ok(new_dir) = account.dir.strip_prefix(&dir) {
account.dir = new_dir.to_path_buf();
modified = true;
if account.dir.is_absolute() {
if let Some(old_path_parent) = account.dir.parent() {
if let Ok(new_path) = account.dir.strip_prefix(old_path_parent) {
account.dir = new_path.to_path_buf();
modified = true;
}
}
}
}
if modified && writable {

View File

@@ -14,7 +14,6 @@ use once_cell::sync::Lazy;
use crate::config::Config;
use crate::context::Context;
use crate::headerdef::HeaderDef;
use crate::tools::time;
/// `authres` is short for the Authentication-Results header, defined in
/// <https://datatracker.ietf.org/doc/html/rfc8601>, which contains info
@@ -29,45 +28,28 @@ pub(crate) async fn handle_authres(
context: &Context,
mail: &ParsedMail<'_>,
from: &str,
message_time: i64,
) -> Result<DkimResults> {
let from_domain = match EmailAddress::new(from) {
Ok(email) => email.domain,
Err(e) => {
// This email is invalid, but don't return an error, we still want to
// add a stub to the database so that it's not downloaded again
return Err(anyhow::format_err!("invalid email {}: {:#}", from, e));
}
};
let authres = parse_authres_headers(&mail.get_headers(), &from_domain);
update_authservid_candidates(context, &authres).await?;
compute_dkim_results(context, authres, &from_domain, message_time).await
compute_dkim_results(context, authres).await
}
#[derive(Debug)]
pub(crate) struct DkimResults {
/// Whether DKIM passed for this particular e-mail.
pub dkim_passed: bool,
/// Whether DKIM is known to work for e-mails coming from the sender's domain,
/// i.e. whether we expect DKIM to work.
pub dkim_should_work: bool,
/// Whether changing the public Autocrypt key should be allowed.
/// This is false if we expected DKIM to work (dkim_works=true),
/// but it failed now (dkim_passed=false).
pub allow_keychange: bool,
}
impl fmt::Display for DkimResults {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(
fmt,
"DKIM Results: Passed={}, Works={}, Allow_Keychange={}",
self.dkim_passed, self.dkim_should_work, self.allow_keychange
)?;
if !self.allow_keychange {
write!(fmt, " KEYCHANGES NOT ALLOWED!!!!")?;
}
write!(fmt, "DKIM Results: Passed={}", self.dkim_passed)?;
Ok(())
}
}
@@ -218,10 +200,6 @@ async fn update_authservid_candidates(
context
.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
// reset our expectation which DKIMs work.
clear_dkim_works(context).await?
}
Ok(())
}
@@ -238,8 +216,6 @@ async fn update_authservid_candidates(
async fn compute_dkim_results(
context: &Context,
mut authres: ParsedAuthresHeaders,
from_domain: &str,
message_time: i64,
) -> Result<DkimResults> {
let mut dkim_passed = false;
@@ -272,71 +248,7 @@ async fn compute_dkim_results(
}
}
let last_working_timestamp = dkim_works_timestamp(context, from_domain).await?;
let mut dkim_should_work = dkim_should_work(last_working_timestamp)?;
if message_time > last_working_timestamp && dkim_passed {
set_dkim_works_timestamp(context, from_domain, message_time).await?;
dkim_should_work = true;
}
Ok(DkimResults {
dkim_passed,
dkim_should_work,
allow_keychange: dkim_passed || !dkim_should_work,
})
}
/// Whether DKIM in emails from this domain should be considered to work.
fn dkim_should_work(last_working_timestamp: i64) -> Result<bool> {
// When we get an email with valid DKIM-Authentication-Results,
// then we assume that DKIM works for 30 days from this time on.
let should_work_until = last_working_timestamp + 3600 * 24 * 30;
let dkim_ever_worked = last_working_timestamp > 0;
// We're using time() here and not the time when the message
// claims to have been sent (passed around as `message_time`)
// because otherwise an attacker could just put a time way
// in the future into the `Date` header and then we would
// assume that DKIM doesn't have to be valid anymore.
let dkim_should_work_now = should_work_until > time();
Ok(dkim_ever_worked && dkim_should_work_now)
}
async fn dkim_works_timestamp(context: &Context, from_domain: &str) -> Result<i64, anyhow::Error> {
let last_working_timestamp: i64 = context
.sql
.query_get_value(
"SELECT dkim_works FROM sending_domains WHERE domain=?",
(from_domain,),
)
.await?
.unwrap_or(0);
Ok(last_working_timestamp)
}
async fn set_dkim_works_timestamp(
context: &Context,
from_domain: &str,
timestamp: i64,
) -> Result<()> {
context
.sql
.execute(
"INSERT INTO sending_domains (domain, dkim_works) VALUES (?,?)
ON CONFLICT(domain) DO UPDATE SET dkim_works=excluded.dkim_works",
(from_domain, timestamp),
)
.await?;
Ok(())
}
async fn clear_dkim_works(context: &Context) -> Result<()> {
context
.sql
.execute("DELETE FROM sending_domains", ())
.await?;
Ok(())
Ok(DkimResults { dkim_passed })
}
fn parse_authservid_candidates_config(config: &Option<String>) -> BTreeSet<&str> {
@@ -349,19 +261,12 @@ fn parse_authservid_candidates_config(config: &Option<String>) -> BTreeSet<&str>
#[cfg(test)]
mod tests {
#![allow(clippy::indexing_slicing)]
use std::time::Duration;
use tokio::fs;
use tokio::io::AsyncReadExt;
use super::*;
use crate::aheader::EncryptPreference;
use crate::e2ee;
use crate::mimeparser;
use crate::peerstate::Peerstate;
use crate::securejoin::get_securejoin_qr;
use crate::securejoin::join_securejoin;
use crate::test_utils;
use crate::test_utils::TestContext;
use crate::test_utils::TestContextManager;
use crate::tools;
@@ -574,33 +479,8 @@ Authentication-Results: box.hispanilandia.net; spf=pass smtp.mailfrom=adbenitez@
let mail = mailparse::parse_mail(&bytes)?;
let from = &mimeparser::get_from(&mail.headers).unwrap().addr;
let res = handle_authres(&t, &mail, from, time()).await?;
assert!(res.allow_keychange);
}
for entry in &dir {
let mut file = fs::File::open(entry.path()).await?;
bytes.clear();
file.read_to_end(&mut bytes).await.unwrap();
let mail = mailparse::parse_mail(&bytes)?;
let from = &mimeparser::get_from(&mail.headers).unwrap().addr;
let res = handle_authres(&t, &mail, from, time()).await?;
if !res.allow_keychange {
println!(
"!!!!!! FAILURE Receiving {:?}, keychange is not allowed !!!!!!",
entry.path()
);
test_failed = true;
}
let res = handle_authres(&t, &mail, from).await?;
let from_domain = EmailAddress::new(from).unwrap().domain;
assert_eq!(
res.dkim_should_work,
dkim_should_work(dkim_works_timestamp(&t, &from_domain).await?)?
);
assert_eq!(res.dkim_passed, res.dkim_should_work);
// delta.blinzeln.de and gmx.de have invalid DKIM, so the DKIM check should fail
let expected_result = (from_domain != "delta.blinzeln.de") && (from_domain != "gmx.de")
@@ -613,9 +493,8 @@ Authentication-Results: box.hispanilandia.net; spf=pass smtp.mailfrom=adbenitez@
if res.dkim_passed != expected_result {
if authres_parsing_works {
println!(
"!!!!!! FAILURE Receiving {:?}, order {:#?} wrong result: !!!!!!",
"!!!!!! FAILURE Receiving {:?} wrong result: !!!!!!",
entry.path(),
dir.iter().map(|e| e.file_name()).collect::<Vec<_>>()
);
test_failed = true;
}
@@ -638,116 +517,7 @@ Authentication-Results: box.hispanilandia.net; spf=pass smtp.mailfrom=adbenitez@
let bytes = b"From: invalid@from.com
Authentication-Results: dkim=";
let mail = mailparse::parse_mail(bytes).unwrap();
handle_authres(&t, &mail, "invalid@rom.com", time())
.await
.unwrap();
}
#[ignore = "Disallowing keychanges is disabled for now"]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_handle_authres_fails() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = tcm.alice().await;
let bob = tcm.bob().await;
// Bob sends Alice a message, so she gets his key
tcm.send_recv_accept(&bob, &alice, "Hi").await;
// We don't need bob anymore, let's make sure it's not accidentally used
drop(bob);
// Assume Alice receives an email from bob@example.net with
// correct DKIM -> `set_dkim_works()` was called
set_dkim_works_timestamp(&alice, "example.net", time()).await?;
// And Alice knows her server's authserv-id
alice
.set_config(Config::AuthservIdCandidates, Some("example.org"))
.await?;
tcm.section("An attacker, bob2, sends a from-forged email to Alice!");
// Sleep to make sure key reset is ignored because of DKIM failure
// and not because reordering is suspected.
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
let bob2 = tcm.unconfigured().await;
bob2.configure_addr("bob@example.net").await;
e2ee::ensure_secret_key_exists(&bob2).await?;
let chat = bob2.create_chat(&alice).await;
let mut sent = bob2
.send_text(chat.id, "Please send me lots of money")
.await;
sent.payload
.insert_str(0, "Authentication-Results: example.org; dkim=fail\n");
let received = alice.recv_msg(&sent).await;
// Assert that the error tells the user about the problem
assert!(received.error.unwrap().contains("DKIM failed"));
let bob_state = Peerstate::from_addr(&alice, "bob@example.net")
.await?
.unwrap();
// Encryption preference is still mutual.
assert_eq!(bob_state.prefer_encrypt, EncryptPreference::Mutual);
// Also check that the keypair was not changed
assert_eq!(
bob_state.public_key.unwrap(),
test_utils::bob_keypair().public
);
// Since Alice didn't change the key, Bob can't read her message
let received = tcm
.try_send_recv(&alice, &bob2, "My credit card number is 1234")
.await;
assert!(!received.text.contains("1234"));
assert!(received.error.is_some());
tcm.section("Turns out bob2 wasn't an attacker at all, Bob just has a new phone and DKIM just stopped working.");
tcm.section("To fix the key problems, Bob scans Alice's QR code.");
let qr = get_securejoin_qr(&alice.ctx, None).await.unwrap();
join_securejoin(&bob2.ctx, &qr).await.unwrap();
loop {
if let Some(mut sent) = bob2.pop_sent_msg_opt(Duration::ZERO).await {
sent.payload
.insert_str(0, "Authentication-Results: example.org; dkim=fail\n");
alice.recv_msg(&sent).await;
} else if let Some(sent) = alice.pop_sent_msg_opt(Duration::ZERO).await {
bob2.recv_msg(&sent).await;
} else {
break;
}
}
// Unfortunately, securejoin currently doesn't work with authres-checking,
// so these checks would fail:
// let contact_bob = alice.add_or_lookup_contact(&bob2).await;
// assert_eq!(
// contact_bob.is_verified(&alice.ctx).await.unwrap(),
// VerifiedStatus::BidirectVerified
// );
// let contact_alice = bob2.add_or_lookup_contact(&alice).await;
// assert_eq!(
// contact_alice.is_verified(&bob2.ctx).await.unwrap(),
// VerifiedStatus::BidirectVerified
// );
// // Bob can read Alice's messages again
// let received = tcm
// .try_send_recv(&alice, &bob2, "Can you read this again?")
// .await;
// assert_eq!(received.text.as_ref().unwrap(), "Can you read this again?");
// assert!(received.error.is_none());
Ok(())
handle_authres(&t, &mail, "invalid@rom.com").await.unwrap();
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
@@ -796,10 +566,7 @@ Authentication-Results: dkim=";
let alice = tcm.alice().await;
let bob = tcm.bob().await;
// Assume Bob received an email from something@example.net with
// correct DKIM -> `set_dkim_works()` was called
set_dkim_works_timestamp(&bob, "example.org", time()).await?;
// And Bob knows his server's authserv-id
// Bob knows his server's authserv-id
bob.set_config(Config::AuthservIdCandidates, Some("example.net"))
.await?;
@@ -821,15 +588,13 @@ Authentication-Results: dkim=";
.insert_str(0, "Authentication-Results: example.net; dkim=fail\n");
let rcvd = bob.recv_msg(&sent).await;
// Disallowing keychanges is disabled for now:
// assert!(rcvd.error.unwrap().contains("DKIM failed"));
// The message info should contain a warning:
assert!(rcvd
.id
.get_info(&bob)
.await
.unwrap()
.contains("KEYCHANGES NOT ALLOWED"));
.contains("DKIM Results: Passed=false"));
Ok(())
}

View File

@@ -3,7 +3,7 @@
use core::cmp::max;
use std::ffi::OsStr;
use std::fmt;
use std::io::Cursor;
use std::io::{Cursor, Seek};
use std::iter::FusedIterator;
use std::mem;
use std::path::{Path, PathBuf};
@@ -12,6 +12,7 @@ use anyhow::{format_err, Context as _, Result};
use base64::Engine as _;
use futures::StreamExt;
use image::codecs::jpeg::JpegEncoder;
use image::ImageReader;
use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat, Pixel, Rgba};
use num_traits::FromPrimitive;
use tokio::io::AsyncWriteExt;
@@ -426,9 +427,25 @@ impl<'a> BlobObject<'a> {
let mut no_exif = false;
let no_exif_ref = &mut no_exif;
let res = tokio::task::block_in_place(move || {
let (nr_bytes, exif) = self.metadata()?;
let mut file = std::fs::File::open(self.to_abs_path())?;
let (nr_bytes, exif) = image_metadata(&file)?;
*no_exif_ref = exif.is_none();
let mut img = image::open(&blob_abs).context("image decode failure")?;
// It's strange that BufReader modifies a file position while it takes a non-mut
// reference. Ok, just rewind it.
file.rewind()?;
let imgreader = ImageReader::new(std::io::BufReader::new(&file)).with_guessed_format();
let imgreader = match imgreader {
Ok(ir) => ir,
_ => {
file.rewind()?;
ImageReader::with_format(
std::io::BufReader::new(&file),
ImageFormat::from_path(&blob_abs)?,
)
}
};
let fmt = imgreader.format().context("No format??")?;
let mut img = imgreader.decode().context("image decode failure")?;
let orientation = exif.as_ref().map(|exif| exif_orientation(exif, context));
let mut encoded = Vec::new();
let mut changed_name = None;
@@ -457,10 +474,9 @@ impl<'a> BlobObject<'a> {
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,
Ok(ImageFormat::Jpeg) => {
ImageFormat::Png if !exceeds_max_bytes => ImageOutputFormat::Png,
ImageFormat::Jpeg => {
add_white_bg = false;
ImageOutputFormat::Jpeg {
quality: jpeg_quality,
@@ -497,7 +513,7 @@ impl<'a> BlobObject<'a> {
img_wh = max(img.width(), img.height());
// PNGs and WebPs may be huge because of animation, which is lost by the `image`
// crate when recoding, so don't scale them down.
if matches!(fmt, Ok(ImageFormat::Jpeg)) || !encoded.is_empty() {
if matches!(fmt, ImageFormat::Jpeg) || !encoded.is_empty() {
img_wh = img_wh * 2 / 3;
}
}
@@ -538,7 +554,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))
if !matches!(fmt, ImageFormat::Jpeg)
&& matches!(ofmt, ImageOutputFormat::Jpeg { .. })
{
blob_abs = blob_abs.with_extension("jpg");
@@ -575,15 +591,14 @@ impl<'a> BlobObject<'a> {
}
}
}
}
/// Returns image file size and Exif.
pub fn metadata(&self) -> Result<(u64, Option<exif::Exif>)> {
let file = std::fs::File::open(self.to_abs_path())?;
let len = file.metadata()?.len();
let mut bufreader = std::io::BufReader::new(&file);
let exif = exif::Reader::new().read_from_container(&mut bufreader).ok();
Ok((len, exif))
}
/// Returns image file size and Exif.
pub fn image_metadata(file: &std::fs::File) -> Result<(u64, Option<exif::Exif>)> {
let len = file.metadata()?.len();
let mut bufreader = std::io::BufReader::new(file);
let exif = exif::Reader::new().read_from_container(&mut bufreader).ok();
Ok((len, exif))
}
fn exif_orientation(exif: &exif::Exif, context: &Context) -> i32 {
@@ -698,7 +713,10 @@ fn encode_img(
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)?;
// Convert image into RGB8 to avoid the error
// "The encoder or decoder for Jpeg does not support the color type Rgba8"
// (<https://github.com/image-rs/image/issues/2211>).
img.clone().into_rgb8().write_with_encoder(encoder)?;
}
}
Ok(())
@@ -1083,32 +1101,34 @@ mod tests {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_recode_image_1() {
let bytes = include_bytes!("../test-data/image/avatar1000x1000.jpg");
send_image_check_mediaquality(
Viewtype::Image,
Some("0"),
SendImageCheckMediaquality {
viewtype: Viewtype::Image,
media_quality_config: "0",
bytes,
"jpg",
true, // has Exif
1000,
1000,
0,
1000,
1000,
)
extension: "jpg",
has_exif: true,
original_width: 1000,
original_height: 1000,
compressed_width: 1000,
compressed_height: 1000,
..Default::default()
}
.test()
.await
.unwrap();
send_image_check_mediaquality(
Viewtype::Image,
Some("1"),
SendImageCheckMediaquality {
viewtype: Viewtype::Image,
media_quality_config: "1",
bytes,
"jpg",
true, // has Exif
1000,
1000,
0,
1000,
1000,
)
extension: "jpg",
has_exif: true,
original_width: 1000,
original_height: 1000,
compressed_width: 1000,
compressed_height: 1000,
..Default::default()
}
.test()
.await
.unwrap();
}
@@ -1117,18 +1137,20 @@ mod tests {
async fn test_recode_image_2() {
// The "-rotated" files are rotated by 270 degrees using the Exif metadata
let bytes = include_bytes!("../test-data/image/rectangle2000x1800-rotated.jpg");
let img_rotated = send_image_check_mediaquality(
Viewtype::Image,
Some("0"),
let img_rotated = SendImageCheckMediaquality {
viewtype: Viewtype::Image,
media_quality_config: "0",
bytes,
"jpg",
true, // has Exif
2000,
1800,
270,
1800,
2000,
)
extension: "jpg",
has_exif: true,
original_width: 2000,
original_height: 1800,
orientation: 270,
compressed_width: 1800,
compressed_height: 2000,
..Default::default()
}
.test()
.await
.unwrap();
assert_correct_rotation(&img_rotated);
@@ -1137,18 +1159,18 @@ mod tests {
img_rotated.write_to(&mut buf, ImageFormat::Jpeg).unwrap();
let bytes = buf.into_inner();
let img_rotated = send_image_check_mediaquality(
Viewtype::Image,
Some("1"),
&bytes,
"jpg",
false, // no Exif
1800,
2000,
0,
1800,
2000,
)
let img_rotated = SendImageCheckMediaquality {
viewtype: Viewtype::Image,
media_quality_config: "1",
bytes: &bytes,
extension: "jpg",
original_width: 1800,
original_height: 2000,
compressed_width: 1800,
compressed_height: 2000,
..Default::default()
}
.test()
.await
.unwrap();
assert_correct_rotation(&img_rotated);
@@ -1158,49 +1180,102 @@ mod tests {
async fn test_recode_image_balanced_png() {
let bytes = include_bytes!("../test-data/image/screenshot.png");
send_image_check_mediaquality(
Viewtype::Image,
Some("0"),
SendImageCheckMediaquality {
viewtype: Viewtype::Image,
media_quality_config: "0",
bytes,
"png",
false, // no Exif
1920,
1080,
0,
1920,
1080,
)
extension: "png",
original_width: 1920,
original_height: 1080,
compressed_width: 1920,
compressed_height: 1080,
..Default::default()
}
.test()
.await
.unwrap();
send_image_check_mediaquality(
Viewtype::Image,
Some("1"),
SendImageCheckMediaquality {
viewtype: Viewtype::Image,
media_quality_config: "1",
bytes,
"png",
false, // no Exif
1920,
1080,
0,
constants::WORSE_IMAGE_SIZE,
constants::WORSE_IMAGE_SIZE * 1080 / 1920,
)
extension: "png",
original_width: 1920,
original_height: 1080,
compressed_width: constants::WORSE_IMAGE_SIZE,
compressed_height: constants::WORSE_IMAGE_SIZE * 1080 / 1920,
..Default::default()
}
.test()
.await
.unwrap();
SendImageCheckMediaquality {
viewtype: Viewtype::File,
media_quality_config: "1",
bytes,
extension: "png",
original_width: 1920,
original_height: 1080,
compressed_width: 1920,
compressed_height: 1080,
..Default::default()
}
.test()
.await
.unwrap();
SendImageCheckMediaquality {
viewtype: Viewtype::File,
media_quality_config: "1",
bytes,
extension: "png",
original_width: 1920,
original_height: 1080,
compressed_width: 1920,
compressed_height: 1080,
set_draft: true,
..Default::default()
}
.test()
.await
.unwrap();
// This will be sent as Image, see [`BlobObject::maybe_sticker`] for explanation.
send_image_check_mediaquality(
Viewtype::Sticker,
Some("0"),
SendImageCheckMediaquality {
viewtype: Viewtype::Sticker,
media_quality_config: "0",
bytes,
"png",
false, // no Exif
1920,
1080,
0,
1920,
1080,
)
extension: "png",
original_width: 1920,
original_height: 1080,
compressed_width: 1920,
compressed_height: 1080,
..Default::default()
}
.test()
.await
.unwrap();
}
/// Tests that RGBA PNG can be recoded into JPEG
/// by dropping alpha channel.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_recode_image_rgba_png_to_jpeg() {
let bytes = include_bytes!("../test-data/image/screenshot-rgba.png");
SendImageCheckMediaquality {
viewtype: Viewtype::Image,
media_quality_config: "1",
bytes,
extension: "png",
original_width: 1920,
original_height: 1080,
compressed_width: constants::WORSE_IMAGE_SIZE,
compressed_height: constants::WORSE_IMAGE_SIZE * 1080 / 1920,
..Default::default()
}
.test()
.await
.unwrap();
}
@@ -1208,18 +1283,19 @@ mod tests {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_recode_image_huge_jpg() {
let bytes = include_bytes!("../test-data/image/screenshot.jpg");
send_image_check_mediaquality(
Viewtype::Image,
Some("0"),
SendImageCheckMediaquality {
viewtype: Viewtype::Image,
media_quality_config: "0",
bytes,
"jpg",
true, // has Exif
1920,
1080,
0,
constants::BALANCED_IMAGE_SIZE,
constants::BALANCED_IMAGE_SIZE * 1080 / 1920,
)
extension: "jpg",
has_exif: true,
original_width: 1920,
original_height: 1080,
compressed_width: constants::BALANCED_IMAGE_SIZE,
compressed_height: constants::BALANCED_IMAGE_SIZE * 1080 / 1920,
..Default::default()
}
.test()
.await
.unwrap();
}
@@ -1241,65 +1317,128 @@ mod tests {
assert_eq!(luma, 0);
}
#[allow(clippy::too_many_arguments)]
async fn send_image_check_mediaquality(
viewtype: Viewtype,
media_quality_config: Option<&str>,
bytes: &[u8],
extension: &str,
has_exif: bool,
original_width: u32,
original_height: u32,
orientation: i32,
compressed_width: u32,
compressed_height: u32,
) -> anyhow::Result<DynamicImage> {
#[derive(Default)]
struct SendImageCheckMediaquality<'a> {
pub(crate) viewtype: Viewtype,
pub(crate) media_quality_config: &'a str,
pub(crate) bytes: &'a [u8],
pub(crate) extension: &'a str,
pub(crate) has_exif: bool,
pub(crate) original_width: u32,
pub(crate) original_height: u32,
pub(crate) orientation: i32,
pub(crate) compressed_width: u32,
pub(crate) compressed_height: u32,
pub(crate) set_draft: bool,
}
impl SendImageCheckMediaquality<'_> {
pub(crate) async fn test(self) -> anyhow::Result<DynamicImage> {
let viewtype = self.viewtype;
let media_quality_config = self.media_quality_config;
let bytes = self.bytes;
let extension = self.extension;
let has_exif = self.has_exif;
let original_width = self.original_width;
let original_height = self.original_height;
let orientation = self.orientation;
let compressed_width = self.compressed_width;
let compressed_height = self.compressed_height;
let set_draft = self.set_draft;
let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await;
alice
.set_config(Config::MediaQuality, Some(media_quality_config))
.await?;
let file = alice.get_blobdir().join("file").with_extension(extension);
fs::write(&file, &bytes)
.await
.context("failed to write file")?;
check_image_size(&file, original_width, original_height);
let (_, exif) = image_metadata(&std::fs::File::open(&file)?)?;
if has_exif {
let exif = exif.unwrap();
assert_eq!(exif_orientation(&exif, &alice), orientation);
} else {
assert!(exif.is_none());
}
let mut msg = Message::new(viewtype);
msg.set_file(file.to_str().unwrap(), None);
let chat = alice.create_chat(&bob).await;
if set_draft {
chat.id.set_draft(&alice, Some(&mut msg)).await.unwrap();
msg = chat.id.get_draft(&alice).await.unwrap().unwrap();
assert_eq!(msg.get_viewtype(), Viewtype::File);
}
let sent = alice.send_msg(chat.id, &mut msg).await;
let alice_msg = alice.get_last_msg().await;
assert_eq!(alice_msg.get_width() as u32, compressed_width);
assert_eq!(alice_msg.get_height() as u32, compressed_height);
let file_saved = alice
.get_blobdir()
.join("saved-".to_string() + &alice_msg.get_filename().unwrap());
alice_msg.save_file(&alice, &file_saved).await?;
check_image_size(file_saved, compressed_width, compressed_height);
let bob_msg = bob.recv_msg(&sent).await;
assert_eq!(bob_msg.get_viewtype(), Viewtype::Image);
assert_eq!(bob_msg.get_width() as u32, compressed_width);
assert_eq!(bob_msg.get_height() as u32, compressed_height);
let file_saved = bob
.get_blobdir()
.join("saved-".to_string() + &bob_msg.get_filename().unwrap());
bob_msg.save_file(&bob, &file_saved).await?;
if viewtype == Viewtype::File {
assert_eq!(file_saved.extension().unwrap(), extension);
let bytes1 = fs::read(&file_saved).await?;
assert_eq!(&bytes1, bytes);
}
let (_, exif) = image_metadata(&std::fs::File::open(&file_saved)?)?;
assert!(exif.is_none());
let img = check_image_size(file_saved, compressed_width, compressed_height);
Ok(img)
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_send_big_gif_as_image() -> Result<()> {
let bytes = include_bytes!("../test-data/image/screenshot.gif");
let (width, height) = (1920u32, 1080u32);
let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await;
alice
.set_config(Config::MediaQuality, media_quality_config)
.set_config(
Config::MediaQuality,
Some(&(MediaQuality::Worse as i32).to_string()),
)
.await?;
let file = alice.get_blobdir().join("file").with_extension(extension);
let file = alice.get_blobdir().join("file").with_extension("gif");
fs::write(&file, &bytes)
.await
.context("failed to write file")?;
check_image_size(&file, original_width, original_height);
let blob = BlobObject::new_from_path(&alice, &file).await?;
let (_, exif) = blob.metadata()?;
if has_exif {
let exif = exif.unwrap();
assert_eq!(exif_orientation(&exif, &alice), orientation);
} else {
assert!(exif.is_none());
}
let mut msg = Message::new(viewtype);
let mut msg = Message::new(Viewtype::Image);
msg.set_file(file.to_str().unwrap(), None);
let chat = alice.create_chat(&bob).await;
let sent = alice.send_msg(chat.id, &mut msg).await;
let alice_msg = alice.get_last_msg().await;
assert_eq!(alice_msg.get_width() as u32, compressed_width);
assert_eq!(alice_msg.get_height() as u32, compressed_height);
check_image_size(
alice_msg.get_file(&alice).unwrap(),
compressed_width,
compressed_height,
);
let bob_msg = bob.recv_msg(&sent).await;
assert_eq!(bob_msg.get_viewtype(), Viewtype::Image);
assert_eq!(bob_msg.get_width() as u32, compressed_width);
assert_eq!(bob_msg.get_height() as u32, compressed_height);
let file = bob_msg.get_file(&bob).unwrap();
let blob = BlobObject::new_from_path(&bob, &file).await?;
let (_, exif) = blob.metadata()?;
assert!(exif.is_none());
let img = check_image_size(file, compressed_width, compressed_height);
Ok(img)
// DC must detect the image as GIF and send it w/o reencoding.
assert_eq!(bob_msg.get_viewtype(), Viewtype::Gif);
assert_eq!(bob_msg.get_width() as u32, width);
assert_eq!(bob_msg.get_height() as u32, height);
let file_saved = bob
.get_blobdir()
.join("saved-".to_string() + &bob_msg.get_filename().unwrap());
bob_msg.save_file(&bob, &file_saved).await?;
let (file_size, _) = image_metadata(&std::fs::File::open(&file_saved)?)?;
assert_eq!(file_size, bytes.len() as u64);
check_image_size(file_saved, width, height);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]

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