Compare commits
30 Commits
stable-1.1
...
hpk-gh-pyt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e454ede4c | ||
|
|
029f60df27 | ||
|
|
20c82b324a | ||
|
|
e7ebb40cd1 | ||
|
|
bca8094fcb | ||
|
|
7ab5d36b5b | ||
|
|
58ad14d9c3 | ||
|
|
e539bddc3b | ||
|
|
2fc1d21959 | ||
|
|
b43d9d2ffe | ||
|
|
5b73951b9b | ||
|
|
8595b92fcf | ||
|
|
6054b90975 | ||
|
|
b8427ab56e | ||
|
|
02f72eea61 | ||
|
|
a5a20078f0 | ||
|
|
7383094b33 | ||
|
|
e225a6fb17 | ||
|
|
6bdc207277 | ||
|
|
101141c67a | ||
|
|
d07afe5bd6 | ||
|
|
00e22d4339 | ||
|
|
91c8f48c21 | ||
|
|
3d76d21925 | ||
|
|
3349c0e9dc | ||
|
|
7b35104b83 | ||
|
|
22b6e8f6e2 | ||
|
|
ad118aa0df | ||
|
|
9044b80b9f | ||
|
|
b948e973c5 |
@@ -1,11 +0,0 @@
|
|||||||
[env]
|
|
||||||
# In unoptimised builds tokio tends to use a lot of stack space when
|
|
||||||
# creating some complicated futures, tokio has an open issue for this:
|
|
||||||
# https://github.com/tokio-rs/tokio/issues/2055. Some of our tests
|
|
||||||
# manage to not fit in the default 2MiB stack anymore due to this, so
|
|
||||||
# while the issue is not resolved we want to work around this.
|
|
||||||
# Because compiling optimised builds takes a very long time we prefer
|
|
||||||
# to avoid that. Setting this environment variable ensures that when
|
|
||||||
# invoking `cargo test` threads are allowed to have a large enough
|
|
||||||
# stack size without needing to use an optimised build.
|
|
||||||
RUST_MIN_STACK = "8388608"
|
|
||||||
218
.circleci/config.yml
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
version: 2.1
|
||||||
|
executors:
|
||||||
|
default:
|
||||||
|
docker:
|
||||||
|
- image: filecoin/rust:latest
|
||||||
|
working_directory: /mnt/crate
|
||||||
|
doxygen:
|
||||||
|
docker:
|
||||||
|
- image: hrektts/doxygen
|
||||||
|
|
||||||
|
restore-workspace: &restore-workspace
|
||||||
|
attach_workspace:
|
||||||
|
at: /mnt
|
||||||
|
|
||||||
|
restore-cache: &restore-cache
|
||||||
|
restore_cache:
|
||||||
|
keys:
|
||||||
|
- cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||||
|
- repo-source-{{ .Branch }}-{{ .Revision }}
|
||||||
|
|
||||||
|
commands:
|
||||||
|
test_target:
|
||||||
|
parameters:
|
||||||
|
target:
|
||||||
|
type: string
|
||||||
|
steps:
|
||||||
|
- *restore-workspace
|
||||||
|
- *restore-cache
|
||||||
|
- run:
|
||||||
|
name: Test (<< parameters.target >>)
|
||||||
|
command: TARGET=<< parameters.target >> ci_scripts/run-rust-test.sh
|
||||||
|
no_output_timeout: 15m
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cargo_fetch:
|
||||||
|
executor: default
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Update submodules
|
||||||
|
command: git submodule update --init --recursive
|
||||||
|
- run:
|
||||||
|
name: Calculate dependencies
|
||||||
|
command: cargo generate-lockfile
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||||
|
- run: rustup install $(cat rust-toolchain)
|
||||||
|
- run: rustup default $(cat rust-toolchain)
|
||||||
|
- run: rustup component add --toolchain $(cat rust-toolchain) rustfmt
|
||||||
|
- run: rustup component add --toolchain $(cat rust-toolchain) clippy-preview
|
||||||
|
- run: cargo update
|
||||||
|
- run: cargo fetch
|
||||||
|
- run: rustc +stable --version
|
||||||
|
- run: rustc +$(cat rust-toolchain) --version
|
||||||
|
# make sure this git repo doesn't grow too big
|
||||||
|
- run: git gc
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: /mnt
|
||||||
|
paths:
|
||||||
|
- crate
|
||||||
|
- save_cache:
|
||||||
|
key: cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||||
|
paths:
|
||||||
|
- "~/.cargo"
|
||||||
|
- "~/.rustup"
|
||||||
|
|
||||||
|
rustfmt:
|
||||||
|
executor: default
|
||||||
|
steps:
|
||||||
|
- *restore-workspace
|
||||||
|
- *restore-cache
|
||||||
|
- run:
|
||||||
|
name: Run cargo fmt
|
||||||
|
command: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
test_macos:
|
||||||
|
macos:
|
||||||
|
xcode: "10.0.0"
|
||||||
|
working_directory: ~/crate
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: Configure environment variables
|
||||||
|
command: |
|
||||||
|
echo 'export PATH="${HOME}/.cargo/bin:${HOME}/.bin:${PATH}"' >> $BASH_ENV
|
||||||
|
echo 'export CIRCLE_ARTIFACTS="/tmp"' >> $BASH_ENV
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Install Rust
|
||||||
|
command: |
|
||||||
|
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||||
|
- run: rustup install $(cat rust-toolchain)
|
||||||
|
- run: rustup default $(cat rust-toolchain)
|
||||||
|
- run: cargo update
|
||||||
|
- run: cargo fetch
|
||||||
|
- run:
|
||||||
|
name: Test
|
||||||
|
command: TARGET=x86_64-apple-darwin ci_scripts/run-rust-test.sh
|
||||||
|
|
||||||
|
test_x86_64-unknown-linux-gnu:
|
||||||
|
executor: default
|
||||||
|
steps:
|
||||||
|
- test_target:
|
||||||
|
target: "x86_64-unknown-linux-gnu"
|
||||||
|
|
||||||
|
test_i686-unknown-linux-gnu:
|
||||||
|
executor: default
|
||||||
|
steps:
|
||||||
|
- test_target:
|
||||||
|
target: "i686-unknown-linux-gnu"
|
||||||
|
|
||||||
|
test_aarch64-linux-android:
|
||||||
|
executor: default
|
||||||
|
steps:
|
||||||
|
- test_target:
|
||||||
|
target: "aarch64-linux-android"
|
||||||
|
|
||||||
|
|
||||||
|
build_doxygen:
|
||||||
|
executor: doxygen
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: bash ci_scripts/run-doxygen.sh
|
||||||
|
- run: mkdir -p workspace/c-docs
|
||||||
|
- run: cp -av deltachat-ffi/{html,xml} workspace/c-docs/
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: workspace
|
||||||
|
paths:
|
||||||
|
- c-docs
|
||||||
|
|
||||||
|
build_test_docs_wheel:
|
||||||
|
docker:
|
||||||
|
- image: deltachat/coredeps
|
||||||
|
environment:
|
||||||
|
TESTS: 1
|
||||||
|
DOCS: 1
|
||||||
|
working_directory: /mnt/crate
|
||||||
|
steps:
|
||||||
|
- *restore-workspace
|
||||||
|
- *restore-cache
|
||||||
|
- run:
|
||||||
|
name: build docs, run tests and build wheels
|
||||||
|
command: ci_scripts/run-python.sh
|
||||||
|
- run:
|
||||||
|
name: copying docs and wheels to workspace
|
||||||
|
command: |
|
||||||
|
mkdir -p workspace/python
|
||||||
|
# cp -av docs workspace/c-docs
|
||||||
|
cp -av python/.docker-tox/wheelhouse workspace/
|
||||||
|
cp -av python/doc/_build/ workspace/py-docs
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: workspace
|
||||||
|
paths:
|
||||||
|
# - c-docs
|
||||||
|
- py-docs
|
||||||
|
- wheelhouse
|
||||||
|
|
||||||
|
upload_docs_wheels:
|
||||||
|
machine: true
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: workspace
|
||||||
|
- run: pyenv global 3.5.2
|
||||||
|
- run: ls -laR workspace
|
||||||
|
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse workspace/c-docs
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
executor: default
|
||||||
|
steps:
|
||||||
|
- *restore-workspace
|
||||||
|
- *restore-cache
|
||||||
|
- run:
|
||||||
|
name: Run cargo clippy
|
||||||
|
command: cargo clippy --all
|
||||||
|
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
version: 2.1
|
||||||
|
|
||||||
|
test:
|
||||||
|
jobs:
|
||||||
|
- cargo_fetch
|
||||||
|
- build_doxygen
|
||||||
|
|
||||||
|
- build_test_docs_wheel:
|
||||||
|
requires:
|
||||||
|
- cargo_fetch
|
||||||
|
- upload_docs_wheels:
|
||||||
|
requires:
|
||||||
|
- build_test_docs_wheel
|
||||||
|
- build_doxygen
|
||||||
|
- rustfmt:
|
||||||
|
requires:
|
||||||
|
- cargo_fetch
|
||||||
|
- clippy:
|
||||||
|
requires:
|
||||||
|
- cargo_fetch
|
||||||
|
|
||||||
|
# Linux Desktop 64bit
|
||||||
|
- test_x86_64-unknown-linux-gnu:
|
||||||
|
requires:
|
||||||
|
- cargo_fetch
|
||||||
|
|
||||||
|
# Linux Desktop 32bit
|
||||||
|
# - test_i686-unknown-linux-gnu:
|
||||||
|
# requires:
|
||||||
|
# - cargo_fetch
|
||||||
|
|
||||||
|
# Android 64bit
|
||||||
|
# - test_aarch64-linux-android:
|
||||||
|
# requires:
|
||||||
|
# - cargo_fetch
|
||||||
|
|
||||||
|
# Desktop Apple
|
||||||
|
# - test_macos:
|
||||||
|
# requires:
|
||||||
|
# - cargo_fetch
|
||||||
5
.gitattributes
vendored
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
# This directory contains email messages verbatim, and changing CRLF to
|
# This directory contains email messages verbatim, and changing CRLF to
|
||||||
# LF will corrupt them.
|
# LF will corrupt them.
|
||||||
test-data/** text=false
|
test-data/* text=false
|
||||||
|
|
||||||
# binary files should be detected by git, however, to be sure, you can add them here explicitly
|
# binary files should be detected by git, however, to be sure, you can add them here explicitly
|
||||||
*.png binary
|
*.png binary
|
||||||
@@ -12,6 +12,3 @@ test-data/** text=false
|
|||||||
*.gif binary
|
*.gif binary
|
||||||
*.ico binary
|
*.ico binary
|
||||||
|
|
||||||
*.py diff=python
|
|
||||||
*.rs diff=rust
|
|
||||||
*.md diff=markdown
|
|
||||||
|
|||||||
9
.github/dependabot.yml
vendored
@@ -1,9 +0,0 @@
|
|||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "cargo"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "monthly"
|
|
||||||
commit-message:
|
|
||||||
prefix: "cargo"
|
|
||||||
open-pull-requests-limit: 50
|
|
||||||
26
.github/mergeable.yml
vendored
@@ -1,26 +0,0 @@
|
|||||||
version: 2
|
|
||||||
mergeable:
|
|
||||||
- when: pull_request.*
|
|
||||||
name: "Changelog check"
|
|
||||||
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']
|
|
||||||
fail:
|
|
||||||
- do: checks
|
|
||||||
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."
|
|
||||||
162
.github/workflows/ci.yml
vendored
@@ -1,162 +0,0 @@
|
|||||||
name: Rust CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
- trying
|
|
||||||
|
|
||||||
env:
|
|
||||||
RUSTFLAGS: -Dwarnings
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
name: Rustfmt
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
|
||||||
override: true
|
|
||||||
- run: rustup component add rustfmt
|
|
||||||
- name: Cache rust cargo artifacts
|
|
||||||
uses: swatinem/rust-cache@v2
|
|
||||||
- run: cargo fmt --all -- --check
|
|
||||||
|
|
||||||
run_clippy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
components: clippy
|
|
||||||
override: true
|
|
||||||
- name: Cache rust cargo artifacts
|
|
||||||
uses: swatinem/rust-cache@v2
|
|
||||||
- uses: actions-rs/clippy-check@v1
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
args: --workspace --tests --examples --benches --features repl -- -D warnings
|
|
||||||
|
|
||||||
docs:
|
|
||||||
name: Rust doc comments
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
RUSTDOCFLAGS: -Dwarnings
|
|
||||||
steps:
|
|
||||||
- name: Checkout sources
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Install rust stable toolchain
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
profile: minimal
|
|
||||||
components: rust-docs
|
|
||||||
override: true
|
|
||||||
- name: Cache rust cargo artifacts
|
|
||||||
uses: swatinem/rust-cache@v2
|
|
||||||
- name: Rustdoc
|
|
||||||
run: cargo doc --document-private-items --no-deps
|
|
||||||
|
|
||||||
build_and_test:
|
|
||||||
name: Build and test
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# Currently used Rust version.
|
|
||||||
- os: ubuntu-latest
|
|
||||||
rust: 1.64.0
|
|
||||||
python: 3.9
|
|
||||||
- os: windows-latest
|
|
||||||
rust: 1.64.0
|
|
||||||
python: false # Python bindings compilation on Windows is not supported.
|
|
||||||
|
|
||||||
# Minimum Supported Rust Version = 1.63.0
|
|
||||||
#
|
|
||||||
# Minimum Supported Python Version = 3.7
|
|
||||||
# This is the minimum version for which manylinux Python wheels are
|
|
||||||
# built.
|
|
||||||
- os: ubuntu-latest
|
|
||||||
rust: 1.63.0
|
|
||||||
python: 3.7
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@master
|
|
||||||
|
|
||||||
- name: Install ${{ matrix.rust }}
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: ${{ matrix.rust }}
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Cache rust cargo artifacts
|
|
||||||
uses: swatinem/rust-cache@v2
|
|
||||||
|
|
||||||
- name: check
|
|
||||||
run: cargo check --all --bins --examples --tests --features repl --benches
|
|
||||||
|
|
||||||
- name: tests
|
|
||||||
run: cargo test --all
|
|
||||||
|
|
||||||
- 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 tox
|
|
||||||
if: ${{ matrix.python }}
|
|
||||||
run: pip install tox
|
|
||||||
|
|
||||||
- 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: 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: 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: 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
|
|
||||||
21
.github/workflows/dependabot.yml
vendored
@@ -1,21 +0,0 @@
|
|||||||
name: Dependabot auto-approve
|
|
||||||
on: pull_request
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
dependabot:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
|
||||||
steps:
|
|
||||||
- name: Dependabot metadata
|
|
||||||
id: metadata
|
|
||||||
uses: dependabot/fetch-metadata@v1.1.1
|
|
||||||
with:
|
|
||||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
- name: Approve a PR
|
|
||||||
run: gh pr review --approve "$PR_URL"
|
|
||||||
env:
|
|
||||||
PR_URL: ${{github.event.pull_request.html_url}}
|
|
||||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
|
||||||
83
.github/workflows/jsonrpc-client-npm-package.yml
vendored
@@ -1,83 +0,0 @@
|
|||||||
name: 'jsonrpc js client build'
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '*'
|
|
||||||
- '!py-*'
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
pack-module:
|
|
||||||
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
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: '16'
|
|
||||||
- name: get tag
|
|
||||||
id: tag
|
|
||||||
uses: dawidd6/action-get-tag@v1
|
|
||||||
continue-on-error: true
|
|
||||||
- name: Get Pullrequest ID
|
|
||||||
id: prepare
|
|
||||||
run: |
|
|
||||||
tag=${{ steps.tag.outputs.tag }}
|
|
||||||
if [ -z "$tag" ]; then
|
|
||||||
node -e "console.log('DELTACHAT_JSONRPC_TAR_GZ=deltachat-jsonrpc-client-' + '${{ github.ref }}'.split('/')[2] + '.tar.gz')" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "DELTACHAT_JSONRPC_TAR_GZ=deltachat-jsonrpc-client-${{ steps.tag.outputs.tag }}.tar.gz" >> $GITHUB_ENV
|
|
||||||
echo "No preview will be uploaded this time, but the $tag release"
|
|
||||||
fi
|
|
||||||
- name: System info
|
|
||||||
run: |
|
|
||||||
npm --version
|
|
||||||
node --version
|
|
||||||
echo $DELTACHAT_JSONRPC_TAR_GZ
|
|
||||||
- name: install dependencies without running scripts
|
|
||||||
run: |
|
|
||||||
cd deltachat-jsonrpc/typescript
|
|
||||||
npm install --ignore-scripts
|
|
||||||
- name: package
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cd deltachat-jsonrpc/typescript
|
|
||||||
npm run build
|
|
||||||
npm pack .
|
|
||||||
ls -lah
|
|
||||||
mv $(find deltachat-jsonrpc-client-*) $DELTACHAT_JSONRPC_TAR_GZ
|
|
||||||
- name: Upload Prebuild
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: deltachat-jsonrpc-client.tgz
|
|
||||||
path: deltachat-jsonrpc/typescript/${{ env.DELTACHAT_JSONRPC_TAR_GZ }}
|
|
||||||
# Upload to download.delta.chat/node/preview/
|
|
||||||
- name: Upload deltachat-jsonrpc-client preview to download.delta.chat/node/preview/
|
|
||||||
if: ${{ ! steps.tag.outputs.tag }}
|
|
||||||
id: upload-preview
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo -e "${{ secrets.SSH_KEY }}" >__TEMP_INPUT_KEY_FILE
|
|
||||||
chmod 600 __TEMP_INPUT_KEY_FILE
|
|
||||||
scp -o StrictHostKeyChecking=no -v -i __TEMP_INPUT_KEY_FILE -P "22" -r deltachat-jsonrpc/typescript/$DELTACHAT_JSONRPC_TAR_GZ "${{ secrets.USERNAME }}"@"download.delta.chat":"/var/www/html/download/node/preview/"
|
|
||||||
continue-on-error: true
|
|
||||||
- name: "Post links to details"
|
|
||||||
if: steps.upload-preview.outcome == 'success'
|
|
||||||
run: node ./node/scripts/postLinksToDetails.js
|
|
||||||
env:
|
|
||||||
URL: preview/${{ env.DELTACHAT_JSONRPC_TAR_GZ }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
MSG_CONTEXT: Download the deltachat-jsonrpc-client.tgz
|
|
||||||
# Upload to download.delta.chat/node/
|
|
||||||
- name: Upload deltachat-jsonrpc-client build to download.delta.chat/node/
|
|
||||||
if: ${{ steps.tag.outputs.tag }}
|
|
||||||
id: upload
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo -e "${{ secrets.SSH_KEY }}" >__TEMP_INPUT_KEY_FILE
|
|
||||||
chmod 600 __TEMP_INPUT_KEY_FILE
|
|
||||||
scp -o StrictHostKeyChecking=no -v -i __TEMP_INPUT_KEY_FILE -P "22" -r deltachat-jsonrpc/typescript/$DELTACHAT_JSONRPC_TAR_GZ "${{ secrets.USERNAME }}"@"download.delta.chat":"/var/www/html/download/node/"
|
|
||||||
41
.github/workflows/jsonrpc.yml
vendored
@@ -1,41 +0,0 @@
|
|||||||
name: JSON-RPC API Test
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
pull_request:
|
|
||||||
branches: [master]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
RUST_MIN_STACK: "8388608"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_and_test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Use Node.js 16.x
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 16.x
|
|
||||||
- name: Add Rust cache
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
- name: npm install
|
|
||||||
run: |
|
|
||||||
cd deltachat-jsonrpc/typescript
|
|
||||||
npm install
|
|
||||||
- name: Build TypeScript, run Rust tests, generate bindings
|
|
||||||
run: |
|
|
||||||
cd deltachat-jsonrpc/typescript
|
|
||||||
npm run build
|
|
||||||
- name: Run integration tests
|
|
||||||
run: |
|
|
||||||
cd deltachat-jsonrpc/typescript
|
|
||||||
npm run test
|
|
||||||
env:
|
|
||||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
|
||||||
- name: Run linter
|
|
||||||
run: |
|
|
||||||
cd deltachat-jsonrpc/typescript
|
|
||||||
npm run prettier:check
|
|
||||||
32
.github/workflows/node-delete-preview.yml
vendored
@@ -1,32 +0,0 @@
|
|||||||
# documentation: https://github.com/deltachat/sysadmin/tree/master/download.delta.chat
|
|
||||||
name: Delete node PR previews
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [closed]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
delete:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- 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/"
|
|
||||||
34
.github/workflows/node-docs.yml
vendored
@@ -1,34 +0,0 @@
|
|||||||
name: Generate & upload node.js documentation
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
generate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- 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: 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/"
|
|
||||||
165
.github/workflows/node-package.yml
vendored
@@ -1,165 +0,0 @@
|
|||||||
name: 'node.js build'
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '*'
|
|
||||||
- '!py-*'
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
prebuild:
|
|
||||||
name: 'prebuild'
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-18.04, macos-latest, windows-latest]
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: '16'
|
|
||||||
- name: System info
|
|
||||||
run: |
|
|
||||||
rustc -vV
|
|
||||||
rustup -vV
|
|
||||||
cargo -vV
|
|
||||||
npm --version
|
|
||||||
node --version
|
|
||||||
|
|
||||||
- name: Cache node modules
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
${{ env.APPDATA }}/npm-cache
|
|
||||||
~/.npm
|
|
||||||
key: ${{ matrix.os }}-node-${{ hashFiles('**/package.json') }}
|
|
||||||
|
|
||||||
- name: Cache cargo index
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry/
|
|
||||||
~/.cargo/git
|
|
||||||
target
|
|
||||||
key: ${{ matrix.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}-2
|
|
||||||
|
|
||||||
- name: Install dependencies & build
|
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
run: |
|
|
||||||
cd node
|
|
||||||
npm install --verbose
|
|
||||||
|
|
||||||
- name: Build Prebuild
|
|
||||||
run: |
|
|
||||||
cd node
|
|
||||||
npm run prebuildify
|
|
||||||
tar -zcvf "${{ matrix.os }}.tar.gz" -C prebuilds .
|
|
||||||
|
|
||||||
- name: Upload Prebuild
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: ${{ matrix.os }}
|
|
||||||
path: node/${{ matrix.os }}.tar.gz
|
|
||||||
|
|
||||||
pack-module:
|
|
||||||
needs: prebuild
|
|
||||||
name: 'Package deltachat-node and upload to download.delta.chat'
|
|
||||||
runs-on: ubuntu-18.04
|
|
||||||
steps:
|
|
||||||
- name: install tree
|
|
||||||
run: sudo apt install tree
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: '16'
|
|
||||||
- name: get tag
|
|
||||||
id: tag
|
|
||||||
uses: dawidd6/action-get-tag@v1
|
|
||||||
continue-on-error: true
|
|
||||||
- name: Get Pullrequest ID
|
|
||||||
id: prepare
|
|
||||||
run: |
|
|
||||||
tag=${{ steps.tag.outputs.tag }}
|
|
||||||
if [ -z "$tag" ]; then
|
|
||||||
node -e "console.log('DELTACHAT_NODE_TAR_GZ=deltachat-node-' + '${{ github.ref }}'.split('/')[2] + '.tar.gz')" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "DELTACHAT_NODE_TAR_GZ=deltachat-node-${{ steps.tag.outputs.tag }}.tar.gz" >> $GITHUB_ENV
|
|
||||||
echo "No preview will be uploaded this time, but the $tag release"
|
|
||||||
fi
|
|
||||||
- name: System info
|
|
||||||
run: |
|
|
||||||
rustc -vV
|
|
||||||
rustup -vV
|
|
||||||
cargo -vV
|
|
||||||
npm --version
|
|
||||||
node --version
|
|
||||||
echo $DELTACHAT_NODE_TAR_GZ
|
|
||||||
- name: Download ubuntu prebuild
|
|
||||||
uses: actions/download-artifact@v1
|
|
||||||
with:
|
|
||||||
name: ubuntu-18.04
|
|
||||||
- name: Download macos prebuild
|
|
||||||
uses: actions/download-artifact@v1
|
|
||||||
with:
|
|
||||||
name: macos-latest
|
|
||||||
- name: Download windows prebuild
|
|
||||||
uses: actions/download-artifact@v1
|
|
||||||
with:
|
|
||||||
name: windows-latest
|
|
||||||
- shell: bash
|
|
||||||
run: |
|
|
||||||
mkdir 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-18.04 macos-latest windows-latest
|
|
||||||
- name: install dependencies without running scripts
|
|
||||||
run: |
|
|
||||||
npm install --ignore-scripts
|
|
||||||
- name: build constants
|
|
||||||
run: |
|
|
||||||
npm run build:core:constants
|
|
||||||
- name: build typescript part
|
|
||||||
run: |
|
|
||||||
npm run build:bindings:ts
|
|
||||||
- name: package
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
mv node/README.md README.md
|
|
||||||
npm pack .
|
|
||||||
ls -lah
|
|
||||||
mv $(find deltachat-node-*) $DELTACHAT_NODE_TAR_GZ
|
|
||||||
- name: Upload Prebuild
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: deltachat-node.tgz
|
|
||||||
path: ${{ env.DELTACHAT_NODE_TAR_GZ }}
|
|
||||||
# Upload to download.delta.chat/node/preview/
|
|
||||||
- name: Upload deltachat-node preview to download.delta.chat/node/preview/
|
|
||||||
if: ${{ ! steps.tag.outputs.tag }}
|
|
||||||
id: upload-preview
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo -e "${{ secrets.SSH_KEY }}" >__TEMP_INPUT_KEY_FILE
|
|
||||||
chmod 600 __TEMP_INPUT_KEY_FILE
|
|
||||||
scp -o StrictHostKeyChecking=no -v -i __TEMP_INPUT_KEY_FILE -P "22" -r $DELTACHAT_NODE_TAR_GZ "${{ secrets.USERNAME }}"@"download.delta.chat":"/var/www/html/download/node/preview/"
|
|
||||||
continue-on-error: true
|
|
||||||
- name: "Post links to details"
|
|
||||||
if: steps.upload-preview.outcome == 'success'
|
|
||||||
run: node ./node/scripts/postLinksToDetails.js
|
|
||||||
env:
|
|
||||||
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 }}
|
|
||||||
id: upload
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo -e "${{ secrets.SSH_KEY }}" >__TEMP_INPUT_KEY_FILE
|
|
||||||
chmod 600 __TEMP_INPUT_KEY_FILE
|
|
||||||
scp -o StrictHostKeyChecking=no -v -i __TEMP_INPUT_KEY_FILE -P "22" -r $DELTACHAT_NODE_TAR_GZ "${{ secrets.USERNAME }}"@"download.delta.chat":"/var/www/html/download/node/"
|
|
||||||
71
.github/workflows/node-tests.yml
vendored
@@ -1,71 +0,0 @@
|
|||||||
name: 'node.js tests'
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- staging
|
|
||||||
- trying
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
tests:
|
|
||||||
name: 'tests'
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-18.04, macos-latest, windows-latest]
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: '16'
|
|
||||||
- name: System info
|
|
||||||
run: |
|
|
||||||
rustc -vV
|
|
||||||
rustup -vV
|
|
||||||
cargo -vV
|
|
||||||
npm --version
|
|
||||||
node --version
|
|
||||||
|
|
||||||
- name: Cache node modules
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
${{ env.APPDATA }}/npm-cache
|
|
||||||
~/.npm
|
|
||||||
key: ${{ matrix.os }}-node-${{ hashFiles('**/package.json') }}
|
|
||||||
|
|
||||||
- name: Cache cargo index
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry/
|
|
||||||
~/.cargo/git
|
|
||||||
target
|
|
||||||
key: ${{ matrix.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}-2
|
|
||||||
|
|
||||||
- name: Install dependencies & build
|
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
|
||||||
run: |
|
|
||||||
cd node
|
|
||||||
npm install --verbose
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
timeout-minutes: 10
|
|
||||||
if: runner.os != 'Windows'
|
|
||||||
run: |
|
|
||||||
cd node
|
|
||||||
npm run test
|
|
||||||
env:
|
|
||||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
|
||||||
NODE_OPTIONS: '--force-node-api-uncaught-exceptions-policy=true'
|
|
||||||
- name: Run tests on Windows, except lint
|
|
||||||
timeout-minutes: 10
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
run: |
|
|
||||||
cd node
|
|
||||||
npm run test:mocha
|
|
||||||
env:
|
|
||||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
|
||||||
NODE_OPTIONS: '--force-node-api-uncaught-exceptions-policy=true'
|
|
||||||
29
.github/workflows/repl.yml
vendored
@@ -1,29 +0,0 @@
|
|||||||
# Manually triggered action to build a Windows repl.exe which users can
|
|
||||||
# download to debug complex bugs.
|
|
||||||
|
|
||||||
name: Build Windows REPL .exe
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_repl:
|
|
||||||
name: Build REPL example
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Install Rust
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: 1.66.0
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: build
|
|
||||||
run: cargo build --example repl --features repl,vendored
|
|
||||||
|
|
||||||
- name: Upload binary
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: repl.exe
|
|
||||||
path: 'target/debug/examples/repl.exe'
|
|
||||||
30
.github/workflows/rust.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: -Dwarnings
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: 3.7 python tests against core
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
|
- name: Setup python
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
architecture: x64
|
||||||
|
|
||||||
|
- run: bash ci_scripts/run-python.sh
|
||||||
27
.github/workflows/upload-docs.yml
vendored
@@ -1,27 +0,0 @@
|
|||||||
name: Build & Deploy Documentation on rs.delta.chat
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- docs-gh-action
|
|
||||||
|
|
||||||
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/"
|
|
||||||
|
|
||||||
27
.github/workflows/upload-ffi-docs.yml
vendored
@@ -1,27 +0,0 @@
|
|||||||
name: Build & Deploy Documentation on cffi.delta.chat
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- docs-gh-action
|
|
||||||
|
|
||||||
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/"
|
|
||||||
|
|
||||||
19
.gitignore
vendored
@@ -1,6 +1,5 @@
|
|||||||
/target
|
/target
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
/build
|
|
||||||
|
|
||||||
# ignore vi temporaries
|
# ignore vi temporaries
|
||||||
*~
|
*~
|
||||||
@@ -12,8 +11,8 @@ include
|
|||||||
*.db
|
*.db
|
||||||
*.db-blobs
|
*.db-blobs
|
||||||
|
|
||||||
.tox
|
|
||||||
python/.eggs
|
python/.eggs
|
||||||
|
python/.tox
|
||||||
*.egg-info
|
*.egg-info
|
||||||
__pycache__
|
__pycache__
|
||||||
python/src/deltachat/capi*.so
|
python/src/deltachat/capi*.so
|
||||||
@@ -24,19 +23,3 @@ python/liveconfig*
|
|||||||
# ignore doxgen generated files
|
# ignore doxgen generated files
|
||||||
deltachat-ffi/html
|
deltachat-ffi/html
|
||||||
deltachat-ffi/xml
|
deltachat-ffi/xml
|
||||||
|
|
||||||
.rsynclist
|
|
||||||
|
|
||||||
coverage/
|
|
||||||
.DS_Store
|
|
||||||
.vscode/launch.json
|
|
||||||
python/accounts.txt
|
|
||||||
python/all-testaccounts.txt
|
|
||||||
tmp/
|
|
||||||
|
|
||||||
# from deltachat-node
|
|
||||||
node_modules/
|
|
||||||
node/build/
|
|
||||||
node/dist/
|
|
||||||
node/prebuilds/
|
|
||||||
node/.nyc_output/
|
|
||||||
|
|||||||
56
.npmignore
@@ -1,56 +0,0 @@
|
|||||||
.circleci/
|
|
||||||
.gitmodules
|
|
||||||
node/.nyc_output/
|
|
||||||
.travis.yml
|
|
||||||
appveyor.yml
|
|
||||||
node/build/
|
|
||||||
node/README.md
|
|
||||||
rustfmt.toml
|
|
||||||
spec.md
|
|
||||||
test-data/
|
|
||||||
build2/
|
|
||||||
node_modules
|
|
||||||
.git
|
|
||||||
.idea/
|
|
||||||
.pytest_cache
|
|
||||||
CMakeLists.txt
|
|
||||||
README.md
|
|
||||||
contrib/
|
|
||||||
node/ci_scripts/
|
|
||||||
coverage/
|
|
||||||
node/.circleci
|
|
||||||
node/appveyor.yml
|
|
||||||
ci/
|
|
||||||
ci_scripts/
|
|
||||||
python/
|
|
||||||
target
|
|
||||||
proptest-regressions
|
|
||||||
deltachat-ffi/Doxyfile
|
|
||||||
scripts
|
|
||||||
webxdc.md
|
|
||||||
standards.md
|
|
||||||
draft/
|
|
||||||
node/examples/
|
|
||||||
# deltachat-core-rust/assets # don't exclude assets, otherwise it won't build
|
|
||||||
node/images/
|
|
||||||
node/test/
|
|
||||||
node/windows.md
|
|
||||||
node/*.tar.gz
|
|
||||||
node/old_docs.md
|
|
||||||
.vscode/
|
|
||||||
.github/
|
|
||||||
node/.prettierrc.yml
|
|
||||||
|
|
||||||
deltachat-jsonrpc/TODO.md
|
|
||||||
deltachat-jsonrpc/README.MD
|
|
||||||
deltachat-jsonrpc/.gitignore
|
|
||||||
deltachat-jsonrpc/typescript/.gitignore
|
|
||||||
deltachat-jsonrpc/typescript/.prettierignore
|
|
||||||
deltachat-jsonrpc/typescript/accounts/
|
|
||||||
deltachat-jsonrpc/typescript/index.html
|
|
||||||
deltachat-jsonrpc/typescript/node-demo.js
|
|
||||||
deltachat-jsonrpc/typescript/report_api_coverage.mjs
|
|
||||||
deltachat-jsonrpc/typescript/test
|
|
||||||
deltachat-jsonrpc/typescript/example.ts
|
|
||||||
|
|
||||||
.DS_Store
|
|
||||||
2128
CHANGELOG.md
@@ -1,50 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
|
||||||
project(deltachat LANGUAGES C)
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
find_program(CARGO cargo)
|
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
set(DYNAMIC_EXT "dylib")
|
|
||||||
elseif(UNIX)
|
|
||||||
set(DYNAMIC_EXT "so")
|
|
||||||
else()
|
|
||||||
set(DYNAMIC_EXT "dll")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT
|
|
||||||
"target/release/libdeltachat.a"
|
|
||||||
"target/release/libdeltachat.${DYNAMIC_EXT}"
|
|
||||||
"target/release/pkgconfig/deltachat.pc"
|
|
||||||
COMMAND
|
|
||||||
PREFIX=${CMAKE_INSTALL_PREFIX}
|
|
||||||
LIBDIR=${CMAKE_INSTALL_FULL_LIBDIR}
|
|
||||||
INCLUDEDIR=${CMAKE_INSTALL_FULL_INCLUDEDIR}
|
|
||||||
${CARGO} build --release --no-default-features --features jsonrpc
|
|
||||||
|
|
||||||
# Build in `deltachat-ffi` directory instead of using
|
|
||||||
# `--package deltachat_ffi` to avoid feature resolver version
|
|
||||||
# "1" bug which makes `--no-default-features` affect only
|
|
||||||
# `deltachat`, but not `deltachat-ffi` package.
|
|
||||||
#
|
|
||||||
# We can't enable version "2" resolver [1] because it is not
|
|
||||||
# stable yet on rust 1.50.0.
|
|
||||||
#
|
|
||||||
# [1] https://doc.rust-lang.org/nightly/cargo/reference/features.html#resolver-version-2-command-line-flags
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/deltachat-ffi
|
|
||||||
)
|
|
||||||
|
|
||||||
add_custom_target(
|
|
||||||
lib_deltachat
|
|
||||||
ALL
|
|
||||||
DEPENDS
|
|
||||||
"target/release/libdeltachat.a"
|
|
||||||
"target/release/libdeltachat.${DYNAMIC_EXT}"
|
|
||||||
"target/release/pkgconfig/deltachat.pc"
|
|
||||||
)
|
|
||||||
|
|
||||||
install(FILES "deltachat-ffi/deltachat.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
|
||||||
install(FILES "target/release/libdeltachat.a" DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
||||||
install(FILES "target/release/libdeltachat.${DYNAMIC_EXT}" DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
||||||
install(FILES "target/release/pkgconfig/deltachat.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
|
||||||
5043
Cargo.lock
generated
184
Cargo.toml
@@ -1,154 +1,84 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deltachat"
|
name = "deltachat"
|
||||||
version = "1.107.1"
|
version = "1.0.0-beta.7"
|
||||||
edition = "2021"
|
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||||
license = "MPL-2.0"
|
edition = "2018"
|
||||||
rust-version = "1.63"
|
license = "MPL"
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
debug = 0
|
|
||||||
panic = 'abort'
|
|
||||||
opt-level = 1
|
|
||||||
|
|
||||||
[profile.test]
|
|
||||||
opt-level = 0
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = true
|
|
||||||
panic = 'abort'
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
deltachat_derive = { path = "./deltachat_derive" }
|
deltachat_derive = { path = "./deltachat_derive" }
|
||||||
format-flowed = { path = "./format-flowed" }
|
mmime = { version = "0.1.2", path = "./mmime" }
|
||||||
ratelimit = { path = "./deltachat-ratelimit" }
|
|
||||||
|
|
||||||
ansi_term = { version = "0.12.1", optional = true }
|
libc = "0.2.51"
|
||||||
anyhow = "1"
|
pgp = { version = "0.2.3", default-features = false }
|
||||||
async-imap = { git = "https://github.com/async-email/async-imap", branch = "master", default-features = false, features = ["runtime-tokio"] }
|
hex = "0.3.2"
|
||||||
async-native-tls = { version = "0.4", default-features = false, features = ["runtime-tokio"] }
|
sha2 = "0.8.0"
|
||||||
async-smtp = { version = "0.5", default-features = false, features = ["smtp-transport", "socks5", "runtime-tokio"] }
|
rand = "0.6.5"
|
||||||
trust-dns-resolver = "0.22"
|
smallvec = "0.6.9"
|
||||||
tokio = { version = "1", features = ["fs", "rt-multi-thread", "macros"] }
|
reqwest = { version = "0.9.15", default-features = false, features = ["rustls-tls"] }
|
||||||
tokio-tar = { version = "0.3" } # TODO: integrate tokio into async-tar
|
num-derive = "0.2.5"
|
||||||
backtrace = "0.3"
|
num-traits = "0.2.6"
|
||||||
base64 = "0.20"
|
lettre = { git = "https://github.com/deltachat/lettre", branch = "feat/rustls" }
|
||||||
bitflags = "1.3"
|
async-imap = "0.1"
|
||||||
chrono = { version = "0.4", default-features=false, features = ["clock", "std"] }
|
async-tls = "0.6"
|
||||||
dirs = { version = "4", optional=true }
|
async-std = { version = "1.0", features = ["unstable"] }
|
||||||
email = { git = "https://github.com/deltachat/rust-email", branch = "master" }
|
base64 = "0.10"
|
||||||
encoded-words = { git = "https://github.com/async-email/encoded-words", branch = "master" }
|
charset = "0.1"
|
||||||
escaper = "0.1"
|
percent-encoding = "2.0"
|
||||||
futures = "0.3"
|
|
||||||
hex = "0.4.0"
|
|
||||||
image = { version = "0.24.5", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] }
|
|
||||||
kamadak-exif = "0.5"
|
|
||||||
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
|
|
||||||
libc = "0.2"
|
|
||||||
log = {version = "0.4.16", optional = true }
|
|
||||||
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"
|
|
||||||
pgp = { version = "0.9", default-features = false }
|
|
||||||
pretty_env_logger = { version = "0.4", optional = true }
|
|
||||||
quick-xml = "0.27"
|
|
||||||
r2d2 = "0.8"
|
|
||||||
r2d2_sqlite = "0.20"
|
|
||||||
rand = "0.8"
|
|
||||||
regex = "1.7"
|
|
||||||
rusqlite = { version = "0.27", features = ["sqlcipher"] }
|
|
||||||
rust-hsluv = "0.1"
|
|
||||||
rustyline = { version = "10", optional = true }
|
|
||||||
sanitize-filename = "0.4"
|
|
||||||
serde_json = "1.0"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
sha-1 = "0.10"
|
serde_json = "1.0"
|
||||||
sha2 = "0.10"
|
chrono = "0.4.6"
|
||||||
smallvec = "1"
|
failure = "0.1.5"
|
||||||
strum = "0.24"
|
failure_derive = "0.1.5"
|
||||||
strum_macros = "0.24"
|
# TODO: make optional
|
||||||
thiserror = "1"
|
rustyline = "4.1.0"
|
||||||
toml = "0.5"
|
lazy_static = "1.4.0"
|
||||||
url = "2"
|
regex = "1.1.6"
|
||||||
uuid = { version = "1", features = ["serde", "v4"] }
|
rusqlite = { version = "0.20", features = ["bundled"] }
|
||||||
fast-socks5 = "0.8"
|
r2d2_sqlite = "0.12.0"
|
||||||
humansize = "2"
|
r2d2 = "0.8.5"
|
||||||
qrcodegen = "1.7.0"
|
strum = "0.16.0"
|
||||||
tagger = "4.3.4"
|
strum_macros = "0.16.0"
|
||||||
textwrap = "0.16.0"
|
thread-local-object = "0.1.0"
|
||||||
async-channel = "1.8.0"
|
backtrace = "0.3.33"
|
||||||
futures-lite = "1.12.0"
|
byteorder = "1.3.1"
|
||||||
tokio-stream = { version = "0.1.11", features = ["fs"] }
|
itertools = "0.8.0"
|
||||||
tokio-io-timeout = "1.2.0"
|
image-meta = "0.1.0"
|
||||||
reqwest = { version = "0.11.13", features = ["json"] }
|
quick-xml = "0.15.0"
|
||||||
async_zip = { version = "0.0.9", default-features = false, features = ["deflate"] }
|
escaper = "0.1.0"
|
||||||
|
bitflags = "1.1.0"
|
||||||
|
jetscii = "0.4.4"
|
||||||
|
debug_stub_derive = "0.3.0"
|
||||||
|
sanitize-filename = "0.2.1"
|
||||||
|
stop-token = { version = "0.1.1", features = ["unstable"] }
|
||||||
|
rustls = "0.16.0"
|
||||||
|
webpki-roots = "0.18.0"
|
||||||
|
webpki = "0.21.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ansi_term = "0.12.0"
|
tempfile = "3.0"
|
||||||
criterion = { version = "0.4.0", features = ["async_tokio"] }
|
pretty_assertions = "0.6.1"
|
||||||
futures-lite = "1.12"
|
pretty_env_logger = "0.3.0"
|
||||||
log = "0.4"
|
proptest = "0.9.4"
|
||||||
pretty_env_logger = "0.4"
|
|
||||||
proptest = { version = "1", default-features = false, features = ["std"] }
|
|
||||||
tempfile = "3"
|
|
||||||
tokio = { version = "1", features = ["parking_lot", "rt-multi-thread", "macros"] }
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"deltachat-ffi",
|
"deltachat-ffi",
|
||||||
"deltachat_derive",
|
"deltachat_derive",
|
||||||
"deltachat-jsonrpc",
|
"mmime",
|
||||||
"deltachat-rpc-server",
|
|
||||||
"deltachat-ratelimit",
|
|
||||||
"format-flowed",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "simple"
|
name = "simple"
|
||||||
path = "examples/simple.rs"
|
path = "examples/simple.rs"
|
||||||
required-features = ["repl"]
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "repl"
|
name = "repl"
|
||||||
path = "examples/repl/main.rs"
|
path = "examples/repl/main.rs"
|
||||||
required-features = ["repl"]
|
|
||||||
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "create_account"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "contacts"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "search_msgs"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "receive_emails"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "get_chat_msgs"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "get_chatlist"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["vendored"]
|
default = ["nightly", "ringbuf"]
|
||||||
internals = []
|
vendored = []
|
||||||
repl = ["internals", "rustyline", "log", "pretty_env_logger", "ansi_term", "dirs"]
|
|
||||||
vendored = [
|
|
||||||
"async-native-tls/vendored",
|
|
||||||
"async-smtp/native-tls-vendored",
|
|
||||||
"rusqlite/bundled-sqlcipher-vendored-openssl",
|
|
||||||
"reqwest/native-tls-vendored"
|
|
||||||
]
|
|
||||||
nightly = ["pgp/nightly"]
|
nightly = ["pgp/nightly"]
|
||||||
|
ringbuf = ["pgp/ringbuf"]
|
||||||
|
|||||||
3
LICENSE
@@ -2,6 +2,9 @@ The files in this directory and under its subdirectories
|
|||||||
are (c) 2019 by Bjoern Petersen and contributors and released under the
|
are (c) 2019 by Bjoern Petersen and contributors and released under the
|
||||||
Mozilla Public License Version 2.0, see below for a copy.
|
Mozilla Public License Version 2.0, see below for a copy.
|
||||||
|
|
||||||
|
NOTE that the files in the "libs" directory are copyrighted by third parties
|
||||||
|
and come with their own respective licenses.
|
||||||
|
|
||||||
Mozilla Public License Version 2.0
|
Mozilla Public License Version 2.0
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
|
|||||||
93
README.md
@@ -1,27 +1,26 @@
|
|||||||
# Delta Chat Rust
|
# Delta Chat Rust
|
||||||
|
|
||||||
> Deltachat-core written in Rust
|
> Project porting deltachat-core to rust
|
||||||
|
|
||||||
[](https://github.com/deltachat/deltachat-core-rust/actions/workflows/ci.yml)
|
[![CircleCI build status][circle-shield]][circle] [![Appveyor build status][appveyor-shield]][appveyor]
|
||||||
|
|
||||||
|
Current commit on deltachat/deltachat-core: `12ef73c8e76185f9b78e844ea673025f56a959ab`.
|
||||||
|
|
||||||
## Installing Rust and Cargo
|
## Installing Rust and Cargo
|
||||||
|
|
||||||
To download and install the official compiler for the Rust programming language, and the Cargo package manager, run the command in your user environment:
|
To download and install the official compiler for the Rust programming language, and the Cargo package manager, run the command in your user environment:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl https://sh.rustup.rs -sSf | sh
|
curl https://sh.rustup.rs -sSf | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
> On Windows, you may need to also install **Perl** to be able to compile deltachat-core.
|
|
||||||
|
|
||||||
## Using the CLI client
|
## Using the CLI client
|
||||||
|
|
||||||
Compile and run Delta Chat Core command line utility, using `cargo`:
|
Compile and run Delta Chat Core using `cargo`:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ RUST_LOG=repl=info cargo run --example repl --features repl -- ~/deltachat-db
|
cargo run --example repl -- /path/to/db
|
||||||
```
|
```
|
||||||
where ~/deltachat-db is the database file. Delta Chat will create it if it does not exist.
|
|
||||||
|
|
||||||
Configure your account (if not already configured):
|
Configure your account (if not already configured):
|
||||||
|
|
||||||
@@ -81,16 +80,6 @@ For more commands type:
|
|||||||
> help
|
> help
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installing libdeltachat system wide
|
|
||||||
|
|
||||||
```
|
|
||||||
$ git clone https://github.com/deltachat/deltachat-core-rust.git
|
|
||||||
$ cd deltachat-core-rust
|
|
||||||
$ cmake -B build . -DCMAKE_INSTALL_PREFIX=/usr
|
|
||||||
$ cmake --build build
|
|
||||||
$ sudo cmake --install build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -100,13 +89,6 @@ $ cargo test --all
|
|||||||
$ cargo build -p deltachat_ffi --release
|
$ cargo build -p deltachat_ffi --release
|
||||||
```
|
```
|
||||||
|
|
||||||
## Debugging environment variables
|
|
||||||
|
|
||||||
- `DCC_MIME_DEBUG`: if set outgoing and incoming message will be printed
|
|
||||||
|
|
||||||
- `RUST_LOG=repl=info,async_imap=trace,async_smtp=trace`: enable IMAP and
|
|
||||||
SMTP tracing in addition to info messages.
|
|
||||||
|
|
||||||
### Expensive tests
|
### Expensive tests
|
||||||
|
|
||||||
Some tests are expensive and marked with `#[ignore]`, to run these
|
Some tests are expensive and marked with `#[ignore]`, to run these
|
||||||
@@ -115,64 +97,13 @@ use the `--ignored` argument to the test binary (not to cargo itself):
|
|||||||
$ cargo test -- --ignored
|
$ cargo test -- --ignored
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fuzzing
|
|
||||||
|
|
||||||
Install [`cargo-bolero`](https://github.com/camshaft/bolero) with
|
|
||||||
```sh
|
|
||||||
$ cargo install cargo-bolero
|
|
||||||
```
|
|
||||||
|
|
||||||
Run fuzzing tests with
|
|
||||||
```sh
|
|
||||||
$ cd fuzz
|
|
||||||
$ cargo bolero test fuzz_mailparse --release=false -s NONE
|
|
||||||
```
|
|
||||||
|
|
||||||
Corpus is created at `fuzz/fuzz_targets/corpus`,
|
|
||||||
you can add initial inputs there.
|
|
||||||
For `fuzz_mailparse` target corpus can be populated with
|
|
||||||
`../test-data/message/*.eml`.
|
|
||||||
|
|
||||||
To run with AFL instead of libFuzzer:
|
|
||||||
```sh
|
|
||||||
$ cargo bolero test fuzz_format_flowed --release=false -e afl -s NONE
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- `vendored`: When using Openssl for TLS, this bundles a vendored version.
|
- `vendored`: When using Openssl for TLS, this bundles a vendored version.
|
||||||
- `nightly`: Enable nightly only performance and security related features.
|
- `nightly`: Enable nightly only performance and security related features.
|
||||||
|
- `ringbuf`: Enable the use of [`slice_deque`](https://github.com/gnzlbg/slice_deque) in pgp.
|
||||||
|
|
||||||
## Update Provider Data
|
[circle-shield]: https://img.shields.io/circleci/project/github/deltachat/deltachat-core-rust/master.svg?style=flat-square
|
||||||
|
[circle]: https://circleci.com/gh/deltachat/deltachat-core-rust/
|
||||||
To add the updates from the
|
[appveyor-shield]: https://ci.appveyor.com/api/projects/status/lqpegel3ld4ipxj8/branch/master?style=flat-square
|
||||||
[provider-db](https://github.com/deltachat/provider-db) to the core, run:
|
[appveyor]: https://ci.appveyor.com/project/dignifiedquire/deltachat-core-rust/branch/master
|
||||||
|
|
||||||
```
|
|
||||||
./src/provider/update.py ../provider-db/_providers/ > src/provider/data.rs
|
|
||||||
```
|
|
||||||
|
|
||||||
## Language bindings and frontend projects
|
|
||||||
|
|
||||||
Language bindings are available for:
|
|
||||||
|
|
||||||
- **C** \[[📂 source](./deltachat-ffi) | [📚 docs](https://c.delta.chat)\]
|
|
||||||
- **Node.js**
|
|
||||||
- over cffi (legacy): \[[📂 source](./node) | [📦 npm](https://www.npmjs.com/package/deltachat-node) | [📚 docs](https://js.delta.chat)\]
|
|
||||||
- over jsonrpc built with napi.rs: \[[📂 source](https://github.com/deltachat/napi-jsonrpc) | [📦 npm](https://www.npmjs.com/package/@deltachat/napi-jsonrpc)\]
|
|
||||||
- **Python** \[[📂 source](./python) | [📦 pypi](https://pypi.org/project/deltachat) | [📚 docs](https://py.delta.chat)\]
|
|
||||||
- **Go**[^1] \[[📂 source](https://github.com/deltachat/go-deltachat/)\]
|
|
||||||
- **Free Pascal**[^1] \[[📂 source](https://github.com/deltachat/deltachat-fp/)\]
|
|
||||||
- **Java** and **Swift** (contained in the Android/iOS repos)
|
|
||||||
|
|
||||||
The following "frontend" projects make use of the Rust-library
|
|
||||||
or its language bindings:
|
|
||||||
|
|
||||||
- [Android](https://github.com/deltachat/deltachat-android)
|
|
||||||
- [iOS](https://github.com/deltachat/deltachat-ios)
|
|
||||||
- [Desktop](https://github.com/deltachat/deltachat-desktop)
|
|
||||||
- [Pidgin](https://code.ur.gs/lupine/purple-plugin-delta/)
|
|
||||||
- [Telepathy](https://code.ur.gs/lupine/telepathy-padfoot/)
|
|
||||||
- several **Bots**
|
|
||||||
|
|
||||||
[^1]: Out of date / unmaintained, if you like those languages feel free to start maintaining them. If you have questions we'll help you, please ask in the issues.
|
|
||||||
|
|||||||
20
appveyor.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
environment:
|
||||||
|
matrix:
|
||||||
|
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||||
|
|
||||||
|
install:
|
||||||
|
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||||
|
- rustup-init -yv --default-toolchain nightly-2019-07-10
|
||||||
|
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||||
|
- rustc -vV
|
||||||
|
- cargo -vV
|
||||||
|
- cargo update
|
||||||
|
|
||||||
|
build: false
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- cargo test --release --all
|
||||||
|
|
||||||
|
cache:
|
||||||
|
- target
|
||||||
|
- C:\Users\appveyor\.cargo\registry
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,60 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
width="60"
|
|
||||||
height="60"
|
|
||||||
viewBox="0 0 60 60"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="2"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
class="feather feather-archive"
|
|
||||||
version="1.1"
|
|
||||||
id="svg8"
|
|
||||||
sodipodi:docname="icon-archive.svg"
|
|
||||||
inkscape:version="1.2.2 (b0a84865, 2022-12-01)"
|
|
||||||
inkscape:export-filename="icon-archive.png"
|
|
||||||
inkscape:export-xdpi="409.60001"
|
|
||||||
inkscape:export-ydpi="409.60001"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs12" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview10"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#000000"
|
|
||||||
borderopacity="0.25"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="6.4597151"
|
|
||||||
inkscape:cx="24.459283"
|
|
||||||
inkscape:cy="32.509174"
|
|
||||||
inkscape:window-width="1457"
|
|
||||||
inkscape:window-height="860"
|
|
||||||
inkscape:window-x="55"
|
|
||||||
inkscape:window-y="38"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="svg8" />
|
|
||||||
<g
|
|
||||||
id="g846"
|
|
||||||
transform="translate(0.558605,0.464417)">
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#808080;stroke-width:1.78186;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 38.749006,25.398867 V 38.843194 H 20.133784 V 25.398867"
|
|
||||||
id="path847" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#808080;stroke-width:1.78186;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 18.065427,20.227972 h 22.751936 v 5.170894 H 18.065427 Z"
|
|
||||||
id="path845" />
|
|
||||||
<path
|
|
||||||
style="fill:#ff0000;fill-opacity:1;stroke:#808080;stroke-width:1.78186;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 27.373036,29.535581 h 4.136718"
|
|
||||||
id="line6" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,149 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
enable-background="new 0 0 128 128"
|
|
||||||
viewBox="0 0 60 60"
|
|
||||||
version="1.1"
|
|
||||||
id="svg878"
|
|
||||||
sodipodi:docname="icon-broadcast.svg"
|
|
||||||
width="60"
|
|
||||||
height="60"
|
|
||||||
inkscape:version="1.0.2 (e86c8708, 2021-01-15)"
|
|
||||||
inkscape:export-filename="/Users/bpetersen/projects/deltachat-core-rust/assets/icon-broadcast.png"
|
|
||||||
inkscape:export-xdpi="409.60001"
|
|
||||||
inkscape:export-ydpi="409.60001">
|
|
||||||
<metadata
|
|
||||||
id="metadata884">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs882" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="1329"
|
|
||||||
inkscape:window-height="847"
|
|
||||||
id="namedview880"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="5.21875"
|
|
||||||
inkscape:cx="36.598802"
|
|
||||||
inkscape:cy="32.191617"
|
|
||||||
inkscape:window-x="111"
|
|
||||||
inkscape:window-y="205"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="svg878"
|
|
||||||
inkscape:document-rotation="0" />
|
|
||||||
<radialGradient
|
|
||||||
id="c"
|
|
||||||
cx="65.25"
|
|
||||||
cy="89"
|
|
||||||
r="26.440001"
|
|
||||||
gradientTransform="matrix(0.77611266,0.11996647,-0.18999676,1.2286617,-11.305867,-60.065999)"
|
|
||||||
gradientUnits="userSpaceOnUse">
|
|
||||||
<stop
|
|
||||||
stop-color="#FFC107"
|
|
||||||
offset="0"
|
|
||||||
id="stop833" />
|
|
||||||
<stop
|
|
||||||
stop-color="#FFBD06"
|
|
||||||
offset=".3502"
|
|
||||||
id="stop835" />
|
|
||||||
<stop
|
|
||||||
stop-color="#FFB104"
|
|
||||||
offset=".6938"
|
|
||||||
id="stop837" />
|
|
||||||
<stop
|
|
||||||
stop-color="#FFA000"
|
|
||||||
offset="1"
|
|
||||||
id="stop839" />
|
|
||||||
</radialGradient>
|
|
||||||
<radialGradient
|
|
||||||
id="b"
|
|
||||||
cx="52.5"
|
|
||||||
cy="19.75"
|
|
||||||
r="92.975998"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="rotate(45.323856,68.997115,75.979538)">
|
|
||||||
<stop
|
|
||||||
stop-color="#EF5350"
|
|
||||||
offset="0"
|
|
||||||
id="stop848" />
|
|
||||||
<stop
|
|
||||||
stop-color="#EB4F4C"
|
|
||||||
offset=".246"
|
|
||||||
id="stop850" />
|
|
||||||
<stop
|
|
||||||
stop-color="#E04341"
|
|
||||||
offset=".4878"
|
|
||||||
id="stop852" />
|
|
||||||
<stop
|
|
||||||
stop-color="#CD302F"
|
|
||||||
offset=".7272"
|
|
||||||
id="stop854" />
|
|
||||||
<stop
|
|
||||||
stop-color="#C62828"
|
|
||||||
offset=".8004"
|
|
||||||
id="stop856" />
|
|
||||||
<stop
|
|
||||||
stop-color="#C62828"
|
|
||||||
offset="1"
|
|
||||||
id="stop858" />
|
|
||||||
</radialGradient>
|
|
||||||
<radialGradient
|
|
||||||
id="a"
|
|
||||||
cx="16.979"
|
|
||||||
cy="92"
|
|
||||||
r="24.165001"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="rotate(45.323856,68.997115,75.979538)"
|
|
||||||
xlink:href="#b">
|
|
||||||
<stop
|
|
||||||
stop-color="#E0E0E0"
|
|
||||||
offset="0"
|
|
||||||
id="stop863" />
|
|
||||||
<stop
|
|
||||||
stop-color="#CFCFCF"
|
|
||||||
offset=".3112"
|
|
||||||
id="stop865" />
|
|
||||||
<stop
|
|
||||||
stop-color="#A4A4A4"
|
|
||||||
offset=".9228"
|
|
||||||
id="stop867" />
|
|
||||||
<stop
|
|
||||||
stop-color="#9E9E9E"
|
|
||||||
offset="1"
|
|
||||||
id="stop869" />
|
|
||||||
</radialGradient>
|
|
||||||
<rect
|
|
||||||
y="0"
|
|
||||||
x="0"
|
|
||||||
height="60"
|
|
||||||
width="60"
|
|
||||||
id="rect1420"
|
|
||||||
style="fill:#7cc0bc;fill-opacity:1;stroke:none;stroke-width:1.29077" />
|
|
||||||
<path
|
|
||||||
id="path872"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.336872;stroke-opacity:1"
|
|
||||||
d="m 8.6780027,35.573064 0.032831,-11.910176 c 0.00138,-0.476406 0.4881282,-0.794259 0.9235226,-0.604877 l 4.1144877,2.345752 -0.02386,8.656315 -4.1268029,2.122946 C 9.1617452,36.370003 8.6766889,36.049472 8.6780027,35.573064 Z m 5.0469633,-1.508222 0.02386,-8.656314 31.145424,-9.537653 c 0.841472,-0.219211 1.65915,0.41667 1.656755,1.283728 l -0.06929,25.139995 c -0.0024,0.867062 -0.825942,1.500799 -1.663803,1.274581 z m 3.8042,6.892234 C 16.681121,40.104348 16.315444,38.819414 16.69043,37.591308 l 2.252234,-7.347193 c 0.2644,-0.861571 0.845185,-1.567441 1.641953,-1.989251 0.796769,-0.421808 1.706956,-0.509819 2.568531,-0.245419 l 7.263888,2.225804 c 1.775518,0.543235 2.780299,2.432591 2.232297,4.208094 L 30.3971,41.790532 c -0.545627,1.777887 -2.432591,2.780297 -4.208095,2.232298 l -7.263891,-2.225804 c -0.545033,-0.165864 -1.01825,-0.460162 -1.395948,-0.83995 z m 12.377693,-7.976728 c -0.07601,-0.07642 -0.17114,-0.133864 -0.280621,-0.167516 l -7.263891,-2.225803 c -0.233244,-0.07209 -0.421626,0.0013 -0.512275,0.04861 -0.09064,0.0474 -0.25772,0.166033 -0.327435,0.396899 l -2.252234,7.347191 c -0.108166,0.354628 0.09088,0.731541 0.447888,0.842099 l 7.263891,2.225802 c 0.354626,0.108174 0.731539,-0.09088 0.842099,-0.447888 l 2.249845,-7.344814 c 0.07453,-0.245145 0.0014,-0.504991 -0.167267,-0.67458 z" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.6 KiB |
@@ -1,83 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
inkscape:export-ydpi="409.60001"
|
|
||||||
inkscape:export-xdpi="409.60001"
|
|
||||||
inkscape:export-filename="/Users/bpetersen/projects/deltachat-core-rust/assets/icon-device.png"
|
|
||||||
version="1.0"
|
|
||||||
width="60"
|
|
||||||
height="60"
|
|
||||||
viewBox="0 0 45 45"
|
|
||||||
preserveAspectRatio="xMidYMid meet"
|
|
||||||
id="svg4344"
|
|
||||||
sodipodi:docname="icon-device.svg"
|
|
||||||
inkscape:version="1.0beta1 (32d4812, 2019-09-19)">
|
|
||||||
<defs
|
|
||||||
id="defs4348" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
inkscape:snap-global="false"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="1600"
|
|
||||||
inkscape:window-height="1035"
|
|
||||||
id="namedview4346"
|
|
||||||
showgrid="false"
|
|
||||||
units="px"
|
|
||||||
inkscape:zoom="3.959798"
|
|
||||||
inkscape:cx="28.322498"
|
|
||||||
inkscape:cy="24.898474"
|
|
||||||
inkscape:window-x="45"
|
|
||||||
inkscape:window-y="23"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="svg4344" />
|
|
||||||
<metadata
|
|
||||||
id="metadata4336">
|
|
||||||
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<rect
|
|
||||||
y="-4.4408921e-16"
|
|
||||||
x="0"
|
|
||||||
height="45"
|
|
||||||
width="45"
|
|
||||||
id="rect860"
|
|
||||||
style="opacity:1;fill:#76868b;fill-opacity:1;stroke-width:0.819271" />
|
|
||||||
<g
|
|
||||||
fill="#000000"
|
|
||||||
stroke="none"
|
|
||||||
style="fill:#ffffff;fill-opacity:1"
|
|
||||||
transform="matrix(0.00255113,0,0,-0.00255113,5.586152,38.200477)"
|
|
||||||
id="g4342">
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1"
|
|
||||||
d="m 8175,12765 c -703,-114 -1248,-608 -1387,-1258 -17,-82 -21,-136 -22,-277 0,-202 15,-307 70,-470 149,-446 499,-733 1009,-828 142,-26 465,-23 619,6 691,131 1201,609 1328,1244 31,158 31,417 0,565 -114,533 -482,889 -1038,1004 -133,27 -448,35 -579,14 z"
|
|
||||||
id="path4338"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1"
|
|
||||||
d="m 7070,9203 c -212,-20 -275,-27 -397,-48 -691,-117 -1400,-444 -2038,-940 -182,-142 -328,-270 -585,-517 -595,-571 -911,-974 -927,-1181 -6,-76 11,-120 69,-184 75,-80 159,-108 245,-79 109,37 263,181 632,595 539,606 774,826 1035,969 135,75 231,105 341,106 82,1 94,-2 138,-27 116,-68 161,-209 122,-376 -9,-36 -349,-868 -757,-1850 -407,-982 -785,-1892 -838,-2021 -287,-694 -513,-1389 -615,-1889 -70,-342 -90,-683 -52,-874 88,-440 381,-703 882,-792 124,-23 401,-30 562,-16 783,69 1674,461 2561,1125 796,596 1492,1354 1607,1751 43,146 -33,308 -168,360 -61,23 -100,15 -173,-36 -105,-74 -202,-170 -539,-529 -515,-551 -762,-783 -982,-927 -251,-164 -437,-186 -543,-65 -56,64 -74,131 -67,247 13,179 91,434 249,815 135,324 1588,4102 1646,4280 106,325 151,561 159,826 9,281 -22,463 -112,652 -58,122 -114,199 -211,292 -245,233 -582,343 -1044,338 -91,-1 -181,-3 -200,-5 z"
|
|
||||||
id="path4340"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,71 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
inkscape:export-ydpi="409.60001"
|
|
||||||
inkscape:export-xdpi="409.60001"
|
|
||||||
inkscape:export-filename="/home/kerle/test-icon.png"
|
|
||||||
version="1.0"
|
|
||||||
width="60"
|
|
||||||
height="60"
|
|
||||||
viewBox="0 0 45 45"
|
|
||||||
preserveAspectRatio="xMidYMid meet"
|
|
||||||
id="svg4344"
|
|
||||||
sodipodi:docname="icon-saved-messages.svg"
|
|
||||||
inkscape:version="1.0beta1 (32d4812, 2019-09-19)">
|
|
||||||
<defs
|
|
||||||
id="defs4348" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="1395"
|
|
||||||
inkscape:window-height="855"
|
|
||||||
id="namedview4346"
|
|
||||||
showgrid="false"
|
|
||||||
units="px"
|
|
||||||
inkscape:zoom="4"
|
|
||||||
inkscape:cx="29.308676"
|
|
||||||
inkscape:cy="49.03624"
|
|
||||||
inkscape:window-x="89"
|
|
||||||
inkscape:window-y="108"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="svg4344"
|
|
||||||
inkscape:lockguides="false" />
|
|
||||||
<metadata
|
|
||||||
id="metadata4336">
|
|
||||||
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<rect
|
|
||||||
y="0"
|
|
||||||
x="0"
|
|
||||||
height="45"
|
|
||||||
width="45"
|
|
||||||
id="rect1420"
|
|
||||||
style="fill:#87aade;fill-opacity:1;stroke:none;stroke-width:0.968078" />
|
|
||||||
<path
|
|
||||||
id="rect846"
|
|
||||||
style="fill:#ffffff;stroke-width:0.58409804"
|
|
||||||
d="M 13.5,7.5 V 39 h 0.08654 L 22.533801,29.370239 31.482419,39 h 0.01758 V 7.5 Z m 9.004056,4.108698 1.879508,4.876388 5.039514,0.359779 -3.879358,3.363728 1.227764,5.095749 -4.276893,-2.796643 -4.280949,2.788618 1.237229,-5.093073 -3.873949,-3.371754 5.040866,-0.350417 z"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
@@ -1,181 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="80mm"
|
|
||||||
height="297mm"
|
|
||||||
viewBox="0 0 80 297"
|
|
||||||
version="1.1"
|
|
||||||
id="svg71"
|
|
||||||
inkscape:version="1.0.2 (e86c8708, 2021-01-15)"
|
|
||||||
sodipodi:docname="icon-webxdc.svg"
|
|
||||||
inkscape:export-filename="C:\Users\user\OneDrive - BFW-Leipzig\Documents\LogoDC\finalohnerand.png"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96">
|
|
||||||
<metadata
|
|
||||||
id="metadata856">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview73"
|
|
||||||
pagecolor="#767676"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="true"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
showgrid="false"
|
|
||||||
showborder="false"
|
|
||||||
inkscape:snap-bbox="true"
|
|
||||||
inkscape:bbox-paths="true"
|
|
||||||
inkscape:bbox-nodes="true"
|
|
||||||
inkscape:snap-bbox-edge-midpoints="true"
|
|
||||||
inkscape:snap-bbox-midpoints="true"
|
|
||||||
inkscape:object-paths="true"
|
|
||||||
inkscape:snap-intersection-paths="true"
|
|
||||||
inkscape:zoom="1.4142136"
|
|
||||||
inkscape:cx="-90.271136"
|
|
||||||
inkscape:cy="-1233.1209"
|
|
||||||
inkscape:window-width="1864"
|
|
||||||
inkscape:window-height="1027"
|
|
||||||
inkscape:window-x="56"
|
|
||||||
inkscape:window-y="25"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:snap-global="false"
|
|
||||||
showguides="false"
|
|
||||||
inkscape:guide-bbox="true"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
units="px">
|
|
||||||
<sodipodi:guide
|
|
||||||
position="-154.76097,641.11689"
|
|
||||||
orientation="0,-1"
|
|
||||||
id="guide21118" />
|
|
||||||
<sodipodi:guide
|
|
||||||
position="-60.286487,633.36619"
|
|
||||||
orientation="0,-1"
|
|
||||||
id="guide21120" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<defs
|
|
||||||
id="defs68">
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient4375">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#364e59;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4377" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#364e59;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop4379" />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<g
|
|
||||||
inkscape:label="Ebene 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
|
||||||
<rect
|
|
||||||
style="fill:#1a1a1a;stroke:#000000;stroke-width:0.167903"
|
|
||||||
id="rect880"
|
|
||||||
width="79.8321"
|
|
||||||
height="79.8321"
|
|
||||||
x="-64.03286"
|
|
||||||
y="-375.9097"
|
|
||||||
ry="0" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path3799-2"
|
|
||||||
d="m -24.089585,-372.59579 c -19.986026,0.24336 -36.196903,16.666 -36.196903,36.67011 0,20.00409 16.210877,36.03233 36.196903,35.78912 19.0024236,-0.076 14.5340713,-10.6146 35.538854,-0.85693 -11.50627538,-17.97454 0.390097,-20.36737 0.658079,-35.81316 0,-20.00411 -16.2108788,-36.03235 -36.196911,-35.78914 z"
|
|
||||||
style="fill:#364e59;fill-opacity:1;stroke:none;stroke-width:1.93355"
|
|
||||||
sodipodi:nodetypes="sscccs" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="M -54.193871,-325.26419 Z"
|
|
||||||
id="path3846" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="M -49.397951,-326.67773 Z"
|
|
||||||
id="path3848" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m -49.397951,-326.67773 v 0 0"
|
|
||||||
id="path3850" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:0.01;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m -51.35133,-325.0334 -7.964067,5.98895 z"
|
|
||||||
id="path3965" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path11037"
|
|
||||||
d="m -24.089585,-372.19891 c -19.986026,0.24156 -36.196903,16.54352 -36.196903,36.40062 0,7.86524 2.543315,15.1113 6.857155,20.97971 6.577146,8.94734 11.123515,9.77363 11.123515,9.77363 1.343237,1.78324 10.270932,4.3223 10.270932,4.3223 l 16.791727,-70.86654 -0.468369,-0.33457 c 0.458597,0.26445 0.428277,-0.27515 -8.378035,-0.27515 z"
|
|
||||||
style="fill:#7cc5cc;fill-opacity:1;stroke:none;stroke-width:1.92643"
|
|
||||||
sodipodi:nodetypes="sssccccss" />
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="M -49.944239,-310.69957 Z"
|
|
||||||
id="path13674" />
|
|
||||||
<g
|
|
||||||
id="g15178"
|
|
||||||
transform="matrix(0.79975737,0,0,0.79975737,53.088959,-63.716396)">
|
|
||||||
<rect
|
|
||||||
style="fill:#364e59;fill-opacity:1;stroke-width:0.01;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
id="rect15072"
|
|
||||||
width="29.897917"
|
|
||||||
height="6.8791666"
|
|
||||||
x="-334.4964"
|
|
||||||
y="-154.51025"
|
|
||||||
transform="rotate(45)" />
|
|
||||||
<rect
|
|
||||||
style="fill:#364e59;fill-opacity:1;stroke-width:0.01;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
id="rect15072-5"
|
|
||||||
width="29.897917"
|
|
||||||
height="6.8791666"
|
|
||||||
x="147.63107"
|
|
||||||
y="-334.4964"
|
|
||||||
transform="rotate(-45)"
|
|
||||||
inkscape:transform-center-x="-0.74835017"
|
|
||||||
inkscape:transform-center-y="0.37417525" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
id="g22468"
|
|
||||||
transform="translate(3.3033974)">
|
|
||||||
<g
|
|
||||||
id="g15178-0"
|
|
||||||
transform="matrix(-0.79975737,0,0,0.79975737,-103.11028,-63.716404)"
|
|
||||||
style="fill:#7cc5cc;fill-opacity:1">
|
|
||||||
<rect
|
|
||||||
style="fill:#7cc5cc;fill-opacity:1;stroke-width:0.01;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
id="rect15072-2"
|
|
||||||
width="29.897917"
|
|
||||||
height="6.8791666"
|
|
||||||
x="-334.4964"
|
|
||||||
y="-154.51025"
|
|
||||||
transform="rotate(45)" />
|
|
||||||
<rect
|
|
||||||
style="fill:#7cc5cc;fill-opacity:1;stroke-width:0.01;stroke-miterlimit:4;stroke-dasharray:none"
|
|
||||||
id="rect15072-5-5"
|
|
||||||
width="29.897917"
|
|
||||||
height="6.8791666"
|
|
||||||
x="147.63107"
|
|
||||||
y="-334.4964"
|
|
||||||
transform="rotate(-45)"
|
|
||||||
inkscape:transform-center-x="-0.74835017"
|
|
||||||
inkscape:transform-center-y="0.37417525" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 6.5 KiB |
@@ -1,10 +0,0 @@
|
|||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-weight:bold;font-size:24.4118px;line-height:1.25;font-family:sans-serif;fill:#aaaaaa;fill-opacity:1;stroke:none;stroke-width:0.915439"
|
|
||||||
x="42.325161"
|
|
||||||
y="23.32255"
|
|
||||||
id="text72398">get.delta.chat</text>
|
|
||||||
<path
|
|
||||||
id="path84310"
|
|
||||||
style="opacity:0.25;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.915439"
|
|
||||||
d="M 17.13769,0.00129321 C 7.6753075,0.11650893 0,7.8915283 0,17.362467 c 0,9.47094 7.6753075,17.059745 17.13769,16.944599 8.99669,-0.03598 6.880074,-5.025654 16.824785,-0.405885 -5.447648,-8.510047 0.184241,-9.642482 0.311117,-16.955289 0,-9.4709395 -7.673512,-17.0597453 -17.135895,-16.94459879 z M 17.0769,4.9986797 c 1.84214,0 3.447355,0.253959 4.815003,0.7616693 1.381603,0.5076411 2.072253,1.207862 2.072253,2.0990711 0,0.4286855 -0.167495,0.7836052 -0.50242,1.0656242 -0.334921,0.2819844 -0.724544,0.4237724 -1.171121,0.4237724 -0.641952,0 -1.396532,-0.3909376 -2.261778,-1.169353 C 19.14963,7.3898036 18.402555,6.83791 17.788507,6.5220182 17.188416,6.1950547 16.484552,6.0321266 15.675129,6.0321266 c -1.032717,0 -1.883352,0.1854523 -2.553215,0.5578447 -0.655913,0.372254 -0.98517,0.8460916 -0.98517,1.4214436 0,0.5414792 0.272815,1.0495355 0.817093,1.5233385 0.544275,0.4738026 1.946291,1.3367446 4.207097,2.5889976 2.414319,1.342419 4.117377,2.390985 5.108232,3.146807 1.004795,0.755857 1.821505,1.675853 2.449514,2.758846 0.628002,1.082993 0.942253,2.227607 0.942253,3.434674 0,2.120834 -0.929555,3.993314 -2.785656,5.617786 -1.84214,1.613228 -3.99694,2.41915 -6.467082,2.41915 -2.246845,0 -4.145607,-0.647976 -5.694677,-1.945312 -1.5490699,-1.297336 -2.3225722,-3.028063 -2.3225722,-5.194049 0,-2.087031 0.8506345,-3.83094 2.5532182,-5.229825 1.716541,-1.398884 3.824203,-2.245599 6.322256,-2.538897 -0.697774,-0.631749 -1.668763,-1.387225 -2.910816,-2.267155 -1.367648,-0.970199 -2.287914,-1.73045 -2.762402,-2.283243 -0.474491,-0.5640381 -0.711618,-1.1795944 -0.711618,-1.8451814 0,-0.9927581 0.572093,-1.7710351 1.716451,-2.3351077 1.144362,-0.5753173 2.636724,-0.8635642 4.478865,-0.8635642 z m 1.110327,10.3738083 c -4.005262,0.5302 -6.007576,2.75279 -6.007576,6.667322 0,2.01932 0.49495,3.587291 1.485805,4.704157 1.004806,1.116832 2.169696,1.675299 3.495479,1.675299 1.381602,0 2.520072,-0.535632 3.413229,-1.60738 0.893168,-1.082959 1.339187,-2.545264 1.339187,-4.384079 0,-2.662348 -1.242022,-5.013441 -3.726124,-7.055319 z" />
|
|
||||||
|
Before Width: | Height: | Size: 112 KiB |
@@ -1,44 +0,0 @@
|
|||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
||||||
use deltachat::contact::Contact;
|
|
||||||
use deltachat::context::Context;
|
|
||||||
use deltachat::stock_str::StockStrings;
|
|
||||||
use deltachat::Events;
|
|
||||||
use tempfile::tempdir;
|
|
||||||
|
|
||||||
async fn address_book_benchmark(n: u32, read_count: u32) {
|
|
||||||
let dir = tempdir().unwrap();
|
|
||||||
let dbfile = dir.path().join("db.sqlite");
|
|
||||||
let id = 100;
|
|
||||||
let context = Context::new(&dbfile, id, Events::new(), StockStrings::new())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let book = (0..n)
|
|
||||||
.map(|i| format!("Name {}\naddr{}@example.org\n", i, i))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("");
|
|
||||||
|
|
||||||
Contact::add_address_book(&context, &book).await.unwrap();
|
|
||||||
|
|
||||||
let query: Option<&str> = None;
|
|
||||||
for _ in 0..read_count {
|
|
||||||
Contact::get_all(&context, 0, query).await.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn criterion_benchmark(c: &mut Criterion) {
|
|
||||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
||||||
|
|
||||||
c.bench_function("create 500 contacts", |b| {
|
|
||||||
b.to_async(&rt)
|
|
||||||
.iter(|| async { address_book_benchmark(black_box(500), black_box(0)).await })
|
|
||||||
});
|
|
||||||
|
|
||||||
c.bench_function("create 100 contacts and read it 1000 times", |b| {
|
|
||||||
b.to_async(&rt)
|
|
||||||
.iter(|| async { address_book_benchmark(black_box(100), black_box(1000)).await })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, criterion_benchmark);
|
|
||||||
criterion_main!(benches);
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
||||||
use deltachat::accounts::Accounts;
|
|
||||||
use tempfile::tempdir;
|
|
||||||
|
|
||||||
async fn create_accounts(n: u32) {
|
|
||||||
let dir = tempdir().unwrap();
|
|
||||||
let p: PathBuf = dir.path().join("accounts");
|
|
||||||
|
|
||||||
let mut accounts = Accounts::new(p.clone()).await.unwrap();
|
|
||||||
|
|
||||||
for expected_id in 2..n {
|
|
||||||
let id = accounts.add_account().await.unwrap();
|
|
||||||
assert_eq!(id, expected_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn criterion_benchmark(c: &mut Criterion) {
|
|
||||||
c.bench_function("create 1 account", |b| {
|
|
||||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
||||||
b.to_async(&rt).iter(|| create_accounts(black_box(1)))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, criterion_benchmark);
|
|
||||||
criterion_main!(benches);
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
||||||
use deltachat::chat::{self, ChatId};
|
|
||||||
use deltachat::chatlist::Chatlist;
|
|
||||||
use deltachat::context::Context;
|
|
||||||
use deltachat::stock_str::StockStrings;
|
|
||||||
use deltachat::Events;
|
|
||||||
|
|
||||||
async fn get_chat_msgs_benchmark(dbfile: &Path, chats: &[ChatId]) {
|
|
||||||
let id = 100;
|
|
||||||
let context = Context::new(dbfile, id, Events::new(), StockStrings::new())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
for c in chats.iter().take(10) {
|
|
||||||
black_box(chat::get_chat_msgs(&context, *c, 0).await.ok());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn criterion_benchmark(c: &mut Criterion) {
|
|
||||||
// To enable this benchmark, set `DELTACHAT_BENCHMARK_DATABASE` to some large database with many
|
|
||||||
// messages, such as your primary account.
|
|
||||||
if let Ok(path) = std::env::var("DELTACHAT_BENCHMARK_DATABASE") {
|
|
||||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
||||||
|
|
||||||
let chats: Vec<_> = rt.block_on(async {
|
|
||||||
let context = Context::new(Path::new(&path), 100, Events::new(), StockStrings::new())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let chatlist = Chatlist::try_load(&context, 0, None, None).await.unwrap();
|
|
||||||
let len = chatlist.len();
|
|
||||||
(0..len).map(|i| chatlist.get_chat_id(i).unwrap()).collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
c.bench_function("chat::get_chat_msgs (load messages from 10 chats)", |b| {
|
|
||||||
b.to_async(&rt)
|
|
||||||
.iter(|| get_chat_msgs_benchmark(black_box(path.as_ref()), black_box(&chats)))
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
println!("env var not set: DELTACHAT_BENCHMARK_DATABASE");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, criterion_benchmark);
|
|
||||||
criterion_main!(benches);
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
||||||
use deltachat::chatlist::Chatlist;
|
|
||||||
use deltachat::context::Context;
|
|
||||||
use deltachat::stock_str::StockStrings;
|
|
||||||
use deltachat::Events;
|
|
||||||
|
|
||||||
async fn get_chat_list_benchmark(context: &Context) {
|
|
||||||
Chatlist::try_load(context, 0, None, None).await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn criterion_benchmark(c: &mut Criterion) {
|
|
||||||
// To enable this benchmark, set `DELTACHAT_BENCHMARK_DATABASE` to some large database with many
|
|
||||||
// messages, such as your primary account.
|
|
||||||
if let Ok(path) = std::env::var("DELTACHAT_BENCHMARK_DATABASE") {
|
|
||||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
||||||
let context = rt.block_on(async {
|
|
||||||
Context::new(Path::new(&path), 100, Events::new(), StockStrings::new())
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
c.bench_function("chatlist:try_load (Get Chatlist)", |b| {
|
|
||||||
b.to_async(&rt)
|
|
||||||
.iter(|| get_chat_list_benchmark(black_box(&context)))
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
println!("env var not set: DELTACHAT_BENCHMARK_DATABASE");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, criterion_benchmark);
|
|
||||||
criterion_main!(benches);
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
||||||
use deltachat::{
|
|
||||||
config::Config,
|
|
||||||
context::Context,
|
|
||||||
imex::{imex, ImexMode},
|
|
||||||
receive_imf::receive_imf,
|
|
||||||
stock_str::StockStrings,
|
|
||||||
Events,
|
|
||||||
};
|
|
||||||
use tempfile::tempdir;
|
|
||||||
|
|
||||||
async fn recv_all_emails(context: Context) -> Context {
|
|
||||||
for i in 0..100 {
|
|
||||||
let imf_raw = format!(
|
|
||||||
"Subject: Benchmark
|
|
||||||
Message-ID: Mr.OssSYnOFkhR.{i}@testrun.org
|
|
||||||
Date: Sat, 07 Dec 2019 19:00:27 +0000
|
|
||||||
To: alice@example.com
|
|
||||||
From: sender@testrun.org
|
|
||||||
Chat-Version: 1.0
|
|
||||||
Chat-Disposition-Notification-To: sender@testrun.org
|
|
||||||
Chat-User-Avatar: 0
|
|
||||||
In-Reply-To: Mr.OssSYnOFkhR.{i_dec}@testrun.org
|
|
||||||
MIME-Version: 1.0
|
|
||||||
|
|
||||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
|
||||||
|
|
||||||
Hello {i}",
|
|
||||||
i = i,
|
|
||||||
i_dec = i - 1,
|
|
||||||
);
|
|
||||||
receive_imf(&context, black_box(imf_raw.as_bytes()), false)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
context
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Receive 100 emails that remove charlie@example.com and add
|
|
||||||
/// him back
|
|
||||||
async fn recv_groupmembership_emails(context: Context) -> Context {
|
|
||||||
for i in 0..50 {
|
|
||||||
let imf_raw = format!(
|
|
||||||
"Subject: Benchmark
|
|
||||||
Message-ID: Gr.OssSYnOFkhR.{i}@testrun.org
|
|
||||||
Date: Sat, 07 Dec 2019 19:00:27 +0000
|
|
||||||
To: alice@example.com, b@example.com, c@example.com, d@example.com, e@example.com, f@example.com
|
|
||||||
From: sender@testrun.org
|
|
||||||
Chat-Version: 1.0
|
|
||||||
Chat-Disposition-Notification-To: sender@testrun.org
|
|
||||||
Chat-User-Avatar: 0
|
|
||||||
Chat-Group-Member-Added: charlie@example.com
|
|
||||||
In-Reply-To: Gr.OssSYnOFkhR.{i_dec}@testrun.org
|
|
||||||
MIME-Version: 1.0
|
|
||||||
|
|
||||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
|
||||||
|
|
||||||
Hello {i}",
|
|
||||||
i = i,
|
|
||||||
i_dec = i - 1,
|
|
||||||
);
|
|
||||||
receive_imf(&context, black_box(imf_raw.as_bytes()), false)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let imf_raw = format!(
|
|
||||||
"Subject: Benchmark
|
|
||||||
Message-ID: Gr.OssSYnOFkhR.{i}@testrun.org
|
|
||||||
Date: Sat, 07 Dec 2019 19:00:27 +0000
|
|
||||||
To: alice@example.com, b@example.com, c@example.com, d@example.com, e@example.com, f@example.com
|
|
||||||
From: sender@testrun.org
|
|
||||||
Chat-Version: 1.0
|
|
||||||
Chat-Disposition-Notification-To: sender@testrun.org
|
|
||||||
Chat-User-Avatar: 0
|
|
||||||
Chat-Group-Member-Removed: charlie@example.com
|
|
||||||
In-Reply-To: Gr.OssSYnOFkhR.{i_dec}@testrun.org
|
|
||||||
MIME-Version: 1.0
|
|
||||||
|
|
||||||
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
|
|
||||||
|
|
||||||
Hello {i}",
|
|
||||||
i = i,
|
|
||||||
i_dec = i - 1,
|
|
||||||
);
|
|
||||||
receive_imf(&context, black_box(imf_raw.as_bytes()), false)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
context
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_context() -> Context {
|
|
||||||
let dir = tempdir().unwrap();
|
|
||||||
let dbfile = dir.path().join("db.sqlite");
|
|
||||||
let id = 100;
|
|
||||||
let context = Context::new(dbfile.as_path(), id, Events::new(), StockStrings::new())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let backup: PathBuf = std::env::current_dir()
|
|
||||||
.unwrap()
|
|
||||||
.join("delta-chat-backup.tar");
|
|
||||||
|
|
||||||
if backup.exists() {
|
|
||||||
println!("Importing backup");
|
|
||||||
imex(&context, ImexMode::ImportBackup, backup.as_path(), None)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let addr = "alice@example.com";
|
|
||||||
context.set_config(Config::Addr, Some(addr)).await.unwrap();
|
|
||||||
context
|
|
||||||
.set_config(Config::ConfiguredAddr, Some(addr))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
context
|
|
||||||
.set_config(Config::Configured, Some("1"))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
context
|
|
||||||
}
|
|
||||||
|
|
||||||
fn criterion_benchmark(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("Receive messages");
|
|
||||||
group.bench_function("Receive 100 simple text msgs", |b| {
|
|
||||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
||||||
let context = rt.block_on(create_context());
|
|
||||||
|
|
||||||
b.to_async(&rt).iter(|| {
|
|
||||||
let ctx = context.clone();
|
|
||||||
async move {
|
|
||||||
recv_all_emails(black_box(ctx)).await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
group.bench_function(
|
|
||||||
"Receive 100 Chat-Group-Member-{Added|Removed} messages",
|
|
||||||
|b| {
|
|
||||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
||||||
let context = rt.block_on(create_context());
|
|
||||||
|
|
||||||
b.to_async(&rt).iter(|| {
|
|
||||||
let ctx = context.clone();
|
|
||||||
async move {
|
|
||||||
recv_groupmembership_emails(black_box(ctx)).await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, criterion_benchmark);
|
|
||||||
criterion_main!(benches);
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
||||||
use deltachat::context::Context;
|
|
||||||
use deltachat::stock_str::StockStrings;
|
|
||||||
use deltachat::Events;
|
|
||||||
|
|
||||||
async fn search_benchmark(dbfile: impl AsRef<Path>) {
|
|
||||||
let id = 100;
|
|
||||||
let context = Context::new(dbfile.as_ref(), id, Events::new(), StockStrings::new())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
for _ in 0..10u32 {
|
|
||||||
context.search_msgs(None, "hello").await.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn criterion_benchmark(c: &mut Criterion) {
|
|
||||||
// To enable this benchmark, set `DELTACHAT_BENCHMARK_DATABASE` to some large database with many
|
|
||||||
// messages, such as your primary account.
|
|
||||||
if let Ok(path) = std::env::var("DELTACHAT_BENCHMARK_DATABASE") {
|
|
||||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
||||||
|
|
||||||
c.bench_function("search hello", |b| {
|
|
||||||
b.to_async(&rt).iter(|| search_benchmark(black_box(&path)))
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
println!("env var not set: DELTACHAT_BENCHMARK_DATABASE");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, criterion_benchmark);
|
|
||||||
criterion_main!(benches);
|
|
||||||
52
ci_scripts/README.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
# Continuous Integration Scripts for Delta Chat
|
||||||
|
|
||||||
|
Continuous Integration is run through CircleCI
|
||||||
|
but is largely independent of it.
|
||||||
|
|
||||||
|
|
||||||
|
## Generating docker containers for performing build step work
|
||||||
|
|
||||||
|
All tests, docs and wheel building is run in docker containers:
|
||||||
|
|
||||||
|
- **coredeps/Dockerfile** specifies an image that contains all
|
||||||
|
of Delta Chat's core dependencies as linkable libraries.
|
||||||
|
It also serves to run python tests and build wheels
|
||||||
|
(binary packages for Python).
|
||||||
|
|
||||||
|
- **doxygen/Dockerfile** specifies an image that contains
|
||||||
|
the doxygen tool which is used to generate C-docs.
|
||||||
|
|
||||||
|
To run tests locally you can pull existing images from "docker.io",
|
||||||
|
the hub for sharing Docker images::
|
||||||
|
|
||||||
|
docker pull deltachat/coredeps
|
||||||
|
docker pull deltachat/doxygen
|
||||||
|
|
||||||
|
or you can build the docker images yourself locally
|
||||||
|
to avoid the relatively large download::
|
||||||
|
|
||||||
|
cd ci_scripts # where all CI things are
|
||||||
|
docker build -t deltachat/coredeps docker-coredeps
|
||||||
|
docker build -t deltachat/doxygen docker-doxygen
|
||||||
|
|
||||||
|
## ci_run.sh (main entrypoint called by circle-ci)
|
||||||
|
|
||||||
|
Once you have the docker images available
|
||||||
|
you can run python testing, documentation generation
|
||||||
|
and building binary wheels::
|
||||||
|
|
||||||
|
sh DOCS=1 TESTS=1 ci_scripts/ci_run.sh
|
||||||
|
|
||||||
|
## ci_upload.sh (uploading artifacts on success)
|
||||||
|
|
||||||
|
- python docs to `https://py.delta.chat/_unofficial_unreleased_docs/<BRANCH>`
|
||||||
|
|
||||||
|
- doxygen docs to `https://c.delta.chat/_unofficial_unreleased_docs/<BRANCH>`
|
||||||
|
|
||||||
|
- python wheels to `https://m.devpi.net/dc/<BRANCH>`
|
||||||
|
so that you install fully self-contained wheels like this:
|
||||||
|
`pip install -U -i https://m.devpi.net/dc/<BRANCH> deltachat`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
51
ci_scripts/ci_upload.sh
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ -z "$DEVPI_LOGIN" ] ; then
|
||||||
|
echo "required: password for 'dc' user on https://m.devpi/net/dc index"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -xe
|
||||||
|
|
||||||
|
PYDOCDIR=${1:?directory with python docs}
|
||||||
|
WHEELHOUSEDIR=${2:?directory with pre-built wheels}
|
||||||
|
DOXYDOCDIR=${3:?directory where doxygen docs to be found}
|
||||||
|
|
||||||
|
export BRANCH=${CIRCLE_BRANCH:?specify branch for uploading purposes}
|
||||||
|
|
||||||
|
|
||||||
|
# python docs to py.delta.chat
|
||||||
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@py.delta.chat mkdir -p build/${BRANCH}
|
||||||
|
rsync -avz \
|
||||||
|
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
|
||||||
|
"$PYDOCDIR/html/" \
|
||||||
|
delta@py.delta.chat:build/${BRANCH}
|
||||||
|
|
||||||
|
# C docs to c.delta.chat
|
||||||
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@c.delta.chat mkdir -p build-c/${BRANCH}
|
||||||
|
rsync -avz \
|
||||||
|
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
|
||||||
|
"$DOXYDOCDIR/html/" \
|
||||||
|
delta@c.delta.chat:build-c/${BRANCH}
|
||||||
|
|
||||||
|
echo -----------------------
|
||||||
|
echo upload wheels
|
||||||
|
echo -----------------------
|
||||||
|
|
||||||
|
# Bundle external shared libraries into the wheels
|
||||||
|
pushd $WHEELHOUSEDIR
|
||||||
|
|
||||||
|
pip3 install devpi-client
|
||||||
|
devpi use https://m.devpi.net
|
||||||
|
devpi login dc --password $DEVPI_LOGIN
|
||||||
|
|
||||||
|
N_BRANCH=${BRANCH//[\/]}
|
||||||
|
|
||||||
|
devpi use dc/$N_BRANCH || {
|
||||||
|
devpi index -c $N_BRANCH
|
||||||
|
devpi use dc/$N_BRANCH
|
||||||
|
}
|
||||||
|
devpi index $N_BRANCH bases=/root/pypi
|
||||||
|
devpi upload deltachat*.whl
|
||||||
|
|
||||||
|
popd
|
||||||
24
ci_scripts/docker-coredeps/Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
FROM quay.io/pypa/manylinux1_x86_64
|
||||||
|
|
||||||
|
# Configure ld.so/ldconfig and pkg-config
|
||||||
|
RUN echo /usr/local/lib64 > /etc/ld.so.conf.d/local.conf && \
|
||||||
|
echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf
|
||||||
|
ENV PKG_CONFIG_PATH /usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig
|
||||||
|
|
||||||
|
# Install a recent Perl, needed to install OpenSSL
|
||||||
|
ADD deps/build_perl.sh /builder/build_perl.sh
|
||||||
|
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1
|
||||||
|
|
||||||
|
# Install OpenSSL
|
||||||
|
ADD deps/build_openssl.sh /builder/build_openssl.sh
|
||||||
|
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_openssl.sh && cd .. && rm -r tmp1
|
||||||
|
|
||||||
|
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
|
||||||
|
|
||||||
|
# Install python tools (auditwheels,tox, ...)
|
||||||
|
ADD deps/build_python.sh /builder/build_python.sh
|
||||||
|
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_python.sh && cd .. && rm -r tmp1
|
||||||
|
|
||||||
|
# Install Rust nightly
|
||||||
|
ADD deps/build_rust.sh /builder/build_rust.sh
|
||||||
|
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_rust.sh && cd .. && rm -r tmp1
|
||||||
21
ci_scripts/docker-coredeps/deps/build_openssl.sh
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e -x
|
||||||
|
|
||||||
|
OPENSSL_VERSION=1.1.1a
|
||||||
|
OPENSSL_SHA256=fc20130f8b7cbd2fb918b2f14e2f429e109c31ddd0fb38fc5d71d9ffed3f9f41
|
||||||
|
|
||||||
|
curl -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz
|
||||||
|
echo "${OPENSSL_SHA256} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c -
|
||||||
|
tar xzf openssl-${OPENSSL_VERSION}.tar.gz
|
||||||
|
cd openssl-${OPENSSL_VERSION}
|
||||||
|
./config shared no-ssl2 no-ssl3 -fPIC --prefix=/usr/local
|
||||||
|
|
||||||
|
sed -i "s/^SHLIB_MAJOR=.*/SHLIB_MAJOR=200/" Makefile && \
|
||||||
|
sed -i "s/^SHLIB_MINOR=.*/SHLIB_MINOR=0.0/" Makefile && \
|
||||||
|
sed -i "s/^SHLIB_VERSION_NUMBER=.*/SHLIB_VERSION_NUMBER=200.0.0/" Makefile
|
||||||
|
|
||||||
|
make depend
|
||||||
|
make
|
||||||
|
make install_sw install_ssldirs
|
||||||
|
ldconfig -v | grep ssl
|
||||||
12
ci_scripts/docker-coredeps/deps/build_perl.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
PERL_VERSION=5.30.0
|
||||||
|
# PERL_SHA256=7e929f64d4cb0e9d1159d4a59fc89394e27fa1f7004d0836ca0d514685406ea8
|
||||||
|
curl -O https://www.cpan.org/src/5.0/perl-${PERL_VERSION}.tar.gz
|
||||||
|
# echo "${PERL_SHA256} perl-${PERL_VERSION}.tar.gz" | sha256sum -c -
|
||||||
|
tar -xzf perl-${PERL_VERSION}.tar.gz
|
||||||
|
cd perl-${PERL_VERSION}
|
||||||
|
|
||||||
|
./Configure -de
|
||||||
|
make
|
||||||
|
make install
|
||||||
14
ci_scripts/docker-coredeps/deps/build_python.sh
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -x -e
|
||||||
|
|
||||||
|
# we use the python3.5 environment as the base environment
|
||||||
|
/opt/python/cp35-cp35m/bin/pip install tox devpi-client auditwheel
|
||||||
|
|
||||||
|
pushd /usr/bin
|
||||||
|
|
||||||
|
ln -s /opt/_internal/cpython-3.5.*/bin/tox
|
||||||
|
ln -s /opt/_internal/cpython-3.5.*/bin/devpi
|
||||||
|
ln -s /opt/_internal/cpython-3.5.*/bin/auditwheel
|
||||||
|
|
||||||
|
popd
|
||||||
8
ci_scripts/docker-coredeps/deps/build_rust.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e -x
|
||||||
|
|
||||||
|
# Install Rust
|
||||||
|
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-09-12 -y
|
||||||
|
export PATH=/root/.cargo/bin:$PATH
|
||||||
|
rustc --version
|
||||||
49
ci_scripts/docker-coredeps/deps/run_all.sh
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Build the Delta Chat C/Rust library
|
||||||
|
#
|
||||||
|
set -e -x
|
||||||
|
|
||||||
|
# perform clean build of core and install
|
||||||
|
export TOXWORKDIR=.docker-tox
|
||||||
|
|
||||||
|
# build core library
|
||||||
|
|
||||||
|
cargo build --release -p deltachat_ffi
|
||||||
|
|
||||||
|
# configure access to a base python and
|
||||||
|
# to several python interpreters needed by tox below
|
||||||
|
export PATH=$PATH:/opt/python/cp35-cp35m/bin
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
|
pushd /bin
|
||||||
|
ln -s /opt/python/cp27-cp27m/bin/python2.7
|
||||||
|
ln -s /opt/python/cp36-cp36m/bin/python3.6
|
||||||
|
ln -s /opt/python/cp37-cp37m/bin/python3.7
|
||||||
|
popd
|
||||||
|
|
||||||
|
#
|
||||||
|
# run python tests
|
||||||
|
#
|
||||||
|
|
||||||
|
if [ -n "$TESTS" ]; then
|
||||||
|
|
||||||
|
echo ----------------
|
||||||
|
echo run python tests
|
||||||
|
echo ----------------
|
||||||
|
|
||||||
|
pushd python
|
||||||
|
# first run all tests ...
|
||||||
|
rm -rf tests/__pycache__
|
||||||
|
rm -rf src/deltachat/__pycache__
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
|
tox --workdir "$TOXWORKDIR" -e py27,py35,py36,py37
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ -n "$DOCS" ]; then
|
||||||
|
echo -----------------------
|
||||||
|
echo generating python docs
|
||||||
|
echo -----------------------
|
||||||
|
(cd python && tox --workdir "$TOXWORKDIR" -e doc)
|
||||||
|
fi
|
||||||
5
ci_scripts/docker-doxygen/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FROM debian:stable
|
||||||
|
|
||||||
|
# this is tagged as deltachat/doxygen
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y doxygen
|
||||||
7
ci_scripts/run-doxygen.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
cd deltachat-ffi
|
||||||
|
doxygen
|
||||||
|
|
||||||
50
ci_scripts/run-python.sh
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Build the Delta Chat C/Rust library typically run in a docker
|
||||||
|
# container that contains all library deps but should also work
|
||||||
|
# outside if you have the dependencies installed on your system.
|
||||||
|
|
||||||
|
set -e -x
|
||||||
|
|
||||||
|
# Perform clean build of core and install.
|
||||||
|
export TOXWORKDIR=.docker-tox
|
||||||
|
|
||||||
|
# install core lib
|
||||||
|
|
||||||
|
export PATH=/root/.cargo/bin:$PATH
|
||||||
|
cargo build --release -p deltachat_ffi
|
||||||
|
|
||||||
|
# Statically link against libdeltachat.a.
|
||||||
|
export DCC_RS_DEV=$(pwd)
|
||||||
|
|
||||||
|
# Configure access to a base python and to several python interpreters
|
||||||
|
# needed by tox below.
|
||||||
|
export PATH=$PATH:/opt/python/cp35-cp35m/bin
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
|
pushd python
|
||||||
|
# prepare a clean tox run
|
||||||
|
rm -rf tests/__pycache__
|
||||||
|
rm -rf src/deltachat/__pycache__
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
|
# run tox. The circle-ci project env-var-setting DCC_PY_LIVECONFIG
|
||||||
|
# allows running of "liveconfig" tests but for speed reasons
|
||||||
|
# we run them only for the highest python version we support
|
||||||
|
|
||||||
|
# we split out qr-tests run to minimize likelyness of flaky tests
|
||||||
|
# (some qr tests are pretty heavy in terms of send/received
|
||||||
|
# messages and rust's imap code likely has concurrency problems)
|
||||||
|
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "not qr"
|
||||||
|
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "qr"
|
||||||
|
unset DCC_PY_LIVECONFIG
|
||||||
|
#tox --workdir "$TOXWORKDIR" -p4 -e lint,py35,py36,doc
|
||||||
|
#tox --workdir "$TOXWORKDIR" -e auditwheels
|
||||||
|
popd
|
||||||
|
|
||||||
|
# if [ -n "$DOCS" ]; then
|
||||||
|
# echo -----------------------
|
||||||
|
# echo generating python docs
|
||||||
|
# echo -----------------------
|
||||||
|
# (cd python && tox --workdir "$TOXWORKDIR" -e doc)
|
||||||
|
# fi
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
shopt -s huponexit
|
|
||||||
|
|
||||||
#export RUST_TEST_THREADS=1
|
export RUST_TEST_THREADS=1
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
export RUSTFLAGS='--deny warnings'
|
export RUSTFLAGS='--deny warnings'
|
||||||
export OPT="--target=$TARGET"
|
export OPT="--target=$TARGET"
|
||||||
@@ -38,9 +37,7 @@ else
|
|||||||
export OPT_RELEASE_IGNORED="${OPT_RELEASE} -- --ignored"
|
export OPT_RELEASE_IGNORED="${OPT_RELEASE} -- --ignored"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run all the test configurations
|
# Run all the test configurations:
|
||||||
# RUSTC_WRAPPER=SCCACHE seems to destroy parallelism / prolong the test
|
|
||||||
unset RUSTC_WRAPPER
|
|
||||||
$CARGO_CMD $CARGO_SUBCMD $OPT
|
$CARGO_CMD $CARGO_SUBCMD $OPT
|
||||||
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE
|
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE
|
||||||
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE_IGNORED
|
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE_IGNORED
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# Examples:
|
|
||||||
#
|
|
||||||
# Original server that doesn't use SSL:
|
|
||||||
# ./proxy.py 8080 imap.nauta.cu 143
|
|
||||||
# ./proxy.py 8081 smtp.nauta.cu 25
|
|
||||||
#
|
|
||||||
# Original server that uses SSL:
|
|
||||||
# ./proxy.py 8080 testrun.org 993 --ssl
|
|
||||||
# ./proxy.py 8081 testrun.org 465 --ssl
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
import argparse
|
|
||||||
import selectors
|
|
||||||
import ssl
|
|
||||||
import socket
|
|
||||||
import socketserver
|
|
||||||
|
|
||||||
|
|
||||||
class Proxy(socketserver.ThreadingTCPServer):
|
|
||||||
allow_reuse_address = True
|
|
||||||
|
|
||||||
def __init__(self, proxy_host, proxy_port, real_host, real_port, use_ssl):
|
|
||||||
self.real_host = real_host
|
|
||||||
self.real_port = real_port
|
|
||||||
self.use_ssl = use_ssl
|
|
||||||
super().__init__((proxy_host, proxy_port), RequestHandler)
|
|
||||||
|
|
||||||
|
|
||||||
class RequestHandler(socketserver.BaseRequestHandler):
|
|
||||||
|
|
||||||
def handle(self):
|
|
||||||
print('{} - {} CONNECTED.'.format(datetime.now(), self.client_address))
|
|
||||||
|
|
||||||
total = 0
|
|
||||||
real_server = (self.server.real_host, self.server.real_port)
|
|
||||||
with socket.create_connection(real_server) as sock:
|
|
||||||
if self.server.use_ssl:
|
|
||||||
context = ssl.create_default_context()
|
|
||||||
sock = context.wrap_socket(
|
|
||||||
sock, server_hostname=real_server[0])
|
|
||||||
|
|
||||||
forward = {self.request: sock, sock: self.request}
|
|
||||||
|
|
||||||
sel = selectors.DefaultSelector()
|
|
||||||
sel.register(self.request, selectors.EVENT_READ,
|
|
||||||
self.client_address)
|
|
||||||
sel.register(sock, selectors.EVENT_READ, real_server)
|
|
||||||
|
|
||||||
active = True
|
|
||||||
while active:
|
|
||||||
events = sel.select()
|
|
||||||
for key, mask in events:
|
|
||||||
print('\n{} - {} wrote:'.format(datetime.now(), key.data))
|
|
||||||
data = key.fileobj.recv(1024)
|
|
||||||
received = len(data)
|
|
||||||
total += received
|
|
||||||
print(data)
|
|
||||||
print('{} Bytes\nTotal: {} Bytes'.format(received, total))
|
|
||||||
if data:
|
|
||||||
forward[key.fileobj].sendall(data)
|
|
||||||
else:
|
|
||||||
print('\nCLOSING CONNECTION.\n\n')
|
|
||||||
forward[key.fileobj].close()
|
|
||||||
key.fileobj.close()
|
|
||||||
active = False
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
p = argparse.ArgumentParser(description='Simple Python Proxy')
|
|
||||||
p.add_argument(
|
|
||||||
"proxy_port", help="the port where the proxy will listen", type=int)
|
|
||||||
p.add_argument('host', help="the real host")
|
|
||||||
p.add_argument('port', help="the port of the real host", type=int)
|
|
||||||
p.add_argument("--ssl", help="use ssl to connect to the real host",
|
|
||||||
action="store_true")
|
|
||||||
args = p.parse_args()
|
|
||||||
|
|
||||||
with Proxy('', args.proxy_port, args.host, args.port, args.ssl) as proxy:
|
|
||||||
proxy.serve_forever()
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deltachat_ffi"
|
name = "deltachat_ffi"
|
||||||
version = "1.107.1"
|
version = "1.0.0-beta.7"
|
||||||
description = "Deltachat FFI"
|
description = "Deltachat FFI"
|
||||||
|
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MPL-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
keywords = ["deltachat", "chat", "openpgp", "email", "encryption"]
|
keywords = ["deltachat", "chat", "openpgp", "email", "encryption"]
|
||||||
categories = ["cryptography", "std", "email"]
|
categories = ["cryptography", "std", "email"]
|
||||||
@@ -15,20 +16,13 @@ crate-type = ["cdylib", "staticlib"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
deltachat = { path = "../", default-features = false }
|
deltachat = { path = "../", default-features = false }
|
||||||
deltachat-jsonrpc = { path = "../deltachat-jsonrpc", optional = true }
|
deltachat-provider-database = "0.2.1"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
human-panic = "1"
|
human-panic = "1.0.1"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2.6"
|
||||||
serde_json = "1.0"
|
|
||||||
tokio = { version = "1", features = ["rt-multi-thread"] }
|
|
||||||
anyhow = "1"
|
|
||||||
thiserror = "1"
|
|
||||||
rand = "0.7"
|
|
||||||
once_cell = "1.17.0"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["vendored"]
|
default = ["vendored", "nightly", "ringbuf"]
|
||||||
vendored = ["deltachat/vendored"]
|
vendored = ["deltachat/vendored"]
|
||||||
nightly = ["deltachat/nightly"]
|
nightly = ["deltachat/nightly"]
|
||||||
jsonrpc = ["deltachat-jsonrpc"]
|
ringbuf = ["deltachat/ringbuf"]
|
||||||
|
|
||||||
|
|||||||
@@ -236,6 +236,12 @@ TAB_SIZE = 4
|
|||||||
|
|
||||||
ALIASES =
|
ALIASES =
|
||||||
|
|
||||||
|
# This tag can be used to specify a number of word-keyword mappings (TCL only).
|
||||||
|
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
||||||
|
# will allow you to use the command class in the itcl::class meaning.
|
||||||
|
|
||||||
|
TCL_SUBST =
|
||||||
|
|
||||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||||
# only. Doxygen will then generate output that is more tailored for C. For
|
# only. Doxygen will then generate output that is more tailored for C. For
|
||||||
# instance, some of the names that are used will be different. The list of all
|
# instance, some of the names that are used will be different. The list of all
|
||||||
@@ -583,7 +589,7 @@ SORT_MEMBERS_CTORS_1ST = NO
|
|||||||
# appear in their defined order.
|
# appear in their defined order.
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
SORT_GROUP_NAMES = YES
|
SORT_GROUP_NAMES = NO
|
||||||
|
|
||||||
# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
|
# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
|
||||||
# fully-qualified names, including namespaces. If set to NO, the class list will
|
# fully-qualified names, including namespaces. If set to NO, the class list will
|
||||||
|
|||||||
@@ -4,16 +4,4 @@ div.fragment {
|
|||||||
background-color: #e0e0e0;
|
background-color: #e0e0e0;
|
||||||
border: 0;
|
border: 0;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
border-radius: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #e0e0e0;
|
|
||||||
padding-left: .5em;
|
|
||||||
padding-right: .5em;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: .5em;
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<doxygenlayout version="1.0">
|
|
||||||
<!-- Generated by doxygen 1.8.20 -->
|
|
||||||
<!-- Navigation index tabs for HTML output -->
|
|
||||||
<navindex>
|
|
||||||
<tab type="mainpage" visible="yes" title=""/>
|
|
||||||
<tab type="classes" visible="yes" title="">
|
|
||||||
<tab type="classlist" visible="no" title="" intro=""/>
|
|
||||||
<tab type="classindex" visible="no" title=""/>
|
|
||||||
<tab type="hierarchy" visible="no" title="" intro=""/>
|
|
||||||
<tab type="classmembers" visible="no" title="" intro=""/>
|
|
||||||
</tab>
|
|
||||||
<tab type="modules" visible="yes" title="Constants" intro="Here is a list of constants:"/>
|
|
||||||
<tab type="pages" visible="yes" title="" intro=""/>
|
|
||||||
<tab type="namespaces" visible="yes" title="">
|
|
||||||
<tab type="namespacelist" visible="yes" title="" intro=""/>
|
|
||||||
<tab type="namespacemembers" visible="yes" title="" intro=""/>
|
|
||||||
</tab>
|
|
||||||
<tab type="interfaces" visible="yes" title="">
|
|
||||||
<tab type="interfacelist" visible="yes" title="" intro=""/>
|
|
||||||
<tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
|
||||||
<tab type="interfacehierarchy" visible="yes" title="" intro=""/>
|
|
||||||
</tab>
|
|
||||||
<tab type="structs" visible="yes" title="">
|
|
||||||
<tab type="structlist" visible="yes" title="" intro=""/>
|
|
||||||
<tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
|
||||||
</tab>
|
|
||||||
<tab type="exceptions" visible="yes" title="">
|
|
||||||
<tab type="exceptionlist" visible="yes" title="" intro=""/>
|
|
||||||
<tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
|
||||||
<tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
|
|
||||||
</tab>
|
|
||||||
<tab type="files" visible="yes" title="">
|
|
||||||
<tab type="filelist" visible="yes" title="" intro=""/>
|
|
||||||
<tab type="globals" visible="yes" title="" intro=""/>
|
|
||||||
</tab>
|
|
||||||
<tab type="examples" visible="yes" title="" intro=""/>
|
|
||||||
</navindex>
|
|
||||||
</doxygenlayout>
|
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
# Delta Chat C Interface
|
# Delta Chat C Interface
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
see `Installing libdeltachat system wide` in [../README.md](../README.md)
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
To generate the C Interface documentation,
|
To generate the C Interface documentation,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::io::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
@@ -18,18 +19,15 @@ fn main() {
|
|||||||
include_str!("deltachat.pc.in"),
|
include_str!("deltachat.pc.in"),
|
||||||
name = "deltachat",
|
name = "deltachat",
|
||||||
description = env::var("CARGO_PKG_DESCRIPTION").unwrap(),
|
description = env::var("CARGO_PKG_DESCRIPTION").unwrap(),
|
||||||
url = env::var("CARGO_PKG_HOMEPAGE").unwrap_or_else(|_| "".to_string()),
|
url = env::var("CARGO_PKG_HOMEPAGE").unwrap_or("".to_string()),
|
||||||
version = env::var("CARGO_PKG_VERSION").unwrap(),
|
version = env::var("CARGO_PKG_VERSION").unwrap(),
|
||||||
libs_priv = libs_priv,
|
libs_priv = libs_priv,
|
||||||
prefix = env::var("PREFIX").unwrap_or_else(|_| "/usr/local".to_string()),
|
prefix = env::var("PREFIX").unwrap_or("/usr/local".to_string()),
|
||||||
libdir = env::var("LIBDIR").unwrap_or_else(|_| "/usr/local/lib".to_string()),
|
|
||||||
includedir = env::var("INCLUDEDIR").unwrap_or_else(|_| "/usr/local/include".to_string()),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
fs::create_dir_all(target_path.join("pkgconfig")).unwrap();
|
fs::create_dir_all(target_path.join("pkgconfig")).unwrap();
|
||||||
fs::write(
|
fs::File::create(target_path.join("pkgconfig").join("deltachat.pc"))
|
||||||
target_path.join("pkgconfig").join("deltachat.pc"),
|
.unwrap()
|
||||||
pkg_config.as_bytes(),
|
.write_all(&pkg_config.as_bytes())
|
||||||
)
|
.unwrap();
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
prefix={prefix}
|
prefix={prefix}
|
||||||
libdir={libdir}
|
libdir=${{prefix}}/lib
|
||||||
includedir={includedir}
|
includedir=${{prefix}}/include
|
||||||
|
|
||||||
Name: {name}
|
Name: {name}
|
||||||
Description: {description}
|
Description: {description}
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
use crate::chat::ChatItem;
|
|
||||||
use crate::constants::DC_MSG_ID_DAYMARKER;
|
|
||||||
use crate::contact::ContactId;
|
|
||||||
use crate::location::Location;
|
|
||||||
use crate::message::MsgId;
|
|
||||||
|
|
||||||
/* * the structure behind dc_array_t */
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum dc_array_t {
|
|
||||||
MsgIds(Vec<MsgId>),
|
|
||||||
ContactIds(Vec<ContactId>),
|
|
||||||
Chat(Vec<ChatItem>),
|
|
||||||
Locations(Vec<Location>),
|
|
||||||
Uint(Vec<u32>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl dc_array_t {
|
|
||||||
pub(crate) fn get_id(&self, index: usize) -> u32 {
|
|
||||||
match self {
|
|
||||||
Self::MsgIds(array) => array[index].to_u32(),
|
|
||||||
Self::ContactIds(array) => array[index].to_u32(),
|
|
||||||
Self::Chat(array) => match array[index] {
|
|
||||||
ChatItem::Message { msg_id } => msg_id.to_u32(),
|
|
||||||
ChatItem::DayMarker { .. } => DC_MSG_ID_DAYMARKER,
|
|
||||||
},
|
|
||||||
Self::Locations(array) => array[index].location_id,
|
|
||||||
Self::Uint(array) => array[index],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_timestamp(&self, index: usize) -> Option<i64> {
|
|
||||||
match self {
|
|
||||||
Self::MsgIds(_) => None,
|
|
||||||
Self::ContactIds(_) => None,
|
|
||||||
Self::Chat(array) => array.get(index).and_then(|item| match item {
|
|
||||||
ChatItem::Message { .. } => None,
|
|
||||||
ChatItem::DayMarker { timestamp } => Some(*timestamp),
|
|
||||||
}),
|
|
||||||
Self::Locations(array) => array.get(index).map(|location| location.timestamp),
|
|
||||||
Self::Uint(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_marker(&self, index: usize) -> Option<&str> {
|
|
||||||
match self {
|
|
||||||
Self::MsgIds(_) => None,
|
|
||||||
Self::ContactIds(_) => None,
|
|
||||||
Self::Chat(_) => None,
|
|
||||||
Self::Locations(array) => array
|
|
||||||
.get(index)
|
|
||||||
.and_then(|location| location.marker.as_deref()),
|
|
||||||
Self::Uint(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_location(&self, index: usize) -> &Location {
|
|
||||||
if let Self::Locations(array) = self {
|
|
||||||
&array[index]
|
|
||||||
} else {
|
|
||||||
panic!("Not an array of locations")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the number of elements in the array.
|
|
||||||
pub(crate) fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Self::MsgIds(array) => array.len(),
|
|
||||||
Self::ContactIds(array) => array.len(),
|
|
||||||
Self::Chat(array) => array.len(),
|
|
||||||
Self::Locations(array) => array.len(),
|
|
||||||
Self::Uint(array) => array.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn search_id(&self, needle: u32) -> Option<usize> {
|
|
||||||
(0..self.len()).find(|i| self.get_id(*i) == needle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<u32>> for dc_array_t {
|
|
||||||
fn from(array: Vec<u32>) -> Self {
|
|
||||||
dc_array_t::Uint(array)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<MsgId>> for dc_array_t {
|
|
||||||
fn from(array: Vec<MsgId>) -> Self {
|
|
||||||
dc_array_t::MsgIds(array)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<ContactId>> for dc_array_t {
|
|
||||||
fn from(array: Vec<ContactId>) -> Self {
|
|
||||||
dc_array_t::ContactIds(array)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<ChatItem>> for dc_array_t {
|
|
||||||
fn from(array: Vec<ChatItem>) -> Self {
|
|
||||||
dc_array_t::Chat(array)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<Location>> for dc_array_t {
|
|
||||||
fn from(array: Vec<Location>) -> Self {
|
|
||||||
dc_array_t::Locations(array)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_dc_array() {
|
|
||||||
let arr: dc_array_t = Vec::<u32>::new().into();
|
|
||||||
assert!(arr.len() == 0);
|
|
||||||
|
|
||||||
let ids: Vec<u32> = (2..1002).collect();
|
|
||||||
let arr: dc_array_t = ids.into();
|
|
||||||
|
|
||||||
assert_eq!(arr.len(), 1000);
|
|
||||||
|
|
||||||
for i in 0..1000 {
|
|
||||||
assert_eq!(arr.get_id(i), (i + 2) as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(arr.search_id(10), Some(8));
|
|
||||||
assert_eq!(arr.search_id(1), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn test_dc_array_out_of_bounds() {
|
|
||||||
let ids: Vec<u32> = (2..1002).collect();
|
|
||||||
let arr: dc_array_t = ids.into();
|
|
||||||
arr.get_id(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
//! # Legacy generic return values for C API.
|
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use anyhow::Error;
|
|
||||||
|
|
||||||
use crate::message::MessageState;
|
|
||||||
use crate::qr::Qr;
|
|
||||||
use crate::summary::{Summary, SummaryPrefix};
|
|
||||||
|
|
||||||
/// An object containing a set of values.
|
|
||||||
/// The meaning of the values is defined by the function returning the object.
|
|
||||||
/// Lot objects are created
|
|
||||||
/// eg. by chatlist.get_summary() or dc_msg_get_summary().
|
|
||||||
///
|
|
||||||
/// *Lot* is used in the meaning *heap* here.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Lot {
|
|
||||||
Summary(Summary),
|
|
||||||
Qr(Qr),
|
|
||||||
Error(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Meaning {
|
|
||||||
None = 0,
|
|
||||||
Text1Draft = 1,
|
|
||||||
Text1Username = 2,
|
|
||||||
Text1Self = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Meaning {
|
|
||||||
fn default() -> Self {
|
|
||||||
Meaning::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Lot {
|
|
||||||
pub fn get_text1(&self) -> Option<&str> {
|
|
||||||
match self {
|
|
||||||
Self::Summary(summary) => match &summary.prefix {
|
|
||||||
None => None,
|
|
||||||
Some(SummaryPrefix::Draft(text)) => Some(text),
|
|
||||||
Some(SummaryPrefix::Username(username)) => Some(username),
|
|
||||||
Some(SummaryPrefix::Me(text)) => Some(text),
|
|
||||||
},
|
|
||||||
Self::Qr(qr) => match qr {
|
|
||||||
Qr::AskVerifyContact { .. } => None,
|
|
||||||
Qr::AskVerifyGroup { grpname, .. } => Some(grpname),
|
|
||||||
Qr::FprOk { .. } => None,
|
|
||||||
Qr::FprMismatch { .. } => None,
|
|
||||||
Qr::FprWithoutAddr { fingerprint, .. } => Some(fingerprint),
|
|
||||||
Qr::Account { domain } => Some(domain),
|
|
||||||
Qr::WebrtcInstance { domain, .. } => Some(domain),
|
|
||||||
Qr::Addr { draft, .. } => draft.as_deref(),
|
|
||||||
Qr::Url { url } => Some(url),
|
|
||||||
Qr::Text { text } => Some(text),
|
|
||||||
Qr::WithdrawVerifyContact { .. } => None,
|
|
||||||
Qr::WithdrawVerifyGroup { grpname, .. } => Some(grpname),
|
|
||||||
Qr::ReviveVerifyContact { .. } => None,
|
|
||||||
Qr::ReviveVerifyGroup { grpname, .. } => Some(grpname),
|
|
||||||
Qr::Login { address, .. } => Some(address),
|
|
||||||
},
|
|
||||||
Self::Error(err) => Some(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_text2(&self) -> Option<Cow<str>> {
|
|
||||||
match self {
|
|
||||||
Self::Summary(summary) => Some(summary.truncated_text(160)),
|
|
||||||
Self::Qr(_) => None,
|
|
||||||
Self::Error(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_text1_meaning(&self) -> Meaning {
|
|
||||||
match self {
|
|
||||||
Self::Summary(summary) => match &summary.prefix {
|
|
||||||
None => Meaning::None,
|
|
||||||
Some(SummaryPrefix::Draft(_text)) => Meaning::Text1Draft,
|
|
||||||
Some(SummaryPrefix::Username(_username)) => Meaning::Text1Username,
|
|
||||||
Some(SummaryPrefix::Me(_text)) => Meaning::Text1Self,
|
|
||||||
},
|
|
||||||
Self::Qr(qr) => match qr {
|
|
||||||
Qr::Addr {
|
|
||||||
draft: Some(_draft),
|
|
||||||
..
|
|
||||||
} => Meaning::Text1Draft,
|
|
||||||
_ => Meaning::None,
|
|
||||||
},
|
|
||||||
Self::Error(_err) => Meaning::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_state(&self) -> LotState {
|
|
||||||
match self {
|
|
||||||
Self::Summary(summary) => summary.state.into(),
|
|
||||||
Self::Qr(qr) => match qr {
|
|
||||||
Qr::AskVerifyContact { .. } => LotState::QrAskVerifyContact,
|
|
||||||
Qr::AskVerifyGroup { .. } => LotState::QrAskVerifyGroup,
|
|
||||||
Qr::FprOk { .. } => LotState::QrFprOk,
|
|
||||||
Qr::FprMismatch { .. } => LotState::QrFprMismatch,
|
|
||||||
Qr::FprWithoutAddr { .. } => LotState::QrFprWithoutAddr,
|
|
||||||
Qr::Account { .. } => LotState::QrAccount,
|
|
||||||
Qr::WebrtcInstance { .. } => LotState::QrWebrtcInstance,
|
|
||||||
Qr::Addr { .. } => LotState::QrAddr,
|
|
||||||
Qr::Url { .. } => LotState::QrUrl,
|
|
||||||
Qr::Text { .. } => LotState::QrText,
|
|
||||||
Qr::WithdrawVerifyContact { .. } => LotState::QrWithdrawVerifyContact,
|
|
||||||
Qr::WithdrawVerifyGroup { .. } => LotState::QrWithdrawVerifyGroup,
|
|
||||||
Qr::ReviveVerifyContact { .. } => LotState::QrReviveVerifyContact,
|
|
||||||
Qr::ReviveVerifyGroup { .. } => LotState::QrReviveVerifyGroup,
|
|
||||||
Qr::Login { .. } => LotState::QrLogin,
|
|
||||||
},
|
|
||||||
Self::Error(_err) => LotState::QrError,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_id(&self) -> u32 {
|
|
||||||
match self {
|
|
||||||
Self::Summary(_) => Default::default(),
|
|
||||||
Self::Qr(qr) => match qr {
|
|
||||||
Qr::AskVerifyContact { contact_id, .. } => contact_id.to_u32(),
|
|
||||||
Qr::AskVerifyGroup { .. } => Default::default(),
|
|
||||||
Qr::FprOk { contact_id } => contact_id.to_u32(),
|
|
||||||
Qr::FprMismatch { contact_id } => contact_id.unwrap_or_default().to_u32(),
|
|
||||||
Qr::FprWithoutAddr { .. } => Default::default(),
|
|
||||||
Qr::Account { .. } => Default::default(),
|
|
||||||
Qr::WebrtcInstance { .. } => Default::default(),
|
|
||||||
Qr::Addr { contact_id, .. } => contact_id.to_u32(),
|
|
||||||
Qr::Url { .. } => Default::default(),
|
|
||||||
Qr::Text { .. } => Default::default(),
|
|
||||||
Qr::WithdrawVerifyContact { contact_id, .. } => contact_id.to_u32(),
|
|
||||||
Qr::WithdrawVerifyGroup { .. } => Default::default(),
|
|
||||||
Qr::ReviveVerifyContact { contact_id, .. } => contact_id.to_u32(),
|
|
||||||
Qr::ReviveVerifyGroup { .. } => Default::default(),
|
|
||||||
Qr::Login { .. } => Default::default(),
|
|
||||||
},
|
|
||||||
Self::Error(_) => Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_timestamp(&self) -> i64 {
|
|
||||||
match self {
|
|
||||||
Self::Summary(summary) => summary.timestamp,
|
|
||||||
Self::Qr(_) => Default::default(),
|
|
||||||
Self::Error(_) => Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u32)]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum LotState {
|
|
||||||
// Default
|
|
||||||
Undefined = 0,
|
|
||||||
|
|
||||||
// Qr States
|
|
||||||
/// id=contact
|
|
||||||
QrAskVerifyContact = 200,
|
|
||||||
|
|
||||||
/// text1=groupname
|
|
||||||
QrAskVerifyGroup = 202,
|
|
||||||
|
|
||||||
/// id=contact
|
|
||||||
QrFprOk = 210,
|
|
||||||
|
|
||||||
/// id=contact
|
|
||||||
QrFprMismatch = 220,
|
|
||||||
|
|
||||||
/// text1=formatted fingerprint
|
|
||||||
QrFprWithoutAddr = 230,
|
|
||||||
|
|
||||||
/// text1=domain
|
|
||||||
QrAccount = 250,
|
|
||||||
|
|
||||||
/// text1=domain, text2=instance pattern
|
|
||||||
QrWebrtcInstance = 260,
|
|
||||||
|
|
||||||
/// id=contact
|
|
||||||
QrAddr = 320,
|
|
||||||
|
|
||||||
/// text1=text
|
|
||||||
QrText = 330,
|
|
||||||
|
|
||||||
/// text1=URL
|
|
||||||
QrUrl = 332,
|
|
||||||
|
|
||||||
/// text1=error string
|
|
||||||
QrError = 400,
|
|
||||||
|
|
||||||
QrWithdrawVerifyContact = 500,
|
|
||||||
|
|
||||||
/// text1=groupname
|
|
||||||
QrWithdrawVerifyGroup = 502,
|
|
||||||
|
|
||||||
QrReviveVerifyContact = 510,
|
|
||||||
|
|
||||||
/// text1=groupname
|
|
||||||
QrReviveVerifyGroup = 512,
|
|
||||||
|
|
||||||
/// text1=email_address
|
|
||||||
QrLogin = 520,
|
|
||||||
|
|
||||||
// Message States
|
|
||||||
MsgInFresh = 10,
|
|
||||||
MsgInNoticed = 13,
|
|
||||||
MsgInSeen = 16,
|
|
||||||
MsgOutPreparing = 18,
|
|
||||||
MsgOutDraft = 19,
|
|
||||||
MsgOutPending = 20,
|
|
||||||
MsgOutFailed = 24,
|
|
||||||
MsgOutDelivered = 26,
|
|
||||||
MsgOutMdnRcvd = 28,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for LotState {
|
|
||||||
fn default() -> Self {
|
|
||||||
LotState::Undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<MessageState> for LotState {
|
|
||||||
fn from(s: MessageState) -> Self {
|
|
||||||
use MessageState::*;
|
|
||||||
match s {
|
|
||||||
Undefined => LotState::Undefined,
|
|
||||||
InFresh => LotState::MsgInFresh,
|
|
||||||
InNoticed => LotState::MsgInNoticed,
|
|
||||||
InSeen => LotState::MsgInSeen,
|
|
||||||
OutPreparing => LotState::MsgOutPreparing,
|
|
||||||
OutDraft => LotState::MsgOutDraft,
|
|
||||||
OutPending => LotState::MsgOutPending,
|
|
||||||
OutFailed => LotState::MsgOutFailed,
|
|
||||||
OutDelivered => LotState::MsgOutDelivered,
|
|
||||||
OutMdnRcvd => LotState::MsgOutMdnRcvd,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Summary> for Lot {
|
|
||||||
fn from(summary: Summary) -> Self {
|
|
||||||
Lot::Summary(summary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Qr> for Lot {
|
|
||||||
fn from(qr: Qr) -> Self {
|
|
||||||
Lot::Qr(qr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make it easy to convert errors into the final `Lot`.
|
|
||||||
impl From<Error> for Lot {
|
|
||||||
fn from(error: Error) -> Self {
|
|
||||||
Lot::Error(error.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
93
deltachat-ffi/src/providers.rs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
extern crate deltachat_provider_database;
|
||||||
|
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
use deltachat::dc_tools::{to_string_lossy, StrExt};
|
||||||
|
use deltachat_provider_database::StatusState;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub type dc_provider_t = deltachat_provider_database::Provider;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_new_from_domain(
|
||||||
|
domain: *const libc::c_char,
|
||||||
|
) -> *const dc_provider_t {
|
||||||
|
match deltachat_provider_database::get_provider_info(&to_string_lossy(domain)) {
|
||||||
|
Some(provider) => provider,
|
||||||
|
None => ptr::null(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_new_from_email(
|
||||||
|
email: *const libc::c_char,
|
||||||
|
) -> *const dc_provider_t {
|
||||||
|
let email = to_string_lossy(email);
|
||||||
|
let domain = deltachat_provider_database::get_domain_from_email(&email);
|
||||||
|
match deltachat_provider_database::get_provider_info(domain) {
|
||||||
|
Some(provider) => provider,
|
||||||
|
None => ptr::null(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! null_guard {
|
||||||
|
($context:tt) => {
|
||||||
|
if $context.is_null() {
|
||||||
|
return ptr::null_mut() as *mut libc::c_char;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_get_overview_page(
|
||||||
|
provider: *const dc_provider_t,
|
||||||
|
) -> *mut libc::c_char {
|
||||||
|
null_guard!(provider);
|
||||||
|
format!(
|
||||||
|
"{}/{}",
|
||||||
|
deltachat_provider_database::PROVIDER_OVERVIEW_URL,
|
||||||
|
(*provider).overview_page
|
||||||
|
)
|
||||||
|
.strdup()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_get_name(provider: *const dc_provider_t) -> *mut libc::c_char {
|
||||||
|
null_guard!(provider);
|
||||||
|
(*provider).name.strdup()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_get_markdown(
|
||||||
|
provider: *const dc_provider_t,
|
||||||
|
) -> *mut libc::c_char {
|
||||||
|
null_guard!(provider);
|
||||||
|
(*provider).markdown.strdup()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_get_status_date(
|
||||||
|
provider: *const dc_provider_t,
|
||||||
|
) -> *mut libc::c_char {
|
||||||
|
null_guard!(provider);
|
||||||
|
(*provider).status.date.strdup()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_get_status(provider: *const dc_provider_t) -> u32 {
|
||||||
|
if provider.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
match (*provider).status.state {
|
||||||
|
StatusState::OK => 1,
|
||||||
|
StatusState::PREPARATION => 2,
|
||||||
|
StatusState::BROKEN => 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_unref(_provider: *const dc_provider_t) {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO expose general provider overview url?
|
||||||
@@ -1,422 +0,0 @@
|
|||||||
use std::ffi::{CStr, CString};
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
/// Duplicates a string
|
|
||||||
///
|
|
||||||
/// returns an empty string if NULL is given, never returns NULL (exits on errors)
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust,norun
|
|
||||||
/// use crate::string::{dc_strdup, to_string_lossy};
|
|
||||||
/// unsafe {
|
|
||||||
/// let str_a = b"foobar\x00" as *const u8 as *const libc::c_char;
|
|
||||||
/// let str_a_copy = dc_strdup(str_a);
|
|
||||||
/// assert_eq!(to_string_lossy(str_a_copy), "foobar");
|
|
||||||
/// assert_ne!(str_a, str_a_copy);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
unsafe fn dc_strdup(s: *const libc::c_char) -> *mut libc::c_char {
|
|
||||||
let ret: *mut libc::c_char = if !s.is_null() {
|
|
||||||
libc::strdup(s)
|
|
||||||
} else {
|
|
||||||
libc::calloc(1, 1) as *mut libc::c_char
|
|
||||||
};
|
|
||||||
assert!(!ret.is_null());
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error type for the [OsStrExt] trait
|
|
||||||
#[derive(Debug, PartialEq, thiserror::Error)]
|
|
||||||
pub(crate) enum CStringError {
|
|
||||||
/// The string contains an interior null byte
|
|
||||||
#[error("String contains an interior null byte")]
|
|
||||||
InteriorNullByte,
|
|
||||||
/// The string is not valid Unicode
|
|
||||||
#[error("String is not valid unicode")]
|
|
||||||
NotUnicode,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extra convenience methods on [std::ffi::OsStr] to work with `*libc::c_char`.
|
|
||||||
///
|
|
||||||
/// The primary function of this trait is to more easily convert
|
|
||||||
/// [OsStr], [OsString] or [Path] into pointers to C strings. This always
|
|
||||||
/// allocates a new string since it is very common for the source
|
|
||||||
/// string not to have the required terminal null byte.
|
|
||||||
///
|
|
||||||
/// It is implemented for `AsRef<std::ffi::OsStr>>` trait, which
|
|
||||||
/// allows any type which implements this trait to transparently use
|
|
||||||
/// this. This is how the conversion for [Path] works.
|
|
||||||
///
|
|
||||||
/// [OsStr]: std::ffi::OsStr
|
|
||||||
/// [OsString]: std::ffi::OsString
|
|
||||||
/// [Path]: std::path::Path
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use deltachat::tools::{dc_strdup, OsStrExt};
|
|
||||||
/// let path = std::path::Path::new("/some/path");
|
|
||||||
/// let path_c = path.to_c_string().unwrap();
|
|
||||||
/// unsafe {
|
|
||||||
/// let mut c_ptr: *mut libc::c_char = dc_strdup(path_c.as_ptr());
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub(crate) trait OsStrExt {
|
|
||||||
/// Convert a [std::ffi::OsStr] to an [std::ffi::CString]
|
|
||||||
///
|
|
||||||
/// This is useful to convert e.g. a [std::path::Path] to
|
|
||||||
/// [*libc::c_char] by using
|
|
||||||
/// [Path::as_os_str()](std::path::Path::as_os_str) and
|
|
||||||
/// [CStr::as_ptr()](std::ffi::CStr::as_ptr).
|
|
||||||
///
|
|
||||||
/// This returns [CString] and not [&CStr] because not all [OsStr]
|
|
||||||
/// slices end with a null byte, particularly those coming from
|
|
||||||
/// [Path] do not have a null byte and having to handle this as
|
|
||||||
/// the caller would defeat the point of this function.
|
|
||||||
///
|
|
||||||
/// On Windows this requires that the [OsStr] contains valid
|
|
||||||
/// unicode, which should normally be the case for a [Path].
|
|
||||||
///
|
|
||||||
/// [CString]: std::ffi::CString
|
|
||||||
/// [CStr]: std::ffi::CStr
|
|
||||||
/// [OsStr]: std::ffi::OsStr
|
|
||||||
/// [Path]: std::path::Path
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Since a C `*char` is terminated by a NULL byte this conversion
|
|
||||||
/// will fail, when the [OsStr] has an interior null byte. The
|
|
||||||
/// function will return
|
|
||||||
/// `[Err]([CStringError::InteriorNullByte])`. When converting
|
|
||||||
/// from a [Path] it should be safe to
|
|
||||||
/// [`.unwrap()`](std::result::Result::unwrap) this anyway since a
|
|
||||||
/// [Path] should not contain interior null bytes.
|
|
||||||
///
|
|
||||||
/// On windows when the string contains invalid Unicode
|
|
||||||
/// `[Err]([CStringError::NotUnicode])` is returned.
|
|
||||||
fn to_c_string(&self) -> Result<CString, CStringError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<std::ffi::OsStr>> OsStrExt for T {
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
fn to_c_string(&self) -> Result<CString, CStringError> {
|
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
CString::new(self.as_ref().as_bytes()).map_err(|err| {
|
|
||||||
let std::ffi::NulError { .. } = err;
|
|
||||||
CStringError::InteriorNullByte
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
fn to_c_string(&self) -> Result<CString, CStringError> {
|
|
||||||
os_str_to_c_string_unicode(&self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation for os_str_to_c_string on windows.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn os_str_to_c_string_unicode(
|
|
||||||
os_str: &dyn AsRef<std::ffi::OsStr>,
|
|
||||||
) -> Result<CString, CStringError> {
|
|
||||||
match os_str.as_ref().to_str() {
|
|
||||||
Some(val) => CString::new(val.as_bytes()).map_err(|err| {
|
|
||||||
let std::ffi::NulError { .. } = err;
|
|
||||||
CStringError::InteriorNullByte
|
|
||||||
}),
|
|
||||||
None => Err(CStringError::NotUnicode),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience methods/associated functions for working with [CString]
|
|
||||||
trait CStringExt {
|
|
||||||
/// Create a new [CString], best effort
|
|
||||||
///
|
|
||||||
/// Like the [to_string_lossy] this doesn't give up in the face of
|
|
||||||
/// bad input (embedded null bytes in this case) instead it does
|
|
||||||
/// the best it can by stripping the embedded null bytes.
|
|
||||||
fn new_lossy<T: Into<Vec<u8>>>(t: T) -> CString {
|
|
||||||
let mut s = t.into();
|
|
||||||
s.retain(|&c| c != 0);
|
|
||||||
CString::new(s).unwrap_or_default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CStringExt for CString {}
|
|
||||||
|
|
||||||
/// Convenience methods to turn strings into C strings.
|
|
||||||
///
|
|
||||||
/// To interact with (legacy) C APIs we often need to convert from
|
|
||||||
/// Rust strings to raw C strings. This can be clumsy to do correctly
|
|
||||||
/// and the compiler sometimes allows it in an unsafe way. These
|
|
||||||
/// methods make it more succinct and help you get it right.
|
|
||||||
pub(crate) trait Strdup {
|
|
||||||
/// Allocate a new raw C `*char` version of this string.
|
|
||||||
///
|
|
||||||
/// This allocates a new raw C string which must be freed using
|
|
||||||
/// `free`. It takes care of some common pitfalls with using
|
|
||||||
/// [CString.as_ptr].
|
|
||||||
///
|
|
||||||
/// [CString.as_ptr]: std::ffi::CString.as_ptr
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// This function will panic when the original string contains an
|
|
||||||
/// interior null byte as this can not be represented in raw C
|
|
||||||
/// strings.
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Strdup for str {
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char {
|
|
||||||
let tmp = CString::new_lossy(self);
|
|
||||||
dc_strdup(tmp.as_ptr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Strdup for String {
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char {
|
|
||||||
let s: &str = self;
|
|
||||||
s.strdup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Strdup for std::path::Path {
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char {
|
|
||||||
let tmp = self.to_c_string().unwrap_or_else(|_| CString::default());
|
|
||||||
dc_strdup(tmp.as_ptr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Strdup for [u8] {
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char {
|
|
||||||
let tmp = CString::new_lossy(self);
|
|
||||||
dc_strdup(tmp.as_ptr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience methods to turn optional strings into C strings.
|
|
||||||
///
|
|
||||||
/// This is the same as the [Strdup] trait but a different trait name
|
|
||||||
/// to work around the type system not allowing to implement [Strdup]
|
|
||||||
/// for `Option<impl Strdup>` When we already have an [Strdup] impl
|
|
||||||
/// for `AsRef<&str>`.
|
|
||||||
///
|
|
||||||
/// When the [Option] is [Option::Some] this behaves just like
|
|
||||||
/// [Strdup::strdup], when it is [Option::None] a null pointer is
|
|
||||||
/// returned.
|
|
||||||
pub(crate) trait OptStrdup {
|
|
||||||
/// Allocate a new raw C `*char` version of this string, or NULL.
|
|
||||||
///
|
|
||||||
/// See [Strdup::strdup] for details.
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<str>> OptStrdup for Option<T> {
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char {
|
|
||||||
match self {
|
|
||||||
Some(s) => {
|
|
||||||
let tmp = CString::new_lossy(s.as_ref());
|
|
||||||
dc_strdup(tmp.as_ptr())
|
|
||||||
}
|
|
||||||
None => ptr::null_mut(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn to_string_lossy(s: *const libc::c_char) -> String {
|
|
||||||
if s.is_null() {
|
|
||||||
return "".into();
|
|
||||||
}
|
|
||||||
|
|
||||||
let cstr = unsafe { CStr::from_ptr(s) };
|
|
||||||
|
|
||||||
cstr.to_string_lossy().to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn to_opt_string_lossy(s: *const libc::c_char) -> Option<String> {
|
|
||||||
if s.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(to_string_lossy(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert a C `*char` pointer to a [std::path::Path] slice.
|
|
||||||
///
|
|
||||||
/// This converts a `*libc::c_char` pointer to a [Path] slice. This
|
|
||||||
/// essentially has to convert the pointer to [std::ffi::OsStr] to do
|
|
||||||
/// so and thus is the inverse of [OsStrExt::to_c_string]. Just like
|
|
||||||
/// [OsStrExt::to_c_string] requires valid Unicode on Windows, this
|
|
||||||
/// requires that the pointer contains valid UTF-8 on Windows.
|
|
||||||
///
|
|
||||||
/// Because this returns a reference the [Path] slice can not outlive
|
|
||||||
/// the original pointer.
|
|
||||||
///
|
|
||||||
/// [Path]: std::path::Path
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
pub(crate) fn as_path<'a>(s: *const libc::c_char) -> &'a std::path::Path {
|
|
||||||
assert!(!s.is_null(), "cannot be used on null pointers");
|
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
unsafe {
|
|
||||||
let c_str = std::ffi::CStr::from_ptr(s).to_bytes();
|
|
||||||
let os_str = std::ffi::OsStr::from_bytes(c_str);
|
|
||||||
std::path::Path::new(os_str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// as_path() implementation for windows, documented above.
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub(crate) fn as_path<'a>(s: *const libc::c_char) -> &'a std::path::Path {
|
|
||||||
as_path_unicode(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation for as_path() on Windows.
|
|
||||||
//
|
|
||||||
// Having this as a separate function means it can be tested on unix
|
|
||||||
// too.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn as_path_unicode<'a>(s: *const libc::c_char) -> &'a std::path::Path {
|
|
||||||
assert!(!s.is_null(), "cannot be used on null pointers");
|
|
||||||
|
|
||||||
let cstr = unsafe { CStr::from_ptr(s) };
|
|
||||||
let str = cstr.to_str().unwrap_or_else(|err| panic!("{}", err));
|
|
||||||
|
|
||||||
std::path::Path::new(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use libc::{free, strcmp};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_to_c_string_cwd() {
|
|
||||||
let some_dir = std::env::current_dir().unwrap();
|
|
||||||
some_dir.as_os_str().to_c_string().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_to_c_string_unicode() {
|
|
||||||
let some_str = String::from("/some/valid/utf8");
|
|
||||||
let some_dir = std::path::Path::new(&some_str);
|
|
||||||
assert_eq!(
|
|
||||||
some_dir.as_os_str().to_c_string().unwrap(),
|
|
||||||
CString::new("/some/valid/utf8").unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_to_c_string_nul() {
|
|
||||||
let some_str = std::ffi::OsString::from("foo\x00bar");
|
|
||||||
assert_eq!(
|
|
||||||
some_str.to_c_string().err().unwrap(),
|
|
||||||
CStringError::InteriorNullByte
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_path_to_c_string_cwd() {
|
|
||||||
let some_dir = std::env::current_dir().unwrap();
|
|
||||||
some_dir.to_c_string().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_path_to_c_string_unicode() {
|
|
||||||
let some_str = String::from("/some/valid/utf8");
|
|
||||||
let some_dir = std::path::Path::new(&some_str);
|
|
||||||
assert_eq!(
|
|
||||||
some_dir.as_os_str().to_c_string().unwrap(),
|
|
||||||
CString::new("/some/valid/utf8").unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_to_c_string_unicode_fn() {
|
|
||||||
let some_str = std::ffi::OsString::from("foo");
|
|
||||||
assert_eq!(
|
|
||||||
os_str_to_c_string_unicode(&some_str).unwrap(),
|
|
||||||
CString::new("foo").unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_path_to_c_string_unicode_fn() {
|
|
||||||
let some_str = String::from("/some/path");
|
|
||||||
let some_path = std::path::Path::new(&some_str);
|
|
||||||
assert_eq!(
|
|
||||||
os_str_to_c_string_unicode(&some_path).unwrap(),
|
|
||||||
CString::new("/some/path").unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_to_c_string_unicode_fn_nul() {
|
|
||||||
let some_str = std::ffi::OsString::from("fooz\x00bar");
|
|
||||||
assert_eq!(
|
|
||||||
os_str_to_c_string_unicode(&some_str).err().unwrap(),
|
|
||||||
CStringError::InteriorNullByte
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_as_path() {
|
|
||||||
let some_path = CString::new("/some/path").unwrap();
|
|
||||||
let ptr = some_path.as_ptr();
|
|
||||||
assert_eq!(as_path(ptr), std::ffi::OsString::from("/some/path"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_as_path_unicode_fn() {
|
|
||||||
let some_path = CString::new("/some/path").unwrap();
|
|
||||||
let ptr = some_path.as_ptr();
|
|
||||||
assert_eq!(as_path_unicode(ptr), std::ffi::OsString::from("/some/path"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cstring_new_lossy() {
|
|
||||||
assert!(CString::new("hel\x00lo").is_err());
|
|
||||||
assert!(CString::new(String::from("hel\x00o")).is_err());
|
|
||||||
let r = CString::new("hello").unwrap();
|
|
||||||
assert_eq!(CString::new_lossy("hello"), r);
|
|
||||||
assert_eq!(CString::new_lossy("hel\x00lo"), r);
|
|
||||||
assert_eq!(CString::new_lossy(String::from("hello")), r);
|
|
||||||
assert_eq!(CString::new_lossy(String::from("hel\x00lo")), r);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_strdup_str() {
|
|
||||||
unsafe {
|
|
||||||
let s = "hello".strdup();
|
|
||||||
let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char);
|
|
||||||
free(s as *mut libc::c_void);
|
|
||||||
assert_eq!(cmp, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_strdup_string() {
|
|
||||||
unsafe {
|
|
||||||
let s = String::from("hello").strdup();
|
|
||||||
let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char);
|
|
||||||
free(s as *mut libc::c_void);
|
|
||||||
assert_eq!(cmp, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_strdup_opt_string() {
|
|
||||||
unsafe {
|
|
||||||
let s = Some("hello");
|
|
||||||
let c = s.strdup();
|
|
||||||
let cmp = strcmp(c, b"hello\x00" as *const u8 as *const libc::c_char);
|
|
||||||
free(c as *mut libc::c_void);
|
|
||||||
assert_eq!(cmp, 0);
|
|
||||||
|
|
||||||
let s: Option<&str> = None;
|
|
||||||
let c = s.strdup();
|
|
||||||
assert_eq!(c, ptr::null_mut());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
3
deltachat-jsonrpc/.gitignore
vendored
@@ -1,3 +0,0 @@
|
|||||||
accounts/
|
|
||||||
|
|
||||||
.cargo
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "deltachat-jsonrpc"
|
|
||||||
version = "1.107.1"
|
|
||||||
description = "DeltaChat JSON-RPC API"
|
|
||||||
edition = "2021"
|
|
||||||
default-run = "deltachat-jsonrpc-server"
|
|
||||||
license = "MPL-2.0"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "deltachat-jsonrpc-server"
|
|
||||||
path = "src/webserver.rs"
|
|
||||||
required-features = ["webserver"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow = "1"
|
|
||||||
deltachat = { path = ".." }
|
|
||||||
num-traits = "0.2"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
tempfile = "3.3.0"
|
|
||||||
log = "0.4"
|
|
||||||
async-channel = { version = "1.8.0" }
|
|
||||||
futures = { version = "0.3.25" }
|
|
||||||
serde_json = "1.0.91"
|
|
||||||
yerpc = { version = "^0.3.1", features = ["anyhow_expose"] }
|
|
||||||
typescript-type-def = { version = "0.5.5", features = ["json_value"] }
|
|
||||||
tokio = { version = "1.23.1" }
|
|
||||||
sanitize-filename = "0.4"
|
|
||||||
walkdir = "2.3.2"
|
|
||||||
|
|
||||||
# optional dependencies
|
|
||||||
axum = { version = "0.6.1", optional = true, features = ["ws"] }
|
|
||||||
env_logger = { version = "0.10.0", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
tokio = { version = "1.23.1", features = ["full", "rt-multi-thread"] }
|
|
||||||
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
webserver = ["env_logger", "axum", "tokio/full", "yerpc/support-axum"]
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
# deltachat-jsonrpc
|
|
||||||
|
|
||||||
This crate provides a [JSON-RPC 2.0](https://www.jsonrpc.org/specification) interface to DeltaChat.
|
|
||||||
|
|
||||||
The JSON-RPC API is exposed in two fashions:
|
|
||||||
|
|
||||||
* A executable that exposes the JSON-RPC API through a [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) server running on localhost.
|
|
||||||
* The JSON-RPC API can also be called through the [C FFI](../deltachat-ffi). The C FFI needs to be built with the `jsonrpc` feature. It will then expose the functions `dc_jsonrpc_init`, `dc_jsonrpc_request`, `dc_jsonrpc_next_response` and `dc_jsonrpc_unref`. See the docs in the [header file](../deltachat-ffi/deltachat.h) for details.
|
|
||||||
|
|
||||||
We also include a JavaScript and TypeScript client for the JSON-RPC API. The source for this is in the [`typescript`](typescript) folder. The client can easily be used with the WebSocket server to build DeltaChat apps for web browsers or Node.js. See the [examples](typescript/example) for details.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
#### Running the WebSocket server
|
|
||||||
|
|
||||||
From within this folder, you can start the WebSocket server with the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo run --features webserver
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to use the server in a production setup, first build it in release mode:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo build --features webserver --release
|
|
||||||
```
|
|
||||||
You will then find the `deltachat-jsonrpc-server` executable in your `target/release` folder.
|
|
||||||
|
|
||||||
The executable currently does not support any command-line arguments. By default, once started it will accept WebSocket connections on `ws://localhost:20808/ws`. It will store the persistent configuration and databases in a `./accounts` folder relative to the directory from where it is started.
|
|
||||||
|
|
||||||
The server can be configured with environment variables:
|
|
||||||
|
|
||||||
|variable|default|description|
|
|
||||||
|-|-|-|
|
|
||||||
|`DC_PORT`|`20808`|port to listen on|
|
|
||||||
|`DC_ACCOUNTS_PATH`|`./accounts`|path to storage directory|
|
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Using the TypeScript/JavaScript client
|
|
||||||
|
|
||||||
The package includes a JavaScript/TypeScript client which is partially auto-generated through the JSON-RPC library used by this crate ([yerpc](https://github.com/Frando/yerpc/)). Find the source in the [`typescript`](typescript) folder.
|
|
||||||
|
|
||||||
To use it locally, first install the dependencies and compile the TypeScript code to JavaScript:
|
|
||||||
```sh
|
|
||||||
cd typescript
|
|
||||||
npm install
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
The JavaScript client is not yet published on NPM (but will likely be soon). Currently, it is recommended to vendor the bundled build. After running `npm run build` as documented above, there will be a file `dist/deltachat.bundle.js`. This is an ESM module containing all dependencies. Copy this file to your project and import the DeltaChat class.
|
|
||||||
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { DeltaChat } from './deltachat.bundle.js'
|
|
||||||
const dc = new DeltaChat('ws://localhost:20808/ws')
|
|
||||||
const accounts = await dc.rpc.getAllAccounts()
|
|
||||||
console.log('accounts', accounts)
|
|
||||||
```
|
|
||||||
|
|
||||||
A script is included to build autogenerated documentation, which includes all RPC methods:
|
|
||||||
```sh
|
|
||||||
cd typescript
|
|
||||||
npm run docs
|
|
||||||
```
|
|
||||||
Then open the [`typescript/docs`](typescript/docs) folder in a web browser.
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
#### Running the example app
|
|
||||||
|
|
||||||
We include a small demo web application that talks to the WebSocket server. It can be used for testing. Feel invited to expand this.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cd typescript
|
|
||||||
npm run build
|
|
||||||
npm run example:build
|
|
||||||
npm run example:start
|
|
||||||
```
|
|
||||||
Then, open [`http://localhost:8080/example.html`](http://localhost:8080/example.html) in a web browser.
|
|
||||||
|
|
||||||
Run `npm run example:dev` to live-rebuild the example app when files changes.
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
The crate includes both a basic Rust smoke test and more featureful integration tests that use the TypeScript client.
|
|
||||||
|
|
||||||
#### Rust tests
|
|
||||||
|
|
||||||
To run the Rust test, use this command:
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo test
|
|
||||||
```
|
|
||||||
|
|
||||||
#### TypeScript tests
|
|
||||||
|
|
||||||
```
|
|
||||||
cd typescript
|
|
||||||
npm run test
|
|
||||||
```
|
|
||||||
|
|
||||||
This will build the `deltachat-jsonrpc-server` binary and then run a test suite against the WebSocket server.
|
|
||||||
|
|
||||||
The test suite includes some tests that need online connectivity and a way to create test email accounts. To run these tests, talk to DeltaChat developers to get a token for the `testrun.org` service, or use a local instance of [`mailadm`](https://github.com/deltachat/docker-mailadm).
|
|
||||||
|
|
||||||
Then, set the `DCC_NEW_TMP_EMAIL` environment variable to your mailadm token before running the tests.
|
|
||||||
|
|
||||||
```
|
|
||||||
DCC_NEW_TMP_EMAIL=https://testrun.org/new_email?t=yourtoken npm run test
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Test Coverage
|
|
||||||
|
|
||||||
Running `npm run test` will report test coverage. For the coverage to be accurate the online tests need to be run.
|
|
||||||
|
|
||||||
> If you are offline and want to see the coverage results anyway (even though they are inaccurate), you can bypass the errors of the online tests by setting the `COVERAGE_OFFLINE=1` environment variable.
|
|
||||||
|
|
||||||
A summary of the coverage will be reported in the terminal after the test run. Open `coverage/index.html` in a web browser for a detailed report.
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# TODO
|
|
||||||
|
|
||||||
- [ ] different test type to simulate two devices: to test autocrypt_initiate_key_transfer & autocrypt_continue_key_transfer
|
|
||||||
|
|
||||||
## MVP - Websocket server&client
|
|
||||||
|
|
||||||
For kaiOS and other experiments, like a deltachat "web" over network from an android phone.
|
|
||||||
|
|
||||||
- [ ] coverage for a majority of the API
|
|
||||||
- [ ] Blobs served
|
|
||||||
- [ ] Blob upload (for attachments, setting profile-picture, importing backup and so on)
|
|
||||||
- [ ] other way blobs can be addressed when using websocket vs. jsonrpc over dc-node
|
|
||||||
- [ ] Web push API? At least some kind of notification hook closure this lib can accept.
|
|
||||||
|
|
||||||
### Other Ideas for the Websocket server
|
|
||||||
|
|
||||||
- [ ] make sure there can only be one connection at a time to the ws
|
|
||||||
- why? , it could give problems if its commanded from multiple connections
|
|
||||||
- [ ] encrypted connection?
|
|
||||||
- [ ] authenticated connection?
|
|
||||||
- [ ] Look into unit-testing for the proc macros?
|
|
||||||
- [ ] proc macro taking over doc comments to generated typescript file
|
|
||||||
|
|
||||||
## Desktop Apis
|
|
||||||
|
|
||||||
Incomplete todo for desktop api porting, just some remainders for points that might need more work:
|
|
||||||
|
|
||||||
- [ ] manual start/stop io functions in the api for context and accounts, so "not syncing all accounts" can still be done in desktop -> webserver should then not do start io on all accounts by default
|
|
||||||
@@ -1,402 +0,0 @@
|
|||||||
use deltachat::{Event, EventType};
|
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::{json, Value};
|
|
||||||
use typescript_type_def::TypeDef;
|
|
||||||
|
|
||||||
pub fn event_to_json_rpc_notification(event: Event) -> Value {
|
|
||||||
let id: JSONRPCEventType = event.typ.into();
|
|
||||||
json!({
|
|
||||||
"event": id,
|
|
||||||
"contextId": event.id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(tag = "type", rename = "Event")]
|
|
||||||
pub enum JSONRPCEventType {
|
|
||||||
/// The library-user may write an informational string to the log.
|
|
||||||
///
|
|
||||||
/// This event should *not* be reported to the end-user using a popup or something like
|
|
||||||
/// that.
|
|
||||||
Info {
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Emitted when SMTP connection is established and login was successful.
|
|
||||||
SmtpConnected {
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Emitted when IMAP connection is established and login was successful.
|
|
||||||
ImapConnected {
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Emitted when a message was successfully sent to the SMTP server.
|
|
||||||
SmtpMessageSent {
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Emitted when an IMAP message has been marked as deleted
|
|
||||||
ImapMessageDeleted {
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Emitted when an IMAP message has been moved
|
|
||||||
ImapMessageMoved {
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Emitted when an new file in the $BLOBDIR was created
|
|
||||||
NewBlobFile {
|
|
||||||
file: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Emitted when an file in the $BLOBDIR was deleted
|
|
||||||
DeletedBlobFile {
|
|
||||||
file: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// The library-user should write a warning string to the log.
|
|
||||||
///
|
|
||||||
/// This event should *not* be reported to the end-user using a popup or something like
|
|
||||||
/// that.
|
|
||||||
Warning {
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// The library-user should report an error to the end-user.
|
|
||||||
///
|
|
||||||
/// As most things are asynchronous, things may go wrong at any time and the user
|
|
||||||
/// should not be disturbed by a dialog or so. Instead, use a bubble or so.
|
|
||||||
///
|
|
||||||
/// However, for ongoing processes (eg. configure())
|
|
||||||
/// or for functions that are expected to fail (eg. autocryptContinueKeyTransfer())
|
|
||||||
/// it might be better to delay showing these events until the function has really
|
|
||||||
/// failed (returned false). It should be sufficient to report only the *last* error
|
|
||||||
/// in a messasge box then.
|
|
||||||
Error {
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// An action cannot be performed because the user is not in the group.
|
|
||||||
/// Reported eg. after a call to
|
|
||||||
/// setChatName(), setChatProfileImage(),
|
|
||||||
/// addContactToChat(), removeContactFromChat(),
|
|
||||||
/// and messages sending functions.
|
|
||||||
ErrorSelfNotInGroup {
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Messages or chats changed. One or more messages or chats changed for various
|
|
||||||
/// reasons in the database:
|
|
||||||
/// - Messages sent, received or removed
|
|
||||||
/// - Chats created, deleted or archived
|
|
||||||
/// - A draft has been set
|
|
||||||
///
|
|
||||||
/// `chatId` is set if only a single chat is affected by the changes, otherwise 0.
|
|
||||||
/// `msgId` is set if only a single message is affected by the changes, otherwise 0.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
MsgsChanged {
|
|
||||||
chat_id: u32,
|
|
||||||
msg_id: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Reactions for the message changed.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
ReactionsChanged {
|
|
||||||
chat_id: u32,
|
|
||||||
msg_id: u32,
|
|
||||||
contact_id: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// There is a fresh message. Typically, the user will show an notification
|
|
||||||
/// when receiving this message.
|
|
||||||
///
|
|
||||||
/// There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
IncomingMsg {
|
|
||||||
chat_id: u32,
|
|
||||||
msg_id: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Downloading a bunch of messages just finished. This is an experimental
|
|
||||||
/// event to allow the UI to only show one notification per message bunch,
|
|
||||||
/// instead of cluttering the user with many notifications.
|
|
||||||
///
|
|
||||||
/// msg_ids contains the message ids.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
IncomingMsgBunch {
|
|
||||||
msg_ids: Vec<u32>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Messages were seen or noticed.
|
|
||||||
/// chat id is always set.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
MsgsNoticed {
|
|
||||||
chat_id: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to
|
|
||||||
/// DC_STATE_OUT_DELIVERED, see `Message.state`.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
MsgDelivered {
|
|
||||||
chat_id: u32,
|
|
||||||
msg_id: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to
|
|
||||||
/// DC_STATE_OUT_FAILED, see `Message.state`.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
MsgFailed {
|
|
||||||
chat_id: u32,
|
|
||||||
msg_id: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to
|
|
||||||
/// DC_STATE_OUT_MDN_RCVD, see `Message.state`.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
MsgRead {
|
|
||||||
chat_id: u32,
|
|
||||||
msg_id: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Chat changed. The name or the image of a chat group was changed or members were added or removed.
|
|
||||||
/// Or the verify state of a chat has changed.
|
|
||||||
/// See setChatName(), setChatProfileImage(), addContactToChat()
|
|
||||||
/// and removeContactFromChat().
|
|
||||||
///
|
|
||||||
/// This event does not include ephemeral timer modification, which
|
|
||||||
/// is a separate event.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
ChatModified {
|
|
||||||
chat_id: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Chat ephemeral timer changed.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
ChatEphemeralTimerModified {
|
|
||||||
chat_id: u32,
|
|
||||||
timer: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Contact(s) created, renamed, blocked or deleted.
|
|
||||||
///
|
|
||||||
/// @param data1 (int) If set, this is the contact_id of an added contact that should be selected.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
ContactsChanged {
|
|
||||||
contact_id: Option<u32>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Location of one or more contact has changed.
|
|
||||||
///
|
|
||||||
/// @param data1 (u32) contact_id of the contact for which the location has changed.
|
|
||||||
/// If the locations of several contacts have been changed,
|
|
||||||
/// this parameter is set to `None`.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
LocationChanged {
|
|
||||||
contact_id: Option<u32>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Inform about the configuration progress started by configure().
|
|
||||||
ConfigureProgress {
|
|
||||||
/// Progress.
|
|
||||||
///
|
|
||||||
/// 0=error, 1-999=progress in permille, 1000=success and done
|
|
||||||
progress: usize,
|
|
||||||
|
|
||||||
/// Progress comment or error, something to display to the user.
|
|
||||||
comment: Option<String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Inform about the import/export progress started by imex().
|
|
||||||
///
|
|
||||||
/// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done
|
|
||||||
/// @param data2 0
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
ImexProgress {
|
|
||||||
progress: usize,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// A file has been exported. A file has been written by imex().
|
|
||||||
/// This event may be sent multiple times by a single call to imex().
|
|
||||||
///
|
|
||||||
/// A typical purpose for a handler of this event may be to make the file public to some system
|
|
||||||
/// services.
|
|
||||||
///
|
|
||||||
/// @param data2 0
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
ImexFileWritten {
|
|
||||||
path: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Progress information of a secure-join handshake from the view of the inviter
|
|
||||||
/// (Alice, the person who shows the QR code).
|
|
||||||
///
|
|
||||||
/// These events are typically sent after a joiner has scanned the QR code
|
|
||||||
/// generated by getChatSecurejoinQrCodeSvg().
|
|
||||||
///
|
|
||||||
/// @param data1 (int) ID of the contact that wants to join.
|
|
||||||
/// @param data2 (int) Progress as:
|
|
||||||
/// 300=vg-/vc-request received, typically shown as "bob@addr joins".
|
|
||||||
/// 600=vg-/vc-request-with-auth received, vg-member-added/vc-contact-confirm sent, typically shown as "bob@addr verified".
|
|
||||||
/// 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol.
|
|
||||||
/// 1000=Protocol finished for this contact.
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
SecurejoinInviterProgress {
|
|
||||||
contact_id: u32,
|
|
||||||
progress: usize,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Progress information of a secure-join handshake from the view of the joiner
|
|
||||||
/// (Bob, the person who scans the QR code).
|
|
||||||
/// The events are typically sent while secureJoin(), which
|
|
||||||
/// may take some time, is executed.
|
|
||||||
/// @param data1 (int) ID of the inviting contact.
|
|
||||||
/// @param data2 (int) Progress as:
|
|
||||||
/// 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself."
|
|
||||||
/// (Bob has verified alice and waits until Alice does the same for him)
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
SecurejoinJoinerProgress {
|
|
||||||
contact_id: u32,
|
|
||||||
progress: usize,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// The connectivity to the server changed.
|
|
||||||
/// This means that you should refresh the connectivity view
|
|
||||||
/// and possibly the connectivtiy HTML; see getConnectivity() and
|
|
||||||
/// getConnectivityHtml() for details.
|
|
||||||
ConnectivityChanged,
|
|
||||||
|
|
||||||
SelfavatarChanged,
|
|
||||||
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
WebxdcStatusUpdate {
|
|
||||||
msg_id: u32,
|
|
||||||
status_update_serial: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Inform that a message containing a webxdc instance has been deleted
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
WebxdcInstanceDeleted {
|
|
||||||
msg_id: u32,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<EventType> for JSONRPCEventType {
|
|
||||||
fn from(event: EventType) -> Self {
|
|
||||||
use JSONRPCEventType::*;
|
|
||||||
match event {
|
|
||||||
EventType::Info(msg) => Info { msg },
|
|
||||||
EventType::SmtpConnected(msg) => SmtpConnected { msg },
|
|
||||||
EventType::ImapConnected(msg) => ImapConnected { msg },
|
|
||||||
EventType::SmtpMessageSent(msg) => SmtpMessageSent { msg },
|
|
||||||
EventType::ImapMessageDeleted(msg) => ImapMessageDeleted { msg },
|
|
||||||
EventType::ImapMessageMoved(msg) => ImapMessageMoved { msg },
|
|
||||||
EventType::NewBlobFile(file) => NewBlobFile { file },
|
|
||||||
EventType::DeletedBlobFile(file) => DeletedBlobFile { file },
|
|
||||||
EventType::Warning(msg) => Warning { msg },
|
|
||||||
EventType::Error(msg) => Error { msg },
|
|
||||||
EventType::ErrorSelfNotInGroup(msg) => ErrorSelfNotInGroup { msg },
|
|
||||||
EventType::MsgsChanged { chat_id, msg_id } => MsgsChanged {
|
|
||||||
chat_id: chat_id.to_u32(),
|
|
||||||
msg_id: msg_id.to_u32(),
|
|
||||||
},
|
|
||||||
EventType::ReactionsChanged {
|
|
||||||
chat_id,
|
|
||||||
msg_id,
|
|
||||||
contact_id,
|
|
||||||
} => ReactionsChanged {
|
|
||||||
chat_id: chat_id.to_u32(),
|
|
||||||
msg_id: msg_id.to_u32(),
|
|
||||||
contact_id: contact_id.to_u32(),
|
|
||||||
},
|
|
||||||
EventType::IncomingMsg { chat_id, msg_id } => IncomingMsg {
|
|
||||||
chat_id: chat_id.to_u32(),
|
|
||||||
msg_id: msg_id.to_u32(),
|
|
||||||
},
|
|
||||||
EventType::IncomingMsgBunch { msg_ids } => IncomingMsgBunch {
|
|
||||||
msg_ids: msg_ids.into_iter().map(|id| id.to_u32()).collect(),
|
|
||||||
},
|
|
||||||
EventType::MsgsNoticed(chat_id) => MsgsNoticed {
|
|
||||||
chat_id: chat_id.to_u32(),
|
|
||||||
},
|
|
||||||
EventType::MsgDelivered { chat_id, msg_id } => MsgDelivered {
|
|
||||||
chat_id: chat_id.to_u32(),
|
|
||||||
msg_id: msg_id.to_u32(),
|
|
||||||
},
|
|
||||||
EventType::MsgFailed { chat_id, msg_id } => MsgFailed {
|
|
||||||
chat_id: chat_id.to_u32(),
|
|
||||||
msg_id: msg_id.to_u32(),
|
|
||||||
},
|
|
||||||
EventType::MsgRead { chat_id, msg_id } => MsgRead {
|
|
||||||
chat_id: chat_id.to_u32(),
|
|
||||||
msg_id: msg_id.to_u32(),
|
|
||||||
},
|
|
||||||
EventType::ChatModified(chat_id) => ChatModified {
|
|
||||||
chat_id: chat_id.to_u32(),
|
|
||||||
},
|
|
||||||
EventType::ChatEphemeralTimerModified { chat_id, timer } => {
|
|
||||||
ChatEphemeralTimerModified {
|
|
||||||
chat_id: chat_id.to_u32(),
|
|
||||||
timer: timer.to_u32(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EventType::ContactsChanged(contact) => ContactsChanged {
|
|
||||||
contact_id: contact.map(|c| c.to_u32()),
|
|
||||||
},
|
|
||||||
EventType::LocationChanged(contact) => LocationChanged {
|
|
||||||
contact_id: contact.map(|c| c.to_u32()),
|
|
||||||
},
|
|
||||||
EventType::ConfigureProgress { progress, comment } => {
|
|
||||||
ConfigureProgress { progress, comment }
|
|
||||||
}
|
|
||||||
EventType::ImexProgress(progress) => ImexProgress { progress },
|
|
||||||
EventType::ImexFileWritten(path) => ImexFileWritten {
|
|
||||||
path: path.to_str().unwrap_or_default().to_owned(),
|
|
||||||
},
|
|
||||||
EventType::SecurejoinInviterProgress {
|
|
||||||
contact_id,
|
|
||||||
progress,
|
|
||||||
} => SecurejoinInviterProgress {
|
|
||||||
contact_id: contact_id.to_u32(),
|
|
||||||
progress,
|
|
||||||
},
|
|
||||||
EventType::SecurejoinJoinerProgress {
|
|
||||||
contact_id,
|
|
||||||
progress,
|
|
||||||
} => SecurejoinJoinerProgress {
|
|
||||||
contact_id: contact_id.to_u32(),
|
|
||||||
progress,
|
|
||||||
},
|
|
||||||
EventType::ConnectivityChanged => ConnectivityChanged,
|
|
||||||
EventType::SelfavatarChanged => SelfavatarChanged,
|
|
||||||
EventType::WebxdcStatusUpdate {
|
|
||||||
msg_id,
|
|
||||||
status_update_serial,
|
|
||||||
} => WebxdcStatusUpdate {
|
|
||||||
msg_id: msg_id.to_u32(),
|
|
||||||
status_update_serial: status_update_serial.to_u32(),
|
|
||||||
},
|
|
||||||
EventType::WebxdcInstanceDeleted { msg_id } => WebxdcInstanceDeleted {
|
|
||||||
msg_id: msg_id.to_u32(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[test]
|
|
||||||
fn generate_events_ts_types_definition() {
|
|
||||||
let events = {
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
let options = typescript_type_def::DefinitionFileOptions {
|
|
||||||
root_namespace: None,
|
|
||||||
..typescript_type_def::DefinitionFileOptions::default()
|
|
||||||
};
|
|
||||||
typescript_type_def::write_definition_file::<_, JSONRPCEventType>(&mut buf, options)
|
|
||||||
.unwrap();
|
|
||||||
String::from_utf8(buf).unwrap()
|
|
||||||
};
|
|
||||||
std::fs::write("typescript/generated/events.ts", events).unwrap();
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use deltachat::config::Config;
|
|
||||||
use deltachat::contact::{Contact, ContactId};
|
|
||||||
use serde::Serialize;
|
|
||||||
use typescript_type_def::TypeDef;
|
|
||||||
|
|
||||||
use super::color_int_to_hex_string;
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(tag = "type")]
|
|
||||||
pub enum Account {
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
Configured {
|
|
||||||
id: u32,
|
|
||||||
display_name: Option<String>,
|
|
||||||
addr: Option<String>,
|
|
||||||
// size: u32,
|
|
||||||
profile_image: Option<String>, // TODO: This needs to be converted to work with blob http server.
|
|
||||||
color: String,
|
|
||||||
},
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
Unconfigured { id: u32 },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Account {
|
|
||||||
pub async fn from_context(ctx: &deltachat::context::Context, id: u32) -> Result<Self> {
|
|
||||||
if ctx.is_configured().await? {
|
|
||||||
let display_name = ctx.get_config(Config::Displayname).await?;
|
|
||||||
let addr = ctx.get_config(Config::Addr).await?;
|
|
||||||
let profile_image = ctx.get_config(Config::Selfavatar).await?;
|
|
||||||
let color = color_int_to_hex_string(
|
|
||||||
Contact::get_by_id(ctx, ContactId::SELF).await?.get_color(),
|
|
||||||
);
|
|
||||||
Ok(Account::Configured {
|
|
||||||
id,
|
|
||||||
display_name,
|
|
||||||
addr,
|
|
||||||
profile_image,
|
|
||||||
color,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok(Account::Unconfigured { id })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
use std::time::{Duration, SystemTime};
|
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
|
||||||
use deltachat::chat::{self, get_chat_contacts, ChatVisibility};
|
|
||||||
use deltachat::chat::{Chat, ChatId};
|
|
||||||
use deltachat::constants::Chattype;
|
|
||||||
use deltachat::contact::{Contact, ContactId};
|
|
||||||
use deltachat::context::Context;
|
|
||||||
use num_traits::cast::ToPrimitive;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use typescript_type_def::TypeDef;
|
|
||||||
|
|
||||||
use super::color_int_to_hex_string;
|
|
||||||
use super::contact::ContactObject;
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct FullChat {
|
|
||||||
id: u32,
|
|
||||||
name: String,
|
|
||||||
is_protected: bool,
|
|
||||||
profile_image: Option<String>, //BLOBS ?
|
|
||||||
archived: bool,
|
|
||||||
// subtitle - will be moved to frontend because it uses translation functions
|
|
||||||
chat_type: u32,
|
|
||||||
is_unpromoted: bool,
|
|
||||||
is_self_talk: bool,
|
|
||||||
contacts: Vec<ContactObject>,
|
|
||||||
contact_ids: Vec<u32>,
|
|
||||||
color: String,
|
|
||||||
fresh_message_counter: usize,
|
|
||||||
// is_group - please check over chat.type in frontend instead
|
|
||||||
is_contact_request: bool,
|
|
||||||
is_device_chat: bool,
|
|
||||||
self_in_group: bool,
|
|
||||||
is_muted: bool,
|
|
||||||
ephemeral_timer: u32, //TODO look if there are more important properties in newer core versions
|
|
||||||
can_send: bool,
|
|
||||||
was_seen_recently: bool,
|
|
||||||
mailing_list_address: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FullChat {
|
|
||||||
pub async fn try_from_dc_chat_id(context: &Context, chat_id: u32) -> Result<Self> {
|
|
||||||
let rust_chat_id = ChatId::new(chat_id);
|
|
||||||
let chat = Chat::load_from_db(context, rust_chat_id).await?;
|
|
||||||
|
|
||||||
let contact_ids = get_chat_contacts(context, rust_chat_id).await?;
|
|
||||||
|
|
||||||
let mut contacts = Vec::with_capacity(contact_ids.len());
|
|
||||||
|
|
||||||
for contact_id in &contact_ids {
|
|
||||||
contacts.push(
|
|
||||||
ContactObject::try_from_dc_contact(
|
|
||||||
context,
|
|
||||||
Contact::load_from_db(context, *contact_id).await?,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let profile_image = match chat.get_profile_image(context).await? {
|
|
||||||
Some(path_buf) => path_buf.to_str().map(|s| s.to_owned()),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let color = color_int_to_hex_string(chat.get_color(context).await?);
|
|
||||||
let fresh_message_counter = rust_chat_id.get_fresh_msg_cnt(context).await?;
|
|
||||||
let ephemeral_timer = rust_chat_id.get_ephemeral_timer(context).await?.to_u32();
|
|
||||||
|
|
||||||
let can_send = chat.can_send(context).await?;
|
|
||||||
|
|
||||||
let was_seen_recently = if chat.get_type() == Chattype::Single {
|
|
||||||
match contact_ids.get(0) {
|
|
||||||
Some(contact) => Contact::load_from_db(context, *contact)
|
|
||||||
.await?
|
|
||||||
.was_seen_recently(),
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
let mailing_list_address = chat.get_mailinglist_addr().map(|s| s.to_string());
|
|
||||||
|
|
||||||
Ok(FullChat {
|
|
||||||
id: chat_id,
|
|
||||||
name: chat.name.clone(),
|
|
||||||
is_protected: chat.is_protected(),
|
|
||||||
profile_image, //BLOBS ?
|
|
||||||
archived: chat.get_visibility() == chat::ChatVisibility::Archived,
|
|
||||||
chat_type: chat
|
|
||||||
.get_type()
|
|
||||||
.to_u32()
|
|
||||||
.ok_or_else(|| anyhow!("unknown chat type id"))?, // TODO get rid of this unwrap?
|
|
||||||
is_unpromoted: chat.is_unpromoted(),
|
|
||||||
is_self_talk: chat.is_self_talk(),
|
|
||||||
contacts,
|
|
||||||
contact_ids: contact_ids.iter().map(|id| id.to_u32()).collect(),
|
|
||||||
color,
|
|
||||||
fresh_message_counter,
|
|
||||||
is_contact_request: chat.is_contact_request(),
|
|
||||||
is_device_chat: chat.is_device_talk(),
|
|
||||||
self_in_group: contact_ids.contains(&ContactId::SELF),
|
|
||||||
is_muted: chat.is_muted(),
|
|
||||||
ephemeral_timer,
|
|
||||||
can_send,
|
|
||||||
was_seen_recently,
|
|
||||||
mailing_list_address,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// cheaper version of fullchat, omits:
|
|
||||||
/// - contacts
|
|
||||||
/// - contact_ids
|
|
||||||
/// - fresh_message_counter
|
|
||||||
/// - ephemeral_timer
|
|
||||||
/// - self_in_group
|
|
||||||
/// - was_seen_recently
|
|
||||||
/// - can_send
|
|
||||||
///
|
|
||||||
/// used when you only need the basic metadata of a chat like type, name, profile picture
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct BasicChat {
|
|
||||||
id: u32,
|
|
||||||
name: String,
|
|
||||||
is_protected: bool,
|
|
||||||
profile_image: Option<String>, //BLOBS ?
|
|
||||||
archived: bool,
|
|
||||||
chat_type: u32,
|
|
||||||
is_unpromoted: bool,
|
|
||||||
is_self_talk: bool,
|
|
||||||
color: String,
|
|
||||||
is_contact_request: bool,
|
|
||||||
is_device_chat: bool,
|
|
||||||
is_muted: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BasicChat {
|
|
||||||
pub async fn try_from_dc_chat_id(context: &Context, chat_id: u32) -> Result<Self> {
|
|
||||||
let rust_chat_id = ChatId::new(chat_id);
|
|
||||||
let chat = Chat::load_from_db(context, rust_chat_id).await?;
|
|
||||||
|
|
||||||
let profile_image = match chat.get_profile_image(context).await? {
|
|
||||||
Some(path_buf) => path_buf.to_str().map(|s| s.to_owned()),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
let color = color_int_to_hex_string(chat.get_color(context).await?);
|
|
||||||
|
|
||||||
Ok(BasicChat {
|
|
||||||
id: chat_id,
|
|
||||||
name: chat.name.clone(),
|
|
||||||
is_protected: chat.is_protected(),
|
|
||||||
profile_image, //BLOBS ?
|
|
||||||
archived: chat.get_visibility() == chat::ChatVisibility::Archived,
|
|
||||||
chat_type: chat
|
|
||||||
.get_type()
|
|
||||||
.to_u32()
|
|
||||||
.ok_or_else(|| anyhow!("unknown chat type id"))?, // TODO get rid of this unwrap?
|
|
||||||
is_unpromoted: chat.is_unpromoted(),
|
|
||||||
is_self_talk: chat.is_self_talk(),
|
|
||||||
color,
|
|
||||||
is_contact_request: chat.is_contact_request(),
|
|
||||||
is_device_chat: chat.is_device_talk(),
|
|
||||||
is_muted: chat.is_muted(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, TypeDef)]
|
|
||||||
pub enum MuteDuration {
|
|
||||||
NotMuted,
|
|
||||||
Forever,
|
|
||||||
Until(i64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MuteDuration {
|
|
||||||
pub fn try_into_core_type(self) -> Result<chat::MuteDuration> {
|
|
||||||
match self {
|
|
||||||
MuteDuration::NotMuted => Ok(chat::MuteDuration::NotMuted),
|
|
||||||
MuteDuration::Forever => Ok(chat::MuteDuration::Forever),
|
|
||||||
MuteDuration::Until(n) => {
|
|
||||||
if n <= 0 {
|
|
||||||
bail!("failed to read mute duration")
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(SystemTime::now()
|
|
||||||
.checked_add(Duration::from_secs(n as u64))
|
|
||||||
.map_or(chat::MuteDuration::Forever, chat::MuteDuration::Until))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, TypeDef)]
|
|
||||||
#[serde(rename = "ChatVisibility")]
|
|
||||||
pub enum JSONRPCChatVisibility {
|
|
||||||
Normal,
|
|
||||||
Archived,
|
|
||||||
Pinned,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JSONRPCChatVisibility {
|
|
||||||
pub fn into_core_type(self) -> ChatVisibility {
|
|
||||||
match self {
|
|
||||||
JSONRPCChatVisibility::Normal => ChatVisibility::Normal,
|
|
||||||
JSONRPCChatVisibility::Archived => ChatVisibility::Archived,
|
|
||||||
JSONRPCChatVisibility::Pinned => ChatVisibility::Pinned,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use deltachat::constants::*;
|
|
||||||
use deltachat::contact::{Contact, ContactId};
|
|
||||||
use deltachat::{
|
|
||||||
chat::{get_chat_contacts, ChatVisibility},
|
|
||||||
chatlist::Chatlist,
|
|
||||||
};
|
|
||||||
use deltachat::{
|
|
||||||
chat::{Chat, ChatId},
|
|
||||||
message::MsgId,
|
|
||||||
};
|
|
||||||
use num_traits::cast::ToPrimitive;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use typescript_type_def::TypeDef;
|
|
||||||
|
|
||||||
use super::color_int_to_hex_string;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, TypeDef)]
|
|
||||||
pub struct ChatListEntry(pub u32, pub u32);
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(tag = "type")]
|
|
||||||
pub enum ChatListItemFetchResult {
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
ChatListItem {
|
|
||||||
id: u32,
|
|
||||||
name: String,
|
|
||||||
avatar_path: Option<String>,
|
|
||||||
color: String,
|
|
||||||
last_updated: Option<i64>,
|
|
||||||
summary_text1: String,
|
|
||||||
summary_text2: String,
|
|
||||||
summary_status: u32,
|
|
||||||
is_protected: bool,
|
|
||||||
is_group: bool,
|
|
||||||
fresh_message_counter: usize,
|
|
||||||
is_self_talk: bool,
|
|
||||||
is_device_talk: bool,
|
|
||||||
is_sending_location: bool,
|
|
||||||
is_self_in_group: bool,
|
|
||||||
is_archived: bool,
|
|
||||||
is_pinned: bool,
|
|
||||||
is_muted: bool,
|
|
||||||
is_contact_request: bool,
|
|
||||||
/// true when chat is a broadcastlist
|
|
||||||
is_broadcast: bool,
|
|
||||||
/// contact id if this is a dm chat (for view profile entry in context menu)
|
|
||||||
dm_chat_contact: Option<u32>,
|
|
||||||
was_seen_recently: bool,
|
|
||||||
},
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
ArchiveLink { fresh_message_counter: usize },
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
Error { id: u32, error: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn get_chat_list_item_by_id(
|
|
||||||
ctx: &deltachat::context::Context,
|
|
||||||
entry: &ChatListEntry,
|
|
||||||
) -> Result<ChatListItemFetchResult> {
|
|
||||||
let chat_id = ChatId::new(entry.0);
|
|
||||||
let last_msgid = match entry.1 {
|
|
||||||
0 => None,
|
|
||||||
_ => Some(MsgId::new(entry.1)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let fresh_message_counter = chat_id.get_fresh_msg_cnt(ctx).await?;
|
|
||||||
|
|
||||||
if chat_id.is_archived_link() {
|
|
||||||
return Ok(ChatListItemFetchResult::ArchiveLink {
|
|
||||||
fresh_message_counter,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let chat = Chat::load_from_db(ctx, chat_id).await?;
|
|
||||||
let summary = Chatlist::get_summary2(ctx, chat_id, last_msgid, Some(&chat)).await?;
|
|
||||||
|
|
||||||
let summary_text1 = summary.prefix.map_or_else(String::new, |s| s.to_string());
|
|
||||||
let summary_text2 = summary.text.to_owned();
|
|
||||||
|
|
||||||
let visibility = chat.get_visibility();
|
|
||||||
|
|
||||||
let avatar_path = chat
|
|
||||||
.get_profile_image(ctx)
|
|
||||||
.await?
|
|
||||||
.map(|path| path.to_str().unwrap_or("invalid/path").to_owned());
|
|
||||||
|
|
||||||
let last_updated = match last_msgid {
|
|
||||||
Some(id) => {
|
|
||||||
let last_message = deltachat::message::Message::load_from_db(ctx, id).await?;
|
|
||||||
Some(last_message.get_timestamp() * 1000)
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let chat_contacts = get_chat_contacts(ctx, chat_id).await?;
|
|
||||||
|
|
||||||
let self_in_group = chat_contacts.contains(&ContactId::SELF);
|
|
||||||
|
|
||||||
let (dm_chat_contact, was_seen_recently) = if chat.get_type() == Chattype::Single {
|
|
||||||
let contact = chat_contacts.get(0);
|
|
||||||
let was_seen_recently = match contact {
|
|
||||||
Some(contact) => Contact::load_from_db(ctx, *contact)
|
|
||||||
.await?
|
|
||||||
.was_seen_recently(),
|
|
||||||
None => false,
|
|
||||||
};
|
|
||||||
(
|
|
||||||
contact.map(|contact_id| contact_id.to_u32()),
|
|
||||||
was_seen_recently,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(None, false)
|
|
||||||
};
|
|
||||||
|
|
||||||
let color = color_int_to_hex_string(chat.get_color(ctx).await?);
|
|
||||||
|
|
||||||
Ok(ChatListItemFetchResult::ChatListItem {
|
|
||||||
id: chat_id.to_u32(),
|
|
||||||
name: chat.get_name().to_owned(),
|
|
||||||
avatar_path,
|
|
||||||
color,
|
|
||||||
last_updated,
|
|
||||||
summary_text1,
|
|
||||||
summary_text2,
|
|
||||||
summary_status: summary.state.to_u32().expect("impossible"), // idea and a function to transform the constant to strings? or return string enum
|
|
||||||
is_protected: chat.is_protected(),
|
|
||||||
is_group: chat.get_type() == Chattype::Group,
|
|
||||||
fresh_message_counter,
|
|
||||||
is_self_talk: chat.is_self_talk(),
|
|
||||||
is_device_talk: chat.is_device_talk(),
|
|
||||||
is_self_in_group: self_in_group,
|
|
||||||
is_sending_location: chat.is_sending_locations(),
|
|
||||||
is_archived: visibility == ChatVisibility::Archived,
|
|
||||||
is_pinned: visibility == ChatVisibility::Pinned,
|
|
||||||
is_muted: chat.is_muted(),
|
|
||||||
is_contact_request: chat.is_contact_request(),
|
|
||||||
is_broadcast: chat.get_type() == Chattype::Broadcast,
|
|
||||||
dm_chat_contact,
|
|
||||||
was_seen_recently,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use deltachat::contact::VerifiedStatus;
|
|
||||||
use deltachat::context::Context;
|
|
||||||
use serde::Serialize;
|
|
||||||
use typescript_type_def::TypeDef;
|
|
||||||
|
|
||||||
use super::color_int_to_hex_string;
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename = "Contact", rename_all = "camelCase")]
|
|
||||||
pub struct ContactObject {
|
|
||||||
address: String,
|
|
||||||
color: String,
|
|
||||||
auth_name: String,
|
|
||||||
status: String,
|
|
||||||
display_name: String,
|
|
||||||
id: u32,
|
|
||||||
name: String,
|
|
||||||
profile_image: Option<String>, // BLOBS
|
|
||||||
name_and_addr: String,
|
|
||||||
is_blocked: bool,
|
|
||||||
is_verified: bool,
|
|
||||||
/// the address that verified this contact
|
|
||||||
verifier_addr: Option<String>,
|
|
||||||
/// the id of the contact that verified this contact
|
|
||||||
verifier_id: Option<u32>,
|
|
||||||
/// the contact's last seen timestamp
|
|
||||||
last_seen: i64,
|
|
||||||
was_seen_recently: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContactObject {
|
|
||||||
pub async fn try_from_dc_contact(
|
|
||||||
context: &Context,
|
|
||||||
contact: deltachat::contact::Contact,
|
|
||||||
) -> Result<Self> {
|
|
||||||
let profile_image = match contact.get_profile_image(context).await? {
|
|
||||||
Some(path_buf) => path_buf.to_str().map(|s| s.to_owned()),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
let is_verified = contact.is_verified(context).await? == VerifiedStatus::BidirectVerified;
|
|
||||||
|
|
||||||
let (verifier_addr, verifier_id) = if is_verified {
|
|
||||||
(
|
|
||||||
contact.get_verifier_addr(context).await?,
|
|
||||||
contact
|
|
||||||
.get_verifier_id(context)
|
|
||||||
.await?
|
|
||||||
.map(|contact_id| contact_id.to_u32()),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ContactObject {
|
|
||||||
address: contact.get_addr().to_owned(),
|
|
||||||
color: color_int_to_hex_string(contact.get_color()),
|
|
||||||
auth_name: contact.get_authname().to_owned(),
|
|
||||||
status: contact.get_status().to_owned(),
|
|
||||||
display_name: contact.get_display_name().to_owned(),
|
|
||||||
id: contact.id.to_u32(),
|
|
||||||
name: contact.get_name().to_owned(),
|
|
||||||
profile_image, //BLOBS
|
|
||||||
name_and_addr: contact.get_name_n_addr(),
|
|
||||||
is_blocked: contact.is_blocked(),
|
|
||||||
is_verified,
|
|
||||||
verifier_addr,
|
|
||||||
verifier_id,
|
|
||||||
last_seen: contact.last_seen(),
|
|
||||||
was_seen_recently: contact.was_seen_recently(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
use deltachat::location::Location;
|
|
||||||
use serde::Serialize;
|
|
||||||
use typescript_type_def::TypeDef;
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename = "Location", rename_all = "camelCase")]
|
|
||||||
pub struct JsonrpcLocation {
|
|
||||||
pub location_id: u32,
|
|
||||||
pub is_independent: bool,
|
|
||||||
pub latitude: f64,
|
|
||||||
pub longitude: f64,
|
|
||||||
pub accuracy: f64,
|
|
||||||
pub timestamp: i64,
|
|
||||||
pub contact_id: u32,
|
|
||||||
pub msg_id: u32,
|
|
||||||
pub chat_id: u32,
|
|
||||||
pub marker: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Location> for JsonrpcLocation {
|
|
||||||
fn from(location: Location) -> Self {
|
|
||||||
let Location {
|
|
||||||
location_id,
|
|
||||||
independent,
|
|
||||||
latitude,
|
|
||||||
longitude,
|
|
||||||
accuracy,
|
|
||||||
timestamp,
|
|
||||||
contact_id,
|
|
||||||
msg_id,
|
|
||||||
chat_id,
|
|
||||||
marker,
|
|
||||||
} = location;
|
|
||||||
Self {
|
|
||||||
location_id,
|
|
||||||
is_independent: independent != 0,
|
|
||||||
latitude,
|
|
||||||
longitude,
|
|
||||||
accuracy,
|
|
||||||
timestamp,
|
|
||||||
contact_id: contact_id.to_u32(),
|
|
||||||
msg_id,
|
|
||||||
chat_id: chat_id.to_u32(),
|
|
||||||
marker,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,492 +0,0 @@
|
|||||||
use anyhow::{anyhow, Result};
|
|
||||||
use deltachat::chat::Chat;
|
|
||||||
use deltachat::chat::ChatItem;
|
|
||||||
use deltachat::constants::Chattype;
|
|
||||||
use deltachat::contact::Contact;
|
|
||||||
use deltachat::context::Context;
|
|
||||||
use deltachat::download;
|
|
||||||
use deltachat::message::Message;
|
|
||||||
use deltachat::message::MsgId;
|
|
||||||
use deltachat::message::Viewtype;
|
|
||||||
use deltachat::reaction::get_msg_reactions;
|
|
||||||
use num_traits::cast::ToPrimitive;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use serde::Serialize;
|
|
||||||
use typescript_type_def::TypeDef;
|
|
||||||
|
|
||||||
use super::color_int_to_hex_string;
|
|
||||||
use super::contact::ContactObject;
|
|
||||||
use super::reactions::JSONRPCReactions;
|
|
||||||
use super::webxdc::WebxdcMessageInfo;
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename = "Message", rename_all = "camelCase")]
|
|
||||||
pub struct MessageObject {
|
|
||||||
id: u32,
|
|
||||||
chat_id: u32,
|
|
||||||
from_id: u32,
|
|
||||||
quote: Option<MessageQuote>,
|
|
||||||
parent_id: Option<u32>,
|
|
||||||
|
|
||||||
text: Option<String>,
|
|
||||||
has_location: bool,
|
|
||||||
has_html: bool,
|
|
||||||
view_type: MessageViewtype,
|
|
||||||
state: u32,
|
|
||||||
|
|
||||||
/// An error text, if there is one.
|
|
||||||
error: Option<String>,
|
|
||||||
|
|
||||||
timestamp: i64,
|
|
||||||
sort_timestamp: i64,
|
|
||||||
received_timestamp: i64,
|
|
||||||
has_deviating_timestamp: bool,
|
|
||||||
|
|
||||||
// summary - use/create another function if you need it
|
|
||||||
subject: String,
|
|
||||||
show_padlock: bool,
|
|
||||||
is_setupmessage: bool,
|
|
||||||
is_info: bool,
|
|
||||||
is_forwarded: bool,
|
|
||||||
/// when is_info is true this describes what type of system message it is
|
|
||||||
system_message_type: SystemMessageType,
|
|
||||||
|
|
||||||
duration: i32,
|
|
||||||
dimensions_height: i32,
|
|
||||||
dimensions_width: i32,
|
|
||||||
|
|
||||||
videochat_type: Option<u32>,
|
|
||||||
videochat_url: Option<String>,
|
|
||||||
|
|
||||||
override_sender_name: Option<String>,
|
|
||||||
sender: ContactObject,
|
|
||||||
|
|
||||||
setup_code_begin: Option<String>,
|
|
||||||
|
|
||||||
file: Option<String>,
|
|
||||||
file_mime: Option<String>,
|
|
||||||
file_bytes: u64,
|
|
||||||
file_name: Option<String>,
|
|
||||||
|
|
||||||
webxdc_info: Option<WebxdcMessageInfo>,
|
|
||||||
|
|
||||||
download_state: DownloadState,
|
|
||||||
|
|
||||||
reactions: Option<JSONRPCReactions>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(tag = "kind")]
|
|
||||||
enum MessageQuote {
|
|
||||||
JustText {
|
|
||||||
text: String,
|
|
||||||
},
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
WithMessage {
|
|
||||||
text: String,
|
|
||||||
message_id: u32,
|
|
||||||
author_display_name: String,
|
|
||||||
author_display_color: String,
|
|
||||||
override_sender_name: Option<String>,
|
|
||||||
image: Option<String>,
|
|
||||||
is_forwarded: bool,
|
|
||||||
view_type: MessageViewtype,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MessageObject {
|
|
||||||
pub async fn from_message_id(context: &Context, message_id: u32) -> Result<Self> {
|
|
||||||
let msg_id = MsgId::new(message_id);
|
|
||||||
Self::from_msg_id(context, msg_id).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn from_msg_id(context: &Context, msg_id: MsgId) -> Result<Self> {
|
|
||||||
let message = Message::load_from_db(context, msg_id).await?;
|
|
||||||
|
|
||||||
let sender_contact = Contact::load_from_db(context, message.get_from_id()).await?;
|
|
||||||
let sender = ContactObject::try_from_dc_contact(context, sender_contact).await?;
|
|
||||||
let file_bytes = message.get_filebytes(context).await?.unwrap_or_default();
|
|
||||||
let override_sender_name = message.get_override_sender_name();
|
|
||||||
|
|
||||||
let webxdc_info = if message.get_viewtype() == Viewtype::Webxdc {
|
|
||||||
Some(WebxdcMessageInfo::get_for_message(context, msg_id).await?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let parent_id = message.parent(context).await?.map(|m| m.get_id().to_u32());
|
|
||||||
|
|
||||||
let download_state = message.download_state().into();
|
|
||||||
|
|
||||||
let quote = if let Some(quoted_text) = message.quoted_text() {
|
|
||||||
match message.quoted_message(context).await? {
|
|
||||||
Some(quote) => {
|
|
||||||
let quote_author = Contact::load_from_db(context, quote.get_from_id()).await?;
|
|
||||||
Some(MessageQuote::WithMessage {
|
|
||||||
text: quoted_text,
|
|
||||||
message_id: quote.get_id().to_u32(),
|
|
||||||
author_display_name: quote_author.get_display_name().to_owned(),
|
|
||||||
author_display_color: color_int_to_hex_string(quote_author.get_color()),
|
|
||||||
override_sender_name: quote.get_override_sender_name(),
|
|
||||||
image: if quote.get_viewtype() == Viewtype::Image
|
|
||||||
|| quote.get_viewtype() == Viewtype::Gif
|
|
||||||
|| quote.get_viewtype() == Viewtype::Sticker
|
|
||||||
{
|
|
||||||
match quote.get_file(context) {
|
|
||||||
Some(path_buf) => path_buf.to_str().map(|s| s.to_owned()),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
is_forwarded: quote.is_forwarded(),
|
|
||||||
view_type: quote.get_viewtype().into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
None => Some(MessageQuote::JustText { text: quoted_text }),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let reactions = get_msg_reactions(context, msg_id).await?;
|
|
||||||
let reactions = if reactions.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(reactions.into())
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(MessageObject {
|
|
||||||
id: msg_id.to_u32(),
|
|
||||||
chat_id: message.get_chat_id().to_u32(),
|
|
||||||
from_id: message.get_from_id().to_u32(),
|
|
||||||
quote,
|
|
||||||
parent_id,
|
|
||||||
text: message.get_text(),
|
|
||||||
has_location: message.has_location(),
|
|
||||||
has_html: message.has_html(),
|
|
||||||
view_type: message.get_viewtype().into(),
|
|
||||||
state: message
|
|
||||||
.get_state()
|
|
||||||
.to_u32()
|
|
||||||
.ok_or_else(|| anyhow!("state conversion to number failed"))?,
|
|
||||||
error: message.error(),
|
|
||||||
|
|
||||||
timestamp: message.get_timestamp(),
|
|
||||||
sort_timestamp: message.get_sort_timestamp(),
|
|
||||||
received_timestamp: message.get_received_timestamp(),
|
|
||||||
has_deviating_timestamp: message.has_deviating_timestamp(),
|
|
||||||
|
|
||||||
subject: message.get_subject().to_owned(),
|
|
||||||
show_padlock: message.get_showpadlock(),
|
|
||||||
is_setupmessage: message.is_setupmessage(),
|
|
||||||
is_info: message.is_info(),
|
|
||||||
is_forwarded: message.is_forwarded(),
|
|
||||||
system_message_type: message.get_info_type().into(),
|
|
||||||
|
|
||||||
duration: message.get_duration(),
|
|
||||||
dimensions_height: message.get_height(),
|
|
||||||
dimensions_width: message.get_width(),
|
|
||||||
|
|
||||||
videochat_type: match message.get_videochat_type() {
|
|
||||||
Some(vct) => Some(
|
|
||||||
vct.to_u32()
|
|
||||||
.ok_or_else(|| anyhow!("state conversion to number failed"))?,
|
|
||||||
),
|
|
||||||
None => None,
|
|
||||||
},
|
|
||||||
videochat_url: message.get_videochat_url(),
|
|
||||||
|
|
||||||
override_sender_name,
|
|
||||||
sender,
|
|
||||||
|
|
||||||
setup_code_begin: message.get_setupcodebegin(context).await,
|
|
||||||
|
|
||||||
file: match message.get_file(context) {
|
|
||||||
Some(path_buf) => path_buf.to_str().map(|s| s.to_owned()),
|
|
||||||
None => None,
|
|
||||||
}, //BLOBS
|
|
||||||
file_mime: message.get_filemime(),
|
|
||||||
file_bytes,
|
|
||||||
file_name: message.get_filename(),
|
|
||||||
webxdc_info,
|
|
||||||
|
|
||||||
download_state,
|
|
||||||
|
|
||||||
reactions,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, TypeDef)]
|
|
||||||
#[serde(rename = "Viewtype")]
|
|
||||||
pub enum MessageViewtype {
|
|
||||||
Unknown,
|
|
||||||
|
|
||||||
/// Text message.
|
|
||||||
Text,
|
|
||||||
|
|
||||||
/// Image message.
|
|
||||||
/// If the image is an animated GIF, the type `Viewtype.Gif` should be used.
|
|
||||||
Image,
|
|
||||||
|
|
||||||
/// Animated GIF message.
|
|
||||||
Gif,
|
|
||||||
|
|
||||||
/// Message containing a sticker, similar to image.
|
|
||||||
/// If possible, the ui should display the image without borders in a transparent way.
|
|
||||||
/// A click on a sticker will offer to install the sticker set in some future.
|
|
||||||
Sticker,
|
|
||||||
|
|
||||||
/// Message containing an Audio file.
|
|
||||||
Audio,
|
|
||||||
|
|
||||||
/// A voice message that was directly recorded by the user.
|
|
||||||
/// For all other audio messages, the type `Viewtype.Audio` should be used.
|
|
||||||
Voice,
|
|
||||||
|
|
||||||
/// Video messages.
|
|
||||||
Video,
|
|
||||||
|
|
||||||
/// Message containing any file, eg. a PDF.
|
|
||||||
File,
|
|
||||||
|
|
||||||
/// Message is an invitation to a videochat.
|
|
||||||
VideochatInvitation,
|
|
||||||
|
|
||||||
/// Message is an webxdc instance.
|
|
||||||
Webxdc,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Viewtype> for MessageViewtype {
|
|
||||||
fn from(viewtype: Viewtype) -> Self {
|
|
||||||
match viewtype {
|
|
||||||
Viewtype::Unknown => MessageViewtype::Unknown,
|
|
||||||
Viewtype::Text => MessageViewtype::Text,
|
|
||||||
Viewtype::Image => MessageViewtype::Image,
|
|
||||||
Viewtype::Gif => MessageViewtype::Gif,
|
|
||||||
Viewtype::Sticker => MessageViewtype::Sticker,
|
|
||||||
Viewtype::Audio => MessageViewtype::Audio,
|
|
||||||
Viewtype::Voice => MessageViewtype::Voice,
|
|
||||||
Viewtype::Video => MessageViewtype::Video,
|
|
||||||
Viewtype::File => MessageViewtype::File,
|
|
||||||
Viewtype::VideochatInvitation => MessageViewtype::VideochatInvitation,
|
|
||||||
Viewtype::Webxdc => MessageViewtype::Webxdc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<MessageViewtype> for Viewtype {
|
|
||||||
fn from(viewtype: MessageViewtype) -> Self {
|
|
||||||
match viewtype {
|
|
||||||
MessageViewtype::Unknown => Viewtype::Unknown,
|
|
||||||
MessageViewtype::Text => Viewtype::Text,
|
|
||||||
MessageViewtype::Image => Viewtype::Image,
|
|
||||||
MessageViewtype::Gif => Viewtype::Gif,
|
|
||||||
MessageViewtype::Sticker => Viewtype::Sticker,
|
|
||||||
MessageViewtype::Audio => Viewtype::Audio,
|
|
||||||
MessageViewtype::Voice => Viewtype::Voice,
|
|
||||||
MessageViewtype::Video => Viewtype::Video,
|
|
||||||
MessageViewtype::File => Viewtype::File,
|
|
||||||
MessageViewtype::VideochatInvitation => Viewtype::VideochatInvitation,
|
|
||||||
MessageViewtype::Webxdc => Viewtype::Webxdc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
pub enum DownloadState {
|
|
||||||
Done,
|
|
||||||
Available,
|
|
||||||
Failure,
|
|
||||||
InProgress,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<download::DownloadState> for DownloadState {
|
|
||||||
fn from(state: download::DownloadState) -> Self {
|
|
||||||
match state {
|
|
||||||
download::DownloadState::Done => DownloadState::Done,
|
|
||||||
download::DownloadState::Available => DownloadState::Available,
|
|
||||||
download::DownloadState::Failure => DownloadState::Failure,
|
|
||||||
download::DownloadState::InProgress => DownloadState::InProgress,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
pub enum SystemMessageType {
|
|
||||||
Unknown,
|
|
||||||
GroupNameChanged,
|
|
||||||
GroupImageChanged,
|
|
||||||
MemberAddedToGroup,
|
|
||||||
MemberRemovedFromGroup,
|
|
||||||
AutocryptSetupMessage,
|
|
||||||
SecurejoinMessage,
|
|
||||||
LocationStreamingEnabled,
|
|
||||||
LocationOnly,
|
|
||||||
|
|
||||||
/// Chat ephemeral message timer is changed.
|
|
||||||
EphemeralTimerChanged,
|
|
||||||
|
|
||||||
// Chat protection state changed
|
|
||||||
ChatProtectionEnabled,
|
|
||||||
ChatProtectionDisabled,
|
|
||||||
|
|
||||||
/// Self-sent-message that contains only json used for multi-device-sync;
|
|
||||||
/// if possible, we attach that to other messages as for locations.
|
|
||||||
MultiDeviceSync,
|
|
||||||
|
|
||||||
// Sync message that contains a json payload
|
|
||||||
// sent to the other webxdc instances
|
|
||||||
// These messages are not shown in the chat.
|
|
||||||
WebxdcStatusUpdate,
|
|
||||||
|
|
||||||
/// Webxdc info added with `info` set in `send_webxdc_status_update()`.
|
|
||||||
WebxdcInfoMessage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<deltachat::mimeparser::SystemMessage> for SystemMessageType {
|
|
||||||
fn from(system_message_type: deltachat::mimeparser::SystemMessage) -> Self {
|
|
||||||
use deltachat::mimeparser::SystemMessage;
|
|
||||||
match system_message_type {
|
|
||||||
SystemMessage::Unknown => SystemMessageType::Unknown,
|
|
||||||
SystemMessage::GroupNameChanged => SystemMessageType::GroupNameChanged,
|
|
||||||
SystemMessage::GroupImageChanged => SystemMessageType::GroupImageChanged,
|
|
||||||
SystemMessage::MemberAddedToGroup => SystemMessageType::MemberAddedToGroup,
|
|
||||||
SystemMessage::MemberRemovedFromGroup => SystemMessageType::MemberRemovedFromGroup,
|
|
||||||
SystemMessage::AutocryptSetupMessage => SystemMessageType::AutocryptSetupMessage,
|
|
||||||
SystemMessage::SecurejoinMessage => SystemMessageType::SecurejoinMessage,
|
|
||||||
SystemMessage::LocationStreamingEnabled => SystemMessageType::LocationStreamingEnabled,
|
|
||||||
SystemMessage::LocationOnly => SystemMessageType::LocationOnly,
|
|
||||||
SystemMessage::EphemeralTimerChanged => SystemMessageType::EphemeralTimerChanged,
|
|
||||||
SystemMessage::ChatProtectionEnabled => SystemMessageType::ChatProtectionEnabled,
|
|
||||||
SystemMessage::ChatProtectionDisabled => SystemMessageType::ChatProtectionDisabled,
|
|
||||||
SystemMessage::MultiDeviceSync => SystemMessageType::MultiDeviceSync,
|
|
||||||
SystemMessage::WebxdcStatusUpdate => SystemMessageType::WebxdcStatusUpdate,
|
|
||||||
SystemMessage::WebxdcInfoMessage => SystemMessageType::WebxdcInfoMessage,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct MessageNotificationInfo {
|
|
||||||
id: u32,
|
|
||||||
chat_id: u32,
|
|
||||||
account_id: u32,
|
|
||||||
|
|
||||||
image: Option<String>,
|
|
||||||
image_mime_type: Option<String>,
|
|
||||||
|
|
||||||
chat_name: String,
|
|
||||||
chat_profile_image: Option<String>,
|
|
||||||
|
|
||||||
/// also known as summary_text1
|
|
||||||
summary_prefix: Option<String>,
|
|
||||||
/// also known as summary_text2
|
|
||||||
summary_text: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MessageNotificationInfo {
|
|
||||||
pub async fn from_msg_id(context: &Context, msg_id: MsgId) -> Result<Self> {
|
|
||||||
let message = Message::load_from_db(context, msg_id).await?;
|
|
||||||
let chat = Chat::load_from_db(context, message.get_chat_id()).await?;
|
|
||||||
|
|
||||||
let image = if matches!(
|
|
||||||
message.get_viewtype(),
|
|
||||||
Viewtype::Image | Viewtype::Gif | Viewtype::Sticker
|
|
||||||
) {
|
|
||||||
message
|
|
||||||
.get_file(context)
|
|
||||||
.map(|path_buf| path_buf.to_str().map(|s| s.to_owned()))
|
|
||||||
.unwrap_or_default()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let chat_profile_image = chat
|
|
||||||
.get_profile_image(context)
|
|
||||||
.await?
|
|
||||||
.map(|path_buf| path_buf.to_str().map(|s| s.to_owned()))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let summary = message.get_summary(context, Some(&chat)).await?;
|
|
||||||
|
|
||||||
Ok(MessageNotificationInfo {
|
|
||||||
id: msg_id.to_u32(),
|
|
||||||
chat_id: message.get_chat_id().to_u32(),
|
|
||||||
account_id: context.get_id(),
|
|
||||||
image,
|
|
||||||
image_mime_type: message.get_filemime(),
|
|
||||||
chat_name: chat.name,
|
|
||||||
chat_profile_image,
|
|
||||||
summary_prefix: summary.prefix.map(|s| s.to_string()),
|
|
||||||
summary_text: summary.text,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct MessageSearchResult {
|
|
||||||
id: u32,
|
|
||||||
author_profile_image: Option<String>,
|
|
||||||
author_name: String,
|
|
||||||
author_color: String,
|
|
||||||
chat_name: Option<String>,
|
|
||||||
message: String,
|
|
||||||
timestamp: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MessageSearchResult {
|
|
||||||
pub async fn from_msg_id(context: &Context, msg_id: MsgId) -> Result<Self> {
|
|
||||||
let message = Message::load_from_db(context, msg_id).await?;
|
|
||||||
let chat = Chat::load_from_db(context, message.get_chat_id()).await?;
|
|
||||||
let sender = Contact::load_from_db(context, message.get_from_id()).await?;
|
|
||||||
|
|
||||||
let profile_image = match sender.get_profile_image(context).await? {
|
|
||||||
Some(path_buf) => path_buf.to_str().map(|s| s.to_owned()),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
id: msg_id.to_u32(),
|
|
||||||
author_profile_image: profile_image,
|
|
||||||
author_name: sender.get_display_name().to_owned(),
|
|
||||||
author_color: color_int_to_hex_string(sender.get_color()),
|
|
||||||
chat_name: if chat.get_type() == Chattype::Single {
|
|
||||||
Some(chat.get_name().to_owned())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
message: message.get_text().unwrap_or_default(),
|
|
||||||
timestamp: message.get_timestamp(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename_all = "camelCase", rename = "MessageListItem", tag = "kind")]
|
|
||||||
pub enum JSONRPCMessageListItem {
|
|
||||||
Message {
|
|
||||||
msg_id: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Day marker, separating messages that correspond to different
|
|
||||||
/// days according to local time.
|
|
||||||
DayMarker {
|
|
||||||
/// Marker timestamp, for day markers, in unix milliseconds
|
|
||||||
timestamp: i64,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ChatItem> for JSONRPCMessageListItem {
|
|
||||||
fn from(item: ChatItem) -> Self {
|
|
||||||
match item {
|
|
||||||
ChatItem::Message { msg_id } => JSONRPCMessageListItem::Message {
|
|
||||||
msg_id: msg_id.to_u32(),
|
|
||||||
},
|
|
||||||
ChatItem::DayMarker { timestamp } => JSONRPCMessageListItem::DayMarker { timestamp },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
pub mod account;
|
|
||||||
pub mod chat;
|
|
||||||
pub mod chat_list;
|
|
||||||
pub mod contact;
|
|
||||||
pub mod location;
|
|
||||||
pub mod message;
|
|
||||||
pub mod provider_info;
|
|
||||||
pub mod qr;
|
|
||||||
pub mod reactions;
|
|
||||||
pub mod webxdc;
|
|
||||||
|
|
||||||
pub fn color_int_to_hex_string(color: u32) -> String {
|
|
||||||
format!("{:#08x}", color).replace("0x", "#")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn maybe_empty_string_to_option(string: String) -> Option<String> {
|
|
||||||
if string.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
use deltachat::provider::Provider;
|
|
||||||
use num_traits::cast::ToPrimitive;
|
|
||||||
use serde::Serialize;
|
|
||||||
use typescript_type_def::TypeDef;
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct ProviderInfo {
|
|
||||||
pub before_login_hint: String,
|
|
||||||
pub overview_page: String,
|
|
||||||
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 {
|
|
||||||
pub fn from_dc_type(provider: Option<&Provider>) -> Option<Self> {
|
|
||||||
provider.map(|p| ProviderInfo {
|
|
||||||
before_login_hint: p.before_login_hint.to_owned(),
|
|
||||||
overview_page: p.overview_page.to_owned(),
|
|
||||||
status: p.status.to_u32().unwrap(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
use deltachat::qr::Qr;
|
|
||||||
use serde::Serialize;
|
|
||||||
use typescript_type_def::TypeDef;
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename = "Qr", rename_all = "camelCase")]
|
|
||||||
#[serde(tag = "type")]
|
|
||||||
pub enum QrObject {
|
|
||||||
AskVerifyContact {
|
|
||||||
contact_id: u32,
|
|
||||||
fingerprint: String,
|
|
||||||
invitenumber: String,
|
|
||||||
authcode: String,
|
|
||||||
},
|
|
||||||
AskVerifyGroup {
|
|
||||||
grpname: String,
|
|
||||||
grpid: String,
|
|
||||||
contact_id: u32,
|
|
||||||
fingerprint: String,
|
|
||||||
invitenumber: String,
|
|
||||||
authcode: String,
|
|
||||||
},
|
|
||||||
FprOk {
|
|
||||||
contact_id: u32,
|
|
||||||
},
|
|
||||||
FprMismatch {
|
|
||||||
contact_id: Option<u32>,
|
|
||||||
},
|
|
||||||
FprWithoutAddr {
|
|
||||||
fingerprint: String,
|
|
||||||
},
|
|
||||||
Account {
|
|
||||||
domain: String,
|
|
||||||
},
|
|
||||||
WebrtcInstance {
|
|
||||||
domain: String,
|
|
||||||
instance_pattern: String,
|
|
||||||
},
|
|
||||||
Addr {
|
|
||||||
contact_id: u32,
|
|
||||||
draft: Option<String>,
|
|
||||||
},
|
|
||||||
Url {
|
|
||||||
url: String,
|
|
||||||
},
|
|
||||||
Text {
|
|
||||||
text: String,
|
|
||||||
},
|
|
||||||
WithdrawVerifyContact {
|
|
||||||
contact_id: u32,
|
|
||||||
fingerprint: String,
|
|
||||||
invitenumber: String,
|
|
||||||
authcode: String,
|
|
||||||
},
|
|
||||||
WithdrawVerifyGroup {
|
|
||||||
grpname: String,
|
|
||||||
grpid: String,
|
|
||||||
contact_id: u32,
|
|
||||||
fingerprint: String,
|
|
||||||
invitenumber: String,
|
|
||||||
authcode: String,
|
|
||||||
},
|
|
||||||
ReviveVerifyContact {
|
|
||||||
contact_id: u32,
|
|
||||||
fingerprint: String,
|
|
||||||
invitenumber: String,
|
|
||||||
authcode: String,
|
|
||||||
},
|
|
||||||
ReviveVerifyGroup {
|
|
||||||
grpname: String,
|
|
||||||
grpid: String,
|
|
||||||
contact_id: u32,
|
|
||||||
fingerprint: String,
|
|
||||||
invitenumber: String,
|
|
||||||
authcode: String,
|
|
||||||
},
|
|
||||||
Login {
|
|
||||||
address: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Qr> for QrObject {
|
|
||||||
fn from(qr: Qr) -> Self {
|
|
||||||
match qr {
|
|
||||||
Qr::AskVerifyContact {
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
} => {
|
|
||||||
let contact_id = contact_id.to_u32();
|
|
||||||
let fingerprint = fingerprint.to_string();
|
|
||||||
QrObject::AskVerifyContact {
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Qr::AskVerifyGroup {
|
|
||||||
grpname,
|
|
||||||
grpid,
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
} => {
|
|
||||||
let contact_id = contact_id.to_u32();
|
|
||||||
let fingerprint = fingerprint.to_string();
|
|
||||||
QrObject::AskVerifyGroup {
|
|
||||||
grpname,
|
|
||||||
grpid,
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Qr::FprOk { contact_id } => {
|
|
||||||
let contact_id = contact_id.to_u32();
|
|
||||||
QrObject::FprOk { contact_id }
|
|
||||||
}
|
|
||||||
Qr::FprMismatch { contact_id } => {
|
|
||||||
let contact_id = contact_id.map(|contact_id| contact_id.to_u32());
|
|
||||||
QrObject::FprMismatch { contact_id }
|
|
||||||
}
|
|
||||||
Qr::FprWithoutAddr { fingerprint } => QrObject::FprWithoutAddr { fingerprint },
|
|
||||||
Qr::Account { domain } => QrObject::Account { domain },
|
|
||||||
Qr::WebrtcInstance {
|
|
||||||
domain,
|
|
||||||
instance_pattern,
|
|
||||||
} => QrObject::WebrtcInstance {
|
|
||||||
domain,
|
|
||||||
instance_pattern,
|
|
||||||
},
|
|
||||||
Qr::Addr { contact_id, draft } => {
|
|
||||||
let contact_id = contact_id.to_u32();
|
|
||||||
QrObject::Addr { contact_id, draft }
|
|
||||||
}
|
|
||||||
Qr::Url { url } => QrObject::Url { url },
|
|
||||||
Qr::Text { text } => QrObject::Text { text },
|
|
||||||
Qr::WithdrawVerifyContact {
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
} => {
|
|
||||||
let contact_id = contact_id.to_u32();
|
|
||||||
let fingerprint = fingerprint.to_string();
|
|
||||||
QrObject::WithdrawVerifyContact {
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Qr::WithdrawVerifyGroup {
|
|
||||||
grpname,
|
|
||||||
grpid,
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
} => {
|
|
||||||
let contact_id = contact_id.to_u32();
|
|
||||||
let fingerprint = fingerprint.to_string();
|
|
||||||
QrObject::WithdrawVerifyGroup {
|
|
||||||
grpname,
|
|
||||||
grpid,
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Qr::ReviveVerifyContact {
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
} => {
|
|
||||||
let contact_id = contact_id.to_u32();
|
|
||||||
let fingerprint = fingerprint.to_string();
|
|
||||||
QrObject::ReviveVerifyContact {
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Qr::ReviveVerifyGroup {
|
|
||||||
grpname,
|
|
||||||
grpid,
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
} => {
|
|
||||||
let contact_id = contact_id.to_u32();
|
|
||||||
let fingerprint = fingerprint.to_string();
|
|
||||||
QrObject::ReviveVerifyGroup {
|
|
||||||
grpname,
|
|
||||||
grpid,
|
|
||||||
contact_id,
|
|
||||||
fingerprint,
|
|
||||||
invitenumber,
|
|
||||||
authcode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Qr::Login { address, .. } => QrObject::Login { address },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use deltachat::reaction::Reactions;
|
|
||||||
use serde::Serialize;
|
|
||||||
use typescript_type_def::TypeDef;
|
|
||||||
|
|
||||||
/// Structure representing all reactions to a particular message.
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename = "Reactions", rename_all = "camelCase")]
|
|
||||||
pub struct JSONRPCReactions {
|
|
||||||
/// Map from a contact to it's reaction to message.
|
|
||||||
reactions_by_contact: BTreeMap<u32, Vec<String>>,
|
|
||||||
/// Unique reactions and their count
|
|
||||||
reactions: BTreeMap<String, u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Reactions> for JSONRPCReactions {
|
|
||||||
fn from(reactions: Reactions) -> Self {
|
|
||||||
let mut reactions_by_contact: BTreeMap<u32, Vec<String>> = BTreeMap::new();
|
|
||||||
let mut unique_reactions: BTreeMap<String, u32> = BTreeMap::new();
|
|
||||||
|
|
||||||
for contact_id in reactions.contacts() {
|
|
||||||
let reaction = reactions.get(contact_id);
|
|
||||||
if reaction.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let emojis: Vec<String> = reaction
|
|
||||||
.emojis()
|
|
||||||
.into_iter()
|
|
||||||
.map(|emoji| emoji.to_owned())
|
|
||||||
.collect();
|
|
||||||
reactions_by_contact.insert(contact_id.to_u32(), emojis.clone());
|
|
||||||
for emoji in emojis {
|
|
||||||
if let Some(x) = unique_reactions.get_mut(&emoji) {
|
|
||||||
*x += 1;
|
|
||||||
} else {
|
|
||||||
unique_reactions.insert(emoji, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONRPCReactions {
|
|
||||||
reactions_by_contact,
|
|
||||||
reactions: unique_reactions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
use deltachat::{
|
|
||||||
context::Context,
|
|
||||||
message::{Message, MsgId},
|
|
||||||
webxdc::WebxdcInfo,
|
|
||||||
};
|
|
||||||
use serde::Serialize;
|
|
||||||
use typescript_type_def::TypeDef;
|
|
||||||
|
|
||||||
use super::maybe_empty_string_to_option;
|
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
|
||||||
#[serde(rename = "WebxdcMessageInfo", rename_all = "camelCase")]
|
|
||||||
pub struct WebxdcMessageInfo {
|
|
||||||
/// The name of the app.
|
|
||||||
///
|
|
||||||
/// Defaults to the filename if not set in the manifest.
|
|
||||||
name: String,
|
|
||||||
/// App icon file name.
|
|
||||||
/// Defaults to an standard icon if nothing is set in the manifest.
|
|
||||||
///
|
|
||||||
/// To get the file, use dc_msg_get_webxdc_blob(). (not yet in jsonrpc, use rust api or cffi for it)
|
|
||||||
///
|
|
||||||
/// App icons should should be square,
|
|
||||||
/// the implementations will add round corners etc. as needed.
|
|
||||||
icon: String,
|
|
||||||
/// if the Webxdc represents a document, then this is the name of the document
|
|
||||||
document: Option<String>,
|
|
||||||
/// short string describing the state of the app,
|
|
||||||
/// sth. as "2 votes", "Highscore: 123",
|
|
||||||
/// can be changed by the apps
|
|
||||||
summary: Option<String>,
|
|
||||||
/// URL where the source code of the Webxdc and other information can be found;
|
|
||||||
/// defaults to an empty string.
|
|
||||||
/// Implementations may offer an menu or a button to open this URL.
|
|
||||||
source_code_url: Option<String>,
|
|
||||||
/// True if full internet access should be granted to the app.
|
|
||||||
internet_access: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WebxdcMessageInfo {
|
|
||||||
pub async fn get_for_message(
|
|
||||||
context: &Context,
|
|
||||||
instance_message_id: MsgId,
|
|
||||||
) -> anyhow::Result<Self> {
|
|
||||||
let message = Message::load_from_db(context, instance_message_id).await?;
|
|
||||||
let WebxdcInfo {
|
|
||||||
name,
|
|
||||||
icon,
|
|
||||||
document,
|
|
||||||
summary,
|
|
||||||
source_code_url,
|
|
||||||
internet_access,
|
|
||||||
} = message.get_webxdc_info(context).await?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
name,
|
|
||||||
icon,
|
|
||||||
document: maybe_empty_string_to_option(document),
|
|
||||||
summary: maybe_empty_string_to_option(summary),
|
|
||||||
source_code_url: maybe_empty_string_to_option(source_code_url),
|
|
||||||
internet_access,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
pub mod api;
|
|
||||||
pub use api::events;
|
|
||||||
pub use yerpc;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use async_channel::unbounded;
|
|
||||||
use futures::StreamExt;
|
|
||||||
use tempfile::TempDir;
|
|
||||||
use yerpc::{RpcClient, RpcSession};
|
|
||||||
|
|
||||||
use super::api::{Accounts, CommandApi};
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn basic_json_rpc_functionality() -> anyhow::Result<()> {
|
|
||||||
let tmp_dir = TempDir::new().unwrap().path().into();
|
|
||||||
let accounts = Accounts::new(tmp_dir).await?;
|
|
||||||
let api = CommandApi::new(accounts);
|
|
||||||
|
|
||||||
let (sender, mut receiver) = unbounded::<String>();
|
|
||||||
|
|
||||||
let (client, mut rx) = RpcClient::new();
|
|
||||||
let session = RpcSession::new(client, api);
|
|
||||||
tokio::spawn({
|
|
||||||
async move {
|
|
||||||
while let Some(message) = rx.next().await {
|
|
||||||
let message = serde_json::to_string(&message)?;
|
|
||||||
sender.send(message).await?;
|
|
||||||
}
|
|
||||||
let res: Result<(), anyhow::Error> = Ok(());
|
|
||||||
res
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
let request = r#"{"jsonrpc":"2.0","method":"add_account","params":[],"id":1}"#;
|
|
||||||
let response = r#"{"jsonrpc":"2.0","id":1,"result":1}"#;
|
|
||||||
session.handle_incoming(request).await;
|
|
||||||
let result = receiver.next().await;
|
|
||||||
println!("{:?}", result);
|
|
||||||
assert_eq!(result, Some(response.to_owned()));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let request = r#"{"jsonrpc":"2.0","method":"get_all_account_ids","params":[],"id":2}"#;
|
|
||||||
let response = r#"{"jsonrpc":"2.0","id":2,"result":[1]}"#;
|
|
||||||
session.handle_incoming(request).await;
|
|
||||||
let result = receiver.next().await;
|
|
||||||
println!("{:?}", result);
|
|
||||||
assert_eq!(result, Some(response.to_owned()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn test_batch_set_config() -> anyhow::Result<()> {
|
|
||||||
let tmp_dir = TempDir::new().unwrap().path().into();
|
|
||||||
let accounts = Accounts::new(tmp_dir).await?;
|
|
||||||
let api = CommandApi::new(accounts);
|
|
||||||
|
|
||||||
let (sender, mut receiver) = unbounded::<String>();
|
|
||||||
|
|
||||||
let (client, mut rx) = RpcClient::new();
|
|
||||||
let session = RpcSession::new(client, api);
|
|
||||||
tokio::spawn({
|
|
||||||
async move {
|
|
||||||
while let Some(message) = rx.next().await {
|
|
||||||
let message = serde_json::to_string(&message)?;
|
|
||||||
sender.send(message).await?;
|
|
||||||
}
|
|
||||||
let res: Result<(), anyhow::Error> = Ok(());
|
|
||||||
res
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
let request = r#"{"jsonrpc":"2.0","method":"add_account","params":[],"id":1}"#;
|
|
||||||
let response = r#"{"jsonrpc":"2.0","id":1,"result":1}"#;
|
|
||||||
session.handle_incoming(request).await;
|
|
||||||
let result = receiver.next().await;
|
|
||||||
assert_eq!(result, Some(response.to_owned()));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let request = r#"{"jsonrpc":"2.0","method":"batch_set_config","id":2,"params":[1,{"addr":"","mail_user":"","mail_pw":"","mail_server":"","mail_port":"","mail_security":"","imap_certificate_checks":"","send_user":"","send_pw":"","send_server":"","send_port":"","send_security":"","smtp_certificate_checks":"","socks5_enabled":"0","socks5_host":"","socks5_port":"","socks5_user":"","socks5_password":""}]}"#;
|
|
||||||
let response = r#"{"jsonrpc":"2.0","id":2,"result":null}"#;
|
|
||||||
session.handle_incoming(request).await;
|
|
||||||
let result = receiver.next().await;
|
|
||||||
assert_eq!(result, Some(response.to_owned()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
use std::net::SocketAddr;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use axum::{extract::ws::WebSocketUpgrade, response::Response, routing::get, Extension, Router};
|
|
||||||
use yerpc::axum::handle_ws_rpc;
|
|
||||||
use yerpc::{RpcClient, RpcSession};
|
|
||||||
|
|
||||||
mod api;
|
|
||||||
use api::events::event_to_json_rpc_notification;
|
|
||||||
use api::{Accounts, CommandApi};
|
|
||||||
|
|
||||||
const DEFAULT_PORT: u16 = 20808;
|
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread")]
|
|
||||||
async fn main() -> Result<(), std::io::Error> {
|
|
||||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
|
||||||
|
|
||||||
let path = std::env::var("DC_ACCOUNTS_PATH").unwrap_or_else(|_| "./accounts".to_string());
|
|
||||||
let port = std::env::var("DC_PORT")
|
|
||||||
.map(|port| port.parse::<u16>().expect("DC_PORT must be a number"))
|
|
||||||
.unwrap_or(DEFAULT_PORT);
|
|
||||||
log::info!("Starting with accounts directory `{path}`.");
|
|
||||||
let accounts = Accounts::new(PathBuf::from(&path)).await.unwrap();
|
|
||||||
let state = CommandApi::new(accounts);
|
|
||||||
|
|
||||||
let app = Router::new()
|
|
||||||
.route("/ws", get(handler))
|
|
||||||
.layer(Extension(state.clone()));
|
|
||||||
|
|
||||||
tokio::spawn(async move {
|
|
||||||
state.accounts.read().await.start_io().await;
|
|
||||||
});
|
|
||||||
|
|
||||||
let addr = SocketAddr::from(([127, 0, 0, 1], port));
|
|
||||||
log::info!("JSON-RPC WebSocket server listening on {}", addr);
|
|
||||||
axum::Server::bind(&addr)
|
|
||||||
.serve(app.into_make_service())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handler(ws: WebSocketUpgrade, Extension(api): Extension<CommandApi>) -> Response {
|
|
||||||
let (client, out_receiver) = RpcClient::new();
|
|
||||||
let session = RpcSession::new(client.clone(), api.clone());
|
|
||||||
tokio::spawn(async move {
|
|
||||||
let events = api.accounts.read().await.get_event_emitter();
|
|
||||||
while let Some(event) = events.recv().await {
|
|
||||||
let event = event_to_json_rpc_notification(event);
|
|
||||||
client.send_notification("event", Some(event)).await.ok();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
handle_ws_rpc(ws, out_receiver, session).await
|
|
||||||
}
|
|
||||||
9
deltachat-jsonrpc/typescript/.gitignore
vendored
@@ -1,9 +0,0 @@
|
|||||||
node_modules
|
|
||||||
dist
|
|
||||||
test_dist
|
|
||||||
coverage
|
|
||||||
yarn.lock
|
|
||||||
package-lock.json
|
|
||||||
docs
|
|
||||||
accounts
|
|
||||||
generated
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
node_modules
|
|
||||||
accounts
|
|
||||||
docs
|
|
||||||
coverage
|
|
||||||
yarn*
|
|
||||||
package-lock.json
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
coverage
|
|
||||||
dist
|
|
||||||
generated
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./src/lib.js";
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>DeltaChat JSON-RPC example</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: monospace;
|
|
||||||
background: black;
|
|
||||||
color: grey;
|
|
||||||
}
|
|
||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 3fr 1fr;
|
|
||||||
grid-template-areas: "a a" "b c";
|
|
||||||
}
|
|
||||||
.message {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
#header {
|
|
||||||
grid-area: a;
|
|
||||||
color: white;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
#header a {
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
#main {
|
|
||||||
grid-area: b;
|
|
||||||
color: green;
|
|
||||||
}
|
|
||||||
#main h2,
|
|
||||||
#main h3 {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
#side {
|
|
||||||
grid-area: c;
|
|
||||||
color: #777;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script type="module" src="dist/example.bundle.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>DeltaChat JSON-RPC example</h1>
|
|
||||||
<div class="grid">
|
|
||||||
<div id="header"></div>
|
|
||||||
<div id="main"></div>
|
|
||||||
<div id="side"><h2>log</h2></div>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Tip: open the dev console and use the client with
|
|
||||||
<code>window.client</code>
|
|
||||||
</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
import { DcEvent, DeltaChat } from "../deltachat.js";
|
|
||||||
|
|
||||||
var SELECTED_ACCOUNT = 0;
|
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", (_event) => {
|
|
||||||
(window as any).selectDeltaAccount = (id: string) => {
|
|
||||||
SELECTED_ACCOUNT = Number(id);
|
|
||||||
window.dispatchEvent(new Event("account-changed"));
|
|
||||||
};
|
|
||||||
console.log("launch run script...");
|
|
||||||
run().catch((err) => console.error("run failed", err));
|
|
||||||
});
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
const $main = document.getElementById("main")!;
|
|
||||||
const $side = document.getElementById("side")!;
|
|
||||||
const $head = document.getElementById("header")!;
|
|
||||||
|
|
||||||
const client = new DeltaChat("ws://localhost:20808/ws");
|
|
||||||
|
|
||||||
(window as any).client = client.rpc;
|
|
||||||
|
|
||||||
client.on("ALL", (accountId, event) => {
|
|
||||||
onIncomingEvent(accountId, event);
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("account-changed", async (_event: Event) => {
|
|
||||||
listChatsForSelectedAccount();
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all([loadAccountsInHeader(), listChatsForSelectedAccount()]);
|
|
||||||
|
|
||||||
async function loadAccountsInHeader() {
|
|
||||||
console.log("load accounts");
|
|
||||||
const accounts = await client.rpc.getAllAccounts();
|
|
||||||
console.log("accounts loaded", accounts);
|
|
||||||
for (const account of accounts) {
|
|
||||||
if (account.type === "Configured") {
|
|
||||||
write(
|
|
||||||
$head,
|
|
||||||
`<a href="#" onclick="selectDeltaAccount(${account.id})">
|
|
||||||
${account.id}: ${account.addr!}
|
|
||||||
</a> `
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
write(
|
|
||||||
$head,
|
|
||||||
`<a href="#">
|
|
||||||
${account.id}: (unconfigured)
|
|
||||||
</a> `
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function listChatsForSelectedAccount() {
|
|
||||||
clear($main);
|
|
||||||
const selectedAccount = SELECTED_ACCOUNT;
|
|
||||||
const info = await client.rpc.getAccountInfo(selectedAccount);
|
|
||||||
if (info.type !== "Configured") {
|
|
||||||
return write($main, "Account is not configured");
|
|
||||||
}
|
|
||||||
write($main, `<h2>${info.addr!}</h2>`);
|
|
||||||
const chats = await client.rpc.getChatlistEntries(
|
|
||||||
selectedAccount,
|
|
||||||
0,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
for (const [chatId, _messageId] of chats) {
|
|
||||||
const chat = await client.rpc.getFullChatById(
|
|
||||||
selectedAccount,
|
|
||||||
chatId
|
|
||||||
);
|
|
||||||
write($main, `<h3>${chat.name}</h3>`);
|
|
||||||
const messageIds = await client.rpc.getMessageIds(
|
|
||||||
selectedAccount,
|
|
||||||
chatId,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
const messages = await client.rpc.getMessages(
|
|
||||||
selectedAccount,
|
|
||||||
messageIds
|
|
||||||
);
|
|
||||||
for (const [_messageId, message] of Object.entries(messages)) {
|
|
||||||
write($main, `<p>${message.text}</p>`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onIncomingEvent(accountId: number, event: DcEvent) {
|
|
||||||
write(
|
|
||||||
$side,
|
|
||||||
`
|
|
||||||
<p class="message">
|
|
||||||
[<strong>${event.type}</strong> on account ${accountId}]<br>
|
|
||||||
<em>f1:</em> ${JSON.stringify(
|
|
||||||
Object.assign({}, event, { type: undefined })
|
|
||||||
)}
|
|
||||||
</p>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function write(el: HTMLElement, html: string) {
|
|
||||||
el.innerHTML += html;
|
|
||||||
}
|
|
||||||
function clear(el: HTMLElement) {
|
|
||||||
el.innerHTML = "";
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { DeltaChat } from "../dist/deltachat.js";
|
|
||||||
|
|
||||||
run().catch(console.error);
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
const delta = new DeltaChat('ws://localhost:20808/ws');
|
|
||||||
delta.on("event", (event) => {
|
|
||||||
console.log("event", event.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
const email = process.argv[2]
|
|
||||||
const password = process.argv[3]
|
|
||||||
if (!email || !password) throw new Error('USAGE: node node-add-account.js <EMAILADDRESS> <PASSWORD>')
|
|
||||||
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);
|
|
||||||
await delta.rpc.setConfig(id, "mail_pw", password);
|
|
||||||
console.log('configuration updated')
|
|
||||||
await delta.rpc.configure(id)
|
|
||||||
console.log('account configured!')
|
|
||||||
|
|
||||||
const accounts = await delta.rpc.getAllAccounts();
|
|
||||||
console.log("accounts", accounts);
|
|
||||||
console.log("waiting for events...")
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { DeltaChat } from "../dist/deltachat.js";
|
|
||||||
|
|
||||||
run().catch(console.error);
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
const delta = new DeltaChat();
|
|
||||||
delta.on("event", (event) => {
|
|
||||||
console.log("event", event.data);
|
|
||||||
});
|
|
||||||
|
|
||||||
const accounts = await delta.rpc.getAllAccounts();
|
|
||||||
console.log("accounts", accounts);
|
|
||||||
console.log("waiting for events...")
|
|
||||||
}
|
|
||||||