Compare commits

..

153 Commits

Author SHA1 Message Date
holger krekel
8294b5eb28 Merge branch 'master' into refactor-jobs 2019-08-19 09:30:53 +02:00
Alexander Krotov
1a8e08e429 Merge pull request #396 from deltachat/chat-array
Remove dc_array_t from chat.rs
2019-08-19 06:55:58 +00:00
holger krekel
bf99c0f2ba Merge branch 'master' into refactor-jobs
and fix conflicts
2019-08-19 08:40:02 +02:00
dignifiedquire
d47a693611 refactor: rename dc_qr -> qr 2019-08-19 08:06:54 +02:00
dignifiedquire
886262539a refactor: save lot implementation and follow up refactors
rewrote qr code to match the now safe lot
2019-08-19 08:06:54 +02:00
dignifiedquire
401c5a7cb0 refactor(lot): rename dc_lot to lot 2019-08-19 08:06:54 +02:00
dignifiedquire
b5c66dd52a refactor(lot): rust memory management 2019-08-19 08:06:54 +02:00
Alexander Krotov
e7cf5a546a Remove dc_array_t from chat.rs 2019-08-18 23:02:16 +03:00
dignifiedquire
c06cf4eba2 refactor(job): rusty and safe 2019-08-18 13:58:52 +02:00
dignifiedquire
fb7c095dad refactor(jobthread): safe and rusty 2019-08-18 13:58:25 +02:00
dignifiedquire
d55f6ee7c7 refactor: rename dc_qr -> qr 2019-08-18 13:58:07 +02:00
dignifiedquire
8a49ae2361 refactor: save lot implementation and follow up refactors
rewrote qr code to match the now safe lot
2019-08-18 13:58:07 +02:00
dignifiedquire
05ec266d9b refactor(lot): rename dc_lot to lot 2019-08-18 13:58:06 +02:00
dignifiedquire
9ec7833a50 refactor(lot): rust memory management 2019-08-18 13:58:06 +02:00
Friedel Ziegelmayer
8c10aa287c fix(python): free allocated string pointers (#393)
* fix(python): free allocated string pointers

* Update python/src/deltachat/cutil.py

Co-Authored-By: holger krekel  <holger@merlinux.eu>

* Update cutil.py

* probably the proper way to handle ffi.strings
2019-08-18 12:45:38 +02:00
Alexander Krotov
6b2fe03d08 Replace dc_array_t with Vec in context.rs 2019-08-18 12:44:49 +02:00
holger krekel
799d362654 cargo fmt 2019-08-18 12:44:03 +02:00
holger krekel
64beb17fa6 subst current_blocks with ok_to_continue 2019-08-18 12:44:03 +02:00
Friedel Ziegelmayer
2bddb8409c Merge pull request #390 from deltachat/remove-dead-code
remove dead code
2019-08-17 14:02:00 +02:00
B. Petersen
b1a056082a remove dead code 2019-08-17 12:36:26 +02:00
Friedel Ziegelmayer
7dc82ba4d7 Merge pull request #374 from deltachat/refactor-chat
refactor(chat): make it rusty and safe
2019-08-17 12:15:28 +02:00
dignifiedquire
4645a4300e refactor: remove unused methods 2019-08-17 11:40:43 +02:00
dignifiedquire
13d8306a7b refactor(chat): some minor cleanup and api improvements 2019-08-17 11:40:43 +02:00
dignifiedquire
9b1a74cc22 refactor(chat): remove C strings from the public interface 2019-08-17 11:40:43 +02:00
dignifiedquire
25e97df641 refactor(chat): store rust strings in the chat struct 2019-08-17 11:35:02 +02:00
dignifiedquire
6b3245ddfc refactor(ffi): use nicer exentsion traits for results 2019-08-17 11:34:10 +02:00
dignifiedquire
001880e1f0 refactor(chat): first round of method renaming and restructuring 2019-08-17 11:34:10 +02:00
dignifiedquire
ddfd067e97 refactor(chat): rust based memory management 2019-08-17 11:30:26 +02:00
dignifiedquire
64117c2964 refactor(chat): rename dc_chat to chat 2019-08-17 11:30:26 +02:00
dignifiedquire
c8ce099f22 refactor(chat): improve field types by using enums and bools 2019-08-17 11:29:08 +02:00
dignifiedquire
4878192a25 refactor: remove now unused saxparser 2019-08-17 01:32:43 +02:00
dignifiedquire
c901ab844f refactor(dehtml): switch to quick-xml for parsing 2019-08-17 01:32:43 +02:00
dignifiedquire
eb2b4028ab refactor(configure) use rust based xml parsing 2019-08-17 01:32:43 +02:00
dignifiedquire
64a0718032 fixup: dont panic on errors 2019-08-17 01:32:43 +02:00
dignifiedquire
b2a6876a50 refactor(location): switch to rust based xml parsing 2019-08-17 01:32:43 +02:00
holger krekel
5ea2da4245 cargo fmt 2019-08-17 01:28:04 +02:00
holger krekel
ff1f286882 remove goto/current_block logic with ok_to_continue 2019-08-17 01:28:04 +02:00
Alexander Krotov
e013532e27 Remove unused dc_array_get_string 2019-08-17 01:04:37 +02:00
Alexander Krotov
1a42a1e6b1 Replace dc_array with Vec in dc_receive_imf.rs 2019-08-17 01:04:37 +02:00
Jikstra
d946774741 Make dc_msg_get_summarytext_by_raw safe (#316)
* Make dc_msg_get_summarytext_by_raw safe

* use dc_truncate method in all places

* Fix tests and add docs to dc_truncate()

* Make text argument an AsRef<&str> and rename type_0 to viewtype

* Fix too early return in dc_msg_get_summarytext_by_raw. Fixes https://github.com/deltachat/deltachat-core-rust/issues/313
2019-08-16 14:58:20 +02:00
Friedel Ziegelmayer
3d080d2733 Remove dead code (#384)
Remove dead code
2019-08-16 11:13:40 +02:00
Dmitry Bogatov
aa7a149560 Check extension of files in case-insensitive way 2019-08-16 08:56:25 +02:00
Dmitry Bogatov
6beea86df7 Use perfrect hash map for new implementation of dc_msg_guess_msgtype_from_suffix 2019-08-16 08:56:25 +02:00
Dmitry Bogatov
5917d05305 Drop old version of {dc_msg_guess_msgtype_from_suffix} 2019-08-16 08:56:25 +02:00
Dmitry Bogatov
ceb4464a9b Adjust call sites to use new version of dc_msg_guess_msgtype_from_suffix 2019-08-16 08:56:25 +02:00
Dmitry Bogatov
8f0e554bc4 Implement Rust version of {dc_msg_guess_msgtype_from_suffix} 2019-08-16 08:56:25 +02:00
Dmitry Bogatov
7c7a1b64df Remove dead code 2019-08-16 04:46:20 +00:00
Dmitry Bogatov
ea661896a1 Do not export more than strictly necessary 2019-08-16 04:46:20 +00:00
Jikstra
2bb2ef07e9 Merge pull request #381 from deltachat/dc_array_ffi
Move some unsafe dc_array_* functions to FFI
2019-08-16 01:48:16 +02:00
Jikstra
c71934215b Merge pull request #382 from deltachat/correct-void-func
declare void function as such
2019-08-16 01:45:04 +02:00
Dmitry Bogatov
f31884fc15 Remove unused `dc_str_contains' function 2019-08-15 21:57:14 +00:00
Dmitry Bogatov
2b1ffb5fb9 Do not export `dc_strlower' function 2019-08-15 21:56:42 +00:00
Alexander Krotov
fadcd43fee Move some unsafe dc_array_* functions to FFI 2019-08-16 00:53:32 +03:00
B. Petersen
5af800b16a declare void function as such 2019-08-15 23:52:04 +02:00
Alexander Krotov
37622af55a Return Vec<dc_location> from dc_get_locations 2019-08-15 20:43:04 +02:00
jikstra
d4dfc5443c Add missing config keys which caused a panic in desktop 2019-08-15 20:38:22 +02:00
holger krekel
18b70bff0e remove redundant unsafe dc_str_to_color impl by calling safe one, fixes #375 2019-08-15 15:48:11 +02:00
jikstra
d4bd9187d6 Fix panic in dc_set_chat_profile_image because of missing
ok_to_continue=false
2019-08-15 13:36:43 +02:00
dignifiedquire
712f5a9782 refactor(tools): cleanup and make clippy a little happier
Uses a crate now for extracing image meta data, instead of our own hand rolled code.
2019-08-15 11:26:32 +02:00
Alexander Krotov
29d4f6888d Store IDs in dc_array as u32
This changes dc_array_get_raw API
2019-08-15 01:54:51 +02:00
Alexander Krotov
5b47409fb0 Remove unused dc_array_* API 2019-08-15 01:54:51 +02:00
Asiel Díaz Benítez
8077fdeddb Update examples.rst
just replaced `send_text_message` with `send_text`
2019-08-15 01:47:03 +02:00
dignifiedquire
6f290e249f chore: release v1.0.0-alpha.4 2019-08-14 13:58:51 +02:00
Simon Laux
a655f2cbba fix: properly handle chat name when contact has no name
* fix chat name empty when contact has no name
Show contacts display_name instead as defined in:
https://c.delta.chat/classdc__chat__t.html#a0fb1b4850bcd899eaa06d23648a1efaf
"For one-to-one chats, this is the name of the contact"
2019-08-14 13:55:56 +02:00
dignifiedquire
0cb42f840d fix(imap): call create, not subscribe
Closes #324
2019-08-14 11:49:39 +02:00
dignifiedquire
99aabef7f3 feat: load package version during build
This removes the duplication of the version string between the `Cargo.toml` and `constants.rs`
2019-08-14 10:35:32 +02:00
holger krekel
7d51c6e4f4 fix documentation 2019-08-14 10:18:06 +02:00
Dmitry Bogatov
f463fb3759 Change type of Context.dbfile to std::path::PathBuf
This change makes code more type-correct, but introduces copying: compiler
refuses to return reference to object behind Arc<RwLock>.
2019-08-14 09:43:26 +02:00
Dmitry Bogatov
cb0eb0e68e refactor: make encrypted_and_signed() return bool, not libc::int 2019-08-14 09:26:08 +02:00
holger krekel
8009f220fc hold smtp lock during the full send action 2019-08-14 01:50:15 +02:00
dignifiedquire
4f1551b91f chore(ci): enable livetests 2019-08-14 01:50:15 +02:00
dignifiedquire
6ba37a135e fix(imap): reduce lock contention around session and config locks
Hopefully closes #331
2019-08-14 01:50:15 +02:00
dignifiedquire
dab514d8bc feat: switch to simpler email address parsing
This stops the psl insanity and matches more closely what the c-core did

Closes #325
2019-08-14 01:48:41 +02:00
Dmitry Bogatov
8342b29618 Fix some clippy warnings 2019-08-14 01:45:39 +02:00
B. Petersen
e05944c6cb simplify progress! macro 2019-08-14 01:33:57 +02:00
Alexander Krotov
88a81f5737 Return Vec from get_all_blocked
And never return nullptr from dc_get_blocked_contacts.
2019-08-13 23:56:17 +02:00
B. Petersen
9cf6ca045c send CONFIGURE_PROGRESS(0) on failure and only on failure 2019-08-13 23:55:56 +02:00
holger krekel
ba381d0d0b fix failing array asserts #355 2019-08-13 23:07:38 +02:00
Friedel Ziegelmayer
033ebc7ce3 Setup clippy (#349)
Setup clippy
2019-08-13 22:37:16 +02:00
holger krekel
6292219551 fix bug that lead to all liveconfig tests failing 2019-08-13 22:34:27 +02:00
Friedel Ziegelmayer
ed237c8d25 refactor(receive_imf): first pass at some more sanity 2019-08-13 17:58:32 +02:00
Alexander Krotov
d46a5345d2 Use Vec instead of dc_array_t in search_chat_ids_by_contact_ids 2019-08-13 17:54:28 +02:00
dignifiedquire
523141597e chore: remove no longer needed features 2019-08-13 12:20:53 +02:00
dignifiedquire
cfed5c914c chore: update rust nightly version 2019-08-13 12:20:53 +02:00
dignifiedquire
20f9bb3b14 chore: setup clippy 2019-08-13 12:20:53 +02:00
Friedel Ziegelmayer
6067160582 Implement procedural macro to derive {To,From}Sql traits (#322)
Implement procedural macro to derive {To,From}Sql traits
2019-08-13 12:16:24 +02:00
Alexander Krotov
3175c4f7ba Merge pull request #333 from link2xt/dc_array-assert
dc_array: panic on null pointers and out of range indexes
2019-08-13 12:53:46 +03:00
Simon Laux
a29f06a730 cargo fmt 2019-08-13 10:45:27 +02:00
Simon Laux
c713474d1f remove lot magic 2019-08-13 10:45:27 +02:00
Simon Laux
89c874d4a9 remove message magic
there is still a reference to C #[repr(C)] so I'm not sure at this
2019-08-13 10:45:27 +02:00
Simon Laux
5e3cba9b70 remove chat magic 2019-08-13 10:45:27 +02:00
Dmitry Bogatov
a7894fd785 cargo fmt 2019-08-13 10:11:17 +02:00
Dmitry Bogatov
c638a770f9 Remove redundant check
Condition '!imffields.is_null()' is always true, since it is contained in conditional

	  if !(in_out_message.is_null() || imffields.is_null()) {

which is equivalent to

	  if !in_out_message.is_null() && !imffields.is_null() {
2019-08-13 10:11:17 +02:00
Simon Laux
6ced6ac23b macro for progress event like in C core 2019-08-13 10:07:13 +02:00
Simon Laux
d0b77b61eb cargo fmt 2019-08-13 10:07:13 +02:00
Simon Laux
b440c3636b replace gotos with ok_to_continue 2019-08-13 10:07:13 +02:00
Floris Bruynooghe
dfd58961f7 Safe load_or_generate_self_public_key
The function is made safe and now returns Result.  Functionally it now
fails when it can not write the newly generated key to the database
whereas before it still returned the key but logged a warning.  There
is no reason this shouldn't be able to store the key and silently not
storing the key may result in later operations assuming the key is
available, so failing seems like a better choice.

The function now also uses a proper mutex to guard against multiple
threads generating keys.  And this mutex is Context-scoped rather than
fully global (static).
2019-08-13 10:04:38 +02:00
Jikstra
139c9f37b1 Merge pull request #339 from deltachat/rm_goto_dc_job
rm GOTO: dc job
2019-08-13 02:10:08 +02:00
Jikstra
2445b12898 Merge pull request #342 from deltachat/rm_goto_cmdline
rm GOTO: cmdline
2019-08-13 02:09:14 +02:00
Alexander Krotov
4d402f3a06 dc_array: panic on null pointers and out of range indexes 2019-08-13 03:07:13 +03:00
Jikstra
ab022ccc33 Merge pull request #334 from link2xt/dc_get_chat_contacts_vec
Return Vec from dc_get_chat_contacts
2019-08-13 02:03:58 +02:00
Alexander Krotov
fb7bbac524 Return Vec from dc_get_chat_contacts 2019-08-13 02:37:18 +03:00
Jikstra
39fbff5fb6 Merge pull request #347 from link2xt/dc_array_t-new
Implement From<Vec<u32>> for dc_array_t and use it instead of new()
2019-08-13 01:30:10 +02:00
Jikstra
3ac1eaf7d2 Merge pull request #341 from deltachat/add_cmdline_quit_cmd
add quit command as alias to exit in cmdline
2019-08-13 00:49:27 +02:00
Alexander Krotov
6c95d008e0 Implement From<Vec<u32>> for dc_array_t and use it instead of new() 2019-08-13 01:40:47 +03:00
Friedel Ziegelmayer
16f891c290 Merge pull request #337 from deltachat/fix-cmdline
fix 'chats' command in cmdline tool
2019-08-12 10:39:11 +02:00
holger krekel
650bddd54b try fix upload failure with / branches 2019-08-12 08:38:15 +02:00
Simon Laux
9e30df4b43 cargo fmt 2019-08-12 05:42:02 +02:00
Simon Laux
50c592e41f convert current_block to goto and remove UDC*
*Unreachable Duplicated Code (I made that shortcut up)
2019-08-12 05:41:24 +02:00
Simon Laux
bdf8cd2dd5 add quit command as alias to exit in cmdline 2019-08-12 01:52:09 +02:00
B. Petersen
5554df29fd show full chatlist by just entering 'chats' in cmdline 2019-08-12 01:40:04 +02:00
Simon Laux
2dd3088f50 cargo fmt 2019-08-12 01:33:07 +02:00
Simon Laux
b9bd128c7a goto to ok_to_continue 2019-08-12 01:32:34 +02:00
B. Petersen
adb67d1910 off by one: show chats cnt-1..0 instead of cnt-1..1 2019-08-12 01:28:36 +02:00
Jikstra
ce3b815bd8 Merge pull request #319 from deltachat/fix_utf8_text_msg_load_from_db
Fix having a msg object without a text in it because of invalid utf8
2019-08-12 01:16:23 +02:00
holger krekel
b94f9ef496 address @flub comments 2019-08-11 23:09:48 +02:00
holger krekel
77db475663 - rework running of liveconfig tests
- better README reflecting how to use things, don't advertise
  run-integration-tests to only have one documented way
  and use less tools for rust-devs that just want to run
  python tests

- fix test skipping and get circle-ci to play along

- update docker related docs as well
2019-08-11 23:09:48 +02:00
jikstra
a3683be047 cargo fmt 2019-08-11 18:55:23 +02:00
Jikstra
9dca19d6c9 Merge pull request #302 from deltachat/rm_goto_dc_imex
Remove goto in dc imex
2019-08-11 18:52:17 +02:00
jikstra
3ba847ece2 Apply requested changes 2019-08-11 16:57:49 +02:00
Simon Laux
91bf948d1e chat magic to const 2019-08-11 10:45:17 +02:00
Simon Laux
91fec77f4b fix msg info: message-id 2019-08-11 09:05:56 +02:00
Simon Laux
8fb25a6340 Cargo fmt: removed two empty llines 2019-08-11 09:04:47 +02:00
Simon Laux
cf49acff67 part 2 2019-08-11 02:07:51 +02:00
Simon Laux
4f1a25e1bf cargo fmt 2019-08-11 02:07:51 +02:00
Simon Laux
8608daa7dc remove goto 2019-08-11 02:07:51 +02:00
Alexander Krotov
828e6e3fd0 Merge pull request #320 from link2xt/dc_tools_files
dc_tools: rustify interfaces of file-related functions
2019-08-10 23:10:24 +00:00
Alexander Krotov
ff021fed1f dc_tools: rustify interfaces of file-related functions 2019-08-10 21:15:48 +03:00
Dmitry Bogatov
ed66f36cb5 Implement procedural macro to derive {To,From}Sql traits
With this macro it is possible to #[derive(ToSql, FromSql)] for enums, that do
not contain data (C-style).
2019-08-10 17:52:48 +00:00
jikstra
b7ff996b15 Cargo fmt + refactoring 2019-08-10 17:57:53 +02:00
jikstra
faf53fe11e Manually get a lossy utf8 string from the database if other fails 2019-08-10 17:53:05 +02:00
jikstra
b23c4b4da6 Remove debug printlns, refactor a bit 2019-08-10 16:51:57 +02:00
jikstra
966bb2271a Put something into the msg object if we fail to get a valid string out of the db 2019-08-10 16:47:38 +02:00
Floris Bruynooghe
5438be891b Remove dc_context_unref from Rust API
This removes the dc_context_unref function from the Rust API which was
just an alias for dc_close.  It still exists on the C API where it
makes sure to free the memory.

It also implements Drop for the context which just calls dc_close to
make sure all the memory is freed.  Since you can call dc_close as
many times as you like this ensures that at the Rust level you can't
Drop the struct without releasing the memory.

Finally since memory is now freed by dropping the struct this removes
the #[repr(C)] for the struct.  This struct is fully opaque to the C
API.
2019-08-10 12:04:11 +02:00
Floris Bruynooghe
f31f603c8b Turn dc_ensure_secret_key_existy into something more rusty
This marks the function safe and returns Result, it also now returns
the ConfiguredAddr since it has to look this up anyway and it makes
testing more easy.  Turns out it reduces some duplicate SQL query in
some callers too.

More test code has been moved from dc_imex to test_utils as it's
more genrally applicable.
2019-08-10 11:14:40 +02:00
Dmitry Bogatov
ff39fa0fed Remove useless cast of format! argument
Both `c_int' and `u32' format is same way by default formatter `{}'.
2019-08-10 03:00:43 +02:00
Dmitry Bogatov
cdf3809634 Set type of dc_msg_t.server_folder to Option<String> 2019-08-10 03:00:43 +02:00
Simon Laux
b2a2791f6f cargo fmt 2019-08-09 23:49:54 +02:00
Simon Laux
125df968d2 replace goto with ok_to_continue 2019-08-09 23:49:54 +02:00
Dmitry Bogatov
1c5d07a29f Reimplement dc_str_replace() with standard Rust functions
This change removes one more use of unstable `wrapping_offset_of'.
2019-08-09 23:40:44 +02:00
Dmitry Bogatov
24b025f573 Replace DC_MOVE_* constants with enum 2019-08-09 21:51:33 +02:00
jikstra
d323bd3593 Write tests and docs for dc_strdup and dc_strdup_keep_null 2019-08-09 19:30:36 +02:00
Alexander Krotov
b7174783f1 Pass is_html to Simplify.simplify() as bool 2019-08-09 16:57:14 +02:00
Alexander Krotov
e3269616bd Use bool for Simplify members 2019-08-09 16:57:14 +02:00
Alexander Krotov
64051fca10 Rename dc_simplify_t into Simplify 2019-08-09 16:57:14 +02:00
Alexander Krotov
14ce55b1a8 Remove #[repr(C)] from dc_simplify_t 2019-08-09 16:57:14 +02:00
holger krekel
be605d8ea5 fix(peerstate): encryption-not-available
Add a test for failing e2e encryption and some info statement to hunt where the e2e encryption failure comes from, as well as fix the issue.


Closes #233
2019-08-09 13:28:48 +02:00
Friedel Ziegelmayer
4d8d5f4e1e Fix broken string allocations in message handling (#306)
Fix broken string allocations in message handling
2019-08-09 11:32:42 +02:00
holger krekel
750d6e99a8 fix some longer standing nonsense code that sent to misleading MSG_READ events instead of one correct one 2019-08-09 11:32:24 +02:00
holger krekel
a67892d414 (jikstra, hpk) fix a logic bug introduced with the stock-string merge which set the better message only if it was empty 2019-08-09 11:32:24 +02:00
holger krekel
1cd2a62caf fix a failure which blocked correctly sending out messages (dc_job_add_smtp mis-set filename) 2019-08-09 11:32:24 +02:00
dignifiedquire
6772d6f66c fix: improve some string handling in the message recieve path 2019-08-09 11:32:24 +02:00
Simon Laux
89531dfb62 fix(dc_lot): correct test2 2019-08-09 11:31:11 +02:00
75 changed files with 9829 additions and 12021 deletions

View File

@@ -13,7 +13,7 @@ restore-workspace: &restore-workspace
restore-cache: &restore-cache
restore_cache:
keys:
- cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- repo-source-{{ .Branch }}-{{ .Revision }}
commands:
@@ -53,10 +53,11 @@ jobs:
command: cargo generate-lockfile
- restore_cache:
keys:
- cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- run: rustup install $(cat rust-toolchain)
- run: rustup default $(cat rust-toolchain)
- run: rustup component add --toolchain $(cat rust-toolchain) rustfmt
- run: rustup component add --toolchain $(cat rust-toolchain) clippy-preview
- run: cargo update
- run: cargo fetch
- run: rustc +stable --version
@@ -67,7 +68,7 @@ jobs:
paths:
- crate
- save_cache:
key: cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
key: cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
paths:
- "~/.cargo"
- "~/.rustup"
@@ -160,6 +161,15 @@ jobs:
- run: ls -laR workspace
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse
clippy:
executor: default
steps:
- *restore-workspace
- *restore-cache
- run:
name: Run cargo clippy
command: cargo clippy --all
workflows:
version: 2.1
@@ -175,6 +185,10 @@ workflows:
requires:
- cargo_fetch
- clippy:
requires:
- cargo_fetch
# Linux Desktop 64bit
- test_x86_64-unknown-linux-gnu:
requires:

689
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat"
version = "1.0.0-alpha.3"
version = "1.0.0-alpha.4"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
edition = "2018"
license = "MPL"
@@ -10,11 +10,13 @@ cc = "1.0.35"
pkg-config = "0.3"
[dependencies]
deltachat_derive = { path = "./deltachat_derive" }
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"
@@ -36,7 +38,6 @@ rustyline = "4.1.0"
lazy_static = "1.3.0"
regex = "1.1.6"
rusqlite = { version = "0.20", features = ["bundled"] }
addr = "0.2.0"
r2d2_sqlite = "0.12.0"
r2d2 = "0.8.5"
strum = "0.15.0"
@@ -45,6 +46,9 @@ thread-local-object = "0.1.0"
backtrace = "0.3.33"
byteorder = "1.3.1"
itertools = "0.8.0"
image-meta = "0.1.0"
quick-xml = "0.15.0"
escaper = "0.1.0"
[dev-dependencies]
tempfile = "3.0"
@@ -53,7 +57,8 @@ pretty_env_logger = "0.3.0"
[workspace]
members = [
"deltachat-ffi"
"deltachat-ffi",
"deltachat_derive",
]
[[example]]

View File

@@ -89,6 +89,14 @@ $ cargo test --all
$ cargo build -p deltachat_ffi --release
```
### Expensive tests
Some tests are expensive and marked with `#[ignore]`, to run these
use the `--ignored` argument to the test binary (not to cargo itself):
```sh
$ cargo test -- --ignored
```
## Features
- `vendored`: When using Openssl for TLS, this bundles a vendored version.

6
Xargo.toml Normal file
View File

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

View File

@@ -16,7 +16,7 @@ export BRANCH=${CIRCLE_BRANCH:-test7}
#fi
# run everything else inside docker (TESTS, DOCS, WHEELS)
docker run -e BRANCH -e TESTS -e DOCS \
docker run -e DCC_PY_LIVECONFIG -e BRANCH -e TESTS -e DOCS \
--rm -it -v $(pwd):/mnt -w /mnt \
deltachat/coredeps ci_scripts/run_all.sh

View File

@@ -15,6 +15,7 @@ export BRANCH=${CIRCLE_BRANCH:?specify branch for uploading purposes}
# python docs to py.delta.chat
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@py.delta.chat mkdir -p build/${BRANCH}
rsync -avz \
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
"$PYDOCDIR/html/" \
@@ -37,11 +38,13 @@ pip install devpi-client
devpi use https://m.devpi.net
devpi login dc --password $DEVPI_LOGIN
devpi use dc/$BRANCH || {
devpi index -c $BRANCH
devpi use dc/$BRANCH
N_BRANCH=${BRANCH//[\/]}
devpi use dc/$N_BRANCH || {
devpi index -c $N_BRANCH
devpi use dc/$N_BRANCH
}
devpi index $BRANCH bases=/root/pypi
devpi index $N_BRANCH bases=/root/pypi
devpi upload deltachat*.whl
popd

View File

@@ -3,9 +3,9 @@
set -e -x
# Install Rust
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-04-19 -y
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-07-10 -y
export PATH=/root/.cargo/bin:$PATH
rustc --version
# remove some 300-400 MB that we don't need for automated builds
rm -rf /root/.rustup/toolchains/nightly-2019-04-19-x86_64-unknown-linux-gnu/share/
rm -rf /root/.rustup/toolchains/nightly-2019-07-10-x86_64-unknown-linux-gnu/share/

View File

@@ -37,6 +37,10 @@ if [ -n "$TESTS" ]; then
export PYTHONDONTWRITEBYTECODE=1
# run tox
# XXX we don't run liveconfig tests because they hang sometimes
# see https://github.com/deltachat/deltachat-core-rust/issues/331
# unset DCC_PY_LIVECONFIG
tox --workdir "$TOXWORKDIR" -e lint,py27,py35,py36,py37,auditwheels
popd
fi

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat_ffi"
version = "1.0.0-alpha.3"
version = "1.0.0-alpha.4"
description = "Deltachat FFI"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
edition = "2018"
@@ -25,4 +25,3 @@ default = ["vendored", "nightly", "ringbuf"]
vendored = ["deltachat/vendored"]
nightly = ["deltachat/nightly"]
ringbuf = ["deltachat/ringbuf"]

View File

@@ -372,14 +372,10 @@ void dc_delete_all_locations (dc_context_t*);
*/
void dc_array_unref (dc_array_t*);
void dc_array_add_uint (dc_array_t*, uintptr_t);
void dc_array_add_id (dc_array_t*, uint32_t);
void dc_array_add_ptr (dc_array_t*, void*);
size_t dc_array_get_cnt (const dc_array_t*);
uintptr_t dc_array_get_uint (const dc_array_t*, size_t index);
uint32_t dc_array_get_id (const dc_array_t*, size_t index);
void* dc_array_get_ptr (const dc_array_t*, size_t index);
double dc_array_get_latitude (const dc_array_t*, size_t index);
double dc_array_get_longitude (const dc_array_t*, size_t index);
double dc_array_get_accuracy (const dc_array_t*, size_t index);
@@ -391,7 +387,7 @@ char* dc_array_get_marker (const dc_array_t*, size_t index);
int dc_array_is_independent (const dc_array_t*, size_t index);
int dc_array_search_id (const dc_array_t*, uint32_t needle, size_t* indx);
const uintptr_t* dc_array_get_raw (const dc_array_t*);
const uint32_t* dc_array_get_raw (const dc_array_t*);
/**
@@ -598,8 +594,6 @@ int dc_contact_is_verified (dc_contact_t*);
#define DC_TEXT1_SELF 3
dc_lot_t* dc_lot_new ();
void dc_lot_empty (dc_lot_t*);
void dc_lot_unref (dc_lot_t*);
char* dc_lot_get_text1 (const dc_lot_t*);
char* dc_lot_get_text2 (const dc_lot_t*);

View File

@@ -12,6 +12,8 @@ extern crate human_panic;
extern crate num_traits;
use num_traits::{FromPrimitive, ToPrimitive};
use std::convert::TryInto;
use std::ptr;
use std::str::FromStr;
use deltachat::contact::Contact;
@@ -44,11 +46,14 @@ pub unsafe extern "C" fn dc_context_new(
Box::into_raw(Box::new(ctx))
}
/// Release the context structure.
///
/// This function releases the memory of the `dc_context_t` structure.
#[no_mangle]
pub unsafe extern "C" fn dc_context_unref(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &mut *context;
context::dc_context_unref(context);
context::dc_close(context);
Box::from_raw(context);
}
@@ -184,7 +189,7 @@ pub unsafe extern "C" fn dc_perform_imap_jobs(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_perform_imap_jobs(context)
job::perform_imap_jobs(context)
}
#[no_mangle]
@@ -192,7 +197,7 @@ pub unsafe extern "C" fn dc_perform_imap_fetch(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_perform_imap_fetch(context)
job::perform_imap_fetch(context)
}
#[no_mangle]
@@ -200,7 +205,7 @@ pub unsafe extern "C" fn dc_perform_imap_idle(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_perform_imap_idle(context)
job::perform_imap_idle(context)
}
#[no_mangle]
@@ -208,7 +213,7 @@ pub unsafe extern "C" fn dc_interrupt_imap_idle(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_interrupt_imap_idle(context)
job::interrupt_imap_idle(context)
}
#[no_mangle]
@@ -216,7 +221,7 @@ pub unsafe extern "C" fn dc_perform_mvbox_fetch(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_perform_mvbox_fetch(context)
job::perform_mvbox_fetch(context)
}
#[no_mangle]
@@ -224,7 +229,7 @@ pub unsafe extern "C" fn dc_perform_mvbox_idle(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_perform_mvbox_idle(context)
job::perform_mvbox_idle(context)
}
#[no_mangle]
@@ -232,7 +237,7 @@ pub unsafe extern "C" fn dc_interrupt_mvbox_idle(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_interrupt_mvbox_idle(context)
job::interrupt_mvbox_idle(context)
}
#[no_mangle]
@@ -240,7 +245,7 @@ pub unsafe extern "C" fn dc_perform_sentbox_fetch(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_perform_sentbox_fetch(context)
job::perform_sentbox_fetch(context)
}
#[no_mangle]
@@ -248,7 +253,7 @@ pub unsafe extern "C" fn dc_perform_sentbox_idle(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_perform_sentbox_idle(context)
job::perform_sentbox_idle(context)
}
#[no_mangle]
@@ -256,7 +261,7 @@ pub unsafe extern "C" fn dc_interrupt_sentbox_idle(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_interrupt_sentbox_idle(context)
job::interrupt_sentbox_idle(context)
}
#[no_mangle]
@@ -264,7 +269,7 @@ pub unsafe extern "C" fn dc_perform_smtp_jobs(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_perform_smtp_jobs(context)
job::perform_smtp_jobs(context)
}
#[no_mangle]
@@ -272,7 +277,7 @@ pub unsafe extern "C" fn dc_perform_smtp_idle(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_perform_smtp_idle(context)
job::perform_smtp_idle(context)
}
#[no_mangle]
@@ -280,7 +285,7 @@ pub unsafe extern "C" fn dc_interrupt_smtp_idle(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_interrupt_smtp_idle(context)
job::interrupt_smtp_idle(context)
}
#[no_mangle]
@@ -288,7 +293,7 @@ pub unsafe extern "C" fn dc_maybe_network(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_job::dc_maybe_network(context)
job::maybe_network(context)
}
#[no_mangle]
@@ -318,7 +323,7 @@ pub unsafe extern "C" fn dc_create_chat_by_msg_id(context: *mut dc_context_t, ms
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_create_chat_by_msg_id(context, msg_id)
chat::create_by_msg_id(context, msg_id).unwrap_or_log_default(context, "Failed to create chat")
}
#[no_mangle]
@@ -329,7 +334,8 @@ pub unsafe extern "C" fn dc_create_chat_by_contact_id(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_create_chat_by_contact_id(context, contact_id)
chat::create_by_contact_id(context, contact_id)
.unwrap_or_log_default(context, "Failed to create chat")
}
#[no_mangle]
@@ -340,7 +346,8 @@ pub unsafe extern "C" fn dc_get_chat_id_by_contact_id(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_get_chat_id_by_contact_id(context, contact_id)
chat::get_by_contact_id(context, contact_id)
.unwrap_or_log_default(context, "Failed to get chat")
}
#[no_mangle]
@@ -353,7 +360,8 @@ pub unsafe extern "C" fn dc_prepare_msg(
assert!(!msg.is_null());
let context = &*context;
dc_chat::dc_prepare_msg(context, chat_id, msg)
chat::prepare_msg(context, chat_id, msg)
.unwrap_or_log_default(context, "Failed to prepare message")
}
#[no_mangle]
@@ -366,7 +374,7 @@ pub unsafe extern "C" fn dc_send_msg(
assert!(!msg.is_null());
let context = &*context;
dc_chat::dc_send_msg(context, chat_id, msg)
chat::send_msg(context, chat_id, msg).unwrap_or_log_default(context, "Failed to send message")
}
#[no_mangle]
@@ -380,7 +388,8 @@ pub unsafe extern "C" fn dc_send_text_msg(
let context = &*context;
let text_to_send = dc_tools::to_string_lossy(text_to_send);
dc_chat::dc_send_text_msg(context, chat_id, text_to_send)
chat::send_text_msg(context, chat_id, text_to_send)
.unwrap_or_log_default(context, "Failed to send text message")
}
#[no_mangle]
@@ -392,7 +401,7 @@ pub unsafe extern "C" fn dc_set_draft(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_set_draft(context, chat_id, msg)
chat::set_draft(context, chat_id, msg)
}
#[no_mangle]
@@ -403,7 +412,7 @@ pub unsafe extern "C" fn dc_get_draft<'a>(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_get_draft(context, chat_id)
chat::get_draft(context, chat_id)
}
#[no_mangle]
@@ -416,7 +425,7 @@ pub unsafe extern "C" fn dc_get_chat_msgs(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_get_chat_msgs(context, chat_id, flags, marker1before)
dc_array_t::from(chat::get_chat_msgs(context, chat_id, flags, marker1before)).into_raw()
}
#[no_mangle]
@@ -424,7 +433,7 @@ pub unsafe extern "C" fn dc_get_msg_cnt(context: *mut dc_context_t, chat_id: u32
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_get_msg_cnt(context, chat_id)
chat::get_msg_cnt(context, chat_id) as libc::c_int
}
#[no_mangle]
@@ -435,7 +444,7 @@ pub unsafe extern "C" fn dc_get_fresh_msg_cnt(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_get_fresh_msg_cnt(context, chat_id)
chat::get_fresh_msg_cnt(context, chat_id) as libc::c_int
}
#[no_mangle]
@@ -445,7 +454,7 @@ pub unsafe extern "C" fn dc_get_fresh_msgs(
assert!(!context.is_null());
let context = &*context;
context::dc_get_fresh_msgs(context)
dc_array_t::from(context::dc_get_fresh_msgs(context)).into_raw()
}
#[no_mangle]
@@ -453,7 +462,7 @@ pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_marknoticed_chat(context, chat_id);
chat::marknoticed_chat(context, chat_id).log_err(context, "Failed marknoticed chat");
}
#[no_mangle]
@@ -461,7 +470,7 @@ pub unsafe extern "C" fn dc_marknoticed_all_chats(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_marknoticed_all_chats(context);
chat::marknoticed_all_chats(context).log_err(context, "Failed marknoticed all chats");
}
fn from_prim<S, T>(s: S) -> Option<T>
@@ -489,7 +498,14 @@ pub unsafe extern "C" fn dc_get_chat_media(
let or_msg_type3 =
from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3));
dc_chat::dc_get_chat_media(context, chat_id, msg_type, or_msg_type2, or_msg_type3)
dc_array_t::from(chat::get_chat_media(
context,
chat_id,
msg_type,
or_msg_type2,
or_msg_type3,
))
.into_raw()
}
#[no_mangle]
@@ -510,7 +526,7 @@ pub unsafe extern "C" fn dc_get_next_media(
let or_msg_type3 =
from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3));
dc_chat::dc_get_next_media(context, msg_id, dir, msg_type, or_msg_type2, or_msg_type3)
chat::get_next_media(context, msg_id, dir, msg_type, or_msg_type2, or_msg_type3)
}
#[no_mangle]
@@ -522,7 +538,15 @@ pub unsafe extern "C" fn dc_archive_chat(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_archive_chat(context, chat_id, archive);
let archive = if archive == 0 {
false
} else if archive == 1 {
true
} else {
return;
};
chat::archive(context, chat_id, archive).log_err(context, "Failed archive chat");
}
#[no_mangle]
@@ -530,8 +554,7 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32
assert!(!context.is_null());
let context = &*context;
// TODO: update to indicate public api success/failure of deletion
dc_chat::dc_delete_chat(context, chat_id);
chat::delete(context, chat_id).log_err(context, "Failed chat delete");
}
#[no_mangle]
@@ -542,7 +565,7 @@ pub unsafe extern "C" fn dc_get_chat_contacts(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_get_chat_contacts(context, chat_id)
dc_array_t::from(chat::get_chat_contacts(context, chat_id)).into_raw()
}
#[no_mangle]
@@ -555,7 +578,7 @@ pub unsafe extern "C" fn dc_search_msgs(
assert!(!query.is_null());
let context = &*context;
context::dc_search_msgs(context, chat_id, query)
dc_array_t::from(context::dc_search_msgs(context, chat_id, query)).into_raw()
}
#[no_mangle]
@@ -566,7 +589,10 @@ pub unsafe extern "C" fn dc_get_chat<'a>(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_get_chat(context, chat_id)
match chat::Chat::load_from_db(context, chat_id) {
Ok(chat) => Box::into_raw(Box::new(chat)),
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
@@ -579,7 +605,14 @@ pub unsafe extern "C" fn dc_create_group_chat(
assert!(!name.is_null());
let context = &*context;
dc_chat::dc_create_group_chat(context, verified, name)
let verified = if let Some(s) = contact::VerifiedStatus::from_i32(verified) {
s
} else {
return 0;
};
chat::create_group_chat(context, verified, as_str(name))
.unwrap_or_log_default(context, "Failed to create group chat")
}
#[no_mangle]
@@ -591,7 +624,7 @@ pub unsafe extern "C" fn dc_is_contact_in_chat(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_is_contact_in_chat(context, chat_id, contact_id)
chat::is_contact_in_chat(context, chat_id, contact_id)
}
#[no_mangle]
@@ -603,7 +636,7 @@ pub unsafe extern "C" fn dc_add_contact_to_chat(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_add_contact_to_chat(context, chat_id, contact_id)
chat::add_contact_to_chat(context, chat_id, contact_id)
}
#[no_mangle]
@@ -615,7 +648,9 @@ pub unsafe extern "C" fn dc_remove_contact_from_chat(
assert!(!context.is_null());
let context = &*context;
dc_chat::dc_remove_contact_from_chat(context, chat_id, contact_id)
chat::remove_contact_from_chat(context, chat_id, contact_id)
.map(|_| 1)
.unwrap_or_log_default(context, "Failed to remove contact")
}
#[no_mangle]
@@ -629,7 +664,9 @@ pub unsafe extern "C" fn dc_set_chat_name(
assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32);
let context = &*context;
dc_chat::dc_set_chat_name(context, chat_id, name)
chat::set_chat_name(context, chat_id, as_str(name))
.map(|_| 1)
.unwrap_or_log_default(context, "Failed to set chat name")
}
#[no_mangle]
@@ -642,7 +679,9 @@ pub unsafe extern "C" fn dc_set_chat_profile_image(
assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32);
let context = &*context;
dc_chat::dc_set_chat_profile_image(context, chat_id, image)
chat::set_chat_profile_image(context, chat_id, as_str(image))
.map(|_| 1)
.unwrap_or_log_default(context, "Failed to set profile image")
}
#[no_mangle]
@@ -694,7 +733,7 @@ pub unsafe extern "C" fn dc_forward_msgs(
assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32);
let context = &*context;
dc_chat::dc_forward_msgs(context, msg_ids, msg_cnt, chat_id)
chat::forward_msgs(context, msg_ids, msg_cnt, chat_id)
}
#[no_mangle]
@@ -814,7 +853,7 @@ pub unsafe extern "C" fn dc_get_contacts(
};
match Contact::get_all(context, flags, query) {
Ok(contacts) => contacts,
Ok(contacts) => dc_array_t::from(contacts).into_raw(),
Err(_) => std::ptr::null_mut(),
}
}
@@ -834,7 +873,7 @@ pub unsafe extern "C" fn dc_get_blocked_contacts(
assert!(!context.is_null());
let context = &*context;
Contact::get_all_blocked(context)
dc_array_t::from(Contact::get_all_blocked(context)).into_raw()
}
#[no_mangle]
@@ -861,7 +900,12 @@ pub unsafe extern "C" fn dc_get_contact_encrinfo(
assert!(!context.is_null());
let context = &*context;
Contact::get_encrinfo(context, contact_id).strdup()
Contact::get_encrinfo(context, contact_id)
.map(|s| s.strdup())
.unwrap_or_else(|e| {
error!(context, 0, "{}", e);
std::ptr::null_mut()
})
}
#[no_mangle]
@@ -949,12 +993,13 @@ pub unsafe extern "C" fn dc_stop_ongoing_process(context: *mut dc_context_t) {
pub unsafe extern "C" fn dc_check_qr(
context: *mut dc_context_t,
qr: *mut libc::c_char,
) -> *mut dc_lot::dc_lot_t {
) -> *mut dc_lot_t {
assert!(!context.is_null());
assert!(!qr.is_null());
let context = &*context;
dc_qr::dc_check_qr(context, qr)
let lot = qr::check_qr(context, as_str(qr));
Box::into_raw(Box::new(lot))
}
#[no_mangle]
@@ -989,7 +1034,7 @@ pub unsafe extern "C" fn dc_send_locations_to_chat(
assert!(!context.is_null());
let context = &*context;
dc_location::dc_send_locations_to_chat(context, chat_id, seconds)
dc_location::dc_send_locations_to_chat(context, chat_id, seconds as i64)
}
#[no_mangle]
@@ -1027,13 +1072,14 @@ pub unsafe extern "C" fn dc_get_locations(
assert!(!context.is_null());
let context = &*context;
dc_location::dc_get_locations(
let res = dc_location::dc_get_locations(
context,
chat_id,
contact_id,
timestamp_begin as i64,
timestamp_end as i64,
)
);
dc_array_t::from(res).into_raw()
}
#[no_mangle]
@@ -1056,24 +1102,12 @@ pub unsafe extern "C" fn dc_array_unref(a: *mut dc_array::dc_array_t) {
dc_array::dc_array_unref(a)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_add_uint(array: *mut dc_array_t, item: libc::uintptr_t) {
assert!(!array.is_null());
dc_array::dc_array_add_uint(array, item)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_add_id(array: *mut dc_array_t, item: libc::c_uint) {
assert!(!array.is_null());
dc_array::dc_array_add_id(array, item)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void) {
assert!(!array.is_null());
dc_array::dc_array_add_ptr(array, item)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_cnt(array: *const dc_array_t) -> libc::size_t {
@@ -1082,15 +1116,6 @@ pub unsafe extern "C" fn dc_array_get_cnt(array: *const dc_array_t) -> libc::siz
dc_array::dc_array_get_cnt(array)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_uint(
array: *const dc_array_t,
index: libc::size_t,
) -> libc::uintptr_t {
assert!(!array.is_null());
dc_array::dc_array_get_uint(array, index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_id(
array: *const dc_array_t,
index: libc::size_t,
@@ -1100,22 +1125,13 @@ pub unsafe extern "C" fn dc_array_get_id(
dc_array::dc_array_get_id(array, index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_ptr(
array: *const dc_array_t,
index: libc::size_t,
) -> *mut libc::c_void {
assert!(!array.is_null());
dc_array::dc_array_get_ptr(array, index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_latitude(
array: *const dc_array_t,
index: libc::size_t,
) -> libc::c_double {
assert!(!array.is_null());
dc_array::dc_array_get_latitude(array, index)
(*array).get_latitude(index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_longitude(
@@ -1124,7 +1140,7 @@ pub unsafe extern "C" fn dc_array_get_longitude(
) -> libc::c_double {
assert!(!array.is_null());
dc_array::dc_array_get_longitude(array, index)
(*array).get_longitude(index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_accuracy(
@@ -1133,7 +1149,7 @@ pub unsafe extern "C" fn dc_array_get_accuracy(
) -> libc::c_double {
assert!(!array.is_null());
dc_array::dc_array_get_accuracy(array, index)
(*array).get_accuracy(index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_timestamp(
@@ -1142,7 +1158,7 @@ pub unsafe extern "C" fn dc_array_get_timestamp(
) -> i64 {
assert!(!array.is_null());
dc_array::dc_array_get_timestamp(array, index)
(*array).get_timestamp(index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_chat_id(
@@ -1151,7 +1167,7 @@ pub unsafe extern "C" fn dc_array_get_chat_id(
) -> libc::c_uint {
assert!(!array.is_null());
dc_array::dc_array_get_chat_id(array, index)
(*array).get_chat_id(index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_contact_id(
@@ -1160,7 +1176,7 @@ pub unsafe extern "C" fn dc_array_get_contact_id(
) -> libc::c_uint {
assert!(!array.is_null());
dc_array::dc_array_get_contact_id(array, index)
(*array).get_contact_id(index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_msg_id(
@@ -1169,7 +1185,7 @@ pub unsafe extern "C" fn dc_array_get_msg_id(
) -> libc::c_uint {
assert!(!array.is_null());
dc_array::dc_array_get_msg_id(array, index)
(*array).get_msg_id(index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_marker(
@@ -1193,7 +1209,7 @@ pub unsafe extern "C" fn dc_array_search_id(
}
#[no_mangle]
pub unsafe extern "C" fn dc_array_get_raw(array: *const dc_array_t) -> *const libc::size_t {
pub unsafe extern "C" fn dc_array_get_raw(array: *const dc_array_t) -> *const u32 {
assert!(!array.is_null());
dc_array::dc_array_get_raw(array)
@@ -1256,11 +1272,14 @@ pub unsafe extern "C" fn dc_chatlist_get_summary<'a>(
chatlist: *mut dc_chatlist_t<'a>,
index: libc::size_t,
chat: *mut dc_chat_t<'a>,
) -> *mut dc_lot::dc_lot_t {
) -> *mut dc_lot_t {
assert!(!chatlist.is_null());
let chat = if chat.is_null() { None } else { Some(&*chat) };
let list = &*chatlist;
list.get_summary(index as usize, chat)
let lot = list.get_summary(index as usize, chat);
Box::into_raw(Box::new(lot))
}
#[no_mangle]
@@ -1276,90 +1295,104 @@ pub unsafe extern "C" fn dc_chatlist_get_context(
// dc_chat_t
#[no_mangle]
pub type dc_chat_t<'a> = dc_chat::Chat<'a>;
pub type dc_chat_t<'a> = chat::Chat<'a>;
#[no_mangle]
pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat_t) {
assert!(!chat.is_null());
dc_chat::dc_chat_unref(chat)
Box::from_raw(chat);
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 {
assert!(!chat.is_null());
let chat = &*chat;
dc_chat::dc_chat_get_id(chat)
chat.get_id()
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int {
assert!(!chat.is_null());
let chat = &*chat;
dc_chat::dc_chat_get_type(chat)
chat.get_type() as libc::c_int
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_char {
assert!(!chat.is_null());
let chat = &*chat;
dc_chat::dc_chat_get_name(chat)
chat.get_name().strdup()
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat_t) -> *mut libc::c_char {
assert!(!chat.is_null());
let chat = &*chat;
dc_chat::dc_chat_get_subtitle(chat)
chat.get_subtitle().strdup()
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut libc::c_char {
assert!(!chat.is_null());
let chat = &*chat;
dc_chat::dc_chat_get_profile_image(chat)
match chat.get_profile_image() {
Some(i) => i.strdup(),
None => ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 {
assert!(!chat.is_null());
let chat = &*chat;
dc_chat::dc_chat_get_color(chat)
chat.get_color()
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat_t) -> libc::c_int {
assert!(!chat.is_null());
let chat = &*chat;
dc_chat::dc_chat_get_archived(chat)
chat.is_archived() as libc::c_int
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int {
assert!(!chat.is_null());
let chat = &*chat;
dc_chat::dc_chat_is_unpromoted(chat)
chat.is_unpromoted() as libc::c_int
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_int {
assert!(!chat.is_null());
let chat = &*chat;
dc_chat::dc_chat_is_self_talk(chat)
chat.is_self_talk() as libc::c_int
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_int {
assert!(!chat.is_null());
let chat = &*chat;
dc_chat::dc_chat_is_verified(chat)
chat.is_verified() as libc::c_int
}
#[no_mangle]
pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int {
assert!(!chat.is_null());
let chat = &*chat;
dc_chat::dc_chat_is_sending_locations(chat)
chat.is_sending_locations() as libc::c_int
}
// dc_msg_t
@@ -1427,7 +1460,7 @@ pub unsafe extern "C" fn dc_msg_get_viewtype(msg: *mut dc_msg::dc_msg_t) -> libc
pub unsafe extern "C" fn dc_msg_get_state(msg: *mut dc_msg::dc_msg_t) -> libc::c_int {
assert!(!msg.is_null());
dc_msg::dc_msg_get_state(msg)
dc_msg::dc_msg_get_state(msg) as libc::c_int
}
#[no_mangle]
@@ -1518,10 +1551,12 @@ pub unsafe extern "C" fn dc_msg_get_showpadlock(msg: *mut dc_msg::dc_msg_t) -> l
pub unsafe extern "C" fn dc_msg_get_summary<'a>(
msg: *mut dc_msg::dc_msg_t<'a>,
chat: *mut dc_chat_t<'a>,
) -> *mut dc_lot::dc_lot_t {
) -> *mut dc_lot_t {
assert!(!msg.is_null());
let chat = if chat.is_null() { None } else { Some(&*chat) };
dc_msg::dc_msg_get_summary(msg, chat)
let lot = dc_msg::dc_msg_get_summary(msg, chat);
Box::into_raw(Box::new(lot))
}
#[no_mangle]
@@ -1531,7 +1566,7 @@ pub unsafe extern "C" fn dc_msg_get_summarytext(
) -> *mut libc::c_char {
assert!(!msg.is_null());
dc_msg::dc_msg_get_summarytext(msg, approx_characters)
dc_msg::dc_msg_get_summarytext(msg, approx_characters.try_into().unwrap())
}
#[no_mangle]
@@ -1764,67 +1799,61 @@ pub unsafe extern "C" fn dc_contact_is_verified(contact: *mut dc_contact_t) -> l
// dc_lot_t
#[no_mangle]
pub type dc_lot_t = dc_lot::dc_lot_t;
pub type dc_lot_t = lot::Lot;
#[no_mangle]
pub unsafe extern "C" fn dc_lot_new() -> *mut dc_lot::dc_lot_t {
dc_lot::dc_lot_new()
pub unsafe extern "C" fn dc_lot_unref(lot: *mut dc_lot_t) {
assert!(!lot.is_null());
Box::from_raw(lot);
}
#[no_mangle]
pub unsafe extern "C" fn dc_lot_empty(lot: *mut dc_lot::dc_lot_t) {
pub unsafe extern "C" fn dc_lot_get_text1(lot: *mut dc_lot_t) -> *mut libc::c_char {
assert!(!lot.is_null());
dc_lot::dc_lot_empty(lot)
let lot = &*lot;
strdup_opt(lot.get_text1())
}
#[no_mangle]
pub unsafe extern "C" fn dc_lot_unref(lot: *mut dc_lot::dc_lot_t) {
pub unsafe extern "C" fn dc_lot_get_text2(lot: *mut dc_lot_t) -> *mut libc::c_char {
assert!(!lot.is_null());
dc_lot::dc_lot_unref(lot)
let lot = &*lot;
strdup_opt(lot.get_text2())
}
#[no_mangle]
pub unsafe extern "C" fn dc_lot_get_text1(lot: *mut dc_lot::dc_lot_t) -> *mut libc::c_char {
pub unsafe extern "C" fn dc_lot_get_text1_meaning(lot: *mut dc_lot_t) -> libc::c_int {
assert!(!lot.is_null());
dc_lot::dc_lot_get_text1(lot)
let lot = &*lot;
lot.get_text1_meaning() as libc::c_int
}
#[no_mangle]
pub unsafe extern "C" fn dc_lot_get_text2(lot: *mut dc_lot::dc_lot_t) -> *mut libc::c_char {
pub unsafe extern "C" fn dc_lot_get_state(lot: *mut dc_lot_t) -> libc::c_int {
assert!(!lot.is_null());
dc_lot::dc_lot_get_text2(lot)
let lot = &*lot;
lot.get_state().to_i64().expect("impossible") as libc::c_int
}
#[no_mangle]
pub unsafe extern "C" fn dc_lot_get_text1_meaning(lot: *mut dc_lot::dc_lot_t) -> libc::c_int {
pub unsafe extern "C" fn dc_lot_get_id(lot: *mut dc_lot_t) -> u32 {
assert!(!lot.is_null());
dc_lot::dc_lot_get_text1_meaning(lot)
let lot = &*lot;
lot.get_id()
}
#[no_mangle]
pub unsafe extern "C" fn dc_lot_get_state(lot: *mut dc_lot::dc_lot_t) -> libc::c_int {
pub unsafe extern "C" fn dc_lot_get_timestamp(lot: *mut dc_lot_t) -> i64 {
assert!(!lot.is_null());
dc_lot::dc_lot_get_state(lot)
}
#[no_mangle]
pub unsafe extern "C" fn dc_lot_get_id(lot: *mut dc_lot::dc_lot_t) -> u32 {
assert!(!lot.is_null());
dc_lot::dc_lot_get_id(lot)
}
#[no_mangle]
pub unsafe extern "C" fn dc_lot_get_timestamp(lot: *mut dc_lot::dc_lot_t) -> i64 {
assert!(!lot.is_null());
dc_lot::dc_lot_get_timestamp(lot)
let lot = &*lot;
lot.get_timestamp()
}
#[no_mangle]
@@ -1839,3 +1868,33 @@ fn as_opt_str<'a>(s: *const libc::c_char) -> Option<&'a str> {
Some(dc_tools::as_str(s))
}
pub trait ResultExt<T: Default> {
fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T;
fn log_err(&self, context: &context::Context, message: &str);
}
impl<T: Default, E: std::fmt::Display> ResultExt<T> for Result<T, E> {
fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T {
match self {
Ok(t) => t,
Err(err) => {
error!(context, 0, "{}: {}", message, err);
Default::default()
}
}
}
fn log_err(&self, context: &context::Context, message: &str) {
if let Err(err) = self {
error!(context, 0, "{}: {}", message, err);
}
}
}
unsafe fn strdup_opt(s: Option<impl AsRef<str>>) -> *mut libc::c_char {
match s {
Some(s) => s.as_ref().strdup(),
None => ptr::null_mut(),
}
}

View File

@@ -0,0 +1,12 @@
[package]
name = "deltachat_derive"
version = "0.1.0"
authors = ["Dmitry Bogatov <KAction@debian.org>"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
syn = "0.14.4"
quote = "0.6.3"

View File

@@ -0,0 +1,44 @@
#![recursion_limit = "128"]
extern crate proc_macro;
use crate::proc_macro::TokenStream;
use quote::quote;
use syn;
// For now, assume (not check) that these macroses are applied to enum without
// data. If this assumption is violated, compiler error will point to
// generated code, which is not very user-friendly.
#[proc_macro_derive(ToSql)]
pub fn to_sql_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let gen = quote! {
impl rusqlite::types::ToSql for #name {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
let num = *self as i64;
let value = rusqlite::types::Value::Integer(num);
let output = rusqlite::types::ToSqlOutput::Owned(value);
std::result::Result::Ok(output)
}
}
};
gen.into()
}
#[proc_macro_derive(FromSql)]
pub fn from_sql_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let gen = quote! {
impl rusqlite::types::FromSql for #name {
fn column_result(col: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
let inner = rusqlite::types::FromSql::column_result(col)?;
num_traits::FromPrimitive::from_i64(inner).ok_or(rusqlite::types::FromSqlError::InvalidType)
}
}
};
gen.into()
}

View File

@@ -1,23 +1,22 @@
use std::ffi::CString;
use std::str::FromStr;
use deltachat::chat::{self, Chat};
use deltachat::chatlist::*;
use deltachat::config;
use deltachat::constants::*;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::dc_array::*;
use deltachat::dc_chat::*;
use deltachat::dc_configure::*;
use deltachat::dc_imex::*;
use deltachat::dc_job::*;
use deltachat::dc_location::*;
use deltachat::dc_lot::*;
use deltachat::dc_msg::*;
use deltachat::dc_qr::*;
use deltachat::dc_receive_imf::*;
use deltachat::dc_tools::*;
use deltachat::job::*;
use deltachat::lot::LotState;
use deltachat::peerstate::*;
use deltachat::qr::*;
use deltachat::sql;
use deltachat::types::*;
use deltachat::x::*;
@@ -125,7 +124,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
return 0;
}
let mut current_block: u64;
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 = 0 as *mut libc::c_char;
@@ -138,77 +137,71 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
.sql
.set_config(context, "import_spec", Some(as_str(real_spec)))
.unwrap();
current_block = 7149356873433890176;
ok_to_continue = true;
} else {
let rs = context.sql.get_config(context, "import_spec");
if rs.is_none() {
error!(context, 0, "Import: No file or folder given.");
current_block = 8522321847195001863;
ok_to_continue = false;
} else {
current_block = 7149356873433890176;
ok_to_continue = true;
}
real_spec = rs.unwrap_or_default().strdup();
}
match current_block {
8522321847195001863 => {}
_ => {
suffix = dc_get_filesuffix_lc(real_spec);
if !suffix.is_null()
&& strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0
{
if 0 != dc_poke_eml_file(context, real_spec) {
read_cnt += 1
}
current_block = 1622411330066726685;
if ok_to_continue {
let ok_to_continue2;
suffix = dc_get_filesuffix_lc(as_str(real_spec));
if !suffix.is_null() && strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0
{
if 0 != dc_poke_eml_file(context, real_spec) {
read_cnt += 1
}
ok_to_continue2 = true;
} else {
/* import a directory */
let dir_name = std::path::Path::new(as_str(real_spec));
let dir = std::fs::read_dir(dir_name);
if dir.is_err() {
error!(
context,
0,
"Import: Cannot open directory \"{}\".",
as_str(real_spec),
);
ok_to_continue2 = false;
} else {
/* import a directory */
let dir_name = std::path::Path::new(as_str(real_spec));
let dir = std::fs::read_dir(dir_name);
if dir.is_err() {
error!(
context,
0,
"Import: Cannot open directory \"{}\".",
as_str(real_spec),
);
current_block = 8522321847195001863;
} else {
let dir = dir.unwrap();
for entry in dir {
if entry.is_err() {
break;
}
let entry = entry.unwrap();
let name_f = entry.file_name();
let name = name_f.to_string_lossy();
if name.ends_with(".eml") {
let path_plus_name = format!("{}/{}", as_str(real_spec), name);
info!(context, 0, "Import: {}", path_plus_name);
let path_plus_name_c = CString::yolo(path_plus_name);
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
read_cnt += 1
}
let dir = dir.unwrap();
for entry in dir {
if entry.is_err() {
break;
}
let entry = entry.unwrap();
let name_f = entry.file_name();
let name = name_f.to_string_lossy();
if name.ends_with(".eml") {
let path_plus_name = format!("{}/{}", as_str(real_spec), name);
info!(context, 0, "Import: {}", path_plus_name);
let path_plus_name_c = CString::yolo(path_plus_name);
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
read_cnt += 1
}
}
current_block = 1622411330066726685;
}
ok_to_continue2 = true;
}
match current_block {
8522321847195001863 => {}
_ => {
info!(
context,
0,
"Import: {} items read from \"{}\".",
read_cnt,
as_str(real_spec)
);
if read_cnt > 0 {
context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t);
}
success = 1
}
}
if ok_to_continue2 {
info!(
context,
0,
"Import: {} items read from \"{}\".",
read_cnt,
as_str(real_spec)
);
if read_cnt > 0 {
context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t);
}
success = 1
}
}
@@ -223,10 +216,10 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
let contact_id = contact.get_id();
let statestr = match dc_msg_get_state(msg) {
DC_STATE_OUT_PENDING => " o",
DC_STATE_OUT_DELIVERED => "",
DC_STATE_OUT_MDN_RCVD => " √√",
DC_STATE_OUT_FAILED => " !!",
MessageState::OutPending => " o",
MessageState::OutDelivered => "",
MessageState::OutMdnRcvd => " √√",
MessageState::OutFailed => " !!",
_ => "",
};
let temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
@@ -249,9 +242,9 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
if dc_msg_is_starred(msg) { "" } else { "" },
if dc_msg_get_from_id(msg) == 1 as libc::c_uint {
""
} else if dc_msg_get_state(msg) == DC_STATE_IN_SEEN {
} else if dc_msg_get_state(msg) == MessageState::InSeen {
"[SEEN]"
} else if dc_msg_get_state(msg) == DC_STATE_IN_NOTICED {
} else if dc_msg_get_state(msg) == MessageState::InNoticed {
"[NOTICED]"
} else {
"[FRESH]"
@@ -267,11 +260,9 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
free(msgtext as *mut libc::c_void);
}
unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
let cnt = dc_array_get_cnt(msglist) as usize;
unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) {
let mut lines_out = 0;
for i in 0..cnt {
let msg_id = dc_array_get_id(msglist, i as size_t);
for &msg_id in msglist {
if msg_id == 9 as libc::c_uint {
info!(
context,
@@ -280,7 +271,7 @@ unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
);
lines_out += 1
} else if msg_id > 0 as libc::c_uint {
} else if msg_id > 0 {
if lines_out == 0 {
info!(
context, 0,
@@ -301,13 +292,12 @@ unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
}
}
unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) {
if !dc_array_search_id(contacts, 1 as uint32_t, 0 as *mut size_t) {
dc_array_add_id(contacts, 1 as uint32_t);
unsafe fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
let mut contacts = contacts.clone();
if !contacts.contains(&1) {
contacts.push(1);
}
let cnt = dc_array_get_cnt(contacts);
for i in 0..cnt {
let contact_id = dc_array_get_id(contacts, i as size_t);
for contact_id in contacts {
let line;
let mut line2 = "".to_string();
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
@@ -356,22 +346,16 @@ pub unsafe fn dc_cmdline_skip_auth() {
S_IS_AUTH = 1;
}
unsafe fn chat_prefix(chat: *const Chat) -> &'static str {
if (*chat).type_0 == 120 {
"Group"
} else if (*chat).type_0 == 130 {
"VerifiedGroup"
} else {
"Single"
}
fn chat_prefix(chat: &Chat) -> &'static str {
chat.typ.into()
}
pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
let chat_id = *context.cmdline_sel_chat_id.read().unwrap();
let mut sel_chat = if chat_id > 0 {
dc_get_chat(context, chat_id)
Chat::load_from_db(context, chat_id).ok()
} else {
std::ptr::null_mut()
None
};
let mut args = line.splitn(3, ' ');
@@ -471,7 +455,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
event <event-id to test>\n\
fileinfo <file>\n\
clear -- clear screen\n\
exit\n\
exit or quit\n\
============================================="
),
},
@@ -597,16 +581,20 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
println!("{}", to_string(dc_get_info(context)));
}
"maybenetwork" => {
dc_maybe_network(context);
maybe_network(context);
}
"housekeeping" => {
sql::housekeeping(context);
}
"listchats" | "listarchived" | "chats" => {
let listflags = if arg0 == "listarchived" { 0x01 } else { 0 };
let chatlist = Chatlist::try_load(context, listflags, Some(arg1), None)?;
let chatlist = Chatlist::try_load(
context,
listflags,
if arg1.is_empty() { None } else { Some(arg1) },
None,
)?;
let mut i: usize;
let cnt = chatlist.len();
if cnt > 0 {
info!(
@@ -614,64 +602,54 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"================================================================================"
);
i = cnt - 1;
while i > 0 {
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
let temp_subtitle = dc_chat_get_subtitle(chat);
let temp_name = dc_chat_get_name(chat);
for i in (0..cnt).rev() {
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
let temp_subtitle = chat.get_subtitle();
let temp_name = chat.get_name();
info!(
context,
0,
"{}#{}: {} [{}] [{} fresh]",
chat_prefix(chat),
dc_chat_get_id(chat) as libc::c_int,
as_str(temp_name),
as_str(temp_subtitle),
dc_get_fresh_msg_cnt(context, dc_chat_get_id(chat)) as libc::c_int,
chat_prefix(&chat),
chat.get_id(),
temp_name,
temp_subtitle,
chat::get_fresh_msg_cnt(context, chat.get_id()),
);
free(temp_subtitle as *mut libc::c_void);
free(temp_name as *mut libc::c_void);
let lot = chatlist.get_summary(i, chat);
let statestr = if 0 != dc_chat_get_archived(chat) {
let lot = chatlist.get_summary(i, Some(&chat));
let statestr = if chat.is_archived() {
" [Archived]"
} else {
match dc_lot_get_state(lot) {
20 => " o",
26 => "",
28 => " √√",
24 => " !!",
match lot.get_state() {
LotState::MsgOutPending => " o",
LotState::MsgOutDelivered => "",
LotState::MsgOutMdnRcvd => " √√",
LotState::MsgOutFailed => " !!",
_ => "",
}
};
let timestr = dc_timestamp_to_str(dc_lot_get_timestamp(lot));
let text1 = dc_lot_get_text1(lot);
let text2 = dc_lot_get_text2(lot);
let timestr = dc_timestamp_to_str(lot.get_timestamp());
let text1 = lot.get_text1();
let text2 = lot.get_text2();
info!(
context,
0,
"{}{}{}{} [{}]{}",
to_string(text1),
if !text1.is_null() { ": " } else { "" },
to_string(text2),
text1.unwrap_or(""),
if text1.is_some() { ": " } else { "" },
text2.unwrap_or(""),
statestr,
&timestr,
if 0 != dc_chat_is_sending_locations(chat) {
if chat.is_sending_locations() {
"📍"
} else {
""
},
);
free(text1 as *mut libc::c_void);
free(text2 as *mut libc::c_void);
dc_lot_unref(lot);
dc_chat_unref(chat);
info!(
context, 0,
"================================================================================"
);
i -= 1
}
}
if dc_is_sending_locations_to_chat(context, 0 as uint32_t) {
@@ -680,109 +658,83 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
println!("{} chats", cnt);
}
"chat" => {
if sel_chat.is_null() && arg1.is_empty() {
if sel_chat.is_none() && arg1.is_empty() {
bail!("Argument [chat-id] is missing.");
}
if !sel_chat.is_null() && !arg1.is_empty() {
dc_chat_unref(sel_chat);
}
if !arg1.is_empty() {
let chat_id = arg1.parse()?;
println!("Selecting chat #{}", chat_id);
sel_chat = dc_get_chat(context, chat_id);
sel_chat = Some(Chat::load_from_db(context, chat_id)?);
*context.cmdline_sel_chat_id.write().unwrap() = chat_id;
}
ensure!(!sel_chat.is_null(), "Failed to select chat");
ensure!(sel_chat.is_some(), "Failed to select chat");
let sel_chat = sel_chat.as_ref().unwrap();
let msglist = dc_get_chat_msgs(context, dc_chat_get_id(sel_chat), 0x1, 0);
let temp2 = dc_chat_get_subtitle(sel_chat);
let temp_name = dc_chat_get_name(sel_chat);
let msglist = chat::get_chat_msgs(context, sel_chat.get_id(), 0x1, 0);
let temp2 = sel_chat.get_subtitle();
let temp_name = sel_chat.get_name();
info!(
context,
0,
"{}#{}: {} [{}]{}",
chat_prefix(sel_chat),
dc_chat_get_id(sel_chat),
as_str(temp_name),
as_str(temp2),
if 0 != dc_chat_is_sending_locations(sel_chat) {
sel_chat.get_id(),
temp_name,
temp2,
if sel_chat.is_sending_locations() {
"📍"
} else {
""
},
);
free(temp_name as *mut libc::c_void);
free(temp2 as *mut libc::c_void);
if !msglist.is_null() {
log_msglist(context, msglist);
dc_array_unref(msglist);
}
let draft = dc_get_draft(context, dc_chat_get_id(sel_chat));
log_msglist(context, &msglist);
let draft = chat::get_draft(context, sel_chat.get_id());
if !draft.is_null() {
log_msg(context, "Draft", draft);
dc_msg_unref(draft);
}
println!(
"{} messages.",
dc_get_msg_cnt(context, dc_chat_get_id(sel_chat))
chat::get_msg_cnt(context, sel_chat.get_id())
);
dc_marknoticed_chat(context, dc_chat_get_id(sel_chat));
chat::marknoticed_chat(context, sel_chat.get_id())?;
}
"createchat" => {
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id: libc::c_int = arg1.parse()?;
let chat_id: libc::c_int =
dc_create_chat_by_contact_id(context, contact_id as uint32_t) as libc::c_int;
if chat_id != 0 {
println!("Single#{} created successfully.", chat_id,);
} else {
bail!("Failed to create chat");
}
let chat_id = chat::create_by_contact_id(context, contact_id as uint32_t)?;
println!("Single#{} created successfully.", chat_id,);
}
"createchatbymsg" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing");
let msg_id_0: libc::c_int = arg1.parse()?;
let chat_id_0: libc::c_int =
dc_create_chat_by_msg_id(context, msg_id_0 as uint32_t) as libc::c_int;
if chat_id_0 != 0 {
let chat_0: *mut Chat = dc_get_chat(context, chat_id_0 as uint32_t);
println!(
"{}#{} created successfully.",
chat_prefix(chat_0),
chat_id_0,
);
dc_chat_unref(chat_0);
} else {
bail!("");
}
let msg_id: u32 = arg1.parse()?;
let chat_id = chat::create_by_msg_id(context, msg_id)?;
let chat = Chat::load_from_db(context, chat_id)?;
println!("{}#{} created successfully.", chat_prefix(&chat), chat_id,);
}
"creategroup" => {
ensure!(!arg1.is_empty(), "Argument <name> missing.");
let chat_id_1: libc::c_int = dc_create_group_chat(context, 0, arg1_c) as libc::c_int;
if chat_id_1 != 0 {
println!("Group#{} created successfully.", chat_id_1,);
} else {
bail!("Failed to create group");
}
let chat_id = chat::create_group_chat(context, VerifiedStatus::Unverified, arg1)?;
println!("Group#{} created successfully.", chat_id);
}
"createverified" => {
ensure!(!arg1.is_empty(), "Argument <name> missing.");
let chat_id_2: libc::c_int = dc_create_group_chat(context, 1, arg1_c) as libc::c_int;
if chat_id_2 != 0 {
println!("VerifiedGroup#{} created successfully.", chat_id_2,);
} else {
bail!("Failed to create verified group");
}
let chat_id = chat::create_group_chat(context, VerifiedStatus::Verified, arg1)?;
println!("VerifiedGroup#{} created successfully.", chat_id);
}
"addmember" => {
ensure!(!sel_chat.is_null(), "No chat selected");
ensure!(sel_chat.is_some(), "No chat selected");
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id_0: libc::c_int = arg1.parse()?;
if 0 != dc_add_contact_to_chat(
if 0 != chat::add_contact_to_chat(
context,
dc_chat_get_id(sel_chat),
sel_chat.as_ref().unwrap().get_id(),
contact_id_0 as uint32_t,
) {
println!("Contact added to chat.");
@@ -791,101 +743,89 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
}
"removemember" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id_1: libc::c_int = arg1.parse()?;
if 0 != dc_remove_contact_from_chat(
chat::remove_contact_from_chat(
context,
dc_chat_get_id(sel_chat),
sel_chat.as_ref().unwrap().get_id(),
contact_id_1 as uint32_t,
) {
println!("Contact added to chat.");
} else {
bail!("Cannot remove member from chat.");
}
)?;
println!("Contact added to chat.");
}
"groupname" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!arg1.is_empty(), "Argument <name> missing.");
if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c) {
println!("Chat name set");
} else {
bail!("Failed to set chat name");
}
chat::set_chat_name(context, sel_chat.as_ref().unwrap().get_id(), arg1)?;
println!("Chat name set");
}
"groupimage" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!arg1.is_empty(), "Argument <image> missing.");
if 0 != dc_set_chat_profile_image(
context,
dc_chat_get_id(sel_chat),
if !arg1.is_empty() {
arg1_c
} else {
std::ptr::null_mut()
},
) {
println!("Chat image set");
} else {
bail!("Failed to set chat image");
}
chat::set_chat_profile_image(context, sel_chat.as_ref().unwrap().get_id(), arg1)?;
println!("Chat image set");
}
"chatinfo" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(sel_chat.is_some(), "No chat selected.");
let contacts = dc_get_chat_contacts(context, dc_chat_get_id(sel_chat));
ensure!(!contacts.is_null(), "Failed to retreive contacts");
let contacts = chat::get_chat_contacts(context, sel_chat.as_ref().unwrap().get_id());
info!(context, 0, "Memberlist:");
log_contactlist(context, contacts);
log_contactlist(context, &contacts);
println!(
"{} contacts\nLocation streaming: {}",
dc_array_get_cnt(contacts),
dc_is_sending_locations_to_chat(context, dc_chat_get_id(sel_chat)),
contacts.len(),
dc_is_sending_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id()),
);
dc_array_unref(contacts);
}
"getlocations" => {
ensure!(sel_chat.is_some(), "No chat selected.");
let contact_id = arg1.parse().unwrap_or_default();
let loc = dc_get_locations(context, dc_chat_get_id(sel_chat), contact_id, 0, 0);
let mut j = 0;
while j < dc_array_get_cnt(loc) {
let timestr_0 = dc_timestamp_to_str(dc_array_get_timestamp(loc, j as size_t));
let marker = dc_array_get_marker(loc, j as size_t);
let locations = dc_get_locations(
context,
sel_chat.as_ref().unwrap().get_id(),
contact_id,
0,
0,
);
let default_marker = "-".to_string();
for location in &locations {
let marker = location.marker.as_ref().unwrap_or(&default_marker);
info!(
context,
0,
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}",
dc_array_get_id(loc, j as size_t),
&timestr_0,
dc_array_get_latitude(loc, j as size_t),
dc_array_get_longitude(loc, j as size_t),
dc_array_get_accuracy(loc, j as size_t),
dc_array_get_chat_id(loc, j as size_t),
dc_array_get_contact_id(loc, j as size_t),
dc_array_get_msg_id(loc, j as size_t),
if !marker.is_null() {
as_str(marker)
} else {
"-"
},
location.location_id,
dc_timestamp_to_str(location.timestamp),
location.latitude,
location.longitude,
location.accuracy,
location.chat_id,
location.contact_id,
location.msg_id,
marker
);
free(marker as *mut libc::c_void);
j += 1
}
if dc_array_get_cnt(loc) == 0 {
if locations.is_empty() {
info!(context, 0, "No locations.");
}
dc_array_unref(loc);
}
"sendlocations" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!arg1.is_empty(), "No timeout given.");
let seconds = arg1.parse()?;
dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds);
println!("Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.", dc_chat_get_id(sel_chat), seconds);
dc_send_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id(), seconds);
println!(
"Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.",
sel_chat.as_ref().unwrap().get_id(),
seconds
);
}
"setlocation" => {
ensure!(
@@ -906,27 +846,19 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
dc_delete_all_locations(context);
}
"send" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!arg1.is_empty(), "No message text given.");
let msg = format!("{} {}", arg1, arg2);
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg) {
println!("Message sent.");
} else {
bail!("Sending failed.");
}
chat::send_text_msg(context, sel_chat.as_ref().unwrap().get_id(), msg)?;
}
"sendempty" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), "".into()) {
println!("Message sent.");
} else {
bail!("Sending failed.");
}
ensure!(sel_chat.is_some(), "No chat selected.");
chat::send_text_msg(context, sel_chat.as_ref().unwrap().get_id(), "".into())?;
}
"sendimage" | "sendfile" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given.");
let msg_0 = dc_msg_new(
@@ -939,54 +871,53 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
);
dc_msg_set_file(msg_0, arg1_c, 0 as *const libc::c_char);
dc_msg_set_text(msg_0, arg2_c);
dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0);
chat::send_msg(context, sel_chat.as_ref().unwrap().get_id(), msg_0)?;
dc_msg_unref(msg_0);
}
"listmsgs" => {
ensure!(!arg1.is_empty(), "Argument <query> missing.");
let chat = if !sel_chat.is_null() {
dc_chat_get_id(sel_chat)
let chat = if let Some(ref sel_chat) = sel_chat {
sel_chat.get_id()
} else {
0 as libc::c_uint
};
let msglist_0 = dc_search_msgs(context, chat, arg1_c);
let msglist = dc_search_msgs(context, chat, arg1_c);
if !msglist_0.is_null() {
log_msglist(context, msglist_0);
println!("{} messages.", dc_array_get_cnt(msglist_0));
dc_array_unref(msglist_0);
}
log_msglist(context, &msglist);
println!("{} messages.", msglist.len());
}
"draft" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(sel_chat.is_some(), "No chat selected.");
if !arg1.is_empty() {
let draft_0 = dc_msg_new(context, Viewtype::Text);
dc_msg_set_text(draft_0, arg1_c);
dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0);
chat::set_draft(context, sel_chat.as_ref().unwrap().get_id(), draft_0);
dc_msg_unref(draft_0);
println!("Draft saved.");
} else {
dc_set_draft(context, dc_chat_get_id(sel_chat), 0 as *mut dc_msg_t);
chat::set_draft(
context,
sel_chat.as_ref().unwrap().get_id(),
0 as *mut dc_msg_t,
);
println!("Draft deleted.");
}
}
"listmedia" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(sel_chat.is_some(), "No chat selected.");
let images = dc_get_chat_media(
let images = chat::get_chat_media(
context,
dc_chat_get_id(sel_chat),
sel_chat.as_ref().unwrap().get_id(),
Viewtype::Image,
Viewtype::Gif,
Viewtype::Video,
);
let icnt: libc::c_int = dc_array_get_cnt(images) as libc::c_int;
println!("{} images or videos: ", icnt);
for i in 0..icnt {
let data = dc_array_get_id(images, i as size_t);
println!("{} images or videos: ", images.len());
for (i, data) in images.iter().enumerate() {
if 0 == i {
print!("Msg#{}", data);
} else {
@@ -994,17 +925,20 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
}
print!("\n");
dc_array_unref(images);
}
"archive" | "unarchive" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
let chat_id = arg1.parse()?;
dc_archive_chat(context, chat_id, if arg0 == "archive" { 1 } else { 0 });
chat::archive(
context,
chat_id,
if arg0 == "archive" { true } else { false },
)?;
}
"delchat" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
let chat_id = arg1.parse()?;
dc_delete_chat(context, chat_id);
chat::delete(context, chat_id)?;
}
"msginfo" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
@@ -1014,11 +948,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"listfresh" => {
let msglist = dc_get_fresh_msgs(context);
ensure!(!msglist.is_null(), "Failed to retrieve messages");
log_msglist(context, msglist);
print!("{} fresh messages.", dc_array_get_cnt(msglist));
dc_array_unref(msglist);
log_msglist(context, &msglist);
print!("{} fresh messages.", msglist.len());
}
"forward" => {
ensure!(
@@ -1029,7 +961,7 @@ 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()?;
dc_forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
chat::forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
}
"markseen" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
@@ -1064,13 +996,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
},
Some(arg1),
)?;
if !contacts.is_null() {
log_contactlist(context, contacts);
println!("{} contacts.", dc_array_get_cnt(contacts) as libc::c_int,);
dc_array_unref(contacts);
} else {
bail!("");
}
log_contactlist(context, &contacts);
println!("{} contacts.", contacts.len());
}
"addcontact" => {
ensure!(!arg1.is_empty(), "Arguments [<name>] <addr> expected.");
@@ -1091,7 +1018,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let mut res = format!("Contact info for: {}:\n\n", name_n_addr);
res += &Contact::get_encrinfo(context, contact_id);
res += &Contact::get_encrinfo(context, contact_id)?;
let chatlist = Chatlist::try_load(context, 0, None, Some(contact_id))?;
let chatlist_cnt = chatlist.len();
@@ -1104,9 +1031,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
if 0 != i {
res += ", ";
}
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
res += &format!("{}#{}", chat_prefix(chat), dc_chat_get_id(chat));
dc_chat_unref(chat);
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
res += &format!("{}#{}", chat_prefix(&chat), chat.get_id());
}
}
@@ -1118,15 +1044,14 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"checkqr" => {
ensure!(!arg1.is_empty(), "Argument <qr-content> missing.");
let res = dc_check_qr(context, arg1_c);
let res = check_qr(context, arg1);
println!(
"state={}, id={}, text1={}, text2={}",
(*res).state as libc::c_int,
(*res).id,
to_string((*res).text1),
to_string((*res).text2)
"state={}, id={}, text1={:?}, text2={:?}",
res.get_state(),
res.get_id(),
res.get_text1(),
res.get_text2()
);
dc_lot_unref(res);
}
"event" => {
ensure!(!arg1.is_empty(), "Argument <id> missing.");
@@ -1140,20 +1065,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"fileinfo" => {
ensure!(!arg1.is_empty(), "Argument <file> missing.");
let mut buf = 0 as *mut libc::c_uchar;
let mut buf_bytes = 0;
let mut w = 0;
let mut h = 0;
if 0 != dc_read_file(
context,
arg1_c,
&mut buf as *mut *mut libc::c_uchar as *mut *mut libc::c_void,
&mut buf_bytes,
) {
dc_get_filemeta(buf as *const libc::c_void, buf_bytes, &mut w, &mut h);
println!("width={}, height={}", w, h,);
free(buf as *mut libc::c_void);
if let Some(buf) = dc_read_file_safe(context, &arg1) {
let (width, height) = dc_get_filemeta(&buf)?;
println!("width={}, height={}", width, height);
} else {
bail!("Command failed.");
}
@@ -1162,10 +1077,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
}
if !sel_chat.is_null() {
dc_chat_unref(sel_chat);
}
free(arg1_c as *mut _);
free(arg2_c as *mut _);

View File

@@ -3,7 +3,6 @@
//!
//! Usage: cargo run --example repl --release -- <databasefile>
//! All further options can be set using the set-command (type ? for help).
#![feature(ptr_cast)]
#[macro_use]
extern crate deltachat;
@@ -22,9 +21,9 @@ use deltachat::config;
use deltachat::constants::*;
use deltachat::context::*;
use deltachat::dc_configure::*;
use deltachat::dc_job::*;
use deltachat::dc_securejoin::*;
use deltachat::dc_tools::*;
use deltachat::job::*;
use deltachat::oauth2::*;
use deltachat::types::*;
use deltachat::x::*;
@@ -173,13 +172,11 @@ fn start_threads(c: Arc<RwLock<Context>>) {
let ctx = c.clone();
let handle_imap = std::thread::spawn(move || loop {
while_running!({
unsafe {
dc_perform_imap_jobs(&ctx.read().unwrap());
dc_perform_imap_fetch(&ctx.read().unwrap());
}
perform_imap_jobs(&ctx.read().unwrap());
perform_imap_fetch(&ctx.read().unwrap());
while_running!({
let context = ctx.read().unwrap();
dc_perform_imap_idle(&context);
perform_imap_idle(&context);
});
});
});
@@ -187,9 +184,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
let ctx = c.clone();
let handle_mvbox = std::thread::spawn(move || loop {
while_running!({
unsafe { dc_perform_mvbox_fetch(&ctx.read().unwrap()) };
perform_mvbox_fetch(&ctx.read().unwrap());
while_running!({
unsafe { dc_perform_mvbox_idle(&ctx.read().unwrap()) };
perform_mvbox_idle(&ctx.read().unwrap());
});
});
});
@@ -197,9 +194,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
let ctx = c.clone();
let handle_sentbox = std::thread::spawn(move || loop {
while_running!({
unsafe { dc_perform_sentbox_fetch(&ctx.read().unwrap()) };
perform_sentbox_fetch(&ctx.read().unwrap());
while_running!({
unsafe { dc_perform_sentbox_idle(&ctx.read().unwrap()) };
perform_sentbox_idle(&ctx.read().unwrap());
});
});
});
@@ -207,9 +204,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
let ctx = c;
let handle_smtp = std::thread::spawn(move || loop {
while_running!({
unsafe { dc_perform_smtp_jobs(&ctx.read().unwrap()) };
perform_smtp_jobs(&ctx.read().unwrap());
while_running!({
unsafe { dc_perform_smtp_idle(&ctx.read().unwrap()) };
perform_smtp_idle(&ctx.read().unwrap());
});
});
});
@@ -227,12 +224,10 @@ fn stop_threads(context: &Context) {
println!("Stopping threads");
IS_RUNNING.store(false, Ordering::Relaxed);
unsafe {
dc_interrupt_imap_idle(context);
dc_interrupt_mvbox_idle(context);
dc_interrupt_sentbox_idle(context);
dc_interrupt_smtp_idle(context);
}
interrupt_imap_idle(context);
interrupt_mvbox_idle(context);
interrupt_sentbox_idle(context);
interrupt_smtp_idle(context);
handle.handle_imap.take().unwrap().join().unwrap();
handle.handle_mvbox.take().unwrap().join().unwrap();
@@ -335,8 +330,8 @@ const CONTACT_COMMANDS: [&'static str; 6] = [
"delcontact",
"cleanupcontacts",
];
const MISC_COMMANDS: [&'static str; 8] = [
"getqr", "getbadqr", "checkqr", "event", "fileinfo", "clear", "exit", "help",
const MISC_COMMANDS: [&'static str; 9] = [
"getqr", "getbadqr", "checkqr", "event", "fileinfo", "clear", "exit", "quit", "help",
];
impl Hinter for DcHelper {
@@ -456,12 +451,6 @@ fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
println!("history saved");
{
stop_threads(&ctx.read().unwrap());
unsafe {
let mut ctx = ctx.write().unwrap();
dc_close(&mut ctx);
dc_context_unref(&mut ctx);
}
}
Ok(())
@@ -494,14 +483,14 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
if HANDLE.clone().lock().unwrap().is_some() {
println!("smtp-jobs are already running in a thread.",);
} else {
dc_perform_smtp_jobs(&ctx.read().unwrap());
perform_smtp_jobs(&ctx.read().unwrap());
}
}
"imap-jobs" => {
if HANDLE.clone().lock().unwrap().is_some() {
println!("imap-jobs are already running in a thread.");
} else {
dc_perform_imap_jobs(&ctx.read().unwrap());
perform_imap_jobs(&ctx.read().unwrap());
}
}
"configure" => {
@@ -556,7 +545,7 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
dc_join_securejoin(&ctx.read().unwrap(), arg1_c);
}
}
"exit" => return Ok(ExitResult::Exit),
"exit" | "quit" => return Ok(ExitResult::Exit),
_ => dc_cmdline(&ctx.read().unwrap(), line)?,
}

View File

@@ -5,18 +5,16 @@ use std::sync::{Arc, RwLock};
use std::{thread, time};
use tempfile::tempdir;
use deltachat::chat;
use deltachat::chatlist::*;
use deltachat::config;
use deltachat::constants::Event;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::dc_chat::*;
use deltachat::dc_configure::*;
use deltachat::dc_job::{
dc_perform_imap_fetch, dc_perform_imap_idle, dc_perform_imap_jobs, dc_perform_smtp_idle,
dc_perform_smtp_jobs,
use deltachat::job::{
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
};
use deltachat::dc_lot::*;
extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize {
println!("[{:?}]", event);
@@ -53,12 +51,12 @@ fn main() {
let r1 = running.clone();
let t1 = thread::spawn(move || {
while *r1.read().unwrap() {
dc_perform_imap_jobs(&ctx1);
perform_imap_jobs(&ctx1);
if *r1.read().unwrap() {
dc_perform_imap_fetch(&ctx1);
perform_imap_fetch(&ctx1);
if *r1.read().unwrap() {
dc_perform_imap_idle(&ctx1);
perform_imap_idle(&ctx1);
}
}
}
@@ -68,9 +66,9 @@ fn main() {
let r1 = running.clone();
let t2 = thread::spawn(move || {
while *r1.read().unwrap() {
dc_perform_smtp_jobs(&ctx1);
perform_smtp_jobs(&ctx1);
if *r1.read().unwrap() {
dc_perform_smtp_idle(&ctx1);
perform_smtp_idle(&ctx1);
}
}
});
@@ -96,29 +94,17 @@ fn main() {
println!("sending a message");
let contact_id =
Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap();
let chat_id = dc_create_chat_by_contact_id(&ctx, contact_id);
dc_send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into());
let chat_id = chat::create_by_contact_id(&ctx, contact_id).unwrap();
chat::send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()).unwrap();
println!("fetching chats..");
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
for i in 0..chats.len() {
let summary = chats.get_summary(0, std::ptr::null_mut());
let text1 = dc_lot_get_text1(summary);
let text2 = dc_lot_get_text2(summary);
let text1_s = if !text1.is_null() {
Some(CStr::from_ptr(text1))
} else {
None
};
let text2_s = if !text2.is_null() {
Some(CStr::from_ptr(text2))
} else {
None
};
println!("chat: {} - {:?} - {:?}", i, text1_s, text2_s,);
dc_lot_unref(summary);
let summary = chats.get_summary(0, None);
let text1 = summary.get_text1();
let text2 = summary.get_text2();
println!("chat: {} - {:?} - {:?}", i, text1, text2,);
}
thread::sleep(duration);
@@ -136,8 +122,8 @@ fn main() {
println!("stopping threads");
*running.clone().write().unwrap() = false;
deltachat::dc_job::dc_interrupt_imap_idle(&ctx);
deltachat::dc_job::dc_interrupt_smtp_idle(&ctx);
deltachat::job::interrupt_imap_idle(&ctx);
deltachat::job::interrupt_smtp_idle(&ctx);
println!("joining");
t1.join().unwrap();

View File

@@ -15,7 +15,7 @@ without any "build-from-source" steps.
1. `Install virtualenv <https://virtualenv.pypa.io/en/stable/installation/>`_,
then create a fresh python environment and activate it in your shell::
virtualenv -p python3 venv
virtualenv venv # or: python -m venv
source venv/bin/activate
Afterwards, invoking ``python`` or ``pip install`` will only
@@ -39,6 +39,12 @@ and push them to a python package index. To install the latest github ``master``
pip install -i https://m.devpi.net/dc/master deltachat
.. note::
If you can help to automate the building of wheels for Mac or Windows,
that'd be much appreciated! please then get
`in contact with us <https://delta.chat/en/contribute>`_.
Installing bindings from source
===============================
@@ -48,34 +54,55 @@ to core deltachat library::
git clone https://github.com/deltachat/deltachat-core-rust
cd deltachat-core-rust
cargo build -p deltachat_ffi --release
This will result in a ``libdeltachat.so`` and ``libdeltachat.a`` files
in the ``target/release`` directory. These files are needed for
creating the python bindings for deltachat::
cd python
DCC_RS_DEV=`pwd`/.. pip install -e .
Now test if the bindings find the correct library::
If you don't have one active, create and activate a python "virtualenv":
python -c 'import deltachat ; print(deltachat.__version__)'
python virtualenv venv # or python -m venv
source venv/bin/activate
This should print your deltachat bindings version.
Afterwards ``which python`` tells you that it comes out of the "venv"
directory that contains all python install artifacts. Let's first
install test tools::
pip install pytest pytest-timeout requests
then cargo-build and install the deltachat bindings::
python install_python_bindings.py
The bindings will be installed in release mode but with debug symbols.
The release mode is neccessary because some tests generate RSA keys
which is prohibitively slow in debug mode.
After succcessul binding installation you can finally run the tests::
pytest -v tests
.. note::
If you can help to automate the building of wheels for Mac or Windows,
that'd be much appreciated! please then get
`in contact with us <https://delta.chat/en/contribute>`_.
Some tests are sometimes failing/hanging because of
https://github.com/deltachat/deltachat-core-rust/issues/331
and
https://github.com/deltachat/deltachat-core-rust/issues/326
Using a system-installed deltachat-core-rust
--------------------------------------------
When calling ``pip`` without specifying the ``DCC_RS_DEV`` environment
variable cffi will try to use a ``deltachat.h`` from a system location
like ``/usr/local/include`` and will try to dynamically link against a
``libdeltachat.so`` in a similar location (e.g. ``/usr/local/lib``).
running "live" tests (experimental)
-----------------------------------
If you want to run "liveconfig" functional tests you can set
``DCC_PY_LIVECONFIG`` to:
- a particular https-url that you can ask for from the delta
chat devs.
- or the path of a file that contains two lines, each describing
via "addr=... mail_pwd=..." a test account login that will
be used for the live tests.
With ``DCC_PY_LIVECONFIG`` set pytest invocations will use real
e-mail accounts and run through all functional "liveconfig" tests.
Code examples
@@ -84,68 +111,34 @@ Code examples
You may look at `examples <https://py.delta.chat/examples.html>`_.
Running tests
=============
Get a checkout of the `deltachat-core-rust github repository`_ and type::
pip install tox
./run-integration-tests.sh
If you want to run functional tests with real
e-mail test accounts, generate a "liveconfig" file where each
lines contains test account settings, for example::
# 'liveconfig' file specifying imap/smtp accounts
addr=some-email@example.org mail_pw=password
addr=other-email@example.org mail_pw=otherpassword
The "keyword=value" style allows to specify any
`deltachat account config setting <https://c.delta.chat/classdc__context__t.html#aff3b894f6cfca46cab5248fdffdf083d>`_ so you can also specify smtp or imap servers, ports, ssl modes etc.
Typically DC's automatic configuration allows to not specify these settings.
The ``run-integration-tests.sh`` script will automatically use
``python/liveconfig`` if it exists, to manually run tests with this
``liveconfig`` file use::
tox -- --liveconfig liveconfig
.. _`deltachat-core-rust github repository`: https://github.com/deltachat/deltachat-core-rust
.. _`deltachat-core`: https://github.com/deltachat/deltachat-core-rust
Running test using a debug build
--------------------------------
If you need to examine e.g. a coredump you may want to run the tests
using a debug build::
DCC_RS_TARGET=debug ./run-integration-tests.sh -e py37 -- -x -v -k failing_test
Building manylinux1 wheels
==========================
.. note::
This section may not fully work.
Building portable manylinux1 wheels which come with libdeltachat.so
and all it's dependencies is easy using the provided docker tooling.
using docker pull / premade images
------------------------------------
We publish a build environment under the ``deltachat/wheel`` tag so
We publish a build environment under the ``deltachat/coredeps`` tag so
that you can pull it from the ``hub.docker.com`` site's "deltachat"
organization::
$ docker pull deltachat/wheel
$ docker pull deltachat/coredeps
The ``deltachat/wheel`` image can be used to build both libdeltachat.so
and the Python wheels::
This docker image can be used to run tests and build Python wheels for all interpreters::
$ docker run --rm -it -v $(pwd):/io/ deltachat/wheel /io/python/wheelbuilder/build-wheels.sh
$ bash ci_scripts/ci_run.sh
This command runs a script within the image, after mounting ``$(pwd)`` as ``/io`` within
the docker image. The script is specified as a path within the docker image's filesystem.
The resulting wheel files will be in ``python/wheelhouse``.
This command runs tests and build-wheel scripts in a docker container.
Optionally build your own docker image
@@ -154,10 +147,10 @@ Optionally build your own docker image
If you want to build your own custom docker image you can do this::
$ cd deltachat-core # cd to deltachat-core checkout directory
$ docker build -t deltachat/wheel python/wheelbuilder/
$ docker build -t deltachat/coredeps ci_scripts/docker_coredeps
This will use the ``python/wheelbuilder/Dockerfile`` to build
up docker image called ``deltachat/wheel``. You can afterwards
This will use the ``ci_scripts/docker_coredeps/Dockerfile`` to build
up docker image called ``deltachat/coredeps``. You can afterwards
find it with::
$ docker images

View File

@@ -37,16 +37,3 @@ Message
.. autoclass:: deltachat.message.Message
:members:
MessageType
------------
.. autoclass:: deltachat.message.MessageType
:members:
MessageState
------------
.. autoclass:: deltachat.message.MessageState
:members:

View File

@@ -8,8 +8,7 @@ Playing around on the commandline
----------------------------------
Once you have :doc:`installed deltachat bindings <install>`
you can start playing from the python interpreter commandline::
you can start playing from the python interpreter commandline.
For example you can type ``python`` and then::
# instantiate and configure deltachat account
@@ -23,7 +22,7 @@ For example you can type ``python`` and then::
# create a contact and send a message
contact = ac.create_contact("someother@email.address")
chat = ac.create_chat_by_contact(contact)
chat.send_text_message("hi from the python interpreter command line")
chat.send_text("hi from the python interpreter command line")
Checkout our :doc:`api` for the various high-level things you can do
to send/receive messages, create contacts and chats.

View File

@@ -6,29 +6,18 @@
import os
import subprocess
import os
if __name__ == "__main__":
os.environ["DCC_RS_TARGET"] = target = "release"
if "DCC_RS_DEV" not in os.environ:
dn = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.environ["DCC_RS_DEV"] = dn
toml = os.path.join(os.getcwd(), "..", "Cargo.toml")
assert os.path.exists(toml)
with open(toml) as f:
s = orig = f.read()
s += "\n"
s += "[profile.release]\n"
s += "debug = true\n"
with open(toml, "w") as f:
f.write(s)
print("temporarily modifying Cargo.toml to provide release build with debug symbols ")
try:
subprocess.check_call([
"cargo", "build", "-p", "deltachat_ffi", "--" + target
])
finally:
with open(toml, "w") as f:
f.write(orig)
print("\nreseted Cargo.toml to previous original state")
os.environ["RUSTFLAGS"] = "-g"
subprocess.check_call([
"cargo", "build", "-p", "deltachat_ffi", "--" + target
])
subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True)
subprocess.check_call([

View File

@@ -34,13 +34,14 @@ def py_dc_callback(ctx, evt, data1, data2):
if data1 and event_sig_types & 1:
data1 = ffi.string(ffi.cast('char*', data1)).decode("utf8")
if data2 and event_sig_types & 2:
data2 = ffi.string(ffi.cast('char*', data2)).decode("utf8")
try:
data2 = ffi.string(ffi.cast('char*', data2)).decode("utf8")
if isinstance(data2, bytes):
data2 = data2.decode("utf8")
except UnicodeDecodeError:
# XXX ignoring this error is not quite correct but for now
# XXX ignoring the decode error is not quite correct but for now
# i don't want to hunt down encoding problems in the c lib
data2 = ffi.string(ffi.cast('char*', data2))
pass
try:
ret = callback(ctx, evt_name, data1, data2)
if ret is None:

View File

@@ -6,10 +6,15 @@ import platform
import os
import cffi
import shutil
from os.path import dirname as dn
from os.path import abspath
def ffibuilder():
projdir = os.environ.get('DCC_RS_DEV')
if not projdir:
p = dn(dn(dn(dn(abspath(__file__)))))
projdir = os.environ["DCC_RS_DEV"] = p
target = os.environ.get('DCC_RS_TARGET', 'release')
if projdir:
if platform.system() == 'Darwin':

View File

@@ -50,8 +50,9 @@ class Account(object):
self._configkeys = self.get_config("sys.config_keys").split()
self._imex_completed = threading.Event()
def __del__(self):
self.shutdown()
# XXX this can cause "illegal instructions" at test ends so we omit it for now
# def __del__(self):
# self.shutdown()
def _check_config_key(self, name):
if name not in self._configkeys:

View File

@@ -16,4 +16,4 @@ def iter_array(dc_array_t, constructor):
def from_dc_charpointer(obj):
return ffi.string(obj).decode("utf8")
return ffi.string(ffi.gc(obj, lib.dc_str_unref)).decode("utf8")

View File

@@ -142,7 +142,7 @@ class Message(object):
import email.parser
mime_headers = lib.dc_get_mime_headers(self._dc_context, self.id)
if mime_headers:
s = ffi.string(mime_headers)
s = ffi.string(ffi.gc(mime_headers, lib.dc_str_unref))
if isinstance(s, bytes):
s = s.decode("ascii")
return email.message_from_string(s)

View File

@@ -1,9 +1,9 @@
from __future__ import print_function
import os
import pytest
import requests
import time
from deltachat import Account
from deltachat import props
from deltachat.capi import lib
import tempfile
@@ -36,6 +36,8 @@ def pytest_runtest_call(item):
def pytest_report_header(config, startdir):
summary = []
t = tempfile.mktemp()
try:
ac = Account(t, eventlogging=False)
@@ -43,13 +45,18 @@ def pytest_report_header(config, startdir):
ac.shutdown()
finally:
os.remove(t)
summary = ['Deltachat core={} sqlite={}'.format(
summary.extend(['Deltachat core={} sqlite={}'.format(
info['deltachat_core_version'],
info['sqlite_version'],
)]
cfg = config.getoption('--liveconfig')
)])
cfg = config.option.liveconfig
if cfg:
summary.append('Liveconfig: {}'.format(os.path.abspath(cfg)))
if "#" in cfg:
url, token = cfg.split("#", 1)
summary.append('Liveconfig provider: {}#<token ommitted>'.format(url))
else:
summary.append('Liveconfig file: {}'.format(cfg))
return summary
@@ -66,9 +73,56 @@ def data():
return Data()
class SessionLiveConfigFromFile:
def __init__(self, fn):
self.fn = fn
self.configlist = []
for line in open(fn):
if line.strip() and not line.strip().startswith('#'):
d = {}
for part in line.split():
name, value = part.split("=")
d[name] = value
self.configlist.append(d)
def get(self, index):
return self.configlist[index]
def exists(self):
return bool(self.configlist)
class SessionLiveConfigFromURL:
def __init__(self, url, create_token):
self.configlist = []
for i in range(2):
res = requests.post(url, json={"token_create_user": int(create_token)})
if res.status_code != 200:
pytest.skip("creating newtmpuser failed {!r}".format(res))
d = res.json()
config = dict(addr=d["email"], mail_pw=d["password"])
self.configlist.append(config)
def get(self, index):
return self.configlist[index]
def exists(self):
return bool(self.configlist)
@pytest.fixture(scope="session")
def session_liveconfig(request):
liveconfig_opt = request.config.option.liveconfig
if liveconfig_opt:
if liveconfig_opt.startswith("http"):
url, create_token = liveconfig_opt.split("#", 1)
return SessionLiveConfigFromURL(url, create_token)
else:
return SessionLiveConfigFromFile(liveconfig_opt)
@pytest.fixture
def acfactory(pytestconfig, tmpdir, request):
fn = pytestconfig.getoption("--liveconfig")
def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
class AccountMaker:
def __init__(self):
@@ -82,18 +136,6 @@ def acfactory(pytestconfig, tmpdir, request):
fin = self._finalizers.pop()
fin()
@props.cached
def configlist(self):
configlist = []
for line in open(fn):
if line.strip() and not line.strip().startswith('#'):
d = {}
for part in line.split():
name, value = part.split("=")
d[name] = value
configlist.append(d)
return configlist
def get_unconfigured_account(self):
self.offline_count += 1
tmpdb = tmpdir.join("offlinedb%d" % self.offline_count)
@@ -116,10 +158,10 @@ def acfactory(pytestconfig, tmpdir, request):
return ac
def get_online_configuring_account(self):
if not fn:
pytest.skip("specify a --liveconfig file to run tests with real accounts")
if not session_liveconfig:
pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")
configdict = session_liveconfig.get(self.live_count)
self.live_count += 1
configdict = self.configlist.pop(0)
if "e2ee_enabled" not in configdict:
configdict["e2ee_enabled"] = "1"
tmpdb = tmpdir.join("livedb%d" % self.live_count)

View File

@@ -19,6 +19,7 @@ deps =
pytest
pytest-faulthandler
pdbpp
requests
[testenv:auditwheels]
skipsdist = True
@@ -51,6 +52,7 @@ commands =
[pytest]
addopts = -v -rs
python_files = tests/test_*.py
norecursedirs = .tox
xfail_strict=true

View File

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

View File

@@ -1 +1 @@
nightly-2019-07-10
nightly-2019-08-13

2045
src/chat.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,10 @@
use crate::chat::*;
use crate::constants::*;
use crate::contact::*;
use crate::context::*;
use crate::dc_chat::*;
use crate::dc_lot::*;
use crate::dc_msg::*;
use crate::dc_tools::*;
use crate::error::Result;
use crate::lot::Lot;
use crate::stock::StockMessage;
/// An object representing a single chatlist in memory.
@@ -248,39 +247,39 @@ impl<'a> Chatlist<'a> {
/// - dc_lot_t::timestamp: the timestamp of the message. 0 if not applicable.
/// - dc_lot_t::state: The state of the message as one of the DC_STATE_* constants (see #dc_msg_get_state()).
// 0 if not applicable.
pub unsafe fn get_summary(&self, index: usize, mut chat: *mut Chat<'a>) -> *mut dc_lot_t {
pub unsafe fn get_summary(&self, index: usize, chat: Option<&Chat<'a>>) -> Lot {
// The summary is created by the chat, not by the last message.
// This is because we may want to display drafts here or stuff as
// "is typing".
// Also, sth. as "No messages" would not work if the summary comes from a message.
let mut ret = dc_lot_new();
let mut ret = Lot::new();
if index >= self.ids.len() {
(*ret).text2 = "ErrBadChatlistIndex".strdup();
ret.text2 = Some("ErrBadChatlistIndex".to_string());
return ret;
}
let lastmsg_id = self.ids[index].1;
let mut lastcontact = None;
if chat.is_null() {
chat = dc_chat_new(self.context);
let chat_to_delete = chat;
if !dc_chat_load_from_db(chat, self.ids[index].0) {
(*ret).text2 = "ErrCannotReadChat".strdup();
dc_chat_unref(chat_to_delete);
let chat_loaded: Chat;
let chat = if let Some(chat) = chat {
chat
} else {
if let Ok(chat) = Chat::load_from_db(self.context, self.ids[index].0) {
chat_loaded = chat;
&chat_loaded
} else {
return ret;
}
}
};
let lastmsg_id = self.ids[index].1;
let mut lastcontact = None;
let lastmsg = if 0 != lastmsg_id {
let lastmsg = dc_msg_new_untyped(self.context);
dc_msg_load_from_db(lastmsg, self.context, lastmsg_id);
if (*lastmsg).from_id != 1 as libc::c_uint
&& ((*chat).type_0 == DC_CHAT_TYPE_GROUP
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP)
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
{
lastcontact = Contact::load_from_db(self.context, (*lastmsg).from_id).ok();
}
@@ -289,12 +288,12 @@ impl<'a> Chatlist<'a> {
std::ptr::null_mut()
};
if (*chat).id == DC_CHAT_ID_ARCHIVED_LINK as u32 {
(*ret).text2 = dc_strdup(0 as *const libc::c_char)
if chat.id == DC_CHAT_ID_ARCHIVED_LINK as u32 {
ret.text2 = None;
} else if lastmsg.is_null() || (*lastmsg).from_id == DC_CONTACT_ID_UNDEFINED as u32 {
(*ret).text2 = self.context.stock_str(StockMessage::NoMessages).strdup();
ret.text2 = Some(self.context.stock_str(StockMessage::NoMessages).to_string());
} else {
dc_lot_fill(ret, lastmsg, chat, lastcontact.as_ref(), self.context);
ret.fill(lastmsg, chat, lastcontact.as_ref(), self.context);
}
dc_msg_unref(lastmsg);

View File

@@ -3,9 +3,9 @@ use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
use crate::constants::DC_VERSION_STR;
use crate::context::Context;
use crate::dc_job::*;
use crate::dc_tools::*;
use crate::error::Error;
use crate::job::*;
use crate::stock::StockMessage;
/// The available configuration keys.
@@ -48,11 +48,14 @@ pub enum Config {
ConfiguredMailUser,
ConfiguredMailPw,
ConfiguredMailPort,
ConfiguredMailSecurity,
ConfiguredSendServer,
ConfiguredSendUser,
ConfiguredSendPw,
ConfiguredSendPort,
ConfiguredServerFlags,
ConfiguredSendSecurity,
ConfiguredE2EEEnabled,
Configured,
// Deprecated
#[strum(serialize = "sys.version")]
@@ -71,7 +74,7 @@ impl Context {
let rel_path = self.sql.get_config(self, key);
rel_path.map(|p| dc_get_abs_path_safe(self, &p).to_str().unwrap().to_string())
}
Config::SysVersion => Some(std::str::from_utf8(DC_VERSION_STR).unwrap().into()),
Config::SysVersion => Some((&*DC_VERSION_STR).clone()),
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
Config::SysConfigKeys => Some(get_config_keys_string()),
_ => self.sql.get_config(self, key),
@@ -99,17 +102,17 @@ impl Context {
}
Config::InboxWatch => {
let ret = self.sql.set_config(self, key, value);
unsafe { dc_interrupt_imap_idle(self) };
interrupt_imap_idle(self);
ret
}
Config::SentboxWatch => {
let ret = self.sql.set_config(self, key, value);
unsafe { dc_interrupt_sentbox_idle(self) };
interrupt_sentbox_idle(self);
ret
}
Config::MvboxWatch => {
let ret = self.sql.set_config(self, key, value);
unsafe { dc_interrupt_mvbox_idle(self) };
interrupt_mvbox_idle(self);
ret
}
Config::Selfstatus => {
@@ -120,8 +123,7 @@ impl Context {
value
};
let ret = self.sql.set_config(self, key, val);
ret
self.sql.set_config(self, key, val)
}
_ => self.sql.set_config(self, key, value),
}

View File

@@ -1,61 +1,77 @@
//! Constants
#![allow(non_camel_case_types)]
use num_traits::{FromPrimitive, ToPrimitive};
use rusqlite as sql;
use rusqlite::types::*;
#![allow(non_camel_case_types, dead_code)]
pub const DC_VERSION_STR: &'static [u8; 14] = b"1.0.0-alpha.3\x00";
use lazy_static::lazy_static;
pub const DC_MOVE_STATE_MOVING: u32 = 3;
pub const DC_MOVE_STATE_STAY: u32 = 2;
pub const DC_MOVE_STATE_PENDING: u32 = 1;
pub const DC_MOVE_STATE_UNDEFINED: u32 = 0;
use deltachat_derive::*;
lazy_static! {
pub static ref DC_VERSION_STR: String = env!("CARGO_PKG_VERSION").to_string();
}
#[repr(u8)]
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
pub enum MoveState {
Undefined = 0,
Pending = 1,
Stay = 2,
Moving = 3,
}
// some defaults
const DC_E2EE_DEFAULT_ENABLED: i32 = 1;
pub const DC_MDNS_DEFAULT_ENABLED: i32 = 1;
const DC_INBOX_WATCH_DEFAULT: i32 = 1;
const DC_SENTBOX_WATCH_DEFAULT: i32 = 1;
const DC_MVBOX_WATCH_DEFAULT: i32 = 1;
const DC_MVBOX_MOVE_DEFAULT: i32 = 1;
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
#[repr(u8)]
pub enum Blocked {
Not = 0,
Manually = 1,
Deaddrop = 2,
}
impl Default for Blocked {
fn default() -> Self {
Blocked::Not
}
}
pub const DC_IMAP_SEEN: u32 = 0x1;
const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
pub const DC_HANDSHAKE_STOP_NORMAL_PROCESSING: i32 = 0x02;
pub const DC_HANDSHAKE_ADD_DELETE_JOB: i32 = 0x04;
pub const DC_GCL_ARCHIVED_ONLY: usize = 0x01;
pub const DC_GCL_NO_SPECIALS: usize = 0x02;
pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
pub const DC_GCM_ADDDAYMARKER: usize = 0x01;
const DC_GCM_ADDDAYMARKER: usize = 0x01;
pub const DC_GCL_VERIFIED_ONLY: usize = 0x01;
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
pub const DC_IMEX_EXPORT_SELF_KEYS: usize = 1;
const DC_IMEX_EXPORT_SELF_KEYS: usize = 1;
/// param1 is a directory where the keys are searched in and read from
pub const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
/// param1 is a directory where the backup is written to
pub const DC_IMEX_EXPORT_BACKUP: usize = 11;
const DC_IMEX_EXPORT_BACKUP: usize = 11;
/// param1 is the file with the backup to import
pub const DC_IMEX_IMPORT_BACKUP: usize = 12;
/// id=contact
pub const DC_QR_ASK_VERIFYCONTACT: usize = 200;
/// text1=groupname
pub const DC_QR_ASK_VERIFYGROUP: usize = 202;
/// id=contact
pub const DC_QR_FPR_OK: usize = 210;
/// id=contact
pub const DC_QR_FPR_MISMATCH: usize = 220;
/// test1=formatted fingerprint
pub const DC_QR_FPR_WITHOUT_ADDR: usize = 230;
/// id=contact
pub const DC_QR_ADDR: usize = 320;
/// text1=text
pub const DC_QR_TEXT: usize = 330;
/// text1=URL
pub const DC_QR_URL: usize = 332;
/// text1=error string
pub const DC_QR_ERROR: usize = 400;
const DC_IMEX_IMPORT_BACKUP: usize = 12;
/// virtual chat showing all messages belonging to chats flagged with chats.blocked=2
pub const DC_CHAT_ID_DEADDROP: usize = 1;
pub(crate) const DC_CHAT_ID_DEADDROP: usize = 1;
/// messages that should be deleted get this chat_id; the messages are deleted from the working thread later then. This is also needed as rfc724_mid should be preset as long as the message is not deleted on the server (otherwise it is downloaded again)
pub const DC_CHAT_ID_TRASH: usize = 3;
/// a message is just in creation but not yet assigned to a chat (eg. we may need the message ID to set up blobs; this avoids unready message to be sent and shown)
pub const DC_CHAT_ID_MSGS_IN_CREATION: usize = 4;
const DC_CHAT_ID_MSGS_IN_CREATION: usize = 4;
/// virtual chat showing all messages flagged with msgs.starred=2
pub const DC_CHAT_ID_STARRED: usize = 5;
const DC_CHAT_ID_STARRED: usize = 5;
/// only an indicator in a chatlist
pub const DC_CHAT_ID_ARCHIVED_LINK: usize = 6;
/// only an indicator in a chatlist
@@ -63,41 +79,47 @@ pub const DC_CHAT_ID_ALLDONE_HINT: usize = 7;
/// larger chat IDs are "real" chats, their messages are "real" messages.
pub const DC_CHAT_ID_LAST_SPECIAL: usize = 9;
pub const DC_CHAT_TYPE_UNDEFINED: i32 = 0;
pub const DC_CHAT_TYPE_SINGLE: i32 = 100;
pub const DC_CHAT_TYPE_GROUP: i32 = 120;
pub const DC_CHAT_TYPE_VERIFIED_GROUP: i32 = 130;
#[derive(
Debug,
Display,
Clone,
Copy,
PartialEq,
Eq,
FromPrimitive,
ToPrimitive,
FromSql,
ToSql,
IntoStaticStr,
)]
#[repr(u32)]
pub enum Chattype {
Undefined = 0,
Single = 100,
Group = 120,
VerifiedGroup = 130,
}
impl Default for Chattype {
fn default() -> Self {
Chattype::Undefined
}
}
pub const DC_MSG_ID_MARKER1: usize = 1;
pub const DC_MSG_ID_DAYMARKER: usize = 9;
const DC_MSG_ID_DAYMARKER: usize = 9;
pub const DC_MSG_ID_LAST_SPECIAL: usize = 9;
pub const DC_STATE_UNDEFINED: i32 = 0;
pub const DC_STATE_IN_FRESH: i32 = 10;
pub const DC_STATE_IN_NOTICED: i32 = 13;
pub const DC_STATE_IN_SEEN: i32 = 16;
pub const DC_STATE_OUT_PREPARING: i32 = 18;
pub const DC_STATE_OUT_DRAFT: i32 = 19;
pub const DC_STATE_OUT_PENDING: i32 = 20;
pub const DC_STATE_OUT_FAILED: i32 = 24;
/// to check if a mail was sent, use dc_msg_is_sent()
pub const DC_STATE_OUT_DELIVERED: i32 = 26;
pub const DC_STATE_OUT_MDN_RCVD: i32 = 28;
/// approx. max. length returned by dc_msg_get_text()
pub const DC_MAX_GET_TEXT_LEN: usize = 30000;
const DC_MAX_GET_TEXT_LEN: usize = 30000;
/// approx. max. length returned by dc_get_msg_info()
pub const DC_MAX_GET_INFO_LEN: usize = 100000;
const DC_MAX_GET_INFO_LEN: usize = 100000;
pub const DC_CONTACT_ID_UNDEFINED: usize = 0;
pub const DC_CONTACT_ID_SELF: usize = 1;
pub const DC_CONTACT_ID_DEVICE: usize = 2;
const DC_CONTACT_ID_DEVICE: usize = 2;
pub const DC_CONTACT_ID_LAST_SPECIAL: usize = 9;
pub const DC_TEXT1_DRAFT: usize = 1;
pub const DC_TEXT1_USERNAME: usize = 2;
pub const DC_TEXT1_SELF: usize = 3;
pub const DC_CREATE_MVBOX: usize = 1;
// Flags for configuring IMAP and SMTP servers.
@@ -112,7 +134,7 @@ pub const DC_LP_AUTH_OAUTH2: usize = 0x2;
/// Force NORMAL authorization, this is the default.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_AUTH_NORMAL: usize = 0x4;
const DC_LP_AUTH_NORMAL: usize = 0x4;
/// Connect to IMAP via STARTTLS.
/// If this flag is set, automatic configuration is skipped.
@@ -120,7 +142,7 @@ pub const DC_LP_IMAP_SOCKET_STARTTLS: usize = 0x100;
/// Connect to IMAP via SSL.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_IMAP_SOCKET_SSL: usize = 0x200;
const DC_LP_IMAP_SOCKET_SSL: usize = 0x200;
/// Connect to IMAP unencrypted, this should not be used.
/// If this flag is set, automatic configuration is skipped.
@@ -132,22 +154,22 @@ pub const DC_LP_SMTP_SOCKET_STARTTLS: usize = 0x10000;
/// Connect to SMTP via SSL.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
/// Connect to SMTP unencrypted, this should not be used.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_SMTP_SOCKET_PLAIN: usize = 0x40000;
/// if none of these flags are set, the default is chosen
pub const DC_LP_AUTH_FLAGS: usize = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
const DC_LP_AUTH_FLAGS: usize = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
/// if none of these flags are set, the default is chosen
pub const DC_LP_IMAP_SOCKET_FLAGS: usize =
const DC_LP_IMAP_SOCKET_FLAGS: usize =
(DC_LP_IMAP_SOCKET_STARTTLS | DC_LP_IMAP_SOCKET_SSL | DC_LP_IMAP_SOCKET_PLAIN);
/// if none of these flags are set, the default is chosen
pub const DC_LP_SMTP_SOCKET_FLAGS: usize =
const DC_LP_SMTP_SOCKET_FLAGS: usize =
(DC_LP_SMTP_SOCKET_STARTTLS | DC_LP_SMTP_SOCKET_SSL | DC_LP_SMTP_SOCKET_PLAIN);
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
#[repr(i32)]
pub enum Viewtype {
Unknown = 0,
@@ -202,23 +224,6 @@ mod tests {
}
}
impl ToSql for Viewtype {
fn to_sql(&self) -> sql::Result<ToSqlOutput> {
let num: i64 = self
.to_i64()
.expect("impossible: Viewtype -> i64 conversion failed");
Ok(ToSqlOutput::Owned(Value::Integer(num)))
}
}
impl FromSql for Viewtype {
fn column_result(col: ValueRef) -> FromSqlResult<Self> {
let inner = FromSql::column_result(col)?;
FromPrimitive::from_i64(inner).ok_or(FromSqlError::InvalidType)
}
}
// These constants are used as events
// reported to the callback given to dc_context_new().
// If you do not want to handle an event, it is always safe to return 0,
@@ -460,65 +465,65 @@ pub enum Event {
GET_STRING = 2091,
}
pub const DC_EVENT_FILE_COPIED: usize = 2055; // deprecated;
pub const DC_EVENT_IS_OFFLINE: usize = 2081; // deprecated;
pub const DC_ERROR_SEE_STRING: usize = 0; // deprecated;
pub const DC_ERROR_SELF_NOT_IN_GROUP: usize = 1; // deprecated;
pub const DC_STR_SELFNOTINGRP: usize = 21; // deprecated;
const DC_EVENT_FILE_COPIED: usize = 2055; // deprecated;
const DC_EVENT_IS_OFFLINE: usize = 2081; // deprecated;
const DC_ERROR_SEE_STRING: usize = 0; // deprecated;
const DC_ERROR_SELF_NOT_IN_GROUP: usize = 1; // deprecated;
const DC_STR_SELFNOTINGRP: usize = 21; // deprecated;
/// Values for dc_get|set_config("show_emails")
pub const DC_SHOW_EMAILS_OFF: usize = 0;
pub const DC_SHOW_EMAILS_ACCEPTED_CONTACTS: usize = 1;
pub const DC_SHOW_EMAILS_ALL: usize = 2;
const DC_SHOW_EMAILS_OFF: usize = 0;
const DC_SHOW_EMAILS_ACCEPTED_CONTACTS: usize = 1;
const DC_SHOW_EMAILS_ALL: usize = 2;
// TODO: Strings need some doumentation about used placeholders.
// These constants are used to request strings using #DC_EVENT_GET_STRING.
pub const DC_STR_NOMESSAGES: usize = 1;
pub const DC_STR_SELF: usize = 2;
pub const DC_STR_DRAFT: usize = 3;
pub const DC_STR_MEMBER: usize = 4;
pub const DC_STR_CONTACT: usize = 6;
pub const DC_STR_VOICEMESSAGE: usize = 7;
pub const DC_STR_DEADDROP: usize = 8;
pub const DC_STR_IMAGE: usize = 9;
pub const DC_STR_VIDEO: usize = 10;
pub const DC_STR_AUDIO: usize = 11;
pub const DC_STR_FILE: usize = 12;
pub const DC_STR_STATUSLINE: usize = 13;
pub const DC_STR_NEWGROUPDRAFT: usize = 14;
pub const DC_STR_MSGGRPNAME: usize = 15;
pub const DC_STR_MSGGRPIMGCHANGED: usize = 16;
pub const DC_STR_MSGADDMEMBER: usize = 17;
pub const DC_STR_MSGDELMEMBER: usize = 18;
pub const DC_STR_MSGGROUPLEFT: usize = 19;
pub const DC_STR_GIF: usize = 23;
pub const DC_STR_ENCRYPTEDMSG: usize = 24;
pub const DC_STR_E2E_AVAILABLE: usize = 25;
pub const DC_STR_ENCR_TRANSP: usize = 27;
pub const DC_STR_ENCR_NONE: usize = 28;
pub const DC_STR_CANTDECRYPT_MSG_BODY: usize = 29;
pub const DC_STR_FINGERPRINTS: usize = 30;
pub const DC_STR_READRCPT: usize = 31;
pub const DC_STR_READRCPT_MAILBODY: usize = 32;
pub const DC_STR_MSGGRPIMGDELETED: usize = 33;
pub const DC_STR_E2E_PREFERRED: usize = 34;
pub const DC_STR_CONTACT_VERIFIED: usize = 35;
pub const DC_STR_CONTACT_NOT_VERIFIED: usize = 36;
pub const DC_STR_CONTACT_SETUP_CHANGED: usize = 37;
pub const DC_STR_ARCHIVEDCHATS: usize = 40;
pub const DC_STR_STARREDMSGS: usize = 41;
pub const DC_STR_AC_SETUP_MSG_SUBJECT: usize = 42;
pub const DC_STR_AC_SETUP_MSG_BODY: usize = 43;
pub const DC_STR_SELFTALK_SUBTITLE: usize = 50;
pub const DC_STR_CANNOT_LOGIN: usize = 60;
pub const DC_STR_SERVER_RESPONSE: usize = 61;
pub const DC_STR_MSGACTIONBYUSER: usize = 62;
pub const DC_STR_MSGACTIONBYME: usize = 63;
pub const DC_STR_MSGLOCATIONENABLED: usize = 64;
pub const DC_STR_MSGLOCATIONDISABLED: usize = 65;
pub const DC_STR_LOCATION: usize = 66;
pub const DC_STR_COUNT: usize = 66;
const DC_STR_NOMESSAGES: usize = 1;
const DC_STR_SELF: usize = 2;
const DC_STR_DRAFT: usize = 3;
const DC_STR_MEMBER: usize = 4;
const DC_STR_CONTACT: usize = 6;
const DC_STR_VOICEMESSAGE: usize = 7;
const DC_STR_DEADDROP: usize = 8;
const DC_STR_IMAGE: usize = 9;
const DC_STR_VIDEO: usize = 10;
const DC_STR_AUDIO: usize = 11;
const DC_STR_FILE: usize = 12;
const DC_STR_STATUSLINE: usize = 13;
const DC_STR_NEWGROUPDRAFT: usize = 14;
const DC_STR_MSGGRPNAME: usize = 15;
const DC_STR_MSGGRPIMGCHANGED: usize = 16;
const DC_STR_MSGADDMEMBER: usize = 17;
const DC_STR_MSGDELMEMBER: usize = 18;
const DC_STR_MSGGROUPLEFT: usize = 19;
const DC_STR_GIF: usize = 23;
const DC_STR_ENCRYPTEDMSG: usize = 24;
const DC_STR_E2E_AVAILABLE: usize = 25;
const DC_STR_ENCR_TRANSP: usize = 27;
const DC_STR_ENCR_NONE: usize = 28;
const DC_STR_CANTDECRYPT_MSG_BODY: usize = 29;
const DC_STR_FINGERPRINTS: usize = 30;
const DC_STR_READRCPT: usize = 31;
const DC_STR_READRCPT_MAILBODY: usize = 32;
const DC_STR_MSGGRPIMGDELETED: usize = 33;
const DC_STR_E2E_PREFERRED: usize = 34;
const DC_STR_CONTACT_VERIFIED: usize = 35;
const DC_STR_CONTACT_NOT_VERIFIED: usize = 36;
const DC_STR_CONTACT_SETUP_CHANGED: usize = 37;
const DC_STR_ARCHIVEDCHATS: usize = 40;
const DC_STR_STARREDMSGS: usize = 41;
const DC_STR_AC_SETUP_MSG_SUBJECT: usize = 42;
const DC_STR_AC_SETUP_MSG_BODY: usize = 43;
const DC_STR_SELFTALK_SUBTITLE: usize = 50;
const DC_STR_CANNOT_LOGIN: usize = 60;
const DC_STR_SERVER_RESPONSE: usize = 61;
const DC_STR_MSGACTIONBYUSER: usize = 62;
const DC_STR_MSGACTIONBYME: usize = 63;
const DC_STR_MSGLOCATIONENABLED: usize = 64;
const DC_STR_MSGLOCATIONDISABLED: usize = 65;
const DC_STR_LOCATION: usize = 66;
const DC_STR_COUNT: usize = 66;
pub const DC_JOB_DELETE_MSG_ON_IMAP: i32 = 110;
@@ -534,6 +539,6 @@ pub const DC_CMD_GROUPIMAGE_CHANGED: libc::c_int = 3;
pub const DC_CMD_MEMBER_ADDED_TO_GROUP: libc::c_int = 4;
pub const DC_CMD_MEMBER_REMOVED_FROM_GROUP: libc::c_int = 5;
pub const DC_CMD_AUTOCRYPT_SETUP_MESSAGE: libc::c_int = 6;
pub const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
pub const DC_CMD_LOCATION_STREAMING_ENABLED: libc::c_int = 8;
pub const DC_CMD_LOCATION_ONLY: libc::c_int = 9;
const DC_CMD_LOCATION_ONLY: libc::c_int = 9;

View File

@@ -7,9 +7,9 @@ use crate::aheader::EncryptPreference;
use crate::config::Config;
use crate::constants::*;
use crate::context::Context;
use crate::dc_array::*;
use crate::dc_e2ee::*;
use crate::dc_loginparam::*;
use crate::dc_msg::MessageState;
use crate::dc_tools::*;
use crate::error::Result;
use crate::key::*;
@@ -139,7 +139,7 @@ pub enum Modifier {
Created,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive)]
#[repr(u8)]
pub enum VerifiedStatus {
/// Contact is not verified.
@@ -251,7 +251,7 @@ impl<'a> Contact<'a> {
context,
&context.sql,
"UPDATE msgs SET state=? WHERE from_id=? AND state=?;",
params![DC_STATE_IN_NOTICED, id as i32, DC_STATE_IN_FRESH],
params![MessageState::InNoticed, id as i32, MessageState::InFresh],
)
.is_ok()
{
@@ -328,7 +328,7 @@ impl<'a> Contact<'a> {
"<unset>"
},
);
bail!("Bad address supplied");
bail!("Bad address supplied: {:?}", addr);
}
let mut update_addr = false;
@@ -472,13 +472,13 @@ impl<'a> Contact<'a> {
context: &Context,
listflags: u32,
query: Option<impl AsRef<str>>,
) -> Result<*mut dc_array_t> {
) -> Result<Vec<u32>> {
let self_addr = context
.get_config(Config::ConfiguredAddr)
.unwrap_or_default();
let mut add_self = false;
let mut ret = dc_array_t::new(100);
let mut ret = Vec::new();
if (listflags & DC_GCL_VERIFIED_ONLY) > 0 || query.is_some() {
let s3str_like_cmd = format!(
@@ -509,7 +509,7 @@ impl<'a> Contact<'a> {
|row| row.get::<_, i32>(0),
|ids| {
for id in ids {
ret.add_id(id? as u32);
ret.push(id? as u32);
}
Ok(())
},
@@ -537,7 +537,7 @@ impl<'a> Contact<'a> {
|row| row.get::<_, i32>(0),
|ids| {
for id in ids {
ret.add_id(id? as u32);
ret.push(id? as u32);
}
Ok(())
}
@@ -545,10 +545,10 @@ impl<'a> Contact<'a> {
}
if 0 != listflags & DC_GCL_ADD_SELF as u32 && add_self {
ret.add_id(DC_CONTACT_ID_SELF as u32);
ret.push(DC_CONTACT_ID_SELF as u32);
}
Ok(ret.into_raw())
Ok(ret)
}
pub fn get_blocked_cnt(context: &Context) -> usize {
@@ -564,27 +564,27 @@ impl<'a> Contact<'a> {
}
/// Get blocked contacts.
pub fn get_all_blocked(context: &Context) -> *mut dc_array_t {
pub fn get_all_blocked(context: &Context) -> Vec<u32> {
context
.sql
.query_map(
"SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(name||addr),id;",
params![DC_CONTACT_ID_LAST_SPECIAL as i32],
|row| row.get::<_, i32>(0),
|row| row.get::<_, u32>(0),
|ids| {
let mut ret = dc_array_t::new(100);
for id in ids {
ret.add_id(id? as u32);
}
Ok(ret.into_raw())
ids.collect::<std::result::Result<Vec<_>, _>>()
.map_err(Into::into)
},
)
.unwrap_or_else(|_| std::ptr::null_mut())
.unwrap_or_default()
}
pub fn get_encrinfo(context: &Context, contact_id: u32) -> String {
/// Returns a textual summary of the encryption state for the contact.
///
/// This function returns a string explaining the encryption state
/// of the contact and if the connection is encrypted the
/// fingerprints of the keys involved.
pub fn get_encrinfo(context: &Context, contact_id: u32) -> Result<String> {
let mut ret = String::new();
if let Ok(contact) = Contact::load_from_db(context, contact_id) {
@@ -603,7 +603,7 @@ impl<'a> Contact<'a> {
});
ret += &p;
if self_key.is_none() {
unsafe { dc_ensure_secret_key_exists(context) };
dc_ensure_secret_key_exists(context)?;
self_key = Key::from_self_public(context, &loginparam.addr, &context.sql);
}
let p = context.stock_str(StockMessage::FingerPrints);
@@ -646,7 +646,7 @@ impl<'a> Contact<'a> {
}
}
ret
Ok(ret)
}
/// Delete a contact. The contact is deleted from the local device. It may happen that this is not
@@ -792,7 +792,7 @@ impl<'a> Contact<'a> {
/// and can be used for an fallback avatar with white initials
/// as well as for headlines in bubbles of group chats.
pub fn get_color(&self) -> u32 {
dc_str_to_color_safe(&self.addr)
dc_str_to_color(&self.addr)
}
/// Check if a contact was verified. E.g. by a secure-join QR code scan
@@ -904,26 +904,15 @@ impl<'a> Contact<'a> {
}
}
pub fn get_first_name<'a>(full_name: &'a str) -> &'a str {
fn get_first_name<'a>(full_name: &'a str) -> &'a str {
full_name.splitn(2, ' ').next().unwrap_or_default()
}
/// Returns false if addr is an invalid address, otherwise true.
pub fn may_be_valid_addr(addr: &str) -> bool {
if addr.is_empty() {
return false;
}
let at = addr.find('@').unwrap_or_default();
if at < 1 {
return false;
}
let dot = addr.find('.').unwrap_or_default();
if dot < 1 || dot > addr.len() - 3 || dot < at + 2 {
return false;
}
true
let res = addr.parse::<EmailAddress>();
println!("{:?}", res);
res.is_ok()
}
pub fn addr_normalize(addr: &str) -> &str {

View File

@@ -1,36 +1,37 @@
use std::sync::{Arc, Condvar, Mutex, RwLock};
use crate::chat::*;
use crate::constants::*;
use crate::contact::*;
use crate::dc_array::*;
use crate::dc_chat::*;
use crate::dc_job::*;
use crate::dc_jobthread::*;
use crate::dc_loginparam::*;
use crate::dc_lot::dc_lot_t;
use crate::dc_move::*;
use crate::dc_msg::*;
use crate::dc_receive_imf::*;
use crate::dc_tools::*;
use crate::imap::*;
use crate::job::*;
use crate::job_thread::JobThread;
use crate::key::*;
use crate::lot::Lot;
use crate::param::Params;
use crate::smtp::*;
use crate::sql::Sql;
use crate::types::*;
use crate::x::*;
use std::ptr;
use std::path::PathBuf;
#[repr(C)]
pub struct Context {
pub userdata: *mut libc::c_void,
pub dbfile: Arc<RwLock<*mut libc::c_char>>,
pub dbfile: Arc<RwLock<Option<PathBuf>>>,
pub blobdir: Arc<RwLock<*mut libc::c_char>>,
pub sql: Sql,
pub inbox: Arc<RwLock<Imap>>,
pub perform_inbox_jobs_needed: Arc<RwLock<bool>>,
pub probe_imap_network: Arc<RwLock<bool>>,
pub sentbox_thread: Arc<RwLock<dc_jobthread_t>>,
pub mvbox_thread: Arc<RwLock<dc_jobthread_t>>,
pub sentbox_thread: Arc<RwLock<JobThread>>,
pub mvbox_thread: Arc<RwLock<JobThread>>,
pub smtp: Arc<Mutex<Smtp>>,
pub smtp_state: Arc<(Mutex<SmtpState>, Condvar)>,
pub oauth2_critical: Arc<Mutex<()>>,
@@ -40,6 +41,8 @@ pub struct Context {
pub bob: Arc<RwLock<BobStatus>>,
pub last_smeared_timestamp: Arc<RwLock<i64>>,
pub running_state: Arc<RwLock<RunningState>>,
/// Mutex to avoid generating the key for the user more than once.
pub generating_key_mutex: Mutex<()>,
}
unsafe impl std::marker::Send for Context {}
@@ -53,15 +56,17 @@ pub struct RunningState {
impl Context {
pub fn has_dbfile(&self) -> bool {
!self.get_dbfile().is_null()
self.get_dbfile().is_some()
}
pub fn has_blobdir(&self) -> bool {
!self.get_blobdir().is_null()
}
pub fn get_dbfile(&self) -> *const libc::c_char {
*self.dbfile.clone().read().unwrap()
pub fn get_dbfile(&self) -> Option<PathBuf> {
(*self.dbfile.clone().read().unwrap())
.as_ref()
.map(|x| x.clone())
}
pub fn get_blobdir(&self) -> *const libc::c_char {
@@ -77,6 +82,14 @@ impl Context {
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe {
dc_close(&self);
}
}
}
impl Default for RunningState {
fn default() -> Self {
RunningState {
@@ -86,21 +99,11 @@ impl Default for RunningState {
}
}
#[derive(Debug, PartialEq, Eq)]
#[derive(Default)]
pub struct BobStatus {
pub expects: i32,
pub status: i32,
pub qr_scan: *mut dc_lot_t,
}
impl Default for BobStatus {
fn default() -> Self {
BobStatus {
expects: 0,
status: 0,
qr_scan: std::ptr::null_mut(),
}
}
pub qr_scan: Option<Lot>,
}
#[derive(Default, Debug)]
@@ -120,7 +123,7 @@ pub fn dc_context_new(
) -> Context {
Context {
blobdir: Arc::new(RwLock::new(std::ptr::null_mut())),
dbfile: Arc::new(RwLock::new(std::ptr::null_mut())),
dbfile: Arc::new(RwLock::new(None)),
inbox: Arc::new(RwLock::new({
Imap::new(
cb_get_config,
@@ -131,7 +134,7 @@ pub fn dc_context_new(
})),
userdata,
cb,
os_name: os_name,
os_name,
running_state: Arc::new(RwLock::new(Default::default())),
sql: Sql::new(),
smtp: Arc::new(Mutex::new(Smtp::new())),
@@ -140,7 +143,7 @@ pub fn dc_context_new(
bob: Arc::new(RwLock::new(Default::default())),
last_smeared_timestamp: Arc::new(RwLock::new(0)),
cmdline_sel_chat_id: Arc::new(RwLock::new(0)),
sentbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
sentbox_thread: Arc::new(RwLock::new(JobThread::new(
"SENTBOX",
"configured_sentbox_folder",
Imap::new(
@@ -150,7 +153,7 @@ pub fn dc_context_new(
cb_receive_imf,
),
))),
mvbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
mvbox_thread: Arc::new(RwLock::new(JobThread::new(
"MVBOX",
"configured_mvbox_folder",
Imap::new(
@@ -162,16 +165,7 @@ pub fn dc_context_new(
))),
probe_imap_network: Arc::new(RwLock::new(false)),
perform_inbox_jobs_needed: Arc::new(RwLock::new(false)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_crashes_on_context_deref() {
let mut ctx = dc_context_new(None, std::ptr::null_mut(), Some("Test OS".into()));
unsafe { dc_context_unref(&mut ctx) };
generating_key_mutex: Mutex::new(()),
}
}
@@ -201,7 +195,7 @@ unsafe fn cb_precheck_imf(
) -> libc::c_int {
let mut rfc724_mid_exists: libc::c_int = 0i32;
let msg_id: uint32_t;
let mut old_server_folder: *mut libc::c_char = 0 as *mut libc::c_char;
let mut old_server_folder: *mut libc::c_char = ptr::null_mut();
let mut old_server_uid: uint32_t = 0i32 as uint32_t;
let mut mark_seen: libc::c_int = 0i32;
msg_id = dc_rfc724_mid_exists(
@@ -229,18 +223,24 @@ unsafe fn cb_precheck_imf(
"[move] detected moved message {}",
as_str(rfc724_mid),
);
dc_update_msg_move_state(context, rfc724_mid, DC_MOVE_STATE_STAY);
dc_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);
}
dc_do_heuristics_moves(context, server_folder, msg_id);
if 0 != mark_seen {
dc_job_add(context, 130, msg_id as libc::c_int, Params::new(), 0);
job_add(
context,
Action::MarkseenMsgOnImap,
msg_id as libc::c_int,
Params::new(),
0,
);
}
}
free(old_server_folder as *mut libc::c_void);
return rfc724_mid_exists;
rfc724_mid_exists
}
fn cb_set_config(context: &Context, key: &str, value: Option<&str>) {
@@ -258,12 +258,6 @@ fn cb_get_config(context: &Context, key: &str) -> Option<String> {
context.sql.get_config(context, key)
}
pub unsafe fn dc_context_unref(context: &mut Context) {
if 0 != dc_is_open(context) {
dc_close(context);
}
}
pub unsafe fn dc_close(context: &Context) {
info!(context, 0, "disconnecting INBOX-watch",);
context.inbox.read().unwrap().disconnect(context);
@@ -287,18 +281,14 @@ pub unsafe fn dc_close(context: &Context) {
context.sql.close(context);
let mut dbfile = context.dbfile.write().unwrap();
free(*dbfile as *mut libc::c_void);
*dbfile = 0 as *mut libc::c_char;
*dbfile = None;
let mut blobdir = context.blobdir.write().unwrap();
free(*blobdir as *mut libc::c_void);
*blobdir = 0 as *mut libc::c_char;
*blobdir = ptr::null_mut();
}
pub unsafe fn dc_is_open(context: &Context) -> libc::c_int {
match context.sql.is_open() {
true => 1,
false => 0,
}
context.sql.is_open() as libc::c_int
}
pub unsafe fn dc_get_userdata(context: &mut Context) -> *mut libc::c_void {
@@ -310,14 +300,14 @@ pub unsafe fn dc_open(context: &Context, dbfile: &str, blobdir: Option<&str>) ->
if 0 != dc_is_open(context) {
return false;
}
*context.dbfile.write().unwrap() = dbfile.strdup();
if blobdir.is_some() && blobdir.unwrap().len() > 0 {
*context.dbfile.write().unwrap() = Some(PathBuf::from(dbfile));
if blobdir.is_some() && !blobdir.unwrap().is_empty() {
let dir = dc_ensure_no_slash_safe(blobdir.unwrap()).strdup();
*context.blobdir.write().unwrap() = dir;
} else {
let dir = (dbfile.to_string() + "-blobs").strdup();
dc_create_folder(context, dir);
*context.blobdir.write().unwrap() = dir;
let dir = dbfile.to_string() + "-blobs";
dc_create_folder(context, &dir);
*context.blobdir.write().unwrap() = dir.strdup();
}
// Create/open sqlite database, this may already use the blobdir
let dbfile_path = std::path::Path::new(dbfile);
@@ -343,7 +333,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
let l = dc_loginparam_read(context, &context.sql, "");
let l2 = dc_loginparam_read(context, &context.sql, "configured_");
let displayname = context.sql.get_config(context, "displayname");
let chats = dc_get_chat_cnt(context) as usize;
let chats = get_chat_cnt(context) as usize;
let real_msgs = dc_get_real_msg_cnt(context) as usize;
let deaddrop_msgs = dc_get_deaddrop_msg_cnt(context) as usize;
let contacts = Contact::get_real_cnt(context) as usize;
@@ -445,7 +435,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
public_key_count={}\n\
fingerprint={}\n\
level=awesome\n",
as_str(DC_VERSION_STR as *const u8 as *const _),
&*DC_VERSION_STR,
rusqlite::version(),
sqlite3_threadsafe(),
// arch
@@ -454,11 +444,10 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
real_msgs,
deaddrop_msgs,
contacts,
if context.has_dbfile() {
as_str(context.get_dbfile())
} else {
unset
},
context
.get_dbfile()
.as_ref()
.map_or(unset, |p| p.to_str().unwrap()),
dbversion,
if context.has_blobdir() {
as_str(context.get_blobdir())
@@ -487,10 +476,10 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
}
pub unsafe fn dc_get_version_str() -> *mut libc::c_char {
dc_strdup(DC_VERSION_STR as *const u8 as *const libc::c_char)
(&*DC_VERSION_STR).strdup()
}
pub fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t {
pub fn dc_get_fresh_msgs(context: &Context) -> Vec<u32> {
let show_deaddrop = 0;
context
@@ -505,13 +494,12 @@ pub fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t {
&[10, 9, if 0 != show_deaddrop { 2 } else { 0 }],
|row| row.get(0),
|rows| {
let mut ret = dc_array_t::new(128);
let mut ret = Vec::new();
for row in rows {
let id = row?;
ret.add_id(id);
let id: u32 = row?;
ret.push(id);
}
Ok(ret.into_raw())
Ok(ret)
},
)
.unwrap()
@@ -522,14 +510,14 @@ pub fn dc_search_msgs(
context: &Context,
chat_id: uint32_t,
query: *const libc::c_char,
) -> *mut dc_array_t {
) -> Vec<u32> {
if query.is_null() {
return std::ptr::null_mut();
return Vec::new();
}
let real_query = to_string(query).trim().to_string();
if real_query.is_empty() {
return std::ptr::null_mut();
return Vec::new();
}
let strLikeInText = format!("%{}%", &real_query);
let strLikeBeg = format!("{}%", &real_query);
@@ -545,28 +533,21 @@ pub fn dc_search_msgs(
AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;"
};
let mut ret = dc_array_t::new(100);
let success = context
context
.sql
.query_map(
query,
params![chat_id as libc::c_int, &strLikeInText, &strLikeBeg],
|row| row.get::<_, i32>(0),
|rows| {
let mut ret = Vec::new();
for id in rows {
ret.add_id(id? as u32);
ret.push(id? as u32);
}
Ok(())
Ok(ret)
},
)
.is_ok();
if success {
return ret.into_raw();
}
std::ptr::null_mut()
.unwrap_or_default()
}
pub fn dc_is_inbox(_context: &Context, folder_name: impl AsRef<str>) -> bool {
@@ -591,3 +572,24 @@ pub fn dc_is_mvbox(context: &Context, folder_name: impl AsRef<str>) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_crashes_on_context_deref() {
let ctx = dc_context_new(None, std::ptr::null_mut(), Some("Test OS".into()));
std::mem::drop(ctx);
}
#[test]
fn test_context_double_close() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
unsafe {
dc_close(&ctx);
dc_close(&ctx);
}
std::mem::drop(ctx);
}
}

View File

@@ -7,7 +7,7 @@ use crate::types::*;
#[allow(non_camel_case_types)]
pub enum dc_array_t {
Locations(Vec<dc_location>),
Uint(Vec<uintptr_t>),
Uint(Vec<u32>),
}
impl dc_array_t {
@@ -24,18 +24,14 @@ impl dc_array_t {
Box::into_raw(Box::new(self))
}
pub fn add_uint(&mut self, item: uintptr_t) {
pub fn add_id(&mut self, item: uint32_t) {
if let Self::Uint(array) = self {
array.push(item);
} else {
panic!("Attempt to add uint to array of other type");
panic!("Attempt to add id to array of other type");
}
}
pub fn add_id(&mut self, item: uint32_t) {
self.add_uint(item as uintptr_t);
}
pub fn add_location(&mut self, location: dc_location) {
if let Self::Locations(array) = self {
array.push(location)
@@ -44,14 +40,6 @@ impl dc_array_t {
}
}
pub fn get_uint(&self, index: usize) -> uintptr_t {
if let Self::Uint(array) = self {
array[index]
} else {
panic!("Attempt to get uint from array of other type");
}
}
pub fn get_id(&self, index: usize) -> uint32_t {
match self {
Self::Locations(array) => array[index].location_id,
@@ -59,14 +47,6 @@ impl dc_array_t {
}
}
pub fn get_ptr(&self, index: size_t) -> *mut libc::c_void {
if let Self::Uint(array) = self {
array[index] as *mut libc::c_void
} else {
panic!("Not an array of pointers");
}
}
pub fn get_location(&self, index: usize) -> &dc_location {
if let Self::Locations(array) = self {
&array[index]
@@ -125,7 +105,7 @@ impl dc_array_t {
}
}
pub fn search_id(&self, needle: uintptr_t) -> Option<usize> {
pub fn search_id(&self, needle: u32) -> Option<usize> {
if let Self::Uint(array) = self {
for (i, &u) in array.iter().enumerate() {
if u == needle {
@@ -147,6 +127,12 @@ impl dc_array_t {
}
}
impl From<Vec<u32>> for dc_array_t {
fn from(array: Vec<u32>) -> Self {
dc_array_t::Uint(array)
}
}
impl From<Vec<dc_location>> for dc_array_t {
fn from(array: Vec<dc_location>) -> Self {
dc_array_t::Locations(array)
@@ -154,120 +140,27 @@ impl From<Vec<dc_location>> for dc_array_t {
}
pub unsafe fn dc_array_unref(array: *mut dc_array_t) {
if array.is_null() {
return;
}
assert!(!array.is_null());
Box::from_raw(array);
}
pub unsafe fn dc_array_add_uint(array: *mut dc_array_t, item: uintptr_t) {
if !array.is_null() {
(*array).add_uint(item);
}
}
pub unsafe fn dc_array_add_id(array: *mut dc_array_t, item: uint32_t) {
if !array.is_null() {
(*array).add_id(item);
}
}
pub unsafe fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void) {
dc_array_add_uint(array, item as uintptr_t);
assert!(!array.is_null());
(*array).add_id(item);
}
pub unsafe fn dc_array_get_cnt(array: *const dc_array_t) -> size_t {
if array.is_null() {
0
} else {
(*array).len()
}
}
pub unsafe fn dc_array_get_uint(array: *const dc_array_t, index: size_t) -> uintptr_t {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_uint(index)
}
assert!(!array.is_null());
(*array).len()
}
pub unsafe fn dc_array_get_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_id(index)
}
}
pub unsafe fn dc_array_get_ptr(array: *const dc_array_t, index: size_t) -> *mut libc::c_void {
if array.is_null() || index >= (*array).len() {
std::ptr::null_mut()
} else {
(*array).get_ptr(index)
}
}
pub unsafe fn dc_array_get_latitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null() || index >= (*array).len() {
0.0
} else {
(*array).get_latitude(index)
}
}
pub unsafe fn dc_array_get_longitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null() || index >= (*array).len() {
0.0
} else {
(*array).get_longitude(index)
}
}
pub unsafe fn dc_array_get_accuracy(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null() || index >= (*array).len() {
0.0
} else {
(*array).get_accuracy(index)
}
}
pub unsafe fn dc_array_get_timestamp(array: *const dc_array_t, index: size_t) -> i64 {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_timestamp(index)
}
}
pub unsafe fn dc_array_get_chat_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_chat_id(index)
}
}
pub unsafe fn dc_array_get_contact_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_contact_id(index)
}
}
pub unsafe fn dc_array_get_msg_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_msg_id(index)
}
assert!(!array.is_null());
(*array).get_id(index)
}
pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *mut libc::c_char {
if array.is_null() || index >= (*array).len() {
return std::ptr::null_mut();
}
assert!(!array.is_null());
if let dc_array_t::Locations(v) = &*array {
if let Some(s) = &v[index].marker {
@@ -276,7 +169,7 @@ pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *m
std::ptr::null_mut()
}
} else {
std::ptr::null_mut()
panic!("Not an array of locations");
}
}
@@ -291,9 +184,7 @@ pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *m
* 1=Location was reported independently.
*/
pub unsafe fn dc_array_is_independent(array: *const dc_array_t, index: size_t) -> libc::c_int {
if array.is_null() || index >= (*array).len() {
return 0;
}
assert!(!array.is_null());
if let dc_array_t::Locations(v) = &*array {
v[index].independent as libc::c_int
@@ -307,10 +198,9 @@ pub unsafe fn dc_array_search_id(
needle: uint32_t,
ret_index: *mut size_t,
) -> bool {
if array.is_null() {
return false;
}
if let Some(i) = (*array).search_id(needle as uintptr_t) {
assert!(!array.is_null());
if let Some(i) = (*array).search_id(needle) {
if !ret_index.is_null() {
*ret_index = i
}
@@ -320,10 +210,9 @@ pub unsafe fn dc_array_search_id(
}
}
pub unsafe fn dc_array_get_raw(array: *const dc_array_t) -> *const uintptr_t {
if array.is_null() {
return 0 as *const uintptr_t;
}
pub unsafe fn dc_array_get_raw(array: *const dc_array_t) -> *const u32 {
assert!(!array.is_null());
if let dc_array_t::Uint(v) = &*array {
v.as_ptr()
} else {
@@ -335,57 +224,22 @@ pub fn dc_array_new(initsize: size_t) -> *mut dc_array_t {
dc_array_t::new(initsize).into_raw()
}
pub fn dc_array_new_locations(initsize: size_t) -> *mut dc_array_t {
dc_array_t::new_locations(initsize).into_raw()
}
#[cfg(test)]
unsafe fn dc_array_empty(array: *mut dc_array_t) {
assert!(!array.is_null());
pub unsafe fn dc_array_empty(array: *mut dc_array_t) {
if array.is_null() {
return;
}
(*array).clear()
}
pub unsafe fn dc_array_duplicate(array: *const dc_array_t) -> *mut dc_array_t {
if array.is_null() {
std::ptr::null_mut()
} else {
(*array).clone().into_raw()
}
}
assert!(!array.is_null());
pub unsafe fn dc_array_get_string(
array: *const dc_array_t,
sep: *const libc::c_char,
) -> *mut libc::c_char {
if array.is_null() || sep.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
if let dc_array_t::Uint(v) = &*array {
let cnt = v.len();
let sep = as_str(sep);
let res = v
.iter()
.enumerate()
.fold(String::with_capacity(2 * cnt), |res, (i, n)| {
if i == 0 {
res + &n.to_string()
} else {
res + sep + &n.to_string()
}
});
res.strdup()
} else {
panic!("Attempt to get string from array of other type");
}
(*array).clone().into_raw()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::x::*;
use std::ffi::CStr;
#[test]
fn test_dc_array() {
@@ -406,10 +260,6 @@ mod tests {
);
}
assert_eq!(dc_array_get_id(arr, -1i32 as size_t), 0);
assert_eq!(dc_array_get_id(arr, 1000 as size_t), 0);
assert_eq!(dc_array_get_id(arr, 1001 as size_t), 0);
dc_array_empty(arr);
assert_eq!(dc_array_get_cnt(arr), 0);
@@ -427,15 +277,17 @@ mod tests {
assert_eq!(dc_array_get_id(arr, 2 as size_t), 13);
assert_eq!(dc_array_get_id(arr, 3 as size_t), 666);
let str = dc_array_get_string(arr, b"-\x00" as *const u8 as *const libc::c_char);
assert_eq!(
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
"0-7-13-666-5000"
);
free(str as *mut libc::c_void);
dc_array_unref(arr);
}
}
#[test]
#[should_panic]
fn test_dc_array_out_of_bounds() {
let arr = dc_array_new(7);
for i in 0..1000 {
unsafe { dc_array_add_id(arr, (i + 2) as uint32_t) };
}
unsafe { dc_array_get_id(arr, 1000) };
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
use lazy_static::lazy_static;
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::dc_saxparser::*;
use crate::dc_tools::*;
use crate::x::*;
@@ -34,56 +35,67 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char
add_text: AddText::YesRemoveLineEnds,
last_href: None,
};
let mut saxparser = dc_saxparser_t {
starttag_cb: None,
endtag_cb: None,
text_cb: None,
userdata: 0 as *mut libc::c_void,
};
dc_saxparser_init(
&mut saxparser,
&mut dehtml as *mut Dehtml as *mut libc::c_void,
);
dc_saxparser_set_tag_handler(
&mut saxparser,
Some(dehtml_starttag_cb),
Some(dehtml_endtag_cb),
);
dc_saxparser_set_text_handler(&mut saxparser, Some(dehtml_text_cb));
dc_saxparser_parse(&mut saxparser, buf_terminated);
let mut reader = quick_xml::Reader::from_str(as_str(buf_terminated));
let mut buf = Vec::new();
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
dehtml_starttag_cb(e, &mut dehtml, &reader)
}
Ok(quick_xml::events::Event::End(ref e)) => dehtml_endtag_cb(e, &mut dehtml),
Ok(quick_xml::events::Event::Text(ref e)) => dehtml_text_cb(e, &mut dehtml),
Ok(quick_xml::events::Event::CData(ref e)) => dehtml_cdata_cb(e, &mut dehtml),
Err(e) => {
eprintln!(
"Parse html error: Error at position {}: {:?}",
reader.buffer_position(),
e
);
}
Ok(quick_xml::events::Event::Eof) => break,
_ => (),
}
buf.clear();
}
dehtml.strbuilder.strdup()
}
unsafe fn dehtml_text_cb(
userdata: *mut libc::c_void,
text: *const libc::c_char,
_len: libc::c_int,
) {
let dehtml = &mut *(userdata as *mut Dehtml);
fn dehtml_text_cb(event: &BytesText, dehtml: &mut Dehtml) {
if dehtml.add_text == AddText::YesPreserveLineEnds
|| dehtml.add_text == AddText::YesRemoveLineEnds
{
let last_added = std::ffi::CStr::from_ptr(text)
.to_str()
.expect("invalid utf8");
// TODO: why does len does not match?
// assert_eq!(last_added.len(), len as usize);
let last_added = escaper::decode_html_buf_sloppy(event.escaped()).unwrap_or_default();
if dehtml.add_text == AddText::YesRemoveLineEnds {
dehtml.strbuilder += LINE_RE.replace_all(last_added.as_ref(), "\r").as_ref();
dehtml.strbuilder += LINE_RE.replace_all(&last_added, "\r").as_ref();
} else {
dehtml.strbuilder += last_added.as_ref();
dehtml.strbuilder += &last_added;
}
}
}
unsafe fn dehtml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
let mut dehtml = &mut *(userdata as *mut Dehtml);
let tag = std::ffi::CStr::from_ptr(tag).to_string_lossy();
fn dehtml_cdata_cb(event: &BytesText, dehtml: &mut Dehtml) {
if dehtml.add_text == AddText::YesPreserveLineEnds
|| dehtml.add_text == AddText::YesRemoveLineEnds
{
let last_added = escaper::decode_html_buf_sloppy(event.escaped()).unwrap_or_default();
match tag.as_ref() {
if dehtml.add_text == AddText::YesRemoveLineEnds {
dehtml.strbuilder += LINE_RE.replace_all(&last_added, "\r").as_ref();
} else {
dehtml.strbuilder += &last_added;
}
}
}
fn dehtml_endtag_cb(event: &BytesEnd, dehtml: &mut Dehtml) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
match tag.as_str() {
"p" | "div" | "table" | "td" | "style" | "script" | "title" | "pre" => {
dehtml.strbuilder += "\n\n";
dehtml.add_text = AddText::YesRemoveLineEnds;
@@ -105,15 +117,14 @@ unsafe fn dehtml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char
}
}
unsafe fn dehtml_starttag_cb(
userdata: *mut libc::c_void,
tag: *const libc::c_char,
attr: *mut *mut libc::c_char,
fn dehtml_starttag_cb<B: std::io::BufRead>(
event: &BytesStart,
dehtml: &mut Dehtml,
reader: &quick_xml::Reader<B>,
) {
let mut dehtml = &mut *(userdata as *mut Dehtml);
let tag = std::ffi::CStr::from_ptr(tag).to_string_lossy();
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
match tag.as_ref() {
match tag.as_str() {
"p" | "div" | "table" | "td" => {
dehtml.strbuilder += "\n\n";
dehtml.add_text = AddText::YesRemoveLineEnds;
@@ -130,14 +141,21 @@ unsafe fn dehtml_starttag_cb(
dehtml.add_text = AddText::YesPreserveLineEnds;
}
"a" => {
let text_c = std::ffi::CStr::from_ptr(dc_attr_find(
attr,
b"href\x00" as *const u8 as *const libc::c_char,
));
let text_r = text_c.to_str().expect("invalid utf8");
if !text_r.is_empty() {
dehtml.last_href = Some(text_r.to_string());
dehtml.strbuilder += "[";
if let Some(href) = event.html_attributes().find(|attr| {
attr.as_ref()
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "href")
.unwrap_or_default()
}) {
let href = href
.unwrap()
.unescape_and_decode_value(reader)
.unwrap_or_default()
.to_lowercase();
if !href.is_empty() {
dehtml.last_href = Some(href);
dehtml.strbuilder += "[";
}
}
}
"b" | "strong" => {

View File

@@ -1,5 +1,8 @@
//! End-to-end encryption support.
use std::collections::HashSet;
use std::ffi::CStr;
use std::ptr;
use std::str::FromStr;
use mmime::clist::*;
@@ -16,10 +19,12 @@ use mmime::mmapstring::*;
use mmime::{mailmime_substitute, MAILIMF_NO_ERROR, MAIL_NO_ERROR};
use crate::aheader::*;
use crate::config::Config;
use crate::context::Context;
use crate::dc_mimeparser::*;
use crate::dc_securejoin::*;
use crate::dc_tools::*;
use crate::error::*;
use crate::key::*;
use crate::keyring::*;
use crate::peerstate::*;
@@ -76,11 +81,6 @@ pub unsafe fn dc_e2ee_encrypt(
let mut peerstates: Vec<Peerstate> = Vec::new();
*helper = Default::default();
info!(
context,
0, "dc_e2ee_encrypt guaruanteed={}", e2ee_guaranteed
);
if !(recipients_addr.is_null()
|| in_out_message.is_null()
|| !(*in_out_message).mm_parent.is_null()
@@ -101,9 +101,11 @@ pub unsafe fn dc_e2ee_encrypt(
let addr = context.sql.get_config(context, "configured_addr");
if let Some(addr) = addr {
if let Some(public_key) =
load_or_generate_self_public_key(context, &addr, in_out_message)
{
let pubkey_ret = load_or_generate_self_public_key(context, &addr).map_err(|err| {
error!(context, 0, "Failed to load public key: {}", err);
err
});
if let Ok(public_key) = pubkey_ret {
/*only for random-seed*/
if prefer_encrypt == EncryptPreference::Mutual || 0 != e2ee_guaranteed {
do_encrypt = 1i32;
@@ -111,10 +113,6 @@ pub unsafe fn dc_e2ee_encrypt(
iter1 = (*recipients_addr).first;
while !iter1.is_null() {
let recipient_addr = to_string((*iter1).data as *const libc::c_char);
info!(
context,
0, "dc_e2ee_encrypt recipient_addr {}", recipient_addr
);
if recipient_addr != addr {
let peerstate =
Peerstate::from_addr(context, &context.sql, &recipient_addr);
@@ -148,7 +146,7 @@ pub unsafe fn dc_e2ee_encrypt(
iter1 = if !iter1.is_null() {
(*iter1).next
} else {
0 as *mut clistcell
ptr::null_mut()
}
}
}
@@ -173,19 +171,19 @@ pub unsafe fn dc_e2ee_encrypt(
mailprivacy_prepare_mime(in_out_message);
let mut part_to_encrypt: *mut mailmime =
(*in_out_message).mm_data.mm_message.mm_msg_mime;
(*part_to_encrypt).mm_parent = 0 as *mut mailmime;
(*part_to_encrypt).mm_parent = ptr::null_mut();
let imffields_encrypted: *mut mailimf_fields = mailimf_fields_new_empty();
/* mailmime_new_message_data() calls mailmime_fields_new_with_version() which would add the unwanted MIME-Version:-header */
let message_to_encrypt: *mut mailmime = mailmime_new(
MAILMIME_MESSAGE as libc::c_int,
0 as *const libc::c_char,
ptr::null(),
0i32 as size_t,
mailmime_fields_new_empty(),
mailmime_get_content_message(),
0 as *mut mailmime_data,
0 as *mut mailmime_data,
0 as *mut mailmime_data,
0 as *mut clist,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
imffields_encrypted,
part_to_encrypt,
);
@@ -217,7 +215,7 @@ pub unsafe fn dc_e2ee_encrypt(
let field: *mut mailimf_field = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
ptr::null_mut()
})
as *mut mailimf_field;
if !field.is_null() {
@@ -268,28 +266,28 @@ pub unsafe fn dc_e2ee_encrypt(
imffields_unprotected,
mailimf_field_new(
MAILIMF_FIELD_SUBJECT 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,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
subject,
0 as *mut mailimf_comments,
0 as *mut mailimf_keywords,
0 as *mut mailimf_optional_field,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
),
);
clist_insert_after(
@@ -317,7 +315,7 @@ pub unsafe fn dc_e2ee_encrypt(
/* create MIME-structure that will contain the encrypted text */
let mut encrypted_part: *mut mailmime = new_data_part(
0 as *mut libc::c_void,
ptr::null_mut(),
0i32 as size_t,
b"multipart/encrypted\x00" as *const u8 as *const libc::c_char
as *mut libc::c_char,
@@ -403,7 +401,7 @@ unsafe fn new_data_part(
let encoding_type: libc::c_int;
let content_type_str: *mut libc::c_char;
let mut do_encoding: libc::c_int;
encoding = 0 as *mut mailmime_mechanism;
encoding = ptr::null_mut();
if default_content_type.is_null() {
content_type_str =
b"application/octet-stream\x00" as *const u8 as *const libc::c_char as *mut libc::c_char
@@ -438,7 +436,7 @@ unsafe fn new_data_part(
} else {
encoding_type = default_encoding
}
encoding = mailmime_mechanism_new(encoding_type, 0 as *mut libc::c_char);
encoding = mailmime_mechanism_new(encoding_type, ptr::null_mut());
if encoding.is_null() {
ok_to_continue = false;
}
@@ -446,10 +444,10 @@ unsafe fn new_data_part(
if ok_to_continue {
mime_fields = mailmime_fields_new_with_data(
encoding,
0 as *mut libc::c_char,
0 as *mut libc::c_char,
0 as *mut mailmime_disposition,
0 as *mut mailmime_language,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
);
if mime_fields.is_null() {
ok_to_continue = false;
@@ -479,68 +477,55 @@ unsafe fn new_data_part(
mailmime_content_free(content);
}
}
return 0 as *mut mailmime;
ptr::null_mut()
}
/*******************************************************************************
* Generate Keypairs
******************************************************************************/
unsafe fn load_or_generate_self_public_key(
context: &Context,
self_addr: impl AsRef<str>,
_random_data_mime: *mut mailmime,
) -> Option<Key> {
/* avoid double creation (we unlock the database during creation) */
static mut S_IN_KEY_CREATION: libc::c_int = 0;
/// Load public key from database or generate a new one.
///
/// This will load a public key from the database, generating and
/// storing a new one when one doesn't exist yet. Care is taken to
/// only generate one key per context even when multiple threads call
/// this function concurrently.
fn load_or_generate_self_public_key(context: &Context, self_addr: impl AsRef<str>) -> Result<Key> {
if let Some(key) = Key::from_self_public(context, &self_addr, &context.sql) {
return Ok(key);
}
let _guard = context.generating_key_mutex.lock().unwrap();
let mut key = Key::from_self_public(context, &self_addr, &context.sql);
if key.is_some() {
return key;
// Check again in case the key was generated while we were waiting for the lock.
if let Some(key) = Key::from_self_public(context, &self_addr, &context.sql) {
return Ok(key);
}
/* create the keypair - this may take a moment, however, as this is in a thread, this is no big deal */
if 0 != S_IN_KEY_CREATION {
return None;
}
let key_creation_here = 1;
S_IN_KEY_CREATION = 1;
let start = clock();
let start = std::time::Instant::now();
info!(
context,
0, "Generating keypair with {} bits, e={} ...", 2048, 65537,
);
if let Some((public_key, private_key)) = dc_pgp_create_keypair(&self_addr) {
if !dc_key_save_self_keypair(
context,
&public_key,
&private_key,
&self_addr,
1i32,
&context.sql,
) {
/*set default*/
warn!(context, 0, "Cannot save keypair.",);
} else {
info!(
match dc_pgp_create_keypair(&self_addr) {
Some((public_key, private_key)) => {
match dc_key_save_self_keypair(
context,
0,
"Keypair generated in {:.3}s.",
clock().wrapping_sub(start) as libc::c_double / 1000000 as libc::c_double,
);
&public_key,
&private_key,
&self_addr,
1,
&context.sql,
) {
true => {
info!(
context,
0,
"Keypair generated in {:.3}s.",
start.elapsed().as_secs()
);
Ok(public_key)
}
false => Err(format_err!("Failed to save keypair")),
}
}
key = Some(public_key);
} else {
warn!(context, 0, "Cannot create keypair.");
None => Err(format_err!("Failed to generate keypair")),
}
if 0 != key_creation_here {
S_IN_KEY_CREATION = 0;
}
key
}
/* returns 1 if sth. was decrypted, 0 in other cases */
@@ -555,25 +540,23 @@ pub unsafe fn dc_e2ee_decrypt(
/*just a pointer into mailmime structure, must not be freed*/
let imffields: *mut mailimf_fields = mailmime_find_mailimf_fields(in_out_message);
let mut message_time = 0;
let mut from: *mut libc::c_char = 0 as *mut libc::c_char;
let mut from: *mut libc::c_char = ptr::null_mut();
let mut private_keyring = Keyring::default();
let mut public_keyring_for_validate = Keyring::default();
let mut gossip_headers: *mut mailimf_fields = 0 as *mut mailimf_fields;
let mut gossip_headers: *mut mailimf_fields = ptr::null_mut();
if !(in_out_message.is_null() || imffields.is_null()) {
if !imffields.is_null() {
let mut field: *mut mailimf_field =
mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
if !field.is_null() && !(*field).fld_data.fld_from.is_null() {
from = mailimf_find_first_addr((*(*field).fld_data.fld_from).frm_mb_list)
}
field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int);
if !field.is_null() && !(*field).fld_data.fld_orig_date.is_null() {
let orig_date: *mut mailimf_orig_date = (*field).fld_data.fld_orig_date;
if !orig_date.is_null() {
message_time = dc_timestamp_from_date((*orig_date).dt_date_time);
if message_time != 0 && message_time > time() {
message_time = time()
}
let mut field: *mut mailimf_field =
mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
if !field.is_null() && !(*field).fld_data.fld_from.is_null() {
from = mailimf_find_first_addr((*(*field).fld_data.fld_from).frm_mb_list)
}
field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int);
if !field.is_null() && !(*field).fld_data.fld_orig_date.is_null() {
let orig_date: *mut mailimf_orig_date = (*field).fld_data.fld_orig_date;
if !orig_date.is_null() {
message_time = dc_timestamp_from_date((*orig_date).dt_date_time);
if message_time != 0 && message_time > time() {
message_time = time()
}
}
}
@@ -594,7 +577,6 @@ pub unsafe fn dc_e2ee_decrypt(
}
} else if let Some(ref header) = autocryptheader {
let p = Peerstate::from_header(context, header, message_time);
info!(context, 0, "setting peerstate from header for {:?}", p.addr);
assert!(p.save_to_db(&context.sql, true));
peerstate = Some(p);
}
@@ -665,7 +647,7 @@ unsafe fn update_gossip_peerstates(
let field: *mut mailimf_field = (if !cur1.is_null() {
(*cur1).data
} else {
0 as *mut libc::c_void
ptr::null_mut()
}) as *mut mailimf_field;
if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
let optional_field: *const mailimf_optional_field =
@@ -717,7 +699,7 @@ unsafe fn update_gossip_peerstates(
cur1 = if !cur1.is_null() {
(*cur1).next
} else {
0 as *mut clistcell
ptr::null_mut()
}
}
@@ -750,7 +732,7 @@ unsafe fn decrypt_recursive(
{
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
while !cur.is_null() {
let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime;
let mut decrypted_mime: *mut mailmime = ptr::null_mut();
if 0 != decrypt_part(
context,
(if !cur.is_null() {
@@ -765,7 +747,7 @@ unsafe fn decrypt_recursive(
) {
if (*ret_gossip_headers).is_null() && ret_valid_signatures.len() > 0 {
let mut dummy: size_t = 0i32 as size_t;
let mut test: *mut mailimf_fields = 0 as *mut mailimf_fields;
let mut test: *mut mailimf_fields = ptr::null_mut();
if mailimf_envelope_and_optional_fields_parse(
(*decrypted_mime).mm_mime_start,
(*decrypted_mime).mm_length,
@@ -796,7 +778,7 @@ unsafe fn decrypt_recursive(
(if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
ptr::null_mut()
}) as *mut mailmime,
private_keyring,
public_keyring_for_validate,
@@ -809,7 +791,7 @@ unsafe fn decrypt_recursive(
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
ptr::null_mut()
}
}
}
@@ -844,12 +826,12 @@ unsafe fn decrypt_part(
let mime_data: *mut mailmime_data;
let mut mime_transfer_encoding: libc::c_int = MAILMIME_MECHANISM_BINARY as libc::c_int;
/* mmap_string_unref()'d if set */
let mut transfer_decoding_buffer: *mut libc::c_char = 0 as *mut libc::c_char;
let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut();
/* must not be free()'d */
let mut decoded_data: *const libc::c_char = 0 as *const libc::c_char;
let mut decoded_data: *const libc::c_char = ptr::null_mut();
let mut decoded_data_bytes: size_t = 0i32 as size_t;
let mut sth_decrypted: libc::c_int = 0i32;
*ret_decrypted_mime = 0 as *mut mailmime;
*ret_decrypted_mime = ptr::null_mut();
mime_data = (*mime).mm_data.mm_single;
/* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */
if !((*mime_data).dt_type != MAILMIME_DATA_TEXT as libc::c_int
@@ -863,7 +845,7 @@ unsafe fn decrypt_part(
let field: *mut mailmime_field = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
ptr::null_mut()
}) as *mut mailmime_field;
if !field.is_null() {
if (*field).fld_type == MAILMIME_FIELD_TRANSFER_ENCODING as libc::c_int
@@ -875,7 +857,7 @@ unsafe fn decrypt_part(
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
ptr::null_mut()
}
}
}
@@ -931,7 +913,7 @@ unsafe fn decrypt_part(
let plain_buf = plain.as_ptr() as *const libc::c_char;
let mut index: size_t = 0i32 as size_t;
let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime;
let mut decrypted_mime: *mut mailmime = ptr::null_mut();
if mailmime_parse(
plain_buf as *const _,
plain_bytes,
@@ -1029,7 +1011,7 @@ unsafe fn contains_report(mime: *mut mailmime) -> libc::c_int {
(if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
ptr::null_mut()
}) as *mut mailmime,
) {
return 1i32;
@@ -1052,36 +1034,52 @@ unsafe fn contains_report(mime: *mut mailmime) -> libc::c_int {
/* frees data referenced by "mailmime" but not freed by mailmime_free(). After calling this function, in_out_message cannot be used any longer! */
pub unsafe fn dc_e2ee_thanks(helper: &mut dc_e2ee_helper_t) {
free(helper.cdata_to_free);
helper.cdata_to_free = 0 as *mut libc::c_void;
helper.cdata_to_free = ptr::null_mut();
}
/* makes sure, the private key exists, needed only for exporting keys and the case no message was sent before */
// TODO should return bool /rtn
pub unsafe fn dc_ensure_secret_key_exists(context: &Context) -> libc::c_int {
/* normally, the key is generated as soon as the first mail is send
(this is to gain some extra-random-seed by the message content and the timespan between program start and message sending) */
let mut success: libc::c_int = 0i32;
let self_addr = context.sql.get_config(context, "configured_addr");
if self_addr.is_none() {
warn!(
context,
0, "Cannot ensure secret key if context is not configured.",
);
} else if load_or_generate_self_public_key(context, self_addr.unwrap(), 0 as *mut mailmime)
.is_some()
{
/*no random text data for seeding available*/
success = 1;
}
success
/// Ensures a private key exists for the configured user.
///
/// Normally the private key is generated when the first message is
/// sent but in a few locations there are no such guarantees,
/// e.g. when exporting keys, and calling this function ensures a
/// private key will be present.
///
/// If this succeeds you are also guaranteed that the
/// [Config::ConfiguredAddr] is configured, this address is returned.
pub fn dc_ensure_secret_key_exists(context: &Context) -> Result<String> {
let self_addr = context
.get_config(Config::ConfiguredAddr)
.ok_or(format_err!(concat!(
"Failed to get self address, ",
"cannot ensure secret key if not configured."
)))?;
load_or_generate_self_public_key(context, &self_addr)?;
Ok(self_addr)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::*;
mod ensure_secret_key_exists {
use super::*;
#[test]
fn test_prexisting() {
let t = dummy_context();
let test_addr = configure_alice_keypair(&t.ctx);
assert_eq!(dc_ensure_secret_key_exists(&t.ctx).unwrap(), test_addr);
}
#[test]
fn test_not_configured() {
let t = dummy_context();
assert!(dc_ensure_secret_key_exists(&t.ctx).is_err());
}
}
#[test]
fn test_mailmime_parse() {
let plain = b"Chat-Disposition-Notification-To: holger@deltachat.de
@@ -1142,4 +1140,47 @@ Sent with my Delta Chat Messenger: https://delta.chat";
unsafe { free(decrypted_mime as *mut _) };
}
mod load_or_generate_self_public_key {
use super::*;
#[test]
fn test_existing() {
let t = dummy_context();
let addr = configure_alice_keypair(&t.ctx);
let key = load_or_generate_self_public_key(&t.ctx, addr);
assert!(key.is_ok());
}
#[test]
#[ignore] // generating keys is expensive
fn test_generate() {
let t = dummy_context();
let addr = "alice@example.org";
let key0 = load_or_generate_self_public_key(&t.ctx, addr);
assert!(key0.is_ok());
let key1 = load_or_generate_self_public_key(&t.ctx, addr);
assert!(key1.is_ok());
assert_eq!(key0.unwrap(), key1.unwrap());
}
#[test]
#[ignore]
fn test_generate_concurrent() {
use std::sync::Arc;
use std::thread;
let t = dummy_context();
let ctx = Arc::new(t.ctx);
let ctx0 = Arc::clone(&ctx);
let thr0 =
thread::spawn(move || load_or_generate_self_public_key(&ctx0, "alice@example.org"));
let ctx1 = Arc::clone(&ctx);
let thr1 =
thread::spawn(move || load_or_generate_self_public_key(&ctx1, "alice@example.org"));
let res0 = thr0.join().unwrap();
let res1 = thr1.join().unwrap();
assert_eq!(res0.unwrap(), res1.unwrap());
}
}
}

View File

@@ -1,20 +1,21 @@
use std::ffi::CString;
use std::ptr;
use mmime::mailmime_content::*;
use mmime::mmapstring::*;
use mmime::other::*;
use rand::{thread_rng, Rng};
use crate::chat;
use crate::config::Config;
use crate::constants::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_configure::*;
use crate::dc_e2ee::*;
use crate::dc_job::*;
use crate::dc_msg::*;
use crate::dc_tools::*;
use crate::error::*;
use crate::job::*;
use crate::key::*;
use crate::param::*;
use crate::pgp::*;
@@ -43,8 +44,8 @@ pub unsafe fn dc_imex(
param.set(Param::Arg2, as_str(param2));
}
dc_job_kill_action(context, 910);
dc_job_add(context, 910, 0, param, 0);
job_kill_action(context, Action::ImexImap);
job_add(context, Action::ImexImap, 0, param, 0);
}
/// Returns the filename of the backup if found, nullptr otherwise.
@@ -61,7 +62,7 @@ pub unsafe fn dc_imex_has_backup(
"Backup check: Cannot open directory \"{}\".\x00",
dir_name.display(),
);
return 0 as *mut libc::c_char;
return ptr::null_mut();
}
let mut newest_backup_time = 0;
let mut newest_backup_path: Option<std::path::PathBuf> = None;
@@ -100,8 +101,8 @@ pub unsafe fn dc_imex_has_backup(
}
pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
let mut setup_file_name: *mut libc::c_char = 0 as *mut libc::c_char;
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
let mut setup_file_name: *mut libc::c_char = ptr::null_mut();
let mut msg: *mut dc_msg_t = ptr::null_mut();
if dc_alloc_ongoing(context) == 0 {
return std::ptr::null_mut();
}
@@ -137,8 +138,7 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
setup_file_content_c.as_bytes().len(),
))
{
let chat_id = dc_create_chat_by_contact_id(context, 1i32 as uint32_t);
if !(chat_id == 0i32 as libc::c_uint) {
if let Ok(chat_id) = chat::create_by_contact_id(context, 1) {
msg = dc_msg_new_untyped(context);
(*msg).type_0 = Viewtype::File;
(*msg).param.set(Param::File, as_str(setup_file_name));
@@ -156,10 +156,9 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
.unwrap()
.shall_stop_ongoing
{
let msg_id = dc_send_msg(context, chat_id, msg);
if msg_id != 0 {
if let Ok(msg_id) = chat::send_msg(context, chat_id, msg) {
dc_msg_unref(msg);
msg = 0 as *mut dc_msg_t;
msg = ptr::null_mut();
info!(context, 0, "Wait for setup message being sent ...",);
loop {
if context
@@ -194,20 +193,15 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
setup_code.strdup()
}
/// Renders HTML body of a setup file message.
///
/// The `passphrase` must be at least 2 characters long.
pub fn dc_render_setup_file(context: &Context, passphrase: &str) -> Result<String> {
ensure!(
passphrase.len() >= 2,
"Passphrase must be at least 2 chars long."
);
unsafe {
ensure!(
!(dc_ensure_secret_key_exists(context) == 0),
"No secret key available."
);
}
let self_addr = context
.get_config(Config::ConfiguredAddr)
.ok_or(format_err!("Failed to get self address."))?;
let self_addr = dc_ensure_secret_key_exists(context)?;
let private_key = Key::from_self_private(context, self_addr, &context.sql)
.ok_or(format_err!("Failed to get private key."))?;
let ac_headers = match context
@@ -290,12 +284,12 @@ pub unsafe fn dc_continue_key_transfer(
setup_code: *const libc::c_char,
) -> libc::c_int {
let mut success: libc::c_int = 0i32;
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
let mut filename: *mut libc::c_char = 0 as *mut libc::c_char;
let mut filecontent: *mut libc::c_char = 0 as *mut libc::c_char;
let mut msg: *mut dc_msg_t = ptr::null_mut();
let mut filename: *mut libc::c_char = ptr::null_mut();
let mut filecontent: *mut libc::c_char = ptr::null_mut();
let mut filebytes: size_t = 0i32 as size_t;
let mut armored_key: *mut libc::c_char = 0 as *mut libc::c_char;
let mut norm_sc: *mut libc::c_char = 0 as *mut libc::c_char;
let mut armored_key: *mut libc::c_char = ptr::null_mut();
let mut norm_sc: *mut libc::c_char = ptr::null_mut();
if !(msg_id <= 9i32 as libc::c_uint || setup_code.is_null()) {
msg = dc_get_msg(context, msg_id);
if msg.is_null()
@@ -428,19 +422,19 @@ pub unsafe fn dc_decrypt_setup_file(
filecontent: *const libc::c_char,
) -> *mut libc::c_char {
let fc_buf: *mut libc::c_char;
let mut fc_headerline: *const libc::c_char = 0 as *const libc::c_char;
let mut fc_base64: *const libc::c_char = 0 as *const libc::c_char;
let mut binary: *mut libc::c_char = 0 as *mut libc::c_char;
let mut fc_headerline: *const libc::c_char = ptr::null();
let mut fc_base64: *const libc::c_char = ptr::null();
let mut binary: *mut libc::c_char = ptr::null_mut();
let mut binary_bytes: size_t = 0i32 as size_t;
let mut indx: size_t = 0i32 as size_t;
let mut payload: *mut libc::c_char = 0 as *mut libc::c_char;
let mut payload: *mut libc::c_char = ptr::null_mut();
fc_buf = dc_strdup(filecontent);
if dc_split_armored_data(
fc_buf,
&mut fc_headerline,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
ptr::null_mut(),
ptr::null_mut(),
&mut fc_base64,
) && !fc_headerline.is_null()
&& strcmp(
@@ -484,7 +478,7 @@ pub unsafe fn dc_normalize_setup_code(
in_0: *const libc::c_char,
) -> *mut libc::c_char {
if in_0.is_null() {
return 0 as *mut libc::c_char;
return ptr::null_mut();
}
let mut out = String::new();
let mut outlen;
@@ -512,17 +506,18 @@ pub unsafe fn dc_normalize_setup_code(
}
#[allow(non_snake_case)]
pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) {
let mut current_block: u64;
pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) {
let mut ok_to_continue = true;
let mut success: libc::c_int = 0;
let mut ongoing_allocated_here: libc::c_int = 0;
let what: libc::c_int;
if !(0 == dc_alloc_ongoing(context)) {
ongoing_allocated_here = 1;
what = (*job).param.get_int(Param::Cmd).unwrap_or_default();
let param1 = CString::yolo((*job).param.get(Param::Arg).unwrap_or_default());
let _param2 = CString::yolo((*job).param.get(Param::Arg2).unwrap_or_default());
what = job.param.get_int(Param::Cmd).unwrap_or_default();
let param1_s = job.param.get(Param::Arg).unwrap_or_default();
let param1 = CString::yolo(param1_s);
let _param2 = CString::yolo(job.param.get(Param::Arg2).unwrap_or_default());
if strlen(param1.as_ptr()) == 0 {
error!(context, 0, "No Import/export dir/file given.",);
@@ -534,185 +529,45 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
} else {
if what == 1 || what == 11 {
/* before we export anything, make sure the private key exists */
if 0 == dc_ensure_secret_key_exists(context) {
if dc_ensure_secret_key_exists(context).is_err() {
error!(
context,
0,
"Import/export: Cannot create private key or private key not available.",
);
current_block = 3568988166330621280;
ok_to_continue = false;
} else {
dc_create_folder(context, param1.as_ptr());
current_block = 4495394744059808450;
dc_create_folder(context, &param1_s);
}
} else {
current_block = 4495394744059808450;
}
match current_block {
3568988166330621280 => {}
_ => match what {
if ok_to_continue {
match what {
1 => {
current_block = 10991094515395304355;
match current_block {
2973387206439775448 => {
if 0 == import_backup(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
11250025114629486028 => {
if 0 == import_self_keys(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
12669919903773909120 => {
if 0 == export_backup(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
_ => {
if 0 == export_self_keys(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
}
match current_block {
3568988166330621280 => {}
_ => {
info!(context, 0, "Import/export completed.",);
success = 1
}
if 0 != export_self_keys(context, param1.as_ptr()) {
info!(context, 0, "Import/export completed.",);
success = 1
}
}
2 => {
current_block = 11250025114629486028;
match current_block {
2973387206439775448 => {
if 0 == import_backup(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
11250025114629486028 => {
if 0 == import_self_keys(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
12669919903773909120 => {
if 0 == export_backup(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
_ => {
if 0 == export_self_keys(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
}
match current_block {
3568988166330621280 => {}
_ => {
info!(context, 0, "Import/export completed.",);
success = 1
}
if 0 != import_self_keys(context, param1.as_ptr()) {
info!(context, 0, "Import/export completed.",);
success = 1
}
}
11 => {
current_block = 12669919903773909120;
match current_block {
2973387206439775448 => {
if 0 == import_backup(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
11250025114629486028 => {
if 0 == import_self_keys(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
12669919903773909120 => {
if 0 == export_backup(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
_ => {
if 0 == export_self_keys(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
}
match current_block {
3568988166330621280 => {}
_ => {
info!(context, 0, "Import/export completed.",);
success = 1
}
if 0 != export_backup(context, param1.as_ptr()) {
info!(context, 0, "Import/export completed.",);
success = 1
}
}
12 => {
current_block = 2973387206439775448;
match current_block {
2973387206439775448 => {
if 0 == import_backup(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
11250025114629486028 => {
if 0 == import_self_keys(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
12669919903773909120 => {
if 0 == export_backup(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
_ => {
if 0 == export_self_keys(context, param1.as_ptr()) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
}
match current_block {
3568988166330621280 => {}
_ => {
info!(context, 0, "Import/export completed.",);
success = 1
}
if 0 != import_backup(context, param1.as_ptr()) {
info!(context, 0, "Import/export completed.",);
success = 1
}
}
_ => {}
},
}
}
}
}
@@ -740,7 +595,10 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
0,
"Import \"{}\" to \"{}\".",
as_str(backup_to_import),
as_str(context.get_dbfile()),
context
.get_dbfile()
.as_ref()
.map_or("<<None>>", |p| p.to_str().unwrap())
);
if 0 != dc_is_configured(context) {
@@ -748,8 +606,8 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
return 0;
}
&context.sql.close(&context);
dc_delete_file(context, context.get_dbfile());
if 0 != dc_file_exist(context, context.get_dbfile()) {
dc_delete_file(context, context.get_dbfile().unwrap());
if dc_file_exist(context, context.get_dbfile().unwrap()) {
error!(
context,
0, "Cannot import backups: Cannot delete the old file.",
@@ -757,12 +615,19 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
return 0;
}
if 0 == dc_copy_file(context, backup_to_import, context.get_dbfile()) {
if !dc_copy_file(
context,
as_path(backup_to_import),
context.get_dbfile().unwrap(),
) {
return 0;
}
/* error already logged */
/* re-open copied database file */
if !context.sql.open(&context, as_path(context.get_dbfile()), 0) {
if !context
.sql
.open(&context, &context.get_dbfile().unwrap(), 0)
{
return 0;
}
@@ -881,11 +746,20 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
context,
0,
"Backup \"{}\" to \"{}\".",
as_str(context.get_dbfile()),
context
.get_dbfile()
.as_ref()
.map_or("<<None>>", |p| p.to_str().unwrap()),
as_str(dest_pathNfilename),
);
if !(0 == dc_copy_file(context, context.get_dbfile(), dest_pathNfilename)) {
context.sql.open(&context, as_path(context.get_dbfile()), 0);
if dc_copy_file(
context,
context.get_dbfile().unwrap(),
as_path(dest_pathNfilename),
) {
context
.sql
.open(&context, &context.get_dbfile().unwrap(), 0);
closed = false;
/* add all files as blobs to the database copy (this does not require the source to be locked, neigher the destination as it is used only here) */
/*for logging only*/
@@ -1041,10 +915,12 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
}
}
if closed {
context.sql.open(&context, as_path(context.get_dbfile()), 0);
context
.sql
.open(&context, &context.get_dbfile().unwrap(), 0);
}
if 0 != delete_dest_file {
dc_delete_file(context, dest_pathNfilename);
dc_delete_file(context, as_path(dest_pathNfilename));
}
free(dest_pathNfilename as *mut libc::c_void);
@@ -1062,16 +938,16 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
Maybe we should make the "default" key handlong also a little bit smarter
(currently, the last imported key is the standard key unless it contains the string "legacy" in its name) */
let mut imported_cnt: libc::c_int = 0;
let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char;
let mut path_plus_name: *mut libc::c_char = 0 as *mut libc::c_char;
let mut suffix: *mut libc::c_char = ptr::null_mut();
let mut path_plus_name: *mut libc::c_char = ptr::null_mut();
let mut set_default: libc::c_int;
let mut buf: *mut libc::c_char = 0 as *mut libc::c_char;
let mut buf: *mut libc::c_char = ptr::null_mut();
let mut buf_bytes: size_t = 0 as size_t;
// a pointer inside buf, MUST NOT be free()'d
let mut private_key: *const libc::c_char;
let mut buf2: *mut libc::c_char = 0 as *mut libc::c_char;
let mut buf2: *mut libc::c_char = ptr::null_mut();
// a pointer inside buf2, MUST NOT be free()'d
let mut buf2_headerline: *const libc::c_char = 0 as *const libc::c_char;
let mut buf2_headerline: *const libc::c_char = ptr::null_mut();
if !dir_name.is_null() {
let dir = std::path::Path::new(as_str(dir_name));
let dir_handle = std::fs::read_dir(dir);
@@ -1092,7 +968,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
free(suffix as *mut libc::c_void);
let name_f = entry.file_name();
let name_c = name_f.to_c_string().unwrap();
suffix = dc_get_filesuffix_lc(name_c.as_ptr());
suffix = dc_get_filesuffix_lc(name_f.to_string_lossy());
if suffix.is_null()
|| strcmp(suffix, b"asc\x00" as *const u8 as *const libc::c_char) != 0
{
@@ -1106,7 +982,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
);
info!(context, 0, "Checking: {}", as_str(path_plus_name));
free(buf as *mut libc::c_void);
buf = 0 as *mut libc::c_char;
buf = ptr::null_mut();
if 0 == dc_read_file(
context,
path_plus_name,
@@ -1264,7 +1140,7 @@ unsafe fn export_key_to_asc_file(
)
}
info!(context, 0, "Exporting key {}", as_str(file_name),);
dc_delete_file(context, file_name);
dc_delete_file(context, as_path(file_name));
if !key.write_asc_to_file(file_name, context) {
error!(context, 0, "Cannot write key to {}", as_str(file_name),);
} else {
@@ -1284,147 +1160,15 @@ unsafe fn export_key_to_asc_file(
mod tests {
use super::*;
use std::ffi::CStr;
use num_traits::ToPrimitive;
use crate::config::Config;
use crate::key;
use crate::test_utils::*;
unsafe extern "C" fn logging_cb(
_ctx: &Context,
evt: Event,
_d1: uintptr_t,
d2: uintptr_t,
) -> uintptr_t {
let to_str = |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap();
match evt {
Event::INFO => println!("I: {}", to_str(d2)),
Event::WARNING => println!("W: {}", to_str(d2)),
Event::ERROR => println!("E: {}", to_str(d2)),
_ => (),
}
0
}
/// Create Alice with a pre-generated keypair.
fn create_alice_keypair(ctx: &Context) {
ctx.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
.unwrap();
// The keypair was created using:
// let (public, private) = crate::pgp::dc_pgp_create_keypair("alice@example.com")
// .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 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=="
),
KeyType::Private,
)
.unwrap();
let saved = key::dc_key_save_self_keypair(
&ctx,
&public,
&private,
"alice@example.org",
1,
&ctx.sql,
);
assert_eq!(saved, true, "Failed to save Alice's key");
}
#[test]
fn test_render_setup_file() {
let t = test_context(Some(logging_cb));
create_alice_keypair(&t.ctx); // Trick things to think we're configured.
configure_alice_keypair(&t.ctx);
let msg = dc_render_setup_file(&t.ctx, "hello").unwrap();
println!("{}", &msg);
// Check some substrings, indicating things got substituted.
@@ -1456,7 +1200,7 @@ mod tests {
#[test]
fn test_render_setup_file_newline_replace() {
let t = test_context(Some(ac_setup_msg_cb));
create_alice_keypair(&t.ctx);
configure_alice_keypair(&t.ctx);
let msg = dc_render_setup_file(&t.ctx, "pw").unwrap();
println!("{}", &msg);
assert!(msg.contains("<p>hello<br>there</p>"));

File diff suppressed because it is too large Load Diff

View File

@@ -1,209 +0,0 @@
use std::sync::{Arc, Condvar, Mutex};
use crate::context::Context;
use crate::dc_configure::*;
use crate::imap::Imap;
use crate::x::*;
#[repr(C)]
pub struct dc_jobthread_t {
pub name: &'static str,
pub folder_config_name: &'static str,
pub imap: Imap,
pub state: Arc<(Mutex<JobState>, Condvar)>,
}
pub fn dc_jobthread_init(
name: &'static str,
folder_config_name: &'static str,
imap: Imap,
) -> dc_jobthread_t {
dc_jobthread_t {
name,
folder_config_name,
imap,
state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
}
}
#[derive(Debug, Default)]
pub struct JobState {
idle: bool,
jobs_needed: i32,
suspended: i32,
using_handle: i32,
}
pub unsafe fn dc_jobthread_suspend(
context: &Context,
jobthread: &dc_jobthread_t,
suspend: libc::c_int,
) {
if 0 != suspend {
info!(context, 0, "Suspending {}-thread.", jobthread.name,);
{
jobthread.state.0.lock().unwrap().suspended = 1;
}
dc_jobthread_interrupt_idle(context, jobthread);
loop {
let using_handle = jobthread.state.0.lock().unwrap().using_handle;
if using_handle == 0 {
return;
}
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
}
} else {
info!(context, 0, "Unsuspending {}-thread.", jobthread.name);
let &(ref lock, ref cvar) = &*jobthread.state.clone();
let mut state = lock.lock().unwrap();
state.suspended = 0;
state.idle = true;
cvar.notify_one();
}
}
pub unsafe fn dc_jobthread_interrupt_idle(context: &Context, jobthread: &dc_jobthread_t) {
{
jobthread.state.0.lock().unwrap().jobs_needed = 1;
}
info!(context, 0, "Interrupting {}-IDLE...", jobthread.name);
jobthread.imap.interrupt_idle();
let &(ref lock, ref cvar) = &*jobthread.state.clone();
let mut state = lock.lock().unwrap();
state.idle = true;
cvar.notify_one();
}
pub unsafe fn dc_jobthread_fetch(
context: &Context,
jobthread: &mut dc_jobthread_t,
use_network: libc::c_int,
) {
let start;
{
let &(ref lock, _) = &*jobthread.state.clone();
let mut state = lock.lock().unwrap();
if 0 != state.suspended {
return;
}
state.using_handle = 1;
}
if 0 != use_network {
start = clock();
if !(0 == connect_to_imap(context, jobthread)) {
info!(context, 0, "{}-fetch started...", jobthread.name);
jobthread.imap.fetch(context);
if jobthread.imap.should_reconnect() {
info!(
context,
0, "{}-fetch aborted, starting over...", jobthread.name,
);
jobthread.imap.fetch(context);
}
info!(
context,
0,
"{}-fetch done in {:.3} ms.",
jobthread.name,
clock().wrapping_sub(start) as f64 / 1000.0,
);
}
}
jobthread.state.0.lock().unwrap().using_handle = 0;
}
/* ******************************************************************************
* the typical fetch, idle, interrupt-idle
******************************************************************************/
unsafe fn connect_to_imap(context: &Context, jobthread: &dc_jobthread_t) -> libc::c_int {
if jobthread.imap.is_connected() {
return 1;
}
let mut ret_connected = dc_connect_to_configured_imap(context, &jobthread.imap);
if !(0 == ret_connected) {
if context
.sql
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
jobthread.imap.configure_folders(context, 0x1);
}
if let Some(mvbox_name) = context
.sql
.get_config(context, jobthread.folder_config_name)
{
jobthread.imap.set_watch_folder(mvbox_name);
} else {
jobthread.imap.disconnect(context);
ret_connected = 0;
}
}
ret_connected
}
pub unsafe fn dc_jobthread_idle(
context: &Context,
jobthread: &dc_jobthread_t,
use_network: libc::c_int,
) {
{
let &(ref lock, ref cvar) = &*jobthread.state.clone();
let mut state = lock.lock().unwrap();
if 0 != state.jobs_needed {
info!(
context,
0,
"{}-IDLE will not be started as it was interrupted while not ideling.",
jobthread.name,
);
state.jobs_needed = 0;
return;
}
if 0 != state.suspended {
while !state.idle {
state = cvar.wait(state).unwrap();
}
state.idle = false;
return;
}
state.using_handle = 1;
if 0 == use_network {
state.using_handle = 0;
while !state.idle {
state = cvar.wait(state).unwrap();
}
state.idle = false;
return;
}
}
connect_to_imap(context, jobthread);
info!(context, 0, "{}-IDLE started...", jobthread.name,);
jobthread.imap.idle(context);
info!(context, 0, "{}-IDLE ended.", jobthread.name);
jobthread.state.0.lock().unwrap().using_handle = 0;
}

View File

@@ -1,14 +1,13 @@
use std::ffi::CString;
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::chat;
use crate::constants::Event;
use crate::constants::*;
use crate::context::*;
use crate::dc_array::*;
use crate::dc_chat::*;
use crate::dc_job::*;
use crate::dc_msg::*;
use crate::dc_saxparser::*;
use crate::dc_tools::*;
use crate::job::*;
use crate::param::*;
use crate::sql;
use crate::stock::StockMessage;
@@ -69,15 +68,11 @@ impl dc_kml_t {
}
// location streaming
pub unsafe fn dc_send_locations_to_chat(
context: &Context,
chat_id: uint32_t,
seconds: libc::c_int,
) {
pub unsafe fn dc_send_locations_to_chat(context: &Context, chat_id: uint32_t, seconds: i64) {
let now = time();
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
let is_sending_locations_before: bool;
if !(seconds < 0i32 || chat_id <= 9i32 as libc::c_uint) {
if !(seconds < 0 || chat_id <= 9i32 as libc::c_uint) {
is_sending_locations_before = dc_is_sending_locations_to_chat(context, chat_id);
if sql::execute(
context,
@@ -88,11 +83,7 @@ pub unsafe fn dc_send_locations_to_chat(
WHERE id=?",
params![
if 0 != seconds { now } else { 0 },
if 0 != seconds {
now + seconds as i64
} else {
0
},
if 0 != seconds { now + seconds } else { 0 },
chat_id as i32,
],
)
@@ -103,16 +94,11 @@ pub unsafe fn dc_send_locations_to_chat(
(*msg).text =
Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0));
(*msg).param.set_int(Param::Cmd, 8);
dc_send_msg(context, chat_id, msg);
chat::send_msg(context, chat_id, msg).unwrap();
} else if 0 == seconds && is_sending_locations_before {
let stock_str = CString::new(context.stock_system_msg(
StockMessage::MsgLocationDisabled,
"",
"",
0,
))
.unwrap();
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
let stock_str =
context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0);
chat::add_device_msg(context, chat_id, stock_str);
}
context.call_cb(
Event::CHAT_MODIFIED,
@@ -121,12 +107,12 @@ pub unsafe fn dc_send_locations_to_chat(
);
if 0 != seconds {
schedule_MAYBE_SEND_LOCATIONS(context, 0i32);
dc_job_add(
job_add(
context,
5007i32,
Action::MaybeSendLocationsEnded,
chat_id as libc::c_int,
Params::new(),
seconds + 1i32,
seconds + 1,
);
}
}
@@ -139,8 +125,8 @@ pub unsafe fn dc_send_locations_to_chat(
******************************************************************************/
#[allow(non_snake_case)]
unsafe fn schedule_MAYBE_SEND_LOCATIONS(context: &Context, flags: libc::c_int) {
if 0 != flags & 0x1 || !dc_job_action_exists(context, 5005) {
dc_job_add(context, 5005, 0, Params::new(), 60);
if 0 != flags & 0x1 || !job_action_exists(context, Action::MaybeSendLocations) {
job_add(context, Action::MaybeSendLocations, 0, Params::new(), 60);
};
}
@@ -201,7 +187,7 @@ pub fn dc_get_locations(
contact_id: uint32_t,
timestamp_from: i64,
mut timestamp_to: i64,
) -> *mut dc_array_t {
) -> Vec<dc_location> {
if timestamp_to == 0 {
timestamp_to = time() + 10;
}
@@ -239,10 +225,10 @@ pub fn dc_get_locations(
accuracy: row.get(3)?,
timestamp: row.get(4)?,
independent: row.get(5)?,
msg_id: msg_id,
msg_id,
contact_id: row.get(7)?,
chat_id: row.get(8)?,
marker: marker,
marker,
};
Ok(loc)
},
@@ -252,10 +238,10 @@ pub fn dc_get_locations(
for location in locations {
ret.push(location?);
}
Ok(dc_array_t::from(ret).into_raw())
Ok(ret)
},
)
.unwrap_or_else(|_| std::ptr::null_mut())
.unwrap_or_default()
}
fn is_marker(txt: &str) -> bool {
@@ -479,165 +465,159 @@ pub unsafe fn dc_kml_parse(
content_bytes: size_t,
) -> dc_kml_t {
let mut kml = dc_kml_t::new();
let mut content_nullterminated: *mut libc::c_char = 0 as *mut libc::c_char;
let mut saxparser: dc_saxparser_t = dc_saxparser_t {
starttag_cb: None,
endtag_cb: None,
text_cb: None,
userdata: 0 as *mut libc::c_void,
};
if content_bytes > (1 * 1024 * 1024) {
warn!(
context,
0, "A kml-files with {} bytes is larger than reasonably expected.", content_bytes,
);
} else {
content_nullterminated = dc_null_terminate(content, content_bytes as libc::c_int);
if !content_nullterminated.is_null() {
kml.locations = Some(Vec::with_capacity(100));
dc_saxparser_init(
&mut saxparser,
&mut kml as *mut dc_kml_t as *mut libc::c_void,
);
dc_saxparser_set_tag_handler(
&mut saxparser,
Some(kml_starttag_cb),
Some(kml_endtag_cb),
);
dc_saxparser_set_text_handler(&mut saxparser, Some(kml_text_cb));
dc_saxparser_parse(&mut saxparser, content_nullterminated);
return kml;
}
let content_null = dc_null_terminate(content, content_bytes as libc::c_int);
if !content_null.is_null() {
let mut reader = quick_xml::Reader::from_str(as_str(content_null));
reader.trim_text(true);
kml.locations = Some(Vec::with_capacity(100));
let mut buf = Vec::new();
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => kml_starttag_cb(e, &mut kml, &reader),
Ok(quick_xml::events::Event::End(ref e)) => kml_endtag_cb(e, &mut kml),
Ok(quick_xml::events::Event::Text(ref e)) => kml_text_cb(e, &mut kml, &reader),
Err(e) => {
error!(
context,
0,
"Location parsing: Error at position {}: {:?}",
reader.buffer_position(),
e
);
}
Ok(quick_xml::events::Event::Eof) => break,
_ => (),
}
buf.clear();
}
}
free(content_nullterminated as *mut libc::c_void);
free(content_null.cast());
kml
}
unsafe fn kml_text_cb(userdata: *mut libc::c_void, text: *const libc::c_char, _len: libc::c_int) {
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
if 0 != (*kml).tag & (0x4 | 0x10) {
let mut val: *mut libc::c_char = dc_strdup(text);
dc_str_replace(
&mut val,
b"\n\x00" as *const u8 as *const libc::c_char,
b"\x00" as *const u8 as *const libc::c_char,
);
dc_str_replace(
&mut val,
b"\r\x00" as *const u8 as *const libc::c_char,
b"\x00" as *const u8 as *const libc::c_char,
);
dc_str_replace(
&mut val,
b"\t\x00" as *const u8 as *const libc::c_char,
b"\x00" as *const u8 as *const libc::c_char,
);
dc_str_replace(
&mut val,
b" \x00" as *const u8 as *const libc::c_char,
b"\x00" as *const u8 as *const libc::c_char,
);
if 0 != (*kml).tag & 0x4 && strlen(val) >= 19 {
fn kml_text_cb<B: std::io::BufRead>(
event: &BytesText,
kml: &mut dc_kml_t,
reader: &quick_xml::Reader<B>,
) {
if 0 != kml.tag & (0x4 | 0x10) {
let val = event.unescape_and_decode(reader).unwrap_or_default();
let val = val
.replace("\n", "")
.replace("\r", "")
.replace("\t", "")
.replace(" ", "");
if 0 != kml.tag & 0x4 && val.len() >= 19 {
// YYYY-MM-DDTHH:MM:SSZ
// 0 4 7 10 13 16 19
let val_r = as_str(val);
match chrono::NaiveDateTime::parse_from_str(val_r, "%Y-%m-%dT%H:%M:%SZ") {
match chrono::NaiveDateTime::parse_from_str(&val, "%Y-%m-%dT%H:%M:%SZ") {
Ok(res) => {
(*kml).curr.timestamp = res.timestamp();
if (*kml).curr.timestamp > time() {
(*kml).curr.timestamp = time();
kml.curr.timestamp = res.timestamp();
if kml.curr.timestamp > time() {
kml.curr.timestamp = time();
}
}
Err(_err) => {
(*kml).curr.timestamp = time();
kml.curr.timestamp = time();
}
}
} else if 0 != (*kml).tag & 0x10 {
let mut comma: *mut libc::c_char = strchr(val, ',' as i32);
if !comma.is_null() {
let longitude: *mut libc::c_char = val;
let latitude: *mut libc::c_char = comma.offset(1isize);
*comma = 0 as libc::c_char;
comma = strchr(latitude, ',' as i32);
if !comma.is_null() {
*comma = 0 as libc::c_char
}
(*kml).curr.latitude = dc_atof(latitude);
(*kml).curr.longitude = dc_atof(longitude)
} else if 0 != kml.tag & 0x10 {
let parts = val.splitn(2, ',').collect::<Vec<_>>();
if parts.len() == 2 {
kml.curr.longitude = parts[0].parse().unwrap_or_default();
kml.curr.latitude = parts[1].parse().unwrap_or_default();
}
}
free(val as *mut libc::c_void);
};
}
}
unsafe fn kml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0 {
if 0 != (*kml).tag & 0x1
&& 0 != (*kml).curr.timestamp
&& 0. != (*kml).curr.latitude
&& 0. != (*kml).curr.longitude
fn kml_endtag_cb(event: &BytesEnd, kml: &mut dc_kml_t) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
if tag == "placemark" {
if 0 != kml.tag & 0x1
&& 0 != kml.curr.timestamp
&& 0. != kml.curr.latitude
&& 0. != kml.curr.longitude
{
let location = (*kml).curr.clone();
((*kml).locations.as_mut().unwrap()).push(location);
if let Some(ref mut locations) = kml.locations {
locations.push(std::mem::replace(&mut kml.curr, dc_location::new()));
}
}
(*kml).tag = 0
kml.tag = 0
};
}
/*******************************************************************************
* parse kml-files
******************************************************************************/
unsafe fn kml_starttag_cb(
userdata: *mut libc::c_void,
tag: *const libc::c_char,
attr: *mut *mut libc::c_char,
fn kml_starttag_cb<B: std::io::BufRead>(
event: &BytesStart,
kml: &mut dc_kml_t,
reader: &quick_xml::Reader<B>,
) {
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
if strcmp(tag, b"document\x00" as *const u8 as *const libc::c_char) == 0 {
let addr: *const libc::c_char =
dc_attr_find(attr, b"addr\x00" as *const u8 as *const libc::c_char);
if !addr.is_null() {
(*kml).addr = dc_strdup(addr)
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
if tag == "document" {
if let Some(addr) = event.attributes().find(|attr| {
attr.as_ref()
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "addr")
.unwrap_or_default()
}) {
kml.addr = unsafe {
addr.unwrap()
.unescape_and_decode_value(reader)
.unwrap_or_default()
.strdup()
};
}
} else if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0 {
(*kml).tag = 0x1;
(*kml).curr.timestamp = 0;
(*kml).curr.latitude = 0 as libc::c_double;
(*kml).curr.longitude = 0.0f64;
(*kml).curr.accuracy = 0.0f64
} else if strcmp(tag, b"timestamp\x00" as *const u8 as *const libc::c_char) == 0
&& 0 != (*kml).tag & 0x1
{
(*kml).tag = 0x1 | 0x2
} else if strcmp(tag, b"when\x00" as *const u8 as *const libc::c_char) == 0
&& 0 != (*kml).tag & 0x2
{
(*kml).tag = 0x1 | 0x2 | 0x4
} else if strcmp(tag, b"point\x00" as *const u8 as *const libc::c_char) == 0
&& 0 != (*kml).tag & 0x1
{
(*kml).tag = 0x1 | 0x8
} else if strcmp(tag, b"coordinates\x00" as *const u8 as *const libc::c_char) == 0
&& 0 != (*kml).tag & 0x8
{
(*kml).tag = 0x1 | 0x8 | 0x10;
let accuracy: *const libc::c_char =
dc_attr_find(attr, b"accuracy\x00" as *const u8 as *const libc::c_char);
if !accuracy.is_null() {
(*kml).curr.accuracy = dc_atof(accuracy)
} else if tag == "placemark" {
kml.tag = 0x1;
kml.curr.timestamp = 0;
kml.curr.latitude = 0 as libc::c_double;
kml.curr.longitude = 0.0f64;
kml.curr.accuracy = 0.0f64
} else if tag == "timestamp" && 0 != kml.tag & 0x1 {
kml.tag = 0x1 | 0x2
} else if tag == "when" && 0 != kml.tag & 0x2 {
kml.tag = 0x1 | 0x2 | 0x4
} else if tag == "point" && 0 != kml.tag & 0x1 {
kml.tag = 0x1 | 0x8
} else if tag == "coordinates" && 0 != kml.tag & 0x8 {
kml.tag = 0x1 | 0x8 | 0x10;
if let Some(acc) = event.attributes().find(|attr| {
attr.as_ref()
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "accuracy")
.unwrap_or_default()
}) {
let v = acc
.unwrap()
.unescape_and_decode_value(reader)
.unwrap_or_default();
kml.curr.accuracy = v.trim().parse().unwrap_or_default();
}
};
}
}
pub unsafe fn dc_kml_unref(kml: &mut dc_kml_t) {
free((*kml).addr as *mut libc::c_void);
free(kml.addr as *mut libc::c_void);
}
#[allow(non_snake_case)]
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mut dc_job_t) {
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Job) {
let now = time();
let mut continue_streaming: libc::c_int = 1;
info!(
@@ -702,7 +682,8 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mu
let mut msg = dc_msg_new(context, Viewtype::Text);
(*msg).hidden = 1;
(*msg).param.set_int(Param::Cmd, 9);
dc_send_msg(context, chat_id as u32, msg);
// TODO: handle cleanup on error
chat::send_msg(context, chat_id as u32, msg).unwrap();
dc_msg_unref(msg);
}
Ok(())
@@ -718,7 +699,7 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mu
}
#[allow(non_snake_case)]
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut dc_job_t) {
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut Job) {
// this function is called when location-streaming _might_ have ended for a chat.
// the function checks, if location-streaming is really ended;
// if so, a device-message is added if not yet done.
@@ -740,8 +721,8 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut
"UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?",
params![chat_id as i32],
).is_ok() {
let stock_str = CString::new(context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)).unwrap();
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
let stock_str = context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0);
chat::add_device_msg(context, chat_id, stock_str);
context.call_cb(
Event::CHAT_MODIFIED,
chat_id as usize,
@@ -752,3 +733,46 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::dummy_context;
#[test]
fn test_dc_kml_parse() {
unsafe {
let context = dummy_context();
let xml =
b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n<Document addr=\"user@example.org\">\n<Placemark><Timestamp><when>2019-03-06T21:09:57Z</when></Timestamp><Point><coordinates accuracy=\"32.000000\">9.423110,53.790302</coordinates></Point></Placemark>\n<PlaceMARK>\n<Timestamp><WHEN > \n\t2018-12-13T22:11:12Z\t</WHEN></Timestamp><Point><coordinates aCCuracy=\"2.500000\"> 19.423110 \t , \n 63.790302\n </coordinates></Point></PlaceMARK>\n</Document>\n</kml>\x00"
as *const u8 as *const libc::c_char;
let mut kml = dc_kml_parse(&context.ctx, xml, strlen(xml));
assert!(!kml.addr.is_null());
assert_eq!(as_str(kml.addr as *const libc::c_char), "user@example.org",);
let locations_ref = &kml.locations.as_ref().unwrap();
assert_eq!(locations_ref.len(), 2);
assert!(locations_ref[0].latitude > 53.6f64);
assert!(locations_ref[0].latitude < 53.8f64);
assert!(locations_ref[0].longitude > 9.3f64);
assert!(locations_ref[0].longitude < 9.5f64);
assert!(locations_ref[0].accuracy > 31.9f64);
assert!(locations_ref[0].accuracy < 32.1f64);
assert_eq!(locations_ref[0].timestamp, 1551906597);
assert!(locations_ref[1].latitude > 63.6f64);
assert!(locations_ref[1].latitude < 63.8f64);
assert!(locations_ref[1].longitude > 19.3f64);
assert!(locations_ref[1].longitude < 19.5f64);
assert!(locations_ref[1].accuracy > 2.4f64);
assert!(locations_ref[1].accuracy < 2.6f64);
assert_eq!(locations_ref[1].timestamp, 1544739072);
dc_kml_unref(&mut kml);
}
}
}

View File

@@ -1,185 +0,0 @@
use crate::contact::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_msg::*;
use crate::dc_tools::*;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
use std::ffi::CString;
use std::ptr;
/* * Structure behind dc_lot_t */
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_lot_t {
pub magic: uint32_t,
pub text1_meaning: libc::c_int,
pub text1: *mut libc::c_char,
pub text2: *mut libc::c_char,
pub timestamp: i64,
pub state: libc::c_int,
pub id: uint32_t,
pub fingerprint: *mut libc::c_char,
pub invitenumber: *mut libc::c_char,
pub auth: *mut libc::c_char,
}
/* *
* @class dc_lot_t
*
* An object containing a set of values.
* The meaning of the values is defined by the function returning the object.
* Lot objects are created
* eg. by chatlist.get_summary() or dc_msg_get_summary().
*
* NB: _Lot_ is used in the meaning _heap_ here.
*/
pub unsafe fn dc_lot_new() -> *mut dc_lot_t {
let mut lot: *mut dc_lot_t;
lot = calloc(1, ::std::mem::size_of::<dc_lot_t>()) as *mut dc_lot_t;
assert!(!lot.is_null());
(*lot).magic = 0x107107i32 as uint32_t;
(*lot).text1_meaning = 0i32;
lot
}
pub unsafe fn dc_lot_empty(mut lot: *mut dc_lot_t) {
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
return;
}
free((*lot).text1 as *mut libc::c_void);
(*lot).text1 = 0 as *mut libc::c_char;
(*lot).text1_meaning = 0i32;
free((*lot).text2 as *mut libc::c_void);
(*lot).text2 = 0 as *mut libc::c_char;
free((*lot).fingerprint as *mut libc::c_void);
(*lot).fingerprint = 0 as *mut libc::c_char;
free((*lot).invitenumber as *mut libc::c_void);
(*lot).invitenumber = 0 as *mut libc::c_char;
free((*lot).auth as *mut libc::c_void);
(*lot).auth = 0 as *mut libc::c_char;
(*lot).timestamp = 0;
(*lot).state = 0i32;
(*lot).id = 0i32 as uint32_t;
}
pub unsafe fn dc_lot_unref(mut set: *mut dc_lot_t) {
if set.is_null() || (*set).magic != 0x107107i32 as libc::c_uint {
return;
}
dc_lot_empty(set);
(*set).magic = 0i32 as uint32_t;
free(set as *mut libc::c_void);
}
pub unsafe fn dc_lot_get_text1(lot: *const dc_lot_t) -> *mut libc::c_char {
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
return 0 as *mut libc::c_char;
}
dc_strdup_keep_null((*lot).text1)
}
pub unsafe fn dc_lot_get_text2(lot: *const dc_lot_t) -> *mut libc::c_char {
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
return 0 as *mut libc::c_char;
}
dc_strdup_keep_null((*lot).text2)
}
pub unsafe fn dc_lot_get_text1_meaning(lot: *const dc_lot_t) -> libc::c_int {
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
return 0i32;
}
(*lot).text1_meaning
}
pub unsafe fn dc_lot_get_state(lot: *const dc_lot_t) -> libc::c_int {
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
return 0i32;
}
(*lot).state
}
pub unsafe fn dc_lot_get_id(lot: *const dc_lot_t) -> uint32_t {
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
return 0i32 as uint32_t;
}
(*lot).id
}
pub unsafe fn dc_lot_get_timestamp(lot: *const dc_lot_t) -> i64 {
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
return 0;
}
(*lot).timestamp
}
/* library-internal */
/* in practice, the user additionally cuts the string himself pixel-accurate */
pub unsafe fn dc_lot_fill(
mut lot: *mut dc_lot_t,
msg: *mut dc_msg_t,
chat: *const Chat,
contact: Option<&Contact>,
context: &Context,
) {
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint || msg.is_null() {
return;
}
if (*msg).state == 19i32 {
(*lot).text1 = context.stock_str(StockMessage::Draft).strdup();
(*lot).text1_meaning = 1i32
} else if (*msg).from_id == 1i32 as libc::c_uint {
if 0 != dc_msg_is_info(msg) || 0 != dc_chat_is_self_talk(chat) {
(*lot).text1 = 0 as *mut libc::c_char;
(*lot).text1_meaning = 0i32
} else {
(*lot).text1 = context.stock_str(StockMessage::SelfMsg).strdup();
(*lot).text1_meaning = 3i32
}
} else if chat.is_null() {
(*lot).text1 = 0 as *mut libc::c_char;
(*lot).text1_meaning = 0i32
} else if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 {
if 0 != dc_msg_is_info(msg) || contact.is_none() {
(*lot).text1 = 0 as *mut libc::c_char;
(*lot).text1_meaning = 0i32
} else {
if !chat.is_null() && (*chat).id == 1i32 as libc::c_uint {
if let Some(contact) = contact {
(*lot).text1 = contact.get_display_name().strdup();
} else {
(*lot).text1 = std::ptr::null_mut();
}
} else {
if let Some(contact) = contact {
(*lot).text1 = contact.get_first_name().strdup();
} else {
(*lot).text1 = std::ptr::null_mut();
}
}
(*lot).text1_meaning = 2i32;
}
}
let msgtext_c = (*msg)
.text
.as_ref()
.map(|s| CString::yolo(String::as_str(s)));
let msgtext_ptr = msgtext_c.map_or(ptr::null(), |s| s.as_ptr());
(*lot).text2 =
dc_msg_get_summarytext_by_raw((*msg).type_0, msgtext_ptr, &mut (*msg).param, 160, context);
(*lot).timestamp = dc_msg_get_timestamp(msg);
(*lot).state = (*msg).state;
}

View File

@@ -9,12 +9,11 @@ use mmime::mailmime_types_helper::*;
use mmime::mailmime_write_mem::*;
use mmime::mmapstring::*;
use mmime::other::*;
use std::ptr;
use crate::chat::{self, Chat};
use crate::constants::*;
use crate::contact::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::context::{dc_get_version_str, Context};
use crate::dc_e2ee::*;
use crate::dc_location::*;
use crate::dc_msg::*;
@@ -25,8 +24,7 @@ use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
#[derive(Copy, Clone)]
#[repr(C)]
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub struct dc_mimefactory_t<'a> {
pub from_addr: *mut libc::c_char,
@@ -38,7 +36,7 @@ pub struct dc_mimefactory_t<'a> {
pub rfc724_mid: *mut libc::c_char,
pub loaded: dc_mimefactory_loaded_t,
pub msg: *mut dc_msg_t<'a>,
pub chat: *mut Chat<'a>,
pub chat: Option<Chat<'a>>,
pub increation: libc::c_int,
pub in_reply_to: *mut libc::c_char,
pub references: *mut libc::c_char,
@@ -52,23 +50,11 @@ pub struct dc_mimefactory_t<'a> {
}
#[allow(non_camel_case_types)]
pub type dc_mimefactory_loaded_t = libc::c_uint;
pub const DC_MF_MDN_LOADED: dc_mimefactory_loaded_t = 2;
type dc_mimefactory_loaded_t = libc::c_uint;
const DC_MF_MDN_LOADED: dc_mimefactory_loaded_t = 2;
pub const DC_MF_MSG_LOADED: dc_mimefactory_loaded_t = 1;
pub const DC_MF_NOTHING_LOADED: dc_mimefactory_loaded_t = 0;
pub unsafe fn dc_mimefactory_init<'a>(factory: *mut dc_mimefactory_t<'a>, context: &'a Context) {
if factory.is_null() {
return;
}
memset(
factory as *mut libc::c_void,
0,
::std::mem::size_of::<dc_mimefactory_t>(),
);
(*factory).context = context;
}
pub unsafe fn dc_mimefactory_empty(mut factory: *mut dc_mimefactory_t) {
if factory.is_null() {
return;
@@ -93,8 +79,7 @@ pub unsafe fn dc_mimefactory_empty(mut factory: *mut dc_mimefactory_t) {
}
dc_msg_unref((*factory).msg);
(*factory).msg = 0 as *mut dc_msg_t;
dc_chat_unref((*factory).chat);
(*factory).chat = 0 as *mut Chat;
(*factory).chat = None;
free((*factory).in_reply_to as *mut libc::c_void);
(*factory).in_reply_to = 0 as *mut libc::c_char;
free((*factory).references as *mut libc::c_void);
@@ -125,131 +110,139 @@ pub unsafe fn dc_mimefactory_load_msg(
(*factory).recipients_names = clist_new();
(*factory).recipients_addr = clist_new();
(*factory).msg = dc_msg_new_untyped(context);
(*factory).chat = dc_chat_new(context);
if dc_msg_load_from_db((*factory).msg, context, msg_id)
&& dc_chat_load_from_db((*factory).chat, (*(*factory).msg).chat_id)
{
load_from(factory);
(*factory).req_mdn = 0;
if 0 != dc_chat_is_self_talk((*factory).chat) {
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
dc_strdup_keep_null((*factory).from_displayname) as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
dc_strdup((*factory).from_addr) as *mut libc::c_void,
);
} 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?;
let addr_c = addr.strdup();
if clist_search_string_nocase((*factory).recipients_addr, addr_c) == 0 {
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
if !authname.is_empty() {
authname.strdup()
} else {
std::ptr::null_mut()
} as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
addr_c as *mut libc::c_void,
);
}
}
Ok(())
},
)
.unwrap();
let command = (*(*factory).msg)
.param
.get_int(Param::Cmd)
.unwrap_or_default();
if dc_msg_load_from_db((*factory).msg, context, msg_id) {
if let Ok(chat) = Chat::load_from_db(context, (*(*factory).msg).chat_id) {
(*factory).chat = Some(chat);
if command == 5 {
let email_to_remove = (*(*factory).msg).param.get(Param::Arg).unwrap_or_default();
let email_to_remove_c = email_to_remove.strdup();
let chat = (*factory).chat.as_ref().unwrap();
let self_addr = context
load_from(factory);
(*factory).req_mdn = 0;
if chat.is_self_talk() {
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
dc_strdup_keep_null((*factory).from_displayname) as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
dc_strdup((*factory).from_addr) as *mut libc::c_void,
);
} else {
context
.sql
.get_config(context, "configured_addr")
.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?;
let addr_c = addr.strdup();
if clist_search_string_nocase((*factory).recipients_addr, addr_c)
== 0
{
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
if !authname.is_empty() {
authname.strdup()
} else {
std::ptr::null_mut()
}
as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
addr_c as *mut libc::c_void,
);
}
}
Ok(())
},
)
.unwrap();
let command = (*(*factory).msg)
.param
.get_int(Param::Cmd)
.unwrap_or_default();
if !email_to_remove.is_empty() && email_to_remove != self_addr {
if clist_search_string_nocase((*factory).recipients_addr, email_to_remove_c)
== 0
{
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
0 as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
email_to_remove_c as *mut libc::c_void,
);
if command == 5 {
let email_to_remove =
(*(*factory).msg).param.get(Param::Arg).unwrap_or_default();
let email_to_remove_c = email_to_remove.strdup();
let self_addr = context
.sql
.get_config(context, "configured_addr")
.unwrap_or_default();
if !email_to_remove.is_empty() && email_to_remove != self_addr {
if clist_search_string_nocase((*factory).recipients_addr, email_to_remove_c)
== 0
{
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
0 as *mut libc::c_void,
);
clist_insert_after(
(*factory).recipients_addr,
(*(*factory).recipients_addr).last,
email_to_remove_c as *mut libc::c_void,
);
}
}
}
if command != 6
&& command != 7
&& 0 != context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1)
{
(*factory).req_mdn = 1
}
}
if command != 6
&& command != 7
&& 0 != context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1)
{
(*factory).req_mdn = 1
}
}
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)?;
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.strdup();
(*factory).references = references.strdup();
Ok((in_reply_to, references))
},
);
match row {
Ok((in_reply_to, references)) => {
(*factory).in_reply_to = in_reply_to.strdup();
(*factory).references = references.strdup();
}
Err(err) => {
error!(
context,
0, "mimefactory: failed to load mime_in_reply_to: {:?}", err
);
}
}
Err(err) => {
error!(
context,
0, "mimefactory: failed to load mime_in_reply_to: {:?}", err
);
}
}
success = 1;
(*factory).loaded = DC_MF_MSG_LOADED;
(*factory).timestamp = (*(*factory).msg).timestamp_sort;
(*factory).rfc724_mid = dc_strdup((*(*factory).msg).rfc724_mid)
success = 1;
(*factory).loaded = DC_MF_MSG_LOADED;
(*factory).timestamp = (*(*factory).msg).timestamp_sort;
(*factory).rfc724_mid = dc_strdup((*(*factory).msg).rfc724_mid)
}
}
if 0 != success {
(*factory).increation = dc_msg_is_increation((*factory).msg)
@@ -478,18 +471,19 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
.map(|s| format!("/{}", s))
.unwrap_or_default();
let os_part = CString::new(os_part).expect("String -> CString conversion failed");
let version = dc_get_version_str();
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
strdup(b"X-Mailer\x00" as *const u8 as *const libc::c_char),
dc_mprintf(
b"Delta Chat Core %s%s\x00" as *const u8 as *const libc::c_char,
DC_VERSION_STR as *const u8 as *const libc::c_char,
version,
os_part.as_ptr(),
),
),
);
free(version.cast());
mailimf_fields_add(
imf_fields,
@@ -514,11 +508,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
if (*factory).loaded as libc::c_uint == DC_MF_MSG_LOADED as libc::c_int as libc::c_uint {
/* Render a normal message
*********************************************************************/
let chat: *mut Chat = (*factory).chat;
let msg: *mut dc_msg_t = (*factory).msg;
let chat = (*factory).chat.as_ref().unwrap();
let msg = (*factory).msg;
let mut meta_part: *mut mailmime = 0 as *mut mailmime;
let mut placeholdertext: *mut libc::c_char = 0 as *mut libc::c_char;
if (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int {
if chat.typ == Chattype::VerifiedGroup {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -541,29 +535,28 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
.unwrap_or_default()
}
}
if (*chat).gossiped_timestamp == 0
|| ((*chat).gossiped_timestamp + (2 * 24 * 60 * 60)) < time()
if chat.gossiped_timestamp == 0
|| (chat.gossiped_timestamp + (2 * 24 * 60 * 60)) < time()
{
do_gossip = 1
}
/* build header etc. */
let command = (*msg).param.get_int(Param::Cmd).unwrap_or_default();
if (*chat).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int
{
if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
strdup(b"Chat-Group-ID\x00" as *const u8 as *const libc::c_char),
dc_strdup((*chat).grpid),
chat.grpid.strdup(),
),
);
let name = CString::yolo(chat.name.as_bytes());
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
strdup(b"Chat-Group-Name\x00" as *const u8 as *const libc::c_char),
dc_encode_header_words((*chat).name),
dc_encode_header_words(name.as_ptr()),
),
);
if command == 5 {
@@ -594,7 +587,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
email_to_add,
),
);
grpimage = (*chat).param.get(Param::ProfileImage);
grpimage = chat.param.get(Param::ProfileImage);
}
if 0 != (*msg).param.get_int(Param::Arg2).unwrap_or_default() & 0x1 {
info!(
@@ -838,7 +831,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
free(placeholdertext as *mut libc::c_void);
/* add attachment part */
if msgtype_has_file((*msg).type_0) {
if chat::msgtype_has_file((*msg).type_0) {
if !is_file_size_okay(msg) {
let error: *mut libc::c_char = dc_mprintf(
b"Message exceeds the recommended %i MB.\x00" as *const u8
@@ -960,12 +953,14 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
message_text = format!("{}\r\n", p2).strdup();
let human_mime_part: *mut mailmime = build_body_text(message_text);
mailmime_add_part(multipart, human_mime_part);
let version = dc_get_version_str();
message_text2 =
dc_mprintf(b"Reporting-UA: Delta Chat %s\r\nOriginal-Recipient: rfc822;%s\r\nFinal-Recipient: rfc822;%s\r\nOriginal-Message-ID: <%s>\r\nDisposition: manual-action/MDN-sent-automatically; displayed\r\n\x00"
as *const u8 as *const libc::c_char,
DC_VERSION_STR as *const u8 as *const libc::c_char,
version,
(*factory).from_addr, (*factory).from_addr,
(*(*factory).msg).rfc724_mid);
free(version.cast());
let content_type_0: *mut mailmime_content = mailmime_content_new_with_str(
b"message/disposition-notification\x00" as *const u8 as *const libc::c_char,
);
@@ -998,7 +993,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
e.as_ptr(),
);
} else {
subject_str = get_subject((*factory).chat, (*factory).msg, afwd_email)
subject_str = get_subject((*factory).chat.as_ref(), (*factory).msg, afwd_email)
}
subject = mailimf_subject_new(dc_encode_header_words(subject_str));
mailimf_fields_add(
@@ -1041,11 +1036,6 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
&mut e2ee_helper,
);
}
info!(
(*factory).context,
0, "encryption successful {}", e2ee_helper.encryption_successfull
);
if 0 != e2ee_helper.encryption_successfull {
(*factory).out_encrypted = 1;
if 0 != do_gossip {
@@ -1057,6 +1047,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
success = 1
}
}
if !message.is_null() {
mailmime_free(message);
}
@@ -1069,21 +1060,27 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
}
unsafe fn get_subject(
chat: *const Chat,
chat: Option<&Chat>,
msg: *mut dc_msg_t,
afwd_email: libc::c_int,
) -> *mut libc::c_char {
let context = (*chat).context;
if chat.is_none() {
return std::ptr::null_mut();
}
let chat = chat.unwrap();
let context = chat.context;
let ret: *mut libc::c_char;
let raw_subject = {
let msgtext_c = (*msg)
.text
.as_ref()
.map(|s| CString::yolo(String::as_str(s)));
let msgtext_ptr = msgtext_c.map_or(ptr::null(), |s| s.as_ptr());
dc_msg_get_summarytext_by_raw((*msg).type_0, msgtext_ptr, &mut (*msg).param, 32, context)
dc_msg_get_summarytext_by_raw(
(*msg).type_0,
(*msg).text.as_ref(),
&mut (*msg).param,
32,
context,
)
.strdup()
};
let fwd = if 0 != afwd_email {
@@ -1093,15 +1090,14 @@ unsafe fn get_subject(
};
if (*msg).param.get_int(Param::Cmd).unwrap_or_default() == 6 {
ret = context.stock_str(StockMessage::AcSetupMsgSubject).strdup()
} else if (*chat).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int
{
ret = dc_mprintf(
b"Chat: %s: %s%s\x00" as *const u8 as *const libc::c_char,
(*chat).name,
fwd,
raw_subject,
} else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
ret = format!(
"Chat: {}: {}{}",
chat.name,
to_string(fwd),
to_string(raw_subject),
)
.strdup()
} else {
ret = dc_mprintf(
b"Chat: %s%s\x00" as *const u8 as *const libc::c_char,
@@ -1152,22 +1148,20 @@ unsafe fn build_body_file(
let mime_fields: *mut mailmime_fields;
let mut mime_sub: *mut mailmime = 0 as *mut mailmime;
let content: *mut mailmime_content;
let pathNfilename = (*msg)
.param
.get(Param::File)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
let path_filename = (*msg).param.get(Param::File);
let mut mimetype = (*msg)
.param
.get(Param::MimeType)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
let suffix = dc_get_filesuffix_lc(pathNfilename);
let mut filename_to_send = 0 as *mut libc::c_char;
let mut filename_encoded = 0 as *mut libc::c_char;
if !pathNfilename.is_null() {
if let Some(ref path_filename) = path_filename {
let suffix = dc_get_filesuffix_lc(path_filename);
if (*msg).type_0 == Viewtype::Voice {
let ts = chrono::Utc.timestamp((*msg).timestamp_sort as i64, 0);
@@ -1181,7 +1175,7 @@ unsafe fn build_body_file(
.to_string();
filename_to_send = res.strdup();
} else if (*msg).type_0 == Viewtype::Audio {
filename_to_send = dc_get_filename(pathNfilename)
filename_to_send = dc_get_filename(path_filename)
} else if (*msg).type_0 == Viewtype::Image || (*msg).type_0 == Viewtype::Gif {
if base_name.is_null() {
base_name = b"image\x00" as *const u8 as *const libc::c_char
@@ -1205,7 +1199,7 @@ unsafe fn build_body_file(
},
)
} else {
filename_to_send = dc_get_filename(pathNfilename)
filename_to_send = dc_get_filename(path_filename)
}
if mimetype.is_null() {
if suffix.is_null() {
@@ -1298,17 +1292,16 @@ unsafe fn build_body_file(
) as *mut libc::c_void,
);
mime_sub = mailmime_new_empty(content, mime_fields);
mailmime_set_body_file(mime_sub, dc_get_abs_path((*msg).context, pathNfilename));
mailmime_set_body_file(mime_sub, dc_get_abs_path((*msg).context, path_filename));
if !ret_file_name_as_sent.is_null() {
*ret_file_name_as_sent = dc_strdup(filename_to_send)
}
}
}
free(pathNfilename as *mut libc::c_void);
free(mimetype as *mut libc::c_void);
free(filename_to_send as *mut libc::c_void);
free(filename_encoded as *mut libc::c_void);
free(suffix as *mut libc::c_void);
mime_sub
}
@@ -1316,16 +1309,14 @@ unsafe fn build_body_file(
/*******************************************************************************
* Render
******************************************************************************/
#[allow(non_snake_case)]
unsafe fn is_file_size_okay(msg: *const dc_msg_t) -> bool {
let mut file_size_okay = true;
let pathNfilename = (*msg).param.get(Param::File).unwrap_or_default().strdup();
let bytes = dc_get_filebytes((*msg).context, pathNfilename);
let path = (*msg).param.get(Param::File).unwrap_or_default();
let bytes = dc_get_filebytes((*msg).context, &path);
if bytes > (49 * 1024 * 1024 / 4 * 3) {
file_size_okay = false;
}
free(pathNfilename as *mut _);
file_size_okay
}

View File

@@ -61,11 +61,6 @@ pub struct dc_mimeparser_t<'a> {
pub message_kml: Option<dc_kml_t>,
}
// deprecated
pub unsafe fn dc_no_compound_msgs() {
S_GENERATE_COMPOUND_MSGS = 0i32;
}
// deprecated: flag to switch generation of compound messages on and off.
static mut S_GENERATE_COMPOUND_MSGS: libc::c_int = 1i32;
@@ -93,7 +88,7 @@ pub unsafe fn dc_mimeparser_unref(mimeparser: &mut dc_mimeparser_t) {
dc_mimeparser_empty(mimeparser);
}
pub unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) {
unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) {
for part in mimeparser.parts.drain(..) {
dc_mimepart_unref(part);
}
@@ -161,12 +156,7 @@ pub unsafe fn dc_mimeparser_parse(
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
mimeparser.subject = dc_decode_header_words((*(*field).fld_data.fld_subject).sbj_value)
}
if !dc_mimeparser_lookup_optional_field(
mimeparser,
b"Chat-Version\x00" as *const u8 as *const libc::c_char,
)
.is_null()
{
if !dc_mimeparser_lookup_optional_field(mimeparser, "Chat-Version").is_null() {
mimeparser.is_send_by_messenger = 1i32
}
if !dc_mimeparser_lookup_field(mimeparser, "Autocrypt-Setup-Message").is_null() {
@@ -201,10 +191,7 @@ pub unsafe fn dc_mimeparser_parse(
}
}
} else {
optional_field = dc_mimeparser_lookup_optional_field(
mimeparser,
b"Chat-Content\x00" as *const u8 as *const libc::c_char,
);
optional_field = dc_mimeparser_lookup_optional_field(mimeparser, "Chat-Content");
if !optional_field.is_null() && !(*optional_field).fld_value.is_null() {
if strcmp(
(*optional_field).fld_value,
@@ -312,11 +299,7 @@ pub unsafe fn dc_mimeparser_parse(
}
if mimeparser.parts.len() == 1 {
if mimeparser.parts[0].type_0 == 40i32 {
if !dc_mimeparser_lookup_optional_field(
mimeparser,
b"Chat-Voice-Message\x00" as *const u8 as *const libc::c_char,
)
.is_null()
if !dc_mimeparser_lookup_optional_field(mimeparser, "Chat-Voice-Message").is_null()
{
let part_mut = &mut mimeparser.parts[0];
part_mut.type_0 = 41i32
@@ -324,10 +307,7 @@ pub unsafe fn dc_mimeparser_parse(
}
let part = &mimeparser.parts[0];
if part.type_0 == 40i32 || part.type_0 == 41i32 || part.type_0 == 50i32 {
let field_0 = dc_mimeparser_lookup_optional_field(
mimeparser,
b"Chat-Duration\x00" as *const u8 as *const libc::c_char,
);
let field_0 = dc_mimeparser_lookup_optional_field(mimeparser, "Chat-Duration");
if !field_0.is_null() {
let duration_ms: libc::c_int = dc_atoi_null_is_0((*field_0).fld_value);
if duration_ms > 0i32 && duration_ms < 24i32 * 60i32 * 60i32 * 1000i32 {
@@ -338,10 +318,8 @@ pub unsafe fn dc_mimeparser_parse(
}
}
if 0 == mimeparser.decrypting_failed {
let dn_field: *const mailimf_optional_field = dc_mimeparser_lookup_optional_field(
mimeparser,
b"Chat-Disposition-Notification-To\x00" as *const u8 as *const libc::c_char,
);
let dn_field: *const mailimf_optional_field =
dc_mimeparser_lookup_optional_field(mimeparser, "Chat-Disposition-Notification-To");
if !dn_field.is_null() && dc_mimeparser_get_last_nonmeta(mimeparser).is_some() {
let mut mb_list: *mut mailimf_mailbox_list = 0 as *mut mailimf_mailbox_list;
let mut index_0: size_t = 0i32 as size_t;
@@ -459,11 +437,11 @@ pub fn dc_mimeparser_lookup_field(
pub unsafe fn dc_mimeparser_lookup_optional_field(
mimeparser: &dc_mimeparser_t,
field_name: *const libc::c_char,
field_name: &str,
) -> *mut mailimf_optional_field {
let field = mimeparser
.header
.get(as_str(field_name))
.get(field_name)
.map(|v| *v)
.unwrap_or_else(|| std::ptr::null_mut());
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
@@ -1099,7 +1077,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
mimeparser: &mut dc_mimeparser_t,
mime: *mut mailmime,
) -> libc::c_int {
let mut current_block: u64;
let mut ok_to_continue = true;
let old_part_count = mimeparser.parts.len();
let mime_type: libc::c_int;
let mime_data: *mut mailmime_data;
@@ -1112,7 +1090,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
/* must not be free()'d */
let mut decoded_data: *const libc::c_char = 0 as *const libc::c_char;
let mut decoded_data_bytes = 0;
let mut simplifier: Option<dc_simplify_t> = None;
let mut simplifier: Option<Simplify> = None;
if !(mime.is_null() || (*mime).mm_data.mm_single.is_null()) {
mime_type = mailmime_get_mime_type(mime, &mut msg_type, &mut raw_mime);
mime_data = (*mime).mm_data.mm_single;
@@ -1134,7 +1112,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
match mime_type {
60 | 70 => {
if simplifier.is_none() {
simplifier = Some(dc_simplify_t::new());
simplifier = Some(Simplify::new());
}
/* get from `Content-Type: text/...; charset=utf-8`; must not be free()'d */
let charset = mailmime_content_charset_get((*mime).mm_content_type);
@@ -1156,14 +1134,12 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
info!(mimeparser.context, 0, "decoded message: '{}'", res);
if res.is_empty() {
/* no error - but nothing to add */
current_block = 8795901732489102124;
ok_to_continue = false;
} else {
let b = res.as_bytes();
decoded_data = b.as_ptr() as *const libc::c_char;
decoded_data_bytes = b.len();
std::mem::forget(res);
current_block = 17788412896529399552;
}
} else {
warn!(
@@ -1173,45 +1149,35 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
decoded_data_bytes as libc::c_int,
as_str(charset),
);
current_block = 17788412896529399552;
}
} else {
current_block = 17788412896529399552;
}
match current_block {
8795901732489102124 => {}
_ => {
/* check header directly as is_send_by_messenger is not yet set up */
let is_msgrmsg = (!dc_mimeparser_lookup_optional_field(
&mimeparser,
b"Chat-Version\x00" as *const u8 as *const libc::c_char,
)
.is_null())
as libc::c_int;
if ok_to_continue {
/* check header directly as is_send_by_messenger is not yet set up */
let is_msgrmsg =
(!dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Version")
.is_null()) as libc::c_int;
let simplified_txt = simplifier.unwrap().simplify(
decoded_data,
decoded_data_bytes as libc::c_int,
if mime_type == 70i32 { 1i32 } else { 0i32 },
is_msgrmsg,
);
if !simplified_txt.is_null()
&& 0 != *simplified_txt.offset(0isize) as libc::c_int
{
let mut part = dc_mimepart_new();
part.type_0 = 10i32;
part.int_mimetype = mime_type;
part.msg = simplified_txt;
part.msg_raw =
strndup(decoded_data, decoded_data_bytes as libc::c_ulong);
do_add_single_part(mimeparser, part);
} else {
free(simplified_txt as *mut libc::c_void);
}
if 0 != simplifier.unwrap().is_forwarded {
mimeparser.is_forwarded = 1i32
}
current_block = 10261677128829721533;
let simplified_txt = simplifier.unwrap().simplify(
decoded_data,
decoded_data_bytes as libc::c_int,
mime_type == 70i32,
is_msgrmsg,
);
if !simplified_txt.is_null()
&& 0 != *simplified_txt.offset(0isize) as libc::c_int
{
let mut part = dc_mimepart_new();
part.type_0 = 10i32;
part.int_mimetype = mime_type;
part.msg = simplified_txt;
part.msg_raw =
strndup(decoded_data, decoded_data_bytes as libc::c_ulong);
do_add_single_part(mimeparser, part);
} else {
free(simplified_txt as *mut libc::c_void);
}
if simplifier.unwrap().is_forwarded {
mimeparser.is_forwarded = 1i32
}
}
}
@@ -1314,76 +1280,61 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
b"file.%s\x00" as *const u8 as *const libc::c_char,
(*(*mime).mm_content_type).ct_subtype,
);
current_block = 17019156190352891614;
} else {
current_block = 8795901732489102124;
ok_to_continue = false;
}
} else {
current_block = 17019156190352891614;
}
match current_block {
8795901732489102124 => {}
_ => {
if strncmp(
desired_filename,
b"location\x00" as *const u8 as *const libc::c_char,
8,
if ok_to_continue {
if strncmp(
desired_filename,
b"location\x00" as *const u8 as *const libc::c_char,
8,
) == 0i32
&& strncmp(
desired_filename
.offset(strlen(desired_filename) as isize)
.offset(-4isize),
b".kml\x00" as *const u8 as *const libc::c_char,
4,
) == 0i32
&& strncmp(
desired_filename
.offset(strlen(desired_filename) as isize)
.offset(-4isize),
b".kml\x00" as *const u8 as *const libc::c_char,
4,
) == 0i32
{
mimeparser.location_kml = Some(dc_kml_parse(
mimeparser.context,
decoded_data,
decoded_data_bytes,
));
current_block = 8795901732489102124;
} else if strncmp(
desired_filename,
b"message\x00" as *const u8 as *const libc::c_char,
7,
{
mimeparser.location_kml = Some(dc_kml_parse(
mimeparser.context,
decoded_data,
decoded_data_bytes,
));
} else if strncmp(
desired_filename,
b"message\x00" as *const u8 as *const libc::c_char,
7,
) == 0i32
&& strncmp(
desired_filename
.offset(strlen(desired_filename) as isize)
.offset(-4isize),
b".kml\x00" as *const u8 as *const libc::c_char,
4,
) == 0i32
&& strncmp(
desired_filename
.offset(strlen(desired_filename) as isize)
.offset(-4isize),
b".kml\x00" as *const u8 as *const libc::c_char,
4,
) == 0i32
{
mimeparser.message_kml = Some(dc_kml_parse(
mimeparser.context,
decoded_data,
decoded_data_bytes,
));
current_block = 8795901732489102124;
} else {
dc_replace_bad_utf8_chars(desired_filename);
do_add_single_file_part(
mimeparser,
msg_type,
mime_type,
raw_mime,
decoded_data,
decoded_data_bytes,
desired_filename,
);
current_block = 10261677128829721533;
}
{
mimeparser.message_kml = Some(dc_kml_parse(
mimeparser.context,
decoded_data,
decoded_data_bytes,
));
} else {
dc_replace_bad_utf8_chars(desired_filename);
do_add_single_file_part(
mimeparser,
msg_type,
mime_type,
raw_mime,
decoded_data,
decoded_data_bytes,
desired_filename,
);
}
}
}
_ => {
current_block = 10261677128829721533;
}
}
match current_block {
8795901732489102124 => {}
_ => {}
}
}
@@ -1396,11 +1347,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
free(file_suffix as *mut libc::c_void);
free(desired_filename as *mut libc::c_void);
free(raw_mime as *mut libc::c_void);
return if mimeparser.parts.len() > old_part_count {
1
} else {
0
};
(mimeparser.parts.len() > old_part_count) as libc::c_int
}
#[allow(non_snake_case)]
@@ -1436,16 +1383,15 @@ unsafe fn do_add_single_file_part(
part.param.set(Param::File, as_str(pathNfilename));
part.param.set(Param::MimeType, as_str(raw_mime));
if mime_type == 80 {
let mut w = 0;
let mut h = 0;
if 0 != dc_get_filemeta(
decoded_data as *const libc::c_void,
decoded_data_bytes,
&mut w,
&mut h,
) {
part.param.set_int(Param::Width, w as i32);
part.param.set_int(Param::Height, h as i32);
assert!(!decoded_data.is_null(), "invalid image data");
let data = std::slice::from_raw_parts(
decoded_data as *const u8,
decoded_data_bytes as usize,
);
if let Ok((width, height)) = dc_get_filemeta(data) {
part.param.set_int(Param::Width, width as i32);
part.param.set_int(Param::Height, height as i32);
}
}
do_add_single_part(parser, part);
@@ -1552,10 +1498,8 @@ pub unsafe fn dc_mimeparser_is_mailinglist_message(mimeparser: &dc_mimeparser_t)
if !dc_mimeparser_lookup_field(&mimeparser, "List-Id").is_null() {
return 1i32;
}
let precedence: *mut mailimf_optional_field = dc_mimeparser_lookup_optional_field(
mimeparser,
b"Precedence\x00" as *const u8 as *const libc::c_char,
);
let precedence: *mut mailimf_optional_field =
dc_mimeparser_lookup_optional_field(mimeparser, "Precedence");
if !precedence.is_null() {
if strcasecmp(
(*precedence).fld_value,

View File

@@ -1,7 +1,7 @@
use crate::constants::*;
use crate::context::*;
use crate::dc_job::*;
use crate::dc_msg::*;
use crate::job::*;
use crate::param::Params;
pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
@@ -27,13 +27,19 @@ pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u3
}
if dc_is_mvbox(context, folder) {
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_STAY);
dc_update_msg_move_state(context, (*msg).rfc724_mid, MoveState::Stay);
}
// 1 = dc message, 2 = reply to dc message
if 0 != (*msg).is_dc_message {
dc_job_add(context, 200, (*msg).id as libc::c_int, Params::new(), 0);
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_MOVING);
job_add(
context,
Action::MoveMsg,
(*msg).id as libc::c_int,
Params::new(),
0,
);
dc_update_msg_move_state(context, (*msg).rfc724_mid, MoveState::Moving);
}
dc_msg_unref(msg);

File diff suppressed because it is too large Load Diff

View File

@@ -1,346 +0,0 @@
use percent_encoding::percent_decode_str;
use crate::contact::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_lot::*;
use crate::dc_strencode::*;
use crate::dc_tools::*;
use crate::key::*;
use crate::param::*;
use crate::peerstate::*;
use crate::types::*;
use crate::x::*;
// out-of-band verification
// id=contact
// text1=groupname
// id=contact
// id=contact
// test1=formatted fingerprint
// id=contact
// text1=text
// text1=URL
// text1=error string
pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc_lot_t {
let mut current_block: u64;
let mut payload: *mut libc::c_char = 0 as *mut libc::c_char;
// must be normalized, if set
let mut addr: *mut libc::c_char = 0 as *mut libc::c_char;
// must be normalized, if set
let mut fingerprint: *mut libc::c_char = 0 as *mut libc::c_char;
let mut name: *mut libc::c_char = 0 as *mut libc::c_char;
let mut invitenumber: *mut libc::c_char = 0 as *mut libc::c_char;
let mut auth: *mut libc::c_char = 0 as *mut libc::c_char;
let mut qr_parsed: *mut dc_lot_t = dc_lot_new();
let mut chat_id: uint32_t = 0i32 as uint32_t;
let mut device_msg: *mut libc::c_char = 0 as *mut libc::c_char;
let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char;
let mut grpname: *mut libc::c_char = 0 as *mut libc::c_char;
(*qr_parsed).state = 0i32;
if !qr.is_null() {
info!(context, 0, "Scanned QR code: {}", as_str(qr),);
/* split parameters from the qr code
------------------------------------ */
if strncasecmp(
qr,
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
strlen(b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
payload =
dc_strdup(&*qr.offset(strlen(
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
) as isize));
let mut fragment: *mut libc::c_char = strchr(payload, '#' as i32);
if !fragment.is_null() {
*fragment = 0i32 as libc::c_char;
fragment = fragment.offset(1isize);
let param: Params = as_str(fragment).parse().expect("invalid params");
addr = param
.get(Param::Forwarded)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
if !addr.is_null() {
if let Some(ref name_enc) = param.get(Param::SetLongitude) {
let name_r = percent_decode_str(name_enc)
.decode_utf8()
.expect("invalid name");
name = normalize_name(name_r).strdup();
}
invitenumber = param
.get(Param::ProfileImage)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
auth = param
.get(Param::Auth)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
grpid = param
.get(Param::GroupId)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
if !grpid.is_null() {
if let Some(grpname_enc) = param.get(Param::GroupName) {
let grpname_r = percent_decode_str(grpname_enc)
.decode_utf8()
.expect("invalid groupname");
grpname = grpname_r.strdup();
}
}
}
}
fingerprint = dc_normalize_fingerprint_c(payload);
current_block = 5023038348526654800;
} else if strncasecmp(
qr,
b"mailto:\x00" as *const u8 as *const libc::c_char,
strlen(b"mailto:\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
payload = dc_strdup(
&*qr.offset(strlen(b"mailto:\x00" as *const u8 as *const libc::c_char) as isize),
);
let query: *mut libc::c_char = strchr(payload, '?' as i32);
if !query.is_null() {
*query = 0i32 as libc::c_char
}
addr = dc_strdup(payload);
current_block = 5023038348526654800;
} else if strncasecmp(
qr,
b"SMTP:\x00" as *const u8 as *const libc::c_char,
strlen(b"SMTP:\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
payload = dc_strdup(
&*qr.offset(strlen(b"SMTP:\x00" as *const u8 as *const libc::c_char) as isize),
);
let colon: *mut libc::c_char = strchr(payload, ':' as i32);
if !colon.is_null() {
*colon = 0i32 as libc::c_char
}
addr = dc_strdup(payload);
current_block = 5023038348526654800;
} else if strncasecmp(
qr,
b"MATMSG:\x00" as *const u8 as *const libc::c_char,
strlen(b"MATMSG:\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
/* scheme: `MATMSG:TO:addr...;SUB:subject...;BODY:body...;` - there may or may not be linebreaks after the fields */
/* does not work when the text `TO:` is used in subject/body _and_ TO: is not the first field. we ignore this case. */
let to: *mut libc::c_char = strstr(qr, b"TO:\x00" as *const u8 as *const libc::c_char);
if !to.is_null() {
addr = dc_strdup(&mut *to.offset(3isize));
let semicolon: *mut libc::c_char = strchr(addr, ';' as i32);
if !semicolon.is_null() {
*semicolon = 0i32 as libc::c_char
}
current_block = 5023038348526654800;
} else {
(*qr_parsed).state = 400i32;
(*qr_parsed).text1 =
dc_strdup(b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char);
current_block = 16562876845594826114;
}
} else {
if strncasecmp(
qr,
b"BEGIN:VCARD\x00" as *const u8 as *const libc::c_char,
strlen(b"BEGIN:VCARD\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
let lines = dc_split_into_lines(qr);
for &key in &lines {
dc_trim(key);
let mut value: *mut libc::c_char = strchr(key, ':' as i32);
if !value.is_null() {
*value = 0i32 as libc::c_char;
value = value.offset(1isize);
let mut semicolon_0: *mut libc::c_char = strchr(key, ';' as i32);
if !semicolon_0.is_null() {
*semicolon_0 = 0i32 as libc::c_char
}
if strcasecmp(key, b"EMAIL\x00" as *const u8 as *const libc::c_char) == 0i32
{
semicolon_0 = strchr(value, ';' as i32);
if !semicolon_0.is_null() {
*semicolon_0 = 0i32 as libc::c_char
}
addr = dc_strdup(value)
} else if strcasecmp(key, b"N\x00" as *const u8 as *const libc::c_char)
== 0i32
{
semicolon_0 = strchr(value, ';' as i32);
if !semicolon_0.is_null() {
semicolon_0 = strchr(semicolon_0.offset(1isize), ';' as i32);
if !semicolon_0.is_null() {
*semicolon_0 = 0i32 as libc::c_char
}
}
name = dc_strdup(value);
dc_str_replace(
&mut name,
b";\x00" as *const u8 as *const libc::c_char,
b",\x00" as *const u8 as *const libc::c_char,
);
name = normalize_name(as_str(name)).strdup();
}
}
}
dc_free_splitted_lines(lines);
}
current_block = 5023038348526654800;
}
match current_block {
16562876845594826114 => {}
_ => {
/* check the parameters
---------------------- */
if !addr.is_null() {
/* urldecoding is needed at least for OPENPGP4FPR but should not hurt in the other cases */
let mut temp: *mut libc::c_char = dc_urldecode(addr);
free(addr as *mut libc::c_void);
addr = temp;
temp = addr_normalize(as_str(addr)).strdup();
free(addr as *mut libc::c_void);
addr = temp;
if !may_be_valid_addr(as_str(addr)) {
(*qr_parsed).state = 400i32;
(*qr_parsed).text1 = dc_strdup(
b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char,
);
current_block = 16562876845594826114;
} else {
current_block = 14116432890150942211;
}
} else {
current_block = 14116432890150942211;
}
match current_block {
16562876845594826114 => {}
_ => {
if !fingerprint.is_null() {
if strlen(fingerprint) != 40 {
(*qr_parsed).state = 400i32;
(*qr_parsed).text1 = dc_strdup(
b"Bad fingerprint length in QR code.\x00" as *const u8
as *const libc::c_char,
);
current_block = 16562876845594826114;
} else {
current_block = 5409161009579131794;
}
} else {
current_block = 5409161009579131794;
}
match current_block {
16562876845594826114 => {}
_ => {
if !fingerprint.is_null() {
let peerstate = Peerstate::from_fingerprint(
context,
&context.sql,
as_str(fingerprint),
);
if addr.is_null() || invitenumber.is_null() || auth.is_null() {
if let Some(peerstate) = peerstate {
(*qr_parsed).state = 210i32;
let addr = peerstate
.addr
.as_ref()
.map(|s| s.as_str())
.unwrap_or_else(|| "");
(*qr_parsed).id = Contact::add_or_lookup(
context,
"",
addr,
Origin::UnhandledQrScan,
)
.map(|(id, _)| id)
.unwrap_or_default();
dc_create_or_lookup_nchat_by_contact_id(
context,
(*qr_parsed).id,
2i32,
&mut chat_id,
0 as *mut libc::c_int,
);
device_msg = dc_mprintf(
b"%s verified.\x00" as *const u8
as *const libc::c_char,
peerstate.addr,
)
} else {
(*qr_parsed).text1 =
dc_format_fingerprint_c(fingerprint);
(*qr_parsed).state = 230i32
}
} else {
if !grpid.is_null() && !grpname.is_null() {
(*qr_parsed).state = 202i32;
(*qr_parsed).text1 = dc_strdup(grpname);
(*qr_parsed).text2 = dc_strdup(grpid)
} else {
(*qr_parsed).state = 200i32
}
(*qr_parsed).id = Contact::add_or_lookup(
context,
as_str(name),
as_str(addr),
Origin::UnhandledQrScan,
)
.map(|(id, _)| id)
.unwrap_or_default();
(*qr_parsed).fingerprint = dc_strdup(fingerprint);
(*qr_parsed).invitenumber = dc_strdup(invitenumber);
(*qr_parsed).auth = dc_strdup(auth)
}
} else if !addr.is_null() {
(*qr_parsed).state = 320i32;
(*qr_parsed).id = Contact::add_or_lookup(
context,
as_str(name),
as_str(addr),
Origin::UnhandledQrScan,
)
.map(|(id, _)| id)
.unwrap_or_default();
} else if strstr(
qr,
b"http://\x00" as *const u8 as *const libc::c_char,
) == qr as *mut libc::c_char
|| strstr(
qr,
b"https://\x00" as *const u8 as *const libc::c_char,
) == qr as *mut libc::c_char
{
(*qr_parsed).state = 332i32;
(*qr_parsed).text1 = dc_strdup(qr)
} else {
(*qr_parsed).state = 330i32;
(*qr_parsed).text1 = dc_strdup(qr)
}
if !device_msg.is_null() {
dc_add_device_msg(context, chat_id, device_msg);
}
}
}
}
}
}
}
}
free(addr as *mut libc::c_void);
free(fingerprint as *mut libc::c_void);
free(payload as *mut libc::c_void);
free(name as *mut libc::c_void);
free(invitenumber as *mut libc::c_void);
free(auth as *mut libc::c_void);
free(device_msg as *mut libc::c_void);
free(grpname as *mut libc::c_void);
free(grpid as *mut libc::c_void);
qr_parsed
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,22 @@
use std::ffi::CString;
use mmime::mailimf_types::*;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use crate::aheader::EncryptPreference;
use crate::chat::{self, Chat};
use crate::constants::*;
use crate::contact::*;
use crate::context::Context;
use crate::dc_array::*;
use crate::dc_chat::*;
use crate::dc_configure::*;
use crate::dc_e2ee::*;
use crate::dc_lot::*;
use crate::dc_mimeparser::*;
use crate::dc_msg::*;
use crate::dc_qr::*;
use crate::dc_strencode::*;
use crate::dc_token::*;
use crate::dc_tools::*;
use crate::key::*;
use crate::lot::LotState;
use crate::param::*;
use crate::peerstate::*;
use crate::qr::check_qr;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
@@ -37,12 +33,9 @@ pub unsafe fn dc_get_securejoin_qr(
let mut fingerprint = 0 as *mut libc::c_char;
let mut invitenumber: *mut libc::c_char;
let mut auth: *mut libc::c_char;
let mut chat = 0 as *mut Chat;
let mut group_name = 0 as *mut libc::c_char;
let mut group_name_urlencoded = 0 as *mut libc::c_char;
let mut qr: Option<String> = None;
dc_ensure_secret_key_exists(context);
dc_ensure_secret_key_exists(context).ok();
invitenumber = dc_token_lookup(context, DC_TOKEN_INVITENUMBER, group_chat_id);
if invitenumber.is_null() {
invitenumber = dc_create_id().strdup();
@@ -55,13 +48,10 @@ pub unsafe fn dc_get_securejoin_qr(
}
let self_addr = context.sql.get_config(context, "configured_addr");
let cleanup = |fingerprint, chat, group_name, group_name_urlencoded| {
let cleanup = |fingerprint| {
free(fingerprint as *mut libc::c_void);
free(invitenumber as *mut libc::c_void);
free(auth as *mut libc::c_void);
dc_chat_unref(chat);
free(group_name as *mut libc::c_void);
free(group_name_urlencoded as *mut libc::c_void);
if let Some(qr) = qr {
qr.strdup()
@@ -72,7 +62,7 @@ pub unsafe fn dc_get_securejoin_qr(
if self_addr.is_none() {
error!(context, 0, "Not configured, cannot generate QR code.",);
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
return cleanup(fingerprint);
}
let self_addr = self_addr.unwrap();
@@ -84,34 +74,34 @@ pub unsafe fn dc_get_securejoin_qr(
fingerprint = get_self_fingerprint(context);
if fingerprint.is_null() {
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
return cleanup(fingerprint);
}
let self_addr_urlencoded = utf8_percent_encode(&self_addr, NON_ALPHANUMERIC).to_string();
let self_name_urlencoded = utf8_percent_encode(&self_name, NON_ALPHANUMERIC).to_string();
qr = if 0 != group_chat_id {
chat = dc_get_chat(context, group_chat_id);
if chat.is_null() {
if let Ok(chat) = Chat::load_from_db(context, group_chat_id) {
let group_name = chat.get_name();
let group_name_urlencoded =
utf8_percent_encode(&group_name, NON_ALPHANUMERIC).to_string();
Some(format!(
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
as_str(fingerprint),
self_addr_urlencoded,
&group_name_urlencoded,
&chat.grpid,
as_str(invitenumber),
as_str(auth),
))
} else {
error!(
context,
0, "Cannot get QR-code for chat-id {}", group_chat_id,
);
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
return cleanup(fingerprint);
}
group_name = dc_chat_get_name(chat);
group_name_urlencoded = dc_urlencode(group_name);
Some(format!(
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
as_str(fingerprint),
self_addr_urlencoded,
as_str(group_name_urlencoded),
as_str((*chat).grpid),
as_str(invitenumber),
as_str(auth),
))
} else {
Some(format!(
"OPENPGP4FPR:{}#a={}&n={}&i={}&s={}",
@@ -125,7 +115,7 @@ pub unsafe fn dc_get_securejoin_qr(
info!(context, 0, "Generated QR code: {}", qr.as_ref().unwrap());
cleanup(fingerprint, chat, group_name, group_name_urlencoded)
cleanup(fingerprint)
}
fn get_self_fingerprint(context: &Context) -> *mut libc::c_char {
@@ -147,16 +137,19 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
let ongoing_allocated: libc::c_int;
let mut contact_chat_id: uint32_t = 0i32 as uint32_t;
let mut join_vg: libc::c_int = 0i32;
let mut qr_scan: *mut dc_lot_t = 0 as *mut dc_lot_t;
info!(context, 0, "Requesting secure-join ...",);
dc_ensure_secret_key_exists(context);
dc_ensure_secret_key_exists(context).ok();
ongoing_allocated = dc_alloc_ongoing(context);
if !(ongoing_allocated == 0i32) {
qr_scan = dc_check_qr(context, qr);
if qr_scan.is_null() || (*qr_scan).state != 200i32 && (*qr_scan).state != 202i32 {
let qr_scan = check_qr(context, as_str(qr));
if qr_scan.state != LotState::QrAskVerifyContact
&& qr_scan.state != LotState::QrAskVerifyGroup
{
error!(context, 0, "Unknown QR code.",);
} else {
contact_chat_id = dc_create_chat_by_contact_id(context, (*qr_scan).id);
contact_chat_id = chat::create_by_contact_id(context, qr_scan.id).unwrap_or_default();
if contact_chat_id == 0i32 as libc::c_uint {
error!(context, 0, "Unknown contact.",);
} else if !(context
@@ -166,17 +159,28 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
.unwrap()
.shall_stop_ongoing)
{
join_vg = ((*qr_scan).state == 202i32) as libc::c_int;
join_vg = (qr_scan.get_state() == LotState::QrAskVerifyGroup) as libc::c_int;
{
let bob_a = context.bob.clone();
let mut bob = bob_a.write().unwrap();
let mut bob = context.bob.write().unwrap();
bob.status = 0;
bob.qr_scan = qr_scan;
bob.qr_scan = Some(qr_scan);
}
if 0 != fingerprint_equals_sender(context, (*qr_scan).fingerprint, contact_chat_id)
{
if 0 != fingerprint_equals_sender(
context,
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.fingerprint
.as_ref()
.unwrap(),
contact_chat_id,
) {
info!(context, 0, "Taking protocol shortcut.");
context.bob.clone().write().unwrap().expects = 6;
context.bob.write().unwrap().expects = 6;
context.call_cb(
Event::SECUREJOIN_JOINER_PROGRESS,
chat_id_2_contact_id(context, contact_chat_id) as uintptr_t,
@@ -191,17 +195,37 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
} else {
b"vc-request-with-auth\x00" as *const u8 as *const libc::c_char
},
(*qr_scan).auth,
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.auth
.as_ref()
.unwrap()
.to_string(),
own_fingerprint,
if 0 != join_vg {
(*qr_scan).text2
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.text2
.as_ref()
.unwrap()
.to_string()
} else {
0 as *mut libc::c_char
"".to_string()
},
);
free(own_fingerprint as *mut libc::c_void);
} else {
context.bob.clone().write().unwrap().expects = 2;
context.bob.write().unwrap().expects = 2;
send_handshake_msg(
context,
contact_chat_id,
@@ -210,9 +234,18 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
} else {
b"vc-request\x00" as *const u8 as *const libc::c_char
},
(*qr_scan).invitenumber,
0 as *const libc::c_char,
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.invitenumber
.as_ref()
.unwrap(),
0 as *const libc::c_char,
"",
);
}
@@ -229,25 +262,23 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
}
}
}
let bob_a = context.bob.clone();
let mut bob = bob_a.write().unwrap();
let mut bob = context.bob.write().unwrap();
bob.expects = 0;
if bob.status == 1 {
if 0 != join_vg {
ret_chat_id = dc_get_chat_id_by_grpid(
ret_chat_id = chat::get_chat_id_by_grpid(
context,
(*qr_scan).text2,
0 as *mut libc::c_int,
bob.qr_scan.as_ref().unwrap().text2.as_ref().unwrap(),
None,
0 as *mut libc::c_int,
) as libc::c_int
} else {
ret_chat_id = contact_chat_id as libc::c_int
}
}
bob.qr_scan = std::ptr::null_mut();
dc_lot_unref(qr_scan);
bob.qr_scan = None;
if 0 != ongoing_allocated {
dc_free_ongoing(context);
}
@@ -258,9 +289,9 @@ unsafe fn send_handshake_msg(
context: &Context,
contact_chat_id: uint32_t,
step: *const libc::c_char,
param2: *const libc::c_char,
param2: impl AsRef<str>,
fingerprint: *const libc::c_char,
grpid: *const libc::c_char,
grpid: impl AsRef<str>,
) {
let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context);
(*msg).type_0 = Viewtype::Text;
@@ -272,14 +303,14 @@ unsafe fn send_handshake_msg(
} else {
(*msg).param.set(Param::Arg, as_str(step));
}
if !param2.is_null() {
(*msg).param.set(Param::Arg2, as_str(param2));
if !param2.as_ref().is_empty() {
(*msg).param.set(Param::Arg2, param2);
}
if !fingerprint.is_null() {
(*msg).param.set(Param::Arg3, as_str(fingerprint));
}
if !grpid.is_null() {
(*msg).param.set(Param::Arg4, as_str(grpid));
if !grpid.as_ref().is_empty() {
(*msg).param.set(Param::Arg4, grpid.as_ref());
}
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(step, b"vc-request\x00" as *const u8 as *const libc::c_char) == 0i32
@@ -291,37 +322,33 @@ unsafe fn send_handshake_msg(
} else {
(*msg).param.set_int(Param::GuranteeE2ee, 1);
}
dc_send_msg(context, contact_chat_id, msg);
// TODO. handle cleanup on error
chat::send_msg(context, contact_chat_id, msg).unwrap();
dc_msg_unref(msg);
}
unsafe fn chat_id_2_contact_id(context: &Context, contact_chat_id: uint32_t) -> uint32_t {
let mut contact_id: uint32_t = 0i32 as uint32_t;
let contacts: *mut dc_array_t = dc_get_chat_contacts(context, contact_chat_id);
if !(dc_array_get_cnt(contacts) != 1) {
contact_id = dc_array_get_id(contacts, 0i32 as size_t)
let contacts = chat::get_chat_contacts(context, contact_chat_id);
if contacts.len() == 1 {
contacts[0]
} else {
0
}
dc_array_unref(contacts);
contact_id
}
unsafe fn fingerprint_equals_sender(
context: &Context,
fingerprint: *const libc::c_char,
contact_chat_id: uint32_t,
fingerprint: impl AsRef<str>,
contact_chat_id: u32,
) -> libc::c_int {
if fingerprint.is_null() {
return 0;
}
let mut fingerprint_equal: libc::c_int = 0i32;
let contacts = dc_get_chat_contacts(context, contact_chat_id);
let mut fingerprint_equal = 0;
let contacts = chat::get_chat_contacts(context, contact_chat_id);
if !(dc_array_get_cnt(contacts) != 1) {
if let Ok(contact) = Contact::load_from_db(context, dc_array_get_id(contacts, 0)) {
if contacts.len() == 1 {
if let Ok(contact) = Contact::load_from_db(context, contacts[0]) {
if let Some(peerstate) = Peerstate::from_addr(context, &context.sql, contact.get_addr())
{
let fingerprint_normalized = dc_normalize_fingerprint(as_str(fingerprint));
let fingerprint_normalized = dc_normalize_fingerprint(fingerprint.as_ref());
if peerstate.public_key_fingerprint.is_some()
&& &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap()
{
@@ -332,7 +359,6 @@ unsafe fn fingerprint_equals_sender(
return 0;
}
}
dc_array_unref(contacts);
fingerprint_equal
}
@@ -346,12 +372,10 @@ pub unsafe fn dc_handle_securejoin_handshake(
let mut current_block: u64;
let step: *const libc::c_char;
let join_vg: libc::c_int;
let mut scanned_fingerprint_of_alice: *mut libc::c_char = 0 as *mut libc::c_char;
let mut auth: *mut libc::c_char = 0 as *mut libc::c_char;
let mut own_fingerprint: *mut libc::c_char = 0 as *mut libc::c_char;
let mut contact_chat_id: uint32_t = 0i32 as uint32_t;
let mut contact_chat_id_blocked: libc::c_int = 0i32;
let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char;
let contact_chat_id: u32;
let contact_chat_id_blocked: Blocked;
let mut grpid = "".to_string();
let mut ret: libc::c_int = 0i32;
if !(contact_id <= 9i32 as libc::c_uint) {
@@ -365,15 +389,13 @@ pub unsafe fn dc_handle_securejoin_handshake(
);
join_vg = (strncmp(step, b"vg-\x00" as *const u8 as *const libc::c_char, 3) == 0)
as libc::c_int;
dc_create_or_lookup_nchat_by_contact_id(
context,
contact_id,
0i32,
&mut contact_chat_id,
&mut contact_chat_id_blocked,
);
if 0 != contact_chat_id_blocked {
dc_unblock_chat(context, contact_chat_id);
let (id, bl) = chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not)
.unwrap_or_default();
contact_chat_id = id;
contact_chat_id_blocked = bl;
if Blocked::Not != contact_chat_id_blocked {
chat::unblock(context, contact_chat_id);
}
ret = 0x2i32;
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
@@ -411,9 +433,9 @@ pub unsafe fn dc_handle_securejoin_handshake(
} else {
b"vc-auth-required\x00" as *const u8 as *const libc::c_char
},
"",
0 as *const libc::c_char,
0 as *const libc::c_char,
0 as *const libc::c_char,
"",
);
current_block = 10256747982273457880;
}
@@ -427,10 +449,11 @@ pub unsafe fn dc_handle_securejoin_handshake(
) == 0i32
{
let cond = {
let bob_a = context.bob.clone();
let bob = bob_a.read().unwrap();
let scan = bob.qr_scan;
scan.is_null() || bob.expects != 2 || 0 != join_vg && (*scan).state != 202
let bob = context.bob.read().unwrap();
let scan = bob.qr_scan.as_ref();
scan.is_none()
|| bob.expects != 2
|| 0 != join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
};
if cond {
@@ -438,15 +461,44 @@ pub unsafe fn dc_handle_securejoin_handshake(
// no error, just aborted somehow or a mail from another handshake
current_block = 4378276786830486580;
} else {
{
let scan = context.bob.clone().read().unwrap().qr_scan;
scanned_fingerprint_of_alice = dc_strdup((*scan).fingerprint);
auth = dc_strdup((*scan).auth);
if 0 != join_vg {
grpid = dc_strdup((*scan).text2)
}
let scanned_fingerprint_of_alice = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.fingerprint
.as_ref()
.unwrap()
.to_string();
let auth = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.auth
.as_ref()
.unwrap()
.to_string();
if 0 != join_vg {
grpid = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.text2
.as_ref()
.unwrap()
.to_string();
}
if 0 == encrypted_and_signed(mimeparser, scanned_fingerprint_of_alice) {
if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) {
could_not_establish_secure_connection(
context,
contact_chat_id,
@@ -461,7 +513,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
} else if 0
== fingerprint_equals_sender(
context,
scanned_fingerprint_of_alice,
&scanned_fingerprint_of_alice,
contact_chat_id,
)
{
@@ -481,7 +533,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
contact_id as uintptr_t,
400i32 as uintptr_t,
);
context.bob.clone().write().unwrap().expects = 6;
context.bob.write().unwrap().expects = 6;
send_handshake_msg(
context,
@@ -513,8 +565,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
==== Step 6 in "Out-of-band verified groups" protocol ====
============================================================ */
// verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob
let fingerprint: *const libc::c_char;
fingerprint = lookup_field(mimeparser, "Secure-Join-Fingerprint");
let fingerprint = lookup_field(mimeparser, "Secure-Join-Fingerprint");
if fingerprint.is_null() {
could_not_establish_secure_connection(
context,
@@ -522,14 +573,16 @@ pub unsafe fn dc_handle_securejoin_handshake(
b"Fingerprint not provided.\x00" as *const u8 as *const libc::c_char,
);
current_block = 4378276786830486580;
} else if 0 == encrypted_and_signed(mimeparser, fingerprint) {
} else if !encrypted_and_signed(mimeparser, as_str(fingerprint)) {
could_not_establish_secure_connection(
context,
contact_chat_id,
b"Auth not encrypted.\x00" as *const u8 as *const libc::c_char,
);
current_block = 4378276786830486580;
} else if 0 == fingerprint_equals_sender(context, fingerprint, contact_chat_id) {
} else if 0
== fingerprint_equals_sender(context, as_str(fingerprint), contact_chat_id)
{
could_not_establish_secure_connection(
context,
contact_chat_id,
@@ -556,7 +609,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
b"Auth invalid.\x00" as *const u8 as *const libc::c_char,
);
current_block = 4378276786830486580;
} else if 0 == mark_peer_as_verified(context, fingerprint) {
} else if 0 == mark_peer_as_verified(context, as_str(fingerprint)) {
could_not_establish_secure_connection(
context,
contact_chat_id,
@@ -583,18 +636,18 @@ pub unsafe fn dc_handle_securejoin_handshake(
600i32 as uintptr_t,
);
if 0 != join_vg {
grpid = dc_strdup(lookup_field(mimeparser, "Secure-Join-Group"));
let group_chat_id: uint32_t = dc_get_chat_id_by_grpid(
grpid = to_string(lookup_field(mimeparser, "Secure-Join-Group"));
let group_chat_id: uint32_t = chat::get_chat_id_by_grpid(
context,
grpid,
0 as *mut libc::c_int,
&grpid,
None,
0 as *mut libc::c_int,
);
if group_chat_id == 0i32 as libc::c_uint {
error!(context, 0, "Chat {} not found.", as_str(grpid),);
error!(context, 0, "Chat {} not found.", &grpid);
current_block = 4378276786830486580;
} else {
dc_add_contact_to_chat_ex(
chat::add_contact_to_chat_ex(
context,
group_chat_id,
contact_id,
@@ -607,9 +660,9 @@ pub unsafe fn dc_handle_securejoin_handshake(
context,
contact_chat_id,
b"vc-contact-confirm\x00" as *const u8 as *const libc::c_char,
"",
0 as *const libc::c_char,
0 as *const libc::c_char,
0 as *const libc::c_char,
"",
);
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
@@ -632,13 +685,15 @@ pub unsafe fn dc_handle_securejoin_handshake(
if 0 != join_vg {
ret = 0x1i32
}
if context.bob.clone().read().unwrap().expects != 6 {
if context.bob.read().unwrap().expects != 6 {
info!(context, 0, "Message belongs to a different handshake.",);
current_block = 4378276786830486580;
} else {
let cond = {
let scan = context.bob.clone().read().unwrap().qr_scan;
scan.is_null() || 0 != join_vg && (*scan).state != 202
let bob = context.bob.read().unwrap();
let scan = bob.qr_scan.as_ref();
scan.is_none()
|| 0 != join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
};
if cond {
warn!(
@@ -647,20 +702,39 @@ pub unsafe fn dc_handle_securejoin_handshake(
);
current_block = 4378276786830486580;
} else {
{
let scan = context.bob.clone().read().unwrap().qr_scan;
scanned_fingerprint_of_alice = dc_strdup((*scan).fingerprint);
if 0 != join_vg {
grpid = dc_strdup((*scan).text2)
}
let scanned_fingerprint_of_alice = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.fingerprint
.as_ref()
.unwrap()
.to_string();
if 0 != join_vg {
grpid = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.text2
.as_ref()
.unwrap()
.to_string();
}
let mut vg_expect_encrypted: libc::c_int = 1i32;
if 0 != join_vg {
let mut is_verified_group: libc::c_int = 0i32;
dc_get_chat_id_by_grpid(
chat::get_chat_id_by_grpid(
context,
grpid,
0 as *mut libc::c_int,
None,
&mut is_verified_group,
);
if 0 == is_verified_group {
@@ -668,7 +742,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
}
}
if 0 != vg_expect_encrypted {
if 0 == encrypted_and_signed(mimeparser, scanned_fingerprint_of_alice) {
if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) {
could_not_establish_secure_connection(
context,
contact_chat_id,
@@ -686,8 +760,10 @@ pub unsafe fn dc_handle_securejoin_handshake(
match current_block {
4378276786830486580 => {}
_ => {
if 0 == mark_peer_as_verified(context, scanned_fingerprint_of_alice)
{
if 0 == mark_peer_as_verified(
context,
&scanned_fingerprint_of_alice,
) {
could_not_establish_secure_connection(
context,
contact_chat_id,
@@ -730,16 +806,16 @@ pub unsafe fn dc_handle_securejoin_handshake(
4378276786830486580 => {}
_ => {
secure_connection_established(context, contact_chat_id);
context.bob.clone().write().unwrap().expects = 0;
context.bob.write().unwrap().expects = 0;
if 0 != join_vg {
send_handshake_msg(
context,
contact_chat_id,
b"vg-member-added-received\x00" as *const u8
as *const libc::c_char,
"",
0 as *const libc::c_char,
0 as *const libc::c_char,
0 as *const libc::c_char,
"",
);
}
end_bobs_joining(context, 1i32);
@@ -795,16 +871,13 @@ pub unsafe fn dc_handle_securejoin_handshake(
}
}
free(scanned_fingerprint_of_alice as *mut libc::c_void);
free(auth as *mut libc::c_void);
free(own_fingerprint as *mut libc::c_void);
free(grpid as *mut libc::c_void);
ret
}
unsafe fn end_bobs_joining(context: &Context, status: libc::c_int) {
context.bob.clone().write().unwrap().status = status;
context.bob.write().unwrap().status = status;
dc_stop_ongoing_process(context);
}
@@ -816,9 +889,8 @@ unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint
} else {
"?"
};
let msg =
CString::new(context.stock_string_repl_str(StockMessage::ContactVerified, addr)).unwrap();
dc_add_device_msg(context, contact_chat_id, msg.as_ptr());
let msg = context.stock_string_repl_str(StockMessage::ContactVerified, addr);
chat::add_device_msg(context, contact_chat_id, msg);
context.call_cb(
Event::CHAT_MODIFIED,
contact_chat_id as uintptr_t,
@@ -858,21 +930,18 @@ unsafe fn could_not_establish_secure_connection(
"?"
},
);
let msg_c = CString::new(msg.as_str()).unwrap();
dc_add_device_msg(context, contact_chat_id, msg_c.as_ptr());
error!(context, 0, "{} ({})", msg, as_str(details));
chat::add_device_msg(context, contact_chat_id, &msg);
error!(context, 0, "{} ({})", &msg, as_str(details));
}
unsafe fn mark_peer_as_verified(
context: &Context,
fingerprint: *const libc::c_char,
) -> libc::c_int {
unsafe fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef<str>) -> libc::c_int {
let mut success = 0;
if let Some(ref mut peerstate) =
Peerstate::from_fingerprint(context, &context.sql, as_str(fingerprint))
Peerstate::from_fingerprint(context, &context.sql, fingerprint.as_ref())
{
if peerstate.set_verified(1, as_str(fingerprint), 2) {
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);
@@ -887,43 +956,40 @@ unsafe fn mark_peer_as_verified(
* Tools: Misc.
******************************************************************************/
// TODO should return bool
unsafe fn encrypted_and_signed(
mimeparser: &dc_mimeparser_t,
expected_fingerprint: *const libc::c_char,
) -> libc::c_int {
expected_fingerprint: impl AsRef<str>,
) -> bool {
if 0 == mimeparser.e2ee_helper.encrypted {
warn!(mimeparser.context, 0, "Message not encrypted.",);
return 0i32;
return false;
}
if mimeparser.e2ee_helper.signatures.len() <= 0 {
warn!(mimeparser.context, 0, "Message not signed.",);
return 0i32;
return false;
}
if expected_fingerprint.is_null() {
if expected_fingerprint.as_ref().is_empty() {
warn!(mimeparser.context, 0, "Fingerprint for comparison missing.",);
return 0i32;
return false;
}
if !mimeparser
.e2ee_helper
.signatures
.contains(as_str(expected_fingerprint))
.contains(expected_fingerprint.as_ref())
{
warn!(
mimeparser.context,
0,
"Message does not match expected fingerprint {}.",
as_str(expected_fingerprint),
expected_fingerprint.as_ref(),
);
return 0;
return false;
}
1
true
}
pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) {
let mut contact_chat_id = 0;
// - we do not issue an warning for DC_DE_ENCRYPTION_PAUSED as this is quite normal
// - currently, we do not issue an extra warning for DC_DE_VERIFICATION_LOST - this always comes
// together with DC_DE_FINGERPRINT_CHANGED which is logged, the idea is not to bother
@@ -940,22 +1006,17 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate)
)
.unwrap_or_default();
if contact_id > 0 {
dc_create_or_lookup_nchat_by_contact_id(
context,
contact_id as u32,
2,
&mut contact_chat_id,
0 as *mut libc::c_int,
);
let (contact_chat_id, _) =
chat::create_or_lookup_by_contact_id(context, contact_id as u32, Blocked::Deaddrop)
.unwrap_or_default();
let peeraddr: &str = match peerstate.addr {
Some(ref addr) => &addr,
None => "",
};
let msg = CString::new(
context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr),
)
.unwrap();
dc_add_device_msg(context, contact_chat_id, msg.as_ptr());
let msg = context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr);
chat::add_device_msg(context, contact_chat_id, msg);
context.call_cb(
Event::CHAT_MODIFIED,
contact_chat_id as uintptr_t,

View File

@@ -3,19 +3,18 @@ use crate::dc_tools::*;
use crate::x::*;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_simplify_t {
pub is_forwarded: libc::c_int,
pub is_cut_at_begin: libc::c_int,
pub is_cut_at_end: libc::c_int,
pub struct Simplify {
pub is_forwarded: bool,
pub is_cut_at_begin: bool,
pub is_cut_at_end: bool,
}
impl dc_simplify_t {
impl Simplify {
pub fn new() -> Self {
dc_simplify_t {
is_forwarded: 0,
is_cut_at_begin: 0,
is_cut_at_end: 0,
Simplify {
is_forwarded: false,
is_cut_at_begin: false,
is_cut_at_end: false,
}
}
@@ -26,7 +25,7 @@ impl dc_simplify_t {
&mut self,
in_unterminated: *const libc::c_char,
in_bytes: libc::c_int,
is_html: libc::c_int,
is_html: bool,
is_msgrmsg: libc::c_int,
) -> *mut libc::c_char {
if in_bytes <= 0 {
@@ -36,9 +35,9 @@ impl dc_simplify_t {
/* create a copy of the given buffer */
let mut out: *mut libc::c_char;
let mut temp: *mut libc::c_char;
self.is_forwarded = 0i32;
self.is_cut_at_begin = 0i32;
self.is_cut_at_end = 0i32;
self.is_forwarded = false;
self.is_cut_at_begin = false;
self.is_cut_at_end = false;
out = strndup(
in_unterminated as *mut libc::c_char,
in_bytes as libc::c_ulong,
@@ -46,7 +45,7 @@ impl dc_simplify_t {
if out.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
if 0 != is_html {
if is_html {
temp = dc_dehtml(out);
if !temp.is_null() {
free(out as *mut libc::c_void);
@@ -96,7 +95,7 @@ impl dc_simplify_t {
|| strcmp(line, b"----\x00" as *const u8 as *const libc::c_char) == 0i32
{
footer_mark = 1i32;
self.is_cut_at_end = 1i32
self.is_cut_at_end = true
}
if 0 != footer_mark {
l_last = l;
@@ -115,7 +114,7 @@ impl dc_simplify_t {
&& strncmp(line1, b"From: \x00" as *const u8 as *const libc::c_char, 6) == 0i32
&& *line2.offset(0isize) as libc::c_int == 0i32
{
self.is_forwarded = 1i32;
self.is_forwarded = true;
l_first += 3
}
}
@@ -128,7 +127,7 @@ impl dc_simplify_t {
|| strncmp(line, b"~~~~~\x00" as *const u8 as *const libc::c_char, 5) == 0i32
{
l_last = l;
self.is_cut_at_end = 1i32;
self.is_cut_at_end = true;
/* done */
break;
}
@@ -145,7 +144,7 @@ impl dc_simplify_t {
}
if l_lastQuotedLine.is_some() {
l_last = l_lastQuotedLine.unwrap();
self.is_cut_at_end = 1i32;
self.is_cut_at_end = true;
if l_last > 1 {
if is_empty_line(lines[l_last - 1]) {
l_last -= 1
@@ -180,12 +179,12 @@ impl dc_simplify_t {
}
if l_lastQuotedLine_0.is_some() {
l_first = l_lastQuotedLine_0.unwrap() + 1;
self.is_cut_at_begin = 1i32
self.is_cut_at_begin = true
}
}
/* re-create buffer from the remaining lines */
let mut ret = String::new();
if 0 != self.is_cut_at_begin {
if self.is_cut_at_begin {
ret += "[...]";
}
/* we write empty lines only in case and non-empty line follows */
@@ -211,7 +210,7 @@ impl dc_simplify_t {
pending_linebreaks = 1i32
}
}
if 0 != self.is_cut_at_end && (0 == self.is_cut_at_begin || 0 != content_lines_added) {
if self.is_cut_at_end && (!self.is_cut_at_begin || 0 != content_lines_added) {
ret += " [...]";
}
dc_free_splitted_lines(lines);
@@ -268,11 +267,11 @@ mod tests {
#[test]
fn test_simplify_trim() {
unsafe {
let mut simplify = dc_simplify_t::new();
let mut simplify = Simplify::new();
let html: *const libc::c_char =
b"\r\r\nline1<br>\r\n\r\n\r\rline2\n\r\x00" as *const u8 as *const libc::c_char;
let plain: *mut libc::c_char =
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
assert_eq!(
CStr::from_ptr(plain as *const libc::c_char)
@@ -288,11 +287,11 @@ mod tests {
#[test]
fn test_simplify_parse_href() {
unsafe {
let mut simplify = dc_simplify_t::new();
let mut simplify = Simplify::new();
let html: *const libc::c_char =
b"<a href=url>text</a\x00" as *const u8 as *const libc::c_char;
let plain: *mut libc::c_char =
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
assert_eq!(
CStr::from_ptr(plain as *const libc::c_char)
@@ -308,12 +307,11 @@ mod tests {
#[test]
fn test_simplify_bold_text() {
unsafe {
let mut simplify = dc_simplify_t::new();
let mut simplify = Simplify::new();
let html: *const libc::c_char =
b"<!DOCTYPE name [<!DOCTYPE ...>]><!-- comment -->text <b><?php echo ... ?>bold</b><![CDATA[<>]]>\x00"
as *const u8 as *const libc::c_char;
let plain: *mut libc::c_char =
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
let plain = simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
assert_eq!(
CStr::from_ptr(plain as *const libc::c_char)
@@ -329,18 +327,17 @@ mod tests {
#[test]
fn test_simplify_html_encoded() {
unsafe {
let mut simplify = dc_simplify_t::new();
let html: *const libc::c_char =
b"&lt;&gt;&quot;&apos;&amp; &auml;&Auml;&ouml;&Ouml;&uuml;&Uuml;&szlig; foo&AElig;&ccedil;&Ccedil; &diams;&noent;&lrm;&rlm;&zwnj;&zwj;\x00"
let mut simplify = Simplify::new();
let html =
b"&lt;&gt;&quot;&apos;&amp; &auml;&Auml;&ouml;&Ouml;&uuml;&Uuml;&szlig; foo&AElig;&ccedil;&Ccedil; &diams;&lrm;&rlm;&zwnj;&noent;&zwj;\x00"
as *const u8 as *const libc::c_char;
let plain: *mut libc::c_char =
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
let plain = simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
assert_eq!(
strcmp(plain,
b"<>\"\'& \xc3\xa4\xc3\x84\xc3\xb6\xc3\x96\xc3\xbc\xc3\x9c\xc3\x9f foo\xc3\x86\xc3\xa7\xc3\x87 \xe2\x99\xa6&noent;\x00"
as *const u8 as *const libc::c_char),
0,
CStr::from_ptr(plain as *const libc::c_char)
.to_str()
.unwrap(),
"<>\"\'& äÄöÖüÜß fooÆçÇ \u{2666}\u{200e}\u{200f}\u{200c}&noent;\u{200d}"
);
free(plain as *mut libc::c_void);

View File

@@ -10,7 +10,7 @@ use crate::types::*;
use crate::x::*;
#[inline]
pub fn isalnum(c: libc::c_int) -> libc::c_int {
fn isalnum(c: libc::c_int) -> libc::c_int {
if c < std::u8::MAX as libc::c_int {
(c as u8 as char).is_ascii_alphanumeric() as libc::c_int
} else {
@@ -18,48 +18,6 @@ pub fn isalnum(c: libc::c_int) -> libc::c_int {
}
}
pub unsafe fn dc_urlencode(to_encode: *const libc::c_char) -> *mut libc::c_char {
let mut pstr: *const libc::c_char = to_encode;
if to_encode.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
let buf: *mut libc::c_char =
malloc(strlen(to_encode).wrapping_mul(3).wrapping_add(1)) as *mut libc::c_char;
let mut pbuf: *mut libc::c_char = buf;
assert!(!buf.is_null());
while 0 != *pstr {
if 0 != isalnum(*pstr as libc::c_int)
|| *pstr as libc::c_int == '-' as i32
|| *pstr as libc::c_int == '_' as i32
|| *pstr as libc::c_int == '.' as i32
|| *pstr as libc::c_int == '~' as i32
{
let fresh0 = pbuf;
pbuf = pbuf.offset(1);
*fresh0 = *pstr
} else if *pstr as libc::c_int == ' ' as i32 {
let fresh1 = pbuf;
pbuf = pbuf.offset(1);
*fresh1 = '+' as i32 as libc::c_char
} else {
let fresh2 = pbuf;
pbuf = pbuf.offset(1);
*fresh2 = '%' as i32 as libc::c_char;
let fresh3 = pbuf;
pbuf = pbuf.offset(1);
*fresh3 = int_2_uppercase_hex((*pstr as libc::c_int >> 4i32) as libc::c_char);
let fresh4 = pbuf;
pbuf = pbuf.offset(1);
*fresh4 = int_2_uppercase_hex((*pstr as libc::c_int & 15i32) as libc::c_char)
}
pstr = pstr.offset(1isize)
}
*pbuf = '\u{0}' as i32 as libc::c_char;
buf
}
/* ******************************************************************************
* URL encoding and decoding, RFC 3986
******************************************************************************/
@@ -331,7 +289,8 @@ pub unsafe fn dc_decode_header_words(in_0: *const libc::c_char) -> *mut libc::c_
out
}
pub unsafe fn dc_encode_modified_utf7(
#[cfg(test)]
unsafe fn dc_encode_modified_utf7(
mut to_encode: *const libc::c_char,
change_spaces: libc::c_int,
) -> *mut libc::c_char {
@@ -475,13 +434,15 @@ pub unsafe fn dc_encode_modified_utf7(
******************************************************************************/
// UTF7 modified base64 alphabet
#[cfg(test)]
static mut BASE64CHARS: [libc::c_char; 65] = [
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 44, 0,
];
pub unsafe fn dc_decode_modified_utf7(
#[cfg(test)]
unsafe fn dc_decode_modified_utf7(
to_decode: *const libc::c_char,
change_spaces: libc::c_int,
) -> *mut libc::c_char {
@@ -691,11 +652,11 @@ pub unsafe fn dc_decode_ext_header(to_decode: *const libc::c_char) -> *mut libc:
}
}
free(charset as *mut libc::c_void);
return if !decoded.is_null() {
if !decoded.is_null() {
decoded
} else {
dc_strdup(to_decode)
};
}
}
unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
@@ -710,6 +671,7 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
#[cfg(test)]
mod tests {
use super::*;
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
use std::ffi::CStr;
#[test]
@@ -889,14 +851,15 @@ mod tests {
#[test]
fn test_dc_urlencode_urldecode() {
unsafe {
let buf1 =
dc_urlencode(b"Bj\xc3\xb6rn Petersen\x00" as *const u8 as *const libc::c_char);
let buf1 = percent_encode(b"Bj\xc3\xb6rn Petersen", NON_ALPHANUMERIC)
.to_string()
.strdup();
assert_eq!(
CStr::from_ptr(buf1 as *const libc::c_char)
.to_str()
.unwrap(),
"Bj%C3%B6rn+Petersen"
"Bj%C3%B6rn%20Petersen"
);
let buf2 = dc_urldecode(buf1);

View File

@@ -4,7 +4,7 @@ use crate::sql;
// Token namespaces
#[allow(non_camel_case_types)]
pub type dc_tokennamespc_t = usize;
type dc_tokennamespc_t = usize;
pub const DC_TOKEN_AUTH: dc_tokennamespc_t = 110;
pub const DC_TOKEN_INVITENUMBER: dc_tokennamespc_t = 100;

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,10 @@ pub enum Error {
Io(std::io::Error),
#[fail(display = "{:?}", _0)]
Message(String),
#[fail(display = "{:?}", _0)]
Image(image_meta::ImageError),
#[fail(display = "{:?}", _0)]
Utf8(std::str::Utf8Error),
}
pub type Result<T> = std::result::Result<T, Error>;
@@ -46,6 +50,18 @@ impl From<std::io::Error> for Error {
}
}
impl From<std::str::Utf8Error> for Error {
fn from(err: std::str::Utf8Error) -> Error {
Error::Utf8(err)
}
}
impl From<image_meta::ImageError> for Error {
fn from(err: image_meta::ImageError) -> Error {
Error::Image(err)
}
}
#[macro_export]
macro_rules! bail {
($e:expr) => {

View File

@@ -1,6 +1,9 @@
use std::ffi::CString;
use std::net;
use std::sync::{Arc, Condvar, Mutex, RwLock};
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, Condvar, Mutex, RwLock,
};
use std::time::{Duration, SystemTime};
use crate::constants::*;
@@ -10,19 +13,18 @@ use crate::dc_tools::CStringExt;
use crate::oauth2::dc_get_oauth2_access_token;
use crate::types::*;
pub const DC_IMAP_SEEN: usize = 0x0001;
pub const DC_REGENERATE: usize = 0x01;
const DC_IMAP_SEEN: usize = 0x0001;
const DC_REGENERATE: usize = 0x01;
pub const DC_SUCCESS: usize = 3;
pub const DC_ALREADY_DONE: usize = 2;
pub const DC_RETRY_LATER: usize = 1;
pub const DC_FAILED: usize = 0;
const DC_SUCCESS: usize = 3;
const DC_ALREADY_DONE: usize = 2;
const DC_RETRY_LATER: usize = 1;
const DC_FAILED: usize = 0;
const PREFETCH_FLAGS: &'static str = "(UID ENVELOPE)";
const BODY_FLAGS: &'static str = "(FLAGS BODY.PEEK[])";
const FETCH_FLAGS: &'static str = "(FLAGS)";
const PREFETCH_FLAGS: &str = "(UID ENVELOPE)";
const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])";
const FETCH_FLAGS: &str = "(FLAGS)";
#[repr(C)]
pub struct Imap {
config: Arc<RwLock<ImapConfig>>,
watch: Arc<(Mutex<bool>, Condvar)>,
@@ -35,6 +37,8 @@ pub struct Imap {
session: Arc<Mutex<Option<Session>>>,
stream: Arc<RwLock<Option<net::TcpStream>>>,
connected: Arc<Mutex<bool>>,
should_reconnect: AtomicBool,
}
struct OAuth2 {
@@ -55,13 +59,13 @@ impl imap::Authenticator for OAuth2 {
}
#[derive(Debug)]
pub enum FolderMeaning {
enum FolderMeaning {
Unknown,
SentObjects,
Other,
}
pub enum Client {
enum Client {
Secure(
imap::Client<native_tls::TlsStream<net::TcpStream>>,
net::TcpStream,
@@ -69,12 +73,12 @@ pub enum Client {
Insecure(imap::Client<net::TcpStream>, net::TcpStream),
}
pub enum Session {
enum Session {
Secure(imap::Session<native_tls::TlsStream<net::TcpStream>>),
Insecure(imap::Session<net::TcpStream>),
}
pub enum IdleHandle<'a> {
enum IdleHandle<'a> {
Secure(imap::extensions::idle::Handle<'a, native_tls::TlsStream<net::TcpStream>>),
Insecure(imap::extensions::idle::Handle<'a, net::TcpStream>),
}
@@ -199,8 +203,8 @@ impl Session {
pub fn create<S: AsRef<str>>(&mut self, mailbox_name: S) -> imap::error::Result<()> {
match self {
Session::Secure(i) => i.subscribe(mailbox_name),
Session::Insecure(i) => i.subscribe(mailbox_name),
Session::Secure(i) => i.create(mailbox_name),
Session::Insecure(i) => i.create(mailbox_name),
}
}
@@ -260,8 +264,8 @@ impl Session {
pub fn idle(&mut self) -> imap::error::Result<IdleHandle> {
match self {
Session::Secure(i) => i.idle().map(|h| IdleHandle::Secure(h)),
Session::Insecure(i) => i.idle().map(|h| IdleHandle::Insecure(h)),
Session::Secure(i) => i.idle().map(IdleHandle::Secure),
Session::Insecure(i) => i.idle().map(IdleHandle::Insecure),
}
}
@@ -303,7 +307,7 @@ impl Session {
}
}
pub struct ImapConfig {
struct ImapConfig {
pub addr: String,
pub imap_server: String,
pub imap_port: u16,
@@ -313,7 +317,6 @@ pub struct ImapConfig {
pub selected_folder: Option<String>,
pub selected_mailbox: Option<imap::types::Mailbox>,
pub selected_folder_needs_expunge: bool,
pub should_reconnect: bool,
pub can_idle: bool,
pub has_xlist: bool,
pub imap_delimiter: char,
@@ -332,7 +335,6 @@ impl Default for ImapConfig {
selected_folder: None,
selected_mailbox: None,
selected_folder_needs_expunge: false,
should_reconnect: false,
can_idle: false,
has_xlist: false,
imap_delimiter: '.',
@@ -360,6 +362,7 @@ impl Imap {
precheck_imf,
receive_imf,
connected: Arc::new(Mutex::new(false)),
should_reconnect: AtomicBool::new(false),
}
}
@@ -368,7 +371,7 @@ impl Imap {
}
pub fn should_reconnect(&self) -> bool {
self.config.read().unwrap().should_reconnect
self.should_reconnect.load(Ordering::Relaxed)
}
fn setup_handle_if_needed(&self, context: &Context) -> bool {
@@ -381,7 +384,7 @@ impl Imap {
}
if self.is_connected() && self.stream.read().unwrap().is_some() {
self.config.write().unwrap().should_reconnect = false;
self.should_reconnect.store(false, Ordering::Relaxed);
return true;
}
@@ -451,7 +454,7 @@ impl Imap {
}
};
self.config.write().unwrap().should_reconnect = false;
self.should_reconnect.store(false, Ordering::Relaxed);
match login_res {
Ok((session, stream)) => {
@@ -539,7 +542,7 @@ impl Imap {
let mut config = self.config.write().unwrap();
config.addr = addr.to_string();
config.imap_server = imap_server.to_string();
config.imap_port = imap_port.into();
config.imap_port = imap_port;
config.imap_user = imap_user.to_string();
config.imap_pw = imap_pw.to_string();
config.server_flags = server_flags;
@@ -550,14 +553,12 @@ impl Imap {
return false;
}
let teardown: bool;
match &mut *self.session.lock().unwrap() {
let (teardown, can_idle, has_xlist) = match &mut *self.session.lock().unwrap() {
Some(ref mut session) => {
if let Ok(caps) = session.capabilities() {
if !context.sql.is_open() {
warn!(context, 0, "IMAP-LOGIN as {} ok but ABORTING", lp.mail_user,);
teardown = true;
(true, false, false)
} else {
let can_idle = caps.has("IDLE");
let has_xlist = caps.has("XLIST");
@@ -574,24 +575,23 @@ impl Imap {
lp.mail_user,
caps_list,
);
self.config.write().unwrap().can_idle = can_idle;
self.config.write().unwrap().has_xlist = has_xlist;
*self.connected.lock().unwrap() = true;
teardown = false;
(false, can_idle, has_xlist)
}
} else {
teardown = true;
(true, false, false)
}
}
None => {
teardown = true;
}
}
None => (true, false, false),
};
if teardown {
self.unsetup_handle(context);
self.free_connect_params();
false
} else {
self.config.write().unwrap().can_idle = can_idle;
self.config.write().unwrap().has_xlist = has_xlist;
*self.connected.lock().unwrap() = true;
true
}
}
@@ -689,9 +689,8 @@ impl Imap {
err
);
let mut config = self.config.write().unwrap();
config.selected_folder = None;
config.should_reconnect = true;
self.config.write().unwrap().selected_folder = None;
self.should_reconnect.store(true, Ordering::Relaxed);
return 0;
}
}
@@ -777,7 +776,7 @@ impl Imap {
match session.fetch(set, PREFETCH_FLAGS) {
Ok(list) => list,
Err(_err) => {
self.config.write().unwrap().should_reconnect = true;
self.should_reconnect.store(true, Ordering::Relaxed);
info!(
context,
0,
@@ -822,7 +821,7 @@ impl Imap {
match session.uid_fetch(set, PREFETCH_FLAGS) {
Ok(list) => list,
Err(err) => {
eprintln!("fetch err: {:?}", err);
warn!(context, 0, "failed to fetch uids: {}", err);
return 0;
}
}
@@ -928,15 +927,13 @@ impl Imap {
return 0;
}
let mut retry_later = false;
let set = format!("{}", server_uid);
let msgs = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_fetch(set, BODY_FLAGS) {
Ok(msgs) => msgs,
Err(err) => {
self.config.write().unwrap().should_reconnect = true;
self.should_reconnect.store(true, Ordering::Relaxed);
warn!(
context,
0,
@@ -946,17 +943,11 @@ impl Imap {
self.should_reconnect(),
err
);
if self.should_reconnect() {
// maybe we should also retry on other errors, however, we should check this carefully, as this may result in a dead lock!
retry_later = true;
}
return if retry_later { 0 } else { 1 };
return 0;
}
}
} else {
return if retry_later { 0 } else { 1 };
return 1;
};
if msgs.is_empty() {
@@ -1004,11 +995,7 @@ impl Imap {
}
}
if retry_later {
0
} else {
1
}
1
}
pub fn idle(&self, context: &Context) {
@@ -1073,7 +1060,7 @@ impl Imap {
context,
0, "IMAP-IDLE wait cancelled, we will reconnect soon."
);
self.config.write().unwrap().should_reconnect = true;
self.should_reconnect.store(true, Ordering::Relaxed);
}
_ => {
warn!(context, 0, "IMAP-IDLE returns unknown value: {}", err);
@@ -1576,20 +1563,23 @@ impl Imap {
info!(context, 0, "MVBOX-folder created.",);
}
Err(err) => {
eprintln!("create error: {:?}", err);
warn!(
context,
0, "Cannot create MVBOX-folder, using trying INBOX subfolder."
0,
"Cannot create MVBOX-folder, using trying INBOX subfolder. ({})",
err
);
match session.create(&fallback_folder) {
Ok(_) => {
mvbox_folder = Some(fallback_folder);
info!(context, 0, "MVBOX-folder created as INBOX subfolder.",);
info!(
context,
0, "MVBOX-folder created as INBOX subfolder. ({})", err
);
}
Err(err) => {
eprintln!("create error: {:?}", err);
warn!(context, 0, "Cannot create MVBOX-folder.",);
warn!(context, 0, "Cannot create MVBOX-folder. ({})", err);
}
}
}

1181
src/job.rs Normal file

File diff suppressed because it is too large Load Diff

181
src/job_thread.rs Normal file
View File

@@ -0,0 +1,181 @@
use std::sync::{Arc, Condvar, Mutex};
use crate::context::Context;
use crate::dc_configure::*;
use crate::imap::Imap;
pub struct JobThread {
pub name: &'static str,
pub folder_config_name: &'static str,
pub imap: Imap,
pub state: Arc<(Mutex<JobState>, Condvar)>,
}
#[derive(Clone, Debug, Default)]
pub struct JobState {
idle: bool,
jobs_needed: i32,
suspended: bool,
using_handle: bool,
}
impl JobThread {
pub fn new(name: &'static str, folder_config_name: &'static str, imap: Imap) -> Self {
JobThread {
name,
folder_config_name,
imap,
state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
}
}
pub fn suspend(&self, context: &Context) {
info!(context, 0, "Suspending {}-thread.", self.name,);
{
self.state.0.lock().unwrap().suspended = true;
}
self.interrupt_idle(context);
loop {
let using_handle = self.state.0.lock().unwrap().using_handle;
if !using_handle {
return;
}
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
}
}
pub fn unsuspend(&self, context: &Context) {
info!(context, 0, "Unsuspending {}-thread.", self.name);
let &(ref lock, ref cvar) = &*self.state.clone();
let mut state = lock.lock().unwrap();
state.suspended = false;
state.idle = true;
cvar.notify_one();
}
pub fn interrupt_idle(&self, context: &Context) {
{
self.state.0.lock().unwrap().jobs_needed = 1;
}
info!(context, 0, "Interrupting {}-IDLE...", self.name);
self.imap.interrupt_idle();
let &(ref lock, ref cvar) = &*self.state.clone();
let mut state = lock.lock().unwrap();
state.idle = true;
cvar.notify_one();
}
pub fn fetch(&mut self, context: &Context, use_network: bool) {
{
let &(ref lock, _) = &*self.state.clone();
let mut state = lock.lock().unwrap();
if state.suspended {
return;
}
state.using_handle = true;
}
if use_network {
let start = std::time::Instant::now();
if self.connect_to_imap(context) {
info!(context, 0, "{}-fetch started...", self.name);
self.imap.fetch(context);
if self.imap.should_reconnect() {
info!(context, 0, "{}-fetch aborted, starting over...", self.name,);
self.imap.fetch(context);
}
info!(
context,
0,
"{}-fetch done in {:.3} ms.",
self.name,
start.elapsed().as_millis(),
);
}
}
self.state.0.lock().unwrap().using_handle = false;
}
fn connect_to_imap(&self, context: &Context) -> bool {
if self.imap.is_connected() {
return true;
}
let mut ret_connected = dc_connect_to_configured_imap(context, &self.imap) != 0;
if ret_connected {
if context
.sql
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
self.imap.configure_folders(context, 0x1);
}
if let Some(mvbox_name) = context.sql.get_config(context, self.folder_config_name) {
self.imap.set_watch_folder(mvbox_name);
} else {
self.imap.disconnect(context);
ret_connected = false;
}
}
ret_connected
}
pub fn idle(&self, context: &Context, use_network: bool) {
{
let &(ref lock, ref cvar) = &*self.state.clone();
let mut state = lock.lock().unwrap();
if 0 != state.jobs_needed {
info!(
context,
0,
"{}-IDLE will not be started as it was interrupted while not ideling.",
self.name,
);
state.jobs_needed = 0;
return;
}
if state.suspended {
while !state.idle {
state = cvar.wait(state).unwrap();
}
state.idle = false;
return;
}
state.using_handle = true;
if !use_network {
state.using_handle = false;
while !state.idle {
state = cvar.wait(state).unwrap();
}
state.idle = false;
return;
}
}
self.connect_to_imap(context);
info!(context, 0, "{}-IDLE started...", self.name,);
self.imap.idle(context);
info!(context, 0, "{}-IDLE ended.", self.name);
self.state.0.lock().unwrap().using_handle = false;
}
}

View File

@@ -89,7 +89,7 @@ impl Key {
}
pub fn from_slice(bytes: &[u8], key_type: KeyType) -> Option<Self> {
if 0 == bytes.len() {
if bytes.is_empty() {
return None;
}
let res: Result<Key, _> = match key_type {
@@ -283,7 +283,7 @@ impl Key {
Key::Public(_) => None,
Key::Secret(k) => {
let pub_key = k.public_key();
pub_key.sign(k, || "".into()).map(|k| Key::Public(k)).ok()
pub_key.sign(k, || "".into()).map(Key::Public).ok()
}
}
}

View File

@@ -1,4 +1,9 @@
#![feature(c_variadic, ptr_wrapping_offset_from, ptr_cast)]
#![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)]
#[macro_use]
extern crate failure_derive;
@@ -15,52 +20,49 @@ extern crate strum_macros;
#[macro_use]
mod log;
#[macro_use]
pub mod error;
mod error;
pub mod aheader;
mod aheader;
pub mod chat;
pub mod chatlist;
pub mod config;
pub mod constants;
pub mod contact;
pub mod context;
pub mod imap;
mod imap;
pub mod job;
mod job_thread;
pub mod key;
pub mod keyring;
pub mod lot;
pub mod oauth2;
pub mod param;
mod param;
pub mod peerstate;
pub mod pgp;
pub mod smtp;
pub mod qr;
mod smtp;
pub mod sql;
pub mod stock;
mod stock;
pub mod types;
pub mod x;
pub mod dc_array;
pub mod dc_chat;
pub mod dc_configure;
pub mod dc_dehtml;
pub mod dc_e2ee;
mod dc_dehtml;
mod dc_e2ee;
pub mod dc_imex;
pub mod dc_job;
pub mod dc_jobthread;
pub mod dc_location;
pub mod dc_loginparam;
pub mod dc_lot;
pub mod dc_mimefactory;
mod dc_loginparam;
mod dc_mimefactory;
pub mod dc_mimeparser;
pub mod dc_move;
mod dc_move;
pub mod dc_msg;
pub mod dc_qr;
pub mod dc_receive_imf;
pub mod dc_saxparser;
pub mod dc_securejoin;
pub mod dc_simplify;
pub mod dc_strencode;
pub mod dc_token;
mod dc_simplify;
mod dc_strencode;
mod dc_token;
pub mod dc_tools;
pub use self::constants::*;
#[cfg(test)]
pub mod test_utils;
mod test_utils;

109
src/lot.rs Normal file
View File

@@ -0,0 +1,109 @@
use deltachat_derive::{FromSql, ToSql};
/// An object containing a set of values.
/// The meaning of the values is defined by the function returning the object.
/// Lot objects are created
/// eg. by chatlist.get_summary() or dc_msg_get_summary().
///
/// _Lot_ is used in the meaning _heap_ here.
#[derive(Default, Debug, Clone)]
pub struct Lot {
pub(crate) text1_meaning: Meaning,
pub(crate) text1: Option<String>,
pub(crate) text2: Option<String>,
pub(crate) timestamp: i64,
pub(crate) state: LotState,
pub(crate) id: u32,
pub(crate) fingerprint: Option<String>,
pub(crate) invitenumber: Option<String>,
pub(crate) auth: Option<String>,
}
#[repr(u8)]
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
pub enum Meaning {
None = 0,
Text1Draft = 1,
Text1Username = 2,
Text1Self = 3,
}
impl Default for Meaning {
fn default() -> Self {
Meaning::None
}
}
impl Lot {
pub fn new() -> Self {
Default::default()
}
pub fn get_text1(&self) -> Option<&str> {
self.text1.as_ref().map(|s| s.as_str())
}
pub fn get_text2(&self) -> Option<&str> {
self.text2.as_ref().map(|s| s.as_str())
}
pub fn get_text1_meaning(&self) -> Meaning {
self.text1_meaning
}
pub fn get_state(&self) -> LotState {
self.state
}
pub fn get_id(&self) -> u32 {
self.id
}
pub fn get_timestamp(&self) -> i64 {
self.timestamp
}
}
#[repr(i32)]
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
pub enum LotState {
// Default
Undefined = 0,
// Qr States
/// id=contact
QrAskVerifyContact = 200,
/// text1=groupname
QrAskVerifyGroup = 202,
/// id=contact
QrFprOk = 210,
/// id=contact
QrFprMissmatch = 220,
/// test1=formatted fingerprint
QrFprWithoutAddr = 230,
/// id=contact
QrAddr = 320,
/// text1=text
QrText = 330,
/// text1=URL
QrUrl = 332,
/// text1=error string
QrError = 400,
// Message States
MsgInFresh = 10,
MsgInNoticed = 13,
MsgInSeen = 16,
MsgOutPreparing = 18,
MsgOutDraft = 19,
MsgOutPending = 20,
MsgOutFailed = 24,
MsgOutDelivered = 26,
MsgOutMdnRcvd = 28,
}
impl Default for LotState {
fn default() -> Self {
LotState::Undefined
}
}

View File

@@ -23,7 +23,7 @@ const OAUTH2_YANDEX: Oauth2 = Oauth2 {
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Oauth2 {
struct Oauth2 {
client_id: &'static str,
get_code: &'static str,
init_token: &'static str,

View File

@@ -113,7 +113,7 @@ impl str::FromStr for Params {
continue;
}
// TODO: probably nicer using a regex
ensure!(pair.len() > 2, "Invalid key pair: '{}'", pair);
ensure!(pair.len() > 1, "Invalid key pair: '{}'", pair);
let mut split = pair.splitn(2, '=');
let key = split.next();
let value = split.next();
@@ -235,4 +235,12 @@ mod tests {
assert!(p1.is_empty());
assert_eq!(p1.len(), 0)
}
#[test]
fn test_regression() {
let p1: Params = "a=cli%40deltachat.de\nn=\ni=TbnwJ6lSvD5\ns=0ejvbdFSQxB"
.parse()
.unwrap();
assert_eq!(p1.get(Param::Forwarded).unwrap(), "cli%40deltachat.de");
}
}

View File

@@ -4,9 +4,9 @@ use std::fmt;
use num_traits::FromPrimitive;
use crate::aheader::*;
use crate::chat::*;
use crate::constants::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::key::*;
use crate::sql::{self, Sql};
@@ -85,7 +85,7 @@ pub enum DegradeEvent {
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum VerifiedKey {
enum VerifiedKey {
Gossip,
Public,
None,
@@ -166,7 +166,6 @@ impl<'a> Peerstate<'a> {
pub fn from_addr(context: &'a Context, _sql: &Sql, addr: &str) -> Option<Self> {
let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, verified_key, verified_key_fingerprint FROM acpeerstates WHERE addr=? COLLATE NOCASE;";
info!(context, 0, "peerstate.from_addr {}", addr);
Self::from_stmt(context, query, &[addr])
}
@@ -188,7 +187,6 @@ impl<'a> Peerstate<'a> {
P: IntoIterator,
P::Item: rusqlite::ToSql,
{
info!(context, 0, "from_stmt query {:?}", query);
context
.sql
.query_row(query, params, |row| {
@@ -200,28 +198,38 @@ impl<'a> Peerstate<'a> {
let mut res = Self::new(context);
res.addr = Some(row.get(0)?);
info!(context, 0, "from_stmt res.addr {:?} starting", res.addr);
res.last_seen = row.get(1)?;
res.last_seen_autocrypt = row.get(2)?;
res.prefer_encrypt = EncryptPreference::from_i32(row.get(3)?).unwrap_or_default();
res.gossip_timestamp = row.get(5)?;
let pkf: String = row.get(7)?;
res.public_key_fingerprint = if pkf.is_empty() { None } else { Some(pkf) };
let t: Result<String, rusqlite::Error> = row.get(8);
let gkf: String = match t {
Err(_) => String::from(""),
Ok(res) => res,
};
res.gossip_key_fingerprint = if gkf.is_empty() { None } else { Some(gkf) };
let t: Result<String, rusqlite::Error> = row.get(10);
let vkf: String = match t {
Err(_) => String::from(""),
Ok(res) => res,
};
res.verified_key_fingerprint = if vkf.is_empty() { None } else { Some(vkf) };
res.public_key_fingerprint = row.get(7)?;
if res
.public_key_fingerprint
.as_ref()
.map(|s| s.is_empty())
.unwrap_or_default()
{
res.public_key_fingerprint = None;
}
res.gossip_key_fingerprint = row.get(8)?;
if res
.gossip_key_fingerprint
.as_ref()
.map(|s| s.is_empty())
.unwrap_or_default()
{
res.gossip_key_fingerprint = None;
}
res.verified_key_fingerprint = row.get(10)?;
if res
.verified_key_fingerprint
.as_ref()
.map(|s| s.is_empty())
.unwrap_or_default()
{
res.verified_key_fingerprint = None;
}
res.public_key = row
.get(4)
.ok()
@@ -234,6 +242,7 @@ impl<'a> Peerstate<'a> {
.get(9)
.ok()
.and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Public));
res.verified_key = if vk == res.gossip_key && res.gossip_key.is_some() {
VerifiedKey::Gossip
} else if vk == res.public_key {
@@ -417,10 +426,6 @@ impl<'a> Peerstate<'a> {
}
if self.to_save == Some(ToSave::All) || create {
info!(
self.context,
0, "update acpeerstates with peerstate {:?}", self
);
success = sql::execute(
self.context,
sql,
@@ -461,7 +466,7 @@ impl<'a> Peerstate<'a> {
}
if self.to_save == Some(ToSave::All) || create {
dc_reset_gossiped_timestamp(self.context, 0);
reset_gossiped_timestamp(self.context, 0);
}
success

View File

@@ -2,6 +2,7 @@ use std::collections::HashSet;
use std::convert::TryInto;
use std::ffi::CStr;
use std::io::Cursor;
use std::ptr;
use pgp::composed::{
Deserializable, KeyType as PgpKeyType, Message, SecretKeyParamsBuilder, SignedPublicKey,
@@ -29,19 +30,19 @@ pub unsafe fn dc_split_armored_data(
let mut line: *mut libc::c_char = buf;
let mut p1: *mut libc::c_char = buf;
let mut p2: *mut libc::c_char;
let mut headerline: *mut libc::c_char = 0 as *mut libc::c_char;
let mut base64: *mut libc::c_char = 0 as *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 = 0 as *const libc::c_char
}
if !ret_setupcodebegin.is_null() {
*ret_setupcodebegin = 0 as *const libc::c_char
*ret_setupcodebegin = ptr::null_mut();
}
if !ret_preferencrypt.is_null() {
*ret_preferencrypt = 0 as *const libc::c_char
*ret_preferencrypt = ptr::null();
}
if !ret_base64.is_null() {
*ret_base64 = 0 as *const libc::c_char
*ret_base64 = ptr::null();
}
if !(buf.is_null() || ret_headerline.is_null()) {
dc_remove_cr_chars(buf);
@@ -143,7 +144,7 @@ pub fn dc_pgp_create_keypair(addr: impl AsRef<str>) -> Option<(Key, Key)> {
.key_type(PgpKeyType::Rsa(2048))
.can_create_certificates(true)
.can_sign(true)
.primary_user_id(user_id.into())
.primary_user_id(user_id)
.passphrase(None)
.preferred_symmetric_algorithms(smallvec![
SymmetricKeyAlgorithm::AES256,

469
src/qr.rs Normal file
View File

@@ -0,0 +1,469 @@
use lazy_static::lazy_static;
use percent_encoding::percent_decode_str;
use crate::chat;
use crate::constants::Blocked;
use crate::contact::*;
use crate::context::Context;
use crate::error::Error;
use crate::key::dc_format_fingerprint;
use crate::key::*;
use crate::lot::{Lot, LotState};
use crate::param::*;
use crate::peerstate::*;
const OPENPGP4FPR_SCHEME: &str = "OPENPGP4FPR:"; // yes: uppercase
const MAILTO_SCHEME: &str = "mailto:";
const MATMSG_SCHEME: &str = "MATMSG:";
const VCARD_SCHEME: &str = "BEGIN:VCARD";
const SMTP_SCHEME: &str = "SMTP:";
const HTTP_SCHEME: &str = "http://";
const HTTPS_SCHEME: &str = "https://";
// Make it easy to convert errors into the final `Lot`.
impl Into<Lot> for Error {
fn into(self) -> Lot {
let mut l = Lot::new();
l.state = LotState::QrError;
l.text1 = Some(self.to_string());
l
}
}
/// Check a scanned QR code.
/// The function should be called after a QR code is scanned.
/// The function takes the raw text scanned and checks what can be done with it.
pub fn check_qr(context: &Context, qr: impl AsRef<str>) -> Lot {
let qr = qr.as_ref();
info!(context, 0, "Scanned QR code: {}", qr);
if qr.starts_with(OPENPGP4FPR_SCHEME) {
decode_openpgp(context, qr)
} else if qr.starts_with(MAILTO_SCHEME) {
decode_mailto(context, qr)
} else if qr.starts_with(SMTP_SCHEME) {
decode_smtp(context, qr)
} else if qr.starts_with(MATMSG_SCHEME) {
decode_matmsg(context, qr)
} else if qr.starts_with(VCARD_SCHEME) {
decode_vcard(context, qr)
} else if qr.starts_with(HTTP_SCHEME) || qr.starts_with(HTTPS_SCHEME) {
Lot::from_url(qr)
} else {
Lot::from_text(qr)
}
}
/// scheme: `OPENPGP4FPR:FINGERPRINT#a=ADDR&n=NAME&i=INVITENUMBER&s=AUTH`
/// or: `OPENPGP4FPR:FINGERPRINT#a=ADDR&g=GROUPNAME&x=GROUPID&i=INVITENUMBER&s=AUTH`
fn decode_openpgp(context: &Context, qr: &str) -> Lot {
let payload = &qr[OPENPGP4FPR_SCHEME.len()..];
let (fingerprint, fragment) = match payload.find('#').map(|offset| {
let (fp, rest) = payload.split_at(offset);
// need to remove the # from the fragment
(fp, &rest[1..])
}) {
Some(pair) => pair,
None => return format_err!("Invalid OPENPGP4FPR found").into(),
};
dbg!(fingerprint);
dbg!(fragment);
// replace & with \n to match expected param format
let fragment = fragment.replace('&', "\n");
dbg!(&fragment);
// Then parse the parameters
let param: Params = match fragment.parse() {
Ok(params) => params,
Err(err) => return err.into(),
};
dbg!(&param);
let addr = if let Some(addr) = param.get(Param::Forwarded) {
match normalize_address(addr) {
Ok(addr) => addr,
Err(err) => return err.into(),
}
} else {
return format_err!("Missing address").into();
};
// what is up with that param name?
let name = if let Some(encoded_name) = param.get(Param::SetLongitude) {
match percent_decode_str(encoded_name).decode_utf8() {
Ok(name) => name.to_string(),
Err(err) => return format_err!("Invalid name: {}", err).into(),
}
} else {
"".to_string()
};
let invitenumber = param.get(Param::ProfileImage).map(|s| s.to_string());
let auth = param.get(Param::Auth).map(|s| s.to_string());
let grpid = param.get(Param::GroupId).map(|s| s.to_string());
let grpname = if grpid.is_some() {
if let Some(encoded_name) = param.get(Param::GroupName) {
match percent_decode_str(encoded_name).decode_utf8() {
Ok(name) => Some(name.to_string()),
Err(err) => return format_err!("Invalid group name: {}", err).into(),
}
} else {
None
}
} else {
None
};
let fingerprint = dc_normalize_fingerprint(fingerprint);
// ensure valid fingerprint
if fingerprint.len() != 40 {
return format_err!("Bad fingerprint length in QR code").into();
}
println!(
"{:?} {:?} {:?} {:?} {:?} {:?} {:?}",
addr, name, invitenumber, auth, grpid, grpname, fingerprint
);
let mut lot = Lot::new();
// retrieve known state for this fingerprint
let peerstate = Peerstate::from_fingerprint(context, &context.sql, &fingerprint);
if invitenumber.is_none() || auth.is_none() {
if let Some(peerstate) = peerstate {
lot.state = LotState::QrFprOk;
let addr = peerstate
.addr
.as_ref()
.map(|s| s.as_str())
.unwrap_or_else(|| "");
lot.id = Contact::add_or_lookup(context, name, addr, Origin::UnhandledQrScan)
.map(|(id, _)| id)
.unwrap_or_default();
let (id, _) = chat::create_or_lookup_by_contact_id(context, lot.id, Blocked::Deaddrop)
.unwrap_or_default();
chat::add_device_msg(
context,
id,
format!("{} verified.", peerstate.addr.unwrap_or_default()),
);
} else {
lot.state = LotState::QrFprWithoutAddr;
lot.text1 = Some(dc_format_fingerprint(&fingerprint));
}
} else {
if grpid.is_some() && grpname.is_some() {
lot.state = LotState::QrAskVerifyGroup;
lot.text1 = grpname;
lot.text2 = grpid
} else {
lot.state = LotState::QrAskVerifyContact;
}
lot.id = Contact::add_or_lookup(context, &name, &addr, Origin::UnhandledQrScan)
.map(|(id, _)| id)
.unwrap_or_default();
lot.fingerprint = Some(fingerprint);
lot.invitenumber = invitenumber;
lot.auth = auth;
}
lot
}
/// Extract address for the mailto scheme.
///
/// Scheme: `mailto:addr...?subject=...&body=..`
fn decode_mailto(context: &Context, qr: &str) -> Lot {
let payload = &qr[MAILTO_SCHEME.len()..];
let addr = if let Some(query_index) = payload.find('?') {
&payload[..query_index]
} else {
return format_err!("Invalid mailto found").into();
};
let addr = match normalize_address(addr) {
Ok(addr) => addr,
Err(err) => return err.into(),
};
let name = "".to_string();
Lot::from_address(context, name, addr)
}
/// Extract address for the smtp scheme.
///
/// Scheme: `SMTP:addr...:subject...:body...`
fn decode_smtp(context: &Context, qr: &str) -> Lot {
let payload = &qr[SMTP_SCHEME.len()..];
let addr = if let Some(query_index) = payload.find(':') {
&payload[..query_index]
} else {
return format_err!("Invalid SMTP found").into();
};
let addr = match normalize_address(addr) {
Ok(addr) => addr,
Err(err) => return err.into(),
};
let name = "".to_string();
Lot::from_address(context, name, addr)
}
/// Extract address for the matmsg scheme.
///
/// Scheme: `MATMSG:TO:addr...;SUB:subject...;BODY:body...;`
///
/// There may or may not be linebreaks after the fields.
fn decode_matmsg(context: &Context, qr: &str) -> Lot {
// Does not work when the text `TO:` is used in subject/body _and_ TO: is not the first field.
// we ignore this case.
let addr = if let Some(to_index) = qr.find("TO:") {
let addr = qr[to_index + 3..].trim();
if let Some(semi_index) = addr.find(';') {
addr[..semi_index].trim()
} else {
addr
}
} else {
return format_err!("Invalid MATMSG found").into();
};
let addr = match normalize_address(addr) {
Ok(addr) => addr,
Err(err) => return err.into(),
};
let name = "".to_string();
Lot::from_address(context, name, addr)
}
lazy_static! {
static ref VCARD_NAME_RE: regex::Regex =
regex::Regex::new(r"(?m)^N:([^;]*);([^;\n]*)").unwrap();
static ref VCARD_EMAIL_RE: regex::Regex =
regex::Regex::new(r"(?m)^EMAIL([^:\n]*):([^;\n]*)").unwrap();
}
/// Extract address for the matmsg scheme.
///
/// Scheme: `VCARD:BEGIN\nN:last name;first name;...;\nEMAIL;<type>:addr...;
fn decode_vcard(context: &Context, qr: &str) -> Lot {
let name = VCARD_NAME_RE
.captures(qr)
.map(|caps| {
let last_name = &caps[1];
let first_name = &caps[2];
format!("{} {}", first_name.trim(), last_name.trim())
})
.unwrap_or_default();
let addr = if let Some(caps) = VCARD_EMAIL_RE.captures(qr) {
match normalize_address(caps[2].trim()) {
Ok(addr) => addr,
Err(err) => return err.into(),
}
} else {
return format_err!("Bad e-mail address").into();
};
Lot::from_address(context, name, addr)
}
impl Lot {
pub fn from_text(text: impl AsRef<str>) -> Self {
let mut l = Lot::new();
l.state = LotState::QrText;
l.text1 = Some(text.as_ref().to_string());
l
}
pub fn from_url(url: impl AsRef<str>) -> Self {
let mut l = Lot::new();
l.state = LotState::QrUrl;
l.text1 = Some(url.as_ref().to_string());
l
}
pub fn from_address(context: &Context, name: String, addr: String) -> Self {
let mut l = Lot::new();
l.state = LotState::QrAddr;
l.id = match Contact::add_or_lookup(context, name, addr, Origin::UnhandledQrScan) {
Ok((id, _)) => id,
Err(err) => return err.into(),
};
l
}
}
/// URL decodes a given address, does basic email validation on the result.
fn normalize_address(addr: &str) -> Result<String, Error> {
// urldecoding is needed at least for OPENPGP4FPR but should not hurt in the other cases
let new_addr = percent_decode_str(addr).decode_utf8()?;
let new_addr = addr_normalize(&new_addr);
ensure!(may_be_valid_addr(&new_addr), "Bad e-mail address");
Ok(new_addr.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::dummy_context;
#[test]
fn test_decode_http() {
let ctx = dummy_context();
let res = check_qr(&ctx.ctx, "http://www.hello.com");
assert_eq!(res.get_state(), LotState::QrUrl);
assert_eq!(res.get_id(), 0);
assert_eq!(res.get_text1().unwrap(), "http://www.hello.com");
assert!(res.get_text2().is_none());
}
#[test]
fn test_decode_https() {
let ctx = dummy_context();
let res = check_qr(&ctx.ctx, "https://www.hello.com");
assert_eq!(res.get_state(), LotState::QrUrl);
assert_eq!(res.get_id(), 0);
assert_eq!(res.get_text1().unwrap(), "https://www.hello.com");
assert!(res.get_text2().is_none());
}
#[test]
fn test_decode_text() {
let ctx = dummy_context();
let res = check_qr(&ctx.ctx, "I am so cool");
assert_eq!(res.get_state(), LotState::QrText);
assert_eq!(res.get_id(), 0);
assert_eq!(res.get_text1().unwrap(), "I am so cool");
assert!(res.get_text2().is_none());
}
#[test]
fn test_decode_vcard() {
let ctx = dummy_context();
let res = check_qr(
&ctx.ctx,
"BEGIN:VCARD\nVERSION:3.0\nN:Last;First\nEMAIL;TYPE=INTERNET:stress@test.local\nEND:VCARD"
);
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAddr);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "stress@test.local");
assert_eq!(contact.get_name(), "First Last");
}
#[test]
fn test_decode_matmsg() {
let ctx = dummy_context();
let res = check_qr(
&ctx.ctx,
"MATMSG:TO:\n\nstress@test.local ; \n\nSUB:\n\nSubject here\n\nBODY:\n\nhelloworld\n;;",
);
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAddr);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "stress@test.local");
}
#[test]
fn test_decode_mailto() {
let ctx = dummy_context();
let res = check_qr(
&ctx.ctx,
"mailto:stress@test.local?subject=hello&body=world",
);
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAddr);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "stress@test.local");
}
#[test]
fn test_decode_smtp() {
let ctx = dummy_context();
let res = check_qr(&ctx.ctx, "SMTP:stress@test.local:subjecthello:bodyworld");
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAddr);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "stress@test.local");
}
#[test]
fn test_decode_openpgp_group() {
let ctx = dummy_context();
let res = check_qr(
&ctx.ctx,
"OPENPGP4FPR:79252762C34C5096AF57958F4FC3D21A81B0F0A7#a=cli%40deltachat.de&g=testtesttest&x=h-0oKQf2CDK&i=9JEXlxAqGM0&s=0V7LzL9cxRL"
);
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAskVerifyGroup);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "cli@deltachat.de");
}
#[test]
fn test_decode_openpgp_secure_join() {
let ctx = dummy_context();
let res = check_qr(
&ctx.ctx,
"OPENPGP4FPR:79252762C34C5096AF57958F4FC3D21A81B0F0A7#a=cli%40deltachat.de&n=&i=TbnwJ6lSvD5&s=0ejvbdFSQxB"
);
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAskVerifyContact);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "cli@deltachat.de");
}
}

View File

@@ -4,7 +4,6 @@ use std::sync::{Arc, RwLock};
use rusqlite::{Connection, OpenFlags, Statement, NO_PARAMS};
use thread_local_object::ThreadLocal;
use crate::constants::*;
use crate::context::Context;
use crate::dc_tools::*;
use crate::error::{Error, Result};
@@ -287,7 +286,7 @@ impl Sql {
fn table_exists(conn: &Connection, name: impl AsRef<str>) -> Result<bool> {
let mut exists = false;
conn.pragma(None, "table_info", &format!("{}", name.as_ref()), |_row| {
conn.pragma(None, "table_info", &name.as_ref().to_string(), |_row| {
// will only be executed if the info was found
exists = true;
Ok(())
@@ -689,10 +688,6 @@ fn open(
"ALTER TABLE msgs ADD COLUMN move_state INTEGER DEFAULT 1;",
params![],
)?;
assert_eq!(DC_MOVE_STATE_UNDEFINED as libc::c_int, 0);
assert_eq!(DC_MOVE_STATE_PENDING as libc::c_int, 1);
assert_eq!(DC_MOVE_STATE_STAY as libc::c_int, 2);
assert_eq!(DC_MOVE_STATE_MOVING as libc::c_int, 3);
dbversion = 48;
sql.set_config_int(context, "dbversion", 48)?;
@@ -835,7 +830,7 @@ where
&err,
querystr.as_ref()
);
Err(err.into())
Err(err)
}
}
}
@@ -1008,10 +1003,10 @@ pub fn housekeeping(context: &Context) {
let name_f = entry.file_name();
let name_s = name_f.to_string_lossy();
if is_file_in_use(&mut files_in_use, None, &name_s)
|| is_file_in_use(&mut files_in_use, Some(".increation"), &name_s)
|| is_file_in_use(&mut files_in_use, Some(".waveform"), &name_s)
|| is_file_in_use(&mut files_in_use, Some("-preview.jpg"), &name_s)
if is_file_in_use(&files_in_use, None, &name_s)
|| is_file_in_use(&files_in_use, Some(".increation"), &name_s)
|| is_file_in_use(&files_in_use, Some(".waveform"), &name_s)
|| is_file_in_use(&files_in_use, Some("-preview.jpg"), &name_s)
{
continue;
}
@@ -1047,8 +1042,8 @@ pub fn housekeeping(context: &Context) {
unreferenced_count,
entry.file_name()
);
let path = entry.path().to_c_string().unwrap();
dc_delete_file(context, path.as_ptr());
let path = entry.path();
dc_delete_file(context, path);
}
}
Err(err) => {

View File

@@ -2,9 +2,15 @@
//!
//! This module is only compiled for test runs.
use std::ffi::CStr;
use libc::uintptr_t;
use tempfile::{tempdir, TempDir};
use crate::config::Config;
use crate::constants::{Event, KeyType};
use crate::context::{dc_context_new, dc_open, Context};
use crate::key;
use crate::types::dc_callback_t;
/// A Context and temporary directory.
@@ -44,3 +50,127 @@ pub fn test_context(cb: Option<dc_callback_t>) -> TestContext {
pub fn dummy_context() -> TestContext {
test_context(None)
}
pub unsafe extern "C" fn logging_cb(
_ctx: &Context,
evt: Event,
_d1: uintptr_t,
d2: uintptr_t,
) -> uintptr_t {
let to_str = |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap();
match evt {
Event::INFO => println!("I: {}", to_str(d2)),
Event::WARNING => println!("W: {}", to_str(d2)),
Event::ERROR => println!("E: {}", to_str(d2)),
_ => (),
}
0
}
/// Creates Alice with a pre-generated keypair.
///
/// Returns the address of the keypair created (alice@example.org).
pub fn configure_alice_keypair(ctx: &Context) -> String {
let addr = String::from("alice@example.org");
ctx.set_config(Config::ConfiguredAddr, Some(&addr)).unwrap();
// The keypair was created using:
// let (public, private) = crate::pgp::dc_pgp_create_keypair("alice@example.com")
// .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 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=="
),
KeyType::Private,
)
.unwrap();
let saved = key::dc_key_save_self_keypair(&ctx, &public, &private, &addr, 1, &ctx.sql);
assert_eq!(saved, true, "Failed to save Alice's key");
addr
}

View File

@@ -16,8 +16,6 @@ pub use rusqlite::ffi::*;
pub type dc_callback_t =
unsafe extern "C" fn(_: &Context, _: Event, _: uintptr_t, _: uintptr_t) -> uintptr_t;
pub type dc_move_state_t = u32;
pub type dc_receive_imf_t = unsafe fn(
_: &Context,
_: *const libc::c_char,
@@ -36,14 +34,9 @@ pub type dc_precheck_imf_t =
pub type dc_set_config_t = fn(_: &Context, _: &str, _: Option<&str>) -> ();
pub type dc_get_config_t = fn(_: &Context, _: &str) -> Option<String>;
pub type sqlite_int64 = i64;
pub type sqlite3_int64 = sqlite_int64;
pub type int32_t = i32;
pub type int64_t = i64;
pub type uintptr_t = libc::uintptr_t;
pub type size_t = libc::size_t;
pub type ssize_t = libc::ssize_t;
pub type uint32_t = libc::c_uint;
pub type uint8_t = libc::c_uchar;
pub type uint16_t = libc::c_ushort;

View File

@@ -54,27 +54,6 @@ pub(crate) unsafe fn strcasecmp(s1: *const libc::c_char, s2: *const libc::c_char
}
}
pub(crate) unsafe fn strncasecmp(
s1: *const libc::c_char,
s2: *const libc::c_char,
n: libc::size_t,
) -> 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();
let m1 = std::cmp::min(n, s1.len());
let m2 = std::cmp::min(n, s2.len());
if s1[..m1] == s2[..m2] {
0
} else {
1
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -6,20 +6,13 @@ use std::ffi::CString;
use mmime::mailimf_types::*;
use tempfile::{tempdir, TempDir};
use deltachat::chat::{self, Chat};
use deltachat::config;
use deltachat::constants::*;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::dc_array::*;
use deltachat::dc_chat::*;
use deltachat::dc_configure::*;
use deltachat::dc_imex::*;
use deltachat::dc_location::*;
use deltachat::dc_lot::*;
use deltachat::dc_mimeparser::*;
use deltachat::dc_qr::*;
use deltachat::dc_saxparser::*;
use deltachat::dc_securejoin::*;
use deltachat::dc_tools::*;
use deltachat::key::*;
use deltachat::keyring::*;
@@ -40,56 +33,16 @@ static mut S_EM_SETUPFILE: *const libc::c_char =
as *const u8 as *const libc::c_char;
unsafe fn stress_functions(context: &Context) {
let mut saxparser: dc_saxparser_t = dc_saxparser_t {
starttag_cb: None,
endtag_cb: None,
text_cb: None,
userdata: 0 as *mut libc::c_void,
};
dc_saxparser_init(&mut saxparser, 0 as *mut libc::c_void);
dc_saxparser_parse(
&mut saxparser,
b"<tag attr=val=\x00" as *const u8 as *const libc::c_char,
);
dc_saxparser_parse(
&mut saxparser,
b"<tag attr=\"val\"=\x00" as *const u8 as *const libc::c_char,
);
if 0 != dc_is_open(context) {
if 0 != dc_file_exist(
context,
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
) || 0
!= dc_file_exist(
context,
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
)
|| 0 != dc_file_exist(
context,
b"$BLOBDIR/foobar.dadada\x00" as *const u8 as *const libc::c_char,
)
|| 0 != dc_file_exist(
context,
b"$BLOBDIR/foobar-folder\x00" as *const u8 as *const libc::c_char,
)
if dc_file_exist(context, "$BLOBDIR/foobar")
|| dc_file_exist(context, "$BLOBDIR/dada")
|| dc_file_exist(context, "$BLOBDIR/foobar.dadada")
|| dc_file_exist(context, "$BLOBDIR/foobar-folder")
{
dc_delete_file(
context,
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
);
dc_delete_file(
context,
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
);
dc_delete_file(
context,
b"$BLOBDIR/foobar.dadada\x00" as *const u8 as *const libc::c_char,
);
dc_delete_file(
context,
b"$BLOBDIR/foobar-folder\x00" as *const u8 as *const libc::c_char,
);
dc_delete_file(context, "$BLOBDIR/foobar");
dc_delete_file(context, "$BLOBDIR/dada");
dc_delete_file(context, "$BLOBDIR/foobar.dadada");
dc_delete_file(context, "$BLOBDIR/foobar-folder");
}
dc_write_file(
context,
@@ -97,25 +50,10 @@ unsafe fn stress_functions(context: &Context) {
b"content\x00" as *const u8 as *const libc::c_char as *const libc::c_void,
7i32 as size_t,
);
assert_ne!(
0,
dc_file_exist(
context,
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
)
);
assert!(dc_file_exist(context, "$BLOBDIR/foobar",));
assert!(!dc_file_exist(context, "$BLOBDIR/foobarx"));
assert_eq!(
0,
dc_file_exist(
context,
b"$BLOBDIR/foobarx\x00" as *const u8 as *const libc::c_char,
)
);
assert_eq!(
dc_get_filebytes(
context,
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
),
dc_get_filebytes(context, "$BLOBDIR/foobar",),
7i32 as libc::c_ulonglong
);
@@ -124,32 +62,13 @@ unsafe fn stress_functions(context: &Context) {
context.get_blobdir(),
b"foobar\x00" as *const u8 as *const libc::c_char,
);
assert!(dc_is_blobdir_path(context, abs_path));
assert!(dc_is_blobdir_path(
context,
b"$BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
));
assert!(!dc_is_blobdir_path(
context,
b"/BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
));
assert_ne!(0, dc_file_exist(context, abs_path));
assert!(dc_is_blobdir_path(context, as_str(abs_path)));
assert!(dc_is_blobdir_path(context, "$BLOBDIR/fofo",));
assert!(!dc_is_blobdir_path(context, "/BLOBDIR/fofo",));
assert!(dc_file_exist(context, as_path(abs_path)));
free(abs_path as *mut libc::c_void);
assert_ne!(
0,
dc_copy_file(
context,
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
)
);
assert_eq!(
dc_get_filebytes(
context,
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
),
7
);
assert!(dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada",));
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/dada",), 7);
let mut buf: *mut libc::c_void = 0 as *mut libc::c_void;
let mut buf_bytes: size_t = 0;
@@ -170,41 +89,11 @@ unsafe fn stress_functions(context: &Context) {
);
free(buf as *mut _);
assert_ne!(
0,
dc_delete_file(
context,
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
)
);
assert_ne!(
0,
dc_delete_file(
context,
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
)
);
assert_ne!(
0,
dc_create_folder(
context,
b"$BLOBDIR/foobar-folder\x00" as *const u8 as *const libc::c_char,
)
);
assert_ne!(
0,
dc_file_exist(
context,
b"$BLOBDIR/foobar-folder\x00" as *const u8 as *const libc::c_char,
)
);
assert_ne!(
0,
dc_delete_file(
context,
b"$BLOBDIR/foobar-folder\x00" as *const u8 as *const libc::c_char,
)
);
assert!(dc_delete_file(context, "$BLOBDIR/foobar"));
assert!(dc_delete_file(context, "$BLOBDIR/dada"));
assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder"));
assert!(dc_file_exist(context, "$BLOBDIR/foobar-folder",));
assert!(dc_delete_file(context, "$BLOBDIR/foobar-folder"));
let fn0: *mut libc::c_char = dc_get_fine_pathNfilename(
context,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
@@ -237,7 +126,7 @@ unsafe fn stress_functions(context: &Context) {
),
0
);
assert_ne!(0, dc_delete_file(context, fn0));
assert!(dc_delete_file(context, as_path(fn0)));
free(fn0 as *mut libc::c_void);
free(fn1 as *mut libc::c_void);
}
@@ -520,63 +409,55 @@ unsafe fn stress_functions(context: &Context) {
0
);
free(buf_1 as *mut libc::c_void);
if 0 != dc_is_configured(context) {
let setupcode = dc_create_setup_code(context);
let setupcode_c = CString::yolo(setupcode.clone());
let setupfile = dc_render_setup_file(context, &setupcode).unwrap();
let setupfile_c = CString::yolo(setupfile);
let payload: *mut libc::c_char;
let mut headerline_2: *const libc::c_char = 0 as *const libc::c_char;
payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr());
assert!(payload.is_null());
assert!(!dc_split_armored_data(
payload,
&mut headerline_2,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
));
assert!(!headerline_2.is_null());
assert_eq!(
strcmp(
headerline_2,
b"-----BEGIN PGP PRIVATE KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
),
0
);
free(payload as *mut libc::c_void);
}
if 0 != dc_is_configured(context) {
let qr: *mut libc::c_char = dc_get_securejoin_qr(context, 0i32 as uint32_t);
assert!(
!(strlen(qr) > 55
&& strncmp(
qr,
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
12,
) == 0i32
&& strncmp(
&mut *qr.offset(52isize),
b"#a=\x00" as *const u8 as *const libc::c_char,
3,
) == 0i32)
);
let mut res: *mut dc_lot_t = dc_check_qr(context, qr);
assert!(res.is_null());
assert!(!((*res).state == 200i32 || (*res).state == 220i32 || (*res).state == 230i32));
// Cant check, no configured context
// assert!(dc_is_configured(context) != 0, "Missing configured context");
dc_lot_unref(res);
free(qr as *mut libc::c_void);
res =
dc_check_qr(context,
b"BEGIN:VCARD\nVERSION:3.0\nN:Last;First\nEMAIL;TYPE=INTERNET:stress@test.local\nEND:VCARD\x00"
as *const u8 as *const libc::c_char);
assert!(res.is_null());
assert!(!((*res).state == 320i32));
assert!(!((*res).id != 0i32 as libc::c_uint));
dc_lot_unref(res);
};
// let setupcode = dc_create_setup_code(context);
// let setupcode_c = CString::yolo(setupcode.clone());
// let setupfile = dc_render_setup_file(context, &setupcode).unwrap();
// let setupfile_c = CString::yolo(setupfile);
// let mut headerline_2: *const libc::c_char = 0 as *const libc::c_char;
// let payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr());
// assert!(payload.is_null());
// assert!(!dc_split_armored_data(
// payload,
// &mut headerline_2,
// 0 as *mut *const libc::c_char,
// 0 as *mut *const libc::c_char,
// 0 as *mut *const libc::c_char,
// ));
// assert!(!headerline_2.is_null());
// assert_eq!(
// strcmp(
// headerline_2,
// b"-----BEGIN PGP PRIVATE KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
// ),
// 0
// );
// free(payload as *mut libc::c_void);
// Cant check, no configured context
// assert!(dc_is_configured(context) != 0, "missing configured context");
// let qr = dc_get_securejoin_qr(context, 0);
// assert!(!qr.is_null(), "Invalid qr code generated");
// let qr_r = as_str(qr);
// assert!(qr_r.len() > 55);
// assert!(qr_r.starts_with("OPENPGP4FPR:"));
// let res = dc_check_qr(context, qr);
// let s = res.get_state();
// assert!(
// s == QrState::AskVerifyContact
// || s == QrState::FprMissmatch
// || s == QrState::FprWithoutAddr
// );
// free(qr.cast());
}
#[test]
@@ -772,43 +653,6 @@ unsafe fn create_test_context() -> TestContext {
TestContext { ctx: ctx, dir: dir }
}
#[test]
fn test_dc_kml_parse() {
unsafe {
let context = create_test_context();
let xml: *const libc::c_char =
b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n<Document addr=\"user@example.org\">\n<Placemark><Timestamp><when>2019-03-06T21:09:57Z</when></Timestamp><Point><coordinates accuracy=\"32.000000\">9.423110,53.790302</coordinates></Point></Placemark>\n<PlaceMARK>\n<Timestamp><WHEN > \n\t2018-12-13T22:11:12Z\t</wHeN></Timestamp><Point><coordinates aCCuracy=\"2.500000\"> 19.423110 \t , \n 63.790302\n </coordinates></Point></Placemark>\n</Document>\n</kml>\x00"
as *const u8 as *const libc::c_char;
let mut kml = dc_kml_parse(&context.ctx, xml, strlen(xml));
assert!(!kml.addr.is_null());
assert_eq!(as_str(kml.addr as *const libc::c_char), "user@example.org",);
let locations_ref = &kml.locations.as_ref().unwrap();
assert_eq!(locations_ref.len(), 2);
assert!(locations_ref[0].latitude > 53.6f64);
assert!(locations_ref[0].latitude < 53.8f64);
assert!(locations_ref[0].longitude > 9.3f64);
assert!(locations_ref[0].longitude < 9.5f64);
assert!(locations_ref[0].accuracy > 31.9f64);
assert!(locations_ref[0].accuracy < 32.1f64);
assert_eq!(locations_ref[0].timestamp, 1551906597);
assert!(locations_ref[1].latitude > 63.6f64);
assert!(locations_ref[1].latitude < 63.8f64);
assert!(locations_ref[1].longitude > 19.3f64);
assert!(locations_ref[1].longitude < 19.5f64);
assert!(locations_ref[1].accuracy > 2.4f64);
assert!(locations_ref[1].accuracy < 2.6f64);
assert_eq!(locations_ref[1].timestamp, 1544739072);
dc_kml_unref(&mut kml);
}
}
#[test]
fn test_dc_mimeparser_with_context() {
unsafe {
@@ -825,22 +669,14 @@ fn test_dc_mimeparser_with_context() {
"inner-subject",
);
let mut of: *mut mailimf_optional_field = dc_mimeparser_lookup_optional_field(
&mimeparser,
b"X-Special-A\x00" as *const u8 as *const libc::c_char,
);
let mut of: *mut mailimf_optional_field =
dc_mimeparser_lookup_optional_field(&mimeparser, "X-Special-A");
assert_eq!(as_str((*of).fld_value as *const libc::c_char), "special-a",);
of = dc_mimeparser_lookup_optional_field(
&mimeparser,
b"Foo\x00" as *const u8 as *const libc::c_char,
);
of = dc_mimeparser_lookup_optional_field(&mimeparser, "Foo");
assert_eq!(as_str((*of).fld_value as *const libc::c_char), "Bar",);
of = dc_mimeparser_lookup_optional_field(
&mimeparser,
b"Chat-Version\x00" as *const u8 as *const libc::c_char,
);
of = dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Version");
assert_eq!(as_str((*of).fld_value as *const libc::c_char), "1.0",);
assert_eq!(mimeparser.parts.len(), 1);
@@ -891,19 +727,16 @@ fn test_get_contacts() {
unsafe {
let context = create_test_context();
let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap();
assert_eq!(dc_array_get_cnt(contacts), 0);
dc_array_unref(contacts);
assert_eq!(contacts.len(), 0);
let id = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
assert_ne!(id, 0);
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).unwrap();
assert_eq!(dc_array_get_cnt(contacts), 1);
dc_array_unref(contacts);
assert_eq!(contacts.len(), 1);
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap();
assert_eq!(dc_array_get_cnt(contacts), 0);
dc_array_unref(contacts);
assert_eq!(contacts.len(), 0);
}
}
@@ -914,17 +747,15 @@ fn test_chat() {
let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
assert_ne!(contact1, 0);
let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
let chat_id = chat::create_by_contact_id(&context.ctx, contact1).unwrap();
assert!(chat_id > 9, "chat_id too small {}", chat_id);
let chat = dc_chat_new(&context.ctx);
assert!(dc_chat_load_from_db(chat, chat_id));
let chat = Chat::load_from_db(&context.ctx, chat_id).unwrap();
let chat2_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
let chat2_id = chat::create_by_contact_id(&context.ctx, contact1).unwrap();
assert_eq!(chat2_id, chat_id);
let chat2 = dc_chat_new(&context.ctx);
assert!(dc_chat_load_from_db(chat2, chat2_id));
let chat2 = Chat::load_from_db(&context.ctx, chat2_id).unwrap();
assert_eq!(as_str((*chat2).name), as_str((*chat).name));
assert_eq!(chat2.name, chat.name);
}
}