Compare commits

..

137 Commits

Author SHA1 Message Date
dignifiedquire
38e2cb97ea refactor(imex): almost all unsafe gone here 2019-09-27 18:34:46 -06:00
holger krekel
f28a971b96 several fixes and streamlinings, probably verified-group encryption is fixed, or at least we should see better errors 2019-09-27 23:24:57 +02:00
holger krekel
18808d0a61 majorly rustify and simplify the incoming decryption pipeline 2019-09-27 23:24:57 +02:00
holger krekel
86369148ee fix #616 -- allow invalid certs for smtp and imap connections -- this is the behaviour of C-core. 2019-09-27 21:44:21 +02:00
holger krekel
f45ee2ab4d fix #615 -- like with c-core Chat-Version is left in unprotected headers because
it's eg used in server-filters for detecting DC messages
2019-09-27 18:28:47 +02:00
holger krekel
2b73fab913 cargo fmt 2019-09-27 18:28:29 +02:00
holger krekel
e0d750ac64 little cleanup dc_imex 2019-09-27 18:28:29 +02:00
Alexander Krotov
bb57c6e7b7 Merge pull request #627 from deltachat/dc_receive_imf-slice
Pass slice to dc_receive_imf
2019-09-27 16:27:14 +00:00
Alexander Krotov
f346a052c1 Return Result from dc_initiate_key_transfer 2019-09-27 17:57:45 +02:00
Alexander Krotov
3933353b5f Pass slice to dc_receive_imf
instead of pointer and length
2019-09-27 17:53:41 +03:00
Dmitry Bogatov
6c9c21c135 quote_word: avoid dependency on phf crate 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
d02a721eed Reimplement dc_encode_header_words in safe Rust
This change fixes proptest, introduced in [THIS~2] commit.
2019-09-27 04:11:50 +02:00
Dmitry Bogatov
8ffb4ae127 Add proptest seed that reveals strencoding bug 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
96fbeb583b Add proptest to check dc_header_{encode,decode} 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
33b98a15d3 Remove unused "print_hex" function 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
e523ebe3c1 Implement safe version of quote_word 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
e17c671b7c Rename local variables to not misleadingly refer to MMAPString 2019-09-27 04:11:50 +02:00
Dmitry Bogatov
e7565e1a2a Use rust strings instead of MMapString in src/dc_strencode.rs
Since Rust strings operations are assumed to never fail, this commit
removes a lot of checking, whether appending to mmapstring fails. Now it
is Rust runtime burden.
2019-09-27 04:11:50 +02:00
B. Petersen
b73d6377fc do not truncate messages in contact requests
core-c has truncated messages in the contact requests.
this is questionable in general, as
- all messages, including contact requests,
  are already truncated, unquoted, simplified etc.
- the ui should be capable of showing the full text anyway
  (when the contact request is accepted, the whole messase is shown)
- also, all current ui show the contact requests by name only in the
  chatlist; the user often does not even come to the contact request view.
- if the ui wants to show the contact request is a special way,
  it is probably better to leave this truncation up to the ui
