mirror of
https://github.com/chatmail/core.git
synced 2026-04-04 06:22:16 +03:00
Compare commits
362 Commits
fix_upload
...
invalid_ce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e82d76a112 | ||
|
|
f45ee2ab4d | ||
|
|
2b73fab913 | ||
|
|
e0d750ac64 | ||
|
|
bb57c6e7b7 | ||
|
|
f346a052c1 | ||
|
|
3933353b5f | ||
|
|
6c9c21c135 | ||
|
|
d02a721eed | ||
|
|
8ffb4ae127 | ||
|
|
96fbeb583b | ||
|
|
33b98a15d3 | ||
|
|
e523ebe3c1 | ||
|
|
e17c671b7c | ||
|
|
e7565e1a2a | ||
|
|
b73d6377fc | ||
|
|
31f5fffc45 | ||
|
|
64c518c2f2 | ||
|
|
1ed543b0e8 | ||
|
|
8b7cd2dd1a | ||
|
|
8520b5211a | ||
|
|
03661e2a71 | ||
|
|
20b82b3638 | ||
|
|
cb499ae502 | ||
|
|
02b73207f9 | ||
|
|
53b5cbc12a | ||
|
|
f4c6decd2d | ||
|
|
69f1497986 | ||
|
|
2b46f01fe3 | ||
|
|
dd4adb57cf | ||
|
|
452bce07e1 | ||
|
|
8d702d0b77 | ||
|
|
e1dc4b69f5 | ||
|
|
6cd3580263 | ||
|
|
d5383aecc9 | ||
|
|
71cbbab2c9 | ||
|
|
8518d8f456 | ||
|
|
adc0db04bc | ||
|
|
c61fc59003 | ||
|
|
40f9072250 | ||
|
|
ea30bb351e | ||
|
|
b93550f6c8 | ||
|
|
60bd053095 | ||
|
|
8165b76001 | ||
|
|
efc563f5ff | ||
|
|
e52acc994c | ||
|
|
646833d3ec | ||
|
|
fd72c27afe | ||
|
|
c13bcc25c6 | ||
|
|
21c9ff6c85 | ||
|
|
4d6b367654 | ||
|
|
e2fd22a78e | ||
|
|
0759bdde01 | ||
|
|
faa03e0e14 | ||
|
|
f70897a6d3 | ||
|
|
ba231d2c5f | ||
|
|
095cb759ed | ||
|
|
5cbcb76039 | ||
|
|
3388b42f20 | ||
|
|
e1d541b02e | ||
|
|
cb784615ee | ||
|
|
321c5e049b | ||
|
|
ed7cf218f8 | ||
|
|
74d8368525 | ||
|
|
dcbfa272f9 | ||
|
|
42dd600e0c | ||
|
|
9689df601f | ||
|
|
f6019583b7 | ||
|
|
2d50a3335d | ||
|
|
202bfa987d | ||
|
|
93f9c7cfbd | ||
|
|
8f6a0bbf09 | ||
|
|
1a47c148e5 | ||
|
|
2435ba1ea0 | ||
|
|
9ba57a923b | ||
|
|
90e2b6f26b | ||
|
|
05f9f454c3 | ||
|
|
b85f59798c | ||
|
|
e80345a05b | ||
|
|
0bdcc3d616 | ||
|
|
987f12740e | ||
|
|
1265016a55 | ||
|
|
48d1de3678 | ||
|
|
43f6db3252 | ||
|
|
5b917e7d10 | ||
|
|
0523868a88 | ||
|
|
26f176eb7e | ||
|
|
ad32b5ca8f | ||
|
|
dbf14179dc | ||
|
|
45b0a4ec27 | ||
|
|
1969ee02a5 | ||
|
|
266b205c75 | ||
|
|
7dd3bad8bd | ||
|
|
4b45be7cda | ||
|
|
497ffd86fa | ||
|
|
0bdcc4269f | ||
|
|
e583c99f94 | ||
|
|
e22e50c3fa | ||
|
|
e9c9a3e1ce | ||
|
|
fa7bb71f3f | ||
|
|
34a3ad82e0 | ||
|
|
2f5d74dbf4 | ||
|
|
4bf5ba594c | ||
|
|
6e2da27f45 | ||
|
|
6ee9465d43 | ||
|
|
391a6bf422 | ||
|
|
5001a0e37d | ||
|
|
fd8d16a7db | ||
|
|
f3ac9306f3 | ||
|
|
59740d0b56 | ||
|
|
fb05a6c26f | ||
|
|
7943b708d2 | ||
|
|
04e37d1eca | ||
|
|
91b98e8c6d | ||
|
|
70234e5b19 | ||
|
|
ceff85d892 | ||
|
|
9f914dd42e | ||
|
|
24f5d68fef | ||
|
|
735fc325b1 | ||
|
|
178b216e48 | ||
|
|
5d0481f7a2 | ||
|
|
711bc69750 | ||
|
|
7263c9490d | ||
|
|
0c88bc6ac7 | ||
|
|
fda8d0a2e2 | ||
|
|
14bdf7fae8 | ||
|
|
d4ff7ecbaa | ||
|
|
030aec7373 | ||
|
|
6a351de4f9 | ||
|
|
bbff1c9c3e | ||
|
|
bbb8144129 | ||
|
|
83f3e23297 | ||
|
|
4f880932ae | ||
|
|
62e8c2497c | ||
|
|
37f854be3e | ||
|
|
e0e82e1877 | ||
|
|
95d8665dbe | ||
|
|
8667de994e | ||
|
|
cee0e22ce7 | ||
|
|
dc8a2f54e5 | ||
|
|
b3bc5b2520 | ||
|
|
00e929afac | ||
|
|
8a3bf6a5d9 | ||
|
|
d5f361d386 | ||
|
|
7bb4a27b60 | ||
|
|
3bd36feede | ||
|
|
fc1f1ce37c | ||
|
|
ee327dc87d | ||
|
|
d644ca5563 | ||
|
|
b3b1e37192 | ||
|
|
38f39c8d32 | ||
|
|
a773b7929c | ||
|
|
7b73103133 | ||
|
|
b6803191cb | ||
|
|
2b038a34c9 | ||
|
|
8c2c3f8bee | ||
|
|
e710836276 | ||
|
|
6be3c9a48a | ||
|
|
c0747bf68d | ||
|
|
0346dd15d9 | ||
|
|
ff0aa8423d | ||
|
|
0f718e0d08 | ||
|
|
9534a9ad30 | ||
|
|
d091857cef | ||
|
|
72a9ca0aa5 | ||
|
|
ecaae42b80 | ||
|
|
84bf1ec6e7 | ||
|
|
0bf3d20e07 | ||
|
|
5486ac5b9f | ||
|
|
ac12b2e643 | ||
|
|
16c281a9b7 | ||
|
|
413e3eb62d | ||
|
|
f31f341a50 | ||
|
|
c2501258b6 | ||
|
|
de1e3e1d4f | ||
|
|
a3f64d4e95 | ||
|
|
afc9a31080 | ||
|
|
e5699e8ba9 | ||
|
|
8a7143b791 | ||
|
|
79d23909b5 | ||
|
|
24fe4740d3 | ||
|
|
1e1d3f4aa8 | ||
|
|
51bf875826 | ||
|
|
f9ce6c7c81 | ||
|
|
2e91c3d334 | ||
|
|
d1b762af04 | ||
|
|
e1e02839d1 | ||
|
|
50a812ea5e | ||
|
|
27a4adb9c6 | ||
|
|
3932d8f48f | ||
|
|
d978a8e0a2 | ||
|
|
fad49e08d7 | ||
|
|
0f67b16f29 | ||
|
|
97edd23910 | ||
|
|
400ab2cdab | ||
|
|
db20ecf985 | ||
|
|
e59f421e79 | ||
|
|
75b7de712a | ||
|
|
fa8192177d | ||
|
|
48ffef7955 | ||
|
|
055796e6b3 | ||
|
|
34433c4962 | ||
|
|
6d1076e1f6 | ||
|
|
bb4081e503 | ||
|
|
bef25ad5f6 | ||
|
|
f47652e72d | ||
|
|
7c4d7fb3dd | ||
|
|
cdfc7281d0 | ||
|
|
500e80784a | ||
|
|
c5803d9b4c | ||
|
|
ebac00fbde | ||
|
|
f198ce29b1 | ||
|
|
df47e0ed63 | ||
|
|
477470ff70 | ||
|
|
aefddf7f5e | ||
|
|
5ce27b16f1 | ||
|
|
8302d6833d | ||
|
|
649c2eb676 | ||
|
|
a0b5e32f98 | ||
|
|
e73671a6ff | ||
|
|
9503aca78d | ||
|
|
dfaa8e4529 | ||
|
|
cbc3579e9a | ||
|
|
dd2e3d35fd | ||
|
|
ca9dccfcd7 | ||
|
|
64b00fce7d | ||
|
|
177ab0229a | ||
|
|
68b5e34fed | ||
|
|
2eda839303 | ||
|
|
71a01d3002 | ||
|
|
995548002b | ||
|
|
38ad16887b | ||
|
|
c20e8f7613 | ||
|
|
5bd4606854 | ||
|
|
e7b198849d | ||
|
|
3ab0d74af2 | ||
|
|
7ed5a8e72f | ||
|
|
57daa0f7f0 | ||
|
|
2dd3f169db | ||
|
|
b97b618b4b | ||
|
|
bb12488200 | ||
|
|
706a97b013 | ||
|
|
1cdb9c733a | ||
|
|
ffc525af9e | ||
|
|
1576dc1d13 | ||
|
|
4fbb5fbb25 | ||
|
|
e9da21a02e | ||
|
|
6c4d7ad8cc | ||
|
|
f1c026c5ec | ||
|
|
3d61c06ea9 | ||
|
|
22c1ee1f55 | ||
|
|
971960a242 | ||
|
|
69f8973339 | ||
|
|
bf1d9b6d06 | ||
|
|
6a2368f83c | ||
|
|
d0960f7f7f | ||
|
|
e5ad697466 | ||
|
|
188eab5faf | ||
|
|
2b257e3d0d | ||
|
|
77c9746be5 | ||
|
|
28cae607a4 | ||
|
|
814281ed7d | ||
|
|
5b0c8dd9dd | ||
|
|
650d8c45ec | ||
|
|
383d8980d6 | ||
|
|
6ea706c646 | ||
|
|
1ed2af08b8 | ||
|
|
7563a5abe0 | ||
|
|
0a8b187f80 | ||
|
|
275aa853f5 | ||
|
|
3614d57f9f | ||
|
|
1367873949 | ||
|
|
d933183e0a | ||
|
|
7e11def527 | ||
|
|
f3e53a05a6 | ||
|
|
dd381a5c1c | ||
|
|
8eee449305 | ||
|
|
96e02af0da | ||
|
|
60fb1478c3 | ||
|
|
aa7d0679df | ||
|
|
8e3cc192a5 | ||
|
|
0f939995d1 | ||
|
|
2e1bc9b14e | ||
|
|
fd1ac6ab2d | ||
|
|
d224924dc8 | ||
|
|
00e5ddd6f0 | ||
|
|
c603ca0e7a | ||
|
|
d07ef01204 | ||
|
|
d8630b5029 | ||
|
|
3b397326f8 | ||
|
|
81cabd08a9 | ||
|
|
5663c7dec3 | ||
|
|
06673b2108 | ||
|
|
6b7498a4b1 | ||
|
|
7f4ef493b9 | ||
|
|
d9d0dee0d5 | ||
|
|
9605370f0b | ||
|
|
7d9fc682a0 | ||
|
|
c4c08f2552 | ||
|
|
400740fdba | ||
|
|
42bce7c0bf | ||
|
|
a2281489a6 | ||
|
|
9bf7b0bf96 | ||
|
|
1f82ba74aa | ||
|
|
1062ac6ade | ||
|
|
aa5304a4f3 | ||
|
|
3a57ba1142 | ||
|
|
c0e7293360 | ||
|
|
dc1839760c | ||
|
|
a4e4b0fc17 | ||
|
|
743e4deb36 | ||
|
|
1d75f8478c | ||
|
|
cc0428aa50 | ||
|
|
4be481275f | ||
|
|
28cfe36f43 | ||
|
|
e0df78c5f7 | ||
|
|
4d8b058b65 | ||
|
|
da25611758 | ||
|
|
27732c85af | ||
|
|
5ffc84eb59 | ||
|
|
0a6e540394 | ||
|
|
9f09c73ec1 | ||
|
|
4bbab876ae | ||
|
|
b2fafeff19 | ||
|
|
79510a83de | ||
|
|
dd0afdfeb0 | ||
|
|
b6997c4455 | ||
|
|
2920732435 | ||
|
|
b9cfcce284 | ||
|
|
70d997964b | ||
|
|
4ffe71e1df | ||
|
|
cc2339fbe2 | ||
|
|
8fb859c0c4 | ||
|
|
5ff472dae0 | ||
|
|
6be4a6ed00 | ||
|
|
094d46293e | ||
|
|
c8d945db56 | ||
|
|
f78f0079c1 | ||
|
|
98d6bdb48a | ||
|
|
0391aebaeb | ||
|
|
46520edd87 | ||
|
|
14daa99802 | ||
|
|
4f9f67a477 | ||
|
|
85f182067c | ||
|
|
66ab6874f8 | ||
|
|
906b901e3d | ||
|
|
65adff4bdd | ||
|
|
e60fc0dc30 | ||
|
|
ffd719962c | ||
|
|
1a1f0c0a7c | ||
|
|
3944592c09 | ||
|
|
a5f862a564 | ||
|
|
489f25940f | ||
|
|
6288909481 | ||
|
|
c95f134963 | ||
|
|
024c2883c0 | ||
|
|
d7b0ecad75 | ||
|
|
50947b81c0 | ||
|
|
e5a1b721f1 | ||
|
|
aeb1a88e7a | ||
|
|
c406675d6a | ||
|
|
1ec193991b |
@@ -12,7 +12,7 @@ restore-workspace: &restore-workspace
|
||||
restore-cache: &restore-cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- cargo-v1-{{ 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:
|
||||
@@ -23,20 +23,9 @@ commands:
|
||||
steps:
|
||||
- *restore-workspace
|
||||
- *restore-cache
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: true
|
||||
# TODO: move into image
|
||||
- run:
|
||||
name: Install Docker client
|
||||
command: |
|
||||
set -x
|
||||
VER="18.09.2"
|
||||
curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz
|
||||
tar -xz -C /tmp -f /tmp/docker-$VER.tgz
|
||||
mv /tmp/docker/* /usr/bin
|
||||
- run:
|
||||
name: Test (<< parameters.target >>)
|
||||
command: TARGET=<< parameters.target >> ci/run.sh
|
||||
command: TARGET=<< parameters.target >> ci_scripts/run-rust-test.sh
|
||||
no_output_timeout: 15m
|
||||
|
||||
jobs:
|
||||
@@ -52,7 +41,7 @@ jobs:
|
||||
command: cargo generate-lockfile
|
||||
- restore_cache:
|
||||
keys:
|
||||
- cargo-v1-{{ 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
|
||||
@@ -61,13 +50,14 @@ jobs:
|
||||
- run: cargo fetch
|
||||
- run: rustc +stable --version
|
||||
- run: rustc +$(cat rust-toolchain) --version
|
||||
- run: rm -rf .git
|
||||
# make sure this git repo doesn't grow too big
|
||||
- run: git gc
|
||||
- persist_to_workspace:
|
||||
root: /mnt
|
||||
paths:
|
||||
- crate
|
||||
- save_cache:
|
||||
key: cargo-v1-{{ 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"
|
||||
@@ -102,7 +92,7 @@ jobs:
|
||||
- run: cargo fetch
|
||||
- run:
|
||||
name: Test
|
||||
command: TARGET=x86_64-apple-darwin ci/run.sh
|
||||
command: TARGET=x86_64-apple-darwin ci_scripts/run-rust-test.sh
|
||||
|
||||
test_x86_64-unknown-linux-gnu:
|
||||
executor: default
|
||||
@@ -124,18 +114,18 @@ jobs:
|
||||
|
||||
|
||||
build_test_docs_wheel:
|
||||
machine: True
|
||||
steps:
|
||||
- checkout
|
||||
# - run: docker pull deltachat/doxygen
|
||||
- run: docker pull deltachat/coredeps
|
||||
- run:
|
||||
name: build docs, run tests and build wheels
|
||||
command: ci_scripts/ci_run.sh
|
||||
environment:
|
||||
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: |
|
||||
@@ -143,7 +133,6 @@ jobs:
|
||||
# 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:
|
||||
@@ -176,15 +165,16 @@ workflows:
|
||||
|
||||
test:
|
||||
jobs:
|
||||
- build_test_docs_wheel
|
||||
- cargo_fetch
|
||||
- build_test_docs_wheel:
|
||||
requires:
|
||||
- cargo_fetch
|
||||
- upload_docs_wheels:
|
||||
requires:
|
||||
- build_test_docs_wheel
|
||||
- cargo_fetch
|
||||
- rustfmt:
|
||||
requires:
|
||||
- cargo_fetch
|
||||
|
||||
- clippy:
|
||||
requires:
|
||||
- cargo_fetch
|
||||
|
||||
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -2,6 +2,10 @@
|
||||
# ensures this even if the user has not set core.autocrlf.
|
||||
* text=auto
|
||||
|
||||
# This directory contains email messages verbatim, and changing CRLF to
|
||||
# LF will corrupt them.
|
||||
test-data/* text=false
|
||||
|
||||
# binary files should be detected by git, however, to be sure, you can add them here explicitly
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,6 +16,7 @@ python/.tox
|
||||
*.egg-info
|
||||
__pycache__
|
||||
python/src/deltachat/capi*.so
|
||||
python/.venv/
|
||||
|
||||
python/liveconfig*
|
||||
|
||||
|
||||
762
Cargo.lock
generated
762
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
22
Cargo.toml
22
Cargo.toml
@@ -1,30 +1,26 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.0.0-alpha.4"
|
||||
version = "1.0.0-alpha.5"
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MPL"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.35"
|
||||
pkg-config = "0.3"
|
||||
|
||||
[dependencies]
|
||||
deltachat_derive = { path = "./deltachat_derive" }
|
||||
mmime = { version = "0.1.2", path = "./mmime" }
|
||||
|
||||
libc = "0.2.51"
|
||||
pgp = { version = "0.2", default-features = false }
|
||||
hex = "0.3.2"
|
||||
sha2 = "0.8.0"
|
||||
rand = "0.6.5"
|
||||
phf = { git = "https://github.com/sfackler/rust-phf", rev = "0d00821", features = ["macros"] }
|
||||
smallvec = "0.6.9"
|
||||
reqwest = "0.9.15"
|
||||
num-derive = "0.2.5"
|
||||
num-traits = "0.2.6"
|
||||
native-tls = "0.2.3"
|
||||
lettre = "0.9.0"
|
||||
imap = "1.0.1"
|
||||
mmime = "0.1.0"
|
||||
imap = { git = "https://github.com/jonhoo/rust-imap", rev = "281d2eb8ab50dc656ceff2ae749ca5045f334e15" }
|
||||
base64 = "0.10"
|
||||
charset = "0.1"
|
||||
percent-encoding = "2.0"
|
||||
@@ -35,13 +31,13 @@ failure = "0.1.5"
|
||||
failure_derive = "0.1.5"
|
||||
# TODO: make optional
|
||||
rustyline = "4.1.0"
|
||||
lazy_static = "1.3.0"
|
||||
lazy_static = "1.4.0"
|
||||
regex = "1.1.6"
|
||||
rusqlite = { version = "0.20", features = ["bundled"] }
|
||||
r2d2_sqlite = "0.12.0"
|
||||
r2d2 = "0.8.5"
|
||||
strum = "0.15.0"
|
||||
strum_macros = "0.15.0"
|
||||
strum = "0.16.0"
|
||||
strum_macros = "0.16.0"
|
||||
thread-local-object = "0.1.0"
|
||||
backtrace = "0.3.33"
|
||||
byteorder = "1.3.1"
|
||||
@@ -50,16 +46,20 @@ image-meta = "0.1.0"
|
||||
quick-xml = "0.15.0"
|
||||
escaper = "0.1.0"
|
||||
bitflags = "1.1.0"
|
||||
jetscii = "0.4.4"
|
||||
debug_stub_derive = "0.3.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
pretty_assertions = "0.6.1"
|
||||
pretty_env_logger = "0.3.0"
|
||||
proptest = "0.9.4"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"deltachat-ffi",
|
||||
"deltachat_derive",
|
||||
"mmime",
|
||||
]
|
||||
|
||||
[[example]]
|
||||
|
||||
@@ -13,7 +13,7 @@ install:
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --release
|
||||
- cargo test --release --all
|
||||
|
||||
cache:
|
||||
- target
|
||||
|
||||
38
build.rs
38
build.rs
@@ -1,38 +0,0 @@
|
||||
extern crate cc;
|
||||
|
||||
fn link_static(lib: &str) {
|
||||
println!("cargo:rustc-link-lib=static={}", lib);
|
||||
}
|
||||
|
||||
fn link_framework(fw: &str) {
|
||||
println!("cargo:rustc-link-lib=framework={}", fw);
|
||||
}
|
||||
|
||||
fn add_search_path(p: &str) {
|
||||
println!("cargo:rustc-link-search={}", p);
|
||||
}
|
||||
|
||||
fn build_tools() {
|
||||
let mut config = cc::Build::new();
|
||||
config.file("misc.c").compile("libtools.a");
|
||||
|
||||
println!("rerun-if-changed=build.rs");
|
||||
println!("rerun-if-changed=misc.h");
|
||||
println!("rerun-if-changed=misc.c");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
build_tools();
|
||||
|
||||
add_search_path("/usr/local/lib");
|
||||
|
||||
let target = std::env::var("TARGET").unwrap();
|
||||
if target.contains("-apple") || target.contains("-darwin") {
|
||||
link_framework("CoreFoundation");
|
||||
link_framework("CoreServices");
|
||||
link_framework("Security");
|
||||
}
|
||||
|
||||
// local tools
|
||||
link_static("tools");
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
# perform CI jobs on PRs and after merges to master.
|
||||
# triggered from .circleci/config.yml
|
||||
|
||||
set -e -x
|
||||
|
||||
export BRANCH=${CIRCLE_BRANCH:-test7}
|
||||
|
||||
# run doxygen on c-source (needed by later doc-generation steps).
|
||||
# XXX modifies the host filesystem docs/xml and docs/html directories
|
||||
# XXX which you can then only remove with sudo as they belong to root
|
||||
|
||||
# XXX we don't do doxygen doc generation with Rust anymore, needs to be
|
||||
# substituted with rust-docs
|
||||
#if [ -n "$DOCS" ] ; then
|
||||
# docker run --rm -it -v $PWD:/mnt -w /mnt/docs deltachat/doxygen doxygen
|
||||
#fi
|
||||
|
||||
# run everything else inside docker (TESTS, DOCS, WHEELS)
|
||||
docker run -e DCC_PY_LIVECONFIG -e BRANCH -e TESTS -e DOCS \
|
||||
--rm -it -v $(pwd):/mnt -w /mnt \
|
||||
deltachat/coredeps ci_scripts/run_all.sh
|
||||
|
||||
@@ -34,7 +34,6 @@ echo -----------------------
|
||||
# Bundle external shared libraries into the wheels
|
||||
pushd $WHEELHOUSEDIR
|
||||
|
||||
pip3 install -U setuptools
|
||||
pip3 install devpi-client
|
||||
devpi use https://m.devpi.net
|
||||
devpi login dc --password $DEVPI_LOGIN
|
||||
|
||||
@@ -36,19 +36,25 @@ if [ -n "$TESTS" ]; then
|
||||
rm -rf src/deltachat/__pycache__
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# run tox
|
||||
# XXX we don't run liveconfig tests because they hang sometimes
|
||||
# see https://github.com/deltachat/deltachat-core-rust/issues/331
|
||||
# unset DCC_PY_LIVECONFIG
|
||||
|
||||
tox --workdir "$TOXWORKDIR" -e lint,py27,py35,py36,py37,auditwheels
|
||||
# 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 -- -k "not qr"
|
||||
tox --workdir "$TOXWORKDIR" -e py37 -- -k "qr"
|
||||
unset DCC_PY_LIVECONFIG
|
||||
tox --workdir "$TOXWORKDIR" -p4 -e lint,py35,py36,doc
|
||||
tox --workdir "$TOXWORKDIR" -e auditwheels
|
||||
popd
|
||||
fi
|
||||
|
||||
|
||||
if [ -n "$DOCS" ]; then
|
||||
echo -----------------------
|
||||
echo generating python docs
|
||||
echo -----------------------
|
||||
(cd python && tox --workdir "$TOXWORKDIR" -e doc)
|
||||
fi
|
||||
# if [ -n "$DOCS" ]; then
|
||||
# echo -----------------------
|
||||
# echo generating python docs
|
||||
# echo -----------------------
|
||||
# (cd python && tox --workdir "$TOXWORKDIR" -e doc)
|
||||
# fi
|
||||
@@ -31,7 +31,7 @@ fi
|
||||
if [[ $NORUN == "1" ]]; then
|
||||
export CARGO_SUBCMD="build"
|
||||
else
|
||||
export CARGO_SUBCMD="test"
|
||||
export CARGO_SUBCMD="test --all"
|
||||
export OPT="${OPT} "
|
||||
export OPT_RELEASE="${OPT_RELEASE} "
|
||||
export OPT_RELEASE_IGNORED="${OPT_RELEASE} -- --ignored"
|
||||
@@ -41,6 +41,3 @@ fi
|
||||
$CARGO_CMD $CARGO_SUBCMD $OPT
|
||||
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE
|
||||
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE_IGNORED
|
||||
|
||||
# Build the ffi lib
|
||||
$CARGO_CMD $CARGO_SUBCMD $OPT_FFI_RELEASE
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.0.0-alpha.4"
|
||||
version = "1.0.0-alpha.5"
|
||||
description = "Deltachat FFI"
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
edition = "2018"
|
||||
@@ -16,6 +16,7 @@ crate-type = ["cdylib", "staticlib"]
|
||||
|
||||
[dependencies]
|
||||
deltachat = { path = "../", default-features = false }
|
||||
deltachat-provider-database = "0.2.1"
|
||||
libc = "0.2"
|
||||
human-panic = "1.0.1"
|
||||
num-traits = "0.2.6"
|
||||
|
||||
@@ -11,6 +11,16 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct _dc_context dc_context_t;
|
||||
typedef struct _dc_array dc_array_t;
|
||||
typedef struct _dc_chatlist dc_chatlist_t;
|
||||
typedef struct _dc_chat dc_chat_t;
|
||||
typedef struct _dc_msg dc_msg_t;
|
||||
typedef struct _dc_contact dc_contact_t;
|
||||
typedef struct _dc_lot dc_lot_t;
|
||||
typedef struct _dc_provider dc_provider_t;
|
||||
|
||||
|
||||
/**
|
||||
* @mainpage Getting started
|
||||
*
|
||||
@@ -189,13 +199,6 @@ extern "C" {
|
||||
* SQLite database for offline functionality and for account-related
|
||||
* settings.
|
||||
*/
|
||||
typedef struct _dc_context dc_context_t;
|
||||
typedef struct _dc_array dc_array_t;
|
||||
typedef struct _dc_chatlist dc_chatlist_t;
|
||||
typedef struct _dc_chat dc_chat_t;
|
||||
typedef struct _dc_msg dc_msg_t;
|
||||
typedef struct _dc_contact dc_contact_t;
|
||||
typedef struct _dc_lot dc_lot_t;
|
||||
|
||||
|
||||
/**
|
||||
@@ -393,17 +396,23 @@ int dc_set_config (dc_context_t* context, const char*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object as created by dc_context_new(). For querying system values, this can be NULL.
|
||||
* @param key The key to query.
|
||||
* @return Returns current value of "key", if "key" is unset, the default value is returned.
|
||||
* The returned value must be free()'d, NULL is never returned.
|
||||
* @return Returns current value of "key", if "key" is unset, the default
|
||||
* value is returned. The returned value must be free()'d, NULL is never
|
||||
* returned. If there is an error an empty string will be returned.
|
||||
*/
|
||||
char* dc_get_config (dc_context_t* context, const char* key);
|
||||
|
||||
|
||||
/**
|
||||
* Get information about the context.
|
||||
*
|
||||
* The information is returned by a multi-line string
|
||||
* and contains information about the current configuration.
|
||||
*
|
||||
* If the context is not open or configured only a subset of the information
|
||||
* will be available. There is no guarantee about which information will be
|
||||
* included when however.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return String which must be free()'d after usage. Never returns NULL.
|
||||
@@ -442,7 +451,6 @@ char* dc_get_info (dc_context_t* context);
|
||||
char* dc_get_oauth2_url (dc_context_t* context, const char* addr, const char* redirect_uri);
|
||||
|
||||
|
||||
|
||||
// connect
|
||||
|
||||
/**
|
||||
@@ -607,12 +615,8 @@ void dc_interrupt_imap_idle (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Fetch new messages from the MVBOX, if any.
|
||||
* The MVBOX is a folder on the account where chat messages are moved to.
|
||||
* The moving is done to not disturb shared accounts that are used by both,
|
||||
* Delta Chat and a classical MUA.
|
||||
*
|
||||
* This function and dc_perform_mvbox_idle()
|
||||
* Execute pending mvbox-jobs.
|
||||
* This function and dc_perform_mvbox_fetch() and dc_perform_mvbox_idle()
|
||||
* must be called from the same thread, typically in a loop.
|
||||
*
|
||||
* Example:
|
||||
@@ -620,6 +624,7 @@ void dc_interrupt_imap_idle (dc_context_t* context);
|
||||
* void* mvbox_thread_func(void* context)
|
||||
* {
|
||||
* while (true) {
|
||||
* dc_perform_mvbox_jobs(context);
|
||||
* dc_perform_mvbox_fetch(context);
|
||||
* dc_perform_mvbox_idle(context);
|
||||
* }
|
||||
@@ -633,13 +638,26 @@ void dc_interrupt_imap_idle (dc_context_t* context);
|
||||
*
|
||||
* // network becomes available again -
|
||||
* // the interrupt causes dc_perform_mvbox_idle() in the thread above
|
||||
* // to return so that and messages are fetched.
|
||||
* // to return so that jobs are executed and messages are fetched.
|
||||
* dc_maybe_network(context);
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_mvbox_jobs (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Fetch new messages from the MVBOX, if any.
|
||||
* The MVBOX is a folder on the account where chat messages are moved to.
|
||||
* The moving is done to not disturb shared accounts that are used by both,
|
||||
* Delta Chat and a classical MUA.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_mvbox_fetch (dc_context_t* context);
|
||||
|
||||
|
||||
@@ -676,6 +694,39 @@ void dc_perform_mvbox_idle (dc_context_t* context);
|
||||
*/
|
||||
void dc_interrupt_mvbox_idle (dc_context_t* context);
|
||||
|
||||
/**
|
||||
* Execute pending sentbox-jobs.
|
||||
* This function and dc_perform_sentbox_fetch() and dc_perform_sentbox_idle()
|
||||
* must be called from the same thread, typically in a loop.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* void* sentbox_thread_func(void* context)
|
||||
* {
|
||||
* while (true) {
|
||||
* dc_perform_sentbox_jobs(context);
|
||||
* dc_perform_sentbox_fetch(context);
|
||||
* dc_perform_sentbox_idle(context);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // start sentbox-thread that runs forever
|
||||
* pthread_t sentbox_thread;
|
||||
* pthread_create(&sentbox_thread, NULL, sentbox_thread_func, context);
|
||||
*
|
||||
* ... program runs ...
|
||||
*
|
||||
* // network becomes available again -
|
||||
* // the interrupt causes dc_perform_sentbox_idle() in the thread above
|
||||
* // to return so that jobs are executed and messages are fetched.
|
||||
* dc_maybe_network(context);
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_sentbox_jobs (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Fetch new messages from the Sent folder, if any.
|
||||
@@ -3418,6 +3469,110 @@ int dc_contact_is_blocked (const dc_contact_t* contact);
|
||||
int dc_contact_is_verified (dc_contact_t* contact);
|
||||
|
||||
|
||||
/**
|
||||
* @class dc_provider_t
|
||||
*
|
||||
* Opaque object containing information about one single email provider.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Create a provider struct for the given domain.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param domain The domain to get provider info for.
|
||||
* @return a dc_provider_t struct which can be used with the dc_provider_get_*
|
||||
* accessor functions. If no provider info is found, NULL will be
|
||||
* returned.
|
||||
*/
|
||||
dc_provider_t* dc_provider_new_from_domain (char* domain);
|
||||
|
||||
|
||||
/**
|
||||
* Create a provider struct for the given email address.
|
||||
*
|
||||
* The provider is extracted from the email address and it's information is returned.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param email The user's email address to extract the provider info form.
|
||||
* @return a dc_provider_t struct which can be used with the dc_provider_get_*
|
||||
* accessor functions. If no provider info is found, NULL will be
|
||||
* returned.
|
||||
*/
|
||||
dc_provider_t* dc_provider_new_from_email (char* email);
|
||||
|
||||
|
||||
/**
|
||||
* URL of the overview page.
|
||||
*
|
||||
* This URL allows linking to the providers page on providers.delta.chat.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
* @return A string which must be free()d.
|
||||
*/
|
||||
char* dc_provider_get_overview_page (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* The provider's name.
|
||||
*
|
||||
* The name of the provider, e.g. "POSTEO".
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
* @return A string which must be free()d.
|
||||
*/
|
||||
char* dc_provider_get_name (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* The markdown content of the providers page.
|
||||
*
|
||||
* This contains the preparation steps or additional information if the status
|
||||
* is @ref DC_PROVIDER_STATUS_BROKEN.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
* @return A string which must be free()d.
|
||||
*/
|
||||
char* dc_provider_get_markdown (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* Date of when the state was last checked/updated.
|
||||
*
|
||||
* This is returned as a string.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
* @return A string which must be free()d.
|
||||
*/
|
||||
char* dc_provider_get_status_date (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* Whether DC works with this provider.
|
||||
*
|
||||
* Can be one of @ref DC_PROVIDER_STATUS_OK, @ref
|
||||
* DC_PROVIDER_STATUS_PREPARATION and @ref DC_PROVIDER_STATUS_BROKEN.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
* @return The status as a constant number.
|
||||
*/
|
||||
int dc_provider_get_status (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* Free the provider info struct.
|
||||
*
|
||||
* @memberof dc_provider_t
|
||||
* @param provider The dc_provider_t struct.
|
||||
*/
|
||||
void dc_provider_unref (const dc_provider_t* provider);
|
||||
|
||||
|
||||
/**
|
||||
* @class dc_lot_t
|
||||
*
|
||||
@@ -4025,6 +4180,41 @@ void dc_array_add_id (dc_array_t*, uint32_t); // depreca
|
||||
#define DC_SHOW_EMAILS_ALL 2
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup DC_PROVIDER_STATUS DC_PROVIDER_STATUS
|
||||
*
|
||||
* These constants are used as return values for dc_provider_get_status().
|
||||
*
|
||||
* @addtogroup DC_PROVIDER_STATUS
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provider status returned by dc_provider_get_status().
|
||||
*
|
||||
* Works right out of the box without any preperation steps needed
|
||||
*/
|
||||
#define DC_PROVIDER_STATUS_OK 1
|
||||
|
||||
/**
|
||||
* Provider status returned by dc_provider_get_status().
|
||||
*
|
||||
* Works, but preparation steps are needed
|
||||
*/
|
||||
#define DC_PROVIDER_STATUS_PREPARATION 2
|
||||
|
||||
/**
|
||||
* Provider status returned by dc_provider_get_status().
|
||||
*
|
||||
* Doesn't work (too unstable to use falls also in this category)
|
||||
*/
|
||||
#define DC_PROVIDER_STATUS_BROKEN 3
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* TODO: Strings need some doumentation about used placeholders.
|
||||
*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
92
deltachat-ffi/src/providers.rs
Normal file
92
deltachat-ffi/src/providers.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
extern crate deltachat_provider_database;
|
||||
|
||||
use std::ptr;
|
||||
|
||||
use deltachat::dc_tools::{as_str, StrExt};
|
||||
use deltachat_provider_database::StatusState;
|
||||
|
||||
#[no_mangle]
|
||||
pub type dc_provider_t = deltachat_provider_database::Provider;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_new_from_domain(
|
||||
domain: *const libc::c_char,
|
||||
) -> *const dc_provider_t {
|
||||
match deltachat_provider_database::get_provider_info(as_str(domain)) {
|
||||
Some(provider) => provider,
|
||||
None => ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_new_from_email(
|
||||
email: *const libc::c_char,
|
||||
) -> *const dc_provider_t {
|
||||
let domain = deltachat_provider_database::get_domain_from_email(as_str(email));
|
||||
match deltachat_provider_database::get_provider_info(domain) {
|
||||
Some(provider) => provider,
|
||||
None => ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! null_guard {
|
||||
($context:tt) => {
|
||||
if $context.is_null() {
|
||||
return ptr::null_mut() as *mut libc::c_char;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_get_overview_page(
|
||||
provider: *const dc_provider_t,
|
||||
) -> *mut libc::c_char {
|
||||
null_guard!(provider);
|
||||
format!(
|
||||
"{}/{}",
|
||||
deltachat_provider_database::PROVIDER_OVERVIEW_URL,
|
||||
(*provider).overview_page
|
||||
)
|
||||
.strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_get_name(provider: *const dc_provider_t) -> *mut libc::c_char {
|
||||
null_guard!(provider);
|
||||
(*provider).name.strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_get_markdown(
|
||||
provider: *const dc_provider_t,
|
||||
) -> *mut libc::c_char {
|
||||
null_guard!(provider);
|
||||
(*provider).markdown.strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_get_status_date(
|
||||
provider: *const dc_provider_t,
|
||||
) -> *mut libc::c_char {
|
||||
null_guard!(provider);
|
||||
(*provider).status.date.strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_get_status(provider: *const dc_provider_t) -> u32 {
|
||||
if provider.is_null() {
|
||||
return 0;
|
||||
}
|
||||
match (*provider).status.state {
|
||||
StatusState::OK => 1,
|
||||
StatusState::PREPARATION => 2,
|
||||
StatusState::BROKEN => 3,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_provider_unref(_provider: *const dc_provider_t) {
|
||||
()
|
||||
}
|
||||
|
||||
// TODO expose general provider overview url?
|
||||
@@ -36,7 +36,7 @@ pub fn from_sql_derive(input: TokenStream) -> TokenStream {
|
||||
impl rusqlite::types::FromSql for #name {
|
||||
fn column_result(col: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
|
||||
let inner = rusqlite::types::FromSql::column_result(col)?;
|
||||
num_traits::FromPrimitive::from_i64(inner).ok_or(rusqlite::types::FromSqlError::InvalidType)
|
||||
Ok(num_traits::FromPrimitive::from_i64(inner).unwrap_or_default())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use deltachat::chat::{self, Chat};
|
||||
@@ -16,22 +15,21 @@ use deltachat::error::Error;
|
||||
use deltachat::job::*;
|
||||
use deltachat::location;
|
||||
use deltachat::lot::LotState;
|
||||
use deltachat::message::*;
|
||||
use deltachat::message::{self, Message, MessageState};
|
||||
use deltachat::peerstate::*;
|
||||
use deltachat::qr::*;
|
||||
use deltachat::sql;
|
||||
use deltachat::types::*;
|
||||
use deltachat::x::*;
|
||||
use num_traits::FromPrimitive;
|
||||
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.
|
||||
/// e.g. bitmask 7 triggers actions definded with bits 1, 2 and 4.
|
||||
pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
||||
info!(context, 0, "Resetting tables ({})...", bits);
|
||||
info!(context, "Resetting tables ({})...", bits);
|
||||
if 0 != bits & 1 {
|
||||
sql::execute(context, &context.sql, "DELETE FROM jobs;", params![]).unwrap();
|
||||
info!(context, 0, "(1) Jobs reset.");
|
||||
info!(context, "(1) Jobs reset.");
|
||||
}
|
||||
if 0 != bits & 2 {
|
||||
sql::execute(
|
||||
@@ -41,11 +39,11 @@ pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
||||
params![],
|
||||
)
|
||||
.unwrap();
|
||||
info!(context, 0, "(2) Peerstates reset.");
|
||||
info!(context, "(2) Peerstates reset.");
|
||||
}
|
||||
if 0 != bits & 4 {
|
||||
sql::execute(context, &context.sql, "DELETE FROM keypairs;", params![]).unwrap();
|
||||
info!(context, 0, "(4) Private keypairs reset.");
|
||||
info!(context, "(4) Private keypairs reset.");
|
||||
}
|
||||
if 0 != bits & 8 {
|
||||
sql::execute(
|
||||
@@ -84,32 +82,22 @@ pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
||||
)
|
||||
.unwrap();
|
||||
sql::execute(context, &context.sql, "DELETE FROM leftgrps;", params![]).unwrap();
|
||||
info!(context, 0, "(8) Rest but server config reset.");
|
||||
info!(context, "(8) Rest but server config reset.");
|
||||
}
|
||||
|
||||
context.call_cb(Event::MSGS_CHANGED, 0, 0);
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
msg_id: 0,
|
||||
});
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
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: size_t = 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);
|
||||
fn dc_poke_eml_file(context: &Context, filename: impl AsRef<Path>) -> Result<(), Error> {
|
||||
let data = dc_read_file(context, filename)?;
|
||||
|
||||
success
|
||||
unsafe { dc_receive_imf(context, &data, "import", 0, 0) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Import a file to the database.
|
||||
@@ -120,146 +108,113 @@ unsafe fn dc_poke_eml_file(context: &Context, filename: *const libc::c_char) ->
|
||||
/// @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.
|
||||
unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
|
||||
fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
|
||||
if !context.sql.is_open() {
|
||||
error!(context, 0, "Import: Database not opened.");
|
||||
error!(context, "Import: Database not opened.");
|
||||
return 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;
|
||||
let real_spec: String;
|
||||
let mut read_cnt = 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 = dc_strdup(spec);
|
||||
real_spec = to_string(spec);
|
||||
context
|
||||
.sql
|
||||
.set_config(context, "import_spec", Some(as_str(real_spec)))
|
||||
.set_config(context, "import_spec", Some(&real_spec))
|
||||
.unwrap();
|
||||
ok_to_continue = true;
|
||||
} else {
|
||||
let rs = context.sql.get_config(context, "import_spec");
|
||||
if rs.is_none() {
|
||||
error!(context, 0, "Import: No file or folder given.");
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
error!(context, "Import: No file or folder given.");
|
||||
return 0;
|
||||
}
|
||||
real_spec = rs.unwrap_or_default().strdup();
|
||||
real_spec = rs.unwrap();
|
||||
}
|
||||
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) {
|
||||
if let Some(suffix) = dc_get_filesuffix_lc(&real_spec) {
|
||||
if suffix == "eml" {
|
||||
if dc_poke_eml_file(context, &real_spec).is_ok() {
|
||||
read_cnt += 1
|
||||
}
|
||||
ok_to_continue2 = true;
|
||||
}
|
||||
} 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;
|
||||
} 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,
|
||||
0,
|
||||
"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, 0, "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
|
||||
}
|
||||
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_continue2 = true;
|
||||
}
|
||||
}
|
||||
if ok_to_continue2 {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Import: {} items read from \"{}\".",
|
||||
read_cnt,
|
||||
as_str(real_spec)
|
||||
);
|
||||
if read_cnt > 0 {
|
||||
context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t);
|
||||
}
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
|
||||
free(real_spec as *mut libc::c_void);
|
||||
free(suffix as *mut libc::c_void);
|
||||
success
|
||||
info!(
|
||||
context,
|
||||
"Import: {} items read from \"{}\".", read_cnt, &real_spec
|
||||
);
|
||||
if read_cnt > 0 {
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
msg_id: 0,
|
||||
});
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
let contact = Contact::get_by_id(context, dc_msg_get_from_id(msg)).expect("invalid contact");
|
||||
let contact = Contact::get_by_id(context, msg.get_from_id()).expect("invalid contact");
|
||||
let contact_name = contact.get_name();
|
||||
let contact_id = contact.get_id();
|
||||
|
||||
let statestr = match dc_msg_get_state(msg) {
|
||||
let statestr = match msg.get_state() {
|
||||
MessageState::OutPending => " o",
|
||||
MessageState::OutDelivered => " √",
|
||||
MessageState::OutMdnRcvd => " √√",
|
||||
MessageState::OutFailed => " !!",
|
||||
_ => "",
|
||||
};
|
||||
let temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
|
||||
let msgtext = dc_msg_get_text(msg);
|
||||
let temp2 = dc_timestamp_to_str(msg.get_timestamp());
|
||||
let msgtext = msg.get_text();
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}#{}{}{}: {} (Contact#{}): {} {}{}{}{} [{}]",
|
||||
prefix.as_ref(),
|
||||
dc_msg_get_id(msg) as libc::c_int,
|
||||
if 0 != dc_msg_get_showpadlock(msg) {
|
||||
"🔒"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if dc_msg_has_location(msg) { "📍" } else { "" },
|
||||
msg.get_id() as libc::c_int,
|
||||
if msg.get_showpadlock() { "🔒" } else { "" },
|
||||
if msg.has_location() { "📍" } else { "" },
|
||||
&contact_name,
|
||||
contact_id,
|
||||
as_str(msgtext),
|
||||
if dc_msg_is_starred(msg) { "★" } else { "" },
|
||||
if dc_msg_get_from_id(msg) == 1 as libc::c_uint {
|
||||
msgtext.unwrap_or_default(),
|
||||
if msg.is_starred() { "★" } else { "" },
|
||||
if msg.get_from_id() == 1 as libc::c_uint {
|
||||
""
|
||||
} else if dc_msg_get_state(msg) == MessageState::InSeen {
|
||||
} else if msg.get_state() == MessageState::InSeen {
|
||||
"[SEEN]"
|
||||
} else if dc_msg_get_state(msg) == MessageState::InNoticed {
|
||||
} else if msg.get_state() == MessageState::InNoticed {
|
||||
"[NOTICED]"
|
||||
} else {
|
||||
"[FRESH]"
|
||||
},
|
||||
if 0 != dc_msg_is_info(msg) {
|
||||
"[INFO]"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if msg.is_info() { "[INFO]" } else { "" },
|
||||
statestr,
|
||||
&temp2,
|
||||
);
|
||||
free(msgtext as *mut libc::c_void);
|
||||
}
|
||||
|
||||
unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error> {
|
||||
@@ -268,7 +223,6 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
|
||||
if msg_id == 9 as libc::c_uint {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"--------------------------------------------------------------------------------"
|
||||
);
|
||||
|
||||
@@ -276,19 +230,19 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
|
||||
} else if msg_id > 0 {
|
||||
if lines_out == 0 {
|
||||
info!(
|
||||
context, 0,
|
||||
context,
|
||||
"--------------------------------------------------------------------------------",
|
||||
);
|
||||
lines_out += 1
|
||||
}
|
||||
let msg = dc_get_msg(context, msg_id)?;
|
||||
let msg = Message::load_from_db(context, msg_id)?;
|
||||
log_msg(context, "Msg", &msg);
|
||||
}
|
||||
}
|
||||
if lines_out > 0 {
|
||||
info!(
|
||||
context,
|
||||
0, "--------------------------------------------------------------------------------"
|
||||
"--------------------------------------------------------------------------------"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
@@ -305,7 +259,7 @@ unsafe fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||
let name = contact.get_name();
|
||||
let addr = contact.get_addr();
|
||||
let verified_state = contact.is_verified();
|
||||
let verified_state = contact.is_verified(context);
|
||||
let verified_str = if VerifiedStatus::Unverified != verified_state {
|
||||
if verified_state == VerifiedStatus::BidirectVerified {
|
||||
" √√"
|
||||
@@ -337,17 +291,11 @@ unsafe fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
|
||||
);
|
||||
}
|
||||
|
||||
info!(context, 0, "Contact#{}: {}{}", contact_id, line, line2);
|
||||
info!(context, "Contact#{}: {}{}", contact_id, line, line2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut S_IS_AUTH: libc::c_int = 0;
|
||||
|
||||
pub unsafe fn dc_cmdline_skip_auth() {
|
||||
S_IS_AUTH = 1;
|
||||
}
|
||||
|
||||
fn chat_prefix(chat: &Chat) -> &'static str {
|
||||
chat.typ.into()
|
||||
}
|
||||
@@ -369,12 +317,8 @@ 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.
|
||||
@@ -428,7 +372,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
send <text>\n\
|
||||
send-garbage\n\
|
||||
sendimage <file> [<text>]\n\
|
||||
sendfile <file>\n\
|
||||
sendfile <file> [<text>]\n\
|
||||
draft [<text>]\n\
|
||||
listmedia\n\
|
||||
archive <chat-id>\n\
|
||||
@@ -461,52 +405,24 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
============================================="
|
||||
),
|
||||
},
|
||||
"auth" => {
|
||||
if 0 == S_IS_AUTH {
|
||||
let is_pw = context
|
||||
.get_config(config::Config::MailPw)
|
||||
.unwrap_or_default();
|
||||
if arg1 == is_pw {
|
||||
S_IS_AUTH = 1;
|
||||
} else {
|
||||
println!("Bad password.");
|
||||
}
|
||||
} else {
|
||||
println!("Already authorized.");
|
||||
}
|
||||
}
|
||||
"open" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <file> missing");
|
||||
dc_close(context);
|
||||
ensure!(dc_open(context, arg1, None), "Open failed");
|
||||
}
|
||||
"close" => {
|
||||
dc_close(context);
|
||||
}
|
||||
"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");
|
||||
};
|
||||
}
|
||||
"initiate-key-transfer" => match dc_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),
|
||||
},
|
||||
"get-setupcodebegin" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
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(&msg);
|
||||
let msg = Message::load_from_db(context, msg_id)?;
|
||||
if msg.is_setupmessage() {
|
||||
let setupcodebegin = msg.get_setupcodebegin(context);
|
||||
println!(
|
||||
"The setup code for setup message Msg#{} starts with: {}",
|
||||
msg_id,
|
||||
as_str(setupcodebegin),
|
||||
setupcodebegin.unwrap_or_default(),
|
||||
);
|
||||
free(setupcodebegin as *mut libc::c_void);
|
||||
} else {
|
||||
bail!("Msg#{} is no setup message.", msg_id,);
|
||||
}
|
||||
@@ -516,43 +432,34 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
!arg1.is_empty() && !arg2.is_empty(),
|
||||
"Arguments <msg-id> <setup-code> expected"
|
||||
);
|
||||
if 0 == dc_continue_key_transfer(context, arg1.parse()?, arg2_c) {
|
||||
bail!("Continue key transfer failed");
|
||||
}
|
||||
dc_continue_key_transfer(context, arg1.parse()?, &arg2)?;
|
||||
}
|
||||
"has-backup" => {
|
||||
let ret = dc_imex_has_backup(context, context.get_blobdir());
|
||||
if ret.is_null() {
|
||||
println!("No backup found.");
|
||||
}
|
||||
dc_imex_has_backup(context, blobdir)?;
|
||||
}
|
||||
"export-backup" => {
|
||||
dc_imex(context, 11, context.get_blobdir(), ptr::null());
|
||||
dc_imex(context, 11, Some(blobdir));
|
||||
}
|
||||
"import-backup" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <backup-file> missing.");
|
||||
dc_imex(context, 12, arg1_c, ptr::null());
|
||||
dc_imex(context, 12, Some(arg1));
|
||||
}
|
||||
"export-keys" => {
|
||||
dc_imex(context, 1, context.get_blobdir(), ptr::null());
|
||||
dc_imex(context, 1, Some(blobdir));
|
||||
}
|
||||
"import-keys" => {
|
||||
dc_imex(context, 2, context.get_blobdir(), ptr::null());
|
||||
dc_imex(context, 2, Some(blobdir));
|
||||
}
|
||||
"export-setup" => {
|
||||
let setup_code = dc_create_setup_code(context);
|
||||
let file_name: *mut libc::c_char = dc_mprintf(
|
||||
b"%s/autocrypt-setup-message.html\x00" as *const u8 as *const libc::c_char,
|
||||
context.get_blobdir(),
|
||||
);
|
||||
let file_name = blobdir.join("autocrypt-setup-message.html");
|
||||
let file_content = dc_render_setup_file(context, &setup_code)?;
|
||||
std::fs::write(as_str(file_name), file_content)?;
|
||||
std::fs::write(&file_name, file_content)?;
|
||||
println!(
|
||||
"Setup message written to: {}\nSetup code: {}",
|
||||
as_str(file_name),
|
||||
file_name.display(),
|
||||
&setup_code,
|
||||
);
|
||||
free(file_name as *mut libc::c_void);
|
||||
}
|
||||
"poke" => {
|
||||
ensure!(0 != poke_spec(context, arg1_c), "Poke failed");
|
||||
@@ -579,7 +486,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
println!("{}={:?}", key, val);
|
||||
}
|
||||
"info" => {
|
||||
println!("{}", to_string(dc_get_info(context)));
|
||||
println!("{:#?}", context.get_info());
|
||||
}
|
||||
"maybenetwork" => {
|
||||
maybe_network(context);
|
||||
@@ -599,17 +506,16 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let cnt = chatlist.len();
|
||||
if cnt > 0 {
|
||||
info!(
|
||||
context, 0,
|
||||
context,
|
||||
"================================================================================"
|
||||
);
|
||||
|
||||
for i in (0..cnt).rev() {
|
||||
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
|
||||
let temp_subtitle = chat.get_subtitle();
|
||||
let temp_subtitle = chat.get_subtitle(context);
|
||||
let temp_name = chat.get_name();
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}#{}: {} [{}] [{} fresh]",
|
||||
chat_prefix(&chat),
|
||||
chat.get_id(),
|
||||
@@ -617,7 +523,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
temp_subtitle,
|
||||
chat::get_fresh_msg_cnt(context, chat.get_id()),
|
||||
);
|
||||
let lot = chatlist.get_summary(i, Some(&chat));
|
||||
let lot = chatlist.get_summary(context, i, Some(&chat));
|
||||
let statestr = if chat.is_archived() {
|
||||
" [Archived]"
|
||||
} else {
|
||||
@@ -634,7 +540,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let text2 = lot.get_text2();
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}{}{}{} [{}]{}",
|
||||
text1.unwrap_or(""),
|
||||
if text1.is_some() { ": " } else { "" },
|
||||
@@ -648,13 +553,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
},
|
||||
);
|
||||
info!(
|
||||
context, 0,
|
||||
context,
|
||||
"================================================================================"
|
||||
);
|
||||
}
|
||||
}
|
||||
if location::is_sending_locations_to_chat(context, 0 as uint32_t) {
|
||||
info!(context, 0, "Location streaming enabled.");
|
||||
if location::is_sending_locations_to_chat(context, 0) {
|
||||
info!(context, "Location streaming enabled.");
|
||||
}
|
||||
println!("{} chats", cnt);
|
||||
}
|
||||
@@ -673,11 +578,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let sel_chat = sel_chat.as_ref().unwrap();
|
||||
|
||||
let msglist = chat::get_chat_msgs(context, sel_chat.get_id(), 0x1, 0);
|
||||
let temp2 = sel_chat.get_subtitle();
|
||||
let temp2 = sel_chat.get_subtitle(context);
|
||||
let temp_name = sel_chat.get_name();
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}#{}: {} [{}]{}",
|
||||
chat_prefix(sel_chat),
|
||||
sel_chat.get_id(),
|
||||
@@ -690,7 +594,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
},
|
||||
);
|
||||
log_msglist(context, &msglist)?;
|
||||
if let Ok(draft) = chat::get_draft(context, sel_chat.get_id()) {
|
||||
if let Some(draft) = chat::get_draft(context, sel_chat.get_id())? {
|
||||
log_msg(context, "Draft", &draft);
|
||||
}
|
||||
|
||||
@@ -703,7 +607,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
"createchat" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
||||
let contact_id: libc::c_int = arg1.parse()?;
|
||||
let chat_id = chat::create_by_contact_id(context, contact_id as uint32_t)?;
|
||||
let chat_id = chat::create_by_contact_id(context, contact_id as u32)?;
|
||||
|
||||
println!("Single#{} created successfully.", chat_id,);
|
||||
}
|
||||
@@ -732,10 +636,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
||||
|
||||
let contact_id_0: libc::c_int = arg1.parse()?;
|
||||
if 0 != chat::add_contact_to_chat(
|
||||
if chat::add_contact_to_chat(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
contact_id_0 as uint32_t,
|
||||
contact_id_0 as u32,
|
||||
) {
|
||||
println!("Contact added to chat.");
|
||||
} else {
|
||||
@@ -749,7 +653,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
chat::remove_contact_from_chat(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
contact_id_1 as uint32_t,
|
||||
contact_id_1 as u32,
|
||||
)?;
|
||||
|
||||
println!("Contact added to chat.");
|
||||
@@ -773,7 +677,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
let contacts = chat::get_chat_contacts(context, sel_chat.as_ref().unwrap().get_id());
|
||||
info!(context, 0, "Memberlist:");
|
||||
info!(context, "Memberlist:");
|
||||
|
||||
log_contactlist(context, &contacts);
|
||||
println!(
|
||||
@@ -801,7 +705,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let marker = location.marker.as_ref().unwrap_or(&default_marker);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}",
|
||||
location.location_id,
|
||||
dc_timestamp_to_str(location.timestamp),
|
||||
@@ -815,7 +718,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
);
|
||||
}
|
||||
if locations.is_empty() {
|
||||
info!(context, 0, "No locations.");
|
||||
info!(context, "No locations.");
|
||||
}
|
||||
}
|
||||
"sendlocations" => {
|
||||
@@ -862,18 +765,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"sendimage" | "sendfile" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given.");
|
||||
ensure!(!arg1.is_empty(), "No file given.");
|
||||
|
||||
let mut msg = dc_msg_new(
|
||||
context,
|
||||
if arg0 == "sendimage" {
|
||||
Viewtype::Image
|
||||
} else {
|
||||
Viewtype::File
|
||||
},
|
||||
);
|
||||
dc_msg_set_file(&mut msg, arg1_c, ptr::null());
|
||||
dc_msg_set_text(&mut msg, arg2_c);
|
||||
let mut msg = Message::new(if arg0 == "sendimage" {
|
||||
Viewtype::Image
|
||||
} else {
|
||||
Viewtype::File
|
||||
});
|
||||
msg.set_file(arg1, None);
|
||||
if !arg2.is_empty() {
|
||||
msg.set_text(Some(arg2.to_string()));
|
||||
}
|
||||
chat::send_msg(context, sel_chat.as_ref().unwrap().get_id(), &mut msg)?;
|
||||
}
|
||||
"listmsgs" => {
|
||||
@@ -885,7 +787,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
0 as libc::c_uint
|
||||
};
|
||||
|
||||
let msglist = dc_search_msgs(context, chat, arg1_c);
|
||||
let msglist = context.search_msgs(chat, arg1);
|
||||
|
||||
log_msglist(context, &msglist)?;
|
||||
println!("{} messages.", msglist.len());
|
||||
@@ -894,8 +796,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 = dc_msg_new(context, Viewtype::Text);
|
||||
dc_msg_set_text(&mut draft, arg1_c);
|
||||
let mut draft = Message::new(Viewtype::Text);
|
||||
draft.set_text(Some(arg1.to_string()));
|
||||
chat::set_draft(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
@@ -944,11 +846,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
"msginfo" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let id = arg1.parse()?;
|
||||
let res = dc_get_msg_info(context, id);
|
||||
println!("{}", as_str(res));
|
||||
let res = message::get_msg_info(context, id);
|
||||
println!("{}", res);
|
||||
}
|
||||
"listfresh" => {
|
||||
let msglist = dc_get_fresh_msgs(context);
|
||||
let msglist = context.get_fresh_msgs();
|
||||
|
||||
log_msglist(context, &msglist)?;
|
||||
print!("{} fresh messages.", msglist.len());
|
||||
@@ -962,30 +864,25 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let mut msg_ids = [0; 1];
|
||||
let chat_id = arg2.parse()?;
|
||||
msg_ids[0] = arg1.parse()?;
|
||||
chat::forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
|
||||
chat::forward_msgs(context, &msg_ids, chat_id);
|
||||
}
|
||||
"markseen" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let mut msg_ids = [0; 1];
|
||||
msg_ids[0] = arg1.parse()?;
|
||||
dc_markseen_msgs(context, msg_ids.as_mut_ptr(), 1);
|
||||
message::markseen_msgs(context, &msg_ids);
|
||||
}
|
||||
"star" | "unstar" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
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 },
|
||||
);
|
||||
message::star_msgs(context, &msg_ids, arg0 == "star");
|
||||
}
|
||||
"delmsg" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let mut ids = [0; 1];
|
||||
ids[0] = arg1.parse()?;
|
||||
dc_delete_msgs(context, ids.as_mut_ptr(), 1);
|
||||
message::delete_msgs(context, &ids);
|
||||
}
|
||||
"listcontacts" | "contacts" | "listverified" => {
|
||||
let contacts = Contact::get_all(
|
||||
@@ -1054,20 +951,21 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
res.get_text2()
|
||||
);
|
||||
}
|
||||
"event" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <id> missing.");
|
||||
let event = arg1.parse()?;
|
||||
let event = Event::from_u32(event).ok_or(format_err!("Event::from_u32({})", event))?;
|
||||
let r = context.call_cb(event, 0 as uintptr_t, 0 as uintptr_t);
|
||||
println!(
|
||||
"Sending event {:?}({}), received value {}.",
|
||||
event, event as usize, r as libc::c_int,
|
||||
);
|
||||
}
|
||||
// TODO: implement this again, unclear how to match this through though, without writing a parser.
|
||||
// "event" => {
|
||||
// ensure!(!arg1.is_empty(), "Argument <id> missing.");
|
||||
// let event = arg1.parse()?;
|
||||
// let event = Event::from_u32(event).ok_or(format_err!("Event::from_u32({})", event))?;
|
||||
// let r = context.call_cb(event, 0 as libc::uintptr_t, 0 as libc::uintptr_t);
|
||||
// println!(
|
||||
// "Sending event {:?}({}), received value {}.",
|
||||
// event, event as usize, r as libc::c_int,
|
||||
// );
|
||||
// }
|
||||
"fileinfo" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <file> missing.");
|
||||
|
||||
if let Some(buf) = dc_read_file_safe(context, &arg1) {
|
||||
if let Ok(buf) = dc_read_file(context, &arg1) {
|
||||
let (width, height) = dc_get_filemeta(&buf)?;
|
||||
println!("width={}, height={}", width, height);
|
||||
} else {
|
||||
@@ -1079,7 +977,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
|
||||
free(arg1_c as *mut _);
|
||||
free(arg2_c as *mut _);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -14,20 +14,19 @@ extern crate lazy_static;
|
||||
extern crate rusqlite;
|
||||
|
||||
use std::borrow::Cow::{self, Borrowed, Owned};
|
||||
use std::ptr;
|
||||
use std::io::{self, Write};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use deltachat::config;
|
||||
use deltachat::configure::*;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_securejoin::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::job::*;
|
||||
use deltachat::oauth2::*;
|
||||
use deltachat::types::*;
|
||||
use deltachat::x::*;
|
||||
use deltachat::securejoin::*;
|
||||
use deltachat::Event;
|
||||
use rustyline::completion::{Completer, FilenameCompleter, Pair};
|
||||
use rustyline::config::OutputStreamType;
|
||||
use rustyline::error::ReadlineError;
|
||||
@@ -42,96 +41,75 @@ use self::cmdline::*;
|
||||
|
||||
// Event Handler
|
||||
|
||||
unsafe extern "C" fn receive_event(
|
||||
_context: &Context,
|
||||
event: Event,
|
||||
data1: uintptr_t,
|
||||
data2: uintptr_t,
|
||||
) -> uintptr_t {
|
||||
fn receive_event(_context: &Context, event: Event) -> libc::uintptr_t {
|
||||
match event {
|
||||
Event::GET_STRING => {}
|
||||
Event::INFO => {
|
||||
Event::GetString { .. } => {}
|
||||
Event::Info(msg) => {
|
||||
/* do not show the event as this would fill the screen */
|
||||
println!("{}", to_string(data2 as *const _),);
|
||||
println!("{}", msg);
|
||||
}
|
||||
Event::SMTP_CONNECTED => {
|
||||
println!("[DC_EVENT_SMTP_CONNECTED] {}", to_string(data2 as *const _));
|
||||
Event::SmtpConnected(msg) => {
|
||||
println!("[DC_EVENT_SMTP_CONNECTED] {}", msg);
|
||||
}
|
||||
Event::IMAP_CONNECTED => {
|
||||
println!("[DC_EVENT_IMAP_CONNECTED] {}", to_string(data2 as *const _),);
|
||||
Event::ImapConnected(msg) => {
|
||||
println!("[DC_EVENT_IMAP_CONNECTED] {}", msg);
|
||||
}
|
||||
Event::SMTP_MESSAGE_SENT => {
|
||||
println!(
|
||||
"[DC_EVENT_SMTP_MESSAGE_SENT] {}",
|
||||
to_string(data2 as *const _),
|
||||
);
|
||||
Event::SmtpMessageSent(msg) => {
|
||||
println!("[DC_EVENT_SMTP_MESSAGE_SENT] {}", msg);
|
||||
}
|
||||
Event::WARNING => {
|
||||
println!("[Warning] {}", to_string(data2 as *const _),);
|
||||
Event::Warning(msg) => {
|
||||
println!("[Warning] {}", msg);
|
||||
}
|
||||
Event::ERROR => {
|
||||
println!(
|
||||
"\x1b[31m[DC_EVENT_ERROR] {}\x1b[0m",
|
||||
to_string(data2 as *const _),
|
||||
);
|
||||
Event::Error(msg) => {
|
||||
println!("\x1b[31m[DC_EVENT_ERROR] {}\x1b[0m", msg);
|
||||
}
|
||||
Event::ERROR_NETWORK => {
|
||||
println!(
|
||||
"\x1b[31m[DC_EVENT_ERROR_NETWORK] first={}, msg={}\x1b[0m",
|
||||
data1 as usize,
|
||||
to_string(data2 as *const _),
|
||||
);
|
||||
Event::ErrorNetwork(msg) => {
|
||||
println!("\x1b[31m[DC_EVENT_ERROR_NETWORK] msg={}\x1b[0m", msg);
|
||||
}
|
||||
Event::ERROR_SELF_NOT_IN_GROUP => {
|
||||
println!(
|
||||
"\x1b[31m[DC_EVENT_ERROR_SELF_NOT_IN_GROUP] {}\x1b[0m",
|
||||
to_string(data2 as *const _),
|
||||
);
|
||||
Event::ErrorSelfNotInGroup(msg) => {
|
||||
println!("\x1b[31m[DC_EVENT_ERROR_SELF_NOT_IN_GROUP] {}\x1b[0m", msg);
|
||||
}
|
||||
Event::MSGS_CHANGED => {
|
||||
Event::MsgsChanged { chat_id, msg_id } => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_MSGS_CHANGED({}, {})}}\n\x1b[0m",
|
||||
data1 as usize, data2 as usize,
|
||||
"\x1b[33m{{Received DC_EVENT_MSGS_CHANGED(chat_id={}, msg_id={})}}\n\x1b[0m",
|
||||
chat_id, msg_id,
|
||||
);
|
||||
}
|
||||
Event::CONTACTS_CHANGED => {
|
||||
Event::ContactsChanged(_) => {
|
||||
print!("\x1b[33m{{Received DC_EVENT_CONTACTS_CHANGED()}}\n\x1b[0m");
|
||||
}
|
||||
Event::LOCATION_CHANGED => {
|
||||
Event::LocationChanged(contact) => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_LOCATION_CHANGED(contact={})}}\n\x1b[0m",
|
||||
data1 as usize,
|
||||
"\x1b[33m{{Received DC_EVENT_LOCATION_CHANGED(contact={:?})}}\n\x1b[0m",
|
||||
contact,
|
||||
);
|
||||
}
|
||||
Event::CONFIGURE_PROGRESS => {
|
||||
Event::ConfigureProgress(progress) => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_CONFIGURE_PROGRESS({} ‰)}}\n\x1b[0m",
|
||||
data1 as usize,
|
||||
progress,
|
||||
);
|
||||
}
|
||||
Event::IMEX_PROGRESS => {
|
||||
Event::ImexProgress(progress) => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_IMEX_PROGRESS({} ‰)}}\n\x1b[0m",
|
||||
data1 as usize,
|
||||
progress,
|
||||
);
|
||||
}
|
||||
Event::IMEX_FILE_WRITTEN => {
|
||||
Event::ImexFileWritten(file) => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_IMEX_FILE_WRITTEN({})}}\n\x1b[0m",
|
||||
to_string(data1 as *const _)
|
||||
file.display()
|
||||
);
|
||||
}
|
||||
Event::CHAT_MODIFIED => {
|
||||
Event::ChatModified(chat) => {
|
||||
print!(
|
||||
"\x1b[33m{{Received DC_EVENT_CHAT_MODIFIED({})}}\n\x1b[0m",
|
||||
data1 as usize,
|
||||
chat
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
print!(
|
||||
"\x1b[33m{{Received {:?}({}, {})}}\n\x1b[0m",
|
||||
event, data1 as usize, data2 as usize,
|
||||
);
|
||||
print!("\x1b[33m{{Received {:?}}}\n\x1b[0m", event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,17 +363,15 @@ impl Highlighter for DcHelper {
|
||||
impl Helper for DcHelper {}
|
||||
|
||||
fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
|
||||
let mut context = dc_context_new(Some(receive_event), ptr::null_mut(), Some("CLI".into()));
|
||||
|
||||
unsafe { dc_cmdline_skip_auth() };
|
||||
|
||||
if args.len() == 2 {
|
||||
if unsafe { !dc_open(&mut context, &args[1], None) } {
|
||||
println!("Error: Cannot open {}.", args[0],);
|
||||
}
|
||||
} else if args.len() != 1 {
|
||||
if args.len() < 2 {
|
||||
println!("Error: Bad arguments, expected [db-name].");
|
||||
return Err(format_err!("No db-name specified"));
|
||||
}
|
||||
let context = Context::new(
|
||||
Box::new(receive_event),
|
||||
"CLI".into(),
|
||||
Path::new(&args[1]).to_path_buf(),
|
||||
)?;
|
||||
|
||||
println!("Delta Chat Core is awaiting your commands.");
|
||||
|
||||
@@ -463,11 +439,6 @@ 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" => {
|
||||
@@ -516,38 +487,33 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
|
||||
}
|
||||
"getqr" | "getbadqr" => {
|
||||
start_threads(ctx.clone());
|
||||
let qrstr =
|
||||
dc_get_securejoin_qr(&ctx.read().unwrap(), arg1.parse().unwrap_or_default());
|
||||
if !qrstr.is_null() && 0 != *qrstr.offset(0isize) as libc::c_int {
|
||||
if arg0 == "getbadqr" && strlen(qrstr) > 40 {
|
||||
let mut i: libc::c_int = 12i32;
|
||||
while i < 22i32 {
|
||||
*qrstr.offset(i as isize) = '0' as i32 as libc::c_char;
|
||||
i += 1
|
||||
if let Some(mut qr) =
|
||||
dc_get_securejoin_qr(&ctx.read().unwrap(), arg1.parse().unwrap_or_default())
|
||||
{
|
||||
if !qr.is_empty() {
|
||||
if arg0 == "getbadqr" && qr.len() > 40 {
|
||||
qr.replace_range(12..22, "0000000000")
|
||||
}
|
||||
println!("{}", qr);
|
||||
let output = Command::new("qrencode")
|
||||
.args(&["-t", "ansiutf8", qr.as_str(), "-o", "-"])
|
||||
.output()
|
||||
.expect("failed to execute process");
|
||||
io::stdout().write_all(&output.stdout).unwrap();
|
||||
io::stderr().write_all(&output.stderr).unwrap();
|
||||
}
|
||||
println!("{}", to_string(qrstr as *const _));
|
||||
let syscmd = dc_mprintf(
|
||||
b"qrencode -t ansiutf8 \"%s\" -o -\x00" as *const u8 as *const libc::c_char,
|
||||
qrstr,
|
||||
);
|
||||
system(syscmd);
|
||||
free(syscmd as *mut libc::c_void);
|
||||
}
|
||||
free(qrstr as *mut libc::c_void);
|
||||
}
|
||||
"joinqr" => {
|
||||
start_threads(ctx.clone());
|
||||
if !arg0.is_empty() {
|
||||
dc_join_securejoin(&ctx.read().unwrap(), arg1_c);
|
||||
dc_join_securejoin(&ctx.read().unwrap(), arg1);
|
||||
}
|
||||
}
|
||||
"exit" | "quit" => return Ok(ExitResult::Exit),
|
||||
_ => dc_cmdline(&ctx.read().unwrap(), line)?,
|
||||
}
|
||||
|
||||
free(arg1_c as *mut _);
|
||||
|
||||
Ok(ExitResult::Continue)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
extern crate deltachat;
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::{thread, time};
|
||||
use tempfile::tempdir;
|
||||
@@ -9,42 +8,43 @@ use deltachat::chat;
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::config;
|
||||
use deltachat::configure::*;
|
||||
use deltachat::constants::Event;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::job::{
|
||||
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
|
||||
};
|
||||
use deltachat::Event;
|
||||
|
||||
extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize {
|
||||
println!("[{:?}]", event);
|
||||
fn cb(_ctx: &Context, event: Event) -> usize {
|
||||
print!("[{:?}]", event);
|
||||
|
||||
match event {
|
||||
Event::CONFIGURE_PROGRESS => {
|
||||
println!(" progress: {}", data1);
|
||||
Event::ConfigureProgress(progress) => {
|
||||
print!(" progress: {}\n", progress);
|
||||
0
|
||||
}
|
||||
Event::INFO | Event::WARNING | Event::ERROR | Event::ERROR_NETWORK => {
|
||||
println!(
|
||||
" {}",
|
||||
unsafe { CStr::from_ptr(data2 as *const _) }
|
||||
.to_str()
|
||||
.unwrap()
|
||||
);
|
||||
Event::Info(msg) | Event::Warning(msg) | Event::Error(msg) | Event::ErrorNetwork(msg) => {
|
||||
print!(" {}\n", msg);
|
||||
0
|
||||
}
|
||||
_ => {
|
||||
print!("\n");
|
||||
0
|
||||
}
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
|
||||
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 = dc_get_info(&ctx);
|
||||
let info_s = CStr::from_ptr(info);
|
||||
let info = ctx.get_info();
|
||||
let duration = time::Duration::from_millis(4000);
|
||||
println!("info: {}", info_s.to_str().unwrap());
|
||||
println!("info: {:#?}", info);
|
||||
|
||||
let ctx = Arc::new(ctx);
|
||||
let ctx1 = ctx.clone();
|
||||
@@ -73,13 +73,6 @@ fn main() {
|
||||
}
|
||||
});
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
|
||||
println!("opening database {:?}", dbfile);
|
||||
|
||||
assert!(dc_open(&ctx, dbfile.to_str().unwrap(), None));
|
||||
|
||||
println!("configuring");
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
assert_eq!(args.len(), 2, "missing password");
|
||||
@@ -101,7 +94,7 @@ fn main() {
|
||||
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
|
||||
|
||||
for i in 0..chats.len() {
|
||||
let summary = chats.get_summary(0, None);
|
||||
let summary = chats.get_summary(&ctx, 0, None);
|
||||
let text1 = summary.get_text1();
|
||||
let text2 = summary.get_text2();
|
||||
println!("chat: {} - {:?} - {:?}", i, text1, text2,);
|
||||
@@ -130,6 +123,5 @@ fn main() {
|
||||
t2.join().unwrap();
|
||||
|
||||
println!("closing");
|
||||
dc_close(&ctx);
|
||||
}
|
||||
}
|
||||
|
||||
52
misc.c
52
misc.c
@@ -1,52 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "misc.h"
|
||||
|
||||
|
||||
static char* internal_dc_strdup(const char* s) /* strdup(NULL) is undefined, save_strdup(NULL) returns an empty string in this case */
|
||||
{
|
||||
char* ret = NULL;
|
||||
if (s) {
|
||||
if ((ret=strdup(s))==NULL) {
|
||||
exit(16); /* cannot allocate (little) memory, unrecoverable error */
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((ret=(char*)calloc(1, 1))==NULL) {
|
||||
exit(17); /* cannot allocate little memory, unrecoverable error */
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
char* dc_mprintf(const char* format, ...)
|
||||
{
|
||||
char testbuf[1];
|
||||
char* buf = NULL;
|
||||
int char_cnt_without_zero = 0;
|
||||
|
||||
va_list argp;
|
||||
va_list argp_copy;
|
||||
va_start(argp, format);
|
||||
va_copy(argp_copy, argp);
|
||||
|
||||
char_cnt_without_zero = vsnprintf(testbuf, 0, format, argp);
|
||||
va_end(argp);
|
||||
if (char_cnt_without_zero < 0) {
|
||||
va_end(argp_copy);
|
||||
return internal_dc_strdup("ErrFmt");
|
||||
}
|
||||
|
||||
buf = malloc(char_cnt_without_zero+2 /* +1 would be enough, however, protect against off-by-one-errors */);
|
||||
if (buf==NULL) {
|
||||
va_end(argp_copy);
|
||||
return internal_dc_strdup("ErrMem");
|
||||
}
|
||||
|
||||
vsnprintf(buf, char_cnt_without_zero+1, format, argp_copy);
|
||||
va_end(argp_copy);
|
||||
return buf;
|
||||
}
|
||||
1
misc.h
1
misc.h
@@ -1 +0,0 @@
|
||||
char* dc_mprintf (const char* format, ...); /* The result must be free()'d. */
|
||||
42
mmime/.circleci/config.yml
Normal file
42
mmime/.circleci/config.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
# 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
|
||||
23
mmime/Cargo.toml
Normal file
23
mmime/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
[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.3.2"
|
||||
201
mmime/LICENSE-APACHE
Normal file
201
mmime/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
||||
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.
|
||||
23
mmime/LICENSE-MIT
Normal file
23
mmime/LICENSE-MIT
Normal file
@@ -0,0 +1,23 @@
|
||||
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.
|
||||
4
mmime/LICENSE.md
Normal file
4
mmime/LICENSE.md
Normal file
@@ -0,0 +1,4 @@
|
||||
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.
|
||||
16
mmime/README.md
Normal file
16
mmime/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# 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
|
||||
32
mmime/src/charconv.rs
Normal file
32
mmime/src/charconv.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
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_str().unwrap().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();
|
||||
*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
|
||||
}
|
||||
}
|
||||
427
mmime/src/chash.rs
Normal file
427
mmime/src/chash.rs
Normal file
@@ -0,0 +1,427 @@
|
||||
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;
|
||||
}
|
||||
202
mmime/src/clist.rs
Normal file
202
mmime/src/clist.rs
Normal file
@@ -0,0 +1,202 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
71
mmime/src/constants.rs
Normal file
71
mmime/src/constants.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
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;
|
||||
460
mmime/src/lib.rs
Normal file
460
mmime/src/lib.rs
Normal file
@@ -0,0 +1,460 @@
|
||||
#![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 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::mailimf::*;
|
||||
pub use self::mailmime::*;
|
||||
pub use self::mmapstring::*;
|
||||
pub use self::other::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mailimf::types::*;
|
||||
use crate::mailmime::types::*;
|
||||
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
1 => {
|
||||
println!("single part");
|
||||
}
|
||||
2 => {
|
||||
println!("multipart");
|
||||
}
|
||||
3 => 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 {
|
||||
1 => {
|
||||
display_mime_data((*mime).mm_data.mm_single);
|
||||
}
|
||||
2 => {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
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);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5921
mmime/src/mailimf/mod.rs
Normal file
5921
mmime/src/mailimf/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
1181
mmime/src/mailimf/types.rs
Normal file
1181
mmime/src/mailimf/types.rs
Normal file
File diff suppressed because it is too large
Load Diff
89
mmime/src/mailimf/types_helper.rs
Normal file
89
mmime/src/mailimf/types_helper.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
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;
|
||||
}
|
||||
1985
mmime/src/mailimf/write_generic.rs
Normal file
1985
mmime/src/mailimf/write_generic.rs
Normal file
File diff suppressed because it is too large
Load Diff
2357
mmime/src/mailmime/content.rs
Normal file
2357
mmime/src/mailmime/content.rs
Normal file
File diff suppressed because it is too large
Load Diff
860
mmime/src/mailmime/decode.rs
Normal file
860
mmime/src/mailmime/decode.rs
Normal file
@@ -0,0 +1,860 @@
|
||||
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;
|
||||
}
|
||||
583
mmime/src/mailmime/disposition.rs
Normal file
583
mmime/src/mailmime/disposition.rs
Normal file
@@ -0,0 +1,583 @@
|
||||
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;
|
||||
}
|
||||
1143
mmime/src/mailmime/mod.rs
Normal file
1143
mmime/src/mailmime/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
869
mmime/src/mailmime/types.rs
Normal file
869
mmime/src/mailmime/types.rs
Normal file
@@ -0,0 +1,869 @@
|
||||
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_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);
|
||||
}
|
||||
1445
mmime/src/mailmime/types_helper.rs
Normal file
1445
mmime/src/mailmime/types_helper.rs
Normal file
File diff suppressed because it is too large
Load Diff
1979
mmime/src/mailmime/write_generic.rs
Normal file
1979
mmime/src/mailmime/write_generic.rs
Normal file
File diff suppressed because it is too large
Load Diff
82
mmime/src/mailmime/write_mem.rs
Normal file
82
mmime/src/mailmime/write_mem.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
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);
|
||||
}
|
||||
397
mmime/src/mmapstring.rs
Normal file
397
mmime/src/mmapstring.rs
Normal file
@@ -0,0 +1,397 @@
|
||||
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() {}
|
||||
1707
mmime/src/other.rs
Normal file
1707
mmime/src/other.rs
Normal file
File diff suppressed because it is too large
Load Diff
8
proptest-regressions/dc_strencode.txt
Normal file
8
proptest-regressions/dc_strencode.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
# 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 679506fe9ac59df773f8cfa800fdab5f0a32fe49d2ab370394000a1aa5bc2a72 # shrinks to buf = "%0A"
|
||||
cc e34960438edb2426904b44fb4215154e7e2880f2fd1c3183b98bfcc76fec4882 # shrinks to input = " 0"
|
||||
9
proptest-regressions/dc_tools.txt
Normal file
9
proptest-regressions/dc_tools.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# 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 c310754465ee0261807b96fa9bcc4861ff9aa286e94667524b5960c69f9b6620 # shrinks to buf = "", approx_chars = 0, do_unwrap = false
|
||||
cc 5fd8d730b0a9cdf7308ce58818ca9aefc0255c9ba2a0878944fc48d43a67315b # shrinks to buf = "𑒀ὐ¢🜀\u{1e01b}A a🟠", approx_chars = 0, do_unwrap = false
|
||||
cc c6a0029a54137a4b9efc9ef2ea6d9a7dd1d60d1c937bb472b66a174618ba8013 # shrinks to buf = "𐠈0Aᝮa𫝀®!ꫛa¡0A𐢧00𐹠®A 丽ⷐએ ", approx_chars = 0, do_unwrap = false
|
||||
@@ -65,17 +65,17 @@ Afterwards ``which python`` tells you that it comes out of the "venv"
|
||||
directory that contains all python install artifacts. Let's first
|
||||
install test tools::
|
||||
|
||||
pip install pytest pytest-timeout requests
|
||||
pip install pytest pytest-timeout pytest-rerunfailures requests
|
||||
|
||||
then cargo-build and install the deltachat bindings::
|
||||
|
||||
python install_python_bindings.py
|
||||
|
||||
The bindings will be installed in release mode but with debug symbols.
|
||||
The release mode is neccessary because some tests generate RSA keys
|
||||
The release mode is necessary because some tests generate RSA keys
|
||||
which is prohibitively slow in debug mode.
|
||||
|
||||
After succcessul binding installation you can finally run the tests::
|
||||
After successful binding installation you can finally run the tests::
|
||||
|
||||
pytest -v tests
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ["DCC_RS_TARGET"] = target = "release"
|
||||
@@ -21,5 +21,5 @@ if __name__ == "__main__":
|
||||
subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True)
|
||||
|
||||
subprocess.check_call([
|
||||
"pip", "install", "-e", "."
|
||||
sys.executable, "-m", "pip", "install", "-e", "."
|
||||
])
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
from __future__ import print_function
|
||||
import threading
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from array import array
|
||||
@@ -14,7 +13,7 @@ except ImportError:
|
||||
import deltachat
|
||||
from . import const
|
||||
from .capi import ffi, lib
|
||||
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
|
||||
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array, DCLot
|
||||
from .chatting import Contact, Chat, Message
|
||||
|
||||
|
||||
@@ -23,7 +22,7 @@ class Account(object):
|
||||
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):
|
||||
def __init__(self, db_path, logid=None, eventlogging=True, debug=True):
|
||||
""" initialize account object.
|
||||
|
||||
:param db_path: a path to the account database. The database
|
||||
@@ -31,13 +30,14 @@ 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)
|
||||
self._evlogger = EventLogger(self._dc_context, logid, debug)
|
||||
deltachat.set_context_callback(self._dc_context, self._process_event)
|
||||
self._threads = IOThreads(self._dc_context, self._evlogger._log_event)
|
||||
else:
|
||||
@@ -48,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_completed = threading.Event()
|
||||
self._imex_events = Queue()
|
||||
|
||||
# XXX this can cause "illegal instructions" at test ends so we omit it for now
|
||||
# def __del__(self):
|
||||
# self.shutdown()
|
||||
def __del__(self):
|
||||
self.shutdown()
|
||||
|
||||
def _check_config_key(self, name):
|
||||
if name not in self._configkeys:
|
||||
@@ -232,7 +231,7 @@ class Account(object):
|
||||
:returns: a :class:`deltachat.chatting.Chat` object.
|
||||
"""
|
||||
bytes_name = name.encode("utf8")
|
||||
chat_id = lib.dc_create_group_chat(self._dc_context, verified, bytes_name)
|
||||
chat_id = lib.dc_create_group_chat(self._dc_context, int(verified), bytes_name)
|
||||
return Chat(self, chat_id)
|
||||
|
||||
def get_chats(self):
|
||||
@@ -290,31 +289,64 @@ 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_to_dir(self, backupdir):
|
||||
"""return after all delta chat state is exported to a new file in
|
||||
the specified directory.
|
||||
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.
|
||||
"""
|
||||
snap_files = os.listdir(backupdir)
|
||||
self._imex_completed.clear()
|
||||
lib.dc_imex(self._dc_context, 11, as_dc_charpointer(backupdir), ffi.NULL)
|
||||
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)
|
||||
if not self._threads.is_started():
|
||||
lib.dc_perform_imap_jobs(self._dc_context)
|
||||
self._imex_completed.wait()
|
||||
for x in os.listdir(backupdir):
|
||||
if x not in snap_files:
|
||||
return os.path.join(backupdir, x)
|
||||
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
|
||||
|
||||
def import_from_file(self, path):
|
||||
"""import delta chat state from the specified backup file.
|
||||
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).
|
||||
|
||||
The account must be in unconfigured state for import to attempted.
|
||||
"""
|
||||
assert not self.is_configured(), "cannot import into configured account"
|
||||
self._imex_completed.clear()
|
||||
lib.dc_imex(self._dc_context, 12, as_dc_charpointer(path), ffi.NULL)
|
||||
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)
|
||||
if not self._threads.is_started():
|
||||
lib.dc_perform_imap_jobs(self._dc_context)
|
||||
self._imex_completed.wait()
|
||||
if not self._imex_events.get():
|
||||
raise ValueError("import from path '{}' failed".format(path))
|
||||
|
||||
def initiate_key_transfer(self):
|
||||
"""return setup code after a Autocrypt setup message
|
||||
@@ -329,7 +361,66 @@ class Account(object):
|
||||
raise RuntimeError("could not send out autocrypt setup message")
|
||||
return from_dc_charpointer(res)
|
||||
|
||||
def start_threads(self):
|
||||
def get_setup_contact_qr(self):
|
||||
""" get/create Setup-Contact QR Code as ascii-string.
|
||||
|
||||
this string needs to be transferred to another DC account
|
||||
in a second channel (typically used by mobiles with QRcode-show + scan UX)
|
||||
where qr_setup_contact(qr) is called.
|
||||
"""
|
||||
res = lib.dc_get_securejoin_qr(self._dc_context, 0)
|
||||
return from_dc_charpointer(res)
|
||||
|
||||
def check_qr(self, qr):
|
||||
""" check qr code and return :class:`ScannedQRCode` instance representing the result"""
|
||||
res = ffi.gc(
|
||||
lib.dc_check_qr(self._dc_context, as_dc_charpointer(qr)),
|
||||
lib.dc_lot_unref
|
||||
)
|
||||
lot = DCLot(res)
|
||||
if lot.state() == const.DC_QR_ERROR:
|
||||
raise ValueError("invalid or unknown QR code: {}".format(lot.text1()))
|
||||
return ScannedQRCode(lot)
|
||||
|
||||
def qr_setup_contact(self, qr):
|
||||
""" 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.chatting.Chat` instance
|
||||
is returned.
|
||||
:param qr: valid "setup contact" QR code (all other QR codes will result in an exception)
|
||||
"""
|
||||
assert self.check_qr(qr).is_ask_verifycontact()
|
||||
chat_id = lib.dc_join_securejoin(self._dc_context, as_dc_charpointer(qr))
|
||||
if chat_id == 0:
|
||||
raise ValueError("could not setup secure contact")
|
||||
return Chat(self, chat_id)
|
||||
|
||||
def qr_join_chat(self, qr):
|
||||
""" 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.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)
|
||||
"""
|
||||
assert self.check_qr(qr).is_ask_verifygroup()
|
||||
chat_id = lib.dc_join_securejoin(self._dc_context, as_dc_charpointer(qr))
|
||||
if chat_id == 0:
|
||||
raise ValueError("could not join group")
|
||||
return Chat(self, chat_id)
|
||||
|
||||
#
|
||||
# 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):
|
||||
""" start IMAP/SMTP threads (and configure account if it hasn't happened).
|
||||
|
||||
:raises: ValueError if 'addr' or 'mail_pw' are not configured.
|
||||
@@ -337,7 +428,7 @@ class Account(object):
|
||||
"""
|
||||
if not self.is_configured():
|
||||
self.configure()
|
||||
self._threads.start()
|
||||
self._threads.start(mvbox=mvbox, sentbox=sentbox)
|
||||
|
||||
def stop_threads(self, wait=True):
|
||||
""" stop IMAP/SMTP threads. """
|
||||
@@ -347,7 +438,8 @@ class Account(object):
|
||||
def shutdown(self, wait=True):
|
||||
""" stop threads and close and remove underlying dc_context and callbacks. """
|
||||
if hasattr(self, "_dc_context") and hasattr(self, "_threads"):
|
||||
self.stop_threads(wait=False) # to interrupt idle and tell python threads to stop
|
||||
# print("SHUTDOWN", self)
|
||||
self.stop_threads(wait=False)
|
||||
lib.dc_close(self._dc_context)
|
||||
self.stop_threads(wait=wait) # to wait for threads
|
||||
deltachat.clear_context_callback(self._dc_context)
|
||||
@@ -364,7 +456,12 @@ class Account(object):
|
||||
|
||||
def on_dc_event_imex_progress(self, data1, data2):
|
||||
if data1 == 1000:
|
||||
self._imex_completed.set()
|
||||
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)
|
||||
|
||||
|
||||
class IOThreads:
|
||||
@@ -377,10 +474,14 @@ class IOThreads:
|
||||
def is_started(self):
|
||||
return len(self._name2thread) > 0
|
||||
|
||||
def start(self, imap=True, smtp=True):
|
||||
def start(self, imap=True, smtp=True, mvbox=False, sentbox=False):
|
||||
assert not self.is_started()
|
||||
if imap:
|
||||
self._start_one_thread("imap", self.imap_thread_run)
|
||||
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)
|
||||
if smtp:
|
||||
self._start_one_thread("smtp", self.smtp_thread_run)
|
||||
|
||||
@@ -393,17 +494,35 @@ 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, "IMAP THREAD START")
|
||||
self._log_event("py-bindings-info", 0, "INBOX THREAD START")
|
||||
while not self._thread_quitflag:
|
||||
lib.dc_perform_imap_jobs(self._dc_context)
|
||||
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")
|
||||
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")
|
||||
|
||||
def smtp_thread_run(self):
|
||||
self._log_event("py-bindings-info", 0, "SMTP THREAD START")
|
||||
@@ -433,6 +552,10 @@ class EventLogger:
|
||||
def set_timeout(self, timeout):
|
||||
self._timeout = timeout
|
||||
|
||||
def consume_events(self, check_error=True):
|
||||
while not self._event_queue.empty():
|
||||
self.get()
|
||||
|
||||
def get(self, timeout=None, check_error=True):
|
||||
timeout = timeout or self._timeout
|
||||
ev = self._event_queue.get(timeout=timeout)
|
||||
@@ -492,3 +615,18 @@ def _destroy_dc_context(dc_context, dc_context_unref=lib.dc_context_unref):
|
||||
# we are deep into Python Interpreter shutdown,
|
||||
# so no need to clear the callback context mapping.
|
||||
pass
|
||||
|
||||
|
||||
class ScannedQRCode:
|
||||
def __init__(self, dc_lot):
|
||||
self._dc_lot = dc_lot
|
||||
|
||||
def is_ask_verifycontact(self):
|
||||
return self._dc_lot.state() == const.DC_QR_ASK_VERIFYCONTACT
|
||||
|
||||
def is_ask_verifygroup(self):
|
||||
return self._dc_lot.state() == const.DC_QR_ASK_VERIFYGROUP
|
||||
|
||||
@property
|
||||
def contact_id(self):
|
||||
return self._dc_lot.id()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
""" chatting related objects: Contact, Chat, Message. """
|
||||
|
||||
import mimetypes
|
||||
import os
|
||||
from . import props
|
||||
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
|
||||
from .capi import lib, ffi
|
||||
@@ -108,6 +109,13 @@ 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.
|
||||
|
||||
@@ -131,6 +139,16 @@ class Chat(object):
|
||||
"""
|
||||
return lib.dc_chat_get_type(self._dc_chat)
|
||||
|
||||
def get_join_qr(self):
|
||||
""" get/create Join-Group QR Code as ascii-string.
|
||||
|
||||
this string needs to be transferred to another DC account
|
||||
in a second channel (typically used by mobiles with QRcode-show + scan UX)
|
||||
where account.join_with_qrcode(qr) needs to be called.
|
||||
"""
|
||||
res = lib.dc_get_securejoin_qr(self._dc_context, self.id)
|
||||
return from_dc_charpointer(res)
|
||||
|
||||
# ------ chat messaging API ------------------------------
|
||||
|
||||
def send_text(self, text):
|
||||
@@ -294,7 +312,6 @@ class Chat(object):
|
||||
def get_contacts(self):
|
||||
""" get all contacts for this chat.
|
||||
:params: contact object.
|
||||
:raises ValueError: if contact could not be added
|
||||
:returns: list of :class:`deltachat.chatting.Contact` objects for this chat
|
||||
|
||||
"""
|
||||
@@ -305,3 +322,46 @@ class Chat(object):
|
||||
return list(iter_array(
|
||||
dc_array, lambda id: Contact(self._dc_context, id))
|
||||
)
|
||||
|
||||
def set_profile_image(self, img_path):
|
||||
"""Set group profile image.
|
||||
|
||||
If the group is already promoted (any message was sent to the group),
|
||||
all group members are informed by a special status message that is sent
|
||||
automatically by this function.
|
||||
:params img_path: path to image object
|
||||
:raises ValueError: if profile image could not be set
|
||||
:returns: None
|
||||
"""
|
||||
assert os.path.exists(img_path), img_path
|
||||
p = as_dc_charpointer(img_path)
|
||||
res = lib.dc_set_chat_profile_image(self._dc_context, self.id, p)
|
||||
if res != 1:
|
||||
raise ValueError("Setting Profile Image {!r} failed".format(p))
|
||||
|
||||
def remove_profile_image(self):
|
||||
"""remove group profile image.
|
||||
|
||||
If the group is already promoted (any message was sent to the group),
|
||||
all group members are informed by a special status message that is sent
|
||||
automatically by this function.
|
||||
:raises ValueError: if profile image could not be reset
|
||||
:returns: None
|
||||
"""
|
||||
res = lib.dc_set_chat_profile_image(self._dc_context, self.id, ffi.NULL)
|
||||
if res != 1:
|
||||
raise ValueError("Removing Profile Image failed")
|
||||
|
||||
def get_profile_image(self):
|
||||
"""Get group profile image.
|
||||
|
||||
For groups, this is the image set by any group member using
|
||||
set_chat_profile_image(). For normal chats, this is the image
|
||||
set by each remote user on their own using dc_set_config(context,
|
||||
"selfavatar", image).
|
||||
:returns: path to profile image, None if no profile image exists.
|
||||
"""
|
||||
dc_res = lib.dc_chat_get_profile_image(self._dc_chat)
|
||||
if dc_res == ffi.NULL:
|
||||
return None
|
||||
return from_dc_charpointer(dc_res)
|
||||
|
||||
@@ -8,11 +8,23 @@ 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
|
||||
DC_GCL_VERIFIED_ONLY = 0x01
|
||||
DC_GCL_ADD_SELF = 0x02
|
||||
DC_QR_ASK_VERIFYCONTACT = 200
|
||||
DC_QR_ASK_VERIFYGROUP = 202
|
||||
DC_QR_FPR_OK = 210
|
||||
DC_QR_FPR_MISMATCH = 220
|
||||
DC_QR_FPR_WITHOUT_ADDR = 230
|
||||
DC_QR_ADDR = 320
|
||||
DC_QR_TEXT = 330
|
||||
DC_QR_URL = 332
|
||||
DC_QR_ERROR = 400
|
||||
DC_CHAT_ID_DEADDROP = 1
|
||||
DC_CHAT_ID_TRASH = 3
|
||||
DC_CHAT_ID_MSGS_IN_CREATION = 4
|
||||
@@ -69,15 +81,14 @@ DC_EVENT_IMEX_FILE_WRITTEN = 2052
|
||||
DC_EVENT_SECUREJOIN_INVITER_PROGRESS = 2060
|
||||
DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061
|
||||
DC_EVENT_GET_STRING = 2091
|
||||
DC_EVENT_HTTP_GET = 2100
|
||||
DC_EVENT_HTTP_POST = 2110
|
||||
DC_EVENT_FILE_COPIED = 2055
|
||||
DC_EVENT_IS_OFFLINE = 2081
|
||||
# end const generated
|
||||
|
||||
|
||||
def read_event_defines(f):
|
||||
rex = re.compile(r'#define\s+((?:DC_EVENT_|DC_MSG|DC_STATE_|DC_CONTACT_ID_|DC_GCL|DC_CHAT)\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:
|
||||
@@ -90,7 +101,7 @@ if __name__ == "__main__":
|
||||
if len(sys.argv) >= 2:
|
||||
deltah = sys.argv[1]
|
||||
else:
|
||||
deltah = joinpath(dirname(dirname(dirname(here_dir))), "src", "deltachat.h")
|
||||
deltah = joinpath(dirname(dirname(dirname(here_dir))), "deltachat-ffi", "deltachat.h")
|
||||
assert os.path.exists(deltah)
|
||||
|
||||
lines = []
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from .capi import lib
|
||||
from .capi import ffi
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def as_dc_charpointer(obj):
|
||||
@@ -17,3 +18,29 @@ def iter_array(dc_array_t, constructor):
|
||||
|
||||
def from_dc_charpointer(obj):
|
||||
return ffi.string(ffi.gc(obj, lib.dc_str_unref)).decode("utf8")
|
||||
|
||||
|
||||
class DCLot:
|
||||
def __init__(self, dc_lot):
|
||||
self._dc_lot = dc_lot
|
||||
|
||||
def id(self):
|
||||
return lib.dc_lot_get_id(self._dc_lot)
|
||||
|
||||
def state(self):
|
||||
return lib.dc_lot_get_state(self._dc_lot)
|
||||
|
||||
def text1(self):
|
||||
return from_dc_charpointer(lib.dc_lot_get_text1(self._dc_lot))
|
||||
|
||||
def text1_meaning(self):
|
||||
return lib.dc_lot_get_text1_meaning(self._dc_lot)
|
||||
|
||||
def text2(self):
|
||||
return from_dc_charpointer(lib.dc_lot_get_text2(self._dc_lot))
|
||||
|
||||
def timestamp(self):
|
||||
ts = lib.dc_lot_get_timestamp(self._dc_lot)
|
||||
if ts == 0:
|
||||
return None
|
||||
return datetime.utcfromtimestamp(ts)
|
||||
|
||||
@@ -101,6 +101,14 @@ 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 get_message_info(self):
|
||||
""" Return informational text for a single message.
|
||||
|
||||
@@ -110,7 +118,13 @@ class Message(object):
|
||||
|
||||
def continue_key_transfer(self, setup_code):
|
||||
""" extract key and use it as primary key for this account. """
|
||||
lib.dc_continue_key_transfer(self._dc_context, self.id, as_dc_charpointer(setup_code))
|
||||
res = lib.dc_continue_key_transfer(
|
||||
self._dc_context,
|
||||
self.id,
|
||||
as_dc_charpointer(setup_code)
|
||||
)
|
||||
if res == 0:
|
||||
raise ValueError("could not decrypt")
|
||||
|
||||
@props.with_doc
|
||||
def time_sent(self):
|
||||
|
||||
67
python/src/deltachat/provider.py
Normal file
67
python/src/deltachat/provider.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""Provider info class."""
|
||||
|
||||
from .capi import ffi, lib
|
||||
from .cutil import as_dc_charpointer, from_dc_charpointer
|
||||
|
||||
|
||||
class ProviderNotFoundError(Exception):
|
||||
"""The provider information was not found."""
|
||||
|
||||
|
||||
class Provider(object):
|
||||
"""Provider information.
|
||||
|
||||
:param domain: The domain to get the provider info for, this is
|
||||
normally the part following the `@` of the domain.
|
||||
"""
|
||||
|
||||
def __init__(self, domain):
|
||||
provider = ffi.gc(
|
||||
lib.dc_provider_new_from_domain(as_dc_charpointer(domain)),
|
||||
lib.dc_provider_unref,
|
||||
)
|
||||
if provider == ffi.NULL:
|
||||
raise ProviderNotFoundError("Provider not found")
|
||||
self._provider = provider
|
||||
|
||||
@classmethod
|
||||
def from_email(cls, email):
|
||||
"""Create provider info from an email address.
|
||||
|
||||
:param email: Email address to get provider info for.
|
||||
"""
|
||||
return cls(email.split('@')[-1])
|
||||
|
||||
@property
|
||||
def overview_page(self):
|
||||
"""URL to the overview page of the provider on providers.delta.chat."""
|
||||
return from_dc_charpointer(
|
||||
lib.dc_provider_get_overview_page(self._provider))
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""The name of the provider."""
|
||||
return from_dc_charpointer(lib.dc_provider_get_name(self._provider))
|
||||
|
||||
@property
|
||||
def markdown(self):
|
||||
"""Content of the information page, formatted as markdown."""
|
||||
return from_dc_charpointer(
|
||||
lib.dc_provider_get_markdown(self._provider))
|
||||
|
||||
@property
|
||||
def status_date(self):
|
||||
"""The date the provider info was last updated, as a string."""
|
||||
return from_dc_charpointer(
|
||||
lib.dc_provider_get_status_date(self._provider))
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
"""The status of the provider information.
|
||||
|
||||
This is one of the
|
||||
:attr:`deltachat.const.DC_PROVIDER_STATUS_OK`,
|
||||
:attr:`deltachat.const.DC_PROVIDER_STATUS_PREPARATION` or
|
||||
:attr:`deltachat.const.DC_PROVIDER_STATUS_BROKEN` constants.
|
||||
"""
|
||||
return lib.dc_provider_get_status(self._provider)
|
||||
@@ -24,17 +24,6 @@ def pytest_configure(config):
|
||||
config.option.liveconfig = cfg
|
||||
|
||||
|
||||
@pytest.hookimpl(trylast=True)
|
||||
def pytest_runtest_call(item):
|
||||
# perform early finalization because we otherwise get cloberred
|
||||
# output from concurrent threads printing between execution
|
||||
# of the test function and the teardown phase of that test function
|
||||
if "acfactory" in item.funcargs:
|
||||
print("*"*30, "finalizing", "*"*30)
|
||||
acfactory = item.funcargs["acfactory"]
|
||||
acfactory.finalize()
|
||||
|
||||
|
||||
def pytest_report_header(config, startdir):
|
||||
summary = []
|
||||
|
||||
@@ -136,13 +125,17 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
|
||||
fin = self._finalizers.pop()
|
||||
fin()
|
||||
|
||||
def make_account(self, path, logid):
|
||||
ac = Account(path, logid=logid)
|
||||
self._finalizers.append(ac.shutdown)
|
||||
return ac
|
||||
|
||||
def get_unconfigured_account(self):
|
||||
self.offline_count += 1
|
||||
tmpdb = tmpdir.join("offlinedb%d" % self.offline_count)
|
||||
ac = Account(tmpdb.strpath, logid="ac{}".format(self.offline_count))
|
||||
ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.offline_count))
|
||||
ac._evlogger.init_time = self.init_time
|
||||
ac._evlogger.set_timeout(2)
|
||||
self._finalizers.append(ac.shutdown)
|
||||
return ac
|
||||
|
||||
def get_configured_offline_account(self):
|
||||
@@ -157,7 +150,7 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
|
||||
lib.dc_set_config(ac._dc_context, b"configured", b"1")
|
||||
return ac
|
||||
|
||||
def get_online_configuring_account(self):
|
||||
def get_online_config(self):
|
||||
if not session_liveconfig:
|
||||
pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")
|
||||
configdict = session_liveconfig.get(self.live_count)
|
||||
@@ -165,26 +158,39 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
|
||||
if "e2ee_enabled" not in configdict:
|
||||
configdict["e2ee_enabled"] = "1"
|
||||
tmpdb = tmpdir.join("livedb%d" % self.live_count)
|
||||
ac = Account(tmpdb.strpath, logid="ac{}".format(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()
|
||||
self._finalizers.append(ac.shutdown)
|
||||
ac.start_threads(mvbox=mvbox, sentbox=sentbox)
|
||||
return ac
|
||||
|
||||
def get_two_online_accounts(self):
|
||||
ac1 = self.get_online_configuring_account()
|
||||
ac2 = self.get_online_configuring_account()
|
||||
wait_successful_IMAP_SMTP_connection(ac1)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
wait_successful_IMAP_SMTP_connection(ac2)
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
return ac1, ac2
|
||||
|
||||
def clone_online_account(self, account):
|
||||
self.live_count += 1
|
||||
tmpdb = tmpdir.join("livedb%d" % self.live_count)
|
||||
ac = Account(tmpdb.strpath, logid="ac{}".format(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)
|
||||
ac.configure(addr=account.get_config("addr"), mail_pw=account.get_config("mail_pw"))
|
||||
ac.start_threads()
|
||||
self._finalizers.append(ac.shutdown)
|
||||
return ac
|
||||
|
||||
return AccountMaker()
|
||||
am = AccountMaker()
|
||||
request.addfinalizer(am.finalize)
|
||||
return am
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -204,12 +210,22 @@ def lp():
|
||||
return Printer()
|
||||
|
||||
|
||||
def wait_configuration_progress(account, target):
|
||||
def wait_configuration_progress(account, min_target, max_target=1001):
|
||||
min_target = min(min_target, max_target)
|
||||
while 1:
|
||||
evt_name, data1, data2 = \
|
||||
account._evlogger.get_matching("DC_EVENT_CONFIGURE_PROGRESS")
|
||||
if data1 >= target:
|
||||
print("** CONFIG PROGRESS {}".format(target), account)
|
||||
if data1 >= min_target and data1 <= max_target:
|
||||
print("** CONFIG PROGRESS {}".format(min_target), account)
|
||||
break
|
||||
|
||||
|
||||
def wait_securejoin_inviter_progress(account, target):
|
||||
while 1:
|
||||
evt_name, data1, data2 = \
|
||||
account._evlogger.get_matching("DC_EVENT_SECUREJOIN_INVITER_PROGRESS")
|
||||
if data2 >= target:
|
||||
print("** SECUREJOINT-INVITER PROGRESS {}".format(target), account)
|
||||
break
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import os
|
||||
from deltachat import const, Account
|
||||
from deltachat.message import Message
|
||||
from datetime import datetime, timedelta
|
||||
from conftest import wait_configuration_progress, wait_successful_IMAP_SMTP_connection
|
||||
from conftest import wait_configuration_progress, wait_successful_IMAP_SMTP_connection, wait_securejoin_inviter_progress
|
||||
|
||||
|
||||
class TestOfflineAccountBasic:
|
||||
@@ -93,8 +93,9 @@ 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)
|
||||
chat.send_text("one messae")
|
||||
msg = chat.send_text("one messae")
|
||||
assert not ac1.delete_contact(contact1)
|
||||
assert not msg.filemime
|
||||
|
||||
|
||||
class TestOfflineChat:
|
||||
@@ -106,7 +107,7 @@ 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):
|
||||
@@ -140,6 +141,22 @@ class TestOfflineChat:
|
||||
chat.set_name("title2")
|
||||
assert chat.get_name() == "title2"
|
||||
|
||||
@pytest.mark.parametrize("verified", [True, False])
|
||||
def test_group_chat_qr(self, acfactory, ac1, verified):
|
||||
ac2 = acfactory.get_configured_offline_account()
|
||||
chat = ac1.create_group_chat(name="title1", verified=verified)
|
||||
qr = chat.get_join_qr()
|
||||
assert ac2.check_qr(qr).is_ask_verifygroup
|
||||
|
||||
def test_get_set_profile_image_simple(self, ac1, data):
|
||||
chat = ac1.create_group_chat(name="title1")
|
||||
p = data.get_path("d.png")
|
||||
chat.set_profile_image(p)
|
||||
p2 = chat.get_profile_image()
|
||||
assert open(p, "rb").read() == open(p2, "rb").read()
|
||||
chat.remove_profile_image()
|
||||
assert chat.get_profile_image() is None
|
||||
|
||||
def test_delete_and_send_fails(self, ac1, chat1):
|
||||
chat1.delete()
|
||||
ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||
@@ -277,10 +294,10 @@ class TestOfflineChat:
|
||||
assert contact == ac1.get_self_contact()
|
||||
assert not backupdir.listdir()
|
||||
|
||||
path = ac1.export_to_dir(backupdir.strpath)
|
||||
path = ac1.export_all(backupdir.strpath)
|
||||
assert os.path.exists(path)
|
||||
ac2 = acfactory.get_unconfigured_account()
|
||||
ac2.import_from_file(path)
|
||||
ac2.import_all(path)
|
||||
contacts = ac2.get_contacts(query="some1")
|
||||
assert len(contacts) == 1
|
||||
contact2 = contacts[0]
|
||||
@@ -306,18 +323,39 @@ class TestOfflineChat:
|
||||
chat1.set_draft(None)
|
||||
assert chat1.get_draft() is None
|
||||
|
||||
def test_qr_setup_contact(self, acfactory, lp):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
ac2 = acfactory.get_configured_offline_account()
|
||||
qr = ac1.get_setup_contact_qr()
|
||||
assert qr.startswith("OPENPGP4FPR:")
|
||||
res = ac2.check_qr(qr)
|
||||
assert res.is_ask_verifycontact()
|
||||
assert not res.is_ask_verifygroup()
|
||||
assert res.contact_id == 10
|
||||
|
||||
|
||||
class TestOnlineAccount:
|
||||
def test_one_account_init(self, acfactory):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
wait_successful_IMAP_SMTP_connection(ac1)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
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
|
||||
return chat
|
||||
|
||||
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(self, acfactory):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
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)
|
||||
|
||||
@@ -326,35 +364,19 @@ class TestOnlineAccount:
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||
assert ev[1] == msg_out.id
|
||||
|
||||
def test_two_accounts_send_receive(self, acfactory):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
def test_mvbox_sentbox_threads(self, acfactory):
|
||||
ac1 = acfactory.get_online_configuring_account(mvbox=True, sentbox=True)
|
||||
ac2 = acfactory.get_online_configuring_account()
|
||||
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
|
||||
wait_successful_IMAP_SMTP_connection(ac1)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
wait_successful_IMAP_SMTP_connection(ac2)
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
|
||||
msg_out = chat.send_text("message1")
|
||||
|
||||
# wait for other account to receive
|
||||
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] == msg_out.id
|
||||
msg_in = ac2.get_message_by_id(msg_out.id)
|
||||
assert msg_in.text == "message1"
|
||||
assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL
|
||||
|
||||
def test_forward_messages(self, acfactory):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
ac2 = acfactory.get_online_configuring_account()
|
||||
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
|
||||
wait_successful_IMAP_SMTP_connection(ac1)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
wait_successful_IMAP_SMTP_connection(ac2)
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||
chat = self.get_chat(ac1, ac2)
|
||||
|
||||
msg_out = chat.send_text("message2")
|
||||
|
||||
@@ -377,16 +399,11 @@ class TestOnlineAccount:
|
||||
ac2.delete_messages(messages)
|
||||
assert not chat3.get_messages()
|
||||
|
||||
def test_send_and_receive_message(self, acfactory, lp):
|
||||
lp.sec("starting accounts, waiting for configuration")
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
ac2 = acfactory.get_online_configuring_account()
|
||||
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
|
||||
def test_send_and_receive_message_markseen(self, acfactory, lp):
|
||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
lp.sec("ac1: create chat with ac2")
|
||||
chat = self.get_chat(ac1, ac2)
|
||||
|
||||
lp.sec("sending text message from ac1 to ac2")
|
||||
msg_out = chat.send_text("message1")
|
||||
@@ -422,24 +439,20 @@ 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()
|
||||
|
||||
def test_send_and_receive_will_encrypt_decrypt(self, acfactory, lp):
|
||||
lp.sec("starting accounts, waiting for configuration")
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
ac2 = acfactory.get_online_configuring_account()
|
||||
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
|
||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
lp.sec("ac1: create chat with ac2")
|
||||
chat = self.get_chat(ac1, ac2)
|
||||
|
||||
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")
|
||||
@@ -457,16 +470,15 @@ 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()
|
||||
|
||||
def test_saved_mime_on_received_message(self, acfactory, lp):
|
||||
lp.sec("starting accounts, waiting for configuration")
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
ac2 = acfactory.get_online_configuring_account()
|
||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||
|
||||
lp.sec("configure ac2 to save mime headers, create ac1/ac2 chat")
|
||||
ac2.set_config("save_mime_headers", "1")
|
||||
c2 = ac1.create_contact(email=ac2.get_config("addr"))
|
||||
chat = ac1.create_chat_by_contact(c2)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
chat = self.get_chat(ac1, ac2)
|
||||
|
||||
lp.sec("sending text message from ac1 to ac2")
|
||||
msg_out = chat.send_text("message1")
|
||||
ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED")
|
||||
@@ -480,14 +492,8 @@ class TestOnlineAccount:
|
||||
assert mime.get_all("Received")
|
||||
|
||||
def test_send_and_receive_image(self, acfactory, lp, data):
|
||||
lp.sec("starting accounts, waiting for configuration")
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
ac2 = acfactory.get_online_configuring_account()
|
||||
c2 = ac1.create_contact(email=ac2.get_config("addr"))
|
||||
chat = ac1.create_chat_by_contact(c2)
|
||||
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||
chat = self.get_chat(ac1, ac2)
|
||||
|
||||
lp.sec("sending image message from ac1 to ac2")
|
||||
path = data.get_path("d.png")
|
||||
@@ -506,19 +512,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(self, acfactory, tmpdir):
|
||||
backupdir = tmpdir.mkdir("backup")
|
||||
def test_import_export_online_all(self, acfactory, tmpdir):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
|
||||
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||
chat = ac1.create_chat_by_contact(contact1)
|
||||
chat.send_text("msg1")
|
||||
path = ac1.export_to_dir(backupdir.strpath)
|
||||
backupdir = tmpdir.mkdir("backup")
|
||||
path = ac1.export_all(backupdir.strpath)
|
||||
assert os.path.exists(path)
|
||||
|
||||
ac2 = acfactory.get_unconfigured_account()
|
||||
ac2.import_from_file(path)
|
||||
ac2.import_all(path)
|
||||
contacts = ac2.get_contacts(query="some1")
|
||||
assert len(contacts) == 1
|
||||
contact2 = contacts[0]
|
||||
@@ -528,7 +534,7 @@ class TestOnlineAccount:
|
||||
assert len(messages) == 1
|
||||
assert messages[0].text == "msg1"
|
||||
|
||||
def test_ac_setup_message(self, acfactory):
|
||||
def test_ac_setup_message(self, acfactory, lp):
|
||||
# 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
|
||||
@@ -536,13 +542,152 @@ 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")
|
||||
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_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")
|
||||
qr = ac1.get_setup_contact_qr()
|
||||
lp.sec("ac2: start QR-code based setup contact protocol")
|
||||
ch = ac2.qr_setup_contact(qr)
|
||||
assert ch.id >= 10
|
||||
wait_securejoin_inviter_progress(ac1, 1000)
|
||||
|
||||
def test_qr_join_chat(self, acfactory, lp):
|
||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||
lp.sec("ac1: create QR code and let ac2 scan it, starting the securejoin")
|
||||
chat = ac1.create_group_chat("hello")
|
||||
qr = chat.get_join_qr()
|
||||
lp.sec("ac2: start QR-code based join-group protocol")
|
||||
ch = ac2.qr_join_chat(qr)
|
||||
assert ch.id >= 10
|
||||
wait_securejoin_inviter_progress(ac1, 1000)
|
||||
|
||||
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()
|
||||
|
||||
lp.sec("create unpromoted group chat")
|
||||
chat = ac1.create_group_chat("hello")
|
||||
p = data.get_path("d.png")
|
||||
|
||||
lp.sec("ac1: set profile image on unpromoted chat")
|
||||
chat.set_profile_image(p)
|
||||
ac1._evlogger.get_matching("DC_EVENT_CHAT_MODIFIED")
|
||||
assert not chat.is_promoted()
|
||||
|
||||
lp.sec("ac1: send text to promote chat (XXX without contact added)")
|
||||
# XXX first promote the chat before adding contact
|
||||
# because DC does not send out profile images for unpromoted chats
|
||||
# otherwise
|
||||
chat.send_text("ac1: initial message to promote chat (workaround)")
|
||||
assert chat.is_promoted()
|
||||
|
||||
lp.sec("ac2: add ac1 to a chat so the message does not land in DEADDROP")
|
||||
c1 = ac2.create_contact(email=ac1.get_config("addr"))
|
||||
ac2.create_chat_by_contact(c1)
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||
|
||||
lp.sec("ac1: add ac2 to promoted group chat")
|
||||
c2 = ac1.create_contact(email=ac2.get_config("addr"))
|
||||
chat.add_contact(c2)
|
||||
|
||||
lp.sec("ac1: send a first message to ac2")
|
||||
chat.send_text("hi")
|
||||
assert chat.is_promoted()
|
||||
|
||||
lp.sec("ac2: wait for receiving message from ac1")
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
|
||||
msg_in = ac2.get_message_by_id(ev[2])
|
||||
assert not msg_in.chat.is_deaddrop()
|
||||
|
||||
lp.sec("ac2: create chat and read profile image")
|
||||
chat2 = ac2.create_chat_by_message(msg_in)
|
||||
p2 = chat2.get_profile_image()
|
||||
assert p2 is not None
|
||||
assert open(p2, "rb").read() == open(p, "rb").read()
|
||||
|
||||
ac2._evlogger.consume_events()
|
||||
ac1._evlogger.consume_events()
|
||||
lp.sec("ac2: delete profile image from chat")
|
||||
chat2.remove_profile_image()
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
|
||||
assert ev[1] == chat.id
|
||||
chat1b = ac1.create_chat_by_message(ev[2])
|
||||
assert chat1b.get_profile_image() is None
|
||||
assert chat.get_profile_image() is None
|
||||
|
||||
|
||||
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 "authentication failed" 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 "authentication failed" 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)
|
||||
|
||||
@@ -4,7 +4,7 @@ from deltachat import const
|
||||
from conftest import wait_configuration_progress, wait_msgs_changed
|
||||
|
||||
|
||||
class TestInCreation:
|
||||
class TestOnlineInCreation:
|
||||
def test_forward_increation(self, acfactory, data, lp):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
ac2 = acfactory.get_online_configuring_account()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from __future__ import print_function
|
||||
from deltachat import capi, const, set_context_callback, clear_context_callback
|
||||
from deltachat import capi, cutil, const, set_context_callback, clear_context_callback
|
||||
from deltachat.capi import ffi
|
||||
from deltachat.capi import lib
|
||||
from deltachat.account import EventLogger
|
||||
@@ -17,11 +17,19 @@ def test_callback_None2int():
|
||||
clear_context_callback(ctx)
|
||||
|
||||
|
||||
def test_dc_close_events():
|
||||
ctx = capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL)
|
||||
def test_dc_close_events(tmpdir):
|
||||
ctx = ffi.gc(
|
||||
capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_unref,
|
||||
)
|
||||
evlog = EventLogger(ctx)
|
||||
evlog.set_timeout(5)
|
||||
set_context_callback(ctx, lambda ctx, evt_name, data1, data2: evlog(evt_name, data1, data2))
|
||||
set_context_callback(
|
||||
ctx,
|
||||
lambda ctx, evt_name, data1, data2: evlog(evt_name, data1, data2)
|
||||
)
|
||||
p = tmpdir.join("hello.db")
|
||||
lib.dc_open(ctx, p.strpath.encode("ascii"), ffi.NULL)
|
||||
capi.lib.dc_close(ctx)
|
||||
|
||||
def find(info_string):
|
||||
@@ -51,6 +59,16 @@ 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
|
||||
@@ -75,3 +93,58 @@ def test_markseen_invalid_message_ids(acfactory):
|
||||
msg_ids = [9]
|
||||
lib.dc_markseen_msgs(ac1._dc_context, msg_ids, len(msg_ids))
|
||||
ac1._evlogger.ensure_event_not_queued("DC_EVENT_WARNING|DC_EVENT_ERROR")
|
||||
|
||||
|
||||
def test_provider_info():
|
||||
provider = lib.dc_provider_new_from_email(cutil.as_dc_charpointer("ex@example.com"))
|
||||
assert cutil.from_dc_charpointer(
|
||||
lib.dc_provider_get_overview_page(provider)
|
||||
) == "https://providers.delta.chat/example.com"
|
||||
assert cutil.from_dc_charpointer(lib.dc_provider_get_name(provider)) == "Example"
|
||||
assert cutil.from_dc_charpointer(lib.dc_provider_get_markdown(provider)) == "\n..."
|
||||
assert cutil.from_dc_charpointer(lib.dc_provider_get_status_date(provider)) == "2018-09"
|
||||
assert lib.dc_provider_get_status(provider) == const.DC_PROVIDER_STATUS_PREPARATION
|
||||
|
||||
|
||||
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
|
||||
|
||||
27
python/tests/test_provider_info.py
Normal file
27
python/tests/test_provider_info.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import pytest
|
||||
|
||||
from deltachat import const
|
||||
from deltachat import provider
|
||||
|
||||
|
||||
def test_provider_info_from_email():
|
||||
example = provider.Provider.from_email("email@example.com")
|
||||
assert example.overview_page == "https://providers.delta.chat/example.com"
|
||||
assert example.name == "Example"
|
||||
assert example.markdown == "\n..."
|
||||
assert example.status_date == "2018-09"
|
||||
assert example.status == const.DC_PROVIDER_STATUS_PREPARATION
|
||||
|
||||
|
||||
def test_provider_info_from_domain():
|
||||
example = provider.Provider("example.com")
|
||||
assert example.overview_page == "https://providers.delta.chat/example.com"
|
||||
assert example.name == "Example"
|
||||
assert example.markdown == "\n..."
|
||||
assert example.status_date == "2018-09"
|
||||
assert example.status == const.DC_PROVIDER_STATUS_PREPARATION
|
||||
|
||||
|
||||
def test_provider_info_none():
|
||||
with pytest.raises(provider.ProviderNotFoundError):
|
||||
provider.Provider.from_email("email@unexistent.no")
|
||||
@@ -1,7 +1,6 @@
|
||||
[tox]
|
||||
# make sure to update environment list in travis.yml and appveyor.yml
|
||||
envlist =
|
||||
py27
|
||||
py35
|
||||
lint
|
||||
auditwheels
|
||||
@@ -17,12 +16,15 @@ passenv =
|
||||
DCC_PY_LIVECONFIG
|
||||
deps =
|
||||
pytest
|
||||
pytest-faulthandler
|
||||
pytest-rerunfailures
|
||||
pytest-timeout
|
||||
pytest-xdist
|
||||
pdbpp
|
||||
requests
|
||||
|
||||
[testenv:auditwheels]
|
||||
skipsdist = True
|
||||
deps = auditwheel
|
||||
commands =
|
||||
python tests/auditwheels.py {toxworkdir}/wheelhouse
|
||||
|
||||
@@ -43,7 +45,7 @@ commands =
|
||||
[testenv:doc]
|
||||
basepython = python3.5
|
||||
deps =
|
||||
sphinx==2.0.1
|
||||
sphinx==2.2.0
|
||||
breathe
|
||||
|
||||
changedir = doc
|
||||
@@ -52,11 +54,12 @@ commands =
|
||||
|
||||
|
||||
[pytest]
|
||||
addopts = -v -rs
|
||||
addopts = -v -rs --reruns 3 --reruns-delay 2
|
||||
python_files = tests/test_*.py
|
||||
norecursedirs = .tox
|
||||
xfail_strict=true
|
||||
timeout = 60
|
||||
timeout = 60
|
||||
timeout_method = thread
|
||||
|
||||
[flake8]
|
||||
max-line-length = 120
|
||||
|
||||
373
spec.md
Normal file
373
spec.md
Normal file
@@ -0,0 +1,373 @@
|
||||
# Chat-over-Email specification
|
||||
|
||||
Version 0.19.0
|
||||
|
||||
This document describes how emails can be used
|
||||
to implement typical messenger functions
|
||||
while staying compatible to existing MUAs.
|
||||
|
||||
- [Encryption](#encryption)
|
||||
- [Outgoing messages](#outgoing-messages)
|
||||
- [Incoming messages](#incoming-messages)
|
||||
- [Forwarded messages](#forwarded-messages)
|
||||
- [Groups](#groups)
|
||||
- [Outgoing group messages](#outgoing-group-messages)
|
||||
- [Incoming group messages](#incoming-group-messages)
|
||||
- [Add and remove members](#add-and-remove-members)
|
||||
- [Change group name](#change-group-name)
|
||||
- [Set group image](#set-group-image)
|
||||
- [Set profile image](#set-profile-image)
|
||||
- [Miscellaneous](#miscellaneous)
|
||||
|
||||
|
||||
# Encryption
|
||||
|
||||
Messages SHOULD be encrypted by the
|
||||
[Autocrypt](https://autocrypt.org/level1.html) standard;
|
||||
`prefer-encrypt=mutual` MAY be set by default.
|
||||
|
||||
Meta data (at least the subject and all chat-headers) SHOULD be encrypted
|
||||
by the [Memoryhole](https://github.com/autocrypt/memoryhole) standard.
|
||||
If Memoryhole is not used,
|
||||
the subject of encrypted messages SHOULD be replaced by the string
|
||||
`Chat: Encrypted message` where the part after the colon MAY be localized.
|
||||
|
||||
|
||||
# Outgoing messages
|
||||
|
||||
Messengers MUST add a `Chat-Version: 1.0` header to outgoing messages.
|
||||
For filtering and smart appearance of the messages in normal MUAs,
|
||||
the `Subject` header SHOULD start with the characters `Chat:`
|
||||
and SHOULD be an excerpt of the message.
|
||||
Replies to messages MAY follow the typical `Re:`-format.
|
||||
|
||||
The body MAY contain text which MUST have the content type `text/plain`
|
||||
or `mulipart/alternative` containing `text/plain`.
|
||||
|
||||
The text MAY be divided into a user-text-part and a footer-part using the
|
||||
line `-- ` (minus, minus, space, lineend).
|
||||
|
||||
The user-text-part MUST contain only user generated content.
|
||||
User generated content are eg. texts a user has actually typed
|
||||
or pasted or forwarded from another user.
|
||||
Full quotes, footers or sth. like that MUST NOT go to the user-text-part.
|
||||
|
||||
From: sender@domain
|
||||
To: rcpt@domain
|
||||
Chat-Version: 1.0
|
||||
Content-Type: text/plain
|
||||
Subject: Chat: Hello ...
|
||||
|
||||
Hello world!
|
||||
|
||||
|
||||
# Incoming messages
|
||||
|
||||
The `Chat-Version` header MAY be used
|
||||
to detect if a messages comes from a compatible messenger.
|
||||
|
||||
The `Subject` header MUST NOT be used
|
||||
to detect compatible messengers, groups or whatever.
|
||||
|
||||
Messenger SHOULD show the `Subject`
|
||||
if the message comes from a normal MUA together with the email-body.
|
||||
The email-body SHOULD be converted
|
||||
to plain text, full-quotes and similar regions SHOULD be cut.
|
||||
|
||||
Attachments SHOULD be shown where possible.
|
||||
If an attachment cannot be shown, a non-distracting warning SHOULD be printed.
|
||||
|
||||
|
||||
# Forwarded messages
|
||||
|
||||
Forwarded messages are outgoing messages that contain a forwarded-header
|
||||
before the user generated content.
|
||||
|
||||
The forwarded header MUST contain two lines:
|
||||
The first line contains the text
|
||||
`---------- Forwarded message ----------`
|
||||
(10 minus, space, text `Forwarded message`, space, 10 minus).
|
||||
The second line starts with `From: ` followed by the original sender
|
||||
which SHOULD be anonymized or just a placeholder.
|
||||
|
||||
From: sender@domain
|
||||
To: rcpt@domain
|
||||
Chat-Version: 1.0
|
||||
Content-Type: text/plain
|
||||
Subject: Chat: Forwarded message
|
||||
|
||||
---------- Forwarded message ----------
|
||||
From: Messenger
|
||||
|
||||
Hello world!
|
||||
|
||||
Incoming forwarded messages are detected by the header.
|
||||
The messenger SHOULD mark these messages in a way that
|
||||
it becomes obvious that the message is not created by the sender.
|
||||
Note that most messengers do not show the original sender of forwarded messages
|
||||
but MUAs typically expose the sender in the UI.
|
||||
|
||||
|
||||
# Groups
|
||||
|
||||
Groups are chats with usually more than one recipient,
|
||||
each defined by an email-address.
|
||||
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.
|
||||
|
||||
Groups MUST have a group-name.
|
||||
The group-name is any non-zero-length UTF-8 string.
|
||||
|
||||
Groups MAY have a group-image.
|
||||
|
||||
|
||||
## Outgoing groups messages
|
||||
|
||||
All group members MUST be added to the `From`/`To` headers.
|
||||
The group-id MUST be written to the `Chat-Group-ID` header.
|
||||
The group-name MUST be written to `Chat-Group-Name` header
|
||||
(the forced presence of this header makes it easier
|
||||
to join a group chat on a second device any time).
|
||||
|
||||
The `Subject` header of outgoing group messages
|
||||
SHOULD start with the characters `Chat:`
|
||||
followed by the group-name and a colon followed by an excerpt of the message.
|
||||
|
||||
To identify the group-id on replies from normal MUAs,
|
||||
the group-id MUST also be added to the message-id of outgoing messages.
|
||||
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-Name: My Group
|
||||
Message-ID: Gr.12345uvwxyZ.0001@domain
|
||||
Subject: Chat: My Group: Hello group ...
|
||||
|
||||
Hello group - this group contains three members
|
||||
|
||||
Messengers adding the member list in the form `Name <email-address>`
|
||||
MUST take care only to spread the names authorized by the contacts themselves.
|
||||
Otherwise, names as _Daddy_ or _Honey_ may be spread
|
||||
(this issue is also true for normal MUAs, however,
|
||||
for more contact- and chat-centralized apps
|
||||
such situations happen more frequently).
|
||||
|
||||
|
||||
## Incoming group messages
|
||||
|
||||
The messenger MUST search incoming messages for the group-id
|
||||
in the following headers: `Chat-Group-ID`,
|
||||
`Message-ID`, `In-Reply-To` and `References` (in this order).
|
||||
|
||||
If the messenger finds a valid and existent group-id,
|
||||
the message SHOULD be assigned to the given group.
|
||||
If the messenger finds a valid but not existent group-id,
|
||||
the messenger MAY create a new group.
|
||||
If no group-id is found,
|
||||
the message MAY be assigned
|
||||
to a normal single-user chat with the email-address given in `From`.
|
||||
|
||||
|
||||
## Add and remove members
|
||||
|
||||
Messenger clients MUST construct the member list
|
||||
from the `From`/`To` headers only on the first group message
|
||||
or if they see a `Chat-Group-Member-Added`
|
||||
or `Chat-Group-Member-Removed` action header.
|
||||
Both headers MUST have the email-address
|
||||
of the added or removed member as the value.
|
||||
Messenger clients MUST NOT construct the member list
|
||||
on other group messages
|
||||
(this is to avoid accidentally altered To-lists in normal MUAs;
|
||||
the user does not expect adding a user to a _message_
|
||||
will also add him to the _group_ "forever").
|
||||
|
||||
The messenger SHOULD send an explicit mail for each added or removed member.
|
||||
The body of the message SHOULD contain
|
||||
a localized description about what happened
|
||||
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-Name: My Group
|
||||
Chat-Group-Member-Added: member4@domain
|
||||
Message-ID: Gr.12345uvwxyZ.0002@domain
|
||||
Subject: Chat: My Group: Hello, ...
|
||||
|
||||
Hello, I've added member4@domain to our group. Now we have 4 members.
|
||||
|
||||
To remove a member:
|
||||
|
||||
From: member1@domain
|
||||
To: member2@domain, member3@domain
|
||||
Chat-Version: 1.0
|
||||
Chat-Group-ID: 12345uvwxyZ
|
||||
Chat-Group-Name: My Group
|
||||
Chat-Group-Member-Removed: member4@domain
|
||||
Message-ID: Gr.12345uvwxyZ.0003@domain
|
||||
Subject: Chat: My Group: Hello, ...
|
||||
|
||||
Hello, I've removed member4@domain from our group. Now we have 3 members.
|
||||
|
||||
|
||||
## Change group name
|
||||
|
||||
To change the group-name,
|
||||
the messenger MUST send the action header `Chat-Group-Name-Changed`
|
||||
with the value set to the old group name to all group members.
|
||||
The new group name goes to the header `Chat-Group-Name`.
|
||||
|
||||
The messenger SHOULD send an explicit mail for each name change.
|
||||
The body of the message SHOULD contain
|
||||
a localized description about what happened
|
||||
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-Name: Our Group
|
||||
Chat-Group-Name-Changed: My Group
|
||||
Message-ID: Gr.12345uvwxyZ.0004@domain
|
||||
Subject: Chat: Our Group: Hello, ...
|
||||
|
||||
Hello, I've changed the group name from "My Group" to "Our Group".
|
||||
|
||||
|
||||
## Set group image
|
||||
|
||||
A group MAY have a group-image.
|
||||
To change or set the group-image,
|
||||
the messenger MUST attach an image file to a message
|
||||
and MUST add the header `Chat-Group-Image`
|
||||
with the value set to the image name.
|
||||
|
||||
To remove the group-image,
|
||||
the messenger MUST add the header `Chat-Group-Image: 0`.
|
||||
|
||||
The messenger SHOULD send an explicit mail for each group image change.
|
||||
The body of the message SHOULD contain
|
||||
a localized description about what happened
|
||||
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-Name: Our Group
|
||||
Chat-Group-Image: image.jpg
|
||||
Message-ID: Gr.12345uvwxyZ.0005@domain
|
||||
Subject: Chat: Our Group: Hello, ...
|
||||
Content-Type: multipart/mixed; boundary="==break=="
|
||||
|
||||
--==break==
|
||||
Content-Type: text/plain
|
||||
|
||||
Hello, I've changed the group image.
|
||||
--==break==
|
||||
Content-Type: image/jpeg
|
||||
Content-Disposition: attachment; filename="image.jpg"
|
||||
|
||||
/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBw ...
|
||||
--==break==--
|
||||
|
||||
The image format SHOULD be image/jpeg or image/png.
|
||||
To save data, it is RECOMMENDED
|
||||
to add a `Chat-Group-Image` only on image changes.
|
||||
|
||||
|
||||
# Set profile image
|
||||
|
||||
A user MAY have a profile-image that MAY be spread to his contacts.
|
||||
To change or set the profile-image,
|
||||
the messenger MUST attach an image file to a message
|
||||
and MUST add the header `Chat-Profile-Image`
|
||||
with the value set to the image name.
|
||||
|
||||
To remove the profile-image,
|
||||
the messenger MUST add the header `Chat-Profile-Image: 0`.
|
||||
|
||||
To spread the image,
|
||||
the messenger MAY send the profile image
|
||||
together with the next mail to a given contact
|
||||
(to do this only once,
|
||||
the messenger has to keep a `profile_image_update_state` somewhere).
|
||||
Alternatively, the messenger MAY send an explicit mail
|
||||
for each profile-image change to all contacts using a compatible messenger.
|
||||
The messenger SHOULD NOT send an explicit mail to normal MUAs.
|
||||
|
||||
From: sender@domain
|
||||
To: rcpt@domain
|
||||
Chat-Version: 1.0
|
||||
Chat-Profile-Image: photo.jpg
|
||||
Subject: Chat: Hello, ...
|
||||
Content-Type: multipart/mixed; boundary="==break=="
|
||||
|
||||
--==break==
|
||||
Content-Type: text/plain
|
||||
|
||||
Hello, I've changed my profile image.
|
||||
--==break==
|
||||
Content-Type: image/jpeg
|
||||
Content-Disposition: attachment; filename="photo.jpg"
|
||||
|
||||
AKCgkJi3j4l5kjoldfUAKCgkJi3j4lldfHjgWICwgIEBQYFBA ...
|
||||
--==break==--
|
||||
|
||||
The image format SHOULD be image/jpeg or image/png.
|
||||
Note that `Chat-Profile-Image` may appear together with all other headers,
|
||||
eg. there may be a `Chat-Profile-Image` and a `Chat-Group-Image` header
|
||||
in the same message.
|
||||
To save data, it is RECOMMENDED to add a `Chat-Profile-Image` header
|
||||
only on image changes.
|
||||
|
||||
|
||||
# Miscellaneous
|
||||
|
||||
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.
|
||||
|
||||
Messengers MAY add a `Chat-Duration` header
|
||||
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.
|
||||
|
||||
Chat-Predecessor: foo123@domain
|
||||
Chat-Voice-Message: 1
|
||||
Chat-Duration: 10000
|
||||
|
||||
Messengers MAY send and receive Message Disposition Notifications
|
||||
(MDNs, [RFC 8098](https://tools.ietf.org/html/rfc8098),
|
||||
[RFC 3503](https://tools.ietf.org/html/rfc3503))
|
||||
using the `Chat-Disposition-Notification-To` header
|
||||
instead of the `Disposition-Notification-To`
|
||||
(which unfortunately forces many other MUAs
|
||||
to send weird mails not following any standard).
|
||||
|
||||
|
||||
## Sync messages
|
||||
|
||||
If some action is required by a message header,
|
||||
the action should only be performed if the _effective date_ is newer
|
||||
than the date the last action was performed.
|
||||
|
||||
We define the effective date of a message
|
||||
as the sending time of the message as indicated by its Date header,
|
||||
or the time of first receipt if that date is in the future or unavailable.
|
||||
|
||||
|
||||
Copyright © 2017-2019 Delta Chat contributors.
|
||||
@@ -3,11 +3,10 @@ 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::*;
|
||||
use crate::dc_tools::as_str;
|
||||
use crate::key::*;
|
||||
|
||||
/// Possible values for encryption preference
|
||||
@@ -64,11 +63,8 @@ impl Aheader {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_imffields(
|
||||
wanted_from: *const libc::c_char,
|
||||
header: *const mailimf_fields,
|
||||
) -> Option<Self> {
|
||||
if wanted_from.is_null() || header.is_null() {
|
||||
pub fn from_imffields(wanted_from: &str, header: *const mailimf_fields) -> Option<Self> {
|
||||
if header.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -94,7 +90,7 @@ impl Aheader {
|
||||
|
||||
match Self::from_str(value) {
|
||||
Ok(test) => {
|
||||
if addr_cmp(&test.addr, as_str(wanted_from)) {
|
||||
if addr_cmp(&test.addr, wanted_from) {
|
||||
if fine_header.is_none() {
|
||||
fine_header = Some(test);
|
||||
} else {
|
||||
|
||||
1328
src/chat.rs
1328
src/chat.rs
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ use crate::contact::*;
|
||||
use crate::context::*;
|
||||
use crate::error::Result;
|
||||
use crate::lot::Lot;
|
||||
use crate::message::*;
|
||||
use crate::message::Message;
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
/// An object representing a single chatlist in memory.
|
||||
@@ -31,17 +31,13 @@ use crate::stock::StockMessage;
|
||||
/// first entry and only present on new messages, there is the rough idea that it can be optionally always
|
||||
/// present and sorted into the list by date. Rendering the deaddrop in the described way
|
||||
/// would not add extra work in the UI then.
|
||||
pub struct Chatlist<'a> {
|
||||
context: &'a Context,
|
||||
#[derive(Debug)]
|
||||
pub struct Chatlist {
|
||||
/// Stores pairs of `chat_id, message_id`
|
||||
ids: Vec<(u32, u32)>,
|
||||
}
|
||||
|
||||
impl<'a> Chatlist<'a> {
|
||||
pub fn get_context(&self) -> &Context {
|
||||
self.context
|
||||
}
|
||||
|
||||
impl Chatlist {
|
||||
/// Get a list of chats.
|
||||
/// The list can be filtered by query parameters.
|
||||
///
|
||||
@@ -85,7 +81,7 @@ impl<'a> Chatlist<'a> {
|
||||
/// `query_contact_id`: An optional contact ID for filtering the list. Only chats including this contact ID
|
||||
/// are returned.
|
||||
pub fn try_load(
|
||||
context: &'a Context,
|
||||
context: &Context,
|
||||
listflags: usize,
|
||||
query: Option<&str>,
|
||||
query_contact_id: Option<u32>,
|
||||
@@ -200,7 +196,7 @@ impl<'a> Chatlist<'a> {
|
||||
ids.push((DC_CHAT_ID_ARCHIVED_LINK, 0));
|
||||
}
|
||||
|
||||
Ok(Chatlist { context, ids })
|
||||
Ok(Chatlist { ids })
|
||||
}
|
||||
|
||||
/// Find out the number of chats.
|
||||
@@ -247,7 +243,7 @@ impl<'a> Chatlist<'a> {
|
||||
/// - dc_lot_t::timestamp: the timestamp of the message. 0 if not applicable.
|
||||
/// - dc_lot_t::state: The state of the message as one of the DC_STATE_* constants (see #dc_msg_get_state()).
|
||||
// 0 if not applicable.
|
||||
pub fn get_summary(&self, index: usize, chat: Option<&Chat<'a>>) -> Lot {
|
||||
pub fn get_summary(&self, context: &Context, index: usize, chat: Option<&Chat>) -> Lot {
|
||||
// The summary is created by the chat, not by the last message.
|
||||
// This is because we may want to display drafts here or stuff as
|
||||
// "is typing".
|
||||
@@ -263,7 +259,7 @@ impl<'a> Chatlist<'a> {
|
||||
let chat = if let Some(chat) = chat {
|
||||
chat
|
||||
} else {
|
||||
if let Ok(chat) = Chat::load_from_db(self.context, self.ids[index].0) {
|
||||
if let Ok(chat) = Chat::load_from_db(context, self.ids[index].0) {
|
||||
chat_loaded = chat;
|
||||
&chat_loaded
|
||||
} else {
|
||||
@@ -275,11 +271,11 @@ impl<'a> Chatlist<'a> {
|
||||
let mut lastcontact = None;
|
||||
|
||||
let lastmsg = if 0 != lastmsg_id {
|
||||
if let Ok(lastmsg) = dc_msg_load_from_db(self.context, lastmsg_id) {
|
||||
if let Ok(lastmsg) = Message::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(self.context, lastmsg.from_id).ok();
|
||||
lastcontact = Contact::load_from_db(context, lastmsg.from_id).ok();
|
||||
}
|
||||
|
||||
Some(lastmsg)
|
||||
@@ -294,14 +290,9 @@ impl<'a> Chatlist<'a> {
|
||||
ret.text2 = None;
|
||||
} else if lastmsg.is_none() || lastmsg.as_ref().unwrap().from_id == DC_CONTACT_ID_UNDEFINED
|
||||
{
|
||||
ret.text2 = Some(self.context.stock_str(StockMessage::NoMessages).to_string());
|
||||
ret.text2 = Some(context.stock_str(StockMessage::NoMessages).to_string());
|
||||
} else {
|
||||
ret.fill(
|
||||
&mut lastmsg.unwrap(),
|
||||
chat,
|
||||
lastcontact.as_ref(),
|
||||
self.context,
|
||||
);
|
||||
ret.fill(&mut lastmsg.unwrap(), chat, lastcontact.as_ref(), context);
|
||||
}
|
||||
|
||||
ret
|
||||
@@ -311,11 +302,10 @@ impl<'a> Chatlist<'a> {
|
||||
pub fn dc_get_archived_cnt(context: &Context) -> u32 {
|
||||
context
|
||||
.sql
|
||||
.query_row_col(
|
||||
.query_get_value(
|
||||
context,
|
||||
"SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;",
|
||||
params![],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
@@ -325,7 +315,7 @@ fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 {
|
||||
// only few fresh messages.
|
||||
context
|
||||
.sql
|
||||
.query_row_col(
|
||||
.query_get_value(
|
||||
context,
|
||||
"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \
|
||||
WHERE m.state=10 \
|
||||
@@ -333,7 +323,6 @@ fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 {
|
||||
AND c.blocked=2 \
|
||||
ORDER BY m.timestamp DESC, m.id DESC;",
|
||||
params![],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ pub enum Config {
|
||||
E2eeEnabled,
|
||||
#[strum(props(default = "1"))]
|
||||
MdnsEnabled,
|
||||
#[strum(props(default = "1"))]
|
||||
InboxWatch,
|
||||
#[strum(props(default = "1"))]
|
||||
SentboxWatch,
|
||||
@@ -72,7 +73,7 @@ impl Context {
|
||||
let value = match key {
|
||||
Config::Selfavatar => {
|
||||
let rel_path = self.sql.get_config(self, key);
|
||||
rel_path.map(|p| dc_get_abs_path_safe(self, &p).to_str().unwrap().to_string())
|
||||
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)),
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use libc::free;
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::x::*;
|
||||
use crate::login_param::LoginParam;
|
||||
|
||||
use super::read_autoconf_file;
|
||||
/* ******************************************************************************
|
||||
@@ -13,10 +14,10 @@ use super::read_autoconf_file;
|
||||
/* documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
|
||||
#[repr(C)]
|
||||
struct moz_autoconfigure_t<'a> {
|
||||
pub in_0: &'a dc_loginparam_t,
|
||||
pub in_emaildomain: *mut libc::c_char,
|
||||
pub in_emaillocalpart: *mut libc::c_char,
|
||||
pub out: dc_loginparam_t,
|
||||
pub in_0: &'a LoginParam,
|
||||
pub in_emaildomain: &'a str,
|
||||
pub in_emaillocalpart: &'a str,
|
||||
pub out: LoginParam,
|
||||
pub out_imap_set: libc::c_int,
|
||||
pub out_smtp_set: libc::c_int,
|
||||
pub tag_server: libc::c_int,
|
||||
@@ -26,44 +27,37 @@ struct moz_autoconfigure_t<'a> {
|
||||
pub unsafe fn moz_autoconfigure(
|
||||
context: &Context,
|
||||
url: &str,
|
||||
param_in: &dc_loginparam_t,
|
||||
) -> Option<dc_loginparam_t> {
|
||||
let mut moz_ac = moz_autoconfigure_t {
|
||||
in_0: param_in,
|
||||
in_emaildomain: std::ptr::null_mut(),
|
||||
in_emaillocalpart: std::ptr::null_mut(),
|
||||
out: dc_loginparam_new(),
|
||||
out_imap_set: 0,
|
||||
out_smtp_set: 0,
|
||||
tag_server: 0,
|
||||
tag_config: 0,
|
||||
};
|
||||
|
||||
let url_c = url.strdup();
|
||||
let xml_raw = read_autoconf_file(context, url_c);
|
||||
free(url_c as *mut libc::c_void);
|
||||
param_in: &LoginParam,
|
||||
) -> Option<LoginParam> {
|
||||
let xml_raw = read_autoconf_file(context, url);
|
||||
if xml_raw.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
moz_ac.in_emaillocalpart = param_in.addr.strdup();
|
||||
let p = strchr(moz_ac.in_emaillocalpart, '@' as i32);
|
||||
|
||||
if p.is_null() {
|
||||
// Split address into local part and domain part.
|
||||
let p = param_in.addr.find("@");
|
||||
if p.is_none() {
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
free(moz_ac.in_emaildomain as *mut libc::c_void);
|
||||
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
|
||||
return None;
|
||||
}
|
||||
|
||||
*p = 0 as libc::c_char;
|
||||
moz_ac.in_emaildomain = dc_strdup(p.offset(1isize));
|
||||
let (in_emaillocalpart, in_emaildomain) = param_in.addr.split_at(p.unwrap());
|
||||
let in_emaildomain = &in_emaildomain[1..];
|
||||
|
||||
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: 0,
|
||||
out_smtp_set: 0,
|
||||
tag_server: 0,
|
||||
tag_config: 0,
|
||||
};
|
||||
loop {
|
||||
match reader.read_event(&mut buf) {
|
||||
Ok(quick_xml::events::Event::Start(ref e)) => {
|
||||
@@ -76,7 +70,6 @@ pub unsafe fn moz_autoconfigure(
|
||||
Err(e) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Configure xml: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
@@ -93,17 +86,13 @@ pub unsafe fn moz_autoconfigure(
|
||||
|| moz_ac.out.send_server.is_empty()
|
||||
|| moz_ac.out.send_port == 0
|
||||
{
|
||||
let r = dc_loginparam_get_readable(&moz_ac.out);
|
||||
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
|
||||
let r = moz_ac.out.to_string();
|
||||
warn!(context, "Bad or incomplete autoconfig: {}", r,);
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
free(moz_ac.in_emaildomain as *mut libc::c_void);
|
||||
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
|
||||
return None;
|
||||
}
|
||||
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
free(moz_ac.in_emaildomain as *mut libc::c_void);
|
||||
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
|
||||
Some(moz_ac.out)
|
||||
}
|
||||
|
||||
@@ -115,8 +104,8 @@ fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
|
||||
let val = event.unescape_and_decode(reader).unwrap_or_default();
|
||||
|
||||
let addr = &moz_ac.in_0.addr;
|
||||
let email_local = as_str(moz_ac.in_emaillocalpart);
|
||||
let email_domain = as_str(moz_ac.in_emaildomain);
|
||||
let email_local = moz_ac.in_emaillocalpart;
|
||||
let email_domain = moz_ac.in_emaildomain;
|
||||
|
||||
let val = val
|
||||
.trim()
|
||||
@@ -132,13 +121,13 @@ fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
|
||||
13 => {
|
||||
let val_lower = val.to_lowercase();
|
||||
if val_lower == "ssl" {
|
||||
moz_ac.out.server_flags |= 0x200
|
||||
moz_ac.out.server_flags |= DC_LP_IMAP_SOCKET_SSL as i32
|
||||
}
|
||||
if val_lower == "starttls" {
|
||||
moz_ac.out.server_flags |= 0x100
|
||||
moz_ac.out.server_flags |= DC_LP_IMAP_SOCKET_STARTTLS as i32
|
||||
}
|
||||
if val_lower == "plain" {
|
||||
moz_ac.out.server_flags |= 0x400
|
||||
moz_ac.out.server_flags |= DC_LP_IMAP_SOCKET_PLAIN as i32
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -151,13 +140,13 @@ fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
|
||||
13 => {
|
||||
let val_lower = val.to_lowercase();
|
||||
if val_lower == "ssl" {
|
||||
moz_ac.out.server_flags |= 0x20000
|
||||
moz_ac.out.server_flags |= DC_LP_SMTP_SOCKET_SSL as i32
|
||||
}
|
||||
if val_lower == "starttls" {
|
||||
moz_ac.out.server_flags |= 0x10000
|
||||
moz_ac.out.server_flags |= DC_LP_SMTP_SOCKET_STARTTLS as i32
|
||||
}
|
||||
if val_lower == "plain" {
|
||||
moz_ac.out.server_flags |= 0x40000
|
||||
moz_ac.out.server_flags |= DC_LP_SMTP_SOCKET_PLAIN as i32
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use std::ptr;
|
||||
|
||||
use libc::free;
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::x::*;
|
||||
use std::ptr;
|
||||
use crate::login_param::LoginParam;
|
||||
|
||||
use super::read_autoconf_file;
|
||||
/* ******************************************************************************
|
||||
@@ -13,8 +15,8 @@ use super::read_autoconf_file;
|
||||
******************************************************************************/
|
||||
#[repr(C)]
|
||||
struct outlk_autodiscover_t<'a> {
|
||||
pub in_0: &'a dc_loginparam_t,
|
||||
pub out: dc_loginparam_t,
|
||||
pub in_0: &'a LoginParam,
|
||||
pub out: LoginParam,
|
||||
pub out_imap_set: libc::c_int,
|
||||
pub out_smtp_set: libc::c_int,
|
||||
pub tag_config: libc::c_int,
|
||||
@@ -25,13 +27,13 @@ struct outlk_autodiscover_t<'a> {
|
||||
pub unsafe fn outlk_autodiscover(
|
||||
context: &Context,
|
||||
url__: &str,
|
||||
param_in: &dc_loginparam_t,
|
||||
) -> Option<dc_loginparam_t> {
|
||||
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: dc_loginparam_new(),
|
||||
out: LoginParam::new(),
|
||||
out_imap_set: 0,
|
||||
out_smtp_set: 0,
|
||||
tag_config: 0,
|
||||
@@ -45,12 +47,12 @@ pub unsafe fn outlk_autodiscover(
|
||||
ok_to_continue = true;
|
||||
break;
|
||||
}
|
||||
memset(
|
||||
libc::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, url);
|
||||
xml_raw = read_autoconf_file(context, as_str(url));
|
||||
if xml_raw.is_null() {
|
||||
ok_to_continue = false;
|
||||
break;
|
||||
@@ -75,7 +77,6 @@ pub unsafe fn outlk_autodiscover(
|
||||
Err(e) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Configure xml: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
@@ -108,8 +109,8 @@ pub unsafe fn outlk_autodiscover(
|
||||
|| outlk_ad.out.send_server.is_empty()
|
||||
|| outlk_ad.out.send_port == 0
|
||||
{
|
||||
let r = dc_loginparam_get_readable(&outlk_ad.out);
|
||||
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
|
||||
let r = outlk_ad.out.to_string();
|
||||
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);
|
||||
@@ -168,9 +169,9 @@ unsafe fn outlk_autodiscover_endtag_cb(event: &BytesEnd, outlk_ad: &mut outlk_au
|
||||
outlk_ad.out.mail_server = to_string(outlk_ad.config[2]);
|
||||
outlk_ad.out.mail_port = port;
|
||||
if 0 != ssl_on {
|
||||
outlk_ad.out.server_flags |= 0x200
|
||||
outlk_ad.out.server_flags |= DC_LP_IMAP_SOCKET_SSL as i32
|
||||
} else if 0 != ssl_off {
|
||||
outlk_ad.out.server_flags |= 0x400
|
||||
outlk_ad.out.server_flags |= DC_LP_IMAP_SOCKET_PLAIN as i32
|
||||
}
|
||||
outlk_ad.out_imap_set = 1
|
||||
} else if strcasecmp(
|
||||
@@ -182,9 +183,9 @@ unsafe fn outlk_autodiscover_endtag_cb(event: &BytesEnd, outlk_ad: &mut outlk_au
|
||||
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 |= 0x20000
|
||||
outlk_ad.out.server_flags |= DC_LP_SMTP_SOCKET_SSL as i32
|
||||
} else if 0 != ssl_off {
|
||||
outlk_ad.out.server_flags |= 0x40000
|
||||
outlk_ad.out.server_flags |= DC_LP_SMTP_SOCKET_PLAIN as i32
|
||||
}
|
||||
outlk_ad.out_smtp_set = 1
|
||||
}
|
||||
|
||||
1054
src/configure/mod.rs
1054
src/configure/mod.rs
File diff suppressed because it is too large
Load Diff
298
src/constants.rs
298
src/constants.rs
@@ -1,9 +1,8 @@
|
||||
//! Constants
|
||||
#![allow(non_camel_case_types, dead_code)]
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use deltachat_derive::*;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref DC_VERSION_STR: String = env!("CARGO_PKG_VERSION").to_string();
|
||||
@@ -18,6 +17,12 @@ pub enum MoveState {
|
||||
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;
|
||||
@@ -42,7 +47,7 @@ impl Default for Blocked {
|
||||
|
||||
pub const DC_IMAP_SEEN: u32 = 0x1;
|
||||
|
||||
const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
|
||||
pub const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
|
||||
pub const DC_HANDSHAKE_STOP_NORMAL_PROCESSING: i32 = 0x02;
|
||||
pub const DC_HANDSHAKE_ADD_DELETE_JOB: i32 = 0x04;
|
||||
|
||||
@@ -52,17 +57,21 @@ pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
|
||||
|
||||
const DC_GCM_ADDDAYMARKER: usize = 0x01;
|
||||
|
||||
const DC_GCL_VERIFIED_ONLY: 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;
|
||||
pub const DC_IMEX_EXPORT_SELF_KEYS: i32 = 1;
|
||||
/// param1 is a directory where the keys are searched in and read from
|
||||
const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
|
||||
pub const DC_IMEX_IMPORT_SELF_KEYS: i32 = 2;
|
||||
/// param1 is a directory where the backup is written to
|
||||
const DC_IMEX_EXPORT_BACKUP: usize = 11;
|
||||
pub const DC_IMEX_EXPORT_BACKUP: i32 = 11;
|
||||
/// param1 is the file with the backup to import
|
||||
const DC_IMEX_IMPORT_BACKUP: usize = 12;
|
||||
pub const DC_IMEX_IMPORT_BACKUP: i32 = 12;
|
||||
|
||||
/// virtual chat showing all messages belonging to chats flagged with chats.blocked=2
|
||||
pub(crate) const DC_CHAT_ID_DEADDROP: u32 = 1;
|
||||
@@ -71,7 +80,7 @@ pub const DC_CHAT_ID_TRASH: u32 = 3;
|
||||
/// a message is just in creation but not yet assigned to a chat (eg. we may need the message ID to set up blobs; this avoids unready message to be sent and shown)
|
||||
const DC_CHAT_ID_MSGS_IN_CREATION: u32 = 4;
|
||||
/// virtual chat showing all messages flagged with msgs.starred=2
|
||||
const DC_CHAT_ID_STARRED: u32 = 5;
|
||||
pub const DC_CHAT_ID_STARRED: u32 = 5;
|
||||
/// only an indicator in a chatlist
|
||||
pub const DC_CHAT_ID_ARCHIVED_LINK: u32 = 6;
|
||||
/// only an indicator in a chatlist
|
||||
@@ -117,7 +126,7 @@ const DC_MAX_GET_INFO_LEN: usize = 100000;
|
||||
|
||||
pub const DC_CONTACT_ID_UNDEFINED: u32 = 0;
|
||||
pub const DC_CONTACT_ID_SELF: u32 = 1;
|
||||
const DC_CONTACT_ID_DEVICE: u32 = 2;
|
||||
pub const DC_CONTACT_ID_DEVICE: u32 = 2;
|
||||
pub const DC_CONTACT_ID_LAST_SPECIAL: u32 = 9;
|
||||
|
||||
pub const DC_CREATE_MVBOX: usize = 1;
|
||||
@@ -134,7 +143,7 @@ pub const DC_LP_AUTH_OAUTH2: usize = 0x2;
|
||||
|
||||
/// Force NORMAL authorization, this is the default.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
const DC_LP_AUTH_NORMAL: usize = 0x4;
|
||||
pub const DC_LP_AUTH_NORMAL: usize = 0x4;
|
||||
|
||||
/// Connect to IMAP via STARTTLS.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
@@ -142,7 +151,7 @@ pub const DC_LP_IMAP_SOCKET_STARTTLS: usize = 0x100;
|
||||
|
||||
/// Connect to IMAP via SSL.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
const DC_LP_IMAP_SOCKET_SSL: usize = 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.
|
||||
@@ -154,21 +163,27 @@ pub const DC_LP_SMTP_SOCKET_STARTTLS: usize = 0x10000;
|
||||
|
||||
/// Connect to SMTP via SSL.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
|
||||
pub const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
|
||||
|
||||
/// Connect to SMTP unencrypted, this should not be used.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
pub const DC_LP_SMTP_SOCKET_PLAIN: usize = 0x40000;
|
||||
|
||||
/// if none of these flags are set, the default is chosen
|
||||
const DC_LP_AUTH_FLAGS: usize = (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
|
||||
const DC_LP_IMAP_SOCKET_FLAGS: usize =
|
||||
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
|
||||
const DC_LP_SMTP_SOCKET_FLAGS: usize =
|
||||
pub const DC_LP_SMTP_SOCKET_FLAGS: usize =
|
||||
(DC_LP_SMTP_SOCKET_STARTTLS | DC_LP_SMTP_SOCKET_SSL | DC_LP_SMTP_SOCKET_PLAIN);
|
||||
|
||||
// QR code scanning (view from Bob, the joiner)
|
||||
pub const DC_VC_AUTH_REQUIRED: i32 = 2;
|
||||
pub const DC_VC_CONTACT_CONFIRM: i32 = 6;
|
||||
pub const DC_BOB_ERROR: i32 = 0;
|
||||
pub const DC_BOB_SUCCESS: i32 = 1;
|
||||
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
|
||||
#[repr(i32)]
|
||||
pub enum Viewtype {
|
||||
@@ -214,6 +229,12 @@ pub enum Viewtype {
|
||||
File = 60,
|
||||
}
|
||||
|
||||
impl Default for Viewtype {
|
||||
fn default() -> Self {
|
||||
Viewtype::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -229,242 +250,6 @@ mod tests {
|
||||
// If you do not want to handle an event, it is always safe to return 0,
|
||||
// so there is no need to add a "case" for every event.
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum Event {
|
||||
/// The library-user may write an informational string to the log.
|
||||
/// Passed to the callback given to dc_context_new().
|
||||
/// This event should not be reported to the end-user using a popup or something like that.
|
||||
/// @param data1 0
|
||||
/// @param data2 (const char*) Info string in english language.
|
||||
/// Must not be free()'d or modified and is valid only until the callback returns.
|
||||
/// @return 0
|
||||
INFO = 100,
|
||||
|
||||
/// Emitted when SMTP connection is established and login was successful.
|
||||
///
|
||||
/// @param data1 0
|
||||
/// @param data2 (const char*) Info string in english language.
|
||||
/// Must not be free()'d or modified and is valid only until the callback returns.
|
||||
/// @return 0
|
||||
SMTP_CONNECTED = 101,
|
||||
|
||||
/// Emitted when IMAP connection is established and login was successful.
|
||||
///
|
||||
/// @param data1 0
|
||||
/// @param data2 (const char*) Info string in english language.
|
||||
/// Must not be free()'d or modified and is valid only until the callback returns.
|
||||
/// @return 0
|
||||
IMAP_CONNECTED = 102,
|
||||
|
||||
/// Emitted when a message was successfully sent to the SMTP server.
|
||||
///
|
||||
/// @param data1 0
|
||||
/// @param data2 (const char*) Info string in english language.
|
||||
/// Must not be free()'d or modified and is valid only until the callback returns.
|
||||
/// @return 0
|
||||
SMTP_MESSAGE_SENT = 103,
|
||||
|
||||
/// The library-user should write a warning string to the log.
|
||||
/// Passed to the callback given to dc_context_new().
|
||||
///
|
||||
/// This event should not be reported to the end-user using a popup or something like that.
|
||||
///
|
||||
/// @param data1 0
|
||||
/// @param data2 (const char*) Warning string in english language.
|
||||
/// Must not be free()'d or modified and is valid only until the callback returns.
|
||||
/// @return 0
|
||||
WARNING = 300,
|
||||
|
||||
/// The library-user should report an error to the end-user.
|
||||
/// Passed to the callback given to dc_context_new().
|
||||
///
|
||||
/// As most things are asynchronous, things may go wrong at any time and the user
|
||||
/// should not be disturbed by a dialog or so. Instead, use a bubble or so.
|
||||
///
|
||||
/// However, for ongoing processes (eg. configure())
|
||||
/// or for functions that are expected to fail (eg. dc_continue_key_transfer())
|
||||
/// it might be better to delay showing these events until the function has really
|
||||
/// failed (returned false). It should be sufficient to report only the _last_ error
|
||||
/// in a messasge box then.
|
||||
///
|
||||
/// @param data1 0
|
||||
/// @param data2 (const char*) Error string, always set, never NULL. Frequent error strings are
|
||||
/// localized using #DC_EVENT_GET_STRING, however, most error strings will be in english language.
|
||||
/// Must not be free()'d or modified and is valid only until the callback returns.
|
||||
/// @return 0
|
||||
ERROR = 400,
|
||||
|
||||
/// An action cannot be performed because there is no network available.
|
||||
///
|
||||
/// The library will typically try over after a some time
|
||||
/// and when dc_maybe_network() is called.
|
||||
///
|
||||
/// Network errors should be reported to users in a non-disturbing way,
|
||||
/// however, as network errors may come in a sequence,
|
||||
/// it is not useful to raise each an every error to the user.
|
||||
/// For this purpose, data1 is set to 1 if the error is probably worth reporting.
|
||||
///
|
||||
/// Moreover, if the UI detects that the device is offline,
|
||||
/// it is probably more useful to report this to the user
|
||||
/// instead of the string from data2.
|
||||
///
|
||||
/// @param data1 (int) 1=first/new network error, should be reported the user;
|
||||
/// 0=subsequent network error, should be logged only
|
||||
/// @param data2 (const char*) Error string, always set, never NULL.
|
||||
/// Must not be free()'d or modified and is valid only until the callback returns.
|
||||
/// @return 0
|
||||
ERROR_NETWORK = 401,
|
||||
|
||||
/// An action cannot be performed because the user is not in the group.
|
||||
/// Reported eg. after a call to
|
||||
/// dc_set_chat_name(), dc_set_chat_profile_image(),
|
||||
/// dc_add_contact_to_chat(), dc_remove_contact_from_chat(),
|
||||
/// dc_send_text_msg() or another sending function.
|
||||
///
|
||||
/// @param data1 0
|
||||
/// @param data2 (const char*) Info string in english language.
|
||||
/// Must not be free()'d or modified
|
||||
/// and is valid only until the callback returns.
|
||||
/// @return 0
|
||||
ERROR_SELF_NOT_IN_GROUP = 410,
|
||||
|
||||
/// Messages or chats changed. One or more messages or chats changed for various
|
||||
/// reasons in the database:
|
||||
/// - Messages sent, received or removed
|
||||
/// - Chats created, deleted or archived
|
||||
/// - A draft has been set
|
||||
///
|
||||
/// @param data1 (int) chat_id for single added messages
|
||||
/// @param data2 (int) msg_id for single added messages
|
||||
/// @return 0
|
||||
MSGS_CHANGED = 2000,
|
||||
|
||||
/// There is a fresh message. Typically, the user will show an notification
|
||||
/// when receiving this message.
|
||||
///
|
||||
/// There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event.
|
||||
///
|
||||
/// @param data1 (int) chat_id
|
||||
/// @param data2 (int) msg_id
|
||||
/// @return 0
|
||||
INCOMING_MSG = 2005,
|
||||
|
||||
/// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to
|
||||
/// DC_STATE_OUT_DELIVERED, see dc_msg_get_state().
|
||||
///
|
||||
/// @param data1 (int) chat_id
|
||||
/// @param data2 (int) msg_id
|
||||
/// @return 0
|
||||
MSG_DELIVERED = 2010,
|
||||
|
||||
/// 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().
|
||||
///
|
||||
/// @param data1 (int) chat_id
|
||||
/// @param data2 (int) msg_id
|
||||
/// @return 0
|
||||
MSG_FAILED = 2012,
|
||||
|
||||
/// 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().
|
||||
///
|
||||
/// @param data1 (int) chat_id
|
||||
/// @param data2 (int) msg_id
|
||||
/// @return 0
|
||||
MSG_READ = 2015,
|
||||
|
||||
/// Chat changed. The name or the image of a chat group was changed or members were added or removed.
|
||||
/// Or the verify state of a chat has changed.
|
||||
/// See dc_set_chat_name(), dc_set_chat_profile_image(), dc_add_contact_to_chat()
|
||||
/// and dc_remove_contact_from_chat().
|
||||
///
|
||||
/// @param data1 (int) chat_id
|
||||
/// @param data2 0
|
||||
/// @return 0
|
||||
CHAT_MODIFIED = 2020,
|
||||
|
||||
/// Contact(s) created, renamed, blocked or deleted.
|
||||
///
|
||||
/// @param data1 (int) If not 0, this is the contact_id of an added contact that should be selected.
|
||||
/// @param data2 0
|
||||
/// @return 0
|
||||
CONTACTS_CHANGED = 2030,
|
||||
|
||||
/// Location of one or more contact has changed.
|
||||
///
|
||||
/// @param data1 (int) contact_id of the contact for which the location has changed.
|
||||
/// If the locations of several contacts have been changed,
|
||||
/// eg. after calling dc_delete_all_locations(), this parameter is set to 0.
|
||||
/// @param data2 0
|
||||
/// @return 0
|
||||
LOCATION_CHANGED = 2035,
|
||||
|
||||
/// Inform about the configuration progress started by configure().
|
||||
///
|
||||
/// @param data1 (int) 0=error, 1-999=progress in permille, 1000=success and done
|
||||
/// @param data2 0
|
||||
/// @return 0
|
||||
CONFIGURE_PROGRESS = 2041,
|
||||
|
||||
/// Inform about the import/export progress started by dc_imex().
|
||||
///
|
||||
/// @param data1 (int) 0=error, 1-999=progress in permille, 1000=success and done
|
||||
/// @param data2 0
|
||||
/// @return 0
|
||||
IMEX_PROGRESS = 2051,
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// @param data1 (const char*) Path and file name.
|
||||
/// Must not be free()'d or modified and is valid only until the callback returns.
|
||||
/// @param data2 0
|
||||
/// @return 0
|
||||
IMEX_FILE_WRITTEN = 2052,
|
||||
|
||||
/// Progress information of a secure-join handshake from the view of the inviter
|
||||
/// (Alice, the person who shows the QR code).
|
||||
///
|
||||
/// These events are typically sent after a joiner has scanned the QR code
|
||||
/// generated by dc_get_securejoin_qr().
|
||||
///
|
||||
/// @param data1 (int) ID of the contact that wants to join.
|
||||
/// @param data2 (int) Progress as:
|
||||
/// 300=vg-/vc-request received, typically shown as "bob@addr joins".
|
||||
/// 600=vg-/vc-request-with-auth received, vg-member-added/vc-contact-confirm sent, typically shown as "bob@addr verified".
|
||||
/// 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol.
|
||||
/// 1000=Protocol finished for this contact.
|
||||
/// @return 0
|
||||
SECUREJOIN_INVITER_PROGRESS = 2060,
|
||||
|
||||
/// Progress information of a secure-join handshake from the view of the joiner
|
||||
/// (Bob, the person who scans the QR code).
|
||||
/// The events are typically sent while dc_join_securejoin(), which
|
||||
/// may take some time, is executed.
|
||||
/// @param data1 (int) ID of the inviting contact.
|
||||
/// @param data2 (int) Progress as:
|
||||
/// 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself."
|
||||
/// (Bob has verified alice and waits until Alice does the same for him)
|
||||
/// @return 0
|
||||
SECUREJOIN_JOINER_PROGRESS = 2061,
|
||||
|
||||
// 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.
|
||||
GET_STRING = 2091,
|
||||
}
|
||||
|
||||
const DC_EVENT_FILE_COPIED: usize = 2055; // deprecated;
|
||||
const DC_EVENT_IS_OFFLINE: usize = 2081; // deprecated;
|
||||
const DC_ERROR_SEE_STRING: usize = 0; // deprecated;
|
||||
@@ -533,12 +318,3 @@ pub enum KeyType {
|
||||
Public = 0,
|
||||
Private = 1,
|
||||
}
|
||||
|
||||
pub const DC_CMD_GROUPNAME_CHANGED: libc::c_int = 2;
|
||||
pub const DC_CMD_GROUPIMAGE_CHANGED: libc::c_int = 3;
|
||||
pub const DC_CMD_MEMBER_ADDED_TO_GROUP: libc::c_int = 4;
|
||||
pub const DC_CMD_MEMBER_REMOVED_FROM_GROUP: libc::c_int = 5;
|
||||
pub const DC_CMD_AUTOCRYPT_SETUP_MESSAGE: libc::c_int = 6;
|
||||
const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
|
||||
pub const DC_CMD_LOCATION_STREAMING_ENABLED: libc::c_int = 8;
|
||||
const DC_CMD_LOCATION_ONLY: libc::c_int = 9;
|
||||
|
||||
185
src/contact.rs
185
src/contact.rs
@@ -1,24 +1,23 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deltachat_derive::*;
|
||||
use itertools::Itertools;
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
use rusqlite;
|
||||
use rusqlite::types::*;
|
||||
|
||||
use crate::aheader::EncryptPreference;
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::e2ee;
|
||||
use crate::error::Result;
|
||||
use crate::events::Event;
|
||||
use crate::key::*;
|
||||
use crate::login_param::LoginParam;
|
||||
use crate::message::MessageState;
|
||||
use crate::peerstate::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
use crate::types::*;
|
||||
|
||||
const DC_GCL_VERIFIED_ONLY: u32 = 0x01;
|
||||
|
||||
/// Contacts with at least this origin value are shown in the contact list.
|
||||
const DC_ORIGIN_MIN_CONTACT_LIST: i32 = 0x100;
|
||||
@@ -33,8 +32,8 @@ const DC_ORIGIN_MIN_CONTACT_LIST: i32 = 0x100;
|
||||
/// For this purpose, internally, two names are tracked -
|
||||
/// authorized-name and given-name.
|
||||
/// By default, these names are equal, but functions working with contact names
|
||||
pub struct Contact<'a> {
|
||||
context: &'a Context,
|
||||
#[derive(Debug)]
|
||||
pub struct Contact {
|
||||
/// The contact ID.
|
||||
///
|
||||
/// Special message IDs:
|
||||
@@ -60,7 +59,9 @@ pub struct Contact<'a> {
|
||||
}
|
||||
|
||||
/// Possible origins of a contact.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromPrimitive, ToPrimitive)]
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromPrimitive, ToPrimitive, FromSql, ToSql,
|
||||
)]
|
||||
#[repr(i32)]
|
||||
pub enum Origin {
|
||||
Unknown = 0,
|
||||
@@ -98,20 +99,9 @@ pub enum Origin {
|
||||
ManuallyCreated = 0x4000000,
|
||||
}
|
||||
|
||||
impl ToSql for Origin {
|
||||
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
|
||||
let num: i64 = self
|
||||
.to_i64()
|
||||
.expect("impossible: Origin -> i64 conversion failed");
|
||||
|
||||
Ok(ToSqlOutput::Owned(Value::Integer(num)))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for Origin {
|
||||
fn column_result(col: ValueRef) -> FromSqlResult<Self> {
|
||||
let inner = FromSql::column_result(col)?;
|
||||
FromPrimitive::from_i64(inner).ok_or(FromSqlError::InvalidType)
|
||||
impl Default for Origin {
|
||||
fn default() -> Self {
|
||||
Origin::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,11 +140,10 @@ pub enum VerifiedStatus {
|
||||
BidirectVerified = 2,
|
||||
}
|
||||
|
||||
impl<'a> Contact<'a> {
|
||||
pub fn load_from_db(context: &'a Context, contact_id: u32) -> Result<Self> {
|
||||
impl Contact {
|
||||
pub fn load_from_db(context: &Context, contact_id: u32) -> Result<Self> {
|
||||
if contact_id == DC_CONTACT_ID_SELF {
|
||||
let contact = Contact {
|
||||
context,
|
||||
id: contact_id,
|
||||
name: context.stock_str(StockMessage::SelfMsg).into(),
|
||||
authname: "".into(),
|
||||
@@ -173,7 +162,6 @@ impl<'a> Contact<'a> {
|
||||
params![contact_id as i32],
|
||||
|row| {
|
||||
let contact = Self {
|
||||
context,
|
||||
id: contact_id,
|
||||
name: row.get::<_, String>(0)?,
|
||||
authname: row.get::<_, String>(4)?,
|
||||
@@ -192,7 +180,7 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
|
||||
/// Check if a contact is blocked.
|
||||
pub fn is_blocked_load(context: &'a Context, id: u32) -> bool {
|
||||
pub fn is_blocked_load(context: &Context, id: u32) -> bool {
|
||||
Self::load_from_db(context, id)
|
||||
.map(|contact| contact.blocked)
|
||||
.unwrap_or_default()
|
||||
@@ -226,15 +214,13 @@ impl<'a> Contact<'a> {
|
||||
let (contact_id, sth_modified) =
|
||||
Contact::add_or_lookup(context, name, addr, Origin::ManuallyCreated)?;
|
||||
let blocked = Contact::is_blocked_load(context, contact_id);
|
||||
context.call_cb(
|
||||
Event::CONTACTS_CHANGED,
|
||||
(if sth_modified == Modifier::Created {
|
||||
contact_id
|
||||
context.call_cb(Event::ContactsChanged(
|
||||
if sth_modified == Modifier::Created {
|
||||
Some(contact_id)
|
||||
} else {
|
||||
0
|
||||
}) as uintptr_t,
|
||||
0 as uintptr_t,
|
||||
);
|
||||
None
|
||||
},
|
||||
));
|
||||
if blocked {
|
||||
Contact::unblock(context, contact_id);
|
||||
}
|
||||
@@ -255,7 +241,10 @@ impl<'a> Contact<'a> {
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
context.call_cb(Event::MSGS_CHANGED, 0, 0);
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: 0,
|
||||
msg_id: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +267,7 @@ impl<'a> Contact<'a> {
|
||||
return 1;
|
||||
}
|
||||
|
||||
context.sql.query_row_col(
|
||||
context.sql.query_get_value(
|
||||
context,
|
||||
"SELECT id FROM contacts WHERE addr=?1 COLLATE NOCASE AND id>?2 AND origin>=?3 AND blocked=0;",
|
||||
params![
|
||||
@@ -286,7 +275,6 @@ impl<'a> Contact<'a> {
|
||||
DC_CONTACT_ID_LAST_SPECIAL as i32,
|
||||
DC_ORIGIN_MIN_CONTACT_LIST,
|
||||
],
|
||||
0
|
||||
).unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -319,7 +307,6 @@ impl<'a> Contact<'a> {
|
||||
if !may_be_valid_addr(&addr) {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Bad address \"{}\" for contact \"{}\".",
|
||||
addr,
|
||||
if !name.as_ref().is_empty() {
|
||||
@@ -343,19 +330,22 @@ impl<'a> Contact<'a> {
|
||||
let row_id = row.get(0)?;
|
||||
let row_name: String = row.get(1)?;
|
||||
let row_addr: String = row.get(2)?;
|
||||
let row_origin = row.get(3)?;
|
||||
let row_origin: Origin = row.get(3)?;
|
||||
let row_authname: String = row.get(4)?;
|
||||
|
||||
if !name.as_ref().is_empty() && !row_name.is_empty() {
|
||||
if origin >= row_origin && name.as_ref() != row_name {
|
||||
if !name.as_ref().is_empty() {
|
||||
if !row_name.is_empty() {
|
||||
if origin >= row_origin && name.as_ref() != row_name {
|
||||
update_name = true;
|
||||
}
|
||||
} else {
|
||||
update_name = true;
|
||||
}
|
||||
} else {
|
||||
update_name = true;
|
||||
}
|
||||
if origin == Origin::IncomingUnknownFrom && name.as_ref() != row_authname {
|
||||
update_authname = true;
|
||||
if origin == Origin::IncomingUnknownFrom && name.as_ref() != row_authname {
|
||||
update_authname = true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((row_id, row_name, row_addr, row_origin, row_authname))
|
||||
},
|
||||
) {
|
||||
@@ -395,7 +385,7 @@ impl<'a> Contact<'a> {
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET name=? WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?);",
|
||||
params![name.as_ref(), 100, row_id]
|
||||
params![name.as_ref(), Chattype::Single, row_id]
|
||||
).ok();
|
||||
}
|
||||
sth_modified = Modifier::Modified;
|
||||
@@ -412,7 +402,7 @@ impl<'a> Contact<'a> {
|
||||
row_id = sql::get_rowid(context, &context.sql, "contacts", "addr", addr);
|
||||
sth_modified = Modifier::Created;
|
||||
} else {
|
||||
error!(context, 0, "Cannot add contact.");
|
||||
error!(context, "Cannot add contact.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,19 +423,13 @@ impl<'a> Contact<'a> {
|
||||
/// To add a single contact entered by the user, you should prefer `Contact::create`,
|
||||
/// however, for adding a bunch of addresses, this function is _much_ faster.
|
||||
///
|
||||
/// The `adr_book` is a multiline string in the format `Name one\nAddress one\nName two\nAddress two`.
|
||||
/// The `addr_book` is a multiline string in the format `Name one\nAddress one\nName two\nAddress two`.
|
||||
///
|
||||
/// Returns the number of modified contacts.
|
||||
pub fn add_address_book(context: &Context, adr_book: impl AsRef<str>) -> Result<usize> {
|
||||
pub fn add_address_book(context: &Context, addr_book: impl AsRef<str>) -> Result<usize> {
|
||||
let mut modify_cnt = 0;
|
||||
|
||||
for chunk in &adr_book.as_ref().lines().chunks(2) {
|
||||
let chunk = chunk.collect::<Vec<_>>();
|
||||
if chunk.len() < 2 {
|
||||
break;
|
||||
}
|
||||
let name = chunk[0];
|
||||
let addr = chunk[1];
|
||||
for (name, addr) in split_address_book(addr_book.as_ref()).into_iter() {
|
||||
let name = normalize_name(name);
|
||||
let (_, modified) = Contact::add_or_lookup(context, name, addr, Origin::AdressBook)?;
|
||||
if modified != Modifier::None {
|
||||
@@ -453,7 +437,7 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
}
|
||||
if modify_cnt > 0 {
|
||||
context.call_cb(Event::CONTACTS_CHANGED, 0 as uintptr_t, 0 as uintptr_t);
|
||||
context.call_cb(Event::ContactsChanged(None));
|
||||
}
|
||||
|
||||
Ok(modify_cnt)
|
||||
@@ -479,8 +463,10 @@ impl<'a> Contact<'a> {
|
||||
|
||||
let mut add_self = false;
|
||||
let mut ret = Vec::new();
|
||||
let flag_verified_only = listflags_has(listflags, DC_GCL_VERIFIED_ONLY);
|
||||
let flag_add_self = listflags_has(listflags, DC_GCL_ADD_SELF);
|
||||
|
||||
if (listflags & DC_GCL_VERIFIED_ONLY) > 0 || query.is_some() {
|
||||
if flag_verified_only || query.is_some() {
|
||||
let s3str_like_cmd = format!(
|
||||
"%{}%",
|
||||
query
|
||||
@@ -504,7 +490,7 @@ impl<'a> Contact<'a> {
|
||||
0x100,
|
||||
&s3str_like_cmd,
|
||||
&s3str_like_cmd,
|
||||
if 0 != listflags & 0x1 { 0 } else { 1 },
|
||||
if flag_verified_only { 0 } else { 1 },
|
||||
],
|
||||
|row| row.get::<_, i32>(0),
|
||||
|ids| {
|
||||
@@ -544,7 +530,7 @@ impl<'a> Contact<'a> {
|
||||
)?;
|
||||
}
|
||||
|
||||
if 0 != listflags & DC_GCL_ADD_SELF as u32 && add_self {
|
||||
if flag_add_self && add_self {
|
||||
ret.push(DC_CONTACT_ID_SELF);
|
||||
}
|
||||
|
||||
@@ -554,11 +540,10 @@ impl<'a> Contact<'a> {
|
||||
pub fn get_blocked_cnt(context: &Context) -> usize {
|
||||
context
|
||||
.sql
|
||||
.query_row_col::<_, isize>(
|
||||
.query_get_value::<_, isize>(
|
||||
context,
|
||||
"SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0",
|
||||
params![DC_CONTACT_ID_LAST_SPECIAL as i32],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default() as usize
|
||||
}
|
||||
@@ -589,7 +574,7 @@ impl<'a> Contact<'a> {
|
||||
|
||||
if let Ok(contact) = Contact::load_from_db(context, contact_id) {
|
||||
let peerstate = Peerstate::from_addr(context, &context.sql, &contact.addr);
|
||||
let loginparam = dc_loginparam_read(context, &context.sql, "configured_");
|
||||
let loginparam = LoginParam::from_database(context, "configured_");
|
||||
|
||||
let mut self_key = Key::from_self_public(context, &loginparam.addr, &context.sql);
|
||||
|
||||
@@ -603,7 +588,7 @@ impl<'a> Contact<'a> {
|
||||
});
|
||||
ret += &p;
|
||||
if self_key.is_none() {
|
||||
dc_ensure_secret_key_exists(context)?;
|
||||
e2ee::ensure_secret_key_exists(context)?;
|
||||
self_key = Key::from_self_public(context, &loginparam.addr, &context.sql);
|
||||
}
|
||||
let p = context.stock_str(StockMessage::FingerPrints);
|
||||
@@ -661,22 +646,20 @@ impl<'a> Contact<'a> {
|
||||
|
||||
let count_contacts: i32 = context
|
||||
.sql
|
||||
.query_row_col(
|
||||
.query_get_value(
|
||||
context,
|
||||
"SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?;",
|
||||
params![contact_id as i32],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
let count_msgs: i32 = if count_contacts > 0 {
|
||||
context
|
||||
.sql
|
||||
.query_row_col(
|
||||
.query_get_value(
|
||||
context,
|
||||
"SELECT COUNT(*) FROM msgs WHERE from_id=? OR to_id=?;",
|
||||
params![contact_id as i32, contact_id as i32],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
@@ -691,11 +674,11 @@ impl<'a> Contact<'a> {
|
||||
params![contact_id as i32],
|
||||
) {
|
||||
Ok(_) => {
|
||||
context.call_cb(Event::CONTACTS_CHANGED, 0, 0);
|
||||
context.call_cb(Event::ContactsChanged(None));
|
||||
return Ok(());
|
||||
}
|
||||
Err(err) => {
|
||||
error!(context, 0, "delete_contact {} failed ({})", contact_id, err);
|
||||
error!(context, "delete_contact {} failed ({})", contact_id, err);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
@@ -703,7 +686,7 @@ impl<'a> Contact<'a> {
|
||||
|
||||
info!(
|
||||
context,
|
||||
0, "could not delete contact {}, there are {} messages with it", contact_id, count_msgs
|
||||
"could not delete contact {}, there are {} messages with it", contact_id, count_msgs
|
||||
);
|
||||
bail!("Could not delete contact with messages in it");
|
||||
}
|
||||
@@ -779,9 +762,11 @@ impl<'a> Contact<'a> {
|
||||
/// Get the contact's profile image.
|
||||
/// This is the image set by each remote user on their own
|
||||
/// using dc_set_config(context, "selfavatar", image).
|
||||
pub fn get_profile_image(&self) -> Option<String> {
|
||||
pub fn get_profile_image(&self, context: &Context) -> Option<PathBuf> {
|
||||
if self.id == DC_CONTACT_ID_SELF {
|
||||
return self.context.get_config(Config::Selfavatar);
|
||||
if let Some(p) = context.get_config(Config::Selfavatar) {
|
||||
return Some(PathBuf::from(p));
|
||||
}
|
||||
}
|
||||
// TODO: else get image_abs from contact param
|
||||
None
|
||||
@@ -800,14 +785,18 @@ impl<'a> Contact<'a> {
|
||||
///
|
||||
/// The UI may draw a checkbox or something like that beside verified contacts.
|
||||
///
|
||||
pub fn is_verified(&self) -> VerifiedStatus {
|
||||
self.is_verified_ex(None)
|
||||
pub fn is_verified(&self, context: &Context) -> VerifiedStatus {
|
||||
self.is_verified_ex(context, None)
|
||||
}
|
||||
|
||||
/// Same as `Contact::is_verified` but allows speeding up things
|
||||
/// by adding the peerstate belonging to the contact.
|
||||
/// If you do not have the peerstate available, it is loaded automatically.
|
||||
pub fn is_verified_ex(&self, peerstate: Option<&Peerstate<'a>>) -> VerifiedStatus {
|
||||
pub fn is_verified_ex(
|
||||
&self,
|
||||
context: &Context,
|
||||
peerstate: Option<&Peerstate>,
|
||||
) -> VerifiedStatus {
|
||||
// We're always sort of secured-verified as we could verify the key on this device any time with the key
|
||||
// on this device
|
||||
if self.id == DC_CONTACT_ID_SELF {
|
||||
@@ -820,7 +809,7 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let peerstate = Peerstate::from_addr(self.context, &self.context.sql, &self.addr);
|
||||
let peerstate = Peerstate::from_addr(context, &context.sql, &self.addr);
|
||||
if let Some(ps) = peerstate {
|
||||
if ps.verified_key().is_some() {
|
||||
return VerifiedStatus::BidirectVerified;
|
||||
@@ -854,11 +843,10 @@ impl<'a> Contact<'a> {
|
||||
|
||||
context
|
||||
.sql
|
||||
.query_row_col::<_, isize>(
|
||||
.query_get_value::<_, isize>(
|
||||
context,
|
||||
"SELECT COUNT(*) FROM contacts WHERE id>?;",
|
||||
params![DC_CONTACT_ID_LAST_SPECIAL as i32],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default() as usize
|
||||
}
|
||||
@@ -951,11 +939,7 @@ fn set_block_contact(context: &Context, contact_id: u32, new_blocking: bool) {
|
||||
params![new_blocking, 100, contact_id as i32],
|
||||
).is_ok() {
|
||||
Contact::mark_noticed(context, contact_id);
|
||||
context.call_cb(
|
||||
Event::CONTACTS_CHANGED,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
context.call_cb(Event::ContactsChanged(None));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1043,6 +1027,21 @@ pub fn addr_equals_self(context: &Context, addr: impl AsRef<str>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn split_address_book(book: &str) -> Vec<(&str, &str)> {
|
||||
book.lines()
|
||||
.chunks(2)
|
||||
.into_iter()
|
||||
.filter_map(|mut chunk| {
|
||||
let name = chunk.next().unwrap();
|
||||
let addr = match chunk.next() {
|
||||
Some(a) => a,
|
||||
None => return None,
|
||||
};
|
||||
Some((name, addr))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -1078,4 +1077,14 @@ mod tests {
|
||||
fn test_get_first_name() {
|
||||
assert_eq!(get_first_name("John Doe"), "John");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_address_book() {
|
||||
let book = "Name one\nAddress one\nName two\nAddress two\nrest name";
|
||||
let list = split_address_book(&book);
|
||||
assert_eq!(
|
||||
list,
|
||||
vec![("Name one", "Address one"), ("Name two", "Address two")]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
935
src/context.rs
935
src/context.rs
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,7 @@
|
||||
use crate::location::Location;
|
||||
use crate::types::*;
|
||||
|
||||
/* * the structure behind dc_array_t */
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum dc_array_t {
|
||||
Locations(Vec<Location>),
|
||||
@@ -19,7 +18,7 @@ impl dc_array_t {
|
||||
dc_array_t::Locations(Vec::with_capacity(capacity))
|
||||
}
|
||||
|
||||
pub fn add_id(&mut self, item: uint32_t) {
|
||||
pub fn add_id(&mut self, item: u32) {
|
||||
if let Self::Uint(array) = self {
|
||||
array.push(item);
|
||||
} else {
|
||||
@@ -35,10 +34,10 @@ impl dc_array_t {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_id(&self, index: usize) -> uint32_t {
|
||||
pub fn get_id(&self, index: usize) -> u32 {
|
||||
match self {
|
||||
Self::Locations(array) => array[index].location_id,
|
||||
Self::Uint(array) => array[index] as uint32_t,
|
||||
Self::Uint(array) => array[index] as u32,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -165,3 +165,28 @@ fn dehtml_starttag_cb<B: std::io::BufRead>(
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_dc_dehtml() {
|
||||
let cases = vec",
|
||||
),
|
||||
("<img href='/foo.png'>", ""),
|
||||
("<b> bar </b>", "* bar *"),
|
||||
("<b> bar <i> foo", "* bar _ foo"),
|
||||
("& bar", "& bar"),
|
||||
// Note missing '
|
||||
("<a href='/foo.png>Hi</a> ", ""),
|
||||
("", ""),
|
||||
];
|
||||
for (input, output) in cases {
|
||||
assert_eq!(dc_dehtml(input), output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1186
src/dc_e2ee.rs
1186
src/dc_e2ee.rs
File diff suppressed because it is too large
Load Diff
1073
src/dc_imex.rs
1073
src/dc_imex.rs
File diff suppressed because it is too large
Load Diff
@@ -1,214 +0,0 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::sql::Sql;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct dc_loginparam_t {
|
||||
pub addr: String,
|
||||
pub mail_server: String,
|
||||
pub mail_user: String,
|
||||
pub mail_pw: String,
|
||||
pub mail_port: i32,
|
||||
pub send_server: String,
|
||||
pub send_user: String,
|
||||
pub send_pw: String,
|
||||
pub send_port: i32,
|
||||
pub server_flags: i32,
|
||||
}
|
||||
|
||||
impl dc_loginparam_t {
|
||||
pub fn addr_str(&self) -> &str {
|
||||
self.addr.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_loginparam_new() -> dc_loginparam_t {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn dc_loginparam_read(
|
||||
context: &Context,
|
||||
sql: &Sql,
|
||||
prefix: impl AsRef<str>,
|
||||
) -> dc_loginparam_t {
|
||||
let prefix = prefix.as_ref();
|
||||
|
||||
let key = format!("{}addr", prefix);
|
||||
let addr = sql
|
||||
.get_config(context, key)
|
||||
.unwrap_or_default()
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
let key = format!("{}mail_server", prefix);
|
||||
let mail_server = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}mail_port", prefix);
|
||||
let mail_port = sql.get_config_int(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}mail_user", prefix);
|
||||
let mail_user = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}mail_pw", prefix);
|
||||
let mail_pw = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_server", prefix);
|
||||
let send_server = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_port", prefix);
|
||||
let send_port = sql.get_config_int(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_user", prefix);
|
||||
let send_user = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_pw", prefix);
|
||||
let send_pw = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}server_flags", prefix);
|
||||
let server_flags = sql.get_config_int(context, key).unwrap_or_default();
|
||||
|
||||
dc_loginparam_t {
|
||||
addr: addr.to_string(),
|
||||
mail_server,
|
||||
mail_user,
|
||||
mail_pw,
|
||||
mail_port,
|
||||
send_server,
|
||||
send_user,
|
||||
send_pw,
|
||||
send_port,
|
||||
server_flags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_loginparam_write(
|
||||
context: &Context,
|
||||
loginparam: &dc_loginparam_t,
|
||||
sql: &Sql,
|
||||
prefix: impl AsRef<str>,
|
||||
) {
|
||||
let prefix = prefix.as_ref();
|
||||
|
||||
let key = format!("{}addr", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.addr)).ok();
|
||||
|
||||
let key = format!("{}mail_server", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.mail_server))
|
||||
.ok();
|
||||
|
||||
let key = format!("{}mail_port", prefix);
|
||||
sql.set_config_int(context, key, loginparam.mail_port).ok();
|
||||
|
||||
let key = format!("{}mail_user", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.mail_user))
|
||||
.ok();
|
||||
|
||||
let key = format!("{}mail_pw", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.mail_pw)).ok();
|
||||
|
||||
let key = format!("{}send_server", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.send_server))
|
||||
.ok();
|
||||
|
||||
let key = format!("{}send_port", prefix);
|
||||
sql.set_config_int(context, key, loginparam.send_port).ok();
|
||||
|
||||
let key = format!("{}send_user", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.send_user))
|
||||
.ok();
|
||||
|
||||
let key = format!("{}send_pw", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.send_pw)).ok();
|
||||
|
||||
let key = format!("{}server_flags", prefix);
|
||||
sql.set_config_int(context, key, loginparam.server_flags)
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn unset_empty(s: &String) -> Cow<String> {
|
||||
if s.is_empty() {
|
||||
Cow::Owned("unset".to_string())
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_loginparam_get_readable(loginparam: &dc_loginparam_t) -> String {
|
||||
let unset = "0";
|
||||
let pw = "***";
|
||||
|
||||
let flags_readable = get_readable_flags(loginparam.server_flags);
|
||||
|
||||
format!(
|
||||
"{} {}:{}:{}:{} {}:{}:{}:{} {}",
|
||||
unset_empty(&loginparam.addr),
|
||||
unset_empty(&loginparam.mail_user),
|
||||
if !loginparam.mail_pw.is_empty() {
|
||||
pw
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
unset_empty(&loginparam.mail_server),
|
||||
loginparam.mail_port,
|
||||
unset_empty(&loginparam.send_user),
|
||||
if !loginparam.send_pw.is_empty() {
|
||||
pw
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
unset_empty(&loginparam.send_server),
|
||||
loginparam.send_port,
|
||||
flags_readable,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_readable_flags(flags: i32) -> String {
|
||||
let mut res = String::new();
|
||||
for bit in 0..31 {
|
||||
if 0 != flags & 1 << bit {
|
||||
let mut flag_added = 0;
|
||||
if 1 << bit == 0x2 {
|
||||
res += "OAUTH2 ";
|
||||
flag_added = 1;
|
||||
}
|
||||
if 1 << bit == 0x4 {
|
||||
res += "AUTH_NORMAL ";
|
||||
flag_added = 1;
|
||||
}
|
||||
if 1 << bit == 0x100 {
|
||||
res += "IMAP_STARTTLS ";
|
||||
flag_added = 1;
|
||||
}
|
||||
if 1 << bit == 0x200 {
|
||||
res += "IMAP_SSL ";
|
||||
flag_added = 1;
|
||||
}
|
||||
if 1 << bit == 0x400 {
|
||||
res += "IMAP_PLAIN ";
|
||||
flag_added = 1;
|
||||
}
|
||||
if 1 << bit == 0x10000 {
|
||||
res += "SMTP_STARTTLS ";
|
||||
flag_added = 1
|
||||
}
|
||||
if 1 << bit == 0x20000 {
|
||||
res += "SMTP_SSL ";
|
||||
flag_added = 1
|
||||
}
|
||||
if 1 << bit == 0x40000 {
|
||||
res += "SMTP_PLAIN ";
|
||||
flag_added = 1
|
||||
}
|
||||
if 0 == flag_added {
|
||||
res += &format!("{:#0x}", 1 << bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
if res.is_empty() {
|
||||
res += "0";
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
2620
src/dc_mimeparser.rs
2620
src/dc_mimeparser.rs
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
||||
use crate::constants::*;
|
||||
use crate::context::*;
|
||||
use crate::job::*;
|
||||
use crate::message::*;
|
||||
use crate::param::Params;
|
||||
|
||||
pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "mvbox_move")
|
||||
.unwrap_or_else(|| 1)
|
||||
== 0
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if !dc_is_inbox(context, folder) && !dc_is_sentbox(context, folder) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Ok(msg) = dc_msg_new_load(context, 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 dc_is_mvbox(context, folder) {
|
||||
dc_update_msg_move_state(context, msg.rfc724_mid, MoveState::Stay);
|
||||
}
|
||||
|
||||
// 1 = dc message, 2 = reply to dc message
|
||||
if 0 != msg.is_dc_message {
|
||||
job_add(
|
||||
context,
|
||||
Action::MoveMsg,
|
||||
msg.id as libc::c_int,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
dc_update_msg_move_state(context, msg.rfc724_mid, MoveState::Moving);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
1008
src/dc_securejoin.rs
1008
src/dc_securejoin.rs
File diff suppressed because it is too large
Load Diff
@@ -211,6 +211,16 @@ fn is_plain_quote(buf: &str) -> bool {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use proptest::prelude::*;
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
// proptest does not support [[:graphical:][:space:]] regex.
|
||||
fn test_simplify_plain_text_fuzzy(input in "[!-~\t \n]+") {
|
||||
let output = Simplify::new().simplify_plain_text(&input, true);
|
||||
assert!(output.split('\n').all(|s| s != "-- "));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_trim() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,65 +0,0 @@
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::*;
|
||||
use crate::sql;
|
||||
|
||||
// Token namespaces
|
||||
#[allow(non_camel_case_types)]
|
||||
type dc_tokennamespc_t = usize;
|
||||
pub const DC_TOKEN_AUTH: dc_tokennamespc_t = 110;
|
||||
pub const DC_TOKEN_INVITENUMBER: dc_tokennamespc_t = 100;
|
||||
|
||||
// Functions to read/write token from/to the database. A token is any string associated with a key.
|
||||
|
||||
pub fn dc_token_save(
|
||||
context: &Context,
|
||||
namespc: dc_tokennamespc_t,
|
||||
foreign_id: u32,
|
||||
token: *const libc::c_char,
|
||||
) -> bool {
|
||||
if token.is_null() {
|
||||
return false;
|
||||
}
|
||||
// foreign_id may be 0
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"INSERT INTO tokens (namespc, foreign_id, token, timestamp) VALUES (?, ?, ?, ?);",
|
||||
params![namespc as i32, foreign_id as i32, as_str(token), time()],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn dc_token_lookup(
|
||||
context: &Context,
|
||||
namespc: dc_tokennamespc_t,
|
||||
foreign_id: u32,
|
||||
) -> *mut libc::c_char {
|
||||
context
|
||||
.sql
|
||||
.query_row_col::<_, String>(
|
||||
context,
|
||||
"SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;",
|
||||
params![namespc as i32, foreign_id as i32],
|
||||
0,
|
||||
)
|
||||
.map(|s| unsafe { s.strdup() })
|
||||
.unwrap_or_else(|| std::ptr::null_mut())
|
||||
}
|
||||
|
||||
pub fn dc_token_exists(
|
||||
context: &Context,
|
||||
namespc: dc_tokennamespc_t,
|
||||
token: *const libc::c_char,
|
||||
) -> bool {
|
||||
if token.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
context
|
||||
.sql
|
||||
.exists(
|
||||
"SELECT id FROM tokens WHERE namespc=? AND token=?;",
|
||||
params![namespc as i32, as_str(token)],
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
1199
src/dc_tools.rs
1199
src/dc_tools.rs
File diff suppressed because it is too large
Load Diff
965
src/e2ee.rs
Normal file
965
src/e2ee.rs
Normal file
@@ -0,0 +1,965 @@
|
||||
//! End-to-end encryption support.
|
||||
|
||||
use std::any::Any;
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use libc::{free, strcmp, strlen, strncmp};
|
||||
use mmime::clist::*;
|
||||
use mmime::mailimf::types::*;
|
||||
use mmime::mailimf::types_helper::*;
|
||||
use mmime::mailimf::*;
|
||||
use mmime::mailmime::content::*;
|
||||
use mmime::mailmime::types::*;
|
||||
use mmime::mailmime::types_helper::*;
|
||||
use mmime::mailmime::write_mem::*;
|
||||
use mmime::mailmime::*;
|
||||
use mmime::mailprivacy_prepare_mime;
|
||||
use mmime::mmapstring::*;
|
||||
use mmime::{mailmime_substitute, MAILIMF_NO_ERROR, MAIL_NO_ERROR};
|
||||
|
||||
use crate::aheader::*;
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::dc_mimeparser::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::*;
|
||||
use crate::key::*;
|
||||
use crate::keyring::*;
|
||||
use crate::mimefactory::MimeFactory;
|
||||
use crate::peerstate::*;
|
||||
use crate::pgp::*;
|
||||
use crate::securejoin::handle_degrade_event;
|
||||
use crate::wrapmime;
|
||||
use crate::wrapmime::*;
|
||||
|
||||
// standard mime-version header aka b"Version: 1\r\n\x00"
|
||||
static mut VERSION_CONTENT: [libc::c_char; 13] =
|
||||
[86, 101, 114, 115, 105, 111, 110, 58, 32, 49, 13, 10, 0];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EncryptHelper {
|
||||
pub prefer_encrypt: EncryptPreference,
|
||||
pub addr: String,
|
||||
pub public_key: Key,
|
||||
}
|
||||
|
||||
impl EncryptHelper {
|
||||
pub fn new(context: &Context) -> Result<EncryptHelper> {
|
||||
let e2ee = context.sql.get_config_int(&context, "e2ee_enabled");
|
||||
let prefer_encrypt = if 0 != e2ee.unwrap_or_default() {
|
||||
EncryptPreference::Mutual
|
||||
} else {
|
||||
EncryptPreference::NoPreference
|
||||
};
|
||||
let addr = match context.get_config(Config::ConfiguredAddr) {
|
||||
None => {
|
||||
bail!("addr not configured!");
|
||||
}
|
||||
Some(addr) => addr,
|
||||
};
|
||||
|
||||
let public_key = load_or_generate_self_public_key(context, &addr)?;
|
||||
Ok(EncryptHelper {
|
||||
prefer_encrypt,
|
||||
addr,
|
||||
public_key,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_aheader(&self) -> Aheader {
|
||||
let pk = self.public_key.clone();
|
||||
let addr = self.addr.to_string();
|
||||
Aheader::new(addr, pk, self.prefer_encrypt)
|
||||
}
|
||||
|
||||
pub fn try_encrypt(
|
||||
&mut self,
|
||||
factory: &mut MimeFactory,
|
||||
e2ee_guaranteed: bool,
|
||||
min_verified: libc::c_int,
|
||||
do_gossip: bool,
|
||||
mut in_out_message: *mut Mailmime,
|
||||
imffields_unprotected: *mut mailimf_fields,
|
||||
) -> Result<bool> {
|
||||
/* libEtPan's pgp_encrypt_mime() takes the parent as the new root.
|
||||
We just expect the root as being given to this function. */
|
||||
if in_out_message.is_null() || unsafe { !(*in_out_message).mm_parent.is_null() } {
|
||||
bail!("corrupted inputs");
|
||||
}
|
||||
if !(self.prefer_encrypt == EncryptPreference::Mutual || e2ee_guaranteed) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let context = &factory.context;
|
||||
let mut keyring = Keyring::default();
|
||||
let mut gossip_headers: Vec<String> = Vec::with_capacity(factory.recipients_addr.len());
|
||||
|
||||
// determine if we can and should encrypt
|
||||
for recipient_addr in factory.recipients_addr.iter() {
|
||||
if recipient_addr == &self.addr {
|
||||
continue;
|
||||
}
|
||||
let peerstate = match Peerstate::from_addr(context, &context.sql, recipient_addr) {
|
||||
Some(peerstate) => peerstate,
|
||||
None => {
|
||||
let msg = format!("peerstate for {} missing, cannot encrypt", recipient_addr);
|
||||
if e2ee_guaranteed {
|
||||
bail!("{}", msg);
|
||||
} else {
|
||||
info!(context, "{}", msg);
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
if peerstate.prefer_encrypt != EncryptPreference::Mutual && !e2ee_guaranteed {
|
||||
info!(context, "peerstate for {} is no-encrypt", recipient_addr);
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if let Some(key) = peerstate.peek_key(min_verified as usize) {
|
||||
keyring.add_owned(key.clone());
|
||||
if do_gossip {
|
||||
if let Some(header) = peerstate.render_gossip_header(min_verified as usize) {
|
||||
gossip_headers.push(header.to_string());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bail!(
|
||||
"proper enc-key for {} missing, cannot encrypt",
|
||||
recipient_addr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let sign_key = {
|
||||
keyring.add_ref(&self.public_key);
|
||||
let key = Key::from_self_private(context, self.addr.clone(), &context.sql);
|
||||
if key.is_none() {
|
||||
bail!("no own private key found")
|
||||
}
|
||||
key
|
||||
};
|
||||
|
||||
/* encrypt message */
|
||||
unsafe {
|
||||
mailprivacy_prepare_mime(in_out_message);
|
||||
let mut part_to_encrypt: *mut Mailmime =
|
||||
(*in_out_message).mm_data.mm_message.mm_msg_mime;
|
||||
(*part_to_encrypt).mm_parent = ptr::null_mut();
|
||||
let imffields_encrypted: *mut mailimf_fields = mailimf_fields_new_empty();
|
||||
/* mailmime_new_message_data() calls mailmime_fields_new_with_version() which would add the unwanted MIME-Version:-header */
|
||||
let message_to_encrypt: *mut Mailmime = mailmime_new(
|
||||
MAILMIME_MESSAGE as libc::c_int,
|
||||
ptr::null(),
|
||||
0 as libc::size_t,
|
||||
mailmime_fields_new_empty(),
|
||||
mailmime_get_content_message(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
imffields_encrypted,
|
||||
part_to_encrypt,
|
||||
);
|
||||
|
||||
for header in &gossip_headers {
|
||||
wrapmime::new_custom_field(imffields_encrypted, "Autocrypt-Gossip", &header)
|
||||
}
|
||||
|
||||
/* memoryhole headers: move some headers into encrypted part */
|
||||
// XXX note we can't use clist's into_iter() because the loop body also removes items
|
||||
let mut cur: *mut clistiter = (*(*imffields_unprotected).fld_list).first;
|
||||
while !cur.is_null() {
|
||||
let field: *mut mailimf_field = (*cur).data as *mut mailimf_field;
|
||||
let mut move_to_encrypted = false;
|
||||
if !field.is_null() {
|
||||
if (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
|
||||
move_to_encrypted = true;
|
||||
} else if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
||||
let opt_field = (*field).fld_data.fld_optional_field;
|
||||
if !opt_field.is_null() && !(*opt_field).fld_name.is_null() {
|
||||
let fld_name = to_string_lossy((*opt_field).fld_name);
|
||||
if fld_name.starts_with("Secure-Join")
|
||||
|| (fld_name.starts_with("Chat-") && fld_name != "Chat-Version")
|
||||
{
|
||||
move_to_encrypted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if move_to_encrypted {
|
||||
mailimf_fields_add(imffields_encrypted, field);
|
||||
cur = clist_delete((*imffields_unprotected).fld_list, cur);
|
||||
} else {
|
||||
cur = (*cur).next;
|
||||
}
|
||||
}
|
||||
let subject: *mut mailimf_subject = mailimf_subject_new("...".strdup());
|
||||
mailimf_fields_add(
|
||||
imffields_unprotected,
|
||||
mailimf_field_new(
|
||||
MAILIMF_FIELD_SUBJECT as libc::c_int,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
subject,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
),
|
||||
);
|
||||
wrapmime::append_ct_param(
|
||||
(*part_to_encrypt).mm_content_type,
|
||||
"protected-headers",
|
||||
"v1",
|
||||
)?;
|
||||
let plain: *mut MMAPString =
|
||||
mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
|
||||
let mut col: libc::c_int = 0i32;
|
||||
mailmime_write_mem(plain, &mut col, message_to_encrypt);
|
||||
mailmime_free(message_to_encrypt);
|
||||
if (*plain).str_0.is_null() || (*plain).len <= 0 {
|
||||
bail!("could not write/allocate");
|
||||
}
|
||||
|
||||
let ctext = dc_pgp_pk_encrypt(
|
||||
std::slice::from_raw_parts((*plain).str_0 as *const u8, (*plain).len),
|
||||
&keyring,
|
||||
sign_key.as_ref(),
|
||||
);
|
||||
mmap_string_free(plain);
|
||||
|
||||
if let Ok(ctext_v) = ctext {
|
||||
/* create MIME-structure that will contain the encrypted text */
|
||||
let mut encrypted_part: *mut Mailmime = new_data_part(
|
||||
ptr::null_mut(),
|
||||
0 as libc::size_t,
|
||||
"multipart/encrypted",
|
||||
MAILMIME_MECHANISM_BASE64,
|
||||
)?;
|
||||
let content: *mut mailmime_content = (*encrypted_part).mm_content_type;
|
||||
wrapmime::append_ct_param(content, "protocol", "application/pgp-encrypted")?;
|
||||
let version_mime: *mut Mailmime = new_data_part(
|
||||
VERSION_CONTENT.as_mut_ptr() as *mut libc::c_void,
|
||||
strlen(VERSION_CONTENT.as_mut_ptr()),
|
||||
"application/pgp-encrypted",
|
||||
MAILMIME_MECHANISM_7BIT,
|
||||
)?;
|
||||
mailmime_smart_add_part(encrypted_part, version_mime);
|
||||
|
||||
// we assume that ctext_v is not dropped until the end
|
||||
// of this if-scope
|
||||
let ctext_part: *mut Mailmime = new_data_part(
|
||||
ctext_v.as_ptr() as *mut libc::c_void,
|
||||
ctext_v.len(),
|
||||
"application/octet-stream",
|
||||
MAILMIME_MECHANISM_7BIT,
|
||||
)?;
|
||||
mailmime_smart_add_part(encrypted_part, ctext_part);
|
||||
(*in_out_message).mm_data.mm_message.mm_msg_mime = encrypted_part;
|
||||
(*encrypted_part).mm_parent = in_out_message;
|
||||
let gossiped = !&gossip_headers.is_empty();
|
||||
factory.finalize_mime_message(in_out_message, true, gossiped)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
bail!("encryption failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct E2eeHelper {
|
||||
cdata_to_free: Option<Box<dyn Any>>,
|
||||
|
||||
// for decrypting only
|
||||
pub encrypted: bool,
|
||||
pub signatures: HashSet<String>,
|
||||
pub gossipped_addr: HashSet<String>,
|
||||
}
|
||||
|
||||
impl E2eeHelper {
|
||||
/// Frees data referenced by "mailmime" but not freed by mailmime_free(). After calling this function,
|
||||
/// in_out_message cannot be used any longer!
|
||||
pub unsafe fn thanks(&mut self) {
|
||||
if let Some(data) = self.cdata_to_free.take() {
|
||||
free(Box::into_raw(data) as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn decrypt(&mut self, context: &Context, in_out_message: *mut Mailmime) {
|
||||
/* return values: 0=nothing to decrypt/cannot decrypt, 1=sth. decrypted
|
||||
(to detect parts that could not be decrypted, simply look for left "multipart/encrypted" MIME types */
|
||||
/*just a pointer into mailmime structure, must not be freed*/
|
||||
let imffields: *mut mailimf_fields = mailmime_find_mailimf_fields(in_out_message);
|
||||
let mut message_time = 0;
|
||||
let mut from = None;
|
||||
let mut private_keyring = Keyring::default();
|
||||
let mut public_keyring_for_validate = Keyring::default();
|
||||
let mut gossip_headers: *mut mailimf_fields = ptr::null_mut();
|
||||
if !(in_out_message.is_null() || imffields.is_null()) {
|
||||
let mut field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
|
||||
|
||||
if !field.is_null() && !(*field).fld_data.fld_from.is_null() {
|
||||
from = mailimf_find_first_addr((*(*field).fld_data.fld_from).frm_mb_list)
|
||||
}
|
||||
|
||||
field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int);
|
||||
if !field.is_null() && !(*field).fld_data.fld_orig_date.is_null() {
|
||||
let orig_date: *mut mailimf_orig_date = (*field).fld_data.fld_orig_date;
|
||||
if !orig_date.is_null() {
|
||||
message_time = dc_timestamp_from_date((*orig_date).dt_date_time);
|
||||
if message_time != 0 && message_time > time() {
|
||||
message_time = time()
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut peerstate = None;
|
||||
let autocryptheader = from
|
||||
.as_ref()
|
||||
.and_then(|from| Aheader::from_imffields(from, imffields));
|
||||
if message_time > 0 {
|
||||
if let Some(ref from) = from {
|
||||
peerstate = Peerstate::from_addr(context, &context.sql, from);
|
||||
|
||||
if let Some(ref mut peerstate) = peerstate {
|
||||
if let Some(ref header) = autocryptheader {
|
||||
peerstate.apply_header(&header, message_time);
|
||||
peerstate.save_to_db(&context.sql, false);
|
||||
} else if message_time > peerstate.last_seen_autocrypt
|
||||
&& !contains_report(in_out_message)
|
||||
{
|
||||
peerstate.degrade_encryption(message_time);
|
||||
peerstate.save_to_db(&context.sql, false);
|
||||
}
|
||||
} else if let Some(ref header) = autocryptheader {
|
||||
let p = Peerstate::from_header(context, header, message_time);
|
||||
assert!(p.save_to_db(&context.sql, true));
|
||||
peerstate = Some(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* load private key for decryption */
|
||||
let self_addr = context.get_config(Config::ConfiguredAddr);
|
||||
if let Some(self_addr) = self_addr {
|
||||
if private_keyring.load_self_private_for_decrypting(
|
||||
context,
|
||||
self_addr,
|
||||
&context.sql,
|
||||
) {
|
||||
if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 {
|
||||
peerstate =
|
||||
Peerstate::from_addr(&context, &context.sql, &from.unwrap_or_default());
|
||||
}
|
||||
if let Some(ref peerstate) = peerstate {
|
||||
if peerstate.degrade_event.is_some() {
|
||||
handle_degrade_event(context, &peerstate);
|
||||
}
|
||||
if let Some(ref key) = peerstate.gossip_key {
|
||||
public_keyring_for_validate.add_ref(key);
|
||||
}
|
||||
if let Some(ref key) = peerstate.public_key {
|
||||
public_keyring_for_validate.add_ref(key);
|
||||
}
|
||||
}
|
||||
for iterations in 0..10 {
|
||||
let mut has_unencrypted_parts: libc::c_int = 0i32;
|
||||
if decrypt_recursive(
|
||||
context,
|
||||
in_out_message,
|
||||
&private_keyring,
|
||||
&public_keyring_for_validate,
|
||||
&mut self.signatures,
|
||||
&mut gossip_headers,
|
||||
&mut has_unencrypted_parts,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
break;
|
||||
}
|
||||
/* if we're here, sth. was encrypted. if we're on top-level,
|
||||
and there are no additional unencrypted parts in the message
|
||||
the encryption was fine (signature is handled separately and
|
||||
returned as `signatures`) */
|
||||
if iterations == 0 && 0 == has_unencrypted_parts {
|
||||
self.encrypted = true;
|
||||
}
|
||||
}
|
||||
/* check for Autocrypt-Gossip */
|
||||
if !gossip_headers.is_null() {
|
||||
self.gossipped_addr = update_gossip_peerstates(
|
||||
context,
|
||||
message_time,
|
||||
imffields,
|
||||
gossip_headers,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//mailmime_print(in_out_message);
|
||||
if !gossip_headers.is_null() {
|
||||
mailimf_fields_free(gossip_headers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn new_data_part(
|
||||
data: *mut libc::c_void,
|
||||
data_bytes: libc::size_t,
|
||||
content_type: &str,
|
||||
default_encoding: u32,
|
||||
) -> Result<*mut Mailmime> {
|
||||
let content = new_content_type(&content_type)?;
|
||||
unsafe {
|
||||
let mut encoding: *mut mailmime_mechanism = ptr::null_mut();
|
||||
if wrapmime::content_type_needs_encoding(content) {
|
||||
encoding = mailmime_mechanism_new(default_encoding as i32, ptr::null_mut());
|
||||
ensure!(!encoding.is_null(), "failed to create encoding");
|
||||
}
|
||||
let mime_fields = mailmime_fields_new_with_data(
|
||||
encoding,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
ensure!(!mime_fields.is_null(), "internal mime error");
|
||||
|
||||
let mime = mailmime_new_empty(content, mime_fields);
|
||||
ensure!(!mime.is_null(), "internal mime error");
|
||||
|
||||
if (*mime).mm_type == MAILMIME_SINGLE as libc::c_int {
|
||||
if !data.is_null() && data_bytes > 0 {
|
||||
mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes);
|
||||
}
|
||||
}
|
||||
return Ok(mime);
|
||||
}
|
||||
}
|
||||
|
||||
/// Load public key from database or generate a new one.
|
||||
///
|
||||
/// This will load a public key from the database, generating and
|
||||
/// storing a new one when one doesn't exist yet. Care is taken to
|
||||
/// only generate one key per context even when multiple threads call
|
||||
/// this function concurrently.
|
||||
fn load_or_generate_self_public_key(context: &Context, self_addr: impl AsRef<str>) -> Result<Key> {
|
||||
if let Some(key) = Key::from_self_public(context, &self_addr, &context.sql) {
|
||||
return Ok(key);
|
||||
}
|
||||
let _guard = context.generating_key_mutex.lock().unwrap();
|
||||
|
||||
// Check again in case the key was generated while we were waiting for the lock.
|
||||
if let Some(key) = Key::from_self_public(context, &self_addr, &context.sql) {
|
||||
return Ok(key);
|
||||
}
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
info!(
|
||||
context,
|
||||
"Generating keypair with {} bits, e={} ...", 2048, 65537,
|
||||
);
|
||||
match dc_pgp_create_keypair(&self_addr) {
|
||||
Some((public_key, private_key)) => {
|
||||
match dc_key_save_self_keypair(
|
||||
context,
|
||||
&public_key,
|
||||
&private_key,
|
||||
&self_addr,
|
||||
1,
|
||||
&context.sql,
|
||||
) {
|
||||
true => {
|
||||
info!(
|
||||
context,
|
||||
"Keypair generated in {:.3}s.",
|
||||
start.elapsed().as_secs()
|
||||
);
|
||||
Ok(public_key)
|
||||
}
|
||||
false => Err(format_err!("Failed to save keypair")),
|
||||
}
|
||||
}
|
||||
None => Err(format_err!("Failed to generate keypair")),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn update_gossip_peerstates(
|
||||
context: &Context,
|
||||
message_time: i64,
|
||||
imffields: *mut mailimf_fields,
|
||||
gossip_headers: *const mailimf_fields,
|
||||
) -> HashSet<String> {
|
||||
let mut recipients: Option<HashSet<String>> = None;
|
||||
let mut gossipped_addr: HashSet<String> = Default::default();
|
||||
|
||||
for cur_data in (*(*gossip_headers).fld_list).into_iter() {
|
||||
let field: *mut mailimf_field = cur_data as *mut _;
|
||||
if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
||||
let optional_field = (*field).fld_data.fld_optional_field;
|
||||
if !optional_field.is_null()
|
||||
&& !(*optional_field).fld_name.is_null()
|
||||
&& strcasecmp(
|
||||
(*optional_field).fld_name,
|
||||
b"Autocrypt-Gossip\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0i32
|
||||
{
|
||||
let value = CStr::from_ptr((*optional_field).fld_value)
|
||||
.to_str()
|
||||
.unwrap();
|
||||
let gossip_header = Aheader::from_str(value);
|
||||
if let Ok(ref header) = gossip_header {
|
||||
if recipients.is_none() {
|
||||
recipients = Some(mailimf_get_recipients(imffields));
|
||||
}
|
||||
if recipients.as_ref().unwrap().contains(&header.addr) {
|
||||
let mut peerstate =
|
||||
Peerstate::from_addr(context, &context.sql, &header.addr);
|
||||
if let Some(ref mut peerstate) = peerstate {
|
||||
peerstate.apply_gossip(header, message_time);
|
||||
peerstate.save_to_db(&context.sql, false);
|
||||
} else {
|
||||
let p = Peerstate::from_gossip(context, header, message_time);
|
||||
p.save_to_db(&context.sql, true);
|
||||
peerstate = Some(p);
|
||||
}
|
||||
if let Some(peerstate) = peerstate {
|
||||
if peerstate.degrade_event.is_some() {
|
||||
handle_degrade_event(context, &peerstate);
|
||||
}
|
||||
}
|
||||
|
||||
gossipped_addr.insert(header.addr.clone());
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
"Ignoring gossipped \"{}\" as the address is not in To/Cc list.",
|
||||
&header.addr,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gossipped_addr
|
||||
}
|
||||
|
||||
unsafe fn decrypt_recursive(
|
||||
context: &Context,
|
||||
mime: *mut Mailmime,
|
||||
private_keyring: &Keyring,
|
||||
public_keyring_for_validate: &Keyring,
|
||||
ret_valid_signatures: &mut HashSet<String>,
|
||||
ret_gossip_headers: *mut *mut mailimf_fields,
|
||||
ret_has_unencrypted_parts: *mut libc::c_int,
|
||||
) -> Result<()> {
|
||||
ensure!(!mime.is_null(), "Invalid mime reference");
|
||||
let ct: *mut mailmime_content;
|
||||
|
||||
if (*mime).mm_type == MAILMIME_MULTIPLE as libc::c_int {
|
||||
ct = (*mime).mm_content_type;
|
||||
if !ct.is_null()
|
||||
&& !(*ct).ct_subtype.is_null()
|
||||
&& strcmp(
|
||||
(*ct).ct_subtype,
|
||||
b"encrypted\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0i32
|
||||
{
|
||||
for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() {
|
||||
let mut decrypted_mime: *mut Mailmime = ptr::null_mut();
|
||||
let decrypted = match decrypt_part(
|
||||
context,
|
||||
cur_data as *mut Mailmime,
|
||||
private_keyring,
|
||||
public_keyring_for_validate,
|
||||
ret_valid_signatures,
|
||||
&mut decrypted_mime,
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(err) => bail!("decrypt_part: {}", err),
|
||||
};
|
||||
if decrypted {
|
||||
if (*ret_gossip_headers).is_null() && ret_valid_signatures.len() > 0 {
|
||||
let mut dummy: libc::size_t = 0;
|
||||
let mut test: *mut mailimf_fields = ptr::null_mut();
|
||||
if mailimf_envelope_and_optional_fields_parse(
|
||||
(*decrypted_mime).mm_mime_start,
|
||||
(*decrypted_mime).mm_length,
|
||||
&mut dummy,
|
||||
&mut test,
|
||||
) == MAILIMF_NO_ERROR as libc::c_int
|
||||
&& !test.is_null()
|
||||
{
|
||||
*ret_gossip_headers = test
|
||||
}
|
||||
}
|
||||
mailmime_substitute(mime, decrypted_mime);
|
||||
mailmime_free(mime);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
*ret_has_unencrypted_parts = 1i32
|
||||
} else {
|
||||
for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() {
|
||||
if decrypt_recursive(
|
||||
context,
|
||||
cur_data as *mut Mailmime,
|
||||
private_keyring,
|
||||
public_keyring_for_validate,
|
||||
ret_valid_signatures,
|
||||
ret_gossip_headers,
|
||||
ret_has_unencrypted_parts,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (*mime).mm_type == MAILMIME_MESSAGE as libc::c_int {
|
||||
if decrypt_recursive(
|
||||
context,
|
||||
(*mime).mm_data.mm_message.mm_msg_mime,
|
||||
private_keyring,
|
||||
public_keyring_for_validate,
|
||||
ret_valid_signatures,
|
||||
ret_gossip_headers,
|
||||
ret_has_unencrypted_parts,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
*ret_has_unencrypted_parts = 1;
|
||||
}
|
||||
|
||||
Err(format_err!("Failed to decrypt"))
|
||||
}
|
||||
|
||||
unsafe fn decrypt_part(
|
||||
_context: &Context,
|
||||
mime: *mut Mailmime,
|
||||
private_keyring: &Keyring,
|
||||
public_keyring_for_validate: &Keyring,
|
||||
ret_valid_signatures: &mut HashSet<String>,
|
||||
ret_decrypted_mime: *mut *mut Mailmime,
|
||||
) -> Result<bool> {
|
||||
let mime_data: *mut mailmime_data;
|
||||
let mut mime_transfer_encoding: libc::c_int = MAILMIME_MECHANISM_BINARY as libc::c_int;
|
||||
let mut sth_decrypted = false;
|
||||
|
||||
let cleanup = |transfer_decoding_buffer: *mut libc::c_char| {
|
||||
if !transfer_decoding_buffer.is_null() {
|
||||
mmap_string_unref(transfer_decoding_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
*ret_decrypted_mime = ptr::null_mut();
|
||||
mime_data = (*mime).mm_data.mm_single;
|
||||
/* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */
|
||||
if (*mime_data).dt_type != MAILMIME_DATA_TEXT as libc::c_int
|
||||
|| (*mime_data).dt_data.dt_text.dt_data.is_null()
|
||||
|| (*mime_data).dt_data.dt_text.dt_length <= 0
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
if !(*mime).mm_mime_fields.is_null() {
|
||||
for cur_data in (*(*(*mime).mm_mime_fields).fld_list).into_iter() {
|
||||
let field: *mut mailmime_field = cur_data as *mut _;
|
||||
if (*field).fld_type == MAILMIME_FIELD_TRANSFER_ENCODING as libc::c_int
|
||||
&& !(*field).fld_data.fld_encoding.is_null()
|
||||
{
|
||||
mime_transfer_encoding = (*(*field).fld_data.fld_encoding).enc_type
|
||||
}
|
||||
}
|
||||
}
|
||||
/* regarding `Content-Transfer-Encoding:` */
|
||||
/* mmap_string_unref()'d if set */
|
||||
let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut();
|
||||
let decoded_data: *const libc::c_char;
|
||||
let mut decoded_data_bytes: libc::size_t = 0;
|
||||
if mime_transfer_encoding == MAILMIME_MECHANISM_7BIT as libc::c_int
|
||||
|| mime_transfer_encoding == MAILMIME_MECHANISM_8BIT as libc::c_int
|
||||
|| mime_transfer_encoding == MAILMIME_MECHANISM_BINARY as libc::c_int
|
||||
{
|
||||
decoded_data = (*mime_data).dt_data.dt_text.dt_data;
|
||||
decoded_data_bytes = (*mime_data).dt_data.dt_text.dt_length;
|
||||
if decoded_data.is_null() || decoded_data_bytes <= 0 {
|
||||
/* no error - but no data */
|
||||
return Ok(false);
|
||||
}
|
||||
} else {
|
||||
let r: libc::c_int;
|
||||
let mut current_index: libc::size_t = 0;
|
||||
r = mailmime_part_parse(
|
||||
(*mime_data).dt_data.dt_text.dt_data,
|
||||
(*mime_data).dt_data.dt_text.dt_length,
|
||||
&mut current_index,
|
||||
mime_transfer_encoding,
|
||||
&mut transfer_decoding_buffer,
|
||||
&mut decoded_data_bytes,
|
||||
);
|
||||
if r != MAILIMF_NO_ERROR as libc::c_int
|
||||
|| transfer_decoding_buffer.is_null()
|
||||
|| decoded_data_bytes <= 0
|
||||
{
|
||||
cleanup(transfer_decoding_buffer);
|
||||
bail!("mailmime_part_parse returned error or invalid data");
|
||||
}
|
||||
decoded_data = transfer_decoding_buffer;
|
||||
}
|
||||
|
||||
/* encrypted, decoded data in decoded_data now ... */
|
||||
if has_decrypted_pgp_armor(decoded_data, decoded_data_bytes as libc::c_int) {
|
||||
let add_signatures = if ret_valid_signatures.is_empty() {
|
||||
Some(ret_valid_signatures)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
/*if we already have fingerprints, do not add more; this ensures, only the fingerprints from the outer-most part are collected */
|
||||
let plain = match dc_pgp_pk_decrypt(
|
||||
std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes),
|
||||
&private_keyring,
|
||||
&public_keyring_for_validate,
|
||||
add_signatures,
|
||||
) {
|
||||
Ok(plain) => plain,
|
||||
Err(err) => {
|
||||
cleanup(transfer_decoding_buffer);
|
||||
bail!("could not decrypt: {}", err)
|
||||
}
|
||||
};
|
||||
let plain_bytes = plain.len();
|
||||
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||
|
||||
let mut index: libc::size_t = 0;
|
||||
let mut decrypted_mime: *mut Mailmime = ptr::null_mut();
|
||||
if mailmime_parse(
|
||||
plain_buf as *const _,
|
||||
plain_bytes,
|
||||
&mut index,
|
||||
&mut decrypted_mime,
|
||||
) != MAIL_NO_ERROR as libc::c_int
|
||||
|| decrypted_mime.is_null()
|
||||
{
|
||||
if !decrypted_mime.is_null() {
|
||||
mailmime_free(decrypted_mime);
|
||||
}
|
||||
} else {
|
||||
*ret_decrypted_mime = decrypted_mime;
|
||||
sth_decrypted = true;
|
||||
}
|
||||
std::mem::forget(plain);
|
||||
}
|
||||
//mailmime_substitute(mime, new_mime);
|
||||
//s. mailprivacy_gnupg.c::pgp_decrypt()
|
||||
cleanup(transfer_decoding_buffer);
|
||||
|
||||
Ok(sth_decrypted)
|
||||
}
|
||||
|
||||
unsafe fn has_decrypted_pgp_armor(str__: *const libc::c_char, mut str_bytes: libc::c_int) -> bool {
|
||||
let str_end: *const libc::c_uchar = (str__ as *const libc::c_uchar).offset(str_bytes as isize);
|
||||
let mut p: *const libc::c_uchar = str__ as *const libc::c_uchar;
|
||||
while p < str_end {
|
||||
if *p as libc::c_int > ' ' as i32 {
|
||||
break;
|
||||
}
|
||||
p = p.offset(1isize);
|
||||
str_bytes -= 1
|
||||
}
|
||||
str_bytes > 27i32
|
||||
&& strncmp(
|
||||
p as *const libc::c_char,
|
||||
b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char,
|
||||
27,
|
||||
) == 0
|
||||
}
|
||||
|
||||
/// Check if a MIME structure contains a multipart/report part.
|
||||
///
|
||||
/// As reports are often unencrypted, we do not reset the Autocrypt header in
|
||||
/// this case.
|
||||
///
|
||||
/// However, Delta Chat itself has no problem with encrypted multipart/report
|
||||
/// parts and MUAs should be encouraged to encrpyt multipart/reports as well so
|
||||
/// that we could use the normal Autocrypt processing.
|
||||
unsafe fn contains_report(mime: *mut Mailmime) -> bool {
|
||||
if (*mime).mm_type == MAILMIME_MULTIPLE as libc::c_int {
|
||||
if (*(*(*mime).mm_content_type).ct_type).tp_type
|
||||
== MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int
|
||||
&& (*(*(*(*mime).mm_content_type).ct_type)
|
||||
.tp_data
|
||||
.tp_composite_type)
|
||||
.ct_type
|
||||
== MAILMIME_COMPOSITE_TYPE_MULTIPART as libc::c_int
|
||||
&& strcmp(
|
||||
(*(*mime).mm_content_type).ct_subtype,
|
||||
b"report\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0i32
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for cur_data in (*(*(*mime).mm_mime_fields).fld_list).into_iter() {
|
||||
if contains_report(cur_data as *mut Mailmime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (*mime).mm_type == MAILMIME_MESSAGE as libc::c_int {
|
||||
if contains_report((*mime).mm_data.mm_message.mm_msg_mime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Ensures a private key exists for the configured user.
|
||||
///
|
||||
/// Normally the private key is generated when the first message is
|
||||
/// sent but in a few locations there are no such guarantees,
|
||||
/// e.g. when exporting keys, and calling this function ensures a
|
||||
/// private key will be present.
|
||||
///
|
||||
/// If this succeeds you are also guaranteed that the
|
||||
/// [Config::ConfiguredAddr] is configured, this address is returned.
|
||||
pub fn ensure_secret_key_exists(context: &Context) -> Result<String> {
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.ok_or(format_err!(concat!(
|
||||
"Failed to get self address, ",
|
||||
"cannot ensure secret key if not configured."
|
||||
)))?;
|
||||
load_or_generate_self_public_key(context, &self_addr)?;
|
||||
Ok(self_addr)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::test_utils::*;
|
||||
|
||||
mod ensure_secret_key_exists {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_prexisting() {
|
||||
let t = dummy_context();
|
||||
let test_addr = configure_alice_keypair(&t.ctx);
|
||||
assert_eq!(ensure_secret_key_exists(&t.ctx).unwrap(), test_addr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_configured() {
|
||||
let t = dummy_context();
|
||||
assert!(ensure_secret_key_exists(&t.ctx).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mailmime_parse() {
|
||||
let plain = b"Chat-Disposition-Notification-To: holger@deltachat.de
|
||||
Chat-Group-ID: CovhGgau8M-
|
||||
Chat-Group-Name: Delta Chat Dev
|
||||
Subject: =?utf-8?Q?Chat=3A?= Delta Chat =?utf-8?Q?Dev=3A?= sidenote for
|
||||
=?utf-8?Q?all=3A?= rust core master ...
|
||||
Content-Type: text/plain; charset=\"utf-8\"; protected-headers=\"v1\"
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
sidenote for all: rust core master is broken currently ... so dont recomm=
|
||||
end to try to run with desktop or ios unless you are ready to hunt bugs
|
||||
|
||||
-- =20
|
||||
Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
let plain_bytes = plain.len();
|
||||
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||
|
||||
let mut index = 0;
|
||||
let mut decrypted_mime = std::ptr::null_mut();
|
||||
|
||||
let res = unsafe {
|
||||
mailmime_parse(
|
||||
plain_buf as *const _,
|
||||
plain_bytes,
|
||||
&mut index,
|
||||
&mut decrypted_mime,
|
||||
)
|
||||
};
|
||||
unsafe {
|
||||
let msg1 = (*decrypted_mime).mm_data.mm_message.mm_msg_mime;
|
||||
let data = mailmime_transfer_decode(msg1).unwrap();
|
||||
println!("{:?}", String::from_utf8_lossy(&data));
|
||||
}
|
||||
|
||||
assert_eq!(res, 0);
|
||||
assert!(!decrypted_mime.is_null());
|
||||
|
||||
unsafe { free(decrypted_mime as *mut _) };
|
||||
}
|
||||
|
||||
mod load_or_generate_self_public_key {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_existing() {
|
||||
let t = dummy_context();
|
||||
let addr = configure_alice_keypair(&t.ctx);
|
||||
let key = load_or_generate_self_public_key(&t.ctx, addr);
|
||||
assert!(key.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // generating keys is expensive
|
||||
fn test_generate() {
|
||||
let t = dummy_context();
|
||||
let addr = "alice@example.org";
|
||||
let key0 = load_or_generate_self_public_key(&t.ctx, addr);
|
||||
assert!(key0.is_ok());
|
||||
let key1 = load_or_generate_self_public_key(&t.ctx, addr);
|
||||
assert!(key1.is_ok());
|
||||
assert_eq!(key0.unwrap(), key1.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_generate_concurrent() {
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
let t = dummy_context();
|
||||
let ctx = Arc::new(t.ctx);
|
||||
let ctx0 = Arc::clone(&ctx);
|
||||
let thr0 =
|
||||
thread::spawn(move || load_or_generate_self_public_key(&ctx0, "alice@example.org"));
|
||||
let ctx1 = Arc::clone(&ctx);
|
||||
let thr1 =
|
||||
thread::spawn(move || load_or_generate_self_public_key(&ctx1, "alice@example.org"));
|
||||
let res0 = thr0.join().unwrap();
|
||||
let res1 = thr1.join().unwrap();
|
||||
assert_eq!(res0.unwrap(), res1.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/error.rs
16
src/error.rs
@@ -22,6 +22,10 @@ pub enum Error {
|
||||
Image(image_meta::ImageError),
|
||||
#[fail(display = "{:?}", _0)]
|
||||
Utf8(std::str::Utf8Error),
|
||||
#[fail(display = "{:?}", _0)]
|
||||
CStringError(crate::dc_tools::CStringError),
|
||||
#[fail(display = "PGP: {:?}", _0)]
|
||||
Pgp(pgp::errors::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -62,6 +66,18 @@ impl From<image_meta::ImageError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::dc_tools::CStringError> for Error {
|
||||
fn from(err: crate::dc_tools::CStringError) -> Error {
|
||||
Error::CStringError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<pgp::errors::Error> for Error {
|
||||
fn from(err: pgp::errors::Error) -> Error {
|
||||
Error::Pgp(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bail {
|
||||
($e:expr) => {
|
||||
|
||||
229
src/events.rs
Normal file
229
src/events.rs
Normal file
@@ -0,0 +1,229 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use strum::EnumProperty;
|
||||
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
impl Event {
|
||||
/// Returns the corresponding Event id.
|
||||
pub fn as_id(&self) -> i32 {
|
||||
self.get_str("id")
|
||||
.expect("missing id")
|
||||
.parse()
|
||||
.expect("invalid id")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, EnumProperty)]
|
||||
pub enum Event {
|
||||
/// The library-user may write an informational string to the log.
|
||||
/// Passed to the callback given to dc_context_new().
|
||||
/// This event should not be reported to the end-user using a popup or something like that.
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "100"))]
|
||||
Info(String),
|
||||
|
||||
/// Emitted when SMTP connection is established and login was successful.
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "101"))]
|
||||
SmtpConnected(String),
|
||||
|
||||
/// Emitted when IMAP connection is established and login was successful.
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "102"))]
|
||||
ImapConnected(String),
|
||||
|
||||
/// Emitted when a message was successfully sent to the SMTP server.
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "103"))]
|
||||
SmtpMessageSent(String),
|
||||
|
||||
/// The library-user should write a warning string to the log.
|
||||
/// Passed to the callback given to dc_context_new().
|
||||
///
|
||||
/// This event should not be reported to the end-user using a popup or something like that.
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "300"))]
|
||||
Warning(String),
|
||||
|
||||
/// The library-user should report an error to the end-user.
|
||||
/// Passed to the callback given to dc_context_new().
|
||||
///
|
||||
/// As most things are asynchronous, things may go wrong at any time and the user
|
||||
/// should not be disturbed by a dialog or so. Instead, use a bubble or so.
|
||||
///
|
||||
/// However, for ongoing processes (eg. configure())
|
||||
/// or for functions that are expected to fail (eg. dc_continue_key_transfer())
|
||||
/// it might be better to delay showing these events until the function has really
|
||||
/// failed (returned false). It should be sufficient to report only the _last_ error
|
||||
/// in a messasge box then.
|
||||
///
|
||||
/// @return
|
||||
#[strum(props(id = "400"))]
|
||||
Error(String),
|
||||
|
||||
/// An action cannot be performed because there is no network available.
|
||||
///
|
||||
/// The library will typically try over after a some time
|
||||
/// and when dc_maybe_network() is called.
|
||||
///
|
||||
/// Network errors should be reported to users in a non-disturbing way,
|
||||
/// however, as network errors may come in a sequence,
|
||||
/// it is not useful to raise each an every error to the user.
|
||||
/// For this purpose, data1 is set to 1 if the error is probably worth reporting.
|
||||
///
|
||||
/// Moreover, if the UI detects that the device is offline,
|
||||
/// it is probably more useful to report this to the user
|
||||
/// instead of the string from data2.
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "401"))]
|
||||
ErrorNetwork(String),
|
||||
|
||||
/// An action cannot be performed because the user is not in the group.
|
||||
/// Reported eg. after a call to
|
||||
/// dc_set_chat_name(), dc_set_chat_profile_image(),
|
||||
/// dc_add_contact_to_chat(), dc_remove_contact_from_chat(),
|
||||
/// dc_send_text_msg() or another sending function.
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "410"))]
|
||||
ErrorSelfNotInGroup(String),
|
||||
|
||||
/// Messages or chats changed. One or more messages or chats changed for various
|
||||
/// reasons in the database:
|
||||
/// - Messages sent, received or removed
|
||||
/// - Chats created, deleted or archived
|
||||
/// - A draft has been set
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "2000"))]
|
||||
MsgsChanged { chat_id: u32, msg_id: u32 },
|
||||
|
||||
/// There is a fresh message. Typically, the user will show an notification
|
||||
/// when receiving this message.
|
||||
///
|
||||
/// There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event.
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "2005"))]
|
||||
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: 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: 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: u32 },
|
||||
|
||||
/// Chat changed. The name or the image of a chat group was changed or members were added or removed.
|
||||
/// Or the verify state of a chat has changed.
|
||||
/// See dc_set_chat_name(), dc_set_chat_profile_image(), dc_add_contact_to_chat()
|
||||
/// and dc_remove_contact_from_chat().
|
||||
///
|
||||
/// @return 0
|
||||
#[strum(props(id = "2020"))]
|
||||
ChatModified(u32),
|
||||
|
||||
/// Contact(s) created, renamed, blocked or deleted.
|
||||
///
|
||||
/// @param data1 (int) If set, this is the contact_id of an added contact that should be selected.
|
||||
/// @return 0
|
||||
#[strum(props(id = "2030"))]
|
||||
ContactsChanged(Option<u32>),
|
||||
|
||||
/// Location of one or more contact has changed.
|
||||
///
|
||||
/// @param data1 (u32) contact_id of the contact for which the location has changed.
|
||||
/// If the locations of several contacts have been changed,
|
||||
/// eg. after calling dc_delete_all_locations(), this parameter is set to `None`.
|
||||
/// @return 0
|
||||
#[strum(props(id = "2035"))]
|
||||
LocationChanged(Option<u32>),
|
||||
|
||||
/// Inform about the configuration progress started by configure().
|
||||
///
|
||||
/// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done
|
||||
/// @return 0
|
||||
#[strum(props(id = "2041"))]
|
||||
ConfigureProgress(usize),
|
||||
|
||||
/// 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
|
||||
/// @return 0
|
||||
#[strum(props(id = "2051"))]
|
||||
ImexProgress(usize),
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// @param data2 0
|
||||
/// @return 0
|
||||
#[strum(props(id = "2052"))]
|
||||
ImexFileWritten(PathBuf),
|
||||
|
||||
/// Progress information of a secure-join handshake from the view of the inviter
|
||||
/// (Alice, the person who shows the QR code).
|
||||
///
|
||||
/// These events are typically sent after a joiner has scanned the QR code
|
||||
/// generated by dc_get_securejoin_qr().
|
||||
///
|
||||
/// @param data1 (int) ID of the contact that wants to join.
|
||||
/// @param data2 (int) Progress as:
|
||||
/// 300=vg-/vc-request received, typically shown as "bob@addr joins".
|
||||
/// 600=vg-/vc-request-with-auth received, vg-member-added/vc-contact-confirm sent, typically shown as "bob@addr verified".
|
||||
/// 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol.
|
||||
/// 1000=Protocol finished for this contact.
|
||||
/// @return 0
|
||||
#[strum(props(id = "2060"))]
|
||||
SecurejoinInviterProgress { contact_id: u32, progress: usize },
|
||||
|
||||
/// Progress information of a secure-join handshake from the view of the joiner
|
||||
/// (Bob, the person who scans the QR code).
|
||||
/// The events are typically sent while dc_join_securejoin(), which
|
||||
/// may take some time, is executed.
|
||||
/// @param data1 (int) ID of the inviting contact.
|
||||
/// @param data2 (int) Progress as:
|
||||
/// 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself."
|
||||
/// (Bob has verified alice and waits until Alice does the same for him)
|
||||
/// @return 0
|
||||
#[strum(props(id = "2061"))]
|
||||
SecurejoinJoinerProgress { contact_id: u32, progress: usize },
|
||||
|
||||
// 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 },
|
||||
}
|
||||
305
src/imap.rs
305
src/imap.rs
@@ -1,4 +1,3 @@
|
||||
use std::ffi::CString;
|
||||
use std::net;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
@@ -8,32 +7,33 @@ use std::time::{Duration, SystemTime};
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_tools::CStringExt;
|
||||
use crate::dc_receive_imf::dc_receive_imf;
|
||||
use crate::events::Event;
|
||||
use crate::job::{job_add, Action};
|
||||
use crate::login_param::LoginParam;
|
||||
use crate::message::{self, update_msg_move_state, update_server_uid};
|
||||
use crate::oauth2::dc_get_oauth2_access_token;
|
||||
use crate::types::*;
|
||||
use crate::param::Params;
|
||||
|
||||
const DC_IMAP_SEEN: usize = 0x0001;
|
||||
const DC_REGENERATE: usize = 0x01;
|
||||
|
||||
const DC_SUCCESS: usize = 3;
|
||||
const DC_ALREADY_DONE: usize = 2;
|
||||
const DC_RETRY_LATER: usize = 1;
|
||||
const DC_FAILED: usize = 0;
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ImapResult {
|
||||
Failed,
|
||||
RetryLater,
|
||||
AlreadyDone,
|
||||
Success,
|
||||
}
|
||||
|
||||
const PREFETCH_FLAGS: &str = "(UID ENVELOPE)";
|
||||
const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])";
|
||||
const FETCH_FLAGS: &str = "(FLAGS)";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Imap {
|
||||
config: Arc<RwLock<ImapConfig>>,
|
||||
watch: Arc<(Mutex<bool>, Condvar)>,
|
||||
|
||||
get_config: dc_get_config_t,
|
||||
set_config: dc_set_config_t,
|
||||
precheck_imf: dc_precheck_imf_t,
|
||||
receive_imf: dc_receive_imf_t,
|
||||
|
||||
session: Arc<Mutex<Option<Session>>>,
|
||||
stream: Arc<RwLock<Option<net::TcpStream>>>,
|
||||
connected: Arc<Mutex<bool>>,
|
||||
@@ -41,6 +41,7 @@ pub struct Imap {
|
||||
should_reconnect: AtomicBool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct OAuth2 {
|
||||
user: String,
|
||||
access_token: String,
|
||||
@@ -65,6 +66,7 @@ enum FolderMeaning {
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Client {
|
||||
Secure(
|
||||
imap::Client<native_tls::TlsStream<net::TcpStream>>,
|
||||
@@ -73,11 +75,13 @@ enum Client {
|
||||
Insecure(imap::Client<net::TcpStream>, net::TcpStream),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Session {
|
||||
Secure(imap::Session<native_tls::TlsStream<net::TcpStream>>),
|
||||
Insecure(imap::Session<net::TcpStream>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum IdleHandle<'a> {
|
||||
Secure(imap::extensions::idle::Handle<'a, native_tls::TlsStream<net::TcpStream>>),
|
||||
Insecure(imap::extensions::idle::Handle<'a, net::TcpStream>),
|
||||
@@ -106,6 +110,8 @@ impl Client {
|
||||
) -> imap::error::Result<Self> {
|
||||
let stream = net::TcpStream::connect(addr)?;
|
||||
let tls = native_tls::TlsConnector::builder()
|
||||
// see also: https://github.com/deltachat/deltachat-core-rust/issues/203
|
||||
.danger_accept_invalid_certs(true)
|
||||
.danger_accept_invalid_hostnames(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
@@ -307,6 +313,7 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ImapConfig {
|
||||
pub addr: String,
|
||||
pub imap_server: String,
|
||||
@@ -346,21 +353,12 @@ impl Default for ImapConfig {
|
||||
}
|
||||
|
||||
impl Imap {
|
||||
pub fn new(
|
||||
get_config: dc_get_config_t,
|
||||
set_config: dc_set_config_t,
|
||||
precheck_imf: dc_precheck_imf_t,
|
||||
receive_imf: dc_receive_imf_t,
|
||||
) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Imap {
|
||||
session: Arc::new(Mutex::new(None)),
|
||||
stream: Arc::new(RwLock::new(None)),
|
||||
config: Arc::new(RwLock::new(ImapConfig::default())),
|
||||
watch: Arc::new((Mutex::new(false), Condvar::new())),
|
||||
get_config,
|
||||
set_config,
|
||||
precheck_imf,
|
||||
receive_imf,
|
||||
connected: Arc::new(Mutex::new(false)),
|
||||
should_reconnect: AtomicBool::new(false),
|
||||
}
|
||||
@@ -420,9 +418,7 @@ impl Imap {
|
||||
if (server_flags & DC_LP_AUTH_OAUTH2) != 0 {
|
||||
let addr: &str = config.addr.as_ref();
|
||||
|
||||
if let Some(token) =
|
||||
dc_get_oauth2_access_token(context, addr, imap_pw, DC_REGENERATE as usize)
|
||||
{
|
||||
if let Some(token) = dc_get_oauth2_access_token(context, addr, imap_pw, true) {
|
||||
let auth = OAuth2 {
|
||||
user: imap_user.into(),
|
||||
access_token: token,
|
||||
@@ -440,14 +436,12 @@ impl Imap {
|
||||
let imap_server: &str = config.imap_server.as_ref();
|
||||
let imap_port = config.imap_port;
|
||||
|
||||
log_event!(
|
||||
emit_event!(
|
||||
context,
|
||||
Event::ERROR_NETWORK,
|
||||
0,
|
||||
"Could not connect to IMAP-server {}:{}. ({})",
|
||||
imap_server,
|
||||
imap_port,
|
||||
err
|
||||
Event::ErrorNetwork(format!(
|
||||
"Could not connect to IMAP-server {}:{}. ({})",
|
||||
imap_server, imap_port, err
|
||||
))
|
||||
);
|
||||
|
||||
return false;
|
||||
@@ -463,7 +457,10 @@ impl Imap {
|
||||
true
|
||||
}
|
||||
Err((err, _)) => {
|
||||
log_event!(context, Event::ERROR_NETWORK, 0, "Cannot login ({})", err);
|
||||
emit_event!(
|
||||
context,
|
||||
Event::ErrorNetwork(format!("Cannot login ({})", err))
|
||||
);
|
||||
self.unsetup_handle(context);
|
||||
|
||||
false
|
||||
@@ -472,12 +469,9 @@ impl Imap {
|
||||
}
|
||||
|
||||
fn unsetup_handle(&self, context: &Context) {
|
||||
info!(context, 0, "IMAP unsetup_handle starts");
|
||||
info!(context, "IMAP unsetup_handle starts");
|
||||
|
||||
info!(
|
||||
context,
|
||||
0, "IMAP unsetup_handle step 1 (closing down stream)."
|
||||
);
|
||||
info!(context, "IMAP unsetup_handle step 1 (closing down stream).");
|
||||
let stream = self.stream.write().unwrap().take();
|
||||
if let Some(stream) = stream {
|
||||
if let Err(err) = stream.shutdown(net::Shutdown::Both) {
|
||||
@@ -487,7 +481,7 @@ impl Imap {
|
||||
|
||||
info!(
|
||||
context,
|
||||
0, "IMAP unsetup_handle step 2 (acquiring session.lock)"
|
||||
"IMAP unsetup_handle step 2 (acquiring session.lock)"
|
||||
);
|
||||
if let Some(mut session) = self.session.lock().unwrap().take() {
|
||||
if let Err(err) = session.close() {
|
||||
@@ -495,10 +489,10 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
info!(context, 0, "IMAP unsetup_handle step 3 (clearing config).");
|
||||
info!(context, "IMAP unsetup_handle step 3 (clearing config).");
|
||||
self.config.write().unwrap().selected_folder = None;
|
||||
self.config.write().unwrap().selected_mailbox = None;
|
||||
info!(context, 0, "IMAP unsetup_handle step 4 (disconnected).",);
|
||||
info!(context, "IMAP unsetup_handle step 4 (disconnected).",);
|
||||
}
|
||||
|
||||
fn free_connect_params(&self) {
|
||||
@@ -516,7 +510,7 @@ impl Imap {
|
||||
cfg.watch_folder = None;
|
||||
}
|
||||
|
||||
pub fn connect(&self, context: &Context, lp: &dc_loginparam_t) -> bool {
|
||||
pub fn connect(&self, context: &Context, lp: &LoginParam) -> bool {
|
||||
if lp.mail_server.is_empty() || lp.mail_user.is_empty() || lp.mail_pw.is_empty() {
|
||||
return false;
|
||||
}
|
||||
@@ -548,33 +542,32 @@ impl Imap {
|
||||
}
|
||||
|
||||
let (teardown, can_idle, has_xlist) = match &mut *self.session.lock().unwrap() {
|
||||
Some(ref mut session) => {
|
||||
if let Ok(caps) = session.capabilities() {
|
||||
Some(ref mut session) => match session.capabilities() {
|
||||
Ok(caps) => {
|
||||
if !context.sql.is_open() {
|
||||
warn!(context, 0, "IMAP-LOGIN as {} ok but ABORTING", lp.mail_user,);
|
||||
warn!(context, "IMAP-LOGIN as {} ok but ABORTING", lp.mail_user,);
|
||||
(true, false, false)
|
||||
} else {
|
||||
let can_idle = caps.has("IDLE");
|
||||
let has_xlist = caps.has("XLIST");
|
||||
let caps_list = caps.iter().fold(String::new(), |mut s, c| {
|
||||
s += " ";
|
||||
s += c;
|
||||
s
|
||||
});
|
||||
log_event!(
|
||||
let can_idle = caps.has_str("IDLE");
|
||||
let has_xlist = caps.has_str("XLIST");
|
||||
let caps_list = caps
|
||||
.iter()
|
||||
.fold(String::new(), |s, c| s + &format!(" {:?}", c));
|
||||
emit_event!(
|
||||
context,
|
||||
Event::IMAP_CONNECTED,
|
||||
0,
|
||||
"IMAP-LOGIN as {}, capabilities: {}",
|
||||
lp.mail_user,
|
||||
caps_list,
|
||||
Event::ImapConnected(format!(
|
||||
"IMAP-LOGIN as {}, capabilities: {}",
|
||||
lp.mail_user, caps_list,
|
||||
))
|
||||
);
|
||||
(false, can_idle, has_xlist)
|
||||
}
|
||||
} else {
|
||||
}
|
||||
Err(err) => {
|
||||
info!(context, "CAPABILITY command error: {}", err);
|
||||
(true, false, false)
|
||||
}
|
||||
}
|
||||
},
|
||||
None => (true, false, false),
|
||||
};
|
||||
|
||||
@@ -647,7 +640,7 @@ impl Imap {
|
||||
// deselect existing folder, if needed (it's also done implicitly by SELECT, however, without EXPUNGE then)
|
||||
if self.config.read().unwrap().selected_folder_needs_expunge {
|
||||
if let Some(ref folder) = self.config.read().unwrap().selected_folder {
|
||||
info!(context, 0, "Expunge messages in \"{}\".", folder);
|
||||
info!(context, "Expunge messages in \"{}\".", folder);
|
||||
|
||||
// A CLOSE-SELECT is considerably faster than an EXPUNGE-SELECT, see
|
||||
// https://tools.ietf.org/html/rfc3501#section-6.4.2
|
||||
@@ -677,7 +670,6 @@ impl Imap {
|
||||
Err(err) => {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder: {}; {:?}.",
|
||||
folder.as_ref(),
|
||||
err
|
||||
@@ -698,7 +690,7 @@ impl Imap {
|
||||
|
||||
fn get_config_last_seen_uid<S: AsRef<str>>(&self, context: &Context, folder: S) -> (u32, u32) {
|
||||
let key = format!("imap.mailbox.{}", folder.as_ref());
|
||||
if let Some(entry) = (self.get_config)(context, &key) {
|
||||
if let Some(entry) = context.sql.get_config(context, &key) {
|
||||
// the entry has the format `imap.mailbox.<folder>=<uidvalidity>:<lastseenuid>`
|
||||
let mut parts = entry.split(':');
|
||||
(
|
||||
@@ -714,7 +706,6 @@ impl Imap {
|
||||
if !self.is_connected() {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Cannot fetch from \"{}\" - not connected.",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -725,7 +716,6 @@ impl Imap {
|
||||
if self.select_folder(context, Some(&folder)) == 0 {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder \"{}\" for fetching.",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -742,7 +732,6 @@ impl Imap {
|
||||
if mailbox.uid_validity.is_none() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Cannot get UIDVALIDITY for folder \"{}\".",
|
||||
folder.as_ref(),
|
||||
);
|
||||
@@ -754,7 +743,7 @@ impl Imap {
|
||||
// first time this folder is selected or UIDVALIDITY has changed, init lastseenuid and save it to config
|
||||
|
||||
if mailbox.exists == 0 {
|
||||
info!(context, 0, "Folder \"{}\" is empty.", folder.as_ref());
|
||||
info!(context, "Folder \"{}\" is empty.", folder.as_ref());
|
||||
|
||||
// set lastseenuid=0 for empty folders.
|
||||
// id we do not do this here, we'll miss the first message
|
||||
@@ -773,7 +762,6 @@ impl Imap {
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"No result returned for folder \"{}\".",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -796,7 +784,6 @@ impl Imap {
|
||||
self.set_config_last_seen_uid(context, &folder, uid_validity, last_seen_uid);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"lastseenuid initialized to {} for {}@{}",
|
||||
last_seen_uid,
|
||||
folder.as_ref(),
|
||||
@@ -815,7 +802,7 @@ impl Imap {
|
||||
match session.uid_fetch(set, PREFETCH_FLAGS) {
|
||||
Ok(list) => list,
|
||||
Err(err) => {
|
||||
warn!(context, 0, "failed to fetch uids: {}", err);
|
||||
warn!(context, "failed to fetch uids: {}", err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -835,15 +822,11 @@ impl Imap {
|
||||
.message_id
|
||||
.expect("missing message id");
|
||||
|
||||
if 0 == unsafe {
|
||||
let message_id_c = CString::yolo(message_id);
|
||||
(self.precheck_imf)(context, message_id_c.as_ptr(), folder.as_ref(), cur_uid)
|
||||
} {
|
||||
if !precheck_imf(context, &message_id, folder.as_ref(), cur_uid) {
|
||||
// check passed, go fetch the rest
|
||||
if self.fetch_single_msg(context, &folder, cur_uid) == 0 {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Read error for message {} from \"{}\", trying over later.",
|
||||
message_id,
|
||||
folder.as_ref()
|
||||
@@ -855,7 +838,6 @@ impl Imap {
|
||||
// check failed
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Skipping message {} from \"{}\" by precheck.",
|
||||
message_id,
|
||||
folder.as_ref(),
|
||||
@@ -876,7 +858,6 @@ impl Imap {
|
||||
if read_errors > 0 {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"{} mails read from \"{}\" with {} errors.",
|
||||
read_cnt,
|
||||
folder.as_ref(),
|
||||
@@ -885,7 +866,6 @@ impl Imap {
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{} mails read from \"{}\".",
|
||||
read_cnt,
|
||||
folder.as_ref()
|
||||
@@ -905,7 +885,7 @@ impl Imap {
|
||||
let key = format!("imap.mailbox.{}", folder.as_ref());
|
||||
let val = format!("{}:{}", uidvalidity, lastseenuid);
|
||||
|
||||
(self.set_config)(context, &key, Some(&val));
|
||||
context.sql.set_config(context, &key, Some(&val)).ok();
|
||||
}
|
||||
|
||||
fn fetch_single_msg<S: AsRef<str>>(
|
||||
@@ -930,7 +910,6 @@ impl Imap {
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Error on fetching message #{} from folder \"{}\"; retry={}; error={}.",
|
||||
server_uid,
|
||||
folder.as_ref(),
|
||||
@@ -947,7 +926,6 @@ impl Imap {
|
||||
if msgs.is_empty() {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Message #{} does not exist in folder \"{}\".",
|
||||
server_uid,
|
||||
folder.as_ref()
|
||||
@@ -977,14 +955,7 @@ impl Imap {
|
||||
if !is_deleted && msg.body().is_some() {
|
||||
let body = msg.body().unwrap();
|
||||
unsafe {
|
||||
(self.receive_imf)(
|
||||
context,
|
||||
body.as_ptr() as *const libc::c_char,
|
||||
body.len(),
|
||||
folder.as_ref(),
|
||||
server_uid,
|
||||
flags as u32,
|
||||
);
|
||||
dc_receive_imf(context, &body, folder.as_ref(), server_uid, flags as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1001,7 +972,7 @@ impl Imap {
|
||||
|
||||
let watch_folder = self.config.read().unwrap().watch_folder.clone();
|
||||
if self.select_folder(context, watch_folder.as_ref()) == 0 {
|
||||
warn!(context, 0, "IMAP-IDLE not setup.",);
|
||||
warn!(context, "IMAP-IDLE not setup.",);
|
||||
|
||||
return self.fake_idle(context);
|
||||
}
|
||||
@@ -1011,7 +982,7 @@ impl Imap {
|
||||
let (sender, receiver) = std::sync::mpsc::channel();
|
||||
let v = self.watch.clone();
|
||||
|
||||
info!(context, 0, "IMAP-IDLE SPAWNING");
|
||||
info!(context, "IMAP-IDLE SPAWNING");
|
||||
std::thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*v;
|
||||
if let Some(ref mut session) = &mut *session.lock().unwrap() {
|
||||
@@ -1046,18 +1017,15 @@ impl Imap {
|
||||
|
||||
let handle_res = |res| match res {
|
||||
Ok(()) => {
|
||||
info!(context, 0, "IMAP-IDLE has data.");
|
||||
info!(context, "IMAP-IDLE has data.");
|
||||
}
|
||||
Err(err) => match err {
|
||||
imap::error::Error::ConnectionLost => {
|
||||
info!(
|
||||
context,
|
||||
0, "IMAP-IDLE wait cancelled, we will reconnect soon."
|
||||
);
|
||||
info!(context, "IMAP-IDLE wait cancelled, we will reconnect soon.");
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
}
|
||||
_ => {
|
||||
warn!(context, 0, "IMAP-IDLE returns unknown value: {}", err);
|
||||
warn!(context, "IMAP-IDLE returns unknown value: {}", err);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1073,7 +1041,7 @@ impl Imap {
|
||||
if let Ok(res) = worker.as_ref().unwrap().try_recv() {
|
||||
handle_res(res);
|
||||
} else {
|
||||
info!(context, 0, "IMAP-IDLE interrupted");
|
||||
info!(context, "IMAP-IDLE interrupted");
|
||||
}
|
||||
|
||||
drop(worker.take());
|
||||
@@ -1091,7 +1059,7 @@ impl Imap {
|
||||
let fake_idle_start_time = SystemTime::now();
|
||||
let mut wait_long = false;
|
||||
|
||||
info!(context, 0, "IMAP-fake-IDLEing...");
|
||||
info!(context, "IMAP-fake-IDLEing...");
|
||||
|
||||
let mut do_fake_idle = true;
|
||||
while do_fake_idle {
|
||||
@@ -1158,27 +1126,25 @@ impl Imap {
|
||||
uid: u32,
|
||||
dest_folder: S2,
|
||||
dest_uid: &mut u32,
|
||||
) -> usize {
|
||||
let mut res = DC_RETRY_LATER;
|
||||
) -> ImapResult {
|
||||
let mut res = ImapResult::RetryLater;
|
||||
let set = format!("{}", uid);
|
||||
|
||||
if uid == 0 {
|
||||
res = DC_FAILED;
|
||||
res = ImapResult::Failed;
|
||||
} else if folder.as_ref() == dest_folder.as_ref() {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Skip moving message; message {}/{} is already in {}...",
|
||||
folder.as_ref(),
|
||||
uid,
|
||||
dest_folder.as_ref()
|
||||
);
|
||||
|
||||
res = DC_ALREADY_DONE;
|
||||
res = ImapResult::AlreadyDone;
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Moving message {}/{} to {}...",
|
||||
folder.as_ref(),
|
||||
uid,
|
||||
@@ -1188,7 +1154,6 @@ impl Imap {
|
||||
if self.select_folder(context, Some(folder.as_ref())) == 0 {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder {} for moving message.",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -1196,13 +1161,12 @@ impl Imap {
|
||||
let moved = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.uid_mv(&set, &dest_folder) {
|
||||
Ok(_) => {
|
||||
res = DC_SUCCESS;
|
||||
res = ImapResult::Success;
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Cannot move message, fallback to COPY/DELETE {}/{} to {}: {}",
|
||||
folder.as_ref(),
|
||||
uid,
|
||||
@@ -1223,7 +1187,7 @@ impl Imap {
|
||||
Ok(_) => true,
|
||||
Err(err) => {
|
||||
eprintln!("error copy: {:?}", err);
|
||||
info!(context, 0, "Cannot copy message.",);
|
||||
info!(context, "Cannot copy message.",);
|
||||
|
||||
false
|
||||
}
|
||||
@@ -1234,25 +1198,25 @@ impl Imap {
|
||||
|
||||
if copied {
|
||||
if self.add_flag(context, uid, "\\Deleted") == 0 {
|
||||
warn!(context, 0, "Cannot mark message as \"Deleted\".",);
|
||||
warn!(context, "Cannot mark message as \"Deleted\".",);
|
||||
}
|
||||
self.config.write().unwrap().selected_folder_needs_expunge = true;
|
||||
res = DC_SUCCESS;
|
||||
res = ImapResult::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if res == DC_SUCCESS {
|
||||
if res == ImapResult::Success {
|
||||
// TODO: is this correct?
|
||||
*dest_uid = uid;
|
||||
}
|
||||
|
||||
if res == DC_RETRY_LATER {
|
||||
if res == ImapResult::RetryLater {
|
||||
if self.should_reconnect() {
|
||||
DC_RETRY_LATER
|
||||
ImapResult::RetryLater
|
||||
} else {
|
||||
DC_FAILED
|
||||
ImapResult::Failed
|
||||
}
|
||||
} else {
|
||||
res
|
||||
@@ -1271,7 +1235,7 @@ impl Imap {
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
0, "IMAP failed to store: ({}, {}) {:?}", set, query, err
|
||||
"IMAP failed to store: ({}, {}) {:?}", set, query, err
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1286,15 +1250,14 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_seen<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> usize {
|
||||
let mut res = DC_RETRY_LATER;
|
||||
pub fn set_seen<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> ImapResult {
|
||||
let mut res = ImapResult::RetryLater;
|
||||
|
||||
if uid == 0 {
|
||||
res = DC_FAILED
|
||||
res = ImapResult::Failed
|
||||
} else if self.is_connected() {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Marking message {}/{} as seen...",
|
||||
folder.as_ref(),
|
||||
uid,
|
||||
@@ -1303,39 +1266,37 @@ impl Imap {
|
||||
if self.select_folder(context, Some(folder.as_ref())) == 0 {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder {} for setting SEEN flag.",
|
||||
folder.as_ref(),
|
||||
);
|
||||
} else if self.add_flag(context, uid, "\\Seen") == 0 {
|
||||
warn!(context, 0, "Cannot mark message as seen.",);
|
||||
warn!(context, "Cannot mark message as seen.",);
|
||||
} else {
|
||||
res = DC_SUCCESS
|
||||
res = ImapResult::Success
|
||||
}
|
||||
}
|
||||
|
||||
if res == DC_RETRY_LATER {
|
||||
if res == ImapResult::RetryLater {
|
||||
if self.should_reconnect() {
|
||||
DC_RETRY_LATER
|
||||
ImapResult::RetryLater
|
||||
} else {
|
||||
DC_FAILED
|
||||
ImapResult::Failed
|
||||
}
|
||||
} else {
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_mdnsent<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> usize {
|
||||
pub fn set_mdnsent<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> ImapResult {
|
||||
// returns 0=job should be retried later, 1=job done, 2=job done and flag just set
|
||||
let mut res = DC_RETRY_LATER;
|
||||
let mut res = ImapResult::RetryLater;
|
||||
let set = format!("{}", uid);
|
||||
|
||||
if uid == 0 {
|
||||
res = DC_FAILED;
|
||||
res = ImapResult::Failed;
|
||||
} else if self.is_connected() {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Marking message {}/{} as $MDNSent...",
|
||||
folder.as_ref(),
|
||||
uid,
|
||||
@@ -1344,7 +1305,6 @@ impl Imap {
|
||||
if self.select_folder(context, Some(folder.as_ref())) == 0 {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder {} for setting $MDNSent flag.",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -1403,34 +1363,34 @@ impl Imap {
|
||||
.unwrap_or_else(|| false);
|
||||
|
||||
res = if flag_set {
|
||||
DC_ALREADY_DONE
|
||||
ImapResult::AlreadyDone
|
||||
} else if self.add_flag(context, uid, "$MDNSent") != 0 {
|
||||
DC_SUCCESS
|
||||
ImapResult::Success
|
||||
} else {
|
||||
res
|
||||
};
|
||||
|
||||
if res == DC_SUCCESS {
|
||||
info!(context, 0, "$MDNSent just set and MDN will be sent.");
|
||||
if res == ImapResult::Success {
|
||||
info!(context, "$MDNSent just set and MDN will be sent.");
|
||||
} else {
|
||||
info!(context, 0, "$MDNSent already set and MDN already sent.");
|
||||
info!(context, "$MDNSent already set and MDN already sent.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = DC_SUCCESS;
|
||||
res = ImapResult::Success;
|
||||
info!(
|
||||
context,
|
||||
0, "Cannot store $MDNSent flags, risk sending duplicate MDN.",
|
||||
"Cannot store $MDNSent flags, risk sending duplicate MDN.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if res == DC_RETRY_LATER {
|
||||
if res == ImapResult::RetryLater {
|
||||
if self.should_reconnect() {
|
||||
DC_RETRY_LATER
|
||||
ImapResult::RetryLater
|
||||
} else {
|
||||
DC_FAILED
|
||||
ImapResult::Failed
|
||||
}
|
||||
} else {
|
||||
res
|
||||
@@ -1451,7 +1411,6 @@ impl Imap {
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Marking message \"{}\", {}/{} for deletion...",
|
||||
message_id.as_ref(),
|
||||
folder.as_ref(),
|
||||
@@ -1461,7 +1420,6 @@ impl Imap {
|
||||
if self.select_folder(context, Some(&folder)) == 0 {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder {} for deleting message.",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -1482,7 +1440,6 @@ impl Imap {
|
||||
{
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot delete on IMAP, {}/{} does not match {}.",
|
||||
folder.as_ref(),
|
||||
server_uid,
|
||||
@@ -1496,7 +1453,6 @@ impl Imap {
|
||||
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot delete on IMAP, {}/{} not found.",
|
||||
folder.as_ref(),
|
||||
server_uid,
|
||||
@@ -1508,7 +1464,7 @@ impl Imap {
|
||||
|
||||
// mark the message for deletion
|
||||
if self.add_flag(context, *server_uid, "\\Deleted") == 0 {
|
||||
warn!(context, 0, "Cannot mark message as \"Deleted\".");
|
||||
warn!(context, "Cannot mark message as \"Deleted\".");
|
||||
} else {
|
||||
self.config.write().unwrap().selected_folder_needs_expunge = true;
|
||||
success = true
|
||||
@@ -1528,7 +1484,7 @@ impl Imap {
|
||||
return;
|
||||
}
|
||||
|
||||
info!(context, 0, "Configuring IMAP-folders.");
|
||||
info!(context, "Configuring IMAP-folders.");
|
||||
|
||||
let folders = self.list_folders(context).unwrap();
|
||||
let delimiter = self.config.read().unwrap().imap_delimiter;
|
||||
@@ -1547,21 +1503,19 @@ impl Imap {
|
||||
});
|
||||
|
||||
if mvbox_folder.is_none() && 0 != (flags as usize & DC_CREATE_MVBOX) {
|
||||
info!(context, 0, "Creating MVBOX-folder \"DeltaChat\"...",);
|
||||
info!(context, "Creating MVBOX-folder \"DeltaChat\"...",);
|
||||
|
||||
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.create("DeltaChat") {
|
||||
Ok(_) => {
|
||||
mvbox_folder = Some("DeltaChat".into());
|
||||
|
||||
info!(context, 0, "MVBOX-folder created.",);
|
||||
info!(context, "MVBOX-folder created.",);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot create MVBOX-folder, using trying INBOX subfolder. ({})",
|
||||
err
|
||||
"Cannot create MVBOX-folder, using trying INBOX subfolder. ({})", err
|
||||
);
|
||||
|
||||
match session.create(&fallback_folder) {
|
||||
@@ -1569,11 +1523,11 @@ impl Imap {
|
||||
mvbox_folder = Some(fallback_folder);
|
||||
info!(
|
||||
context,
|
||||
0, "MVBOX-folder created as INBOX subfolder. ({})", err
|
||||
"MVBOX-folder created as INBOX subfolder. ({})", err
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, 0, "Cannot create MVBOX-folder. ({})", err);
|
||||
warn!(context, "Cannot create MVBOX-folder. ({})", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1619,13 +1573,13 @@ impl Imap {
|
||||
match session.list(Some(""), Some("*")) {
|
||||
Ok(list) => {
|
||||
if list.is_empty() {
|
||||
warn!(context, 0, "Folder list is empty.",);
|
||||
warn!(context, "Folder list is empty.",);
|
||||
}
|
||||
Some(list)
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("list error: {:?}", err);
|
||||
warn!(context, 0, "Cannot get folder list.",);
|
||||
warn!(context, "Cannot get folder list.",);
|
||||
|
||||
None
|
||||
}
|
||||
@@ -1679,3 +1633,38 @@ fn get_folder_meaning(folder_name: &imap::types::Name) -> FolderMeaning {
|
||||
_ => res,
|
||||
}
|
||||
}
|
||||
|
||||
fn precheck_imf(context: &Context, rfc724_mid: &str, server_folder: &str, server_uid: u32) -> bool {
|
||||
let mut rfc724_mid_exists = false;
|
||||
let mut mark_seen = false;
|
||||
|
||||
if let Ok((old_server_folder, old_server_uid, msg_id)) =
|
||||
message::rfc724_mid_exists(context, &rfc724_mid)
|
||||
{
|
||||
rfc724_mid_exists = true;
|
||||
|
||||
if old_server_folder.is_empty() && old_server_uid == 0 {
|
||||
info!(context, "[move] detected bbc-self {}", rfc724_mid,);
|
||||
mark_seen = true;
|
||||
} else if old_server_folder != server_folder {
|
||||
info!(context, "[move] detected moved message {}", rfc724_mid,);
|
||||
update_msg_move_state(context, &rfc724_mid, MoveState::Stay);
|
||||
}
|
||||
if old_server_folder != server_folder || old_server_uid != server_uid {
|
||||
update_server_uid(context, &rfc724_mid, server_folder, server_uid);
|
||||
}
|
||||
context.do_heuristics_moves(server_folder, msg_id);
|
||||
|
||||
if mark_seen {
|
||||
job_add(
|
||||
context,
|
||||
Action::MarkseenMsgOnImap,
|
||||
msg_id as libc::c_int,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
rfc724_mid_exists
|
||||
}
|
||||
|
||||
540
src/job.rs
540
src/job.rs
@@ -1,5 +1,3 @@
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
use std::time::Duration;
|
||||
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
@@ -10,28 +8,36 @@ use crate::configure::*;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_imex::*;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_mimefactory::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::events::Event;
|
||||
use crate::imap::*;
|
||||
use crate::location;
|
||||
use crate::message::*;
|
||||
use crate::login_param::LoginParam;
|
||||
use crate::message::{self, Message, MessageState};
|
||||
use crate::mimefactory::{vec_contains_lowercase, Loaded, MimeFactory};
|
||||
use crate::param::*;
|
||||
use crate::sql;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
/// Thread IDs
|
||||
#[derive(Debug, Display, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
|
||||
#[repr(i32)]
|
||||
enum Thread {
|
||||
Unknown = 0,
|
||||
Imap = 100,
|
||||
Smtp = 5000,
|
||||
}
|
||||
|
||||
impl Default for Thread {
|
||||
fn default() -> Self {
|
||||
Thread::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
|
||||
#[repr(i32)]
|
||||
pub enum Action {
|
||||
Unknown = 0,
|
||||
|
||||
// Jobs in the INBOX-thread, range from DC_IMAP_THREAD..DC_IMAP_THREAD+999
|
||||
Housekeeping = 105, // low priority ...
|
||||
DeleteMsgOnImap = 110,
|
||||
@@ -50,11 +56,19 @@ pub enum Action {
|
||||
SendMsgToSmtp = 5901, // ... high priority
|
||||
}
|
||||
|
||||
impl Default for Action {
|
||||
fn default() -> Self {
|
||||
Action::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Action> for Thread {
|
||||
fn from(action: Action) -> Thread {
|
||||
use Action::*;
|
||||
|
||||
match action {
|
||||
Unknown => Thread::Unknown,
|
||||
|
||||
Housekeeping => Thread::Imap,
|
||||
DeleteMsgOnImap => Thread::Imap,
|
||||
MarkseenMdnOnImap => Thread::Imap,
|
||||
@@ -111,31 +125,19 @@ impl Job {
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn do_DC_JOB_SEND(&mut self, context: &Context) {
|
||||
let ok_to_continue;
|
||||
let mut filename = ptr::null_mut();
|
||||
let mut buf = ptr::null_mut();
|
||||
let mut buf_bytes = 0;
|
||||
|
||||
/* connect to SMTP server, if not yet done */
|
||||
if !context.smtp.lock().unwrap().is_connected() {
|
||||
let loginparam = dc_loginparam_read(context, &context.sql, "configured_");
|
||||
let loginparam = LoginParam::from_database(context, "configured_");
|
||||
let connected = context.smtp.lock().unwrap().connect(context, &loginparam);
|
||||
|
||||
if !connected {
|
||||
self.try_again_later(3i32, None);
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
let filename_s = self.param.get(Param::File).unwrap_or_default();
|
||||
filename = unsafe { filename_s.strdup() };
|
||||
if unsafe { strlen(filename) } == 0 {
|
||||
warn!(context, 0, "Missing file name for job {}", self.job_id,);
|
||||
} else if 0 != unsafe { dc_read_file(context, filename, &mut buf, &mut buf_bytes) } {
|
||||
|
||||
if let Some(filename) = self.param.get(Param::File) {
|
||||
if let Ok(body) = dc_read_file(context, filename) {
|
||||
if let Some(recipients) = self.param.get(Param::Recipients) {
|
||||
let recipients_list = recipients
|
||||
.split("\x1e")
|
||||
@@ -147,129 +149,106 @@ impl Job {
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
/* if there is a msg-id and it does not exist in the db, cancel sending.
|
||||
this happends if dc_delete_msgs() was called
|
||||
before the generated mime was sent out */
|
||||
let ok_to_continue1;
|
||||
if 0 != self.foreign_id {
|
||||
if 0 == unsafe { dc_msg_exists(context, self.foreign_id) } {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Message {} for job {} does not exist",
|
||||
self.foreign_id,
|
||||
self.job_id,
|
||||
);
|
||||
ok_to_continue1 = false;
|
||||
} else {
|
||||
ok_to_continue1 = true;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue1 = true;
|
||||
}
|
||||
if ok_to_continue1 {
|
||||
/* send message */
|
||||
let body = unsafe {
|
||||
std::slice::from_raw_parts(buf as *const u8, buf_bytes).to_vec()
|
||||
};
|
||||
if 0 != self.foreign_id && !message::exists(context, self.foreign_id) {
|
||||
warn!(
|
||||
context,
|
||||
"Message {} for job {} does not exist", self.foreign_id, self.job_id,
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
// hold the smtp lock during sending of a job and
|
||||
// its ok/error response processing. Note that if a message
|
||||
// was sent we need to mark it in the database as we
|
||||
// otherwise might send it twice.
|
||||
let mut sock = context.smtp.lock().unwrap();
|
||||
if 0 == sock.send(context, recipients_list, body) {
|
||||
sock.disconnect();
|
||||
self.try_again_later(-1i32, Some(as_str(sock.error)));
|
||||
} else {
|
||||
dc_delete_file(context, filename_s);
|
||||
if 0 != self.foreign_id {
|
||||
dc_update_msg_state(
|
||||
// hold the smtp lock during sending of a job and
|
||||
// its ok/error response processing. Note that if a message
|
||||
// was sent we need to mark it in the database as we
|
||||
// otherwise might send it twice.
|
||||
let mut sock = context.smtp.lock().unwrap();
|
||||
if 0 == sock.send(context, recipients_list, body) {
|
||||
sock.disconnect();
|
||||
self.try_again_later(-1i32, sock.error.clone());
|
||||
} else {
|
||||
dc_delete_file(context, filename);
|
||||
if 0 != self.foreign_id {
|
||||
message::update_msg_state(
|
||||
context,
|
||||
self.foreign_id,
|
||||
MessageState::OutDelivered,
|
||||
);
|
||||
let chat_id: i32 = context
|
||||
.sql
|
||||
.query_get_value(
|
||||
context,
|
||||
self.foreign_id,
|
||||
MessageState::OutDelivered,
|
||||
);
|
||||
let chat_id: i32 = context
|
||||
.sql
|
||||
.query_row_col(
|
||||
context,
|
||||
"SELECT chat_id FROM msgs WHERE id=?",
|
||||
params![self.foreign_id as i32],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
context.call_cb(
|
||||
Event::MSG_DELIVERED,
|
||||
chat_id as uintptr_t,
|
||||
self.foreign_id as uintptr_t,
|
||||
);
|
||||
}
|
||||
"SELECT chat_id FROM msgs WHERE id=?",
|
||||
params![self.foreign_id as i32],
|
||||
)
|
||||
.unwrap_or_default();
|
||||
context.call_cb(Event::MsgDelivered {
|
||||
chat_id: chat_id as u32,
|
||||
msg_id: self.foreign_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn!(context, 0, "Missing recipients for job {}", self.job_id,);
|
||||
warn!(context, "Missing recipients for job {}", self.job_id,);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe { free(buf) };
|
||||
unsafe { free(filename.cast()) };
|
||||
}
|
||||
|
||||
// this value does not increase the number of tries
|
||||
fn try_again_later(&mut self, try_again: libc::c_int, pending_error: Option<&str>) {
|
||||
fn try_again_later(&mut self, try_again: libc::c_int, pending_error: Option<String>) {
|
||||
self.try_again = try_again;
|
||||
self.pending_error = pending_error.map(|s| s.to_string());
|
||||
self.pending_error = pending_error;
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn do_DC_JOB_MOVE_MSG(&mut self, context: &Context) {
|
||||
let ok_to_continue;
|
||||
let mut dest_uid = 0;
|
||||
|
||||
let inbox = context.inbox.read().unwrap();
|
||||
|
||||
if !inbox.is_connected() {
|
||||
connect_to_inbox(context, &inbox);
|
||||
if !inbox.is_connected() {
|
||||
self.try_again_later(3, None);
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
if let Ok(msg) = dc_msg_load_from_db(context, self.foreign_id) {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "folders_configured")
|
||||
.unwrap_or_default()
|
||||
< 3
|
||||
{
|
||||
inbox.configure_folders(context, 0x1i32);
|
||||
}
|
||||
let dest_folder = context.sql.get_config(context, "configured_mvbox_folder");
|
||||
if let Ok(msg) = Message::load_from_db(context, self.foreign_id) {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "folders_configured")
|
||||
.unwrap_or_default()
|
||||
< 3
|
||||
{
|
||||
inbox.configure_folders(context, 0x1i32);
|
||||
}
|
||||
let dest_folder = context.sql.get_config(context, "configured_mvbox_folder");
|
||||
|
||||
if let Some(dest_folder) = dest_folder {
|
||||
let server_folder = msg.server_folder.as_ref().unwrap();
|
||||
if let Some(dest_folder) = dest_folder {
|
||||
let server_folder = msg.server_folder.as_ref().unwrap();
|
||||
let mut dest_uid = 0;
|
||||
|
||||
match inbox.mv(
|
||||
context,
|
||||
server_folder,
|
||||
msg.server_uid,
|
||||
&dest_folder,
|
||||
&mut dest_uid,
|
||||
) as libc::c_uint
|
||||
{
|
||||
1 => {
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
3 => {
|
||||
dc_update_server_uid(context, msg.rfc724_mid, &dest_folder, dest_uid);
|
||||
}
|
||||
0 | 2 | _ => {}
|
||||
match inbox.mv(
|
||||
context,
|
||||
server_folder,
|
||||
msg.server_uid,
|
||||
&dest_folder,
|
||||
&mut dest_uid,
|
||||
) {
|
||||
ImapResult::RetryLater => {
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
ImapResult::Success => {
|
||||
message::update_server_uid(
|
||||
context,
|
||||
&msg.rfc724_mid,
|
||||
&dest_folder,
|
||||
dest_uid,
|
||||
);
|
||||
}
|
||||
ImapResult::Failed | ImapResult::AlreadyDone => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,101 +256,75 @@ impl Job {
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn do_DC_JOB_DELETE_MSG_ON_IMAP(&mut self, context: &Context) {
|
||||
let mut delete_from_server = 1;
|
||||
let inbox = context.inbox.read().unwrap();
|
||||
|
||||
if let Ok(mut msg) = dc_msg_load_from_db(context, self.foreign_id) {
|
||||
if !(msg.rfc724_mid.is_null()
|
||||
|| unsafe { *msg.rfc724_mid.offset(0isize) as libc::c_int == 0 })
|
||||
{
|
||||
let ok_to_continue1;
|
||||
if let Ok(mut msg) = Message::load_from_db(context, self.foreign_id) {
|
||||
if !msg.rfc724_mid.is_empty() {
|
||||
/* eg. device messages have no Message-ID */
|
||||
if dc_rfc724_mid_cnt(context, msg.rfc724_mid) != 1 {
|
||||
let mut delete_from_server = true;
|
||||
if message::rfc724_mid_cnt(context, &msg.rfc724_mid) != 1 {
|
||||
info!(
|
||||
context,
|
||||
0, "The message is deleted from the server when all parts are deleted.",
|
||||
"The message is deleted from the server when all parts are deleted.",
|
||||
);
|
||||
delete_from_server = 0i32
|
||||
delete_from_server = false;
|
||||
}
|
||||
/* if this is the last existing part of the message, we delete the message from the server */
|
||||
if 0 != delete_from_server {
|
||||
let ok_to_continue;
|
||||
if delete_from_server {
|
||||
if !inbox.is_connected() {
|
||||
connect_to_inbox(context, &inbox);
|
||||
if !inbox.is_connected() {
|
||||
self.try_again_later(3i32, None);
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
let mid = unsafe { CStr::from_ptr(msg.rfc724_mid).to_str().unwrap() };
|
||||
let server_folder = msg.server_folder.as_ref().unwrap();
|
||||
if 0 == inbox.delete_msg(context, mid, server_folder, &mut msg.server_uid) {
|
||||
self.try_again_later(-1i32, None);
|
||||
ok_to_continue1 = false;
|
||||
} else {
|
||||
ok_to_continue1 = true;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue1 = false;
|
||||
let mid = msg.rfc724_mid;
|
||||
let server_folder = msg.server_folder.as_ref().unwrap();
|
||||
if 0 == inbox.delete_msg(context, &mid, server_folder, &mut msg.server_uid) {
|
||||
self.try_again_later(-1i32, None);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue1 = true;
|
||||
}
|
||||
if ok_to_continue1 {
|
||||
dc_delete_msg_from_db(context, msg.id);
|
||||
}
|
||||
Message::delete_from_db(context, msg.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn do_DC_JOB_MARKSEEN_MSG_ON_IMAP(&mut self, context: &Context) {
|
||||
let ok_to_continue;
|
||||
let inbox = context.inbox.read().unwrap();
|
||||
|
||||
if !inbox.is_connected() {
|
||||
connect_to_inbox(context, &inbox);
|
||||
if !inbox.is_connected() {
|
||||
self.try_again_later(3i32, None);
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
if let Ok(msg) = dc_msg_load_from_db(context, self.foreign_id) {
|
||||
let server_folder = msg.server_folder.as_ref().unwrap();
|
||||
match inbox.set_seen(context, server_folder, msg.server_uid) as libc::c_uint {
|
||||
0 => {}
|
||||
1 => {
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
_ => {
|
||||
if 0 != msg.param.get_int(Param::WantsMdn).unwrap_or_default()
|
||||
&& 0 != context
|
||||
.sql
|
||||
.get_config_int(context, "mdns_enabled")
|
||||
.unwrap_or_else(|| 1)
|
||||
{
|
||||
let folder = msg.server_folder.as_ref().unwrap();
|
||||
if let Ok(msg) = Message::load_from_db(context, self.foreign_id) {
|
||||
let server_folder = msg.server_folder.as_ref().unwrap();
|
||||
match inbox.set_seen(context, server_folder, msg.server_uid) {
|
||||
ImapResult::Failed => {}
|
||||
ImapResult::RetryLater => {
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
_ => {
|
||||
if 0 != msg.param.get_int(Param::WantsMdn).unwrap_or_default()
|
||||
&& 0 != context
|
||||
.sql
|
||||
.get_config_int(context, "mdns_enabled")
|
||||
.unwrap_or_else(|| 1)
|
||||
{
|
||||
let folder = msg.server_folder.as_ref().unwrap();
|
||||
|
||||
match inbox.set_mdnsent(context, folder, msg.server_uid) as libc::c_uint
|
||||
{
|
||||
1 => {
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
3 => {
|
||||
send_mdn(context, msg.id);
|
||||
}
|
||||
0 | 2 | _ => {}
|
||||
match inbox.set_mdnsent(context, folder, msg.server_uid) {
|
||||
ImapResult::RetryLater => {
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
ImapResult::Success => {
|
||||
send_mdn(context, msg.id);
|
||||
}
|
||||
ImapResult::Failed | ImapResult::AlreadyDone => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -381,47 +334,40 @@ impl Job {
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn do_DC_JOB_MARKSEEN_MDN_ON_IMAP(&mut self, context: &Context) {
|
||||
let ok_to_continue;
|
||||
let folder = self
|
||||
.param
|
||||
.get(Param::ServerFolder)
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
let uid = self.param.get_int(Param::ServerUid).unwrap_or_default() as u32;
|
||||
let mut dest_uid = 0;
|
||||
let inbox = context.inbox.read().unwrap();
|
||||
|
||||
if !inbox.is_connected() {
|
||||
connect_to_inbox(context, &inbox);
|
||||
if !inbox.is_connected() {
|
||||
self.try_again_later(3, None);
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
if inbox.set_seen(context, &folder, uid) == 0 {
|
||||
self.try_again_later(3i32, None);
|
||||
if inbox.set_seen(context, &folder, uid) == ImapResult::Failed {
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
if 0 != self.param.get_int(Param::AlsoMove).unwrap_or_default() {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "folders_configured")
|
||||
.unwrap_or_default()
|
||||
< 3
|
||||
{
|
||||
inbox.configure_folders(context, 0x1i32);
|
||||
}
|
||||
if 0 != self.param.get_int(Param::AlsoMove).unwrap_or_default() {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "folders_configured")
|
||||
.unwrap_or_default()
|
||||
< 3
|
||||
let dest_folder = context.sql.get_config(context, "configured_mvbox_folder");
|
||||
if let Some(dest_folder) = dest_folder {
|
||||
let mut dest_uid = 0;
|
||||
if ImapResult::RetryLater
|
||||
== inbox.mv(context, folder, uid, dest_folder, &mut dest_uid)
|
||||
{
|
||||
inbox.configure_folders(context, 0x1i32);
|
||||
}
|
||||
let dest_folder = context.sql.get_config(context, "configured_mvbox_folder");
|
||||
if let Some(dest_folder) = dest_folder {
|
||||
if 1 == inbox.mv(context, folder, uid, dest_folder, &mut dest_uid)
|
||||
as libc::c_uint
|
||||
{
|
||||
self.try_again_later(3, None);
|
||||
}
|
||||
self.try_again_later(3, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -452,18 +398,17 @@ pub fn perform_imap_fetch(context: &Context) {
|
||||
.unwrap_or_else(|| 1)
|
||||
== 0
|
||||
{
|
||||
info!(context, 0, "INBOX-watch disabled.",);
|
||||
info!(context, "INBOX-watch disabled.",);
|
||||
return;
|
||||
}
|
||||
info!(context, 0, "INBOX-fetch started...",);
|
||||
info!(context, "INBOX-fetch started...",);
|
||||
inbox.fetch(context);
|
||||
if inbox.should_reconnect() {
|
||||
info!(context, 0, "INBOX-fetch aborted, starting over...",);
|
||||
info!(context, "INBOX-fetch aborted, starting over...",);
|
||||
inbox.fetch(context);
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"INBOX-fetch done in {:.4} ms.",
|
||||
start.elapsed().as_nanos() as f64 / 1000.0,
|
||||
);
|
||||
@@ -477,13 +422,13 @@ pub fn perform_imap_idle(context: &Context) {
|
||||
if *context.perform_inbox_jobs_needed.clone().read().unwrap() {
|
||||
info!(
|
||||
context,
|
||||
0, "INBOX-IDLE will not be started because of waiting jobs."
|
||||
"INBOX-IDLE will not be started because of waiting jobs."
|
||||
);
|
||||
return;
|
||||
}
|
||||
info!(context, 0, "INBOX-IDLE started...");
|
||||
info!(context, "INBOX-IDLE started...");
|
||||
inbox.idle(context);
|
||||
info!(context, 0, "INBOX-IDLE ended.");
|
||||
info!(context, "INBOX-IDLE ended.");
|
||||
}
|
||||
|
||||
pub fn perform_mvbox_fetch(context: &Context) {
|
||||
@@ -560,16 +505,16 @@ pub fn perform_smtp_jobs(context: &Context) {
|
||||
state.perform_jobs_needed = 0;
|
||||
|
||||
if state.suspended {
|
||||
info!(context, 0, "SMTP-jobs suspended.",);
|
||||
info!(context, "SMTP-jobs suspended.",);
|
||||
return;
|
||||
}
|
||||
state.doing_jobs = true;
|
||||
probe_smtp_network
|
||||
};
|
||||
|
||||
info!(context, 0, "SMTP-jobs started...",);
|
||||
info!(context, "SMTP-jobs started...",);
|
||||
job_perform(context, Thread::Smtp, probe_smtp_network);
|
||||
info!(context, 0, "SMTP-jobs ended.");
|
||||
info!(context, "SMTP-jobs ended.");
|
||||
|
||||
{
|
||||
let &(ref lock, _) = &*context.smtp_state.clone();
|
||||
@@ -580,7 +525,7 @@ pub fn perform_smtp_jobs(context: &Context) {
|
||||
}
|
||||
|
||||
pub fn perform_smtp_idle(context: &Context) {
|
||||
info!(context, 0, "SMTP-idle started...",);
|
||||
info!(context, "SMTP-idle started...",);
|
||||
{
|
||||
let &(ref lock, ref cvar) = &*context.smtp_state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
@@ -588,7 +533,7 @@ pub fn perform_smtp_idle(context: &Context) {
|
||||
if state.perform_jobs_needed == 1 {
|
||||
info!(
|
||||
context,
|
||||
0, "SMTP-idle will not be started because of waiting jobs.",
|
||||
"SMTP-idle will not be started because of waiting jobs.",
|
||||
);
|
||||
} else {
|
||||
let dur = get_next_wakeup_time(context, Thread::Smtp);
|
||||
@@ -606,17 +551,16 @@ pub fn perform_smtp_idle(context: &Context) {
|
||||
}
|
||||
}
|
||||
|
||||
info!(context, 0, "SMTP-idle ended.",);
|
||||
info!(context, "SMTP-idle ended.",);
|
||||
}
|
||||
|
||||
fn get_next_wakeup_time(context: &Context, thread: Thread) -> Duration {
|
||||
let t: i64 = context
|
||||
.sql
|
||||
.query_row_col(
|
||||
.query_get_value(
|
||||
context,
|
||||
"SELECT MIN(desired_timestamp) FROM jobs WHERE thread=?;",
|
||||
params![thread],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -657,15 +601,15 @@ pub fn job_action_exists(context: &Context, action: Action) -> bool {
|
||||
|
||||
/* special case for DC_JOB_SEND_MSG_TO_SMTP */
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
pub fn job_send_msg(context: &Context, msg_id: u32) -> libc::c_int {
|
||||
let mut success = 0;
|
||||
|
||||
/* load message data */
|
||||
let mimefactory = dc_mimefactory_load_msg(context, msg_id);
|
||||
if mimefactory.is_err() || mimefactory.as_ref().unwrap().from_addr.is_null() {
|
||||
let mimefactory = MimeFactory::load_msg(context, msg_id);
|
||||
if mimefactory.is_err() {
|
||||
warn!(
|
||||
context,
|
||||
0, "Cannot load data to send, maybe the message is deleted in between.",
|
||||
"Cannot load data to send, maybe the message is deleted in between.",
|
||||
);
|
||||
} else {
|
||||
let mut mimefactory = mimefactory.unwrap();
|
||||
@@ -684,63 +628,55 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
mimefactory.msg.param.set_int(Param::Width, 0);
|
||||
mimefactory.msg.param.set_int(Param::Height, 0);
|
||||
|
||||
if let Some(buf) = dc_read_file_safe(context, pathNfilename) {
|
||||
if let Ok(buf) = dc_read_file(context, pathNfilename) {
|
||||
if let Ok((width, height)) = dc_get_filemeta(&buf) {
|
||||
mimefactory.msg.param.set_int(Param::Width, width as i32);
|
||||
mimefactory.msg.param.set_int(Param::Height, height as i32);
|
||||
}
|
||||
}
|
||||
dc_msg_save_param_to_disk(&mut mimefactory.msg);
|
||||
mimefactory.msg.save_param_to_disk(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* create message */
|
||||
if 0 == dc_mimefactory_render(&mut mimefactory) {
|
||||
dc_set_msg_failed(context, msg_id, as_opt_str(mimefactory.error));
|
||||
if let Err(msg) = unsafe { mimefactory.render() } {
|
||||
let e = msg.to_string();
|
||||
message::set_msg_failed(context, msg_id, Some(e));
|
||||
} else if 0
|
||||
!= mimefactory
|
||||
.msg
|
||||
.param
|
||||
.get_int(Param::GuranteeE2ee)
|
||||
.unwrap_or_default()
|
||||
&& 0 == mimefactory.out_encrypted
|
||||
&& !mimefactory.out_encrypted
|
||||
{
|
||||
/* unrecoverable */
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"e2e encryption unavailable {} - {:?}",
|
||||
msg_id,
|
||||
mimefactory.msg.param.get_int(Param::GuranteeE2ee),
|
||||
);
|
||||
dc_set_msg_failed(
|
||||
message::set_msg_failed(
|
||||
context,
|
||||
msg_id,
|
||||
Some("End-to-end-encryption unavailable unexpectedly."),
|
||||
);
|
||||
} else {
|
||||
/* unrecoverable */
|
||||
if clist_search_string_nocase(mimefactory.recipients_addr, mimefactory.from_addr)
|
||||
== 0i32
|
||||
{
|
||||
clist_insert_after(
|
||||
mimefactory.recipients_names,
|
||||
(*mimefactory.recipients_names).last,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
clist_insert_after(
|
||||
mimefactory.recipients_addr,
|
||||
(*mimefactory.recipients_addr).last,
|
||||
dc_strdup(mimefactory.from_addr) as *mut libc::c_void,
|
||||
);
|
||||
if !vec_contains_lowercase(&mimefactory.recipients_addr, &mimefactory.from_addr) {
|
||||
mimefactory.recipients_names.push("".to_string());
|
||||
mimefactory
|
||||
.recipients_addr
|
||||
.push(mimefactory.from_addr.to_string());
|
||||
}
|
||||
if 0 != mimefactory.out_gossiped {
|
||||
if mimefactory.out_gossiped {
|
||||
chat::set_gossiped_timestamp(context, mimefactory.msg.chat_id, time());
|
||||
}
|
||||
if 0 != mimefactory.out_last_added_location_id {
|
||||
if let Err(err) =
|
||||
location::set_kml_sent_timestamp(context, mimefactory.msg.chat_id, time())
|
||||
{
|
||||
error!(context, 0, "Failed to set kml sent_timestamp: {:?}", err);
|
||||
error!(context, "Failed to set kml sent_timestamp: {:?}", err);
|
||||
}
|
||||
if !mimefactory.msg.hidden {
|
||||
if let Err(err) = location::set_msg_location_id(
|
||||
@@ -748,11 +684,11 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
mimefactory.msg.id,
|
||||
mimefactory.out_last_added_location_id,
|
||||
) {
|
||||
error!(context, 0, "Failed to set msg_location_id: {:?}", err);
|
||||
error!(context, "Failed to set msg_location_id: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if 0 != mimefactory.out_encrypted
|
||||
if mimefactory.out_encrypted
|
||||
&& mimefactory
|
||||
.msg
|
||||
.param
|
||||
@@ -761,7 +697,7 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
== 0
|
||||
{
|
||||
mimefactory.msg.param.set_int(Param::GuranteeE2ee, 1);
|
||||
dc_msg_save_param_to_disk(&mut mimefactory.msg);
|
||||
mimefactory.msg.save_param_to_disk(context);
|
||||
}
|
||||
success = add_smtp_job(context, Action::SendMsgToSmtp, &mut mimefactory);
|
||||
}
|
||||
@@ -771,14 +707,22 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
}
|
||||
|
||||
pub fn perform_imap_jobs(context: &Context) {
|
||||
info!(context, 0, "dc_perform_imap_jobs starting.",);
|
||||
info!(context, "dc_perform_imap_jobs starting.",);
|
||||
|
||||
let probe_imap_network = *context.probe_imap_network.clone().read().unwrap();
|
||||
*context.probe_imap_network.write().unwrap() = false;
|
||||
*context.perform_inbox_jobs_needed.write().unwrap() = false;
|
||||
|
||||
job_perform(context, Thread::Imap, probe_imap_network);
|
||||
info!(context, 0, "dc_perform_imap_jobs ended.",);
|
||||
info!(context, "dc_perform_imap_jobs ended.",);
|
||||
}
|
||||
|
||||
pub fn perform_mvbox_jobs(context: &Context) {
|
||||
info!(context, "dc_perform_mbox_jobs EMPTY (for now).",);
|
||||
}
|
||||
|
||||
pub fn perform_sentbox_jobs(context: &Context) {
|
||||
info!(context, "dc_perform_sentbox_jobs EMPTY (for now).",);
|
||||
}
|
||||
|
||||
fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
@@ -826,14 +770,13 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
match jobs {
|
||||
Ok(ref _res) => {}
|
||||
Err(ref err) => {
|
||||
info!(context, 0, "query failed: {:?}", err);
|
||||
info!(context, "query failed: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
for mut job in jobs.unwrap_or_default() {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-job #{}, action {} started...",
|
||||
if thread == Thread::Imap {
|
||||
"INBOX"
|
||||
@@ -871,14 +814,22 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
job.try_again = 0;
|
||||
|
||||
match job.action {
|
||||
Action::Unknown => {
|
||||
warn!(context, "Unknown job id found");
|
||||
}
|
||||
Action::SendMsgToSmtp => job.do_DC_JOB_SEND(context),
|
||||
Action::DeleteMsgOnImap => job.do_DC_JOB_DELETE_MSG_ON_IMAP(context),
|
||||
Action::MarkseenMsgOnImap => job.do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context),
|
||||
Action::MarkseenMdnOnImap => job.do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context),
|
||||
Action::MoveMsg => job.do_DC_JOB_MOVE_MSG(context),
|
||||
Action::SendMdn => job.do_DC_JOB_SEND(context),
|
||||
Action::ConfigureImap => unsafe { dc_job_do_DC_JOB_CONFIGURE_IMAP(context, &job) },
|
||||
Action::ImexImap => unsafe { dc_job_do_DC_JOB_IMEX_IMAP(context, &job) },
|
||||
Action::ConfigureImap => unsafe { dc_job_do_DC_JOB_CONFIGURE_IMAP(context) },
|
||||
Action::ImexImap => match dc_job_do_DC_JOB_IMEX_IMAP(context, &job) {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
error!(context, "{}", err);
|
||||
}
|
||||
},
|
||||
Action::MaybeSendLocations => {
|
||||
location::job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context, &job)
|
||||
}
|
||||
@@ -913,7 +864,6 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
// just try over next loop unconditionally, the ui typically interrupts idle when the file (video) is ready
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-job #{} not yet ready and will be delayed.",
|
||||
if thread == Thread::Imap {
|
||||
"INBOX"
|
||||
@@ -931,7 +881,6 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
job.update(context);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-job #{} not succeeded on try #{}, retry in ADD_TIME+{} (in {} seconds).",
|
||||
if thread == Thread::Imap {
|
||||
"INBOX"
|
||||
@@ -954,7 +903,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
}
|
||||
} else {
|
||||
if job.action == Action::SendMsgToSmtp {
|
||||
dc_set_msg_failed(context, job.foreign_id, job.pending_error.as_ref());
|
||||
message::set_msg_failed(context, job.foreign_id, job.pending_error.as_ref());
|
||||
}
|
||||
job.delete(context);
|
||||
}
|
||||
@@ -1006,66 +955,41 @@ fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int {
|
||||
ret_connected
|
||||
}
|
||||
|
||||
fn send_mdn(context: &Context, msg_id: uint32_t) {
|
||||
if let Ok(mut mimefactory) = unsafe { dc_mimefactory_load_mdn(context, msg_id) } {
|
||||
if 0 != unsafe { dc_mimefactory_render(&mut mimefactory) } {
|
||||
fn send_mdn(context: &Context, msg_id: u32) {
|
||||
if let Ok(mut mimefactory) = MimeFactory::load_mdn(context, msg_id) {
|
||||
if unsafe { mimefactory.render() }.is_ok() {
|
||||
add_smtp_job(context, Action::SendMdn, &mut mimefactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn add_smtp_job(context: &Context, action: Action, mimefactory: &dc_mimefactory_t) -> libc::c_int {
|
||||
let pathNfilename: *mut libc::c_char;
|
||||
fn add_smtp_job(context: &Context, action: Action, mimefactory: &MimeFactory) -> libc::c_int {
|
||||
let mut success: libc::c_int = 0i32;
|
||||
let mut recipients: *mut libc::c_char = ptr::null_mut();
|
||||
let mut param = Params::new();
|
||||
pathNfilename = unsafe {
|
||||
dc_get_fine_pathNfilename(
|
||||
context,
|
||||
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
|
||||
mimefactory.rfc724_mid,
|
||||
let path_filename = dc_get_fine_path_filename(context, "$BLOBDIR", &mimefactory.rfc724_mid);
|
||||
let bytes = unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
(*mimefactory.out).str_0 as *const u8,
|
||||
(*mimefactory.out).len,
|
||||
)
|
||||
};
|
||||
if pathNfilename.is_null() {
|
||||
if !dc_write_file(context, &path_filename, bytes) {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Could not find free file name for message with ID <{}>.",
|
||||
to_string(mimefactory.rfc724_mid),
|
||||
);
|
||||
} else if 0
|
||||
== unsafe {
|
||||
dc_write_file(
|
||||
context,
|
||||
pathNfilename,
|
||||
(*mimefactory.out).str_0 as *const libc::c_void,
|
||||
(*mimefactory.out).len,
|
||||
)
|
||||
}
|
||||
{
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Could not write message <{}> to \"{}\".",
|
||||
to_string(mimefactory.rfc724_mid),
|
||||
as_str(pathNfilename),
|
||||
mimefactory.rfc724_mid,
|
||||
path_filename.display(),
|
||||
);
|
||||
} else {
|
||||
recipients = unsafe {
|
||||
dc_str_from_clist(
|
||||
mimefactory.recipients_addr,
|
||||
b"\x1e\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
};
|
||||
param.set(Param::File, as_str(pathNfilename));
|
||||
param.set(Param::Recipients, as_str(recipients));
|
||||
info!(context, "add_smtp_job file written: {:?}", path_filename);
|
||||
let recipients = mimefactory.recipients_addr.join("\x1e");
|
||||
param.set(Param::File, path_filename.to_string_lossy());
|
||||
param.set(Param::Recipients, &recipients);
|
||||
job_add(
|
||||
context,
|
||||
action,
|
||||
(if mimefactory.loaded as libc::c_uint
|
||||
== DC_MF_MSG_LOADED as libc::c_int as libc::c_uint
|
||||
{
|
||||
(if mimefactory.loaded == Loaded::Message {
|
||||
mimefactory.msg.id
|
||||
} else {
|
||||
0
|
||||
@@ -1075,10 +999,6 @@ fn add_smtp_job(context: &Context, action: Action, mimefactory: &dc_mimefactory_
|
||||
);
|
||||
success = 1;
|
||||
}
|
||||
unsafe {
|
||||
free(recipients.cast());
|
||||
free(pathNfilename.cast());
|
||||
}
|
||||
success
|
||||
}
|
||||
|
||||
@@ -1089,6 +1009,11 @@ pub fn job_add(
|
||||
param: Params,
|
||||
delay_seconds: i64,
|
||||
) {
|
||||
if action == Action::Unknown {
|
||||
error!(context, "Invalid action passed to job_add");
|
||||
return;
|
||||
}
|
||||
|
||||
let timestamp = time();
|
||||
let thread: Thread = action.into();
|
||||
|
||||
@@ -1109,11 +1034,12 @@ pub fn job_add(
|
||||
match thread {
|
||||
Thread::Imap => interrupt_imap_idle(context),
|
||||
Thread::Smtp => interrupt_smtp_idle(context),
|
||||
Thread::Unknown => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt_smtp_idle(context: &Context) {
|
||||
info!(context, 0, "Interrupting SMTP-idle...",);
|
||||
info!(context, "Interrupting SMTP-idle...",);
|
||||
|
||||
let &(ref lock, ref cvar) = &*context.smtp_state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
@@ -1124,7 +1050,7 @@ pub fn interrupt_smtp_idle(context: &Context) {
|
||||
}
|
||||
|
||||
pub fn interrupt_imap_idle(context: &Context) {
|
||||
info!(context, 0, "Interrupting IMAP-IDLE...",);
|
||||
info!(context, "Interrupting IMAP-IDLE...",);
|
||||
|
||||
*context.perform_inbox_jobs_needed.write().unwrap() = true;
|
||||
context.inbox.read().unwrap().interrupt_idle();
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::configure::*;
|
||||
use crate::context::Context;
|
||||
use crate::imap::Imap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JobThread {
|
||||
pub name: &'static str,
|
||||
pub folder_config_name: &'static str,
|
||||
@@ -30,7 +31,7 @@ impl JobThread {
|
||||
}
|
||||
|
||||
pub fn suspend(&self, context: &Context) {
|
||||
info!(context, 0, "Suspending {}-thread.", self.name,);
|
||||
info!(context, "Suspending {}-thread.", self.name,);
|
||||
{
|
||||
self.state.0.lock().unwrap().suspended = true;
|
||||
}
|
||||
@@ -45,7 +46,7 @@ impl JobThread {
|
||||
}
|
||||
|
||||
pub fn unsuspend(&self, context: &Context) {
|
||||
info!(context, 0, "Unsuspending {}-thread.", self.name);
|
||||
info!(context, "Unsuspending {}-thread.", self.name);
|
||||
|
||||
let &(ref lock, ref cvar) = &*self.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
@@ -60,7 +61,7 @@ impl JobThread {
|
||||
self.state.0.lock().unwrap().jobs_needed = 1;
|
||||
}
|
||||
|
||||
info!(context, 0, "Interrupting {}-IDLE...", self.name);
|
||||
info!(context, "Interrupting {}-IDLE...", self.name);
|
||||
|
||||
self.imap.interrupt_idle();
|
||||
|
||||
@@ -86,16 +87,15 @@ impl JobThread {
|
||||
if use_network {
|
||||
let start = std::time::Instant::now();
|
||||
if self.connect_to_imap(context) {
|
||||
info!(context, 0, "{}-fetch started...", self.name);
|
||||
info!(context, "{}-fetch started...", self.name);
|
||||
self.imap.fetch(context);
|
||||
|
||||
if self.imap.should_reconnect() {
|
||||
info!(context, 0, "{}-fetch aborted, starting over...", self.name,);
|
||||
info!(context, "{}-fetch aborted, starting over...", self.name,);
|
||||
self.imap.fetch(context);
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-fetch done in {:.3} ms.",
|
||||
self.name,
|
||||
start.elapsed().as_millis(),
|
||||
@@ -142,7 +142,6 @@ impl JobThread {
|
||||
if 0 != state.jobs_needed {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-IDLE will not be started as it was interrupted while not ideling.",
|
||||
self.name,
|
||||
);
|
||||
@@ -172,9 +171,9 @@ impl JobThread {
|
||||
}
|
||||
|
||||
self.connect_to_imap(context);
|
||||
info!(context, 0, "{}-IDLE started...", self.name,);
|
||||
info!(context, "{}-IDLE started...", self.name,);
|
||||
self.imap.idle(context);
|
||||
info!(context, 0, "{}-IDLE ended.", self.name);
|
||||
info!(context, "{}-IDLE ended.", self.name);
|
||||
|
||||
self.state.0.lock().unwrap().using_handle = false;
|
||||
}
|
||||
|
||||
97
src/key.rs
97
src/key.rs
@@ -1,7 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io::Cursor;
|
||||
use std::slice;
|
||||
use std::path::Path;
|
||||
|
||||
use libc;
|
||||
use pgp::composed::{Deserializable, SignedPublicKey, SignedSecretKey};
|
||||
@@ -12,7 +11,6 @@ use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::*;
|
||||
use crate::sql::{self, Sql};
|
||||
use crate::x::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum Key {
|
||||
@@ -106,15 +104,6 @@ impl Key {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_binary(data: *const u8, len: libc::c_int, key_type: KeyType) -> Option<Self> {
|
||||
if data.is_null() || len == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let bytes = unsafe { slice::from_raw_parts(data, len as usize) };
|
||||
Self::from_slice(bytes, key_type)
|
||||
}
|
||||
|
||||
pub fn from_armored_string(
|
||||
data: &str,
|
||||
key_type: KeyType,
|
||||
@@ -152,11 +141,10 @@ impl Key {
|
||||
) -> Option<Self> {
|
||||
let addr = self_addr.as_ref();
|
||||
|
||||
sql.query_row_col(
|
||||
sql.query_get_value(
|
||||
context,
|
||||
"SELECT public_key FROM keypairs WHERE addr=? AND is_default=1;",
|
||||
&[addr],
|
||||
0,
|
||||
)
|
||||
.and_then(|blob: Vec<u8>| Self::from_slice(&blob, KeyType::Public))
|
||||
}
|
||||
@@ -166,11 +154,10 @@ impl Key {
|
||||
self_addr: impl AsRef<str>,
|
||||
sql: &Sql,
|
||||
) -> Option<Self> {
|
||||
sql.query_row_col(
|
||||
sql.query_get_value(
|
||||
context,
|
||||
"SELECT private_key FROM keypairs WHERE addr=? AND is_default=1;",
|
||||
&[self_addr.as_ref()],
|
||||
0,
|
||||
)
|
||||
.and_then(|blob: Vec<u8>| Self::from_slice(&blob, KeyType::Private))
|
||||
}
|
||||
@@ -228,30 +215,15 @@ impl Key {
|
||||
.expect("failed to serialize key")
|
||||
}
|
||||
|
||||
pub fn write_asc_to_file(&self, file: *const libc::c_char, context: &Context) -> bool {
|
||||
if file.is_null() {
|
||||
pub fn write_asc_to_file(&self, file: impl AsRef<Path>, context: &Context) -> bool {
|
||||
let file_content = self.to_asc(None).into_bytes();
|
||||
|
||||
if dc_write_file(context, &file, &file_content) {
|
||||
return true;
|
||||
} else {
|
||||
error!(context, "Cannot write key to {}", file.as_ref().display());
|
||||
return false;
|
||||
}
|
||||
|
||||
let file_content = self.to_asc(None);
|
||||
let file_content_c = CString::new(file_content).unwrap();
|
||||
|
||||
let success = if 0
|
||||
== unsafe {
|
||||
dc_write_file(
|
||||
context,
|
||||
file,
|
||||
file_content_c.as_ptr() as *const libc::c_void,
|
||||
file_content_c.as_bytes().len(),
|
||||
)
|
||||
} {
|
||||
error!(context, 0, "Cannot write key to {}", to_string(file));
|
||||
false
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
success
|
||||
}
|
||||
|
||||
pub fn fingerprint(&self) -> String {
|
||||
@@ -261,23 +233,11 @@ impl Key {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fingerprint_c(&self) -> *mut libc::c_char {
|
||||
let res = CString::new(self.fingerprint()).unwrap();
|
||||
|
||||
unsafe { strdup(res.as_ptr()) }
|
||||
}
|
||||
|
||||
pub fn formatted_fingerprint(&self) -> String {
|
||||
let rawhex = self.fingerprint();
|
||||
dc_format_fingerprint(&rawhex)
|
||||
}
|
||||
|
||||
pub fn formatted_fingerprint_c(&self) -> *mut libc::c_char {
|
||||
let res = CString::new(self.formatted_fingerprint()).unwrap();
|
||||
|
||||
unsafe { strdup(res.as_ptr()) }
|
||||
}
|
||||
|
||||
pub fn split_key(&self) -> Option<Key> {
|
||||
match self {
|
||||
Key::Public(_) => None,
|
||||
@@ -323,14 +283,6 @@ pub fn dc_format_fingerprint(fingerprint: &str) -> String {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn dc_format_fingerprint_c(fp: *const libc::c_char) -> *mut libc::c_char {
|
||||
let input = unsafe { CStr::from_ptr(fp).to_str().unwrap() };
|
||||
let res = dc_format_fingerprint(input);
|
||||
let res_c = CString::new(res).unwrap();
|
||||
|
||||
unsafe { strdup(res_c.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Bring a human-readable or otherwise formatted fingerprint back to the 40-characters-uppercase-hex format.
|
||||
pub fn dc_normalize_fingerprint(fp: &str) -> String {
|
||||
fp.to_uppercase()
|
||||
@@ -339,14 +291,6 @@ pub fn dc_normalize_fingerprint(fp: &str) -> String {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn dc_normalize_fingerprint_c(fp: *const libc::c_char) -> *mut libc::c_char {
|
||||
let input = unsafe { CStr::from_ptr(fp).to_str().unwrap() };
|
||||
let res = dc_normalize_fingerprint(input);
|
||||
let res_c = CString::new(res).unwrap();
|
||||
|
||||
unsafe { strdup(res_c.as_ptr()) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -449,6 +393,27 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
|
||||
assert_eq!(private_key, private_key2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_slice_bad_data() {
|
||||
let mut bad_data: [u8; 4096] = [0; 4096];
|
||||
|
||||
for i in 0..4096 {
|
||||
bad_data[i] = (i & 0xff) as u8;
|
||||
}
|
||||
|
||||
for j in 0..(4096 / 40) {
|
||||
let bad_key = Key::from_slice(
|
||||
&bad_data[j..j + 4096 / 2 + j],
|
||||
if 0 != j & 1 {
|
||||
KeyType::Public
|
||||
} else {
|
||||
KeyType::Private
|
||||
},
|
||||
);
|
||||
assert!(bad_key.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // is too expensive
|
||||
fn test_ascii_roundtrip() {
|
||||
|
||||
@@ -33,11 +33,10 @@ impl<'a> Keyring<'a> {
|
||||
self_addr: impl AsRef<str>,
|
||||
sql: &Sql,
|
||||
) -> bool {
|
||||
sql.query_row_col(
|
||||
sql.query_get_value(
|
||||
context,
|
||||
"SELECT private_key FROM keypairs ORDER BY addr=? DESC, is_default DESC;",
|
||||
&[self_addr.as_ref()],
|
||||
0,
|
||||
)
|
||||
.and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Private))
|
||||
.map(|key| self.add_owned(key))
|
||||
|
||||
24
src/lib.rs
24
src/lib.rs
@@ -1,4 +1,4 @@
|
||||
#![deny(clippy::correctness)]
|
||||
#![deny(clippy::correctness, missing_debug_implementations)]
|
||||
// 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.
|
||||
@@ -16,12 +16,19 @@ extern crate rusqlite;
|
||||
extern crate strum;
|
||||
#[macro_use]
|
||||
extern crate strum_macros;
|
||||
#[macro_use]
|
||||
extern crate jetscii;
|
||||
#[macro_use]
|
||||
extern crate debug_stub_derive;
|
||||
|
||||
#[macro_use]
|
||||
mod log;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
|
||||
pub(crate) mod events;
|
||||
pub use events::*;
|
||||
|
||||
mod aheader;
|
||||
pub mod chat;
|
||||
pub mod chatlist;
|
||||
@@ -30,6 +37,7 @@ pub mod configure;
|
||||
pub mod constants;
|
||||
pub mod contact;
|
||||
pub mod context;
|
||||
mod e2ee;
|
||||
mod imap;
|
||||
pub mod job;
|
||||
mod job_thread;
|
||||
@@ -46,23 +54,21 @@ pub mod qr;
|
||||
mod smtp;
|
||||
pub mod sql;
|
||||
mod stock;
|
||||
pub mod types;
|
||||
pub mod x;
|
||||
|
||||
pub mod dc_array;
|
||||
mod dc_dehtml;
|
||||
mod dc_e2ee;
|
||||
pub mod dc_imex;
|
||||
mod dc_loginparam;
|
||||
mod dc_mimefactory;
|
||||
pub mod dc_mimeparser;
|
||||
mod dc_move;
|
||||
pub mod dc_receive_imf;
|
||||
pub mod dc_securejoin;
|
||||
mod dc_simplify;
|
||||
mod dc_strencode;
|
||||
mod dc_token;
|
||||
pub mod dc_tools;
|
||||
mod login_param;
|
||||
mod mimefactory;
|
||||
pub mod securejoin;
|
||||
mod token;
|
||||
#[macro_use]
|
||||
mod wrapmime;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_utils;
|
||||
|
||||
180
src/location.rs
180
src/location.rs
@@ -3,17 +3,16 @@ use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::chat;
|
||||
use crate::constants::Event;
|
||||
use crate::constants::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::events::Event;
|
||||
use crate::job::*;
|
||||
use crate::message::*;
|
||||
use crate::message::Message;
|
||||
use crate::param::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
use crate::types::*;
|
||||
|
||||
// location handling
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -84,7 +83,6 @@ impl Kml {
|
||||
Err(e) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Location parsing: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
@@ -216,21 +214,17 @@ pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) {
|
||||
.is_ok()
|
||||
{
|
||||
if 0 != seconds && !is_sending_locations_before {
|
||||
msg = dc_msg_new(context, Viewtype::Text);
|
||||
msg = Message::new(Viewtype::Text);
|
||||
msg.text =
|
||||
Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0));
|
||||
msg.param.set_int(Param::Cmd, 8);
|
||||
unsafe { chat::send_msg(context, chat_id, &mut msg).unwrap() };
|
||||
chat::send_msg(context, chat_id, &mut msg).unwrap();
|
||||
} else if 0 == seconds && is_sending_locations_before {
|
||||
let stock_str =
|
||||
context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0);
|
||||
chat::add_device_msg(context, chat_id, stock_str);
|
||||
}
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
chat_id as uintptr_t,
|
||||
0i32 as uintptr_t,
|
||||
);
|
||||
context.call_cb(Event::ChatModified(chat_id));
|
||||
if 0 != seconds {
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0i32);
|
||||
job_add(
|
||||
@@ -266,16 +260,16 @@ pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> l
|
||||
if latitude == 0.0 && longitude == 0.0 {
|
||||
return 1;
|
||||
}
|
||||
let mut continue_streaming = false;
|
||||
|
||||
context.sql.query_map(
|
||||
if let Ok(chats) = context.sql.query_map(
|
||||
"SELECT id FROM chats WHERE locations_send_until>?;",
|
||||
params![time()], |row| row.get::<_, i32>(0),
|
||||
|chats| {
|
||||
let mut continue_streaming = false;
|
||||
|
||||
for chat in chats {
|
||||
let chat_id = chat?;
|
||||
context.sql.execute(
|
||||
params![time()],
|
||||
|row| row.get::<_, i32>(0),
|
||||
|chats| chats.collect::<Result<Vec<_>, _>>().map_err(Into::into),
|
||||
) {
|
||||
for chat_id in chats {
|
||||
if let Err(err) = context.sql.execute(
|
||||
"INSERT INTO locations \
|
||||
(latitude, longitude, accuracy, timestamp, chat_id, from_id) VALUES (?,?,?,?,?,?);",
|
||||
params![
|
||||
@@ -286,16 +280,19 @@ pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> l
|
||||
chat_id,
|
||||
1,
|
||||
]
|
||||
)?;
|
||||
) {
|
||||
warn!(context, "failed to store location {:?}", err);
|
||||
} else {
|
||||
continue_streaming = true;
|
||||
}
|
||||
if continue_streaming {
|
||||
context.call_cb(Event::LOCATION_CHANGED, 1, 0);
|
||||
};
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0);
|
||||
Ok(continue_streaming as libc::c_int)
|
||||
}
|
||||
).unwrap_or_default()
|
||||
if continue_streaming {
|
||||
context.call_cb(Event::LocationChanged(Some(1)));
|
||||
};
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0);
|
||||
}
|
||||
|
||||
continue_streaming as libc::c_int
|
||||
}
|
||||
|
||||
pub fn get_range(
|
||||
@@ -367,7 +364,7 @@ fn is_marker(txt: &str) -> bool {
|
||||
|
||||
pub fn delete_all(context: &Context) -> Result<(), Error> {
|
||||
sql::execute(context, &context.sql, "DELETE FROM locations;", params![])?;
|
||||
context.call_cb(Event::LOCATION_CHANGED, 0, 0);
|
||||
context.call_cb(Event::LocationChanged(None));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -546,76 +543,81 @@ pub fn job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Job) {
|
||||
let mut continue_streaming: libc::c_int = 1;
|
||||
info!(
|
||||
context,
|
||||
0, " ----------------- MAYBE_SEND_LOCATIONS -------------- ",
|
||||
" ----------------- MAYBE_SEND_LOCATIONS -------------- ",
|
||||
);
|
||||
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT id, locations_send_begin, locations_last_sent \
|
||||
FROM chats \
|
||||
WHERE locations_send_until>?;",
|
||||
params![now],
|
||||
|row| {
|
||||
let chat_id: i32 = row.get(0)?;
|
||||
let locations_send_begin: i64 = row.get(1)?;
|
||||
let locations_last_sent: i64 = row.get(2)?;
|
||||
continue_streaming = 1;
|
||||
if let Ok(rows) = context.sql.query_map(
|
||||
"SELECT id, locations_send_begin, locations_last_sent \
|
||||
FROM chats \
|
||||
WHERE locations_send_until>?;",
|
||||
params![now],
|
||||
|row| {
|
||||
let chat_id: i32 = row.get(0)?;
|
||||
let locations_send_begin: i64 = row.get(1)?;
|
||||
let locations_last_sent: i64 = row.get(2)?;
|
||||
continue_streaming = 1;
|
||||
|
||||
// be a bit tolerant as the timer may not align exactly with time(NULL)
|
||||
if now - locations_last_sent < (60 - 3) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some((chat_id, locations_send_begin, locations_last_sent)))
|
||||
}
|
||||
},
|
||||
|rows| {
|
||||
context.sql.prepare(
|
||||
"SELECT id \
|
||||
FROM locations \
|
||||
WHERE from_id=? \
|
||||
AND timestamp>=? \
|
||||
AND timestamp>? \
|
||||
AND independent=0 \
|
||||
ORDER BY timestamp;",
|
||||
|mut stmt_locations, _| {
|
||||
for (chat_id, locations_send_begin, locations_last_sent) in
|
||||
rows.filter_map(|r| match r {
|
||||
Ok(Some(v)) => Some(v),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
// TODO: do I need to reset?
|
||||
// be a bit tolerant as the timer may not align exactly with time(NULL)
|
||||
if now - locations_last_sent < (60 - 3) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some((chat_id, locations_send_begin, locations_last_sent)))
|
||||
}
|
||||
},
|
||||
|rows| {
|
||||
rows.filter_map(|v| v.transpose())
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(Into::into)
|
||||
},
|
||||
) {
|
||||
let msgs = context
|
||||
.sql
|
||||
.prepare(
|
||||
"SELECT id \
|
||||
FROM locations \
|
||||
WHERE from_id=? \
|
||||
AND timestamp>=? \
|
||||
AND timestamp>? \
|
||||
AND independent=0 \
|
||||
ORDER BY timestamp;",
|
||||
|mut stmt_locations, _| {
|
||||
let msgs = rows
|
||||
.into_iter()
|
||||
.filter_map(|(chat_id, locations_send_begin, locations_last_sent)| {
|
||||
if !stmt_locations
|
||||
.exists(params![1, locations_send_begin, locations_last_sent,])
|
||||
.unwrap_or_default()
|
||||
{
|
||||
// if there is no new location, there's nothing to send.
|
||||
// however, maybe we want to bypass this test eg. 15 minutes
|
||||
continue;
|
||||
None
|
||||
} else {
|
||||
// pending locations are attached automatically to every message,
|
||||
// so also to this empty text message.
|
||||
// DC_CMD_LOCATION is only needed to create a nicer subject.
|
||||
//
|
||||
// for optimisation and to avoid flooding the sending queue,
|
||||
// we could sending these messages only if we're really online.
|
||||
// the easiest way to determine this, is to check for an empty message queue.
|
||||
// (might not be 100%, however, as positions are sent combined later
|
||||
// and dc_set_location() is typically called periodically, this is ok)
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.hidden = true;
|
||||
msg.param.set_int(Param::Cmd, 9);
|
||||
Some((chat_id, msg))
|
||||
}
|
||||
// pending locations are attached automatically to every message,
|
||||
// so also to this empty text message.
|
||||
// DC_CMD_LOCATION is only needed to create a nicer subject.
|
||||
//
|
||||
// for optimisation and to avoid flooding the sending queue,
|
||||
// we could sending these messages only if we're really online.
|
||||
// the easiest way to determine this, is to check for an empty message queue.
|
||||
// (might not be 100%, however, as positions are sent combined later
|
||||
// and dc_set_location() is typically called periodically, this is ok)
|
||||
let mut msg = dc_msg_new(context, Viewtype::Text);
|
||||
msg.hidden = true;
|
||||
msg.param.set_int(Param::Cmd, 9);
|
||||
// TODO: handle cleanup on error
|
||||
unsafe { chat::send_msg(context, chat_id as u32, &mut msg).unwrap() };
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
.unwrap(); // TODO: Better error handling
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
Ok(msgs)
|
||||
},
|
||||
)
|
||||
.unwrap_or_default(); // TODO: Better error handling
|
||||
|
||||
for (chat_id, mut msg) in msgs.into_iter() {
|
||||
// TODO: better error handling
|
||||
chat::send_msg(context, chat_id as u32, &mut msg).unwrap();
|
||||
}
|
||||
}
|
||||
if 0 != continue_streaming {
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0x1);
|
||||
}
|
||||
@@ -646,11 +648,7 @@ pub fn job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut Job) {
|
||||
).is_ok() {
|
||||
let stock_str = context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0);
|
||||
chat::add_device_msg(context, chat_id, stock_str);
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
chat_id as usize,
|
||||
0,
|
||||
);
|
||||
context.call_cb(Event::ChatModified(chat_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
60
src/log.rs
60
src/log.rs
@@ -1,59 +1,39 @@
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
info!($ctx, $data1, $msg,)
|
||||
($ctx:expr, $msg:expr) => {
|
||||
info!($ctx, $msg,)
|
||||
};
|
||||
($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
emit_event!($ctx, $crate::Event::Info(formatted));
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($crate::constants::Event::INFO, $data1 as libc::uintptr_t,
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
warn!($ctx, $data1, $msg,)
|
||||
($ctx:expr, $msg:expr) => {
|
||||
warn!($ctx, $msg,)
|
||||
};
|
||||
($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
emit_event!($ctx, $crate::Event::Warning(formatted));
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t,
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
error!($ctx, $data1, $msg,)
|
||||
($ctx:expr, $msg:expr) => {
|
||||
error!($ctx, $msg,)
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($crate::constants::Event::ERROR, $data1 as libc::uintptr_t,
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
emit_event!($ctx, $crate::Event::Error(formatted));
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log_event {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
log_event!($ctx, $data1, $msg,)
|
||||
macro_rules! emit_event {
|
||||
($ctx:expr, $event:expr) => {
|
||||
$ctx.call_cb($event);
|
||||
};
|
||||
($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($event, $data1 as libc::uintptr_t,
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
}
|
||||
|
||||
206
src/login_param.rs
Normal file
206
src/login_param.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::error::Error;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct LoginParam {
|
||||
pub addr: String,
|
||||
pub mail_server: String,
|
||||
pub mail_user: String,
|
||||
pub mail_pw: String,
|
||||
pub mail_port: i32,
|
||||
pub send_server: String,
|
||||
pub send_user: String,
|
||||
pub send_pw: String,
|
||||
pub send_port: i32,
|
||||
pub server_flags: i32,
|
||||
}
|
||||
|
||||
impl LoginParam {
|
||||
/// Create a new `LoginParam` with default values.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Read the login parameters from the database.
|
||||
pub fn from_database(context: &Context, prefix: impl AsRef<str>) -> Self {
|
||||
let prefix = prefix.as_ref();
|
||||
let sql = &context.sql;
|
||||
|
||||
let key = format!("{}addr", prefix);
|
||||
let addr = sql
|
||||
.get_config(context, key)
|
||||
.unwrap_or_default()
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
let key = format!("{}mail_server", prefix);
|
||||
let mail_server = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}mail_port", prefix);
|
||||
let mail_port = sql.get_config_int(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}mail_user", prefix);
|
||||
let mail_user = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}mail_pw", prefix);
|
||||
let mail_pw = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_server", prefix);
|
||||
let send_server = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_port", prefix);
|
||||
let send_port = sql.get_config_int(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_user", prefix);
|
||||
let send_user = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_pw", prefix);
|
||||
let send_pw = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}server_flags", prefix);
|
||||
let server_flags = sql.get_config_int(context, key).unwrap_or_default();
|
||||
|
||||
LoginParam {
|
||||
addr: addr.to_string(),
|
||||
mail_server,
|
||||
mail_user,
|
||||
mail_pw,
|
||||
mail_port,
|
||||
send_server,
|
||||
send_user,
|
||||
send_pw,
|
||||
send_port,
|
||||
server_flags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addr_str(&self) -> &str {
|
||||
self.addr.as_str()
|
||||
}
|
||||
|
||||
/// Save this loginparam to the database.
|
||||
pub fn save_to_database(
|
||||
&self,
|
||||
context: &Context,
|
||||
prefix: impl AsRef<str>,
|
||||
) -> Result<(), Error> {
|
||||
let prefix = prefix.as_ref();
|
||||
let sql = &context.sql;
|
||||
|
||||
let key = format!("{}addr", prefix);
|
||||
sql.set_config(context, key, Some(&self.addr))?;
|
||||
|
||||
let key = format!("{}mail_server", prefix);
|
||||
sql.set_config(context, key, Some(&self.mail_server))?;
|
||||
|
||||
let key = format!("{}mail_port", prefix);
|
||||
sql.set_config_int(context, key, self.mail_port)?;
|
||||
|
||||
let key = format!("{}mail_user", prefix);
|
||||
sql.set_config(context, key, Some(&self.mail_user))?;
|
||||
|
||||
let key = format!("{}mail_pw", prefix);
|
||||
sql.set_config(context, key, Some(&self.mail_pw))?;
|
||||
|
||||
let key = format!("{}send_server", prefix);
|
||||
sql.set_config(context, key, Some(&self.send_server))?;
|
||||
|
||||
let key = format!("{}send_port", prefix);
|
||||
sql.set_config_int(context, key, self.send_port)?;
|
||||
|
||||
let key = format!("{}send_user", prefix);
|
||||
sql.set_config(context, key, Some(&self.send_user))?;
|
||||
|
||||
let key = format!("{}send_pw", prefix);
|
||||
sql.set_config(context, key, Some(&self.send_pw))?;
|
||||
|
||||
let key = format!("{}server_flags", prefix);
|
||||
sql.set_config_int(context, key, self.server_flags)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LoginParam {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let unset = "0";
|
||||
let pw = "***";
|
||||
|
||||
let flags_readable = get_readable_flags(self.server_flags);
|
||||
|
||||
write!(
|
||||
f,
|
||||
"{} {}:{}:{}:{} {}:{}:{}:{} {}",
|
||||
unset_empty(&self.addr),
|
||||
unset_empty(&self.mail_user),
|
||||
if !self.mail_pw.is_empty() { pw } else { unset },
|
||||
unset_empty(&self.mail_server),
|
||||
self.mail_port,
|
||||
unset_empty(&self.send_user),
|
||||
if !self.send_pw.is_empty() { pw } else { unset },
|
||||
unset_empty(&self.send_server),
|
||||
self.send_port,
|
||||
flags_readable,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn unset_empty(s: &String) -> Cow<String> {
|
||||
if s.is_empty() {
|
||||
Cow::Owned("unset".to_string())
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_readable_flags(flags: i32) -> String {
|
||||
let mut res = String::new();
|
||||
for bit in 0..31 {
|
||||
if 0 != flags & 1 << bit {
|
||||
let mut flag_added = 0;
|
||||
if 1 << bit == 0x2 {
|
||||
res += "OAUTH2 ";
|
||||
flag_added = 1;
|
||||
}
|
||||
if 1 << bit == 0x4 {
|
||||
res += "AUTH_NORMAL ";
|
||||
flag_added = 1;
|
||||
}
|
||||
if 1 << bit == 0x100 {
|
||||
res += "IMAP_STARTTLS ";
|
||||
flag_added = 1;
|
||||
}
|
||||
if 1 << bit == 0x200 {
|
||||
res += "IMAP_SSL ";
|
||||
flag_added = 1;
|
||||
}
|
||||
if 1 << bit == 0x400 {
|
||||
res += "IMAP_PLAIN ";
|
||||
flag_added = 1;
|
||||
}
|
||||
if 1 << bit == 0x10000 {
|
||||
res += "SMTP_STARTTLS ";
|
||||
flag_added = 1
|
||||
}
|
||||
if 1 << bit == 0x20000 {
|
||||
res += "SMTP_SSL ";
|
||||
flag_added = 1
|
||||
}
|
||||
if 1 << bit == 0x40000 {
|
||||
res += "SMTP_PLAIN ";
|
||||
flag_added = 1
|
||||
}
|
||||
if 0 == flag_added {
|
||||
res += &format!("{:#0x}", 1 << bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
if res.is_empty() {
|
||||
res += "0";
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
1282
src/message.rs
1282
src/message.rs
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user