Compare commits
30 Commits
1.33.0
...
hpk-gh-pyt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e454ede4c | ||
|
|
029f60df27 | ||
|
|
20c82b324a | ||
|
|
e7ebb40cd1 | ||
|
|
bca8094fcb | ||
|
|
7ab5d36b5b | ||
|
|
58ad14d9c3 | ||
|
|
e539bddc3b | ||
|
|
2fc1d21959 | ||
|
|
b43d9d2ffe | ||
|
|
5b73951b9b | ||
|
|
8595b92fcf | ||
|
|
6054b90975 | ||
|
|
b8427ab56e | ||
|
|
02f72eea61 | ||
|
|
a5a20078f0 | ||
|
|
7383094b33 | ||
|
|
e225a6fb17 | ||
|
|
6bdc207277 | ||
|
|
101141c67a | ||
|
|
d07afe5bd6 | ||
|
|
00e22d4339 | ||
|
|
91c8f48c21 | ||
|
|
3d76d21925 | ||
|
|
3349c0e9dc | ||
|
|
7b35104b83 | ||
|
|
22b6e8f6e2 | ||
|
|
ad118aa0df | ||
|
|
9044b80b9f | ||
|
|
b948e973c5 |
@@ -7,10 +7,6 @@ executors:
|
|||||||
doxygen:
|
doxygen:
|
||||||
docker:
|
docker:
|
||||||
- image: hrektts/doxygen
|
- image: hrektts/doxygen
|
||||||
python:
|
|
||||||
docker:
|
|
||||||
- image: 3.7.7-stretch
|
|
||||||
|
|
||||||
|
|
||||||
restore-workspace: &restore-workspace
|
restore-workspace: &restore-workspace
|
||||||
attach_workspace:
|
attach_workspace:
|
||||||
@@ -40,6 +36,12 @@ jobs:
|
|||||||
executor: default
|
executor: default
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Update submodules
|
||||||
|
command: git submodule update --init --recursive
|
||||||
|
- run:
|
||||||
|
name: Calculate dependencies
|
||||||
|
command: cargo generate-lockfile
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
- cargo-v3-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||||
@@ -47,6 +49,7 @@ jobs:
|
|||||||
- run: rustup default $(cat rust-toolchain)
|
- run: rustup default $(cat rust-toolchain)
|
||||||
- run: rustup component add --toolchain $(cat rust-toolchain) rustfmt
|
- run: rustup component add --toolchain $(cat rust-toolchain) rustfmt
|
||||||
- run: rustup component add --toolchain $(cat rust-toolchain) clippy-preview
|
- run: rustup component add --toolchain $(cat rust-toolchain) clippy-preview
|
||||||
|
- run: cargo update
|
||||||
- run: cargo fetch
|
- run: cargo fetch
|
||||||
- run: rustc +stable --version
|
- run: rustc +stable --version
|
||||||
- run: rustc +$(cat rust-toolchain) --version
|
- run: rustc +$(cat rust-toolchain) --version
|
||||||
@@ -88,6 +91,7 @@ jobs:
|
|||||||
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||||
- run: rustup install $(cat rust-toolchain)
|
- run: rustup install $(cat rust-toolchain)
|
||||||
- run: rustup default $(cat rust-toolchain)
|
- run: rustup default $(cat rust-toolchain)
|
||||||
|
- run: cargo update
|
||||||
- run: cargo fetch
|
- run: cargo fetch
|
||||||
- run:
|
- run:
|
||||||
name: Test
|
name: Test
|
||||||
@@ -124,13 +128,26 @@ jobs:
|
|||||||
paths:
|
paths:
|
||||||
- c-docs
|
- c-docs
|
||||||
|
|
||||||
remote_python_packaging:
|
build_test_docs_wheel:
|
||||||
machine: true
|
docker:
|
||||||
|
- image: deltachat/coredeps
|
||||||
|
environment:
|
||||||
|
TESTS: 1
|
||||||
|
DOCS: 1
|
||||||
|
working_directory: /mnt/crate
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- *restore-workspace
|
||||||
# the following commands on success produces
|
- *restore-cache
|
||||||
# workspace/{wheelhouse,py-docs} as artefact directories
|
- run:
|
||||||
- run: bash ci_scripts/remote_python_packaging.sh
|
name: build docs, run tests and build wheels
|
||||||
|
command: ci_scripts/run-python.sh
|
||||||
|
- run:
|
||||||
|
name: copying docs and wheels to workspace
|
||||||
|
command: |
|
||||||
|
mkdir -p workspace/python
|
||||||
|
# cp -av docs workspace/c-docs
|
||||||
|
cp -av python/.docker-tox/wheelhouse workspace/
|
||||||
|
cp -av python/doc/_build/ workspace/py-docs
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: workspace
|
root: workspace
|
||||||
paths:
|
paths:
|
||||||
@@ -138,25 +155,12 @@ jobs:
|
|||||||
- py-docs
|
- py-docs
|
||||||
- wheelhouse
|
- wheelhouse
|
||||||
|
|
||||||
remote_tests_rust:
|
|
||||||
machine: true
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: ci_scripts/remote_tests_rust.sh
|
|
||||||
|
|
||||||
remote_tests_python:
|
|
||||||
machine: true
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: ci_scripts/remote_tests_python.sh
|
|
||||||
|
|
||||||
upload_docs_wheels:
|
upload_docs_wheels:
|
||||||
machine: true
|
machine: true
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: workspace
|
at: workspace
|
||||||
- run: pyenv versions
|
|
||||||
- run: pyenv global 3.5.2
|
- run: pyenv global 3.5.2
|
||||||
- run: ls -laR workspace
|
- run: ls -laR workspace
|
||||||
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse workspace/c-docs
|
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse workspace/c-docs
|
||||||
@@ -168,7 +172,7 @@ jobs:
|
|||||||
- *restore-cache
|
- *restore-cache
|
||||||
- run:
|
- run:
|
||||||
name: Run cargo clippy
|
name: Run cargo clippy
|
||||||
command: cargo clippy
|
command: cargo clippy --all
|
||||||
|
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
@@ -176,49 +180,27 @@ workflows:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
jobs:
|
jobs:
|
||||||
# - cargo_fetch
|
- cargo_fetch
|
||||||
|
- build_doxygen
|
||||||
- remote_tests_rust:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
|
|
||||||
- remote_tests_python:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
|
|
||||||
- remote_python_packaging:
|
|
||||||
requires:
|
|
||||||
- remote_tests_python
|
|
||||||
- remote_tests_rust
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
|
|
||||||
|
- build_test_docs_wheel:
|
||||||
|
requires:
|
||||||
|
- cargo_fetch
|
||||||
- upload_docs_wheels:
|
- upload_docs_wheels:
|
||||||
requires:
|
requires:
|
||||||
- remote_python_packaging
|
- build_test_docs_wheel
|
||||||
- build_doxygen
|
- build_doxygen
|
||||||
filters:
|
- rustfmt:
|
||||||
tags:
|
requires:
|
||||||
only: /.*/
|
- cargo_fetch
|
||||||
# - rustfmt:
|
- clippy:
|
||||||
# requires:
|
requires:
|
||||||
# - cargo_fetch
|
- cargo_fetch
|
||||||
# - clippy:
|
|
||||||
# requires:
|
|
||||||
# - cargo_fetch
|
|
||||||
|
|
||||||
- build_doxygen:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
|
|
||||||
# Linux Desktop 64bit
|
# Linux Desktop 64bit
|
||||||
# - test_x86_64-unknown-linux-gnu:
|
- test_x86_64-unknown-linux-gnu:
|
||||||
# requires:
|
requires:
|
||||||
# - cargo_fetch
|
- cargo_fetch
|
||||||
|
|
||||||
# Linux Desktop 32bit
|
# Linux Desktop 32bit
|
||||||
# - test_i686-unknown-linux-gnu:
|
# - test_i686-unknown-linux-gnu:
|
||||||
|
|||||||
48
.github/workflows/code-quality.yml
vendored
@@ -1,48 +0,0 @@
|
|||||||
on: push
|
|
||||||
name: Code Quality
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
check:
|
|
||||||
name: Check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: nightly-2020-03-12
|
|
||||||
override: true
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: --workspace --examples --tests --all-features
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
name: Rustfmt
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: nightly-2020-03-12
|
|
||||||
override: true
|
|
||||||
- run: rustup component add rustfmt
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: fmt
|
|
||||||
args: --all -- --check
|
|
||||||
|
|
||||||
run_clippy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: nightly-2020-03-12
|
|
||||||
components: clippy
|
|
||||||
override: true
|
|
||||||
- uses: actions-rs/clippy-check@v1
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
args: --all-features
|
|
||||||
30
.github/workflows/rust.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: -Dwarnings
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: 3.7 python tests against core
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
|
- name: Setup python
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: 3.x
|
||||||
|
architecture: x64
|
||||||
|
|
||||||
|
- run: bash ci_scripts/run-python.sh
|
||||||
2
.gitignore
vendored
@@ -23,5 +23,3 @@ python/liveconfig*
|
|||||||
# ignore doxgen generated files
|
# ignore doxgen generated files
|
||||||
deltachat-ffi/html
|
deltachat-ffi/html
|
||||||
deltachat-ffi/xml
|
deltachat-ffi/xml
|
||||||
|
|
||||||
.rsynclist
|
|
||||||
|
|||||||
412
CHANGELOG.md
@@ -1,417 +1,5 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 1.33.0
|
|
||||||
|
|
||||||
- let `dc_set_muted()` also mute one-to-one chats #1470
|
|
||||||
|
|
||||||
- fix a but that led to load and traffic if the server does not use sent-folder
|
|
||||||
#1472
|
|
||||||
|
|
||||||
|
|
||||||
## 1.32.0
|
|
||||||
|
|
||||||
- fix endless loop when trying to download messages with bad RFC Message-ID,
|
|
||||||
also be more reliable on similar errors #1463 #1466 #1462
|
|
||||||
|
|
||||||
- fix bug with comma in contact request #1438
|
|
||||||
|
|
||||||
- do not refer to hidden messages on replies #1459
|
|
||||||
|
|
||||||
- improve error handling #1468 #1465 #1464
|
|
||||||
|
|
||||||
|
|
||||||
## 1.31.0
|
|
||||||
|
|
||||||
- always describe the context of the displayed error #1451
|
|
||||||
|
|
||||||
- do not emit `DC_EVENT_ERROR` when message sending fails;
|
|
||||||
`dc_msg_get_state()` and `dc_get_msg_info()` are sufficient #1451
|
|
||||||
|
|
||||||
- new config-option `media_quality` #1449
|
|
||||||
|
|
||||||
- try over if writing message to database fails #1447
|
|
||||||
|
|
||||||
|
|
||||||
## 1.30.0
|
|
||||||
|
|
||||||
- expunge deleted messages #1440
|
|
||||||
|
|
||||||
- do not send `DC_EVENT_MSGS_CHANGED|INCOMING_MSG` on hidden messages #1439
|
|
||||||
|
|
||||||
|
|
||||||
## 1.29.0
|
|
||||||
|
|
||||||
- new config options `delete_device_after` and `delete_server_after`,
|
|
||||||
each taking an amount of seconds after which messages
|
|
||||||
are deleted from the device and/or the server #1310 #1335 #1411 #1417 #1423
|
|
||||||
|
|
||||||
- new api `dc_estimate_deletion_cnt()` to estimate the effect
|
|
||||||
of `delete_device_after` and `delete_server_after`
|
|
||||||
|
|
||||||
- use Ed25519 keys by default, these keys are much shorter
|
|
||||||
than RSA keys, which results in saving traffic and speed improvements #1362
|
|
||||||
|
|
||||||
- improve message ellipsizing #1397 #1430
|
|
||||||
|
|
||||||
- emit `DC_EVENT_ERROR_NETWORK` also on smtp-errors #1378
|
|
||||||
|
|
||||||
- do not show badly formatted non-delta-messages as empty #1384
|
|
||||||
|
|
||||||
- try over SMTP on potentially recoverable error 5.5.0 #1379
|
|
||||||
|
|
||||||
- remove device-chat from forward-to-chat-list #1367
|
|
||||||
|
|
||||||
- improve group-handling #1368
|
|
||||||
|
|
||||||
- `dc_get_info()` returns uptime (how long the context is in use)
|
|
||||||
|
|
||||||
- python improvements and adaptions #1408 #1415
|
|
||||||
|
|
||||||
- log to the stdout and stderr in tests #1416
|
|
||||||
|
|
||||||
- refactoring, code improvements #1363 #1365 #1366 #1370 #1375 #1389 #1390 #1418 #1419
|
|
||||||
|
|
||||||
- removed api: `dc_chat_get_subtitle()`, `dc_get_version_str()`, `dc_array_add_id()`
|
|
||||||
|
|
||||||
- removed events: `DC_EVENT_MEMBER_ADDED`, `DC_EVENT_MEMBER_REMOVED`
|
|
||||||
|
|
||||||
|
|
||||||
## 1.28.0
|
|
||||||
|
|
||||||
- new flag DC_GCL_FOR_FORWARDING for dc_get_chatlist()
|
|
||||||
that will sort the "saved messages" chat to the top of the chatlist #1336
|
|
||||||
- mark mails as being deleted from server in dc_empty_server() #1333
|
|
||||||
- fix interaction with servers that do not allow folder creation on root-level;
|
|
||||||
use path separator as defined by the email server #1359
|
|
||||||
- fix group creation if group was created by non-delta clients #1357
|
|
||||||
- fix showing replies from non-delta clients #1353
|
|
||||||
- fix member list on rejoining left groups #1343
|
|
||||||
- fix crash when using empty groups #1354
|
|
||||||
- fix potential crash on special names #1350
|
|
||||||
|
|
||||||
|
|
||||||
## 1.27.0
|
|
||||||
|
|
||||||
- handle keys reliably on armv7 #1327
|
|
||||||
|
|
||||||
|
|
||||||
## 1.26.0
|
|
||||||
|
|
||||||
- change generated key type back to RSA as shipped versions
|
|
||||||
have problems to encrypt to Ed25519 keys
|
|
||||||
|
|
||||||
- update rPGP to encrypt reliably to Ed25519 keys;
|
|
||||||
one of the next versions can finally use Ed25519 keys then
|
|
||||||
|
|
||||||
|
|
||||||
## 1.25.0
|
|
||||||
|
|
||||||
- save traffic by downloading only messages that are really displayed #1236
|
|
||||||
|
|
||||||
- change generated key type to Ed25519, these keys are much shorter
|
|
||||||
than RSA keys, which results in saving traffic and speed improvements #1287
|
|
||||||
|
|
||||||
- improve key handling #1237 #1240 #1242 #1247
|
|
||||||
|
|
||||||
- mute handling, apis are dc_set_chat_mute_duration()
|
|
||||||
dc_chat_is_muted() and dc_chat_get_remaining_mute_duration() #1143
|
|
||||||
|
|
||||||
- pinning chats, new apis are dc_set_chat_visibility() and
|
|
||||||
dc_chat_get_visibility() #1248
|
|
||||||
|
|
||||||
- add dc_provider_new_from_email() api that queries the new, integrated
|
|
||||||
provider-database #1207
|
|
||||||
|
|
||||||
- account creation by scanning a qr code
|
|
||||||
in the DCACCOUNT scheme (https://mailadm.readthedocs.io),
|
|
||||||
new api is dc_set_config_from_qr() #1249
|
|
||||||
|
|
||||||
- if possible, dc_join_securejoin(), returns the new chat-id immediately
|
|
||||||
and does the handshake in background #1225
|
|
||||||
|
|
||||||
- update imap and smtp dependencies #1115
|
|
||||||
|
|
||||||
- check for MOVE capability before using MOVE command #1263
|
|
||||||
|
|
||||||
- allow inline attachments from RFC 2183 #1280
|
|
||||||
|
|
||||||
- fix updating names from incoming mails #1298
|
|
||||||
|
|
||||||
- fix error messages shown on import #1234
|
|
||||||
|
|
||||||
- directly attempt to re-connect if the smtp connection is maybe stale #1296
|
|
||||||
|
|
||||||
- improve adding group members #1291
|
|
||||||
|
|
||||||
- improve rust-api #1261
|
|
||||||
|
|
||||||
- cleanup #1302 #1283 #1282 #1276 #1270-#1274 #1267 #1258-#1260
|
|
||||||
#1257 #1239 #1231 #1224
|
|
||||||
|
|
||||||
- update spec #1286 #1291
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0-beta.24
|
|
||||||
|
|
||||||
- fix oauth2/gmail bug introduced in beta23 (not used in releases) #1219
|
|
||||||
|
|
||||||
- fix panic when receiving eg. cyrillic filenames #1216
|
|
||||||
|
|
||||||
- delete all consumed secure-join handshake messagess #1209 #1212
|
|
||||||
|
|
||||||
- rust-level cleanups #1218 #1217 #1210 #1205
|
|
||||||
|
|
||||||
- python-level cleanups #1204 #1202 #1201
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0-beta.23
|
|
||||||
|
|
||||||
- #1197 fix imap-deletion of messages
|
|
||||||
|
|
||||||
- #1171 Combine multiple MDNs into a single mail, reducing traffic
|
|
||||||
|
|
||||||
- #1155 fix to not send out gossip always, reducing traffic
|
|
||||||
|
|
||||||
- #1160 fix reply-to-encrypted determination
|
|
||||||
|
|
||||||
- #1182 Add "Auto-Submitted: auto-replied" header to MDNs
|
|
||||||
|
|
||||||
- #1194 produce python wheels again, fix c/py.delta.chat
|
|
||||||
master-deployment
|
|
||||||
|
|
||||||
- rust-level housekeeping and improvements #1161 #1186 #1185 #1190 #1194 #1199 #1191 #1190 #1184 and more
|
|
||||||
|
|
||||||
- #1063 clarify licensing
|
|
||||||
|
|
||||||
- #1147 use mailparse 0.10.2
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0-beta.22
|
|
||||||
|
|
||||||
- #1095 normalize email lineends to CRLF
|
|
||||||
|
|
||||||
- #1095 enable link-time-optimization, saves eg. on android 11 mb
|
|
||||||
|
|
||||||
- #1099 fix import regarding devicechats
|
|
||||||
|
|
||||||
- #1092 improve logging
|
|
||||||
|
|
||||||
- #1096 #1097 #1094 #1090 #1091 internal cleanups
|
|
||||||
|
|
||||||
## 1.0.0-beta.21
|
|
||||||
|
|
||||||
- #1078 #1082 ensure RFC compliance by producing 78 column lines for
|
|
||||||
encoded attachments.
|
|
||||||
|
|
||||||
- #1080 don't recreate and thus break group membership if an unknown
|
|
||||||
sender (or mailer-daemon) sends a message referencing the group chat
|
|
||||||
|
|
||||||
- #1081 #1079 some internal cleanups
|
|
||||||
|
|
||||||
- update imap-proto dependency, to fix yandex/oauth
|
|
||||||
|
|
||||||
## 1.0.0-beta.20
|
|
||||||
|
|
||||||
- #1074 fix OAUTH2/gmail
|
|
||||||
- #1072 fix group members not appearing in contact list
|
|
||||||
- #1071 never block interrupt_idle (thus hopefully also not on maybe_network())
|
|
||||||
- #1069 reduce smtp-timeout to 30 seconds
|
|
||||||
- #1066 #1065 avoid unwrap in dehtml, make literals more readable
|
|
||||||
|
|
||||||
## 1.0.0-beta.19
|
|
||||||
|
|
||||||
- #1058 timeout smtp-send if it doesn't complete in 15 minutes
|
|
||||||
|
|
||||||
- #1059 trim down logging
|
|
||||||
|
|
||||||
## 1.0.0-beta.18
|
|
||||||
|
|
||||||
- #1056 avoid panicking when we couldn't read imap-server's greeting
|
|
||||||
message
|
|
||||||
|
|
||||||
- #1055 avoid panicking when we don't have a selected folder
|
|
||||||
|
|
||||||
- #1052 #1049 #1051 improve logging to add thread-id/name and
|
|
||||||
file/lineno to each info/warn message.
|
|
||||||
|
|
||||||
- #1050 allow python bindings to initialize Account with "os_name".
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0-beta.17
|
|
||||||
|
|
||||||
- #1044 implement avatar recoding to 192x192 in core to keep file sizes small.
|
|
||||||
|
|
||||||
- #1024 fix #1021 SQL/injection malformed Chat-Group-Name breakage
|
|
||||||
|
|
||||||
- #1036 fix smtp crash by pulling in a fixed async-smtp
|
|
||||||
|
|
||||||
- #1039 fix read-receipts appearing as normal messages when you change
|
|
||||||
MDN settings
|
|
||||||
|
|
||||||
- #1040 do not panic on SystemTimeDifference
|
|
||||||
|
|
||||||
- #1043 avoid potential crashes in malformed From/Chat-Disposition... headers
|
|
||||||
|
|
||||||
- #1045 #1041 #1038 #1035 #1034 #1029 #1025 various cleanups and doc
|
|
||||||
improvments
|
|
||||||
|
|
||||||
## 1.0.0-beta.16
|
|
||||||
|
|
||||||
- alleviate login problems with providers which only
|
|
||||||
support RSA1024 keys by switching back from Rustls
|
|
||||||
to native-tls, by using the new async-email/async-native-tls
|
|
||||||
crate from @dignifiedquire. thanks @link2xt.
|
|
||||||
|
|
||||||
- introduce per-contact profile images to send out
|
|
||||||
own profile image heuristically, and fix sending
|
|
||||||
out of profile images in "in-prepare" groups.
|
|
||||||
this also extends the Chat-spec that is maintained
|
|
||||||
in core to specify Chat-Group-Image and Chat-Group-Avatar
|
|
||||||
headers. thanks @r10s and @hpk42.
|
|
||||||
|
|
||||||
- fix merging of protected headers from the encrypted
|
|
||||||
to the unencrypted parts, now not happening recursively
|
|
||||||
anymore. thanks @hpk and @r10s
|
|
||||||
|
|
||||||
- fix/optimize autocrypt gossip headers to only get
|
|
||||||
sent when there are more than 2 people in a chat.
|
|
||||||
thanks @link2xt
|
|
||||||
|
|
||||||
- fix displayname to use the authenticated name
|
|
||||||
when available (displayname as coming from contacts
|
|
||||||
themselves). thanks @simon-laux
|
|
||||||
|
|
||||||
- introduce preliminary support for offline autoconfig
|
|
||||||
for nauta provider. thanks @hpk42 @r10s
|
|
||||||
|
|
||||||
## 1.0.0-beta.15
|
|
||||||
|
|
||||||
- fix #994 attachment appeared doubled in chats (and where actually
|
|
||||||
downloaded after smtp-send). @hpk42
|
|
||||||
|
|
||||||
## 1.0.0-beta.14
|
|
||||||
|
|
||||||
- fix packaging issue with our rust-email fork, now we are tracking
|
|
||||||
master again there. hpk42
|
|
||||||
|
|
||||||
## 1.0.0-beta.13
|
|
||||||
|
|
||||||
- fix #976 -- unicode-issues in display-name of email addresses. @hpk42
|
|
||||||
|
|
||||||
- fix #985 group add/remove member bugs resulting in broken groups. @hpk42
|
|
||||||
|
|
||||||
- fix hanging IMAP connections -- we now detect with a 15second timeout
|
|
||||||
if we cannot terminate the IDLE IMAP protocol. @hpk42 @link2xt
|
|
||||||
|
|
||||||
- fix incoming multipart/mixed containing html, to show up as
|
|
||||||
attachments again. Fixes usage for simplebot which sends html
|
|
||||||
files for users to interact with the bot. @adbenitez @hpk42
|
|
||||||
|
|
||||||
- refinements to internal autocrypt-handling code, do not send
|
|
||||||
prefer-encrypt=nopreference as it is the default if no attribute
|
|
||||||
is present. @linkxt
|
|
||||||
|
|
||||||
- simplify, modularize and rustify several parts
|
|
||||||
of dc-core (general WIP). @link2xt @flub @hpk42 @r10s
|
|
||||||
|
|
||||||
- use async-email/async-smtp to handle SMTP connections, might
|
|
||||||
fix connection/reconnection issues. @link2xt
|
|
||||||
|
|
||||||
- more tests and refinements for dealing with blobstorage @flub @hpk42
|
|
||||||
|
|
||||||
- use a dedicated build-server for CI testing of core PRs
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0-beta.12
|
|
||||||
|
|
||||||
- fix python bindings to use core for copying attachments to blobdir
|
|
||||||
and fix core to actually do it. @hpk42
|
|
||||||
|
|
||||||
## 1.0.0-beta.11
|
|
||||||
|
|
||||||
- trigger reconnect more often on imap error states. Should fix an
|
|
||||||
issue observed when trying to empty a folder. @hpk42
|
|
||||||
|
|
||||||
- un-split qr tests: we fixed qr-securejoin protocol flakyness
|
|
||||||
last weeks. @hpk42
|
|
||||||
|
|
||||||
## 1.0.0-beta.10
|
|
||||||
|
|
||||||
- fix grpid-determination from in-reply-to and references headers. @hpk42
|
|
||||||
|
|
||||||
- only send Autocrypt-gossip headers on encrypted messages. @dignifiedquire
|
|
||||||
|
|
||||||
- fix reply-to-encrypted message to also be encrypted. @hpk42
|
|
||||||
|
|
||||||
- remove last unsafe code from dc_receive_imf :) @hpk42
|
|
||||||
|
|
||||||
- add experimental new dc_chat_get_info_json FFI/API so that desktop devs
|
|
||||||
can play with using it. @jikstra
|
|
||||||
|
|
||||||
- fix encoding of subjects and attachment-filenames @hpk42
|
|
||||||
@dignifiedquire .
|
|
||||||
|
|
||||||
## 1.0.0-beta.9
|
|
||||||
|
|
||||||
- historic: we now use the mailparse crate and lettre-email to generate mime
|
|
||||||
messages. This got rid of mmime completely, the C2rust generated port of the libetpan
|
|
||||||
mime-parse -- IOW 22KLocs of cumbersome code removed! see
|
|
||||||
https://github.com/deltachat/deltachat-core-rust/pull/904#issuecomment-561163330
|
|
||||||
many thanks @dignifiedquire for making everybody's life easier
|
|
||||||
and @jonhoo (from rust-imap fame) for suggesting to use the mailparse crate :)
|
|
||||||
|
|
||||||
- lots of improvements and better error handling in many rust modules
|
|
||||||
thanks @link2xt @flub @r10s, @hpk42 and @dignifiedquire
|
|
||||||
|
|
||||||
- @r10s introduced a new device chat which has an initial
|
|
||||||
welcome message. See
|
|
||||||
https://c.delta.chat/classdc__context__t.html#a1a2aad98bd23c1d21ee42374e241f389
|
|
||||||
for the main new FFI-API.
|
|
||||||
|
|
||||||
- fix moving self-sent messages, thanks @r10s, @flub, @hpk42
|
|
||||||
|
|
||||||
- fix flakyness/sometimes-failing verified/join-protocols,
|
|
||||||
thanks @flub, @r10s, @hpk42
|
|
||||||
|
|
||||||
- fix reply-to-encrypted message to keep encryption
|
|
||||||
|
|
||||||
- new DC_EVENT_SECUREJOIN_MEMBER_ADDED event
|
|
||||||
|
|
||||||
- many little fixes and rustifications (@link2xt, @flub, @hpk42)
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0-beta.8
|
|
||||||
|
|
||||||
- now uses async-email/async-imap as the new base
|
|
||||||
which makes imap-idle interruptible and thus fixes
|
|
||||||
several issues around the imap thread being in zombie state .
|
|
||||||
thanks @dignifiedquire, @hpk42 and @link2xt.
|
|
||||||
|
|
||||||
- fixes imap-protocol parsing bugs that lead to infinitely
|
|
||||||
repeated crashing while trying to receive messages with
|
|
||||||
a subjec that contained non-utf8. thanks @link2xt
|
|
||||||
|
|
||||||
- fixed logic to find encryption subkey -- previously
|
|
||||||
delta chat would use the primary key for encryption
|
|
||||||
(which works with RSA but not ECC). thanks @link2xt
|
|
||||||
|
|
||||||
- introduce a new device chat where core and UIs can
|
|
||||||
add "device" messages. Android uses it for an initial
|
|
||||||
welcome message. thanks @r10s
|
|
||||||
|
|
||||||
- fix time smearing (when two message are virtually send
|
|
||||||
in the same second, there would be misbehaviour because
|
|
||||||
we didn't persist smeared time). thanks @r10s
|
|
||||||
|
|
||||||
- fix double-dotted extensions like .html.zip or .tar.gz
|
|
||||||
to not mangle them when creating blobfiles. thanks @flub
|
|
||||||
|
|
||||||
- fix backup/exports where the wrong sql file would be modified,
|
|
||||||
leading to problems when exporting twice. thanks @hpk42
|
|
||||||
|
|
||||||
- several other little fixes and improvements
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.0-beta.7
|
## 1.0.0-beta.7
|
||||||
|
|
||||||
- fix location-streaming #782
|
- fix location-streaming #782
|
||||||
|
|||||||
2453
Cargo.lock
generated
64
Cargo.toml
@@ -1,42 +1,41 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deltachat"
|
name = "deltachat"
|
||||||
version = "1.33.0"
|
version = "1.0.0-beta.7"
|
||||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MPL-2.0"
|
license = "MPL"
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = true
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
deltachat_derive = { path = "./deltachat_derive" }
|
deltachat_derive = { path = "./deltachat_derive" }
|
||||||
|
mmime = { version = "0.1.2", path = "./mmime" }
|
||||||
|
|
||||||
libc = "0.2.51"
|
libc = "0.2.51"
|
||||||
pgp = { version = "0.5.1", default-features = false }
|
pgp = { version = "0.2.3", default-features = false }
|
||||||
hex = "0.4.0"
|
hex = "0.3.2"
|
||||||
sha2 = "0.8.0"
|
sha2 = "0.8.0"
|
||||||
rand = "0.7.0"
|
rand = "0.6.5"
|
||||||
smallvec = "1.0.0"
|
smallvec = "0.6.9"
|
||||||
reqwest = { version = "0.10.0", features = ["blocking", "json"] }
|
reqwest = { version = "0.9.15", default-features = false, features = ["rustls-tls"] }
|
||||||
num-derive = "0.3.0"
|
num-derive = "0.2.5"
|
||||||
num-traits = "0.2.6"
|
num-traits = "0.2.6"
|
||||||
async-smtp = "0.2"
|
lettre = { git = "https://github.com/deltachat/lettre", branch = "feat/rustls" }
|
||||||
email = { git = "https://github.com/deltachat/rust-email", branch = "master" }
|
async-imap = "0.1"
|
||||||
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
|
async-tls = "0.6"
|
||||||
async-imap = "0.2"
|
async-std = { version = "1.0", features = ["unstable"] }
|
||||||
async-native-tls = "0.3.1"
|
base64 = "0.10"
|
||||||
async-std = { version = "1.4", features = ["unstable"] }
|
|
||||||
base64 = "0.11"
|
|
||||||
charset = "0.1"
|
charset = "0.1"
|
||||||
percent-encoding = "2.0"
|
percent-encoding = "2.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
chrono = "0.4.6"
|
chrono = "0.4.6"
|
||||||
indexmap = "1.3.0"
|
failure = "0.1.5"
|
||||||
|
failure_derive = "0.1.5"
|
||||||
|
# TODO: make optional
|
||||||
|
rustyline = "4.1.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
regex = "1.1.6"
|
regex = "1.1.6"
|
||||||
rusqlite = { version = "0.21", features = ["bundled"] }
|
rusqlite = { version = "0.20", features = ["bundled"] }
|
||||||
r2d2_sqlite = "0.13.0"
|
r2d2_sqlite = "0.12.0"
|
||||||
r2d2 = "0.8.5"
|
r2d2 = "0.8.5"
|
||||||
strum = "0.16.0"
|
strum = "0.16.0"
|
||||||
strum_macros = "0.16.0"
|
strum_macros = "0.16.0"
|
||||||
@@ -45,21 +44,16 @@ backtrace = "0.3.33"
|
|||||||
byteorder = "1.3.1"
|
byteorder = "1.3.1"
|
||||||
itertools = "0.8.0"
|
itertools = "0.8.0"
|
||||||
image-meta = "0.1.0"
|
image-meta = "0.1.0"
|
||||||
quick-xml = "0.17.1"
|
quick-xml = "0.15.0"
|
||||||
escaper = "0.1.0"
|
escaper = "0.1.0"
|
||||||
bitflags = "1.1.0"
|
bitflags = "1.1.0"
|
||||||
|
jetscii = "0.4.4"
|
||||||
debug_stub_derive = "0.3.0"
|
debug_stub_derive = "0.3.0"
|
||||||
sanitize-filename = "0.2.1"
|
sanitize-filename = "0.2.1"
|
||||||
stop-token = { version = "0.1.1", features = ["unstable"] }
|
stop-token = { version = "0.1.1", features = ["unstable"] }
|
||||||
mailparse = "0.12.0"
|
rustls = "0.16.0"
|
||||||
encoded-words = { git = "https://github.com/async-email/encoded-words", branch="master" }
|
webpki-roots = "0.18.0"
|
||||||
native-tls = "0.2.3"
|
webpki = "0.21.0"
|
||||||
image = { version = "0.22.4", default-features=false, features = ["gif_codec", "jpeg", "ico", "png_codec", "pnm", "webp", "bmp"] }
|
|
||||||
pretty_env_logger = "0.3.1"
|
|
||||||
|
|
||||||
rustyline = { version = "4.1.0", optional = true }
|
|
||||||
thiserror = "1.0.14"
|
|
||||||
anyhow = "1.0.28"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.0"
|
tempfile = "3.0"
|
||||||
@@ -71,6 +65,7 @@ proptest = "0.9.4"
|
|||||||
members = [
|
members = [
|
||||||
"deltachat-ffi",
|
"deltachat-ffi",
|
||||||
"deltachat_derive",
|
"deltachat_derive",
|
||||||
|
"mmime",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@@ -80,11 +75,10 @@ path = "examples/simple.rs"
|
|||||||
[[example]]
|
[[example]]
|
||||||
name = "repl"
|
name = "repl"
|
||||||
path = "examples/repl/main.rs"
|
path = "examples/repl/main.rs"
|
||||||
required-features = ["rustyline"]
|
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["nightly"]
|
default = ["nightly", "ringbuf"]
|
||||||
vendored = ["async-native-tls/vendored", "reqwest/native-tls-vendored", "async-smtp/native-tls-vendored"]
|
vendored = []
|
||||||
nightly = ["pgp/nightly"]
|
nightly = ["pgp/nightly"]
|
||||||
|
ringbuf = ["pgp/ringbuf"]
|
||||||
|
|||||||
3
LICENSE
@@ -2,6 +2,9 @@ The files in this directory and under its subdirectories
|
|||||||
are (c) 2019 by Bjoern Petersen and contributors and released under the
|
are (c) 2019 by Bjoern Petersen and contributors and released under the
|
||||||
Mozilla Public License Version 2.0, see below for a copy.
|
Mozilla Public License Version 2.0, see below for a copy.
|
||||||
|
|
||||||
|
NOTE that the files in the "libs" directory are copyrighted by third parties
|
||||||
|
and come with their own respective licenses.
|
||||||
|
|
||||||
Mozilla Public License Version 2.0
|
Mozilla Public License Version 2.0
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
|
|||||||
38
README.md
@@ -1,9 +1,11 @@
|
|||||||
# Delta Chat Rust
|
# Delta Chat Rust
|
||||||
|
|
||||||
> Deltachat-core written in Rust
|
> Project porting deltachat-core to rust
|
||||||
|
|
||||||
[![CircleCI build status][circle-shield]][circle] [![Appveyor build status][appveyor-shield]][appveyor]
|
[![CircleCI build status][circle-shield]][circle] [![Appveyor build status][appveyor-shield]][appveyor]
|
||||||
|
|
||||||
|
Current commit on deltachat/deltachat-core: `12ef73c8e76185f9b78e844ea673025f56a959ab`.
|
||||||
|
|
||||||
## Installing Rust and Cargo
|
## Installing Rust and Cargo
|
||||||
|
|
||||||
To download and install the official compiler for the Rust programming language, and the Cargo package manager, run the command in your user environment:
|
To download and install the official compiler for the Rust programming language, and the Cargo package manager, run the command in your user environment:
|
||||||
@@ -14,12 +16,11 @@ curl https://sh.rustup.rs -sSf | sh
|
|||||||
|
|
||||||
## Using the CLI client
|
## Using the CLI client
|
||||||
|
|
||||||
Compile and run Delta Chat Core command line utility, using `cargo`:
|
Compile and run Delta Chat Core using `cargo`:
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo run --example repl -- ~/deltachat-db
|
cargo run --example repl -- /path/to/db
|
||||||
```
|
```
|
||||||
where ~/deltachat-db is the database file. Delta Chat will create it if it does not exist.
|
|
||||||
|
|
||||||
Configure your account (if not already configured):
|
Configure your account (if not already configured):
|
||||||
|
|
||||||
@@ -88,15 +89,6 @@ $ cargo test --all
|
|||||||
$ cargo build -p deltachat_ffi --release
|
$ cargo build -p deltachat_ffi --release
|
||||||
```
|
```
|
||||||
|
|
||||||
## Debugging environment variables
|
|
||||||
|
|
||||||
- `DCC_IMAP_DEBUG`: if set IMAP protocol commands and responses will be
|
|
||||||
printed
|
|
||||||
|
|
||||||
- `DCC_MIME_DEBUG`: if set outgoing and incoming message will be printed
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Expensive tests
|
### Expensive tests
|
||||||
|
|
||||||
Some tests are expensive and marked with `#[ignore]`, to run these
|
Some tests are expensive and marked with `#[ignore]`, to run these
|
||||||
@@ -109,27 +101,9 @@ $ cargo test -- --ignored
|
|||||||
|
|
||||||
- `vendored`: When using Openssl for TLS, this bundles a vendored version.
|
- `vendored`: When using Openssl for TLS, this bundles a vendored version.
|
||||||
- `nightly`: Enable nightly only performance and security related features.
|
- `nightly`: Enable nightly only performance and security related features.
|
||||||
|
- `ringbuf`: Enable the use of [`slice_deque`](https://github.com/gnzlbg/slice_deque) in pgp.
|
||||||
|
|
||||||
[circle-shield]: https://img.shields.io/circleci/project/github/deltachat/deltachat-core-rust/master.svg?style=flat-square
|
[circle-shield]: https://img.shields.io/circleci/project/github/deltachat/deltachat-core-rust/master.svg?style=flat-square
|
||||||
[circle]: https://circleci.com/gh/deltachat/deltachat-core-rust/
|
[circle]: https://circleci.com/gh/deltachat/deltachat-core-rust/
|
||||||
[appveyor-shield]: https://ci.appveyor.com/api/projects/status/lqpegel3ld4ipxj8/branch/master?style=flat-square
|
[appveyor-shield]: https://ci.appveyor.com/api/projects/status/lqpegel3ld4ipxj8/branch/master?style=flat-square
|
||||||
[appveyor]: https://ci.appveyor.com/project/dignifiedquire/deltachat-core-rust/branch/master
|
[appveyor]: https://ci.appveyor.com/project/dignifiedquire/deltachat-core-rust/branch/master
|
||||||
|
|
||||||
## Language bindings and frontend projects
|
|
||||||
|
|
||||||
Language bindings are available for:
|
|
||||||
|
|
||||||
- [C](https://c.delta.chat)
|
|
||||||
- [Node.js](https://www.npmjs.com/package/deltachat-node)
|
|
||||||
- [Python](https://py.delta.chat)
|
|
||||||
- [Go](https://github.com/hugot/go-deltachat/)
|
|
||||||
- **Java** and **Swift** (contained in the Android/iOS repos)
|
|
||||||
|
|
||||||
The following "frontend" projects make use of the Rust-library
|
|
||||||
or its language bindings:
|
|
||||||
|
|
||||||
- [Android](https://github.com/deltachat/deltachat-android)
|
|
||||||
- [iOS](https://github.com/deltachat/deltachat-ios)
|
|
||||||
- [Desktop](https://github.com/deltachat/deltachat-desktop)
|
|
||||||
- [Pidgin](https://code.ur.gs/lupine/purple-plugin-delta/)
|
|
||||||
- several **Bots**
|
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ environment:
|
|||||||
|
|
||||||
install:
|
install:
|
||||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||||
- rustup-init -yv --default-toolchain nightly-2020-03-12
|
- rustup-init -yv --default-toolchain nightly-2019-07-10
|
||||||
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||||
- rustc -vV
|
- rustc -vV
|
||||||
- cargo -vV
|
- cargo -vV
|
||||||
|
- cargo update
|
||||||
|
|
||||||
build: false
|
build: false
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 3.6 KiB |
@@ -1,83 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
inkscape:export-ydpi="409.60001"
|
|
||||||
inkscape:export-xdpi="409.60001"
|
|
||||||
inkscape:export-filename="/Users/bpetersen/projects/deltachat-core-rust/assets/icon-device.png"
|
|
||||||
version="1.0"
|
|
||||||
width="60"
|
|
||||||
height="60"
|
|
||||||
viewBox="0 0 45 45"
|
|
||||||
preserveAspectRatio="xMidYMid meet"
|
|
||||||
id="svg4344"
|
|
||||||
sodipodi:docname="icon-device.svg"
|
|
||||||
inkscape:version="1.0beta1 (32d4812, 2019-09-19)">
|
|
||||||
<defs
|
|
||||||
id="defs4348" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
inkscape:snap-global="false"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="1600"
|
|
||||||
inkscape:window-height="1035"
|
|
||||||
id="namedview4346"
|
|
||||||
showgrid="false"
|
|
||||||
units="px"
|
|
||||||
inkscape:zoom="3.959798"
|
|
||||||
inkscape:cx="28.322498"
|
|
||||||
inkscape:cy="24.898474"
|
|
||||||
inkscape:window-x="45"
|
|
||||||
inkscape:window-y="23"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="svg4344" />
|
|
||||||
<metadata
|
|
||||||
id="metadata4336">
|
|
||||||
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<rect
|
|
||||||
y="-4.4408921e-16"
|
|
||||||
x="0"
|
|
||||||
height="45"
|
|
||||||
width="45"
|
|
||||||
id="rect860"
|
|
||||||
style="opacity:1;fill:#76868b;fill-opacity:1;stroke-width:0.819271" />
|
|
||||||
<g
|
|
||||||
fill="#000000"
|
|
||||||
stroke="none"
|
|
||||||
style="fill:#ffffff;fill-opacity:1"
|
|
||||||
transform="matrix(0.00255113,0,0,-0.00255113,5.586152,38.200477)"
|
|
||||||
id="g4342">
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1"
|
|
||||||
d="m 8175,12765 c -703,-114 -1248,-608 -1387,-1258 -17,-82 -21,-136 -22,-277 0,-202 15,-307 70,-470 149,-446 499,-733 1009,-828 142,-26 465,-23 619,6 691,131 1201,609 1328,1244 31,158 31,417 0,565 -114,533 -482,889 -1038,1004 -133,27 -448,35 -579,14 z"
|
|
||||||
id="path4338"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
<path
|
|
||||||
style="fill:#ffffff;fill-opacity:1"
|
|
||||||
d="m 7070,9203 c -212,-20 -275,-27 -397,-48 -691,-117 -1400,-444 -2038,-940 -182,-142 -328,-270 -585,-517 -595,-571 -911,-974 -927,-1181 -6,-76 11,-120 69,-184 75,-80 159,-108 245,-79 109,37 263,181 632,595 539,606 774,826 1035,969 135,75 231,105 341,106 82,1 94,-2 138,-27 116,-68 161,-209 122,-376 -9,-36 -349,-868 -757,-1850 -407,-982 -785,-1892 -838,-2021 -287,-694 -513,-1389 -615,-1889 -70,-342 -90,-683 -52,-874 88,-440 381,-703 882,-792 124,-23 401,-30 562,-16 783,69 1674,461 2561,1125 796,596 1492,1354 1607,1751 43,146 -33,308 -168,360 -61,23 -100,15 -173,-36 -105,-74 -202,-170 -539,-529 -515,-551 -762,-783 -982,-927 -251,-164 -437,-186 -543,-65 -56,64 -74,131 -67,247 13,179 91,434 249,815 135,324 1588,4102 1646,4280 106,325 151,561 159,826 9,281 -22,463 -112,652 -58,122 -114,199 -211,292 -245,233 -582,343 -1044,338 -91,-1 -181,-3 -200,-5 z"
|
|
||||||
id="path4340"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,71 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
inkscape:export-ydpi="409.60001"
|
|
||||||
inkscape:export-xdpi="409.60001"
|
|
||||||
inkscape:export-filename="/home/kerle/test-icon.png"
|
|
||||||
version="1.0"
|
|
||||||
width="60"
|
|
||||||
height="60"
|
|
||||||
viewBox="0 0 45 45"
|
|
||||||
preserveAspectRatio="xMidYMid meet"
|
|
||||||
id="svg4344"
|
|
||||||
sodipodi:docname="icon-saved-messages.svg"
|
|
||||||
inkscape:version="1.0beta1 (32d4812, 2019-09-19)">
|
|
||||||
<defs
|
|
||||||
id="defs4348" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="1395"
|
|
||||||
inkscape:window-height="855"
|
|
||||||
id="namedview4346"
|
|
||||||
showgrid="false"
|
|
||||||
units="px"
|
|
||||||
inkscape:zoom="4"
|
|
||||||
inkscape:cx="29.308676"
|
|
||||||
inkscape:cy="49.03624"
|
|
||||||
inkscape:window-x="89"
|
|
||||||
inkscape:window-y="108"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="svg4344"
|
|
||||||
inkscape:lockguides="false" />
|
|
||||||
<metadata
|
|
||||||
id="metadata4336">
|
|
||||||
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<rect
|
|
||||||
y="0"
|
|
||||||
x="0"
|
|
||||||
height="45"
|
|
||||||
width="45"
|
|
||||||
id="rect1420"
|
|
||||||
style="fill:#87aade;fill-opacity:1;stroke:none;stroke-width:0.968078" />
|
|
||||||
<path
|
|
||||||
id="rect846"
|
|
||||||
style="fill:#ffffff;stroke-width:0.58409804"
|
|
||||||
d="M 13.5,7.5 V 39 h 0.08654 L 22.533801,29.370239 31.482419,39 h 0.01758 V 7.5 Z m 9.004056,4.108698 1.879508,4.876388 5.039514,0.359779 -3.879358,3.363728 1.227764,5.095749 -4.276893,-2.796643 -4.280949,2.788618 1.237229,-5.093073 -3.873949,-3.371754 5.040866,-0.350417 z"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 113 KiB |
@@ -1,46 +1,52 @@
|
|||||||
|
|
||||||
# Continuous Integration Scripts for Delta Chat
|
# Continuous Integration Scripts for Delta Chat
|
||||||
|
|
||||||
Continuous Integration, run through CircleCI and an own build machine.
|
Continuous Integration is run through CircleCI
|
||||||
|
but is largely independent of it.
|
||||||
## Description of scripts
|
|
||||||
|
|
||||||
- `../.circleci/config.yml` describing the build jobs that are run
|
|
||||||
by Circle-CI
|
|
||||||
|
|
||||||
- `remote_tests_python.sh` rsyncs to a build machine and runs
|
|
||||||
`run-python-test.sh` remotely on the build machine.
|
|
||||||
|
|
||||||
- `remote_tests_rust.sh` rsyncs to the build machine and runs
|
|
||||||
`run-rust-test.sh` remotely on the build machine.
|
|
||||||
|
|
||||||
- `doxygen/Dockerfile` specifies an image that contains
|
|
||||||
the doxygen tool which is used by `run-doxygen.sh`
|
|
||||||
to generate C-docs which are then uploaded
|
|
||||||
via `ci_upload.sh` to `https://c.delta.chat/_unofficial_unreleased_docs/<BRANCH>`
|
|
||||||
(and the master branch is linked to https://c.delta.chat proper).
|
|
||||||
|
|
||||||
|
|
||||||
## Triggering runs on the build machine locally (fast!)
|
## Generating docker containers for performing build step work
|
||||||
|
|
||||||
There is experimental support for triggering a remote Python or Rust test run
|
All tests, docs and wheel building is run in docker containers:
|
||||||
from your local checkout/branch. You will need to be authorized to login to
|
|
||||||
the build machine (ask your friendly sysadmin on #deltachat freenode) to type::
|
|
||||||
|
|
||||||
ci_scripts/manual_remote_tests.sh rust
|
- **coredeps/Dockerfile** specifies an image that contains all
|
||||||
ci_scripts/manual_remote_tests.sh python
|
of Delta Chat's core dependencies as linkable libraries.
|
||||||
|
It also serves to run python tests and build wheels
|
||||||
|
(binary packages for Python).
|
||||||
|
|
||||||
This will **rsync** your current checkout to the remote build machine
|
- **doxygen/Dockerfile** specifies an image that contains
|
||||||
(no need to commit before) and then run either rust or python tests.
|
the doxygen tool which is used to generate C-docs.
|
||||||
|
|
||||||
# Outdated files (for later re-use)
|
To run tests locally you can pull existing images from "docker.io",
|
||||||
|
the hub for sharing Docker images::
|
||||||
|
|
||||||
`coredeps/Dockerfile` specifies an image that contains all
|
docker pull deltachat/coredeps
|
||||||
of Delta Chat's core dependencies. It used to run
|
docker pull deltachat/doxygen
|
||||||
python tests and build wheels (binary packages for Python)
|
|
||||||
|
|
||||||
You can build the docker images yourself locally
|
or you can build the docker images yourself locally
|
||||||
to avoid the relatively large download::
|
to avoid the relatively large download::
|
||||||
|
|
||||||
cd ci_scripts # where all CI things are
|
cd ci_scripts # where all CI things are
|
||||||
docker build -t deltachat/coredeps docker-coredeps
|
docker build -t deltachat/coredeps docker-coredeps
|
||||||
docker build -t deltachat/doxygen docker-doxygen
|
docker build -t deltachat/doxygen docker-doxygen
|
||||||
|
|
||||||
|
## ci_run.sh (main entrypoint called by circle-ci)
|
||||||
|
|
||||||
|
Once you have the docker images available
|
||||||
|
you can run python testing, documentation generation
|
||||||
|
and building binary wheels::
|
||||||
|
|
||||||
|
sh DOCS=1 TESTS=1 ci_scripts/ci_run.sh
|
||||||
|
|
||||||
|
## ci_upload.sh (uploading artifacts on success)
|
||||||
|
|
||||||
|
- python docs to `https://py.delta.chat/_unofficial_unreleased_docs/<BRANCH>`
|
||||||
|
|
||||||
|
- doxygen docs to `https://c.delta.chat/_unofficial_unreleased_docs/<BRANCH>`
|
||||||
|
|
||||||
|
- python wheels to `https://m.devpi.net/dc/<BRANCH>`
|
||||||
|
so that you install fully self-contained wheels like this:
|
||||||
|
`pip install -U -i https://m.devpi.net/dc/<BRANCH> deltachat`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,15 +17,13 @@ export BRANCH=${CIRCLE_BRANCH:?specify branch for uploading purposes}
|
|||||||
# python docs to py.delta.chat
|
# python docs to py.delta.chat
|
||||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@py.delta.chat mkdir -p build/${BRANCH}
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@py.delta.chat mkdir -p build/${BRANCH}
|
||||||
rsync -avz \
|
rsync -avz \
|
||||||
--delete \
|
|
||||||
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
|
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
|
||||||
"$PYDOCDIR/html/" \
|
"$PYDOCDIR/html/" \
|
||||||
delta@py.delta.chat:build/${BRANCH}
|
delta@py.delta.chat:build/${BRANCH}
|
||||||
|
|
||||||
# C docs to c.delta.chat
|
# C docs to c.delta.chat
|
||||||
ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@c.delta.chat mkdir -p build-c/${BRANCH}
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@c.delta.chat mkdir -p build-c/${BRANCH}
|
||||||
rsync -avz \
|
rsync -avz \
|
||||||
--delete \
|
|
||||||
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
|
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
|
||||||
"$DOXYDOCDIR/html/" \
|
"$DOXYDOCDIR/html/" \
|
||||||
delta@c.delta.chat:build-c/${BRANCH}
|
delta@c.delta.chat:build-c/${BRANCH}
|
||||||
@@ -37,7 +35,6 @@ echo -----------------------
|
|||||||
# Bundle external shared libraries into the wheels
|
# Bundle external shared libraries into the wheels
|
||||||
pushd $WHEELHOUSEDIR
|
pushd $WHEELHOUSEDIR
|
||||||
|
|
||||||
pip3 install -U pip setuptools
|
|
||||||
pip3 install devpi-client
|
pip3 install devpi-client
|
||||||
devpi use https://m.devpi.net
|
devpi use https://m.devpi.net
|
||||||
devpi login dc --password $DEVPI_LOGIN
|
devpi login dc --password $DEVPI_LOGIN
|
||||||
@@ -49,9 +46,6 @@ devpi use dc/$N_BRANCH || {
|
|||||||
devpi use dc/$N_BRANCH
|
devpi use dc/$N_BRANCH
|
||||||
}
|
}
|
||||||
devpi index $N_BRANCH bases=/root/pypi
|
devpi index $N_BRANCH bases=/root/pypi
|
||||||
devpi upload deltachat*
|
devpi upload deltachat*.whl
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
# remove devpi non-master dc indices if thy are too old
|
|
||||||
python ci_scripts/cleanup_devpi_indices.py
|
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
"""
|
|
||||||
Remove old "dc" indices except for master which always stays.
|
|
||||||
|
|
||||||
"""
|
|
||||||
from requests import Session
|
|
||||||
import datetime
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
MAXDAYS=7
|
|
||||||
|
|
||||||
session = Session()
|
|
||||||
session.headers["Accept"] = "application/json"
|
|
||||||
|
|
||||||
|
|
||||||
def get_indexes(baseurl, username):
|
|
||||||
response = session.get(baseurl + username)
|
|
||||||
assert response.status_code == 200
|
|
||||||
result = response.json()["result"]
|
|
||||||
return result["indexes"]
|
|
||||||
|
|
||||||
|
|
||||||
def get_projectnames(baseurl, username, indexname):
|
|
||||||
response = session.get(baseurl + username + "/" + indexname)
|
|
||||||
assert response.status_code == 200
|
|
||||||
result = response.json()["result"]
|
|
||||||
return result["projects"]
|
|
||||||
|
|
||||||
|
|
||||||
def get_release_dates(baseurl, username, indexname, projectname):
|
|
||||||
response = session.get(baseurl + username + "/" + indexname + "/" + projectname)
|
|
||||||
assert response.status_code == 200
|
|
||||||
result = response.json()["result"]
|
|
||||||
dates = set()
|
|
||||||
for value in result.values():
|
|
||||||
if "+links" not in value:
|
|
||||||
continue
|
|
||||||
for link in value["+links"]:
|
|
||||||
for log in link["log"]:
|
|
||||||
dates.add(tuple(log["when"]))
|
|
||||||
return dates
|
|
||||||
|
|
||||||
|
|
||||||
def run():
|
|
||||||
baseurl = "https://m.devpi.net/"
|
|
||||||
username = "dc"
|
|
||||||
for indexname in get_indexes(baseurl, username):
|
|
||||||
projectnames = get_projectnames(baseurl, username, indexname)
|
|
||||||
if indexname == "master" or not indexname:
|
|
||||||
continue
|
|
||||||
clear_index = not projectnames
|
|
||||||
for projectname in projectnames:
|
|
||||||
dates = get_release_dates(baseurl, username, indexname, projectname)
|
|
||||||
if not dates:
|
|
||||||
print(
|
|
||||||
"%s has no releases" % (baseurl + username + "/" + indexname),
|
|
||||||
file=sys.stderr)
|
|
||||||
date = datetime.datetime.now()
|
|
||||||
else:
|
|
||||||
date = datetime.datetime(*max(dates))
|
|
||||||
if (datetime.datetime.now() - date) > datetime.timedelta(days=MAXDAYS):
|
|
||||||
assert username and indexname
|
|
||||||
clear_index = True
|
|
||||||
break
|
|
||||||
if clear_index:
|
|
||||||
url = baseurl + username + "/" + indexname
|
|
||||||
subprocess.check_call(["devpi", "index", "-y", "--delete", url])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run()
|
|
||||||
@@ -5,11 +5,14 @@ RUN echo /usr/local/lib64 > /etc/ld.so.conf.d/local.conf && \
|
|||||||
echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf
|
echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf
|
||||||
ENV PKG_CONFIG_PATH /usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig
|
ENV PKG_CONFIG_PATH /usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig
|
||||||
|
|
||||||
# Install a recent Perl, needed to install the openssl crate
|
# Install a recent Perl, needed to install OpenSSL
|
||||||
ADD deps/build_perl.sh /builder/build_perl.sh
|
ADD deps/build_perl.sh /builder/build_perl.sh
|
||||||
RUN rm /usr/bin/perl
|
|
||||||
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1
|
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_perl.sh && cd .. && rm -r tmp1
|
||||||
|
|
||||||
|
# Install OpenSSL
|
||||||
|
ADD deps/build_openssl.sh /builder/build_openssl.sh
|
||||||
|
RUN mkdir tmp1 && cd tmp1 && bash /builder/build_openssl.sh && cd .. && rm -r tmp1
|
||||||
|
|
||||||
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
|
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
|
||||||
|
|
||||||
# Install python tools (auditwheels,tox, ...)
|
# Install python tools (auditwheels,tox, ...)
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
set -e -x
|
set -e -x
|
||||||
|
|
||||||
# Install Rust
|
# Install Rust
|
||||||
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2020-03-12 -y
|
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-09-12 -y
|
||||||
export PATH=/root/.cargo/bin:$PATH
|
export PATH=/root/.cargo/bin:$PATH
|
||||||
rustc --version
|
rustc --version
|
||||||
|
|
||||||
# remove some 300-400 MB that we don't need for automated builds
|
|
||||||
rm -rf /root/.rustup/toolchains/nightly-2020-03-12-x86_64-unknown-linux-gnu/share/
|
|
||||||
|
|||||||
49
ci_scripts/docker-coredeps/deps/run_all.sh
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Build the Delta Chat C/Rust library
|
||||||
|
#
|
||||||
|
set -e -x
|
||||||
|
|
||||||
|
# perform clean build of core and install
|
||||||
|
export TOXWORKDIR=.docker-tox
|
||||||
|
|
||||||
|
# build core library
|
||||||
|
|
||||||
|
cargo build --release -p deltachat_ffi
|
||||||
|
|
||||||
|
# configure access to a base python and
|
||||||
|
# to several python interpreters needed by tox below
|
||||||
|
export PATH=$PATH:/opt/python/cp35-cp35m/bin
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
|
pushd /bin
|
||||||
|
ln -s /opt/python/cp27-cp27m/bin/python2.7
|
||||||
|
ln -s /opt/python/cp36-cp36m/bin/python3.6
|
||||||
|
ln -s /opt/python/cp37-cp37m/bin/python3.7
|
||||||
|
popd
|
||||||
|
|
||||||
|
#
|
||||||
|
# run python tests
|
||||||
|
#
|
||||||
|
|
||||||
|
if [ -n "$TESTS" ]; then
|
||||||
|
|
||||||
|
echo ----------------
|
||||||
|
echo run python tests
|
||||||
|
echo ----------------
|
||||||
|
|
||||||
|
pushd python
|
||||||
|
# first run all tests ...
|
||||||
|
rm -rf tests/__pycache__
|
||||||
|
rm -rf src/deltachat/__pycache__
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
|
tox --workdir "$TOXWORKDIR" -e py27,py35,py36,py37
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ -n "$DOCS" ]; then
|
||||||
|
echo -----------------------
|
||||||
|
echo generating python docs
|
||||||
|
echo -----------------------
|
||||||
|
(cd python && tox --workdir "$TOXWORKDIR" -e doc)
|
||||||
|
fi
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -xe
|
|
||||||
export CIRCLE_JOB=remote_tests_${1:?need to specify 'rust' or 'python'}
|
|
||||||
export CIRCLE_BUILD_NUM=$USER
|
|
||||||
export CIRCLE_BRANCH=`git branch | grep \* | cut -d ' ' -f2`
|
|
||||||
export CIRCLE_PROJECT_REPONAME=$(basename `git rev-parse --show-toplevel`)
|
|
||||||
|
|
||||||
time bash ci_scripts/$CIRCLE_JOB.sh
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
|
|
||||||
env:
|
|
||||||
RUSTFLAGS: -Dwarnings
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_and_test:
|
|
||||||
name: Build and test
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
|
||||||
rust: [nightly]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@master
|
|
||||||
|
|
||||||
- name: Install ${{ matrix.rust }}
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: ${{ matrix.rust }}
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: check
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
if: matrix.rust == 'nightly'
|
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: --all --bins --examples --tests
|
|
||||||
|
|
||||||
- name: tests
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: --all
|
|
||||||
|
|
||||||
- name: tests ignored
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: --all --release -- --ignored
|
|
||||||
|
|
||||||
check_fmt:
|
|
||||||
name: Checking fmt and docs
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@master
|
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: nightly
|
|
||||||
override: true
|
|
||||||
components: rustfmt
|
|
||||||
|
|
||||||
- name: fmt
|
|
||||||
run: cargo fmt --all -- --check
|
|
||||||
|
|
||||||
# clippy_check:
|
|
||||||
# name: Clippy check
|
|
||||||
# runs-on: ubuntu-latest
|
|
||||||
#
|
|
||||||
# steps:
|
|
||||||
# - uses: actions/checkout@v1
|
|
||||||
# - uses: actions-rs/toolchain@v1
|
|
||||||
# with:
|
|
||||||
# profile: minimal
|
|
||||||
# toolchain: nightly
|
|
||||||
# override: true
|
|
||||||
# components: clippy
|
|
||||||
#
|
|
||||||
# - name: clippy
|
|
||||||
# run: cargo clippy --all
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Build the Delta Chat C/Rust library typically run in a docker
|
|
||||||
# container that contains all library deps but should also work
|
|
||||||
# outside if you have the dependencies installed on your system.
|
|
||||||
|
|
||||||
set -e -x
|
|
||||||
|
|
||||||
# Perform clean build of core and install.
|
|
||||||
export TOXWORKDIR=.docker-tox
|
|
||||||
|
|
||||||
# install core lib
|
|
||||||
|
|
||||||
export PATH=/root/.cargo/bin:$PATH
|
|
||||||
cargo build --release -p deltachat_ffi
|
|
||||||
# cargo test --all --all-features
|
|
||||||
|
|
||||||
# Statically link against libdeltachat.a.
|
|
||||||
export DCC_RS_DEV=$(pwd)
|
|
||||||
|
|
||||||
# Configure access to a base python and to several python interpreters
|
|
||||||
# needed by tox below.
|
|
||||||
export PATH=$PATH:/opt/python/cp35-cp35m/bin
|
|
||||||
export PYTHONDONTWRITEBYTECODE=1
|
|
||||||
pushd /bin
|
|
||||||
ln -s /opt/python/cp27-cp27m/bin/python2.7
|
|
||||||
ln -s /opt/python/cp36-cp36m/bin/python3.6
|
|
||||||
ln -s /opt/python/cp37-cp37m/bin/python3.7
|
|
||||||
popd
|
|
||||||
|
|
||||||
if [ -n "$TESTS" ]; then
|
|
||||||
|
|
||||||
pushd python
|
|
||||||
# prepare a clean tox run
|
|
||||||
rm -rf tests/__pycache__
|
|
||||||
rm -rf src/deltachat/__pycache__
|
|
||||||
export PYTHONDONTWRITEBYTECODE=1
|
|
||||||
|
|
||||||
# run tox. The circle-ci project env-var-setting DCC_PY_LIVECONFIG
|
|
||||||
# allows running of "liveconfig" tests but for speed reasons
|
|
||||||
# we run them only for the highest python version we support
|
|
||||||
|
|
||||||
# we split out qr-tests run to minimize likelyness of flaky tests
|
|
||||||
# (some qr tests are pretty heavy in terms of send/received
|
|
||||||
# messages and rust's imap code likely has concurrency problems)
|
|
||||||
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "not qr"
|
|
||||||
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "qr"
|
|
||||||
unset DCC_PY_LIVECONFIG
|
|
||||||
unset DCC_NEW_TMP_EMAIL
|
|
||||||
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
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export BRANCH=${CIRCLE_BRANCH:?branch to build}
|
|
||||||
export REPONAME=${CIRCLE_PROJECT_REPONAME:?repository name}
|
|
||||||
export SSHTARGET=${SSHTARGET-ci@b1.delta.chat}
|
|
||||||
|
|
||||||
# we construct the BUILDDIR such that we can easily share the
|
|
||||||
# CARGO_TARGET_DIR between runs ("..")
|
|
||||||
export BUILDDIR=ci_builds/$REPONAME/$BRANCH/${CIRCLE_JOB:?jobname}/${CIRCLE_BUILD_NUM:?circle-build-number}
|
|
||||||
|
|
||||||
echo "--- Copying files to $SSHTARGET:$BUILDDIR"
|
|
||||||
|
|
||||||
set -xe
|
|
||||||
|
|
||||||
ssh -oBatchMode=yes -oStrictHostKeyChecking=no $SSHTARGET mkdir -p "$BUILDDIR"
|
|
||||||
git ls-files >.rsynclist
|
|
||||||
# we seem to need .git for setuptools_scm versioning
|
|
||||||
find .git >>.rsynclist
|
|
||||||
rsync --delete --files-from=.rsynclist -az ./ "$SSHTARGET:$BUILDDIR"
|
|
||||||
|
|
||||||
set +x
|
|
||||||
|
|
||||||
# we have to create a remote file for the remote-docker run
|
|
||||||
# so we can do a simple ssh command with a TTY
|
|
||||||
# so that when our job dies, all container-runs are aborted.
|
|
||||||
# sidenote: the circle-ci machinery will kill ongoing jobs
|
|
||||||
# if there are new commits and we want to ensure that
|
|
||||||
# everything is terminated/cleaned up and we have no orphaned
|
|
||||||
# useless still-running docker-containers consuming resources.
|
|
||||||
|
|
||||||
ssh $SSHTARGET bash -c "cat >$BUILDDIR/exec_docker_run" <<_HERE
|
|
||||||
set +x -e
|
|
||||||
cd $BUILDDIR
|
|
||||||
export DCC_PY_LIVECONFIG=$DCC_PY_LIVECONFIG
|
|
||||||
export DCC_NEW_TMP_EMAIL=$DCC_NEW_TMP_EMAIL
|
|
||||||
set -x
|
|
||||||
|
|
||||||
# run everything else inside docker
|
|
||||||
docker run -e DCC_NEW_TMP_EMAIL -e DCC_PY_LIVECONFIG \
|
|
||||||
--rm -it -v \$(pwd):/mnt -w /mnt \
|
|
||||||
deltachat/coredeps ci_scripts/run_all.sh
|
|
||||||
|
|
||||||
_HERE
|
|
||||||
|
|
||||||
echo "--- Running $CIRCLE_JOB remotely"
|
|
||||||
|
|
||||||
ssh -t $SSHTARGET bash "$BUILDDIR/exec_docker_run"
|
|
||||||
mkdir -p workspace
|
|
||||||
rsync -avz "$SSHTARGET:$BUILDDIR/python/.docker-tox/wheelhouse/*manylinux1*" workspace/wheelhouse/
|
|
||||||
rsync -avz "$SSHTARGET:$BUILDDIR/python/.docker-tox/dist/*" workspace/wheelhouse/
|
|
||||||
rsync -avz "$SSHTARGET:$BUILDDIR/python/doc/_build/" workspace/py-docs
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export BRANCH=${CIRCLE_BRANCH:?branch to build}
|
|
||||||
export REPONAME=${CIRCLE_PROJECT_REPONAME:?repository name}
|
|
||||||
export SSHTARGET=${SSHTARGET-ci@b1.delta.chat}
|
|
||||||
|
|
||||||
# we construct the BUILDDIR such that we can easily share the
|
|
||||||
# CARGO_TARGET_DIR between runs ("..")
|
|
||||||
export BUILDDIR=ci_builds/$REPONAME/$BRANCH/${CIRCLE_JOB:?jobname}/${CIRCLE_BUILD_NUM:?circle-build-number}
|
|
||||||
|
|
||||||
echo "--- Copying files to $SSHTARGET:$BUILDDIR"
|
|
||||||
|
|
||||||
set -xe
|
|
||||||
|
|
||||||
ssh -oBatchMode=yes -oStrictHostKeyChecking=no $SSHTARGET mkdir -p "$BUILDDIR"
|
|
||||||
git ls-files >.rsynclist
|
|
||||||
# we seem to need .git for setuptools_scm versioning
|
|
||||||
find .git >>.rsynclist
|
|
||||||
rsync --delete --files-from=.rsynclist -az ./ "$SSHTARGET:$BUILDDIR"
|
|
||||||
|
|
||||||
set +x
|
|
||||||
|
|
||||||
echo "--- Running $CIRCLE_JOB remotely"
|
|
||||||
|
|
||||||
ssh $SSHTARGET <<_HERE
|
|
||||||
set +x -e
|
|
||||||
cd $BUILDDIR
|
|
||||||
# let's share the target dir with our last run on this branch/job-type
|
|
||||||
# cargo will make sure to block/unblock us properly
|
|
||||||
export CARGO_TARGET_DIR=\`pwd\`/../target
|
|
||||||
export TARGET=release
|
|
||||||
export DCC_PY_LIVECONFIG=$DCC_PY_LIVECONFIG
|
|
||||||
export DCC_NEW_TMP_EMAIL=$DCC_NEW_TMP_EMAIL
|
|
||||||
|
|
||||||
#we rely on tox/virtualenv being available in the host
|
|
||||||
#rm -rf virtualenv venv
|
|
||||||
#virtualenv -q -p python3.7 venv
|
|
||||||
#source venv/bin/activate
|
|
||||||
#pip install -q tox virtualenv
|
|
||||||
|
|
||||||
set -x
|
|
||||||
which python
|
|
||||||
source \$HOME/venv/bin/activate
|
|
||||||
which python
|
|
||||||
|
|
||||||
bash ci_scripts/run-python-test.sh
|
|
||||||
_HERE
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export BRANCH=${CIRCLE_BRANCH:?branch to build}
|
|
||||||
export REPONAME=${CIRCLE_PROJECT_REPONAME:?repository name}
|
|
||||||
export SSHTARGET=${SSHTARGET-ci@b1.delta.chat}
|
|
||||||
|
|
||||||
# we construct the BUILDDIR such that we can easily share the
|
|
||||||
# CARGO_TARGET_DIR between runs ("..")
|
|
||||||
export BUILDDIR=ci_builds/$REPONAME/$BRANCH/${CIRCLE_JOB:?jobname}/${CIRCLE_BUILD_NUM:?circle-build-number}
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "--- Copying files to $SSHTARGET:$BUILDDIR"
|
|
||||||
|
|
||||||
ssh -oBatchMode=yes -oStrictHostKeyChecking=no $SSHTARGET mkdir -p "$BUILDDIR"
|
|
||||||
git ls-files >.rsynclist
|
|
||||||
rsync --delete --files-from=.rsynclist -az ./ "$SSHTARGET:$BUILDDIR"
|
|
||||||
|
|
||||||
echo "--- Running $CIRCLE_JOB remotely"
|
|
||||||
|
|
||||||
ssh $SSHTARGET <<_HERE
|
|
||||||
set +x -e
|
|
||||||
cd $BUILDDIR
|
|
||||||
# let's share the target dir with our last run on this branch/job-type
|
|
||||||
# cargo will make sure to block/unblock us properly
|
|
||||||
export CARGO_TARGET_DIR=\`pwd\`/../target
|
|
||||||
export TARGET=x86_64-unknown-linux-gnu
|
|
||||||
export RUSTC_WRAPPER=sccache
|
|
||||||
|
|
||||||
bash ci_scripts/run-rust-test.sh
|
|
||||||
_HERE
|
|
||||||
|
|
||||||
@@ -3,4 +3,5 @@
|
|||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
cd deltachat-ffi
|
cd deltachat-ffi
|
||||||
PROJECT_NUMBER=$(git log -1 --format "%h (%cd)") doxygen
|
doxygen
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Run functional tests for Delta Chat core using the python bindings
|
|
||||||
# and tox/pytest.
|
|
||||||
|
|
||||||
set -e -x
|
|
||||||
|
|
||||||
# for core-building and python install step
|
|
||||||
export DCC_RS_TARGET=debug
|
|
||||||
export DCC_RS_DEV=`pwd`
|
|
||||||
|
|
||||||
cd python
|
|
||||||
|
|
||||||
python install_python_bindings.py onlybuild
|
|
||||||
|
|
||||||
# remove and inhibit writing PYC files
|
|
||||||
rm -rf tests/__pycache__
|
|
||||||
rm -rf src/deltachat/__pycache__
|
|
||||||
export PYTHONDONTWRITEBYTECODE=1
|
|
||||||
|
|
||||||
# run python tests (tox invokes pytest to run tests in python/tests)
|
|
||||||
#TOX_PARALLEL_NO_SPINNER=1 tox -e lint,doc
|
|
||||||
tox -e lint
|
|
||||||
tox -e doc,py37
|
|
||||||
50
ci_scripts/run-python.sh
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Build the Delta Chat C/Rust library typically run in a docker
|
||||||
|
# container that contains all library deps but should also work
|
||||||
|
# outside if you have the dependencies installed on your system.
|
||||||
|
|
||||||
|
set -e -x
|
||||||
|
|
||||||
|
# Perform clean build of core and install.
|
||||||
|
export TOXWORKDIR=.docker-tox
|
||||||
|
|
||||||
|
# install core lib
|
||||||
|
|
||||||
|
export PATH=/root/.cargo/bin:$PATH
|
||||||
|
cargo build --release -p deltachat_ffi
|
||||||
|
|
||||||
|
# Statically link against libdeltachat.a.
|
||||||
|
export DCC_RS_DEV=$(pwd)
|
||||||
|
|
||||||
|
# Configure access to a base python and to several python interpreters
|
||||||
|
# needed by tox below.
|
||||||
|
export PATH=$PATH:/opt/python/cp35-cp35m/bin
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
|
pushd python
|
||||||
|
# prepare a clean tox run
|
||||||
|
rm -rf tests/__pycache__
|
||||||
|
rm -rf src/deltachat/__pycache__
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
|
# run tox. The circle-ci project env-var-setting DCC_PY_LIVECONFIG
|
||||||
|
# allows running of "liveconfig" tests but for speed reasons
|
||||||
|
# we run them only for the highest python version we support
|
||||||
|
|
||||||
|
# we split out qr-tests run to minimize likelyness of flaky tests
|
||||||
|
# (some qr tests are pretty heavy in terms of send/received
|
||||||
|
# messages and rust's imap code likely has concurrency problems)
|
||||||
|
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "not qr"
|
||||||
|
tox --workdir "$TOXWORKDIR" -e py37 -- --reruns 3 -k "qr"
|
||||||
|
unset DCC_PY_LIVECONFIG
|
||||||
|
#tox --workdir "$TOXWORKDIR" -p4 -e lint,py35,py36,doc
|
||||||
|
#tox --workdir "$TOXWORKDIR" -e auditwheels
|
||||||
|
popd
|
||||||
|
|
||||||
|
# if [ -n "$DOCS" ]; then
|
||||||
|
# echo -----------------------
|
||||||
|
# echo generating python docs
|
||||||
|
# echo -----------------------
|
||||||
|
# (cd python && tox --workdir "$TOXWORKDIR" -e doc)
|
||||||
|
# fi
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
#export RUST_TEST_THREADS=1
|
export RUST_TEST_THREADS=1
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
export RUSTFLAGS='--deny warnings'
|
export RUSTFLAGS='--deny warnings'
|
||||||
export OPT="--target=$TARGET"
|
export OPT="--target=$TARGET"
|
||||||
@@ -37,9 +37,7 @@ else
|
|||||||
export OPT_RELEASE_IGNORED="${OPT_RELEASE} -- --ignored"
|
export OPT_RELEASE_IGNORED="${OPT_RELEASE} -- --ignored"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run all the test configurations
|
# Run all the test configurations:
|
||||||
# RUSTC_WRAPPER=SCCACHE seems to destroy parallelism / prolong the test
|
|
||||||
unset RUSTC_WRAPPER
|
|
||||||
$CARGO_CMD $CARGO_SUBCMD $OPT
|
$CARGO_CMD $CARGO_SUBCMD $OPT
|
||||||
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE
|
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE
|
||||||
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE_IGNORED
|
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE_IGNORED
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Build the Delta Chat Core Rust library, Python wheels and docs
|
|
||||||
|
|
||||||
set -e -x
|
|
||||||
|
|
||||||
# Perform clean build of core and install.
|
|
||||||
export TOXWORKDIR=.docker-tox
|
|
||||||
|
|
||||||
# compile core lib
|
|
||||||
|
|
||||||
export PATH=/root/.cargo/bin:$PATH
|
|
||||||
cargo build --release -p deltachat_ffi
|
|
||||||
# cargo test --all --all-features
|
|
||||||
|
|
||||||
# Statically link against libdeltachat.a.
|
|
||||||
export DCC_RS_DEV=$(pwd)
|
|
||||||
|
|
||||||
# Configure access to a base python and to several python interpreters
|
|
||||||
# needed by tox below.
|
|
||||||
export PATH=$PATH:/opt/python/cp35-cp35m/bin
|
|
||||||
export PYTHONDONTWRITEBYTECODE=1
|
|
||||||
pushd /bin
|
|
||||||
ln -s /opt/python/cp27-cp27m/bin/python2.7
|
|
||||||
ln -s /opt/python/cp36-cp36m/bin/python3.6
|
|
||||||
ln -s /opt/python/cp37-cp37m/bin/python3.7
|
|
||||||
ln -s /opt/python/cp38-cp38/bin/python3.8
|
|
||||||
popd
|
|
||||||
|
|
||||||
pushd python
|
|
||||||
# prepare a clean tox run
|
|
||||||
rm -rf tests/__pycache__
|
|
||||||
rm -rf src/deltachat/__pycache__
|
|
||||||
mkdir -p $TOXWORKDIR
|
|
||||||
|
|
||||||
# disable live-account testing to speed up test runs and wheel building
|
|
||||||
# XXX we may switch on some live-tests on for better ensurances
|
|
||||||
# Note that the independent remote_tests_python step does all kinds of
|
|
||||||
# live-testing already.
|
|
||||||
unset DCC_PY_LIVECONFIG
|
|
||||||
unset DCC_NEW_TMP_EMAIL
|
|
||||||
tox --workdir "$TOXWORKDIR" -e py35,py36,py37,py38,auditwheels
|
|
||||||
popd
|
|
||||||
|
|
||||||
|
|
||||||
echo -----------------------
|
|
||||||
echo generating python docs
|
|
||||||
echo -----------------------
|
|
||||||
(cd python && tox --workdir "$TOXWORKDIR" -e doc)
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deltachat_ffi"
|
name = "deltachat_ffi"
|
||||||
version = "1.33.0"
|
version = "1.0.0-beta.7"
|
||||||
description = "Deltachat FFI"
|
description = "Deltachat FFI"
|
||||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MPL-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
keywords = ["deltachat", "chat", "openpgp", "email", "encryption"]
|
keywords = ["deltachat", "chat", "openpgp", "email", "encryption"]
|
||||||
categories = ["cryptography", "std", "email"]
|
categories = ["cryptography", "std", "email"]
|
||||||
@@ -16,14 +16,13 @@ crate-type = ["cdylib", "staticlib"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
deltachat = { path = "../", default-features = false }
|
deltachat = { path = "../", default-features = false }
|
||||||
|
deltachat-provider-database = "0.2.1"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
human-panic = "1.0.1"
|
human-panic = "1.0.1"
|
||||||
num-traits = "0.2.6"
|
num-traits = "0.2.6"
|
||||||
serde_json = "1.0"
|
|
||||||
anyhow = "1.0.28"
|
|
||||||
thiserror = "1.0.14"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["vendored", "nightly"]
|
default = ["vendored", "nightly", "ringbuf"]
|
||||||
vendored = ["deltachat/vendored"]
|
vendored = ["deltachat/vendored"]
|
||||||
nightly = ["deltachat/nightly"]
|
nightly = ["deltachat/nightly"]
|
||||||
|
ringbuf = ["deltachat/ringbuf"]
|
||||||
|
|||||||
93
deltachat-ffi/src/providers.rs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
extern crate deltachat_provider_database;
|
||||||
|
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
use deltachat::dc_tools::{to_string_lossy, StrExt};
|
||||||
|
use deltachat_provider_database::StatusState;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub type dc_provider_t = deltachat_provider_database::Provider;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_new_from_domain(
|
||||||
|
domain: *const libc::c_char,
|
||||||
|
) -> *const dc_provider_t {
|
||||||
|
match deltachat_provider_database::get_provider_info(&to_string_lossy(domain)) {
|
||||||
|
Some(provider) => provider,
|
||||||
|
None => ptr::null(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_new_from_email(
|
||||||
|
email: *const libc::c_char,
|
||||||
|
) -> *const dc_provider_t {
|
||||||
|
let email = to_string_lossy(email);
|
||||||
|
let domain = deltachat_provider_database::get_domain_from_email(&email);
|
||||||
|
match deltachat_provider_database::get_provider_info(domain) {
|
||||||
|
Some(provider) => provider,
|
||||||
|
None => ptr::null(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! null_guard {
|
||||||
|
($context:tt) => {
|
||||||
|
if $context.is_null() {
|
||||||
|
return ptr::null_mut() as *mut libc::c_char;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_get_overview_page(
|
||||||
|
provider: *const dc_provider_t,
|
||||||
|
) -> *mut libc::c_char {
|
||||||
|
null_guard!(provider);
|
||||||
|
format!(
|
||||||
|
"{}/{}",
|
||||||
|
deltachat_provider_database::PROVIDER_OVERVIEW_URL,
|
||||||
|
(*provider).overview_page
|
||||||
|
)
|
||||||
|
.strdup()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_get_name(provider: *const dc_provider_t) -> *mut libc::c_char {
|
||||||
|
null_guard!(provider);
|
||||||
|
(*provider).name.strdup()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_get_markdown(
|
||||||
|
provider: *const dc_provider_t,
|
||||||
|
) -> *mut libc::c_char {
|
||||||
|
null_guard!(provider);
|
||||||
|
(*provider).markdown.strdup()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_get_status_date(
|
||||||
|
provider: *const dc_provider_t,
|
||||||
|
) -> *mut libc::c_char {
|
||||||
|
null_guard!(provider);
|
||||||
|
(*provider).status.date.strdup()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_get_status(provider: *const dc_provider_t) -> u32 {
|
||||||
|
if provider.is_null() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
match (*provider).status.state {
|
||||||
|
StatusState::OK => 1,
|
||||||
|
StatusState::PREPARATION => 2,
|
||||||
|
StatusState::BROKEN => 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_provider_unref(_provider: *const dc_provider_t) {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO expose general provider overview url?
|
||||||
@@ -1,410 +0,0 @@
|
|||||||
use std::ffi::{CStr, CString};
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
/// Duplicates a string
|
|
||||||
///
|
|
||||||
/// returns an empty string if NULL is given, never returns NULL (exits on errors)
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust,norun
|
|
||||||
/// use crate::string::{dc_strdup, to_string_lossy};
|
|
||||||
/// unsafe {
|
|
||||||
/// let str_a = b"foobar\x00" as *const u8 as *const libc::c_char;
|
|
||||||
/// let str_a_copy = dc_strdup(str_a);
|
|
||||||
/// assert_eq!(to_string_lossy(str_a_copy), "foobar");
|
|
||||||
/// assert_ne!(str_a, str_a_copy);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
unsafe fn dc_strdup(s: *const libc::c_char) -> *mut libc::c_char {
|
|
||||||
let ret: *mut libc::c_char;
|
|
||||||
if !s.is_null() {
|
|
||||||
ret = libc::strdup(s);
|
|
||||||
assert!(!ret.is_null());
|
|
||||||
} else {
|
|
||||||
ret = libc::calloc(1, 1) as *mut libc::c_char;
|
|
||||||
assert!(!ret.is_null());
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error type for the [OsStrExt] trait
|
|
||||||
#[derive(Debug, PartialEq, thiserror::Error)]
|
|
||||||
pub(crate) enum CStringError {
|
|
||||||
/// The string contains an interior null byte
|
|
||||||
#[error("String contains an interior null byte")]
|
|
||||||
InteriorNullByte,
|
|
||||||
/// The string is not valid Unicode
|
|
||||||
#[error("String is not valid unicode")]
|
|
||||||
NotUnicode,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extra convenience methods on [std::ffi::OsStr] to work with `*libc::c_char`.
|
|
||||||
///
|
|
||||||
/// The primary function of this trait is to more easily convert
|
|
||||||
/// [OsStr], [OsString] or [Path] into pointers to C strings. This always
|
|
||||||
/// allocates a new string since it is very common for the source
|
|
||||||
/// string not to have the required terminal null byte.
|
|
||||||
///
|
|
||||||
/// It is implemented for `AsRef<std::ffi::OsStr>>` trait, which
|
|
||||||
/// allows any type which implements this trait to transparently use
|
|
||||||
/// this. This is how the conversion for [Path] works.
|
|
||||||
///
|
|
||||||
/// [OsStr]: std::ffi::OsStr
|
|
||||||
/// [OsString]: std::ffi::OsString
|
|
||||||
/// [Path]: std::path::Path
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use deltachat::dc_tools::{dc_strdup, OsStrExt};
|
|
||||||
/// let path = std::path::Path::new("/some/path");
|
|
||||||
/// let path_c = path.to_c_string().unwrap();
|
|
||||||
/// unsafe {
|
|
||||||
/// let mut c_ptr: *mut libc::c_char = dc_strdup(path_c.as_ptr());
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub(crate) trait OsStrExt {
|
|
||||||
/// Convert a [std::ffi::OsStr] to an [std::ffi::CString]
|
|
||||||
///
|
|
||||||
/// This is useful to convert e.g. a [std::path::Path] to
|
|
||||||
/// [*libc::c_char] by using
|
|
||||||
/// [Path::as_os_str()](std::path::Path::as_os_str) and
|
|
||||||
/// [CStr::as_ptr()](std::ffi::CStr::as_ptr).
|
|
||||||
///
|
|
||||||
/// This returns [CString] and not [&CStr] because not all [OsStr]
|
|
||||||
/// slices end with a null byte, particularly those coming from
|
|
||||||
/// [Path] do not have a null byte and having to handle this as
|
|
||||||
/// the caller would defeat the point of this function.
|
|
||||||
///
|
|
||||||
/// On Windows this requires that the [OsStr] contains valid
|
|
||||||
/// unicode, which should normally be the case for a [Path].
|
|
||||||
///
|
|
||||||
/// [CString]: std::ffi::CString
|
|
||||||
/// [CStr]: std::ffi::CStr
|
|
||||||
/// [OsStr]: std::ffi::OsStr
|
|
||||||
/// [Path]: std::path::Path
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Since a C `*char` is terminated by a NULL byte this conversion
|
|
||||||
/// will fail, when the [OsStr] has an interior null byte. The
|
|
||||||
/// function will return
|
|
||||||
/// `[Err]([CStringError::InteriorNullByte])`. When converting
|
|
||||||
/// from a [Path] it should be safe to
|
|
||||||
/// [`.unwrap()`](std::result::Result::unwrap) this anyway since a
|
|
||||||
/// [Path] should not contain interior null bytes.
|
|
||||||
///
|
|
||||||
/// On windows when the string contains invalid Unicode
|
|
||||||
/// `[Err]([CStringError::NotUnicode])` is returned.
|
|
||||||
fn to_c_string(&self) -> Result<CString, CStringError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<std::ffi::OsStr>> OsStrExt for T {
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
fn to_c_string(&self) -> Result<CString, CStringError> {
|
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
CString::new(self.as_ref().as_bytes()).map_err(|err| match err {
|
|
||||||
std::ffi::NulError { .. } => CStringError::InteriorNullByte,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
fn to_c_string(&self) -> Result<CString, CStringError> {
|
|
||||||
os_str_to_c_string_unicode(&self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation for os_str_to_c_string on windows.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn os_str_to_c_string_unicode(
|
|
||||||
os_str: &dyn AsRef<std::ffi::OsStr>,
|
|
||||||
) -> Result<CString, CStringError> {
|
|
||||||
match os_str.as_ref().to_str() {
|
|
||||||
Some(val) => CString::new(val.as_bytes()).map_err(|err| match err {
|
|
||||||
std::ffi::NulError { .. } => CStringError::InteriorNullByte,
|
|
||||||
}),
|
|
||||||
None => Err(CStringError::NotUnicode),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience methods/associated functions for working with [CString]
|
|
||||||
trait CStringExt {
|
|
||||||
/// Create a new [CString], best effort
|
|
||||||
///
|
|
||||||
/// Like the [to_string_lossy] this doesn't give up in the face of
|
|
||||||
/// bad input (embedded null bytes in this case) instead it does
|
|
||||||
/// the best it can by stripping the embedded null bytes.
|
|
||||||
fn new_lossy<T: Into<Vec<u8>>>(t: T) -> CString {
|
|
||||||
let mut s = t.into();
|
|
||||||
s.retain(|&c| c != 0);
|
|
||||||
CString::new(s).unwrap_or_default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CStringExt for CString {}
|
|
||||||
|
|
||||||
/// Convenience methods to turn strings into C strings.
|
|
||||||
///
|
|
||||||
/// To interact with (legacy) C APIs we often need to convert from
|
|
||||||
/// Rust strings to raw C strings. This can be clumsy to do correctly
|
|
||||||
/// and the compiler sometimes allows it in an unsafe way. These
|
|
||||||
/// methods make it more succinct and help you get it right.
|
|
||||||
pub(crate) trait Strdup {
|
|
||||||
/// Allocate a new raw C `*char` version of this string.
|
|
||||||
///
|
|
||||||
/// This allocates a new raw C string which must be freed using
|
|
||||||
/// `free`. It takes care of some common pitfalls with using
|
|
||||||
/// [CString.as_ptr].
|
|
||||||
///
|
|
||||||
/// [CString.as_ptr]: std::ffi::CString.as_ptr
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// This function will panic when the original string contains an
|
|
||||||
/// interior null byte as this can not be represented in raw C
|
|
||||||
/// strings.
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<str>> Strdup for T {
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char {
|
|
||||||
let tmp = CString::new_lossy(self.as_ref());
|
|
||||||
dc_strdup(tmp.as_ptr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can not implement for AsRef<OsStr> because we already implement
|
|
||||||
// AsRev<str> and this conflicts. So implement for Path directly.
|
|
||||||
impl Strdup for std::path::Path {
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char {
|
|
||||||
let tmp = self.to_c_string().unwrap_or_else(|_| CString::default());
|
|
||||||
dc_strdup(tmp.as_ptr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience methods to turn optional strings into C strings.
|
|
||||||
///
|
|
||||||
/// This is the same as the [Strdup] trait but a different trait name
|
|
||||||
/// to work around the type system not allowing to implement [Strdup]
|
|
||||||
/// for `Option<impl Strdup>` When we already have an [Strdup] impl
|
|
||||||
/// for `AsRef<&str>`.
|
|
||||||
///
|
|
||||||
/// When the [Option] is [Option::Some] this behaves just like
|
|
||||||
/// [Strdup::strdup], when it is [Option::None] a null pointer is
|
|
||||||
/// returned.
|
|
||||||
pub(crate) trait OptStrdup {
|
|
||||||
/// Allocate a new raw C `*char` version of this string, or NULL.
|
|
||||||
///
|
|
||||||
/// See [Strdup::strdup] for details.
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<str>> OptStrdup for Option<T> {
|
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char {
|
|
||||||
match self {
|
|
||||||
Some(s) => {
|
|
||||||
let tmp = CString::new_lossy(s.as_ref());
|
|
||||||
dc_strdup(tmp.as_ptr())
|
|
||||||
}
|
|
||||||
None => ptr::null_mut(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn to_string_lossy(s: *const libc::c_char) -> String {
|
|
||||||
if s.is_null() {
|
|
||||||
return "".into();
|
|
||||||
}
|
|
||||||
|
|
||||||
let cstr = unsafe { CStr::from_ptr(s) };
|
|
||||||
|
|
||||||
cstr.to_string_lossy().to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn to_opt_string_lossy(s: *const libc::c_char) -> Option<String> {
|
|
||||||
if s.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(to_string_lossy(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert a C `*char` pointer to a [std::path::Path] slice.
|
|
||||||
///
|
|
||||||
/// This converts a `*libc::c_char` pointer to a [Path] slice. This
|
|
||||||
/// essentially has to convert the pointer to [std::ffi::OsStr] to do
|
|
||||||
/// so and thus is the inverse of [OsStrExt::to_c_string]. Just like
|
|
||||||
/// [OsStrExt::to_c_string] requires valid Unicode on Windows, this
|
|
||||||
/// requires that the pointer contains valid UTF-8 on Windows.
|
|
||||||
///
|
|
||||||
/// Because this returns a reference the [Path] silce can not outlive
|
|
||||||
/// the original pointer.
|
|
||||||
///
|
|
||||||
/// [Path]: std::path::Path
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
pub(crate) fn as_path<'a>(s: *const libc::c_char) -> &'a std::path::Path {
|
|
||||||
assert!(!s.is_null(), "cannot be used on null pointers");
|
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
unsafe {
|
|
||||||
let c_str = std::ffi::CStr::from_ptr(s).to_bytes();
|
|
||||||
let os_str = std::ffi::OsStr::from_bytes(c_str);
|
|
||||||
std::path::Path::new(os_str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// as_path() implementation for windows, documented above.
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub(crate) fn as_path<'a>(s: *const libc::c_char) -> &'a std::path::Path {
|
|
||||||
as_path_unicode(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation for as_path() on Windows.
|
|
||||||
//
|
|
||||||
// Having this as a separate function means it can be tested on unix
|
|
||||||
// too.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn as_path_unicode<'a>(s: *const libc::c_char) -> &'a std::path::Path {
|
|
||||||
assert!(!s.is_null(), "cannot be used on null pointers");
|
|
||||||
|
|
||||||
let cstr = unsafe { CStr::from_ptr(s) };
|
|
||||||
let str = cstr.to_str().unwrap_or_else(|err| panic!("{}", err));
|
|
||||||
|
|
||||||
std::path::Path::new(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use libc::{free, strcmp};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_to_c_string_cwd() {
|
|
||||||
let some_dir = std::env::current_dir().unwrap();
|
|
||||||
some_dir.as_os_str().to_c_string().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_to_c_string_unicode() {
|
|
||||||
let some_str = String::from("/some/valid/utf8");
|
|
||||||
let some_dir = std::path::Path::new(&some_str);
|
|
||||||
assert_eq!(
|
|
||||||
some_dir.as_os_str().to_c_string().unwrap(),
|
|
||||||
CString::new("/some/valid/utf8").unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_to_c_string_nul() {
|
|
||||||
let some_str = std::ffi::OsString::from("foo\x00bar");
|
|
||||||
assert_eq!(
|
|
||||||
some_str.to_c_string().err().unwrap(),
|
|
||||||
CStringError::InteriorNullByte
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_path_to_c_string_cwd() {
|
|
||||||
let some_dir = std::env::current_dir().unwrap();
|
|
||||||
some_dir.to_c_string().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_path_to_c_string_unicode() {
|
|
||||||
let some_str = String::from("/some/valid/utf8");
|
|
||||||
let some_dir = std::path::Path::new(&some_str);
|
|
||||||
assert_eq!(
|
|
||||||
some_dir.as_os_str().to_c_string().unwrap(),
|
|
||||||
CString::new("/some/valid/utf8").unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_to_c_string_unicode_fn() {
|
|
||||||
let some_str = std::ffi::OsString::from("foo");
|
|
||||||
assert_eq!(
|
|
||||||
os_str_to_c_string_unicode(&some_str).unwrap(),
|
|
||||||
CString::new("foo").unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_path_to_c_string_unicode_fn() {
|
|
||||||
let some_str = String::from("/some/path");
|
|
||||||
let some_path = std::path::Path::new(&some_str);
|
|
||||||
assert_eq!(
|
|
||||||
os_str_to_c_string_unicode(&some_path).unwrap(),
|
|
||||||
CString::new("/some/path").unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_to_c_string_unicode_fn_nul() {
|
|
||||||
let some_str = std::ffi::OsString::from("fooz\x00bar");
|
|
||||||
assert_eq!(
|
|
||||||
os_str_to_c_string_unicode(&some_str).err().unwrap(),
|
|
||||||
CStringError::InteriorNullByte
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_as_path() {
|
|
||||||
let some_path = CString::new("/some/path").unwrap();
|
|
||||||
let ptr = some_path.as_ptr();
|
|
||||||
assert_eq!(as_path(ptr), std::ffi::OsString::from("/some/path"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_as_path_unicode_fn() {
|
|
||||||
let some_path = CString::new("/some/path").unwrap();
|
|
||||||
let ptr = some_path.as_ptr();
|
|
||||||
assert_eq!(as_path_unicode(ptr), std::ffi::OsString::from("/some/path"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cstring_new_lossy() {
|
|
||||||
assert!(CString::new("hel\x00lo").is_err());
|
|
||||||
assert!(CString::new(String::from("hel\x00o")).is_err());
|
|
||||||
let r = CString::new("hello").unwrap();
|
|
||||||
assert_eq!(CString::new_lossy("hello"), r);
|
|
||||||
assert_eq!(CString::new_lossy("hel\x00lo"), r);
|
|
||||||
assert_eq!(CString::new_lossy(String::from("hello")), r);
|
|
||||||
assert_eq!(CString::new_lossy(String::from("hel\x00lo")), r);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_strdup_str() {
|
|
||||||
unsafe {
|
|
||||||
let s = "hello".strdup();
|
|
||||||
let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char);
|
|
||||||
free(s as *mut libc::c_void);
|
|
||||||
assert_eq!(cmp, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_strdup_string() {
|
|
||||||
unsafe {
|
|
||||||
let s = String::from("hello").strdup();
|
|
||||||
let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char);
|
|
||||||
free(s as *mut libc::c_void);
|
|
||||||
assert_eq!(cmp, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_strdup_opt_string() {
|
|
||||||
unsafe {
|
|
||||||
let s = Some("hello");
|
|
||||||
let c = s.strdup();
|
|
||||||
let cmp = strcmp(c, b"hello\x00" as *const u8 as *const libc::c_char);
|
|
||||||
free(c as *mut libc::c_void);
|
|
||||||
assert_eq!(cmp, 0);
|
|
||||||
|
|
||||||
let s: Option<&str> = None;
|
|
||||||
let c = s.strdup();
|
|
||||||
assert_eq!(c, ptr::null_mut());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "deltachat_derive"
|
name = "deltachat_derive"
|
||||||
version = "2.0.0"
|
version = "0.1.0"
|
||||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
authors = ["Dmitry Bogatov <KAction@debian.org>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MPL-2.0"
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
syn = "1.0.13"
|
syn = "0.14.4"
|
||||||
quote = "1.0.2"
|
quote = "0.6.3"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ extern crate proc_macro;
|
|||||||
|
|
||||||
use crate::proc_macro::TokenStream;
|
use crate::proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use syn;
|
||||||
|
|
||||||
// For now, assume (not check) that these macroses are applied to enum without
|
// For now, assume (not check) that these macroses are applied to enum without
|
||||||
// data. If this assumption is violated, compiler error will point to
|
// data. If this assumption is violated, compiler error will point to
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
|
|
||||||
Problem: missing eventual group consistency
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
If group members are concurrently adding new members,
|
|
||||||
the new members will miss each other's additions, example:
|
|
||||||
|
|
||||||
- Alice and Bob are in a two-member group
|
|
||||||
|
|
||||||
- Alice adds Carol, concurrently Bob adds Doris
|
|
||||||
|
|
||||||
- Carol will see a three-member group (Alice, Bob, Carol),
|
|
||||||
Doris will see a different three-member group (Alice, Bob, Doris),
|
|
||||||
and only Alice and Bob will have all four members.
|
|
||||||
|
|
||||||
Note that for verified groups any mitigation mechanism likely
|
|
||||||
needs to make all clients to know who originally added a member.
|
|
||||||
|
|
||||||
|
|
||||||
solution: memorize+attach (possible encrypted) chat-meta mime messages
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
For reference, please see https://github.com/deltachat/deltachat-core-rust/blob/master/spec.md#add-and-remove-members how MemberAdded/Removed messages are shaped.
|
|
||||||
|
|
||||||
|
|
||||||
- All Chat-Group-Member-Added/Removed messages are recorded in their
|
|
||||||
full raw (signed and encrypted) mime-format in the DB
|
|
||||||
|
|
||||||
- If an incoming member-add/member-delete messages has a member list
|
|
||||||
which is, apart from the added/removed member, not consistent
|
|
||||||
with our own view, broadcast a "Chat-Group-Member-Correction" message to
|
|
||||||
all members, attaching the original added/removed mime-message for all mismatching
|
|
||||||
contacts. If we have no relevant add/del information, don't send a
|
|
||||||
correction message out.
|
|
||||||
|
|
||||||
- Upong receiving added/removed attachments we don't do the
|
|
||||||
check_consistency+correction message dance.
|
|
||||||
This avoids recursion problems and hard-to-reason-about chatter.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
- mechanism works for both encrypted and unencrypted add/del messages
|
|
||||||
|
|
||||||
- we already have a "mime_headers" column in the DB for each incoming message.
|
|
||||||
We could extend it to also include the payload and store mime unconditionally
|
|
||||||
for member-added/removed messages.
|
|
||||||
|
|
||||||
- multiple member-added/removed messages can be attached in a single
|
|
||||||
correction message
|
|
||||||
|
|
||||||
- it is minimal on the number of overall messages to reach group consistency
|
|
||||||
(best-case: no extra messages, the ABCD case above: max two extra messages)
|
|
||||||
|
|
||||||
- somewhat backward compatible: older clients will probably ignore
|
|
||||||
messages which are signed by someone who is not the outer From-address.
|
|
||||||
|
|
||||||
- the correction-protocol also helps with dropped messages. If a member
|
|
||||||
did not see a member-added/removed message, the next member add/removed
|
|
||||||
message in the group will likely heal group consistency for this member.
|
|
||||||
|
|
||||||
- we can quite easily extend the mechanism to also provide the group-avatar or
|
|
||||||
other meta-information.
|
|
||||||
|
|
||||||
Discussions of variants
|
|
||||||
++++++++++++++++++++++++
|
|
||||||
|
|
||||||
- instead of acting on MemberAdded/Removed message we could send
|
|
||||||
corrections for any received message that addresses inconsistent group members but
|
|
||||||
a) this would delay group-membership healing
|
|
||||||
b) could lead to a lot of members sending corrections
|
|
||||||
|
|
||||||
- instead of broadcasting correction messages we could only send it to
|
|
||||||
the sender of the inconsistent member-added/removed message.
|
|
||||||
A receiver of such a correction message would then need to forward
|
|
||||||
the message to the members it thinks also have an inconsistent view.
|
|
||||||
This sounds complicated and error-prone. Concretely, if Alice
|
|
||||||
receives Bob's "Member-added: Doris" message, then Alice
|
|
||||||
broadcasting the correction message with "Member-added: Carol"
|
|
||||||
would reach all four members, healing group consistency in one step.
|
|
||||||
If Bob meanwhile receives Alice's "Member-Added: Carol" message,
|
|
||||||
Bob would broadcast a correction message to all four members as well.
|
|
||||||
(Imagine a situation where Alice/Bob added Carol/Doris
|
|
||||||
while both being in an offline or bad-connection situation).
|
|
||||||
|
|
||||||
|
|
||||||
solution2: repeat member-added/removed messages
|
|
||||||
---------------------------------------------------
|
|
||||||
|
|
||||||
Introduce a new Chat-Group-Member-Changed header and deprecate Chat-Group-Member-Added/Removed
|
|
||||||
but keep sending out the old headers until the new protocol is sufficiently deployed.
|
|
||||||
|
|
||||||
The new Chat-Group-Member-Changed header contains a Time-to-Live number (TTL)
|
|
||||||
which controls repetition of the signed "add/del e-mail address" payload.
|
|
||||||
|
|
||||||
Example::
|
|
||||||
|
|
||||||
Chat-Group-Member-Changed: TTL add "somedisplayname" someone@example.org
|
|
||||||
owEBYQGe/pANAwACAY47A6J5t3LWAcsxYgBeTQypYWRkICJzb21lZGlzcGxheW5h
|
|
||||||
bWUiIHNvbWVvbmVAZXhhbXBsZS5vcmcgCokBHAQAAQIABgUCXk0MqQAKCRCOOwOi
|
|
||||||
ebdy1hfRB/wJ74tgFQulicthcv9n+ZsqzwOtBKMEVIHqJCzzDB/Hg/2z8ogYoZNR
|
|
||||||
iUKKrv3Y1XuFvdKyOC+wC/unXAWKFHYzY6Tv6qDp6r+amt+ad+8Z02q53h9E55IP
|
|
||||||
FUBdq2rbS8hLGjQB+mVRowYrUACrOqGgNbXMZjQfuV7fSc7y813OsCQgi3tjstup
|
|
||||||
b+uduVzxCp3PChGhcZPs3iOGCnQvSB8VAaLGMWE2d7nTo/yMQ0Jx69x5qwfXogTk
|
|
||||||
mTt5rOJyrosbtf09TMKFzGgtqBcEqHLp3+mQpZQ+WHUKAbsRa8Jc9DOUOSKJ8SNM
|
|
||||||
clKdskprY+4LY0EBwLD3SQ7dPkTITCRD
|
|
||||||
=P6GG
|
|
||||||
|
|
||||||
TTL is set to "2" on an initial Chat-Group-Member-Changed add/del message.
|
|
||||||
Receivers will apply the add/del change to the group-membership,
|
|
||||||
decrease the TTL by 1, and if TTL>0 re-sent the header.
|
|
||||||
|
|
||||||
The "add|del e-mail address" payload is pgp-signed and repeated verbatim.
|
|
||||||
This allows to propagate, in a cryptographically secured way,
|
|
||||||
who added a member. This is particularly important for allowing
|
|
||||||
to show in verified groups who added a member (planned).
|
|
||||||
|
|
||||||
Disadvantage to solution 1:
|
|
||||||
|
|
||||||
- requires to specify encoding and precise rules for what/how is signed.
|
|
||||||
|
|
||||||
- causes O(N^2) extra messages
|
|
||||||
|
|
||||||
- Not easily extendable for other things (without introducing a new
|
|
||||||
header / encoding)
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{bail, ensure};
|
use deltachat::chat::{self, Chat};
|
||||||
use deltachat::chat::{self, Chat, ChatId, ChatVisibility};
|
|
||||||
use deltachat::chatlist::*;
|
use deltachat::chatlist::*;
|
||||||
|
use deltachat::config;
|
||||||
use deltachat::constants::*;
|
use deltachat::constants::*;
|
||||||
use deltachat::contact::*;
|
use deltachat::contact::*;
|
||||||
use deltachat::context::*;
|
use deltachat::context::*;
|
||||||
@@ -19,16 +19,16 @@ use deltachat::peerstate::*;
|
|||||||
use deltachat::qr::*;
|
use deltachat::qr::*;
|
||||||
use deltachat::sql;
|
use deltachat::sql;
|
||||||
use deltachat::Event;
|
use deltachat::Event;
|
||||||
use deltachat::{config, provider};
|
use libc::free;
|
||||||
|
|
||||||
/// Reset database tables.
|
/// Reset database tables. This function is called from Core cmdline.
|
||||||
/// Argument is a bitmask, executing single or multiple actions in one call.
|
/// 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.
|
/// e.g. bitmask 7 triggers actions definded with bits 1, 2 and 4.
|
||||||
fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
||||||
println!("Resetting tables ({})...", bits);
|
info!(context, "Resetting tables ({})...", bits);
|
||||||
if 0 != bits & 1 {
|
if 0 != bits & 1 {
|
||||||
sql::execute(context, &context.sql, "DELETE FROM jobs;", params![]).unwrap();
|
sql::execute(context, &context.sql, "DELETE FROM jobs;", params![]).unwrap();
|
||||||
println!("(1) Jobs reset.");
|
info!(context, "(1) Jobs reset.");
|
||||||
}
|
}
|
||||||
if 0 != bits & 2 {
|
if 0 != bits & 2 {
|
||||||
sql::execute(
|
sql::execute(
|
||||||
@@ -38,11 +38,11 @@ fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
|||||||
params![],
|
params![],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!("(2) Peerstates reset.");
|
info!(context, "(2) Peerstates reset.");
|
||||||
}
|
}
|
||||||
if 0 != bits & 4 {
|
if 0 != bits & 4 {
|
||||||
sql::execute(context, &context.sql, "DELETE FROM keypairs;", params![]).unwrap();
|
sql::execute(context, &context.sql, "DELETE FROM keypairs;", params![]).unwrap();
|
||||||
println!("(4) Private keypairs reset.");
|
info!(context, "(4) Private keypairs reset.");
|
||||||
}
|
}
|
||||||
if 0 != bits & 8 {
|
if 0 != bits & 8 {
|
||||||
sql::execute(
|
sql::execute(
|
||||||
@@ -81,23 +81,21 @@ fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
sql::execute(context, &context.sql, "DELETE FROM leftgrps;", params![]).unwrap();
|
sql::execute(context, &context.sql, "DELETE FROM leftgrps;", params![]).unwrap();
|
||||||
println!("(8) Rest but server config reset.");
|
info!(context, "(8) Rest but server config reset.");
|
||||||
}
|
}
|
||||||
|
|
||||||
context.call_cb(Event::MsgsChanged {
|
context.call_cb(Event::MsgsChanged {
|
||||||
chat_id: ChatId::new(0),
|
chat_id: 0,
|
||||||
msg_id: MsgId::new(0),
|
msg_id: MsgId::new(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dc_poke_eml_file(context: &Context, filename: impl AsRef<Path>) -> Result<(), anyhow::Error> {
|
fn dc_poke_eml_file(context: &Context, filename: impl AsRef<Path>) -> Result<(), Error> {
|
||||||
let data = dc_read_file(context, filename)?;
|
let data = dc_read_file(context, filename)?;
|
||||||
|
|
||||||
if let Err(err) = dc_receive_imf(context, &data, "import", 0, false) {
|
unsafe { dc_receive_imf(context, &data, "import", 0, 0) };
|
||||||
println!("dc_receive_imf errored: {:?}", err);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,19 +107,18 @@ fn dc_poke_eml_file(context: &Context, filename: impl AsRef<Path>) -> Result<(),
|
|||||||
/// @param context The context as created by dc_context_new().
|
/// @param context The context as created by dc_context_new().
|
||||||
/// @param spec The file or directory to import. NULL for the last command.
|
/// @param spec The file or directory to import. NULL for the last command.
|
||||||
/// @return 1=success, 0=error.
|
/// @return 1=success, 0=error.
|
||||||
fn poke_spec(context: &Context, spec: Option<&str>) -> libc::c_int {
|
fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
|
||||||
if !context.sql.is_open() {
|
if !context.sql.is_open() {
|
||||||
error!(context, "Import: Database not opened.");
|
error!(context, "Import: Database not opened.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let real_spec: String;
|
||||||
let mut read_cnt = 0;
|
let mut read_cnt = 0;
|
||||||
|
|
||||||
let real_spec: String;
|
|
||||||
|
|
||||||
/* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
|
/* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
|
||||||
if let Some(spec) = spec {
|
if !spec.is_null() {
|
||||||
real_spec = spec.to_string();
|
real_spec = to_string_lossy(spec);
|
||||||
context
|
context
|
||||||
.sql
|
.sql
|
||||||
.set_raw_config(context, "import_spec", Some(&real_spec))
|
.set_raw_config(context, "import_spec", Some(&real_spec))
|
||||||
@@ -135,8 +132,10 @@ fn poke_spec(context: &Context, spec: Option<&str>) -> libc::c_int {
|
|||||||
real_spec = rs.unwrap();
|
real_spec = rs.unwrap();
|
||||||
}
|
}
|
||||||
if let Some(suffix) = dc_get_filesuffix_lc(&real_spec) {
|
if let Some(suffix) = dc_get_filesuffix_lc(&real_spec) {
|
||||||
if suffix == "eml" && dc_poke_eml_file(context, &real_spec).is_ok() {
|
if suffix == "eml" {
|
||||||
read_cnt += 1
|
if dc_poke_eml_file(context, &real_spec).is_ok() {
|
||||||
|
read_cnt += 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* import a directory */
|
/* import a directory */
|
||||||
@@ -156,7 +155,7 @@ fn poke_spec(context: &Context, spec: Option<&str>) -> libc::c_int {
|
|||||||
let name = name_f.to_string_lossy();
|
let name = name_f.to_string_lossy();
|
||||||
if name.ends_with(".eml") {
|
if name.ends_with(".eml") {
|
||||||
let path_plus_name = format!("{}/{}", &real_spec, name);
|
let path_plus_name = format!("{}/{}", &real_spec, name);
|
||||||
println!("Import: {}", path_plus_name);
|
info!(context, "Import: {}", path_plus_name);
|
||||||
if dc_poke_eml_file(context, path_plus_name).is_ok() {
|
if dc_poke_eml_file(context, path_plus_name).is_ok() {
|
||||||
read_cnt += 1
|
read_cnt += 1
|
||||||
}
|
}
|
||||||
@@ -164,17 +163,20 @@ fn poke_spec(context: &Context, spec: Option<&str>) -> libc::c_int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("Import: {} items read from \"{}\".", read_cnt, &real_spec);
|
info!(
|
||||||
|
context,
|
||||||
|
"Import: {} items read from \"{}\".", read_cnt, &real_spec
|
||||||
|
);
|
||||||
if read_cnt > 0 {
|
if read_cnt > 0 {
|
||||||
context.call_cb(Event::MsgsChanged {
|
context.call_cb(Event::MsgsChanged {
|
||||||
chat_id: ChatId::new(0),
|
chat_id: 0,
|
||||||
msg_id: MsgId::new(0),
|
msg_id: MsgId::new(0),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||||
let contact = Contact::get_by_id(context, msg.get_from_id()).expect("invalid contact");
|
let contact = Contact::get_by_id(context, msg.get_from_id()).expect("invalid contact");
|
||||||
let contact_name = contact.get_name();
|
let contact_name = contact.get_name();
|
||||||
let contact_id = contact.get_id();
|
let contact_id = contact.get_id();
|
||||||
@@ -188,7 +190,8 @@ fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
|||||||
};
|
};
|
||||||
let temp2 = dc_timestamp_to_str(msg.get_timestamp());
|
let temp2 = dc_timestamp_to_str(msg.get_timestamp());
|
||||||
let msgtext = msg.get_text();
|
let msgtext = msg.get_text();
|
||||||
println!(
|
info!(
|
||||||
|
context,
|
||||||
"{}{}{}{}: {} (Contact#{}): {} {}{}{}{}{} [{}]",
|
"{}{}{}{}: {} (Contact#{}): {} {}{}{}{}{} [{}]",
|
||||||
prefix.as_ref(),
|
prefix.as_ref(),
|
||||||
msg.get_id(),
|
msg.get_id(),
|
||||||
@@ -218,18 +221,20 @@ fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log_msglist(context: &Context, msglist: &Vec<MsgId>) -> Result<(), Error> {
|
unsafe fn log_msglist(context: &Context, msglist: &Vec<MsgId>) -> Result<(), Error> {
|
||||||
let mut lines_out = 0;
|
let mut lines_out = 0;
|
||||||
for &msg_id in msglist {
|
for &msg_id in msglist {
|
||||||
if msg_id.is_daymarker() {
|
if msg_id.is_daymarker() {
|
||||||
println!(
|
info!(
|
||||||
|
context,
|
||||||
"--------------------------------------------------------------------------------"
|
"--------------------------------------------------------------------------------"
|
||||||
);
|
);
|
||||||
|
|
||||||
lines_out += 1
|
lines_out += 1
|
||||||
} else if !msg_id.is_special() {
|
} else if !msg_id.is_special() {
|
||||||
if lines_out == 0 {
|
if lines_out == 0 {
|
||||||
println!(
|
info!(
|
||||||
|
context,
|
||||||
"--------------------------------------------------------------------------------",
|
"--------------------------------------------------------------------------------",
|
||||||
);
|
);
|
||||||
lines_out += 1
|
lines_out += 1
|
||||||
@@ -239,14 +244,15 @@ fn log_msglist(context: &Context, msglist: &Vec<MsgId>) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if lines_out > 0 {
|
if lines_out > 0 {
|
||||||
println!(
|
info!(
|
||||||
|
context,
|
||||||
"--------------------------------------------------------------------------------"
|
"--------------------------------------------------------------------------------"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
|
unsafe fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
|
||||||
let mut contacts = contacts.clone();
|
let mut contacts = contacts.clone();
|
||||||
if !contacts.contains(&1) {
|
if !contacts.contains(&1) {
|
||||||
contacts.push(1);
|
contacts.push(1);
|
||||||
@@ -289,7 +295,7 @@ fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Contact#{}: {}{}", contact_id, line, line2);
|
info!(context, "Contact#{}: {}{}", contact_id, line, line2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,9 +304,9 @@ fn chat_prefix(chat: &Chat) -> &'static str {
|
|||||||
chat.typ.into()
|
chat.typ.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||||
let chat_id = *context.cmdline_sel_chat_id.read().unwrap();
|
let chat_id = *context.cmdline_sel_chat_id.read().unwrap();
|
||||||
let mut sel_chat = if !chat_id.is_unset() {
|
let mut sel_chat = if chat_id > 0 {
|
||||||
Chat::load_from_db(context, chat_id).ok()
|
Chat::load_from_db(context, chat_id).ok()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -309,6 +315,11 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
let mut args = line.splitn(3, ' ');
|
let mut args = line.splitn(3, ' ');
|
||||||
let arg0 = args.next().unwrap_or_default();
|
let arg0 = args.next().unwrap_or_default();
|
||||||
let arg1 = 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() as *const _
|
||||||
|
};
|
||||||
let arg2 = args.next().unwrap_or_default();
|
let arg2 = args.next().unwrap_or_default();
|
||||||
|
|
||||||
let blobdir = context.get_blobdir();
|
let blobdir = context.get_blobdir();
|
||||||
@@ -372,8 +383,6 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
listmedia\n\
|
listmedia\n\
|
||||||
archive <chat-id>\n\
|
archive <chat-id>\n\
|
||||||
unarchive <chat-id>\n\
|
unarchive <chat-id>\n\
|
||||||
pin <chat-id>\n\
|
|
||||||
unpin <chat-id>\n\
|
|
||||||
delchat <chat-id>\n\
|
delchat <chat-id>\n\
|
||||||
===========================Message commands==\n\
|
===========================Message commands==\n\
|
||||||
listmsgs <query>\n\
|
listmsgs <query>\n\
|
||||||
@@ -395,10 +404,8 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
getqr [<chat-id>]\n\
|
getqr [<chat-id>]\n\
|
||||||
getbadqr\n\
|
getbadqr\n\
|
||||||
checkqr <qr-content>\n\
|
checkqr <qr-content>\n\
|
||||||
providerinfo <addr>\n\
|
|
||||||
event <event-id to test>\n\
|
event <event-id to test>\n\
|
||||||
fileinfo <file>\n\
|
fileinfo <file>\n\
|
||||||
estimatedeletion <seconds>\n\
|
|
||||||
emptyserver <flags> (1=MVBOX 2=INBOX)\n\
|
emptyserver <flags> (1=MVBOX 2=INBOX)\n\
|
||||||
clear -- clear screen\n\
|
clear -- clear screen\n\
|
||||||
exit or quit\n\
|
exit or quit\n\
|
||||||
@@ -462,7 +469,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
"poke" => {
|
"poke" => {
|
||||||
ensure!(0 != poke_spec(context, Some(arg1)), "Poke failed");
|
ensure!(0 != poke_spec(context, arg1_c), "Poke failed");
|
||||||
}
|
}
|
||||||
"reset" => {
|
"reset" => {
|
||||||
ensure!(!arg1.is_empty(), "Argument <bits> missing: 1=jobs, 2=peerstates, 4=private keys, 8=rest but server config");
|
ensure!(!arg1.is_empty(), "Argument <bits> missing: 1=jobs, 2=peerstates, 4=private keys, 8=rest but server config");
|
||||||
@@ -489,7 +496,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
println!("{:#?}", context.get_info());
|
println!("{:#?}", context.get_info());
|
||||||
}
|
}
|
||||||
"interrupt" => {
|
"interrupt" => {
|
||||||
interrupt_inbox_idle(context);
|
interrupt_imap_idle(context);
|
||||||
}
|
}
|
||||||
"maybenetwork" => {
|
"maybenetwork" => {
|
||||||
maybe_network(context);
|
maybe_network(context);
|
||||||
@@ -508,26 +515,23 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
|
|
||||||
let cnt = chatlist.len();
|
let cnt = chatlist.len();
|
||||||
if cnt > 0 {
|
if cnt > 0 {
|
||||||
println!(
|
info!(
|
||||||
|
context,
|
||||||
"================================================================================"
|
"================================================================================"
|
||||||
);
|
);
|
||||||
|
|
||||||
for i in (0..cnt).rev() {
|
for i in (0..cnt).rev() {
|
||||||
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
|
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
|
||||||
println!(
|
info!(
|
||||||
"{}#{}: {} [{} fresh] {}",
|
context,
|
||||||
|
"{}#{}: {} [{} fresh]",
|
||||||
chat_prefix(&chat),
|
chat_prefix(&chat),
|
||||||
chat.get_id(),
|
chat.get_id(),
|
||||||
chat.get_name(),
|
chat.get_name(),
|
||||||
chat.get_id().get_fresh_msg_cnt(context),
|
chat::get_fresh_msg_cnt(context, chat.get_id()),
|
||||||
match chat.visibility {
|
|
||||||
ChatVisibility::Normal => "",
|
|
||||||
ChatVisibility::Archived => "📦",
|
|
||||||
ChatVisibility::Pinned => "📌",
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
let lot = chatlist.get_summary(context, i, Some(&chat));
|
let lot = chatlist.get_summary(context, i, Some(&chat));
|
||||||
let statestr = if chat.visibility == ChatVisibility::Archived {
|
let statestr = if chat.is_archived() {
|
||||||
" [Archived]"
|
" [Archived]"
|
||||||
} else {
|
} else {
|
||||||
match lot.get_state() {
|
match lot.get_state() {
|
||||||
@@ -541,7 +545,8 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
let timestr = dc_timestamp_to_str(lot.get_timestamp());
|
let timestr = dc_timestamp_to_str(lot.get_timestamp());
|
||||||
let text1 = lot.get_text1();
|
let text1 = lot.get_text1();
|
||||||
let text2 = lot.get_text2();
|
let text2 = lot.get_text2();
|
||||||
println!(
|
info!(
|
||||||
|
context,
|
||||||
"{}{}{}{} [{}]{}",
|
"{}{}{}{} [{}]{}",
|
||||||
text1.unwrap_or(""),
|
text1.unwrap_or(""),
|
||||||
if text1.is_some() { ": " } else { "" },
|
if text1.is_some() { ": " } else { "" },
|
||||||
@@ -554,13 +559,14 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
""
|
""
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
println!(
|
info!(
|
||||||
|
context,
|
||||||
"================================================================================"
|
"================================================================================"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if location::is_sending_locations_to_chat(context, ChatId::new(0)) {
|
if location::is_sending_locations_to_chat(context, 0) {
|
||||||
println!("Location streaming enabled.");
|
info!(context, "Location streaming enabled.");
|
||||||
}
|
}
|
||||||
println!("{} chats", cnt);
|
println!("{} chats", cnt);
|
||||||
}
|
}
|
||||||
@@ -569,8 +575,8 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
bail!("Argument [chat-id] is missing.");
|
bail!("Argument [chat-id] is missing.");
|
||||||
}
|
}
|
||||||
if !arg1.is_empty() {
|
if !arg1.is_empty() {
|
||||||
let chat_id = ChatId::new(arg1.parse()?);
|
let chat_id = arg1.parse()?;
|
||||||
println!("Selecting chat {}", chat_id);
|
println!("Selecting chat #{}", chat_id);
|
||||||
sel_chat = Some(Chat::load_from_db(context, chat_id)?);
|
sel_chat = Some(Chat::load_from_db(context, chat_id)?);
|
||||||
*context.cmdline_sel_chat_id.write().unwrap() = chat_id;
|
*context.cmdline_sel_chat_id.write().unwrap() = chat_id;
|
||||||
}
|
}
|
||||||
@@ -582,13 +588,14 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
let members = chat::get_chat_contacts(context, sel_chat.id);
|
let members = chat::get_chat_contacts(context, sel_chat.id);
|
||||||
let subtitle = if sel_chat.is_device_talk() {
|
let subtitle = if sel_chat.is_device_talk() {
|
||||||
"device-talk".to_string()
|
"device-talk".to_string()
|
||||||
} else if sel_chat.get_type() == Chattype::Single && !members.is_empty() {
|
} else if sel_chat.get_type() == Chattype::Single && members.len() >= 1 {
|
||||||
let contact = Contact::get_by_id(context, members[0])?;
|
let contact = Contact::get_by_id(context, members[0])?;
|
||||||
contact.get_addr().to_string()
|
contact.get_addr().to_string()
|
||||||
} else {
|
} else {
|
||||||
format!("{} member(s)", members.len())
|
format!("{} member(s)", members.len())
|
||||||
};
|
};
|
||||||
println!(
|
info!(
|
||||||
|
context,
|
||||||
"{}#{}: {} [{}]{}{}",
|
"{}#{}: {} [{}]{}{}",
|
||||||
chat_prefix(sel_chat),
|
chat_prefix(sel_chat),
|
||||||
sel_chat.get_id(),
|
sel_chat.get_id(),
|
||||||
@@ -608,11 +615,14 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
log_msglist(context, &msglist)?;
|
log_msglist(context, &msglist)?;
|
||||||
if let Some(draft) = sel_chat.get_id().get_draft(context)? {
|
if let Some(draft) = chat::get_draft(context, sel_chat.get_id())? {
|
||||||
log_msg(context, "Draft", &draft);
|
log_msg(context, "Draft", &draft);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{} messages.", sel_chat.get_id().get_msg_cnt(context));
|
println!(
|
||||||
|
"{} messages.",
|
||||||
|
chat::get_msg_cnt(context, sel_chat.get_id())
|
||||||
|
);
|
||||||
chat::marknoticed_chat(context, sel_chat.get_id())?;
|
chat::marknoticed_chat(context, sel_chat.get_id())?;
|
||||||
}
|
}
|
||||||
"createchat" => {
|
"createchat" => {
|
||||||
@@ -688,7 +698,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||||
|
|
||||||
let contacts = chat::get_chat_contacts(context, sel_chat.as_ref().unwrap().get_id());
|
let contacts = chat::get_chat_contacts(context, sel_chat.as_ref().unwrap().get_id());
|
||||||
println!("Memberlist:");
|
info!(context, "Memberlist:");
|
||||||
|
|
||||||
log_contactlist(context, &contacts);
|
log_contactlist(context, &contacts);
|
||||||
println!(
|
println!(
|
||||||
@@ -714,7 +724,8 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
let default_marker = "-".to_string();
|
let default_marker = "-".to_string();
|
||||||
for location in &locations {
|
for location in &locations {
|
||||||
let marker = location.marker.as_ref().unwrap_or(&default_marker);
|
let marker = location.marker.as_ref().unwrap_or(&default_marker);
|
||||||
println!(
|
info!(
|
||||||
|
context,
|
||||||
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} {} {}",
|
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} {} {}",
|
||||||
location.location_id,
|
location.location_id,
|
||||||
dc_timestamp_to_str(location.timestamp),
|
dc_timestamp_to_str(location.timestamp),
|
||||||
@@ -728,7 +739,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if locations.is_empty() {
|
if locations.is_empty() {
|
||||||
println!("No locations.");
|
info!(context, "No locations.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"sendlocations" => {
|
"sendlocations" => {
|
||||||
@@ -794,7 +805,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
let chat = if let Some(ref sel_chat) = sel_chat {
|
let chat = if let Some(ref sel_chat) = sel_chat {
|
||||||
sel_chat.get_id()
|
sel_chat.get_id()
|
||||||
} else {
|
} else {
|
||||||
ChatId::new(0)
|
0 as libc::c_uint
|
||||||
};
|
};
|
||||||
|
|
||||||
let msglist = context.search_msgs(chat, arg1);
|
let msglist = context.search_msgs(chat, arg1);
|
||||||
@@ -808,14 +819,14 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
if !arg1.is_empty() {
|
if !arg1.is_empty() {
|
||||||
let mut draft = Message::new(Viewtype::Text);
|
let mut draft = Message::new(Viewtype::Text);
|
||||||
draft.set_text(Some(arg1.to_string()));
|
draft.set_text(Some(arg1.to_string()));
|
||||||
sel_chat
|
chat::set_draft(
|
||||||
.as_ref()
|
context,
|
||||||
.unwrap()
|
sel_chat.as_ref().unwrap().get_id(),
|
||||||
.get_id()
|
Some(&mut draft),
|
||||||
.set_draft(context, Some(&mut draft));
|
);
|
||||||
println!("Draft saved.");
|
println!("Draft saved.");
|
||||||
} else {
|
} else {
|
||||||
sel_chat.as_ref().unwrap().get_id().set_draft(context, None);
|
chat::set_draft(context, sel_chat.as_ref().unwrap().get_id(), None);
|
||||||
println!("Draft deleted.");
|
println!("Draft deleted.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -826,10 +837,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
);
|
);
|
||||||
let mut msg = Message::new(Viewtype::Text);
|
let mut msg = Message::new(Viewtype::Text);
|
||||||
msg.set_text(Some(arg1.to_string()));
|
msg.set_text(Some(arg1.to_string()));
|
||||||
chat::add_device_msg(context, None, Some(&mut msg))?;
|
chat::add_device_msg(context, &mut msg)?;
|
||||||
}
|
|
||||||
"updatedevicechats" => {
|
|
||||||
context.update_device_chats()?;
|
|
||||||
}
|
}
|
||||||
"listmedia" => {
|
"listmedia" => {
|
||||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||||
@@ -851,23 +859,19 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
print!("\n");
|
print!("\n");
|
||||||
}
|
}
|
||||||
"archive" | "unarchive" | "pin" | "unpin" => {
|
"archive" | "unarchive" => {
|
||||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||||
let chat_id = ChatId::new(arg1.parse()?);
|
let chat_id = arg1.parse()?;
|
||||||
chat_id.set_visibility(
|
chat::archive(
|
||||||
context,
|
context,
|
||||||
match arg0 {
|
chat_id,
|
||||||
"archive" => ChatVisibility::Archived,
|
if arg0 == "archive" { true } else { false },
|
||||||
"unarchive" | "unpin" => ChatVisibility::Normal,
|
|
||||||
"pin" => ChatVisibility::Pinned,
|
|
||||||
_ => panic!("Unexpected command (This should never happen)"),
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
"delchat" => {
|
"delchat" => {
|
||||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||||
let chat_id = ChatId::new(arg1.parse()?);
|
let chat_id = arg1.parse()?;
|
||||||
chat_id.delete(context)?;
|
chat::delete(context, chat_id)?;
|
||||||
}
|
}
|
||||||
"msginfo" => {
|
"msginfo" => {
|
||||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||||
@@ -883,12 +887,12 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
"forward" => {
|
"forward" => {
|
||||||
ensure!(
|
ensure!(
|
||||||
!arg1.is_empty() && !arg2.is_empty(),
|
!arg1.is_empty() && arg2.is_empty(),
|
||||||
"Arguments <msg-id> <chat-id> expected"
|
"Arguments <msg-id> <chat-id> expected"
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut msg_ids = [MsgId::new(0); 1];
|
let mut msg_ids = [MsgId::new(0); 1];
|
||||||
let chat_id = ChatId::new(arg2.parse()?);
|
let chat_id = arg2.parse()?;
|
||||||
msg_ids[0] = MsgId::new(arg1.parse()?);
|
msg_ids[0] = MsgId::new(arg1.parse()?);
|
||||||
chat::forward_msgs(context, &msg_ids, chat_id)?;
|
chat::forward_msgs(context, &msg_ids, chat_id)?;
|
||||||
}
|
}
|
||||||
@@ -940,14 +944,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
let contact = Contact::get_by_id(context, contact_id)?;
|
let contact = Contact::get_by_id(context, contact_id)?;
|
||||||
let name_n_addr = contact.get_name_n_addr();
|
let name_n_addr = contact.get_name_n_addr();
|
||||||
|
|
||||||
let mut res = format!(
|
let mut res = format!("Contact info for: {}:\n\n", name_n_addr);
|
||||||
"Contact info for: {}:\nIcon: {}\n",
|
|
||||||
name_n_addr,
|
|
||||||
match contact.get_profile_image(context) {
|
|
||||||
Some(image) => image.to_str().unwrap().to_string(),
|
|
||||||
None => "NoIcon".to_string(),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
res += &Contact::get_encrinfo(context, contact_id)?;
|
res += &Contact::get_encrinfo(context, contact_id)?;
|
||||||
|
|
||||||
@@ -984,31 +981,6 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
res.get_text2()
|
res.get_text2()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
"setqr" => {
|
|
||||||
ensure!(!arg1.is_empty(), "Argument <qr-content> missing.");
|
|
||||||
match set_config_from_qr(context, arg1) {
|
|
||||||
Ok(()) => println!("Config set from QR code, you can now call 'configure'"),
|
|
||||||
Err(err) => println!("Cannot set config from QR code: {:?}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"providerinfo" => {
|
|
||||||
ensure!(!arg1.is_empty(), "Argument <addr> missing.");
|
|
||||||
match provider::get_provider_info(arg1) {
|
|
||||||
Some(info) => {
|
|
||||||
println!("Information for provider belonging to {}:", arg1);
|
|
||||||
println!("status: {}", info.status as u32);
|
|
||||||
println!("before_login_hint: {}", info.before_login_hint);
|
|
||||||
println!("after_login_hint: {}", info.after_login_hint);
|
|
||||||
println!("overview_page: {}", info.overview_page);
|
|
||||||
for server in info.server.iter() {
|
|
||||||
println!("server: {}:{}", server.hostname, server.port,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
println!("No information for provider belonging to {} found.", arg1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: implement this again, unclear how to match this through though, without writing a parser.
|
// TODO: implement this again, unclear how to match this through though, without writing a parser.
|
||||||
// "event" => {
|
// "event" => {
|
||||||
// ensure!(!arg1.is_empty(), "Argument <id> missing.");
|
// ensure!(!arg1.is_empty(), "Argument <id> missing.");
|
||||||
@@ -1030,16 +1002,6 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
bail!("Command failed.");
|
bail!("Command failed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"estimatedeletion" => {
|
|
||||||
ensure!(!arg1.is_empty(), "Argument <seconds> missing");
|
|
||||||
let seconds = arg1.parse()?;
|
|
||||||
let device_cnt = message::estimate_deletion_cnt(context, false, seconds)?;
|
|
||||||
let server_cnt = message::estimate_deletion_cnt(context, true, seconds)?;
|
|
||||||
println!(
|
|
||||||
"estimated count of messages older than {} seconds:\non device: {}\non server: {}",
|
|
||||||
seconds, device_cnt, server_cnt
|
|
||||||
);
|
|
||||||
}
|
|
||||||
"emptyserver" => {
|
"emptyserver" => {
|
||||||
ensure!(!arg1.is_empty(), "Argument <flags> missing");
|
ensure!(!arg1.is_empty(), "Argument <flags> missing");
|
||||||
|
|
||||||
@@ -1049,5 +1011,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), Error> {
|
|||||||
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
|
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(arg1_c as *mut _);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate deltachat;
|
extern crate deltachat;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
extern crate failure;
|
||||||
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rusqlite;
|
extern crate rusqlite;
|
||||||
@@ -18,9 +20,8 @@ use std::process::Command;
|
|||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
use anyhow::{bail, Error};
|
|
||||||
use deltachat::chat::ChatId;
|
|
||||||
use deltachat::config;
|
use deltachat::config;
|
||||||
|
use deltachat::configure::*;
|
||||||
use deltachat::context::*;
|
use deltachat::context::*;
|
||||||
use deltachat::job::*;
|
use deltachat::job::*;
|
||||||
use deltachat::oauth2::*;
|
use deltachat::oauth2::*;
|
||||||
@@ -40,7 +41,7 @@ use self::cmdline::*;
|
|||||||
|
|
||||||
// Event Handler
|
// Event Handler
|
||||||
|
|
||||||
fn receive_event(_context: &Context, event: Event) {
|
fn receive_event(_context: &Context, event: Event) -> libc::uintptr_t {
|
||||||
match event {
|
match event {
|
||||||
Event::Info(msg) => {
|
Event::Info(msg) => {
|
||||||
/* do not show the event as this would fill the screen */
|
/* do not show the event as this would fill the screen */
|
||||||
@@ -110,6 +111,8 @@ fn receive_event(_context: &Context, event: Event) {
|
|||||||
print!("\x1b[33m{{Received {:?}}}\n\x1b[0m", event);
|
print!("\x1b[33m{{Received {:?}}}\n\x1b[0m", event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Threads for waiting for messages and for jobs
|
// Threads for waiting for messages and for jobs
|
||||||
@@ -147,11 +150,11 @@ fn start_threads(c: Arc<RwLock<Context>>) {
|
|||||||
let ctx = c.clone();
|
let ctx = c.clone();
|
||||||
let handle_imap = std::thread::spawn(move || loop {
|
let handle_imap = std::thread::spawn(move || loop {
|
||||||
while_running!({
|
while_running!({
|
||||||
perform_inbox_jobs(&ctx.read().unwrap());
|
perform_imap_jobs(&ctx.read().unwrap());
|
||||||
perform_inbox_fetch(&ctx.read().unwrap());
|
perform_imap_fetch(&ctx.read().unwrap());
|
||||||
while_running!({
|
while_running!({
|
||||||
let context = ctx.read().unwrap();
|
let context = ctx.read().unwrap();
|
||||||
perform_inbox_idle(&context);
|
perform_imap_idle(&context);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -199,7 +202,7 @@ fn stop_threads(context: &Context) {
|
|||||||
println!("Stopping threads");
|
println!("Stopping threads");
|
||||||
IS_RUNNING.store(false, Ordering::Relaxed);
|
IS_RUNNING.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
interrupt_inbox_idle(context);
|
interrupt_imap_idle(context);
|
||||||
interrupt_mvbox_idle(context);
|
interrupt_mvbox_idle(context);
|
||||||
interrupt_sentbox_idle(context);
|
interrupt_sentbox_idle(context);
|
||||||
interrupt_smtp_idle(context);
|
interrupt_smtp_idle(context);
|
||||||
@@ -232,7 +235,7 @@ impl Completer for DcHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const IMEX_COMMANDS: [&str; 12] = [
|
const IMEX_COMMANDS: [&'static str; 12] = [
|
||||||
"initiate-key-transfer",
|
"initiate-key-transfer",
|
||||||
"get-setupcodebegin",
|
"get-setupcodebegin",
|
||||||
"continue-key-transfer",
|
"continue-key-transfer",
|
||||||
@@ -247,7 +250,7 @@ const IMEX_COMMANDS: [&str; 12] = [
|
|||||||
"stop",
|
"stop",
|
||||||
];
|
];
|
||||||
|
|
||||||
const DB_COMMANDS: [&str; 11] = [
|
const DB_COMMANDS: [&'static str; 11] = [
|
||||||
"info",
|
"info",
|
||||||
"open",
|
"open",
|
||||||
"close",
|
"close",
|
||||||
@@ -261,7 +264,7 @@ const DB_COMMANDS: [&str; 11] = [
|
|||||||
"housekeeping",
|
"housekeeping",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CHAT_COMMANDS: [&str; 26] = [
|
const CHAT_COMMANDS: [&'static str; 24] = [
|
||||||
"listchats",
|
"listchats",
|
||||||
"listarchived",
|
"listarchived",
|
||||||
"chat",
|
"chat",
|
||||||
@@ -285,11 +288,9 @@ const CHAT_COMMANDS: [&str; 26] = [
|
|||||||
"listmedia",
|
"listmedia",
|
||||||
"archive",
|
"archive",
|
||||||
"unarchive",
|
"unarchive",
|
||||||
"pin",
|
|
||||||
"unpin",
|
|
||||||
"delchat",
|
"delchat",
|
||||||
];
|
];
|
||||||
const MESSAGE_COMMANDS: [&str; 8] = [
|
const MESSAGE_COMMANDS: [&'static str; 8] = [
|
||||||
"listmsgs",
|
"listmsgs",
|
||||||
"msginfo",
|
"msginfo",
|
||||||
"listfresh",
|
"listfresh",
|
||||||
@@ -299,7 +300,7 @@ const MESSAGE_COMMANDS: [&str; 8] = [
|
|||||||
"unstar",
|
"unstar",
|
||||||
"delmsg",
|
"delmsg",
|
||||||
];
|
];
|
||||||
const CONTACT_COMMANDS: [&str; 6] = [
|
const CONTACT_COMMANDS: [&'static str; 6] = [
|
||||||
"listcontacts",
|
"listcontacts",
|
||||||
"listverified",
|
"listverified",
|
||||||
"addcontact",
|
"addcontact",
|
||||||
@@ -307,17 +308,8 @@ const CONTACT_COMMANDS: [&str; 6] = [
|
|||||||
"delcontact",
|
"delcontact",
|
||||||
"cleanupcontacts",
|
"cleanupcontacts",
|
||||||
];
|
];
|
||||||
const MISC_COMMANDS: [&str; 10] = [
|
const MISC_COMMANDS: [&'static str; 9] = [
|
||||||
"getqr",
|
"getqr", "getbadqr", "checkqr", "event", "fileinfo", "clear", "exit", "quit", "help",
|
||||||
"getbadqr",
|
|
||||||
"checkqr",
|
|
||||||
"event",
|
|
||||||
"fileinfo",
|
|
||||||
"clear",
|
|
||||||
"exit",
|
|
||||||
"quit",
|
|
||||||
"help",
|
|
||||||
"estimatedeletion",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
impl Hinter for DcHelper {
|
impl Hinter for DcHelper {
|
||||||
@@ -342,8 +334,8 @@ impl Hinter for DcHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static COLORED_PROMPT: &str = "\x1b[1;32m> \x1b[0m";
|
static COLORED_PROMPT: &'static str = "\x1b[1;32m> \x1b[0m";
|
||||||
static PROMPT: &str = "> ";
|
static PROMPT: &'static str = "> ";
|
||||||
|
|
||||||
impl Highlighter for DcHelper {
|
impl Highlighter for DcHelper {
|
||||||
fn highlight_prompt<'p>(&self, prompt: &'p str) -> Cow<'p, str> {
|
fn highlight_prompt<'p>(&self, prompt: &'p str) -> Cow<'p, str> {
|
||||||
@@ -369,10 +361,10 @@ impl Highlighter for DcHelper {
|
|||||||
|
|
||||||
impl Helper for DcHelper {}
|
impl Helper for DcHelper {}
|
||||||
|
|
||||||
fn main_0(args: Vec<String>) -> Result<(), Error> {
|
fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
println!("Error: Bad arguments, expected [db-name].");
|
println!("Error: Bad arguments, expected [db-name].");
|
||||||
bail!("No db-name specified");
|
return Err(format_err!("No db-name specified"));
|
||||||
}
|
}
|
||||||
let context = Context::new(
|
let context = Context::new(
|
||||||
Box::new(receive_event),
|
Box::new(receive_event),
|
||||||
@@ -411,7 +403,7 @@ fn main_0(args: Vec<String>) -> Result<(), Error> {
|
|||||||
// TODO: ignore "set mail_pw"
|
// TODO: ignore "set mail_pw"
|
||||||
rl.add_history_entry(line.as_str());
|
rl.add_history_entry(line.as_str());
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
match handle_cmd(line.trim(), ctx) {
|
match unsafe { handle_cmd(line.trim(), ctx) } {
|
||||||
Ok(ExitResult::Continue) => {}
|
Ok(ExitResult::Continue) => {}
|
||||||
Ok(ExitResult::Exit) => break,
|
Ok(ExitResult::Exit) => break,
|
||||||
Err(err) => println!("Error: {}", err),
|
Err(err) => println!("Error: {}", err),
|
||||||
@@ -442,7 +434,7 @@ enum ExitResult {
|
|||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, Error> {
|
unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, failure::Error> {
|
||||||
let mut args = line.splitn(2, ' ');
|
let mut args = line.splitn(2, ' ');
|
||||||
let arg0 = args.next().unwrap_or_default();
|
let arg0 = args.next().unwrap_or_default();
|
||||||
let arg1 = args.next().unwrap_or_default();
|
let arg1 = args.next().unwrap_or_default();
|
||||||
@@ -463,14 +455,14 @@ fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, Error
|
|||||||
}
|
}
|
||||||
"imap-jobs" => {
|
"imap-jobs" => {
|
||||||
if HANDLE.clone().lock().unwrap().is_some() {
|
if HANDLE.clone().lock().unwrap().is_some() {
|
||||||
println!("inbox-jobs are already running in a thread.");
|
println!("imap-jobs are already running in a thread.");
|
||||||
} else {
|
} else {
|
||||||
perform_inbox_jobs(&ctx.read().unwrap());
|
perform_imap_jobs(&ctx.read().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"configure" => {
|
"configure" => {
|
||||||
start_threads(ctx.clone());
|
start_threads(ctx.clone());
|
||||||
ctx.read().unwrap().configure();
|
configure(&ctx.read().unwrap());
|
||||||
}
|
}
|
||||||
"oauth2" => {
|
"oauth2" => {
|
||||||
if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) {
|
if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) {
|
||||||
@@ -494,10 +486,9 @@ fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, Error
|
|||||||
}
|
}
|
||||||
"getqr" | "getbadqr" => {
|
"getqr" | "getbadqr" => {
|
||||||
start_threads(ctx.clone());
|
start_threads(ctx.clone());
|
||||||
if let Some(mut qr) = dc_get_securejoin_qr(
|
if let Some(mut qr) =
|
||||||
&ctx.read().unwrap(),
|
dc_get_securejoin_qr(&ctx.read().unwrap(), arg1.parse().unwrap_or_default())
|
||||||
ChatId::new(arg1.parse().unwrap_or_default()),
|
{
|
||||||
) {
|
|
||||||
if !qr.is_empty() {
|
if !qr.is_empty() {
|
||||||
if arg0 == "getbadqr" && qr.len() > 40 {
|
if arg0 == "getbadqr" && qr.len() > 40 {
|
||||||
qr.replace_range(12..22, "0000000000")
|
qr.replace_range(12..22, "0000000000")
|
||||||
@@ -525,7 +516,7 @@ fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, Error
|
|||||||
Ok(ExitResult::Continue)
|
Ok(ExitResult::Continue)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() -> Result<(), Error> {
|
pub fn main() -> Result<(), failure::Error> {
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
|||||||
@@ -7,26 +7,29 @@ use tempfile::tempdir;
|
|||||||
use deltachat::chat;
|
use deltachat::chat;
|
||||||
use deltachat::chatlist::*;
|
use deltachat::chatlist::*;
|
||||||
use deltachat::config;
|
use deltachat::config;
|
||||||
|
use deltachat::configure::*;
|
||||||
use deltachat::contact::*;
|
use deltachat::contact::*;
|
||||||
use deltachat::context::*;
|
use deltachat::context::*;
|
||||||
use deltachat::job::{
|
use deltachat::job::{
|
||||||
perform_inbox_fetch, perform_inbox_idle, perform_inbox_jobs, perform_smtp_idle,
|
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
|
||||||
perform_smtp_jobs,
|
|
||||||
};
|
};
|
||||||
use deltachat::Event;
|
use deltachat::Event;
|
||||||
|
|
||||||
fn cb(_ctx: &Context, event: Event) {
|
fn cb(_ctx: &Context, event: Event) -> usize {
|
||||||
print!("[{:?}]", event);
|
print!("[{:?}]", event);
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::ConfigureProgress(progress) => {
|
Event::ConfigureProgress(progress) => {
|
||||||
println!(" progress: {}", progress);
|
print!(" progress: {}\n", progress);
|
||||||
|
0
|
||||||
}
|
}
|
||||||
Event::Info(msg) | Event::Warning(msg) | Event::Error(msg) | Event::ErrorNetwork(msg) => {
|
Event::Info(msg) | Event::Warning(msg) | Event::Error(msg) | Event::ErrorNetwork(msg) => {
|
||||||
println!(" {}", msg);
|
print!(" {}\n", msg);
|
||||||
|
0
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!();
|
print!("\n");
|
||||||
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,12 +50,12 @@ fn main() {
|
|||||||
let r1 = running.clone();
|
let r1 = running.clone();
|
||||||
let t1 = thread::spawn(move || {
|
let t1 = thread::spawn(move || {
|
||||||
while *r1.read().unwrap() {
|
while *r1.read().unwrap() {
|
||||||
perform_inbox_jobs(&ctx1);
|
perform_imap_jobs(&ctx1);
|
||||||
if *r1.read().unwrap() {
|
if *r1.read().unwrap() {
|
||||||
perform_inbox_fetch(&ctx1);
|
perform_imap_fetch(&ctx1);
|
||||||
|
|
||||||
if *r1.read().unwrap() {
|
if *r1.read().unwrap() {
|
||||||
perform_inbox_idle(&ctx1);
|
perform_imap_idle(&ctx1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +79,7 @@ fn main() {
|
|||||||
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
|
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
|
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
|
||||||
ctx.configure();
|
configure(&ctx);
|
||||||
|
|
||||||
thread::sleep(duration);
|
thread::sleep(duration);
|
||||||
|
|
||||||
@@ -97,10 +100,20 @@ fn main() {
|
|||||||
|
|
||||||
thread::sleep(duration);
|
thread::sleep(duration);
|
||||||
|
|
||||||
|
// let msglist = dc_get_chat_msgs(&ctx, chat_id, 0, 0);
|
||||||
|
// for i in 0..dc_array_get_cnt(msglist) {
|
||||||
|
// let msg_id = dc_array_get_id(msglist, i);
|
||||||
|
// let msg = dc_get_msg(context, msg_id);
|
||||||
|
// let text = CStr::from_ptr(dc_msg_get_text(msg)).unwrap();
|
||||||
|
// println!("Message {}: {}\n", i + 1, text.to_str().unwrap());
|
||||||
|
// dc_msg_unref(msg);
|
||||||
|
// }
|
||||||
|
// dc_array_unref(msglist);
|
||||||
|
|
||||||
println!("stopping threads");
|
println!("stopping threads");
|
||||||
|
|
||||||
*running.write().unwrap() = false;
|
*running.clone().write().unwrap() = false;
|
||||||
deltachat::job::interrupt_inbox_idle(&ctx);
|
deltachat::job::interrupt_imap_idle(&ctx);
|
||||||
deltachat::job::interrupt_smtp_idle(&ctx);
|
deltachat::job::interrupt_smtp_idle(&ctx);
|
||||||
|
|
||||||
println!("joining");
|
println!("joining");
|
||||||
|
|||||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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_string_lossy().as_bytes())
|
||||||
|
{
|
||||||
|
let data = std::slice::from_raw_parts(s as *const u8, strlen(s));
|
||||||
|
|
||||||
|
let (res, _, _) = encoding.decode(data);
|
||||||
|
let res_c = CString::new(res.as_bytes()).unwrap_or_default();
|
||||||
|
*result = strdup(res_c.as_ptr()) as *mut _;
|
||||||
|
|
||||||
|
MAIL_CHARCONV_NO_ERROR as libc::c_int
|
||||||
|
} else {
|
||||||
|
MAIL_CHARCONV_ERROR_UNKNOWN_CHARSET as libc::c_int
|
||||||
|
}
|
||||||
|
}
|
||||||
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
@@ -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
@@ -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;
|
||||||
386
mmime/src/display.rs
Normal file
@@ -0,0 +1,386 @@
|
|||||||
|
use crate::clist::*;
|
||||||
|
|
||||||
|
use crate::mailimf::types::*;
|
||||||
|
use crate::mailmime::types::*;
|
||||||
|
|
||||||
|
use std::ffi::CStr;
|
||||||
|
|
||||||
|
pub unsafe fn display_mime(mut mime: *mut Mailmime) {
|
||||||
|
let mut cur: *mut clistiter = 0 as *mut clistiter;
|
||||||
|
println!("{}", (*mime).mm_type);
|
||||||
|
|
||||||
|
match (*mime).mm_type as u32 {
|
||||||
|
MAILMIME_SINGLE => {
|
||||||
|
println!("single part");
|
||||||
|
}
|
||||||
|
MAILMIME_MULTIPLE => {
|
||||||
|
println!("multipart");
|
||||||
|
}
|
||||||
|
MAILMIME_MESSAGE => println!("message"),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if !(*mime).mm_mime_fields.is_null() {
|
||||||
|
if !(*(*(*mime).mm_mime_fields).fld_list).first.is_null() {
|
||||||
|
print!("MIME headers begin");
|
||||||
|
display_mime_fields((*mime).mm_mime_fields);
|
||||||
|
println!("MIME headers end");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
display_mime_content((*mime).mm_content_type);
|
||||||
|
match (*mime).mm_type as u32 {
|
||||||
|
MAILMIME_SINGLE => {
|
||||||
|
display_mime_data((*mime).mm_data.mm_single);
|
||||||
|
}
|
||||||
|
MAILMIME_MULTIPLE => {
|
||||||
|
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
|
||||||
|
while !cur.is_null() {
|
||||||
|
display_mime(
|
||||||
|
(if !cur.is_null() {
|
||||||
|
(*cur).data
|
||||||
|
} else {
|
||||||
|
0 as *mut libc::c_void
|
||||||
|
}) as *mut Mailmime,
|
||||||
|
);
|
||||||
|
cur = if !cur.is_null() {
|
||||||
|
(*cur).next
|
||||||
|
} else {
|
||||||
|
0 as *mut clistcell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MAILMIME_MESSAGE => {
|
||||||
|
if !(*mime).mm_data.mm_message.mm_fields.is_null() {
|
||||||
|
if !(*(*(*mime).mm_data.mm_message.mm_fields).fld_list)
|
||||||
|
.first
|
||||||
|
.is_null()
|
||||||
|
{
|
||||||
|
println!("headers begin");
|
||||||
|
display_fields((*mime).mm_data.mm_message.mm_fields);
|
||||||
|
println!("headers end");
|
||||||
|
}
|
||||||
|
if !(*mime).mm_data.mm_message.mm_msg_mime.is_null() {
|
||||||
|
display_mime((*mime).mm_data.mm_message.mm_msg_mime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn display_mime_content(mut content_type: *mut mailmime_content) {
|
||||||
|
print!("type: ");
|
||||||
|
display_mime_type((*content_type).ct_type);
|
||||||
|
println!(
|
||||||
|
"/{}",
|
||||||
|
CStr::from_ptr((*content_type).ct_subtype).to_str().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
unsafe fn display_mime_type(mut type_0: *mut mailmime_type) {
|
||||||
|
match (*type_0).tp_type {
|
||||||
|
1 => {
|
||||||
|
display_mime_discrete_type((*type_0).tp_data.tp_discrete_type);
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
display_mime_composite_type((*type_0).tp_data.tp_composite_type);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
unsafe fn display_mime_composite_type(mut ct: *mut mailmime_composite_type) {
|
||||||
|
match (*ct).ct_type {
|
||||||
|
1 => {
|
||||||
|
print!("message");
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
print!("multipart");
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
print!("{}", CStr::from_ptr((*ct).ct_token).to_str().unwrap());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
unsafe fn display_mime_discrete_type(mut discrete_type: *mut mailmime_discrete_type) {
|
||||||
|
match (*discrete_type).dt_type {
|
||||||
|
1 => {
|
||||||
|
print!("text");
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
print!("image");
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
print!("audio");
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
print!("video");
|
||||||
|
}
|
||||||
|
5 => {
|
||||||
|
print!("application");
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
print!("{}", (*discrete_type).dt_extension as u8 as char);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub unsafe fn display_mime_data(mut data: *mut mailmime_data) {
|
||||||
|
match (*data).dt_type {
|
||||||
|
0 => {
|
||||||
|
println!(
|
||||||
|
"data : {} bytes",
|
||||||
|
(*data).dt_data.dt_text.dt_length as libc::c_uint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
println!(
|
||||||
|
"data (file) : {}",
|
||||||
|
CStr::from_ptr((*data).dt_data.dt_filename)
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
unsafe fn display_mime_dsp_parm(mut param: *mut mailmime_disposition_parm) {
|
||||||
|
match (*param).pa_type {
|
||||||
|
0 => {
|
||||||
|
println!(
|
||||||
|
"filename: {}",
|
||||||
|
CStr::from_ptr((*param).pa_data.pa_filename)
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
unsafe fn display_mime_disposition(mut disposition: *mut mailmime_disposition) {
|
||||||
|
let mut cur: *mut clistiter = 0 as *mut clistiter;
|
||||||
|
cur = (*(*disposition).dsp_parms).first;
|
||||||
|
while !cur.is_null() {
|
||||||
|
let mut param: *mut mailmime_disposition_parm = 0 as *mut mailmime_disposition_parm;
|
||||||
|
param = (if !cur.is_null() {
|
||||||
|
(*cur).data
|
||||||
|
} else {
|
||||||
|
0 as *mut libc::c_void
|
||||||
|
}) as *mut mailmime_disposition_parm;
|
||||||
|
display_mime_dsp_parm(param);
|
||||||
|
cur = if !cur.is_null() {
|
||||||
|
(*cur).next
|
||||||
|
} else {
|
||||||
|
0 as *mut clistcell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe fn display_mime_field(mut field: *mut mailmime_field) {
|
||||||
|
match (*field).fld_type {
|
||||||
|
1 => {
|
||||||
|
print!("content-type: ");
|
||||||
|
display_mime_content((*field).fld_data.fld_content);
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
display_mime_disposition((*field).fld_data.fld_disposition);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
unsafe fn display_mime_fields(mut fields: *mut mailmime_fields) {
|
||||||
|
let mut cur: *mut clistiter = 0 as *mut clistiter;
|
||||||
|
cur = (*(*fields).fld_list).first;
|
||||||
|
while !cur.is_null() {
|
||||||
|
let mut field: *mut mailmime_field = 0 as *mut mailmime_field;
|
||||||
|
field = (if !cur.is_null() {
|
||||||
|
(*cur).data
|
||||||
|
} else {
|
||||||
|
0 as *mut libc::c_void
|
||||||
|
}) as *mut mailmime_field;
|
||||||
|
display_mime_field(field);
|
||||||
|
cur = if !cur.is_null() {
|
||||||
|
(*cur).next
|
||||||
|
} else {
|
||||||
|
0 as *mut clistcell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe fn display_date_time(mut d: *mut mailimf_date_time) {
|
||||||
|
print!(
|
||||||
|
"{:02}/{:02}/{:02} {:02}:{:02}:{:02} +{:04}",
|
||||||
|
(*d).dt_day,
|
||||||
|
(*d).dt_month,
|
||||||
|
(*d).dt_year,
|
||||||
|
(*d).dt_hour,
|
||||||
|
(*d).dt_min,
|
||||||
|
(*d).dt_sec,
|
||||||
|
(*d).dt_zone,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
unsafe fn display_orig_date(mut orig_date: *mut mailimf_orig_date) {
|
||||||
|
display_date_time((*orig_date).dt_date_time);
|
||||||
|
}
|
||||||
|
unsafe fn display_mailbox(mut mb: *mut mailimf_mailbox) {
|
||||||
|
if !(*mb).mb_display_name.is_null() {
|
||||||
|
print!(
|
||||||
|
"{}",
|
||||||
|
CStr::from_ptr((*mb).mb_display_name).to_str().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
print!("<{}>", CStr::from_ptr((*mb).mb_addr_spec).to_str().unwrap());
|
||||||
|
}
|
||||||
|
unsafe fn display_mailbox_list(mut mb_list: *mut mailimf_mailbox_list) {
|
||||||
|
let mut cur: *mut clistiter = 0 as *mut clistiter;
|
||||||
|
cur = (*(*mb_list).mb_list).first;
|
||||||
|
while !cur.is_null() {
|
||||||
|
let mut mb: *mut mailimf_mailbox = 0 as *mut mailimf_mailbox;
|
||||||
|
mb = (if !cur.is_null() {
|
||||||
|
(*cur).data
|
||||||
|
} else {
|
||||||
|
0 as *mut libc::c_void
|
||||||
|
}) as *mut mailimf_mailbox;
|
||||||
|
display_mailbox(mb);
|
||||||
|
if !if !cur.is_null() {
|
||||||
|
(*cur).next
|
||||||
|
} else {
|
||||||
|
0 as *mut clistcell
|
||||||
|
}
|
||||||
|
.is_null()
|
||||||
|
{
|
||||||
|
print!(", ");
|
||||||
|
}
|
||||||
|
cur = if !cur.is_null() {
|
||||||
|
(*cur).next
|
||||||
|
} else {
|
||||||
|
0 as *mut clistcell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe fn display_group(mut group: *mut mailimf_group) {
|
||||||
|
let mut cur: *mut clistiter = 0 as *mut clistiter;
|
||||||
|
print!(
|
||||||
|
"{}: ",
|
||||||
|
CStr::from_ptr((*group).grp_display_name).to_str().unwrap()
|
||||||
|
);
|
||||||
|
cur = (*(*(*group).grp_mb_list).mb_list).first;
|
||||||
|
while !cur.is_null() {
|
||||||
|
let mut mb: *mut mailimf_mailbox = 0 as *mut mailimf_mailbox;
|
||||||
|
mb = (if !cur.is_null() {
|
||||||
|
(*cur).data
|
||||||
|
} else {
|
||||||
|
0 as *mut libc::c_void
|
||||||
|
}) as *mut mailimf_mailbox;
|
||||||
|
display_mailbox(mb);
|
||||||
|
cur = if !cur.is_null() {
|
||||||
|
(*cur).next
|
||||||
|
} else {
|
||||||
|
0 as *mut clistcell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print!("; ");
|
||||||
|
}
|
||||||
|
unsafe fn display_address(mut a: *mut mailimf_address) {
|
||||||
|
match (*a).ad_type {
|
||||||
|
2 => {
|
||||||
|
display_group((*a).ad_data.ad_group);
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
display_mailbox((*a).ad_data.ad_mailbox);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
unsafe fn display_address_list(mut addr_list: *mut mailimf_address_list) {
|
||||||
|
let mut cur: *mut clistiter = 0 as *mut clistiter;
|
||||||
|
cur = (*(*addr_list).ad_list).first;
|
||||||
|
while !cur.is_null() {
|
||||||
|
let mut addr: *mut mailimf_address = 0 as *mut mailimf_address;
|
||||||
|
addr = (if !cur.is_null() {
|
||||||
|
(*cur).data
|
||||||
|
} else {
|
||||||
|
0 as *mut libc::c_void
|
||||||
|
}) as *mut mailimf_address;
|
||||||
|
display_address(addr);
|
||||||
|
if !if !cur.is_null() {
|
||||||
|
(*cur).next
|
||||||
|
} else {
|
||||||
|
0 as *mut clistcell
|
||||||
|
}
|
||||||
|
.is_null()
|
||||||
|
{
|
||||||
|
print!(", ");
|
||||||
|
}
|
||||||
|
cur = if !cur.is_null() {
|
||||||
|
(*cur).next
|
||||||
|
} else {
|
||||||
|
0 as *mut clistcell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe fn display_from(mut from: *mut mailimf_from) {
|
||||||
|
display_mailbox_list((*from).frm_mb_list);
|
||||||
|
}
|
||||||
|
unsafe fn display_to(mut to: *mut mailimf_to) {
|
||||||
|
display_address_list((*to).to_addr_list);
|
||||||
|
}
|
||||||
|
unsafe fn display_cc(mut cc: *mut mailimf_cc) {
|
||||||
|
display_address_list((*cc).cc_addr_list);
|
||||||
|
}
|
||||||
|
unsafe fn display_subject(mut subject: *mut mailimf_subject) {
|
||||||
|
print!("{}", CStr::from_ptr((*subject).sbj_value).to_str().unwrap());
|
||||||
|
}
|
||||||
|
unsafe fn display_field(mut field: *mut mailimf_field) {
|
||||||
|
match (*field).fld_type {
|
||||||
|
9 => {
|
||||||
|
print!("Date: ");
|
||||||
|
display_orig_date((*field).fld_data.fld_orig_date);
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
10 => {
|
||||||
|
print!("From: ");
|
||||||
|
display_from((*field).fld_data.fld_from);
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
13 => {
|
||||||
|
print!("To: ");
|
||||||
|
display_to((*field).fld_data.fld_to);
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
14 => {
|
||||||
|
print!("Cc: ");
|
||||||
|
display_cc((*field).fld_data.fld_cc);
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
19 => {
|
||||||
|
print!("Subject: ");
|
||||||
|
display_subject((*field).fld_data.fld_subject);
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
16 => {
|
||||||
|
println!(
|
||||||
|
"Message-ID: {}",
|
||||||
|
CStr::from_ptr((*(*field).fld_data.fld_message_id).mid_value)
|
||||||
|
.to_str()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
unsafe fn display_fields(mut fields: *mut mailimf_fields) {
|
||||||
|
let mut cur: *mut clistiter = 0 as *mut clistiter;
|
||||||
|
cur = (*(*fields).fld_list).first;
|
||||||
|
while !cur.is_null() {
|
||||||
|
let mut f: *mut mailimf_field = 0 as *mut mailimf_field;
|
||||||
|
f = (if !cur.is_null() {
|
||||||
|
(*cur).data
|
||||||
|
} else {
|
||||||
|
0 as *mut libc::c_void
|
||||||
|
}) as *mut mailimf_field;
|
||||||
|
display_field(f);
|
||||||
|
cur = if !cur.is_null() {
|
||||||
|
(*cur).next
|
||||||
|
} else {
|
||||||
|
0 as *mut clistcell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
mmime/src/lib.rs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#![deny(clippy::correctness)]
|
||||||
|
// TODO: make all of these errors, such that clippy actually passes.
|
||||||
|
#![warn(clippy::all, clippy::perf, clippy::not_unsafe_ptr_arg_deref)]
|
||||||
|
// This is nice, but for now just annoying.
|
||||||
|
#![allow(clippy::unreadable_literal)]
|
||||||
|
#![feature(ptr_wrapping_offset_from)]
|
||||||
|
#![allow(unused_attributes)]
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
#![allow(mutable_transmutes)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(unused_assignments)]
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
#![allow(unused_must_use)]
|
||||||
|
#![feature(extern_types)]
|
||||||
|
#![feature(const_raw_ptr_to_usize_cast)]
|
||||||
|
|
||||||
|
pub mod charconv;
|
||||||
|
pub mod chash;
|
||||||
|
pub mod clist;
|
||||||
|
pub mod display;
|
||||||
|
pub mod mailimf;
|
||||||
|
pub mod mailmime;
|
||||||
|
pub mod mmapstring;
|
||||||
|
pub mod other;
|
||||||
|
|
||||||
|
pub use self::charconv::*;
|
||||||
|
pub use self::chash::*;
|
||||||
|
pub use self::clist::*;
|
||||||
|
pub use self::display::*;
|
||||||
|
pub use self::mailimf::*;
|
||||||
|
pub use self::mailmime::*;
|
||||||
|
pub use self::mmapstring::*;
|
||||||
|
pub use self::other::*;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mailmime_parse_test() {
|
||||||
|
unsafe {
|
||||||
|
let data = "MIME-Version: 1.0\
|
||||||
|
Content-Type: multipart/mixed; boundary=frontier\
|
||||||
|
\
|
||||||
|
This is a message with multiple parts in MIME format.\
|
||||||
|
--frontier\
|
||||||
|
Content-Type: text/plain\
|
||||||
|
\
|
||||||
|
This is the body of the message.\
|
||||||
|
--frontier\
|
||||||
|
Content-Type: application/octet-stream\
|
||||||
|
Content-Transfer-Encoding: base64\
|
||||||
|
\
|
||||||
|
PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg\
|
||||||
|
Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==\
|
||||||
|
--frontier--";
|
||||||
|
let c_data = std::ffi::CString::new(data).unwrap();
|
||||||
|
|
||||||
|
let mut current_index = 0;
|
||||||
|
let mut mime = std::ptr::null_mut();
|
||||||
|
let res = crate::mailmime::content::mailmime_parse(
|
||||||
|
c_data.as_ptr(),
|
||||||
|
data.len() as usize,
|
||||||
|
&mut current_index,
|
||||||
|
&mut mime,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(res, MAIL_NO_ERROR as libc::c_int);
|
||||||
|
assert!(!mime.is_null());
|
||||||
|
|
||||||
|
display_mime(mime);
|
||||||
|
|
||||||
|
mailmime::types::mailmime_free(mime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5921
mmime/src/mailimf/mod.rs
Normal file
1196
mmime/src/mailimf/types.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
2357
mmime/src/mailmime/content.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
@@ -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
891
mmime/src/mailmime/types.rs
Normal file
@@ -0,0 +1,891 @@
|
|||||||
|
use crate::clist::*;
|
||||||
|
use crate::mailimf::types::*;
|
||||||
|
use crate::mmapstring::*;
|
||||||
|
use crate::other::*;
|
||||||
|
|
||||||
|
pub const MAILMIME_MECHANISM_TOKEN: libc::c_uint = 6;
|
||||||
|
pub const MAILMIME_MECHANISM_BASE64: libc::c_uint = 5;
|
||||||
|
pub const MAILMIME_MECHANISM_QUOTED_PRINTABLE: libc::c_uint = 4;
|
||||||
|
pub const MAILMIME_MECHANISM_BINARY: libc::c_uint = 3;
|
||||||
|
pub const MAILMIME_MECHANISM_8BIT: libc::c_uint = 2;
|
||||||
|
pub const MAILMIME_MECHANISM_7BIT: libc::c_uint = 1;
|
||||||
|
pub const MAILMIME_MECHANISM_ERROR: libc::c_uint = 0;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_composite_type {
|
||||||
|
pub ct_type: libc::c_int,
|
||||||
|
pub ct_token: *mut libc::c_char,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_content {
|
||||||
|
pub ct_type: *mut mailmime_type,
|
||||||
|
pub ct_subtype: *mut libc::c_char,
|
||||||
|
pub ct_parameters: *mut clist,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_type {
|
||||||
|
pub tp_type: libc::c_int,
|
||||||
|
pub tp_data: unnamed,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub union unnamed {
|
||||||
|
pub tp_discrete_type: *mut mailmime_discrete_type,
|
||||||
|
pub tp_composite_type: *mut mailmime_composite_type,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_discrete_type {
|
||||||
|
pub dt_type: libc::c_int,
|
||||||
|
pub dt_extension: *mut libc::c_char,
|
||||||
|
}
|
||||||
|
pub type unnamed_0 = libc::c_uint;
|
||||||
|
pub const MAILMIME_FIELD_LOCATION: unnamed_0 = 8;
|
||||||
|
pub const MAILMIME_FIELD_LANGUAGE: unnamed_0 = 7;
|
||||||
|
pub const MAILMIME_FIELD_DISPOSITION: unnamed_0 = 6;
|
||||||
|
pub const MAILMIME_FIELD_VERSION: unnamed_0 = 5;
|
||||||
|
pub const MAILMIME_FIELD_DESCRIPTION: unnamed_0 = 4;
|
||||||
|
pub const MAILMIME_FIELD_ID: unnamed_0 = 3;
|
||||||
|
pub const MAILMIME_FIELD_TRANSFER_ENCODING: unnamed_0 = 2;
|
||||||
|
pub const MAILMIME_FIELD_TYPE: unnamed_0 = 1;
|
||||||
|
pub const MAILMIME_FIELD_NONE: unnamed_0 = 0;
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_field {
|
||||||
|
pub fld_type: libc::c_int,
|
||||||
|
pub fld_data: unnamed_1,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub union unnamed_1 {
|
||||||
|
pub fld_content: *mut mailmime_content,
|
||||||
|
pub fld_encoding: *mut mailmime_mechanism,
|
||||||
|
pub fld_id: *mut libc::c_char,
|
||||||
|
pub fld_description: *mut libc::c_char,
|
||||||
|
pub fld_version: uint32_t,
|
||||||
|
pub fld_disposition: *mut mailmime_disposition,
|
||||||
|
pub fld_language: *mut mailmime_language,
|
||||||
|
pub fld_location: *mut libc::c_char,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_language {
|
||||||
|
pub lg_list: *mut clist,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_disposition {
|
||||||
|
pub dsp_type: *mut mailmime_disposition_type,
|
||||||
|
pub dsp_parms: *mut clist,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_disposition_type {
|
||||||
|
pub dsp_type: libc::c_int,
|
||||||
|
pub dsp_extension: *mut libc::c_char,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_mechanism {
|
||||||
|
pub enc_type: libc::c_int,
|
||||||
|
pub enc_token: *mut libc::c_char,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_fields {
|
||||||
|
pub fld_list: *mut clist,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_parameter {
|
||||||
|
pub pa_name: *mut libc::c_char,
|
||||||
|
pub pa_value: *mut libc::c_char,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_disposition_parm {
|
||||||
|
pub pa_type: libc::c_int,
|
||||||
|
pub pa_data: unnamed_3,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub union unnamed_3 {
|
||||||
|
pub pa_filename: *mut libc::c_char,
|
||||||
|
pub pa_creation_date: *mut libc::c_char,
|
||||||
|
pub pa_modification_date: *mut libc::c_char,
|
||||||
|
pub pa_read_date: *mut libc::c_char,
|
||||||
|
pub pa_size: size_t,
|
||||||
|
pub pa_parameter: *mut mailmime_parameter,
|
||||||
|
}
|
||||||
|
pub const MAILMIME_DISPOSITION_PARM_PARAMETER: unnamed_11 = 5;
|
||||||
|
pub const MAILMIME_DISPOSITION_PARM_READ_DATE: unnamed_11 = 3;
|
||||||
|
pub const MAILMIME_DISPOSITION_PARM_MODIFICATION_DATE: unnamed_11 = 2;
|
||||||
|
pub const MAILMIME_DISPOSITION_PARM_CREATION_DATE: unnamed_11 = 1;
|
||||||
|
pub const MAILMIME_DISPOSITION_PARM_FILENAME: unnamed_11 = 0;
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_multipart_body {
|
||||||
|
pub bd_list: *mut clist,
|
||||||
|
}
|
||||||
|
pub type unnamed_4 = libc::c_uint;
|
||||||
|
pub const MAILMIME_DATA_FILE: unnamed_4 = 1;
|
||||||
|
pub const MAILMIME_DATA_TEXT: unnamed_4 = 0;
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_data {
|
||||||
|
pub dt_type: libc::c_int,
|
||||||
|
pub dt_encoding: libc::c_int,
|
||||||
|
pub dt_encoded: libc::c_int,
|
||||||
|
pub dt_data: unnamed_5,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub union unnamed_5 {
|
||||||
|
pub dt_text: unnamed_6,
|
||||||
|
pub dt_filename: *mut libc::c_char,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct unnamed_6 {
|
||||||
|
pub dt_data: *const libc::c_char,
|
||||||
|
pub dt_length: size_t,
|
||||||
|
}
|
||||||
|
pub type unnamed_7 = libc::c_uint;
|
||||||
|
pub const MAILMIME_MESSAGE: unnamed_7 = 3;
|
||||||
|
pub const MAILMIME_MULTIPLE: unnamed_7 = 2;
|
||||||
|
pub const MAILMIME_SINGLE: unnamed_7 = 1;
|
||||||
|
pub const MAILMIME_NONE: unnamed_7 = 0;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Mailmime {
|
||||||
|
pub mm_parent_type: libc::c_int,
|
||||||
|
pub mm_parent: *mut Mailmime,
|
||||||
|
pub mm_multipart_pos: *mut clistiter,
|
||||||
|
pub mm_type: libc::c_int,
|
||||||
|
pub mm_mime_start: *const libc::c_char,
|
||||||
|
pub mm_length: size_t,
|
||||||
|
pub mm_mime_fields: *mut mailmime_fields,
|
||||||
|
pub mm_content_type: *mut mailmime_content,
|
||||||
|
pub mm_body: *mut mailmime_data,
|
||||||
|
pub mm_data: unnamed_8,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub union unnamed_8 {
|
||||||
|
pub mm_single: *mut mailmime_data,
|
||||||
|
pub mm_multipart: unnamed_10,
|
||||||
|
pub mm_message: unnamed_9,
|
||||||
|
}
|
||||||
|
/* message */
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct unnamed_9 {
|
||||||
|
pub mm_fields: *mut mailimf_fields,
|
||||||
|
pub mm_msg_mime: *mut Mailmime,
|
||||||
|
}
|
||||||
|
/* multi-part */
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct unnamed_10 {
|
||||||
|
pub mm_preamble: *mut mailmime_data,
|
||||||
|
pub mm_epilogue: *mut mailmime_data,
|
||||||
|
pub mm_mp_list: *mut clist,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_encoded_word {
|
||||||
|
pub wd_charset: *mut libc::c_char,
|
||||||
|
pub wd_text: *mut libc::c_char,
|
||||||
|
}
|
||||||
|
pub type unnamed_11 = libc::c_uint;
|
||||||
|
pub const MAILMIME_DISPOSITION_PARM_SIZE: unnamed_11 = 4;
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct mailmime_section {
|
||||||
|
pub sec_list: *mut clist,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_attribute_free(mut attribute: *mut libc::c_char) {
|
||||||
|
mailmime_token_free(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_token_free(mut token: *mut libc::c_char) {
|
||||||
|
free(token as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
pub unsafe fn mailmime_composite_type_new(
|
||||||
|
mut ct_type: libc::c_int,
|
||||||
|
mut ct_token: *mut libc::c_char,
|
||||||
|
) -> *mut mailmime_composite_type {
|
||||||
|
let mut ct: *mut mailmime_composite_type = 0 as *mut mailmime_composite_type;
|
||||||
|
ct = malloc(::std::mem::size_of::<mailmime_composite_type>() as libc::size_t)
|
||||||
|
as *mut mailmime_composite_type;
|
||||||
|
if ct.is_null() {
|
||||||
|
return 0 as *mut mailmime_composite_type;
|
||||||
|
}
|
||||||
|
(*ct).ct_type = ct_type;
|
||||||
|
(*ct).ct_token = ct_token;
|
||||||
|
return ct;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_composite_type_free(mut ct: *mut mailmime_composite_type) {
|
||||||
|
if !(*ct).ct_token.is_null() {
|
||||||
|
mailmime_extension_token_free((*ct).ct_token);
|
||||||
|
}
|
||||||
|
free(ct as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_extension_token_free(mut extension: *mut libc::c_char) {
|
||||||
|
mailmime_token_free(extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_content_new(
|
||||||
|
mut ct_type: *mut mailmime_type,
|
||||||
|
mut ct_subtype: *mut libc::c_char,
|
||||||
|
mut ct_parameters: *mut clist,
|
||||||
|
) -> *mut mailmime_content {
|
||||||
|
let mut content: *mut mailmime_content = 0 as *mut mailmime_content;
|
||||||
|
content =
|
||||||
|
malloc(::std::mem::size_of::<mailmime_content>() as libc::size_t) as *mut mailmime_content;
|
||||||
|
if content.is_null() {
|
||||||
|
return 0 as *mut mailmime_content;
|
||||||
|
}
|
||||||
|
(*content).ct_type = ct_type;
|
||||||
|
(*content).ct_subtype = ct_subtype;
|
||||||
|
(*content).ct_parameters = ct_parameters;
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_content_free(mut content: *mut mailmime_content) {
|
||||||
|
mailmime_type_free((*content).ct_type);
|
||||||
|
mailmime_subtype_free((*content).ct_subtype);
|
||||||
|
if !(*content).ct_parameters.is_null() {
|
||||||
|
clist_foreach(
|
||||||
|
(*content).ct_parameters,
|
||||||
|
::std::mem::transmute::<Option<unsafe fn(_: *mut mailmime_parameter) -> ()>, clist_func>(
|
||||||
|
Some(mailmime_parameter_free),
|
||||||
|
),
|
||||||
|
0 as *mut libc::c_void,
|
||||||
|
);
|
||||||
|
clist_free((*content).ct_parameters);
|
||||||
|
}
|
||||||
|
free(content as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_parameter_free(mut parameter: *mut mailmime_parameter) {
|
||||||
|
mailmime_attribute_free((*parameter).pa_name);
|
||||||
|
mailmime_value_free((*parameter).pa_value);
|
||||||
|
free(parameter as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_value_free(mut value: *mut libc::c_char) {
|
||||||
|
free(value as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_subtype_free(mut subtype: *mut libc::c_char) {
|
||||||
|
mailmime_extension_token_free(subtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_type_free(mut type_0: *mut mailmime_type) {
|
||||||
|
match (*type_0).tp_type {
|
||||||
|
1 => {
|
||||||
|
mailmime_discrete_type_free((*type_0).tp_data.tp_discrete_type);
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
mailmime_composite_type_free((*type_0).tp_data.tp_composite_type);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
free(type_0 as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_discrete_type_free(mut discrete_type: *mut mailmime_discrete_type) {
|
||||||
|
if !(*discrete_type).dt_extension.is_null() {
|
||||||
|
mailmime_extension_token_free((*discrete_type).dt_extension);
|
||||||
|
}
|
||||||
|
free(discrete_type as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_description_free(mut description: *mut libc::c_char) {
|
||||||
|
free(description as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_location_free(mut location: *mut libc::c_char) {
|
||||||
|
free(location as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_discrete_type_new(
|
||||||
|
mut dt_type: libc::c_int,
|
||||||
|
mut dt_extension: *mut libc::c_char,
|
||||||
|
) -> *mut mailmime_discrete_type {
|
||||||
|
let mut discrete_type: *mut mailmime_discrete_type = 0 as *mut mailmime_discrete_type;
|
||||||
|
discrete_type = malloc(::std::mem::size_of::<mailmime_discrete_type>() as libc::size_t)
|
||||||
|
as *mut mailmime_discrete_type;
|
||||||
|
if discrete_type.is_null() {
|
||||||
|
return 0 as *mut mailmime_discrete_type;
|
||||||
|
}
|
||||||
|
(*discrete_type).dt_type = dt_type;
|
||||||
|
(*discrete_type).dt_extension = dt_extension;
|
||||||
|
return discrete_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_encoding_free(mut encoding: *mut mailmime_mechanism) {
|
||||||
|
mailmime_mechanism_free(encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_mechanism_free(mut mechanism: *mut mailmime_mechanism) {
|
||||||
|
if !(*mechanism).enc_token.is_null() {
|
||||||
|
mailmime_token_free((*mechanism).enc_token);
|
||||||
|
}
|
||||||
|
free(mechanism as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_id_free(mut id: *mut libc::c_char) {
|
||||||
|
mailimf_msg_id_free(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_mechanism_new(
|
||||||
|
mut enc_type: libc::c_int,
|
||||||
|
mut enc_token: *mut libc::c_char,
|
||||||
|
) -> *mut mailmime_mechanism {
|
||||||
|
let mut mechanism: *mut mailmime_mechanism = 0 as *mut mailmime_mechanism;
|
||||||
|
mechanism = malloc(::std::mem::size_of::<mailmime_mechanism>() as libc::size_t)
|
||||||
|
as *mut mailmime_mechanism;
|
||||||
|
if mechanism.is_null() {
|
||||||
|
return 0 as *mut mailmime_mechanism;
|
||||||
|
}
|
||||||
|
(*mechanism).enc_type = enc_type;
|
||||||
|
(*mechanism).enc_token = enc_token;
|
||||||
|
return mechanism;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_parameter_new(
|
||||||
|
mut pa_name: *mut libc::c_char,
|
||||||
|
mut pa_value: *mut libc::c_char,
|
||||||
|
) -> *mut mailmime_parameter {
|
||||||
|
let mut parameter: *mut mailmime_parameter = 0 as *mut mailmime_parameter;
|
||||||
|
parameter = malloc(::std::mem::size_of::<mailmime_parameter>() as libc::size_t)
|
||||||
|
as *mut mailmime_parameter;
|
||||||
|
if parameter.is_null() {
|
||||||
|
return 0 as *mut mailmime_parameter;
|
||||||
|
}
|
||||||
|
(*parameter).pa_name = pa_name;
|
||||||
|
(*parameter).pa_value = pa_value;
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_type_new(
|
||||||
|
mut tp_type: libc::c_int,
|
||||||
|
mut tp_discrete_type: *mut mailmime_discrete_type,
|
||||||
|
mut tp_composite_type: *mut mailmime_composite_type,
|
||||||
|
) -> *mut mailmime_type {
|
||||||
|
let mut mime_type: *mut mailmime_type = 0 as *mut mailmime_type;
|
||||||
|
mime_type =
|
||||||
|
malloc(::std::mem::size_of::<mailmime_type>() as libc::size_t) as *mut mailmime_type;
|
||||||
|
if mime_type.is_null() {
|
||||||
|
return 0 as *mut mailmime_type;
|
||||||
|
}
|
||||||
|
(*mime_type).tp_type = tp_type;
|
||||||
|
match tp_type {
|
||||||
|
1 => (*mime_type).tp_data.tp_discrete_type = tp_discrete_type,
|
||||||
|
2 => (*mime_type).tp_data.tp_composite_type = tp_composite_type,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
return mime_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_language_new(mut lg_list: *mut clist) -> *mut mailmime_language {
|
||||||
|
let mut lang: *mut mailmime_language = 0 as *mut mailmime_language;
|
||||||
|
lang = malloc(::std::mem::size_of::<mailmime_language>() as libc::size_t)
|
||||||
|
as *mut mailmime_language;
|
||||||
|
if lang.is_null() {
|
||||||
|
return 0 as *mut mailmime_language;
|
||||||
|
}
|
||||||
|
(*lang).lg_list = lg_list;
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_language_free(mut lang: *mut mailmime_language) {
|
||||||
|
clist_foreach(
|
||||||
|
(*lang).lg_list,
|
||||||
|
::std::mem::transmute::<Option<unsafe fn(_: *mut libc::c_char) -> ()>, clist_func>(Some(
|
||||||
|
mailimf_atom_free,
|
||||||
|
)),
|
||||||
|
0 as *mut libc::c_void,
|
||||||
|
);
|
||||||
|
clist_free((*lang).lg_list);
|
||||||
|
free(lang as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
void mailmime_x_token_free(gchar * x_token);
|
||||||
|
*/
|
||||||
|
pub unsafe fn mailmime_field_new(
|
||||||
|
mut fld_type: libc::c_int,
|
||||||
|
mut fld_content: *mut mailmime_content,
|
||||||
|
mut fld_encoding: *mut mailmime_mechanism,
|
||||||
|
mut fld_id: *mut libc::c_char,
|
||||||
|
mut fld_description: *mut libc::c_char,
|
||||||
|
mut fld_version: uint32_t,
|
||||||
|
mut fld_disposition: *mut mailmime_disposition,
|
||||||
|
mut fld_language: *mut mailmime_language,
|
||||||
|
mut fld_location: *mut libc::c_char,
|
||||||
|
) -> *mut mailmime_field {
|
||||||
|
let mut field: *mut mailmime_field = 0 as *mut mailmime_field;
|
||||||
|
field = malloc(::std::mem::size_of::<mailmime_field>() as libc::size_t) as *mut mailmime_field;
|
||||||
|
if field.is_null() {
|
||||||
|
return 0 as *mut mailmime_field;
|
||||||
|
}
|
||||||
|
(*field).fld_type = fld_type;
|
||||||
|
match fld_type {
|
||||||
|
1 => (*field).fld_data.fld_content = fld_content,
|
||||||
|
2 => (*field).fld_data.fld_encoding = fld_encoding,
|
||||||
|
3 => (*field).fld_data.fld_id = fld_id,
|
||||||
|
4 => (*field).fld_data.fld_description = fld_description,
|
||||||
|
5 => (*field).fld_data.fld_version = fld_version,
|
||||||
|
6 => (*field).fld_data.fld_disposition = fld_disposition,
|
||||||
|
7 => (*field).fld_data.fld_language = fld_language,
|
||||||
|
8 => (*field).fld_data.fld_location = fld_location,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_field_free(mut field: *mut mailmime_field) {
|
||||||
|
match (*field).fld_type {
|
||||||
|
1 => {
|
||||||
|
if !(*field).fld_data.fld_content.is_null() {
|
||||||
|
mailmime_content_free((*field).fld_data.fld_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
if !(*field).fld_data.fld_encoding.is_null() {
|
||||||
|
mailmime_encoding_free((*field).fld_data.fld_encoding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
if !(*field).fld_data.fld_id.is_null() {
|
||||||
|
mailmime_id_free((*field).fld_data.fld_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
if !(*field).fld_data.fld_description.is_null() {
|
||||||
|
mailmime_description_free((*field).fld_data.fld_description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
if !(*field).fld_data.fld_disposition.is_null() {
|
||||||
|
mailmime_disposition_free((*field).fld_data.fld_disposition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
7 => {
|
||||||
|
if !(*field).fld_data.fld_language.is_null() {
|
||||||
|
mailmime_language_free((*field).fld_data.fld_language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
8 => {
|
||||||
|
if !(*field).fld_data.fld_location.is_null() {
|
||||||
|
mailmime_location_free((*field).fld_data.fld_location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
free(field as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_disposition_free(mut dsp: *mut mailmime_disposition) {
|
||||||
|
mailmime_disposition_type_free((*dsp).dsp_type);
|
||||||
|
clist_foreach(
|
||||||
|
(*dsp).dsp_parms,
|
||||||
|
::std::mem::transmute::<
|
||||||
|
Option<unsafe fn(_: *mut mailmime_disposition_parm) -> ()>,
|
||||||
|
clist_func,
|
||||||
|
>(Some(mailmime_disposition_parm_free)),
|
||||||
|
0 as *mut libc::c_void,
|
||||||
|
);
|
||||||
|
clist_free((*dsp).dsp_parms);
|
||||||
|
free(dsp as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_disposition_parm_free(mut dsp_parm: *mut mailmime_disposition_parm) {
|
||||||
|
match (*dsp_parm).pa_type {
|
||||||
|
0 => {
|
||||||
|
mailmime_filename_parm_free((*dsp_parm).pa_data.pa_filename);
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
mailmime_creation_date_parm_free((*dsp_parm).pa_data.pa_creation_date);
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
mailmime_modification_date_parm_free((*dsp_parm).pa_data.pa_modification_date);
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
mailmime_read_date_parm_free((*dsp_parm).pa_data.pa_read_date);
|
||||||
|
}
|
||||||
|
5 => {
|
||||||
|
mailmime_parameter_free((*dsp_parm).pa_data.pa_parameter);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
free(dsp_parm as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_read_date_parm_free(mut date: *mut libc::c_char) {
|
||||||
|
mailmime_quoted_date_time_free(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_quoted_date_time_free(mut date: *mut libc::c_char) {
|
||||||
|
mailimf_quoted_string_free(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_modification_date_parm_free(mut date: *mut libc::c_char) {
|
||||||
|
mailmime_quoted_date_time_free(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_creation_date_parm_free(mut date: *mut libc::c_char) {
|
||||||
|
mailmime_quoted_date_time_free(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_filename_parm_free(mut filename: *mut libc::c_char) {
|
||||||
|
mailmime_value_free(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_disposition_type_free(mut dsp_type: *mut mailmime_disposition_type) {
|
||||||
|
if !(*dsp_type).dsp_extension.is_null() {
|
||||||
|
free((*dsp_type).dsp_extension as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
free(dsp_type as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_fields_new(mut fld_list: *mut clist) -> *mut mailmime_fields {
|
||||||
|
let mut fields: *mut mailmime_fields = 0 as *mut mailmime_fields;
|
||||||
|
fields =
|
||||||
|
malloc(::std::mem::size_of::<mailmime_fields>() as libc::size_t) as *mut mailmime_fields;
|
||||||
|
if fields.is_null() {
|
||||||
|
return 0 as *mut mailmime_fields;
|
||||||
|
}
|
||||||
|
(*fields).fld_list = fld_list;
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_fields_free(mut fields: *mut mailmime_fields) {
|
||||||
|
clist_foreach(
|
||||||
|
(*fields).fld_list,
|
||||||
|
::std::mem::transmute::<Option<unsafe fn(_: *mut mailmime_field) -> ()>, clist_func>(Some(
|
||||||
|
mailmime_field_free,
|
||||||
|
)),
|
||||||
|
0 as *mut libc::c_void,
|
||||||
|
);
|
||||||
|
clist_free((*fields).fld_list);
|
||||||
|
free(fields as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_multipart_body_new(mut bd_list: *mut clist) -> *mut mailmime_multipart_body {
|
||||||
|
let mut mp_body: *mut mailmime_multipart_body = 0 as *mut mailmime_multipart_body;
|
||||||
|
mp_body = malloc(::std::mem::size_of::<mailmime_multipart_body>() as libc::size_t)
|
||||||
|
as *mut mailmime_multipart_body;
|
||||||
|
if mp_body.is_null() {
|
||||||
|
return 0 as *mut mailmime_multipart_body;
|
||||||
|
}
|
||||||
|
(*mp_body).bd_list = bd_list;
|
||||||
|
return mp_body;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_multipart_body_free(mut mp_body: *mut mailmime_multipart_body) {
|
||||||
|
clist_foreach(
|
||||||
|
(*mp_body).bd_list,
|
||||||
|
::std::mem::transmute::<Option<unsafe fn(_: *mut mailimf_body) -> ()>, clist_func>(Some(
|
||||||
|
mailimf_body_free,
|
||||||
|
)),
|
||||||
|
0 as *mut libc::c_void,
|
||||||
|
);
|
||||||
|
clist_free((*mp_body).bd_list);
|
||||||
|
free(mp_body as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_data_new(
|
||||||
|
mut dt_type: libc::c_int,
|
||||||
|
mut dt_encoding: libc::c_int,
|
||||||
|
mut dt_encoded: libc::c_int,
|
||||||
|
mut dt_data: *const libc::c_char,
|
||||||
|
mut dt_length: size_t,
|
||||||
|
mut dt_filename: *mut libc::c_char,
|
||||||
|
) -> *mut mailmime_data {
|
||||||
|
let mut mime_data: *mut mailmime_data = 0 as *mut mailmime_data;
|
||||||
|
mime_data =
|
||||||
|
malloc(::std::mem::size_of::<mailmime_data>() as libc::size_t) as *mut mailmime_data;
|
||||||
|
if mime_data.is_null() {
|
||||||
|
return 0 as *mut mailmime_data;
|
||||||
|
}
|
||||||
|
(*mime_data).dt_type = dt_type;
|
||||||
|
(*mime_data).dt_encoding = dt_encoding;
|
||||||
|
(*mime_data).dt_encoded = dt_encoded;
|
||||||
|
match dt_type {
|
||||||
|
0 => {
|
||||||
|
(*mime_data).dt_data.dt_text.dt_data = dt_data;
|
||||||
|
(*mime_data).dt_data.dt_text.dt_length = dt_length
|
||||||
|
}
|
||||||
|
1 => (*mime_data).dt_data.dt_filename = dt_filename,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
return mime_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_data_free(mut mime_data: *mut mailmime_data) {
|
||||||
|
match (*mime_data).dt_type {
|
||||||
|
1 => {
|
||||||
|
free((*mime_data).dt_data.dt_filename as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
free(mime_data as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_new(
|
||||||
|
mut mm_type: libc::c_int,
|
||||||
|
mut mm_mime_start: *const libc::c_char,
|
||||||
|
mut mm_length: size_t,
|
||||||
|
mut mm_mime_fields: *mut mailmime_fields,
|
||||||
|
mut mm_content_type: *mut mailmime_content,
|
||||||
|
mut mm_body: *mut mailmime_data,
|
||||||
|
mut mm_preamble: *mut mailmime_data,
|
||||||
|
mut mm_epilogue: *mut mailmime_data,
|
||||||
|
mut mm_mp_list: *mut clist,
|
||||||
|
mut mm_fields: *mut mailimf_fields,
|
||||||
|
mut mm_msg_mime: *mut Mailmime,
|
||||||
|
) -> *mut Mailmime {
|
||||||
|
let mut mime: *mut Mailmime = 0 as *mut Mailmime;
|
||||||
|
let mut cur: *mut clistiter = 0 as *mut clistiter;
|
||||||
|
mime = malloc(::std::mem::size_of::<Mailmime>() as libc::size_t) as *mut Mailmime;
|
||||||
|
if mime.is_null() {
|
||||||
|
return 0 as *mut Mailmime;
|
||||||
|
}
|
||||||
|
(*mime).mm_parent = 0 as *mut Mailmime;
|
||||||
|
(*mime).mm_parent_type = MAILMIME_NONE as libc::c_int;
|
||||||
|
(*mime).mm_multipart_pos = 0 as *mut clistiter;
|
||||||
|
(*mime).mm_type = mm_type;
|
||||||
|
(*mime).mm_mime_start = mm_mime_start;
|
||||||
|
(*mime).mm_length = mm_length;
|
||||||
|
(*mime).mm_mime_fields = mm_mime_fields;
|
||||||
|
(*mime).mm_content_type = mm_content_type;
|
||||||
|
(*mime).mm_body = mm_body;
|
||||||
|
match mm_type {
|
||||||
|
1 => (*mime).mm_data.mm_single = mm_body,
|
||||||
|
2 => {
|
||||||
|
(*mime).mm_data.mm_multipart.mm_preamble = mm_preamble;
|
||||||
|
(*mime).mm_data.mm_multipart.mm_epilogue = mm_epilogue;
|
||||||
|
(*mime).mm_data.mm_multipart.mm_mp_list = mm_mp_list;
|
||||||
|
cur = (*mm_mp_list).first;
|
||||||
|
while !cur.is_null() {
|
||||||
|
let mut submime: *mut Mailmime = 0 as *mut Mailmime;
|
||||||
|
submime = (if !cur.is_null() {
|
||||||
|
(*cur).data
|
||||||
|
} else {
|
||||||
|
0 as *mut libc::c_void
|
||||||
|
}) as *mut Mailmime;
|
||||||
|
(*submime).mm_parent = mime;
|
||||||
|
(*submime).mm_parent_type = MAILMIME_MULTIPLE as libc::c_int;
|
||||||
|
(*submime).mm_multipart_pos = cur;
|
||||||
|
cur = if !cur.is_null() {
|
||||||
|
(*cur).next
|
||||||
|
} else {
|
||||||
|
0 as *mut clistcell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
(*mime).mm_data.mm_message.mm_fields = mm_fields;
|
||||||
|
(*mime).mm_data.mm_message.mm_msg_mime = mm_msg_mime;
|
||||||
|
if !mm_msg_mime.is_null() {
|
||||||
|
(*mm_msg_mime).mm_parent = mime;
|
||||||
|
(*mm_msg_mime).mm_parent_type = MAILMIME_MESSAGE as libc::c_int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
return mime;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_new_simple(
|
||||||
|
mut mm_type: libc::c_int,
|
||||||
|
mut mm_mime_fields: *mut mailmime_fields,
|
||||||
|
mut mm_content_type: *mut mailmime_content,
|
||||||
|
mut mm_fields: *mut mailimf_fields,
|
||||||
|
mut mm_msg_mime: *mut Mailmime,
|
||||||
|
) -> *mut Mailmime {
|
||||||
|
mailmime_new(
|
||||||
|
mm_type,
|
||||||
|
std::ptr::null(),
|
||||||
|
0,
|
||||||
|
mm_mime_fields,
|
||||||
|
mm_content_type,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
mm_fields,
|
||||||
|
mm_msg_mime,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_free(mut mime: *mut Mailmime) {
|
||||||
|
match (*mime).mm_type {
|
||||||
|
1 => {
|
||||||
|
if (*mime).mm_body.is_null() && !(*mime).mm_data.mm_single.is_null() {
|
||||||
|
mailmime_data_free((*mime).mm_data.mm_single);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
/* do nothing */
|
||||||
|
if !(*mime).mm_data.mm_multipart.mm_preamble.is_null() {
|
||||||
|
mailmime_data_free((*mime).mm_data.mm_multipart.mm_preamble);
|
||||||
|
}
|
||||||
|
if !(*mime).mm_data.mm_multipart.mm_epilogue.is_null() {
|
||||||
|
mailmime_data_free((*mime).mm_data.mm_multipart.mm_epilogue);
|
||||||
|
}
|
||||||
|
clist_foreach(
|
||||||
|
(*mime).mm_data.mm_multipart.mm_mp_list,
|
||||||
|
::std::mem::transmute::<Option<unsafe fn(_: *mut Mailmime) -> ()>, clist_func>(
|
||||||
|
Some(mailmime_free),
|
||||||
|
),
|
||||||
|
0 as *mut libc::c_void,
|
||||||
|
);
|
||||||
|
clist_free((*mime).mm_data.mm_multipart.mm_mp_list);
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
if !(*mime).mm_data.mm_message.mm_fields.is_null() {
|
||||||
|
mailimf_fields_free((*mime).mm_data.mm_message.mm_fields);
|
||||||
|
}
|
||||||
|
if !(*mime).mm_data.mm_message.mm_msg_mime.is_null() {
|
||||||
|
mailmime_free((*mime).mm_data.mm_message.mm_msg_mime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if !(*mime).mm_body.is_null() {
|
||||||
|
mailmime_data_free((*mime).mm_body);
|
||||||
|
}
|
||||||
|
if !(*mime).mm_mime_fields.is_null() {
|
||||||
|
mailmime_fields_free((*mime).mm_mime_fields);
|
||||||
|
}
|
||||||
|
if !(*mime).mm_content_type.is_null() {
|
||||||
|
mailmime_content_free((*mime).mm_content_type);
|
||||||
|
}
|
||||||
|
free(mime as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_encoded_word_new(
|
||||||
|
mut wd_charset: *mut libc::c_char,
|
||||||
|
mut wd_text: *mut libc::c_char,
|
||||||
|
) -> *mut mailmime_encoded_word {
|
||||||
|
let mut ew: *mut mailmime_encoded_word = 0 as *mut mailmime_encoded_word;
|
||||||
|
ew = malloc(::std::mem::size_of::<mailmime_encoded_word>() as libc::size_t)
|
||||||
|
as *mut mailmime_encoded_word;
|
||||||
|
if ew.is_null() {
|
||||||
|
return 0 as *mut mailmime_encoded_word;
|
||||||
|
}
|
||||||
|
(*ew).wd_charset = wd_charset;
|
||||||
|
(*ew).wd_text = wd_text;
|
||||||
|
return ew;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_encoded_word_free(mut ew: *mut mailmime_encoded_word) {
|
||||||
|
mailmime_charset_free((*ew).wd_charset);
|
||||||
|
mailmime_encoded_text_free((*ew).wd_text);
|
||||||
|
free(ew as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_encoded_text_free(mut text: *mut libc::c_char) {
|
||||||
|
free(text as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_charset_free(mut charset: *mut libc::c_char) {
|
||||||
|
free(charset as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_disposition_new(
|
||||||
|
mut dsp_type: *mut mailmime_disposition_type,
|
||||||
|
mut dsp_parms: *mut clist,
|
||||||
|
) -> *mut mailmime_disposition {
|
||||||
|
let mut dsp: *mut mailmime_disposition = 0 as *mut mailmime_disposition;
|
||||||
|
dsp = malloc(::std::mem::size_of::<mailmime_disposition>() as libc::size_t)
|
||||||
|
as *mut mailmime_disposition;
|
||||||
|
if dsp.is_null() {
|
||||||
|
return 0 as *mut mailmime_disposition;
|
||||||
|
}
|
||||||
|
(*dsp).dsp_type = dsp_type;
|
||||||
|
(*dsp).dsp_parms = dsp_parms;
|
||||||
|
return dsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_disposition_type_new(
|
||||||
|
mut dsp_type: libc::c_int,
|
||||||
|
mut dsp_extension: *mut libc::c_char,
|
||||||
|
) -> *mut mailmime_disposition_type {
|
||||||
|
let mut m_dsp_type: *mut mailmime_disposition_type = 0 as *mut mailmime_disposition_type;
|
||||||
|
m_dsp_type = malloc(::std::mem::size_of::<mailmime_disposition_type>() as libc::size_t)
|
||||||
|
as *mut mailmime_disposition_type;
|
||||||
|
if m_dsp_type.is_null() {
|
||||||
|
return 0 as *mut mailmime_disposition_type;
|
||||||
|
}
|
||||||
|
(*m_dsp_type).dsp_type = dsp_type;
|
||||||
|
(*m_dsp_type).dsp_extension = dsp_extension;
|
||||||
|
return m_dsp_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_disposition_parm_new(
|
||||||
|
mut pa_type: libc::c_int,
|
||||||
|
mut pa_filename: *mut libc::c_char,
|
||||||
|
mut pa_creation_date: *mut libc::c_char,
|
||||||
|
mut pa_modification_date: *mut libc::c_char,
|
||||||
|
mut pa_read_date: *mut libc::c_char,
|
||||||
|
mut pa_size: size_t,
|
||||||
|
mut pa_parameter: *mut mailmime_parameter,
|
||||||
|
) -> *mut mailmime_disposition_parm {
|
||||||
|
let mut dsp_parm: *mut mailmime_disposition_parm = 0 as *mut mailmime_disposition_parm;
|
||||||
|
dsp_parm = malloc(::std::mem::size_of::<mailmime_disposition_parm>() as libc::size_t)
|
||||||
|
as *mut mailmime_disposition_parm;
|
||||||
|
if dsp_parm.is_null() {
|
||||||
|
return 0 as *mut mailmime_disposition_parm;
|
||||||
|
}
|
||||||
|
(*dsp_parm).pa_type = pa_type;
|
||||||
|
match pa_type {
|
||||||
|
0 => (*dsp_parm).pa_data.pa_filename = pa_filename,
|
||||||
|
1 => (*dsp_parm).pa_data.pa_creation_date = pa_creation_date,
|
||||||
|
2 => (*dsp_parm).pa_data.pa_modification_date = pa_modification_date,
|
||||||
|
3 => (*dsp_parm).pa_data.pa_read_date = pa_read_date,
|
||||||
|
4 => (*dsp_parm).pa_data.pa_size = pa_size,
|
||||||
|
5 => (*dsp_parm).pa_data.pa_parameter = pa_parameter,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
return dsp_parm;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_section_new(mut sec_list: *mut clist) -> *mut mailmime_section {
|
||||||
|
let mut section: *mut mailmime_section = 0 as *mut mailmime_section;
|
||||||
|
section =
|
||||||
|
malloc(::std::mem::size_of::<mailmime_section>() as libc::size_t) as *mut mailmime_section;
|
||||||
|
if section.is_null() {
|
||||||
|
return 0 as *mut mailmime_section;
|
||||||
|
}
|
||||||
|
(*section).sec_list = sec_list;
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_section_free(mut section: *mut mailmime_section) {
|
||||||
|
clist_foreach(
|
||||||
|
(*section).sec_list,
|
||||||
|
::std::mem::transmute::<Option<unsafe extern "C" fn(_: *mut libc::c_void) -> ()>, clist_func>(
|
||||||
|
Some(free),
|
||||||
|
),
|
||||||
|
0 as *mut libc::c_void,
|
||||||
|
);
|
||||||
|
clist_free((*section).sec_list);
|
||||||
|
free(section as *mut libc::c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn mailmime_decoded_part_free(mut part: *mut libc::c_char) {
|
||||||
|
mmap_string_unref(part);
|
||||||
|
}
|
||||||
1445
mmime/src/mailmime/types_helper.rs
Normal file
1979
mmime/src/mailmime/write_generic.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
@@ -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() {}
|
||||||
1728
mmime/src/other.rs
Normal file
@@ -1,29 +1,6 @@
|
|||||||
0.900.0 (DRAFT)
|
0.600.1
|
||||||
---------------
|
|
||||||
|
|
||||||
- refactored internals to use plugin-approach
|
|
||||||
|
|
||||||
- introduced PerAccount and Global hooks that plugins can implement
|
|
||||||
|
|
||||||
- introduced `ac_member_added()` and `ac_member_removed()` plugin events.
|
|
||||||
|
|
||||||
- introduced two documented examples for an echo and a group-membership
|
|
||||||
tracking plugin.
|
|
||||||
|
|
||||||
0.800.0
|
|
||||||
-------
|
|
||||||
|
|
||||||
- use latest core 1.25.0
|
|
||||||
|
|
||||||
- refine tests and some internal changes to core bindings
|
|
||||||
|
|
||||||
0.700.0
|
|
||||||
---------
|
---------
|
||||||
|
|
||||||
- lots of new Python APIs
|
|
||||||
|
|
||||||
- use rust core-beta23
|
|
||||||
|
|
||||||
- introduce automatic versioning via setuptools_scm,
|
- introduce automatic versioning via setuptools_scm,
|
||||||
based on py-X.Y.Z tags
|
based on py-X.Y.Z tags
|
||||||
|
|
||||||
|
|||||||
@@ -3,98 +3,41 @@ deltachat python bindings
|
|||||||
=========================
|
=========================
|
||||||
|
|
||||||
This package provides bindings to the deltachat-core_ Rust -library
|
This package provides bindings to the deltachat-core_ Rust -library
|
||||||
which implements IMAP/SMTP/MIME/PGP e-mail standards and offers
|
which provides imap/smtp/crypto handling as well as chat/group/messages
|
||||||
a low-level Chat/Contact/Message API to user interfaces and bots.
|
handling to Android, Desktop and IO user interfaces.
|
||||||
|
|
||||||
|
Installing pre-built packages (linux-only)
|
||||||
|
==========================================
|
||||||
|
|
||||||
Installing bindings from source (Updated: 20-Jan-2020)
|
If you have a linux system you may install the ``deltachat`` binary "wheel" package
|
||||||
=========================================================
|
|
||||||
|
|
||||||
Install Rust and Cargo first. Deltachat needs a specific nightly
|
|
||||||
version, the easiest is probably to first install Rust stable from
|
|
||||||
rustup and then use this to install the correct nightly version.
|
|
||||||
|
|
||||||
Bootstrap Rust and Cargo by using rustup::
|
|
||||||
|
|
||||||
curl https://sh.rustup.rs -sSf | sh
|
|
||||||
|
|
||||||
Then GIT clone the deltachat-core-rust repo and get the actual
|
|
||||||
rust- and cargo-toolchain needed by deltachat::
|
|
||||||
|
|
||||||
git clone https://github.com/deltachat/deltachat-core-rust
|
|
||||||
cd deltachat-core-rust
|
|
||||||
rustup show
|
|
||||||
|
|
||||||
To install the Delta Chat Python bindings make sure you have Python3 installed.
|
|
||||||
E.g. on Debian-based systems `apt install python3 python3-pip
|
|
||||||
python3-venv` should give you a usable python installation.
|
|
||||||
|
|
||||||
Ensure you are in the deltachat-core-rust/python directory, create the
|
|
||||||
virtual environment and activate it in your shell::
|
|
||||||
|
|
||||||
cd python
|
|
||||||
python3 -m venv venv # or: virtualenv venv
|
|
||||||
source venv/bin/activate
|
|
||||||
|
|
||||||
You should now be able to build the python bindings using the supplied script::
|
|
||||||
|
|
||||||
./install_python_bindings.py
|
|
||||||
|
|
||||||
The installation might take a while, depending on your machine.
|
|
||||||
The bindings will be installed in release mode but with debug symbols.
|
|
||||||
The release mode is currently necessary because some tests generate RSA keys
|
|
||||||
which is prohibitively slow in non-release mode.
|
|
||||||
|
|
||||||
After successful binding installation you can install a few more
|
|
||||||
Python packages before running the tests::
|
|
||||||
|
|
||||||
python -m pip install pytest pytest-timeout pytest-rerunfailures requests
|
|
||||||
pytest -v tests
|
|
||||||
|
|
||||||
|
|
||||||
running "live" tests with temporary accounts
|
|
||||||
---------------------------------------------
|
|
||||||
|
|
||||||
If you want to run "liveconfig" functional tests you can set
|
|
||||||
``DCC_NEW_TMP_EMAIL`` to:
|
|
||||||
|
|
||||||
- a particular https-url that you can ask for from the delta
|
|
||||||
chat devs. This is implemented on the server side via
|
|
||||||
the [mailadm](https://github.com/deltachat/mailadm) command line tool.
|
|
||||||
|
|
||||||
- or the path of a file that contains two lines, each describing
|
|
||||||
via "addr=... mail_pw=..." a test account login that will
|
|
||||||
be used for the live tests.
|
|
||||||
|
|
||||||
With ``DCC_NEW_TMP_EMAIL`` set pytest invocations will use real
|
|
||||||
e-mail accounts and run through all functional "liveconfig" tests.
|
|
||||||
|
|
||||||
|
|
||||||
Installing pre-built packages (Linux-only)
|
|
||||||
========================================================
|
|
||||||
|
|
||||||
If you have a Linux system you may try to install the ``deltachat`` binary "wheel" packages
|
|
||||||
without any "build-from-source" steps.
|
without any "build-from-source" steps.
|
||||||
|
|
||||||
We suggest to `Install virtualenv <https://virtualenv.pypa.io/en/stable/installation/>`_,
|
1. `Install virtualenv <https://virtualenv.pypa.io/en/stable/installation/>`_,
|
||||||
then create a fresh Python virtual environment and activate it in your shell::
|
then create a fresh python environment and activate it in your shell::
|
||||||
|
|
||||||
virtualenv venv # or: python -m venv
|
virtualenv venv # or: python -m venv
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
|
|
||||||
Afterwards, invoking ``python`` or ``pip install`` only
|
Afterwards, invoking ``python`` or ``pip install`` will only
|
||||||
modifies files in your ``venv`` directory and leaves
|
modify files in your ``venv`` directory and leave your system installation
|
||||||
your system installation alone.
|
alone.
|
||||||
|
|
||||||
|
2. Install the wheel for linux::
|
||||||
|
|
||||||
|
pip install deltachat
|
||||||
|
|
||||||
|
Verify it worked by typing::
|
||||||
|
|
||||||
|
python -c "import deltachat"
|
||||||
|
|
||||||
|
|
||||||
|
Installing a wheel from a PR/branch
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
For Linux, we automatically build wheels for all github PR branches
|
For Linux, we automatically build wheels for all github PR branches
|
||||||
and push them to a python package index. To install the latest
|
and push them to a python package index. To install the latest github ``master`` branch::
|
||||||
github ``master`` branch::
|
|
||||||
|
|
||||||
pip install --pre -i https://m.devpi.net/dc/master deltachat
|
pip install -i https://m.devpi.net/dc/master deltachat
|
||||||
|
|
||||||
To verify it worked::
|
|
||||||
|
|
||||||
python -c "import deltachat"
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@@ -103,6 +46,65 @@ To verify it worked::
|
|||||||
`in contact with us <https://delta.chat/en/contribute>`_.
|
`in contact with us <https://delta.chat/en/contribute>`_.
|
||||||
|
|
||||||
|
|
||||||
|
Installing bindings from source
|
||||||
|
===============================
|
||||||
|
|
||||||
|
If you can't use "binary" method above then you need to compile
|
||||||
|
to core deltachat library::
|
||||||
|
|
||||||
|
git clone https://github.com/deltachat/deltachat-core-rust
|
||||||
|
cd deltachat-core-rust
|
||||||
|
cd python
|
||||||
|
|
||||||
|
If you don't have one active, create and activate a python "virtualenv":
|
||||||
|
|
||||||
|
python virtualenv venv # or python -m venv
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
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 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 necessary because some tests generate RSA keys
|
||||||
|
which is prohibitively slow in debug mode.
|
||||||
|
|
||||||
|
After successful binding installation you can finally run the tests::
|
||||||
|
|
||||||
|
pytest -v tests
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Some tests are sometimes failing/hanging because of
|
||||||
|
https://github.com/deltachat/deltachat-core-rust/issues/331
|
||||||
|
and
|
||||||
|
https://github.com/deltachat/deltachat-core-rust/issues/326
|
||||||
|
|
||||||
|
|
||||||
|
running "live" tests (experimental)
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
If you want to run "liveconfig" functional tests you can set
|
||||||
|
``DCC_PY_LIVECONFIG`` to:
|
||||||
|
|
||||||
|
- a particular https-url that you can ask for from the delta
|
||||||
|
chat devs.
|
||||||
|
|
||||||
|
- or the path of a file that contains two lines, each describing
|
||||||
|
via "addr=... mail_pw=..." a test account login that will
|
||||||
|
be used for the live tests.
|
||||||
|
|
||||||
|
With ``DCC_PY_LIVECONFIG`` set pytest invocations will use real
|
||||||
|
e-mail accounts and run through all functional "liveconfig" tests.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Code examples
|
Code examples
|
||||||
=============
|
=============
|
||||||
|
|
||||||
@@ -113,11 +115,15 @@ You may look at `examples <https://py.delta.chat/examples.html>`_.
|
|||||||
.. _`deltachat-core`: https://github.com/deltachat/deltachat-core-rust
|
.. _`deltachat-core`: https://github.com/deltachat/deltachat-core-rust
|
||||||
|
|
||||||
|
|
||||||
Building manylinux1 based wheels
|
Building manylinux1 wheels
|
||||||
================================
|
==========================
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This section may not fully work.
|
||||||
|
|
||||||
Building portable manylinux1 wheels which come with libdeltachat.so
|
Building portable manylinux1 wheels which come with libdeltachat.so
|
||||||
can be done with docker-tooling.
|
and all it's dependencies is easy using the provided docker tooling.
|
||||||
|
|
||||||
using docker pull / premade images
|
using docker pull / premade images
|
||||||
------------------------------------
|
------------------------------------
|
||||||
@@ -130,9 +136,9 @@ organization::
|
|||||||
|
|
||||||
This docker image can be used to run tests and build Python wheels for all interpreters::
|
This docker image can be used to run tests and build Python wheels for all interpreters::
|
||||||
|
|
||||||
$ docker run -e DCC_NEW_TMP_EMAIL \
|
$ bash ci_scripts/ci_run.sh
|
||||||
--rm -it -v \$(pwd):/mnt -w /mnt \
|
|
||||||
deltachat/coredeps ci_scripts/run_all.sh
|
This command runs tests and build-wheel scripts in a docker container.
|
||||||
|
|
||||||
|
|
||||||
Optionally build your own docker image
|
Optionally build your own docker image
|
||||||
|
|||||||
4
python/doc/_static/custom.css
vendored
@@ -15,7 +15,3 @@ div.globaltoc {
|
|||||||
img.logo {
|
img.logo {
|
||||||
height: 120px;
|
height: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.footer {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
high level API reference
|
high level API reference
|
||||||
========================
|
========================
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This API is work in progress and may change in versions prior to 1.0.
|
||||||
|
|
||||||
- :class:`deltachat.account.Account` (your main entry point, creates the
|
- :class:`deltachat.account.Account` (your main entry point, creates the
|
||||||
other classes)
|
other classes)
|
||||||
- :class:`deltachat.contact.Contact`
|
- :class:`deltachat.contact.Contact`
|
||||||
|
|||||||
7
python/doc/capi.rst
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
C deltachat interface
|
||||||
|
=====================
|
||||||
|
|
||||||
|
See :doc:`lapi` for accessing many of the below functions
|
||||||
|
through the ``deltachat.capi.lib`` namespace.
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ master_doc = 'index'
|
|||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'deltachat'
|
project = u'deltachat'
|
||||||
copyright = u'2020, holger krekel and contributors'
|
copyright = u'2018, holger krekel and contributors'
|
||||||
|
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
|||||||
@@ -1,60 +1,37 @@
|
|||||||
|
|
||||||
|
|
||||||
examples
|
examples
|
||||||
========
|
========
|
||||||
|
|
||||||
|
|
||||||
|
Playing around on the commandline
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
Once you have :doc:`installed deltachat bindings <install>`
|
Once you have :doc:`installed deltachat bindings <install>`
|
||||||
you need email/password credentials for an IMAP/SMTP account.
|
you can start playing from the python interpreter commandline.
|
||||||
Delta Chat developers and the CI system use a special URL to create
|
For example you can type ``python`` and then::
|
||||||
temporary e-mail accounts on [testrun.org](https://testrun.org) for testing.
|
|
||||||
|
|
||||||
Receiving a Chat message from the command line
|
# instantiate and configure deltachat account
|
||||||
----------------------------------------------
|
import deltachat
|
||||||
|
ac = deltachat.Account("/tmp/db")
|
||||||
|
|
||||||
Here is a simple bot that:
|
# start configuration activity and smtp/imap threads
|
||||||
|
ac.start_threads()
|
||||||
|
ac.configure(addr="test2@hq5.merlinux.eu", mail_pw="********")
|
||||||
|
|
||||||
- receives a message and sends back ("echoes") a message
|
# create a contact and send a message
|
||||||
|
contact = ac.create_contact("someother@email.address")
|
||||||
|
chat = ac.create_chat_by_contact(contact)
|
||||||
|
chat.send_text("hi from the python interpreter command line")
|
||||||
|
|
||||||
- terminates the bot if the message `/quit` is sent
|
Checkout our :doc:`api` for the various high-level things you can do
|
||||||
|
to send/receive messages, create contacts and chats.
|
||||||
|
|
||||||
.. include:: ../examples/echo_and_quit.py
|
|
||||||
:literal:
|
|
||||||
|
|
||||||
With this file in your working directory you can run the bot
|
Looking at a real example
|
||||||
by specifying a database path, an e-mail address and password of
|
|
||||||
a SMTP-IMAP account::
|
|
||||||
|
|
||||||
$ cd examples
|
|
||||||
$ python echo_and_quit.py /tmp/db --email ADDRESS --password PASSWORD
|
|
||||||
|
|
||||||
While this process is running you can start sending chat messages
|
|
||||||
to `ADDRESS`.
|
|
||||||
|
|
||||||
Track member additions and removals in a group
|
|
||||||
----------------------------------------------
|
|
||||||
|
|
||||||
Here is a simple bot that:
|
|
||||||
|
|
||||||
- echoes messages sent to it
|
|
||||||
|
|
||||||
- tracks if configuration completed
|
|
||||||
|
|
||||||
- tracks member additions and removals for all chat groups
|
|
||||||
|
|
||||||
.. include:: ../examples/group_tracking.py
|
|
||||||
:literal:
|
|
||||||
|
|
||||||
With this file in your working directory you can run the bot
|
|
||||||
by specifying a database path, an e-mail address and password of
|
|
||||||
a SMTP-IMAP account::
|
|
||||||
|
|
||||||
python group_tracking.py --email ADDRESS --password PASSWORD /tmp/db
|
|
||||||
|
|
||||||
When this process is running you can start sending chat messages
|
|
||||||
to `ADDRESS`.
|
|
||||||
|
|
||||||
Writing bots for real
|
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
The `deltabot repository <https://github.com/deltachat/deltabot#deltachat-example-bot>`_
|
The `deltabot repository <https://github.com/deltachat/deltabot#deltachat-example-bot>`_
|
||||||
contains a little framework for writing deltachat bots in Python.
|
contains a real-life example of Python bindings usage.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
deltachat python bindings
|
deltachat python bindings
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
The ``deltachat`` Python package provides two layers of bindings for the
|
The ``deltachat`` Python package provides two bindings for the core Rust-library
|
||||||
core Rust-library of the https://delta.chat messaging ecosystem:
|
of the https://delta.chat messaging ecosystem:
|
||||||
|
|
||||||
- :doc:`api` is a high level interface to deltachat-core.
|
- :doc:`api` is a high level interface to deltachat-core which aims
|
||||||
|
to be memory safe and thoroughly tested through continous tox/pytest runs.
|
||||||
|
|
||||||
- :doc:`plugins` is a brief introduction into implementing plugin hooks.
|
- :doc:`capi` is a lowlevel CFFI-binding to the previous
|
||||||
|
`deltachat-core C-API <https://c.delta.chat>`_ (so far the Rust library
|
||||||
- :doc:`lapi` is a lowlevel CFFI-binding to the `Rust Core
|
replicates exactly the same C-level API).
|
||||||
<https://github.com/deltachat/deltachat-core-rust>`_.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -28,8 +28,8 @@ getting started
|
|||||||
links
|
links
|
||||||
changelog
|
changelog
|
||||||
api
|
api
|
||||||
|
capi
|
||||||
lapi
|
lapi
|
||||||
plugins
|
|
||||||
|
|
||||||
..
|
..
|
||||||
Indices and tables
|
Indices and tables
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
|
|
||||||
Implementing Plugin Hooks
|
|
||||||
==========================
|
|
||||||
|
|
||||||
The Delta Chat Python bindings use `pluggy <https://pluggy.readthedocs.io>`_
|
|
||||||
for managing global and per-account plugin registration, and performing
|
|
||||||
hook calls. There are two kinds of plugins:
|
|
||||||
|
|
||||||
- Global plugins that are active for all accounts; they can implement
|
|
||||||
hooks at account-creation and account-shutdown time.
|
|
||||||
|
|
||||||
- Account plugins that are only active during the lifetime of a
|
|
||||||
single Account instance.
|
|
||||||
|
|
||||||
|
|
||||||
Registering a plugin
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. autofunction:: deltachat.register_global_plugin
|
|
||||||
:noindex:
|
|
||||||
|
|
||||||
.. automethod:: deltachat.account.Account.add_account_plugin
|
|
||||||
:noindex:
|
|
||||||
|
|
||||||
|
|
||||||
Per-Account Hook specifications
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
.. autoclass:: deltachat.hookspec.PerAccount
|
|
||||||
:members:
|
|
||||||
|
|
||||||
|
|
||||||
Global Hook specifications
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
.. autoclass:: deltachat.hookspec.Global
|
|
||||||
:members:
|
|
||||||
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
|
|
||||||
# content of echo_and_quit.py
|
|
||||||
|
|
||||||
from deltachat import account_hookimpl, run_cmdline
|
|
||||||
|
|
||||||
|
|
||||||
class EchoPlugin:
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_incoming_message(self, message):
|
|
||||||
print("process_incoming message", message)
|
|
||||||
if message.text.strip() == "/quit":
|
|
||||||
message.account.shutdown()
|
|
||||||
else:
|
|
||||||
# unconditionally accept the chat
|
|
||||||
message.accept_sender_contact()
|
|
||||||
addr = message.get_sender_contact().addr
|
|
||||||
text = message.text
|
|
||||||
message.chat.send_text("echoing from {}:\n{}".format(addr, text))
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_message_delivered(self, message):
|
|
||||||
print("ac_message_delivered", message)
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
|
||||||
run_cmdline(argv=argv, account_plugins=[EchoPlugin()])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
|
|
||||||
# content of group_tracking.py
|
|
||||||
|
|
||||||
from deltachat import account_hookimpl, run_cmdline
|
|
||||||
|
|
||||||
|
|
||||||
class GroupTrackingPlugin:
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_incoming_message(self, message):
|
|
||||||
print("process_incoming message", message)
|
|
||||||
if message.text.strip() == "/quit":
|
|
||||||
message.account.shutdown()
|
|
||||||
else:
|
|
||||||
# unconditionally accept the chat
|
|
||||||
message.accept_sender_contact()
|
|
||||||
addr = message.get_sender_contact().addr
|
|
||||||
text = message.text
|
|
||||||
message.chat.send_text("echoing from {}:\n{}".format(addr, text))
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_outgoing_message(self, message):
|
|
||||||
print("ac_outgoing_message:", message)
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_configure_completed(self, success):
|
|
||||||
print("ac_configure_completed:", success)
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_chat_modified(self, chat):
|
|
||||||
print("ac_chat_modified:", chat.id, chat.get_name())
|
|
||||||
for member in chat.get_contacts():
|
|
||||||
print("chat member: {}".format(member.addr))
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_member_added(self, chat, contact, message):
|
|
||||||
print("ac_member_added {} to chat {} from {}".format(
|
|
||||||
contact.addr, chat.id, message.get_sender_contact().addr))
|
|
||||||
for member in chat.get_contacts():
|
|
||||||
print("chat member: {}".format(member.addr))
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_member_removed(self, chat, contact, message):
|
|
||||||
print("ac_member_removed {} from chat {} by {}".format(
|
|
||||||
contact.addr, chat.id, message.get_sender_contact().addr))
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
|
||||||
run_cmdline(argv=argv, account_plugins=[GroupTrackingPlugin()])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
|
|
||||||
import pytest
|
|
||||||
import py
|
|
||||||
import echo_and_quit
|
|
||||||
import group_tracking
|
|
||||||
from deltachat.eventlogger import FFIEventLogger
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
|
||||||
def datadir():
|
|
||||||
"""The py.path.local object of the test-data/ directory."""
|
|
||||||
for path in reversed(py.path.local(__file__).parts()):
|
|
||||||
datadir = path.join('test-data')
|
|
||||||
if datadir.isdir():
|
|
||||||
return datadir
|
|
||||||
else:
|
|
||||||
pytest.skip('test-data directory not found')
|
|
||||||
|
|
||||||
|
|
||||||
def test_echo_quit_plugin(acfactory):
|
|
||||||
botproc = acfactory.run_bot_process(echo_and_quit)
|
|
||||||
|
|
||||||
ac1 = acfactory.get_one_online_account()
|
|
||||||
bot_contact = ac1.create_contact(botproc.addr)
|
|
||||||
ch1 = ac1.create_chat_by_contact(bot_contact)
|
|
||||||
ch1.send_text("hello")
|
|
||||||
reply = ac1._evtracker.wait_next_incoming_message()
|
|
||||||
assert "hello" in reply.text
|
|
||||||
assert reply.chat == ch1
|
|
||||||
ch1.send_text("/quit")
|
|
||||||
botproc.wait()
|
|
||||||
|
|
||||||
|
|
||||||
def test_group_tracking_plugin(acfactory, lp):
|
|
||||||
lp.sec("creating one group-tracking bot and two temp accounts")
|
|
||||||
botproc = acfactory.run_bot_process(group_tracking, ffi=False)
|
|
||||||
|
|
||||||
ac1, ac2 = acfactory.get_two_online_accounts(quiet=True)
|
|
||||||
|
|
||||||
botproc.fnmatch_lines("""
|
|
||||||
*ac_configure_completed*
|
|
||||||
""")
|
|
||||||
ac1.add_account_plugin(FFIEventLogger(ac1, "ac1"))
|
|
||||||
ac2.add_account_plugin(FFIEventLogger(ac2, "ac2"))
|
|
||||||
|
|
||||||
lp.sec("creating bot test group with bot")
|
|
||||||
bot_contact = ac1.create_contact(botproc.addr)
|
|
||||||
ch = ac1.create_group_chat("bot test group")
|
|
||||||
ch.add_contact(bot_contact)
|
|
||||||
ch.send_text("hello")
|
|
||||||
|
|
||||||
botproc.fnmatch_lines("""
|
|
||||||
*ac_chat_modified*bot test group*
|
|
||||||
""")
|
|
||||||
|
|
||||||
lp.sec("adding third member {}".format(ac2.get_config("addr")))
|
|
||||||
contact3 = ac1.create_contact(ac2.get_config("addr"))
|
|
||||||
ch.add_contact(contact3)
|
|
||||||
|
|
||||||
reply = ac1._evtracker.wait_next_incoming_message()
|
|
||||||
assert "hello" in reply.text
|
|
||||||
|
|
||||||
lp.sec("now looking at what the bot received")
|
|
||||||
botproc.fnmatch_lines("""
|
|
||||||
*ac_member_added {}*
|
|
||||||
""".format(contact3.addr))
|
|
||||||
|
|
||||||
lp.sec("contact successfully added, now removing")
|
|
||||||
ch.remove_contact(contact3)
|
|
||||||
botproc.fnmatch_lines("""
|
|
||||||
*ac_member_removed {}*
|
|
||||||
""".format(contact3.addr))
|
|
||||||
@@ -9,20 +9,17 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
target = os.environ.get("DCC_RS_TARGET")
|
os.environ["DCC_RS_TARGET"] = target = "release"
|
||||||
if target is None:
|
|
||||||
os.environ["DCC_RS_TARGET"] = target = "debug"
|
|
||||||
if "DCC_RS_DEV" not in os.environ:
|
if "DCC_RS_DEV" not in os.environ:
|
||||||
dn = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
dn = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
os.environ["DCC_RS_DEV"] = dn
|
os.environ["DCC_RS_DEV"] = dn
|
||||||
|
|
||||||
cmd = ["cargo", "build", "-p", "deltachat_ffi"]
|
os.environ["RUSTFLAGS"] = "-g"
|
||||||
if target == 'release':
|
subprocess.check_call([
|
||||||
cmd.append("--release")
|
"cargo", "build", "-p", "deltachat_ffi", "--" + target
|
||||||
subprocess.check_call(cmd)
|
])
|
||||||
subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True)
|
subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True)
|
||||||
|
|
||||||
if len(sys.argv) <= 1 or sys.argv[1] != "onlybuild":
|
subprocess.check_call([
|
||||||
subprocess.check_call([
|
sys.executable, "-m", "pip", "install", "-e", "."
|
||||||
sys.executable, "-m", "pip", "install", "-e", "."
|
])
|
||||||
])
|
|
||||||
|
|||||||
@@ -13,20 +13,14 @@ def main():
|
|||||||
"root": "..",
|
"root": "..",
|
||||||
"relative_to": __file__,
|
"relative_to": __file__,
|
||||||
'tag_regex': r'^(?P<prefix>py-)?(?P<version>[^\+]+)(?P<suffix>.*)?$',
|
'tag_regex': r'^(?P<prefix>py-)?(?P<version>[^\+]+)(?P<suffix>.*)?$',
|
||||||
'git_describe_command': "git describe --dirty --tags --long --match py-*.*",
|
|
||||||
},
|
},
|
||||||
description='Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat',
|
description='Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat',
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
author='holger krekel, Floris Bruynooghe, Bjoern Petersen and contributors',
|
author='holger krekel, Floris Bruynooghe, Bjoern Petersen and contributors',
|
||||||
install_requires=['cffi>=1.0.0', 'pluggy'],
|
install_requires=['cffi>=1.0.0', 'six'],
|
||||||
packages=setuptools.find_packages('src'),
|
packages=setuptools.find_packages('src'),
|
||||||
package_dir={'': 'src'},
|
package_dir={'': 'src'},
|
||||||
cffi_modules=['src/deltachat/_build.py:ffibuilder'],
|
cffi_modules=['src/deltachat/_build.py:ffibuilder'],
|
||||||
entry_points = {
|
|
||||||
'pytest11': [
|
|
||||||
'deltachat.testplugin = deltachat.testplugin',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 4 - Beta',
|
'Development Status :: 4 - Beta',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
import sys
|
from deltachat import capi, const
|
||||||
|
from deltachat.capi import ffi
|
||||||
from . import capi, const, hookspec
|
from deltachat.account import Account # noqa
|
||||||
from .capi import ffi
|
|
||||||
from .account import Account # noqa
|
|
||||||
from .message import Message # noqa
|
|
||||||
from .contact import Contact # noqa
|
|
||||||
from .chat import Chat # noqa
|
|
||||||
from .hookspec import account_hookimpl, global_hookimpl # noqa
|
|
||||||
from . import eventlogger
|
|
||||||
|
|
||||||
from pkg_resources import get_distribution, DistributionNotFound
|
from pkg_resources import get_distribution, DistributionNotFound
|
||||||
try:
|
try:
|
||||||
@@ -81,62 +74,3 @@ def get_dc_event_name(integer, _DC_EVENTNAME_MAP={}):
|
|||||||
if name.startswith("DC_EVENT_"):
|
if name.startswith("DC_EVENT_"):
|
||||||
_DC_EVENTNAME_MAP[val] = name
|
_DC_EVENTNAME_MAP[val] = name
|
||||||
return _DC_EVENTNAME_MAP[integer]
|
return _DC_EVENTNAME_MAP[integer]
|
||||||
|
|
||||||
|
|
||||||
def register_global_plugin(plugin):
|
|
||||||
""" Register a global plugin which implements one or more
|
|
||||||
of the :class:`deltachat.hookspec.Global` hooks.
|
|
||||||
"""
|
|
||||||
gm = hookspec.Global._get_plugin_manager()
|
|
||||||
gm.register(plugin)
|
|
||||||
gm.check_pending()
|
|
||||||
|
|
||||||
|
|
||||||
def unregister_global_plugin(plugin):
|
|
||||||
gm = hookspec.Global._get_plugin_manager()
|
|
||||||
gm.unregister(plugin)
|
|
||||||
|
|
||||||
|
|
||||||
register_global_plugin(eventlogger)
|
|
||||||
|
|
||||||
|
|
||||||
def run_cmdline(argv=None, account_plugins=None):
|
|
||||||
""" Run a simple default command line app, registering the specified
|
|
||||||
account plugins. """
|
|
||||||
import argparse
|
|
||||||
if argv is None:
|
|
||||||
argv = sys.argv
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog=argv[0] if argv else None)
|
|
||||||
parser.add_argument("db", action="store", help="database file")
|
|
||||||
parser.add_argument("--show-ffi", action="store_true", help="show low level ffi events")
|
|
||||||
parser.add_argument("--email", action="store", help="email address")
|
|
||||||
parser.add_argument("--password", action="store", help="password")
|
|
||||||
|
|
||||||
args = parser.parse_args(argv[1:])
|
|
||||||
|
|
||||||
ac = Account(args.db)
|
|
||||||
|
|
||||||
if args.show_ffi:
|
|
||||||
log = eventlogger.FFIEventLogger(ac, "bot")
|
|
||||||
ac.add_account_plugin(log)
|
|
||||||
|
|
||||||
if not ac.is_configured():
|
|
||||||
assert args.email and args.password, (
|
|
||||||
"you must specify --email and --password once to configure this database/account"
|
|
||||||
)
|
|
||||||
ac.set_config("addr", args.email)
|
|
||||||
ac.set_config("mail_pw", args.password)
|
|
||||||
ac.set_config("mvbox_move", "0")
|
|
||||||
ac.set_config("mvbox_watch", "0")
|
|
||||||
ac.set_config("sentbox_watch", "0")
|
|
||||||
|
|
||||||
for plugin in account_plugins or []:
|
|
||||||
ac.add_account_plugin(plugin)
|
|
||||||
|
|
||||||
# start IO threads and configure if neccessary
|
|
||||||
ac.start()
|
|
||||||
|
|
||||||
print("{}: waiting for message".format(ac.get_config("addr")))
|
|
||||||
|
|
||||||
ac.wait_shutdown()
|
|
||||||
|
|||||||
@@ -29,10 +29,7 @@ def ffibuilder():
|
|||||||
extra_link_args = []
|
extra_link_args = []
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Compilation not supported yet on Windows, can you help?")
|
raise NotImplementedError("Compilation not supported yet on Windows, can you help?")
|
||||||
target_dir = os.environ.get("CARGO_TARGET_DIR")
|
objs = [os.path.join(projdir, 'target', target, 'libdeltachat.a')]
|
||||||
if target_dir is None:
|
|
||||||
target_dir = os.path.join(projdir, 'target')
|
|
||||||
objs = [os.path.join(target_dir, target, 'libdeltachat.a')]
|
|
||||||
assert os.path.exists(objs[0]), objs
|
assert os.path.exists(objs[0]), objs
|
||||||
incs = [os.path.join(projdir, 'deltachat-ffi')]
|
incs = [os.path.join(projdir, 'deltachat-ffi')]
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -2,25 +2,22 @@
|
|||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import atexit
|
import atexit
|
||||||
from contextlib import contextmanager
|
import threading
|
||||||
from email.utils import parseaddr
|
import re
|
||||||
import queue
|
import time
|
||||||
from threading import Event
|
|
||||||
import os
|
|
||||||
from array import array
|
from array import array
|
||||||
|
try:
|
||||||
|
from queue import Queue, Empty
|
||||||
|
except ImportError:
|
||||||
|
from Queue import Queue, Empty
|
||||||
|
|
||||||
import deltachat
|
import deltachat
|
||||||
from . import const
|
from . import const
|
||||||
from .capi import ffi, lib
|
from .capi import ffi, lib
|
||||||
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array, DCLot
|
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array, DCLot
|
||||||
from .chat import Chat
|
from .chat import Chat
|
||||||
from .message import Message, map_system_message
|
from .message import Message
|
||||||
from .contact import Contact
|
from .contact import Contact
|
||||||
from .tracker import ImexTracker
|
|
||||||
from . import hookspec, iothreads
|
|
||||||
|
|
||||||
|
|
||||||
class MissingCredentials(ValueError):
|
|
||||||
""" Account is missing `addr` and `mail_pw` config values. """
|
|
||||||
|
|
||||||
|
|
||||||
class Account(object):
|
class Account(object):
|
||||||
@@ -28,53 +25,38 @@ class Account(object):
|
|||||||
by the underlying deltachat core library. All public Account methods are
|
by the underlying deltachat core library. All public Account methods are
|
||||||
meant to be memory-safe and return memory-safe objects.
|
meant to be memory-safe and return memory-safe objects.
|
||||||
"""
|
"""
|
||||||
MissingCredentials = MissingCredentials
|
def __init__(self, db_path, logid=None, eventlogging=True, debug=True):
|
||||||
|
|
||||||
def __init__(self, db_path, os_name=None):
|
|
||||||
""" initialize account object.
|
""" initialize account object.
|
||||||
|
|
||||||
:param db_path: a path to the account database. The database
|
:param db_path: a path to the account database. The database
|
||||||
will be created if it doesn't exist.
|
will be created if it doesn't exist.
|
||||||
:param os_name: this will be put to the X-Mailer header in outgoing messages
|
: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.
|
||||||
"""
|
"""
|
||||||
# initialize per-account plugin system
|
|
||||||
self._pm = hookspec.PerAccount._make_plugin_manager()
|
|
||||||
self.add_account_plugin(self)
|
|
||||||
|
|
||||||
self._dc_context = ffi.gc(
|
self._dc_context = ffi.gc(
|
||||||
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, as_dc_charpointer(os_name)),
|
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||||
_destroy_dc_context,
|
_destroy_dc_context,
|
||||||
)
|
)
|
||||||
|
if eventlogging:
|
||||||
|
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:
|
||||||
|
self._threads = IOThreads(self._dc_context)
|
||||||
|
|
||||||
hook = hookspec.Global._get_plugin_manager().hook
|
|
||||||
|
|
||||||
self._threads = iothreads.IOThreads(self)
|
|
||||||
self._hook_event_queue = queue.Queue()
|
|
||||||
self._in_use_iter_events = False
|
|
||||||
self._shutdown_event = Event()
|
|
||||||
|
|
||||||
# open database
|
|
||||||
self.db_path = db_path
|
|
||||||
if hasattr(db_path, "encode"):
|
if hasattr(db_path, "encode"):
|
||||||
db_path = db_path.encode("utf8")
|
db_path = db_path.encode("utf8")
|
||||||
if not lib.dc_open(self._dc_context, db_path, ffi.NULL):
|
if not lib.dc_open(self._dc_context, db_path, ffi.NULL):
|
||||||
raise ValueError("Could not dc_open: {}".format(db_path))
|
raise ValueError("Could not dc_open: {}".format(db_path))
|
||||||
self._configkeys = self.get_config("sys.config_keys").split()
|
self._configkeys = self.get_config("sys.config_keys").split()
|
||||||
|
self._imex_events = Queue()
|
||||||
atexit.register(self.shutdown)
|
atexit.register(self.shutdown)
|
||||||
hook.dc_account_init(account=self)
|
|
||||||
|
|
||||||
@hookspec.account_hookimpl
|
|
||||||
def ac_process_ffi_event(self, ffi_event):
|
|
||||||
for name, kwargs in self._map_ffi_event(ffi_event):
|
|
||||||
ev = HookEvent(self, name=name, kwargs=kwargs)
|
|
||||||
self._hook_event_queue.put(ev)
|
|
||||||
|
|
||||||
# def __del__(self):
|
# def __del__(self):
|
||||||
# self.shutdown()
|
# self.shutdown()
|
||||||
|
|
||||||
def ac_log_line(self, msg):
|
|
||||||
self._pm.hook.ac_log_line(message=msg)
|
|
||||||
|
|
||||||
def _check_config_key(self, name):
|
def _check_config_key(self, name):
|
||||||
if name not in self._configkeys:
|
if name not in self._configkeys:
|
||||||
raise KeyError("{!r} not a valid config key, existing keys: {!r}".format(
|
raise KeyError("{!r} not a valid config key, existing keys: {!r}".format(
|
||||||
@@ -112,12 +94,9 @@ class Account(object):
|
|||||||
"""
|
"""
|
||||||
self._check_config_key(name)
|
self._check_config_key(name)
|
||||||
name = name.encode("utf8")
|
name = name.encode("utf8")
|
||||||
|
value = value.encode("utf8")
|
||||||
if name == b"addr" and self.is_configured():
|
if name == b"addr" and self.is_configured():
|
||||||
raise ValueError("can not change 'addr' after account is configured.")
|
raise ValueError("can not change 'addr' after account is configured.")
|
||||||
if value is not None:
|
|
||||||
value = value.encode("utf8")
|
|
||||||
else:
|
|
||||||
value = ffi.NULL
|
|
||||||
lib.dc_set_config(self._dc_context, name, value)
|
lib.dc_set_config(self._dc_context, name, value)
|
||||||
|
|
||||||
def get_config(self, name):
|
def get_config(self, name):
|
||||||
@@ -134,27 +113,16 @@ class Account(object):
|
|||||||
assert res != ffi.NULL, "config value not found for: {!r}".format(name)
|
assert res != ffi.NULL, "config value not found for: {!r}".format(name)
|
||||||
return from_dc_charpointer(res)
|
return from_dc_charpointer(res)
|
||||||
|
|
||||||
def _preconfigure_keypair(self, addr, public, secret):
|
def configure(self, **kwargs):
|
||||||
"""See dc_preconfigure_keypair() in deltachat.h.
|
""" set config values and configure this account.
|
||||||
|
|
||||||
In other words, you don't need this.
|
|
||||||
"""
|
|
||||||
res = lib.dc_preconfigure_keypair(self._dc_context,
|
|
||||||
as_dc_charpointer(addr),
|
|
||||||
as_dc_charpointer(public),
|
|
||||||
as_dc_charpointer(secret))
|
|
||||||
if res == 0:
|
|
||||||
raise Exception("Failed to set key")
|
|
||||||
|
|
||||||
def update_config(self, kwargs):
|
|
||||||
""" update config values.
|
|
||||||
|
|
||||||
:param kwargs: name=value config settings for this account.
|
:param kwargs: name=value config settings for this account.
|
||||||
values need to be unicode.
|
values need to be unicode.
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
for key, value in kwargs.items():
|
for name, value in kwargs.items():
|
||||||
self.set_config(key, str(value))
|
self.set_config(name, value)
|
||||||
|
lib.dc_configure(self._dc_context)
|
||||||
|
|
||||||
def is_configured(self):
|
def is_configured(self):
|
||||||
""" determine if the account is configured already; an initial connection
|
""" determine if the account is configured already; an initial connection
|
||||||
@@ -162,19 +130,7 @@ class Account(object):
|
|||||||
|
|
||||||
:returns: True if account is configured.
|
:returns: True if account is configured.
|
||||||
"""
|
"""
|
||||||
return bool(lib.dc_is_configured(self._dc_context))
|
return lib.dc_is_configured(self._dc_context)
|
||||||
|
|
||||||
def set_avatar(self, img_path):
|
|
||||||
"""Set self avatar.
|
|
||||||
|
|
||||||
:raises ValueError: if profile image could not be set
|
|
||||||
:returns: None
|
|
||||||
"""
|
|
||||||
if img_path is None:
|
|
||||||
self.set_config("selfavatar", None)
|
|
||||||
else:
|
|
||||||
assert os.path.exists(img_path), img_path
|
|
||||||
self.set_config("selfavatar", img_path)
|
|
||||||
|
|
||||||
def check_is_configured(self):
|
def check_is_configured(self):
|
||||||
""" Raise ValueError if this account is not configured. """
|
""" Raise ValueError if this account is not configured. """
|
||||||
@@ -192,6 +148,11 @@ class Account(object):
|
|||||||
raise ValueError("no flags set")
|
raise ValueError("no flags set")
|
||||||
lib.dc_empty_server(self._dc_context, flags)
|
lib.dc_empty_server(self._dc_context, flags)
|
||||||
|
|
||||||
|
def get_infostring(self):
|
||||||
|
""" return info of the configured account. """
|
||||||
|
self.check_is_configured()
|
||||||
|
return from_dc_charpointer(lib.dc_get_info(self._dc_context))
|
||||||
|
|
||||||
def get_latest_backupfile(self, backupdir):
|
def get_latest_backupfile(self, backupdir):
|
||||||
""" return the latest backup file in a given directory.
|
""" return the latest backup file in a given directory.
|
||||||
"""
|
"""
|
||||||
@@ -213,7 +174,8 @@ class Account(object):
|
|||||||
|
|
||||||
:returns: :class:`deltachat.contact.Contact`
|
:returns: :class:`deltachat.contact.Contact`
|
||||||
"""
|
"""
|
||||||
return Contact(self, const.DC_CONTACT_ID_SELF)
|
self.check_is_configured()
|
||||||
|
return Contact(self._dc_context, const.DC_CONTACT_ID_SELF)
|
||||||
|
|
||||||
def create_contact(self, email, name=None):
|
def create_contact(self, email, name=None):
|
||||||
""" create a (new) Contact. If there already is a Contact
|
""" create a (new) Contact. If there already is a Contact
|
||||||
@@ -224,14 +186,11 @@ class Account(object):
|
|||||||
:param name: display name for this contact (optional)
|
:param name: display name for this contact (optional)
|
||||||
:returns: :class:`deltachat.contact.Contact` instance.
|
:returns: :class:`deltachat.contact.Contact` instance.
|
||||||
"""
|
"""
|
||||||
realname, addr = parseaddr(email)
|
name = as_dc_charpointer(name)
|
||||||
if name:
|
email = as_dc_charpointer(email)
|
||||||
realname = name
|
contact_id = lib.dc_create_contact(self._dc_context, name, email)
|
||||||
realname = as_dc_charpointer(realname)
|
|
||||||
addr = as_dc_charpointer(addr)
|
|
||||||
contact_id = lib.dc_create_contact(self._dc_context, realname, addr)
|
|
||||||
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL
|
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL
|
||||||
return Contact(self, contact_id)
|
return Contact(self._dc_context, contact_id)
|
||||||
|
|
||||||
def delete_contact(self, contact):
|
def delete_contact(self, contact):
|
||||||
""" delete a Contact.
|
""" delete a Contact.
|
||||||
@@ -244,14 +203,6 @@ class Account(object):
|
|||||||
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL
|
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL
|
||||||
return bool(lib.dc_delete_contact(self._dc_context, contact_id))
|
return bool(lib.dc_delete_contact(self._dc_context, contact_id))
|
||||||
|
|
||||||
def get_contact_by_addr(self, email):
|
|
||||||
""" get a contact for the email address or None if it's blocked or doesn't exist. """
|
|
||||||
_, addr = parseaddr(email)
|
|
||||||
addr = as_dc_charpointer(addr)
|
|
||||||
contact_id = lib.dc_lookup_contact_id_by_addr(self._dc_context, addr)
|
|
||||||
if contact_id:
|
|
||||||
return self.get_contact_by_id(contact_id)
|
|
||||||
|
|
||||||
def get_contacts(self, query=None, with_self=False, only_verified=False):
|
def get_contacts(self, query=None, with_self=False, only_verified=False):
|
||||||
""" get a (filtered) list of contacts.
|
""" get a (filtered) list of contacts.
|
||||||
|
|
||||||
@@ -271,15 +222,7 @@ class Account(object):
|
|||||||
lib.dc_get_contacts(self._dc_context, flags, query),
|
lib.dc_get_contacts(self._dc_context, flags, query),
|
||||||
lib.dc_array_unref
|
lib.dc_array_unref
|
||||||
)
|
)
|
||||||
return list(iter_array(dc_array, lambda x: Contact(self, x)))
|
return list(iter_array(dc_array, lambda x: Contact(self._dc_context, x)))
|
||||||
|
|
||||||
def get_fresh_messages(self):
|
|
||||||
""" yield all fresh messages from all chats. """
|
|
||||||
dc_array = ffi.gc(
|
|
||||||
lib.dc_get_fresh_msgs(self._dc_context),
|
|
||||||
lib.dc_array_unref
|
|
||||||
)
|
|
||||||
yield from iter_array(dc_array, lambda x: Message.from_db(self, x))
|
|
||||||
|
|
||||||
def create_chat_by_contact(self, contact):
|
def create_chat_by_contact(self, contact):
|
||||||
""" create or get an existing 1:1 chat object for the specified contact or contact id.
|
""" create or get an existing 1:1 chat object for the specified contact or contact id.
|
||||||
@@ -301,9 +244,6 @@ class Account(object):
|
|||||||
""" create or get an existing chat object for the
|
""" create or get an existing chat object for the
|
||||||
the specified message.
|
the specified message.
|
||||||
|
|
||||||
If this message is in the deaddrop chat then
|
|
||||||
the sender will become an accepted contact.
|
|
||||||
|
|
||||||
:param message: messsage id or message instance.
|
:param message: messsage id or message instance.
|
||||||
:returns: a :class:`deltachat.chat.Chat` object.
|
:returns: a :class:`deltachat.chat.Chat` object.
|
||||||
"""
|
"""
|
||||||
@@ -356,13 +296,6 @@ class Account(object):
|
|||||||
"""
|
"""
|
||||||
return Message.from_db(self, msg_id)
|
return Message.from_db(self, msg_id)
|
||||||
|
|
||||||
def get_contact_by_id(self, contact_id):
|
|
||||||
""" return Contact instance or None.
|
|
||||||
:param contact_id: integer id of this contact.
|
|
||||||
:returns: None or :class:`deltachat.contact.Contact` instance.
|
|
||||||
"""
|
|
||||||
return Contact(self, contact_id)
|
|
||||||
|
|
||||||
def get_chat_by_id(self, chat_id):
|
def get_chat_by_id(self, chat_id):
|
||||||
""" return Chat instance.
|
""" return Chat instance.
|
||||||
:param chat_id: integer id of this chat.
|
:param chat_id: integer id of this chat.
|
||||||
@@ -407,37 +340,45 @@ class Account(object):
|
|||||||
lib.dc_delete_msgs(self._dc_context, msg_ids, len(msg_ids))
|
lib.dc_delete_msgs(self._dc_context, msg_ids, len(msg_ids))
|
||||||
|
|
||||||
def export_self_keys(self, path):
|
def export_self_keys(self, path):
|
||||||
""" export public and private keys to the specified directory.
|
""" export public and private keys to the specified directory. """
|
||||||
|
|
||||||
Note that the account does not have to be started.
|
|
||||||
"""
|
|
||||||
return self._export(path, imex_cmd=1)
|
return self._export(path, imex_cmd=1)
|
||||||
|
|
||||||
def export_all(self, path):
|
def export_all(self, path):
|
||||||
"""return new file containing a backup of all database state
|
"""return new file containing a backup of all database state
|
||||||
(chats, contacts, keys, media, ...). The file is created in the
|
(chats, contacts, keys, media, ...). The file is created in the
|
||||||
the `path` directory.
|
the `path` directory.
|
||||||
|
|
||||||
Note that the account does not have to be started.
|
|
||||||
"""
|
"""
|
||||||
export_files = self._export(path, 11)
|
export_files = self._export(path, 11)
|
||||||
if len(export_files) != 1:
|
if len(export_files) != 1:
|
||||||
raise RuntimeError("found more than one new file")
|
raise RuntimeError("found more than one new file")
|
||||||
return export_files[0]
|
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):
|
def _export(self, path, imex_cmd):
|
||||||
with self.temp_plugin(ImexTracker()) as imex_tracker:
|
self._imex_events_clear()
|
||||||
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
|
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
|
||||||
if not self._threads.is_started():
|
if not self._threads.is_started():
|
||||||
lib.dc_perform_imap_jobs(self._dc_context)
|
lib.dc_perform_imap_jobs(self._dc_context)
|
||||||
return imex_tracker.wait_finish()
|
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_self_keys(self, path):
|
def import_self_keys(self, path):
|
||||||
""" Import private keys found in the `path` directory.
|
""" Import private keys found in the `path` directory.
|
||||||
The last imported key is made the default keys unless its name
|
The last imported key is made the default keys unless its name
|
||||||
contains the string legacy. Public keys are not imported.
|
contains the string legacy. Public keys are not imported.
|
||||||
|
|
||||||
Note that the account does not have to be started.
|
|
||||||
"""
|
"""
|
||||||
self._import(path, imex_cmd=2)
|
self._import(path, imex_cmd=2)
|
||||||
|
|
||||||
@@ -450,11 +391,12 @@ class Account(object):
|
|||||||
self._import(path, imex_cmd=12)
|
self._import(path, imex_cmd=12)
|
||||||
|
|
||||||
def _import(self, path, imex_cmd):
|
def _import(self, path, imex_cmd):
|
||||||
with self.temp_plugin(ImexTracker()) as imex_tracker:
|
self._imex_events_clear()
|
||||||
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
|
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
|
||||||
if not self._threads.is_started():
|
if not self._threads.is_started():
|
||||||
lib.dc_perform_imap_jobs(self._dc_context)
|
lib.dc_perform_imap_jobs(self._dc_context)
|
||||||
imex_tracker.wait_finish()
|
if not self._imex_events.get():
|
||||||
|
raise ValueError("import from path '{}' failed".format(path))
|
||||||
|
|
||||||
def initiate_key_transfer(self):
|
def initiate_key_transfer(self):
|
||||||
"""return setup code after a Autocrypt setup message
|
"""return setup code after a Autocrypt setup message
|
||||||
@@ -519,6 +461,63 @@ class Account(object):
|
|||||||
raise ValueError("could not join group")
|
raise ValueError("could not join group")
|
||||||
return Chat(self, chat_id)
|
return Chat(self, chat_id)
|
||||||
|
|
||||||
|
def stop_ongoing(self):
|
||||||
|
lib.dc_stop_ongoing_process(self._dc_context)
|
||||||
|
|
||||||
|
#
|
||||||
|
# meta API for start/stop and event based processing
|
||||||
|
#
|
||||||
|
|
||||||
|
def wait_next_incoming_message(self):
|
||||||
|
""" wait for and return next incoming message. """
|
||||||
|
ev = self._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
|
||||||
|
return self.get_message_by_id(ev[2])
|
||||||
|
|
||||||
|
def start_threads(self, mvbox=False, sentbox=False):
|
||||||
|
""" start IMAP/SMTP threads (and configure account if it hasn't happened).
|
||||||
|
|
||||||
|
:raises: ValueError if 'addr' or 'mail_pw' are not configured.
|
||||||
|
:returns: None
|
||||||
|
"""
|
||||||
|
if not self.is_configured():
|
||||||
|
self.configure()
|
||||||
|
self._threads.start(mvbox=mvbox, sentbox=sentbox)
|
||||||
|
|
||||||
|
def stop_threads(self, wait=True):
|
||||||
|
""" stop IMAP/SMTP threads. """
|
||||||
|
if self._threads.is_started():
|
||||||
|
self.stop_ongoing()
|
||||||
|
self._threads.stop(wait=wait)
|
||||||
|
|
||||||
|
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"):
|
||||||
|
# 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)
|
||||||
|
del self._dc_context
|
||||||
|
atexit.unregister(self.shutdown)
|
||||||
|
|
||||||
|
def _process_event(self, ctx, evt_name, data1, data2):
|
||||||
|
assert ctx == self._dc_context
|
||||||
|
if hasattr(self, "_evlogger"):
|
||||||
|
self._evlogger(evt_name, data1, data2)
|
||||||
|
method = getattr(self, "on_" + evt_name.lower(), None)
|
||||||
|
if method is not None:
|
||||||
|
method(data1, data2)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def on_dc_event_imex_progress(self, data1, data2):
|
||||||
|
if data1 == 1000:
|
||||||
|
self._imex_events.put(True)
|
||||||
|
elif data1 == 0:
|
||||||
|
self._imex_events.put(False)
|
||||||
|
|
||||||
|
def on_dc_event_imex_file_written(self, data1, data2):
|
||||||
|
self._imex_events.put(data1)
|
||||||
|
|
||||||
def set_location(self, latitude=0.0, longitude=0.0, accuracy=0.0):
|
def set_location(self, latitude=0.0, longitude=0.0, accuracy=0.0):
|
||||||
"""set a new location. It effects all chats where we currently
|
"""set a new location. It effects all chats where we currently
|
||||||
have enabled location streaming.
|
have enabled location streaming.
|
||||||
@@ -533,122 +532,147 @@ class Account(object):
|
|||||||
if dc_res == 0:
|
if dc_res == 0:
|
||||||
raise ValueError("no chat is streaming locations")
|
raise ValueError("no chat is streaming locations")
|
||||||
|
|
||||||
#
|
|
||||||
# meta API for start/stop and event based processing
|
|
||||||
#
|
|
||||||
|
|
||||||
def add_account_plugin(self, plugin, name=None):
|
class IOThreads:
|
||||||
""" add an account plugin which implements one or more of
|
def __init__(self, dc_context, log_event=lambda *args: None):
|
||||||
the :class:`deltachat.hookspec.PerAccount` hooks.
|
self._dc_context = dc_context
|
||||||
"""
|
self._thread_quitflag = False
|
||||||
self._pm.register(plugin, name=name)
|
self._name2thread = {}
|
||||||
self._pm.check_pending()
|
self._log_event = log_event
|
||||||
return plugin
|
|
||||||
|
|
||||||
@contextmanager
|
def is_started(self):
|
||||||
def temp_plugin(self, plugin):
|
return len(self._name2thread) > 0
|
||||||
""" run a with-block with the given plugin temporarily registered. """
|
|
||||||
self._pm.register(plugin)
|
|
||||||
yield plugin
|
|
||||||
self._pm.unregister(plugin)
|
|
||||||
|
|
||||||
def stop_ongoing(self):
|
def start(self, imap=True, smtp=True, mvbox=False, sentbox=False):
|
||||||
""" Stop ongoing securejoin, configuration or other core jobs. """
|
assert not self.is_started()
|
||||||
lib.dc_stop_ongoing_process(self._dc_context)
|
if imap:
|
||||||
|
self._start_one_thread("inbox", self.imap_thread_run)
|
||||||
|
if mvbox:
|
||||||
|
self._start_one_thread("mvbox", self.mvbox_thread_run)
|
||||||
|
if sentbox:
|
||||||
|
self._start_one_thread("sentbox", self.sentbox_thread_run)
|
||||||
|
if smtp:
|
||||||
|
self._start_one_thread("smtp", self.smtp_thread_run)
|
||||||
|
|
||||||
def start(self, callback_thread=True):
|
def _start_one_thread(self, name, func):
|
||||||
""" start this account (activate imap/smtp threads etc.)
|
self._name2thread[name] = t = threading.Thread(target=func, name=name)
|
||||||
and return immediately.
|
t.setDaemon(1)
|
||||||
|
t.start()
|
||||||
|
|
||||||
If this account is not configured, an internal configuration
|
def stop(self, wait=False):
|
||||||
job will be scheduled if config values are sufficiently specified.
|
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()
|
||||||
|
|
||||||
You may call `wait_shutdown` or `shutdown` after the
|
def imap_thread_run(self):
|
||||||
account is in started mode.
|
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, "INBOX THREAD FINISHED")
|
||||||
|
|
||||||
:raises MissingCredentials: if `addr` and `mail_pw` values are not set.
|
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")
|
||||||
|
|
||||||
:returns: None
|
def sentbox_thread_run(self):
|
||||||
"""
|
self._log_event("py-bindings-info", 0, "SENTBOX THREAD START")
|
||||||
if not self.is_configured():
|
while not self._thread_quitflag:
|
||||||
if not self.get_config("addr") or not self.get_config("mail_pw"):
|
lib.dc_perform_sentbox_jobs(self._dc_context)
|
||||||
raise MissingCredentials("addr or mail_pwd not set in config")
|
lib.dc_perform_sentbox_fetch(self._dc_context)
|
||||||
lib.dc_configure(self._dc_context)
|
lib.dc_perform_sentbox_idle(self._dc_context)
|
||||||
self._threads.start(callback_thread=callback_thread)
|
self._log_event("py-bindings-info", 0, "SENTBOX THREAD FINISHED")
|
||||||
|
|
||||||
def wait_shutdown(self):
|
def smtp_thread_run(self):
|
||||||
""" wait until shutdown of this account has completed. """
|
self._log_event("py-bindings-info", 0, "SMTP THREAD START")
|
||||||
self._shutdown_event.wait()
|
while not self._thread_quitflag:
|
||||||
|
lib.dc_perform_smtp_jobs(self._dc_context)
|
||||||
|
lib.dc_perform_smtp_idle(self._dc_context)
|
||||||
|
self._log_event("py-bindings-info", 0, "SMTP THREAD FINISHED")
|
||||||
|
|
||||||
def shutdown(self, wait=True):
|
|
||||||
""" shutdown account, stop threads and close and remove
|
|
||||||
underlying dc_context and callbacks. """
|
|
||||||
dc_context = self._dc_context
|
|
||||||
if dc_context is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._threads.is_started():
|
class EventLogger:
|
||||||
self.stop_ongoing()
|
_loglock = threading.RLock()
|
||||||
self._threads.stop(wait=False)
|
|
||||||
lib.dc_close(dc_context)
|
|
||||||
self._hook_event_queue.put(None)
|
|
||||||
self._threads.stop(wait=wait) # to wait for threads
|
|
||||||
self._dc_context = None
|
|
||||||
atexit.unregister(self.shutdown)
|
|
||||||
self._shutdown_event.set()
|
|
||||||
hook = hookspec.Global._get_plugin_manager().hook
|
|
||||||
hook.dc_account_after_shutdown(account=self, dc_context=dc_context)
|
|
||||||
|
|
||||||
def _handle_current_events(self):
|
def __init__(self, dc_context, logid=None, debug=True):
|
||||||
""" handle all currently queued events and then return. """
|
self._dc_context = dc_context
|
||||||
|
self._event_queue = Queue()
|
||||||
|
self._debug = debug
|
||||||
|
if logid is None:
|
||||||
|
logid = str(self._dc_context).strip(">").split()[-1]
|
||||||
|
self.logid = logid
|
||||||
|
self._timeout = None
|
||||||
|
self.init_time = time.time()
|
||||||
|
|
||||||
|
def __call__(self, evt_name, data1, data2):
|
||||||
|
self._log_event(evt_name, data1, data2)
|
||||||
|
self._event_queue.put((evt_name, data1, data2))
|
||||||
|
|
||||||
|
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)
|
||||||
|
if check_error and ev[0] == "DC_EVENT_ERROR":
|
||||||
|
raise ValueError("{}({!r},{!r})".format(*ev))
|
||||||
|
return ev
|
||||||
|
|
||||||
|
def ensure_event_not_queued(self, event_name_regex):
|
||||||
|
__tracebackhide__ = True
|
||||||
|
rex = re.compile("(?:{}).*".format(event_name_regex))
|
||||||
while 1:
|
while 1:
|
||||||
try:
|
try:
|
||||||
event = self._hook_event_queue.get(block=False)
|
ev = self._event_queue.get(False)
|
||||||
except queue.Empty:
|
except Empty:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
event.call_hook()
|
assert not rex.match(ev[0]), "event found {}".format(ev)
|
||||||
|
|
||||||
def iter_events(self, timeout=None):
|
def get_matching(self, event_name_regex, check_error=True, timeout=None):
|
||||||
""" yield hook events until shutdown.
|
self._log("-- waiting for event with regex: {} --".format(event_name_regex))
|
||||||
|
rex = re.compile("(?:{}).*".format(event_name_regex))
|
||||||
It is not allowed to call iter_events() from multiple threads.
|
|
||||||
"""
|
|
||||||
if self._in_use_iter_events:
|
|
||||||
raise RuntimeError("can only call iter_events() from one thread")
|
|
||||||
self._in_use_iter_events = True
|
|
||||||
while 1:
|
while 1:
|
||||||
event = self._hook_event_queue.get(timeout=timeout)
|
ev = self.get(timeout=timeout, check_error=check_error)
|
||||||
if event is None:
|
if rex.match(ev[0]):
|
||||||
break
|
return ev
|
||||||
yield event
|
|
||||||
|
|
||||||
def _map_ffi_event(self, ffi_event):
|
def get_info_matching(self, regex):
|
||||||
name = ffi_event.name
|
rex = re.compile("(?:{}).*".format(regex))
|
||||||
if name == "DC_EVENT_CONFIGURE_PROGRESS":
|
while 1:
|
||||||
data1 = ffi_event.data1
|
ev = self.get_matching("DC_EVENT_INFO")
|
||||||
if data1 == 0 or data1 == 1000:
|
if rex.match(ev[2]):
|
||||||
success = data1 == 1000
|
return ev
|
||||||
yield "ac_configure_completed", dict(success=success)
|
|
||||||
elif name == "DC_EVENT_INCOMING_MSG":
|
def _log_event(self, evt_name, data1, data2):
|
||||||
msg = self.get_message_by_id(ffi_event.data2)
|
# don't show events that are anyway empty impls now
|
||||||
yield map_system_message(msg) or ("ac_incoming_message", dict(message=msg))
|
if evt_name == "DC_EVENT_GET_STRING":
|
||||||
elif name == "DC_EVENT_MSGS_CHANGED":
|
return
|
||||||
if ffi_event.data2 != 0:
|
if self._debug:
|
||||||
msg = self.get_message_by_id(ffi_event.data2)
|
evpart = "{}({!r},{!r})".format(evt_name, data1, data2)
|
||||||
if msg.is_outgoing():
|
self._log(evpart)
|
||||||
res = map_system_message(msg)
|
|
||||||
if res and res[0].startswith("ac_member"):
|
def _log(self, msg):
|
||||||
yield res
|
t = threading.currentThread()
|
||||||
yield "ac_outgoing_message", dict(message=msg)
|
tname = getattr(t, "name", t)
|
||||||
elif msg.is_in_fresh():
|
if tname == "MainThread":
|
||||||
yield map_system_message(msg) or ("ac_incoming_message", dict(message=msg))
|
tname = "MAIN"
|
||||||
elif name == "DC_EVENT_MSG_DELIVERED":
|
with self._loglock:
|
||||||
msg = self.get_message_by_id(ffi_event.data2)
|
print("{:2.2f} [{}-{}] {}".format(time.time() - self.init_time, tname, self.logid, msg))
|
||||||
yield "ac_message_delivered", dict(message=msg)
|
|
||||||
elif name == "DC_EVENT_CHAT_MODIFIED":
|
|
||||||
chat = self.get_chat_by_id(ffi_event.data1)
|
|
||||||
yield "ac_chat_modified", dict(chat=chat)
|
|
||||||
|
|
||||||
|
|
||||||
def _destroy_dc_context(dc_context, dc_context_unref=lib.dc_context_unref):
|
def _destroy_dc_context(dc_context, dc_context_unref=lib.dc_context_unref):
|
||||||
@@ -675,17 +699,3 @@ class ScannedQRCode:
|
|||||||
@property
|
@property
|
||||||
def contact_id(self):
|
def contact_id(self):
|
||||||
return self._dc_lot.id()
|
return self._dc_lot.id()
|
||||||
|
|
||||||
|
|
||||||
class HookEvent:
|
|
||||||
def __init__(self, account, name, kwargs):
|
|
||||||
assert hasattr(account._pm.hook, name), name
|
|
||||||
self.account = account
|
|
||||||
self.name = name
|
|
||||||
self.kwargs = kwargs
|
|
||||||
|
|
||||||
def call_hook(self):
|
|
||||||
hook = getattr(self.account._pm.hook, self.name, None)
|
|
||||||
if hook is None:
|
|
||||||
raise ValueError("event_name {} unknown".format(self.name))
|
|
||||||
return hook(**self.kwargs)
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import calendar
|
import calendar
|
||||||
import json
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import os
|
import os
|
||||||
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
|
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
|
||||||
@@ -30,7 +29,7 @@ class Chat(object):
|
|||||||
return not (self == other)
|
return not (self == other)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Chat id={} name={}>".format(self.id, self.get_name())
|
return "<Chat id={} name={} dc_context={}>".format(self.id, self.get_name(), self._dc_context)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _dc_chat(self):
|
def _dc_chat(self):
|
||||||
@@ -51,16 +50,6 @@ class Chat(object):
|
|||||||
|
|
||||||
# ------ chat status/metadata API ------------------------------
|
# ------ chat status/metadata API ------------------------------
|
||||||
|
|
||||||
def is_group(self):
|
|
||||||
""" return true if this chat is a group chat.
|
|
||||||
|
|
||||||
:returns: True if chat is a group-chat, false if it's a contact 1:1 chat.
|
|
||||||
"""
|
|
||||||
return lib.dc_chat_get_type(self._dc_chat) in (
|
|
||||||
const.DC_CHAT_TYPE_GROUP,
|
|
||||||
const.DC_CHAT_TYPE_VERIFIED_GROUP
|
|
||||||
)
|
|
||||||
|
|
||||||
def is_deaddrop(self):
|
def is_deaddrop(self):
|
||||||
""" return true if this chat is a deaddrop chat.
|
""" return true if this chat is a deaddrop chat.
|
||||||
|
|
||||||
@@ -68,13 +57,6 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
return self.id == const.DC_CHAT_ID_DEADDROP
|
return self.id == const.DC_CHAT_ID_DEADDROP
|
||||||
|
|
||||||
def is_muted(self):
|
|
||||||
""" return true if this chat is muted.
|
|
||||||
|
|
||||||
:returns: True if chat is muted, False otherwise.
|
|
||||||
"""
|
|
||||||
return lib.dc_chat_is_muted(self._dc_chat)
|
|
||||||
|
|
||||||
def is_promoted(self):
|
def is_promoted(self):
|
||||||
""" return True if this chat is promoted, i.e.
|
""" return True if this chat is promoted, i.e.
|
||||||
the member contacts are aware of their membership,
|
the member contacts are aware of their membership,
|
||||||
@@ -101,45 +83,14 @@ class Chat(object):
|
|||||||
def set_name(self, name):
|
def set_name(self, name):
|
||||||
""" set name of this chat.
|
""" set name of this chat.
|
||||||
|
|
||||||
:param name: as a unicode string.
|
:param: name as a unicode string.
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
name = as_dc_charpointer(name)
|
name = as_dc_charpointer(name)
|
||||||
return lib.dc_set_chat_name(self._dc_context, self.id, name)
|
return lib.dc_set_chat_name(self._dc_context, self.id, name)
|
||||||
|
|
||||||
def mute(self, duration=None):
|
|
||||||
""" mutes the chat
|
|
||||||
|
|
||||||
:param duration: Number of seconds to mute the chat for. None to mute until unmuted again.
|
|
||||||
:returns: None
|
|
||||||
"""
|
|
||||||
if duration is None:
|
|
||||||
mute_duration = -1
|
|
||||||
else:
|
|
||||||
mute_duration = duration
|
|
||||||
ret = lib.dc_set_chat_mute_duration(self._dc_context, self.id, mute_duration)
|
|
||||||
if not bool(ret):
|
|
||||||
raise ValueError("Call to dc_set_chat_mute_duration failed")
|
|
||||||
|
|
||||||
def unmute(self):
|
|
||||||
""" unmutes the chat
|
|
||||||
|
|
||||||
:returns: None
|
|
||||||
"""
|
|
||||||
ret = lib.dc_set_chat_mute_duration(self._dc_context, self.id, 0)
|
|
||||||
if not bool(ret):
|
|
||||||
raise ValueError("Failed to unmute chat")
|
|
||||||
|
|
||||||
def get_mute_duration(self):
|
|
||||||
""" Returns the number of seconds until the mute of this chat is lifted.
|
|
||||||
|
|
||||||
:param duration:
|
|
||||||
:returns: Returns the number of seconds the chat is still muted for. (0 for not muted, -1 forever muted)
|
|
||||||
"""
|
|
||||||
return bool(lib.dc_chat_get_remaining_mute_duration(self.id))
|
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
""" (deprecated) return type of this chat.
|
""" return type of this chat.
|
||||||
|
|
||||||
:returns: one of const.DC_CHAT_TYPE_*
|
:returns: one of const.DC_CHAT_TYPE_*
|
||||||
"""
|
"""
|
||||||
@@ -157,30 +108,6 @@ class Chat(object):
|
|||||||
|
|
||||||
# ------ chat messaging API ------------------------------
|
# ------ chat messaging API ------------------------------
|
||||||
|
|
||||||
def send_msg(self, msg):
|
|
||||||
"""send a message by using a ready Message object.
|
|
||||||
|
|
||||||
:param msg: a :class:`deltachat.message.Message` instance
|
|
||||||
previously returned by
|
|
||||||
e.g. :meth:`deltachat.message.Message.new_empty` or
|
|
||||||
:meth:`prepare_file`.
|
|
||||||
:raises ValueError: if message can not be sent.
|
|
||||||
|
|
||||||
:returns: a :class:`deltachat.message.Message` instance as
|
|
||||||
sent out. This is the same object as was passed in, which
|
|
||||||
has been modified with the new state of the core.
|
|
||||||
"""
|
|
||||||
if msg.is_out_preparing():
|
|
||||||
assert msg.id != 0
|
|
||||||
# get a fresh copy of dc_msg, the core needs it
|
|
||||||
msg = Message.from_db(self.account, msg.id)
|
|
||||||
sent_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
|
|
||||||
if sent_id == 0:
|
|
||||||
raise ValueError("message could not be sent")
|
|
||||||
# modify message in place to avoid bad state for the caller
|
|
||||||
msg._dc_msg = Message.from_db(self.account, sent_id)._dc_msg
|
|
||||||
return msg
|
|
||||||
|
|
||||||
def send_text(self, text):
|
def send_text(self, text):
|
||||||
""" send a text message and return the resulting Message instance.
|
""" send a text message and return the resulting Message instance.
|
||||||
|
|
||||||
@@ -202,12 +129,9 @@ class Chat(object):
|
|||||||
:raises ValueError: if message can not be send/chat does not exist.
|
:raises ValueError: if message can not be send/chat does not exist.
|
||||||
:returns: the resulting :class:`deltachat.message.Message` instance
|
:returns: the resulting :class:`deltachat.message.Message` instance
|
||||||
"""
|
"""
|
||||||
msg = Message.new_empty(self.account, view_type="file")
|
msg = self.prepare_message_file(path=path, mime_type=mime_type)
|
||||||
msg.set_file(path, mime_type)
|
self.send_prepared(msg)
|
||||||
sent_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
|
return msg
|
||||||
if sent_id == 0:
|
|
||||||
raise ValueError("message could not be sent")
|
|
||||||
return Message.from_db(self.account, sent_id)
|
|
||||||
|
|
||||||
def send_image(self, path):
|
def send_image(self, path):
|
||||||
""" send an image message and return the resulting Message instance.
|
""" send an image message and return the resulting Message instance.
|
||||||
@@ -217,12 +141,9 @@ class Chat(object):
|
|||||||
:returns: the resulting :class:`deltachat.message.Message` instance
|
:returns: the resulting :class:`deltachat.message.Message` instance
|
||||||
"""
|
"""
|
||||||
mime_type = mimetypes.guess_type(path)[0]
|
mime_type = mimetypes.guess_type(path)[0]
|
||||||
msg = Message.new_empty(self.account, view_type="image")
|
msg = self.prepare_message_file(path=path, mime_type=mime_type, view_type="image")
|
||||||
msg.set_file(path, mime_type)
|
self.send_prepared(msg)
|
||||||
sent_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
|
return msg
|
||||||
if sent_id == 0:
|
|
||||||
raise ValueError("message could not be sent")
|
|
||||||
return Message.from_db(self.account, sent_id)
|
|
||||||
|
|
||||||
def prepare_message(self, msg):
|
def prepare_message(self, msg):
|
||||||
""" create a new prepared message.
|
""" create a new prepared message.
|
||||||
@@ -321,12 +242,6 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
return lib.dc_marknoticed_chat(self._dc_context, self.id)
|
return lib.dc_marknoticed_chat(self._dc_context, self.id)
|
||||||
|
|
||||||
def get_summary(self):
|
|
||||||
""" return dictionary with summary information. """
|
|
||||||
dc_res = lib.dc_chat_get_info_json(self._dc_context, self.id)
|
|
||||||
s = from_dc_charpointer(dc_res)
|
|
||||||
return json.loads(s)
|
|
||||||
|
|
||||||
# ------ group management API ------------------------------
|
# ------ group management API ------------------------------
|
||||||
|
|
||||||
def add_contact(self, contact):
|
def add_contact(self, contact):
|
||||||
@@ -363,7 +278,7 @@ class Chat(object):
|
|||||||
lib.dc_array_unref
|
lib.dc_array_unref
|
||||||
)
|
)
|
||||||
return list(iter_array(
|
return list(iter_array(
|
||||||
dc_array, lambda id: Contact(self.account, id))
|
dc_array, lambda id: Contact(self._dc_context, id))
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_profile_image(self, img_path):
|
def set_profile_image(self, img_path):
|
||||||
@@ -409,12 +324,6 @@ class Chat(object):
|
|||||||
return None
|
return None
|
||||||
return from_dc_charpointer(dc_res)
|
return from_dc_charpointer(dc_res)
|
||||||
|
|
||||||
def get_color(self):
|
|
||||||
"""return the color of the chat.
|
|
||||||
:returns: color as 0x00rrggbb
|
|
||||||
"""
|
|
||||||
return lib.dc_chat_get_color(self._dc_chat)
|
|
||||||
|
|
||||||
# ------ location streaming API ------------------------------
|
# ------ location streaming API ------------------------------
|
||||||
|
|
||||||
def is_sending_locations(self):
|
def is_sending_locations(self):
|
||||||
@@ -423,12 +332,6 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
return lib.dc_is_sending_locations_to_chat(self._dc_context, self.id)
|
return lib.dc_is_sending_locations_to_chat(self._dc_context, self.id)
|
||||||
|
|
||||||
def is_archived(self):
|
|
||||||
"""return True if this chat is archived.
|
|
||||||
:returns: True if archived.
|
|
||||||
"""
|
|
||||||
return lib.dc_chat_get_visibility(self._dc_chat) == const.DC_CHAT_VISIBILITY_ARCHIVED
|
|
||||||
|
|
||||||
def enable_sending_locations(self, seconds):
|
def enable_sending_locations(self, seconds):
|
||||||
"""enable sending locations for this chat.
|
"""enable sending locations for this chat.
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ from os.path import join as joinpath
|
|||||||
DC_GCL_ARCHIVED_ONLY = 0x01
|
DC_GCL_ARCHIVED_ONLY = 0x01
|
||||||
DC_GCL_NO_SPECIALS = 0x02
|
DC_GCL_NO_SPECIALS = 0x02
|
||||||
DC_GCL_ADD_ALLDONE_HINT = 0x04
|
DC_GCL_ADD_ALLDONE_HINT = 0x04
|
||||||
DC_GCL_FOR_FORWARDING = 0x08
|
|
||||||
DC_GCL_VERIFIED_ONLY = 0x01
|
DC_GCL_VERIFIED_ONLY = 0x01
|
||||||
DC_GCL_ADD_SELF = 0x02
|
DC_GCL_ADD_SELF = 0x02
|
||||||
DC_QR_ASK_VERIFYCONTACT = 200
|
DC_QR_ASK_VERIFYCONTACT = 200
|
||||||
@@ -19,7 +18,6 @@ DC_QR_ASK_VERIFYGROUP = 202
|
|||||||
DC_QR_FPR_OK = 210
|
DC_QR_FPR_OK = 210
|
||||||
DC_QR_FPR_MISMATCH = 220
|
DC_QR_FPR_MISMATCH = 220
|
||||||
DC_QR_FPR_WITHOUT_ADDR = 230
|
DC_QR_FPR_WITHOUT_ADDR = 230
|
||||||
DC_QR_ACCOUNT = 250
|
|
||||||
DC_QR_ADDR = 320
|
DC_QR_ADDR = 320
|
||||||
DC_QR_TEXT = 330
|
DC_QR_TEXT = 330
|
||||||
DC_QR_URL = 332
|
DC_QR_URL = 332
|
||||||
@@ -70,6 +68,7 @@ DC_LP_SMTP_SOCKET_SSL = 0x20000
|
|||||||
DC_LP_SMTP_SOCKET_PLAIN = 0x40000
|
DC_LP_SMTP_SOCKET_PLAIN = 0x40000
|
||||||
DC_CERTCK_AUTO = 0
|
DC_CERTCK_AUTO = 0
|
||||||
DC_CERTCK_STRICT = 1
|
DC_CERTCK_STRICT = 1
|
||||||
|
DC_CERTCK_ACCEPT_INVALID_HOSTNAMES = 2
|
||||||
DC_CERTCK_ACCEPT_INVALID_CERTIFICATES = 3
|
DC_CERTCK_ACCEPT_INVALID_CERTIFICATES = 3
|
||||||
DC_EMPTY_MVBOX = 0x01
|
DC_EMPTY_MVBOX = 0x01
|
||||||
DC_EMPTY_INBOX = 0x02
|
DC_EMPTY_INBOX = 0x02
|
||||||
@@ -103,18 +102,14 @@ DC_EVENT_FILE_COPIED = 2055
|
|||||||
DC_EVENT_IS_OFFLINE = 2081
|
DC_EVENT_IS_OFFLINE = 2081
|
||||||
DC_EVENT_GET_STRING = 2091
|
DC_EVENT_GET_STRING = 2091
|
||||||
DC_STR_SELFNOTINGRP = 21
|
DC_STR_SELFNOTINGRP = 21
|
||||||
DC_KEY_GEN_DEFAULT = 0
|
|
||||||
DC_KEY_GEN_RSA2048 = 1
|
|
||||||
DC_KEY_GEN_ED25519 = 2
|
|
||||||
DC_PROVIDER_STATUS_OK = 1
|
DC_PROVIDER_STATUS_OK = 1
|
||||||
DC_PROVIDER_STATUS_PREPARATION = 2
|
DC_PROVIDER_STATUS_PREPARATION = 2
|
||||||
DC_PROVIDER_STATUS_BROKEN = 3
|
DC_PROVIDER_STATUS_BROKEN = 3
|
||||||
DC_CHAT_VISIBILITY_NORMAL = 0
|
|
||||||
DC_CHAT_VISIBILITY_ARCHIVED = 1
|
|
||||||
DC_CHAT_VISIBILITY_PINNED = 2
|
|
||||||
DC_STR_NOMESSAGES = 1
|
DC_STR_NOMESSAGES = 1
|
||||||
DC_STR_SELF = 2
|
DC_STR_SELF = 2
|
||||||
DC_STR_DRAFT = 3
|
DC_STR_DRAFT = 3
|
||||||
|
DC_STR_MEMBER = 4
|
||||||
|
DC_STR_CONTACT = 6
|
||||||
DC_STR_VOICEMESSAGE = 7
|
DC_STR_VOICEMESSAGE = 7
|
||||||
DC_STR_DEADDROP = 8
|
DC_STR_DEADDROP = 8
|
||||||
DC_STR_IMAGE = 9
|
DC_STR_IMAGE = 9
|
||||||
@@ -146,6 +141,7 @@ DC_STR_ARCHIVEDCHATS = 40
|
|||||||
DC_STR_STARREDMSGS = 41
|
DC_STR_STARREDMSGS = 41
|
||||||
DC_STR_AC_SETUP_MSG_SUBJECT = 42
|
DC_STR_AC_SETUP_MSG_SUBJECT = 42
|
||||||
DC_STR_AC_SETUP_MSG_BODY = 43
|
DC_STR_AC_SETUP_MSG_BODY = 43
|
||||||
|
DC_STR_SELFTALK_SUBTITLE = 50
|
||||||
DC_STR_CANNOT_LOGIN = 60
|
DC_STR_CANNOT_LOGIN = 60
|
||||||
DC_STR_SERVER_RESPONSE = 61
|
DC_STR_SERVER_RESPONSE = 61
|
||||||
DC_STR_MSGACTIONBYUSER = 62
|
DC_STR_MSGACTIONBYUSER = 62
|
||||||
@@ -154,14 +150,13 @@ DC_STR_MSGLOCATIONENABLED = 64
|
|||||||
DC_STR_MSGLOCATIONDISABLED = 65
|
DC_STR_MSGLOCATIONDISABLED = 65
|
||||||
DC_STR_LOCATION = 66
|
DC_STR_LOCATION = 66
|
||||||
DC_STR_STICKER = 67
|
DC_STR_STICKER = 67
|
||||||
DC_STR_DEVICE_MESSAGES = 68
|
DC_STR_COUNT = 67
|
||||||
DC_STR_COUNT = 68
|
|
||||||
# end const generated
|
# end const generated
|
||||||
|
|
||||||
|
|
||||||
def read_event_defines(f):
|
def read_event_defines(f):
|
||||||
rex = re.compile(r'#define\s+((?:DC_EVENT|DC_QR|DC_MSG|DC_LP|DC_EMPTY|DC_CERTCK|DC_STATE|DC_STR|'
|
rex = re.compile(r'#define\s+((?:DC_EVENT|DC_QR|DC_MSG|DC_LP|DC_EMPTY|DC_CERTCK|DC_STATE|DC_STR|'
|
||||||
r'DC_CONTACT_ID|DC_GCL|DC_CHAT|DC_PROVIDER|DC_KEY_GEN)_\S+)\s+([x\d]+).*')
|
r'DC_CONTACT_ID|DC_GCL|DC_CHAT|DC_PROVIDER)_\S+)\s+([x\d]+).*')
|
||||||
for line in f:
|
for line in f:
|
||||||
m = rex.match(line)
|
m = rex.match(line)
|
||||||
if m:
|
if m:
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ class Contact(object):
|
|||||||
|
|
||||||
You obtain instances of it through :class:`deltachat.account.Account`.
|
You obtain instances of it through :class:`deltachat.account.Account`.
|
||||||
"""
|
"""
|
||||||
def __init__(self, account, id):
|
def __init__(self, dc_context, id):
|
||||||
self.account = account
|
self._dc_context = dc_context
|
||||||
self._dc_context = account._dc_context
|
|
||||||
self.id = id
|
self.id = id
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
@@ -48,17 +47,3 @@ class Contact(object):
|
|||||||
def is_verified(self):
|
def is_verified(self):
|
||||||
""" Return True if the contact is verified. """
|
""" Return True if the contact is verified. """
|
||||||
return lib.dc_contact_is_verified(self._dc_contact)
|
return lib.dc_contact_is_verified(self._dc_contact)
|
||||||
|
|
||||||
def get_profile_image(self):
|
|
||||||
"""Get contact profile image.
|
|
||||||
|
|
||||||
:returns: path to profile image, None if no profile image exists.
|
|
||||||
"""
|
|
||||||
dc_res = lib.dc_contact_get_profile_image(self._dc_contact)
|
|
||||||
if dc_res == ffi.NULL:
|
|
||||||
return None
|
|
||||||
return from_dc_charpointer(dc_res)
|
|
||||||
|
|
||||||
def get_chat(self):
|
|
||||||
"""return 1:1 chat for this contact. """
|
|
||||||
return self.account.create_chat_by_contact(self)
|
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
import deltachat
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
import re
|
|
||||||
from queue import Queue, Empty
|
|
||||||
from .hookspec import account_hookimpl, global_hookimpl
|
|
||||||
|
|
||||||
|
|
||||||
@global_hookimpl
|
|
||||||
def dc_account_init(account):
|
|
||||||
# send all FFI events for this account to a plugin hook
|
|
||||||
def _ll_event(ctx, evt_name, data1, data2):
|
|
||||||
assert ctx == account._dc_context
|
|
||||||
ffi_event = FFIEvent(name=evt_name, data1=data1, data2=data2)
|
|
||||||
account._pm.hook.ac_process_ffi_event(
|
|
||||||
account=account, ffi_event=ffi_event
|
|
||||||
)
|
|
||||||
deltachat.set_context_callback(account._dc_context, _ll_event)
|
|
||||||
|
|
||||||
|
|
||||||
@global_hookimpl
|
|
||||||
def dc_account_after_shutdown(dc_context):
|
|
||||||
deltachat.clear_context_callback(dc_context)
|
|
||||||
|
|
||||||
|
|
||||||
class FFIEvent:
|
|
||||||
def __init__(self, name, data1, data2):
|
|
||||||
self.name = name
|
|
||||||
self.data1 = data1
|
|
||||||
self.data2 = data2
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "{name} data1={data1} data2={data2}".format(**self.__dict__)
|
|
||||||
|
|
||||||
|
|
||||||
class FFIEventLogger:
|
|
||||||
""" If you register an instance of this logger with an Account
|
|
||||||
you'll get all ffi-events printed.
|
|
||||||
"""
|
|
||||||
# to prevent garbled logging
|
|
||||||
_loglock = threading.RLock()
|
|
||||||
|
|
||||||
def __init__(self, account, logid):
|
|
||||||
"""
|
|
||||||
:param logid: an optional logging prefix that should be used with
|
|
||||||
the default internal logging.
|
|
||||||
"""
|
|
||||||
self.account = account
|
|
||||||
self.logid = logid
|
|
||||||
self.init_time = time.time()
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_process_ffi_event(self, ffi_event):
|
|
||||||
self._log_event(ffi_event)
|
|
||||||
|
|
||||||
def _log_event(self, ffi_event):
|
|
||||||
# don't show events that are anyway empty impls now
|
|
||||||
if ffi_event.name == "DC_EVENT_GET_STRING":
|
|
||||||
return
|
|
||||||
self.account.ac_log_line(str(ffi_event))
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_log_line(self, message):
|
|
||||||
t = threading.currentThread()
|
|
||||||
tname = getattr(t, "name", t)
|
|
||||||
if tname == "MainThread":
|
|
||||||
tname = "MAIN"
|
|
||||||
elapsed = time.time() - self.init_time
|
|
||||||
locname = tname
|
|
||||||
if self.logid:
|
|
||||||
locname += "-" + self.logid
|
|
||||||
s = "{:2.2f} [{}] {}".format(elapsed, locname, message)
|
|
||||||
with self._loglock:
|
|
||||||
print(s, flush=True)
|
|
||||||
|
|
||||||
|
|
||||||
class FFIEventTracker:
|
|
||||||
def __init__(self, account, timeout=None):
|
|
||||||
self.account = account
|
|
||||||
self._timeout = timeout
|
|
||||||
self._event_queue = Queue()
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_process_ffi_event(self, ffi_event):
|
|
||||||
self._event_queue.put(ffi_event)
|
|
||||||
|
|
||||||
def set_timeout(self, timeout):
|
|
||||||
self._timeout = timeout
|
|
||||||
|
|
||||||
def consume_events(self, check_error=True):
|
|
||||||
while not self._event_queue.empty():
|
|
||||||
self.get(check_error=check_error)
|
|
||||||
|
|
||||||
def get(self, timeout=None, check_error=True):
|
|
||||||
timeout = timeout if timeout is not None else self._timeout
|
|
||||||
ev = self._event_queue.get(timeout=timeout)
|
|
||||||
if check_error and ev.name == "DC_EVENT_ERROR":
|
|
||||||
raise ValueError(str(ev))
|
|
||||||
return ev
|
|
||||||
|
|
||||||
def ensure_event_not_queued(self, event_name_regex):
|
|
||||||
__tracebackhide__ = True
|
|
||||||
rex = re.compile("(?:{}).*".format(event_name_regex))
|
|
||||||
while 1:
|
|
||||||
try:
|
|
||||||
ev = self._event_queue.get(False)
|
|
||||||
except Empty:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
assert not rex.match(ev.name), "event found {}".format(ev)
|
|
||||||
|
|
||||||
def get_matching(self, event_name_regex, check_error=True, timeout=None):
|
|
||||||
self.account.ac_log_line("-- waiting for event with regex: {} --".format(event_name_regex))
|
|
||||||
rex = re.compile("(?:{}).*".format(event_name_regex))
|
|
||||||
while 1:
|
|
||||||
ev = self.get(timeout=timeout, check_error=check_error)
|
|
||||||
if rex.match(ev.name):
|
|
||||||
return ev
|
|
||||||
|
|
||||||
def get_info_matching(self, regex):
|
|
||||||
rex = re.compile("(?:{}).*".format(regex))
|
|
||||||
while 1:
|
|
||||||
ev = self.get_matching("DC_EVENT_INFO")
|
|
||||||
if rex.match(ev.data2):
|
|
||||||
return ev
|
|
||||||
|
|
||||||
def wait_next_incoming_message(self):
|
|
||||||
""" wait for and return next incoming message. """
|
|
||||||
ev = self.get_matching("DC_EVENT_INCOMING_MSG")
|
|
||||||
return self.account.get_message_by_id(ev.data2)
|
|
||||||
|
|
||||||
def wait_next_messages_changed(self):
|
|
||||||
""" wait for and return next message-changed message or None
|
|
||||||
if the event contains no msgid"""
|
|
||||||
ev = self.get_matching("DC_EVENT_MSGS_CHANGED")
|
|
||||||
if ev.data2 > 0:
|
|
||||||
return self.account.get_message_by_id(ev.data2)
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
""" Hooks for Python bindings to Delta Chat Core Rust CFFI"""
|
|
||||||
|
|
||||||
import pluggy
|
|
||||||
|
|
||||||
|
|
||||||
account_spec_name = "deltachat-account"
|
|
||||||
account_hookspec = pluggy.HookspecMarker(account_spec_name)
|
|
||||||
account_hookimpl = pluggy.HookimplMarker(account_spec_name)
|
|
||||||
|
|
||||||
global_spec_name = "deltachat-global"
|
|
||||||
global_hookspec = pluggy.HookspecMarker(global_spec_name)
|
|
||||||
global_hookimpl = pluggy.HookimplMarker(global_spec_name)
|
|
||||||
|
|
||||||
|
|
||||||
class PerAccount:
|
|
||||||
""" per-Account-instance hook specifications.
|
|
||||||
|
|
||||||
Except for ac_process_ffi_event all hooks are executed
|
|
||||||
in the thread which calls Account.wait_shutdown().
|
|
||||||
"""
|
|
||||||
@classmethod
|
|
||||||
def _make_plugin_manager(cls):
|
|
||||||
pm = pluggy.PluginManager(account_spec_name)
|
|
||||||
pm.add_hookspecs(cls)
|
|
||||||
return pm
|
|
||||||
|
|
||||||
@account_hookspec
|
|
||||||
def ac_process_ffi_event(self, ffi_event):
|
|
||||||
""" process a CFFI low level events for a given account.
|
|
||||||
|
|
||||||
ffi_event has "name", "data1", "data2" values as specified
|
|
||||||
with `DC_EVENT_* <https://c.delta.chat/group__DC__EVENT.html>`_.
|
|
||||||
|
|
||||||
DANGER: this hook is executed from the callback invoked by core.
|
|
||||||
Hook implementations need to be short running and can typically
|
|
||||||
not call back into core because this would easily cause recursion issues.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@account_hookspec
|
|
||||||
def ac_log_line(self, message):
|
|
||||||
""" log a message related to the account. """
|
|
||||||
|
|
||||||
@account_hookspec
|
|
||||||
def ac_configure_completed(self, success):
|
|
||||||
""" Called when a configure process completed. """
|
|
||||||
|
|
||||||
@account_hookspec
|
|
||||||
def ac_incoming_message(self, message):
|
|
||||||
""" Called on any incoming message (to deaddrop or chat). """
|
|
||||||
|
|
||||||
@account_hookspec
|
|
||||||
def ac_outgoing_message(self, message):
|
|
||||||
""" Called on each outgoing message (both system and "normal")."""
|
|
||||||
|
|
||||||
@account_hookspec
|
|
||||||
def ac_message_delivered(self, message):
|
|
||||||
""" Called when an outgoing message has been delivered to SMTP. """
|
|
||||||
|
|
||||||
@account_hookspec
|
|
||||||
def ac_chat_modified(self, chat):
|
|
||||||
""" Chat was created or modified regarding membership, avatar, title. """
|
|
||||||
|
|
||||||
@account_hookspec
|
|
||||||
def ac_member_added(self, chat, contact, message):
|
|
||||||
""" Called for each contact added to an accepted chat. """
|
|
||||||
|
|
||||||
@account_hookspec
|
|
||||||
def ac_member_removed(self, chat, contact, message):
|
|
||||||
""" Called for each contact removed from a chat. """
|
|
||||||
|
|
||||||
|
|
||||||
class Global:
|
|
||||||
""" global hook specifications using a per-process singleton
|
|
||||||
plugin manager instance.
|
|
||||||
|
|
||||||
"""
|
|
||||||
_plugin_manager = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_plugin_manager(cls):
|
|
||||||
if cls._plugin_manager is None:
|
|
||||||
cls._plugin_manager = pm = pluggy.PluginManager(global_spec_name)
|
|
||||||
pm.add_hookspecs(cls)
|
|
||||||
return cls._plugin_manager
|
|
||||||
|
|
||||||
@global_hookspec
|
|
||||||
def dc_account_init(self, account):
|
|
||||||
""" called when `Account::__init__()` function starts executing. """
|
|
||||||
|
|
||||||
@global_hookspec
|
|
||||||
def dc_account_after_shutdown(self, account, dc_context):
|
|
||||||
""" Called after the account has been shutdown. """
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
|
|
||||||
from contextlib import contextmanager
|
|
||||||
|
|
||||||
from .capi import lib
|
|
||||||
|
|
||||||
|
|
||||||
class IOThreads:
|
|
||||||
def __init__(self, account):
|
|
||||||
self.account = account
|
|
||||||
self._dc_context = account._dc_context
|
|
||||||
self._thread_quitflag = False
|
|
||||||
self._name2thread = {}
|
|
||||||
|
|
||||||
def is_started(self):
|
|
||||||
return len(self._name2thread) > 0
|
|
||||||
|
|
||||||
def start(self, callback_thread):
|
|
||||||
assert not self.is_started()
|
|
||||||
self._start_one_thread("inbox", self.imap_thread_run)
|
|
||||||
self._start_one_thread("smtp", self.smtp_thread_run)
|
|
||||||
|
|
||||||
if callback_thread:
|
|
||||||
self._start_one_thread("cb", self.cb_thread_run)
|
|
||||||
|
|
||||||
if int(self.account.get_config("mvbox_watch")):
|
|
||||||
self._start_one_thread("mvbox", self.mvbox_thread_run)
|
|
||||||
|
|
||||||
if int(self.account.get_config("sentbox_watch")):
|
|
||||||
self._start_one_thread("sentbox", self.sentbox_thread_run)
|
|
||||||
|
|
||||||
def _start_one_thread(self, name, func):
|
|
||||||
self._name2thread[name] = t = threading.Thread(target=func, name=name)
|
|
||||||
t.setDaemon(1)
|
|
||||||
t.start()
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def log_execution(self, message):
|
|
||||||
self.account.ac_log_line(message + " START")
|
|
||||||
yield
|
|
||||||
self.account.ac_log_line(message + " FINISHED")
|
|
||||||
|
|
||||||
def stop(self, wait=False):
|
|
||||||
self._thread_quitflag = True
|
|
||||||
|
|
||||||
# Workaround for a race condition. Make sure that thread is
|
|
||||||
# not in between checking for quitflag and entering idle.
|
|
||||||
time.sleep(0.5)
|
|
||||||
|
|
||||||
lib.dc_interrupt_imap_idle(self._dc_context)
|
|
||||||
lib.dc_interrupt_smtp_idle(self._dc_context)
|
|
||||||
if "mvbox" in self._name2thread:
|
|
||||||
lib.dc_interrupt_mvbox_idle(self._dc_context)
|
|
||||||
if "sentbox" in self._name2thread:
|
|
||||||
lib.dc_interrupt_sentbox_idle(self._dc_context)
|
|
||||||
if wait:
|
|
||||||
for name, thread in self._name2thread.items():
|
|
||||||
if thread != threading.currentThread():
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
def cb_thread_run(self):
|
|
||||||
with self.log_execution("CALLBACK THREAD START"):
|
|
||||||
it = self.account.iter_events()
|
|
||||||
while not self._thread_quitflag:
|
|
||||||
try:
|
|
||||||
ev = next(it)
|
|
||||||
except StopIteration:
|
|
||||||
break
|
|
||||||
self.account.ac_log_line("calling hook name={} kwargs={}".format(ev.name, ev.kwargs))
|
|
||||||
ev.call_hook()
|
|
||||||
|
|
||||||
def imap_thread_run(self):
|
|
||||||
with self.log_execution("INBOX THREAD START"):
|
|
||||||
while not self._thread_quitflag:
|
|
||||||
lib.dc_perform_imap_jobs(self._dc_context)
|
|
||||||
if not self._thread_quitflag:
|
|
||||||
lib.dc_perform_imap_fetch(self._dc_context)
|
|
||||||
if not self._thread_quitflag:
|
|
||||||
lib.dc_perform_imap_idle(self._dc_context)
|
|
||||||
|
|
||||||
def mvbox_thread_run(self):
|
|
||||||
with self.log_execution("MVBOX THREAD"):
|
|
||||||
while not self._thread_quitflag:
|
|
||||||
lib.dc_perform_mvbox_jobs(self._dc_context)
|
|
||||||
if not self._thread_quitflag:
|
|
||||||
lib.dc_perform_mvbox_fetch(self._dc_context)
|
|
||||||
if not self._thread_quitflag:
|
|
||||||
lib.dc_perform_mvbox_idle(self._dc_context)
|
|
||||||
|
|
||||||
def sentbox_thread_run(self):
|
|
||||||
with self.log_execution("SENTBOX THREAD"):
|
|
||||||
while not self._thread_quitflag:
|
|
||||||
lib.dc_perform_sentbox_jobs(self._dc_context)
|
|
||||||
if not self._thread_quitflag:
|
|
||||||
lib.dc_perform_sentbox_fetch(self._dc_context)
|
|
||||||
if not self._thread_quitflag:
|
|
||||||
lib.dc_perform_sentbox_idle(self._dc_context)
|
|
||||||
|
|
||||||
def smtp_thread_run(self):
|
|
||||||
with self.log_execution("SMTP THREAD"):
|
|
||||||
while not self._thread_quitflag:
|
|
||||||
lib.dc_perform_smtp_jobs(self._dc_context)
|
|
||||||
if not self._thread_quitflag:
|
|
||||||
lib.dc_perform_smtp_idle(self._dc_context)
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
""" The Message object. """
|
""" The Message object. """
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
from . import props
|
from . import props
|
||||||
from .cutil import from_dc_charpointer, as_dc_charpointer
|
from .cutil import from_dc_charpointer, as_dc_charpointer
|
||||||
from .capi import lib, ffi
|
from .capi import lib, ffi
|
||||||
@@ -28,9 +29,7 @@ class Message(object):
|
|||||||
return self.account == other.account and self.id == other.id
|
return self.account == other.account and self.id == other.id
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
c = self.get_sender_contact()
|
return "<Message id={} dc_context={}>".format(self.id, self._dc_context)
|
||||||
return "<Message id={} sender={}/{} outgoing={} chat={}/{}>".format(
|
|
||||||
self.id, c.id, c.addr, self.is_outgoing(), self.chat.id, self.chat.get_name())
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_db(cls, account, id):
|
def from_db(cls, account, id):
|
||||||
@@ -52,16 +51,6 @@ class Message(object):
|
|||||||
lib.dc_msg_unref
|
lib.dc_msg_unref
|
||||||
))
|
))
|
||||||
|
|
||||||
def accept_sender_contact(self):
|
|
||||||
""" ensure that the sender is an accepted contact
|
|
||||||
and that the message has a non-deaddrop chat object.
|
|
||||||
"""
|
|
||||||
self.account.create_chat_by_message(self)
|
|
||||||
self._dc_msg = ffi.gc(
|
|
||||||
lib.dc_get_msg(self._dc_context, self.id),
|
|
||||||
lib.dc_msg_unref
|
|
||||||
)
|
|
||||||
|
|
||||||
@props.with_doc
|
@props.with_doc
|
||||||
def text(self):
|
def text(self):
|
||||||
"""unicode text of this messages (might be empty if not a text message). """
|
"""unicode text of this messages (might be empty if not a text message). """
|
||||||
@@ -69,6 +58,8 @@ class Message(object):
|
|||||||
|
|
||||||
def set_text(self, text):
|
def set_text(self, text):
|
||||||
"""set text of this message. """
|
"""set text of this message. """
|
||||||
|
assert self.id > 0, "message not prepared"
|
||||||
|
assert self.is_out_preparing()
|
||||||
lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
|
lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
|
||||||
|
|
||||||
@props.with_doc
|
@props.with_doc
|
||||||
@@ -81,6 +72,19 @@ class Message(object):
|
|||||||
mtype = ffi.NULL if mime_type is None else as_dc_charpointer(mime_type)
|
mtype = ffi.NULL if mime_type is None else as_dc_charpointer(mime_type)
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
raise ValueError("path does not exist: {!r}".format(path))
|
raise ValueError("path does not exist: {!r}".format(path))
|
||||||
|
blobdir = self.account.get_blobdir()
|
||||||
|
if not path.startswith(blobdir):
|
||||||
|
for i in range(50):
|
||||||
|
ext = "" if i == 0 else "-" + str(i)
|
||||||
|
dest = os.path.join(blobdir, os.path.basename(path) + ext)
|
||||||
|
if os.path.exists(dest):
|
||||||
|
continue
|
||||||
|
shutil.copyfile(path, dest)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError("could not create blobdir-path for {}".format(path))
|
||||||
|
path = dest
|
||||||
|
assert path.startswith(blobdir), path
|
||||||
lib.dc_msg_set_file(self._dc_msg, as_dc_charpointer(path), mtype)
|
lib.dc_msg_set_file(self._dc_msg, as_dc_charpointer(path), mtype)
|
||||||
|
|
||||||
@props.with_doc
|
@props.with_doc
|
||||||
@@ -93,10 +97,6 @@ class Message(object):
|
|||||||
"""mime type of the file (if it exists)"""
|
"""mime type of the file (if it exists)"""
|
||||||
return from_dc_charpointer(lib.dc_msg_get_filemime(self._dc_msg))
|
return from_dc_charpointer(lib.dc_msg_get_filemime(self._dc_msg))
|
||||||
|
|
||||||
def is_system_message(self):
|
|
||||||
""" return True if this message is a system/info message. """
|
|
||||||
return lib.dc_msg_is_info(self._dc_msg)
|
|
||||||
|
|
||||||
def is_setup_message(self):
|
def is_setup_message(self):
|
||||||
""" return True if this message is a setup message. """
|
""" return True if this message is a setup message. """
|
||||||
return lib.dc_msg_is_setupmessage(self._dc_msg)
|
return lib.dc_msg_is_setupmessage(self._dc_msg)
|
||||||
@@ -162,7 +162,7 @@ class Message(object):
|
|||||||
if mime_headers:
|
if mime_headers:
|
||||||
s = ffi.string(ffi.gc(mime_headers, lib.dc_str_unref))
|
s = ffi.string(ffi.gc(mime_headers, lib.dc_str_unref))
|
||||||
if isinstance(s, bytes):
|
if isinstance(s, bytes):
|
||||||
return email.message_from_bytes(s)
|
s = s.decode("ascii")
|
||||||
return email.message_from_string(s)
|
return email.message_from_string(s)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -175,13 +175,6 @@ class Message(object):
|
|||||||
chat_id = lib.dc_msg_get_chat_id(self._dc_msg)
|
chat_id = lib.dc_msg_get_chat_id(self._dc_msg)
|
||||||
return Chat(self.account, chat_id)
|
return Chat(self.account, chat_id)
|
||||||
|
|
||||||
def get_sender_chat(self):
|
|
||||||
"""return the 1:1 chat with the sender of this message.
|
|
||||||
|
|
||||||
:returns: :class:`deltachat.chat.Chat` instance
|
|
||||||
"""
|
|
||||||
return self.get_sender_contact().get_chat()
|
|
||||||
|
|
||||||
def get_sender_contact(self):
|
def get_sender_contact(self):
|
||||||
"""return the contact of who wrote the message.
|
"""return the contact of who wrote the message.
|
||||||
|
|
||||||
@@ -189,7 +182,7 @@ class Message(object):
|
|||||||
"""
|
"""
|
||||||
from .contact import Contact
|
from .contact import Contact
|
||||||
contact_id = lib.dc_msg_get_from_id(self._dc_msg)
|
contact_id = lib.dc_msg_get_from_id(self._dc_msg)
|
||||||
return Contact(self.account, contact_id)
|
return Contact(self._dc_context, contact_id)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Message State query methods
|
# Message State query methods
|
||||||
@@ -197,7 +190,7 @@ class Message(object):
|
|||||||
@property
|
@property
|
||||||
def _msgstate(self):
|
def _msgstate(self):
|
||||||
if self.id == 0:
|
if self.id == 0:
|
||||||
dc_msg = self._dc_msg
|
dc_msg = self.message._dc_msg
|
||||||
else:
|
else:
|
||||||
# load message from db to get a fresh/current state
|
# load message from db to get a fresh/current state
|
||||||
dc_msg = ffi.gc(
|
dc_msg = ffi.gc(
|
||||||
@@ -230,13 +223,6 @@ class Message(object):
|
|||||||
"""
|
"""
|
||||||
return self._msgstate == const.DC_STATE_IN_SEEN
|
return self._msgstate == const.DC_STATE_IN_SEEN
|
||||||
|
|
||||||
def is_outgoing(self):
|
|
||||||
"""Return True if Message is outgoing. """
|
|
||||||
return self._msgstate in (
|
|
||||||
const.DC_STATE_OUT_PREPARING, const.DC_STATE_OUT_PENDING,
|
|
||||||
const.DC_STATE_OUT_FAILED, const.DC_STATE_OUT_MDN_RCVD,
|
|
||||||
const.DC_STATE_OUT_DELIVERED)
|
|
||||||
|
|
||||||
def is_out_preparing(self):
|
def is_out_preparing(self):
|
||||||
"""Return True if Message is outgoing, but its file is being prepared.
|
"""Return True if Message is outgoing, but its file is being prepared.
|
||||||
"""
|
"""
|
||||||
@@ -299,10 +285,6 @@ class Message(object):
|
|||||||
""" return True if it's a file message. """
|
""" return True if it's a file message. """
|
||||||
return self._view_type == const.DC_MSG_FILE
|
return self._view_type == const.DC_MSG_FILE
|
||||||
|
|
||||||
def mark_seen(self):
|
|
||||||
""" mark this message as seen. """
|
|
||||||
self.account.mark_seen_messages([self.id])
|
|
||||||
|
|
||||||
|
|
||||||
# some code for handling DC_MSG_* view types
|
# some code for handling DC_MSG_* view types
|
||||||
|
|
||||||
@@ -322,29 +304,3 @@ def get_viewtype_code_from_name(view_type_name):
|
|||||||
return code
|
return code
|
||||||
raise ValueError("message typecode not found for {!r}, "
|
raise ValueError("message typecode not found for {!r}, "
|
||||||
"available {!r}".format(view_type_name, list(_view_type_mapping.values())))
|
"available {!r}".format(view_type_name, list(_view_type_mapping.values())))
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# some helper code for turning system messages into hook events
|
|
||||||
#
|
|
||||||
|
|
||||||
def map_system_message(msg):
|
|
||||||
if msg.is_system_message():
|
|
||||||
res = parse_system_add_remove(msg.text)
|
|
||||||
if res:
|
|
||||||
contact = msg.account.get_contact_by_addr(res[1])
|
|
||||||
if contact:
|
|
||||||
d = dict(chat=msg.chat, contact=contact, message=msg)
|
|
||||||
return "ac_member_" + res[0], d
|
|
||||||
|
|
||||||
|
|
||||||
def parse_system_add_remove(text):
|
|
||||||
# Member Me (x@y) removed by a@b.
|
|
||||||
# Member x@y removed by a@b
|
|
||||||
text = text.lower()
|
|
||||||
parts = text.split()
|
|
||||||
if parts[0] == "member":
|
|
||||||
if parts[2] in ("removed", "added"):
|
|
||||||
return parts[2], parts[1]
|
|
||||||
if parts[3] in ("removed", "added"):
|
|
||||||
return parts[3], parts[2].strip("()")
|
|
||||||
|
|||||||
@@ -11,18 +11,27 @@ class ProviderNotFoundError(Exception):
|
|||||||
class Provider(object):
|
class Provider(object):
|
||||||
"""Provider information.
|
"""Provider information.
|
||||||
|
|
||||||
:param domain: The email to get the provider info for.
|
:param domain: The domain to get the provider info for, this is
|
||||||
|
normally the part following the `@` of the domain.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, account, addr):
|
def __init__(self, domain):
|
||||||
provider = ffi.gc(
|
provider = ffi.gc(
|
||||||
lib.dc_provider_new_from_email(account._dc_context, as_dc_charpointer(addr)),
|
lib.dc_provider_new_from_domain(as_dc_charpointer(domain)),
|
||||||
lib.dc_provider_unref,
|
lib.dc_provider_unref,
|
||||||
)
|
)
|
||||||
if provider == ffi.NULL:
|
if provider == ffi.NULL:
|
||||||
raise ProviderNotFoundError("Provider not found")
|
raise ProviderNotFoundError("Provider not found")
|
||||||
self._provider = provider
|
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
|
@property
|
||||||
def overview_page(self):
|
def overview_page(self):
|
||||||
"""URL to the overview page of the provider on providers.delta.chat."""
|
"""URL to the overview page of the provider on providers.delta.chat."""
|
||||||
@@ -30,10 +39,21 @@ class Provider(object):
|
|||||||
lib.dc_provider_get_overview_page(self._provider))
|
lib.dc_provider_get_overview_page(self._provider))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def get_before_login_hints(self):
|
def name(self):
|
||||||
"""Should be shown to the user on login."""
|
"""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(
|
return from_dc_charpointer(
|
||||||
lib.dc_provider_get_before_login_hints(self._provider))
|
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
|
@property
|
||||||
def status(self):
|
def status(self):
|
||||||
|
|||||||
@@ -1,381 +0,0 @@
|
|||||||
from __future__ import print_function
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
import queue
|
|
||||||
import threading
|
|
||||||
import fnmatch
|
|
||||||
import pytest
|
|
||||||
import requests
|
|
||||||
import time
|
|
||||||
from . import Account, const
|
|
||||||
from .tracker import ConfigureTracker
|
|
||||||
from .capi import lib
|
|
||||||
from .eventlogger import FFIEventLogger, FFIEventTracker
|
|
||||||
from _pytest.monkeypatch import MonkeyPatch
|
|
||||||
from _pytest._code import Source
|
|
||||||
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
|
||||||
parser.addoption(
|
|
||||||
"--liveconfig", action="store", default=None,
|
|
||||||
help="a file with >=2 lines where each line "
|
|
||||||
"contains NAME=VALUE config settings for one account"
|
|
||||||
)
|
|
||||||
parser.addoption(
|
|
||||||
"--ignored", action="store_true",
|
|
||||||
help="Also run tests marked with the ignored marker",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_configure(config):
|
|
||||||
config.addinivalue_line(
|
|
||||||
"markers", "ignored: Mark test as bing slow, skipped unless --ignored is used."
|
|
||||||
)
|
|
||||||
cfg = config.getoption('--liveconfig')
|
|
||||||
if not cfg:
|
|
||||||
cfg = os.getenv('DCC_NEW_TMP_EMAIL')
|
|
||||||
if cfg:
|
|
||||||
config.option.liveconfig = cfg
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_setup(item):
|
|
||||||
if (list(item.iter_markers(name="ignored"))
|
|
||||||
and not item.config.getoption("ignored")):
|
|
||||||
pytest.skip("Ignored tests not requested, use --ignored")
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_report_header(config, startdir):
|
|
||||||
summary = []
|
|
||||||
|
|
||||||
t = tempfile.mktemp()
|
|
||||||
m = MonkeyPatch()
|
|
||||||
try:
|
|
||||||
m.setattr(sys.stdout, "write", lambda x: len(x))
|
|
||||||
ac = Account(t)
|
|
||||||
info = ac.get_info()
|
|
||||||
ac.shutdown()
|
|
||||||
finally:
|
|
||||||
m.undo()
|
|
||||||
os.remove(t)
|
|
||||||
summary.extend(['Deltachat core={} sqlite={}'.format(
|
|
||||||
info['deltachat_core_version'],
|
|
||||||
info['sqlite_version'],
|
|
||||||
)])
|
|
||||||
|
|
||||||
cfg = config.option.liveconfig
|
|
||||||
if cfg:
|
|
||||||
if "?" in cfg:
|
|
||||||
url, token = cfg.split("?", 1)
|
|
||||||
summary.append('Liveconfig provider: {}?<token ommitted>'.format(url))
|
|
||||||
else:
|
|
||||||
summary.append('Liveconfig file: {}'.format(cfg))
|
|
||||||
return summary
|
|
||||||
|
|
||||||
|
|
||||||
class SessionLiveConfigFromFile:
|
|
||||||
def __init__(self, fn):
|
|
||||||
self.fn = fn
|
|
||||||
self.configlist = []
|
|
||||||
for line in open(fn):
|
|
||||||
if line.strip() and not line.strip().startswith('#'):
|
|
||||||
d = {}
|
|
||||||
for part in line.split():
|
|
||||||
name, value = part.split("=")
|
|
||||||
d[name] = value
|
|
||||||
self.configlist.append(d)
|
|
||||||
|
|
||||||
def get(self, index):
|
|
||||||
return self.configlist[index]
|
|
||||||
|
|
||||||
def exists(self):
|
|
||||||
return bool(self.configlist)
|
|
||||||
|
|
||||||
|
|
||||||
class SessionLiveConfigFromURL:
|
|
||||||
def __init__(self, url):
|
|
||||||
self.configlist = []
|
|
||||||
self.url = url
|
|
||||||
|
|
||||||
def get(self, index):
|
|
||||||
try:
|
|
||||||
return self.configlist[index]
|
|
||||||
except IndexError:
|
|
||||||
assert index == len(self.configlist), index
|
|
||||||
res = requests.post(self.url)
|
|
||||||
if res.status_code != 200:
|
|
||||||
pytest.skip("creating newtmpuser failed {!r}".format(res))
|
|
||||||
d = res.json()
|
|
||||||
config = dict(addr=d["email"], mail_pw=d["password"])
|
|
||||||
self.configlist.append(config)
|
|
||||||
return config
|
|
||||||
|
|
||||||
def exists(self):
|
|
||||||
return bool(self.configlist)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def session_liveconfig(request):
|
|
||||||
liveconfig_opt = request.config.option.liveconfig
|
|
||||||
if liveconfig_opt:
|
|
||||||
if liveconfig_opt.startswith("http"):
|
|
||||||
return SessionLiveConfigFromURL(liveconfig_opt)
|
|
||||||
else:
|
|
||||||
return SessionLiveConfigFromFile(liveconfig_opt)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def data(request):
|
|
||||||
class Data:
|
|
||||||
def __init__(self):
|
|
||||||
# trying to find test data heuristically
|
|
||||||
# because we are run from a dev-setup with pytest direct,
|
|
||||||
# through tox, and then maybe also from deltachat-binding
|
|
||||||
# users like "deltabot".
|
|
||||||
self.paths = [os.path.normpath(x) for x in [
|
|
||||||
os.path.join(os.path.dirname(request.fspath.strpath), "data"),
|
|
||||||
os.path.join(os.path.dirname(__file__), "..", "..", "..", "test-data")
|
|
||||||
]]
|
|
||||||
|
|
||||||
def get_path(self, bn):
|
|
||||||
""" return path of file or None if it doesn't exist. """
|
|
||||||
for path in self.paths:
|
|
||||||
fn = os.path.join(path, *bn.split("/"))
|
|
||||||
if os.path.exists(fn):
|
|
||||||
return fn
|
|
||||||
print("WARNING: path does not exist: {!r}".format(fn))
|
|
||||||
|
|
||||||
def read_path(self, bn, mode="r"):
|
|
||||||
fn = self.get_path(bn)
|
|
||||||
if fn is not None:
|
|
||||||
with open(fn, mode) as f:
|
|
||||||
return f.read()
|
|
||||||
|
|
||||||
return Data()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def acfactory(pytestconfig, tmpdir, request, session_liveconfig, data):
|
|
||||||
|
|
||||||
class AccountMaker:
|
|
||||||
def __init__(self):
|
|
||||||
self.live_count = 0
|
|
||||||
self.offline_count = 0
|
|
||||||
self._finalizers = []
|
|
||||||
self.init_time = time.time()
|
|
||||||
self._generated_keys = ["alice", "bob", "charlie",
|
|
||||||
"dom", "elena", "fiona"]
|
|
||||||
|
|
||||||
def finalize(self):
|
|
||||||
while self._finalizers:
|
|
||||||
fin = self._finalizers.pop()
|
|
||||||
fin()
|
|
||||||
|
|
||||||
def make_account(self, path, logid, quiet=False):
|
|
||||||
ac = Account(path)
|
|
||||||
ac._evtracker = ac.add_account_plugin(FFIEventTracker(ac))
|
|
||||||
ac._configtracker = ac.add_account_plugin(ConfigureTracker())
|
|
||||||
if not quiet:
|
|
||||||
ac.add_account_plugin(FFIEventLogger(ac, 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 = self.make_account(tmpdb.strpath, logid="ac{}".format(self.offline_count))
|
|
||||||
ac._evtracker.init_time = self.init_time
|
|
||||||
ac._evtracker.set_timeout(2)
|
|
||||||
return ac
|
|
||||||
|
|
||||||
def _preconfigure_key(self, account, addr):
|
|
||||||
# Only set a key if we haven't used it yet for another account.
|
|
||||||
if self._generated_keys:
|
|
||||||
keyname = self._generated_keys.pop(0)
|
|
||||||
fname_pub = data.read_path("key/{name}-public.asc".format(name=keyname))
|
|
||||||
fname_sec = data.read_path("key/{name}-secret.asc".format(name=keyname))
|
|
||||||
if fname_pub and fname_sec:
|
|
||||||
account._preconfigure_keypair(addr, fname_pub, fname_sec)
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("WARN: could not use preconfigured keys for {!r}".format(addr))
|
|
||||||
|
|
||||||
def get_configured_offline_account(self):
|
|
||||||
ac = self.get_unconfigured_account()
|
|
||||||
|
|
||||||
# do a pseudo-configured account
|
|
||||||
addr = "addr{}@offline.org".format(self.offline_count)
|
|
||||||
ac.set_config("addr", addr)
|
|
||||||
self._preconfigure_key(ac, addr)
|
|
||||||
lib.dc_set_config(ac._dc_context, b"configured_addr", addr.encode("ascii"))
|
|
||||||
ac.set_config("mail_pw", "123")
|
|
||||||
lib.dc_set_config(ac._dc_context, b"configured_mail_pw", b"123")
|
|
||||||
lib.dc_set_config(ac._dc_context, b"configured", b"1")
|
|
||||||
return ac
|
|
||||||
|
|
||||||
def get_online_config(self, pre_generated_key=True, quiet=False):
|
|
||||||
if not session_liveconfig:
|
|
||||||
pytest.skip("specify DCC_NEW_TMP_EMAIL or --liveconfig")
|
|
||||||
configdict = session_liveconfig.get(self.live_count)
|
|
||||||
self.live_count += 1
|
|
||||||
if "e2ee_enabled" not in configdict:
|
|
||||||
configdict["e2ee_enabled"] = "1"
|
|
||||||
|
|
||||||
# Enable strict certificate checks for online accounts
|
|
||||||
configdict["imap_certificate_checks"] = str(const.DC_CERTCK_STRICT)
|
|
||||||
configdict["smtp_certificate_checks"] = str(const.DC_CERTCK_STRICT)
|
|
||||||
|
|
||||||
tmpdb = tmpdir.join("livedb%d" % self.live_count)
|
|
||||||
ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count), quiet=quiet)
|
|
||||||
if pre_generated_key:
|
|
||||||
self._preconfigure_key(ac, configdict['addr'])
|
|
||||||
ac._evtracker.init_time = self.init_time
|
|
||||||
ac._evtracker.set_timeout(30)
|
|
||||||
return ac, dict(configdict)
|
|
||||||
|
|
||||||
def get_online_configuring_account(self, mvbox=False, sentbox=False, move=False,
|
|
||||||
pre_generated_key=True, quiet=False, config={}):
|
|
||||||
ac, configdict = self.get_online_config(
|
|
||||||
pre_generated_key=pre_generated_key, quiet=quiet)
|
|
||||||
configdict.update(config)
|
|
||||||
configdict["mvbox_watch"] = str(int(mvbox))
|
|
||||||
configdict["mvbox_move"] = str(int(move))
|
|
||||||
configdict["sentbox_watch"] = str(int(sentbox))
|
|
||||||
ac.update_config(configdict)
|
|
||||||
ac.start()
|
|
||||||
return ac
|
|
||||||
|
|
||||||
def get_one_online_account(self, pre_generated_key=True, mvbox=False, move=False):
|
|
||||||
ac1 = self.get_online_configuring_account(
|
|
||||||
pre_generated_key=pre_generated_key, mvbox=mvbox, move=move)
|
|
||||||
ac1._configtracker.wait_imap_connected()
|
|
||||||
ac1._configtracker.wait_smtp_connected()
|
|
||||||
ac1._configtracker.wait_finish()
|
|
||||||
return ac1
|
|
||||||
|
|
||||||
def get_two_online_accounts(self, move=False, quiet=False):
|
|
||||||
ac1 = self.get_online_configuring_account(move=True, quiet=quiet)
|
|
||||||
ac2 = self.get_online_configuring_account(quiet=quiet)
|
|
||||||
ac1._configtracker.wait_finish()
|
|
||||||
ac2._configtracker.wait_finish()
|
|
||||||
return ac1, ac2
|
|
||||||
|
|
||||||
def clone_online_account(self, account, pre_generated_key=True):
|
|
||||||
self.live_count += 1
|
|
||||||
tmpdb = tmpdir.join("livedb%d" % self.live_count)
|
|
||||||
ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count))
|
|
||||||
if pre_generated_key:
|
|
||||||
self._preconfigure_key(ac, account.get_config("addr"))
|
|
||||||
ac._evtracker.init_time = self.init_time
|
|
||||||
ac._evtracker.set_timeout(30)
|
|
||||||
ac.update_config(dict(
|
|
||||||
addr=account.get_config("addr"),
|
|
||||||
mail_pw=account.get_config("mail_pw"),
|
|
||||||
mvbox_watch=account.get_config("mvbox_watch"),
|
|
||||||
mvbox_move=account.get_config("mvbox_move"),
|
|
||||||
sentbox_watch=account.get_config("sentbox_watch"),
|
|
||||||
))
|
|
||||||
ac.start()
|
|
||||||
return ac
|
|
||||||
|
|
||||||
def run_bot_process(self, module, ffi=True):
|
|
||||||
fn = module.__file__
|
|
||||||
|
|
||||||
bot_ac, bot_cfg = self.get_online_config()
|
|
||||||
|
|
||||||
args = [
|
|
||||||
sys.executable,
|
|
||||||
"-u",
|
|
||||||
fn,
|
|
||||||
"--email", bot_cfg["addr"],
|
|
||||||
"--password", bot_cfg["mail_pw"],
|
|
||||||
bot_ac.db_path,
|
|
||||||
]
|
|
||||||
if ffi:
|
|
||||||
args.insert(-1, "--show-ffi")
|
|
||||||
print("$", " ".join(args))
|
|
||||||
popen = subprocess.Popen(
|
|
||||||
args=args,
|
|
||||||
stdin=subprocess.DEVNULL,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.STDOUT, # combine stdout/stderr in one stream
|
|
||||||
bufsize=0, # line buffering
|
|
||||||
close_fds=True, # close all FDs other than 0/1/2
|
|
||||||
universal_newlines=True # give back text
|
|
||||||
)
|
|
||||||
bot = BotProcess(popen, bot_cfg)
|
|
||||||
self._finalizers.append(bot.kill)
|
|
||||||
return bot
|
|
||||||
|
|
||||||
am = AccountMaker()
|
|
||||||
request.addfinalizer(am.finalize)
|
|
||||||
return am
|
|
||||||
|
|
||||||
|
|
||||||
class BotProcess:
|
|
||||||
def __init__(self, popen, bot_cfg):
|
|
||||||
self.popen = popen
|
|
||||||
self.addr = bot_cfg["addr"]
|
|
||||||
|
|
||||||
# we read stdout as quickly as we can in a thread and make
|
|
||||||
# the (unicode) lines available for readers through a queue.
|
|
||||||
self.stdout_queue = queue.Queue()
|
|
||||||
self.stdout_thread = t = threading.Thread(target=self._run_stdout_thread, name="bot-stdout-thread")
|
|
||||||
t.setDaemon(1)
|
|
||||||
t.start()
|
|
||||||
|
|
||||||
def _run_stdout_thread(self):
|
|
||||||
try:
|
|
||||||
while 1:
|
|
||||||
line = self.popen.stdout.readline()
|
|
||||||
if not line:
|
|
||||||
break
|
|
||||||
line = line.strip()
|
|
||||||
self.stdout_queue.put(line)
|
|
||||||
finally:
|
|
||||||
self.stdout_queue.put(None)
|
|
||||||
|
|
||||||
def kill(self):
|
|
||||||
self.popen.kill()
|
|
||||||
|
|
||||||
def wait(self, timeout=30):
|
|
||||||
self.popen.wait(timeout=timeout)
|
|
||||||
|
|
||||||
def fnmatch_lines(self, pattern_lines):
|
|
||||||
patterns = [x.strip() for x in Source(pattern_lines.rstrip()).lines if x.strip()]
|
|
||||||
for next_pattern in patterns:
|
|
||||||
print("+++FNMATCH:", next_pattern)
|
|
||||||
ignored = []
|
|
||||||
while 1:
|
|
||||||
line = self.stdout_queue.get(timeout=15)
|
|
||||||
if line is None:
|
|
||||||
if ignored:
|
|
||||||
print("BOT stdout terminated after these lines")
|
|
||||||
for line in ignored:
|
|
||||||
print(line)
|
|
||||||
raise IOError("BOT stdout-thread terminated")
|
|
||||||
if fnmatch.fnmatch(line, next_pattern):
|
|
||||||
print("+++MATCHED:", line)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print("+++IGN:", line)
|
|
||||||
ignored.append(line)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def tmp_db_path(tmpdir):
|
|
||||||
return tmpdir.join("test.db").strpath
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def lp():
|
|
||||||
class Printer:
|
|
||||||
def sec(self, msg):
|
|
||||||
print()
|
|
||||||
print("=" * 10, msg, "=" * 10)
|
|
||||||
|
|
||||||
def step(self, msg):
|
|
||||||
print("-" * 5, "step " + msg, "-" * 5)
|
|
||||||
return Printer()
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
|
|
||||||
from queue import Queue
|
|
||||||
from threading import Event
|
|
||||||
|
|
||||||
from .hookspec import account_hookimpl
|
|
||||||
|
|
||||||
|
|
||||||
class ImexFailed(RuntimeError):
|
|
||||||
""" Exception for signalling that import/export operations failed."""
|
|
||||||
|
|
||||||
|
|
||||||
class ImexTracker:
|
|
||||||
def __init__(self):
|
|
||||||
self._imex_events = Queue()
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_process_ffi_event(self, ffi_event):
|
|
||||||
if ffi_event.name == "DC_EVENT_IMEX_PROGRESS":
|
|
||||||
self._imex_events.put(ffi_event.data1)
|
|
||||||
elif ffi_event.name == "DC_EVENT_IMEX_FILE_WRITTEN":
|
|
||||||
self._imex_events.put(ffi_event.data1)
|
|
||||||
|
|
||||||
def wait_finish(self, progress_timeout=60):
|
|
||||||
""" Return list of written files, raise ValueError if ExportFailed. """
|
|
||||||
files_written = []
|
|
||||||
while True:
|
|
||||||
ev = self._imex_events.get(timeout=progress_timeout)
|
|
||||||
if isinstance(ev, str):
|
|
||||||
files_written.append(ev)
|
|
||||||
elif ev == 0:
|
|
||||||
raise ImexFailed("export failed, exp-files: {}".format(files_written))
|
|
||||||
elif ev == 1000:
|
|
||||||
return files_written
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigureFailed(RuntimeError):
|
|
||||||
""" Exception for signalling that configuration failed."""
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigureTracker:
|
|
||||||
ConfigureFailed = ConfigureFailed
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._configure_events = Queue()
|
|
||||||
self._smtp_finished = Event()
|
|
||||||
self._imap_finished = Event()
|
|
||||||
self._ffi_events = []
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_process_ffi_event(self, ffi_event):
|
|
||||||
self._ffi_events.append(ffi_event)
|
|
||||||
if ffi_event.name == "DC_EVENT_SMTP_CONNECTED":
|
|
||||||
self._smtp_finished.set()
|
|
||||||
elif ffi_event.name == "DC_EVENT_IMAP_CONNECTED":
|
|
||||||
self._imap_finished.set()
|
|
||||||
|
|
||||||
@account_hookimpl
|
|
||||||
def ac_configure_completed(self, success):
|
|
||||||
self._configure_events.put(success)
|
|
||||||
|
|
||||||
def wait_smtp_connected(self):
|
|
||||||
""" wait until smtp is configured. """
|
|
||||||
self._smtp_finished.wait()
|
|
||||||
|
|
||||||
def wait_imap_connected(self):
|
|
||||||
""" wait until smtp is configured. """
|
|
||||||
self._imap_finished.wait()
|
|
||||||
|
|
||||||
def wait_finish(self):
|
|
||||||
""" wait until configure is completed.
|
|
||||||
|
|
||||||
Raise Exception if Configure failed
|
|
||||||
"""
|
|
||||||
if not self._configure_events.get():
|
|
||||||
content = "\n".join(map(str, self._ffi_events))
|
|
||||||
raise ConfigureFailed(content)
|
|
||||||
@@ -1,18 +1,268 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
from deltachat import Account
|
||||||
|
from deltachat import const
|
||||||
|
from deltachat.capi import lib
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addoption(
|
||||||
|
"--liveconfig", action="store", default=None,
|
||||||
|
help="a file with >=2 lines where each line "
|
||||||
|
"contains NAME=VALUE config settings for one account"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_configure(config):
|
||||||
|
cfg = config.getoption('--liveconfig')
|
||||||
|
if not cfg:
|
||||||
|
cfg = os.getenv('DCC_PY_LIVECONFIG')
|
||||||
|
if cfg:
|
||||||
|
config.option.liveconfig = cfg
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_report_header(config, startdir):
|
||||||
|
summary = []
|
||||||
|
|
||||||
|
t = tempfile.mktemp()
|
||||||
|
try:
|
||||||
|
ac = Account(t, eventlogging=False)
|
||||||
|
info = ac.get_info()
|
||||||
|
ac.shutdown()
|
||||||
|
finally:
|
||||||
|
os.remove(t)
|
||||||
|
summary.extend(['Deltachat core={} sqlite={}'.format(
|
||||||
|
info['deltachat_core_version'],
|
||||||
|
info['sqlite_version'],
|
||||||
|
)])
|
||||||
|
|
||||||
|
cfg = config.option.liveconfig
|
||||||
|
if cfg:
|
||||||
|
if "#" in cfg:
|
||||||
|
url, token = cfg.split("#", 1)
|
||||||
|
summary.append('Liveconfig provider: {}#<token ommitted>'.format(url))
|
||||||
|
else:
|
||||||
|
summary.append('Liveconfig file: {}'.format(cfg))
|
||||||
|
return summary
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def data():
|
||||||
|
class Data:
|
||||||
|
def __init__(self):
|
||||||
|
self.path = os.path.join(os.path.dirname(__file__), "data")
|
||||||
|
|
||||||
|
def get_path(self, bn):
|
||||||
|
fn = os.path.join(self.path, bn)
|
||||||
|
assert os.path.exists(fn)
|
||||||
|
return fn
|
||||||
|
return Data()
|
||||||
|
|
||||||
|
|
||||||
|
class SessionLiveConfigFromFile:
|
||||||
|
def __init__(self, fn):
|
||||||
|
self.fn = fn
|
||||||
|
self.configlist = []
|
||||||
|
for line in open(fn):
|
||||||
|
if line.strip() and not line.strip().startswith('#'):
|
||||||
|
d = {}
|
||||||
|
for part in line.split():
|
||||||
|
name, value = part.split("=")
|
||||||
|
d[name] = value
|
||||||
|
self.configlist.append(d)
|
||||||
|
|
||||||
|
def get(self, index):
|
||||||
|
return self.configlist[index]
|
||||||
|
|
||||||
|
def exists(self):
|
||||||
|
return bool(self.configlist)
|
||||||
|
|
||||||
|
|
||||||
|
class SessionLiveConfigFromURL:
|
||||||
|
def __init__(self, url, create_token):
|
||||||
|
self.configlist = []
|
||||||
|
for i in range(2):
|
||||||
|
res = requests.post(url, json={"token_create_user": int(create_token)})
|
||||||
|
if res.status_code != 200:
|
||||||
|
pytest.skip("creating newtmpuser failed {!r}".format(res))
|
||||||
|
d = res.json()
|
||||||
|
config = dict(addr=d["email"], mail_pw=d["password"])
|
||||||
|
self.configlist.append(config)
|
||||||
|
|
||||||
|
def get(self, index):
|
||||||
|
return self.configlist[index]
|
||||||
|
|
||||||
|
def exists(self):
|
||||||
|
return bool(self.configlist)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def session_liveconfig(request):
|
||||||
|
liveconfig_opt = request.config.option.liveconfig
|
||||||
|
if liveconfig_opt:
|
||||||
|
if liveconfig_opt.startswith("http"):
|
||||||
|
url, create_token = liveconfig_opt.split("#", 1)
|
||||||
|
return SessionLiveConfigFromURL(url, create_token)
|
||||||
|
else:
|
||||||
|
return SessionLiveConfigFromFile(liveconfig_opt)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
|
||||||
|
|
||||||
|
class AccountMaker:
|
||||||
|
def __init__(self):
|
||||||
|
self.live_count = 0
|
||||||
|
self.offline_count = 0
|
||||||
|
self._finalizers = []
|
||||||
|
self.init_time = time.time()
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
while self._finalizers:
|
||||||
|
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 = self.make_account(tmpdb.strpath, logid="ac{}".format(self.offline_count))
|
||||||
|
ac._evlogger.init_time = self.init_time
|
||||||
|
ac._evlogger.set_timeout(2)
|
||||||
|
return ac
|
||||||
|
|
||||||
|
def get_configured_offline_account(self):
|
||||||
|
ac = self.get_unconfigured_account()
|
||||||
|
|
||||||
|
# do a pseudo-configured account
|
||||||
|
addr = "addr{}@offline.org".format(self.offline_count)
|
||||||
|
ac.set_config("addr", addr)
|
||||||
|
lib.dc_set_config(ac._dc_context, b"configured_addr", addr.encode("ascii"))
|
||||||
|
ac.set_config("mail_pw", "123")
|
||||||
|
lib.dc_set_config(ac._dc_context, b"configured_mail_pw", b"123")
|
||||||
|
lib.dc_set_config(ac._dc_context, b"configured", b"1")
|
||||||
|
return ac
|
||||||
|
|
||||||
|
def peek_online_config(self):
|
||||||
|
if not session_liveconfig:
|
||||||
|
pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")
|
||||||
|
return session_liveconfig.get(self.live_count)
|
||||||
|
|
||||||
|
def get_online_config(self):
|
||||||
|
if not session_liveconfig:
|
||||||
|
pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")
|
||||||
|
configdict = session_liveconfig.get(self.live_count)
|
||||||
|
self.live_count += 1
|
||||||
|
if "e2ee_enabled" not in configdict:
|
||||||
|
configdict["e2ee_enabled"] = "1"
|
||||||
|
|
||||||
|
# Enable strict certificate checks for online accounts
|
||||||
|
configdict["imap_certificate_checks"] = str(const.DC_CERTCK_STRICT)
|
||||||
|
configdict["smtp_certificate_checks"] = str(const.DC_CERTCK_STRICT)
|
||||||
|
|
||||||
|
tmpdb = tmpdir.join("livedb%d" % self.live_count)
|
||||||
|
ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count))
|
||||||
|
ac._evlogger.init_time = self.init_time
|
||||||
|
ac._evlogger.set_timeout(30)
|
||||||
|
return ac, dict(configdict)
|
||||||
|
|
||||||
|
def get_online_configuring_account(self, mvbox=False, sentbox=False):
|
||||||
|
ac, configdict = self.get_online_config()
|
||||||
|
ac.configure(**configdict)
|
||||||
|
ac.start_threads(mvbox=mvbox, sentbox=sentbox)
|
||||||
|
return ac
|
||||||
|
|
||||||
|
def get_one_online_account(self):
|
||||||
|
ac1 = self.get_online_configuring_account()
|
||||||
|
wait_successful_IMAP_SMTP_connection(ac1)
|
||||||
|
wait_configuration_progress(ac1, 1000)
|
||||||
|
return ac1
|
||||||
|
|
||||||
|
def get_two_online_accounts(self):
|
||||||
|
ac1 = self.get_online_configuring_account()
|
||||||
|
ac2 = self.get_online_configuring_account()
|
||||||
|
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 = 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()
|
||||||
|
return ac
|
||||||
|
|
||||||
|
am = AccountMaker()
|
||||||
|
request.addfinalizer(am.finalize)
|
||||||
|
return am
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def tmp_db_path(tmpdir):
|
||||||
|
return tmpdir.join("test.db").strpath
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def lp():
|
||||||
|
class Printer:
|
||||||
|
def sec(self, msg):
|
||||||
|
print()
|
||||||
|
print("=" * 10, msg, "=" * 10)
|
||||||
|
|
||||||
|
def step(self, msg):
|
||||||
|
print("-" * 5, "step " + msg, "-" * 5)
|
||||||
|
return Printer()
|
||||||
|
|
||||||
|
|
||||||
def wait_configuration_progress(account, min_target, max_target=1001):
|
def wait_configuration_progress(account, min_target, max_target=1001):
|
||||||
min_target = min(min_target, max_target)
|
min_target = min(min_target, max_target)
|
||||||
while 1:
|
while 1:
|
||||||
event = account._evtracker.get_matching("DC_EVENT_CONFIGURE_PROGRESS")
|
evt_name, data1, data2 = \
|
||||||
if event.data1 >= min_target and event.data1 <= max_target:
|
account._evlogger.get_matching("DC_EVENT_CONFIGURE_PROGRESS")
|
||||||
|
if data1 >= min_target and data1 <= max_target:
|
||||||
print("** CONFIG PROGRESS {}".format(min_target), account)
|
print("** CONFIG PROGRESS {}".format(min_target), account)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def wait_securejoin_inviter_progress(account, target):
|
def wait_securejoin_inviter_progress(account, target):
|
||||||
while 1:
|
while 1:
|
||||||
event = account._evtracker.get_matching("DC_EVENT_SECUREJOIN_INVITER_PROGRESS")
|
evt_name, data1, data2 = \
|
||||||
if event.data2 >= target:
|
account._evlogger.get_matching("DC_EVENT_SECUREJOIN_INVITER_PROGRESS")
|
||||||
|
if data2 >= target:
|
||||||
print("** SECUREJOINT-INVITER PROGRESS {}".format(target), account)
|
print("** SECUREJOINT-INVITER PROGRESS {}".format(target), account)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def wait_successful_IMAP_SMTP_connection(account):
|
||||||
|
imap_ok = smtp_ok = False
|
||||||
|
while not imap_ok or not smtp_ok:
|
||||||
|
evt_name, data1, data2 = \
|
||||||
|
account._evlogger.get_matching("DC_EVENT_(IMAP|SMTP)_CONNECTED")
|
||||||
|
if evt_name == "DC_EVENT_IMAP_CONNECTED":
|
||||||
|
imap_ok = True
|
||||||
|
print("** IMAP OK", account)
|
||||||
|
if evt_name == "DC_EVENT_SMTP_CONNECTED":
|
||||||
|
smtp_ok = True
|
||||||
|
print("** SMTP OK", account)
|
||||||
|
print("** IMAP and SMTP logins successful", account)
|
||||||
|
|
||||||
|
|
||||||
|
def wait_msgs_changed(account, chat_id, msg_id=None):
|
||||||
|
ev = account._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||||
|
assert ev[1] == chat_id
|
||||||
|
if msg_id is not None:
|
||||||
|
assert ev[2] == msg_id
|
||||||
|
return ev[2]
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.9 KiB |
@@ -1 +0,0 @@
|
|||||||
../../../test-data/key
|
|
||||||