2019-09-27 02:55:23 +02:00
holger krekel
31f5fffc45 cargo fmt 2019-09-26 20:45:03 +02:00
holger krekel
64c518c2f2 remove ok_to_continue 2019-09-26 20:45:03 +02:00
B. Petersen
1ed543b0e8 adapt group-id length to reality 2019-09-26 20:10:33 +02:00
Floris Bruynooghe
8b7cd2dd1a Revert back to only ffi-level checking of open context
The Rust context is always open, the return value of this function was
simply the wrong way around.
2019-09-26 20:08:36 +02:00
Florian Bruhin
8520b5211a python: Add .venv to .gitignore 2019-09-26 19:20:56 +02:00
Florian Bruhin
03661e2a71 python: Allow to configure debug logging via account 2019-09-26 19:20:56 +02:00
jikstra
20b82b3638 Fix ffi actually calling context.sql.is_open() 2019-09-26 18:36:31 +02:00
Alexander Krotov
cb499ae502 Return Result<String> from dc_decrypt_setup_file 2019-09-26 18:05:29 +02:00
holger krekel
02b73207f9 fixup this PR with tests, and returning None from get_filemime 2019-09-26 17:12:06 +02:00
jikstra
53b5cbc12a get_filemime() should return an empty string if no mimetype is present
and not default to `applicatopm/octet-stream`
2019-09-26 17:12:06 +02:00
Friedel Ziegelmayer
f4c6decd2d refactor(mmime): split up into modules (#609)
refactor(mmime): split up into modules
2019-09-26 15:28:36 +02:00
Floris Bruynooghe
69f1497986 Make dc_get_info() work on a closed context
There is very little API guarantees about this, but clients seem to
expect *something* to work on a closed context.  So split this up into
static info an more dynamic context-related info, but let's not
provide any guarantees about what keys are available when.

Fixes #599
2019-09-26 14:22:03 +02:00
Florian Bruhin
2b46f01fe3 Use sys.executable in install_python_bindings.py
When calling pip this way, the virtualenv is used even if not activated.
2019-09-26 13:09:08 +02:00
dignifiedquire
dd4adb57cf refactor(mmime): remove some duplication 2019-09-26 12:36:23 +02:00
dignifiedquire
452bce07e1 refactor(mmime): split up into modules 2019-09-26 12:36:23 +02:00
Simon Laux
8d702d0b77 rename and update providers crate 2019-09-26 01:42:39 +02:00
holger krekel
e1dc4b69f5 address all @dignifiedquire review comments 2019-09-25 23:46:44 +02:00
holger krekel
6cd3580263 rustifying dc_continue_key_transfer and fix master-conflict 2019-09-25 23:46:44 +02:00
holger krekel
d5383aecc9 finish dc_imex refactoring, fix linting, rustify some things 2019-09-25 23:46:44 +02:00
holger krekel
71cbbab2c9 fix #596 and some cleanups 2019-09-25 23:46:44 +02:00
holger krekel
8518d8f456 rustify imex and friends 2019-09-25 23:46:44 +02:00
holger krekel
adc0db04bc failing test 2019-09-25 23:46:44 +02:00
björn petersen
c61fc59003 Merge pull request #608 from deltachat/fix-get-setupcodebegin
fix boolean error that makes get_setupcodebegin() failing
2019-09-25 21:39:48 +02:00
B. Petersen
40f9072250 add get_setupcodebegin to python bindings, test the function 2019-09-25 21:13:05 +02:00
B. Petersen
ea30bb351e fix boolean error that makes get_setupcodebegin() failing 2019-09-25 20:37:36 +02:00
Alexander Krotov
b93550f6c8 Use DC_MSG_ID_LAST_SPECIAL in dc_continue_key_transfer 2019-09-25 03:29:37 +02:00
Alexander Krotov
60bd053095 Pass setup_code to dc_continue_key_transfer as &str 2019-09-25 03:29:37 +02:00
Alexander Krotov
8165b76001 Make dc_normalize_setup_code safe 2019-09-25 03:29:37 +02:00
jikstra
efc563f5ff fix test 2019-09-25 01:06:42 +02:00
jikstra
e52acc994c Make get_draft() return Ok(None) when called for a special chat id 2019-09-25 01:06:42 +02:00
holger krekel
646833d3ec remove phf crate macro usage: it introduced 7 deps and is really an optimization if you very large (100K+) tables -- we have 10 entries or so and it's called once per message. Let's not introduce crates just because we can -- it increases compile time and in the phf case also introduced a github dependency (for whatever reason -- don't want to know ;) 2019-09-25 00:19:47 +02:00
dignifiedquire
fd72c27afe chore(release): release 1.0.0-alpha.5 2019-09-24 17:26:18 +02:00
dignifiedquire
c13bcc25c6 chore(deps): update lock file 2019-09-24 17:25:03 +02:00
holger krekel
21c9ff6c85 cargo fmt 2019-09-23 23:13:41 +02:00
holger krekel
4d6b367654 remove ok_to_continue from job 2019-09-23 23:13:41 +02:00
holger krekel
e2fd22a78e cargo fmt 2019-09-23 21:23:55 +02:00
holger krekel
0759bdde01 cleanup chat.rs: remove ok_to_continue and return result from add_contact methods 2019-09-23 21:23:55 +02:00
holger krekel
faa03e0e14 no functional code change: rename dc_mimefactory to mimefactory and move some functions to become MimeFactory methods 2019-09-23 20:20:34 +02:00
holger krekel
f70897a6d3 rustify new_data_part() and related sanitizations 2019-09-23 18:43:04 +02:00
holger krekel
ba231d2c5f address @dignifiedquire comments 2019-09-23 17:10:21 +02:00
holger krekel
095cb759ed avoid cdata_to_free trick and some more cleanups 2019-09-23 17:10:21 +02:00
holger krekel
5cbcb76039 introduce safety and a particular EncryptHelper 2019-09-23 17:10:21 +02:00
holger krekel
3388b42f20 another rustification of encrypt() 2019-09-23 17:10:21 +02:00
holger krekel
e1d541b02e create wrapmime module and simplify some mailmime code 2019-09-23 17:10:21 +02:00
Friedel Ziegelmayer
cb784615ee feat: import mmime crate 2019-09-23 13:20:30 +02:00
B. Petersen
321c5e049b re-add some comments from core-c 2019-09-22 23:39:16 +02:00
holger krekel
ed7cf218f8 address three comments from @dignifiedquire 2019-09-22 23:39:16 +02:00
holger krekel
74d8368525 rustify references, in_reply_to, mimefactory's recipients_{addr,names} 2019-09-22 23:39:16 +02:00
holger krekel
dcbfa272f9 rustify parts of MimeFactory struct 2019-09-22 23:39:16 +02:00
holger krekel
42dd600e0c more rustifications 2019-09-22 23:39:16 +02:00
holger krekel
9689df601f streamline mimetype guessing and build_body_file 2019-09-22 23:39:16 +02:00
holger krekel
f6019583b7 better looping on some clists 2019-09-22 23:39:16 +02:00
holger krekel
2d50a3335d - add mailimf_field_add helper to reduce number of strdup()s
- make build_body_text avoid char*
2019-09-22 23:39:16 +02:00
holger krekel
202bfa987d dc_mimefactory+friends: simplify used strings, convert message_text and message_text2 to String, convert ints to bools 2019-09-22 23:39:16 +02:00
holger krekel
93f9c7cfbd e2ee_guaranteed -> bool, rustify set_error 2019-09-22 23:39:16 +02:00
Dmitry Bogatov
8f6a0bbf09 Remove unused argument of DC_JOB_CONFIGURE_IMAP 2019-09-22 22:22:00 +02:00
björn petersen
1a47c148e5 Merge pull request #574 from KAction/test-dc_remove_cr
Add test for `dc_remove_cr_chars`
2019-09-22 19:47:22 +02:00
björn petersen
2435ba1ea0 Merge pull request #576 from KAction/quote_word
Remove useless argument to `quote_word`
2019-09-22 19:46:48 +02:00
Dmitry Bogatov
9ba57a923b Remove useless argument to quote_word
Function "quote_word" accepts display charset argument, but on only call
site it is string constant. Change function to always use "utf-8"
display charset, and remove useless argument.
2019-09-21 22:20:56 +00:00
Dmitry Bogatov
90e2b6f26b Add test for dc_remove_cr_chars 2019-09-21 21:26:47 +00:00
dignifiedquire
05f9f454c3 refactor: remove x module and delete deadcode 2019-09-21 17:56:49 +02:00
Friedel Ziegelmayer
b85f59798c refactor(chat): remove c types and unsafe (#572)
refactor(chat): remove c types and unsafe
2019-09-21 17:14:52 +02:00
dignifiedquire
e80345a05b refactor(chat): remove c types and unsafe 2019-09-21 16:59:59 +02:00
dignifiedquire
0bdcc3d616 refactor(message): remove remaining unsafe and c types 2019-09-21 16:37:19 +02:00
dignifiedquire
987f12740e refactor(message): remove unsafe and c types from the Message api 2019-09-21 16:37:19 +02:00
dignifiedquire
1265016a55 refactor(message): rustiy api 2019-09-21 16:37:19 +02:00
Friedel Ziegelmayer
48d1de3678 Avoid ok-to-continue pattern in set_draft_raw (#570)
Avoid ok-to-continue pattern in `set_draft_raw`
2019-09-21 14:32:20 +02:00
Dmitry Bogatov
43f6db3252 cargo-fmt 2019-09-21 06:19:55 +00:00
Dmitry Bogatov
5b917e7d10 Remove last mutable variable from do_set_draft 2019-09-21 06:19:21 +00:00
Dmitry Bogatov
0523868a88 Avoid ok-to-continue pattern in "do_set_draft" function
Note: I strongly suggest reviewing this commit in side-by-side mode.

Note: This commit fails CI due incorrect formatting. It is done
deliberately to simplify review process.
2019-09-21 06:16:15 +00:00
Dmitry Bogatov
26f176eb7e Factor another part of set_draft_raw into separate function 2019-09-21 06:04:55 +00:00
Dmitry Bogatov
ad32b5ca8f cargo-fmt 2019-09-21 05:44:31 +00:00
Dmitry Bogatov
dbf14179dc Make message argument to set_draft_raw() no longer optional
Previously, "set_draft_raw" function was used with "msg = None" only for
side-effect of removing current draft message in chat.

After draft removal functionality was factored into separate
"maybe_delete_draft" function, it is used directly in only call site,
which used to invoke "set_draft_raw" with optional message.

This function is private, and this refactoring does not change FFI
interface.

Note: This commit fails CI due incorrect formatting. It is done
deliberately to simplify review process.
2019-09-21 05:34:48 +00:00
Dmitry Bogatov
45b0a4ec27 Factor part of set_draft_raw into new function maybe_delete_draft 2019-09-21 05:24:03 +00:00
dignifiedquire
1969ee02a5 refactor(mimeparser): rustify mailmime_get_type 2019-09-21 00:51:36 +02:00
dignifiedquire
266b205c75 refactor: rustify interface of maimlime_transfer_decode 2019-09-21 00:51:36 +02:00
Dmitry Bogatov
7dd3bad8bd Add factory method MimeFactory::new
* src/dc_mimefactory.rs(new): add factory method to have verbose
   initialization of all (more than 10) MimeFactory fields only in one place.
 * src/dc_mimefactory.rc(dc_mimefactory_load_msg, dc_mimefactory_load_mdn):
   simplify code (and reduce linecount) using Mimefactory::new
2019-09-20 22:49:33 +02:00
holger krekel
4b45be7cda cargo fmt only 2019-09-20 22:43:20 +02:00
holger krekel
497ffd86fa make logic and comments more like C (early returns instead of nestedness)
next commit: cargo fmt
2019-09-20 22:43:20 +02:00
dignifiedquire
0bdcc4269f refactor(mimeparser): split and cleanup parse_mime_recursive 2019-09-20 21:42:23 +02:00
Friedel Ziegelmayer
e583c99f94 Make return type of Image::mv an enum (#548)
Make return type of Image::mv an enum
2019-09-20 20:56:27 +02:00
Friedel Ziegelmayer
e22e50c3fa Rename dc_mimefactory_t -> MimeFactory (#563)
Rename dc_mimefactory_t -> MimeFactory
2019-09-20 20:56:12 +02:00
Dmitry Bogatov
e9c9a3e1ce Change type of MimeFactory.loaded to enum
* src/dc_mimefactory.rs(MimeFactory): change type of `loaded` field
 * src/dc_mimefactory.rs(Loaded): new enum, describing possible
   values of `loaded` field of `MimeFactory` structure
 * src/dc_mimefactory.rs(dc_mimefactory_loaded_t): remove unused type alias
 * src/job.rs(add_smtp_job): adjust call site by removing multiple casts
 * src/dc_mimefactory.rs(dc_mimefactory_render): ditto
2019-09-20 18:02:57 +00:00
Dmitry Bogatov
fa7bb71f3f Change type of MimeFactory.out_{gossip,encrypted} to bool 2019-09-20 18:02:57 +00:00
Dmitry Bogatov
34a3ad82e0 Rename dc_mimefactory_t -> MimeFactory
CamelCase naming convention is more natural for Rust.

https://rust-lang-nursery.github.io/api-guidelines/naming.html
2019-09-20 18:02:57 +00:00
Dmitry Bogatov
2f5d74dbf4 Remove unused constants from src/imap.rs 2019-09-20 17:35:26 +00:00
Dmitry Bogatov
4bf5ba594c Make Imap::set_mdnseen return enum, not int 2019-09-20 17:35:26 +00:00
Dmitry Bogatov
6e2da27f45 Change return type of Imap::set_seen to enum 2019-09-20 17:35:26 +00:00
Dmitry Bogatov
6ee9465d43 Make return type of Image::mv an enum
Replace named constants with enum to improve type-safety and make
exhausiveness checks possible.

Note, that since this enum never pass FFI border, its numeric values
does not need to be specified explicitly and can be left on compiler's
discretion.
2019-09-20 17:35:25 +00:00
Dmitry Bogatov
391a6bf422 Replace numbers with named constants 2019-09-20 17:35:25 +00:00
Floris Bruynooghe
5001a0e37d Fix dc_make_rel_path
This was not substituting $BLOBDIR correctly.
2019-09-20 18:38:39 +02:00
holger krekel
fd8d16a7db replace weird pointer-loops with nice for-loops (thanks @dignifiedquire for guiding) 2019-09-20 17:52:07 +02:00
holger krekel
f3ac9306f3 use bool instead of int 2019-09-20 17:52:07 +02:00
holger krekel
59740d0b56 remove unused var, numbers to const-names 2019-09-20 17:52:07 +02:00
holger krekel
fb05a6c26f transfer docs to and cleanup some parts of e2ee::decrypt() 2019-09-20 17:52:07 +02:00
holger krekel
7943b708d2 dc_mimeparser: do a round of renames on numbers to constants and add comments from the C code 2019-09-20 15:23:34 +02:00
dignifiedquire
04e37d1eca chore: update mmime to released version and other deps 2019-09-20 12:02:08 +02:00
holger krekel
91b98e8c6d as discussed during camp and otherwise ... add dc_perform_{mvbox,sentbox}_jobs hooks which, however, for now have an empty implementation. They can already be called from UIs, though. Next step is refactoring imap-job handling to only execute jobs belonging to the respective imap folder. 2019-09-20 01:03:25 +02:00
Floris Bruynooghe
70234e5b19 Add a test for fix in #541
A fixed bug should have a test.  This is an easy test to write.
2019-09-20 00:46:54 +02:00
holger krekel
ceff85d892 add test and python API for verified group handling/chatting
add msg.is_encrypted() API and check for it from some tests
2019-09-20 00:33:33 +02:00
holger krekel
9f914dd42e fix test-state modification bug (online tests running after OnlineConfigureFails tests would break) 2019-09-19 23:01:51 +02:00
holger krekel
24f5d68fef add configure-failure tests 2019-09-19 23:01:51 +02:00
Dmitry Bogatov
735fc325b1 Add test for export_key_to_asc_file 2019-09-19 21:43:54 +02:00
Dmitry Bogatov
178b216e48 Reduce number of arguments of export_key_to_asc_file
Previously, this function accepted both key id (integer) and is_default
(boolean). If `is_default` flag was set, value `id` parameter wasn't
used. This commit compresses two argument into single `id: Option<i64>`.
2019-09-19 21:43:54 +02:00
Simon Laux
5d0481f7a2 use provider overview crate instead of git 2019-09-19 20:07:34 +02:00
holger krekel
711bc69750 address @dignifiedquire comment 2019-09-19 20:03:16 +02:00
holger krekel
7263c9490d refactor rfc724_mid parsing and creation to avoid char*, add tests 2019-09-19 20:03:16 +02:00
holger krekel
0c88bc6ac7 more rfc724_mid cleanup 2019-09-19 20:03:16 +02:00
holger krekel
fda8d0a2e2 factory.rfc724_mid is a String now (instead of C-Char*) 2019-09-19 20:03:16 +02:00
holger krekel
14bdf7fae8 make dc_create_outgoing_rfc724_mid safe and simplify call sites 2019-09-19 20:03:16 +02:00
holger krekel
d4ff7ecbaa split qr tests 2019-09-19 20:00:27 +02:00
Dmitry Bogatov
030aec7373 Read example GPG keys from files using include_str!
For consistency with other tests that use example public and private
keys, replace use of `concat!` macro with `include_str!`. This way, key
data embedded into source with single line of code.
2019-09-19 18:57:39 +02:00
B. Petersen
6a351de4f9 cargo fmt 2019-09-19 18:42:29 +02:00
B. Petersen
bbff1c9c3e test that bad credentials do not panic 2019-09-19 18:42:29 +02:00
B. Petersen
bbb8144129 do not panic when 0 (=failure) is passed to the progress! macro 2019-09-19 17:00:01 +02:00
Friedel Ziegelmayer
83f3e23297 improve python caching (#468)
(@dignifiedquire and @hpk42) 
- introduce rust-caching to python test runs 
- skip release and ffi runs, they are check using python bindings
- shuffle files such that ci_scripts/ contains all the ci scripts
- partly parallelize python tox runs
2019-09-19 13:10:19 +02:00
B. Petersen
4f880932ae change oder of typedefs, function-definitions and defines
as c is top-down, we start with the typedefs
so that functions can use them freely.
after that, the functions are declared
and finally the #defines (for less noise in the function sections).
grouping is done by classes.
2019-09-19 12:58:26 +02:00
B. Petersen
62e8c2497c add missing doxygen commands, fix typo 2019-09-19 12:58:26 +02:00
85 changed files with 27255 additions and 6369 deletions

1
.gitignore vendored
View File

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

168
Cargo.lock generated
View File

@@ -84,7 +84,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "backtrace"
version = "0.3.37"
version = "0.3.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -130,7 +130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -317,7 +317,7 @@ name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -419,7 +419,7 @@ dependencies = [
[[package]]
name = "ctor"
version = "0.1.10"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -481,11 +481,11 @@ dependencies = [
[[package]]
name = "deltachat"
version = "1.0.0-alpha.4"
version = "1.0.0-alpha.5"
dependencies = [
"backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -502,13 +502,12 @@ dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"mmime 0.1.2-alpha.0 (git+https://github.com/dignifiedquire/mmime?rev=bccd2c2)",
"mmime 0.1.2",
"native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pgp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.24 (git+https://github.com/sfackler/rust-phf?rev=0d00821)",
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -524,21 +523,20 @@ dependencies = [
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
"strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thread-local-object 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "deltachat-provider-overview"
version = "0.1.0"
source = "git+https://github.com/deltachat/provider-overview?rev=366b41a7503973e4ffac3aa5173b419f2f03c211#366b41a7503973e4ffac3aa5173b419f2f03c211"
name = "deltachat-provider-database"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -552,10 +550,10 @@ dependencies = [
[[package]]
name = "deltachat_ffi"
version = "1.0.0-alpha.4"
version = "1.0.0-alpha.5"
dependencies = [
"deltachat 1.0.0-alpha.4",
"deltachat-provider-overview 0.1.0 (git+https://github.com/deltachat/provider-overview?rev=366b41a7503973e4ffac3aa5173b419f2f03c211)",
"deltachat 1.0.0-alpha.5",
"deltachat-provider-database 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -688,7 +686,7 @@ name = "error-chain"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -705,7 +703,7 @@ name = "failure"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -781,7 +779,7 @@ name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -902,7 +900,7 @@ name = "human-panic"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"os_type 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1090,7 +1088,7 @@ dependencies = [
[[package]]
name = "lexical-core"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1258,8 +1256,7 @@ dependencies = [
[[package]]
name = "mmime"
version = "0.1.2-alpha.0"
source = "git+https://github.com/dignifiedquire/mmime?rev=bccd2c2#bccd2c2c89e9241e05f321c963f638affdccad96"
version = "0.1.2"
dependencies = [
"charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1302,7 +1299,7 @@ name = "nix"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1328,7 +1325,7 @@ name = "nom"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lexical-core 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1411,7 +1408,7 @@ name = "openssl"
version = "0.10.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1603,46 +1600,6 @@ dependencies = [
"x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "phf"
version = "0.7.24"
source = "git+https://github.com/sfackler/rust-phf?rev=0d00821#0d0082178568036736bb6d51cb91f95ca5a616c3"
dependencies = [
"phf_macros 0.7.24 (git+https://github.com/sfackler/rust-phf?rev=0d00821)",
"phf_shared 0.7.24 (git+https://github.com/sfackler/rust-phf?rev=0d00821)",
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "phf_generator"
version = "0.7.24"
source = "git+https://github.com/sfackler/rust-phf?rev=0d00821#0d0082178568036736bb6d51cb91f95ca5a616c3"
dependencies = [
"phf_shared 0.7.24 (git+https://github.com/sfackler/rust-phf?rev=0d00821)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "phf_macros"
version = "0.7.24"
source = "git+https://github.com/sfackler/rust-phf?rev=0d00821#0d0082178568036736bb6d51cb91f95ca5a616c3"
dependencies = [
"phf_generator 0.7.24 (git+https://github.com/sfackler/rust-phf?rev=0d00821)",
"phf_shared 0.7.24 (git+https://github.com/sfackler/rust-phf?rev=0d00821)",
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "phf_shared"
version = "0.7.24"
source = "git+https://github.com/sfackler/rust-phf?rev=0d00821#0d0082178568036736bb6d51cb91f95ca5a616c3"
dependencies = [
"siphasher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pkg-config"
version = "0.3.16"
@@ -1659,7 +1616,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"ctor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1674,16 +1631,6 @@ dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "0.4.30"
@@ -1694,7 +1641,7 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1706,7 +1653,7 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1736,7 +1683,7 @@ name = "pulldown-cmark"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1774,7 +1721,7 @@ name = "quote"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1837,7 +1784,6 @@ dependencies = [
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_pcg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1935,15 +1881,6 @@ dependencies = [
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_pcg"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_xorshift"
version = "0.1.1"
@@ -2065,7 +2002,7 @@ name = "rusqlite"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2215,7 +2152,7 @@ name = "serde_derive"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2275,11 +2212,6 @@ dependencies = [
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "siphasher"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "skeptic"
version = "0.13.4"
@@ -2348,18 +2280,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strum"
version = "0.15.0"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strum_macros"
version = "0.15.0"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2402,7 +2334,7 @@ name = "syn"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2928,13 +2860,13 @@ dependencies = [
"checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875"
"checksum backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "5180c5a20655b14a819b652fd2378fa5f1697b6c9ddad3e695c2f9cedf6df4e2"
"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5"
"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b"
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80"
"checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb"
"checksum bitfield 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182"
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
@@ -2969,13 +2901,13 @@ dependencies = [
"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9"
"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
"checksum ctor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5b6b2f4752cc29efbfd03474c532ce8f916f2d44ec5bb8c21f93bc76e5365528"
"checksum ctor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3e061727ebef83bbccac7c27b9a5ff9fd83094d34cb20f4005440a9562a27de7"
"checksum curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d"
"checksum darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfbcb0c5961907597a7d1148e3af036268f2b773886b8bb3eeb1e1281d3d3d6"
"checksum darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6afc018370c3bff3eb51f89256a6bdb18b4fdcda72d577982a14954a7a0b402c"
"checksum darling_macro 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6d8dac1c6f1d29a41c4712b4400f878cb4fcc4c7628f298dd75038e024998d1"
"checksum debug_stub_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "496b7f8a2f853313c3ca370641d7ff3e42c32974fdccda8f0684599ed0a3ff6b"
"checksum deltachat-provider-overview 0.1.0 (git+https://github.com/deltachat/provider-overview?rev=366b41a7503973e4ffac3aa5173b419f2f03c211)" = "<none>"
"checksum deltachat-provider-database 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "814dba060d9fdc7a989fccdc4810ada9d1c7a1f09131c78e42412bc6c634b93b"
"checksum derive_builder 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ac53fa6a3cda160df823a9346442525dcaf1e171999a1cf23e67067e4fd64d4"
"checksum derive_builder_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0288a23da9333c246bb18c143426074a6ae96747995c5819d2947b64cd942b37"
"checksum derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839"
@@ -3037,7 +2969,7 @@ dependencies = [
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c66afaa5dfadbb81d4e00fd1d1ab057c7cd4c799c5a44e0009386d553587e728"
"checksum lexical-core 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d11f3928ffd249baadf9f083cdea16d7cf317b2a8be6227e1169102432a36d2"
"checksum lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2304bccb228c4b020f3a4835d247df0a02a7c4686098d4167762cfbbe4c5cb14"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b95e89c330291768dc840238db7f9e204fd208511ab6319b56193a7f2ae25"
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
@@ -3057,7 +2989,6 @@ dependencies = [
"checksum miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7108aff85b876d06f22503dcce091e29f76733b2bfdd91eebce81f5e68203a10"
"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum mmime 0.1.2-alpha.0 (git+https://github.com/dignifiedquire/mmime?rev=bccd2c2)" = "<none>"
"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e"
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
"checksum nix 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4dbdc256eaac2e3bd236d93ad999d3479ef775c863dbda3068c4006a92eec51b"
@@ -3089,17 +3020,12 @@ dependencies = [
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
"checksum pgp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb80b37b7debf9a98dc0caca3ed40ddf1d383691208763d0458df0b91521020f"
"checksum phf 0.7.24 (git+https://github.com/sfackler/rust-phf?rev=0d00821)" = "<none>"
"checksum phf_generator 0.7.24 (git+https://github.com/sfackler/rust-phf?rev=0d00821)" = "<none>"
"checksum phf_macros 0.7.24 (git+https://github.com/sfackler/rust-phf?rev=0d00821)" = "<none>"
"checksum phf_shared 0.7.24 (git+https://github.com/sfackler/rust-phf?rev=0d00821)" = "<none>"
"checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea"
"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
"checksum pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "717ee476b1690853d222af4634056d830b5197ffd747726a9a1eee6da9f49074"
"checksum proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e688f31d92ffd7c1ddc57a1b4e6d773c0f2a14ee437a4b0a4f5a69c80eb221c8"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8"
"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc"
"checksum proptest 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cf147e022eacf0c8a054ab864914a7602618adba841d800a9a9868a5237a529f"
"checksum publicsuffix 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9bf259a81de2b2eb9850ec990ec78e6a25319715584fd7652b9b26f96fcb1510"
"checksum pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eef52fac62d0ea7b9b4dc7da092aa64ea7ec3d90af6679422d3d7e0e14b6ee15"
@@ -3124,7 +3050,6 @@ dependencies = [
"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
"checksum rand_pcg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e196346cbbc5c70c77e7b4926147ee8e383a38ee4d15d58a08098b169e492b6"
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
@@ -3159,7 +3084,6 @@ dependencies = [
"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68"
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
"checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf"
"checksum siphasher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9913c75df657d84a03fa689c016b0bb2863ff0b497b26a8d6e9703f8d5df03a8"
"checksum skeptic 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fb8ed853fdc19ce09752d63f3a2e5b5158aeb261520cd75eb618bd60305165"
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum slice-deque 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ffddf594f5f597f63533d897427a570dbaa9feabaaa06595b74b71b7014507d7"
@@ -3169,8 +3093,8 @@ dependencies = [
"checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c"
"checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1c33039533f051704951680f1adfd468fd37ac46816ded0d9ee068e60f05f"
"checksum strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47cd23f5c7dee395a00fa20135e2ec0fffcdfa151c56182966d7a3261343432e"
"checksum strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22"
"checksum strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81"
"checksum subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01f40907d9ffc762709e4ff3eb4a6f6b41b650375a3f09ac92b641942b7fb082"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"

View File

@@ -1,18 +1,19 @@
[package]
name = "deltachat"
version = "1.0.0-alpha.4"
version = "1.0.0-alpha.5"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
edition = "2018"
license = "MPL"
[dependencies]
deltachat_derive = { path = "./deltachat_derive" }
mmime = { version = "0.1.2", path = "./mmime" }
libc = "0.2.51"
pgp = { version = "0.2", default-features = false }
hex = "0.3.2"
sha2 = "0.8.0"
rand = "0.6.5"
phf = { git = "https://github.com/sfackler/rust-phf", rev = "0d00821", features = ["macros"] }
smallvec = "0.6.9"
reqwest = "0.9.15"
num-derive = "0.2.5"
@@ -20,7 +21,6 @@ num-traits = "0.2.6"
native-tls = "0.2.3"
lettre = "0.9.0"
imap = { git = "https://github.com/jonhoo/rust-imap", rev = "281d2eb8ab50dc656ceff2ae749ca5045f334e15" }
mmime = { git = "https://github.com/dignifiedquire/mmime", rev = "bccd2c2" }
base64 = "0.10"
charset = "0.1"
percent-encoding = "2.0"
@@ -36,8 +36,8 @@ regex = "1.1.6"
rusqlite = { version = "0.20", features = ["bundled"] }
r2d2_sqlite = "0.12.0"
r2d2 = "0.8.5"
strum = "0.15.0"
strum_macros = "0.15.0"
strum = "0.16.0"
strum_macros = "0.16.0"
thread-local-object = "0.1.0"
backtrace = "0.3.33"
byteorder = "1.3.1"
@@ -59,6 +59,7 @@ proptest = "0.9.4"
members = [
"deltachat-ffi",
"deltachat_derive",
"mmime",
]
[[example]]

View File

@@ -13,7 +13,7 @@ install:
build: false
test_script:
- cargo test --release
- cargo test --release --all
cache:
- target

View File

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

View File

@@ -31,7 +31,7 @@ fi
if [[ $NORUN == "1" ]]; then
export CARGO_SUBCMD="build"
else
export CARGO_SUBCMD="test"
export CARGO_SUBCMD="test --all"
export OPT="${OPT} "
export OPT_RELEASE="${OPT_RELEASE} "
export OPT_RELEASE_IGNORED="${OPT_RELEASE} -- --ignored"
@@ -39,4 +39,5 @@ fi
# Run all the test configurations:
$CARGO_CMD $CARGO_SUBCMD $OPT
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE
$CARGO_CMD $CARGO_SUBCMD $OPT_RELEASE_IGNORED

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat_ffi"
version = "1.0.0-alpha.4"
version = "1.0.0-alpha.5"
description = "Deltachat FFI"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
edition = "2018"
@@ -16,7 +16,7 @@ crate-type = ["cdylib", "staticlib"]
[dependencies]
deltachat = { path = "../", default-features = false }
deltachat-provider-overview = { git = "https://github.com/deltachat/provider-overview", rev = "366b41a7503973e4ffac3aa5173b419f2f03c211" }
deltachat-provider-database = "0.2.1"
libc = "0.2"
human-panic = "1.0.1"
num-traits = "0.2.6"

View File

@@ -11,6 +11,16 @@ extern "C" {
#endif
typedef struct _dc_context dc_context_t;
typedef struct _dc_array dc_array_t;
typedef struct _dc_chatlist dc_chatlist_t;
typedef struct _dc_chat dc_chat_t;
typedef struct _dc_msg dc_msg_t;
typedef struct _dc_contact dc_contact_t;
typedef struct _dc_lot dc_lot_t;
typedef struct _dc_provider dc_provider_t;
/**
* @mainpage Getting started
*
@@ -189,13 +199,6 @@ extern "C" {
* SQLite database for offline functionality and for account-related
* settings.
*/
typedef struct _dc_context dc_context_t;
typedef struct _dc_array dc_array_t;
typedef struct _dc_chatlist dc_chatlist_t;
typedef struct _dc_chat dc_chat_t;
typedef struct _dc_msg dc_msg_t;
typedef struct _dc_contact dc_contact_t;
typedef struct _dc_lot dc_lot_t;
/**
@@ -402,9 +405,14 @@ char* dc_get_config (dc_context_t* context, const char*
/**
* Get information about the context.
*
* The information is returned by a multi-line string
* and contains information about the current configuration.
*
* If the context is not open or configured only a subset of the information
* will be available. There is no guarantee about which information will be
* included when however.
*
* @memberof dc_context_t
* @param context The context as created by dc_context_new().
* @return String which must be free()'d after usage. Never returns NULL.
@@ -442,112 +450,6 @@ char* dc_get_info (dc_context_t* context);
*/
char* dc_get_oauth2_url (dc_context_t* context, const char* addr, const char* redirect_uri);
/**
* Opaque object containing information about one single email provider.
*/
typedef struct _dc_provider dc_provider_t;
/**
* Create a provider struct for the given domain.
*
* @param doamin The domain to get provider info for.
* @return a dc_provider_t struct which can be used with the dc_provider_get_*
* accessor functions. If no provider info is found, NULL will be
* returned.
*/
dc_provider_t* dc_provider_new_from_domain (char* domain);
/**
* Create a provider struct for the given email address.
*
* The provider is extracted from the email address and it's information is returned.
*
* @param email The user's email address to extract the provider info form.
* @return a dc_provider_t struct which can be used with the dc_provider_get_*
* accessor functions. If no provider info is found, NULL will be
* returned.
*/
dc_provider_t* dc_provider_new_from_email (char* email);
/**
* URL of the overview page.
*
* This URL allows linking to the providers page on providers.delta.chat.
*
* @param provider The dc_provider_t struct.
* @return A string which must be free()d.
*/
char* dc_provider_get_overview_page (const dc_provider_t* provider);
/**
* The provider's name.
*
* The name of the provider, e.g. "POSTEO".
*
* @param provider The dc_provider_t struct.
* @return A string which must be free()d.
*/
char* dc_provider_get_name (const dc_provider_t* provider);
/**
* The markdown content of the providers page.
*
* This contains the preparation steps or additional information if the status
* is DC_PROVIDER_STATUS_BROKEN.
*
* @param provider The dc_provider_t struct.
* @return A string which must be free()d.
*/
char* dc_provider_get_markdown (const dc_provider_t* provider);
/**
* Date of when the state was last checked/updated.
*
* This is returned as a string.
*
* @param provider The dc_provider_t struct.
* @return A string which must be free()d.
*/
char* dc_provider_get_status_date (const dc_provider_t* provider);
/**
* Whether DC works with this provider.
*
* Can be one of @ref DC_PROVIDER_STATUS_OK, @ref
* DC_PROVIDER_STATUS_PREPARATION and @ref DC_PROVIDER_STATUS_BROKEN.
*
* @param provider The dc_provider_t struct.
* @return The status as a constant number.
*/
int dc_provider_get_status (const dc_provider_t* provider);
/**
* Free the provider info struct.
*
* @param provider The dc_provider_t struct.
*/
void dc_provider_unref (const dc_provider_t* provider);
/**
* Provider status returned by dc_provider_get_status().
*
* Works right out of the box without any preperation steps needed
*/
#define DC_PROVIDER_STATUS_OK 1
/**
* Provider status returned by dc_provider_get_status().
*
* Works, but preparation steps are needed
*/
#define DC_PROVIDER_STATUS_PREPARATION 2
/**
* Provider status returned by dc_provider_get_status().
*
* Doesn't work (too unstable to use falls also in this category)
*/
#define DC_PROVIDER_STATUS_BROKEN 3
// connect
@@ -713,12 +615,8 @@ void dc_interrupt_imap_idle (dc_context_t* context);
/**
* Fetch new messages from the MVBOX, if any.
* The MVBOX is a folder on the account where chat messages are moved to.
* The moving is done to not disturb shared accounts that are used by both,
* Delta Chat and a classical MUA.
*
* This function and dc_perform_mvbox_idle()
* Execute pending mvbox-jobs.
* This function and dc_perform_mvbox_fetch() and dc_perform_mvbox_idle()
* must be called from the same thread, typically in a loop.
*
* Example:
@@ -726,6 +624,7 @@ void dc_interrupt_imap_idle (dc_context_t* context);
* void* mvbox_thread_func(void* context)
* {
* while (true) {
* dc_perform_mvbox_jobs(context);
* dc_perform_mvbox_fetch(context);
* dc_perform_mvbox_idle(context);
* }
@@ -739,13 +638,26 @@ void dc_interrupt_imap_idle (dc_context_t* context);
*
* // network becomes available again -
* // the interrupt causes dc_perform_mvbox_idle() in the thread above
* // to return so that and messages are fetched.
* // to return so that jobs are executed and messages are fetched.
* dc_maybe_network(context);
*
* @memberof dc_context_t
* @param context The context as created by dc_context_new().
* @return None.
*/
void dc_perform_mvbox_jobs (dc_context_t* context);
/**
* Fetch new messages from the MVBOX, if any.
* The MVBOX is a folder on the account where chat messages are moved to.
* The moving is done to not disturb shared accounts that are used by both,
* Delta Chat and a classical MUA.
*
* @memberof dc_context_t
* @param context The context as created by dc_context_new().
* @return None.
*/
void dc_perform_mvbox_fetch (dc_context_t* context);
@@ -782,6 +694,39 @@ void dc_perform_mvbox_idle (dc_context_t* context);
*/
void dc_interrupt_mvbox_idle (dc_context_t* context);
/**
* Execute pending sentbox-jobs.
* This function and dc_perform_sentbox_fetch() and dc_perform_sentbox_idle()
* must be called from the same thread, typically in a loop.
*
* Example:
*
* void* sentbox_thread_func(void* context)
* {
* while (true) {
* dc_perform_sentbox_jobs(context);
* dc_perform_sentbox_fetch(context);
* dc_perform_sentbox_idle(context);
* }
* }
*
* // start sentbox-thread that runs forever
* pthread_t sentbox_thread;
* pthread_create(&sentbox_thread, NULL, sentbox_thread_func, context);
*
* ... program runs ...
*
* // network becomes available again -
* // the interrupt causes dc_perform_sentbox_idle() in the thread above
* // to return so that jobs are executed and messages are fetched.
* dc_maybe_network(context);
*
* @memberof dc_context_t
* @param context The context as created by dc_context_new().
* @return None.
*/
void dc_perform_sentbox_jobs (dc_context_t* context);
/**
* Fetch new messages from the Sent folder, if any.
@@ -3524,6 +3469,110 @@ int dc_contact_is_blocked (const dc_contact_t* contact);
int dc_contact_is_verified (dc_contact_t* contact);
/**
* @class dc_provider_t
*
* Opaque object containing information about one single email provider.
*/
/**
* Create a provider struct for the given domain.
*
* @memberof dc_provider_t
* @param domain The domain to get provider info for.
* @return a dc_provider_t struct which can be used with the dc_provider_get_*
* accessor functions. If no provider info is found, NULL will be
* returned.
*/
dc_provider_t* dc_provider_new_from_domain (char* domain);
/**
* Create a provider struct for the given email address.
*
* The provider is extracted from the email address and it's information is returned.
*
* @memberof dc_provider_t
* @param email The user's email address to extract the provider info form.
* @return a dc_provider_t struct which can be used with the dc_provider_get_*
* accessor functions. If no provider info is found, NULL will be
* returned.
*/
dc_provider_t* dc_provider_new_from_email (char* email);
/**
* URL of the overview page.
*
* This URL allows linking to the providers page on providers.delta.chat.
*
* @memberof dc_provider_t
* @param provider The dc_provider_t struct.
* @return A string which must be free()d.
*/
char* dc_provider_get_overview_page (const dc_provider_t* provider);
/**
* The provider's name.
*
* The name of the provider, e.g. "POSTEO".
*
* @memberof dc_provider_t
* @param provider The dc_provider_t struct.
* @return A string which must be free()d.
*/
char* dc_provider_get_name (const dc_provider_t* provider);
/**
* The markdown content of the providers page.
*
* This contains the preparation steps or additional information if the status
* is @ref DC_PROVIDER_STATUS_BROKEN.
*
* @memberof dc_provider_t
* @param provider The dc_provider_t struct.
* @return A string which must be free()d.
*/
char* dc_provider_get_markdown (const dc_provider_t* provider);
/**
* Date of when the state was last checked/updated.
*
* This is returned as a string.
*
* @memberof dc_provider_t
* @param provider The dc_provider_t struct.
* @return A string which must be free()d.
*/
char* dc_provider_get_status_date (const dc_provider_t* provider);
/**
* Whether DC works with this provider.
*
* Can be one of @ref DC_PROVIDER_STATUS_OK, @ref
* DC_PROVIDER_STATUS_PREPARATION and @ref DC_PROVIDER_STATUS_BROKEN.
*
* @memberof dc_provider_t
* @param provider The dc_provider_t struct.
* @return The status as a constant number.
*/
int dc_provider_get_status (const dc_provider_t* provider);
/**
* Free the provider info struct.
*
* @memberof dc_provider_t
* @param provider The dc_provider_t struct.
*/
void dc_provider_unref (const dc_provider_t* provider);
/**
* @class dc_lot_t
*
@@ -4131,6 +4180,41 @@ void dc_array_add_id (dc_array_t*, uint32_t); // depreca
#define DC_SHOW_EMAILS_ALL 2
/**
* @defgroup DC_PROVIDER_STATUS DC_PROVIDER_STATUS
*
* These constants are used as return values for dc_provider_get_status().
*
* @addtogroup DC_PROVIDER_STATUS
* @{
*/
/**
* Provider status returned by dc_provider_get_status().
*
* Works right out of the box without any preperation steps needed
*/
#define DC_PROVIDER_STATUS_OK 1
/**
* Provider status returned by dc_provider_get_status().
*
* Works, but preparation steps are needed
*/
#define DC_PROVIDER_STATUS_PREPARATION 2
/**
* Provider status returned by dc_provider_get_status().
*
* Doesn't work (too unstable to use falls also in this category)
*/
#define DC_PROVIDER_STATUS_BROKEN 3
/**
* @}
*/
/*
* TODO: Strings need some doumentation about used placeholders.
*

View File

@@ -24,7 +24,9 @@ use num_traits::{FromPrimitive, ToPrimitive};
use deltachat::contact::Contact;
use deltachat::context::Context;
use deltachat::dc_tools::{as_path, as_str, dc_strdup, to_string, OsStrExt, StrExt};
use deltachat::dc_tools::{
as_path, as_str, dc_strdup, to_string, to_string_lossy, OsStrExt, StrExt,
};
use deltachat::*;
// as C lacks a good and portable error handling,
@@ -186,7 +188,7 @@ pub unsafe extern "C" fn dc_context_new(
let os_name = if os_name.is_null() {
String::from("DcFFI")
} else {
dc_tools::to_string_lossy(os_name)
to_string_lossy(os_name)
};
let ffi_ctx = ContextWrapper {
cb,
@@ -273,11 +275,11 @@ pub unsafe extern "C" fn dc_is_open(context: *mut dc_context_t) -> libc::c_int {
eprintln!("ignoring careless call to dc_is_open()");
return 0;
}
let ffi_context = &mut *context;
let ffi_context = &*context;
let inner_guard = ffi_context.inner.read().unwrap();
match *inner_guard {
Some(_) => 0,
None => 1,
Some(_) => 1,
None => 0,
}
}
@@ -345,9 +347,12 @@ pub unsafe extern "C" fn dc_get_info(context: *mut dc_context_t) -> *mut libc::c
return dc_strdup(ptr::null());
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| render_info(ctx.get_info()).unwrap_or_default().strdup())
.unwrap_or_else(|_| "".strdup())
let guard = ffi_context.inner.read().unwrap();
let info = match guard.as_ref() {
Some(ref ctx) => ctx.get_info(),
None => context::get_info(),
};
render_info(info).unwrap_or_default().strdup()
}
fn render_info(
@@ -471,6 +476,18 @@ pub unsafe extern "C" fn dc_perform_mvbox_fetch(context: *mut dc_context_t) {
.unwrap_or(())
}
#[no_mangle]
pub unsafe extern "C" fn dc_perform_mvbox_jobs(context: *mut dc_context_t) {
if context.is_null() {
eprintln!("ignoring careless call to dc_perform_mvbox_jobs()");
return;
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| job::perform_mvbox_jobs(ctx))
.unwrap_or(())
}
#[no_mangle]
pub unsafe extern "C" fn dc_perform_mvbox_idle(context: *mut dc_context_t) {
if context.is_null() {
@@ -507,6 +524,18 @@ pub unsafe extern "C" fn dc_perform_sentbox_fetch(context: *mut dc_context_t) {
.unwrap_or(())
}
#[no_mangle]
pub unsafe extern "C" fn dc_perform_sentbox_jobs(context: *mut dc_context_t) {
if context.is_null() {
eprintln!("ignoring careless call to dc_perform_sentbox_jobs()");
return;
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| job::perform_sentbox_jobs(ctx))
.unwrap_or(())
}
#[no_mangle]
pub unsafe extern "C" fn dc_perform_sentbox_idle(context: *mut dc_context_t) {
if context.is_null() {
@@ -711,7 +740,7 @@ pub unsafe extern "C" fn dc_send_text_msg(
return 0;
}
let ffi_context = &*context;
let text_to_send = dc_tools::to_string_lossy(text_to_send);
let text_to_send = to_string_lossy(text_to_send);
ffi_context
.with_inner(|ctx| {
chat::send_text_msg(ctx, chat_id, text_to_send)
@@ -912,6 +941,12 @@ pub unsafe extern "C" fn dc_get_next_media(
eprintln!("ignoring careless call to dc_get_next_media()");
return 0;
}
let direction = if dir < 0 {
chat::Direction::Backward
} else {
chat::Direction::Forward
};
let ffi_context = &*context;
let msg_type = from_prim(msg_type).expect(&format!("invalid msg_type = {}", msg_type));
let or_msg_type2 =
@@ -920,7 +955,7 @@ pub unsafe extern "C" fn dc_get_next_media(
from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3));
ffi_context
.with_inner(|ctx| {
chat::get_next_media(ctx, msg_id, dir, msg_type, or_msg_type2, or_msg_type3)
chat::get_next_media(ctx, msg_id, direction, msg_type, or_msg_type2, or_msg_type3)
})
.unwrap_or(0)
}
@@ -1052,7 +1087,8 @@ pub unsafe extern "C" fn dc_is_contact_in_chat(
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| chat::is_contact_in_chat(ctx, chat_id, contact_id))
.unwrap_or(0)
.unwrap_or_default()
.into()
}
#[no_mangle]
@@ -1148,7 +1184,7 @@ pub unsafe extern "C" fn dc_get_msg_info(
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| message::dc_get_msg_info(ctx, msg_id))
.with_inner(|ctx| message::get_msg_info(ctx, msg_id).strdup())
.unwrap_or_else(|_| ptr::null_mut())
}
@@ -1163,7 +1199,11 @@ pub unsafe extern "C" fn dc_get_mime_headers(
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| message::dc_get_mime_headers(ctx, msg_id))
.with_inner(|ctx| {
message::get_mime_headers(ctx, msg_id)
.map(|s| s.strdup())
.unwrap_or_else(|| ptr::null_mut())
})
.unwrap_or_else(|_| ptr::null_mut())
}
@@ -1178,8 +1218,11 @@ pub unsafe extern "C" fn dc_delete_msgs(
return;
}
let ffi_context = &*context;
let ids = std::slice::from_raw_parts(msg_ids, msg_cnt as usize);
ffi_context
.with_inner(|ctx| message::dc_delete_msgs(ctx, msg_ids, msg_cnt))
.with_inner(|ctx| message::delete_msgs(ctx, ids))
.unwrap_or(())
}
@@ -1198,9 +1241,11 @@ pub unsafe extern "C" fn dc_forward_msgs(
eprintln!("ignoring careless call to dc_forward_msgs()");
return;
}
let ids = std::slice::from_raw_parts(msg_ids, msg_cnt as usize);
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| chat::forward_msgs(ctx, msg_ids, msg_cnt, chat_id))
.with_inner(|ctx| chat::forward_msgs(ctx, ids, chat_id))
.unwrap_or(())
}
@@ -1226,9 +1271,11 @@ pub unsafe extern "C" fn dc_markseen_msgs(
eprintln!("ignoring careless call to dc_markseen_msgs()");
return;
}
let ids = std::slice::from_raw_parts(msg_ids, msg_cnt as usize);
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| message::dc_markseen_msgs(ctx, msg_ids, msg_cnt as usize))
.with_inner(|ctx| message::markseen_msgs(ctx, ids))
.ok();
}
@@ -1243,9 +1290,12 @@ pub unsafe extern "C" fn dc_star_msgs(
eprintln!("ignoring careless call to dc_star_msgs()");
return;
}
let ids = std::slice::from_raw_parts(msg_ids, msg_cnt as usize);
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| message::dc_star_msgs(ctx, msg_ids, msg_cnt, star))
.with_inner(|ctx| message::star_msgs(ctx, ids, star == 1))
.ok();
}
@@ -1258,7 +1308,7 @@ pub unsafe extern "C" fn dc_get_msg(context: *mut dc_context_t, msg_id: u32) ->
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| {
let message = match message::dc_get_msg(ctx, msg_id) {
let message = match message::Message::load_from_db(ctx, msg_id) {
Ok(msg) => msg,
Err(e) => {
error!(ctx, "Error getting msg #{}: {}", msg_id, e);
@@ -1472,15 +1522,23 @@ pub unsafe extern "C" fn dc_imex(
context: *mut dc_context_t,
what: libc::c_int,
param1: *mut libc::c_char,
param2: *mut libc::c_char,
_param2: *mut libc::c_char,
) {
if context.is_null() {
eprintln!("ignoring careless call to dc_imex()");
return;
}
let what = match imex::ImexMode::from_i32(what as i32) {
Some(what) => what,
None => {
eprintln!("ignoring invalid argument {} to dc_imex", what);
return;
}
};
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| dc_imex::dc_imex(ctx, what, as_opt_str(param1), param2))
.with_inner(|ctx| imex::imex(ctx, what, as_opt_str(param1)))
.ok();
}
@@ -1495,7 +1553,13 @@ pub unsafe extern "C" fn dc_imex_has_backup(
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| dc_imex::dc_imex_has_backup(ctx, as_str(dir)))
.with_inner(|ctx| match imex::has_backup(ctx, as_str(dir)) {
Ok(res) => res.strdup(),
Err(err) => {
error!(ctx, "dc_imex_has_backup: {}", err);
ptr::null_mut()
}
})
.unwrap_or_else(|_| ptr::null_mut())
}
@@ -1507,7 +1571,13 @@ pub unsafe extern "C" fn dc_initiate_key_transfer(context: *mut dc_context_t) ->
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| dc_imex::dc_initiate_key_transfer(ctx))
.with_inner(|ctx| match imex::initiate_key_transfer(ctx) {
Ok(res) => res.strdup(),
Err(err) => {
error!(ctx, "dc_initiate_key_transfer(): {}", err);
ptr::null_mut()
}
})
.unwrap_or_else(|_| ptr::null_mut())
}
@@ -1526,7 +1596,15 @@ pub unsafe extern "C" fn dc_continue_key_transfer(
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| dc_imex::dc_continue_key_transfer(ctx, msg_id, setup_code) as libc::c_int)
.with_inner(
|ctx| match imex::continue_key_transfer(ctx, msg_id, as_str(setup_code)) {
Ok(()) => 1,
Err(err) => {
error!(ctx, "dc_continue_key_transfer: {}", err);
0
}
},
)
.unwrap_or(0)
}
@@ -2152,7 +2230,7 @@ pub unsafe extern "C" fn dc_msg_new(
let viewtype = from_prim(viewtype).expect(&format!("invalid viewtype = {}", viewtype));
let msg = MessageWrapper {
context,
message: message::dc_msg_new(viewtype),
message: message::Message::new(viewtype),
};
Box::into_raw(Box::new(msg))
}
@@ -2174,7 +2252,7 @@ pub unsafe extern "C" fn dc_msg_get_id(msg: *mut dc_msg_t) -> u32 {
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_id(&ffi_msg.message)
ffi_msg.message.get_id()
}
#[no_mangle]
@@ -2184,7 +2262,7 @@ pub unsafe extern "C" fn dc_msg_get_from_id(msg: *mut dc_msg_t) -> u32 {
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_from_id(&ffi_msg.message)
ffi_msg.message.get_from_id()
}
#[no_mangle]
@@ -2194,7 +2272,7 @@ pub unsafe extern "C" fn dc_msg_get_chat_id(msg: *mut dc_msg_t) -> u32 {
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_chat_id(&ffi_msg.message)
ffi_msg.message.get_chat_id()
}
#[no_mangle]
@@ -2204,7 +2282,9 @@ pub unsafe extern "C" fn dc_msg_get_viewtype(msg: *mut dc_msg_t) -> libc::c_int
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_viewtype(&ffi_msg.message)
ffi_msg
.message
.get_viewtype()
.to_i64()
.expect("impossible: Viewtype -> i64 conversion failed") as libc::c_int
}
@@ -2216,7 +2296,7 @@ pub unsafe extern "C" fn dc_msg_get_state(msg: *mut dc_msg_t) -> libc::c_int {
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_state(&ffi_msg.message) as libc::c_int
ffi_msg.message.get_state() as libc::c_int
}
#[no_mangle]
@@ -2226,7 +2306,7 @@ pub unsafe extern "C" fn dc_msg_get_timestamp(msg: *mut dc_msg_t) -> i64 {
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_timestamp(&ffi_msg.message)
ffi_msg.message.get_timestamp()
}
#[no_mangle]
@@ -2236,7 +2316,7 @@ pub unsafe extern "C" fn dc_msg_get_received_timestamp(msg: *mut dc_msg_t) -> i6
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_received_timestamp(&ffi_msg.message)
ffi_msg.message.get_received_timestamp()
}
#[no_mangle]
@@ -2246,7 +2326,7 @@ pub unsafe extern "C" fn dc_msg_get_sort_timestamp(msg: *mut dc_msg_t) -> i64 {
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_sort_timestamp(&ffi_msg.message)
ffi_msg.message.get_sort_timestamp()
}
#[no_mangle]
@@ -2256,7 +2336,7 @@ pub unsafe extern "C" fn dc_msg_get_text(msg: *mut dc_msg_t) -> *mut libc::c_cha
return dc_strdup(ptr::null());
}
let ffi_msg = &*msg;
message::dc_msg_get_text(&ffi_msg.message)
ffi_msg.message.get_text().unwrap_or_default().strdup()
}
#[no_mangle]
@@ -2269,7 +2349,9 @@ pub unsafe extern "C" fn dc_msg_get_file(msg: *mut dc_msg_t) -> *mut libc::c_cha
let ffi_context = &*ffi_msg.context;
ffi_context
.with_inner(|ctx| {
message::dc_msg_get_file(ctx, &ffi_msg.message)
ffi_msg
.message
.get_file(ctx)
.and_then(|p| p.to_c_string().ok())
.map(|cs| dc_strdup(cs.as_ptr()))
.unwrap_or_else(|| "".strdup())
@@ -2284,7 +2366,7 @@ pub unsafe extern "C" fn dc_msg_get_filename(msg: *mut dc_msg_t) -> *mut libc::c
return dc_strdup(ptr::null());
}
let ffi_msg = &*msg;
message::dc_msg_get_filename(&ffi_msg.message)
ffi_msg.message.get_filename().unwrap_or_default().strdup()
}
#[no_mangle]
@@ -2294,7 +2376,11 @@ pub unsafe extern "C" fn dc_msg_get_filemime(msg: *mut dc_msg_t) -> *mut libc::c
return dc_strdup(ptr::null());
}
let ffi_msg = &*msg;
message::dc_msg_get_filemime(&ffi_msg.message).strdup()
if let Some(x) = ffi_msg.message.get_filemime() {
x.strdup()
} else {
return dc_strdup(ptr::null());
}
}
#[no_mangle]
@@ -2306,7 +2392,7 @@ pub unsafe extern "C" fn dc_msg_get_filebytes(msg: *mut dc_msg_t) -> u64 {
let ffi_msg = &*msg;
let ffi_context = &*ffi_msg.context;
ffi_context
.with_inner(|ctx| message::dc_msg_get_filebytes(ctx, &ffi_msg.message))
.with_inner(|ctx| ffi_msg.message.get_filebytes(ctx))
.unwrap_or(0)
}
@@ -2317,7 +2403,7 @@ pub unsafe extern "C" fn dc_msg_get_width(msg: *mut dc_msg_t) -> libc::c_int {
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_width(&ffi_msg.message)
ffi_msg.message.get_width()
}
#[no_mangle]
@@ -2327,7 +2413,7 @@ pub unsafe extern "C" fn dc_msg_get_height(msg: *mut dc_msg_t) -> libc::c_int {
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_height(&ffi_msg.message)
ffi_msg.message.get_height()
}
#[no_mangle]
@@ -2337,7 +2423,7 @@ pub unsafe extern "C" fn dc_msg_get_duration(msg: *mut dc_msg_t) -> libc::c_int
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_duration(&ffi_msg.message)
ffi_msg.message.get_duration()
}
#[no_mangle]
@@ -2347,7 +2433,7 @@ pub unsafe extern "C" fn dc_msg_get_showpadlock(msg: *mut dc_msg_t) -> libc::c_i
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_get_showpadlock(&ffi_msg.message) as libc::c_int
ffi_msg.message.get_showpadlock() as libc::c_int
}
#[no_mangle]
@@ -2369,7 +2455,7 @@ pub unsafe extern "C" fn dc_msg_get_summary(
let ffi_context = &*ffi_msg.context;
ffi_context
.with_inner(|ctx| {
let lot = message::dc_msg_get_summary(ctx, &mut ffi_msg.message, maybe_chat);
let lot = ffi_msg.message.get_summary(ctx, maybe_chat);
Box::into_raw(Box::new(lot))
})
.unwrap_or_else(|_| ptr::null_mut())
@@ -2388,13 +2474,12 @@ pub unsafe extern "C" fn dc_msg_get_summarytext(
let ffi_context = &*ffi_msg.context;
ffi_context
.with_inner(|ctx| {
message::dc_msg_get_summarytext(
ctx,
&mut ffi_msg.message,
approx_characters.try_into().unwrap(),
)
ffi_msg
.message
.get_summarytext(ctx, approx_characters.try_into().unwrap())
})
.unwrap_or_else(|_| "".strdup())
.unwrap_or_default()
.strdup()
}
#[no_mangle]
@@ -2404,7 +2489,7 @@ pub unsafe extern "C" fn dc_msg_has_deviating_timestamp(msg: *mut dc_msg_t) -> l
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_has_deviating_timestamp(&ffi_msg.message)
ffi_msg.message.has_deviating_timestamp().into()
}
#[no_mangle]
@@ -2414,7 +2499,7 @@ pub unsafe extern "C" fn dc_msg_has_location(msg: *mut dc_msg_t) -> libc::c_int
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_has_location(&ffi_msg.message) as libc::c_int
ffi_msg.message.has_location() as libc::c_int
}
#[no_mangle]
@@ -2424,7 +2509,7 @@ pub unsafe extern "C" fn dc_msg_is_sent(msg: *mut dc_msg_t) -> libc::c_int {
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_is_sent(&ffi_msg.message).into()
ffi_msg.message.is_sent().into()
}
#[no_mangle]
@@ -2434,7 +2519,7 @@ pub unsafe extern "C" fn dc_msg_is_starred(msg: *mut dc_msg_t) -> libc::c_int {
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_is_starred(&ffi_msg.message).into()
ffi_msg.message.is_starred().into()
}
#[no_mangle]
@@ -2444,7 +2529,7 @@ pub unsafe extern "C" fn dc_msg_is_forwarded(msg: *mut dc_msg_t) -> libc::c_int
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_is_forwarded(&ffi_msg.message).into()
ffi_msg.message.is_forwarded().into()
}
#[no_mangle]
@@ -2454,7 +2539,7 @@ pub unsafe extern "C" fn dc_msg_is_info(msg: *mut dc_msg_t) -> libc::c_int {
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_is_info(&ffi_msg.message).into()
ffi_msg.message.is_info().into()
}
#[no_mangle]
@@ -2464,7 +2549,7 @@ pub unsafe extern "C" fn dc_msg_is_increation(msg: *mut dc_msg_t) -> libc::c_int
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_is_increation(&ffi_msg.message).into()
ffi_msg.message.is_increation().into()
}
#[no_mangle]
@@ -2474,7 +2559,7 @@ pub unsafe extern "C" fn dc_msg_is_setupmessage(msg: *mut dc_msg_t) -> libc::c_i
return 0;
}
let ffi_msg = &*msg;
message::dc_msg_is_setupmessage(&ffi_msg.message).into()
ffi_msg.message.is_setupmessage().into()
}
#[no_mangle]
@@ -2486,8 +2571,9 @@ pub unsafe extern "C" fn dc_msg_get_setupcodebegin(msg: *mut dc_msg_t) -> *mut l
let ffi_msg = &*msg;
let ffi_context = &*ffi_msg.context;
ffi_context
.with_inner(|ctx| message::dc_msg_get_setupcodebegin(ctx, &ffi_msg.message))
.unwrap_or_else(|_| "".strdup())
.with_inner(|ctx| ffi_msg.message.get_setupcodebegin(ctx).unwrap_or_default())
.unwrap_or_default()
.strdup()
}
#[no_mangle]
@@ -2498,7 +2584,7 @@ pub unsafe extern "C" fn dc_msg_set_text(msg: *mut dc_msg_t, text: *mut libc::c_
}
let ffi_msg = &mut *msg;
// TODO: {text} equal to NULL is treated as "", which is strange. Does anyone rely on it?
message::dc_msg_set_text(&mut ffi_msg.message, text)
ffi_msg.message.set_text(as_opt_str(text).map(Into::into))
}
#[no_mangle]
@@ -2507,12 +2593,12 @@ pub unsafe extern "C" fn dc_msg_set_file(
file: *mut libc::c_char,
filemime: *mut libc::c_char,
) {
if msg.is_null() {
if msg.is_null() || file.is_null() {
eprintln!("ignoring careless call to dc_msg_set_file()");
return;
}
let ffi_msg = &mut *msg;
message::dc_msg_set_file(&mut ffi_msg.message, file, filemime)
ffi_msg.message.set_file(as_str(file), as_opt_str(filemime))
}
#[no_mangle]
@@ -2526,7 +2612,7 @@ pub unsafe extern "C" fn dc_msg_set_dimension(
return;
}
let ffi_msg = &mut *msg;
message::dc_msg_set_dimension(&mut ffi_msg.message, width, height)
ffi_msg.message.set_dimension(width, height)
}
#[no_mangle]
@@ -2536,7 +2622,7 @@ pub unsafe extern "C" fn dc_msg_set_duration(msg: *mut dc_msg_t, duration: libc:
return;
}
let ffi_msg = &mut *msg;
message::dc_msg_set_duration(&mut ffi_msg.message, duration)
ffi_msg.message.set_duration(duration)
}
#[no_mangle]
@@ -2550,7 +2636,7 @@ pub unsafe extern "C" fn dc_msg_set_location(
return;
}
let ffi_msg = &mut *msg;
message::dc_msg_set_location(&mut ffi_msg.message, latitude, longitude)
ffi_msg.message.set_location(latitude, longitude)
}
#[no_mangle]
@@ -2568,7 +2654,9 @@ pub unsafe extern "C" fn dc_msg_latefiling_mediasize(
let ffi_context = &*ffi_msg.context;
ffi_context
.with_inner(|ctx| {
message::dc_msg_latefiling_mediasize(ctx, &mut ffi_msg.message, width, height, duration)
ffi_msg
.message
.latefiling_mediasize(ctx, width, height, duration)
})
.ok();
}
@@ -2810,7 +2898,7 @@ fn as_opt_str<'a>(s: *const libc::c_char) -> Option<&'a str> {
return None;
}
Some(dc_tools::as_str(s))
Some(as_str(s))
}
pub mod providers;

View File

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

View File

@@ -1,5 +1,4 @@
use std::ffi::CString;
use std::ptr;
use std::path::Path;
use std::str::FromStr;
use deltachat::chat::{self, Chat};
@@ -9,19 +8,19 @@ use deltachat::configure::*;
use deltachat::constants::*;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::dc_imex::*;
use deltachat::dc_receive_imf::*;
use deltachat::dc_tools::*;
use deltachat::error::Error;
use deltachat::imex::*;
use deltachat::job::*;
use deltachat::location;
use deltachat::lot::LotState;
use deltachat::message::*;
use deltachat::message::{self, Message, MessageState};
use deltachat::peerstate::*;
use deltachat::qr::*;
use deltachat::sql;
use deltachat::x::*;
use deltachat::Event;
use libc::free;
/// Reset database tables. This function is called from Core cmdline.
/// Argument is a bitmask, executing single or multiple actions in one call.
@@ -94,170 +93,128 @@ pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
1
}
unsafe fn dc_poke_eml_file(context: &Context, filename: *const libc::c_char) -> libc::c_int {
/* mainly for testing, may be called by dc_import_spec() */
let mut success: libc::c_int = 0i32;
let mut data: *mut libc::c_char = ptr::null_mut();
let mut data_bytes = 0;
if !(dc_read_file(
context,
filename,
&mut data as *mut *mut libc::c_char as *mut *mut libc::c_void,
&mut data_bytes,
) == 0i32)
{
dc_receive_imf(context, data, data_bytes, "import", 0, 0);
success = 1;
}
free(data as *mut libc::c_void);
fn dc_poke_eml_file(context: &Context, filename: impl AsRef<Path>) -> Result<(), Error> {
let data = dc_read_file(context, filename)?;
success
unsafe { dc_receive_imf(context, &data, "import", 0, 0) };
Ok(())
}
/// Import a file to the database.
/// For testing, import a folder with eml-files, a single eml-file, e-mail plus public key and so on.
/// For normal importing, use dc_imex().
/// For normal importing, use imex().
///
/// @private @memberof Context
/// @param context The context as created by dc_context_new().
/// @param spec The file or directory to import. NULL for the last command.
/// @return 1=success, 0=error.
unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
if !context.sql.is_open() {
error!(context, "Import: Database not opened.");
return 0;
}
let ok_to_continue;
let mut success: libc::c_int = 0;
let real_spec: *mut libc::c_char;
let mut suffix: *mut libc::c_char = ptr::null_mut();
let mut read_cnt: libc::c_int = 0;
let real_spec: String;
let mut read_cnt = 0;
/* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
if !spec.is_null() {
real_spec = dc_strdup(spec);
real_spec = to_string(spec);
context
.sql
.set_config(context, "import_spec", Some(as_str(real_spec)))
.set_config(context, "import_spec", Some(&real_spec))
.unwrap();
ok_to_continue = true;
} else {
let rs = context.sql.get_config(context, "import_spec");
if rs.is_none() {
error!(context, "Import: No file or folder given.");
ok_to_continue = false;
} else {
ok_to_continue = true;
return 0;
}
real_spec = rs.unwrap_or_default().strdup();
real_spec = rs.unwrap();
}
if ok_to_continue {
let ok_to_continue2;
suffix = dc_get_filesuffix_lc(as_str(real_spec));
if !suffix.is_null() && strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0
{
if 0 != dc_poke_eml_file(context, real_spec) {
if let Some(suffix) = dc_get_filesuffix_lc(&real_spec) {
if suffix == "eml" {
if dc_poke_eml_file(context, &real_spec).is_ok() {
read_cnt += 1
}
ok_to_continue2 = true;
}
} else {
/* import a directory */
let dir_name = std::path::Path::new(&real_spec);
let dir = std::fs::read_dir(dir_name);
if dir.is_err() {
error!(context, "Import: Cannot open directory \"{}\".", &real_spec,);
return 0;
} else {
/* import a directory */
let dir_name = std::path::Path::new(as_str(real_spec));
let dir = std::fs::read_dir(dir_name);
if dir.is_err() {
error!(
context,
"Import: Cannot open directory \"{}\".",
as_str(real_spec),
);
ok_to_continue2 = false;
} else {
let dir = dir.unwrap();
for entry in dir {
if entry.is_err() {
break;
}
let entry = entry.unwrap();
let name_f = entry.file_name();
let name = name_f.to_string_lossy();
if name.ends_with(".eml") {
let path_plus_name = format!("{}/{}", as_str(real_spec), name);
info!(context, "Import: {}", path_plus_name);
let path_plus_name_c = CString::yolo(path_plus_name);
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
read_cnt += 1
}
let dir = dir.unwrap();
for entry in dir {
if entry.is_err() {
break;
}
let entry = entry.unwrap();
let name_f = entry.file_name();
let name = name_f.to_string_lossy();
if name.ends_with(".eml") {
let path_plus_name = format!("{}/{}", &real_spec, name);
info!(context, "Import: {}", path_plus_name);
if dc_poke_eml_file(context, path_plus_name).is_ok() {
read_cnt += 1
}
}
ok_to_continue2 = true;
}
}
if ok_to_continue2 {
info!(
context,
"Import: {} items read from \"{}\".",
read_cnt,
as_str(real_spec)
);
if read_cnt > 0 {
context.call_cb(Event::MsgsChanged {
chat_id: 0,
msg_id: 0,
});
}
success = 1
}
}
free(real_spec as *mut libc::c_void);
free(suffix as *mut libc::c_void);
success
info!(
context,
"Import: {} items read from \"{}\".", read_cnt, &real_spec
);
if read_cnt > 0 {
context.call_cb(Event::MsgsChanged {
chat_id: 0,
msg_id: 0,
});
}
1
}
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
let contact = Contact::get_by_id(context, dc_msg_get_from_id(msg)).expect("invalid contact");
let contact = Contact::get_by_id(context, msg.get_from_id()).expect("invalid contact");
let contact_name = contact.get_name();
let contact_id = contact.get_id();
let statestr = match dc_msg_get_state(msg) {
let statestr = match msg.get_state() {
MessageState::OutPending => " o",
MessageState::OutDelivered => "",
MessageState::OutMdnRcvd => " √√",
MessageState::OutFailed => " !!",
_ => "",
};
let temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
let msgtext = dc_msg_get_text(msg);
let temp2 = dc_timestamp_to_str(msg.get_timestamp());
let msgtext = msg.get_text();
info!(
context,
"{}#{}{}{}: {} (Contact#{}): {} {}{}{}{} [{}]",
prefix.as_ref(),
dc_msg_get_id(msg) as libc::c_int,
if dc_msg_get_showpadlock(msg) {
"🔒"
} else {
""
},
if dc_msg_has_location(msg) { "📍" } else { "" },
msg.get_id() as libc::c_int,
if msg.get_showpadlock() { "🔒" } else { "" },
if msg.has_location() { "📍" } else { "" },
&contact_name,
contact_id,
as_str(msgtext),
if dc_msg_is_starred(msg) { "" } else { "" },
if dc_msg_get_from_id(msg) == 1 as libc::c_uint {
msgtext.unwrap_or_default(),
if msg.is_starred() { "" } else { "" },
if msg.get_from_id() == 1 as libc::c_uint {
""
} else if dc_msg_get_state(msg) == MessageState::InSeen {
} else if msg.get_state() == MessageState::InSeen {
"[SEEN]"
} else if dc_msg_get_state(msg) == MessageState::InNoticed {
} else if msg.get_state() == MessageState::InNoticed {
"[NOTICED]"
} else {
"[FRESH]"
},
if dc_msg_is_info(msg) { "[INFO]" } else { "" },
if msg.is_info() { "[INFO]" } else { "" },
statestr,
&temp2,
);
free(msgtext as *mut libc::c_void);
}
unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error> {
@@ -278,7 +235,7 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
);
lines_out += 1
}
let msg = dc_get_msg(context, msg_id)?;
let msg = Message::load_from_db(context, msg_id)?;
log_msg(context, "Msg", &msg);
}
}
@@ -360,12 +317,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
arg1.strdup() as *const _
};
let arg2 = args.next().unwrap_or_default();
let arg2_c = if arg2.is_empty() {
std::ptr::null()
} else {
arg2.strdup() as *const _
};
let blobdir = context.get_blobdir();
match arg0 {
"help" | "?" => match arg1 {
// TODO: reuse commands definition in main.rs.
@@ -452,30 +405,24 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
============================================="
),
},
"initiate-key-transfer" => {
let setup_code = dc_initiate_key_transfer(context);
if !setup_code.is_null() {
println!(
"Setup code for the transferred setup message: {}",
as_str(setup_code),
);
free(setup_code as *mut libc::c_void);
} else {
bail!("Failed to generate setup code");
};
}
"initiate-key-transfer" => match initiate_key_transfer(context) {
Ok(setup_code) => println!(
"Setup code for the transferred setup message: {}",
setup_code,
),
Err(err) => bail!("Failed to generate setup code: {}", err),
},
"get-setupcodebegin" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let msg_id: u32 = arg1.parse()?;
let msg = dc_get_msg(context, msg_id)?;
if dc_msg_is_setupmessage(&msg) {
let setupcodebegin = dc_msg_get_setupcodebegin(context, &msg);
let msg = Message::load_from_db(context, msg_id)?;
if msg.is_setupmessage() {
let setupcodebegin = msg.get_setupcodebegin(context);
println!(
"The setup code for setup message Msg#{} starts with: {}",
msg_id,
as_str(setupcodebegin),
setupcodebegin.unwrap_or_default(),
);
free(setupcodebegin as *mut libc::c_void);
} else {
bail!("Msg#{} is no setup message.", msg_id,);
}
@@ -485,33 +432,28 @@ 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 !dc_continue_key_transfer(context, arg1.parse()?, arg2_c) {
bail!("Continue key transfer failed");
}
continue_key_transfer(context, arg1.parse()?, &arg2)?;
}
"has-backup" => {
let ret = dc_imex_has_backup(context, context.get_blobdir());
if ret.is_null() {
println!("No backup found.");
}
has_backup(context, blobdir)?;
}
"export-backup" => {
dc_imex(context, 11, Some(context.get_blobdir()), ptr::null());
imex(context, ImexMode::ExportBackup, Some(blobdir));
}
"import-backup" => {
ensure!(!arg1.is_empty(), "Argument <backup-file> missing.");
dc_imex(context, 12, Some(arg1), ptr::null());
imex(context, ImexMode::ImportBackup, Some(arg1));
}
"export-keys" => {
dc_imex(context, 1, Some(context.get_blobdir()), ptr::null());
imex(context, ImexMode::ExportSelfKeys, Some(blobdir));
}
"import-keys" => {
dc_imex(context, 2, Some(context.get_blobdir()), ptr::null());
imex(context, ImexMode::ImportSelfKeys, Some(blobdir));
}
"export-setup" => {
let setup_code = dc_create_setup_code(context);
let file_name = context.get_blobdir().join("autocrypt-setup-message.html");
let file_content = dc_render_setup_file(context, &setup_code)?;
let setup_code = create_setup_code(context);
let file_name = blobdir.join("autocrypt-setup-message.html");
let file_content = render_setup_file(context, &setup_code)?;
std::fs::write(&file_name, file_content)?;
println!(
"Setup message written to: {}\nSetup code: {}",
@@ -825,14 +767,14 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!arg1.is_empty(), "No file given.");
let mut msg = dc_msg_new(if arg0 == "sendimage" {
let mut msg = Message::new(if arg0 == "sendimage" {
Viewtype::Image
} else {
Viewtype::File
});
dc_msg_set_file(&mut msg, arg1_c, ptr::null());
msg.set_file(arg1, None);
if !arg2.is_empty() {
dc_msg_set_text(&mut msg, arg2_c);
msg.set_text(Some(arg2.to_string()));
}
chat::send_msg(context, sel_chat.as_ref().unwrap().get_id(), &mut msg)?;
}
@@ -854,8 +796,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(sel_chat.is_some(), "No chat selected.");
if !arg1.is_empty() {
let mut draft = dc_msg_new(Viewtype::Text);
dc_msg_set_text(&mut draft, arg1_c);
let mut draft = Message::new(Viewtype::Text);
draft.set_text(Some(arg1.to_string()));
chat::set_draft(
context,
sel_chat.as_ref().unwrap().get_id(),
@@ -904,8 +846,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"msginfo" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let id = arg1.parse()?;
let res = dc_get_msg_info(context, id);
println!("{}", as_str(res));
let res = message::get_msg_info(context, id);
println!("{}", res);
}
"listfresh" => {
let msglist = context.get_fresh_msgs();
@@ -922,30 +864,25 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let mut msg_ids = [0; 1];
let chat_id = arg2.parse()?;
msg_ids[0] = arg1.parse()?;
chat::forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
chat::forward_msgs(context, &msg_ids, chat_id);
}
"markseen" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut msg_ids = [0; 1];
msg_ids[0] = arg1.parse()?;
dc_markseen_msgs(context, msg_ids.as_mut_ptr(), 1);
message::markseen_msgs(context, &msg_ids);
}
"star" | "unstar" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut msg_ids = [0; 1];
msg_ids[0] = arg1.parse()?;
dc_star_msgs(
context,
msg_ids.as_mut_ptr(),
1,
if arg0 == "star" { 1 } else { 0 },
);
message::star_msgs(context, &msg_ids, arg0 == "star");
}
"delmsg" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut ids = [0; 1];
ids[0] = arg1.parse()?;
dc_delete_msgs(context, ids.as_mut_ptr(), 1);
message::delete_msgs(context, &ids);
}
"listcontacts" | "contacts" | "listverified" => {
let contacts = Contact::get_all(
@@ -1028,7 +965,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"fileinfo" => {
ensure!(!arg1.is_empty(), "Argument <file> missing.");
if let Some(buf) = dc_read_file_safe(context, &arg1) {
if let Ok(buf) = dc_read_file(context, &arg1) {
let (width, height) = dc_get_filemeta(&buf)?;
println!("width={}, height={}", width, height);
} else {
@@ -1040,7 +977,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
free(arg1_c as *mut _);
free(arg2_c as *mut _);
Ok(())
}

View File

@@ -23,11 +23,9 @@ use std::sync::{Arc, Mutex, RwLock};
use deltachat::config;
use deltachat::configure::*;
use deltachat::context::*;
use deltachat::dc_tools::*;
use deltachat::job::*;
use deltachat::oauth2::*;
use deltachat::securejoin::*;
use deltachat::x::*;
use deltachat::Event;
use rustyline::completion::{Completer, FilenameCompleter, Pair};
use rustyline::config::OutputStreamType;
@@ -441,11 +439,6 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
let mut args = line.splitn(2, ' ');
let arg0 = args.next().unwrap_or_default();
let arg1 = args.next().unwrap_or_default();
let arg1_c = if arg1.is_empty() {
std::ptr::null()
} else {
arg1.strdup()
};
match arg0 {
"connect" => {
@@ -521,8 +514,6 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
_ => dc_cmdline(&ctx.read().unwrap(), line)?,
}
free(arg1_c as *mut _);
Ok(ExitResult::Continue)
}

View File

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

23
mmime/Cargo.toml Normal file
View File

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

201
mmime/LICENSE-APACHE Normal file
View File

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

23
mmime/LICENSE-MIT Normal file
View File

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

4
mmime/LICENSE.md Normal file
View File

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

16
mmime/README.md Normal file
View File

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

32
mmime/src/charconv.rs Normal file
View File

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

427
mmime/src/chash.rs Normal file
View File

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

202
mmime/src/clist.rs Normal file
View File

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

71
mmime/src/constants.rs Normal file
View File

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

386
mmime/src/display.rs Normal file
View File

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

78
mmime/src/lib.rs Normal file
View File

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

5921
mmime/src/mailimf/mod.rs Normal file

File diff suppressed because it is too large Load Diff

1181
mmime/src/mailimf/types.rs Normal file

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

1143
mmime/src/mailmime/mod.rs Normal file

File diff suppressed because it is too large Load Diff

869
mmime/src/mailmime/types.rs Normal file
View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

397
mmime/src/mmapstring.rs Normal file
View File

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

1707
mmime/src/other.rs Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -6,7 +6,7 @@
import os
import subprocess
import os
import sys
if __name__ == "__main__":
os.environ["DCC_RS_TARGET"] = target = "release"
@@ -21,5 +21,5 @@ if __name__ == "__main__":
subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True)
subprocess.check_call([
"pip", "install", "-e", "."
sys.executable, "-m", "pip", "install", "-e", "."
])

View File

@@ -2,7 +2,6 @@
from __future__ import print_function
import threading
import os
import re
import time
from array import array
@@ -23,7 +22,7 @@ class Account(object):
by the underlying deltachat c-library. All public Account methods are
meant to be memory-safe and return memory-safe objects.
"""
def __init__(self, db_path, logid=None, eventlogging=True):
def __init__(self, db_path, logid=None, eventlogging=True, debug=True):
""" initialize account object.
:param db_path: a path to the account database. The database
@@ -31,13 +30,14 @@ class Account(object):
:param logid: an optional logging prefix that should be used with
the default internal logging.
:param eventlogging: if False no eventlogging and no context callback will be configured
:param debug: turn on debug logging for events.
"""
self._dc_context = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
_destroy_dc_context,
)
if eventlogging:
self._evlogger = EventLogger(self._dc_context, logid)
self._evlogger = EventLogger(self._dc_context, logid, debug)
deltachat.set_context_callback(self._dc_context, self._process_event)
self._threads = IOThreads(self._dc_context, self._evlogger._log_event)
else:
@@ -48,7 +48,7 @@ class Account(object):
if not lib.dc_open(self._dc_context, db_path, ffi.NULL):
raise ValueError("Could not dc_open: {}".format(db_path))
self._configkeys = self.get_config("sys.config_keys").split()
self._imex_completed = threading.Event()
self._imex_events = Queue()
def __del__(self):
self.shutdown()
@@ -231,7 +231,7 @@ class Account(object):
:returns: a :class:`deltachat.chatting.Chat` object.
"""
bytes_name = name.encode("utf8")
chat_id = lib.dc_create_group_chat(self._dc_context, verified, bytes_name)
chat_id = lib.dc_create_group_chat(self._dc_context, int(verified), bytes_name)
return Chat(self, chat_id)
def get_chats(self):
@@ -289,31 +289,64 @@ class Account(object):
msg_ids = [msg.id for msg in messages]
lib.dc_delete_msgs(self._dc_context, msg_ids, len(msg_ids))
def export_to_dir(self, backupdir):
"""return after all delta chat state is exported to a new file in
the specified directory.
def export_self_keys(self, path):
""" export public and private keys to the specified directory. """
return self._export(path, imex_cmd=1)
def export_all(self, path):
"""return new file containing a backup of all database state
(chats, contacts, keys, media, ...). The file is created in the
the `path` directory.
"""
snap_files = os.listdir(backupdir)
self._imex_completed.clear()
lib.dc_imex(self._dc_context, 11, as_dc_charpointer(backupdir), ffi.NULL)
export_files = self._export(path, 11)
if len(export_files) != 1:
raise RuntimeError("found more than one new file")
return export_files[0]
def _imex_events_clear(self):
try:
while True:
self._imex_events.get_nowait()
except Empty:
pass
def _export(self, path, imex_cmd):
self._imex_events_clear()
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
if not self._threads.is_started():
lib.dc_perform_imap_jobs(self._dc_context)
self._imex_completed.wait()
for x in os.listdir(backupdir):
if x not in snap_files:
return os.path.join(backupdir, x)
files_written = []
while True:
ev = self._imex_events.get()
if isinstance(ev, str):
files_written.append(ev)
elif isinstance(ev, bool):
if not ev:
raise ValueError("export failed, exp-files: {}".format(files_written))
return files_written
def import_from_file(self, path):
"""import delta chat state from the specified backup file.
def import_self_keys(self, path):
""" Import private keys found in the `path` directory.
The last imported key is made the default keys unless its name
contains the string legacy. Public keys are not imported.
"""
self._import(path, imex_cmd=2)
def import_all(self, path):
"""import delta chat state from the specified backup `path` (a file).
The account must be in unconfigured state for import to attempted.
"""
assert not self.is_configured(), "cannot import into configured account"
self._imex_completed.clear()
lib.dc_imex(self._dc_context, 12, as_dc_charpointer(path), ffi.NULL)
self._import(path, imex_cmd=12)
def _import(self, path, imex_cmd):
self._imex_events_clear()
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
if not self._threads.is_started():
lib.dc_perform_imap_jobs(self._dc_context)
self._imex_completed.wait()
if not self._imex_events.get():
raise ValueError("import from path '{}' failed".format(path))
def initiate_key_transfer(self):
"""return setup code after a Autocrypt setup message
@@ -378,7 +411,16 @@ class Account(object):
raise ValueError("could not join group")
return Chat(self, chat_id)
def start_threads(self):
#
# meta API for start/stop and event based processing
#
def wait_next_incoming_message(self):
""" wait for and return next incoming message. """
ev = self._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
return self.get_message_by_id(ev[2])
def start_threads(self, mvbox=False, sentbox=False):
""" start IMAP/SMTP threads (and configure account if it hasn't happened).
:raises: ValueError if 'addr' or 'mail_pw' are not configured.
@@ -386,7 +428,7 @@ class Account(object):
"""
if not self.is_configured():
self.configure()
self._threads.start()
self._threads.start(mvbox=mvbox, sentbox=sentbox)
def stop_threads(self, wait=True):
""" stop IMAP/SMTP threads. """
@@ -414,7 +456,12 @@ class Account(object):
def on_dc_event_imex_progress(self, data1, data2):
if data1 == 1000:
self._imex_completed.set()
self._imex_events.put(True)
elif data1 == 0:
self._imex_events.put(False)
def on_dc_event_imex_file_written(self, data1, data2):
self._imex_events.put(data1)
class IOThreads:
@@ -427,10 +474,14 @@ class IOThreads:
def is_started(self):
return len(self._name2thread) > 0
def start(self, imap=True, smtp=True):
def start(self, imap=True, smtp=True, mvbox=False, sentbox=False):
assert not self.is_started()
if imap:
self._start_one_thread("imap", self.imap_thread_run)
self._start_one_thread("inbox", self.imap_thread_run)
if mvbox:
self._start_one_thread("mvbox", self.mvbox_thread_run)
if sentbox:
self._start_one_thread("sentbox", self.sentbox_thread_run)
if smtp:
self._start_one_thread("smtp", self.smtp_thread_run)
@@ -443,17 +494,35 @@ class IOThreads:
self._thread_quitflag = True
lib.dc_interrupt_imap_idle(self._dc_context)
lib.dc_interrupt_smtp_idle(self._dc_context)
lib.dc_interrupt_mvbox_idle(self._dc_context)
lib.dc_interrupt_sentbox_idle(self._dc_context)
if wait:
for name, thread in self._name2thread.items():
thread.join()
def imap_thread_run(self):
self._log_event("py-bindings-info", 0, "IMAP THREAD START")
self._log_event("py-bindings-info", 0, "INBOX THREAD START")
while not self._thread_quitflag:
lib.dc_perform_imap_jobs(self._dc_context)
lib.dc_perform_imap_fetch(self._dc_context)
lib.dc_perform_imap_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "IMAP THREAD FINISHED")
self._log_event("py-bindings-info", 0, "INBOX THREAD FINISHED")
def mvbox_thread_run(self):
self._log_event("py-bindings-info", 0, "MVBOX THREAD START")
while not self._thread_quitflag:
lib.dc_perform_mvbox_jobs(self._dc_context)
lib.dc_perform_mvbox_fetch(self._dc_context)
lib.dc_perform_mvbox_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "MVBOX THREAD FINISHED")
def sentbox_thread_run(self):
self._log_event("py-bindings-info", 0, "SENTBOX THREAD START")
while not self._thread_quitflag:
lib.dc_perform_sentbox_jobs(self._dc_context)
lib.dc_perform_sentbox_fetch(self._dc_context)
lib.dc_perform_sentbox_idle(self._dc_context)
self._log_event("py-bindings-info", 0, "SENTBOX THREAD FINISHED")
def smtp_thread_run(self):
self._log_event("py-bindings-info", 0, "SMTP THREAD START")

View File

@@ -109,6 +109,13 @@ class Chat(object):
"""
return not lib.dc_chat_is_unpromoted(self._dc_chat)
def is_verified(self):
""" return True if this chat is a verified group.
:returns: True if chat is verified, False otherwise.
"""
return lib.dc_chat_is_verified(self._dc_chat)
def get_name(self):
""" return name of this chat.

View File

@@ -101,6 +101,14 @@ class Message(object):
""" return True if this message is a setup message. """
return lib.dc_msg_is_setupmessage(self._dc_msg)
def get_setupcodebegin(self):
""" return the first characters of a setup code in a setup message. """
return from_dc_charpointer(lib.dc_msg_get_setupcodebegin(self._dc_msg))
def is_encrypted(self):
""" return True if this message was encrypted. """
return bool(lib.dc_msg_get_showpadlock(self._dc_msg))
def get_message_info(self):
""" Return informational text for a single message.

View File

@@ -150,7 +150,7 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
lib.dc_set_config(ac._dc_context, b"configured", b"1")
return ac
def get_online_configuring_account(self):
def get_online_config(self):
if not session_liveconfig:
pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")
configdict = session_liveconfig.get(self.live_count)
@@ -161,8 +161,12 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count))
ac._evlogger.init_time = self.init_time
ac._evlogger.set_timeout(30)
return ac, dict(configdict)
def get_online_configuring_account(self, mvbox=False, sentbox=False):
ac, configdict = self.get_online_config()
ac.configure(**configdict)
ac.start_threads()
ac.start_threads(mvbox=mvbox, sentbox=sentbox)
return ac
def get_two_online_accounts(self):
@@ -206,12 +210,13 @@ def lp():
return Printer()
def wait_configuration_progress(account, target):
def wait_configuration_progress(account, min_target, max_target=1001):
min_target = min(min_target, max_target)
while 1:
evt_name, data1, data2 = \
account._evlogger.get_matching("DC_EVENT_CONFIGURE_PROGRESS")
if data1 >= target:
print("** CONFIG PROGRESS {}".format(target), account)
if data1 >= min_target and data1 <= max_target:
print("** CONFIG PROGRESS {}".format(min_target), account)
break

View File

@@ -93,8 +93,9 @@ class TestOfflineContact:
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact(email="some1@example.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
chat.send_text("one messae")
msg = chat.send_text("one messae")
assert not ac1.delete_contact(contact1)
assert not msg.filemime
class TestOfflineChat:
@@ -106,7 +107,7 @@ class TestOfflineChat:
def chat1(self, ac1):
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL, chat.id
assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL, chat.id
return chat
def test_display(self, chat1):
@@ -293,10 +294,10 @@ class TestOfflineChat:
assert contact == ac1.get_self_contact()
assert not backupdir.listdir()
path = ac1.export_to_dir(backupdir.strpath)
path = ac1.export_all(backupdir.strpath)
assert os.path.exists(path)
ac2 = acfactory.get_unconfigured_account()
ac2.import_from_file(path)
ac2.import_all(path)
contacts = ac2.get_contacts(query="some1")
assert len(contacts) == 1
contact2 = contacts[0]
@@ -337,14 +338,24 @@ class TestOnlineAccount:
def get_chat(self, ac1, ac2):
c2 = ac1.create_contact(email=ac2.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL
return chat
def test_export_import_self_keys(self, acfactory, tmpdir):
ac1, ac2 = acfactory.get_two_online_accounts()
dir = tmpdir.mkdir("exportdir")
export_files = ac1.export_self_keys(dir.strpath)
assert len(export_files) == 2
for x in export_files:
assert x.startswith(dir.strpath)
ac1._evlogger.consume_events()
ac1.import_self_keys(dir.strpath)
def test_one_account_send(self, acfactory):
ac1 = acfactory.get_online_configuring_account()
c2 = ac1.create_contact(email=ac1.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL
wait_successful_IMAP_SMTP_connection(ac1)
wait_configuration_progress(ac1, 1000)
@@ -353,17 +364,15 @@ class TestOnlineAccount:
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[1] == msg_out.id
def test_two_accounts_send_receive(self, acfactory):
ac1, ac2 = acfactory.get_two_online_accounts()
def test_mvbox_sentbox_threads(self, acfactory):
ac1 = acfactory.get_online_configuring_account(mvbox=True, sentbox=True)
ac2 = acfactory.get_online_configuring_account()
wait_configuration_progress(ac2, 1000)
wait_configuration_progress(ac1, 1000)
chat = self.get_chat(ac1, ac2)
msg_out = chat.send_text("message1")
# wait for other account to receive
chat.send_text("message1")
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
msg_in = ac2.get_message_by_id(msg_out.id)
assert msg_in.text == "message1"
assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL
def test_forward_messages(self, acfactory):
ac1, ac2 = acfactory.get_two_online_accounts()
@@ -390,7 +399,7 @@ class TestOnlineAccount:
ac2.delete_messages(messages)
assert not chat3.get_messages()
def test_send_and_receive_message(self, acfactory, lp):
def test_send_and_receive_message_markseen(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
lp.sec("ac1: create chat with ac2")
@@ -430,8 +439,8 @@ class TestOnlineAccount:
ac2.mark_seen_messages([msg_in])
lp.step("1")
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_READ")
assert ev[1] >= const.DC_CHAT_ID_LAST_SPECIAL
assert ev[2] >= const.DC_MSG_ID_LAST_SPECIAL
assert ev[1] > const.DC_CHAT_ID_LAST_SPECIAL
assert ev[2] > const.DC_MSG_ID_LAST_SPECIAL
lp.step("2")
assert msg_out.is_out_mdn_received()
@@ -443,6 +452,7 @@ class TestOnlineAccount:
lp.sec("sending text message from ac1 to ac2")
msg_out = chat.send_text("message1")
assert not msg_out.is_encrypted()
lp.sec("wait for ac2 to receive message")
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
@@ -460,6 +470,7 @@ class TestOnlineAccount:
assert ev[2] > msg_out.id
msg_back = ac1.get_message_by_id(ev[2])
assert msg_back.text == "message-back"
assert msg_back.is_encrypted()
def test_saved_mime_on_received_message(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
@@ -501,7 +512,7 @@ class TestOnlineAccount:
assert os.path.exists(msg_in.filename)
assert os.stat(msg_in.filename).st_size == os.stat(path).st_size
def test_import_export_online(self, acfactory, tmpdir):
def test_import_export_online_all(self, acfactory, tmpdir):
ac1 = acfactory.get_online_configuring_account()
wait_configuration_progress(ac1, 1000)
@@ -509,11 +520,11 @@ class TestOnlineAccount:
chat = ac1.create_chat_by_contact(contact1)
chat.send_text("msg1")
backupdir = tmpdir.mkdir("backup")
path = ac1.export_to_dir(backupdir.strpath)
path = ac1.export_all(backupdir.strpath)
assert os.path.exists(path)
ac2 = acfactory.get_unconfigured_account()
ac2.import_from_file(path)
ac2.import_all(path)
contacts = ac2.get_contacts(query="some1")
assert len(contacts) == 1
contact2 = contacts[0]
@@ -523,7 +534,7 @@ class TestOnlineAccount:
assert len(messages) == 1
assert messages[0].text == "msg1"
def test_ac_setup_message(self, acfactory):
def test_ac_setup_message(self, acfactory, lp):
# note that the receiving account needs to be configured and running
# before ther setup message is send. DC does not read old messages
# as of Jul2019
@@ -531,15 +542,18 @@ class TestOnlineAccount:
ac2 = acfactory.clone_online_account(ac1)
wait_configuration_progress(ac2, 1000)
wait_configuration_progress(ac1, 1000)
lp.sec("trigger ac setup message and return setupcode")
assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"]
setup_code = ac1.initiate_key_transfer()
ac2._evlogger.set_timeout(30)
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
msg = ac2.get_message_by_id(ev[2])
assert msg.is_setup_message()
# first try a bad setup code
assert msg.get_setupcodebegin() == setup_code[:2]
lp.sec("try a bad setup code")
with pytest.raises(ValueError):
msg.continue_key_transfer(str(reversed(setup_code)))
lp.sec("try a good setup code")
print("*************** Incoming ASM File at: ", msg.filename)
print("*************** Setup Code: ", setup_code)
msg.continue_key_transfer(setup_code)
@@ -564,6 +578,38 @@ class TestOnlineAccount:
assert ch.id >= 10
wait_securejoin_inviter_progress(ac1, 1000)
def test_qr_verified_group_and_chatting(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
lp.sec("ac1: create verified-group QR, ac2 scans and joins")
chat1 = ac1.create_group_chat("hello", verified=True)
assert chat1.is_verified()
qr = chat1.get_join_qr()
lp.sec("ac2: start QR-code based join-group protocol")
chat2 = ac2.qr_join_chat(qr)
assert chat2.id >= 10
wait_securejoin_inviter_progress(ac1, 1000)
lp.sec("ac2: read member added message")
msg = ac2.wait_next_incoming_message()
assert msg.is_encrypted()
assert "added" in msg.text.lower()
lp.sec("ac1: send message")
msg_out = chat1.send_text("hello")
assert msg_out.is_encrypted()
lp.sec("ac2: read message and check it's verified chat")
msg = ac2.wait_next_incoming_message()
assert msg.text == "hello"
assert msg.chat.is_verified()
assert msg.is_encrypted()
lp.sec("ac2: send message and let ac1 read it")
chat2.send_text("world")
msg = ac1.wait_next_incoming_message()
assert msg.text == "world"
assert msg.is_encrypted()
def test_set_get_profile_image(self, acfactory, data, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
@@ -616,3 +662,32 @@ class TestOnlineAccount:
chat1b = ac1.create_chat_by_message(ev[2])
assert chat1b.get_profile_image() is None
assert chat.get_profile_image() is None
class TestOnlineConfigureFails:
def test_invalid_password(self, acfactory):
ac1, configdict = acfactory.get_online_config()
ac1.configure(addr=configdict["addr"], mail_pw="123")
ac1.start_threads()
wait_configuration_progress(ac1, 500)
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
assert "authentication failed" in ev1[2].lower()
wait_configuration_progress(ac1, 0, 0)
def test_invalid_user(self, acfactory):
ac1, configdict = acfactory.get_online_config()
ac1.configure(addr="x" + configdict["addr"], mail_pw=configdict["mail_pw"])
ac1.start_threads()
wait_configuration_progress(ac1, 500)
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
assert "authentication failed" in ev1[2].lower()
wait_configuration_progress(ac1, 0, 0)
def test_invalid_domain(self, acfactory):
ac1, configdict = acfactory.get_online_config()
ac1.configure(addr=configdict["addr"] + "x", mail_pw=configdict["mail_pw"])
ac1.start_threads()
wait_configuration_progress(ac1, 500)
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
assert "could not connect" in ev1[2].lower()
wait_configuration_progress(ac1, 0, 0)

View File

@@ -59,6 +59,16 @@ def test_wrong_db(tmpdir):
assert not lib.dc_open(dc_context, p.strpath.encode("ascii"), ffi.NULL)
def test_empty_blobdir(tmpdir):
# Apparently some client code expects this to be the same as passing NULL.
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
db_fname = tmpdir.join("hello.db")
assert lib.dc_open(ctx, db_fname.strpath.encode("ascii"), b"")
def test_event_defines():
assert const.DC_EVENT_INFO == 100
assert const.DC_CONTACT_ID_SELF
@@ -98,3 +108,43 @@ def test_provider_info():
def test_provider_info_none():
assert lib.dc_provider_new_from_email(cutil.as_dc_charpointer("email@unexistent.no")) == ffi.NULL
def test_get_info_closed():
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
assert 'deltachat_core_version' in info
assert 'database_dir' not in info
def test_get_info_open(tmpdir):
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
db_fname = tmpdir.join("test.db")
lib.dc_open(ctx, db_fname.strpath.encode("ascii"), ffi.NULL)
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
assert 'deltachat_core_version' in info
assert 'database_dir' in info
def test_is_open_closed():
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
assert lib.dc_is_open(ctx) == 0
def test_is_open_actually_open(tmpdir):
ctx = ffi.gc(
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
db_fname = tmpdir.join("test.db")
lib.dc_open(ctx, db_fname.strpath.encode("ascii"), ffi.NULL)
assert lib.dc_is_open(ctx) == 1

23
spec.md
View File

@@ -117,7 +117,8 @@ The sender plus the recipients are the group members.
To allow different groups with the same members,
groups are identified by a group-id.
The group-id MUST be created only from the characters
`0`-`9`, `A`-`Z`, `a`-`z` `_` and `-`.
`0`-`9`, `A`-`Z`, `a`-`z` `_` and `-`
and MUST have a length of at least 11 characters.
Groups MUST have a group-name.
The group-name is any non-zero-length UTF-8 string.
@@ -144,9 +145,9 @@ The message-id MUST have the format `Gr.<group-id>.<unique data>`.
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 1234xyZ
Chat-Group-ID: 12345uvwxyZ
Chat-Group-Name: My Group
Message-ID: Gr.1234xyZ.0001@domain
Message-ID: Gr.12345uvwxyZ.0001@domain
Subject: Chat: My Group: Hello group ...
Hello group - this group contains three members
@@ -196,10 +197,10 @@ and the message SHOULD appear as a message or action from the sender.
From: member1@domain
To: member2@domain, member3@domain, member4@domain
Chat-Version: 1.0
Chat-Group-ID: 1234xyZ
Chat-Group-ID: 12345uvwxyZ
Chat-Group-Name: My Group
Chat-Group-Member-Added: member4@domain
Message-ID: Gr.1234xyZ.0002@domain
Message-ID: Gr.12345uvwxyZ.0002@domain
Subject: Chat: My Group: Hello, ...
Hello, I've added member4@domain to our group. Now we have 4 members.
@@ -209,10 +210,10 @@ To remove a member:
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 1234xyZ
Chat-Group-ID: 12345uvwxyZ
Chat-Group-Name: My Group
Chat-Group-Member-Removed: member4@domain
Message-ID: Gr.1234xyZ.0003@domain
Message-ID: Gr.12345uvwxyZ.0003@domain
Subject: Chat: My Group: Hello, ...
Hello, I've removed member4@domain from our group. Now we have 3 members.
@@ -233,10 +234,10 @@ and the message SHOULD appear as a message or action from the sender.
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 1234xyZ
Chat-Group-ID: 12345uvwxyZ
Chat-Group-Name: Our Group
Chat-Group-Name-Changed: My Group
Message-ID: Gr.1234xyZ.0004@domain
Message-ID: Gr.12345uvwxyZ.0004@domain
Subject: Chat: Our Group: Hello, ...
Hello, I've changed the group name from "My Group" to "Our Group".
@@ -262,10 +263,10 @@ and the message SHOULD appear as a message or action from the sender.
From: member1@domain
To: member2@domain, member3@domain
Chat-Version: 1.0
Chat-Group-ID: 1234xyZ
Chat-Group-ID: 12345uvwxyZ
Chat-Group-Name: Our Group
Chat-Group-Image: image.jpg
Message-ID: Gr.1234xyZ.0005@domain
Message-ID: Gr.12345uvwxyZ.0005@domain
Subject: Chat: Our Group: Hello, ...
Content-Type: multipart/mixed; boundary="==break=="

View File

@@ -3,7 +3,7 @@ use std::ffi::CStr;
use std::str::FromStr;
use std::{fmt, str};
use mmime::mailimf_types::*;
use mmime::mailimf::types::*;
use crate::constants::*;
use crate::contact::*;

View File

@@ -1,6 +1,4 @@
use std::ffi::CString;
use std::path::{Path, PathBuf};
use std::ptr;
use crate::chatlist::*;
use crate::config::*;
@@ -12,11 +10,10 @@ use crate::dc_tools::*;
use crate::error::Error;
use crate::events::Event;
use crate::job::*;
use crate::message::*;
use crate::message::{self, Message, MessageState};
use crate::param::*;
use crate::sql::{self, Sql};
use crate::stock::StockMessage;
use crate::x::*;
/// An object representing a single chat in memory.
/// Chat objects are created using eg. `Chat::load_from_db`
@@ -156,7 +153,7 @@ impl Chat {
}
let cnt = get_chat_contact_cnt(context, self.id);
return context
.stock_string_repl_int(StockMessage::Member, cnt)
.stock_string_repl_int(StockMessage::Member, cnt as i32)
.into();
}
@@ -233,15 +230,14 @@ impl Chat {
self.is_sending_locations
}
#[allow(non_snake_case)]
unsafe fn prepare_msg_raw(
fn prepare_msg_raw(
&mut self,
context: &Context,
msg: &mut Message,
timestamp: i64,
) -> Result<u32, Error> {
let mut do_guarantee_e2ee: libc::c_int;
let e2ee_enabled: libc::c_int;
let mut do_guarantee_e2ee: bool;
let e2ee_enabled: bool;
let mut new_references = "".into();
let mut new_in_reply_to = "".into();
let mut msg_id = 0;
@@ -257,7 +253,7 @@ impl Chat {
}
if (self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup)
&& 0 == is_contact_in_chat(context, self.id, 1 as u32)
&& !is_contact_in_chat(context, self.id, 1 as u32)
{
emit_event!(
context,
@@ -272,7 +268,7 @@ impl Chat {
Chattype::Group | Chattype::VerifiedGroup => Some(self.grpid.as_str()),
_ => None,
};
dc_create_outgoing_rfc724_mid_safe(grpid, &from)
dc_create_outgoing_rfc724_mid(grpid, &from)
};
if self.typ == Chattype::Single {
@@ -302,14 +298,13 @@ impl Chat {
if we guarantee E2EE, and circumstances change
so that E2EE is no longer available at a later point (reset, changed settings),
we do not send the message out at all */
do_guarantee_e2ee = 0;
do_guarantee_e2ee = false;
e2ee_enabled = context
.sql
.get_config_int(context, "e2ee_enabled")
.unwrap_or_else(|| 1);
if 0 != e2ee_enabled
&& msg.param.get_int(Param::ForcePlaintext).unwrap_or_default() == 0
{
.unwrap_or_else(|| 1)
== 1;
if e2ee_enabled && msg.param.get_int(Param::ForcePlaintext).unwrap_or_default() == 0 {
let mut can_encrypt = 1;
let mut all_mutual = 1;
@@ -354,13 +349,13 @@ impl Chat {
if 0 != can_encrypt {
if 0 != all_mutual {
do_guarantee_e2ee = 1;
do_guarantee_e2ee = true;
} else if last_msg_in_chat_encrypted(context, &context.sql, self.id) {
do_guarantee_e2ee = 1;
do_guarantee_e2ee = true;
}
}
}
if 0 != do_guarantee_e2ee {
if do_guarantee_e2ee {
msg.param.set_int(Param::GuranteeE2ee, 1);
}
msg.param.remove(Param::ErroneousE2ee);
@@ -486,7 +481,7 @@ pub fn create_by_msg_id(context: &Context, msg_id: u32) -> Result<u32, Error> {
let mut chat_id = 0;
let mut send_event = false;
if let Ok(msg) = dc_msg_load_from_db(context, msg_id) {
if let Ok(msg) = Message::load_from_db(context, msg_id) {
if let Ok(chat) = Chat::load_from_db(context, msg.chat_id) {
if chat.id > DC_CHAT_ID_LAST_SPECIAL {
chat_id = chat.id;
@@ -693,13 +688,13 @@ fn prepare_msg_common(context: &Context, chat_id: u32, msg: &mut Message) -> Res
// - from FILE to AUDIO/VIDEO/IMAGE
// - from FILE/IMAGE to GIF */
if let Some((better_type, better_mime)) =
dc_msg_guess_msgtype_from_suffix(Path::new(&path_filename))
message::guess_msgtype_from_suffix(Path::new(&path_filename))
{
msg.type_0 = better_type;
msg.param.set(Param::MimeType, better_mime);
}
} else if !msg.param.exists(Param::MimeType) {
if let Some((_, mime)) = dc_msg_guess_msgtype_from_suffix(Path::new(&path_filename)) {
if let Some((_, mime)) = message::guess_msgtype_from_suffix(Path::new(&path_filename)) {
msg.param.set(Param::MimeType, mime);
}
}
@@ -718,7 +713,7 @@ fn prepare_msg_common(context: &Context, chat_id: u32, msg: &mut Message) -> Res
msg.state = MessageState::OutPending;
}
msg.id = unsafe { chat.prepare_msg_raw(context, msg, dc_create_smeared_timestamp(context))? };
msg.id = chat.prepare_msg_raw(context, msg, dc_create_smeared_timestamp(context))?;
msg.chat_id = chat_id;
Ok(msg.id)
@@ -746,7 +741,7 @@ fn last_msg_in_chat_encrypted(context: &Context, sql: &Sql, chat_id: u32) -> boo
}
}
pub fn is_contact_in_chat(context: &Context, chat_id: u32, contact_id: u32) -> libc::c_int {
pub fn is_contact_in_chat(context: &Context, chat_id: u32, contact_id: u32) -> bool {
/* this function works for group and for normal chats, however, it is more useful for group chats.
DC_CONTACT_ID_SELF may be used to check, if the user itself is in a group chat (DC_CONTACT_ID_SELF is not added to normal chats) */
@@ -756,7 +751,7 @@ pub fn is_contact_in_chat(context: &Context, chat_id: u32, contact_id: u32) -> l
"SELECT contact_id FROM chats_contacts WHERE chat_id=? AND contact_id=?;",
params![chat_id as i32, contact_id as i32],
)
.unwrap_or_default() as libc::c_int
.unwrap_or_default()
}
// Should return Result
@@ -785,11 +780,11 @@ pub fn send_msg(context: &Context, chat_id: u32, msg: &mut Message) -> Result<u3
chat_id == 0 || chat_id == msg.chat_id,
"Inconsistent chat ID"
);
dc_update_msg_state(context, msg.id, MessageState::OutPending);
message::update_msg_state(context, msg.id, MessageState::OutPending);
}
ensure!(
unsafe { job_send_msg(context, msg.id) } != 0,
job_send_msg(context, msg.id) != 0,
"Failed to initiate send job"
);
@@ -811,101 +806,111 @@ pub fn send_msg(context: &Context, chat_id: u32, msg: &mut Message) -> Result<u3
// avoid hanging if user tampers with db
break;
} else {
if let Ok(mut copy) = dc_get_msg(context, id as u32) {
if let Ok(mut copy) = Message::load_from_db(context, id as u32) {
// TODO: handle cleanup and return early instead
send_msg(context, 0, &mut copy).unwrap();
}
}
}
msg.param.remove(Param::PrepForwards);
dc_msg_save_param_to_disk(context, msg);
msg.save_param_to_disk(context);
}
}
Ok(msg.id)
}
pub unsafe fn send_text_msg(
context: &Context,
chat_id: u32,
text_to_send: String,
) -> Result<u32, Error> {
pub fn send_text_msg(context: &Context, chat_id: u32, text_to_send: String) -> Result<u32, Error> {
ensure!(
chat_id > DC_CHAT_ID_LAST_SPECIAL,
"bad chat_id = {} <= 9",
chat_id
);
let mut msg = dc_msg_new(Viewtype::Text);
let mut msg = Message::new(Viewtype::Text);
msg.text = Some(text_to_send);
send_msg(context, chat_id, &mut msg)
}
// passing `None` as message jsut deletes the draft
pub unsafe fn set_draft(context: &Context, chat_id: u32, msg: Option<&mut Message>) {
pub fn set_draft(context: &Context, chat_id: u32, msg: Option<&mut Message>) {
if chat_id <= DC_CHAT_ID_LAST_SPECIAL {
return;
}
if set_draft_raw(context, chat_id, msg) {
context.call_cb(Event::MsgsChanged { chat_id, msg_id: 0 });
let changed = match msg {
None => maybe_delete_draft(context, chat_id),
Some(msg) => set_draft_raw(context, chat_id, msg),
};
if changed {
context.call_cb(Event::MsgsChanged { chat_id, msg_id: 0 });
}
}
/// Delete draft message in specified chat, if there is one.
///
/// Return {true}, if message was deleted, {false} otherwise.
fn maybe_delete_draft(context: &Context, chat_id: u32) -> bool {
let draft = get_draft_msg_id(context, chat_id);
if draft != 0 {
Message::delete_from_db(context, draft);
return true;
}
false
}
/// Set provided message as draft message for specified chat.
///
/// Return true on success, false on database error.
fn do_set_draft(context: &Context, chat_id: u32, msg: &mut Message) -> bool {
match msg.type_0 {
Viewtype::Unknown => return false,
Viewtype::Text => {
if msg.text.as_ref().map_or(false, |s| s.is_empty()) {
return false;
}
}
_ => {
if let Some(path_filename) = msg.param.get(Param::File) {
let mut path_filename = path_filename.to_string();
if msg.is_increation() && !dc_is_blobdir_path(context, &path_filename) {
return false;
}
if !dc_make_rel_and_copy(context, &mut path_filename) {
return false;
}
msg.param.set(Param::File, path_filename);
}
}
}
sql::execute(
context,
&context.sql,
"INSERT INTO msgs (chat_id, from_id, timestamp, type, state, txt, param, hidden) \
VALUES (?,?,?, ?,?,?,?,?);",
params![
chat_id as i32,
1,
time(),
msg.type_0,
MessageState::OutDraft,
msg.text.as_ref().map(String::as_str).unwrap_or(""),
msg.param.to_string(),
1,
],
)
.is_ok()
}
// similar to as dc_set_draft() but does not emit an event
#[allow(non_snake_case)]
unsafe fn set_draft_raw(context: &Context, chat_id: u32, mut msg: Option<&mut Message>) -> bool {
let mut OK_TO_CONTINUE = true;
fn set_draft_raw(context: &Context, chat_id: u32, msg: &mut Message) -> bool {
let deleted = maybe_delete_draft(context, chat_id);
let set = do_set_draft(context, chat_id, msg);
let mut sth_changed = false;
let prev_draft_msg_id = get_draft_msg_id(context, chat_id);
if 0 != prev_draft_msg_id {
dc_delete_msg_from_db(context, prev_draft_msg_id);
sth_changed = true;
}
if let Some(ref mut msg) = msg {
// save new draft
if msg.type_0 == Viewtype::Text {
OK_TO_CONTINUE = msg.text.as_ref().map_or(false, |s| !s.is_empty());
} else if msgtype_has_file(msg.type_0) {
if let Some(path_filename) = msg.param.get(Param::File) {
let mut path_filename = path_filename.to_string();
if dc_msg_is_increation(msg) && !dc_is_blobdir_path(context, &path_filename) {
OK_TO_CONTINUE = false;
} else if !dc_make_rel_and_copy(context, &mut path_filename) {
OK_TO_CONTINUE = false;
} else {
msg.param.set(Param::File, path_filename);
}
}
} else {
OK_TO_CONTINUE = false;
}
if OK_TO_CONTINUE {
if sql::execute(
context,
&context.sql,
"INSERT INTO msgs (chat_id, from_id, timestamp, type, state, txt, param, hidden) \
VALUES (?,?,?, ?,?,?,?,?);",
params![
chat_id as i32,
1,
time(),
msg.type_0,
MessageState::OutDraft,
msg.text.as_ref().map(String::as_str).unwrap_or(""),
msg.param.to_string(),
1,
],
)
.is_ok()
{
sth_changed = true;
}
}
}
sth_changed
// Can't inline. Both functions above must be called, no shortcut!
deleted || set
}
fn get_draft_msg_id(context: &Context, chat_id: u32) -> u32 {
@@ -920,12 +925,14 @@ fn get_draft_msg_id(context: &Context, chat_id: u32) -> u32 {
}
pub fn get_draft(context: &Context, chat_id: u32) -> Result<Option<Message>, Error> {
ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat ID");
if chat_id <= DC_CHAT_ID_LAST_SPECIAL {
return Ok(None);
}
let draft_msg_id = get_draft_msg_id(context, chat_id);
if draft_msg_id == 0 {
return Ok(None);
}
Ok(Some(dc_msg_load_from_db(context, draft_msg_id)?))
Ok(Some(Message::load_from_db(context, draft_msg_id)?))
}
pub fn get_chat_msgs(context: &Context, chat_id: u32, flags: u32, marker1before: u32) -> Vec<u32> {
@@ -943,7 +950,7 @@ pub fn get_chat_msgs(context: &Context, chat_id: u32, flags: u32, marker1before:
}
if 0 != flags & 0x1 {
let curr_local_timestamp = ts + cnv_to_local;
let curr_day = (curr_local_timestamp / 86400) as libc::c_int;
let curr_day = curr_local_timestamp / 86400;
if curr_day != last_day {
ret.push(DC_MSG_ID_LAST_SPECIAL);
last_day = curr_day;
@@ -1110,17 +1117,25 @@ pub fn get_chat_media(
).unwrap_or_default()
}
pub unsafe fn get_next_media(
/// Indicates the direction over which to iterate.
#[derive(Debug, Clone, PartialEq, Eq)]
#[repr(i32)]
pub enum Direction {
Forward = 1,
Backward = -1,
}
pub fn get_next_media(
context: &Context,
curr_msg_id: u32,
dir: libc::c_int,
direction: Direction,
msg_type: Viewtype,
msg_type2: Viewtype,
msg_type3: Viewtype,
) -> u32 {
let mut ret = 0;
if let Ok(msg) = dc_msg_load_from_db(context, curr_msg_id) {
if let Ok(msg) = Message::load_from_db(context, curr_msg_id) {
let list = get_chat_media(
context,
msg.chat_id,
@@ -1134,13 +1149,16 @@ pub unsafe fn get_next_media(
);
for i in 0..list.len() {
if curr_msg_id == list[i] {
if dir > 0 {
if i + 1 < list.len() {
ret = list[i + 1]
match direction {
Direction::Forward => {
if i + 1 < list.len() {
ret = list[i + 1]
}
}
} else if dir < 0 {
if i >= 1 {
ret = list[i - 1];
Direction::Backward => {
if i >= 1 {
ret = list[i - 1];
}
}
}
break;
@@ -1257,16 +1275,14 @@ pub fn get_chat_contacts(context: &Context, chat_id: u32) -> Vec<u32> {
.unwrap_or_default()
}
pub unsafe fn create_group_chat(
pub fn create_group_chat(
context: &Context,
verified: VerifiedStatus,
chat_name: impl AsRef<str>,
) -> Result<u32, Error> {
ensure!(!chat_name.as_ref().is_empty(), "Invalid chat name");
let draft_txt =
CString::new(context.stock_string_repl_str(StockMessage::NewGroupDraft, &chat_name))
.unwrap();
let draft_txt = context.stock_string_repl_str(StockMessage::NewGroupDraft, &chat_name);
let grpid = dc_create_id();
sql::execute(
@@ -1288,9 +1304,9 @@ pub unsafe fn create_group_chat(
if chat_id != 0 {
if add_to_chat_contacts_table(context, chat_id, 1) {
let mut draft_msg = dc_msg_new(Viewtype::Text);
dc_msg_set_text(&mut draft_msg, draft_txt.as_ptr());
set_draft_raw(context, chat_id, Some(&mut draft_msg));
let mut draft_msg = Message::new(Viewtype::Text);
draft_msg.set_text(Some(draft_txt));
set_draft_raw(context, chat_id, &mut draft_msg);
}
context.call_cb(Event::MsgsChanged {
@@ -1316,106 +1332,105 @@ pub fn add_to_chat_contacts_table(context: &Context, chat_id: u32, contact_id: u
.is_ok()
}
pub unsafe fn add_contact_to_chat(context: &Context, chat_id: u32, contact_id: u32) -> bool {
add_contact_to_chat_ex(context, chat_id, contact_id, 0)
pub fn add_contact_to_chat(context: &Context, chat_id: u32, contact_id: u32) -> bool {
match add_contact_to_chat_ex(context, chat_id, contact_id, false) {
Ok(res) => res,
Err(err) => {
error!(context, "failed to add contact: {}", err);
false
}
}
}
#[allow(non_snake_case)]
pub fn add_contact_to_chat_ex(
pub(crate) fn add_contact_to_chat_ex(
context: &Context,
chat_id: u32,
contact_id: u32,
flags: libc::c_int,
) -> bool {
let mut OK_TO_CONTINUE = true;
let mut success = false;
let contact = Contact::get_by_id(context, contact_id);
if contact.is_err() || chat_id <= DC_CHAT_ID_LAST_SPECIAL {
return false;
}
let mut msg = dc_msg_new_untyped();
from_handshake: bool,
) -> Result<bool, Error> {
ensure!(
chat_id > DC_CHAT_ID_LAST_SPECIAL,
"can not add member to special chats"
);
let contact = Contact::get_by_id(context, contact_id)?;
let mut msg = Message::default();
reset_gossiped_timestamp(context, chat_id);
let contact = contact.unwrap();
/*this also makes sure, not contacts are added to special or normal chats*/
if let Ok(mut chat) = Chat::load_from_db(context, chat_id) {
if !(!real_group_exists(context, chat_id)
|| !Contact::real_exists_by_id(context, contact_id) && contact_id != DC_CONTACT_ID_SELF)
{
if !(is_contact_in_chat(context, chat_id, 1 as u32) == 1) {
emit_event!(
context,
Event::ErrorSelfNotInGroup(
"Cannot add contact to group; self not in group.".into()
)
);
} else {
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
if 0 != flags & 0x1
&& chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 1
{
chat.param.remove(Param::Unpromoted);
chat.update_param(context).unwrap();
}
let self_addr = context
.sql
.get_config(context, "configured_addr")
.unwrap_or_default();
if contact.get_addr() != &self_addr {
// ourself is added using DC_CONTACT_ID_SELF, do not add it explicitly.
// if SELF is not in the group, members cannot be added at all.
let mut chat = Chat::load_from_db(context, chat_id)?;
ensure!(
real_group_exists(context, chat_id),
"chat_id {} is not a group where one can add members",
chat_id
);
ensure!(
Contact::real_exists_by_id(context, contact_id) && contact_id != DC_CONTACT_ID_SELF,
"invalid contact_id {} for removal in group",
contact_id
);
if 0 != is_contact_in_chat(context, chat_id, contact_id) {
if 0 == flags & 0x1 {
success = true;
OK_TO_CONTINUE = false;
}
} else {
// else continue and send status mail
if chat.typ == Chattype::VerifiedGroup {
if contact.is_verified(context) != VerifiedStatus::BidirectVerified {
error!(
context,
"Only bidirectional verified contacts can be added to verified groups."
);
OK_TO_CONTINUE = false;
}
}
if OK_TO_CONTINUE {
if !add_to_chat_contacts_table(context, chat_id, contact_id) {
OK_TO_CONTINUE = false;
}
}
}
if OK_TO_CONTINUE {
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
msg.type_0 = Viewtype::Text;
msg.text = Some(context.stock_system_msg(
StockMessage::MsgAddMember,
contact.get_addr(),
"",
DC_CONTACT_ID_SELF as u32,
));
msg.param.set_int(Param::Cmd, 4);
msg.param.set(Param::Arg, contact.get_addr());
msg.param.set_int(Param::Arg2, flags);
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
context.call_cb(Event::MsgsChanged {
chat_id,
msg_id: msg.id,
});
}
context.call_cb(Event::MsgsChanged { chat_id, msg_id: 0 });
success = true;
}
}
if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF as u32) {
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
emit_event!(
context,
Event::ErrorSelfNotInGroup("Cannot add contact to group; self not in group.".into())
);
bail!("can not add contact because our account is not part of it");
}
if from_handshake && chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 1 {
chat.param.remove(Param::Unpromoted);
chat.update_param(context).unwrap();
}
let self_addr = context
.sql
.get_config(context, "configured_addr")
.unwrap_or_default();
if contact.get_addr() == &self_addr {
bail!("invalid attempt to add self e-mail address to group");
}
// ourself is added using DC_CONTACT_ID_SELF, do not add it explicitly.
// if SELF is not in the group, members cannot be added at all.
if is_contact_in_chat(context, chat_id, contact_id) {
if !from_handshake {
return Ok(true);
}
} else {
// else continue and send status mail
if chat.typ == Chattype::VerifiedGroup {
if contact.is_verified(context) != VerifiedStatus::BidirectVerified {
error!(
context,
"Only bidirectional verified contacts can be added to verified groups."
);
return Ok(false);
}
}
};
success
if !add_to_chat_contacts_table(context, chat_id, contact_id) {
return Ok(false);
}
}
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
msg.type_0 = Viewtype::Text;
msg.text = Some(context.stock_system_msg(
StockMessage::MsgAddMember,
contact.get_addr(),
"",
DC_CONTACT_ID_SELF as u32,
));
msg.param.set_int(Param::Cmd, 4);
msg.param.set(Param::Arg, contact.get_addr());
msg.param.set_int(Param::Arg2, from_handshake.into());
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
context.call_cb(Event::MsgsChanged {
chat_id,
msg_id: msg.id,
});
}
context.call_cb(Event::MsgsChanged { chat_id, msg_id: 0 });
return Ok(true);
}
fn real_group_exists(context: &Context, chat_id: u32) -> bool {
@@ -1467,7 +1482,7 @@ pub fn set_gossiped_timestamp(context: &Context, chat_id: u32, timestamp: i64) {
}
}
pub unsafe fn remove_contact_from_chat(
pub fn remove_contact_from_chat(
context: &Context,
chat_id: u32,
contact_id: u32,
@@ -1482,14 +1497,14 @@ pub unsafe fn remove_contact_from_chat(
"Cannot remove special contact"
);
let mut msg = dc_msg_new_untyped();
let mut msg = Message::default();
let mut success = false;
/* we do not check if "contact_id" exists but just delete all records with the id from chats_contacts */
/* this allows to delete pending references to deleted contacts. Of course, this should _not_ happen. */
if let Ok(chat) = Chat::load_from_db(context, chat_id) {
if real_group_exists(context, chat_id) {
if !(is_contact_in_chat(context, chat_id, 1 as u32) == 1) {
if !is_contact_in_chat(context, chat_id, 1 as u32) {
emit_event!(
context,
Event::ErrorSelfNotInGroup(
@@ -1568,7 +1583,7 @@ pub fn is_group_explicitly_left(context: &Context, grpid: impl AsRef<str>) -> Re
)
}
pub unsafe fn set_chat_name(
pub fn set_chat_name(
context: &Context,
chat_id: u32,
new_name: impl AsRef<str>,
@@ -1580,12 +1595,12 @@ pub unsafe fn set_chat_name(
ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat ID");
let chat = Chat::load_from_db(context, chat_id)?;
let mut msg = dc_msg_new_untyped();
let mut msg = Message::default();
if real_group_exists(context, chat_id) {
if &chat.name == new_name.as_ref() {
success = true;
} else if !(is_contact_in_chat(context, chat_id, 1) == 1) {
} else if !is_contact_in_chat(context, chat_id, 1) {
emit_event!(
context,
Event::ErrorSelfNotInGroup("Cannot set chat name; self not in group".into())
@@ -1647,7 +1662,7 @@ pub fn set_chat_profile_image(
if real_group_exists(context, chat_id) {
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
if !(is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF) == 1i32) {
if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF) {
emit_event!(
context,
Event::ErrorSelfNotInGroup(
@@ -1669,7 +1684,7 @@ pub fn set_chat_profile_image(
chat.param.set(Param::ProfileImage, &new_image_rel);
if chat.update_param(context).is_ok() {
if chat.is_promoted() {
let mut msg = dc_msg_new_untyped();
let mut msg = Message::default();
msg.param
.set_int(Param::Cmd, SystemMessage::GroupImageChanged as i32);
msg.type_0 = Viewtype::Text;
@@ -1701,13 +1716,8 @@ pub fn set_chat_profile_image(
bail!("Failed to set profile image");
}
pub unsafe fn forward_msgs(
context: &Context,
msg_ids: *const u32,
msg_cnt: libc::c_int,
chat_id: u32,
) {
if msg_ids.is_null() || msg_cnt <= 0 || chat_id <= DC_CHAT_ID_LAST_SPECIAL {
pub fn forward_msgs(context: &Context, msg_ids: &[u32], chat_id: u32) {
if msg_ids.is_empty() || chat_id <= DC_CHAT_ID_LAST_SPECIAL {
return;
}
@@ -1716,14 +1726,13 @@ pub unsafe fn forward_msgs(
unarchive(context, chat_id).unwrap();
if let Ok(mut chat) = Chat::load_from_db(context, chat_id) {
curr_timestamp = dc_create_smeared_timestamps(context, msg_cnt);
let idsstr = std::slice::from_raw_parts(msg_ids, msg_cnt as usize)
.iter()
curr_timestamp = dc_create_smeared_timestamps(context, msg_ids.len());
let idsstr = msg_ids
.into_iter()
.enumerate()
.fold(
String::with_capacity(2 * msg_cnt as usize),
|acc, (i, n)| (if i == 0 { acc } else { acc + "," }) + &n.to_string(),
);
.fold(String::with_capacity(2 * msg_ids.len()), |acc, (i, n)| {
(if i == 0 { acc } else { acc + "," }) + &n.to_string()
});
let ids = context
.sql
@@ -1740,7 +1749,7 @@ pub unsafe fn forward_msgs(
for id in ids {
let src_msg_id = id;
let msg = dc_msg_load_from_db(context, src_msg_id as u32);
let msg = Message::load_from_db(context, src_msg_id as u32);
if msg.is_err() {
break;
}
@@ -1771,7 +1780,7 @@ pub unsafe fn forward_msgs(
msg.param.set(Param::PrepForwards, new_msg_id.to_string());
}
dc_msg_save_param_to_disk(context, &mut msg);
msg.save_param_to_disk(context);
msg.param = save_param;
} else {
msg.state = MessageState::OutPending;
@@ -1795,15 +1804,15 @@ pub unsafe fn forward_msgs(
}
}
pub fn get_chat_contact_cnt(context: &Context, chat_id: u32) -> libc::c_int {
pub fn get_chat_contact_cnt(context: &Context, chat_id: u32) -> usize {
context
.sql
.query_get_value(
.query_get_value::<_, isize>(
context,
"SELECT COUNT(*) FROM chats_contacts WHERE chat_id=?;",
params![chat_id as i32],
)
.unwrap_or_default()
.unwrap_or_default() as usize
}
pub fn get_chat_cnt(context: &Context) -> usize {
@@ -1840,12 +1849,7 @@ pub fn get_chat_id_by_grpid(context: &Context, grpid: impl AsRef<str>) -> (u32,
}
pub fn add_device_msg(context: &Context, chat_id: u32, text: impl AsRef<str>) {
let rfc724_mid = unsafe {
dc_create_outgoing_rfc724_mid(
ptr::null(),
b"@device\x00" as *const u8 as *const libc::c_char,
)
};
let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device");
if context.sql.execute(
"INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,rfc724_mid) VALUES (?,?,?, ?,?,?, ?,?);",
@@ -1857,21 +1861,13 @@ pub fn add_device_msg(context: &Context, chat_id: u32, text: impl AsRef<str>) {
Viewtype::Text,
MessageState::InNoticed,
text.as_ref(),
as_str(rfc724_mid),
rfc724_mid,
]
).is_err() {
unsafe { free(rfc724_mid as *mut libc::c_void) };
return;
}
let msg_id = sql::get_rowid(
context,
&context.sql,
"msgs",
"rfc724_mid",
as_str(rfc724_mid),
);
unsafe { free(rfc724_mid as *mut libc::c_void) };
let msg_id = sql::get_rowid(context, &context.sql, "msgs", "rfc724_mid", &rfc724_mid);
context.call_cb(Event::MsgsChanged { chat_id, msg_id });
}
@@ -1892,8 +1888,8 @@ mod tests {
#[test]
fn test_get_draft_special_chat_id() {
let t = dummy_context();
let draft = get_draft(&t.ctx, DC_CHAT_ID_LAST_SPECIAL);
assert!(draft.is_err());
let draft = get_draft(&t.ctx, DC_CHAT_ID_LAST_SPECIAL).unwrap();
assert!(draft.is_none());
}
#[test]
@@ -1907,16 +1903,14 @@ mod tests {
#[test]
fn test_get_draft() {
unsafe {
let t = dummy_context();
let chat_id = create_by_contact_id(&t.ctx, DC_CONTACT_ID_SELF).unwrap();
let mut msg = dc_msg_new(Viewtype::Text);
dc_msg_set_text(&mut msg, b"hello\x00" as *const u8 as *const libc::c_char);
set_draft(&t.ctx, chat_id, Some(&mut msg));
let draft = get_draft(&t.ctx, chat_id).unwrap().unwrap();
let msg_text = dc_msg_get_text(&msg);
let draft_text = dc_msg_get_text(&draft);
assert_eq!(as_str(msg_text), as_str(draft_text));
}
let t = dummy_context();
let chat_id = create_by_contact_id(&t.ctx, DC_CONTACT_ID_SELF).unwrap();
let mut msg = Message::new(Viewtype::Text);
msg.set_text(Some("hello".to_string()));
set_draft(&t.ctx, chat_id, Some(&mut msg));
let draft = get_draft(&t.ctx, chat_id).unwrap().unwrap();
let msg_text = msg.get_text();
let draft_text = draft.get_text();
assert_eq!(msg_text, draft_text);
}
}

View File

@@ -4,7 +4,7 @@ use crate::contact::*;
use crate::context::*;
use crate::error::Result;
use crate::lot::Lot;
use crate::message::*;
use crate::message::Message;
use crate::stock::StockMessage;
/// An object representing a single chatlist in memory.
@@ -271,7 +271,7 @@ impl Chatlist {
let mut lastcontact = None;
let lastmsg = if 0 != lastmsg_id {
if let Ok(lastmsg) = dc_msg_load_from_db(context, lastmsg_id) {
if let Ok(lastmsg) = Message::load_from_db(context, lastmsg_id) {
if lastmsg.from_id != 1 as libc::c_uint
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
{

View File

@@ -1,3 +1,4 @@
use libc::free;
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
@@ -5,7 +6,6 @@ use crate::constants::*;
use crate::context::Context;
use crate::dc_tools::*;
use crate::login_param::LoginParam;
use crate::x::*;
use super::read_autoconf_file;
/* ******************************************************************************

View File

@@ -1,3 +1,6 @@
use std::ptr;
use libc::free;
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
@@ -5,8 +8,6 @@ use crate::constants::*;
use crate::context::Context;
use crate::dc_tools::*;
use crate::login_param::LoginParam;
use crate::x::*;
use std::ptr;
use super::read_autoconf_file;
/* ******************************************************************************
@@ -46,7 +47,7 @@ pub unsafe fn outlk_autodiscover(
ok_to_continue = true;
break;
}
memset(
libc::memset(
&mut outlk_ad as *mut outlk_autodiscover_t as *mut libc::c_void,
0,
::std::mem::size_of::<outlk_autodiscover_t>(),

View File

@@ -18,7 +18,7 @@ use auto_mozilla::moz_autoconfigure;
macro_rules! progress {
($context:tt, $progress:expr) => {
assert!(
$progress > 0 && $progress <= 1000,
$progress <= 1000,
"value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success"
);
$context.call_cb($crate::events::Event::ConfigureProgress($progress));
@@ -45,7 +45,7 @@ pub fn dc_is_configured(context: &Context) -> bool {
******************************************************************************/
// the other dc_job_do_DC_JOB_*() functions are declared static in the c-file
#[allow(non_snake_case, unused_must_use)]
pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
let mut success = false;
let mut imap_connected_here = false;
let mut smtp_connected_here = false;
@@ -660,3 +660,23 @@ pub fn read_autoconf_file(context: &Context, url: &str) -> *mut libc::c_char {
}
}
}
#[cfg(test)]
mod tests {
use crate::config::*;
use crate::configure::dc_job_do_DC_JOB_CONFIGURE_IMAP;
use crate::test_utils::*;
#[test]
fn test_no_panic_on_bad_credentials() {
let t = dummy_context();
t.ctx
.set_config(Config::Addr, Some("probably@unexistant.addr"))
.unwrap();
t.ctx.set_config(Config::MailPw, Some("123456")).unwrap();
unsafe {
dc_job_do_DC_JOB_CONFIGURE_IMAP(&t.ctx);
}
}
}

View File

@@ -60,14 +60,9 @@ const DC_GCM_ADDDAYMARKER: usize = 0x01;
pub const DC_GCL_VERIFIED_ONLY: usize = 0x01;
pub const DC_GCL_ADD_SELF: usize = 0x02;
/// param1 is a directory where the keys are written to
const DC_IMEX_EXPORT_SELF_KEYS: usize = 1;
/// param1 is a directory where the keys are searched in and read from
const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
/// param1 is a directory where the backup is written to
const DC_IMEX_EXPORT_BACKUP: usize = 11;
/// param1 is the file with the backup to import
const DC_IMEX_IMPORT_BACKUP: usize = 12;
// values for DC_PARAM_FORCE_PLAINTEXT
pub(crate) const DC_FP_NO_AUTOCRYPT_HEADER: i32 = 2;
pub(crate) const DC_FP_ADD_AUTOCRYPT_HEADER: i32 = 1;
/// virtual chat showing all messages belonging to chats flagged with chats.blocked=2
pub(crate) const DC_CHAT_ID_DEADDROP: u32 = 1;
@@ -122,7 +117,7 @@ const DC_MAX_GET_INFO_LEN: usize = 100000;
pub const DC_CONTACT_ID_UNDEFINED: u32 = 0;
pub const DC_CONTACT_ID_SELF: u32 = 1;
const DC_CONTACT_ID_DEVICE: u32 = 2;
pub const DC_CONTACT_ID_DEVICE: u32 = 2;
pub const DC_CONTACT_ID_LAST_SPECIAL: u32 = 9;
pub const DC_CREATE_MVBOX: usize = 1;

View File

@@ -16,7 +16,7 @@ use crate::job_thread::JobThread;
use crate::key::*;
use crate::login_param::LoginParam;
use crate::lot::Lot;
use crate::message::*;
use crate::message::{self, Message};
use crate::param::Params;
use crate::smtp::*;
use crate::sql::Sql;
@@ -66,6 +66,30 @@ pub struct RunningState {
pub shall_stop_ongoing: bool,
}
/// Return some info about deltachat-core
///
/// This contains information mostly about the library itself, the
/// actual keys and their values which will be present are not
/// guaranteed. Calling [Context::get_info] also includes information
/// about the context on top of the information here.
pub fn get_info() -> HashMap<&'static str, String> {
let mut res = HashMap::new();
res.insert("deltachat_core_version", format!("v{}", &*DC_VERSION_STR));
res.insert("sqlite_version", rusqlite::version().to_string());
res.insert(
"sqlite_thread_safe",
unsafe { rusqlite::ffi::sqlite3_threadsafe() }.to_string(),
);
res.insert(
"arch",
(::std::mem::size_of::<*mut libc::c_void>())
.wrapping_mul(8)
.to_string(),
);
res.insert("level", "awesome".into());
res
}
impl Context {
pub fn new(cb: Box<ContextCallback>, os_name: String, dbfile: PathBuf) -> Result<Context> {
let mut blob_fname = OsString::new();
@@ -144,8 +168,8 @@ impl Context {
let l2 = LoginParam::from_database(self, "configured_");
let displayname = self.sql.get_config(self, "displayname");
let chats = get_chat_cnt(self) as usize;
let real_msgs = dc_get_real_msg_cnt(self) as usize;
let deaddrop_msgs = dc_get_deaddrop_msg_cnt(self) as usize;
let real_msgs = message::get_real_msg_cnt(self) as usize;
let deaddrop_msgs = message::get_deaddrop_msg_cnt(self) as usize;
let contacts = Contact::get_real_cnt(self) as usize;
let is_configured = self
.sql
@@ -209,19 +233,7 @@ impl Context {
.get_config(self, "configured_mvbox_folder")
.unwrap_or_else(|| "<unset>".to_string());
let mut res = HashMap::new();
res.insert("deltachat_core_version", format!("v{}", &*DC_VERSION_STR));
res.insert("sqlite_version", rusqlite::version().to_string());
res.insert(
"sqlite_thread_safe",
unsafe { rusqlite::ffi::sqlite3_threadsafe() }.to_string(),
);
res.insert(
"arch",
(::std::mem::size_of::<*mut libc::c_void>())
.wrapping_mul(8)
.to_string(),
);
let mut res = get_info();
res.insert("number_of_chats", chats.to_string());
res.insert("number_of_chat_messages", real_msgs.to_string());
res.insert("messages_in_contact_requests", deaddrop_msgs.to_string());
@@ -251,7 +263,6 @@ impl Context {
pub_key_cnt.unwrap_or_default().to_string(),
);
res.insert("fingerprint", fingerprint_str);
res.insert("level", "awesome".into());
res
}
@@ -354,15 +365,15 @@ impl Context {
return;
}
if let Ok(msg) = dc_msg_new_load(self, msg_id) {
if dc_msg_is_setupmessage(&msg) {
if let Ok(msg) = Message::load_from_db(self, msg_id) {
if msg.is_setupmessage() {
// do not move setup messages;
// there may be a non-delta device that wants to handle it
return;
}
if self.is_mvbox(folder) {
dc_update_msg_move_state(self, msg.rfc724_mid, MoveState::Stay);
message::update_msg_move_state(self, &msg.rfc724_mid, MoveState::Stay);
}
// 1 = dc message, 2 = reply to dc message
@@ -374,7 +385,7 @@ impl Context {
Params::new(),
0,
);
dc_update_msg_move_state(self, msg.rfc724_mid, MoveState::Moving);
message::update_msg_move_state(self, &msg.rfc724_mid, MoveState::Moving);
}
}
}
@@ -504,6 +515,14 @@ mod tests {
let t = dummy_context();
let info = t.ctx.get_info();
assert!(info.get("database_dir").is_some());
}
#[test]
fn test_get_info_no_context() {
let info = get_info();
assert!(info.get("deltachat_core_version").is_some());
assert!(info.get("database_dir").is_none());
assert_eq!(info.get("level").unwrap(), "awesome");
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,13 +2,13 @@ use std::ffi::CString;
use std::ptr;
use itertools::join;
use libc::{free, strcmp, strlen};
use mmime::clist::*;
use mmime::mailimf::types::*;
use mmime::mailimf::*;
use mmime::mailimf_types::*;
use mmime::mailmime::content::*;
use mmime::mailmime::types::*;
use mmime::mailmime::*;
use mmime::mailmime_content::*;
use mmime::mailmime_types::*;
use mmime::mmapstring::*;
use mmime::other::*;
use sha2::{Digest, Sha256};
@@ -23,13 +23,12 @@ use crate::error::Result;
use crate::events::Event;
use crate::job::*;
use crate::location;
use crate::message::*;
use crate::message::{self, MessageState};
use crate::param::*;
use crate::peerstate::*;
use crate::securejoin::handle_securejoin_handshake;
use crate::sql;
use crate::stock::StockMessage;
use crate::x::*;
#[derive(Debug, PartialEq, Eq)]
enum CreateEvent {
@@ -40,8 +39,7 @@ enum CreateEvent {
/// Receive a message and add it to the database.
pub unsafe fn dc_receive_imf(
context: &Context,
imf_raw_not_terminated: *const libc::c_char,
imf_raw_bytes: libc::size_t,
imf_raw: &[u8],
server_folder: impl AsRef<str>,
server_uid: u32,
flags: u32,
@@ -62,9 +60,10 @@ pub unsafe fn dc_receive_imf(
// we use mailmime_parse() through dc_mimeparser (both call mailimf_struct_multiple_parse()
// somewhen, I did not found out anything that speaks against this approach yet)
let body = std::slice::from_raw_parts(imf_raw_not_terminated as *const u8, imf_raw_bytes);
let mut mime_parser = MimeParser::new(context);
mime_parser.parse(body);
if let Err(err) = mime_parser.parse(imf_raw) {
error!(context, "dc_receive_imf parse error: {}", err);
};
if mime_parser.header.is_empty() {
// Error - even adding an empty record won't help as we do not know the message ID
@@ -85,8 +84,6 @@ pub unsafe fn dc_receive_imf(
let mut add_delete_job: libc::c_int = 0;
let mut insert_msg_id = 0;
// Message-ID from the header
let rfc724_mid = std::ptr::null_mut();
let mut sent_timestamp = 0;
let mut created_db_entries = Vec::new();
let mut create_event_to_send = Some(CreateEvent::MsgsChanged);
@@ -96,12 +93,9 @@ pub unsafe fn dc_receive_imf(
// helper method to handle early exit and memory cleanup
let cleanup = |context: &Context,
rfc724_mid: *mut libc::c_char,
create_event_to_send: &Option<CreateEvent>,
created_db_entries: &Vec<(usize, usize)>,
rr_event_to_send: &Vec<(u32, u32)>| {
free(rfc724_mid.cast());
if let Some(create_event_to_send) = create_event_to_send {
for (chat_id, msg_id) in created_db_entries {
let event = match create_event_to_send {
@@ -184,18 +178,39 @@ pub unsafe fn dc_receive_imf(
}
// Add parts
let rfc724_mid = match mime_parser.get_rfc724_mid() {
Some(x) => x,
None => {
// missing Message-IDs may come if the mail was set from this account with another
// client that relies in the SMTP server to generate one.
// true eg. for the Webmailer used in all-inkl-KAS
match dc_create_incoming_rfc724_mid(sent_timestamp, from_id, &to_ids) {
Some(x) => x.to_string(),
None => {
error!(context, "can not create incoming rfc724_mid");
cleanup(
context,
&create_event_to_send,
&created_db_entries,
&rr_event_to_send,
);
return;
}
}
}
};
if mime_parser.get_last_nonmeta().is_some() {
if let Err(err) = add_parts(
context,
&mut mime_parser,
imf_raw_not_terminated,
imf_raw_bytes,
imf_raw,
incoming,
&mut incoming_origin,
server_folder.as_ref(),
server_uid,
&mut to_ids,
rfc724_mid,
&rfc724_mid,
&mut sent_timestamp,
&mut from_id,
from_id_blocked,
@@ -213,7 +228,6 @@ pub unsafe fn dc_receive_imf(
cleanup(
context,
rfc724_mid,
&create_event_to_send,
&created_db_entries,
&rr_event_to_send,
@@ -263,14 +277,11 @@ pub unsafe fn dc_receive_imf(
info!(
context,
"received message {} has Message-Id: {}",
server_uid,
to_string(rfc724_mid)
"received message {} has Message-Id: {}", server_uid, rfc724_mid
);
cleanup(
context,
rfc724_mid,
&create_event_to_send,
&created_db_entries,
&rr_event_to_send,
@@ -280,14 +291,13 @@ pub unsafe fn dc_receive_imf(
unsafe fn add_parts(
context: &Context,
mut mime_parser: &mut MimeParser,
imf_raw_not_terminated: *const libc::c_char,
imf_raw_bytes: libc::size_t,
imf_raw: &[u8],
incoming: i32,
incoming_origin: &mut Origin,
server_folder: impl AsRef<str>,
server_uid: u32,
to_ids: &mut Vec<u32>,
mut rfc724_mid: *mut libc::c_char,
rfc724_mid: &str,
sent_timestamp: &mut i64,
from_id: &mut u32,
from_id_blocked: i32,
@@ -336,42 +346,16 @@ unsafe fn add_parts(
}
}
// get Message-ID; if the header is lacking one, generate one based on fields that do never
// change. (missing Message-IDs may come if the mail was set from this account with another
// client that relies in the SMTP server to generate one.
// true eg. for the Webmailer used in all-inkl-KAS)
if let Some(field) = mime_parser.lookup_field_typ("Message-ID", MAILIMF_FIELD_MESSAGE_ID) {
let fld_message_id = (*field).fld_data.fld_message_id;
if !fld_message_id.is_null() {
rfc724_mid = dc_strdup((*fld_message_id).mid_value)
}
}
if rfc724_mid.is_null() {
rfc724_mid = dc_create_incoming_rfc724_mid(*sent_timestamp, *from_id, to_ids);
if rfc724_mid.is_null() {
cleanup(mime_in_reply_to, mime_references);
bail!("Cannot create Message-ID");
}
}
// check, if the mail is already in our database - if so, just update the folder/uid
// (if the mail was moved around) and finish. (we may get a mail twice eg. if it is
// moved between folders. make sure, this check is done eg. before securejoin-processing) */
let mut old_server_folder = std::ptr::null_mut();
let mut old_server_uid = 0;
if 0 != dc_rfc724_mid_exists(
context,
rfc724_mid,
&mut old_server_folder,
&mut old_server_uid,
) {
if as_str(old_server_folder) != server_folder.as_ref() || old_server_uid != server_uid {
dc_update_server_uid(context, rfc724_mid, server_folder.as_ref(), server_uid);
if let Ok((old_server_folder, old_server_uid, _)) =
message::rfc724_mid_exists(context, &rfc724_mid)
{
if old_server_folder != server_folder.as_ref() || old_server_uid != server_uid {
message::update_server_uid(context, &rfc724_mid, server_folder.as_ref(), server_uid);
}
free(old_server_folder.cast());
cleanup(mime_in_reply_to, mime_references);
bail!("Message already in DB");
}
@@ -666,8 +650,19 @@ unsafe fn add_parts(
.set_int(Param::Cmd, mime_parser.is_system_message as i32);
}
/*
info!(
context,
"received mime message {:?}",
String::from_utf8_lossy(std::slice::from_raw_parts(
imf_raw_not_terminated as *const u8,
imf_raw_bytes,
))
);
*/
stmt.execute(params![
as_str(rfc724_mid),
rfc724_mid,
server_folder.as_ref(),
server_uid as libc::c_int,
*chat_id as libc::c_int,
@@ -686,10 +681,7 @@ unsafe fn add_parts(
part.bytes,
*hidden,
if save_mime_headers {
Some(String::from_utf8_lossy(std::slice::from_raw_parts(
imf_raw_not_terminated as *const u8,
imf_raw_bytes,
)))
Some(String::from_utf8_lossy(imf_raw))
} else {
None
},
@@ -698,13 +690,8 @@ unsafe fn add_parts(
])?;
txt_raw = None;
*insert_msg_id = sql::get_rowid_with_conn(
context,
conn,
"msgs",
"rfc724_mid",
as_str(rfc724_mid),
);
*insert_msg_id =
sql::get_rowid_with_conn(context, conn, "msgs", "rfc724_mid", &rfc724_mid);
created_db_entries.push((*chat_id as usize, *insert_msg_id as usize));
}
Ok(())
@@ -760,10 +747,7 @@ unsafe fn handle_reports(
for report_root in &mime_parser.reports {
let report_root = *report_root;
let mut mdn_consumed = 0;
let report_type = mailmime_find_ct_parameter(
report_root,
b"report-type\x00" as *const u8 as *const libc::c_char,
);
let report_type = mailmime_find_ct_parameter(report_root, "report-type");
if report_root.is_null() || report_type.is_null() || (*report_type).pa_value.is_null() {
continue;
@@ -799,7 +783,7 @@ unsafe fn handle_reports(
.data
} else {
ptr::null_mut()
}) as *mut mailmime;
}) as *mut Mailmime;
if !report_data.is_null()
&& (*(*(*report_data).mm_content_type).ct_type).tp_type
@@ -814,22 +798,13 @@ unsafe fn handle_reports(
b"disposition-notification\x00" as *const u8 as *const libc::c_char,
) == 0
{
let mut report_body = std::ptr::null();
let mut report_body_bytes = 0;
let mut to_mmap_string_unref = std::ptr::null_mut();
if mailmime_transfer_decode(
report_data,
&mut report_body,
&mut report_body_bytes,
&mut to_mmap_string_unref,
) {
if let Ok(report_body) = mailmime_transfer_decode(report_data) {
let mut report_parsed = std::ptr::null_mut();
let mut dummy = 0;
if mailmime_parse(
report_body,
report_body_bytes,
report_body.as_ptr() as *const _,
report_body.len(),
&mut dummy,
&mut report_parsed,
) == MAIL_NO_ERROR as libc::c_int
@@ -864,10 +839,10 @@ unsafe fn handle_reports(
let mut chat_id_0 = 0;
let mut msg_id = 0;
if 0 != dc_mdn_from_ext(
if message::mdn_from_ext(
context,
from_id,
rfc724_mid_0,
as_str(rfc724_mid_0),
sent_timestamp,
&mut chat_id_0,
&mut msg_id,
@@ -881,9 +856,6 @@ unsafe fn handle_reports(
}
mailmime_free(report_parsed);
}
if !to_mmap_string_unref.is_null() {
mmap_string_unref(to_mmap_string_unref);
}
}
}
}
@@ -1178,26 +1150,20 @@ unsafe fn create_or_lookup_group(
// check, if we have a chat with this group ID
let (mut chat_id, chat_id_verified, _blocked) = chat::get_chat_id_by_grpid(context, &grpid);
if chat_id != 0 {
let mut failure_reason = std::ptr::null_mut();
if chat_id_verified
&& 0 == check_verified_properties(
context,
mime_parser,
from_id as u32,
to_ids,
&mut failure_reason,
)
{
mime_parser.repl_msg_by_error(to_string(failure_reason));
if chat_id_verified {
if let Err(err) =
check_verified_properties(context, mime_parser, from_id as u32, to_ids)
{
warn!(context, "verification problem: {}", err);
let s = format!("{}. See 'Info' for more details", err);
mime_parser.repl_msg_by_error(s);
}
}
free(failure_reason.cast());
}
// check if the sender is a member of the existing group -
// if not, we'll recreate the group list
if chat_id != 0 && 0 == chat::is_contact_in_chat(context, chat_id, from_id as u32) {
if chat_id != 0 && !chat::is_contact_in_chat(context, chat_id, from_id as u32) {
recreate_member_list = 1;
}
@@ -1221,18 +1187,14 @@ unsafe fn create_or_lookup_group(
let mut create_verified = VerifiedStatus::Unverified;
if mime_parser.lookup_field("Chat-Verified").is_some() {
create_verified = VerifiedStatus::Verified;
let mut failure_reason = std::ptr::null_mut();
if 0 == check_verified_properties(
context,
mime_parser,
from_id as u32,
to_ids,
&mut failure_reason,
) {
mime_parser.repl_msg_by_error(to_string(failure_reason));
if let Err(err) =
check_verified_properties(context, mime_parser, from_id as u32, to_ids)
{
warn!(context, "verification problem: {}", err);
let s = format!("{}. See 'Info' for more details", err);
mime_parser.repl_msg_by_error(&s);
}
free(failure_reason.cast());
}
if 0 == allow_creation {
cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked);
@@ -1639,51 +1601,41 @@ fn search_chat_ids_by_contact_ids(context: &Context, unsorted_contact_ids: &Vec<
chat_ids
}
unsafe fn check_verified_properties(
fn check_verified_properties(
context: &Context,
mimeparser: &MimeParser,
from_id: u32,
to_ids: &Vec<u32>,
failure_reason: *mut *mut libc::c_char,
) -> libc::c_int {
let verify_fail = |reason: String| {
*failure_reason = format!("{}. See \"Info\" for details.", reason).strdup();
warn!(context, "{}", reason);
};
) -> Result<()> {
let contact = Contact::load_from_db(context, from_id)?;
let contact = match Contact::load_from_db(context, from_id) {
Ok(contact) => contact,
Err(_err) => {
verify_fail("Internal Error; cannot load contact".into());
return 0;
}
};
if !mimeparser.e2ee_helper.encrypted {
verify_fail("This message is not encrypted".into());
return 0;
}
ensure!(
mimeparser.e2ee_helper.encrypted,
"This message is not encrypted."
);
// ensure, the contact is verified
// and the message is signed with a verified key of the sender.
// this check is skipped for SELF as there is no proper SELF-peerstate
// and results in group-splits otherwise.
if from_id != 1 {
if from_id != DC_CONTACT_ID_SELF {
let peerstate = Peerstate::from_addr(context, &context.sql, contact.get_addr());
if peerstate.is_none()
|| contact.is_verified_ex(context, peerstate.as_ref())
!= VerifiedStatus::BidirectVerified
{
verify_fail("The sender of this message is not verified.".into());
return 0;
bail!(
"Sender of this message is not verified: {}",
contact.get_addr()
);
}
if let Some(peerstate) = peerstate {
if !peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures) {
verify_fail("The message was sent with non-verified encryption.".into());
return 0;
}
ensure!(
peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures),
"The message was sent with non-verified encryption."
);
}
}
@@ -1696,18 +1648,18 @@ unsafe fn check_verified_properties(
to_ids_str,
),
params![],
|row| Ok((row.get::<_, String>(0)?, row.get::<_, i32>(1)?)),
|row| Ok((row.get::<_, String>(0)?, row.get::<_, i32>(1).unwrap_or(0))),
|rows| {
rows.collect::<std::result::Result<Vec<_>, _>>()
.map_err(Into::into)
},
);
)?;
if rows.is_err() {
return 0;
}
for (to_addr, mut is_verified) in rows.unwrap().into_iter() {
for (to_addr, _is_verified) in rows.into_iter() {
let mut is_verified = _is_verified != 0;
let mut peerstate = Peerstate::from_addr(context, &context.sql, &to_addr);
// mark gossiped keys (if any) as verified
if mimeparser.e2ee_helper.gossipped_addr.contains(&to_addr) && peerstate.is_some() {
let peerstate = peerstate.as_mut().unwrap();
@@ -1716,29 +1668,27 @@ unsafe fn check_verified_properties(
// - OR if the verified-key does not match public-key or gossip-key
// (otherwise a verified key can _only_ be updated through QR scan which might be annoying,
// see https://github.com/nextleap-project/countermitm/issues/46 for a discussion about this point)
if 0 == is_verified
if !is_verified
|| peerstate.verified_key_fingerprint != peerstate.public_key_fingerprint
&& peerstate.verified_key_fingerprint != peerstate.gossip_key_fingerprint
{
info!(context, "{} has verfied {}.", contact.get_addr(), to_addr,);
info!(context, "{} has verified {}.", contact.get_addr(), to_addr,);
let fp = peerstate.gossip_key_fingerprint.clone();
if let Some(fp) = fp {
peerstate.set_verified(0, &fp, 2);
peerstate.save_to_db(&context.sql, false);
is_verified = 1;
peerstate.save_to_db(&context.sql, false).unwrap();
is_verified = true;
}
}
}
if 0 == is_verified {
verify_fail(format!(
if !is_verified {
bail!(
"{} is not a member of this verified group",
to_addr
));
return 0;
to_addr.to_string()
);
}
}
1
Ok(())
}
fn set_better_msg(mime_parser: &mut MimeParser, better_msg: impl AsRef<str>) {
@@ -1870,11 +1820,11 @@ unsafe fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: *const clis
while !cur.is_null() {
if 0 != is_msgrmsg_rfc724_mid(
context,
(if !cur.is_null() {
(*cur).data
if !cur.is_null() {
as_str((*cur).data as *const libc::c_char)
} else {
ptr::null_mut()
}) as *const libc::c_char,
""
},
) {
return 1;
}
@@ -1889,15 +1839,15 @@ unsafe fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: *const clis
}
/// Check if a message is a reply to any messenger message.
fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) -> libc::c_int {
if rfc724_mid.is_null() {
fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: &str) -> libc::c_int {
if rfc724_mid.is_empty() {
return 0;
}
context
.sql
.exists(
"SELECT id FROM msgs WHERE rfc724_mid=? AND msgrmsg!=0 AND chat_id>9;",
params![as_str(rfc724_mid)],
params![rfc724_mid],
)
.unwrap_or_default() as libc::c_int
}

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,10 @@ pub enum Error {
CStringError(crate::dc_tools::CStringError),
#[fail(display = "PGP: {:?}", _0)]
Pgp(pgp::errors::Error),
#[fail(display = "Base64Decode: {:?}", _0)]
Base64Decode(base64::DecodeError),
#[fail(display = "{:?}", _0)]
FromUtf8(std::string::FromUtf8Error),
}
pub type Result<T> = std::result::Result<T, Error>;
@@ -36,6 +40,12 @@ impl From<rusqlite::Error> for Error {
}
}
impl From<base64::DecodeError> for Error {
fn from(err: base64::DecodeError) -> Error {
Error::Base64Decode(err)
}
}
impl From<failure::Error> for Error {
fn from(err: failure::Error) -> Error {
Error::Failure(err)
@@ -60,6 +70,12 @@ impl From<std::str::Utf8Error> for Error {
}
}
impl From<std::string::FromUtf8Error> for Error {
fn from(err: std::string::FromUtf8Error) -> Error {
Error::FromUtf8(err)
}
}
impl From<image_meta::ImageError> for Error {
fn from(err: image_meta::ImageError) -> Error {
Error::Image(err)

View File

@@ -167,7 +167,7 @@ pub enum Event {
#[strum(props(id = "2041"))]
ConfigureProgress(usize),
/// Inform about the import/export progress started by dc_imex().
/// Inform about the import/export progress started by imex().
///
/// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done
/// @param data2 0
@@ -175,8 +175,8 @@ pub enum Event {
#[strum(props(id = "2051"))]
ImexProgress(usize),
/// A file has been exported. A file has been written by dc_imex().
/// This event may be sent multiple times by a single call to dc_imex().
/// A file has been exported. A file has been written by imex().
/// This event may be sent multiple times by a single call to imex().
///
/// A typical purpose for a handler of this event may be to make the file public to some system
/// services.

View File

@@ -1,6 +1,4 @@
use std::ffi::CString;
use std::net;
use std::ptr;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, Condvar, Mutex, RwLock,
@@ -10,21 +8,22 @@ use std::time::{Duration, SystemTime};
use crate::constants::*;
use crate::context::Context;
use crate::dc_receive_imf::dc_receive_imf;
use crate::dc_tools::CStringExt;
use crate::dc_tools::*;
use crate::events::Event;
use crate::job::{job_add, Action};
use crate::login_param::LoginParam;
use crate::message::{dc_rfc724_mid_exists, dc_update_msg_move_state, dc_update_server_uid};
use crate::message::{self, update_msg_move_state, update_server_uid};
use crate::oauth2::dc_get_oauth2_access_token;
use crate::param::Params;
const DC_IMAP_SEEN: usize = 0x0001;
const DC_SUCCESS: usize = 3;
const DC_ALREADY_DONE: usize = 2;
const DC_RETRY_LATER: usize = 1;
const DC_FAILED: usize = 0;
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq)]
pub enum ImapResult {
Failed,
RetryLater,
AlreadyDone,
Success,
}
const PREFETCH_FLAGS: &str = "(UID ENVELOPE)";
const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])";
@@ -111,6 +110,8 @@ impl Client {
) -> imap::error::Result<Self> {
let stream = net::TcpStream::connect(addr)?;
let tls = native_tls::TlsConnector::builder()
// see also: https://github.com/deltachat/deltachat-core-rust/issues/203
.danger_accept_invalid_certs(true)
.danger_accept_invalid_hostnames(true)
.build()
.unwrap();
@@ -821,10 +822,7 @@ impl Imap {
.message_id
.expect("missing message id");
if 0 == unsafe {
let message_id_c = CString::yolo(message_id);
precheck_imf(context, message_id_c.as_ptr(), folder.as_ref(), cur_uid)
} {
if !precheck_imf(context, &message_id, folder.as_ref(), cur_uid) {
// check passed, go fetch the rest
if self.fetch_single_msg(context, &folder, cur_uid) == 0 {
info!(
@@ -957,14 +955,7 @@ impl Imap {
if !is_deleted && msg.body().is_some() {
let body = msg.body().unwrap();
unsafe {
dc_receive_imf(
context,
body.as_ptr() as *const libc::c_char,
body.len(),
folder.as_ref(),
server_uid,
flags as u32,
);
dc_receive_imf(context, &body, folder.as_ref(), server_uid, flags as u32);
}
}
}
@@ -1135,12 +1126,12 @@ impl Imap {
uid: u32,
dest_folder: S2,
dest_uid: &mut u32,
) -> usize {
let mut res = DC_RETRY_LATER;
) -> ImapResult {
let mut res = ImapResult::RetryLater;
let set = format!("{}", uid);
if uid == 0 {
res = DC_FAILED;
res = ImapResult::Failed;
} else if folder.as_ref() == dest_folder.as_ref() {
info!(
context,
@@ -1150,7 +1141,7 @@ impl Imap {
dest_folder.as_ref()
);
res = DC_ALREADY_DONE;
res = ImapResult::AlreadyDone;
} else {
info!(
context,
@@ -1170,7 +1161,7 @@ impl Imap {
let moved = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_mv(&set, &dest_folder) {
Ok(_) => {
res = DC_SUCCESS;
res = ImapResult::Success;
true
}
Err(err) => {
@@ -1210,22 +1201,22 @@ impl Imap {
warn!(context, "Cannot mark message as \"Deleted\".",);
}
self.config.write().unwrap().selected_folder_needs_expunge = true;
res = DC_SUCCESS;
res = ImapResult::Success;
}
}
}
}
if res == DC_SUCCESS {
if res == ImapResult::Success {
// TODO: is this correct?
*dest_uid = uid;
}
if res == DC_RETRY_LATER {
if res == ImapResult::RetryLater {
if self.should_reconnect() {
DC_RETRY_LATER
ImapResult::RetryLater
} else {
DC_FAILED
ImapResult::Failed
}
} else {
res
@@ -1259,11 +1250,11 @@ impl Imap {
}
}
pub fn set_seen<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> usize {
let mut res = DC_RETRY_LATER;
pub fn set_seen<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> ImapResult {
let mut res = ImapResult::RetryLater;
if uid == 0 {
res = DC_FAILED
res = ImapResult::Failed
} else if self.is_connected() {
info!(
context,
@@ -1281,28 +1272,28 @@ impl Imap {
} else if self.add_flag(context, uid, "\\Seen") == 0 {
warn!(context, "Cannot mark message as seen.",);
} else {
res = DC_SUCCESS
res = ImapResult::Success
}
}
if res == DC_RETRY_LATER {
if res == ImapResult::RetryLater {
if self.should_reconnect() {
DC_RETRY_LATER
ImapResult::RetryLater
} else {
DC_FAILED
ImapResult::Failed
}
} else {
res
}
}
pub fn set_mdnsent<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> usize {
pub fn set_mdnsent<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> ImapResult {
// returns 0=job should be retried later, 1=job done, 2=job done and flag just set
let mut res = DC_RETRY_LATER;
let mut res = ImapResult::RetryLater;
let set = format!("{}", uid);
if uid == 0 {
res = DC_FAILED;
res = ImapResult::Failed;
} else if self.is_connected() {
info!(
context,
@@ -1372,21 +1363,21 @@ impl Imap {
.unwrap_or_else(|| false);
res = if flag_set {
DC_ALREADY_DONE
ImapResult::AlreadyDone
} else if self.add_flag(context, uid, "$MDNSent") != 0 {
DC_SUCCESS
ImapResult::Success
} else {
res
};
if res == DC_SUCCESS {
if res == ImapResult::Success {
info!(context, "$MDNSent just set and MDN will be sent.");
} else {
info!(context, "$MDNSent already set and MDN already sent.");
}
}
} else {
res = DC_SUCCESS;
res = ImapResult::Success;
info!(
context,
"Cannot store $MDNSent flags, risk sending duplicate MDN.",
@@ -1395,11 +1386,11 @@ impl Imap {
}
}
if res == DC_RETRY_LATER {
if res == ImapResult::RetryLater {
if self.should_reconnect() {
DC_RETRY_LATER
ImapResult::RetryLater
} else {
DC_FAILED
ImapResult::Failed
}
} else {
res
@@ -1643,43 +1634,28 @@ fn get_folder_meaning(folder_name: &imap::types::Name) -> FolderMeaning {
}
}
unsafe fn precheck_imf(
context: &Context,
rfc724_mid: *const libc::c_char,
server_folder: &str,
server_uid: u32,
) -> libc::c_int {
let mut rfc724_mid_exists: libc::c_int = 0i32;
let msg_id: u32;
let mut old_server_folder: *mut libc::c_char = ptr::null_mut();
let mut old_server_uid: u32 = 0i32 as u32;
let mut mark_seen: libc::c_int = 0i32;
msg_id = dc_rfc724_mid_exists(
context,
rfc724_mid,
&mut old_server_folder,
&mut old_server_uid,
);
if msg_id != 0i32 as libc::c_uint {
rfc724_mid_exists = 1i32;
if *old_server_folder.offset(0isize) as libc::c_int == 0i32
&& old_server_uid == 0i32 as libc::c_uint
{
info!(context, "[move] detected bbc-self {}", as_str(rfc724_mid),);
mark_seen = 1i32
} else if as_str(old_server_folder) != server_folder {
info!(
context,
"[move] detected moved message {}",
as_str(rfc724_mid),
);
dc_update_msg_move_state(context, rfc724_mid, MoveState::Stay);
fn precheck_imf(context: &Context, rfc724_mid: &str, server_folder: &str, server_uid: u32) -> bool {
let mut rfc724_mid_exists = false;
let mut mark_seen = false;
if let Ok((old_server_folder, old_server_uid, msg_id)) =
message::rfc724_mid_exists(context, &rfc724_mid)
{
rfc724_mid_exists = true;
if old_server_folder.is_empty() && old_server_uid == 0 {
info!(context, "[move] detected bbc-self {}", rfc724_mid,);
mark_seen = true;
} else if old_server_folder != server_folder {
info!(context, "[move] detected moved message {}", rfc724_mid,);
update_msg_move_state(context, &rfc724_mid, MoveState::Stay);
}
if as_str(old_server_folder) != server_folder || old_server_uid != server_uid {
dc_update_server_uid(context, rfc724_mid, server_folder, server_uid);
if old_server_folder != server_folder || old_server_uid != server_uid {
update_server_uid(context, &rfc724_mid, server_folder, server_uid);
}
context.do_heuristics_moves(server_folder, msg_id);
if 0 != mark_seen {
if mark_seen {
job_add(
context,
Action::MarkseenMsgOnImap,
@@ -1689,6 +1665,6 @@ unsafe fn precheck_imf(
);
}
}
libc::free(old_server_folder as *mut libc::c_void);
rfc724_mid_exists
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,22 @@
use std::ffi::CStr;
use std::ptr;
use std::time::Duration;
use deltachat_derive::{FromSql, ToSql};
use mmime::clist::*;
use rand::{thread_rng, Rng};
use crate::chat;
use crate::configure::*;
use crate::constants::*;
use crate::context::Context;
use crate::dc_imex::*;
use crate::dc_mimefactory::*;
use crate::dc_tools::*;
use crate::events::Event;
use crate::imap::*;
use crate::imex::*;
use crate::location;
use crate::login_param::LoginParam;
use crate::message::*;
use crate::message::{self, Message, MessageState};
use crate::mimefactory::{vec_contains_lowercase, Loaded, MimeFactory};
use crate::param::*;
use crate::sql;
use crate::x::*;
/// Thread IDs
#[derive(Debug, Display, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
@@ -141,7 +137,7 @@ impl Job {
}
if let Some(filename) = self.param.get(Param::File) {
if let Some(body) = dc_read_file_safe(context, filename) {
if let Ok(body) = dc_read_file(context, filename) {
if let Some(recipients) = self.param.get(Param::Recipients) {
let recipients_list = recipients
.split("\x1e")
@@ -157,7 +153,7 @@ impl Job {
/* if there is a msg-id and it does not exist in the db, cancel sending.
this happends if dc_delete_msgs() was called
before the generated mime was sent out */
if 0 != self.foreign_id && !dc_msg_exists(context, self.foreign_id) {
if 0 != self.foreign_id && !message::exists(context, self.foreign_id) {
warn!(
context,
"Message {} for job {} does not exist", self.foreign_id, self.job_id,
@@ -176,7 +172,7 @@ impl Job {
} else {
dc_delete_file(context, filename);
if 0 != self.foreign_id {
dc_update_msg_state(
message::update_msg_state(
context,
self.foreign_id,
MessageState::OutDelivered,
@@ -210,53 +206,49 @@ impl Job {
#[allow(non_snake_case)]
fn do_DC_JOB_MOVE_MSG(&mut self, context: &Context) {
let ok_to_continue;
let mut dest_uid = 0;
let inbox = context.inbox.read().unwrap();
if !inbox.is_connected() {
connect_to_inbox(context, &inbox);
if !inbox.is_connected() {
self.try_again_later(3, None);
ok_to_continue = false;
} else {
ok_to_continue = true;
return;
}
} else {
ok_to_continue = true;
}
if ok_to_continue {
if let Ok(msg) = dc_msg_load_from_db(context, self.foreign_id) {
if context
.sql
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
inbox.configure_folders(context, 0x1i32);
}
let dest_folder = context.sql.get_config(context, "configured_mvbox_folder");
if let Ok(msg) = Message::load_from_db(context, self.foreign_id) {
if context
.sql
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
inbox.configure_folders(context, 0x1i32);
}
let dest_folder = context.sql.get_config(context, "configured_mvbox_folder");
if let Some(dest_folder) = dest_folder {
let server_folder = msg.server_folder.as_ref().unwrap();
if let Some(dest_folder) = dest_folder {
let server_folder = msg.server_folder.as_ref().unwrap();
let mut dest_uid = 0;
match inbox.mv(
context,
server_folder,
msg.server_uid,
&dest_folder,
&mut dest_uid,
) as libc::c_uint
{
1 => {
self.try_again_later(3i32, None);
}
3 => {
dc_update_server_uid(context, msg.rfc724_mid, &dest_folder, dest_uid);
}
0 | 2 | _ => {}
match inbox.mv(
context,
server_folder,
msg.server_uid,
&dest_folder,
&mut dest_uid,
) {
ImapResult::RetryLater => {
self.try_again_later(3i32, None);
}
ImapResult::Success => {
message::update_server_uid(
context,
&msg.rfc724_mid,
&dest_folder,
dest_uid,
);
}
ImapResult::Failed | ImapResult::AlreadyDone => {}
}
}
}
@@ -264,101 +256,75 @@ impl Job {
#[allow(non_snake_case)]
fn do_DC_JOB_DELETE_MSG_ON_IMAP(&mut self, context: &Context) {
let mut delete_from_server = 1;
let inbox = context.inbox.read().unwrap();
if let Ok(mut msg) = dc_msg_load_from_db(context, self.foreign_id) {
if !(msg.rfc724_mid.is_null()
|| unsafe { *msg.rfc724_mid.offset(0isize) as libc::c_int == 0 })
{
let ok_to_continue1;
if let Ok(mut msg) = Message::load_from_db(context, self.foreign_id) {
if !msg.rfc724_mid.is_empty() {
/* eg. device messages have no Message-ID */
if dc_rfc724_mid_cnt(context, msg.rfc724_mid) != 1 {
let mut delete_from_server = true;
if message::rfc724_mid_cnt(context, &msg.rfc724_mid) != 1 {
info!(
context,
"The message is deleted from the server when all parts are deleted.",
);
delete_from_server = 0i32
delete_from_server = false;
}
/* if this is the last existing part of the message, we delete the message from the server */
if 0 != delete_from_server {
let ok_to_continue;
if delete_from_server {
if !inbox.is_connected() {
connect_to_inbox(context, &inbox);
if !inbox.is_connected() {
self.try_again_later(3i32, None);
ok_to_continue = false;
} else {
ok_to_continue = true;
return;
}
} else {
ok_to_continue = true;
}
if ok_to_continue {
let mid = unsafe { CStr::from_ptr(msg.rfc724_mid).to_str().unwrap() };
let server_folder = msg.server_folder.as_ref().unwrap();
if 0 == inbox.delete_msg(context, mid, server_folder, &mut msg.server_uid) {
self.try_again_later(-1i32, None);
ok_to_continue1 = false;
} else {
ok_to_continue1 = true;
}
} else {
ok_to_continue1 = false;
let mid = msg.rfc724_mid;
let server_folder = msg.server_folder.as_ref().unwrap();
if 0 == inbox.delete_msg(context, &mid, server_folder, &mut msg.server_uid) {
self.try_again_later(-1i32, None);
return;
}
} else {
ok_to_continue1 = true;
}
if ok_to_continue1 {
dc_delete_msg_from_db(context, msg.id);
}
Message::delete_from_db(context, msg.id);
}
}
}
#[allow(non_snake_case)]
fn do_DC_JOB_MARKSEEN_MSG_ON_IMAP(&mut self, context: &Context) {
let ok_to_continue;
let inbox = context.inbox.read().unwrap();
if !inbox.is_connected() {
connect_to_inbox(context, &inbox);
if !inbox.is_connected() {
self.try_again_later(3i32, None);
ok_to_continue = false;
} else {
ok_to_continue = true;
return;
}
} else {
ok_to_continue = true;
}
if ok_to_continue {
if let Ok(msg) = dc_msg_load_from_db(context, self.foreign_id) {
let server_folder = msg.server_folder.as_ref().unwrap();
match inbox.set_seen(context, server_folder, msg.server_uid) as libc::c_uint {
0 => {}
1 => {
self.try_again_later(3i32, None);
}
_ => {
if 0 != msg.param.get_int(Param::WantsMdn).unwrap_or_default()
&& 0 != context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1)
{
let folder = msg.server_folder.as_ref().unwrap();
if let Ok(msg) = Message::load_from_db(context, self.foreign_id) {
let server_folder = msg.server_folder.as_ref().unwrap();
match inbox.set_seen(context, server_folder, msg.server_uid) {
ImapResult::Failed => {}
ImapResult::RetryLater => {
self.try_again_later(3i32, None);
}
_ => {
if 0 != msg.param.get_int(Param::WantsMdn).unwrap_or_default()
&& 0 != context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1)
{
let folder = msg.server_folder.as_ref().unwrap();
match inbox.set_mdnsent(context, folder, msg.server_uid) as libc::c_uint
{
1 => {
self.try_again_later(3i32, None);
}
3 => {
send_mdn(context, msg.id);
}
0 | 2 | _ => {}
match inbox.set_mdnsent(context, folder, msg.server_uid) {
ImapResult::RetryLater => {
self.try_again_later(3i32, None);
}
ImapResult::Success => {
send_mdn(context, msg.id);
}
ImapResult::Failed | ImapResult::AlreadyDone => {}
}
}
}
@@ -368,47 +334,40 @@ impl Job {
#[allow(non_snake_case)]
fn do_DC_JOB_MARKSEEN_MDN_ON_IMAP(&mut self, context: &Context) {
let ok_to_continue;
let folder = self
.param
.get(Param::ServerFolder)
.unwrap_or_default()
.to_string();
let uid = self.param.get_int(Param::ServerUid).unwrap_or_default() as u32;
let mut dest_uid = 0;
let inbox = context.inbox.read().unwrap();
if !inbox.is_connected() {
connect_to_inbox(context, &inbox);
if !inbox.is_connected() {
self.try_again_later(3, None);
ok_to_continue = false;
} else {
ok_to_continue = true;
return;
}
} else {
ok_to_continue = true;
}
if ok_to_continue {
if inbox.set_seen(context, &folder, uid) == 0 {
self.try_again_later(3i32, None);
if inbox.set_seen(context, &folder, uid) == ImapResult::Failed {
self.try_again_later(3i32, None);
}
if 0 != self.param.get_int(Param::AlsoMove).unwrap_or_default() {
if context
.sql
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
inbox.configure_folders(context, 0x1i32);
}
if 0 != self.param.get_int(Param::AlsoMove).unwrap_or_default() {
if context
.sql
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
let dest_folder = context.sql.get_config(context, "configured_mvbox_folder");
if let Some(dest_folder) = dest_folder {
let mut dest_uid = 0;
if ImapResult::RetryLater
== inbox.mv(context, folder, uid, dest_folder, &mut dest_uid)
{
inbox.configure_folders(context, 0x1i32);
}
let dest_folder = context.sql.get_config(context, "configured_mvbox_folder");
if let Some(dest_folder) = dest_folder {
if 1 == inbox.mv(context, folder, uid, dest_folder, &mut dest_uid)
as libc::c_uint
{
self.try_again_later(3, None);
}
self.try_again_later(3, None);
}
}
}
@@ -642,12 +601,12 @@ pub fn job_action_exists(context: &Context, action: Action) -> bool {
/* special case for DC_JOB_SEND_MSG_TO_SMTP */
#[allow(non_snake_case)]
pub unsafe fn job_send_msg(context: &Context, msg_id: u32) -> libc::c_int {
pub fn job_send_msg(context: &Context, msg_id: u32) -> libc::c_int {
let mut success = 0;
/* load message data */
let mimefactory = dc_mimefactory_load_msg(context, msg_id);
if mimefactory.is_err() || mimefactory.as_ref().unwrap().from_addr.is_null() {
let mimefactory = MimeFactory::load_msg(context, msg_id);
if mimefactory.is_err() {
warn!(
context,
"Cannot load data to send, maybe the message is deleted in between.",
@@ -669,53 +628,48 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: u32) -> libc::c_int {
mimefactory.msg.param.set_int(Param::Width, 0);
mimefactory.msg.param.set_int(Param::Height, 0);
if let Some(buf) = dc_read_file_safe(context, pathNfilename) {
if let Ok(buf) = dc_read_file(context, pathNfilename) {
if let Ok((width, height)) = dc_get_filemeta(&buf) {
mimefactory.msg.param.set_int(Param::Width, width as i32);
mimefactory.msg.param.set_int(Param::Height, height as i32);
}
}
dc_msg_save_param_to_disk(context, &mut mimefactory.msg);
mimefactory.msg.save_param_to_disk(context);
}
}
}
/* create message */
if !dc_mimefactory_render(context, &mut mimefactory) {
dc_set_msg_failed(context, msg_id, as_opt_str(mimefactory.error));
if let Err(msg) = unsafe { mimefactory.render() } {
let e = msg.to_string();
message::set_msg_failed(context, msg_id, Some(e));
} else if 0
!= mimefactory
.msg
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
&& 0 == mimefactory.out_encrypted
&& !mimefactory.out_encrypted
{
/* unrecoverable */
warn!(
context,
"e2e encryption unavailable {} - {:?}",
msg_id,
mimefactory.msg.param.get_int(Param::GuranteeE2ee),
);
dc_set_msg_failed(
message::set_msg_failed(
context,
msg_id,
Some("End-to-end-encryption unavailable unexpectedly."),
);
} else {
/* unrecoverable */
if !clist_search_string_nocase(mimefactory.recipients_addr, mimefactory.from_addr) {
clist_insert_after(
mimefactory.recipients_names,
(*mimefactory.recipients_names).last,
ptr::null_mut(),
);
clist_insert_after(
mimefactory.recipients_addr,
(*mimefactory.recipients_addr).last,
dc_strdup(mimefactory.from_addr) as *mut libc::c_void,
);
if !vec_contains_lowercase(&mimefactory.recipients_addr, &mimefactory.from_addr) {
mimefactory.recipients_names.push("".to_string());
mimefactory
.recipients_addr
.push(mimefactory.from_addr.to_string());
}
if 0 != mimefactory.out_gossiped {
if mimefactory.out_gossiped {
chat::set_gossiped_timestamp(context, mimefactory.msg.chat_id, time());
}
if 0 != mimefactory.out_last_added_location_id {
@@ -734,7 +688,7 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: u32) -> libc::c_int {
}
}
}
if 0 != mimefactory.out_encrypted
if mimefactory.out_encrypted
&& mimefactory
.msg
.param
@@ -743,7 +697,7 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: u32) -> libc::c_int {
== 0
{
mimefactory.msg.param.set_int(Param::GuranteeE2ee, 1);
dc_msg_save_param_to_disk(context, &mut mimefactory.msg);
mimefactory.msg.save_param_to_disk(context);
}
success = add_smtp_job(context, Action::SendMsgToSmtp, &mut mimefactory);
}
@@ -763,6 +717,14 @@ pub fn perform_imap_jobs(context: &Context) {
info!(context, "dc_perform_imap_jobs ended.",);
}
pub fn perform_mvbox_jobs(context: &Context) {
info!(context, "dc_perform_mbox_jobs EMPTY (for now).",);
}
pub fn perform_sentbox_jobs(context: &Context) {
info!(context, "dc_perform_sentbox_jobs EMPTY (for now).",);
}
fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
let query = if !probe_network {
// processing for first-try and after backoff-timeouts:
@@ -861,8 +823,13 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
Action::MarkseenMdnOnImap => job.do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context),
Action::MoveMsg => job.do_DC_JOB_MOVE_MSG(context),
Action::SendMdn => job.do_DC_JOB_SEND(context),
Action::ConfigureImap => unsafe { dc_job_do_DC_JOB_CONFIGURE_IMAP(context, &job) },
Action::ImexImap => unsafe { dc_job_do_DC_JOB_IMEX_IMAP(context, &job) },
Action::ConfigureImap => unsafe { dc_job_do_DC_JOB_CONFIGURE_IMAP(context) },
Action::ImexImap => match job_do_DC_JOB_IMEX_IMAP(context, &job) {
Ok(()) => {}
Err(err) => {
error!(context, "{}", err);
}
},
Action::MaybeSendLocations => {
location::job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context, &job)
}
@@ -936,7 +903,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
}
} else {
if job.action == Action::SendMsgToSmtp {
dc_set_msg_failed(context, job.foreign_id, job.pending_error.as_ref());
message::set_msg_failed(context, job.foreign_id, job.pending_error.as_ref());
}
job.delete(context);
}
@@ -989,20 +956,18 @@ fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int {
}
fn send_mdn(context: &Context, msg_id: u32) {
if let Ok(mut mimefactory) = unsafe { dc_mimefactory_load_mdn(context, msg_id) } {
if unsafe { dc_mimefactory_render(context, &mut mimefactory) } {
if let Ok(mut mimefactory) = MimeFactory::load_mdn(context, msg_id) {
if unsafe { mimefactory.render() }.is_ok() {
add_smtp_job(context, Action::SendMdn, &mut mimefactory);
}
}
}
#[allow(non_snake_case)]
fn add_smtp_job(context: &Context, action: Action, mimefactory: &dc_mimefactory_t) -> libc::c_int {
fn add_smtp_job(context: &Context, action: Action, mimefactory: &MimeFactory) -> libc::c_int {
let mut success: libc::c_int = 0i32;
let mut recipients: *mut libc::c_char = ptr::null_mut();
let mut param = Params::new();
let path_filename =
dc_get_fine_path_filename(context, "$BLOBDIR", as_str(mimefactory.rfc724_mid));
let path_filename = dc_get_fine_path_filename(context, "$BLOBDIR", &mimefactory.rfc724_mid);
let bytes = unsafe {
std::slice::from_raw_parts(
(*mimefactory.out).str_0 as *const u8,
@@ -1013,24 +978,18 @@ fn add_smtp_job(context: &Context, action: Action, mimefactory: &dc_mimefactory_
error!(
context,
"Could not write message <{}> to \"{}\".",
to_string(mimefactory.rfc724_mid),
mimefactory.rfc724_mid,
path_filename.display(),
);
} else {
recipients = unsafe {
dc_str_from_clist(
mimefactory.recipients_addr,
b"\x1e\x00" as *const u8 as *const libc::c_char,
)
};
info!(context, "add_smtp_job file written: {:?}", path_filename);
let recipients = mimefactory.recipients_addr.join("\x1e");
param.set(Param::File, path_filename.to_string_lossy());
param.set(Param::Recipients, as_str(recipients));
param.set(Param::Recipients, &recipients);
job_add(
context,
action,
(if mimefactory.loaded as libc::c_uint
== DC_MF_MSG_LOADED as libc::c_int as libc::c_uint
{
(if mimefactory.loaded == Loaded::Message {
mimefactory.msg.id
} else {
0
@@ -1040,9 +999,6 @@ fn add_smtp_job(context: &Context, action: Action, mimefactory: &dc_mimefactory_
);
success = 1;
}
unsafe {
free(recipients.cast());
}
success
}

View File

@@ -2,7 +2,6 @@ use std::collections::BTreeMap;
use std::io::Cursor;
use std::path::Path;
use libc;
use pgp::composed::{Deserializable, SignedPublicKey, SignedSecretKey};
use pgp::ser::Serialize;
use pgp::types::{KeyTrait, SecretKeyTrait};
@@ -254,14 +253,14 @@ pub fn dc_key_save_self_keypair(
public_key: &Key,
private_key: &Key,
addr: impl AsRef<str>,
is_default: libc::c_int,
is_default: bool,
sql: &Sql,
) -> bool {
sql::execute(
context,
sql,
"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()],
params![addr.as_ref(), is_default as i32, public_key.to_bytes(), private_key.to_bytes(), time()],
).is_ok()
}

View File

@@ -39,35 +39,36 @@ pub mod contact;
pub mod context;
mod e2ee;
mod imap;
pub mod imex;
pub mod job;
mod job_thread;
pub mod key;
pub mod keyring;
pub mod location;
mod login_param;
pub mod lot;
pub mod message;
mod mimefactory;
pub mod oauth2;
mod param;
pub mod peerstate;
pub mod pgp;
pub mod qr;
pub mod securejoin;
mod smtp;
pub mod sql;
mod stock;
pub mod x;
mod token;
#[macro_use]
mod wrapmime;
pub mod dc_array;
mod dc_dehtml;
pub mod dc_imex;
mod dc_mimefactory;
pub mod dc_mimeparser;
pub mod dc_receive_imf;
mod dc_simplify;
mod dc_strencode;
pub mod dc_tools;
mod login_param;
pub mod securejoin;
mod token;
#[cfg(test)]
mod test_utils;

View File

@@ -9,7 +9,7 @@ use crate::dc_tools::*;
use crate::error::Error;
use crate::events::Event;
use crate::job::*;
use crate::message::*;
use crate::message::Message;
use crate::param::*;
use crate::sql;
use crate::stock::StockMessage;
@@ -214,7 +214,7 @@ pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) {
.is_ok()
{
if 0 != seconds && !is_sending_locations_before {
msg = dc_msg_new(Viewtype::Text);
msg = Message::new(Viewtype::Text);
msg.text =
Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0));
msg.param.set_int(Param::Cmd, 8);
@@ -601,7 +601,7 @@ pub fn job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Job) {
// the easiest way to determine this, is to check for an empty message queue.
// (might not be 100%, however, as positions are sent combined later
// and dc_set_location() is typically called periodically, this is ok)
let mut msg = dc_msg_new(Viewtype::Text);
let mut msg = Message::new(Viewtype::Text);
msg.hidden = true;
msg.param.set_int(Param::Cmd, 9);
Some((chat_id, msg))

File diff suppressed because it is too large Load Diff

954
src/mimefactory.rs Normal file
View File

@@ -0,0 +1,954 @@
use std::path::Path;
use std::ptr;
use chrono::TimeZone;
use mmime::clist::*;
use mmime::mailimf::types::*;
use mmime::mailimf::types_helper::*;
use mmime::mailmime::disposition::*;
use mmime::mailmime::types::*;
use mmime::mailmime::types_helper::*;
use mmime::mailmime::write_mem::*;
use mmime::mmapstring::*;
use mmime::other::*;
use crate::chat::{self, Chat};
use crate::constants::*;
use crate::contact::*;
use crate::context::{get_version_str, Context};
use crate::dc_mimeparser::{mailmime_find_mailimf_fields, SystemMessage};
use crate::dc_strencode::*;
use crate::dc_tools::*;
use crate::e2ee::*;
use crate::error::Error;
use crate::location;
use crate::message::{self, Message};
use crate::param::*;
use crate::stock::StockMessage;
use crate::wrapmime;
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Loaded {
Nothing,
Message,
MDN, // TODO: invent more descriptive name
}
#[derive(Clone)]
pub struct MimeFactory<'a> {
pub from_addr: String,
pub from_displayname: String,
pub selfstatus: String,
pub recipients_names: Vec<String>,
pub recipients_addr: Vec<String>,
pub timestamp: i64,
pub rfc724_mid: String,
pub loaded: Loaded,
pub msg: Message,
pub chat: Option<Chat>,
pub increation: bool,
pub in_reply_to: String,
pub references: String,
pub req_mdn: bool,
pub out: *mut MMAPString,
pub out_encrypted: bool,
pub out_gossiped: bool,
pub out_last_added_location_id: u32,
pub context: &'a Context,
}
impl<'a> MimeFactory<'a> {
fn new(context: &'a Context, msg: Message) -> Self {
let cget = |context: &Context, name: &str| context.sql.get_config(context, name);
MimeFactory {
from_addr: cget(&context, "configured_addr").unwrap_or_default(),
from_displayname: cget(&context, "displayname").unwrap_or_default(),
selfstatus: cget(&context, "selfstatus")
.unwrap_or_else(|| context.stock_str(StockMessage::StatusLine).to_string()),
recipients_names: Vec::with_capacity(5),
recipients_addr: Vec::with_capacity(5),
timestamp: 0,
rfc724_mid: String::default(),
loaded: Loaded::Nothing,
msg,
chat: None,
increation: false,
in_reply_to: String::default(),
references: String::default(),
req_mdn: false,
out: ptr::null_mut(),
out_encrypted: false,
out_gossiped: false,
out_last_added_location_id: 0,
context,
}
}
pub fn finalize_mime_message(
&mut self,
message: *mut Mailmime,
encrypted: bool,
gossiped: bool,
) -> Result<(), Error> {
unsafe {
assert!(self.out.is_null()); // guard against double-calls
self.out = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
let mut col: libc::c_int = 0;
ensure_eq!(
mailmime_write_mem(self.out, &mut col, message),
0,
"mem-error"
);
}
self.out_encrypted = encrypted;
self.out_gossiped = encrypted && gossiped;
Ok(())
}
pub fn load_mdn(context: &'a Context, msg_id: u32) -> Result<MimeFactory, Error> {
if 0 == context
.sql
.get_config_int(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 ...
bail!("MDNs disabled ")
}
let msg = Message::load_from_db(context, msg_id)?;
let mut factory = MimeFactory::new(context, msg);
let contact = Contact::load_from_db(factory.context, factory.msg.from_id)?;
// Do not send MDNs trash etc.; chats.blocked is already checked by the caller
// in dc_markseen_msgs()
ensure!(!contact.is_blocked(), "Contact blocked");
ensure!(
factory.msg.chat_id > DC_CHAT_ID_LAST_SPECIAL,
"Invalid chat id"
);
factory
.recipients_names
.push(contact.get_authname().to_string());
factory.recipients_addr.push(contact.get_addr().to_string());
factory.timestamp = dc_create_smeared_timestamp(factory.context);
factory.rfc724_mid = dc_create_outgoing_rfc724_mid(None, &factory.from_addr);
factory.loaded = Loaded::MDN;
Ok(factory)
}
/*******************************************************************************
* Render
******************************************************************************/
// restrict unsafe to parts, introduce wrapmime helpers where appropriate
pub unsafe fn render(&mut self) -> Result<(), Error> {
if self.loaded == Loaded::Nothing || !self.out.is_null() {
bail!("Invalid use of mimefactory-object.");
}
let context = &self.context;
/* create basic mail
*************************************************************************/
let from: *mut mailimf_mailbox_list = mailimf_mailbox_list_new_empty();
mailimf_mailbox_list_add(
from,
mailimf_mailbox_new(
if !self.from_displayname.is_empty() {
dc_encode_header_words(&self.from_displayname).strdup()
} else {
ptr::null_mut()
},
self.from_addr.strdup(),
),
);
let mut to: *mut mailimf_address_list = ptr::null_mut();
if !self.recipients_names.is_empty() && !self.recipients_addr.is_empty() {
to = mailimf_address_list_new_empty();
let name_iter = self.recipients_names.iter();
let addr_iter = self.recipients_addr.iter();
for (name, addr) in name_iter.zip(addr_iter) {
mailimf_address_list_add(
to,
mailimf_address_new(
MAILIMF_ADDRESS_MAILBOX as libc::c_int,
mailimf_mailbox_new(
if !name.is_empty() {
dc_encode_header_words(&name).strdup()
} else {
ptr::null_mut()
},
addr.strdup(),
),
ptr::null_mut(),
),
);
}
}
let references_list = if !self.references.is_empty() {
dc_str_to_clist(&self.references, " ")
} else {
ptr::null_mut()
};
let in_reply_to_list = if !self.in_reply_to.is_empty() {
dc_str_to_clist(&self.in_reply_to, " ")
} else {
ptr::null_mut()
};
let imf_fields = mailimf_fields_new_with_data_all(
mailimf_get_date(self.timestamp as i64),
from,
ptr::null_mut(),
ptr::null_mut(),
to,
ptr::null_mut(),
ptr::null_mut(),
self.rfc724_mid.strdup(),
in_reply_to_list,
references_list,
ptr::null_mut(),
);
let os_name = &self.context.os_name;
let os_part = os_name
.as_ref()
.map(|s| format!("/{}", s))
.unwrap_or_default();
let version = get_version_str();
let headerval = format!("Delta Chat Core {}{}", version, os_part);
/* Add a X-Mailer header.
This is only informational for debugging and may be removed in the release.
We do not rely on this header as it may be removed by MTAs. */
wrapmime::new_custom_field(imf_fields, "X-Mailer", &headerval);
wrapmime::new_custom_field(imf_fields, "Chat-Version", "1.0");
if self.req_mdn {
/* we use "Chat-Disposition-Notification-To"
because replies to "Disposition-Notification-To" are weird in many cases
eg. are just freetext and/or do not follow any standard. */
wrapmime::new_custom_field(
imf_fields,
"Chat-Disposition-Notification-To",
&self.from_addr,
);
}
let cleanup = |message: *mut Mailmime| {
if !message.is_null() {
mailmime_free(message);
}
};
let message = mailmime_new_message_data(0 as *mut Mailmime);
ensure!(!message.is_null(), "could not create mime message data");
mailmime_set_imf_fields(message, imf_fields);
// 1=add Autocrypt-header (needed eg. for handshaking), 2=no Autocrypte-header (used for MDN)
let mut e2ee_guaranteed = false;
let mut min_verified: libc::c_int = 0;
let mut do_gossip = false;
let mut grpimage = None;
let force_plaintext: libc::c_int;
let subject_str = match self.loaded {
Loaded::Message => {
/* Render a normal message
*********************************************************************/
let chat = self.chat.as_ref().unwrap();
let mut meta_part: *mut Mailmime = ptr::null_mut();
let mut placeholdertext = None;
if chat.typ == Chattype::VerifiedGroup {
wrapmime::new_custom_field(imf_fields, "Chat-Verified", "1");
force_plaintext = 0;
e2ee_guaranteed = true;
min_verified = 2
} else {
force_plaintext = self
.msg
.param
.get_int(Param::ForcePlaintext)
.unwrap_or_default();
if force_plaintext == 0 {
e2ee_guaranteed = self
.msg
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
!= 0;
}
}
/* beside key- and member-changes, force re-gossip every 48 hours */
if chat.gossiped_timestamp == 0
|| (chat.gossiped_timestamp + (2 * 24 * 60 * 60)) < time()
{
do_gossip = true
}
/* build header etc. */
let command = self.msg.param.get_cmd();
if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
wrapmime::new_custom_field(imf_fields, "Chat-Group-ID", &chat.grpid);
let encoded = dc_encode_header_words(&chat.name);
wrapmime::new_custom_field(imf_fields, "Chat-Group-Name", &encoded);
match command {
SystemMessage::MemberRemovedFromGroup => {
let email_to_remove =
self.msg.param.get(Param::Arg).unwrap_or_default();
if !email_to_remove.is_empty() {
wrapmime::new_custom_field(
imf_fields,
"Chat-Group-Member-Removed",
&email_to_remove,
);
}
}
SystemMessage::MemberAddedToGroup => {
let msg = &self.msg;
do_gossip = true;
let email_to_add = msg.param.get(Param::Arg).unwrap_or_default();
if !email_to_add.is_empty() {
wrapmime::new_custom_field(
imf_fields,
"Chat-Group-Member-Added",
&email_to_add,
);
grpimage = chat.param.get(Param::ProfileImage);
}
if 0 != msg.param.get_int(Param::Arg2).unwrap_or_default() & 0x1 {
info!(
context,
"sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>",
"vg-member-added",
);
wrapmime::new_custom_field(
imf_fields,
"Secure-Join",
"vg-member-added",
);
}
}
SystemMessage::GroupNameChanged => {
let msg = &self.msg;
let value_to_add = msg.param.get(Param::Arg).unwrap_or_default();
wrapmime::new_custom_field(
imf_fields,
"Chat-Group-Name-Changed",
&value_to_add,
);
}
SystemMessage::GroupImageChanged => {
let msg = &self.msg;
grpimage = msg.param.get(Param::Arg);
if grpimage.is_none() {
wrapmime::new_custom_field(imf_fields, "Chat-Group-Image", "0");
}
}
_ => {}
}
}
match command {
SystemMessage::LocationStreamingEnabled => {
wrapmime::new_custom_field(
imf_fields,
"Chat-Content",
"location-streaming-enabled",
);
}
SystemMessage::AutocryptSetupMessage => {
wrapmime::new_custom_field(imf_fields, "Autocrypt-Setup-Message", "v1");
placeholdertext = Some(
self.context
.stock_str(StockMessage::AcSetupMsgBody)
.to_string(),
);
}
SystemMessage::SecurejoinMessage => {
let msg = &self.msg;
let step = msg.param.get(Param::Arg).unwrap_or_default();
if !step.is_empty() {
info!(
context,
"sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>",
step,
);
wrapmime::new_custom_field(imf_fields, "Secure-Join", &step);
let param2 = msg.param.get(Param::Arg2).unwrap_or_default();
if !param2.is_empty() {
wrapmime::new_custom_field(
imf_fields,
if step == "vg-request-with-auth"
|| step == "vc-request-with-auth"
{
"Secure-Join-Auth"
} else {
"Secure-Join-Invitenumber"
},
param2,
)
}
let fingerprint = msg.param.get(Param::Arg3).unwrap_or_default();
if !fingerprint.is_empty() {
wrapmime::new_custom_field(
imf_fields,
"Secure-Join-Fingerprint",
&fingerprint,
);
}
match msg.param.get(Param::Arg4) {
Some(id) => {
wrapmime::new_custom_field(
imf_fields,
"Secure-Join-Group",
&id,
);
}
None => {}
};
}
}
_ => {}
}
if let Some(grpimage) = grpimage {
info!(self.context, "setting group image '{}'", grpimage);
let mut meta = Message::default();
meta.type_0 = Viewtype::Image;
meta.param.set(Param::File, grpimage);
let res = build_body_file(context, &meta, "group-image")?;
meta_part = res.0;
let filename_as_sent = res.1;
if !meta_part.is_null() {
wrapmime::new_custom_field(
imf_fields,
"Chat-Group-Image",
&filename_as_sent,
)
}
}
if self.msg.type_0 == Viewtype::Voice
|| self.msg.type_0 == Viewtype::Audio
|| self.msg.type_0 == Viewtype::Video
{
if self.msg.type_0 == Viewtype::Voice {
wrapmime::new_custom_field(imf_fields, "Chat-Voice-Message", "1");
}
let duration_ms = self.msg.param.get_int(Param::Duration).unwrap_or_default();
if duration_ms > 0 {
let dur = duration_ms.to_string();
wrapmime::new_custom_field(imf_fields, "Chat-Duration", &dur);
}
}
/* add text part - we even add empty text and force a MIME-multipart-message as:
- some Apps have problems with Non-text in the main part (eg. "Mail" from stock Android)
- we can add "forward hints" this way
- it looks better */
let afwd_email = self.msg.param.exists(Param::Forwarded);
let fwdhint = if afwd_email {
Some(
"---------- Forwarded message ----------\r\nFrom: Delta Chat\r\n\r\n"
.to_string(),
)
} else {
None
};
let final_text = {
if let Some(ref text) = placeholdertext {
text
} else if let Some(ref text) = self.msg.text {
text
} else {
""
}
};
let footer = &self.selfstatus;
let message_text = format!(
"{}{}{}{}{}",
fwdhint.unwrap_or_default(),
&final_text,
if !final_text.is_empty() && !footer.is_empty() {
"\r\n\r\n"
} else {
""
},
if !footer.is_empty() { "-- \r\n" } else { "" },
footer
);
let text_part = wrapmime::build_body_text(&message_text)?;
mailmime_smart_add_part(message, text_part);
/* add attachment part */
if chat::msgtype_has_file(self.msg.type_0) {
if !is_file_size_okay(context, &self.msg) {
cleanup(message);
bail!(
"Message exceeds the recommended {} MB.",
24 * 1024 * 1024 / 4 * 3 / 1000 / 1000,
);
} else {
let (file_part, _) = build_body_file(context, &self.msg, "")?;
mailmime_smart_add_part(message, file_part);
}
}
if !meta_part.is_null() {
mailmime_smart_add_part(message, meta_part);
}
if self.msg.param.exists(Param::SetLatitude) {
let param = &self.msg.param;
let kml_file = location::get_message_kml(
self.msg.timestamp_sort,
param.get_float(Param::SetLatitude).unwrap_or_default(),
param.get_float(Param::SetLongitude).unwrap_or_default(),
);
wrapmime::add_filename_part(
message,
"message.kml",
"application/vnd.google-earth.kml+xml",
&kml_file,
)?;
}
if location::is_sending_locations_to_chat(context, self.msg.chat_id) {
if let Ok((kml_file, last_added_location_id)) =
location::get_kml(context, self.msg.chat_id)
{
wrapmime::add_filename_part(
message,
"location.kml",
"application/vnd.google-earth.kml+xml",
&kml_file,
)?;
if !self.msg.param.exists(Param::SetLatitude) {
// otherwise, the independent location is already filed
self.out_last_added_location_id = last_added_location_id;
}
}
}
get_subject(context, self.chat.as_ref(), &mut self.msg, afwd_email)
}
Loaded::MDN => {
/* Render a MDN
*********************************************************************/
/* RFC 6522, this also requires the `report-type` parameter which is equal
to the MIME subtype of the second body part of the multipart/report */
let multipart = mailmime_multiple_new(
b"multipart/report\x00" as *const u8 as *const libc::c_char,
);
wrapmime::append_ct_param(
(*multipart).mm_content_type,
"report-type",
"disposition-notification",
)?;
mailmime_add_part(message, multipart);
/* first body part: always human-readable, always REQUIRED by RFC 6522 */
let p1 = if 0
!= self
.msg
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
{
self.context
.stock_str(StockMessage::EncryptedMsg)
.into_owned()
} else {
self.msg.get_summarytext(context, 32)
};
let p2 = self
.context
.stock_string_repl_str(StockMessage::ReadRcptMailBody, p1);
let message_text = format!("{}\r\n", p2);
let human_mime_part = wrapmime::build_body_text(&message_text)?;
mailmime_add_part(multipart, human_mime_part);
/* second body part: machine-readable, always REQUIRED by RFC 6522 */
let version = get_version_str();
let message_text2 = format!(
"Reporting-UA: Delta Chat {}\r\nOriginal-Recipient: rfc822;{}\r\nFinal-Recipient: rfc822;{}\r\nOriginal-Message-ID: <{}>\r\nDisposition: manual-action/MDN-sent-automatically; displayed\r\n",
version,
self.from_addr,
self.from_addr,
self.msg.rfc724_mid
);
let content_type_0 =
wrapmime::new_content_type("message/disposition-notification")?;
let mime_fields_0: *mut mailmime_fields =
mailmime_fields_new_encoding(MAILMIME_MECHANISM_8BIT as libc::c_int);
let mach_mime_part: *mut Mailmime =
mailmime_new_empty(content_type_0, mime_fields_0);
wrapmime::set_body_text(mach_mime_part, &message_text2)?;
mailmime_add_part(multipart, mach_mime_part);
force_plaintext = DC_FP_NO_AUTOCRYPT_HEADER;
info!(context, "sending MDM {:?}", message_text2);
/* currently, we do not send MDNs encrypted:
- in a multi-device-setup that is not set up properly, MDNs would disturb the communication as they
are send automatically which may lead to spreading outdated Autocrypt headers.
- they do not carry any information but the Message-ID
- this save some KB
- in older versions, we did not encrypt messages to ourself when they to to SMTP - however, if these messages
are forwarded for any reasons (eg. gmail always forwards to IMAP), we have no chance to decrypt them;
this issue is fixed with 0.9.4 */
let e = self.context.stock_str(StockMessage::ReadRcpt);
format!("Chat: {}", e).to_string()
}
_ => {
cleanup(message);
bail!("No message loaded.");
}
};
/* Create the mime message
*************************************************************************/
mailimf_fields_add(
imf_fields,
mailimf_field_new(
MAILIMF_FIELD_SUBJECT as libc::c_int,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
mailimf_subject_new(dc_encode_header_words(subject_str).strdup()),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
),
);
/*just a pointer into mailmime structure, must not be freed*/
let imffields_unprotected = mailmime_find_mailimf_fields(message);
ensure!(
!imffields_unprotected.is_null(),
"could not find mime fields"
);
let mut encrypt_helper = EncryptHelper::new(&context)?;
if force_plaintext != DC_FP_NO_AUTOCRYPT_HEADER {
// unless determined otherwise we add Autocrypt header
let aheader = encrypt_helper.get_aheader().to_string();
wrapmime::new_custom_field(imffields_unprotected, "Autocrypt", &aheader);
}
let mut finalized = false;
if force_plaintext == 0 {
finalized = encrypt_helper.try_encrypt(
self,
e2ee_guaranteed,
min_verified,
do_gossip,
message,
imffields_unprotected,
)?;
}
if !finalized {
self.finalize_mime_message(message, false, false)?;
}
cleanup(message);
Ok(())
}
pub fn load_msg(context: &Context, msg_id: u32) -> Result<MimeFactory, Error> {
ensure!(msg_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat id");
let msg = Message::load_from_db(context, msg_id)?;
let chat = Chat::load_from_db(context, msg.chat_id)?;
let mut factory = MimeFactory::new(context, msg);
factory.chat = Some(chat);
// just set the chat above
let chat = factory.chat.as_ref().unwrap();
if chat.is_self_talk() {
factory
.recipients_names
.push(factory.from_displayname.to_string());
factory.recipients_addr.push(factory.from_addr.to_string());
} else {
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| {
for row in rows {
let (authname, addr) = row?;
if !vec_contains_lowercase(&factory.recipients_addr, &addr) {
factory.recipients_addr.push(addr);
factory.recipients_names.push(authname);
}
}
Ok(())
},
)
.unwrap();
let command = factory.msg.param.get_cmd();
let msg = &factory.msg;
/* for added members, the list is just fine */
if command == SystemMessage::MemberRemovedFromGroup {
let email_to_remove = msg.param.get(Param::Arg).unwrap_or_default();
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 !vec_contains_lowercase(&factory.recipients_addr, &email_to_remove) {
factory.recipients_names.push("".to_string());
factory.recipients_addr.push(email_to_remove.to_string());
}
}
}
if command != SystemMessage::AutocryptSetupMessage
&& command != SystemMessage::SecurejoinMessage
&& 0 != context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1)
{
factory.req_mdn = true;
}
}
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 = in_reply_to;
factory.references = references;
}
Err(err) => {
error!(
context,
"mimefactory: failed to load mime_in_reply_to: {:?}", err
);
}
}
factory.loaded = Loaded::Message;
factory.timestamp = factory.msg.timestamp_sort;
factory.rfc724_mid = factory.msg.rfc724_mid.clone();
factory.increation = factory.msg.is_increation();
Ok(factory)
}
}
impl<'a> Drop for MimeFactory<'a> {
fn drop(&mut self) {
unsafe {
if !self.out.is_null() {
mmap_string_free(self.out);
}
}
}
}
fn get_subject(
context: &Context,
chat: Option<&Chat>,
msg: &mut Message,
afwd_email: bool,
) -> String {
if chat.is_none() {
return String::default();
}
let chat = chat.unwrap();
let raw_subject =
message::get_summarytext_by_raw(msg.type_0, msg.text.as_ref(), &mut msg.param, 32, context);
let fwd = if afwd_email { "Fwd: " } else { "" };
if msg.param.get_cmd() == SystemMessage::AutocryptSetupMessage {
/* do not add the "Chat:" prefix for setup messages */
context
.stock_str(StockMessage::AcSetupMsgSubject)
.into_owned()
} else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
format!("Chat: {}: {}{}", chat.name, fwd, raw_subject,)
} else {
format!("Chat: {}{}", fwd, raw_subject)
}
}
#[allow(non_snake_case)]
fn build_body_file(
context: &Context,
msg: &Message,
base_name: &str,
) -> Result<(*mut Mailmime, String), Error> {
let path_filename = match msg.param.get(Param::File) {
None => {
bail!("msg has no filename");
}
Some(path) => path,
};
let suffix = dc_get_filesuffix_lc(path_filename).unwrap_or_else(|| "dat".into());
/* get file name to use for sending
(for privacy purposes, we do not transfer the original filenames eg. for images;
these names are normally not needed and contain timestamps, running numbers etc.) */
let filename_to_send = match msg.type_0 {
Viewtype::Voice => chrono::Utc
.timestamp(msg.timestamp_sort as i64, 0)
.format(&format!("voice-message_%Y-%m-%d_%H-%M-%S.{}", suffix))
.to_string(),
Viewtype::Audio => Path::new(path_filename)
.file_name()
.map(|c| c.to_string_lossy().to_string())
.unwrap_or_default(),
Viewtype::Image | Viewtype::Gif => format!(
"{}.{}",
if base_name.is_empty() {
"image"
} else {
base_name
},
&suffix,
),
Viewtype::Video => format!("video.{}", &suffix),
_ => Path::new(path_filename)
.file_name()
.map(|c| c.to_string_lossy().to_string())
.unwrap_or_default(),
};
/* check mimetype */
let mimetype = match msg.param.get(Param::MimeType) {
Some(mtype) => mtype,
None => {
let path = Path::new(path_filename);
if let Some(res) = message::guess_msgtype_from_suffix(&path) {
res.1
} else {
"application/octet-stream"
}
}
};
let needs_ext = dc_needs_ext_header(&filename_to_send);
unsafe {
/* create mime part, for Content-Disposition, see RFC 2183.
`Content-Disposition: attachment` seems not to make a difference to `Content-Disposition: inline` at least on tested Thunderbird and Gma'l in 2017.
But I've heard about problems with inline and outl'k, so we just use the attachment-type until we run into other problems ... */
let mime_fields = mailmime_fields_new_filename(
MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int,
if needs_ext {
ptr::null_mut()
} else {
filename_to_send.strdup()
},
MAILMIME_MECHANISM_BASE64 as libc::c_int,
);
if needs_ext {
for cur_data in (*(*mime_fields).fld_list).into_iter() {
let field: *mut mailmime_field = cur_data as *mut _;
if (*field).fld_type == MAILMIME_FIELD_DISPOSITION as libc::c_int
&& !(*field).fld_data.fld_disposition.is_null()
{
let file_disposition = (*field).fld_data.fld_disposition;
if !file_disposition.is_null() {
let parm = mailmime_disposition_parm_new(
MAILMIME_DISPOSITION_PARM_PARAMETER as libc::c_int,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0 as libc::size_t,
mailmime_parameter_new(
strdup(b"filename*\x00" as *const u8 as *const libc::c_char),
dc_encode_ext_header(&filename_to_send).strdup(),
),
);
if !parm.is_null() {
clist_insert_after(
(*file_disposition).dsp_parms,
(*(*file_disposition).dsp_parms).last,
parm as *mut libc::c_void,
);
}
}
break;
}
}
}
let content = wrapmime::new_content_type(&mimetype)?;
let filename_encoded = dc_encode_header_words(&filename_to_send);
wrapmime::append_ct_param(content, "name", &filename_encoded)?;
let mime_sub = mailmime_new_empty(content, mime_fields);
let abs_path = dc_get_abs_path(context, path_filename)
.to_c_string()
.unwrap();
mailmime_set_body_file(mime_sub, dc_strdup(abs_path.as_ptr()));
Ok((mime_sub, filename_to_send))
}
}
pub(crate) fn vec_contains_lowercase(vec: &Vec<String>, part: &str) -> bool {
let partlc = part.to_lowercase();
for cur in vec.iter() {
if cur.to_lowercase() == partlc {
return true;
}
}
false
}
fn is_file_size_okay(context: &Context, msg: &Message) -> bool {
let mut file_size_okay = true;
let path = msg.param.get(Param::File).unwrap_or_default();
let bytes = dc_get_filebytes(context, &path);
if bytes > (49 * 1024 * 1024 / 4 * 3) {
file_size_okay = false;
}
file_size_okay
}

View File

@@ -7,6 +7,7 @@ use crate::aheader::*;
use crate::chat::*;
use crate::constants::*;
use crate::context::Context;
use crate::error::*;
use crate::key::*;
use crate::sql::{self, Sql};
@@ -408,28 +409,19 @@ impl<'a> Peerstate<'a> {
success
}
pub fn save_to_db(&self, sql: &Sql, create: bool) -> bool {
let mut success = false;
if self.addr.is_none() {
return success;
}
pub fn save_to_db(&self, sql: &Sql, create: bool) -> Result<()> {
ensure!(!self.addr.is_none(), "self.addr is not configured");
if create {
if sql::execute(
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 {
success = sql::execute(
sql::execute(
self.context,
sql,
"UPDATE acpeerstates \
@@ -450,10 +442,9 @@ impl<'a> Peerstate<'a> {
&self.verified_key_fingerprint,
&self.addr,
],
).is_ok();
assert_eq!(success, true);
)?
} else if self.to_save == Some(ToSave::Timestamps) {
success = sql::execute(
sql::execute(
self.context,
sql,
"UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=?, gossip_timestamp=? \
@@ -464,15 +455,14 @@ impl<'a> Peerstate<'a> {
self.gossip_timestamp,
&self.addr
],
)
.is_ok();
)?;
}
if self.to_save == Some(ToSave::All) || create {
reset_gossiped_timestamp(self.context, 0);
}
success
Ok(())
}
pub fn has_verified_key(&self, fingerprints: &HashSet<String>) -> bool {
@@ -522,7 +512,10 @@ mod tests {
degrade_event: None,
};
assert!(peerstate.save_to_db(&ctx.ctx.sql, true), "failed to save");
assert!(
peerstate.save_to_db(&ctx.ctx.sql, true).is_ok(),
"failed to save to db"
);
let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr.into())
.expect("failed to load peerstate from db");
@@ -564,7 +557,10 @@ mod tests {
degrade_event: None,
};
assert!(peerstate.save_to_db(&ctx.ctx.sql, true), "failed to save");
assert!(
peerstate.save_to_db(&ctx.ctx.sql, true).is_ok(),
"failed to save"
);
let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr.into())
.expect("failed to load peerstate from db");

View File

@@ -3,6 +3,7 @@ use std::convert::TryInto;
use std::io::Cursor;
use std::ptr;
use libc::{strchr, strlen, strncmp, strspn, strstr};
use pgp::composed::{
Deserializable, KeyType as PgpKeyType, Message, SecretKeyParamsBuilder, SignedPublicKey,
SignedSecretKey, SubkeyParamsBuilder,
@@ -15,11 +16,10 @@ use crate::dc_tools::*;
use crate::error::Error;
use crate::key::*;
use crate::keyring::*;
use crate::x::*;
pub unsafe fn dc_split_armored_data(
buf: *mut libc::c_char,
ret_headerline: *mut *const libc::c_char,
ret_headerline: *mut String,
ret_setupcodebegin: *mut *const libc::c_char,
ret_preferencrypt: *mut *const libc::c_char,
ret_base64: *mut *const libc::c_char,
@@ -31,9 +31,6 @@ pub unsafe fn dc_split_armored_data(
let mut p2: *mut libc::c_char;
let mut headerline: *mut libc::c_char = ptr::null_mut();
let mut base64: *mut libc::c_char = ptr::null_mut();
if !ret_headerline.is_null() {
*ret_headerline = ptr::null()
}
if !ret_setupcodebegin.is_null() {
*ret_setupcodebegin = ptr::null_mut();
}
@@ -43,7 +40,7 @@ pub unsafe fn dc_split_armored_data(
if !ret_base64.is_null() {
*ret_base64 = ptr::null();
}
if !(buf.is_null() || ret_headerline.is_null()) {
if !buf.is_null() {
dc_remove_cr_chars(buf);
while 0 != *p1 {
if *p1 as libc::c_int == '\n' as i32 {
@@ -62,9 +59,7 @@ pub unsafe fn dc_split_armored_data(
) == 0i32
{
headerline = line;
if !ret_headerline.is_null() {
*ret_headerline = headerline
}
*ret_headerline = as_str(headerline).to_string();
}
} else if strspn(line, b"\t\r\n \x00" as *const u8 as *const libc::c_char)
== strlen(line)

View File

@@ -13,7 +13,7 @@ use crate::error::Error;
use crate::events::Event;
use crate::key::*;
use crate::lot::LotState;
use crate::message::*;
use crate::message::Message;
use crate::param::*;
use crate::peerstate::*;
use crate::qr::check_qr;
@@ -266,7 +266,7 @@ fn send_handshake_msg(
fingerprint: Option<String>,
grpid: impl AsRef<str>,
) {
let mut msg = dc_msg_new_untyped();
let mut msg = Message::default();
msg.type_0 = Viewtype::Text;
msg.text = Some(format!("Secure-Join: {}", step));
msg.hidden = true;
@@ -522,7 +522,11 @@ pub fn handle_securejoin_handshake(
error!(context, "Chat {} not found.", &field_grpid);
return ret;
} else {
chat::add_contact_to_chat_ex(context, group_chat_id, contact_id, 0x1i32);
if let Err(err) =
chat::add_contact_to_chat_ex(context, group_chat_id, contact_id, true)
{
error!(context, "failed to add contact: {}", err);
}
}
} else {
send_handshake_msg(context, contact_chat_id, "vc-contact-confirm", "", None, "");
@@ -674,7 +678,7 @@ fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef<str>) -> Res
if peerstate.set_verified(1, fingerprint.as_ref(), 2) {
peerstate.prefer_encrypt = EncryptPreference::Mutual;
peerstate.to_save = Some(ToSave::All);
peerstate.save_to_db(&context.sql, false);
peerstate.save_to_db(&context.sql, false).unwrap();
return Ok(());
}
}

View File

@@ -70,8 +70,9 @@ impl Smtp {
let port = lp.send_port as u16;
let tls = native_tls::TlsConnector::builder()
// FIXME: unfortunately this is needed to make things work on macos + testrun.org
// see also: https://github.com/deltachat/deltachat-core-rust/issues/203
.danger_accept_invalid_hostnames(true)
.danger_accept_invalid_certs(true)
.min_protocol_version(Some(DEFAULT_TLS_PROTOCOLS[0]))
.build()
.unwrap();

View File

@@ -777,7 +777,7 @@ fn open(
if let Some(ref mut peerstate) = Peerstate::from_addr(context, sql, &addr?)
{
peerstate.recalc_fingerprint();
peerstate.save_to_db(sql, false);
peerstate.save_to_db(sql, false).unwrap();
}
}
Ok(())

View File

@@ -7,7 +7,6 @@ use crate::contact::*;
use crate::context::Context;
use crate::dc_tools::*;
use crate::events::Event;
use libc::free;
/// Stock strings
///
@@ -133,7 +132,7 @@ impl Context {
Cow::Borrowed(id.fallback())
} else {
let ret = to_string(ptr);
unsafe { free(ptr as *mut libc::c_void) };
unsafe { libc::free(ptr as *mut libc::c_void) };
Cow::Owned(ret)
}
}

View File

@@ -68,98 +68,15 @@ pub fn configure_alice_keypair(ctx: &Context) -> String {
// .unwrap();
// println!("{}", public.to_base64(64));
// println!("{}", private.to_base64(64));
let public = key::Key::from_base64(
concat!(
"xsBNBF086ewBCACmJKuoxIO6T87yi4Q3MyNpMch3Y8KrtHDQyUszU36eqM3Pmd1l",
"FrbcCd8ZWo2pq6OJSwsM/jjRGn1zo2YOaQeJRRrC+KrKGqSUtRSYQBPrPjE2YjSX",
"AMbu8jBI6VVUhHeghriBkK79PY9O/oRhIJC0p14IJe6CQ6OI2fTmTUHF9i/nJ3G4",
"Wb3/K1bU3yVfyPZjTZQPYPvvh03vxHULKurtYkX5DTEMSWsF4qzLMps+l87VuLV9",
"iQnbN7YMRLHHx2KkX5Ivi7JCefhCa54M0K3bDCCxuVWAM5wjQwNZjzR+WL+dYchw",
"oFvuF8NrlzjM9dSv+2rn+J7f99ijSXarzPC7ABEBAAHNEzxhbGljZUBleGFtcGxl",
"LmNvbT7CwIkEEAEIADMCGQEFAl086fgCGwMECwkIBwYVCAkKCwIDFgIBFiEE+iai",
"x4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcei3ogf/cruUmQ+th52TFHTHdkw9",
"OHUl3MrXtZ7QmHyOAFvbXE/6n5Eeh+eZoF8MWWV72m14Wbs+vTcNQkFVTdOPptkK",
"A8e4cJqwDOHsyAnvQXZ7WNje9+BMzcoipIUawHP4ORFaPDsKLZQ0b4wBbKn8ziea",
"6zjGF0/qljTdoxTtsYpv5wXYuhwbYklrLOqgSa5M7LXUe7E3g9mbg+9iX1GuB8m6",
"GkquJN814Y+xny4xhZzGOfue6SeP12jJMNSjSP7416dRq7794VGnkkW9V/7oFEUK",
"u5wO9FFbgDySOSlEjByGejSGuBmho0iJSjcPjZ7EY/j3M3orq4dpza5C82OeSvxD",
"Fc7ATQRdPOnsAQgA5oLxXRLnyugzOmNCy7dxV3JrDZscA6JNlJlDWIShT0YSs+zG",
"9JzDeQql+sYXgUSxOoIayItuXtnFn7tstwGoOnYvadm/e5/7V5fKAQRtCtdN51av",
"62n18Venlm0yNKpROPcZ6M/sc4m6uU6YRZ/a1idal8VGY0wLKlghjIBuIiBoVQ/R",
"noW+/fhmwIg08dQ5m8hQe3GEOZEeLrTWL/9awPlXK7Y+DmJOoR4qbHWEcRfbzS6q",
"4zW8vk2ztB8ngwbnqYy8zrN1DCICN1gYdlU++uVw6Bb1XfY8Cdldh1VLKpF35mAm",
"jxLZfVDcoObFH3Cv2GB7BEYxv86KC2Y6T74Q/wARAQABwsB2BBgBCAAgBQJdPOn4",
"AhsMFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcegLxwf/dXshJnoW",
"qEnRsf6rVb9/Mc66ti+NVQLfNd275dybh/QIJdK3NmSxdnTPIEJRVspJywJoupwX",
"FNrnHG2Ff6QPvHqI+/oNu986r9d7Z1oQibbLHKt8t6kpOfg/xGxotagJuiCQvR9m",
"MjD1DqsdB37SjDxGupZOOJSXWi6KX60IE+uM+QOBfeOZziQwuFmA5wV6RDXIeYJf",
"qrcbeXeR1d0nfNpPHQR1gBiqmxNb6KBbdXD2+EXW60axC7D2b1APmzMlammDliPw",
"sK9U1rc9nuquEBvGDOJf4K+Dzn+mDCqRpP6uAuQ7RKHyim4uyN0wwKObzPqgJCGw",
"jTglkixw+aSTXw=="
),
KeyType::Public,
)
.unwrap();
let public =
key::Key::from_base64(include_str!("../test-data/key/public.asc"), KeyType::Public)
.unwrap();
let private = key::Key::from_base64(
concat!(
"xcLYBF086ewBCACmJKuoxIO6T87yi4Q3MyNpMch3Y8KrtHDQyUszU36eqM3Pmd1l",
"FrbcCd8ZWo2pq6OJSwsM/jjRGn1zo2YOaQeJRRrC+KrKGqSUtRSYQBPrPjE2YjSX",
"AMbu8jBI6VVUhHeghriBkK79PY9O/oRhIJC0p14IJe6CQ6OI2fTmTUHF9i/nJ3G4",
"Wb3/K1bU3yVfyPZjTZQPYPvvh03vxHULKurtYkX5DTEMSWsF4qzLMps+l87VuLV9",
"iQnbN7YMRLHHx2KkX5Ivi7JCefhCa54M0K3bDCCxuVWAM5wjQwNZjzR+WL+dYchw",
"oFvuF8NrlzjM9dSv+2rn+J7f99ijSXarzPC7ABEBAAEACAChqzVOuErmVRqvcYtq",
"m1xt1H+ZjX20z5Sn1fhTLYAcq236AWMqJvwxCXoKlc8bt2UfB+Ls9cQb1YcVq353",
"r0QiExiDeK3YlCxqd/peXJwFYTNKFC3QcnUhtpG9oS/jWjN+BRotGbjtu6Vj3M68",
"JJAq+mHJ0/9OyrqrREvGfo7uLZt7iMGemDlrDakvrbIyZrPLgay+nZ3dEFKeOQ6F",
"FrU05jyUVdoHBy0Tqx/6VpFUX9+IHcMHL2lTJB0nynBj+XZ/G4aX3WYoo3YlixHb",
"Iu35fGFA0TChoGaGPzqcI/kg2Z+b/BryG9NM3LA2cO8iGrGXAE1nPFp91jmCrQ3V",
"WushBADERP+uojjjfdO5J+RkmcFe9mFYDdtkhN+kV+LdePjiNNtcXMBhasstio0S",
"ut0GKnE7DFRhX7mkN9w2apJ2ooeFeVVWot18eSdp6Rzh6/1Z7TmhYFJ3oUxxLbnQ",
"sWIXIec1SzqWBFJUCn3IP0mCnJktFg/uGW6yLs01r5ds52uSBQQA2LSWiTwk9tEm",
"dr9mz3tHnmrkyGiyKhKGM1Z7Rch63D5yQc1s4kUMBlyuLL2QtM/e4dtaz2JAkO8k",
"QrYCnNgJ+2roTAK3kDZgYtymjdvK3HpQNtjVo7dds5RJVb6U618phZwU5WNFAEJW",
"yyImmycGfjLv+18cW/3mq0QVZejkM78D/2kHaIeJAowtBOFY2zDrKyDRoBHaUSgj",
"5BjGoviRC5rYihWDEyYDQ6mBJQstAD0Ty3MYzyUxl6ruB/BMWnMDFq5+TqtdBzu3",
"jCtZ8OEyH8A5Kdo68Wzo/PGxzMtusOdNj9+3PBmSq4yibJxbLSrn59aVUYpGLjeG",
"Kyvm9OTKkrOGN27NEzxhbGljZUBleGFtcGxlLmNvbT7CwIkEEAEIADMCGQEFAl08",
"6fgCGwMECwkIBwYVCAkKCwIDFgIBFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQ",
"k6DcNkbrcei3ogf/cruUmQ+th52TFHTHdkw9OHUl3MrXtZ7QmHyOAFvbXE/6n5Ee",
"h+eZoF8MWWV72m14Wbs+vTcNQkFVTdOPptkKA8e4cJqwDOHsyAnvQXZ7WNje9+BM",
"zcoipIUawHP4ORFaPDsKLZQ0b4wBbKn8ziea6zjGF0/qljTdoxTtsYpv5wXYuhwb",
"YklrLOqgSa5M7LXUe7E3g9mbg+9iX1GuB8m6GkquJN814Y+xny4xhZzGOfue6SeP",
"12jJMNSjSP7416dRq7794VGnkkW9V/7oFEUKu5wO9FFbgDySOSlEjByGejSGuBmh",
"o0iJSjcPjZ7EY/j3M3orq4dpza5C82OeSvxDFcfC2ARdPOnsAQgA5oLxXRLnyugz",
"OmNCy7dxV3JrDZscA6JNlJlDWIShT0YSs+zG9JzDeQql+sYXgUSxOoIayItuXtnF",
"n7tstwGoOnYvadm/e5/7V5fKAQRtCtdN51av62n18Venlm0yNKpROPcZ6M/sc4m6",
"uU6YRZ/a1idal8VGY0wLKlghjIBuIiBoVQ/RnoW+/fhmwIg08dQ5m8hQe3GEOZEe",
"LrTWL/9awPlXK7Y+DmJOoR4qbHWEcRfbzS6q4zW8vk2ztB8ngwbnqYy8zrN1DCIC",
"N1gYdlU++uVw6Bb1XfY8Cdldh1VLKpF35mAmjxLZfVDcoObFH3Cv2GB7BEYxv86K",
"C2Y6T74Q/wARAQABAAgAhSvFEYZoj1sSrXrHDjZOrryViGjCCH9t3pmkxLDrGIdd",
"KsFyN8ORUo6KUZS745yx3yFnI9EZ1IZvm9aF+jxk2lGJFtgLvfoxFOvGckwCSy8T",
"/MCiJZkz01hWo5s2VCLJheWL/GqTKjS5wXDcm+y8Wtilh+UawycdlDsSNr/D4MZL",
"j3Chq9K03l5UIR8DcC7SavNi55R2oGOfboXsdvwOlrNZdCkZOlXDI4ZKFwbDHCtp",
"Do5FS30hnJi2TecUPZWB1CaGFWnevINd4ikugVjcAoZj/QAIvfrOCgqisF/Ylg9u",
"RMUPBapmcJUueILwd0iQqvGG0aCqtchvSmlg15/lQQQA9G1NNjNAH+NQrXvDJFJe",
"/V1U3F3pz7jCjQa69c0dxSBUeNX1pG8XXD6tSkkd4Ni1mzZGcZXOmVUM6cA9I7RH",
"95RqV+QIfnXVneCRrlCjV8m6OBlkivkESXc3nW5wtCIfw7oKg9w1xuVNUaAlbCt9",
"QVLaxXJiY7ad0f5U9XJ1+w8EAPFs+M/+GZK1wOZYBL1vo7x0gL9ZggmjC4B+viBJ",
"8Q60mqTrphYFsbXHuwKV0g9aIoZMucKyEE0QLR7imttiLEz1nD8bfEScbGy9ZG//",
"wRfyJmCVAjA0pQ6LtB93d70PSVzzJrMHgbLKrDuSd6RChl7n9BIEdVyk7LEph0Yg",
"9UsRBADm6DvpKL+P3lQ0eLTfAgcQTOqLZDYmI3PvqqSkHb1kHChqOXXs8hGOSSwK",
"Gjcd4CZeNOGWR42rZyRhVgtkt6iYviIaVAWUfme6K+sLQBCeyMlmEGtykAA+LmPB",
"f4zdyUNADfoxgZF3EKHf6I3nlVn5cdT+o/9vjdY2XAOwcls1RzaFwsB2BBgBCAAg",
"BQJdPOn4AhsMFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcegLxwf/",
"dXshJnoWqEnRsf6rVb9/Mc66ti+NVQLfNd275dybh/QIJdK3NmSxdnTPIEJRVspJ",
"ywJoupwXFNrnHG2Ff6QPvHqI+/oNu986r9d7Z1oQibbLHKt8t6kpOfg/xGxotagJ",
"uiCQvR9mMjD1DqsdB37SjDxGupZOOJSXWi6KX60IE+uM+QOBfeOZziQwuFmA5wV6",
"RDXIeYJfqrcbeXeR1d0nfNpPHQR1gBiqmxNb6KBbdXD2+EXW60axC7D2b1APmzMl",
"ammDliPwsK9U1rc9nuquEBvGDOJf4K+Dzn+mDCqRpP6uAuQ7RKHyim4uyN0wwKOb",
"zPqgJCGwjTglkixw+aSTXw=="
),
include_str!("../test-data/key/private.asc"),
KeyType::Private,
)
.unwrap();
let saved = key::dc_key_save_self_keypair(&ctx, &public, &private, &addr, 1, &ctx.sql);
let saved = key::dc_key_save_self_keypair(&ctx, &public, &private, &addr, true, &ctx.sql);
assert_eq!(saved, true, "Failed to save Alice's key");
addr
}

View File

@@ -15,19 +15,21 @@ if __name__ == "__main__":
unsafe = s.count("unsafe")
free = s.count("free(")
gotoblocks = s.count("ok_to_continue") + s.count('OK_TO_CONTINUE')
filestats.append((fn, unsafe, free, gotoblocks))
chars = s.count("c_char") + s.count("CStr")
filestats.append((fn, unsafe, free, gotoblocks, chars))
sum_unsafe, sum_free, sum_gotoblocks = 0, 0, 0
sum_unsafe, sum_free, sum_gotoblocks, sum_chars = 0, 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} ok_to_continue: {3: >3}".format(str(fn), unsafe, free, gotoblocks))
for fn, unsafe, free, gotoblocks, chars in reversed(sorted(filestats, key=lambda x: sum(x[1:]))):
print("{0: <25} unsafe: {1: >3} free: {2: >3} ok_to_cont: {3: >3} chars: {4: >3}".format(str(fn), unsafe, free, gotoblocks, chars))
sum_unsafe += unsafe
sum_free += free
sum_gotoblocks += gotoblocks
sum_chars += chars
print()
print("total unsafe:", sum_unsafe)
print("total free:", sum_free)
print("total ok_to_continue:", sum_gotoblocks)
print("total c_chars:", sum_chars)

282
src/wrapmime.rs Normal file
View File

@@ -0,0 +1,282 @@
use std::ffi::CString;
use std::ptr;
use crate::dc_tools::*;
use crate::error::Error;
use mmime::clist::*;
use mmime::mailimf::types::*;
use mmime::mailimf::types_helper::*;
use mmime::mailmime::content::*;
use mmime::mailmime::disposition::*;
use mmime::mailmime::types::*;
use mmime::mailmime::types_helper::*;
use mmime::mailmime::*;
use mmime::other::*;
#[macro_export]
macro_rules! clist_append {
($clist:expr, $item:expr) => {
if clist_insert_after(
$clist as *mut clist,
(*$clist).last,
$item as *mut libc::c_void,
) != 0
{
bail!("could not allocate or append list item");
}
};
}
/**************************************
* mime parsing API
**************************************/
pub fn get_ct_subtype(mime: *mut Mailmime) -> Option<String> {
unsafe {
let ct: *mut mailmime_content = (*mime).mm_content_type;
if !ct.is_null() && !(*ct).ct_subtype.is_null() {
println!("ct_subtype: {}", to_string((*ct).ct_subtype));
Some(to_string((*ct).ct_subtype))
} else {
None
}
}
}
pub fn get_autocrypt_mime(
mime_undetermined: *mut Mailmime,
) -> Result<(*mut Mailmime, *mut Mailmime), Error> {
/* return Result with two mime pointers:
First mime pointer is to the multipart-mime message
(which is replaced with a decrypted version later)
Second one is to the encrypted payload.
For non-autocrypt message an Error is returned.
*/
unsafe {
ensure!(
(*mime_undetermined).mm_type == MAILMIME_MESSAGE as libc::c_int,
"Not a root mime message"
);
let mime = (*mime_undetermined).mm_data.mm_message.mm_msg_mime;
ensure!(
(*mime).mm_type == MAILMIME_MULTIPLE as libc::c_int
&& "encrypted" == get_ct_subtype(mime).unwrap_or_default(),
"Not a multipart/encrypted message"
);
let parts: Vec<_> = (*(*mime).mm_data.mm_multipart.mm_mp_list)
.into_iter()
.map(|c| c as *mut Mailmime)
.collect();
ensure!(parts.len() == 2, "Invalid Autocrypt Level 1 Mime Parts");
// XXX ensure protocol-parameter "application/pgp-encrypted")
// XXX ensure wrapmime::get_content_type(parts[1])) == "application/octetstream"
// a proper OpenPGP multipart/encrypted Autocrypt Level 1 message
// https://tools.ietf.org/html/rfc3156.html#section-4
Ok((mime, parts[1]))
}
}
pub fn has_decryptable_data(mime_data: *mut mailmime_data) -> bool {
/* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */
unsafe {
(*mime_data).dt_type == MAILMIME_DATA_TEXT as libc::c_int
&& !(*mime_data).dt_data.dt_text.dt_data.is_null()
&& (*mime_data).dt_data.dt_text.dt_length > 0
}
}
pub fn get_mime_transfer_encoding(mime: *mut Mailmime) -> Option<libc::c_int> {
unsafe {
let mm_mime_fields = (*mime).mm_mime_fields;
if !mm_mime_fields.is_null() {
for cur_data in (*(*mm_mime_fields).fld_list).into_iter() {
let field: *mut mailmime_field = cur_data as *mut _;
if (*field).fld_type == MAILMIME_FIELD_TRANSFER_ENCODING as libc::c_int
&& !(*field).fld_data.fld_encoding.is_null()
{
return Some((*(*field).fld_data.fld_encoding).enc_type);
}
}
}
}
None
}
pub fn decode_dt_data(
mime_data: *mut mailmime_data,
mime_transfer_encoding: libc::c_int,
) -> Result<(*mut libc::c_char, libc::size_t), Error> {
// Decode data according to mime_transfer_encoding
// returns Ok with a (decoded_data,decoded_data_bytes) pointer
// where the caller must make sure to free it.
// It may return Ok(ptr::null_mut(), 0)
let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut();
let decoded_data: *mut libc::c_char;
let mut decoded_data_bytes: libc::size_t = 0;
if mime_transfer_encoding == MAILMIME_MECHANISM_7BIT as libc::c_int
|| mime_transfer_encoding == MAILMIME_MECHANISM_8BIT as libc::c_int
|| mime_transfer_encoding == MAILMIME_MECHANISM_BINARY as libc::c_int
{
unsafe {
decoded_data = (*mime_data).dt_data.dt_text.dt_data as *mut _;
decoded_data_bytes = (*mime_data).dt_data.dt_text.dt_length;
}
ensure!(
!decoded_data.is_null() && decoded_data_bytes > 0,
"could not decode mime message"
);
} else {
let mut current_index: libc::size_t = 0;
unsafe {
let r = mailmime_part_parse(
(*mime_data).dt_data.dt_text.dt_data,
(*mime_data).dt_data.dt_text.dt_length,
&mut current_index,
mime_transfer_encoding,
&mut transfer_decoding_buffer,
&mut decoded_data_bytes,
);
if r != MAILIMF_NO_ERROR as libc::c_int
|| transfer_decoding_buffer.is_null()
|| decoded_data_bytes <= 0
{
bail!("mailmime_part_parse returned error or invalid data");
}
decoded_data = transfer_decoding_buffer;
}
}
Ok((decoded_data, decoded_data_bytes))
}
/**************************************
* mime creation API
**************************************/
pub fn add_filename_part(
message: *mut Mailmime,
basename: &str,
mime_type: &str,
file_content: &str,
) -> Result<(), Error> {
let mime_type_c = CString::new(mime_type.to_string()).expect("failed to create CString");
unsafe {
let content_type = mailmime_content_new_with_str(mime_type_c.as_ptr());
let mime_fields = mailmime_fields_new_filename(
MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int,
basename.strdup(),
MAILMIME_MECHANISM_8BIT as libc::c_int,
);
let file_mime_part = mailmime_new_empty(content_type, mime_fields);
set_body_text(file_mime_part, file_content)?;
mailmime_smart_add_part(message, file_mime_part);
}
Ok(())
}
pub fn new_custom_field(fields: *mut mailimf_fields, name: &str, value: &str) {
unsafe {
let field = mailimf_field_new_custom(name.strdup(), value.strdup());
let res = mailimf_fields_add(fields, field);
assert!(
res as u32 == MAILIMF_NO_ERROR,
"could not create mailimf field"
);
}
}
pub fn build_body_text(text: &str) -> Result<*mut Mailmime, Error> {
let mime_fields: *mut mailmime_fields;
let message_part: *mut Mailmime;
let content = new_content_type("text/plain")?;
append_ct_param(content, "charset", "utf-8")?;
unsafe {
mime_fields = mailmime_fields_new_encoding(MAILMIME_MECHANISM_8BIT as libc::c_int);
message_part = mailmime_new_empty(content, mime_fields);
}
set_body_text(message_part, text)?;
Ok(message_part)
}
pub fn append_ct_param(
content: *mut mailmime_content,
name: &str,
value: &str,
) -> Result<(), Error> {
unsafe {
let name_c = CString::new(name).unwrap();
let value_c = CString::new(value).unwrap();
clist_append!(
(*content).ct_parameters,
mailmime_param_new_with_data(
name_c.as_ptr() as *const u8 as *const libc::c_char as *mut libc::c_char,
value_c.as_ptr() as *const u8 as *const libc::c_char as *mut libc::c_char
)
);
}
Ok(())
}
pub fn new_content_type(content_type: &str) -> Result<*mut mailmime_content, Error> {
let ct = CString::new(content_type).unwrap();
let content: *mut mailmime_content;
// mailmime_content_new_with_str only parses but does not retain/own ct
unsafe {
content = mailmime_content_new_with_str(ct.as_ptr());
}
ensure!(!content.is_null(), "mailimf failed to allocate");
Ok(content)
}
pub fn set_body_text(part: *mut Mailmime, text: &str) -> Result<(), Error> {
use libc::strlen;
unsafe {
let text_c = text.strdup();
if 0 != mailmime_set_body_text(part, text_c, strlen(text_c)) {
bail!("could not set body text on mime-structure");
}
}
Ok(())
}
pub fn content_type_needs_encoding(content: *const mailmime_content) -> bool {
unsafe {
if (*(*content).ct_type).tp_type == MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int {
let composite = (*(*content).ct_type).tp_data.tp_composite_type;
match (*composite).ct_type as u32 {
MAILMIME_COMPOSITE_TYPE_MESSAGE => as_str((*content).ct_subtype) != "rfc822",
MAILMIME_COMPOSITE_TYPE_MULTIPART => false,
_ => false,
}
} else {
true
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_needs_encoding() {
assert!(content_type_needs_encoding(
new_content_type("text/plain").unwrap()
));
assert!(content_type_needs_encoding(
new_content_type("application/octect-stream").unwrap()
));
assert!(!content_type_needs_encoding(
new_content_type("multipart/encrypted").unwrap()
));
assert!(content_type_needs_encoding(
new_content_type("application/pgp-encrypted").unwrap()
));
}
}

View File

@@ -1,67 +0,0 @@
pub use libc::{
calloc, exit, free, malloc, memcmp, memcpy, memmove, memset, realloc, strcat, strchr, strcmp,
strcpy, strcspn, strlen, strncmp, strncpy, strrchr, strspn, strstr, strtol, system,
};
pub unsafe fn strdup(s: *const libc::c_char) -> *mut libc::c_char {
if s.is_null() {
return std::ptr::null_mut();
}
let slen = strlen(s);
let result = malloc(slen + 1);
if result.is_null() {
return std::ptr::null_mut();
}
memcpy(result, s as *const _, slen + 1);
result as *mut _
}
pub fn strndup(s: *const libc::c_char, n: libc::c_ulong) -> *mut libc::c_char {
if s.is_null() {
return std::ptr::null_mut();
}
let end = std::cmp::min(n as usize, unsafe { strlen(s) });
unsafe {
let result = malloc(end + 1);
memcpy(result, s as *const _, end);
std::ptr::write_bytes(result.offset(end as isize), b'\x00', 1);
result as *mut _
}
}
pub(crate) unsafe fn strcasecmp(s1: *const libc::c_char, s2: *const libc::c_char) -> libc::c_int {
let s1 = std::ffi::CStr::from_ptr(s1)
.to_string_lossy()
.to_lowercase();
let s2 = std::ffi::CStr::from_ptr(s2)
.to_string_lossy()
.to_lowercase();
if s1 == s2 {
0
} else {
1
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dc_tools::to_string;
#[test]
fn test_strndup() {
unsafe {
let res = strndup(b"helloworld\x00" as *const u8 as *const libc::c_char, 4);
assert_eq!(
to_string(res),
to_string(b"hell\x00" as *const u8 as *const libc::c_char)
);
assert_eq!(strlen(res), 4);
free(res as *mut _);
}
}
}

View File

@@ -0,0 +1 @@
xcLYBF086ewBCACmJKuoxIO6T87yi4Q3MyNpMch3Y8KrtHDQyUszU36eqM3Pmd1lFrbcCd8ZWo2pq6OJSwsM/jjRGn1zo2YOaQeJRRrC+KrKGqSUtRSYQBPrPjE2YjSXAMbu8jBI6VVUhHeghriBkK79PY9O/oRhIJC0p14IJe6CQ6OI2fTmTUHF9i/nJ3G4Wb3/K1bU3yVfyPZjTZQPYPvvh03vxHULKurtYkX5DTEMSWsF4qzLMps+l87VuLV9iQnbN7YMRLHHx2KkX5Ivi7JCefhCa54M0K3bDCCxuVWAM5wjQwNZjzR+WL+dYchwoFvuF8NrlzjM9dSv+2rn+J7f99ijSXarzPC7ABEBAAEACAChqzVOuErmVRqvcYtqm1xt1H+ZjX20z5Sn1fhTLYAcq236AWMqJvwxCXoKlc8bt2UfB+Ls9cQb1YcVq353r0QiExiDeK3YlCxqd/peXJwFYTNKFC3QcnUhtpG9oS/jWjN+BRotGbjtu6Vj3M68JJAq+mHJ0/9OyrqrREvGfo7uLZt7iMGemDlrDakvrbIyZrPLgay+nZ3dEFKeOQ6FFrU05jyUVdoHBy0Tqx/6VpFUX9+IHcMHL2lTJB0nynBj+XZ/G4aX3WYoo3YlixHbIu35fGFA0TChoGaGPzqcI/kg2Z+b/BryG9NM3LA2cO8iGrGXAE1nPFp91jmCrQ3VWushBADERP+uojjjfdO5J+RkmcFe9mFYDdtkhN+kV+LdePjiNNtcXMBhasstio0Sut0GKnE7DFRhX7mkN9w2apJ2ooeFeVVWot18eSdp6Rzh6/1Z7TmhYFJ3oUxxLbnQsWIXIec1SzqWBFJUCn3IP0mCnJktFg/uGW6yLs01r5ds52uSBQQA2LSWiTwk9tEmdr9mz3tHnmrkyGiyKhKGM1Z7Rch63D5yQc1s4kUMBlyuLL2QtM/e4dtaz2JAkO8kQrYCnNgJ+2roTAK3kDZgYtymjdvK3HpQNtjVo7dds5RJVb6U618phZwU5WNFAEJWyyImmycGfjLv+18cW/3mq0QVZejkM78D/2kHaIeJAowtBOFY2zDrKyDRoBHaUSgj5BjGoviRC5rYihWDEyYDQ6mBJQstAD0Ty3MYzyUxl6ruB/BMWnMDFq5+TqtdBzu3jCtZ8OEyH8A5Kdo68Wzo/PGxzMtusOdNj9+3PBmSq4yibJxbLSrn59aVUYpGLjeGKyvm9OTKkrOGN27NEzxhbGljZUBleGFtcGxlLmNvbT7CwIkEEAEIADMCGQEFAl086fgCGwMECwkIBwYVCAkKCwIDFgIBFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcei3ogf/cruUmQ+th52TFHTHdkw9OHUl3MrXtZ7QmHyOAFvbXE/6n5Eeh+eZoF8MWWV72m14Wbs+vTcNQkFVTdOPptkKA8e4cJqwDOHsyAnvQXZ7WNje9+BMzcoipIUawHP4ORFaPDsKLZQ0b4wBbKn8ziea6zjGF0/qljTdoxTtsYpv5wXYuhwbYklrLOqgSa5M7LXUe7E3g9mbg+9iX1GuB8m6GkquJN814Y+xny4xhZzGOfue6SeP12jJMNSjSP7416dRq7794VGnkkW9V/7oFEUKu5wO9FFbgDySOSlEjByGejSGuBmho0iJSjcPjZ7EY/j3M3orq4dpza5C82OeSvxDFcfC2ARdPOnsAQgA5oLxXRLnyugzOmNCy7dxV3JrDZscA6JNlJlDWIShT0YSs+zG9JzDeQql+sYXgUSxOoIayItuXtnFn7tstwGoOnYvadm/e5/7V5fKAQRtCtdN51av62n18Venlm0yNKpROPcZ6M/sc4m6uU6YRZ/a1idal8VGY0wLKlghjIBuIiBoVQ/RnoW+/fhmwIg08dQ5m8hQe3GEOZEeLrTWL/9awPlXK7Y+DmJOoR4qbHWEcRfbzS6q4zW8vk2ztB8ngwbnqYy8zrN1DCICN1gYdlU++uVw6Bb1XfY8Cdldh1VLKpF35mAmjxLZfVDcoObFH3Cv2GB7BEYxv86KC2Y6T74Q/wARAQABAAgAhSvFEYZoj1sSrXrHDjZOrryViGjCCH9t3pmkxLDrGIddKsFyN8ORUo6KUZS745yx3yFnI9EZ1IZvm9aF+jxk2lGJFtgLvfoxFOvGckwCSy8T/MCiJZkz01hWo5s2VCLJheWL/GqTKjS5wXDcm+y8Wtilh+UawycdlDsSNr/D4MZLj3Chq9K03l5UIR8DcC7SavNi55R2oGOfboXsdvwOlrNZdCkZOlXDI4ZKFwbDHCtpDo5FS30hnJi2TecUPZWB1CaGFWnevINd4ikugVjcAoZj/QAIvfrOCgqisF/Ylg9uRMUPBapmcJUueILwd0iQqvGG0aCqtchvSmlg15/lQQQA9G1NNjNAH+NQrXvDJFJe/V1U3F3pz7jCjQa69c0dxSBUeNX1pG8XXD6tSkkd4Ni1mzZGcZXOmVUM6cA9I7RH95RqV+QIfnXVneCRrlCjV8m6OBlkivkESXc3nW5wtCIfw7oKg9w1xuVNUaAlbCt9QVLaxXJiY7ad0f5U9XJ1+w8EAPFs+M/+GZK1wOZYBL1vo7x0gL9ZggmjC4B+viBJ8Q60mqTrphYFsbXHuwKV0g9aIoZMucKyEE0QLR7imttiLEz1nD8bfEScbGy9ZG//wRfyJmCVAjA0pQ6LtB93d70PSVzzJrMHgbLKrDuSd6RChl7n9BIEdVyk7LEph0Yg9UsRBADm6DvpKL+P3lQ0eLTfAgcQTOqLZDYmI3PvqqSkHb1kHChqOXXs8hGOSSwKGjcd4CZeNOGWR42rZyRhVgtkt6iYviIaVAWUfme6K+sLQBCeyMlmEGtykAA+LmPBf4zdyUNADfoxgZF3EKHf6I3nlVn5cdT+o/9vjdY2XAOwcls1RzaFwsB2BBgBCAAgBQJdPOn4AhsMFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcegLxwf/dXshJnoWqEnRsf6rVb9/Mc66ti+NVQLfNd275dybh/QIJdK3NmSxdnTPIEJRVspJywJoupwXFNrnHG2Ff6QPvHqI+/oNu986r9d7Z1oQibbLHKt8t6kpOfg/xGxotagJuiCQvR9mMjD1DqsdB37SjDxGupZOOJSXWi6KX60IE+uM+QOBfeOZziQwuFmA5wV6

View File

@@ -0,0 +1,13 @@
Return-Path: <x@testrun.org>
Received: from hq5.merlinux.eu
by hq5.merlinux.eu (Dovecot) with LMTP id yRKOBakcfV1AewAAPzvFDg
; Sat, 14 Sep 2019 19:00:25 +0200
Received: from localhost (unknown 7.165.105.24])
by hq5.merlinux.eu (Postfix) with ESMTPSA id 8D9844E023;
Sat, 14 Sep 2019 19:00:22 +0200 (CEST)
message-id: <2dfdbde7@example.org>
Date: Sat, 14 Sep 2019 19:00:13 +0200
From: lmn <x@tux.org>
To: abc <abc@bcd.com>, def <def@def.de>,
jik

View File

@@ -0,0 +1,125 @@
-----BEGIN PGP MESSAGE-----
Passphrase-Format: numeric9x4
Passphrase-Begin: 17
wy4ECQMI0jNRBQfVKHVg1+a2Yihd6JAjR9H0kk3oDVeX7nc4Oi+IjEtonUJt
PQpO0tPWASWYuYvjZSuTz9r1yZYV+y4mu9bu9NEQoRlWg2wnbjoUoKk4emFF
FweUj84iI6VWTCSRyMu5d5JS1RfOdX4CG/muLAegyIHezqYOEC0Z3b9Ci9rd
DiSgqqN+/LDkUR/vr7L2CSLN5suBP9Hsz75AtaV8DJ2DYDywYX89yH1CfL1O
WohyrJPdmGJZfdvQX0LI9mzN7MH0W6vUJeCaUpujc+UkLiOM6TDB74rmYF+V
Z7K9BXbaN4V6dyxVZfgpXUoZlaNpvqPJXuLHJ68umkuIgIyQvzmMj3mFgZ8s
akCt6Cf3o5O9n2PJvX89vuNnDGJrO5booEqGaBJfwUk0Rwb0gWsm5U0gceUz
dce8KZK15CzX+bNv5OC+8jjjBw7mBHVt+2q8LI+G9fEy9NIREkp5/v2ZRN0G
R6lpZwW+8TkMvJnriQeABqDpxsJVT6ENYAhkPG3AZCr/whGBU3EbDzPexXkz
qt8Pdu5DrazLSFtjpjkekrjCh43vHjGl8IOiWxKQx0VfBkHJ7O9CsHmb0r1o
F++fMh0bH1/aewmlg5wd0ixwZoP1o79he8Q4kfATZAjvB1xSLyMma+jxW5uu
U3wYUOsUmYmzo46/QzizFCUpaTJ4ZQZY1/4sflidsl/XgZ0fD1NCrdkWBNA1
0tQF949pEAeA4hSfHfQDNKAY8A7fk8lZblqWPkyu/0x8eV537QOhs89ZvhSB
V87KEAwxWt60+Eolf8PvvkvB/AKlfWq4MYShgyldwwCfkED3rv2mvTsdqfvW
WvqZNo4eRkJrnv9Be3LaXoFyY6a3z+ObBIkKI+u5azGJYge97O4E2DrUEKdQ
cScq5upzXity0E+Yhm964jzBzxnA52S4RoXzkjTxH+AHjQ5+MHQxmRfMd2ly
7skM106weVOR0JgOdkvfiOFDTHZLIVCzVyYVlOUJYYwPhmM1426zbegHNkaM
M2WgvjMp5G+X9qfDWKecntQJTziyDFZKfd1UrUCPHrvl1Ac9cuqgcCXLtdUS
jI+e1Y9fXvgyvHiMX0ztSz1yfvnRt34508G9j68fEQFQR/VIepULB5/SqKbq
p2flgJL48kY32hEw2GRPri64Tv3vMPIWa//zvQDhQPmcd3S4TqnTIIKUoTAO
NUo6GS9UAX12fdSFPZINcAkNIaB69+iwGyuJE4FLHKVkqNnNmDwF3fl0Oczo
hbboWzA3GlpR2Ri6kfe0SocfGR0CHT5ZmqI6es8hWx+RN8hpXcsRxGS0BMi2
mcJ7fPY+bKastnEeatP+b0XN/eaJAPZPZSF8PuPeQ0Uc735fylPrrgtWK9Gp
Wq0DPaWV/+O94OB/JvWT5wq7d/EEVbTck5FPl4gdv3HHpaaQ6/8G89wVMEXA
GUxB8WuvNeHAtQ7qXF7TkaZvUpF0rb1aV88uABOOPpsfAyWJo/PExCZacg8R
GOQYI6inV5HcGUw06yDSqArHZmONveqjbDBApenearcskv6Uz7q+Bp60GGSA
lvU3C3RyP/OUc1azOp72MIe0+JvP8S5DN9/Ltc/5ZyZHOjLoG+npIXnThYwV
0kkrlsi/7loCzvhcWOac1vrSaGVCfifkYf+LUFQFrFVbxKLOQ6vTsYZWM0yM
QsMMywW5A6CdROT5UB0UKRh/S1cwCwrN5UFTRt2UpDF3wSBAcChsHyy90RAL
Xd4+ZIyf29GIFuwwQyzGBWnXQ2ytU4kg/D5XSqJbJJTya386UuyQpnFjI19R
uuD0mvEfFvojCKDJDWguUNtWsHSg01NXDSrY26BhlOkMpUrzPfX5r0FQpgDS
zOdY9SIG+y9MKG+4nwmYnFM6V5NxVL+6XZ7BQTvlLIcIIu+BujVNWteDnWNZ
T1UukCGmFd8sNZpCc3wu4o/gLDQxih/545tWMf0dmeUfYhKcjSX9uucMRZHT
1N0FINw04fDdp2LccL+WCGatFGnkZVPw3asid4d1od9RG9DbNRBJEp/QeNhc
/peJCPLGYlA1NjTEq+MVB+DHdGNOuy//be3KhedBr6x4VVaDzL6jyHu/a7PR
BWRVtI1CIVDxyrEXucHdGQoEm7p+0G2zouOe/oxbPFoEYrjaI+0e/FN3u/Y3
aG0dlYWbxeHMqTh2F3lB/CFALReeGqqN6PwRyePWKaVctZYb6ydf9JVl6q1/
aV9C5rf9eFGqqA+OIx/+XuAG1w0rwlznvtajHzCoUeA4QfbmuOV/t5drWN2N
PCk2mJlcSmd7lx53rnOIgme1hggchjezc4TisL4PvSLxjJ7DxzktD2jv2I/Q
OlSxTUaXnGfIVedsI0WjFomz5w9tZjC0B5O5TpSRRz6gfpe/OC3kV7qs1YCS
lJTTxj1mTs6wqt0WjKkN/Ke0Cm5r7NQ79szDNlcC0AViEOQb3U1R88nNdiVx
ymKT5Dl+yM6acv53lNX6O5BH+mpP2/pCpi3x+kYFyr4cUsNgVVGlhmkPWctZ
trHvO7wcLrAsrLNqRxt1G3DLjQt9VY+w5qOPJv6s9qd5JBL/qtH5zqIXiXlM
IWI9LLwHFFXqjk/f6G4LyOeHB9AqccGQ4IztgzTKmYEmFWVIpTO4UN6+E7yQ
gtcYSIUEJo824ht5rL+ODqmCSAWsWIomEoTPvgn9QqO0YRwAEMpsFtE17klS
qjbYyV7Y5A0jpCvqbnGmZPqCgzjjN/p5VKSNjSdM0vdwBRgpXlyooXg/EGoJ
ZTZH8nLSuYMMu7AK8c7DKJ1AocTNYHRe9xFV8RzEiIm3zaezxa0r+Fo3nuTX
UR9DOH0EHaDLrFQcfS5y1iRxY9CHg0N2ECaUzr/H7jck9mLZ7v9xisj3QDuv
i0xQbC4BTxMEBGTK8fOcjHHOABOyhqotOreERqwOV2c1OOGUQE8QK18zJCUd
BTmQZ709ttASD7VWK4TraOGczZXkZsKdZko5T6+6EkFy9H+gwENLUG9zk0x9
2G5zicDr6PDoAGDuoB3B3VA8ertXTX7zEz30N6m+tcAtPWka0owokLy3f0o7
ZdytBPkly8foTMWKF2vsJ8K4Xdn/57jJ2qFku32xmtiPIoa6s8wINO06AVB0
0/AuttvxcPr+ycE+9wRZHx6JBujAqOZztU3zu8WZMaqVKb7gnmkWPiL+1XFp
2+mr0AghScIvjzTDEjigDtLydURJrW01wXjaR0ByBT4z8ZjaNmQAxIPOIRFC
bD0mviaoX61qgQLmSc6mzVlzzNZRCKtSvvGEK5NJ6CB6g2EeFau8+w0Zd+vv
/iv6Img3pUBgvpMaIsxRXvGZwmo2R0tztJt+CqHRvyTWjQL+CjIAWyoHEdVH
k7ne/q9zo3iIMsQUO7tVYtgURpRYc2OM1IVQtrgbmbYGEdOrhMjaWULg9C7o
6oDM0EFlCAId3P8ykXQNMluFKlf9il5nr19B/qf/wh6C7DFLOmnjTWDXrEiP
6wFEWTeUWLchGlbpiJFEu05MWPIRoRd3BHQvVpzLLgeBdxMVW7D6WCK+KJxI
W1rOKhhLVvKU3BrFgr12A4uQm+6w1j33Feh68Y0JB7GLDBBGe11QtLCD6kz5
RzFl+GbgiwpHi3nlCc5yiNwyPq/JRxU3GRb62YJcsSQBg+CD3Mk5FGiDcuvp
kZXOcTE2FAnUDigjEs+oH2qkhD4/5CiHkrfFJTzv+wqw+jwxPor2jkZH2akN
6PssXQYupXJE3NmcyaYT+b5E6qbkIyQj7CknkiqmrqrmxkOQxA+Ab2Vy9zrW
u0+Wvf+C+SebWTo3qfJZQ3KcASZHa5AGoSHetWzH2fNLIHfULXac/T++1DWE
nbeNvhXiFmAJ+BRsZj9p6RcnSamk4bjAbX1lg2G3Sq6MiA1fIRSMlSjuDLrQ
8xfVFrg7gfBIIQPErJWv2GdAsz76sLxuSXQLKYpFnozvMT7xRs84+iRNWWh9
SNibbEjlh0DcJlKw49Eis/bN22sDQWy4awHuRvvQetk/QCgp54epuqWnbxoE
XZDgGBBkMc3or+6Cxr3q9x7J/oHLvPb+Q5yVP9fyz6ZiSVWluMefA9smjJ/A
KMD84s7uO/8/4yug+swXGrcBjHSddTcy05vm+7X6o9IEZKZb5tz7VqAfEcuk
QNPUWCMudhzxSNr4+yVXRVpcjsjKtplJcXC5aIuJwq3C5OdysCGqXWjLuUu1
OFSoPvTsYC2VxYdFUcczeHEFTxXoXz3I0TyLPyxUNsJiKpUGt/SXmV/IyAx+
h6pZ2OUXspC9d78DdiHZtItPjEGiIb678ZyMxWPE59XQd/ad92mlPHU8InXD
yTq6otZ7LwAOLGbDR9bqN7oX8PCHRwuu30hk2b4+WkZn/WLd2KCPddQswZJg
Qgi5ajUaFhZvxF5YNTqIzzYVh7Y8fFMfzH9AO+SJqy+0ECX0GwtHHeVsXYNb
P/NO/ma4MI8301JyipPmdtzvvt9NOD/PJcnZH2KmDquARXMO/vKbn3rNUXog
pTFqqyNTr4L5FK86QPEoE4hDy9ItHGlEuiNVD+5suGVGUgYfV7AvZU46EeqO
rfFj8wNSX1aK/pIwWmh1EkygPSxomWRUANLX1jO6zX9wk2X80Xn9q/8jot1k
Vl54OOd7cvGls2wKkEZi5h3p6KKZHJ+WIDBQupeJbuma1GK8wAiwjDH59Y0X
wXHAk7XA+t4u0dgRpZbUUMqQmvEvfJaCr4qMlpuGdEYbbpIMUB1qCfYU9taL
zbepMIT+XYD5mTyytZhR+zrsfpt1EzbrhuabqPioySoIS/1+bWfxvndq16r0
AdNxR5LiVSVh8QJr3B/HJhVghgSVrrynniG3E94abNWL/GNxPS/dTHSf8ass
vbv7+uznADzHsMiG/ZlLAEkQJ9j0ENJvHmnayeVFIXDV6jPCcQJ+rURDgl7z
/qTLfe3o3zBMG78LcB+xDNXTQrK5Z0LX7h17hLSElpiUghFa9nviCsT0nkcr
nz302P4IOFwJuYMMCEfW+ywTn+CHpKjLHWkZSZ4q6LzNTbbgXZn/vh7njNf0
QHaHmaMNxnDhUw/Bl13uM52qtsfEYK07SEhLFlJbAk0G7q+OabK8dJxCRwS3
X9k4juzLUYhX8XBovg9G3YEVckb6iM8/LF/yvNXbUsPrdhYU9lPA63xD0Pgb
zthZCLIlnF+lS6e41WJv3n1dc4dFWD7F5tmt/7uwLC6oUGYsccSzY+bUkYhL
dp7tlQRd5AG/Xz8XilORk8cUjvi6uZss5LyQpKvGSU+77C8ZV/oS62BdS5TE
osBTrO2/9FGzQtHT+8DJSTPPgR6rcQUWLPemiG09ACKfRQ/g3b9Qj0upOcKL
6dti0lq7Aorc39vV18DPMFBOwzchUEBlBFyuSa4AoD30tsoilAC3qbzBwu3z
QLjmst76HEcWDkxgDAhlBz6/XgiVZsCivn7ygigmc2+hNEzIdDsKKfM9bkoe
3uJzmmsv8Bh5ZEtfGoGNmu/zA7tgvTOCBeotYeHr2O6pLmYb3hK+E/qCBl14
8pK4qYrjAlF+ZMq9BzXcaz5mRfKVfAQtghHOaNqopBczSE1bjFF6HaNhIaGa
N8YdabNQG7mLI/fgBxJfkPl6HdIhEpctp4RURbSFhW+wn0o85VyHM6a+6Vgj
NrYmhxPZ6N1KN0Qy76aNiw7nAToRRcOv87uZnkDIeVH8mP/0hldyiy/Y97cG
QgOeQHOG27QW57nHhqLRqvf0zzQZekuXWFbqajpaabEcdGXyiUpJ8/ZopBPM
AJwfkyA2LkV946IA4JV6sPnu9pYzpXQ4vdQKJ6DoDUyRTQmgmfSFGtfHAozY
V9k0iQeetSkYYtOagTrg3t92v7M00o/NJW/rKX4jj2djD8wtBovOcv4kxg4Z
o58Iv94ROim48XfyesvSYKN1xqqbXH4sfE6b4b9pLUxQVOmWANLK9MK8D+Ci
IvrGbz5U5bZP6vlNbe9bYzjvWTPjaMrjXknRTBcikavqOfDTSIVFtT4qvhvK
42PpOrm0qdiLwExGKQ9FfEfYZRgEcYRGg7rH3oNz6ZNOEXppF3tCl9yVOlFb
ygdIeT3Z3HeOQbAsi8jK7o16DSXL7ZOpFq9Bv9yzusrF7Eht/fSEpAVUO3D1
IuqjZcsQRhMtIvnF0oFujFtooJx9x3dj/RarvEGX/NzwATZkgJ+yWs2etruA
EzMQqED4j7Lb790zEWnt+nuHdCdlPnNy8RG5u5X62p3h5KqUbg9HfmIuuESi
hwr6dKsVQGc5XUB5KTt0dtjWlK5iaetDsZFuF5+aE0Xa6PmiQ2e7ZPFyxXmO
T/PSHzobx0qClKCu+tSWA1HDSL08IeoGZEyyhoaxyn5D9r1Mqg101v/iu59r
lRRs+plAhbuq5aQA3WKtF1N6Zb5+AVRpNUyrxyHoH36ddR4/n7lnIld3STGD
RqZLrOuKHS3dCNW2Pt15lU+loYsWFZwC6T/tAbvwhax+XaBMiKQSDFmG9sBw
TiM1JWXhq2IsjXBvCl6k2AKWLQOvc/Hin+oYs4d7M9mi0vdoEOAMadU/+Pqn
uZzP941mOUV5UeTCCbjpyfI7qtIi3TH1cQmC2kG2HrvQYuM6Momp//JusH1+
9eHgFo25HbitcKJ1sAqxsnYIW5/jIVyIJC7tatxmNfFQQ/LUb2cT+Jowwsf4
bbPinA9S6aQFy9k3vk07V2ouYl+cpMMXmNAUrboFRLxw7QDapWYMKdmnbU5O
HZuDz3iyrm0lMPsRtt/f5WUhZYY4vXT5/dj+8P6Pr5fdc4S84i5qEzf7bX/I
Sc6fpISdYBscfHdv6uXsEVtVPKEuQVYwhyc4kkwVKjZBaqsgjAA7VEhQXzO3
rC7di4UhabWQCQTG1GYZyrj4bm6dg/32uVxMoLS5kuSpi3nMz5JmQahLqRxh
argg13K2/MJ7w2AI23gCvO5bEmD1ZXIi1aGYdZfu7+KqrTumYxj0KgIesgU0
6ekmPh4Zu5lIyKopa89nfQVj3uKbwr9LLHegfzeMhvI5WQWghKcNcXEvJwSA
vEik5aXm2qSKXT+ijXBy5MuNeICoGaQ5WA0OJ30Oh5dN0XpLtFUWHZKThJvR
mngm1QCMMw2v/j8=
=9sJE
-----END PGP MESSAGE-----

File diff suppressed because one or more lines are too long