Compare commits
1 Commits
py-1.81.0
...
fix/setupc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
601b284ed8 |
210
.circleci/config.yml
Normal file
@@ -0,0 +1,210 @@
|
||||
version: 2.1
|
||||
executors:
|
||||
default:
|
||||
docker:
|
||||
- image: filecoin/rust:latest
|
||||
working_directory: /mnt/crate
|
||||
|
||||
restore-workspace: &restore-workspace
|
||||
attach_workspace:
|
||||
at: /mnt
|
||||
|
||||
restore-cache: &restore-cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- cargo-v1-{{ 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
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: true
|
||||
# TODO: move into image
|
||||
- run:
|
||||
name: Install Docker client
|
||||
command: |
|
||||
set -x
|
||||
VER="18.09.2"
|
||||
curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz
|
||||
tar -xz -C /tmp -f /tmp/docker-$VER.tgz
|
||||
mv /tmp/docker/* /usr/bin
|
||||
- run:
|
||||
name: Test (<< parameters.target >>)
|
||||
command: TARGET=<< parameters.target >> ci/run.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-v1-{{ 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
|
||||
- run: rm -rf .git
|
||||
- persist_to_workspace:
|
||||
root: /mnt
|
||||
paths:
|
||||
- crate
|
||||
- save_cache:
|
||||
key: cargo-v1-{{ 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/run.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_test_docs_wheel:
|
||||
machine: True
|
||||
steps:
|
||||
- checkout
|
||||
# - run: docker pull deltachat/doxygen
|
||||
- run: docker pull deltachat/coredeps
|
||||
- run:
|
||||
name: build docs, run tests and build wheels
|
||||
command: ci_scripts/ci_run.sh
|
||||
environment:
|
||||
TESTS: 1
|
||||
DOCS: 1
|
||||
|
||||
- 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
|
||||
|
||||
clippy:
|
||||
executor: default
|
||||
steps:
|
||||
- *restore-workspace
|
||||
- *restore-cache
|
||||
- run:
|
||||
name: Run cargo clippy
|
||||
command: cargo clippy --all
|
||||
|
||||
|
||||
workflows:
|
||||
version: 2.1
|
||||
|
||||
test:
|
||||
jobs:
|
||||
- build_test_docs_wheel
|
||||
- upload_docs_wheels:
|
||||
requires:
|
||||
- build_test_docs_wheel
|
||||
- cargo_fetch
|
||||
- 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
|
||||
3
.gitattributes
vendored
@@ -12,6 +12,3 @@ test-data/* text=false
|
||||
*.gif 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/**'
|
||||
required: ['CHANGELOG.md']
|
||||
fail:
|
||||
- do: checks
|
||||
status: 'action_required'
|
||||
payload:
|
||||
title: Changlog might need an update
|
||||
summary: "Check if CHANGELOG.md needs an update or add #skip-changelog to the PR description."
|
||||
143
.github/workflows/ci.yml
vendored
@@ -1,143 +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@v2
|
||||
- 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@v1
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
run_clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
components: clippy
|
||||
override: true
|
||||
- name: Cache rust cargo artifacts
|
||||
uses: swatinem/rust-cache@v1
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --workspace --tests --examples --benches
|
||||
|
||||
docs:
|
||||
name: Rust doc comments
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
- 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@v1
|
||||
- name: Rustdoc
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
args: --document-private-items --no-deps
|
||||
|
||||
build_and_test:
|
||||
name: Build and test
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# Currently used Rust version, same as in `rust-toolchain` file.
|
||||
- os: ubuntu-latest
|
||||
rust: 1.60.0
|
||||
python: 3.9
|
||||
- os: windows-latest
|
||||
rust: 1.60.0
|
||||
python: false # Python bindings compilation on Windows is not supported.
|
||||
|
||||
# Minimum Supported Rust Version = 1.56.0
|
||||
#
|
||||
# Minimum Supported Python Version = 3.7
|
||||
# This is the minimum version for which manylinux Python wheels are
|
||||
# built.
|
||||
- os: ubuntu-latest
|
||||
rust: 1.56.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@v1
|
||||
|
||||
- name: check
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --all --bins --examples --tests --features repl --benches
|
||||
|
||||
- name: tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --all
|
||||
|
||||
- name: install python
|
||||
if: ${{ matrix.python }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: install tox
|
||||
if: ${{ matrix.python }}
|
||||
run: pip install tox
|
||||
|
||||
- name: build C library
|
||||
if: ${{ matrix.python }}
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: -p deltachat_ffi
|
||||
|
||||
- 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
|
||||
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}}
|
||||
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 ::set-output name=prid::$PULLREQUEST_ID
|
||||
- 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/"
|
||||
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@v1
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v1
|
||||
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/"
|
||||
170
.github/workflows/node-package.yml
vendored
@@ -1,170 +0,0 @@
|
||||
name: 'node.js'
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
|
||||
jobs:
|
||||
prebuild:
|
||||
name: 'Tests & Prebuild'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-latest, windows-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
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@v2
|
||||
with:
|
||||
path: |
|
||||
${{ env.APPDATA }}/npm-cache
|
||||
~/.npm
|
||||
key: ${{ matrix.os }}-node-${{ hashFiles('**/package.json') }}
|
||||
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v2
|
||||
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
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
cd node
|
||||
npm run test
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
- name: Run tests on Windows, except lint
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
cd node
|
||||
npm run test:mocha
|
||||
env:
|
||||
DCC_NEW_TMP_EMAIL: ${{ secrets.DCC_NEW_TMP_EMAIL }}
|
||||
- name: Build Prebuild
|
||||
run: |
|
||||
cd node
|
||||
npm run prebuildify
|
||||
tar -zcvf "${{ matrix.os }}.tar.gz" -C prebuilds .
|
||||
|
||||
- name: Upload Prebuild
|
||||
uses: actions/upload-artifact@v1
|
||||
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@v2
|
||||
- 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('::set-output name=prid::' + '${{ github.ref }}'.split('/')[2])"
|
||||
else
|
||||
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
|
||||
- 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 prebuilds
|
||||
tar -xvzf ubuntu-18.04/ubuntu-18.04.tar.gz -C prebuilds
|
||||
tar -xvzf macos-latest/macos-latest.tar.gz -C prebuilds
|
||||
tar -xvzf windows-latest/windows-latest.tar.gz -C prebuilds
|
||||
tree prebuilds
|
||||
- name: install dependencies without running scripts
|
||||
run: |
|
||||
npm install --ignore-scripts
|
||||
- name: build typescript part
|
||||
run: |
|
||||
npm run build:bindings:ts
|
||||
- name: package
|
||||
shell: bash
|
||||
run: |
|
||||
npm pack .
|
||||
ls -lah
|
||||
mv $(find deltachat-node-*) deltachat-node-${{ steps.prepare.outputs.prid }}.tar.gz
|
||||
- name: Upload Prebuild
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: deltachat-node.tgz
|
||||
path: deltachat-node-${{ steps.prepare.outputs.prid }}.tar.gz
|
||||
# Upload to download.delta.chat/node/preview/
|
||||
- name: Upload deltachat-node preview to download.delta.chat/node/preview/
|
||||
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-${{ steps.prepare.outputs.prid }}.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/deltachat-node-${{ steps.prepare.outputs.prid }}
|
||||
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-${{ steps.prepare.outputs.prid }}.tar.gz "${{ secrets.USERNAME }}"@"download.delta.chat":"/var/www/html/download/node/"
|
||||
32
.github/workflows/repl.yml
vendored
@@ -1,32 +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@master
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.50.0
|
||||
override: true
|
||||
|
||||
- name: build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --example repl --features repl,vendored
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: repl.exe
|
||||
path: 'target/debug/examples/repl.exe'
|
||||
28
.github/workflows/upload-docs.yml
vendored
@@ -1,28 +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@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- 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/"
|
||||
|
||||
28
.github/workflows/upload-ffi-docs.yml
vendored
@@ -1,28 +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@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- 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/"
|
||||
|
||||
18
.gitignore
vendored
@@ -1,6 +1,5 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
/build
|
||||
|
||||
# ignore vi temporaries
|
||||
*~
|
||||
@@ -17,26 +16,9 @@ python/.tox
|
||||
*.egg-info
|
||||
__pycache__
|
||||
python/src/deltachat/capi*.so
|
||||
python/.venv/
|
||||
|
||||
python/liveconfig*
|
||||
|
||||
# ignore doxgen generated files
|
||||
deltachat-ffi/html
|
||||
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/
|
||||
|
||||
42
.npmignore
@@ -1,42 +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
|
||||
1631
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
|
||||
|
||||
# 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)
|
||||
4845
Cargo.lock
generated
160
Cargo.toml
@@ -1,92 +1,59 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.81.0"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2021"
|
||||
license = "MPL-2.0"
|
||||
rust-version = "1.56"
|
||||
|
||||
[profile.dev]
|
||||
debug = 0
|
||||
panic = 'abort'
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
panic = 'abort'
|
||||
version = "1.0.0-alpha.4"
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MPL"
|
||||
|
||||
[dependencies]
|
||||
deltachat_derive = { path = "./deltachat_derive" }
|
||||
|
||||
ansi_term = { version = "0.12.1", optional = true }
|
||||
anyhow = "1"
|
||||
async-imap = { git = "https://github.com/async-email/async-imap" }
|
||||
async-native-tls = { version = "0.3" }
|
||||
async-smtp = { git = "https://github.com/async-email/async-smtp", branch="master", default-features=false, features = ["smtp-transport", "socks5"] }
|
||||
async-std-resolver = "0.21"
|
||||
async-std = { version = "1" }
|
||||
async-tar = { version = "0.4", default-features=false }
|
||||
async-trait = "0.1"
|
||||
backtrace = "0.3"
|
||||
base64 = "0.13"
|
||||
bitflags = "1.3"
|
||||
chrono = "0.4"
|
||||
dirs = { version = "4", optional=true }
|
||||
email = { git = "https://github.com/deltachat/rust-email", branch = "master" }
|
||||
encoded-words = { git = "https://github.com/async-email/encoded-words", branch="master" }
|
||||
escaper = "0.1"
|
||||
futures = "0.3"
|
||||
hex = "0.4.0"
|
||||
image = { version = "0.24.1", 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.13"
|
||||
native-tls = "0.2"
|
||||
num_cpus = "1.13"
|
||||
num-derive = "0.3"
|
||||
num-traits = "0.2"
|
||||
once_cell = "1.10.0"
|
||||
libc = "0.2.51"
|
||||
pgp = { version = "0.2", default-features = false }
|
||||
hex = "0.3.2"
|
||||
sha2 = "0.8.0"
|
||||
rand = "0.6.5"
|
||||
phf = { git = "https://github.com/sfackler/rust-phf", rev = "0d00821", features = ["macros"] }
|
||||
smallvec = "0.6.9"
|
||||
reqwest = "0.9.15"
|
||||
num-derive = "0.2.5"
|
||||
num-traits = "0.2.6"
|
||||
native-tls = "0.2.3"
|
||||
lettre = "0.9.0"
|
||||
imap = { git = "https://github.com/jonhoo/rust-imap", rev = "281d2eb8ab50dc656ceff2ae749ca5045f334e15" }
|
||||
mmime = { git = "https://github.com/dignifiedquire/mmime", rev = "bccd2c2" }
|
||||
base64 = "0.10"
|
||||
charset = "0.1"
|
||||
percent-encoding = "2.0"
|
||||
pgp = { version = "0.7", default-features = false }
|
||||
pretty_env_logger = { version = "0.4", optional = true }
|
||||
quick-xml = "0.22"
|
||||
r2d2 = "0.8"
|
||||
r2d2_sqlite = "0.20"
|
||||
rand = "0.7"
|
||||
regex = "1.5"
|
||||
rusqlite = { version = "0.27", features = ["sqlcipher"] }
|
||||
rust-hsluv = "0.1"
|
||||
rustyline = { version = "9", optional = true }
|
||||
sanitize-filename = "0.3"
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sha-1 = "0.10"
|
||||
sha2 = "0.10"
|
||||
smallvec = "1"
|
||||
strum = "0.24"
|
||||
strum_macros = "0.24"
|
||||
surf = { version = "2.3", default-features = false, features = ["h1-client"] }
|
||||
thiserror = "1"
|
||||
toml = "0.5"
|
||||
url = "2"
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
fast-socks5 = "0.4"
|
||||
humansize = "1"
|
||||
qrcodegen = "1.7.0"
|
||||
tagger = "4.3.3"
|
||||
textwrap = "0.15.0"
|
||||
zip = { version = "0.6.2", default-features = false, features = ["deflate"] }
|
||||
serde_json = "1.0"
|
||||
chrono = "0.4.6"
|
||||
failure = "0.1.5"
|
||||
failure_derive = "0.1.5"
|
||||
# TODO: make optional
|
||||
rustyline = "4.1.0"
|
||||
lazy_static = "1.4.0"
|
||||
regex = "1.1.6"
|
||||
rusqlite = { version = "0.20", features = ["bundled"] }
|
||||
r2d2_sqlite = "0.12.0"
|
||||
r2d2 = "0.8.5"
|
||||
strum = "0.15.0"
|
||||
strum_macros = "0.15.0"
|
||||
thread-local-object = "0.1.0"
|
||||
backtrace = "0.3.33"
|
||||
byteorder = "1.3.1"
|
||||
itertools = "0.8.0"
|
||||
image-meta = "0.1.0"
|
||||
quick-xml = "0.15.0"
|
||||
escaper = "0.1.0"
|
||||
bitflags = "1.1.0"
|
||||
jetscii = "0.4.4"
|
||||
debug_stub_derive = "0.3.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ansi_term = "0.12.0"
|
||||
async-std = { version = "1", features = ["unstable", "attributes"] }
|
||||
criterion = { version = "0.3.4", features = ["async_std"] }
|
||||
futures-lite = "1.12"
|
||||
log = "0.4"
|
||||
pretty_env_logger = "0.4"
|
||||
proptest = { version = "1", default-features = false, features = ["std"] }
|
||||
tempfile = "3"
|
||||
tempfile = "3.0"
|
||||
pretty_assertions = "0.6.1"
|
||||
pretty_env_logger = "0.3.0"
|
||||
proptest = "0.9.4"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
@@ -97,41 +64,14 @@ members = [
|
||||
[[example]]
|
||||
name = "simple"
|
||||
path = "examples/simple.rs"
|
||||
required-features = ["repl"]
|
||||
|
||||
[[example]]
|
||||
name = "repl"
|
||||
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]
|
||||
default = ["vendored"]
|
||||
internals = []
|
||||
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"]
|
||||
default = ["nightly", "ringbuf"]
|
||||
vendored = ["native-tls/vendored", "reqwest/default-tls-vendored"]
|
||||
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
|
||||
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
|
||||
==================================
|
||||
|
||||
|
||||
60
README.md
@@ -1,27 +1,26 @@
|
||||
# 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
|
||||
|
||||
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
|
||||
|
||||
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):
|
||||
|
||||
@@ -81,16 +80,6 @@ For more commands type:
|
||||
> 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
|
||||
|
||||
```sh
|
||||
@@ -100,16 +89,6 @@ $ cargo test --all
|
||||
$ cargo build -p deltachat_ffi --release
|
||||
```
|
||||
|
||||
## Debugging environment variables
|
||||
|
||||
- `DCC_IMAP_DEBUG`: if set IMAP protocol commands and responses will be
|
||||
printed
|
||||
|
||||
- `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
|
||||
|
||||
Some tests are expensive and marked with `#[ignore]`, to run these
|
||||
@@ -122,24 +101,9 @@ $ cargo test -- --ignored
|
||||
|
||||
- `vendored`: When using Openssl for TLS, this bundles a vendored version.
|
||||
- `nightly`: Enable nightly only performance and security related features.
|
||||
- `ringbuf`: Enable the use of [`slice_deque`](https://github.com/gnzlbg/slice_deque) in pgp.
|
||||
|
||||
## Language bindings and frontend projects
|
||||
|
||||
Language bindings are available for:
|
||||
|
||||
- **C** \[[📂 source](./deltachat-ffi) | [📚 docs](https://c.delta.chat)\]
|
||||
- **Node.js** \[[📂 source](./node) | [📦 npm](https://www.npmjs.com/package/deltachat-node) | [📚 docs](https://js.delta.chat)\]
|
||||
- **Python** \[[📂 source](./python) | [📦 pypi](https://pypi.org/project/deltachat) | [📚 docs](https://py.delta.chat)\]
|
||||
- **Go** \[[📂 source](https://github.com/deltachat/go-deltachat/)\]
|
||||
- **Free Pascal** \[[📂 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**
|
||||
[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/
|
||||
[appveyor-shield]: https://ci.appveyor.com/api/projects/status/lqpegel3ld4ipxj8/branch/master?style=flat-square
|
||||
[appveyor]: https://ci.appveyor.com/project/dignifiedquire/deltachat-core-rust/branch/master
|
||||
|
||||
6
Xargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[dependencies.std]
|
||||
features = ["panic-unwind"]
|
||||
|
||||
# if using `cargo test`
|
||||
[dependencies.test]
|
||||
stage = 1
|
||||
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
|
||||
|
||||
cache:
|
||||
- target
|
||||
- C:\Users\appveyor\.cargo\registry
|
||||
|
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 |
@@ -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,37 +0,0 @@
|
||||
use async_std::task::block_on;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use deltachat::contact::Contact;
|
||||
use deltachat::context::Context;
|
||||
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.into(), id).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) {
|
||||
c.bench_function("create 500 contacts", |b| {
|
||||
b.iter(|| block_on(async { address_book_benchmark(black_box(500), black_box(0)).await }))
|
||||
});
|
||||
|
||||
c.bench_function("create 100 contacts and read it 1000 times", |b| {
|
||||
b.iter(|| block_on(async { address_book_benchmark(black_box(100), black_box(1000)).await }))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
||||
@@ -1,26 +0,0 @@
|
||||
use async_std::path::PathBuf;
|
||||
use async_std::task::block_on;
|
||||
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").into();
|
||||
|
||||
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| {
|
||||
b.iter(|| block_on(async { create_accounts(black_box(1)).await }))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
||||
@@ -1,40 +0,0 @@
|
||||
use async_std::path::Path;
|
||||
|
||||
use criterion::async_executor::AsyncStdExecutor;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
use deltachat::chat::{self, ChatId};
|
||||
use deltachat::chatlist::Chatlist;
|
||||
use deltachat::context::Context;
|
||||
|
||||
async fn get_chat_msgs_benchmark(dbfile: &Path, chats: &[ChatId]) {
|
||||
let id = 100;
|
||||
let context = Context::new(dbfile.into(), id).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 chats: Vec<_> = async_std::task::block_on(async {
|
||||
let context = Context::new((&path).into(), 100).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(AsyncStdExecutor)
|
||||
.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,27 +0,0 @@
|
||||
use criterion::async_executor::AsyncStdExecutor;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
use deltachat::chatlist::Chatlist;
|
||||
use deltachat::context::Context;
|
||||
|
||||
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 context =
|
||||
async_std::task::block_on(async { Context::new(path.into(), 100).await.unwrap() });
|
||||
c.bench_function("chatlist:try_load (Get Chatlist)", |b| {
|
||||
b.to_async(AsyncStdExecutor)
|
||||
.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,84 +0,0 @@
|
||||
use async_std::{path::PathBuf, task::block_on};
|
||||
use criterion::{
|
||||
async_executor::AsyncStdExecutor, black_box, criterion_group, criterion_main, BatchSize,
|
||||
Criterion,
|
||||
};
|
||||
use deltachat::{
|
||||
config::Config,
|
||||
context::Context,
|
||||
dc_receive_imf::dc_receive_imf,
|
||||
imex::{imex, ImexMode},
|
||||
};
|
||||
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,
|
||||
);
|
||||
dc_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.into(), id).await.unwrap();
|
||||
|
||||
let backup: PathBuf = std::env::current_dir()
|
||||
.unwrap()
|
||||
.join("delta-chat-backup.tar")
|
||||
.into();
|
||||
if backup.exists().await {
|
||||
println!("Importing backup");
|
||||
imex(&context, ImexMode::ImportBackup, &backup, 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| {
|
||||
b.to_async(AsyncStdExecutor).iter_batched(
|
||||
|| block_on(create_context()),
|
||||
|context| recv_all_emails(black_box(context)),
|
||||
BatchSize::LargeInput,
|
||||
);
|
||||
});
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
||||
@@ -1,29 +0,0 @@
|
||||
use async_std::task::block_on;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use deltachat::context::Context;
|
||||
use std::path::Path;
|
||||
|
||||
async fn search_benchmark(path: impl AsRef<Path>) {
|
||||
let dbfile = path.as_ref();
|
||||
let id = 100;
|
||||
let context = Context::new(dbfile.into(), id).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") {
|
||||
c.bench_function("search hello", |b| {
|
||||
b.iter(|| block_on(async { search_benchmark(black_box(&path)).await }))
|
||||
});
|
||||
} else {
|
||||
println!("env var not set: DELTACHAT_BENCHMARK_DATABASE");
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
||||
@@ -1,9 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
shopt -s huponexit
|
||||
|
||||
#export RUST_TEST_THREADS=1
|
||||
export RUST_TEST_THREADS=1
|
||||
export RUST_BACKTRACE=1
|
||||
export RUSTFLAGS='--deny warnings'
|
||||
export OPT="--target=$TARGET"
|
||||
@@ -32,15 +31,16 @@ fi
|
||||
if [[ $NORUN == "1" ]]; then
|
||||
export CARGO_SUBCMD="build"
|
||||
else
|
||||
export CARGO_SUBCMD="test --all"
|
||||
export CARGO_SUBCMD="test"
|
||||
export OPT="${OPT} "
|
||||
export OPT_RELEASE="${OPT_RELEASE} "
|
||||
export OPT_RELEASE_IGNORED="${OPT_RELEASE} -- --ignored"
|
||||
fi
|
||||
|
||||
# Run all the test configurations
|
||||
# RUSTC_WRAPPER=SCCACHE seems to destroy parallelism / prolong the test
|
||||
unset RUSTC_WRAPPER
|
||||
# Run all the test configurations:
|
||||
$CARGO_CMD $CARGO_SUBCMD $OPT
|
||||
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE
|
||||
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE_IGNORED
|
||||
|
||||
# Build the ffi lib
|
||||
$CARGO_CMD $CARGO_SUBCMD $OPT_FFI_RELEASE
|
||||
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`
|
||||
|
||||
|
||||
|
||||
22
ci_scripts/ci_run.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
# perform CI jobs on PRs and after merges to master.
|
||||
# triggered from .circleci/config.yml
|
||||
|
||||
set -e -x
|
||||
|
||||
export BRANCH=${CIRCLE_BRANCH:-test7}
|
||||
|
||||
# run doxygen on c-source (needed by later doc-generation steps).
|
||||
# XXX modifies the host filesystem docs/xml and docs/html directories
|
||||
# XXX which you can then only remove with sudo as they belong to root
|
||||
|
||||
# XXX we don't do doxygen doc generation with Rust anymore, needs to be
|
||||
# substituted with rust-docs
|
||||
#if [ -n "$DOCS" ] ; then
|
||||
# docker run --rm -it -v $PWD:/mnt -w /mnt/docs deltachat/doxygen doxygen
|
||||
#fi
|
||||
|
||||
# run everything else inside docker (TESTS, DOCS, WHEELS)
|
||||
docker run -e DCC_PY_LIVECONFIG -e BRANCH -e TESTS -e DOCS \
|
||||
--rm -it -v $(pwd):/mnt -w /mnt \
|
||||
deltachat/coredeps ci_scripts/run_all.sh
|
||||
|
||||
50
ci_scripts/ci_upload.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/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
|
||||
|
||||
#DOXYDOCDIR=${1:?directory where doxygen docs to be found}
|
||||
PYDOCDIR=${1:?directory with python docs}
|
||||
WHEELHOUSEDIR=${2:?directory with pre-built wheels}
|
||||
|
||||
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
|
||||
#rsync -avz \
|
||||
# -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
|
||||
# "$DOXYDOCDIR/html/" \
|
||||
# delta@py.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
|
||||
@@ -1,21 +1,25 @@
|
||||
FROM quay.io/pypa/manylinux2014_aarch64
|
||||
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 the openssl crate
|
||||
ADD deps/build_perl.sh /builder/build_perl.sh
|
||||
RUN rm /usr/bin/perl
|
||||
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.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
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
12
ci_scripts/docker-coredeps/deps/build_perl.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
PERL_VERSION=5.28.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
|
||||
11
ci_scripts/docker-coredeps/deps/build_rust.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -x
|
||||
|
||||
# Install Rust
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-07-10 -y
|
||||
export PATH=/root/.cargo/bin:$PATH
|
||||
rustc --version
|
||||
|
||||
# remove some 300-400 MB that we don't need for automated builds
|
||||
rm -rf /root/.rustup/toolchains/nightly-2019-07-10-x86_64-unknown-linux-gnu/share/
|
||||
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
|
||||
55
ci_scripts/run_all.sh
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/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
|
||||
# cargo test --all --all-features
|
||||
|
||||
# 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 /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
|
||||
|
||||
if [ -n "$TESTS" ]; then
|
||||
|
||||
pushd python
|
||||
# prepare a clean tox run
|
||||
rm -rf tests/__pycache__
|
||||
rm -rf src/deltachat/__pycache__
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# run tox
|
||||
# XXX we don't run liveconfig tests because they hang sometimes
|
||||
# see https://github.com/deltachat/deltachat-core-rust/issues/331
|
||||
# unset DCC_PY_LIVECONFIG
|
||||
|
||||
tox --workdir "$TOXWORKDIR" -e lint,py35,py36,py37,auditwheels -- -k "not qr"
|
||||
tox --workdir "$TOXWORKDIR" -e py35,py36,py37 -- -k "qr"
|
||||
popd
|
||||
fi
|
||||
|
||||
|
||||
if [ -n "$DOCS" ]; then
|
||||
echo -----------------------
|
||||
echo generating python docs
|
||||
echo -----------------------
|
||||
(cd python && tox --workdir "$TOXWORKDIR" -e doc)
|
||||
fi
|
||||
@@ -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,11 +1,11 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.81.0"
|
||||
version = "1.0.0-alpha.4"
|
||||
description = "Deltachat FFI"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
edition = "2018"
|
||||
readme = "README.md"
|
||||
license = "MPL-2.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
keywords = ["deltachat", "chat", "openpgp", "email", "encryption"]
|
||||
categories = ["cryptography", "std", "email"]
|
||||
@@ -17,16 +17,11 @@ crate-type = ["cdylib", "staticlib"]
|
||||
[dependencies]
|
||||
deltachat = { path = "../", default-features = false }
|
||||
libc = "0.2"
|
||||
human-panic = "1"
|
||||
num-traits = "0.2"
|
||||
serde_json = "1.0"
|
||||
async-std = "1"
|
||||
anyhow = "1"
|
||||
thiserror = "1"
|
||||
rand = "0.7"
|
||||
human-panic = "1.0.1"
|
||||
num-traits = "0.2.6"
|
||||
|
||||
[features]
|
||||
default = ["vendored"]
|
||||
default = ["vendored", "nightly", "ringbuf"]
|
||||
vendored = ["deltachat/vendored"]
|
||||
nightly = ["deltachat/nightly"]
|
||||
|
||||
ringbuf = ["deltachat/ringbuf"]
|
||||
|
||||
@@ -236,6 +236,12 @@ TAB_SIZE = 4
|
||||
|
||||
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
|
||||
# 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
|
||||
@@ -583,7 +589,7 @@ SORT_MEMBERS_CTORS_1ST = NO
|
||||
# appear in their defined order.
|
||||
# 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
|
||||
# fully-qualified names, including namespaces. If set to NO, the class list will
|
||||
|
||||
@@ -4,16 +4,4 @@ div.fragment {
|
||||
background-color: #e0e0e0;
|
||||
border: 0;
|
||||
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
|
||||
|
||||
## Installation
|
||||
|
||||
see `Installing libdeltachat system wide` in [../README.md](../README.md)
|
||||
|
||||
## Documentation
|
||||
|
||||
To generate the C Interface documentation,
|
||||
|
||||
@@ -19,17 +19,15 @@ fn main() {
|
||||
include_str!("deltachat.pc.in"),
|
||||
name = "deltachat",
|
||||
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(),
|
||||
libs_priv = libs_priv,
|
||||
prefix = env::var("PREFIX").unwrap_or_else(|_| "/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()),
|
||||
prefix = env::var("PREFIX").unwrap_or("/usr/local".to_string()),
|
||||
);
|
||||
|
||||
fs::create_dir_all(target_path.join("pkgconfig")).unwrap();
|
||||
fs::File::create(target_path.join("pkgconfig").join("deltachat.pc"))
|
||||
.unwrap()
|
||||
.write_all(pkg_config.as_bytes())
|
||||
.write_all(&pkg_config.as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
prefix={prefix}
|
||||
libdir={libdir}
|
||||
includedir={includedir}
|
||||
libdir=${{prefix}}/lib
|
||||
includedir=${{prefix}}/include
|
||||
|
||||
Name: {name}
|
||||
Description: {description}
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
use crate::chat::ChatItem;
|
||||
use crate::constants::DC_MSG_ID_DAYMARKER;
|
||||
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>),
|
||||
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::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::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::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::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<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,245 +0,0 @@
|
||||
//! # Legacy generic return values for C API.
|
||||
|
||||
use crate::message::MessageState;
|
||||
use crate::qr::Qr;
|
||||
use crate::summary::{Summary, SummaryPrefix};
|
||||
use anyhow::Error;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// 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 { .. } => None,
|
||||
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),
|
||||
},
|
||||
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) => 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,
|
||||
},
|
||||
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(),
|
||||
},
|
||||
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,
|
||||
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
@@ -1,421 +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::dc_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 super::*;
|
||||
use libc::{free, strcmp};
|
||||
|
||||
#[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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
[package]
|
||||
name = "deltachat_derive"
|
||||
version = "2.0.0"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
version = "0.1.0"
|
||||
authors = ["Dmitry Bogatov <KAction@debian.org>"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "1"
|
||||
quote = "1"
|
||||
syn = "0.14.4"
|
||||
quote = "0.6.3"
|
||||
|
||||
@@ -3,6 +3,7 @@ extern crate proc_macro;
|
||||
|
||||
use crate::proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn;
|
||||
|
||||
// For now, assume (not check) that these macroses are applied to enum without
|
||||
// data. If this assumption is violated, compiler error will point to
|
||||
@@ -35,11 +36,7 @@ pub fn from_sql_derive(input: TokenStream) -> TokenStream {
|
||||
impl rusqlite::types::FromSql for #name {
|
||||
fn column_result(col: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
|
||||
let inner = rusqlite::types::FromSql::column_result(col)?;
|
||||
if let Some(value) = num_traits::FromPrimitive::from_i64(inner) {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(rusqlite::types::FromSqlError::OutOfRange(inner))
|
||||
}
|
||||
Ok(num_traits::FromPrimitive::from_i64(inner).unwrap_or_default())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
AEAP MVP
|
||||
========
|
||||
|
||||
Changes to the UIs
|
||||
------------------
|
||||
|
||||
- The secondary self addresses (see below) are shown in the UI, but not editable.
|
||||
|
||||
- When the user changed the email address in the configure screen, show a dialog to the user, either directly explaining things or with a link to the FAQ (see "Other" below)
|
||||
|
||||
Changes in the core
|
||||
-------------------
|
||||
|
||||
- We have one primary self address and any number of secondary self addresses. `is_self_addr()` checks all of them.
|
||||
|
||||
- If the user does a reconfigure and changes the email address, the previous address is added as a secondary self address.
|
||||
|
||||
- don't forget to deduplicate secondary self addresses in case the user switches back and forth between addresses).
|
||||
|
||||
- The key stays the same.
|
||||
|
||||
- No changes for 1:1 chats, there simply is a new one
|
||||
|
||||
- When we send a message to a group, and the primary address is not a member of a group, but a secondary address is:
|
||||
|
||||
Add Chat-Group-Member-Removed=<old address> and Chat-Group-Member-Added=<new address> headers to this message
|
||||
|
||||
- On the receiving side, make sure that we accept this (even in verified groups) if the message is signed and the key stayed the same
|
||||
|
||||
Other
|
||||
-----
|
||||
|
||||
- The user is responsible that messages to the old address arrive at the new address, for example by configuring the old provider to forward all emails to the new one.
|
||||
@@ -1,111 +0,0 @@
|
||||
|
||||
Problem: missing eventual group consistency
|
||||
--------------------------------------------
|
||||
|
||||
If group members are concurrently adding new members,
|
||||
the new members will miss each other's additions, example:
|
||||
|
||||
1. Alice and Bob are in a two-member group
|
||||
|
||||
2. Then Alice adds Carol, while concurrently Bob adds Doris
|
||||
|
||||
Right now, the group has inconsistent memberships:
|
||||
|
||||
- Alice and Carol see a (Alice, Carol, Bob) group
|
||||
|
||||
- Bob and Doris see a (Bob, Doris, Alice)
|
||||
|
||||
This then leads to "sender is unknown" messages in the chat,
|
||||
for example when Alice receives a message from Doris,
|
||||
or when Bob receives a message from Carol.
|
||||
|
||||
There are also other sources for group membership inconsistency:
|
||||
|
||||
- leaving/deleting/adding in larger groups, while being offline,
|
||||
increases chances for inconsistent group membership
|
||||
|
||||
- dropped group-membership messages
|
||||
|
||||
- group-membership messages landing in "Spam"
|
||||
|
||||
|
||||
Note that all these problems (can) also happen with verified groups,
|
||||
then raising "false alarms" which could lure people to ignore such issues.
|
||||
|
||||
IOW, it's clear we need to do something about it to improve overall
|
||||
reliability in group-settings.
|
||||
|
||||
|
||||
|
||||
Solution: replay group modification messages on inconsistencies
|
||||
------------------------------------------------------------------
|
||||
|
||||
For brevity let's abbreviate "group membership modification" as **GMM**.
|
||||
|
||||
Delta chat has explicit GMM messages, typically encrypted to the group members
|
||||
as seen by the device that sends the GMM. The `Spec <https://github.com/deltachat/deltachat-core-rust/blob/master/spec.md#add-and-remove-members>`_ details the Mime headers and format.
|
||||
|
||||
If we detect membership inconsistencies we can resend relevant GMM messages
|
||||
to the respective chat. The receiving devices can process those GMM messages
|
||||
as if it would be an incoming message. If for example they have already seen
|
||||
the Message-ID of the GMM message, they will ignore the message. It's
|
||||
probably useful to record GMM message in their original MIME-format and
|
||||
not invent a new recording format. Few notes on three aspects:
|
||||
|
||||
- **group-membership-tracking**: All valid GMM messages are persisted in
|
||||
their full raw (signed/encrypted?) MIME-format in the DB. Note that GMM messages
|
||||
already are in the msgs table, and there is a mime_header column which we could
|
||||
extend to contain the raw Mime GMM message.
|
||||
|
||||
- **consistency_checking**: If an incoming GMM has a member list which is
|
||||
not consistent with our own view, broadcast a "Group-Member-Correction"
|
||||
message to all members containing a multipart list of GMMs.
|
||||
|
||||
- **correcting_memberships**: Upon receiving a Group-Member-Correction
|
||||
message we pass the contained GMMs to the "incoming mail pipeline"
|
||||
(without **consistency_checking** them, to avoid recursion issues)
|
||||
|
||||
|
||||
Alice/Carol and Bob/Doris getting on the same page
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Recall that Alice/Carol and Bob/Doris had a differening view of
|
||||
group membership. With the proposed solution, when Bob receives
|
||||
Alice's "Carol added" message, he will notice that Alice (and thus
|
||||
also carol) did not know about Doris. Bob's device sends a
|
||||
"Chat-Group-Member-Correction" message containing his own GMM
|
||||
when adding Doris. Therefore, the group's membership is healed
|
||||
for everyone in a single broadcast message.
|
||||
|
||||
Alice might also send a Group-member-Correction message,
|
||||
so there is a second chance that the group gets to know all GMMs.
|
||||
|
||||
Note, for example, that if for some reason Bobs and Carols provider
|
||||
drop GMM messages between them (spam) that Alice and Doris can heal
|
||||
it by resending GMM messages whenever they detect them to be out of sync.
|
||||
|
||||
|
||||
Discussions of variants
|
||||
++++++++++++++++++++++++
|
||||
|
||||
- instead of acting on GMM messages we could send corrections
|
||||
for any received message that addresses inconsistent group members but
|
||||
a) this could delay group-membership healing
|
||||
b) could lead to a lot of members sending corrections
|
||||
c) means we might rely on "To-Addresses" which we also like to strike
|
||||
at least for protected chats.
|
||||
|
||||
- instead of broadcasting correction messages we could only send it to
|
||||
the sender of the inconsistent member-added/removed message.
|
||||
A receiver of such a correction message would then need to forward
|
||||
the message to the members it thinks also have an inconsistent view.
|
||||
This sounds complicated and error-prone. Concretely, if Alice
|
||||
receives Bob's "Member-added: Doris" message, then Alice
|
||||
broadcasting the correction message with "Member-added: Carol"
|
||||
would reach all four members, healing group consistency in one step.
|
||||
If Bob meanwhile receives Alice's "Member-Added: Carol" message,
|
||||
Bob would broadcast a correction message to all four members as well.
|
||||
(Imagine a situation where Alice/Bob added Carol/Doris
|
||||
while both being in an offline or bad-connection situation).
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
|
||||
simplify/streamline mark-seen/delete/move/send-mdn job handling
|
||||
---------------------------------------------------------------
|
||||
|
||||
Idea: Introduce a new "msgwork" sql table that looks very
|
||||
much like the jobs table but has a primary key "msgid"
|
||||
and no job id and no foreign-id anymore. This opens up
|
||||
bulk-processing by looking at the whole table and combining
|
||||
flag-setting to reduce imap-roundtrips and select-folder calls.
|
||||
|
||||
Concretely, these IMAP jobs:
|
||||
|
||||
DeleteMsgOnImap
|
||||
MarkseenMsgOnImap
|
||||
MoveMsg
|
||||
|
||||
Would be replaced by a few per-message columns in the new msgwork table:
|
||||
|
||||
- needs_mark_seen: (bool) message shall be marked as seen on imap
|
||||
- needs_to_move: (bool) message should be moved to mvbox_folder
|
||||
- deletion_time: (target_time or 0) message shall be deleted at specified time
|
||||
- needs_send_mdn: (bool) MDN shall be sent
|
||||
|
||||
The various places that currently add the (replaced) jobs
|
||||
would now add/modify the respective message record in the message-work table.
|
||||
|
||||
Looking at a single message-work entry conceptually looks like this::
|
||||
|
||||
if msg.server_uid==0:
|
||||
return RetryLater # nothing can be done without server_uid
|
||||
|
||||
if msg.deletion_time > current_time:
|
||||
imap.mark_delete(msg) # might trigger early exit with a RetryLater/Failed
|
||||
clear(needs_deletion)
|
||||
clear(mark_seen)
|
||||
|
||||
if needs_mark_seen:
|
||||
imap.mark_seen(msg) # might trigger early exit with a RetryLater/Failed
|
||||
clear(needs_mark_seen)
|
||||
|
||||
if needs_send_mdn:
|
||||
schedule_smtp_send_mdn(msg)
|
||||
clear(needs_send_mdn)
|
||||
|
||||
if any_flag_set():
|
||||
retrylater
|
||||
# remove msgwork entry from table
|
||||
|
||||
|
||||
Notes/Questions:
|
||||
|
||||
- it's unclear how much we need per-message retry-time tracking/backoff
|
||||
|
||||
- drafting bulk processing algo is useful before
|
||||
going for the implementation, i.e. including select_folder calls etc.
|
||||
|
||||
- maybe it's better to not have bools for the flags but
|
||||
|
||||
0 (no change)
|
||||
1 (set the imap flag)
|
||||
2 (clear the imap flag)
|
||||
|
||||
and design such that we can cover all imap flags.
|
||||
|
||||
- It might not be neccessary to keep needs_send_mdn state in this table
|
||||
if this can be decided rather when we succeed with mark_seen/mark_delete.
|
||||
@@ -1,198 +0,0 @@
|
||||
# Webxdc Developer Reference
|
||||
|
||||
(This document may eventually be merged with the [webxdc guidebook](https://deltachat.github.io/webxdc_docs/), where you may currently find other useful information.)
|
||||
|
||||
## Webxdc File Format
|
||||
|
||||
- a **Webxdc** is a **ZIP-file** with the extension `.xdc`
|
||||
- the ZIP-file must use the default compression methods as of RFC 1950,
|
||||
this is "Deflate" or "Store"
|
||||
- the ZIP-file must contain at least the file `index.html`
|
||||
- if the Webxdc is started, `index.html` is opened in a restricted webview
|
||||
that allow accessing resources only from the ZIP-file
|
||||
|
||||
|
||||
## Webxdc API
|
||||
|
||||
There are some additional APIs available once `webxdc.js` is included
|
||||
(the file will be provided by the concrete implementations,
|
||||
no need to add `webxdc.js` to your ZIP-file):
|
||||
|
||||
```html
|
||||
<script src="webxdc.js"></script>
|
||||
```
|
||||
|
||||
### sendUpdate()
|
||||
|
||||
```js
|
||||
window.webxdc.sendUpdate(update, descr);
|
||||
```
|
||||
|
||||
A Webxdc is usually shared in a chat and run independently on each peer.
|
||||
To get a shared state, the peers use `sendUpdate()` to send updates to each other.
|
||||
|
||||
- `update`: an object with the following properties:
|
||||
- `update.payload`: any javascript primitive, array or object.
|
||||
- `update.info`: optional, short, informational message that will be added to the chat,
|
||||
eg. "Alice voted" or "Bob scored 123 in MyGame";
|
||||
usually only one line of text is shown,
|
||||
use this option sparingly to not spam the chat.
|
||||
- `update.document`: optional, name of the document in edit,
|
||||
must not be used eg. in games where the Webxdc does not create documents
|
||||
- `update.summary`: optional, short text, shown beside Webxdc icon;
|
||||
it is recommended to use some aggregated value, eg. "8 votes", "Highscore: 123"
|
||||
|
||||
- `descr`: short, human-readable description what this update is about.
|
||||
this is shown eg. as a fallback text in an email program.
|
||||
|
||||
All peers, including the sending one,
|
||||
will receive the update by the callback given to `setUpdateListener()`.
|
||||
|
||||
There are situations where the user cannot send messages to a chat,
|
||||
eg. if the webxdc instance comes as a contact request or if the user has left a group.
|
||||
In these cases, you can still call `sendUpdate()`,
|
||||
however, the update won't be sent to other peers
|
||||
and you won't get the update by `setUpdateListener()`.
|
||||
|
||||
|
||||
### setUpdateListener()
|
||||
|
||||
```js
|
||||
let promise = window.webxdc.setUpdateListener((update) => {}, serial);
|
||||
```
|
||||
|
||||
With `setUpdateListener()` you define a callback that receives the updates
|
||||
sent by `sendUpdate()`. The callback is called for updates sent by you or other peers.
|
||||
The `serial` specifies the last serial that you know about (defaults to 0).
|
||||
The returned promise resolves when the listener has processed all the update messages known at the time when `setUpdateListener` was called.
|
||||
|
||||
Each `update` which is passed to the callback comes with the following properties:
|
||||
|
||||
- `update.payload`: equals the payload given to `sendUpdate()`
|
||||
|
||||
- `update.serial`: the serial number of this update.
|
||||
Serials are larger `0` and newer serials have higher numbers.
|
||||
There may be gaps in the serials
|
||||
and it is not guaranteed that the next serial is exactly incremented by one.
|
||||
|
||||
- `update.max_serial`: the maximum serial currently known.
|
||||
If `max_serial` equals `serial` this update is the last update (until new network messages arrive).
|
||||
|
||||
- `update.info`: optional, short, informational message (see `sendUpdate()`)
|
||||
|
||||
- `update.document`: optional, document name as set by the sender, (see `sendUpdate()`),
|
||||
implementations show the document name eg. beside the app icon or in the title bar
|
||||
|
||||
- `update.summary`: optional, short text, shown beside icon (see `sendUpdate()`)
|
||||
|
||||
|
||||
### selfAddr
|
||||
|
||||
```js
|
||||
window.webxdc.selfAddr
|
||||
```
|
||||
|
||||
Property with the peer's own address.
|
||||
This is esp. useful if you want to differ between different peers -
|
||||
just send the address along with the payload,
|
||||
and, if needed, compare the payload addresses against selfAddr() later on.
|
||||
|
||||
|
||||
### selfName
|
||||
|
||||
```js
|
||||
window.webxdc.selfName
|
||||
```
|
||||
|
||||
Property with the peer's own name.
|
||||
This is name chosen by the user in their settings,
|
||||
if there is nothing set, that defaults to the peer's address.
|
||||
|
||||
|
||||
## manifest.toml
|
||||
|
||||
If the ZIP-file contains a `manifest.toml` in its root directory,
|
||||
some basic information are read and used from there.
|
||||
|
||||
the `manifest.toml` has the following format
|
||||
|
||||
```toml
|
||||
name = "My Name"
|
||||
source_code_url = "https://example.org/orga/repo"
|
||||
```
|
||||
|
||||
- `name` - The name of the Webxdc.
|
||||
If no name is set or if there is no manifest, the filename is used as the Webxdc name.
|
||||
|
||||
- `source_code_url` - Optional URL where the source code of the Webxdc and maybe other information can be found.
|
||||
UI may make the url accessible via a "Help" menu in the Webxdc window.
|
||||
|
||||
|
||||
## Webxdc Icon
|
||||
|
||||
If the ZIP-root contains an `icon.png` or `icon.jpg`,
|
||||
these files are used as the icon for the Webxdc.
|
||||
The icon should be a square at reasonable width/height;
|
||||
round corners etc. will be added by the implementations as needed.
|
||||
If no icon is set, a default icon will be used.
|
||||
|
||||
|
||||
## Webxdc Examples
|
||||
|
||||
The following example shows an input field and every input is show on all peers.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<script src="webxdc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<input id="input" type="text"/>
|
||||
<a href="" onclick="sendMsg(); return false;">Send</a>
|
||||
<p id="output"></p>
|
||||
<script>
|
||||
|
||||
function sendMsg() {
|
||||
msg = document.getElementById("input").value;
|
||||
window.webxdc.sendUpdate({payload: msg}, 'Someone typed "'+msg+'".');
|
||||
}
|
||||
|
||||
function receiveUpdate(update) {
|
||||
document.getElementById('output').innerHTML += update.payload + "<br>";
|
||||
}
|
||||
|
||||
window.webxdc.setUpdateListener(receiveUpdate, 0);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
[Webxdc Development Tool](https://github.com/deltachat/webxdc-dev)
|
||||
offers an **Webxdc Simulator** that can be used in many browsers without any installation needed.
|
||||
You can also use that repository as a template for your own Webxdc -
|
||||
just clone and start adapting things to your need.
|
||||
|
||||
|
||||
### Advanced Examples
|
||||
|
||||
- [2048](https://github.com/adbenitez/2048.xdc)
|
||||
- [Draw](https://github.com/adbenitez/draw.xdc)
|
||||
- [Poll](https://github.com/r10s/webxdc-poll/)
|
||||
- [Tic Tac Toe](https://github.com/Simon-Laux/tictactoe.xdc)
|
||||
- Even more with [Topic #webxdc on Github](https://github.com/topics/webxdc) or in the [webxdc GitHub organization](https://github.com/webxdc)
|
||||
|
||||
|
||||
## Closing Remarks
|
||||
|
||||
- older devices might not have the newest js features in their webview,
|
||||
you may want to transpile your code down to an older js version eg. with https://babeljs.io
|
||||
- viewport and scaling features are implementation specific,
|
||||
if you want to have an explicit behavior, you can add eg.
|
||||
`<meta name="viewport" content="initial-scale=1; user-scalable=no">` to your Webxdc
|
||||
- the `<title>` tag should not be used and its content is usually not displayed;
|
||||
instead, use the `name` property from `manifest.toml`
|
||||
- there are tons of ideas for enhancements of the API and the file format,
|
||||
eg. in the future, we will may define icon- and manifest-files,
|
||||
allow to aggregate the state or add metadata.
|
||||
@@ -6,118 +6,215 @@
|
||||
|
||||
#[macro_use]
|
||||
extern crate deltachat;
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate rusqlite;
|
||||
|
||||
use std::borrow::Cow::{self, Borrowed, Owned};
|
||||
use std::io::{self, Write};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use ansi_term::Color;
|
||||
use anyhow::{bail, Error};
|
||||
use async_std::path::Path;
|
||||
use deltachat::chat::ChatId;
|
||||
use deltachat::config;
|
||||
use deltachat::configure::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::job::*;
|
||||
use deltachat::oauth2::*;
|
||||
use deltachat::securejoin::*;
|
||||
use deltachat::EventType;
|
||||
use log::{error, info, warn};
|
||||
use deltachat::x::*;
|
||||
use deltachat::Event;
|
||||
use rustyline::completion::{Completer, FilenameCompleter, Pair};
|
||||
use rustyline::config::OutputStreamType;
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
|
||||
use rustyline::hint::{Hinter, HistoryHinter};
|
||||
use rustyline::validate::Validator;
|
||||
use rustyline::{
|
||||
Cmd, CompletionType, Config, Context as RustyContext, EditMode, Editor, Helper, KeyEvent,
|
||||
Cmd, CompletionType, Config, Context as RustyContext, EditMode, Editor, Helper, KeyPress,
|
||||
};
|
||||
|
||||
mod cmdline;
|
||||
use self::cmdline::*;
|
||||
use deltachat::qr_code_generator::get_securejoin_qr_svg;
|
||||
use std::fs;
|
||||
|
||||
/// Event Handler
|
||||
fn receive_event(event: EventType) {
|
||||
let yellow = Color::Yellow.normal();
|
||||
// Event Handler
|
||||
|
||||
fn receive_event(_context: &Context, event: Event) -> libc::uintptr_t {
|
||||
match event {
|
||||
EventType::Info(msg) => {
|
||||
Event::GetString { .. } => {}
|
||||
Event::Info(msg) => {
|
||||
/* do not show the event as this would fill the screen */
|
||||
info!("{}", msg);
|
||||
println!("{}", msg);
|
||||
}
|
||||
EventType::SmtpConnected(msg) => {
|
||||
info!("[SMTP_CONNECTED] {}", msg);
|
||||
Event::SmtpConnected(msg) => {
|
||||
println!("[DC_EVENT_SMTP_CONNECTED] {}", msg);
|
||||
}
|
||||
EventType::ImapConnected(msg) => {
|
||||
info!("[IMAP_CONNECTED] {}", msg);
|
||||
Event::ImapConnected(msg) => {
|
||||
println!("[DC_EVENT_IMAP_CONNECTED] {}", msg);
|
||||
}
|
||||
EventType::SmtpMessageSent(msg) => {
|
||||
info!("[SMTP_MESSAGE_SENT] {}", msg);
|
||||
Event::SmtpMessageSent(msg) => {
|
||||
println!("[DC_EVENT_SMTP_MESSAGE_SENT] {}", msg);
|
||||
}
|
||||
EventType::Warning(msg) => {
|
||||
warn!("{}", msg);
|
||||
Event::Warning(msg) => {
|
||||
println!("[Warning] {}", msg);
|
||||
}
|
||||
EventType::Error(msg) => {
|
||||
error!("{}", msg);
|
||||
Event::Error(msg) => {
|
||||
println!("\x1b[31m[DC_EVENT_ERROR] {}\x1b[0m", msg);
|
||||
}
|
||||
EventType::ErrorSelfNotInGroup(msg) => {
|
||||
error!("[SELF_NOT_IN_GROUP] {}", msg);
|
||||
Event::ErrorNetwork(msg) => {
|
||||
println!("\x1b[31m[DC_EVENT_ERROR_NETWORK] msg={}\x1b[0m", msg);
|
||||
}
|
||||
EventType::MsgsChanged { chat_id, msg_id } => {
|
||||
info!(
|
||||
"{}",
|
||||
yellow.paint(format!(
|
||||
"Received MSGS_CHANGED(chat_id={}, msg_id={})",
|
||||
chat_id, msg_id,
|
||||
))
|
||||
Event::ErrorSelfNotInGroup(msg) => {
|
||||
println!("\x1b[31m[DC_EVENT_ERROR_SELF_NOT_IN_GROUP] {}\x1b[0m", msg);
|
||||
}
|
||||
Event::MsgsChanged { chat_id, msg_id } => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_MSGS_CHANGED(chat_id={}, msg_id={})}}\n\x1b[0m",
|
||||
chat_id, msg_id,
|
||||
);
|
||||
}
|
||||
EventType::ContactsChanged(_) => {
|
||||
info!("{}", yellow.paint("Received CONTACTS_CHANGED()"));
|
||||
Event::ContactsChanged(_) => {
|
||||
print!("\x1b[33m{{Received DC_EVENT_CONTACTS_CHANGED()}}\n\x1b[0m");
|
||||
}
|
||||
EventType::LocationChanged(contact) => {
|
||||
info!(
|
||||
"{}",
|
||||
yellow.paint(format!("Received LOCATION_CHANGED(contact={:?})", contact))
|
||||
Event::LocationChanged(contact) => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_LOCATION_CHANGED(contact={:?})}}\n\x1b[0m",
|
||||
contact,
|
||||
);
|
||||
}
|
||||
EventType::ConfigureProgress { progress, comment } => {
|
||||
if let Some(comment) = comment {
|
||||
info!(
|
||||
"{}",
|
||||
yellow.paint(format!(
|
||||
"Received CONFIGURE_PROGRESS({} ‰, {})",
|
||||
progress, comment
|
||||
))
|
||||
);
|
||||
} else {
|
||||
info!(
|
||||
"{}",
|
||||
yellow.paint(format!("Received CONFIGURE_PROGRESS({} ‰)", progress))
|
||||
);
|
||||
}
|
||||
}
|
||||
EventType::ImexProgress(progress) => {
|
||||
info!(
|
||||
"{}",
|
||||
yellow.paint(format!("Received IMEX_PROGRESS({} ‰)", progress))
|
||||
Event::ConfigureProgress(progress) => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_CONFIGURE_PROGRESS({} ‰)}}\n\x1b[0m",
|
||||
progress,
|
||||
);
|
||||
}
|
||||
EventType::ImexFileWritten(file) => {
|
||||
info!(
|
||||
"{}",
|
||||
yellow.paint(format!("Received IMEX_FILE_WRITTEN({})", file.display()))
|
||||
Event::ImexProgress(progress) => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_IMEX_PROGRESS({} ‰)}}\n\x1b[0m",
|
||||
progress,
|
||||
);
|
||||
}
|
||||
EventType::ChatModified(chat) => {
|
||||
info!(
|
||||
"{}",
|
||||
yellow.paint(format!("Received CHAT_MODIFIED({})", chat))
|
||||
Event::ImexFileWritten(file) => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_IMEX_FILE_WRITTEN({})}}\n\x1b[0m",
|
||||
file.display()
|
||||
);
|
||||
}
|
||||
Event::ChatModified(chat) => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_CHAT_MODIFIED({})}}\n\x1b[0m",
|
||||
chat
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
info!("Received {:?}", event);
|
||||
print!("\x1b[33m{{Received {:?}}}\n\x1b[0m", event);
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
// Threads for waiting for messages and for jobs
|
||||
|
||||
lazy_static! {
|
||||
static ref HANDLE: Arc<Mutex<Option<Handle>>> = Arc::new(Mutex::new(None));
|
||||
static ref IS_RUNNING: AtomicBool = AtomicBool::new(true);
|
||||
}
|
||||
|
||||
struct Handle {
|
||||
handle_imap: Option<std::thread::JoinHandle<()>>,
|
||||
handle_mvbox: Option<std::thread::JoinHandle<()>>,
|
||||
handle_sentbox: Option<std::thread::JoinHandle<()>>,
|
||||
handle_smtp: Option<std::thread::JoinHandle<()>>,
|
||||
}
|
||||
|
||||
macro_rules! while_running {
|
||||
($code:block) => {
|
||||
if IS_RUNNING.load(Ordering::Relaxed) {
|
||||
$code
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn start_threads(c: Arc<RwLock<Context>>) {
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("Starting threads");
|
||||
IS_RUNNING.store(true, Ordering::Relaxed);
|
||||
|
||||
let ctx = c.clone();
|
||||
let handle_imap = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_imap_jobs(&ctx.read().unwrap());
|
||||
perform_imap_fetch(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
let context = ctx.read().unwrap();
|
||||
perform_imap_idle(&context);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let ctx = c.clone();
|
||||
let handle_mvbox = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_mvbox_fetch(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
perform_mvbox_idle(&ctx.read().unwrap());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let ctx = c.clone();
|
||||
let handle_sentbox = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_sentbox_fetch(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
perform_sentbox_idle(&ctx.read().unwrap());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let ctx = c;
|
||||
let handle_smtp = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_smtp_jobs(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
perform_smtp_idle(&ctx.read().unwrap());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
*HANDLE.clone().lock().unwrap() = Some(Handle {
|
||||
handle_imap: Some(handle_imap),
|
||||
handle_mvbox: Some(handle_mvbox),
|
||||
handle_sentbox: Some(handle_sentbox),
|
||||
handle_smtp: Some(handle_smtp),
|
||||
});
|
||||
}
|
||||
|
||||
fn stop_threads(context: &Context) {
|
||||
if let Some(ref mut handle) = *HANDLE.clone().lock().unwrap() {
|
||||
println!("Stopping threads");
|
||||
IS_RUNNING.store(false, Ordering::Relaxed);
|
||||
|
||||
interrupt_imap_idle(context);
|
||||
interrupt_mvbox_idle(context);
|
||||
interrupt_sentbox_idle(context);
|
||||
interrupt_smtp_idle(context);
|
||||
|
||||
handle.handle_imap.take().unwrap().join().unwrap();
|
||||
handle.handle_mvbox.take().unwrap().join().unwrap();
|
||||
handle.handle_sentbox.take().unwrap().join().unwrap();
|
||||
handle.handle_smtp.take().unwrap().join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// === The main loop
|
||||
@@ -141,7 +238,7 @@ impl Completer for DcHelper {
|
||||
}
|
||||
}
|
||||
|
||||
const IMEX_COMMANDS: [&str; 12] = [
|
||||
const IMEX_COMMANDS: [&'static str; 12] = [
|
||||
"initiate-key-transfer",
|
||||
"get-setupcodebegin",
|
||||
"continue-key-transfer",
|
||||
@@ -156,27 +253,28 @@ const IMEX_COMMANDS: [&str; 12] = [
|
||||
"stop",
|
||||
];
|
||||
|
||||
const DB_COMMANDS: [&str; 10] = [
|
||||
const DB_COMMANDS: [&'static str; 11] = [
|
||||
"info",
|
||||
"open",
|
||||
"close",
|
||||
"set",
|
||||
"get",
|
||||
"oauth2",
|
||||
"configure",
|
||||
"connect",
|
||||
"disconnect",
|
||||
"connectivity",
|
||||
"maybenetwork",
|
||||
"housekeeping",
|
||||
];
|
||||
|
||||
const CHAT_COMMANDS: [&str; 36] = [
|
||||
const CHAT_COMMANDS: [&'static str; 24] = [
|
||||
"listchats",
|
||||
"listarchived",
|
||||
"chat",
|
||||
"createchat",
|
||||
"createchatbymsg",
|
||||
"creategroup",
|
||||
"createbroadcast",
|
||||
"createprotected",
|
||||
"createverified",
|
||||
"addmember",
|
||||
"removemember",
|
||||
"groupname",
|
||||
@@ -189,63 +287,36 @@ const CHAT_COMMANDS: [&str; 36] = [
|
||||
"send",
|
||||
"sendimage",
|
||||
"sendfile",
|
||||
"sendhtml",
|
||||
"sendsyncmsg",
|
||||
"sendupdate",
|
||||
"videochat",
|
||||
"draft",
|
||||
"listmedia",
|
||||
"archive",
|
||||
"unarchive",
|
||||
"pin",
|
||||
"unpin",
|
||||
"mute",
|
||||
"unmute",
|
||||
"protect",
|
||||
"unprotect",
|
||||
"delchat",
|
||||
"accept",
|
||||
"blockchat",
|
||||
];
|
||||
const MESSAGE_COMMANDS: [&str; 8] = [
|
||||
const MESSAGE_COMMANDS: [&'static str; 8] = [
|
||||
"listmsgs",
|
||||
"msginfo",
|
||||
"listfresh",
|
||||
"forward",
|
||||
"resend",
|
||||
"markseen",
|
||||
"star",
|
||||
"unstar",
|
||||
"delmsg",
|
||||
"download",
|
||||
];
|
||||
const CONTACT_COMMANDS: [&str; 9] = [
|
||||
const CONTACT_COMMANDS: [&'static str; 6] = [
|
||||
"listcontacts",
|
||||
"listverified",
|
||||
"addcontact",
|
||||
"contactinfo",
|
||||
"delcontact",
|
||||
"cleanupcontacts",
|
||||
"block",
|
||||
"unblock",
|
||||
"listblocked",
|
||||
];
|
||||
const MISC_COMMANDS: [&str; 11] = [
|
||||
"getqr",
|
||||
"getqrsvg",
|
||||
"getbadqr",
|
||||
"checkqr",
|
||||
"joinqr",
|
||||
"fileinfo",
|
||||
"clear",
|
||||
"exit",
|
||||
"quit",
|
||||
"help",
|
||||
"estimatedeletion",
|
||||
const MISC_COMMANDS: [&'static str; 9] = [
|
||||
"getqr", "getbadqr", "checkqr", "event", "fileinfo", "clear", "exit", "quit", "help",
|
||||
];
|
||||
|
||||
impl Hinter for DcHelper {
|
||||
type Hint = String;
|
||||
|
||||
fn hint(&self, line: &str, pos: usize, ctx: &RustyContext<'_>) -> Option<Self::Hint> {
|
||||
fn hint(&self, line: &str, pos: usize, ctx: &RustyContext<'_>) -> Option<String> {
|
||||
if !line.is_empty() {
|
||||
for &cmds in &[
|
||||
&IMEX_COMMANDS[..],
|
||||
@@ -266,11 +337,12 @@ impl Hinter for DcHelper {
|
||||
}
|
||||
}
|
||||
|
||||
static COLORED_PROMPT: &str = "\x1b[1;32m> \x1b[0m";
|
||||
static COLORED_PROMPT: &'static str = "\x1b[1;32m> \x1b[0m";
|
||||
static PROMPT: &'static str = "> ";
|
||||
|
||||
impl Highlighter for DcHelper {
|
||||
fn highlight_prompt<'b, 's: 'b, 'p: 'b>(&self, prompt: &'p str, default: bool) -> Cow<'b, str> {
|
||||
if default {
|
||||
fn highlight_prompt<'p>(&self, prompt: &'p str) -> Cow<'p, str> {
|
||||
if prompt == PROMPT {
|
||||
Borrowed(COLORED_PROMPT)
|
||||
} else {
|
||||
Borrowed(prompt)
|
||||
@@ -291,83 +363,70 @@ impl Highlighter for DcHelper {
|
||||
}
|
||||
|
||||
impl Helper for DcHelper {}
|
||||
impl Validator for DcHelper {}
|
||||
|
||||
async fn start(args: Vec<String>) -> Result<(), Error> {
|
||||
fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
|
||||
if args.len() < 2 {
|
||||
println!("Error: Bad arguments, expected [db-name].");
|
||||
bail!("No db-name specified");
|
||||
return Err(format_err!("No db-name specified"));
|
||||
}
|
||||
let context = Context::new(Path::new(&args[1]).to_path_buf(), 0).await?;
|
||||
|
||||
let events = context.get_event_emitter();
|
||||
async_std::task::spawn(async move {
|
||||
while let Some(event) = events.recv().await {
|
||||
receive_event(event.typ);
|
||||
}
|
||||
});
|
||||
let context = Context::new(
|
||||
Box::new(receive_event),
|
||||
"CLI".into(),
|
||||
Path::new(&args[1]).to_path_buf(),
|
||||
)?;
|
||||
|
||||
println!("Delta Chat Core is awaiting your commands.");
|
||||
|
||||
let ctx = Arc::new(RwLock::new(context));
|
||||
|
||||
let config = Config::builder()
|
||||
.history_ignore_space(true)
|
||||
.completion_type(CompletionType::List)
|
||||
.edit_mode(EditMode::Emacs)
|
||||
.output_stream(OutputStreamType::Stdout)
|
||||
.build();
|
||||
let mut selected_chat = ChatId::default();
|
||||
let (reader_s, reader_r) = async_std::channel::bounded(100);
|
||||
let input_loop = async_std::task::spawn_blocking(move || {
|
||||
let h = DcHelper {
|
||||
completer: FilenameCompleter::new(),
|
||||
highlighter: MatchingBracketHighlighter::new(),
|
||||
hinter: HistoryHinter {},
|
||||
};
|
||||
let mut rl = Editor::with_config(config);
|
||||
rl.set_helper(Some(h));
|
||||
rl.bind_sequence(KeyEvent::alt('N'), Cmd::HistorySearchForward);
|
||||
rl.bind_sequence(KeyEvent::alt('P'), Cmd::HistorySearchBackward);
|
||||
if rl.load_history(".dc-history.txt").is_err() {
|
||||
println!("No previous history.");
|
||||
}
|
||||
let h = DcHelper {
|
||||
completer: FilenameCompleter::new(),
|
||||
highlighter: MatchingBracketHighlighter::new(),
|
||||
hinter: HistoryHinter {},
|
||||
};
|
||||
let mut rl = Editor::with_config(config);
|
||||
rl.set_helper(Some(h));
|
||||
rl.bind_sequence(KeyPress::Meta('N'), Cmd::HistorySearchForward);
|
||||
rl.bind_sequence(KeyPress::Meta('P'), Cmd::HistorySearchBackward);
|
||||
if rl.load_history(".dc-history.txt").is_err() {
|
||||
println!("No previous history.");
|
||||
}
|
||||
|
||||
loop {
|
||||
let p = "> ";
|
||||
let readline = rl.readline(p);
|
||||
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
// TODO: ignore "set mail_pw"
|
||||
rl.add_history_entry(line.as_str());
|
||||
async_std::task::block_on(reader_s.send(line)).unwrap();
|
||||
}
|
||||
Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
|
||||
println!("Exiting...");
|
||||
drop(reader_s);
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Error: {}", err);
|
||||
drop(reader_s);
|
||||
break;
|
||||
loop {
|
||||
let p = "> ";
|
||||
let readline = rl.readline(&p);
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
// TODO: ignore "set mail_pw"
|
||||
rl.add_history_entry(line.as_str());
|
||||
let ctx = ctx.clone();
|
||||
match unsafe { handle_cmd(line.trim(), ctx) } {
|
||||
Ok(ExitResult::Continue) => {}
|
||||
Ok(ExitResult::Exit) => break,
|
||||
Err(err) => println!("Error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rl.save_history(".dc-history.txt")?;
|
||||
println!("history saved");
|
||||
Ok::<_, Error>(())
|
||||
});
|
||||
|
||||
while let Ok(line) = reader_r.recv().await {
|
||||
match handle_cmd(line.trim(), context.clone(), &mut selected_chat).await {
|
||||
Ok(ExitResult::Continue) => {}
|
||||
Ok(ExitResult::Exit) => break,
|
||||
Err(err) => println!("Error: {}", err),
|
||||
Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
|
||||
println!("Exiting...");
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Error: {}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
context.stop_io().await;
|
||||
input_loop.await?;
|
||||
rl.save_history(".dc-history.txt")?;
|
||||
println!("history saved");
|
||||
{
|
||||
stop_threads(&ctx.read().unwrap());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -378,29 +437,48 @@ enum ExitResult {
|
||||
Exit,
|
||||
}
|
||||
|
||||
async fn handle_cmd(
|
||||
line: &str,
|
||||
ctx: Context,
|
||||
selected_chat: &mut ChatId,
|
||||
) -> Result<ExitResult, Error> {
|
||||
unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, failure::Error> {
|
||||
let mut args = line.splitn(2, ' ');
|
||||
let arg0 = args.next().unwrap_or_default();
|
||||
let arg1 = args.next().unwrap_or_default();
|
||||
let arg1_c = if arg1.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
arg1.strdup()
|
||||
};
|
||||
|
||||
match arg0 {
|
||||
"connect" => {
|
||||
ctx.start_io().await;
|
||||
start_threads(ctx);
|
||||
}
|
||||
"disconnect" => {
|
||||
ctx.stop_io().await;
|
||||
stop_threads(&ctx.read().unwrap());
|
||||
}
|
||||
"smtp-jobs" => {
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
println!("smtp-jobs are already running in a thread.",);
|
||||
} else {
|
||||
perform_smtp_jobs(&ctx.read().unwrap());
|
||||
}
|
||||
}
|
||||
"imap-jobs" => {
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
println!("imap-jobs are already running in a thread.");
|
||||
} else {
|
||||
perform_imap_jobs(&ctx.read().unwrap());
|
||||
}
|
||||
}
|
||||
"configure" => {
|
||||
ctx.configure().await?;
|
||||
start_threads(ctx.clone());
|
||||
configure(&ctx.read().unwrap());
|
||||
}
|
||||
"oauth2" => {
|
||||
if let Some(addr) = ctx.get_config(config::Config::Addr).await? {
|
||||
let oauth2_url =
|
||||
dc_get_oauth2_url(&ctx, &addr, "chat.delta:/com.b44t.messenger").await?;
|
||||
if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) {
|
||||
let oauth2_url = dc_get_oauth2_url(
|
||||
&ctx.read().unwrap(),
|
||||
&addr,
|
||||
"chat.delta:/com.b44t.messenger",
|
||||
);
|
||||
if oauth2_url.is_none() {
|
||||
println!("OAuth2 not available for {}.", &addr);
|
||||
} else {
|
||||
@@ -415,54 +493,44 @@ async fn handle_cmd(
|
||||
print!("\x1b[1;1H\x1b[2J");
|
||||
}
|
||||
"getqr" | "getbadqr" => {
|
||||
ctx.start_io().await;
|
||||
let group = arg1.parse::<u32>().ok().map(ChatId::new);
|
||||
let mut qr = dc_get_securejoin_qr(&ctx, group).await?;
|
||||
if !qr.is_empty() {
|
||||
if arg0 == "getbadqr" && qr.len() > 40 {
|
||||
qr.replace_range(12..22, "0000000000")
|
||||
}
|
||||
println!("{}", qr);
|
||||
let output = Command::new("qrencode")
|
||||
.args(&["-t", "ansiutf8", qr.as_str(), "-o", "-"])
|
||||
.output()
|
||||
.expect("failed to execute process");
|
||||
io::stdout().write_all(&output.stdout).unwrap();
|
||||
io::stderr().write_all(&output.stderr).unwrap();
|
||||
}
|
||||
}
|
||||
"getqrsvg" => {
|
||||
ctx.start_io().await;
|
||||
let group = arg1.parse::<u32>().ok().map(ChatId::new);
|
||||
let file = dirs::home_dir().unwrap_or_default().join("qr.svg");
|
||||
match get_securejoin_qr_svg(&ctx, group).await {
|
||||
Ok(svg) => {
|
||||
fs::write(&file, svg)?;
|
||||
println!("QR code svg written to: {:#?}", file);
|
||||
}
|
||||
Err(err) => {
|
||||
bail!("Failed to get QR code svg: {}", err);
|
||||
start_threads(ctx.clone());
|
||||
if let Some(mut qr) =
|
||||
dc_get_securejoin_qr(&ctx.read().unwrap(), arg1.parse().unwrap_or_default())
|
||||
{
|
||||
if !qr.is_empty() {
|
||||
if arg0 == "getbadqr" && qr.len() > 40 {
|
||||
qr.replace_range(12..22, "0000000000")
|
||||
}
|
||||
println!("{}", qr);
|
||||
let output = Command::new("qrencode")
|
||||
.args(&["-t", "ansiutf8", qr.as_str(), "-o", "-"])
|
||||
.output()
|
||||
.expect("failed to execute process");
|
||||
io::stdout().write_all(&output.stdout).unwrap();
|
||||
io::stderr().write_all(&output.stderr).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
"joinqr" => {
|
||||
ctx.start_io().await;
|
||||
start_threads(ctx.clone());
|
||||
if !arg0.is_empty() {
|
||||
dc_join_securejoin(&ctx, arg1).await?;
|
||||
dc_join_securejoin(&ctx.read().unwrap(), arg1);
|
||||
}
|
||||
}
|
||||
"exit" | "quit" => return Ok(ExitResult::Exit),
|
||||
_ => cmdline(ctx.clone(), line, selected_chat).await?,
|
||||
_ => dc_cmdline(&ctx.read().unwrap(), line)?,
|
||||
}
|
||||
|
||||
free(arg1_c as *mut _);
|
||||
|
||||
Ok(ExitResult::Continue)
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
pub fn main() -> Result<(), failure::Error> {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
|
||||
let args = std::env::args().collect();
|
||||
async_std::task::block_on(async move { start(args).await })?;
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
main_0(args)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,100 +1,127 @@
|
||||
extern crate deltachat;
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::{thread, time};
|
||||
use tempfile::tempdir;
|
||||
|
||||
use deltachat::chat::{self, ChatId};
|
||||
use deltachat::chat;
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::config;
|
||||
use deltachat::configure::*;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::message::Message;
|
||||
use deltachat::EventType;
|
||||
use deltachat::job::{
|
||||
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
|
||||
};
|
||||
use deltachat::Event;
|
||||
|
||||
fn cb(_ctx: &Context, event: Event) -> usize {
|
||||
print!("[{:?}]", event);
|
||||
|
||||
fn cb(event: EventType) {
|
||||
match event {
|
||||
EventType::ConfigureProgress { progress, .. } => {
|
||||
log::info!("progress: {}", progress);
|
||||
Event::ConfigureProgress(progress) => {
|
||||
print!(" progress: {}\n", progress);
|
||||
0
|
||||
}
|
||||
EventType::Info(msg) => {
|
||||
log::info!("{}", msg);
|
||||
Event::Info(msg) | Event::Warning(msg) | Event::Error(msg) | Event::ErrorNetwork(msg) => {
|
||||
print!(" {}\n", msg);
|
||||
0
|
||||
}
|
||||
EventType::Warning(msg) => {
|
||||
log::warn!("{}", msg);
|
||||
}
|
||||
EventType::Error(msg) => {
|
||||
log::error!("{}", msg);
|
||||
}
|
||||
event => {
|
||||
log::info!("{:?}", event);
|
||||
_ => {
|
||||
print!("\n");
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run with `RUST_LOG=simple=info cargo run --release --example simple --features repl -- email pw`.
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
pretty_env_logger::try_init_timed().ok();
|
||||
fn main() {
|
||||
unsafe {
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
println!("creating database {:?}", dbfile);
|
||||
let ctx =
|
||||
Context::new(Box::new(cb), "FakeOs".into(), dbfile).expect("Failed to create context");
|
||||
let running = Arc::new(RwLock::new(true));
|
||||
let info = ctx.get_info();
|
||||
let duration = time::Duration::from_millis(4000);
|
||||
println!("info: {:#?}", info);
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
log::info!("creating database {:?}", dbfile);
|
||||
let ctx = Context::new(dbfile.into(), 0)
|
||||
.await
|
||||
.expect("Failed to create context");
|
||||
let info = ctx.get_info().await;
|
||||
log::info!("info: {:#?}", info);
|
||||
let ctx = Arc::new(ctx);
|
||||
let ctx1 = ctx.clone();
|
||||
let r1 = running.clone();
|
||||
let t1 = thread::spawn(move || {
|
||||
while *r1.read().unwrap() {
|
||||
perform_imap_jobs(&ctx1);
|
||||
if *r1.read().unwrap() {
|
||||
perform_imap_fetch(&ctx1);
|
||||
|
||||
let events = ctx.get_event_emitter();
|
||||
let events_spawn = async_std::task::spawn(async move {
|
||||
while let Some(event) = events.recv().await {
|
||||
cb(event.typ);
|
||||
if *r1.read().unwrap() {
|
||||
perform_imap_idle(&ctx1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let ctx1 = ctx.clone();
|
||||
let r1 = running.clone();
|
||||
let t2 = thread::spawn(move || {
|
||||
while *r1.read().unwrap() {
|
||||
perform_smtp_jobs(&ctx1);
|
||||
if *r1.read().unwrap() {
|
||||
perform_smtp_idle(&ctx1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
println!("configuring");
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
assert_eq!(args.len(), 2, "missing password");
|
||||
let pw = args[1].clone();
|
||||
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
|
||||
.unwrap();
|
||||
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
|
||||
configure(&ctx);
|
||||
|
||||
thread::sleep(duration);
|
||||
|
||||
println!("sending a message");
|
||||
let contact_id =
|
||||
Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap();
|
||||
let chat_id = chat::create_by_contact_id(&ctx, contact_id).unwrap();
|
||||
chat::send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()).unwrap();
|
||||
|
||||
println!("fetching chats..");
|
||||
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
|
||||
|
||||
for i in 0..chats.len() {
|
||||
let summary = chats.get_summary(&ctx, 0, None);
|
||||
let text1 = summary.get_text1();
|
||||
let text2 = summary.get_text2();
|
||||
println!("chat: {} - {:?} - {:?}", i, text1, text2,);
|
||||
}
|
||||
});
|
||||
|
||||
log::info!("configuring");
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
assert_eq!(args.len(), 3, "requires email password");
|
||||
let email = args[1].clone();
|
||||
let pw = args[2].clone();
|
||||
ctx.set_config(config::Config::Addr, Some(&email))
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.set_config(config::Config::MailPw, Some(&pw))
|
||||
.await
|
||||
.unwrap();
|
||||
thread::sleep(duration);
|
||||
|
||||
ctx.configure().await.unwrap();
|
||||
// let msglist = dc_get_chat_msgs(&ctx, chat_id, 0, 0);
|
||||
// for i in 0..dc_array_get_cnt(msglist) {
|
||||
// let msg_id = dc_array_get_id(msglist, i);
|
||||
// let msg = dc_get_msg(context, msg_id);
|
||||
// let text = CStr::from_ptr(dc_msg_get_text(msg)).unwrap();
|
||||
// println!("Message {}: {}\n", i + 1, text.to_str().unwrap());
|
||||
// dc_msg_unref(msg);
|
||||
// }
|
||||
// dc_array_unref(msglist);
|
||||
|
||||
log::info!("------ RUN ------");
|
||||
ctx.start_io().await;
|
||||
log::info!("--- SENDING A MESSAGE ---");
|
||||
println!("stopping threads");
|
||||
|
||||
let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com")
|
||||
.await
|
||||
.unwrap();
|
||||
let chat_id = ChatId::create_for_contact(&ctx, contact_id).await.unwrap();
|
||||
*running.clone().write().unwrap() = false;
|
||||
deltachat::job::interrupt_imap_idle(&ctx);
|
||||
deltachat::job::interrupt_smtp_idle(&ctx);
|
||||
|
||||
for i in 0..1 {
|
||||
log::info!("sending message {}", i);
|
||||
chat::send_text_msg(&ctx, chat_id, format!("Hi, here is my {}nth message!", i))
|
||||
.await
|
||||
.unwrap();
|
||||
println!("joining");
|
||||
t1.join().unwrap();
|
||||
t2.join().unwrap();
|
||||
|
||||
println!("closing");
|
||||
}
|
||||
|
||||
// wait for the message to be sent out
|
||||
async_std::task::sleep(std::time::Duration::from_secs(1)).await;
|
||||
|
||||
log::info!("fetching chats..");
|
||||
let chats = Chatlist::try_load(&ctx, 0, None, None).await.unwrap();
|
||||
|
||||
for i in 0..chats.len() {
|
||||
let msg = Message::load_from_db(&ctx, chats.get_msg_id(i).unwrap().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
log::info!("[{}] msg: {:?}", i, msg);
|
||||
}
|
||||
|
||||
log::info!("stopping");
|
||||
ctx.stop_io().await;
|
||||
log::info!("closing");
|
||||
drop(ctx);
|
||||
events_spawn.await;
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# .prettierrc
|
||||
trailingComma: es5
|
||||
tabWidth: 2
|
||||
semi: false
|
||||
singleQuote: true
|
||||
jsxSingleQuote: true
|
||||
@@ -1,21 +0,0 @@
|
||||
# Contributors
|
||||
|
||||
| Name | GitHub |
|
||||
| :-------------------- | :----------------------------------------------- |
|
||||
| **Lars-Magnus Skog** | |
|
||||
| **jikstra** | |
|
||||
| **Simon Laux** | [**@Simon-Laux**](https://github.com/Simon-Laux) |
|
||||
| **Jikstra** | [**@Jikstra**](https://github.com/Jikstra) |
|
||||
| **Nico de Haen** | |
|
||||
| **B. Petersen** | |
|
||||
| **Karissa McKelvey** | [**@karissa**](https://github.com/karissa) |
|
||||
| **developer** | |
|
||||
| **Alexander Krotov** | |
|
||||
| **Floris Bruynooghe** | |
|
||||
| **lefherz** | |
|
||||
| **Pablo** | [**@pabzm**](https://github.com/pabzm) |
|
||||
| **pabzm** | |
|
||||
| **holger krekel** | |
|
||||
| **Robert Schütz** | |
|
||||
| **bb** | |
|
||||
| **Charles Paul** | |
|
||||
674
node/LICENSE
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
247
node/README.md
@@ -1,247 +0,0 @@
|
||||
# deltachat-node
|
||||
|
||||
> node.js bindings for [`deltachat-core-rust`](..)
|
||||
|
||||
[](https://www.npmjs.com/package/deltachat-node)
|
||||

|
||||
[](https://prettier.io)
|
||||
|
||||
`deltachat-node` primarily aims to offer two things:
|
||||
|
||||
- A high level JavaScript api with syntactic sugar
|
||||
- A low level c binding api around [`deltachat-core-rust`](..)
|
||||
|
||||
This code used to live at [`deltachat-node`](https://github.com/deltachat/deltachat-node)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<details><summary>Click to expand</summary>
|
||||
|
||||
- [Install](#install)
|
||||
- [Dependencies](#dependencies)
|
||||
- [Build from source](#build-from-source)
|
||||
- [Usage](#usage)
|
||||
- [Developing](#developing)
|
||||
- [License](#license)
|
||||
|
||||
</details>
|
||||
|
||||
## Install
|
||||
|
||||
By default the installation will build try to use the bundled prebuilds in the
|
||||
npm package. If this fails it falls back to compile `../deltachat-core-rust` from
|
||||
this repository, using `scripts/rebuild-core.js`.
|
||||
|
||||
To install from npm use:
|
||||
|
||||
```
|
||||
npm install deltachat-node
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Nodejs >= `v16.0.0`
|
||||
- rustup (optional if you can't use the prebuilds)
|
||||
|
||||
> On Windows, you may need to also install **Perl** to be able to compile deltachat-core.
|
||||
|
||||
## Build from source
|
||||
|
||||
If you want to build from source, make sure that you have `rustup` installed.
|
||||
You can either use `npm install deltachat-node --build-from-source` to force
|
||||
building from source or clone this repository and follow this steps:
|
||||
|
||||
1. `git clone https://github.com/deltachat/deltachat-core-rust.git`
|
||||
2. `cd deltachat-core-rust`
|
||||
3. `npm i`
|
||||
4. `npm run build`
|
||||
|
||||
> Our `package.json` file is located in the root directory of this repository,
|
||||
> not inside this folder. (We need this in order to include the rust source
|
||||
> code in the npm package.)
|
||||
|
||||
### Use build-from-source in deltachat-desktop
|
||||
|
||||
If you want to use the manually built node bindings in the desktop client (for
|
||||
example), you can follow these instructions:
|
||||
|
||||
First clone the
|
||||
[deltachat-desktop](https://github.com/deltachat/deltachat-desktop) repository,
|
||||
e.g. with `git clone https://github.com/deltachat/deltachat-desktop`.
|
||||
|
||||
Then you need to make sure that this directory is referenced correctly in
|
||||
deltachat-desktop's package.json. You need to change
|
||||
`deltachat-desktop/package.json` like this:
|
||||
|
||||
```
|
||||
diff --git i/package.json w/package.json
|
||||
index 45893894..5154512c 100644
|
||||
--- i/package.json
|
||||
+++ w/package.json
|
||||
@@ -83,7 +83,7 @@
|
||||
"application-config": "^1.0.1",
|
||||
"classnames": "^2.3.1",
|
||||
"debounce": "^1.2.0",
|
||||
- "deltachat-node": "1.79.3",
|
||||
+ "deltachat-node": "file:../deltachat-core-rust/",
|
||||
"emoji-js-clean": "^4.0.0",
|
||||
"emoji-mart": "^3.0.1",
|
||||
"emoji-regex": "^9.2.2",
|
||||
```
|
||||
|
||||
Then, in the `deltachat-desktop` repository, run:
|
||||
|
||||
1. `npm i`
|
||||
2. `npm run build`
|
||||
3. And `npm run start` to start the newly built client.
|
||||
|
||||
### Workaround to build for x86_64 on Apple's M1
|
||||
|
||||
deltachat doesn't support universal (fat) binaries (that contain builds for both cpu architectures) yet, until it does you can use the following workaround to get x86_64 builds:
|
||||
|
||||
```
|
||||
$ fnm install 17 --arch x64
|
||||
$ fnm use 17
|
||||
$ node -p process.arch
|
||||
# result should be x64
|
||||
$ cd deltachat-core-rust && rustup target add x86_64-apple-darwin && cd -
|
||||
$ git apply patches/m1_build_use_x86_64.patch
|
||||
$ CARGO_BUILD_TARGET=x86_64-apple-darwin npm run build
|
||||
$ npm run test
|
||||
```
|
||||
|
||||
(when using [fnm](https://github.com/Schniz/fnm) instead of nvm, you can select the architecture)
|
||||
If your node and electron are already build for arm64 you can also try bulding for arm:
|
||||
|
||||
```
|
||||
$ fnm install 16 --arch arm64
|
||||
$ fnm use 16
|
||||
$ node -p process.arch
|
||||
# result should be arm64
|
||||
$ npm_config_arch=arm64 npm run build
|
||||
$ npm run test
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
const { Context } = require('deltachat-node')
|
||||
|
||||
const opts = {
|
||||
addr: '[email]',
|
||||
mail_pw: '[password]',
|
||||
}
|
||||
|
||||
const contact = '[email]'
|
||||
|
||||
async function main() {
|
||||
const dc = Context.open('./')
|
||||
dc.on('ALL', console.log.bind(null, 'core |'))
|
||||
|
||||
try {
|
||||
await dc.configure(opts)
|
||||
} catch (err) {
|
||||
console.error('Failed to configure because of: ', err)
|
||||
dc.unref()
|
||||
return
|
||||
}
|
||||
|
||||
dc.startIO()
|
||||
console.log('fully configured')
|
||||
|
||||
const contactId = dc.createContact('Test', contact)
|
||||
const chatId = dc.createChatByContactId(contactId)
|
||||
dc.sendMessage(chatId, 'Hi!')
|
||||
|
||||
console.log('sent message')
|
||||
|
||||
dc.once('DC_EVENT_SMTP_MESSAGE_SENT', async () => {
|
||||
console.log('Message sent, shutting down...')
|
||||
dc.stopIO()
|
||||
console.log('stopped io')
|
||||
dc.unref()
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
||||
```
|
||||
this example can also be found in the examples folder [examples/send_message.js](./examples/send_message.js)
|
||||
|
||||
### Generating Docs
|
||||
|
||||
We are curently migrating to automaticaly generated documentation.
|
||||
You can find the old documentation at [old_docs](./old_docs).
|
||||
|
||||
to generate the documentation, run:
|
||||
|
||||
```
|
||||
npx typedoc
|
||||
```
|
||||
|
||||
The resulting documentation can be found in the `docs/` folder.
|
||||
An online version can be found under [js.delta.chat](https://js.delta.chat).
|
||||
|
||||
## Developing
|
||||
|
||||
### Tests and Coverage
|
||||
|
||||
Running `npm test` ends with showing a code coverage report, which is produced by [`nyc`](https://github.com/istanbuljs/nyc#readme).
|
||||
|
||||

|
||||
|
||||
The coverage report from `nyc` in the console is rather limited. To get a more detailed coverage report you can run `npm run coverage-html-report`. This will produce a html report from the `nyc` data and display it in a browser on your local machine.
|
||||
|
||||
To run the integration tests you need to set the `DCC_NEW_TMP_EMAIL` environment variables. E.g.:
|
||||
|
||||
```
|
||||
$ export DCC_NEW_TMP_EMAIL=https://testrun.org/new_email?t=[token]
|
||||
$ npm run test
|
||||
```
|
||||
|
||||
### Scripts
|
||||
|
||||
We have the following scripts for building, testing and coverage:
|
||||
|
||||
- `npm run coverage` Creates a coverage report and passes it to `coveralls`. Only done by `Travis`.
|
||||
- `npm run coverage-html-report` Generates a html report from the coverage data and opens it in a browser on the local machine.
|
||||
- `npm run generate-constants` Generates `constants.js` and `events.js` based on the `deltachat-core-rust/deltachat-ffi/deltachat.h` header file.
|
||||
- `npm install` After dependencies are installed, runs `node-gyp-build` to see if the native code needs to be rebuilt.
|
||||
- `npm run build` Rebuilds all code.
|
||||
- `npm run build:core` Rebuilds code in `deltachat-core-rust`.
|
||||
- `npm run build:bindings` Rebuilds the bindings and links with `deltachat-core-rust`.
|
||||
- `ǹpm run clean` Removes all built code
|
||||
- `npm run prebuildify` Builds prebuilt binary to `prebuilds/$PLATFORM-$ARCH`. Copies `deltachat.dll` from `deltachat-core-rust` for windows.
|
||||
- `npm run download-prebuilds` Downloads all prebuilt binaries from github before `npm publish`.
|
||||
- `npm test` Runs `standard` and then the tests in `test/index.js`.
|
||||
- `npm run test-integration` Runs the integration tests.
|
||||
- `npm run hallmark` Runs `hallmark` on all markdown files.
|
||||
|
||||
### Releases
|
||||
|
||||
The following steps are needed to make a release:
|
||||
|
||||
1. Wait until `pack-module` github action is completed
|
||||
2. Run `npm publish https://download.delta.chat/node/deltachat-node-v1.x.x.tar.gz` to publish it to npm. You probably need write rights to npm.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under `GPL-3.0-or-later`, see [LICENSE](./LICENSE) file for details.
|
||||
|
||||
> Copyright © 2018 `DeltaChat` contributors.
|
||||
>
|
||||
> This program is free software: you can redistribute it and/or modify
|
||||
> it under the terms of the GNU General Public License as published by
|
||||
> the Free Software Foundation, either version 3 of the License, or
|
||||
> (at your option) any later version.
|
||||
>
|
||||
> This program is distributed in the hope that it will be useful,
|
||||
> but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
> GNU General Public License for more details.
|
||||
>
|
||||
> You should have received a copy of the GNU General Public License
|
||||
> along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
[appveyor-shield]: https://ci.appveyor.com/api/projects/status/t0narp672wpbl6pd?svg=true
|
||||
|
||||
[appveyor]: https://ci.appveyor.com/project/ralphtheninja/deltachat-node-d4bf8
|
||||
@@ -1,78 +0,0 @@
|
||||
{
|
||||
# documentation about the format of this file can be found under https://gyp.gsrc.io/docs/InputFormatReference.md
|
||||
# Variables can be specified when calling node-gyp as so:
|
||||
# node-gyp configure -- -Dvarname=value
|
||||
"variables": {
|
||||
# Whether to use a system-wide installation of deltachat-core
|
||||
# using pkg-config. Set to either "true" or "false".
|
||||
"USE_SYSTEM_LIBDELTACHAT%": "<!(echo $USE_SYSTEM_LIBDELTACHAT)",
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "deltachat",
|
||||
"sources": ["./src/module.c"],
|
||||
"include_dirs": ["<!(node -e \"require('napi-macros')\")"],
|
||||
"conditions": [
|
||||
[
|
||||
"OS == 'win'",
|
||||
{
|
||||
"include_dirs": ["../deltachat-ffi"],
|
||||
"libraries": [
|
||||
"../../target/release/deltachat.dll.lib",
|
||||
],
|
||||
"conditions": [
|
||||
[
|
||||
"USE_SYSTEM_LIBDELTACHAT == 'true'",
|
||||
{
|
||||
"cflags": ["<!(pkg-config --cflags deltachat)"],
|
||||
"libraries": ["<!(pkg-config --libs deltachat)"],
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
"OS == 'linux' or OS == 'mac'",
|
||||
{
|
||||
"libraries": ["-lpthread"],
|
||||
"cflags": ["-std=gnu99"],
|
||||
"conditions": [
|
||||
[
|
||||
"USE_SYSTEM_LIBDELTACHAT != 'true'",
|
||||
{
|
||||
"include_dirs": ["../deltachat-ffi"],
|
||||
"ldflags": ["-Wl,-Bsymbolic"], # Prevent sqlite3 from electron from overriding sqlcipher
|
||||
"libraries": [
|
||||
"../../target/release/libdeltachat.a",
|
||||
"-ldl",
|
||||
],
|
||||
"conditions": [],
|
||||
},
|
||||
{
|
||||
# USE_SYSTEM_LIBDELTACHAT == 'true'
|
||||
"cflags": ["<!(pkg-config --cflags deltachat)"],
|
||||
"libraries": ["<!(pkg-config --libs deltachat)"],
|
||||
},
|
||||
],
|
||||
[
|
||||
"OS == 'mac'",
|
||||
{
|
||||
"libraries": [
|
||||
"-framework CoreFoundation",
|
||||
"-framework CoreServices",
|
||||
"-framework Security",
|
||||
"-lresolv",
|
||||
],
|
||||
},
|
||||
{
|
||||
# OS == 'linux'
|
||||
"libraries": ["-lm", "-lrt"],
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('node-gyp-build')(__dirname)
|
||||
@@ -1,226 +0,0 @@
|
||||
// Generated!
|
||||
|
||||
module.exports = {
|
||||
DC_CERTCK_ACCEPT_INVALID_CERTIFICATES: 3,
|
||||
DC_CERTCK_AUTO: 0,
|
||||
DC_CERTCK_STRICT: 1,
|
||||
DC_CHAT_ID_ALLDONE_HINT: 7,
|
||||
DC_CHAT_ID_ARCHIVED_LINK: 6,
|
||||
DC_CHAT_ID_LAST_SPECIAL: 9,
|
||||
DC_CHAT_ID_TRASH: 3,
|
||||
DC_CHAT_TYPE_BROADCAST: 160,
|
||||
DC_CHAT_TYPE_GROUP: 120,
|
||||
DC_CHAT_TYPE_MAILINGLIST: 140,
|
||||
DC_CHAT_TYPE_SINGLE: 100,
|
||||
DC_CHAT_TYPE_UNDEFINED: 0,
|
||||
DC_CHAT_VISIBILITY_ARCHIVED: 1,
|
||||
DC_CHAT_VISIBILITY_NORMAL: 0,
|
||||
DC_CHAT_VISIBILITY_PINNED: 2,
|
||||
DC_CONNECTIVITY_CONNECTED: 4000,
|
||||
DC_CONNECTIVITY_CONNECTING: 2000,
|
||||
DC_CONNECTIVITY_NOT_CONNECTED: 1000,
|
||||
DC_CONNECTIVITY_WORKING: 3000,
|
||||
DC_CONTACT_ID_DEVICE: 5,
|
||||
DC_CONTACT_ID_INFO: 2,
|
||||
DC_CONTACT_ID_LAST_SPECIAL: 9,
|
||||
DC_CONTACT_ID_SELF: 1,
|
||||
DC_DOWNLOAD_AVAILABLE: 10,
|
||||
DC_DOWNLOAD_DONE: 0,
|
||||
DC_DOWNLOAD_FAILURE: 20,
|
||||
DC_DOWNLOAD_IN_PROGRESS: 1000,
|
||||
DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED: 2021,
|
||||
DC_EVENT_CHAT_MODIFIED: 2020,
|
||||
DC_EVENT_CONFIGURE_PROGRESS: 2041,
|
||||
DC_EVENT_CONNECTIVITY_CHANGED: 2100,
|
||||
DC_EVENT_CONTACTS_CHANGED: 2030,
|
||||
DC_EVENT_DELETED_BLOB_FILE: 151,
|
||||
DC_EVENT_ERROR: 400,
|
||||
DC_EVENT_ERROR_SELF_NOT_IN_GROUP: 410,
|
||||
DC_EVENT_IMAP_CONNECTED: 102,
|
||||
DC_EVENT_IMAP_MESSAGE_DELETED: 104,
|
||||
DC_EVENT_IMAP_MESSAGE_MOVED: 105,
|
||||
DC_EVENT_IMEX_FILE_WRITTEN: 2052,
|
||||
DC_EVENT_IMEX_PROGRESS: 2051,
|
||||
DC_EVENT_INCOMING_MSG: 2005,
|
||||
DC_EVENT_INFO: 100,
|
||||
DC_EVENT_LOCATION_CHANGED: 2035,
|
||||
DC_EVENT_MSGS_CHANGED: 2000,
|
||||
DC_EVENT_MSGS_NOTICED: 2008,
|
||||
DC_EVENT_MSG_DELIVERED: 2010,
|
||||
DC_EVENT_MSG_FAILED: 2012,
|
||||
DC_EVENT_MSG_READ: 2015,
|
||||
DC_EVENT_NEW_BLOB_FILE: 150,
|
||||
DC_EVENT_SECUREJOIN_INVITER_PROGRESS: 2060,
|
||||
DC_EVENT_SECUREJOIN_JOINER_PROGRESS: 2061,
|
||||
DC_EVENT_SELFAVATAR_CHANGED: 2110,
|
||||
DC_EVENT_SMTP_CONNECTED: 101,
|
||||
DC_EVENT_SMTP_MESSAGE_SENT: 103,
|
||||
DC_EVENT_WARNING: 300,
|
||||
DC_EVENT_WEBXDC_STATUS_UPDATE: 2120,
|
||||
DC_GCL_ADD_ALLDONE_HINT: 4,
|
||||
DC_GCL_ADD_SELF: 2,
|
||||
DC_GCL_ARCHIVED_ONLY: 1,
|
||||
DC_GCL_FOR_FORWARDING: 8,
|
||||
DC_GCL_NO_SPECIALS: 2,
|
||||
DC_GCL_VERIFIED_ONLY: 1,
|
||||
DC_GCM_ADDDAYMARKER: 1,
|
||||
DC_GCM_INFO_ONLY: 2,
|
||||
DC_IMEX_EXPORT_BACKUP: 11,
|
||||
DC_IMEX_EXPORT_SELF_KEYS: 1,
|
||||
DC_IMEX_IMPORT_BACKUP: 12,
|
||||
DC_IMEX_IMPORT_SELF_KEYS: 2,
|
||||
DC_INFO_PROTECTION_DISABLED: 12,
|
||||
DC_INFO_PROTECTION_ENABLED: 11,
|
||||
DC_KEY_GEN_DEFAULT: 0,
|
||||
DC_KEY_GEN_ED25519: 2,
|
||||
DC_KEY_GEN_RSA2048: 1,
|
||||
DC_LP_AUTH_NORMAL: 4,
|
||||
DC_LP_AUTH_OAUTH2: 2,
|
||||
DC_MEDIA_QUALITY_BALANCED: 0,
|
||||
DC_MEDIA_QUALITY_WORSE: 1,
|
||||
DC_MSG_AUDIO: 40,
|
||||
DC_MSG_FILE: 60,
|
||||
DC_MSG_GIF: 21,
|
||||
DC_MSG_ID_DAYMARKER: 9,
|
||||
DC_MSG_ID_LAST_SPECIAL: 9,
|
||||
DC_MSG_ID_MARKER1: 1,
|
||||
DC_MSG_IMAGE: 20,
|
||||
DC_MSG_STICKER: 23,
|
||||
DC_MSG_TEXT: 10,
|
||||
DC_MSG_VIDEO: 50,
|
||||
DC_MSG_VIDEOCHAT_INVITATION: 70,
|
||||
DC_MSG_VOICE: 41,
|
||||
DC_MSG_WEBXDC: 80,
|
||||
DC_PROVIDER_STATUS_BROKEN: 3,
|
||||
DC_PROVIDER_STATUS_OK: 1,
|
||||
DC_PROVIDER_STATUS_PREPARATION: 2,
|
||||
DC_QR_ACCOUNT: 250,
|
||||
DC_QR_ADDR: 320,
|
||||
DC_QR_ASK_VERIFYCONTACT: 200,
|
||||
DC_QR_ASK_VERIFYGROUP: 202,
|
||||
DC_QR_ERROR: 400,
|
||||
DC_QR_FPR_MISMATCH: 220,
|
||||
DC_QR_FPR_OK: 210,
|
||||
DC_QR_FPR_WITHOUT_ADDR: 230,
|
||||
DC_QR_REVIVE_VERIFYCONTACT: 510,
|
||||
DC_QR_REVIVE_VERIFYGROUP: 512,
|
||||
DC_QR_TEXT: 330,
|
||||
DC_QR_URL: 332,
|
||||
DC_QR_WEBRTC_INSTANCE: 260,
|
||||
DC_QR_WITHDRAW_VERIFYCONTACT: 500,
|
||||
DC_QR_WITHDRAW_VERIFYGROUP: 502,
|
||||
DC_SHOW_EMAILS_ACCEPTED_CONTACTS: 1,
|
||||
DC_SHOW_EMAILS_ALL: 2,
|
||||
DC_SHOW_EMAILS_OFF: 0,
|
||||
DC_SOCKET_AUTO: 0,
|
||||
DC_SOCKET_PLAIN: 3,
|
||||
DC_SOCKET_SSL: 1,
|
||||
DC_SOCKET_STARTTLS: 2,
|
||||
DC_STATE_IN_FRESH: 10,
|
||||
DC_STATE_IN_NOTICED: 13,
|
||||
DC_STATE_IN_SEEN: 16,
|
||||
DC_STATE_OUT_DELIVERED: 26,
|
||||
DC_STATE_OUT_DRAFT: 19,
|
||||
DC_STATE_OUT_FAILED: 24,
|
||||
DC_STATE_OUT_MDN_RCVD: 28,
|
||||
DC_STATE_OUT_PENDING: 20,
|
||||
DC_STATE_OUT_PREPARING: 18,
|
||||
DC_STATE_UNDEFINED: 0,
|
||||
DC_STR_AC_SETUP_MSG_BODY: 43,
|
||||
DC_STR_AC_SETUP_MSG_SUBJECT: 42,
|
||||
DC_STR_ARCHIVEDCHATS: 40,
|
||||
DC_STR_AUDIO: 11,
|
||||
DC_STR_BAD_TIME_MSG_BODY: 85,
|
||||
DC_STR_BROADCAST_LIST: 115,
|
||||
DC_STR_CANNOT_LOGIN: 60,
|
||||
DC_STR_CANTDECRYPT_MSG_BODY: 29,
|
||||
DC_STR_CONFIGURATION_FAILED: 84,
|
||||
DC_STR_CONNECTED: 107,
|
||||
DC_STR_CONNTECTING: 108,
|
||||
DC_STR_CONTACT_NOT_VERIFIED: 36,
|
||||
DC_STR_CONTACT_SETUP_CHANGED: 37,
|
||||
DC_STR_CONTACT_VERIFIED: 35,
|
||||
DC_STR_DEVICE_MESSAGES: 68,
|
||||
DC_STR_DEVICE_MESSAGES_HINT: 70,
|
||||
DC_STR_DOWNLOAD_AVAILABILITY: 100,
|
||||
DC_STR_DRAFT: 3,
|
||||
DC_STR_E2E_AVAILABLE: 25,
|
||||
DC_STR_E2E_PREFERRED: 34,
|
||||
DC_STR_ENCRYPTEDMSG: 24,
|
||||
DC_STR_ENCR_NONE: 28,
|
||||
DC_STR_ENCR_TRANSP: 27,
|
||||
DC_STR_EPHEMERAL_DAY: 79,
|
||||
DC_STR_EPHEMERAL_DAYS: 95,
|
||||
DC_STR_EPHEMERAL_DISABLED: 75,
|
||||
DC_STR_EPHEMERAL_FOUR_WEEKS: 81,
|
||||
DC_STR_EPHEMERAL_HOUR: 78,
|
||||
DC_STR_EPHEMERAL_HOURS: 94,
|
||||
DC_STR_EPHEMERAL_MINUTE: 77,
|
||||
DC_STR_EPHEMERAL_MINUTES: 93,
|
||||
DC_STR_EPHEMERAL_SECONDS: 76,
|
||||
DC_STR_EPHEMERAL_WEEK: 80,
|
||||
DC_STR_EPHEMERAL_WEEKS: 96,
|
||||
DC_STR_ERROR: 112,
|
||||
DC_STR_ERROR_NO_NETWORK: 87,
|
||||
DC_STR_FAILED_SENDING_TO: 74,
|
||||
DC_STR_FILE: 12,
|
||||
DC_STR_FINGERPRINTS: 30,
|
||||
DC_STR_FORWARDED: 97,
|
||||
DC_STR_GIF: 23,
|
||||
DC_STR_IMAGE: 9,
|
||||
DC_STR_INCOMING_MESSAGES: 103,
|
||||
DC_STR_LAST_MSG_SENT_SUCCESSFULLY: 111,
|
||||
DC_STR_LOCATION: 66,
|
||||
DC_STR_MESSAGES: 114,
|
||||
DC_STR_MSGACTIONBYME: 63,
|
||||
DC_STR_MSGACTIONBYUSER: 62,
|
||||
DC_STR_MSGADDMEMBER: 17,
|
||||
DC_STR_MSGDELMEMBER: 18,
|
||||
DC_STR_MSGGROUPLEFT: 19,
|
||||
DC_STR_MSGGRPIMGCHANGED: 16,
|
||||
DC_STR_MSGGRPIMGDELETED: 33,
|
||||
DC_STR_MSGGRPNAME: 15,
|
||||
DC_STR_MSGLOCATIONDISABLED: 65,
|
||||
DC_STR_MSGLOCATIONENABLED: 64,
|
||||
DC_STR_NOMESSAGES: 1,
|
||||
DC_STR_NOT_CONNECTED: 121,
|
||||
DC_STR_NOT_SUPPORTED_BY_PROVIDER: 113,
|
||||
DC_STR_ONE_MOMENT: 106,
|
||||
DC_STR_OUTGOING_MESSAGES: 104,
|
||||
DC_STR_PARTIAL_DOWNLOAD_MSG_BODY: 99,
|
||||
DC_STR_PART_OF_TOTAL_USED: 116,
|
||||
DC_STR_PROTECTION_DISABLED: 89,
|
||||
DC_STR_PROTECTION_ENABLED: 88,
|
||||
DC_STR_QUOTA_EXCEEDING_MSG_BODY: 98,
|
||||
DC_STR_READRCPT: 31,
|
||||
DC_STR_READRCPT_MAILBODY: 32,
|
||||
DC_STR_REPLY_NOUN: 90,
|
||||
DC_STR_SAVED_MESSAGES: 69,
|
||||
DC_STR_SECURE_JOIN_GROUP_QR_DESC: 120,
|
||||
DC_STR_SECURE_JOIN_REPLIES: 118,
|
||||
DC_STR_SECURE_JOIN_STARTED: 117,
|
||||
DC_STR_SELF: 2,
|
||||
DC_STR_SELF_DELETED_MSG_BODY: 91,
|
||||
DC_STR_SENDING: 110,
|
||||
DC_STR_SERVER_TURNED_OFF: 92,
|
||||
DC_STR_SETUP_CONTACT_QR_DESC: 119,
|
||||
DC_STR_STICKER: 67,
|
||||
DC_STR_STORAGE_ON_DOMAIN: 105,
|
||||
DC_STR_SUBJECT_FOR_NEW_CONTACT: 73,
|
||||
DC_STR_SYNC_MSG_BODY: 102,
|
||||
DC_STR_SYNC_MSG_SUBJECT: 101,
|
||||
DC_STR_UNKNOWN_SENDER_FOR_CHAT: 72,
|
||||
DC_STR_UPDATE_REMINDER_MSG_BODY: 86,
|
||||
DC_STR_UPDATING: 109,
|
||||
DC_STR_VIDEO: 10,
|
||||
DC_STR_VIDEOCHAT_INVITATION: 82,
|
||||
DC_STR_VIDEOCHAT_INVITE_MSG_BODY: 83,
|
||||
DC_STR_VOICEMESSAGE: 7,
|
||||
DC_STR_WELCOME_MESSAGE: 71,
|
||||
DC_TEXT1_DRAFT: 1,
|
||||
DC_TEXT1_SELF: 3,
|
||||
DC_TEXT1_USERNAME: 2,
|
||||
DC_VIDEOCHATTYPE_BASICWEBRTC: 1,
|
||||
DC_VIDEOCHATTYPE_JITSI: 2,
|
||||
DC_VIDEOCHATTYPE_UNKNOWN: 0
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/* eslint-disable quotes */
|
||||
// Generated!
|
||||
|
||||
module.exports = {
|
||||
100: 'DC_EVENT_INFO',
|
||||
101: 'DC_EVENT_SMTP_CONNECTED',
|
||||
102: 'DC_EVENT_IMAP_CONNECTED',
|
||||
103: 'DC_EVENT_SMTP_MESSAGE_SENT',
|
||||
104: 'DC_EVENT_IMAP_MESSAGE_DELETED',
|
||||
105: 'DC_EVENT_IMAP_MESSAGE_MOVED',
|
||||
150: 'DC_EVENT_NEW_BLOB_FILE',
|
||||
151: 'DC_EVENT_DELETED_BLOB_FILE',
|
||||
300: 'DC_EVENT_WARNING',
|
||||
400: 'DC_EVENT_ERROR',
|
||||
410: 'DC_EVENT_ERROR_SELF_NOT_IN_GROUP',
|
||||
2000: 'DC_EVENT_MSGS_CHANGED',
|
||||
2005: 'DC_EVENT_INCOMING_MSG',
|
||||
2008: 'DC_EVENT_MSGS_NOTICED',
|
||||
2010: 'DC_EVENT_MSG_DELIVERED',
|
||||
2012: 'DC_EVENT_MSG_FAILED',
|
||||
2015: 'DC_EVENT_MSG_READ',
|
||||
2020: 'DC_EVENT_CHAT_MODIFIED',
|
||||
2021: 'DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED',
|
||||
2030: 'DC_EVENT_CONTACTS_CHANGED',
|
||||
2035: 'DC_EVENT_LOCATION_CHANGED',
|
||||
2041: 'DC_EVENT_CONFIGURE_PROGRESS',
|
||||
2051: 'DC_EVENT_IMEX_PROGRESS',
|
||||
2052: 'DC_EVENT_IMEX_FILE_WRITTEN',
|
||||
2060: 'DC_EVENT_SECUREJOIN_INVITER_PROGRESS',
|
||||
2061: 'DC_EVENT_SECUREJOIN_JOINER_PROGRESS',
|
||||
2100: 'DC_EVENT_CONNECTIVITY_CHANGED',
|
||||
2110: 'DC_EVENT_SELFAVATAR_CHANGED',
|
||||
2120: 'DC_EVENT_WEBXDC_STATUS_UPDATE'
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
//@ts-check
|
||||
const { Context } = require('../dist')
|
||||
|
||||
const opts = {
|
||||
addr: '[email]',
|
||||
mail_pw: '[password]',
|
||||
}
|
||||
|
||||
const contact = '[email]'
|
||||
|
||||
async function main() {
|
||||
const dc = Context.open('./')
|
||||
dc.on('ALL', console.log.bind(null, 'core |'))
|
||||
|
||||
try {
|
||||
await dc.configure(opts)
|
||||
} catch (err) {
|
||||
console.error('Failed to configure because of: ', err)
|
||||
dc.unref()
|
||||
return
|
||||
}
|
||||
|
||||
dc.startIO()
|
||||
console.log('fully configured')
|
||||
|
||||
const contactId = dc.createContact('Test', contact)
|
||||
const chatId = dc.createChatByContactId(contactId)
|
||||
dc.sendMessage(chatId, 'Hi!')
|
||||
|
||||
console.log('sent message')
|
||||
|
||||
dc.once('DC_EVENT_SMTP_MESSAGE_SENT', async () => {
|
||||
console.log('Message sent, shutting down...')
|
||||
dc.stopIO()
|
||||
console.log('stopped io')
|
||||
dc.unref()
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
||||
|
Before Width: | Height: | Size: 26 KiB |
@@ -1,9 +0,0 @@
|
||||
import { join } from 'path'
|
||||
|
||||
/**
|
||||
* bindings are not typed yet.
|
||||
* if the availible function names are required they can be found inside of `../src/module.c`
|
||||
*/
|
||||
export const bindings: any = require('node-gyp-build')(join(__dirname, '../'))
|
||||
|
||||
export default bindings
|
||||
106
node/lib/chat.ts
@@ -1,106 +0,0 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import binding from './binding'
|
||||
import rawDebug from 'debug'
|
||||
const debug = rawDebug('deltachat:node:chat')
|
||||
import { C } from './constants'
|
||||
import { integerToHexColor } from './util'
|
||||
import { ChatJSON } from './types'
|
||||
|
||||
interface NativeChat {}
|
||||
/**
|
||||
* Wrapper around dc_chat_t*
|
||||
*/
|
||||
|
||||
export class Chat {
|
||||
constructor(public dc_chat: NativeChat) {
|
||||
debug('Chat constructor')
|
||||
if (dc_chat === null) {
|
||||
throw new Error('native chat can not be null')
|
||||
}
|
||||
}
|
||||
|
||||
getVisibility():
|
||||
| C.DC_CHAT_VISIBILITY_NORMAL
|
||||
| C.DC_CHAT_VISIBILITY_ARCHIVED
|
||||
| C.DC_CHAT_VISIBILITY_PINNED {
|
||||
return binding.dcn_chat_get_visibility(this.dc_chat)
|
||||
}
|
||||
|
||||
get color(): string {
|
||||
return integerToHexColor(binding.dcn_chat_get_color(this.dc_chat))
|
||||
}
|
||||
|
||||
getId(): number {
|
||||
return binding.dcn_chat_get_id(this.dc_chat)
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return binding.dcn_chat_get_name(this.dc_chat)
|
||||
}
|
||||
|
||||
getProfileImage(): string {
|
||||
return binding.dcn_chat_get_profile_image(this.dc_chat)
|
||||
}
|
||||
|
||||
getType(): number {
|
||||
return binding.dcn_chat_get_type(this.dc_chat)
|
||||
}
|
||||
|
||||
isSelfTalk(): boolean {
|
||||
return Boolean(binding.dcn_chat_is_self_talk(this.dc_chat))
|
||||
}
|
||||
|
||||
isContactRequest(): boolean {
|
||||
return Boolean(binding.dcn_chat_is_contact_request(this.dc_chat))
|
||||
}
|
||||
|
||||
isUnpromoted(): boolean {
|
||||
return Boolean(binding.dcn_chat_is_unpromoted(this.dc_chat))
|
||||
}
|
||||
|
||||
isProtected(): boolean {
|
||||
return Boolean(binding.dcn_chat_is_protected(this.dc_chat))
|
||||
}
|
||||
|
||||
get canSend(): boolean {
|
||||
return Boolean(binding.dcn_chat_can_send(this.dc_chat))
|
||||
}
|
||||
|
||||
isDeviceTalk(): boolean {
|
||||
return Boolean(binding.dcn_chat_is_device_talk(this.dc_chat))
|
||||
}
|
||||
|
||||
isSingle(): boolean {
|
||||
return this.getType() === C.DC_CHAT_TYPE_SINGLE
|
||||
}
|
||||
|
||||
isGroup(): boolean {
|
||||
return this.getType() === C.DC_CHAT_TYPE_GROUP
|
||||
}
|
||||
|
||||
isMuted(): boolean {
|
||||
return Boolean(binding.dcn_chat_is_muted(this.dc_chat))
|
||||
}
|
||||
|
||||
toJson(): ChatJSON {
|
||||
debug('toJson')
|
||||
const visibility = this.getVisibility()
|
||||
return {
|
||||
archived: visibility === C.DC_CHAT_VISIBILITY_ARCHIVED,
|
||||
pinned: visibility === C.DC_CHAT_VISIBILITY_PINNED,
|
||||
color: this.color,
|
||||
id: this.getId(),
|
||||
name: this.getName(),
|
||||
profileImage: this.getProfileImage(),
|
||||
type: this.getType(),
|
||||
isSelfTalk: this.isSelfTalk(),
|
||||
isUnpromoted: this.isUnpromoted(),
|
||||
isProtected: this.isProtected(),
|
||||
canSend: this.canSend,
|
||||
isDeviceTalk: this.isDeviceTalk(),
|
||||
isContactRequest: this.isContactRequest(),
|
||||
muted: this.isMuted(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import binding from './binding'
|
||||
import { Lot } from './lot'
|
||||
import { Chat } from './chat'
|
||||
const debug = require('debug')('deltachat:node:chatlist')
|
||||
|
||||
interface NativeChatList {}
|
||||
/**
|
||||
* Wrapper around dc_chatlist_t*
|
||||
*/
|
||||
export class ChatList {
|
||||
constructor(private dc_chatlist: NativeChatList) {
|
||||
debug('ChatList constructor')
|
||||
if (dc_chatlist === null) {
|
||||
throw new Error('native chat list can not be null')
|
||||
}
|
||||
}
|
||||
|
||||
getChatId(index: number): number {
|
||||
debug(`getChatId ${index}`)
|
||||
return binding.dcn_chatlist_get_chat_id(this.dc_chatlist, index)
|
||||
}
|
||||
|
||||
getCount(): number {
|
||||
debug('getCount')
|
||||
return binding.dcn_chatlist_get_cnt(this.dc_chatlist)
|
||||
}
|
||||
|
||||
getMessageId(index: number): number {
|
||||
debug(`getMessageId ${index}`)
|
||||
return binding.dcn_chatlist_get_msg_id(this.dc_chatlist, index)
|
||||
}
|
||||
|
||||
getSummary(index: number, chat?: Chat): Lot {
|
||||
debug(`getSummary ${index}`)
|
||||
const dc_chat = (chat && chat.dc_chat) || null
|
||||
return new Lot(
|
||||
binding.dcn_chatlist_get_summary(this.dc_chatlist, index, dc_chat)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
// Generated!
|
||||
|
||||
export enum C {
|
||||
DC_CERTCK_ACCEPT_INVALID_CERTIFICATES = 3,
|
||||
DC_CERTCK_AUTO = 0,
|
||||
DC_CERTCK_STRICT = 1,
|
||||
DC_CHAT_ID_ALLDONE_HINT = 7,
|
||||
DC_CHAT_ID_ARCHIVED_LINK = 6,
|
||||
DC_CHAT_ID_LAST_SPECIAL = 9,
|
||||
DC_CHAT_ID_TRASH = 3,
|
||||
DC_CHAT_TYPE_BROADCAST = 160,
|
||||
DC_CHAT_TYPE_GROUP = 120,
|
||||
DC_CHAT_TYPE_MAILINGLIST = 140,
|
||||
DC_CHAT_TYPE_SINGLE = 100,
|
||||
DC_CHAT_TYPE_UNDEFINED = 0,
|
||||
DC_CHAT_VISIBILITY_ARCHIVED = 1,
|
||||
DC_CHAT_VISIBILITY_NORMAL = 0,
|
||||
DC_CHAT_VISIBILITY_PINNED = 2,
|
||||
DC_CONNECTIVITY_CONNECTED = 4000,
|
||||
DC_CONNECTIVITY_CONNECTING = 2000,
|
||||
DC_CONNECTIVITY_NOT_CONNECTED = 1000,
|
||||
DC_CONNECTIVITY_WORKING = 3000,
|
||||
DC_CONTACT_ID_DEVICE = 5,
|
||||
DC_CONTACT_ID_INFO = 2,
|
||||
DC_CONTACT_ID_LAST_SPECIAL = 9,
|
||||
DC_CONTACT_ID_SELF = 1,
|
||||
DC_DOWNLOAD_AVAILABLE = 10,
|
||||
DC_DOWNLOAD_DONE = 0,
|
||||
DC_DOWNLOAD_FAILURE = 20,
|
||||
DC_DOWNLOAD_IN_PROGRESS = 1000,
|
||||
DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED = 2021,
|
||||
DC_EVENT_CHAT_MODIFIED = 2020,
|
||||
DC_EVENT_CONFIGURE_PROGRESS = 2041,
|
||||
DC_EVENT_CONNECTIVITY_CHANGED = 2100,
|
||||
DC_EVENT_CONTACTS_CHANGED = 2030,
|
||||
DC_EVENT_DELETED_BLOB_FILE = 151,
|
||||
DC_EVENT_ERROR = 400,
|
||||
DC_EVENT_ERROR_SELF_NOT_IN_GROUP = 410,
|
||||
DC_EVENT_IMAP_CONNECTED = 102,
|
||||
DC_EVENT_IMAP_MESSAGE_DELETED = 104,
|
||||
DC_EVENT_IMAP_MESSAGE_MOVED = 105,
|
||||
DC_EVENT_IMEX_FILE_WRITTEN = 2052,
|
||||
DC_EVENT_IMEX_PROGRESS = 2051,
|
||||
DC_EVENT_INCOMING_MSG = 2005,
|
||||
DC_EVENT_INFO = 100,
|
||||
DC_EVENT_LOCATION_CHANGED = 2035,
|
||||
DC_EVENT_MSGS_CHANGED = 2000,
|
||||
DC_EVENT_MSGS_NOTICED = 2008,
|
||||
DC_EVENT_MSG_DELIVERED = 2010,
|
||||
DC_EVENT_MSG_FAILED = 2012,
|
||||
DC_EVENT_MSG_READ = 2015,
|
||||
DC_EVENT_NEW_BLOB_FILE = 150,
|
||||
DC_EVENT_SECUREJOIN_INVITER_PROGRESS = 2060,
|
||||
DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061,
|
||||
DC_EVENT_SELFAVATAR_CHANGED = 2110,
|
||||
DC_EVENT_SMTP_CONNECTED = 101,
|
||||
DC_EVENT_SMTP_MESSAGE_SENT = 103,
|
||||
DC_EVENT_WARNING = 300,
|
||||
DC_EVENT_WEBXDC_STATUS_UPDATE = 2120,
|
||||
DC_GCL_ADD_ALLDONE_HINT = 4,
|
||||
DC_GCL_ADD_SELF = 2,
|
||||
DC_GCL_ARCHIVED_ONLY = 1,
|
||||
DC_GCL_FOR_FORWARDING = 8,
|
||||
DC_GCL_NO_SPECIALS = 2,
|
||||
DC_GCL_VERIFIED_ONLY = 1,
|
||||
DC_GCM_ADDDAYMARKER = 1,
|
||||
DC_GCM_INFO_ONLY = 2,
|
||||
DC_IMEX_EXPORT_BACKUP = 11,
|
||||
DC_IMEX_EXPORT_SELF_KEYS = 1,
|
||||
DC_IMEX_IMPORT_BACKUP = 12,
|
||||
DC_IMEX_IMPORT_SELF_KEYS = 2,
|
||||
DC_INFO_PROTECTION_DISABLED = 12,
|
||||
DC_INFO_PROTECTION_ENABLED = 11,
|
||||
DC_KEY_GEN_DEFAULT = 0,
|
||||
DC_KEY_GEN_ED25519 = 2,
|
||||
DC_KEY_GEN_RSA2048 = 1,
|
||||
DC_LP_AUTH_NORMAL = 4,
|
||||
DC_LP_AUTH_OAUTH2 = 2,
|
||||
DC_MEDIA_QUALITY_BALANCED = 0,
|
||||
DC_MEDIA_QUALITY_WORSE = 1,
|
||||
DC_MSG_AUDIO = 40,
|
||||
DC_MSG_FILE = 60,
|
||||
DC_MSG_GIF = 21,
|
||||
DC_MSG_ID_DAYMARKER = 9,
|
||||
DC_MSG_ID_LAST_SPECIAL = 9,
|
||||
DC_MSG_ID_MARKER1 = 1,
|
||||
DC_MSG_IMAGE = 20,
|
||||
DC_MSG_STICKER = 23,
|
||||
DC_MSG_TEXT = 10,
|
||||
DC_MSG_VIDEO = 50,
|
||||
DC_MSG_VIDEOCHAT_INVITATION = 70,
|
||||
DC_MSG_VOICE = 41,
|
||||
DC_MSG_WEBXDC = 80,
|
||||
DC_PROVIDER_STATUS_BROKEN = 3,
|
||||
DC_PROVIDER_STATUS_OK = 1,
|
||||
DC_PROVIDER_STATUS_PREPARATION = 2,
|
||||
DC_QR_ACCOUNT = 250,
|
||||
DC_QR_ADDR = 320,
|
||||
DC_QR_ASK_VERIFYCONTACT = 200,
|
||||
DC_QR_ASK_VERIFYGROUP = 202,
|
||||
DC_QR_ERROR = 400,
|
||||
DC_QR_FPR_MISMATCH = 220,
|
||||
DC_QR_FPR_OK = 210,
|
||||
DC_QR_FPR_WITHOUT_ADDR = 230,
|
||||
DC_QR_REVIVE_VERIFYCONTACT = 510,
|
||||
DC_QR_REVIVE_VERIFYGROUP = 512,
|
||||
DC_QR_TEXT = 330,
|
||||
DC_QR_URL = 332,
|
||||
DC_QR_WEBRTC_INSTANCE = 260,
|
||||
DC_QR_WITHDRAW_VERIFYCONTACT = 500,
|
||||
DC_QR_WITHDRAW_VERIFYGROUP = 502,
|
||||
DC_SHOW_EMAILS_ACCEPTED_CONTACTS = 1,
|
||||
DC_SHOW_EMAILS_ALL = 2,
|
||||
DC_SHOW_EMAILS_OFF = 0,
|
||||
DC_SOCKET_AUTO = 0,
|
||||
DC_SOCKET_PLAIN = 3,
|
||||
DC_SOCKET_SSL = 1,
|
||||
DC_SOCKET_STARTTLS = 2,
|
||||
DC_STATE_IN_FRESH = 10,
|
||||
DC_STATE_IN_NOTICED = 13,
|
||||
DC_STATE_IN_SEEN = 16,
|
||||
DC_STATE_OUT_DELIVERED = 26,
|
||||
DC_STATE_OUT_DRAFT = 19,
|
||||
DC_STATE_OUT_FAILED = 24,
|
||||
DC_STATE_OUT_MDN_RCVD = 28,
|
||||
DC_STATE_OUT_PENDING = 20,
|
||||
DC_STATE_OUT_PREPARING = 18,
|
||||
DC_STATE_UNDEFINED = 0,
|
||||
DC_STR_AC_SETUP_MSG_BODY = 43,
|
||||
DC_STR_AC_SETUP_MSG_SUBJECT = 42,
|
||||
DC_STR_ARCHIVEDCHATS = 40,
|
||||
DC_STR_AUDIO = 11,
|
||||
DC_STR_BAD_TIME_MSG_BODY = 85,
|
||||
DC_STR_BROADCAST_LIST = 115,
|
||||
DC_STR_CANNOT_LOGIN = 60,
|
||||
DC_STR_CANTDECRYPT_MSG_BODY = 29,
|
||||
DC_STR_CONFIGURATION_FAILED = 84,
|
||||
DC_STR_CONNECTED = 107,
|
||||
DC_STR_CONNTECTING = 108,
|
||||
DC_STR_CONTACT_NOT_VERIFIED = 36,
|
||||
DC_STR_CONTACT_SETUP_CHANGED = 37,
|
||||
DC_STR_CONTACT_VERIFIED = 35,
|
||||
DC_STR_DEVICE_MESSAGES = 68,
|
||||
DC_STR_DEVICE_MESSAGES_HINT = 70,
|
||||
DC_STR_DOWNLOAD_AVAILABILITY = 100,
|
||||
DC_STR_DRAFT = 3,
|
||||
DC_STR_E2E_AVAILABLE = 25,
|
||||
DC_STR_E2E_PREFERRED = 34,
|
||||
DC_STR_ENCRYPTEDMSG = 24,
|
||||
DC_STR_ENCR_NONE = 28,
|
||||
DC_STR_ENCR_TRANSP = 27,
|
||||
DC_STR_EPHEMERAL_DAY = 79,
|
||||
DC_STR_EPHEMERAL_DAYS = 95,
|
||||
DC_STR_EPHEMERAL_DISABLED = 75,
|
||||
DC_STR_EPHEMERAL_FOUR_WEEKS = 81,
|
||||
DC_STR_EPHEMERAL_HOUR = 78,
|
||||
DC_STR_EPHEMERAL_HOURS = 94,
|
||||
DC_STR_EPHEMERAL_MINUTE = 77,
|
||||
DC_STR_EPHEMERAL_MINUTES = 93,
|
||||
DC_STR_EPHEMERAL_SECONDS = 76,
|
||||
DC_STR_EPHEMERAL_WEEK = 80,
|
||||
DC_STR_EPHEMERAL_WEEKS = 96,
|
||||
DC_STR_ERROR = 112,
|
||||
DC_STR_ERROR_NO_NETWORK = 87,
|
||||
DC_STR_FAILED_SENDING_TO = 74,
|
||||
DC_STR_FILE = 12,
|
||||
DC_STR_FINGERPRINTS = 30,
|
||||
DC_STR_FORWARDED = 97,
|
||||
DC_STR_GIF = 23,
|
||||
DC_STR_IMAGE = 9,
|
||||
DC_STR_INCOMING_MESSAGES = 103,
|
||||
DC_STR_LAST_MSG_SENT_SUCCESSFULLY = 111,
|
||||
DC_STR_LOCATION = 66,
|
||||
DC_STR_MESSAGES = 114,
|
||||
DC_STR_MSGACTIONBYME = 63,
|
||||
DC_STR_MSGACTIONBYUSER = 62,
|
||||
DC_STR_MSGADDMEMBER = 17,
|
||||
DC_STR_MSGDELMEMBER = 18,
|
||||
DC_STR_MSGGROUPLEFT = 19,
|
||||
DC_STR_MSGGRPIMGCHANGED = 16,
|
||||
DC_STR_MSGGRPIMGDELETED = 33,
|
||||
DC_STR_MSGGRPNAME = 15,
|
||||
DC_STR_MSGLOCATIONDISABLED = 65,
|
||||
DC_STR_MSGLOCATIONENABLED = 64,
|
||||
DC_STR_NOMESSAGES = 1,
|
||||
DC_STR_NOT_CONNECTED = 121,
|
||||
DC_STR_NOT_SUPPORTED_BY_PROVIDER = 113,
|
||||
DC_STR_ONE_MOMENT = 106,
|
||||
DC_STR_OUTGOING_MESSAGES = 104,
|
||||
DC_STR_PARTIAL_DOWNLOAD_MSG_BODY = 99,
|
||||
DC_STR_PART_OF_TOTAL_USED = 116,
|
||||
DC_STR_PROTECTION_DISABLED = 89,
|
||||
DC_STR_PROTECTION_ENABLED = 88,
|
||||
DC_STR_QUOTA_EXCEEDING_MSG_BODY = 98,
|
||||
DC_STR_READRCPT = 31,
|
||||
DC_STR_READRCPT_MAILBODY = 32,
|
||||
DC_STR_REPLY_NOUN = 90,
|
||||
DC_STR_SAVED_MESSAGES = 69,
|
||||
DC_STR_SECURE_JOIN_GROUP_QR_DESC = 120,
|
||||
DC_STR_SECURE_JOIN_REPLIES = 118,
|
||||
DC_STR_SECURE_JOIN_STARTED = 117,
|
||||
DC_STR_SELF = 2,
|
||||
DC_STR_SELF_DELETED_MSG_BODY = 91,
|
||||
DC_STR_SENDING = 110,
|
||||
DC_STR_SERVER_TURNED_OFF = 92,
|
||||
DC_STR_SETUP_CONTACT_QR_DESC = 119,
|
||||
DC_STR_STICKER = 67,
|
||||
DC_STR_STORAGE_ON_DOMAIN = 105,
|
||||
DC_STR_SUBJECT_FOR_NEW_CONTACT = 73,
|
||||
DC_STR_SYNC_MSG_BODY = 102,
|
||||
DC_STR_SYNC_MSG_SUBJECT = 101,
|
||||
DC_STR_UNKNOWN_SENDER_FOR_CHAT = 72,
|
||||
DC_STR_UPDATE_REMINDER_MSG_BODY = 86,
|
||||
DC_STR_UPDATING = 109,
|
||||
DC_STR_VIDEO = 10,
|
||||
DC_STR_VIDEOCHAT_INVITATION = 82,
|
||||
DC_STR_VIDEOCHAT_INVITE_MSG_BODY = 83,
|
||||
DC_STR_VOICEMESSAGE = 7,
|
||||
DC_STR_WELCOME_MESSAGE = 71,
|
||||
DC_TEXT1_DRAFT = 1,
|
||||
DC_TEXT1_SELF = 3,
|
||||
DC_TEXT1_USERNAME = 2,
|
||||
DC_VIDEOCHATTYPE_BASICWEBRTC = 1,
|
||||
DC_VIDEOCHATTYPE_JITSI = 2,
|
||||
DC_VIDEOCHATTYPE_UNKNOWN = 0,
|
||||
}
|
||||
|
||||
// Generated!
|
||||
|
||||
export const EventId2EventName: { [key: number]: string } = {
|
||||
100: 'DC_EVENT_INFO',
|
||||
101: 'DC_EVENT_SMTP_CONNECTED',
|
||||
102: 'DC_EVENT_IMAP_CONNECTED',
|
||||
103: 'DC_EVENT_SMTP_MESSAGE_SENT',
|
||||
104: 'DC_EVENT_IMAP_MESSAGE_DELETED',
|
||||
105: 'DC_EVENT_IMAP_MESSAGE_MOVED',
|
||||
150: 'DC_EVENT_NEW_BLOB_FILE',
|
||||
151: 'DC_EVENT_DELETED_BLOB_FILE',
|
||||
300: 'DC_EVENT_WARNING',
|
||||
400: 'DC_EVENT_ERROR',
|
||||
410: 'DC_EVENT_ERROR_SELF_NOT_IN_GROUP',
|
||||
2000: 'DC_EVENT_MSGS_CHANGED',
|
||||
2005: 'DC_EVENT_INCOMING_MSG',
|
||||
2008: 'DC_EVENT_MSGS_NOTICED',
|
||||
2010: 'DC_EVENT_MSG_DELIVERED',
|
||||
2012: 'DC_EVENT_MSG_FAILED',
|
||||
2015: 'DC_EVENT_MSG_READ',
|
||||
2020: 'DC_EVENT_CHAT_MODIFIED',
|
||||
2021: 'DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED',
|
||||
2030: 'DC_EVENT_CONTACTS_CHANGED',
|
||||
2035: 'DC_EVENT_LOCATION_CHANGED',
|
||||
2041: 'DC_EVENT_CONFIGURE_PROGRESS',
|
||||
2051: 'DC_EVENT_IMEX_PROGRESS',
|
||||
2052: 'DC_EVENT_IMEX_FILE_WRITTEN',
|
||||
2060: 'DC_EVENT_SECUREJOIN_INVITER_PROGRESS',
|
||||
2061: 'DC_EVENT_SECUREJOIN_JOINER_PROGRESS',
|
||||
2100: 'DC_EVENT_CONNECTIVITY_CHANGED',
|
||||
2110: 'DC_EVENT_SELFAVATAR_CHANGED',
|
||||
2120: 'DC_EVENT_WEBXDC_STATUS_UPDATE',
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
import { integerToHexColor } from './util'
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import binding from './binding'
|
||||
const debug = require('debug')('deltachat:node:contact')
|
||||
|
||||
interface NativeContact {}
|
||||
/**
|
||||
* Wrapper around dc_contact_t*
|
||||
*/
|
||||
export class Contact {
|
||||
constructor(public dc_contact: NativeContact) {
|
||||
debug('Contact constructor')
|
||||
if (dc_contact === null) {
|
||||
throw new Error('native contact can not be null')
|
||||
}
|
||||
}
|
||||
|
||||
toJson() {
|
||||
debug('toJson')
|
||||
return {
|
||||
address: this.getAddress(),
|
||||
color: this.color,
|
||||
authName: this.authName,
|
||||
status: this.status,
|
||||
displayName: this.getDisplayName(),
|
||||
id: this.getId(),
|
||||
lastSeen: this.lastSeen,
|
||||
name: this.getName(),
|
||||
profileImage: this.getProfileImage(),
|
||||
nameAndAddr: this.getNameAndAddress(),
|
||||
isBlocked: this.isBlocked(),
|
||||
isVerified: this.isVerified(),
|
||||
}
|
||||
}
|
||||
|
||||
getAddress(): string {
|
||||
return binding.dcn_contact_get_addr(this.dc_contact)
|
||||
}
|
||||
|
||||
/** Get original contact name.
|
||||
* This is the name of the contact as defined by the contact themself.
|
||||
* If the contact themself does not define such a name,
|
||||
* an empty string is returned. */
|
||||
get authName(): string {
|
||||
return binding.dcn_contact_get_auth_name(this.dc_contact)
|
||||
}
|
||||
|
||||
get color(): string {
|
||||
return integerToHexColor(binding.dcn_contact_get_color(this.dc_contact))
|
||||
}
|
||||
|
||||
/**
|
||||
* contact's status
|
||||
*
|
||||
* Status is the last signature received in a message from this contact.
|
||||
*/
|
||||
get status(): string {
|
||||
return binding.dcn_contact_get_status(this.dc_contact)
|
||||
}
|
||||
|
||||
getDisplayName(): string {
|
||||
return binding.dcn_contact_get_display_name(this.dc_contact)
|
||||
}
|
||||
|
||||
getId(): number {
|
||||
return binding.dcn_contact_get_id(this.dc_contact)
|
||||
}
|
||||
|
||||
get lastSeen(): number {
|
||||
return binding.dcn_contact_get_last_seen(this.dc_contact)
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return binding.dcn_contact_get_name(this.dc_contact)
|
||||
}
|
||||
|
||||
getNameAndAddress(): string {
|
||||
return binding.dcn_contact_get_name_n_addr(this.dc_contact)
|
||||
}
|
||||
|
||||
getProfileImage(): string {
|
||||
return binding.dcn_contact_get_profile_image(this.dc_contact)
|
||||
}
|
||||
|
||||
isBlocked() {
|
||||
return Boolean(binding.dcn_contact_is_blocked(this.dc_contact))
|
||||
}
|
||||
|
||||
isVerified() {
|
||||
return Boolean(binding.dcn_contact_is_verified(this.dc_contact))
|
||||
}
|
||||
}
|
||||
@@ -1,955 +0,0 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import binding from './binding'
|
||||
import { C, EventId2EventName } from './constants'
|
||||
import { Chat } from './chat'
|
||||
import { ChatList } from './chatlist'
|
||||
import { Contact } from './contact'
|
||||
import { Message } from './message'
|
||||
import { Lot } from './lot'
|
||||
import { Locations } from './locations'
|
||||
import rawDebug from 'debug'
|
||||
import { AccountManager } from './deltachat'
|
||||
import { join } from 'path'
|
||||
import { EventEmitter } from 'stream'
|
||||
const debug = rawDebug('deltachat:node:index')
|
||||
|
||||
const noop = function () {}
|
||||
interface NativeContext {}
|
||||
|
||||
/**
|
||||
* Wrapper around dcn_context_t*
|
||||
*
|
||||
* only acts as event emitter when created in standalone mode (without account manager)
|
||||
* with `Context.open`
|
||||
*/
|
||||
export class Context extends EventEmitter {
|
||||
constructor(
|
||||
readonly manager: AccountManager | null,
|
||||
private inner_dcn_context: NativeContext,
|
||||
readonly account_id: number | null
|
||||
) {
|
||||
super()
|
||||
debug('DeltaChat constructor')
|
||||
if (inner_dcn_context === null) {
|
||||
throw new Error('inner_dcn_context can not be null')
|
||||
}
|
||||
}
|
||||
|
||||
/** Opens a stanalone context (without an account manager)
|
||||
* automatically starts the event handler */
|
||||
static open(cwd: string): Context {
|
||||
const dbFile = join(cwd, 'db.sqlite')
|
||||
const context = new Context(null, binding.dcn_context_new(dbFile), null)
|
||||
debug('Opened context')
|
||||
function handleCoreEvent(
|
||||
eventId: number,
|
||||
data1: number,
|
||||
data2: number | string
|
||||
) {
|
||||
const eventString = EventId2EventName[eventId]
|
||||
debug(eventString, data1, data2)
|
||||
if (!context.emit) {
|
||||
console.log('Received an event but EventEmitter is already destroyed.')
|
||||
console.log(eventString, data1, data2)
|
||||
return
|
||||
}
|
||||
context.emit(eventString, data1, data2)
|
||||
context.emit('ALL', eventString, data1, data2)
|
||||
}
|
||||
binding.dcn_start_event_handler(
|
||||
context.dcn_context,
|
||||
handleCoreEvent.bind(this)
|
||||
)
|
||||
debug('Started event handler')
|
||||
return context
|
||||
}
|
||||
|
||||
get dcn_context() {
|
||||
return this.inner_dcn_context
|
||||
}
|
||||
|
||||
get is_open() {
|
||||
return Boolean(binding.dcn_context_is_open())
|
||||
}
|
||||
|
||||
open(passphrase?: string) {
|
||||
return Boolean(
|
||||
binding.dcn_context_open(this.dcn_context, passphrase ? passphrase : '')
|
||||
)
|
||||
}
|
||||
|
||||
unref() {
|
||||
binding.dcn_context_unref(this.dcn_context)
|
||||
;(this.inner_dcn_context as any) = null
|
||||
}
|
||||
|
||||
acceptChat(chatId: number) {
|
||||
binding.dcn_accept_chat(this.dcn_context, chatId)
|
||||
}
|
||||
|
||||
blockChat(chatId: number) {
|
||||
binding.dcn_block_chat(this.dcn_context, chatId)
|
||||
}
|
||||
|
||||
addAddressBook(addressBook: string) {
|
||||
debug(`addAddressBook ${addressBook}`)
|
||||
return binding.dcn_add_address_book(this.dcn_context, addressBook)
|
||||
}
|
||||
|
||||
addContactToChat(chatId: number, contactId: number) {
|
||||
debug(`addContactToChat ${chatId} ${contactId}`)
|
||||
return Boolean(
|
||||
binding.dcn_add_contact_to_chat(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
Number(contactId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
addDeviceMessage(label: string, msg: Message | string) {
|
||||
debug(`addDeviceMessage ${label} ${msg}`)
|
||||
if (!msg) {
|
||||
throw new Error('invalid msg parameter')
|
||||
}
|
||||
if (typeof label !== 'string') {
|
||||
throw new Error('invalid label parameter, must be a string')
|
||||
}
|
||||
if (typeof msg === 'string') {
|
||||
const msgObj = this.messageNew()
|
||||
msgObj.setText(msg)
|
||||
msg = msgObj
|
||||
}
|
||||
if (!msg.dc_msg) {
|
||||
throw new Error('invalid msg object')
|
||||
}
|
||||
return binding.dcn_add_device_msg(this.dcn_context, label, msg.dc_msg)
|
||||
}
|
||||
|
||||
setChatVisibility(
|
||||
chatId: number,
|
||||
visibility:
|
||||
| C.DC_CHAT_VISIBILITY_NORMAL
|
||||
| C.DC_CHAT_VISIBILITY_ARCHIVED
|
||||
| C.DC_CHAT_VISIBILITY_PINNED
|
||||
) {
|
||||
debug(`setChatVisibility ${chatId} ${visibility}`)
|
||||
binding.dcn_set_chat_visibility(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
visibility
|
||||
)
|
||||
}
|
||||
|
||||
blockContact(contactId: number, block: boolean) {
|
||||
debug(`blockContact ${contactId} ${block}`)
|
||||
binding.dcn_block_contact(
|
||||
this.dcn_context,
|
||||
Number(contactId),
|
||||
block ? 1 : 0
|
||||
)
|
||||
}
|
||||
|
||||
checkQrCode(qrCode: string) {
|
||||
debug(`checkQrCode ${qrCode}`)
|
||||
const dc_lot = binding.dcn_check_qr(this.dcn_context, qrCode)
|
||||
let result = dc_lot ? new Lot(dc_lot) : null
|
||||
if (result) {
|
||||
return { id: result.getId(), ...result.toJson() }
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
configure(opts: any): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
debug('configure')
|
||||
|
||||
const onSuccess = () => {
|
||||
removeListeners()
|
||||
resolve()
|
||||
}
|
||||
const onFail = (error: string) => {
|
||||
removeListeners()
|
||||
reject(new Error(error))
|
||||
}
|
||||
|
||||
let onConfigure: (...args: any[]) => void
|
||||
if (this.account_id === null) {
|
||||
onConfigure = (data1: number, data2: string) => {
|
||||
if (data1 === 0) return onFail(data2)
|
||||
else if (data1 === 1000) return onSuccess()
|
||||
}
|
||||
} else {
|
||||
onConfigure = (accountId: number, data1: number, data2: string) => {
|
||||
if (this.account_id !== accountId) {
|
||||
return
|
||||
}
|
||||
if (data1 === 0) return onFail(data2)
|
||||
else if (data1 === 1000) return onSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
const removeListeners = () => {
|
||||
;(this.manager || this).removeListener(
|
||||
'DC_EVENT_CONFIGURE_PROGRESS',
|
||||
onConfigure
|
||||
)
|
||||
}
|
||||
|
||||
const registerListeners = () => {
|
||||
;(this.manager || this).on('DC_EVENT_CONFIGURE_PROGRESS', onConfigure)
|
||||
}
|
||||
|
||||
registerListeners()
|
||||
|
||||
if (!opts) opts = {}
|
||||
Object.keys(opts).forEach((key) => {
|
||||
const value = opts[key]
|
||||
this.setConfig(key, value)
|
||||
})
|
||||
|
||||
binding.dcn_configure(this.dcn_context)
|
||||
})
|
||||
}
|
||||
|
||||
continueKeyTransfer(messageId: number, setupCode: string) {
|
||||
debug(`continueKeyTransfer ${messageId}`)
|
||||
return new Promise((resolve, reject) => {
|
||||
binding.dcn_continue_key_transfer(
|
||||
this.dcn_context,
|
||||
Number(messageId),
|
||||
setupCode,
|
||||
(result: number) => resolve(result === 1)
|
||||
)
|
||||
})
|
||||
}
|
||||
/** @returns chatId */
|
||||
createBroadcastList(): number {
|
||||
debug(`createBroadcastList`)
|
||||
return binding.dcn_create_broadcast_list(this.dcn_context)
|
||||
}
|
||||
|
||||
/** @returns chatId */
|
||||
createChatByContactId(contactId: number): number {
|
||||
debug(`createChatByContactId ${contactId}`)
|
||||
return binding.dcn_create_chat_by_contact_id(
|
||||
this.dcn_context,
|
||||
Number(contactId)
|
||||
)
|
||||
}
|
||||
|
||||
/** @returns contactId */
|
||||
createContact(name: string, addr: string): number {
|
||||
debug(`createContact ${name} ${addr}`)
|
||||
return binding.dcn_create_contact(this.dcn_context, name, addr)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param chatName The name of the chat that should be created
|
||||
* @param is_protected Whether the chat should be protected at creation time
|
||||
* @returns chatId
|
||||
*/
|
||||
createGroupChat(chatName: string, is_protected: boolean = false): number {
|
||||
debug(`createGroupChat ${chatName} [protected:${is_protected}]`)
|
||||
return binding.dcn_create_group_chat(
|
||||
this.dcn_context,
|
||||
is_protected ? 1 : 0,
|
||||
chatName
|
||||
)
|
||||
}
|
||||
|
||||
deleteChat(chatId: number) {
|
||||
debug(`deleteChat ${chatId}`)
|
||||
binding.dcn_delete_chat(this.dcn_context, Number(chatId))
|
||||
}
|
||||
|
||||
deleteContact(contactId: number) {
|
||||
debug(`deleteContact ${contactId}`)
|
||||
return Boolean(
|
||||
binding.dcn_delete_contact(this.dcn_context, Number(contactId))
|
||||
)
|
||||
}
|
||||
|
||||
deleteMessages(messageIds: number[]) {
|
||||
if (!Array.isArray(messageIds)) {
|
||||
messageIds = [messageIds]
|
||||
}
|
||||
messageIds = messageIds.map((id) => Number(id))
|
||||
debug('deleteMessages', messageIds)
|
||||
binding.dcn_delete_msgs(this.dcn_context, messageIds)
|
||||
}
|
||||
|
||||
forwardMessages(messageIds: number[], chatId: number) {
|
||||
if (!Array.isArray(messageIds)) {
|
||||
messageIds = [messageIds]
|
||||
}
|
||||
messageIds = messageIds.map((id) => Number(id))
|
||||
debug('forwardMessages', messageIds)
|
||||
binding.dcn_forward_msgs(this.dcn_context, messageIds, chatId)
|
||||
}
|
||||
|
||||
getBlobdir(): string {
|
||||
debug('getBlobdir')
|
||||
return binding.dcn_get_blobdir(this.dcn_context)
|
||||
}
|
||||
|
||||
getBlockedCount(): number {
|
||||
debug('getBlockedCount')
|
||||
return binding.dcn_get_blocked_cnt(this.dcn_context)
|
||||
}
|
||||
|
||||
getBlockedContacts(): number[] {
|
||||
debug('getBlockedContacts')
|
||||
return binding.dcn_get_blocked_contacts(this.dcn_context)
|
||||
}
|
||||
|
||||
getChat(chatId: number) {
|
||||
debug(`getChat ${chatId}`)
|
||||
const dc_chat = binding.dcn_get_chat(this.dcn_context, Number(chatId))
|
||||
return dc_chat ? new Chat(dc_chat) : null
|
||||
}
|
||||
|
||||
getChatContacts(chatId: number): number[] {
|
||||
debug(`getChatContacts ${chatId}`)
|
||||
return binding.dcn_get_chat_contacts(this.dcn_context, Number(chatId))
|
||||
}
|
||||
|
||||
getChatIdByContactId(contactId: number): number {
|
||||
debug(`getChatIdByContactId ${contactId}`)
|
||||
return binding.dcn_get_chat_id_by_contact_id(
|
||||
this.dcn_context,
|
||||
Number(contactId)
|
||||
)
|
||||
}
|
||||
|
||||
getChatMedia(
|
||||
chatId: number,
|
||||
msgType1: number,
|
||||
msgType2: number,
|
||||
msgType3: number
|
||||
): number[] {
|
||||
debug(`getChatMedia ${chatId}`)
|
||||
return binding.dcn_get_chat_media(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
msgType1,
|
||||
msgType2 || 0,
|
||||
msgType3 || 0
|
||||
)
|
||||
}
|
||||
|
||||
getMimeHeaders(messageId: number): string {
|
||||
debug(`getMimeHeaders ${messageId}`)
|
||||
return binding.dcn_get_mime_headers(this.dcn_context, Number(messageId))
|
||||
}
|
||||
|
||||
getChatlistItemSummary(chatId: number, messageId: number) {
|
||||
debug(`getChatlistItemSummary ${chatId} ${messageId}`)
|
||||
return new Lot(
|
||||
binding.dcn_chatlist_get_summary2(this.dcn_context, chatId, messageId)
|
||||
)
|
||||
}
|
||||
|
||||
getChatMessages(chatId: number, flags: number, marker1before: number) {
|
||||
debug(`getChatMessages ${chatId} ${flags} ${marker1before}`)
|
||||
return binding.dcn_get_chat_msgs(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
flags,
|
||||
marker1before
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get encryption info for a chat.
|
||||
* Get a multi-line encryption info, containing encryption preferences of all members.
|
||||
* Can be used to find out why messages sent to group are not encrypted.
|
||||
*
|
||||
* @param chatId ID of the chat to get the encryption info for.
|
||||
* @return Multi-line text, must be released using dc_str_unref() after usage.
|
||||
*/
|
||||
getChatEncrytionInfo(chatId: number): string {
|
||||
return binding.dcn_get_chat_encrinfo(this.dcn_context, chatId)
|
||||
}
|
||||
|
||||
getChats(listFlags: number, queryStr: string, queryContactId: number) {
|
||||
debug('getChats')
|
||||
const result = []
|
||||
const list = this.getChatList(listFlags, queryStr, queryContactId)
|
||||
const count = list.getCount()
|
||||
for (let i = 0; i < count; i++) {
|
||||
result.push(list.getChatId(i))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
getChatList(listFlags: number, queryStr: string, queryContactId: number) {
|
||||
listFlags = listFlags || 0
|
||||
queryStr = queryStr || ''
|
||||
queryContactId = queryContactId || 0
|
||||
debug(`getChatList ${listFlags} ${queryStr} ${queryContactId}`)
|
||||
return new ChatList(
|
||||
binding.dcn_get_chatlist(
|
||||
this.dcn_context,
|
||||
listFlags,
|
||||
queryStr,
|
||||
Number(queryContactId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
getConfig(key: string): string {
|
||||
debug(`getConfig ${key}`)
|
||||
return binding.dcn_get_config(this.dcn_context, key)
|
||||
}
|
||||
|
||||
getContact(contactId: number) {
|
||||
debug(`getContact ${contactId}`)
|
||||
const dc_contact = binding.dcn_get_contact(
|
||||
this.dcn_context,
|
||||
Number(contactId)
|
||||
)
|
||||
return dc_contact ? new Contact(dc_contact) : null
|
||||
}
|
||||
|
||||
getContactEncryptionInfo(contactId: number) {
|
||||
debug(`getContactEncryptionInfo ${contactId}`)
|
||||
return binding.dcn_get_contact_encrinfo(this.dcn_context, Number(contactId))
|
||||
}
|
||||
|
||||
getContacts(listFlags: number, query: string) {
|
||||
listFlags = listFlags || 0
|
||||
query = query || ''
|
||||
debug(`getContacts ${listFlags} ${query}`)
|
||||
return binding.dcn_get_contacts(this.dcn_context, listFlags, query)
|
||||
}
|
||||
|
||||
wasDeviceMessageEverAdded(label: string) {
|
||||
debug(`wasDeviceMessageEverAdded ${label}`)
|
||||
const added = binding.dcn_was_device_msg_ever_added(this.dcn_context, label)
|
||||
return added === 1
|
||||
}
|
||||
|
||||
getDraft(chatId: number) {
|
||||
debug(`getDraft ${chatId}`)
|
||||
const dc_msg = binding.dcn_get_draft(this.dcn_context, Number(chatId))
|
||||
return dc_msg ? new Message(dc_msg) : null
|
||||
}
|
||||
|
||||
getFreshMessageCount(chatId: number): number {
|
||||
debug(`getFreshMessageCount ${chatId}`)
|
||||
return binding.dcn_get_fresh_msg_cnt(this.dcn_context, Number(chatId))
|
||||
}
|
||||
|
||||
getFreshMessages() {
|
||||
debug('getFreshMessages')
|
||||
return binding.dcn_get_fresh_msgs(this.dcn_context)
|
||||
}
|
||||
|
||||
getInfo() {
|
||||
debug('getInfo')
|
||||
const info = binding.dcn_get_info(this.dcn_context)
|
||||
return AccountManager.parseGetInfo(info)
|
||||
}
|
||||
|
||||
getMessage(messageId: number) {
|
||||
debug(`getMessage ${messageId}`)
|
||||
const dc_msg = binding.dcn_get_msg(this.dcn_context, Number(messageId))
|
||||
return dc_msg ? new Message(dc_msg) : null
|
||||
}
|
||||
|
||||
getMessageCount(chatId: number): number {
|
||||
debug(`getMessageCount ${chatId}`)
|
||||
return binding.dcn_get_msg_cnt(this.dcn_context, Number(chatId))
|
||||
}
|
||||
|
||||
getMessageInfo(messageId: number): string {
|
||||
debug(`getMessageInfo ${messageId}`)
|
||||
return binding.dcn_get_msg_info(this.dcn_context, Number(messageId))
|
||||
}
|
||||
|
||||
getMessageHTML(messageId: number): string {
|
||||
debug(`getMessageHTML ${messageId}`)
|
||||
return binding.dcn_get_msg_html(this.dcn_context, Number(messageId))
|
||||
}
|
||||
|
||||
getNextMediaMessage(
|
||||
messageId: number,
|
||||
msgType1: number,
|
||||
msgType2: number,
|
||||
msgType3: number
|
||||
) {
|
||||
debug(
|
||||
`getNextMediaMessage ${messageId} ${msgType1} ${msgType2} ${msgType3}`
|
||||
)
|
||||
return this._getNextMedia(messageId, 1, msgType1, msgType2, msgType3)
|
||||
}
|
||||
|
||||
getPreviousMediaMessage(
|
||||
messageId: number,
|
||||
msgType1: number,
|
||||
msgType2: number,
|
||||
msgType3: number
|
||||
) {
|
||||
debug(
|
||||
`getPreviousMediaMessage ${messageId} ${msgType1} ${msgType2} ${msgType3}`
|
||||
)
|
||||
return this._getNextMedia(messageId, -1, msgType1, msgType2, msgType3)
|
||||
}
|
||||
|
||||
_getNextMedia(
|
||||
messageId: number,
|
||||
dir: number,
|
||||
msgType1: number,
|
||||
msgType2: number,
|
||||
msgType3: number
|
||||
): number {
|
||||
return binding.dcn_get_next_media(
|
||||
this.dcn_context,
|
||||
Number(messageId),
|
||||
dir,
|
||||
msgType1 || 0,
|
||||
msgType2 || 0,
|
||||
msgType3 || 0
|
||||
)
|
||||
}
|
||||
|
||||
getSecurejoinQrCode(chatId: number): string {
|
||||
debug(`getSecurejoinQrCode ${chatId}`)
|
||||
return binding.dcn_get_securejoin_qr(this.dcn_context, Number(chatId))
|
||||
}
|
||||
|
||||
getSecurejoinQrCodeSVG(chatId: number): string {
|
||||
debug(`getSecurejoinQrCodeSVG ${chatId}`)
|
||||
return binding.dcn_get_securejoin_qr_svg(this.dcn_context, chatId)
|
||||
}
|
||||
|
||||
startIO(): void {
|
||||
debug(`startIO`)
|
||||
binding.dcn_start_io(this.dcn_context)
|
||||
}
|
||||
|
||||
stopIO(): void {
|
||||
debug(`stopIO`)
|
||||
binding.dcn_stop_io(this.dcn_context)
|
||||
}
|
||||
|
||||
stopOngoingProcess(): void {
|
||||
debug(`stopOngoingProcess`)
|
||||
binding.dcn_stop_ongoing_process(this.dcn_context)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @deprectated please use `AccountManager.getSystemInfo()` instead
|
||||
*/
|
||||
static getSystemInfo() {
|
||||
return AccountManager.getSystemInfo()
|
||||
}
|
||||
|
||||
getConnectivity(): number {
|
||||
return binding.dcn_get_connectivity(this.dcn_context)
|
||||
}
|
||||
|
||||
getConnectivityHTML(): String {
|
||||
return binding.dcn_get_connectivity_html(this.dcn_context)
|
||||
}
|
||||
|
||||
importExport(what: number, param1: string, param2 = '') {
|
||||
debug(`importExport ${what} ${param1} ${param2}`)
|
||||
binding.dcn_imex(this.dcn_context, what, param1, param2)
|
||||
}
|
||||
|
||||
importExportHasBackup(dir: string) {
|
||||
debug(`importExportHasBackup ${dir}`)
|
||||
return binding.dcn_imex_has_backup(this.dcn_context, dir)
|
||||
}
|
||||
|
||||
initiateKeyTransfer(): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
debug('initiateKeyTransfer2')
|
||||
binding.dcn_initiate_key_transfer(this.dcn_context, resolve)
|
||||
})
|
||||
}
|
||||
|
||||
isConfigured() {
|
||||
debug('isConfigured')
|
||||
return Boolean(binding.dcn_is_configured(this.dcn_context))
|
||||
}
|
||||
|
||||
isContactInChat(chatId: number, contactId: number) {
|
||||
debug(`isContactInChat ${chatId} ${contactId}`)
|
||||
return Boolean(
|
||||
binding.dcn_is_contact_in_chat(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
Number(contactId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns resulting chat id or 0 on error
|
||||
*/
|
||||
joinSecurejoin(qrCode: string): number {
|
||||
debug(`joinSecurejoin ${qrCode}`)
|
||||
return binding.dcn_join_securejoin(this.dcn_context, qrCode)
|
||||
}
|
||||
|
||||
lookupContactIdByAddr(addr: string): number {
|
||||
debug(`lookupContactIdByAddr ${addr}`)
|
||||
return binding.dcn_lookup_contact_id_by_addr(this.dcn_context, addr)
|
||||
}
|
||||
|
||||
markNoticedChat(chatId: number) {
|
||||
debug(`markNoticedChat ${chatId}`)
|
||||
binding.dcn_marknoticed_chat(this.dcn_context, Number(chatId))
|
||||
}
|
||||
|
||||
markSeenMessages(messageIds: number[]) {
|
||||
if (!Array.isArray(messageIds)) {
|
||||
messageIds = [messageIds]
|
||||
}
|
||||
messageIds = messageIds.map((id) => Number(id))
|
||||
debug('markSeenMessages', messageIds)
|
||||
binding.dcn_markseen_msgs(this.dcn_context, messageIds)
|
||||
}
|
||||
|
||||
maybeNetwork() {
|
||||
debug('maybeNetwork')
|
||||
binding.dcn_maybe_network(this.dcn_context)
|
||||
}
|
||||
|
||||
messageNew(viewType = C.DC_MSG_TEXT) {
|
||||
debug(`messageNew ${viewType}`)
|
||||
return new Message(binding.dcn_msg_new(this.dcn_context, viewType))
|
||||
}
|
||||
|
||||
removeContactFromChat(chatId: number, contactId: number) {
|
||||
debug(`removeContactFromChat ${chatId} ${contactId}`)
|
||||
return Boolean(
|
||||
binding.dcn_remove_contact_from_chat(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
Number(contactId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param chatId ID of the chat to search messages in. Set this to 0 for a global search.
|
||||
* @param query The query to search for.
|
||||
*/
|
||||
searchMessages(chatId: number, query: string): number[] {
|
||||
debug(`searchMessages ${chatId} ${query}`)
|
||||
return binding.dcn_search_msgs(this.dcn_context, Number(chatId), query)
|
||||
}
|
||||
|
||||
sendMessage(chatId: number, msg: string | Message) {
|
||||
debug(`sendMessage ${chatId}`)
|
||||
if (!msg) {
|
||||
throw new Error('invalid msg parameter')
|
||||
}
|
||||
if (typeof msg === 'string') {
|
||||
const msgObj = this.messageNew()
|
||||
msgObj.setText(msg)
|
||||
msg = msgObj
|
||||
}
|
||||
if (!msg.dc_msg) {
|
||||
throw new Error('invalid msg object')
|
||||
}
|
||||
return binding.dcn_send_msg(this.dcn_context, Number(chatId), msg.dc_msg)
|
||||
}
|
||||
|
||||
downloadFullMessage(messageId: number) {
|
||||
binding.dcn_download_full_msg(this.dcn_context, messageId)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Promise<number>} Promise that resolves into the resulting message id
|
||||
*/
|
||||
sendVideochatInvitation(chatId: number): Promise<number> {
|
||||
debug(`sendVideochatInvitation ${chatId}`)
|
||||
return new Promise((resolve, reject) => {
|
||||
binding.dcn_send_videochat_invitation(
|
||||
this.dcn_context,
|
||||
chatId,
|
||||
(result: number) => {
|
||||
if (result !== 0) {
|
||||
resolve(result)
|
||||
} else {
|
||||
reject(
|
||||
'Videochatinvitation failed to send, see error events for detailed info'
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
setChatName(chatId: number, name: string) {
|
||||
debug(`setChatName ${chatId} ${name}`)
|
||||
return Boolean(
|
||||
binding.dcn_set_chat_name(this.dcn_context, Number(chatId), name)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param chatId
|
||||
* @param protect
|
||||
* @returns success boolean
|
||||
*/
|
||||
setChatProtection(chatId: number, protect: boolean) {
|
||||
debug(`setChatProtection ${chatId} ${protect}`)
|
||||
return Boolean(
|
||||
binding.dcn_set_chat_protection(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
protect ? 1 : 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
getChatEphemeralTimer(chatId: number): number {
|
||||
debug(`getChatEphemeralTimer ${chatId}`)
|
||||
return binding.dcn_get_chat_ephemeral_timer(
|
||||
this.dcn_context,
|
||||
Number(chatId)
|
||||
)
|
||||
}
|
||||
|
||||
setChatEphemeralTimer(chatId: number, timer: number) {
|
||||
debug(`setChatEphemeralTimer ${chatId} ${timer}`)
|
||||
return Boolean(
|
||||
binding.dcn_set_chat_ephemeral_timer(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
Number(timer)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
setChatProfileImage(chatId: number, image: string) {
|
||||
debug(`setChatProfileImage ${chatId} ${image}`)
|
||||
return Boolean(
|
||||
binding.dcn_set_chat_profile_image(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
image || ''
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
setConfig(key: string, value: string | boolean | number): number {
|
||||
debug(`setConfig (string) ${key} ${value}`)
|
||||
if (value === null) {
|
||||
return binding.dcn_set_config_null(this.dcn_context, key)
|
||||
} else {
|
||||
if (typeof value === 'boolean') {
|
||||
value = value === true ? '1' : '0'
|
||||
} else if (typeof value === 'number') {
|
||||
value = String(value)
|
||||
}
|
||||
return binding.dcn_set_config(this.dcn_context, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
setConfigFromQr(qrcodeContent: string): boolean {
|
||||
return Boolean(
|
||||
binding.dcn_set_config_from_qr(this.dcn_context, qrcodeContent)
|
||||
)
|
||||
}
|
||||
|
||||
estimateDeletionCount(fromServer: boolean, seconds: number): number {
|
||||
debug(`estimateDeletionCount fromServer: ${fromServer} seconds: ${seconds}`)
|
||||
return binding.dcn_estimate_deletion_cnt(
|
||||
this.dcn_context,
|
||||
fromServer === true ? 1 : 0,
|
||||
seconds
|
||||
)
|
||||
}
|
||||
|
||||
setStockTranslation(stockId: number, stockMsg: string) {
|
||||
debug(`setStockTranslation ${stockId} ${stockMsg}`)
|
||||
return Boolean(
|
||||
binding.dcn_set_stock_translation(
|
||||
this.dcn_context,
|
||||
Number(stockId),
|
||||
stockMsg
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
setDraft(chatId: number, msg: Message | null) {
|
||||
debug(`setDraft ${chatId}`)
|
||||
binding.dcn_set_draft(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
msg ? msg.dc_msg : null
|
||||
)
|
||||
}
|
||||
|
||||
setLocation(latitude: number, longitude: number, accuracy: number) {
|
||||
debug(`setLocation ${latitude}`)
|
||||
binding.dcn_set_location(
|
||||
this.dcn_context,
|
||||
Number(latitude),
|
||||
Number(longitude),
|
||||
Number(accuracy)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* @param chatId Chat-id to get location information for.
|
||||
* 0 to get locations independently of the chat.
|
||||
* @param contactId Contact id to get location information for.
|
||||
* If also a chat-id is given, this should be a member of the given chat.
|
||||
* 0 to get locations independently of the contact.
|
||||
* @param timestampFrom Start of timespan to return.
|
||||
* Must be given in number of seconds since 00:00 hours, Jan 1, 1970 UTC.
|
||||
* 0 for "start from the beginning".
|
||||
* @param timestampTo End of timespan to return.
|
||||
* Must be given in number of seconds since 00:00 hours, Jan 1, 1970 UTC.
|
||||
* 0 for "all up to now".
|
||||
* @return Array of locations, NULL is never returned.
|
||||
* The array is sorted decending;
|
||||
* the first entry in the array is the location with the newest timestamp.
|
||||
*
|
||||
* Examples:
|
||||
* // get locations from the last hour for a global map
|
||||
* getLocations(0, 0, time(NULL)-60*60, 0);
|
||||
*
|
||||
* // get locations from a contact for a global map
|
||||
* getLocations(0, contact_id, 0, 0);
|
||||
*
|
||||
* // get all locations known for a given chat
|
||||
* getLocations(chat_id, 0, 0, 0);
|
||||
*
|
||||
* // get locations from a single contact for a given chat
|
||||
* getLocations(chat_id, contact_id, 0, 0);
|
||||
*/
|
||||
|
||||
getLocations(
|
||||
chatId: number,
|
||||
contactId: number,
|
||||
timestampFrom = 0,
|
||||
timestampTo = 0
|
||||
) {
|
||||
const locations = new Locations(
|
||||
binding.dcn_get_locations(
|
||||
this.dcn_context,
|
||||
Number(chatId),
|
||||
Number(contactId),
|
||||
timestampFrom,
|
||||
timestampTo
|
||||
)
|
||||
)
|
||||
return locations.toJson()
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param duration The duration (0 for no mute, -1 for forever mute, everything else is is the relative mute duration from now in seconds)
|
||||
*/
|
||||
setChatMuteDuration(chatId: number, duration: number) {
|
||||
return Boolean(
|
||||
binding.dcn_set_chat_mute_duration(this.dcn_context, chatId, duration)
|
||||
)
|
||||
}
|
||||
|
||||
/** get information about the provider */
|
||||
getProviderFromEmail(email: string) {
|
||||
debug('DeltaChat.getProviderFromEmail')
|
||||
const provider = binding.dcn_provider_new_from_email(
|
||||
this.dcn_context,
|
||||
email
|
||||
)
|
||||
if (!provider) {
|
||||
return undefined
|
||||
}
|
||||
return {
|
||||
before_login_hint: binding.dcn_provider_get_before_login_hint(provider),
|
||||
overview_page: binding.dcn_provider_get_overview_page(provider),
|
||||
status: binding.dcn_provider_get_status(provider),
|
||||
}
|
||||
}
|
||||
|
||||
sendWebxdcStatusUpdate<T>(
|
||||
msgId: number,
|
||||
json: WebxdcSendingStatusUpdate<T>,
|
||||
descr: string
|
||||
) {
|
||||
return Boolean(
|
||||
binding.dcn_send_webxdc_status_update(
|
||||
this.dcn_context,
|
||||
msgId,
|
||||
JSON.stringify(json),
|
||||
descr
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
getWebxdcStatusUpdates<T>(
|
||||
msgId: number,
|
||||
serial = 0
|
||||
): WebxdcReceivedStatusUpdate<T>[] {
|
||||
return JSON.parse(
|
||||
binding.dcn_get_webxdc_status_updates(this.dcn_context, msgId, serial)
|
||||
)
|
||||
}
|
||||
|
||||
/** the string contains the binary data, it is an "u8 string", maybe we will use a more efficient type in the future. */
|
||||
getWebxdcBlob(message: Message, filename: string): Buffer | null {
|
||||
return binding.dcn_msg_get_webxdc_blob(message.dc_msg, filename)
|
||||
}
|
||||
}
|
||||
|
||||
export type WebxdcInfo = {
|
||||
name: string
|
||||
icon: string
|
||||
summary: string
|
||||
/**
|
||||
* if set by the webxdc, name of the document in edit
|
||||
*/
|
||||
document?: string
|
||||
}
|
||||
|
||||
type WebxdcSendingStatusUpdate<T> = {
|
||||
/** the payload, deserialized json:
|
||||
* any javascript primitive, array or object. */
|
||||
payload: T
|
||||
/** optional, short, informational message that will be added to the chat,
|
||||
* eg. "Alice voted" or "Bob scored 123 in MyGame";
|
||||
* usually only one line of text is shown,
|
||||
* use this option sparingly to not spam the chat. */
|
||||
info?: string
|
||||
/** optional, short text, shown beside app icon;
|
||||
* it is recommended to use some aggregated value,
|
||||
* eg. "8 votes", "Highscore: 123" */
|
||||
summary?: string
|
||||
/**
|
||||
* optional, name of the document in edit,
|
||||
* must not be used eg. in games where the Webxdc does not create documents
|
||||
*/
|
||||
document?: string
|
||||
}
|
||||
|
||||
type WebxdcReceivedStatusUpdate<T> = {
|
||||
/** the payload, deserialized json */
|
||||
payload: T
|
||||
/** the serial number of this update. Serials are larger `0` and newer serials have higher numbers. */
|
||||
serial: number
|
||||
/** the maximum serial currently known.
|
||||
* If `max_serial` equals `serial` this update is the last update (until new network messages arrive). */
|
||||
max_serial: number
|
||||
/** optional, short, informational message. */
|
||||
info?: string
|
||||
/** optional, short text, shown beside app icon. If there are no updates, an empty JSON-array is returned. */
|
||||
summary?: string
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import binding from './binding'
|
||||
import { EventId2EventName } from './constants'
|
||||
import { EventEmitter } from 'events'
|
||||
import { existsSync } from 'fs'
|
||||
import rawDebug from 'debug'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { Context } from './context'
|
||||
const debug = rawDebug('deltachat:node:index')
|
||||
|
||||
const noop = function () {}
|
||||
interface NativeAccount {}
|
||||
|
||||
/**
|
||||
* Wrapper around dcn_account_t*
|
||||
*/
|
||||
export class AccountManager extends EventEmitter {
|
||||
dcn_accounts: NativeAccount
|
||||
accountDir: string
|
||||
|
||||
constructor(cwd: string, os = 'deltachat-node') {
|
||||
debug('DeltaChat constructor')
|
||||
super()
|
||||
|
||||
this.accountDir = cwd
|
||||
this.dcn_accounts = binding.dcn_accounts_new(os, this.accountDir)
|
||||
}
|
||||
|
||||
getAllAccountIds() {
|
||||
return binding.dcn_accounts_get_all(this.dcn_accounts)
|
||||
}
|
||||
|
||||
selectAccount(account_id: number) {
|
||||
return binding.dcn_accounts_select_account(this.dcn_accounts, account_id)
|
||||
}
|
||||
|
||||
selectedAccount(): number {
|
||||
return binding.dcn_accounts_get_selected_account(this.dcn_accounts)
|
||||
}
|
||||
|
||||
addAccount(): number {
|
||||
return binding.dcn_accounts_add_account(this.dcn_accounts)
|
||||
}
|
||||
|
||||
addClosedAccount(): number {
|
||||
return binding.dcn_accounts_add_closed_account(this.dcn_accounts)
|
||||
}
|
||||
|
||||
removeAccount(account_id: number) {
|
||||
return binding.dcn_accounts_remove_account(this.dcn_accounts, account_id)
|
||||
}
|
||||
|
||||
accountContext(account_id: number) {
|
||||
const native_context = binding.dcn_accounts_get_account(
|
||||
this.dcn_accounts,
|
||||
account_id
|
||||
)
|
||||
if (native_context === null) {
|
||||
throw new Error(
|
||||
`could not get context with id ${account_id}, does it even exist? please check your ids`
|
||||
)
|
||||
}
|
||||
return new Context(this, native_context, account_id)
|
||||
}
|
||||
|
||||
migrateAccount(dbfile: string): number {
|
||||
return binding.dcn_accounts_migrate_account(this.dcn_accounts, dbfile)
|
||||
}
|
||||
|
||||
close() {
|
||||
this.stopIO()
|
||||
debug('unrefing context')
|
||||
binding.dcn_accounts_unref(this.dcn_accounts)
|
||||
debug('Unref end')
|
||||
}
|
||||
|
||||
emit(
|
||||
event: string | symbol,
|
||||
account_id: number,
|
||||
data1: any,
|
||||
data2: any
|
||||
): boolean {
|
||||
super.emit('ALL', event, account_id, data1, data2)
|
||||
return super.emit(event, account_id, data1, data2)
|
||||
}
|
||||
|
||||
handleCoreEvent(
|
||||
eventId: number,
|
||||
accountId: number,
|
||||
data1: number,
|
||||
data2: number | string
|
||||
) {
|
||||
const eventString = EventId2EventName[eventId]
|
||||
debug('event', eventString, accountId, data1, data2)
|
||||
debug(eventString, data1, data2)
|
||||
if (!this.emit) {
|
||||
console.log('Received an event but EventEmitter is already destroyed.')
|
||||
console.log(eventString, data1, data2)
|
||||
return
|
||||
}
|
||||
this.emit(eventString, accountId, data1, data2)
|
||||
}
|
||||
|
||||
startEvents() {
|
||||
if (this.dcn_accounts === null) {
|
||||
throw new Error('dcn_account is null')
|
||||
}
|
||||
binding.dcn_accounts_start_event_handler(
|
||||
this.dcn_accounts,
|
||||
this.handleCoreEvent.bind(this)
|
||||
)
|
||||
debug('Started event handler')
|
||||
}
|
||||
|
||||
startIO() {
|
||||
binding.dcn_accounts_start_io(this.dcn_accounts)
|
||||
}
|
||||
|
||||
stopIO() {
|
||||
binding.dcn_accounts_stop_io(this.dcn_accounts)
|
||||
}
|
||||
|
||||
static maybeValidAddr(addr: string) {
|
||||
debug('DeltaChat.maybeValidAddr')
|
||||
if (addr === null) return false
|
||||
return Boolean(binding.dcn_maybe_valid_addr(addr))
|
||||
}
|
||||
|
||||
static parseGetInfo(info: string) {
|
||||
debug('static _getInfo')
|
||||
const result: { [key: string]: string } = {}
|
||||
|
||||
const regex = /^(\w+)=(.*)$/i
|
||||
info
|
||||
.split('\n')
|
||||
.filter(Boolean)
|
||||
.forEach((line) => {
|
||||
const match = regex.exec(line)
|
||||
if (match) {
|
||||
result[match[1]] = match[2]
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
static newTemporary() {
|
||||
let directory = null
|
||||
while (true) {
|
||||
const randomString = Math.random().toString(36).substr(2, 5)
|
||||
directory = join(tmpdir(), 'deltachat-' + randomString)
|
||||
if (!existsSync(directory)) break
|
||||
}
|
||||
const dc = new AccountManager(directory)
|
||||
const accountId = dc.addAccount()
|
||||
const context = dc.accountContext(accountId)
|
||||
return { dc, context, accountId, directory }
|
||||
}
|
||||
|
||||
static getSystemInfo() {
|
||||
debug('DeltaChat.getSystemInfo')
|
||||
const { dc, context } = AccountManager.newTemporary()
|
||||
const info = AccountManager.parseGetInfo(
|
||||
binding.dcn_get_info(context.dcn_context)
|
||||
)
|
||||
const {
|
||||
deltachat_core_version,
|
||||
sqlite_version,
|
||||
sqlite_thread_safe,
|
||||
libetpan_version,
|
||||
openssl_version,
|
||||
compile_date,
|
||||
arch,
|
||||
} = info
|
||||
const result = {
|
||||
deltachat_core_version,
|
||||
sqlite_version,
|
||||
sqlite_thread_safe,
|
||||
libetpan_version,
|
||||
openssl_version,
|
||||
compile_date,
|
||||
arch,
|
||||
}
|
||||
context.unref()
|
||||
dc.close()
|
||||
return result
|
||||
}
|
||||
|
||||
/** get information about the provider
|
||||
*
|
||||
* This function creates a temporary context to be standalone,
|
||||
* if posible use `Context.getProviderFromEmail` instead. (otherwise potential proxy settings are not used)
|
||||
* @deprecated
|
||||
*/
|
||||
static getProviderFromEmail(email: string) {
|
||||
debug('DeltaChat.getProviderFromEmail')
|
||||
const { dc, context } = AccountManager.newTemporary()
|
||||
const provider = context.getProviderFromEmail(email)
|
||||
context.unref()
|
||||
dc.close()
|
||||
return provider
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { AccountManager } from './deltachat'
|
||||
|
||||
export default AccountManager
|
||||
|
||||
export { Context } from './context'
|
||||
export { Chat } from './chat'
|
||||
export { ChatList } from './chatlist'
|
||||
export { C } from './constants'
|
||||
export { Contact } from './contact'
|
||||
export { AccountManager as DeltaChat }
|
||||
export { Locations } from './locations'
|
||||
export { Lot } from './lot'
|
||||
export {
|
||||
Message,
|
||||
MessageState,
|
||||
MessageViewType,
|
||||
MessageDownloadState,
|
||||
} from './message'
|
||||
|
||||
export * from './types'
|
||||
@@ -1,82 +0,0 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
const binding = require('../binding')
|
||||
const debug = require('debug')('deltachat:node:locations')
|
||||
|
||||
interface NativeLocations {}
|
||||
/**
|
||||
* Wrapper around dc_location_t*
|
||||
*/
|
||||
export class Locations {
|
||||
constructor(public dc_locations: NativeLocations) {
|
||||
debug('Locations constructor')
|
||||
if (dc_locations === null) {
|
||||
throw new Error('dc_locations can not be null')
|
||||
}
|
||||
}
|
||||
|
||||
locationToJson(index: number) {
|
||||
debug('locationToJson')
|
||||
return {
|
||||
accuracy: this.getAccuracy(index),
|
||||
latitude: this.getLatitude(index),
|
||||
longitude: this.getLongitude(index),
|
||||
timestamp: this.getTimestamp(index),
|
||||
contactId: this.getContactId(index),
|
||||
msgId: this.getMsgId(index),
|
||||
chatId: this.getChatId(index),
|
||||
isIndependent: this.isIndependent(index),
|
||||
marker: this.getMarker(index),
|
||||
}
|
||||
}
|
||||
|
||||
toJson(): ReturnType<Locations['locationToJson']>[] {
|
||||
debug('toJson')
|
||||
const locations = []
|
||||
const count = this.getCount()
|
||||
for (let index = 0; index < count; index++) {
|
||||
locations.push(this.locationToJson(index))
|
||||
}
|
||||
return locations
|
||||
}
|
||||
|
||||
getCount(): number {
|
||||
return binding.dcn_array_get_cnt(this.dc_locations)
|
||||
}
|
||||
|
||||
getAccuracy(index: number): number {
|
||||
return binding.dcn_array_get_accuracy(this.dc_locations, index)
|
||||
}
|
||||
|
||||
getLatitude(index: number): number {
|
||||
return binding.dcn_array_get_latitude(this.dc_locations, index)
|
||||
}
|
||||
|
||||
getLongitude(index: number): number {
|
||||
return binding.dcn_array_get_longitude(this.dc_locations, index)
|
||||
}
|
||||
|
||||
getTimestamp(index: number): number {
|
||||
return binding.dcn_array_get_timestamp(this.dc_locations, index)
|
||||
}
|
||||
|
||||
getMsgId(index: number): number {
|
||||
return binding.dcn_array_get_msg_id(this.dc_locations, index)
|
||||
}
|
||||
|
||||
getContactId(index: number): number {
|
||||
return binding.dcn_array_get_contact_id(this.dc_locations, index)
|
||||
}
|
||||
|
||||
getChatId(index: number): number {
|
||||
return binding.dcn_array_get_chat_id(this.dc_locations, index)
|
||||
}
|
||||
|
||||
isIndependent(index: number): boolean {
|
||||
return binding.dcn_array_is_independent(this.dc_locations, index)
|
||||
}
|
||||
|
||||
getMarker(index: number): string {
|
||||
return binding.dcn_array_get_marker(this.dc_locations, index)
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
const binding = require('../binding')
|
||||
const debug = require('debug')('deltachat:node:lot')
|
||||
|
||||
interface NativeLot {}
|
||||
/**
|
||||
* Wrapper around dc_lot_t*
|
||||
*/
|
||||
export class Lot {
|
||||
constructor(public dc_lot: NativeLot) {
|
||||
debug('Lot constructor')
|
||||
if (dc_lot === null) {
|
||||
throw new Error('dc_lot can not be null')
|
||||
}
|
||||
}
|
||||
|
||||
toJson() {
|
||||
debug('toJson')
|
||||
return {
|
||||
state: this.getState(),
|
||||
text1: this.getText1(),
|
||||
text1Meaning: this.getText1Meaning(),
|
||||
text2: this.getText2(),
|
||||
timestamp: this.getTimestamp(),
|
||||
}
|
||||
}
|
||||
|
||||
getId(): number {
|
||||
return binding.dcn_lot_get_id(this.dc_lot)
|
||||
}
|
||||
|
||||
getState(): number {
|
||||
return binding.dcn_lot_get_state(this.dc_lot)
|
||||
}
|
||||
|
||||
getText1(): string {
|
||||
return binding.dcn_lot_get_text1(this.dc_lot)
|
||||
}
|
||||
|
||||
getText1Meaning(): string {
|
||||
return binding.dcn_lot_get_text1_meaning(this.dc_lot)
|
||||
}
|
||||
|
||||
getText2(): string {
|
||||
return binding.dcn_lot_get_text2(this.dc_lot)
|
||||
}
|
||||
|
||||
getTimestamp(): number {
|
||||
return binding.dcn_lot_get_timestamp(this.dc_lot)
|
||||
}
|
||||
}
|
||||
@@ -1,370 +0,0 @@
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import binding from './binding'
|
||||
import { C } from './constants'
|
||||
import { Lot } from './lot'
|
||||
import { Chat } from './chat'
|
||||
import { WebxdcInfo } from './context'
|
||||
const debug = require('debug')('deltachat:node:message')
|
||||
|
||||
export enum MessageDownloadState {
|
||||
Available = C.DC_DOWNLOAD_AVAILABLE,
|
||||
Done = C.DC_DOWNLOAD_DONE,
|
||||
Failure = C.DC_DOWNLOAD_FAILURE,
|
||||
InProgress = C.DC_DOWNLOAD_IN_PROGRESS,
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for message states so you can do e.g.
|
||||
*
|
||||
* if (msg.getState().isPending()) { .. }
|
||||
*
|
||||
*/
|
||||
export class MessageState {
|
||||
constructor(public state: number) {
|
||||
debug(`MessageState constructor ${state}`)
|
||||
}
|
||||
|
||||
isUndefined() {
|
||||
return this.state === C.DC_STATE_UNDEFINED
|
||||
}
|
||||
|
||||
isFresh() {
|
||||
return this.state === C.DC_STATE_IN_FRESH
|
||||
}
|
||||
|
||||
isNoticed() {
|
||||
return this.state === C.DC_STATE_IN_NOTICED
|
||||
}
|
||||
|
||||
isSeen() {
|
||||
return this.state === C.DC_STATE_IN_SEEN
|
||||
}
|
||||
|
||||
isPending() {
|
||||
return this.state === C.DC_STATE_OUT_PENDING
|
||||
}
|
||||
|
||||
isFailed() {
|
||||
return this.state === C.DC_STATE_OUT_FAILED
|
||||
}
|
||||
|
||||
isDelivered() {
|
||||
return this.state === C.DC_STATE_OUT_DELIVERED
|
||||
}
|
||||
|
||||
isReceived() {
|
||||
return this.state === C.DC_STATE_OUT_MDN_RCVD
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for message types so you can do e.g.
|
||||
*
|
||||
* if (msg.getViewType().isVideo()) { .. }
|
||||
*
|
||||
*/
|
||||
export class MessageViewType {
|
||||
constructor(public viewType: number) {
|
||||
debug(`MessageViewType constructor ${viewType}`)
|
||||
}
|
||||
|
||||
isText() {
|
||||
return this.viewType === C.DC_MSG_TEXT
|
||||
}
|
||||
|
||||
isImage() {
|
||||
return this.viewType === C.DC_MSG_IMAGE || this.viewType === C.DC_MSG_GIF
|
||||
}
|
||||
|
||||
isGif() {
|
||||
return this.viewType === C.DC_MSG_GIF
|
||||
}
|
||||
|
||||
isAudio() {
|
||||
return this.viewType === C.DC_MSG_AUDIO || this.viewType === C.DC_MSG_VOICE
|
||||
}
|
||||
|
||||
isVoice() {
|
||||
return this.viewType === C.DC_MSG_VOICE
|
||||
}
|
||||
|
||||
isVideo() {
|
||||
return this.viewType === C.DC_MSG_VIDEO
|
||||
}
|
||||
|
||||
isFile() {
|
||||
return this.viewType === C.DC_MSG_FILE
|
||||
}
|
||||
|
||||
isVideochatInvitation() {
|
||||
return this.viewType === C.DC_MSG_VIDEOCHAT_INVITATION
|
||||
}
|
||||
}
|
||||
|
||||
interface NativeMessage {}
|
||||
/**
|
||||
* Wrapper around dc_msg_t*
|
||||
*/
|
||||
export class Message {
|
||||
constructor(public dc_msg: NativeMessage) {
|
||||
debug('Message constructor')
|
||||
if (dc_msg === null) {
|
||||
throw new Error('dc_msg can not be null')
|
||||
}
|
||||
}
|
||||
|
||||
toJson() {
|
||||
debug('toJson')
|
||||
const quotedMessage = this.getQuotedMessage()
|
||||
const viewType = binding.dcn_msg_get_viewtype(this.dc_msg)
|
||||
return {
|
||||
chatId: this.getChatId(),
|
||||
webxdcInfo: viewType == C.DC_MSG_WEBXDC ? this.webxdcInfo : null,
|
||||
downloadState: this.downloadState,
|
||||
duration: this.getDuration(),
|
||||
file: this.getFile(),
|
||||
fromId: this.getFromId(),
|
||||
id: this.getId(),
|
||||
quotedText: this.getQuotedText(),
|
||||
quotedMessageId: quotedMessage ? quotedMessage.getId() : null,
|
||||
receivedTimestamp: this.getReceivedTimestamp(),
|
||||
sortTimestamp: this.getSortTimestamp(),
|
||||
text: this.getText(),
|
||||
timestamp: this.getTimestamp(),
|
||||
hasLocation: this.hasLocation(),
|
||||
hasHTML: this.hasHTML,
|
||||
viewType,
|
||||
state: binding.dcn_msg_get_state(this.dc_msg),
|
||||
hasDeviatingTimestamp: this.hasDeviatingTimestamp(),
|
||||
showPadlock: this.getShowpadlock(),
|
||||
summary: this.getSummary().toJson(),
|
||||
subject: this.subject,
|
||||
isSetupmessage: this.isSetupmessage(),
|
||||
isInfo: this.isInfo(),
|
||||
isForwarded: this.isForwarded(),
|
||||
dimensions: {
|
||||
height: this.getHeight(),
|
||||
width: this.getWidth(),
|
||||
},
|
||||
videochatType: this.getVideochatType(),
|
||||
videochatUrl: this.getVideochatUrl(),
|
||||
overrideSenderName: this.overrideSenderName,
|
||||
parentId: this.parent?.getId(),
|
||||
}
|
||||
}
|
||||
|
||||
getChatId(): number {
|
||||
return binding.dcn_msg_get_chat_id(this.dc_msg)
|
||||
}
|
||||
|
||||
get webxdcInfo(): WebxdcInfo | null {
|
||||
let info = binding.dcn_msg_get_webxdc_info(this.dc_msg)
|
||||
return info
|
||||
? JSON.parse(binding.dcn_msg_get_webxdc_info(this.dc_msg))
|
||||
: null
|
||||
}
|
||||
|
||||
get downloadState(): MessageDownloadState {
|
||||
return binding.dcn_msg_get_download_state(this.dc_msg)
|
||||
}
|
||||
|
||||
get parent(): Message | null {
|
||||
let msg = binding.dcn_msg_get_parent(this.dc_msg)
|
||||
return msg ? new Message(msg) : null
|
||||
}
|
||||
|
||||
getDuration(): number {
|
||||
return binding.dcn_msg_get_duration(this.dc_msg)
|
||||
}
|
||||
|
||||
getFile(): string {
|
||||
return binding.dcn_msg_get_file(this.dc_msg)
|
||||
}
|
||||
|
||||
getFilebytes(): number {
|
||||
return binding.dcn_msg_get_filebytes(this.dc_msg)
|
||||
}
|
||||
|
||||
getFilemime(): string {
|
||||
return binding.dcn_msg_get_filemime(this.dc_msg)
|
||||
}
|
||||
|
||||
getFilename(): string {
|
||||
return binding.dcn_msg_get_filename(this.dc_msg)
|
||||
}
|
||||
|
||||
getFromId(): number {
|
||||
return binding.dcn_msg_get_from_id(this.dc_msg)
|
||||
}
|
||||
|
||||
getHeight(): number {
|
||||
return binding.dcn_msg_get_height(this.dc_msg)
|
||||
}
|
||||
|
||||
getId(): number {
|
||||
return binding.dcn_msg_get_id(this.dc_msg)
|
||||
}
|
||||
|
||||
getQuotedText(): string {
|
||||
return binding.dcn_msg_get_quoted_text(this.dc_msg)
|
||||
}
|
||||
|
||||
getQuotedMessage(): Message | null {
|
||||
const dc_msg = binding.dcn_msg_get_quoted_msg(this.dc_msg)
|
||||
return dc_msg ? new Message(dc_msg) : null
|
||||
}
|
||||
|
||||
getReceivedTimestamp(): number {
|
||||
return binding.dcn_msg_get_received_timestamp(this.dc_msg)
|
||||
}
|
||||
|
||||
getSetupcodebegin() {
|
||||
return binding.dcn_msg_get_setupcodebegin(this.dc_msg)
|
||||
}
|
||||
|
||||
getShowpadlock() {
|
||||
return Boolean(binding.dcn_msg_get_showpadlock(this.dc_msg))
|
||||
}
|
||||
|
||||
getSortTimestamp(): number {
|
||||
return binding.dcn_msg_get_sort_timestamp(this.dc_msg)
|
||||
}
|
||||
|
||||
getState() {
|
||||
return new MessageState(binding.dcn_msg_get_state(this.dc_msg))
|
||||
}
|
||||
|
||||
getSummary(chat?: Chat) {
|
||||
const dc_chat = (chat && chat.dc_chat) || null
|
||||
return new Lot(binding.dcn_msg_get_summary(this.dc_msg, dc_chat))
|
||||
}
|
||||
|
||||
get subject(): string {
|
||||
return binding.dcn_msg_get_subject(this.dc_msg)
|
||||
}
|
||||
|
||||
getSummarytext(approxCharacters: number): string {
|
||||
approxCharacters = approxCharacters || 0
|
||||
return binding.dcn_msg_get_summarytext(this.dc_msg, approxCharacters)
|
||||
}
|
||||
|
||||
getText(): string {
|
||||
return binding.dcn_msg_get_text(this.dc_msg)
|
||||
}
|
||||
|
||||
getTimestamp(): number {
|
||||
return binding.dcn_msg_get_timestamp(this.dc_msg)
|
||||
}
|
||||
|
||||
getViewType() {
|
||||
return new MessageViewType(binding.dcn_msg_get_viewtype(this.dc_msg))
|
||||
}
|
||||
|
||||
getVideochatType(): number {
|
||||
return binding.dcn_msg_get_videochat_type(this.dc_msg)
|
||||
}
|
||||
|
||||
getVideochatUrl(): string {
|
||||
return binding.dcn_msg_get_videochat_url(this.dc_msg)
|
||||
}
|
||||
|
||||
getWidth(): number {
|
||||
return binding.dcn_msg_get_width(this.dc_msg)
|
||||
}
|
||||
|
||||
get overrideSenderName(): string {
|
||||
return binding.dcn_msg_get_override_sender_name(this.dc_msg)
|
||||
}
|
||||
|
||||
hasDeviatingTimestamp() {
|
||||
return binding.dcn_msg_has_deviating_timestamp(this.dc_msg)
|
||||
}
|
||||
|
||||
hasLocation() {
|
||||
return Boolean(binding.dcn_msg_has_location(this.dc_msg))
|
||||
}
|
||||
|
||||
get hasHTML() {
|
||||
return Boolean(binding.dcn_msg_has_html(this.dc_msg))
|
||||
}
|
||||
|
||||
isDeadDrop() {
|
||||
// TODO: Fix
|
||||
//return this.getChatId() === C.DC_CHAT_ID_DEADDROP
|
||||
return false
|
||||
}
|
||||
|
||||
isForwarded() {
|
||||
return Boolean(binding.dcn_msg_is_forwarded(this.dc_msg))
|
||||
}
|
||||
|
||||
isIncreation() {
|
||||
return Boolean(binding.dcn_msg_is_increation(this.dc_msg))
|
||||
}
|
||||
|
||||
isInfo() {
|
||||
return Boolean(binding.dcn_msg_is_info(this.dc_msg))
|
||||
}
|
||||
|
||||
isSent() {
|
||||
return Boolean(binding.dcn_msg_is_sent(this.dc_msg))
|
||||
}
|
||||
|
||||
isSetupmessage() {
|
||||
return Boolean(binding.dcn_msg_is_setupmessage(this.dc_msg))
|
||||
}
|
||||
|
||||
latefilingMediasize(width: number, height: number, duration: number) {
|
||||
binding.dcn_msg_latefiling_mediasize(this.dc_msg, width, height, duration)
|
||||
}
|
||||
|
||||
setDimension(width: number, height: number) {
|
||||
binding.dcn_msg_set_dimension(this.dc_msg, width, height)
|
||||
return this
|
||||
}
|
||||
|
||||
setDuration(duration: number) {
|
||||
binding.dcn_msg_set_duration(this.dc_msg, duration)
|
||||
return this
|
||||
}
|
||||
|
||||
setFile(file: string, mime?: string) {
|
||||
if (typeof file !== 'string') throw new Error('Missing filename')
|
||||
binding.dcn_msg_set_file(this.dc_msg, file, mime || '')
|
||||
return this
|
||||
}
|
||||
|
||||
setLocation(longitude: number, latitude: number) {
|
||||
binding.dcn_msg_set_location(this.dc_msg, longitude, latitude)
|
||||
return this
|
||||
}
|
||||
|
||||
setQuote(quotedMessage: Message | null) {
|
||||
binding.dcn_msg_set_quote(this.dc_msg, quotedMessage?.dc_msg)
|
||||
return this
|
||||
}
|
||||
|
||||
setText(text: string) {
|
||||
binding.dcn_msg_set_text(this.dc_msg, text)
|
||||
return this
|
||||
}
|
||||
|
||||
setHTML(html: string) {
|
||||
binding.dcn_msg_set_html(this.dc_msg, html)
|
||||
return this
|
||||
}
|
||||
|
||||
setOverrideSenderName(senderName: string) {
|
||||
binding.dcn_msg_set_override_sender_name(this.dc_msg, senderName)
|
||||
return this
|
||||
}
|
||||
|
||||
/** Force the message to be sent in plain text.
|
||||
*
|
||||
* This API is for bots, there is no need to expose it in the UI.
|
||||
*/
|
||||
forcePlaintext() {
|
||||
binding.dcn_msg_force_plaintext(this.dc_msg)
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { C } from './constants'
|
||||
|
||||
export type ChatTypes =
|
||||
| C.DC_CHAT_TYPE_GROUP
|
||||
| C.DC_CHAT_TYPE_MAILINGLIST
|
||||
| C.DC_CHAT_TYPE_SINGLE
|
||||
| C.DC_CHAT_TYPE_UNDEFINED
|
||||
|
||||
export interface ChatJSON {
|
||||
archived: boolean
|
||||
pinned: boolean
|
||||
color: string
|
||||
id: number
|
||||
name: string
|
||||
profileImage: string
|
||||
type: number
|
||||
isSelfTalk: boolean
|
||||
isUnpromoted: boolean
|
||||
isProtected: boolean
|
||||
canSend: boolean
|
||||
isDeviceTalk: boolean
|
||||
isContactRequest: boolean
|
||||
muted: boolean
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* @param integerColor expects a 24bit rgb integer (left to right: 8bits red, 8bits green, 8bits blue)
|
||||
*/
|
||||
export function integerToHexColor(integerColor: number) {
|
||||
return '#' + (integerColor + 16777216).toString(16).substring(1)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
diff --git i/node/binding.gyp w/node/binding.gyp
|
||||
index b0d92eae..c5e504fa 100644
|
||||
--- i/node/binding.gyp
|
||||
+++ w/node/binding.gyp
|
||||
@@ -43,7 +43,7 @@
|
||||
"include_dirs": ["../deltachat-ffi"],
|
||||
"ldflags": ["-Wl,-Bsymbolic"], # Prevent sqlite3 from electron from overriding sqlcipher
|
||||
"libraries": [
|
||||
- "../../target/release/libdeltachat.a",
|
||||
+ "../../target/x86_64-apple-darwin/release/libdeltachat.a",
|
||||
"-ldl",
|
||||
],
|
||||
"conditions": [],
|
||||
@@ -1,26 +0,0 @@
|
||||
const spawnSync = require('child_process').spawnSync
|
||||
|
||||
const verbose = isVerbose()
|
||||
|
||||
function spawn (cmd, args, opts) {
|
||||
log(`>> spawn: ${cmd} ${args.join(' ')}`)
|
||||
const result = spawnSync(cmd, args, opts)
|
||||
if (result.status === null) {
|
||||
console.error(`Could not find ${cmd}`)
|
||||
process.exit(1)
|
||||
} else if (result.status !== 0) {
|
||||
console.error(`${cmd} failed with code ${result.status}`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
function log (...args) {
|
||||
if (verbose) console.log(...args)
|
||||
}
|
||||
|
||||
function isVerbose () {
|
||||
const loglevel = process.env.npm_config_loglevel
|
||||
return loglevel === 'verbose' || process.env.CI === 'true'
|
||||
}
|
||||
|
||||
module.exports = { spawn, log, isVerbose, verbose }
|
||||
@@ -1,73 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const split = require('split2')
|
||||
|
||||
const data = []
|
||||
const regex = /^#define\s+(\w+)\s+(\w+)/i
|
||||
const header = path.resolve(
|
||||
__dirname,
|
||||
'../../deltachat-ffi/deltachat.h'
|
||||
)
|
||||
|
||||
console.log('Generating constants...')
|
||||
|
||||
fs.createReadStream(header)
|
||||
.pipe(split())
|
||||
.on('data', (line) => {
|
||||
const match = regex.exec(line)
|
||||
if (match) {
|
||||
const key = match[1]
|
||||
const value = parseInt(match[2])
|
||||
if (isNaN(value)) return
|
||||
|
||||
data.push({ key, value })
|
||||
}
|
||||
})
|
||||
.on('end', () => {
|
||||
const constants = data
|
||||
.filter(
|
||||
({ key }) => key.toUpperCase()[0] === key[0] // check if define name is uppercase
|
||||
)
|
||||
.sort((lhs, rhs) => {
|
||||
if (lhs.key < rhs.key) return -1
|
||||
else if (lhs.key > rhs.key) return 1
|
||||
return 0
|
||||
})
|
||||
.map((row) => {
|
||||
return ` ${row.key}: ${row.value}`
|
||||
})
|
||||
.join(',\n')
|
||||
|
||||
const events = data
|
||||
.sort((lhs, rhs) => {
|
||||
if (lhs.value < rhs.value) return -1
|
||||
else if (lhs.value > rhs.value) return 1
|
||||
return 0
|
||||
})
|
||||
.filter((i) => {
|
||||
return i.key.startsWith('DC_EVENT_')
|
||||
})
|
||||
.map((i) => {
|
||||
return ` ${i.value}: '${i.key}'`
|
||||
})
|
||||
.join(',\n')
|
||||
|
||||
// backwards compat
|
||||
fs.writeFileSync(
|
||||
path.resolve(__dirname, '../constants.js'),
|
||||
`// Generated!\n\nmodule.exports = {\n${constants}\n}\n`
|
||||
)
|
||||
// backwards compat
|
||||
fs.writeFileSync(
|
||||
path.resolve(__dirname, '../events.js'),
|
||||
`/* eslint-disable quotes */\n// Generated!\n\nmodule.exports = {\n${events}\n}\n`
|
||||
)
|
||||
|
||||
fs.writeFileSync(
|
||||
path.resolve(__dirname, '../lib/constants.ts'),
|
||||
`// Generated!\n\nexport enum C {\n${constants.replace(/:/g, ' =')},\n}\n
|
||||
// Generated!\n\nexport const EventId2EventName: { [key: number]: string } = {\n${events},\n}\n`
|
||||
)
|
||||
})
|
||||