mirror of
https://github.com/chatmail/core.git
synced 2026-10-17 18:33:01 +03:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2ab9456cd | ||
|
|
cdaef3bd0d |
+14
-14
@@ -5,22 +5,22 @@ mergeable:
|
||||
validate:
|
||||
- do: or
|
||||
validate:
|
||||
- do: description
|
||||
must_include:
|
||||
regex: "#skip-changelog"
|
||||
- do: and
|
||||
validate:
|
||||
- do: dependent
|
||||
changed:
|
||||
file: "src/**"
|
||||
required: ["CHANGELOG.md"]
|
||||
- do: dependent
|
||||
changed:
|
||||
file: "deltachat-ffi/src/**"
|
||||
required: ["CHANGELOG.md"]
|
||||
- do: description
|
||||
must_include:
|
||||
regex: '#skip-changelog'
|
||||
- do: and
|
||||
validate:
|
||||
- do: dependent
|
||||
changed:
|
||||
file: 'src/**'
|
||||
required: ['CHANGELOG.md']
|
||||
- do: dependent
|
||||
changed:
|
||||
file: 'deltachat-ffi/src/**'
|
||||
required: ['CHANGELOG.md']
|
||||
fail:
|
||||
- do: checks
|
||||
status: "action_required"
|
||||
status: 'action_required'
|
||||
payload:
|
||||
title: Changelog might need an update
|
||||
summary: "Check if CHANGELOG.md needs an update or add #skip-changelog to the PR description."
|
||||
|
||||
+58
-83
@@ -1,11 +1,5 @@
|
||||
name: Rust CI
|
||||
|
||||
# Cancel previously started workflow runs
|
||||
# when the branch is updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
@@ -14,7 +8,7 @@ on:
|
||||
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Rustfmt and Clippy
|
||||
@@ -24,7 +18,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install rustfmt and clippy
|
||||
run: rustup toolchain install $RUSTUP_TOOLCHAIN --profile minimal --component rustfmt --component clippy
|
||||
run: rustup toolchain install $RUSTUP_TOOLCHAIN --component rustfmt --component clippy
|
||||
- name: Cache rust cargo artifacts
|
||||
uses: swatinem/rust-cache@v2
|
||||
- name: Run rustfmt
|
||||
@@ -32,25 +26,6 @@ jobs:
|
||||
- name: Run clippy
|
||||
run: scripts/clippy.sh
|
||||
|
||||
cargo_deny:
|
||||
name: cargo deny
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
with:
|
||||
arguments: --all-features --workspace
|
||||
command: check
|
||||
command-arguments: "-Dwarnings"
|
||||
|
||||
provider_database:
|
||||
name: Check provider database
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check provider database
|
||||
run: scripts/update-provider-database.sh
|
||||
|
||||
docs:
|
||||
name: Rust doc comments
|
||||
runs-on: ubuntu-latest
|
||||
@@ -88,73 +63,73 @@ jobs:
|
||||
python: 3.7
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- name: Install Rust ${{ matrix.rust }}
|
||||
run: rustup toolchain install --profile minimal ${{ matrix.rust }}
|
||||
- run: rustup override set ${{ matrix.rust }}
|
||||
- name: Install Rust ${{ matrix.rust }}
|
||||
run: rustup toolchain install ${{ matrix.rust }}
|
||||
- run: rustup override set ${{ matrix.rust }}
|
||||
|
||||
- name: Cache rust cargo artifacts
|
||||
uses: swatinem/rust-cache@v2
|
||||
- name: Cache rust cargo artifacts
|
||||
uses: swatinem/rust-cache@v2
|
||||
|
||||
- name: Check
|
||||
run: cargo check --workspace --bins --examples --tests --benches
|
||||
- name: Check
|
||||
run: cargo check --workspace --bins --examples --tests --benches
|
||||
|
||||
- name: Tests
|
||||
run: cargo test --workspace
|
||||
- name: Tests
|
||||
run: cargo test --workspace
|
||||
|
||||
- name: Test cargo vendor
|
||||
run: cargo vendor
|
||||
- name: Test cargo vendor
|
||||
run: cargo vendor
|
||||
|
||||
- name: Install python
|
||||
if: ${{ matrix.python }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- name: Install python
|
||||
if: ${{ matrix.python }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: Install tox
|
||||
if: ${{ matrix.python }}
|
||||
run: pip install tox
|
||||
- name: Install tox
|
||||
if: ${{ matrix.python }}
|
||||
run: pip install tox
|
||||
|
||||
- name: Build C library
|
||||
if: ${{ matrix.python }}
|
||||
run: cargo build -p deltachat_ffi --features jsonrpc
|
||||
- name: Build C library
|
||||
if: ${{ matrix.python }}
|
||||
run: cargo build -p deltachat_ffi --features jsonrpc
|
||||
|
||||
- name: Run python tests
|
||||
if: ${{ matrix.python }}
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
DCC_RS_TARGET: debug
|
||||
DCC_RS_DEV: ${{ github.workspace }}
|
||||
working-directory: python
|
||||
run: tox -e lint,mypy,doc,py3
|
||||
- name: Run python tests
|
||||
if: ${{ matrix.python }}
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
DCC_RS_TARGET: debug
|
||||
DCC_RS_DEV: ${{ github.workspace }}
|
||||
working-directory: python
|
||||
run: tox -e lint,mypy,doc,py3
|
||||
|
||||
- name: Build deltachat-rpc-server
|
||||
if: ${{ matrix.python }}
|
||||
run: cargo build -p deltachat-rpc-server
|
||||
- name: Build deltachat-rpc-server
|
||||
if: ${{ matrix.python }}
|
||||
run: cargo build -p deltachat-rpc-server
|
||||
|
||||
- name: Add deltachat-rpc-server to path
|
||||
if: ${{ matrix.python }}
|
||||
run: echo ${{ github.workspace }}/target/debug >> $GITHUB_PATH
|
||||
- name: Add deltachat-rpc-server to path
|
||||
if: ${{ matrix.python }}
|
||||
run: echo ${{ github.workspace }}/target/debug >> $GITHUB_PATH
|
||||
|
||||
- name: Run deltachat-rpc-client tests
|
||||
if: ${{ matrix.python }}
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
working-directory: deltachat-rpc-client
|
||||
run: tox -e py3,lint
|
||||
- name: Run deltachat-rpc-client tests
|
||||
if: ${{ matrix.python }}
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
working-directory: deltachat-rpc-client
|
||||
run: tox -e py3,lint
|
||||
|
||||
- name: Install pypy
|
||||
if: ${{ matrix.python }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "pypy${{ matrix.python }}"
|
||||
- name: Install pypy
|
||||
if: ${{ matrix.python }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 'pypy${{ matrix.python }}'
|
||||
|
||||
- name: Run pypy tests
|
||||
if: ${{ matrix.python }}
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
DCC_RS_TARGET: debug
|
||||
DCC_RS_DEV: ${{ github.workspace }}
|
||||
working-directory: python
|
||||
run: tox -e pypy3
|
||||
- name: Run pypy tests
|
||||
if: ${{ matrix.python }}
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
DCC_RS_TARGET: debug
|
||||
DCC_RS_DEV: ${{ github.workspace }}
|
||||
working-directory: python
|
||||
run: tox -e pypy3
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
# Manually triggered action to build deltachat-rpc-server binaries.
|
||||
|
||||
name: Build deltachat-rpc-server binaries
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
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_aarch64_linux:
|
||||
name: Cross-compile deltachat-rpc-server for aarch64 Linux
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build
|
||||
run: sh scripts/aarch64-unknown-linux-musl.sh
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-aarch64
|
||||
path: target/aarch64-unknown-linux-musl/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:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
artifact: win32.exe
|
||||
path: deltachat-rpc-server.exe
|
||||
target: i686-pc-windows-msvc
|
||||
|
||||
- os: windows-latest
|
||||
artifact: win64.exe
|
||||
path: deltachat-rpc-server.exe
|
||||
target: x86_64-pc-windows-msvc
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
|
||||
- name: Build
|
||||
run: cargo build --release --package deltachat-rpc-server --target ${{ matrix.target }} --features vendored
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: deltachat-rpc-server-${{ matrix.artifact }}
|
||||
path: target/${{ matrix.target}}/release/${{ matrix.path }}
|
||||
if-no-files-found: error
|
||||
@@ -1,15 +1,16 @@
|
||||
name: "jsonrpc js client build"
|
||||
name: 'jsonrpc js client build'
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
- "!py-*"
|
||||
- '*'
|
||||
- '!py-*'
|
||||
|
||||
|
||||
jobs:
|
||||
pack-module:
|
||||
name: "Package @deltachat/jsonrpc-client and upload to download.delta.chat"
|
||||
runs-on: ubuntu-20.04
|
||||
name: 'Package @deltachat/jsonrpc-client and upload to download.delta.chat'
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Install tree
|
||||
run: sudo apt install tree
|
||||
@@ -17,12 +18,12 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
node-version: '16'
|
||||
- name: Get tag
|
||||
id: tag
|
||||
uses: dawidd6/action-get-tag@v1
|
||||
continue-on-error: true
|
||||
- name: Get Pull Request ID
|
||||
- name: Get Pullrequest ID
|
||||
id: prepare
|
||||
run: |
|
||||
tag=${{ steps.tag.outputs.tag }}
|
||||
@@ -68,9 +69,9 @@ jobs:
|
||||
if: steps.upload-preview.outcome == 'success'
|
||||
run: node ./node/scripts/postLinksToDetails.js
|
||||
env:
|
||||
URL: preview/${{ env.DELTACHAT_JSONRPC_TAR_GZ }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
MSG_CONTEXT: Download the deltachat-jsonrpc-client.tgz
|
||||
URL: preview/${{ env.DELTACHAT_JSONRPC_TAR_GZ }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
MSG_CONTEXT: Download the deltachat-jsonrpc-client.tgz
|
||||
# Upload to download.delta.chat/node/
|
||||
- name: Upload deltachat-jsonrpc-client build to download.delta.chat/node/
|
||||
if: ${{ steps.tag.outputs.tag }}
|
||||
|
||||
@@ -7,25 +7,26 @@ on:
|
||||
|
||||
jobs:
|
||||
delete:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Get Pull Request ID
|
||||
id: getid
|
||||
run: |
|
||||
export PULLREQUEST_ID=$(jq .number < $GITHUB_EVENT_PATH)
|
||||
echo "prid=$PULLREQUEST_ID" >> $GITHUB_OUTPUT
|
||||
- name: Renaming
|
||||
run: |
|
||||
# create empty file to copy it over the outdated deliverable on download.delta.chat
|
||||
echo "This preview build is outdated and has been removed." > empty
|
||||
cp empty deltachat-node-${{ steps.getid.outputs.prid }}.tar.gz
|
||||
- name: Replace builds with dummy files
|
||||
uses: horochx/deploy-via-scp@v1.0.1
|
||||
with:
|
||||
user: ${{ secrets.USERNAME }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
host: "download.delta.chat"
|
||||
port: 22
|
||||
local: "deltachat-node-${{ steps.getid.outputs.prid }}.tar.gz"
|
||||
remote: "/var/www/html/download/node/preview/"
|
||||
- name: Get Pullrequest ID
|
||||
id: getid
|
||||
run: |
|
||||
export PULLREQUEST_ID=$(jq .number < $GITHUB_EVENT_PATH)
|
||||
echo "prid=$PULLREQUEST_ID" >> $GITHUB_OUTPUT
|
||||
- name: Renaming
|
||||
run: |
|
||||
# create empty file to copy it over the outdated deliverable on download.delta.chat
|
||||
echo "This preview build is outdated and has been removed." > empty
|
||||
cp empty deltachat-node-${{ steps.getid.outputs.prid }}.tar.gz
|
||||
- name: Replace builds with dummy files
|
||||
uses: horochx/deploy-via-scp@v1.0.1
|
||||
with:
|
||||
user: ${{ secrets.USERNAME }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
host: "download.delta.chat"
|
||||
port: 22
|
||||
local: "deltachat-node-${{ steps.getid.outputs.prid }}.tar.gz"
|
||||
remote: "/var/www/html/download/node/preview/"
|
||||
|
||||
@@ -9,26 +9,26 @@ jobs:
|
||||
generate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: npm install and generate documentation
|
||||
run: |
|
||||
cd node
|
||||
npm i --ignore-scripts
|
||||
npx typedoc
|
||||
mv docs js
|
||||
- name: npm install and generate documentation
|
||||
run: |
|
||||
cd node
|
||||
npm i --ignore-scripts
|
||||
npx typedoc
|
||||
mv docs js
|
||||
|
||||
- name: Upload
|
||||
uses: horochx/deploy-via-scp@v1.0.1
|
||||
with:
|
||||
user: ${{ secrets.USERNAME }}
|
||||
key: ${{ secrets.KEY }}
|
||||
host: "delta.chat"
|
||||
port: 22
|
||||
local: "node/js"
|
||||
remote: "/var/www/html/"
|
||||
- name: Upload
|
||||
uses: horochx/deploy-via-scp@v1.0.1
|
||||
with:
|
||||
user: ${{ secrets.USERNAME }}
|
||||
key: ${{ secrets.KEY }}
|
||||
host: "delta.chat"
|
||||
port: 22
|
||||
local: "node/js"
|
||||
remote: "/var/www/html/"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
name: "node.js build"
|
||||
name: 'node.js build'
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
- "!py-*"
|
||||
- '*'
|
||||
- '!py-*'
|
||||
|
||||
|
||||
jobs:
|
||||
prebuild:
|
||||
@@ -12,13 +13,13 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-latest, windows-latest]
|
||||
os: [ubuntu-18.04, macos-latest, windows-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
node-version: '16'
|
||||
- name: System info
|
||||
run: |
|
||||
rustc -vV
|
||||
@@ -65,7 +66,7 @@ jobs:
|
||||
pack-module:
|
||||
needs: prebuild
|
||||
name: Package deltachat-node and upload to download.delta.chat
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Install tree
|
||||
run: sudo apt install tree
|
||||
@@ -73,12 +74,12 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "16"
|
||||
node-version: '16'
|
||||
- name: Get tag
|
||||
id: tag
|
||||
uses: dawidd6/action-get-tag@v1
|
||||
continue-on-error: true
|
||||
- name: Get Pull Request ID
|
||||
- name: Get Pullrequest ID
|
||||
id: prepare
|
||||
run: |
|
||||
tag=${{ steps.tag.outputs.tag }}
|
||||
@@ -99,7 +100,7 @@ jobs:
|
||||
- name: Download Ubuntu prebuild
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: ubuntu-20.04
|
||||
name: ubuntu-18.04
|
||||
- name: Download macOS prebuild
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
@@ -111,11 +112,11 @@ jobs:
|
||||
- shell: bash
|
||||
run: |
|
||||
mkdir node/prebuilds
|
||||
tar -xvzf ubuntu-20.04/ubuntu-20.04.tar.gz -C node/prebuilds
|
||||
tar -xvzf ubuntu-18.04/ubuntu-18.04.tar.gz -C node/prebuilds
|
||||
tar -xvzf macos-latest/macos-latest.tar.gz -C node/prebuilds
|
||||
tar -xvzf windows-latest/windows-latest.tar.gz -C node/prebuilds
|
||||
tree node/prebuilds
|
||||
rm -rf ubuntu-20.04 macos-latest windows-latest
|
||||
rm -rf ubuntu-18.04 macos-latest windows-latest
|
||||
- name: Install dependencies without running scripts
|
||||
run: |
|
||||
npm install --ignore-scripts
|
||||
@@ -151,8 +152,8 @@ jobs:
|
||||
if: steps.upload-preview.outcome == 'success'
|
||||
run: node ./node/scripts/postLinksToDetails.js
|
||||
env:
|
||||
URL: preview/${{ env.DELTACHAT_NODE_TAR_GZ }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
URL: preview/${{ env.DELTACHAT_NODE_TAR_GZ }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Upload to download.delta.chat/node/
|
||||
- name: Upload deltachat-node build to download.delta.chat/node/
|
||||
if: ${{ steps.tag.outputs.tag }}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
name: "node.js tests"
|
||||
|
||||
# Cancel previously started workflow runs
|
||||
# when the branch is updated.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
name: 'node.js tests'
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- staging
|
||||
- trying
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
@@ -18,13 +13,13 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
os: [ubuntu-18.04, macos-latest, windows-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
node-version: '16'
|
||||
- name: System info
|
||||
run: |
|
||||
rustc -vV
|
||||
@@ -64,7 +59,7 @@ jobs:
|
||||
npm run test
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
NODE_OPTIONS: "--force-node-api-uncaught-exceptions-policy=true"
|
||||
NODE_OPTIONS: '--force-node-api-uncaught-exceptions-policy=true'
|
||||
- name: Run tests on Windows, except lint
|
||||
timeout-minutes: 10
|
||||
if: runner.os == 'Windows'
|
||||
@@ -73,4 +68,4 @@ jobs:
|
||||
npm run test:mocha
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
NODE_OPTIONS: "--force-node-api-uncaught-exceptions-policy=true"
|
||||
NODE_OPTIONS: '--force-node-api-uncaught-exceptions-policy=true'
|
||||
|
||||
@@ -11,13 +11,13 @@ jobs:
|
||||
name: Build REPL example
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build
|
||||
run: cargo build -p deltachat-repl --features vendored
|
||||
- name: Build
|
||||
run: cargo build -p deltachat-repl --features vendored
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: repl.exe
|
||||
path: "target/debug/deltachat-repl.exe"
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: repl.exe
|
||||
path: 'target/debug/deltachat-repl.exe'
|
||||
|
||||
@@ -8,18 +8,20 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build the documentation with cargo
|
||||
run: |
|
||||
cargo doc --package deltachat --no-deps
|
||||
- name: Upload to rs.delta.chat
|
||||
uses: up9cloud/action-rsync@v1.3
|
||||
env:
|
||||
USER: ${{ secrets.USERNAME }}
|
||||
KEY: ${{ secrets.KEY }}
|
||||
HOST: "delta.chat"
|
||||
SOURCE: "target/doc"
|
||||
TARGET: "/var/www/html/rs/"
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build the documentation with cargo
|
||||
run: |
|
||||
cargo doc --package deltachat --no-deps
|
||||
- name: Upload to rs.delta.chat
|
||||
uses: up9cloud/action-rsync@v1.3
|
||||
env:
|
||||
USER: ${{ secrets.USERNAME }}
|
||||
KEY: ${{ secrets.KEY }}
|
||||
HOST: "delta.chat"
|
||||
SOURCE: "target/doc"
|
||||
TARGET: "/var/www/html/rs/"
|
||||
|
||||
|
||||
@@ -8,18 +8,20 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build the documentation with cargo
|
||||
run: |
|
||||
cargo doc --package deltachat_ffi --no-deps
|
||||
- name: Upload to cffi.delta.chat
|
||||
uses: up9cloud/action-rsync@v1.3
|
||||
env:
|
||||
USER: ${{ secrets.USERNAME }}
|
||||
KEY: ${{ secrets.KEY }}
|
||||
HOST: "delta.chat"
|
||||
SOURCE: "target/doc"
|
||||
TARGET: "/var/www/html/cffi/"
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build the documentation with cargo
|
||||
run: |
|
||||
cargo doc --package deltachat_ffi --no-deps
|
||||
- name: Upload to cffi.delta.chat
|
||||
uses: up9cloud/action-rsync@v1.3
|
||||
env:
|
||||
USER: ${{ secrets.USERNAME }}
|
||||
KEY: ${{ secrets.KEY }}
|
||||
HOST: "delta.chat"
|
||||
SOURCE: "target/doc"
|
||||
TARGET: "/var/www/html/cffi/"
|
||||
|
||||
|
||||
+13
-45
@@ -1,42 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 1.111.0
|
||||
|
||||
### Changes
|
||||
- Make smeared timestamp generation non-async. #4075
|
||||
- Set minimum TLS version to 1.2. #4096
|
||||
- Run `cargo-deny` in CI. #4101
|
||||
- Check provider database with CI. #4099
|
||||
- Switch to DEFERRED transactions #4100
|
||||
|
||||
### Fixes
|
||||
- Do not block async task executor while decrypting the messages. #4079
|
||||
- Housekeeping: delete the blobs backup dir #4123
|
||||
|
||||
### API-Changes
|
||||
- jsonrpc: add more advanced API to send a message. #4097
|
||||
- jsonrpc: add get webxdc blob API `getWebxdcBlob` #4070
|
||||
|
||||
## 1.110.0
|
||||
## Unreleased
|
||||
|
||||
### Changes
|
||||
- use transaction in `Contact::add_or_lookup()` #4059
|
||||
- Organize the connection pool as a stack rather than a queue to ensure that
|
||||
connection page cache is reused more often.
|
||||
This speeds up tests by 28%, real usage will have lower speedup. #4065
|
||||
- Use transaction in `update_blocked_mailinglist_contacts`. #4058
|
||||
- Remove `Sql.get_conn()` interface in favor of `.call()` and `.transaction()`. #4055
|
||||
- Updated provider database.
|
||||
- Disable DKIM-Checks again #4076
|
||||
- Switch from "X.Y.Z" and "py-X.Y.Z" to "vX.Y.Z" tags. #4089
|
||||
- mimeparser: handle headers from the signed part of unencrypted signed message #4013
|
||||
|
||||
### Fixes
|
||||
- Start SQL transactions with IMMEDIATE behaviour rather than default DEFERRED one. #4063
|
||||
- Fix a problem with Gmail where (auto-)deleted messages would get archived instead of deleted.
|
||||
Move them to the Trash folder for Gmail which auto-deletes trashed messages in 30 days #3972
|
||||
- Clear config cache after backup import. This bug sometimes resulted in the import to seemingly work at first. #4067
|
||||
- Update timestamps in `param` columns with transactions. #4083
|
||||
|
||||
### API-Changes
|
||||
|
||||
@@ -70,7 +39,6 @@
|
||||
- Prefer TLS over STARTTLS during autoconfiguration #4021
|
||||
- Use SOCKS5 configuration for HTTP requests #4017
|
||||
- Show non-deltachat emails by default for new installations #4019
|
||||
- Re-enabled SMTP pipelining after disabling it in #4006
|
||||
|
||||
### Fixes
|
||||
- Fix Securejoin for multiple devices on a joining side #3982
|
||||
@@ -102,7 +70,7 @@
|
||||
|
||||
### Fixes
|
||||
- Securejoin: Fix adding and handling Autocrypt-Gossip headers #3914
|
||||
- fix verifier-by addr was empty string instead of None #3961
|
||||
- fix verifier-by addr was empty string intead of None #3961
|
||||
- Emit DC_EVENT_MSGS_CHANGED for DC_CHAT_ID_ARCHIVED_LINK when the number of archived chats with
|
||||
unread messages increases #3959
|
||||
- Fix Peerstate comparison #3962
|
||||
@@ -203,7 +171,7 @@
|
||||
- jsonrpc: Add async Python client #3734
|
||||
|
||||
### Fixes
|
||||
- Make sure malformed messages will never block receiving further messages anymore #3771
|
||||
- Make sure malformed messsages will never block receiving further messages anymore #3771
|
||||
- strip leading/trailing whitespace from "Chat-Group-Name{,-Changed}:" headers content #3650
|
||||
- Assume all Thunderbird users prefer encryption #3774
|
||||
- refactor peerstate handling to ensure no duplicate peerstates #3776
|
||||
@@ -355,7 +323,7 @@
|
||||
- `importBackup()`
|
||||
- `getMessageHtml()` #3671
|
||||
- `miscGetStickerFolder` and `miscGetStickers` #3672
|
||||
- breaking: jsonrpc: remove function `messageListGetMessageIds()`, it is replaced by `getMessageIds()` and `getMessageListItems()` the latter returns a new `MessageListItem` type, which is the now preferred way of using the message list.
|
||||
- breaking: jsonrpc: remove function `messageListGetMessageIds()`, it is replaced by `getMessageIds()` and `getMessageListItems()` the latter returns a new `MessageListItem` type, which is the now prefered way of using the message list.
|
||||
- jsonrpc: add type: #3641, #3645
|
||||
- `MessageSearchResult`
|
||||
- `Location`
|
||||
@@ -381,7 +349,7 @@
|
||||
- jsonrpc js client:
|
||||
- Change package name from `deltachat-jsonrpc-client` to `@deltachat/jsonrpc-client`
|
||||
- remove relative file dependency to it from `deltachat-node` (because it did not work anyway and broke the nix build of desktop)
|
||||
- ci: add github ci action to upload it to our download server automatically on release
|
||||
- ci: add github ci action to upload it to our download server automaticaly on realease
|
||||
|
||||
## 1.95.0
|
||||
|
||||
@@ -458,7 +426,7 @@
|
||||
- Auto accept contact requests if `Config::Bot` is set for a client #3567
|
||||
- Don't prepend the subject to chat messages in mailinglists
|
||||
- fix `set_core_version.py` script to also update version in `deltachat-jsonrpc/typescript/package.json` #3585
|
||||
- Reject webxdc-updates from contacts who are not group members #3568
|
||||
- Reject webxcd-updates from contacts who are not group members #3568
|
||||
|
||||
|
||||
## 1.93.0
|
||||
@@ -681,7 +649,7 @@
|
||||
|
||||
### Fixes
|
||||
- node: throw error when getting context with an invalid account id
|
||||
- node: throw error when instantiating a wrapper class on `null` (Context, Message, Chat, ChatList and so on)
|
||||
- node: throw error when instanciating a wrapper class on `null` (Context, Message, Chat, ChatList and so on)
|
||||
- use same contact-color if email address differ only in upper-/lowercase #3327
|
||||
- repair encrypted mails "mixed up" by Google Workspace "Append footer" function #3315
|
||||
|
||||
@@ -769,7 +737,7 @@
|
||||
- hopefully fix a bug where outgoing messages appear twice with Amazon SES #3077
|
||||
- do not delete messages without Message-IDs as duplicates #3095
|
||||
- assign replies from a different email address to the correct chat #3119
|
||||
- assign outgoing private replies to the correct chat #3177
|
||||
- assing outgoing private replies to the correct chat #3177
|
||||
- start ephemeral timer when seen status is synchronized via IMAP #3122
|
||||
- do not create empty contact requests with "setup changed" messages;
|
||||
instead, send a "setup changed" message into all chats we share with the peer #3187
|
||||
@@ -822,7 +790,7 @@
|
||||
- don't watch Sent folder by default #3025
|
||||
- use webxdc app name in chatlist/quotes/replies etc. #3027
|
||||
- make it possible to cancel message sending by removing the message #3034,
|
||||
this was previously removed in 1.71.0 #2939
|
||||
this was previosuly removed in 1.71.0 #2939
|
||||
- synchronize Seen flags only on watched folders to speed up
|
||||
folder scanning #3041
|
||||
- remove direct dependency on `byteorder` crate #3031
|
||||
@@ -2044,7 +2012,7 @@
|
||||
- #1043 avoid potential crashes in malformed From/Chat-Disposition... headers
|
||||
|
||||
- #1045 #1041 #1038 #1035 #1034 #1029 #1025 various cleanups and doc
|
||||
improvements
|
||||
improvments
|
||||
|
||||
## 1.0.0-beta.16
|
||||
|
||||
@@ -2123,7 +2091,7 @@
|
||||
- trigger reconnect more often on imap error states. Should fix an
|
||||
issue observed when trying to empty a folder. @hpk42
|
||||
|
||||
- un-split qr tests: we fixed qr-securejoin protocol flakiness
|
||||
- un-split qr tests: we fixed qr-securejoin protocol flakyness
|
||||
last weeks. @hpk42
|
||||
|
||||
## 1.0.0-beta.10
|
||||
@@ -2161,7 +2129,7 @@
|
||||
|
||||
- fix moving self-sent messages, thanks @r10s, @flub, @hpk42
|
||||
|
||||
- fix flakiness/sometimes-failing verified/join-protocols,
|
||||
- fix flakyness/sometimes-failing verified/join-protocols,
|
||||
thanks @flub, @r10s, @hpk42
|
||||
|
||||
- fix reply-to-encrypted message to keep encryption
|
||||
@@ -2180,7 +2148,7 @@
|
||||
|
||||
- fixes imap-protocol parsing bugs that lead to infinitely
|
||||
repeated crashing while trying to receive messages with
|
||||
a subject that contained non-utf8. thanks @link2xt
|
||||
a subjec that contained non-utf8. thanks @link2xt
|
||||
|
||||
- fixed logic to find encryption subkey -- previously
|
||||
delta chat would use the primary key for encryption
|
||||
|
||||
Generated
+459
-507
File diff suppressed because it is too large
Load Diff
+5
-5
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.111.0"
|
||||
version = "1.109.0"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
rust-version = "1.63"
|
||||
@@ -17,12 +17,11 @@ opt-level = 0
|
||||
# This does not apply to crates in the workspace.
|
||||
# <https://doc.rust-lang.org/cargo/reference/profiles.html#overrides>
|
||||
[profile.dev.package."*"]
|
||||
opt-level = "z"
|
||||
opt-level = 3
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
panic = 'abort'
|
||||
opt-level = "z"
|
||||
|
||||
[dependencies]
|
||||
deltachat_derive = { path = "./deltachat_derive" }
|
||||
@@ -32,13 +31,14 @@ ratelimit = { path = "./deltachat-ratelimit" }
|
||||
anyhow = "1"
|
||||
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-native-tls = { version = "0.4", default-features = false, features = ["runtime-tokio"] }
|
||||
async-smtp = { version = "0.8", default-features = false, features = ["runtime-tokio"] }
|
||||
async_zip = { version = "0.0.9", default-features = false, features = ["deflate"] }
|
||||
backtrace = "0.3"
|
||||
base64 = "0.21"
|
||||
bitflags = "1.3"
|
||||
chrono = { version = "0.4", default-features=false, features = ["clock", "std"] }
|
||||
crossbeam-queue = "0.3"
|
||||
email = { git = "https://github.com/deltachat/rust-email", branch = "master" }
|
||||
encoded-words = { git = "https://github.com/async-email/encoded-words", branch = "master" }
|
||||
escaper = "0.1"
|
||||
@@ -52,12 +52,12 @@ kamadak-exif = "0.5"
|
||||
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
|
||||
libc = "0.2"
|
||||
mailparse = "0.14"
|
||||
native-tls = "0.2"
|
||||
num_cpus = "1.15"
|
||||
num-derive = "0.3"
|
||||
num-traits = "0.2"
|
||||
once_cell = "1.17.0"
|
||||
percent-encoding = "2.2"
|
||||
parking_lot = "0.12"
|
||||
pgp = { version = "0.9", default-features = false }
|
||||
pretty_env_logger = { version = "0.4", optional = true }
|
||||
qrcodegen = "1.7.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.111.0"
|
||||
version = "1.109.0"
|
||||
description = "Deltachat FFI"
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
@@ -29,5 +29,5 @@ once_cell = "1.17.0"
|
||||
[features]
|
||||
default = ["vendored"]
|
||||
vendored = ["deltachat/vendored"]
|
||||
jsonrpc = ["dep:deltachat-jsonrpc"]
|
||||
jsonrpc = ["deltachat-jsonrpc"]
|
||||
|
||||
|
||||
@@ -612,7 +612,7 @@ int dc_all_work_done (dc_context_t* context);
|
||||
* While dc_configure() returns immediately,
|
||||
* the started configuration-job may take a while.
|
||||
*
|
||||
* During configuration, #DC_EVENT_CONFIGURE_PROGRESS events are emitted;
|
||||
* During configuration, #DC_EVENT_CONFIGURE_PROGRESS events are emmited;
|
||||
* they indicate a successful configuration as well as errors
|
||||
* and may be used to create a progress bar.
|
||||
*
|
||||
@@ -869,7 +869,7 @@ uint32_t dc_get_chat_id_by_contact_id (dc_context_t* context, uint32_t co
|
||||
* @param context The context object as returned from dc_context_new().
|
||||
* @param chat_id The chat ID to send the message to.
|
||||
* @param msg The message object to send to the chat defined by the chat ID.
|
||||
* On success, msg_id and state of the object are set up,
|
||||
* On succcess, msg_id and state of the object are set up,
|
||||
* The function does not take ownership of the object,
|
||||
* so you have to free it using dc_msg_unref() as usual.
|
||||
* @return The ID of the message that is being prepared.
|
||||
@@ -880,7 +880,7 @@ uint32_t dc_prepare_msg (dc_context_t* context, uint32_t ch
|
||||
/**
|
||||
* Send a message defined by a dc_msg_t object to a chat.
|
||||
*
|
||||
* Sends the event #DC_EVENT_MSGS_CHANGED on success.
|
||||
* Sends the event #DC_EVENT_MSGS_CHANGED on succcess.
|
||||
* However, this does not imply, the message really reached the recipient -
|
||||
* sending may be delayed e.g. due to network problems. However, from your
|
||||
* view, you're done with the message. Sooner or later it will find its way.
|
||||
@@ -909,7 +909,7 @@ uint32_t dc_prepare_msg (dc_context_t* context, uint32_t ch
|
||||
* @param chat_id The chat ID to send the message to.
|
||||
* If dc_prepare_msg() was called before, this parameter can be 0.
|
||||
* @param msg The message object to send to the chat defined by the chat ID.
|
||||
* On success, msg_id of the object is set up,
|
||||
* On succcess, msg_id of the object is set up,
|
||||
* The function does not take ownership of the object,
|
||||
* so you have to free it using dc_msg_unref() as usual.
|
||||
* @return The ID of the message that is about to be sent. 0 in case of errors.
|
||||
@@ -926,7 +926,7 @@ uint32_t dc_send_msg (dc_context_t* context, uint32_t ch
|
||||
* @param chat_id The chat ID to send the message to.
|
||||
* If dc_prepare_msg() was called before, this parameter can be 0.
|
||||
* @param msg The message object to send to the chat defined by the chat ID.
|
||||
* On success, msg_id of the object is set up,
|
||||
* On succcess, msg_id of the object is set up,
|
||||
* The function does not take ownership of the object,
|
||||
* so you have to free it using dc_msg_unref() as usual.
|
||||
* @return The ID of the message that is about to be sent. 0 in case of errors.
|
||||
@@ -937,7 +937,7 @@ uint32_t dc_send_msg_sync (dc_context_t* context, uint32
|
||||
/**
|
||||
* Send a simple text message a given chat.
|
||||
*
|
||||
* Sends the event #DC_EVENT_MSGS_CHANGED on success.
|
||||
* Sends the event #DC_EVENT_MSGS_CHANGED on succcess.
|
||||
* However, this does not imply, the message really reached the recipient -
|
||||
* sending may be delayed e.g. due to network problems. However, from your
|
||||
* view, you're done with the message. Sooner or later it will find its way.
|
||||
@@ -1146,7 +1146,7 @@ void dc_set_draft (dc_context_t* context, uint32_t ch
|
||||
* // not now and not when this code is executed again
|
||||
* dc_add_device_msg(context, "update-123", NULL);
|
||||
* } else {
|
||||
* // welcome message was not added now, this is an older installation,
|
||||
* // welcome message was not added now, this is an oder installation,
|
||||
* // add a changelog
|
||||
* dc_add_device_msg(context, "update-123", changelog_msg);
|
||||
* }
|
||||
@@ -3184,7 +3184,7 @@ dc_lot_t* dc_chatlist_get_summary (const dc_chatlist_t* chatlist, siz
|
||||
* it takes the chat ID and the message ID as returned by dc_chatlist_get_chat_id() and dc_chatlist_get_msg_id()
|
||||
* as arguments. The chatlist object itself is not needed directly.
|
||||
*
|
||||
* This maybe useful if you convert the complete object into a different representation
|
||||
* This maybe useful if you convert the complete object into a different represenation
|
||||
* as done e.g. in the node-bindings.
|
||||
* If you have access to the chatlist object in some way, using this function is not recommended,
|
||||
* use dc_chatlist_get_summary() in this case instead.
|
||||
@@ -6341,7 +6341,7 @@ void dc_event_unref(dc_event_t* event);
|
||||
///
|
||||
/// Used in status messages.
|
||||
///
|
||||
/// @deprecated 2022-09-10
|
||||
/// @deperecated 2022-09-10
|
||||
#define DC_STR_EPHEMERAL_MINUTE 77
|
||||
|
||||
/// "Message deletion timer is set to 1 hour."
|
||||
|
||||
+27
-16
@@ -2661,7 +2661,7 @@ pub unsafe fn dc_array_is_independent(
|
||||
///
|
||||
/// This is the structure behind [dc_chatlist_t] which is the opaque
|
||||
/// structure representing a chatlist in the FFI API. It exists
|
||||
/// because the FFI API has a reference from the message to the
|
||||
/// because the FFI API has a refernce from the message to the
|
||||
/// context, but the Rust API does not, so the FFI layer needs to glue
|
||||
/// these together.
|
||||
pub struct ChatlistWrapper {
|
||||
@@ -2805,7 +2805,7 @@ pub unsafe extern "C" fn dc_chatlist_get_context(
|
||||
///
|
||||
/// This is the structure behind [dc_chat_t] which is the opaque
|
||||
/// structure representing a chat in the FFI API. It exists
|
||||
/// because the FFI API has a reference from the message to the
|
||||
/// because the FFI API has a refernce from the message to the
|
||||
/// context, but the Rust API does not, so the FFI layer needs to glue
|
||||
/// these together.
|
||||
pub struct ChatWrapper {
|
||||
@@ -3061,7 +3061,7 @@ pub unsafe extern "C" fn dc_chat_get_info_json(
|
||||
///
|
||||
/// This is the structure behind [dc_msg_t] which is the opaque
|
||||
/// structure representing a message in the FFI API. It exists
|
||||
/// because the FFI API has a reference from the message to the
|
||||
/// because the FFI API has a refernce from the message to the
|
||||
/// context, but the Rust API does not, so the FFI layer needs to glue
|
||||
/// these together.
|
||||
pub struct MessageWrapper {
|
||||
@@ -3814,7 +3814,7 @@ pub unsafe extern "C" fn dc_msg_force_plaintext(msg: *mut dc_msg_t) {
|
||||
///
|
||||
/// This is the structure behind [dc_contact_t] which is the opaque
|
||||
/// structure representing a contact in the FFI API. It exists
|
||||
/// because the FFI API has a reference from the message to the
|
||||
/// because the FFI API has a refernce from the message to the
|
||||
/// context, but the Rust API does not, so the FFI layer needs to glue
|
||||
/// these together.
|
||||
pub struct ContactWrapper {
|
||||
@@ -4607,22 +4607,33 @@ mod jsonrpc {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let account_manager = &*account_manager;
|
||||
let events = block_on(account_manager.read()).get_event_emitter();
|
||||
let cmd_api = deltachat_jsonrpc::api::CommandApi::from_arc(account_manager.inner.clone());
|
||||
let cmd_api =
|
||||
deltachat_jsonrpc::api::CommandApi::from_arc((*account_manager).inner.clone());
|
||||
|
||||
let (request_handle, receiver) = RpcClient::new();
|
||||
let handle = RpcSession::new(request_handle.clone(), cmd_api);
|
||||
let request_handle2 = request_handle.clone();
|
||||
let handle = RpcSession::new(request_handle, cmd_api);
|
||||
|
||||
let event_thread = spawn(async move {
|
||||
while let Some(event) = events.recv().await {
|
||||
let event = event_to_json_rpc_notification(event);
|
||||
request_handle
|
||||
.send_notification("event", Some(event))
|
||||
.await?;
|
||||
let events = block_on({
|
||||
async {
|
||||
let am = (*account_manager).inner.clone();
|
||||
let ev = am.read().await.get_event_emitter();
|
||||
drop(am);
|
||||
ev
|
||||
}
|
||||
});
|
||||
|
||||
let event_thread = spawn({
|
||||
async move {
|
||||
while let Some(event) = events.recv().await {
|
||||
let event = event_to_json_rpc_notification(event);
|
||||
request_handle2
|
||||
.send_notification("event", Some(event))
|
||||
.await?;
|
||||
}
|
||||
let res: Result<(), anyhow::Error> = Ok(());
|
||||
res
|
||||
}
|
||||
let res: Result<(), anyhow::Error> = Ok(());
|
||||
res
|
||||
});
|
||||
|
||||
let instance = dc_jsonrpc_instance_t {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-jsonrpc"
|
||||
version = "1.111.0"
|
||||
version = "1.109.0"
|
||||
description = "DeltaChat JSON-RPC API"
|
||||
edition = "2021"
|
||||
default-run = "deltachat-jsonrpc-server"
|
||||
@@ -21,15 +21,14 @@ log = "0.4"
|
||||
async-channel = { version = "1.8.0" }
|
||||
futures = { version = "0.3.26" }
|
||||
serde_json = "1.0.91"
|
||||
yerpc = { version = "0.4.3", features = ["anyhow_expose"] }
|
||||
yerpc = { version = "^0.4.0", features = ["anyhow_expose"] }
|
||||
typescript-type-def = { version = "0.5.5", features = ["json_value"] }
|
||||
tokio = { version = "1.25.0" }
|
||||
sanitize-filename = "0.4"
|
||||
walkdir = "2.3.2"
|
||||
base64 = "0.21"
|
||||
|
||||
# optional dependencies
|
||||
axum = { version = "0.6.6", optional = true, features = ["ws"] }
|
||||
axum = { version = "0.5.9", optional = true, features = ["ws"] }
|
||||
env_logger = { version = "0.10.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -37,6 +36,5 @@ tokio = { version = "1.25.0", features = ["full", "rt-multi-thread"] }
|
||||
|
||||
|
||||
[features]
|
||||
default = ["vendored"]
|
||||
webserver = ["dep:env_logger", "dep:axum", "tokio/full", "yerpc/support-axum"]
|
||||
vendored = ["deltachat/vendored"]
|
||||
default = []
|
||||
webserver = ["env_logger", "axum", "tokio/full", "yerpc/support-axum"]
|
||||
|
||||
@@ -35,7 +35,7 @@ The server can be configured with environment variables:
|
||||
|`DC_PORT`|`20808`|port to listen on|
|
||||
|`DC_ACCOUNTS_PATH`|`./accounts`|path to storage directory|
|
||||
|
||||
If you are targeting other architectures (like KaiOS or Android), the webserver binary can be cross-compiled easily with [rust-cross](https://github.com/cross-rs/cross):
|
||||
If you are targetting other architectures (like KaiOS or Android), the webserver binary can be cross-compiled easily with [rust-cross](https://github.com/cross-rs/cross):
|
||||
|
||||
```sh
|
||||
cross build --features=webserver --target armv7-linux-androideabi --release
|
||||
|
||||
@@ -41,7 +41,6 @@ use types::account::Account;
|
||||
use types::chat::FullChat;
|
||||
use types::chat_list::ChatListEntry;
|
||||
use types::contact::ContactObject;
|
||||
use types::message::MessageData;
|
||||
use types::message::MessageObject;
|
||||
use types::provider_info::ProviderInfo;
|
||||
use types::webxdc::WebxdcMessageInfo;
|
||||
@@ -1463,23 +1462,6 @@ impl CommandApi {
|
||||
WebxdcMessageInfo::get_for_message(&ctx, MsgId::new(instance_msg_id)).await
|
||||
}
|
||||
|
||||
/// Get blob encoded as base64 from a webxdc message
|
||||
///
|
||||
/// path is the path of the file within webxdc archive
|
||||
async fn get_webxdc_blob(
|
||||
&self,
|
||||
account_id: u32,
|
||||
instance_msg_id: u32,
|
||||
path: String,
|
||||
) -> Result<String> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let message = Message::load_from_db(&ctx, MsgId::new(instance_msg_id)).await?;
|
||||
let blob = message.get_webxdc_blob(&ctx, &path).await?;
|
||||
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
Ok(general_purpose::STANDARD_NO_PAD.encode(blob))
|
||||
}
|
||||
|
||||
/// Forward messages to another chat.
|
||||
///
|
||||
/// All types of messages can be forwarded,
|
||||
@@ -1529,48 +1511,6 @@ impl CommandApi {
|
||||
Ok(message_id.to_u32())
|
||||
}
|
||||
|
||||
async fn send_msg(&self, account_id: u32, chat_id: u32, data: MessageData) -> Result<u32> {
|
||||
let ctx = self.get_context(account_id).await?;
|
||||
let mut message = Message::new(if let Some(viewtype) = data.viewtype {
|
||||
viewtype.into()
|
||||
} else if data.file.is_some() {
|
||||
Viewtype::File
|
||||
} else {
|
||||
Viewtype::Text
|
||||
});
|
||||
if data.text.is_some() {
|
||||
message.set_text(data.text);
|
||||
}
|
||||
if data.html.is_some() {
|
||||
message.set_html(data.html);
|
||||
}
|
||||
if data.override_sender_name.is_some() {
|
||||
message.set_override_sender_name(data.override_sender_name);
|
||||
}
|
||||
if let Some(file) = data.file {
|
||||
message.set_file(file, None);
|
||||
}
|
||||
if let Some((latitude, longitude)) = data.location {
|
||||
message.set_location(latitude, longitude);
|
||||
}
|
||||
if let Some(id) = data.quoted_message_id {
|
||||
message
|
||||
.set_quote(
|
||||
&ctx,
|
||||
Some(
|
||||
&Message::load_from_db(&ctx, MsgId::new(id))
|
||||
.await
|
||||
.context("message to quote could not be loaded")?,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
let msg_id = chat::send_msg(&ctx, ChatId::new(chat_id), &mut message)
|
||||
.await?
|
||||
.to_u32();
|
||||
Ok(msg_id)
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
// functions for the composer
|
||||
// the composer is the message input field
|
||||
|
||||
@@ -502,15 +502,3 @@ impl From<ChatItem> for JSONRPCMessageListItem {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, TypeDef)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MessageData {
|
||||
pub text: Option<String>,
|
||||
pub html: Option<String>,
|
||||
pub viewtype: Option<MessageViewtype>,
|
||||
pub file: Option<String>,
|
||||
pub location: Option<(f64, f64)>,
|
||||
pub override_sender_name: Option<String>,
|
||||
pub quoted_message_id: Option<u32>,
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use typescript_type_def::TypeDef;
|
||||
pub struct ProviderInfo {
|
||||
pub before_login_hint: String,
|
||||
pub overview_page: String,
|
||||
pub status: u32, // in reality this is an enum, but for simplicity and because it gets converted into a number anyway, we use an u32 here.
|
||||
pub status: u32, // in reality this is an enum, but for simlicity and because it gets converted into a number anyway, we use an u32 here.
|
||||
}
|
||||
|
||||
impl ProviderInfo {
|
||||
|
||||
@@ -4,9 +4,3 @@ docs
|
||||
coverage
|
||||
yarn*
|
||||
package-lock.json
|
||||
.prettierignore
|
||||
example.html
|
||||
report_api_coverage.mjs
|
||||
scripts
|
||||
dist/example
|
||||
dist/test
|
||||
@@ -14,7 +14,7 @@ async function run() {
|
||||
throw new Error(
|
||||
"USAGE: node node-add-account.js <EMAILADDRESS> <PASSWORD>"
|
||||
);
|
||||
console.log(`creating account for ${email}`);
|
||||
console.log(`creating acccount for ${email}`);
|
||||
const id = await delta.rpc.addAccount();
|
||||
console.log(`created account id ${id}`);
|
||||
await delta.rpc.setConfig(id, "addr", email);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"dependencies": {
|
||||
"@deltachat/tiny-emitter": "3.0.0",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"yerpc": "^0.4.3"
|
||||
"yerpc": "^0.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.21",
|
||||
@@ -14,7 +14,7 @@
|
||||
"c8": "^7.10.0",
|
||||
"chai": "^4.3.4",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"esbuild": "^0.17.9",
|
||||
"esbuild": "^0.14.11",
|
||||
"http-server": "^14.1.1",
|
||||
"mocha": "^9.1.1",
|
||||
"node-fetch": "^2.6.1",
|
||||
@@ -24,19 +24,12 @@
|
||||
"typescript": "^4.5.5",
|
||||
"ws": "^8.5.0"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/deltachat.js",
|
||||
"require": "./dist/deltachat.cjs"
|
||||
}
|
||||
},
|
||||
"license": "MPL-2.0",
|
||||
"main": "dist/deltachat.js",
|
||||
"name": "@deltachat/jsonrpc-client",
|
||||
"scripts": {
|
||||
"build": "run-s generate-bindings extract-constants build:tsc build:bundle build:cjs",
|
||||
"build": "run-s generate-bindings extract-constants build:tsc build:bundle",
|
||||
"build:bundle": "esbuild --format=esm --bundle dist/deltachat.js --outfile=dist/deltachat.bundle.js",
|
||||
"build:cjs": "esbuild --format=cjs --bundle --packages=external dist/deltachat.js --outfile=dist/deltachat.cjs",
|
||||
"build:tsc": "tsc",
|
||||
"docs": "typedoc --out docs deltachat.ts",
|
||||
"example": "run-s build example:build example:start",
|
||||
@@ -55,5 +48,5 @@
|
||||
},
|
||||
"type": "module",
|
||||
"types": "dist/deltachat.d.ts",
|
||||
"version": "1.111.0"
|
||||
"version": "1.109.0"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { readFileSync } from "fs";
|
||||
// only checks for the coverage of the api functions in bindings.ts for now
|
||||
// only checks for the coverge of the api functions in bindings.ts for now
|
||||
const generatedFile = "typescript/generated/client.ts";
|
||||
const json = JSON.parse(readFileSync("./coverage/coverage-final.json"));
|
||||
const jsonCoverage =
|
||||
|
||||
@@ -53,7 +53,7 @@ describe("basic tests", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
describe("account management", () => {
|
||||
describe("account managment", () => {
|
||||
it("should create account", async () => {
|
||||
const res = await dc.rpc.addAccount();
|
||||
assert((await dc.rpc.getAllAccountIds()).length === 1);
|
||||
@@ -73,7 +73,7 @@ describe("basic tests", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("contact management", function () {
|
||||
describe("contact managment", function () {
|
||||
let accountId: number;
|
||||
before(async () => {
|
||||
accountId = await dc.rpc.addAccount();
|
||||
@@ -103,7 +103,7 @@ describe("basic tests", () => {
|
||||
accountId = await dc.rpc.addAccount();
|
||||
});
|
||||
|
||||
it("set and retrieve", async function () {
|
||||
it("set and retrive", async function () {
|
||||
await dc.rpc.setConfig(accountId, "addr", "valid@email");
|
||||
assert((await dc.rpc.getConfig(accountId, "addr")) == "valid@email");
|
||||
});
|
||||
@@ -115,11 +115,11 @@ describe("basic tests", () => {
|
||||
await expect(dc.rpc.getConfig(accountId, "invalid_key")).to.be.eventually
|
||||
.rejected;
|
||||
});
|
||||
it("set and retrieve ui.*", async function () {
|
||||
it("set and retrive ui.*", async function () {
|
||||
await dc.rpc.setConfig(accountId, "ui.chat_bg", "color:red");
|
||||
assert((await dc.rpc.getConfig(accountId, "ui.chat_bg")) == "color:red");
|
||||
});
|
||||
it("set and retrieve (batch)", async function () {
|
||||
it("set and retrive (batch)", async function () {
|
||||
const config = { addr: "valid@email", mail_pw: "1234" };
|
||||
await dc.rpc.batchSetConfig(accountId, config);
|
||||
const retrieved = await dc.rpc.batchGetConfig(
|
||||
@@ -128,7 +128,7 @@ describe("basic tests", () => {
|
||||
);
|
||||
expect(retrieved).to.deep.equal(config);
|
||||
});
|
||||
it("set and retrieve ui.* (batch)", async function () {
|
||||
it("set and retrive ui.* (batch)", async function () {
|
||||
const config = {
|
||||
"ui.chat_bg": "color:green",
|
||||
"ui.enter_key_sends": "true",
|
||||
@@ -140,7 +140,7 @@ describe("basic tests", () => {
|
||||
);
|
||||
expect(retrieved).to.deep.equal(config);
|
||||
});
|
||||
it("set and retrieve mixed(ui and core) (batch)", async function () {
|
||||
it("set and retrive mixed(ui and core) (batch)", async function () {
|
||||
const config = {
|
||||
"ui.chat_bg": "color:yellow",
|
||||
"ui.enter_key_sends": "false",
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("online tests", function () {
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(
|
||||
"Missing DCC_NEW_TMP_EMAIL environment variable!, skip integration tests"
|
||||
"Missing DCC_NEW_TMP_EMAIL environment variable!, skip intergration tests"
|
||||
);
|
||||
this.skip();
|
||||
}
|
||||
@@ -36,7 +36,7 @@ describe("online tests", function () {
|
||||
account1 = await createTempUser(process.env.DCC_NEW_TMP_EMAIL);
|
||||
if (!account1 || !account1.email || !account1.password) {
|
||||
console.log(
|
||||
"We didn't got back an account from the api, skip integration tests"
|
||||
"We didn't got back an account from the api, skip intergration tests"
|
||||
);
|
||||
this.skip();
|
||||
}
|
||||
@@ -44,7 +44,7 @@ describe("online tests", function () {
|
||||
account2 = await createTempUser(process.env.DCC_NEW_TMP_EMAIL);
|
||||
if (!account2 || !account2.email || !account2.password) {
|
||||
console.log(
|
||||
"We didn't got back an account2 from the api, skip integration tests"
|
||||
"We didn't got back an account2 from the api, skip intergration tests"
|
||||
);
|
||||
this.skip();
|
||||
}
|
||||
@@ -74,7 +74,7 @@ describe("online tests", function () {
|
||||
accountsConfigured = true;
|
||||
});
|
||||
|
||||
it("send and receive text message", async function () {
|
||||
it("send and recieve text message", async function () {
|
||||
if (!accountsConfigured) {
|
||||
this.skip();
|
||||
}
|
||||
@@ -106,7 +106,7 @@ describe("online tests", function () {
|
||||
expect(message.text).equal("Hello");
|
||||
});
|
||||
|
||||
it("send and receive text message roundtrip, encrypted on answer onwards", async function () {
|
||||
it("send and recieve text message roundtrip, encrypted on answer onwards", async function () {
|
||||
if (!accountsConfigured) {
|
||||
this.skip();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-repl"
|
||||
version = "1.111.0"
|
||||
license = "MPL-2.0"
|
||||
version = "1.109.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -12,7 +11,7 @@ dirs = "4"
|
||||
log = "0.4.16"
|
||||
pretty_env_logger = "0.4"
|
||||
rusqlite = "0.28"
|
||||
rustyline = "11"
|
||||
rustyline = "10"
|
||||
tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -29,7 +29,7 @@ use tokio::fs;
|
||||
|
||||
/// Reset database tables.
|
||||
/// Argument is a bitmask, executing single or multiple actions in one call.
|
||||
/// e.g. bitmask 7 triggers actions defined with bits 1, 2 and 4.
|
||||
/// e.g. bitmask 7 triggers actions definded with bits 1, 2 and 4.
|
||||
async fn reset_tables(context: &Context, bits: i32) {
|
||||
println!("Resetting tables ({bits})...");
|
||||
if 0 != bits & 1 {
|
||||
@@ -869,7 +869,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
if continue_streaming {
|
||||
println!("Success, streaming should be continued.");
|
||||
} else {
|
||||
println!("Success, streaming can be stopped.");
|
||||
println!("Success, streaming can be stoppped.");
|
||||
}
|
||||
}
|
||||
"dellocations" => {
|
||||
|
||||
@@ -350,8 +350,8 @@ async fn start(args: Vec<String>) -> Result<(), Error> {
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
// TODO: ignore "set mail_pw"
|
||||
rl.add_history_entry(line.as_str())?;
|
||||
let should_continue = Handle::current().block_on(async {
|
||||
rl.add_history_entry(line.as_str());
|
||||
let contine = Handle::current().block_on(async {
|
||||
match handle_cmd(line.trim(), ctx.clone(), &mut selected_chat).await {
|
||||
Ok(ExitResult::Continue) => true,
|
||||
Ok(ExitResult::Exit) => {
|
||||
@@ -365,7 +365,7 @@ async fn start(args: Vec<String>) -> Result<(), Error> {
|
||||
}
|
||||
});
|
||||
|
||||
if !should_continue {
|
||||
if !contine {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,35 +25,7 @@ deltachat_rpc_client = [
|
||||
line-length = 120
|
||||
|
||||
[tool.ruff]
|
||||
select = [
|
||||
"E", "W", # pycodestyle
|
||||
"F", # Pyflakes
|
||||
"N", # pep8-naming
|
||||
"I", # isort
|
||||
|
||||
"ARG", # flake8-unused-arguments
|
||||
"B", # flake8-bugbear
|
||||
"C4", # flake8-comprehensions
|
||||
"COM", # flake8-commas
|
||||
"DTZ", # flake8-datetimez
|
||||
"ICN", # flake8-import-conventions
|
||||
"ISC", # flake8-implicit-str-concat
|
||||
"PIE", # flake8-pie
|
||||
"PT", # flake8-pytest-style
|
||||
"RET", # flake8-return
|
||||
"SIM", # flake8-simplify
|
||||
"TCH", # flake8-type-checking
|
||||
"TID", # flake8-tidy-imports
|
||||
"YTT", # flake8-2020
|
||||
|
||||
"ERA", # eradicate
|
||||
|
||||
"PLC", # Pylint Convention
|
||||
"PLE", # Pylint Error
|
||||
"PLW", # Pylint Warning
|
||||
|
||||
"RUF006" # asyncio-dangling-task
|
||||
]
|
||||
select = ["E", "F", "W", "N", "YTT", "B", "C4", "ISC", "ICN", "PT", "RET", "SIM", "TID", "ARG", "DTZ", "ERA", "PLC", "PLE", "PLW", "PIE", "COM"]
|
||||
line-length = 120
|
||||
|
||||
[tool.isort]
|
||||
|
||||
@@ -27,7 +27,7 @@ def _to_attrdict(obj):
|
||||
|
||||
|
||||
class AttrDict(dict):
|
||||
"""Dictionary that allows accessing values using the "dot notation" as attributes."""
|
||||
"""Dictionary that allows accessing values usin the "dot notation" as attributes."""
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__({_camel_to_snake(key): _to_attrdict(value) for key, value in dict(*args, **kwargs).items()})
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, List, Optional, Tuple, Union
|
||||
from dataclasses import dataclass
|
||||
|
||||
from ._utils import AttrDict
|
||||
from .chat import Chat
|
||||
from .const import ChatlistFlag, ContactFlag, SpecialContactId
|
||||
from .contact import Contact
|
||||
from .message import Message
|
||||
from .rpc import Rpc
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .deltachat import DeltaChat
|
||||
from .rpc import Rpc
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -20,7 +20,7 @@ class Account:
|
||||
id: int
|
||||
|
||||
@property
|
||||
def _rpc(self) -> "Rpc":
|
||||
def _rpc(self) -> Rpc:
|
||||
return self.manager.rpc
|
||||
|
||||
async def wait_for_event(self) -> AttrDict:
|
||||
@@ -158,7 +158,7 @@ class Account:
|
||||
:param contact: if a contact is specified only chats including this contact are returned.
|
||||
:param archived_only: if True only archived chats are returned.
|
||||
:param for_forwarding: if True the chat list is sorted with "Saved messages" at the top
|
||||
and without "Device chat" and contact requests.
|
||||
and withot "Device chat" and contact requests.
|
||||
:param no_specials: if True archive link is not added to the list.
|
||||
:param alldone_hint: if True the "all done hint" special chat will be added to the list
|
||||
as needed.
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import calendar
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union
|
||||
from dataclasses import dataclass
|
||||
|
||||
from ._utils import AttrDict
|
||||
from .const import ChatVisibility, ViewType
|
||||
from .const import ChatVisibility
|
||||
from .contact import Contact
|
||||
from .message import Message
|
||||
from .rpc import Rpc
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from datetime import datetime
|
||||
|
||||
from .account import Account
|
||||
from .rpc import Rpc
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -22,7 +21,7 @@ class Chat:
|
||||
id: int
|
||||
|
||||
@property
|
||||
def _rpc(self) -> "Rpc":
|
||||
def _rpc(self) -> Rpc:
|
||||
return self.account._rpc
|
||||
|
||||
async def delete(self) -> None:
|
||||
@@ -108,27 +107,15 @@ class Chat:
|
||||
async def send_message(
|
||||
self,
|
||||
text: Optional[str] = None,
|
||||
html: Optional[str] = None,
|
||||
viewtype: Optional[ViewType] = None,
|
||||
file: Optional[str] = None,
|
||||
location: Optional[Tuple[float, float]] = None,
|
||||
override_sender_name: Optional[str] = None,
|
||||
quoted_msg: Optional[Union[int, Message]] = None,
|
||||
) -> Message:
|
||||
"""Send a message and return the resulting Message instance."""
|
||||
if isinstance(quoted_msg, Message):
|
||||
quoted_msg = quoted_msg.id
|
||||
|
||||
draft = {
|
||||
"text": text,
|
||||
"html": html,
|
||||
"viewtype": viewtype,
|
||||
"file": file,
|
||||
"location": location,
|
||||
"overrideSenderName": override_sender_name,
|
||||
"quotedMsg": quoted_msg,
|
||||
}
|
||||
msg_id = await self._rpc.send_msg(self.account.id, self.id, draft)
|
||||
msg_id, _ = await self._rpc.misc_send_msg(self.account.id, self.id, text, file, location, quoted_msg)
|
||||
return Message(self.account, msg_id)
|
||||
|
||||
async def send_text(self, text: str) -> Message:
|
||||
@@ -194,23 +181,19 @@ class Chat:
|
||||
"""Add contacts to this group."""
|
||||
for cnt in contact:
|
||||
if isinstance(cnt, str):
|
||||
contact_id = (await self.account.create_contact(cnt)).id
|
||||
cnt = (await self.account.create_contact(cnt)).id
|
||||
elif not isinstance(cnt, int):
|
||||
contact_id = cnt.id
|
||||
else:
|
||||
contact_id = cnt
|
||||
await self._rpc.add_contact_to_chat(self.account.id, self.id, contact_id)
|
||||
cnt = cnt.id
|
||||
await self._rpc.add_contact_to_chat(self.account.id, self.id, cnt)
|
||||
|
||||
async def remove_contact(self, *contact: Union[int, str, Contact]) -> None:
|
||||
"""Remove members from this group."""
|
||||
for cnt in contact:
|
||||
if isinstance(cnt, str):
|
||||
contact_id = (await self.account.create_contact(cnt)).id
|
||||
cnt = (await self.account.create_contact(cnt)).id
|
||||
elif not isinstance(cnt, int):
|
||||
contact_id = cnt.id
|
||||
else:
|
||||
contact_id = cnt
|
||||
await self._rpc.remove_contact_from_chat(self.account.id, self.id, contact_id)
|
||||
cnt = cnt.id
|
||||
await self._rpc.remove_contact_from_chat(self.account.id, self.id, cnt)
|
||||
|
||||
async def get_contacts(self) -> List[Contact]:
|
||||
"""Get the contacts belonging to this chat.
|
||||
@@ -234,8 +217,8 @@ class Chat:
|
||||
async def get_locations(
|
||||
self,
|
||||
contact: Optional[Contact] = None,
|
||||
timestamp_from: Optional["datetime"] = None,
|
||||
timestamp_to: Optional["datetime"] = None,
|
||||
timestamp_from: Optional[datetime] = None,
|
||||
timestamp_to: Optional[datetime] = None,
|
||||
) -> List[AttrDict]:
|
||||
"""Get list of location snapshots for the given contact in the given timespan."""
|
||||
time_from = calendar.timegm(timestamp_from.utctimetuple()) if timestamp_from else 0
|
||||
@@ -246,9 +229,9 @@ class Chat:
|
||||
locations = []
|
||||
contacts: Dict[int, Contact] = {}
|
||||
for loc in result:
|
||||
location = AttrDict(loc)
|
||||
location["chat"] = self
|
||||
location["contact"] = contacts.setdefault(location.contact_id, Contact(self.account, location.contact_id))
|
||||
location["message"] = Message(self.account, location.msg_id)
|
||||
locations.append(location)
|
||||
loc = AttrDict(loc)
|
||||
loc["chat"] = self
|
||||
loc["contact"] = contacts.setdefault(loc.contact_id, Contact(self.account, loc.contact_id))
|
||||
loc["message"] = Message(self.account, loc.msg_id)
|
||||
locations.append(loc)
|
||||
return locations
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import inspect
|
||||
import logging
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Callable,
|
||||
Coroutine,
|
||||
Dict,
|
||||
@@ -14,6 +13,8 @@ from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
from deltachat_rpc_client.account import Account
|
||||
|
||||
from ._utils import (
|
||||
AttrDict,
|
||||
parse_system_add_remove,
|
||||
@@ -30,16 +31,13 @@ from .events import (
|
||||
RawEvent,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from deltachat_rpc_client.account import Account
|
||||
|
||||
|
||||
class Client:
|
||||
"""Simple Delta Chat client that listen to events of a single account."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
account: "Account",
|
||||
account: Account,
|
||||
hooks: Optional[Iterable[Tuple[Callable, Union[type, EventFilter]]]] = None,
|
||||
logger: Optional[logging.Logger] = None,
|
||||
) -> None:
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from dataclasses import dataclass
|
||||
|
||||
from ._utils import AttrDict
|
||||
from .rpc import Rpc
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .account import Account
|
||||
from .chat import Chat
|
||||
from .rpc import Rpc
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -21,7 +21,7 @@ class Contact:
|
||||
id: int
|
||||
|
||||
@property
|
||||
def _rpc(self) -> "Rpc":
|
||||
def _rpc(self) -> Rpc:
|
||||
return self.account._rpc
|
||||
|
||||
async def block(self) -> None:
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
from typing import TYPE_CHECKING, Dict, List
|
||||
from typing import Dict, List
|
||||
|
||||
from ._utils import AttrDict
|
||||
from .account import Account
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .rpc import Rpc
|
||||
from .rpc import Rpc
|
||||
|
||||
|
||||
class DeltaChat:
|
||||
@@ -13,7 +11,7 @@ class DeltaChat:
|
||||
This is the root of the object oriented API.
|
||||
"""
|
||||
|
||||
def __init__(self, rpc: "Rpc") -> None:
|
||||
def __init__(self, rpc: Rpc) -> None:
|
||||
self.rpc = rpc
|
||||
|
||||
async def add_account(self) -> Account:
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
import inspect
|
||||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Callable, Iterable, Iterator, Optional, Set, Tuple, Union
|
||||
from typing import Callable, Iterable, Iterator, Optional, Set, Tuple, Union
|
||||
|
||||
from ._utils import AttrDict
|
||||
from .const import EventType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ._utils import AttrDict
|
||||
|
||||
|
||||
def _tuple_of(obj, type_: type) -> tuple:
|
||||
if not obj:
|
||||
@@ -82,7 +80,7 @@ class RawEvent(EventFilter):
|
||||
return (self.types, self.func) == (other.types, other.func)
|
||||
return False
|
||||
|
||||
async def filter(self, event: "AttrDict") -> bool:
|
||||
async def filter(self, event: AttrDict) -> bool:
|
||||
if self.types and event.type not in self.types:
|
||||
return False
|
||||
return await self._call_func(event)
|
||||
@@ -120,7 +118,7 @@ class NewMessage(EventFilter):
|
||||
command: Optional[str] = None,
|
||||
is_bot: Optional[bool] = False,
|
||||
is_info: Optional[bool] = None,
|
||||
func: Optional[Callable[["AttrDict"], bool]] = None,
|
||||
func: Optional[Callable[[AttrDict], bool]] = None,
|
||||
) -> None:
|
||||
super().__init__(func=func)
|
||||
self.is_bot = is_bot
|
||||
@@ -159,7 +157,7 @@ class NewMessage(EventFilter):
|
||||
)
|
||||
return False
|
||||
|
||||
async def filter(self, event: "AttrDict") -> bool:
|
||||
async def filter(self, event: AttrDict) -> bool:
|
||||
if self.is_bot is not None and self.is_bot != event.message_snapshot.is_bot:
|
||||
return False
|
||||
if self.is_info is not None and self.is_info != event.message_snapshot.is_info:
|
||||
@@ -201,7 +199,7 @@ class MemberListChanged(EventFilter):
|
||||
return (self.added, self.func) == (other.added, other.func)
|
||||
return False
|
||||
|
||||
async def filter(self, event: "AttrDict") -> bool:
|
||||
async def filter(self, event: AttrDict) -> bool:
|
||||
if self.added is not None and self.added != event.member_added:
|
||||
return False
|
||||
return await self._call_func(event)
|
||||
@@ -233,7 +231,7 @@ class GroupImageChanged(EventFilter):
|
||||
return (self.deleted, self.func) == (other.deleted, other.func)
|
||||
return False
|
||||
|
||||
async def filter(self, event: "AttrDict") -> bool:
|
||||
async def filter(self, event: AttrDict) -> bool:
|
||||
if self.deleted is not None and self.deleted != event.image_deleted:
|
||||
return False
|
||||
return await self._call_func(event)
|
||||
@@ -258,7 +256,7 @@ class GroupNameChanged(EventFilter):
|
||||
return self.func == other.func
|
||||
return False
|
||||
|
||||
async def filter(self, event: "AttrDict") -> bool:
|
||||
async def filter(self, event: AttrDict) -> bool:
|
||||
return await self._call_func(event)
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Union
|
||||
from dataclasses import dataclass
|
||||
|
||||
from ._utils import AttrDict
|
||||
from .contact import Contact
|
||||
from .rpc import Rpc
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .account import Account
|
||||
from .rpc import Rpc
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -18,7 +18,7 @@ class Message:
|
||||
id: int
|
||||
|
||||
@property
|
||||
def _rpc(self) -> "Rpc":
|
||||
def _rpc(self) -> Rpc:
|
||||
return self.account._rpc
|
||||
|
||||
async def send_reaction(self, *reaction: str):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
from typing import AsyncGenerator, List, Optional
|
||||
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import pytest_asyncio
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import asyncio
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
import asyncio
|
||||
|
||||
from deltachat_rpc_client import EventType, events
|
||||
from deltachat_rpc_client.rpc import JsonRpcError
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from deltachat_rpc_client import EventType
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat-rpc-server"
|
||||
version = "1.111.0"
|
||||
version = "1.109.0"
|
||||
description = "DeltaChat JSON-RPC server"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
@@ -13,7 +13,7 @@ categories = ["cryptography", "std", "email"]
|
||||
name = "deltachat-rpc-server"
|
||||
|
||||
[dependencies]
|
||||
deltachat-jsonrpc = { path = "../deltachat-jsonrpc", default-features = false }
|
||||
deltachat-jsonrpc = { path = "../deltachat-jsonrpc" }
|
||||
|
||||
anyhow = "1"
|
||||
env_logger = { version = "0.10.0" }
|
||||
@@ -23,7 +23,3 @@ serde_json = "1.0.91"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tokio = { version = "1.25.0", features = ["io-std"] }
|
||||
yerpc = { version = "0.4.0", features = ["anyhow_expose"] }
|
||||
|
||||
[features]
|
||||
default = ["vendored"]
|
||||
vendored = ["deltachat-jsonrpc/vendored"]
|
||||
|
||||
@@ -5,7 +5,7 @@ use quote::quote;
|
||||
|
||||
use crate::proc_macro::TokenStream;
|
||||
|
||||
// For now, assume (not check) that these macros are applied to enum without
|
||||
// For now, assume (not check) that these macroses are applied to enum without
|
||||
// data. If this assumption is violated, compiler error will point to
|
||||
// generated code, which is not very user-friendly.
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
[advisories]
|
||||
unmaintained = "allow"
|
||||
ignore = [
|
||||
"RUSTSEC-2020-0071",
|
||||
|
||||
# Only affects windows if using non-default allocator (and unmaintained).
|
||||
"RUSTSEC-2021-0145",
|
||||
]
|
||||
|
||||
[bans]
|
||||
# Accept some duplicate versions, ideally we work towards this list
|
||||
# becoming empty. Adding versions forces us to revisit this at least
|
||||
# when upgrading.
|
||||
skip = [
|
||||
{ name = "windows-sys", version = "<0.45" },
|
||||
{ name = "wasi", version = "<0.11" },
|
||||
{ name = "version_check", version = "<0.9" },
|
||||
{ name = "uuid", version = "<1.3" },
|
||||
{ name = "sha2", version = "<0.10" },
|
||||
{ name = "rand_core", version = "<0.6" },
|
||||
{ name = "rand_chacha", version = "<0.3" },
|
||||
{ name = "rand", version = "<0.8" },
|
||||
{ name = "nom", version = "<7.1" },
|
||||
{ name = "idna", version = "<0.3" },
|
||||
{ name = "humantime", version = "<2.1" },
|
||||
{ name = "hermit-abi", version = "<0.3" },
|
||||
{ name = "getrandom", version = "<0.2" },
|
||||
{ name = "quick-error", version = "<2.0" },
|
||||
{ name = "env_logger", version = "<0.10" },
|
||||
{ name = "digest", version = "<0.10" },
|
||||
{ name = "darling_macro", version = "<0.14" },
|
||||
{ name = "darling_core", version = "<0.14" },
|
||||
{ name = "darling", version = "<0.14" },
|
||||
{ name = "block-buffer", version = "<0.10" },
|
||||
{ name = "base64", version = "<0.21" },
|
||||
]
|
||||
|
||||
|
||||
[licenses]
|
||||
allow = [
|
||||
"0BSD",
|
||||
"Apache-2.0",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"CC0-1.0",
|
||||
"MIT",
|
||||
"BSL-1.0", # Boost Software License 1.0
|
||||
"Unicode-DFS-2016",
|
||||
"MPL-2.0",
|
||||
]
|
||||
|
||||
[sources.allow-org]
|
||||
# Organisations which we allow git sources from.
|
||||
github = [
|
||||
"async-email",
|
||||
"deltachat",
|
||||
]
|
||||
+4
-4
@@ -66,13 +66,13 @@ Note that usually a mail is signed by a key that has a UID matching the from add
|
||||
|
||||
#### Upsides:
|
||||
- With this approach, it's easy to switch to a model where the info about the transition is encoded in the PGP key. Since the key is gossiped, the information about the transition will spread virally.
|
||||
- Faster transition: If you send a message to e.g. "Delta Chat Dev", all members of the "sub-group" "delta android" will know of your transition.
|
||||
- Faster transation: If you send a message to e.g. "Delta Chat Dev", all members of the "sub-group" "delta android" will know of your transition.
|
||||
|
||||
### Alternatives and old discussions/plans:
|
||||
|
||||
- Change the contact instead of rewriting the group member lists. This seems to call for more trouble since we will end up with multiple contacts having the same email address.
|
||||
|
||||
- If needed, we could add a header a) indicating that the sender did an address transition or b) listing all the secondary (old) addresses. For now, there is no big enough benefit to warrant introducing another header and its processing on the receiver side (including all the necessary checks and handling of error cases). Instead, we only check for the `Chat-Version` header to prevent accidental transitions when an MUA user sends a message from another email address with the same key.
|
||||
- If needed, we could add a header a) indicating that the sender did an address transition or b) listing all the secondary (old) addresses. For now, there is no big enough benefit to warrant introducing another header and its processing on the receiver side (including all the neccessary checks and handling of error cases). Instead, we only check for the `Chat-Version` header to prevent accidental transitions when an MUA user sends a message from another email address with the same key.
|
||||
|
||||
- The condition for a transition temporarily was:
|
||||
|
||||
@@ -107,7 +107,7 @@ The most obvious alternative would be to create a new contact with the new addre
|
||||
|
||||
#### Upsides:
|
||||
- With this approach, it's easier to switch to a model where the info about the transition is encoded in the PGP key. Since the key is gossiped, the information about the transition will spread virally.
|
||||
- (Also, less important: Slightly faster transition: If you send a message to e.g. "Delta Chat Dev", all members of the "sub-group" "delta android" will know of your transition.)
|
||||
- (Also, less important: Slightly faster transation: If you send a message to e.g. "Delta Chat Dev", all members of the "sub-group" "delta android" will know of your transition.)
|
||||
- It's easier to implement (if too many problems turn up, we can still switch to another approach and didn't wast that much development time.)
|
||||
|
||||
[full messages](https://github.com/deltachat/deltachat-core-rust/pull/2896#discussion_r852002161)
|
||||
@@ -124,4 +124,4 @@ Other
|
||||
Notes during implementing
|
||||
========================
|
||||
|
||||
- As far as I understand the code, unencrypted messages are unsigned. So, the transition only works if both sides have the other side's key.
|
||||
- As far as I understand the code, unencrypted messages are unsigned. So, the transition only works if both sides have the other side's key.
|
||||
@@ -62,5 +62,5 @@ Notes/Questions:
|
||||
|
||||
and design such that we can cover all imap flags.
|
||||
|
||||
- It might not be necessary to keep needs_send_mdn state in this table
|
||||
- It might not be neccessary to keep needs_send_mdn state in this table
|
||||
if this can be decided rather when we succeed with mark_seen/mark_delete.
|
||||
|
||||
+2
-2
@@ -124,7 +124,7 @@ $ npm run test
|
||||
```
|
||||
|
||||
(when using [fnm](https://github.com/Schniz/fnm) instead of nvm, you can select the architecture)
|
||||
If your node and electron are already build for arm64 you can also try building for arm:
|
||||
If your node and electron are already build for arm64 you can also try bulding for arm:
|
||||
|
||||
```
|
||||
$ fnm install 16 --arch arm64
|
||||
@@ -182,7 +182,7 @@ this example can also be found in the examples folder [examples/send_message.js]
|
||||
|
||||
### Generating Docs
|
||||
|
||||
We are currently migrating to automatically generated documentation.
|
||||
We are curently migrating to automaticaly generated documentation.
|
||||
You can find the old documentation at [old_docs](./old_docs).
|
||||
|
||||
to generate the documentation, run:
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { join } from 'path'
|
||||
|
||||
/**
|
||||
* bindings are not typed yet.
|
||||
* if the available function names are required they can be found inside of `../src/module.c`
|
||||
* if the availible function names are required they can be found inside of `../src/module.c`
|
||||
*/
|
||||
export const bindings: any = require('node-gyp-build')(join(__dirname, '../'))
|
||||
|
||||
|
||||
+2
-2
@@ -543,7 +543,7 @@ export class Context extends EventEmitter {
|
||||
|
||||
/**
|
||||
*
|
||||
* @deprecated please use `AccountManager.getSystemInfo()` instead
|
||||
* @deprectated please use `AccountManager.getSystemInfo()` instead
|
||||
*/
|
||||
static getSystemInfo() {
|
||||
return AccountManager.getSystemInfo()
|
||||
@@ -818,7 +818,7 @@ export class Context extends EventEmitter {
|
||||
* Must be given in number of seconds since 00:00 hours, Jan 1, 1970 UTC.
|
||||
* 0 for "all up to now".
|
||||
* @return Array of locations, NULL is never returned.
|
||||
* The array is sorted descending;
|
||||
* The array is sorted decending;
|
||||
* the first entry in the array is the location with the newest timestamp.
|
||||
*
|
||||
* Examples:
|
||||
|
||||
@@ -217,7 +217,7 @@ export class AccountManager extends EventEmitter {
|
||||
/** get information about the provider
|
||||
*
|
||||
* This function creates a temporary context to be standalone,
|
||||
* if possible use `Context.getProviderFromEmail` instead. (otherwise potential proxy settings are not used)
|
||||
* if posible use `Context.getProviderFromEmail` instead. (otherwise potential proxy settings are not used)
|
||||
* @deprecated
|
||||
*/
|
||||
static getProviderFromEmail(email: string) {
|
||||
|
||||
+2
-2
@@ -769,7 +769,7 @@ describe('Integration tests', function () {
|
||||
this.beforeAll(async function () {
|
||||
if (!process.env.DCC_NEW_TMP_EMAIL) {
|
||||
console.log(
|
||||
'Missing DCC_NEW_TMP_EMAIL environment variable!, skip integration tests'
|
||||
'Missing DCC_NEW_TMP_EMAIL environment variable!, skip intergration tests'
|
||||
)
|
||||
this.skip()
|
||||
}
|
||||
@@ -777,7 +777,7 @@ describe('Integration tests', function () {
|
||||
account = await createTempUser(process.env.DCC_NEW_TMP_EMAIL)
|
||||
if (!account || !account.email || !account.password) {
|
||||
console.log(
|
||||
"We didn't got back an account from the api, skip integration tests"
|
||||
"We didn't got back an account from the api, skip intergration tests"
|
||||
)
|
||||
this.skip()
|
||||
}
|
||||
|
||||
+1
-1
@@ -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.111.0"
|
||||
"version": "1.109.0"
|
||||
}
|
||||
+1
-1
@@ -64,7 +64,7 @@
|
||||
- use new experimental full-Rust Delta Chat core
|
||||
- support Autocrypt Setup Messages
|
||||
- remove synchronous events
|
||||
- use CircleCI for continuous integration and packaging of Linux wheels
|
||||
- use CircleCI for continous integration and packaging of Linux wheels
|
||||
- use docker image for building wheels
|
||||
- fix code documentation links
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ classifiers = [
|
||||
dependencies = [
|
||||
"cffi>=1.0.0",
|
||||
"imap-tools",
|
||||
"importlib_metadata;python_version<'3.8'",
|
||||
"pluggy",
|
||||
"requests",
|
||||
]
|
||||
@@ -44,15 +43,17 @@ deltachat = [
|
||||
|
||||
[tool.setuptools_scm]
|
||||
root = ".."
|
||||
tag_regex = '^(?P<prefix>v)?(?P<version>[^\+]+)(?P<suffix>.*)?$'
|
||||
git_describe_command = "git describe --dirty --tags --long --match v*.*"
|
||||
tag_regex = '^(?P<prefix>py-)?(?P<version>[^\+]+)(?P<suffix>.*)?$'
|
||||
git_describe_command = "git describe --dirty --tags --long --match py-*.*"
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
|
||||
[tool.ruff]
|
||||
select = ["E", "F", "W", "YTT", "C4", "ISC", "ICN", "TID", "DTZ", "PLC", "PLE", "PLW", "PIE", "COM", "UP004", "UP010", "UP031", "UP032", "ANN204"]
|
||||
select = ["E", "F", "W", "YTT", "C4", "ISC", "ICN", "TID", "DTZ", "PLC", "PLE", "PLW", "PIE", "COM", "UP004", "UP010", "UP031", "UP032"]
|
||||
line-length = 120
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
|
||||
[tool.pyright]
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
else:
|
||||
from importlib_metadata import PackageNotFoundError, version
|
||||
from pkg_resources import DistributionNotFound, get_distribution
|
||||
|
||||
from . import capi, events, hookspec # noqa
|
||||
from .account import Account, get_core_info # noqa
|
||||
@@ -14,8 +11,8 @@ from .hookspec import account_hookimpl, global_hookimpl # noqa
|
||||
from .message import Message # noqa
|
||||
|
||||
try:
|
||||
__version__ = version(__name__)
|
||||
except PackageNotFoundError:
|
||||
__version__ = get_distribution(__name__).version
|
||||
except DistributionNotFound:
|
||||
# package is not installed
|
||||
__version__ = "0.0.0.dev0-unknown"
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ class Account:
|
||||
"""re-enable logging."""
|
||||
self._logging = True
|
||||
|
||||
def __repr__(self) -> str:
|
||||
def __repr__(self):
|
||||
return f"<Account path={self.db_path}>"
|
||||
|
||||
# def __del__(self):
|
||||
@@ -159,7 +159,7 @@ class Account:
|
||||
"""set stock translation string.
|
||||
|
||||
:param id: id of stock string (const.DC_STR_*)
|
||||
:param value: string to set as new translation
|
||||
:param value: string to set as new transalation
|
||||
:returns: None
|
||||
"""
|
||||
bytestring = string.encode("utf8")
|
||||
@@ -628,7 +628,7 @@ class Account:
|
||||
configtracker = self.configure()
|
||||
configtracker.wait_finish()
|
||||
|
||||
# start IO threads and configure if necessary
|
||||
# start IO threads and configure if neccessary
|
||||
self.start_io()
|
||||
|
||||
def add_account_plugin(self, plugin, name=None):
|
||||
@@ -767,7 +767,7 @@ class Account:
|
||||
|
||||
|
||||
class ScannedQRCode:
|
||||
def __init__(self, dc_lot) -> None:
|
||||
def __init__(self, dc_lot):
|
||||
self._dc_lot = dc_lot
|
||||
|
||||
def is_ask_verifycontact(self):
|
||||
|
||||
@@ -24,7 +24,7 @@ class Chat:
|
||||
You obtain instances of it through :class:`deltachat.account.Account`.
|
||||
"""
|
||||
|
||||
def __init__(self, account, id: int) -> None:
|
||||
def __init__(self, account, id) -> None:
|
||||
from .account import Account
|
||||
|
||||
assert isinstance(account, Account), repr(account)
|
||||
@@ -463,9 +463,10 @@ class Chat:
|
||||
:returns: None
|
||||
"""
|
||||
contact = self.account.get_contact(obj)
|
||||
ret = lib.dc_remove_contact_from_chat(self.account._dc_context, self.id, contact.id)
|
||||
if ret != 1:
|
||||
raise ValueError(f"could not remove contact {contact!r} from chat")
|
||||
if contact:
|
||||
ret = lib.dc_remove_contact_from_chat(self.account._dc_context, self.id, contact.id)
|
||||
if ret != 1:
|
||||
raise ValueError("could not remove contact {contact!r} from chat")
|
||||
|
||||
def get_contacts(self):
|
||||
"""get all contacts for this chat.
|
||||
@@ -532,13 +533,13 @@ class Chat:
|
||||
|
||||
# ------ location streaming API ------------------------------
|
||||
|
||||
def is_sending_locations(self) -> bool:
|
||||
def is_sending_locations(self):
|
||||
"""return True if this chat has location-sending enabled currently.
|
||||
:returns: True if location sending is enabled.
|
||||
"""
|
||||
return bool(lib.dc_is_sending_locations_to_chat(self.account._dc_context, self.id))
|
||||
return lib.dc_is_sending_locations_to_chat(self.account._dc_context, self.id)
|
||||
|
||||
def enable_sending_locations(self, seconds) -> None:
|
||||
def enable_sending_locations(self, seconds):
|
||||
"""enable sending locations for this chat.
|
||||
|
||||
all subsequent messages will carry a location with them.
|
||||
@@ -572,7 +573,7 @@ class Chat:
|
||||
|
||||
|
||||
class Location:
|
||||
def __init__(self, latitude, longitude, accuracy, timestamp, marker) -> None:
|
||||
def __init__(self, latitude, longitude, accuracy, timestamp, marker):
|
||||
assert isinstance(timestamp, datetime)
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
@@ -580,5 +581,5 @@ class Location:
|
||||
self.timestamp = timestamp
|
||||
self.marker = marker
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
@@ -15,7 +15,7 @@ class Contact:
|
||||
You obtain instances of it through :class:`deltachat.account.Account`.
|
||||
"""
|
||||
|
||||
def __init__(self, account, id) -> None:
|
||||
def __init__(self, account, id):
|
||||
from .account import Account
|
||||
|
||||
assert isinstance(account, Account), repr(account)
|
||||
@@ -27,10 +27,10 @@ class Contact:
|
||||
return False
|
||||
return self.account._dc_context == other.account._dc_context and self.id == other.id
|
||||
|
||||
def __ne__(self, other) -> bool:
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self) -> str:
|
||||
def __repr__(self):
|
||||
return f"<Contact id={self.id} addr={self.addr} dc_context={self.account._dc_context}>"
|
||||
|
||||
@property
|
||||
|
||||
@@ -147,7 +147,7 @@ class DirectImap:
|
||||
log("Message", msg.uid, "has empty body")
|
||||
continue
|
||||
|
||||
path = pathlib.Path(str(dir)).joinpath("IMAP", self.logid, imapfolder)
|
||||
path = pathlib.Path(str(dir)).joinpath("IMAP", str(self.logid), imapfolder)
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
fn = path.joinpath(str(msg.uid))
|
||||
fn.write_bytes(body)
|
||||
@@ -191,7 +191,7 @@ class DirectImap:
|
||||
|
||||
|
||||
class IdleManager:
|
||||
def __init__(self, direct_imap) -> None:
|
||||
def __init__(self, direct_imap):
|
||||
self.direct_imap = direct_imap
|
||||
self.log = direct_imap.account.log
|
||||
# fetch latest messages before starting idle so that it only
|
||||
|
||||
@@ -25,12 +25,12 @@ def get_dc_event_name(integer, _DC_EVENTNAME_MAP={}):
|
||||
|
||||
|
||||
class FFIEvent:
|
||||
def __init__(self, name: str, data1, data2) -> None:
|
||||
def __init__(self, name: str, data1, data2):
|
||||
self.name = name
|
||||
self.data1 = data1
|
||||
self.data2 = data2
|
||||
|
||||
def __str__(self) -> str:
|
||||
def __str__(self):
|
||||
if self.name == "DC_EVENT_INFO":
|
||||
return f"INFO {self.data2}"
|
||||
if self.name == "DC_EVENT_WARNING":
|
||||
@@ -84,10 +84,7 @@ class FFIEventLogger:
|
||||
|
||||
|
||||
class FFIEventTracker:
|
||||
account: Account
|
||||
_event_queue: Queue
|
||||
|
||||
def __init__(self, account: Account, timeout=None) -> None:
|
||||
def __init__(self, account, timeout=None):
|
||||
self.account = account
|
||||
self._timeout = timeout
|
||||
self._event_queue = Queue()
|
||||
@@ -114,11 +111,12 @@ class FFIEventTracker:
|
||||
while True:
|
||||
yield self.get(timeout=timeout, check_error=check_error)
|
||||
|
||||
def get_matching(self, event_name_regex, check_error=True, timeout=None):
|
||||
def get_matching(self, event_name_regex, check_error=True, timeout=None) -> FFIEvent:
|
||||
rex = re.compile(f"^(?:{event_name_regex})$")
|
||||
for ev in self.iter_events(timeout=timeout, check_error=check_error):
|
||||
if rex.match(ev.name):
|
||||
return ev
|
||||
assert False
|
||||
|
||||
def get_info_contains(self, regex: str) -> FFIEvent:
|
||||
rex = re.compile(regex)
|
||||
@@ -126,6 +124,7 @@ class FFIEventTracker:
|
||||
ev = self.get_matching("DC_EVENT_INFO")
|
||||
if rex.search(ev.data2):
|
||||
return ev
|
||||
assert False
|
||||
|
||||
def get_info_regex_groups(self, regex, check_error=True):
|
||||
rex = re.compile(regex)
|
||||
|
||||
@@ -19,7 +19,7 @@ class Message:
|
||||
:class:`deltachat.chat.Chat`.
|
||||
"""
|
||||
|
||||
def __init__(self, account, dc_msg) -> None:
|
||||
def __init__(self, account, dc_msg):
|
||||
self.account = account
|
||||
assert isinstance(self.account._dc_context, ffi.CData)
|
||||
assert isinstance(dc_msg, ffi.CData)
|
||||
@@ -33,7 +33,7 @@ class Message:
|
||||
return False
|
||||
return self.account == other.account and self.id == other.id
|
||||
|
||||
def __repr__(self) -> str:
|
||||
def __repr__(self):
|
||||
c = self.get_sender_contact()
|
||||
typ = "outgoing" if self.is_outgoing() else "incoming"
|
||||
return (
|
||||
|
||||
@@ -10,14 +10,14 @@ class Reactions:
|
||||
You obtain instances of it through :class:`deltachat.message.Message`.
|
||||
"""
|
||||
|
||||
def __init__(self, account, dc_reactions) -> None:
|
||||
def __init__(self, account, dc_reactions):
|
||||
assert isinstance(account._dc_context, ffi.CData)
|
||||
assert isinstance(dc_reactions, ffi.CData)
|
||||
assert dc_reactions != ffi.NULL
|
||||
self.account = account
|
||||
self._dc_reactions = dc_reactions
|
||||
|
||||
def __repr__(self) -> str:
|
||||
def __repr__(self):
|
||||
return f"<Reactions dc_reactions={self._dc_reactions}>"
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -9,7 +9,7 @@ import threading
|
||||
import time
|
||||
import weakref
|
||||
from queue import Queue
|
||||
from typing import Callable, List, Optional, Dict, Set
|
||||
from typing import Callable, List, Optional
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
@@ -60,13 +60,13 @@ def pytest_configure(config):
|
||||
|
||||
# Make sure we don't get garbled output because threads keep running
|
||||
# collect all ever created accounts in a weakref-set (so we don't
|
||||
# keep objects unnecessarily alive) and enable/disable logging
|
||||
# keep objects unneccessarily alive) and enable/disable logging
|
||||
# for each pytest test phase # (setup/call/teardown).
|
||||
# Additionally make the acfactory use a logging/no-logging default.
|
||||
|
||||
class LoggingAspect:
|
||||
def __init__(self) -> None:
|
||||
self._accounts: weakref.WeakSet = weakref.WeakSet()
|
||||
def __init__(self):
|
||||
self._accounts = weakref.WeakSet()
|
||||
|
||||
@deltachat.global_hookimpl
|
||||
def dc_account_init(self, account):
|
||||
@@ -129,7 +129,7 @@ def pytest_report_header(config, startdir):
|
||||
if cfg:
|
||||
if "?" in cfg:
|
||||
url, token = cfg.split("?", 1)
|
||||
summary.append(f"Liveconfig provider: {url}?<token omitted>")
|
||||
summary.append(f"Liveconfig provider: {url}?<token ommitted>")
|
||||
else:
|
||||
summary.append(f"Liveconfig file: {cfg}")
|
||||
return summary
|
||||
@@ -143,12 +143,10 @@ def testprocess(request):
|
||||
class TestProcess:
|
||||
"""A pytest session-scoped instance to help with managing "live" account configurations."""
|
||||
|
||||
_addr2files: Dict[str, Dict[pathlib.Path, bytes]]
|
||||
|
||||
def __init__(self, pytestconfig) -> None:
|
||||
def __init__(self, pytestconfig):
|
||||
self.pytestconfig = pytestconfig
|
||||
self._addr2files = {}
|
||||
self._configlist: List[Dict[str, str]] = []
|
||||
self._configlist = []
|
||||
|
||||
def get_liveconfig_producer(self):
|
||||
"""provide live account configs, cached on a per-test-process scope
|
||||
@@ -252,7 +250,7 @@ def data(request):
|
||||
fn = os.path.join(path, *bn.split("/"))
|
||||
if os.path.exists(fn):
|
||||
return fn
|
||||
print(f"WARNING: path does not exist: {fn!r}")
|
||||
print(f"WARNING: path does not exist: {request!r}")
|
||||
return None
|
||||
|
||||
def read_path(self, bn, mode="r"):
|
||||
@@ -279,10 +277,10 @@ class ACSetup:
|
||||
|
||||
_configured_events: Queue
|
||||
|
||||
def __init__(self, testprocess, init_time) -> None:
|
||||
def __init__(self, testprocess, init_time):
|
||||
self._configured_events = Queue()
|
||||
self._account2state: Dict[Account, str] = {}
|
||||
self._imap_cleaned: Set[str] = set()
|
||||
self._account2state = {}
|
||||
self._imap_cleaned = set()
|
||||
self.testprocess = testprocess
|
||||
self.init_time = init_time
|
||||
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
from queue import Queue
|
||||
from threading import Event
|
||||
from typing import List, TYPE_CHECKING
|
||||
|
||||
from .hookspec import Global, account_hookimpl
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .events import FFIEvent
|
||||
|
||||
|
||||
class ImexFailed(RuntimeError):
|
||||
"""Exception for signalling that import/export operations failed."""
|
||||
|
||||
|
||||
class ImexTracker:
|
||||
_imex_events: Queue
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self):
|
||||
self._imex_events = Queue()
|
||||
|
||||
@account_hookimpl
|
||||
def ac_process_ffi_event(self, ffi_event: "FFIEvent") -> None:
|
||||
def ac_process_ffi_event(self, ffi_event):
|
||||
if ffi_event.name == "DC_EVENT_IMEX_PROGRESS":
|
||||
self._imex_events.put(ffi_event.data1)
|
||||
elif ffi_event.name == "DC_EVENT_IMEX_FILE_WRITTEN":
|
||||
@@ -56,13 +50,7 @@ class ConfigureFailed(RuntimeError):
|
||||
class ConfigureTracker:
|
||||
ConfigureFailed = ConfigureFailed
|
||||
|
||||
_configure_events: Queue
|
||||
_smtp_finished: Event
|
||||
_imap_finished: Event
|
||||
_ffi_events: List["FFIEvent"]
|
||||
_progress: Queue
|
||||
|
||||
def __init__(self, account) -> None:
|
||||
def __init__(self, account):
|
||||
self.account = account
|
||||
self._configure_events = Queue()
|
||||
self._smtp_finished = Event()
|
||||
@@ -72,7 +60,7 @@ class ConfigureTracker:
|
||||
self._gm = Global._get_plugin_manager()
|
||||
|
||||
@account_hookimpl
|
||||
def ac_process_ffi_event(self, ffi_event: "FFIEvent") -> None:
|
||||
def ac_process_ffi_event(self, ffi_event):
|
||||
self._ffi_events.append(ffi_event)
|
||||
if ffi_event.name == "DC_EVENT_SMTP_CONNECTED":
|
||||
self._smtp_finished.set()
|
||||
|
||||
@@ -77,7 +77,7 @@ class ReportType:
|
||||
|
||||
|
||||
class AutoReplier:
|
||||
def __init__(self, account, log, num_send, num_bigfiles, report_func) -> None:
|
||||
def __init__(self, account, log, num_send, num_bigfiles, report_func):
|
||||
self.account = account
|
||||
self._log = log
|
||||
self.report_func = report_func
|
||||
@@ -90,7 +90,7 @@ class AutoReplier:
|
||||
self._thread.setDaemon(True)
|
||||
self._thread.start()
|
||||
|
||||
def log(self, message) -> None:
|
||||
def log(self, message):
|
||||
self._log(f"{self.addr} {message}")
|
||||
|
||||
def thread_stats(self):
|
||||
|
||||
@@ -1484,7 +1484,7 @@ def test_import_export_online_all(acfactory, tmpdir, data, lp):
|
||||
|
||||
def test_ac_setup_message(acfactory, lp):
|
||||
# note that the receiving account needs to be configured and running
|
||||
# before the setup message is send. DC does not read old messages
|
||||
# before ther setup message is send. DC does not read old messages
|
||||
# as of Jul2019
|
||||
ac1 = acfactory.new_online_configuring_account()
|
||||
ac2 = acfactory.new_online_configuring_account(cloned_from=ac1)
|
||||
@@ -1604,7 +1604,7 @@ def test_add_remove_member_remote_events(acfactory, lp):
|
||||
in_list = queue.Queue()
|
||||
|
||||
class EventHolder:
|
||||
def __init__(self, **kwargs) -> None:
|
||||
def __init__(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
|
||||
class InPlugin:
|
||||
@@ -1954,7 +1954,6 @@ def test_immediate_autodelete(acfactory, lp):
|
||||
assert msg.text == "hello"
|
||||
|
||||
lp.sec("ac2: wait for close/expunge on autodelete")
|
||||
ac2._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED")
|
||||
ac2._evtracker.get_info_contains("close/expunge succeeded")
|
||||
|
||||
lp.sec("ac2: check that message was autodeleted on server")
|
||||
@@ -1987,48 +1986,13 @@ def test_delete_multiple_messages(acfactory, lp):
|
||||
lp.sec("ac2: deleting all messages except third")
|
||||
assert len(to_delete) == len(texts) - 1
|
||||
ac2.delete_messages(to_delete)
|
||||
ac2._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED")
|
||||
|
||||
ac2._evtracker.get_info_contains("close/expunge succeeded")
|
||||
|
||||
lp.sec("ac2: test that only one message is left")
|
||||
while 1:
|
||||
ac2._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED")
|
||||
ac2._evtracker.get_info_contains("close/expunge succeeded")
|
||||
ac2.direct_imap.select_config_folder("inbox")
|
||||
nr_msgs = len(ac2.direct_imap.get_all_messages())
|
||||
assert nr_msgs > 0
|
||||
if nr_msgs == 1:
|
||||
break
|
||||
|
||||
|
||||
def test_trash_multiple_messages(acfactory, lp):
|
||||
ac1, ac2 = acfactory.get_online_accounts(2)
|
||||
ac2.set_config("delete_to_trash", "1")
|
||||
chat12 = acfactory.get_accepted_chat(ac1, ac2)
|
||||
|
||||
lp.sec("ac1: sending 3 messages")
|
||||
texts = ["first", "second", "third"]
|
||||
for text in texts:
|
||||
chat12.send_text(text)
|
||||
|
||||
lp.sec("ac2: waiting for all messages on the other side")
|
||||
to_delete = []
|
||||
for text in texts:
|
||||
msg = ac2._evtracker.wait_next_incoming_message()
|
||||
assert msg.text in texts
|
||||
if text != "second":
|
||||
to_delete.append(msg)
|
||||
|
||||
lp.sec("ac2: deleting all messages except second")
|
||||
assert len(to_delete) == len(texts) - 1
|
||||
ac2.delete_messages(to_delete)
|
||||
|
||||
lp.sec("ac2: test that only one message is left")
|
||||
while 1:
|
||||
ac2._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
|
||||
ac2.direct_imap.select_config_folder("inbox")
|
||||
nr_msgs = len(ac2.direct_imap.get_all_messages())
|
||||
assert nr_msgs > 0
|
||||
if nr_msgs == 1:
|
||||
break
|
||||
ac2.direct_imap.select_config_folder("inbox")
|
||||
assert len(ac2.direct_imap.get_all_messages()) == 1
|
||||
|
||||
|
||||
def test_configure_error_msgs_wrong_pw(acfactory):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import os.path
|
||||
import shutil
|
||||
from filecmp import cmp
|
||||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -13,7 +14,7 @@ def wait_msg_delivered(account, msg_list):
|
||||
msg_list.remove((ev.data1, ev.data2))
|
||||
|
||||
|
||||
def wait_msgs_changed(account, msgs_list):
|
||||
def wait_msgs_changed(account, msgs_list: list[tuple[int, Optional[int]]]):
|
||||
"""wait for one or more MSGS_CHANGED events to match msgs_list contents."""
|
||||
account.log(f"waiting for msgs_list={msgs_list}")
|
||||
msgs_list = list(msgs_list)
|
||||
|
||||
@@ -151,7 +151,7 @@ def test_markseen_invalid_message_ids(acfactory):
|
||||
ac1 = acfactory.get_pseudo_configured_account()
|
||||
contact1 = ac1.create_contact("some1@example.com", name="some1")
|
||||
chat = contact1.create_chat()
|
||||
chat.send_text("one message")
|
||||
chat.send_text("one messae")
|
||||
ac1._evtracker.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||
msg_ids = [9]
|
||||
lib.dc_markseen_msgs(ac1._dc_context, msg_ids, len(msg_ids))
|
||||
|
||||
@@ -5,6 +5,7 @@ envlist =
|
||||
lint
|
||||
mypy
|
||||
auditwheels
|
||||
pyright
|
||||
|
||||
[testenv]
|
||||
commands =
|
||||
@@ -25,6 +26,7 @@ deps =
|
||||
pytest-xdist
|
||||
pdbpp
|
||||
requests
|
||||
pyright: pyright
|
||||
|
||||
[testenv:.pkg]
|
||||
passenv =
|
||||
@@ -68,6 +70,10 @@ deps =
|
||||
commands =
|
||||
mypy --no-incremental src/
|
||||
|
||||
[testenv:pyright]
|
||||
commands =
|
||||
pyright
|
||||
|
||||
[testenv:doc]
|
||||
changedir=doc
|
||||
deps =
|
||||
|
||||
@@ -20,8 +20,6 @@ and an own build machine.
|
||||
|
||||
- `run_all.sh` builds Python wheels
|
||||
|
||||
- `aarch64-unknown-linux-musl.sh` cross-compiles static `deltachat-rpc-server` for aarch64
|
||||
|
||||
## Triggering runs on the build machine locally (fast!)
|
||||
|
||||
There is experimental support for triggering a remote Python or Rust test run
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Build statically linked deltachat-rpc-server for aarch64-unknown-linux-musl.
|
||||
|
||||
set -x
|
||||
set -e
|
||||
|
||||
# Download Zig
|
||||
rm -fr zig-linux-x86_64-0.10.1 zig-linux-x86_64-0.10.1.tar.xz
|
||||
wget https://ziglang.org/download/0.10.1/zig-linux-x86_64-0.10.1.tar.xz
|
||||
tar xf zig-linux-x86_64-0.10.1.tar.xz
|
||||
export PATH="$PATH:$PWD/zig-linux-x86_64-0.10.1"
|
||||
|
||||
cargo install cargo-zigbuild
|
||||
|
||||
rustup target add aarch64-unknown-linux-musl
|
||||
|
||||
cargo zigbuild --release --target aarch64-unknown-linux-musl -p deltachat-rpc-server --features vendored
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/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
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
# Run clippy for all Rust code in the project.
|
||||
cargo clippy --workspace --all-targets -- -D warnings
|
||||
cargo clippy --workspace --tests --examples --benches -- -D warnings
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
codespell \
|
||||
--skip './test-data,./.git,node_modules,.mypy_cache,./src/provider/data.rs,.tox,site-packages,target,Cargo.lock,*.js.map,package-lock.json,./proptest-regressions' \
|
||||
--ignore-words-list crate,keypair,keypairs,iif
|
||||
+347
-347
@@ -1,370 +1,370 @@
|
||||
resources:
|
||||
- name: deltachat-core-rust
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
branch: master
|
||||
uri: https://github.com/deltachat/deltachat-core-rust.git
|
||||
- name: deltachat-core-rust
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
branch: master
|
||||
uri: https://github.com/deltachat/deltachat-core-rust.git
|
||||
|
||||
- name: deltachat-core-rust-release
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
branch: master
|
||||
uri: https://github.com/deltachat/deltachat-core-rust.git
|
||||
tag_filter: "v*"
|
||||
- name: deltachat-core-rust-release
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
branch: master
|
||||
uri: https://github.com/deltachat/deltachat-core-rust.git
|
||||
tag_filter: "py-*"
|
||||
|
||||
jobs:
|
||||
- name: doxygen
|
||||
plan:
|
||||
- get: deltachat-core-rust
|
||||
trigger: true
|
||||
- name: doxygen
|
||||
plan:
|
||||
- get: deltachat-core-rust
|
||||
trigger: true
|
||||
|
||||
# Build Doxygen documentation
|
||||
- task: build-doxygen
|
||||
config:
|
||||
inputs:
|
||||
- name: deltachat-core-rust
|
||||
outputs:
|
||||
- name: c-docs
|
||||
image_resource:
|
||||
source:
|
||||
repository: alpine
|
||||
type: registry-image
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apk add --no-cache doxygen git
|
||||
cd deltachat-core-rust
|
||||
scripts/run-doxygen.sh
|
||||
cd ..
|
||||
cp -av deltachat-core-rust/deltachat-ffi/html deltachat-core-rust/deltachat-ffi/xml c-docs/
|
||||
# Build Doxygen documentation
|
||||
- task: build-doxygen
|
||||
config:
|
||||
inputs:
|
||||
- name: deltachat-core-rust
|
||||
outputs:
|
||||
- name: c-docs
|
||||
image_resource:
|
||||
source:
|
||||
repository: alpine
|
||||
type: registry-image
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apk add --no-cache doxygen git
|
||||
cd deltachat-core-rust
|
||||
scripts/run-doxygen.sh
|
||||
cd ..
|
||||
cp -av deltachat-core-rust/deltachat-ffi/html deltachat-core-rust/deltachat-ffi/xml c-docs/
|
||||
|
||||
- task: upload-c-docs
|
||||
config:
|
||||
inputs:
|
||||
- name: c-docs
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: alpine
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apk add --no-cache rsync openssh-client
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
echo "(("c.delta.chat".private_key))" > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -avz --delete c-docs/html/ delta@c.delta.chat:build-c/master
|
||||
- task: upload-c-docs
|
||||
config:
|
||||
inputs:
|
||||
- name: c-docs
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: alpine
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apk add --no-cache rsync openssh-client
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
echo "(("c.delta.chat".private_key))" > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -avz --delete c-docs/html/ delta@c.delta.chat:build-c/master
|
||||
|
||||
- name: python-x86_64
|
||||
plan:
|
||||
- get: deltachat-core-rust
|
||||
- get: deltachat-core-rust-release
|
||||
trigger: true
|
||||
- name: python-x86_64
|
||||
plan:
|
||||
- get: deltachat-core-rust
|
||||
- get: deltachat-core-rust-release
|
||||
trigger: true
|
||||
|
||||
# Build manylinux image with additional dependencies
|
||||
- task: build-coredeps
|
||||
privileged: true
|
||||
config:
|
||||
inputs:
|
||||
# Building the latest, not tagged coredeps
|
||||
- name: deltachat-core-rust
|
||||
image_resource:
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
type: registry-image
|
||||
outputs:
|
||||
- name: coredeps-image
|
||||
path: image
|
||||
params:
|
||||
CONTEXT: deltachat-core-rust/scripts/coredeps
|
||||
UNPACK_ROOTFS: "true"
|
||||
BUILD_ARG_BASEIMAGE: quay.io/pypa/manylinux2014_x86_64
|
||||
platform: linux
|
||||
caches:
|
||||
- path: cache
|
||||
run:
|
||||
path: build
|
||||
# Build manylinux image with additional dependencies
|
||||
- task: build-coredeps
|
||||
privileged: true
|
||||
config:
|
||||
inputs:
|
||||
# Building the latest, not tagged coredeps
|
||||
- name: deltachat-core-rust
|
||||
image_resource:
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
type: registry-image
|
||||
outputs:
|
||||
- name: coredeps-image
|
||||
path: image
|
||||
params:
|
||||
CONTEXT: deltachat-core-rust/scripts/coredeps
|
||||
UNPACK_ROOTFS: "true"
|
||||
BUILD_ARG_BASEIMAGE: quay.io/pypa/manylinux2014_x86_64
|
||||
platform: linux
|
||||
caches:
|
||||
- path: cache
|
||||
run:
|
||||
path: build
|
||||
|
||||
# Use built image to build python wheels
|
||||
- task: build-wheels
|
||||
image: coredeps-image
|
||||
config:
|
||||
inputs:
|
||||
- name: deltachat-core-rust-release
|
||||
path: .
|
||||
outputs:
|
||||
- name: py-docs
|
||||
path: ./python/doc/_build/
|
||||
# Binary wheels
|
||||
- name: py-wheels
|
||||
path: ./python/.docker-tox/wheelhouse/
|
||||
platform: linux
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -exc
|
||||
- |
|
||||
scripts/run_all.sh
|
||||
# Use built image to build python wheels
|
||||
- task: build-wheels
|
||||
image: coredeps-image
|
||||
config:
|
||||
inputs:
|
||||
- name: deltachat-core-rust-release
|
||||
path: .
|
||||
outputs:
|
||||
- name: py-docs
|
||||
path: ./python/doc/_build/
|
||||
# Binary wheels
|
||||
- name: py-wheels
|
||||
path: ./python/.docker-tox/wheelhouse/
|
||||
platform: linux
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -exc
|
||||
- |
|
||||
scripts/run_all.sh
|
||||
|
||||
# Upload python docs to py.delta.chat
|
||||
- task: upload-py-docs
|
||||
config:
|
||||
inputs:
|
||||
- name: py-docs
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: alpine
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apk add --no-cache rsync openssh-client
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
echo "(("c.delta.chat".private_key))" > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -avz --delete py-docs/html/ delta@py.delta.chat:build/master
|
||||
# Upload python docs to py.delta.chat
|
||||
- task: upload-py-docs
|
||||
config:
|
||||
inputs:
|
||||
- name: py-docs
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: alpine
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apk add --no-cache rsync openssh-client
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
echo "(("c.delta.chat".private_key))" > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
rsync -e "ssh -o StrictHostKeyChecking=no" -avz --delete py-docs/html/ delta@py.delta.chat:build/master
|
||||
|
||||
# Upload x86_64 wheels and source packages
|
||||
- task: upload-wheels
|
||||
config:
|
||||
inputs:
|
||||
- name: py-wheels
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: debian
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends python3-pip python3-setuptools
|
||||
pip3 install devpi
|
||||
devpi use https://m.devpi.net/dc/master
|
||||
devpi login ((devpi.login)) --password ((devpi.password))
|
||||
devpi upload py-wheels/*manylinux201*
|
||||
# Upload x86_64 wheels and source packages
|
||||
- task: upload-wheels
|
||||
config:
|
||||
inputs:
|
||||
- name: py-wheels
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: debian
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends python3-pip python3-setuptools
|
||||
pip3 install devpi
|
||||
devpi use https://m.devpi.net/dc/master
|
||||
devpi login ((devpi.login)) --password ((devpi.password))
|
||||
devpi upload py-wheels/*manylinux201*
|
||||
|
||||
- name: python-aarch64
|
||||
plan:
|
||||
- get: deltachat-core-rust
|
||||
- get: deltachat-core-rust-release
|
||||
trigger: true
|
||||
- name: python-aarch64
|
||||
plan:
|
||||
- get: deltachat-core-rust
|
||||
- get: deltachat-core-rust-release
|
||||
trigger: true
|
||||
|
||||
# Build manylinux image with additional dependencies
|
||||
- task: build-coredeps
|
||||
privileged: true
|
||||
config:
|
||||
inputs:
|
||||
# Building the latest, not tagged coredeps
|
||||
- name: deltachat-core-rust
|
||||
image_resource:
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
type: registry-image
|
||||
outputs:
|
||||
- name: coredeps-image
|
||||
path: image
|
||||
params:
|
||||
CONTEXT: deltachat-core-rust/scripts/coredeps
|
||||
UNPACK_ROOTFS: "true"
|
||||
BUILD_ARG_BASEIMAGE: quay.io/pypa/manylinux2014_aarch64
|
||||
platform: linux
|
||||
caches:
|
||||
- path: cache
|
||||
run:
|
||||
path: build
|
||||
# Build manylinux image with additional dependencies
|
||||
- task: build-coredeps
|
||||
privileged: true
|
||||
config:
|
||||
inputs:
|
||||
# Building the latest, not tagged coredeps
|
||||
- name: deltachat-core-rust
|
||||
image_resource:
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
type: registry-image
|
||||
outputs:
|
||||
- name: coredeps-image
|
||||
path: image
|
||||
params:
|
||||
CONTEXT: deltachat-core-rust/scripts/coredeps
|
||||
UNPACK_ROOTFS: "true"
|
||||
BUILD_ARG_BASEIMAGE: quay.io/pypa/manylinux2014_aarch64
|
||||
platform: linux
|
||||
caches:
|
||||
- path: cache
|
||||
run:
|
||||
path: build
|
||||
|
||||
# Use built image to build python wheels
|
||||
- task: build-wheels
|
||||
image: coredeps-image
|
||||
config:
|
||||
inputs:
|
||||
- name: deltachat-core-rust-release
|
||||
path: .
|
||||
outputs:
|
||||
- name: py-wheels
|
||||
path: ./python/.docker-tox/wheelhouse/
|
||||
platform: linux
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -exc
|
||||
- |
|
||||
scripts/run_all.sh
|
||||
# Use built image to build python wheels
|
||||
- task: build-wheels
|
||||
image: coredeps-image
|
||||
config:
|
||||
inputs:
|
||||
- name: deltachat-core-rust-release
|
||||
path: .
|
||||
outputs:
|
||||
- name: py-wheels
|
||||
path: ./python/.docker-tox/wheelhouse/
|
||||
platform: linux
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -exc
|
||||
- |
|
||||
scripts/run_all.sh
|
||||
|
||||
# Upload aarch64 wheels
|
||||
- task: upload-wheels
|
||||
config:
|
||||
inputs:
|
||||
- name: py-wheels
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: debian
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends python3-pip python3-setuptools
|
||||
pip3 install devpi
|
||||
devpi use https://m.devpi.net/dc/master
|
||||
devpi login ((devpi.login)) --password ((devpi.password))
|
||||
devpi upload py-wheels/*manylinux201*
|
||||
# Upload aarch64 wheels
|
||||
- task: upload-wheels
|
||||
config:
|
||||
inputs:
|
||||
- name: py-wheels
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: debian
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends python3-pip python3-setuptools
|
||||
pip3 install devpi
|
||||
devpi use https://m.devpi.net/dc/master
|
||||
devpi login ((devpi.login)) --password ((devpi.password))
|
||||
devpi upload py-wheels/*manylinux201*
|
||||
|
||||
- name: python-musl-x86_64
|
||||
plan:
|
||||
- get: deltachat-core-rust
|
||||
- get: deltachat-core-rust-release
|
||||
trigger: true
|
||||
- name: python-musl-x86_64
|
||||
plan:
|
||||
- get: deltachat-core-rust
|
||||
- get: deltachat-core-rust-release
|
||||
trigger: true
|
||||
|
||||
# Build manylinux image with additional dependencies
|
||||
- task: build-coredeps
|
||||
privileged: true
|
||||
config:
|
||||
inputs:
|
||||
# Building the latest, not tagged coredeps
|
||||
- name: deltachat-core-rust
|
||||
image_resource:
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
type: registry-image
|
||||
outputs:
|
||||
- name: coredeps-image
|
||||
path: image
|
||||
params:
|
||||
CONTEXT: deltachat-core-rust/scripts/coredeps
|
||||
UNPACK_ROOTFS: "true"
|
||||
BUILD_ARG_BASEIMAGE: quay.io/pypa/musllinux_1_1_x86_64
|
||||
platform: linux
|
||||
caches:
|
||||
- path: cache
|
||||
run:
|
||||
path: build
|
||||
# Build manylinux image with additional dependencies
|
||||
- task: build-coredeps
|
||||
privileged: true
|
||||
config:
|
||||
inputs:
|
||||
# Building the latest, not tagged coredeps
|
||||
- name: deltachat-core-rust
|
||||
image_resource:
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
type: registry-image
|
||||
outputs:
|
||||
- name: coredeps-image
|
||||
path: image
|
||||
params:
|
||||
CONTEXT: deltachat-core-rust/scripts/coredeps
|
||||
UNPACK_ROOTFS: "true"
|
||||
BUILD_ARG_BASEIMAGE: quay.io/pypa/musllinux_1_1_x86_64
|
||||
platform: linux
|
||||
caches:
|
||||
- path: cache
|
||||
run:
|
||||
path: build
|
||||
|
||||
# Use built image to build python wheels
|
||||
- task: build-wheels
|
||||
image: coredeps-image
|
||||
config:
|
||||
inputs:
|
||||
- name: deltachat-core-rust-release
|
||||
path: .
|
||||
outputs:
|
||||
- name: py-wheels
|
||||
path: ./python/.docker-tox/wheelhouse/
|
||||
platform: linux
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -exc
|
||||
- |
|
||||
scripts/run_all.sh
|
||||
# Use built image to build python wheels
|
||||
- task: build-wheels
|
||||
image: coredeps-image
|
||||
config:
|
||||
inputs:
|
||||
- name: deltachat-core-rust-release
|
||||
path: .
|
||||
outputs:
|
||||
- name: py-wheels
|
||||
path: ./python/.docker-tox/wheelhouse/
|
||||
platform: linux
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -exc
|
||||
- |
|
||||
scripts/run_all.sh
|
||||
|
||||
# Upload musl x86_64 wheels
|
||||
- task: upload-wheels
|
||||
config:
|
||||
inputs:
|
||||
- name: py-wheels
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: debian
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends python3-pip python3-setuptools
|
||||
pip3 install devpi
|
||||
devpi use https://m.devpi.net/dc/master
|
||||
devpi login ((devpi.login)) --password ((devpi.password))
|
||||
devpi upload py-wheels/*musllinux_1_1_x86_64*
|
||||
# Upload musl x86_64 wheels
|
||||
- task: upload-wheels
|
||||
config:
|
||||
inputs:
|
||||
- name: py-wheels
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: debian
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends python3-pip python3-setuptools
|
||||
pip3 install devpi
|
||||
devpi use https://m.devpi.net/dc/master
|
||||
devpi login ((devpi.login)) --password ((devpi.password))
|
||||
devpi upload py-wheels/*musllinux_1_1_x86_64*
|
||||
|
||||
- name: python-musl-aarch64
|
||||
plan:
|
||||
- get: deltachat-core-rust
|
||||
- get: deltachat-core-rust-release
|
||||
trigger: true
|
||||
- name: python-musl-aarch64
|
||||
plan:
|
||||
- get: deltachat-core-rust
|
||||
- get: deltachat-core-rust-release
|
||||
trigger: true
|
||||
|
||||
# Build manylinux image with additional dependencies
|
||||
- task: build-coredeps
|
||||
privileged: true
|
||||
config:
|
||||
inputs:
|
||||
# Building the latest, not tagged coredeps
|
||||
- name: deltachat-core-rust
|
||||
image_resource:
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
type: registry-image
|
||||
outputs:
|
||||
- name: coredeps-image
|
||||
path: image
|
||||
params:
|
||||
CONTEXT: deltachat-core-rust/scripts/coredeps
|
||||
UNPACK_ROOTFS: "true"
|
||||
BUILD_ARG_BASEIMAGE: quay.io/pypa/musllinux_1_1_aarch64
|
||||
platform: linux
|
||||
caches:
|
||||
- path: cache
|
||||
run:
|
||||
path: build
|
||||
# Build manylinux image with additional dependencies
|
||||
- task: build-coredeps
|
||||
privileged: true
|
||||
config:
|
||||
inputs:
|
||||
# Building the latest, not tagged coredeps
|
||||
- name: deltachat-core-rust
|
||||
image_resource:
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
type: registry-image
|
||||
outputs:
|
||||
- name: coredeps-image
|
||||
path: image
|
||||
params:
|
||||
CONTEXT: deltachat-core-rust/scripts/coredeps
|
||||
UNPACK_ROOTFS: "true"
|
||||
BUILD_ARG_BASEIMAGE: quay.io/pypa/musllinux_1_1_aarch64
|
||||
platform: linux
|
||||
caches:
|
||||
- path: cache
|
||||
run:
|
||||
path: build
|
||||
|
||||
# Use built image to build python wheels
|
||||
- task: build-wheels
|
||||
image: coredeps-image
|
||||
config:
|
||||
inputs:
|
||||
- name: deltachat-core-rust-release
|
||||
path: .
|
||||
outputs:
|
||||
- name: py-wheels
|
||||
path: ./python/.docker-tox/wheelhouse/
|
||||
platform: linux
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -exc
|
||||
- |
|
||||
scripts/run_all.sh
|
||||
# Use built image to build python wheels
|
||||
- task: build-wheels
|
||||
image: coredeps-image
|
||||
config:
|
||||
inputs:
|
||||
- name: deltachat-core-rust-release
|
||||
path: .
|
||||
outputs:
|
||||
- name: py-wheels
|
||||
path: ./python/.docker-tox/wheelhouse/
|
||||
platform: linux
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -exc
|
||||
- |
|
||||
scripts/run_all.sh
|
||||
|
||||
# Upload musl aarch64 wheels
|
||||
- task: upload-wheels
|
||||
config:
|
||||
inputs:
|
||||
- name: py-wheels
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: debian
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends python3-pip python3-setuptools
|
||||
pip3 install devpi
|
||||
devpi use https://m.devpi.net/dc/master
|
||||
devpi login ((devpi.login)) --password ((devpi.password))
|
||||
devpi upload py-wheels/*musllinux_1_1_aarch64*
|
||||
# Upload musl aarch64 wheels
|
||||
- task: upload-wheels
|
||||
config:
|
||||
inputs:
|
||||
- name: py-wheels
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: debian
|
||||
platform: linux
|
||||
run:
|
||||
path: sh
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends python3-pip python3-setuptools
|
||||
pip3 install devpi
|
||||
devpi use https://m.devpi.net/dc/master
|
||||
devpi login ((devpi.login)) --password ((devpi.password))
|
||||
devpi upload py-wheels/*musllinux_1_1_aarch64*
|
||||
|
||||
@@ -115,8 +115,10 @@ def main():
|
||||
|
||||
print("after commit, on master make sure to: ")
|
||||
print("")
|
||||
print(f" git tag -a v{newversion}")
|
||||
print(f" git push origin v{newversion}")
|
||||
print(f" git tag -a {newversion}")
|
||||
print(f" git push origin {newversion}")
|
||||
print(f" git tag -a py-{newversion}")
|
||||
print(f" git push origin py-{newversion}")
|
||||
print("")
|
||||
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Updates provider database.
|
||||
# Returns 1 if the database is changed, 0 otherwise.
|
||||
set -euo pipefail
|
||||
|
||||
export TZ=UTC
|
||||
|
||||
# Provider database revision.
|
||||
REV=3c8f7e846c915a183dc44536fb5480d1f25d7c42
|
||||
|
||||
CORE_ROOT="$PWD"
|
||||
TMP="$(mktemp -d)"
|
||||
git clone --filter=blob:none https://github.com/deltachat/provider-db.git "$TMP"
|
||||
cd "$TMP"
|
||||
git checkout "$REV"
|
||||
DATE=$(git show -s --format=%cs)
|
||||
"$CORE_ROOT"/scripts/create-provider-data-rs.py "$TMP/_providers" "$DATE" >"$CORE_ROOT/src/provider/data.rs"
|
||||
rustfmt "$CORE_ROOT/src/provider/data.rs"
|
||||
rm -fr "$TMP"
|
||||
|
||||
cd "$CORE_ROOT"
|
||||
test -z "$(git status --porcelain src/provider/data.rs)"
|
||||
@@ -339,13 +339,13 @@ only on image changes.
|
||||
|
||||
In older specs, the profile-image was sent as an attachment
|
||||
and `Chat-User-Avatar:` specified its name.
|
||||
However, it turned out that these attachments are kind of unuexpected to users,
|
||||
However, it turned out that these attachements are kind of unuexpected to users,
|
||||
therefore the profile-image go to the header now.
|
||||
|
||||
|
||||
# Locations
|
||||
|
||||
Locations can be attached to messages using
|
||||
Locations can be attachted to messages using
|
||||
[standard kml-files](https://www.opengeospatial.org/standards/kml/)
|
||||
with well-known names.
|
||||
|
||||
|
||||
+2
-5
@@ -427,7 +427,7 @@ impl Config {
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
/// Removes an existing account entirely.
|
||||
/// Removes an existing acccount entirely.
|
||||
pub async fn remove_account(&mut self, id: u32) -> Result<()> {
|
||||
{
|
||||
if let Some(idx) = self.inner.accounts.iter().position(|e| e.id == id) {
|
||||
@@ -476,18 +476,15 @@ impl Config {
|
||||
struct AccountConfig {
|
||||
/// Unique id.
|
||||
pub id: u32,
|
||||
|
||||
/// Root directory for all data for this account.
|
||||
///
|
||||
/// The path is relative to the account manager directory.
|
||||
pub dir: std::path::PathBuf,
|
||||
|
||||
/// Universally unique account identifier.
|
||||
pub uuid: Uuid,
|
||||
}
|
||||
|
||||
impl AccountConfig {
|
||||
/// Get the canonical dbfile name for this configuration.
|
||||
/// Get the canoncial dbfile name for this configuration.
|
||||
pub fn dbfile(&self, accounts_dir: &Path) -> std::path::PathBuf {
|
||||
accounts_dir.join(&self.dir).join(DB_NAME)
|
||||
}
|
||||
|
||||
+1
-1
@@ -235,7 +235,7 @@ mod tests {
|
||||
assert!(Aheader::from_str("foo").is_err());
|
||||
assert!(Aheader::from_str("\n\n\n").is_err());
|
||||
assert!(Aheader::from_str(" ;;").is_err());
|
||||
assert!(Aheader::from_str("addr=a@t.de; unknown=1; keydata=jau").is_err());
|
||||
assert!(Aheader::from_str("addr=a@t.de; unknwon=1; keydata=jau").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+1
-3
@@ -644,7 +644,6 @@ Authentication-Results: dkim=";
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[ignore = "Disallowing keychanges is disabled for now"]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_handle_authres_fails() -> Result<()> {
|
||||
let mut tcm = TestContextManager::new();
|
||||
@@ -822,8 +821,7 @@ Authentication-Results: dkim=";
|
||||
.insert_str(0, "Authentication-Results: example.net; dkim=fail\n");
|
||||
let rcvd = bob.recv_msg(&sent).await;
|
||||
|
||||
// Disallowing keychanges is disabled for now:
|
||||
// assert!(rcvd.error.unwrap().contains("DKIM failed"));
|
||||
assert!(rcvd.error.unwrap().contains("DKIM failed"));
|
||||
// The message info should contain a warning:
|
||||
assert!(message::get_msg_info(&bob, rcvd.id)
|
||||
.await
|
||||
|
||||
+28
-22
@@ -46,7 +46,7 @@ use crate::{location, sql};
|
||||
pub enum ChatItem {
|
||||
/// Chat message stored in the database.
|
||||
Message {
|
||||
/// Database ID of the message.
|
||||
/// Database ID of the messsage.
|
||||
msg_id: MsgId,
|
||||
},
|
||||
|
||||
@@ -276,7 +276,7 @@ impl ChatId {
|
||||
grpname,
|
||||
grpid,
|
||||
create_blocked,
|
||||
create_smeared_timestamp(context),
|
||||
create_smeared_timestamp(context).await,
|
||||
create_protected,
|
||||
param.unwrap_or_default(),
|
||||
],
|
||||
@@ -482,7 +482,7 @@ impl ChatId {
|
||||
self,
|
||||
&msg_text,
|
||||
cmd,
|
||||
create_smeared_timestamp(context),
|
||||
create_smeared_timestamp(context).await,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -907,8 +907,7 @@ impl ChatId {
|
||||
|
||||
async fn parent_query<T, F>(self, context: &Context, fields: &str, f: F) -> Result<Option<T>>
|
||||
where
|
||||
F: Send + FnOnce(&rusqlite::Row) -> rusqlite::Result<T>,
|
||||
T: Send + 'static,
|
||||
F: FnOnce(&rusqlite::Row) -> rusqlite::Result<T>,
|
||||
{
|
||||
let sql = &context.sql;
|
||||
let query = format!(
|
||||
@@ -1206,10 +1205,7 @@ impl Chat {
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
context,
|
||||
"failed to load contacts for {}: {:#}", chat.id, err
|
||||
);
|
||||
error!(context, "faild to load contacts for {}: {:#}", chat.id, err);
|
||||
}
|
||||
}
|
||||
chat.name = chat_name;
|
||||
@@ -1884,10 +1880,7 @@ pub(crate) async fn update_special_chat_names(context: &Context) -> Result<()> {
|
||||
/// [`Deref`]: std::ops::Deref
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ChatIdBlocked {
|
||||
/// Chat ID.
|
||||
pub id: ChatId,
|
||||
|
||||
/// Whether the chat is blocked, unblocked or a contact request.
|
||||
pub blocked: Blocked,
|
||||
}
|
||||
|
||||
@@ -1959,6 +1952,7 @@ impl ChatIdBlocked {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let created_timestamp = create_smeared_timestamp(context).await;
|
||||
let chat_id = context
|
||||
.sql
|
||||
.transaction(move |transaction| {
|
||||
@@ -1971,7 +1965,7 @@ impl ChatIdBlocked {
|
||||
chat_name,
|
||||
params.to_string(),
|
||||
create_blocked as u8,
|
||||
create_smeared_timestamp(context)
|
||||
created_timestamp,
|
||||
],
|
||||
)?;
|
||||
let chat_id = ChatId::new(
|
||||
@@ -2119,7 +2113,7 @@ async fn prepare_msg_common(
|
||||
context,
|
||||
msg,
|
||||
update_msg_id,
|
||||
create_smeared_timestamp(context),
|
||||
create_smeared_timestamp(context).await,
|
||||
)
|
||||
.await?;
|
||||
msg.chat_id = chat_id;
|
||||
@@ -2150,7 +2144,7 @@ pub async fn is_contact_in_chat(
|
||||
|
||||
/// Sends a message object to a chat.
|
||||
///
|
||||
/// Sends the event #DC_EVENT_MSGS_CHANGED on success.
|
||||
/// Sends the event #DC_EVENT_MSGS_CHANGED on succcess.
|
||||
/// However, this does not imply, the message really reached the recipient -
|
||||
/// sending may be delayed eg. due to network problems. However, from your
|
||||
/// view, you're done with the message. Sooner or later it will find its way.
|
||||
@@ -2514,7 +2508,7 @@ pub async fn get_chat_msgs_ex(
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
// GLOB is used here instead of LIKE because it is case-sensitive
|
||||
// GLOB is used here instead of LIKE becase it is case-sensitive
|
||||
"SELECT m.id AS id, m.timestamp AS timestamp, m.param AS param, m.from_id AS from_id, m.to_id AS to_id
|
||||
FROM msgs m
|
||||
WHERE m.chat_id=?
|
||||
@@ -2844,7 +2838,7 @@ pub async fn create_group_chat(
|
||||
Chattype::Group,
|
||||
chat_name,
|
||||
grpid,
|
||||
create_smeared_timestamp(context),
|
||||
create_smeared_timestamp(context).await,
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
@@ -2902,7 +2896,7 @@ pub async fn create_broadcast_list(context: &Context) -> Result<ChatId> {
|
||||
Chattype::Broadcast,
|
||||
chat_name,
|
||||
grpid,
|
||||
create_smeared_timestamp(context),
|
||||
create_smeared_timestamp(context).await,
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
@@ -3363,7 +3357,7 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId)
|
||||
if let Some(reason) = chat.why_cant_send(context).await? {
|
||||
bail!("cannot send to {}: {}", chat_id, reason);
|
||||
}
|
||||
curr_timestamp = create_smeared_timestamps(context, msg_ids.len());
|
||||
curr_timestamp = create_smeared_timestamps(context, msg_ids.len()).await;
|
||||
let ids = context
|
||||
.sql
|
||||
.query_map(
|
||||
@@ -3565,7 +3559,7 @@ pub async fn add_device_msg_with_importance(
|
||||
msg.try_calc_and_set_dimensions(context).await.ok();
|
||||
prepare_msg_blob(context, msg).await?;
|
||||
|
||||
let timestamp_sent = create_smeared_timestamp(context);
|
||||
let timestamp_sent = create_smeared_timestamp(context).await;
|
||||
|
||||
// makes sure, the added message is the last one,
|
||||
// even if the date is wrong (useful esp. when warning about bad dates)
|
||||
@@ -3665,7 +3659,7 @@ pub async fn was_device_msg_ever_added(context: &Context, label: &str) -> Result
|
||||
// - deletion in `msgs` with `ContactId::DEVICE` makes sure,
|
||||
// no wrong information are shown in the device chat
|
||||
// - deletion in `devmsglabels` makes sure,
|
||||
// deleted messages are reset and useful messages can be added again
|
||||
// deleted messages are resetted and useful messages can be added again
|
||||
// - we reset the config-option `QuotaExceeding`
|
||||
// that is used as a helper to drive the corresponding device message.
|
||||
pub(crate) async fn delete_and_reset_all_device_msgs(context: &Context) -> Result<()> {
|
||||
@@ -4093,6 +4087,7 @@ mod tests {
|
||||
send_text_msg(&alice, alice_chat_id, "populate".to_string()).await?;
|
||||
|
||||
add_contact_to_chat(&alice, alice_chat_id, bob_id).await?;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
let add1 = alice.pop_sent_msg().await;
|
||||
|
||||
add_contact_to_chat(&alice, alice_chat_id, claire_id).await?;
|
||||
@@ -4111,18 +4106,29 @@ mod tests {
|
||||
|
||||
remove_contact_from_chat(&alice, alice_chat_id, daisy_id).await?;
|
||||
let remove2 = alice.pop_sent_msg().await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
assert_eq!(get_chat_contacts(&alice, alice_chat_id).await?.len(), 2);
|
||||
|
||||
// Bob receives the add and deletion messages out of order
|
||||
let bob = TestContext::new_bob().await;
|
||||
bob.recv_msg(&add1).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
bob.recv_msg(&add3).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
let bob_chat_id = bob.recv_msg(&add2).await.chat_id;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?.len(), 4);
|
||||
|
||||
bob.recv_msg(&remove2).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
bob.recv_msg(&remove1).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
|
||||
|
||||
assert_eq!(get_chat_contacts(&bob, bob_chat_id).await?.len(), 2);
|
||||
|
||||
Ok(())
|
||||
@@ -4434,7 +4440,7 @@ mod tests {
|
||||
.unwrap();
|
||||
assert!(msg_id2.is_unset());
|
||||
|
||||
// ... unless everything is deleted and reset - as needed eg. on device switch
|
||||
// ... unless everything is deleted and resetted - as needed eg. on device switch
|
||||
delete_and_reset_all_device_msgs(&t).await.unwrap();
|
||||
assert!(!was_device_msg_ever_added(&t, "some-label").await.unwrap());
|
||||
let msg_id3 = add_device_msg(&t, Some("some-label"), Some(&mut msg))
|
||||
|
||||
+12
-30
@@ -1,7 +1,5 @@
|
||||
//! # Key-value configuration management.
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{ensure, Context as _, Result};
|
||||
use strum::{EnumProperty, IntoEnumIterator};
|
||||
use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
|
||||
@@ -175,10 +173,6 @@ pub enum Config {
|
||||
#[strum(props(default = "0"))]
|
||||
DeleteDeviceAfter,
|
||||
|
||||
/// Move messages to the Trash folder instead of marking them "\Deleted". Overrides
|
||||
/// `ProviderOptions::delete_to_trash`.
|
||||
DeleteToTrash,
|
||||
|
||||
/// Save raw MIME messages with headers in the database if true.
|
||||
SaveMimeHeaders,
|
||||
|
||||
@@ -233,9 +227,6 @@ pub enum Config {
|
||||
/// Configured "Sent" folder.
|
||||
ConfiguredSentboxFolder,
|
||||
|
||||
/// Configured "Trash" folder.
|
||||
ConfiguredTrashFolder,
|
||||
|
||||
/// Unix timestamp of the last successful configuration.
|
||||
ConfiguredTimestamp,
|
||||
|
||||
@@ -246,7 +237,7 @@ pub enum Config {
|
||||
Configured,
|
||||
|
||||
/// All secondary self addresses separated by spaces
|
||||
/// (`addr1@example.org addr2@example.org addr3@example.org`)
|
||||
/// (`addr1@example.org addr2@exapmle.org addr3@example.org`)
|
||||
SecondaryAddrs,
|
||||
|
||||
/// Read-only core version string.
|
||||
@@ -300,9 +291,6 @@ pub enum Config {
|
||||
/// See `crate::authres::update_authservid_candidates`.
|
||||
AuthservIdCandidates,
|
||||
|
||||
/// Make all outgoing messages with Autocrypt header "multipart/signed".
|
||||
SignUnencrypted,
|
||||
|
||||
/// Let the core save all events to the database.
|
||||
/// This value is used internally to remember the MsgId of the logging xdc
|
||||
#[strum(props(default = "0"))]
|
||||
@@ -339,37 +327,30 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns Some(T) if a value for the given key exists and was successfully parsed.
|
||||
/// Returns None if could not parse.
|
||||
pub async fn get_config_parsed<T: FromStr>(&self, key: Config) -> Result<Option<T>> {
|
||||
self.get_config(key)
|
||||
.await
|
||||
.map(|s: Option<String>| s.and_then(|s| s.parse().ok()))
|
||||
}
|
||||
|
||||
/// Returns 32-bit signed integer configuration value for the given key.
|
||||
pub async fn get_config_int(&self, key: Config) -> Result<i32> {
|
||||
Ok(self.get_config_parsed(key).await?.unwrap_or_default())
|
||||
self.get_config(key)
|
||||
.await
|
||||
.map(|s: Option<String>| s.and_then(|s| s.parse().ok()).unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Returns 64-bit signed integer configuration value for the given key.
|
||||
pub async fn get_config_i64(&self, key: Config) -> Result<i64> {
|
||||
Ok(self.get_config_parsed(key).await?.unwrap_or_default())
|
||||
self.get_config(key)
|
||||
.await
|
||||
.map(|s: Option<String>| s.and_then(|s| s.parse().ok()).unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Returns 64-bit unsigned integer configuration value for the given key.
|
||||
pub async fn get_config_u64(&self, key: Config) -> Result<u64> {
|
||||
Ok(self.get_config_parsed(key).await?.unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Returns boolean configuration value (if any) for the given key.
|
||||
pub async fn get_config_bool_opt(&self, key: Config) -> Result<Option<bool>> {
|
||||
Ok(self.get_config_parsed::<i32>(key).await?.map(|x| x != 0))
|
||||
self.get_config(key)
|
||||
.await
|
||||
.map(|s: Option<String>| s.and_then(|s| s.parse().ok()).unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Returns boolean configuration value for the given key.
|
||||
pub async fn get_config_bool(&self, key: Config) -> Result<bool> {
|
||||
Ok(self.get_config_bool_opt(key).await?.unwrap_or_default())
|
||||
Ok(self.get_config_int(key).await? != 0)
|
||||
}
|
||||
|
||||
/// Returns true if movebox ("DeltaChat" folder) should be watched.
|
||||
@@ -569,6 +550,7 @@ fn get_config_keys_string() -> String {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
+2
-6
@@ -249,7 +249,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
}
|
||||
}
|
||||
},
|
||||
strict_tls: Some(provider.opt.strict_tls),
|
||||
strict_tls: Some(provider.strict_tls),
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -338,7 +338,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
.collect();
|
||||
let provider_strict_tls = param
|
||||
.provider
|
||||
.map_or(socks5_config.is_some(), |provider| provider.opt.strict_tls);
|
||||
.map_or(socks5_config.is_some(), |provider| provider.strict_tls);
|
||||
|
||||
let smtp_config_task = task::spawn(async move {
|
||||
let mut smtp_configured = false;
|
||||
@@ -646,14 +646,10 @@ async fn try_smtp_one_param(
|
||||
}
|
||||
}
|
||||
|
||||
/// Failure to connect and login with email client configuration.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("Trying {config}…\nError: {msg}")]
|
||||
pub struct ConfigurationError {
|
||||
/// Tried configuration description.
|
||||
config: String,
|
||||
|
||||
/// Error message.
|
||||
msg: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::provider::{Protocol, Socket};
|
||||
|
||||
/// Set of variable parameters to try during configuration.
|
||||
///
|
||||
/// Can be loaded from offline provider database, online configuration
|
||||
/// Can be loaded from offline provider database, online configuraiton
|
||||
/// or derived from user entered parameters.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct ServerParams {
|
||||
|
||||
+5
-5
@@ -103,7 +103,7 @@ pub(crate) const DC_RESEND_USER_AVATAR_DAYS: i64 = 14;
|
||||
// warn about an outdated app after a given number of days.
|
||||
// as we use the "provider-db generation date" as reference (that might not be updated very often)
|
||||
// and as not all system get speedy updates,
|
||||
// do not use too small value that will annoy users checking for nonexistent updates.
|
||||
// do not use too small value that will annoy users checking for nonexistant updates.
|
||||
pub(crate) const DC_OUTDATED_WARNING_DAYS: i64 = 365;
|
||||
|
||||
/// messages that should be deleted get this chat_id; the messages are deleted from the working thread later then. This is also needed as rfc724_mid should be preset as long as the message is not deleted on the server (otherwise it is downloaded again)
|
||||
@@ -190,18 +190,18 @@ pub const DC_LP_AUTH_NORMAL: i32 = 0x4;
|
||||
pub const DC_LP_AUTH_FLAGS: i32 = DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL;
|
||||
|
||||
/// How many existing messages shall be fetched after configuration.
|
||||
pub(crate) const DC_FETCH_EXISTING_MSGS_COUNT: i64 = 100;
|
||||
pub const DC_FETCH_EXISTING_MSGS_COUNT: i64 = 100;
|
||||
|
||||
// max. width/height of an avatar
|
||||
pub(crate) const BALANCED_AVATAR_SIZE: u32 = 256;
|
||||
pub(crate) const WORSE_AVATAR_SIZE: u32 = 128;
|
||||
pub const BALANCED_AVATAR_SIZE: u32 = 256;
|
||||
pub const WORSE_AVATAR_SIZE: u32 = 128;
|
||||
|
||||
// max. width/height of images
|
||||
pub const BALANCED_IMAGE_SIZE: u32 = 1280;
|
||||
pub const WORSE_IMAGE_SIZE: u32 = 640;
|
||||
|
||||
// this value can be increased if the folder configuration is changed and must be redone on next program start
|
||||
pub(crate) const DC_FOLDERS_CONFIGURED_VERSION: i32 = 4;
|
||||
pub(crate) const DC_FOLDERS_CONFIGURED_VERSION: i32 = 3;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
+38
-36
@@ -869,45 +869,47 @@ impl Contact {
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Adds blocked mailinglists as contacts
|
||||
/// to allow unblocking them as if they are contacts
|
||||
/// (this way, only one unblock-ffi is needed and only one set of ui-functions,
|
||||
/// from the users perspective,
|
||||
/// there is not much difference in an email- and a mailinglist-address)
|
||||
// add blocked mailinglists as contacts
|
||||
// to allow unblocking them as if they are contacts
|
||||
// (this way, only one unblock-ffi is needed and only one set of ui-functions,
|
||||
// from the users perspective,
|
||||
// there is not much difference in an email- and a mailinglist-address)
|
||||
async fn update_blocked_mailinglist_contacts(context: &Context) -> Result<()> {
|
||||
context
|
||||
let blocked_mailinglists = context
|
||||
.sql
|
||||
.transaction(move |transaction| {
|
||||
let mut stmt = transaction
|
||||
.prepare("SELECT name, grpid FROM chats WHERE type=? AND blocked=?")?;
|
||||
let rows = stmt.query_map(params![Chattype::Mailinglist, Blocked::Yes], |row| {
|
||||
let name: String = row.get(0)?;
|
||||
let grpid: String = row.get(1)?;
|
||||
Ok((name, grpid))
|
||||
})?;
|
||||
let blocked_mailinglists = rows.collect::<std::result::Result<Vec<_>, _>>()?;
|
||||
for (name, grpid) in blocked_mailinglists {
|
||||
let count = transaction.query_row(
|
||||
"SELECT COUNT(id) FROM contacts WHERE addr=?",
|
||||
[&grpid],
|
||||
|row| {
|
||||
let count: isize = row.get(0)?;
|
||||
Ok(count)
|
||||
},
|
||||
)?;
|
||||
if count == 0 {
|
||||
transaction.execute("INSERT INTO contacts (addr) VALUES (?)", [&grpid])?;
|
||||
}
|
||||
|
||||
// Always do an update in case the blocking is reset or name is changed.
|
||||
transaction.execute(
|
||||
"UPDATE contacts SET name=?, origin=?, blocked=1 WHERE addr=?",
|
||||
params![&name, Origin::MailinglistAddress, &grpid],
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.query_map(
|
||||
"SELECT name, grpid FROM chats WHERE type=? AND blocked=?;",
|
||||
paramsv![Chattype::Mailinglist, Blocked::Yes],
|
||||
|row| Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)),
|
||||
|rows| {
|
||||
rows.collect::<std::result::Result<Vec<_>, _>>()
|
||||
.map_err(Into::into)
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
for (name, grpid) in blocked_mailinglists {
|
||||
if !context
|
||||
.sql
|
||||
.exists(
|
||||
"SELECT COUNT(id) FROM contacts WHERE addr=?;",
|
||||
paramsv![grpid],
|
||||
)
|
||||
.await?
|
||||
{
|
||||
context
|
||||
.sql
|
||||
.execute("INSERT INTO contacts (addr) VALUES (?);", paramsv![grpid])
|
||||
.await?;
|
||||
}
|
||||
// always do an update in case the blocking is reset or name is changed
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE contacts SET name=?, origin=?, blocked=1 WHERE addr=?;",
|
||||
paramsv![name, Origin::MailinglistAddress, grpid],
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+7
-61
@@ -4,11 +4,10 @@ use std::collections::{BTreeMap, HashMap};
|
||||
use std::ffi::OsString;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant, SystemTime};
|
||||
|
||||
use anyhow::{bail, ensure, Context as _, Result};
|
||||
use anyhow::{bail, ensure, Result};
|
||||
use async_channel::{self as channel, Receiver, Sender};
|
||||
use ratelimit::Ratelimit;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
@@ -27,7 +26,6 @@ use crate::quota::QuotaInfo;
|
||||
use crate::scheduler::Scheduler;
|
||||
use crate::sql::Sql;
|
||||
use crate::stock_str::StockStrings;
|
||||
use crate::timesmearing::SmearedTimestamp;
|
||||
use crate::tools::{duration_to_str, time};
|
||||
|
||||
/// Builder for the [`Context`].
|
||||
@@ -114,7 +112,7 @@ impl ContextBuilder {
|
||||
/// Sets the event channel for this [`Context`].
|
||||
///
|
||||
/// Mostly useful when using multiple [`Context`]s, this allows creating one [`Events`]
|
||||
/// channel and passing it to all [`Context`]s so all events are received on the same
|
||||
/// channel and passing it to all [`Context`]s so all events are recieved on the same
|
||||
/// channel.
|
||||
///
|
||||
/// Note that the [account manager](crate::accounts::Accounts) is designed to handle the
|
||||
@@ -190,13 +188,13 @@ pub struct InnerContext {
|
||||
/// Blob directory path
|
||||
pub(crate) blobdir: PathBuf,
|
||||
pub(crate) sql: Sql,
|
||||
pub(crate) smeared_timestamp: SmearedTimestamp,
|
||||
pub(crate) last_smeared_timestamp: RwLock<i64>,
|
||||
running_state: RwLock<RunningState>,
|
||||
/// Mutex to avoid generating the key for the user more than once.
|
||||
pub(crate) generating_key_mutex: Mutex<()>,
|
||||
/// Mutex to enforce only a single running oauth2 is running.
|
||||
pub(crate) oauth2_mutex: Mutex<()>,
|
||||
/// Mutex to prevent a race condition when a "your pw is wrong" warning is sent, resulting in multiple messages being sent.
|
||||
/// Mutex to prevent a race condition when a "your pw is wrong" warning is sent, resulting in multiple messeges being sent.
|
||||
pub(crate) wrong_pw_warning_mutex: Mutex<()>,
|
||||
pub(crate) translated_stockstrings: StockStrings,
|
||||
pub(crate) events: Events,
|
||||
@@ -208,12 +206,6 @@ pub struct InnerContext {
|
||||
/// Set to `None` if quota was never tried to load.
|
||||
pub(crate) quota: RwLock<Option<QuotaInfo>>,
|
||||
|
||||
/// Set to true if quota update is requested.
|
||||
pub(crate) quota_update_request: AtomicBool,
|
||||
|
||||
/// IMAP UID resync request.
|
||||
pub(crate) resync_request: AtomicBool,
|
||||
|
||||
/// Server ID response if ID capability is supported
|
||||
/// and the server returned non-NIL on the inbox connection.
|
||||
/// <https://datatracker.ietf.org/doc/html/rfc2971>
|
||||
@@ -234,7 +226,7 @@ pub struct InnerContext {
|
||||
/// `last_error` should be used to avoid races with the event thread.
|
||||
pub(crate) last_error: std::sync::RwLock<String>,
|
||||
|
||||
/// If debug logging is enabled, this contains all necessary information
|
||||
/// If debug logging is enabled, this contains all neccesary information
|
||||
pub(crate) debug_logging: RwLock<Option<DebugLogging>>,
|
||||
}
|
||||
|
||||
@@ -242,7 +234,7 @@ pub struct InnerContext {
|
||||
pub(crate) struct DebugLogging {
|
||||
/// The message containing the logging xdc
|
||||
pub(crate) msg_id: MsgId,
|
||||
/// Handle to the background task responsible for sending
|
||||
/// Handle to the background task responisble for sending
|
||||
pub(crate) loop_handle: task::JoinHandle<()>,
|
||||
/// Channel that log events should be send to
|
||||
/// A background loop will receive and handle them
|
||||
@@ -364,7 +356,7 @@ impl Context {
|
||||
blobdir,
|
||||
running_state: RwLock::new(Default::default()),
|
||||
sql: Sql::new(dbfile),
|
||||
smeared_timestamp: SmearedTimestamp::new(),
|
||||
last_smeared_timestamp: RwLock::new(0),
|
||||
generating_key_mutex: Mutex::new(()),
|
||||
oauth2_mutex: Mutex::new(()),
|
||||
wrong_pw_warning_mutex: Mutex::new(()),
|
||||
@@ -373,8 +365,6 @@ impl Context {
|
||||
scheduler: RwLock::new(None),
|
||||
ratelimit: RwLock::new(Ratelimit::new(Duration::new(60, 0), 6.0)), // Allow to send 6 messages immediately, no more than once every 10 seconds.
|
||||
quota: RwLock::new(None),
|
||||
quota_update_request: AtomicBool::new(false),
|
||||
resync_request: AtomicBool::new(false),
|
||||
server_id: RwLock::new(None),
|
||||
creation_time: std::time::SystemTime::now(),
|
||||
last_full_folder_scan: Mutex::new(None),
|
||||
@@ -633,10 +623,6 @@ impl Context {
|
||||
.get_config(Config::ConfiguredMvboxFolder)
|
||||
.await?
|
||||
.unwrap_or_else(|| "<unset>".to_string());
|
||||
let configured_trash_folder = self
|
||||
.get_config(Config::ConfiguredTrashFolder)
|
||||
.await?
|
||||
.unwrap_or_else(|| "<unset>".to_string());
|
||||
|
||||
let mut res = get_info();
|
||||
|
||||
@@ -703,7 +689,6 @@ impl Context {
|
||||
res.insert("configured_inbox_folder", configured_inbox_folder);
|
||||
res.insert("configured_sentbox_folder", configured_sentbox_folder);
|
||||
res.insert("configured_mvbox_folder", configured_mvbox_folder);
|
||||
res.insert("configured_trash_folder", configured_trash_folder);
|
||||
res.insert("mdns_enabled", mdns_enabled.to_string());
|
||||
res.insert("e2ee_enabled", e2ee_enabled.to_string());
|
||||
res.insert(
|
||||
@@ -737,12 +722,6 @@ impl Context {
|
||||
.await?
|
||||
.to_string(),
|
||||
);
|
||||
res.insert(
|
||||
"delete_to_trash",
|
||||
self.get_config(Config::DeleteToTrash)
|
||||
.await?
|
||||
.unwrap_or_else(|| "<unset>".to_string()),
|
||||
);
|
||||
res.insert(
|
||||
"last_housekeeping",
|
||||
self.get_config_int(Config::LastHousekeeping)
|
||||
@@ -767,12 +746,6 @@ impl Context {
|
||||
.await?
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
res.insert(
|
||||
"sign_unencrypted",
|
||||
self.get_config_int(Config::SignUnencrypted)
|
||||
.await?
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
res.insert(
|
||||
"debug_logging",
|
||||
@@ -914,33 +887,6 @@ impl Context {
|
||||
Ok(mvbox.as_deref() == Some(folder_name))
|
||||
}
|
||||
|
||||
/// Returns true if given folder name is the name of the trash folder.
|
||||
pub async fn is_trash(&self, folder_name: &str) -> Result<bool> {
|
||||
let trash = self.get_config(Config::ConfiguredTrashFolder).await?;
|
||||
Ok(trash.as_deref() == Some(folder_name))
|
||||
}
|
||||
|
||||
pub(crate) async fn should_delete_to_trash(&self) -> Result<bool> {
|
||||
if let Some(v) = self.get_config_bool_opt(Config::DeleteToTrash).await? {
|
||||
return Ok(v);
|
||||
}
|
||||
if let Some(provider) = self.get_configured_provider().await? {
|
||||
return Ok(provider.opt.delete_to_trash);
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Returns `target` for deleted messages as per `imap` table. Empty string means "delete w/o
|
||||
/// moving to trash".
|
||||
pub(crate) async fn get_delete_msgs_target(&self) -> Result<String> {
|
||||
if !self.should_delete_to_trash().await? {
|
||||
return Ok("".into());
|
||||
}
|
||||
self.get_config(Config::ConfiguredTrashFolder)
|
||||
.await?
|
||||
.context("No configured trash folder")
|
||||
}
|
||||
|
||||
pub(crate) fn derive_blobdir(dbfile: &Path) -> PathBuf {
|
||||
let mut blob_fname = OsString::new();
|
||||
blob_fname.push(dbfile.file_name().unwrap_or_default());
|
||||
|
||||
+1
-2
@@ -99,8 +99,7 @@ pub(crate) async fn prepare_decryption(
|
||||
from,
|
||||
autocrypt_header.as_ref(),
|
||||
message_time,
|
||||
// Disallowing keychanges is disabled for now:
|
||||
true, // dkim_results.allow_keychange,
|
||||
dkim_results.allow_keychange,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
+8
-3
@@ -13,6 +13,7 @@ use crate::imap::{Imap, ImapActionResult};
|
||||
use crate::job::{self, Action, Job, Status};
|
||||
use crate::message::{Message, MsgId, Viewtype};
|
||||
use crate::mimeparser::{MimeMessage, Part};
|
||||
use crate::param::Params;
|
||||
use crate::tools::time;
|
||||
use crate::{job_try, stock_str, EventType};
|
||||
|
||||
@@ -22,7 +23,7 @@ use crate::{job_try, stock_str, EventType};
|
||||
/// need to be downloaded completely to handle them correctly,
|
||||
/// eg. to assign them to the correct chat.
|
||||
/// As these messages are typically small,
|
||||
/// they're caught by `MIN_DOWNLOAD_LIMIT`.
|
||||
/// they're catched by `MIN_DOWNLOAD_LIMIT`.
|
||||
const MIN_DOWNLOAD_LIMIT: u32 = 32768;
|
||||
|
||||
/// If a message is downloaded only partially
|
||||
@@ -85,7 +86,11 @@ impl MsgId {
|
||||
DownloadState::Available | DownloadState::Failure => {
|
||||
self.update_download_state(context, DownloadState::InProgress)
|
||||
.await?;
|
||||
job::add(context, Job::new(Action::DownloadMsg, self.to_u32())).await?;
|
||||
job::add(
|
||||
context,
|
||||
Job::new(Action::DownloadMsg, self.to_u32(), Params::new(), 0),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -133,7 +138,7 @@ impl Job {
|
||||
context
|
||||
.sql
|
||||
.query_row_optional(
|
||||
"SELECT uid, folder FROM imap WHERE rfc724_mid=? AND target=folder",
|
||||
"SELECT uid, folder FROM imap WHERE rfc724_mid=? AND target!=''",
|
||||
paramsv![msg.rfc724_mid],
|
||||
|row| {
|
||||
let server_uid: u32 = row.get(0)?;
|
||||
|
||||
-13
@@ -124,19 +124,6 @@ impl EncryptHelper {
|
||||
|
||||
Ok(ctext)
|
||||
}
|
||||
|
||||
/// Signs the passed-in `mail` using the private key from `context`.
|
||||
/// Returns the payload and the signature.
|
||||
pub async fn sign(
|
||||
self,
|
||||
context: &Context,
|
||||
mail: lettre_email::PartBuilder,
|
||||
) -> Result<(lettre_email::MimeMessage, String)> {
|
||||
let sign_key = SignedSecretKey::load_self(context).await?;
|
||||
let mime_message = mail.build();
|
||||
let signature = pgp::pk_calc_signature(mime_message.as_string().as_bytes(), &sign_key)?;
|
||||
Ok((mime_message, signature))
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures a private key exists for the configured user.
|
||||
|
||||
+4
-10
@@ -588,25 +588,19 @@ pub(crate) async fn delete_expired_imap_messages(context: &Context) -> Result<()
|
||||
now - max(delete_server_after, MIN_DELETE_SERVER_AFTER),
|
||||
),
|
||||
};
|
||||
let target = context.get_delete_msgs_target().await?;
|
||||
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE imap
|
||||
SET target=?
|
||||
SET target=''
|
||||
WHERE rfc724_mid IN (
|
||||
SELECT rfc724_mid FROM msgs
|
||||
WHERE ((download_state = 0 AND timestamp < ?) OR
|
||||
(download_state != 0 AND timestamp < ?) OR
|
||||
(ephemeral_timestamp != 0 AND ephemeral_timestamp <= ?))
|
||||
)",
|
||||
paramsv![
|
||||
target,
|
||||
threshold_timestamp,
|
||||
threshold_timestamp_extended,
|
||||
now,
|
||||
],
|
||||
paramsv![threshold_timestamp, threshold_timestamp_extended, now],
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -650,7 +644,7 @@ mod tests {
|
||||
use crate::download::DownloadState;
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::test_utils::TestContext;
|
||||
use crate::timesmearing::MAX_SECONDS_TO_LEND_FROM_FUTURE;
|
||||
use crate::tools::MAX_SECONDS_TO_LEND_FROM_FUTURE;
|
||||
use crate::{
|
||||
chat::{self, create_group_chat, send_text_msg, Chat, ChatItem, ProtectionStatus},
|
||||
tools::IsNoneOrEmpty,
|
||||
@@ -1273,7 +1267,7 @@ mod tests {
|
||||
// protection.
|
||||
//
|
||||
// Previously Delta Chat fallen back to using <first@example.com> in this case and
|
||||
// compared received timer value to the timer value of the <first@example.com>. Because
|
||||
// compared received timer value to the timer value of the <first@examle.com>. Because
|
||||
// their timer values are the same ("disabled"), Delta Chat assumed that the timer was not
|
||||
// changed explicitly and the change should be ignored.
|
||||
//
|
||||
|
||||
+2
-2
@@ -70,7 +70,7 @@ impl Events {
|
||||
pub struct EventEmitter(Receiver<Event>);
|
||||
|
||||
impl EventEmitter {
|
||||
/// Async recv of an event. Return `None` if the `Sender` has been dropped.
|
||||
/// Async recv of an event. Return `None` if the `Sender` has been droped.
|
||||
pub async fn recv(&self) -> Option<Event> {
|
||||
self.0.recv().await.ok()
|
||||
}
|
||||
@@ -89,7 +89,7 @@ impl futures::stream::Stream for EventEmitter {
|
||||
|
||||
/// The event emitted by a [`Context`] from an [`EventEmitter`].
|
||||
///
|
||||
/// Events are documented on the C/FFI API in `deltachat.h` as `DC_EVENT_*` constants. The
|
||||
/// Events are documented on the C/FFI API in `deltachat.h` as `DC_EVENT_*` contants. The
|
||||
/// context emits them in relation to various operations happening, a lot of these are again
|
||||
/// documented in `deltachat.h`.
|
||||
///
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
/// Fuzzing target for simplify().
|
||||
///
|
||||
/// Calls simplify() and panics if simplify() panics.
|
||||
/// Does not return any value to avoid exposing internal crate types.
|
||||
/// Does not return any vaule to avoid exposing internal crate types.
|
||||
#[cfg(fuzzing)]
|
||||
pub fn simplify(input: String, is_chat_message: bool) {
|
||||
crate::simplify::simplify(input, is_chat_message);
|
||||
|
||||
+91
-162
@@ -113,17 +113,13 @@ impl async_imap::Authenticator for OAuth2 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum FolderMeaning {
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
enum FolderMeaning {
|
||||
Unknown,
|
||||
|
||||
/// Spam folder.
|
||||
Spam,
|
||||
Inbox,
|
||||
Mvbox,
|
||||
Sent,
|
||||
Trash,
|
||||
Drafts,
|
||||
Other,
|
||||
|
||||
/// Virtual folders.
|
||||
///
|
||||
@@ -135,15 +131,13 @@ pub enum FolderMeaning {
|
||||
}
|
||||
|
||||
impl FolderMeaning {
|
||||
pub fn to_config(self) -> Option<Config> {
|
||||
fn to_config(self) -> Option<Config> {
|
||||
match self {
|
||||
FolderMeaning::Unknown => None,
|
||||
FolderMeaning::Spam => None,
|
||||
FolderMeaning::Inbox => Some(Config::ConfiguredInboxFolder),
|
||||
FolderMeaning::Mvbox => Some(Config::ConfiguredMvboxFolder),
|
||||
FolderMeaning::Sent => Some(Config::ConfiguredSentboxFolder),
|
||||
FolderMeaning::Trash => Some(Config::ConfiguredTrashFolder),
|
||||
FolderMeaning::Drafts => None,
|
||||
FolderMeaning::Other => None,
|
||||
FolderMeaning::Virtual => None,
|
||||
}
|
||||
}
|
||||
@@ -151,11 +145,8 @@ impl FolderMeaning {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ImapConfig {
|
||||
/// Email address.
|
||||
pub addr: String,
|
||||
pub lp: ServerLoginParam,
|
||||
|
||||
/// SOCKS 5 configuration.
|
||||
pub socks5_config: Option<Socks5Config>,
|
||||
pub strict_tls: bool,
|
||||
}
|
||||
@@ -279,7 +270,7 @@ impl Imap {
|
||||
param
|
||||
.provider
|
||||
.map_or(param.socks5_config.is_some(), |provider| {
|
||||
provider.opt.strict_tls
|
||||
provider.strict_tls
|
||||
}),
|
||||
idle_interrupt_receiver,
|
||||
)?;
|
||||
@@ -458,7 +449,7 @@ impl Imap {
|
||||
&mut self,
|
||||
context: &Context,
|
||||
watch_folder: &str,
|
||||
folder_meaning: FolderMeaning,
|
||||
is_spam_folder: bool,
|
||||
) -> Result<()> {
|
||||
if !context.sql.is_open().await {
|
||||
// probably shutdown
|
||||
@@ -467,7 +458,7 @@ impl Imap {
|
||||
self.prepare(context).await?;
|
||||
|
||||
let msgs_fetched = self
|
||||
.fetch_new_messages(context, watch_folder, folder_meaning, false)
|
||||
.fetch_new_messages(context, watch_folder, is_spam_folder, false)
|
||||
.await
|
||||
.context("fetch_new_messages")?;
|
||||
if msgs_fetched && context.get_config_delete_device_after().await?.is_some() {
|
||||
@@ -499,60 +490,49 @@ impl Imap {
|
||||
pub(crate) async fn resync_folder_uids(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
folder: &str,
|
||||
folder_meaning: FolderMeaning,
|
||||
folder: String,
|
||||
) -> Result<()> {
|
||||
// Collect pairs of UID and Message-ID.
|
||||
let mut msgs = BTreeMap::new();
|
||||
let mut msg_ids = BTreeMap::new();
|
||||
|
||||
let session = self
|
||||
.session
|
||||
.as_mut()
|
||||
.context("IMAP No connection established")?;
|
||||
|
||||
session.select_folder(context, Some(folder)).await?;
|
||||
session.select_folder(context, Some(&folder)).await?;
|
||||
|
||||
let mut list = session
|
||||
.uid_fetch("1:*", RFC724MID_UID)
|
||||
.await
|
||||
.with_context(|| format!("can't resync folder {folder}"))?;
|
||||
while let Some(fetch) = list.next().await {
|
||||
let fetch = fetch?;
|
||||
let headers = match get_fetch_headers(&fetch) {
|
||||
Ok(headers) => headers,
|
||||
Err(err) => {
|
||||
warn!(context, "Failed to parse FETCH headers: {}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let message_id = prefetch_get_message_id(&headers);
|
||||
let msg = fetch?;
|
||||
|
||||
if let (Some(uid), Some(rfc724_mid)) = (fetch.uid, message_id) {
|
||||
msgs.insert(
|
||||
uid,
|
||||
(
|
||||
rfc724_mid,
|
||||
target_folder(context, folder, folder_meaning, &headers).await?,
|
||||
),
|
||||
);
|
||||
// Get Message-ID
|
||||
let message_id =
|
||||
get_fetch_headers(&msg).map_or(None, |headers| prefetch_get_message_id(&headers));
|
||||
|
||||
if let (Some(uid), Some(rfc724_mid)) = (msg.uid, message_id) {
|
||||
msg_ids.insert(uid, rfc724_mid);
|
||||
}
|
||||
}
|
||||
|
||||
info!(
|
||||
context,
|
||||
"Resync: collected {} message IDs in folder {}",
|
||||
msgs.len(),
|
||||
folder,
|
||||
msg_ids.len(),
|
||||
&folder
|
||||
);
|
||||
|
||||
let uid_validity = get_uidvalidity(context, folder).await?;
|
||||
let uid_validity = get_uidvalidity(context, &folder).await?;
|
||||
|
||||
// Write collected UIDs to SQLite database.
|
||||
context
|
||||
.sql
|
||||
.transaction(move |transaction| {
|
||||
transaction.execute("DELETE FROM imap WHERE folder=?", params![folder])?;
|
||||
for (uid, (rfc724_mid, target)) in &msgs {
|
||||
for (uid, rfc724_mid) in &msg_ids {
|
||||
// This may detect previously undetected moved
|
||||
// messages, so we update server_folder too.
|
||||
transaction.execute(
|
||||
@@ -561,7 +541,7 @@ impl Imap {
|
||||
ON CONFLICT(folder, uid, uidvalidity)
|
||||
DO UPDATE SET rfc724_mid=excluded.rfc724_mid,
|
||||
target=excluded.target",
|
||||
params![rfc724_mid, folder, uid, uid_validity, target],
|
||||
params![rfc724_mid, folder, uid, uid_validity, folder],
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -703,10 +683,10 @@ impl Imap {
|
||||
&mut self,
|
||||
context: &Context,
|
||||
folder: &str,
|
||||
folder_meaning: FolderMeaning,
|
||||
is_spam_folder: bool,
|
||||
fetch_existing_msgs: bool,
|
||||
) -> Result<bool> {
|
||||
if should_ignore_folder(context, folder, folder_meaning).await? {
|
||||
if should_ignore_folder(context, folder, is_spam_folder).await? {
|
||||
info!(context, "Not fetching from {}", folder);
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -733,6 +713,8 @@ impl Imap {
|
||||
};
|
||||
let read_cnt = msgs.len();
|
||||
|
||||
let show_emails = ShowEmails::from_i32(context.get_config_int(Config::ShowEmails).await?)
|
||||
.unwrap_or_default();
|
||||
let download_limit = context.download_limit().await?;
|
||||
let mut uids_fetch = Vec::<(_, bool /* partially? */)>::with_capacity(msgs.len() + 1);
|
||||
let mut uid_message_ids = BTreeMap::new();
|
||||
@@ -750,7 +732,14 @@ impl Imap {
|
||||
|
||||
// Get the Message-ID or generate a fake one to identify the message in the database.
|
||||
let message_id = prefetch_get_or_create_message_id(&headers);
|
||||
let target = target_folder(context, folder, folder_meaning, &headers).await?;
|
||||
|
||||
let target = match target_folder(context, folder, is_spam_folder, &headers).await? {
|
||||
Some(config) => match context.get_config(config).await? {
|
||||
Some(target) => target,
|
||||
None => folder.to_string(),
|
||||
},
|
||||
None => folder.to_string(),
|
||||
};
|
||||
|
||||
context
|
||||
.sql
|
||||
@@ -774,13 +763,14 @@ impl Imap {
|
||||
// Never download messages directly from the spam folder.
|
||||
// If the sender is known, the message will be moved to the Inbox or Mvbox
|
||||
// and then we download the message from there.
|
||||
// Also see `spam_target_folder_cfg()`.
|
||||
&& folder_meaning != FolderMeaning::Spam
|
||||
// Also see `spam_target_folder()`.
|
||||
&& !is_spam_folder
|
||||
&& prefetch_should_download(
|
||||
context,
|
||||
&headers,
|
||||
&message_id,
|
||||
fetch_response.flags(),
|
||||
show_emails,
|
||||
)
|
||||
.await.context("prefetch_should_download")?
|
||||
{
|
||||
@@ -880,21 +870,17 @@ impl Imap {
|
||||
.context("failed to get recipients from the inbox")?;
|
||||
|
||||
if context.get_config_bool(Config::FetchExistingMsgs).await? {
|
||||
for meaning in [
|
||||
FolderMeaning::Mvbox,
|
||||
FolderMeaning::Inbox,
|
||||
FolderMeaning::Sent,
|
||||
for config in &[
|
||||
Config::ConfiguredMvboxFolder,
|
||||
Config::ConfiguredInboxFolder,
|
||||
Config::ConfiguredSentboxFolder,
|
||||
] {
|
||||
let config = match meaning.to_config() {
|
||||
Some(c) => c,
|
||||
None => continue,
|
||||
};
|
||||
if let Some(folder) = context.get_config(config).await? {
|
||||
if let Some(folder) = context.get_config(*config).await? {
|
||||
info!(
|
||||
context,
|
||||
"Fetching existing messages from folder \"{}\"", folder
|
||||
);
|
||||
self.fetch_new_messages(context, &folder, meaning, true)
|
||||
self.fetch_new_messages(context, &folder, false, true)
|
||||
.await
|
||||
.context("could not fetch existing messages")?;
|
||||
}
|
||||
@@ -904,24 +890,6 @@ impl Imap {
|
||||
info!(context, "Done fetching existing messages.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Synchronizes UIDs for all folders.
|
||||
pub(crate) async fn resync_folders(&mut self, context: &Context) -> Result<()> {
|
||||
self.prepare(context).await?;
|
||||
|
||||
let all_folders = self
|
||||
.list_folders(context)
|
||||
.await
|
||||
.context("listing folders for resync")?;
|
||||
for folder in all_folders {
|
||||
let folder_meaning = get_folder_meaning(&folder);
|
||||
if folder_meaning != FolderMeaning::Virtual {
|
||||
self.resync_folder_uids(context, folder.name(), folder_meaning)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Session {
|
||||
@@ -984,60 +952,44 @@ impl Session {
|
||||
return Ok(());
|
||||
}
|
||||
Err(err) => {
|
||||
if context.should_delete_to_trash().await? {
|
||||
error!(
|
||||
context,
|
||||
"Cannot move messages {} to {}, no fallback to COPY/DELETE because \
|
||||
delete_to_trash is set. Error: {:#}",
|
||||
set,
|
||||
target,
|
||||
err,
|
||||
);
|
||||
return Err(err.into());
|
||||
}
|
||||
warn!(
|
||||
context,
|
||||
"Cannot move messages, fallback to COPY/DELETE {} to {}: {}",
|
||||
"Cannot move message, fallback to COPY/DELETE {} to {}: {}",
|
||||
set,
|
||||
target,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Server does not support MOVE or MOVE failed.
|
||||
// Copy messages to the destination folder if needed and mark records for deletion.
|
||||
let copy = !context.is_trash(target).await?;
|
||||
if copy {
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"Server does not support MOVE, fallback to COPY/DELETE {} to {}", set, target
|
||||
);
|
||||
self.uid_copy(&set, &target).await?;
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
"Server does not support MOVE, fallback to DELETE {} to {}", set, target,
|
||||
);
|
||||
}
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
&format!(
|
||||
"UPDATE imap SET target='' WHERE id IN ({})",
|
||||
sql::repeat_vars(row_ids.len())
|
||||
),
|
||||
rusqlite::params_from_iter(row_ids),
|
||||
)
|
||||
.await
|
||||
.context("cannot plan deletion of messages")?;
|
||||
if copy {
|
||||
context.emit_event(EventType::ImapMessageMoved(format!(
|
||||
"IMAP messages {set} copied to {target}"
|
||||
)));
|
||||
|
||||
// Server does not support MOVE or MOVE failed.
|
||||
// Copy the message to the destination folder and mark the record for deletion.
|
||||
match self.uid_copy(&set, &target).await {
|
||||
Ok(()) => {
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
&format!(
|
||||
"UPDATE imap SET target='' WHERE id IN ({})",
|
||||
sql::repeat_vars(row_ids.len())
|
||||
),
|
||||
rusqlite::params_from_iter(row_ids),
|
||||
)
|
||||
.await
|
||||
.context("cannot plan deletion of copied messages")?;
|
||||
context.emit_event(EventType::ImapMessageMoved(format!(
|
||||
"IMAP messages {set} copied to {target}"
|
||||
)));
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Moves and deletes messages as planned in the `imap` table.
|
||||
@@ -1394,7 +1346,7 @@ impl Imap {
|
||||
// Check if FETCH response is already in `uid_msgs`.
|
||||
let mut fetch_response = uid_msgs.remove(&request_uid);
|
||||
|
||||
// Try to find a requested UID in returned FETCH responses.
|
||||
// Try to find a requsted UID in returned FETCH responses.
|
||||
while fetch_response.is_none() {
|
||||
let next_fetch_response =
|
||||
if let Some(next_fetch_response) = fetch_responses.next().await {
|
||||
@@ -1692,7 +1644,7 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
let folder_meaning = get_folder_meaning_by_attrs(folder.attributes());
|
||||
let folder_meaning = get_folder_meaning(&folder);
|
||||
let folder_name_meaning = get_folder_meaning_by_name(folder.name());
|
||||
if let Some(config) = folder_meaning.to_config() {
|
||||
// Always takes precedence
|
||||
@@ -1824,7 +1776,7 @@ async fn should_move_out_of_spam(
|
||||
/// If this returns None, the message will not be moved out of the
|
||||
/// Spam folder, and as `fetch_new_messages()` doesn't download
|
||||
/// messages from the Spam folder, the message will be ignored.
|
||||
async fn spam_target_folder_cfg(
|
||||
async fn spam_target_folder(
|
||||
context: &Context,
|
||||
headers: &[mailparse::MailHeader<'_>],
|
||||
) -> Result<Option<Config>> {
|
||||
@@ -1845,18 +1797,18 @@ async fn spam_target_folder_cfg(
|
||||
|
||||
/// Returns `ConfiguredInboxFolder`, `ConfiguredMvboxFolder` or `ConfiguredSentboxFolder` if
|
||||
/// the message needs to be moved from `folder`. Otherwise returns `None`.
|
||||
pub async fn target_folder_cfg(
|
||||
pub async fn target_folder(
|
||||
context: &Context,
|
||||
folder: &str,
|
||||
folder_meaning: FolderMeaning,
|
||||
is_spam_folder: bool,
|
||||
headers: &[mailparse::MailHeader<'_>],
|
||||
) -> Result<Option<Config>> {
|
||||
if context.is_mvbox(folder).await? {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if folder_meaning == FolderMeaning::Spam {
|
||||
spam_target_folder_cfg(context, headers).await
|
||||
if is_spam_folder {
|
||||
spam_target_folder(context, headers).await
|
||||
} else if needs_move_to_mvbox(context, headers).await? {
|
||||
Ok(Some(Config::ConfiguredMvboxFolder))
|
||||
} else {
|
||||
@@ -1864,21 +1816,6 @@ pub async fn target_folder_cfg(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn target_folder(
|
||||
context: &Context,
|
||||
folder: &str,
|
||||
folder_meaning: FolderMeaning,
|
||||
headers: &[mailparse::MailHeader<'_>],
|
||||
) -> Result<String> {
|
||||
match target_folder_cfg(context, folder, folder_meaning, headers).await? {
|
||||
Some(config) => match context.get_config(config).await? {
|
||||
Some(target) => Ok(target),
|
||||
None => Ok(folder.to_string()),
|
||||
},
|
||||
None => Ok(folder.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn needs_move_to_mvbox(
|
||||
context: &Context,
|
||||
headers: &[mailparse::MailHeader<'_>],
|
||||
@@ -2003,10 +1940,10 @@ fn get_folder_meaning_by_name(folder_name: &str) -> FolderMeaning {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_folder_meaning_by_attrs(folder_attrs: &[NameAttribute]) -> FolderMeaning {
|
||||
for attr in folder_attrs {
|
||||
fn get_folder_meaning(folder_name: &Name) -> FolderMeaning {
|
||||
for attr in folder_name.attributes() {
|
||||
match attr {
|
||||
NameAttribute::Trash => return FolderMeaning::Trash,
|
||||
NameAttribute::Trash => return FolderMeaning::Other,
|
||||
NameAttribute::Sent => return FolderMeaning::Sent,
|
||||
NameAttribute::Junk => return FolderMeaning::Spam,
|
||||
NameAttribute::Drafts => return FolderMeaning::Drafts,
|
||||
@@ -2024,13 +1961,6 @@ fn get_folder_meaning_by_attrs(folder_attrs: &[NameAttribute]) -> FolderMeaning
|
||||
FolderMeaning::Unknown
|
||||
}
|
||||
|
||||
pub(crate) fn get_folder_meaning(folder: &Name) -> FolderMeaning {
|
||||
match get_folder_meaning_by_attrs(folder.attributes()) {
|
||||
FolderMeaning::Unknown => get_folder_meaning_by_name(folder.name()),
|
||||
meaning => meaning,
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the headers from the FETCH result.
|
||||
fn get_fetch_headers(prefetch_msg: &Fetch) -> Result<Vec<mailparse::MailHeader>> {
|
||||
match prefetch_msg.header() {
|
||||
@@ -2075,6 +2005,7 @@ pub(crate) async fn prefetch_should_download(
|
||||
headers: &[mailparse::MailHeader<'_>],
|
||||
message_id: &str,
|
||||
mut flags: impl Iterator<Item = Flag<'_>>,
|
||||
show_emails: ShowEmails,
|
||||
) -> Result<bool> {
|
||||
if message::rfc724_mid_exists(context, message_id)
|
||||
.await?
|
||||
@@ -2134,9 +2065,6 @@ pub(crate) async fn prefetch_should_download(
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let show_emails =
|
||||
ShowEmails::from_i32(context.get_config_int(Config::ShowEmails).await?).unwrap_or_default();
|
||||
|
||||
let show = is_autocrypt_setup_message
|
||||
|| match show_emails {
|
||||
ShowEmails::Off => is_chat_message || is_reply_to_chat_message,
|
||||
@@ -2204,11 +2132,11 @@ async fn mark_seen_by_uid(
|
||||
.with_context(|| format!("failed to start ephemeral timer for message {msg_id}"))?;
|
||||
Ok(Some(chat_id))
|
||||
} else {
|
||||
// Message state has not changed.
|
||||
// Message state has not chnaged.
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
// There is no message is `msgs` table matching the given UID.
|
||||
// There is no message is `msgs` table matchng the given UID.
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
@@ -2344,7 +2272,7 @@ pub async fn get_config_last_seen_uid(context: &Context, folder: &str) -> Result
|
||||
async fn should_ignore_folder(
|
||||
context: &Context,
|
||||
folder: &str,
|
||||
folder_meaning: FolderMeaning,
|
||||
is_spam_folder: bool,
|
||||
) -> Result<bool> {
|
||||
if !context.get_config_bool(Config::OnlyFetchMvbox).await? {
|
||||
return Ok(false);
|
||||
@@ -2353,7 +2281,7 @@ async fn should_ignore_folder(
|
||||
// Still respect the SentboxWatch setting.
|
||||
return Ok(!context.get_config_bool(Config::SentboxWatch).await?);
|
||||
}
|
||||
Ok(!(context.is_mvbox(folder).await? || folder_meaning == FolderMeaning::Spam))
|
||||
Ok(!(context.is_mvbox(folder).await? || is_spam_folder))
|
||||
}
|
||||
|
||||
/// Builds a list of sequence/uid sets. The returned sets have each no more than around 1000
|
||||
@@ -2636,13 +2564,14 @@ mod tests {
|
||||
};
|
||||
|
||||
let (headers, _) = mailparse::parse_headers(bytes)?;
|
||||
let actual = if let Some(config) =
|
||||
target_folder_cfg(&t, folder, get_folder_meaning_by_name(folder), &headers).await?
|
||||
{
|
||||
t.get_config(config).await?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let is_spam_folder = folder == "Spam";
|
||||
let actual =
|
||||
if let Some(config) = target_folder(&t, folder, is_spam_folder, &headers).await? {
|
||||
t.get_config(config).await?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let expected = if expected_destination == folder {
|
||||
None
|
||||
|
||||
+11
-5
@@ -11,9 +11,9 @@ use tokio::io::BufWriter;
|
||||
use super::capabilities::Capabilities;
|
||||
use super::session::Session;
|
||||
use crate::context::Context;
|
||||
use crate::login_param::build_tls;
|
||||
use crate::net::connect_tcp;
|
||||
use crate::net::session::SessionStream;
|
||||
use crate::net::tls::wrap_tls;
|
||||
use crate::socks::Socks5Config;
|
||||
|
||||
/// IMAP write and read timeout.
|
||||
@@ -95,7 +95,8 @@ impl Client {
|
||||
strict_tls: bool,
|
||||
) -> Result<Self> {
|
||||
let tcp_stream = connect_tcp(context, hostname, port, IMAP_TIMEOUT, strict_tls).await?;
|
||||
let tls_stream = wrap_tls(strict_tls, hostname, tcp_stream).await?;
|
||||
let tls = build_tls(strict_tls);
|
||||
let tls_stream = tls.connect(hostname, tcp_stream).await?;
|
||||
let buffered_stream = BufWriter::new(tls_stream);
|
||||
let session_stream: Box<dyn SessionStream> = Box::new(buffered_stream);
|
||||
let mut client = ImapClient::new(session_stream);
|
||||
@@ -141,7 +142,9 @@ impl Client {
|
||||
.context("STARTTLS command failed")?;
|
||||
let tcp_stream = client.into_inner();
|
||||
|
||||
let tls_stream = wrap_tls(strict_tls, hostname, tcp_stream)
|
||||
let tls = build_tls(strict_tls);
|
||||
let tls_stream = tls
|
||||
.connect(hostname, tcp_stream)
|
||||
.await
|
||||
.context("STARTTLS upgrade failed")?;
|
||||
|
||||
@@ -162,7 +165,8 @@ impl Client {
|
||||
let socks5_stream = socks5_config
|
||||
.connect(context, domain, port, IMAP_TIMEOUT, strict_tls)
|
||||
.await?;
|
||||
let tls_stream = wrap_tls(strict_tls, domain, socks5_stream).await?;
|
||||
let tls = build_tls(strict_tls);
|
||||
let tls_stream = tls.connect(domain, socks5_stream).await?;
|
||||
let buffered_stream = BufWriter::new(tls_stream);
|
||||
let session_stream: Box<dyn SessionStream> = Box::new(buffered_stream);
|
||||
let mut client = ImapClient::new(session_stream);
|
||||
@@ -217,7 +221,9 @@ impl Client {
|
||||
.context("STARTTLS command failed")?;
|
||||
let socks5_stream = client.into_inner();
|
||||
|
||||
let tls_stream = wrap_tls(strict_tls, hostname, socks5_stream)
|
||||
let tls = build_tls(strict_tls);
|
||||
let tls_stream = tls
|
||||
.connect(hostname, socks5_stream)
|
||||
.await
|
||||
.context("STARTTLS upgrade failed")?;
|
||||
let buffered_stream = BufWriter::new(tls_stream);
|
||||
|
||||
+2
-3
@@ -7,7 +7,7 @@ use futures_lite::FutureExt;
|
||||
|
||||
use super::session::Session;
|
||||
use super::Imap;
|
||||
use crate::imap::{client::IMAP_TIMEOUT, FolderMeaning};
|
||||
use crate::imap::client::IMAP_TIMEOUT;
|
||||
use crate::{context::Context, scheduler::InterruptInfo};
|
||||
|
||||
const IDLE_TIMEOUT: Duration = Duration::from_secs(23 * 60);
|
||||
@@ -113,7 +113,6 @@ impl Imap {
|
||||
&mut self,
|
||||
context: &Context,
|
||||
watch_folder: Option<String>,
|
||||
folder_meaning: FolderMeaning,
|
||||
) -> InterruptInfo {
|
||||
// Idle using polling. This is also needed if we're not yet configured -
|
||||
// in this case, we're waiting for a configure job (and an interrupt).
|
||||
@@ -174,7 +173,7 @@ impl Imap {
|
||||
// will have already fetched the messages so perform_*_fetch
|
||||
// will not find any new.
|
||||
match self
|
||||
.fetch_new_messages(context, &watch_folder, folder_meaning, false)
|
||||
.fetch_new_messages(context, &watch_folder, false, false)
|
||||
.await
|
||||
{
|
||||
Ok(res) => {
|
||||
|
||||
+19
-20
@@ -3,7 +3,7 @@ use std::{collections::BTreeMap, time::Instant};
|
||||
use anyhow::{Context as _, Result};
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
use super::{get_folder_meaning_by_attrs, get_folder_meaning_by_name};
|
||||
use super::{get_folder_meaning, get_folder_meaning_by_name};
|
||||
use crate::config::Config;
|
||||
use crate::imap::Imap;
|
||||
use crate::log::LogExt;
|
||||
@@ -33,7 +33,7 @@ impl Imap {
|
||||
let mut folder_configs = BTreeMap::new();
|
||||
|
||||
for folder in folders {
|
||||
let folder_meaning = get_folder_meaning_by_attrs(folder.attributes());
|
||||
let folder_meaning = get_folder_meaning(&folder);
|
||||
if folder_meaning == FolderMeaning::Virtual {
|
||||
// Gmail has virtual folders that should be skipped. For example,
|
||||
// emails appear in the inbox and under "All Mail" as soon as it is
|
||||
@@ -53,22 +53,21 @@ impl Imap {
|
||||
.or_insert_with(|| folder.name().to_string());
|
||||
}
|
||||
|
||||
let folder_meaning = match folder_meaning {
|
||||
FolderMeaning::Unknown => folder_name_meaning,
|
||||
_ => folder_meaning,
|
||||
};
|
||||
let is_drafts = folder_meaning == FolderMeaning::Drafts
|
||||
|| (folder_meaning == FolderMeaning::Unknown
|
||||
&& folder_name_meaning == FolderMeaning::Drafts);
|
||||
let is_spam_folder = folder_meaning == FolderMeaning::Spam
|
||||
|| (folder_meaning == FolderMeaning::Unknown
|
||||
&& folder_name_meaning == FolderMeaning::Spam);
|
||||
|
||||
// Don't scan folders that are watched anyway
|
||||
if !watched_folders.contains(&folder.name().to_string())
|
||||
&& folder_meaning != FolderMeaning::Drafts
|
||||
&& folder_meaning != FolderMeaning::Trash
|
||||
{
|
||||
if !watched_folders.contains(&folder.name().to_string()) && !is_drafts {
|
||||
let session = self.session.as_mut().context("no session")?;
|
||||
// Drain leftover unsolicited EXISTS messages
|
||||
session.server_sent_unsolicited_exists(context)?;
|
||||
|
||||
loop {
|
||||
self.fetch_move_delete(context, folder.name(), folder_meaning)
|
||||
self.fetch_move_delete(context, folder.name(), is_spam_folder)
|
||||
.await
|
||||
.ok_or_log_msg(context, "Can't fetch new msgs in scanned folder");
|
||||
|
||||
@@ -81,15 +80,15 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
// Set configs for necessary folders. Or reset if the folder was deleted.
|
||||
for conf in [
|
||||
Config::ConfiguredSentboxFolder,
|
||||
Config::ConfiguredTrashFolder,
|
||||
] {
|
||||
context
|
||||
.set_config(conf, folder_configs.get(&conf).map(|s| s.as_str()))
|
||||
.await?;
|
||||
}
|
||||
// Set the `ConfiguredSentboxFolder` or set it to `None` if the folder was deleted.
|
||||
context
|
||||
.set_config(
|
||||
Config::ConfiguredSentboxFolder,
|
||||
folder_configs
|
||||
.get(&Config::ConfiguredSentboxFolder)
|
||||
.map(|s| s.as_str()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
last_scan.replace(Instant::now());
|
||||
Ok(true)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user