mirror of
https://github.com/chatmail/core.git
synced 2026-07-23 03:13:06 +03:00
Compare commits
2 Commits
v1.112.2
...
flub/log_e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16bb02ba96 | ||
|
|
cabefee4c0 |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -77,9 +77,6 @@ jobs:
|
||||
- os: windows-latest
|
||||
rust: 1.68.0
|
||||
python: false # Python bindings compilation on Windows is not supported.
|
||||
- os: macos-latest
|
||||
rust: 1.68.0
|
||||
python: 3.9
|
||||
|
||||
# Minimum Supported Rust Version = 1.64.0
|
||||
#
|
||||
|
||||
106
.github/workflows/deltachat-rpc-server.yml
vendored
106
.github/workflows/deltachat-rpc-server.yml
vendored
@@ -8,14 +8,35 @@ concurrency:
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
# Build a version statically linked against musl libc
|
||||
# to avoid problems with glibc version incompatibility.
|
||||
build_static_linux:
|
||||
name: Build deltachat-rpc-server for Linux
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add x86_64-unknown-linux-musl
|
||||
|
||||
- name: Install musl-gcc
|
||||
run: sudo apt install musl-tools
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
RUSTFLAGS: "-C link-arg=-s"
|
||||
run: cargo build --release --target x86_64-unknown-linux-musl -p deltachat-rpc-server --features vendored
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-x86_64
|
||||
path: target/x86_64-unknown-linux-musl/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
build_linux:
|
||||
name: Cross-compile deltachat-rpc-server for x86_64, aarch64 and armv7 Linux
|
||||
name: Cross-compile deltachat-rpc-server for aarch64 and armv7 Linux
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -23,13 +44,6 @@ jobs:
|
||||
- name: Build
|
||||
run: sh scripts/zig-rpc-server.sh
|
||||
|
||||
- name: Upload x86_64 binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-x86_64
|
||||
path: target/x86_64-unknown-linux-musl/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload aarch64 binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -44,6 +58,50 @@ jobs:
|
||||
path: target/armv7-unknown-linux-musleabihf/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
build_android:
|
||||
name: Cross-compile deltachat-rpc-server for Android (armeabi-v7a, arm64-v8a, x86 and x86_64)
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r21d
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
run: sh scripts/android-rpc-server.sh
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-android-armv7
|
||||
path: target/armv7-linux-androideabi/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-android-aarch64
|
||||
path: target/aarch64-linux-android/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-android-i686
|
||||
path: target/i686-linux-android/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-android-x86_64
|
||||
path: target/x86_64-linux-android/release/deltachat-rpc-server
|
||||
if-no-files-found: error
|
||||
|
||||
build_windows:
|
||||
name: Build deltachat-rpc-server for Windows
|
||||
strategy:
|
||||
@@ -76,31 +134,3 @@ jobs:
|
||||
name: deltachat-rpc-server-${{ matrix.artifact }}
|
||||
path: target/${{ matrix.target}}/release/${{ matrix.path }}
|
||||
if-no-files-found: error
|
||||
|
||||
publish:
|
||||
name: Upload binaries to the release
|
||||
needs: ["build_linux", "build_windows"]
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: Download built binaries
|
||||
uses: "actions/download-artifact@v3"
|
||||
|
||||
- name: Compose dist/ directory
|
||||
run: |
|
||||
mkdir dist
|
||||
for x in x86_64 aarch64 armv7 win32.exe win64.exe; do
|
||||
mv "deltachat-rpc-server-$x"/* "dist/deltachat-rpc-server-$x"
|
||||
done
|
||||
|
||||
- name: List downloaded artifacts
|
||||
run: ls -l dist/
|
||||
|
||||
- name: Upload binaries to the GitHub release
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
run: |
|
||||
gh release upload ${{ github.ref_name }} \
|
||||
--repo ${{ github.repository }} \
|
||||
dist/*
|
||||
|
||||
52
CHANGELOG.md
52
CHANGELOG.md
@@ -1,40 +1,20 @@
|
||||
# Changelog
|
||||
|
||||
## [1.112.2] - 2023-03-30
|
||||
|
||||
### Changes
|
||||
- Update iroh, remove `default-net` from `[patch.crates-io]` section.
|
||||
- transfer backup: Connect to mutliple provider addresses concurrently. This should speed up connection time significantly on the getter side. #4240
|
||||
- Make sure BackupProvider is cancelled on drop (or dc_backup_provider_unref). The BackupProvider will now alaway finish with an IMEX event of 1000 or 0, previoulsy it would sometimes finishe with 1000 (success) when it really was 0 (failure). #4242
|
||||
|
||||
### Fixes
|
||||
- Do not return media from trashed messages in the "All media" view. #4247
|
||||
|
||||
|
||||
## [1.112.1] - 2023-03-27
|
||||
|
||||
### Changes
|
||||
- Add support for `--version` argument to `deltachat-rpc-server`. #4224
|
||||
It can be used to check the installed version without starting the server.
|
||||
|
||||
### Fixes
|
||||
- deltachat-rpc-client: fix bug in `Chat.send_message()`: invalid `MessageData` field `quotedMsg` instead of `quotedMsgId`
|
||||
- `receive_imf`: Mark special messages as seen. Exactly: delivery reports, webxdc status updates. #4230
|
||||
|
||||
|
||||
## [1.112.0] - 2023-03-23
|
||||
## [Unreleased]
|
||||
|
||||
### Changes
|
||||
- "full message view" not needed because of footers that go to contact status #4151
|
||||
- Pick up system's light/dark mode in generated message HTML #4150
|
||||
- Support non-persistent configuration with DELTACHAT_* env
|
||||
- Print deltachat-repl errors with causes. #4166
|
||||
- Increase MSRV to 1.64. #4167
|
||||
- Core takes care of stopping and re-starting IO itself where needed,
|
||||
e.g. during backup creation.
|
||||
It is no longer needed to call `dc_stop_io()`.
|
||||
`dc_start_io()` can now be called at any time without harm. #4138
|
||||
- Pick up system's light/dark mode in generated message HTML. #4150
|
||||
- More accurate `maybe_add_bcc_self` device message text. #4175
|
||||
- "Full message view" not needed because of footers that go to contact status. #4151
|
||||
- Support non-persistent configuration with `DELTACHAT_*` env. #4154
|
||||
- Print deltachat-repl errors with causes. #4166
|
||||
e.g. during backup creation. It is no longer needed to call
|
||||
dc_stop_io(). dc_start_io() can now be called at any time without
|
||||
harm. #4138
|
||||
- More accurate maybe_add_bcc_self device message text #4175
|
||||
- LogExt::log_err now calls Context::set_last_error so that the FFI
|
||||
can use this and have consistent dc_get_last_error() reporting. #4187
|
||||
|
||||
### Fixes
|
||||
- Fix segmentation fault if `dc_context_unref()` is called during
|
||||
@@ -44,12 +24,6 @@
|
||||
- Delete expired messages using multiple SQL requests. #4158
|
||||
- Do not emit "Failed to run incremental vacuum" warnings on success. #4160
|
||||
- Ability to send backup over network and QR code to setup second device #4007
|
||||
- Disable buffering during STARTTLS setup. #4190
|
||||
- Add `DC_EVENT_IMAP_INBOX_IDLE` event to wait until the account
|
||||
is ready for testing.
|
||||
It is used to fix race condition between fetching
|
||||
existing messages and starting the test. #4208
|
||||
|
||||
|
||||
## [1.111.0] - 2023-03-05
|
||||
|
||||
@@ -2341,7 +2315,5 @@ For a full list of changes, please see our closed Pull Requests:
|
||||
|
||||
https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed
|
||||
|
||||
[unreleased]: https://github.com/deltachat/deltachat-core-rust/compare/v1.111.0...HEAD
|
||||
[1.111.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.110.0...v1.111.0
|
||||
[1.112.0]: https://github.com/deltachat/deltachat-core-rust/compare/v1.111.0...v1.112.0
|
||||
[1.112.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.112.0...v1.112.1
|
||||
[1.112.1]: https://github.com/deltachat/deltachat-core-rust/compare/v1.112.1...v1.112.2
|
||||
|
||||
471
Cargo.lock
generated
471
Cargo.lock
generated
@@ -146,9 +146,9 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -158,9 +158,9 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -265,21 +265,30 @@ version = "0.1.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async_io_utilities"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b20cffc5590f4bf33f05f97a3ea587feba9c50d20325b401daa096b92ff7da0"
|
||||
dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async_zip"
|
||||
version = "0.0.11"
|
||||
version = "0.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50d29ab7e2f9e808cca1a69ea56a36f4ff216f54a41a23aae1fd4afc05cc020"
|
||||
checksum = "6a36d43bdefc7215b2b3a97edd03b1553b7969ad76551025eedd3b913c645f6e"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"async_io_utilities",
|
||||
"chrono",
|
||||
"crc32fast",
|
||||
"log",
|
||||
"pin-project",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
@@ -368,6 +377,12 @@ dependencies = [
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base-x"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.1.1"
|
||||
@@ -656,11 +671,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"clap_lex 0.2.4",
|
||||
"indexmap",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex 0.3.2",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
@@ -670,6 +713,15 @@ dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "4.5.0"
|
||||
@@ -712,6 +764,19 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"unicode-width",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.2"
|
||||
@@ -733,9 +798,9 @@ version = "0.2.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d897f47bf7270cf70d370f8f98c1abb6d2d4cf60a6845d30e05bfb90c6568650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"unicode-xid 0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -806,7 +871,7 @@ dependencies = [
|
||||
"atty",
|
||||
"cast",
|
||||
"ciborium",
|
||||
"clap",
|
||||
"clap 3.2.23",
|
||||
"criterion-plot",
|
||||
"futures",
|
||||
"itertools",
|
||||
@@ -943,10 +1008,10 @@ dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"scratch",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -961,9 +1026,9 @@ version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -994,10 +1059,10 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"strsim",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1008,10 +1073,10 @@ checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"strsim",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1021,8 +1086,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||
dependencies = [
|
||||
"darling_core 0.13.4",
|
||||
"quote",
|
||||
"syn",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1032,8 +1097,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685"
|
||||
dependencies = [
|
||||
"darling_core 0.14.3",
|
||||
"quote",
|
||||
"syn",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1043,12 +1108,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||
|
||||
[[package]]
|
||||
name = "default-net"
|
||||
version = "0.14.1"
|
||||
name = "data-encoding-macro"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4898b43aed56499fad6b294d15b3e76a51df68079bf492e5daae38ca084e003"
|
||||
checksum = "86927b7cd2fe88fa698b87404b287ab98d1a0063a34071d92e575b72d3029aca"
|
||||
dependencies = [
|
||||
"dlopen2",
|
||||
"data-encoding",
|
||||
"data-encoding-macro-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding-macro-internal"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db"
|
||||
dependencies = [
|
||||
"data-encoding",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "default-net"
|
||||
version = "0.13.1"
|
||||
source = "git+https://github.com/dignifiedquire/default-net.git?branch=feat-android#7a257095bac009c4be0b93c2979801624fdd337b"
|
||||
dependencies = [
|
||||
"dlopen",
|
||||
"libc",
|
||||
"memalloc",
|
||||
"netlink-packet-core",
|
||||
@@ -1061,7 +1145,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.112.2"
|
||||
version = "1.111.0"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"anyhow",
|
||||
@@ -1126,7 +1210,6 @@ dependencies = [
|
||||
"tokio-io-timeout",
|
||||
"tokio-stream",
|
||||
"tokio-tar",
|
||||
"tokio-util",
|
||||
"toml",
|
||||
"trust-dns-resolver",
|
||||
"url",
|
||||
@@ -1135,7 +1218,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.112.2"
|
||||
version = "1.111.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-channel",
|
||||
@@ -1158,7 +1241,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-repl"
|
||||
version = "1.112.2"
|
||||
version = "1.111.0"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"anyhow",
|
||||
@@ -1173,10 +1256,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.112.2"
|
||||
version = "1.111.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
"deltachat-jsonrpc",
|
||||
"env_logger 0.10.0",
|
||||
"futures-lite",
|
||||
@@ -1191,13 +1273,13 @@ dependencies = [
|
||||
name = "deltachat_derive"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.112.2"
|
||||
version = "1.111.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deltachat",
|
||||
@@ -1206,7 +1288,7 @@ dependencies = [
|
||||
"libc",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"rand 0.8.5",
|
||||
"rand 0.7.3",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@@ -1245,9 +1327,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ef71ddb5b3a1f53dee24817c8f70dfa1cb29e804c18d88c228d4bc9c86ee3b9"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1266,9 +1348,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4"
|
||||
dependencies = [
|
||||
"darling 0.14.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1278,7 +1360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1288,10 +1370,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"convert_case 0.4.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1371,32 +1453,32 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlopen2"
|
||||
version = "0.4.1"
|
||||
name = "dlopen"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b121caccfc363e4d9a4589528f3bef7c71b83c6ed01c8dc68cbeeb7fd29ec698"
|
||||
checksum = "71e80ad39f814a9abe68583cd50a2d45c8a67561c3361ab8da240587dda80937"
|
||||
dependencies = [
|
||||
"dlopen2_derive",
|
||||
"dlopen_derive",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlopen2_derive"
|
||||
version = "0.2.0"
|
||||
name = "dlopen_derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a09ac8bb8c16a282264c379dffba707b9c998afc7506009137f3c6136888078"
|
||||
checksum = "f236d9e1b1fbd81cea0f9cbdc8dcc7e8ebcd80e6659cd7cb2ad5f6c05946c581"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"libc",
|
||||
"quote 0.6.13",
|
||||
"syn 0.15.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1443,9 +1525,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb0188e3c3ba8df5753894d54461f0e39bc91741dc5b22e1c46999ec2c71f4e4"
|
||||
dependencies = [
|
||||
"enum-ordinalize",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1488,6 +1570,12 @@ dependencies = [
|
||||
"version_check 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "encoded-words"
|
||||
version = "0.2.0"
|
||||
@@ -1594,9 +1682,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1607,10 +1695,10 @@ checksum = "a62bb1df8b45ecb7ffa78dca1c17a438fb193eb083db0b1b494d2a61bcb5096a"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1781,7 +1869,7 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"pin-project",
|
||||
"spin 0.9.7",
|
||||
"spin 0.9.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1887,9 +1975,9 @@ version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2277,6 +2365,19 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729"
|
||||
dependencies = [
|
||||
"console",
|
||||
"number_prefix",
|
||||
"portable-atomic 0.3.19",
|
||||
"tokio",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
@@ -2325,15 +2426,17 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
|
||||
|
||||
[[package]]
|
||||
name = "iroh"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c019223f5af15f978ff44ae02b8b83d21d53df4c42d4475aa80670819c3ecdce"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/n0-computer/iroh?branch=flub/ticket-multiple-addrs#aacd1d84e11b218e55e9622c4500973224904fe9"
|
||||
dependencies = [
|
||||
"abao",
|
||||
"anyhow",
|
||||
"base64 0.21.0",
|
||||
"blake3",
|
||||
"bytes",
|
||||
"clap 4.1.8",
|
||||
"console",
|
||||
"data-encoding",
|
||||
"default-net",
|
||||
"der",
|
||||
"derive_more",
|
||||
@@ -2341,8 +2444,10 @@ dependencies = [
|
||||
"ed25519-dalek",
|
||||
"futures",
|
||||
"hex",
|
||||
"indicatif",
|
||||
"multibase",
|
||||
"num_cpus",
|
||||
"portable-atomic",
|
||||
"portable-atomic 1.0.1",
|
||||
"postcard",
|
||||
"quic-rpc",
|
||||
"quinn",
|
||||
@@ -2356,12 +2461,10 @@ dependencies = [
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-futures",
|
||||
"tracing-subscriber",
|
||||
"walkdir",
|
||||
"webpki",
|
||||
"x509-parser",
|
||||
"zeroize",
|
||||
@@ -2634,6 +2737,17 @@ dependencies = [
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multibase"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404"
|
||||
dependencies = [
|
||||
"base-x",
|
||||
"data-encoding",
|
||||
"data-encoding-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mutate_once"
|
||||
version = "0.1.1"
|
||||
@@ -2811,9 +2925,9 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2868,6 +2982,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.30.3"
|
||||
@@ -2906,9 +3026,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.48"
|
||||
version = "0.10.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2"
|
||||
checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
@@ -2925,9 +3045,9 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2947,9 +3067,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.83"
|
||||
version = "0.9.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b"
|
||||
checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
@@ -2994,9 +3114,9 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3146,9 +3266,9 @@ version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3231,6 +3351,12 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.0.1"
|
||||
@@ -3255,9 +3381,9 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc4b01218787dd4420daf63875163a787a78294ad48a24e9f6fa8c6507759a79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3283,9 +3409,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
"version_check 0.9.4",
|
||||
]
|
||||
|
||||
@@ -3295,11 +3421,20 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"version_check 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
@@ -3420,13 +3555,22 @@ dependencies = [
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2 1.0.51",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3978,9 +4122,9 @@ version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4147,9 +4291,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.7"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0959fd6f767df20b231736396e4f602171e00d95205676286e79d4a4eb67bef"
|
||||
checksum = "b5d6e0250b93c8427a177b849d144a96d5acc57006149479403d7861ab721e34"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
@@ -4242,10 +4386,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"rustversion",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4254,14 +4398,25 @@ version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30",
|
||||
"quote 0.6.13",
|
||||
"unicode-xid 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
@@ -4277,10 +4432,10 @@ version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unicode-xid",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
"unicode-xid 0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4386,9 +4541,9 @@ version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4500,9 +4655,9 @@ version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4663,9 +4818,9 @@ version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4821,9 +4976,9 @@ dependencies = [
|
||||
"darling 0.13.4",
|
||||
"ident_case",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4875,6 +5030,12 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
@@ -5008,9 +5169,9 @@ dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -5032,7 +5193,7 @@ version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"quote 1.0.23",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
@@ -5042,9 +5203,9 @@ version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -5347,9 +5508,9 @@ checksum = "6bd53ff9053698697b92c2535bf7ecb983fd5d546d690b7c725e5070d6d9a620"
|
||||
dependencies = [
|
||||
"convert_case 0.5.0",
|
||||
"darling 0.14.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5367,8 +5528,8 @@ version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"proc-macro2 1.0.51",
|
||||
"quote 1.0.23",
|
||||
"syn 1.0.109",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
11
Cargo.toml
11
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.112.2"
|
||||
version = "1.111.0"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
rust-version = "1.64"
|
||||
@@ -25,6 +25,7 @@ panic = 'abort'
|
||||
opt-level = "z"
|
||||
|
||||
[patch.crates-io]
|
||||
default-net = { git = "https://github.com/dignifiedquire/default-net.git", branch="feat-android" }
|
||||
quinn-udp = { git = "https://github.com/quinn-rs/quinn", branch="main" }
|
||||
quinn-proto = { git = "https://github.com/quinn-rs/quinn", branch="main" }
|
||||
|
||||
@@ -38,7 +39,7 @@ async-channel = "1.8.0"
|
||||
async-imap = { git = "https://github.com/async-email/async-imap", branch = "master", default-features = false, features = ["runtime-tokio"] }
|
||||
async-native-tls = { version = "0.5", default-features = false, features = ["runtime-tokio"] }
|
||||
async-smtp = { version = "0.9", default-features = false, features = ["runtime-tokio"] }
|
||||
async_zip = { version = "0.0.11", default-features = false, features = ["deflate", "fs"] }
|
||||
async_zip = { version = "0.0.9", default-features = false, features = ["deflate"] }
|
||||
backtrace = "0.3"
|
||||
base64 = "0.21"
|
||||
bitflags = "1.3"
|
||||
@@ -52,7 +53,8 @@ futures-lite = "1.12.0"
|
||||
hex = "0.4.0"
|
||||
humansize = "2"
|
||||
image = { version = "0.24.5", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] }
|
||||
iroh = { version = "0.4.0", default-features = false }
|
||||
# iroh = { version = "0.3.0", default-features = false }
|
||||
iroh = { git = 'https://github.com/n0-computer/iroh', branch = "flub/ticket-multiple-addrs" }
|
||||
kamadak-exif = "0.5"
|
||||
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
|
||||
libc = "0.2"
|
||||
@@ -83,11 +85,10 @@ strum_macros = "0.24"
|
||||
tagger = "4.3.4"
|
||||
textwrap = "0.16.0"
|
||||
thiserror = "1"
|
||||
tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] }
|
||||
tokio-io-timeout = "1.2.0"
|
||||
tokio-stream = { version = "0.1.11", features = ["fs"] }
|
||||
tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar
|
||||
tokio-util = "0.7.7"
|
||||
tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] }
|
||||
toml = "0.7"
|
||||
trust-dns-resolver = "0.22"
|
||||
url = "2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.112.2"
|
||||
version = "1.111.0"
|
||||
description = "Deltachat FFI"
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
@@ -23,7 +23,7 @@ serde_json = "1.0"
|
||||
tokio = { version = "1", features = ["rt-multi-thread"] }
|
||||
anyhow = "1"
|
||||
thiserror = "1"
|
||||
rand = "0.8"
|
||||
rand = "0.7"
|
||||
once_cell = "1.17.0"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -72,6 +72,7 @@ typedef struct _dc_event_emitter dc_accounts_event_emitter_t;
|
||||
*
|
||||
* The example above uses "pthreads",
|
||||
* however, you can also use anything else for thread handling.
|
||||
* All deltachat-core functions, unless stated otherwise, are thread-safe.
|
||||
*
|
||||
* Now you can **configure the context:**
|
||||
*
|
||||
@@ -141,67 +142,6 @@ typedef struct _dc_event_emitter dc_accounts_event_emitter_t;
|
||||
* ~~~
|
||||
*
|
||||
*
|
||||
* ## Thread safety
|
||||
*
|
||||
* All deltachat-core functions, unless stated otherwise, are thread-safe.
|
||||
* In particular, it is safe to pass the same dc_context_t pointer
|
||||
* to multiple functions running concurrently in different threads.
|
||||
*
|
||||
* All the functions are guaranteed not to use the reference passed to them
|
||||
* after returning. If the function spawns a long-running process,
|
||||
* such as dc_configure() or dc_imex(), it will ensure that the objects
|
||||
* passed to them are not deallocated as long as they are needed.
|
||||
* For example, it is safe to call dc_imex(context, ...) and
|
||||
* call dc_context_unref(context) immediately after return from dc_imex().
|
||||
* It is however **not safe** to call dc_context_unref(context) concurrently
|
||||
* until dc_imex() returns, because dc_imex() may have not increased
|
||||
* the reference count of dc_context_t yet.
|
||||
*
|
||||
* This means that the context may be still in use after
|
||||
* dc_context_unref() call.
|
||||
* For example, it is possible to start the import/export process,
|
||||
* call dc_context_unref(context) immediately after
|
||||
* and observe #DC_EVENT_IMEX_PROGRESS events via the event emitter.
|
||||
* Once dc_get_next_event() returns NULL,
|
||||
* it is safe to terminate the application.
|
||||
*
|
||||
* It is recommended to create dc_context_t in the main thread
|
||||
* and only call dc_context_unref() once other threads that may use it,
|
||||
* such as the event loop thread, are terminated.
|
||||
* Common mistake is to use dc_context_unref() as a way
|
||||
* to cause dc_get_next_event() return NULL and terminate event loop this way.
|
||||
* If event loop thread is inside a function taking dc_context_t
|
||||
* as an argument at the moment dc_context_unref() is called on the main thread,
|
||||
* the behavior is undefined.
|
||||
*
|
||||
* Recommended way to safely terminate event loop
|
||||
* and shutdown the application is
|
||||
* to use a boolean variable
|
||||
* indicating that the event loop should stop
|
||||
* and check it in the event loop thread
|
||||
* every time before calling dc_get_next_event().
|
||||
* To terminate the event loop, main thread should:
|
||||
* 1. Notify event loop that it should terminate by atomically setting the
|
||||
* boolean flag in the memory shared between the main thread and event loop.
|
||||
* 2. Call dc_stop_io() or dc_accounts_stop_io(), depending
|
||||
* on whether a single account or account manager is used.
|
||||
* Stopping I/O is guaranteed to emit at least one event
|
||||
* and interrupt the event loop even if it was blocked on dc_get_next_event().
|
||||
* 3. Wait until the event loop thread notices the flag,
|
||||
* exits the event loop and terminates.
|
||||
* 4. Call dc_context_unref() or dc_accounts_unref().
|
||||
* 5. Keep calling dc_get_next_event() in a loop until it returns NULL,
|
||||
* indicating that the contexts are deallocated.
|
||||
* 6. Terminate the application.
|
||||
*
|
||||
* When using C API via FFI in runtimes that use automatic memory management,
|
||||
* such as CPython, JVM or Node.js, take care to ensure the correct
|
||||
* shutdown order and avoid calling dc_context_unref() or dc_accounts_unref()
|
||||
* on the objects still in use in other threads,
|
||||
* e.g. by keeping a reference to the wrapper object.
|
||||
* The details depend on the runtime being used.
|
||||
*
|
||||
*
|
||||
* ## Class reference
|
||||
*
|
||||
* For a class reference, see the "Classes" link atop.
|
||||
@@ -2704,6 +2644,10 @@ void dc_str_unref (char* str);
|
||||
/**
|
||||
* Creates an object for sending a backup to another device.
|
||||
*
|
||||
* Before calling this function IO must be stopped using dc_accounts_stop_io()
|
||||
* or dc_stop_io() so that no changes to the blobs or database are happening.
|
||||
* IO should only be restarted once dc_backup_provider_wait() has returned.
|
||||
*
|
||||
* The backup is sent to through a peer-to-peer channel which is bootstrapped
|
||||
* by a QR-code. The backup contains the entire state of the account
|
||||
* including credentials. This can be used to setup a new device.
|
||||
@@ -2764,7 +2708,9 @@ char* dc_backup_provider_get_qr_svg (const dc_backup_provider_t* backup_provider
|
||||
/**
|
||||
* Waits for the sending to finish.
|
||||
*
|
||||
* This is a blocking call and should only be called once.
|
||||
* This is a blocking call and should only be called once. Once this function
|
||||
* returns IO can be started again using dc_accounts_start_io() or
|
||||
* dc_start_io().
|
||||
*
|
||||
* @memberof dc_backup_provider_t
|
||||
* @param backup_provider The backup provider object as created by
|
||||
@@ -2775,12 +2721,6 @@ void dc_backup_provider_wait (dc_backup_provider_t* backup_provider);
|
||||
/**
|
||||
* Frees a dc_backup_provider_t object.
|
||||
*
|
||||
* If the provider has not yet finished, as indicated by
|
||||
* dc_backup_provider_wait() or the #DC_EVENT_IMEX_PROGRESS event with value
|
||||
* of 0 (failed) or 1000 (succeeded), this will also abort any in-progress
|
||||
* transfer. If this aborts the provider a #DC_EVENT_IMEX_PROGRESS event with
|
||||
* value 0 (failed) will be emitted.
|
||||
*
|
||||
* @memberof dc_backup_provider_t
|
||||
* @param backup_provider The backup provider object as created by
|
||||
* dc_backup_provider_new().
|
||||
@@ -5771,14 +5711,6 @@ void dc_event_unref(dc_event_t* event);
|
||||
*/
|
||||
#define DC_EVENT_IMAP_MESSAGE_MOVED 105
|
||||
|
||||
/**
|
||||
* Emitted before going into IDLE on the Inbox folder.
|
||||
*
|
||||
* @param data1 0
|
||||
* @param data2 0
|
||||
*/
|
||||
#define DC_EVENT_IMAP_INBOX_IDLE 106
|
||||
|
||||
/**
|
||||
* Emitted when a new blob file was successfully written
|
||||
*
|
||||
@@ -7064,11 +6996,6 @@ void dc_event_unref(dc_event_t* event);
|
||||
/// `%1$s` will be replaced by name and address of the contact.
|
||||
#define DC_STR_PROTECTION_DISABLED_BY_OTHER 161
|
||||
|
||||
/// "Scan to set up second device for %1$s"
|
||||
///
|
||||
/// `%1$s` will be replaced by name and address of the account.
|
||||
#define DC_STR_BACKUP_TRANSFER_QR 162
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@@ -500,7 +500,6 @@ pub unsafe extern "C" fn dc_event_get_id(event: *mut dc_event_t) -> libc::c_int
|
||||
EventType::SmtpMessageSent(_) => 103,
|
||||
EventType::ImapMessageDeleted(_) => 104,
|
||||
EventType::ImapMessageMoved(_) => 105,
|
||||
EventType::ImapInboxIdle => 106,
|
||||
EventType::NewBlobFile(_) => 150,
|
||||
EventType::DeletedBlobFile(_) => 151,
|
||||
EventType::Warning(_) => 300,
|
||||
@@ -545,7 +544,6 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
|
||||
| EventType::SmtpMessageSent(_)
|
||||
| EventType::ImapMessageDeleted(_)
|
||||
| EventType::ImapMessageMoved(_)
|
||||
| EventType::ImapInboxIdle
|
||||
| EventType::NewBlobFile(_)
|
||||
| EventType::DeletedBlobFile(_)
|
||||
| EventType::Warning(_)
|
||||
@@ -596,7 +594,6 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
|
||||
| EventType::SmtpMessageSent(_)
|
||||
| EventType::ImapMessageDeleted(_)
|
||||
| EventType::ImapMessageMoved(_)
|
||||
| EventType::ImapInboxIdle
|
||||
| EventType::NewBlobFile(_)
|
||||
| EventType::DeletedBlobFile(_)
|
||||
| EventType::Warning(_)
|
||||
@@ -656,7 +653,6 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
|
||||
EventType::MsgsChanged { .. }
|
||||
| EventType::ReactionsChanged { .. }
|
||||
| EventType::IncomingMsg { .. }
|
||||
| EventType::ImapInboxIdle
|
||||
| EventType::MsgsNoticed(_)
|
||||
| EventType::MsgDelivered { .. }
|
||||
| EventType::MsgFailed { .. }
|
||||
@@ -4170,8 +4166,6 @@ pub unsafe extern "C" fn dc_backup_provider_new(
|
||||
})
|
||||
.map(|ffi_provider| Box::into_raw(Box::new(ffi_provider)))
|
||||
.log_err(ctx, "BackupProvider failed")
|
||||
.context("BackupProvider failed")
|
||||
.set_last_error(ctx)
|
||||
.unwrap_or(ptr::null_mut())
|
||||
}
|
||||
|
||||
@@ -4187,8 +4181,6 @@ pub unsafe extern "C" fn dc_backup_provider_get_qr(
|
||||
let ctx = &*ffi_provider.context;
|
||||
deltachat::qr::format_backup(&ffi_provider.provider.qr())
|
||||
.log_err(ctx, "BackupProvider get_qr failed")
|
||||
.context("BackupProvider get_qr failed")
|
||||
.set_last_error(ctx)
|
||||
.unwrap_or_default()
|
||||
.strdup()
|
||||
}
|
||||
@@ -4206,8 +4198,6 @@ pub unsafe extern "C" fn dc_backup_provider_get_qr_svg(
|
||||
let provider = &ffi_provider.provider;
|
||||
block_on(generate_backup_qr(ctx, &provider.qr()))
|
||||
.log_err(ctx, "BackupProvider get_qr_svg failed")
|
||||
.context("BackupProvider get_qr_svg failed")
|
||||
.set_last_error(ctx)
|
||||
.unwrap_or_default()
|
||||
.strdup()
|
||||
}
|
||||
@@ -4223,17 +4213,11 @@ pub unsafe extern "C" fn dc_backup_provider_wait(provider: *mut dc_backup_provid
|
||||
let provider = &mut ffi_provider.provider;
|
||||
block_on(provider)
|
||||
.log_err(ctx, "Failed to await BackupProvider")
|
||||
.context("Failed to await BackupProvider")
|
||||
.set_last_error(ctx)
|
||||
.ok();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_backup_provider_unref(provider: *mut dc_backup_provider_t) {
|
||||
if provider.is_null() {
|
||||
eprintln!("ignoring careless call to dc_backup_provider_unref()");
|
||||
return;
|
||||
}
|
||||
drop(Box::from_raw(provider));
|
||||
}
|
||||
|
||||
@@ -4248,28 +4232,17 @@ pub unsafe extern "C" fn dc_receive_backup(
|
||||
}
|
||||
let ctx = &*context;
|
||||
let qr_text = to_string_lossy(qr);
|
||||
receive_backup(ctx.clone(), qr_text)
|
||||
}
|
||||
|
||||
// Because this is a long-running operation make sure we own the Context. This stops a FFI
|
||||
// user from deallocating it by calling unref on the object while we are using it.
|
||||
fn receive_backup(ctx: Context, qr_text: String) -> libc::c_int {
|
||||
let qr = match block_on(qr::check_qr(&ctx, &qr_text))
|
||||
.log_err(&ctx, "Invalid QR code")
|
||||
.context("Invalid QR code")
|
||||
.set_last_error(&ctx)
|
||||
{
|
||||
let qr = match block_on(qr::check_qr(ctx, &qr_text)).log_err(ctx, "Invalid QR code") {
|
||||
Ok(qr) => qr,
|
||||
Err(_) => return 0,
|
||||
};
|
||||
match block_on(imex::get_backup(&ctx, qr))
|
||||
.log_err(&ctx, "Get backup failed")
|
||||
.context("Get backup failed")
|
||||
.set_last_error(&ctx)
|
||||
{
|
||||
Ok(_) => 1,
|
||||
Err(_) => 0,
|
||||
}
|
||||
spawn(async move {
|
||||
imex::get_backup(ctx, qr)
|
||||
.await
|
||||
.log_err(ctx, "Get backup failed")
|
||||
.ok();
|
||||
});
|
||||
1
|
||||
}
|
||||
|
||||
trait ResultExt<T, E> {
|
||||
@@ -4292,56 +4265,6 @@ impl<T: Default, E: std::fmt::Display> ResultExt<T, E> for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
trait ResultLastError<T, E>
|
||||
where
|
||||
E: std::fmt::Display,
|
||||
{
|
||||
/// Sets this `Err` value using [`Context::set_last_error`].
|
||||
///
|
||||
/// Normally each FFI-API *should* call this if it handles an error from the Rust API:
|
||||
/// errors which need to be reported to users in response to an API call need to be
|
||||
/// propagated up the Rust API and at the FFI boundary need to be stored into the "last
|
||||
/// error" so the FFI users can retrieve an appropriate error message on failure. Often
|
||||
/// you will want to combine this with a call to [`LogExt::log_err`].
|
||||
///
|
||||
/// Since historically calls to the `deltachat::log::error!()` macro were (and sometimes
|
||||
/// still are) shown as error toasts to the user, this macro also calls
|
||||
/// [`Context::set_last_error`]. It is preferable however to rely on normal error
|
||||
/// propagation in Rust code however and only use this `ResultExt::set_last_error` call
|
||||
/// in the FFI layer.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Fully handling an error in the FFI code looks like this currently:
|
||||
///
|
||||
/// ```no_compile
|
||||
/// some_dc_rust_api_call_returning_result()
|
||||
/// .log_err(&context, "My API call failed")
|
||||
/// .context("My API call failed")
|
||||
/// .set_last_error(&context)
|
||||
/// .unwrap_or_default()
|
||||
/// ```
|
||||
///
|
||||
/// As shows it is a shame the `.log_err()` call currently needs a message instead of
|
||||
/// relying on an implicit call to the [`anyhow::Context`] call if needed. This stems
|
||||
/// from a time before we fully embraced anyhow. Some day we'll also fix that.
|
||||
///
|
||||
/// [`Context::set_last_error`]: context::Context::set_last_error
|
||||
fn set_last_error(self, context: &context::Context) -> Result<T, E>;
|
||||
}
|
||||
|
||||
impl<T, E> ResultLastError<T, E> for Result<T, E>
|
||||
where
|
||||
E: std::fmt::Display,
|
||||
{
|
||||
fn set_last_error(self, context: &context::Context) -> Result<T, E> {
|
||||
if let Err(ref err) = self {
|
||||
context.set_last_error(&format!("{err:#}"));
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
trait ResultNullableExt<T> {
|
||||
fn into_raw(self) -> *mut T;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.112.2"
|
||||
version = "1.111.0"
|
||||
description = "DeltaChat JSON-RPC API"
|
||||
edition = "2021"
|
||||
default-run = "deltachat-jsonrpc-server"
|
||||
|
||||
@@ -47,9 +47,6 @@ pub enum JSONRPCEventType {
|
||||
msg: String,
|
||||
},
|
||||
|
||||
/// Emitted before going into IDLE on the Inbox folder.
|
||||
ImapInboxIdle,
|
||||
|
||||
/// Emitted when an new file in the $BLOBDIR was created
|
||||
NewBlobFile {
|
||||
file: String,
|
||||
@@ -296,7 +293,6 @@ impl From<EventType> for JSONRPCEventType {
|
||||
EventType::SmtpMessageSent(msg) => SmtpMessageSent { msg },
|
||||
EventType::ImapMessageDeleted(msg) => ImapMessageDeleted { msg },
|
||||
EventType::ImapMessageMoved(msg) => ImapMessageMoved { msg },
|
||||
EventType::ImapInboxIdle => ImapInboxIdle,
|
||||
EventType::NewBlobFile(file) => NewBlobFile { file },
|
||||
EventType::DeletedBlobFile(file) => DeletedBlobFile { file },
|
||||
EventType::Warning(msg) => Warning { msg },
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
pub use deltachat::accounts::Accounts;
|
||||
use deltachat::qr::Qr;
|
||||
use deltachat::{
|
||||
chat::{
|
||||
self, add_contact_to_chat, forward_msgs, get_chat_media, get_chat_msgs, get_chat_msgs_ex,
|
||||
@@ -30,8 +29,7 @@ use deltachat::{
|
||||
webxdc::StatusUpdateSerial,
|
||||
};
|
||||
use sanitize_filename::is_sanitized;
|
||||
use tokio::fs;
|
||||
use tokio::sync::{watch, Mutex, RwLock};
|
||||
use tokio::{fs, sync::RwLock};
|
||||
use walkdir::WalkDir;
|
||||
use yerpc::rpc;
|
||||
|
||||
@@ -59,45 +57,21 @@ use self::types::{
|
||||
use crate::api::types::chat_list::{get_chat_list_item_by_id, ChatListItemFetchResult};
|
||||
use crate::api::types::qr::QrObject;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AccountState {
|
||||
/// The Qr code for current [`CommandApi::provide_backup`] call.
|
||||
///
|
||||
/// If there currently is a call to [`CommandApi::provide_backup`] this will be
|
||||
/// `Pending` or `Ready`, otherwise `NoProvider`.
|
||||
backup_provider_qr: watch::Sender<ProviderQr>,
|
||||
}
|
||||
|
||||
impl Default for AccountState {
|
||||
fn default() -> Self {
|
||||
let (tx, _rx) = watch::channel(ProviderQr::NoProvider);
|
||||
Self {
|
||||
backup_provider_qr: tx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CommandApi {
|
||||
pub(crate) accounts: Arc<RwLock<Accounts>>,
|
||||
|
||||
states: Arc<Mutex<BTreeMap<u32, AccountState>>>,
|
||||
}
|
||||
|
||||
impl CommandApi {
|
||||
pub fn new(accounts: Accounts) -> Self {
|
||||
CommandApi {
|
||||
accounts: Arc::new(RwLock::new(accounts)),
|
||||
states: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn from_arc(accounts: Arc<RwLock<Accounts>>) -> Self {
|
||||
CommandApi {
|
||||
accounts,
|
||||
states: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
}
|
||||
CommandApi { accounts }
|
||||
}
|
||||
|
||||
async fn get_context(&self, id: u32) -> Result<deltachat::context::Context> {
|
||||
@@ -109,38 +83,6 @@ impl CommandApi {
|
||||
.ok_or_else(|| anyhow!("account with id {} not found", id))?;
|
||||
Ok(sc)
|
||||
}
|
||||
|
||||
async fn with_state<F, T>(&self, id: u32, with_state: F) -> T
|
||||
where
|
||||
F: FnOnce(&AccountState) -> T,
|
||||
{
|
||||
let mut states = self.states.lock().await;
|
||||
let state = states.entry(id).or_insert_with(Default::default);
|
||||
with_state(state)
|
||||
}
|
||||
|
||||
async fn inner_get_backup_qr(&self, account_id: u32) -> Result<Qr> {
|
||||
let mut receiver = self
|
||||
.with_state(account_id, |state| state.backup_provider_qr.subscribe())
|
||||
.await;
|
||||
|
||||
let val: ProviderQr = receiver.borrow_and_update().clone();
|
||||
match val {
|
||||
ProviderQr::NoProvider => bail!("No backup being provided"),
|
||||
ProviderQr::Pending => loop {
|
||||
if receiver.changed().await.is_err() {
|
||||
bail!("No backup being provided (account state dropped)");
|
||||
}
|
||||
let val: ProviderQr = receiver.borrow().clone();
|
||||
match val {
|
||||
ProviderQr::NoProvider => bail!("No backup being provided"),
|
||||
ProviderQr::Pending => continue,
|
||||
ProviderQr::Ready(qr) => break Ok(qr),
|
||||
};
|
||||
},
|
||||
ProviderQr::Ready(qr) => Ok(qr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rpc(all_positional, ts_outdir = "typescript/generated")]
|
||||
@@ -173,13 +115,7 @@ impl CommandApi {
|
||||
}
|
||||
|
||||
async fn remove_account(&self, account_id: u32) -> Result<()> {
|
||||
self.accounts
|
||||
.write()
|
||||
.await
|
||||
.remove_account(account_id)
|
||||
.await?;
|
||||
self.states.lock().await.remove(&account_id);
|
||||
Ok(())
|
||||
self.accounts.write().await.remove_account(account_id).await
|
||||
}
|
||||
|
||||
async fn get_all_account_ids(&self) -> Vec<u32> {
|
||||
@@ -1422,35 +1358,31 @@ impl CommandApi {
|
||||
///
|
||||
/// This **stops IO** while it is running.
|
||||
///
|
||||
/// Returns once a remote device has retrieved the backup, or is cancelled.
|
||||
/// Returns once a remote device has retrieved the backup.
|
||||
async fn provide_backup(&self, account_id: u32) -> Result<()> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
self.with_state(account_id, |state| {
|
||||
state.backup_provider_qr.send_replace(ProviderQr::Pending);
|
||||
})
|
||||
.await;
|
||||
|
||||
let provider = imex::BackupProvider::prepare(&ctx).await?;
|
||||
self.with_state(account_id, |state| {
|
||||
state
|
||||
.backup_provider_qr
|
||||
.send_replace(ProviderQr::Ready(provider.qr()));
|
||||
})
|
||||
.await;
|
||||
|
||||
provider.await
|
||||
ctx.stop_io().await;
|
||||
let provider = match imex::BackupProvider::prepare(&ctx).await {
|
||||
Ok(provider) => provider,
|
||||
Err(err) => {
|
||||
ctx.start_io().await;
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
let res = provider.await;
|
||||
ctx.start_io().await;
|
||||
res
|
||||
}
|
||||
|
||||
/// Returns the text of the QR code for the running [`CommandApi::provide_backup`].
|
||||
///
|
||||
/// This QR code text can be used in [`CommandApi::get_backup`] on a second device to
|
||||
/// retrieve the backup and setup this second device.
|
||||
///
|
||||
/// This call will fail if there is currently no concurrent call to
|
||||
/// [`CommandApi::provide_backup`]. This call may block if the QR code is not yet
|
||||
/// ready.
|
||||
async fn get_backup_qr(&self, account_id: u32) -> Result<String> {
|
||||
let qr = self.inner_get_backup_qr(account_id).await?;
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let qr = ctx
|
||||
.backup_export_qr()
|
||||
.ok_or(anyhow!("no backup being exported"))?;
|
||||
qr::format_backup(&qr)
|
||||
}
|
||||
|
||||
@@ -1459,14 +1391,12 @@ impl CommandApi {
|
||||
/// This QR code can be used in [`CommandApi::get_backup`] on a second device to
|
||||
/// retrieve the backup and setup this second device.
|
||||
///
|
||||
/// This call will fail if there is currently no concurrent call to
|
||||
/// [`CommandApi::provide_backup`]. This call may block if the QR code is not yet
|
||||
/// ready.
|
||||
///
|
||||
/// Returns the QR code rendered as an SVG image.
|
||||
async fn get_backup_qr_svg(&self, account_id: u32) -> Result<String> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let qr = self.inner_get_backup_qr(account_id).await?;
|
||||
let qr = ctx
|
||||
.backup_export_qr()
|
||||
.ok_or(anyhow!("no backup being exported"))?;
|
||||
generate_backup_qr(&ctx, &qr).await
|
||||
}
|
||||
|
||||
@@ -1970,15 +1900,3 @@ async fn get_config(
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a QR code for a BackupProvider is currently available.
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone, Debug)]
|
||||
enum ProviderQr {
|
||||
/// There is no provider, asking for a QR is an error.
|
||||
NoProvider,
|
||||
/// There is a provider, the QR code is pending.
|
||||
Pending,
|
||||
/// There is a provider and QR code.
|
||||
Ready(Qr),
|
||||
}
|
||||
|
||||
@@ -55,5 +55,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "dist/deltachat.d.ts",
|
||||
"version": "1.112.2"
|
||||
}
|
||||
"version": "1.111.0"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-repl"
|
||||
version = "1.112.2"
|
||||
version = "1.111.0"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -21,9 +21,6 @@ deltachat_rpc_client = [
|
||||
[project.entry-points.pytest11]
|
||||
"deltachat_rpc_client.pytestplugin" = "deltachat_rpc_client.pytestplugin"
|
||||
|
||||
[tool.setuptools_scm]
|
||||
root = ".."
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ class Chat:
|
||||
"file": file,
|
||||
"location": location,
|
||||
"overrideSenderName": override_sender_name,
|
||||
"quotedMessageId": quoted_msg,
|
||||
"quotedMsg": quoted_msg,
|
||||
}
|
||||
msg_id = await self._rpc.send_msg(self.account.id, self.id, draft)
|
||||
return Message(self.account, msg_id)
|
||||
|
||||
@@ -31,7 +31,6 @@ class EventType(str, Enum):
|
||||
SMTP_MESSAGE_SENT = "SmtpMessageSent"
|
||||
IMAP_MESSAGE_DELETED = "ImapMessageDeleted"
|
||||
IMAP_MESSAGE_MOVED = "ImapMessageMoved"
|
||||
IMAP_INBOX_IDLE = "ImapInboxIdle"
|
||||
NEW_BLOB_FILE = "NewBlobFile"
|
||||
DELETED_BLOB_FILE = "DeletedBlobFile"
|
||||
WARNING = "Warning"
|
||||
|
||||
@@ -55,11 +55,6 @@ class ACFactory:
|
||||
async def get_online_account(self) -> Account:
|
||||
account = await self.new_configured_account()
|
||||
await account.start_io()
|
||||
while True:
|
||||
event = await account.wait_for_event()
|
||||
print(event)
|
||||
if event.type == EventType.IMAP_INBOX_IDLE:
|
||||
break
|
||||
return account
|
||||
|
||||
async def get_online_accounts(self, num: int) -> List[Account]:
|
||||
|
||||
@@ -15,7 +15,6 @@ passenv =
|
||||
deps =
|
||||
pytest
|
||||
pytest-asyncio
|
||||
pytest-timeout
|
||||
aiohttp
|
||||
aiodns
|
||||
|
||||
@@ -26,8 +25,5 @@ deps =
|
||||
ruff
|
||||
black
|
||||
commands =
|
||||
black --quiet --check --diff src/ examples/ tests/
|
||||
black --check --diff src/ examples/ tests/
|
||||
ruff src/ examples/ tests/
|
||||
|
||||
[pytest]
|
||||
timeout = 60
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.112.2"
|
||||
version = "1.111.0"
|
||||
description = "DeltaChat JSON-RPC server"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
@@ -9,9 +9,11 @@ license = "MPL-2.0"
|
||||
keywords = ["deltachat", "chat", "openpgp", "email", "encryption"]
|
||||
categories = ["cryptography", "std", "email"]
|
||||
|
||||
[[bin]]
|
||||
name = "deltachat-rpc-server"
|
||||
|
||||
[dependencies]
|
||||
deltachat-jsonrpc = { path = "../deltachat-jsonrpc", default-features = false }
|
||||
deltachat = { path = "..", default-features = false }
|
||||
|
||||
anyhow = "1"
|
||||
env_logger = { version = "0.10.0" }
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use std::env;
|
||||
///! Delta Chat core RPC server.
|
||||
///!
|
||||
///! It speaks JSON Lines over stdio.
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use deltachat::constants::DC_VERSION_STR;
|
||||
use anyhow::Result;
|
||||
use deltachat_jsonrpc::api::events::event_to_json_rpc_notification;
|
||||
use deltachat_jsonrpc::api::{Accounts, CommandApi};
|
||||
use futures_lite::stream::StreamExt;
|
||||
@@ -15,23 +13,6 @@ use yerpc::{RpcClient, RpcSession};
|
||||
|
||||
#[tokio::main(flavor = "multi_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
let mut args = env::args_os();
|
||||
let _program_name = args.next().context("no command line arguments found")?;
|
||||
if let Some(first_arg) = args.next() {
|
||||
if first_arg.to_str() == Some("--version") {
|
||||
if let Some(arg) = args.next() {
|
||||
return Err(anyhow!("Unrecognized argument {:?}", arg));
|
||||
}
|
||||
eprintln!("{}", &*DC_VERSION_STR);
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(anyhow!("Unrecognized option {:?}", first_arg));
|
||||
}
|
||||
}
|
||||
if let Some(arg) = args.next() {
|
||||
return Err(anyhow!("Unrecognized argument {:?}", arg));
|
||||
}
|
||||
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
||||
|
||||
let path = std::env::var("DC_ACCOUNTS_PATH").unwrap_or_else(|_| "accounts".to_string());
|
||||
24
deny.toml
24
deny.toml
@@ -11,13 +11,9 @@ ignore = [
|
||||
# Accept some duplicate versions, ideally we work towards this list
|
||||
# becoming empty. Adding versions forces us to revisit this at least
|
||||
# when upgrading.
|
||||
# Please keep this list alphabetically sorted.
|
||||
skip = [
|
||||
{ name = "base64", version = "<0.21" },
|
||||
{ name = "block-buffer", version = "<0.10" },
|
||||
{ name = "clap", version = "3.2.23" },
|
||||
{ name = "clap_lex", version = "0.2.4" },
|
||||
{ name = "convert_case", version = "0.4.0" },
|
||||
{ name = "darling", version = "<0.14" },
|
||||
{ name = "darling_core", version = "<0.14" },
|
||||
{ name = "darling_macro", version = "<0.14" },
|
||||
@@ -33,16 +29,24 @@ skip = [
|
||||
{ name = "rand_chacha", version = "<0.3" },
|
||||
{ name = "rand_core", version = "<0.6" },
|
||||
{ name = "sha2", version = "<0.10" },
|
||||
{ name = "spin", version = "<0.9.6" },
|
||||
{ name = "time", version = "<0.3" },
|
||||
{ name = "version_check", version = "<0.9" },
|
||||
{ name = "wasi", version = "<0.11" },
|
||||
{ name = "windows-sys", version = "<0.45" },
|
||||
{ name = "windows_aarch64_msvc", version = "<0.42" },
|
||||
{ name = "windows_i686_gnu", version = "<0.42" },
|
||||
{ name = "windows_i686_msvc", version = "<0.42" },
|
||||
{ name = "windows_x86_64_gnu", version = "<0.42" },
|
||||
{ name = "windows_x86_64_msvc", version = "<0.42" },
|
||||
{ name = "windows_x86_64_gnu", version = "<0.42" },
|
||||
{ name = "windows_i686_msvc", version = "<0.42" },
|
||||
{ name = "windows_i686_gnu", version = "<0.42" },
|
||||
{ name = "windows_aarch64_msvc", version = "<0.42" },
|
||||
{ name = "unicode-xid", version = "<0.2.4" },
|
||||
{ name = "syn", version = "<1.0" },
|
||||
{ name = "quote", version = "<1.0" },
|
||||
{ name = "proc-macro2", version = "<1.0" },
|
||||
{ name = "portable-atomic", version = "<1.0" },
|
||||
{ name = "spin", version = "<0.9.6" },
|
||||
{ name = "convert_case", version = "0.4.0" },
|
||||
{ name = "clap_lex", version = "0.2.4" },
|
||||
{ name = "clap", version = "3.2.23" },
|
||||
]
|
||||
|
||||
|
||||
@@ -74,5 +78,7 @@ license-files = [
|
||||
github = [
|
||||
"async-email",
|
||||
"deltachat",
|
||||
"n0-computer",
|
||||
"quinn-rs",
|
||||
"dignifiedquire",
|
||||
]
|
||||
|
||||
1287
fuzz/Cargo.lock
generated
1287
fuzz/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,6 @@ module.exports = {
|
||||
DC_EVENT_ERROR: 400,
|
||||
DC_EVENT_ERROR_SELF_NOT_IN_GROUP: 410,
|
||||
DC_EVENT_IMAP_CONNECTED: 102,
|
||||
DC_EVENT_IMAP_INBOX_IDLE: 106,
|
||||
DC_EVENT_IMAP_MESSAGE_DELETED: 104,
|
||||
DC_EVENT_IMAP_MESSAGE_MOVED: 105,
|
||||
DC_EVENT_IMEX_FILE_WRITTEN: 2052,
|
||||
@@ -113,7 +112,6 @@ module.exports = {
|
||||
DC_QR_ADDR: 320,
|
||||
DC_QR_ASK_VERIFYCONTACT: 200,
|
||||
DC_QR_ASK_VERIFYGROUP: 202,
|
||||
DC_QR_BACKUP: 251,
|
||||
DC_QR_ERROR: 400,
|
||||
DC_QR_FPR_MISMATCH: 220,
|
||||
DC_QR_FPR_OK: 210,
|
||||
@@ -151,7 +149,6 @@ module.exports = {
|
||||
DC_STR_AEAP_EXPLANATION_AND_LINK: 123,
|
||||
DC_STR_ARCHIVEDCHATS: 40,
|
||||
DC_STR_AUDIO: 11,
|
||||
DC_STR_BACKUP_TRANSFER_QR: 162,
|
||||
DC_STR_BAD_TIME_MSG_BODY: 85,
|
||||
DC_STR_BROADCAST_LIST: 115,
|
||||
DC_STR_CANNOT_LOGIN: 60,
|
||||
|
||||
@@ -8,7 +8,6 @@ module.exports = {
|
||||
103: 'DC_EVENT_SMTP_MESSAGE_SENT',
|
||||
104: 'DC_EVENT_IMAP_MESSAGE_DELETED',
|
||||
105: 'DC_EVENT_IMAP_MESSAGE_MOVED',
|
||||
106: 'DC_EVENT_IMAP_INBOX_IDLE',
|
||||
150: 'DC_EVENT_NEW_BLOB_FILE',
|
||||
151: 'DC_EVENT_DELETED_BLOB_FILE',
|
||||
300: 'DC_EVENT_WARNING',
|
||||
|
||||
@@ -37,7 +37,6 @@ export enum C {
|
||||
DC_EVENT_ERROR = 400,
|
||||
DC_EVENT_ERROR_SELF_NOT_IN_GROUP = 410,
|
||||
DC_EVENT_IMAP_CONNECTED = 102,
|
||||
DC_EVENT_IMAP_INBOX_IDLE = 106,
|
||||
DC_EVENT_IMAP_MESSAGE_DELETED = 104,
|
||||
DC_EVENT_IMAP_MESSAGE_MOVED = 105,
|
||||
DC_EVENT_IMEX_FILE_WRITTEN = 2052,
|
||||
@@ -113,7 +112,6 @@ export enum C {
|
||||
DC_QR_ADDR = 320,
|
||||
DC_QR_ASK_VERIFYCONTACT = 200,
|
||||
DC_QR_ASK_VERIFYGROUP = 202,
|
||||
DC_QR_BACKUP = 251,
|
||||
DC_QR_ERROR = 400,
|
||||
DC_QR_FPR_MISMATCH = 220,
|
||||
DC_QR_FPR_OK = 210,
|
||||
@@ -151,7 +149,6 @@ export enum C {
|
||||
DC_STR_AEAP_EXPLANATION_AND_LINK = 123,
|
||||
DC_STR_ARCHIVEDCHATS = 40,
|
||||
DC_STR_AUDIO = 11,
|
||||
DC_STR_BACKUP_TRANSFER_QR = 162,
|
||||
DC_STR_BAD_TIME_MSG_BODY = 85,
|
||||
DC_STR_BROADCAST_LIST = 115,
|
||||
DC_STR_CANNOT_LOGIN = 60,
|
||||
@@ -292,7 +289,6 @@ export const EventId2EventName: { [key: number]: string } = {
|
||||
103: 'DC_EVENT_SMTP_MESSAGE_SENT',
|
||||
104: 'DC_EVENT_IMAP_MESSAGE_DELETED',
|
||||
105: 'DC_EVENT_IMAP_MESSAGE_MOVED',
|
||||
106: 'DC_EVENT_IMAP_INBOX_IDLE',
|
||||
150: 'DC_EVENT_NEW_BLOB_FILE',
|
||||
151: 'DC_EVENT_DELETED_BLOB_FILE',
|
||||
300: 'DC_EVENT_WARNING',
|
||||
|
||||
@@ -60,5 +60,5 @@
|
||||
"test:mocha": "mocha -r esm node/test/test.js --growl --reporter=spec --bail --exit"
|
||||
},
|
||||
"types": "node/dist/index.d.ts",
|
||||
"version": "1.112.2"
|
||||
"version": "1.111.0"
|
||||
}
|
||||
@@ -190,7 +190,7 @@ class FFIEventTracker:
|
||||
- ac2 is still running FetchExsistingMsgs job and thinks it's an existing, old message
|
||||
- therefore no DC_EVENT_INCOMING_MSG is sent
|
||||
"""
|
||||
self.get_matching("DC_EVENT_IMAP_INBOX_IDLE")
|
||||
self.get_info_contains("INBOX: Idle entering")
|
||||
|
||||
def wait_next_incoming_message(self):
|
||||
"""wait for and return next incoming message."""
|
||||
|
||||
19
python/tests/auditwheels.py
Normal file
19
python/tests/auditwheels.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
assert len(sys.argv) == 2
|
||||
workspacedir = sys.argv[1]
|
||||
for relpath in os.listdir(workspacedir):
|
||||
if relpath.startswith("deltachat"):
|
||||
p = os.path.join(workspacedir, relpath)
|
||||
subprocess.check_call(
|
||||
[
|
||||
"auditwheel",
|
||||
"repair",
|
||||
p,
|
||||
"-w",
|
||||
workspacedir,
|
||||
],
|
||||
)
|
||||
@@ -220,16 +220,16 @@ def test_fetch_existing(acfactory, lp, mvbox_move):
|
||||
acfactory.bring_accounts_online()
|
||||
assert_folders_configured(ac1)
|
||||
|
||||
lp.sec("send out message with bcc to ourselves")
|
||||
ac1.set_config("bcc_self", "1")
|
||||
chat = acfactory.get_accepted_chat(ac1, ac2)
|
||||
chat.send_text("message text")
|
||||
assert ac1.direct_imap.select_config_folder("mvbox" if mvbox_move else "inbox")
|
||||
with ac1.direct_imap.idle() as idle1:
|
||||
lp.sec("send out message with bcc to ourselves")
|
||||
ac1.set_config("bcc_self", "1")
|
||||
chat = acfactory.get_accepted_chat(ac1, ac2)
|
||||
chat.send_text("message text")
|
||||
assert_folders_configured(ac1)
|
||||
|
||||
lp.sec("wait until the bcc_self message arrives in correct folder and is marked seen")
|
||||
if mvbox_move:
|
||||
ac1._evtracker.get_info_contains("Marked messages [0-9]+ in folder DeltaChat as seen.")
|
||||
else:
|
||||
ac1._evtracker.get_info_contains("Marked messages [0-9]+ in folder INBOX as seen.")
|
||||
lp.sec("wait until the bcc_self message arrives in correct folder and is marked seen")
|
||||
assert idle1.wait_for_seen()
|
||||
assert_folders_configured(ac1)
|
||||
|
||||
lp.sec("create a cloned ac1 and fetch contact history during configure")
|
||||
@@ -271,12 +271,12 @@ def test_fetch_existing_msgs_group_and_single(acfactory, lp):
|
||||
ac1._evtracker.wait_next_incoming_message()
|
||||
|
||||
lp.sec("send out message with bcc to ourselves")
|
||||
ac1.set_config("bcc_self", "1")
|
||||
ac1_ac2_chat = ac1.create_chat(ac2)
|
||||
ac1_ac2_chat.send_text("outgoing, encrypted direct message, creating a chat")
|
||||
|
||||
# wait until the bcc_self message arrives
|
||||
ac1._evtracker.get_info_contains("Marked messages [0-9]+ in folder INBOX as seen.")
|
||||
with ac1.direct_imap.idle() as idle1:
|
||||
ac1.set_config("bcc_self", "1")
|
||||
ac1_ac2_chat = ac1.create_chat(ac2)
|
||||
ac1_ac2_chat.send_text("outgoing, encrypted direct message, creating a chat")
|
||||
# wait until the bcc_self message arrives
|
||||
assert idle1.wait_for_seen()
|
||||
|
||||
lp.sec("Clone online account and let it fetch the existing messages")
|
||||
ac1_clone = acfactory.new_online_configuring_account(cloned_from=ac1)
|
||||
|
||||
@@ -134,27 +134,22 @@ def test_one_account_send_bcc_setting(acfactory, lp):
|
||||
ac1.set_config("bcc_self", "1")
|
||||
|
||||
lp.sec("send out message with bcc to ourselves")
|
||||
msg_out = chat.send_text("message2")
|
||||
with ac1.direct_imap.idle() as idle1:
|
||||
msg_out = chat.send_text("message2")
|
||||
|
||||
# wait for send out (BCC)
|
||||
ev = ac1._evtracker.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
|
||||
assert ac1.get_config("bcc_self") == "1"
|
||||
# wait for send out (BCC)
|
||||
ev = ac1._evtracker.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
|
||||
assert ac1.get_config("bcc_self") == "1"
|
||||
|
||||
# Second client receives only second message, but not the first.
|
||||
# now make sure we are sending message to ourselves too
|
||||
assert self_addr in ev.data2
|
||||
assert other_addr in ev.data2
|
||||
assert idle1.wait_for_seen()
|
||||
|
||||
# Second client receives only second message, but not the first
|
||||
ev_msg = ac1_clone._evtracker.wait_next_messages_changed()
|
||||
assert ev_msg.text == msg_out.text
|
||||
|
||||
# now make sure we are sending message to ourselves too
|
||||
assert self_addr in ev.data2
|
||||
assert other_addr in ev.data2
|
||||
|
||||
# BCC-self messages are marked as seen by the sender device.
|
||||
ac1._evtracker.get_info_contains("Marked messages [0-9]+ in folder INBOX as seen.")
|
||||
|
||||
# Check that the message is marked as seen on IMAP.
|
||||
ac1.direct_imap.select_folder("Inbox")
|
||||
assert len(list(ac1.direct_imap.conn.fetch(AND(seen=True)))) == 1
|
||||
|
||||
|
||||
def test_send_file_twice_unicode_filename_mangling(tmpdir, acfactory, lp):
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
@@ -319,9 +314,6 @@ def test_webxdc_message(acfactory, data, lp):
|
||||
assert msg2.text == "message1"
|
||||
assert msg2.is_webxdc()
|
||||
assert msg2.filename
|
||||
ac2._evtracker.get_info_contains("Marked messages [0-9]+ in folder INBOX as seen.")
|
||||
ac2.direct_imap.select_folder("Inbox")
|
||||
assert len(list(ac2.direct_imap.conn.fetch(AND(seen=True)))) == 1
|
||||
|
||||
|
||||
def test_mvbox_sentbox_threads(acfactory, lp):
|
||||
@@ -521,22 +513,22 @@ def test_send_and_receive_message_markseen(acfactory, lp):
|
||||
msg4 = ac2._evtracker.wait_next_incoming_message()
|
||||
|
||||
lp.sec("mark messages as seen on ac2, wait for changes on ac1")
|
||||
ac2.mark_seen_messages([msg2, msg4])
|
||||
ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_NOTICED")
|
||||
assert msg2.chat.id == msg4.chat.id
|
||||
assert ev.data1 == msg2.chat.id
|
||||
assert ev.data2 == 0
|
||||
ac2._evtracker.get_info_contains("Marked messages .* in folder INBOX as seen.")
|
||||
with ac1.direct_imap.idle() as idle1:
|
||||
with ac2.direct_imap.idle() as idle2:
|
||||
ac2.mark_seen_messages([msg2, msg4])
|
||||
ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_NOTICED")
|
||||
assert msg2.chat.id == msg4.chat.id
|
||||
assert ev.data1 == msg2.chat.id
|
||||
assert ev.data2 == 0
|
||||
idle2.wait_for_seen()
|
||||
|
||||
lp.step("1")
|
||||
for _i in range(2):
|
||||
ev = ac1._evtracker.get_matching("DC_EVENT_MSG_READ")
|
||||
assert ev.data1 > const.DC_CHAT_ID_LAST_SPECIAL
|
||||
assert ev.data2 > const.DC_MSG_ID_LAST_SPECIAL
|
||||
lp.step("2")
|
||||
|
||||
# Check that ac1 marks the read receipt as read.
|
||||
ac1._evtracker.get_info_contains("Marked messages .* in folder INBOX as seen.")
|
||||
lp.step("1")
|
||||
for _i in range(2):
|
||||
ev = ac1._evtracker.get_matching("DC_EVENT_MSG_READ")
|
||||
assert ev.data1 > const.DC_CHAT_ID_LAST_SPECIAL
|
||||
assert ev.data2 > const.DC_MSG_ID_LAST_SPECIAL
|
||||
lp.step("2")
|
||||
idle1.wait_for_seen() # Check that ac1 marks the read receipt as read
|
||||
|
||||
assert msg1.is_out_mdn_received()
|
||||
assert msg3.is_out_mdn_received()
|
||||
@@ -621,24 +613,18 @@ def test_markseen_message_and_mdn(acfactory, mvbox_move):
|
||||
# Do not send BCC to self, we only want to test MDN on ac1.
|
||||
ac1.set_config("bcc_self", "0")
|
||||
|
||||
acfactory.get_accepted_chat(ac1, ac2).send_text("hi")
|
||||
msg = ac2._evtracker.wait_next_incoming_message()
|
||||
|
||||
ac2.mark_seen_messages([msg])
|
||||
|
||||
folder = "mvbox" if mvbox_move else "inbox"
|
||||
for ac in [ac1, ac2]:
|
||||
if mvbox_move:
|
||||
ac._evtracker.get_info_contains("Marked messages [0-9]+ in folder DeltaChat as seen.")
|
||||
else:
|
||||
ac._evtracker.get_info_contains("Marked messages [0-9]+ in folder INBOX as seen.")
|
||||
ac1.direct_imap.select_config_folder(folder)
|
||||
ac2.direct_imap.select_config_folder(folder)
|
||||
with ac1.direct_imap.idle() as idle1:
|
||||
with ac2.direct_imap.idle() as idle2:
|
||||
acfactory.get_accepted_chat(ac1, ac2).send_text("hi")
|
||||
msg = ac2._evtracker.wait_next_incoming_message()
|
||||
|
||||
# Check that the mdn is marked as seen
|
||||
assert len(list(ac1.direct_imap.conn.fetch(AND(seen=True)))) == 1
|
||||
# Check original message is marked as seen
|
||||
assert len(list(ac2.direct_imap.conn.fetch(AND(seen=True)))) == 1
|
||||
ac2.mark_seen_messages([msg])
|
||||
|
||||
idle2.wait_for_seen() # Check original message is marked as seen
|
||||
idle1.wait_for_seen() # Check that the mdn is marked as seen
|
||||
|
||||
|
||||
def test_reply_privately(acfactory):
|
||||
@@ -692,24 +678,23 @@ def test_mdn_asymmetric(acfactory, lp):
|
||||
|
||||
assert len(msg.chat.get_messages()) == 1
|
||||
|
||||
lp.sec("ac2: mark incoming message as seen")
|
||||
ac2.mark_seen_messages([msg])
|
||||
ac1.direct_imap.select_config_folder("mvbox")
|
||||
with ac1.direct_imap.idle() as idle1:
|
||||
lp.sec("ac2: mark incoming message as seen")
|
||||
ac2.mark_seen_messages([msg])
|
||||
|
||||
lp.sec("ac1: waiting for incoming activity")
|
||||
# MDN should be moved even though MDNs are already disabled
|
||||
ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
|
||||
lp.sec("ac1: waiting for incoming activity")
|
||||
# MDN should be moved even though MDNs are already disabled
|
||||
ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
|
||||
|
||||
assert len(chat.get_messages()) == 1
|
||||
assert len(chat.get_messages()) == 1
|
||||
|
||||
# Wait for the message to be marked as seen on IMAP.
|
||||
ac1._evtracker.get_info_contains("Marked messages 1 in folder DeltaChat as seen.")
|
||||
# Wait for the message to be marked as seen on IMAP.
|
||||
assert idle1.wait_for_seen()
|
||||
|
||||
# MDN is received even though MDNs are already disabled
|
||||
assert msg_out.is_out_mdn_received()
|
||||
|
||||
ac1.direct_imap.select_config_folder("mvbox")
|
||||
assert len(list(ac1.direct_imap.conn.fetch(AND(seen=True)))) == 1
|
||||
|
||||
|
||||
def test_send_and_receive_will_encrypt_decrypt(acfactory, lp):
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
|
||||
@@ -33,6 +33,18 @@ passenv =
|
||||
CARGO_TARGET_DIR
|
||||
RUSTC_WRAPPER
|
||||
|
||||
[testenv:auditwheels]
|
||||
skipsdist = True
|
||||
deps = auditwheel
|
||||
passenv =
|
||||
DCC_RS_DEV
|
||||
DCC_RS_TARGET
|
||||
AUDITWHEEL_ARCH
|
||||
AUDITWHEEL_PLAT
|
||||
AUDITWHEEL_POLICY
|
||||
commands =
|
||||
python tests/auditwheels.py {toxworkdir}/wheelhouse
|
||||
|
||||
[testenv:lint]
|
||||
skipsdist = True
|
||||
skip_install = True
|
||||
@@ -43,7 +55,7 @@ deps =
|
||||
pygments
|
||||
restructuredtext_lint
|
||||
commands =
|
||||
black --quiet --check --diff setup.py install_python_bindings.py src/deltachat examples/ tests/
|
||||
black --check --diff setup.py install_python_bindings.py src/deltachat examples/ tests/
|
||||
ruff src/deltachat tests/ examples/
|
||||
rst-lint --encoding 'utf-8' README.rst
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@ and an own build machine.
|
||||
|
||||
- `clippy.sh` runs `cargo clippy` for all Rust code in the project.
|
||||
|
||||
- `deny.sh` runs `cargo deny` for all Rust code in the project.
|
||||
|
||||
- `../.github/workflows` contains jobs run by GitHub Actions.
|
||||
|
||||
- `remote_tests_python.sh` rsyncs to a build machine and runs
|
||||
|
||||
43
scripts/android-rpc-server.sh
Executable file
43
scripts/android-rpc-server.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
# Build deltachat-rpc-server for Android.
|
||||
|
||||
set -e
|
||||
|
||||
test -n "$ANDROID_NDK_ROOT" || exit 1
|
||||
|
||||
RUSTUP_TOOLCHAIN="1.64.0"
|
||||
rustup install "$RUSTUP_TOOLCHAIN"
|
||||
rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android --toolchain "$RUSTUP_TOOLCHAIN"
|
||||
|
||||
KERNEL="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
ARCH="$(uname -m)"
|
||||
NDK_HOST_TAG="$KERNEL-$ARCH"
|
||||
TOOLCHAIN="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$NDK_HOST_TAG"
|
||||
|
||||
PACKAGE="deltachat-rpc-server"
|
||||
|
||||
export CARGO_PROFILE_RELEASE_LTO=on
|
||||
|
||||
CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="$TOOLCHAIN/bin/armv7a-linux-androideabi16-clang" \
|
||||
TARGET_CC="$TOOLCHAIN/bin/armv7a-linux-androideabi16-clang" \
|
||||
TARGET_AR="$TOOLCHAIN/bin/llvm-ar" \
|
||||
TARGET_RANLIB="$TOOLCHAIN/bin/llvm-ranlib" \
|
||||
cargo "+$RUSTUP_TOOLCHAIN" rustc --release --target armv7-linux-androideabi -p $PACKAGE
|
||||
|
||||
CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$TOOLCHAIN/bin/aarch64-linux-android21-clang" \
|
||||
TARGET_CC="$TOOLCHAIN/bin/aarch64-linux-android21-clang" \
|
||||
TARGET_AR="$TOOLCHAIN/bin/llvm-ar" \
|
||||
TARGET_RANLIB="$TOOLCHAIN/bin/llvm-ranlib" \
|
||||
cargo "+$RUSTUP_TOOLCHAIN" rustc --release --target aarch64-linux-android -p $PACKAGE
|
||||
|
||||
CARGO_TARGET_I686_LINUX_ANDROID_LINKER="$TOOLCHAIN/bin/i686-linux-android16-clang" \
|
||||
TARGET_CC="$TOOLCHAIN/bin/i686-linux-android16-clang" \
|
||||
TARGET_AR="$TOOLCHAIN/bin/llvm-ar" \
|
||||
TARGET_RANLIB="$TOOLCHAIN/bin/llvm-ranlib" \
|
||||
cargo "+$RUSTUP_TOOLCHAIN" rustc --release --target i686-linux-android -p $PACKAGE
|
||||
|
||||
CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER="$TOOLCHAIN/bin/x86_64-linux-android21-clang" \
|
||||
TARGET_CC="$TOOLCHAIN/bin/x86_64-linux-android21-clang" \
|
||||
TARGET_AR="$TOOLCHAIN/bin/llvm-ar" \
|
||||
TARGET_RANLIB="$TOOLCHAIN/bin/llvm-ranlib" \
|
||||
cargo "+$RUSTUP_TOOLCHAIN" rustc --release --target x86_64-linux-android -p $PACKAGE
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
cargo deny --workspace --all-features check -D warnings
|
||||
@@ -31,9 +31,7 @@ unset DCC_NEW_TMP_EMAIL
|
||||
|
||||
# Try to build wheels for a range of interpreters, but don't fail if they are not available.
|
||||
# E.g. musllinux_1_1 does not have PyPy interpreters as of 2022-07-10
|
||||
tox --workdir "$TOXWORKDIR" -e py37,py38,py39,py310,py311,pypy37,pypy38,pypy39 --skip-missing-interpreters true
|
||||
|
||||
auditwheel repair "$TOXWORKDIR/wheelhouse/deltachat*" -w "$TOXWORKDIR/wheelhouse"
|
||||
tox --workdir "$TOXWORKDIR" -e py37,py38,py39,py310,py311,pypy37,pypy38,pypy39,auditwheels --skip-missing-interpreters true
|
||||
|
||||
|
||||
echo -----------------------
|
||||
|
||||
@@ -93,7 +93,7 @@ def main():
|
||||
if "alpha" not in newversion:
|
||||
for line in open("CHANGELOG.md"):
|
||||
## 1.25.0
|
||||
if line.startswith("## [") and line[4:].strip().startswith(newversion):
|
||||
if line.startswith("## ") and line[2:].strip().startswith(newversion):
|
||||
break
|
||||
else:
|
||||
raise SystemExit(
|
||||
|
||||
@@ -7,17 +7,17 @@ set -e
|
||||
|
||||
unset RUSTFLAGS
|
||||
|
||||
ZIG_VERSION=0.11.0-dev.2213+515e1c93e
|
||||
ZIG_VERSION=0.11.0-dev.1935+1d96a17af
|
||||
|
||||
# Download Zig
|
||||
rm -fr "$ZIG_VERSION" "zig-linux-x86_64-$ZIG_VERSION.tar.xz"
|
||||
rm -fr "$ZIG_VERSION" "ZIG_VERSION.tar.xz"
|
||||
wget "https://ziglang.org/builds/zig-linux-x86_64-$ZIG_VERSION.tar.xz"
|
||||
tar xf "zig-linux-x86_64-$ZIG_VERSION.tar.xz"
|
||||
export PATH="$PWD/zig-linux-x86_64-$ZIG_VERSION:$PATH"
|
||||
|
||||
cargo install cargo-zigbuild
|
||||
|
||||
for TARGET in x86_64-unknown-linux-musl aarch64-unknown-linux-musl armv7-unknown-linux-musleabihf; do
|
||||
for TARGET in aarch64-unknown-linux-musl armv7-unknown-linux-musleabihf; do
|
||||
rustup target add "$TARGET"
|
||||
cargo zigbuild --release --target "$TARGET" -p deltachat-rpc-server --features vendored
|
||||
done
|
||||
|
||||
@@ -262,7 +262,7 @@ impl Accounts {
|
||||
pub async fn stop_io(&self) {
|
||||
// Sending an event here wakes up event loop even
|
||||
// if there are no accounts.
|
||||
info!(self, "Stopping IO for all accounts.");
|
||||
info!(self, "Stopping IO for all accounts");
|
||||
for account in self.accounts.values() {
|
||||
account.stop_io().await;
|
||||
}
|
||||
|
||||
10
src/blob.rs
10
src/blob.rs
@@ -417,7 +417,7 @@ impl<'a> BlobObject<'a> {
|
||||
|
||||
info!(
|
||||
context,
|
||||
"Final scaled-down image size: {}B ({}px).",
|
||||
"Final scaled-down image size: {}B ({}px)",
|
||||
encoded.len(),
|
||||
img_wh
|
||||
);
|
||||
@@ -458,7 +458,7 @@ impl<'a> BlobObject<'a> {
|
||||
Some(3) => return Ok(180),
|
||||
Some(6) => return Ok(90),
|
||||
Some(8) => return Ok(270),
|
||||
other => warn!(context, "Exif orientation value ignored: {other:?}."),
|
||||
other => warn!(context, "exif orientation value ignored: {:?}", other),
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
@@ -490,7 +490,7 @@ impl<'a> BlobDirContents<'a> {
|
||||
match entry {
|
||||
Ok(entry) => Some(entry),
|
||||
Err(err) => {
|
||||
error!(context, "Failed to read blob file: {err}.");
|
||||
error!(context, "Failed to read blob file: {err}");
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -501,7 +501,7 @@ impl<'a> BlobDirContents<'a> {
|
||||
false => {
|
||||
warn!(
|
||||
context,
|
||||
"Export: Found blob dir entry {} that is not a file, ignoring.",
|
||||
"Export: Found blob dir entry {} that is not a file, ignoring",
|
||||
entry.path().display()
|
||||
);
|
||||
None
|
||||
@@ -569,7 +569,7 @@ fn encoded_img_exceeds_bytes(
|
||||
if encoded.len() > max_bytes {
|
||||
info!(
|
||||
context,
|
||||
"Image size {}B ({}x{}px) exceeds {}B, need to scale down.",
|
||||
"image size {}B ({}x{}px) exceeds {}B, need to scale down",
|
||||
encoded.len(),
|
||||
img.width(),
|
||||
img.height(),
|
||||
|
||||
60
src/chat.rs
60
src/chat.rs
@@ -247,7 +247,7 @@ impl ChatId {
|
||||
} else {
|
||||
warn!(
|
||||
context,
|
||||
"Cannot create chat, contact {contact_id} does not exist."
|
||||
"Cannot create chat, contact {} does not exist.", contact_id,
|
||||
);
|
||||
bail!("Can not create chat for non-existing contact");
|
||||
}
|
||||
@@ -285,7 +285,7 @@ impl ChatId {
|
||||
let chat_id = ChatId::new(u32::try_from(row_id)?);
|
||||
info!(
|
||||
context,
|
||||
"Created group/mailinglist '{}' grpid={} as {}, blocked={}.",
|
||||
"Created group/mailinglist '{}' grpid={} as {}, blocked={}",
|
||||
grpname,
|
||||
grpid,
|
||||
chat_id,
|
||||
@@ -338,14 +338,14 @@ impl ChatId {
|
||||
if contact_id != ContactId::SELF {
|
||||
info!(
|
||||
context,
|
||||
"Blocking the contact {contact_id} to block 1:1 chat."
|
||||
"Blocking the contact {} to block 1:1 chat", contact_id
|
||||
);
|
||||
Contact::block(context, contact_id).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Chattype::Group => {
|
||||
info!(context, "Can't block groups yet, deleting the chat.");
|
||||
info!(context, "Can't block groups yet, deleting the chat");
|
||||
self.delete(context).await?;
|
||||
}
|
||||
Chattype::Mailinglist => {
|
||||
@@ -500,7 +500,7 @@ impl ChatId {
|
||||
let chat = Chat::load_from_db(context, self).await?;
|
||||
|
||||
if let Err(e) = self.inner_set_protection(context, protect).await {
|
||||
error!(context, "Cannot set protection: {e:#}."); // make error user-visible
|
||||
error!(context, "Cannot set protection: {}", e); // make error user-visible
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
@@ -1070,7 +1070,7 @@ impl ChatId {
|
||||
);
|
||||
info!(
|
||||
context,
|
||||
"Set gossiped_timestamp for chat {} to {}.", self, timestamp,
|
||||
"set gossiped_timestamp for chat {} to {}.", self, timestamp,
|
||||
);
|
||||
|
||||
context
|
||||
@@ -1211,7 +1211,7 @@ impl Chat {
|
||||
Err(err) => {
|
||||
error!(
|
||||
context,
|
||||
"Failed to load contacts for {}: {:#}.", chat.id, err
|
||||
"failed to load contacts for {}: {:#}", chat.id, err
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2032,11 +2032,8 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
|
||||
.with_context(|| format!("attachment missing for message of type #{}", msg.viewtype))?;
|
||||
|
||||
if msg.viewtype == Viewtype::Image {
|
||||
if let Err(err) = blob.recode_to_image_size(context).await {
|
||||
warn!(
|
||||
context,
|
||||
"Cannot recode image, using original data: {err:#}."
|
||||
);
|
||||
if let Err(e) = blob.recode_to_image_size(context).await {
|
||||
warn!(context, "Cannot recode image, using original data: {:?}", e);
|
||||
}
|
||||
}
|
||||
msg.param.set(Param::File, blob.as_name());
|
||||
@@ -2259,7 +2256,7 @@ async fn create_send_msg_job(context: &Context, msg_id: MsgId) -> Result<Option<
|
||||
let attach_selfavatar = match shall_attach_selfavatar(context, msg.chat_id).await {
|
||||
Ok(attach_selfavatar) => attach_selfavatar,
|
||||
Err(err) => {
|
||||
warn!(context, "SMTP job cannot get selfavatar-state: {err:#}.");
|
||||
warn!(context, "job: cannot get selfavatar-state: {:#}", err);
|
||||
false
|
||||
}
|
||||
};
|
||||
@@ -2286,7 +2283,7 @@ async fn create_send_msg_job(context: &Context, msg_id: MsgId) -> Result<Option<
|
||||
// may happen eg. for groups with only SELF and bcc_self disabled
|
||||
info!(
|
||||
context,
|
||||
"Message {msg_id} has no recipient, skipping smtp-send."
|
||||
"message {} has no recipient, skipping smtp-send", msg_id
|
||||
);
|
||||
msg_id.set_delivered(context).await?;
|
||||
return Ok(None);
|
||||
@@ -2321,27 +2318,27 @@ async fn create_send_msg_job(context: &Context, msg_id: MsgId) -> Result<Option<
|
||||
|
||||
if 0 != rendered_msg.last_added_location_id {
|
||||
if let Err(err) = location::set_kml_sent_timestamp(context, msg.chat_id, time()).await {
|
||||
error!(context, "Failed to set kml sent_timestamp: {err:#}.");
|
||||
error!(context, "Failed to set kml sent_timestamp: {:#}", err);
|
||||
}
|
||||
if !msg.hidden {
|
||||
if let Err(err) =
|
||||
location::set_msg_location_id(context, msg.id, rendered_msg.last_added_location_id)
|
||||
.await
|
||||
{
|
||||
error!(context, "Failed to set msg_location_id: {err:#}.");
|
||||
error!(context, "Failed to set msg_location_id: {:#}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(sync_ids) = rendered_msg.sync_ids_to_delete {
|
||||
if let Err(err) = context.delete_sync_ids(sync_ids).await {
|
||||
error!(context, "Failed to delete sync ids: {err:#}.");
|
||||
error!(context, "Failed to delete sync ids: {:#}", err);
|
||||
}
|
||||
}
|
||||
|
||||
if attach_selfavatar {
|
||||
if let Err(err) = msg.chat_id.set_selfavatar_timestamp(context, time()).await {
|
||||
error!(context, "Failed to set selfavatar timestamp: {err:#}.");
|
||||
error!(context, "Failed to set selfavatar timestamp: {:#}", err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2696,7 +2693,8 @@ pub(crate) async fn mark_old_messages_as_noticed(
|
||||
if !changed_chats.is_empty() {
|
||||
info!(
|
||||
context,
|
||||
"Marking chats as noticed because there are newer outgoing messages: {changed_chats:?}."
|
||||
"Marking chats as noticed because there are newer outgoing messages: {:?}",
|
||||
changed_chats
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2727,14 +2725,12 @@ pub async fn get_chat_media(
|
||||
"SELECT id
|
||||
FROM msgs
|
||||
WHERE (1=? OR chat_id=?)
|
||||
AND chat_id != ?
|
||||
AND (type=? OR type=? OR type=?)
|
||||
AND hidden=0
|
||||
ORDER BY timestamp, id;",
|
||||
paramsv![
|
||||
chat_id.is_none(),
|
||||
chat_id.unwrap_or_else(|| ChatId::new(0)),
|
||||
DC_CHAT_ID_TRASH,
|
||||
msg_type,
|
||||
if msg_type2 != Viewtype::Unknown {
|
||||
msg_type2
|
||||
@@ -3019,7 +3015,7 @@ pub(crate) async fn add_contact_to_chat_ex(
|
||||
// if SELF is not in the group, members cannot be added at all.
|
||||
warn!(
|
||||
context,
|
||||
"Invalid attempt to add self e-mail address to group."
|
||||
"invalid attempt to add self e-mail address to group"
|
||||
);
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -3566,7 +3562,7 @@ pub async fn add_device_msg_with_importance(
|
||||
|
||||
if let Some(label) = label {
|
||||
if was_device_msg_ever_added(context, label).await? {
|
||||
info!(context, "Device-message {label} already added.");
|
||||
info!(context, "device-message {} already added", label);
|
||||
return Ok(msg_id);
|
||||
}
|
||||
}
|
||||
@@ -3797,7 +3793,6 @@ mod tests {
|
||||
use crate::chatlist::{get_archived_cnt, Chatlist};
|
||||
use crate::constants::{DC_GCL_ARCHIVED_ONLY, DC_GCL_NO_SPECIALS};
|
||||
use crate::contact::{Contact, ContactAddress};
|
||||
use crate::message::delete_msgs;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
@@ -5980,7 +5975,7 @@ mod tests {
|
||||
include_bytes!("../test-data/image/avatar64x64.png"),
|
||||
)
|
||||
.await?;
|
||||
let second_image_msg_id = send_media(
|
||||
send_media(
|
||||
&t,
|
||||
chat_id2,
|
||||
Viewtype::Image,
|
||||
@@ -6082,21 +6077,6 @@ mod tests {
|
||||
4
|
||||
);
|
||||
|
||||
// Delete an image.
|
||||
delete_msgs(&t, &[second_image_msg_id]).await?;
|
||||
assert_eq!(
|
||||
get_chat_media(
|
||||
&t,
|
||||
None,
|
||||
Viewtype::Image,
|
||||
Viewtype::Sticker,
|
||||
Viewtype::Webxdc,
|
||||
)
|
||||
.await?
|
||||
.len(),
|
||||
3
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ impl Chatlist {
|
||||
// allow searching over special names that may change at any time
|
||||
// when the ui calls set_stock_translation()
|
||||
if let Err(err) = update_special_chat_names(context).await {
|
||||
warn!(context, "Cannot update special chat names: {err:#}.")
|
||||
warn!(context, "cannot update special chat names: {:?}", err)
|
||||
}
|
||||
|
||||
let str_like_cmd = format!("%{query}%");
|
||||
|
||||
@@ -23,6 +23,7 @@ use crate::events::{Event, EventEmitter, EventType, Events};
|
||||
use crate::key::{DcKey, SignedPublicKey};
|
||||
use crate::login_param::LoginParam;
|
||||
use crate::message::{self, MessageState, MsgId};
|
||||
use crate::qr::Qr;
|
||||
use crate::quota::QuotaInfo;
|
||||
use crate::scheduler::SchedulerState;
|
||||
use crate::sql::Sql;
|
||||
@@ -240,6 +241,14 @@ pub struct InnerContext {
|
||||
|
||||
/// If debug logging is enabled, this contains all necessary information
|
||||
pub(crate) debug_logging: RwLock<Option<DebugLogging>>,
|
||||
|
||||
/// QR code for currently running [`BackupProvider`].
|
||||
///
|
||||
/// This is only available if a backup export is currently running, it will also be
|
||||
/// holding the ongoing process while running.
|
||||
///
|
||||
/// [`BackupProvider`]: crate::imex::BackupProvider
|
||||
pub(crate) export_provider: std::sync::Mutex<Option<Qr>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -384,6 +393,7 @@ impl Context {
|
||||
last_full_folder_scan: Mutex::new(None),
|
||||
last_error: std::sync::RwLock::new("".to_string()),
|
||||
debug_logging: RwLock::new(None),
|
||||
export_provider: std::sync::Mutex::new(None),
|
||||
};
|
||||
|
||||
let ctx = Context {
|
||||
@@ -558,6 +568,17 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the QR-code of the currently running [`BackupProvider`].
|
||||
///
|
||||
/// [`BackupProvider`]: crate::imex::BackupProvider
|
||||
pub fn backup_export_qr(&self) -> Option<Qr> {
|
||||
self.export_provider
|
||||
.lock()
|
||||
.expect("poisoned lock")
|
||||
.as_ref()
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* UI chat/message related API
|
||||
******************************************************************************/
|
||||
|
||||
@@ -133,9 +133,6 @@ pub enum EventType {
|
||||
/// Emitted when an IMAP message has been moved
|
||||
ImapMessageMoved(String),
|
||||
|
||||
/// Emitted before going into IDLE on the Inbox folder.
|
||||
ImapInboxIdle,
|
||||
|
||||
/// Emitted when an new file in the $BLOBDIR was created
|
||||
NewBlobFile(String),
|
||||
|
||||
|
||||
@@ -91,13 +91,15 @@ pub async fn imex(
|
||||
let cancel = context.alloc_ongoing().await?;
|
||||
|
||||
let res = {
|
||||
let _guard = context.scheduler.pause(context.clone()).await;
|
||||
imex_inner(context, what, path, passphrase)
|
||||
let mut guard = context.scheduler.pause(context).await;
|
||||
let res = imex_inner(context, what, path, passphrase)
|
||||
.race(async {
|
||||
cancel.recv().await.ok();
|
||||
Err(format_err!("canceled"))
|
||||
})
|
||||
.await
|
||||
.await;
|
||||
guard.resume().await;
|
||||
res
|
||||
};
|
||||
context.free_ongoing().await;
|
||||
|
||||
|
||||
@@ -32,8 +32,7 @@ use std::task::Poll;
|
||||
use anyhow::{anyhow, bail, ensure, format_err, Context as _, Result};
|
||||
use async_channel::Receiver;
|
||||
use futures_lite::StreamExt;
|
||||
use iroh::blobs::Collection;
|
||||
use iroh::get::DataStream;
|
||||
use iroh::get::{DataStream, Options};
|
||||
use iroh::progress::ProgressEmitter;
|
||||
use iroh::protocol::AuthToken;
|
||||
use iroh::provider::{DataSource, Event, Provider, Ticket};
|
||||
@@ -44,7 +43,6 @@ use tokio::sync::broadcast::error::RecvError;
|
||||
use tokio::sync::{broadcast, Mutex};
|
||||
use tokio::task::{JoinHandle, JoinSet};
|
||||
use tokio_stream::wrappers::ReadDirStream;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::blob::BlobDirContents;
|
||||
use crate::chat::delete_and_reset_all_device_msgs;
|
||||
@@ -54,8 +52,6 @@ use crate::{e2ee, EventType};
|
||||
|
||||
use super::{export_database, DBFILE_BACKUP_NAME};
|
||||
|
||||
const MAX_CONCURRENT_DIALS: u8 = 16;
|
||||
|
||||
/// Provide or send a backup of this device.
|
||||
///
|
||||
/// This creates a backup of the current device and starts a service which offers another
|
||||
@@ -75,8 +71,6 @@ pub struct BackupProvider {
|
||||
handle: JoinHandle<Result<()>>,
|
||||
/// The ticket to retrieve the backup collection.
|
||||
ticket: Ticket,
|
||||
/// Guard to cancel the provider on drop.
|
||||
_drop_guard: tokio_util::sync::DropGuard,
|
||||
}
|
||||
|
||||
impl BackupProvider {
|
||||
@@ -97,7 +91,6 @@ impl BackupProvider {
|
||||
|
||||
// Acquire global "ongoing" mutex.
|
||||
let cancel_token = context.alloc_ongoing().await?;
|
||||
let paused_guard = context.scheduler.pause(context.clone()).await;
|
||||
let context_dir = context
|
||||
.get_blobdir()
|
||||
.parent()
|
||||
@@ -128,25 +121,16 @@ impl BackupProvider {
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
let drop_token = CancellationToken::new();
|
||||
let handle = {
|
||||
let context = context.clone();
|
||||
let drop_token = drop_token.clone();
|
||||
tokio::spawn(async move {
|
||||
let res = Self::watch_provider(&context, provider, cancel_token, drop_token).await;
|
||||
context.free_ongoing().await;
|
||||
|
||||
// Explicit drop to move the guards into this future
|
||||
drop(paused_guard);
|
||||
drop(dbfile);
|
||||
res
|
||||
})
|
||||
};
|
||||
Ok(Self {
|
||||
handle,
|
||||
ticket,
|
||||
_drop_guard: drop_token.drop_guard(),
|
||||
})
|
||||
let handle = tokio::spawn(Self::watch_provider(
|
||||
context.clone(),
|
||||
provider,
|
||||
cancel_token,
|
||||
dbfile,
|
||||
));
|
||||
let slf = Self { handle, ticket };
|
||||
let qr = slf.qr();
|
||||
*context.export_provider.lock().expect("poisoned lock") = Some(qr);
|
||||
Ok(slf)
|
||||
}
|
||||
|
||||
/// Creates the provider task.
|
||||
@@ -182,7 +166,7 @@ impl BackupProvider {
|
||||
.spawn()?;
|
||||
context.emit_event(SendProgress::ProviderListening.into());
|
||||
info!(context, "Waiting for remote to connect");
|
||||
let ticket = provider.ticket(hash)?;
|
||||
let ticket = provider.ticket(hash);
|
||||
Ok((provider, ticket))
|
||||
}
|
||||
|
||||
@@ -197,11 +181,12 @@ impl BackupProvider {
|
||||
/// The *cancel_token* is the handle for the ongoing process mutex, when this completes
|
||||
/// we must cancel this operation.
|
||||
async fn watch_provider(
|
||||
context: &Context,
|
||||
context: Context,
|
||||
mut provider: Provider,
|
||||
cancel_token: Receiver<()>,
|
||||
drop_token: CancellationToken,
|
||||
_dbfile: TempPathGuard,
|
||||
) -> Result<()> {
|
||||
// _dbfile exists so we can clean up the file once it is no longer needed
|
||||
let mut events = provider.subscribe();
|
||||
let mut total_size = 0;
|
||||
let mut current_size = 0;
|
||||
@@ -261,14 +246,15 @@ impl BackupProvider {
|
||||
},
|
||||
_ = cancel_token.recv() => {
|
||||
provider.shutdown();
|
||||
break Err(anyhow!("BackupProvider cancelled"));
|
||||
break Err(anyhow!("BackupSender cancelled"));
|
||||
},
|
||||
_ = drop_token.cancelled() => {
|
||||
provider.shutdown();
|
||||
break Err(anyhow!("BackupProvider dropped"));
|
||||
}
|
||||
}
|
||||
};
|
||||
context
|
||||
.export_provider
|
||||
.lock()
|
||||
.expect("poisoned lock")
|
||||
.take();
|
||||
match &res {
|
||||
Ok(_) => context.emit_event(SendProgress::Completed.into()),
|
||||
Err(err) => {
|
||||
@@ -276,6 +262,7 @@ impl BackupProvider {
|
||||
context.emit_event(SendProgress::Failed.into())
|
||||
}
|
||||
}
|
||||
context.free_ongoing().await;
|
||||
res
|
||||
}
|
||||
|
||||
@@ -386,7 +373,7 @@ pub async fn get_backup(context: &Context, qr: Qr) -> Result<()> {
|
||||
!context.is_configured().await?,
|
||||
"Cannot import backups to accounts in use."
|
||||
);
|
||||
let _guard = context.scheduler.pause(context.clone()).await;
|
||||
let mut guard = context.scheduler.pause(context).await;
|
||||
|
||||
// Acquire global "ongoing" mutex.
|
||||
let cancel_token = context.alloc_ongoing().await?;
|
||||
@@ -398,6 +385,7 @@ pub async fn get_backup(context: &Context, qr: Qr) -> Result<()> {
|
||||
}
|
||||
_ = cancel_token.recv() => Err(format_err!("cancelled")),
|
||||
};
|
||||
guard.resume().await;
|
||||
res
|
||||
}
|
||||
|
||||
@@ -406,67 +394,104 @@ async fn get_backup_inner(context: &Context, qr: Qr) -> Result<()> {
|
||||
Qr::Backup { ticket } => ticket,
|
||||
_ => bail!("QR code for backup must be of type DCBACKUP"),
|
||||
};
|
||||
|
||||
match transfer_from_provider(context, &ticket).await {
|
||||
Ok(()) => {
|
||||
delete_and_reset_all_device_msgs(context).await?;
|
||||
context.emit_event(ReceiveProgress::Completed.into());
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
// Clean up any blobs we already wrote.
|
||||
let readdir = fs::read_dir(context.get_blobdir()).await?;
|
||||
let mut readdir = ReadDirStream::new(readdir);
|
||||
while let Some(dirent) = readdir.next().await {
|
||||
if let Ok(dirent) = dirent {
|
||||
fs::remove_file(dirent.path()).await.ok();
|
||||
}
|
||||
if ticket.addrs.is_empty() {
|
||||
bail!("ticket is missing addresses to dial");
|
||||
}
|
||||
for addr in &ticket.addrs {
|
||||
let opts = Options {
|
||||
addr: *addr,
|
||||
peer_id: Some(ticket.peer),
|
||||
keylog: false,
|
||||
};
|
||||
info!(context, "attempting to contact {}", addr);
|
||||
match transfer_from_provider(context, &ticket, opts).await {
|
||||
Ok(_) => {
|
||||
delete_and_reset_all_device_msgs(context).await?;
|
||||
context.emit_event(ReceiveProgress::Completed.into());
|
||||
return Ok(());
|
||||
}
|
||||
Err(TransferError::ConnectionError(err)) => {
|
||||
warn!(context, "Connection error: {err:#}.");
|
||||
continue;
|
||||
}
|
||||
Err(TransferError::Other(err)) => {
|
||||
// Clean up any blobs we already wrote.
|
||||
let readdir = fs::read_dir(context.get_blobdir()).await?;
|
||||
let mut readdir = ReadDirStream::new(readdir);
|
||||
while let Some(dirent) = readdir.next().await {
|
||||
if let Ok(dirent) = dirent {
|
||||
fs::remove_file(dirent.path()).await.ok();
|
||||
}
|
||||
}
|
||||
context.emit_event(ReceiveProgress::Failed.into());
|
||||
return Err(err);
|
||||
}
|
||||
context.emit_event(ReceiveProgress::Failed.into());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
Err(anyhow!("failed to contact provider"))
|
||||
}
|
||||
|
||||
async fn transfer_from_provider(context: &Context, ticket: &Ticket) -> Result<()> {
|
||||
/// Error during a single transfer attempt.
|
||||
///
|
||||
/// Mostly exists to distinguish between `ConnectionError` and any other errors.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum TransferError {
|
||||
#[error("connection error")]
|
||||
ConnectionError(#[source] anyhow::Error),
|
||||
#[error("other")]
|
||||
Other(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
async fn transfer_from_provider(
|
||||
context: &Context,
|
||||
ticket: &Ticket,
|
||||
opts: Options,
|
||||
) -> Result<(), TransferError> {
|
||||
let progress = ProgressEmitter::new(0, ReceiveProgress::max_blob_progress());
|
||||
spawn_progress_proxy(context.clone(), progress.subscribe());
|
||||
let mut connected = false;
|
||||
let on_connected = || {
|
||||
context.emit_event(ReceiveProgress::Connected.into());
|
||||
async { Ok(()) }
|
||||
};
|
||||
let on_collection = |collection: &Collection| {
|
||||
context.emit_event(ReceiveProgress::CollectionReceived.into());
|
||||
progress.set_total(collection.total_blobs_size());
|
||||
connected = true;
|
||||
async { Ok(()) }
|
||||
};
|
||||
let jobs = Mutex::new(JoinSet::default());
|
||||
let on_blob =
|
||||
|hash, reader, name| on_blob(context, &progress, &jobs, ticket, hash, reader, name);
|
||||
|
||||
// Perform the transfer.
|
||||
let keylog = false; // Do not enable rustls SSLKEYLOGFILE env var functionality
|
||||
let stats = iroh::get::run_ticket(
|
||||
ticket,
|
||||
keylog,
|
||||
MAX_CONCURRENT_DIALS,
|
||||
let res = iroh::get::run(
|
||||
ticket.hash,
|
||||
ticket.token,
|
||||
opts,
|
||||
on_connected,
|
||||
on_collection,
|
||||
|collection| {
|
||||
context.emit_event(ReceiveProgress::CollectionRecieved.into());
|
||||
progress.set_total(collection.total_blobs_size());
|
||||
async { Ok(()) }
|
||||
},
|
||||
on_blob,
|
||||
)
|
||||
.await?;
|
||||
.await;
|
||||
|
||||
let mut jobs = jobs.lock().await;
|
||||
while let Some(job) = jobs.join_next().await {
|
||||
job.context("job failed")?;
|
||||
job.context("job failed").map_err(TransferError::Other)?;
|
||||
}
|
||||
|
||||
drop(progress);
|
||||
info!(
|
||||
context,
|
||||
"Backup transfer finished, transfer rate was {} Mbps.",
|
||||
stats.mbits()
|
||||
);
|
||||
Ok(())
|
||||
match res {
|
||||
Ok(stats) => {
|
||||
info!(
|
||||
context,
|
||||
"Backup transfer finished, transfer rate is {} Mbps.",
|
||||
stats.mbits()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => match connected {
|
||||
true => Err(TransferError::Other(err)),
|
||||
false => Err(TransferError::ConnectionError(err)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get callback when a blob is received from the provider.
|
||||
@@ -508,7 +533,7 @@ async fn on_blob(
|
||||
|
||||
if name.starts_with("db/") {
|
||||
let context = context.clone();
|
||||
let token = ticket.token().to_string();
|
||||
let token = ticket.token.to_string();
|
||||
jobs.lock().await.spawn(async move {
|
||||
if let Err(err) = context.sql.import(&path, token).await {
|
||||
error!(context, "cannot import database: {:#?}", err);
|
||||
@@ -550,7 +575,7 @@ fn spawn_progress_proxy(context: Context, mut rx: broadcast::Receiver<u16>) {
|
||||
#[derive(Debug)]
|
||||
enum ReceiveProgress {
|
||||
Connected,
|
||||
CollectionReceived,
|
||||
CollectionRecieved,
|
||||
/// A value between 0 and 85 interpreted as a percentage.
|
||||
///
|
||||
/// Other values are already used by the other variants of this enum.
|
||||
@@ -572,7 +597,7 @@ impl From<ReceiveProgress> for EventType {
|
||||
fn from(source: ReceiveProgress) -> Self {
|
||||
let val = match source {
|
||||
ReceiveProgress::Connected => 50,
|
||||
ReceiveProgress::CollectionReceived => 100,
|
||||
ReceiveProgress::CollectionRecieved => 100,
|
||||
ReceiveProgress::BlobProgress(val) => 100 + 10 * val,
|
||||
ReceiveProgress::Completed => 1000,
|
||||
ReceiveProgress::Failed => 0,
|
||||
@@ -670,16 +695,4 @@ mod tests {
|
||||
assert_eq!(out, EventType::ImexProgress(progress));
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_drop_provider() {
|
||||
let mut tcm = TestContextManager::new();
|
||||
let ctx = tcm.alice().await;
|
||||
|
||||
let provider = BackupProvider::prepare(&ctx).await.unwrap();
|
||||
drop(provider);
|
||||
ctx.evtracker
|
||||
.get_matching(|ev| matches!(ev, EventType::ImexProgress(0)))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
31
src/job.rs
31
src/job.rs
@@ -153,7 +153,7 @@ impl<'a> Connection<'a> {
|
||||
}
|
||||
|
||||
pub(crate) async fn perform_job(context: &Context, mut connection: Connection<'_>, mut job: Job) {
|
||||
info!(context, "Job {} started...", &job);
|
||||
info!(context, "job {} started...", &job);
|
||||
|
||||
let try_res = match perform_job_action(context, &mut job, &mut connection, 0).await {
|
||||
Status::RetryNow => perform_job_action(context, &mut job, &mut connection, 1).await,
|
||||
@@ -165,7 +165,7 @@ pub(crate) async fn perform_job(context: &Context, mut connection: Connection<'_
|
||||
let tries = job.tries + 1;
|
||||
|
||||
if tries < JOB_RETRIES {
|
||||
info!(context, "Increase job {job} tries to {tries}.");
|
||||
info!(context, "increase job {} tries to {}", job, tries);
|
||||
job.tries = tries;
|
||||
let time_offset = get_backoff_time_offset(tries);
|
||||
job.desired_timestamp = time() + time_offset;
|
||||
@@ -177,23 +177,26 @@ pub(crate) async fn perform_job(context: &Context, mut connection: Connection<'_
|
||||
time_offset
|
||||
);
|
||||
job.save(context).await.unwrap_or_else(|err| {
|
||||
error!(context, "Failed to save job: {err:#}.");
|
||||
error!(context, "failed to save job: {:#}", err);
|
||||
});
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"Remove job {job} as it exhausted {JOB_RETRIES} retries."
|
||||
"remove job {} as it exhausted {} retries", job, JOB_RETRIES
|
||||
);
|
||||
job.delete(context).await.unwrap_or_else(|err| {
|
||||
error!(context, "Failed to delete job: {err:#}.");
|
||||
error!(context, "failed to delete job: {:#}", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
Status::Finished(res) => {
|
||||
if let Err(err) = res {
|
||||
warn!(context, "Remove job {job} as it failed with error {err:#}.");
|
||||
warn!(
|
||||
context,
|
||||
"remove job {} as it failed with error {:#}", job, err
|
||||
);
|
||||
} else {
|
||||
info!(context, "Remove job {job} as it succeeded.");
|
||||
info!(context, "remove job {} as it succeeded", job);
|
||||
}
|
||||
|
||||
job.delete(context).await.unwrap_or_else(|err| {
|
||||
@@ -209,13 +212,13 @@ async fn perform_job_action(
|
||||
connection: &mut Connection<'_>,
|
||||
tries: u32,
|
||||
) -> Status {
|
||||
info!(context, "Begin immediate try {tries} of job {job}.");
|
||||
info!(context, "begin immediate try {} of job {}", tries, job);
|
||||
|
||||
let try_res = match job.action {
|
||||
Action::DownloadMsg => job.download_msg(context, connection.inbox()).await,
|
||||
};
|
||||
|
||||
info!(context, "Finished immediate try {tries} of job {job}.");
|
||||
info!(context, "Finished immediate try {} of job {}", tries, job);
|
||||
|
||||
try_res
|
||||
}
|
||||
@@ -247,7 +250,7 @@ pub(crate) async fn schedule_resync(context: &Context) -> Result<()> {
|
||||
pub async fn add(context: &Context, job: Job) -> Result<()> {
|
||||
job.save(context).await.context("failed to save job")?;
|
||||
|
||||
info!(context, "Interrupt: IMAP.");
|
||||
info!(context, "interrupt: imap");
|
||||
context
|
||||
.scheduler
|
||||
.interrupt_inbox(InterruptInfo::new(false))
|
||||
@@ -261,7 +264,7 @@ pub async fn add(context: &Context, job: Job) -> Result<()> {
|
||||
/// jobs, this is tricky and probably wrong currently. Look at the
|
||||
/// SQL queries for details.
|
||||
pub(crate) async fn load_next(context: &Context, info: &InterruptInfo) -> Result<Option<Job>> {
|
||||
info!(context, "Loading job.");
|
||||
info!(context, "loading job");
|
||||
|
||||
let query;
|
||||
let params;
|
||||
@@ -313,19 +316,19 @@ LIMIT 1;
|
||||
Ok(job) => return Ok(job),
|
||||
Err(err) => {
|
||||
// Remove invalid job from the DB
|
||||
info!(context, "Cleaning up job, because of {err:#}.");
|
||||
info!(context, "cleaning up job, because of {:#}", err);
|
||||
|
||||
// TODO: improve by only doing a single query
|
||||
let id = context
|
||||
.sql
|
||||
.query_row(query, params.clone(), |row| row.get::<_, i32>(0))
|
||||
.await
|
||||
.context("failed to retrieve invalid job ID from the database")?;
|
||||
.context("Failed to retrieve invalid job ID from the database")?;
|
||||
context
|
||||
.sql
|
||||
.execute("DELETE FROM jobs WHERE id=?;", paramsv![id])
|
||||
.await
|
||||
.with_context(|| format!("failed to delete invalid job {id}"))?;
|
||||
.with_context(|| format!("Failed to delete invalid job {id}"))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ impl<T, E: std::fmt::Display> LogExt<T, E> for Result<T, E> {
|
||||
);
|
||||
// We can't use the warn!() macro here as the file!() and line!() macros
|
||||
// don't work with #[track_caller]
|
||||
context.set_last_error(&full);
|
||||
context.emit_event(crate::EventType::Warning(full));
|
||||
};
|
||||
self
|
||||
|
||||
@@ -1976,10 +1976,10 @@ pub(crate) fn get_list_post(headers: &[MailHeader]) -> Option<String> {
|
||||
.map(|s| s.addr)
|
||||
}
|
||||
|
||||
fn get_all_addresses_from_header(
|
||||
headers: &[MailHeader],
|
||||
pred: fn(String) -> bool,
|
||||
) -> Vec<SingleInfo> {
|
||||
fn get_all_addresses_from_header<F>(headers: &[MailHeader], pred: F) -> Vec<SingleInfo>
|
||||
where
|
||||
F: Fn(String) -> bool,
|
||||
{
|
||||
let mut result: Vec<SingleInfo> = Default::default();
|
||||
|
||||
headers
|
||||
|
||||
@@ -96,7 +96,7 @@ pub(crate) async fn receive_imf_inner(
|
||||
is_partial_download: Option<u32>,
|
||||
fetching_existing_messages: bool,
|
||||
) -> Result<Option<ReceivedMsg>> {
|
||||
info!(context, "Receiving message, seen={seen}...");
|
||||
info!(context, "Receiving message, seen={}...", seen);
|
||||
|
||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||
info!(
|
||||
@@ -109,7 +109,7 @@ pub(crate) async fn receive_imf_inner(
|
||||
let mut mime_parser = match MimeMessage::from_bytes(context, imf_raw, is_partial_download).await
|
||||
{
|
||||
Err(err) => {
|
||||
warn!(context, "receive_imf: can't parse MIME: {err:#}.");
|
||||
warn!(context, "receive_imf: can't parse MIME: {:#}", err);
|
||||
let msg_ids;
|
||||
if !rfc724_mid.starts_with(GENERATED_PREFIX) {
|
||||
let row_id = context
|
||||
@@ -138,11 +138,11 @@ pub(crate) async fn receive_imf_inner(
|
||||
|
||||
// we can not add even an empty record if we have no info whatsoever
|
||||
if !mime_parser.has_headers() {
|
||||
warn!(context, "receive_imf: no headers found.");
|
||||
warn!(context, "receive_imf: no headers found");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
info!(context, "Received message has Message-Id: {rfc724_mid}");
|
||||
info!(context, "received message has Message-Id: {}", rfc724_mid);
|
||||
|
||||
// check, if the mail is already in our database.
|
||||
// make sure, this check is done eg. before securejoin-processing.
|
||||
@@ -182,7 +182,7 @@ pub(crate) async fn receive_imf_inner(
|
||||
None => {
|
||||
warn!(
|
||||
context,
|
||||
"receive_imf: From field does not contain an acceptable address."
|
||||
"receive_imf: From field does not contain an acceptable address"
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -269,13 +269,13 @@ pub(crate) async fn receive_imf_inner(
|
||||
if from_id == ContactId::SELF {
|
||||
if mime_parser.was_encrypted() {
|
||||
if let Err(err) = context.execute_sync_items(sync_items).await {
|
||||
warn!(context, "receive_imf cannot execute sync items: {err:#}.");
|
||||
warn!(context, "receive_imf cannot execute sync items: {:#}", err);
|
||||
}
|
||||
} else {
|
||||
warn!(context, "Sync items are not encrypted.");
|
||||
warn!(context, "sync items are not encrypted.");
|
||||
}
|
||||
} else {
|
||||
warn!(context, "Sync items not sent by self.");
|
||||
warn!(context, "sync items not sent by self.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ pub(crate) async fn receive_imf_inner(
|
||||
.receive_status_update(from_id, insert_msg_id, status_update)
|
||||
.await
|
||||
{
|
||||
warn!(context, "receive_imf cannot update status: {err:#}.");
|
||||
warn!(context, "receive_imf cannot update status: {:#}", err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +302,10 @@ pub(crate) async fn receive_imf_inner(
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!(context, "receive_imf cannot update profile image: {err:#}.");
|
||||
warn!(
|
||||
context,
|
||||
"receive_imf cannot update profile image: {:#}", err
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -328,7 +331,7 @@ pub(crate) async fn receive_imf_inner(
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!(context, "Cannot update contact status: {err:#}.");
|
||||
warn!(context, "cannot update contact status: {:#}", err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,7 +394,7 @@ pub async fn from_field_to_contact_id(
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
"Cannot create a contact for the given From field: {err:#}."
|
||||
"Cannot create a contact for the given From field: {:#}.", err
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -473,7 +476,7 @@ async fn add_parts(
|
||||
// this message is a classic email not a chat-message nor a reply to one
|
||||
match show_emails {
|
||||
ShowEmails::Off => {
|
||||
info!(context, "Classical email not shown (TRASH).");
|
||||
info!(context, "Classical email not shown (TRASH)");
|
||||
chat_id = Some(DC_CHAT_ID_TRASH);
|
||||
allow_creation = false;
|
||||
}
|
||||
@@ -516,7 +519,7 @@ async fn add_parts(
|
||||
securejoin_seen = false;
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, "Error in Secure-Join message handling: {err:#}.");
|
||||
warn!(context, "Error in Secure-Join message handling: {:#}", err);
|
||||
chat_id = Some(DC_CHAT_ID_TRASH);
|
||||
securejoin_seen = true;
|
||||
}
|
||||
@@ -533,8 +536,7 @@ async fn add_parts(
|
||||
|
||||
if chat_id.is_none() && mime_parser.delivery_report.is_some() {
|
||||
chat_id = Some(DC_CHAT_ID_TRASH);
|
||||
info!(context, "Message is a DSN (TRASH).",);
|
||||
markseen_on_imap_table(context, rfc724_mid).await.ok();
|
||||
info!(context, "Message is a DSN (TRASH)",);
|
||||
}
|
||||
|
||||
if chat_id.is_none() {
|
||||
@@ -752,7 +754,7 @@ async fn add_parts(
|
||||
chat_id = None;
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, "Error in Secure-Join watching: {err:#}.");
|
||||
warn!(context, "Error in Secure-Join watching: {:#}", err);
|
||||
chat_id = Some(DC_CHAT_ID_TRASH);
|
||||
}
|
||||
}
|
||||
@@ -769,7 +771,7 @@ async fn add_parts(
|
||||
|
||||
if is_draft {
|
||||
// Most mailboxes have a "Drafts" folder where constantly new emails appear but we don't actually want to show them
|
||||
info!(context, "Email is probably just a draft (TRASH).");
|
||||
info!(context, "Email is probably just a draft (TRASH)");
|
||||
chat_id = Some(DC_CHAT_ID_TRASH);
|
||||
}
|
||||
|
||||
@@ -861,15 +863,14 @@ async fn add_parts(
|
||||
if fetching_existing_messages && mime_parser.decrypting_failed {
|
||||
chat_id = Some(DC_CHAT_ID_TRASH);
|
||||
// We are only gathering old messages on first start. We do not want to add loads of non-decryptable messages to the chats.
|
||||
info!(context, "Existing non-decipherable message (TRASH).");
|
||||
info!(context, "Existing non-decipherable message. (TRASH)");
|
||||
}
|
||||
|
||||
if mime_parser.webxdc_status_update.is_some() && mime_parser.parts.len() == 1 {
|
||||
if let Some(part) = mime_parser.parts.first() {
|
||||
if part.typ == Viewtype::Text && part.msg.is_empty() {
|
||||
chat_id = Some(DC_CHAT_ID_TRASH);
|
||||
info!(context, "Message is a status update only (TRASH).");
|
||||
markseen_on_imap_table(context, rfc724_mid).await.ok();
|
||||
info!(context, "Message is a status update only (TRASH)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -879,7 +880,7 @@ async fn add_parts(
|
||||
DC_CHAT_ID_TRASH
|
||||
} else {
|
||||
chat_id.unwrap_or_else(|| {
|
||||
info!(context, "No chat id for message (TRASH).");
|
||||
info!(context, "No chat id for message (TRASH)");
|
||||
DC_CHAT_ID_TRASH
|
||||
})
|
||||
};
|
||||
@@ -891,7 +892,10 @@ async fn add_parts(
|
||||
match value.parse::<EphemeralTimer>() {
|
||||
Ok(timer) => timer,
|
||||
Err(err) => {
|
||||
warn!(context, "Can't parse ephemeral timer \"{value}\": {err:#}.");
|
||||
warn!(
|
||||
context,
|
||||
"can't parse ephemeral timer \"{}\": {:#}", value, err
|
||||
);
|
||||
EphemeralTimer::Disabled
|
||||
}
|
||||
}
|
||||
@@ -911,7 +915,12 @@ async fn add_parts(
|
||||
&& !mime_parser.parts.is_empty()
|
||||
&& chat_id.get_ephemeral_timer(context).await? != ephemeral_timer
|
||||
{
|
||||
info!(context, "Received new ephemeral timer value {ephemeral_timer:?} for chat {chat_id}, checking if it should be applied.");
|
||||
info!(
|
||||
context,
|
||||
"received new ephemeral timer value {:?} for chat {}, checking if it should be applied",
|
||||
ephemeral_timer,
|
||||
chat_id
|
||||
);
|
||||
if is_dc_message == MessengerMessage::Yes
|
||||
&& get_previous_message(context, mime_parser)
|
||||
.await?
|
||||
@@ -927,7 +936,9 @@ async fn add_parts(
|
||||
// have seen or sent ourselves, so we ignore incoming timer to prevent a rollback.
|
||||
warn!(
|
||||
context,
|
||||
"Ignoring ephemeral timer change to {ephemeral_timer:?} for chat {chat_id} to avoid rollback.",
|
||||
"ignoring ephemeral timer change to {:?} for chat {} to avoid rollback",
|
||||
ephemeral_timer,
|
||||
chat_id
|
||||
);
|
||||
} else if chat_id
|
||||
.update_timestamp(context, Param::EphemeralSettingsTimestamp, sent_timestamp)
|
||||
@@ -939,12 +950,12 @@ async fn add_parts(
|
||||
{
|
||||
warn!(
|
||||
context,
|
||||
"Failed to modify timer for chat {chat_id}: {err:#}."
|
||||
"failed to modify timer for chat {}: {:#}", chat_id, err
|
||||
);
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"Updated ephemeral timer to {ephemeral_timer:?} for chat {chat_id}."
|
||||
"updated ephemeral timer to {:?} for chat {}", ephemeral_timer, chat_id
|
||||
);
|
||||
if mime_parser.is_system_message != SystemMessage::EphemeralTimerChanged {
|
||||
chat::add_info_msg(
|
||||
@@ -959,7 +970,7 @@ async fn add_parts(
|
||||
} else {
|
||||
warn!(
|
||||
context,
|
||||
"Ignoring ephemeral timer change to {ephemeral_timer:?} because it is outdated."
|
||||
"ignoring ephemeral timer change to {:?} because it's outdated", ephemeral_timer
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -988,7 +999,7 @@ async fn add_parts(
|
||||
if chat.is_protected() || new_status.is_some() {
|
||||
if let Err(err) = check_verified_properties(context, mime_parser, from_id, to_ids).await
|
||||
{
|
||||
warn!(context, "Verification problem: {err:#}.");
|
||||
warn!(context, "verification problem: {:#}", err);
|
||||
let s = format!("{err}. See 'Info' for more details");
|
||||
mime_parser.repl_msg_by_error(&s);
|
||||
} else {
|
||||
@@ -1418,7 +1429,7 @@ async fn lookup_chat_by_reply(
|
||||
|
||||
info!(
|
||||
context,
|
||||
"Assigning message to {} as it's a reply to {}.", parent_chat.id, parent.rfc724_mid
|
||||
"Assigning message to {} as it's a reply to {}", parent_chat.id, parent.rfc724_mid
|
||||
);
|
||||
return Ok(Some((parent_chat.id, parent_chat.blocked)));
|
||||
}
|
||||
@@ -1488,7 +1499,7 @@ async fn create_or_lookup_group(
|
||||
.map(|chat_id| (chat_id, create_blocked));
|
||||
return Ok(res);
|
||||
} else {
|
||||
info!(context, "Creating ad-hoc group prevented from caller.");
|
||||
info!(context, "creating ad-hoc group prevented from caller");
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
@@ -1514,7 +1525,7 @@ async fn create_or_lookup_group(
|
||||
|
||||
let create_protected = if mime_parser.get_header(HeaderDef::ChatVerified).is_some() {
|
||||
if let Err(err) = check_verified_properties(context, mime_parser, from_id, to_ids).await {
|
||||
warn!(context, "Verification problem: {err:#}.");
|
||||
warn!(context, "verification problem: {:#}", err);
|
||||
let s = format!("{err}. See 'Info' for more details");
|
||||
mime_parser.repl_msg_by_error(&s);
|
||||
}
|
||||
@@ -1546,7 +1557,7 @@ async fn create_or_lookup_group(
|
||||
{
|
||||
// Group does not exist but should be created.
|
||||
if !allow_creation {
|
||||
info!(context, "Creating group forbidden by caller.");
|
||||
info!(context, "creating group forbidden by caller");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -1607,7 +1618,7 @@ async fn create_or_lookup_group(
|
||||
} else {
|
||||
// The message was decrypted successfully, but contains a late "quit" or otherwise
|
||||
// unwanted message.
|
||||
info!(context, "Message belongs to unwanted group (TRASH).");
|
||||
info!(context, "message belongs to unwanted group (TRASH)");
|
||||
Ok(Some((DC_CHAT_ID_TRASH, Blocked::Not)))
|
||||
}
|
||||
}
|
||||
@@ -1647,7 +1658,7 @@ async fn apply_group_changes(
|
||||
Some(stock_str::msg_del_member(context, &removed_addr, from_id).await)
|
||||
};
|
||||
}
|
||||
None => warn!(context, "Removed {removed_addr:?} has no contact_id."),
|
||||
None => warn!(context, "removed {:?} has no contact_id", removed_addr),
|
||||
}
|
||||
} else {
|
||||
removed_id = None;
|
||||
@@ -1672,7 +1683,7 @@ async fn apply_group_changes(
|
||||
.update_timestamp(context, Param::GroupNameTimestamp, sent_timestamp)
|
||||
.await?
|
||||
{
|
||||
info!(context, "Updating grpname for chat {chat_id}.");
|
||||
info!(context, "updating grpname for chat {}", chat_id);
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
@@ -1712,7 +1723,7 @@ async fn apply_group_changes(
|
||||
|
||||
if mime_parser.get_header(HeaderDef::ChatVerified).is_some() {
|
||||
if let Err(err) = check_verified_properties(context, mime_parser, from_id, to_ids).await {
|
||||
warn!(context, "Verification problem: {err:#}.");
|
||||
warn!(context, "verification problem: {:#}", err);
|
||||
let s = format!("{err}. See 'Info' for more details");
|
||||
mime_parser.repl_msg_by_error(&s);
|
||||
}
|
||||
@@ -1732,7 +1743,9 @@ async fn apply_group_changes(
|
||||
{
|
||||
warn!(
|
||||
context,
|
||||
"Contact {from_id} attempts to modify group chat {chat_id} member list without being a member."
|
||||
"Contact {} attempts to modify group chat {} member list without being a member.",
|
||||
from_id,
|
||||
chat_id
|
||||
);
|
||||
} else if chat_id
|
||||
.update_timestamp(context, Param::MemberListTimestamp, sent_timestamp)
|
||||
@@ -1765,7 +1778,10 @@ async fn apply_group_changes(
|
||||
}
|
||||
members_to_add.dedup();
|
||||
|
||||
info!(context, "Adding {members_to_add:?} to chat id={chat_id}.");
|
||||
info!(
|
||||
context,
|
||||
"adding {:?} to chat id={}", members_to_add, chat_id
|
||||
);
|
||||
chat::add_to_chat_contacts_table(context, chat_id, &members_to_add).await?;
|
||||
send_event_chat_modified = true;
|
||||
}
|
||||
@@ -1775,15 +1791,17 @@ async fn apply_group_changes(
|
||||
if !chat::is_contact_in_chat(context, chat_id, ContactId::SELF).await? {
|
||||
warn!(
|
||||
context,
|
||||
"Received group avatar update for group chat {chat_id} we are not a member of."
|
||||
"Received group avatar update for group chat {} we are not a member of.", chat_id
|
||||
);
|
||||
} else if !chat::is_contact_in_chat(context, chat_id, from_id).await? {
|
||||
warn!(
|
||||
context,
|
||||
"Contact {from_id} attempts to modify group chat {chat_id} avatar without being a member.",
|
||||
"Contact {} attempts to modify group chat {} avatar without being a member.",
|
||||
from_id,
|
||||
chat_id
|
||||
);
|
||||
} else {
|
||||
info!(context, "Group-avatar change for {chat_id}.");
|
||||
info!(context, "group-avatar change for {}", chat_id);
|
||||
if chat
|
||||
.param
|
||||
.update_timestamp(Param::AvatarTimestamp, sent_timestamp)?
|
||||
@@ -1917,7 +1935,7 @@ async fn create_or_lookup_mailinglist(
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to create mailinglist '{}' for grpid={}",
|
||||
"Failed to create mailinglist '{}' for grpid={}",
|
||||
&name, &listid
|
||||
)
|
||||
})?;
|
||||
@@ -1925,7 +1943,7 @@ async fn create_or_lookup_mailinglist(
|
||||
chat::add_to_chat_contacts_table(context, chat_id, &[ContactId::SELF]).await?;
|
||||
Ok(Some((chat_id, blocked)))
|
||||
} else {
|
||||
info!(context, "Creating list forbidden by caller.");
|
||||
info!(context, "creating list forbidden by caller");
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
@@ -2017,7 +2035,7 @@ async fn create_adhoc_group(
|
||||
if mime_parser.is_mailinglist_message() {
|
||||
info!(
|
||||
context,
|
||||
"Not creating ad-hoc group for mailing list message."
|
||||
"not creating ad-hoc group for mailing list message"
|
||||
);
|
||||
|
||||
return Ok(None);
|
||||
@@ -2034,13 +2052,13 @@ async fn create_adhoc_group(
|
||||
// Instead, assign the message to 1:1 chat with the sender.
|
||||
warn!(
|
||||
context,
|
||||
"Not creating ad-hoc group for message that cannot be decrypted."
|
||||
"not creating ad-hoc group for message that cannot be decrypted"
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if member_ids.len() < 3 {
|
||||
info!(context, "Not creating ad-hoc group: too few contacts.");
|
||||
info!(context, "not creating ad-hoc group: too few contacts");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -2147,7 +2165,7 @@ async fn check_verified_properties(
|
||||
for (to_addr, mut is_verified) in rows {
|
||||
info!(
|
||||
context,
|
||||
"check_verified_properties: {:?} self={:?}.",
|
||||
"check_verified_properties: {:?} self={:?}",
|
||||
to_addr,
|
||||
context.is_self_addr(&to_addr).await
|
||||
);
|
||||
|
||||
@@ -5,7 +5,7 @@ use anyhow::{bail, Context as _, Result};
|
||||
use async_channel::{self as channel, Receiver, Sender};
|
||||
use futures::future::try_join_all;
|
||||
use futures_lite::FutureExt;
|
||||
use tokio::sync::{oneshot, RwLock, RwLockWriteGuard};
|
||||
use tokio::sync::{RwLock, RwLockWriteGuard};
|
||||
use tokio::task;
|
||||
|
||||
use self::connectivity::ConnectivityStore;
|
||||
@@ -13,7 +13,6 @@ use crate::config::Config;
|
||||
use crate::contact::{ContactId, RecentlySeenLoop};
|
||||
use crate::context::Context;
|
||||
use crate::ephemeral::{self, delete_expired_imap_messages};
|
||||
use crate::events::EventType;
|
||||
use crate::imap::{FolderMeaning, Imap};
|
||||
use crate::job;
|
||||
use crate::location;
|
||||
@@ -90,28 +89,20 @@ impl SchedulerState {
|
||||
|
||||
/// Pauses the IO scheduler.
|
||||
///
|
||||
/// If it is currently running the scheduler will be stopped. When the
|
||||
/// [`IoPausedGuard`] is dropped the scheduler is started again.
|
||||
/// If it is currently running the scheduler will be stopped. When
|
||||
/// [`IoPausedGuard::resume`] is called the scheduler is started again.
|
||||
///
|
||||
/// If in the meantime [`SchedulerState::start`] or [`SchedulerState::stop`] is called
|
||||
/// resume will do the right thing and restore the scheduler to the state requested by
|
||||
/// the last call.
|
||||
pub(crate) async fn pause<'a>(&'_ self, context: Context) -> IoPausedGuard {
|
||||
{
|
||||
let mut inner = self.inner.write().await;
|
||||
inner.paused = true;
|
||||
Self::do_stop(inner, &context).await;
|
||||
pub(crate) async fn pause<'a>(&'_ self, context: &'a Context) -> IoPausedGuard<'a> {
|
||||
let mut inner = self.inner.write().await;
|
||||
inner.paused = true;
|
||||
Self::do_stop(inner, context).await;
|
||||
IoPausedGuard {
|
||||
context,
|
||||
done: false,
|
||||
}
|
||||
let (tx, rx) = oneshot::channel();
|
||||
tokio::spawn(async move {
|
||||
rx.await.ok();
|
||||
let mut inner = context.scheduler.inner.write().await;
|
||||
inner.paused = false;
|
||||
if inner.started && inner.scheduler.is_none() {
|
||||
SchedulerState::do_start(inner, context.clone()).await;
|
||||
}
|
||||
});
|
||||
IoPausedGuard { sender: Some(tx) }
|
||||
}
|
||||
|
||||
/// Restarts the scheduler, only if it is running.
|
||||
@@ -203,24 +194,34 @@ struct InnerSchedulerState {
|
||||
paused: bool,
|
||||
}
|
||||
|
||||
/// Guard to make sure the IO Scheduler is resumed.
|
||||
///
|
||||
/// Returned by [`SchedulerState::pause`]. To resume the IO scheduler simply drop this
|
||||
/// guard.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct IoPausedGuard {
|
||||
sender: Option<oneshot::Sender<()>>,
|
||||
pub(crate) struct IoPausedGuard<'a> {
|
||||
context: &'a Context,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl Drop for IoPausedGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(sender) = self.sender.take() {
|
||||
// Can only fail if receiver is dropped, but then we're already resumed.
|
||||
sender.send(()).ok();
|
||||
impl<'a> IoPausedGuard<'a> {
|
||||
pub(crate) async fn resume(&mut self) {
|
||||
self.done = true;
|
||||
let mut inner = self.context.scheduler.inner.write().await;
|
||||
inner.paused = false;
|
||||
if inner.started && inner.scheduler.is_none() {
|
||||
SchedulerState::do_start(inner, self.context.clone()).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for IoPausedGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
if self.done {
|
||||
return;
|
||||
}
|
||||
|
||||
// Async .resume() should be called manually due to lack of async drop.
|
||||
error!(self.context, "Pause guard dropped without resuming.");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SchedBox {
|
||||
meaning: FolderMeaning,
|
||||
@@ -478,7 +479,6 @@ async fn fetch_idle(
|
||||
|
||||
connection.connectivity.set_connected(ctx).await;
|
||||
|
||||
ctx.emit_event(EventType::ImapInboxIdle);
|
||||
if let Some(session) = connection.session.take() {
|
||||
if !session.can_idle() {
|
||||
info!(
|
||||
|
||||
@@ -143,7 +143,7 @@ impl Smtp {
|
||||
// Run STARTTLS command and convert the client back into a stream.
|
||||
let client = smtp::SmtpClient::new().smtp_utf8(true);
|
||||
let transport = SmtpTransport::new(client, BufStream::new(socks5_stream)).await?;
|
||||
let tcp_stream = transport.starttls().await?.into_inner();
|
||||
let tcp_stream = transport.starttls().await?;
|
||||
let tls_stream = wrap_tls(strict_tls, hostname, tcp_stream)
|
||||
.await
|
||||
.context("STARTTLS upgrade failed")?;
|
||||
@@ -199,7 +199,7 @@ impl Smtp {
|
||||
// Run STARTTLS command and convert the client back into a stream.
|
||||
let client = smtp::SmtpClient::new().smtp_utf8(true);
|
||||
let transport = SmtpTransport::new(client, BufStream::new(tcp_stream)).await?;
|
||||
let tcp_stream = transport.starttls().await?.into_inner();
|
||||
let tcp_stream = transport.starttls().await?;
|
||||
let tls_stream = wrap_tls(strict_tls, hostname, tcp_stream)
|
||||
.await
|
||||
.context("STARTTLS upgrade failed")?;
|
||||
|
||||
84
src/sql.rs
84
src/sql.rs
@@ -306,49 +306,37 @@ impl Sql {
|
||||
}
|
||||
}
|
||||
|
||||
/// Locks the write transactions mutex in order to make sure that there never are
|
||||
/// multiple write transactions at once.
|
||||
///
|
||||
/// Doing the locking ourselves instead of relying on SQLite has these reasons:
|
||||
///
|
||||
/// - SQLite's locking mechanism is non-async, blocking a thread
|
||||
/// - SQLite's locking mechanism just sleeps in a loop, which is really inefficient
|
||||
///
|
||||
/// ---
|
||||
///
|
||||
/// More considerations on alternatives to the current approach:
|
||||
///
|
||||
/// We use [DEFERRED](https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions) transactions.
|
||||
///
|
||||
/// In order to never get concurrency issues, we could make all transactions IMMEDIATE,
|
||||
/// but this would mean that there can never be two simultaneous transactions.
|
||||
///
|
||||
/// Read transactions can simply be made DEFERRED to run in parallel w/o any drawbacks.
|
||||
///
|
||||
/// DEFERRED write transactions without doing the locking ourselves would have these drawbacks:
|
||||
///
|
||||
/// 1. As mentioned above, SQLite's locking mechanism is non-async and sleeps in a loop.
|
||||
/// 2. If there are other write transactions, we block the db connection until
|
||||
/// upgraded. If some reader comes then, it has to get the next, less used connection with a
|
||||
/// worse per-connection page cache (SQLite allows one write and any number of reads in parallel).
|
||||
/// 3. If a transaction is blocked for more than `busy_timeout`, it fails with SQLITE_BUSY.
|
||||
/// 4. If upon a successful upgrade to a write transaction the db has been modified,
|
||||
/// the transaction has to be rolled back and retried, which means extra work in terms of
|
||||
/// Locks the write transactions mutex.
|
||||
/// We do not make all transactions
|
||||
/// [IMMEDIATE](https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions)
|
||||
/// for more parallelism -- at least read transactions can be made DEFERRED to run in parallel
|
||||
/// w/o any drawbacks. But if we make write transactions DEFERRED also w/o any external locking,
|
||||
/// then they are upgraded from read to write ones on the first write statement. This has some
|
||||
/// drawbacks:
|
||||
/// - If there are other write transactions, we block the thread and the db connection until
|
||||
/// upgraded. Also if some reader comes then, it has to get next, less used connection with a
|
||||
/// worse per-connection page cache.
|
||||
/// - If a transaction is blocked for more than busy_timeout, it fails with SQLITE_BUSY.
|
||||
/// - Configuring busy_timeout is not the best way to manage transaction timeouts, we would
|
||||
/// prefer it to be integrated with Rust/tokio asyncs. Moreover, SQLite implements waiting
|
||||
/// using sleeps.
|
||||
/// - If upon a successful upgrade to a write transaction the db has been modified by another
|
||||
/// one, the transaction has to be rolled back and retried. It is an extra work in terms of
|
||||
/// CPU/battery.
|
||||
///
|
||||
/// The only pro of making write transactions DEFERRED w/o the external locking would be some
|
||||
/// parallelism between them.
|
||||
///
|
||||
/// Another option would be to make write transactions IMMEDIATE, also
|
||||
/// w/o the external locking. But then cons 1. - 3. above would still be valid.
|
||||
/// - Maybe minor, but we lose some fairness in servicing write transactions, i.e. we service
|
||||
/// them in the order of the first write statement, not in the order they come.
|
||||
/// The only pro of making write transactions DEFERRED w/o the external locking is some
|
||||
/// parallelism between them. Also we have an option to make write transactions IMMEDIATE, also
|
||||
/// w/o the external locking. But then the most of cons above are still valid. Instead, if we
|
||||
/// perform all write transactions under an async mutex, the only cons is losing some
|
||||
/// parallelism for write transactions.
|
||||
pub async fn write_lock(&self) -> MutexGuard<'_, ()> {
|
||||
self.write_mtx.lock().await
|
||||
}
|
||||
|
||||
/// Allocates a connection and calls `function` with the connection. If `function` does write
|
||||
/// queries,
|
||||
/// - either first take a lock using `write_lock()`
|
||||
/// - or use `call_write()` instead.
|
||||
/// queries, either a lock must be taken first using `write_lock()` or `call_write()` used
|
||||
/// instead.
|
||||
///
|
||||
/// Returns the result of the function.
|
||||
async fn call<'a, F, R>(&'a self, function: F) -> Result<R>
|
||||
@@ -699,26 +687,26 @@ pub async fn housekeeping(context: &Context) -> Result<()> {
|
||||
if let Err(err) = remove_unused_files(context).await {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: cannot remove unused files: {:#}.", err
|
||||
"Housekeeping: cannot remove unused files: {:#}", err
|
||||
);
|
||||
}
|
||||
|
||||
if let Err(err) = start_ephemeral_timers(context).await {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: cannot start ephemeral timers: {:#}.", err
|
||||
"Housekeeping: cannot start ephemeral timers: {:#}", err
|
||||
);
|
||||
}
|
||||
|
||||
if let Err(err) = prune_tombstones(&context.sql).await {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: Cannot prune message tombstones: {:#}.", err
|
||||
"Housekeeping: Cannot prune message tombstones: {:#}", err
|
||||
);
|
||||
}
|
||||
|
||||
if let Err(err) = deduplicate_peerstates(&context.sql).await {
|
||||
warn!(context, "Failed to deduplicate peerstates: {:#}.", err)
|
||||
warn!(context, "Failed to deduplicate peerstates: {:#}", err)
|
||||
}
|
||||
|
||||
context.schedule_quota_update().await?;
|
||||
@@ -731,7 +719,7 @@ pub async fn housekeeping(context: &Context) -> Result<()> {
|
||||
.await
|
||||
{
|
||||
Err(err) => {
|
||||
warn!(context, "Failed to run incremental vacuum: {err:#}.");
|
||||
warn!(context, "Failed to run incremental vacuum: {err:#}");
|
||||
}
|
||||
Ok(Some(())) => {
|
||||
// Incremental vacuum returns a zero-column result if it did anything.
|
||||
@@ -747,7 +735,7 @@ pub async fn housekeeping(context: &Context) -> Result<()> {
|
||||
.set_config(Config::LastHousekeeping, Some(&time().to_string()))
|
||||
.await
|
||||
{
|
||||
warn!(context, "Can't set config: {e:#}.");
|
||||
warn!(context, "Can't set config: {}", e);
|
||||
}
|
||||
|
||||
context
|
||||
@@ -814,7 +802,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
||||
.await
|
||||
.context("housekeeping: failed to SELECT value FROM config")?;
|
||||
|
||||
info!(context, "{} files in use.", files_in_use.len());
|
||||
info!(context, "{} files in use.", files_in_use.len(),);
|
||||
/* go through directories and delete unused files */
|
||||
let blobdir = context.get_blobdir();
|
||||
for p in [&blobdir.join(BLOBS_BACKUP_NAME), blobdir] {
|
||||
@@ -846,7 +834,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
||||
// environment f.e. So, no warning.
|
||||
info!(
|
||||
context,
|
||||
"Housekeeping: Cannot rmdir {}: {:#}.",
|
||||
"Housekeeping: Cannot rmdir {}: {:#}",
|
||||
entry.path().display(),
|
||||
e
|
||||
);
|
||||
@@ -868,7 +856,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
||||
{
|
||||
info!(
|
||||
context,
|
||||
"Housekeeping: Keeping new unreferenced file #{}: {:?}.",
|
||||
"Housekeeping: Keeping new unreferenced file #{}: {:?}",
|
||||
unreferenced_count,
|
||||
entry.file_name(),
|
||||
);
|
||||
@@ -879,7 +867,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
"Housekeeping: Deleting unreferenced file #{}: {:?}.",
|
||||
"Housekeeping: Deleting unreferenced file #{}: {:?}",
|
||||
unreferenced_count,
|
||||
entry.file_name()
|
||||
);
|
||||
@@ -897,7 +885,7 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
"Housekeeping: Cannot read dir {}: {:#}.",
|
||||
"Housekeeping: Cannot read dir {}: {:#}",
|
||||
p.display(),
|
||||
err
|
||||
);
|
||||
|
||||
@@ -165,19 +165,6 @@ pub(crate) struct StatusUpdateItemAndSerial {
|
||||
max_serial: StatusUpdateSerial,
|
||||
}
|
||||
|
||||
/// Returns an entry index and a reference.
|
||||
fn find_zip_entry<'a>(
|
||||
file: &'a async_zip::ZipFile,
|
||||
name: &str,
|
||||
) -> Option<(usize, &'a async_zip::StoredZipEntry)> {
|
||||
for (i, ent) in file.entries().iter().enumerate() {
|
||||
if ent.entry().filename() == name {
|
||||
return Some((i, ent));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// check if a file is an acceptable webxdc for sending or receiving.
|
||||
pub(crate) async fn is_webxdc_file(&self, filename: &str, file: &[u8]) -> Result<bool> {
|
||||
@@ -193,7 +180,7 @@ impl Context {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let archive = match async_zip::read::mem::ZipFileReader::new(file.to_vec()).await {
|
||||
let archive = match async_zip::read::mem::ZipFileReader::new(file).await {
|
||||
Ok(archive) => archive,
|
||||
Err(_) => {
|
||||
info!(self, "{} cannot be opened as zip-file", &filename);
|
||||
@@ -201,7 +188,7 @@ impl Context {
|
||||
}
|
||||
};
|
||||
|
||||
if find_zip_entry(archive.file(), "index.html").is_none() {
|
||||
if archive.entry("index.html").is_none() {
|
||||
info!(self, "{} misses index.html", &filename);
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -228,7 +215,7 @@ impl Context {
|
||||
|
||||
let valid = match async_zip::read::fs::ZipFileReader::new(path).await {
|
||||
Ok(archive) => {
|
||||
if find_zip_entry(archive.file(), "index.html").is_none() {
|
||||
if archive.entry("index.html").is_none() {
|
||||
info!(self, "{} misses index.html", filename);
|
||||
false
|
||||
} else {
|
||||
@@ -662,9 +649,10 @@ fn parse_webxdc_manifest(bytes: &[u8]) -> Result<WebxdcManifest> {
|
||||
}
|
||||
|
||||
async fn get_blob(archive: &mut async_zip::read::fs::ZipFileReader, name: &str) -> Result<Vec<u8>> {
|
||||
let (i, _) = find_zip_entry(archive.file(), name)
|
||||
let (i, _) = archive
|
||||
.entry(name)
|
||||
.ok_or_else(|| anyhow!("no entry found for {}", name))?;
|
||||
let mut reader = archive.entry(i).await?;
|
||||
let mut reader = archive.entry_reader(i).await?;
|
||||
let mut buf = Vec::new();
|
||||
reader.read_to_end(&mut buf).await?;
|
||||
Ok(buf)
|
||||
@@ -766,9 +754,9 @@ impl Message {
|
||||
} else {
|
||||
self.get_filename().unwrap_or_default()
|
||||
},
|
||||
icon: if find_zip_entry(archive.file(), "icon.png").is_some() {
|
||||
icon: if archive.entry("icon.png").is_some() {
|
||||
"icon.png".to_string()
|
||||
} else if find_zip_entry(archive.file(), "icon.jpg").is_some() {
|
||||
} else if archive.entry("icon.jpg").is_some() {
|
||||
"icon.jpg".to_string()
|
||||
} else {
|
||||
WEBXDC_DEFAULT_ICON.to_string()
|
||||
|
||||
Reference in New Issue
Block a user