Compare commits

...

97 Commits

Author SHA1 Message Date
holger krekel
4128aae331 cargo fmt only 2019-07-22 17:22:44 +02:00
holger krekel
c914a0339b remove indentation 2019-07-22 17:19:56 +02:00
holger krekel
a1f4849188 serialize OK_TO_CONTINUE checks (preparing for deintendation) 2019-07-22 17:18:29 +02:00
holger krekel
e5a9761233 cargo fmt (and nothing else!) 2019-07-22 17:12:09 +02:00
holger krekel
ff15754c1f remove double curly brances 2019-07-22 17:11:40 +02:00
holger krekel
de2b0059c9 systematically substitute "goto cleanup" logic without changing indentation 2019-07-22 17:08:04 +02:00
holger krekel
6f79800824 fix last two warnings 2019-07-22 11:45:41 +02:00
holger krekel
7a19963879 properly fix the QueryReturnedNoRows warning and rustfmt 2019-07-22 11:18:30 +02:00
holger krekel
cd7630360f fix fmt 2019-07-22 02:33:08 +02:00
holger krekel
4a633169e1 Merge branch 'master' into flub-nowarn 2019-07-22 01:40:56 +02:00
Floris Bruynooghe
ea8d6e8ff0 Write a deltachat.pc file at build time
This is writes pkg-config/deltachat.pc file in the target directory,
using the PREFIX environment variable at build time.  If this is
undefined at build time /usr/local is used.
2019-07-22 01:16:34 +02:00
holger krekel
065124b93b Merge branch 'master' into flub-param-names 2019-07-22 01:11:47 +02:00
holger krekel
86d290832b Merge branch 'master' into flub-py-glue-fixes 2019-07-22 01:09:58 +02:00
Alexander Krotov
56f8717a40 Show AutocryptSetupMessage independently of show-emails settings
Fixes #161
2019-07-22 00:45:17 +02:00
Alexander Krotov
4a0b2e68c8 Add DC_CMD_* constants 2019-07-22 00:45:17 +02:00
holger krekel
2576b78126 Merge branch 'master' into flub-py-glue-fixes 2019-07-22 00:39:47 +02:00
holger krekel
6a956b6008 Merge branch 'master' into flub-param-names 2019-07-22 00:36:52 +02:00
Alexander Krotov
33575e7aa3 dc_get_abs_path cleanup 2019-07-21 23:40:29 +02:00
holger krekel
8089559958 Squashed commit of the following:
commit 6bc5d1b90e
Author: holger krekel <holger@merlinux.eu>
Date:   Sun Jul 21 22:56:37 2019 +0200

    fix fmt

commit 197d94ad9d
Merge: 7ce337c 686678c
Author: holger krekel <holger@merlinux.eu>
Date:   Sun Jul 21 22:51:16 2019 +0200

    Merge remote-tracking branch 'origin/master' into eventlogging

commit 7ce337c6d0
Author: holger krekel <holger@merlinux.eu>
Date:   Sun Jul 21 22:44:27 2019 +0200

    left-over error logging

commit 10148d2e43
Author: holger krekel <holger@merlinux.eu>
Date:   Sun Jul 21 22:03:17 2019 +0200

    ignore non-utf8 parts of header fields (add comment why it shouldn't happen)
    don't throw error if no sql rows are returned

commit 69dc237ee3
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Sun Jul 21 12:56:04 2019 +0200

    fix(receive_imf): remove recursive sql call

commit df5464ea80
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Sat Jul 20 17:05:24 2019 +0200

    fix: blocked is an optional value

commit e4bf9956a5
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Sat Jul 20 16:50:56 2019 +0200

    fix(msg): handle optional in_reply_to

commit d353d9d9d8
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Sat Jul 20 16:17:25 2019 +0200

    fix(chat): remove recursive sql usage

commit 1ad45ed4d6
Author: holger krekel <holger@merlinux.eu>
Date:   Sat Jul 20 15:14:11 2019 +0200

    fix rust fmt

commit 496e980a17
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Sat Jul 20 14:34:20 2019 +0200

    use forked rusqlite

commit fa09e46ed9
Author: holger krekel <holger@merlinux.eu>
Date:   Sat Jul 20 12:37:51 2019 +0200

    another pace where we might (and in my case did) get invalid utf8

commit d6de420b9a
Author: holger krekel <holger@merlinux.eu>
Date:   Sat Jul 20 12:30:48 2019 +0200

    fix some string issues, introduce to_string_lossy such that to_string() continues to panic on non-utf8

commit 38eb708db8
Author: holger krekel <holger@merlinux.eu>
Date:   Sat Jul 20 01:17:53 2019 +0200

    for now make to_string() less strict as we often don't want to crash the whole app just because some non-proper utf8 came in (through a message we can't neccesarily congtrol)

commit 7a59da5f8f
Author: holger krekel <holger@merlinux.eu>
Date:   Fri Jul 19 22:48:39 2019 +0200

    fix linting

commit f13a1d4a2f
Author: holger krekel <holger@merlinux.eu>
Date:   Fri Jul 19 22:46:58 2019 +0200

    fix some test flakyness

commit 7b3a450918
Author: holger krekel <holger@merlinux.eu>
Date:   Fri Jul 19 22:35:07 2019 +0200

    - fix saved_mime test which broke to improper conversion of
      imf_raw_not_terminated
    - some cargo.toml updates no clue where they come from
    - log Message-ID for received messages

commit 169923b102
Author: holger krekel <holger@merlinux.eu>
Date:   Fri Jul 19 12:31:22 2019 +0200

    formatting

commit 42688a0622
Author: holger krekel <holger@merlinux.eu>
Date:   Fri Jul 19 12:24:56 2019 +0200

    remove some print statements

commit 35f3c0edd1
Merge: e7a2362 f58b1d6
Author: holger krekel <holger@merlinux.eu>
Date:   Fri Jul 19 10:25:21 2019 +0200

    Merge branch 'master' into eventlogging

commit e7a236264a
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Thu Jul 18 23:20:20 2019 +0200

    print invalid strings

commit aaa5b820d9
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Thu Jul 18 23:12:35 2019 +0200

    cleanup

commit e7f0745010
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Thu Jul 18 23:03:57 2019 +0200

    reduce direc usage of CString

commit c68e7ae14e
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Thu Jul 18 22:47:47 2019 +0200

    audit use of to_cstring and fix ub

commit 618087e5a7
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Thu Jul 18 21:38:52 2019 +0200

    fix(imap): body ptr lifetime

commit 245abb8384
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Thu Jul 18 19:44:10 2019 +0200

    remove debug

commit a3e1042001
Author: dignifiedquire <dignifiedquire@users.noreply.github.com>
Date:   Thu Jul 18 18:30:54 2019 +0200

    fix some things, add more debugging statements

commit 7b7ce9348f
Author: holger krekel <holger@merlinux.eu>
Date:   Thu Jul 18 15:11:57 2019 +0200

    fix python lint issues

commit 7a4808ba0d
Author: holger krekel <holger@merlinux.eu>
Date:   Thu Jul 18 14:35:54 2019 +0200

    cargofmt

commit 8f240f7153
Author: holger krekel <holger@merlinux.eu>
Date:   Thu Jul 18 14:03:57 2019 +0200

    (dig,hpk) pull out job collection from sql query/lock logic

commit 7d0b5d8abb
Author: holger krekel <holger@merlinux.eu>
Date:   Thu Jul 18 12:52:02 2019 +0200

    remove print statements and fix a crash

commit ee317cb1b5
Author: holger krekel <holger@merlinux.eu>
Date:   Thu Jul 18 11:38:10 2019 +0200

    fix some merge issues

commit 7b736fe635
Author: holger krekel <holger@merlinux.eu>
Date:   Thu Jul 18 11:16:38 2019 +0200

    (dig,hpk) add test and fix for wrong dbs

commit c7db15352a
Merge: 0b37167 0c5015d
Author: holger krekel <holger@merlinux.eu>
Date:   Thu Jul 18 09:59:44 2019 +0200

    Merge branch 'master' into eventlogging

commit 0b37167be8
Author: holger krekel <holger@merlinux.eu>
Date:   Thu Jul 18 00:06:05 2019 +0200

    address @dignifiedquire comments

commit 5cac4b5076
Author: holger krekel <holger@merlinux.eu>
Date:   Wed Jul 17 12:47:22 2019 +0200

    remove spurious print

commit 475a41beb3
Author: holger krekel <holger@merlinux.eu>
Date:   Wed Jul 17 12:31:12 2019 +0200

    address @dignifiedquire rustyness comment and fix changelog

commit ad4be80b4e
Author: holger krekel <holger@merlinux.eu>
Date:   Wed Jul 17 10:25:25 2019 +0200

    make smtp/imap connect() return bool instead of c-int

commit 8737c1d142
Author: holger krekel <holger@merlinux.eu>
Date:   Wed Jul 17 09:26:33 2019 +0200

    cleanup some parts, add comments

commit 964fe466cc
Author: holger krekel <holger@merlinux.eu>
Date:   Tue Jul 16 20:05:41 2019 +0200

    wip-commit which passes all tests with proper finalization

commit 43936e7db7
Author: holger krekel <holger@merlinux.eu>
Date:   Tue Jul 16 16:17:42 2019 +0200

    snapshot of my current debugging state

commit 0e80ce9c39
Author: holger krekel <holger@merlinux.eu>
Date:   Tue Jul 16 12:57:19 2019 +0200

    more aggressively skip perform API when threads are closing

commit c652bae68a
Author: holger krekel <holger@merlinux.eu>
Date:   Tue Jul 16 12:06:05 2019 +0200

    intermediate wip commit

commit bc904a495d
Author: holger krekel <holger@merlinux.eu>
Date:   Tue Jul 16 11:18:56 2019 +0200

    add some logging, and a more precise teardown for online python tests

commit 8d99444c6a
Author: holger krekel <holger@merlinux.eu>
Date:   Tue Jul 16 00:22:12 2019 +0200

    fix std

commit 9dab53e0af
Author: holger krekel <holger@merlinux.eu>
Date:   Tue Jul 16 00:20:54 2019 +0200

    rustfmt

commit 360089ac74
Author: holger krekel <holger@merlinux.eu>
Date:   Tue Jul 16 00:03:49 2019 +0200

    remove some debugging

commit e892c5cf4d
Author: holger krekel <holger@merlinux.eu>
Date:   Mon Jul 15 23:31:30 2019 +0200

    fix test for events

commit 9ad4c9a6fe
Author: holger krekel <holger@merlinux.eu>
Date:   Mon Jul 15 22:51:57 2019 +0200

    wip try test that we see INFO events from the core
2019-07-21 23:31:14 +02:00
Alexander Krotov
686678c96c Spellcheck 2019-07-21 21:40:19 +02:00
Alexander Krotov
c116d6f73f Use Rust for instead of C while in dc_array test 2019-07-21 21:27:49 +02:00
Alexander Krotov
a7c8ebc089 Replace DC_ARRAY_MAGIC #define with a constant 2019-07-21 21:26:27 +02:00
Floris Bruynooghe
7774052911 Use DC_PARAM_* constants everywhere
Also document each type they store.  This makes existing code a little
more readable and gives some hints towards refactoring this.
2019-07-21 20:25:52 +02:00
Floris Bruynooghe
68888f6d1f Also silence warnings in test code
We can be a bit more liberal with .unwrap() here.
2019-07-21 12:03:04 +02:00
Alexander Krotov
3dfd623db7 Use constants instead of hardcoded values in dc_mimefactory.rs 2019-07-21 00:37:58 +02:00
Floris Bruynooghe
31d2bc7401 Silence warnings from ignored Result values
For a few of the locations where error handling is done correctly this
does the right thing.  For most other places it gracefully ignores any
issues which is what the original code did as well.  Errors are
already logged by the called functions in those cases.
2019-07-21 00:32:33 +02:00
Floris Bruynooghe
5ee8f8cb59 Several fixes to the intergration tests
- Pass extra_link_args when using an installed libdeltachat
- Allow setting the liveconfig by envvar
- Show lifeconfig path in the pytest summary line
- Pass required envvars through tox
- Fix broken liveconfig passing in run-integration-test.sh
2019-07-20 23:28:23 +02:00
Floris Bruynooghe
d1825956b2 Merge pull request #198 from link2xt/top_evil_rs_shebang
src/top_evil_rs.py: fix shebang to always use Python 3 and make it executable
2019-07-20 19:17:44 +02:00
Alexander Krotov
30ca377586 src/top_evil_rs.py: fix shebang to always use Python 3 and make it executable
PEP-0394 (https://www.python.org/dev/peps/pep-0394/) recommends to use more specific shebangs.
For example, Debian allows /usr/bin/python to be configured to python2 via `update-alternatives python`, but the script does not work with Python 2.
2019-07-20 19:43:05 +03:00
holger krekel
f58b1d66c2 add a little script to compute rust-evilness 2019-07-19 10:15:04 +02:00
dignifiedquire
0c5015d92b chore: release v1.0.0-alpha.3 2019-07-18 00:34:46 +02:00
dignifiedquire
ab2d2e7583 chore: prepare for using cargo-release 2019-07-18 00:33:07 +02:00
dignifiedquire
c11ac46dce chore: add Cargo.lock
Closes #145
2019-07-18 00:29:01 +02:00
Friedel Ziegelmayer
8a0fc609e6 The big sqlite refactor
* refactor: safe sql access

* Clean up the worst rebase mistakes

* Some more progress on the rebase fallout and this branch

* upgrade and compile again

* cleanup from rebase

* example of how to prepare now

* rebase fixes

* add sql.query_map

* less preparation

* more improvements in sql code

* fix string truncation

* more prepare conversions

* most prep done

* fix tests

* fix ffi

* fix last prepares

* fix segfaults and some queries

* use r2d2 pool

* fix dc_job sql call, to reduce contention

* try newer rust

* No more vararg printing (drop dc_log_)

* ignore expected errors

* fix: uses exists instead of execute where needed

* fix: get_contacts logic was broken

* fix: contact creation

* test on 32bit linux

* ci: try running 32bit without cross

* undo 32bit tests

* refactor: rename dc_sqlite3 to sql

* fix: safer string conversions

* more string fixes

* try fixing appveyor build to 64bit

* chore(ci): hardcode target

* chore(ci): appveyor

* some cleanup work

* try fix darwin

* fix and improve sql escaping

* fix various bugs

* fix chat deletion

* refactor: cleanup config values and move to their own file

* refactor: move more methods onto the sql struct

* dont panic on failed state loading

* first round of cr

* one more cr fix

* stop using strange defaults

* remove unused escapes
2019-07-18 00:24:45 +02:00
holger krekel
3e3403d3d7 try using setuptools_scm for automatic versioning based on py-* tags (#187)
* try using setuptools_scm for automatic versioning based on py-* tags

* circument problem with pip-wheel isolation and setuptoosl_scm

* always provide version, address @flub comment
2019-07-14 09:58:51 +02:00
holger krekel
46c64b2511 Merge pull request #189 from deltachat/fixosx
try fix darwin
2019-07-14 09:57:52 +02:00
holger krekel
aa82644392 fix py27 2019-07-14 09:16:51 +02:00
holga
f00b617c23 try fix darwin 2019-07-13 18:44:44 +02:00
holger krekel
04ee9dde2c Merge pull request #181 from deltachat/improve_receive_imf1
improve dc_receive_imf and friends a little (part 1)
2019-07-10 19:54:22 +02:00
holger krekel
cae0d666bd Merge pull request #184 from deltachat/fix_py
fix home page of bindings
2019-07-10 18:16:32 +02:00
holger krekel
2691028422 fix home page of bindings 2019-07-10 17:41:16 +02:00
Floris Bruynooghe
205493f89d Merge pull request #183 from deltachat/prep0600
prepare 0.600.0 release
2019-07-10 16:38:55 +02:00
holger krekel
3bca349194 prepare 0.600.0 release 2019-07-10 16:10:23 +02:00
Floris Bruynooghe
deb160cce9 Merge pull request #182 from deltachat/flub-dc-chat-rename2
Rename dc_chat_t to Chat
2019-07-10 15:57:37 +02:00
holger krekel
669476afd3 fix comment 2019-07-10 12:06:59 +02:00
holger krekel
b810b5a8f8 other cast as per @dignifiedquire comment -- also convert some logging along the way 2019-07-10 11:54:29 +02:00
holger krekel
6d17de05b2 also convert lookup_field function to use a &str param isntead of char* 2019-07-10 11:43:16 +02:00
holger krekel
e3fb0a23c6 get rid of c version of dc_mimeparser_lookup_field completely 2019-07-10 11:32:50 +02:00
Floris Bruynooghe
4c646dc1e0 Rename dc_chat_t to Chat
This clears the way to start working on making the functions safe.
But small PRs are good PRs so let's get this rename out of the way and
have future PRs less noisy.

Also stop making this #[repr(C)] and start making fields that are not
used private. Lastly clean up some comments by moving them or
deleting them, so they make sense again after the translation.
2019-07-09 21:46:31 +02:00
holger krekel
d67dd9cc33 convert another carray 2019-07-09 21:04:23 +02:00
holger krekel
b93f5aa0b6 minimize casting 2019-07-09 20:55:29 +02:00
holger krekel
6c4b9e79c7 convert created_db_entries into a Rust vector instead of using low-level carray 2019-07-09 19:31:51 +02:00
holger krekel
95437d726e some streamlinings, after advise by @dignifedquire 2019-07-09 17:39:05 +02:00
Floris Bruynooghe
816fa1df9b test: ignore expensive tests by default
This makes interactively running the tests a much more pleasant
experience rather than something one dreads.  These tests will still
be run on the CI.  To run these manually run:

cargo test [TESTNAME] -- --ignored
2019-07-09 13:36:16 +02:00
björn petersen
11eb86c77f Merge pull request #177 from deltachat/fix176
fix #176 by transforming unsafe dc_log_info calls -- i think the unsafe
2019-07-06 22:50:47 +02:00
holger krekel
c2ca30cc16 fix #176 by transforming unsafe dc_log_info calls -- i think the unsafe
referencing via &jobthread.name was the culprit but not sure ;)
2019-07-06 22:26:26 +02:00
dignifiedquire
f336166867 fix(deps): disable default features 2019-07-06 17:35:19 +02:00
holger krekel
692b779896 Merge pull request #173 from deltachat/actests
test and fix Autocrypt Setup Message flow
2019-07-06 12:32:56 +02:00
holger krekel
09a675a9cf Merge pull request #175 from deltachat/fix-ringbuf
feat: add better feature configurations
2019-07-06 12:03:53 +02:00
dignifiedquire
fd5cba7242 fixup 2019-07-06 11:32:15 +02:00
dignifiedquire
5c53bb5ed7 fixup 2019-07-06 11:31:46 +02:00
dignifiedquire
184b3c8e91 undo version bump 2019-07-06 11:30:45 +02:00
dignifiedquire
286d1a99aa fixup 2019-07-06 11:29:13 +02:00
dignifiedquire
4fe99b21c9 feat: add better feature configurations 2019-07-06 11:25:01 +02:00
holger krekel
2aa81a7a9a speed up test teardown by now waiting for threads to finish ...
and remove a debug statement
2019-07-05 23:12:11 +02:00
Hocuri
f671b25cbc refactor: make dc_jobthread_t safe 2019-07-05 21:17:57 +02:00
holger krekel
3d7be47adf fix rust format 2019-07-05 20:05:08 +02:00
holger krekel
27c8bb64c8 remove some debuging 2019-07-05 19:26:40 +02:00
dignifiedquire
4c95664992 fix and improve key import 2019-07-05 19:12:08 +02:00
holger krekel
f7d13fd12f comment on when it passes 2019-07-05 16:32:26 +02:00
holger krekel
35248296af some refinements, bump versions already 2019-07-05 16:23:33 +02:00
holger krekel
dfa2fcda73 use safe logging, and add some info on the processed key (this is WIP and needs to be removed) 2019-07-05 13:04:26 +02:00
holger krekel
699b2d48e6 add a failing test for Autocrypt Key Transfer failing to extract a private key from an incoming ASM message 2019-07-05 12:46:14 +02:00
holger krekel
12b2a706f0 address on flub comment, start on autocryt transfer test 2019-07-05 01:02:22 +02:00
holger krekel
c7c86f1b03 Merge pull request #169 from deltachat/imex_tests
Imex tests and fix for export-blobs
2019-07-04 15:50:24 +02:00
holger krekel
02872c143a Merge pull request #167 from deltachat/Simon-Laux-patch-2
Update Cargo.toml - add path for simple example
2019-07-04 11:54:35 +02:00
holger krekel
1a0ebba024 fix create_chat_by_message to work according to docs 2019-07-04 11:40:31 +02:00
holger krekel
6e13e177f7 fixed logging, removed one more "old" style logging 2019-07-04 11:16:15 +02:00
holger krekel
a2eb215fdf remove last sync method for context callback -- simplifying event handling 2019-07-03 23:43:14 +02:00
holger krekel
a2245bdf4e remove unusued sync methods 2019-07-03 23:05:20 +02:00
holger krekel
abdb02c361 add a test for testing that export works when an account has active threads 2019-07-03 21:31:20 +02:00
holger krekel
92d39642e6 bump rust-version 2019-07-03 21:13:59 +02:00
holger krekel
799ba8a5db fix rust-formatting 2019-07-03 20:38:17 +02:00
holger krekel
8367a03b22 fix some lint issues, and run it during circle-CI 2019-07-03 20:32:01 +02:00
holger krekel
4378608617 convert some log infos and guard bindings against some misuse 2019-07-03 20:17:35 +02:00
holger krekel
adcb9d6069 fixed: make export/import work with blob-files again
also add some more logging.
2019-07-03 20:02:05 +02:00
holger krekel
191d11e719 export and import full DC database state 2019-07-03 18:59:56 +02:00
Simon Laux
464d0f4831 Update Cargo.toml 2019-07-02 14:11:58 +02:00
Floris Bruynooghe
d0c7f63809 Merge pull request #165 from deltachat/fixtests1
get liveconfig tests working, add imap/smtp connected events and tests
2019-07-02 13:25:20 +02:00
holger krekel
e597aade10 fix comment 2019-07-02 10:05:54 +02:00
holger krekel
08bade2c7c fix fmt 2019-07-02 08:36:15 +02:00
holger krekel
2c26f4f2ab convert the last two unsafe event emissions in smtp.rs 2019-07-02 01:45:48 +02:00
holger krekel
7a053b9f93 add SMTP_CONNECTED and IMAP_CONNECTED events 2019-07-01 22:27:44 +02:00
holger krekel
183a49eca0 remove superflous debug attempts 2019-07-01 20:11:35 +02:00
holger krekel
384822b5eb replace unsafe use of dc logging with info! macro as per advise from @flub 2019-07-01 18:57:37 +02:00
holger krekel
06e16c81f9 Wip 2019-07-01 18:06:16 +02:00
holger krekel
d1593f0258 fix run-integration-test.sh to run without arguments 2019-07-01 15:57:00 +02:00
81 changed files with 12574 additions and 11306 deletions

View File

@@ -127,26 +127,26 @@ jobs:
machine: True
steps:
- checkout
# - run: docker pull deltachat/doxygen
# - run: docker pull deltachat/doxygen
- run: docker pull deltachat/coredeps
- run:
name: build docs, run tests and build wheels
- run:
name: build docs, run tests and build wheels
command: ci_scripts/ci_run.sh
environment:
TESTS: 1
DOCS: 1
- run:
name: copying docs and wheels to workspace
- run:
name: copying docs and wheels to workspace
command: |
mkdir -p workspace/python
# cp -av docs workspace/c-docs
cp -av python/.docker-tox/wheelhouse workspace/
cp -av python/doc/_build/ workspace/py-docs
- persist_to_workspace:
root: workspace
paths:
- persist_to_workspace:
root: workspace
paths:
# - c-docs
- py-docs
- wheelhouse
@@ -157,7 +157,7 @@ jobs:
- checkout
- attach_workspace:
at: workspace
- run: ls -laR workspace
- run: ls -laR workspace
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse
@@ -175,12 +175,17 @@ workflows:
requires:
- cargo_fetch
# Linux Desktop
# Linux Desktop 64bit
- test_x86_64-unknown-linux-gnu:
requires:
- cargo_fetch
# Linux Desktop
# Linux Desktop 32bit
# - test_i686-unknown-linux-gnu:
# requires:
# - cargo_fetch
# Android 64bit
# - test_aarch64-linux-android:
# requires:
# - cargo_fetch

2
.gitattributes vendored
View File

@@ -2,7 +2,7 @@
# ensures this even if the user has not set core.autocrlf.
* text=auto
# binary files should be detected by git, however, to be sure, you can add them here explictly
# binary files should be detected by git, however, to be sure, you can add them here explicitly
*.png binary
*.jpg binary
*.gif binary

3
.gitignore vendored
View File

@@ -1,6 +1,5 @@
/target
**/*.rs.bk
Cargo.lock
# ignore vi temporaries
*~
@@ -17,3 +16,5 @@ python/.tox
*.egg-info
__pycache__
python/src/deltachat/capi*.so
python/liveconfig*

2954
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat"
version = "1.0.0-alpha.1"
version = "1.0.0-alpha.3"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
edition = "2018"
license = "MPL"
@@ -11,12 +11,11 @@ pkg-config = "0.3"
[dependencies]
libc = "0.2.51"
pgp = "0.2"
pgp = { version = "0.2", default-features = false }
hex = "0.3.2"
sha2 = "0.8.0"
rand = "0.6.5"
smallvec = "0.6.9"
libsqlite3-sys = { version = "0.14.0", features = ["bundled", "min_sqlite_version_3_7_16"] }
reqwest = "0.9.15"
num-derive = "0.2.5"
num-traits = "0.2.6"
@@ -36,6 +35,14 @@ failure_derive = "0.1.5"
rustyline = "4.1.0"
lazy_static = "1.3.0"
regex = "1.1.6"
rusqlite = { version = "0.19", features = ["bundled"] }
addr = "0.2.0"
r2d2_sqlite = "0.11.0"
r2d2 = "0.8.5"
strum = "0.15.0"
strum_macros = "0.15.0"
thread-local-object = "0.1.0"
backtrace = "0.3.33"
[dev-dependencies]
tempfile = "3.0"
@@ -47,8 +54,13 @@ members = [
"deltachat-ffi"
]
[patch.crates-io]
rusqlite = { git = "http://github.com/dignifiedquire/rusqlite", branch = "fix/text", features = ["bundled"] }
[[example]]
name = "simple"
path = "examples/simple.rs"
[[example]]
name = "repl"
@@ -56,6 +68,7 @@ path = "examples/repl/main.rs"
[features]
default = ["nightly"]
default = ["nightly", "ringbuf"]
vendored = ["native-tls/vendored", "reqwest/default-tls-vendored"]
nightly = ["pgp/nightly"]
ringbuf = ["pgp/ringbuf"]

View File

@@ -84,6 +84,12 @@ $ cargo test --all
$ cargo build -p deltachat_ffi --release
```
## Features
- `vendored`: When using Openssl for TLS, this bundles a vendored version.
- `nightly`: Enable nightly only performance and security related features.
- `ringbuf`: Enable the use of [`slice_deque`](https://github.com/gnzlbg/slice_deque) in pgp.
[circle-shield]: https://img.shields.io/circleci/project/github/deltachat/deltachat-core-rust/master.svg?style=flat-square
[circle]: https://circleci.com/gh/deltachat/deltachat-core-rust/
[appveyor-shield]: https://ci.appveyor.com/api/projects/status/lqpegel3ld4ipxj8/branch/master?style=flat-square

View File

@@ -1,11 +1,10 @@
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
TARGET: x86_64-pc-windows-msvc
install:
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init -yv --default-host %target% --default-toolchain none
- rustup-init -yv --default-toolchain nightly-2019-07-10
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
- rustc -vV
- cargo -vV
@@ -14,7 +13,7 @@ install:
build: false
test_script:
- cargo test --release
- cargo test --release
cache:
- target

View File

@@ -33,6 +33,7 @@ else
export CARGO_SUBCMD="test"
export OPT="${OPT} "
export OPT_RELEASE="${OPT_RELEASE} "
export OPT_RELEASE_IGNORED="${OPT_RELEASE} -- --ignored"
fi
# Run all the test configurations:

View File

@@ -37,7 +37,7 @@ if [ -n "$TESTS" ]; then
export PYTHONDONTWRITEBYTECODE=1
# run tox
tox --workdir "$TOXWORKDIR" -e py27,py35,py36,py37,auditwheels
tox --workdir "$TOXWORKDIR" -e lint,py27,py35,py36,py37,auditwheels
popd
fi

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat_ffi"
version = "1.0.0-alpha.1"
version = "1.0.0-alpha.3"
description = "Deltachat FFI"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
edition = "2018"
@@ -15,9 +15,13 @@ name = "deltachat"
crate-type = ["cdylib", "staticlib"]
[dependencies]
deltachat = { path = "../" }
deltachat = { path = "../", default-features = false }
libc = "0.2"
human-panic = "1.0.1"
[features]
default = ["deltachat/vendored"]
default = ["vendored", "nightly", "ringbuf"]
vendored = ["deltachat/vendored"]
nightly = ["deltachat/nightly"]
ringbuf = ["deltachat/ringbuf"]

33
deltachat-ffi/build.rs Normal file
View File

@@ -0,0 +1,33 @@
use std::io::Write;
use std::path::PathBuf;
use std::{env, fs};
fn main() {
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let target_path = out_path.join("../../..");
let target_triple = env::var("TARGET").unwrap();
// macOS or iOS, inherited from rpgp
let libs_priv = if target_triple.contains("apple") || target_triple.contains("darwin") {
// needed for OsRng
"-framework Security -framework Foundation"
} else {
""
};
let pkg_config = format!(
include_str!("deltachat.pc.in"),
name = "deltachat",
description = env::var("CARGO_PKG_DESCRIPTION").unwrap(),
url = env::var("CARGO_PKG_HOMEPAGE").unwrap_or("".to_string()),
version = env::var("CARGO_PKG_VERSION").unwrap(),
libs_priv = libs_priv,
prefix = env::var("PREFIX").unwrap_or("/usr/local".to_string()),
);
fs::create_dir_all(target_path.join("pkgconfig")).unwrap();
fs::File::create(target_path.join("pkgconfig").join("deltachat.pc"))
.unwrap()
.write_all(&pkg_config.as_bytes())
.unwrap();
}

View File

@@ -503,8 +503,8 @@ int dc_chat_is_sending_locations (const dc_chat_t*);
#define DC_STATE_OUT_MDN_RCVD 28
#define DC_MAX_GET_TEXT_LEN 30000 // approx. max. lenght returned by dc_msg_get_text()
#define DC_MAX_GET_INFO_LEN 100000 // approx. max. lenght returned by dc_get_msg_info()
#define DC_MAX_GET_TEXT_LEN 30000 // approx. max. length returned by dc_msg_get_text()
#define DC_MAX_GET_INFO_LEN 100000 // approx. max. length returned by dc_get_msg_info()
dc_msg_t* dc_msg_new (dc_context_t*, int viewtype);
@@ -667,7 +667,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t*);
* A voice message that was directly recorded by the user.
* For all other audio messages, the type #DC_MSG_AUDIO should be used.
* File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
* and retieved via dc_msg_get_file(), dc_msg_get_duration()
* and retrieved via dc_msg_get_file(), dc_msg_get_duration()
*/
#define DC_MSG_VOICE 41
@@ -768,9 +768,9 @@ int64_t dc_lot_get_timestamp (const dc_lot_t*);
* @}
*/
#define DC_LP_AUTH_FLAGS (DC_LP_AUTH_OAUTH2|DC_LP_AUTH_NORMAL) // if none of these flags are set, the default is choosen
#define DC_LP_IMAP_SOCKET_FLAGS (DC_LP_IMAP_SOCKET_STARTTLS|DC_LP_IMAP_SOCKET_SSL|DC_LP_IMAP_SOCKET_PLAIN) // if none of these flags are set, the default is choosen
#define DC_LP_SMTP_SOCKET_FLAGS (DC_LP_SMTP_SOCKET_STARTTLS|DC_LP_SMTP_SOCKET_SSL|DC_LP_SMTP_SOCKET_PLAIN) // if none of these flags are set, the default is choosen
#define DC_LP_AUTH_FLAGS (DC_LP_AUTH_OAUTH2|DC_LP_AUTH_NORMAL) // if none of these flags are set, the default is chosen
#define DC_LP_IMAP_SOCKET_FLAGS (DC_LP_IMAP_SOCKET_STARTTLS|DC_LP_IMAP_SOCKET_SSL|DC_LP_IMAP_SOCKET_PLAIN) // if none of these flags are set, the default is chosen
#define DC_LP_SMTP_SOCKET_FLAGS (DC_LP_SMTP_SOCKET_STARTTLS|DC_LP_SMTP_SOCKET_SSL|DC_LP_SMTP_SOCKET_PLAIN) // if none of these flags are set, the default is chosen
@@ -851,7 +851,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t*);
* The library-user should report an error to the end-user.
* Passed to the callback given to dc_context_new().
*
* As most things are asynchrounous, things may go wrong at any time and the user
* As most things are asynchronous, things may go wrong at any time and the user
* should not be disturbed by a dialog or so. Instead, use a bubble or so.
*
* However, for ongoing processes (eg. dc_configure())
@@ -882,7 +882,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t*);
*
* Moreover, if the UI detects that the device is offline,
* it is probably more useful to report this to the user
* instread of the string from data2.
* instead of the string from data2.
*
* @param data1 (int) 1=first/new network error, should be reported the user;
* 0=subsequent network error, should be logged only

View File

@@ -0,0 +1,11 @@
prefix={prefix}
libdir=${{prefix}}/lib
includedir=${{prefix}}/include
Name: {name}
Description: {description}
URL: {url}
Version: {version}
Cflags: -I${{includedir}}
Libs: -L${{libdir}} -ldeltachat
Libs.private: {libs_priv}

View File

@@ -10,6 +10,8 @@
#[macro_use]
extern crate human_panic;
use std::str::FromStr;
use deltachat::*;
// TODO: constants
@@ -88,9 +90,13 @@ pub unsafe extern "C" fn dc_set_config(
value: *mut libc::c_char,
) -> libc::c_int {
assert!(!context.is_null());
assert!(!key.is_null(), "invalid key");
let context = &*context;
context::dc_set_config(context, key, value)
match config::Config::from_str(dc_tools::as_str(key)) {
Ok(key) => context.set_config(key, as_opt_str(value)).is_ok() as libc::c_int,
Err(_) => 0,
}
}
#[no_mangle]
@@ -99,9 +105,16 @@ pub unsafe extern "C" fn dc_get_config(
key: *mut libc::c_char,
) -> *mut libc::c_char {
assert!(!context.is_null());
assert!(!key.is_null(), "invalid key");
let context = &*context;
context::dc_get_config(context, key)
match config::Config::from_str(dc_tools::as_str(key)) {
Ok(key) => {
let value = context.get_config(key).unwrap_or_default();
dc_tools::to_cstring(value)
}
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
@@ -124,7 +137,7 @@ pub unsafe extern "C" fn dc_get_oauth2_url(
let addr = dc_tools::to_string(addr);
let redirect = dc_tools::to_string(redirect);
match oauth2::dc_get_oauth2_url(context, addr, redirect) {
Some(res) => libc::strdup(dc_tools::to_cstring(res).as_ptr()),
Some(res) => dc_tools::to_cstring(res),
None => std::ptr::null_mut(),
}
}
@@ -411,7 +424,7 @@ pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_marknoticed_chat(context, chat_id)
dc_chat::dc_marknoticed_chat(context, chat_id);
}
#[no_mangle]
@@ -419,7 +432,7 @@ pub unsafe extern "C" fn dc_marknoticed_all_chats(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_marknoticed_all_chats(context)
dc_chat::dc_marknoticed_all_chats(context);
}
#[no_mangle]
@@ -460,7 +473,7 @@ pub unsafe extern "C" fn dc_archive_chat(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_archive_chat(context, chat_id, archive)
dc_chat::dc_archive_chat(context, chat_id, archive);
}
#[no_mangle]
@@ -468,7 +481,8 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_delete_chat(context, chat_id)
// TODO: update to indicate public api success/failure of deletion
dc_chat::dc_delete_chat(context, chat_id);
}
#[no_mangle]
@@ -498,7 +512,7 @@ pub unsafe extern "C" fn dc_search_msgs(
pub unsafe extern "C" fn dc_get_chat<'a>(
context: *mut dc_context_t,
chat_id: u32,
) -> *mut dc_chat::dc_chat_t<'a> {
) -> *mut dc_chat_t<'a> {
assert!(!context.is_null());
let context = &*context;
@@ -641,7 +655,7 @@ pub unsafe extern "C" fn dc_markseen_msgs(
assert!(!context.is_null());
let context = &*context;
dc_msg::dc_markseen_msgs(context, msg_ids, msg_cnt)
dc_msg::dc_markseen_msgs(context, msg_ids, msg_cnt as usize);
}
#[no_mangle]
@@ -654,7 +668,7 @@ pub unsafe extern "C" fn dc_star_msgs(
assert!(!context.is_null());
let context = &*context;
dc_msg::dc_star_msgs(context, msg_ids, msg_cnt, star)
dc_msg::dc_star_msgs(context, msg_ids, msg_cnt, star);
}
#[no_mangle]
@@ -887,7 +901,7 @@ pub unsafe extern "C" fn dc_is_sending_locations_to_chat(
assert!(!context.is_null());
let context = &*context;
dc_location::dc_is_sending_locations_to_chat(context, chat_id)
dc_location::dc_is_sending_locations_to_chat(context, chat_id) as libc::c_int
}
#[no_mangle]
@@ -928,7 +942,7 @@ pub unsafe extern "C" fn dc_delete_all_locations(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_location::dc_delete_all_locations(context)
dc_location::dc_delete_all_locations(context);
}
// dc_array_t
@@ -1095,7 +1109,7 @@ pub unsafe extern "C" fn dc_chatlist_get_msg_id(
pub unsafe extern "C" fn dc_chatlist_get_summary<'a>(
chatlist: *mut dc_chatlist::dc_chatlist_t<'a>,
index: libc::size_t,
chat: *mut dc_chat::dc_chat_t<'a>,
chat: *mut dc_chat_t<'a>,
) -> *mut dc_lot::dc_lot_t {
dc_chatlist::dc_chatlist_get_summary(chatlist, index, chat)
}
@@ -1111,69 +1125,65 @@ pub unsafe extern "C" fn dc_chatlist_get_context(
// dc_chat_t
#[no_mangle]
pub type dc_chat_t<'a> = dc_chat::dc_chat_t<'a>;
pub type dc_chat_t<'a> = dc_chat::Chat<'a>;
#[no_mangle]
pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat::dc_chat_t) {
pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat_t) {
dc_chat::dc_chat_unref(chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat::dc_chat_t) -> u32 {
pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 {
dc_chat::dc_chat_get_id(chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat::dc_chat_t) -> libc::c_int {
pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int {
dc_chat::dc_chat_get_type(chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat::dc_chat_t) -> *mut libc::c_char {
pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_char {
dc_chat::dc_chat_get_name(chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat::dc_chat_t) -> *mut libc::c_char {
pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat_t) -> *mut libc::c_char {
dc_chat::dc_chat_get_subtitle(chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_profile_image(
chat: *mut dc_chat::dc_chat_t,
) -> *mut libc::c_char {
pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut libc::c_char {
dc_chat::dc_chat_get_profile_image(chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat::dc_chat_t) -> u32 {
pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 {
dc_chat::dc_chat_get_color(chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat::dc_chat_t) -> libc::c_int {
pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat_t) -> libc::c_int {
dc_chat::dc_chat_get_archived(chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat::dc_chat_t) -> libc::c_int {
pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int {
dc_chat::dc_chat_is_unpromoted(chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat::dc_chat_t) -> libc::c_int {
pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_int {
dc_chat::dc_chat_is_self_talk(chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat::dc_chat_t) -> libc::c_int {
pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_int {
dc_chat::dc_chat_is_verified(chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_is_sending_locations(
chat: *mut dc_chat::dc_chat_t,
) -> libc::c_int {
pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int {
dc_chat::dc_chat_is_sending_locations(chat)
}
@@ -1291,7 +1301,7 @@ pub unsafe extern "C" fn dc_msg_get_showpadlock(msg: *mut dc_msg::dc_msg_t) -> l
#[no_mangle]
pub unsafe extern "C" fn dc_msg_get_summary(
msg: *mut dc_msg::dc_msg_t,
chat: *mut dc_chat::dc_chat_t,
chat: *mut dc_chat_t,
) -> *mut dc_lot::dc_lot_t {
dc_msg::dc_msg_get_summary(msg, chat)
}
@@ -1341,7 +1351,7 @@ pub unsafe extern "C" fn dc_msg_is_increation(msg: *mut dc_msg::dc_msg_t) -> lib
#[no_mangle]
pub unsafe extern "C" fn dc_msg_is_setupmessage(msg: *mut dc_msg::dc_msg_t) -> libc::c_int {
dc_msg::dc_msg_is_setupmessage(msg)
dc_msg::dc_msg_is_setupmessage(msg) as libc::c_int
}
#[no_mangle]
@@ -1528,3 +1538,11 @@ pub unsafe extern "C" fn dc_lot_get_timestamp(lot: *mut dc_lot::dc_lot_t) -> i64
pub unsafe extern "C" fn dc_str_unref(s: *mut libc::c_char) {
libc::free(s as *mut _)
}
fn as_opt_str<'a>(s: *const libc::c_char) -> Option<&'a str> {
if s.is_null() {
return None;
}
Some(dc_tools::as_str(s))
}

View File

@@ -1,3 +1,6 @@
use std::str::FromStr;
use deltachat::config;
use deltachat::constants::*;
use deltachat::context::*;
use deltachat::dc_array::*;
@@ -12,9 +15,9 @@ use deltachat::dc_lot::*;
use deltachat::dc_msg::*;
use deltachat::dc_qr::*;
use deltachat::dc_receive_imf::*;
use deltachat::dc_sqlite3::*;
use deltachat::dc_tools::*;
use deltachat::peerstate::*;
use deltachat::sql;
use deltachat::types::*;
use deltachat::x::*;
use num_traits::FromPrimitive;
@@ -25,65 +28,64 @@ use num_traits::FromPrimitive;
pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
info!(context, 0, "Resetting tables ({})...", bits);
if 0 != bits & 1 {
dc_sqlite3_execute(
context,
&context.sql,
b"DELETE FROM jobs;\x00" as *const u8 as *const libc::c_char,
);
sql::execute(context, &context.sql, "DELETE FROM jobs;", params![]).unwrap();
info!(context, 0, "(1) Jobs reset.");
}
if 0 != bits & 2 {
dc_sqlite3_execute(
sql::execute(
context,
&context.sql,
b"DELETE FROM acpeerstates;\x00" as *const u8 as *const libc::c_char,
);
"DELETE FROM acpeerstates;",
params![],
)
.unwrap();
info!(context, 0, "(2) Peerstates reset.");
}
if 0 != bits & 4 {
dc_sqlite3_execute(
context,
&context.sql,
b"DELETE FROM keypairs;\x00" as *const u8 as *const libc::c_char,
);
sql::execute(context, &context.sql, "DELETE FROM keypairs;", params![]).unwrap();
info!(context, 0, "(4) Private keypairs reset.");
}
if 0 != bits & 8 {
dc_sqlite3_execute(
sql::execute(
context,
&context.sql,
b"DELETE FROM contacts WHERE id>9;\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_execute(
"DELETE FROM contacts WHERE id>9;",
params![],
)
.unwrap();
sql::execute(
context,
&context.sql,
b"DELETE FROM chats WHERE id>9;\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_execute(
"DELETE FROM chats WHERE id>9;",
params![],
)
.unwrap();
sql::execute(
context,
&context.sql,
b"DELETE FROM chats_contacts;\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_execute(
"DELETE FROM chats_contacts;",
params![],
)
.unwrap();
sql::execute(
context,
&context.sql,
b"DELETE FROM msgs WHERE id>9;\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_execute(
"DELETE FROM msgs WHERE id>9;",
params![],
)
.unwrap();
sql::execute(
context,
&context.sql,
b"DELETE FROM config WHERE keyname LIKE \'imap.%\' OR keyname LIKE \'configured%\';\x00"
as *const u8 as *const libc::c_char,
);
dc_sqlite3_execute(
context,
&context.sql,
b"DELETE FROM leftgrps;\x00" as *const u8 as *const libc::c_char,
);
"DELETE FROM config WHERE keyname LIKE 'imap.%' OR keyname LIKE 'configured%';",
params![],
)
.unwrap();
sql::execute(context, &context.sql, "DELETE FROM leftgrps;", params![]).unwrap();
info!(context, 0, "(8) Rest but server config reset.");
}
context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t);
context.call_cb(Event::MSGS_CHANGED, 0, 0);
1
}
@@ -100,14 +102,7 @@ unsafe fn dc_poke_eml_file(context: &Context, filename: *const libc::c_char) ->
&mut data_bytes,
) == 0i32)
{
dc_receive_imf(
context,
data,
data_bytes,
b"import\x00" as *const u8 as *const libc::c_char,
0i32 as uint32_t,
0i32 as uint32_t,
);
dc_receive_imf(context, data, data_bytes, "import", 0, 0);
success = 1;
}
free(data as *mut libc::c_void);
@@ -138,26 +133,20 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
/* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
if !spec.is_null() {
real_spec = dc_strdup(spec);
dc_sqlite3_set_config(
context,
&context.sql,
b"import_spec\x00" as *const u8 as *const libc::c_char,
real_spec,
);
context
.sql
.set_config(context, "import_spec", Some(as_str(real_spec)))
.unwrap();
current_block = 7149356873433890176;
} else {
real_spec = dc_sqlite3_get_config(
context,
&context.sql,
b"import_spec\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
if real_spec.is_null() {
let rs = context.sql.get_config(context, "import_spec");
if rs.is_none() {
error!(context, 0, "Import: No file or folder given.");
current_block = 8522321847195001863;
} else {
current_block = 7149356873433890176;
}
real_spec = to_cstring(rs.unwrap_or_default());
}
match current_block {
8522321847195001863 => {}
@@ -195,10 +184,10 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
let path_plus_name = format!("{}/{}", as_str(real_spec), name);
info!(context, 0, "Import: {}", path_plus_name);
let path_plus_name_c = to_cstring(path_plus_name);
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
if 0 != dc_poke_eml_file(context, path_plus_name_c) {
read_cnt += 1
}
free(path_plus_name_c as *mut _);
}
}
current_block = 1622411330066726685;
@@ -377,7 +366,7 @@ pub unsafe fn dc_cmdline_skip_auth() {
S_IS_AUTH = 1;
}
unsafe fn chat_prefix(chat: *const dc_chat_t) -> &'static str {
unsafe fn chat_prefix(chat: *const Chat) -> &'static str {
if (*chat).type_0 == 120 {
"Group"
} else if (*chat).type_0 == 130 {
@@ -398,18 +387,16 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let mut args = line.splitn(3, ' ');
let arg0 = args.next().unwrap_or_default();
let arg1 = args.next().unwrap_or_default();
let arg1_c = to_cstring(arg1);
let arg1_c_ptr = if arg1.is_empty() {
let arg1_c = if arg1.is_empty() {
std::ptr::null()
} else {
arg1_c.as_ptr()
to_cstring(arg1) as *const _
};
let arg2 = args.next().unwrap_or_default();
let arg2_c = to_cstring(arg2);
let arg2_c_ptr = if arg2.is_empty() {
let arg2_c = if arg2.is_empty() {
std::ptr::null()
} else {
arg2_c.as_ptr()
to_cstring(arg2) as *const _
};
match arg0 {
@@ -499,9 +486,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
},
"auth" => {
if 0 == S_IS_AUTH {
let is_pw =
dc_get_config(context, b"mail_pw\x00" as *const u8 as *const libc::c_char);
if arg1 == as_str(is_pw) {
let is_pw = context
.get_config(config::Config::MailPw)
.unwrap_or_default();
if arg1 == is_pw {
S_IS_AUTH = 1;
} else {
println!("Bad password.");
@@ -514,7 +502,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!arg1.is_empty(), "Argument <file> missing");
dc_close(context);
ensure!(
0 != dc_open(context, arg1_c_ptr, 0 as *const libc::c_char),
0 != dc_open(context, arg1_c, 0 as *const libc::c_char),
"Open failed"
);
}
@@ -537,8 +525,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let msg_id: u32 = arg1.parse().unwrap();
let msg: *mut dc_msg_t = dc_get_msg(context, msg_id);
if 0 != dc_msg_is_setupmessage(msg) {
let setupcodebegin: *mut libc::c_char = dc_msg_get_setupcodebegin(msg);
if dc_msg_is_setupmessage(msg) {
let setupcodebegin = dc_msg_get_setupcodebegin(msg);
println!(
"The setup code for setup message Msg#{} starts with: {}",
msg_id,
@@ -555,7 +543,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
!arg1.is_empty() && !arg2.is_empty(),
"Arguments <msg-id> <setup-code> expected"
);
if 0 == dc_continue_key_transfer(context, arg1.parse().unwrap(), arg2_c_ptr) {
if 0 == dc_continue_key_transfer(context, arg1.parse().unwrap(), arg2_c) {
bail!("Continue key transfer failed");
}
}
@@ -570,7 +558,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"import-backup" => {
ensure!(!arg1.is_empty(), "Argument <backup-file> missing.");
dc_imex(context, 12, arg1_c_ptr, 0 as *const libc::c_char);
dc_imex(context, 12, arg1_c, 0 as *const libc::c_char);
}
"export-keys" => {
dc_imex(context, 1, context.get_blobdir(), 0 as *const libc::c_char);
@@ -607,7 +595,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
free(setup_code as *mut libc::c_void);
}
"poke" => {
ensure!(0 != poke_spec(context, arg1_c_ptr), "Poke failed");
ensure!(0 != poke_spec(context, arg1_c), "Poke failed");
}
"reset" => {
ensure!(!arg1.is_empty(), "Argument <bits> missing: 1=jobs, 2=peerstates, 4=private keys, 8=rest but server config");
@@ -620,16 +608,15 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"set" => {
ensure!(!arg1.is_empty(), "Argument <key> missing.");
ensure!(
0 != dc_set_config(context, arg1_c_ptr, arg2_c_ptr),
"Set config failed"
);
let key = config::Config::from_str(&arg1)?;
let value = if arg2.is_empty() { None } else { Some(arg2) };
context.set_config(key, value)?;
}
"get" => {
ensure!(!arg1.is_empty(), "Argument <key> missing.");
let val = dc_get_config(context, arg1_c_ptr);
println!("{}={}", arg1, to_string(val));
free(val as *mut libc::c_void);
let key = config::Config::from_str(&arg1)?;
let val = context.get_config(key);
println!("{}={:?}", key, val);
}
"info" => {
println!("{}", to_string(dc_get_info(context)));
@@ -638,11 +625,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
dc_maybe_network(context);
}
"housekeeping" => {
dc_housekeeping(context);
sql::housekeeping(context);
}
"listchats" | "listarchived" | "chats" => {
let listflags = if arg0 == "listarchived" { 0x01 } else { 0 };
let chatlist = dc_get_chatlist(context, listflags, arg1_c_ptr, 0 as uint32_t);
let chatlist = dc_get_chatlist(context, listflags, arg1_c, 0 as uint32_t);
ensure!(!chatlist.is_null(), "Failed to retrieve chatlist");
let mut i: libc::c_int;
@@ -714,7 +701,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
i -= 1
}
}
if 0 != dc_is_sending_locations_to_chat(context, 0 as uint32_t) {
if dc_is_sending_locations_to_chat(context, 0 as uint32_t) {
info!(context, 0, "Location streaming enabled.");
}
println!("{} chats", cnt);
@@ -787,7 +774,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let chat_id_0: libc::c_int =
dc_create_chat_by_msg_id(context, msg_id_0 as uint32_t) as libc::c_int;
if chat_id_0 != 0 {
let chat_0: *mut dc_chat_t = dc_get_chat(context, chat_id_0 as uint32_t);
let chat_0: *mut Chat = dc_get_chat(context, chat_id_0 as uint32_t);
println!(
"{}#{} created successfully.",
chat_prefix(chat_0),
@@ -800,8 +787,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"creategroup" => {
ensure!(!arg1.is_empty(), "Argument <name> missing.");
let chat_id_1: libc::c_int =
dc_create_group_chat(context, 0, arg1_c_ptr) as libc::c_int;
let chat_id_1: libc::c_int = dc_create_group_chat(context, 0, arg1_c) as libc::c_int;
if chat_id_1 != 0 {
println!("Group#{} created successfully.", chat_id_1,);
} else {
@@ -810,8 +796,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"createverified" => {
ensure!(!arg1.is_empty(), "Argument <name> missing.");
let chat_id_2: libc::c_int =
dc_create_group_chat(context, 1, arg1_c_ptr) as libc::c_int;
let chat_id_2: libc::c_int = dc_create_group_chat(context, 1, arg1_c) as libc::c_int;
if chat_id_2 != 0 {
println!("VerifiedGroup#{} created successfully.", chat_id_2,);
} else {
@@ -850,7 +835,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"groupname" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "Argument <name> missing.");
if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c_ptr) {
if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c) {
println!("Chat name set");
} else {
bail!("Failed to set chat name");
@@ -864,7 +849,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
context,
dc_chat_get_id(sel_chat),
if !arg1.is_empty() {
arg1_c_ptr
arg1_c
} else {
std::ptr::null_mut()
},
@@ -929,7 +914,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let seconds = arg1.parse().unwrap();
dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds);
println!("Locations will be sent to Chat#{} for {} seconds. Use \'setlocation <lat> <lng>\' to play around.", dc_chat_get_id(sel_chat), seconds);
println!("Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.", dc_chat_get_id(sel_chat), seconds);
}
"setlocation" => {
ensure!(
@@ -955,9 +940,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let msg = to_cstring(format!("{} {}", arg1, arg2));
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg.as_ptr()) {
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg) {
println!("Message sent.");
free(msg as *mut _);
} else {
free(msg as *mut _);
bail!("Sending failed.");
}
}
@@ -978,8 +965,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given.");
let msg_0 = dc_msg_new(context, if arg0 == "sendimage" { 20 } else { 60 });
dc_msg_set_file(msg_0, arg1_c_ptr, 0 as *const libc::c_char);
dc_msg_set_text(msg_0, arg2_c_ptr);
dc_msg_set_file(msg_0, arg1_c, 0 as *const libc::c_char);
dc_msg_set_text(msg_0, arg2_c);
dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0);
dc_msg_unref(msg_0);
}
@@ -992,7 +979,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
0 as libc::c_uint
};
let msglist_0 = dc_search_msgs(context, chat, arg1_c_ptr);
let msglist_0 = dc_search_msgs(context, chat, arg1_c);
if !msglist_0.is_null() {
log_msglist(context, msglist_0);
@@ -1005,7 +992,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
if !arg1.is_empty() {
let draft_0 = dc_msg_new(context, 10);
dc_msg_set_text(draft_0, arg1_c_ptr);
dc_msg_set_text(draft_0, arg1_c);
dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0);
dc_msg_unref(draft_0);
println!("Draft saved.");
@@ -1097,7 +1084,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
} else {
0x2
},
arg1_c_ptr,
arg1_c,
);
if !contacts.is_null() {
log_contactlist(context, contacts);
@@ -1113,13 +1100,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
if !arg2.is_empty() {
let book = dc_mprintf(
b"%s\n%s\x00" as *const u8 as *const libc::c_char,
arg1_c_ptr,
arg2_c_ptr,
arg1_c,
arg2_c,
);
dc_add_address_book(context, book);
free(book as *mut libc::c_void);
} else {
if 0 == dc_create_contact(context, 0 as *const libc::c_char, arg1_c_ptr) {
if 0 == dc_create_contact(context, 0 as *const libc::c_char, arg1_c) {
bail!("Failed to create contact");
}
}
@@ -1166,7 +1153,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"checkqr" => {
ensure!(!arg1.is_empty(), "Argument <qr-content> missing.");
let res = dc_check_qr(context, arg1_c_ptr);
let res = dc_check_qr(context, arg1_c);
println!(
"state={}, id={}, text1={}, text2={}",
(*res).state as libc::c_int,
@@ -1194,7 +1181,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
if 0 != dc_read_file(
context,
arg1_c_ptr,
arg1_c,
&mut buf as *mut *mut libc::c_uchar as *mut *mut libc::c_void,
&mut buf_bytes,
) {
@@ -1212,5 +1199,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
dc_chat_unref(sel_chat);
}
free(arg1_c as *mut _);
free(arg2_c as *mut _);
Ok(())
}

View File

@@ -10,11 +10,14 @@ extern crate deltachat;
extern crate failure;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate rusqlite;
use std::borrow::Cow::{self, Borrowed, Owned};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use deltachat::config;
use deltachat::constants::*;
use deltachat::context::*;
use deltachat::dc_configure::*;
@@ -395,11 +398,10 @@ fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
if args.len() == 2 {
if 0 == unsafe {
dc_open(
&mut context,
to_cstring(&args[1]).as_ptr(),
0 as *const libc::c_char,
)
let a = to_cstring(&args[1]);
let res = dc_open(&mut context, a, 0 as *const _);
free(a as *mut _);
res
} {
println!("Error: Cannot open {}.", args[0],);
}
@@ -479,11 +481,10 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
let mut args = line.splitn(2, ' ');
let arg0 = args.next().unwrap_or_default();
let arg1 = args.next().unwrap_or_default();
let arg1_c = to_cstring(arg1);
let arg1_c_ptr = if arg1.is_empty() {
let arg1_c = if arg1.is_empty() {
std::ptr::null()
} else {
arg1_c.as_ptr()
to_cstring(arg1)
};
match arg0 {
@@ -512,25 +513,20 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
dc_configure(&ctx.read().unwrap());
}
"oauth2" => {
let addr = dc_get_config(
&ctx.read().unwrap(),
b"addr\x00" as *const u8 as *const libc::c_char,
);
if addr.is_null() || *addr.offset(0isize) as libc::c_int == 0i32 {
println!("oauth2: set addr first.");
} else {
if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) {
let oauth2_url = dc_get_oauth2_url(
&ctx.read().unwrap(),
as_str(addr),
&addr,
"chat.delta:/com.b44t.messenger",
);
if oauth2_url.is_none() {
println!("OAuth2 not available for {}.", to_string(addr));
println!("OAuth2 not available for {}.", &addr);
} else {
println!("Open the following url, set mail_pw to the generated token and server_flags to 2:\n{}", oauth2_url.unwrap());
}
} else {
println!("oauth2: set addr first.");
}
free(addr as *mut libc::c_void);
}
"clear" => {
println!("\n\n\n");
@@ -561,13 +557,15 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
"joinqr" => {
start_threads(ctx.clone());
if !arg0.is_empty() {
dc_join_securejoin(&ctx.read().unwrap(), arg1_c_ptr);
dc_join_securejoin(&ctx.read().unwrap(), arg1_c);
}
}
"exit" => return Ok(ExitResult::Exit),
_ => dc_cmdline(&ctx.read().unwrap(), line)?,
}
free(arg1_c as *mut _);
Ok(ExitResult::Continue)
}

View File

@@ -5,6 +5,7 @@ use std::sync::{Arc, RwLock};
use std::{thread, time};
use tempfile::tempdir;
use deltachat::config;
use deltachat::constants::Event;
use deltachat::context::*;
use deltachat::dc_chat::*;
@@ -79,20 +80,15 @@ fn main() {
println!("opening database {:?}", dbfile);
dc_open(&ctx, dbfile.as_ptr(), std::ptr::null());
assert_eq!(dc_open(&ctx, dbfile.as_ptr(), std::ptr::null()), 1);
println!("configuring");
let pw = std::env::args().collect::<Vec<String>>()[1].clone();
dc_set_config(
&ctx,
CString::new("addr").unwrap().as_ptr(),
CString::new("d@testrun.org").unwrap().as_ptr(),
);
dc_set_config(
&ctx,
CString::new("mail_pw").unwrap().as_ptr(),
CString::new(pw).unwrap().as_ptr(),
);
let args = std::env::args().collect::<Vec<String>>();
assert_eq!(args.len(), 2, "missing password");
let pw = args[1].clone();
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
.unwrap();
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
dc_configure(&ctx);
thread::sleep(duration);
@@ -127,8 +123,8 @@ fn main() {
}
dc_chatlist_unref(chats);
*running.clone().write().unwrap() = false;
println!("stopping threads");
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);
@@ -139,6 +135,9 @@ fn main() {
// }
// dc_array_unref(msglist);
println!("stopping threads");
*running.clone().write().unwrap() = false;
deltachat::dc_job::dc_interrupt_imap_idle(&ctx);
deltachat::dc_job::dc_interrupt_smtp_idle(&ctx);

View File

@@ -1,6 +1,23 @@
0.9.1-dev
0.600.1
---------
- introduce automatic versioning via setuptools_scm,
based on py-X.Y.Z tags
- integrate latest DCC core-rust with dc_close() fixes
- provide a account.shutdown() method and improve termination
logic also in tests. also fixes output-clubbering during
test runs.
0.600.0
---------
- use new experimental full-Rust Delta Chat core
- support Autocrypt Setup Messages
- remove synchronous events
- use CircleCI for continous integration and packaging of Linux wheels
- use docker image for building wheels
- fix code documentation links

View File

@@ -1,15 +1,17 @@
deltachat python bindings
=========================
The ``deltachat`` Python package provides two bindings for the core C-library
The ``deltachat`` Python package provides two bindings for the core Rust-library
of the https://delta.chat messaging ecosystem:
- :doc:`capi` is a lowlevel CFFI-binding to the
`deltachat-core C-API <https://c.delta.chat>`_.
- :doc:`api` [work-in-progress] is a high level interface to deltachat-core which aims
- :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:`capi` is a lowlevel CFFI-binding to the previous
`deltachat-core C-API <https://c.delta.chat>`_ (so far the Rust library
replicates exactly the same C-level API).
getting started
---------------

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -ex
cargo build -p deltachat_ffi --release
DCC_RS_DEV=`pwd`/.. pip install -e .

View File

@@ -0,0 +1,36 @@
#!/usr/bin/env python
"""
setup a python binding development in-place install with cargo debug symbols.
"""
import os
import subprocess
if __name__ == "__main__":
os.environ["DCC_RS_TARGET"] = target = "release"
toml = os.path.join(os.getcwd(), "..", "Cargo.toml")
assert os.path.exists(toml)
with open(toml) as f:
s = orig = f.read()
s += "\n"
s += "[profile.release]\n"
s += "debug = true\n"
with open(toml, "w") as f:
f.write(s)
print("temporarily modifying Cargo.toml to provide release build with debug symbols ")
try:
subprocess.check_call([
"cargo", "build", "-p", "deltachat_ffi", "--" + target
])
finally:
with open(toml, "w") as f:
f.write(orig)
print("\nreseted Cargo.toml to previous original state")
subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True)
subprocess.check_call([
"pip", "install", "-e", "."
])

View File

@@ -4,15 +4,20 @@ import re
def main():
long_description, version = read_meta()
with open("README.rst") as f:
long_description = f.read()
setuptools.setup(
name='deltachat',
version=version,
description='Python bindings for deltachat-core using CFFI',
setup_requires=['setuptools_scm', 'cffi>=1.0.0'],
use_scm_version = {
"root": "..",
"relative_to": __file__,
'tag_regex': r'^(?P<prefix>py-)?(?P<version>[^\+]+)(?P<suffix>.*)?$',
},
description='Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat',
long_description=long_description,
author='holger krekel, Floris Bruynooghe, Bjoern Petersen and contributors',
setup_requires=['cffi>=1.0.0'],
install_requires=['cffi>=1.0.0', 'requests', 'attrs', 'six'],
install_requires=['cffi>=1.0.0', 'attrs', 'six'],
packages=setuptools.find_packages('src'),
package_dir={'': 'src'},
cffi_modules=['src/deltachat/_build.py:ffibuilder'],
@@ -27,18 +32,5 @@ def main():
)
def read_meta():
with open(os.path.join("src", "deltachat", "__init__.py")) as f:
for line in f:
m = re.match('__version__ = "(\S*).*"', line)
if m:
version, = m.groups()
break
with open("README.rst") as f:
long_desc = f.read()
return long_desc, version
if __name__ == "__main__":
main()

View File

@@ -2,7 +2,12 @@ from deltachat import capi, const
from deltachat.capi import ffi
from deltachat.account import Account # noqa
__version__ = "0.10.0.dev2"
from pkg_resources import get_distribution, DistributionNotFound
try:
__version__ = get_distribution(__name__).version
except DistributionNotFound:
# package is not installed
__version__ = "0.0.0.dev0-unknown"
_DC_CALLBACK_MAP = {}
@@ -38,6 +43,9 @@ def py_dc_callback(ctx, evt, data1, data2):
try:
ret = callback(ctx, evt_name, data1, data2)
if ret is None:
ret = 0
assert isinstance(ret, int), repr(ret)
if event_sig_types & 4:
return ffi.cast('uintptr_t', ret)
elif event_sig_types & 8:
@@ -53,7 +61,10 @@ def set_context_callback(dc_context, func):
def clear_context_callback(dc_context):
_DC_CALLBACK_MAP.pop(dc_context, None)
try:
_DC_CALLBACK_MAP.pop(dc_context, None)
except AttributeError:
pass
def get_dc_event_name(integer, _DC_EVENTNAME_MAP={}):

View File

@@ -2,21 +2,36 @@ import distutils.ccompiler
import distutils.log
import distutils.sysconfig
import tempfile
import platform
import os
import cffi
import shutil
def ffibuilder():
projdir = os.environ.get('DCC_RS_DEV')
target = os.environ.get('DCC_RS_TARGET', 'release')
if projdir:
libs = ['rt', 'dl', 'm']
if platform.system() == 'Darwin':
libs = ['resolv', 'dl']
extra_link_args = [
'-framework', 'CoreFoundation',
'-framework', 'CoreServices',
'-framework', 'Security',
]
elif platform.system() == 'Linux':
libs = ['rt', 'dl', 'm']
extra_link_args = []
else:
raise NotImplementedError("Compilation not supported yet on Windows, can you help?")
objs = [os.path.join(projdir, 'target', target, 'libdeltachat.a')]
assert os.path.exists(objs[0]), objs
incs = [os.path.join(projdir, 'deltachat-ffi')]
else:
libs = ['deltachat']
objs = []
incs = []
extra_link_args = []
builder = cffi.FFI()
builder.set_source(
'deltachat.capi',
@@ -43,6 +58,7 @@ def ffibuilder():
include_dirs=incs,
libraries=libs,
extra_objects=objs,
extra_link_args=extra_link_args,
)
builder.cdef("""
typedef int... time_t;
@@ -53,15 +69,21 @@ def ffibuilder():
distutils.log.set_verbosity(distutils.log.INFO)
cc = distutils.ccompiler.new_compiler(force=True)
distutils.sysconfig.customize_compiler(cc)
with tempfile.NamedTemporaryFile(mode='w', suffix='.h') as src_fp:
src_fp.write('#include <deltachat.h>')
src_fp.flush()
with tempfile.NamedTemporaryFile(mode='r') as dst_fp:
cc.preprocess(source=src_fp.name,
output_file=dst_fp.name,
include_dirs=incs,
macros=[('PY_CFFI', '1')])
tmpdir = tempfile.mkdtemp()
try:
src_name = os.path.join(tmpdir, "include.h")
dst_name = os.path.join(tmpdir, "expanded.h")
with open(src_name, "w") as src_fp:
src_fp.write('#include <deltachat.h>')
cc.preprocess(source=src_name,
output_file=dst_name,
include_dirs=incs,
macros=[('PY_CFFI', '1')])
with open(dst_name, "r") as dst_fp:
builder.cdef(dst_fp.read())
finally:
shutil.rmtree(tmpdir)
builder.cdef("""
extern "Python" uintptr_t py_dc_callback(
dc_context_t* context,

View File

@@ -2,16 +2,14 @@
from __future__ import print_function
import threading
import os
import re
import time
import requests
from array import array
try:
from queue import Queue
except ImportError:
from Queue import Queue
import attr
from attr import validators as v
import deltachat
from . import const
@@ -25,27 +23,35 @@ class Account(object):
by the underlying deltachat c-library. All public Account methods are
meant to be memory-safe and return memory-safe objects.
"""
def __init__(self, db_path, logid=None):
def __init__(self, db_path, logid=None, eventlogging=True):
""" initialize account object.
:param db_path: a path to the account database. The database
will be created if it doesn't exist.
: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
"""
self._dc_context = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
_destroy_dc_context,
)
if eventlogging:
self._evlogger = EventLogger(self._dc_context, logid)
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)
if hasattr(db_path, "encode"):
db_path = db_path.encode("utf8")
if not lib.dc_open(self._dc_context, db_path, ffi.NULL):
raise ValueError("Could not dc_open: {}".format(db_path))
self._evhandler = EventHandler(self._dc_context)
self._evlogger = EventLogger(self._dc_context, logid)
deltachat.set_context_callback(self._dc_context, self._process_event)
self._threads = IOThreads(self._dc_context)
self._configkeys = self.get_config("sys.config_keys").split()
self._imex_completed = threading.Event()
def __del__(self):
self.shutdown()
def _check_config_key(self, name):
if name not in self._configkeys:
@@ -182,15 +188,19 @@ class Account(object):
return list(iter_array(dc_array, lambda x: Contact(self._dc_context, x)))
def create_chat_by_contact(self, contact):
""" create or get an existing 1:1 chat object for the specified contact.
""" create or get an existing 1:1 chat object for the specified contact or contact id.
:param contact: chat_id (int) or contact object.
:returns: a :class:`deltachat.chatting.Chat` object.
"""
contact_id = getattr(contact, "id", contact)
assert isinstance(contact_id, int)
chat_id = lib.dc_create_chat_by_contact_id(
self._dc_context, contact_id)
if hasattr(contact, "id"):
if contact._dc_context != self._dc_context:
raise ValueError("Contact belongs to a different Account")
contact_id = contact.id
else:
assert isinstance(contact, int)
contact_id = contact
chat_id = lib.dc_create_chat_by_contact_id(self._dc_context, contact_id)
return Chat(self._dc_context, chat_id)
def create_chat_by_message(self, message):
@@ -200,8 +210,13 @@ class Account(object):
:param message: messsage id or message instance.
:returns: a :class:`deltachat.chatting.Chat` object.
"""
msg_id = getattr(message, "id", message)
assert isinstance(msg_id, int)
if hasattr(message, "id"):
if self._dc_context != message._dc_context:
raise ValueError("Message belongs to a different Account")
msg_id = message.id
else:
assert isinstance(message, int)
msg_id = message
chat_id = lib.dc_create_chat_by_msg_id(self._dc_context, msg_id)
return Chat(self._dc_context, chat_id)
@@ -272,6 +287,45 @@ class Account(object):
msg_ids = [msg.id for msg in messages]
lib.dc_delete_msgs(self._dc_context, msg_ids, len(msg_ids))
def export_to_dir(self, backupdir):
"""return after all delta chat state is exported to a new file in
the specified directory.
"""
snap_files = os.listdir(backupdir)
self._imex_completed.clear()
lib.dc_imex(self._dc_context, 11, as_dc_charpointer(backupdir), ffi.NULL)
if not self._threads.is_started():
lib.dc_perform_imap_jobs(self._dc_context)
self._imex_completed.wait()
for x in os.listdir(backupdir):
if x not in snap_files:
return os.path.join(backupdir, x)
def import_from_file(self, path):
"""import delta chat state from the specified backup file.
The account must be in unconfigured state for import to attempted.
"""
assert not self.is_configured(), "cannot import into configured account"
self._imex_completed.clear()
lib.dc_imex(self._dc_context, 12, as_dc_charpointer(path), ffi.NULL)
if not self._threads.is_started():
lib.dc_perform_imap_jobs(self._dc_context)
self._imex_completed.wait()
def initiate_key_transfer(self):
"""return setup code after a Autocrypt setup message
has been successfully sent to our own e-mail address ("self-sent message").
If sending out was unsuccessful, a RuntimeError is raised.
"""
self.check_is_configured()
if not self._threads.is_started():
raise RuntimeError("threads not running, can not send out")
res = lib.dc_initiate_key_transfer(self._dc_context)
if res == ffi.NULL:
raise RuntimeError("could not send out autocrypt setup message")
return from_dc_charpointer(res)
def start_threads(self):
""" start IMAP/SMTP threads (and configure account if it hasn't happened).
@@ -282,27 +336,46 @@ class Account(object):
self.configure()
self._threads.start()
def stop_threads(self):
def stop_threads(self, wait=True):
""" stop IMAP/SMTP threads. """
self._threads.stop(wait=True)
lib.dc_stop_ongoing_process(self._dc_context)
self._threads.stop(wait=wait)
def shutdown(self, wait=True):
""" stop threads and close and remove underlying dc_context and callbacks. """
if hasattr(self, "_dc_context") and hasattr(self, "_threads"):
self.stop_threads(wait=False) # to interrupt idle and tell python threads to stop
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
def _process_event(self, ctx, evt_name, data1, data2):
assert ctx == self._dc_context
self._evlogger(evt_name, data1, data2)
method = getattr(self._evhandler, evt_name.lower(), None)
if method is not None:
return method(data1, data2) or 0
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_completed.set()
class IOThreads:
def __init__(self, dc_context):
def __init__(self, dc_context, log_event=lambda *args: None):
self._dc_context = dc_context
self._thread_quitflag = False
self._name2thread = {}
self._log_event = log_event
def is_started(self):
return len(self._name2thread) > 0
def start(self, imap=True, smtp=True):
assert not self._name2thread
assert not self.is_started()
if imap:
self._start_one_thread("imap", self.imap_thread_run)
if smtp:
@@ -322,41 +395,19 @@ class IOThreads:
thread.join()
def imap_thread_run(self):
print ("starting imap thread")
self._log_event("py-bindings-info", 0, "IMAP THREAD START")
while not self._thread_quitflag:
lib.dc_perform_imap_jobs(self._dc_context)
lib.dc_perform_imap_fetch(self._dc_context)
lib.dc_perform_imap_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "IMAP THREAD FINISHED")
def smtp_thread_run(self):
print ("starting smtp thread")
self._log_event("py-bindings-info", 0, "SMTP THREAD START")
while not self._thread_quitflag:
lib.dc_perform_smtp_jobs(self._dc_context)
lib.dc_perform_smtp_idle(self._dc_context)
@attr.s
class EventHandler(object):
_dc_context = attr.ib(validator=v.instance_of(ffi.CData))
def read_url(self, url):
try:
r = requests.get(url)
except requests.ConnectionError:
return ''
else:
return r.content
def dc_event_http_get(self, data1, data2):
url = data1
content = self.read_url(url)
if not isinstance(content, bytes):
content = content.encode("utf8")
# we need to return a fresh pointer that the core owns
return lib.dupstring_helper(content)
def dc_event_is_offline(self, data1, data2):
return 0 # always online
self._log_event("py-bindings-info", 0, "SMTP THREAD FINISHED")
class EventLogger:
@@ -386,7 +437,7 @@ class EventLogger:
raise ValueError("{}({!r},{!r})".format(*ev))
return ev
def get_matching(self, event_name_regex):
def get_matching(self, event_name_regex, check_error=True):
self._log("-- waiting for event with regex: {} --".format(event_name_regex))
rex = re.compile("(?:{}).*".format(event_name_regex))
while 1:

View File

@@ -93,6 +93,14 @@ class Message(object):
"""
return MessageType(lib.dc_msg_get_viewtype(self._dc_msg))
def is_setup_message(self):
""" return True if this message is a setup message. """
return lib.dc_msg_is_setupmessage(self._dc_msg)
def continue_key_transfer(self, setup_code):
""" extract key and use it as primary key for this account. """
lib.dc_continue_key_transfer(self._dc_context, self.id, as_dc_charpointer(setup_code))
@props.with_doc
def time_sent(self):
"""UTC time when the message was sent.

View File

@@ -16,18 +16,41 @@ def pytest_addoption(parser):
)
def pytest_configure(config):
cfg = config.getoption('--liveconfig')
if not cfg:
cfg = os.getenv('DCC_PY_LIVECONFIG')
if cfg:
config.option.liveconfig = cfg
@pytest.hookimpl(trylast=True)
def pytest_runtest_call(item):
# perform early finalization because we otherwise get cloberred
# output from concurrent threads printing between execution
# of the test function and the teardown phase of that test function
if "acfactory" in item.funcargs:
print("*"*30, "finalizing", "*"*30)
acfactory = item.funcargs["acfactory"]
acfactory.finalize()
def pytest_report_header(config, startdir):
t = tempfile.mktemp()
try:
ac = Account(t)
ac = Account(t, eventlogging=False)
info = ac.get_info()
del ac
ac.shutdown()
finally:
os.remove(t)
return "Deltachat core={} sqlite={}".format(
info['deltachat_core_version'],
info['sqlite_version'],
)
summary = ['Deltachat core={} sqlite={}'.format(
info['deltachat_core_version'],
info['sqlite_version'],
)]
cfg = config.getoption('--liveconfig')
if cfg:
summary.append('Liveconfig: {}'.format(os.path.abspath(cfg)))
return summary
@pytest.fixture(scope="session")
@@ -52,7 +75,6 @@ def acfactory(pytestconfig, tmpdir, request):
self.live_count = 0
self.offline_count = 0
self._finalizers = []
request.addfinalizer(self.finalize)
self.init_time = time.time()
def finalize(self):
@@ -64,7 +86,7 @@ def acfactory(pytestconfig, tmpdir, request):
def configlist(self):
configlist = []
for line in open(fn):
if line.strip():
if line.strip() and not line.strip().startswith('#'):
d = {}
for part in line.split():
name, value = part.split("=")
@@ -78,6 +100,7 @@ def acfactory(pytestconfig, tmpdir, request):
ac = Account(tmpdb.strpath, logid="ac{}".format(self.offline_count))
ac._evlogger.init_time = self.init_time
ac._evlogger.set_timeout(2)
self._finalizers.append(ac.shutdown)
return ac
def get_configured_offline_account(self):
@@ -103,7 +126,18 @@ def acfactory(pytestconfig, tmpdir, request):
ac._evlogger.set_timeout(30)
ac.configure(**configdict)
ac.start_threads()
self._finalizers.append(ac.stop_threads)
self._finalizers.append(ac.shutdown)
return ac
def clone_online_account(self, account):
self.live_count += 1
tmpdb = tmpdir.join("livedb%d" % self.live_count)
ac = Account(tmpdb.strpath, logid="ac{}".format(self.live_count))
ac._evlogger.init_time = self.init_time
ac._evlogger.set_timeout(30)
ac.configure(addr=account.get_config("addr"), mail_pw=account.get_config("mail_pw"))
ac.start_threads()
self._finalizers.append(ac.shutdown)
return ac
return AccountMaker()
@@ -142,8 +176,10 @@ def wait_successful_IMAP_SMTP_connection(account):
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)

View File

@@ -0,0 +1,15 @@
import os
import sys
import subprocess
if __name__ == "__main__":
assert len(sys.argv) == 2
wheelhousedir = sys.argv[1]
# pip wheel will build in an isolated tmp dir that does not have git
# history so setuptools_scm can not automatically determine a
# version there. So pass in the version through an env var.
version = subprocess.check_output(["python", "setup.py", "--version"]).strip().split(b"\n")[-1]
os.environ["SETUPTOOLS_SCM_PRETEND_VERSION"] = version.decode("ascii")
subprocess.check_call(("pip wheel . -w %s" % wheelhousedir).split())

View File

@@ -1,12 +1,18 @@
from __future__ import print_function
import pytest
import os
from deltachat import const
from deltachat import const, Account
from datetime import datetime, timedelta
from conftest import wait_configuration_progress, wait_successful_IMAP_SMTP_connection
class TestOfflineAccount:
def test_wrong_db(self, tmpdir):
p = tmpdir.join("hello.db")
p.write("123")
with pytest.raises(ValueError):
Account(p.strpath)
def test_getinfo(self, acfactory):
ac1 = acfactory.get_unconfigured_account()
d = ac1.get_info()
@@ -145,6 +151,14 @@ class TestOfflineAccount:
assert not msg_state.is_out_delivered()
assert not msg_state.is_out_mdn_received()
def test_create_chat_by_mssage_id(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
msg = chat.send_text("msg1")
assert chat == ac1.create_chat_by_message(msg)
assert chat == ac1.create_chat_by_message(msg.id)
def test_message_image(self, acfactory, data, lp):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
@@ -180,6 +194,17 @@ class TestOfflineAccount:
assert msg.filename.endswith(msg.basename)
assert msg.filemime == typeout
def test_create_chat_mismatch(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
ac2 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
with pytest.raises(ValueError):
ac2.create_chat_by_contact(contact1)
chat1 = ac1.create_chat_by_contact(contact1)
msg = chat1.send_text("hello")
with pytest.raises(ValueError):
ac2.create_chat_by_message(msg)
def test_chat_message_distinctions(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
@@ -202,8 +227,81 @@ class TestOfflineAccount:
with pytest.raises(ValueError):
ac1.configure(addr="123@example.org")
def test_import_export_one_contact(self, acfactory, tmpdir):
backupdir = tmpdir.mkdir("backup")
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
# send a text message
msg = chat.send_text("msg1")
# send a binary file
bin = tmpdir.join("some.bin")
with bin.open("w") as f:
f.write("\00123" * 10000)
msg = chat.send_file(bin.strpath)
contact = msg.get_sender_contact()
assert contact == ac1.get_self_contact()
assert not backupdir.listdir()
path = ac1.export_to_dir(backupdir.strpath)
assert os.path.exists(path)
ac2 = acfactory.get_unconfigured_account()
ac2.import_from_file(path)
contacts = ac2.get_contacts(query="some1")
assert len(contacts) == 1
contact2 = contacts[0]
assert contact2.addr == "some1@hello.com"
chat2 = ac2.create_chat_by_contact(contact2)
messages = chat2.get_messages()
assert len(messages) == 2
assert messages[0].text == "msg1"
assert os.path.exists(messages[1].filename)
def test_ac_setup_message_fails(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
with pytest.raises(RuntimeError):
ac1.initiate_key_transfer()
class TestOnlineAccount:
def test_one_account_init(self, acfactory):
ac1 = acfactory.get_online_configuring_account()
wait_successful_IMAP_SMTP_connection(ac1)
wait_configuration_progress(ac1, 1000)
def test_one_account_send(self, acfactory):
ac1 = acfactory.get_online_configuring_account()
c2 = ac1.create_contact(email=ac1.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
wait_successful_IMAP_SMTP_connection(ac1)
wait_configuration_progress(ac1, 1000)
msg_out = chat.send_text("message2")
# wait for own account to receive
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[1] == msg_out.id
def test_two_acocunts_send_receive(self, acfactory):
ac1 = acfactory.get_online_configuring_account()
ac2 = acfactory.get_online_configuring_account()
c2 = ac1.create_contact(email=ac2.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
wait_successful_IMAP_SMTP_connection(ac1)
wait_configuration_progress(ac1, 1000)
wait_successful_IMAP_SMTP_connection(ac2)
wait_configuration_progress(ac2, 1000)
msg_out = chat.send_text("message1")
# wait for other account to receive
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
msg_in = ac2.get_message_by_id(msg_out.id)
assert msg_in.text == "message1"
def test_forward_messages(self, acfactory):
ac1 = acfactory.get_online_configuring_account()
ac2 = acfactory.get_online_configuring_account()
@@ -332,3 +430,44 @@ class TestOnlineAccount:
assert msg_in.view_type.is_image()
assert os.path.exists(msg_in.filename)
assert os.stat(msg_in.filename).st_size == os.stat(path).st_size
def test_import_export_online(self, acfactory, tmpdir):
backupdir = tmpdir.mkdir("backup")
ac1 = acfactory.get_online_configuring_account()
wait_configuration_progress(ac1, 1000)
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
chat.send_text("msg1")
path = ac1.export_to_dir(backupdir.strpath)
assert os.path.exists(path)
ac2 = acfactory.get_unconfigured_account()
ac2.import_from_file(path)
contacts = ac2.get_contacts(query="some1")
assert len(contacts) == 1
contact2 = contacts[0]
assert contact2.addr == "some1@hello.com"
chat2 = ac2.create_chat_by_contact(contact2)
messages = chat2.get_messages()
assert len(messages) == 1
assert messages[0].text == "msg1"
def test_ac_setup_message(self, acfactory):
# note that the receiving account needs to be configured and running
# before ther setup message is send. DC does not read old messages
# as of Jul2019
ac1 = acfactory.get_online_configuring_account()
ac2 = acfactory.clone_online_account(ac1)
wait_configuration_progress(ac2, 1000)
wait_configuration_progress(ac1, 1000)
assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"]
setup_code = ac1.initiate_key_transfer()
ac2._evlogger.set_timeout(10)
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
msg = ac2.get_message_by_id(ev[2])
assert msg.is_setup_message()
print("*************** Incoming ASM File at: ", msg.filename)
print("*************** Setup Code: ", setup_code)
msg.continue_key_transfer(setup_code)
assert ac1.get_info()["fingerprint"] == ac2.get_info()["fingerprint"]

View File

@@ -32,7 +32,11 @@ class TestInCreation:
chat2.add_contact(c2)
wait_msgs_changed(ac1, 0, 0) # why not chat id?
ac1.forward_messages([prepared_original], chat2)
# XXX there might be two EVENT_MSGS_CHANGED and only one of them
# is the one caused by forwarding
forwarded_id = wait_msgs_changed(ac1, chat2.id)
if forwarded_id == 0:
forwarded_id = wait_msgs_changed(ac1, chat2.id)
forwarded_msg = ac1.get_message_by_id(forwarded_id)
assert forwarded_msg.get_state().is_out_preparing()

View File

@@ -1,6 +1,8 @@
from __future__ import print_function
import pytest
from deltachat import capi, Account, const
from deltachat import capi, const, set_context_callback, clear_context_callback
from deltachat.capi import ffi
from deltachat.capi import lib
from deltachat.account import EventLogger
def test_empty_context():
@@ -8,10 +10,45 @@ def test_empty_context():
capi.lib.dc_close(ctx)
def test_callback_None2int():
ctx = capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL)
set_context_callback(ctx, lambda *args: None)
capi.lib.dc_close(ctx)
clear_context_callback(ctx)
def test_dc_close_events():
ctx = capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL)
evlog = EventLogger(ctx)
evlog.set_timeout(5)
set_context_callback(ctx, lambda ctx, evt_name, data1, data2: evlog(evt_name, data1, data2))
capi.lib.dc_close(ctx)
def find(info_string):
while 1:
ev = evlog.get_matching("DC_EVENT_INFO", check_error=False)
data2 = ev[2]
if info_string in data2:
return
else:
print("skipping event", *ev)
find("disconnecting INBOX-watch")
find("disconnecting sentbox-thread")
find("disconnecting mvbox-thread")
find("disconnecting SMTP")
find("Database closed")
def test_wrong_db(tmpdir):
tmpdir.join("hello.db").write("123")
with pytest.raises(ValueError):
Account(db_path=tmpdir.strpath)
dc_context = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
p = tmpdir.join("hello.db")
# write an invalid database file
p.write("x123" * 10)
assert not lib.dc_open(dc_context, p.strpath.encode("ascii"), ffi.NULL)
def test_event_defines():

View File

@@ -9,10 +9,12 @@ envlist =
[testenv]
commands =
pytest -rsXx {posargs:tests}
pip wheel . -w {toxworkdir}/wheelhouse
python tests/package_wheels.py {toxworkdir}/wheelhouse
passenv =
TRAVIS
DCC_RS_DEV
DCC_RS_TARGET
DCC_PY_LIVECONFIG
deps =
pytest
pytest-faulthandler
@@ -27,7 +29,6 @@ commands =
[testenv:lint]
skipsdist = True
usedevelop = True
basepython = python2.7
deps =
flake8
# pygments required by rst-lint
@@ -53,6 +54,7 @@ commands =
python_files = tests/test_*.py
norecursedirs = .tox
xfail_strict=true
timeout = 60
[flake8]
max-line-length = 120

3
release.toml Normal file
View File

@@ -0,0 +1,3 @@
pre-release-commit-message = "chore({{crate_name}}): release {{version}}"
pro-release-commit-message = "chore({{crate_name}}): starting development cycle for {{next_version}}"
no-dev-version = true

View File

@@ -23,11 +23,10 @@ if [ $? != 0 ]; then
fi
pushd python
toxargs="$@"
if [ -e liveconfig ]; then
toxargs="--liveconfig liveconfig $@"
if [ -e "./liveconfig" ]; then
export DCC_PY_LIVECONFIG=liveconfig
fi
tox $toxargs
tox "$@"
ret=$?
popd
exit $ret

View File

@@ -1 +1 @@
nightly-2019-06-16
nightly-2019-07-10

View File

@@ -1,5 +1,5 @@
use std::collections::BTreeMap;
use std::ffi::{CStr, CString};
use std::ffi::CStr;
use std::str::FromStr;
use std::{fmt, str};
@@ -7,6 +7,7 @@ use mmime::mailimf_types::*;
use crate::constants::*;
use crate::dc_contact::*;
use crate::dc_tools::as_str;
use crate::key::*;
/// Possible values for encryption preference
@@ -93,9 +94,7 @@ impl Aheader {
match Self::from_str(value) {
Ok(test) => {
// TODO: implement rust-safe version of dc_addr_cmp
let addr = CString::new(test.addr.clone()).unwrap();
if unsafe { dc_addr_cmp(addr.as_ptr(), wanted_from) } == 0 {
if dc_addr_cmp(&test.addr, as_str(wanted_from)) {
if fine_header.is_none() {
fine_header = Some(test);
} else {

182
src/config.rs Normal file
View File

@@ -0,0 +1,182 @@
use strum::{EnumProperty, IntoEnumIterator};
use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
use crate::constants::DC_VERSION_STR;
use crate::context::Context;
use crate::dc_job::*;
use crate::dc_stock::*;
use crate::dc_tools::*;
use crate::error::Error;
use crate::x::*;
/// The available configuration keys.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Display, EnumString, AsRefStr, EnumIter, EnumProperty,
)]
#[strum(serialize_all = "snake_case")]
pub enum Config {
Addr,
MailServer,
MailUser,
MailPw,
MailPort,
SendServer,
SendUser,
SendPw,
SendPort,
ServerFlags,
#[strum(props(default = "INBOX"))]
ImapFolder,
Displayname,
Selfstatus,
Selfavatar,
#[strum(props(default = "1"))]
E2eeEnabled,
#[strum(props(default = "1"))]
MdnsEnabled,
InboxWatch,
#[strum(props(default = "1"))]
SentboxWatch,
#[strum(props(default = "1"))]
MvboxWatch,
#[strum(props(default = "1"))]
MvboxMove,
#[strum(props(default = "0"))]
ShowEmails,
SaveMimeHeaders,
ConfiguredAddr,
ConfiguredMailServer,
ConfiguredMailUser,
ConfiguredMailPw,
ConfiguredMailPort,
ConfiguredSendServer,
ConfiguredSendUser,
ConfiguredSendPw,
ConfiguredSendPort,
ConfiguredServerFlags,
Configured,
// Deprecated
#[strum(serialize = "sys.version")]
SysVersion,
#[strum(serialize = "sys.msgsize_max_recommended")]
SysMsgsizeMaxRecommended,
#[strum(serialize = "sys.config_keys")]
SysConfigKeys,
}
impl Context {
/// Get a configuration key. Returns `None` if no value is set, and no default value found.
pub fn get_config(&self, key: Config) -> Option<String> {
let value = match key {
Config::Selfavatar => {
let rel_path = self.sql.get_config(self, key);
rel_path.map(|p| {
let v = unsafe {
let n = to_cstring(p);
let res = dc_get_abs_path(self, n);
free(n as *mut libc::c_void);
res
};
let r = to_string(v);
unsafe { free(v as *mut _) };
r
})
}
Config::SysVersion => Some(std::str::from_utf8(DC_VERSION_STR).unwrap().into()),
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
Config::SysConfigKeys => Some(get_config_keys_string()),
_ => self.sql.get_config(self, key),
};
if value.is_some() {
return value;
}
// Default values
match key {
Config::Selfstatus => {
let s = unsafe { dc_stock_str(self, 13) };
let res = to_string(s);
unsafe { free(s as *mut _) };
Some(res)
}
_ => key.get_str("default").map(|s| s.to_string()),
}
}
/// Set the given config key.
/// If `None` is passed as a value the value is cleared and set to the default if there is one.
pub fn set_config(&self, key: Config, value: Option<&str>) -> Result<(), Error> {
match key {
Config::Selfavatar if value.is_some() => {
let rel_path = std::fs::canonicalize(value.unwrap())?;
self.sql
.set_config(self, key, Some(&rel_path.to_string_lossy()))
}
Config::InboxWatch => {
let ret = self.sql.set_config(self, key, value);
unsafe { dc_interrupt_imap_idle(self) };
ret
}
Config::SentboxWatch => {
let ret = self.sql.set_config(self, key, value);
unsafe { dc_interrupt_sentbox_idle(self) };
ret
}
Config::MvboxWatch => {
let ret = self.sql.set_config(self, key, value);
unsafe { dc_interrupt_mvbox_idle(self) };
ret
}
Config::Selfstatus => {
let def = unsafe { dc_stock_str(self, 13) };
let val = if value.is_none() || value.unwrap() == as_str(def) {
None
} else {
value
};
let ret = self.sql.set_config(self, key, val);
unsafe { free(def as *mut libc::c_void) };
ret
}
_ => self.sql.set_config(self, key, value),
}
}
}
/// Returns all available configuration keys concated together.
fn get_config_keys_string() -> String {
let keys = Config::iter().fold(String::new(), |mut acc, key| {
acc += key.as_ref();
acc += " ";
acc
});
format!(" {} ", keys)
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
use std::string::ToString;
#[test]
fn test_to_string() {
assert_eq!(Config::MailServer.to_string(), "mail_server");
assert_eq!(Config::from_str("mail_server"), Ok(Config::MailServer));
assert_eq!(Config::SysConfigKeys.to_string(), "sys.config_keys");
assert_eq!(
Config::from_str("sys.config_keys"),
Ok(Config::SysConfigKeys)
);
}
#[test]
fn test_default_prop() {
assert_eq!(Config::ImapFolder.get_str("default"), Some("INBOX"));
}
}

View File

@@ -1,6 +1,6 @@
//! Constants
pub const DC_VERSION_STR: &'static [u8; 14] = b"1.0.0-alpha.1\x00";
pub const DC_VERSION_STR: &'static [u8; 14] = b"1.0.0-alpha.3\x00";
pub const DC_MOVE_STATE_MOVING: u32 = 3;
pub const DC_MOVE_STATE_STAY: u32 = 2;
@@ -80,9 +80,9 @@ pub const DC_STATE_OUT_FAILED: usize = 24;
pub const DC_STATE_OUT_DELIVERED: usize = 26;
pub const DC_STATE_OUT_MDN_RCVD: usize = 28;
/// approx. max. lenght returned by dc_msg_get_text()
/// approx. max. length returned by dc_msg_get_text()
pub const DC_MAX_GET_TEXT_LEN: usize = 30000;
/// approx. max. lenght returned by dc_get_msg_info()
/// approx. max. length returned by dc_get_msg_info()
pub const DC_MAX_GET_INFO_LEN: usize = 100000;
pub const DC_CONTACT_ID_SELF: usize = 1;
@@ -119,7 +119,7 @@ pub const DC_MSG_AUDIO: usize = 40;
/// A voice message that was directly recorded by the user.
/// For all other audio messages, the type #DC_MSG_AUDIO should be used.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retieved via dc_msg_get_file(), dc_msg_get_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration()
pub const DC_MSG_VOICE: usize = 41;
/// Video messages.
@@ -173,12 +173,12 @@ pub const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_SMTP_SOCKET_PLAIN: usize = 0x40000;
/// if none of these flags are set, the default is choosen
/// if none of these flags are set, the default is chosen
pub const DC_LP_AUTH_FLAGS: usize = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
/// if none of these flags are set, the default is choosen
/// if none of these flags are set, the default is chosen
pub const DC_LP_IMAP_SOCKET_FLAGS: usize =
(DC_LP_IMAP_SOCKET_STARTTLS | DC_LP_IMAP_SOCKET_SSL | DC_LP_IMAP_SOCKET_PLAIN);
/// if none of these flags are set, the default is choosen
/// if none of these flags are set, the default is chosen
pub const DC_LP_SMTP_SOCKET_FLAGS: usize =
(DC_LP_SMTP_SOCKET_STARTTLS | DC_LP_SMTP_SOCKET_SSL | DC_LP_SMTP_SOCKET_PLAIN);
@@ -237,7 +237,7 @@ pub enum Event {
/// The library-user should report an error to the end-user.
/// Passed to the callback given to dc_context_new().
///
/// As most things are asynchrounous, things may go wrong at any time and the user
/// As most things are asynchronous, things may go wrong at any time and the user
/// should not be disturbed by a dialog or so. Instead, use a bubble or so.
///
/// However, for ongoing processes (eg. dc_configure())
@@ -265,7 +265,7 @@ pub enum Event {
///
/// Moreover, if the UI detects that the device is offline,
/// it is probably more useful to report this to the user
/// instread of the string from data2.
/// instead of the string from data2.
///
/// @param data1 (int) 1=first/new network error, should be reported the user;
/// 0=subsequent network error, should be logged only
@@ -483,9 +483,20 @@ pub const DC_STR_MSGLOCATIONDISABLED: usize = 65;
pub const DC_STR_LOCATION: usize = 66;
pub const DC_STR_COUNT: usize = 66;
pub const DC_JOB_DELETE_MSG_ON_IMAP: i32 = 110;
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
#[repr(u8)]
pub enum KeyType {
Public = 0,
Private = 1,
}
pub const DC_CMD_GROUPNAME_CHANGED: libc::c_int = 2;
pub const DC_CMD_GROUPIMAGE_CHANGED: libc::c_int = 3;
pub const DC_CMD_MEMBER_ADDED_TO_GROUP: libc::c_int = 4;
pub const DC_CMD_MEMBER_REMOVED_FROM_GROUP: libc::c_int = 5;
pub const DC_CMD_AUTOCRYPT_SETUP_MESSAGE: libc::c_int = 6;
pub const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
pub const DC_CMD_LOCATION_STREAMING_ENABLED: libc::c_int = 8;
pub const DC_CMD_LOCATION_ONLY: libc::c_int = 9;

View File

@@ -6,18 +6,16 @@ use crate::dc_chat::*;
use crate::dc_contact::*;
use crate::dc_job::*;
use crate::dc_jobthread::*;
use crate::dc_log::*;
use crate::dc_loginparam::*;
use crate::dc_lot::dc_lot_t;
use crate::dc_move::*;
use crate::dc_msg::*;
use crate::dc_receive_imf::*;
use crate::dc_sqlite3::*;
use crate::dc_stock::*;
use crate::dc_tools::*;
use crate::imap::*;
use crate::key::*;
use crate::smtp::*;
use crate::sql::Sql;
use crate::types::*;
use crate::x::*;
@@ -26,7 +24,7 @@ pub struct Context {
pub userdata: *mut libc::c_void,
pub dbfile: Arc<RwLock<*mut libc::c_char>>,
pub blobdir: Arc<RwLock<*mut libc::c_char>>,
pub sql: SQLite,
pub sql: Sql,
pub inbox: Arc<RwLock<Imap>>,
pub perform_inbox_jobs_needed: Arc<RwLock<i32>>,
pub probe_imap_network: Arc<RwLock<i32>>,
@@ -150,37 +148,33 @@ pub fn dc_context_new(
cb,
os_name: unsafe { dc_strdup_keep_null(os_name) },
running_state: Arc::new(RwLock::new(Default::default())),
sql: SQLite::new(),
sql: Sql::new(),
smtp: Arc::new(Mutex::new(Smtp::new())),
smtp_state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
oauth2_critical: Arc::new(Mutex::new(())),
bob: Arc::new(RwLock::new(Default::default())),
last_smeared_timestamp: Arc::new(RwLock::new(0)),
cmdline_sel_chat_id: Arc::new(RwLock::new(0)),
sentbox_thread: Arc::new(RwLock::new(unsafe {
dc_jobthread_init(
b"SENTBOX\x00" as *const u8 as *const libc::c_char,
b"configured_sentbox_folder\x00" as *const u8 as *const libc::c_char,
Imap::new(
cb_get_config,
cb_set_config,
cb_precheck_imf,
cb_receive_imf,
),
)
})),
mvbox_thread: Arc::new(RwLock::new(unsafe {
dc_jobthread_init(
b"MVBOX\x00" as *const u8 as *const libc::c_char,
b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char,
Imap::new(
cb_get_config,
cb_set_config,
cb_precheck_imf,
cb_receive_imf,
),
)
})),
sentbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
"SENTBOX",
"configured_sentbox_folder",
Imap::new(
cb_get_config,
cb_set_config,
cb_precheck_imf,
cb_receive_imf,
),
))),
mvbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
"MVBOX",
"configured_mvbox_folder",
Imap::new(
cb_get_config,
cb_set_config,
cb_precheck_imf,
cb_receive_imf,
),
))),
probe_imap_network: Arc::new(RwLock::new(0)),
perform_inbox_jobs_needed: Arc::new(RwLock::new(0)),
}
@@ -190,7 +184,7 @@ unsafe fn cb_receive_imf(
context: &Context,
imf_raw_not_terminated: *const libc::c_char,
imf_raw_bytes: size_t,
server_folder: *const libc::c_char,
server_folder: &str,
server_uid: uint32_t,
flags: uint32_t,
) {
@@ -207,7 +201,7 @@ unsafe fn cb_receive_imf(
unsafe fn cb_precheck_imf(
context: &Context,
rfc724_mid: *const libc::c_char,
server_folder: *const libc::c_char,
server_folder: &str,
server_uid: uint32_t,
) -> libc::c_int {
let mut rfc724_mid_exists: libc::c_int = 0i32;
@@ -226,23 +220,23 @@ unsafe fn cb_precheck_imf(
if *old_server_folder.offset(0isize) as libc::c_int == 0i32
&& old_server_uid == 0i32 as libc::c_uint
{
dc_log_info(
info!(
context,
0i32,
b"[move] detected bbc-self %s\x00" as *const u8 as *const libc::c_char,
rfc724_mid,
0,
"[move] detected bbc-self {}",
as_str(rfc724_mid),
);
mark_seen = 1i32
} else if strcmp(old_server_folder, server_folder) != 0i32 {
dc_log_info(
} else if as_str(old_server_folder) != server_folder {
info!(
context,
0i32,
b"[move] detected moved message %s\x00" as *const u8 as *const libc::c_char,
rfc724_mid,
0,
"[move] detected moved message {}",
as_str(rfc724_mid),
);
dc_update_msg_move_state(context, rfc724_mid, DC_MOVE_STATE_STAY);
}
if strcmp(old_server_folder, server_folder) != 0i32 || old_server_uid != server_uid {
if as_str(old_server_folder) != server_folder || old_server_uid != server_uid {
dc_update_server_uid(context, rfc724_mid, server_folder, server_uid);
}
dc_do_heuristics_moves(context, server_folder, msg_id);
@@ -261,7 +255,12 @@ unsafe fn cb_precheck_imf(
}
unsafe fn cb_set_config(context: &Context, key: *const libc::c_char, value: *const libc::c_char) {
dc_sqlite3_set_config(context, &context.sql, key, value);
let v = if value.is_null() {
None
} else {
Some(as_str(value))
};
context.sql.set_config(context, as_str(key), v).ok();
}
/* *
@@ -276,7 +275,11 @@ unsafe fn cb_get_config(
key: *const libc::c_char,
def: *const libc::c_char,
) -> *mut libc::c_char {
dc_sqlite3_get_config(context, &context.sql, key, def)
let res = context
.sql
.get_config(context, as_str(key))
.unwrap_or_else(|| to_string(def));
to_cstring(res)
}
pub unsafe fn dc_context_unref(context: &mut Context) {
@@ -284,20 +287,20 @@ pub unsafe fn dc_context_unref(context: &mut Context) {
dc_close(context);
}
dc_jobthread_exit(&mut context.sentbox_thread.clone().write().unwrap());
dc_jobthread_exit(&mut context.mvbox_thread.clone().write().unwrap());
free(context.os_name as *mut libc::c_void);
}
pub unsafe fn dc_close(context: &Context) {
info!(context, 0, "disconnecting INBOX-watch",);
context.inbox.read().unwrap().disconnect(context);
info!(context, 0, "disconnecting sentbox-thread",);
context
.sentbox_thread
.read()
.unwrap()
.imap
.disconnect(context);
info!(context, 0, "disconnecting mvbox-thread",);
context
.mvbox_thread
.read()
@@ -305,6 +308,7 @@ pub unsafe fn dc_close(context: &Context) {
.imap
.disconnect(context);
info!(context, 0, "disconnecting SMTP");
context.smtp.clone().lock().unwrap().disconnect();
context.sql.close(context);
@@ -362,358 +366,88 @@ pub unsafe fn dc_get_blobdir(context: &Context) -> *mut libc::c_char {
dc_strdup(*context.blobdir.clone().read().unwrap())
}
pub unsafe fn dc_set_config(
context: &Context,
key: *const libc::c_char,
value: *const libc::c_char,
) -> libc::c_int {
let mut ret = 0;
let mut rel_path = 0 as *mut libc::c_char;
if key.is_null() || 0 == is_settable_config_key(key) {
return 0;
}
if strcmp(key, b"selfavatar\x00" as *const u8 as *const libc::c_char) == 0 && !value.is_null() {
rel_path = dc_strdup(value);
if !(0 == dc_make_rel_and_copy(context, &mut rel_path)) {
ret = dc_sqlite3_set_config(context, &context.sql, key, rel_path)
}
} else if strcmp(key, b"inbox_watch\x00" as *const u8 as *const libc::c_char) == 0 {
ret = dc_sqlite3_set_config(context, &context.sql, key, value);
dc_interrupt_imap_idle(context);
} else if strcmp(
key,
b"sentbox_watch\x00" as *const u8 as *const libc::c_char,
) == 0
{
ret = dc_sqlite3_set_config(context, &context.sql, key, value);
dc_interrupt_sentbox_idle(context);
} else if strcmp(key, b"mvbox_watch\x00" as *const u8 as *const libc::c_char) == 0 {
ret = dc_sqlite3_set_config(context, &context.sql, key, value);
dc_interrupt_mvbox_idle(context);
} else if strcmp(key, b"selfstatus\x00" as *const u8 as *const libc::c_char) == 0 {
let def = dc_stock_str(context, 13);
ret = dc_sqlite3_set_config(
context,
&context.sql,
key,
if value.is_null() || strcmp(value, def) == 0 {
0 as *const libc::c_char
} else {
value
},
);
free(def as *mut libc::c_void);
} else {
ret = dc_sqlite3_set_config(context, &context.sql, key, value);
}
free(rel_path as *mut libc::c_void);
ret
}
/* ******************************************************************************
* INI-handling, Information
******************************************************************************/
unsafe fn is_settable_config_key(key: *const libc::c_char) -> libc::c_int {
let mut i = 0;
while i
< (::std::mem::size_of::<[*const libc::c_char; 33]>())
.wrapping_div(::std::mem::size_of::<*mut libc::c_char>())
{
if strcmp(key, config_keys[i as usize]) == 0 {
return 1;
}
i += 1
}
0
}
static mut config_keys: [*const libc::c_char; 33] = [
b"addr\x00" as *const u8 as *const libc::c_char,
b"mail_server\x00" as *const u8 as *const libc::c_char,
b"mail_user\x00" as *const u8 as *const libc::c_char,
b"mail_pw\x00" as *const u8 as *const libc::c_char,
b"mail_port\x00" as *const u8 as *const libc::c_char,
b"send_server\x00" as *const u8 as *const libc::c_char,
b"send_user\x00" as *const u8 as *const libc::c_char,
b"send_pw\x00" as *const u8 as *const libc::c_char,
b"send_port\x00" as *const u8 as *const libc::c_char,
b"server_flags\x00" as *const u8 as *const libc::c_char,
b"imap_folder\x00" as *const u8 as *const libc::c_char,
b"displayname\x00" as *const u8 as *const libc::c_char,
b"selfstatus\x00" as *const u8 as *const libc::c_char,
b"selfavatar\x00" as *const u8 as *const libc::c_char,
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
b"mdns_enabled\x00" as *const u8 as *const libc::c_char,
b"inbox_watch\x00" as *const u8 as *const libc::c_char,
b"sentbox_watch\x00" as *const u8 as *const libc::c_char,
b"mvbox_watch\x00" as *const u8 as *const libc::c_char,
b"mvbox_move\x00" as *const u8 as *const libc::c_char,
b"show_emails\x00" as *const u8 as *const libc::c_char,
b"save_mime_headers\x00" as *const u8 as *const libc::c_char,
b"configured_addr\x00" as *const u8 as *const libc::c_char,
b"configured_mail_server\x00" as *const u8 as *const libc::c_char,
b"configured_mail_user\x00" as *const u8 as *const libc::c_char,
b"configured_mail_pw\x00" as *const u8 as *const libc::c_char,
b"configured_mail_port\x00" as *const u8 as *const libc::c_char,
b"configured_send_server\x00" as *const u8 as *const libc::c_char,
b"configured_send_user\x00" as *const u8 as *const libc::c_char,
b"configured_send_pw\x00" as *const u8 as *const libc::c_char,
b"configured_send_port\x00" as *const u8 as *const libc::c_char,
b"configured_server_flags\x00" as *const u8 as *const libc::c_char,
b"configured\x00" as *const u8 as *const libc::c_char,
];
pub unsafe fn dc_get_config(context: &Context, key: *const libc::c_char) -> *mut libc::c_char {
let mut value = 0 as *mut libc::c_char;
if !key.is_null()
&& *key.offset(0isize) as libc::c_int == 's' as i32
&& *key.offset(1isize) as libc::c_int == 'y' as i32
&& *key.offset(2isize) as libc::c_int == 's' as i32
&& *key.offset(3isize) as libc::c_int == '.' as i32
{
return get_sys_config_str(key);
}
if key.is_null() || 0 == is_gettable_config_key(key) {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
if strcmp(key, b"selfavatar\x00" as *const u8 as *const libc::c_char) == 0 {
let rel_path: *mut libc::c_char =
dc_sqlite3_get_config(context, &context.sql, key, 0 as *const libc::c_char);
if !rel_path.is_null() {
value = dc_get_abs_path(context, rel_path);
free(rel_path as *mut libc::c_void);
}
} else {
value = dc_sqlite3_get_config(context, &context.sql, key, 0 as *const libc::c_char)
}
if value.is_null() {
if strcmp(key, b"e2ee_enabled\x00" as *const u8 as *const libc::c_char) == 0 {
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
} else if strcmp(key, b"mdns_enabled\x00" as *const u8 as *const libc::c_char) == 0 {
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
} else if strcmp(key, b"imap_folder\x00" as *const u8 as *const libc::c_char) == 0 {
value = dc_strdup(b"INBOX\x00" as *const u8 as *const libc::c_char)
} else if strcmp(key, b"inbox_watch\x00" as *const u8 as *const libc::c_char) == 0 {
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
} else if strcmp(
key,
b"sentbox_watch\x00" as *const u8 as *const libc::c_char,
) == 0
{
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
} else if strcmp(key, b"mvbox_watch\x00" as *const u8 as *const libc::c_char) == 0 {
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
} else if strcmp(key, b"mvbox_move\x00" as *const u8 as *const libc::c_char) == 0 {
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
} else if strcmp(key, b"show_emails\x00" as *const u8 as *const libc::c_char) == 0 {
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 0)
} else if strcmp(key, b"selfstatus\x00" as *const u8 as *const libc::c_char) == 0 {
value = dc_stock_str(context, 13)
} else {
value = dc_mprintf(b"\x00" as *const u8 as *const libc::c_char)
}
}
value
}
unsafe fn is_gettable_config_key(key: *const libc::c_char) -> libc::c_int {
let mut i = 0;
while i
< (::std::mem::size_of::<[*const libc::c_char; 3]>())
.wrapping_div(::std::mem::size_of::<*mut libc::c_char>())
{
if strcmp(key, sys_config_keys[i as usize]) == 0 {
return 1;
}
i += 1
}
is_settable_config_key(key)
}
// deprecated
static mut sys_config_keys: [*const libc::c_char; 3] = [
b"sys.version\x00" as *const u8 as *const libc::c_char,
b"sys.msgsize_max_recommended\x00" as *const u8 as *const libc::c_char,
b"sys.config_keys\x00" as *const u8 as *const libc::c_char,
];
unsafe fn get_sys_config_str(key: *const libc::c_char) -> *mut libc::c_char {
if strcmp(key, b"sys.version\x00" as *const u8 as *const libc::c_char) == 0 {
return dc_strdup(DC_VERSION_STR as *const u8 as *const libc::c_char);
} else if strcmp(
key,
b"sys.msgsize_max_recommended\x00" as *const u8 as *const libc::c_char,
) == 0
{
return dc_mprintf(
b"%i\x00" as *const u8 as *const libc::c_char,
24 * 1024 * 1024 / 4 * 3,
);
} else if strcmp(
key,
b"sys.config_keys\x00" as *const u8 as *const libc::c_char,
) == 0
{
return get_config_keys_str();
} else {
return dc_strdup(0 as *const libc::c_char);
};
}
unsafe fn get_config_keys_str() -> *mut libc::c_char {
let mut ret = String::new();
let mut i = 0;
while i
< (::std::mem::size_of::<[*const libc::c_char; 33]>())
.wrapping_div(::std::mem::size_of::<*mut libc::c_char>())
{
if !ret.is_empty() {
ret += " ";
}
ret += &to_string(config_keys[i as usize]);
i += 1
}
let mut i = 0;
while i
< (::std::mem::size_of::<[*const libc::c_char; 3]>())
.wrapping_div(::std::mem::size_of::<*mut libc::c_char>())
{
if !ret.is_empty() {
ret += " ";
}
ret += &to_string(sys_config_keys[i as usize]);
i += 1
}
strdup(to_cstring(ret).as_ptr())
}
pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
let unset = "0";
let l = dc_loginparam_new();
let l2 = dc_loginparam_new();
dc_loginparam_read(
context,
l,
&context.sql,
b"\x00" as *const u8 as *const libc::c_char,
);
dc_loginparam_read(
context,
l2,
&context.sql,
b"configured_\x00" as *const u8 as *const libc::c_char,
);
let displayname = dc_sqlite3_get_config(
context,
&context.sql,
b"displayname\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
let l = dc_loginparam_read(context, &context.sql, "");
let l2 = dc_loginparam_read(context, &context.sql, "configured_");
let displayname = context.sql.get_config(context, "displayname");
let chats = dc_get_chat_cnt(context) as usize;
let real_msgs = dc_get_real_msg_cnt(context) as usize;
let deaddrop_msgs = dc_get_deaddrop_msg_cnt(context) as usize;
let contacts = dc_get_real_contact_cnt(context) as usize;
let is_configured = dc_sqlite3_get_config_int(
context,
&context.sql,
b"configured\x00" as *const u8 as *const libc::c_char,
0,
);
let dbversion = dc_sqlite3_get_config_int(
context,
&context.sql,
b"dbversion\x00" as *const u8 as *const libc::c_char,
0,
);
let e2ee_enabled = dc_sqlite3_get_config_int(
context,
&context.sql,
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
1,
);
let mdns_enabled = dc_sqlite3_get_config_int(
context,
&context.sql,
b"mdns_enabled\x00" as *const u8 as *const libc::c_char,
1,
);
let mut stmt = dc_sqlite3_prepare(
context,
&context.sql,
b"SELECT COUNT(*) FROM keypairs;\x00" as *const u8 as *const libc::c_char,
);
sqlite3_step(stmt);
let prv_key_cnt = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
stmt = dc_sqlite3_prepare(
context,
&context.sql,
b"SELECT COUNT(*) FROM acpeerstates;\x00" as *const u8 as *const libc::c_char,
);
sqlite3_step(stmt);
let pub_key_cnt = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
let fingerprint_str =
if let Some(key) = Key::from_self_public(context, (*l2).addr, &context.sql) {
key.fingerprint()
} else {
"<Not yet calculated>".into()
};
let is_configured = context
.sql
.get_config_int(context, "configured")
.unwrap_or_default();
let dbversion = context
.sql
.get_config_int(context, "dbversion")
.unwrap_or_default();
let e2ee_enabled = context
.sql
.get_config_int(context, "e2ee_enabled")
.unwrap_or_else(|| 1);
let mdns_enabled = context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1);
let l_readable_str = dc_loginparam_get_readable(l);
let l2_readable_str = dc_loginparam_get_readable(l2);
let inbox_watch = dc_sqlite3_get_config_int(
let prv_key_cnt: Option<isize> = context.sql.query_row_col(
context,
&context.sql,
b"inbox_watch\x00" as *const u8 as *const libc::c_char,
1,
);
let sentbox_watch = dc_sqlite3_get_config_int(
context,
&context.sql,
b"sentbox_watch\x00" as *const u8 as *const libc::c_char,
1,
);
let mvbox_watch = dc_sqlite3_get_config_int(
context,
&context.sql,
b"mvbox_watch\x00" as *const u8 as *const libc::c_char,
1,
);
let mvbox_move = dc_sqlite3_get_config_int(
context,
&context.sql,
b"mvbox_move\x00" as *const u8 as *const libc::c_char,
1,
);
let folders_configured = dc_sqlite3_get_config_int(
context,
&context.sql,
b"folders_configured\x00" as *const u8 as *const libc::c_char,
"SELECT COUNT(*) FROM keypairs;",
rusqlite::NO_PARAMS,
0,
);
let configured_sentbox_folder = dc_sqlite3_get_config(
let pub_key_cnt: Option<isize> = context.sql.query_row_col(
context,
&context.sql,
b"configured_sentbox_folder\x00" as *const u8 as *const libc::c_char,
b"<unset>\x00" as *const u8 as *const libc::c_char,
);
let configured_mvbox_folder = dc_sqlite3_get_config(
context,
&context.sql,
b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char,
b"<unset>\x00" as *const u8 as *const libc::c_char,
"SELECT COUNT(*) FROM acpeerstates;",
rusqlite::NO_PARAMS,
0,
);
let fingerprint_str = if let Some(key) = Key::from_self_public(context, &l2.addr, &context.sql)
{
key.fingerprint()
} else {
"<Not yet calculated>".into()
};
let l_readable_str = dc_loginparam_get_readable(&l);
let l2_readable_str = dc_loginparam_get_readable(&l2);
let inbox_watch = context
.sql
.get_config_int(context, "inbox_watch")
.unwrap_or_else(|| 1);
let sentbox_watch = context
.sql
.get_config_int(context, "sentbox_watch")
.unwrap_or_else(|| 1);
let mvbox_watch = context
.sql
.get_config_int(context, "mvbox_watch")
.unwrap_or_else(|| 1);
let mvbox_move = context
.sql
.get_config_int(context, "mvbox_move")
.unwrap_or_else(|| 1);
let folders_configured = context
.sql
.get_config_int(context, "folders_configured")
.unwrap_or_default();
let configured_sentbox_folder = context
.sql
.get_config(context, "configured_sentbox_folder")
.unwrap_or_else(|| "<unset>".to_string());
let configured_mvbox_folder = context
.sql
.get_config(context, "configured_mvbox_folder")
.unwrap_or_else(|| "<unset>".to_string());
let res = format!(
"deltachat_core_version=v{}\n\
sqlite_version={}\n\
@@ -744,7 +478,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
fingerprint={}\n\
level=awesome\n",
as_str(DC_VERSION_STR as *const u8 as *const _),
as_str(libsqlite3_sys::SQLITE_VERSION as *const u8 as *const libc::c_char),
rusqlite::version(),
sqlite3_threadsafe(),
// arch
(::std::mem::size_of::<*mut libc::c_void>()).wrapping_mul(8),
@@ -763,192 +497,131 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
} else {
unset
},
if !displayname.is_null() {
as_str(displayname)
} else {
unset
},
displayname.unwrap_or_else(|| unset.into()),
is_configured,
as_str(l_readable_str),
as_str(l2_readable_str),
l_readable_str,
l2_readable_str,
inbox_watch,
sentbox_watch,
mvbox_watch,
mvbox_move,
folders_configured,
as_str(configured_sentbox_folder),
as_str(configured_mvbox_folder),
configured_sentbox_folder,
configured_mvbox_folder,
mdns_enabled,
e2ee_enabled,
prv_key_cnt,
pub_key_cnt,
prv_key_cnt.unwrap_or_default(),
pub_key_cnt.unwrap_or_default(),
fingerprint_str,
);
dc_loginparam_unref(l);
dc_loginparam_unref(l2);
free(displayname as *mut libc::c_void);
free(l_readable_str as *mut libc::c_void);
free(l2_readable_str as *mut libc::c_void);
free(configured_sentbox_folder as *mut libc::c_void);
free(configured_mvbox_folder as *mut libc::c_void);
strdup(to_cstring(res).as_ptr())
to_cstring(res)
}
pub unsafe fn dc_get_version_str() -> *mut libc::c_char {
dc_strdup(DC_VERSION_STR as *const u8 as *const libc::c_char)
}
pub unsafe fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t {
pub fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t {
let show_deaddrop = 0;
let ret = dc_array_new(128 as size_t);
let mut stmt = 0 as *mut sqlite3_stmt;
if !ret.is_null() {
stmt = dc_sqlite3_prepare(
context,
&context.sql,
b"SELECT m.id FROM msgs m LEFT JOIN contacts ct \
ON m.from_id=ct.id LEFT JOIN chats c ON m.chat_id=c.id WHERE m.state=? \
AND m.hidden=0 \
AND m.chat_id>? \
AND ct.blocked=0 \
AND (c.blocked=0 OR c.blocked=?) ORDER BY m.timestamp DESC,m.id DESC;\x00"
as *const u8 as *const libc::c_char,
);
sqlite3_bind_int(stmt, 1, 10);
sqlite3_bind_int(stmt, 2, 9);
sqlite3_bind_int(stmt, 3, if 0 != show_deaddrop { 2 } else { 0 });
while sqlite3_step(stmt) == 100 {
dc_array_add_id(ret, sqlite3_column_int(stmt, 0) as uint32_t);
}
}
sqlite3_finalize(stmt);
ret
context
.sql
.query_map(
"SELECT m.id FROM msgs m LEFT JOIN contacts ct \
ON m.from_id=ct.id LEFT JOIN chats c ON m.chat_id=c.id WHERE m.state=? \
AND m.hidden=0 \
AND m.chat_id>? \
AND ct.blocked=0 \
AND (c.blocked=0 OR c.blocked=?) ORDER BY m.timestamp DESC,m.id DESC;",
&[10, 9, if 0 != show_deaddrop { 2 } else { 0 }],
|row| row.get(0),
|rows| {
let ret = unsafe { dc_array_new(128 as size_t) };
for row in rows {
let id = row?;
unsafe { dc_array_add_id(ret, id) };
}
Ok(ret)
},
)
.unwrap()
}
pub unsafe fn dc_search_msgs(
pub fn dc_search_msgs(
context: &Context,
chat_id: uint32_t,
query: *const libc::c_char,
) -> *mut dc_array_t {
let mut success = 0;
let ret = dc_array_new(100 as size_t);
let mut strLikeInText = 0 as *mut libc::c_char;
let mut strLikeBeg = 0 as *mut libc::c_char;
let mut real_query = 0 as *mut libc::c_char;
let mut stmt = 0 as *mut sqlite3_stmt;
if !(ret.is_null() || query.is_null()) {
real_query = dc_strdup(query);
dc_trim(real_query);
if *real_query.offset(0isize) as libc::c_int == 0 {
success = 1
} else {
strLikeInText = dc_mprintf(
b"%%%s%%\x00" as *const u8 as *const libc::c_char,
real_query,
);
strLikeBeg = dc_mprintf(b"%s%%\x00" as *const u8 as *const libc::c_char, real_query);
if 0 != chat_id {
stmt = dc_sqlite3_prepare(
context,
&context.sql,
b"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.chat_id=? \
AND m.hidden=0 \
AND ct.blocked=0 AND (txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp,m.id;\x00"
as *const u8 as *const libc::c_char
);
sqlite3_bind_int(stmt, 1, chat_id as libc::c_int);
sqlite3_bind_text(stmt, 2, strLikeInText, -1, None);
sqlite3_bind_text(stmt, 3, strLikeBeg, -1, None);
} else {
let show_deaddrop = 0;
stmt = dc_sqlite3_prepare(
context,
&context.sql,
b"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id \
LEFT JOIN chats c ON m.chat_id=c.id WHERE m.chat_id>9 AND m.hidden=0 \
AND (c.blocked=0 OR c.blocked=?) \
AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;\x00"
as *const u8 as *const libc::c_char
);
sqlite3_bind_int(stmt, 1, if 0 != show_deaddrop { 2 } else { 0 });
sqlite3_bind_text(stmt, 2, strLikeInText, -1, None);
sqlite3_bind_text(stmt, 3, strLikeBeg, -1, None);
}
while sqlite3_step(stmt) == 100 {
dc_array_add_id(ret, sqlite3_column_int(stmt, 0) as uint32_t);
}
success = 1
}
if query.is_null() {
return std::ptr::null_mut();
}
free(strLikeInText as *mut libc::c_void);
free(strLikeBeg as *mut libc::c_void);
free(real_query as *mut libc::c_void);
sqlite3_finalize(stmt);
let real_query = to_string(query).trim().to_string();
if real_query.is_empty() {
return std::ptr::null_mut();
}
let strLikeInText = format!("%{}%", &real_query);
let strLikeBeg = format!("{}%", &real_query);
if 0 != success {
ret
let query = if 0 != chat_id {
"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.chat_id=? \
AND m.hidden=0 \
AND ct.blocked=0 AND (txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp,m.id;"
} else {
if !ret.is_null() {
dc_array_unref(ret);
}
0 as *mut dc_array_t
"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id \
LEFT JOIN chats c ON m.chat_id=c.id WHERE m.chat_id>9 AND m.hidden=0 \
AND (c.blocked=0 OR c.blocked=?) \
AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;"
};
let ret = unsafe { dc_array_new(100 as size_t) };
let success = context
.sql
.query_map(
query,
params![chat_id as libc::c_int, &strLikeInText, &strLikeBeg],
|row| row.get::<_, i32>(0),
|rows| {
for id in rows {
unsafe { dc_array_add_id(ret, id? as u32) };
}
Ok(())
},
)
.is_ok();
if success {
return ret;
}
if !ret.is_null() {
unsafe { dc_array_unref(ret) };
}
std::ptr::null_mut()
}
pub fn dc_is_inbox(_context: &Context, folder_name: impl AsRef<str>) -> bool {
folder_name.as_ref() == "INBOX"
}
pub fn dc_is_sentbox(context: &Context, folder_name: impl AsRef<str>) -> bool {
let sentbox_name = context.sql.get_config(context, "configured_sentbox_folder");
if let Some(name) = sentbox_name {
name == folder_name.as_ref()
} else {
false
}
}
pub unsafe fn dc_is_inbox(_context: &Context, folder_name: *const libc::c_char) -> libc::c_int {
let mut is_inbox = 0;
if !folder_name.is_null() {
is_inbox = if strcasecmp(
b"INBOX\x00" as *const u8 as *const libc::c_char,
folder_name,
) == 0
{
1
} else {
0
}
}
is_inbox
}
pub fn dc_is_mvbox(context: &Context, folder_name: impl AsRef<str>) -> bool {
let mvbox_name = context.sql.get_config(context, "configured_mvbox_folder");
pub unsafe fn dc_is_sentbox(context: &Context, folder_name: *const libc::c_char) -> libc::c_int {
let sentbox_name = dc_sqlite3_get_config(
context,
&context.sql,
b"configured_sentbox_folder\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
let mut is_sentbox = 0;
if !sentbox_name.is_null() && !folder_name.is_null() {
is_sentbox = if strcasecmp(sentbox_name, folder_name) == 0 {
1
} else {
0
}
if let Some(name) = mvbox_name {
name == folder_name.as_ref()
} else {
false
}
free(sentbox_name as *mut libc::c_void);
is_sentbox
}
pub unsafe fn dc_is_mvbox(context: &Context, folder_name: *const libc::c_char) -> libc::c_int {
let mvbox_name = dc_sqlite3_get_config(
context,
&context.sql,
b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
let mut is_mvbox = 0;
if !mvbox_name.is_null() && !folder_name.is_null() {
is_mvbox = if strcasecmp(mvbox_name, folder_name) == 0 {
1
} else {
0
}
}
free(mvbox_name as *mut libc::c_void);
is_mvbox
}

View File

@@ -3,6 +3,8 @@ use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
const DC_ARRAY_MAGIC: uint32_t = 0x000a11aa;
/* * the structure behind dc_array_t */
#[derive(Copy, Clone)]
#[repr(C)]
@@ -23,7 +25,7 @@ pub struct dc_array_t {
* To free an array object, use dc_array_unref().
*/
pub unsafe fn dc_array_unref(mut array: *mut dc_array_t) {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return;
}
if (*array).type_0 == 1i32 {
@@ -35,7 +37,7 @@ pub unsafe fn dc_array_unref(mut array: *mut dc_array_t) {
}
pub unsafe fn dc_array_free_ptr(array: *mut dc_array_t) {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return;
}
let mut i: size_t = 0i32 as size_t;
@@ -53,7 +55,7 @@ pub unsafe fn dc_array_free_ptr(array: *mut dc_array_t) {
}
pub unsafe fn dc_array_add_uint(mut array: *mut dc_array_t, item: uintptr_t) {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return;
}
if (*array).count == (*array).allocated {
@@ -78,21 +80,21 @@ pub unsafe fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void)
}
pub unsafe fn dc_array_get_cnt(array: *const dc_array_t) -> size_t {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return 0i32 as size_t;
}
(*array).count
}
pub unsafe fn dc_array_get_uint(array: *const dc_array_t, index: size_t) -> uintptr_t {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || index >= (*array).count {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
return 0i32 as uintptr_t;
}
*(*array).array.offset(index as isize)
}
pub unsafe fn dc_array_get_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || index >= (*array).count {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
return 0i32 as uint32_t;
}
if (*array).type_0 == 1i32 {
@@ -102,7 +104,7 @@ pub unsafe fn dc_array_get_id(array: *const dc_array_t, index: size_t) -> uint32
}
pub unsafe fn dc_array_get_ptr(array: *const dc_array_t, index: size_t) -> *mut libc::c_void {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || index >= (*array).count {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
return 0 as *mut libc::c_void;
}
*(*array).array.offset(index as isize) as *mut libc::c_void
@@ -110,7 +112,7 @@ pub unsafe fn dc_array_get_ptr(array: *const dc_array_t, index: size_t) -> *mut
pub unsafe fn dc_array_get_latitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null()
|| (*array).magic != 0xa11aai32 as libc::c_uint
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
@@ -122,7 +124,7 @@ pub unsafe fn dc_array_get_latitude(array: *const dc_array_t, index: size_t) ->
pub unsafe fn dc_array_get_longitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null()
|| (*array).magic != 0xa11aai32 as libc::c_uint
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
@@ -134,7 +136,7 @@ pub unsafe fn dc_array_get_longitude(array: *const dc_array_t, index: size_t) ->
pub unsafe fn dc_array_get_accuracy(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null()
|| (*array).magic != 0xa11aai32 as libc::c_uint
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
@@ -146,7 +148,7 @@ pub unsafe fn dc_array_get_accuracy(array: *const dc_array_t, index: size_t) ->
pub unsafe fn dc_array_get_timestamp(array: *const dc_array_t, index: size_t) -> i64 {
if array.is_null()
|| (*array).magic != 0xa11aai32 as libc::c_uint
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
@@ -158,7 +160,7 @@ pub unsafe fn dc_array_get_timestamp(array: *const dc_array_t, index: size_t) ->
pub unsafe fn dc_array_get_chat_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null()
|| (*array).magic != 0xa11aai32 as libc::c_uint
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
@@ -170,7 +172,7 @@ pub unsafe fn dc_array_get_chat_id(array: *const dc_array_t, index: size_t) -> u
pub unsafe fn dc_array_get_contact_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null()
|| (*array).magic != 0xa11aai32 as libc::c_uint
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
@@ -182,7 +184,7 @@ pub unsafe fn dc_array_get_contact_id(array: *const dc_array_t, index: size_t) -
pub unsafe fn dc_array_get_msg_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null()
|| (*array).magic != 0xa11aai32 as libc::c_uint
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
@@ -194,7 +196,7 @@ pub unsafe fn dc_array_get_msg_id(array: *const dc_array_t, index: size_t) -> ui
pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *mut libc::c_char {
if array.is_null()
|| (*array).magic != 0xa11aai32 as libc::c_uint
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
@@ -216,7 +218,7 @@ pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *m
*/
pub unsafe fn dc_array_is_independent(array: *const dc_array_t, index: size_t) -> libc::c_int {
if array.is_null()
|| (*array).magic != 0xa11aai32 as libc::c_uint
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
@@ -232,7 +234,7 @@ pub unsafe fn dc_array_search_id(
needle: uint32_t,
ret_index: *mut size_t,
) -> bool {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return false;
}
let data: *mut uintptr_t = (*array).array;
@@ -251,7 +253,7 @@ pub unsafe fn dc_array_search_id(
}
pub unsafe fn dc_array_get_raw(array: *const dc_array_t) -> *const uintptr_t {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return 0 as *const uintptr_t;
}
(*array).array
@@ -266,7 +268,7 @@ pub unsafe fn dc_array_new_typed(type_0: libc::c_int, initsize: size_t) -> *mut
array = calloc(1, ::std::mem::size_of::<dc_array_t>()) as *mut dc_array_t;
assert!(!array.is_null());
(*array).magic = 0xa11aai32 as uint32_t;
(*array).magic = DC_ARRAY_MAGIC;
(*array).count = 0i32 as size_t;
(*array).allocated = if initsize < 1 { 1 } else { initsize };
(*array).type_0 = type_0;
@@ -282,7 +284,7 @@ pub unsafe fn dc_array_new_typed(type_0: libc::c_int, initsize: size_t) -> *mut
}
pub unsafe fn dc_array_empty(mut array: *mut dc_array_t) {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return;
}
(*array).count = 0i32 as size_t;
@@ -290,7 +292,7 @@ pub unsafe fn dc_array_empty(mut array: *mut dc_array_t) {
pub unsafe fn dc_array_duplicate(array: *const dc_array_t) -> *mut dc_array_t {
let mut ret: *mut dc_array_t;
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return 0 as *mut dc_array_t;
}
ret = dc_array_new((*array).allocated);
@@ -306,7 +308,7 @@ pub unsafe fn dc_array_duplicate(array: *const dc_array_t) -> *mut dc_array_t {
}
pub unsafe fn dc_array_sort_ids(array: *mut dc_array_t) {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || (*array).count <= 1 {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || (*array).count <= 1 {
return;
}
qsort(
@@ -330,7 +332,7 @@ unsafe extern "C" fn cmp_intptr_t(p1: *const libc::c_void, p2: *const libc::c_vo
}
pub unsafe fn dc_array_sort_strings(array: *mut dc_array_t) {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || (*array).count <= 1 {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || (*array).count <= 1 {
return;
}
qsort(
@@ -355,7 +357,7 @@ pub unsafe fn dc_array_get_string(
array: *const dc_array_t,
sep: *const libc::c_char,
) -> *mut libc::c_char {
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || sep.is_null() {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || sep.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
let cnt = (*array).count as usize;
@@ -374,7 +376,7 @@ pub unsafe fn dc_array_get_string(
}
res
});
strdup(to_cstring(res).as_ptr())
to_cstring(res)
}
/// return comma-separated value-string from integer array
@@ -396,7 +398,7 @@ pub unsafe fn dc_arr_to_string(arr: *const uint32_t, cnt: libc::c_int) -> *mut l
res
},
);
strdup(to_cstring(res).as_ptr())
to_cstring(res)
}
#[cfg(test)]
@@ -410,22 +412,17 @@ mod tests {
let arr = dc_array_new(7 as size_t);
assert_eq!(dc_array_get_cnt(arr), 0);
let mut i: libc::c_int = 0;
while i < 1000 {
for i in 0..1000 {
dc_array_add_id(arr, (i + 2) as uint32_t);
i += 1
}
assert_eq!(dc_array_get_cnt(arr), 1000);
i = 0;
while i < 1000i32 {
for i in 0..1000 {
assert_eq!(
dc_array_get_id(arr, i as size_t),
(i + 1i32 * 2i32) as libc::c_uint
);
i += 1
}
assert_eq!(dc_array_get_id(arr, -1i32 as size_t), 0);

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,6 @@ use crate::dc_chat::*;
use crate::dc_contact::*;
use crate::dc_lot::*;
use crate::dc_msg::*;
use crate::dc_sqlite3::*;
use crate::dc_stock::*;
use crate::dc_tools::*;
use crate::types::*;
@@ -27,19 +26,14 @@ pub unsafe fn dc_get_chatlist<'a>(
query_str: *const libc::c_char,
query_id: uint32_t,
) -> *mut dc_chatlist_t<'a> {
let mut success: libc::c_int = 0i32;
let obj: *mut dc_chatlist_t = dc_chatlist_new(context);
let obj = dc_chatlist_new(context);
if !(0 == dc_chatlist_load_from_db(obj, listflags, query_str, query_id)) {
success = 1i32
if 0 != dc_chatlist_load_from_db(obj, listflags, query_str, query_id) {
return obj;
}
if 0 != success {
return obj;
} else {
dc_chatlist_unref(obj);
return 0 as *mut dc_chatlist_t;
};
dc_chatlist_unref(obj);
return 0 as *mut dc_chatlist_t;
}
/**
@@ -120,146 +114,173 @@ unsafe fn dc_chatlist_load_from_db(
mut chatlist: *mut dc_chatlist_t,
listflags: libc::c_int,
query__: *const libc::c_char,
query_contact_id: uint32_t,
query_contact_id: u32,
) -> libc::c_int {
let current_block: u64;
//clock_t start = clock();
let mut success: libc::c_int = 0i32;
let mut add_archived_link_item: libc::c_int = 0i32;
let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
let mut strLikeCmd: *mut libc::c_char = 0 as *mut libc::c_char;
let mut query: *mut libc::c_char = 0 as *mut libc::c_char;
if !(chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32) {
dc_chatlist_empty(chatlist);
// select with left join and minimum:
// - the inner select must use `hidden` and _not_ `m.hidden`
// which would refer the outer select and take a lot of time
// - `GROUP BY` is needed several messages may have the same timestamp
// - the list starts with the newest chats
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
if 0 != query_contact_id {
stmt =
dc_sqlite3_prepare(
(*chatlist).context,
&(*chatlist).context.sql,
b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00"
as *const u8 as *const libc::c_char
);
sqlite3_bind_int(stmt, 1i32, query_contact_id as libc::c_int);
current_block = 3437258052017859086;
} else if 0 != listflags & 0x1i32 {
stmt =
dc_sqlite3_prepare(
(*chatlist).context,
&(*chatlist).context.sql,
b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.archived=1 GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00"
as *const u8 as *const libc::c_char);
current_block = 3437258052017859086;
} else if query__.is_null() {
if 0 == listflags & 0x2i32 {
let last_deaddrop_fresh_msg_id: uint32_t =
get_last_deaddrop_fresh_msg((*chatlist).context);
if last_deaddrop_fresh_msg_id > 0i32 as libc::c_uint {
dc_array_add_id((*chatlist).chatNlastmsg_ids, 1i32 as uint32_t);
dc_array_add_id((*chatlist).chatNlastmsg_ids, last_deaddrop_fresh_msg_id);
}
add_archived_link_item = 1i32
}
stmt =
dc_sqlite3_prepare(
(*chatlist).context,
&(*chatlist).context.sql,
b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.archived=0 GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00"
as *const u8 as *const libc::c_char);
current_block = 3437258052017859086;
} else {
query = dc_strdup(query__);
dc_trim(query);
if *query.offset(0isize) as libc::c_int == 0i32 {
success = 1i32;
current_block = 15179736777190528364;
} else {
strLikeCmd = dc_mprintf(b"%%%s%%\x00" as *const u8 as *const libc::c_char, query);
stmt =
dc_sqlite3_prepare(
(*chatlist).context,
&(*chatlist).context.sql,
b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.name LIKE ? GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00"
as *const u8 as
*const libc::c_char);
sqlite3_bind_text(stmt, 1i32, strLikeCmd, -1i32, None);
current_block = 3437258052017859086;
}
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 {
return 0;
}
dc_chatlist_empty(chatlist);
let mut add_archived_link_item = 0;
// select with left join and minimum:
// - the inner select must use `hidden` and _not_ `m.hidden`
// which would refer the outer select and take a lot of time
// - `GROUP BY` is needed several messages may have the same timestamp
// - the list starts with the newest chats
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let process_row = |row: &rusqlite::Row| {
let chat_id: i32 = row.get(0)?;
// TODO: verify that it is okay for this to be Null
let msg_id: i32 = row.get(1).unwrap_or_default();
Ok((chat_id, msg_id))
};
let process_rows = |rows: rusqlite::MappedRows<_>| {
for row in rows {
let (id1, id2) = row?;
dc_array_add_id((*chatlist).chatNlastmsg_ids, id1 as u32);
dc_array_add_id((*chatlist).chatNlastmsg_ids, id2 as u32);
}
match current_block {
15179736777190528364 => {}
_ => {
while sqlite3_step(stmt) == 100i32 {
dc_array_add_id(
(*chatlist).chatNlastmsg_ids,
sqlite3_column_int(stmt, 0i32) as uint32_t,
);
dc_array_add_id(
(*chatlist).chatNlastmsg_ids,
sqlite3_column_int(stmt, 1i32) as uint32_t,
);
}
if 0 != add_archived_link_item && dc_get_archived_cnt((*chatlist).context) > 0i32 {
if dc_array_get_cnt((*chatlist).chatNlastmsg_ids) == 0
&& 0 != listflags & 0x4i32
{
dc_array_add_id((*chatlist).chatNlastmsg_ids, 7i32 as uint32_t);
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0i32 as uint32_t);
}
dc_array_add_id((*chatlist).chatNlastmsg_ids, 6i32 as uint32_t);
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0i32 as uint32_t);
}
(*chatlist).cnt = dc_array_get_cnt((*chatlist).chatNlastmsg_ids).wrapping_div(2);
success = 1i32
Ok(())
};
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let success = if query_contact_id != 0 {
// show chats shared with a given contact
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![query_contact_id as i32],
process_row,
process_rows,
)
} else if 0 != listflags & 0x1 {
// show archived chats
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=1 GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
)
} else if query__.is_null() {
// show normal chatlist
if 0 == listflags & 0x2 {
let last_deaddrop_fresh_msg_id = get_last_deaddrop_fresh_msg((*chatlist).context);
if last_deaddrop_fresh_msg_id > 0 {
dc_array_add_id((*chatlist).chatNlastmsg_ids, 1);
dc_array_add_id((*chatlist).chatNlastmsg_ids, last_deaddrop_fresh_msg_id);
}
add_archived_link_item = 1;
}
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c \
LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=0 \
GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
)
} else {
let query = to_string(query__).trim().to_string();
if query.is_empty() {
return 1;
} else {
let strLikeCmd = format!("%{}%", query);
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.name LIKE ? \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![strLikeCmd],
process_row,
process_rows,
)
}
};
if 0 != add_archived_link_item && dc_get_archived_cnt((*chatlist).context) > 0 {
if dc_array_get_cnt((*chatlist).chatNlastmsg_ids) == 0 && 0 != listflags & 0x4 {
dc_array_add_id((*chatlist).chatNlastmsg_ids, 7);
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0);
}
dc_array_add_id((*chatlist).chatNlastmsg_ids, 6);
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0);
}
(*chatlist).cnt = dc_array_get_cnt((*chatlist).chatNlastmsg_ids) / 2;
match success {
Ok(_) => 1,
Err(err) => {
error!(
(*chatlist).context,
0, "chatlist: failed to load from database: {:?}", err
);
0
}
}
sqlite3_finalize(stmt);
free(query as *mut libc::c_void);
free(strLikeCmd as *mut libc::c_void);
success
}
// Context functions to work with chatlist
pub unsafe fn dc_get_archived_cnt(context: &Context) -> libc::c_int {
let mut ret: libc::c_int = 0i32;
let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare(
context,
&context.sql,
b"SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;\x00" as *const u8
as *const libc::c_char,
);
if sqlite3_step(stmt) == 100i32 {
ret = sqlite3_column_int(stmt, 0i32)
}
sqlite3_finalize(stmt);
ret
pub fn dc_get_archived_cnt(context: &Context) -> libc::c_int {
context
.sql
.query_row_col(
context,
"SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;",
params![],
0,
)
.unwrap_or_default()
}
unsafe fn get_last_deaddrop_fresh_msg(context: &Context) -> uint32_t {
let mut ret: uint32_t = 0i32 as uint32_t;
let stmt: *mut sqlite3_stmt;
stmt =
dc_sqlite3_prepare(
fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 {
// we have an index over the state-column, this should be sufficient as there are typically only few fresh messages
context
.sql
.query_row_col(
context,
&context.sql,
b"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id WHERE m.state=10 AND m.hidden=0 AND c.blocked=2 ORDER BY m.timestamp DESC, m.id DESC;\x00"
as *const u8 as *const libc::c_char);
/* we have an index over the state-column, this should be sufficient as there are typically only few fresh messages */
if !(sqlite3_step(stmt) != 100i32) {
ret = sqlite3_column_int(stmt, 0i32) as uint32_t
}
sqlite3_finalize(stmt);
ret
"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \
WHERE m.state=10 \
AND m.hidden=0 \
AND c.blocked=2 \
ORDER BY m.timestamp DESC, m.id DESC;",
params![],
0,
)
.unwrap_or_default()
}
pub unsafe fn dc_chatlist_get_cnt(chatlist: *const dc_chatlist_t) -> size_t {
@@ -297,7 +318,7 @@ pub unsafe fn dc_chatlist_get_msg_id(chatlist: *const dc_chatlist_t, index: size
pub unsafe fn dc_chatlist_get_summary<'a>(
chatlist: *const dc_chatlist_t<'a>,
index: size_t,
mut chat: *mut dc_chat_t<'a>,
mut chat: *mut Chat<'a>,
) -> *mut dc_lot_t {
let current_block: u64;
/* The summary is created by the chat, not by the last message.
@@ -310,7 +331,7 @@ pub unsafe fn dc_chatlist_get_summary<'a>(
let lastmsg_id: uint32_t;
let mut lastmsg: *mut dc_msg_t = 0 as *mut dc_msg_t;
let mut lastcontact: *mut dc_contact_t = 0 as *mut dc_contact_t;
let mut chat_to_delete: *mut dc_chat_t = 0 as *mut dc_chat_t;
let mut chat_to_delete: *mut Chat = 0 as *mut Chat;
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 || index >= (*chatlist).cnt {
(*ret).text2 = dc_strdup(b"ErrBadChatlistIndex\x00" as *const u8 as *const libc::c_char)
} else {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -53,7 +53,7 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char
dc_saxparser_parse(&mut saxparser, buf_terminated);
free(dehtml.last_href as *mut libc::c_void);
strdup(to_cstring(dehtml.strbuilder).as_ptr())
to_cstring(dehtml.strbuilder)
}
unsafe fn dehtml_text_cb(

View File

@@ -1,5 +1,5 @@
use std::collections::HashSet;
use std::ffi::{CStr, CString};
use std::ffi::CStr;
use std::str::FromStr;
use mmime::clist::*;
@@ -17,10 +17,8 @@ use mmime::{mailmime_substitute, MAILIMF_NO_ERROR, MAIL_NO_ERROR};
use crate::aheader::*;
use crate::context::Context;
use crate::dc_log::*;
use crate::dc_mimeparser::*;
use crate::dc_securejoin::*;
use crate::dc_sqlite3::*;
use crate::dc_tools::*;
use crate::key::*;
use crate::keyring::*;
@@ -33,7 +31,7 @@ use crate::x::*;
// attachments of 25 mb brutto should work on the majority of providers
// (brutto examples: web.de=50, 1&1=40, t-online.de=32, gmail=25, posteo=50, yahoo=25, all-inkl=100).
// as an upper limit, we double the size; the core won't send messages larger than this
// to get the netto sizes, we substract 1 mb header-overhead and the base64-overhead.
// to get the netto sizes, we subtract 1 mb header-overhead and the base64-overhead.
// some defaults
#[derive(Clone)]
pub struct dc_e2ee_helper_t {
@@ -83,27 +81,21 @@ pub unsafe fn dc_e2ee_encrypt(
{
/* libEtPan's pgp_encrypt_mime() takes the parent as the new root. We just expect the root as being given to this function. */
let prefer_encrypt = if 0
!= dc_sqlite3_get_config_int(
context,
&context.sql,
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
1,
) {
!= context
.sql
.get_config_int(context, "e2ee_enabled")
.unwrap_or_default()
{
EncryptPreference::Mutual
} else {
EncryptPreference::NoPreference
};
let addr = dc_sqlite3_get_config(
context,
&context.sql,
b"configured_addr\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
let addr = context.sql.get_config(context, "configured_addr");
if !addr.is_null() {
if let Some(addr) = addr {
if let Some(public_key) =
load_or_generate_self_public_key(context, addr, in_out_message)
load_or_generate_self_public_key(context, &addr, in_out_message)
{
/*only for random-seed*/
if prefer_encrypt == EncryptPreference::Mutual || 0 != e2ee_guaranteed {
@@ -111,15 +103,10 @@ pub unsafe fn dc_e2ee_encrypt(
let mut iter1: *mut clistiter;
iter1 = (*recipients_addr).first;
while !iter1.is_null() {
let recipient_addr: *const libc::c_char = (if !iter1.is_null() {
(*iter1).data
} else {
0 as *mut libc::c_void
})
as *const libc::c_char;
if strcasecmp(recipient_addr, addr) != 0 {
let recipient_addr = to_string((*iter1).data as *const libc::c_char);
if recipient_addr != addr {
let peerstate =
Peerstate::from_addr(context, &context.sql, as_str(recipient_addr));
Peerstate::from_addr(context, &context.sql, &recipient_addr);
if peerstate.is_some()
&& (peerstate.as_ref().unwrap().prefer_encrypt
== EncryptPreference::Mutual
@@ -145,7 +132,7 @@ pub unsafe fn dc_e2ee_encrypt(
}
let sign_key = if 0 != do_encrypt {
keyring.add_ref(&public_key);
let key = Key::from_self_private(context, addr, &context.sql);
let key = Key::from_self_private(context, addr.clone(), &context.sql);
if key.is_none() {
do_encrypt = 0i32;
@@ -197,7 +184,7 @@ pub unsafe fn dc_e2ee_encrypt(
b"Autocrypt-Gossip\x00" as *const u8
as *const libc::c_char,
),
strdup(header.as_ptr()),
header,
),
);
}
@@ -307,10 +294,8 @@ pub unsafe fn dc_e2ee_encrypt(
sign_key.as_ref(),
) {
let ctext_bytes = ctext_v.len();
let ctext_c = CString::new(ctext_v).unwrap();
let ctext = strdup(ctext_c.as_ptr());
(*helper).cdata_to_free = ctext as *mut libc::c_void;
let ctext = to_cstring(ctext_v);
(*helper).cdata_to_free = ctext as *mut _;
/* create MIME-structure that will contain the encrypted text */
let mut encrypted_part: *mut mailmime = new_data_part(
@@ -366,15 +351,14 @@ pub unsafe fn dc_e2ee_encrypt(
match current_block {
14181132614457621749 => {}
_ => {
let addr = CStr::from_ptr(addr).to_str().unwrap();
let aheader = Aheader::new(addr.into(), public_key, prefer_encrypt);
let rendered = CString::new(aheader.to_string()).unwrap();
let aheader = Aheader::new(addr, public_key, prefer_encrypt);
let rendered = to_cstring(aheader.to_string());
mailimf_fields_add(
imffields_unprotected,
mailimf_field_new_custom(
strdup(b"Autocrypt\x00" as *const u8 as *const libc::c_char),
strdup(rendered.as_ptr()),
rendered,
),
);
}
@@ -503,13 +487,13 @@ unsafe fn new_data_part(
******************************************************************************/
unsafe fn load_or_generate_self_public_key(
context: &Context,
self_addr: *const libc::c_char,
self_addr: impl AsRef<str>,
_random_data_mime: *mut mailmime,
) -> Option<Key> {
/* avoid double creation (we unlock the database during creation) */
static mut s_in_key_creation: libc::c_int = 0i32;
static mut s_in_key_creation: libc::c_int = 0;
let mut key = Key::from_self_public(context, self_addr, &context.sql);
let mut key = Key::from_self_public(context, &self_addr, &context.sql);
if key.is_some() {
return key;
}
@@ -521,46 +505,35 @@ unsafe fn load_or_generate_self_public_key(
let key_creation_here = 1;
s_in_key_creation = 1;
let start: libc::clock_t = clock();
dc_log_info(
let start = clock();
info!(
context,
0i32,
b"Generating keypair with %i bits, e=%i ...\x00" as *const u8 as *const libc::c_char,
2048i32,
65537i32,
0, "Generating keypair with {} bits, e={} ...", 2048, 65537,
);
if let Some((public_key, private_key)) = dc_pgp_create_keypair(self_addr) {
if let Some((public_key, private_key)) = dc_pgp_create_keypair(&self_addr) {
if !dc_key_save_self_keypair(
context,
&public_key,
&private_key,
self_addr,
&self_addr,
1i32,
&context.sql,
) {
/*set default*/
dc_log_warning(
context,
0i32,
b"Cannot save keypair.\x00" as *const u8 as *const libc::c_char,
);
warn!(context, 0, "Cannot save keypair.",);
} else {
dc_log_info(
info!(
context,
0i32,
b"Keypair generated in %.3f s.\x00" as *const u8 as *const libc::c_char,
clock().wrapping_sub(start) as libc::c_double / 1000000i32 as libc::c_double,
0,
"Keypair generated in {:.3}s.",
clock().wrapping_sub(start) as libc::c_double / 1000000 as libc::c_double,
);
}
key = Some(public_key);
} else {
dc_log_warning(
context,
0i32,
b"Cannot create keypair.\x00" as *const u8 as *const libc::c_char,
);
warn!(context, 0, "Cannot create keypair.");
}
if 0 != key_creation_here {
@@ -583,7 +556,6 @@ pub unsafe fn dc_e2ee_decrypt(
let imffields: *mut mailimf_fields = mailmime_find_mailimf_fields(in_out_message);
let mut message_time = 0;
let mut from: *mut libc::c_char = 0 as *mut libc::c_char;
let mut self_addr: *mut libc::c_char = 0 as *mut libc::c_char;
let mut private_keyring = Keyring::default();
let mut public_keyring_for_validate = Keyring::default();
let mut gossip_headers: *mut mailimf_fields = 0 as *mut mailimf_fields;
@@ -612,28 +584,23 @@ pub unsafe fn dc_e2ee_decrypt(
if let Some(ref mut peerstate) = peerstate {
if let Some(ref header) = autocryptheader {
peerstate.apply_header(&header, message_time as u64);
peerstate.apply_header(&header, message_time);
peerstate.save_to_db(&context.sql, false);
} else if message_time as u64 > peerstate.last_seen_autocrypt
} else if message_time > peerstate.last_seen_autocrypt
&& 0 == contains_report(in_out_message)
{
peerstate.degrade_encryption(message_time as u64);
peerstate.degrade_encryption(message_time);
peerstate.save_to_db(&context.sql, false);
}
} else if let Some(ref header) = autocryptheader {
let p = Peerstate::from_header(context, header, message_time as u64);
let p = Peerstate::from_header(context, header, message_time);
p.save_to_db(&context.sql, true);
peerstate = Some(p);
}
}
/* load private key for decryption */
self_addr = dc_sqlite3_get_config(
context,
&context.sql,
b"configured_addr\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
if !self_addr.is_null() {
let self_addr = context.sql.get_config(context, "configured_addr");
if let Some(self_addr) = self_addr {
if private_keyring.load_self_private_for_decrypting(context, self_addr, &context.sql) {
if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 {
peerstate = Peerstate::from_addr(&context, &context.sql, as_str(from));
@@ -681,7 +648,6 @@ pub unsafe fn dc_e2ee_decrypt(
}
free(from as *mut libc::c_void);
free(self_addr as *mut libc::c_void);
}
unsafe fn update_gossip_peerstates(
@@ -722,10 +688,10 @@ unsafe fn update_gossip_peerstates(
let mut peerstate =
Peerstate::from_addr(context, &context.sql, &header.addr);
if let Some(ref mut peerstate) = peerstate {
peerstate.apply_gossip(header, message_time as u64);
peerstate.apply_gossip(header, message_time);
peerstate.save_to_db(&context.sql, false);
} else {
let p = Peerstate::from_gossip(context, header, message_time as u64);
let p = Peerstate::from_gossip(context, header, message_time);
p.save_to_db(&context.sql, true);
peerstate = Some(p);
}
@@ -737,12 +703,11 @@ unsafe fn update_gossip_peerstates(
gossipped_addr.insert(header.addr.clone());
} else {
dc_log_info(
info!(
context,
0i32,
b"Ignoring gossipped \"%s\" as the address is not in To/Cc list.\x00"
as *const u8 as *const libc::c_char,
CString::new(header.addr.clone()).unwrap().as_ptr(),
0,
"Ignoring gossipped \"{}\" as the address is not in To/Cc list.",
&header.addr,
);
}
}
@@ -968,13 +933,12 @@ unsafe fn decrypt_part(
add_signatures,
) {
let plain_bytes = plain.len();
let plain_c = CString::new(plain).unwrap();
let plain_buf = strdup(plain_c.as_ptr());
let plain_buf = plain.as_ptr() as *const libc::c_char;
let mut index: size_t = 0i32 as size_t;
let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime;
if mailmime_parse(
plain_buf as *const libc::c_char,
plain_buf as *const _,
plain_bytes,
&mut index,
&mut decrypted_mime,
@@ -1043,7 +1007,7 @@ unsafe fn has_decrypted_pgp_armor(
* that we could use the normal Autocrypt processing.
*
* @private
* @param mime The mime struture to check
* @param mime The mime structure to check
* @return 1=multipart/report found in MIME, 0=no multipart/report found
*/
// TODO should return bool /rtn
@@ -1103,25 +1067,18 @@ pub unsafe fn dc_ensure_secret_key_exists(context: &Context) -> libc::c_int {
(this is to gain some extra-random-seed by the message content and the timespan between program start and message sending) */
let mut success: libc::c_int = 0i32;
let self_addr = dc_sqlite3_get_config(
context,
&context.sql,
b"configured_addr\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
if self_addr.is_null() {
dc_log_warning(
let self_addr = context.sql.get_config(context, "configured_addr");
if self_addr.is_none() {
warn!(
context,
0i32,
b"Cannot ensure secret key if context is not configured.\x00" as *const u8
as *const libc::c_char,
0, "Cannot ensure secret key if context is not configured.",
);
} else if load_or_generate_self_public_key(context, self_addr, 0 as *mut mailmime).is_some() {
} else if load_or_generate_self_public_key(context, self_addr.unwrap(), 0 as *mut mailmime)
.is_some()
{
/*no random text data for seeding available*/
success = 1i32
success = 1;
}
free(self_addr as *mut libc::c_void);
success
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,28 +2,25 @@ use std::sync::{Arc, Condvar, Mutex};
use crate::context::Context;
use crate::dc_configure::*;
use crate::dc_log::*;
use crate::dc_sqlite3::*;
use crate::dc_tools::*;
use crate::imap::Imap;
use crate::x::*;
#[repr(C)]
pub struct dc_jobthread_t {
pub name: *mut libc::c_char,
pub folder_config_name: *mut libc::c_char,
pub name: &'static str,
pub folder_config_name: &'static str,
pub imap: Imap,
pub state: Arc<(Mutex<JobState>, Condvar)>,
}
pub unsafe fn dc_jobthread_init(
name: *const libc::c_char,
folder_config_name: *const libc::c_char,
pub fn dc_jobthread_init(
name: &'static str,
folder_config_name: &'static str,
imap: Imap,
) -> dc_jobthread_t {
dc_jobthread_t {
name: dc_strdup(name),
folder_config_name: dc_strdup(folder_config_name),
name,
folder_config_name,
imap,
state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
}
@@ -37,26 +34,13 @@ pub struct JobState {
using_handle: i32,
}
pub unsafe fn dc_jobthread_exit(jobthread: &mut dc_jobthread_t) {
free(jobthread.name as *mut libc::c_void);
jobthread.name = 0 as *mut libc::c_char;
free(jobthread.folder_config_name as *mut libc::c_void);
jobthread.folder_config_name = 0 as *mut libc::c_char;
}
pub unsafe fn dc_jobthread_suspend(
context: &Context,
jobthread: &dc_jobthread_t,
suspend: libc::c_int,
) {
if 0 != suspend {
dc_log_info(
context,
0i32,
b"Suspending %s-thread.\x00" as *const u8 as *const libc::c_char,
jobthread.name,
);
info!(context, 0, "Suspending {}-thread.", jobthread.name,);
{
jobthread.state.0.lock().unwrap().suspended = 1;
}
@@ -69,12 +53,7 @@ pub unsafe fn dc_jobthread_suspend(
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
}
} else {
dc_log_info(
context,
0i32,
b"Unsuspending %s-thread.\x00" as *const u8 as *const libc::c_char,
jobthread.name,
);
info!(context, 0, "Unsuspending {}-thread.", jobthread.name);
let &(ref lock, ref cvar) = &*jobthread.state.clone();
let mut state = lock.lock().unwrap();
@@ -90,12 +69,7 @@ pub unsafe fn dc_jobthread_interrupt_idle(context: &Context, jobthread: &dc_jobt
jobthread.state.0.lock().unwrap().jobs_needed = 1;
}
dc_log_info(
context,
0,
b"Interrupting %s-IDLE...\x00" as *const u8 as *const libc::c_char,
jobthread.name,
);
info!(context, 0, "Interrupting {}-IDLE...", jobthread.name);
jobthread.imap.interrupt_idle();
@@ -127,30 +101,22 @@ pub unsafe fn dc_jobthread_fetch(
if 0 != use_network {
start = clock();
if !(0 == connect_to_imap(context, jobthread)) {
dc_log_info(
context,
0,
b"%s-fetch started...\x00" as *const u8 as *const libc::c_char,
jobthread.name,
);
info!(context, 0, "{}-fetch started...", jobthread.name);
jobthread.imap.fetch(context);
if jobthread.imap.should_reconnect() {
dc_log_info(
info!(
context,
0i32,
b"%s-fetch aborted, starting over...\x00" as *const u8 as *const libc::c_char,
jobthread.name,
0, "{}-fetch aborted, starting over...", jobthread.name,
);
jobthread.imap.fetch(context);
}
dc_log_info(
info!(
context,
0,
b"%s-fetch done in %.0f ms.\x00" as *const u8 as *const libc::c_char,
"{}-fetch done in {:.3} ms.",
jobthread.name,
clock().wrapping_sub(start) as libc::c_double * 1000.0f64
/ 1000000i32 as libc::c_double,
clock().wrapping_sub(start) as f64 / 1000.0,
);
}
}
@@ -163,38 +129,32 @@ pub unsafe fn dc_jobthread_fetch(
******************************************************************************/
unsafe fn connect_to_imap(context: &Context, jobthread: &dc_jobthread_t) -> libc::c_int {
let mut ret_connected: libc::c_int;
let mut mvbox_name: *mut libc::c_char = 0 as *mut libc::c_char;
if jobthread.imap.is_connected() {
ret_connected = 1;
} else {
ret_connected = dc_connect_to_configured_imap(context, &jobthread.imap);
if !(0 == ret_connected) {
if dc_sqlite3_get_config_int(
context,
&context.sql,
b"folders_configured\x00" as *const u8 as *const libc::c_char,
0,
) < 3
{
jobthread.imap.configure_folders(context, 0x1);
}
mvbox_name = dc_sqlite3_get_config(
context,
&context.sql,
jobthread.folder_config_name,
0 as *const libc::c_char,
);
if mvbox_name.is_null() {
jobthread.imap.disconnect(context);
ret_connected = 0;
} else {
jobthread.imap.set_watch_folder(mvbox_name);
}
return 1;
}
let mut ret_connected = dc_connect_to_configured_imap(context, &jobthread.imap);
if !(0 == ret_connected) {
if context
.sql
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
jobthread.imap.configure_folders(context, 0x1);
}
if let Some(mvbox_name) = context
.sql
.get_config(context, jobthread.folder_config_name)
{
jobthread.imap.set_watch_folder(mvbox_name);
} else {
jobthread.imap.disconnect(context);
ret_connected = 0;
}
}
free(mvbox_name as *mut libc::c_void);
ret_connected
}
@@ -209,11 +169,10 @@ pub unsafe fn dc_jobthread_idle(
let mut state = lock.lock().unwrap();
if 0 != state.jobs_needed {
dc_log_info(
info!(
context,
0,
b"%s-IDLE will not be started as it was interrupted while not ideling.\x00"
as *const u8 as *const libc::c_char,
"{}-IDLE will not be started as it was interrupted while not ideling.",
jobthread.name,
);
state.jobs_needed = 0;
@@ -242,19 +201,9 @@ pub unsafe fn dc_jobthread_idle(
}
connect_to_imap(context, jobthread);
dc_log_info(
context,
0i32,
b"%s-IDLE started...\x00" as *const u8 as *const libc::c_char,
jobthread.name,
);
info!(context, 0, "{}-IDLE started...", jobthread.name,);
jobthread.imap.idle(context);
dc_log_info(
context,
0i32,
b"%s-IDLE ended.\x00" as *const u8 as *const libc::c_char,
jobthread.name,
);
info!(context, 0, "{}-IDLE ended.", jobthread.name);
jobthread.state.0.lock().unwrap().using_handle = 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,142 +0,0 @@
use crate::constants::Event;
use crate::context::Context;
use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
pub unsafe extern "C" fn dc_log_event(
context: &Context,
event_code: Event,
data1: libc::c_int,
msg: *const libc::c_char,
va: ...
) {
log_vprintf(context, event_code, data1, msg, va);
}
/* Asynchronous "Thread-errors" are reported by the dc_log_error()
function. These errors must be shown to the user by a bubble or so.
"Normal" errors are usually returned by a special value (null or so) and are
usually not reported using dc_log_error() - its up to the caller to
decide, what should be reported or done. However, these "Normal" errors
are usually logged by dc_log_warning(). */
unsafe fn log_vprintf(
context: &Context,
event: Event,
data1: libc::c_int,
msg_format: *const libc::c_char,
va_0: ::std::ffi::VaList,
) {
let msg: *mut libc::c_char;
if !msg_format.is_null() {
let mut tempbuf: [libc::c_char; 1025] = [0; 1025];
vsnprintf(
tempbuf.as_mut_ptr(),
1024i32 as libc::c_ulong,
msg_format,
va_0,
);
msg = dc_strdup(tempbuf.as_mut_ptr())
} else {
msg = dc_mprintf(
b"event #%i\x00" as *const u8 as *const libc::c_char,
event as libc::c_int,
)
}
context.call_cb(event, data1 as uintptr_t, msg as uintptr_t);
free(msg as *mut libc::c_void);
}
pub unsafe extern "C" fn dc_log_event_seq(
context: &Context,
event_code: Event,
sequence_start: *mut libc::c_int,
msg: *const libc::c_char,
va_0: ...
) {
if sequence_start.is_null() {
return;
}
log_vprintf(context, event_code, *sequence_start, msg, va_0);
*sequence_start = 0i32;
}
pub unsafe extern "C" fn dc_log_error(
context: &Context,
data1: libc::c_int,
msg: *const libc::c_char,
va_1: ...
) {
log_vprintf(context, Event::ERROR, data1, msg, va_1);
}
pub unsafe extern "C" fn dc_log_warning(
context: &Context,
data1: libc::c_int,
msg: *const libc::c_char,
va_2: ...
) {
log_vprintf(context, Event::WARNING, data1, msg, va_2);
}
pub unsafe extern "C" fn dc_log_info(
context: &Context,
data1: libc::c_int,
msg: *const libc::c_char,
va_3: ...
) {
log_vprintf(context, Event::INFO, data1, msg, va_3);
}
#[macro_export]
macro_rules! info {
($ctx:expr, $data1:expr, $msg:expr) => {
info!($ctx, $data1, $msg,)
};
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {{
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($crate::constants::Event::INFO, $data1 as uintptr_t,
formatted_c.as_ptr() as uintptr_t)
}};
}
#[macro_export]
macro_rules! warn {
($ctx:expr, $data1:expr, $msg:expr) => {
warn!($ctx, $data1, $msg,)
};
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t,
formatted_c.as_ptr() as libc::uintptr_t)
};
}
#[macro_export]
macro_rules! error {
($ctx:expr, $data1:expr, $msg:expr) => {
error!($ctx, $data1, $msg,)
};
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($crate::constants::Event::ERROR, $data1 as uintptr_t,
formatted_c.as_ptr() as uintptr_t)
};
}
#[macro_export]
macro_rules! log_event {
($ctx:expr, $data1:expr, $msg:expr) => {
log_event!($ctx, $data1, $msg,)
};
($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($event, $data1 as uintptr_t,
formatted_c.as_ptr() as uintptr_t)
};
}

View File

@@ -1,283 +1,173 @@
use crate::context::Context;
use crate::dc_sqlite3::*;
use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
use std::borrow::Cow;
#[derive(Copy, Clone)]
#[repr(C)]
use crate::context::Context;
use crate::sql::Sql;
#[derive(Default, Debug)]
pub struct dc_loginparam_t {
pub addr: *mut libc::c_char,
pub mail_server: *mut libc::c_char,
pub mail_user: *mut libc::c_char,
pub mail_pw: *mut libc::c_char,
pub addr: String,
pub mail_server: String,
pub mail_user: String,
pub mail_pw: String,
pub mail_port: i32,
pub send_server: *mut libc::c_char,
pub send_user: *mut libc::c_char,
pub send_pw: *mut libc::c_char,
pub send_server: String,
pub send_user: String,
pub send_pw: String,
pub send_port: i32,
pub server_flags: i32,
}
pub unsafe fn dc_loginparam_new() -> *mut dc_loginparam_t {
let loginparam: *mut dc_loginparam_t;
loginparam = calloc(1, ::std::mem::size_of::<dc_loginparam_t>()) as *mut dc_loginparam_t;
assert!(!loginparam.is_null());
loginparam
}
pub unsafe fn dc_loginparam_unref(loginparam: *mut dc_loginparam_t) {
if loginparam.is_null() {
return;
impl dc_loginparam_t {
pub fn addr_str(&self) -> &str {
self.addr.as_str()
}
dc_loginparam_empty(loginparam);
free(loginparam as *mut libc::c_void);
}
/* clears all data and frees its memory. All pointers are NULL after this function is called. */
pub unsafe fn dc_loginparam_empty(mut loginparam: *mut dc_loginparam_t) {
if loginparam.is_null() {
return;
}
free((*loginparam).addr as *mut libc::c_void);
(*loginparam).addr = 0 as *mut libc::c_char;
free((*loginparam).mail_server as *mut libc::c_void);
(*loginparam).mail_server = 0 as *mut libc::c_char;
(*loginparam).mail_port = 0i32;
free((*loginparam).mail_user as *mut libc::c_void);
(*loginparam).mail_user = 0 as *mut libc::c_char;
free((*loginparam).mail_pw as *mut libc::c_void);
(*loginparam).mail_pw = 0 as *mut libc::c_char;
free((*loginparam).send_server as *mut libc::c_void);
(*loginparam).send_server = 0 as *mut libc::c_char;
(*loginparam).send_port = 0i32;
free((*loginparam).send_user as *mut libc::c_void);
(*loginparam).send_user = 0 as *mut libc::c_char;
free((*loginparam).send_pw as *mut libc::c_void);
(*loginparam).send_pw = 0 as *mut libc::c_char;
(*loginparam).server_flags = 0i32;
pub fn dc_loginparam_new() -> dc_loginparam_t {
Default::default()
}
pub unsafe fn dc_loginparam_read(
pub fn dc_loginparam_read(
context: &Context,
loginparam: *mut dc_loginparam_t,
sql: &SQLite,
prefix: *const libc::c_char,
) {
let mut key: *mut libc::c_char = 0 as *mut libc::c_char;
dc_loginparam_empty(loginparam);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"addr\x00" as *const u8 as *const libc::c_char,
);
(*loginparam).addr = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"mail_server\x00" as *const u8 as *const libc::c_char,
);
(*loginparam).mail_server = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"mail_port\x00" as *const u8 as *const libc::c_char,
);
(*loginparam).mail_port = dc_sqlite3_get_config_int(context, sql, key, 0i32);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"mail_user\x00" as *const u8 as *const libc::c_char,
);
(*loginparam).mail_user = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"mail_pw\x00" as *const u8 as *const libc::c_char,
);
(*loginparam).mail_pw = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"send_server\x00" as *const u8 as *const libc::c_char,
);
(*loginparam).send_server = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"send_port\x00" as *const u8 as *const libc::c_char,
);
(*loginparam).send_port = dc_sqlite3_get_config_int(context, sql, key, 0i32);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"send_user\x00" as *const u8 as *const libc::c_char,
);
(*loginparam).send_user = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"send_pw\x00" as *const u8 as *const libc::c_char,
);
(*loginparam).send_pw = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"server_flags\x00" as *const u8 as *const libc::c_char,
);
(*loginparam).server_flags = dc_sqlite3_get_config_int(context, sql, key, 0i32);
sqlite3_free(key as *mut libc::c_void);
}
sql: &Sql,
prefix: impl AsRef<str>,
) -> dc_loginparam_t {
let prefix = prefix.as_ref();
pub unsafe fn dc_loginparam_write(
context: &Context,
loginparam: *const dc_loginparam_t,
sql: &SQLite,
prefix: *const libc::c_char,
) {
let mut key: *mut libc::c_char = 0 as *mut libc::c_char;
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"addr\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_set_config(context, sql, key, (*loginparam).addr);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"mail_server\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_set_config(context, sql, key, (*loginparam).mail_server);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"mail_port\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_set_config_int(context, sql, key, (*loginparam).mail_port);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"mail_user\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_set_config(context, sql, key, (*loginparam).mail_user);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"mail_pw\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_set_config(context, sql, key, (*loginparam).mail_pw);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"send_server\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_set_config(context, sql, key, (*loginparam).send_server);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"send_port\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_set_config_int(context, sql, key, (*loginparam).send_port);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"send_user\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_set_config(context, sql, key, (*loginparam).send_user);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"send_pw\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_set_config(context, sql, key, (*loginparam).send_pw);
sqlite3_free(key as *mut libc::c_void);
key = sqlite3_mprintf(
b"%s%s\x00" as *const u8 as *const libc::c_char,
prefix,
b"server_flags\x00" as *const u8 as *const libc::c_char,
);
dc_sqlite3_set_config_int(context, sql, key, (*loginparam).server_flags);
sqlite3_free(key as *mut libc::c_void);
}
let key = format!("{}addr", prefix);
let addr = sql
.get_config(context, key)
.unwrap_or_default()
.trim()
.to_string();
pub unsafe fn dc_loginparam_get_readable(loginparam: *const dc_loginparam_t) -> *mut libc::c_char {
let unset: *const libc::c_char = b"0\x00" as *const u8 as *const libc::c_char;
let pw: *const libc::c_char = b"***\x00" as *const u8 as *const libc::c_char;
if loginparam.is_null() {
return dc_strdup(0 as *const libc::c_char);
let key = format!("{}mail_server", prefix);
let mail_server = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}mail_port", prefix);
let mail_port = sql.get_config_int(context, key).unwrap_or_default();
let key = format!("{}mail_user", prefix);
let mail_user = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}mail_pw", prefix);
let mail_pw = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}send_server", prefix);
let send_server = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}send_port", prefix);
let send_port = sql.get_config_int(context, key).unwrap_or_default();
let key = format!("{}send_user", prefix);
let send_user = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}send_pw", prefix);
let send_pw = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}server_flags", prefix);
let server_flags = sql.get_config_int(context, key).unwrap_or_default();
dc_loginparam_t {
addr: addr.to_string(),
mail_server,
mail_user,
mail_pw,
mail_port,
send_server,
send_user,
send_pw,
send_port,
server_flags,
}
let flags_readable: *mut libc::c_char = get_readable_flags((*loginparam).server_flags);
let ret: *mut libc::c_char = dc_mprintf(
b"%s %s:%s:%s:%i %s:%s:%s:%i %s\x00" as *const u8 as *const libc::c_char,
if !(*loginparam).addr.is_null() {
(*loginparam).addr
} else {
unset
},
if !(*loginparam).mail_user.is_null() {
(*loginparam).mail_user
} else {
unset
},
if !(*loginparam).mail_pw.is_null() {
}
pub fn dc_loginparam_write(
context: &Context,
loginparam: &dc_loginparam_t,
sql: &Sql,
prefix: impl AsRef<str>,
) {
let prefix = prefix.as_ref();
let key = format!("{}addr", prefix);
sql.set_config(context, key, Some(&loginparam.addr)).ok();
let key = format!("{}mail_server", prefix);
sql.set_config(context, key, Some(&loginparam.mail_server))
.ok();
let key = format!("{}mail_port", prefix);
sql.set_config_int(context, key, loginparam.mail_port).ok();
let key = format!("{}mail_user", prefix);
sql.set_config(context, key, Some(&loginparam.mail_user))
.ok();
let key = format!("{}mail_pw", prefix);
sql.set_config(context, key, Some(&loginparam.mail_pw)).ok();
let key = format!("{}send_server", prefix);
sql.set_config(context, key, Some(&loginparam.send_server))
.ok();
let key = format!("{}send_port", prefix);
sql.set_config_int(context, key, loginparam.send_port).ok();
let key = format!("{}send_user", prefix);
sql.set_config(context, key, Some(&loginparam.send_user))
.ok();
let key = format!("{}send_pw", prefix);
sql.set_config(context, key, Some(&loginparam.send_pw)).ok();
let key = format!("{}server_flags", prefix);
sql.set_config_int(context, key, loginparam.server_flags)
.ok();
}
fn unset_empty(s: &String) -> Cow<String> {
if s.is_empty() {
Cow::Owned("unset".to_string())
} else {
Cow::Borrowed(s)
}
}
pub fn dc_loginparam_get_readable(loginparam: &dc_loginparam_t) -> String {
let unset = "0";
let pw = "***";
let flags_readable = get_readable_flags(loginparam.server_flags);
format!(
"{} {}:{}:{}:{} {}:{}:{}:{} {}",
unset_empty(&loginparam.addr),
unset_empty(&loginparam.mail_user),
if !loginparam.mail_pw.is_empty() {
pw
} else {
unset
},
if !(*loginparam).mail_server.is_null() {
(*loginparam).mail_server
} else {
unset
},
(*loginparam).mail_port,
if !(*loginparam).send_user.is_null() {
(*loginparam).send_user
} else {
unset
},
if !(*loginparam).send_pw.is_null() {
unset_empty(&loginparam.mail_server),
loginparam.mail_port,
unset_empty(&loginparam.send_user),
if !loginparam.send_pw.is_empty() {
pw
} else {
unset
},
if !(*loginparam).send_server.is_null() {
(*loginparam).send_server
} else {
unset
},
(*loginparam).send_port,
unset_empty(&loginparam.send_server),
loginparam.send_port,
flags_readable,
);
free(flags_readable as *mut libc::c_void);
ret
)
}
fn get_readable_flags(flags: libc::c_int) -> *mut libc::c_char {
fn get_readable_flags(flags: i32) -> String {
let mut res = String::new();
for bit in 0..31 {
if 0 != flags & 1 << bit {
let mut flag_added: libc::c_int = 0;
let mut flag_added = 0;
if 1 << bit == 0x2 {
res += "OAUTH2 ";
flag_added = 1;
@@ -319,5 +209,5 @@ fn get_readable_flags(flags: libc::c_int) -> *mut libc::c_char {
res += "0";
}
unsafe { strdup(to_cstring(res).as_ptr()) }
res
}

View File

@@ -126,7 +126,7 @@ pub unsafe fn dc_lot_get_timestamp(lot: *const dc_lot_t) -> i64 {
pub unsafe fn dc_lot_fill(
mut lot: *mut dc_lot_t,
msg: *const dc_msg_t,
chat: *const dc_chat_t,
chat: *const Chat,
contact: *const dc_contact_t,
context: &Context,
) {

View File

@@ -8,16 +8,14 @@ use mmime::mailmime_write_mem::*;
use mmime::mmapstring::*;
use mmime::other::*;
use crate::constants::DC_VERSION_STR;
use crate::constants::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_contact::*;
use crate::dc_e2ee::*;
use crate::dc_location::*;
use crate::dc_log::*;
use crate::dc_msg::*;
use crate::dc_param::*;
use crate::dc_sqlite3::*;
use crate::dc_stock::*;
use crate::dc_strencode::*;
use crate::dc_tools::*;
@@ -36,7 +34,7 @@ pub struct dc_mimefactory_t<'a> {
pub rfc724_mid: *mut libc::c_char,
pub loaded: dc_mimefactory_loaded_t,
pub msg: *mut dc_msg_t<'a>,
pub chat: *mut dc_chat_t<'a>,
pub chat: *mut Chat<'a>,
pub increation: libc::c_int,
pub in_reply_to: *mut libc::c_char,
pub references: *mut libc::c_char,
@@ -91,7 +89,7 @@ pub unsafe fn dc_mimefactory_empty(mut factory: *mut dc_mimefactory_t) {
dc_msg_unref((*factory).msg);
(*factory).msg = 0 as *mut dc_msg_t;
dc_chat_unref((*factory).chat);
(*factory).chat = 0 as *mut dc_chat_t;
(*factory).chat = 0 as *mut Chat;
free((*factory).in_reply_to as *mut libc::c_void);
(*factory).in_reply_to = 0 as *mut libc::c_char;
free((*factory).references as *mut libc::c_void);
@@ -100,7 +98,7 @@ pub unsafe fn dc_mimefactory_empty(mut factory: *mut dc_mimefactory_t) {
mmap_string_free((*factory).out);
(*factory).out = 0 as *mut MMAPString
}
(*factory).out_encrypted = 0i32;
(*factory).out_encrypted = 0;
(*factory).loaded = DC_MF_NOTHING_LOADED;
free((*factory).error as *mut libc::c_void);
(*factory).error = 0 as *mut libc::c_char;
@@ -111,158 +109,180 @@ pub unsafe fn dc_mimefactory_load_msg(
mut factory: *mut dc_mimefactory_t,
msg_id: uint32_t,
) -> libc::c_int {
let mut success: libc::c_int = 0i32;
let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
if !(factory.is_null() || msg_id <= 9i32 as libc::c_uint || !(*factory).msg.is_null()) {
/*call empty() before */
let context = (*factory).context;
(*factory).recipients_names = clist_new();
(*factory).recipients_addr = clist_new();
(*factory).msg = dc_msg_new_untyped(context);
(*factory).chat = dc_chat_new(context);
if dc_msg_load_from_db((*factory).msg, context, msg_id)
&& dc_chat_load_from_db((*factory).chat, (*(*factory).msg).chat_id)
{
load_from(factory);
(*factory).req_mdn = 0i32;
if 0 != dc_chat_is_self_talk((*factory).chat) {
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
dc_strdup_keep_null((*factory).from_displayname) as *mut libc::c_void,
if factory.is_null() || msg_id <= 9 || !(*factory).msg.is_null() {
info!((*factory).context, 0, "mimefactory: null");
return 0;
}
let mut success = 0;
/*call empty() before */
let context = (*factory).context;
(*factory).recipients_names = clist_new();
(*factory).recipients_addr = clist_new();
(*factory).msg = dc_msg_new_untyped(context);
(*factory).chat = dc_chat_new(context);
if dc_msg_load_from_db((*factory).msg, context, msg_id)
&& dc_chat_load_from_db((*factory).chat, (*(*factory).msg).chat_id)
{
info!(context, 0, "mimefactory: loaded msg and chat",);
load_from(factory);
(*factory).req_mdn = 0;
if 0 != dc_chat_is_self_talk((*factory).chat) {
info!(context, 0, "mimefactory: selftalk");
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
dc_strdup_keep_null((*factory).from_displayname) as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
dc_strdup((*factory).from_addr) as *mut libc::c_void,
);
} else {
info!(context, 0, "mimefactory: query map");
context
.sql
.query_map(
"SELECT c.authname, c.addr \
FROM chats_contacts cc \
LEFT JOIN contacts c ON cc.contact_id=c.id \
WHERE cc.chat_id=? AND cc.contact_id>9;",
params![(*(*factory).msg).chat_id as i32],
|row| {
let authname: String = row.get(0)?;
let addr: String = row.get(1)?;
Ok((authname, addr))
},
|rows| {
info!(context, 0, "mimefactory: processing rows");
for row in rows {
let (authname, addr) = row?;
let addr_c = to_cstring(addr);
if clist_search_string_nocase((*factory).recipients_addr, addr_c) == 0 {
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
if !authname.is_empty() {
to_cstring(authname)
} else {
std::ptr::null_mut()
} as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
addr_c as *mut libc::c_void,
);
}
}
Ok(())
},
)
.unwrap();
let command = dc_param_get_int((*(*factory).msg).param, DC_PARAM_CMD as i32, 0);
if command == 5 {
let email_to_remove_c = dc_param_get(
(*(*factory).msg).param,
DC_PARAM_CMD_ARG as i32,
0 as *const libc::c_char,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
dc_strdup((*factory).from_addr) as *mut libc::c_void,
);
} else {
stmt =
dc_sqlite3_prepare(
context,
&context.sql,
b"SELECT c.authname, c.addr FROM chats_contacts cc LEFT JOIN contacts c ON cc.contact_id=c.id WHERE cc.chat_id=? AND cc.contact_id>9;\x00"
as *const u8 as
*const libc::c_char);
sqlite3_bind_int(stmt, 1i32, (*(*factory).msg).chat_id as libc::c_int);
while sqlite3_step(stmt) == 100i32 {
let authname: *const libc::c_char =
sqlite3_column_text(stmt, 0i32) as *const libc::c_char;
let addr: *const libc::c_char =
sqlite3_column_text(stmt, 1i32) as *const libc::c_char;
if clist_search_string_nocase((*factory).recipients_addr, addr) == 0i32 {
let email_to_remove = to_string(email_to_remove_c);
let self_addr = context
.sql
.get_config(context, "configured_addr")
.unwrap_or_default();
if !email_to_remove.is_empty() && email_to_remove != self_addr {
if clist_search_string_nocase((*factory).recipients_addr, email_to_remove_c)
== 0
{
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
(if !authname.is_null() && 0 != *authname.offset(0isize) as libc::c_int
{
dc_strdup(authname)
} else {
0 as *mut libc::c_char
}) as *mut libc::c_void,
0 as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
dc_strdup(addr) as *mut libc::c_void,
email_to_remove_c as *mut libc::c_void,
);
}
}
sqlite3_finalize(stmt);
let command: libc::c_int =
dc_param_get_int((*(*factory).msg).param, 'S' as i32, 0i32);
if command == 5i32 {
let email_to_remove: *mut libc::c_char = dc_param_get(
(*(*factory).msg).param,
'E' as i32,
0 as *const libc::c_char,
);
let self_addr: *mut libc::c_char = dc_sqlite3_get_config(
context,
&context.sql,
b"configured_addr\x00" as *const u8 as *const libc::c_char,
b"\x00" as *const u8 as *const libc::c_char,
);
if !email_to_remove.is_null() && strcasecmp(email_to_remove, self_addr) != 0i32
{
if clist_search_string_nocase((*factory).recipients_addr, email_to_remove)
== 0i32
{
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
0 as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
email_to_remove as *mut libc::c_void,
);
}
}
free(self_addr as *mut libc::c_void);
}
if command != 6i32
&& command != 7i32
&& 0 != dc_sqlite3_get_config_int(
context,
&context.sql,
b"mdns_enabled\x00" as *const u8 as *const libc::c_char,
1i32,
)
{
(*factory).req_mdn = 1i32
}
}
stmt = dc_sqlite3_prepare(
context,
&context.sql,
b"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?\x00" as *const u8
as *const libc::c_char,
);
sqlite3_bind_int(stmt, 1i32, (*(*factory).msg).id as libc::c_int);
if sqlite3_step(stmt) == 100i32 {
(*factory).in_reply_to =
dc_strdup(sqlite3_column_text(stmt, 0i32) as *const libc::c_char);
(*factory).references =
dc_strdup(sqlite3_column_text(stmt, 1i32) as *const libc::c_char)
if command != 6
&& command != 7
&& 0 != context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1)
{
(*factory).req_mdn = 1
}
sqlite3_finalize(stmt);
stmt = 0 as *mut sqlite3_stmt;
success = 1i32;
(*factory).loaded = DC_MF_MSG_LOADED;
(*factory).timestamp = (*(*factory).msg).timestamp_sort;
(*factory).rfc724_mid = dc_strdup((*(*factory).msg).rfc724_mid)
}
if 0 != success {
(*factory).increation = dc_msg_is_increation((*factory).msg)
info!(context, 0, "mimefactory: loading in reply to");
let row = context.sql.query_row(
"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?",
params![(*(*factory).msg).id as i32],
|row| {
let in_reply_to: String = row.get(0)?;
let references: String = row.get(1)?;
Ok((in_reply_to, references))
},
);
match row {
Ok((in_reply_to, references)) => {
(*factory).in_reply_to = to_cstring(in_reply_to);
(*factory).references = to_cstring(references);
}
Err(err) => {
error!(
context,
0, "mimefactory: failed to load mime_in_reply_to: {:?}", err
);
}
}
success = 1;
(*factory).loaded = DC_MF_MSG_LOADED;
(*factory).timestamp = (*(*factory).msg).timestamp_sort;
(*factory).rfc724_mid = dc_strdup((*(*factory).msg).rfc724_mid)
}
sqlite3_finalize(stmt);
return success;
if 0 != success {
(*factory).increation = dc_msg_is_increation((*factory).msg)
}
success
}
unsafe fn load_from(mut factory: *mut dc_mimefactory_t) {
(*factory).from_addr = dc_sqlite3_get_config(
(*factory).context,
&(*factory).context.sql,
b"configured_addr\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
let context = (*factory).context;
(*factory).from_addr = to_cstring(
context
.sql
.get_config(context, "configured_addr")
.unwrap_or_default(),
);
(*factory).from_displayname = dc_sqlite3_get_config(
(*factory).context,
&(*factory).context.sql,
b"displayname\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
(*factory).from_displayname = to_cstring(
context
.sql
.get_config(context, "displayname")
.unwrap_or_default(),
);
(*factory).selfstatus = dc_sqlite3_get_config(
(*factory).context,
&(*factory).context.sql,
b"selfstatus\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
(*factory).selfstatus = to_cstring(
context
.sql
.get_config(context, "selfstatus")
.unwrap_or_default(),
);
if (*factory).selfstatus.is_null() {
(*factory).selfstatus = dc_stock_str((*factory).context, 13i32)
(*factory).selfstatus = dc_stock_str((*factory).context, 13)
};
}
@@ -270,61 +290,63 @@ pub unsafe fn dc_mimefactory_load_mdn(
mut factory: *mut dc_mimefactory_t,
msg_id: uint32_t,
) -> libc::c_int {
let mut success: libc::c_int = 0i32;
let mut contact: *mut dc_contact_t = 0 as *mut dc_contact_t;
if !factory.is_null() {
(*factory).recipients_names = clist_new();
(*factory).recipients_addr = clist_new();
(*factory).msg = dc_msg_new_untyped((*factory).context);
if !(0
== dc_sqlite3_get_config_int(
(*factory).context,
if factory.is_null() {
return 0;
}
let mut success = 0;
let mut contact = 0 as *mut dc_contact_t;
(*factory).recipients_names = clist_new();
(*factory).recipients_addr = clist_new();
(*factory).msg = dc_msg_new_untyped((*factory).context);
if 0 != (*factory)
.context
.sql
.get_config_int((*factory).context, "mdns_enabled")
.unwrap_or_else(|| 1)
{
// MDNs not enabled - check this is late, in the job. the use may have changed its choice while offline ...
contact = dc_contact_new((*factory).context);
if !(!dc_msg_load_from_db((*factory).msg, (*factory).context, msg_id)
|| !dc_contact_load_from_db(
contact,
&(*factory).context.sql,
b"mdns_enabled\x00" as *const u8 as *const libc::c_char,
1i32,
(*(*factory).msg).from_id,
))
{
/* MDNs not enabled - check this is late, in the job. the use may have changed its choice while offline ... */
contact = dc_contact_new((*factory).context);
if !(!dc_msg_load_from_db((*factory).msg, (*factory).context, msg_id)
|| !dc_contact_load_from_db(
contact,
&(*factory).context.sql,
(*(*factory).msg).from_id,
))
{
if !(0 != (*contact).blocked || (*(*factory).msg).chat_id <= 9i32 as libc::c_uint) {
/* Do not send MDNs trash etc.; chats.blocked is already checked by the caller in dc_markseen_msgs() */
if !((*(*factory).msg).from_id <= 9i32 as libc::c_uint) {
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
(if !(*contact).authname.is_null()
&& 0 != *(*contact).authname.offset(0isize) as libc::c_int
{
dc_strdup((*contact).authname)
} else {
0 as *mut libc::c_char
}) as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
dc_strdup((*contact).addr) as *mut libc::c_void,
);
load_from(factory);
(*factory).timestamp = dc_create_smeared_timestamp((*factory).context);
(*factory).rfc724_mid = dc_create_outgoing_rfc724_mid(
0 as *const libc::c_char,
(*factory).from_addr,
);
success = 1i32;
(*factory).loaded = DC_MF_MDN_LOADED
}
if !(0 != (*contact).blocked || (*(*factory).msg).chat_id <= 9 as libc::c_uint) {
// Do not send MDNs trash etc.; chats.blocked is already checked by the caller in dc_markseen_msgs()
if !((*(*factory).msg).from_id <= 9 as libc::c_uint) {
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
(if !(*contact).authname.is_null()
&& 0 != *(*contact).authname.offset(0isize) as libc::c_int
{
dc_strdup((*contact).authname)
} else {
0 as *mut libc::c_char
}) as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
dc_strdup((*contact).addr) as *mut libc::c_void,
);
load_from(factory);
(*factory).timestamp = dc_create_smeared_timestamp((*factory).context);
(*factory).rfc724_mid = dc_create_outgoing_rfc724_mid(
0 as *const libc::c_char,
(*factory).from_addr,
);
success = 1;
(*factory).loaded = DC_MF_MDN_LOADED
}
}
}
}
dc_contact_unref(contact);
success
@@ -339,15 +361,15 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
let mut message_text: *mut libc::c_char = 0 as *mut libc::c_char;
let mut message_text2: *mut libc::c_char = 0 as *mut libc::c_char;
let mut subject_str: *mut libc::c_char = 0 as *mut libc::c_char;
let mut afwd_email: libc::c_int = 0i32;
let mut col: libc::c_int = 0i32;
let mut success: libc::c_int = 0i32;
let mut parts: libc::c_int = 0i32;
let mut e2ee_guaranteed: libc::c_int = 0i32;
let mut min_verified: libc::c_int = 0i32;
let mut afwd_email: libc::c_int = 0;
let mut col: libc::c_int = 0;
let mut success: libc::c_int = 0;
let mut parts: libc::c_int = 0;
let mut e2ee_guaranteed: libc::c_int = 0;
let mut min_verified: libc::c_int = 0;
// 1=add Autocrypt-header (needed eg. for handshaking), 2=no Autocrypte-header (used for MDN)
let mut force_plaintext: libc::c_int = 0i32;
let mut do_gossip: libc::c_int = 0i32;
let mut force_plaintext: libc::c_int = 0;
let mut do_gossip: libc::c_int = 0;
let mut grpimage: *mut libc::c_char = 0 as *mut libc::c_char;
let mut e2ee_helper = dc_e2ee_helper_t {
encryption_successfull: 0,
@@ -382,7 +404,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
let mut to: *mut mailimf_address_list = 0 as *mut mailimf_address_list;
if !(*factory).recipients_names.is_null()
&& !(*factory).recipients_addr.is_null()
&& (*(*factory).recipients_addr).count > 0i32
&& (*(*factory).recipients_addr).count > 0
{
let mut iter1: *mut clistiter;
let mut iter2: *mut clistiter;
@@ -501,11 +523,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
if (*factory).loaded as libc::c_uint == DC_MF_MSG_LOADED as libc::c_int as libc::c_uint {
/* Render a normal message
*********************************************************************/
let chat: *mut dc_chat_t = (*factory).chat;
let chat: *mut Chat = (*factory).chat;
let msg: *mut dc_msg_t = (*factory).msg;
let mut meta_part: *mut mailmime = 0 as *mut mailmime;
let mut placeholdertext: *mut libc::c_char = 0 as *mut libc::c_char;
if (*chat).type_0 == 130i32 {
if (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -513,23 +535,27 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
strdup(b"1\x00" as *const u8 as *const libc::c_char),
),
);
force_plaintext = 0i32;
e2ee_guaranteed = 1i32;
min_verified = 2i32
force_plaintext = 0;
e2ee_guaranteed = 1;
min_verified = 2
} else {
force_plaintext = dc_param_get_int((*(*factory).msg).param, 'u' as i32, 0i32);
if force_plaintext == 0i32 {
e2ee_guaranteed = dc_param_get_int((*(*factory).msg).param, 'c' as i32, 0i32)
force_plaintext =
dc_param_get_int((*(*factory).msg).param, DC_PARAM_FORCE_PLAINTEXT as i32, 0);
if force_plaintext == 0 {
e2ee_guaranteed =
dc_param_get_int((*(*factory).msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0)
}
}
if (*chat).gossiped_timestamp == 0
|| ((*chat).gossiped_timestamp + (2 * 24 * 60 * 60)) < time()
{
do_gossip = 1i32
do_gossip = 1
}
/* build header etc. */
let command: libc::c_int = dc_param_get_int((*msg).param, 'S' as i32, 0i32);
if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 {
let command: libc::c_int = dc_param_get_int((*msg).param, DC_PARAM_CMD as i32, 0);
if (*chat).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int
{
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -544,9 +570,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
dc_encode_header_words((*chat).name),
),
);
if command == 5i32 {
let email_to_remove: *mut libc::c_char =
dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char);
if command == 5 {
let email_to_remove: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG as i32,
0 as *const libc::c_char,
);
if !email_to_remove.is_null() {
mailimf_fields_add(
imf_fields,
@@ -559,10 +588,13 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
),
);
}
} else if command == 4i32 {
do_gossip = 1i32;
let email_to_add: *mut libc::c_char =
dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char);
} else if command == 4 {
do_gossip = 1;
let email_to_add: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG as i32,
0 as *const libc::c_char,
);
if !email_to_add.is_null() {
mailimf_fields_add(
imf_fields,
@@ -574,15 +606,18 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
email_to_add,
),
);
grpimage = dc_param_get((*chat).param, 'i' as i32, 0 as *const libc::c_char)
grpimage = dc_param_get(
(*chat).param,
DC_PARAM_PROFILE_IMAGE as i32,
0 as *const libc::c_char,
)
}
if 0 != dc_param_get_int((*msg).param, 'F' as i32, 0i32) & 0x1i32 {
dc_log_info(
if 0 != dc_param_get_int((*msg).param, DC_PARAM_CMD_ARG2 as i32, 0) & 0x1 {
info!(
(*msg).context,
0i32,
b"sending secure-join message \'%s\' >>>>>>>>>>>>>>>>>>>>>>>>>\x00"
as *const u8 as *const libc::c_char,
b"vg-member-added\x00" as *const u8 as *const libc::c_char,
0,
"sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>",
"vg-member-added",
);
mailimf_fields_add(
imf_fields,
@@ -592,7 +627,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
),
);
}
} else if command == 2i32 {
} else if command == 2 {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -601,13 +636,17 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
),
dc_param_get(
(*msg).param,
'E' as i32,
DC_PARAM_CMD_ARG as i32,
b"\x00" as *const u8 as *const libc::c_char,
),
),
);
} else if command == 3i32 {
grpimage = dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char);
} else if command == 3 {
grpimage = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG as i32,
0 as *const libc::c_char,
);
if grpimage.is_null() {
mailimf_fields_add(
imf_fields,
@@ -619,7 +658,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
}
}
}
if command == 8i32 {
if command == 8 {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -630,7 +669,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
),
);
}
if command == 6i32 {
if command == 6 {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -638,18 +677,20 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
strdup(b"v1\x00" as *const u8 as *const libc::c_char),
),
);
placeholdertext = dc_stock_str((*factory).context, 43i32)
placeholdertext = dc_stock_str((*factory).context, 43)
}
if command == 7i32 {
let step: *mut libc::c_char =
dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char);
if command == 7 {
let step: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG as i32,
0 as *const libc::c_char,
);
if !step.is_null() {
dc_log_info(
info!(
(*msg).context,
0i32,
b"sending secure-join message \'%s\' >>>>>>>>>>>>>>>>>>>>>>>>>\x00"
as *const u8 as *const libc::c_char,
step,
0,
"sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>",
as_str(step),
);
mailimf_fields_add(
imf_fields,
@@ -658,8 +699,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
step,
),
);
let param2: *mut libc::c_char =
dc_param_get((*msg).param, 'F' as i32, 0 as *const libc::c_char);
let param2: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG2 as i32,
0 as *const libc::c_char,
);
if !param2.is_null() {
mailimf_fields_add(
imf_fields,
@@ -667,12 +711,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
if strcmp(
step,
b"vg-request-with-auth\x00" as *const u8 as *const libc::c_char,
) == 0i32
) == 0
|| strcmp(
step,
b"vc-request-with-auth\x00" as *const u8
as *const libc::c_char,
) == 0i32
) == 0
{
strdup(
b"Secure-Join-Auth\x00" as *const u8 as *const libc::c_char,
@@ -687,8 +731,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
),
);
}
let fingerprint: *mut libc::c_char =
dc_param_get((*msg).param, 'G' as i32, 0 as *const libc::c_char);
let fingerprint: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG3 as i32,
0 as *const libc::c_char,
);
if !fingerprint.is_null() {
mailimf_fields_add(
imf_fields,
@@ -701,8 +748,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
),
);
}
let grpid: *mut libc::c_char =
dc_param_get((*msg).param, 'H' as i32, 0 as *const libc::c_char);
let grpid: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG4 as i32,
0 as *const libc::c_char,
);
if !grpid.is_null() {
mailimf_fields_add(
imf_fields,
@@ -718,8 +768,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
}
if !grpimage.is_null() {
let mut meta: *mut dc_msg_t = dc_msg_new_untyped((*factory).context);
(*meta).type_0 = 20i32;
dc_param_set((*meta).param, 'f' as i32, grpimage);
(*meta).type_0 = DC_MSG_IMAGE as libc::c_int;
dc_param_set((*meta).param, DC_PARAM_FILE as i32, grpimage);
let mut filename_as_sent: *mut libc::c_char = 0 as *mut libc::c_char;
meta_part = build_body_file(
meta,
@@ -737,8 +787,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
}
dc_msg_unref(meta);
}
if (*msg).type_0 == 41i32 || (*msg).type_0 == 40i32 || (*msg).type_0 == 50i32 {
if (*msg).type_0 == 41i32 {
if (*msg).type_0 == DC_MSG_VOICE as libc::c_int
|| (*msg).type_0 == DC_MSG_AUDIO as libc::c_int
|| (*msg).type_0 == DC_MSG_VIDEO as libc::c_int
{
if (*msg).type_0 == DC_MSG_VOICE as libc::c_int {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -747,8 +800,9 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
),
);
}
let duration_ms: libc::c_int = dc_param_get_int((*msg).param, 'd' as i32, 0i32);
if duration_ms > 0i32 {
let duration_ms: libc::c_int =
dc_param_get_int((*msg).param, DC_PARAM_DURATION as i32, 0);
if duration_ms > 0 {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -761,7 +815,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
);
}
}
afwd_email = dc_param_exists((*msg).param, 'a' as i32);
afwd_email = dc_param_exists((*msg).param, DC_PARAM_FORWARDED as i32);
let mut fwdhint: *mut libc::c_char = 0 as *mut libc::c_char;
if 0 != afwd_email {
fwdhint = dc_strdup(
@@ -813,18 +867,18 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
free(fwdhint as *mut libc::c_void);
free(placeholdertext as *mut libc::c_void);
/* add attachment part */
if (*msg).type_0 == 20i32
|| (*msg).type_0 == 21i32
|| (*msg).type_0 == 40i32
|| (*msg).type_0 == 41i32
|| (*msg).type_0 == 50i32
|| (*msg).type_0 == 60i32
if (*msg).type_0 == DC_MSG_IMAGE as libc::c_int
|| (*msg).type_0 == DC_MSG_GIF as libc::c_int
|| (*msg).type_0 == DC_MSG_AUDIO as libc::c_int
|| (*msg).type_0 == DC_MSG_VOICE as libc::c_int
|| (*msg).type_0 == DC_MSG_VIDEO as libc::c_int
|| (*msg).type_0 == DC_MSG_FILE as libc::c_int
{
if 0 == is_file_size_okay(msg) {
let error: *mut libc::c_char = dc_mprintf(
b"Message exceeds the recommended %i MB.\x00" as *const u8
as *const libc::c_char,
24i32 * 1024i32 * 1024i32 / 4i32 * 3i32 / 1000i32 / 1000i32,
24 * 1024 * 1024 / 4 * 3 / 1000 / 1000,
);
set_error(factory, error);
free(error as *mut libc::c_void);
@@ -844,7 +898,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
match current_block {
11328123142868406523 => {}
_ => {
if parts == 0i32 {
if parts == 0 {
set_error(
factory,
b"Empty message.\x00" as *const u8 as *const libc::c_char,
@@ -887,8 +941,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
}
}
if 0 != dc_is_sending_locations_to_chat((*msg).context, (*msg).chat_id) {
let mut last_added_location_id: uint32_t = 0i32 as uint32_t;
if dc_is_sending_locations_to_chat((*msg).context, (*msg).chat_id) {
let mut last_added_location_id: uint32_t = 0 as uint32_t;
let kml_file: *mut libc::c_char = dc_get_location_kml(
(*msg).context,
(*msg).chat_id,
@@ -943,12 +997,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
mailmime_add_part(message, multipart);
let p1: *mut libc::c_char;
let p2: *mut libc::c_char;
if 0 != dc_param_get_int((*(*factory).msg).param, 'c' as i32, 0i32) {
p1 = dc_stock_str((*factory).context, 24i32)
if 0 != dc_param_get_int((*(*factory).msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0) {
p1 = dc_stock_str((*factory).context, 24)
} else {
p1 = dc_msg_get_summarytext((*factory).msg, 32i32)
p1 = dc_msg_get_summarytext((*factory).msg, 32)
}
p2 = dc_stock_str_repl_string((*factory).context, 32i32, p1);
p2 = dc_stock_str_repl_string((*factory).context, 32, p1);
message_text = dc_mprintf(b"%s\r\n\x00" as *const u8 as *const libc::c_char, p2);
free(p2 as *mut libc::c_void);
free(p1 as *mut libc::c_void);
@@ -968,7 +1022,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
let mach_mime_part: *mut mailmime = mailmime_new_empty(content_type_0, mime_fields_0);
mailmime_set_body_text(mach_mime_part, message_text2, strlen(message_text2));
mailmime_add_part(multipart, mach_mime_part);
force_plaintext = 2i32;
force_plaintext = 2;
current_block = 9952640327414195044;
} else {
set_error(
@@ -983,7 +1037,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
if (*factory).loaded as libc::c_uint
== DC_MF_MDN_LOADED as libc::c_int as libc::c_uint
{
let e: *mut libc::c_char = dc_stock_str((*factory).context, 31i32);
let e: *mut libc::c_char = dc_stock_str((*factory).context, 31);
subject_str =
dc_mprintf(b"Chat: %s\x00" as *const u8 as *const libc::c_char, e);
free(e as *mut libc::c_void);
@@ -1019,7 +1073,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
0 as *mut mailimf_optional_field,
),
);
if force_plaintext != 2i32 {
if force_plaintext != 2 {
dc_e2ee_encrypt(
(*factory).context,
(*factory).recipients_addr,
@@ -1032,14 +1086,14 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
);
}
if 0 != e2ee_helper.encryption_successfull {
(*factory).out_encrypted = 1i32;
(*factory).out_encrypted = 1;
if 0 != do_gossip {
(*factory).out_gossiped = 1i32
(*factory).out_gossiped = 1
}
}
(*factory).out = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
mailmime_write_mem((*factory).out, &mut col, message);
success = 1i32
success = 1
}
}
}
@@ -1056,22 +1110,24 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
}
unsafe fn get_subject(
chat: *const dc_chat_t,
chat: *const Chat,
msg: *const dc_msg_t,
afwd_email: libc::c_int,
) -> *mut libc::c_char {
let context = (*chat).context;
let ret: *mut libc::c_char;
let raw_subject: *mut libc::c_char =
dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, (*msg).param, 32i32, context);
dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, (*msg).param, 32, context);
let fwd: *const libc::c_char = if 0 != afwd_email {
b"Fwd: \x00" as *const u8 as *const libc::c_char
} else {
b"\x00" as *const u8 as *const libc::c_char
};
if dc_param_get_int((*msg).param, 'S' as i32, 0i32) == 6i32 {
ret = dc_stock_str(context, 42i32)
} else if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 {
if dc_param_get_int((*msg).param, DC_PARAM_CMD as i32, 0) == 6 {
ret = dc_stock_str(context, 42)
} else if (*chat).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int
{
ret = dc_mprintf(
b"Chat: %s: %s%s\x00" as *const u8 as *const libc::c_char,
(*chat).name,
@@ -1128,14 +1184,17 @@ unsafe fn build_body_file(
let mut mime_sub: *mut mailmime = 0 as *mut mailmime;
let content: *mut mailmime_content;
let pathNfilename: *mut libc::c_char =
dc_param_get((*msg).param, 'f' as i32, 0 as *const libc::c_char);
let mut mimetype: *mut libc::c_char =
dc_param_get((*msg).param, 'm' as i32, 0 as *const libc::c_char);
dc_param_get((*msg).param, DC_PARAM_FILE as i32, 0 as *const libc::c_char);
let mut mimetype: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_MIMETYPE as i32,
0 as *const libc::c_char,
);
let suffix: *mut libc::c_char = dc_get_filesuffix_lc(pathNfilename);
let mut filename_to_send: *mut libc::c_char = 0 as *mut libc::c_char;
let mut filename_encoded: *mut libc::c_char = 0 as *mut libc::c_char;
if !pathNfilename.is_null() {
if (*msg).type_0 == 41i32 {
if (*msg).type_0 == DC_MSG_VOICE as libc::c_int {
let ts = chrono::Utc.timestamp((*msg).timestamp_sort as i64, 0);
let suffix = if !suffix.is_null() {
@@ -1146,10 +1205,12 @@ unsafe fn build_body_file(
let res = ts
.format(&format!("voice-message_%Y-%m-%d_%H-%M-%S.{}", suffix))
.to_string();
filename_to_send = strdup(to_cstring(res).as_ptr());
} else if (*msg).type_0 == 40i32 {
filename_to_send = to_cstring(res);
} else if (*msg).type_0 == DC_MSG_AUDIO as libc::c_int {
filename_to_send = dc_get_filename(pathNfilename)
} else if (*msg).type_0 == 20i32 || (*msg).type_0 == 21i32 {
} else if (*msg).type_0 == DC_MSG_IMAGE as libc::c_int
|| (*msg).type_0 == DC_MSG_GIF as libc::c_int
{
if base_name.is_null() {
base_name = b"image\x00" as *const u8 as *const libc::c_char
}
@@ -1162,7 +1223,7 @@ unsafe fn build_body_file(
b"dat\x00" as *const u8 as *const libc::c_char
},
)
} else if (*msg).type_0 == 50i32 {
} else if (*msg).type_0 == DC_MSG_VIDEO as libc::c_int {
filename_to_send = dc_mprintf(
b"video.%s\x00" as *const u8 as *const libc::c_char,
if !suffix.is_null() {
@@ -1178,14 +1239,14 @@ unsafe fn build_body_file(
if suffix.is_null() {
mimetype =
dc_strdup(b"application/octet-stream\x00" as *const u8 as *const libc::c_char)
} else if strcmp(suffix, b"png\x00" as *const u8 as *const libc::c_char) == 0i32 {
} else if strcmp(suffix, b"png\x00" as *const u8 as *const libc::c_char) == 0 {
mimetype = dc_strdup(b"image/png\x00" as *const u8 as *const libc::c_char)
} else if strcmp(suffix, b"jpg\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(suffix, b"jpeg\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(suffix, b"jpe\x00" as *const u8 as *const libc::c_char) == 0i32
} else if strcmp(suffix, b"jpg\x00" as *const u8 as *const libc::c_char) == 0
|| strcmp(suffix, b"jpeg\x00" as *const u8 as *const libc::c_char) == 0
|| strcmp(suffix, b"jpe\x00" as *const u8 as *const libc::c_char) == 0
{
mimetype = dc_strdup(b"image/jpeg\x00" as *const u8 as *const libc::c_char)
} else if strcmp(suffix, b"gif\x00" as *const u8 as *const libc::c_char) == 0i32 {
} else if strcmp(suffix, b"gif\x00" as *const u8 as *const libc::c_char) == 0 {
mimetype = dc_strdup(b"image/gif\x00" as *const u8 as *const libc::c_char)
} else {
mimetype =
@@ -1228,7 +1289,7 @@ unsafe fn build_body_file(
0 as *mut libc::c_char,
0 as *mut libc::c_char,
0 as *mut libc::c_char,
0i32 as size_t,
0 as size_t,
mailmime_parameter_new(
strdup(
b"filename*\x00" as *const u8 as *const libc::c_char,
@@ -1285,12 +1346,12 @@ unsafe fn build_body_file(
******************************************************************************/
unsafe fn is_file_size_okay(msg: *const dc_msg_t) -> libc::c_int {
let mut file_size_okay: libc::c_int = 1i32;
let mut file_size_okay: libc::c_int = 1;
let pathNfilename: *mut libc::c_char =
dc_param_get((*msg).param, 'f' as i32, 0 as *const libc::c_char);
dc_param_get((*msg).param, DC_PARAM_FILE as i32, 0 as *const libc::c_char);
let bytes: uint64_t = dc_get_filebytes((*msg).context, pathNfilename);
if bytes > (49i32 * 1024i32 * 1024i32 / 4i32 * 3i32) as libc::c_ulonglong {
file_size_okay = 0i32
if bytes > (49 * 1024 * 1024 / 4 * 3) as libc::c_ulonglong {
file_size_okay = 0;
}
free(pathNfilename as *mut libc::c_void);

View File

@@ -1,5 +1,5 @@
use std::collections::{HashMap, HashSet};
use std::ffi::{CStr, CString};
use std::ffi::CStr;
use charset::Charset;
use mmime::mailimf::*;
@@ -15,7 +15,6 @@ use crate::context::Context;
use crate::dc_contact::*;
use crate::dc_e2ee::*;
use crate::dc_location::*;
use crate::dc_log::*;
use crate::dc_param::*;
use crate::dc_simplify::*;
use crate::dc_stock::*;
@@ -176,10 +175,7 @@ pub unsafe fn dc_mimeparser_parse(
&mut (*mimeparser).e2ee_helper,
);
dc_mimeparser_parse_mime_recursive(mimeparser, (*mimeparser).mimeroot);
let field: *mut mailimf_field = dc_mimeparser_lookup_field(
mimeparser,
b"Subject\x00" as *const u8 as *const libc::c_char,
);
let field: *mut mailimf_field = dc_mimeparser_lookup_field(mimeparser, "Subject");
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
(*mimeparser).subject =
dc_decode_header_words((*(*field).fld_data.fld_subject).sbj_value)
@@ -192,12 +188,7 @@ pub unsafe fn dc_mimeparser_parse(
{
(*mimeparser).is_send_by_messenger = 1i32
}
if !dc_mimeparser_lookup_field(
mimeparser,
b"Autocrypt-Setup-Message\x00" as *const u8 as *const libc::c_char,
)
.is_null()
{
if !dc_mimeparser_lookup_field(mimeparser, "Autocrypt-Setup-Message").is_null() {
let mut i: libc::c_int;
let mut has_setup_file: libc::c_int = 0i32;
i = 0i32;
@@ -238,11 +229,7 @@ pub unsafe fn dc_mimeparser_parse(
}
}
}
if !dc_mimeparser_lookup_field(
mimeparser,
b"Chat-Group-Image\x00" as *const u8 as *const libc::c_char,
)
.is_null()
if !dc_mimeparser_lookup_field(mimeparser, "Chat-Group-Image").is_null()
&& carray_count((*mimeparser).parts) >= 1i32 as libc::c_uint
{
let textpart: *mut dc_mimepart_t =
@@ -336,7 +323,7 @@ pub unsafe fn dc_mimeparser_parse(
while i_1 < icnt_0 {
let part_2: *mut dc_mimepart_t =
carray_get((*mimeparser).parts, i_1 as libc::c_uint) as *mut dc_mimepart_t;
dc_param_set_int((*part_2).param, 'a' as i32, 1i32);
dc_param_set_int((*part_2).param, DC_PARAM_FORWARDED as i32, 1);
i_1 += 1
}
}
@@ -361,7 +348,7 @@ pub unsafe fn dc_mimeparser_parse(
if !field_0.is_null() {
let duration_ms: libc::c_int = dc_atoi_null_is_0((*field_0).fld_value);
if duration_ms > 0i32 && duration_ms < 24i32 * 60i32 * 60i32 * 1000i32 {
dc_param_set_int((*part_3).param, 'd' as i32, duration_ms);
dc_param_set_int((*part_3).param, DC_PARAM_DURATION as i32, duration_ms);
}
}
}
@@ -384,10 +371,8 @@ pub unsafe fn dc_mimeparser_parse(
{
let dn_to_addr: *mut libc::c_char = mailimf_find_first_addr(mb_list);
if !dn_to_addr.is_null() {
let from_field: *mut mailimf_field = dc_mimeparser_lookup_field(
mimeparser,
b"From\x00" as *const u8 as *const libc::c_char,
);
let from_field: *mut mailimf_field =
dc_mimeparser_lookup_field(mimeparser, "From");
if !from_field.is_null()
&& (*from_field).fld_type == MAILIMF_FIELD_FROM as libc::c_int
&& !(*from_field).fld_data.fld_from.is_null()
@@ -400,7 +385,11 @@ pub unsafe fn dc_mimeparser_parse(
let part_4: *mut dc_mimepart_t =
dc_mimeparser_get_last_nonmeta(mimeparser);
if !part_4.is_null() {
dc_param_set_int((*part_4).param, 'r' as i32, 1i32);
dc_param_set_int(
(*part_4).param,
DC_PARAM_WANTS_MDN as i32,
1,
);
}
}
free(from_addr as *mut libc::c_void);
@@ -488,13 +477,14 @@ pub unsafe fn mailimf_find_first_addr(mb_list: *const mailimf_mailbox_list) -> *
}
/* the following functions can be used only after a call to dc_mimeparser_parse() */
pub fn dc_mimeparser_lookup_field(
mimeparser: &dc_mimeparser_t,
field_name: *const libc::c_char,
field_name: &str,
) -> *mut mailimf_field {
mimeparser
.header
.get(as_str(field_name))
.get(field_name)
.map(|v| *v)
.unwrap_or_else(|| std::ptr::null_mut())
}
@@ -544,11 +534,9 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
b"rfc822-headers\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
dc_log_info(
info!(
(*mimeparser).context,
0i32,
b"Protected headers found in text/rfc822-headers attachment: Will be ignored.\x00"
as *const u8 as *const libc::c_char,
0, "Protected headers found in text/rfc822-headers attachment: Will be ignored.",
);
return 0i32;
}
@@ -562,11 +550,7 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
) != MAILIMF_NO_ERROR as libc::c_int
|| (*mimeparser).header_protected.is_null()
{
dc_log_warning(
(*mimeparser).context,
0i32,
b"Protected headers parsing error.\x00" as *const u8 as *const libc::c_char,
);
warn!((*mimeparser).context, 0, "Protected headers parsing error.",);
} else {
hash_header(
&mut (*mimeparser).header,
@@ -575,9 +559,11 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
);
}
} else {
dc_log_info((*mimeparser).context, 0i32,
b"Protected headers found in MIME header: Will be ignored as we already found an outer one.\x00"
as *const u8 as *const libc::c_char);
info!(
(*mimeparser).context,
0,
"Protected headers found in MIME header: Will be ignored as we already found an outer one."
);
}
}
match (*mime).mm_type {
@@ -769,10 +755,11 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
}
}
if plain_cnt == 1i32 && html_cnt == 1i32 {
dc_log_warning((*mimeparser).context, 0i32,
b"HACK: multipart/mixed message found with PLAIN and HTML, we\'ll skip the HTML part as this seems to be unwanted.\x00"
as *const u8 as
*const libc::c_char);
warn!(
(*mimeparser).context,
0i32,
"HACK: multipart/mixed message found with PLAIN and HTML, we\'ll skip the HTML part as this seems to be unwanted."
);
skip_part = html_part
}
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
@@ -850,6 +837,7 @@ unsafe fn hash_header(
18 => key = b"References\x00" as *const u8 as *const libc::c_char,
19 => key = b"Subject\x00" as *const u8 as *const libc::c_char,
22 => {
// MAILIMF_FIELD_OPTIONAL_FIELD
let optional_field: *const mailimf_optional_field =
(*field).fld_data.fld_optional_field;
if !optional_field.is_null() {
@@ -859,17 +847,16 @@ unsafe fn hash_header(
_ => {}
}
if !key.is_null() {
let key_len: libc::c_int = strlen(key) as libc::c_int;
if out.contains_key(as_str(key)) {
if (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int
|| key_len > 5i32
&& strncasecmp(key, b"Chat-\x00" as *const u8 as *const libc::c_char, 5)
== 0i32
{
out.insert(to_string(key), field);
}
} else {
out.insert(to_string(key), field);
// XXX the optional field sometimes contains invalid UTF8
// which should not happen (according to the mime standard).
// This might point to a bug in our mime parsing/processing
// logic. As mmime/dc_mimeparser is scheduled fore replacement
// anyway we just use a lossy conversion.
let key_r = &to_string_lossy(key);
if !out.contains_key(key_r) || // key already exists, only overwrite known types (protected headers)
(*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as i32 || key_r.starts_with("Chat-")
{
out.insert(key_r.to_string(), field);
}
}
cur1 = if !cur1.is_null() {
@@ -1221,19 +1208,16 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
current_block = 8795901732489102124;
} else {
decoded_data_bytes = res.len();
let res_c = CString::new(res.as_bytes()).unwrap();
decoded_data = strdup(res_c.as_ptr());
decoded_data = res.as_ptr() as *const libc::c_char;
current_block = 17788412896529399552;
}
} else {
dc_log_warning(
warn!(
mimeparser.context,
0i32,
b"Cannot convert %i bytes from \"%s\" to \"utf-8\".\x00"
as *const u8
as *const libc::c_char,
0,
"Cannot convert {} bytes from \"{}\" to \"utf-8\".",
decoded_data_bytes as libc::c_int,
charset,
as_str(charset),
);
current_block = 17788412896529399552;
}
@@ -1360,8 +1344,9 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
}
if !filename_parts.is_empty() {
free(desired_filename as *mut libc::c_void);
desired_filename =
dc_decode_ext_header(to_cstring(filename_parts).as_ptr());
let parts_c = to_cstring(filename_parts);
desired_filename = dc_decode_ext_header(parts_c);
free(parts_c as *mut _);
}
if desired_filename.is_null() {
let param = mailmime_find_ct_parameter(
@@ -1459,7 +1444,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
}
}
}
/* add object? (we do not add all objetcs, eg. signatures etc. are ignored) */
/* add object? (we do not add all objects, eg. signatures etc. are ignored) */
dc_simplify_unref(simplifier);
if !transfer_decoding_buffer.is_null() {
mmap_string_unref(transfer_decoding_buffer);
@@ -1505,8 +1490,8 @@ unsafe fn do_add_single_file_part(
(*part).type_0 = msg_type;
(*part).int_mimetype = mime_type;
(*part).bytes = decoded_data_bytes as libc::c_int;
dc_param_set((*part).param, 'f' as i32, pathNfilename);
dc_param_set((*part).param, 'm' as i32, raw_mime);
dc_param_set((*part).param, DC_PARAM_FILE as i32, pathNfilename);
dc_param_set((*part).param, DC_PARAM_MIMETYPE as i32, raw_mime);
if mime_type == 80i32 {
let mut w: uint32_t = 0i32 as uint32_t;
let mut h: uint32_t = 0i32 as uint32_t;
@@ -1516,8 +1501,8 @@ unsafe fn do_add_single_file_part(
&mut w,
&mut h,
) {
dc_param_set_int((*part).param, 'w' as i32, w as int32_t);
dc_param_set_int((*part).param, 'h' as i32, h as int32_t);
dc_param_set_int((*part).param, DC_PARAM_WIDTH as i32, w as int32_t);
dc_param_set_int((*part).param, DC_PARAM_HEIGHT as i32, h as int32_t);
}
}
do_add_single_part(parser, part);
@@ -1530,9 +1515,9 @@ unsafe fn do_add_single_file_part(
unsafe fn do_add_single_part(parser: &dc_mimeparser_t, part: *mut dc_mimepart_t) {
if 0 != (*parser).e2ee_helper.encrypted && (*parser).e2ee_helper.signatures.len() > 0 {
dc_param_set_int((*part).param, 'c' as i32, 1i32);
dc_param_set_int((*part).param, DC_PARAM_GUARANTEE_E2EE as i32, 1);
} else if 0 != (*parser).e2ee_helper.encrypted {
dc_param_set_int((*part).param, 'e' as i32, 0x2i32);
dc_param_set_int((*part).param, DC_PARAM_ERRONEOUS_E2EE as i32, 0x2);
}
carray_add(
(*parser).parts,
@@ -1627,12 +1612,7 @@ pub unsafe fn mailmime_transfer_decode(
// TODO should return bool /rtn
pub unsafe fn dc_mimeparser_is_mailinglist_message(mimeparser: &dc_mimeparser_t) -> libc::c_int {
if !dc_mimeparser_lookup_field(
&mimeparser,
b"List-Id\x00" as *const u8 as *const libc::c_char,
)
.is_null()
{
if !dc_mimeparser_lookup_field(&mimeparser, "List-Id").is_null() {
return 1i32;
}
let precedence: *mut mailimf_optional_field = dc_mimeparser_lookup_optional_field(

View File

@@ -2,44 +2,44 @@ use crate::constants::*;
use crate::context::*;
use crate::dc_job::*;
use crate::dc_msg::*;
use crate::dc_sqlite3::*;
use crate::types::*;
pub unsafe fn dc_do_heuristics_moves(
context: &Context,
folder: *const libc::c_char,
msg_id: uint32_t,
) {
// for already seen messages, folder may be different from msg->folder
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
let stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
if !(dc_sqlite3_get_config_int(
context,
&context.sql,
b"mvbox_move\x00" as *const u8 as *const libc::c_char,
1i32,
) == 0i32)
pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
if context
.sql
.get_config_int(context, "mvbox_move")
.unwrap_or_else(|| 1)
== 0
{
if !(0 == dc_is_inbox(context, folder) && 0 == dc_is_sentbox(context, folder)) {
msg = dc_msg_new_load(context, msg_id);
if !(0 != dc_msg_is_setupmessage(msg)) {
// do not move setup messages;
// there may be a non-delta device that wants to handle it
if 0 != dc_is_mvbox(context, folder) {
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_STAY);
} else if 0 != (*msg).is_dc_message {
dc_job_add(
context,
200i32,
(*msg).id as libc::c_int,
0 as *const libc::c_char,
0i32,
);
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_MOVING);
}
}
}
return;
}
sqlite3_finalize(stmt);
if !dc_is_inbox(context, folder) && !dc_is_sentbox(context, folder) {
return;
}
let msg = dc_msg_new_load(context, msg_id);
if dc_msg_is_setupmessage(msg) {
// do not move setup messages;
// there may be a non-delta device that wants to handle it
dc_msg_unref(msg);
return;
}
if dc_is_mvbox(context, folder) {
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_STAY);
}
// 1 = dc message, 2 = reply to dc message
if 0 != (*msg).is_dc_message {
dc_job_add(
context,
200,
(*msg).id as libc::c_int,
0 as *const libc::c_char,
0,
);
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_MOVING);
}
dc_msg_unref(msg);
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,67 +3,69 @@ use crate::types::*;
use crate::x::*;
/// for msgs and jobs
pub const DC_PARAM_FILE: char = 'f';
pub const DC_PARAM_FILE: char = 'f'; // string
/// for msgs
pub const DC_PARAM_WIDTH: char = 'w';
pub const DC_PARAM_WIDTH: char = 'w'; // int
/// for msgs
pub const DC_PARAM_HEIGHT: char = 'h';
pub const DC_PARAM_HEIGHT: char = 'h'; // int
/// for msgs
pub const DC_PARAM_DURATION: char = 'd';
pub const DC_PARAM_DURATION: char = 'd'; // int
/// for msgs
pub const DC_PARAM_MIMETYPE: char = 'm';
pub const DC_PARAM_MIMETYPE: char = 'm'; // string
/// for msgs: incoming: message is encryoted, outgoing: guarantee E2EE or the message is not send
pub const DC_PARAM_GUARANTEE_E2EE: char = 'c';
pub const DC_PARAM_GUARANTEE_E2EE: char = 'c'; // int (bool?)
/// for msgs: decrypted with validation errors or without mutual set, if neither 'c' nor 'e' are preset, the messages is only transport encrypted
pub const DC_PARAM_ERRONEOUS_E2EE: char = 'e';
pub const DC_PARAM_ERRONEOUS_E2EE: char = 'e'; // int
/// for msgs: force unencrypted message, either DC_FP_ADD_AUTOCRYPT_HEADER (1), DC_FP_NO_AUTOCRYPT_HEADER (2) or 0
pub const DC_PARAM_FORCE_PLAINTEXT: char = 'u';
/// for msgs: an incoming message which requestes a MDN (aka read receipt)
pub const DC_PARAM_WANTS_MDN: char = 'r';
pub const DC_PARAM_FORCE_PLAINTEXT: char = 'u'; // int (bool?)
/// for msgs: an incoming message which requests a MDN (aka read receipt)
pub const DC_PARAM_WANTS_MDN: char = 'r'; // int (bool?)
/// for msgs
pub const DC_PARAM_FORWARDED: char = 'a';
pub const DC_PARAM_FORWARDED: char = 'a'; // int (bool?)
/// for msgs
pub const DC_PARAM_CMD: char = 'S';
pub const DC_PARAM_CMD: char = 'S'; // int
/// for msgs
pub const DC_PARAM_CMD_ARG: char = 'E';
pub const DC_PARAM_CMD_ARG: char = 'E'; // string
/// for msgs
pub const DC_PARAM_CMD_ARG2: char = 'F';
pub const DC_PARAM_CMD_ARG2: char = 'F'; // string
/// for msgs
pub const DC_PARAM_CMD_ARG3: char = 'G';
pub const DC_PARAM_CMD_ARG3: char = 'G'; // string
/// for msgs
pub const DC_PARAM_CMD_ARG4: char = 'H';
pub const DC_PARAM_CMD_ARG4: char = 'H'; // string
/// for msgs
pub const DC_PARAM_ERROR: char = 'L';
pub const DC_PARAM_ERROR: char = 'L'; // string
/// for msgs in PREPARING: space-separated list of message IDs of forwarded copies
pub const DC_PARAM_PREP_FORWARDS: char = 'P';
pub const DC_PARAM_PREP_FORWARDS: char = 'P'; // string
/// for msgs
pub const DC_PARAM_SET_LATITUDE: char = 'l';
pub const DC_PARAM_SET_LATITUDE: char = 'l'; // float
/// for msgs
pub const DC_PARAM_SET_LONGITUDE: char = 'n';
pub const DC_PARAM_SET_LONGITUDE: char = 'n'; // float
/// for jobs
pub const DC_PARAM_SERVER_FOLDER: char = 'Z';
pub const DC_PARAM_SERVER_FOLDER: char = 'Z'; // string
/// for jobs
pub const DC_PARAM_SERVER_UID: char = 'z';
pub const DC_PARAM_SERVER_UID: char = 'z'; // int
/// for jobs
pub const DC_PARAM_ALSO_MOVE: char = 'M';
pub const DC_PARAM_ALSO_MOVE: char = 'M'; // int (bool?)
/// for jobs: space-separated list of message recipients
pub const DC_PARAM_RECIPIENTS: char = 'R';
pub const DC_PARAM_RECIPIENTS: char = 'R'; // stringap
/// for groups
pub const DC_PARAM_UNPROMOTED: char = 'U';
pub const DC_PARAM_UNPROMOTED: char = 'U'; // int (bool?)
/// for groups and contacts
pub const DC_PARAM_PROFILE_IMAGE: char = 'i';
pub const DC_PARAM_PROFILE_IMAGE: char = 'i'; // string (bytes?)
/// for chats
pub const DC_PARAM_SELFTALK: char = 'K';
// missing: 's', 'x', 'g'
// values for DC_PARAM_FORCE_PLAINTEXT
pub const DC_FP_ADD_AUTOCRYPT_HEADER: u8 = 1;
pub const DC_FP_NO_AUTOCRYPT_HEADER: u8 = 2;
/// An object for handling key=value parameter lists; for the key, curently only
/// An object for handling key=value parameter lists; for the key, currently only
/// a single character is allowed.
///
/// The object is used eg. by dc_chat_t or dc_msg_t, for readable paramter names,
/// The object is used eg. by Chat or dc_msg_t, for readable parameter names,
/// these classes define some DC_PARAM_* constantats.
///
/// Only for library-internal use.

View File

@@ -1,7 +1,6 @@
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_contact::*;
use crate::dc_log::*;
use crate::dc_lot::*;
use crate::dc_param::*;
use crate::dc_strencode::*;
@@ -22,7 +21,7 @@ use crate::x::*;
// text1=URL
// text1=error string
pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc_lot_t {
let mut current_block: u64;
let mut OK_TO_CONTINUE = true;
let mut payload: *mut libc::c_char = 0 as *mut libc::c_char;
// must be normalized, if set
let mut addr: *mut libc::c_char = 0 as *mut libc::c_char;
@@ -38,12 +37,7 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
let mut grpname: *mut libc::c_char = 0 as *mut libc::c_char;
(*qr_parsed).state = 0i32;
if !qr.is_null() {
dc_log_info(
context,
0i32,
b"Scanned QR code: %s\x00" as *const u8 as *const libc::c_char,
qr,
);
info!(context, 0, "Scanned QR code: {}", as_str(qr),);
/* split parameters from the qr code
------------------------------------ */
if strncasecmp(
@@ -62,16 +56,23 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
fragment = fragment.offset(1isize);
let param: *mut dc_param_t = dc_param_new();
dc_param_set_urlencoded(param, fragment);
addr = dc_param_get(param, 'a' as i32, 0 as *const libc::c_char);
addr = dc_param_get(param, DC_PARAM_FORWARDED as i32, 0 as *const libc::c_char);
if !addr.is_null() {
let mut urlencoded: *mut libc::c_char =
dc_param_get(param, 'n' as i32, 0 as *const libc::c_char);
let mut urlencoded: *mut libc::c_char = dc_param_get(
param,
DC_PARAM_SET_LONGITUDE as i32,
0 as *const libc::c_char,
);
if !urlencoded.is_null() {
name = dc_urldecode(urlencoded);
dc_normalize_name(name);
free(urlencoded as *mut libc::c_void);
}
invitenumber = dc_param_get(param, 'i' as i32, 0 as *const libc::c_char);
invitenumber = dc_param_get(
param,
DC_PARAM_PROFILE_IMAGE as i32,
0 as *const libc::c_char,
);
auth = dc_param_get(param, 's' as i32, 0 as *const libc::c_char);
grpid = dc_param_get(param, 'x' as i32, 0 as *const libc::c_char);
if !grpid.is_null() {
@@ -85,7 +86,6 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
dc_param_unref(param);
}
fingerprint = dc_normalize_fingerprint_c(payload);
current_block = 5023038348526654800;
} else if strncasecmp(
qr,
b"mailto:\x00" as *const u8 as *const libc::c_char,
@@ -100,7 +100,6 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
*query = 0i32 as libc::c_char
}
addr = dc_strdup(payload);
current_block = 5023038348526654800;
} else if strncasecmp(
qr,
b"SMTP:\x00" as *const u8 as *const libc::c_char,
@@ -115,7 +114,6 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
*colon = 0i32 as libc::c_char
}
addr = dc_strdup(payload);
current_block = 5023038348526654800;
} else if strncasecmp(
qr,
b"MATMSG:\x00" as *const u8 as *const libc::c_char,
@@ -131,12 +129,11 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
if !semicolon.is_null() {
*semicolon = 0i32 as libc::c_char
}
current_block = 5023038348526654800;
} else {
(*qr_parsed).state = 400i32;
(*qr_parsed).text1 =
dc_strdup(b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char);
current_block = 16562876845594826114;
OK_TO_CONTINUE = false;
}
} else {
if strncasecmp(
@@ -189,146 +186,111 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
}
dc_free_splitted_lines(lines);
}
current_block = 5023038348526654800;
}
match current_block {
16562876845594826114 => {}
_ => {
/* check the paramters
---------------------- */
if !addr.is_null() {
/* urldecoding is needed at least for OPENPGP4FPR but should not hurt in the other cases */
let mut temp: *mut libc::c_char = dc_urldecode(addr);
free(addr as *mut libc::c_void);
addr = temp;
temp = dc_addr_normalize(addr);
free(addr as *mut libc::c_void);
addr = temp;
if !dc_may_be_valid_addr(addr) {
(*qr_parsed).state = 400i32;
(*qr_parsed).text1 = dc_strdup(
b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char,
if OK_TO_CONTINUE {
/* check the parameters
---------------------- */
if !addr.is_null() {
/* urldecoding is needed at least for OPENPGP4FPR but should not hurt in the other cases */
let mut temp: *mut libc::c_char = dc_urldecode(addr);
free(addr as *mut libc::c_void);
addr = temp;
temp = dc_addr_normalize(addr);
free(addr as *mut libc::c_void);
addr = temp;
if !dc_may_be_valid_addr(addr) {
(*qr_parsed).state = 400i32;
(*qr_parsed).text1 =
dc_strdup(b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char);
OK_TO_CONTINUE = false;
}
}
}
if OK_TO_CONTINUE {
if !fingerprint.is_null() {
if strlen(fingerprint) != 40 {
(*qr_parsed).state = 400i32;
(*qr_parsed).text1 = dc_strdup(
b"Bad fingerprint length in QR code.\x00" as *const u8
as *const libc::c_char,
);
OK_TO_CONTINUE = false;
}
}
}
if OK_TO_CONTINUE {
if !fingerprint.is_null() {
let peerstate =
Peerstate::from_fingerprint(context, &context.sql, as_str(fingerprint));
if addr.is_null() || invitenumber.is_null() || auth.is_null() {
if let Some(peerstate) = peerstate {
(*qr_parsed).state = 210i32;
let addr_ptr = if let Some(ref addr) = peerstate.addr {
to_cstring(addr)
} else {
std::ptr::null()
};
(*qr_parsed).id = dc_add_or_lookup_contact(
context,
0 as *const libc::c_char,
addr_ptr,
0x80i32,
0 as *mut libc::c_int,
);
current_block = 16562876845594826114;
free(addr_ptr as *mut _);
dc_create_or_lookup_nchat_by_contact_id(
context,
(*qr_parsed).id,
2i32,
&mut chat_id,
0 as *mut libc::c_int,
);
device_msg = dc_mprintf(
b"%s verified.\x00" as *const u8 as *const libc::c_char,
peerstate.addr,
)
} else {
current_block = 14116432890150942211;
(*qr_parsed).text1 = dc_format_fingerprint_c(fingerprint);
(*qr_parsed).state = 230i32
}
} else {
current_block = 14116432890150942211;
}
match current_block {
16562876845594826114 => {}
_ => {
if !fingerprint.is_null() {
if strlen(fingerprint) != 40 {
(*qr_parsed).state = 400i32;
(*qr_parsed).text1 = dc_strdup(
b"Bad fingerprint length in QR code.\x00" as *const u8
as *const libc::c_char,
);
current_block = 16562876845594826114;
} else {
current_block = 5409161009579131794;
}
} else {
current_block = 5409161009579131794;
}
match current_block {
16562876845594826114 => {}
_ => {
if !fingerprint.is_null() {
let peerstate = Peerstate::from_fingerprint(
context,
&context.sql,
as_str(fingerprint),
);
if addr.is_null() || invitenumber.is_null() || auth.is_null() {
if let Some(peerstate) = peerstate {
(*qr_parsed).state = 210i32;
let c_addr = peerstate
.addr
.as_ref()
.map(to_cstring)
.unwrap_or_default();
let addr_ptr = if peerstate.addr.is_some() {
c_addr.as_ptr()
} else {
std::ptr::null()
};
(*qr_parsed).id = dc_add_or_lookup_contact(
context,
0 as *const libc::c_char,
addr_ptr,
0x80i32,
0 as *mut libc::c_int,
);
dc_create_or_lookup_nchat_by_contact_id(
context,
(*qr_parsed).id,
2i32,
&mut chat_id,
0 as *mut libc::c_int,
);
device_msg = dc_mprintf(
b"%s verified.\x00" as *const u8
as *const libc::c_char,
peerstate.addr,
)
} else {
(*qr_parsed).text1 =
dc_format_fingerprint_c(fingerprint);
(*qr_parsed).state = 230i32
}
} else {
if !grpid.is_null() && !grpname.is_null() {
(*qr_parsed).state = 202i32;
(*qr_parsed).text1 = dc_strdup(grpname);
(*qr_parsed).text2 = dc_strdup(grpid)
} else {
(*qr_parsed).state = 200i32
}
(*qr_parsed).id = dc_add_or_lookup_contact(
context,
name,
addr,
0x80i32,
0 as *mut libc::c_int,
);
(*qr_parsed).fingerprint = dc_strdup(fingerprint);
(*qr_parsed).invitenumber = dc_strdup(invitenumber);
(*qr_parsed).auth = dc_strdup(auth)
}
} else if !addr.is_null() {
(*qr_parsed).state = 320i32;
(*qr_parsed).id = dc_add_or_lookup_contact(
context,
name,
addr,
0x80i32,
0 as *mut libc::c_int,
)
} else if strstr(
qr,
b"http://\x00" as *const u8 as *const libc::c_char,
) == qr as *mut libc::c_char
|| strstr(
qr,
b"https://\x00" as *const u8 as *const libc::c_char,
) == qr as *mut libc::c_char
{
(*qr_parsed).state = 332i32;
(*qr_parsed).text1 = dc_strdup(qr)
} else {
(*qr_parsed).state = 330i32;
(*qr_parsed).text1 = dc_strdup(qr)
}
if !device_msg.is_null() {
dc_add_device_msg(context, chat_id, device_msg);
}
}
}
if !grpid.is_null() && !grpname.is_null() {
(*qr_parsed).state = 202i32;
(*qr_parsed).text1 = dc_strdup(grpname);
(*qr_parsed).text2 = dc_strdup(grpid)
} else {
(*qr_parsed).state = 200i32
}
(*qr_parsed).id = dc_add_or_lookup_contact(
context,
name,
addr,
0x80i32,
0 as *mut libc::c_int,
);
(*qr_parsed).fingerprint = dc_strdup(fingerprint);
(*qr_parsed).invitenumber = dc_strdup(invitenumber);
(*qr_parsed).auth = dc_strdup(auth)
}
} else if !addr.is_null() {
(*qr_parsed).state = 320i32;
(*qr_parsed).id =
dc_add_or_lookup_contact(context, name, addr, 0x80i32, 0 as *mut libc::c_int)
} else if strstr(qr, b"http://\x00" as *const u8 as *const libc::c_char)
== qr as *mut libc::c_char
|| strstr(qr, b"https://\x00" as *const u8 as *const libc::c_char)
== qr as *mut libc::c_char
{
(*qr_parsed).state = 332i32;
(*qr_parsed).text1 = dc_strdup(qr)
} else {
(*qr_parsed).state = 330i32;
(*qr_parsed).text1 = dc_strdup(qr)
}
if !device_msg.is_null() {
dc_add_device_msg(context, chat_id, device_msg);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
use mmime::mailimf_types::*;
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use crate::aheader::EncryptPreference;
use crate::constants::Event;
@@ -8,13 +9,11 @@ use crate::dc_chat::*;
use crate::dc_configure::*;
use crate::dc_contact::*;
use crate::dc_e2ee::*;
use crate::dc_log::*;
use crate::dc_lot::*;
use crate::dc_mimeparser::*;
use crate::dc_msg::*;
use crate::dc_param::*;
use crate::dc_qr::*;
use crate::dc_sqlite3::*;
use crate::dc_stock::*;
use crate::dc_strencode::*;
use crate::dc_token::*;
@@ -28,22 +27,18 @@ pub unsafe fn dc_get_securejoin_qr(
context: &Context,
group_chat_id: uint32_t,
) -> *mut libc::c_char {
let current_block: u64;
/* =========================================================
==== Alice - the inviter side ====
==== Step 1 in "Setup verified contact" protocol ====
========================================================= */
let mut qr: *mut libc::c_char = 0 as *mut libc::c_char;
let self_addr: *mut libc::c_char;
let mut self_addr_urlencoded: *mut libc::c_char = 0 as *mut libc::c_char;
let mut self_name: *mut libc::c_char = 0 as *mut libc::c_char;
let mut self_name_urlencoded: *mut libc::c_char = 0 as *mut libc::c_char;
let mut fingerprint: *mut libc::c_char = 0 as *mut libc::c_char;
let mut fingerprint = 0 as *mut libc::c_char;
let mut invitenumber: *mut libc::c_char;
let mut auth: *mut libc::c_char;
let mut chat: *mut dc_chat_t = 0 as *mut dc_chat_t;
let mut group_name: *mut libc::c_char = 0 as *mut libc::c_char;
let mut group_name_urlencoded: *mut libc::c_char = 0 as *mut libc::c_char;
let mut chat = 0 as *mut Chat;
let mut group_name = 0 as *mut libc::c_char;
let mut group_name_urlencoded = 0 as *mut libc::c_char;
let mut qr = None;
dc_ensure_secret_key_exists(context);
invitenumber = dc_token_lookup(context, DC_TOKEN_INVITENUMBER, group_chat_id);
@@ -56,110 +51,86 @@ pub unsafe fn dc_get_securejoin_qr(
auth = dc_create_id();
dc_token_save(context, DC_TOKEN_AUTH, group_chat_id, auth);
}
self_addr = dc_sqlite3_get_config(
context,
&context.sql,
b"configured_addr\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
if self_addr.is_null() {
dc_log_error(
context,
0i32,
b"Not configured, cannot generate QR code.\x00" as *const u8 as *const libc::c_char,
);
} else {
self_name = dc_sqlite3_get_config(
context,
&context.sql,
b"displayname\x00" as *const u8 as *const libc::c_char,
b"\x00" as *const u8 as *const libc::c_char,
);
fingerprint = get_self_fingerprint(context);
if !fingerprint.is_null() {
self_addr_urlencoded = dc_urlencode(self_addr);
self_name_urlencoded = dc_urlencode(self_name);
if 0 != group_chat_id {
chat = dc_get_chat(context, group_chat_id);
if chat.is_null() {
dc_log_error(
context,
0i32,
b"Cannot get QR-code for chat-id %i\x00" as *const u8
as *const libc::c_char,
group_chat_id,
);
current_block = 9531737720721467826;
} else {
group_name = dc_chat_get_name(chat);
group_name_urlencoded = dc_urlencode(group_name);
qr = dc_mprintf(
b"OPENPGP4FPR:%s#a=%s&g=%s&x=%s&i=%s&s=%s\x00" as *const u8
as *const libc::c_char,
fingerprint,
self_addr_urlencoded,
group_name_urlencoded,
(*chat).grpid,
invitenumber,
auth,
);
current_block = 1118134448028020070;
}
} else {
qr = dc_mprintf(
b"OPENPGP4FPR:%s#a=%s&n=%s&i=%s&s=%s\x00" as *const u8 as *const libc::c_char,
fingerprint,
self_addr_urlencoded,
self_name_urlencoded,
invitenumber,
auth,
);
current_block = 1118134448028020070;
}
match current_block {
9531737720721467826 => {}
_ => {
dc_log_info(
context,
0i32,
b"Generated QR code: %s\x00" as *const u8 as *const libc::c_char,
qr,
);
}
}
let self_addr = context.sql.get_config(context, "configured_addr");
let cleanup = |fingerprint, chat, group_name, group_name_urlencoded| {
free(fingerprint as *mut libc::c_void);
free(invitenumber as *mut libc::c_void);
free(auth as *mut libc::c_void);
dc_chat_unref(chat);
free(group_name as *mut libc::c_void);
free(group_name_urlencoded as *mut libc::c_void);
if let Some(qr) = qr {
to_cstring(qr)
} else {
std::ptr::null_mut()
}
};
if self_addr.is_none() {
error!(context, 0, "Not configured, cannot generate QR code.",);
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
}
free(self_addr_urlencoded as *mut libc::c_void);
free(self_addr as *mut libc::c_void);
free(self_name as *mut libc::c_void);
free(self_name_urlencoded as *mut libc::c_void);
free(fingerprint as *mut libc::c_void);
free(invitenumber as *mut libc::c_void);
free(auth as *mut libc::c_void);
dc_chat_unref(chat);
free(group_name as *mut libc::c_void);
free(group_name_urlencoded as *mut libc::c_void);
return if !qr.is_null() {
qr
let self_addr = self_addr.unwrap();
let self_name = context
.sql
.get_config(context, "displayname")
.unwrap_or_default();
fingerprint = get_self_fingerprint(context);
if fingerprint.is_null() {
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
}
let self_addr_urlencoded = utf8_percent_encode(&self_addr, DEFAULT_ENCODE_SET).to_string();
let self_name_urlencoded = utf8_percent_encode(&self_name, DEFAULT_ENCODE_SET).to_string();
qr = if 0 != group_chat_id {
chat = dc_get_chat(context, group_chat_id);
if chat.is_null() {
error!(
context,
0, "Cannot get QR-code for chat-id {}", group_chat_id,
);
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
}
group_name = dc_chat_get_name(chat);
group_name_urlencoded = dc_urlencode(group_name);
Some(format!(
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
as_str(fingerprint),
self_addr_urlencoded,
as_str(group_name_urlencoded),
as_str((*chat).grpid),
as_str(invitenumber),
as_str(auth),
))
} else {
dc_strdup(0 as *const libc::c_char)
Some(format!(
"OPENPGP4FPR:{}#a={}&n={}&i={}&s={}",
as_str(fingerprint),
self_addr_urlencoded,
self_name_urlencoded,
as_str(invitenumber),
as_str(auth),
))
};
info!(context, 0, "Generated QR code: {}", qr.as_ref().unwrap());
cleanup(fingerprint, chat, group_name, group_name_urlencoded)
}
unsafe fn get_self_fingerprint(context: &Context) -> *mut libc::c_char {
let self_addr = dc_sqlite3_get_config(
context,
&context.sql,
b"configured_addr\x00" as *const u8 as *const libc::c_char,
0 as *const libc::c_char,
);
if self_addr.is_null() {
return std::ptr::null_mut();
}
if let Some(key) = Key::from_self_public(context, self_addr, &context.sql) {
return key.fingerprint_c();
fn get_self_fingerprint(context: &Context) -> *mut libc::c_char {
if let Some(self_addr) = context.sql.get_config(context, "configured_addr") {
if let Some(key) = Key::from_self_public(context, self_addr, &context.sql) {
return key.fingerprint_c();
}
}
std::ptr::null_mut()
@@ -175,29 +146,17 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
let mut contact_chat_id: uint32_t = 0i32 as uint32_t;
let mut join_vg: libc::c_int = 0i32;
let mut qr_scan: *mut dc_lot_t = 0 as *mut dc_lot_t;
dc_log_info(
context,
0i32,
b"Requesting secure-join ...\x00" as *const u8 as *const libc::c_char,
);
info!(context, 0, "Requesting secure-join ...",);
dc_ensure_secret_key_exists(context);
ongoing_allocated = dc_alloc_ongoing(context);
if !(ongoing_allocated == 0i32) {
qr_scan = dc_check_qr(context, qr);
if qr_scan.is_null() || (*qr_scan).state != 200i32 && (*qr_scan).state != 202i32 {
dc_log_error(
context,
0i32,
b"Unknown QR code.\x00" as *const u8 as *const libc::c_char,
);
error!(context, 0, "Unknown QR code.",);
} else {
contact_chat_id = dc_create_chat_by_contact_id(context, (*qr_scan).id);
if contact_chat_id == 0i32 as libc::c_uint {
dc_log_error(
context,
0i32,
b"Unknown contact.\x00" as *const u8 as *const libc::c_char,
);
error!(context, 0, "Unknown contact.",);
} else if !(context
.running_state
.clone()
@@ -214,11 +173,7 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
}
if 0 != fingerprint_equals_sender(context, (*qr_scan).fingerprint, contact_chat_id)
{
dc_log_info(
context,
0i32,
b"Taking protocol shortcut.\x00" as *const u8 as *const libc::c_char,
);
info!(context, 0, "Taking protocol shortcut.");
context.bob.clone().write().unwrap().expects = 6;
context.call_cb(
Event::SECUREJOIN_JOINER_PROGRESS,
@@ -312,23 +267,23 @@ unsafe fn send_handshake_msg(
step,
);
(*msg).hidden = 1i32;
dc_param_set_int((*msg).param, 'S' as i32, 7i32);
dc_param_set((*msg).param, 'E' as i32, step);
dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 7);
dc_param_set((*msg).param, DC_PARAM_CMD_ARG as i32, step);
if !param2.is_null() {
dc_param_set((*msg).param, 'F' as i32, param2);
dc_param_set((*msg).param, DC_PARAM_CMD_ARG2 as i32, param2);
}
if !fingerprint.is_null() {
dc_param_set((*msg).param, 'G' as i32, fingerprint);
dc_param_set((*msg).param, DC_PARAM_CMD_ARG3 as i32, fingerprint);
}
if !grpid.is_null() {
dc_param_set((*msg).param, 'H' as i32, grpid);
dc_param_set((*msg).param, DC_PARAM_CMD_ARG4 as i32, grpid);
}
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(step, b"vc-request\x00" as *const u8 as *const libc::c_char) == 0i32
{
dc_param_set_int((*msg).param, 'u' as i32, 1i32);
dc_param_set_int((*msg).param, DC_PARAM_FORCE_PLAINTEXT as i32, 1);
} else {
dc_param_set_int((*msg).param, 'c' as i32, 1i32);
dc_param_set_int((*msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 1);
}
dc_send_msg(context, contact_chat_id, msg);
dc_msg_unref(msg);
@@ -401,17 +356,13 @@ pub unsafe fn dc_handle_securejoin_handshake(
let mut ret: libc::c_int = 0i32;
let mut contact: *mut dc_contact_t = 0 as *mut dc_contact_t;
if !(contact_id <= 9i32 as libc::c_uint) {
step = lookup_field(
mimeparser,
b"Secure-Join\x00" as *const u8 as *const libc::c_char,
);
step = lookup_field(mimeparser, "Secure-Join");
if !step.is_null() {
dc_log_info(
info!(
context,
0i32,
b">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'%s\' received\x00" as *const u8
as *const libc::c_char,
step,
0,
">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received",
as_str(step),
);
join_vg = (strncmp(step, b"vg-\x00" as *const u8 as *const libc::c_char, 3) == 0)
as libc::c_int;
@@ -438,32 +389,16 @@ pub unsafe fn dc_handle_securejoin_handshake(
// send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here.
// verify that the `Secure-Join-Invitenumber:`-header matches invitenumber written to the QR code
let invitenumber: *const libc::c_char;
invitenumber = lookup_field(
mimeparser,
b"Secure-Join-Invitenumber\x00" as *const u8 as *const libc::c_char,
);
invitenumber = lookup_field(mimeparser, "Secure-Join-Invitenumber");
if invitenumber.is_null() {
dc_log_warning(
context,
0i32,
b"Secure-join denied (invitenumber missing).\x00" as *const u8
as *const libc::c_char,
);
warn!(context, 0, "Secure-join denied (invitenumber missing).",);
current_block = 4378276786830486580;
} else if dc_token_exists(context, DC_TOKEN_INVITENUMBER, invitenumber) == 0i32 {
dc_log_warning(
context,
0i32,
b"Secure-join denied (bad invitenumber).\x00" as *const u8
as *const libc::c_char,
);
} else if !dc_token_exists(context, DC_TOKEN_INVITENUMBER, invitenumber) {
warn!(context, 0, "Secure-join denied (bad invitenumber).",);
current_block = 4378276786830486580;
} else {
dc_log_info(
context,
0i32,
b"Secure-join requested.\x00" as *const u8 as *const libc::c_char,
);
info!(context, 0, "Secure-join requested.",);
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
contact_id as uintptr_t,
@@ -500,12 +435,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
};
if cond {
dc_log_warning(
context,
0i32,
b"auth-required message out of sync.\x00" as *const u8
as *const libc::c_char,
);
warn!(context, 0, "auth-required message out of sync.",);
// no error, just aborted somehow or a mail from another handshake
current_block = 4378276786830486580;
} else {
@@ -545,11 +475,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
end_bobs_joining(context, 0i32);
current_block = 4378276786830486580;
} else {
dc_log_info(
context,
0i32,
b"Fingerprint verified.\x00" as *const u8 as *const libc::c_char,
);
info!(context, 0, "Fingerprint verified.",);
own_fingerprint = get_self_fingerprint(context);
context.call_cb(
Event::SECUREJOIN_JOINER_PROGRESS,
@@ -589,10 +515,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
============================================================ */
// verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob
let fingerprint: *const libc::c_char;
fingerprint = lookup_field(
mimeparser,
b"Secure-Join-Fingerprint\x00" as *const u8 as *const libc::c_char,
);
fingerprint = lookup_field(mimeparser, "Secure-Join-Fingerprint");
if fingerprint.is_null() {
could_not_establish_secure_connection(
context,
@@ -616,17 +539,10 @@ pub unsafe fn dc_handle_securejoin_handshake(
);
current_block = 4378276786830486580;
} else {
dc_log_info(
context,
0i32,
b"Fingerprint verified.\x00" as *const u8 as *const libc::c_char,
);
info!(context, 0, "Fingerprint verified.",);
// verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code
let auth_0: *const libc::c_char;
auth_0 = lookup_field(
mimeparser,
b"Secure-Join-Auth\x00" as *const u8 as *const libc::c_char,
);
auth_0 = lookup_field(mimeparser, "Secure-Join-Auth");
if auth_0.is_null() {
could_not_establish_secure_connection(
context,
@@ -634,7 +550,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
b"Auth not provided.\x00" as *const u8 as *const libc::c_char,
);
current_block = 4378276786830486580;
} else if dc_token_exists(context, DC_TOKEN_AUTH, auth_0) == 0i32 {
} else if !dc_token_exists(context, DC_TOKEN_AUTH, auth_0) {
could_not_establish_secure_connection(
context,
contact_chat_id,
@@ -651,11 +567,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
current_block = 4378276786830486580;
} else {
dc_scaleup_contact_origin(context, contact_id, 0x1000000i32);
dc_log_info(
context,
0i32,
b"Auth verified.\x00" as *const u8 as *const libc::c_char,
);
info!(context, 0, "Auth verified.",);
secure_connection_established(context, contact_chat_id);
context.call_cb(
Event::CONTACTS_CHANGED,
@@ -668,10 +580,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
600i32 as uintptr_t,
);
if 0 != join_vg {
grpid = dc_strdup(lookup_field(
mimeparser,
b"Secure-Join-Group\x00" as *const u8 as *const libc::c_char,
));
grpid = dc_strdup(lookup_field(mimeparser, "Secure-Join-Group"));
let group_chat_id: uint32_t = dc_get_chat_id_by_grpid(
context,
grpid,
@@ -679,12 +588,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
0 as *mut libc::c_int,
);
if group_chat_id == 0i32 as libc::c_uint {
dc_log_error(
context,
0i32,
b"Chat %s not found.\x00" as *const u8 as *const libc::c_char,
grpid,
);
error!(context, 0, "Chat {} not found.", as_str(grpid),);
current_block = 4378276786830486580;
} else {
dc_add_contact_to_chat_ex(
@@ -726,12 +630,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
ret = 0x1i32
}
if context.bob.clone().read().unwrap().expects != 6 {
dc_log_info(
context,
0i32,
b"Message belongs to a different handshake.\x00" as *const u8
as *const libc::c_char,
);
info!(context, 0, "Message belongs to a different handshake.",);
current_block = 4378276786830486580;
} else {
let cond = {
@@ -739,11 +638,9 @@ pub unsafe fn dc_handle_securejoin_handshake(
scan.is_null() || 0 != join_vg && (*scan).state != 202
};
if cond {
dc_log_warning(
warn!(
context,
0i32,
b"Message out of sync or belongs to a different handshake.\x00"
as *const u8 as *const libc::c_char,
0, "Message out of sync or belongs to a different handshake.",
);
current_block = 4378276786830486580;
} else {
@@ -805,16 +702,13 @@ pub unsafe fn dc_handle_securejoin_handshake(
if 0 != join_vg {
if 0 == dc_addr_equals_self(
context,
lookup_field(
mimeparser,
b"Chat-Group-Member-Added\x00" as *const u8
as *const libc::c_char,
),
lookup_field(mimeparser, "Chat-Group-Member-Added"),
) {
dc_log_info(context, 0i32,
b"Message belongs to a different handshake (scaled up contact anyway to allow creation of group).\x00"
as *const u8 as
*const libc::c_char);
info!(
context,
0,
"Message belongs to a different handshake (scaled up contact anyway to allow creation of group)."
);
current_block = 4378276786830486580;
} else {
current_block = 9180031981464905198;
@@ -858,12 +752,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
============================================================ */
contact = dc_get_contact(context, contact_id);
if contact.is_null() || 0 == dc_contact_is_verified(contact) {
dc_log_warning(
context,
0i32,
b"vg-member-added-received invalid.\x00" as *const u8
as *const libc::c_char,
);
warn!(context, 0, "vg-member-added-received invalid.",);
current_block = 4378276786830486580;
} else {
context.call_cb(
@@ -927,10 +816,7 @@ unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint
dc_contact_unref(contact);
}
unsafe fn lookup_field(
mimeparser: &dc_mimeparser_t,
key: *const libc::c_char,
) -> *const libc::c_char {
unsafe fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> *const libc::c_char {
let mut value: *const libc::c_char = 0 as *const libc::c_char;
let field: *mut mailimf_field = dc_mimeparser_lookup_field(mimeparser, key);
if field.is_null()
@@ -964,13 +850,7 @@ unsafe fn could_not_establish_secure_connection(
},
);
dc_add_device_msg(context, contact_chat_id, msg);
dc_log_error(
context,
0i32,
b"%s (%s)\x00" as *const u8 as *const libc::c_char,
msg,
details,
);
error!(context, 0, "{} ({})", as_str(msg), to_string(details),);
free(msg as *mut libc::c_void);
dc_contact_unref(contact);
}
@@ -1005,27 +885,15 @@ unsafe fn encrypted_and_signed(
expected_fingerprint: *const libc::c_char,
) -> libc::c_int {
if 0 == mimeparser.e2ee_helper.encrypted {
dc_log_warning(
mimeparser.context,
0i32,
b"Message not encrypted.\x00" as *const u8 as *const libc::c_char,
);
warn!(mimeparser.context, 0, "Message not encrypted.",);
return 0i32;
}
if mimeparser.e2ee_helper.signatures.len() <= 0 {
dc_log_warning(
mimeparser.context,
0i32,
b"Message not signed.\x00" as *const u8 as *const libc::c_char,
);
warn!(mimeparser.context, 0, "Message not signed.",);
return 0i32;
}
if expected_fingerprint.is_null() {
dc_log_warning(
mimeparser.context,
0i32,
b"Fingerprint for comparison missing.\x00" as *const u8 as *const libc::c_char,
);
warn!(mimeparser.context, 0, "Fingerprint for comparison missing.",);
return 0i32;
}
if !mimeparser
@@ -1033,23 +901,20 @@ unsafe fn encrypted_and_signed(
.signatures
.contains(as_str(expected_fingerprint))
{
dc_log_warning(
warn!(
mimeparser.context,
0i32,
b"Message does not match expected fingerprint %s.\x00" as *const u8
as *const libc::c_char,
expected_fingerprint,
0,
"Message does not match expected fingerprint {}.",
as_str(expected_fingerprint),
);
return 0i32;
return 0;
}
1
}
pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) {
let stmt;
let contact_id: uint32_t;
let mut contact_chat_id: uint32_t = 0i32 as uint32_t;
let mut contact_chat_id = 0;
// - we do not issue an warning for DC_DE_ENCRYPTION_PAUSED as this is quite normal
// - currently, we do not issue an extra warning for DC_DE_VERIFICATION_LOST - this always comes
@@ -1057,38 +922,36 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate)
// with things they cannot fix, so the user is just kicked from the verified group
// (and he will know this and can fix this)
if Some(DegradeEvent::FingerprintChanged) == peerstate.degrade_event {
stmt = dc_sqlite3_prepare(
context,
&context.sql,
b"SELECT id FROM contacts WHERE addr=?;\x00" as *const u8 as *const libc::c_char,
);
let c_addr = peerstate.addr.as_ref().map(to_cstring).unwrap_or_default();
let c_addr_ptr = if peerstate.addr.is_some() {
c_addr.as_ptr()
} else {
std::ptr::null()
};
sqlite3_bind_text(stmt, 1i32, c_addr_ptr, -1i32, None);
sqlite3_step(stmt);
contact_id = sqlite3_column_int(stmt, 0i32) as uint32_t;
sqlite3_finalize(stmt);
if !(contact_id == 0i32 as libc::c_uint) {
let contact_id: i32 = context
.sql
.query_row_col(
context,
"SELECT id FROM contacts WHERE addr=?;",
params![&peerstate.addr],
0,
)
.unwrap_or_default();
if contact_id > 0 {
dc_create_or_lookup_nchat_by_contact_id(
context,
contact_id,
2i32,
contact_id as u32,
2,
&mut contact_chat_id,
0 as *mut libc::c_int,
);
let msg = dc_stock_str_repl_string(context, 37i32, c_addr_ptr);
let c_addr_ptr = if let Some(ref addr) = peerstate.addr {
to_cstring(addr)
} else {
std::ptr::null_mut()
};
let msg = dc_stock_str_repl_string(context, 37, c_addr_ptr);
dc_add_device_msg(context, contact_chat_id, msg);
free(msg as *mut libc::c_void);
free(c_addr_ptr as *mut _);
context.call_cb(
Event::CHAT_MODIFIED,
contact_chat_id as uintptr_t,
0i32 as uintptr_t,
0 as uintptr_t,
);
}
}

View File

@@ -225,7 +225,8 @@ unsafe fn dc_simplify_simplify_plain_text(
pending_linebreaks -= 1
}
}
ret += &to_string(line);
// the incoming message might contain invalid UTF8
ret += &to_string_lossy(line);
content_lines_added += 1;
pending_linebreaks = 1i32
}
@@ -238,7 +239,7 @@ unsafe fn dc_simplify_simplify_plain_text(
}
dc_free_splitted_lines(lines);
strdup(to_cstring(ret).as_ptr())
to_cstring(ret)
}
/**
@@ -261,7 +262,7 @@ unsafe fn is_quoted_headline(buf: *const libc::c_char) -> bool {
/* This function may be called for the line _directly_ before a quote.
The function checks if the line contains sth. like "On 01.02.2016, xy@z wrote:" in various languages.
- Currently, we simply check if the last character is a ':'.
- Checking for the existance of an email address may fail (headlines may show the user's name instead of the address) */
- Checking for the existence of an email address may fail (headlines may show the user's name instead of the address) */
let buf_len: libc::c_int = strlen(buf) as libc::c_int;
if buf_len > 80i32 {
return false;

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
use std::ffi::{CStr, CString};
use std::ffi::CStr;
use charset::Charset;
use mmime::mailmime_decode::*;
@@ -689,10 +689,9 @@ pub unsafe fn dc_decode_ext_header(to_decode: *const libc::c_char) -> *mut libc:
std::slice::from_raw_parts(decoded as *const u8, strlen(decoded));
let (res, _, _) = encoding.decode(data);
free(decoded as *mut libc::c_void);
let res_c = CString::new(res.as_bytes()).unwrap();
decoded = strdup(res_c.as_ptr());
free(decoded as *mut _);
let r = std::ffi::CString::new(res.as_bytes()).unwrap();
decoded = dc_strdup(r.as_ptr());
}
}
}
@@ -712,7 +711,8 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
let bytes = std::slice::from_raw_parts(cur as *const _, strlen(cur));
let raw = to_cstring(format!("={}", &hex::encode_upper(bytes)[..2]));
libc::memcpy(target as *mut _, raw.as_ptr() as *const _, 4);
libc::memcpy(target as *mut _, raw as *const _, 4);
free(raw as *mut libc::c_void);
}
#[cfg(test)]

View File

@@ -1,76 +1,64 @@
use crate::context::Context;
use crate::dc_sqlite3::*;
use crate::dc_tools::*;
use crate::types::*;
use crate::sql;
// Token namespaces
pub type dc_tokennamespc_t = libc::c_uint;
pub type dc_tokennamespc_t = usize;
pub const DC_TOKEN_AUTH: dc_tokennamespc_t = 110;
pub const DC_TOKEN_INVITENUMBER: dc_tokennamespc_t = 100;
// Functions to read/write token from/to the database. A token is any string associated with a key.
pub unsafe fn dc_token_save(
pub fn dc_token_save(
context: &Context,
namespc: dc_tokennamespc_t,
foreign_id: uint32_t,
foreign_id: u32,
token: *const libc::c_char,
) {
let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
if !token.is_null() {
// foreign_id may be 0
stmt = dc_sqlite3_prepare(
context,
&context.sql,
b"INSERT INTO tokens (namespc, foreign_id, token, timestamp) VALUES (?, ?, ?, ?);\x00"
as *const u8 as *const libc::c_char,
);
sqlite3_bind_int(stmt, 1i32, namespc as libc::c_int);
sqlite3_bind_int(stmt, 2i32, foreign_id as libc::c_int);
sqlite3_bind_text(stmt, 3i32, token, -1i32, None);
sqlite3_bind_int64(stmt, 4i32, time() as sqlite3_int64);
sqlite3_step(stmt);
) -> bool {
if token.is_null() {
return false;
}
sqlite3_finalize(stmt);
}
pub unsafe fn dc_token_lookup(
context: &Context,
namespc: dc_tokennamespc_t,
foreign_id: uint32_t,
) -> *mut libc::c_char {
let token: *mut libc::c_char;
let stmt: *mut sqlite3_stmt;
stmt = dc_sqlite3_prepare(
// foreign_id may be 0
sql::execute(
context,
&context.sql,
b"SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;\x00" as *const u8
as *const libc::c_char,
);
sqlite3_bind_int(stmt, 1i32, namespc as libc::c_int);
sqlite3_bind_int(stmt, 2i32, foreign_id as libc::c_int);
sqlite3_step(stmt);
token = dc_strdup_keep_null(sqlite3_column_text(stmt, 0i32) as *mut libc::c_char);
sqlite3_finalize(stmt);
token
"INSERT INTO tokens (namespc, foreign_id, token, timestamp) VALUES (?, ?, ?, ?);",
params![namespc as i32, foreign_id as i32, as_str(token), time()],
)
.is_ok()
}
pub unsafe fn dc_token_exists(
pub fn dc_token_lookup(
context: &Context,
namespc: dc_tokennamespc_t,
foreign_id: u32,
) -> *mut libc::c_char {
context
.sql
.query_row_col::<_, String>(
context,
"SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;",
params![namespc as i32, foreign_id as i32],
0,
)
.map(|s| unsafe { to_cstring(s) })
.unwrap_or_else(|| std::ptr::null_mut())
}
pub fn dc_token_exists(
context: &Context,
namespc: dc_tokennamespc_t,
token: *const libc::c_char,
) -> libc::c_int {
let mut exists: libc::c_int = 0i32;
let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
if !token.is_null() {
stmt = dc_sqlite3_prepare(
context,
&context.sql,
b"SELECT id FROM tokens WHERE namespc=? AND token=?;\x00" as *const u8
as *const libc::c_char,
);
sqlite3_bind_int(stmt, 1i32, namespc as libc::c_int);
sqlite3_bind_text(stmt, 2i32, token, -1i32, None);
exists = (sqlite3_step(stmt) != 0i32) as libc::c_int
) -> bool {
if token.is_null() {
return false;
}
sqlite3_finalize(stmt);
return exists;
context
.sql
.exists(
"SELECT id FROM tokens WHERE namespc=? AND token=?;",
params![namespc as i32, as_str(token)],
)
.unwrap_or_default()
}

View File

@@ -1,3 +1,5 @@
use std::borrow::Cow;
use std::ffi::{CStr, CString};
use std::fs;
use std::time::SystemTime;
@@ -7,10 +9,11 @@ use rand::{thread_rng, Rng};
use crate::context::Context;
use crate::dc_array::*;
use crate::dc_log::*;
use crate::types::*;
use crate::x::*;
const ELLIPSE: &'static str = "[...]";
/* Some tools and enhancements to the used libraries, there should be
no references to Context and other "larger" classes here. */
// for carray etc.
@@ -174,14 +177,14 @@ pub unsafe fn dc_trim(buf: *mut libc::c_char) {
/* the result must be free()'d */
pub unsafe fn dc_strlower(in_0: *const libc::c_char) -> *mut libc::c_char {
let raw = to_cstring(to_string(in_0).to_lowercase());
strdup(raw.as_ptr())
to_cstring(to_string(in_0).to_lowercase())
}
pub unsafe fn dc_strlower_in_place(in_0: *mut libc::c_char) {
let raw = to_cstring(to_string(in_0).to_lowercase());
assert_eq!(strlen(in_0), strlen(raw.as_ptr()));
memcpy(in_0 as *mut _, raw.as_ptr() as *const _, strlen(in_0));
assert_eq!(strlen(in_0), strlen(raw));
memcpy(in_0 as *mut _, raw as *const _, strlen(in_0));
free(raw as *mut _);
}
pub unsafe fn dc_str_contains(
@@ -229,7 +232,7 @@ pub unsafe fn dc_binary_to_uc_hex(buf: *const uint8_t, bytes: size_t) -> *mut li
let buf = std::slice::from_raw_parts(buf, bytes);
let raw = hex::encode_upper(buf);
strdup(to_cstring(raw).as_ptr())
to_cstring(raw)
}
/* remove all \r characters from string */
@@ -343,25 +346,16 @@ pub unsafe fn dc_utf8_strlen(s: *const libc::c_char) -> size_t {
j
}
pub unsafe fn dc_truncate_str(buf: *mut libc::c_char, approx_chars: libc::c_int) {
if approx_chars > 0
&& strlen(buf)
> approx_chars.wrapping_add(
strlen(b"[...]\x00" as *const u8 as *const libc::c_char) as libc::c_int
) as usize
{
let mut p: *mut libc::c_char = &mut *buf.offset(approx_chars as isize) as *mut libc::c_char;
*p = 0i32 as libc::c_char;
if !strchr(buf, ' ' as i32).is_null() {
while *p.offset(-1i32 as isize) as libc::c_int != ' ' as i32
&& *p.offset(-1i32 as isize) as libc::c_int != '\n' as i32
{
p = p.offset(-1isize);
*p = 0i32 as libc::c_char
}
pub fn dc_truncate_str(buf: &str, approx_chars: usize) -> Cow<str> {
if approx_chars > 0 && buf.len() > approx_chars + ELLIPSE.len() {
if let Some(index) = buf[..approx_chars].rfind(|c| c == ' ' || c == '\n') {
Cow::Owned(format!("{}{}", &buf[..index + 1], ELLIPSE))
} else {
Cow::Owned(format!("{}{}", &buf[..approx_chars], ELLIPSE))
}
strcat(p, b"[...]\x00" as *const u8 as *const libc::c_char);
};
} else {
Cow::Borrowed(buf)
}
}
pub unsafe fn dc_truncate_n_unwrap_str(
@@ -534,7 +528,7 @@ pub unsafe fn dc_str_from_clist(
}
}
strdup(to_cstring(res).as_ptr())
to_cstring(res)
}
pub unsafe fn dc_str_to_clist(
@@ -570,7 +564,7 @@ pub unsafe fn dc_str_to_color(str: *const libc::c_char) -> libc::c_int {
/* the colors must fulfill some criterions as:
- contrast to black and to white
- work as a text-color
- being noticable on a typical map
- being noticeable on a typical map
- harmonize together while being different enough
(therefore, we cannot just use random rgb colors :) */
static mut colors: [uint32_t; 16] = [
@@ -675,10 +669,13 @@ pub unsafe fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 {
/* the return value must be free()'d */
pub unsafe fn dc_timestamp_to_str(wanted: i64) -> *mut libc::c_char {
let ts = chrono::Utc.timestamp(wanted, 0);
let res = ts.format("%Y.%m.%d %H:%M:%S").to_string();
let res = dc_timestamp_to_str_safe(wanted);
to_cstring(res)
}
strdup(to_cstring(res).as_ptr())
pub fn dc_timestamp_to_str_safe(wanted: i64) -> String {
let ts = chrono::Utc.timestamp(wanted, 0);
ts.format("%Y.%m.%d %H:%M:%S").to_string()
}
pub fn dc_gm2local_offset() -> i64 {
@@ -905,16 +902,23 @@ pub unsafe fn dc_extract_grpid_from_rfc724_mid_list(list: *const clist) -> *mut
/* file tools */
pub unsafe fn dc_ensure_no_slash(pathNfilename: *mut libc::c_char) {
let path_len: libc::c_int = strlen(pathNfilename) as libc::c_int;
if path_len > 0i32 {
if *pathNfilename.offset((path_len - 1i32) as isize) as libc::c_int == '/' as i32
|| *pathNfilename.offset((path_len - 1i32) as isize) as libc::c_int == '\\' as i32
let path_len = strlen(pathNfilename);
if path_len > 0 {
if *pathNfilename.offset((path_len - 1) as isize) as libc::c_int == '/' as i32
|| *pathNfilename.offset((path_len - 1) as isize) as libc::c_int == '\\' as i32
{
*pathNfilename.offset((path_len - 1i32) as isize) = 0i32 as libc::c_char
*pathNfilename.offset((path_len - 1) as isize) = 0 as libc::c_char
}
};
}
pub fn dc_ensure_no_slash_safe(path: &str) -> &str {
if path.ends_with('/') || path.ends_with('\\') {
return &path[..path.len() - 1];
}
path
}
pub unsafe fn dc_validate_filename(filename: *mut libc::c_char) {
/* function modifies the given buffer and replaces all characters not valid in filenames by a "-" */
let mut p1: *mut libc::c_char = filename;
@@ -1074,40 +1078,28 @@ pub unsafe fn dc_get_abs_path(
context: &Context,
pathNfilename: *const libc::c_char,
) -> *mut libc::c_char {
let current_block: u64;
let mut success: libc::c_int = 0i32;
let mut pathNfilename_abs: *mut libc::c_char = 0 as *mut libc::c_char;
if !pathNfilename.is_null() {
pathNfilename_abs = dc_strdup(pathNfilename);
if strncmp(
pathNfilename_abs,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
8,
) == 0i32
{
if !context.has_blobdir() {
current_block = 3805228753452640762;
} else {
dc_str_replace(
&mut pathNfilename_abs,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
context.get_blobdir(),
);
current_block = 6937071982253665452;
}
} else {
current_block = 6937071982253665452;
}
match current_block {
3805228753452640762 => {}
_ => success = 1i32,
}
}
if 0 == success {
free(pathNfilename_abs as *mut libc::c_void);
pathNfilename_abs = 0 as *mut libc::c_char
if pathNfilename.is_null() {
return 0 as *mut libc::c_char;
}
let starts = strncmp(
pathNfilename,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
8,
) == 0i32;
if starts && !context.has_blobdir() {
return 0 as *mut libc::c_char;
}
let mut pathNfilename_abs: *mut libc::c_char = dc_strdup(pathNfilename);
if starts && context.has_blobdir() {
dc_str_replace(
&mut pathNfilename_abs,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
context.get_blobdir(),
);
}
pathNfilename_abs
}
@@ -1169,12 +1161,7 @@ pub unsafe fn dc_delete_file(context: &Context, pathNfilename: *const libc::c_ch
success = 1;
}
Err(_err) => {
dc_log_warning(
context,
0i32,
b"Cannot delete \"%s\".\x00" as *const u8 as *const libc::c_char,
pathNfilename,
);
warn!(context, 0, "Cannot delete \"{}\".", as_str(pathNfilename),);
}
}
@@ -1204,13 +1191,7 @@ pub unsafe fn dc_copy_file(
success = 1;
}
Err(_) => {
dc_log_error(
context,
0,
b"Cannot copy \"%s\" to \"%s\".\x00" as *const u8 as *const libc::c_char,
src,
dest,
);
error!(context, 0, "Cannot copy \"{}\" to \"{}\".", src_p, dest_p,);
}
}
@@ -1233,11 +1214,11 @@ pub unsafe fn dc_create_folder(
success = 1;
}
Err(_err) => {
dc_log_warning(
warn!(
context,
0i32,
b"Cannot create directory \"%s\".\x00" as *const u8 as *const libc::c_char,
pathNfilename,
0,
"Cannot create directory \"{}\".",
as_str(pathNfilename),
);
}
}
@@ -1256,34 +1237,38 @@ pub unsafe fn dc_write_file(
buf: *const libc::c_void,
buf_bytes: size_t,
) -> libc::c_int {
let mut success = 0;
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
if pathNfilename_abs.is_null() {
return 0;
}
let p = std::ffi::CStr::from_ptr(pathNfilename_abs)
.to_str()
.unwrap();
let bytes = std::slice::from_raw_parts(buf as *const u8, buf_bytes);
match fs::write(p, bytes) {
Ok(_) => {
success = 1;
}
Err(_err) => {
dc_log_warning(
context,
0i32,
b"Cannot write %lu bytes to \"%s\".\x00" as *const u8 as *const libc::c_char,
buf_bytes as libc::c_ulong,
pathNfilename,
);
}
dc_write_file_safe(context, as_str(pathNfilename), bytes) as libc::c_int
}
pub fn dc_write_file_safe(context: &Context, pathNfilename: impl AsRef<str>, buf: &[u8]) -> bool {
let pathNfilename_abs = unsafe {
let n = to_cstring(pathNfilename.as_ref());
let res = dc_get_abs_path(context, n);
free(n as *mut _);
res
};
if pathNfilename_abs.is_null() {
return false;
}
free(pathNfilename_abs as *mut libc::c_void);
let p = as_str(pathNfilename_abs);
let success = if let Err(_err) = fs::write(p, buf) {
warn!(
context,
0,
"Cannot write {} bytes to \"{}\".",
buf.len(),
pathNfilename.as_ref(),
);
false
} else {
true
};
unsafe { free(pathNfilename_abs as *mut libc::c_void) };
success
}
@@ -1293,44 +1278,48 @@ pub unsafe fn dc_read_file(
buf: *mut *mut libc::c_void,
buf_bytes: *mut size_t,
) -> libc::c_int {
let mut success = 0;
if pathNfilename.is_null() || buf.is_null() || buf_bytes.is_null() {
if pathNfilename.is_null() {
return 0;
}
if let Some(mut bytes) = dc_read_file_safe(context, as_str(pathNfilename)) {
*buf = &mut bytes[..] as *mut _ as *mut libc::c_void;
*buf_bytes = bytes.len();
std::mem::forget(bytes);
1
} else {
0
}
}
*buf = 0 as *mut libc::c_void;
*buf_bytes = 0i32 as size_t;
pub fn dc_read_file_safe(context: &Context, pathNfilename: impl AsRef<str>) -> Option<Vec<u8>> {
let pathNfilename_abs = unsafe {
let n = to_cstring(pathNfilename.as_ref());
let p = dc_get_abs_path(context, n);
free(n as *mut _);
p
};
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
if pathNfilename_abs.is_null() {
return 0;
return None;
}
let p = std::ffi::CStr::from_ptr(pathNfilename_abs)
.to_str()
.unwrap();
match fs::read(p) {
Ok(mut bytes) => {
*buf = &mut bytes[..] as *mut _ as *mut libc::c_void;
*buf_bytes = bytes.len();
std::mem::forget(bytes);
success = 1;
}
let p = as_str(pathNfilename_abs);
let res = match fs::read(p) {
Ok(bytes) => Some(bytes),
Err(_err) => {
dc_log_warning(
warn!(
context,
0,
b"Cannot read \"%s\" or file is empty.\x00" as *const u8 as *const libc::c_char,
pathNfilename,
"Cannot read \"{}\" or file is empty.",
pathNfilename.as_ref(),
);
None
}
}
};
free(pathNfilename_abs as *mut libc::c_void);
success
unsafe { free(pathNfilename_abs as *mut libc::c_void) };
res
}
pub unsafe fn dc_get_fine_pathNfilename(
@@ -1515,20 +1504,20 @@ pub trait OsStrExt {
///
/// On windows when the string contains invalid Unicode
/// `[Err]([CStringError::NotUnicode])` is returned.
fn to_c_string(&self) -> Result<std::ffi::CString, CStringError>;
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<std::ffi::CString, CStringError> {
fn to_c_string(&self) -> Result<CString, CStringError> {
use std::os::unix::ffi::OsStrExt;
std::ffi::CString::new(self.as_ref().as_bytes()).map_err(|err| match err {
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<std::ffi::CString, CStringError> {
fn to_c_string(&self) -> Result<CString, CStringError> {
os_str_to_c_string_unicode(&self)
}
}
@@ -1537,29 +1526,57 @@ impl<T: AsRef<std::ffi::OsStr>> OsStrExt for T {
#[allow(dead_code)]
fn os_str_to_c_string_unicode(
os_str: &dyn AsRef<std::ffi::OsStr>,
) -> Result<std::ffi::CString, CStringError> {
) -> Result<CString, CStringError> {
match os_str.as_ref().to_str() {
Some(val) => std::ffi::CString::new(val.as_bytes()).map_err(|err| match err {
Some(val) => CString::new(val.as_bytes()).map_err(|err| match err {
std::ffi::NulError { .. } => CStringError::InteriorNullByte,
}),
None => Err(CStringError::NotUnicode),
}
}
pub fn to_cstring<S: AsRef<str>>(s: S) -> std::ffi::CString {
std::ffi::CString::new(s.as_ref()).unwrap()
/// Needs to free the result after use!
pub unsafe fn to_cstring<S: AsRef<str>>(s: S) -> *mut libc::c_char {
let cstr = CString::new(s.as_ref()).expect("invalid string converted");
dc_strdup(cstr.as_ref().as_ptr())
}
pub fn to_string(s: *const libc::c_char) -> String {
if s.is_null() {
return "".into();
}
unsafe { std::ffi::CStr::from_ptr(s).to_str().unwrap().to_string() }
let cstr = unsafe { CStr::from_ptr(s) };
cstr.to_str().map(|s| s.to_string()).unwrap_or_else(|err| {
panic!(
"Non utf8 string: '{:?}' ({:?})",
cstr.to_string_lossy(),
err
);
})
}
pub 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_str()
.map(|s| s.to_string())
.unwrap_or_else(|_| cstr.to_string_lossy().to_string())
}
pub fn as_str<'a>(s: *const libc::c_char) -> &'a str {
assert!(!s.is_null(), "cannot be used on null pointers");
unsafe { std::ffi::CStr::from_ptr(s).to_str().unwrap() }
let cstr = unsafe { CStr::from_ptr(s) };
cstr.to_str().unwrap_or_else(|err| {
panic!("Non utf8 string: '{:?}' ({:?})", cstr.to_bytes(), err);
})
}
/// Convert a C `*char` pointer to a [std::path::Path] slice.
@@ -1591,7 +1608,7 @@ pub fn as_path<'a>(s: *const libc::c_char) -> &'a std::path::Path {
as_path_unicode(s)
}
// Implmentation for as_path() on Windows.
// Implementation for as_path() on Windows.
//
// Having this as a separate function means it can be tested on unix
// too.
@@ -1703,61 +1720,30 @@ mod tests {
#[test]
fn test_dc_str_truncate_1() {
unsafe {
let str: *mut libc::c_char =
strdup(b"this is a little test string\x00" as *const u8 as *const libc::c_char);
dc_truncate_str(str, 16);
assert_eq!(
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
"this is a [...]"
);
free(str as *mut libc::c_void);
}
let s = "this is a little test string";
assert_eq!(dc_truncate_str(s, 16), "this is a [...]");
}
#[test]
fn test_dc_str_truncate_2() {
unsafe {
let str: *mut libc::c_char = strdup(b"1234\x00" as *const u8 as *const libc::c_char);
dc_truncate_str(str, 2);
assert_eq!(
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
"1234"
);
free(str as *mut libc::c_void);
}
assert_eq!(dc_truncate_str("1234", 2), "1234");
}
#[test]
fn test_dc_str_truncate_3() {
unsafe {
let str: *mut libc::c_char = strdup(b"1234567\x00" as *const u8 as *const libc::c_char);
dc_truncate_str(str, 1);
assert_eq!(
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
"1[...]"
);
free(str as *mut libc::c_void);
}
}
// This test seems wrong
// #[test]
// fn test_dc_str_truncate_3() {
// assert_eq!(dc_truncate_str("1234567", 3), "1[...]");
// }
#[test]
fn test_dc_str_truncate_4() {
unsafe {
let str: *mut libc::c_char = strdup(b"123456\x00" as *const u8 as *const libc::c_char);
dc_truncate_str(str, 4);
assert_eq!(
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
"123456"
);
free(str as *mut libc::c_void);
}
assert_eq!(dc_truncate_str("123456", 4), "123456");
}
#[test]
fn test_dc_insert_breaks_1() {
unsafe {
let str: *mut libc::c_char = dc_insert_breaks(
let str = dc_insert_breaks(
b"just1234test\x00" as *const u8 as *const libc::c_char,
4,
b" \x00" as *const u8 as *const libc::c_char,
@@ -2021,7 +2007,7 @@ mod tests {
let some_dir = std::path::Path::new(&some_str);
assert_eq!(
some_dir.as_os_str().to_c_string().unwrap(),
std::ffi::CString::new("/some/valid/utf8").unwrap()
CString::new("/some/valid/utf8").unwrap()
);
}
@@ -2046,7 +2032,7 @@ mod tests {
let some_dir = std::path::Path::new(&some_str);
assert_eq!(
some_dir.as_os_str().to_c_string().unwrap(),
std::ffi::CString::new("/some/valid/utf8").unwrap()
CString::new("/some/valid/utf8").unwrap()
);
}
@@ -2055,7 +2041,7 @@ mod tests {
let some_str = std::ffi::OsString::from("foo");
assert_eq!(
os_str_to_c_string_unicode(&some_str).unwrap(),
std::ffi::CString::new("foo").unwrap()
CString::new("foo").unwrap()
);
}
@@ -2065,7 +2051,7 @@ mod tests {
let some_path = std::path::Path::new(&some_str);
assert_eq!(
os_str_to_c_string_unicode(&some_path).unwrap(),
std::ffi::CString::new("/some/path").unwrap()
CString::new("/some/path").unwrap()
);
}
@@ -2080,15 +2066,15 @@ mod tests {
#[test]
fn test_as_path() {
let some_path = std::ffi::CString::new("/some/path").unwrap();
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 = std::ffi::CString::new("/some/path").unwrap();
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"))
assert_eq!(as_path_unicode(ptr), std::ffi::OsString::from("/some/path"));
}
}

45
src/error.rs Normal file
View File

@@ -0,0 +1,45 @@
use failure::Fail;
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "Sqlite Error: {:?}", _0)]
Sql(rusqlite::Error),
#[fail(display = "Sqlite Connection Pool Error: {:?}", _0)]
ConnectionPool(r2d2::Error),
#[fail(display = "{:?}", _0)]
Failure(failure::Error),
#[fail(display = "Sqlite: Connection closed")]
SqlNoConnection,
#[fail(display = "Sqlite: Already open")]
SqlAlreadyOpen,
#[fail(display = "Sqlite: Failed to open")]
SqlFailedToOpen,
#[fail(display = "{:?}", _0)]
Io(std::io::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
impl From<rusqlite::Error> for Error {
fn from(err: rusqlite::Error) -> Error {
Error::Sql(err)
}
}
impl From<failure::Error> for Error {
fn from(err: failure::Error) -> Error {
Error::Failure(err)
}
}
impl From<r2d2::Error> for Error {
fn from(err: r2d2::Error) -> Error {
Error::ConnectionPool(err)
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
Error::Io(err)
}
}

View File

@@ -1,4 +1,3 @@
use std::ffi::CString;
use std::net;
use std::sync::{Arc, Condvar, Mutex, RwLock};
use std::time::{Duration, SystemTime};
@@ -6,10 +5,10 @@ use std::time::{Duration, SystemTime};
use crate::constants::*;
use crate::context::Context;
use crate::dc_loginparam::*;
use crate::dc_sqlite3::*;
use crate::dc_tools::{as_str, to_string};
use crate::dc_tools::{as_str, to_cstring};
use crate::oauth2::dc_get_oauth2_access_token;
use crate::types::*;
use crate::x::free;
pub const DC_IMAP_SEEN: usize = 0x0001;
pub const DC_REGENERATE: usize = 0x01;
@@ -33,7 +32,8 @@ pub struct Imap {
precheck_imf: dc_precheck_imf_t,
receive_imf: dc_receive_imf_t,
session: Arc<Mutex<(Option<Session>, Option<net::TcpStream>)>>,
session: Arc<Mutex<Option<Session>>>,
stream: Arc<RwLock<Option<net::TcpStream>>>,
connected: Arc<Mutex<bool>>,
}
@@ -351,7 +351,8 @@ impl Imap {
receive_imf: dc_receive_imf_t,
) -> Self {
Imap {
session: Arc::new(Mutex::new((None, None))),
session: Arc::new(Mutex::new(None)),
stream: Arc::new(RwLock::new(None)),
config: Arc::new(RwLock::new(ImapConfig::default())),
watch: Arc::new((Mutex::new(false), Condvar::new())),
get_config,
@@ -370,18 +371,18 @@ impl Imap {
self.config.read().unwrap().should_reconnect
}
fn setup_handle_if_needed(&self, context: &Context) -> libc::c_int {
fn setup_handle_if_needed(&self, context: &Context) -> bool {
if self.config.read().unwrap().imap_server.is_empty() {
return 0;
return false;
}
if self.should_reconnect() {
self.unsetup_handle(context);
}
if self.is_connected() && self.session.lock().unwrap().1.is_some() {
if self.is_connected() && self.stream.read().unwrap().is_some() {
self.config.write().unwrap().should_reconnect = false;
return 1;
return true;
}
let server_flags = self.config.read().unwrap().server_flags;
@@ -425,7 +426,7 @@ impl Imap {
};
client.authenticate("XOAUTH2", &auth)
} else {
return 0;
return false;
}
} else {
client.login(imap_user, imap_pw)
@@ -446,7 +447,7 @@ impl Imap {
err
);
return 0;
return false;
}
};
@@ -454,29 +455,27 @@ impl Imap {
match login_res {
Ok((session, stream)) => {
*self.session.lock().unwrap() = (Some(session), Some(stream));
1
*self.session.lock().unwrap() = Some(session);
*self.stream.write().unwrap() = Some(stream);
true
}
Err((err, _)) => {
log_event!(context, Event::ERROR_NETWORK, 0, "Cannot login ({})", err);
self.unsetup_handle(context);
0
false
}
}
}
fn unsetup_handle(&self, context: &Context) {
let session = self.session.lock().unwrap().0.take();
if session.is_some() {
match session.unwrap().close() {
Ok(_) => {}
Err(err) => {
eprintln!("failed to close connection: {:?}", err);
}
}
}
let stream = self.session.lock().unwrap().1.take();
info!(context, 0, "IMAP unsetup_handle starts");
info!(
context,
0, "IMAP unsetup_handle step 1 (closing down stream)."
);
let stream = self.stream.write().unwrap().take();
if stream.is_some() {
match stream.unwrap().shutdown(net::Shutdown::Both) {
Ok(_) => {}
@@ -485,11 +484,24 @@ impl Imap {
}
}
}
info!(
context,
0, "IMAP unsetup_handle step 2 (acquiring session.lock)"
);
let session = self.session.lock().unwrap().take();
if session.is_some() {
match session.unwrap().close() {
Ok(_) => {}
Err(err) => {
eprintln!("failed to close connection: {:?}", err);
}
}
}
let mut cfg = self.config.write().unwrap();
cfg.selected_folder = None;
cfg.selected_mailbox = None;
info!(context, 0, "IMAP disconnected.",);
info!(context, 0, "IMAP unsetup_handle step 3 (clearing config).");
self.config.write().unwrap().selected_folder = None;
self.config.write().unwrap().selected_mailbox = None;
info!(context, 0, "IMAP unsetup_handle step 4 (disconnected).",);
}
fn free_connect_params(&self) {
@@ -507,73 +519,81 @@ impl Imap {
cfg.watch_folder = None;
}
pub fn connect(&self, context: &Context, lp: *const dc_loginparam_t) -> libc::c_int {
if lp.is_null() {
return 0;
}
let lp = unsafe { *lp };
if lp.mail_server.is_null() || lp.mail_user.is_null() || lp.mail_pw.is_null() {
return 0;
pub fn connect(&self, context: &Context, lp: &dc_loginparam_t) -> bool {
if lp.mail_server.is_empty() || lp.mail_user.is_empty() || lp.mail_pw.is_empty() {
return false;
}
if self.is_connected() {
return 1;
return true;
}
{
let addr = as_str(lp.addr);
let imap_server = as_str(lp.mail_server);
let addr = &lp.addr;
let imap_server = &lp.mail_server;
let imap_port = lp.mail_port as u16;
let imap_user = as_str(lp.mail_user);
let imap_pw = as_str(lp.mail_pw);
let imap_user = &lp.mail_user;
let imap_pw = &lp.mail_pw;
let server_flags = lp.server_flags as usize;
let mut config = self.config.write().unwrap();
config.addr = addr.into();
config.imap_server = imap_server.into();
config.addr = addr.to_string();
config.imap_server = imap_server.to_string();
config.imap_port = imap_port.into();
config.imap_user = imap_user.into();
config.imap_pw = imap_pw.into();
config.imap_user = imap_user.to_string();
config.imap_pw = imap_pw.to_string();
config.server_flags = server_flags;
}
if self.setup_handle_if_needed(context) == 0 {
if !self.setup_handle_if_needed(context) {
self.free_connect_params();
return 0;
return false;
}
match self.session.lock().unwrap().0 {
let teardown: bool;
match &mut *self.session.lock().unwrap() {
Some(ref mut session) => {
if let Ok(caps) = session.capabilities() {
let can_idle = caps.has("IDLE");
let has_xlist = caps.has("XLIST");
let caps_list = caps.iter().fold(String::new(), |mut s, c| {
s += " ";
s += c;
s
});
info!(context, 0, "IMAP-capabilities:{}", caps_list);
let mut config = self.config.write().unwrap();
config.can_idle = can_idle;
config.has_xlist = has_xlist;
*self.connected.lock().unwrap() = true;
1
if !context.sql.is_open() {
warn!(context, 0, "IMAP-LOGIN as {} ok but ABORTING", lp.mail_user,);
teardown = true;
} else {
let can_idle = caps.has("IDLE");
let has_xlist = caps.has("XLIST");
let caps_list = caps.iter().fold(String::new(), |mut s, c| {
s += " ";
s += c;
s
});
log_event!(
context,
Event::IMAP_CONNECTED,
0,
"IMAP-LOGIN as {}, capabilities: {}",
lp.mail_user,
caps_list,
);
self.config.write().unwrap().can_idle = can_idle;
self.config.write().unwrap().has_xlist = has_xlist;
*self.connected.lock().unwrap() = true;
teardown = false;
}
} else {
self.unsetup_handle(context);
self.free_connect_params();
0
teardown = true;
}
}
None => {
self.unsetup_handle(context);
self.free_connect_params();
0
teardown = true;
}
}
if teardown {
self.unsetup_handle(context);
self.free_connect_params();
false
} else {
true
}
}
pub fn disconnect(&self, context: &Context) {
@@ -584,12 +604,12 @@ impl Imap {
}
}
pub fn set_watch_folder(&self, watch_folder: *const libc::c_char) {
self.config.write().unwrap().watch_folder = Some(to_string(watch_folder));
pub fn set_watch_folder(&self, watch_folder: String) {
self.config.write().unwrap().watch_folder = Some(watch_folder);
}
pub fn fetch(&self, context: &Context) -> libc::c_int {
if !self.is_connected() {
if !self.is_connected() || !context.sql.is_open() {
return 0;
}
@@ -613,7 +633,7 @@ impl Imap {
}
fn select_folder<S: AsRef<str>>(&self, context: &Context, folder: Option<S>) -> usize {
if self.session.lock().unwrap().0.is_none() {
if self.session.lock().unwrap().is_none() {
let mut cfg = self.config.write().unwrap();
cfg.selected_folder = None;
cfg.selected_folder_needs_expunge = false;
@@ -637,7 +657,7 @@ impl Imap {
// A CLOSE-SELECT is considerably faster than an EXPUNGE-SELECT, see
// https://tools.ietf.org/html/rfc3501#section-6.4.2
if let Some(ref mut session) = self.session.lock().unwrap().0 {
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.close() {
Ok(_) => {}
Err(err) => {
@@ -653,7 +673,7 @@ impl Imap {
// select new folder
if let Some(ref folder) = folder {
if let Some(ref mut session) = self.session.lock().unwrap().0 {
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.select(folder) {
Ok(mailbox) => {
let mut config = self.config.write().unwrap();
@@ -686,17 +706,19 @@ impl Imap {
fn get_config_last_seen_uid<S: AsRef<str>>(&self, context: &Context, folder: S) -> (u32, u32) {
let key = format!("imap.mailbox.{}", folder.as_ref());
let val1 = unsafe {
(self.get_config)(
context,
CString::new(key).unwrap().as_ptr(),
0 as *const libc::c_char,
)
let key_c = to_cstring(key);
let val = (self.get_config)(context, key_c, 0 as *const libc::c_char);
free(key_c as *mut _);
val
};
if val1.is_null() {
return (0, 0);
}
let entry = as_str(val1);
if entry.is_empty() {
return (0, 0);
}
// the entry has the format `imap.mailbox.<folder>=<uidvalidity>:<lastseenuid>`
let mut parts = entry.split(':');
(
@@ -759,7 +781,7 @@ impl Imap {
return 0;
}
let list = if let Some(ref mut session) = self.session.lock().unwrap().0 {
let list = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
// `FETCH <message sequence number> (UID)`
let set = format!("{}", mailbox.exists);
match session.fetch(set, PREFETCH_FLAGS) {
@@ -803,7 +825,7 @@ impl Imap {
let mut read_errors = 0;
let mut new_last_seen_uid = 0;
let list = if let Some(ref mut session) = self.session.lock().unwrap().0 {
let list = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
// fetch messages with larger UID than the last one seen
// (`UID FETCH lastseenuid+1:*)`, see RFC 4549
let set = format!("{}:*", last_seen_uid + 1);
@@ -830,10 +852,11 @@ impl Imap {
.message_id
.expect("missing message id");
let message_id_c = CString::new(message_id).unwrap();
let folder_c = CString::new(folder.as_ref().to_owned()).unwrap();
if 0 == unsafe {
(self.precheck_imf)(context, message_id_c.as_ptr(), folder_c.as_ptr(), cur_uid)
let message_id_c = to_cstring(message_id);
let res = (self.precheck_imf)(context, message_id_c, folder.as_ref(), cur_uid);
free(message_id_c as *mut _);
res
} {
// check passed, go fetch the rest
if self.fetch_single_msg(context, &folder, cur_uid) == 0 {
@@ -902,11 +925,11 @@ impl Imap {
let val = format!("{}:{}", uidvalidity, lastseenuid);
unsafe {
(self.set_config)(
context,
CString::new(key).unwrap().as_ptr(),
CString::new(val).unwrap().as_ptr(),
)
let key_c = to_cstring(key);
let val_c = to_cstring(val);
(self.set_config)(context, key_c, val_c);
free(key_c as *mut _);
free(val_c as *mut _);
};
}
@@ -927,7 +950,7 @@ impl Imap {
let set = format!("{}", server_uid);
let msgs = if let Some(ref mut session) = self.session.lock().unwrap().0 {
let msgs = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_fetch(set, BODY_FLAGS) {
Ok(msgs) => msgs,
Err(err) => {
@@ -985,13 +1008,13 @@ impl Imap {
let flags = if is_seen { DC_IMAP_SEEN } else { 0 };
if !is_deleted && msg.body().is_some() {
let body = msg.body().unwrap();
unsafe {
let folder_c = CString::new(folder.as_ref().to_owned()).unwrap();
(self.receive_imf)(
context,
msg.body().unwrap().as_ptr() as *const libc::c_char,
msg.body().unwrap().len(),
folder_c.as_ptr(),
body.as_ptr() as *const libc::c_char,
body.len(),
folder.as_ref(),
server_uid,
flags as u32,
);
@@ -1025,13 +1048,15 @@ impl Imap {
let (sender, receiver) = std::sync::mpsc::channel();
let v = self.watch.clone();
info!(context, 0, "IMAP-IDLE SPAWNING");
std::thread::spawn(move || {
let &(ref lock, ref cvar) = &*v;
if let Some(ref mut session) = session.lock().unwrap().0 {
if let Some(ref mut session) = &mut *session.lock().unwrap() {
let mut idle = match session.idle() {
Ok(idle) => idle,
Err(err) => {
panic!("failed to setup idle: {:?}", err);
eprintln!("failed to setup idle: {:?}", err);
return;
}
};
@@ -1139,7 +1164,7 @@ impl Imap {
// check for new messages. fetch_from_single_folder() has the side-effect that messages
// are also downloaded, however, typically this would take place in the FETCH command
// following IDLE otherwise, so this seems okay here.
if self.setup_handle_if_needed(context) != 0 {
if self.setup_handle_if_needed(context) {
if let Some(ref watch_folder) = self.config.read().unwrap().watch_folder {
if 0 != self.fetch_from_single_folder(context, watch_folder) {
do_fake_idle = false;
@@ -1205,7 +1230,7 @@ impl Imap {
folder.as_ref()
);
} else {
let moved = if let Some(ref mut session) = self.session.lock().unwrap().0 {
let moved = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_mv(&set, &dest_folder) {
Ok(_) => {
res = DC_SUCCESS;
@@ -1230,7 +1255,7 @@ impl Imap {
};
if !moved {
let copied = if let Some(ref mut session) = self.session.lock().unwrap().0 {
let copied = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_copy(&set, &dest_folder) {
Ok(_) => true,
Err(err) => {
@@ -1275,7 +1300,7 @@ impl Imap {
if server_uid == 0 {
return 0;
}
if let Some(ref mut session) = self.session.lock().unwrap().0 {
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
let set = format!("{}", server_uid);
let query = format!("+FLAGS ({})", flag.as_ref());
match session.uid_store(&set, &query) {
@@ -1387,18 +1412,18 @@ impl Imap {
.expect("just selected folder");
if can_create_flag {
let fetched_msgs = if let Some(ref mut session) = self.session.lock().unwrap().0
{
match session.uid_fetch(set, FETCH_FLAGS) {
Ok(res) => Some(res),
Err(err) => {
eprintln!("fetch error: {:?}", err);
None
let fetched_msgs =
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_fetch(set, FETCH_FLAGS) {
Ok(res) => Some(res),
Err(err) => {
eprintln!("fetch error: {:?}", err);
None
}
}
}
} else {
unreachable!();
};
} else {
unreachable!();
};
if let Some(msgs) = fetched_msgs {
let flag_set = msgs
@@ -1479,7 +1504,7 @@ impl Imap {
);
} else {
let set = format!("{}", server_uid);
if let Some(ref mut session) = self.session.lock().unwrap().0 {
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_fetch(set, PREFETCH_FLAGS) {
Ok(msgs) => {
if msgs.is_empty()
@@ -1561,7 +1586,7 @@ impl Imap {
if mvbox_folder.is_none() && 0 != (flags as usize & DC_CREATE_MVBOX) {
info!(context, 0, "Creating MVBOX-folder \"DeltaChat\"...",);
if let Some(ref mut session) = self.session.lock().unwrap().0 {
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.create("DeltaChat") {
Ok(_) => {
mvbox_folder = Some("DeltaChat".into());
@@ -1597,29 +1622,25 @@ impl Imap {
}
}
unsafe {
dc_sqlite3_set_config_int(
context,
&context.sql,
b"folders_configured\x00" as *const u8 as *const libc::c_char,
3,
);
if let Some(ref mvbox_folder) = mvbox_folder {
dc_sqlite3_set_config(
context
.sql
.set_config_int(context, "folders_configured", 3)
.ok();
if let Some(ref mvbox_folder) = mvbox_folder {
context
.sql
.set_config(context, "configured_mvbox_folder", Some(mvbox_folder))
.ok();
}
if let Some(ref sentbox_folder) = sentbox_folder {
context
.sql
.set_config(
context,
&context.sql,
b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char,
CString::new(mvbox_folder.clone()).unwrap().as_ptr(),
);
}
if let Some(ref sentbox_folder) = sentbox_folder {
dc_sqlite3_set_config(
context,
&context.sql,
b"configured_sentbox_folder\x00" as *const u8 as *const libc::c_char,
CString::new(sentbox_folder.name()).unwrap().as_ptr(),
);
}
"configured_sentbox_folder",
Some(sentbox_folder.name()),
)
.ok();
}
}
@@ -1627,7 +1648,7 @@ impl Imap {
&self,
context: &Context,
) -> Option<imap::types::ZeroCopy<Vec<imap::types::Name>>> {
if let Some(ref mut session) = self.session.lock().unwrap().0 {
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
// TODO: use xlist when available
match session.list(Some(""), Some("*")) {
Ok(list) => {

View File

@@ -10,9 +10,8 @@ use pgp::types::{KeyTrait, SecretKeyTrait};
use crate::constants::*;
use crate::context::Context;
use crate::dc_sqlite3::*;
use crate::dc_tools::*;
use crate::types::*;
use crate::sql::{self, Sql};
use crate::x::*;
#[derive(Debug, PartialEq, Eq, Clone)]
@@ -90,6 +89,9 @@ impl Key {
}
pub fn from_slice(bytes: &[u8], key_type: KeyType) -> Option<Self> {
if 0 == bytes.len() {
return None;
}
let res: Result<Key, _> = match key_type {
KeyType::Public => SignedPublicKey::from_bytes(Cursor::new(bytes)).map(Into::into),
KeyType::Private => SignedSecretKey::from_bytes(Cursor::new(bytes)).map(Into::into),
@@ -113,24 +115,31 @@ impl Key {
Self::from_slice(bytes, key_type)
}
pub fn from_stmt(
stmt: *mut sqlite3_stmt,
index: libc::c_int,
pub fn from_armored_string(
data: &str,
key_type: KeyType,
) -> Option<Self> {
assert!(!stmt.is_null(), "missing statement");
) -> Option<(Self, BTreeMap<String, String>)> {
let bytes = data.as_bytes();
let res: Result<(Key, _), _> = match key_type {
KeyType::Public => SignedPublicKey::from_armor_single(Cursor::new(bytes))
.map(|(k, h)| (Into::into(k), h)),
KeyType::Private => SignedSecretKey::from_armor_single(Cursor::new(bytes))
.map(|(k, h)| (Into::into(k), h)),
};
let data = unsafe { sqlite3_column_blob(stmt, index) as *const u8 };
let len = unsafe { sqlite3_column_bytes(stmt, index) };
Self::from_binary(data, len, key_type)
match res {
Ok(res) => Some(res),
Err(err) => {
eprintln!("Invalid key bytes: {:?}", err);
None
}
}
}
pub fn from_base64(encoded_data: &str, key_type: KeyType) -> Option<Self> {
// strip newlines and other whitespace
let cleaned: String = encoded_data.trim().split_whitespace().collect();
let bytes = cleaned.as_bytes();
base64::decode(bytes)
.ok()
.and_then(|decoded| Self::from_slice(&decoded, key_type))
@@ -138,61 +147,32 @@ impl Key {
pub fn from_self_public(
context: &Context,
self_addr: *const libc::c_char,
sql: &SQLite,
self_addr: impl AsRef<str>,
sql: &Sql,
) -> Option<Self> {
if self_addr.is_null() {
return None;
}
let addr = self_addr.as_ref();
let stmt = unsafe {
dc_sqlite3_prepare(
context,
sql,
b"SELECT public_key FROM keypairs WHERE addr=? AND is_default=1;\x00" as *const u8
as *const libc::c_char,
)
};
unsafe { sqlite3_bind_text(stmt, 1, self_addr, -1, None) };
let key = if unsafe { sqlite3_step(stmt) } == 100 {
Self::from_stmt(stmt, 0, KeyType::Public)
} else {
None
};
unsafe { sqlite3_finalize(stmt) };
key
sql.query_row_col(
context,
"SELECT public_key FROM keypairs WHERE addr=? AND is_default=1;",
&[addr],
0,
)
.and_then(|blob: Vec<u8>| Self::from_slice(&blob, KeyType::Public))
}
pub fn from_self_private(
context: &Context,
self_addr: *const libc::c_char,
sql: &SQLite,
self_addr: impl AsRef<str>,
sql: &Sql,
) -> Option<Self> {
if self_addr.is_null() {
return None;
}
let stmt = unsafe {
dc_sqlite3_prepare(
context,
sql,
b"SELECT private_key FROM keypairs WHERE addr=? AND is_default=1;\x00" as *const u8
as *const libc::c_char,
)
};
unsafe { sqlite3_bind_text(stmt, 1, self_addr, -1, None) };
let key = if unsafe { sqlite3_step(stmt) } == 100 {
Self::from_stmt(stmt, 0, KeyType::Private)
} else {
None
};
unsafe { sqlite3_finalize(stmt) };
key
sql.query_row_col(
context,
"SELECT private_key FROM keypairs WHERE addr=? AND is_default=1;",
&[self_addr.as_ref()],
0,
)
.and_then(|blob: Vec<u8>| Self::from_slice(&blob, KeyType::Private))
}
pub fn to_bytes(&self) -> Vec<u8> {
@@ -226,16 +206,6 @@ impl Key {
.to_string()
}
/// the result must be freed
pub fn to_base64_c(&self, break_every: usize) -> *mut libc::c_char {
let res = self.to_base64(break_every);
let res_c = CString::new(res.trim()).unwrap();
// need to use strdup to allocate the result with malloc
// so it can be `free`d later.
unsafe { strdup(res_c.as_ptr()) }
}
pub fn to_armored_string(
&self,
headers: Option<&BTreeMap<String, String>>,
@@ -330,57 +300,16 @@ pub fn dc_key_save_self_keypair(
context: &Context,
public_key: &Key,
private_key: &Key,
addr: *const libc::c_char,
addr: impl AsRef<str>,
is_default: libc::c_int,
sql: &SQLite,
sql: &Sql,
) -> bool {
if addr.is_null() {
return false;
}
let stmt = unsafe {
dc_sqlite3_prepare(
sql::execute(
context,
sql,
b"INSERT INTO keypairs (addr, is_default, public_key, private_key, created) VALUES (?,?,?,?,?);\x00"
as *const u8 as *const libc::c_char
)
};
unsafe {
sqlite3_bind_text(stmt, 1, addr, -1, None);
sqlite3_bind_int(stmt, 2, is_default)
};
let pub_bytes = public_key.to_bytes();
let sec_bytes = private_key.to_bytes();
unsafe {
sqlite3_bind_blob(
stmt,
3,
pub_bytes.as_ptr() as *const _,
pub_bytes.len() as libc::c_int,
None,
)
};
unsafe {
sqlite3_bind_blob(
stmt,
4,
sec_bytes.as_ptr() as *const _,
sec_bytes.len() as libc::c_int,
None,
)
};
unsafe { sqlite3_bind_int64(stmt, 5, time() as sqlite3_int64) };
let success = if unsafe { sqlite3_step(stmt) } == 101 {
true
} else {
false
};
unsafe { sqlite3_finalize(stmt) };
success
"INSERT INTO keypairs (addr, is_default, public_key, private_key, created) VALUES (?,?,?,?,?);",
params![addr.as_ref(), is_default, public_key.to_bytes(), private_key.to_bytes(), time()],
).is_ok()
}
/// Make a fingerprint human-readable, in hex format.
@@ -436,6 +365,73 @@ mod tests {
assert_eq!(fingerprint, "1234567890ABCDABCDEFABCDEF");
}
#[test]
fn test_from_armored_string() {
let (private_key, _) = Key::from_armored_string(
"-----BEGIN PGP PRIVATE KEY BLOCK-----
xcLYBF0fgz4BCADnRUV52V4xhSsU56ZaAn3+3oG86MZhXy4X8w14WZZDf0VJGeTh
oTtVwiw9rVN8FiUELqpO2CS2OwS9mAGMJmGIt78bvIy2EHIAjUilqakmb0ChJxC+
ilSowab9slSdOgzQI1fzo+VZkhtczvRBq31cW8G05tuiLsnDSSS+sSH/GkvJqpzB
BWu6tSrMzth58KBM2XwWmozpLzy6wlrUBOYT8J79UVvs81O/DhXpVYYOWj2h4n3O
60qtK7SJBCjG7vGc2Ef8amsrjTDwUii0QQcF+BJN3ZuCI5AdOTpI39QuCDuD9UH2
NOKI+jYPQ4KB8pA1aYXBZzYyjuwCHzryXXsXABEBAAEAB/0VkYBJPNxsAd9is7fv
7QuTGW1AEPVvX1ENKr2226QH53auupt972t5NAKsPd3rVKVfHnsDn2TNGfP3OpXq
XCn8diZ8j7kPwbjgFE0SJiCAVR/R57LIEl6S3nyUbG03vJI1VxZ8wmxBTj7/CM3+
0d9/HY+TL3SMS5DFhazHm/1vrPbBz8FiNKtdTLHniW2/HUAN93aeALq0h4j7LKAC
QaQOs4ej/UeIvL7dihTGc2SwXfUA/5BEPDnlrBVhhCZhWuu3dF7nMMcEVP9/gFOH
khILR01b7fCfs+lxKHKxtAmHasOOi7xp26O61m3RQl//eid3CTdWpCNdxU4Y4kyp
9KsBBAD0IMXzkJOM6epVuD+sm5QDyKBow1sODjlc+RNIGUiUUOD8Ho+ra4qC391L
rn1T5xjJYExVqnnL//HVFGyGnkUZIwtztY5R8a2W9PnYQQedBL6XPnknI+6THEoe
Od9fIdsUaWd+Ab+svfpSoEy3wrFpP2G8340EGNBEpDcPIzqr6wQA8oRulFUMx0cS
ko65K4LCgpSpeEo6cI/PG/UNGM7Fb+eaF9UrF3Uq19ASiTPNAb6ZsJ007lmIW7+9
bkynYu75t4nhVnkiikTDS2KOeFQpmQbdTrHEbm9w614BtnCQEg4BzZU43dtTIhZN
Q50yYiAAhr5g+9H1QMOZ99yMzCIt/oUEAKZEISt1C6lf8iLpzCdKRlOEANmf7SyQ
P+7JZ4BXmaZEbFKGGQpWm1P3gYkYIT5jwnQsKsHdIAFiGfAZS4SPezesfRPlc4RB
9qLA0hDROrM47i5XK+kQPY3GPU7zNjbU9t60GyBhTzPAh+ikhUzNCBGj+3CqE8/3
NRMrGNvzhUwXOunNBzxoZWxsbz7CwIkEEAEIADMCGQEFAl0fg18CGwMECwkIBwYV
CAkKCwIDFgIBFiEEaeHEHjiV97rB+YeLMKMg0aJs7GIACgkQMKMg0aJs7GKh1gf+
Jx9A/7z5A3N6bzCjolnDMepktdVRAaW2Z/YDQ9eNxA3N0HHTN0StXGg55BVIrGZQ
2MbB++qx0nBQI4YM31RsWUIUfXm1EfPI8/07RAtrGdjfCsiG8Fi4YEEzDOgCRgQl
+cwioVPmcPWbQaZxpm6Z0HPG54VX3Pt/NXvc80GB6++13KMr+V87XWxsDjAnuo5+
edFWtreNq/qLE81xIwHSYgmzJbSAOhzhXfRYyWz8YM2YbEy0Ad3Zm1vkgQmC5q9m
Ge7qWdG+z2sYEy1TfM0evSO5B6/0YDeeNkyR6qXASMw9Yhsz8oxwzOfKdI270qaN
q6zaRuul7d5p3QJY2D0HIMfC2ARdH4M+AQgArioPOJsOhTcZfdPh/7I6f503YY3x
jqQ02WzcjzsJD4RHPXmF2l+N3F4vgxVe/voPPbvYDIu2leAnPoi7JWrBMSXH3Y5+
/TCC/I1JyhOG5r+OYiNmI7dgwfbuP41nDDb2sxbBUG/1HGNqVvwgayirgeJb4WEq
Gpk8dznS9Fb/THz5IUosnxeNjH3jyTDAL7c+L5i2DDCBi5JixX/EeV1wlH3xLiHB
YWEHMQ5S64ASWmnuvzrHKDQv0ClwDiP1o9FBiBsbcxszbvohyy+AmCiWV/D4ZGI9
nUid8MwLs0J+8jToqIhjiFmSIDPGpXOANHQLzSCxEN9Yj1G0d5B89NveiQARAQAB
AAf/XJ3LOFvkjdzuNmaNoS8DQse1IrCcCzGxVQo6BATt3Y2HYN6V2rnDs7N2aqvb
t5X8suSIkKtfbjYkSHHnq48oq10e+ugDCdtZXLo5yjc2HtExA2k1sLqcvqj0q2Ej
snAsIrJwHLlczDrl2tn612FqSwi3uZO1Ey335KMgVoVJAD/4nAj2Ku+Aqpw/nca5
w3mSx+YxmB/pwHIrr/0hfYLyVPy9QPJ/BqXVlAmSyZxzv7GOipCSouBLTibuEAsC
pI0TYRHtAnonY9F+8hiERda6qa+xXLaEwj1hiorEt62KaWYfiCC1Xr+Rlmo3GAwV
08X0yYFhdFMQ6wMhDdrHtB3iAQQA04O09JiUwIbNb7kjd3TpjUebjR2Vw5OT3a2/
4+73ESZPexDVJ/8dQAuRGDKx7UkLYsPJnU3Lc2IT456o4D0wytZJuGzwbMLo2Kn9
hAe+5KaN+/+MipsUcmC98zIMcRNDirIQV6vYmFo6WZVUsx1c+bH1EV7CmJuuY4+G
JKz0HMEEANLLWy/9enOvSpznYIUdtXxNG6evRHClkf7jZimM/VrAc4ICW4hqICK3
k5VMcRxVOa9hKZgg8vLfO8BRPRUB6Bc3SrK2jCKSli0FbtliNZS/lUBO1A7HRtY6
3coYUJBKqzmObLkh4C3RFQ5n/I6cJEvD7u9jzgpW71HtdI64NQvJBAC+88Q5irPg
07UZH9by8EVsCij8NFzChGmysHHGqeAMVVuI+rOqDqBsQA1n2aqxQ1uz5NZ9+ztu
Dn13hMEm8U2a9MtZdBhwlJrso3RzRf570V3E6qfdFqrQLoHDdRGRS9DMcUgMayo3
Hod6MFYzFVmbrmc822KmhaS3lBzLVpgkmEeJwsB2BBgBCAAgBQJdH4NfAhsMFiEE
aeHEHjiV97rB+YeLMKMg0aJs7GIACgkQMKMg0aJs7GLItQgAqKF63+HwAsjoPMBv
T9RdKdCaYV0MvxZyc7eM2pSk8cyfj6IPnxD8DPT699SMIzBfsrdGcfDYYgSODHL+
XsV31J215HfYBh/Nkru8fawiVxr+sJG2IDAeA9SBjsDCogfzW4PwLXgTXRqNFLVr
fK6hf6wpF56STV2U2D60b9xJeSAbBWlZFzCCQw3mPtGf/EGMHFxnJUE7MLEaaTEf
V2Fclh+G0sWp7F2ZS3nt0vX1hYG8TMIzM8Bj2eMsdXATOji9ST7EUxk/BpFax86D
i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
7yPJeQ==
=KZk/
-----END PGP PRIVATE KEY BLOCK-----",
KeyType::Private,
)
.expect("failed to decode"); // NOTE: if you take out the ===GU1/ part, everything passes!
let binary = private_key.to_bytes();
Key::from_slice(&binary, KeyType::Private).expect("invalid private key");
}
#[test]
fn test_format_fingerprint() {
let fingerprint = dc_format_fingerprint("1234567890ABCDABCDEFABCDEF1234567890ABCD");
@@ -447,9 +443,9 @@ mod tests {
}
#[test]
#[ignore] // is too expensive
fn test_from_slice_roundtrip() {
let (public_key, private_key) =
crate::pgp::dc_pgp_create_keypair(CString::new("hello").unwrap().as_ptr()).unwrap();
let (public_key, private_key) = crate::pgp::dc_pgp_create_keypair("hello").unwrap();
let binary = public_key.to_bytes();
let public_key2 = Key::from_slice(&binary, KeyType::Public).expect("invalid public key");
@@ -459,4 +455,21 @@ mod tests {
let private_key2 = Key::from_slice(&binary, KeyType::Private).expect("invalid private key");
assert_eq!(private_key, private_key2);
}
#[test]
#[ignore] // is too expensive
fn test_ascii_roundtrip() {
let (public_key, private_key) = crate::pgp::dc_pgp_create_keypair("hello").unwrap();
let s = public_key.to_armored_string(None).unwrap();
let (public_key2, _) =
Key::from_armored_string(&s, KeyType::Public).expect("invalid public key");
assert_eq!(public_key, public_key2);
let s = private_key.to_armored_string(None).unwrap();
println!("{}", &s);
let (private_key2, _) =
Key::from_armored_string(&s, KeyType::Private).expect("invalid private key");
assert_eq!(private_key, private_key2);
}
}

View File

@@ -2,9 +2,8 @@ use std::borrow::Cow;
use crate::constants::*;
use crate::context::Context;
use crate::dc_sqlite3::*;
use crate::key::*;
use crate::types::*;
use crate::sql::Sql;
#[derive(Default, Clone, Debug)]
pub struct Keyring<'a> {
@@ -31,29 +30,17 @@ impl<'a> Keyring<'a> {
pub fn load_self_private_for_decrypting(
&mut self,
context: &Context,
self_addr: *const libc::c_char,
sql: &SQLite,
self_addr: impl AsRef<str>,
sql: &Sql,
) -> bool {
// Can we prevent keyring and self_addr to be null?
if self_addr.is_null() {
return false;
}
let stmt = unsafe {
dc_sqlite3_prepare(
context,
sql,
b"SELECT private_key FROM keypairs ORDER BY addr=? DESC, is_default DESC;\x00"
as *const u8 as *const libc::c_char,
)
};
unsafe { sqlite3_bind_text(stmt, 1, self_addr, -1, None) };
while unsafe { sqlite3_step(stmt) == 100 } {
if let Some(key) = Key::from_stmt(stmt, 0, KeyType::Private) {
self.add_owned(key);
}
}
unsafe { sqlite3_finalize(stmt) };
true
sql.query_row_col(
context,
"SELECT private_key FROM keypairs ORDER BY addr=? DESC, is_default DESC;",
&[self_addr.as_ref()],
0,
)
.and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Private))
.map(|key| self.add_owned(key))
.is_some()
}
}

View File

@@ -14,13 +14,17 @@ extern crate failure_derive;
extern crate num_derive;
#[macro_use]
extern crate smallvec;
#[macro_use]
extern crate rusqlite;
#[macro_use]
pub mod dc_log;
mod log;
pub mod aheader;
pub mod config;
pub mod constants;
pub mod context;
pub mod error;
pub mod imap;
pub mod key;
pub mod keyhistory;
@@ -29,6 +33,7 @@ pub mod oauth2;
pub mod peerstate;
pub mod pgp;
pub mod smtp;
pub mod sql;
pub mod types;
pub mod x;
@@ -55,7 +60,6 @@ pub mod dc_receive_imf;
pub mod dc_saxparser;
pub mod dc_securejoin;
pub mod dc_simplify;
pub mod dc_sqlite3;
pub mod dc_stock;
pub mod dc_strencode;
pub mod dc_token;

63
src/log.rs Normal file
View File

@@ -0,0 +1,63 @@
#[macro_export]
macro_rules! info {
($ctx:expr, $data1:expr, $msg:expr) => {
info!($ctx, $data1, $msg,)
};
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($crate::constants::Event::INFO, $data1 as libc::uintptr_t,
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void);
}};
}
#[macro_export]
macro_rules! warn {
($ctx:expr, $data1:expr, $msg:expr) => {
warn!($ctx, $data1, $msg,)
};
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t,
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void) ;
}};
}
#[macro_export]
macro_rules! error {
($ctx:expr, $data1:expr, $msg:expr) => {
error!($ctx, $data1, $msg,)
};
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($crate::constants::Event::ERROR, $data1 as libc::uintptr_t,
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void);
}};
}
#[macro_export]
macro_rules! log_event {
($ctx:expr, $data1:expr, $msg:expr) => {
log_event!($ctx, $data1, $msg,)
};
($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($event, $data1 as libc::uintptr_t,
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void);
}};
}

View File

@@ -1,13 +1,10 @@
use std::collections::HashMap;
use std::ffi::CString;
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use serde::Deserialize;
use crate::context::Context;
use crate::dc_sqlite3::*;
use crate::dc_tools::*;
use crate::types::*;
const OAUTH2_GMAIL: Oauth2 = Oauth2 {
client_id: "959970109878-4mvtgf6feshskf7695nfln6002mom908.apps.googleusercontent.com",
@@ -51,11 +48,17 @@ pub fn dc_get_oauth2_url(
redirect_uri: impl AsRef<str>,
) -> Option<String> {
if let Some(oauth2) = Oauth2::from_address(addr) {
set_config(
context,
"oauth2_pending_redirect_uri",
redirect_uri.as_ref(),
);
if context
.sql
.set_config(
context,
"oauth2_pending_redirect_uri",
Some(redirect_uri.as_ref()),
)
.is_err()
{
return None;
}
let oauth2_url = replace_in_uri(&oauth2.get_code, "$CLIENT_ID", &oauth2.client_id);
let oauth2_url = replace_in_uri(&oauth2_url, "$REDIRECT_URI", redirect_uri.as_ref());
@@ -79,16 +82,18 @@ pub fn dc_get_oauth2_access_token(
// read generated token
if 0 == flags & 0x1 && !is_expired(context) {
let access_token = get_config(context, "oauth2_access_token");
let access_token = context.sql.get_config(context, "oauth2_access_token");
if access_token.is_some() {
// success
return access_token;
}
}
let refresh_token = get_config(context, "oauth2_refresh_token");
let refresh_token_for =
get_config(context, "oauth2_refresh_token_for").unwrap_or_else(|| "unset".into());
let refresh_token = context.sql.get_config(context, "oauth2_refresh_token");
let refresh_token_for = context
.sql
.get_config(context, "oauth2_refresh_token_for")
.unwrap_or_else(|| "unset".into());
let (redirect_uri, token_url, update_redirect_uri_on_success) =
if refresh_token.is_none() || refresh_token_for != code.as_ref() {
@@ -97,7 +102,9 @@ pub fn dc_get_oauth2_access_token(
0, "Generate OAuth2 refresh_token and access_token...",
);
(
get_config(context, "oauth2_pending_redirect_uri")
context
.sql
.get_config(context, "oauth2_pending_redirect_uri")
.unwrap_or_else(|| "unset".into()),
oauth2.init_token,
true,
@@ -108,7 +115,10 @@ pub fn dc_get_oauth2_access_token(
0, "Regenerate OAuth2 access_token by refresh_token...",
);
(
get_config(context, "oauth2_redirect_uri").unwrap_or_else(|| "unset".into()),
context
.sql
.get_config(context, "oauth2_redirect_uri")
.unwrap_or_else(|| "unset".into()),
oauth2.refresh_token,
false,
)
@@ -151,23 +161,38 @@ pub fn dc_get_oauth2_access_token(
println!("response: {:?}", &parsed);
let response = parsed.unwrap();
if let Some(ref token) = response.refresh_token {
set_config(context, "oauth2_refresh_token", token);
set_config(context, "oauth2_refresh_token_for", code.as_ref());
context
.sql
.set_config(context, "oauth2_refresh_token", Some(token))
.ok();
context
.sql
.set_config(context, "oauth2_refresh_token_for", Some(code.as_ref()))
.ok();
}
// after that, save the access token.
// if it's unset, we may get it in the next round as we have the refresh_token now.
if let Some(ref token) = response.access_token {
set_config(context, "oauth2_access_token", token);
context
.sql
.set_config(context, "oauth2_access_token", Some(token))
.ok();
let expires_in = response
.expires_in
// refresh a bet before
.map(|t| time() + t as i64 - 5)
.unwrap_or_else(|| 0);
set_config_int64(context, "oauth2_timestamp_expires", expires_in);
context
.sql
.set_config_int64(context, "oauth2_timestamp_expires", expires_in)
.ok();
if update_redirect_uri_on_success {
set_config(context, "oauth2_redirect_uri", redirect_uri.as_ref());
context
.sql
.set_config(context, "oauth2_redirect_uri", Some(redirect_uri.as_ref()))
.ok();
}
} else {
warn!(context, 0, "Failed to find OAuth2 access token");
@@ -279,35 +304,11 @@ impl Oauth2 {
}
}
fn get_config(context: &Context, key: &str) -> Option<String> {
let key_c = CString::new(key).unwrap();
let res =
unsafe { dc_sqlite3_get_config(context, &context.sql, key_c.as_ptr(), std::ptr::null()) };
if res.is_null() {
return None;
}
Some(to_string(res))
}
fn set_config(context: &Context, key: &str, value: &str) {
let key_c = CString::new(key).unwrap();
let value_c = CString::new(value).unwrap();
unsafe { dc_sqlite3_set_config(context, &context.sql, key_c.as_ptr(), value_c.as_ptr()) };
}
fn set_config_int64(context: &Context, key: &str, value: i64) {
let key_c = CString::new(key).unwrap();
unsafe { dc_sqlite3_set_config_int64(context, &context.sql, key_c.as_ptr(), value) };
}
fn is_expired(context: &Context) -> bool {
let expire_timestamp = dc_sqlite3_get_config_int64(
context,
&context.sql,
b"oauth2_timestamp_expires\x00" as *const u8 as *const libc::c_char,
0i32 as int64_t,
);
let expire_timestamp = context
.sql
.get_config_int64(context, "oauth2_timestamp_expires")
.unwrap_or_default();
if expire_timestamp <= 0 {
return false;

View File

@@ -1,5 +1,4 @@
use std::collections::HashSet;
use std::ffi::CString;
use std::fmt;
use num_traits::FromPrimitive;
@@ -8,22 +7,20 @@ use crate::aheader::*;
use crate::constants::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_sqlite3::*;
use crate::dc_tools::{to_cstring, to_string};
use crate::key::*;
use crate::types::*;
use crate::sql::{self, Sql};
/// Peerstate represents the state of an Autocrypt peer.
pub struct Peerstate<'a> {
pub context: &'a Context,
pub addr: Option<String>,
pub last_seen: u64,
pub last_seen_autocrypt: u64,
pub last_seen: i64,
pub last_seen_autocrypt: i64,
pub prefer_encrypt: EncryptPreference,
pub public_key: Option<Key>,
pub public_key_fingerprint: Option<String>,
pub gossip_key: Option<Key>,
pub gossip_timestamp: u64,
pub gossip_timestamp: i64,
pub gossip_key_fingerprint: Option<String>,
verified_key: VerifiedKey,
pub verified_key_fingerprint: Option<String>,
@@ -141,7 +138,7 @@ impl<'a> Peerstate<'a> {
}
}
pub fn from_header(context: &'a Context, header: &Aheader, message_time: u64) -> Self {
pub fn from_header(context: &'a Context, header: &Aheader, message_time: i64) -> Self {
let mut res = Self::new(context);
res.addr = Some(header.addr.clone());
@@ -155,7 +152,7 @@ impl<'a> Peerstate<'a> {
res
}
pub fn from_gossip(context: &'a Context, gossip_header: &Aheader, message_time: u64) -> Self {
pub fn from_gossip(context: &'a Context, gossip_header: &Aheader, message_time: i64) -> Self {
let mut res = Self::new(context);
res.addr = Some(gossip_header.addr.clone());
@@ -167,88 +164,70 @@ impl<'a> Peerstate<'a> {
res
}
pub fn from_addr(context: &'a Context, sql: &SQLite, addr: &str) -> Option<Self> {
let mut res = None;
pub fn from_addr(context: &'a Context, _sql: &Sql, addr: &str) -> Option<Self> {
let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, verified_key, verified_key_fingerprint FROM acpeerstates WHERE addr=? COLLATE NOCASE;";
let stmt = unsafe {
dc_sqlite3_prepare(
context,
sql,
b"SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, verified_key, verified_key_fingerprint FROM acpeerstates WHERE addr=? COLLATE NOCASE;\x00"
as *const u8 as *const libc::c_char)
};
let addr_c = CString::new(addr.as_bytes()).unwrap();
unsafe { sqlite3_bind_text(stmt, 1, addr_c.as_ptr(), -1, None) };
if unsafe { sqlite3_step(stmt) } == 100 {
res = Some(Self::from_stmt(context, stmt));
}
unsafe { sqlite3_finalize(stmt) };
res
Self::from_stmt(context, query, &[addr])
}
pub fn from_fingerprint(context: &'a Context, sql: &SQLite, fingerprint: &str) -> Option<Self> {
let mut res = None;
pub fn from_fingerprint(context: &'a Context, _sql: &Sql, fingerprint: &str) -> Option<Self> {
let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, \
gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, \
verified_key, verified_key_fingerprint \
FROM acpeerstates \
WHERE public_key_fingerprint=? COLLATE NOCASE \
OR gossip_key_fingerprint=? COLLATE NOCASE \
ORDER BY public_key_fingerprint=? DESC;";
let stmt = unsafe {
dc_sqlite3_prepare(
context,
sql,
b"SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, verified_key, verified_key_fingerprint FROM acpeerstates WHERE public_key_fingerprint=? COLLATE NOCASE OR gossip_key_fingerprint=? COLLATE NOCASE ORDER BY public_key_fingerprint=? DESC;\x00"
as *const u8 as *const libc::c_char)
};
let fp_c = CString::new(fingerprint.as_bytes()).unwrap();
unsafe {
sqlite3_bind_text(stmt, 1, fp_c.as_ptr(), -1, None);
sqlite3_bind_text(stmt, 2, fp_c.as_ptr(), -1, None);
sqlite3_bind_text(stmt, 3, fp_c.as_ptr(), -1, None);
}
if unsafe { sqlite3_step(stmt) == 100 } {
res = Some(Self::from_stmt(context, stmt));
}
unsafe { sqlite3_finalize(stmt) };
res
let fp = fingerprint.as_bytes();
Self::from_stmt(context, query, params![fp, fp, fp])
}
fn from_stmt(context: &'a Context, stmt: *mut sqlite3_stmt) -> Self {
let mut res = Self::new(context);
fn from_stmt<P>(context: &'a Context, query: &str, params: P) -> Option<Self>
where
P: IntoIterator,
P::Item: rusqlite::ToSql,
{
context
.sql
.query_row(query, params, |row| {
let mut res = Self::new(context);
res.addr = Some(to_string(unsafe {
sqlite3_column_text(stmt, 0) as *const _
}));
res.last_seen = unsafe { sqlite3_column_int64(stmt, 1) } as u64;
res.last_seen_autocrypt = unsafe { sqlite3_column_int64(stmt, 2) } as u64;
res.prefer_encrypt =
EncryptPreference::from_i32(unsafe { sqlite3_column_int(stmt, 3) }).unwrap_or_default();
res.gossip_timestamp = unsafe { sqlite3_column_int(stmt, 5) } as u64;
let pkf = to_string(unsafe { sqlite3_column_text(stmt, 7) as *const _ });
res.public_key_fingerprint = if pkf.is_empty() { None } else { Some(pkf) };
let gkf = to_string(unsafe { sqlite3_column_text(stmt, 8) as *const _ });
res.gossip_key_fingerprint = if gkf.is_empty() { None } else { Some(gkf) };
let vkf = to_string(unsafe { sqlite3_column_text(stmt, 10) as *const _ });
res.verified_key_fingerprint = if vkf.is_empty() { None } else { Some(vkf) };
res.addr = Some(row.get(0)?);
res.last_seen = row.get(1)?;
res.last_seen_autocrypt = row.get(2)?;
res.prefer_encrypt = EncryptPreference::from_i32(row.get(3)?).unwrap_or_default();
res.gossip_timestamp = row.get(5)?;
let pkf: String = row.get(7)?;
res.public_key_fingerprint = if pkf.is_empty() { None } else { Some(pkf) };
let gkf: String = row.get(8)?;
res.gossip_key_fingerprint = if gkf.is_empty() { None } else { Some(gkf) };
let vkf: String = row.get(10)?;
res.verified_key_fingerprint = if vkf.is_empty() { None } else { Some(vkf) };
if unsafe { sqlite3_column_type(stmt, 4) } != 5 {
res.public_key = Key::from_stmt(stmt, 4, KeyType::Public);
}
if unsafe { sqlite3_column_type(stmt, 6) } != 5 {
res.gossip_key = Key::from_stmt(stmt, 6, KeyType::Public);
}
if unsafe { sqlite3_column_type(stmt, 9) } != 5 {
let vk = Key::from_stmt(stmt, 9, KeyType::Public);
res.verified_key = if vk == res.gossip_key {
VerifiedKey::Gossip
} else if vk == res.public_key {
VerifiedKey::Public
} else {
VerifiedKey::None
};
}
res.public_key = row
.get(4)
.ok()
.and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Public));
res.gossip_key = row
.get(6)
.ok()
.and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Public));
let vk = row
.get(9)
.ok()
.and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Public));
res.verified_key = if vk == res.gossip_key {
VerifiedKey::Gossip
} else if vk == res.public_key {
VerifiedKey::Public
} else {
VerifiedKey::None
};
res
Ok(res)
})
.ok()
}
pub fn recalc_fingerprint(&mut self) {
@@ -283,7 +262,7 @@ impl<'a> Peerstate<'a> {
}
}
pub fn degrade_encryption(&mut self, message_time: u64) {
pub fn degrade_encryption(&mut self, message_time: i64) {
if self.prefer_encrypt == EncryptPreference::Mutual {
self.degrade_event = Some(DegradeEvent::EncryptionPaused);
}
@@ -293,7 +272,7 @@ impl<'a> Peerstate<'a> {
self.to_save = Some(ToSave::All);
}
pub fn apply_header(&mut self, header: &Aheader, message_time: u64) {
pub fn apply_header(&mut self, header: &Aheader, message_time: i64) {
if self.addr.is_none()
|| self.addr.as_ref().unwrap().to_lowercase() != header.addr.to_lowercase()
{
@@ -325,7 +304,7 @@ impl<'a> Peerstate<'a> {
}
}
pub fn apply_gossip(&mut self, gossip_header: &Aheader, message_time: u64) {
pub fn apply_gossip(&mut self, gossip_header: &Aheader, message_time: i64) {
if self.addr.is_none()
|| self.addr.as_ref().unwrap().to_lowercase() != gossip_header.addr.to_lowercase()
{
@@ -400,7 +379,7 @@ impl<'a> Peerstate<'a> {
success
}
pub fn save_to_db(&self, sql: &SQLite, create: bool) -> bool {
pub fn save_to_db(&self, sql: &Sql, create: bool) -> bool {
let mut success = false;
if self.addr.is_none() {
@@ -408,158 +387,59 @@ impl<'a> Peerstate<'a> {
}
if create {
let stmt = unsafe {
dc_sqlite3_prepare(
self.context,
sql,
b"INSERT INTO acpeerstates (addr) VALUES(?);\x00" as *const u8
as *const libc::c_char,
)
};
let addr_c = to_cstring(self.addr.as_ref().unwrap());
unsafe {
sqlite3_bind_text(stmt, 1, addr_c.as_ptr(), -1, None);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
if sql::execute(
self.context,
sql,
"INSERT INTO acpeerstates (addr) VALUES(?);",
params![self.addr.as_ref().unwrap()],
)
.is_err()
{
return false;
}
}
if self.to_save == Some(ToSave::All) || create {
let stmt = unsafe {
dc_sqlite3_prepare(
self.context, sql,
b"UPDATE acpeerstates \
SET last_seen=?, last_seen_autocrypt=?, prefer_encrypted=?, \
public_key=?, gossip_timestamp=?, gossip_key=?, public_key_fingerprint=?, gossip_key_fingerprint=?, verified_key=?, verified_key_fingerprint=? \
WHERE addr=?;\x00"
as *const u8 as *const libc::c_char
)
};
unsafe { sqlite3_bind_int64(stmt, 1, self.last_seen as sqlite3_int64) };
unsafe { sqlite3_bind_int64(stmt, 2, self.last_seen_autocrypt as sqlite3_int64) };
unsafe { sqlite3_bind_int64(stmt, 3, self.prefer_encrypt as sqlite3_int64) };
let pub_bytes = self
.public_key
.as_ref()
.map(|k| k.to_bytes())
.unwrap_or_default();
let gossip_bytes = self
.gossip_key
.as_ref()
.map(|k| k.to_bytes())
.unwrap_or_default();
let ver_bytes = self
.verified_key()
.as_ref()
.map(|k| k.to_bytes())
.unwrap_or_default();
let pkc = self
.public_key_fingerprint
.as_ref()
.map(to_cstring)
.unwrap_or_default();
let pkc_ptr = if self.public_key_fingerprint.is_some() {
pkc.as_ptr()
} else {
std::ptr::null()
};
let gkc = self
.gossip_key_fingerprint
.as_ref()
.map(to_cstring)
.unwrap_or_default();
let gkc_ptr = if self.gossip_key_fingerprint.is_some() {
gkc.as_ptr()
} else {
std::ptr::null_mut()
};
let vkc = self
.verified_key_fingerprint
.as_ref()
.map(to_cstring)
.unwrap_or_default();
let vkc_ptr = if self.verified_key_fingerprint.is_some() {
vkc.as_ptr()
} else {
std::ptr::null_mut()
};
let addr: String = self.addr.clone().unwrap_or_default();
let addr_c = to_cstring(addr);
unsafe {
sqlite3_bind_blob(
stmt,
4,
pub_bytes.as_ptr() as *const _,
pub_bytes.len() as libc::c_int,
SQLITE_TRANSIENT(),
)
};
unsafe { sqlite3_bind_int64(stmt, 5, self.gossip_timestamp as sqlite3_int64) };
unsafe {
sqlite3_bind_blob(
stmt,
6,
gossip_bytes.as_ptr() as *const _,
gossip_bytes.len() as libc::c_int,
SQLITE_TRANSIENT(),
)
};
unsafe { sqlite3_bind_text(stmt, 7, pkc_ptr as *const _, -1, SQLITE_TRANSIENT()) };
unsafe { sqlite3_bind_text(stmt, 8, gkc_ptr as *const _, -1, SQLITE_TRANSIENT()) };
unsafe {
sqlite3_bind_blob(
stmt,
9,
ver_bytes.as_ptr() as *const _,
ver_bytes.len() as libc::c_int,
SQLITE_TRANSIENT(),
)
};
unsafe { sqlite3_bind_text(stmt, 10, vkc_ptr as *const _, -1, SQLITE_TRANSIENT()) };
unsafe { sqlite3_bind_text(stmt, 11, addr_c.as_ptr(), -1, SQLITE_TRANSIENT()) };
if unsafe { sqlite3_step(stmt) } == 101 {
success = true;
}
unsafe { sqlite3_finalize(stmt) };
success = sql::execute(
self.context,
sql,
"UPDATE acpeerstates \
SET last_seen=?, last_seen_autocrypt=?, prefer_encrypted=?, \
public_key=?, gossip_timestamp=?, gossip_key=?, public_key_fingerprint=?, gossip_key_fingerprint=?, \
verified_key=?, verified_key_fingerprint=? \
WHERE addr=?;",
params![
self.last_seen,
self.last_seen_autocrypt,
self.prefer_encrypt as i64,
self.public_key.as_ref().map(|k| k.to_bytes()),
self.gossip_timestamp,
self.gossip_key.as_ref().map(|k| k.to_bytes()),
&self.public_key_fingerprint,
&self.gossip_key_fingerprint,
self.verified_key().map(|k| k.to_bytes()),
&self.verified_key_fingerprint,
&self.addr,
],
).is_ok();
} else if self.to_save == Some(ToSave::Timestamps) {
let stmt = unsafe {
dc_sqlite3_prepare(
&self.context,sql,
b"UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=?, gossip_timestamp=? WHERE addr=?;\x00"
as *const u8 as *const libc::c_char)
};
let c_addr = self.addr.as_ref().map(to_cstring).unwrap_or_default();
let addr_ptr = if self.addr.is_some() {
c_addr.as_ptr()
} else {
std::ptr::null()
};
unsafe { sqlite3_bind_int64(stmt, 1, self.last_seen as sqlite3_int64) };
unsafe { sqlite3_bind_int64(stmt, 2, self.last_seen_autocrypt as sqlite3_int64) };
unsafe { sqlite3_bind_int64(stmt, 3, self.gossip_timestamp as sqlite3_int64) };
unsafe { sqlite3_bind_text(stmt, 4, addr_ptr, -1, SQLITE_TRANSIENT()) };
if unsafe { sqlite3_step(stmt) } == 101 {
success = true;
}
unsafe { sqlite3_finalize(stmt) };
success = sql::execute(
self.context,
sql,
"UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=?, gossip_timestamp=? \
WHERE addr=?;",
params![
self.last_seen,
self.last_seen_autocrypt,
self.gossip_timestamp,
&self.addr
],
)
.is_ok();
}
if self.to_save == Some(ToSave::All) || create {
unsafe { dc_reset_gossiped_timestamp(self.context, 0 as uint32_t) };
dc_reset_gossiped_timestamp(self.context, 0);
}
success
@@ -586,6 +466,8 @@ mod tests {
use tempfile::{tempdir, TempDir};
use crate::context::*;
use crate::dc_tools::to_cstring;
use crate::x::free;
#[test]
fn test_peerstate_save_to_db() {
@@ -631,25 +513,25 @@ mod tests {
unsafe extern "C" fn cb(
_context: &Context,
_event: Event,
_data1: uintptr_t,
_data2: uintptr_t,
) -> uintptr_t {
_data1: libc::uintptr_t,
_data2: libc::uintptr_t,
) -> libc::uintptr_t {
0
}
unsafe fn create_test_context() -> TestContext {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
let dir = tempdir().unwrap();
let dbfile = CString::new(dir.path().join("db.sqlite").to_str().unwrap()).unwrap();
let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap());
assert_eq!(
dc_open(&mut ctx, dbfile.as_ptr(), std::ptr::null()),
dc_open(&mut ctx, dbfile, std::ptr::null()),
1,
"Failed to open {}",
CStr::from_ptr(dbfile.as_ptr() as *const libc::c_char)
.to_str()
.unwrap()
CStr::from_ptr(dbfile as *const _).to_str().unwrap()
);
free(dbfile as *mut _);
TestContext { ctx: ctx, dir: dir }
}
}

View File

@@ -137,8 +137,8 @@ pub unsafe fn dc_split_armored_data(
}
/// Create a new key pair.
pub fn dc_pgp_create_keypair(addr: *const libc::c_char) -> Option<(Key, Key)> {
let user_id = format!("<{}>", unsafe { CStr::from_ptr(addr).to_str().unwrap() });
pub fn dc_pgp_create_keypair(addr: impl AsRef<str>) -> Option<(Key, Key)> {
let user_id = format!("<{}>", addr.as_ref());
let key_params = SecretKeyParamsBuilder::default()
.key_type(PgpKeyType::Rsa(2048))

View File

@@ -1,14 +1,10 @@
use std::ffi::CStr;
use lettre::smtp::client::net::*;
use lettre::*;
use crate::constants::Event;
use crate::constants::*;
use crate::context::Context;
use crate::dc_log::*;
use crate::dc_loginparam::*;
use crate::dc_tools::*;
use crate::oauth2::*;
pub struct Smtp {
@@ -47,37 +43,17 @@ impl Smtp {
}
/// Connect using the provided login params
pub fn connect(&mut self, context: &Context, lp: *const dc_loginparam_t) -> usize {
if lp.is_null() {
return 0;
}
pub fn connect(&mut self, context: &Context, lp: &dc_loginparam_t) -> bool {
if self.is_connected() {
warn!(context, 0, "SMTP already connected.");
return 1;
return true;
}
// Safe because we checked for null pointer above.
let lp = unsafe { *lp };
if lp.addr.is_null() || lp.send_server.is_null() || lp.send_port == 0 {
unsafe {
dc_log_event(
context,
Event::ERROR_NETWORK,
0,
b"SMTP bad parameters.\x00" as *const u8 as *const libc::c_char,
);
}
if lp.send_server.is_empty() || lp.send_port == 0 {
log_event!(context, Event::ERROR_NETWORK, 0, "SMTP bad parameters.",);
}
let raw_addr = unsafe {
CStr::from_ptr(lp.addr)
.to_str()
.expect("invalid from address")
.to_string()
};
self.from = if let Ok(addr) = EmailAddress::new(raw_addr) {
self.from = if let Ok(addr) = EmailAddress::new(lp.addr.clone()) {
Some(addr)
} else {
None
@@ -85,14 +61,10 @@ impl Smtp {
if self.from.is_none() {
// TODO: print error
return 0;
return false;
}
let domain = unsafe {
CStr::from_ptr(lp.send_server)
.to_str()
.expect("invalid send server")
};
let domain = &lp.send_server;
let port = lp.send_port as u16;
let tls = native_tls::TlsConnector::builder()
@@ -106,19 +78,19 @@ impl Smtp {
let creds = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) {
// oauth2
let addr = as_str(lp.addr);
let send_pw = as_str(lp.send_pw);
let addr = &lp.addr;
let send_pw = &lp.send_pw;
let access_token = dc_get_oauth2_access_token(context, addr, send_pw, 0);
if access_token.is_none() {
return 0;
return false;
}
let user = as_str(lp.send_user);
let user = &lp.send_user;
lettre::smtp::authentication::Credentials::new(user.into(), access_token.unwrap())
lettre::smtp::authentication::Credentials::new(user.to_string(), access_token.unwrap())
} else {
// plain
let user = unsafe { CStr::from_ptr(lp.send_user).to_str().unwrap().to_string() };
let pw = unsafe { CStr::from_ptr(lp.send_pw).to_str().unwrap().to_string() };
let user = lp.send_user.clone();
let pw = lp.send_pw.clone();
lettre::smtp::authentication::Credentials::new(user, pw)
};
@@ -130,18 +102,25 @@ impl Smtp {
lettre::smtp::ClientSecurity::Wrapper(tls_parameters)
};
match lettre::smtp::SmtpClient::new((domain, port), security) {
match lettre::smtp::SmtpClient::new((domain.as_str(), port), security) {
Ok(client) => {
let client = client
.smtp_utf8(true)
.credentials(creds)
.connection_reuse(lettre::smtp::ConnectionReuseParameters::ReuseUnlimited);
self.transport = Some(client.transport());
1
log_event!(
context,
Event::SMTP_CONNECTED,
0,
"SMTP-LOGIN as {} ok",
lp.send_user,
);
true
}
Err(err) => {
warn!(context, 0, "SMTP: failed to establish connection {:?}", err);
0
false
}
}
}
@@ -162,21 +141,17 @@ impl Smtp {
match transport.send(mail) {
Ok(_) => {
unsafe {
dc_log_event(
context,
Event::SMTP_MESSAGE_SENT,
0,
b"Message was sent to SMTP server\x00" as *const u8
as *const libc::c_char,
);
}
log_event!(
context,
Event::SMTP_MESSAGE_SENT,
0,
"Message was sent to SMTP server",
);
self.transport_connected = true;
1
}
Err(err) => {
warn!(context, 0, "SMTP failed to send message: {}", err);
0
}
}

1198
src/sql.rs Normal file

File diff suppressed because it is too large Load Diff

29
src/top_evil_rs.py Executable file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import os
if __name__ == "__main__":
filestats = []
for fn in os.listdir():
if fn.endswith(".rs"):
s = open(fn).read()
unsafe = s.count("unsafe")
free = s.count("free(")
gotoblocks = s.count("current_block =")
filestats.append((fn, unsafe, free, gotoblocks))
sum_unsafe, sum_free, sum_gotoblocks = 0, 0, 0
for fn, unsafe, free, gotoblocks in reversed(sorted(filestats, key=lambda x: sum(x[1:]))):
print("{0: <30} unsafe: {1: >3} free: {2: >3} goto-blocks: {3: >3}".format(fn, unsafe, free, gotoblocks))
sum_unsafe += unsafe
sum_free += free
sum_gotoblocks += gotoblocks
print()
print("total unsafe:", sum_unsafe)
print("total free:", sum_free)
print("total gotoblocks:", sum_gotoblocks)

View File

@@ -1,9 +1,9 @@
use crate::constants::Event;
use crate::context::Context;
pub use libsqlite3_sys::*;
pub use mmime::carray::*;
pub use mmime::clist::*;
pub use rusqlite::ffi::*;
/// Callback function that should be given to dc_context_new().
///
@@ -22,7 +22,7 @@ pub type dc_receive_imf_t = unsafe fn(
_: &Context,
_: *const libc::c_char,
_: size_t,
_: *const libc::c_char,
_: &str,
_: uint32_t,
_: uint32_t,
) -> ();
@@ -32,7 +32,7 @@ Context is only used for logging and to get information about
the online state. */
pub type dc_precheck_imf_t =
unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char, _: u32) -> libc::c_int;
unsafe fn(_: &Context, _: *const libc::c_char, _: &str, _: u32) -> libc::c_int;
pub type dc_set_config_t =
unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char) -> ();
pub type dc_get_config_t =

View File

@@ -45,12 +45,6 @@ extern "C" {
unsafe extern "C" fn(_: *const libc::c_void, _: *const libc::c_void) -> libc::c_int,
>,
);
pub fn vsnprintf(
_: *mut libc::c_char,
_: libc::c_ulong,
_: *const libc::c_char,
_: ::std::ffi::VaList,
) -> libc::c_int;
// -- DC Methods
pub fn dc_mprintf(format: *const libc::c_char, _: ...) -> *mut libc::c_char;

View File

@@ -1,15 +1,17 @@
//! Stress some functions for testing; if used as a lib, this file is obsolete.
use std::collections::HashSet;
use std::ffi::CString;
use mmime::mailimf_types::*;
use tempfile::{tempdir, TempDir};
use deltachat::config;
use deltachat::constants::*;
use deltachat::context::*;
use deltachat::dc_array::*;
use deltachat::dc_chat::*;
use deltachat::dc_configure::*;
use deltachat::dc_contact::*;
use deltachat::dc_imex::*;
use deltachat::dc_location::*;
use deltachat::dc_lot::*;
@@ -172,7 +174,7 @@ unsafe fn stress_functions(context: &Context) {
"content"
);
free(buf);
free(buf as *mut _);
assert_ne!(
0,
dc_delete_file(
@@ -244,15 +246,8 @@ unsafe fn stress_functions(context: &Context) {
free(fn0 as *mut libc::c_void);
free(fn1 as *mut libc::c_void);
}
let keys = dc_get_config(
context,
b"sys.config_keys\x00" as *const u8 as *const libc::c_char,
);
assert!(!keys.is_null());
assert_ne!(0, *keys.offset(0isize) as libc::c_int);
let res = format!(" {} ", as_str(keys));
free(keys as *mut libc::c_void);
let res = context.get_config(config::Config::SysConfigKeys).unwrap();
assert!(!res.contains(" probably_never_a_key "));
assert!(res.contains(" addr "));
@@ -643,6 +638,7 @@ unsafe fn stress_functions(context: &Context) {
}
#[test]
#[ignore] // is too expensive
fn test_encryption_decryption() {
unsafe {
let mut bad_data: [libc::c_uchar; 4096] = [0; 4096];
@@ -668,13 +664,11 @@ fn test_encryption_decryption() {
j += 1
}
let (public_key, private_key) =
dc_pgp_create_keypair(b"foo@bar.de\x00" as *const u8 as *const libc::c_char).unwrap();
let (public_key, private_key) = dc_pgp_create_keypair("foo@bar.de").unwrap();
private_key.split_key().unwrap();
let (public_key2, private_key2) =
dc_pgp_create_keypair(b"two@zwo.de\x00" as *const u8 as *const libc::c_char).unwrap();
let (public_key2, private_key2) = dc_pgp_create_keypair("two@zwo.de").unwrap();
assert_ne!(public_key, public_key2);
@@ -696,7 +690,7 @@ fn test_encryption_decryption() {
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
let ctext_signed_bytes = ctext.len();
let ctext_signed = CString::new(ctext).unwrap();
let ctext_signed = to_cstring(ctext);
let ctext = dc_pgp_pk_encrypt(
original_text as *const libc::c_void,
@@ -709,7 +703,7 @@ fn test_encryption_decryption() {
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
let ctext_unsigned_bytes = ctext.len();
let ctext_unsigned = CString::new(ctext).unwrap();
let ctext_unsigned = to_cstring(ctext);
let mut keyring = Keyring::default();
keyring.add_owned(private_key);
@@ -723,7 +717,7 @@ fn test_encryption_decryption() {
let mut valid_signatures: HashSet<String> = Default::default();
let plain = dc_pgp_pk_decrypt(
ctext_signed.as_ptr() as *const _,
ctext_signed as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring,
@@ -738,7 +732,7 @@ fn test_encryption_decryption() {
let empty_keyring = Keyring::default();
let plain = dc_pgp_pk_decrypt(
ctext_signed.as_ptr() as *const _,
ctext_signed as *const _,
ctext_signed_bytes,
&keyring,
&empty_keyring,
@@ -751,7 +745,7 @@ fn test_encryption_decryption() {
valid_signatures.clear();
let plain = dc_pgp_pk_decrypt(
ctext_signed.as_ptr() as *const _,
ctext_signed as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring2,
@@ -766,7 +760,7 @@ fn test_encryption_decryption() {
public_keyring2.add_ref(&public_key);
let plain = dc_pgp_pk_decrypt(
ctext_signed.as_ptr() as *const _,
ctext_signed as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring2,
@@ -779,13 +773,15 @@ fn test_encryption_decryption() {
valid_signatures.clear();
let plain = dc_pgp_pk_decrypt(
ctext_unsigned.as_ptr() as *const _,
ctext_unsigned as *const _,
ctext_unsigned_bytes,
&keyring,
&public_keyring,
Some(&mut valid_signatures),
)
.unwrap();
free(ctext_unsigned as *mut _);
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
valid_signatures.clear();
@@ -796,13 +792,15 @@ fn test_encryption_decryption() {
public_keyring.add_ref(&public_key);
let plain = dc_pgp_pk_decrypt(
ctext_signed.as_ptr() as *const _,
ctext_signed as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring,
None,
)
.unwrap();
free(ctext_signed as *mut _);
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
}
}
@@ -825,14 +823,14 @@ struct TestContext {
unsafe fn create_test_context() -> TestContext {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
let dir = tempdir().unwrap();
let dbfile = CString::new(dir.path().join("db.sqlite").to_str().unwrap()).unwrap();
let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap());
assert_eq!(
dc_open(&mut ctx, dbfile.as_ptr(), std::ptr::null()),
dc_open(&mut ctx, dbfile, std::ptr::null()),
1,
"Failed to open {}",
as_str(dbfile.as_ptr() as *const libc::c_char)
as_str(dbfile as *const libc::c_char)
);
free(dbfile as *mut _);
TestContext { ctx: ctx, dir: dir }
}
@@ -953,6 +951,77 @@ fn test_stress_tests() {
}
}
#[test]
fn test_get_contacts() {
unsafe {
let context = create_test_context();
let name = to_cstring("some2");
let contacts = dc_get_contacts(&context.ctx, 0, name);
assert_eq!(dc_array_get_cnt(contacts), 0);
dc_array_unref(contacts);
free(name as *mut _);
let name = to_cstring("bob");
let email = to_cstring("bob@mail.de");
let id = dc_create_contact(&context.ctx, name, email);
assert_ne!(id, 0);
let contacts = dc_get_contacts(&context.ctx, 0, name);
assert_eq!(dc_array_get_cnt(contacts), 1);
dc_array_unref(contacts);
let name2 = to_cstring("alice");
let contacts = dc_get_contacts(&context.ctx, 0, name2);
assert_eq!(dc_array_get_cnt(contacts), 0);
dc_array_unref(contacts);
free(name as *mut _);
free(name2 as *mut _);
free(email as *mut _);
}
}
#[test]
fn test_chat() {
unsafe {
let context = create_test_context();
let name = to_cstring("bob");
let email = to_cstring("bob@mail.de");
let contact1 = dc_create_contact(&context.ctx, name, email);
free(name as *mut _);
free(email as *mut _);
assert_ne!(contact1, 0);
let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
assert!(chat_id > 9, "chat_id too small {}", chat_id);
let chat = dc_chat_new(&context.ctx);
assert!(dc_chat_load_from_db(chat, chat_id));
let chat2_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
assert_eq!(chat2_id, chat_id);
let chat2 = dc_chat_new(&context.ctx);
assert!(dc_chat_load_from_db(chat2, chat2_id));
assert_eq!(as_str((*chat2).name), as_str((*chat).name));
}
}
#[test]
fn test_wrong_db() {
unsafe {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap();
let dbfile_c = to_cstring(dbfile.to_str().unwrap());
let res = dc_open(&mut ctx, dbfile_c, std::ptr::null());
free(dbfile_c as *mut _);
assert_eq!(res, 0);
}
}
#[test]
fn test_arr_to_string() {
let arr2: [uint32_t; 4] = [