Compare commits

..

20 Commits

Author SHA1 Message Date
holger krekel
4ef3a091fe parallelize tox-runs somewhat 2019-09-19 12:11:45 +02:00
holger krekel
977e3e08d7 refine python test run further 2019-09-19 11:37:26 +02:00
holger krekel
bb23e1487d try optimize python run a little 2019-09-19 11:24:21 +02:00
holger krekel
30783adef2 shuffle files such that ci_scripts/ contains all the ci scripts 2019-09-19 11:17:13 +02:00
holger krekel
a2c585c7a5 think these two env var settings are not needed 2019-09-19 10:51:09 +02:00
holger krekel
ff331061a0 remove unused script 2019-09-19 10:30:34 +02:00
holger krekel
77cb0276a6 try let upload of docs and python wheels outside docker and cache semantics 2019-09-19 09:46:47 +02:00
holger krekel
2747939b52 pyenv only exist in machine-runs -- let's see if upload passes without it or if there is a python path missing (the docker image contains several python versions) 2019-09-19 09:39:42 +02:00
dignifiedquire
dfb2ebb533 skip release and ffi runs, they are check using python bindings 2019-09-19 09:39:42 +02:00
dignifiedquire
4c579e6cf6 remove .git from cache dir, no need 2019-09-19 09:39:42 +02:00
dignifiedquire
cc1d520580 persist .git 2019-09-19 09:39:42 +02:00
dignifiedquire
c7686e0a97 fixup 2019-09-19 09:39:42 +02:00
dignifiedquire
d41bcccd41 add more details 2019-09-19 09:39:42 +02:00
dignifiedquire
3c1a4ebfe0 use correct script 2019-09-19 09:39:42 +02:00
dignifiedquire
361f14bffe ... 2019-09-19 09:39:42 +02:00
dignifiedquire
939ca7f7d3 .. 2019-09-19 09:39:42 +02:00
dignifiedquire
cdacf6a40f try fix config 2019-09-19 09:39:42 +02:00
dignifiedquire
feb4dfc3af try sth 2019-09-19 09:39:42 +02:00
dignifiedquire
8c13771d6c fix executor 2019-09-19 09:39:42 +02:00
dignifiedquire
5c3e1a6593 improve python caching 2019-09-19 09:39:42 +02:00
130 changed files with 12692 additions and 37926 deletions

View File

@@ -4,9 +4,6 @@ executors:
docker:
- image: filecoin/rust:latest
working_directory: /mnt/crate
doxygen:
docker:
- image: hrektts/doxygen
restore-workspace: &restore-workspace
attach_workspace:
@@ -15,7 +12,7 @@ restore-workspace: &restore-workspace
restore-cache: &restore-cache
restore_cache:
keys:
- cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- cargo-v2-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- repo-source-{{ .Branch }}-{{ .Revision }}
commands:
@@ -44,7 +41,7 @@ jobs:
command: cargo generate-lockfile
- restore_cache:
keys:
- cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- cargo-v2-{{ 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
@@ -60,7 +57,7 @@ jobs:
paths:
- crate
- save_cache:
key: cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
key: cargo-v2-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
paths:
- "~/.cargo"
- "~/.rustup"
@@ -116,59 +113,32 @@ jobs:
target: "aarch64-linux-android"
build_doxygen:
executor: doxygen
build_test_docs_wheel:
docker:
- image: deltachat/coredeps
environment:
TESTS: 1
DOCS: 1
working_directory: /mnt/crate
steps:
- checkout
- run: bash ci_scripts/run-doxygen.sh
- run: mkdir -p workspace/c-docs
- run: cp -av deltachat-ffi/{html,xml} workspace/c-docs/
- *restore-workspace
- *restore-cache
- run:
name: build docs, run tests and build wheels
command: ci_scripts/run-python.sh
- run:
name: copying docs and wheels to workspace
command: |
mkdir -p workspace/python
# cp -av docs workspace/c-docs
cp -av python/.docker-tox/wheelhouse workspace/
cp -av python/doc/_build/ workspace/py-docs
- persist_to_workspace:
root: workspace
paths:
- c-docs
# build_test_docs_wheel:
# docker:
# - image: deltachat/coredeps
# environment:
# TESTS: 1
# DOCS: 1
# working_directory: /mnt/crate
# steps:
# - *restore-workspace
# - *restore-cache
# - run:
# name: build docs, run tests and build wheels
# command: ci_scripts/run-python.sh
# - run:
# name: copying docs and wheels to workspace
# command: |
# mkdir -p workspace/python
# # cp -av docs workspace/c-docs
# cp -av python/.docker-tox/wheelhouse workspace/
# cp -av python/doc/_build/ workspace/py-docs
# - persist_to_workspace:
# root: workspace
# paths:
# # - c-docs
# - py-docs
# - wheelhouse
remote_tests_rust:
machine: true
steps:
- checkout
- run: ci_scripts/remote_tests_rust.sh
remote_tests_python:
machine: true
steps:
- checkout
#- attach_workspace:
# at: workspace
- run: ci_scripts/remote_tests_python.sh
# workspace/py-docs workspace/wheelhouse workspace/c-docs
# - c-docs
- py-docs
- wheelhouse
upload_docs_wheels:
machine: true
@@ -178,7 +148,7 @@ jobs:
at: workspace
- run: pyenv global 3.5.2
- run: ls -laR workspace
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse workspace/c-docs
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse
clippy:
executor: default
@@ -196,28 +166,23 @@ workflows:
test:
jobs:
- cargo_fetch
- remote_tests_rust
- remote_tests_python
# - upload_docs_wheels:
# requires:
# - build_test_docs_wheel
# - build_doxygen
- build_test_docs_wheel:
requires:
- cargo_fetch
- upload_docs_wheels:
requires:
- build_test_docs_wheel
- rustfmt:
requires:
- cargo_fetch
- clippy:
requires:
- cargo_fetch
- build_doxygen
- cargo_fetch
# Linux Desktop 64bit
# - test_x86_64-unknown-linux-gnu:
# requires:
# - cargo_fetch
- test_x86_64-unknown-linux-gnu:
requires:
- cargo_fetch
# Linux Desktop 32bit
# - test_i686-unknown-linux-gnu:

1
.gitignore vendored
View File

@@ -16,7 +16,6 @@ python/.tox
*.egg-info
__pycache__
python/src/deltachat/capi*.so
python/.venv/
python/liveconfig*

View File

@@ -1,120 +0,0 @@
# Changelog
## 1.0.0-beta.8
- now uses async-email/async-imap as the new base
which makes imap-idle interruptible and thus fixes
several issues around the imap thread being in zombie state .
thanks @dignifiedquire, @hpk42 and @link2xt.
- fixes imap-protocol parsing bugs that lead to infinitely
repeated crashing while trying to receive messages with
a subjec that contained non-utf8. thanks @link2xt
- fixed logic to find encryption subkey -- previously
delta chat would use the primary key for encryption
(which works with RSA but not ECC). thanks @link2xt
- introduce a new device chat where core and UIs can
add "device" messages. Android uses it for an initial
welcome message. thanks @r10s
- fix time smearing (when two message are virtually send
in the same second, there would be misbehaviour because
we didn't persist smeared time). thanks @r10s
- fix double-dotted extensions like .html.zip or .tar.gz
to not mangle them when creating blobfiles. thanks @flub
- fix backup/exports where the wrong sql file would be modified,
leading to problems when exporting twice. thanks @hpk42
- several other little fixes and improvements
## 1.0.0-beta.7
- fix location-streaming #782
- fix display of messages that could not be decrypted #785
- fix smtp MAILER-DAEMON bug #786
- fix a logging of durations #783
- add more error logging #779
- do not panic on some bad utf-8 mime #776
## 1.0.0-beta.6
- fix chatlist.get_msg_id to return id, instead of wrongly erroring
## 1.0.0-beta.5
- fix dc_get_msg() to return empty messages when asked for special ones
## 1.0.0-beta.4
- fix more than one sending of autocrypt setup message
- fix recognition of mailto-address-qr-codes, add tests
- tune down error to warning when adding self to chat
## 1.0.0-beta.3
- add back `dc_empty_server()` #682
- if `show_emails` is set to `DC_SHOW_EMAILS_ALL`,
email-based contact requests are added to the chatlist directly
- fix IMAP hangs #717 and cleanups
- several rPGP fixes
- code streamlining and rustifications
## 1.0.0-beta.2
- https://c.delta.chat docs are now regenerated again through our CI
- several rPGP cleanups, security fixes and better multi-platform support
- reconnect on io errors and broken pipes (imap)
- probe SMTP with real connection not just setup
- various imap/smtp related fixes
- use to_string_lossy in most places instead of relying on valid utf-8
encodings
- rework, rustify and test autoconfig-reading and parsing
- some rustifications/boolifications of c-ints
## 1.0.0-beta.1
- first beta of the Delta Chat Rust core library. many fixes of crashes
and other issues compared to 1.0.0-alpha.5.
- Most code is now "rustified" and does not do manual memory allocation anymore.
- The `DC_EVENT_GET_STRING` event is not used anymore, removing the last
event where the core requested a return value from the event callback.
Please now use `dc_set_stock_translation()` API for core messages
to be properly localized.
- Deltachat FFI docs are automatically generated and available here:
https://c.delta.chat
- New events ImapMessageMoved and ImapMessageDeleted
For a full list of changes, please see our closed Pull Requests:
https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed

1626
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,26 @@
[package]
name = "deltachat"
version = "1.0.0-beta.8"
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
version = "1.0.0-alpha.4"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
edition = "2018"
license = "MPL"
[dependencies]
deltachat_derive = { path = "./deltachat_derive" }
mmime = { version = "0.1.2", path = "./mmime" }
libc = "0.2.51"
pgp = { git = "https://github.com/rpgp/rpgp", branch = "master", default-features = false }
hex = "0.4.0"
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 = { version = "0.9.15", default-features = false, features = ["rustls-tls"] }
reqwest = "0.9.15"
num-derive = "0.2.5"
num-traits = "0.2.6"
lettre = { git = "https://github.com/deltachat/lettre", branch = "feat/rustls" }
async-imap = { git = "https://github.com/async-email/async-imap", branch="master" }
async-tls = "0.6"
async-std = { version = "1.0", features = ["unstable"] }
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"
@@ -37,8 +36,8 @@ regex = "1.1.6"
rusqlite = { version = "0.20", features = ["bundled"] }
r2d2_sqlite = "0.12.0"
r2d2 = "0.8.5"
strum = "0.16.0"
strum_macros = "0.16.0"
strum = "0.15.0"
strum_macros = "0.15.0"
thread-local-object = "0.1.0"
backtrace = "0.3.33"
byteorder = "1.3.1"
@@ -49,11 +48,6 @@ escaper = "0.1.0"
bitflags = "1.1.0"
jetscii = "0.4.4"
debug_stub_derive = "0.3.0"
sanitize-filename = "0.2.1"
stop-token = { version = "0.1.1", features = ["unstable"] }
rustls = "0.16.0"
webpki-roots = "0.18.0"
webpki = "0.21.0"
[dev-dependencies]
tempfile = "3.0"
@@ -65,7 +59,6 @@ proptest = "0.9.4"
members = [
"deltachat-ffi",
"deltachat_derive",
"mmime",
]
[[example]]
@@ -79,6 +72,6 @@ path = "examples/repl/main.rs"
[features]
default = ["nightly", "ringbuf"]
vendored = []
vendored = ["native-tls/vendored", "reqwest/default-tls-vendored"]
nightly = ["pgp/nightly"]
ringbuf = ["pgp/ringbuf"]

View File

@@ -1,9 +1,11 @@
# Delta Chat Rust
> Deltachat-core written in Rust
> Project porting deltachat-core to rust
[![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:
@@ -14,7 +16,7 @@ curl https://sh.rustup.rs -sSf | sh
## Using the CLI client
Compile and run Delta Chat Core command line utility, using `cargo`:
Compile and run Delta Chat Core using `cargo`:
```
cargo run --example repl -- /path/to/db

6
Xargo.toml Normal file
View File

@@ -0,0 +1,6 @@
[dependencies.std]
features = ["panic-unwind"]
# if using `cargo test`
[dependencies.test]
stage = 1

View File

@@ -1,5 +1,4 @@
environment:
RUST_BACKTRACE: full
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
@@ -14,7 +13,7 @@ install:
build: false
test_script:
- cargo test --release --all
- cargo test --release
cache:
- target

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,110 +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"
inkscape:version="1.0beta1 (32d4812, 2019-09-19)"
sodipodi:docname="icon-device.svg"
id="svg4344"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 45 45"
height="60"
width="60"
version="1.0"
inkscape:export-filename="/Users/bpetersen/Documents/deltachat/icons/device4.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs4348">
<linearGradient
inkscape:collect="always"
id="linearGradient865">
<stop
style="stop-color:#364e59;stop-opacity:1;"
offset="0"
id="stop861" />
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="1"
id="stop863" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient865"
id="linearGradient867"
x1="10.227795"
y1="43.5238"
x2="-25.458139"
y2="-7.4257693"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
inkscape:current-layer="svg4344"
inkscape:window-maximized="0"
inkscape:window-y="23"
inkscape:window-x="45"
inkscape:cy="28.471578"
inkscape:cx="41.455164"
inkscape:zoom="5.6"
units="px"
showgrid="false"
id="namedview4346"
inkscape:window-height="1035"
inkscape:window-width="1600"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10"
gridtolerance="10"
objecttolerance="10"
borderopacity="1"
inkscape:document-rotation="0"
bordercolor="#666666"
pagecolor="#ffffff" />
<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>
<circle
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke-width:0.731071"
id="path859"
cx="22.5"
cy="22.5"
r="22.5" />
<circle
r="22.5"
cy="22.5"
cx="22.5"
id="path4917"
style="fill:url(#linearGradient867);fill-opacity:1;stroke-width:0.588554" />
<g
id="g4342"
stroke="none"
fill="#000000"
transform="matrix(0.00255113,0,0,-0.00255113,5.586152,38.200477)"
style="fill:#ffffff;fill-opacity:1">
<path
inkscape:connector-curvature="0"
id="path4338"
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"
style="fill:#ffffff;fill-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4340"
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"
style="fill:#ffffff;fill-opacity:1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,99 +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"
inkscape:export-ydpi="204.8"
inkscape:export-xdpi="204.8"
inkscape:export-filename="/Users/bpetersen/Documents/deltachat/icons/saved-messages.png"
version="1.0"
width="60"
height="60"
viewBox="0 0 45 45"
preserveAspectRatio="xMidYMid meet"
id="svg4344"
sodipodi:docname="saved-messages.svg"
inkscape:version="1.0beta1 (32d4812, 2019-09-19)">
<defs
id="defs4348">
<linearGradient
inkscape:collect="always"
id="linearGradient906">
<stop
style="stop-color:#046cdb;stop-opacity:1"
offset="0"
id="stop902" />
<stop
id="stop845"
offset="1"
style="stop-color:#ffffff;stop-opacity:1" />
</linearGradient>
<linearGradient
y2="-11.049108"
x2="-3.1473193"
y1="31.473215"
x1="36.160713"
gradientUnits="userSpaceOnUse"
id="linearGradient847"
xlink:href="#linearGradient906"
inkscape:collect="always" />
</defs>
<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="1600"
inkscape:window-height="1035"
id="namedview4346"
showgrid="false"
units="px"
inkscape:zoom="2.8"
inkscape:cx="48.214285"
inkscape:cy="55"
inkscape:window-x="45"
inkscape:window-y="23"
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>
<circle
r="22.5"
cy="22.5"
cx="22.5"
id="path859"
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke-width:0.731071" />
<circle
style="fill:url(#linearGradient847);fill-opacity:1;stroke-width:0.588554"
id="path4917"
cx="22.5"
cy="22.5"
r="22.5" />
<path
id="rect846"
style="fill:#ffffff;stroke-width:0.84832"
d="M 17 9 L 17 55 L 17.125 55 L 30.048828 40.9375 L 42.974609 55 L 43 55 L 43 9 L 17 9 z M 30.005859 15 L 32.720703 22.121094 L 40 22.646484 L 34.396484 27.558594 L 36.169922 35 L 29.992188 30.916016 L 23.808594 34.988281 L 25.595703 27.550781 L 20 22.626953 L 27.28125 22.115234 L 30.005859 15 z "
transform="scale(0.75)" />
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,46 +1,52 @@
# Continuous Integration Scripts for Delta Chat
Continuous Integration, run through CircleCI and an own build machine.
## Description of scripts
- `../.circleci/config.yml` describing the build jobs that are run
by Circle-CI
- `remote_tests_python.sh` rsyncs to a build machine and runs
`run-python-test.sh` remotely on the build machine.
- `remote_tests_rust.sh` rsyncs to the build machine and runs
`run-rust-test.sh` remotely on the build machine.
- `doxygen/Dockerfile` specifies an image that contains
the doxygen tool which is used by `run-doxygen.sh`
to generate C-docs which are then uploaded
via `ci_upload.sh` to `https://c.delta.chat/_unofficial_unreleased_docs/<BRANCH>`
(and the master branch is linked to https://c.delta.chat proper).
Continuous Integration is run through CircleCI
but is largely independent of it.
## Triggering runs on the build machine locally (fast!)
## Generating docker containers for performing build step work
There is experimental support for triggering a remote Python or Rust test run
from your local checkout/branch. You will need to be authorized to login to
the build machine (ask your friendly sysadmin on #deltachat freenode) to type::
All tests, docs and wheel building is run in docker containers:
ci_scripts/manual_remote_tests.sh rust
ci_scripts/manual_remote_tests.sh python
- **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).
This will **rsync** your current checkout to the remote build machine
(no need to commit before) and then run either rust or python tests.
- **doxygen/Dockerfile** specifies an image that contains
the doxygen tool which is used to generate C-docs.
# Outdated files (for later re-use)
To run tests locally you can pull existing images from "docker.io",
the hub for sharing Docker images::
`coredeps/Dockerfile` specifies an image that contains all
of Delta Chat's core dependencies. It used to run
python tests and build wheels (binary packages for Python)
docker pull deltachat/coredeps
docker pull deltachat/doxygen
You can build the docker images yourself locally
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`

View File

@@ -7,30 +7,25 @@ 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}
DOXYDOCDIR=${3:?directory where doxygen docs to be found}
export BRANCH=${CIRCLE_BRANCH:?specify branch for uploading purposes}
# DISABLED: python docs to py.delta.chat
#ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@py.delta.chat mkdir -p build/${BRANCH}
#rsync -avz \
# -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
# "$PYDOCDIR/html/" \
# delta@py.delta.chat:build/${BRANCH}
# C docs to c.delta.chat
ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@c.delta.chat mkdir -p build-c/${BRANCH}
# 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" \
"$DOXYDOCDIR/html/" \
delta@c.delta.chat:build-c/${BRANCH}
"$PYDOCDIR/html/" \
delta@py.delta.chat:build/${BRANCH}
exit 0
# OUTDATED -- for re-use from python release-scripts
# 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

View File

@@ -5,6 +5,16 @@ 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
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
# Install python tools (auditwheels,tox, ...)
ADD deps/build_python.sh /builder/build_python.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_python.sh && cd .. && rm -r tmp1
# Install Rust nightly
ADD deps/build_rust.sh /builder/build_rust.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_rust.sh && cd .. && rm -r tmp1
# 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
@@ -13,12 +23,3 @@ RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1
ADD deps/build_openssl.sh /builder/build_openssl.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_openssl.sh && cd .. && rm -r tmp1
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
# Install python tools (auditwheels,tox, ...)
ADD deps/build_python.sh /builder/build_python.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_python.sh && cd .. && rm -r tmp1
# Install Rust nightly
ADD deps/build_rust.sh /builder/build_rust.sh
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_rust.sh && cd .. && rm -r tmp1

View File

@@ -1,11 +1,11 @@
#!/bin/bash
PERL_VERSION=5.30.0
# PERL_SHA256=7e929f64d4cb0e9d1159d4a59fc89394e27fa1f7004d0836ca0d514685406ea8
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}
echo "${PERL_SHA256} perl-${PERL_VERSION}.tar.gz" | sha256sum -c -
tar xzf perl-${PERL_VERSION}.tar.gz
cd perl-${PERL_VERSION}
./Configure -de
make

View File

@@ -1,8 +1,11 @@
#!/bin/bash
set -e -x
set -e -x
# Install Rust
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-09-12 -y
# 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/

View File

@@ -1,9 +0,0 @@
#!/bin/bash
set -xe
export CIRCLE_JOB=remote_tests_${1:?need to specify 'rust' or 'python'}
export CIRCLE_BUILD_NUM=$USER
export CIRCLE_BRANCH=`git branch | grep \* | cut -d ' ' -f2`
export CIRCLE_PROJECT_REPONAME=$(basename `git rev-parse --show-toplevel`)
time bash ci_scripts/$CIRCLE_JOB.sh

View File

@@ -1,77 +0,0 @@
name: CI
on:
pull_request:
push:
env:
RUSTFLAGS: -Dwarnings
jobs:
build_and_test:
name: Build and test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
rust: [nightly]
steps:
- uses: actions/checkout@master
- name: Install ${{ matrix.rust }}
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
override: true
- name: check
uses: actions-rs/cargo@v1
if: matrix.rust == 'nightly'
with:
command: check
args: --all --bins --examples --tests
- name: tests
uses: actions-rs/cargo@v1
with:
command: test
args: --all
- name: tests ignored
uses: actions-rs/cargo@v1
with:
command: test
args: --all --release -- --ignored
check_fmt:
name: Checking fmt and docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
components: rustfmt
- name: fmt
run: cargo fmt --all -- --check
# clippy_check:
# name: Clippy check
# runs-on: ubuntu-latest
#
# steps:
# - uses: actions/checkout@v1
# - uses: actions-rs/toolchain@v1
# with:
# profile: minimal
# toolchain: nightly
# override: true
# components: clippy
#
# - name: clippy
# run: cargo clippy --all

View File

@@ -1,46 +0,0 @@
#!/bin/bash
export BRANCH=${CIRCLE_BRANCH:?branch to build}
export REPONAME=${CIRCLE_PROJECT_REPONAME:?repository name}
export SSHTARGET=ci@b1.delta.chat
# we construct the BUILDDIR such that we can easily share the
# CARGO_TARGET_DIR between runs ("..")
export BUILDDIR=ci_builds/$REPONAME/$BRANCH/${CIRCLE_JOB:?jobname}/${CIRCLE_BUILD_NUM:?circle-build-number}
echo "--- Copying files to $SSHTARGET:$BUILDDIR"
set -xe
ssh -oBatchMode=yes -oStrictHostKeyChecking=no $SSHTARGET mkdir -p "$BUILDDIR"
git ls-files >.rsynclist
# we seem to need .git for setuptools_scm versioning
find .git >>.rsynclist
rsync --delete --files-from=.rsynclist -az ./ "$SSHTARGET:$BUILDDIR"
set +x
echo "--- Running $CIRCLE_JOB remotely"
ssh $SSHTARGET <<_HERE
set +x -e
cd $BUILDDIR
# let's share the target dir with our last run on this branch/job-type
# cargo will make sure to block/unblock us properly
export CARGO_TARGET_DIR=\`pwd\`/../target
export TARGET=release
export DCC_PY_LIVECONFIG=$DCC_PY_LIVECONFIG
#we rely on tox/virtualenv being available in the host
#rm -rf virtualenv venv
#virtualenv -q -p python3.7 venv
#source venv/bin/activate
#pip install -q tox virtualenv
set -x
which python
source \$HOME/venv/bin/activate
which python
bash ci_scripts/run-python-test.sh
_HERE

View File

@@ -1,32 +0,0 @@
#!/bin/bash
export BRANCH=${CIRCLE_BRANCH:?branch to build}
export REPONAME=${CIRCLE_PROJECT_REPONAME:?repository name}
export SSHTARGET=ci@b1.delta.chat
# we construct the BUILDDIR such that we can easily share the
# CARGO_TARGET_DIR between runs ("..")
export BUILDDIR=ci_builds/$REPONAME/$BRANCH/${CIRCLE_JOB:?jobname}/${CIRCLE_BUILD_NUM:?circle-build-number}
set -e
echo "--- Copying files to $SSHTARGET:$BUILDDIR"
ssh -oBatchMode=yes -oStrictHostKeyChecking=no $SSHTARGET mkdir -p "$BUILDDIR"
git ls-files >.rsynclist
rsync --delete --files-from=.rsynclist -az ./ "$SSHTARGET:$BUILDDIR"
echo "--- Running $CIRCLE_JOB remotely"
ssh $SSHTARGET <<_HERE
set +x -e
cd $BUILDDIR
# let's share the target dir with our last run on this branch/job-type
# cargo will make sure to block/unblock us properly
export CARGO_TARGET_DIR=\`pwd\`/../target
export TARGET=x86_64-unknown-linux-gnu
export RUSTC_WRAPPER=sccache
bash ci_scripts/run-rust-test.sh
_HERE

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -ex
cd deltachat-ffi
doxygen

View File

@@ -1,24 +0,0 @@
#!/bin/bash
#
# Run functional tests for Delta Chat core using the python bindings
# and tox/pytest.
set -e -x
# for core-building and python install step
export DCC_RS_TARGET=release
export DCC_RS_DEV=`pwd`
cd python
python install_python_bindings.py onlybuild
# remove and inhibit writing PYC files
rm -rf tests/__pycache__
rm -rf src/deltachat/__pycache__
export PYTHONDONTWRITEBYTECODE=1
# run python tests (tox invokes pytest to run tests in python/tests)
#TOX_PARALLEL_NO_SPINNER=1 tox -e lint,doc
tox -e lint
tox -e doc,py37

View File

@@ -39,12 +39,8 @@ if [ -n "$TESTS" ]; then
# run tox. The circle-ci project env-var-setting DCC_PY_LIVECONFIG
# allows running of "liveconfig" tests but for speed reasons
# we run them only for the highest python version we support
# we split out qr-tests run to minimize likelyness of flaky tests
# (some qr tests are pretty heavy in terms of send/received
# messages and rust's imap code likely has concurrency problems)
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "not qr"
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "qr"
tox --workdir "$TOXWORKDIR" -e py37
unset DCC_PY_LIVECONFIG
tox --workdir "$TOXWORKDIR" -p4 -e lint,py35,py36,doc
tox --workdir "$TOXWORKDIR" -e auditwheels

View File

@@ -2,7 +2,7 @@
set -ex
#export RUST_TEST_THREADS=1
export RUST_TEST_THREADS=1
export RUST_BACKTRACE=1
export RUSTFLAGS='--deny warnings'
export OPT="--target=$TARGET"
@@ -31,15 +31,12 @@ 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

View File

@@ -1,8 +1,8 @@
[package]
name = "deltachat_ffi"
version = "1.0.0-beta.8"
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 = "MIT OR Apache-2.0"
@@ -16,7 +16,7 @@ crate-type = ["cdylib", "staticlib"]
[dependencies]
deltachat = { path = "../", default-features = false }
deltachat-provider-database = "0.2.1"
deltachat-provider-overview = { git = "https://github.com/deltachat/provider-overview", rev = "366b41a7503973e4ffac3aa5173b419f2f03c211" }
libc = "0.2"
human-panic = "1.0.1"
num-traits = "0.2.6"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,18 @@
extern crate deltachat_provider_database;
extern crate deltachat_provider_overview;
use std::ptr;
use deltachat::dc_tools::{to_string_lossy, StrExt};
use deltachat_provider_database::StatusState;
use deltachat::dc_tools::{as_str, StrExt};
use deltachat_provider_overview::StatusState;
#[no_mangle]
pub type dc_provider_t = deltachat_provider_database::Provider;
pub type dc_provider_t = deltachat_provider_overview::Provider;
#[no_mangle]
pub unsafe extern "C" fn dc_provider_new_from_domain(
domain: *const libc::c_char,
) -> *const dc_provider_t {
match deltachat_provider_database::get_provider_info(&to_string_lossy(domain)) {
match deltachat_provider_overview::get_provider_info(as_str(domain)) {
Some(provider) => provider,
None => ptr::null(),
}
@@ -22,9 +22,8 @@ pub unsafe extern "C" fn dc_provider_new_from_domain(
pub unsafe extern "C" fn dc_provider_new_from_email(
email: *const libc::c_char,
) -> *const dc_provider_t {
let email = to_string_lossy(email);
let domain = deltachat_provider_database::get_domain_from_email(&email);
match deltachat_provider_database::get_provider_info(domain) {
let domain = deltachat_provider_overview::get_domain_from_email(as_str(email));
match deltachat_provider_overview::get_provider_info(domain) {
Some(provider) => provider,
None => ptr::null(),
}
@@ -45,7 +44,7 @@ pub unsafe extern "C" fn dc_provider_get_overview_page(
null_guard!(provider);
format!(
"{}/{}",
deltachat_provider_database::PROVIDER_OVERVIEW_URL,
deltachat_provider_overview::PROVIDER_OVERVIEW_URL,
(*provider).overview_page
)
.strdup()

View File

@@ -1,25 +1,27 @@
use std::path::Path;
use std::ffi::CString;
use std::ptr;
use std::str::FromStr;
use deltachat::chat::{self, Chat};
use deltachat::chatlist::*;
use deltachat::config;
use deltachat::configure::*;
use deltachat::constants::*;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::dc_imex::*;
use deltachat::dc_receive_imf::*;
use deltachat::dc_tools::*;
use deltachat::error::Error;
use deltachat::imex::*;
use deltachat::job::*;
use deltachat::location;
use deltachat::lot::LotState;
use deltachat::message::{self, Message, MessageState, MsgId};
use deltachat::message::*;
use deltachat::peerstate::*;
use deltachat::qr::*;
use deltachat::sql;
use deltachat::x::*;
use deltachat::Event;
use libc::free;
/// Reset database tables. This function is called from Core cmdline.
/// Argument is a bitmask, executing single or multiple actions in one call.
@@ -86,150 +88,189 @@ pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
context.call_cb(Event::MsgsChanged {
chat_id: 0,
msg_id: MsgId::new(0),
msg_id: 0,
});
1
}
fn dc_poke_eml_file(context: &Context, filename: impl AsRef<Path>) -> Result<(), Error> {
let data = dc_read_file(context, filename)?;
unsafe fn dc_poke_eml_file(context: &Context, filename: *const libc::c_char) -> libc::c_int {
/* mainly for testing, may be called by dc_import_spec() */
let mut success: libc::c_int = 0i32;
let mut data: *mut libc::c_char = ptr::null_mut();
let mut data_bytes = 0;
if !(dc_read_file(
context,
filename,
&mut data as *mut *mut libc::c_char as *mut *mut libc::c_void,
&mut data_bytes,
) == 0i32)
{
dc_receive_imf(context, data, data_bytes, "import", 0, 0);
success = 1;
}
free(data as *mut libc::c_void);
unsafe { dc_receive_imf(context, &data, "import", 0, 0) };
Ok(())
success
}
/// Import a file to the database.
/// For testing, import a folder with eml-files, a single eml-file, e-mail plus public key and so on.
/// For normal importing, use imex().
/// For normal importing, use dc_imex().
///
/// @private @memberof Context
/// @param context The context as created by dc_context_new().
/// @param spec The file or directory to import. NULL for the last command.
/// @return 1=success, 0=error.
fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
if !context.sql.is_open() {
error!(context, "Import: Database not opened.");
return 0;
}
let real_spec: String;
let mut read_cnt = 0;
let ok_to_continue;
let mut success: libc::c_int = 0;
let real_spec: *mut libc::c_char;
let mut suffix: *mut libc::c_char = ptr::null_mut();
let mut read_cnt: libc::c_int = 0;
/* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
if !spec.is_null() {
real_spec = to_string_lossy(spec);
real_spec = dc_strdup(spec);
context
.sql
.set_raw_config(context, "import_spec", Some(&real_spec))
.set_config(context, "import_spec", Some(as_str(real_spec)))
.unwrap();
ok_to_continue = true;
} else {
let rs = context.sql.get_raw_config(context, "import_spec");
let rs = context.sql.get_config(context, "import_spec");
if rs.is_none() {
error!(context, "Import: No file or folder given.");
return 0;
}
real_spec = rs.unwrap();
}
if let Some(suffix) = dc_get_filesuffix_lc(&real_spec) {
if suffix == "eml" && dc_poke_eml_file(context, &real_spec).is_ok() {
read_cnt += 1
}
} else {
/* import a directory */
let dir_name = std::path::Path::new(&real_spec);
let dir = std::fs::read_dir(dir_name);
if dir.is_err() {
error!(context, "Import: Cannot open directory \"{}\".", &real_spec,);
return 0;
ok_to_continue = false;
} else {
let dir = dir.unwrap();
for entry in dir {
if entry.is_err() {
break;
}
let entry = entry.unwrap();
let name_f = entry.file_name();
let name = name_f.to_string_lossy();
if name.ends_with(".eml") {
let path_plus_name = format!("{}/{}", &real_spec, name);
info!(context, "Import: {}", path_plus_name);
if dc_poke_eml_file(context, path_plus_name).is_ok() {
read_cnt += 1
ok_to_continue = true;
}
real_spec = rs.unwrap_or_default().strdup();
}
if ok_to_continue {
let ok_to_continue2;
suffix = dc_get_filesuffix_lc(as_str(real_spec));
if !suffix.is_null() && strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0
{
if 0 != dc_poke_eml_file(context, real_spec) {
read_cnt += 1
}
ok_to_continue2 = true;
} else {
/* import a directory */
let dir_name = std::path::Path::new(as_str(real_spec));
let dir = std::fs::read_dir(dir_name);
if dir.is_err() {
error!(
context,
"Import: Cannot open directory \"{}\".",
as_str(real_spec),
);
ok_to_continue2 = false;
} else {
let dir = dir.unwrap();
for entry in dir {
if entry.is_err() {
break;
}
let entry = entry.unwrap();
let name_f = entry.file_name();
let name = name_f.to_string_lossy();
if name.ends_with(".eml") {
let path_plus_name = format!("{}/{}", as_str(real_spec), name);
info!(context, "Import: {}", path_plus_name);
let path_plus_name_c = CString::yolo(path_plus_name);
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
read_cnt += 1
}
}
}
ok_to_continue2 = true;
}
}
if ok_to_continue2 {
info!(
context,
"Import: {} items read from \"{}\".",
read_cnt,
as_str(real_spec)
);
if read_cnt > 0 {
context.call_cb(Event::MsgsChanged {
chat_id: 0,
msg_id: 0,
});
}
success = 1
}
}
info!(
context,
"Import: {} items read from \"{}\".", read_cnt, &real_spec
);
if read_cnt > 0 {
context.call_cb(Event::MsgsChanged {
chat_id: 0,
msg_id: MsgId::new(0),
});
}
1
free(real_spec as *mut libc::c_void);
free(suffix as *mut libc::c_void);
success
}
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
let contact = Contact::get_by_id(context, msg.get_from_id()).expect("invalid contact");
let contact = Contact::get_by_id(context, dc_msg_get_from_id(msg)).expect("invalid contact");
let contact_name = contact.get_name();
let contact_id = contact.get_id();
let statestr = match msg.get_state() {
let statestr = match dc_msg_get_state(msg) {
MessageState::OutPending => " o",
MessageState::OutDelivered => "",
MessageState::OutMdnRcvd => " √√",
MessageState::OutFailed => " !!",
_ => "",
};
let temp2 = dc_timestamp_to_str(msg.get_timestamp());
let msgtext = msg.get_text();
let temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
let msgtext = dc_msg_get_text(msg);
info!(
context,
"{}{}{}{}: {} (Contact#{}): {} {}{}{}{}{} [{}]",
"{}#{}{}{}: {} (Contact#{}): {} {}{}{}{} [{}]",
prefix.as_ref(),
msg.get_id(),
if msg.get_showpadlock() { "🔒" } else { "" },
if msg.has_location() { "📍" } else { "" },
dc_msg_get_id(msg) as libc::c_int,
if dc_msg_get_showpadlock(msg) {
"🔒"
} else {
""
},
if dc_msg_has_location(msg) { "📍" } else { "" },
&contact_name,
contact_id,
msgtext.unwrap_or_default(),
if msg.is_starred() { "" } else { "" },
if msg.get_from_id() == 1 as libc::c_uint {
as_str(msgtext),
if dc_msg_is_starred(msg) { "" } else { "" },
if dc_msg_get_from_id(msg) == 1 as libc::c_uint {
""
} else if msg.get_state() == MessageState::InSeen {
} else if dc_msg_get_state(msg) == MessageState::InSeen {
"[SEEN]"
} else if msg.get_state() == MessageState::InNoticed {
} else if dc_msg_get_state(msg) == MessageState::InNoticed {
"[NOTICED]"
} else {
"[FRESH]"
},
if msg.is_info() { "[INFO]" } else { "" },
if msg.is_forwarded() {
"[FORWARDED]"
} else {
""
},
if dc_msg_is_info(msg) { "[INFO]" } else { "" },
statestr,
&temp2,
);
free(msgtext as *mut libc::c_void);
}
unsafe fn log_msglist(context: &Context, msglist: &Vec<MsgId>) -> Result<(), Error> {
unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error> {
let mut lines_out = 0;
for &msg_id in msglist {
if msg_id.is_daymarker() {
if msg_id == 9 as libc::c_uint {
info!(
context,
"--------------------------------------------------------------------------------"
);
lines_out += 1
} else if !msg_id.is_special() {
} else if msg_id > 0 {
if lines_out == 0 {
info!(
context,
@@ -237,8 +278,8 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<MsgId>) -> Result<(), Err
);
lines_out += 1
}
let msg = Message::load_from_db(context, msg_id)?;
log_msg(context, "", &msg);
let msg = dc_get_msg(context, msg_id)?;
log_msg(context, "Msg", &msg);
}
}
if lines_out > 0 {
@@ -319,8 +360,12 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
arg1.strdup() as *const _
};
let arg2 = args.next().unwrap_or_default();
let arg2_c = if arg2.is_empty() {
std::ptr::null()
} else {
arg2.strdup() as *const _
};
let blobdir = context.get_blobdir();
match arg0 {
"help" | "?" => match arg1 {
// TODO: reuse commands definition in main.rs.
@@ -351,7 +396,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
configure\n\
connect\n\
disconnect\n\
interrupt\n\
maybenetwork\n\
housekeeping\n\
help imex (Import/Export)\n\
@@ -377,7 +421,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
sendimage <file> [<text>]\n\
sendfile <file> [<text>]\n\
draft [<text>]\n\
devicemsg <text>\n\
listmedia\n\
archive <chat-id>\n\
unarchive <chat-id>\n\
@@ -404,32 +447,37 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
checkqr <qr-content>\n\
event <event-id to test>\n\
fileinfo <file>\n\
emptyserver <flags> (1=MVBOX 2=INBOX)\n\
clear -- clear screen\n\
exit or quit\n\
============================================="
),
},
"initiate-key-transfer" => match initiate_key_transfer(context) {
Ok(setup_code) => println!(
"Setup code for the transferred setup message: {}",
setup_code,
),
Err(err) => bail!("Failed to generate setup code: {}", err),
},
"initiate-key-transfer" => {
let setup_code = dc_initiate_key_transfer(context);
if !setup_code.is_null() {
println!(
"Setup code for the transferred setup message: {}",
as_str(setup_code),
);
free(setup_code as *mut libc::c_void);
} else {
bail!("Failed to generate setup code");
};
}
"get-setupcodebegin" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let msg_id: MsgId = MsgId::new(arg1.parse()?);
let msg = Message::load_from_db(context, msg_id)?;
if msg.is_setupmessage() {
let setupcodebegin = msg.get_setupcodebegin(context);
let msg_id: u32 = arg1.parse()?;
let msg = dc_get_msg(context, msg_id)?;
if dc_msg_is_setupmessage(&msg) {
let setupcodebegin = dc_msg_get_setupcodebegin(context, &msg);
println!(
"The setup code for setup message {} starts with: {}",
"The setup code for setup message Msg#{} starts with: {}",
msg_id,
setupcodebegin.unwrap_or_default(),
as_str(setupcodebegin),
);
free(setupcodebegin as *mut libc::c_void);
} else {
bail!("{} is no setup message.", msg_id,);
bail!("Msg#{} is no setup message.", msg_id,);
}
}
"continue-key-transfer" => {
@@ -437,28 +485,33 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
!arg1.is_empty() && !arg2.is_empty(),
"Arguments <msg-id> <setup-code> expected"
);
continue_key_transfer(context, MsgId::new(arg1.parse()?), &arg2)?;
if !dc_continue_key_transfer(context, arg1.parse()?, arg2_c) {
bail!("Continue key transfer failed");
}
}
"has-backup" => {
has_backup(context, blobdir)?;
let ret = dc_imex_has_backup(context, context.get_blobdir());
if ret.is_null() {
println!("No backup found.");
}
}
"export-backup" => {
imex(context, ImexMode::ExportBackup, Some(blobdir));
dc_imex(context, 11, Some(context.get_blobdir()), ptr::null());
}
"import-backup" => {
ensure!(!arg1.is_empty(), "Argument <backup-file> missing.");
imex(context, ImexMode::ImportBackup, Some(arg1));
dc_imex(context, 12, Some(arg1), ptr::null());
}
"export-keys" => {
imex(context, ImexMode::ExportSelfKeys, Some(blobdir));
dc_imex(context, 1, Some(context.get_blobdir()), ptr::null());
}
"import-keys" => {
imex(context, ImexMode::ImportSelfKeys, Some(blobdir));
dc_imex(context, 2, Some(context.get_blobdir()), ptr::null());
}
"export-setup" => {
let setup_code = create_setup_code(context);
let file_name = blobdir.join("autocrypt-setup-message.html");
let file_content = render_setup_file(context, &setup_code)?;
let setup_code = dc_create_setup_code(context);
let file_name = context.get_blobdir().join("autocrypt-setup-message.html");
let file_content = dc_render_setup_file(context, &setup_code)?;
std::fs::write(&file_name, file_content)?;
println!(
"Setup message written to: {}\nSetup code: {}",
@@ -476,7 +529,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(0 != dc_reset_tables(context, bits), "Reset failed");
}
"stop" => {
context.stop_ongoing();
dc_stop_ongoing_process(context);
}
"set" => {
ensure!(!arg1.is_empty(), "Argument <key> missing.");
@@ -493,9 +546,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"info" => {
println!("{:#?}", context.get_info());
}
"interrupt" => {
interrupt_inbox_idle(context, true);
}
"maybenetwork" => {
maybe_network(context);
}
@@ -520,12 +570,15 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
for i in (0..cnt).rev() {
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
let temp_subtitle = chat.get_subtitle(context);
let temp_name = chat.get_name();
info!(
context,
"{}#{}: {} [{} fresh]",
"{}#{}: {} [{}] [{} fresh]",
chat_prefix(&chat),
chat.get_id(),
chat.get_name(),
temp_name,
temp_subtitle,
chat::get_fresh_msg_cnt(context, chat.get_id()),
);
let lot = chatlist.get_summary(context, i, Some(&chat));
@@ -582,35 +635,21 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(sel_chat.is_some(), "Failed to select chat");
let sel_chat = sel_chat.as_ref().unwrap();
let msglist = chat::get_chat_msgs(context, sel_chat.get_id(), 0x1, None);
let members = chat::get_chat_contacts(context, sel_chat.id);
let subtitle = if sel_chat.is_device_talk() {
"device-talk".to_string()
} else if sel_chat.get_type() == Chattype::Single && !members.is_empty() {
let contact = Contact::get_by_id(context, members[0])?;
contact.get_addr().to_string()
} else {
format!("{} member(s)", members.len())
};
let msglist = chat::get_chat_msgs(context, sel_chat.get_id(), 0x1, 0);
let temp2 = sel_chat.get_subtitle(context);
let temp_name = sel_chat.get_name();
info!(
context,
"{}#{}: {} [{}]{}{}",
"{}#{}: {} [{}]{}",
chat_prefix(sel_chat),
sel_chat.get_id(),
sel_chat.get_name(),
subtitle,
temp_name,
temp2,
if sel_chat.is_sending_locations() {
"📍"
} else {
""
},
match sel_chat.get_profile_image(context) {
Some(icon) => match icon.to_str() {
Some(icon) => format!(" Icon: {}", icon),
_ => " Icon: Err".to_string(),
},
_ => "".to_string(),
},
);
log_msglist(context, &msglist)?;
if let Some(draft) = chat::get_draft(context, sel_chat.get_id())? {
@@ -632,7 +671,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"createchatbymsg" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing");
let msg_id = MsgId::new(arg1.parse()?);
let msg_id: u32 = arg1.parse()?;
let chat_id = chat::create_by_msg_id(context, msg_id)?;
let chat = Chat::load_from_db(context, chat_id)?;
@@ -724,7 +763,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let marker = location.marker.as_ref().unwrap_or(&default_marker);
info!(
context,
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} {} {}",
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}",
location.location_id,
dc_timestamp_to_str(location.timestamp),
location.latitude,
@@ -761,7 +800,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let longitude = arg2.parse()?;
let continue_streaming = location::set(context, latitude, longitude, 0.);
if continue_streaming {
if 0 != continue_streaming {
println!("Success, streaming should be continued.");
} else {
println!("Success, streaming can be stoppped.");
@@ -786,14 +825,14 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!arg1.is_empty(), "No file given.");
let mut msg = Message::new(if arg0 == "sendimage" {
let mut msg = dc_msg_new(if arg0 == "sendimage" {
Viewtype::Image
} else {
Viewtype::File
});
msg.set_file(arg1, None);
dc_msg_set_file(&mut msg, arg1_c, ptr::null());
if !arg2.is_empty() {
msg.set_text(Some(arg2.to_string()));
dc_msg_set_text(&mut msg, arg2_c);
}
chat::send_msg(context, sel_chat.as_ref().unwrap().get_id(), &mut msg)?;
}
@@ -815,8 +854,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(sel_chat.is_some(), "No chat selected.");
if !arg1.is_empty() {
let mut draft = Message::new(Viewtype::Text);
draft.set_text(Some(arg1.to_string()));
let mut draft = dc_msg_new(Viewtype::Text);
dc_msg_set_text(&mut draft, arg1_c);
chat::set_draft(
context,
sel_chat.as_ref().unwrap().get_id(),
@@ -828,15 +867,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
println!("Draft deleted.");
}
}
"devicemsg" => {
ensure!(
!arg1.is_empty(),
"Please specify text to add as device message."
);
let mut msg = Message::new(Viewtype::Text);
msg.set_text(Some(arg1.to_string()));
chat::add_device_msg(context, None, Some(&mut msg))?;
}
"listmedia" => {
ensure!(sel_chat.is_some(), "No chat selected.");
@@ -850,9 +880,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
println!("{} images or videos: ", images.len());
for (i, data) in images.iter().enumerate() {
if 0 == i {
print!("{}", data);
print!("Msg#{}", data);
} else {
print!(", {}", data);
print!(", Msg#{}", data);
}
}
print!("\n");
@@ -860,7 +890,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"archive" | "unarchive" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
let chat_id = arg1.parse()?;
chat::archive(context, chat_id, arg0 == "archive")?;
chat::archive(
context,
chat_id,
if arg0 == "archive" { true } else { false },
)?;
}
"delchat" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
@@ -869,9 +903,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"msginfo" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let id = MsgId::new(arg1.parse()?);
let res = message::get_msg_info(context, id);
println!("{}", res);
let id = arg1.parse()?;
let res = dc_get_msg_info(context, id);
println!("{}", as_str(res));
}
"listfresh" => {
let msglist = context.get_fresh_msgs();
@@ -885,28 +919,33 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"Arguments <msg-id> <chat-id> expected"
);
let mut msg_ids = [MsgId::new(0); 1];
let mut msg_ids = [0; 1];
let chat_id = arg2.parse()?;
msg_ids[0] = MsgId::new(arg1.parse()?);
chat::forward_msgs(context, &msg_ids, chat_id)?;
msg_ids[0] = arg1.parse()?;
chat::forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
}
"markseen" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut msg_ids = [MsgId::new(0); 1];
msg_ids[0] = MsgId::new(arg1.parse()?);
message::markseen_msgs(context, &msg_ids);
let mut msg_ids = [0; 1];
msg_ids[0] = arg1.parse()?;
dc_markseen_msgs(context, msg_ids.as_mut_ptr(), 1);
}
"star" | "unstar" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut msg_ids = [MsgId::new(0); 1];
msg_ids[0] = MsgId::new(arg1.parse()?);
message::star_msgs(context, &msg_ids, arg0 == "star");
let mut msg_ids = [0; 1];
msg_ids[0] = arg1.parse()?;
dc_star_msgs(
context,
msg_ids.as_mut_ptr(),
1,
if arg0 == "star" { 1 } else { 0 },
);
}
"delmsg" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut ids = [MsgId::new(0); 1];
ids[0] = MsgId::new(arg1.parse()?);
message::delete_msgs(context, &ids);
let mut ids = [0; 1];
ids[0] = arg1.parse()?;
dc_delete_msgs(context, ids.as_mut_ptr(), 1);
}
"listcontacts" | "contacts" | "listverified" => {
let contacts = Contact::get_all(
@@ -989,23 +1028,19 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"fileinfo" => {
ensure!(!arg1.is_empty(), "Argument <file> missing.");
if let Ok(buf) = dc_read_file(context, &arg1) {
if let Some(buf) = dc_read_file_safe(context, &arg1) {
let (width, height) = dc_get_filemeta(&buf)?;
println!("width={}, height={}", width, height);
} else {
bail!("Command failed.");
}
}
"emptyserver" => {
ensure!(!arg1.is_empty(), "Argument <flags> missing");
message::dc_empty_server(context, arg1.parse()?);
}
"" => (),
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
}
free(arg1_c as *mut _);
free(arg2_c as *mut _);
Ok(())
}

View File

@@ -23,9 +23,11 @@ use std::sync::{Arc, Mutex, RwLock};
use deltachat::config;
use deltachat::configure::*;
use deltachat::context::*;
use deltachat::dc_tools::*;
use deltachat::job::*;
use deltachat::oauth2::*;
use deltachat::securejoin::*;
use deltachat::x::*;
use deltachat::Event;
use rustyline::completion::{Completer, FilenameCompleter, Pair};
use rustyline::config::OutputStreamType;
@@ -43,6 +45,7 @@ use self::cmdline::*;
fn receive_event(_context: &Context, event: Event) -> libc::uintptr_t {
match event {
Event::GetString { .. } => {}
Event::Info(msg) => {
/* do not show the event as this would fill the screen */
println!("{}", msg);
@@ -150,11 +153,11 @@ fn start_threads(c: Arc<RwLock<Context>>) {
let ctx = c.clone();
let handle_imap = std::thread::spawn(move || loop {
while_running!({
perform_inbox_jobs(&ctx.read().unwrap());
perform_inbox_fetch(&ctx.read().unwrap());
perform_imap_jobs(&ctx.read().unwrap());
perform_imap_fetch(&ctx.read().unwrap());
while_running!({
let context = ctx.read().unwrap();
perform_inbox_idle(&context);
perform_imap_idle(&context);
});
});
});
@@ -202,7 +205,7 @@ fn stop_threads(context: &Context) {
println!("Stopping threads");
IS_RUNNING.store(false, Ordering::Relaxed);
interrupt_inbox_idle(context, true);
interrupt_imap_idle(context);
interrupt_mvbox_idle(context);
interrupt_sentbox_idle(context);
interrupt_smtp_idle(context);
@@ -235,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",
@@ -250,7 +253,7 @@ const IMEX_COMMANDS: [&str; 12] = [
"stop",
];
const DB_COMMANDS: [&str; 11] = [
const DB_COMMANDS: [&'static str; 11] = [
"info",
"open",
"close",
@@ -264,7 +267,7 @@ const DB_COMMANDS: [&str; 11] = [
"housekeeping",
];
const CHAT_COMMANDS: [&str; 24] = [
const CHAT_COMMANDS: [&'static str; 24] = [
"listchats",
"listarchived",
"chat",
@@ -290,7 +293,7 @@ const CHAT_COMMANDS: [&str; 24] = [
"unarchive",
"delchat",
];
const MESSAGE_COMMANDS: [&str; 8] = [
const MESSAGE_COMMANDS: [&'static str; 8] = [
"listmsgs",
"msginfo",
"listfresh",
@@ -300,7 +303,7 @@ const MESSAGE_COMMANDS: [&str; 8] = [
"unstar",
"delmsg",
];
const CONTACT_COMMANDS: [&str; 6] = [
const CONTACT_COMMANDS: [&'static str; 6] = [
"listcontacts",
"listverified",
"addcontact",
@@ -308,7 +311,7 @@ const CONTACT_COMMANDS: [&str; 6] = [
"delcontact",
"cleanupcontacts",
];
const MISC_COMMANDS: [&str; 9] = [
const MISC_COMMANDS: [&'static str; 9] = [
"getqr", "getbadqr", "checkqr", "event", "fileinfo", "clear", "exit", "quit", "help",
];
@@ -334,8 +337,8 @@ impl Hinter for DcHelper {
}
}
static COLORED_PROMPT: &str = "\x1b[1;32m> \x1b[0m";
static PROMPT: &str = "> ";
static COLORED_PROMPT: &'static str = "\x1b[1;32m> \x1b[0m";
static PROMPT: &'static str = "> ";
impl Highlighter for DcHelper {
fn highlight_prompt<'p>(&self, prompt: &'p str) -> Cow<'p, str> {
@@ -366,12 +369,11 @@ fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
println!("Error: Bad arguments, expected [db-name].");
return Err(format_err!("No db-name specified"));
}
let context = ContextBuilder::new(
let context = Context::new(
Box::new(receive_event),
"CLI".into(),
Path::new(&args[1]).to_path_buf(),
)
.create()?;
)?;
println!("Delta Chat Core is awaiting your commands.");
@@ -439,6 +441,11 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
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" => {
@@ -456,9 +463,9 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
}
"imap-jobs" => {
if HANDLE.clone().lock().unwrap().is_some() {
println!("inbox-jobs are already running in a thread.");
println!("imap-jobs are already running in a thread.");
} else {
perform_inbox_jobs(&ctx.read().unwrap());
perform_imap_jobs(&ctx.read().unwrap());
}
}
"configure" => {
@@ -514,6 +521,8 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
_ => dc_cmdline(&ctx.read().unwrap(), line)?,
}
free(arg1_c as *mut _);
Ok(ExitResult::Continue)
}

View File

@@ -11,8 +11,7 @@ use deltachat::configure::*;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::job::{
perform_inbox_fetch, perform_inbox_idle, perform_inbox_jobs, perform_smtp_idle,
perform_smtp_jobs,
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
};
use deltachat::Event;
@@ -36,91 +35,93 @@ fn cb(_ctx: &Context, event: Event) -> usize {
}
fn main() {
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
println!("creating database {:?}", dbfile);
let ctx = ContextBuilder::new(Box::new(cb), "FakeOs".into(), dbfile)
.create()
.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 ctx = Arc::new(ctx);
let ctx1 = ctx.clone();
let r1 = running.clone();
let t1 = thread::spawn(move || {
while *r1.read().unwrap() {
perform_inbox_jobs(&ctx1);
if *r1.read().unwrap() {
perform_inbox_fetch(&ctx1);
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 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_inbox_idle(&ctx1);
perform_imap_fetch(&ctx1);
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);
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,);
}
});
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);
thread::sleep(duration);
// 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);
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!("stopping threads");
println!("fetching chats..");
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
*running.clone().write().unwrap() = false;
deltachat::job::interrupt_imap_idle(&ctx);
deltachat::job::interrupt_smtp_idle(&ctx);
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,);
println!("joining");
t1.join().unwrap();
t2.join().unwrap();
println!("closing");
}
thread::sleep(duration);
// 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);
println!("stopping threads");
*running.write().unwrap() = false;
deltachat::job::interrupt_inbox_idle(&ctx, true);
deltachat::job::interrupt_smtp_idle(&ctx);
println!("joining");
t1.join().unwrap();
t2.join().unwrap();
println!("closing");
}

View File

@@ -1,42 +0,0 @@
# copied from http://koushiro.me/2019/04/30/Building-and-Testing-Rust-projects-on-CircleCI/
version: 2.1
jobs:
build:
docker:
- image: ubuntu:18.04
working_directory: ~/deltachat-core-rust
steps:
- checkout
- run:
name: Setup build environment
command: |
apt update
apt install -y curl build-essential autoconf libtool git python pkg-config
# this will pick default toolchain from `rust-toolchain` file
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --no-modify-path --default-toolchain none -y;
source $HOME/.cargo/env
no_output_timeout: 1800s
- run:
name: Format
command: |
export PATH=~/.cargo/bin:$PATH
rustup component add rustfmt
cargo fmt -- --check
- run:
name: Test
command: |
export PATH=~/.cargo/bin:$PATH
export RUST_BACKTRACE=1
cargo test
workflows:
version: 2.1
build:
jobs:
- build

View File

@@ -1,23 +0,0 @@
[package]
name = "mmime"
version = "0.1.2"
authors = ["dignifiedquire <dignifiedquire@users.noreply.github.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
homepage = "https://github.com/deltachat/deltachat-core-rust"
repository = "https://github.com/deltachat/deltachat-core-rust"
readme = "README.md"
description = "Mime parsing for email"
keywords = ["mail", "mim", "email", "imap", "smtp"]
categories = ["std", "email"]
[dependencies]
libc = "0.2.54"
charset = "0.1.2"
memmap = "0.7.0"
lazy_static = "1.3.0"
rand = "0.6.5"
chrono = "0.4.6"
hex = "0.4.0"

View File

@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,23 +0,0 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@@ -1,4 +0,0 @@
This library is primarly distributed under the terms of both the MIT license and
the Apache License (Version 2.0).
See LICENSE-MIT and LICENSE-APACHE for details.

View File

@@ -1,16 +0,0 @@
# mmime
[![CircleCI build status][circle-shield]][circle] [![Appveyor build status][appveyor-shield]][appveyor] [![License][license-shield]][license]
> mmmmmmime parsing
Base code was compiled using c2rust from libetpan.
[circle-shield]: https://img.shields.io/circleci/project/github/dignifiedquire/mmime/master.svg?style=flat-square
[circle]: https://circleci.com/gh/dignifiedquire/mmime/
[appveyor-shield]: https://ci.appveyor.com/api/projects/status/l26co5rba32knrlu/branch/master?style=flat-square
[appveyor]: https://ci.appveyor.com/project/dignifiedquire/mmime/branch/master
[license-shield]: https://img.shields.io/badge/License-MIT%2FApache2.0-green.svg?style=flat-square
[license]: https://github.com/rpgp/rpgp/blob/master/LICENSE.md

View File

@@ -1,32 +0,0 @@
use crate::other::*;
use libc;
use std::ffi::{CStr, CString};
pub const MAIL_CHARCONV_ERROR_CONV: libc::c_uint = 3;
pub const MAIL_CHARCONV_ERROR_MEMORY: libc::c_uint = 2;
pub const MAIL_CHARCONV_ERROR_UNKNOWN_CHARSET: libc::c_uint = 1;
pub const MAIL_CHARCONV_NO_ERROR: libc::c_uint = 0;
pub unsafe fn charconv(
tocode: *const libc::c_char,
fromcode: *const libc::c_char,
s: *const libc::c_char,
length: size_t,
result: *mut *mut libc::c_char,
) -> libc::c_int {
assert!(!fromcode.is_null(), "invalid fromcode");
assert!(!s.is_null(), "invalid input string");
if let Some(encoding) =
charset::Charset::for_label(CStr::from_ptr(fromcode).to_string_lossy().as_bytes())
{
let data = std::slice::from_raw_parts(s as *const u8, strlen(s));
let (res, _, _) = encoding.decode(data);
let res_c = CString::new(res.as_bytes()).unwrap_or_default();
*result = strdup(res_c.as_ptr()) as *mut _;
MAIL_CHARCONV_NO_ERROR as libc::c_int
} else {
MAIL_CHARCONV_ERROR_UNKNOWN_CHARSET as libc::c_int
}
}

View File

@@ -1,427 +0,0 @@
use libc;
use crate::other::*;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct chashdatum {
pub data: *mut libc::c_void,
pub len: libc::c_uint,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct chash {
pub size: libc::c_uint,
pub count: libc::c_uint,
pub copyvalue: libc::c_int,
pub copykey: libc::c_int,
pub cells: *mut *mut chashcell,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct chashcell {
pub func: libc::c_uint,
pub key: chashdatum,
pub value: chashdatum,
pub next: *mut chashcell,
}
pub type chashiter = chashcell;
/* Allocates a new (empty) hash using this initial size and the given flags,
specifying which data should be copied in the hash.
CHASH_COPYNONE : Keys/Values are not copied.
CHASH_COPYKEY : Keys are dupped and freed as needed in the hash.
CHASH_COPYVALUE : Values are dupped and freed as needed in the hash.
CHASH_COPYALL : Both keys and values are dupped in the hash.
*/
pub unsafe fn chash_new(mut size: libc::c_uint, mut flags: libc::c_int) -> *mut chash {
let mut h: *mut chash = 0 as *mut chash;
h = malloc(::std::mem::size_of::<chash>() as libc::size_t) as *mut chash;
if h.is_null() {
return 0 as *mut chash;
}
if size < 13i32 as libc::c_uint {
size = 13i32 as libc::c_uint
}
(*h).count = 0i32 as libc::c_uint;
(*h).cells = calloc(
size as libc::size_t,
::std::mem::size_of::<*mut chashcell>() as libc::size_t,
) as *mut *mut chashcell;
if (*h).cells.is_null() {
free(h as *mut libc::c_void);
return 0 as *mut chash;
}
(*h).size = size;
(*h).copykey = flags & 1i32;
(*h).copyvalue = flags & 2i32;
return h;
}
/* Frees a hash */
pub unsafe fn chash_free(mut hash: *mut chash) {
let mut indx: libc::c_uint = 0;
let mut iter: *mut chashiter = 0 as *mut chashiter;
let mut next: *mut chashiter = 0 as *mut chashiter;
indx = 0i32 as libc::c_uint;
while indx < (*hash).size {
iter = *(*hash).cells.offset(indx as isize);
while !iter.is_null() {
next = (*iter).next;
if 0 != (*hash).copykey {
free((*iter).key.data);
}
if 0 != (*hash).copyvalue {
free((*iter).value.data);
}
free(iter as *mut libc::c_void);
iter = next
}
indx = indx.wrapping_add(1)
}
free((*hash).cells as *mut libc::c_void);
free(hash as *mut libc::c_void);
}
/* Removes all elements from a hash */
pub unsafe fn chash_clear(mut hash: *mut chash) {
let mut indx: libc::c_uint = 0;
let mut iter: *mut chashiter = 0 as *mut chashiter;
let mut next: *mut chashiter = 0 as *mut chashiter;
indx = 0i32 as libc::c_uint;
while indx < (*hash).size {
iter = *(*hash).cells.offset(indx as isize);
while !iter.is_null() {
next = (*iter).next;
if 0 != (*hash).copykey {
free((*iter).key.data);
}
if 0 != (*hash).copyvalue {
free((*iter).value.data);
}
free(iter as *mut libc::c_void);
iter = next
}
indx = indx.wrapping_add(1)
}
memset(
(*hash).cells as *mut libc::c_void,
0i32,
((*hash).size as libc::size_t)
.wrapping_mul(::std::mem::size_of::<*mut chashcell>() as libc::size_t),
);
(*hash).count = 0i32 as libc::c_uint;
}
/* Adds an entry in the hash table.
Length can be 0 if key/value are strings.
If an entry already exists for this key, it is replaced, and its value
is returned. Otherwise, the data pointer will be NULL and the length
field be set to TRUE or FALSe to indicate success or failure. */
pub unsafe fn chash_set(
mut hash: *mut chash,
mut key: *mut chashdatum,
mut value: *mut chashdatum,
mut oldvalue: *mut chashdatum,
) -> libc::c_int {
let mut current_block: u64;
let mut func: libc::c_uint = 0;
let mut indx: libc::c_uint = 0;
let mut iter: *mut chashiter = 0 as *mut chashiter;
let mut cell: *mut chashiter = 0 as *mut chashiter;
let mut r: libc::c_int = 0;
if (*hash).count > (*hash).size.wrapping_mul(3i32 as libc::c_uint) {
r = chash_resize(
hash,
(*hash)
.count
.wrapping_div(3i32 as libc::c_uint)
.wrapping_mul(2i32 as libc::c_uint)
.wrapping_add(1i32 as libc::c_uint),
);
if r < 0i32 {
current_block = 17701753836843438419;
} else {
current_block = 7095457783677275021;
}
} else {
current_block = 7095457783677275021;
}
match current_block {
7095457783677275021 => {
func = chash_func((*key).data as *const libc::c_char, (*key).len);
indx = func.wrapping_rem((*hash).size);
iter = *(*hash).cells.offset(indx as isize);
loop {
if iter.is_null() {
current_block = 17788412896529399552;
break;
}
if (*iter).key.len == (*key).len
&& (*iter).func == func
&& 0 == memcmp((*iter).key.data, (*key).data, (*key).len as libc::size_t)
{
/* found, replacing entry */
if 0 != (*hash).copyvalue {
let mut data: *mut libc::c_char = 0 as *mut libc::c_char;
data = chash_dup((*value).data, (*value).len);
if data.is_null() {
current_block = 17701753836843438419;
break;
}
free((*iter).value.data);
(*iter).value.data = data as *mut libc::c_void;
(*iter).value.len = (*value).len
} else {
if !oldvalue.is_null() {
(*oldvalue).data = (*iter).value.data;
(*oldvalue).len = (*iter).value.len
}
(*iter).value.data = (*value).data;
(*iter).value.len = (*value).len
}
if 0 == (*hash).copykey {
(*iter).key.data = (*key).data
}
if !oldvalue.is_null() {
(*oldvalue).data = (*value).data;
(*oldvalue).len = (*value).len
}
return 0i32;
} else {
iter = (*iter).next
}
}
match current_block {
17701753836843438419 => {}
_ => {
if !oldvalue.is_null() {
(*oldvalue).data = 0 as *mut libc::c_void;
(*oldvalue).len = 0i32 as libc::c_uint
}
cell = malloc(::std::mem::size_of::<chashcell>() as libc::size_t)
as *mut chashcell;
if !cell.is_null() {
if 0 != (*hash).copykey {
(*cell).key.data =
chash_dup((*key).data, (*key).len) as *mut libc::c_void;
if (*cell).key.data.is_null() {
current_block = 4267898785354516004;
} else {
current_block = 7226443171521532240;
}
} else {
(*cell).key.data = (*key).data;
current_block = 7226443171521532240;
}
match current_block {
7226443171521532240 => {
(*cell).key.len = (*key).len;
if 0 != (*hash).copyvalue {
(*cell).value.data =
chash_dup((*value).data, (*value).len) as *mut libc::c_void;
if (*cell).value.data.is_null() {
if 0 != (*hash).copykey {
free((*cell).key.data);
}
current_block = 4267898785354516004;
} else {
current_block = 6717214610478484138;
}
} else {
(*cell).value.data = (*value).data;
current_block = 6717214610478484138;
}
match current_block {
4267898785354516004 => {}
_ => {
(*cell).value.len = (*value).len;
(*cell).func = func;
(*cell).next = *(*hash).cells.offset(indx as isize);
let ref mut fresh0 = *(*hash).cells.offset(indx as isize);
*fresh0 = cell;
(*hash).count = (*hash).count.wrapping_add(1);
return 0i32;
}
}
}
_ => {}
}
free(cell as *mut libc::c_void);
}
}
}
}
_ => {}
}
return -1i32;
}
#[inline]
unsafe fn chash_dup(mut data: *const libc::c_void, mut len: libc::c_uint) -> *mut libc::c_char {
let mut r: *mut libc::c_void = 0 as *mut libc::c_void;
r = malloc(len as libc::size_t) as *mut libc::c_char as *mut libc::c_void;
if r.is_null() {
return 0 as *mut libc::c_char;
}
memcpy(r, data, len as libc::size_t);
return r as *mut libc::c_char;
}
#[inline]
unsafe fn chash_func(mut key: *const libc::c_char, mut len: libc::c_uint) -> libc::c_uint {
let mut c: libc::c_uint = 5381i32 as libc::c_uint;
let mut k: *const libc::c_char = key;
loop {
let fresh1 = len;
len = len.wrapping_sub(1);
if !(0 != fresh1) {
break;
}
let fresh2 = k;
k = k.offset(1);
c = (c << 5i32)
.wrapping_add(c)
.wrapping_add(*fresh2 as libc::c_uint)
}
return c;
}
/* Resizes the hash table to the passed size. */
pub unsafe fn chash_resize(mut hash: *mut chash, mut size: libc::c_uint) -> libc::c_int {
let mut cells: *mut *mut chashcell = 0 as *mut *mut chashcell;
let mut indx: libc::c_uint = 0;
let mut nindx: libc::c_uint = 0;
let mut iter: *mut chashiter = 0 as *mut chashiter;
let mut next: *mut chashiter = 0 as *mut chashiter;
if (*hash).size == size {
return 0i32;
}
cells = calloc(
size as libc::size_t,
::std::mem::size_of::<*mut chashcell>() as libc::size_t,
) as *mut *mut chashcell;
if cells.is_null() {
return -1i32;
}
indx = 0i32 as libc::c_uint;
while indx < (*hash).size {
iter = *(*hash).cells.offset(indx as isize);
while !iter.is_null() {
next = (*iter).next;
nindx = (*iter).func.wrapping_rem(size);
(*iter).next = *cells.offset(nindx as isize);
let ref mut fresh3 = *cells.offset(nindx as isize);
*fresh3 = iter;
iter = next
}
indx = indx.wrapping_add(1)
}
free((*hash).cells as *mut libc::c_void);
(*hash).size = size;
(*hash).cells = cells;
return 0i32;
}
/* Retrieves the data associated to the key if it is found in the hash table.
The data pointer and the length will be NULL if not found*/
pub unsafe fn chash_get(
mut hash: *mut chash,
mut key: *mut chashdatum,
mut result: *mut chashdatum,
) -> libc::c_int {
let mut func: libc::c_uint = 0;
let mut iter: *mut chashiter = 0 as *mut chashiter;
func = chash_func((*key).data as *const libc::c_char, (*key).len);
iter = *(*hash)
.cells
.offset(func.wrapping_rem((*hash).size) as isize);
while !iter.is_null() {
if (*iter).key.len == (*key).len
&& (*iter).func == func
&& 0 == memcmp((*iter).key.data, (*key).data, (*key).len as libc::size_t)
{
*result = (*iter).value;
return 0i32;
}
iter = (*iter).next
}
return -1i32;
}
/* Removes the entry associated to this key if it is found in the hash table,
and returns its contents if not dupped (otherwise, pointer will be NULL
and len TRUE). If entry is not found both pointer and len will be NULL. */
pub unsafe fn chash_delete(
mut hash: *mut chash,
mut key: *mut chashdatum,
mut oldvalue: *mut chashdatum,
) -> libc::c_int {
/* chashdatum result = { NULL, TRUE }; */
let mut func: libc::c_uint = 0;
let mut indx: libc::c_uint = 0;
let mut iter: *mut chashiter = 0 as *mut chashiter;
let mut old: *mut chashiter = 0 as *mut chashiter;
func = chash_func((*key).data as *const libc::c_char, (*key).len);
indx = func.wrapping_rem((*hash).size);
old = 0 as *mut chashiter;
iter = *(*hash).cells.offset(indx as isize);
while !iter.is_null() {
if (*iter).key.len == (*key).len
&& (*iter).func == func
&& 0 == memcmp((*iter).key.data, (*key).data, (*key).len as libc::size_t)
{
if !old.is_null() {
(*old).next = (*iter).next
} else {
let ref mut fresh4 = *(*hash).cells.offset(indx as isize);
*fresh4 = (*iter).next
}
if 0 != (*hash).copykey {
free((*iter).key.data);
}
if 0 != (*hash).copyvalue {
free((*iter).value.data);
} else if !oldvalue.is_null() {
(*oldvalue).data = (*iter).value.data;
(*oldvalue).len = (*iter).value.len
}
free(iter as *mut libc::c_void);
(*hash).count = (*hash).count.wrapping_sub(1);
return 0i32;
}
old = iter;
iter = (*iter).next
}
return -1i32;
}
/* Returns an iterator to the first non-empty entry of the hash table */
pub unsafe fn chash_begin(mut hash: *mut chash) -> *mut chashiter {
let mut iter: *mut chashiter = 0 as *mut chashiter;
let mut indx: libc::c_uint = 0i32 as libc::c_uint;
iter = *(*hash).cells.offset(0isize);
while iter.is_null() {
indx = indx.wrapping_add(1);
if indx >= (*hash).size {
return 0 as *mut chashiter;
}
iter = *(*hash).cells.offset(indx as isize)
}
return iter;
}
/* Returns the next non-empty entry of the hash table */
pub unsafe fn chash_next(mut hash: *mut chash, mut iter: *mut chashiter) -> *mut chashiter {
let mut indx: libc::c_uint = 0;
if iter.is_null() {
return 0 as *mut chashiter;
}
indx = (*iter).func.wrapping_rem((*hash).size);
iter = (*iter).next;
while iter.is_null() {
indx = indx.wrapping_add(1);
if indx >= (*hash).size {
return 0 as *mut chashiter;
}
iter = *(*hash).cells.offset(indx as isize)
}
return iter;
}

View File

@@ -1,202 +0,0 @@
use libc;
use crate::other::*;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct clistcell {
pub data: *mut libc::c_void,
pub previous: *mut clistcell,
pub next: *mut clistcell,
}
#[derive(Clone)]
#[repr(C)]
pub struct clist {
pub first: *mut clistcell,
pub last: *mut clistcell,
pub count: libc::c_int,
}
impl Default for clist {
fn default() -> Self {
Self {
first: std::ptr::null_mut(),
last: std::ptr::null_mut(),
count: 0,
}
}
}
impl Drop for clist {
fn drop(&mut self) {
unsafe {
let mut l1 = self.first;
while !l1.is_null() {
let l2 = (*l1).next;
free(l1 as *mut libc::c_void);
l1 = l2
}
}
}
}
pub type clistiter = clistcell;
pub struct CListIterator {
cur: *mut clistiter,
}
impl Iterator for CListIterator {
type Item = *mut libc::c_void;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if self.cur.is_null() {
None
} else {
let data = (*self.cur).data;
self.cur = (*self.cur).next;
Some(data)
}
}
}
}
impl IntoIterator for &clist {
type Item = *mut libc::c_void;
type IntoIter = CListIterator;
fn into_iter(self) -> Self::IntoIter {
return CListIterator { cur: self.first };
}
}
pub type clist_func =
Option<unsafe extern "C" fn(_: *mut libc::c_void, _: *mut libc::c_void) -> ()>;
/* Allocate a new pointer list */
pub fn clist_new() -> *mut clist {
Box::into_raw(Box::new(Default::default()))
}
/* Destroys a list. Data pointed by data pointers is NOT freed. */
pub unsafe fn clist_free(mut lst: *mut clist) {
Box::from_raw(lst);
}
/* Inserts this data pointer after the element pointed by the iterator */
pub unsafe fn clist_insert_after(
mut lst: *mut clist,
mut iter: *mut clistiter,
mut data: *mut libc::c_void,
) -> libc::c_int {
let mut c: *mut clistcell = 0 as *mut clistcell;
c = malloc(::std::mem::size_of::<clistcell>() as libc::size_t) as *mut clistcell;
if c.is_null() {
return -1i32;
}
(*c).data = data;
(*lst).count += 1;
if (*lst).first == (*lst).last && (*lst).last.is_null() {
(*c).next = 0 as *mut clistcell;
(*c).previous = (*c).next;
(*lst).last = c;
(*lst).first = (*lst).last;
return 0i32;
}
if iter.is_null() {
(*c).previous = (*lst).last;
(*(*c).previous).next = c;
(*c).next = 0 as *mut clistcell;
(*lst).last = c;
return 0i32;
}
(*c).previous = iter;
(*c).next = (*iter).next;
if !(*c).next.is_null() {
(*(*c).next).previous = c
} else {
(*lst).last = c
}
(*(*c).previous).next = c;
return 0i32;
}
/* Deletes the element pointed by the iterator.
Returns an iterator to the next element. */
pub unsafe fn clist_delete(mut lst: *mut clist, mut iter: *mut clistiter) -> *mut clistiter {
let mut ret: *mut clistiter = 0 as *mut clistiter;
if iter.is_null() {
return 0 as *mut clistiter;
}
if !(*iter).previous.is_null() {
(*(*iter).previous).next = (*iter).next
} else {
(*lst).first = (*iter).next
}
if !(*iter).next.is_null() {
(*(*iter).next).previous = (*iter).previous;
ret = (*iter).next
} else {
(*lst).last = (*iter).previous;
ret = 0 as *mut clistiter
}
free(iter as *mut libc::c_void);
(*lst).count -= 1;
return ret;
}
pub unsafe fn clist_foreach(
mut lst: *mut clist,
mut func: clist_func,
mut data: *mut libc::c_void,
) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*lst).first;
while !cur.is_null() {
func.expect("non-null function pointer")((*cur).data, data);
cur = (*cur).next
}
}
pub unsafe fn clist_nth_data(mut lst: *mut clist, mut indx: libc::c_int) -> *mut libc::c_void {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = internal_clist_nth(lst, indx);
if cur.is_null() {
return 0 as *mut libc::c_void;
}
return (*cur).data;
}
#[inline]
unsafe fn internal_clist_nth(mut lst: *mut clist, mut indx: libc::c_int) -> *mut clistiter {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*lst).first;
while indx > 0i32 && !cur.is_null() {
cur = (*cur).next;
indx -= 1
}
if cur.is_null() {
return 0 as *mut clistiter;
}
return cur;
}
pub unsafe fn clist_nth(mut lst: *mut clist, mut indx: libc::c_int) -> *mut clistiter {
return internal_clist_nth(lst, indx);
}
#[cfg(test)]
mod tests {
use super::*;
use std::ptr;
#[test]
fn test_clist_iterator() {
unsafe {
let mut c = clist_new();
assert!(!c.is_null());
clist_insert_after(c, ptr::null_mut(), clist_nth as _);
assert_eq!((*c).count, 1);
/* Only one iteration */
for data in &*c {
assert_eq!(data, clist_nth as _);
}
assert_eq!((*c).count, 1);
clist_free(c);
}
}
}

View File

@@ -1,71 +0,0 @@
pub const MAIL_ERROR_SSL: libc::c_uint = 58;
pub const MAIL_ERROR_FOLDER: libc::c_uint = 57;
pub const MAIL_ERROR_UNABLE: libc::c_uint = 56;
pub const MAIL_ERROR_SYSTEM: libc::c_uint = 55;
pub const MAIL_ERROR_COMMAND: libc::c_uint = 54;
pub const MAIL_ERROR_SEND: libc::c_uint = 53;
pub const MAIL_ERROR_CHAR_ENCODING_FAILED: libc::c_uint = 52;
pub const MAIL_ERROR_SUBJECT_NOT_FOUND: libc::c_uint = 51;
/* 50 */
pub const MAIL_ERROR_PROGRAM_ERROR: libc::c_uint = 50;
pub const MAIL_ERROR_NO_PERMISSION: libc::c_uint = 49;
pub const MAIL_ERROR_COMMAND_NOT_SUPPORTED: libc::c_uint = 48;
pub const MAIL_ERROR_NO_APOP: libc::c_uint = 47;
pub const MAIL_ERROR_READONLY: libc::c_uint = 46;
pub const MAIL_ERROR_FATAL: libc::c_uint = 45;
pub const MAIL_ERROR_CLOSE: libc::c_uint = 44;
pub const MAIL_ERROR_CAPABILITY: libc::c_uint = 43;
pub const MAIL_ERROR_PROTOCOL: libc::c_uint = 42;
/* misc errors */
pub const MAIL_ERROR_MISC: libc::c_uint = 41;
/* 40 */
pub const MAIL_ERROR_EXPUNGE: libc::c_uint = 40;
pub const MAIL_ERROR_NO_TLS: libc::c_uint = 39;
pub const MAIL_ERROR_CACHE_MISS: libc::c_uint = 38;
pub const MAIL_ERROR_STARTTLS: libc::c_uint = 37;
pub const MAIL_ERROR_MOVE: libc::c_uint = 36;
pub const MAIL_ERROR_FOLDER_NOT_FOUND: libc::c_uint = 35;
pub const MAIL_ERROR_REMOVE: libc::c_uint = 34;
pub const MAIL_ERROR_PART_NOT_FOUND: libc::c_uint = 33;
pub const MAIL_ERROR_INVAL: libc::c_uint = 32;
pub const MAIL_ERROR_PARSE: libc::c_uint = 31;
/* 30 */
pub const MAIL_ERROR_MSG_NOT_FOUND: libc::c_uint = 30;
pub const MAIL_ERROR_DISKSPACE: libc::c_uint = 29;
pub const MAIL_ERROR_SEARCH: libc::c_uint = 28;
pub const MAIL_ERROR_STORE: libc::c_uint = 27;
pub const MAIL_ERROR_FETCH: libc::c_uint = 26;
pub const MAIL_ERROR_COPY: libc::c_uint = 25;
pub const MAIL_ERROR_APPEND: libc::c_uint = 24;
pub const MAIL_ERROR_LSUB: libc::c_uint = 23;
pub const MAIL_ERROR_LIST: libc::c_uint = 22;
pub const MAIL_ERROR_UNSUBSCRIBE: libc::c_uint = 21;
/* 20 */
pub const MAIL_ERROR_SUBSCRIBE: libc::c_uint = 20;
pub const MAIL_ERROR_STATUS: libc::c_uint = 19;
pub const MAIL_ERROR_MEMORY: libc::c_uint = 18;
pub const MAIL_ERROR_SELECT: libc::c_uint = 17;
pub const MAIL_ERROR_EXAMINE: libc::c_uint = 16;
pub const MAIL_ERROR_CHECK: libc::c_uint = 15;
pub const MAIL_ERROR_RENAME: libc::c_uint = 14;
pub const MAIL_ERROR_NOOP: libc::c_uint = 13;
pub const MAIL_ERROR_LOGOUT: libc::c_uint = 12;
pub const MAIL_ERROR_DELETE: libc::c_uint = 11;
/* 10 */
pub const MAIL_ERROR_CREATE: libc::c_uint = 10;
pub const MAIL_ERROR_LOGIN: libc::c_uint = 9;
pub const MAIL_ERROR_STREAM: libc::c_uint = 8;
pub const MAIL_ERROR_FILE: libc::c_uint = 7;
pub const MAIL_ERROR_BAD_STATE: libc::c_uint = 6;
pub const MAIL_ERROR_CONNECT: libc::c_uint = 5;
pub const MAIL_ERROR_UNKNOWN: libc::c_uint = 4;
pub const MAIL_ERROR_NOT_IMPLEMENTED: libc::c_uint = 3;
pub const MAIL_NO_ERROR_NON_AUTHENTICATED: libc::c_uint = 2;
pub const MAIL_NO_ERROR_AUTHENTICATED: libc::c_uint = 1;
pub const MAIL_NO_ERROR: libc::c_uint = 0;
pub const MAILIMF_ERROR_FILE: libc::c_uint = 4;
pub const MAILIMF_ERROR_INVAL: libc::c_uint = 3;
pub const MAILIMF_ERROR_MEMORY: libc::c_uint = 2;
pub const MAILIMF_ERROR_PARSE: libc::c_uint = 1;
pub const MAILIMF_NO_ERROR: libc::c_uint = 0;

View File

@@ -1,386 +0,0 @@
use crate::clist::*;
use crate::mailimf::types::*;
use crate::mailmime::types::*;
use std::ffi::CStr;
pub unsafe fn display_mime(mut mime: *mut Mailmime) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
println!("{}", (*mime).mm_type);
match (*mime).mm_type as u32 {
MAILMIME_SINGLE => {
println!("single part");
}
MAILMIME_MULTIPLE => {
println!("multipart");
}
MAILMIME_MESSAGE => println!("message"),
_ => {}
}
if !(*mime).mm_mime_fields.is_null() {
if !(*(*(*mime).mm_mime_fields).fld_list).first.is_null() {
print!("MIME headers begin");
display_mime_fields((*mime).mm_mime_fields);
println!("MIME headers end");
}
}
display_mime_content((*mime).mm_content_type);
match (*mime).mm_type as u32 {
MAILMIME_SINGLE => {
display_mime_data((*mime).mm_data.mm_single);
}
MAILMIME_MULTIPLE => {
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
while !cur.is_null() {
display_mime(
(if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut Mailmime,
);
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
}
MAILMIME_MESSAGE => {
if !(*mime).mm_data.mm_message.mm_fields.is_null() {
if !(*(*(*mime).mm_data.mm_message.mm_fields).fld_list)
.first
.is_null()
{
println!("headers begin");
display_fields((*mime).mm_data.mm_message.mm_fields);
println!("headers end");
}
if !(*mime).mm_data.mm_message.mm_msg_mime.is_null() {
display_mime((*mime).mm_data.mm_message.mm_msg_mime);
}
}
}
_ => {}
};
}
unsafe fn display_mime_content(mut content_type: *mut mailmime_content) {
print!("type: ");
display_mime_type((*content_type).ct_type);
println!(
"/{}",
CStr::from_ptr((*content_type).ct_subtype).to_str().unwrap()
);
}
unsafe fn display_mime_type(mut type_0: *mut mailmime_type) {
match (*type_0).tp_type {
1 => {
display_mime_discrete_type((*type_0).tp_data.tp_discrete_type);
}
2 => {
display_mime_composite_type((*type_0).tp_data.tp_composite_type);
}
_ => {}
};
}
unsafe fn display_mime_composite_type(mut ct: *mut mailmime_composite_type) {
match (*ct).ct_type {
1 => {
print!("message");
}
2 => {
print!("multipart");
}
3 => {
print!("{}", CStr::from_ptr((*ct).ct_token).to_str().unwrap());
}
_ => {}
};
}
unsafe fn display_mime_discrete_type(mut discrete_type: *mut mailmime_discrete_type) {
match (*discrete_type).dt_type {
1 => {
print!("text");
}
2 => {
print!("image");
}
3 => {
print!("audio");
}
4 => {
print!("video");
}
5 => {
print!("application");
}
6 => {
print!("{}", (*discrete_type).dt_extension as u8 as char);
}
_ => {}
};
}
pub unsafe fn display_mime_data(mut data: *mut mailmime_data) {
match (*data).dt_type {
0 => {
println!(
"data : {} bytes",
(*data).dt_data.dt_text.dt_length as libc::c_uint,
);
}
1 => {
println!(
"data (file) : {}",
CStr::from_ptr((*data).dt_data.dt_filename)
.to_str()
.unwrap()
);
}
_ => {}
};
}
unsafe fn display_mime_dsp_parm(mut param: *mut mailmime_disposition_parm) {
match (*param).pa_type {
0 => {
println!(
"filename: {}",
CStr::from_ptr((*param).pa_data.pa_filename)
.to_str()
.unwrap()
);
}
_ => {}
};
}
unsafe fn display_mime_disposition(mut disposition: *mut mailmime_disposition) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*(*disposition).dsp_parms).first;
while !cur.is_null() {
let mut param: *mut mailmime_disposition_parm = 0 as *mut mailmime_disposition_parm;
param = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailmime_disposition_parm;
display_mime_dsp_parm(param);
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
}
unsafe fn display_mime_field(mut field: *mut mailmime_field) {
match (*field).fld_type {
1 => {
print!("content-type: ");
display_mime_content((*field).fld_data.fld_content);
println!("");
}
6 => {
display_mime_disposition((*field).fld_data.fld_disposition);
}
_ => {}
};
}
unsafe fn display_mime_fields(mut fields: *mut mailmime_fields) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*(*fields).fld_list).first;
while !cur.is_null() {
let mut field: *mut mailmime_field = 0 as *mut mailmime_field;
field = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailmime_field;
display_mime_field(field);
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
}
unsafe fn display_date_time(mut d: *mut mailimf_date_time) {
print!(
"{:02}/{:02}/{:02} {:02}:{:02}:{:02} +{:04}",
(*d).dt_day,
(*d).dt_month,
(*d).dt_year,
(*d).dt_hour,
(*d).dt_min,
(*d).dt_sec,
(*d).dt_zone,
);
}
unsafe fn display_orig_date(mut orig_date: *mut mailimf_orig_date) {
display_date_time((*orig_date).dt_date_time);
}
unsafe fn display_mailbox(mut mb: *mut mailimf_mailbox) {
if !(*mb).mb_display_name.is_null() {
print!(
"{}",
CStr::from_ptr((*mb).mb_display_name).to_str().unwrap()
);
}
print!("<{}>", CStr::from_ptr((*mb).mb_addr_spec).to_str().unwrap());
}
unsafe fn display_mailbox_list(mut mb_list: *mut mailimf_mailbox_list) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*(*mb_list).mb_list).first;
while !cur.is_null() {
let mut mb: *mut mailimf_mailbox = 0 as *mut mailimf_mailbox;
mb = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_mailbox;
display_mailbox(mb);
if !if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
.is_null()
{
print!(", ");
}
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
}
unsafe fn display_group(mut group: *mut mailimf_group) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
print!(
"{}: ",
CStr::from_ptr((*group).grp_display_name).to_str().unwrap()
);
cur = (*(*(*group).grp_mb_list).mb_list).first;
while !cur.is_null() {
let mut mb: *mut mailimf_mailbox = 0 as *mut mailimf_mailbox;
mb = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_mailbox;
display_mailbox(mb);
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
print!("; ");
}
unsafe fn display_address(mut a: *mut mailimf_address) {
match (*a).ad_type {
2 => {
display_group((*a).ad_data.ad_group);
}
1 => {
display_mailbox((*a).ad_data.ad_mailbox);
}
_ => {}
};
}
unsafe fn display_address_list(mut addr_list: *mut mailimf_address_list) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*(*addr_list).ad_list).first;
while !cur.is_null() {
let mut addr: *mut mailimf_address = 0 as *mut mailimf_address;
addr = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_address;
display_address(addr);
if !if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
.is_null()
{
print!(", ");
}
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
}
unsafe fn display_from(mut from: *mut mailimf_from) {
display_mailbox_list((*from).frm_mb_list);
}
unsafe fn display_to(mut to: *mut mailimf_to) {
display_address_list((*to).to_addr_list);
}
unsafe fn display_cc(mut cc: *mut mailimf_cc) {
display_address_list((*cc).cc_addr_list);
}
unsafe fn display_subject(mut subject: *mut mailimf_subject) {
print!("{}", CStr::from_ptr((*subject).sbj_value).to_str().unwrap());
}
unsafe fn display_field(mut field: *mut mailimf_field) {
match (*field).fld_type {
9 => {
print!("Date: ");
display_orig_date((*field).fld_data.fld_orig_date);
println!("");
}
10 => {
print!("From: ");
display_from((*field).fld_data.fld_from);
println!("");
}
13 => {
print!("To: ");
display_to((*field).fld_data.fld_to);
println!("");
}
14 => {
print!("Cc: ");
display_cc((*field).fld_data.fld_cc);
println!("");
}
19 => {
print!("Subject: ");
display_subject((*field).fld_data.fld_subject);
println!("");
}
16 => {
println!(
"Message-ID: {}",
CStr::from_ptr((*(*field).fld_data.fld_message_id).mid_value)
.to_str()
.unwrap(),
);
}
_ => {}
};
}
unsafe fn display_fields(mut fields: *mut mailimf_fields) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*(*fields).fld_list).first;
while !cur.is_null() {
let mut f: *mut mailimf_field = 0 as *mut mailimf_field;
f = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_field;
display_field(f);
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
}

View File

@@ -1,78 +0,0 @@
#![deny(clippy::correctness)]
// TODO: make all of these errors, such that clippy actually passes.
#![warn(clippy::all, clippy::perf, clippy::not_unsafe_ptr_arg_deref)]
// This is nice, but for now just annoying.
#![allow(clippy::unreadable_literal)]
#![feature(ptr_wrapping_offset_from)]
#![allow(unused_attributes)]
#![allow(unused_variables)]
#![allow(mutable_transmutes)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(unused_assignments)]
#![allow(unused_mut)]
#![allow(unused_must_use)]
#![feature(extern_types)]
#![feature(const_raw_ptr_to_usize_cast)]
pub mod charconv;
pub mod chash;
pub mod clist;
pub mod display;
pub mod mailimf;
pub mod mailmime;
pub mod mmapstring;
pub mod other;
pub use self::charconv::*;
pub use self::chash::*;
pub use self::clist::*;
pub use self::display::*;
pub use self::mailimf::*;
pub use self::mailmime::*;
pub use self::mmapstring::*;
pub use self::other::*;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mailmime_parse_test() {
unsafe {
let data = "MIME-Version: 1.0\
Content-Type: multipart/mixed; boundary=frontier\
\
This is a message with multiple parts in MIME format.\
--frontier\
Content-Type: text/plain\
\
This is the body of the message.\
--frontier\
Content-Type: application/octet-stream\
Content-Transfer-Encoding: base64\
\
PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg\
Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==\
--frontier--";
let c_data = std::ffi::CString::new(data).unwrap();
let mut current_index = 0;
let mut mime = std::ptr::null_mut();
let res = crate::mailmime::content::mailmime_parse(
c_data.as_ptr(),
data.len() as usize,
&mut current_index,
&mut mime,
);
assert_eq!(res, MAIL_NO_ERROR as libc::c_int);
assert!(!mime.is_null());
display_mime(mime);
mailmime::types::mailmime_free(mime);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,89 +0,0 @@
use crate::clist::*;
use crate::mailimf::types::*;
use crate::other::*;
/*
this function creates a new mailimf_fields structure with no fields
*/
pub unsafe fn mailimf_fields_new_empty() -> *mut mailimf_fields {
let mut list: *mut clist = 0 as *mut clist;
let mut fields_list: *mut mailimf_fields = 0 as *mut mailimf_fields;
list = clist_new();
if list.is_null() {
return 0 as *mut mailimf_fields;
}
fields_list = mailimf_fields_new(list);
if fields_list.is_null() {
return 0 as *mut mailimf_fields;
}
return fields_list;
}
/*
this function adds a field to the mailimf_fields structure
@return MAILIMF_NO_ERROR will be returned on success,
other code will be returned otherwise
*/
pub unsafe fn mailimf_fields_add(
mut fields: *mut mailimf_fields,
mut field: *mut mailimf_field,
) -> libc::c_int {
let mut r: libc::c_int = 0;
r = clist_insert_after(
(*fields).fld_list,
(*(*fields).fld_list).last,
field as *mut libc::c_void,
);
if r < 0i32 {
return MAILIMF_ERROR_MEMORY as libc::c_int;
}
return MAILIMF_NO_ERROR as libc::c_int;
}
/*
mailimf_field_new_custom creates a new field of type optional
@param name should be allocated with malloc()
@param value should be allocated with malloc()
*/
pub unsafe fn mailimf_field_new_custom(
mut name: *mut libc::c_char,
mut value: *mut libc::c_char,
) -> *mut mailimf_field {
let mut opt_field: *mut mailimf_optional_field = 0 as *mut mailimf_optional_field;
let mut field: *mut mailimf_field = 0 as *mut mailimf_field;
opt_field = mailimf_optional_field_new(name, value);
if !opt_field.is_null() {
field = mailimf_field_new(
MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int,
0 as *mut mailimf_return,
0 as *mut mailimf_orig_date,
0 as *mut mailimf_from,
0 as *mut mailimf_sender,
0 as *mut mailimf_to,
0 as *mut mailimf_cc,
0 as *mut mailimf_bcc,
0 as *mut mailimf_message_id,
0 as *mut mailimf_orig_date,
0 as *mut mailimf_from,
0 as *mut mailimf_sender,
0 as *mut mailimf_reply_to,
0 as *mut mailimf_to,
0 as *mut mailimf_cc,
0 as *mut mailimf_bcc,
0 as *mut mailimf_message_id,
0 as *mut mailimf_in_reply_to,
0 as *mut mailimf_references,
0 as *mut mailimf_subject,
0 as *mut mailimf_comments,
0 as *mut mailimf_keywords,
opt_field,
);
if field.is_null() {
mailimf_optional_field_free(opt_field);
} else {
return field;
}
}
return 0 as *mut mailimf_field;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,860 +0,0 @@
use libc;
use libc::toupper;
use crate::charconv::*;
use crate::mailimf::*;
use crate::mailmime::content::*;
use crate::mailmime::types::*;
use crate::mmapstring::*;
use crate::other::*;
pub const TYPE_WORD: libc::c_uint = 1;
pub const TYPE_ENCODED_WORD: libc::c_uint = 2;
pub const MAILMIME_ENCODING_Q: libc::c_uint = 1;
pub const MAILMIME_ENCODING_B: libc::c_uint = 0;
pub const TYPE_ERROR: libc::c_uint = 0;
pub unsafe fn mailmime_encoded_phrase_parse(
mut default_fromcode: *const libc::c_char,
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut tocode: *const libc::c_char,
mut result: *mut *mut libc::c_char,
) -> libc::c_int {
let mut current_block: u64;
let mut gphrase: *mut MMAPString = 0 as *mut MMAPString;
let mut word: *mut mailmime_encoded_word = 0 as *mut mailmime_encoded_word;
let mut first: libc::c_int = 0;
let mut cur_token: size_t = 0;
let mut r: libc::c_int = 0;
let mut res: libc::c_int = 0;
let mut str: *mut libc::c_char = 0 as *mut libc::c_char;
let mut wordutf8: *mut libc::c_char = 0 as *mut libc::c_char;
let mut type_0: libc::c_int = 0;
let mut missing_closing_quote: libc::c_int = 0;
cur_token = *indx;
gphrase = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
if gphrase.is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int
} else {
first = 1i32;
type_0 = TYPE_ERROR as libc::c_int;
loop {
let mut has_fwd: libc::c_int = 0;
word = 0 as *mut mailmime_encoded_word;
r = mailmime_encoded_word_parse(
message,
length,
&mut cur_token,
&mut word,
&mut has_fwd,
&mut missing_closing_quote,
);
if r == MAILIMF_NO_ERROR as libc::c_int {
if 0 == first && 0 != has_fwd {
if type_0 != TYPE_ENCODED_WORD as libc::c_int {
if mmap_string_append_c(gphrase, ' ' as i32 as libc::c_char).is_null() {
mailmime_encoded_word_free(word);
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 13246848547199022064;
break;
}
}
}
type_0 = TYPE_ENCODED_WORD as libc::c_int;
wordutf8 = 0 as *mut libc::c_char;
r = charconv(
tocode,
(*word).wd_charset,
(*word).wd_text,
strlen((*word).wd_text),
&mut wordutf8,
);
match r {
2 => {
mailmime_encoded_word_free(word);
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 13246848547199022064;
break;
}
1 => {
r = charconv(
tocode,
b"iso-8859-1\x00" as *const u8 as *const libc::c_char,
(*word).wd_text,
strlen((*word).wd_text),
&mut wordutf8,
)
}
3 => {
mailmime_encoded_word_free(word);
res = MAILIMF_ERROR_PARSE as libc::c_int;
current_block = 13246848547199022064;
break;
}
_ => {}
}
match r {
2 => {
mailmime_encoded_word_free(word);
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 13246848547199022064;
break;
}
3 => {
mailmime_encoded_word_free(word);
res = MAILIMF_ERROR_PARSE as libc::c_int;
current_block = 13246848547199022064;
break;
}
_ => {
if !wordutf8.is_null() {
if mmap_string_append(gphrase, wordutf8).is_null() {
mailmime_encoded_word_free(word);
free(wordutf8 as *mut libc::c_void);
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 13246848547199022064;
break;
} else {
free(wordutf8 as *mut libc::c_void);
}
}
mailmime_encoded_word_free(word);
first = 0i32
}
}
} else if !(r == MAILIMF_ERROR_PARSE as libc::c_int) {
/* do nothing */
res = r;
current_block = 13246848547199022064;
break;
}
if !(r == MAILIMF_ERROR_PARSE as libc::c_int) {
continue;
}
let mut raw_word: *mut libc::c_char = 0 as *mut libc::c_char;
raw_word = 0 as *mut libc::c_char;
r = mailmime_non_encoded_word_parse(
message,
length,
&mut cur_token,
&mut raw_word,
&mut has_fwd,
);
if r == MAILIMF_NO_ERROR as libc::c_int {
if 0 == first && 0 != has_fwd {
if mmap_string_append_c(gphrase, ' ' as i32 as libc::c_char).is_null() {
free(raw_word as *mut libc::c_void);
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 13246848547199022064;
break;
}
}
type_0 = TYPE_WORD as libc::c_int;
wordutf8 = 0 as *mut libc::c_char;
r = charconv(
tocode,
default_fromcode,
raw_word,
strlen(raw_word),
&mut wordutf8,
);
match r {
2 => {
free(raw_word as *mut libc::c_void);
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 13246848547199022064;
break;
}
1 | 3 => {
free(raw_word as *mut libc::c_void);
res = MAILIMF_ERROR_PARSE as libc::c_int;
current_block = 13246848547199022064;
break;
}
_ => {
if mmap_string_append(gphrase, wordutf8).is_null() {
free(wordutf8 as *mut libc::c_void);
free(raw_word as *mut libc::c_void);
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 13246848547199022064;
break;
} else {
free(wordutf8 as *mut libc::c_void);
free(raw_word as *mut libc::c_void);
first = 0i32
}
}
}
} else if r == MAILIMF_ERROR_PARSE as libc::c_int {
r = mailimf_fws_parse(message, length, &mut cur_token);
if r != MAILIMF_NO_ERROR as libc::c_int {
current_block = 5005389895767293342;
break;
}
if mmap_string_append_c(gphrase, ' ' as i32 as libc::c_char).is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 13246848547199022064;
break;
} else {
first = 0i32;
current_block = 5005389895767293342;
break;
}
} else {
res = r;
current_block = 13246848547199022064;
break;
}
}
match current_block {
5005389895767293342 => {
if 0 != first {
if cur_token != length {
res = MAILIMF_ERROR_PARSE as libc::c_int;
current_block = 13246848547199022064;
} else {
current_block = 7072655752890836508;
}
} else {
current_block = 7072655752890836508;
}
match current_block {
13246848547199022064 => {}
_ => {
str = strdup((*gphrase).str_0);
if str.is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int
} else {
mmap_string_free(gphrase);
*result = str;
*indx = cur_token;
return MAILIMF_NO_ERROR as libc::c_int;
}
}
}
}
_ => {}
}
mmap_string_free(gphrase);
}
return res;
}
unsafe fn mailmime_non_encoded_word_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut *mut libc::c_char,
mut p_has_fwd: *mut libc::c_int,
) -> libc::c_int {
let mut end: libc::c_int = 0;
let mut cur_token: size_t = 0;
let mut res: libc::c_int = 0;
let mut text: *mut libc::c_char = 0 as *mut libc::c_char;
let mut r: libc::c_int = 0;
let mut begin: size_t = 0;
let mut state: libc::c_int = 0;
let mut has_fwd: libc::c_int = 0;
cur_token = *indx;
has_fwd = 0i32;
r = mailimf_fws_parse(message, length, &mut cur_token);
if r == MAILIMF_NO_ERROR as libc::c_int {
has_fwd = 1i32
}
if r != MAILIMF_NO_ERROR as libc::c_int && r != MAILIMF_ERROR_PARSE as libc::c_int {
res = r
} else {
begin = cur_token;
state = 0i32;
end = 0i32;
while !(cur_token >= length) {
let mut current_block_17: u64;
match *message.offset(cur_token as isize) as libc::c_int {
32 | 9 | 13 | 10 => {
state = 0i32;
end = 1i32;
current_block_17 = 16924917904204750491;
}
61 => {
state = 1i32;
current_block_17 = 16924917904204750491;
}
63 => {
if state == 1i32 {
cur_token = cur_token.wrapping_sub(1);
end = 1i32
}
current_block_17 = 10192508258555769664;
}
_ => {
current_block_17 = 10192508258555769664;
}
}
match current_block_17 {
10192508258555769664 => state = 0i32,
_ => {}
}
if 0 != end {
break;
}
cur_token = cur_token.wrapping_add(1)
}
if cur_token.wrapping_sub(begin) == 0i32 as libc::size_t {
res = MAILIMF_ERROR_PARSE as libc::c_int
} else {
text = malloc(
cur_token
.wrapping_sub(begin)
.wrapping_add(1i32 as libc::size_t),
) as *mut libc::c_char;
if text.is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int
} else {
memcpy(
text as *mut libc::c_void,
message.offset(begin as isize) as *const libc::c_void,
cur_token.wrapping_sub(begin),
);
*text.offset(cur_token.wrapping_sub(begin) as isize) =
'\u{0}' as i32 as libc::c_char;
*indx = cur_token;
*result = text;
*p_has_fwd = has_fwd;
return MAILIMF_NO_ERROR as libc::c_int;
}
}
}
return res;
}
pub unsafe fn mailmime_encoded_word_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut *mut mailmime_encoded_word,
mut p_has_fwd: *mut libc::c_int,
mut p_missing_closing_quote: *mut libc::c_int,
) -> libc::c_int {
let mut current_block: u64;
/*
Parse the following, when a unicode character encoding is split.
=?UTF-8?B?4Lij4Liw4LmA4Lia4Li04LiU4LiE4Lin4Liy4Lih4Lih4Lix4LiZ4Liq4LmM?=
=?UTF-8?B?4LmA4LiV4LmH4Lih4Lie4Li04LiB4Lix4LiUIFRSQU5TRk9STUVSUyA0IOC4?=
=?UTF-8?B?oeC4seC4meC4quC5jOC4hOC4o+C4muC4l+C4uOC4geC4o+C4sOC4muC4miDg?=
=?UTF-8?B?uJfguLXguYjguYDguJTguLXguKLguKfguYPguJnguYDguKHguLfguK3guIfg?=
=?UTF-8?B?uYTguJfguKI=?=
Expected result:
ระเบิดความมันส์เต็มพิกัด TRANSFORMERS 4 มันส์ครบทุกระบบ ที่เดียวในเมืองไทย
libetpan result:
ระเบิดความมันส์เต็มพิกัด TRANSFORMERS 4 ?ันส์ครบทุกระบบ ??ี่เดียวในเมือง??ทย
See https://github.com/dinhviethoa/libetpan/pull/211
*/
let mut cur_token: size_t = 0;
let mut charset: *mut libc::c_char = 0 as *mut libc::c_char;
let mut encoding: libc::c_int = 0;
let mut body: *mut libc::c_char = 0 as *mut libc::c_char;
let mut old_body_len: size_t = 0;
let mut text: *mut libc::c_char = 0 as *mut libc::c_char;
let mut end_encoding: size_t = 0;
let mut lookfwd_cur_token: size_t = 0;
let mut lookfwd_charset: *mut libc::c_char = 0 as *mut libc::c_char;
let mut lookfwd_encoding: libc::c_int = 0;
let mut copy_len: size_t = 0;
let mut decoded_token: size_t = 0;
let mut decoded: *mut libc::c_char = 0 as *mut libc::c_char;
let mut decoded_len: size_t = 0;
let mut ew: *mut mailmime_encoded_word = 0 as *mut mailmime_encoded_word;
let mut r: libc::c_int = 0;
let mut res: libc::c_int = 0;
let mut opening_quote: libc::c_int = 0;
let mut end: libc::c_int = 0;
let mut has_fwd: libc::c_int = 0;
let mut missing_closing_quote: libc::c_int = 0;
cur_token = *indx;
text = 0 as *mut libc::c_char;
lookfwd_charset = 0 as *mut libc::c_char;
missing_closing_quote = 0i32;
has_fwd = 0i32;
r = mailimf_fws_parse(message, length, &mut cur_token);
if r == MAILIMF_NO_ERROR as libc::c_int {
has_fwd = 1i32
}
if r != MAILIMF_NO_ERROR as libc::c_int && r != MAILIMF_ERROR_PARSE as libc::c_int {
res = r
} else {
opening_quote = 0i32;
r = mailimf_char_parse(message, length, &mut cur_token, '\"' as i32 as libc::c_char);
if r == MAILIMF_NO_ERROR as libc::c_int {
opening_quote = 1i32;
current_block = 17788412896529399552;
} else if r == MAILIMF_ERROR_PARSE as libc::c_int {
current_block = 17788412896529399552;
} else {
/* do nothing */
res = r;
current_block = 7995813543095296079;
}
match current_block {
7995813543095296079 => {}
_ => {
r = mailimf_token_case_insensitive_len_parse(
message,
length,
&mut cur_token,
b"=?\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
strlen(b"=?\x00" as *const u8 as *const libc::c_char),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
res = r
} else {
r = mailmime_charset_parse(message, length, &mut cur_token, &mut charset);
if r != MAILIMF_NO_ERROR as libc::c_int {
res = r
} else {
r = mailimf_char_parse(
message,
length,
&mut cur_token,
'?' as i32 as libc::c_char,
);
if r != MAILIMF_NO_ERROR as libc::c_int {
res = r
} else {
r = mailmime_encoding_parse(
message,
length,
&mut cur_token,
&mut encoding,
);
if r != MAILIMF_NO_ERROR as libc::c_int {
res = r
} else {
r = mailimf_char_parse(
message,
length,
&mut cur_token,
'?' as i32 as libc::c_char,
);
if r != MAILIMF_NO_ERROR as libc::c_int {
res = r
} else {
lookfwd_cur_token = cur_token;
body = 0 as *mut libc::c_char;
old_body_len = 0i32 as size_t;
loop {
let mut has_base64_padding: libc::c_int = 0;
end = 0i32;
has_base64_padding = 0i32;
end_encoding = cur_token;
while !(end_encoding >= length) {
if end_encoding.wrapping_add(1i32 as libc::size_t)
< length
{
if *message.offset(end_encoding as isize)
as libc::c_int
== '?' as i32
&& *message.offset(
end_encoding
.wrapping_add(1i32 as libc::size_t)
as isize,
)
as libc::c_int
== '=' as i32
{
end = 1i32
}
}
if 0 != end {
break;
}
end_encoding = end_encoding.wrapping_add(1)
}
copy_len = end_encoding.wrapping_sub(lookfwd_cur_token);
if copy_len > 0i32 as libc::size_t {
if encoding == MAILMIME_ENCODING_B as libc::c_int {
if end_encoding >= 1i32 as libc::size_t {
if *message.offset(
end_encoding
.wrapping_sub(1i32 as libc::size_t)
as isize,
)
as libc::c_int
== '=' as i32
{
has_base64_padding = 1i32
}
}
}
body = realloc(
body as *mut libc::c_void,
old_body_len
.wrapping_add(copy_len)
.wrapping_add(1i32 as libc::size_t),
)
as *mut libc::c_char;
if body.is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 13900684162107791171;
break;
} else {
memcpy(
body.offset(old_body_len as isize)
as *mut libc::c_void,
&*message.offset(cur_token as isize)
as *const libc::c_char
as *const libc::c_void,
copy_len,
);
*body
.offset(old_body_len.wrapping_add(copy_len)
as isize) = '\u{0}' as i32 as libc::c_char;
old_body_len = (old_body_len as libc::size_t)
.wrapping_add(copy_len)
as size_t
as size_t
}
}
cur_token = end_encoding;
r = mailimf_token_case_insensitive_len_parse(
message,
length,
&mut cur_token,
b"?=\x00" as *const u8 as *const libc::c_char
as *mut libc::c_char,
strlen(b"?=\x00" as *const u8 as *const libc::c_char),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
current_block = 2652804691515851435;
break;
}
if 0 != has_base64_padding {
current_block = 2652804691515851435;
break;
}
lookfwd_cur_token = cur_token;
r = mailimf_fws_parse(
message,
length,
&mut lookfwd_cur_token,
);
if r != MAILIMF_NO_ERROR as libc::c_int
&& r != MAILIMF_ERROR_PARSE as libc::c_int
{
current_block = 2652804691515851435;
break;
}
r = mailimf_token_case_insensitive_len_parse(
message,
length,
&mut lookfwd_cur_token,
b"=?\x00" as *const u8 as *const libc::c_char
as *mut libc::c_char,
strlen(b"=?\x00" as *const u8 as *const libc::c_char),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
current_block = 2652804691515851435;
break;
}
r = mailmime_charset_parse(
message,
length,
&mut lookfwd_cur_token,
&mut lookfwd_charset,
);
if r != MAILIMF_NO_ERROR as libc::c_int {
current_block = 2652804691515851435;
break;
}
r = mailimf_char_parse(
message,
length,
&mut lookfwd_cur_token,
'?' as i32 as libc::c_char,
);
if r != MAILIMF_NO_ERROR as libc::c_int {
current_block = 2652804691515851435;
break;
}
r = mailmime_encoding_parse(
message,
length,
&mut lookfwd_cur_token,
&mut lookfwd_encoding,
);
if r != MAILIMF_NO_ERROR as libc::c_int {
current_block = 2652804691515851435;
break;
}
r = mailimf_char_parse(
message,
length,
&mut lookfwd_cur_token,
'?' as i32 as libc::c_char,
);
if r != MAILIMF_NO_ERROR as libc::c_int {
current_block = 2652804691515851435;
break;
}
if strcasecmp(charset, lookfwd_charset) == 0i32
&& encoding == lookfwd_encoding
{
cur_token = lookfwd_cur_token;
mailmime_charset_free(lookfwd_charset);
lookfwd_charset = 0 as *mut libc::c_char
} else {
/* the next charset is not matched with the current one,
therefore exit the loop to decode the body appended so far */
current_block = 2652804691515851435;
break;
}
}
match current_block {
2652804691515851435 => {
if !lookfwd_charset.is_null() {
mailmime_charset_free(lookfwd_charset);
lookfwd_charset = 0 as *mut libc::c_char
}
if body.is_null() {
body = strdup(
b"\x00" as *const u8 as *const libc::c_char,
);
if body.is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 13900684162107791171;
} else {
current_block = 16778110326724371720;
}
} else {
current_block = 16778110326724371720;
}
match current_block {
13900684162107791171 => {}
_ => {
decoded_token = 0i32 as size_t;
decoded_len = 0i32 as size_t;
decoded = 0 as *mut libc::c_char;
match encoding {
0 => {
r = mailmime_base64_body_parse(
body,
strlen(body),
&mut decoded_token,
&mut decoded,
&mut decoded_len,
);
if r != MAILIMF_NO_ERROR as libc::c_int
{
res = r;
current_block =
13900684162107791171;
} else {
current_block = 7337917895049117968;
}
}
1 => {
r =
mailmime_quoted_printable_body_parse(body,
strlen(body),
&mut decoded_token,
&mut decoded,
&mut decoded_len,
1i32);
if r != MAILIMF_NO_ERROR as libc::c_int
{
res = r;
current_block =
13900684162107791171;
} else {
current_block = 7337917895049117968;
}
}
_ => {
current_block = 7337917895049117968;
}
}
match current_block {
13900684162107791171 => {}
_ => {
text =
malloc(decoded_len.wrapping_add(
1i32 as libc::size_t,
))
as *mut libc::c_char;
if text.is_null() {
res = MAILIMF_ERROR_MEMORY
as libc::c_int
} else {
if decoded_len
> 0i32 as libc::size_t
{
memcpy(
text as *mut libc::c_void,
decoded
as *const libc::c_void,
decoded_len,
);
}
*text
.offset(decoded_len as isize) =
'\u{0}' as i32 as libc::c_char;
if 0 != opening_quote {
r = mailimf_char_parse(
message,
length,
&mut cur_token,
'\"' as i32 as libc::c_char,
);
if r == MAILIMF_ERROR_PARSE
as libc::c_int
{
missing_closing_quote = 1i32
}
}
if strcasecmp(
charset,
b"utf8\x00" as *const u8
as *const libc::c_char,
) == 0i32
{
free(
charset
as *mut libc::c_void,
);
charset = strdup(
b"utf-8\x00" as *const u8
as *const libc::c_char,
)
}
ew = mailmime_encoded_word_new(
charset, text,
);
if ew.is_null() {
res = MAILIMF_ERROR_MEMORY
as libc::c_int
} else {
*result = ew;
*indx = cur_token;
*p_has_fwd = has_fwd;
*p_missing_closing_quote =
missing_closing_quote;
mailmime_decoded_part_free(
decoded,
);
free(body as *mut libc::c_void);
return MAILIMF_NO_ERROR
as libc::c_int;
}
}
mailmime_decoded_part_free(decoded);
}
}
}
}
}
_ => {}
}
free(body as *mut libc::c_void);
mailmime_encoded_text_free(text);
}
}
}
mailmime_charset_free(charset);
}
}
}
}
}
return res;
}
unsafe fn mailmime_encoding_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut libc::c_int,
) -> libc::c_int {
let mut cur_token: size_t = 0;
let mut encoding: libc::c_int = 0;
cur_token = *indx;
if cur_token >= length {
return MAILIMF_ERROR_PARSE as libc::c_int;
}
match toupper(*message.offset(cur_token as isize) as libc::c_uchar as libc::c_int)
as libc::c_char as libc::c_int
{
81 => encoding = MAILMIME_ENCODING_Q as libc::c_int,
66 => encoding = MAILMIME_ENCODING_B as libc::c_int,
_ => return MAILIMF_ERROR_INVAL as libc::c_int,
}
cur_token = cur_token.wrapping_add(1);
*result = encoding;
*indx = cur_token;
return MAILIMF_NO_ERROR as libc::c_int;
}
/*
* libEtPan! -- a mail stuff library
*
* Copyright (C) 2001, 2005 - DINH Viet Hoa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the libEtPan! project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* $Id: mailmime_decode.c,v 1.37 2010/11/16 20:52:28 hoa Exp $
*/
/*
RFC 2047 : MIME (Multipurpose Internet Mail Extensions) Part Three:
Message Header Extensions for Non-ASCII Text
*/
unsafe fn mailmime_charset_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut charset: *mut *mut libc::c_char,
) -> libc::c_int {
return mailmime_etoken_parse(message, length, indx, charset);
}
unsafe fn mailmime_etoken_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut *mut libc::c_char,
) -> libc::c_int {
return mailimf_custom_string_parse(message, length, indx, result, Some(is_etoken_char));
}
unsafe fn is_etoken_char(mut ch: libc::c_char) -> libc::c_int {
let mut uch: libc::c_uchar = ch as libc::c_uchar;
if (uch as libc::c_int) < 31i32 {
return 0i32;
}
match uch as libc::c_int {
32 | 40 | 41 | 60 | 62 | 64 | 44 | 59 | 58 | 34 | 47 | 91 | 93 | 63 | 61 => return 0i32,
_ => {}
}
return 1i32;
}

View File

@@ -1,583 +0,0 @@
use libc::{self, toupper};
use crate::clist::*;
use crate::mailimf::*;
use crate::mailmime::types::*;
use crate::mailmime::*;
use crate::other::*;
pub const MAILMIME_DISPOSITION_TYPE_EXTENSION: libc::c_uint = 3;
pub const MAILMIME_DISPOSITION_TYPE_ATTACHMENT: libc::c_uint = 2;
pub const MAILMIME_DISPOSITION_TYPE_INLINE: libc::c_uint = 1;
pub const MAILMIME_DISPOSITION_TYPE_ERROR: libc::c_uint = 0;
pub unsafe fn mailmime_disposition_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut *mut mailmime_disposition,
) -> libc::c_int {
let mut current_block: u64;
let mut final_token: size_t = 0;
let mut cur_token: size_t = 0;
let mut dsp_type: *mut mailmime_disposition_type = 0 as *mut mailmime_disposition_type;
let mut list: *mut clist = 0 as *mut clist;
let mut dsp: *mut mailmime_disposition = 0 as *mut mailmime_disposition;
let mut r: libc::c_int = 0;
let mut res: libc::c_int = 0;
cur_token = *indx;
r = mailmime_disposition_type_parse(message, length, &mut cur_token, &mut dsp_type);
if r != MAILIMF_NO_ERROR as libc::c_int {
res = r
} else {
list = clist_new();
if list.is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int
} else {
loop {
let mut param: *mut mailmime_disposition_parm = 0 as *mut mailmime_disposition_parm;
final_token = cur_token;
r = mailimf_unstrict_char_parse(
message,
length,
&mut cur_token,
';' as i32 as libc::c_char,
);
if r == MAILIMF_NO_ERROR as libc::c_int {
param = 0 as *mut mailmime_disposition_parm;
r = mailmime_disposition_parm_parse(
message,
length,
&mut cur_token,
&mut param,
);
if r == MAILIMF_NO_ERROR as libc::c_int {
r = clist_insert_after(list, (*list).last, param as *mut libc::c_void);
if !(r < 0i32) {
continue;
}
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 18290070879695007868;
break;
} else if r == MAILIMF_ERROR_PARSE as libc::c_int {
cur_token = final_token;
current_block = 652864300344834934;
break;
} else {
res = r;
current_block = 18290070879695007868;
break;
}
} else {
/* do nothing */
if r == MAILIMF_ERROR_PARSE as libc::c_int {
current_block = 652864300344834934;
break;
}
res = r;
current_block = 18290070879695007868;
break;
}
}
match current_block {
652864300344834934 => {
dsp = mailmime_disposition_new(dsp_type, list);
if dsp.is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int
} else {
*result = dsp;
*indx = cur_token;
return MAILIMF_NO_ERROR as libc::c_int;
}
}
_ => {}
}
clist_foreach(
list,
::std::mem::transmute::<
Option<unsafe fn(_: *mut mailmime_disposition_parm) -> ()>,
clist_func,
>(Some(mailmime_disposition_parm_free)),
0 as *mut libc::c_void,
);
clist_free(list);
}
mailmime_disposition_type_free(dsp_type);
}
return res;
}
/*
* libEtPan! -- a mail stuff library
*
* Copyright (C) 2001, 2005 - DINH Viet Hoa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the libEtPan! project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* $Id: mailmime_disposition.c,v 1.17 2011/05/03 16:30:22 hoa Exp $
*/
unsafe fn mailmime_disposition_parm_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut *mut mailmime_disposition_parm,
) -> libc::c_int {
let mut current_block: u64;
let mut filename: *mut libc::c_char = 0 as *mut libc::c_char;
let mut creation_date: *mut libc::c_char = 0 as *mut libc::c_char;
let mut modification_date: *mut libc::c_char = 0 as *mut libc::c_char;
let mut read_date: *mut libc::c_char = 0 as *mut libc::c_char;
let mut size: size_t = 0;
let mut parameter: *mut mailmime_parameter = 0 as *mut mailmime_parameter;
let mut cur_token: size_t = 0;
let mut dsp_parm: *mut mailmime_disposition_parm = 0 as *mut mailmime_disposition_parm;
let mut type_0: libc::c_int = 0;
let mut guessed_type: libc::c_int = 0;
let mut r: libc::c_int = 0;
let mut res: libc::c_int = 0;
cur_token = *indx;
filename = 0 as *mut libc::c_char;
creation_date = 0 as *mut libc::c_char;
modification_date = 0 as *mut libc::c_char;
read_date = 0 as *mut libc::c_char;
size = 0i32 as size_t;
parameter = 0 as *mut mailmime_parameter;
r = mailimf_cfws_parse(message, length, &mut cur_token);
if r != MAILIMF_NO_ERROR as libc::c_int && r != MAILIMF_ERROR_PARSE as libc::c_int {
res = r
} else {
guessed_type = mailmime_disposition_guess_type(message, length, cur_token);
type_0 = MAILMIME_DISPOSITION_PARM_PARAMETER as libc::c_int;
match guessed_type {
0 => {
r = mailmime_filename_parm_parse(message, length, &mut cur_token, &mut filename);
if r == MAILIMF_NO_ERROR as libc::c_int {
type_0 = guessed_type;
current_block = 13826291924415791078;
} else if r == MAILIMF_ERROR_PARSE as libc::c_int {
current_block = 13826291924415791078;
} else {
/* do nothing */
res = r;
current_block = 9120900589700563584;
}
}
1 => {
r = mailmime_creation_date_parm_parse(
message,
length,
&mut cur_token,
&mut creation_date,
);
if r == MAILIMF_NO_ERROR as libc::c_int {
type_0 = guessed_type;
current_block = 13826291924415791078;
} else if r == MAILIMF_ERROR_PARSE as libc::c_int {
current_block = 13826291924415791078;
} else {
/* do nothing */
res = r;
current_block = 9120900589700563584;
}
}
2 => {
r = mailmime_modification_date_parm_parse(
message,
length,
&mut cur_token,
&mut modification_date,
);
if r == MAILIMF_NO_ERROR as libc::c_int {
type_0 = guessed_type;
current_block = 13826291924415791078;
} else if r == MAILIMF_ERROR_PARSE as libc::c_int {
current_block = 13826291924415791078;
} else {
/* do nothing */
res = r;
current_block = 9120900589700563584;
}
}
3 => {
r = mailmime_read_date_parm_parse(message, length, &mut cur_token, &mut read_date);
if r == MAILIMF_NO_ERROR as libc::c_int {
type_0 = guessed_type;
current_block = 13826291924415791078;
} else if r == MAILIMF_ERROR_PARSE as libc::c_int {
current_block = 13826291924415791078;
} else {
/* do nothing */
res = r;
current_block = 9120900589700563584;
}
}
4 => {
r = mailmime_size_parm_parse(message, length, &mut cur_token, &mut size);
if r == MAILIMF_NO_ERROR as libc::c_int {
type_0 = guessed_type;
current_block = 13826291924415791078;
} else if r == MAILIMF_ERROR_PARSE as libc::c_int {
current_block = 13826291924415791078;
} else {
/* do nothing */
res = r;
current_block = 9120900589700563584;
}
}
_ => {
current_block = 13826291924415791078;
}
}
match current_block {
9120900589700563584 => {}
_ => {
if type_0 == MAILMIME_DISPOSITION_PARM_PARAMETER as libc::c_int {
r = mailmime_parameter_parse(message, length, &mut cur_token, &mut parameter);
if r != MAILIMF_NO_ERROR as libc::c_int {
type_0 = guessed_type;
res = r;
current_block = 9120900589700563584;
} else {
current_block = 6721012065216013753;
}
} else {
current_block = 6721012065216013753;
}
match current_block {
9120900589700563584 => {}
_ => {
dsp_parm = mailmime_disposition_parm_new(
type_0,
filename,
creation_date,
modification_date,
read_date,
size,
parameter,
);
if dsp_parm.is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int;
if !filename.is_null() {
mailmime_filename_parm_free(filename);
}
if !creation_date.is_null() {
mailmime_creation_date_parm_free(creation_date);
}
if !modification_date.is_null() {
mailmime_modification_date_parm_free(modification_date);
}
if !read_date.is_null() {
mailmime_read_date_parm_free(read_date);
}
if !parameter.is_null() {
mailmime_parameter_free(parameter);
}
} else {
*result = dsp_parm;
*indx = cur_token;
return MAILIMF_NO_ERROR as libc::c_int;
}
}
}
}
}
}
return res;
}
unsafe fn mailmime_size_parm_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut size_t,
) -> libc::c_int {
let mut value: uint32_t = 0;
let mut cur_token: size_t = 0;
let mut r: libc::c_int = 0;
cur_token = *indx;
r = mailimf_token_case_insensitive_len_parse(
message,
length,
&mut cur_token,
b"size\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
strlen(b"size\x00" as *const u8 as *const libc::c_char),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_unstrict_char_parse(message, length, &mut cur_token, '=' as i32 as libc::c_char);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_cfws_parse(message, length, &mut cur_token);
if r != MAILIMF_NO_ERROR as libc::c_int && r != MAILIMF_ERROR_PARSE as libc::c_int {
return r;
}
r = mailimf_number_parse(message, length, &mut cur_token, &mut value);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
*indx = cur_token;
*result = value as size_t;
return MAILIMF_NO_ERROR as libc::c_int;
}
unsafe fn mailmime_read_date_parm_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut *mut libc::c_char,
) -> libc::c_int {
let mut value: *mut libc::c_char = 0 as *mut libc::c_char;
let mut cur_token: size_t = 0;
let mut r: libc::c_int = 0;
cur_token = *indx;
r = mailimf_token_case_insensitive_len_parse(
message,
length,
&mut cur_token,
b"read-date\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
strlen(b"read-date\x00" as *const u8 as *const libc::c_char),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_unstrict_char_parse(message, length, &mut cur_token, '=' as i32 as libc::c_char);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_cfws_parse(message, length, &mut cur_token);
if r != MAILIMF_NO_ERROR as libc::c_int && r != MAILIMF_ERROR_PARSE as libc::c_int {
return r;
}
r = mailmime_quoted_date_time_parse(message, length, &mut cur_token, &mut value);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
*indx = cur_token;
*result = value;
return MAILIMF_NO_ERROR as libc::c_int;
}
unsafe fn mailmime_quoted_date_time_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut *mut libc::c_char,
) -> libc::c_int {
return mailimf_quoted_string_parse(message, length, indx, result);
}
unsafe fn mailmime_modification_date_parm_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut *mut libc::c_char,
) -> libc::c_int {
let mut value: *mut libc::c_char = 0 as *mut libc::c_char;
let mut cur_token: size_t = 0;
let mut r: libc::c_int = 0;
cur_token = *indx;
r = mailimf_token_case_insensitive_len_parse(
message,
length,
&mut cur_token,
b"modification-date\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
strlen(b"modification-date\x00" as *const u8 as *const libc::c_char),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_unstrict_char_parse(message, length, &mut cur_token, '=' as i32 as libc::c_char);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_cfws_parse(message, length, &mut cur_token);
if r != MAILIMF_NO_ERROR as libc::c_int && r != MAILIMF_ERROR_PARSE as libc::c_int {
return r;
}
r = mailmime_quoted_date_time_parse(message, length, &mut cur_token, &mut value);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
*indx = cur_token;
*result = value;
return MAILIMF_NO_ERROR as libc::c_int;
}
unsafe fn mailmime_creation_date_parm_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut *mut libc::c_char,
) -> libc::c_int {
let mut value: *mut libc::c_char = 0 as *mut libc::c_char;
let mut r: libc::c_int = 0;
let mut cur_token: size_t = 0;
cur_token = *indx;
r = mailimf_token_case_insensitive_len_parse(
message,
length,
&mut cur_token,
b"creation-date\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
strlen(b"creation-date\x00" as *const u8 as *const libc::c_char),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_unstrict_char_parse(message, length, &mut cur_token, '=' as i32 as libc::c_char);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_cfws_parse(message, length, &mut cur_token);
if r != MAILIMF_NO_ERROR as libc::c_int && r != MAILIMF_ERROR_PARSE as libc::c_int {
return r;
}
r = mailmime_quoted_date_time_parse(message, length, &mut cur_token, &mut value);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
*indx = cur_token;
*result = value;
return MAILIMF_NO_ERROR as libc::c_int;
}
unsafe fn mailmime_filename_parm_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut *mut libc::c_char,
) -> libc::c_int {
let mut value: *mut libc::c_char = 0 as *mut libc::c_char;
let mut r: libc::c_int = 0;
let mut cur_token: size_t = 0;
cur_token = *indx;
r = mailimf_token_case_insensitive_len_parse(
message,
length,
&mut cur_token,
b"filename\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
strlen(b"filename\x00" as *const u8 as *const libc::c_char),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_unstrict_char_parse(message, length, &mut cur_token, '=' as i32 as libc::c_char);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_cfws_parse(message, length, &mut cur_token);
if r != MAILIMF_NO_ERROR as libc::c_int && r != MAILIMF_ERROR_PARSE as libc::c_int {
return r;
}
r = mailmime_value_parse(message, length, &mut cur_token, &mut value);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
*indx = cur_token;
*result = value;
return MAILIMF_NO_ERROR as libc::c_int;
}
pub unsafe fn mailmime_disposition_guess_type(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: size_t,
) -> libc::c_int {
if indx >= length {
return MAILMIME_DISPOSITION_PARM_PARAMETER as libc::c_int;
}
match toupper(*message.offset(indx as isize) as libc::c_uchar as libc::c_int) as libc::c_char
as libc::c_int
{
70 => return MAILMIME_DISPOSITION_PARM_FILENAME as libc::c_int,
67 => return MAILMIME_DISPOSITION_PARM_CREATION_DATE as libc::c_int,
77 => return MAILMIME_DISPOSITION_PARM_MODIFICATION_DATE as libc::c_int,
82 => return MAILMIME_DISPOSITION_PARM_READ_DATE as libc::c_int,
83 => return MAILMIME_DISPOSITION_PARM_SIZE as libc::c_int,
_ => return MAILMIME_DISPOSITION_PARM_PARAMETER as libc::c_int,
};
}
pub unsafe fn mailmime_disposition_type_parse(
mut message: *const libc::c_char,
mut length: size_t,
mut indx: *mut size_t,
mut result: *mut *mut mailmime_disposition_type,
) -> libc::c_int {
let mut cur_token: size_t = 0;
let mut type_0: libc::c_int = 0;
let mut extension: *mut libc::c_char = 0 as *mut libc::c_char;
let mut dsp_type: *mut mailmime_disposition_type = 0 as *mut mailmime_disposition_type;
let mut r: libc::c_int = 0;
let mut res: libc::c_int = 0;
cur_token = *indx;
r = mailimf_cfws_parse(message, length, &mut cur_token);
if r != MAILIMF_NO_ERROR as libc::c_int && r != MAILIMF_ERROR_PARSE as libc::c_int {
res = r
} else {
type_0 = MAILMIME_DISPOSITION_TYPE_ERROR as libc::c_int;
extension = 0 as *mut libc::c_char;
r = mailimf_token_case_insensitive_len_parse(
message,
length,
&mut cur_token,
b"inline\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
strlen(b"inline\x00" as *const u8 as *const libc::c_char),
);
if r == MAILIMF_NO_ERROR as libc::c_int {
type_0 = MAILMIME_DISPOSITION_TYPE_INLINE as libc::c_int
}
if r == MAILIMF_ERROR_PARSE as libc::c_int {
r = mailimf_token_case_insensitive_len_parse(
message,
length,
&mut cur_token,
b"attachment\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
strlen(b"attachment\x00" as *const u8 as *const libc::c_char),
);
if r == MAILIMF_NO_ERROR as libc::c_int {
type_0 = MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int
}
}
if r == MAILIMF_ERROR_PARSE as libc::c_int {
r = mailmime_extension_token_parse(message, length, &mut cur_token, &mut extension);
if r == MAILIMF_NO_ERROR as libc::c_int {
type_0 = MAILMIME_DISPOSITION_TYPE_EXTENSION as libc::c_int
}
}
if r != MAILIMF_NO_ERROR as libc::c_int {
res = r
} else {
dsp_type = mailmime_disposition_type_new(type_0, extension);
if dsp_type.is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int;
if !extension.is_null() {
free(extension as *mut libc::c_void);
}
} else {
*result = dsp_type;
*indx = cur_token;
return MAILIMF_NO_ERROR as libc::c_int;
}
}
}
return res;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,891 +0,0 @@
use crate::clist::*;
use crate::mailimf::types::*;
use crate::mmapstring::*;
use crate::other::*;
pub const MAILMIME_MECHANISM_TOKEN: libc::c_uint = 6;
pub const MAILMIME_MECHANISM_BASE64: libc::c_uint = 5;
pub const MAILMIME_MECHANISM_QUOTED_PRINTABLE: libc::c_uint = 4;
pub const MAILMIME_MECHANISM_BINARY: libc::c_uint = 3;
pub const MAILMIME_MECHANISM_8BIT: libc::c_uint = 2;
pub const MAILMIME_MECHANISM_7BIT: libc::c_uint = 1;
pub const MAILMIME_MECHANISM_ERROR: libc::c_uint = 0;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_composite_type {
pub ct_type: libc::c_int,
pub ct_token: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_content {
pub ct_type: *mut mailmime_type,
pub ct_subtype: *mut libc::c_char,
pub ct_parameters: *mut clist,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_type {
pub tp_type: libc::c_int,
pub tp_data: unnamed,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub union unnamed {
pub tp_discrete_type: *mut mailmime_discrete_type,
pub tp_composite_type: *mut mailmime_composite_type,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_discrete_type {
pub dt_type: libc::c_int,
pub dt_extension: *mut libc::c_char,
}
pub type unnamed_0 = libc::c_uint;
pub const MAILMIME_FIELD_LOCATION: unnamed_0 = 8;
pub const MAILMIME_FIELD_LANGUAGE: unnamed_0 = 7;
pub const MAILMIME_FIELD_DISPOSITION: unnamed_0 = 6;
pub const MAILMIME_FIELD_VERSION: unnamed_0 = 5;
pub const MAILMIME_FIELD_DESCRIPTION: unnamed_0 = 4;
pub const MAILMIME_FIELD_ID: unnamed_0 = 3;
pub const MAILMIME_FIELD_TRANSFER_ENCODING: unnamed_0 = 2;
pub const MAILMIME_FIELD_TYPE: unnamed_0 = 1;
pub const MAILMIME_FIELD_NONE: unnamed_0 = 0;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_field {
pub fld_type: libc::c_int,
pub fld_data: unnamed_1,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub union unnamed_1 {
pub fld_content: *mut mailmime_content,
pub fld_encoding: *mut mailmime_mechanism,
pub fld_id: *mut libc::c_char,
pub fld_description: *mut libc::c_char,
pub fld_version: uint32_t,
pub fld_disposition: *mut mailmime_disposition,
pub fld_language: *mut mailmime_language,
pub fld_location: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_language {
pub lg_list: *mut clist,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_disposition {
pub dsp_type: *mut mailmime_disposition_type,
pub dsp_parms: *mut clist,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_disposition_type {
pub dsp_type: libc::c_int,
pub dsp_extension: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_mechanism {
pub enc_type: libc::c_int,
pub enc_token: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_fields {
pub fld_list: *mut clist,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_parameter {
pub pa_name: *mut libc::c_char,
pub pa_value: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_disposition_parm {
pub pa_type: libc::c_int,
pub pa_data: unnamed_3,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub union unnamed_3 {
pub pa_filename: *mut libc::c_char,
pub pa_creation_date: *mut libc::c_char,
pub pa_modification_date: *mut libc::c_char,
pub pa_read_date: *mut libc::c_char,
pub pa_size: size_t,
pub pa_parameter: *mut mailmime_parameter,
}
pub const MAILMIME_DISPOSITION_PARM_PARAMETER: unnamed_11 = 5;
pub const MAILMIME_DISPOSITION_PARM_READ_DATE: unnamed_11 = 3;
pub const MAILMIME_DISPOSITION_PARM_MODIFICATION_DATE: unnamed_11 = 2;
pub const MAILMIME_DISPOSITION_PARM_CREATION_DATE: unnamed_11 = 1;
pub const MAILMIME_DISPOSITION_PARM_FILENAME: unnamed_11 = 0;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_multipart_body {
pub bd_list: *mut clist,
}
pub type unnamed_4 = libc::c_uint;
pub const MAILMIME_DATA_FILE: unnamed_4 = 1;
pub const MAILMIME_DATA_TEXT: unnamed_4 = 0;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_data {
pub dt_type: libc::c_int,
pub dt_encoding: libc::c_int,
pub dt_encoded: libc::c_int,
pub dt_data: unnamed_5,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub union unnamed_5 {
pub dt_text: unnamed_6,
pub dt_filename: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct unnamed_6 {
pub dt_data: *const libc::c_char,
pub dt_length: size_t,
}
pub type unnamed_7 = libc::c_uint;
pub const MAILMIME_MESSAGE: unnamed_7 = 3;
pub const MAILMIME_MULTIPLE: unnamed_7 = 2;
pub const MAILMIME_SINGLE: unnamed_7 = 1;
pub const MAILMIME_NONE: unnamed_7 = 0;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Mailmime {
pub mm_parent_type: libc::c_int,
pub mm_parent: *mut Mailmime,
pub mm_multipart_pos: *mut clistiter,
pub mm_type: libc::c_int,
pub mm_mime_start: *const libc::c_char,
pub mm_length: size_t,
pub mm_mime_fields: *mut mailmime_fields,
pub mm_content_type: *mut mailmime_content,
pub mm_body: *mut mailmime_data,
pub mm_data: unnamed_8,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub union unnamed_8 {
pub mm_single: *mut mailmime_data,
pub mm_multipart: unnamed_10,
pub mm_message: unnamed_9,
}
/* message */
#[derive(Copy, Clone)]
#[repr(C)]
pub struct unnamed_9 {
pub mm_fields: *mut mailimf_fields,
pub mm_msg_mime: *mut Mailmime,
}
/* multi-part */
#[derive(Copy, Clone)]
#[repr(C)]
pub struct unnamed_10 {
pub mm_preamble: *mut mailmime_data,
pub mm_epilogue: *mut mailmime_data,
pub mm_mp_list: *mut clist,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_encoded_word {
pub wd_charset: *mut libc::c_char,
pub wd_text: *mut libc::c_char,
}
pub type unnamed_11 = libc::c_uint;
pub const MAILMIME_DISPOSITION_PARM_SIZE: unnamed_11 = 4;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct mailmime_section {
pub sec_list: *mut clist,
}
pub unsafe fn mailmime_attribute_free(mut attribute: *mut libc::c_char) {
mailmime_token_free(attribute);
}
pub unsafe fn mailmime_token_free(mut token: *mut libc::c_char) {
free(token as *mut libc::c_void);
}
pub unsafe fn mailmime_composite_type_new(
mut ct_type: libc::c_int,
mut ct_token: *mut libc::c_char,
) -> *mut mailmime_composite_type {
let mut ct: *mut mailmime_composite_type = 0 as *mut mailmime_composite_type;
ct = malloc(::std::mem::size_of::<mailmime_composite_type>() as libc::size_t)
as *mut mailmime_composite_type;
if ct.is_null() {
return 0 as *mut mailmime_composite_type;
}
(*ct).ct_type = ct_type;
(*ct).ct_token = ct_token;
return ct;
}
pub unsafe fn mailmime_composite_type_free(mut ct: *mut mailmime_composite_type) {
if !(*ct).ct_token.is_null() {
mailmime_extension_token_free((*ct).ct_token);
}
free(ct as *mut libc::c_void);
}
pub unsafe fn mailmime_extension_token_free(mut extension: *mut libc::c_char) {
mailmime_token_free(extension);
}
pub unsafe fn mailmime_content_new(
mut ct_type: *mut mailmime_type,
mut ct_subtype: *mut libc::c_char,
mut ct_parameters: *mut clist,
) -> *mut mailmime_content {
let mut content: *mut mailmime_content = 0 as *mut mailmime_content;
content =
malloc(::std::mem::size_of::<mailmime_content>() as libc::size_t) as *mut mailmime_content;
if content.is_null() {
return 0 as *mut mailmime_content;
}
(*content).ct_type = ct_type;
(*content).ct_subtype = ct_subtype;
(*content).ct_parameters = ct_parameters;
return content;
}
pub unsafe fn mailmime_content_free(mut content: *mut mailmime_content) {
mailmime_type_free((*content).ct_type);
mailmime_subtype_free((*content).ct_subtype);
if !(*content).ct_parameters.is_null() {
clist_foreach(
(*content).ct_parameters,
::std::mem::transmute::<Option<unsafe fn(_: *mut mailmime_parameter) -> ()>, clist_func>(
Some(mailmime_parameter_free),
),
0 as *mut libc::c_void,
);
clist_free((*content).ct_parameters);
}
free(content as *mut libc::c_void);
}
pub unsafe fn mailmime_parameter_free(mut parameter: *mut mailmime_parameter) {
mailmime_attribute_free((*parameter).pa_name);
mailmime_value_free((*parameter).pa_value);
free(parameter as *mut libc::c_void);
}
pub unsafe fn mailmime_value_free(mut value: *mut libc::c_char) {
free(value as *mut libc::c_void);
}
pub unsafe fn mailmime_subtype_free(mut subtype: *mut libc::c_char) {
mailmime_extension_token_free(subtype);
}
pub unsafe fn mailmime_type_free(mut type_0: *mut mailmime_type) {
match (*type_0).tp_type {
1 => {
mailmime_discrete_type_free((*type_0).tp_data.tp_discrete_type);
}
2 => {
mailmime_composite_type_free((*type_0).tp_data.tp_composite_type);
}
_ => {}
}
free(type_0 as *mut libc::c_void);
}
pub unsafe fn mailmime_discrete_type_free(mut discrete_type: *mut mailmime_discrete_type) {
if !(*discrete_type).dt_extension.is_null() {
mailmime_extension_token_free((*discrete_type).dt_extension);
}
free(discrete_type as *mut libc::c_void);
}
pub unsafe fn mailmime_description_free(mut description: *mut libc::c_char) {
free(description as *mut libc::c_void);
}
pub unsafe fn mailmime_location_free(mut location: *mut libc::c_char) {
free(location as *mut libc::c_void);
}
pub unsafe fn mailmime_discrete_type_new(
mut dt_type: libc::c_int,
mut dt_extension: *mut libc::c_char,
) -> *mut mailmime_discrete_type {
let mut discrete_type: *mut mailmime_discrete_type = 0 as *mut mailmime_discrete_type;
discrete_type = malloc(::std::mem::size_of::<mailmime_discrete_type>() as libc::size_t)
as *mut mailmime_discrete_type;
if discrete_type.is_null() {
return 0 as *mut mailmime_discrete_type;
}
(*discrete_type).dt_type = dt_type;
(*discrete_type).dt_extension = dt_extension;
return discrete_type;
}
pub unsafe fn mailmime_encoding_free(mut encoding: *mut mailmime_mechanism) {
mailmime_mechanism_free(encoding);
}
pub unsafe fn mailmime_mechanism_free(mut mechanism: *mut mailmime_mechanism) {
if !(*mechanism).enc_token.is_null() {
mailmime_token_free((*mechanism).enc_token);
}
free(mechanism as *mut libc::c_void);
}
pub unsafe fn mailmime_id_free(mut id: *mut libc::c_char) {
mailimf_msg_id_free(id);
}
pub unsafe fn mailmime_mechanism_new(
mut enc_type: libc::c_int,
mut enc_token: *mut libc::c_char,
) -> *mut mailmime_mechanism {
let mut mechanism: *mut mailmime_mechanism = 0 as *mut mailmime_mechanism;
mechanism = malloc(::std::mem::size_of::<mailmime_mechanism>() as libc::size_t)
as *mut mailmime_mechanism;
if mechanism.is_null() {
return 0 as *mut mailmime_mechanism;
}
(*mechanism).enc_type = enc_type;
(*mechanism).enc_token = enc_token;
return mechanism;
}
pub unsafe fn mailmime_parameter_new(
mut pa_name: *mut libc::c_char,
mut pa_value: *mut libc::c_char,
) -> *mut mailmime_parameter {
let mut parameter: *mut mailmime_parameter = 0 as *mut mailmime_parameter;
parameter = malloc(::std::mem::size_of::<mailmime_parameter>() as libc::size_t)
as *mut mailmime_parameter;
if parameter.is_null() {
return 0 as *mut mailmime_parameter;
}
(*parameter).pa_name = pa_name;
(*parameter).pa_value = pa_value;
return parameter;
}
pub unsafe fn mailmime_type_new(
mut tp_type: libc::c_int,
mut tp_discrete_type: *mut mailmime_discrete_type,
mut tp_composite_type: *mut mailmime_composite_type,
) -> *mut mailmime_type {
let mut mime_type: *mut mailmime_type = 0 as *mut mailmime_type;
mime_type =
malloc(::std::mem::size_of::<mailmime_type>() as libc::size_t) as *mut mailmime_type;
if mime_type.is_null() {
return 0 as *mut mailmime_type;
}
(*mime_type).tp_type = tp_type;
match tp_type {
1 => (*mime_type).tp_data.tp_discrete_type = tp_discrete_type,
2 => (*mime_type).tp_data.tp_composite_type = tp_composite_type,
_ => {}
}
return mime_type;
}
pub unsafe fn mailmime_language_new(mut lg_list: *mut clist) -> *mut mailmime_language {
let mut lang: *mut mailmime_language = 0 as *mut mailmime_language;
lang = malloc(::std::mem::size_of::<mailmime_language>() as libc::size_t)
as *mut mailmime_language;
if lang.is_null() {
return 0 as *mut mailmime_language;
}
(*lang).lg_list = lg_list;
return lang;
}
pub unsafe fn mailmime_language_free(mut lang: *mut mailmime_language) {
clist_foreach(
(*lang).lg_list,
::std::mem::transmute::<Option<unsafe fn(_: *mut libc::c_char) -> ()>, clist_func>(Some(
mailimf_atom_free,
)),
0 as *mut libc::c_void,
);
clist_free((*lang).lg_list);
free(lang as *mut libc::c_void);
}
/*
void mailmime_x_token_free(gchar * x_token);
*/
pub unsafe fn mailmime_field_new(
mut fld_type: libc::c_int,
mut fld_content: *mut mailmime_content,
mut fld_encoding: *mut mailmime_mechanism,
mut fld_id: *mut libc::c_char,
mut fld_description: *mut libc::c_char,
mut fld_version: uint32_t,
mut fld_disposition: *mut mailmime_disposition,
mut fld_language: *mut mailmime_language,
mut fld_location: *mut libc::c_char,
) -> *mut mailmime_field {
let mut field: *mut mailmime_field = 0 as *mut mailmime_field;
field = malloc(::std::mem::size_of::<mailmime_field>() as libc::size_t) as *mut mailmime_field;
if field.is_null() {
return 0 as *mut mailmime_field;
}
(*field).fld_type = fld_type;
match fld_type {
1 => (*field).fld_data.fld_content = fld_content,
2 => (*field).fld_data.fld_encoding = fld_encoding,
3 => (*field).fld_data.fld_id = fld_id,
4 => (*field).fld_data.fld_description = fld_description,
5 => (*field).fld_data.fld_version = fld_version,
6 => (*field).fld_data.fld_disposition = fld_disposition,
7 => (*field).fld_data.fld_language = fld_language,
8 => (*field).fld_data.fld_location = fld_location,
_ => {}
}
return field;
}
pub unsafe fn mailmime_field_free(mut field: *mut mailmime_field) {
match (*field).fld_type {
1 => {
if !(*field).fld_data.fld_content.is_null() {
mailmime_content_free((*field).fld_data.fld_content);
}
}
2 => {
if !(*field).fld_data.fld_encoding.is_null() {
mailmime_encoding_free((*field).fld_data.fld_encoding);
}
}
3 => {
if !(*field).fld_data.fld_id.is_null() {
mailmime_id_free((*field).fld_data.fld_id);
}
}
4 => {
if !(*field).fld_data.fld_description.is_null() {
mailmime_description_free((*field).fld_data.fld_description);
}
}
6 => {
if !(*field).fld_data.fld_disposition.is_null() {
mailmime_disposition_free((*field).fld_data.fld_disposition);
}
}
7 => {
if !(*field).fld_data.fld_language.is_null() {
mailmime_language_free((*field).fld_data.fld_language);
}
}
8 => {
if !(*field).fld_data.fld_location.is_null() {
mailmime_location_free((*field).fld_data.fld_location);
}
}
_ => {}
}
free(field as *mut libc::c_void);
}
pub unsafe fn mailmime_disposition_free(mut dsp: *mut mailmime_disposition) {
mailmime_disposition_type_free((*dsp).dsp_type);
clist_foreach(
(*dsp).dsp_parms,
::std::mem::transmute::<
Option<unsafe fn(_: *mut mailmime_disposition_parm) -> ()>,
clist_func,
>(Some(mailmime_disposition_parm_free)),
0 as *mut libc::c_void,
);
clist_free((*dsp).dsp_parms);
free(dsp as *mut libc::c_void);
}
pub unsafe fn mailmime_disposition_parm_free(mut dsp_parm: *mut mailmime_disposition_parm) {
match (*dsp_parm).pa_type {
0 => {
mailmime_filename_parm_free((*dsp_parm).pa_data.pa_filename);
}
1 => {
mailmime_creation_date_parm_free((*dsp_parm).pa_data.pa_creation_date);
}
2 => {
mailmime_modification_date_parm_free((*dsp_parm).pa_data.pa_modification_date);
}
3 => {
mailmime_read_date_parm_free((*dsp_parm).pa_data.pa_read_date);
}
5 => {
mailmime_parameter_free((*dsp_parm).pa_data.pa_parameter);
}
_ => {}
}
free(dsp_parm as *mut libc::c_void);
}
pub unsafe fn mailmime_read_date_parm_free(mut date: *mut libc::c_char) {
mailmime_quoted_date_time_free(date);
}
pub unsafe fn mailmime_quoted_date_time_free(mut date: *mut libc::c_char) {
mailimf_quoted_string_free(date);
}
pub unsafe fn mailmime_modification_date_parm_free(mut date: *mut libc::c_char) {
mailmime_quoted_date_time_free(date);
}
pub unsafe fn mailmime_creation_date_parm_free(mut date: *mut libc::c_char) {
mailmime_quoted_date_time_free(date);
}
pub unsafe fn mailmime_filename_parm_free(mut filename: *mut libc::c_char) {
mailmime_value_free(filename);
}
pub unsafe fn mailmime_disposition_type_free(mut dsp_type: *mut mailmime_disposition_type) {
if !(*dsp_type).dsp_extension.is_null() {
free((*dsp_type).dsp_extension as *mut libc::c_void);
}
free(dsp_type as *mut libc::c_void);
}
pub unsafe fn mailmime_fields_new(mut fld_list: *mut clist) -> *mut mailmime_fields {
let mut fields: *mut mailmime_fields = 0 as *mut mailmime_fields;
fields =
malloc(::std::mem::size_of::<mailmime_fields>() as libc::size_t) as *mut mailmime_fields;
if fields.is_null() {
return 0 as *mut mailmime_fields;
}
(*fields).fld_list = fld_list;
return fields;
}
pub unsafe fn mailmime_fields_free(mut fields: *mut mailmime_fields) {
clist_foreach(
(*fields).fld_list,
::std::mem::transmute::<Option<unsafe fn(_: *mut mailmime_field) -> ()>, clist_func>(Some(
mailmime_field_free,
)),
0 as *mut libc::c_void,
);
clist_free((*fields).fld_list);
free(fields as *mut libc::c_void);
}
pub unsafe fn mailmime_multipart_body_new(mut bd_list: *mut clist) -> *mut mailmime_multipart_body {
let mut mp_body: *mut mailmime_multipart_body = 0 as *mut mailmime_multipart_body;
mp_body = malloc(::std::mem::size_of::<mailmime_multipart_body>() as libc::size_t)
as *mut mailmime_multipart_body;
if mp_body.is_null() {
return 0 as *mut mailmime_multipart_body;
}
(*mp_body).bd_list = bd_list;
return mp_body;
}
pub unsafe fn mailmime_multipart_body_free(mut mp_body: *mut mailmime_multipart_body) {
clist_foreach(
(*mp_body).bd_list,
::std::mem::transmute::<Option<unsafe fn(_: *mut mailimf_body) -> ()>, clist_func>(Some(
mailimf_body_free,
)),
0 as *mut libc::c_void,
);
clist_free((*mp_body).bd_list);
free(mp_body as *mut libc::c_void);
}
pub unsafe fn mailmime_data_new(
mut dt_type: libc::c_int,
mut dt_encoding: libc::c_int,
mut dt_encoded: libc::c_int,
mut dt_data: *const libc::c_char,
mut dt_length: size_t,
mut dt_filename: *mut libc::c_char,
) -> *mut mailmime_data {
let mut mime_data: *mut mailmime_data = 0 as *mut mailmime_data;
mime_data =
malloc(::std::mem::size_of::<mailmime_data>() as libc::size_t) as *mut mailmime_data;
if mime_data.is_null() {
return 0 as *mut mailmime_data;
}
(*mime_data).dt_type = dt_type;
(*mime_data).dt_encoding = dt_encoding;
(*mime_data).dt_encoded = dt_encoded;
match dt_type {
0 => {
(*mime_data).dt_data.dt_text.dt_data = dt_data;
(*mime_data).dt_data.dt_text.dt_length = dt_length
}
1 => (*mime_data).dt_data.dt_filename = dt_filename,
_ => {}
}
return mime_data;
}
pub unsafe fn mailmime_data_free(mut mime_data: *mut mailmime_data) {
match (*mime_data).dt_type {
1 => {
free((*mime_data).dt_data.dt_filename as *mut libc::c_void);
}
_ => {}
}
free(mime_data as *mut libc::c_void);
}
pub unsafe fn mailmime_new(
mut mm_type: libc::c_int,
mut mm_mime_start: *const libc::c_char,
mut mm_length: size_t,
mut mm_mime_fields: *mut mailmime_fields,
mut mm_content_type: *mut mailmime_content,
mut mm_body: *mut mailmime_data,
mut mm_preamble: *mut mailmime_data,
mut mm_epilogue: *mut mailmime_data,
mut mm_mp_list: *mut clist,
mut mm_fields: *mut mailimf_fields,
mut mm_msg_mime: *mut Mailmime,
) -> *mut Mailmime {
let mut mime: *mut Mailmime = 0 as *mut Mailmime;
let mut cur: *mut clistiter = 0 as *mut clistiter;
mime = malloc(::std::mem::size_of::<Mailmime>() as libc::size_t) as *mut Mailmime;
if mime.is_null() {
return 0 as *mut Mailmime;
}
(*mime).mm_parent = 0 as *mut Mailmime;
(*mime).mm_parent_type = MAILMIME_NONE as libc::c_int;
(*mime).mm_multipart_pos = 0 as *mut clistiter;
(*mime).mm_type = mm_type;
(*mime).mm_mime_start = mm_mime_start;
(*mime).mm_length = mm_length;
(*mime).mm_mime_fields = mm_mime_fields;
(*mime).mm_content_type = mm_content_type;
(*mime).mm_body = mm_body;
match mm_type {
1 => (*mime).mm_data.mm_single = mm_body,
2 => {
(*mime).mm_data.mm_multipart.mm_preamble = mm_preamble;
(*mime).mm_data.mm_multipart.mm_epilogue = mm_epilogue;
(*mime).mm_data.mm_multipart.mm_mp_list = mm_mp_list;
cur = (*mm_mp_list).first;
while !cur.is_null() {
let mut submime: *mut Mailmime = 0 as *mut Mailmime;
submime = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut Mailmime;
(*submime).mm_parent = mime;
(*submime).mm_parent_type = MAILMIME_MULTIPLE as libc::c_int;
(*submime).mm_multipart_pos = cur;
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
}
3 => {
(*mime).mm_data.mm_message.mm_fields = mm_fields;
(*mime).mm_data.mm_message.mm_msg_mime = mm_msg_mime;
if !mm_msg_mime.is_null() {
(*mm_msg_mime).mm_parent = mime;
(*mm_msg_mime).mm_parent_type = MAILMIME_MESSAGE as libc::c_int
}
}
_ => {}
}
return mime;
}
pub unsafe fn mailmime_new_simple(
mut mm_type: libc::c_int,
mut mm_mime_fields: *mut mailmime_fields,
mut mm_content_type: *mut mailmime_content,
mut mm_fields: *mut mailimf_fields,
mut mm_msg_mime: *mut Mailmime,
) -> *mut Mailmime {
mailmime_new(
mm_type,
std::ptr::null(),
0,
mm_mime_fields,
mm_content_type,
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
mm_fields,
mm_msg_mime,
)
}
pub unsafe fn mailmime_free(mut mime: *mut Mailmime) {
match (*mime).mm_type {
1 => {
if (*mime).mm_body.is_null() && !(*mime).mm_data.mm_single.is_null() {
mailmime_data_free((*mime).mm_data.mm_single);
}
}
2 => {
/* do nothing */
if !(*mime).mm_data.mm_multipart.mm_preamble.is_null() {
mailmime_data_free((*mime).mm_data.mm_multipart.mm_preamble);
}
if !(*mime).mm_data.mm_multipart.mm_epilogue.is_null() {
mailmime_data_free((*mime).mm_data.mm_multipart.mm_epilogue);
}
clist_foreach(
(*mime).mm_data.mm_multipart.mm_mp_list,
::std::mem::transmute::<Option<unsafe fn(_: *mut Mailmime) -> ()>, clist_func>(
Some(mailmime_free),
),
0 as *mut libc::c_void,
);
clist_free((*mime).mm_data.mm_multipart.mm_mp_list);
}
3 => {
if !(*mime).mm_data.mm_message.mm_fields.is_null() {
mailimf_fields_free((*mime).mm_data.mm_message.mm_fields);
}
if !(*mime).mm_data.mm_message.mm_msg_mime.is_null() {
mailmime_free((*mime).mm_data.mm_message.mm_msg_mime);
}
}
_ => {}
}
if !(*mime).mm_body.is_null() {
mailmime_data_free((*mime).mm_body);
}
if !(*mime).mm_mime_fields.is_null() {
mailmime_fields_free((*mime).mm_mime_fields);
}
if !(*mime).mm_content_type.is_null() {
mailmime_content_free((*mime).mm_content_type);
}
free(mime as *mut libc::c_void);
}
pub unsafe fn mailmime_encoded_word_new(
mut wd_charset: *mut libc::c_char,
mut wd_text: *mut libc::c_char,
) -> *mut mailmime_encoded_word {
let mut ew: *mut mailmime_encoded_word = 0 as *mut mailmime_encoded_word;
ew = malloc(::std::mem::size_of::<mailmime_encoded_word>() as libc::size_t)
as *mut mailmime_encoded_word;
if ew.is_null() {
return 0 as *mut mailmime_encoded_word;
}
(*ew).wd_charset = wd_charset;
(*ew).wd_text = wd_text;
return ew;
}
pub unsafe fn mailmime_encoded_word_free(mut ew: *mut mailmime_encoded_word) {
mailmime_charset_free((*ew).wd_charset);
mailmime_encoded_text_free((*ew).wd_text);
free(ew as *mut libc::c_void);
}
pub unsafe fn mailmime_encoded_text_free(mut text: *mut libc::c_char) {
free(text as *mut libc::c_void);
}
pub unsafe fn mailmime_charset_free(mut charset: *mut libc::c_char) {
free(charset as *mut libc::c_void);
}
pub unsafe fn mailmime_disposition_new(
mut dsp_type: *mut mailmime_disposition_type,
mut dsp_parms: *mut clist,
) -> *mut mailmime_disposition {
let mut dsp: *mut mailmime_disposition = 0 as *mut mailmime_disposition;
dsp = malloc(::std::mem::size_of::<mailmime_disposition>() as libc::size_t)
as *mut mailmime_disposition;
if dsp.is_null() {
return 0 as *mut mailmime_disposition;
}
(*dsp).dsp_type = dsp_type;
(*dsp).dsp_parms = dsp_parms;
return dsp;
}
pub unsafe fn mailmime_disposition_type_new(
mut dsp_type: libc::c_int,
mut dsp_extension: *mut libc::c_char,
) -> *mut mailmime_disposition_type {
let mut m_dsp_type: *mut mailmime_disposition_type = 0 as *mut mailmime_disposition_type;
m_dsp_type = malloc(::std::mem::size_of::<mailmime_disposition_type>() as libc::size_t)
as *mut mailmime_disposition_type;
if m_dsp_type.is_null() {
return 0 as *mut mailmime_disposition_type;
}
(*m_dsp_type).dsp_type = dsp_type;
(*m_dsp_type).dsp_extension = dsp_extension;
return m_dsp_type;
}
pub unsafe fn mailmime_disposition_parm_new(
mut pa_type: libc::c_int,
mut pa_filename: *mut libc::c_char,
mut pa_creation_date: *mut libc::c_char,
mut pa_modification_date: *mut libc::c_char,
mut pa_read_date: *mut libc::c_char,
mut pa_size: size_t,
mut pa_parameter: *mut mailmime_parameter,
) -> *mut mailmime_disposition_parm {
let mut dsp_parm: *mut mailmime_disposition_parm = 0 as *mut mailmime_disposition_parm;
dsp_parm = malloc(::std::mem::size_of::<mailmime_disposition_parm>() as libc::size_t)
as *mut mailmime_disposition_parm;
if dsp_parm.is_null() {
return 0 as *mut mailmime_disposition_parm;
}
(*dsp_parm).pa_type = pa_type;
match pa_type {
0 => (*dsp_parm).pa_data.pa_filename = pa_filename,
1 => (*dsp_parm).pa_data.pa_creation_date = pa_creation_date,
2 => (*dsp_parm).pa_data.pa_modification_date = pa_modification_date,
3 => (*dsp_parm).pa_data.pa_read_date = pa_read_date,
4 => (*dsp_parm).pa_data.pa_size = pa_size,
5 => (*dsp_parm).pa_data.pa_parameter = pa_parameter,
_ => {}
}
return dsp_parm;
}
pub unsafe fn mailmime_section_new(mut sec_list: *mut clist) -> *mut mailmime_section {
let mut section: *mut mailmime_section = 0 as *mut mailmime_section;
section =
malloc(::std::mem::size_of::<mailmime_section>() as libc::size_t) as *mut mailmime_section;
if section.is_null() {
return 0 as *mut mailmime_section;
}
(*section).sec_list = sec_list;
return section;
}
pub unsafe fn mailmime_section_free(mut section: *mut mailmime_section) {
clist_foreach(
(*section).sec_list,
::std::mem::transmute::<Option<unsafe extern "C" fn(_: *mut libc::c_void) -> ()>, clist_func>(
Some(free),
),
0 as *mut libc::c_void,
);
clist_free((*section).sec_list);
free(section as *mut libc::c_void);
}
pub unsafe fn mailmime_decoded_part_free(mut part: *mut libc::c_char) {
mmap_string_unref(part);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,82 +0,0 @@
use crate::mailmime::types::*;
use crate::mailmime::write_generic::*;
use crate::mmapstring::*;
use crate::other::*;
unsafe fn do_write(
mut data: *mut libc::c_void,
mut str: *const libc::c_char,
mut length: size_t,
) -> libc::c_int {
let mut f: *mut MMAPString = 0 as *mut MMAPString;
f = data as *mut MMAPString;
if mmap_string_append_len(f, str, length).is_null() {
return 0i32;
} else {
return length as libc::c_int;
};
}
pub unsafe fn mailmime_content_write_mem(
mut f: *mut MMAPString,
mut col: *mut libc::c_int,
mut content: *mut mailmime_content,
) -> libc::c_int {
return mailmime_content_write_driver(Some(do_write), f as *mut libc::c_void, col, content);
}
pub unsafe fn mailmime_content_type_write_mem(
mut f: *mut MMAPString,
mut col: *mut libc::c_int,
mut content: *mut mailmime_content,
) -> libc::c_int {
return mailmime_content_type_write_driver(
Some(do_write),
f as *mut libc::c_void,
col,
content,
);
}
pub unsafe fn mailmime_write_mem(
mut f: *mut MMAPString,
mut col: *mut libc::c_int,
mut build_info: *mut Mailmime,
) -> libc::c_int {
return mailmime_write_driver(Some(do_write), f as *mut libc::c_void, col, build_info);
}
pub unsafe fn mailmime_quoted_printable_write_mem(
mut f: *mut MMAPString,
mut col: *mut libc::c_int,
mut istext: libc::c_int,
mut text: *const libc::c_char,
mut size: size_t,
) -> libc::c_int {
return mailmime_quoted_printable_write_driver(
Some(do_write),
f as *mut libc::c_void,
col,
istext,
text,
size,
);
}
pub unsafe fn mailmime_base64_write_mem(
mut f: *mut MMAPString,
mut col: *mut libc::c_int,
mut text: *const libc::c_char,
mut size: size_t,
) -> libc::c_int {
return mailmime_base64_write_driver(Some(do_write), f as *mut libc::c_void, col, text, size);
}
pub unsafe fn mailmime_data_write_mem(
mut f: *mut MMAPString,
mut col: *mut libc::c_int,
mut data: *mut mailmime_data,
mut istext: libc::c_int,
) -> libc::c_int {
return mailmime_data_write_driver(Some(do_write), f as *mut libc::c_void, col, data, istext);
}

View File

@@ -1,397 +0,0 @@
use std::sync::Mutex;
use lazy_static::lazy_static;
use libc;
use crate::chash::*;
use crate::other::*;
lazy_static! {
static ref mmapstring_lock: Mutex<()> = Mutex::new(());
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct MMAPString {
pub str_0: *mut libc::c_char,
pub len: size_t,
pub allocated_len: size_t,
pub fd: libc::c_int,
pub mmapped_size: size_t,
}
pub const TMPDIR: &'static str = "/tmp";
pub unsafe fn mmap_string_new(mut init: *const libc::c_char) -> *mut MMAPString {
let mut string: *mut MMAPString = 0 as *mut MMAPString;
string = mmap_string_sized_new(if !init.is_null() {
strlen(init).wrapping_add(2i32 as libc::size_t)
} else {
2i32 as libc::size_t
});
if string.is_null() {
return 0 as *mut MMAPString;
}
if !init.is_null() {
mmap_string_append(string, init);
}
return string;
}
pub unsafe fn mmap_string_append(
mut string: *mut MMAPString,
mut val: *const libc::c_char,
) -> *mut MMAPString {
return mmap_string_insert_len(string, (*string).len, val, strlen(val));
}
pub unsafe fn mmap_string_insert_len(
mut string: *mut MMAPString,
mut pos: size_t,
mut val: *const libc::c_char,
mut len: size_t,
) -> *mut MMAPString {
if mmap_string_maybe_expand(string, len).is_null() {
return 0 as *mut MMAPString;
}
if pos < (*string).len {
memmove(
(*string).str_0.offset(pos as isize).offset(len as isize) as *mut libc::c_void,
(*string).str_0.offset(pos as isize) as *const libc::c_void,
(*string).len.wrapping_sub(pos),
);
}
memmove(
(*string).str_0.offset(pos as isize) as *mut libc::c_void,
val as *const libc::c_void,
len,
);
(*string).len = ((*string).len as libc::size_t).wrapping_add(len) as size_t as size_t;
*(*string).str_0.offset((*string).len as isize) = 0i32 as libc::c_char;
return string;
}
unsafe fn mmap_string_maybe_expand(
mut string: *mut MMAPString,
mut len: size_t,
) -> *mut MMAPString {
if (*string).len.wrapping_add(len) >= (*string).allocated_len {
let mut old_size: size_t = 0;
let mut newstring: *mut MMAPString = 0 as *mut MMAPString;
old_size = (*string).allocated_len;
(*string).allocated_len = nearest_power(
1i32 as size_t,
(*string)
.len
.wrapping_add(len)
.wrapping_add(1i32 as libc::size_t),
);
newstring = mmap_string_realloc_memory(string);
if newstring.is_null() {
(*string).allocated_len = old_size
}
return newstring;
}
return string;
}
/* Strings.
*/
/* SEB */
unsafe fn mmap_string_realloc_memory(mut string: *mut MMAPString) -> *mut MMAPString {
let mut tmp: *mut libc::c_char = 0 as *mut libc::c_char;
tmp = realloc(
(*string).str_0 as *mut libc::c_void,
(*string).allocated_len,
) as *mut libc::c_char;
if tmp.is_null() {
string = 0 as *mut MMAPString
} else {
(*string).str_0 = tmp
}
return string;
}
/* MMAPString */
#[inline]
unsafe fn nearest_power(mut base: size_t, mut num: size_t) -> size_t {
if num > (-1i32 as size_t).wrapping_div(2i32 as libc::size_t) {
return -1i32 as size_t;
} else {
let mut n: size_t = base;
while n < num {
n <<= 1i32
}
return n;
};
}
pub unsafe fn mmap_string_sized_new(mut dfl_size: size_t) -> *mut MMAPString {
let mut string: *mut MMAPString = 0 as *mut MMAPString;
string = malloc(::std::mem::size_of::<MMAPString>() as libc::size_t) as *mut MMAPString;
if string.is_null() {
return 0 as *mut MMAPString;
}
(*string).allocated_len = 0i32 as size_t;
(*string).len = 0i32 as size_t;
(*string).str_0 = 0 as *mut libc::c_char;
(*string).fd = -1i32;
(*string).mmapped_size = 0i32 as size_t;
if mmap_string_maybe_expand(
string,
if dfl_size > 2i32 as libc::size_t {
dfl_size
} else {
2i32 as libc::size_t
},
)
.is_null()
{
free(string as *mut libc::c_void);
return 0 as *mut MMAPString;
}
*(*string).str_0.offset(0isize) = 0i32 as libc::c_char;
return string;
}
pub unsafe fn mmap_string_new_len(
mut init: *const libc::c_char,
mut len: size_t,
) -> *mut MMAPString {
let mut string: *mut MMAPString = 0 as *mut MMAPString;
if len <= 0i32 as libc::size_t {
return mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
} else {
string = mmap_string_sized_new(len);
if string.is_null() {
return string;
}
if !init.is_null() {
mmap_string_append_len(string, init, len);
}
return string;
};
}
pub unsafe fn mmap_string_append_len(
mut string: *mut MMAPString,
mut val: *const libc::c_char,
mut len: size_t,
) -> *mut MMAPString {
return mmap_string_insert_len(string, (*string).len, val, len);
}
pub unsafe fn mmap_string_free(mut string: *mut MMAPString) {
if string.is_null() {
return;
}
free((*string).str_0 as *mut libc::c_void);
free(string as *mut libc::c_void);
}
pub unsafe fn mmap_string_assign(
mut string: *mut MMAPString,
mut rval: *const libc::c_char,
) -> *mut MMAPString {
mmap_string_truncate(string, 0i32 as size_t);
if mmap_string_append(string, rval).is_null() {
return 0 as *mut MMAPString;
}
return string;
}
pub unsafe fn mmap_string_truncate(
mut string: *mut MMAPString,
mut len: size_t,
) -> *mut MMAPString {
(*string).len = if len < (*string).len {
len
} else {
(*string).len
};
*(*string).str_0.offset((*string).len as isize) = 0i32 as libc::c_char;
return string;
}
pub unsafe fn mmap_string_set_size(
mut string: *mut MMAPString,
mut len: size_t,
) -> *mut MMAPString {
if len >= (*string).allocated_len {
if mmap_string_maybe_expand(string, len.wrapping_sub((*string).len)).is_null() {
return 0 as *mut MMAPString;
}
}
(*string).len = len;
*(*string).str_0.offset(len as isize) = 0i32 as libc::c_char;
return string;
}
pub unsafe fn mmap_string_append_c(
mut string: *mut MMAPString,
mut c: libc::c_char,
) -> *mut MMAPString {
return mmap_string_insert_c(string, (*string).len, c);
}
pub unsafe fn mmap_string_insert_c(
mut string: *mut MMAPString,
mut pos: size_t,
mut c: libc::c_char,
) -> *mut MMAPString {
if mmap_string_maybe_expand(string, 1i32 as size_t).is_null() {
return 0 as *mut MMAPString;
}
if pos < (*string).len {
memmove(
(*string).str_0.offset(pos as isize).offset(1isize) as *mut libc::c_void,
(*string).str_0.offset(pos as isize) as *const libc::c_void,
(*string).len.wrapping_sub(pos),
);
}
*(*string).str_0.offset(pos as isize) = c;
(*string).len =
((*string).len as libc::size_t).wrapping_add(1i32 as libc::size_t) as size_t as size_t;
*(*string).str_0.offset((*string).len as isize) = 0i32 as libc::c_char;
return string;
}
pub unsafe fn mmap_string_prepend(
mut string: *mut MMAPString,
mut val: *const libc::c_char,
) -> *mut MMAPString {
return mmap_string_insert_len(string, 0i32 as size_t, val, strlen(val));
}
pub unsafe fn mmap_string_prepend_c(
mut string: *mut MMAPString,
mut c: libc::c_char,
) -> *mut MMAPString {
return mmap_string_insert_c(string, 0i32 as size_t, c);
}
pub unsafe fn mmap_string_prepend_len(
mut string: *mut MMAPString,
mut val: *const libc::c_char,
mut len: size_t,
) -> *mut MMAPString {
return mmap_string_insert_len(string, 0i32 as size_t, val, len);
}
pub unsafe fn mmap_string_insert(
mut string: *mut MMAPString,
mut pos: size_t,
mut val: *const libc::c_char,
) -> *mut MMAPString {
return mmap_string_insert_len(string, pos, val, strlen(val));
}
pub unsafe fn mmap_string_erase(
mut string: *mut MMAPString,
mut pos: size_t,
mut len: size_t,
) -> *mut MMAPString {
if pos.wrapping_add(len) < (*string).len {
memmove(
(*string).str_0.offset(pos as isize) as *mut libc::c_void,
(*string).str_0.offset(pos as isize).offset(len as isize) as *const libc::c_void,
(*string).len.wrapping_sub(pos.wrapping_add(len)),
);
}
(*string).len = ((*string).len as libc::size_t).wrapping_sub(len) as size_t as size_t;
*(*string).str_0.offset((*string).len as isize) = 0i32 as libc::c_char;
return string;
}
pub unsafe fn mmap_string_set_ceil(mut ceil: size_t) {
mmap_string_ceil = ceil;
}
static mut mmap_string_ceil: size_t = (8i32 * 1024i32 * 1024i32) as size_t;
pub unsafe fn mmap_string_ref(mut string: *mut MMAPString) -> libc::c_int {
let mut ht: *mut chash = 0 as *mut chash;
let mut r: libc::c_int = 0;
let mut key: chashdatum = chashdatum {
data: 0 as *mut libc::c_void,
len: 0,
};
let mut data: chashdatum = chashdatum {
data: 0 as *mut libc::c_void,
len: 0,
};
mmapstring_lock.lock().unwrap();
if mmapstring_hashtable.is_null() {
mmapstring_hashtable_init();
}
ht = mmapstring_hashtable;
if ht.is_null() {
return -1i32;
}
key.data = &mut (*string).str_0 as *mut *mut libc::c_char as *mut libc::c_void;
key.len = ::std::mem::size_of::<*mut libc::c_char>() as libc::size_t as libc::c_uint;
data.data = string as *mut libc::c_void;
data.len = 0i32 as libc::c_uint;
r = chash_set(
mmapstring_hashtable,
&mut key,
&mut data,
0 as *mut chashdatum,
);
if r < 0i32 {
return r;
}
return 0i32;
}
static mut mmapstring_hashtable: *mut chash = 0 as *const chash as *mut chash;
unsafe fn mmapstring_hashtable_init() {
mmapstring_hashtable = chash_new(13i32 as libc::c_uint, 1i32);
}
pub unsafe fn mmap_string_unref(mut str: *mut libc::c_char) -> libc::c_int {
let mut string: *mut MMAPString = 0 as *mut MMAPString;
let mut ht: *mut chash = 0 as *mut chash;
let mut key: chashdatum = chashdatum {
data: 0 as *mut libc::c_void,
len: 0,
};
let mut data: chashdatum = chashdatum {
data: 0 as *mut libc::c_void,
len: 0,
};
let mut r: libc::c_int = 0;
if str.is_null() {
return -1i32;
}
mmapstring_lock.lock().unwrap();
ht = mmapstring_hashtable;
if ht.is_null() {
return -1i32;
}
key.data = &mut str as *mut *mut libc::c_char as *mut libc::c_void;
key.len = ::std::mem::size_of::<*mut libc::c_char>() as libc::size_t as libc::c_uint;
r = chash_get(ht, &mut key, &mut data);
if r < 0i32 {
string = 0 as *mut MMAPString
} else {
string = data.data as *mut MMAPString
}
if !string.is_null() {
chash_delete(ht, &mut key, 0 as *mut chashdatum);
if chash_count(ht) == 0i32 as libc::c_uint {
chash_free(ht);
mmapstring_hashtable = 0 as *mut chash
}
}
if !string.is_null() {
mmap_string_free(string);
return 0i32;
} else {
return -1i32;
};
}
#[inline]
unsafe fn chash_count(mut hash: *mut chash) -> libc::c_uint {
return (*hash).count;
}
pub unsafe fn mmapstring_init_lock() {}
pub unsafe fn mmapstring_uninit_lock() {}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +0,0 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc 03cab93c6d1f3a8245f63cf84dacb307944294fe6333c1e38f078a6600659c7a # shrinks to data = "a\t0aA\ta\t0 \ta\t0 \ta a\t\ta A\tAA0a0a 0\t a\t aA \t a\t A0\t AAa\taA0\taAAaA\t0\taa0a\ta Aa Aaaa A0A\t a aA 0\t A\t0\t0\t\t\t\t\t\tA \t\t a\tA Aa aAA0A0AA0aaA A\t\t aa0\ta\t \tAa\taA\t00 AA A a\tA a aAAa \t 00\t0 \t\t a A 0\t\t\t aAA Aa \taAAAA0a A0A\t\t1\\E$\t$R\tc\t^=\t\"\tQ<Uk\t\t>A\t\t&\t}v&\tM^\'|\tW5?dn\t\t+\t\tP\te`\t\t>:Brlq--?\t$#Q\tK=zJ\tb\"9*.\"\t`\tF&T*\tBs,\tg\'*\\\t:\t?l$\t\t|A\"HR:Hk\t\\KkV\t\t{=&!^e%:|_*0wV\t[$`\t:\t$%f\t\t[!\"Y. \tP\t\th\'?\'/?%:++NfQo#@\"+?\t(\\??{\t\'\'$Dzj\t0.?{s4?&?Y=/yj]Z=\t4n\t?Ja\"\t{I\t$\t;I}_8V\t&\t?N\'\tI2/\t9.\tFT%9%`\'\tz\to7Y\t|AXP&@G12g\t\'w\t\t%??\t\"h$?F\"\"6%q\\\\{\tT\t\"]$87$}h\'\t<\t$\tc%U:mT2:<v\t#Rl!;U\t\t\"^D\tRZ(BZ{n\t%[$fgH`\t{B}:*\t\t%%*F`%W\t//B}PQ\t\tsu2\tLz<1*!p-X\tnKv&&0\thm4<n\\.\\/.w\'\t<)E1g3#ood\t`?\t\\({N?,8_?h\ty\t0%\t*$A\t\t*w-ViQUj\tTiH\t%\t%&0p\'\'\tA%r**Fo\'Z\\\tNI*ely4=I?}$.o`\t$\ts\'&lt\t\",:~=Nv/0O%=+<LS\t%P\'\t$r%d.\t{G{/L:\t&I&8-`cy*\"{\t/%fP9.P<\t\t\'/`\t\t`\t\t`!t:\t::\t\tW\'X^\t@.uL&a\tN\t\t\t.\t?0*\tvUK>UJ\\\tQbj%w]P=Js\t\"R\t&s^?</=%\t\'VI:\" kT`{b*<\t\tF&?Y\t\t:/\t!C\'e0`\t\t\tx-\t*\\N\\wwu.I4\tb/y\t\"P:P%\"\t\tQ][\\\t^x\t\t):\t\t&s\t$1-\t\t\tXt`0\t;\t/UeCP*\"G\t\t\':\tk@91Hf&#\t(Uzz&`p2;\t{]\t\"I_%=\\%,XD\"\'06QfCd<;$:q^\t8:\"*\"H\t\to\t&xK/\t\ty0/<}<j<|*`~::b\t=S.=}=Ki\t<Y.\'{\tf\t{Ub*H%(_4*?*\tn2\t/\'\t\t\t/,4]\tt\t<y\t\t\tWi\t\tT&\"\t\t\t\t\t=/1Wu?\t\'A\"W-P\t$?e\\\t`\t6`vD\t8`\t\tccD J\tY&jI//?_\t\\j\t_\tsiq$\t?9\tQ\t.^%&..?%Jm??A%+<\tN&*\t.g\tS$W\"\"\tMpq\t\t:&\\\thTz&<iz%/%T&\'F\t\\]&\t\t}\t\t\tXr=dtp`:+<,\t%60Y*<(&K*kJ\todO\t=<V?&\tMU/\"\t= Y!)<\tV\t9\t)\t&v8q{\t\t&pT\t3\ttB,qcq\'i$;gv%j_%M_{[\"&;\t\t\t.B;6Y\\%\t\"\tY/a\t\\`:?\t<\t?]\taNwD;\\\t%l*74%5T?QS :~yR/E*R\t\t=u\t\\\t\t.Q<;\\\t_S/{&F$^\tw_>\'h=|%\t\t:W!\\<U2\'$\tb&`\t=|=L9\t\t\t\\WZ:! }{\t ;\t;\t\t 0.*\t.%\"\'r%{<Mh_t[[}\t-\tJo\"b/AC*-=`=T\tz$2\tC\t\t/k{47\"\t\t,q%\tZ\tT3\t\tf>%\t\'?%@^tx\t7\"1Bk{b{n\t\"Pj3\tHc\t\tt\tY<\t#?\tSh\\yk/N\\\t8 7=R4*9Cw&m\t\\-\'f\t|\'#t(Etu.Hdu(E\t%&v:\'aqW~A5\t\t w.s{J%lX<\"\t\'*%m<&:/B<&\':U}$&`.{)\t\t6S\t:/$*kQ-Z\t^\'t${/tH*\'v\t3\t=\t\tDyp:B\t`I_R&4SO\t\t&-j=*.\t87&\'e*( \t\t\t\'<$\\DJ<$p?{}\'&\tv\t\\Xk<Y:Y`!$K{\tF&\tzd\t\t*i$\tj\'\t<)R*\t%?\t!.\t=\"@#~:=*\t\tXO=_T,1\"\'.%%\"`{\\:\t\"\tfkeOb/\'$I~\ta\t|&\t[\\KK\"1&Z\t<k\t\t)%\'-~\"2n\tj\tW?*<@w{g%d\ta\\\'\'I\t;:ySR%ke:4\tc\t$=\t&9P]x4\tJ=\t6C6%a\t`0\tF\tm-\tTr\t}\t\tQum\t&@\typ|w2&\t\t3`i&t\t\tT5\"\t.&b&e*/==1.\'*\\[U*\tqPL%?$-0/}~|q`\t\t}\t$\tq==o+T$\'!H\t\ti&um\"?\"%%\t/\'p\tg>?{0{J{\t\t/\t\t{zKZ&>=\t[\"1h<H%z/8,/]s\tv{7\t\t:j*H,M//\t\t\td\'.)\t"

View File

@@ -5,4 +5,3 @@
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc 679506fe9ac59df773f8cfa800fdab5f0a32fe49d2ab370394000a1aa5bc2a72 # shrinks to buf = "%0A"
cc e34960438edb2426904b44fb4215154e7e2880f2fd1c3183b98bfcc76fec4882 # shrinks to input = " 0"

View File

@@ -97,7 +97,7 @@ If you want to run "liveconfig" functional tests you can set
chat devs.
- or the path of a file that contains two lines, each describing
via "addr=... mail_pw=..." a test account login that will
via "addr=... mail_pwd=..." a test account login that will
be used for the live tests.
With ``DCC_PY_LIVECONFIG`` set pytest invocations will use real

View File

@@ -6,6 +6,7 @@
<li><a href="{{ pathto('install') }}">install</a></li>
<li><a href="{{ pathto('api') }}">high level API</a></li>
<li><a href="{{ pathto('lapi') }}">low level API</a></li>
<li><a href="{{ pathto('capi') }}">C deltachat.h</a></li>
</ul>
<b>external links:</b>
<ul>

View File

@@ -8,8 +8,8 @@ high level API reference
- :class:`deltachat.account.Account` (your main entry point, creates the
other classes)
- :class:`deltachat.contact.Contact`
- :class:`deltachat.chat.Chat`
- :class:`deltachat.chatting.Contact`
- :class:`deltachat.chatting.Chat`
- :class:`deltachat.message.Message`
Account
@@ -22,13 +22,13 @@ Account
Contact
-------
.. autoclass:: deltachat.contact.Contact
.. autoclass:: deltachat.chatting.Contact
:members:
Chat
----
.. autoclass:: deltachat.chat.Chat
.. autoclass:: deltachat.chatting.Chat
:members:
Message

View File

@@ -2,13 +2,7 @@
low level API reference
===================================
for full doxygen-generated C-docs, defines and functions please checkout
https://c.delta.chat
Python low-level capi calls
---------------------------
for full C-docs, defines and function checkout :doc:`capi`
.. automodule:: deltachat.capi.lib

View File

@@ -6,26 +6,20 @@
import os
import subprocess
import sys
import os
if __name__ == "__main__":
target = os.environ.get("DCC_RS_TARGET")
if target is None:
os.environ["DCC_RS_TARGET"] = target = "release"
os.environ["DCC_RS_TARGET"] = target = "release"
if "DCC_RS_DEV" not in os.environ:
dn = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.environ["DCC_RS_DEV"] = dn
# build the core library in release + debug mode because
# as of Nov 2019 rPGP generates RSA keys which take
# prohibitively long for non-release installs
os.environ["RUSTFLAGS"] = "-g"
subprocess.check_call([
"cargo", "build", "-p", "deltachat_ffi", "--" + target
])
subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True)
if len(sys.argv) <= 1 or sys.argv[1] != "onlybuild":
subprocess.check_call([
sys.executable, "-m", "pip", "install", "-e", "."
])
subprocess.check_call([
"pip", "install", "-e", "."
])

View File

@@ -29,10 +29,7 @@ def ffibuilder():
extra_link_args = []
else:
raise NotImplementedError("Compilation not supported yet on Windows, can you help?")
target_dir = os.environ.get("CARGO_TARGET_DIR")
if target_dir is None:
target_dir = os.path.join(projdir, 'target')
objs = [os.path.join(target_dir, target, 'libdeltachat.a')]
objs = [os.path.join(projdir, 'target', target, 'libdeltachat.a')]
assert os.path.exists(objs[0]), objs
incs = [os.path.join(projdir, 'deltachat-ffi')]
else:

View File

@@ -1,8 +1,8 @@
""" Account class implementation. """
from __future__ import print_function
import atexit
import threading
import os
import re
import time
from array import array
@@ -15,17 +15,15 @@ import deltachat
from . import const
from .capi import ffi, lib
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array, DCLot
from .chat import Chat
from .message import Message
from .contact import Contact
from .chatting import Contact, Chat, Message
class Account(object):
""" Each account is tied to a sqlite database file which is fully managed
by the underlying deltachat core library. All public Account methods are
by the underlying deltachat c-library. All public Account methods are
meant to be memory-safe and return memory-safe objects.
"""
def __init__(self, db_path, logid=None, eventlogging=True, debug=True):
def __init__(self, db_path, logid=None, eventlogging=True):
""" initialize account object.
:param db_path: a path to the account database. The database
@@ -33,14 +31,13 @@ class Account(object):
:param logid: an optional logging prefix that should be used with
the default internal logging.
:param eventlogging: if False no eventlogging and no context callback will be configured
:param debug: turn on debug logging for events.
"""
self._dc_context = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
_destroy_dc_context,
)
if eventlogging:
self._evlogger = EventLogger(self._dc_context, logid, debug)
self._evlogger = EventLogger(self._dc_context, logid)
deltachat.set_context_callback(self._dc_context, self._process_event)
self._threads = IOThreads(self._dc_context, self._evlogger._log_event)
else:
@@ -51,11 +48,10 @@ class Account(object):
if not lib.dc_open(self._dc_context, db_path, ffi.NULL):
raise ValueError("Could not dc_open: {}".format(db_path))
self._configkeys = self.get_config("sys.config_keys").split()
self._imex_events = Queue()
atexit.register(self.shutdown)
self._imex_completed = threading.Event()
# def __del__(self):
# self.shutdown()
def __del__(self):
self.shutdown()
def _check_config_key(self, name):
if name not in self._configkeys:
@@ -73,18 +69,6 @@ class Account(object):
d[key.lower()] = value
return d
def set_stock_translation(self, id, string):
""" set stock translation string.
:param id: id of stock string (const.DC_STR_*)
:param value: string to set as new transalation
:returns: None
"""
string = string.encode("utf8")
res = lib.dc_set_stock_translation(self._dc_context, id, string)
if res == 0:
raise ValueError("could not set translation string")
def set_config(self, name, value):
""" set configuration values.
@@ -137,30 +121,11 @@ class Account(object):
if not self.is_configured():
raise ValueError("need to configure first")
def empty_server_folders(self, inbox=False, mvbox=False):
""" empty server folders. """
flags = 0
if inbox:
flags |= const.DC_EMPTY_INBOX
if mvbox:
flags |= const.DC_EMPTY_MVBOX
if not flags:
raise ValueError("no flags set")
lib.dc_empty_server(self._dc_context, flags)
def get_infostring(self):
""" return info of the configured account. """
self.check_is_configured()
return from_dc_charpointer(lib.dc_get_info(self._dc_context))
def get_latest_backupfile(self, backupdir):
""" return the latest backup file in a given directory.
"""
res = lib.dc_imex_has_backup(self._dc_context, as_dc_charpointer(backupdir))
if res == ffi.NULL:
return None
return from_dc_charpointer(res)
def get_blobdir(self):
""" return the directory for files.
@@ -170,9 +135,9 @@ class Account(object):
return from_dc_charpointer(lib.dc_get_blobdir(self._dc_context))
def get_self_contact(self):
""" return this account's identity as a :class:`deltachat.contact.Contact`.
""" return this account's identity as a :class:`deltachat.chatting.Contact`.
:returns: :class:`deltachat.contact.Contact`
:returns: :class:`deltachat.chatting.Contact`
"""
self.check_is_configured()
return Contact(self._dc_context, const.DC_CONTACT_ID_SELF)
@@ -184,7 +149,7 @@ class Account(object):
:param email: email-address (text type)
:param name: display name for this contact (optional)
:returns: :class:`deltachat.contact.Contact` instance.
:returns: :class:`deltachat.chatting.Contact` instance.
"""
name = as_dc_charpointer(name)
email = as_dc_charpointer(email)
@@ -210,7 +175,7 @@ class Account(object):
whose name or e-mail matches query.
:param only_verified: if true only return verified contacts.
:param with_self: if true the self-contact is also returned.
:returns: list of :class:`deltachat.contact.Contact` objects.
:returns: list of :class:`deltachat.chatting.Contact` objects.
"""
flags = 0
query = as_dc_charpointer(query)
@@ -228,7 +193,7 @@ class Account(object):
""" create or get an existing 1:1 chat object for the specified contact or contact id.
:param contact: chat_id (int) or contact object.
:returns: a :class:`deltachat.chat.Chat` object.
:returns: a :class:`deltachat.chatting.Chat` object.
"""
if hasattr(contact, "id"):
if contact._dc_context != self._dc_context:
@@ -245,7 +210,7 @@ class Account(object):
the specified message.
:param message: messsage id or message instance.
:returns: a :class:`deltachat.chat.Chat` object.
:returns: a :class:`deltachat.chatting.Chat` object.
"""
if hasattr(message, "id"):
if self._dc_context != message._dc_context:
@@ -263,16 +228,16 @@ class Account(object):
Chats are unpromoted until the first message is sent.
:param verified: if true only verified contacts can be added.
:returns: a :class:`deltachat.chat.Chat` object.
:returns: a :class:`deltachat.chatting.Chat` object.
"""
bytes_name = name.encode("utf8")
chat_id = lib.dc_create_group_chat(self._dc_context, int(verified), bytes_name)
chat_id = lib.dc_create_group_chat(self._dc_context, verified, bytes_name)
return Chat(self, chat_id)
def get_chats(self):
""" return list of chats.
:returns: a list of :class:`deltachat.chat.Chat` objects.
:returns: a list of :class:`deltachat.chatting.Chat` objects.
"""
dc_chatlist = ffi.gc(
lib.dc_get_chatlist(self._dc_context, 0, ffi.NULL, 0),
@@ -290,24 +255,9 @@ class Account(object):
return Chat(self, const.DC_CHAT_ID_DEADDROP)
def get_message_by_id(self, msg_id):
""" return Message instance.
:param msg_id: integer id of this message.
:returns: :class:`deltachat.message.Message` instance.
"""
""" return Message instance. """
return Message.from_db(self, msg_id)
def get_chat_by_id(self, chat_id):
""" return Chat instance.
:param chat_id: integer id of this chat.
:returns: :class:`deltachat.chat.Chat` instance.
:raises: ValueError if chat does not exist.
"""
res = lib.dc_get_chat(self._dc_context, chat_id)
if res == ffi.NULL:
raise ValueError("cannot get chat with id={}".format(chat_id))
lib.dc_chat_unref(res)
return Chat(self, chat_id)
def mark_seen_messages(self, messages):
""" mark the given set of messages as seen.
@@ -324,7 +274,7 @@ class Account(object):
""" Forward list of messages to a chat.
:param messages: list of :class:`deltachat.message.Message` object.
:param chat: :class:`deltachat.chat.Chat` object.
:param chat: :class:`deltachat.chatting.Chat` object.
:returns: None
"""
msg_ids = [msg.id for msg in messages]
@@ -339,64 +289,31 @@ class Account(object):
msg_ids = [msg.id for msg in messages]
lib.dc_delete_msgs(self._dc_context, msg_ids, len(msg_ids))
def export_self_keys(self, path):
""" export public and private keys to the specified directory. """
return self._export(path, imex_cmd=1)
def export_all(self, path):
"""return new file containing a backup of all database state
(chats, contacts, keys, media, ...). The file is created in the
the `path` directory.
def export_to_dir(self, backupdir):
"""return after all delta chat state is exported to a new file in
the specified directory.
"""
export_files = self._export(path, 11)
if len(export_files) != 1:
raise RuntimeError("found more than one new file")
return export_files[0]
def _imex_events_clear(self):
try:
while True:
self._imex_events.get_nowait()
except Empty:
pass
def _export(self, path, imex_cmd):
self._imex_events_clear()
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
snap_files = os.listdir(backupdir)
self._imex_completed.clear()
lib.dc_imex(self._dc_context, 11, as_dc_charpointer(backupdir), ffi.NULL)
if not self._threads.is_started():
lib.dc_perform_imap_jobs(self._dc_context)
files_written = []
while True:
ev = self._imex_events.get()
if isinstance(ev, str):
files_written.append(ev)
elif isinstance(ev, bool):
if not ev:
raise ValueError("export failed, exp-files: {}".format(files_written))
return files_written
self._imex_completed.wait()
for x in os.listdir(backupdir):
if x not in snap_files:
return os.path.join(backupdir, x)
def import_self_keys(self, path):
""" Import private keys found in the `path` directory.
The last imported key is made the default keys unless its name
contains the string legacy. Public keys are not imported.
"""
self._import(path, imex_cmd=2)
def import_all(self, path):
"""import delta chat state from the specified backup `path` (a file).
def import_from_file(self, path):
"""import delta chat state from the specified backup file.
The account must be in unconfigured state for import to attempted.
"""
assert not self.is_configured(), "cannot import into configured account"
self._import(path, imex_cmd=12)
def _import(self, path, imex_cmd):
self._imex_events_clear()
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
self._imex_completed.clear()
lib.dc_imex(self._dc_context, 12, as_dc_charpointer(path), ffi.NULL)
if not self._threads.is_started():
lib.dc_perform_imap_jobs(self._dc_context)
if not self._imex_events.get():
raise ValueError("import from path '{}' failed".format(path))
self._imex_completed.wait()
def initiate_key_transfer(self):
"""return setup code after a Autocrypt setup message
@@ -436,7 +353,7 @@ class Account(object):
""" setup contact and return a Chat after contact is established.
Note that this function may block for a long time as messages are exchanged
with the emitter of the QR code. On success a :class:`deltachat.chat.Chat` instance
with the emitter of the QR code. On success a :class:`deltachat.chatting.Chat` instance
is returned.
:param qr: valid "setup contact" QR code (all other QR codes will result in an exception)
"""
@@ -450,7 +367,7 @@ class Account(object):
""" join a chat group through a QR code.
Note that this function may block for a long time as messages are exchanged
with the emitter of the QR code. On success a :class:`deltachat.chat.Chat` instance
with the emitter of the QR code. On success a :class:`deltachat.chatting.Chat` instance
is returned which is the chat that we just joined.
:param qr: valid "join-group" QR code (all other QR codes will result in an exception)
@@ -461,19 +378,7 @@ class Account(object):
raise ValueError("could not join group")
return Chat(self, chat_id)
def stop_ongoing(self):
lib.dc_stop_ongoing_process(self._dc_context)
#
# meta API for start/stop and event based processing
#
def wait_next_incoming_message(self):
""" wait for and return next incoming message. """
ev = self._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
return self.get_message_by_id(ev[2])
def start_threads(self, mvbox=False, sentbox=False):
def start_threads(self):
""" start IMAP/SMTP threads (and configure account if it hasn't happened).
:raises: ValueError if 'addr' or 'mail_pw' are not configured.
@@ -481,13 +386,12 @@ class Account(object):
"""
if not self.is_configured():
self.configure()
self._threads.start(mvbox=mvbox, sentbox=sentbox)
self._threads.start()
def stop_threads(self, wait=True):
""" stop IMAP/SMTP threads. """
if self._threads.is_started():
self.stop_ongoing()
self._threads.stop(wait=wait)
lib.dc_stop_ongoing_process(self._dc_context)
self._threads.stop(wait=wait)
def shutdown(self, wait=True):
""" stop threads and close and remove underlying dc_context and callbacks. """
@@ -498,7 +402,6 @@ class Account(object):
self.stop_threads(wait=wait) # to wait for threads
deltachat.clear_context_callback(self._dc_context)
del self._dc_context
atexit.unregister(self.shutdown)
def _process_event(self, ctx, evt_name, data1, data2):
assert ctx == self._dc_context
@@ -511,26 +414,7 @@ class Account(object):
def on_dc_event_imex_progress(self, data1, data2):
if data1 == 1000:
self._imex_events.put(True)
elif data1 == 0:
self._imex_events.put(False)
def on_dc_event_imex_file_written(self, data1, data2):
self._imex_events.put(data1)
def set_location(self, latitude=0.0, longitude=0.0, accuracy=0.0):
"""set a new location. It effects all chats where we currently
have enabled location streaming.
:param latitude: float (use 0.0 if not known)
:param longitude: float (use 0.0 if not known)
:param accuracy: float (use 0.0 if not known)
:raises: ValueError if no chat is currently streaming locations
:returns: None
"""
dc_res = lib.dc_set_location(self._dc_context, latitude, longitude, accuracy)
if dc_res == 0:
raise ValueError("no chat is streaming locations")
self._imex_completed.set()
class IOThreads:
@@ -543,14 +427,10 @@ class IOThreads:
def is_started(self):
return len(self._name2thread) > 0
def start(self, imap=True, smtp=True, mvbox=False, sentbox=False):
def start(self, imap=True, smtp=True):
assert not self.is_started()
if imap:
self._start_one_thread("inbox", self.imap_thread_run)
if mvbox:
self._start_one_thread("mvbox", self.mvbox_thread_run)
if sentbox:
self._start_one_thread("sentbox", self.sentbox_thread_run)
self._start_one_thread("imap", self.imap_thread_run)
if smtp:
self._start_one_thread("smtp", self.smtp_thread_run)
@@ -563,37 +443,17 @@ class IOThreads:
self._thread_quitflag = True
lib.dc_interrupt_imap_idle(self._dc_context)
lib.dc_interrupt_smtp_idle(self._dc_context)
lib.dc_interrupt_mvbox_idle(self._dc_context)
lib.dc_interrupt_sentbox_idle(self._dc_context)
if wait:
for name, thread in self._name2thread.items():
thread.join()
def imap_thread_run(self):
self._log_event("py-bindings-info", 0, "INBOX THREAD START")
self._log_event("py-bindings-info", 0, "IMAP THREAD START")
while not self._thread_quitflag:
lib.dc_perform_imap_jobs(self._dc_context)
if not self._thread_quitflag:
lib.dc_perform_imap_fetch(self._dc_context)
if not self._thread_quitflag:
lib.dc_perform_imap_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "INBOX THREAD FINISHED")
def mvbox_thread_run(self):
self._log_event("py-bindings-info", 0, "MVBOX THREAD START")
while not self._thread_quitflag:
lib.dc_perform_mvbox_jobs(self._dc_context)
lib.dc_perform_mvbox_fetch(self._dc_context)
lib.dc_perform_mvbox_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "MVBOX THREAD FINISHED")
def sentbox_thread_run(self):
self._log_event("py-bindings-info", 0, "SENTBOX THREAD START")
while not self._thread_quitflag:
lib.dc_perform_sentbox_jobs(self._dc_context)
lib.dc_perform_sentbox_fetch(self._dc_context)
lib.dc_perform_sentbox_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "SENTBOX THREAD FINISHED")
lib.dc_perform_imap_fetch(self._dc_context)
lib.dc_perform_imap_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "IMAP THREAD FINISHED")
def smtp_thread_run(self):
self._log_event("py-bindings-info", 0, "SMTP THREAD START")
@@ -645,11 +505,11 @@ class EventLogger:
else:
assert not rex.match(ev[0]), "event found {}".format(ev)
def get_matching(self, event_name_regex, check_error=True, timeout=None):
def get_matching(self, event_name_regex, check_error=True):
self._log("-- waiting for event with regex: {} --".format(event_name_regex))
rex = re.compile("(?:{}).*".format(event_name_regex))
while 1:
ev = self.get(timeout=timeout, check_error=check_error)
ev = self.get()
if rex.match(ev[0]):
return ev

View File

@@ -1,15 +1,58 @@
""" Chat and Location related API. """
""" chatting related objects: Contact, Chat, Message. """
import mimetypes
import calendar
from datetime import datetime
import os
from . import props
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
from .capi import lib, ffi
from . import const
from .message import Message
class Contact(object):
""" Delta-Chat Contact.
You obtain instances of it through :class:`deltachat.account.Account`.
"""
def __init__(self, dc_context, id):
self._dc_context = dc_context
self.id = id
def __eq__(self, other):
return self._dc_context == other._dc_context and self.id == other.id
def __ne__(self, other):
return not (self == other)
def __repr__(self):
return "<Contact id={} addr={} dc_context={}>".format(self.id, self.addr, self._dc_context)
@property
def _dc_contact(self):
return ffi.gc(
lib.dc_get_contact(self._dc_context, self.id),
lib.dc_contact_unref
)
@props.with_doc
def addr(self):
""" normalized e-mail address for this account. """
return from_dc_charpointer(lib.dc_contact_get_addr(self._dc_contact))
@props.with_doc
def display_name(self):
""" display name for this contact. """
return from_dc_charpointer(lib.dc_contact_get_display_name(self._dc_contact))
def is_blocked(self):
""" Return True if the contact is blocked. """
return lib.dc_contact_is_blocked(self._dc_contact)
def is_verified(self):
""" Return True if the contact is verified. """
return lib.dc_contact_is_verified(self._dc_contact)
class Chat(object):
""" Chat object which manages members and through which you can send and retrieve messages.
@@ -66,13 +109,6 @@ class Chat(object):
"""
return not lib.dc_chat_is_unpromoted(self._dc_chat)
def is_verified(self):
""" return True if this chat is a verified group.
:returns: True if chat is verified, False otherwise.
"""
return lib.dc_chat_is_verified(self._dc_chat)
def get_name(self):
""" return name of this chat.
@@ -269,10 +305,9 @@ class Chat(object):
def get_contacts(self):
""" get all contacts for this chat.
:params: contact object.
:returns: list of :class:`deltachat.contact.Contact` objects for this chat
:returns: list of :class:`deltachat.chatting.Contact` objects for this chat
"""
from .contact import Contact
dc_array = ffi.gc(
lib.dc_get_chat_contacts(self._dc_context, self.id),
lib.dc_array_unref
@@ -323,62 +358,3 @@ class Chat(object):
if dc_res == ffi.NULL:
return None
return from_dc_charpointer(dc_res)
# ------ location streaming API ------------------------------
def is_sending_locations(self):
"""return True if this chat has location-sending enabled currently.
:returns: True if location sending is enabled.
"""
return lib.dc_is_sending_locations_to_chat(self._dc_context, self.id)
def enable_sending_locations(self, seconds):
"""enable sending locations for this chat.
all subsequent messages will carry a location with them.
"""
lib.dc_send_locations_to_chat(self._dc_context, self.id, seconds)
def get_locations(self, contact=None, timestamp_from=None, timestamp_to=None):
"""return list of locations for the given contact in the given timespan.
:param contact: the contact for which locations shall be returned.
:param timespan_from: a datetime object or None (indicating "since beginning")
:param timespan_to: a datetime object or None (indicating up till now)
:returns: list of :class:`deltachat.chat.Location` objects.
"""
if timestamp_from is None:
time_from = 0
else:
time_from = calendar.timegm(timestamp_from.utctimetuple())
if timestamp_to is None:
time_to = 0
else:
time_to = calendar.timegm(timestamp_to.utctimetuple())
if contact is None:
contact_id = 0
else:
contact_id = contact.id
dc_array = lib.dc_get_locations(self._dc_context, self.id, contact_id, time_from, time_to)
return [
Location(
latitude=lib.dc_array_get_latitude(dc_array, i),
longitude=lib.dc_array_get_longitude(dc_array, i),
accuracy=lib.dc_array_get_accuracy(dc_array, i),
timestamp=datetime.utcfromtimestamp(lib.dc_array_get_timestamp(dc_array, i)))
for i in range(lib.dc_array_get_cnt(dc_array))
]
class Location:
def __init__(self, latitude, longitude, accuracy, timestamp):
assert isinstance(timestamp, datetime)
self.latitude = latitude
self.longitude = longitude
self.accuracy = accuracy
self.timestamp = timestamp
def __eq__(self, other):
return self.__dict__ == other.__dict__

View File

@@ -8,6 +8,9 @@ from os.path import join as joinpath
# this works well when you in a git-checkout
# run "python deltachat/const.py" to regenerate events
# begin const generated
DC_PROVIDER_STATUS_OK = 1
DC_PROVIDER_STATUS_PREPARATION = 2
DC_PROVIDER_STATUS_BROKEN = 3
DC_GCL_ARCHIVED_ONLY = 0x01
DC_GCL_NO_SPECIALS = 0x02
DC_GCL_ADD_ALLDONE_HINT = 0x04
@@ -47,40 +50,19 @@ DC_STATE_OUT_FAILED = 24
DC_STATE_OUT_DELIVERED = 26
DC_STATE_OUT_MDN_RCVD = 28
DC_CONTACT_ID_SELF = 1
DC_CONTACT_ID_INFO = 2
DC_CONTACT_ID_DEVICE = 5
DC_CONTACT_ID_DEVICE = 2
DC_CONTACT_ID_LAST_SPECIAL = 9
DC_MSG_TEXT = 10
DC_MSG_IMAGE = 20
DC_MSG_GIF = 21
DC_MSG_STICKER = 23
DC_MSG_AUDIO = 40
DC_MSG_VOICE = 41
DC_MSG_VIDEO = 50
DC_MSG_FILE = 60
DC_LP_AUTH_OAUTH2 = 0x2
DC_LP_AUTH_NORMAL = 0x4
DC_LP_IMAP_SOCKET_STARTTLS = 0x100
DC_LP_IMAP_SOCKET_SSL = 0x200
DC_LP_IMAP_SOCKET_PLAIN = 0x400
DC_LP_SMTP_SOCKET_STARTTLS = 0x10000
DC_LP_SMTP_SOCKET_SSL = 0x20000
DC_LP_SMTP_SOCKET_PLAIN = 0x40000
DC_CERTCK_AUTO = 0
DC_CERTCK_STRICT = 1
DC_CERTCK_ACCEPT_INVALID_HOSTNAMES = 2
DC_CERTCK_ACCEPT_INVALID_CERTIFICATES = 3
DC_EMPTY_MVBOX = 0x01
DC_EMPTY_INBOX = 0x02
DC_EVENT_INFO = 100
DC_EVENT_SMTP_CONNECTED = 101
DC_EVENT_IMAP_CONNECTED = 102
DC_EVENT_SMTP_MESSAGE_SENT = 103
DC_EVENT_IMAP_MESSAGE_DELETED = 104
DC_EVENT_IMAP_MESSAGE_MOVED = 105
DC_EVENT_IMAP_FOLDER_EMPTIED = 106
DC_EVENT_NEW_BLOB_FILE = 150
DC_EVENT_DELETED_BLOB_FILE = 151
DC_EVENT_WARNING = 300
DC_EVENT_ERROR = 400
DC_EVENT_ERROR_NETWORK = 401
@@ -98,67 +80,15 @@ DC_EVENT_IMEX_PROGRESS = 2051
DC_EVENT_IMEX_FILE_WRITTEN = 2052
DC_EVENT_SECUREJOIN_INVITER_PROGRESS = 2060
DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061
DC_EVENT_SECUREJOIN_MEMBER_ADDED = 2062
DC_EVENT_GET_STRING = 2091
DC_EVENT_FILE_COPIED = 2055
DC_EVENT_IS_OFFLINE = 2081
DC_EVENT_GET_STRING = 2091
DC_STR_SELFNOTINGRP = 21
DC_PROVIDER_STATUS_OK = 1
DC_PROVIDER_STATUS_PREPARATION = 2
DC_PROVIDER_STATUS_BROKEN = 3
DC_STR_NOMESSAGES = 1
DC_STR_SELF = 2
DC_STR_DRAFT = 3
DC_STR_MEMBER = 4
DC_STR_CONTACT = 6
DC_STR_VOICEMESSAGE = 7
DC_STR_DEADDROP = 8
DC_STR_IMAGE = 9
DC_STR_VIDEO = 10
DC_STR_AUDIO = 11
DC_STR_FILE = 12
DC_STR_STATUSLINE = 13
DC_STR_NEWGROUPDRAFT = 14
DC_STR_MSGGRPNAME = 15
DC_STR_MSGGRPIMGCHANGED = 16
DC_STR_MSGADDMEMBER = 17
DC_STR_MSGDELMEMBER = 18
DC_STR_MSGGROUPLEFT = 19
DC_STR_GIF = 23
DC_STR_ENCRYPTEDMSG = 24
DC_STR_E2E_AVAILABLE = 25
DC_STR_ENCR_TRANSP = 27
DC_STR_ENCR_NONE = 28
DC_STR_CANTDECRYPT_MSG_BODY = 29
DC_STR_FINGERPRINTS = 30
DC_STR_READRCPT = 31
DC_STR_READRCPT_MAILBODY = 32
DC_STR_MSGGRPIMGDELETED = 33
DC_STR_E2E_PREFERRED = 34
DC_STR_CONTACT_VERIFIED = 35
DC_STR_CONTACT_NOT_VERIFIED = 36
DC_STR_CONTACT_SETUP_CHANGED = 37
DC_STR_ARCHIVEDCHATS = 40
DC_STR_STARREDMSGS = 41
DC_STR_AC_SETUP_MSG_SUBJECT = 42
DC_STR_AC_SETUP_MSG_BODY = 43
DC_STR_SELFTALK_SUBTITLE = 50
DC_STR_CANNOT_LOGIN = 60
DC_STR_SERVER_RESPONSE = 61
DC_STR_MSGACTIONBYUSER = 62
DC_STR_MSGACTIONBYME = 63
DC_STR_MSGLOCATIONENABLED = 64
DC_STR_MSGLOCATIONDISABLED = 65
DC_STR_LOCATION = 66
DC_STR_STICKER = 67
DC_STR_DEVICE_MESSAGES = 68
DC_STR_COUNT = 68
# end const generated
def read_event_defines(f):
rex = re.compile(r'#define\s+((?:DC_EVENT|DC_QR|DC_MSG|DC_LP|DC_EMPTY|DC_CERTCK|DC_STATE|DC_STR|'
r'DC_CONTACT_ID|DC_GCL|DC_CHAT|DC_PROVIDER)_\S+)\s+([x\d]+).*')
rex = re.compile(r'#define\s+((?:DC_EVENT_|DC_QR|DC_MSG|DC_STATE_|'
r'DC_CONTACT_ID_|DC_GCL|DC_CHAT|DC_PROVIDER)\S+)\s+([x\d]+).*')
for line in f:
m = rex.match(line)
if m:

View File

@@ -1,49 +0,0 @@
""" Contact object. """
from . import props
from .cutil import from_dc_charpointer
from .capi import lib, ffi
class Contact(object):
""" Delta-Chat Contact.
You obtain instances of it through :class:`deltachat.account.Account`.
"""
def __init__(self, dc_context, id):
self._dc_context = dc_context
self.id = id
def __eq__(self, other):
return self._dc_context == other._dc_context and self.id == other.id
def __ne__(self, other):
return not (self == other)
def __repr__(self):
return "<Contact id={} addr={} dc_context={}>".format(self.id, self.addr, self._dc_context)
@property
def _dc_contact(self):
return ffi.gc(
lib.dc_get_contact(self._dc_context, self.id),
lib.dc_contact_unref
)
@props.with_doc
def addr(self):
""" normalized e-mail address for this account. """
return from_dc_charpointer(lib.dc_contact_get_addr(self._dc_contact))
@props.with_doc
def display_name(self):
""" display name for this contact. """
return from_dc_charpointer(lib.dc_contact_get_display_name(self._dc_contact))
def is_blocked(self):
""" Return True if the contact is blocked. """
return lib.dc_contact_is_blocked(self._dc_contact)
def is_verified(self):
""" Return True if the contact is verified. """
return lib.dc_contact_is_verified(self._dc_contact)

View File

@@ -1,4 +1,4 @@
""" The Message object. """
""" chatting related objects: Contact, Chat, Message. """
import os
import shutil
@@ -13,7 +13,7 @@ class Message(object):
""" Message object.
You obtain instances of it through :class:`deltachat.account.Account` or
:class:`deltachat.chat.Chat`.
:class:`deltachat.chatting.Chat`.
"""
def __init__(self, account, dc_msg):
self.account = account
@@ -101,18 +101,6 @@ class Message(object):
""" return True if this message is a setup message. """
return lib.dc_msg_is_setupmessage(self._dc_msg)
def get_setupcodebegin(self):
""" return the first characters of a setup code in a setup message. """
return from_dc_charpointer(lib.dc_msg_get_setupcodebegin(self._dc_msg))
def is_encrypted(self):
""" return True if this message was encrypted. """
return bool(lib.dc_msg_get_showpadlock(self._dc_msg))
def is_forwarded(self):
""" return True if this message was forwarded. """
return bool(lib.dc_msg_is_forwarded(self._dc_msg))
def get_message_info(self):
""" Return informational text for a single message.
@@ -169,18 +157,18 @@ class Message(object):
def chat(self):
"""chat this message was posted in.
:returns: :class:`deltachat.chat.Chat` object
:returns: :class:`deltachat.chatting.Chat` object
"""
from .chat import Chat
from .chatting import Chat
chat_id = lib.dc_msg_get_chat_id(self._dc_msg)
return Chat(self.account, chat_id)
def get_sender_contact(self):
"""return the contact of who wrote the message.
:returns: :class:`deltachat.chat.Contact` instance
:returns: :class:`deltachat.chatting.Contact` instance
"""
from .contact import Contact
from .chatting import Contact
contact_id = lib.dc_msg_get_from_id(self._dc_msg)
return Contact(self._dc_context, contact_id)

View File

@@ -4,7 +4,6 @@ import pytest
import requests
import time
from deltachat import Account
from deltachat import const
from deltachat.capi import lib
import tempfile
@@ -151,41 +150,21 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
lib.dc_set_config(ac._dc_context, b"configured", b"1")
return ac
def peek_online_config(self):
if not session_liveconfig:
pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")
return session_liveconfig.get(self.live_count)
def get_online_config(self):
def get_online_configuring_account(self):
if not session_liveconfig:
pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")
configdict = session_liveconfig.get(self.live_count)
self.live_count += 1
if "e2ee_enabled" not in configdict:
configdict["e2ee_enabled"] = "1"
# Enable strict certificate checks for online accounts
configdict["imap_certificate_checks"] = str(const.DC_CERTCK_STRICT)
configdict["smtp_certificate_checks"] = str(const.DC_CERTCK_STRICT)
tmpdb = tmpdir.join("livedb%d" % self.live_count)
ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count))
ac._evlogger.init_time = self.init_time
ac._evlogger.set_timeout(30)
return ac, dict(configdict)
def get_online_configuring_account(self, mvbox=False, sentbox=False):
ac, configdict = self.get_online_config()
ac.configure(**configdict)
ac.start_threads(mvbox=mvbox, sentbox=sentbox)
ac.start_threads()
return ac
def get_one_online_account(self):
ac1 = self.get_online_configuring_account()
wait_successful_IMAP_SMTP_connection(ac1)
wait_configuration_progress(ac1, 1000)
return ac1
def get_two_online_accounts(self):
ac1 = self.get_online_configuring_account()
ac2 = self.get_online_configuring_account()
@@ -227,13 +206,12 @@ def lp():
return Printer()
def wait_configuration_progress(account, min_target, max_target=1001):
min_target = min(min_target, max_target)
def wait_configuration_progress(account, target):
while 1:
evt_name, data1, data2 = \
account._evlogger.get_matching("DC_EVENT_CONFIGURE_PROGRESS")
if data1 >= min_target and data1 <= max_target:
print("** CONFIG PROGRESS {}".format(min_target), account)
if data1 >= target:
print("** CONFIG PROGRESS {}".format(target), account)
break

View File

@@ -1,8 +1,6 @@
from __future__ import print_function
import pytest
import os
import queue
import time
from deltachat import const, Account
from deltachat.message import Message
from datetime import datetime, timedelta
@@ -21,7 +19,6 @@ class TestOfflineAccountBasic:
d = ac1.get_info()
assert d["arch"]
assert d["number_of_chats"] == "0"
assert d["bcc_self"] == "0"
def test_is_not_configured(self, acfactory):
ac1 = acfactory.get_unconfigured_account()
@@ -40,11 +37,6 @@ class TestOfflineAccountBasic:
ac1 = acfactory.get_unconfigured_account()
assert "save_mime_headers" in ac1.get_config("sys.config_keys").split()
def test_has_bccself(self, acfactory):
ac1 = acfactory.get_unconfigured_account()
assert "bcc_self" in ac1.get_config("sys.config_keys").split()
assert ac1.get_config("bcc_self") == "0"
def test_selfcontact_if_unconfigured(self, acfactory):
ac1 = acfactory.get_unconfigured_account()
with pytest.raises(ValueError):
@@ -101,9 +93,8 @@ class TestOfflineContact:
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact(email="some1@example.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
msg = chat.send_text("one message")
chat.send_text("one messae")
assert not ac1.delete_contact(contact1)
assert not msg.filemime
class TestOfflineChat:
@@ -115,19 +106,13 @@ class TestOfflineChat:
def chat1(self, ac1):
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL, chat.id
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL, chat.id
return chat
def test_display(self, chat1):
str(chat1)
repr(chat1)
def test_chat_by_id(self, chat1):
chat2 = chat1.account.get_chat_by_id(chat1.id)
assert chat2 == chat1
with pytest.raises(ValueError):
chat1.account.get_chat_by_id(123123)
def test_chat_idempotent(self, chat1, ac1):
contact1 = chat1.get_contacts()[0]
chat2 = ac1.create_chat_by_contact(contact1.id)
@@ -155,27 +140,6 @@ class TestOfflineChat:
chat.set_name("title2")
assert chat.get_name() == "title2"
def test_group_chat_creation_with_translation(self, ac1):
ac1.set_stock_translation(const.DC_STR_NEWGROUPDRAFT, "xyz %1$s")
ac1._evlogger.consume_events()
with pytest.raises(ValueError):
ac1.set_stock_translation(const.DC_STR_NEWGROUPDRAFT, "xyz %2$s")
ac1._evlogger.get_matching("DC_EVENT_WARNING")
with pytest.raises(ValueError):
ac1.set_stock_translation(500, "xyz %1$s")
ac1._evlogger.get_matching("DC_EVENT_WARNING")
contact1 = ac1.create_contact("some1@hello.com", name="some1")
contact2 = ac1.create_contact("some2@hello.com", name="some2")
chat = ac1.create_group_chat(name="title1")
chat.add_contact(contact1)
chat.add_contact(contact2)
assert chat.get_name() == "title1"
assert contact1 in chat.get_contacts()
assert contact2 in chat.get_contacts()
assert not chat.is_promoted()
msg = chat.get_draft()
assert msg.text == "xyz title1"
@pytest.mark.parametrize("verified", [True, False])
def test_group_chat_qr(self, acfactory, ac1, verified):
ac2 = acfactory.get_configured_offline_account()
@@ -257,9 +221,7 @@ class TestOfflineChat:
chat1.send_image(path="notexists")
fn = data.get_path("d.png")
lp.sec("sending image")
chat1.account._evlogger.consume_events()
msg = chat1.send_image(fn)
chat1.account._evlogger.get_matching("DC_EVENT_NEW_BLOB_FILE")
assert msg.is_image()
assert msg
assert msg.id > 0
@@ -331,10 +293,10 @@ class TestOfflineChat:
assert contact == ac1.get_self_contact()
assert not backupdir.listdir()
path = ac1.export_all(backupdir.strpath)
path = ac1.export_to_dir(backupdir.strpath)
assert os.path.exists(path)
ac2 = acfactory.get_unconfigured_account()
ac2.import_all(path)
ac2.import_from_file(path)
contacts = ac2.get_contacts(query="some1")
assert len(contacts) == 1
contact2 = contacts[0]
@@ -372,108 +334,36 @@ class TestOfflineChat:
class TestOnlineAccount:
def get_chat(self, ac1, ac2, both_created=False):
def get_chat(self, ac1, ac2):
c2 = ac1.create_contact(email=ac2.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL
if both_created:
ac2.create_chat_by_contact(ac2.create_contact(email=ac1.get_config("addr")))
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
return chat
def test_configure_canceled(self, acfactory):
def test_one_account_send(self, acfactory):
ac1 = acfactory.get_online_configuring_account()
wait_configuration_progress(ac1, 200)
ac1.stop_ongoing()
wait_configuration_progress(ac1, 0, 0)
def test_export_import_self_keys(self, acfactory, tmpdir):
ac1, ac2 = acfactory.get_two_online_accounts()
dir = tmpdir.mkdir("exportdir")
export_files = ac1.export_self_keys(dir.strpath)
assert len(export_files) == 2
for x in export_files:
assert x.startswith(dir.strpath)
ac1._evlogger.consume_events()
ac1.import_self_keys(dir.strpath)
def test_one_account_send_bcc_setting(self, acfactory, lp):
ac1 = acfactory.get_online_configuring_account()
ac2_config = acfactory.peek_online_config()
c2 = ac1.create_contact(email=ac2_config["addr"])
c2 = ac1.create_contact(email=ac1.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
wait_successful_IMAP_SMTP_connection(ac1)
wait_configuration_progress(ac1, 1000)
lp.sec("ac1: setting bcc_self=1")
ac1.set_config("bcc_self", "1")
lp.sec("send out message with bcc to ourselves")
msg_out = chat.send_text("message2")
ev = ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
# wait for send out (BCC)
assert ac1.get_config("bcc_self") == "1"
self_addr = ac1.get_config("addr")
ev = ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
assert self_addr in ev[2]
ev = ac1._evlogger.get_matching("DC_EVENT_DELETED_BLOB_FILE")
# wait for own account to receive
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[1] == msg_out.id
ac1._evlogger.consume_events()
lp.sec("send out message without bcc")
ac1.set_config("bcc_self", "0")
msg_out = chat.send_text("message3")
assert not msg_out.is_forwarded()
ev = ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
ev = ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
assert self_addr not in ev[2]
ev = ac1._evlogger.get_matching("DC_EVENT_DELETED_BLOB_FILE")
def test_mvbox_sentbox_threads(self, acfactory, lp):
lp.sec("ac1: start with mvbox thread")
ac1 = acfactory.get_online_configuring_account(mvbox=True, sentbox=True)
lp.sec("ac2: start without mvbox/sentbox threads")
ac2 = acfactory.get_online_configuring_account()
lp.sec("ac2: waiting for configuration")
wait_configuration_progress(ac2, 1000)
lp.sec("ac1: waiting for configuration")
wait_configuration_progress(ac1, 1000)
lp.sec("ac1: send message and wait for ac2 to receive it")
def test_two_accounts_send_receive(self, acfactory):
ac1, ac2 = acfactory.get_two_online_accounts()
chat = self.get_chat(ac1, ac2)
chat.send_text("message1")
msg_out = chat.send_text("message1")
# wait for other account to receive
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL
lp.sec("test finished")
def test_move_works(self, acfactory):
ac1 = acfactory.get_online_configuring_account()
ac2 = acfactory.get_online_configuring_account(mvbox=True)
wait_configuration_progress(ac2, 1000)
wait_configuration_progress(ac1, 1000)
chat = self.get_chat(ac1, ac2)
chat.send_text("message1")
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL
ev = ac2._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
def test_move_works_on_self_sent(self, acfactory):
ac1 = acfactory.get_online_configuring_account(mvbox=True)
ac1.set_config("bcc_self", "1")
ac2 = acfactory.get_online_configuring_account()
wait_configuration_progress(ac2, 1000)
wait_configuration_progress(ac1, 1000)
chat = self.get_chat(ac1, ac2)
chat.send_text("message1")
chat.send_text("message2")
chat.send_text("message3")
ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
assert ev[2] == msg_out.id
msg_in = ac2.get_message_by_id(msg_out.id)
assert msg_in.text == "message1"
def test_forward_messages(self, acfactory):
ac1, ac2 = acfactory.get_two_online_accounts()
@@ -490,7 +380,6 @@ class TestOnlineAccount:
# check the message arrived in contact-requests/deaddrop
chat2 = msg_in.chat
assert msg_in in chat2.get_messages()
assert not msg_in.is_forwarded()
assert chat2.is_deaddrop()
assert chat2 == ac2.get_deaddrop_chat()
chat3 = ac2.create_group_chat("newgroup")
@@ -498,48 +387,10 @@ class TestOnlineAccount:
ac2.forward_messages([msg_in], chat3)
assert chat3.is_promoted()
messages = chat3.get_messages()
msg = messages[-1]
assert msg.is_forwarded()
ac2.delete_messages(messages)
assert not chat3.get_messages()
def test_forward_own_message(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
chat = self.get_chat(ac1, ac2, both_created=True)
lp.sec("sending message")
msg_out = chat.send_text("message2")
lp.sec("receiving message")
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
msg_in = ac2.get_message_by_id(ev[2])
assert msg_in.text == "message2"
assert not msg_in.is_forwarded()
lp.sec("ac1: creating group chat, and forward own message")
group = ac1.create_group_chat("newgroup2")
group.add_contact(ac1.create_contact(ac2.get_config("addr")))
ac1.forward_messages([msg_out], group)
# wait for other account to receive
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
msg_in = ac2.get_message_by_id(ev[2])
assert msg_in.text == "message2"
assert msg_in.is_forwarded()
def test_send_self_message_and_empty_folder(self, acfactory, lp):
ac1 = acfactory.get_one_online_account()
lp.sec("ac1: create self chat")
chat = ac1.create_chat_by_contact(ac1.get_self_contact())
chat.send_text("hello")
ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
ac1.empty_server_folders(inbox=True, mvbox=True)
ev = ac1._evlogger.get_matching("DC_EVENT_IMAP_FOLDER_EMPTIED")
assert ev[2] == "DeltaChat"
ev = ac1._evlogger.get_matching("DC_EVENT_IMAP_FOLDER_EMPTIED")
assert ev[2] == "INBOX"
def test_send_and_receive_message_markseen(self, acfactory, lp):
def test_send_and_receive_message(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
lp.sec("ac1: create chat with ac2")
@@ -558,7 +409,6 @@ class TestOnlineAccount:
assert ev[2] == msg_out.id
msg_in = ac2.get_message_by_id(msg_out.id)
assert msg_in.text == "message1"
assert not msg_in.is_forwarded()
lp.sec("check the message arrived in contact-requets/deaddrop")
chat2 = msg_in.chat
@@ -580,19 +430,11 @@ class TestOnlineAccount:
ac2.mark_seen_messages([msg_in])
lp.step("1")
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_READ")
assert ev[1] > const.DC_CHAT_ID_LAST_SPECIAL
assert ev[2] > const.DC_MSG_ID_LAST_SPECIAL
assert ev[1] >= const.DC_CHAT_ID_LAST_SPECIAL
assert ev[2] >= const.DC_MSG_ID_LAST_SPECIAL
lp.step("2")
assert msg_out.is_out_mdn_received()
lp.sec("check that a second call to mark_seen does not create change or smtp job")
ac2._evlogger.consume_events()
ac2.mark_seen_messages([msg_in])
try:
ac2._evlogger.get_matching("DC_EVENT_MSG_READ", timeout=0.01)
except queue.Empty:
pass # mark_seen_messages() has generated events before it returns
def test_send_and_receive_will_encrypt_decrypt(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
@@ -601,7 +443,6 @@ class TestOnlineAccount:
lp.sec("sending text message from ac1 to ac2")
msg_out = chat.send_text("message1")
assert not msg_out.is_encrypted()
lp.sec("wait for ac2 to receive message")
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
@@ -619,15 +460,6 @@ class TestOnlineAccount:
assert ev[2] > msg_out.id
msg_back = ac1.get_message_by_id(ev[2])
assert msg_back.text == "message-back"
assert msg_back.is_encrypted()
lp.sec("create group chat with two members, one of which has no encrypt state")
chat = ac1.create_group_chat("encryption test")
chat.add_contact(ac1.create_contact(ac2.get_config("addr")))
chat.add_contact(ac1.create_contact("notexisting@testrun.org"))
msg = chat.send_text("test not encrypt")
ev = ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
assert not msg.is_encrypted()
def test_saved_mime_on_received_message(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
@@ -669,30 +501,19 @@ class TestOnlineAccount:
assert os.path.exists(msg_in.filename)
assert os.stat(msg_in.filename).st_size == os.stat(path).st_size
def test_import_export_online_all(self, acfactory, tmpdir, lp):
def test_import_export_online(self, acfactory, tmpdir):
ac1 = acfactory.get_online_configuring_account()
wait_configuration_progress(ac1, 1000)
lp.sec("create some chat content")
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
chat.send_text("msg1")
backupdir = tmpdir.mkdir("backup")
lp.sec("export all to {}".format(backupdir))
path = ac1.export_all(backupdir.strpath)
path = ac1.export_to_dir(backupdir.strpath)
assert os.path.exists(path)
t = time.time()
lp.sec("get fresh empty account")
ac2 = acfactory.get_unconfigured_account()
lp.sec("get latest backup file")
path2 = ac2.get_latest_backupfile(backupdir.strpath)
assert path2 == path
lp.sec("import backup and check it's proper")
ac2.import_all(path)
ac2.import_from_file(path)
contacts = ac2.get_contacts(query="some1")
assert len(contacts) == 1
contact2 = contacts[0]
@@ -702,18 +523,7 @@ class TestOnlineAccount:
assert len(messages) == 1
assert messages[0].text == "msg1"
# wait until a second passed since last backup
# because get_latest_backupfile() shall return the latest backup
# from a UI it's unlikely anyone manages to export two
# backups in one second.
time.sleep(max(0, 1 - (time.time() - t)))
lp.sec("Second-time export all to {}".format(backupdir))
path2 = ac1.export_all(backupdir.strpath)
assert os.path.exists(path2)
assert path2 != path
assert ac2.get_latest_backupfile(backupdir.strpath) == path2
def test_ac_setup_message(self, acfactory, lp):
def test_ac_setup_message(self, acfactory):
# note that the receiving account needs to be configured and running
# before ther setup message is send. DC does not read old messages
# as of Jul2019
@@ -721,46 +531,20 @@ class TestOnlineAccount:
ac2 = acfactory.clone_online_account(ac1)
wait_configuration_progress(ac2, 1000)
wait_configuration_progress(ac1, 1000)
lp.sec("trigger ac setup message and return setupcode")
assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"]
setup_code = ac1.initiate_key_transfer()
ac2._evlogger.set_timeout(30)
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
msg = ac2.get_message_by_id(ev[2])
assert msg.is_setup_message()
assert msg.get_setupcodebegin() == setup_code[:2]
lp.sec("try a bad setup code")
# first try a bad setup code
with pytest.raises(ValueError):
msg.continue_key_transfer(str(reversed(setup_code)))
lp.sec("try a good setup code")
print("*************** Incoming ASM File at: ", msg.filename)
print("*************** Setup Code: ", setup_code)
msg.continue_key_transfer(setup_code)
assert ac1.get_info()["fingerprint"] == ac2.get_info()["fingerprint"]
def test_ac_setup_message_twice(self, acfactory, lp):
ac1 = acfactory.get_online_configuring_account()
ac2 = acfactory.clone_online_account(ac1)
ac2._evlogger.set_timeout(30)
wait_configuration_progress(ac2, 1000)
wait_configuration_progress(ac1, 1000)
lp.sec("trigger ac setup message but ignore")
assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"]
ac1.initiate_key_transfer()
ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
lp.sec("trigger second ac setup message, wait for receive ")
setup_code2 = ac1.initiate_key_transfer()
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
msg = ac2.get_message_by_id(ev[2])
assert msg.is_setup_message()
assert msg.get_setupcodebegin() == setup_code2[:2]
lp.sec("process second setup message")
msg.continue_key_transfer(setup_code2)
assert ac1.get_info()["fingerprint"] == ac2.get_info()["fingerprint"]
def test_qr_setup_contact(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
lp.sec("ac1: create QR code and let ac2 scan it, starting the securejoin")
@@ -778,43 +562,7 @@ class TestOnlineAccount:
lp.sec("ac2: start QR-code based join-group protocol")
ch = ac2.qr_join_chat(qr)
assert ch.id >= 10
# check that at least some of the handshake messages are deleted
ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED")
ac2._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED")
wait_securejoin_inviter_progress(ac1, 1000)
ac1._evlogger.get_matching("DC_EVENT_SECUREJOIN_MEMBER_ADDED")
def test_qr_verified_group_and_chatting(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
lp.sec("ac1: create verified-group QR, ac2 scans and joins")
chat1 = ac1.create_group_chat("hello", verified=True)
assert chat1.is_verified()
qr = chat1.get_join_qr()
lp.sec("ac2: start QR-code based join-group protocol")
chat2 = ac2.qr_join_chat(qr)
assert chat2.id >= 10
wait_securejoin_inviter_progress(ac1, 1000)
lp.sec("ac2: read member added message")
msg = ac2.wait_next_incoming_message()
assert msg.is_encrypted()
assert "added" in msg.text.lower()
lp.sec("ac1: send message")
msg_out = chat1.send_text("hello")
assert msg_out.is_encrypted()
lp.sec("ac2: read message and check it's verified chat")
msg = ac2.wait_next_incoming_message()
assert msg.text == "hello"
assert msg.chat.is_verified()
assert msg.is_encrypted()
lp.sec("ac2: send message and let ac1 read it")
chat2.send_text("world")
msg = ac1.wait_next_incoming_message()
assert msg.text == "world"
assert msg.is_encrypted()
def test_set_get_profile_image(self, acfactory, data, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
@@ -868,80 +616,3 @@ class TestOnlineAccount:
chat1b = ac1.create_chat_by_message(ev[2])
assert chat1b.get_profile_image() is None
assert chat.get_profile_image() is None
def test_send_receive_locations(self, acfactory, lp):
now = datetime.utcnow()
ac1, ac2 = acfactory.get_two_online_accounts()
lp.sec("ac1: create chat with ac2")
chat1 = self.get_chat(ac1, ac2)
chat2 = self.get_chat(ac2, ac1)
assert not chat1.is_sending_locations()
with pytest.raises(ValueError):
ac1.set_location(latitude=0.0, longitude=10.0)
ac1._evlogger.consume_events()
ac2._evlogger.consume_events()
lp.sec("ac1: enable location sending in chat")
chat1.enable_sending_locations(seconds=100)
assert chat1.is_sending_locations()
ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
ac1.set_location(latitude=2.0, longitude=3.0, accuracy=0.5)
ac1._evlogger.get_matching("DC_EVENT_LOCATION_CHANGED")
chat1.send_text("hello")
ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
lp.sec("ac2: wait for incoming location message")
ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") # "enabled-location streaming"
# currently core emits location changed before event_incoming message
ac2._evlogger.get_matching("DC_EVENT_LOCATION_CHANGED")
ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") # text message with location
locations = chat2.get_locations()
assert len(locations) == 1
assert locations[0].latitude == 2.0
assert locations[0].longitude == 3.0
assert locations[0].accuracy == 0.5
assert locations[0].timestamp > now
contact = ac2.create_contact(ac1.get_config("addr"))
locations2 = chat2.get_locations(contact=contact)
assert len(locations2) == 1
assert locations2 == locations
contact = ac2.create_contact("nonexisting@example.org")
locations3 = chat2.get_locations(contact=contact)
assert not locations3
class TestOnlineConfigureFails:
def test_invalid_password(self, acfactory):
ac1, configdict = acfactory.get_online_config()
ac1.configure(addr=configdict["addr"], mail_pw="123")
ac1.start_threads()
wait_configuration_progress(ac1, 500)
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
assert "cannot login" in ev1[2].lower()
wait_configuration_progress(ac1, 0, 0)
def test_invalid_user(self, acfactory):
ac1, configdict = acfactory.get_online_config()
ac1.configure(addr="x" + configdict["addr"], mail_pw=configdict["mail_pw"])
ac1.start_threads()
wait_configuration_progress(ac1, 500)
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
assert "cannot login" in ev1[2].lower()
wait_configuration_progress(ac1, 0, 0)
def test_invalid_domain(self, acfactory):
ac1, configdict = acfactory.get_online_config()
ac1.configure(addr=configdict["addr"] + "x", mail_pw=configdict["mail_pw"])
ac1.start_threads()
wait_configuration_progress(ac1, 500)
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
assert "could not connect" in ev1[2].lower()
wait_configuration_progress(ac1, 0, 0)

View File

@@ -41,7 +41,7 @@ def test_dc_close_events(tmpdir):
else:
print("skipping event", *ev)
find("disconnecting inbox-thread")
find("disconnecting INBOX-watch")
find("disconnecting sentbox-thread")
find("disconnecting mvbox-thread")
find("disconnecting SMTP")
@@ -59,16 +59,6 @@ def test_wrong_db(tmpdir):
assert not lib.dc_open(dc_context, p.strpath.encode("ascii"), ffi.NULL)
def test_empty_blobdir(tmpdir):
# Apparently some client code expects this to be the same as passing NULL.
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
db_fname = tmpdir.join("hello.db")
assert lib.dc_open(ctx, db_fname.strpath.encode("ascii"), b"")
def test_event_defines():
assert const.DC_EVENT_INFO == 100
assert const.DC_CONTACT_ID_SELF
@@ -95,13 +85,6 @@ def test_markseen_invalid_message_ids(acfactory):
ac1._evlogger.ensure_event_not_queued("DC_EVENT_WARNING|DC_EVENT_ERROR")
def test_get_special_message_id_returns_empty_message(acfactory):
ac1 = acfactory.get_configured_offline_account()
for i in range(1, 10):
msg = ac1.get_message_by_id(i)
assert msg.id == 0
def test_provider_info():
provider = lib.dc_provider_new_from_email(cutil.as_dc_charpointer("ex@example.com"))
assert cutil.from_dc_charpointer(
@@ -115,43 +98,3 @@ def test_provider_info():
def test_provider_info_none():
assert lib.dc_provider_new_from_email(cutil.as_dc_charpointer("email@unexistent.no")) == ffi.NULL
def test_get_info_closed():
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
assert 'deltachat_core_version' in info
assert 'database_dir' not in info
def test_get_info_open(tmpdir):
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
db_fname = tmpdir.join("test.db")
lib.dc_open(ctx, db_fname.strpath.encode("ascii"), ffi.NULL)
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
assert 'deltachat_core_version' in info
assert 'database_dir' in info
def test_is_open_closed():
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
assert lib.dc_is_open(ctx) == 0
def test_is_open_actually_open(tmpdir):
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
db_fname = tmpdir.join("test.db")
lib.dc_open(ctx, db_fname.strpath.encode("ascii"), ffi.NULL)
assert lib.dc_is_open(ctx) == 1

View File

@@ -1,25 +1,19 @@
[tox]
# make sure to update environment list in travis.yml and appveyor.yml
envlist =
py37
py35
lint
auditwheels
[testenv]
commands =
# (some qr tests are pretty heavy in terms of send/received
# messages and async-imap's likely has concurrency problems,
# eg https://github.com/async-email/async-imap/issues/4 )
pytest -n6 --reruns 3 --reruns-delay 5 -v -rsXx -k "not qr" {posargs:tests}
pytest -n6 --reruns 5 --reruns-delay 5 -v -rsXx -k "qr" {posargs:tests}
# python tests/package_wheels.py {toxworkdir}/wheelhouse
pytest -v -rsXx {posargs:tests}
python tests/package_wheels.py {toxworkdir}/wheelhouse
passenv =
TRAVIS
DCC_RS_DEV
DCC_RS_TARGET
DCC_PY_LIVECONFIG
CARGO_TARGET_DIR
RUSTC_WRAPPER
deps =
pytest
pytest-rerunfailures
@@ -34,6 +28,7 @@ deps = auditwheel
commands =
python tests/auditwheels.py {toxworkdir}/wheelhouse
[testenv:lint]
skipsdist = True
usedevelop = True
@@ -48,28 +43,18 @@ commands =
rst-lint --encoding 'utf-8' README.rst
[testenv:doc]
changedir=doc
basepython = python3.5
deps =
sphinx==2.2.0
breathe
changedir = doc
commands =
sphinx-build -Q -w toxdoc-warnings.log -b html . _build/html
[testenv:lintdoc]
skipsdist = True
usedevelop = True
deps =
{[testenv:lint]deps}
{[testenv:doc]deps}
commands =
{[testenv:lint]commands}
{[testenv:doc]commands}
sphinx-build -w docker-toxdoc-warnings.log -b html . _build/html
[pytest]
addopts = -v -rs
addopts = -v -rs --reruns 3 --reruns-delay 2
python_files = tests/test_*.py
norecursedirs = .tox
xfail_strict=true

View File

@@ -23,7 +23,7 @@ if [ $? != 0 ]; then
fi
pushd python
if [ -e "./liveconfig" -a -z "$DCC_PY_LIVECONFIG" ]; then
if [ -e "./liveconfig" && -z "$DCC_PY_LIVECONFIG" ]; then
export DCC_PY_LIVECONFIG=liveconfig
fi
tox "$@"

View File

@@ -1 +1 @@
nightly-2019-11-06
nightly-2019-08-13

View File

@@ -1,61 +0,0 @@
#!/usr/bin/env python
import os
import sys
import re
import pathlib
import subprocess
rex = re.compile(r'version = "(\S+)"')
def read_toml_version(relpath):
p = pathlib.Path(relpath)
assert p.exists()
for line in open(str(p)):
m = rex.match(line)
if m is not None:
return m.group(1)
raise ValueError("no version found in {}".format(relpath))
def replace_toml_version(relpath, newversion):
p = pathlib.Path(relpath)
assert p.exists()
tmp_path = str(p) + "_tmp"
with open(tmp_path, "w") as f:
for line in open(str(p)):
m = rex.match(line)
if m is not None:
f.write('version = "{}"\n'.format(newversion))
else:
f.write(line)
os.rename(tmp_path, str(p))
if __name__ == "__main__":
if len(sys.argv) < 2:
raise SystemExit("need argument: new version, example 1.0.0-beta.27")
newversion = sys.argv[1]
if newversion.count(".") < 2:
raise SystemExit("need at least two dots in version")
core_toml = read_toml_version("Cargo.toml")
ffi_toml = read_toml_version("deltachat-ffi/Cargo.toml")
assert core_toml == ffi_toml, (core_toml, ffi_toml)
for line in open("CHANGELOG.md"):
## 1.0.0-beta5
if line.startswith("## "):
if line[2:].strip().startswith(newversion):
break
else:
raise SystemExit("CHANGELOG.md contains no entry for version: {}".format(newversion))
replace_toml_version("Cargo.toml", newversion)
replace_toml_version("deltachat-ffi/Cargo.toml", newversion)
subprocess.call(["cargo", "update", "-p", "deltachat"])
print("after commit make sure to: ")
print("")
print(" git tag {}".format(newversion))
print("")

29
spec.md
View File

@@ -117,8 +117,7 @@ The sender plus the recipients are the group members.
To allow different groups with the same members,
groups are identified by a group-id.
The group-id MUST be created only from the characters
`0`-`9`, `A`-`Z`, `a`-`z` `_` and `-`
and MUST have a length of at least 11 characters.
`0`-`9`, `A`-`Z`, `a`-`z` `_` and `-`.
Groups MUST have a group-name.
The group-name is any non-zero-length UTF-8 string.
@@ -145,9 +144,9 @@ The message-id MUST have the format `Gr.<group-id>.<unique data>`.
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 12345uvwxyZ
Chat-Group-ID: 1234xyZ
Chat-Group-Name: My Group
Message-ID: Gr.12345uvwxyZ.0001@domain
Message-ID: Gr.1234xyZ.0001@domain
Subject: Chat: My Group: Hello group ...
Hello group - this group contains three members
@@ -197,10 +196,10 @@ and the message SHOULD appear as a message or action from the sender.
From: member1@domain
To: member2@domain, member3@domain, member4@domain
Chat-Version: 1.0
Chat-Group-ID: 12345uvwxyZ
Chat-Group-ID: 1234xyZ
Chat-Group-Name: My Group
Chat-Group-Member-Added: member4@domain
Message-ID: Gr.12345uvwxyZ.0002@domain
Message-ID: Gr.1234xyZ.0002@domain
Subject: Chat: My Group: Hello, ...
Hello, I've added member4@domain to our group. Now we have 4 members.
@@ -210,10 +209,10 @@ To remove a member:
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 12345uvwxyZ
Chat-Group-ID: 1234xyZ
Chat-Group-Name: My Group
Chat-Group-Member-Removed: member4@domain
Message-ID: Gr.12345uvwxyZ.0003@domain
Message-ID: Gr.1234xyZ.0003@domain
Subject: Chat: My Group: Hello, ...
Hello, I've removed member4@domain from our group. Now we have 3 members.
@@ -234,10 +233,10 @@ and the message SHOULD appear as a message or action from the sender.
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 12345uvwxyZ
Chat-Group-ID: 1234xyZ
Chat-Group-Name: Our Group
Chat-Group-Name-Changed: My Group
Message-ID: Gr.12345uvwxyZ.0004@domain
Message-ID: Gr.1234xyZ.0004@domain
Subject: Chat: Our Group: Hello, ...
Hello, I've changed the group name from "My Group" to "Our Group".
@@ -263,10 +262,10 @@ and the message SHOULD appear as a message or action from the sender.
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 12345uvwxyZ
Chat-Group-ID: 1234xyZ
Chat-Group-Name: Our Group
Chat-Group-Image: image.jpg
Message-ID: Gr.12345uvwxyZ.0005@domain
Message-ID: Gr.1234xyZ.0005@domain
Subject: Chat: Our Group: Hello, ...
Content-Type: multipart/mixed; boundary="==break=="
@@ -334,7 +333,9 @@ only on image changes.
# Miscellaneous
Messengers SHOULD use the header `In-Reply-To` as usual.
Messengers SHOULD use the header `Chat-Predecessor`
instead of `In-Reply-To` as the latter one results
in infinite threads on typical MUAs.
Messengers SHOULD add a `Chat-Voice-message: 1` header
if an attached audio file is a voice message.
@@ -344,7 +345,7 @@ to specify the duration of attached audio or video files.
The value MUST be the duration in milliseconds.
This allows the receiver to show the time without knowing the file format.
In-Reply-To: Gr.12345uvwxyZ.0005@domain
Chat-Predecessor: foo123@domain
Chat-Voice-Message: 1
Chat-Duration: 10000

View File

@@ -1,13 +1,9 @@
//! # Autocrypt header module
//!
//! Parse and create [Autocrypt-headers](https://autocrypt.org/en/latest/level1.html#the-autocrypt-header).
use std::collections::BTreeMap;
use std::ffi::CStr;
use std::str::FromStr;
use std::{fmt, str};
use mmime::mailimf::types::*;
use mmime::mailimf_types::*;
use crate::constants::*;
use crate::contact::*;
@@ -50,7 +46,7 @@ impl str::FromStr for EncryptPreference {
}
}
/// Autocrypt header
/// Parse and create [Autocrypt-headers](https://autocrypt.org/en/latest/level1.html#the-autocrypt-header).
#[derive(Debug)]
pub struct Aheader {
pub addr: String,
@@ -59,7 +55,6 @@ pub struct Aheader {
}
impl Aheader {
/// Creates new autocrypt header
pub fn new(addr: String, public_key: Key, prefer_encrypt: EncryptPreference) -> Self {
Aheader {
addr,
@@ -84,21 +79,27 @@ impl Aheader {
let optional_field = unsafe { (*field).fld_data.fld_optional_field };
if !optional_field.is_null()
&& unsafe { !(*optional_field).fld_name.is_null() }
&& unsafe { CStr::from_ptr((*optional_field).fld_name).to_string_lossy() }
&& unsafe { CStr::from_ptr((*optional_field).fld_name).to_str().unwrap() }
== "Autocrypt"
{
let value =
unsafe { CStr::from_ptr((*optional_field).fld_value).to_string_lossy() };
let value = unsafe {
CStr::from_ptr((*optional_field).fld_value)
.to_str()
.unwrap()
};
if let Ok(test) = Self::from_str(&value) {
if addr_cmp(&test.addr, wanted_from) {
if fine_header.is_none() {
fine_header = Some(test);
} else {
// TODO: figure out what kind of error case this is
return None;
match Self::from_str(value) {
Ok(test) => {
if addr_cmp(&test.addr, wanted_from) {
if fine_header.is_none() {
fine_header = Some(test);
} else {
// TODO: figure out what kind of error case this is
return None;
}
}
}
_ => {}
}
}
}
@@ -130,9 +131,9 @@ impl str::FromStr for Aheader {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut attributes: BTreeMap<String, String> = s
.split(';')
.split(";")
.filter_map(|a| {
let attribute: Vec<&str> = a.trim().splitn(2, '=').collect();
let attribute: Vec<&str> = a.trim().splitn(2, "=").collect();
if attribute.len() < 2 {
return None;
}
@@ -177,7 +178,7 @@ impl str::FromStr for Aheader {
// Autocrypt-Level0: unknown attributes starting with an underscore can be safely ignored
// Autocrypt-Level0: unknown attribute, treat the header as invalid
if attributes.keys().any(|k| !k.starts_with('_')) {
if attributes.keys().find(|k| !k.starts_with("_")).is_some() {
return Err(());
}

View File

@@ -1,697 +0,0 @@
//! # Blob directory management
use std::ffi::OsStr;
use std::fmt;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use crate::context::Context;
use crate::events::Event;
/// Represents a file in the blob directory.
///
/// The object has a name, which will always be valid UTF-8. Having a
/// blob object does not imply the respective file exists, however
/// when using one of the `create*()` methods a unique file is
/// created.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlobObject<'a> {
blobdir: &'a Path,
name: String,
}
impl<'a> BlobObject<'a> {
/// Creates a new blob object with a unique name.
///
/// Creates a new file in the blob directory. The name will be
/// derived from the platform-agnostic basename of the suggested
/// name, followed by a random number and followed by a possible
/// extension. The `data` will be written into the file without
/// race-conditions.
///
/// # Errors
///
/// [BlobErrorKind::CreateFailure] is used when the file could not
/// be created. You can expect [BlobError.cause] to contain an
/// underlying error.
///
/// [BlobErrorKind::WriteFailure] is used when the file could not
/// be written to. You can expect [BlobError.cause] to contain an
/// underlying error.
pub fn create(
context: &'a Context,
suggested_name: impl AsRef<str>,
data: &[u8],
) -> std::result::Result<BlobObject<'a>, BlobError> {
let blobdir = context.get_blobdir();
let (stem, ext) = BlobObject::sanitise_name(suggested_name.as_ref());
let (name, mut file) = BlobObject::create_new_file(&blobdir, &stem, &ext)?;
file.write_all(data)
.map_err(|err| BlobError::new_write_failure(blobdir, &name, err))?;
let blob = BlobObject {
blobdir,
name: format!("$BLOBDIR/{}", name),
};
context.call_cb(Event::NewBlobFile(blob.as_name().to_string()));
Ok(blob)
}
// Creates a new file, returning a tuple of the name and the handle.
fn create_new_file(dir: &Path, stem: &str, ext: &str) -> Result<(String, fs::File), BlobError> {
let max_attempt = 15;
let mut name = format!("{}{}", stem, ext);
for attempt in 0..max_attempt {
let path = dir.join(&name);
match fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(&path)
{
Ok(file) => return Ok((name, file)),
Err(err) => {
if attempt == max_attempt {
return Err(BlobError::new_create_failure(dir, &name, err));
} else {
name = format!("{}-{}{}", stem, rand::random::<u32>(), ext);
}
}
}
}
Err(BlobError::new_create_failure(
dir,
&name,
format_err!("Unreachable code - supposedly"),
))
}
/// Creates a new blob object with unique name by copying an existing file.
///
/// This creates a new blob as described in [BlobObject::create]
/// but also copies an existing file into it. This is done in a
/// in way which avoids race-conditions when multiple files are
/// concurrently created.
///
/// # Errors
///
/// In addition to the errors in [BlobObject::create] the
/// [BlobErrorKind::CopyFailure] is used when the data can not be
/// copied.
pub fn create_and_copy(
context: &'a Context,
src: impl AsRef<Path>,
) -> std::result::Result<BlobObject<'a>, BlobError> {
let mut src_file = fs::File::open(src.as_ref()).map_err(|err| {
BlobError::new_copy_failure(context.get_blobdir(), "", src.as_ref(), err)
})?;
let (stem, ext) = BlobObject::sanitise_name(&src.as_ref().to_string_lossy());
let (name, mut dst_file) = BlobObject::create_new_file(context.get_blobdir(), &stem, &ext)?;
std::io::copy(&mut src_file, &mut dst_file).map_err(|err| {
{
// Attempt to remove the failed file, swallow errors resulting from that.
let path = context.get_blobdir().join(&name);
fs::remove_file(path).ok();
}
BlobError::new_copy_failure(context.get_blobdir(), &name, src.as_ref(), err)
})?;
let blob = BlobObject {
blobdir: context.get_blobdir(),
name: format!("$BLOBDIR/{}", name),
};
context.call_cb(Event::NewBlobFile(blob.as_name().to_string()));
Ok(blob)
}
/// Creates a blob from a file, possibly copying it to the blobdir.
///
/// If the source file is not a path to into the blob directory
/// the file will be copied into the blob directory first. If the
/// source file is already in the blobdir it will not be copied
/// and only be created if it is a valid blobname, that is no
/// subdirectory is used and [BlobObject::sanitise_name] does not
/// modify the filename.
///
/// # Errors
///
/// This merely delegates to the [BlobObject::create_and_copy] and
/// the [BlobObject::from_path] methods. See those for possible
/// errors.
pub fn create_from_path(
context: &Context,
src: impl AsRef<Path>,
) -> std::result::Result<BlobObject, BlobError> {
if src.as_ref().starts_with(context.get_blobdir()) {
BlobObject::from_path(context, src)
} else {
BlobObject::create_and_copy(context, src)
}
}
/// Returns a [BlobObject] for an existing blob from a path.
///
/// The path must designate a file directly in the blobdir and
/// must use a valid blob name. That is after sanitisation the
/// name must still be the same, that means it must be valid UTF-8
/// and not have any special characters in it.
///
/// # Errors
///
/// [BlobErrorKind::WrongBlobdir] is used if the path is not in
/// the blob directory.
///
/// [BlobErrorKind::WrongName] is used if the file name does not
/// remain identical after sanitisation.
pub fn from_path(
context: &Context,
path: impl AsRef<Path>,
) -> std::result::Result<BlobObject, BlobError> {
let rel_path = path
.as_ref()
.strip_prefix(context.get_blobdir())
.map_err(|_| BlobError::new_wrong_blobdir(context.get_blobdir(), path.as_ref()))?;
if !BlobObject::is_acceptible_blob_name(&rel_path) {
return Err(BlobError::new_wrong_name(path.as_ref()));
}
let name = rel_path
.to_str()
.ok_or_else(|| BlobError::new_wrong_name(path.as_ref()))?;
BlobObject::from_name(context, name.to_string())
}
/// Returns a [BlobObject] for an existing blob.
///
/// The `name` may optionally be prefixed with the `$BLOBDIR/`
/// prefixed, as returned by [BlobObject::as_name]. This is how
/// you want to create a [BlobObject] for a filename read from the
/// database.
///
/// # Errors
///
/// [BlobErrorKind::WrongName] is used if the name is not a valid
/// blobname, i.e. if [BlobObject::sanitise_name] does modify the
/// provided name.
pub fn from_name(
context: &'a Context,
name: String,
) -> std::result::Result<BlobObject<'a>, BlobError> {
let name: String = match name.starts_with("$BLOBDIR/") {
true => name.splitn(2, '/').last().unwrap().to_string(),
false => name,
};
if !BlobObject::is_acceptible_blob_name(&name) {
return Err(BlobError::new_wrong_name(name));
}
Ok(BlobObject {
blobdir: context.get_blobdir(),
name: format!("$BLOBDIR/{}", name),
})
}
/// Returns the absolute path to the blob in the filesystem.
pub fn to_abs_path(&self) -> PathBuf {
let fname = Path::new(&self.name).strip_prefix("$BLOBDIR/").unwrap();
self.blobdir.join(fname)
}
/// Returns the blob name, as stored in the database.
///
/// This returns the blob in the `$BLOBDIR/<name>` format used in
/// the database. Do not use this unless you're about to store
/// this string in the database or [Params]. Eventually even
/// those conversions should be handled by the type system.
///
/// [Params]: crate::param::Params
pub fn as_name(&self) -> &str {
&self.name
}
/// Returns the filename of the blob.
pub fn as_file_name(&self) -> &str {
self.name.rsplitn(2, '/').next().unwrap()
}
/// The path relative in the blob directory.
pub fn as_rel_path(&self) -> &Path {
Path::new(self.as_file_name())
}
/// Returns the extension of the blob.
///
/// If a blob's filename has an extension, it is always guaranteed
/// to be lowercase.
pub fn suffix(&self) -> Option<&str> {
let ext = self.name.rsplitn(2, '.').next();
if ext == Some(&self.name) {
None
} else {
ext
}
}
/// Create a safe name based on a messy input string.
///
/// The safe name will be a valid filename on Unix and Windows and
/// not contain any path separators. The input can contain path
/// segments separated by either Unix or Windows path separators,
/// the rightmost non-empty segment will be used as name,
/// sanitised for special characters.
///
/// The resulting name is returned as a tuple, the first part
/// being the stem or basename and the second being an extension,
/// including the dot. E.g. "foo.txt" is returned as `("foo",
/// ".txt")` while "bar" is returned as `("bar", "")`.
///
/// The extension part will always be lowercased.
fn sanitise_name(name: &str) -> (String, String) {
let mut name = name.to_string();
for part in name.rsplit('/') {
if !part.is_empty() {
name = part.to_string();
break;
}
}
for part in name.rsplit('\\') {
if !part.is_empty() {
name = part.to_string();
break;
}
}
let opts = sanitize_filename::Options {
truncate: true,
windows: true,
replacement: "",
};
let clean = sanitize_filename::sanitize_with_options(name, opts);
let mut iter = clean.splitn(2, '.');
let mut stem = iter.next().unwrap_or_default().to_string();
let mut ext = iter.next().unwrap_or_default().to_string();
stem.truncate(64);
ext.truncate(32);
match ext.len() {
0 => (stem, "".to_string()),
_ => (stem, format!(".{}", ext).to_lowercase()),
}
}
/// Checks whether a name is a valid blob name.
///
/// This is slightly less strict than stanitise_name, presumably
/// someone already created a file with such a name so we just
/// ensure it's not actually a path in disguise is actually utf-8.
fn is_acceptible_blob_name(name: impl AsRef<OsStr>) -> bool {
let uname = match name.as_ref().to_str() {
Some(name) => name,
None => return false,
};
if uname.find('/').is_some() {
return false;
}
if uname.find('\\').is_some() {
return false;
}
if uname.find('\0').is_some() {
return false;
}
true
}
}
impl<'a> fmt::Display for BlobObject<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "$BLOBDIR/{}", self.name)
}
}
/// Errors for the [BlobObject].
///
/// To keep the return type small and thus the happy path fast this
/// stores everything on the heap.
#[derive(Debug)]
pub struct BlobError {
inner: Box<BlobErrorInner>,
}
#[derive(Debug)]
struct BlobErrorInner {
kind: BlobErrorKind,
data: BlobErrorData,
backtrace: failure::Backtrace,
}
/// Error kind for [BlobError].
///
/// Each error kind has associated data in the [BlobErrorData].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlobErrorKind {
/// Failed to create the blob.
CreateFailure,
/// Failed to write data to blob.
WriteFailure,
/// Failed to copy data to blob.
CopyFailure,
/// Blob is not in the blobdir.
WrongBlobdir,
/// Blob has a bad name.
///
/// E.g. the name is not sanitised correctly or contains a
/// sub-directory.
WrongName,
}
/// Associated data for each [BlobError] error kind.
///
/// This is not stored directly on the [BlobErrorKind] so that the
/// kind can stay trivially Copy and Eq. It is however possible to
/// create a [BlobError] with mismatching [BlobErrorKind] and
/// [BlobErrorData], don't do that.
///
/// Any blobname stored here is the bare name, without the `$BLOBDIR`
/// prefix. All data is owned so that errors do not need to be tied
/// to any lifetimes.
#[derive(Debug)]
enum BlobErrorData {
CreateFailure {
blobdir: PathBuf,
blobname: String,
cause: failure::Error,
},
WriteFailure {
blobdir: PathBuf,
blobname: String,
cause: failure::Error,
},
CopyFailure {
blobdir: PathBuf,
blobname: String,
src: PathBuf,
cause: failure::Error,
},
WrongBlobdir {
blobdir: PathBuf,
src: PathBuf,
},
WrongName {
blobname: PathBuf,
},
}
impl BlobError {
pub fn kind(&self) -> BlobErrorKind {
self.inner.kind
}
fn new_create_failure(
blobdir: impl Into<PathBuf>,
blobname: impl Into<String>,
cause: impl Into<failure::Error>,
) -> BlobError {
BlobError {
inner: Box::new(BlobErrorInner {
kind: BlobErrorKind::CreateFailure,
data: BlobErrorData::CreateFailure {
blobdir: blobdir.into(),
blobname: blobname.into(),
cause: cause.into(),
},
backtrace: failure::Backtrace::new(),
}),
}
}
fn new_write_failure(
blobdir: impl Into<PathBuf>,
blobname: impl Into<String>,
cause: impl Into<failure::Error>,
) -> BlobError {
BlobError {
inner: Box::new(BlobErrorInner {
kind: BlobErrorKind::WriteFailure,
data: BlobErrorData::WriteFailure {
blobdir: blobdir.into(),
blobname: blobname.into(),
cause: cause.into(),
},
backtrace: failure::Backtrace::new(),
}),
}
}
fn new_copy_failure(
blobdir: impl Into<PathBuf>,
blobname: impl Into<String>,
src: impl Into<PathBuf>,
cause: impl Into<failure::Error>,
) -> BlobError {
BlobError {
inner: Box::new(BlobErrorInner {
kind: BlobErrorKind::CopyFailure,
data: BlobErrorData::CopyFailure {
blobdir: blobdir.into(),
blobname: blobname.into(),
src: src.into(),
cause: cause.into(),
},
backtrace: failure::Backtrace::new(),
}),
}
}
fn new_wrong_blobdir(blobdir: impl Into<PathBuf>, src: impl Into<PathBuf>) -> BlobError {
BlobError {
inner: Box::new(BlobErrorInner {
kind: BlobErrorKind::WrongBlobdir,
data: BlobErrorData::WrongBlobdir {
blobdir: blobdir.into(),
src: src.into(),
},
backtrace: failure::Backtrace::new(),
}),
}
}
fn new_wrong_name(blobname: impl Into<PathBuf>) -> BlobError {
BlobError {
inner: Box::new(BlobErrorInner {
kind: BlobErrorKind::WrongName,
data: BlobErrorData::WrongName {
blobname: blobname.into(),
},
backtrace: failure::Backtrace::new(),
}),
}
}
}
impl fmt::Display for BlobError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Match on the data rather than kind, they are equivalent for
// identifying purposes but contain the actual data we need.
match &self.inner.data {
BlobErrorData::CreateFailure {
blobdir, blobname, ..
} => write!(
f,
"Failed to create blob {} in {}",
blobname,
blobdir.display()
),
BlobErrorData::WriteFailure {
blobdir, blobname, ..
} => write!(
f,
"Failed to write data to blob {} in {}",
blobname,
blobdir.display()
),
BlobErrorData::CopyFailure {
blobdir,
blobname,
src,
..
} => write!(
f,
"Failed to copy data from {} to blob {} in {}",
src.display(),
blobname,
blobdir.display(),
),
BlobErrorData::WrongBlobdir { blobdir, src } => write!(
f,
"File path {} is not in blobdir {}",
src.display(),
blobdir.display(),
),
BlobErrorData::WrongName { blobname } => {
write!(f, "Blob has a bad name: {}", blobname.display(),)
}
}
}
}
impl failure::Fail for BlobError {
fn cause(&self) -> Option<&dyn failure::Fail> {
match &self.inner.data {
BlobErrorData::CreateFailure { cause, .. }
| BlobErrorData::WriteFailure { cause, .. }
| BlobErrorData::CopyFailure { cause, .. } => Some(cause.as_fail()),
_ => None,
}
}
fn backtrace(&self) -> Option<&failure::Backtrace> {
Some(&self.inner.backtrace)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::*;
#[test]
fn test_create() {
let t = dummy_context();
let blob = BlobObject::create(&t.ctx, "foo", b"hello").unwrap();
let fname = t.ctx.get_blobdir().join("foo");
let data = fs::read(fname).unwrap();
assert_eq!(data, b"hello");
assert_eq!(blob.as_name(), "$BLOBDIR/foo");
assert_eq!(blob.to_abs_path(), t.ctx.get_blobdir().join("foo"));
}
#[test]
fn test_lowercase_ext() {
let t = dummy_context();
let blob = BlobObject::create(&t.ctx, "foo.TXT", b"hello").unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/foo.txt");
}
#[test]
fn test_as_file_name() {
let t = dummy_context();
let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap();
assert_eq!(blob.as_file_name(), "foo.txt");
}
#[test]
fn test_as_rel_path() {
let t = dummy_context();
let blob = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap();
assert_eq!(blob.as_rel_path(), Path::new("foo.txt"));
}
#[test]
fn test_suffix() {
let t = dummy_context();
let foo = BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap();
assert_eq!(foo.suffix(), Some("txt"));
let bar = BlobObject::create(&t.ctx, "bar", b"world").unwrap();
assert_eq!(bar.suffix(), None);
}
#[test]
fn test_create_dup() {
let t = dummy_context();
BlobObject::create(&t.ctx, "foo.txt", b"hello").unwrap();
let foo = t.ctx.get_blobdir().join("foo.txt");
assert!(foo.exists());
BlobObject::create(&t.ctx, "foo.txt", b"world").unwrap();
for dirent in fs::read_dir(t.ctx.get_blobdir()).unwrap() {
let fname = dirent.unwrap().file_name();
if fname == foo.file_name().unwrap() {
assert_eq!(fs::read(&foo).unwrap(), b"hello");
} else {
let name = fname.to_str().unwrap();
assert!(name.starts_with("foo"));
assert!(name.ends_with(".txt"));
}
}
}
#[test]
fn test_double_ext_preserved() {
let t = dummy_context();
BlobObject::create(&t.ctx, "foo.tar.gz", b"hello").unwrap();
let foo = t.ctx.get_blobdir().join("foo.tar.gz");
assert!(foo.exists());
BlobObject::create(&t.ctx, "foo.tar.gz", b"world").unwrap();
for dirent in fs::read_dir(t.ctx.get_blobdir()).unwrap() {
let fname = dirent.unwrap().file_name();
if fname == foo.file_name().unwrap() {
assert_eq!(fs::read(&foo).unwrap(), b"hello");
} else {
let name = fname.to_str().unwrap();
println!("{}", name);
assert!(name.starts_with("foo"));
assert!(name.ends_with(".tar.gz"));
}
}
}
#[test]
fn test_create_long_names() {
let t = dummy_context();
let s = "1".repeat(150);
let blob = BlobObject::create(&t.ctx, &s, b"data").unwrap();
let blobname = blob.as_name().split('/').last().unwrap();
assert!(blobname.len() < 128);
}
#[test]
fn test_create_and_copy() {
let t = dummy_context();
let src = t.dir.path().join("src");
fs::write(&src, b"boo").unwrap();
let blob = BlobObject::create_and_copy(&t.ctx, &src).unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/src");
let data = fs::read(blob.to_abs_path()).unwrap();
assert_eq!(data, b"boo");
let whoops = t.dir.path().join("whoops");
assert!(BlobObject::create_and_copy(&t.ctx, &whoops).is_err());
let whoops = t.ctx.get_blobdir().join("whoops");
assert!(!whoops.exists());
}
#[test]
fn test_create_from_path() {
let t = dummy_context();
let src_ext = t.dir.path().join("external");
fs::write(&src_ext, b"boo").unwrap();
let blob = BlobObject::create_from_path(&t.ctx, &src_ext).unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/external");
let data = fs::read(blob.to_abs_path()).unwrap();
assert_eq!(data, b"boo");
let src_int = t.ctx.get_blobdir().join("internal");
fs::write(&src_int, b"boo").unwrap();
let blob = BlobObject::create_from_path(&t.ctx, &src_int).unwrap();
assert_eq!(blob.as_name(), "$BLOBDIR/internal");
let data = fs::read(blob.to_abs_path()).unwrap();
assert_eq!(data, b"boo");
}
#[test]
fn test_create_from_name_long() {
let t = dummy_context();
let src_ext = t.dir.path().join("autocrypt-setup-message-4137848473.html");
fs::write(&src_ext, b"boo").unwrap();
let blob = BlobObject::create_from_path(&t.ctx, &src_ext).unwrap();
assert_eq!(
blob.as_name(),
"$BLOBDIR/autocrypt-setup-message-4137848473.html"
);
}
#[test]
fn test_is_blob_name() {
assert!(BlobObject::is_acceptible_blob_name("foo"));
assert!(BlobObject::is_acceptible_blob_name("foo.txt"));
assert!(BlobObject::is_acceptible_blob_name("f".repeat(128)));
assert!(!BlobObject::is_acceptible_blob_name("foo/bar"));
assert!(!BlobObject::is_acceptible_blob_name("foo\\bar"));
assert!(!BlobObject::is_acceptible_blob_name("foo\x00bar"));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,10 @@
//! # Chat list module
use crate::chat::*;
use crate::constants::*;
use crate::contact::*;
use crate::context::*;
use crate::error::Result;
use crate::lot::Lot;
use crate::message::{Message, MsgId};
use crate::message::*;
use crate::stock::StockMessage;
/// An object representing a single chatlist in memory.
@@ -36,7 +34,7 @@ use crate::stock::StockMessage;
#[derive(Debug)]
pub struct Chatlist {
/// Stores pairs of `chat_id, message_id`
ids: Vec<(u32, MsgId)>,
ids: Vec<(u32, u32)>,
}
impl Chatlist {
@@ -88,12 +86,25 @@ impl Chatlist {
query: Option<&str>,
query_contact_id: Option<u32>,
) -> Result<Self> {
let mut add_archived_link_item = false;
let mut add_archived_link_item = 0;
// select with left join and minimum:
// - the inner select must use `hidden` and _not_ `m.hidden`
// which would refer the outer select and take a lot of time
// - `GROUP BY` is needed several messages may have the same timestamp
// - the list starts with the newest chats
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let process_row = |row: &rusqlite::Row| {
let chat_id: u32 = row.get(0)?;
let msg_id: MsgId = row.get(1).unwrap_or_default();
Ok((chat_id, msg_id))
let chat_id: i32 = row.get(0)?;
// TODO: verify that it is okay for this to be Null
let msg_id: i32 = row.get(1).unwrap_or_default();
Ok((chat_id as u32, msg_id as u32))
};
let process_rows = |rows: rusqlite::MappedRows<_>| {
@@ -101,63 +112,36 @@ impl Chatlist {
.map_err(Into::into)
};
// select with left join and minimum:
//
// - the inner select must use `hidden` and _not_ `m.hidden`
// which would refer the outer select and take a lot of time
// - `GROUP BY` is needed several messages may have the same
// timestamp
// - the list starts with the newest chats
//
// nb: the query currently shows messages from blocked
// contacts in groups. however, for normal-groups, this is
// okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and
// tg do the same) for the deaddrop, however, they should
// really be hidden, however, _currently_ the deaddrop is not
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let mut ids = if let Some(query_contact_id) = query_contact_id {
// show chats shared with a given contact
context.sql.query_map(
concat!(
"SELECT c.id, m.id",
" FROM chats c",
" LEFT JOIN msgs m",
" ON c.id=m.chat_id",
" AND m.timestamp=(",
" SELECT MAX(timestamp)",
" FROM msgs",
" WHERE chat_id=c.id",
" AND hidden=0)",
" WHERE c.id>9",
" AND c.blocked=0",
" AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?)",
" GROUP BY c.id",
" ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;"
),
params![query_contact_id as i32],
process_row,
process_rows,
)?
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![query_contact_id as i32],
process_row,
process_rows,
)?
} else if 0 != listflags & DC_GCL_ARCHIVED_ONLY {
// show archived chats
context.sql.query_map(
concat!(
"SELECT c.id, m.id",
" FROM chats c",
" LEFT JOIN msgs m",
" ON c.id=m.chat_id",
" AND m.timestamp=(",
" SELECT MAX(timestamp)",
" FROM msgs",
" WHERE chat_id=c.id",
" AND hidden=0)",
" WHERE c.id>9",
" AND c.blocked=0",
" AND c.archived=1",
" GROUP BY c.id",
" ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;"
),
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=1 GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
@@ -168,22 +152,13 @@ impl Chatlist {
let str_like_cmd = format!("%{}%", query);
context.sql.query_map(
concat!(
"SELECT c.id, m.id",
" FROM chats c",
" LEFT JOIN msgs m",
" ON c.id=m.chat_id",
" AND m.timestamp=(",
" SELECT MAX(timestamp)",
" FROM msgs",
" WHERE chat_id=c.id",
" AND hidden=0)",
" WHERE c.id>9",
" AND c.blocked=0",
" AND c.name LIKE ?",
" GROUP BY c.id",
" ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;"
),
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.name LIKE ? \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![str_like_cmd],
process_row,
process_rows,
@@ -191,40 +166,34 @@ impl Chatlist {
} else {
// show normal chatlist
let mut ids = context.sql.query_map(
concat!(
"SELECT c.id, m.id",
" FROM chats c",
" LEFT JOIN msgs m",
" ON c.id=m.chat_id",
" AND m.timestamp=(",
" SELECT MAX(timestamp)",
" FROM msgs",
" WHERE chat_id=c.id",
" AND hidden=0)",
" WHERE c.id>9",
" AND c.blocked=0",
" AND c.archived=0",
" GROUP BY c.id",
" ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;"
),
"SELECT c.id, m.id FROM chats c \
LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=0 \
GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
)?;
if 0 == listflags & DC_GCL_NO_SPECIALS {
if let Some(last_deaddrop_fresh_msg_id) = get_last_deaddrop_fresh_msg(context) {
ids.insert(0, (DC_CHAT_ID_DEADDROP, last_deaddrop_fresh_msg_id));
let last_deaddrop_fresh_msg_id = get_last_deaddrop_fresh_msg(context);
if last_deaddrop_fresh_msg_id > 0 {
ids.push((1, last_deaddrop_fresh_msg_id));
}
add_archived_link_item = true;
add_archived_link_item = 1;
}
ids
};
if add_archived_link_item && dc_get_archived_cnt(context) > 0 {
if 0 != add_archived_link_item && dc_get_archived_cnt(context) > 0 {
if ids.is_empty() && 0 != listflags & DC_GCL_ADD_ALLDONE_HINT {
ids.push((DC_CHAT_ID_ALLDONE_HINT, MsgId::new(0)));
ids.push((DC_CHAT_ID_ALLDONE_HINT, 0));
}
ids.push((DC_CHAT_ID_ARCHIVED_LINK, MsgId::new(0)));
ids.push((DC_CHAT_ID_ARCHIVED_LINK, 0));
}
Ok(Chatlist { ids })
@@ -235,7 +204,6 @@ impl Chatlist {
self.ids.len()
}
/// Returns true if chatlist is empty.
pub fn is_empty(&self) -> bool {
self.ids.is_empty()
}
@@ -253,9 +221,12 @@ impl Chatlist {
/// Get a single message ID of a chatlist.
///
/// To get the message object from the message ID, use dc_get_msg().
pub fn get_msg_id(&self, index: usize) -> Result<MsgId> {
ensure!(index < self.ids.len(), "Chatlist index out of range");
Ok(self.ids[index].1)
pub fn get_msg_id(&self, index: usize) -> u32 {
if index >= self.ids.len() {
return 0;
}
self.ids[index].1
}
/// Get a summary for a chatlist index.
@@ -287,38 +258,34 @@ impl Chatlist {
let chat_loaded: Chat;
let chat = if let Some(chat) = chat {
chat
} else if let Ok(chat) = Chat::load_from_db(context, self.ids[index].0) {
chat_loaded = chat;
&chat_loaded
} else {
return ret;
if let Ok(chat) = Chat::load_from_db(context, self.ids[index].0) {
chat_loaded = chat;
&chat_loaded
} else {
return ret;
}
};
let lastmsg_id = self.ids[index].1;
let mut lastcontact = None;
let mut lastmsg = if let Ok(lastmsg) = Message::load_from_db(context, lastmsg_id) {
if lastmsg.from_id != DC_CONTACT_ID_SELF
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
{
lastcontact = Contact::load_from_db(context, lastmsg.from_id).ok();
}
let lastmsg = if 0 != lastmsg_id {
if let Ok(lastmsg) = dc_msg_load_from_db(context, lastmsg_id) {
if lastmsg.from_id != 1 as libc::c_uint
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
{
lastcontact = Contact::load_from_db(context, lastmsg.from_id).ok();
}
Some(lastmsg)
Some(lastmsg)
} else {
None
}
} else {
None
};
if let Ok(draft) = get_draft(context, chat.id) {
if draft.is_some()
&& (lastmsg.is_none()
|| draft.as_ref().unwrap().timestamp_sort
> lastmsg.as_ref().unwrap().timestamp_sort)
{
lastmsg = draft;
}
}
if chat.id == DC_CHAT_ID_ARCHIVED_LINK {
ret.text2 = None;
} else if lastmsg.is_none() || lastmsg.as_ref().unwrap().from_id == DC_CONTACT_ID_UNDEFINED
@@ -332,7 +299,6 @@ impl Chatlist {
}
}
/// Get the number of archived chats
pub fn dc_get_archived_cnt(context: &Context) -> u32 {
context
.sql
@@ -344,21 +310,19 @@ pub fn dc_get_archived_cnt(context: &Context) -> u32 {
.unwrap_or_default()
}
fn get_last_deaddrop_fresh_msg(context: &Context) -> Option<MsgId> {
// We have an index over the state-column, this should be
// sufficient as there are typically only few fresh messages.
context.sql.query_get_value(
context,
concat!(
"SELECT m.id",
" FROM msgs m",
" LEFT JOIN chats c",
" ON c.id=m.chat_id",
" WHERE m.state=10",
" AND m.hidden=0",
" AND c.blocked=2",
" ORDER BY m.timestamp DESC, m.id DESC;"
),
params![],
)
fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 {
// We have an index over the state-column, this should be sufficient as there are typically
// only few fresh messages.
context
.sql
.query_get_value(
context,
"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \
WHERE m.state=10 \
AND m.hidden=0 \
AND c.blocked=2 \
ORDER BY m.timestamp DESC, m.id DESC;",
params![],
)
.unwrap_or_default()
}

View File

@@ -1,9 +1,6 @@
//! # Key-value configuration management
use strum::{EnumProperty, IntoEnumIterator};
use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
use crate::blob::BlobObject;
use crate::constants::DC_VERSION_STR;
use crate::context::Context;
use crate::dc_tools::*;
@@ -22,20 +19,16 @@ pub enum Config {
MailUser,
MailPw,
MailPort,
ImapCertificateChecks,
SendServer,
SendUser,
SendPw,
SendPort,
SmtpCertificateChecks,
ServerFlags,
#[strum(props(default = "INBOX"))]
ImapFolder,
Displayname,
Selfstatus,
Selfavatar,
#[strum(props(default = "0"))]
BccSelf,
#[strum(props(default = "1"))]
E2eeEnabled,
#[strum(props(default = "1"))]
@@ -48,7 +41,7 @@ pub enum Config {
MvboxWatch,
#[strum(props(default = "1"))]
MvboxMove,
#[strum(props(default = "0"))] // also change ShowEmails.default() on changes
#[strum(props(default = "0"))]
ShowEmails,
SaveMimeHeaders,
ConfiguredAddr,
@@ -57,12 +50,10 @@ pub enum Config {
ConfiguredMailPw,
ConfiguredMailPort,
ConfiguredMailSecurity,
ConfiguredImapCertificateChecks,
ConfiguredSendServer,
ConfiguredSendUser,
ConfiguredSendPw,
ConfiguredSendPort,
ConfiguredSmtpCertificateChecks,
ConfiguredServerFlags,
ConfiguredSendSecurity,
ConfiguredE2EEEnabled,
@@ -81,13 +72,13 @@ impl Context {
pub fn get_config(&self, key: Config) -> Option<String> {
let value = match key {
Config::Selfavatar => {
let rel_path = self.sql.get_raw_config(self, key);
rel_path.map(|p| dc_get_abs_path(self, &p).to_string_lossy().into_owned())
let rel_path = self.sql.get_config(self, key);
rel_path.map(|p| dc_get_abs_path(self, &p).to_str().unwrap().to_string())
}
Config::SysVersion => Some((&*DC_VERSION_STR).clone()),
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
Config::SysConfigKeys => Some(get_config_keys_string()),
_ => self.sql.get_raw_config(self, key),
_ => self.sql.get_config(self, key),
};
if value.is_some() {
@@ -101,36 +92,27 @@ impl Context {
}
}
pub fn get_config_int(&self, key: Config) -> i32 {
self.get_config(key)
.and_then(|s| s.parse().ok())
.unwrap_or_default()
}
pub fn get_config_bool(&self, key: Config) -> bool {
self.get_config_int(key) != 0
}
/// Set the given config key.
/// If `None` is passed as a value the value is cleared and set to the default if there is one.
pub fn set_config(&self, key: Config, value: Option<&str>) -> Result<(), Error> {
match key {
Config::Selfavatar if value.is_some() => {
let blob = BlobObject::create_from_path(&self, value.unwrap())?;
self.sql.set_raw_config(self, key, Some(blob.as_name()))
let rel_path = std::fs::canonicalize(value.unwrap())?;
self.sql
.set_config(self, key, Some(&rel_path.to_string_lossy()))
}
Config::InboxWatch => {
let ret = self.sql.set_raw_config(self, key, value);
interrupt_inbox_idle(self, true);
let ret = self.sql.set_config(self, key, value);
interrupt_imap_idle(self);
ret
}
Config::SentboxWatch => {
let ret = self.sql.set_raw_config(self, key, value);
let ret = self.sql.set_config(self, key, value);
interrupt_sentbox_idle(self);
ret
}
Config::MvboxWatch => {
let ret = self.sql.set_raw_config(self, key, value);
let ret = self.sql.set_config(self, key, value);
interrupt_mvbox_idle(self);
ret
}
@@ -142,9 +124,9 @@ impl Context {
value
};
self.sql.set_raw_config(self, key, val)
self.sql.set_config(self, key, val)
}
_ => self.sql.set_raw_config(self, key, value),
_ => self.sql.set_config(self, key, value),
}
}
}
@@ -167,8 +149,6 @@ mod tests {
use std::str::FromStr;
use std::string::ToString;
use crate::test_utils::*;
#[test]
fn test_to_string() {
assert_eq!(Config::MailServer.to_string(), "mail_server");
@@ -185,32 +165,4 @@ mod tests {
fn test_default_prop() {
assert_eq!(Config::ImapFolder.get_str("default"), Some("INBOX"));
}
#[test]
fn test_selfavatar() -> failure::Fallible<()> {
let t = dummy_context();
let avatar_src = t.dir.path().join("avatar.jpg");
std::fs::write(&avatar_src, b"avatar")?;
let avatar_blob = t.ctx.get_blobdir().join("avatar.jpg");
assert!(!avatar_blob.exists());
t.ctx
.set_config(Config::Selfavatar, Some(&avatar_src.to_str().unwrap()))?;
assert!(avatar_blob.exists());
assert_eq!(std::fs::read(&avatar_blob)?, b"avatar");
let avatar_cfg = t.ctx.get_config(Config::Selfavatar);
assert_eq!(avatar_cfg, avatar_blob.to_str().map(|s| s.to_string()));
Ok(())
}
#[test]
fn test_selfavatar_in_blobdir() -> failure::Fallible<()> {
let t = dummy_context();
let avatar_src = t.ctx.get_blobdir().join("avatar.jpg");
std::fs::write(&avatar_src, b"avatar")?;
t.ctx
.set_config(Config::Selfavatar, Some(&avatar_src.to_str().unwrap()))?;
let avatar_cfg = t.ctx.get_config(Config::Selfavatar);
assert_eq!(avatar_cfg, avatar_src.to_str().map(|s| s.to_string()));
Ok(())
}
}

View File

@@ -1,67 +1,63 @@
//! Thunderbird's Autoconfiguration implementation
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::constants::*;
use crate::context::Context;
use crate::error::Error;
use crate::dc_tools::*;
use crate::login_param::LoginParam;
use crate::x::*;
use super::read_autoconf_file;
/* ******************************************************************************
* Thunderbird's Autoconfigure
******************************************************************************/
/* documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
struct MozAutoconfigure<'a> {
pub in_emailaddr: &'a str,
#[repr(C)]
struct moz_autoconfigure_t<'a> {
pub in_0: &'a LoginParam,
pub in_emaildomain: &'a str,
pub in_emaillocalpart: &'a str,
pub out: LoginParam,
pub out_imap_set: bool,
pub out_smtp_set: bool,
pub tag_server: MozServer,
pub tag_config: MozConfigTag,
pub out_imap_set: libc::c_int,
pub out_smtp_set: libc::c_int,
pub tag_server: libc::c_int,
pub tag_config: libc::c_int,
}
#[derive(PartialEq)]
enum MozServer {
Undefined,
Imap,
Smtp,
}
enum MozConfigTag {
Undefined,
Hostname,
Port,
Sockettype,
Username,
}
pub fn moz_parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam, Error> {
let mut reader = quick_xml::Reader::from_str(xml_raw);
reader.trim_text(true);
pub unsafe fn moz_autoconfigure(
context: &Context,
url: &str,
param_in: &LoginParam,
) -> Option<LoginParam> {
let xml_raw = read_autoconf_file(context, url);
if xml_raw.is_null() {
return None;
}
// Split address into local part and domain part.
let p = match in_emailaddr.find('@') {
Some(i) => i,
None => bail!("Email address {} does not contain @", in_emailaddr),
};
let (in_emaillocalpart, in_emaildomain) = in_emailaddr.split_at(p);
let p = param_in.addr.find("@");
if p.is_none() {
free(xml_raw as *mut libc::c_void);
return None;
}
let (in_emaillocalpart, in_emaildomain) = param_in.addr.split_at(p.unwrap());
let in_emaildomain = &in_emaildomain[1..];
let mut moz_ac = MozAutoconfigure {
in_emailaddr,
let mut reader = quick_xml::Reader::from_str(as_str(xml_raw));
reader.trim_text(true);
let mut buf = Vec::new();
let mut moz_ac = moz_autoconfigure_t {
in_0: param_in,
in_emaildomain,
in_emaillocalpart,
out: LoginParam::new(),
out_imap_set: false,
out_smtp_set: false,
tag_server: MozServer::Undefined,
tag_config: MozConfigTag::Undefined,
out_imap_set: 0,
out_smtp_set: 0,
tag_server: 0,
tag_config: 0,
};
let mut buf = Vec::new();
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
@@ -72,7 +68,8 @@ pub fn moz_parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam, Er
moz_autoconfigure_text_cb(e, &mut moz_ac, &reader)
}
Err(e) => {
bail!(
error!(
context,
"Configure xml: Error at position {}: {:?}",
reader.buffer_position(),
e
@@ -90,36 +87,23 @@ pub fn moz_parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam, Er
|| moz_ac.out.send_port == 0
{
let r = moz_ac.out.to_string();
bail!("Bad or incomplete autoconfig: {}", r,);
warn!(context, "Bad or incomplete autoconfig: {}", r,);
free(xml_raw as *mut libc::c_void);
return None;
}
Ok(moz_ac.out)
}
pub fn moz_autoconfigure(
context: &Context,
url: &str,
param_in: &LoginParam,
) -> Option<LoginParam> {
let xml_raw = read_autoconf_file(context, url)?;
match moz_parse_xml(&param_in.addr, &xml_raw) {
Err(err) => {
warn!(context, "{}", err);
None
}
Ok(lp) => Some(lp),
}
free(xml_raw as *mut libc::c_void);
Some(moz_ac.out)
}
fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
event: &BytesText,
moz_ac: &mut MozAutoconfigure,
moz_ac: &mut moz_autoconfigure_t,
reader: &quick_xml::Reader<B>,
) {
let val = event.unescape_and_decode(reader).unwrap_or_default();
let addr = moz_ac.in_emailaddr;
let addr = &moz_ac.in_0.addr;
let email_local = moz_ac.in_emaillocalpart;
let email_domain = moz_ac.in_emaildomain;
@@ -129,12 +113,12 @@ fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
.replace("%EMAILLOCALPART%", email_local)
.replace("%EMAILDOMAIN%", email_domain);
match moz_ac.tag_server {
MozServer::Imap => match moz_ac.tag_config {
MozConfigTag::Hostname => moz_ac.out.mail_server = val,
MozConfigTag::Port => moz_ac.out.mail_port = val.parse().unwrap_or_default(),
MozConfigTag::Username => moz_ac.out.mail_user = val,
MozConfigTag::Sockettype => {
if moz_ac.tag_server == 1 {
match moz_ac.tag_config {
10 => moz_ac.out.mail_server = val,
11 => moz_ac.out.mail_port = val.parse().unwrap_or_default(),
12 => moz_ac.out.mail_user = val,
13 => {
let val_lower = val.to_lowercase();
if val_lower == "ssl" {
moz_ac.out.server_flags |= DC_LP_IMAP_SOCKET_SSL as i32
@@ -147,12 +131,13 @@ fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
}
}
_ => {}
},
MozServer::Smtp => match moz_ac.tag_config {
MozConfigTag::Hostname => moz_ac.out.send_server = val,
MozConfigTag::Port => moz_ac.out.send_port = val.parse().unwrap_or_default(),
MozConfigTag::Username => moz_ac.out.send_user = val,
MozConfigTag::Sockettype => {
}
} else if moz_ac.tag_server == 2 {
match moz_ac.tag_config {
10 => moz_ac.out.send_server = val,
11 => moz_ac.out.send_port = val.parse().unwrap_or_default(),
12 => moz_ac.out.send_user = val,
13 => {
let val_lower = val.to_lowercase();
if val_lower == "ssl" {
moz_ac.out.server_flags |= DC_LP_SMTP_SOCKET_SSL as i32
@@ -165,34 +150,29 @@ fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
}
}
_ => {}
},
MozServer::Undefined => {}
}
}
}
fn moz_autoconfigure_endtag_cb(event: &BytesEnd, moz_ac: &mut MozAutoconfigure) {
fn moz_autoconfigure_endtag_cb(event: &BytesEnd, moz_ac: &mut moz_autoconfigure_t) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
if tag == "incomingserver" {
if moz_ac.tag_server == MozServer::Imap {
moz_ac.out_imap_set = true;
}
moz_ac.tag_server = MozServer::Undefined;
moz_ac.tag_config = MozConfigTag::Undefined;
moz_ac.tag_server = 0;
moz_ac.tag_config = 0;
moz_ac.out_imap_set = 1;
} else if tag == "outgoingserver" {
if moz_ac.tag_server == MozServer::Smtp {
moz_ac.out_smtp_set = true;
}
moz_ac.tag_server = MozServer::Undefined;
moz_ac.tag_config = MozConfigTag::Undefined;
moz_ac.tag_server = 0;
moz_ac.tag_config = 0;
moz_ac.out_smtp_set = 1;
} else {
moz_ac.tag_config = MozConfigTag::Undefined;
moz_ac.tag_config = 0;
}
}
fn moz_autoconfigure_starttag_cb<B: std::io::BufRead>(
event: &BytesStart,
moz_ac: &mut MozAutoconfigure,
moz_ac: &mut moz_autoconfigure_t,
reader: &quick_xml::Reader<B>,
) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
@@ -209,115 +189,25 @@ fn moz_autoconfigure_starttag_cb<B: std::io::BufRead>(
.unwrap_or_default()
.to_lowercase();
if typ == "imap" && !moz_ac.out_imap_set {
MozServer::Imap
if typ == "imap" && moz_ac.out_imap_set == 0 {
1
} else {
MozServer::Undefined
0
}
} else {
MozServer::Undefined
0
};
moz_ac.tag_config = MozConfigTag::Undefined;
moz_ac.tag_config = 0;
} else if tag == "outgoingserver" {
moz_ac.tag_server = if !moz_ac.out_smtp_set {
MozServer::Smtp
} else {
MozServer::Undefined
};
moz_ac.tag_config = MozConfigTag::Undefined;
moz_ac.tag_server = if moz_ac.out_smtp_set == 0 { 2 } else { 0 };
moz_ac.tag_config = 0;
} else if tag == "hostname" {
moz_ac.tag_config = MozConfigTag::Hostname;
moz_ac.tag_config = 10;
} else if tag == "port" {
moz_ac.tag_config = MozConfigTag::Port;
moz_ac.tag_config = 11;
} else if tag == "sockettype" {
moz_ac.tag_config = MozConfigTag::Sockettype;
moz_ac.tag_config = 13;
} else if tag == "username" {
moz_ac.tag_config = MozConfigTag::Username;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_outlook_autoconfig() {
// Copied from https://autoconfig.thunderbird.net/v1.1/outlook.com on 2019-10-11
let xml_raw =
"<clientConfig version=\"1.1\">
<emailProvider id=\"outlook.com\">
<domain>hotmail.com</domain>
<domain>hotmail.co.uk</domain>
<domain>hotmail.co.jp</domain>
<domain>hotmail.com.br</domain>
<domain>hotmail.de</domain>
<domain>hotmail.fr</domain>
<domain>hotmail.it</domain>
<domain>hotmail.es</domain>
<domain>live.com</domain>
<domain>live.co.uk</domain>
<domain>live.co.jp</domain>
<domain>live.de</domain>
<domain>live.fr</domain>
<domain>live.it</domain>
<domain>live.jp</domain>
<domain>msn.com</domain>
<domain>outlook.com</domain>
<displayName>Outlook.com (Microsoft)</displayName>
<displayShortName>Outlook</displayShortName>
<incomingServer type=\"exchange\">
<hostname>outlook.office365.com</hostname>
<port>443</port>
<username>%EMAILADDRESS%</username>
<socketType>SSL</socketType>
<authentication>OAuth2</authentication>
<owaURL>https://outlook.office365.com/owa/</owaURL>
<ewsURL>https://outlook.office365.com/ews/exchange.asmx</ewsURL>
<useGlobalPreferredServer>true</useGlobalPreferredServer>
</incomingServer>
<incomingServer type=\"imap\">
<hostname>outlook.office365.com</hostname>
<port>993</port>
<socketType>SSL</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
</incomingServer>
<incomingServer type=\"pop3\">
<hostname>outlook.office365.com</hostname>
<port>995</port>
<socketType>SSL</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
<pop3>
<leaveMessagesOnServer>true</leaveMessagesOnServer>
<!-- Outlook.com docs specifically mention that POP3 deletes have effect on the main inbox on webmail and IMAP -->
</pop3>
</incomingServer>
<outgoingServer type=\"smtp\">
<hostname>smtp.office365.com</hostname>
<port>587</port>
<socketType>STARTTLS</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
</outgoingServer>
<documentation url=\"http://windows.microsoft.com/en-US/windows/outlook/send-receive-from-app\">
<descr lang=\"en\">Set up an email app with Outlook.com</descr>
</documentation>
</emailProvider>
<webMail>
<loginPage url=\"https://www.outlook.com/\"/>
<loginPageInfo url=\"https://www.outlook.com/\">
<username>%EMAILADDRESS%</username>
<usernameField id=\"i0116\" name=\"login\"/>
<passwordField id=\"i0118\" name=\"passwd\"/>
<loginButton id=\"idSIButton9\" name=\"SI\"/>
</loginPageInfo>
</webMail>
</clientConfig>";
let res = moz_parse_xml("example@outlook.com", xml_raw).expect("XML parsing failed");
assert_eq!(res.mail_server, "outlook.office365.com");
assert_eq!(res.mail_port, 993);
assert_eq!(res.send_server, "smtp.office365.com");
assert_eq!(res.send_port, 587);
moz_ac.tag_config = 12;
}
}

View File

@@ -1,249 +1,213 @@
use quick_xml;
use quick_xml::events::BytesEnd;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::constants::*;
use crate::context::Context;
use crate::error::Error;
use crate::dc_tools::*;
use crate::login_param::LoginParam;
use crate::x::*;
use std::ptr;
use super::read_autoconf_file;
/// Outlook's Autodiscover
struct OutlookAutodiscover {
/* ******************************************************************************
* Outlook's Autodiscover
******************************************************************************/
#[repr(C)]
struct outlk_autodiscover_t<'a> {
pub in_0: &'a LoginParam,
pub out: LoginParam,
pub out_imap_set: bool,
pub out_smtp_set: bool,
pub config_type: Option<String>,
pub config_server: String,
pub config_port: i32,
pub config_ssl: String,
pub config_redirecturl: Option<String>,
pub out_imap_set: libc::c_int,
pub out_smtp_set: libc::c_int,
pub tag_config: libc::c_int,
pub config: [*mut libc::c_char; 6],
pub redirect: *mut libc::c_char,
}
enum ParsingResult {
LoginParam(LoginParam),
RedirectUrl(String),
}
fn outlk_parse_xml(xml_raw: &str) -> Result<ParsingResult, Error> {
let mut outlk_ad = OutlookAutodiscover {
pub unsafe fn outlk_autodiscover(
context: &Context,
url__: &str,
param_in: &LoginParam,
) -> Option<LoginParam> {
let mut xml_raw: *mut libc::c_char = ptr::null_mut();
let mut url = url__.strdup();
let mut outlk_ad = outlk_autodiscover_t {
in_0: param_in,
out: LoginParam::new(),
out_imap_set: false,
out_smtp_set: false,
config_type: None,
config_server: String::new(),
config_port: 0,
config_ssl: String::new(),
config_redirecturl: None,
out_imap_set: 0,
out_smtp_set: 0,
tag_config: 0,
config: [ptr::null_mut(); 6],
redirect: ptr::null_mut(),
};
let mut reader = quick_xml::Reader::from_str(&xml_raw);
reader.trim_text(true);
let mut buf = Vec::new();
let mut current_tag: Option<String> = None;
let ok_to_continue;
let mut i = 0;
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
let tag = String::from_utf8_lossy(e.name()).trim().to_lowercase();
if tag == "protocol" {
outlk_ad.config_type = None;
outlk_ad.config_server = String::new();
outlk_ad.config_port = 0;
outlk_ad.config_ssl = String::new();
outlk_ad.config_redirecturl = None;
current_tag = None;
} else {
current_tag = Some(tag);
}
}
Ok(quick_xml::events::Event::End(ref e)) => {
outlk_autodiscover_endtag_cb(e, &mut outlk_ad);
current_tag = None;
}
Ok(quick_xml::events::Event::Text(ref e)) => {
let val = e.unescape_and_decode(&reader).unwrap_or_default();
if let Some(ref tag) = current_tag {
match tag.as_str() {
"type" => {
outlk_ad.config_type = Some(val.trim().to_lowercase().to_string())
}
"server" => outlk_ad.config_server = val.trim().to_string(),
"port" => outlk_ad.config_port = val.trim().parse().unwrap_or_default(),
"ssl" => outlk_ad.config_ssl = val.trim().to_string(),
"redirecturl" => outlk_ad.config_redirecturl = Some(val.trim().to_string()),
_ => {}
};
}
}
Err(e) => {
bail!(
"Configure xml: Error at position {}: {:?}",
reader.buffer_position(),
e
);
}
Ok(quick_xml::events::Event::Eof) => break,
_ => (),
if !(i < 10) {
ok_to_continue = true;
break;
}
buf.clear();
memset(
&mut outlk_ad as *mut outlk_autodiscover_t as *mut libc::c_void,
0,
::std::mem::size_of::<outlk_autodiscover_t>(),
);
xml_raw = read_autoconf_file(context, as_str(url));
if xml_raw.is_null() {
ok_to_continue = false;
break;
}
let mut reader = quick_xml::Reader::from_str(as_str(xml_raw));
reader.trim_text(true);
let mut buf = Vec::new();
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
outlk_autodiscover_starttag_cb(e, &mut outlk_ad)
}
Ok(quick_xml::events::Event::End(ref e)) => {
outlk_autodiscover_endtag_cb(e, &mut outlk_ad)
}
Ok(quick_xml::events::Event::Text(ref e)) => {
outlk_autodiscover_text_cb(e, &mut outlk_ad, &reader)
}
Err(e) => {
error!(
context,
"Configure xml: Error at position {}: {:?}",
reader.buffer_position(),
e
);
}
Ok(quick_xml::events::Event::Eof) => break,
_ => (),
}
buf.clear();
}
if !(!outlk_ad.config[5].is_null()
&& 0 != *outlk_ad.config[5usize].offset(0isize) as libc::c_int)
{
ok_to_continue = true;
break;
}
free(url as *mut libc::c_void);
url = dc_strdup(outlk_ad.config[5usize]);
outlk_clean_config(&mut outlk_ad);
free(xml_raw as *mut libc::c_void);
xml_raw = ptr::null_mut();
i += 1;
}
// XML redirect via redirecturl
if outlk_ad.config_redirecturl.is_none()
|| outlk_ad.config_redirecturl.as_ref().unwrap().is_empty()
{
if ok_to_continue {
if outlk_ad.out.mail_server.is_empty()
|| outlk_ad.out.mail_port == 0
|| outlk_ad.out.send_server.is_empty()
|| outlk_ad.out.send_port == 0
{
let r = outlk_ad.out.to_string();
bail!("Bad or incomplete autoconfig: {}", r,);
}
Ok(ParsingResult::LoginParam(outlk_ad.out))
} else {
Ok(ParsingResult::RedirectUrl(
outlk_ad.config_redirecturl.unwrap(),
))
}
}
warn!(context, "Bad or incomplete autoconfig: {}", r,);
free(url as *mut libc::c_void);
free(xml_raw as *mut libc::c_void);
outlk_clean_config(&mut outlk_ad);
pub fn outlk_autodiscover(
context: &Context,
url: &str,
_param_in: &LoginParam,
) -> Option<LoginParam> {
let mut url = url.to_string();
/* Follow up to 10 xml-redirects (http-redirects are followed in read_autoconf_file() */
for _i in 0..10 {
if let Some(xml_raw) = read_autoconf_file(context, &url) {
match outlk_parse_xml(&xml_raw) {
Err(err) => {
warn!(context, "{}", err);
return None;
}
Ok(res) => match res {
ParsingResult::RedirectUrl(redirect_url) => url = redirect_url,
ParsingResult::LoginParam(login_param) => return Some(login_param),
},
}
} else {
return None;
}
}
None
free(url as *mut libc::c_void);
free(xml_raw as *mut libc::c_void);
outlk_clean_config(&mut outlk_ad);
Some(outlk_ad.out)
}
fn outlk_autodiscover_endtag_cb(event: &BytesEnd, outlk_ad: &mut OutlookAutodiscover) {
unsafe fn outlk_clean_config(mut outlk_ad: *mut outlk_autodiscover_t) {
for i in 0..6 {
free((*outlk_ad).config[i] as *mut libc::c_void);
(*outlk_ad).config[i] = ptr::null_mut();
}
}
fn outlk_autodiscover_text_cb<B: std::io::BufRead>(
event: &BytesText,
outlk_ad: &mut outlk_autodiscover_t,
reader: &quick_xml::Reader<B>,
) {
let val = event.unescape_and_decode(reader).unwrap_or_default();
unsafe {
free(outlk_ad.config[outlk_ad.tag_config as usize].cast());
outlk_ad.config[outlk_ad.tag_config as usize] = val.trim().strdup();
}
}
unsafe fn outlk_autodiscover_endtag_cb(event: &BytesEnd, outlk_ad: &mut outlk_autodiscover_t) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
if tag == "protocol" {
if let Some(type_) = &outlk_ad.config_type {
let port = outlk_ad.config_port;
let ssl_on = outlk_ad.config_ssl == "on";
let ssl_off = outlk_ad.config_ssl == "off";
if type_ == "imap" && !outlk_ad.out_imap_set {
outlk_ad.out.mail_server =
std::mem::replace(&mut outlk_ad.config_server, String::new());
if !outlk_ad.config[1].is_null() {
let port = dc_atoi_null_is_0(outlk_ad.config[3]);
let ssl_on = (!outlk_ad.config[4].is_null()
&& strcasecmp(
outlk_ad.config[4],
b"on\x00" as *const u8 as *const libc::c_char,
) == 0) as libc::c_int;
let ssl_off = (!outlk_ad.config[4].is_null()
&& strcasecmp(
outlk_ad.config[4],
b"off\x00" as *const u8 as *const libc::c_char,
) == 0) as libc::c_int;
if strcasecmp(
outlk_ad.config[1],
b"imap\x00" as *const u8 as *const libc::c_char,
) == 0
&& outlk_ad.out_imap_set == 0
{
outlk_ad.out.mail_server = to_string(outlk_ad.config[2]);
outlk_ad.out.mail_port = port;
if ssl_on {
if 0 != ssl_on {
outlk_ad.out.server_flags |= DC_LP_IMAP_SOCKET_SSL as i32
} else if ssl_off {
} else if 0 != ssl_off {
outlk_ad.out.server_flags |= DC_LP_IMAP_SOCKET_PLAIN as i32
}
outlk_ad.out_imap_set = true
} else if type_ == "smtp" && !outlk_ad.out_smtp_set {
outlk_ad.out.send_server =
std::mem::replace(&mut outlk_ad.config_server, String::new());
outlk_ad.out.send_port = outlk_ad.config_port;
if ssl_on {
outlk_ad.out_imap_set = 1
} else if strcasecmp(
outlk_ad.config[1usize],
b"smtp\x00" as *const u8 as *const libc::c_char,
) == 0
&& outlk_ad.out_smtp_set == 0
{
outlk_ad.out.send_server = to_string(outlk_ad.config[2]);
outlk_ad.out.send_port = port;
if 0 != ssl_on {
outlk_ad.out.server_flags |= DC_LP_SMTP_SOCKET_SSL as i32
} else if ssl_off {
} else if 0 != ssl_off {
outlk_ad.out.server_flags |= DC_LP_SMTP_SOCKET_PLAIN as i32
}
outlk_ad.out_smtp_set = true
outlk_ad.out_smtp_set = 1
}
}
outlk_clean_config(outlk_ad);
}
outlk_ad.tag_config = 0;
}
#[cfg(test)]
mod tests {
use super::*;
fn outlk_autodiscover_starttag_cb(event: &BytesStart, outlk_ad: &mut outlk_autodiscover_t) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
#[test]
fn test_parse_redirect() {
let res = outlk_parse_xml("
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">
<Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">
<Account>
<AccountType>email</AccountType>
<Action>redirectUrl</Action>
<RedirectUrl>https://mail.example.com/autodiscover/autodiscover.xml</RedirectUrl>
</Account>
</Response>
</Autodiscover>
").expect("XML is not parsed successfully");
match res {
ParsingResult::LoginParam(_lp) => {
panic!("redirecturl is not found");
}
ParsingResult::RedirectUrl(url) => {
assert_eq!(
url,
"https://mail.example.com/autodiscover/autodiscover.xml"
);
}
}
}
#[test]
fn test_parse_loginparam() {
let res = outlk_parse_xml(
"\
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">
<Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">
<Account>
<AccountType>email</AccountType>
<Action>settings</Action>
<Protocol>
<Type>IMAP</Type>
<Server>example.com</Server>
<Port>993</Port>
<SSL>on</SSL>
<AuthRequired>on</AuthRequired>
</Protocol>
<Protocol>
<Type>SMTP</Type>
<Server>smtp.example.com</Server>
<Port>25</Port>
<SSL>off</SSL>
<AuthRequired>on</AuthRequired>
</Protocol>
</Account>
</Response>
</Autodiscover>",
)
.expect("XML is not parsed successfully");
match res {
ParsingResult::LoginParam(lp) => {
assert_eq!(lp.mail_server, "example.com");
assert_eq!(lp.mail_port, 993);
assert_eq!(lp.send_server, "smtp.example.com");
assert_eq!(lp.send_port, 25);
}
ParsingResult::RedirectUrl(_) => {
panic!("RedirectUrl is not expected");
}
}
}
if tag == "protocol" {
unsafe { outlk_clean_config(outlk_ad) };
} else if tag == "type" {
outlk_ad.tag_config = 1
} else if tag == "server" {
outlk_ad.tag_config = 2
} else if tag == "port" {
outlk_ad.tag_config = 3
} else if tag == "ssl" {
outlk_ad.tag_config = 4
} else if tag == "redirecturl" {
outlk_ad.tag_config = 5
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
//! # Constants
//! Constants
#![allow(non_camel_case_types, dead_code)]
use deltachat_derive::*;
@@ -8,8 +8,24 @@ lazy_static! {
pub static ref DC_VERSION_STR: String = env!("CARGO_PKG_VERSION").to_string();
}
#[repr(u8)]
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
pub enum MoveState {
Undefined = 0,
Pending = 1,
Stay = 2,
Moving = 3,
}
impl Default for MoveState {
fn default() -> Self {
MoveState::Undefined
}
}
// some defaults
const DC_E2EE_DEFAULT_ENABLED: i32 = 1;
pub const DC_MDNS_DEFAULT_ENABLED: i32 = 1;
const DC_INBOX_WATCH_DEFAULT: i32 = 1;
const DC_SENTBOX_WATCH_DEFAULT: i32 = 1;
const DC_MVBOX_WATCH_DEFAULT: i32 = 1;
@@ -29,20 +45,6 @@ impl Default for Blocked {
}
}
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
#[repr(u8)]
pub enum ShowEmails {
Off = 0,
AcceptedContacts = 1,
All = 2,
}
impl Default for ShowEmails {
fn default() -> Self {
ShowEmails::Off // also change Config.ShowEmails props(default) on changes
}
}
pub const DC_IMAP_SEEN: u32 = 0x1;
pub const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
@@ -53,14 +55,19 @@ pub const DC_GCL_ARCHIVED_ONLY: usize = 0x01;
pub const DC_GCL_NO_SPECIALS: usize = 0x02;
pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
pub const DC_GCM_ADDDAYMARKER: u32 = 0x01;
const DC_GCM_ADDDAYMARKER: usize = 0x01;
pub const DC_GCL_VERIFIED_ONLY: usize = 0x01;
pub const DC_GCL_ADD_SELF: usize = 0x02;
// values for DC_PARAM_FORCE_PLAINTEXT
pub(crate) const DC_FP_NO_AUTOCRYPT_HEADER: i32 = 2;
pub(crate) const DC_FP_ADD_AUTOCRYPT_HEADER: i32 = 1;
/// param1 is a directory where the keys are written to
const DC_IMEX_EXPORT_SELF_KEYS: usize = 1;
/// param1 is a directory where the keys are searched in and read from
const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
/// param1 is a directory where the backup is written to
const DC_IMEX_EXPORT_BACKUP: usize = 11;
/// param1 is the file with the backup to import
const DC_IMEX_IMPORT_BACKUP: usize = 12;
/// virtual chat showing all messages belonging to chats flagged with chats.blocked=2
pub(crate) const DC_CHAT_ID_DEADDROP: u32 = 1;
@@ -105,7 +112,7 @@ impl Default for Chattype {
}
pub const DC_MSG_ID_MARKER1: u32 = 1;
pub const DC_MSG_ID_DAYMARKER: u32 = 9;
const DC_MSG_ID_DAYMARKER: u32 = 9;
pub const DC_MSG_ID_LAST_SPECIAL: u32 = 9;
/// approx. max. length returned by dc_msg_get_text()
@@ -115,14 +122,10 @@ const DC_MAX_GET_INFO_LEN: usize = 100000;
pub const DC_CONTACT_ID_UNDEFINED: u32 = 0;
pub const DC_CONTACT_ID_SELF: u32 = 1;
pub const DC_CONTACT_ID_INFO: u32 = 2;
pub const DC_CONTACT_ID_DEVICE: u32 = 5;
const DC_CONTACT_ID_DEVICE: u32 = 2;
pub const DC_CONTACT_ID_LAST_SPECIAL: u32 = 9;
// Flags for empty server job
pub const DC_EMPTY_MVBOX: u32 = 0x01;
pub const DC_EMPTY_INBOX: u32 = 0x02;
pub const DC_CREATE_MVBOX: usize = 1;
// Flags for configuring IMAP and SMTP servers.
// These flags are optional
@@ -132,23 +135,23 @@ pub const DC_EMPTY_INBOX: u32 = 0x02;
/// Force OAuth2 authorization. This flag does not skip automatic configuration.
/// Before calling configure() with DC_LP_AUTH_OAUTH2 set,
/// the user has to confirm access at the URL returned by dc_get_oauth2_url().
pub const DC_LP_AUTH_OAUTH2: i32 = 0x2;
pub const DC_LP_AUTH_OAUTH2: usize = 0x2;
/// Force NORMAL authorization, this is the default.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_AUTH_NORMAL: i32 = 0x4;
pub const DC_LP_AUTH_NORMAL: usize = 0x4;
/// Connect to IMAP via STARTTLS.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_IMAP_SOCKET_STARTTLS: i32 = 0x100;
pub const DC_LP_IMAP_SOCKET_STARTTLS: usize = 0x100;
/// Connect to IMAP via SSL.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_IMAP_SOCKET_SSL: i32 = 0x200;
pub const DC_LP_IMAP_SOCKET_SSL: usize = 0x200;
/// Connect to IMAP unencrypted, this should not be used.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_IMAP_SOCKET_PLAIN: i32 = 0x400;
pub const DC_LP_IMAP_SOCKET_PLAIN: usize = 0x400;
/// Connect to SMTP via STARTTLS.
/// If this flag is set, automatic configuration is skipped.
@@ -163,9 +166,9 @@ pub const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
pub const DC_LP_SMTP_SOCKET_PLAIN: usize = 0x40000;
/// if none of these flags are set, the default is chosen
pub const DC_LP_AUTH_FLAGS: i32 = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
pub const DC_LP_AUTH_FLAGS: usize = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
/// if none of these flags are set, the default is chosen
pub const DC_LP_IMAP_SOCKET_FLAGS: i32 =
pub const DC_LP_IMAP_SOCKET_FLAGS: usize =
(DC_LP_IMAP_SOCKET_STARTTLS | DC_LP_IMAP_SOCKET_SSL | DC_LP_IMAP_SOCKET_PLAIN);
/// if none of these flags are set, the default is chosen
pub const DC_LP_SMTP_SOCKET_FLAGS: usize =
@@ -197,11 +200,6 @@ pub enum Viewtype {
/// and retrieved via dc_msg_get_file(), dc_msg_get_width(), dc_msg_get_height().
Gif = 21,
/// Message containing a sticker, similar to image.
/// If possible, the ui should display the image without borders in a transparent way.
/// A click on a sticker will offer to install the sticker set in some future.
Sticker = 23,
/// Message containing an Audio file.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration().
@@ -254,8 +252,13 @@ const DC_ERROR_SEE_STRING: usize = 0; // deprecated;
const DC_ERROR_SELF_NOT_IN_GROUP: usize = 1; // deprecated;
const DC_STR_SELFNOTINGRP: usize = 21; // deprecated;
/// Values for dc_get|set_config("show_emails")
const DC_SHOW_EMAILS_OFF: usize = 0;
const DC_SHOW_EMAILS_ACCEPTED_CONTACTS: usize = 1;
const DC_SHOW_EMAILS_ALL: usize = 2;
// TODO: Strings need some doumentation about used placeholders.
// These constants are used to set stock translation strings
// These constants are used to request strings using #DC_EVENT_GET_STRING.
const DC_STR_NOMESSAGES: usize = 1;
const DC_STR_SELF: usize = 2;
@@ -301,8 +304,7 @@ const DC_STR_MSGACTIONBYME: usize = 63;
const DC_STR_MSGLOCATIONENABLED: usize = 64;
const DC_STR_MSGLOCATIONDISABLED: usize = 65;
const DC_STR_LOCATION: usize = 66;
const DC_STR_STICKER: usize = 67;
const DC_STR_COUNT: usize = 67;
const DC_STR_COUNT: usize = 66;
pub const DC_JOB_DELETE_MSG_ON_IMAP: i32 = 110;

View File

@@ -14,7 +14,7 @@ use crate::error::Result;
use crate::events::Event;
use crate::key::*;
use crate::login_param::LoginParam;
use crate::message::{MessageState, MsgId};
use crate::message::MessageState;
use crate::peerstate::*;
use crate::sql;
use crate::stock::StockMessage;
@@ -47,8 +47,8 @@ pub struct Contact {
/// May be empty, initially set to `authname`.
name: String,
/// Name authorized by the contact himself. Only this name may be spread to others,
/// e.g. in To:-lists. May be empty. It is recommended to use `Contact::get_authname`,
/// to access this field.
/// e.g. in To:-lists. May be empty. It is recommended to use `Contact::get_name`,
/// `Contact::get_display_name` or `Contact::get_name_n_addr` to access this field.
authname: String,
/// E-Mail-Address of the contact. It is recommended to use `Contact::get_addr`` to access this field.
addr: String,
@@ -153,16 +153,7 @@ impl Contact {
blocked: false,
origin: Origin::Unknown,
};
return Ok(contact);
} else if contact_id == DC_CONTACT_ID_DEVICE {
let contact = Contact {
id: contact_id,
name: context.stock_str(StockMessage::DeviceMessages).into(),
authname: "".into(),
addr: "device@localhost".into(),
blocked: false,
origin: Origin::Unknown,
};
return Ok(contact);
}
@@ -252,7 +243,7 @@ impl Contact {
{
context.call_cb(Event::MsgsChanged {
chat_id: 0,
msg_id: MsgId::new(0),
msg_id: 0,
});
}
}
@@ -273,7 +264,7 @@ impl Contact {
.unwrap_or_default();
if addr_normalized == addr_self {
return DC_CONTACT_ID_SELF;
return 1;
}
context.sql.query_get_value(
@@ -310,7 +301,7 @@ impl Contact {
.unwrap_or_default();
if addr == addr_self {
return Ok((DC_CONTACT_ID_SELF, sth_modified));
return Ok((1, sth_modified));
}
if !may_be_valid_addr(&addr) {
@@ -399,18 +390,20 @@ impl Contact {
}
sth_modified = Modifier::Modified;
}
} else if sql::execute(
context,
&context.sql,
"INSERT INTO contacts (name, addr, origin) VALUES(?, ?, ?);",
params![name.as_ref(), addr, origin,],
)
.is_ok()
{
row_id = sql::get_rowid(context, &context.sql, "contacts", "addr", addr);
sth_modified = Modifier::Created;
} else {
error!(context, "Cannot add contact.");
if sql::execute(
context,
&context.sql,
"INSERT INTO contacts (name, addr, origin) VALUES(?, ?, ?);",
params![name.as_ref(), addr, origin,],
)
.is_ok()
{
row_id = sql::get_rowid(context, &context.sql, "contacts", "addr", addr);
sth_modified = Modifier::Created;
} else {
error!(context, "Cannot add contact.");
}
}
Ok((row_id, sth_modified))
@@ -585,12 +578,7 @@ impl Contact {
let mut self_key = Key::from_self_public(context, &loginparam.addr, &context.sql);
if peerstate.is_some()
&& peerstate
.as_ref()
.and_then(|p| p.peek_key(PeerstateVerifiedStatus::Unverified))
.is_some()
{
if peerstate.is_some() && peerstate.as_ref().and_then(|p| p.peek_key(0)).is_some() {
let peerstate = peerstate.as_ref().unwrap();
let p =
context.stock_str(if peerstate.prefer_encrypt == EncryptPreference::Mutual {
@@ -610,11 +598,11 @@ impl Contact {
.map(|k| k.formatted_fingerprint())
.unwrap_or_default();
let fingerprint_other_verified = peerstate
.peek_key(PeerstateVerifiedStatus::BidirectVerified)
.peek_key(2)
.map(|k| k.formatted_fingerprint())
.unwrap_or_default();
let fingerprint_other_unverified = peerstate
.peek_key(PeerstateVerifiedStatus::Unverified)
.peek_key(0)
.map(|k| k.formatted_fingerprint())
.unwrap_or_default();
if peerstate.addr.is_some() && &loginparam.addr < peerstate.addr.as_ref().unwrap() {
@@ -722,7 +710,6 @@ impl Contact {
&self.addr
}
/// Get name authorized by the contact.
pub fn get_authname(&self) -> &str {
&self.authname
}
@@ -817,14 +804,14 @@ impl Contact {
}
if let Some(peerstate) = peerstate {
if peerstate.verified_key.is_some() {
if peerstate.verified_key().is_some() {
return VerifiedStatus::BidirectVerified;
}
}
let peerstate = Peerstate::from_addr(context, &context.sql, &self.addr);
if let Some(ps) = peerstate {
if ps.verified_key.is_some() {
if ps.verified_key().is_some() {
return VerifiedStatus::BidirectVerified;
}
}
@@ -840,7 +827,7 @@ impl Contact {
if let Ok(contact) = Contact::load_from_db(context, contact_id) {
if !contact.addr.is_empty() {
let normalized_addr = addr_normalize(addr.as_ref());
if contact.addr == normalized_addr {
if &contact.addr == &normalized_addr {
return true;
}
}
@@ -881,7 +868,7 @@ impl Contact {
}
pub fn real_exists_by_id(context: &Context, contact_id: u32) -> bool {
if !context.sql.is_open() || contact_id <= DC_CONTACT_ID_LAST_SPECIAL {
if !context.sql.is_open() || contact_id <= 9 {
return false;
}
@@ -905,7 +892,6 @@ impl Contact {
}
}
/// Extracts first name from full name.
fn get_first_name<'a>(full_name: &'a str) -> &'a str {
full_name.splitn(2, ' ').next().unwrap_or_default()
}
@@ -916,7 +902,6 @@ pub fn may_be_valid_addr(addr: &str) -> bool {
res.is_ok()
}
/// Returns address with whitespace trimmed and `mailto:` prefix removed.
pub fn addr_normalize(addr: &str) -> &str {
let norm = addr.trim();
@@ -928,7 +913,7 @@ pub fn addr_normalize(addr: &str) -> &str {
}
fn set_block_contact(context: &Context, contact_id: u32, new_blocking: bool) {
if contact_id <= DC_CONTACT_ID_LAST_SPECIAL {
if contact_id <= 9 {
return;
}
@@ -978,9 +963,9 @@ pub fn normalize_name(full_name: impl AsRef<str>) -> String {
if len > 0 {
let firstchar = full_name.as_bytes()[0];
let lastchar = full_name.as_bytes()[len - 1];
if firstchar == b'\'' && lastchar == b'\''
|| firstchar == b'\"' && lastchar == b'\"'
|| firstchar == b'<' && lastchar == b'>'
if firstchar == '\'' as u8 && lastchar == '\'' as u8
|| firstchar == '\"' as u8 && lastchar == '\"' as u8
|| firstchar == '<' as u8 && lastchar == '>' as u8
{
full_name = &full_name[1..len - 1];
}
@@ -1061,8 +1046,6 @@ fn split_address_book(book: &str) -> Vec<(&str, &str)> {
mod tests {
use super::*;
use crate::test_utils::*;
#[test]
fn test_may_be_valid_addr() {
assert_eq!(may_be_valid_addr(""), false);
@@ -1104,109 +1087,4 @@ mod tests {
vec![("Name one", "Address one"), ("Name two", "Address two")]
)
}
#[test]
fn test_get_contacts() {
let context = dummy_context();
let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap();
assert_eq!(contacts.len(), 0);
let id = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
assert_ne!(id, 0);
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).unwrap();
assert_eq!(contacts.len(), 1);
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap();
assert_eq!(contacts.len(), 0);
}
#[test]
fn test_add_or_lookup() {
// add some contacts, this also tests add_address_book()
let t = dummy_context();
let book = concat!(
" Name one \n one@eins.org \n",
"Name two\ntwo@deux.net\n",
"\nthree@drei.sam\n",
"Name two\ntwo@deux.net\n" // should not be added again
);
assert_eq!(Contact::add_address_book(&t.ctx, book).unwrap(), 3);
// check first added contact, this does not modify because of lower origin
let (contact_id, sth_modified) =
Contact::add_or_lookup(&t.ctx, "bla foo", "one@eins.org", Origin::IncomingUnknownTo)
.unwrap();
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
assert_eq!(sth_modified, Modifier::None);
let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap();
assert_eq!(contact.get_id(), contact_id);
assert_eq!(contact.get_name(), "Name one");
assert_eq!(contact.get_display_name(), "Name one");
assert_eq!(contact.get_addr(), "one@eins.org");
assert_eq!(contact.get_name_n_addr(), "Name one (one@eins.org)");
// modify first added contact
let (contact_id_test, sth_modified) = Contact::add_or_lookup(
&t.ctx,
"Real one",
" one@eins.org ",
Origin::ManuallyCreated,
)
.unwrap();
assert_eq!(contact_id, contact_id_test);
assert_eq!(sth_modified, Modifier::Modified);
let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap();
assert_eq!(contact.get_name(), "Real one");
assert_eq!(contact.get_addr(), "one@eins.org");
assert!(!contact.is_blocked());
// check third added contact (contact without name)
let (contact_id, sth_modified) =
Contact::add_or_lookup(&t.ctx, "", "three@drei.sam", Origin::IncomingUnknownTo)
.unwrap();
assert!(contact_id > DC_CONTACT_ID_LAST_SPECIAL);
assert_eq!(sth_modified, Modifier::None);
let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap();
assert_eq!(contact.get_name(), "");
assert_eq!(contact.get_display_name(), "three@drei.sam");
assert_eq!(contact.get_addr(), "three@drei.sam");
assert_eq!(contact.get_name_n_addr(), "three@drei.sam");
// add name to third contact from incoming message (this becomes authorized name)
let (contact_id_test, sth_modified) = Contact::add_or_lookup(
&t.ctx,
"m. serious",
"three@drei.sam",
Origin::IncomingUnknownFrom,
)
.unwrap();
assert_eq!(contact_id, contact_id_test);
assert_eq!(sth_modified, Modifier::Modified);
let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap();
assert_eq!(contact.get_name_n_addr(), "m. serious (three@drei.sam)");
assert!(!contact.is_blocked());
// manually edit name of third contact (does not changed authorized name)
let (contact_id_test, sth_modified) = Contact::add_or_lookup(
&t.ctx,
"schnucki",
"three@drei.sam",
Origin::ManuallyCreated,
)
.unwrap();
assert_eq!(contact_id, contact_id_test);
assert_eq!(sth_modified, Modifier::Modified);
let contact = Contact::load_from_db(&t.ctx, contact_id).unwrap();
assert_eq!(contact.get_authname(), "m. serious");
assert_eq!(contact.get_name_n_addr(), "schnucki (three@drei.sam)");
assert!(!contact.is_blocked());
// check SELF
let contact = Contact::load_from_db(&t.ctx, DC_CONTACT_ID_SELF).unwrap();
assert_eq!(DC_CONTACT_ID_SELF, 1);
assert_eq!(contact.get_name(), t.ctx.stock_str(StockMessage::SelfMsg));
assert_eq!(contact.get_addr(), ""); // we're not configured
assert!(!contact.is_blocked());
}
}

View File

@@ -6,7 +6,6 @@ use std::sync::{Arc, Condvar, Mutex, RwLock};
use libc::uintptr_t;
use crate::chat::*;
use crate::config::Config;
use crate::constants::*;
use crate::contact::*;
use crate::error::*;
@@ -15,10 +14,9 @@ use crate::imap::*;
use crate::job::*;
use crate::job_thread::JobThread;
use crate::key::*;
use crate::log;
use crate::login_param::LoginParam;
use crate::lot::Lot;
use crate::message::{self, Message, MsgId};
use crate::message::*;
use crate::param::Params;
use crate::smtp::*;
use crate::sql::Sql;
@@ -40,14 +38,12 @@ pub type ContextCallback = dyn Fn(&Context, Event) -> uintptr_t + Send + Sync;
#[derive(DebugStub)]
pub struct Context {
/// Database file path
dbfile: PathBuf,
/// Blob directory path
blobdir: PathBuf,
pub sql: Sql,
pub inbox: Arc<RwLock<Imap>>,
pub perform_inbox_jobs_needed: Arc<RwLock<bool>>,
pub probe_imap_network: Arc<RwLock<bool>>,
pub inbox_thread: Arc<RwLock<JobThread>>,
pub sentbox_thread: Arc<RwLock<JobThread>>,
pub mvbox_thread: Arc<RwLock<JobThread>>,
pub smtp: Arc<Mutex<Smtp>>,
@@ -58,133 +54,45 @@ pub struct Context {
pub os_name: Option<String>,
pub cmdline_sel_chat_id: Arc<RwLock<u32>>,
pub bob: Arc<RwLock<BobStatus>>,
pub last_smeared_timestamp: RwLock<i64>,
pub last_smeared_timestamp: Arc<RwLock<i64>>,
pub running_state: Arc<RwLock<RunningState>>,
/// Mutex to avoid generating the key for the user more than once.
pub generating_key_mutex: Mutex<()>,
pub translated_stockstrings: RwLock<HashMap<usize, String>>,
pub logger: RwLock<log::Logger>,
}
#[derive(Debug, PartialEq, Eq)]
pub struct RunningState {
pub ongoing_running: bool,
shall_stop_ongoing: bool,
}
/// Return some info about deltachat-core
///
/// This contains information mostly about the library itself, the
/// actual keys and their values which will be present are not
/// guaranteed. Calling [Context::get_info] also includes information
/// about the context on top of the information here.
pub fn get_info() -> HashMap<&'static str, String> {
let mut res = HashMap::new();
res.insert("deltachat_core_version", format!("v{}", &*DC_VERSION_STR));
res.insert("sqlite_version", rusqlite::version().to_string());
res.insert(
"sqlite_thread_safe",
unsafe { rusqlite::ffi::sqlite3_threadsafe() }.to_string(),
);
res.insert(
"arch",
(::std::mem::size_of::<*mut libc::c_void>())
.wrapping_mul(8)
.to_string(),
);
res.insert("level", "awesome".into());
res
}
/// Builder for [Context].
///
/// # Example
///
/// ```
/// use deltachat::context::ContextBuilder;
/// let dir = tempfile::tempdir().unwrap();
/// let dbfile = dir.path().join("my-context.db");
/// let ctx = ContextBuilder::new(Box::new(|_, _| 0), "AppName".into(), dbfile)
/// .blobdir(dir.path().join("my-context-blobs"))
/// .logdir(dir.path().join("my-context-logs"))
/// .create()
/// .unwrap();
/// assert_eq!(ctx.get_blobdir(), dir.path().join("my-context-blobs"));
/// ```
#[derive(DebugStub)]
pub struct ContextBuilder {
#[debug_stub = "Callback"]
cb: Box<ContextCallback>,
os_name: String,
dbfile: PathBuf,
blobdir: PathBuf,
logdir: PathBuf,
}
impl ContextBuilder {
pub fn new(cb: Box<ContextCallback>, os_name: String, dbfile: PathBuf) -> Self {
let db_fname = OsString::from(dbfile.file_name().unwrap_or(&OsString::from("dc-context")));
let mut blob_fname = db_fname.clone();
blob_fname.push("-blobs");
let mut log_fname = db_fname.clone();
log_fname.push("-logs");
ContextBuilder {
cb,
os_name,
blobdir: dbfile.with_file_name(blob_fname),
logdir: dbfile.with_file_name(log_fname),
dbfile,
}
}
pub fn blobdir(mut self, path: PathBuf) -> Self {
self.blobdir = path;
self
}
pub fn logdir(mut self, path: PathBuf) -> Self {
self.logdir = path;
self
}
pub fn create(self) -> Result<Context> {
if !self.blobdir.exists() {
std::fs::create_dir_all(&self.blobdir)?;
}
if !self.logdir.exists() {
std::fs::create_dir_all(&self.logdir)?;
}
Context::new(
self.cb,
self.os_name,
self.dbfile,
self.blobdir,
self.logdir,
)
}
pub shall_stop_ongoing: bool,
}
impl Context {
fn new(
pub fn new(cb: Box<ContextCallback>, os_name: String, dbfile: PathBuf) -> Result<Context> {
let mut blob_fname = OsString::new();
blob_fname.push(dbfile.file_name().unwrap_or_default());
blob_fname.push("-blobs");
let blobdir = dbfile.with_file_name(blob_fname);
if !blobdir.exists() {
std::fs::create_dir_all(&blobdir)?;
}
Context::with_blobdir(cb, os_name, dbfile, blobdir)
}
pub fn with_blobdir(
cb: Box<ContextCallback>,
os_name: String,
dbfile: PathBuf,
blobdir: PathBuf,
logdir: PathBuf,
) -> Result<Context> {
ensure!(
blobdir.is_dir(),
"Blobdir does not exist: {}",
blobdir.display()
);
ensure!(
logdir.is_dir(),
"Logdir does not exist: {}",
logdir.display()
);
let ctx = Context {
blobdir,
dbfile,
inbox: Arc::new(RwLock::new(Imap::new())),
cb,
os_name: Some(os_name),
running_state: Arc::new(RwLock::new(Default::default())),
@@ -193,13 +101,8 @@ impl Context {
smtp_state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
oauth2_critical: Arc::new(Mutex::new(())),
bob: Arc::new(RwLock::new(Default::default())),
last_smeared_timestamp: RwLock::new(0),
last_smeared_timestamp: Arc::new(RwLock::new(0)),
cmdline_sel_chat_id: Arc::new(RwLock::new(0)),
inbox_thread: Arc::new(RwLock::new(JobThread::new(
"INBOX",
"configured_inbox_folder",
Imap::new(),
))),
sentbox_thread: Arc::new(RwLock::new(JobThread::new(
"SENTBOX",
"configured_sentbox_folder",
@@ -213,24 +116,20 @@ impl Context {
probe_imap_network: Arc::new(RwLock::new(false)),
perform_inbox_jobs_needed: Arc::new(RwLock::new(false)),
generating_key_mutex: Mutex::new(()),
translated_stockstrings: RwLock::new(HashMap::new()),
logger: RwLock::new(log::Logger::new(logdir)?),
};
ensure!(
ctx.sql.open(&ctx, &ctx.dbfile, false),
ctx.sql.open(&ctx, &ctx.dbfile, 0),
"Failed opening sqlite database"
);
Ok(ctx)
}
/// Returns database file path.
pub fn get_dbfile(&self) -> &Path {
self.dbfile.as_path()
}
/// Returns blob directory path.
pub fn get_blobdir(&self) -> &Path {
self.blobdir.as_path()
}
@@ -239,84 +138,31 @@ impl Context {
(*self.cb)(self, event)
}
/*******************************************************************************
* Ongoing process allocation/free/check
******************************************************************************/
pub fn alloc_ongoing(&self) -> bool {
if self.has_ongoing() {
warn!(self, "There is already another ongoing process running.",);
false
} else {
let s_a = self.running_state.clone();
let mut s = s_a.write().unwrap();
s.ongoing_running = true;
s.shall_stop_ongoing = false;
true
}
}
pub fn free_ongoing(&self) {
let s_a = self.running_state.clone();
let mut s = s_a.write().unwrap();
s.ongoing_running = false;
s.shall_stop_ongoing = true;
}
pub fn has_ongoing(&self) -> bool {
let s_a = self.running_state.clone();
let s = s_a.read().unwrap();
s.ongoing_running || !s.shall_stop_ongoing
}
/// Signal an ongoing process to stop.
pub fn stop_ongoing(&self) {
let s_a = self.running_state.clone();
let mut s = s_a.write().unwrap();
if s.ongoing_running && !s.shall_stop_ongoing {
info!(self, "Signaling the ongoing process to stop ASAP.",);
s.shall_stop_ongoing = true;
} else {
info!(self, "No ongoing process to stop.",);
};
}
pub fn shall_stop_ongoing(&self) -> bool {
self.running_state
.clone()
.read()
.unwrap()
.shall_stop_ongoing
}
/*******************************************************************************
* UI chat/message related API
******************************************************************************/
pub fn get_info(&self) -> HashMap<&'static str, String> {
let unset = "0";
let l = LoginParam::from_database(self, "");
let l2 = LoginParam::from_database(self, "configured_");
let displayname = self.get_config(Config::Displayname);
let displayname = self.sql.get_config(self, "displayname");
let chats = get_chat_cnt(self) as usize;
let real_msgs = message::get_real_msg_cnt(self) as usize;
let deaddrop_msgs = message::get_deaddrop_msg_cnt(self) as usize;
let real_msgs = dc_get_real_msg_cnt(self) as usize;
let deaddrop_msgs = dc_get_deaddrop_msg_cnt(self) as usize;
let contacts = Contact::get_real_cnt(self) as usize;
let is_configured = self.get_config_int(Config::Configured);
let is_configured = self
.sql
.get_config_int(self, "configured")
.unwrap_or_default();
let dbversion = self
.sql
.get_raw_config_int(self, "dbversion")
.get_config_int(self, "dbversion")
.unwrap_or_default();
let e2ee_enabled = self.get_config_int(Config::E2eeEnabled);
let mdns_enabled = self.get_config_int(Config::MdnsEnabled);
let bcc_self = self.get_config_int(Config::BccSelf);
let e2ee_enabled = self
.sql
.get_config_int(self, "e2ee_enabled")
.unwrap_or_else(|| 1);
let mdns_enabled = self
.sql
.get_config_int(self, "mdns_enabled")
.unwrap_or_else(|| 1);
let prv_key_cnt: Option<isize> =
self.sql
@@ -334,25 +180,48 @@ impl Context {
"<Not yet calculated>".into()
};
let inbox_watch = self.get_config_int(Config::InboxWatch);
let sentbox_watch = self.get_config_int(Config::SentboxWatch);
let mvbox_watch = self.get_config_int(Config::MvboxWatch);
let mvbox_move = self.get_config_int(Config::MvboxMove);
let inbox_watch = self
.sql
.get_config_int(self, "inbox_watch")
.unwrap_or_else(|| 1);
let sentbox_watch = self
.sql
.get_config_int(self, "sentbox_watch")
.unwrap_or_else(|| 1);
let mvbox_watch = self
.sql
.get_config_int(self, "mvbox_watch")
.unwrap_or_else(|| 1);
let mvbox_move = self
.sql
.get_config_int(self, "mvbox_move")
.unwrap_or_else(|| 1);
let folders_configured = self
.sql
.get_raw_config_int(self, "folders_configured")
.get_config_int(self, "folders_configured")
.unwrap_or_default();
let configured_sentbox_folder = self
.sql
.get_raw_config(self, "configured_sentbox_folder")
.get_config(self, "configured_sentbox_folder")
.unwrap_or_else(|| "<unset>".to_string());
let configured_mvbox_folder = self
.sql
.get_raw_config(self, "configured_mvbox_folder")
.get_config(self, "configured_mvbox_folder")
.unwrap_or_else(|| "<unset>".to_string());
let mut res = get_info();
let mut res = HashMap::new();
res.insert("deltachat_core_version", format!("v{}", &*DC_VERSION_STR));
res.insert("sqlite_version", rusqlite::version().to_string());
res.insert(
"sqlite_thread_safe",
unsafe { rusqlite::ffi::sqlite3_threadsafe() }.to_string(),
);
res.insert(
"arch",
(::std::mem::size_of::<*mut libc::c_void>())
.wrapping_mul(8)
.to_string(),
);
res.insert("number_of_chats", chats.to_string());
res.insert("number_of_chat_messages", real_msgs.to_string());
res.insert("messages_in_contact_requests", deaddrop_msgs.to_string());
@@ -373,7 +242,6 @@ impl Context {
res.insert("configured_mvbox_folder", configured_mvbox_folder);
res.insert("mdns_enabled", mdns_enabled.to_string());
res.insert("e2ee_enabled", e2ee_enabled.to_string());
res.insert("bcc_self", bcc_self.to_string());
res.insert(
"private_key_count",
prv_key_cnt.unwrap_or_default().to_string(),
@@ -383,43 +251,38 @@ impl Context {
pub_key_cnt.unwrap_or_default().to_string(),
);
res.insert("fingerprint", fingerprint_str);
res.insert("level", "awesome".into());
res
}
pub fn get_fresh_msgs(&self) -> Vec<MsgId> {
pub fn get_fresh_msgs(&self) -> Vec<u32> {
let show_deaddrop = 0;
self.sql
.query_map(
concat!(
"SELECT m.id",
" FROM msgs m",
" LEFT JOIN contacts ct",
" ON m.from_id=ct.id",
" LEFT JOIN chats c",
" ON m.chat_id=c.id",
" WHERE m.state=?",
" AND m.hidden=0",
" AND m.chat_id>?",
" AND ct.blocked=0",
" AND (c.blocked=0 OR c.blocked=?)",
" ORDER BY m.timestamp DESC,m.id DESC;"
),
"SELECT m.id FROM msgs m LEFT JOIN contacts ct \
ON m.from_id=ct.id LEFT JOIN chats c ON m.chat_id=c.id WHERE m.state=? \
AND m.hidden=0 \
AND m.chat_id>? \
AND ct.blocked=0 \
AND (c.blocked=0 OR c.blocked=?) ORDER BY m.timestamp DESC,m.id DESC;",
&[10, 9, if 0 != show_deaddrop { 2 } else { 0 }],
|row| row.get::<_, MsgId>(0),
|row| row.get(0),
|rows| {
let mut ret = Vec::new();
for row in rows {
ret.push(row?);
let id: u32 = row?;
ret.push(id);
}
Ok(ret)
},
)
.unwrap_or_default()
.unwrap()
}
#[allow(non_snake_case)]
pub fn search_msgs(&self, chat_id: u32, query: impl AsRef<str>) -> Vec<MsgId> {
pub fn search_msgs(&self, chat_id: u32, query: impl AsRef<str>) -> Vec<u32> {
let real_query = query.as_ref().trim();
if real_query.is_empty() {
return Vec::new();
@@ -428,43 +291,25 @@ impl Context {
let strLikeBeg = format!("{}%", real_query);
let query = if 0 != chat_id {
concat!(
"SELECT m.id AS id, m.timestamp AS timestamp",
" FROM msgs m",
" LEFT JOIN contacts ct",
" ON m.from_id=ct.id",
" WHERE m.chat_id=?",
" AND m.hidden=0",
" AND ct.blocked=0",
" AND (txt LIKE ? OR ct.name LIKE ?)",
" ORDER BY m.timestamp,m.id;"
)
"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.chat_id=? \
AND m.hidden=0 \
AND ct.blocked=0 AND (txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp,m.id;"
} else {
concat!(
"SELECT m.id AS id, m.timestamp AS timestamp",
" FROM msgs m",
" LEFT JOIN contacts ct",
" ON m.from_id=ct.id",
" LEFT JOIN chats c",
" ON m.chat_id=c.id",
" WHERE m.chat_id>9",
" AND m.hidden=0",
" AND (c.blocked=0 OR c.blocked=?)",
" AND ct.blocked=0",
" AND (m.txt LIKE ? OR ct.name LIKE ?)",
" ORDER BY m.timestamp DESC,m.id DESC;"
)
"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id \
LEFT JOIN chats c ON m.chat_id=c.id WHERE m.chat_id>9 AND m.hidden=0 \
AND (c.blocked=0 OR c.blocked=?) \
AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;"
};
self.sql
.query_map(
query,
params![chat_id as i32, &strLikeInText, &strLikeBeg],
|row| row.get::<_, MsgId>("id"),
|row| row.get::<_, i32>(0),
|rows| {
let mut ret = Vec::new();
for id in rows {
ret.push(id?);
ret.push(id? as u32);
}
Ok(ret)
},
@@ -477,7 +322,7 @@ impl Context {
}
pub fn is_sentbox(&self, folder_name: impl AsRef<str>) -> bool {
let sentbox_name = self.sql.get_raw_config(self, "configured_sentbox_folder");
let sentbox_name = self.sql.get_config(self, "configured_sentbox_folder");
if let Some(name) = sentbox_name {
name == folder_name.as_ref()
} else {
@@ -486,7 +331,7 @@ impl Context {
}
pub fn is_mvbox(&self, folder_name: impl AsRef<str>) -> bool {
let mvbox_name = self.sql.get_raw_config(self, "configured_mvbox_folder");
let mvbox_name = self.sql.get_config(self, "configured_mvbox_folder");
if let Some(name) = mvbox_name {
name == folder_name.as_ref()
@@ -495,30 +340,41 @@ impl Context {
}
}
pub fn do_heuristics_moves(&self, folder: &str, msg_id: MsgId) {
if !self.get_config_bool(Config::MvboxMove) {
pub fn do_heuristics_moves(&self, folder: &str, msg_id: u32) {
if self
.sql
.get_config_int(self, "mvbox_move")
.unwrap_or_else(|| 1)
== 0
{
return;
}
if self.is_mvbox(folder) {
if !self.is_inbox(folder) && !self.is_sentbox(folder) {
return;
}
if let Ok(msg) = Message::load_from_db(self, msg_id) {
if msg.is_setupmessage() {
if let Ok(msg) = dc_msg_new_load(self, msg_id) {
if dc_msg_is_setupmessage(&msg) {
// do not move setup messages;
// there may be a non-delta device that wants to handle it
return;
}
if self.is_mvbox(folder) {
dc_update_msg_move_state(self, msg.rfc724_mid, MoveState::Stay);
}
// 1 = dc message, 2 = reply to dc message
if 0 != msg.is_dc_message {
job_add(
self,
Action::MoveMsg,
msg.id.to_u32() as i32,
msg.id as libc::c_int,
Params::new(),
0,
);
dc_update_msg_move_state(self, msg.rfc724_mid, MoveState::Moving);
}
}
}
@@ -526,8 +382,8 @@ impl Context {
impl Drop for Context {
fn drop(&mut self) {
info!(self, "disconnecting inbox-thread",);
self.inbox_thread.read().unwrap().imap.disconnect(self);
info!(self, "disconnecting INBOX-watch",);
self.inbox.read().unwrap().disconnect(self);
info!(self, "disconnecting sentbox-thread",);
self.sentbox_thread.read().unwrap().imap.disconnect(self);
info!(self, "disconnecting mvbox-thread",);
@@ -554,25 +410,12 @@ pub struct BobStatus {
pub qr_scan: Option<Lot>,
}
#[derive(Debug, PartialEq)]
pub enum PerformJobsNeeded {
Not,
AtOnce,
AvoidDos,
}
impl Default for PerformJobsNeeded {
fn default() -> Self {
Self::Not
}
}
#[derive(Default, Debug)]
pub struct SmtpState {
pub idle: bool,
pub suspended: bool,
pub doing_jobs: bool,
pub perform_jobs_needed: PerformJobsNeeded,
pub perform_jobs_needed: i32,
pub probe_network: bool,
}
@@ -591,7 +434,7 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap();
let res = ContextBuilder::new(Box::new(|_, _| 0), "FakeOs".into(), dbfile).create();
let res = Context::new(Box::new(|_, _| 0), "FakeOs".into(), dbfile);
assert!(res.is_err());
}
@@ -606,9 +449,7 @@ mod tests {
fn test_blobdir_exists() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.create()
.unwrap();
Context::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile).unwrap();
let blobdir = tmp.path().join("db.sqlite-blobs");
assert!(blobdir.is_dir());
}
@@ -619,7 +460,7 @@ mod tests {
let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("db.sqlite-blobs");
std::fs::write(&blobdir, b"123").unwrap();
let res = ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile).create();
let res = Context::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile);
assert!(res.is_err());
}
@@ -629,9 +470,7 @@ mod tests {
let subdir = tmp.path().join("subdir");
let dbfile = subdir.join("db.sqlite");
let dbfile2 = dbfile.clone();
ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.create()
.unwrap();
Context::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile).unwrap();
assert!(subdir.is_dir());
assert!(dbfile2.is_file());
}
@@ -641,9 +480,7 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = PathBuf::new();
let res = ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.blobdir(blobdir)
.create();
let res = Context::with_blobdir(Box::new(|_, _| 0), "FakeOS".into(), dbfile, blobdir);
assert!(res.is_err());
}
@@ -652,36 +489,10 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("blobs");
ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.blobdir(blobdir.clone())
.create()
.unwrap();
assert!(blobdir.is_dir());
}
#[test]
fn test_with_empty_logdir() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let logdir = PathBuf::new();
let res = ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.logdir(logdir)
.create();
let res = Context::with_blobdir(Box::new(|_, _| 0), "FakeOS".into(), dbfile, blobdir);
assert!(res.is_err());
}
#[test]
fn test_with_logdir_not_exists() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let logdir = tmp.path().join("logs");
ContextBuilder::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile)
.logdir(logdir.clone())
.create()
.unwrap();
assert!(logdir.is_dir());
}
#[test]
fn no_crashes_on_context_deref() {
let t = dummy_context();
@@ -693,14 +504,6 @@ mod tests {
let t = dummy_context();
let info = t.ctx.get_info();
assert!(info.get("database_dir").is_some());
}
#[test]
fn test_get_info_no_context() {
let info = get_info();
assert!(info.get("deltachat_core_version").is_some());
assert!(info.get("database_dir").is_none());
assert_eq!(info.get("level").unwrap(), "awesome");
}
}

1094
src/dc_imex.rs Normal file

File diff suppressed because it is too large Load Diff

1294
src/dc_mimefactory.rs Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -10,16 +10,18 @@ pub struct Simplify {
///
/// Also return whether not-standard (rfc3676, §4.3) footer is found.
fn find_message_footer(lines: &[&str]) -> (usize, bool) {
for (ix, &line) in lines.iter().enumerate() {
for ix in 0..lines.len() {
let line = lines[ix];
// quoted-printable may encode `-- ` to `-- =20` which is converted
// back to `-- `
match line {
match line.as_ref() {
"-- " | "-- " => return (ix, false),
"--" | "---" | "----" => return (ix, true),
_ => (),
}
}
(lines.len(), false)
return (lines.len(), false);
}
impl Simplify {
@@ -101,8 +103,10 @@ impl Simplify {
if let Some(last_quoted_line) = l_lastQuotedLine {
l_last = last_quoted_line;
is_cut_at_end = true;
if l_last > 1 && is_empty_line(lines[l_last - 1]) {
l_last -= 1
if l_last > 1 {
if is_empty_line(lines[l_last - 1]) {
l_last -= 1
}
}
if l_last > 1 {
let line = lines[l_last - 1];
@@ -142,8 +146,8 @@ impl Simplify {
ret += "[...]";
}
/* we write empty lines only in case and non-empty line follows */
let mut pending_linebreaks = 0;
let mut content_lines_added = 0;
let mut pending_linebreaks: libc::c_int = 0i32;
let mut content_lines_added: libc::c_int = 0i32;
for l in l_first..l_last {
let line = lines[l];
if is_empty_line(line) {
@@ -201,7 +205,7 @@ fn is_quoted_headline(buf: &str) -> bool {
}
fn is_plain_quote(buf: &str) -> bool {
buf.starts_with('>')
buf.starts_with(">")
}
#[cfg(test)]

View File

@@ -1,15 +1,15 @@
use itertools::Itertools;
use std::borrow::Cow;
use std::ffi::CString;
use std::ptr;
use charset::Charset;
use libc::free;
use mmime::mailmime::decode::mailmime_encoded_phrase_parse;
use mmime::mailmime_decode::*;
use mmime::mmapstring::*;
use mmime::other::*;
use percent_encoding::{percent_decode, utf8_percent_encode, AsciiSet, CONTROLS};
use crate::dc_tools::*;
use crate::x::*;
/**
* Encode non-ascii-strings as `=?UTF-8?Q?Bj=c3=b6rn_Petersen?=`.
@@ -25,50 +25,222 @@ use crate::dc_tools::*;
* @return Returns the encoded string which must be free()'d when no longed needed.
* On errors, NULL is returned.
*/
pub fn dc_encode_header_words(input: impl AsRef<str>) -> String {
let mut result = String::default();
for (_, group) in &input.as_ref().chars().group_by(|c| c.is_whitespace()) {
let word: String = group.collect();
result.push_str(&quote_word(&word.as_bytes()));
pub unsafe fn dc_encode_header_words(to_encode_r: impl AsRef<str>) -> *mut libc::c_char {
let to_encode =
CString::new(to_encode_r.as_ref().as_bytes()).expect("invalid cstring to_encode");
let mut ok_to_continue = true;
let mut ret_str: *mut libc::c_char = ptr::null_mut();
let mut cur: *const libc::c_char = to_encode.as_ptr();
let mmapstr: *mut MMAPString = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
if mmapstr.is_null() {
ok_to_continue = false;
}
result
}
fn must_encode(byte: u8) -> bool {
static SPECIALS: &[u8] = b",:!\"#$@[\\]^`{|}~=?_";
SPECIALS.iter().any(|b| *b == byte)
}
fn quote_word(word: &[u8]) -> String {
let mut result = String::default();
let mut encoded = false;
for byte in word {
let byte = *byte;
if byte >= 128 || must_encode(byte) {
result.push_str(&format!("={:2X}", byte));
encoded = true;
} else if byte == b' ' {
result.push('_');
encoded = true;
loop {
if !ok_to_continue {
if !mmapstr.is_null() {
mmap_string_free(mmapstr);
}
break;
} else {
result.push(byte as _);
if *cur as libc::c_int != '\u{0}' as i32 {
let begin: *const libc::c_char;
let mut end: *const libc::c_char;
let mut do_quote: bool;
let mut quote_words: libc::c_int;
begin = cur;
end = begin;
quote_words = 0i32;
do_quote = true;
while *cur as libc::c_int != '\u{0}' as i32 {
get_word(cur, &mut cur, &mut do_quote);
if !do_quote {
break;
}
quote_words = 1i32;
end = cur;
if *cur as libc::c_int != '\u{0}' as i32 {
cur = cur.offset(1isize)
}
}
if 0 != quote_words {
if !quote_word(
b"utf-8\x00" as *const u8 as *const libc::c_char,
mmapstr,
begin,
end.wrapping_offset_from(begin) as libc::size_t,
) {
ok_to_continue = false;
continue;
}
if *end as libc::c_int == ' ' as i32 || *end as libc::c_int == '\t' as i32 {
if mmap_string_append_c(mmapstr, *end).is_null() {
ok_to_continue = false;
continue;
}
end = end.offset(1isize)
}
if *end as libc::c_int != '\u{0}' as i32 {
if mmap_string_append_len(
mmapstr,
end,
cur.wrapping_offset_from(end) as libc::size_t,
)
.is_null()
{
ok_to_continue = false;
continue;
}
}
} else if mmap_string_append_len(
mmapstr,
begin,
cur.wrapping_offset_from(begin) as libc::size_t,
)
.is_null()
{
ok_to_continue = false;
continue;
}
if !(*cur as libc::c_int == ' ' as i32 || *cur as libc::c_int == '\t' as i32) {
continue;
}
if mmap_string_append_c(mmapstr, *cur).is_null() {
ok_to_continue = false;
continue;
}
cur = cur.offset(1isize);
} else {
ret_str = strdup((*mmapstr).str_0);
ok_to_continue = false;
}
}
}
if encoded {
result = format!("=?utf-8?Q?{}?=", &result);
ret_str
}
unsafe fn quote_word(
display_charset: *const libc::c_char,
mmapstr: *mut MMAPString,
word: *const libc::c_char,
size: libc::size_t,
) -> bool {
let mut cur: *const libc::c_char;
let mut i = 0;
let mut hex: [libc::c_char; 4] = [0; 4];
// let mut col: libc::c_int = 0i32;
if mmap_string_append(mmapstr, b"=?\x00" as *const u8 as *const libc::c_char).is_null() {
return false;
}
result
if mmap_string_append(mmapstr, display_charset).is_null() {
return false;
}
if mmap_string_append(mmapstr, b"?Q?\x00" as *const u8 as *const libc::c_char).is_null() {
return false;
}
// col = (*mmapstr).len as libc::c_int;
cur = word;
while i < size {
let mut do_quote_char = false;
match *cur as u8 as char {
',' | ':' | '!' | '"' | '#' | '$' | '@' | '[' | '\\' | ']' | '^' | '`' | '{' | '|'
| '}' | '~' | '=' | '?' | '_' => do_quote_char = true,
_ => {
if *cur as u8 >= 128 {
do_quote_char = true;
}
}
}
if do_quote_char {
print_hex(hex.as_mut_ptr(), cur);
if mmap_string_append(mmapstr, hex.as_mut_ptr()).is_null() {
return false;
}
// col += 3i32
} else {
if *cur as libc::c_int == ' ' as i32 {
if mmap_string_append_c(mmapstr, '_' as i32 as libc::c_char).is_null() {
return false;
}
} else if mmap_string_append_c(mmapstr, *cur).is_null() {
return false;
}
// col += 3i32
}
cur = cur.offset(1isize);
i = i.wrapping_add(1)
}
if mmap_string_append(mmapstr, b"?=\x00" as *const u8 as *const libc::c_char).is_null() {
return false;
}
true
}
unsafe fn get_word(
begin: *const libc::c_char,
pend: *mut *const libc::c_char,
pto_be_quoted: *mut bool,
) {
let mut cur: *const libc::c_char = begin;
while *cur as libc::c_int != ' ' as i32
&& *cur as libc::c_int != '\t' as i32
&& *cur as libc::c_int != '\u{0}' as i32
{
cur = cur.offset(1isize)
}
*pto_be_quoted = to_be_quoted(begin, cur.wrapping_offset_from(begin) as libc::size_t);
*pend = cur;
}
/* ******************************************************************************
* Encode/decode header words, RFC 2047
******************************************************************************/
pub(crate) fn dc_decode_header_words(input: &str) -> String {
/* see comment below */
unsafe fn to_be_quoted(word: *const libc::c_char, size: libc::size_t) -> bool {
let mut cur: *const libc::c_char = word;
let mut i = 0;
while i < size {
match *cur as libc::c_int {
44 | 58 | 33 | 34 | 35 | 36 | 64 | 91 | 92 | 93 | 94 | 96 | 123 | 124 | 125 | 126
| 61 | 63 | 95 => return true,
_ => {
if *cur as libc::c_uchar as libc::c_int >= 128i32 {
return true;
}
}
}
cur = cur.offset(1isize);
i = i.wrapping_add(1)
}
false
}
pub unsafe fn dc_decode_header_words(in_0: *const libc::c_char) -> *mut libc::c_char {
if in_0.is_null() {
return ptr::null_mut();
}
let mut out: *mut libc::c_char = ptr::null_mut();
let mut cur_token = 0;
let r: libc::c_int = mailmime_encoded_phrase_parse(
b"iso-8859-1\x00" as *const u8 as *const libc::c_char,
in_0,
strlen(in_0),
&mut cur_token,
b"utf-8\x00" as *const u8 as *const libc::c_char,
&mut out,
);
if r != MAILIMF_NO_ERROR as libc::c_int || out.is_null() {
out = dc_strdup(in_0)
}
out
}
pub fn dc_decode_header_words_safe(input: &str) -> String {
static FROM_ENCODING: &[u8] = b"iso-8859-1\x00";
static TO_ENCODING: &[u8] = b"utf-8\x00";
let mut out = ptr::null_mut();
@@ -86,7 +258,7 @@ pub(crate) fn dc_decode_header_words(input: &str) -> String {
if r as u32 != MAILIMF_NO_ERROR || out.is_null() {
input.to_string()
} else {
let res = to_string_lossy(out);
let res = to_string(out);
free(out.cast());
res
}
@@ -152,35 +324,77 @@ pub fn dc_decode_ext_header(to_decode: &[u8]) -> Cow<str> {
String::from_utf8_lossy(to_decode)
}
unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
assert!(!target.is_null());
assert!(!cur.is_null());
let bytes = std::slice::from_raw_parts(cur as *const _, strlen(cur));
let raw = CString::yolo(format!("={}", &hex::encode_upper(bytes)[..2]));
libc::memcpy(target as *mut _, raw.as_ptr() as *const _, 4);
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CStr;
#[test]
fn test_dc_decode_header_words() {
assert_eq!(
dc_decode_header_words("=?utf-8?B?dGVzdMOkw7bDvC50eHQ=?="),
std::string::String::from_utf8(b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt".to_vec()).unwrap(),
);
assert_eq!(dc_decode_header_words("just ascii test"), "just ascii test");
assert_eq!(dc_encode_header_words("abcdef"), "abcdef");
let r = dc_encode_header_words(
std::string::String::from_utf8(b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt".to_vec()).unwrap(),
);
assert!(r.starts_with("=?utf-8"));
assert_eq!(
dc_decode_header_words(&r),
std::string::String::from_utf8(b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt".to_vec()).unwrap(),
);
assert_eq!(
dc_decode_header_words("=?ISO-8859-1?Q?attachment=3B=0D=0A_filename=3D?= =?ISO-8859-1?Q?=22test=E4=F6=FC=2Etxt=22=3B=0D=0A_size=3D39?="),
std::string::String::from_utf8(b"attachment;\r\n filename=\"test\xc3\xa4\xc3\xb6\xc3\xbc.txt\";\r\n size=39".to_vec()).unwrap(),
unsafe {
let mut buf1: *mut libc::c_char = dc_decode_header_words(
b"=?utf-8?B?dGVzdMOkw7bDvC50eHQ=?=\x00" as *const u8 as *const libc::c_char,
);
assert_eq!(
strcmp(
buf1,
b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt\x00" as *const u8 as *const libc::c_char
),
0
);
free(buf1 as *mut libc::c_void);
buf1 =
dc_decode_header_words(b"just ascii test\x00" as *const u8 as *const libc::c_char);
assert_eq!(CStr::from_ptr(buf1).to_str().unwrap(), "just ascii test");
free(buf1 as *mut libc::c_void);
buf1 = dc_encode_header_words("abcdef");
assert_eq!(CStr::from_ptr(buf1).to_str().unwrap(), "abcdef");
free(buf1 as *mut libc::c_void);
buf1 = dc_encode_header_words(
std::string::String::from_utf8(b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt".to_vec())
.unwrap(),
);
assert_eq!(
strncmp(buf1, b"=?utf-8\x00" as *const u8 as *const libc::c_char, 7),
0
);
let buf2: *mut libc::c_char = dc_decode_header_words(buf1);
assert_eq!(
strcmp(
buf2,
b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt\x00" as *const u8 as *const libc::c_char
),
0
);
free(buf1 as *mut libc::c_void);
free(buf2 as *mut libc::c_void);
buf1 = dc_decode_header_words(
b"=?ISO-8859-1?Q?attachment=3B=0D=0A_filename=3D?= =?ISO-8859-1?Q?=22test=E4=F6=FC=2Etxt=22=3B=0D=0A_size=3D39?=\x00" as *const u8 as *const libc::c_char
);
assert_eq!(
strcmp(
buf1,
b"attachment;\r\n filename=\"test\xc3\xa4\xc3\xb6\xc3\xbc.txt\";\r\n size=39\x00" as *const u8 as *const libc::c_char,
),
0
);
free(buf1 as *mut libc::c_void);
}
}
#[test]
@@ -224,6 +438,18 @@ mod tests {
assert_eq!(dc_needs_ext_header("a b"), true);
}
#[test]
fn test_print_hex() {
let mut hex: [libc::c_char; 4] = [0; 4];
let cur = b"helloworld" as *const u8 as *const libc::c_char;
unsafe { print_hex(hex.as_mut_ptr(), cur) };
assert_eq!(to_string(hex.as_ptr() as *const _), "=68");
let cur = b":" as *const u8 as *const libc::c_char;
unsafe { print_hex(hex.as_mut_ptr(), cur) };
assert_eq!(to_string(hex.as_ptr() as *const _), "=3A");
}
use proptest::prelude::*;
proptest! {
@@ -239,13 +465,5 @@ mod tests {
// make sure this never panics
let _decoded = dc_decode_ext_header(&buf);
}
#[test]
fn test_dc_header_roundtrip(input: String) {
let encoded = dc_encode_header_words(&input);
let decoded = dc_decode_header_words(&encoded);
assert_eq!(input, decoded);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -26,34 +26,6 @@ pub enum Error {
CStringError(crate::dc_tools::CStringError),
#[fail(display = "PGP: {:?}", _0)]
Pgp(pgp::errors::Error),
#[fail(display = "Base64Decode: {:?}", _0)]
Base64Decode(base64::DecodeError),
#[fail(display = "{:?}", _0)]
FromUtf8(std::string::FromUtf8Error),
#[fail(display = "{}", _0)]
BlobError(#[cause] crate::blob::BlobError),
#[fail(display = "Invalid Message ID.")]
InvalidMsgId,
#[fail(display = "Watch folder not found {:?}", _0)]
WatchFolderNotFound(String),
#[fail(display = "Connection Failed params: {}", _0)]
ImapConnectionFailed(String),
#[fail(display = "Could not get OAUTH token")]
ImapOauthError,
#[fail(display = "Could not login as {}", _0)]
ImapLoginFailed(String),
#[fail(display = "Cannot idle")]
ImapMissesIdle,
#[fail(display = "Imap IDLE protocol failed to init/complete")]
ImapIdleProtocolFailed(String),
#[fail(display = "Imap IDLE failed to select folder {:?}", _0)]
ImapSelectFailed(String),
#[fail(display = "Connect without configured params")]
ConnectWithoutConfigure,
#[fail(display = "imap operation attempted while imap is torn down")]
ImapInTeardown,
#[fail(display = "No IMAP Connection established")]
ImapNoConnection,
}
pub type Result<T> = std::result::Result<T, Error>;
@@ -64,12 +36,6 @@ impl From<rusqlite::Error> for Error {
}
}
impl From<base64::DecodeError> for Error {
fn from(err: base64::DecodeError) -> Error {
Error::Base64Decode(err)
}
}
impl From<failure::Error> for Error {
fn from(err: failure::Error) -> Error {
Error::Failure(err)
@@ -112,24 +78,6 @@ impl From<pgp::errors::Error> for Error {
}
}
impl From<std::string::FromUtf8Error> for Error {
fn from(err: std::string::FromUtf8Error) -> Error {
Error::FromUtf8(err)
}
}
impl From<crate::blob::BlobError> for Error {
fn from(err: crate::blob::BlobError) -> Error {
Error::BlobError(err)
}
}
impl From<crate::message::InvalidMsgId> for Error {
fn from(_err: crate::message::InvalidMsgId) -> Error {
Error::InvalidMsgId
}
}
#[macro_export]
macro_rules! bail {
($e:expr) => {

View File

@@ -2,7 +2,7 @@ use std::path::PathBuf;
use strum::EnumProperty;
use crate::message::MsgId;
use crate::stock::StockMessage;
impl Event {
/// Returns the corresponding Event id.
@@ -42,36 +42,6 @@ pub enum Event {
#[strum(props(id = "103"))]
SmtpMessageSent(String),
/// Emitted when an IMAP message has been marked as deleted
///
/// @return 0
#[strum(props(id = "104"))]
ImapMessageDeleted(String),
/// Emitted when an IMAP message has been moved
///
/// @return 0
#[strum(props(id = "105"))]
ImapMessageMoved(String),
/// Emitted when an IMAP folder was emptied
///
/// @return 0
#[strum(props(id = "106"))]
ImapFolderEmptied(String),
/// Emitted when an new file in the $BLOBDIR was created
///
/// @return 0
#[strum(props(id = "150"))]
NewBlobFile(String),
/// Emitted when an new file in the $BLOBDIR was created
///
/// @return 0
#[strum(props(id = "151"))]
DeletedBlobFile(String),
/// The library-user should write a warning string to the log.
/// Passed to the callback given to dc_context_new().
///
@@ -133,7 +103,7 @@ pub enum Event {
///
/// @return 0
#[strum(props(id = "2000"))]
MsgsChanged { chat_id: u32, msg_id: MsgId },
MsgsChanged { chat_id: u32, msg_id: u32 },
/// There is a fresh message. Typically, the user will show an notification
/// when receiving this message.
@@ -142,28 +112,28 @@ pub enum Event {
///
/// @return 0
#[strum(props(id = "2005"))]
IncomingMsg { chat_id: u32, msg_id: MsgId },
IncomingMsg { chat_id: u32, msg_id: u32 },
/// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to
/// DC_STATE_OUT_DELIVERED, see dc_msg_get_state().
///
/// @return 0
#[strum(props(id = "2010"))]
MsgDelivered { chat_id: u32, msg_id: MsgId },
MsgDelivered { chat_id: u32, msg_id: u32 },
/// A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to
/// DC_STATE_OUT_FAILED, see dc_msg_get_state().
///
/// @return 0
#[strum(props(id = "2012"))]
MsgFailed { chat_id: u32, msg_id: MsgId },
MsgFailed { chat_id: u32, msg_id: u32 },
/// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to
/// DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state().
///
/// @return 0
#[strum(props(id = "2015"))]
MsgRead { chat_id: u32, msg_id: MsgId },
MsgRead { chat_id: u32, msg_id: u32 },
/// Chat changed. The name or the image of a chat group was changed or members were added or removed.
/// Or the verify state of a chat has changed.
@@ -197,7 +167,7 @@ pub enum Event {
#[strum(props(id = "2041"))]
ConfigureProgress(usize),
/// Inform about the import/export progress started by imex().
/// Inform about the import/export progress started by dc_imex().
///
/// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done
/// @param data2 0
@@ -205,8 +175,8 @@ pub enum Event {
#[strum(props(id = "2051"))]
ImexProgress(usize),
/// A file has been exported. A file has been written by imex().
/// This event may be sent multiple times by a single call to imex().
/// A file has been exported. A file has been written by dc_imex().
/// This event may be sent multiple times by a single call to dc_imex().
///
/// A typical purpose for a handler of this event may be to make the file public to some system
/// services.
@@ -244,10 +214,16 @@ pub enum Event {
#[strum(props(id = "2061"))]
SecurejoinJoinerProgress { contact_id: u32, progress: usize },
/// This event is sent out to the inviter when a joiner successfully joined a group.
/// @param data1 (int) chat_id
/// @param data2 (int) contact_id
/// @return 0
#[strum(props(id = "2062"))]
SecurejoinMemberAdded { chat_id: u32, contact_id: u32 },
// the following events are functions that should be provided by the frontends
/// Requeste a localized string from the frontend.
/// @param data1 (int) ID of the string to request, one of the DC_STR_/// constants.
/// @param data2 (int) The count. If the requested string contains a placeholder for a numeric value,
/// the ui may use this value to return different strings on different plural forms.
/// @return (const char*) Null-terminated UTF-8 string.
/// The string will be free()'d by the core,
/// so it must be allocated using malloc() or a compatible function.
/// Return 0 if the ui cannot provide the requested string
/// the core will use a default string in english language then.
#[strum(props(id = "2091"))]
GetString { id: StockMessage, count: usize },
}

Some files were not shown because too many files have changed in this diff Show More