Compare commits

..

47 Commits

Author SHA1 Message Date
Sebastian Klähn
5a79815a13 method to get busy webxdcs 2022-09-11 20:41:45 +02:00
Sebastian Klähn
6877f16b63 introduce two new events 2022-09-11 19:42:45 +02:00
Sebastian Klähn
70979c55fa typo + only send for webxdcs 2022-09-11 09:39:56 +02:00
Sebastian Klähn
2e2fa95298 fix jsonrpc 2022-09-10 19:46:54 +02:00
Sebastian Klähn
f506b6882c fix id + typo 2022-09-10 19:38:28 +02:00
Sebastian Klähn
fb564aedb2 changelog entry + deltachat.h entry 2022-09-10 19:35:45 +02:00
Sebastian Klähn
3271d509cc delete comment 2022-09-10 19:29:12 +02:00
Sebastian Klähn
24d9345ea0 add delete event for webxdc 2022-09-10 19:27:20 +02:00
Sebastian Klähn
7cff681234 Only apply webxdc updates for members of group (#3568)
Don't accept wexdc updates from members not in group
2022-09-09 18:42:35 +00:00
Simon Laux
54b10106bd jsonrpc: add deleteMessages() and getMessageInfo() (#3587)
* jsonrpc: add `deleteMessages()` and `getMessageInfo()`

* add pr numbers to changelog
2022-09-07 22:22:37 +02:00
Simon Laux
484aa54ed6 jsonrpc: add webxdc_info property to Message (#3588)
* jsonrpc: add `webxdc_info` property to `Message`

* add pr number to changelog
2022-09-07 22:14:13 +02:00
Simon Laux
417bddfa16 add more functions to jsonrpc (#3586)
* jsonrpc: add functions:
  - `deleteChat()`
  - `getChatEncryptionInfo()`
  - `getChatSecurejoinQrCodeSvg()`
  - `leaveGroup()`
  - `removeContactFromChat()`
  - `addContactToChat()`
 also fix doc a little

* Update deltachat-jsonrpc/src/api/mod.rs

Co-authored-by: bjoern <r10s@b44t.com>

Co-authored-by: bjoern <r10s@b44t.com>
2022-09-07 17:45:15 +02:00
Simon Laux
1e4e799f2e add pr id to changelog entry 2022-09-07 11:02:38 +02:00
Simon Laux
33b18e3014 jsonrpc: add was_seen_recently property
to `ChatListItemFetchResult`, `FullChat` and `Contact`
2022-09-07 11:02:38 +02:00
Simon Laux
96ce8eb851 jsonrpc: add is_broadcast property
to `ChatListItemFetchResult`
2022-09-07 11:02:38 +02:00
Simon Laux
8de5e964e0 add pr id to changelog entry 2022-09-07 10:40:18 +02:00
Simon Laux
43150195d4 fix set_core_version.py script to also update version in deltachat-jsonrpc/typescript/package.json 2022-09-07 10:40:18 +02:00
Sebastian Klähn
065c7af9a0 Fix typos & add documentation (#3569)
* fix typo

* tips & typos

* improve doc string

* update documentation

* fmt

* fix typo
2022-09-06 11:15:49 +02:00
bjoern
949370ad63 restricted webxdc internet access (#3516)
* add request_internet_access manifest option

* test request_internet_access

* update CHANGELOG

* force warning when internet access is enabled

if internet access is enabled,
show a warning instead of the normal summary
(the internet access is currently mainly to test out integrations
as maps for video chat; the summary is dispensable in the cases currently)

* adapt json-rpc's WebxdcMessageInfo
2022-09-05 11:45:34 +02:00
link2xt
4b91a88bc9 Do not prepend subject to mailing list chat messages (#3253) 2022-09-05 08:12:33 +00:00
dependabot[bot]
d04a0c8f2f cargo: bump libc from 0.2.129 to 0.2.132 (#3580)
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.129 to 0.2.132.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.129...0.2.132)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 12:43:50 +02:00
dependabot[bot]
6f6618d46f cargo: bump futures from 0.3.21 to 0.3.24 (#3575)
Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.21 to 0.3.24.
- [Release notes](https://github.com/rust-lang/futures-rs/releases)
- [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.21...0.3.24)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 12:43:02 +02:00
dependabot[bot]
90eb39439a cargo: bump chrono from 0.4.21 to 0.4.22 (#3576)
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.21 to 0.4.22.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/v0.4.22/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.21...v0.4.22)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 12:01:12 +02:00
dependabot[bot]
9813265ef4 cargo: bump sha2 from 0.10.2 to 0.10.3 (#3577)
Bumps [sha2](https://github.com/RustCrypto/hashes) from 0.10.2 to 0.10.3.
- [Release notes](https://github.com/RustCrypto/hashes/releases)
- [Commits](https://github.com/RustCrypto/hashes/compare/sha2-v0.10.2...sha2-v0.10.3)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 11:59:46 +02:00
dependabot[bot]
8acbf9babf cargo: bump once_cell from 1.13.0 to 1.13.1 (#3579)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.13.0 to 1.13.1.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.13.0...v1.13.1)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 11:58:47 +02:00
dependabot[bot]
97d7c20549 cargo: bump fast-socks5 from 0.8.0 to 0.8.1 (#3582)
Bumps [fast-socks5](https://github.com/dizda/fast-socks5) from 0.8.0 to 0.8.1.
- [Release notes](https://github.com/dizda/fast-socks5/releases)
- [Commits](https://github.com/dizda/fast-socks5/compare/v0.8.0...v0.8.1)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 11:56:36 +02:00
dependabot[bot]
4eddd7f616 cargo: bump thiserror from 1.0.32 to 1.0.33 (#3572)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.32 to 1.0.33.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.32...1.0.33)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 11:16:09 +02:00
dependabot[bot]
217217bd2b cargo: bump serde_json from 1.0.83 to 1.0.85 (#3573)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.83 to 1.0.85.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.83...v1.0.85)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 11:15:42 +02:00
dependabot[bot]
37eeb55a5b cargo: bump serde from 1.0.143 to 1.0.144 (#3581)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.143 to 1.0.144.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.143...v1.0.144)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 11:14:22 +02:00
dependabot[bot]
d32da8e4d5 cargo: bump anyhow from 1.0.61 to 1.0.63 (#3578)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.61 to 1.0.63.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.61...1.0.63)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 11:13:59 +02:00
bjoern
da3052fdc2 document new bot behavior wrt accepting chats (#3571) 2022-09-01 18:59:32 +02:00
Sebastian Klähn
97982ef93a Auto-accept contact requests for bots (#3567)
Auto accept contact request for bots
Co-authored-by: Hocuri <hocuri@gmx.de>
2022-09-01 14:58:47 +02:00
bjoern
25bff21edd fix doc, dc_accounts_get_next_event() is gone (#3565) 2022-08-29 16:26:38 +02:00
bjoern
b2b22c8b85 order contact lists by "last seen" instead of name/address (#3562)
order all contact lists by last_seen instead of name or address
2022-08-29 13:55:00 +02:00
link2xt
5663b1c539 Add defines for backward compatibility 2022-08-28 21:19:53 +00:00
link2xt
c68a4543db Remove dc_accounts_event_emitter_t
Use dc_event_emitter_t instead.
2022-08-28 20:47:41 +00:00
link2xt
88dc8a389a Update Cargo.lock 2022-08-28 20:47:27 +00:00
link2xt
e7b6c689ce Keep deltachat-jsonrpc version in sync 2022-08-28 17:30:20 +00:00
Hocuri
6ec9b0a0b0 Show attached .eml files as such (#3561)
Co-authored-by: bjoern <r10s@b44t.com>
2022-08-27 14:36:45 +00:00
bjoern
682ec563c8 add was_seen_recently() (#3560)
* add dc_contact_was_seen_recently()

* add wasSeenRecently() to node
2022-08-26 19:21:44 +02:00
B. Petersen
8eb4d9bf08 fix some typos 2022-08-26 09:28:58 +02:00
link2xt
977a8cf33f python: remove unused setup.cfg 2022-08-22 00:11:01 +00:00
link2xt
db496b82fb Add --features jsonrpc to scripts/run_all.sh
Python wheels cannot be built without it
as they expect everything defined in deltachat.h
to be in the C library.
2022-08-22 00:10:30 +00:00
link2xt
9d578884f9 Test that no notifications are displayed for blocked mailing lists 2022-08-20 14:12:30 +00:00
link2xt
84ae27744a test_utils: add EventTracker.conusme_events() 2022-08-20 14:12:22 +00:00
link2xt
54df44d930 test_utils: implement Deref and DerefMut for EventTracker 2022-08-20 14:12:05 +00:00
link2xt
f79b16c244 Do not emit notifications for blocked chats 2022-08-20 13:56:09 +00:00
39 changed files with 1087 additions and 229 deletions

View File

@@ -3,10 +3,42 @@
## Unreleased
### API-Changes
- breaking change: replace `dc_accounts_event_emitter_t` with `dc_event_emitter_t` #3422
Type `dc_accounts_event_emitter_t` is removed.
`dc_accounts_get_event_emitter()` returns `dc_event_emitter_t` now, so
`dc_get_next_event()` should be used instead of `dc_accounts_get_next_event`
and `dc_event_emitter_unref()` should be used instead of
`dc_accounts_event_emitter_unref`.
- add `dc_contact_was_seen_recently()` #3560
- jsonrpc: add functions: #3586, #3587
- `deleteChat()`
- `getChatEncryptionInfo()`
- `getChatSecurejoinQrCodeSvg()`
- `leaveGroup()`
- `removeContactFromChat()`
- `addContactToChat()`
- `deleteMessages()`
- `getMessageInfo()`
- jsonrpc: add `is_broadcast` property to `ChatListItemFetchResult` #3584
- jsonrpc: add `was_seen_recently` property to `ChatListItemFetchResult`, `FullChat` and `Contact` #3584
- jsonrpc: add `webxdc_info` property to `Message` #3588
### Changes
- order contact lists by "last seen";
this affects `dc_get_chat_contacts()`, `dc_get_contacts()` and `dc_get_blocked_contacts()` #3562
- add `internet_access` flag to `dc_msg_get_webxdc_info()` #3516
- `DC_EVENT_WEBXDC_INSTANCE_DELETED` is emitted when a message containing a webxdc gets deleted #3105
- `DC_EVENT_WEBXDC_BUSY_UPDATING` is emitted when a new update has to be sent by an webxdc #3320
- `DC_EVENT_WEBXDC_UP_TO_DATE` is emitted when a webxdc has sent all updates #3320
### Fixes
- do not emit notifications for blocked chats #3557
- Show attached .eml files correctly #3561
- Auto accept contact requests if `Config::Bot` is set for a client #3567
- Don't prepend the subject to chat messages in mailinglists
- fix `set_core_version.py` script to also update version in `deltachat-jsonrpc/typescript/package.json` #3585
- Reject webxcd-updates from contacts who are not group members #3568
## 1.93.0

90
Cargo.lock generated
View File

@@ -87,9 +87,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.61"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "508b352bb5c066aac251f6daf6b36eccd03e8a88e8081cd44959ea277a3af9a8"
checksum = "a26fa4d7e3f2eebadf743988fc8aec9fa9a9e82611acafd77c1462ed6262440a"
[[package]]
name = "ascii_utils"
@@ -497,9 +497,9 @@ dependencies = [
[[package]]
name = "chrono"
version = "0.4.21"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f725f340c3854e3cb3ab736dc21f0cca183303acea3b3ffec30f141503ac8eb"
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
dependencies = [
"iana-time-zone",
"js-sys",
@@ -946,7 +946,7 @@ dependencies = [
"serde",
"serde_json",
"sha-1 0.10.0",
"sha2 0.10.2",
"sha2 0.10.3",
"smallvec",
"strum",
"strum_macros",
@@ -965,7 +965,7 @@ dependencies = [
[[package]]
name = "deltachat-jsonrpc"
version = "1.86.0"
version = "1.93.0"
dependencies = [
"anyhow",
"async-channel",
@@ -1356,9 +1356,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fast-socks5"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25bef00d863e757b64a737857143b5425061271a4149ff5d81201fb4c9bfc01c"
checksum = "d2687b5a6108f18ba8621e0e618a3be1dcc2768632dad24b7cea1f87975375a9"
dependencies = [
"anyhow",
"log",
@@ -1451,9 +1451,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.21"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c"
dependencies = [
"futures-channel",
"futures-core",
@@ -1466,9 +1466,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.21"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050"
dependencies = [
"futures-core",
"futures-sink",
@@ -1476,15 +1476,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.21"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
[[package]]
name = "futures-executor"
version = "0.3.21"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab"
dependencies = [
"futures-core",
"futures-task",
@@ -1493,9 +1493,9 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.21"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68"
[[package]]
name = "futures-lite"
@@ -1514,9 +1514,9 @@ dependencies = [
[[package]]
name = "futures-macro"
version = "0.3.21"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17"
dependencies = [
"proc-macro2",
"quote",
@@ -1525,21 +1525,21 @@ dependencies = [
[[package]]
name = "futures-sink"
version = "0.3.21"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56"
[[package]]
name = "futures-task"
version = "0.3.21"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1"
[[package]]
name = "futures-util"
version = "0.3.21"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
dependencies = [
"futures-channel",
"futures-core",
@@ -1797,12 +1797,12 @@ dependencies = [
[[package]]
name = "iana-time-zone"
version = "0.1.42"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9512e544c25736b82aebbd2bf739a47c8a1c935dfcc3a6adcde10e35cd3cd468"
checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501"
dependencies = [
"android_system_properties",
"core-foundation",
"core-foundation-sys",
"js-sys",
"wasm-bindgen",
"winapi",
@@ -1979,9 +1979,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.129"
version = "0.2.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64de3cc433455c14174d42e554d4027ee631c4d046d43e3ecc6efc4636cdc7a7"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
[[package]]
name = "libm"
@@ -2285,9 +2285,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.13.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
[[package]]
name = "oorandom"
@@ -3105,9 +3105,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.143"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
dependencies = [
"serde_derive",
]
@@ -3124,9 +3124,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.143"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
dependencies = [
"proc-macro2",
"quote",
@@ -3135,9 +3135,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.83"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
dependencies = [
"itoa 1.0.3",
"ryu",
@@ -3195,9 +3195,9 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
checksum = "899bf02746a2c92bf1053d9327dadb252b01af1f81f90cdb902411f518bc7215"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -3419,18 +3419,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.32"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.32"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -46,7 +46,7 @@ native-tls = "0.2"
num_cpus = "1.13"
num-derive = "0.3"
num-traits = "0.2"
once_cell = "1.13.0"
once_cell = "1.13.1"
percent-encoding = "2.0"
pgp = { version = "0.8", default-features = false }
pretty_env_logger = { version = "0.4", optional = true }

View File

@@ -25,7 +25,7 @@ tokio = { version = "1", features = ["rt-multi-thread"] }
anyhow = "1"
thiserror = "1"
rand = "0.7"
once_cell = "1.13.0"
once_cell = "1.13.1"
[features]
default = ["vendored"]

View File

@@ -22,9 +22,11 @@ typedef struct _dc_lot dc_lot_t;
typedef struct _dc_provider dc_provider_t;
typedef struct _dc_event dc_event_t;
typedef struct _dc_event_emitter dc_event_emitter_t;
typedef struct _dc_accounts_event_emitter dc_accounts_event_emitter_t;
typedef struct _dc_jsonrpc_instance dc_jsonrpc_instance_t;
// Alias for backwards compatibility, use dc_event_emitter_t instead.
typedef struct _dc_event_emitter dc_accounts_event_emitter_t;
/**
* @mainpage Getting started
*
@@ -391,7 +393,8 @@ char* dc_get_blobdir (const dc_context_t* context);
* If no type is prefixed, the videochat is handled completely in a browser.
* - `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.
* adds Auto-Submitted header to outgoing messages
* and accepts contact requests automatically (calling dc_accept_chat() is not needed for bots).
* - `fetch_existing_msgs` = 1=fetch most recent existing messages on configure (default),
* 0=do not fetch existing messages on configure.
* In both cases, existing recipients are added to the contact database.
@@ -1385,6 +1388,9 @@ void dc_block_chat (dc_context_t* context, uint32_t ch
*
* Use it to accept "contact request" chats as indicated by dc_chat_is_contact_request().
*
* If the dc_set_config()-option `bot` is set,
* all chats are accepted automatically and calling this function has no effect.
*
* @memberof dc_context_t
* @param context The context object as returned from dc_context_new().
* @param chat_id The ID of the chat to accept.
@@ -2510,9 +2516,9 @@ int dc_set_location (dc_context_t* context, double latit
* Must be given in number of seconds since 00:00 hours, Jan 1, 1970 UTC.
* 0 for "all up to now".
* @return An array of locations, NULL is never returned.
* The array is sorted decending;
* The array is sorted descending;
* the first entry in the array is the location with the newest timestamp.
* Note that this is only realated to the recent postion of the user
* Note that this is only related to the recent position of the user
* if dc_array_is_independent() returns 0.
* The returned array must be freed using dc_array_unref().
*
@@ -2836,7 +2842,7 @@ void dc_accounts_maybe_network_lost (dc_accounts_t* accounts);
*
* The library will emit various @ref DC_EVENT events as "new message", "message read" etc.
* To get these events, you have to create an event emitter using this function
* and call dc_accounts_get_next_event() on the emitter.
* and call dc_get_next_event() on the emitter.
*
* This is similar to dc_get_event_emitter(), which, however,
* must not be called for accounts handled by the account manager.
@@ -2844,13 +2850,13 @@ void dc_accounts_maybe_network_lost (dc_accounts_t* accounts);
* @memberof dc_accounts_t
* @param accounts The account manager as created by dc_accounts_new().
* @return Returns the event emitter, NULL on errors.
* Must be freed using dc_accounts_event_emitter_unref() after usage.
* Must be freed using dc_event_emitter_unref() after usage.
*
* Note: Use only one event emitter per account manager.
* Having more than one event emitter running at the same time on the same account manager
* will result in events randomly delivered to the one or to the other.
*/
dc_accounts_event_emitter_t* dc_accounts_get_event_emitter (dc_accounts_t* accounts);
dc_event_emitter_t* dc_accounts_get_event_emitter (dc_accounts_t* accounts);
/**
@@ -3757,6 +3763,11 @@ char* dc_msg_get_webxdc_blob (const dc_msg_t* msg, const char*
* URL where the source code of the Webxdc and other information can be found;
* defaults to an empty string.
* Implementations may offer an menu or a button to open this URL.
* - internet_access:
* 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.
@@ -4638,6 +4649,22 @@ char* dc_contact_get_status (const dc_contact_t* contact);
*/
int64_t dc_contact_get_last_seen (const dc_contact_t* contact);
/**
* Check if the contact was seen recently.
*
* The UI may highlight these contacts,
* eg. draw a little green dot on the avatars of the users recently seen.
* DC_CONTACT_ID_SELF and other special contact IDs are defined as never seen recently (they should not get a dot).
* To get the time a contact was seen, use dc_contact_get_last_seen().
*
* @memberof dc_contact_t
* @param contact The contact object.
* @return 1=contact seen recently, 0=contact not seen recently.
*/
int dc_contact_was_seen_recently (const dc_contact_t* contact);
/**
* Check if a contact is blocked.
*
@@ -5245,9 +5272,8 @@ char* dc_jsonrpc_next_response(dc_jsonrpc_instance_t* jsonrpc_instance);
* @class dc_event_emitter_t
*
* Opaque object that is used to get events from a single context.
* You can get an event emitter from a context using dc_get_event_emitter().
* If you are using the dc_accounts_t account manager,
* dc_accounts_event_emitter_t must be used instead.
* You can get an event emitter from a context using dc_get_event_emitter()
* or dc_accounts_get_event_emitter().
*/
/**
@@ -5263,6 +5289,8 @@ char* dc_jsonrpc_next_response(dc_jsonrpc_instance_t* jsonrpc_instance);
*/
dc_event_t* dc_get_next_event(dc_event_emitter_t* emitter);
// Alias for backwards compatibility, use dc_get_next_event instead.
#define dc_accounts_get_next_event dc_get_next_event
/**
* Free a context event emitter object.
@@ -5273,39 +5301,8 @@ dc_event_t* dc_get_next_event(dc_event_emitter_t* emitter);
*/
void dc_event_emitter_unref(dc_event_emitter_t* emitter);
/**
* @class dc_accounts_event_emitter_t
*
* Opaque object that is used to get events from the dc_accounts_t account manager.
* You get an event emitter from the account manager using dc_accounts_get_event_emitter().
* If you are not using the dc_accounts_t account manager but just a single dc_context_t object,
* dc_event_emitter_t must be used instead.
*/
/**
* Get the next event from an accounts event emitter object.
*
* @memberof dc_accounts_event_emitter_t
* @param emitter Event emitter object as returned from dc_accounts_get_event_emitter().
* @return An event as an dc_event_t object.
* You can query the event for information using dc_event_get_id(), dc_event_get_data1_int() and so on;
* if you are done with the event, you have to free the event using dc_event_unref().
* If NULL is returned, the contexts belonging to the event emitter are unref'd and no more events will come;
* in this case, free the event emitter using dc_accounts_event_emitter_unref().
*/
dc_event_t* dc_accounts_get_next_event (dc_accounts_event_emitter_t* emitter);
/**
* Free an accounts event emitter object.
*
* @memberof dc_accounts_event_emitter_t
* @param emitter Event emitter object as returned from dc_accounts_get_event_emitter().
* If NULL is given, nothing is done and an error is logged.
*/
void dc_accounts_event_emitter_unref(dc_accounts_event_emitter_t* emitter);
// Alias for backwards compatibility, use dc_event_emtitter_unref instead.
#define dc_accounts_event_emitter_unref dc_event_emitter_unref
/**
* @class dc_event_t
@@ -5377,7 +5374,7 @@ char* dc_event_get_data2_str(dc_event_t* event);
* To get the context object belonging to the event, use dc_accounts_get_account().
*
* @memberof dc_event_t
* @param event The event object as returned from dc_accounts_get_next_event().
* @param event The event object as returned from dc_get_next_event().
* @return The account ID belonging to the event, 0 for account manager errors.
*/
uint32_t dc_event_get_account_id(dc_event_t* event);
@@ -5727,8 +5724,30 @@ void dc_event_unref(dc_event_t* event);
* @param data1 (int) msg_id
* @param data2 (int) status_update_serial - must not be used by UI implementations.
*/
#define DC_EVENT_WEBXDC_STATUS_UPDATE 2120
#define DC_EVENT_WEBXDC_STATUS_UPDATE 2120
/**
* Message deleted which contained a webxdc instance.
*
* @param data1 (int) msg_id
*/
#define DC_EVENT_WEBXDC_INSTANCE_DELETED 2121
/**
* Webxdc has some updates that need to be sent
*
* @param data1 (int) msg_id
*/
#define DC_EVENT_WEBXDC_BUSY_UPDATING 2122
/**
* Webxdc has finished sending updates
*
* @param data1 (int) msg_id
*/
#define DC_EVENT_WEBXDC_UP_TO_DATE 2123
/**
* @}

View File

@@ -504,6 +504,9 @@ pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int
EventType::ConnectivityChanged => 2100,
EventType::SelfavatarChanged => 2110,
EventType::WebxdcStatusUpdate { .. } => 2120,
EventType::WebxdcInstanceDeleted { .. } => 2121,
EventType::WebxdcBusyUpdating { .. } => 2022,
EventType::WebxdcUpToDate { .. } => 2023,
}
}
@@ -550,6 +553,9 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
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::WebxdcBusyUpdating { msg_id } => msg_id.to_u32() as libc::c_int,
EventType::WebxdcUpToDate { msg_id } => msg_id.to_u32() as libc::c_int,
}
}
@@ -581,6 +587,9 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
| EventType::ImexFileWritten(_)
| EventType::MsgsNoticed(_)
| EventType::ConnectivityChanged
| EventType::WebxdcInstanceDeleted { .. }
| EventType::WebxdcBusyUpdating { .. }
| EventType::WebxdcUpToDate { .. }
| EventType::SelfavatarChanged => 0,
EventType::ChatModified(_) => 0,
EventType::MsgsChanged { msg_id, .. }
@@ -637,6 +646,9 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
| EventType::ConnectivityChanged
| EventType::SelfavatarChanged
| EventType::WebxdcStatusUpdate { .. }
| EventType::WebxdcInstanceDeleted { .. }
| EventType::WebxdcBusyUpdating { .. }
| EventType::WebxdcUpToDate { .. }
| EventType::ChatEphemeralTimerModified { .. } => ptr::null_mut(),
EventType::ConfigureProgress { comment, .. } => {
if let Some(comment) = comment {
@@ -3859,6 +3871,16 @@ pub unsafe extern "C" fn dc_contact_get_last_seen(contact: *mut dc_contact_t) ->
ffi_contact.contact.last_seen()
}
#[no_mangle]
pub unsafe extern "C" fn dc_contact_was_seen_recently(contact: *mut dc_contact_t) -> libc::c_int {
if contact.is_null() {
eprintln!("ignoring careless call to dc_contact_was_seen_recently()");
return 0;
}
let ffi_contact = &*contact;
ffi_contact.contact.was_seen_recently() as libc::c_int
}
#[no_mangle]
pub unsafe extern "C" fn dc_contact_is_blocked(contact: *mut dc_contact_t) -> libc::c_int {
if contact.is_null() {
@@ -4406,12 +4428,10 @@ pub unsafe extern "C" fn dc_accounts_maybe_network_lost(accounts: *mut dc_accoun
block_on(async move { accounts.write().await.maybe_network_lost().await });
}
pub type dc_accounts_event_emitter_t = EventEmitter;
#[no_mangle]
pub unsafe extern "C" fn dc_accounts_get_event_emitter(
accounts: *mut dc_accounts_t,
) -> *mut dc_accounts_event_emitter_t {
) -> *mut dc_event_emitter_t {
if accounts.is_null() {
eprintln!("ignoring careless call to dc_accounts_get_event_emitter()");
return ptr::null_mut();
@@ -4423,31 +4443,6 @@ pub unsafe extern "C" fn dc_accounts_get_event_emitter(
Box::into_raw(Box::new(emitter))
}
#[no_mangle]
pub unsafe extern "C" fn dc_accounts_event_emitter_unref(
emitter: *mut dc_accounts_event_emitter_t,
) {
if emitter.is_null() {
eprintln!("ignoring careless call to dc_accounts_event_emitter_unref()");
return;
}
let _ = Box::from_raw(emitter);
}
#[no_mangle]
pub unsafe extern "C" fn dc_accounts_get_next_event(
emitter: *mut dc_accounts_event_emitter_t,
) -> *mut dc_event_t {
if emitter.is_null() {
eprintln!("ignoring careless call to dc_accounts_get_next_event()");
return ptr::null_mut();
}
let emitter = &mut *emitter;
block_on(emitter.recv())
.map(|ev| Box::into_raw(Box::new(ev)))
.unwrap_or_else(ptr::null_mut)
}
#[cfg(feature = "jsonrpc")]
mod jsonrpc {
use super::*;

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat-jsonrpc"
version = "1.86.0"
version = "1.93.0"
description = "DeltaChat JSON-RPC API"
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
edition = "2021"
@@ -20,8 +20,8 @@ serde = { version = "1.0", features = ["derive"] }
tempfile = "3.3.0"
log = "0.4"
async-channel = { version = "1.6.1" }
futures = { version = "0.3.19" }
serde_json = "1.0.75"
futures = { version = "0.3.24" }
serde_json = "1.0.85"
yerpc = { version = "^0.3.1", features = ["anyhow_expose"] }
typescript-type-def = { version = "0.5.3", features = ["json_value"] }
tokio = { version = "1.19.2" }

View File

@@ -60,6 +60,9 @@ pub fn event_to_json_rpc_notification(event: Event) -> Value {
msg_id,
status_update_serial,
} => (json!(msg_id), json!(status_update_serial)),
EventType::WebxdcInstanceDeleted { msg_id } => (json!(msg_id), Value::Null),
EventType::WebxdcBusyUpdating { msg_id } => (json!(msg_id), Value::Null),
EventType::WebxdcUpToDate { msg_id } => (json!(msg_id), Value::Null),
};
let id: EventTypeName = event.typ.into();
@@ -102,6 +105,9 @@ pub enum EventTypeName {
ConnectivityChanged,
SelfavatarChanged,
WebxdcStatusUpdate,
WebxdcInstanceDeleted,
WebxdcBusyUpdating,
WebxdcUpToDate,
}
impl From<EventType> for EventTypeName {
@@ -137,6 +143,9 @@ impl From<EventType> for EventTypeName {
EventType::ConnectivityChanged => ConnectivityChanged,
EventType::SelfavatarChanged => SelfavatarChanged,
EventType::WebxdcStatusUpdate { .. } => WebxdcStatusUpdate,
EventType::WebxdcInstanceDeleted { .. } => WebxdcInstanceDeleted,
EventType::WebxdcBusyUpdating { .. } => WebxdcBusyUpdating,
EventType::WebxdcUpToDate { .. } => WebxdcUpToDate,
}
}
}

View File

@@ -1,13 +1,15 @@
use anyhow::{anyhow, bail, Context, Result};
use deltachat::{
chat::{get_chat_media, get_chat_msgs, ChatId},
chat::{add_contact_to_chat, get_chat_media, get_chat_msgs, remove_contact_from_chat, ChatId},
chatlist::Chatlist,
config::Config,
contact::{may_be_valid_addr, Contact, ContactId},
context::get_info,
message::{Message, MsgId, Viewtype},
message::{delete_msgs, get_msg_info, Message, MsgId, Viewtype},
provider::get_provider_info,
qr,
qr_code_generator::get_securejoin_qr_svg,
securejoin,
webxdc::StatusUpdateSerial,
};
use std::collections::BTreeMap;
@@ -192,12 +194,10 @@ impl CommandApi {
}
/// Set configuration values from a QR code. (technically from the URI that is stored in the qrcode)
/// Before this function is called, dc_check_qr() should confirm the type of the
/// QR code is DC_QR_ACCOUNT or DC_QR_WEBRTC_INSTANCE.
/// Before this function is called, `checkQr()` should confirm the type of the
/// QR code is `account` or `webrtcInstance`.
///
/// Internally, the function will call dc_set_config() with the appropriate keys,
/// e.g. `addr` and `mail_pw` for DC_QR_ACCOUNT
/// or `webrtc_instance` for DC_QR_WEBRTC_INSTANCE.
async fn set_config_from_qr(&self, account_id: u32, qr_content: String) -> Result<()> {
let ctx = self.get_context(account_id).await?;
qr::set_config_from_qr(&ctx, &qr_content).await
@@ -378,6 +378,108 @@ impl CommandApi {
ChatId::new(chat_id).block(&ctx).await
}
/// Delete a chat.
///
/// Messages are deleted from the device and the chat database entry is deleted.
/// After that, the event #DC_EVENT_MSGS_CHANGED is posted.
///
/// Things that are _not done_ implicitly:
///
/// - Messages are **not deleted from the server**.
/// - The chat or the contact is **not blocked**, so new messages from the user/the group may appear as a contact request
/// and the user may create the chat again.
/// - **Groups are not left** - this would
/// be unexpected as (1) deleting a normal chat also does not prevent new mails
/// from arriving, (2) leaving a group requires sending a message to
/// all group members - especially for groups not used for a longer time, this is
/// really unexpected when deletion results in contacting all members again,
/// (3) only leaving groups is also a valid usecase.
///
/// To leave a chat explicitly, use dc_remove_contact_from_chat() with
/// chat_id=DC_CONTACT_ID_SELF)
// TODO fix doc comment after adding dc_remove_contact_from_chat
async fn delete_chat(&self, account_id: u32, chat_id: u32) -> Result<()> {
let ctx = self.get_context(account_id).await?;
ChatId::new(chat_id).delete(&ctx).await
}
/// Get encryption info for a chat.
/// Get a multi-line encryption info, containing encryption preferences of all members.
/// Can be used to find out why messages sent to group are not encrypted.
///
/// returns Multi-line text
async fn get_chat_encryption_info(&self, account_id: u32, chat_id: u32) -> Result<String> {
let ctx = self.get_context(account_id).await?;
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.
/// The QR code is compatible to the OPENPGP4FPR format
/// so that a basic fingerprint comparison also works e.g. with OpenKeychain.
///
/// The scanning device will pass the scanned content to `checkQr()` then;
/// if `checkQr()` returns `askVerifyContact` or `askVerifyGroup`
/// an out-of-band-verification can be joined using dc_join_securejoin()
///
/// chat_id: If set to a group-chat-id,
/// the Verified-Group-Invite protocol is offered in the QR code;
/// works for protected groups as well as for normal groups.
/// If not set, the Setup-Contact protocol is offered in the QR code.
/// See https://countermitm.readthedocs.io/en/latest/new.html
/// for details about both protocols.
// TODO fix doc comment after adding dc_join_securejoin
async fn get_chat_securejoin_qr_code_svg(
&self,
account_id: u32,
chat_id: Option<u32>,
) -> 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?,
))
}
async fn leave_group(&self, account_id: u32, chat_id: u32) -> Result<()> {
let ctx = self.get_context(account_id).await?;
remove_contact_from_chat(&ctx, ChatId::new(chat_id), ContactId::SELF).await
}
/// Remove a member from a group.
///
/// If the group is already _promoted_ (any message was sent to the group),
/// all group members are informed by a special status message that is sent automatically by this function.
///
/// Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent.
async fn remove_contact_from_chat(
&self,
account_id: u32,
chat_id: u32,
contact_id: u32,
) -> Result<()> {
let ctx = self.get_context(account_id).await?;
remove_contact_from_chat(&ctx, ChatId::new(chat_id), ContactId::new(contact_id)).await
}
/// Add a member to a group.
///
/// If the group is already _promoted_ (any message was sent to the group),
/// all group members are informed by a special status message that is sent automatically by this function.
///
/// If the group has group protection enabled, only verified contacts can be added to the group.
///
/// Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent.
async fn add_contact_to_chat(
&self,
account_id: u32,
chat_id: u32,
contact_id: u32,
) -> Result<()> {
let ctx = self.get_context(account_id).await?;
add_contact_to_chat(&ctx, ChatId::new(chat_id), ContactId::new(contact_id)).await
}
// for now only text messages, because we only used text messages in desktop thusfar
async fn add_device_message(
&self,
@@ -435,6 +537,24 @@ impl CommandApi {
Ok(messages)
}
/// Delete messages. The messages are deleted on the current device and
/// on the IMAP server.
async fn delete_messages(&self, account_id: u32, message_ids: Vec<u32>) -> Result<()> {
let ctx = self.get_context(account_id).await?;
let msgs: Vec<MsgId> = message_ids.into_iter().map(MsgId::new).collect();
delete_msgs(&ctx, &msgs).await
}
/// Get an informational text for a single message. The text is multiline and may
/// contain e.g. the raw text of the message.
///
/// The max. text returned is typically longer (about 100000 characters) than the
/// max. text returned by dc_msg_get_text() (about 30000 characters).
async fn get_message_info(&self, account_id: u32, message_id: u32) -> Result<String> {
let ctx = self.get_context(account_id).await?;
get_msg_info(&ctx, MsgId::new(message_id)).await
}
// ---------------------------------------------
// contact
// ---------------------------------------------

View File

@@ -1,6 +1,7 @@
use anyhow::{anyhow, Result};
use deltachat::chat::get_chat_contacts;
use deltachat::chat::{Chat, ChatId};
use deltachat::constants::Chattype;
use deltachat::contact::{Contact, ContactId};
use deltachat::context::Context;
use num_traits::cast::ToPrimitive;
@@ -33,6 +34,7 @@ pub struct FullChat {
is_muted: bool,
ephemeral_timer: u32, //TODO look if there are more important properties in newer core versions
can_send: bool,
was_seen_recently: bool,
}
impl FullChat {
@@ -65,6 +67,17 @@ impl FullChat {
let can_send = chat.can_send(context).await?;
let was_seen_recently = if chat.get_type() == Chattype::Single {
match contact_ids.get(0) {
Some(contact) => Contact::load_from_db(context, *contact)
.await?
.was_seen_recently(),
None => false,
}
} else {
false
};
Ok(FullChat {
id: chat_id,
name: chat.name.clone(),
@@ -87,6 +100,7 @@ impl FullChat {
is_muted: chat.is_muted(),
ephemeral_timer,
can_send,
was_seen_recently,
})
}
}

View File

@@ -1,6 +1,6 @@
use anyhow::Result;
use deltachat::constants::*;
use deltachat::contact::ContactId;
use deltachat::contact::{Contact, ContactId};
use deltachat::{
chat::{get_chat_contacts, ChatVisibility},
chatlist::Chatlist,
@@ -42,8 +42,11 @@ pub enum ChatListItemFetchResult {
is_pinned: bool,
is_muted: bool,
is_contact_request: bool,
/// true when chat is a broadcastlist
is_broadcast: bool,
/// contact id if this is a dm chat (for view profile entry in context menu)
dm_chat_contact: Option<u32>,
was_seen_recently: bool,
},
ArchiveLink,
#[serde(rename_all = "camelCase")]
@@ -92,10 +95,20 @@ pub(crate) async fn get_chat_list_item_by_id(
let self_in_group = chat_contacts.contains(&ContactId::SELF);
let dm_chat_contact = if chat.get_type() == Chattype::Single {
chat_contacts.get(0).map(|contact_id| contact_id.to_u32())
let (dm_chat_contact, was_seen_recently) = if chat.get_type() == Chattype::Single {
let contact = chat_contacts.get(0);
let was_seen_recently = match contact {
Some(contact) => Contact::load_from_db(ctx, *contact)
.await?
.was_seen_recently(),
None => false,
};
(
contact.map(|contact_id| contact_id.to_u32()),
was_seen_recently,
)
} else {
None
(None, false)
};
let fresh_message_counter = chat_id.get_fresh_msg_cnt(ctx).await?;
@@ -121,6 +134,8 @@ pub(crate) async fn get_chat_list_item_by_id(
is_pinned: visibility == ChatVisibility::Pinned,
is_muted: chat.is_muted(),
is_contact_request: chat.is_contact_request(),
is_broadcast: chat.get_type() == Chattype::Broadcast,
dm_chat_contact,
was_seen_recently,
})
}

View File

@@ -20,6 +20,7 @@ pub struct ContactObject {
name_and_addr: String,
is_blocked: bool,
is_verified: bool,
was_seen_recently: bool,
}
impl ContactObject {
@@ -45,6 +46,7 @@ impl ContactObject {
name_and_addr: contact.get_name_n_addr(),
is_blocked: contact.is_blocked(),
is_verified,
was_seen_recently: contact.was_seen_recently(),
})
}
}

View File

@@ -10,6 +10,7 @@ use serde::Serialize;
use typescript_type_def::TypeDef;
use super::contact::ContactObject;
use super::webxdc::WebxdcMessageInfo;
#[derive(Serialize, TypeDef)]
#[serde(rename = "Message", rename_all = "camelCase")]
@@ -53,6 +54,8 @@ pub struct MessageObject {
file_mime: Option<String>,
file_bytes: u64,
file_name: Option<String>,
webxdc_info: Option<WebxdcMessageInfo>,
}
impl MessageObject {
@@ -70,6 +73,12 @@ impl MessageObject {
let file_bytes = message.get_filebytes(context).await;
let override_sender_name = message.get_override_sender_name();
let webxdc_info = if message.get_viewtype() == Viewtype::Webxdc {
Some(WebxdcMessageInfo::get_for_message(context, msg_id).await?)
} else {
None
};
Ok(MessageObject {
id: message_id,
chat_id: message.get_chat_id().to_u32(),
@@ -121,6 +130,7 @@ impl MessageObject {
file_mime: message.get_filemime(),
file_bytes,
file_name: message.get_filename(),
webxdc_info,
})
}
}

View File

@@ -33,6 +33,8 @@ pub struct WebxdcMessageInfo {
/// defaults to an empty string.
/// Implementations may offer an menu or a button to open this URL.
source_code_url: Option<String>,
/// True if full internet access should be granted to the app.
internet_access: bool,
}
impl WebxdcMessageInfo {
@@ -47,6 +49,7 @@ impl WebxdcMessageInfo {
document,
summary,
source_code_url,
internet_access,
} = message.get_webxdc_info(context).await?;
Ok(Self {
@@ -55,6 +58,7 @@ impl WebxdcMessageInfo {
document: maybe_empty_string_to_option(document),
summary: maybe_empty_string_to_option(summary),
source_code_url: maybe_empty_string_to_option(source_code_url),
internet_access,
})
}
}

View File

@@ -111,12 +111,10 @@ export class RawClient {
/**
* Set configuration values from a QR code. (technically from the URI that is stored in the qrcode)
* Before this function is called, dc_check_qr() should confirm the type of the
* QR code is DC_QR_ACCOUNT or DC_QR_WEBRTC_INSTANCE.
* Before this function is called, `checkQr()` should confirm the type of the
* QR code is `account` or `webrtcInstance`.
*
* Internally, the function will call dc_set_config() with the appropriate keys,
* e.g. `addr` and `mail_pw` for DC_QR_ACCOUNT
* or `webrtc_instance` for DC_QR_WEBRTC_INSTANCE.
*/
public setConfigFromQr(accountId: T.U32, qrContent: string): Promise<null> {
return (this._transport.request('set_config_from_qr', [accountId, qrContent] as RPC.Params)) as Promise<null>;
@@ -216,6 +214,93 @@ export class RawClient {
return (this._transport.request('block_chat', [accountId, chatId] as RPC.Params)) as Promise<null>;
}
/**
* Delete a chat.
*
* Messages are deleted from the device and the chat database entry is deleted.
* After that, the event #DC_EVENT_MSGS_CHANGED is posted.
*
* Things that are _not done_ implicitly:
*
* - Messages are **not deleted from the server**.
* - The chat or the contact is **not blocked**, so new messages from the user/the group may appear as a contact request
* and the user may create the chat again.
* - **Groups are not left** - this would
* be unexpected as (1) deleting a normal chat also does not prevent new mails
* from arriving, (2) leaving a group requires sending a message to
* all group members - especially for groups not used for a longer time, this is
* really unexpected when deletion results in contacting all members again,
* (3) only leaving groups is also a valid usecase.
*
* To leave a chat explicitly, use dc_remove_contact_from_chat() with
* chat_id=DC_CONTACT_ID_SELF)
*/
public deleteChat(accountId: T.U32, chatId: T.U32): Promise<null> {
return (this._transport.request('delete_chat', [accountId, chatId] as RPC.Params)) as Promise<null>;
}
/**
* Get encryption info for a chat.
* Get a multi-line encryption info, containing encryption preferences of all members.
* Can be used to find out why messages sent to group are not encrypted.
*
* returns Multi-line text
*/
public getChatEncryptionInfo(accountId: T.U32, chatId: T.U32): Promise<string> {
return (this._transport.request('get_chat_encryption_info', [accountId, chatId] as RPC.Params)) as Promise<string>;
}
/**
* Get QR code (text and SVG) that will offer an 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.
*
* The scanning device will pass the scanned content to `checkQr()` then;
* if `checkQr()` returns `askVerifyContact` or `askVerifyGroup`
* an out-of-band-verification can be joined using dc_join_securejoin()
*
* chat_id: If set to a group-chat-id,
* the Verified-Group-Invite protocol is offered in the QR code;
* works for protected groups as well as for normal groups.
* If not set, the Setup-Contact protocol is offered in the QR code.
* See https://countermitm.readthedocs.io/en/latest/new.html
* for details about both protocols.
*/
public getChatSecurejoinQrCodeSvg(accountId: T.U32, chatId: (T.U32|null)): Promise<[string,string]> {
return (this._transport.request('get_chat_securejoin_qr_code_svg', [accountId, chatId] as RPC.Params)) as Promise<[string,string]>;
}
public leaveGroup(accountId: T.U32, chatId: T.U32): Promise<null> {
return (this._transport.request('leave_group', [accountId, chatId] as RPC.Params)) as Promise<null>;
}
/**
* Remove a member from a group.
*
* If the group is already _promoted_ (any message was sent to the group),
* all group members are informed by a special status message that is sent automatically by this function.
*
* Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent.
*/
public removeContactFromChat(accountId: T.U32, chatId: T.U32, contactId: T.U32): Promise<null> {
return (this._transport.request('remove_contact_from_chat', [accountId, chatId, contactId] as RPC.Params)) as Promise<null>;
}
/**
* Add a member to a group.
*
* If the group is already _promoted_ (any message was sent to the group),
* all group members are informed by a special status message that is sent automatically by this function.
*
* If the group has group protection enabled, only verified contacts can be added to the group.
*
* Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent.
*/
public addContactToChat(accountId: T.U32, chatId: T.U32, contactId: T.U32): Promise<null> {
return (this._transport.request('add_contact_to_chat', [accountId, chatId, contactId] as RPC.Params)) as Promise<null>;
}
public addDeviceMessage(accountId: T.U32, label: string, text: string): Promise<T.U32> {
return (this._transport.request('add_device_message', [accountId, label, text] as RPC.Params)) as Promise<T.U32>;
@@ -236,6 +321,25 @@ export class RawClient {
return (this._transport.request('message_get_messages', [accountId, messageIds] as RPC.Params)) as Promise<Record<T.U32,T.Message>>;
}
/**
* Delete messages. The messages are deleted on the current device and
* on the IMAP server.
*/
public deleteMessages(accountId: T.U32, messageIds: (T.U32)[]): Promise<null> {
return (this._transport.request('delete_messages', [accountId, messageIds] as RPC.Params)) as Promise<null>;
}
/**
* Get an informational text for a single message. The text is multiline and may
* contain e.g. the raw text of the message.
*
* The max. text returned is typically longer (about 100000 characters) than the
* max. text returned by dc_msg_get_text() (about 30000 characters).
*/
public getMessageInfo(accountId: T.U32, messageId: T.U32): Promise<string> {
return (this._transport.request('get_message_info', [accountId, messageId] as RPC.Params)) as Promise<string>;
}
/**
* Get a single contact options by ID.
*/
@@ -299,7 +403,7 @@ export class RawClient {
* The list is already sorted and starts with the oldest message.
* Clients should not try to re-sort the list as this would be an expensive action
* and would result in inconsistencies between clients.
*
*
* Setting `chat_id` to `None` (`null` in typescript) means get messages with media
* from any chat of the currently used account.
*/

View File

@@ -1,3 +1,3 @@
// AUTO-GENERATED by typescript-type-def
export type EventTypeName=("Info"|"SmtpConnected"|"ImapConnected"|"SmtpMessageSent"|"ImapMessageDeleted"|"ImapMessageMoved"|"NewBlobFile"|"DeletedBlobFile"|"Warning"|"Error"|"ErrorSelfNotInGroup"|"MsgsChanged"|"IncomingMsg"|"MsgsNoticed"|"MsgDelivered"|"MsgFailed"|"MsgRead"|"ChatModified"|"ChatEphemeralTimerModified"|"ContactsChanged"|"LocationChanged"|"ConfigureProgress"|"ImexProgress"|"ImexFileWritten"|"SecurejoinInviterProgress"|"SecurejoinJoinerProgress"|"ConnectivityChanged"|"SelfavatarChanged"|"WebxdcStatusUpdate");
export type EventTypeName=("Info"|"SmtpConnected"|"ImapConnected"|"SmtpMessageSent"|"ImapMessageDeleted"|"ImapMessageMoved"|"NewBlobFile"|"DeletedBlobFile"|"Warning"|"Error"|"ErrorSelfNotInGroup"|"MsgsChanged"|"IncomingMsg"|"MsgsNoticed"|"MsgDelivered"|"MsgFailed"|"MsgRead"|"ChatModified"|"ChatEphemeralTimerModified"|"ContactsChanged"|"LocationChanged"|"ConfigureProgress"|"ImexProgress"|"ImexFileWritten"|"SecurejoinInviterProgress"|"SecurejoinJoinerProgress"|"ConnectivityChanged"|"SelfavatarChanged"|"WebxdcStatusUpdate"|"WebXdInstanceDeleted");

View File

@@ -8,12 +8,16 @@ export type Usize=number;
export type ChatListEntry=[U32,U32];
export type I64=number;
export type ChatListItemFetchResult=(({"type":"ChatListItem";}&{"id":U32;"name":string;"avatarPath":(string|null);"color":string;"lastUpdated":(I64|null);"summaryText1":string;"summaryText2":string;"summaryStatus":U32;"isProtected":boolean;"isGroup":boolean;"freshMessageCounter":Usize;"isSelfTalk":boolean;"isDeviceTalk":boolean;"isSendingLocation":boolean;"isSelfInGroup":boolean;"isArchived":boolean;"isPinned":boolean;"isMuted":boolean;"isContactRequest":boolean;
/**
* true when chat is a broadcastlist
*/
"isBroadcast":boolean;
/**
* contact id if this is a dm chat (for view profile entry in context menu)
*/
"dmChatContact":(U32|null);})|{"type":"ArchiveLink";}|({"type":"Error";}&{"id":U32;"error":string;}));
export type Contact={"address":string;"color":string;"authName":string;"status":string;"displayName":string;"id":U32;"name":string;"profileImage":(string|null);"nameAndAddr":string;"isBlocked":boolean;"isVerified":boolean;};
export type FullChat={"id":U32;"name":string;"isProtected":boolean;"profileImage":(string|null);"archived":boolean;"chatType":U32;"isUnpromoted":boolean;"isSelfTalk":boolean;"contacts":(Contact)[];"contactIds":(U32)[];"color":string;"freshMessageCounter":Usize;"isContactRequest":boolean;"isDeviceChat":boolean;"selfInGroup":boolean;"isMuted":boolean;"ephemeralTimer":U32;"canSend":boolean;};
"dmChatContact":(U32|null);"wasSeenRecently":boolean;})|{"type":"ArchiveLink";}|({"type":"Error";}&{"id":U32;"error":string;}));
export type Contact={"address":string;"color":string;"authName":string;"status":string;"displayName":string;"id":U32;"name":string;"profileImage":(string|null);"nameAndAddr":string;"isBlocked":boolean;"isVerified":boolean;"wasSeenRecently":boolean;};
export type FullChat={"id":U32;"name":string;"isProtected":boolean;"profileImage":(string|null);"archived":boolean;"chatType":U32;"isUnpromoted":boolean;"isSelfTalk":boolean;"contacts":(Contact)[];"contactIds":(U32)[];"color":string;"freshMessageCounter":Usize;"isContactRequest":boolean;"isDeviceChat":boolean;"selfInGroup":boolean;"isMuted":boolean;"ephemeralTimer":U32;"canSend":boolean;"wasSeenRecently":boolean;};
export type Viewtype=("Unknown"|
/**
* Text message.
@@ -61,7 +65,6 @@ export type Viewtype=("Unknown"|
"Webxdc");
export type I32=number;
export type U64=number;
export type Message={"id":U32;"chatId":U32;"fromId":U32;"quotedText":(string|null);"quotedMessageId":(U32|null);"text":(string|null);"hasLocation":boolean;"hasHtml":boolean;"viewType":Viewtype;"state":U32;"timestamp":I64;"sortTimestamp":I64;"receivedTimestamp":I64;"hasDeviatingTimestamp":boolean;"subject":string;"showPadlock":boolean;"isSetupmessage":boolean;"isInfo":boolean;"isForwarded":boolean;"duration":I32;"dimensionsHeight":I32;"dimensionsWidth":I32;"videochatType":(U32|null);"videochatUrl":(string|null);"overrideSenderName":(string|null);"sender":Contact;"setupCodeBegin":(string|null);"file":(string|null);"fileMime":(string|null);"fileBytes":U64;"fileName":(string|null);};
export type WebxdcMessageInfo={
/**
* The name of the app.
@@ -94,5 +97,10 @@ export type WebxdcMessageInfo={
* defaults to an empty string.
* Implementations may offer an menu or a button to open this URL.
*/
"sourceCodeUrl":(string|null);};
export type __AllTyps=[string,boolean,Record<string,string>,U32,U32,null,(U32)[],U32,null,(U32|null),(Account)[],U32,Account,U32,string,(ProviderInfo|null),U32,boolean,U32,Record<string,string>,U32,string,(string|null),null,U32,Record<string,(string|null)>,null,U32,string,null,U32,string,Qr,U32,string,(string|null),U32,(string)[],Record<string,(string|null)>,U32,null,U32,null,U32,(U32)[],U32,U32,Usize,U32,string,U32,U32,string,null,U32,(U32|null),(string|null),(U32|null),(ChatListEntry)[],U32,(ChatListEntry)[],Record<U32,ChatListItemFetchResult>,U32,U32,FullChat,U32,U32,null,U32,U32,null,U32,string,string,U32,U32,U32,U32,(U32)[],U32,U32,Message,U32,(U32)[],Record<U32,Message>,U32,U32,Contact,U32,string,(string|null),U32,U32,U32,U32,U32,U32,null,U32,U32,null,U32,(Contact)[],U32,U32,(string|null),(U32)[],U32,U32,(string|null),(Contact)[],U32,(U32)[],Record<U32,Contact>,U32,(U32|null),Viewtype,(Viewtype|null),(Viewtype|null),(U32)[],U32,U32,string,string,null,U32,U32,U32,string,U32,U32,WebxdcMessageInfo,U32,string,U32,U32];
"sourceCodeUrl":(string|null);
/**
* True if full internet access should be granted to the app.
*/
"internetAccess":boolean;};
export type Message={"id":U32;"chatId":U32;"fromId":U32;"quotedText":(string|null);"quotedMessageId":(U32|null);"text":(string|null);"hasLocation":boolean;"hasHtml":boolean;"viewType":Viewtype;"state":U32;"timestamp":I64;"sortTimestamp":I64;"receivedTimestamp":I64;"hasDeviatingTimestamp":boolean;"subject":string;"showPadlock":boolean;"isSetupmessage":boolean;"isInfo":boolean;"isForwarded":boolean;"duration":I32;"dimensionsHeight":I32;"dimensionsWidth":I32;"videochatType":(U32|null);"videochatUrl":(string|null);"overrideSenderName":(string|null);"sender":Contact;"setupCodeBegin":(string|null);"file":(string|null);"fileMime":(string|null);"fileBytes":U64;"fileName":(string|null);"webxdcInfo":(WebxdcMessageInfo|null);};
export type __AllTyps=[string,boolean,Record<string,string>,U32,U32,null,(U32)[],U32,null,(U32|null),(Account)[],U32,Account,U32,string,(ProviderInfo|null),U32,boolean,U32,Record<string,string>,U32,string,(string|null),null,U32,Record<string,(string|null)>,null,U32,string,null,U32,string,Qr,U32,string,(string|null),U32,(string)[],Record<string,(string|null)>,U32,null,U32,null,U32,(U32)[],U32,U32,Usize,U32,string,U32,U32,string,null,U32,(U32|null),(string|null),(U32|null),(ChatListEntry)[],U32,(ChatListEntry)[],Record<U32,ChatListItemFetchResult>,U32,U32,FullChat,U32,U32,null,U32,U32,null,U32,U32,null,U32,U32,string,U32,(U32|null),[string,string],U32,U32,null,U32,U32,U32,null,U32,U32,U32,null,U32,string,string,U32,U32,U32,U32,(U32)[],U32,U32,Message,U32,(U32)[],Record<U32,Message>,U32,(U32)[],null,U32,U32,string,U32,U32,Contact,U32,string,(string|null),U32,U32,U32,U32,U32,U32,null,U32,U32,null,U32,(Contact)[],U32,U32,(string|null),(U32)[],U32,U32,(string|null),(Contact)[],U32,(U32)[],Record<U32,Contact>,U32,(U32|null),Viewtype,(Viewtype|null),(Viewtype|null),(U32)[],U32,U32,string,string,null,U32,U32,U32,string,U32,U32,WebxdcMessageInfo,U32,string,U32,U32];

View File

@@ -1,29 +1,5 @@
{
"name": "deltachat-jsonrpc-client",
"version": "0.1.0",
"main": "dist/deltachat.js",
"types": "dist/deltachat.d.ts",
"type": "module",
"author": "Delta Chat Developers (ML) <delta@codespeak.net>",
"license": "MPL-2.0",
"scripts": {
"prettier:check": "prettier --check **.ts",
"prettier:fix": "prettier --write **.ts",
"generate-bindings": "cargo test",
"build": "run-s generate-bindings build:tsc build:bundle",
"build:tsc": "tsc",
"build:bundle": "esbuild --format=esm --bundle dist/deltachat.js --outfile=dist/deltachat.bundle.js",
"example": "run-s build example:build example:start",
"example:build": "esbuild --bundle dist/example/example.js --outfile=dist/example.bundle.js",
"example:start": "http-server .",
"example:dev": "esbuild example/example.ts --bundle --outfile=dist/example.bundle.js --servedir=.",
"test": "run-s test:prepare test:run-coverage test:report-coverage",
"test:prepare": "cargo build --features webserver --bin deltachat-jsonrpc-server",
"test:run": "mocha dist/test",
"test:run-coverage": "COVERAGE=1 NODE_OPTIONS=--enable-source-maps c8 --include 'dist/*' -r text -r html -r json mocha dist/test",
"test:report-coverage": "node report_api_coverage.mjs",
"docs": "typedoc --out docs deltachat.ts"
},
"dependencies": {
"isomorphic-ws": "^4.0.1",
"tiny-emitter": "git+https://github.com/Simon-Laux/tiny-emitter.git",
@@ -47,5 +23,29 @@
"typedoc": "^0.23.2",
"typescript": "^4.5.5",
"ws": "^8.5.0"
}
}
},
"license": "MPL-2.0",
"main": "dist/deltachat.js",
"name": "deltachat-jsonrpc-client",
"scripts": {
"build": "run-s generate-bindings build:tsc build:bundle",
"build:bundle": "esbuild --format=esm --bundle dist/deltachat.js --outfile=dist/deltachat.bundle.js",
"build:tsc": "tsc",
"docs": "typedoc --out docs deltachat.ts",
"example": "run-s build example:build example:start",
"example:build": "esbuild --bundle dist/example/example.js --outfile=dist/example.bundle.js",
"example:dev": "esbuild example/example.ts --bundle --outfile=dist/example.bundle.js --servedir=.",
"example:start": "http-server .",
"generate-bindings": "cargo test",
"prettier:check": "prettier --check **.ts",
"prettier:fix": "prettier --write **.ts",
"test": "run-s test:prepare test:run-coverage test:report-coverage",
"test:prepare": "cargo build --features webserver --bin deltachat-jsonrpc-server",
"test:report-coverage": "node report_api_coverage.mjs",
"test:run": "mocha dist/test",
"test:run-coverage": "COVERAGE=1 NODE_OPTIONS=--enable-source-maps c8 --include 'dist/*' -r text -r html -r json mocha dist/test"
},
"type": "module",
"types": "dist/deltachat.d.ts",
"version": "1.93.0"
}

View File

@@ -72,6 +72,10 @@ export class Contact {
return binding.dcn_contact_get_last_seen(this.dc_contact)
}
wasSeenRecently() {
return Boolean(binding.dcn_contact_was_seen_recently(this.dc_contact))
}
getName(): string {
return binding.dcn_contact_get_name(this.dc_contact)
}

View File

@@ -1930,6 +1930,13 @@ NAPI_METHOD(dcn_contact_get_last_seen) {
NAPI_RETURN_INT64(timestamp);
}
NAPI_METHOD(dcn_contact_was_seen_recently) {
NAPI_ARGV(1);
NAPI_DC_CONTACT();
int seen_recently = dc_contact_was_seen_recently(dc_contact);
NAPI_RETURN_UINT32(seen_recently);
}
NAPI_METHOD(dcn_contact_is_blocked) {
NAPI_ARGV(1);
NAPI_DC_CONTACT();
@@ -3105,14 +3112,14 @@ static void accounts_event_handler_thread_func(void* arg)
TRACE("event_handler_thread_func starting");
dc_accounts_event_emitter_t * dc_accounts_event_emitter = dc_accounts_get_event_emitter(dcn_accounts->dc_accounts);
dc_event_emitter_t * dc_event_emitter = dc_accounts_get_event_emitter(dcn_accounts->dc_accounts);
dc_event_t* event;
while (true) {
if (dc_accounts_event_emitter == NULL) {
if (dc_event_emitter == NULL) {
TRACE("event emitter is null, bailing");
break;
}
event = dc_accounts_get_next_event(dc_accounts_event_emitter);
event = dc_get_next_event(dc_event_emitter);
if (event == NULL) {
TRACE("no more events");
break;
@@ -3138,7 +3145,7 @@ static void accounts_event_handler_thread_func(void* arg)
}
}
dc_accounts_event_emitter_unref(dc_accounts_event_emitter);
dc_event_emitter_unref(dc_event_emitter);
TRACE("event_handler_thread_func ended");

View File

@@ -1,4 +0,0 @@
[devpi:upload]
formats = sdist.tgz
no-vcs = 1

View File

@@ -8,7 +8,7 @@ set -e -x
# compile core lib
cargo build --release -p deltachat_ffi
cargo build --release -p deltachat_ffi --features jsonrpc
# Statically link against libdeltachat.a.
export DCC_RS_DEV="$PWD"

View File

@@ -42,15 +42,15 @@ def replace_toml_version(relpath, newversion):
def read_json_version(relpath):
p = pathlib.Path("package.json")
p = pathlib.Path(relpath)
assert p.exists()
with open(p, "r") as f:
json_data = json.loads(f.read())
return json_data["version"]
def update_package_json(newversion):
p = pathlib.Path("package.json")
def update_package_json(relpath, newversion):
p = pathlib.Path(relpath)
assert p.exists()
with open(p, "r") as f:
json_data = json.loads(f.read())
@@ -64,13 +64,15 @@ def main():
parser.add_argument("newversion")
toml_list = ["Cargo.toml", "deltachat-ffi/Cargo.toml", "deltachat-jsonrpc/Cargo.toml"]
json_list = ["package.json", "deltachat-jsonrpc/typescript/package.json"]
try:
opts = parser.parse_args()
except SystemExit:
print()
for x in toml_list:
print("{}: {}".format(x, read_toml_version(x)))
print("package.json:", str(read_json_version("package.json")))
for x in json_list:
print("{}: {}".format(x, read_json_version(x)))
print()
raise SystemExit("need argument: new version, example: 1.25.0")
@@ -91,9 +93,12 @@ def main():
else:
raise SystemExit("CHANGELOG.md contains no entry for version: {}".format(newversion))
replace_toml_version("Cargo.toml", newversion)
replace_toml_version("deltachat-ffi/Cargo.toml", newversion)
update_package_json(newversion)
for toml_filename in toml_list:
replace_toml_version(toml_filename, newversion)
for json_filename in json_list:
update_package_json(json_filename, newversion)
print("running cargo check")
subprocess.call(["cargo", "check"])

View File

@@ -2525,7 +2525,7 @@ pub async fn get_chat_contacts(context: &Context, chat_id: ChatId) -> Result<Vec
LEFT JOIN contacts c
ON c.id=cc.contact_id
WHERE cc.chat_id=?
ORDER BY c.id=1, LOWER(c.name||c.addr), c.id;",
ORDER BY c.id=1, c.last_seen DESC, c.id DESC;",
paramsv![chat_id],
|row| row.get::<_, ContactId>(0),
|ids| ids.collect::<Result<Vec<_>, _>>().map_err(Into::into),
@@ -2863,7 +2863,7 @@ pub async fn remove_contact_from_chat(
let mut success = false;
/* we do not check if "contact_id" exists but just delete all records with the id from chats_contacts */
/* this allows to delete pending references to deleted contacts. Of course, this should _not_ happen. */
/* this allows to delete pending references to deleted contacts. Of course, this should _not_ happen. */
if let Ok(chat) = Chat::load_from_db(context, chat_id).await {
if chat.typ == Chattype::Group || chat.typ == Chattype::Broadcast {
if !chat.is_self_in_chat(context).await? {
@@ -5456,8 +5456,8 @@ mod tests {
assert_eq!(
chat_id.get_encryption_info(&alice).await?,
"No encryption:\n\
bob@example.net\n\
fiona@example.net"
fiona@example.net\n\
bob@example.net"
);
let direct_chat = bob.create_chat(&alice).await;

View File

@@ -24,9 +24,12 @@ use crate::mimeparser::AvatarAction;
use crate::param::{Param, Params};
use crate::peerstate::{Peerstate, PeerstateVerifiedStatus};
use crate::sql::{self, params_iter};
use crate::tools::{get_abs_path, improve_single_line_input, EmailAddress};
use crate::tools::{get_abs_path, improve_single_line_input, time, EmailAddress};
use crate::{chat, stock_str};
/// Time during which a contact is considered as seen recently.
const SEEN_RECENTLY_SECONDS: i64 = 600;
/// Contact ID, including reserved IDs.
///
/// Some contact IDs are reserved to identify special contacts. This
@@ -323,6 +326,11 @@ impl Contact {
self.last_seen
}
/// Returns `true` if this contact was seen recently.
pub fn was_seen_recently(&self) -> bool {
time() - self.last_seen <= SEEN_RECENTLY_SECONDS
}
/// Check if a contact is blocked.
pub async fn is_blocked_load(context: &Context, id: ContactId) -> Result<bool> {
let blocked = Self::load_from_db(context, id).await?.blocked;
@@ -708,7 +716,7 @@ impl Contact {
AND c.blocked=0 \
AND (iif(c.name='',c.authname,c.name) LIKE ? OR c.addr LIKE ?) \
AND (1=? OR LENGTH(ps.verified_key_fingerprint)!=0) \
ORDER BY LOWER(iif(c.name='',c.authname,c.name)||c.addr),c.id;",
ORDER BY c.last_seen DESC, c.id DESC;",
sql::repeat_vars(self_addrs.len())
),
rusqlite::params_from_iter(params_iter(&self_addrs).chain(params_iterv![
@@ -760,7 +768,7 @@ impl Contact {
AND id>?
AND origin>=?
AND blocked=0
ORDER BY LOWER(iif(name='',authname,name)||addr),id;",
ORDER BY last_seen DESC, id DESC;",
sql::repeat_vars(self_addrs.len())
),
rusqlite::params_from_iter(params_iter(&self_addrs).chain(params_iterv![
@@ -849,7 +857,7 @@ impl Contact {
let list = context
.sql
.query_map(
"SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(iif(name='',authname,name)||addr),id;",
"SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY last_seen DESC, id DESC;",
paramsv![ContactId::LAST_SPECIAL],
|row| row.get::<_, ContactId>(0),
|ids| {
@@ -1444,7 +1452,7 @@ mod tests {
use crate::chatlist::Chatlist;
use crate::message::Message;
use crate::receive_imf::receive_imf;
use crate::test_utils::{self, TestContext};
use crate::test_utils::{self, TestContext, TestContextManager};
#[test]
fn test_contact_id_values() {
@@ -2270,4 +2278,28 @@ Hi."#;
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_was_seen_recently() -> Result<()> {
let mut tcm = TestContextManager::new().await;
let alice = tcm.alice().await;
let bob = tcm.bob().await;
let chat = alice.create_chat(&bob).await;
let sent_msg = alice.send_text(chat.id, "moin").await;
let chat = bob.create_chat(&alice).await;
let contacts = chat::get_chat_contacts(&bob, chat.id).await?;
let contact = Contact::get_by_id(&bob, *contacts.first().unwrap()).await?;
assert!(!contact.was_seen_recently());
bob.recv_msg(&sent_msg).await;
let contact = Contact::get_by_id(&bob, *contacts.first().unwrap()).await?;
assert!(contact.was_seen_recently());
let self_contact = Contact::get_by_id(&bob, ContactId::SELF).await?;
assert!(!self_contact.was_seen_recently());
Ok(())
}
}

View File

@@ -302,4 +302,17 @@ pub enum EventType {
msg_id: MsgId,
status_update_serial: StatusUpdateSerial,
},
/// Inform that a message containing a webxdc instance has been deleted
WebxdcInstanceDeleted {
msg_id: MsgId,
},
WebxdcBusyUpdating {
msg_id: MsgId,
},
WebxdcUpToDate {
msg_id: MsgId,
},
}

View File

@@ -1236,6 +1236,11 @@ pub async fn delete_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> {
.trash(context)
.await
.with_context(|| format!("Unable to trash message {}", msg_id))?;
if msg.viewtype == Viewtype::Webxdc {
context.emit_event(EventType::WebxdcInstanceDeleted { msg_id: *msg_id });
}
context
.sql
.execute(

View File

@@ -445,6 +445,8 @@ impl<'a> MimeFactory<'a> {
.collect()
}
/// Consumes a `MimeFactory` and renders it into a message which is then stored in
/// `smtp`-table to be used by the SMTP loop
pub async fn render(mut self, context: &Context) -> Result<RenderedEmail> {
let mut headers: MessageHeaders = Default::default();

View File

@@ -551,7 +551,7 @@ impl MimeMessage {
// For mailing lists, always add the subject because sometimes there are different topics
// and otherwise it might be hard to keep track:
if self.is_mailinglist_message() {
if self.is_mailinglist_message() && !self.has_chat_version() {
prepend_subject = true;
}
@@ -746,7 +746,7 @@ impl MimeMessage {
MimeS::Single
}
} else if mimetype.starts_with("message") {
if mimetype == "message/rfc822" {
if mimetype == "message/rfc822" && !is_attachment_disposition(mail) {
MimeS::Message
} else {
MimeS::Single
@@ -1674,14 +1674,18 @@ fn get_mime_type(mail: &mailparse::ParsedMail<'_>) -> Result<(Mime, Viewtype)> {
mime::VIDEO => Viewtype::Video,
mime::MULTIPART => Viewtype::Unknown,
mime::MESSAGE => {
// Enacapsulated messages, see <https://www.w3.org/Protocols/rfc1341/7_3_Message.html>
// Also used as part "message/disposition-notification" of "multipart/report", which, however, will
// be handled separatedly.
// I've not seen any messages using this, so we do not attach these parts (maybe they're used to attach replies,
// which are unwanted at all).
// For now, we skip these parts at all; if desired, we could return DcMimeType::File/DC_MSG_File
// for selected and known subparts.
Viewtype::Unknown
if is_attachment_disposition(mail) {
Viewtype::File
} else {
// Enacapsulated messages, see <https://www.w3.org/Protocols/rfc1341/7_3_Message.html>
// Also used as part "message/disposition-notification" of "multipart/report", which, however, will
// be handled separatedly.
// I've not seen any messages using this, so we do not attach these parts (maybe they're used to attach replies,
// which are unwanted at all).
// For now, we skip these parts at all; if desired, we could return DcMimeType::File/DC_MSG_File
// for selected and known subparts.
Viewtype::Unknown
}
}
mime::APPLICATION => Viewtype::File,
_ => Viewtype::Unknown,
@@ -3299,4 +3303,36 @@ Message.
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_receive_eml() -> Result<()> {
let alice = TestContext::new_alice().await;
let mime_message = MimeMessage::from_bytes(
&alice,
include_bytes!("../test-data/message/attached-eml.eml"),
)
.await?;
assert_eq!(mime_message.parts.len(), 1);
assert_eq!(mime_message.parts[0].typ, Viewtype::File);
assert_eq!(
mime_message.parts[0].mimetype,
Some("message/rfc822".parse().unwrap(),)
);
assert_eq!(
mime_message.parts[0].msg,
"this is a classic email I attached the .EML file".to_string()
);
assert_eq!(
mime_message.parts[0].param.get(Param::File),
Some("$BLOBDIR/.eml")
);
assert_eq!(mime_message.parts[0].org_filename, Some(".eml".to_string()));
dbg!(mime_message);
Ok(())
}
}

View File

@@ -163,7 +163,7 @@ pub enum Param {
/// For Chats: timestamp of group name update.
GroupNameTimestamp = b'g',
/// For Chats: timestamp of group name update.
/// For Chats: timestamp of member list update.
MemberListTimestamp = b'k',
/// For Chats: timestamp of protection settings update.

View File

@@ -388,6 +388,9 @@ pub async fn from_field_to_contact_id(
}
}
/// Creates a `ReceivedMsg` from given parts which might consist of
/// mulitple messages (if there are multiple attachments).
/// Every entry in `mime_parser.parts` produces a new row in the `msgs` table.
#[allow(clippy::too_many_arguments, clippy::cognitive_complexity)]
async fn add_parts(
context: &Context,
@@ -492,7 +495,7 @@ async fn add_parts(
}
let test_normal_chat = if from_id == ContactId::UNDEFINED {
Default::default()
None
} else {
ChatIdBlocked::lookup_by_contact(context, from_id).await?
};
@@ -513,6 +516,9 @@ async fn add_parts(
}
}
// signals wether the current user is a bot
let is_bot = context.get_config(Config::Bot).await?.is_some();
if chat_id.is_none() {
// try to create a group
@@ -521,6 +527,7 @@ async fn add_parts(
id: _,
blocked: Blocked::Not,
}) => Blocked::Not,
_ if is_bot => Blocked::Not,
_ => Blocked::Request,
};
@@ -540,6 +547,9 @@ async fn add_parts(
{
chat_id = Some(new_chat_id);
chat_id_blocked = new_chat_id_blocked;
// if the chat is somehow blocked but we want to create a non-blocked chat,
// unblock the chat
if chat_id_blocked != Blocked::Not && create_blocked == Blocked::Not {
new_chat_id.unblock(context).await?;
chat_id_blocked = Blocked::Not;
@@ -637,10 +647,10 @@ async fn add_parts(
Blocked::Not
} else {
let contact = Contact::load_from_db(context, from_id).await?;
if contact.is_blocked() {
Blocked::Yes
} else {
Blocked::Request
match contact.is_blocked() {
true => Blocked::Yes,
false if is_bot => Blocked::Not,
false => Blocked::Request,
}
};
@@ -676,12 +686,17 @@ async fn add_parts(
}
}
state =
if seen || fetching_existing_messages || is_mdn || location_kml_is || securejoin_seen {
MessageState::InSeen
} else {
MessageState::InFresh
};
state = if seen
|| fetching_existing_messages
|| is_mdn
|| location_kml_is
|| securejoin_seen
|| chat_id_blocked == Blocked::Yes
{
MessageState::InSeen
} else {
MessageState::InFresh
};
} else {
// Outgoing
@@ -3106,8 +3121,14 @@ Hello mailinglist!\r\n"
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 0); // Test that the message disappeared
t.evtracker.consume_events().await;
receive_imf(&t.ctx, DC_MAILINGLIST2, false).await.unwrap();
// Check that no notification is displayed for blocked mailing list message.
while let Ok(event) = t.evtracker.try_recv() {
assert!(!matches!(event.typ, EventType::IncomingMsg { .. }));
}
// Test that the mailing list stays disappeared
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 0); // Test that the message is not shown
@@ -3551,6 +3572,27 @@ Hello mailinglist!\r\n"
)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_mailing_list_chat_message() {
let t = TestContext::new_alice().await;
receive_imf(
&t,
include_bytes!("../test-data/message/mailinglist_chat_message.eml"),
false,
)
.await
.unwrap();
let msg = t.get_last_msg().await;
assert_eq!(msg.text, Some("hello, this is a test 👋\n\n_______________________________________________\nTest1 mailing list -- test1@example.net\nTo unsubscribe send an email to test1-leave@example.net".to_string()));
assert!(!msg.has_html());
let chat = Chat::load_from_db(&t, msg.chat_id).await.unwrap();
assert_eq!(chat.typ, Chattype::Mailinglist);
assert_eq!(chat.blocked, Blocked::Request);
assert_eq!(chat.grpid, "test1.example.net");
assert_eq!(chat.name, "Test1");
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_dont_show_tokens_in_contacts_list() {
check_dont_show_in_contacts_list(
@@ -5079,6 +5121,17 @@ Reply from different address
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_auto_accept_for_bots() -> Result<()> {
let t = TestContext::new_alice().await;
t.set_config(Config::Bot, Some("1")).await.unwrap();
receive_imf(&t, MSGRMSG, false).await?;
let msg = t.get_last_msg().await;
let chat = chat::Chat::load_from_db(&t, msg.chat_id).await?;
assert!(!chat.is_contact_request());
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_no_private_reply_to_blocked_account() -> Result<()> {
let mut tcm = TestContextManager::new().await;

View File

@@ -332,7 +332,7 @@ async fn smtp_loop(ctx: Context, started: Sender<()>, smtp_handlers: SmtpConnect
if !duration_until_can_send.is_zero() {
info!(
ctx,
"smtp got rate limited, waiting for {} until can send again",
"smtp got rate limited, delaying next try by {}",
duration_to_str(duration_until_can_send)
);
tokio::time::timeout(duration_until_can_send, async {

View File

@@ -22,6 +22,7 @@ use crate::mimefactory::MimeFactory;
use crate::oauth2::get_oauth2_access_token;
use crate::provider::Socket;
use crate::sql;
use crate::webxdc::get_busy_webxdc_instances;
use crate::{context::Context, scheduler::connectivity::ConnectivityStore};
/// SMTP write and read timeout in seconds.
@@ -499,7 +500,15 @@ async fn send_mdns(context: &Context, connection: &mut Smtp) -> Result<()> {
pub(crate) async fn send_smtp_messages(context: &Context, connection: &mut Smtp) -> Result<()> {
let ratelimited = if context.ratelimit.read().await.can_send() {
// add status updates and sync messages to end of sending queue
let update_needed = get_busy_webxdc_instances(&context.sql).await?;
context.flush_status_updates().await?;
let update_needed_after_sending = get_busy_webxdc_instances(&context.sql).await?;
for msg_id in update_needed.difference(&update_needed_after_sending) {
context.emit_event(EventType::WebxdcUpToDate { msg_id: *msg_id })
}
context.send_sync_msg().await?;
false
} else {

View File

@@ -7,6 +7,7 @@ use std::path::PathBuf;
use std::time::Duration;
use anyhow::{bail, Context as _, Result};
use rusqlite::types::FromSql;
use rusqlite::{config::DbConfig, Connection, OpenFlags};
use tokio::sync::RwLock;
@@ -363,6 +364,25 @@ impl Sql {
})
}
/// Returns unique values of a `column` in `table`
pub async fn distinct<T: FromSql + Default>(
&self,
table: &str,
column: &str,
) -> Result<Vec<T>> {
let conn = self.get_conn().await?;
let rows: Result<Vec<T>> = tokio::task::block_in_place(move || {
let mut stmt = conn.prepare(&format!("SELECT DISTINCT {column} FROM {table}"))?;
let rows = stmt
.query([])?
.mapped(|r| r.get(0))
.map(|a| a.unwrap_or_default())
.collect();
Ok(rows)
});
rows
}
/// Prepares and executes the statement and maps a function over the resulting rows.
/// Then executes the second function over the returned iterator and returns the
/// result of that function.

View File

@@ -3,7 +3,7 @@
//! This private module is only compiled for test runs.
#![allow(clippy::indexing_slicing)]
use std::collections::BTreeMap;
use std::ops::Deref;
use std::ops::{Deref, DerefMut};
use std::panic;
use std::sync::Arc;
use std::time::{Duration, Instant};
@@ -816,6 +816,20 @@ pub fn fiona_keypair() -> key::KeyPair {
#[derive(Debug)]
pub struct EventTracker(Receiver<Event>);
impl Deref for EventTracker {
type Target = Receiver<Event>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for EventTracker {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl EventTracker {
/// Consumes emitted events returning the first matching one.
///
@@ -824,7 +838,7 @@ impl EventTracker {
pub async fn get_matching<F: Fn(&EventType) -> bool>(&self, event_matcher: F) -> EventType {
tokio::time::timeout(Duration::from_secs(10), async move {
loop {
let event = self.0.recv().await.unwrap();
let event = self.recv().await.unwrap();
if event_matcher(&event.typ) {
return event.typ;
}
@@ -842,6 +856,11 @@ impl EventTracker {
})
.await
}
/// Consumes all pending events.
pub async fn consume_events(&self) {
while self.try_recv().is_ok() {}
}
}
/// Gets a specific message from a chat and asserts that the chat has a specific length.

View File

@@ -1,5 +1,6 @@
//! # Handle webxdc messages.
use std::collections::HashSet;
use std::convert::TryFrom;
use std::path::Path;
@@ -20,6 +21,7 @@ use crate::mimeparser::SystemMessage;
use crate::param::Param;
use crate::param::Params;
use crate::scheduler::InterruptInfo;
use crate::sql::Sql;
use crate::tools::{create_smeared_timestamp, get_abs_path};
use crate::{chat, EventType};
@@ -56,6 +58,7 @@ struct WebxdcManifest {
name: Option<String>,
min_api: Option<u32>,
source_code_url: Option<String>,
request_internet_access: Option<bool>,
}
/// Parsed information from WebxdcManifest and fallbacks.
@@ -66,6 +69,7 @@ pub struct WebxdcInfo {
pub document: String,
pub summary: String,
pub source_code_url: String,
pub internet_access: bool,
}
/// Status Update ID.
@@ -375,6 +379,10 @@ impl Context {
)
.await?;
self.emit_event(EventType::WebxdcBusyUpdating {
msg_id: instance.id,
});
if send_now {
self.sql.insert(
"INSERT INTO smtp_status_updates (msg_id, first_serial, last_serial, descr) VALUES(?, ?, ?, ?)
@@ -388,7 +396,6 @@ impl Context {
}
/// Pops one record of queued webxdc status updates.
/// This function exists to make the sqlite statement testable.
async fn pop_smtp_status_update(
&self,
) -> Result<Option<(MsgId, StatusUpdateSerial, StatusUpdateSerial, String)>> {
@@ -487,6 +494,12 @@ impl Context {
bail!("receive_status_update: status message has no parent.")
};
if from_id != ContactId::SELF
&& !chat::is_contact_in_chat(self, instance.chat_id, from_id).await?
{
bail!("receive_status_update: status sender not chat member.")
}
let updates: StatusUpdates = serde_json::from_str(json)?;
for update_item in updates.updates {
self.create_status_update_record(
@@ -676,6 +689,7 @@ impl Message {
name: None,
min_api: None,
source_code_url: None,
request_internet_access: None,
}
}
} else {
@@ -683,6 +697,7 @@ impl Message {
name: None,
min_api: None,
source_code_url: None,
request_internet_access: None,
}
};
@@ -694,6 +709,10 @@ impl Message {
}
}
let internet_access = manifest.request_internet_access.unwrap_or_default()
&& self.chat_id.is_self_talk(context).await.unwrap_or_default()
&& self.get_showpadlock();
Ok(WebxdcInfo {
name: if let Some(name) = manifest.name {
name
@@ -712,29 +731,43 @@ impl Message {
.get(Param::WebxdcDocument)
.unwrap_or_default()
.to_string(),
summary: self
.param
.get(Param::WebxdcSummary)
.unwrap_or_default()
.to_string(),
summary: if internet_access {
"Dev Mode: Do not enter sensitive data!".to_string()
} else {
self.param
.get(Param::WebxdcSummary)
.unwrap_or_default()
.to_string()
},
source_code_url: if let Some(url) = manifest.source_code_url {
url
} else {
"".to_string()
},
internet_access,
})
}
}
/// Returns a hashset of all webxdc instaces which still have updates to send
pub(crate) async fn get_busy_webxdc_instances(sql: &Sql) -> Result<HashSet<MsgId>> {
Ok(sql
.distinct("smtp_status_updates", "msg_id")
.await?
.into_iter()
.collect())
}
#[cfg(test)]
mod tests {
use crate::chat::{
add_contact_to_chat, create_group_chat, forward_msgs, resend_msgs, send_msg, send_text_msg,
ChatId, ProtectionStatus,
add_contact_to_chat, create_broadcast_list, create_group_chat, forward_msgs,
remove_contact_from_chat, resend_msgs, send_msg, send_text_msg, ChatId, ProtectionStatus,
};
use crate::chatlist::Chatlist;
use crate::config::Config;
use crate::contact::Contact;
use crate::message;
use crate::receive_imf::{receive_imf, receive_imf_inner};
use crate::test_utils::TestContext;
@@ -1781,6 +1814,19 @@ sth_for_the = "future""#
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_parse_webxdc_manifest_request_internet_access() -> Result<()> {
let result = parse_webxdc_manifest(r#"request_internet_access = 3"#.as_bytes());
assert!(result.is_err());
let manifest = parse_webxdc_manifest(r#" source_code_url="https://foo.org""#.as_bytes())?;
assert_eq!(manifest.request_internet_access, None);
let manifest = parse_webxdc_manifest(r#" request_internet_access=false"#.as_bytes())?;
assert_eq!(manifest.request_internet_access, Some(false));
let manifest = parse_webxdc_manifest(r#"request_internet_access = true"#.as_bytes())?;
assert_eq!(manifest.request_internet_access, Some(true));
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_min_api_too_large() -> Result<()> {
let t = TestContext::new_alice().await;
@@ -2191,6 +2237,51 @@ sth_for_the = "future""#
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_internet_access() -> Result<()> {
let t = TestContext::new_alice().await;
let self_id = t.get_self_chat().await.id;
let single_id = t.create_chat_with_contact("bob", "bob@e.com").await.id;
let group_id = create_group_chat(&t, ProtectionStatus::Unprotected, "chat").await?;
let broadcast_id = create_broadcast_list(&t).await?;
let mut first_test = true; // only the first test has all conditions for internet access
for e2ee in ["1", "0"] {
t.set_config(Config::E2eeEnabled, Some(e2ee)).await?;
for chat_id in [self_id, single_id, group_id, broadcast_id] {
for internet_xdc in [true, false] {
let mut instance = create_webxdc_instance(
&t,
"foo.xdc",
if internet_xdc {
include_bytes!("../test-data/webxdc/request-internet-access.xdc")
} else {
include_bytes!("../test-data/webxdc/minimal.xdc")
},
)
.await?;
let instance_id = send_msg(&t, chat_id, &mut instance).await?;
t.send_webxdc_status_update(
instance_id,
r#"{"summary":"real summary", "payload": 42}"#,
"descr",
)
.await?;
let instance = Message::load_from_db(&t, instance_id).await?;
let info = instance.get_webxdc_info(&t).await?;
assert_eq!(info.internet_access, first_test);
assert_eq!(info.summary.contains("Do not enter sensitive"), first_test);
assert_eq!(info.summary.contains("real summary"), !first_test);
first_test = false;
}
}
}
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_chatlist_summary() -> Result<()> {
let t = TestContext::new_alice().await;
@@ -2247,4 +2338,71 @@ sth_for_the = "future""#
Ok(())
}
async fn helper_send_receive_status_update(
bob: &TestContext,
alice: &TestContext,
bob_instance: &Message,
alice_instance: &Message,
) -> Result<String> {
bob.send_webxdc_status_update(
bob_instance.id,
r#"{"payload":7,"info": "i","summary":"s"}"#,
"",
)
.await?;
bob.flush_status_updates().await?;
let msg = bob.pop_sent_msg().await;
alice.recv_msg(&msg).await;
alice
.get_webxdc_status_updates(alice_instance.id, StatusUpdateSerial(0))
.await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_reject_updates_from_non_groupmembers() -> Result<()> {
let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await;
let contact_bob = Contact::create(&alice, "Bob", "bob@example.net").await?;
let chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "Group").await?;
add_contact_to_chat(&alice, chat_id, contact_bob).await?;
let instance = send_webxdc_instance(&alice, chat_id).await?;
bob.recv_msg(&alice.pop_sent_msg().await).await;
let bob_instance = bob.get_last_msg().await;
Chat::load_from_db(&bob, bob_instance.chat_id)
.await?
.id
.accept(&bob)
.await?;
let status =
helper_send_receive_status_update(&bob, &alice, &bob_instance, &instance).await?;
assert_eq!(
status,
r#"[{"payload":7,"info":"i","summary":"s","serial":1,"max_serial":1}]"#
);
remove_contact_from_chat(&alice, chat_id, contact_bob).await?;
let status =
helper_send_receive_status_update(&bob, &alice, &bob_instance, &instance).await?;
assert_eq!(
status,
r#"[{"payload":7,"info":"i","summary":"s","serial":1,"max_serial":1}]"#
);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_webxdc_delete_event() -> Result<()> {
let alice = TestContext::new_alice().await;
let chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "foo").await?;
let instance = send_webxdc_instance(&alice, chat_id).await?;
message::delete_msgs(&alice, &[instance.id]).await?;
alice
.evtracker
.get_matching(|evt| matches!(evt, EventType::WebxdcInstanceDeleted { .. }))
.await;
Ok(())
}
}

View File

@@ -0,0 +1,138 @@
Return-Path: <bob@nauta.cu>
Authentication-Results: gmx.net; dkim=pass header.i=@nauta.cu
Received: from relay22.etecsa.net ([181.225.231.92]) by mx-ha.gmx.net
(mxgmx002 [212.227.15.9]) with ESMTPS (Nemesis) id 1MfpjH-1p2tvu2MDh-00gIrC
for <alice@gmx.de>; Fri, 26 Aug 2022 20:48:28 +0200
Received: from smtp-gw02.enet.cu (unknown [172.29.8.32])
by relay01.etecsa.net (Postfix) with ESMTP id 5FDB0E0136
for <alice@gmx.de>; Fri, 26 Aug 2022 14:48:18 -0400 (CDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nauta.cu; s=nauta;
t=1661539698; bh=pp7iY44vKTrdWyBjLR1PLAHM8rgsP+R0LngZ+hpp+Rc=;
h=Date:From:To:Subject:In-Reply-To:References;
b=H9gt32amS79HP/93yWsIeeMN28cpD/ly9yfTUkkLa+N7oKhUmTgvYd6AS4ztyoMRl
U18Gl9OsFlxKiUtJDA9xbXiJShJ1ixWZjsPWrRnsdlRC6p1NurZcZVC5LlxzMeqHaf
S+T9O6mIqqpMJ0UuEdNPQzFM22vCo7NAXuYICva4=
Received: from amavis2.etecsa.net (unknown [172.29.8.231])
by smtp-gw02.enet.cu (Postfix) with ESMTP id 74CBF180004C
for <alice@gmx.de>; Fri, 26 Aug 2022 14:48:15 -0400 (CDT)
Received: from smtp.nauta.cu ([172.29.8.248])
by amavis2.etecsa.net with ESMTP id 27QImIS3008021-27QImIS4008021;
Fri, 26 Aug 2022 14:48:18 -0400
Date: Fri, 26 Aug 2022 14:48:17 -0400
From: adb <bob@nauta.cu>
To: alice@gmx.de
Subject: this is a classic email
User-Agent: K-9 Mail for Android
In-Reply-To: <Mr.qEqZTYn44yJ.1aNPpfpFAA-@disrooot.org>
References: <Mr.qEqZTYn44yJ.1aNPpfpFAA-@disrooot.org>
Message-ID: <2DFA2B07-37E8-4E91-84D5-F1C9D81FF9E2@nauta.cu>
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary=----X9MFOPC8VAUATAMM54N60RYO457N0T
Content-Transfer-Encoding: 7bit
Envelope-To: <alice@gmx.de>
X-GMX-Antispam: 0 (Mail was not recognized as spam); Detail=V3;
X-Spam-Flag: NO
------X9MFOPC8VAUATAMM54N60RYO457N0T
Content-Type: multipart/alternative;
boundary=----S5IK4AODBC4A8E4GQE13T7ZD9M5SU9
Content-Transfer-Encoding: 7bit
------S5IK4AODBC4A8E4GQE13T7ZD9M5SU9
Content-Type: text/plain;
charset=utf-8
Content-Transfer-Encoding: quoted-printable
I attached the =2EEML file
------S5IK4AODBC4A8E4GQE13T7ZD9M5SU9
Content-Type: text/html;
charset=utf-8
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE html><html><body>I attached the =2EEML file it probably will look=
ugly in delta chat, use classic MUA</body></html>
------S5IK4AODBC4A8E4GQE13T7ZD9M5SU9--
------X9MFOPC8VAUATAMM54N60RYO457N0T
Content-Type: message/rfc822;
name=.eml
Content-Disposition: attachment;
filename=.eml;
size=5349
Return-Path: <charlie@hisp.net>
Delivered-To: <bob@nauta.cu>
Received: from smtp-gw02.enet.cu ([172.29.8.32])
by mail-backend21.enet.cu (Dovecot) with LMTP id 0fibJk4TCWP2QAAAhKHYxA
for <bob@nauta.cu>; Fri, 26 Aug 2022 14:42:34 -0400
Received: from amavis.etecsa.net (unknown [172.29.8.230])
by smtp-gw02.enet.cu (Postfix) with ESMTP id 83A41180004D
for <bob@nauta.cu>; Fri, 26 Aug 2022 14:42:31 -0400 (CDT)
Received: from emx01.etecsa.net (emx01.etecsa.net [181.225.231.41])
by amavis.etecsa.net with ESMTP id 27QIgTFM024250-27QIgTFN024250
for <bob@nauta.cu>; Fri, 26 Aug 2022 14:42:29 -0400
Received-SPF: pass (hisp.net: 51.15.127.36 is authorized to use 'charlie@hisp.net' in 'mfrom' identity (mechanism 'mx' matched)) receiver=unknown; identity=mailfrom; envelope-from="charlie@hisp.net"; helo=box.hisp.net; client-ip=51.15.127.36
Received: from box.hisp.net (box.hisp.net [51.15.127.36])
by emx01.etecsa.net (Postfix) with ESMTP id 9A8969F15B
for <bob@nauta.cu>; Fri, 26 Aug 2022 14:42:28 -0400 (CDT)
Received: from box.hisp.net (localhost [127.0.0.1])
by box.hisp.net (Postfix) with ESMTP id 9621D7E44F
for <bob@nauta.cu>; Fri, 26 Aug 2022 20:42:27 +0200 (CEST)
Authentication-Results: box.hisp.net;
dkim=fail reason="signature verification failed" (2048-bit key; secure) header.d=disrooot.org header.i=@disrooot.org header.b="kqh3WUKq";
dkim-atps=neutral
Authentication-Results: box.hisp.net; dmarc=pass (p=quarantine dis=none) header.from=disrooot.org
Authentication-Results: box.hisp.net; spf=pass smtp.mailfrom=bob@disrooot.org
Received: from localhost (localhost [127.0.0.1])
by disrooot.org (Postfix) with UTF8SMTP id CB886400EA
for <test2@hisp.net>; Fri, 26 Aug 2022 20:42:25 +0200 (CEST)
X-Virus-Scanned: SPAM Filter at disrooot.org
Received: from knopi.disrooot.org ([127.0.0.1])
by localhost (disrooot.org [127.0.0.1]) (amavisd-new, port 10024)
with UTF8SMTP id 8rOR0oFsvsSB for <test2@hisp.net>;
Fri, 26 Aug 2022 20:42:24 +0200 (CEST)
Received: from [127.0.0.1] (unknown [152.207.59.223])
by disrooot.org (Postfix) with UTF8SMTPSA id 97C3440140
for <test2@hisp.net>; Fri, 26 Aug 2022 20:42:21 +0200 (CEST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=disrooot.org; s=mail;
t=1661539342; bh=PDQuwT72ixzAL9uft0/IufLsqJSAuFu4gcNsEA84hd0=;
h=From:To:Date:In-Reply-To:References;
b=kqh3WUKqi5QMv4DT02hqyHcXZvcGu91aHu8bbSuZ2VCUnef5Ggwv0/WE5LBB/Xte2
on7yxLG4eqXRkWCmWStlqJmPhwuGcDupyGy2pFNyvRl8eERPb/uSlTsoSNCIEf5fNT
I2X+bEtyzp7jch3ZIxJgBZ/B3U/ByOO26NsqMP/Gy6ph21TL3YqWhR4VXi60KQXa6G
6A1X1M6pppNGYXofI6qJtjyb+wfQuzeTMRO4hqNqPEnOjETmbFpHYqrBDzz7pL4bZj
+Sdwj7cU+RpFJZPwbRs86wUV2lBIEyP4Pizyr/pgYcyXIt3pzSRHvBsukDQbyQabme
LoyApyOJnzbSA==
From: adb <bob@disrooot.org>
To: <test2@hisp.net>
Date: Fri, 26 Aug 2022 18:42:18 +0000
Message-ID: <Mr.qEqZTYn44yJ.1aNPpfpFAA-@disrooot.org>
In-Reply-To: <Mr.CmBUFS4tI6D.LHAPUDmW-x0@nauta.cu>
References: <Mr.CmBUFS4tI6D.LHAPUDmW-x0@nauta.cu>
<Mr.CmBUFS4tI6D.LHAPUDmW-x0@nauta.cu>
Chat-Version: 1.0
Mime-Version: 1.0
Message-ID-Hash: KJBUH4PALETYB65FC5SOMXP5HRWUIQEH
X-Message-ID-Hash: KJBUH4PALETYB65FC5SOMXP5HRWUIQEH
X-MailFrom: bob@disrooot.org
X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; deltachat; implicit-dest; max-recipients; max-size; news-moderation; digests; suspicious-header
X-Mailman-Version: 3.3.6b1
Precedence: list
List-Id: =?utf-8?b?4pqg77iPIE5PVCBBTk9OWU1PVVMgR1JPVVA=?= <test2.hisp.net>
List-Help: <mailto:test2-request@hisp.net?subject=help>
List-Owner: <mailto:test2-owner@hisp.net>
List-Post: <mailto:test2@hisp.net>
List-Subscribe: <mailto:test2-join@hisp.net>
List-Unsubscribe: <mailto:test2-leave@hisp.net>
Content-Type: text/plain; charset="utf-8"; format="flowed"; delsp="no"
Content-Transfer-Encoding: base64
aGVsbG8gZnJvbSBkaXNyb290IOKYuu+4jw0KDQotLSAKX19fX19fX19fX19fX19fX19fX19fX19f
X19fX19fX19fX19fX19fX19fX19fX18K4pqg77iPIE5PVCBBTk9OWU1PVVMgR1JPVVAgbWFpbGlu
ZyBsaXN0IC0tIHRlc3QyQGhpc3BhbmlsYW5kaWEubmV0ClRvIHVuc3Vic2NyaWJlIHNlbmQgYW4g
ZW1haWwgdG8gdGVzdDItbGVhdmVAaGlzcGFuaWxhbmRpYS5uZXQK
------X9MFOPC8VAUATAMM54N60RYO457N0T--

View File

@@ -0,0 +1,29 @@
From: <tmp.jvg4y@testrun.org>
To: <test1@example.net>
Date: Sat, 23 Apr 2022 00:40:47 +0000
Message-ID: <Mr.rcN94EGkUqw.mz9DVEAlh1p@testrun.org>
In-Reply-To: <Mr.olg8zDlL3HE.rImDv3GWJBk@testrun.org>
References: <Mr.a3MAke9MKLq.NNRzMZnyTr6@example.org>
<Mr.olg8zDlL3HE.rImDv3GWJBk@testrun.org>
Chat-Version: 1.0
MIME-Version: 1.0
Message-ID-Hash: NTNAY2662T7LMIXRSA2VZMFR52VKP35F
X-Message-ID-Hash: NTNAY2662T7LMIXRSA2VZMFR52VKP35F
X-MailFrom: tmp.jvg4y@testrun.org
X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header
X-Mailman-Version: 3.3.6b1
Precedence: list
Subject: [Test1] Re: Message from alice@example.org
List-Id: <test1.example.net>
List-Help: <mailto:test1-request@example.net?subject=help>
List-Owner: <mailto:test1-owner@example.net>
List-Post: <mailto:test1@example.net>
List-Subscribe: <mailto:test1-join@example.net>
List-Unsubscribe: <mailto:test1-leave@example.net>
Content-Type: text/plain; charset="utf-8"; format="flowed"; delsp="no"
Content-Transfer-Encoding: base64
aGVsbG8sIHRoaXMgaXMgYSB0ZXN0IPCfkYsNCg0KX19fX19fX19fX19fX19fX19fX19fX19fX19f
X19fX19fX19fX19fX19fX19fX18KVGVzdDEgbWFpbGluZyBsaXN0IC0tIHRlc3QxQGV4YW1wbGUu
bmV0ClRvIHVuc3Vic2NyaWJlIHNlbmQgYW4gZW1haWwgdG8gdGVzdDEtbGVhdmVAZXhhbXBsZS5u
ZXQK

Binary file not shown.