Compare commits

..

197 Commits

Author SHA1 Message Date
holger krekel
5d47d25c1e fix some longer standing nonsense code that sent to misleading MSG_READ events instead of one correct one 2019-08-08 21:27:33 +02:00
holger krekel
e4f9446e3c (jikstra, hpk) fix a logic bug introduced with the stock-string merge which set the better message only if it was empty 2019-08-08 21:27:30 +02:00
holger krekel
abbbd0cb67 fix a failure which blocked correctly sending out messages (dc_job_add_smtp mis-set filename) 2019-08-08 21:27:12 +02:00
dignifiedquire
ae9f52e001 fix: improve some string handling in the message recieve path 2019-08-08 21:27:05 +02:00
Friedel Ziegelmayer
2414a618e2 Merge pull request #305 from link2xt/keyhistory
Remove empty keyhistory module
2019-08-08 16:20:55 +02:00
Alexander Krotov
7c34806125 Remove empty keyhistory module 2019-08-08 13:32:45 +03:00
Friedel Ziegelmayer
c1f19aa9d3 Merge pull request #292 from link2xt/dc_get_abs_path_safe
Implement dc_get_abs_path_safe
2019-08-08 12:28:19 +02:00
Friedel Ziegelmayer
aab2336223 Merge pull request #304 from link2xt/set_self_key_bool
Return bool from set_self_key()
2019-08-08 12:20:15 +02:00
Friedel Ziegelmayer
7863f5719c Merge pull request #303 from deltachat/fix_chat_list_no_messages
fix own messages displayed as noMsgs in summary
2019-08-08 12:19:31 +02:00
Alexander Krotov
8c933f8fa8 Return bool from set_self_key() 2019-08-08 12:27:01 +03:00
Simon Laux
3d83409375 fix own messages displayed as noMsgs in summary 2019-08-08 08:48:58 +02:00
Alexander Krotov
76dbb37609 Implement dc_get_abs_path_safe 2019-08-08 04:20:12 +03:00
Jikstra
e9ff02bdc5 Merge pull request #296 from deltachat/remove_gotos_dc_e2ee
Remove gotos from dc_e2ee
2019-08-08 00:22:51 +02:00
Jikstra
602d145348 Merge pull request #297 from deltachat/remove_gotos_dc_mimefactory
Remove gotos from dc_mimefactory
2019-08-08 00:15:28 +02:00
Jikstra
ee264117d3 Merge pull request #298 from deltachat/remove_gotos_dc_strencode
Remove gotos from dc_strencode
2019-08-07 23:41:33 +02:00
Jikstra
721dcd7ccd Merge pull request #295 from deltachat/remove_gotos_dc_saxparser
Remove gotos from dc_saxparser
2019-08-07 23:37:50 +02:00
jikstra
cb138fdcb7 Remove comment 2019-08-07 23:17:11 +02:00
jikstra
a993c256e2 Remove comment 2019-08-07 23:15:55 +02:00
Friedel Ziegelmayer
ea6972118a refactor: rusty contact
* refactor(contact): rename and rusty memory allocations
* refactor(contact): use enum to indidcate origin
* refactor(contact): safe blocking and unblocking api
* refactor(contact): only safe and no more cstrings
2019-08-07 22:20:48 +02:00
jikstra
70f6aa9d3a run cargo fmt 2019-08-07 21:21:13 +02:00
jikstra
e1d7871754 Remove gotos in dc_strencode 2019-08-07 21:20:35 +02:00
jikstra
655884b559 run cargo fmt 2019-08-07 21:13:54 +02:00
jikstra
e0dbd47185 Remove gotos from dc_mimefactory 2019-08-07 21:13:20 +02:00
jikstra
925759a922 run cargo fmt 2019-08-07 20:57:58 +02:00
jikstra
9034f316ba Remove gotos from dc_e2ee_encrypt function 2019-08-07 20:57:42 +02:00
jikstra
0276f97d96 run cargo fmt 2019-08-07 20:52:38 +02:00
jikstra
ac2f9fdee5 remove gotos from new_data_part function 2019-08-07 20:52:18 +02:00
jikstra
76b3c532b1 run cargo fmt 2019-08-07 20:40:50 +02:00
Dmitry Bogatov
760332262d Add more assertions to `deltachat-ffi' library
Change code to panic! on invalid input (null pointers, out-of-range
identifiers) instead of silently doing nothing.
2019-08-07 20:36:00 +02:00
Dmitry Bogatov
bc2314586c Avoid unstable `inner_deref' feature 2019-08-07 20:31:02 +02:00
Dmitry Bogatov
765ac2005e Change type of dc_msg_t.text to String
Also, remove `send-garbage' command from REPL, since it is not possible to send
non-utf8 string anymore.
2019-08-07 20:31:02 +02:00
Dmitry Bogatov
d4650ba4a9 Add Rust clone of python `prepare_and_send' test
Previous version of patch, that changed type of `dc_msg_t.text' to String had
been breaking `prepare_and_send' test in Python.

This commit adds same regression test, reimplemented in Rust, since running
Rust tests is simplier and faster.
2019-08-07 20:31:02 +02:00
jikstra
b715df9e97 make ok_to_continue variable mutable 2019-08-07 19:16:21 +02:00
jikstra
acb3d14d7d remove gotos in dc_e2ee in dcrypt_part function 2019-08-07 19:14:35 +02:00
jikstra
c0e3c7a9df run cargo fmt 2019-08-07 19:01:24 +02:00
jikstra
164130a83f Replace gotos with ok_to_continue (is_valid) approach 2019-08-07 19:00:28 +02:00
Dmitry Bogatov
4d9bd46c9d Add note about possible cause of lost messages 2019-08-06 22:09:43 +02:00
jikstra
76d3d86a9e import dc_extract... method for rustdoc example 2019-08-06 21:33:01 +02:00
jikstra
c0b1e41820 Improve function documentation 2019-08-06 21:33:01 +02:00
jikstra
10689f45f4 cargo fmt 2019-08-06 21:33:01 +02:00
jikstra
205e2d4e99 Fix rustdoc 2019-08-06 21:33:01 +02:00
jikstra
5b368960c0 Apply requested changes 2019-08-06 21:33:01 +02:00
jikstra
9ecd0f80dc Fix test and simplify if gate 2019-08-06 21:33:01 +02:00
jikstra
45eec35e6e Make dc_extract_grpid_from_rfc724_mid take/return &str instead of String 2019-08-06 21:33:01 +02:00
jikstra
b74a86f617 Run cargo fmt 2019-08-06 21:33:01 +02:00
jikstra
ff95c44d51 Make receive_imf use safe version of dc_extract_grpid_from_rfc724_mid
- Remove unused unsafe wrapper
- Adjust tests to use safe version
2019-08-06 21:33:01 +02:00
jikstra
d5168916df Refactor dc_extract_grpid_from_rfc724_mid_list and rename test 2019-08-06 21:33:01 +02:00
jikstra
282f964f2f Add tests for dc_extract_grpid_from_rfc724_mid and implement safe
version of dc_extract_grpid_from_rfc724_mid
2019-08-06 21:33:01 +02:00
Simon Laux
7b5073b634 ?1 and ?2 2019-08-06 09:02:41 +02:00
Simon Laux
59bb9add2a fix an type error 2019-08-06 09:02:41 +02:00
Simon Laux
ed95752f8f use DC_CONTACT_ID_SELF constant instead of 1 2019-08-06 09:02:41 +02:00
Simon Laux
81719c4b33 add coment that helps with understanding the code 2019-08-06 09:02:41 +02:00
Simon Laux
fc6019e3c9 fix sql statement in order to close #163 2019-08-06 09:02:41 +02:00
Alexander Krotov
5811248bfc Merge pull request #289 from KAction/new-assert
python: assert that underlying dc_msg_t* for Message is not NULL
2019-08-04 12:58:04 +00:00
Dmitry Bogatov
47b76ceb3e python: assert that underlying dc_msg_t* for Message is not NULL 2019-08-04 09:17:54 +00:00
Floris Bruynooghe
0051720d1b Kill to_cstring with fire
I swear I already did this, see #273.
2019-08-04 09:49:41 +02:00
Jikstra
d814dffbb0 Merge pull request #281 from deltachat/new-mmime
chore: update mmime to 0.1.1
2019-08-03 15:31:36 +02:00
dignifiedquire
4e1e32df65 chore: update mmime to 0.1.1 2019-08-03 11:12:21 +02:00
Floris Bruynooghe
d2b23b727b Restore carriage-return handling to how it was in dcc
Some part of the translation seems to have got this wrong somewhere.
Add a test and restore handling to something more sane again.
2019-08-03 01:00:59 +02:00
Floris Bruynooghe
d37dda6f50 Turn the function safe 2019-08-03 01:00:59 +02:00
Floris Bruynooghe
c568d5dcac Safe string usage
Mostly safe string usage, but some other minor cleanups:

- This isn't used from any C code, so no extern C.
- Use Config enums rather than strings.
- Clean up old unused sql statements.
2019-08-03 01:00:59 +02:00
Floris Bruynooghe
227ef1b467 Add unittest
This should give us some confidence in the refactoring.
2019-08-03 01:00:59 +02:00
dignifiedquire
a8cea64f39 feat(deps): update rusqlite to official version again
A fix for the non utf8 strings has been merged and released in `0.20`
2019-08-03 00:11:23 +02:00
Friedel Ziegelmayer
294e855bbe Merge pull request #276 from link2xt/lines
Return Vec instead of carray from dc_split_into_lines
2019-08-02 20:28:23 +02:00
Alexander Krotov
c5eef21645 Remove carray use and comment 2019-08-02 13:12:32 +03:00
Alexander Krotov
d64fcece5b Return Vec instead of carray from dc_split_into_lines 2019-08-02 13:12:25 +03:00
Friedel Ziegelmayer
0a9f3ae160 Use Vec instead of carray in dc_forward_msgs() (#277)
Use Vec instead of carray in dc_forward_msgs()
2019-08-02 11:05:28 +02:00
Friedel Ziegelmayer
acbedbd352 Remove unused function dc_simplify_simplify (#278)
Remove unused function dc_simplify_simplify
2019-08-02 11:05:05 +02:00
Alexander Krotov
e78b879c9e Remove unused function dc_simplify_simplify 2019-08-02 09:29:54 +03:00
Alexander Krotov
1145c3533b Use Vec instead of carray in dc_forward_msgs() 2019-08-02 01:34:15 +03:00
Friedel Ziegelmayer
0d41a78182 Merge pull request #272 from link2xt/parts_vec
Store dc_mimeparser_t::parts as Vec instead of carray
2019-08-01 23:50:53 +02:00
Alexander Krotov
a4f94dbf86 Store dc_mimeparser_t::parts as Vec instead of carray 2019-08-01 23:21:56 +03:00
Floris Bruynooghe
b6b0849bce Remove to_cstring() naming convention ambiguity
Add a trait for str.strdup() to replace to_cstring() which avoid the
signature ambiguity with .to_string().

Also instruduce CString::yolo() as a shortcut to
CString::new().unwrap() and use it whenever the variable does can be
deallocated by going out of scope.  This is less error prone.

Use some Path.to_c_string() functions where possible.
2019-08-01 19:06:39 +02:00
Friedel Ziegelmayer
e7428887d0 Introduce new type: Viewtype (#256)
Introduce new type: Viewtype
2019-08-01 12:17:33 +02:00
Friedel Ziegelmayer
a7269e7096 Merge pull request #274 from KAction/refine-bool
Narrow types of struct fields, that use only two values.
2019-08-01 11:22:55 +02:00
Dmitry Bogatov
7394666266 Refine type of SmtpState.doing_jobs to bool 2019-08-01 06:51:52 +00:00
Dmitry Bogatov
8eb5cec9ce Refine type of SmtpState.suspended to bool 2019-08-01 06:41:50 +00:00
Dmitry Bogatov
b2807429cc Refine type of SmtpState.probe_network to bool 2019-08-01 06:37:06 +00:00
Dmitry Bogatov
07d7316a9f Refine type of Context.perform_inbox_jobs_needed to bool 2019-08-01 06:31:58 +00:00
Dmitry Bogatov
1f9807ccfe Refine type of Context.probe_network to bool 2019-08-01 06:24:50 +00:00
Dmitry Bogatov
3358d09148 Derive and use Display trait for Viewtype 2019-08-01 06:05:56 +00:00
Dmitry Bogatov
c04c8ff103 Introduce new enum: Viewtype
With this change, kind of message is represented by value of enum
`Viewtype' instead of raw libc::c_int, providing more type safety. This
enum replaces DC_MSG_* constants. The only way to create `Viewtype' from
libc::c_int is smart constructor.

With this change, functions `dc_get_chat_media' and `dc_get_next_media' became
less forgiving about invalid message type arguments. Previously, invalid
message types were implicitly interpreted as 0 (Viewtype::Unknown). Now,
function calls with invalid message type arguments are rejected (error code
returned) on FFI boundary.

Additionally, when `Viewtype' is read from database, it is checked to have
sensible value.

No tests assumed forgiving behaviour.
2019-08-01 06:05:56 +00:00
Friedel Ziegelmayer
e7456248a0 Merge pull request #244 from link2xt/dc_location_vec
Replace some dc_array_t with Vec<dc_location>
2019-07-31 22:15:35 +02:00
Friedel Ziegelmayer
3370b9cfcb Merge pull request #268 from KAction/string-os-name
Change Context.os_name field to String
2019-07-31 22:13:18 +02:00
Friedel Ziegelmayer
022c7c2f07 Merge pull request #266 from KAction/dc_msg_is_starred_bool
Make dc_msg_is_starred() return bool
2019-07-31 22:06:48 +02:00
Alexander Krotov
9e50e77031 Simplify dc_initiate_key_transfer() 2019-07-31 22:06:05 +02:00
Friedel Ziegelmayer
39e9cfd908 Merge pull request #269 from link2xt/dc_array_sort_ids
Make dc_array_sort_ids() safe and move it into impl
2019-07-31 22:04:17 +02:00
Friedel Ziegelmayer
c29c893426 Merge pull request #270 from link2xt/get_backoff_time_offset-safe
Mark get_backoff_time_offset as safe
2019-07-31 22:03:29 +02:00
Friedel Ziegelmayer
16028cc5dc Merge pull request #271 from link2xt/reports_vec
Replace dc_mimeparser_t::reports carray with Vec
2019-07-31 22:02:35 +02:00
Dmitry Bogatov
39e530f759 Change Context.os_name field to String 2019-07-31 15:45:35 +00:00
jikstra
8274c6bf17 Allow non_snake_case in dc_replace_bad_utf8_chars function 2019-07-31 16:26:17 +02:00
jikstra
566d255b33 run cargo fmt 2019-07-31 16:26:17 +02:00
jikstra
9fb9fb0fc1 Remove goto/current_block logic with OK_TO_CONTINUE. Keeps identation 2019-07-31 16:26:17 +02:00
Alexander Krotov
164a8fe39a encode_66bits_as_base64: use base64 crate 2019-07-31 16:25:01 +02:00
Alexander Krotov
0b679f8b95 dc_tools: Make dc_create_id() safe 2019-07-31 16:25:01 +02:00
Alexander Krotov
8267ffb8d2 Replace dc_mimeparser_t::reports carray with Vec 2019-07-31 15:57:05 +03:00
Alexander Krotov
7bc338fc72 Mark get_backoff_time_offset as safe 2019-07-31 15:51:50 +03:00
Alexander Krotov
d6dae0a9e8 Make dc_array_sort_ids() safe and move it into impl 2019-07-31 03:21:13 +03:00
Dmitry Bogatov
a41c0614cc Make dc_msg_is_starred() return bool 2019-07-30 22:57:28 +00:00
Dmitry Bogatov
73298c0273 chore: fix and enforce compiler warnings on CI 2019-07-30 09:58:28 +02:00
Friedel Ziegelmayer
b44c7928f2 Merge pull request #265 from link2xt/safe_cb_set_config
Make cb_set_config() safe
2019-07-30 09:53:56 +02:00
Friedel Ziegelmayer
dd9b83dc24 Merge pull request #264 from link2xt/key-to_asc-safe
Make key::to_asc return String
2019-07-30 09:53:42 +02:00
Friedel Ziegelmayer
e79c168279 Merge pull request #263 from link2xt/sql-is_file_in_use-safe
sql.rs: make is_file_in_use() safe
2019-07-30 09:53:13 +02:00
Alexander Krotov
707c8c2830 Make dc_split_armored_data return bool (#251)
* Make dc_split_armored_data return bool

* Remove double negations
2019-07-30 08:46:36 +02:00
Alexander Krotov
f87c98d6ea Make cb_set_config() safe 2019-07-30 02:43:50 +03:00
Alexander Krotov
76e76470e0 Replace range loop with foreach loop 2019-07-30 02:34:05 +03:00
Alexander Krotov
ae6c41a019 dc_kml_t: replace Option<dc_array_t> with Option<Vec<dc_location>> 2019-07-30 02:34:05 +03:00
Alexander Krotov
81a84620eb Store dc_kml_t::locations as Option<dc_array_t> instead of pointer 2019-07-30 02:34:05 +03:00
Alexander Krotov
14e42b48bd dc_get_locations: use from(Vec<dc_location>) instead of add_location 2019-07-30 02:34:05 +03:00
Alexander Krotov
2688a397aa Implement From<Vec<dc_location>> for dc_array_t 2019-07-30 02:32:24 +03:00
Alexander Krotov
3ace4fcc2f Make key::to_asc return String 2019-07-30 02:22:08 +03:00
Friedel Ziegelmayer
9314a5a8fd Merge pull request #252 from link2xt/dc_get_config-safe
Make dc_get_config_t safe
2019-07-30 00:25:29 +02:00
Friedel Ziegelmayer
5bf2d7c5ac Merge pull request #253 from link2xt/is_file_size_okay-bool
Return bool from is_file_size_okay
2019-07-30 00:24:13 +02:00
Friedel Ziegelmayer
1fbd16310a Merge pull request #262 from link2xt/thread-constants
dc_job.rs: add DC_{IMAP,SMTP}_THREAD constants
2019-07-30 00:23:34 +02:00
Friedel Ziegelmayer
7ba1a6f79f Merge pull request #255 from link2xt/dc_open-safer
Make dc_open arguments rusty
2019-07-30 00:21:03 +02:00
Friedel Ziegelmayer
2c66837df4 Merge pull request #261 from link2xt/dc_timestamp_to_str
Remove unsafe version of dc_timestamp_to_str
2019-07-30 00:19:33 +02:00
Alexander Krotov
21b4147d15 sql.rs: make is_file_in_use() safe 2019-07-30 01:15:51 +03:00
KAction
0c082fac7b refactor: remove unused import of qsort() from C library ( 2019-07-30 00:10:22 +02:00
Alexander Krotov
03603a48a0 dc_job.rs: add DC_{IMAP,SMTP}_THREAD constants 2019-07-29 20:20:52 +03:00
Alexander Krotov
27342f50b5 Remove unsafe version of dc_timestamp_to_str 2019-07-29 18:45:12 +03:00
Alexander Krotov
2d5b04148f Make dc_open arguments rusty 2019-07-29 04:05:21 +03:00
Alexander Krotov
dfce34f275 Return bool from is_file_size_okay 2019-07-29 03:14:21 +03:00
Friedel Ziegelmayer
188da2a020 refactor(params): rustify 2019-07-29 01:49:53 +02:00
Alexander Krotov
3f445a3a6c Make dc_get_config_t safe 2019-07-28 23:57:28 +03:00
Dmitry Bogatov
669ed0e0df Override non_camel_case_types warning on per-declaration basis 2019-07-28 22:44:21 +02:00
Dmitry Bogatov
c6ccfd824e Override `non_shake_case' warning on per-function basis
This change removes global override of `non_shake_case' warning and replaces it
with per-function overrrides. This way, compiler will complain about style
guide violation in new code.

It should be noted, that `rustc' is not smart enough to emit warning when
override is no longer needed, it must be checked manually.
2019-07-28 22:44:21 +02:00
Dmitry Bogatov
39cc93240f Fix 'non_upper_case_globals' compiler warnings 2019-07-28 22:44:21 +02:00
Alexander Krotov
6c818c6123 Make dc_tools::dc_exactly_one_bit_set safe and return bool 2019-07-28 22:40:30 +02:00
Alexander Krotov
3eaab07b0b top_evil_rs.py: remove comments before counting
For example, constants.rs is completely safe now, but has "free()" in the comments.
2019-07-28 22:39:39 +02:00
Alexander
0cffbaf1e9 refactor: rename dc_array_t::as_ptr() into dc_array_t::into_raw()
By convention as_* functions do not consume self by taking the reference.
2019-07-28 20:31:57 +02:00
Friedel Ziegelmayer
c34e66adb6 Make dc_create_setup_code() safe (#239)
Make dc_create_setup_code() safe
2019-07-28 19:50:25 +02:00
Alexander
0132106c7e fix(imex): dc_decrypt_setup_file: extend lifetime of CString
payload_c binding makes sure the memory is not freed until strdup exits.

See as_ptr documentation:
https://doc.rust-lang.org/std/ffi/struct.CString.html#method.as_ptr
2019-07-28 19:47:52 +02:00
Alexander
5a8b4748b8 fix(msg): correct return value for dc_msg_is_setupmessage
It is broken since 8a0fc609e6
where 0i32 was replaced with true instead of false.
2019-07-28 18:33:31 +02:00
Alexander Krotov
3033d8100d Test dc_create_setup_code() without to_cstring() 2019-07-28 15:05:28 +03:00
Alexander Krotov
0e6b12d7ae Move to_string out of dc_create_setup_code 2019-07-28 15:05:28 +03:00
Friedel Ziegelmayer
2407604e37 Merge pull request #211 from link2xt/dc_array-cleanup
dc_array_t cleanup
2019-07-28 11:51:08 +02:00
Friedel Ziegelmayer
b23ca26908 refactor(chatlist): rustify 2019-07-28 11:32:48 +02:00
Alexander Krotov
21d94b1d09 Remove misplaced comment 2019-07-27 19:28:39 +03:00
Alexander Krotov
ae1cbc9596 dc_array: store locations as Vec<dc_location> 2019-07-27 19:28:39 +03:00
Alexander Krotov
86d047f618 dc_chat: use get_id instead of accessing array fields directly 2019-07-27 19:28:39 +03:00
Alexander Krotov
1cd7cb541c Rewrite most location array member accessors 2019-07-27 19:28:39 +03:00
Alexander Krotov
f27dda86ff Move dc_array_search_id into dc_array_t implementation 2019-07-27 19:28:39 +03:00
Alexander Krotov
51319f89e8 Create dc_array_t in dc_get_locations without unsafe 2019-07-27 19:28:39 +03:00
Alexander Krotov
8b4acbb63a Move dc_array_get_ptr inside dc_array_t implementation 2019-07-27 19:28:39 +03:00
Alexander Krotov
928361429e Move dc_array_get_{uint,id} inside dc_array_t implementation 2019-07-27 19:28:39 +03:00
Alexander Krotov
c17632188a Avoid using return in dc_array_get_cnt implementation 2019-07-27 19:28:39 +03:00
Alexander Krotov
ea3c89e913 Move dc_array_unref logic inside dc_array_t implementation
This will allow to make dc_array_t members private in the future.
2019-07-27 19:28:39 +03:00
Alexander Krotov
ea84edf13a Implement dc_array_t::len() 2019-07-27 19:28:39 +03:00
Alexander Krotov
c335348f20 Implement dc_array_t::is_empty() 2019-07-27 19:28:39 +03:00
Alexander Krotov
1e91f6a204 Merge dc_array_free_ptr into dc_array_unref 2019-07-27 19:28:39 +03:00
Alexander Krotov
dfd43cbb97 Rename dc_array_new_typed into dc_array_new_locations
dc_array_new_typed is only used internally, so we can change its API.
2019-07-27 19:28:39 +03:00
Alexander Krotov
c7a6b3caae Remove unnecessary check in dc_array_new_typed
Allocating Vec with 0 capacity is correct.
2019-07-27 19:28:39 +03:00
Alexander Krotov
f3eea41914 Reimplement dc_array_new without dc_array_new_typed 2019-07-27 19:28:39 +03:00
Alexander Krotov
8d43ad4809 Construct dc_array_t id arrays using safe methods 2019-07-27 19:28:39 +03:00
Alexander Krotov
1f63753a8b Simplify dc_array_search_id 2019-07-27 19:28:39 +03:00
Alexander Krotov
e796a4c438 Move dc_array_add_{uint,id} implementations into dc_array_t 2019-07-27 19:28:39 +03:00
Alexander Krotov
85dfd65e48 Simplify dc_array_get_string 2019-07-27 19:28:39 +03:00
Alexander Krotov
a323fe68a6 Simplify dc_array_duplicate 2019-07-27 19:28:39 +03:00
Alexander Krotov
05aca2c529 Make dc_array_new and dc_array_new_typed safe
Just like Box::into_raw, these functions are safe,
even though the caller is responsible for the allocated structure.
2019-07-27 19:16:42 +03:00
Alexander Krotov
1dfad65afd dc_array.rs: remove magic field
It was always set to DC_ARRAY_MAGIC, except immediately before freeing the memory.
2019-07-27 19:16:42 +03:00
Alexander Krotov
e15e3a1e84 Use Vec to store dc_array_t data 2019-07-27 18:25:24 +03:00
Alexander Krotov
252697b174 Implement dc_array_t::new() and use Box to allocate dc_array_t 2019-07-27 18:25:24 +03:00
Alexander Krotov
7764ab3ff3 Replace C loop with Rust loop in dc_array_search_id 2019-07-27 18:25:24 +03:00
Alexander Krotov
7585dc49e3 Replace C loop with Rust loop in dc_array_free_ptr 2019-07-27 18:25:24 +03:00
Alexander Krotov
f0ae5fcd7c Add DC_ARRAY_LOCATIONS constant 2019-07-27 18:25:24 +03:00
Alexander Krotov
7cba2b3f66 Remove unused dc_array_sort_strings 2019-07-27 18:25:23 +03:00
Alexander Krotov
a0594338b2 Remove repr(C) from dc_array_t
All members of dc_array_t structure are private, C code does not need to interact with them.
2019-07-27 17:14:09 +03:00
Floris Bruynooghe
4902310138 Make stock strings rusty
This converts the stock strings API to be more safe-rust style.  The
API is kept roughly the same for now but moved to methods on the
context.
2019-07-27 12:37:22 +02:00
Friedel Ziegelmayer
44b8629811 Merge pull request #232 from deltachat/fix_markseen
(jikstra, hpk) fix markseen logic to work like C
2019-07-27 12:31:59 +02:00
Friedel Ziegelmayer
30668aaecf Merge pull request #237 from KAction/repl__do_not_treat_empty_string_as_command_
repl: do not treat empty string as command
2019-07-27 12:31:30 +02:00
Friedel Ziegelmayer
6dfc019163 Merge pull request #236 from KAction/Make_repl_program_more_robust_
Make repl program more robust
2019-07-27 12:31:02 +02:00
Friedel Ziegelmayer
b584b7eb58 Merge pull request #234 from KAction/send-garbage
Send garbage
2019-07-27 12:30:35 +02:00
Dmitry Bogatov
36b5f4da53 Check if input to dc_send_text_msg is valid utf8
With this change, passing invalid utf8 string to `dc_send_text_msg' does not
crash application, it prints warning and returns error code.

It should be admitted that this fix is sub-optimal: if input C string is valid
utf8 (which is likely), result of successful conversion to `&str' is discarded
in `dc_send_text_msg', and the same input C string is converted again with
`as_str' in `prepare_msg_raw'.

It is not clear how to fix it in non-disruptive way, since input C string is
passed down to call stack as part of `dc_msg_t' struct, which is part of C ABI.
2019-07-27 09:04:20 +00:00
Dmitry Bogatov
d1968d8ccb New repl command: send-garbage
This new command attempts to send malformed utf8 string to current chat with
dc_send_text_msg() function. Currently, instead of error code it causes
application crash with following backtrace:

thread 'main' panicked at 'Non utf8 string: '[255]' (Utf8Error { valid_up_to: 0, error_len: Some(1) })', src/dc_t
ools.rs:1571:9
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.29/src/backtrace/libunwind.rs:88
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.29/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:47
   3: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:36
   4: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:200
   5: std::panicking::default_hook
             at src/libstd/panicking.rs:214
   6: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:477
   7: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:384
   8: std::panicking::begin_panic_fmt
             at src/libstd/panicking.rs:339
   9: deltachat::dc_tools::as_str::{{closure}}
             at src/dc_tools.rs:1571
  10: core::result::Result<T,E>::unwrap_or_else
             at /rustc/0b680cfce544ff9a59d720020e397c4bf3346650/src/libcore/result.rs:818
  11: deltachat::dc_tools::as_str
             at src/dc_tools.rs:1570
  12: deltachat::dc_chat::prepare_msg_raw
             at src/dc_chat.rs:726
  13: deltachat::dc_chat::prepare_msg_common
             at src/dc_chat.rs:461
  14: deltachat::dc_chat::dc_send_msg
             at src/dc_chat.rs:905
  15: deltachat::dc_chat::dc_send_text_msg
             at src/dc_chat.rs:981
  16: repl::cmdline::dc_cmdline
             at examples/repl/cmdline.rs:955
  17: repl::handle_cmd
             at examples/repl/main.rs:566
  18: repl::main_0
             at examples/repl/main.rs:445
  19: repl::main
             at examples/repl/main.rs:578
  20: std::rt::lang_start::{{closure}}
             at /rustc/0b680cfce544ff9a59d720020e397c4bf3346650/src/libstd/rt.rs:64
  21: std::rt::lang_start_internal::{{closure}}
             at src/libstd/rt.rs:49
  22: std::panicking::try::do_call
             at src/libstd/panicking.rs:296
  23: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:82
  24: std::panicking::try
             at src/libstd/panicking.rs:275
  25: std::panic::catch_unwind
             at src/libstd/panic.rs:394
  26: std::rt::lang_start_internal
             at src/libstd/rt.rs:48
  27: std::rt::lang_start
             at /rustc/0b680cfce544ff9a59d720020e397c4bf3346650/src/libstd/rt.rs:64
  28: main
  29: __libc_start_main
  30: _start
2019-07-27 08:16:18 +00:00
Dmitry Bogatov
a9c714748d Make repl program more robust
This change replaces calls to unwrap() function with "?" operator, propagating
error up the call stack. This way "chat foo" (for example) command results in

      Error: invalid digit found in string

message instead of crashing whole repl.
2019-07-27 07:43:45 +00:00
Dmitry Bogatov
7a07629d68 repl: do not treat empty string as command
Previously, just pressing "<Enter>" at prompt resulted in following error
message:

	Error: Unknown command: "" type ? for help.

This message is harmless, but useless.  Also, such behaviour is different from
many (if not all) interactive environments: python, bash, dash, ...

With this change empty input string is silently ignored.
2019-07-27 07:43:44 +00:00
holger krekel
67d9515033 (jikstra, hpk) fix markseen logic to work like C: ignore if we get no rows for a message (eg desktop calls itwith msg_id=9 which is a special id -- and C just ignored it) 2019-07-26 09:02:11 +02:00
holger krekel
f63e79cd6d remove MessageType, remove account.get_blob_dir which is duplicate of get_blobdir() 2019-07-26 08:57:31 +02:00
holger krekel
83346722fd - simplify and clarify dc_msg caching for Message object
- merge state class into Message object proper -- one less intermediate object to worry about for callers
2019-07-26 08:57:31 +02:00
holger krekel
9836e73683 - properly support prepare-msg API and implement get_message_info()
- remove usage of "attr.s" across the code
- make msg.set_file() copy a file into blobdir if it isn't already
- regroup tests
- add set_draft/get_draft API
2019-07-26 08:57:31 +02:00
Alexander Krotov
c7ebf6de09 cargo fmt 2019-07-25 23:32:17 +02:00
Alexander Krotov
2f204fd2aa Simplify dc_simplify_t implementation
- Replace dc_simplify_new and dc_simplify_unref with ::new()
- Move dc_simplify_simplify and dc_simplify_simplify_plain_text into impl
2019-07-25 23:32:17 +02:00
holger krekel
63ed5c4009 re-enable devpi uploads 2019-07-25 22:46:27 +02:00
Alexander Krotov
9f75a5049e dc_location: store marker as Option<String> instead of C string 2019-07-25 22:41:33 +02:00
Alexander Krotov
ec6cc5c355 Allocate dc_kml_t in a rusty way 2019-07-25 22:41:33 +02:00
Alexander Krotov
b0ef825e67 Implement dc_location::new() and dc_kml_t::new() 2019-07-25 22:41:33 +02:00
Alexander Krotov
a791f76e4b Rename _dc_location into dc_location 2019-07-25 22:41:33 +02:00
holger krekel
2cf227571a pure cargo fmt 2019-07-25 21:51:39 +02:00
holger krekel
9a9b49f8f0 - remove current_block logic from dc_chat.rs with the "OK_TO_CONTINUE"
pattern -- re-indentation will come after this commit with a pure application of "cargo fmt"
- bring back comment from C code
- make some path helpers return bool
2019-07-25 21:51:39 +02:00
holger krekel
9d87f2f10b carefully replace msg state and type numbers with DC_MSG_* and DC_STATE_* constants and also declare them as i32 to avoid tons of casts 2019-07-25 09:45:04 +02:00
holger krekel
5de7c35622 disable upload to devpi as it's currently down and unncessarily braks PR testing 2019-07-24 14:10:29 +02:00
Dmitry Bogatov
004cdf6491 Improve punctuation of message, printed by "repl" program
This patch changes output of following command (from README.md)

	$ cargo run --example repl -- /tmp/main.db

from

	First time init: creating tables in ""/tmp/main.db""
	[...]
	Opened ""/tmp/main.db"".

to

	First time init: creating tables in "/tmp/main.db"
	[...]
	Opened "/tmp/main.db".

Note lack of double quotation mark, which was confusing and could have been
interpreted as part of file name.
2019-07-24 11:36:51 +02:00
Dmitry Bogatov
72ad8b5199 Improve error handling in dc_send_text_msg()
Previously, dc_send_text_msg() silently returned 0 in case of incorrect
input. This way "send" command in repl reported "Sending failed" without
any clue what exactly went wrong.
2019-07-24 09:52:17 +02:00
Alexander Krotov
cb75ac3842 Remove dc_location_t
dc_location_t is an incomplete copy of _dc_location

The difference is that it lacks `int independent` field.
As a result, calloc did not allocate memory for this field.
deltachat-core (C version) has only one dc_location_t, that includes the last field.
2019-07-24 09:37:04 +02:00
Alexander Krotov
a5553f98af dc_location.rs: rewrite is_marker in safe Rust 2019-07-24 09:16:44 +02:00
Alexander Krotov
648d3d78aa Remove dc_arr_to_string function that was used only once 2019-07-23 10:09:52 +02:00
holger krekel
afcf48f833 add test, fix and high level python api for dc_delete_contact
the rust-logic was inverted -- you can not delete a contact that still has messages with it.
2019-07-23 09:37:21 +02:00
69 changed files with 7290 additions and 7324 deletions

View File

@@ -169,7 +169,7 @@ workflows:
- build_test_docs_wheel
- upload_docs_wheels:
requires:
- build_test_docs_wheel
- build_test_docs_wheel
- cargo_fetch
- rustfmt:
requires:

343
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,7 @@ imap = "1.0.1"
mmime = "0.1.0"
base64 = "0.10"
charset = "0.1"
percent-encoding = "1.0"
percent-encoding = "2.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = "0.4.6"
@@ -35,14 +35,16 @@ failure_derive = "0.1.5"
rustyline = "4.1.0"
lazy_static = "1.3.0"
regex = "1.1.6"
rusqlite = { version = "0.19", features = ["bundled"] }
rusqlite = { version = "0.20", features = ["bundled"] }
addr = "0.2.0"
r2d2_sqlite = "0.11.0"
r2d2_sqlite = "0.12.0"
r2d2 = "0.8.5"
strum = "0.15.0"
strum_macros = "0.15.0"
thread-local-object = "0.1.0"
backtrace = "0.3.33"
byteorder = "1.3.1"
itertools = "0.8.0"
[dev-dependencies]
tempfile = "3.0"
@@ -54,10 +56,6 @@ members = [
"deltachat-ffi"
]
[patch.crates-io]
rusqlite = { git = "http://github.com/dignifiedquire/rusqlite", branch = "fix/text", features = ["bundled"] }
[[example]]
name = "simple"
path = "examples/simple.rs"

View File

@@ -63,6 +63,11 @@ Single#10: yourfriends@email.org [yourfriends@email.org]
Message sent.
```
If `yourfriend@email.org` uses DeltaChat, but does not receive message just
sent, it is advisable to check `Spam` folder. It is known that at least
`gmx.com` treat such test messages as spam, unless told otherwise with web
interface.
List messages when inside a chat:
```

View File

@@ -4,6 +4,7 @@ set -ex
export RUST_TEST_THREADS=1
export RUST_BACKTRACE=1
export RUSTFLAGS='--deny warnings'
export OPT="--target=$TARGET"
export OPT_RELEASE="--release ${OPT}"
export OPT_FFI_RELEASE="--manifest-path=deltachat-ffi/Cargo.toml --release"

View File

@@ -18,6 +18,7 @@ crate-type = ["cdylib", "staticlib"]
deltachat = { path = "../", default-features = false }
libc = "0.2"
human-panic = "1.0.1"
num-traits = "0.2.6"
[features]
default = ["vendored", "nightly", "ringbuf"]

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,14 @@
use std::ffi::CString;
use std::str::FromStr;
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_chatlist::*;
use deltachat::dc_configure::*;
use deltachat::dc_contact::*;
use deltachat::dc_imex::*;
use deltachat::dc_job::*;
use deltachat::dc_location::*;
@@ -146,7 +147,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
} else {
current_block = 7149356873433890176;
}
real_spec = to_cstring(rs.unwrap_or_default());
real_spec = rs.unwrap_or_default().strdup();
}
match current_block {
8522321847195001863 => {}
@@ -183,11 +184,10 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
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 = to_cstring(path_plus_name);
if 0 != dc_poke_eml_file(context, path_plus_name_c) {
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
}
free(path_plus_name_c as *mut _);
}
}
current_block = 1622411330066726685;
@@ -218,18 +218,19 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
}
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t) {
let contact: *mut dc_contact_t = dc_get_contact(context, dc_msg_get_from_id(msg));
let contact_name: *mut libc::c_char = dc_contact_get_name(contact);
let contact_id: libc::c_int = dc_contact_get_id(contact) as libc::c_int;
let contact = Contact::get_by_id(context, dc_msg_get_from_id(msg)).expect("invalid contact");
let contact_name = contact.get_name();
let contact_id = contact.get_id();
let statestr = match dc_msg_get_state(msg) {
20 => " o",
26 => "",
28 => " √√",
24 => " !!",
DC_STATE_OUT_PENDING => " o",
DC_STATE_OUT_DELIVERED => "",
DC_STATE_OUT_MDN_RCVD => " √√",
DC_STATE_OUT_FAILED => " !!",
_ => "",
};
let temp2: *mut libc::c_char = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
let msgtext: *mut libc::c_char = dc_msg_get_text(msg);
let temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
let msgtext = dc_msg_get_text(msg);
info!(
context,
0,
@@ -242,19 +243,15 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
""
},
if dc_msg_has_location(msg) { "📍" } else { "" },
as_str(contact_name),
&contact_name,
contact_id,
as_str(msgtext),
if 0 != dc_msg_is_starred(msg) {
""
} else {
""
},
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) == 16 {
} else if dc_msg_get_state(msg) == DC_STATE_IN_SEEN {
"[SEEN]"
} else if dc_msg_get_state(msg) == 13 {
} else if dc_msg_get_state(msg) == DC_STATE_IN_NOTICED {
"[NOTICED]"
} else {
"[FRESH]"
@@ -265,12 +262,9 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
""
},
statestr,
as_str(temp2),
&temp2,
);
free(msgtext as *mut libc::c_void);
free(temp2 as *mut libc::c_void);
free(contact_name as *mut libc::c_void);
dc_contact_unref(contact);
}
unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
@@ -308,7 +302,6 @@ unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
}
unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) {
let mut contact: *mut dc_contact_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);
}
@@ -317,13 +310,12 @@ unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) {
let contact_id = dc_array_get_id(contacts, i as size_t);
let line;
let mut line2 = "".to_string();
contact = dc_get_contact(context, contact_id);
if !contact.is_null() {
let name: *mut libc::c_char = dc_contact_get_name(contact);
let addr: *mut libc::c_char = dc_contact_get_addr(contact);
let verified_state: libc::c_int = dc_contact_is_verified(contact);
let verified_str = if 0 != verified_state {
if verified_state == 2 {
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
let name = contact.get_name();
let addr = contact.get_addr();
let verified_state = contact.is_verified();
let verified_str = if VerifiedStatus::Unverified != verified_state {
if verified_state == VerifiedStatus::BidirectVerified {
" √√"
} else {
""
@@ -333,28 +325,26 @@ unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) {
};
line = format!(
"{}{} <{}>",
if !name.is_null() && 0 != *name.offset(0isize) as libc::c_int {
as_str(name)
if !name.is_empty() {
&name
} else {
"<name unset>"
},
verified_str,
if !addr.is_null() && 0 != *addr.offset(0isize) as libc::c_int {
as_str(addr)
if !addr.is_empty() {
&addr
} else {
"addr unset"
}
);
let peerstate = Peerstate::from_addr(context, &context.sql, as_str(addr));
let peerstate = Peerstate::from_addr(context, &context.sql, &addr);
if peerstate.is_some() && contact_id != 1 as libc::c_uint {
line2 = format!(
", prefer-encrypt={}",
peerstate.as_ref().unwrap().prefer_encrypt
);
}
dc_contact_unref(contact);
free(name as *mut libc::c_void);
free(addr as *mut libc::c_void);
info!(context, 0, "Contact#{}: {}{}", contact_id, line, line2);
}
}
@@ -390,13 +380,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let arg1_c = if arg1.is_empty() {
std::ptr::null()
} else {
to_cstring(arg1) as *const _
arg1.strdup() as *const _
};
let arg2 = args.next().unwrap_or_default();
let arg2_c = if arg2.is_empty() {
std::ptr::null()
} else {
to_cstring(arg2) as *const _
arg2.strdup() as *const _
};
match arg0 {
@@ -450,6 +440,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
dellocations\n\
getlocations [<contact-id>]\n\
send <text>\n\
send-garbage\n\
sendimage <file> [<text>]\n\
sendfile <file>\n\
draft [<text>]\n\
@@ -501,10 +492,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"open" => {
ensure!(!arg1.is_empty(), "Argument <file> missing");
dc_close(context);
ensure!(
0 != dc_open(context, arg1_c, 0 as *const libc::c_char),
"Open failed"
);
ensure!(dc_open(context, arg1, None), "Open failed");
}
"close" => {
dc_close(context);
@@ -523,7 +511,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"get-setupcodebegin" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let msg_id: u32 = arg1.parse().unwrap();
let msg_id: u32 = arg1.parse()?;
let msg: *mut dc_msg_t = dc_get_msg(context, msg_id);
if dc_msg_is_setupmessage(msg) {
let setupcodebegin = dc_msg_get_setupcodebegin(msg);
@@ -543,7 +531,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
!arg1.is_empty() && !arg2.is_empty(),
"Arguments <msg-id> <setup-code> expected"
);
if 0 == dc_continue_key_transfer(context, arg1.parse().unwrap(), arg2_c) {
if 0 == dc_continue_key_transfer(context, arg1.parse()?, arg2_c) {
bail!("Continue key transfer failed");
}
}
@@ -567,39 +555,26 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
dc_imex(context, 2, context.get_blobdir(), 0 as *const libc::c_char);
}
"export-setup" => {
let setup_code: *mut libc::c_char = dc_create_setup_code(context);
let setup_code = dc_create_setup_code(context);
let file_name: *mut libc::c_char = dc_mprintf(
b"%s/autocrypt-setup-message.html\x00" as *const u8 as *const libc::c_char,
context.get_blobdir(),
);
let file_content: *mut libc::c_char;
file_content = dc_render_setup_file(context, setup_code);
if !file_content.is_null()
&& 0 != dc_write_file(
context,
file_name,
file_content as *const libc::c_void,
strlen(file_content),
)
{
println!(
"Setup message written to: {}\nSetup code: {}",
as_str(file_name),
as_str(setup_code),
)
} else {
bail!("");
}
free(file_content as *mut libc::c_void);
let file_content = dc_render_setup_file(context, &setup_code)?;
std::fs::write(as_str(file_name), file_content)?;
println!(
"Setup message written to: {}\nSetup code: {}",
as_str(file_name),
&setup_code,
);
free(file_name as *mut libc::c_void);
free(setup_code as *mut libc::c_void);
}
"poke" => {
ensure!(0 != poke_spec(context, arg1_c), "Poke failed");
}
"reset" => {
ensure!(!arg1.is_empty(), "Argument <bits> missing: 1=jobs, 2=peerstates, 4=private keys, 8=rest but server config");
let bits: i32 = arg1.parse().unwrap();
let bits: i32 = arg1.parse()?;
ensure!(bits < 16, "<bits> must be lower than 16.");
ensure!(0 != dc_reset_tables(context, bits), "Reset failed");
}
@@ -629,11 +604,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"listchats" | "listarchived" | "chats" => {
let listflags = if arg0 == "listarchived" { 0x01 } else { 0 };
let chatlist = dc_get_chatlist(context, listflags, arg1_c, 0 as uint32_t);
ensure!(!chatlist.is_null(), "Failed to retrieve chatlist");
let chatlist = Chatlist::try_load(context, listflags, Some(arg1), None)?;
let mut i: libc::c_int;
let cnt = dc_chatlist_get_cnt(chatlist) as libc::c_int;
let mut i: usize;
let cnt = chatlist.len();
if cnt > 0 {
info!(
context, 0,
@@ -642,8 +616,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
i = cnt - 1;
while i >= 0 {
let chat = dc_get_chat(context, dc_chatlist_get_chat_id(chatlist, i as size_t));
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);
info!(
@@ -658,7 +632,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
);
free(temp_subtitle as *mut libc::c_void);
free(temp_name as *mut libc::c_void);
let lot = dc_chatlist_get_summary(chatlist, i as size_t, chat);
let lot = chatlist.get_summary(i, chat);
let statestr = if 0 != dc_chat_get_archived(chat) {
" [Archived]"
} else {
@@ -681,7 +655,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
if !text1.is_null() { ": " } else { "" },
to_string(text2),
statestr,
as_str(timestr),
&timestr,
if 0 != dc_chat_is_sending_locations(chat) {
"📍"
} else {
@@ -690,7 +664,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
);
free(text1 as *mut libc::c_void);
free(text2 as *mut libc::c_void);
free(timestr as *mut libc::c_void);
dc_lot_unref(lot);
dc_chat_unref(chat);
info!(
@@ -705,7 +678,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
info!(context, 0, "Location streaming enabled.");
}
println!("{} chats", cnt);
dc_chatlist_unref(chatlist);
}
"chat" => {
if sel_chat.is_null() && arg1.is_empty() {
@@ -715,7 +687,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
dc_chat_unref(sel_chat);
}
if !arg1.is_empty() {
let chat_id = arg1.parse().unwrap();
let chat_id = arg1.parse()?;
println!("Selecting chat #{}", chat_id);
sel_chat = dc_get_chat(context, chat_id);
*context.cmdline_sel_chat_id.write().unwrap() = chat_id;
@@ -759,7 +731,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"createchat" => {
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id: libc::c_int = arg1.parse().unwrap();
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 {
@@ -770,7 +742,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"createchatbymsg" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing");
let msg_id_0: libc::c_int = arg1.parse().unwrap();
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 {
@@ -807,7 +779,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!sel_chat.is_null(), "No chat selected");
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id_0: libc::c_int = arg1.parse().unwrap();
let contact_id_0: libc::c_int = arg1.parse()?;
if 0 != dc_add_contact_to_chat(
context,
dc_chat_get_id(sel_chat),
@@ -821,7 +793,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"removemember" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id_1: libc::c_int = arg1.parse().unwrap();
let contact_id_1: libc::c_int = arg1.parse()?;
if 0 != dc_remove_contact_from_chat(
context,
dc_chat_get_id(sel_chat),
@@ -886,7 +858,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
0,
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}",
dc_array_get_id(loc, j as size_t),
as_str(timestr_0),
&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),
@@ -899,7 +871,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"-"
},
);
free(timestr_0 as *mut libc::c_void);
free(marker as *mut libc::c_void);
j += 1
}
@@ -912,7 +883,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "No timeout given.");
let seconds = arg1.parse().unwrap();
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);
}
@@ -921,8 +892,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
!arg1.is_empty() && !arg2.is_empty(),
"Latitude or longitude not given."
);
let latitude = arg1.parse().unwrap();
let longitude = arg2.parse().unwrap();
let latitude = arg1.parse()?;
let longitude = arg2.parse()?;
let continue_streaming = dc_set_location(context, latitude, longitude, 0.);
if 0 != continue_streaming {
@@ -938,23 +909,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "No message text given.");
let msg = to_cstring(format!("{} {}", arg1, arg2));
let msg = format!("{} {}", arg1, arg2);
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg) {
println!("Message sent.");
free(msg as *mut _);
} else {
free(msg as *mut _);
bail!("Sending failed.");
}
}
"sendempty" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
if 0 != dc_send_text_msg(
context,
dc_chat_get_id(sel_chat),
b"\x00" as *const u8 as *const libc::c_char,
) {
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), "".into()) {
println!("Message sent.");
} else {
bail!("Sending failed.");
@@ -964,7 +929,14 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given.");
let msg_0 = dc_msg_new(context, if arg0 == "sendimage" { 20 } else { 60 });
let msg_0 = dc_msg_new(
context,
if arg0 == "sendimage" {
Viewtype::Image
} else {
Viewtype::File
},
);
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);
@@ -991,7 +963,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!sel_chat.is_null(), "No chat selected.");
if !arg1.is_empty() {
let draft_0 = dc_msg_new(context, 10);
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);
dc_msg_unref(draft_0);
@@ -1004,7 +976,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"listmedia" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
let images = dc_get_chat_media(context, dc_chat_get_id(sel_chat), 20, 21, 50);
let images = dc_get_chat_media(
context,
dc_chat_get_id(sel_chat),
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 {
@@ -1020,17 +998,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"archive" | "unarchive" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
let chat_id = arg1.parse().unwrap();
let chat_id = arg1.parse()?;
dc_archive_chat(context, chat_id, if arg0 == "archive" { 1 } else { 0 });
}
"delchat" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
let chat_id = arg1.parse().unwrap();
let chat_id = arg1.parse()?;
dc_delete_chat(context, chat_id);
}
"msginfo" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let id = arg1.parse().unwrap();
let id = arg1.parse()?;
let res = dc_get_msg_info(context, id);
println!("{}", as_str(res));
}
@@ -1049,20 +1027,20 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
);
let mut msg_ids = [0; 1];
let chat_id = arg2.parse().unwrap();
msg_ids[0] = arg1.parse().unwrap();
let chat_id = arg2.parse()?;
msg_ids[0] = arg1.parse()?;
dc_forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
}
"markseen" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut msg_ids = [0; 1];
msg_ids[0] = arg1.parse().unwrap();
msg_ids[0] = arg1.parse()?;
dc_markseen_msgs(context, msg_ids.as_mut_ptr(), 1);
}
"star" | "unstar" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut msg_ids = [0; 1];
msg_ids[0] = arg1.parse().unwrap();
msg_ids[0] = arg1.parse()?;
dc_star_msgs(
context,
msg_ids.as_mut_ptr(),
@@ -1073,19 +1051,19 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"delmsg" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut ids = [0; 1];
ids[0] = arg1.parse().unwrap();
ids[0] = arg1.parse()?;
dc_delete_msgs(context, ids.as_mut_ptr(), 1);
}
"listcontacts" | "contacts" | "listverified" => {
let contacts = dc_get_contacts(
let contacts = Contact::get_all(
context,
if arg0 == "listverified" {
0x1 | 0x2
} else {
0x2
},
arg1_c,
);
Some(arg1),
)?;
if !contacts.is_null() {
log_contactlist(context, contacts);
println!("{} contacts.", dc_array_get_cnt(contacts) as libc::c_int,);
@@ -1098,36 +1076,25 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!arg1.is_empty(), "Arguments [<name>] <addr> expected.");
if !arg2.is_empty() {
let book = dc_mprintf(
b"%s\n%s\x00" as *const u8 as *const libc::c_char,
arg1_c,
arg2_c,
);
dc_add_address_book(context, book);
free(book as *mut libc::c_void);
let book = format!("{}\n{}", arg1, arg2);
Contact::add_address_book(context, book)?;
} else {
if 0 == dc_create_contact(context, 0 as *const libc::c_char, arg1_c) {
bail!("Failed to create contact");
}
Contact::create(context, "", arg1)?;
}
}
"contactinfo" => {
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id = arg1.parse().unwrap();
let contact = dc_get_contact(context, contact_id);
let name_n_addr = dc_contact_get_name_n_addr(contact);
let contact_id = arg1.parse()?;
let contact = Contact::get_by_id(context, contact_id)?;
let name_n_addr = contact.get_name_n_addr();
let mut res = format!("Contact info for: {}:\n\n", as_str(name_n_addr),);
free(name_n_addr as *mut libc::c_void);
dc_contact_unref(contact);
let mut res = format!("Contact info for: {}:\n\n", name_n_addr);
let encrinfo = dc_get_contact_encrinfo(context, contact_id);
res += as_str(encrinfo);
free(encrinfo as *mut libc::c_void);
res += &Contact::get_encrinfo(context, contact_id);
let chatlist = dc_get_chatlist(context, 0, 0 as *const libc::c_char, contact_id);
let chatlist_cnt = dc_chatlist_get_cnt(chatlist) as libc::c_int;
let chatlist = Chatlist::try_load(context, 0, None, Some(contact_id))?;
let chatlist_cnt = chatlist.len();
if chatlist_cnt > 0 {
res += &format!(
"\n\n{} chats shared with Contact#{}: ",
@@ -1137,19 +1104,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
if 0 != i {
res += ", ";
}
let chat = dc_get_chat(context, dc_chatlist_get_chat_id(chatlist, i as size_t));
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);
}
}
dc_chatlist_unref(chatlist);
println!("{}", res);
}
"delcontact" => {
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
if !dc_delete_contact(context, arg1.parse().unwrap()) {
bail!("Failed to delete contact");
}
Contact::delete(context, arg1.parse()?)?;
}
"checkqr" => {
ensure!(!arg1.is_empty(), "Argument <qr-content> missing.");
@@ -1165,7 +1130,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"event" => {
ensure!(!arg1.is_empty(), "Argument <id> missing.");
let event = Event::from_u32(arg1.parse().unwrap()).unwrap();
let event = arg1.parse()?;
let event = Event::from_u32(event).ok_or(format_err!("Event::from_u32({})", event))?;
let r = context.call_cb(event, 0 as uintptr_t, 0 as uintptr_t);
println!(
"Sending event {:?}({}), received value {}.",
@@ -1192,6 +1158,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
bail!("Command failed.");
}
}
"" => (),
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
}

View File

@@ -3,6 +3,7 @@
//!
//! 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;
@@ -391,18 +392,13 @@ fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
let mut context = dc_context_new(
Some(receive_event),
0 as *mut libc::c_void,
b"CLI\x00" as *const u8 as *const libc::c_char,
Some("CLI".into()),
);
unsafe { dc_cmdline_skip_auth() };
if args.len() == 2 {
if 0 == unsafe {
let a = to_cstring(&args[1]);
let res = dc_open(&mut context, a, 0 as *const _);
free(a as *mut _);
res
} {
if unsafe { !dc_open(&mut context, &args[1], None) } {
println!("Error: Cannot open {}.", args[0],);
}
} else if args.len() != 1 {
@@ -484,7 +480,7 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
let arg1_c = if arg1.is_empty() {
std::ptr::null()
} else {
to_cstring(arg1)
arg1.strdup()
};
match arg0 {

View File

@@ -1,17 +1,17 @@
extern crate deltachat;
use std::ffi::{CStr, CString};
use std::ffi::CStr;
use std::sync::{Arc, RwLock};
use std::{thread, time};
use tempfile::tempdir;
use deltachat::chatlist::*;
use deltachat::config;
use deltachat::constants::Event;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::dc_chat::*;
use deltachat::dc_chatlist::*;
use deltachat::dc_configure::*;
use deltachat::dc_contact::*;
use deltachat::dc_job::{
dc_perform_imap_fetch, dc_perform_imap_idle, dc_perform_imap_jobs, dc_perform_smtp_idle,
dc_perform_smtp_jobs,
@@ -41,7 +41,7 @@ extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> us
fn main() {
unsafe {
let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let running = Arc::new(RwLock::new(true));
let info = dc_get_info(&ctx);
let info_s = CStr::from_ptr(info);
@@ -76,11 +76,11 @@ fn main() {
});
let dir = tempdir().unwrap();
let dbfile = CString::new(dir.path().join("db.sqlite").to_str().unwrap()).unwrap();
let dbfile = dir.path().join("db.sqlite");
println!("opening database {:?}", dbfile);
assert_eq!(dc_open(&ctx, dbfile.as_ptr(), std::ptr::null()), 1);
assert!(dc_open(&ctx, dbfile.to_str().unwrap(), None));
println!("configuring");
let args = std::env::args().collect::<Vec<String>>();
@@ -93,18 +93,17 @@ fn main() {
thread::sleep(duration);
let email = CString::new("dignifiedquire@gmail.com").unwrap();
println!("sending a message");
let contact_id = dc_create_contact(&ctx, std::ptr::null(), email.as_ptr());
let contact_id =
Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap();
let chat_id = dc_create_chat_by_contact_id(&ctx, contact_id);
let msg_text = CString::new("Hi, here is my first message!").unwrap();
dc_send_text_msg(&ctx, chat_id, msg_text.as_ptr());
dc_send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into());
println!("fetching chats..");
let chats = dc_get_chatlist(&ctx, 0, std::ptr::null(), 0);
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
for i in 0..dc_chatlist_get_cnt(chats) {
let summary = dc_chatlist_get_summary(chats, 0, std::ptr::null_mut());
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);
@@ -121,7 +120,6 @@ fn main() {
println!("chat: {} - {:?} - {:?}", i, text1_s, text2_s,);
dc_lot_unref(summary);
}
dc_chatlist_unref(chats);
thread::sleep(duration);

View File

@@ -11,8 +11,6 @@ high level API reference
- :class:`deltachat.chatting.Contact`
- :class:`deltachat.chatting.Chat`
- :class:`deltachat.message.Message`
- :class:`deltachat.message.MessageType`
- :class:`deltachat.message.MessageState`
Account
-------

View File

@@ -288,10 +288,6 @@ intersphinx_mapping = {'http://docs.python.org/': None}
autodoc_member_order = "bysource"
# always document __init__ functions
def skip(app, what, name, obj, skip, options):
import attr
if name == "__init__":
if not hasattr(obj.im_class, "__attrs_attrs__"):
return False
return skip
def setup(app):

View File

@@ -17,7 +17,7 @@ def main():
description='Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat',
long_description=long_description,
author='holger krekel, Floris Bruynooghe, Bjoern Petersen and contributors',
install_requires=['cffi>=1.0.0', 'attrs', 'six'],
install_requires=['cffi>=1.0.0', 'six'],
packages=setuptools.find_packages('src'),
package_dir={'': 'src'},
cffi_modules=['src/deltachat/_build.py:ffibuilder'],

View File

@@ -7,9 +7,9 @@ import re
import time
from array import array
try:
from queue import Queue
from queue import Queue, Empty
except ImportError:
from Queue import Queue
from Queue import Queue, Empty
import deltachat
from . import const
@@ -142,15 +142,6 @@ class Account(object):
self.check_is_configured()
return Contact(self._dc_context, const.DC_CONTACT_ID_SELF)
def create_message(self, view_type):
""" create a new non persistent message.
:param view_type: a string specifying "text", "video",
"image", "audio" or "file".
:returns: :class:`deltachat.message.Message` instance.
"""
return Message.new(self._dc_context, view_type)
def create_contact(self, email, name=None):
""" create a (new) Contact. If there already is a Contact
with that e-mail address, it is unblocked and its name is
@@ -166,6 +157,17 @@ class Account(object):
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL
return Contact(self._dc_context, contact_id)
def delete_contact(self, contact):
""" delete a Contact.
:param contact: contact object obtained
:returns: True if deletion succeeded (contact was deleted)
"""
contact_id = contact.id
assert contact._dc_context == self._dc_context
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL
return bool(lib.dc_delete_contact(self._dc_context, contact_id))
def get_contacts(self, query=None, with_self=False, only_verified=False):
""" get a (filtered) list of contacts.
@@ -201,7 +203,7 @@ class Account(object):
assert isinstance(contact, int)
contact_id = contact
chat_id = lib.dc_create_chat_by_contact_id(self._dc_context, contact_id)
return Chat(self._dc_context, chat_id)
return Chat(self, chat_id)
def create_chat_by_message(self, message):
""" create or get an existing chat object for the
@@ -218,7 +220,7 @@ class Account(object):
assert isinstance(message, int)
msg_id = message
chat_id = lib.dc_create_chat_by_msg_id(self._dc_context, msg_id)
return Chat(self._dc_context, chat_id)
return Chat(self, chat_id)
def create_group_chat(self, name, verified=False):
""" create a new group chat object.
@@ -230,7 +232,7 @@ class Account(object):
"""
bytes_name = name.encode("utf8")
chat_id = lib.dc_create_group_chat(self._dc_context, verified, bytes_name)
return Chat(self._dc_context, chat_id)
return Chat(self, chat_id)
def get_chats(self):
""" return list of chats.
@@ -246,15 +248,15 @@ class Account(object):
chatlist = []
for i in range(0, lib.dc_chatlist_get_cnt(dc_chatlist)):
chat_id = lib.dc_chatlist_get_chat_id(dc_chatlist, i)
chatlist.append(Chat(self._dc_context, chat_id))
chatlist.append(Chat(self, chat_id))
return chatlist
def get_deaddrop_chat(self):
return Chat(self._dc_context, const.DC_CHAT_ID_DEADDROP)
return Chat(self, const.DC_CHAT_ID_DEADDROP)
def get_message_by_id(self, msg_id):
""" return Message instance. """
return Message.from_db(self._dc_context, msg_id)
return Message.from_db(self, msg_id)
def mark_seen_messages(self, messages):
""" mark the given set of messages as seen.
@@ -437,6 +439,17 @@ class EventLogger:
raise ValueError("{}({!r},{!r})".format(*ev))
return ev
def ensure_event_not_queued(self, event_name_regex):
__tracebackhide__ = True
rex = re.compile("(?:{}).*".format(event_name_regex))
while 1:
try:
ev = self._event_queue.get(False)
except Empty:
break
else:
assert not rex.match(ev[0]), "event found {}".format(ev)
def get_matching(self, event_name_regex, check_error=True):
self._log("-- waiting for event with regex: {} --".format(event_name_regex))
rex = re.compile("(?:{}).*".format(event_name_regex))

View File

@@ -1,24 +1,30 @@
""" chatting related objects: Contact, Chat, Message. """
import os
import mimetypes
from . import props
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
from .capi import lib, ffi
from . import const
import attr
from attr import validators as v
from .message import Message
@attr.s
class Contact(object):
""" Delta-Chat Contact.
You obtain instances of it through :class:`deltachat.account.Account`.
"""
_dc_context = attr.ib(validator=v.instance_of(ffi.CData))
id = attr.ib(validator=v.instance_of(int))
def __init__(self, dc_context, id):
self._dc_context = dc_context
self.id = id
def __eq__(self, other):
return self._dc_context == other._dc_context and self.id == other.id
def __ne__(self, other):
return not (self == other)
def __repr__(self):
return "<Contact id={} addr={} dc_context={}>".format(self.id, self.addr, self._dc_context)
@property
def _dc_contact(self):
@@ -46,14 +52,26 @@ class Contact(object):
return lib.dc_contact_is_verified(self._dc_contact)
@attr.s
class Chat(object):
""" Chat object which manages members and through which you can send and retrieve messages.
You obtain instances of it through :class:`deltachat.account.Account`.
"""
_dc_context = attr.ib(validator=v.instance_of(ffi.CData))
id = attr.ib(validator=v.instance_of(int))
def __init__(self, account, id):
self.account = account
self._dc_context = account._dc_context
self.id = id
def __eq__(self, other):
return self.id == getattr(other, "id", None) and \
self._dc_context == getattr(other, "_dc_context", None)
def __ne__(self, other):
return not (self == other)
def __repr__(self):
return "<Chat id={} name={} dc_context={}>".format(self.id, self.get_name(), self._dc_context)
@property
def _dc_chat(self):
@@ -126,7 +144,7 @@ class Chat(object):
msg_id = lib.dc_send_text_msg(self._dc_context, self.id, msg)
if msg_id == 0:
raise ValueError("message could not be send, does chat exist?")
return Message.from_db(self._dc_context, msg_id)
return Message.from_db(self.account, msg_id)
def send_file(self, path, mime_type="application/octet-stream"):
""" send a file and return the resulting Message instance.
@@ -136,14 +154,9 @@ class Chat(object):
:raises ValueError: if message can not be send/chat does not exist.
:returns: the resulting :class:`deltachat.message.Message` instance
"""
path = as_dc_charpointer(path)
mtype = as_dc_charpointer(mime_type)
msg = Message.new(self._dc_context, "file")
msg.set_file(path, mtype)
msg_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
if msg_id == 0:
raise ValueError("message could not be send, does chat exist?")
return Message.from_db(self._dc_context, msg_id)
msg = self.prepare_message_file(path=path, mime_type=mime_type)
self.send_prepared(msg)
return msg
def send_image(self, path):
""" send an image message and return the resulting Message instance.
@@ -152,14 +165,25 @@ class Chat(object):
:raises ValueError: if message can not be send/chat does not exist.
:returns: the resulting :class:`deltachat.message.Message` instance
"""
if not os.path.exists(path):
raise ValueError("path does not exist: {!r}".format(path))
msg = Message.new(self._dc_context, "image")
msg.set_file(path)
msg_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
return Message.from_db(self._dc_context, msg_id)
mime_type = mimetypes.guess_type(path)[0]
msg = self.prepare_message_file(path=path, mime_type=mime_type, view_type="image")
self.send_prepared(msg)
return msg
def prepare_file(self, path, mime_type=None, view_type="file"):
def prepare_message(self, msg):
""" create a new prepared message.
:param msg: the message to be prepared.
:returns: :class:`deltachat.message.Message` instance.
"""
msg_id = lib.dc_prepare_msg(self._dc_context, self.id, msg._dc_msg)
if msg_id == 0:
raise ValueError("message could not be prepared")
# invalidate passed in message which is not safe to use anymore
msg._dc_msg = msg.id = None
return Message.from_db(self.account, msg_id)
def prepare_message_file(self, path, mime_type=None, view_type="file"):
""" prepare a message for sending and return the resulting Message instance.
To actually send the message, call :meth:`send_prepared`.
@@ -167,18 +191,13 @@ class Chat(object):
:param path: path to the file.
:param mime_type: the mime-type of this file, defaults to auto-detection.
:param view_type: passed to :meth:`MessageType.new`.
:param view_type: "text", "image", "gif", "audio", "video", "file"
:raises ValueError: if message can not be prepared/chat does not exist.
:returns: the resulting :class:`Message` instance
"""
path = as_dc_charpointer(path)
mtype = as_dc_charpointer(mime_type)
msg = Message.new(self._dc_context, view_type)
msg.set_file(path, mtype)
msg_id = lib.dc_prepare_msg(self._dc_context, self.id, msg._dc_msg)
if msg_id == 0:
raise ValueError("message could not be prepared, does chat exist?")
return Message.from_db(self._dc_context, msg_id)
msg = Message.new_empty(self.account, view_type)
msg.set_file(path, mime_type)
return self.prepare_message(msg)
def send_prepared(self, message):
""" send a previously prepared message.
@@ -186,12 +205,42 @@ class Chat(object):
:param message: a :class:`Message` instance previously returned by
:meth:`prepare_file`.
:raises ValueError: if message can not be sent.
:returns: a :class:`deltachat.message.Message` instance with updated state
:returns: a :class:`deltachat.message.Message` instance as sent out.
"""
msg_id = lib.dc_send_msg(self._dc_context, 0, message._dc_msg)
if msg_id == 0:
assert message.id != 0 and message.is_out_preparing()
# get a fresh copy of dc_msg, the core needs it
msg = Message.from_db(self.account, message.id)
# pass 0 as chat-id because core-docs say it's ok when out-preparing
sent_id = lib.dc_send_msg(self._dc_context, 0, msg._dc_msg)
if sent_id == 0:
raise ValueError("message could not be sent")
return Message.from_db(self._dc_context, msg_id)
assert sent_id == msg.id
# modify message in place to avoid bad state for the caller
msg._dc_msg = Message.from_db(self.account, sent_id)._dc_msg
def set_draft(self, message):
""" set message as draft.
:param message: a :class:`Message` instance
:returns: None
"""
if message is None:
lib.dc_set_draft(self._dc_context, self.id, ffi.NULL)
else:
lib.dc_set_draft(self._dc_context, self.id, message._dc_msg)
def get_draft(self):
""" get draft message for this chat.
:param message: a :class:`Message` instance
:returns: Message object or None (if no draft available)
"""
x = lib.dc_get_draft(self._dc_context, self.id)
if x == ffi.NULL:
return None
dc_msg = ffi.gc(x, lib.dc_msg_unref)
return Message(self.account, dc_msg)
def get_messages(self):
""" return list of messages in this chat.
@@ -202,7 +251,7 @@ class Chat(object):
lib.dc_get_chat_msgs(self._dc_context, self.id, 0, 0),
lib.dc_array_unref
)
return list(iter_array(dc_array, lambda x: Message.from_db(self._dc_context, x)))
return list(iter_array(dc_array, lambda x: Message.from_db(self.account, x)))
def count_fresh_messages(self):
""" return number of fresh messages in this chat.

View File

@@ -1,59 +1,55 @@
""" chatting related objects: Contact, Chat, Message. """
import os
import shutil
from . import props
from .cutil import from_dc_charpointer, as_dc_charpointer
from .capi import lib, ffi
from . import const
from datetime import datetime
import attr
from attr import validators as v
@attr.s
class Message(object):
""" Message object.
You obtain instances of it through :class:`deltachat.account.Account` or
:class:`deltachat.chatting.Chat`.
"""
_dc_context = attr.ib(validator=v.instance_of(ffi.CData))
try:
id = attr.ib(validator=v.instance_of((int, long)))
except NameError: # py35
id = attr.ib(validator=v.instance_of(int))
def __init__(self, account, dc_msg):
self.account = account
self._dc_context = account._dc_context
assert isinstance(self._dc_context, ffi.CData)
assert isinstance(dc_msg, ffi.CData)
assert dc_msg != ffi.NULL
self._dc_msg = dc_msg
self.id = lib.dc_msg_get_id(dc_msg)
assert self.id is not None and self.id >= 0, repr(self.id)
@property
def _dc_msg(self):
if self.id > 0:
return ffi.gc(
lib.dc_get_msg(self._dc_context, self.id),
lib.dc_msg_unref
)
return self._dc_msg_volatile
def __eq__(self, other):
return self.account == other.account and self.id == other.id
def __repr__(self):
return "<Message id={} dc_context={}>".format(self.id, self._dc_context)
@classmethod
def from_db(cls, _dc_context, id):
def from_db(cls, account, id):
assert id > 0
return cls(_dc_context, id)
return cls(account, ffi.gc(
lib.dc_get_msg(account._dc_context, id),
lib.dc_msg_unref
))
@classmethod
def new(cls, dc_context, view_type):
""" create a non-persistent method. """
msg = cls(dc_context, 0)
view_type_code = MessageType.get_typecode(view_type)
msg._dc_msg_volatile = ffi.gc(
lib.dc_msg_new(dc_context, view_type_code),
lib.dc_msg_unref
)
return msg
def new_empty(cls, account, view_type):
""" create a non-persistent message.
def get_state(self):
""" get the message in/out state.
:returns: :class:`deltachat.message.MessageState`
:param: view_type is "text", "audio", "video", "file"
"""
return MessageState(self)
view_type_code = get_viewtype_code_from_name(view_type)
return Message(account, ffi.gc(
lib.dc_msg_new(account._dc_context, view_type_code),
lib.dc_msg_unref
))
@props.with_doc
def text(self):
@@ -62,7 +58,9 @@ class Message(object):
def set_text(self, text):
"""set text of this message. """
return lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
assert self.id > 0, "message not prepared"
assert self.is_out_preparing()
lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
@props.with_doc
def filename(self):
@@ -70,9 +68,23 @@ class Message(object):
return from_dc_charpointer(lib.dc_msg_get_file(self._dc_msg))
def set_file(self, path, mime_type=None):
"""set file for this message. """
mtype = ffi.NULL if mime_type is None else mime_type
assert os.path.exists(path)
"""set file for this message from path and mime_type. """
mtype = ffi.NULL if mime_type is None else as_dc_charpointer(mime_type)
if not os.path.exists(path):
raise ValueError("path does not exist: {!r}".format(path))
blobdir = self.account.get_blobdir()
if not path.startswith(blobdir):
for i in range(50):
ext = "" if i == 0 else "-" + str(i)
dest = os.path.join(blobdir, os.path.basename(path) + ext)
if os.path.exists(dest):
continue
shutil.copyfile(path, dest)
break
else:
raise ValueError("could not create blobdir-path for {}".format(path))
path = dest
assert path.startswith(blobdir), path
lib.dc_msg_set_file(self._dc_msg, as_dc_charpointer(path), mtype)
@props.with_doc
@@ -85,18 +97,17 @@ class Message(object):
"""mime type of the file (if it exists)"""
return from_dc_charpointer(lib.dc_msg_get_filemime(self._dc_msg))
@props.with_doc
def view_type(self):
"""the view type of this message.
:returns: a :class:`deltachat.message.MessageType` instance.
"""
return MessageType(lib.dc_msg_get_viewtype(self._dc_msg))
def is_setup_message(self):
""" return True if this message is a setup message. """
return lib.dc_msg_is_setupmessage(self._dc_msg)
def get_message_info(self):
""" Return informational text for a single message.
The text is multiline and may contain eg. the raw text of the message.
"""
return from_dc_charpointer(lib.dc_get_msg_info(self._dc_context, self.id))
def continue_key_transfer(self, setup_code):
""" extract key and use it as primary key for this account. """
lib.dc_continue_key_transfer(self._dc_context, self.id, as_dc_charpointer(setup_code))
@@ -144,7 +155,7 @@ class Message(object):
"""
from .chatting import Chat
chat_id = lib.dc_msg_get_chat_id(self._dc_msg)
return Chat(self._dc_context, chat_id)
return Chat(self.account, chat_id)
def get_sender_contact(self):
"""return the contact of who wrote the message.
@@ -155,66 +166,20 @@ class Message(object):
contact_id = lib.dc_msg_get_from_id(self._dc_msg)
return Contact(self._dc_context, contact_id)
@attr.s
class MessageType(object):
""" DeltaChat message type, with is_* methods. """
_type = attr.ib(validator=v.instance_of(int))
_mapping = {
const.DC_MSG_TEXT: 'text',
const.DC_MSG_IMAGE: 'image',
const.DC_MSG_GIF: 'gif',
const.DC_MSG_AUDIO: 'audio',
const.DC_MSG_VIDEO: 'video',
const.DC_MSG_FILE: 'file'
}
@classmethod
def get_typecode(cls, view_type):
for code, value in cls._mapping.items():
if value == view_type:
return code
raise ValueError("message typecode not found for {!r}".format(view_type))
@props.with_doc
def name(self):
""" human readable type name. """
return self._mapping.get(self._type, "")
def is_text(self):
""" return True if it's a text message. """
return self._type == const.DC_MSG_TEXT
def is_image(self):
""" return True if it's an image message. """
return self._type == const.DC_MSG_IMAGE
def is_gif(self):
""" return True if it's a gif message. """
return self._type == const.DC_MSG_GIF
def is_audio(self):
""" return True if it's an audio message. """
return self._type == const.DC_MSG_AUDIO
def is_video(self):
""" return True if it's a video message. """
return self._type == const.DC_MSG_VIDEO
def is_file(self):
""" return True if it's a file message. """
return self._type == const.DC_MSG_FILE
@attr.s
class MessageState(object):
""" Current Message In/Out state, updated on each call of is_* methods.
"""
message = attr.ib(validator=v.instance_of(Message))
#
# Message State query methods
#
@property
def _msgstate(self):
return lib.dc_msg_get_state(self.message._dc_msg)
if self.id == 0:
dc_msg = self.message._dc_msg
else:
# load message from db to get a fresh/current state
dc_msg = ffi.gc(
lib.dc_get_msg(self._dc_context, self.id),
lib.dc_msg_unref
)
return lib.dc_msg_get_state(dc_msg)
def is_in_fresh(self):
""" return True if Message is incoming fresh message (un-noticed).
@@ -268,3 +233,56 @@ class MessageState(object):
state, you'll receive the event DC_EVENT_MSG_READ.
"""
return self._msgstate == const.DC_STATE_OUT_MDN_RCVD
#
# Message type query methods
#
@property
def _view_type(self):
assert self.id > 0
return lib.dc_msg_get_viewtype(self._dc_msg)
def is_text(self):
""" return True if it's a text message. """
return self._view_type == const.DC_MSG_TEXT
def is_image(self):
""" return True if it's an image message. """
return self._view_type == const.DC_MSG_IMAGE
def is_gif(self):
""" return True if it's a gif message. """
return self._view_type == const.DC_MSG_GIF
def is_audio(self):
""" return True if it's an audio message. """
return self._view_type == const.DC_MSG_AUDIO
def is_video(self):
""" return True if it's a video message. """
return self._view_type == const.DC_MSG_VIDEO
def is_file(self):
""" return True if it's a file message. """
return self._view_type == const.DC_MSG_FILE
# some code for handling DC_MSG_* view types
_view_type_mapping = {
const.DC_MSG_TEXT: 'text',
const.DC_MSG_IMAGE: 'image',
const.DC_MSG_GIF: 'gif',
const.DC_MSG_AUDIO: 'audio',
const.DC_MSG_VIDEO: 'video',
const.DC_MSG_FILE: 'file'
}
def get_viewtype_code_from_name(view_type_name):
for code, value in _view_type_mapping.items():
if value == view_type_name:
return code
raise ValueError("message typecode not found for {!r}, "
"available {!r}".format(view_type_name, list(_view_type_mapping.values())))

View File

@@ -2,11 +2,12 @@ from __future__ import print_function
import pytest
import os
from deltachat import const, Account
from deltachat.message import Message
from datetime import datetime, timedelta
from conftest import wait_configuration_progress, wait_successful_IMAP_SMTP_connection
class TestOfflineAccount:
class TestOfflineAccountBasic:
def test_wrong_db(self, tmpdir):
p = tmpdir.join("hello.db")
p.write("123")
@@ -57,16 +58,22 @@ class TestOfflineAccount:
with pytest.raises(KeyError):
ac1.get_config("123123")
class TestOfflineContact:
def test_contact_attr(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact(email="some1@hello.com", name="some1")
contact2 = ac1.create_contact(email="some1@hello.com", name="some1")
str(contact1)
repr(contact1)
assert contact1 == contact2
assert contact1.id
assert contact1.addr == "some1@hello.com"
assert contact1.display_name == "some1"
assert not contact1.is_blocked()
assert not contact1.is_verified()
def test_get_contacts(self, acfactory):
def test_get_contacts_and_delete(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact(email="some1@hello.com", name="some1")
contacts = ac1.get_contacts()
@@ -79,26 +86,48 @@ class TestOfflineAccount:
contacts = ac1.get_contacts(with_self=True)
assert len(contacts) == 2
def test_chat(self, acfactory):
assert ac1.delete_contact(contact1)
assert contact1 not in ac1.get_contacts()
def test_get_contacts_and_delete_fails(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact(email="some1@example.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
chat.send_text("one messae")
assert not ac1.delete_contact(contact1)
class TestOfflineChat:
@pytest.fixture
def ac1(self, acfactory):
return acfactory.get_configured_offline_account()
@pytest.fixture
def chat1(self, ac1):
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL, chat.id
return chat
def test_display(self, chat1):
str(chat1)
repr(chat1)
def test_chat_idempotent(self, chat1, ac1):
contact1 = chat1.get_contacts()[0]
chat2 = ac1.create_chat_by_contact(contact1.id)
assert chat2.id == chat.id
assert chat2.get_name() == chat.get_name()
assert chat == chat2
assert not (chat != chat2)
assert chat2.id == chat1.id
assert chat2.get_name() == chat1.get_name()
assert chat1 == chat2
assert not (chat1 != chat2)
for ichat in ac1.get_chats():
if ichat.id == chat.id:
if ichat.id == chat1.id:
break
else:
pytest.fail("could not find chat")
def test_group_chat_creation(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
def test_group_chat_creation(self, ac1):
contact1 = ac1.create_contact("some1@hello.com", name="some1")
contact2 = ac1.create_contact("some2@hello.com", name="some2")
chat = ac1.create_group_chat(name="title1")
@@ -111,64 +140,73 @@ class TestOfflineAccount:
chat.set_name("title2")
assert chat.get_name() == "title2"
def test_delete_and_send_fails(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
chat.delete()
def test_delete_and_send_fails(self, ac1, chat1):
chat1.delete()
ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
with pytest.raises(ValueError):
chat.send_text("msg1")
chat1.send_text("msg1")
def test_create_message(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
message = ac1.create_message("text")
assert message.id == 0
assert message._dc_msg is message._dc_msg
message.set_text("hello")
assert message.text == "hello"
assert message.id == 0
def test_prepare_message_and_send(self, ac1, chat1):
msg = chat1.prepare_message(Message.new_empty(chat1.account, "text"))
msg.set_text("hello world")
assert msg.text == "hello world"
assert msg.id > 0
chat1.send_prepared(msg)
assert "Sent" in msg.get_message_info()
str(msg)
repr(msg)
assert msg == ac1.get_message_by_id(msg.id)
def test_message(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
msg = chat.send_text("msg1")
def test_prepare_file(self, ac1, chat1):
blobdir = ac1.get_blobdir()
p = os.path.join(blobdir, "somedata.txt")
with open(p, "w") as f:
f.write("some data")
message = chat1.prepare_message_file(p)
assert message.id > 0
message.set_text("hello world")
assert message.is_out_preparing()
assert message.text == "hello world"
chat1.send_prepared(message)
assert "Sent" in message.get_message_info()
def test_message_eq_contains(self, chat1):
msg = chat1.send_text("msg1")
assert msg in chat1.get_messages()
assert not (msg not in chat1.get_messages())
str(msg)
repr(msg)
def test_message_send_text(self, chat1):
msg = chat1.send_text("msg1")
assert msg
assert msg.view_type.is_text()
assert msg.view_type.name == "text"
assert not msg.view_type.is_audio()
assert not msg.view_type.is_video()
assert not msg.view_type.is_gif()
assert not msg.view_type.is_file()
assert not msg.view_type.is_image()
msg_state = msg.get_state()
assert not msg_state.is_in_fresh()
assert not msg_state.is_in_noticed()
assert not msg_state.is_in_seen()
assert msg_state.is_out_pending()
assert not msg_state.is_out_failed()
assert not msg_state.is_out_delivered()
assert not msg_state.is_out_mdn_received()
assert msg.is_text()
assert not msg.is_audio()
assert not msg.is_video()
assert not msg.is_gif()
assert not msg.is_file()
assert not msg.is_image()
def test_create_chat_by_mssage_id(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
msg = chat.send_text("msg1")
assert chat == ac1.create_chat_by_message(msg)
assert chat == ac1.create_chat_by_message(msg.id)
assert not msg.is_in_fresh()
assert not msg.is_in_noticed()
assert not msg.is_in_seen()
assert msg.is_out_pending()
assert not msg.is_out_failed()
assert not msg.is_out_delivered()
assert not msg.is_out_mdn_received()
def test_message_image(self, acfactory, data, lp):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
def test_create_chat_by_message_id(self, ac1, chat1):
msg = chat1.send_text("msg1")
assert chat1 == ac1.create_chat_by_message(msg)
assert chat1 == ac1.create_chat_by_message(msg.id)
def test_message_image(self, chat1, data, lp):
with pytest.raises(ValueError):
chat.send_image(path="notexists")
chat1.send_image(path="notexists")
fn = data.get_path("d.png")
lp.sec("sending image")
msg = chat.send_image(fn)
assert msg.view_type.name == "image"
msg = chat1.send_image(fn)
assert msg.is_image()
assert msg
assert msg.id > 0
assert os.path.exists(msg.filename)
@@ -179,20 +217,19 @@ class TestOfflineAccount:
("text/plain", "text/plain"),
("image/png", "image/png"),
])
def test_message_file(self, acfactory, data, lp, typein, typeout):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
def test_message_file(self, ac1, chat1, data, lp, typein, typeout):
lp.sec("sending file")
fn = data.get_path("r.txt")
msg = chat.send_file(fn, typein)
msg = chat1.send_file(fn, typein)
assert msg
assert msg.id > 0
assert msg.view_type.name == "file"
assert msg.view_type.is_file()
assert msg.is_file()
assert os.path.exists(msg.filename)
assert msg.filename.endswith(msg.basename)
assert msg.filemime == typeout
msg2 = chat1.send_file(fn, typein)
assert msg2 != msg
assert msg2.filename != msg.filename
def test_create_chat_mismatch(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
@@ -205,12 +242,9 @@ class TestOfflineAccount:
with pytest.raises(ValueError):
ac2.create_chat_by_message(msg)
def test_chat_message_distinctions(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
def test_chat_message_distinctions(self, ac1, chat1):
past1s = datetime.utcnow() - timedelta(seconds=1)
msg = chat.send_text("msg1")
msg = chat1.send_text("msg1")
ts = msg.time_sent
assert msg.time_received is None
assert ts.strftime("Y")
@@ -218,8 +252,7 @@ class TestOfflineAccount:
contact = msg.get_sender_contact()
assert contact == ac1.get_self_contact()
def test_basic_configure_ok_addr_setting_forbidden(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
def test_basic_configure_ok_addr_setting_forbidden(self, ac1):
assert ac1.get_config("mail_pw")
assert ac1.is_configured()
with pytest.raises(ValueError):
@@ -258,11 +291,21 @@ class TestOfflineAccount:
assert messages[0].text == "msg1"
assert os.path.exists(messages[1].filename)
def test_ac_setup_message_fails(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
def test_ac_setup_message_fails(self, ac1):
with pytest.raises(RuntimeError):
ac1.initiate_key_transfer()
def test_set_get_draft(self, chat1):
msg = Message.new_empty(chat1.account, "text")
msg1 = chat1.prepare_message(msg)
msg1.set_text("hello")
chat1.set_draft(msg1)
msg1.set_text("obsolete")
msg2 = chat1.get_draft()
assert msg2.text == "hello"
chat1.set_draft(None)
assert chat1.get_draft() is None
class TestOnlineAccount:
def test_one_account_init(self, acfactory):
@@ -294,48 +337,13 @@ class TestOnlineAccount:
wait_successful_IMAP_SMTP_connection(ac2)
wait_configuration_progress(ac2, 1000)
txt = "message1"
msg_out = chat.send_text(txt)
msg_out = chat.send_text("message1")
# wait for other account to receive
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
msg_in = ac2.get_message_by_id(msg_out.id)
assert msg_in.text == txt
def test_two_accounts_send_receive_encrypted(self, acfactory, lp):
lp.sec("starting accounts, waiting for configuration")
ac1 = acfactory.get_online_configuring_account()
ac2 = acfactory.get_online_configuring_account()
c2 = ac1.create_contact(email=ac2.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
wait_successful_IMAP_SMTP_connection(ac1)
wait_configuration_progress(ac1, 1000)
wait_successful_IMAP_SMTP_connection(ac2)
wait_configuration_progress(ac2, 1000)
lp.sec("sending message from ac1 to ac2")
msg_out = chat.send_text("hello")
lp.sec("wait for second account to receive")
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
lp.sec("chat back with text message, encrypted from ac2 to ac1")
chat_back = ac2.create_chat_by_message(msg_out.id)
txt = "https://testflight.apple.com/join/uEMc1NxS"
msg_back = chat_back.send_text(txt)
lp.sec("wait for first account to receive")
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
if ev[2] != msg_back.id:
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_back.id
chat_back = ac1.create_chat_by_message(msg_back.id)
msg_in = chat_back.get_messages()[-1]
assert msg_in.text == text
assert msg_in.text == "message1"
def test_forward_messages(self, acfactory):
ac1 = acfactory.get_online_configuring_account()
@@ -386,7 +394,7 @@ class TestOnlineAccount:
evt_name, data1, data2 = ev
assert data1 == chat.id
assert data2 == msg_out.id
assert msg_out.get_state().is_out_delivered()
assert msg_out.is_out_delivered()
lp.sec("wait for ac2 to receive message")
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
@@ -413,10 +421,11 @@ class TestOnlineAccount:
lp.sec("mark message as seen on ac2, wait for changes on ac1")
ac2.mark_seen_messages([msg_in])
lp.step("1")
ac1._evlogger.get_matching("DC_EVENT_MSG_READ")
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_READ")
assert ev[1] >= const.DC_CHAT_ID_LAST_SPECIAL
assert ev[2] >= const.DC_MSG_ID_LAST_SPECIAL
lp.step("2")
# ac1._evlogger.get_info_matching("Message marked as seen")
assert msg_out.get_state().is_out_mdn_received()
assert msg_out.is_out_mdn_received()
def test_saved_mime_on_received_message(self, acfactory, lp):
lp.sec("starting accounts, waiting for configuration")
@@ -456,13 +465,13 @@ class TestOnlineAccount:
evt_name, data1, data2 = ev
assert data1 == chat.id
assert data2 == msg_out.id
assert msg_out.get_state().is_out_delivered()
assert msg_out.is_out_delivered()
lp.sec("wait for ac2 to receive message")
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
msg_in = ac2.get_message_by_id(msg_out.id)
assert msg_in.view_type.is_image()
assert msg_in.is_image()
assert os.path.exists(msg_in.filename)
assert os.stat(msg_in.filename).st_size == os.stat(path).st_size
@@ -498,7 +507,7 @@ class TestOnlineAccount:
wait_configuration_progress(ac1, 1000)
assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"]
setup_code = ac1.initiate_key_transfer()
ac2._evlogger.set_timeout(10)
ac2._evlogger.set_timeout(30)
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
msg = ac2.get_message_by_id(ev[2])
assert msg.is_setup_message()

View File

@@ -1,6 +1,4 @@
from __future__ import print_function
import os
import shutil
from filecmp import cmp
from deltachat import const
from conftest import wait_configuration_progress, wait_msgs_changed
@@ -13,18 +11,15 @@ class TestInCreation:
wait_configuration_progress(ac1, 1000)
wait_configuration_progress(ac2, 1000)
blobdir = ac1.get_blobdir()
c2 = ac1.create_contact(email=ac2.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
wait_msgs_changed(ac1, 0, 0) # why no chat id?
lp.sec("create a message with a file in creation")
path = os.path.join(blobdir, "d.png")
open(path, 'a').close()
prepared_original = chat.prepare_file(path)
assert prepared_original.get_state().is_out_preparing()
path = data.get_path("d.png")
prepared_original = chat.prepare_message_file(path)
assert prepared_original.is_out_preparing()
wait_msgs_changed(ac1, chat.id, prepared_original.id)
lp.sec("forward the message while still in creation")
@@ -37,35 +32,36 @@ class TestInCreation:
forwarded_id = wait_msgs_changed(ac1, chat2.id)
if forwarded_id == 0:
forwarded_id = wait_msgs_changed(ac1, chat2.id)
assert forwarded_id
forwarded_msg = ac1.get_message_by_id(forwarded_id)
assert forwarded_msg.get_state().is_out_preparing()
assert forwarded_msg.is_out_preparing()
lp.sec("finish creating the file and send it")
shutil.copy(data.get_path("d.png"), path)
sent_original = chat.send_prepared(prepared_original)
assert sent_original.id == prepared_original.id
state = sent_original.get_state()
assert state.is_out_pending() or state.is_out_delivered()
wait_msgs_changed(ac1, chat.id, sent_original.id)
assert prepared_original.is_out_preparing()
chat.send_prepared(prepared_original)
assert prepared_original.is_out_pending() or prepared_original.is_out_delivered()
wait_msgs_changed(ac1, chat.id, prepared_original.id)
lp.sec("expect the forwarded message to be sent now too")
wait_msgs_changed(ac1, chat2.id, forwarded_id)
state = ac1.get_message_by_id(forwarded_id).get_state()
assert state.is_out_pending() or state.is_out_delivered()
fwd_msg = ac1.get_message_by_id(forwarded_id)
assert fwd_msg.is_out_pending() or fwd_msg.is_out_delivered()
lp.sec("wait for the messages to be delivered to SMTP")
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED")
assert ev[1] == chat.id
assert ev[2] == sent_original.id
assert ev[2] == prepared_original.id
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED")
assert ev[1] == chat2.id
assert ev[2] == forwarded_id
lp.sec("wait for both messages to arrive")
lp.sec("wait1 for original or forwarded messages to arrive")
ev1 = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev1[1] >= const.DC_CHAT_ID_LAST_SPECIAL
received_original = ac2.get_message_by_id(ev1[2])
assert cmp(received_original.filename, path, False)
lp.sec("wait2 for original or forwarded messages to arrive")
ev2 = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev2[1] >= const.DC_CHAT_ID_LAST_SPECIAL
assert ev2[1] != ev1[1]

View File

@@ -64,3 +64,14 @@ def test_sig():
assert sig(const.DC_EVENT_SMTP_CONNECTED) == 2
assert sig(const.DC_EVENT_IMAP_CONNECTED) == 2
assert sig(const.DC_EVENT_SMTP_MESSAGE_SENT) == 2
def test_markseen_invalid_message_ids(acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact(email="some1@example.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
chat.send_text("one messae")
ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
msg_ids = [9]
lib.dc_markseen_msgs(ac1._dc_context, msg_ids, len(msg_ids))
ac1._evlogger.ensure_event_not_queued("DC_EVENT_WARNING|DC_EVENT_ERROR")

View File

@@ -8,7 +8,7 @@ envlist =
[testenv]
commands =
pytest -rsXx {posargs:tests}
pytest -v -rsXx {posargs:tests}
python tests/package_wheels.py {toxworkdir}/wheelhouse
passenv =
TRAVIS

View File

@@ -6,7 +6,7 @@ use std::{fmt, str};
use mmime::mailimf_types::*;
use crate::constants::*;
use crate::dc_contact::*;
use crate::contact::*;
use crate::dc_tools::as_str;
use crate::key::*;
@@ -94,7 +94,7 @@ impl Aheader {
match Self::from_str(value) {
Ok(test) => {
if dc_addr_cmp(&test.addr, as_str(wanted_from)) {
if addr_cmp(&test.addr, as_str(wanted_from)) {
if fine_header.is_none() {
fine_header = Some(test);
} else {

334
src/chatlist.rs Normal file
View File

@@ -0,0 +1,334 @@
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::stock::StockMessage;
/// An object representing a single chatlist in memory.
///
/// Chatlist objects contain chat IDs and, if possible, message IDs belonging to them.
/// The chatlist object is not updated; if you want an update, you have to recreate the object.
///
/// For a **typical chat overview**, the idea is to get the list of all chats via dc_get_chatlist()
/// without any listflags (see below) and to implement a "virtual list" or so
/// (the count of chats is known by chatlist.len()).
///
/// Only for the items that are in view (the list may have several hundreds chats),
/// the UI should call chatlist.get_summary() then.
/// chatlist.get_summary() provides all elements needed for painting the item.
///
/// On a click of such an item, the UI should change to the chat view
/// and get all messages from this view via dc_get_chat_msgs().
/// Again, a "virtual list" is created (the count of messages is known)
/// and for each messages that is scrolled into view, dc_get_msg() is called then.
///
/// Why no listflags?
/// Without listflags, dc_get_chatlist() adds the deaddrop and the archive "link" automatically as needed.
/// The UI can just render these items differently then. Although the deaddrop link is currently always the
/// first entry and only present on new messages, there is the rough idea that it can be optionally always
/// present and sorted into the list by date. Rendering the deaddrop in the described way
/// would not add extra work in the UI then.
pub struct Chatlist<'a> {
context: &'a Context,
/// Stores pairs of `chat_id, message_id`
ids: Vec<(u32, u32)>,
}
impl<'a> Chatlist<'a> {
pub fn get_context(&self) -> &Context {
self.context
}
/// Get a list of chats.
/// The list can be filtered by query parameters.
///
/// The list is already sorted and starts with the most recent chat in use.
/// The sorting takes care of invalid sending dates, drafts and chats without messages.
/// Clients should not try to re-sort the list as this would be an expensive action
/// and would result in inconsistencies between clients.
///
/// To get information about each entry, use eg. chatlist.get_summary().
///
/// By default, the function adds some special entries to the list.
/// These special entries can be identified by the ID returned by chatlist.get_chat_id():
/// - DC_CHAT_ID_DEADDROP (1) - this special chat is present if there are
/// messages from addresses that have no relationship to the configured account.
/// The last of these messages is represented by DC_CHAT_ID_DEADDROP and you can retrieve details
/// about it with chatlist.get_msg_id(). Typically, the UI asks the user "Do you want to chat with NAME?"
/// and offers the options "Yes" (call dc_create_chat_by_msg_id()), "Never" (call dc_block_contact())
/// or "Not now".
/// The UI can also offer a "Close" button that calls dc_marknoticed_contact() then.
/// - DC_CHAT_ID_ARCHIVED_LINK (6) - this special chat is present if the user has
/// archived _any_ chat using dc_archive_chat(). The UI should show a link as
/// "Show archived chats", if the user clicks this item, the UI should show a
/// list of all archived chats that can be created by this function hen using
/// the DC_GCL_ARCHIVED_ONLY flag.
/// - DC_CHAT_ID_ALLDONE_HINT (7) - this special chat is present
/// if DC_GCL_ADD_ALLDONE_HINT is added to listflags
/// and if there are only archived chats.
///
/// The `listflags` is a combination of flags:
/// - if the flag DC_GCL_ARCHIVED_ONLY is set, only archived chats are returned.
/// if DC_GCL_ARCHIVED_ONLY is not set, only unarchived chats are returned and
/// the pseudo-chat DC_CHAT_ID_ARCHIVED_LINK is added if there are _any_ archived
/// chats
/// - if the flag DC_GCL_NO_SPECIALS is set, deaddrop and archive link are not added
/// to the list (may be used eg. for selecting chats on forwarding, the flag is
/// not needed when DC_GCL_ARCHIVED_ONLY is already set)
/// - if the flag DC_GCL_ADD_ALLDONE_HINT is set, DC_CHAT_ID_ALLDONE_HINT
/// is added as needed.
/// `query`: An optional query for filtering the list. Only chats matching this query
/// are returned.
/// `query_contact_id`: An optional contact ID for filtering the list. Only chats including this contact ID
/// are returned.
pub fn try_load(
context: &'a Context,
listflags: usize,
query: Option<&str>,
query_contact_id: Option<u32>,
) -> Result<Self> {
let mut add_archived_link_item = 0;
// select with left join and minimum:
// - the inner select must use `hidden` and _not_ `m.hidden`
// which would refer the outer select and take a lot of time
// - `GROUP BY` is needed several messages may have the same timestamp
// - the list starts with the newest chats
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let process_row = |row: &rusqlite::Row| {
let chat_id: i32 = row.get(0)?;
// TODO: verify that it is okay for this to be Null
let msg_id: i32 = row.get(1).unwrap_or_default();
Ok((chat_id as u32, msg_id as u32))
};
let process_rows = |rows: rusqlite::MappedRows<_>| {
rows.collect::<std::result::Result<Vec<_>, _>>()
.map_err(Into::into)
};
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let mut ids = if let Some(query_contact_id) = query_contact_id {
// show chats shared with a given contact
context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![query_contact_id as i32],
process_row,
process_rows,
)?
} else if 0 != listflags & DC_GCL_ARCHIVED_ONLY {
// show archived chats
context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=1 GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
)?
} else if let Some(query) = query {
let query = query.trim().to_string();
ensure!(!query.is_empty(), "missing query");
let str_like_cmd = format!("%{}%", query);
context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.name LIKE ? \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![str_like_cmd],
process_row,
process_rows,
)?
} else {
// show normal chatlist
let mut ids = context.sql.query_map(
"SELECT c.id, m.id FROM chats c \
LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=0 \
GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
)?;
if 0 == listflags & DC_GCL_NO_SPECIALS {
let last_deaddrop_fresh_msg_id = get_last_deaddrop_fresh_msg(context);
if last_deaddrop_fresh_msg_id > 0 {
ids.push((1, last_deaddrop_fresh_msg_id));
}
add_archived_link_item = 1;
}
ids
};
if 0 != add_archived_link_item && dc_get_archived_cnt(context) > 0 {
if ids.is_empty() && 0 != listflags & DC_GCL_ADD_ALLDONE_HINT {
ids.push((DC_CHAT_ID_ALLDONE_HINT as u32, 0));
}
ids.push((DC_CHAT_ID_ARCHIVED_LINK as u32, 0));
}
Ok(Chatlist { context, ids })
}
/// Find out the number of chats.
pub fn len(&self) -> usize {
self.ids.len()
}
pub fn is_empty(&self) -> bool {
self.ids.is_empty()
}
/// Get a single chat ID of a chatlist.
///
/// To get the message object from the message ID, use dc_get_chat().
pub fn get_chat_id(&self, index: usize) -> u32 {
if index >= self.ids.len() {
return 0;
}
self.ids[index].0
}
/// Get a single message ID of a chatlist.
///
/// To get the message object from the message ID, use dc_get_msg().
pub fn get_msg_id(&self, index: usize) -> u32 {
if index >= self.ids.len() {
return 0;
}
self.ids[index].1
}
/// Get a summary for a chatlist index.
///
/// The summary is returned by a dc_lot_t object with the following fields:
///
/// - dc_lot_t::text1: contains the username or the strings "Me", "Draft" and so on.
/// The string may be colored by having a look at text1_meaning.
/// If there is no such name or it should not be displayed, the element is NULL.
/// - dc_lot_t::text1_meaning: one of DC_TEXT1_USERNAME, DC_TEXT1_SELF or DC_TEXT1_DRAFT.
/// Typically used to show dc_lot_t::text1 with different colors. 0 if not applicable.
/// - dc_lot_t::text2: contains an excerpt of the message text or strings as
/// "No messages". May be NULL of there is no such text (eg. for the archive link)
/// - 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 {
// 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();
if index >= self.ids.len() {
(*ret).text2 = "ErrBadChatlistIndex".strdup();
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);
return ret;
}
}
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)
{
lastcontact = Contact::load_from_db(self.context, (*lastmsg).from_id).ok();
}
lastmsg
} else {
std::ptr::null_mut()
};
if (*chat).id == DC_CHAT_ID_ARCHIVED_LINK as u32 {
(*ret).text2 = dc_strdup(0 as *const libc::c_char)
} else if lastmsg.is_null() || (*lastmsg).from_id == DC_CONTACT_ID_UNDEFINED as u32 {
(*ret).text2 = self.context.stock_str(StockMessage::NoMessages).strdup();
} else {
dc_lot_fill(ret, lastmsg, chat, lastcontact.as_ref(), self.context);
}
dc_msg_unref(lastmsg);
ret
}
}
pub fn dc_get_archived_cnt(context: &Context) -> u32 {
context
.sql
.query_row_col(
context,
"SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;",
params![],
0,
)
.unwrap_or_default()
}
fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 {
// We have an index over the state-column, this should be sufficient as there are typically
// only few fresh messages.
context
.sql
.query_row_col(
context,
"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \
WHERE m.state=10 \
AND m.hidden=0 \
AND c.blocked=2 \
ORDER BY m.timestamp DESC, m.id DESC;",
params![],
0,
)
.unwrap_or_default()
}

View File

@@ -4,10 +4,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_stock::*;
use crate::dc_tools::*;
use crate::error::Error;
use crate::x::*;
use crate::stock::StockMessage;
/// The available configuration keys.
#[derive(
@@ -70,17 +69,7 @@ impl Context {
let value = match key {
Config::Selfavatar => {
let rel_path = self.sql.get_config(self, key);
rel_path.map(|p| {
let v = unsafe {
let n = to_cstring(p);
let res = dc_get_abs_path(self, n);
free(n as *mut libc::c_void);
res
};
let r = to_string(v);
unsafe { free(v as *mut _) };
r
})
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::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
@@ -94,12 +83,7 @@ impl Context {
// Default values
match key {
Config::Selfstatus => {
let s = unsafe { dc_stock_str(self, 13) };
let res = to_string(s);
unsafe { free(s as *mut _) };
Some(res)
}
Config::Selfstatus => Some(self.stock_str(StockMessage::StatusLine).into_owned()),
_ => key.get_str("default").map(|s| s.to_string()),
}
}
@@ -129,15 +113,14 @@ impl Context {
ret
}
Config::Selfstatus => {
let def = unsafe { dc_stock_str(self, 13) };
let val = if value.is_none() || value.unwrap() == as_str(def) {
let def = self.stock_str(StockMessage::StatusLine);
let val = if value.is_none() || value.unwrap() == def {
None
} else {
value
};
let ret = self.sql.set_config(self, key, val);
unsafe { free(def as *mut libc::c_void) };
ret
}
_ => self.sql.set_config(self, key, value),

View File

@@ -1,4 +1,8 @@
//! Constants
#![allow(non_camel_case_types)]
use num_traits::{FromPrimitive, ToPrimitive};
use rusqlite as sql;
use rusqlite::types::*;
pub const DC_VERSION_STR: &'static [u8; 14] = b"1.0.0-alpha.3\x00";
@@ -59,32 +63,33 @@ 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: usize = 0;
pub const DC_CHAT_TYPE_SINGLE: usize = 100;
pub const DC_CHAT_TYPE_GROUP: usize = 120;
pub const DC_CHAT_TYPE_VERIFIED_GROUP: usize = 130;
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;
pub const DC_MSG_ID_MARKER1: usize = 1;
pub const DC_MSG_ID_DAYMARKER: usize = 9;
pub const DC_MSG_ID_LAST_SPECIAL: usize = 9;
pub const DC_STATE_UNDEFINED: usize = 0;
pub const DC_STATE_IN_FRESH: usize = 10;
pub const DC_STATE_IN_NOTICED: usize = 13;
pub const DC_STATE_IN_SEEN: usize = 16;
pub const DC_STATE_OUT_PREPARING: usize = 18;
pub const DC_STATE_OUT_DRAFT: usize = 19;
pub const DC_STATE_OUT_PENDING: usize = 20;
pub const DC_STATE_OUT_FAILED: usize = 24;
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: usize = 26;
pub const DC_STATE_OUT_MDN_RCVD: usize = 28;
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;
/// approx. max. length returned by dc_get_msg_info()
pub 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;
pub const DC_CONTACT_ID_LAST_SPECIAL: usize = 9;
@@ -95,46 +100,6 @@ pub const DC_TEXT1_SELF: usize = 3;
pub const DC_CREATE_MVBOX: usize = 1;
/// Text message.
/// The text of the message is set using dc_msg_set_text()
/// and retrieved with dc_msg_get_text().
pub const DC_MSG_TEXT: usize = 10;
/// Image message.
/// If the image is an animated GIF, the type DC_MSG_GIF should be used.
/// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension
/// and retrieved via dc_msg_set_file(), dc_msg_set_dimension().
pub const DC_MSG_IMAGE: usize = 20;
/// Animated GIF message.
/// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension()
/// and retrieved via dc_msg_get_file(), dc_msg_get_width(), dc_msg_get_height().
pub const DC_MSG_GIF: usize = 21;
/// Message containing an Audio file.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration().
pub const DC_MSG_AUDIO: usize = 40;
/// A voice message that was directly recorded by the user.
/// For all other audio messages, the type #DC_MSG_AUDIO should be used.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration()
pub const DC_MSG_VOICE: usize = 41;
/// Video messages.
/// File, width, height and durarion
/// are set via dc_msg_set_file(), dc_msg_set_dimension(), dc_msg_set_duration()
/// and retrieved via
/// dc_msg_get_file(), dc_msg_get_width(),
/// dc_msg_get_height(), dc_msg_get_duration().
pub const DC_MSG_VIDEO: usize = 50;
/// Message containing any file, eg. a PDF.
/// The file is set via dc_msg_set_file()
/// and retrieved via dc_msg_get_file().
pub const DC_MSG_FILE: usize = 60;
// Flags for configuring IMAP and SMTP servers.
// These flags are optional
// and may be set together with the username, password etc.
@@ -182,6 +147,78 @@ pub const DC_LP_IMAP_SOCKET_FLAGS: usize =
pub const DC_LP_SMTP_SOCKET_FLAGS: usize =
(DC_LP_SMTP_SOCKET_STARTTLS | DC_LP_SMTP_SOCKET_SSL | DC_LP_SMTP_SOCKET_PLAIN);
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
#[repr(i32)]
pub enum Viewtype {
Unknown = 0,
/// Text message.
/// The text of the message is set using dc_msg_set_text()
/// and retrieved with dc_msg_get_text().
Text = 10,
/// Image message.
/// If the image is an animated GIF, the type DC_MSG_GIF should be used.
/// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension
/// and retrieved via dc_msg_set_file(), dc_msg_set_dimension().
Image = 20,
/// Animated GIF message.
/// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension()
/// and retrieved via dc_msg_get_file(), dc_msg_get_width(), dc_msg_get_height().
Gif = 21,
/// Message containing an Audio file.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration().
Audio = 40,
/// A voice message that was directly recorded by the user.
/// For all other audio messages, the type #DC_MSG_AUDIO should be used.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration()
Voice = 41,
/// Video messages.
/// File, width, height and durarion
/// are set via dc_msg_set_file(), dc_msg_set_dimension(), dc_msg_set_duration()
/// and retrieved via
/// dc_msg_get_file(), dc_msg_get_width(),
/// dc_msg_get_height(), dc_msg_get_duration().
Video = 50,
/// Message containing any file, eg. a PDF.
/// The file is set via dc_msg_set_file()
/// and retrieved via dc_msg_get_file().
File = 60,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn derive_display_works_as_expected() {
assert_eq!(format!("{}", Viewtype::Audio), "Audio");
}
}
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,

1093
src/contact.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
use std::sync::{Arc, Condvar, Mutex, RwLock};
use crate::constants::*;
use crate::contact::*;
use crate::dc_array::*;
use crate::dc_chat::*;
use crate::dc_contact::*;
use crate::dc_job::*;
use crate::dc_jobthread::*;
use crate::dc_loginparam::*;
@@ -14,6 +14,7 @@ use crate::dc_receive_imf::*;
use crate::dc_tools::*;
use crate::imap::*;
use crate::key::*;
use crate::param::Params;
use crate::smtp::*;
use crate::sql::Sql;
use crate::types::*;
@@ -26,15 +27,15 @@ pub struct Context {
pub blobdir: Arc<RwLock<*mut libc::c_char>>,
pub sql: Sql,
pub inbox: Arc<RwLock<Imap>>,
pub perform_inbox_jobs_needed: Arc<RwLock<i32>>,
pub probe_imap_network: Arc<RwLock<i32>>,
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 smtp: Arc<Mutex<Smtp>>,
pub smtp_state: Arc<(Mutex<SmtpState>, Condvar)>,
pub oauth2_critical: Arc<Mutex<()>>,
pub cb: Option<dc_callback_t>,
pub os_name: *mut libc::c_char,
pub os_name: Option<String>,
pub cmdline_sel_chat_id: Arc<RwLock<u32>>,
pub bob: Arc<RwLock<BobStatus>>,
pub last_smeared_timestamp: Arc<RwLock<i64>>,
@@ -105,33 +106,17 @@ impl Default for BobStatus {
#[derive(Default, Debug)]
pub struct SmtpState {
pub idle: bool,
pub suspended: i32,
pub doing_jobs: i32,
pub suspended: bool,
pub doing_jobs: bool,
pub perform_jobs_needed: i32,
pub probe_network: i32,
}
// location handling
#[derive(Copy, Clone)]
#[repr(C)]
pub struct _dc_location {
pub location_id: uint32_t,
pub latitude: libc::c_double,
pub longitude: libc::c_double,
pub accuracy: libc::c_double,
pub timestamp: i64,
pub contact_id: uint32_t,
pub msg_id: uint32_t,
pub chat_id: uint32_t,
pub marker: *mut libc::c_char,
pub independent: uint32_t,
pub probe_network: bool,
}
// create/open/config/information
pub fn dc_context_new(
cb: Option<dc_callback_t>,
userdata: *mut libc::c_void,
os_name: *const libc::c_char,
os_name: Option<String>,
) -> Context {
Context {
blobdir: Arc::new(RwLock::new(std::ptr::null_mut())),
@@ -146,7 +131,7 @@ pub fn dc_context_new(
})),
userdata,
cb,
os_name: unsafe { dc_strdup_keep_null(os_name) },
os_name: os_name,
running_state: Arc::new(RwLock::new(Default::default())),
sql: Sql::new(),
smtp: Arc::new(Mutex::new(Smtp::new())),
@@ -175,8 +160,18 @@ pub fn dc_context_new(
cb_receive_imf,
),
))),
probe_imap_network: Arc::new(RwLock::new(0)),
perform_inbox_jobs_needed: Arc::new(RwLock::new(0)),
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) };
}
}
@@ -241,26 +236,15 @@ unsafe fn cb_precheck_imf(
}
dc_do_heuristics_moves(context, server_folder, msg_id);
if 0 != mark_seen {
dc_job_add(
context,
130i32,
msg_id as libc::c_int,
0 as *const libc::c_char,
0i32,
);
dc_job_add(context, 130, msg_id as libc::c_int, Params::new(), 0);
}
}
free(old_server_folder as *mut libc::c_void);
return rfc724_mid_exists;
}
unsafe fn cb_set_config(context: &Context, key: *const libc::c_char, value: *const libc::c_char) {
let v = if value.is_null() {
None
} else {
Some(as_str(value))
};
context.sql.set_config(context, as_str(key), v).ok();
fn cb_set_config(context: &Context, key: &str, value: Option<&str>) {
context.sql.set_config(context, key, value).ok();
}
/* *
@@ -270,24 +254,14 @@ unsafe fn cb_set_config(context: &Context, key: *const libc::c_char, value: *con
*
* @private @memberof Context
*/
unsafe fn cb_get_config(
context: &Context,
key: *const libc::c_char,
def: *const libc::c_char,
) -> *mut libc::c_char {
let res = context
.sql
.get_config(context, as_str(key))
.unwrap_or_else(|| to_string(def));
to_cstring(res)
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);
}
free(context.os_name as *mut libc::c_void);
}
pub unsafe fn dc_close(context: &Context) {
@@ -331,32 +305,26 @@ pub unsafe fn dc_get_userdata(context: &mut Context) -> *mut libc::c_void {
context.userdata as *mut _
}
pub unsafe fn dc_open(
context: &Context,
dbfile: *const libc::c_char,
blobdir: *const libc::c_char,
) -> libc::c_int {
let mut success = 0;
pub unsafe fn dc_open(context: &Context, dbfile: &str, blobdir: Option<&str>) -> bool {
let mut success = false;
if 0 != dc_is_open(context) {
return 0;
return false;
}
if !dbfile.is_null() {
*context.dbfile.write().unwrap() = dc_strdup(dbfile);
if !blobdir.is_null() && 0 != *blobdir.offset(0isize) as libc::c_int {
let dir = dc_strdup(blobdir);
dc_ensure_no_slash(dir);
*context.blobdir.write().unwrap() = dir;
} else {
let dir = dc_mprintf(b"%s-blobs\x00" as *const u8 as *const libc::c_char, dbfile);
dc_create_folder(context, dir);
*context.blobdir.write().unwrap() = dir;
}
// Create/open sqlite database, this may already use the blobdir
if context.sql.open(context, as_path(dbfile), 0) {
success = 1i32
}
*context.dbfile.write().unwrap() = dbfile.strdup();
if blobdir.is_some() && blobdir.unwrap().len() > 0 {
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;
}
if 0 == success {
// Create/open sqlite database, this may already use the blobdir
let dbfile_path = std::path::Path::new(dbfile);
if context.sql.open(context, dbfile_path, 0) {
success = true
}
if !success {
dc_close(context);
}
success
@@ -378,7 +346,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
let chats = dc_get_chat_cnt(context) as usize;
let real_msgs = dc_get_real_msg_cnt(context) as usize;
let deaddrop_msgs = dc_get_deaddrop_msg_cnt(context) as usize;
let contacts = dc_get_real_contact_cnt(context) as usize;
let contacts = Contact::get_real_cnt(context) as usize;
let is_configured = context
.sql
.get_config_int(context, "configured")
@@ -515,7 +483,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
fingerprint_str,
);
to_cstring(res)
res.strdup()
}
pub unsafe fn dc_get_version_str() -> *mut libc::c_char {
@@ -537,18 +505,19 @@ 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 ret = unsafe { dc_array_new(128 as size_t) };
let mut ret = dc_array_t::new(128);
for row in rows {
let id = row?;
unsafe { dc_array_add_id(ret, id) };
ret.add_id(id);
}
Ok(ret)
Ok(ret.into_raw())
},
)
.unwrap()
}
#[allow(non_snake_case)]
pub fn dc_search_msgs(
context: &Context,
chat_id: uint32_t,
@@ -576,7 +545,7 @@ 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 ret = unsafe { dc_array_new(100 as size_t) };
let mut ret = dc_array_t::new(100);
let success = context
.sql
@@ -586,7 +555,7 @@ pub fn dc_search_msgs(
|row| row.get::<_, i32>(0),
|rows| {
for id in rows {
unsafe { dc_array_add_id(ret, id? as u32) };
ret.add_id(id? as u32);
}
Ok(())
},
@@ -594,12 +563,9 @@ pub fn dc_search_msgs(
.is_ok();
if success {
return ret;
return ret.into_raw();
}
if !ret.is_null() {
unsafe { dc_array_unref(ret) };
}
std::ptr::null_mut()
}

View File

@@ -1,78 +1,175 @@
use crate::context::*;
use crate::dc_location::dc_location;
use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
const DC_ARRAY_MAGIC: uint32_t = 0x000a11aa;
/* * the structure behind dc_array_t */
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_array_t {
pub magic: uint32_t,
pub allocated: size_t,
pub count: size_t,
pub type_0: libc::c_int,
pub array: *mut uintptr_t,
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub enum dc_array_t {
Locations(Vec<dc_location>),
Uint(Vec<uintptr_t>),
}
/**
* @class dc_array_t
*
* An object containing a simple array.
* This object is used in several places where functions need to return an array.
* The items of the array are typically IDs.
* To free an array object, use dc_array_unref().
*/
pub unsafe fn dc_array_unref(mut array: *mut dc_array_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return;
impl dc_array_t {
pub fn new(capacity: usize) -> Self {
dc_array_t::Uint(Vec::with_capacity(capacity))
}
if (*array).type_0 == 1i32 {
dc_array_free_ptr(array);
}
free((*array).array as *mut libc::c_void);
(*array).magic = 0i32 as uint32_t;
free(array as *mut libc::c_void);
}
pub unsafe fn dc_array_free_ptr(array: *mut dc_array_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return;
/// Constructs a new, empty `dc_array_t` holding locations with specified `capacity`.
pub fn new_locations(capacity: usize) -> Self {
dc_array_t::Locations(Vec::with_capacity(capacity))
}
let mut i: size_t = 0i32 as size_t;
while i < (*array).count {
if (*array).type_0 == 1i32 {
free(
(*(*(*array).array.offset(i as isize) as *mut _dc_location)).marker
as *mut libc::c_void,
);
pub fn into_raw(self) -> *mut Self {
Box::into_raw(Box::new(self))
}
pub fn add_uint(&mut self, item: uintptr_t) {
if let Self::Uint(array) = self {
array.push(item);
} else {
panic!("Attempt to add uint 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)
} else {
panic!("Attempt to add a location to array of other type");
}
}
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,
Self::Uint(array) => array[index] as uint32_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]
} else {
panic!("Not an array of locations")
}
}
pub fn get_latitude(&self, index: usize) -> libc::c_double {
self.get_location(index).latitude
}
pub fn get_longitude(&self, index: size_t) -> libc::c_double {
self.get_location(index).longitude
}
pub fn get_accuracy(&self, index: size_t) -> libc::c_double {
self.get_location(index).accuracy
}
pub fn get_timestamp(&self, index: size_t) -> i64 {
self.get_location(index).timestamp
}
pub fn get_chat_id(&self, index: size_t) -> uint32_t {
self.get_location(index).chat_id
}
pub fn get_contact_id(&self, index: size_t) -> uint32_t {
self.get_location(index).contact_id
}
pub fn get_msg_id(&self, index: size_t) -> uint32_t {
self.get_location(index).msg_id
}
pub fn is_empty(&self) -> bool {
match self {
Self::Locations(array) => array.is_empty(),
Self::Uint(array) => array.is_empty(),
}
}
/// Returns the number of elements in the array.
pub fn len(&self) -> usize {
match self {
Self::Locations(array) => array.len(),
Self::Uint(array) => array.len(),
}
}
pub fn clear(&mut self) {
match self {
Self::Locations(array) => array.clear(),
Self::Uint(array) => array.clear(),
}
}
pub fn search_id(&self, needle: uintptr_t) -> Option<usize> {
if let Self::Uint(array) = self {
for (i, &u) in array.iter().enumerate() {
if u == needle {
return Some(i);
}
}
None
} else {
panic!("Attempt to search for id in array of other type");
}
}
pub fn sort_ids(&mut self) {
if let dc_array_t::Uint(v) = self {
v.sort();
} else {
panic!("Attempt to sort array of something other than uints");
}
free(*(*array).array.offset(i as isize) as *mut libc::c_void);
*(*array).array.offset(i as isize) = 0i32 as uintptr_t;
i = i.wrapping_add(1)
}
}
pub unsafe fn dc_array_add_uint(mut array: *mut dc_array_t, item: uintptr_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
impl From<Vec<dc_location>> for dc_array_t {
fn from(array: Vec<dc_location>) -> Self {
dc_array_t::Locations(array)
}
}
pub unsafe fn dc_array_unref(array: *mut dc_array_t) {
if array.is_null() {
return;
}
if (*array).count == (*array).allocated {
let newsize = (*array).allocated.wrapping_mul(2).wrapping_add(10);
(*array).array = realloc(
(*array).array as *mut libc::c_void,
(newsize).wrapping_mul(::std::mem::size_of::<uintptr_t>()),
) as *mut uintptr_t;
assert!(!(*array).array.is_null());
(*array).allocated = newsize as size_t
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);
}
*(*array).array.offset((*array).count as isize) = item;
(*array).count = (*array).count.wrapping_add(1);
}
pub unsafe fn dc_array_add_id(array: *mut dc_array_t, item: uint32_t) {
dc_array_add_uint(array, item as uintptr_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) {
@@ -80,130 +177,107 @@ pub unsafe fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void)
}
pub unsafe fn dc_array_get_cnt(array: *const dc_array_t) -> size_t {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return 0i32 as size_t;
if array.is_null() {
0
} else {
(*array).len()
}
(*array).count
}
pub unsafe fn dc_array_get_uint(array: *const dc_array_t, index: size_t) -> uintptr_t {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
return 0i32 as uintptr_t;
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_uint(index)
}
*(*array).array.offset(index as isize)
}
pub unsafe fn dc_array_get_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
return 0i32 as uint32_t;
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_id(index)
}
if (*array).type_0 == 1i32 {
return (*(*(*array).array.offset(index as isize) as *mut _dc_location)).location_id;
}
*(*array).array.offset(index as isize) as uint32_t
}
pub unsafe fn dc_array_get_ptr(array: *const dc_array_t, index: size_t) -> *mut libc::c_void {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
return 0 as *mut libc::c_void;
if array.is_null() || index >= (*array).len() {
std::ptr::null_mut()
} else {
(*array).get_ptr(index)
}
*(*array).array.offset(index as isize) as *mut libc::c_void
}
pub unsafe fn dc_array_get_latitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as libc::c_double;
if array.is_null() || index >= (*array).len() {
0.0
} else {
(*array).get_latitude(index)
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).latitude
}
pub unsafe fn dc_array_get_longitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as libc::c_double;
if array.is_null() || index >= (*array).len() {
0.0
} else {
(*array).get_longitude(index)
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).longitude
}
pub unsafe fn dc_array_get_accuracy(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as libc::c_double;
if array.is_null() || index >= (*array).len() {
0.0
} else {
(*array).get_accuracy(index)
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).accuracy
}
pub unsafe fn dc_array_get_timestamp(array: *const dc_array_t, index: size_t) -> i64 {
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0;
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_timestamp(index)
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).timestamp
}
pub unsafe fn dc_array_get_chat_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as uint32_t;
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_chat_id(index)
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).chat_id
}
pub unsafe fn dc_array_get_contact_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as uint32_t;
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_contact_id(index)
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).contact_id
}
pub unsafe fn dc_array_get_msg_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as uint32_t;
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_msg_id(index)
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).msg_id
}
pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *mut libc::c_char {
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0 as *mut libc::c_char;
if array.is_null() || index >= (*array).len() {
return std::ptr::null_mut();
}
if let dc_array_t::Locations(v) = &*array {
if let Some(s) = &v[index].marker {
s.strdup()
} else {
std::ptr::null_mut()
}
} else {
std::ptr::null_mut()
}
dc_strdup_keep_null((*(*(*array).array.offset(index as isize) as *mut _dc_location)).marker)
}
/**
@@ -217,16 +291,15 @@ 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()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
if array.is_null() || index >= (*array).len() {
return 0;
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).independent as libc::c_int
if let dc_array_t::Locations(v) = &*array {
v[index].independent as libc::c_int
} else {
panic!("Attempt to get location independent field from array of something other than locations");
}
}
pub unsafe fn dc_array_search_id(
@@ -234,176 +307,84 @@ pub unsafe fn dc_array_search_id(
needle: uint32_t,
ret_index: *mut size_t,
) -> bool {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
if array.is_null() {
return false;
}
let data: *mut uintptr_t = (*array).array;
let mut i: size_t = 0;
let cnt: size_t = (*array).count;
while i < cnt {
if *data.offset(i as isize) == needle as size_t {
if !ret_index.is_null() {
*ret_index = i
}
return true;
if let Some(i) = (*array).search_id(needle as uintptr_t) {
if !ret_index.is_null() {
*ret_index = i
}
i = i.wrapping_add(1)
true
} else {
false
}
false
}
pub unsafe fn dc_array_get_raw(array: *const dc_array_t) -> *const uintptr_t {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
if array.is_null() {
return 0 as *const uintptr_t;
}
(*array).array
}
pub unsafe fn dc_array_new(initsize: size_t) -> *mut dc_array_t {
dc_array_new_typed(0, initsize)
}
pub unsafe fn dc_array_new_typed(type_0: libc::c_int, initsize: size_t) -> *mut dc_array_t {
let mut array: *mut dc_array_t;
array = calloc(1, ::std::mem::size_of::<dc_array_t>()) as *mut dc_array_t;
assert!(!array.is_null());
(*array).magic = DC_ARRAY_MAGIC;
(*array).count = 0i32 as size_t;
(*array).allocated = if initsize < 1 { 1 } else { initsize };
(*array).type_0 = type_0;
(*array).array = malloc(
(*array)
.allocated
.wrapping_mul(::std::mem::size_of::<uintptr_t>()),
) as *mut uintptr_t;
if (*array).array.is_null() {
exit(48i32);
if let dc_array_t::Uint(v) = &*array {
v.as_ptr()
} else {
panic!("Attempt to convert array of something other than uints to raw");
}
array
}
pub unsafe fn dc_array_empty(mut array: *mut dc_array_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
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()
}
pub unsafe fn dc_array_empty(array: *mut dc_array_t) {
if array.is_null() {
return;
}
(*array).count = 0i32 as size_t;
(*array).clear()
}
pub unsafe fn dc_array_duplicate(array: *const dc_array_t) -> *mut dc_array_t {
let mut ret: *mut dc_array_t;
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return 0 as *mut dc_array_t;
}
ret = dc_array_new((*array).allocated);
(*ret).count = (*array).count;
memcpy(
(*ret).array as *mut libc::c_void,
(*array).array as *const libc::c_void,
(*array)
.count
.wrapping_mul(::std::mem::size_of::<uintptr_t>()),
);
ret
}
pub unsafe fn dc_array_sort_ids(array: *mut dc_array_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || (*array).count <= 1 {
return;
}
qsort(
(*array).array as *mut libc::c_void,
(*array).count,
::std::mem::size_of::<uintptr_t>(),
Some(cmp_intptr_t),
);
}
unsafe extern "C" fn cmp_intptr_t(p1: *const libc::c_void, p2: *const libc::c_void) -> libc::c_int {
let v1: uintptr_t = *(p1 as *mut uintptr_t);
let v2: uintptr_t = *(p2 as *mut uintptr_t);
return if v1 < v2 {
-1i32
} else if v1 > v2 {
1i32
if array.is_null() {
std::ptr::null_mut()
} else {
0i32
};
}
pub unsafe fn dc_array_sort_strings(array: *mut dc_array_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || (*array).count <= 1 {
return;
(*array).clone().into_raw()
}
qsort(
(*array).array as *mut libc::c_void,
(*array).count,
::std::mem::size_of::<*mut libc::c_char>(),
Some(cmp_strings_t),
);
}
unsafe extern "C" fn cmp_strings_t(
p1: *const libc::c_void,
p2: *const libc::c_void,
) -> libc::c_int {
let v1: *const libc::c_char = *(p1 as *mut *const libc::c_char);
let v2: *const libc::c_char = *(p2 as *mut *const libc::c_char);
strcmp(v1, v2)
}
pub unsafe fn dc_array_get_string(
array: *const dc_array_t,
sep: *const libc::c_char,
) -> *mut libc::c_char {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || sep.is_null() {
if array.is_null() || sep.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
let cnt = (*array).count as usize;
let slice = std::slice::from_raw_parts((*array).array, cnt);
let sep = as_str(sep);
if let dc_array_t::Uint(v) = &*array {
let cnt = v.len();
let sep = as_str(sep);
let res = slice
.iter()
.enumerate()
.fold(String::with_capacity(2 * cnt), |mut res, (i, n)| {
if i == 0 {
res += &n.to_string();
} else {
res += sep;
res += &n.to_string();
}
res
});
to_cstring(res)
}
/// return comma-separated value-string from integer array
pub unsafe fn dc_arr_to_string(arr: *const uint32_t, cnt: libc::c_int) -> *mut libc::c_char {
if arr.is_null() || cnt == 0 {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
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");
}
let slice = std::slice::from_raw_parts(arr, cnt as usize);
let res = slice.iter().enumerate().fold(
String::with_capacity(2 * cnt as usize),
|mut res, (i, n)| {
if i == 0 {
res += &n.to_string();
} else {
res += ",";
res += &n.to_string();
}
res
},
);
to_cstring(res)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::x::*;
use std::ffi::CStr;
#[test]
@@ -439,7 +420,7 @@ mod tests {
dc_array_add_id(arr, 0 as uint32_t);
dc_array_add_id(arr, 5000 as uint32_t);
dc_array_sort_ids(arr);
(*arr).sort_ids();
assert_eq!(dc_array_get_id(arr, 0 as size_t), 0);
assert_eq!(dc_array_get_id(arr, 1 as size_t), 7);
@@ -453,38 +434,6 @@ mod tests {
);
free(str as *mut libc::c_void);
dc_array_empty(arr);
dc_array_add_ptr(
arr,
b"XX\x00" as *const u8 as *const libc::c_char as *mut libc::c_void,
);
dc_array_add_ptr(
arr,
b"item1\x00" as *const u8 as *const libc::c_char as *mut libc::c_void,
);
dc_array_add_ptr(
arr,
b"bbb\x00" as *const u8 as *const libc::c_char as *mut libc::c_void,
);
dc_array_add_ptr(
arr,
b"aaa\x00" as *const u8 as *const libc::c_char as *mut libc::c_void,
);
dc_array_sort_strings(arr);
let str = dc_array_get_ptr(arr, 0 as size_t) as *mut libc::c_char;
assert_eq!(CStr::from_ptr(str).to_str().unwrap(), "XX");
let str = dc_array_get_ptr(arr, 1 as size_t) as *mut libc::c_char;
assert_eq!(CStr::from_ptr(str).to_str().unwrap(), "aaa");
let str = dc_array_get_ptr(arr, 2 as size_t) as *mut libc::c_char;
assert_eq!(CStr::from_ptr(str).to_str().unwrap(), "bbb");
let str = dc_array_get_ptr(arr, 3 as size_t) as *mut libc::c_char;
assert_eq!(CStr::from_ptr(str).to_str().unwrap(), "item1");
dc_array_unref(arr);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,389 +0,0 @@
use crate::context::*;
use crate::dc_array::*;
use crate::dc_chat::*;
use crate::dc_contact::*;
use crate::dc_lot::*;
use crate::dc_msg::*;
use crate::dc_stock::*;
use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
/* * the structure behind dc_chatlist_t */
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_chatlist_t<'a> {
pub magic: uint32_t,
pub context: &'a Context,
pub cnt: size_t,
pub chatNlastmsg_ids: *mut dc_array_t,
}
// handle chatlists
pub unsafe fn dc_get_chatlist<'a>(
context: &'a Context,
listflags: libc::c_int,
query_str: *const libc::c_char,
query_id: uint32_t,
) -> *mut dc_chatlist_t<'a> {
let obj = dc_chatlist_new(context);
if 0 != dc_chatlist_load_from_db(obj, listflags, query_str, query_id) {
return obj;
}
dc_chatlist_unref(obj);
return 0 as *mut dc_chatlist_t;
}
/**
* @class dc_chatlist_t
*
* An object representing a single chatlist in memory.
* Chatlist objects contain chat IDs
* and, if possible, message IDs belonging to them.
* The chatlist object is not updated;
* if you want an update, you have to recreate the object.
*
* For a **typical chat overview**,
* the idea is to get the list of all chats via dc_get_chatlist()
* without any listflags (see below)
* and to implement a "virtual list" or so
* (the count of chats is known by dc_chatlist_get_cnt()).
*
* Only for the items that are in view
* (the list may have several hundreds chats),
* the UI should call dc_chatlist_get_summary() then.
* dc_chatlist_get_summary() provides all elements needed for painting the item.
*
* On a click of such an item,
* the UI should change to the chat view
* and get all messages from this view via dc_get_chat_msgs().
* Again, a "virtual list" is created
* (the count of messages is known)
* and for each messages that is scrolled into view, dc_get_msg() is called then.
*
* Why no listflags?
* Without listflags, dc_get_chatlist() adds the deaddrop
* and the archive "link" automatically as needed.
* The UI can just render these items differently then.
* Although the deaddrop link is currently always the first entry
* and only present on new messages,
* there is the rough idea that it can be optionally always present
* and sorted into the list by date.
* Rendering the deaddrop in the described way
* would not add extra work in the UI then.
*/
pub unsafe fn dc_chatlist_new(context: &Context) -> *mut dc_chatlist_t {
let mut chatlist: *mut dc_chatlist_t;
chatlist = calloc(1, ::std::mem::size_of::<dc_chatlist_t>()) as *mut dc_chatlist_t;
assert!(!chatlist.is_null());
(*chatlist).magic = 0xc4a71157u32;
(*chatlist).context = context;
(*chatlist).chatNlastmsg_ids = dc_array_new(128i32 as size_t);
assert!(!(*chatlist).chatNlastmsg_ids.is_null());
chatlist
}
pub unsafe fn dc_chatlist_unref(mut chatlist: *mut dc_chatlist_t) {
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 {
return;
}
dc_chatlist_empty(chatlist);
dc_array_unref((*chatlist).chatNlastmsg_ids);
(*chatlist).magic = 0i32 as uint32_t;
free(chatlist as *mut libc::c_void);
}
pub unsafe fn dc_chatlist_empty(mut chatlist: *mut dc_chatlist_t) {
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 {
return;
}
(*chatlist).cnt = 0i32 as size_t;
dc_array_empty((*chatlist).chatNlastmsg_ids);
}
/**
* Load a chatlist from the database to the chatlist object.
*
* @private @memberof dc_chatlist_t
*/
// TODO should return bool /rtn
unsafe fn dc_chatlist_load_from_db(
mut chatlist: *mut dc_chatlist_t,
listflags: libc::c_int,
query__: *const libc::c_char,
query_contact_id: u32,
) -> libc::c_int {
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 {
return 0;
}
dc_chatlist_empty(chatlist);
let mut add_archived_link_item = 0;
// select with left join and minimum:
// - the inner select must use `hidden` and _not_ `m.hidden`
// which would refer the outer select and take a lot of time
// - `GROUP BY` is needed several messages may have the same timestamp
// - the list starts with the newest chats
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let process_row = |row: &rusqlite::Row| {
let chat_id: i32 = row.get(0)?;
// TODO: verify that it is okay for this to be Null
let msg_id: i32 = row.get(1).unwrap_or_default();
Ok((chat_id, msg_id))
};
let process_rows = |rows: rusqlite::MappedRows<_>| {
for row in rows {
let (id1, id2) = row?;
dc_array_add_id((*chatlist).chatNlastmsg_ids, id1 as u32);
dc_array_add_id((*chatlist).chatNlastmsg_ids, id2 as u32);
}
Ok(())
};
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let success = if query_contact_id != 0 {
// show chats shared with a given contact
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![query_contact_id as i32],
process_row,
process_rows,
)
} else if 0 != listflags & 0x1 {
// show archived chats
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=1 GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
)
} else if query__.is_null() {
// show normal chatlist
if 0 == listflags & 0x2 {
let last_deaddrop_fresh_msg_id = get_last_deaddrop_fresh_msg((*chatlist).context);
if last_deaddrop_fresh_msg_id > 0 {
dc_array_add_id((*chatlist).chatNlastmsg_ids, 1);
dc_array_add_id((*chatlist).chatNlastmsg_ids, last_deaddrop_fresh_msg_id);
}
add_archived_link_item = 1;
}
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c \
LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=0 \
GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
)
} else {
let query = to_string(query__).trim().to_string();
if query.is_empty() {
return 1;
} else {
let strLikeCmd = format!("%{}%", query);
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.name LIKE ? \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![strLikeCmd],
process_row,
process_rows,
)
}
};
if 0 != add_archived_link_item && dc_get_archived_cnt((*chatlist).context) > 0 {
if dc_array_get_cnt((*chatlist).chatNlastmsg_ids) == 0 && 0 != listflags & 0x4 {
dc_array_add_id((*chatlist).chatNlastmsg_ids, 7);
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0);
}
dc_array_add_id((*chatlist).chatNlastmsg_ids, 6);
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0);
}
(*chatlist).cnt = dc_array_get_cnt((*chatlist).chatNlastmsg_ids) / 2;
match success {
Ok(_) => 1,
Err(err) => {
error!(
(*chatlist).context,
0, "chatlist: failed to load from database: {:?}", err
);
0
}
}
}
// Context functions to work with chatlist
pub fn dc_get_archived_cnt(context: &Context) -> libc::c_int {
context
.sql
.query_row_col(
context,
"SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;",
params![],
0,
)
.unwrap_or_default()
}
fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 {
// we have an index over the state-column, this should be sufficient as there are typically only few fresh messages
context
.sql
.query_row_col(
context,
"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \
WHERE m.state=10 \
AND m.hidden=0 \
AND c.blocked=2 \
ORDER BY m.timestamp DESC, m.id DESC;",
params![],
0,
)
.unwrap_or_default()
}
pub unsafe fn dc_chatlist_get_cnt(chatlist: *const dc_chatlist_t) -> size_t {
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 {
return 0i32 as size_t;
}
(*chatlist).cnt
}
pub unsafe fn dc_chatlist_get_chat_id(chatlist: *const dc_chatlist_t, index: size_t) -> uint32_t {
if chatlist.is_null()
|| (*chatlist).magic != 0xc4a71157u32
|| (*chatlist).chatNlastmsg_ids.is_null()
|| index >= (*chatlist).cnt
{
return 0i32 as uint32_t;
}
dc_array_get_id((*chatlist).chatNlastmsg_ids, index.wrapping_mul(2))
}
pub unsafe fn dc_chatlist_get_msg_id(chatlist: *const dc_chatlist_t, index: size_t) -> uint32_t {
if chatlist.is_null()
|| (*chatlist).magic != 0xc4a71157u32
|| (*chatlist).chatNlastmsg_ids.is_null()
|| index >= (*chatlist).cnt
{
return 0i32 as uint32_t;
}
dc_array_get_id(
(*chatlist).chatNlastmsg_ids,
index.wrapping_mul(2).wrapping_add(1),
)
}
pub unsafe fn dc_chatlist_get_summary<'a>(
chatlist: *const dc_chatlist_t<'a>,
index: size_t,
mut chat: *mut Chat<'a>,
) -> *mut dc_lot_t {
let current_block: u64;
/* The summary is created by the chat, not by the last message.
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. */
/* the function never returns NULL */
let mut ret: *mut dc_lot_t = dc_lot_new();
let lastmsg_id: uint32_t;
let mut lastmsg: *mut dc_msg_t = 0 as *mut dc_msg_t;
let mut lastcontact: *mut dc_contact_t = 0 as *mut dc_contact_t;
let mut chat_to_delete: *mut Chat = 0 as *mut Chat;
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 || index >= (*chatlist).cnt {
(*ret).text2 = dc_strdup(b"ErrBadChatlistIndex\x00" as *const u8 as *const libc::c_char)
} else {
lastmsg_id = dc_array_get_id(
(*chatlist).chatNlastmsg_ids,
index.wrapping_mul(2).wrapping_add(1),
);
if chat.is_null() {
chat = dc_chat_new((*chatlist).context);
chat_to_delete = chat;
if !dc_chat_load_from_db(
chat,
dc_array_get_id((*chatlist).chatNlastmsg_ids, index.wrapping_mul(2)),
) {
(*ret).text2 =
dc_strdup(b"ErrCannotReadChat\x00" as *const u8 as *const libc::c_char);
current_block = 3777403817673069519;
} else {
current_block = 7651349459974463963;
}
} else {
current_block = 7651349459974463963;
}
match current_block {
3777403817673069519 => {}
_ => {
if 0 != lastmsg_id {
lastmsg = dc_msg_new_untyped((*chatlist).context);
dc_msg_load_from_db(lastmsg, (*chatlist).context, lastmsg_id);
if (*lastmsg).from_id != 1i32 as libc::c_uint
&& ((*chat).type_0 == 120i32 || (*chat).type_0 == 130i32)
{
lastcontact = dc_contact_new((*chatlist).context);
dc_contact_load_from_db(
lastcontact,
&(*chatlist).context.sql,
(*lastmsg).from_id,
);
}
}
if (*chat).id == 6i32 as libc::c_uint {
(*ret).text2 = dc_strdup(0 as *const libc::c_char)
} else if lastmsg.is_null() || (*lastmsg).from_id == 0i32 as libc::c_uint {
(*ret).text2 = dc_stock_str((*chatlist).context, 1i32)
} else {
dc_lot_fill(ret, lastmsg, chat, lastcontact, (*chatlist).context);
}
}
}
}
dc_msg_unref(lastmsg);
dc_contact_unref(lastcontact);
dc_chat_unref(chat_to_delete);
ret
}

View File

@@ -1,4 +1,4 @@
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use crate::constants::Event;
use crate::context::Context;
@@ -9,6 +9,7 @@ use crate::dc_saxparser::*;
use crate::dc_tools::*;
use crate::imap::*;
use crate::oauth2::*;
use crate::param::Params;
use crate::types::*;
use crate::x::*;
@@ -60,9 +61,10 @@ pub unsafe fn dc_configure(context: &Context) {
);
return;
}
dc_job_kill_action(context, 900i32);
dc_job_add(context, 900i32, 0i32, 0 as *const libc::c_char, 0i32);
dc_job_kill_action(context, 900);
dc_job_add(context, 900, 0, Params::new(), 0);
}
pub unsafe fn dc_has_ongoing(context: &Context) -> libc::c_int {
let s_a = context.running_state.clone();
let s = s_a.read().unwrap();
@@ -99,6 +101,7 @@ pub fn dc_stop_ongoing_process(context: &Context) {
}
// the other dc_job_do_DC_JOB_*() functions are declared static in the c-file
#[allow(non_snake_case)]
pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_job_t) {
let flags: libc::c_int;
let mut current_block: u64;
@@ -207,8 +210,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j
let parsed = parsed.unwrap();
let param_domain = parsed.host();
let param_addr_urlencoded =
utf8_percent_encode(&param.addr, DEFAULT_ENCODE_SET)
.to_string();
utf8_percent_encode(&param.addr, NON_ALPHANUMERIC).to_string();
if !s.shall_stop_ongoing {
context.call_cb(
@@ -570,13 +572,13 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j
{
param.send_pw = param.mail_pw.clone()
}
if 0 == dc_exactly_one_bit_set(
if !dc_exactly_one_bit_set(
param.server_flags & (0x2 | 0x4),
) {
param.server_flags &= !(0x2 | 0x4);
param.server_flags |= 0x4
}
if 0 == dc_exactly_one_bit_set(
if !dc_exactly_one_bit_set(
param.server_flags & (0x100 | 0x200 | 0x400),
) {
param.server_flags &= !(0x100 | 0x200 | 0x400);
@@ -586,7 +588,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j
0x200
}
}
if 0 == dc_exactly_one_bit_set(
if !dc_exactly_one_bit_set(
param.server_flags & (0x10000 | 0x20000 | 0x40000),
) {
param.server_flags &=
@@ -1100,14 +1102,14 @@ unsafe fn moz_autoconfigure(
tag_config: 0,
};
let url_c = to_cstring(url);
let url_c = url.strdup();
let xml_raw = read_autoconf_file(context, url_c);
free(url_c as *mut libc::c_void);
if xml_raw.is_null() {
return None;
}
moz_ac.in_emaillocalpart = to_cstring(&param_in.addr);
moz_ac.in_emaillocalpart = param_in.addr.strdup();
let p = strchr(moz_ac.in_emaillocalpart, '@' as i32);
if p.is_null() {
@@ -1164,7 +1166,7 @@ unsafe fn moz_autoconfigure_text_cb(
let mut moz_ac: *mut moz_autoconfigure_t = userdata as *mut moz_autoconfigure_t;
let mut val: *mut libc::c_char = dc_strdup(text);
dc_trim(val);
let addr = to_cstring(&(*moz_ac).in_0.addr);
let addr = (*moz_ac).in_0.addr.strdup();
dc_str_replace(
&mut val,
b"%EMAILADDRESS%\x00" as *const u8 as *const libc::c_char,
@@ -1304,7 +1306,7 @@ fn read_autoconf_file(context: &Context, url: *const libc::c_char) -> *mut libc:
.send()
.and_then(|mut res| res.text())
{
Ok(res) => unsafe { to_cstring(res) },
Ok(res) => unsafe { res.strdup() },
Err(_err) => {
info!(context, 0, "Can\'t read file.",);
@@ -1320,7 +1322,7 @@ unsafe fn outlk_autodiscover(
) -> Option<dc_loginparam_t> {
let current_block: u64;
let mut xml_raw: *mut libc::c_char = 0 as *mut libc::c_char;
let mut url = to_cstring(url__);
let mut url = url__.strdup();
let mut outlk_ad = outlk_autodiscover_t {
in_0: param_in,
out: dc_loginparam_new(),

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,7 @@ lazy_static! {
struct Dehtml {
strbuilder: String,
add_text: AddText,
last_href: *mut libc::c_char,
last_href: Option<String>,
}
#[derive(Debug, PartialEq)]
@@ -32,7 +32,7 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char
let mut dehtml = Dehtml {
strbuilder: String::with_capacity(strlen(buf_terminated)),
add_text: AddText::YesRemoveLineEnds,
last_href: 0 as *mut libc::c_char,
last_href: None,
};
let mut saxparser = dc_saxparser_t {
starttag_cb: None,
@@ -51,9 +51,8 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char
);
dc_saxparser_set_text_handler(&mut saxparser, Some(dehtml_text_cb));
dc_saxparser_parse(&mut saxparser, buf_terminated);
free(dehtml.last_href as *mut libc::c_void);
to_cstring(dehtml.strbuilder)
dehtml.strbuilder.strdup()
}
unsafe fn dehtml_text_cb(
@@ -66,7 +65,11 @@ unsafe fn dehtml_text_cb(
if dehtml.add_text == AddText::YesPreserveLineEnds
|| dehtml.add_text == AddText::YesRemoveLineEnds
{
let last_added = std::ffi::CStr::from_ptr(text).to_string_lossy();
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);
if dehtml.add_text == AddText::YesRemoveLineEnds {
dehtml.strbuilder += LINE_RE.replace_all(last_added.as_ref(), "\r").as_ref();
@@ -86,14 +89,10 @@ unsafe fn dehtml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char
dehtml.add_text = AddText::YesRemoveLineEnds;
}
"a" => {
if !dehtml.last_href.is_null() {
if let Some(ref last_href) = dehtml.last_href.take() {
dehtml.strbuilder += "](";
dehtml.strbuilder += std::ffi::CStr::from_ptr((*dehtml).last_href)
.to_string_lossy()
.as_ref();
dehtml.strbuilder += last_href;
dehtml.strbuilder += ")";
free(dehtml.last_href as *mut libc::c_void);
dehtml.last_href = 0 as *mut libc::c_char;
}
}
"b" | "strong" => {
@@ -131,12 +130,13 @@ unsafe fn dehtml_starttag_cb(
dehtml.add_text = AddText::YesPreserveLineEnds;
}
"a" => {
free(dehtml.last_href as *mut libc::c_void);
dehtml.last_href = dc_strdup_keep_null(dc_attr_find(
let text_c = std::ffi::CStr::from_ptr(dc_attr_find(
attr,
b"href\x00" as *const u8 as *const libc::c_char,
));
if !dehtml.last_href.is_null() {
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 += "[";
}
}

View File

@@ -34,6 +34,7 @@ use crate::x::*;
// to get the netto sizes, we subtract 1 mb header-overhead and the base64-overhead.
// some defaults
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub struct dc_e2ee_helper_t {
pub encryption_successfull: libc::c_int,
pub cdata_to_free: *mut libc::c_void,
@@ -54,6 +55,7 @@ impl Default for dc_e2ee_helper_t {
}
}
#[allow(non_snake_case)]
pub unsafe fn dc_e2ee_encrypt(
context: &Context,
recipients_addr: *const clist,
@@ -64,7 +66,7 @@ pub unsafe fn dc_e2ee_encrypt(
mut in_out_message: *mut mailmime,
helper: &mut dc_e2ee_helper_t,
) {
let mut current_block: u64 = 0;
let mut ok_to_continue = true;
let mut col: libc::c_int = 0i32;
let mut do_encrypt: libc::c_int = 0i32;
/*just a pointer into mailmime structure, must not be freed*/
@@ -175,16 +177,12 @@ pub unsafe fn dc_e2ee_encrypt(
let p = peerstates[i as usize]
.render_gossip_header(min_verified as usize);
if p.is_some() {
let header = to_cstring(p.unwrap());
if let Some(header) = p {
mailimf_fields_add(
imffields_encrypted,
mailimf_field_new_custom(
strdup(
b"Autocrypt-Gossip\x00" as *const u8
as *const libc::c_char,
),
header,
"Autocrypt-Gossip".strdup(),
header.strdup(),
),
);
}
@@ -285,7 +283,7 @@ pub unsafe fn dc_e2ee_encrypt(
);
mailmime_write_mem(plain, &mut col, message_to_encrypt);
if (*plain).str_0.is_null() || (*plain).len <= 0 {
current_block = 14181132614457621749;
ok_to_continue = false;
} else {
if let Some(ctext_v) = dc_pgp_pk_encrypt(
(*plain).str_0 as *const libc::c_void,
@@ -294,8 +292,8 @@ pub unsafe fn dc_e2ee_encrypt(
sign_key.as_ref(),
) {
let ctext_bytes = ctext_v.len();
let ctext = to_cstring(ctext_v);
(*helper).cdata_to_free = ctext as *mut _;
let ctext = ctext_v.strdup();
helper.cdata_to_free = ctext as *mut _;
/* create MIME-structure that will contain the encrypted text */
let mut encrypted_part: *mut mailmime = new_data_part(
@@ -318,11 +316,11 @@ pub unsafe fn dc_e2ee_encrypt(
as *mut libc::c_char,
) as *mut libc::c_void,
);
static mut version_content: [libc::c_char; 13] =
static mut VERSION_CONTENT: [libc::c_char; 13] =
[86, 101, 114, 115, 105, 111, 110, 58, 32, 49, 13, 10, 0];
let version_mime: *mut mailmime = new_data_part(
version_content.as_mut_ptr() as *mut libc::c_void,
strlen(version_content.as_mut_ptr()),
VERSION_CONTENT.as_mut_ptr() as *mut libc::c_void,
strlen(VERSION_CONTENT.as_mut_ptr()),
b"application/pgp-encrypted\x00" as *const u8
as *const libc::c_char
as *mut libc::c_char,
@@ -341,27 +339,19 @@ pub unsafe fn dc_e2ee_encrypt(
(*in_out_message).mm_data.mm_message.mm_msg_mime = encrypted_part;
(*encrypted_part).mm_parent = in_out_message;
mailmime_free(message_to_encrypt);
(*helper).encryption_successfull = 1i32;
current_block = 13824533195664196414;
helper.encryption_successfull = 1i32;
}
}
} else {
current_block = 13824533195664196414;
}
match current_block {
14181132614457621749 => {}
_ => {
let aheader = Aheader::new(addr, public_key, prefer_encrypt);
let rendered = to_cstring(aheader.to_string());
mailimf_fields_add(
imffields_unprotected,
mailimf_field_new_custom(
strdup(b"Autocrypt\x00" as *const u8 as *const libc::c_char),
rendered,
),
);
}
if ok_to_continue {
let aheader = Aheader::new(addr, public_key, prefer_encrypt);
mailimf_fields_add(
imffields_unprotected,
mailimf_field_new_custom(
"Autocrypt".strdup(),
aheader.to_string().strdup(),
),
);
}
}
}
@@ -382,7 +372,7 @@ unsafe fn new_data_part(
default_content_type: *mut libc::c_char,
default_encoding: libc::c_int,
) -> *mut mailmime {
let mut current_block: u64;
let mut ok_to_continue = true;
//char basename_buf[PATH_MAX];
let mut encoding: *mut mailmime_mechanism;
let content: *mut mailmime_content;
@@ -402,7 +392,7 @@ unsafe fn new_data_part(
}
content = mailmime_content_new_with_str(content_type_str);
if content.is_null() {
current_block = 16266721588079097885;
ok_to_continue = false;
} else {
do_encoding = 1i32;
if (*(*content).ct_type).tp_type == MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int {
@@ -430,54 +420,44 @@ unsafe fn new_data_part(
}
encoding = mailmime_mechanism_new(encoding_type, 0 as *mut libc::c_char);
if encoding.is_null() {
current_block = 16266721588079097885;
} else {
current_block = 11057878835866523405;
ok_to_continue = false;
}
} else {
current_block = 11057878835866523405;
}
match current_block {
16266721588079097885 => {}
_ => {
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,
);
if mime_fields.is_null() {
current_block = 16266721588079097885;
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,
);
if mime_fields.is_null() {
ok_to_continue = false;
} else {
mime = mailmime_new_empty(content, mime_fields);
if mime.is_null() {
mailmime_fields_free(mime_fields);
mailmime_content_free(content);
} else {
mime = mailmime_new_empty(content, mime_fields);
if mime.is_null() {
mailmime_fields_free(mime_fields);
mailmime_content_free(content);
} else {
if !data.is_null()
&& data_bytes > 0
&& (*mime).mm_type == MAILMIME_SINGLE as libc::c_int
{
mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes);
}
return mime;
if !data.is_null()
&& data_bytes > 0
&& (*mime).mm_type == MAILMIME_SINGLE as libc::c_int
{
mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes);
}
current_block = 13668317689588454213;
return mime;
}
}
}
}
match current_block {
16266721588079097885 => {
if !encoding.is_null() {
mailmime_mechanism_free(encoding);
}
if !content.is_null() {
mailmime_content_free(content);
}
if ok_to_continue == false {
if !encoding.is_null() {
mailmime_mechanism_free(encoding);
}
if !content.is_null() {
mailmime_content_free(content);
}
_ => {}
}
return 0 as *mut mailmime;
}
@@ -491,7 +471,7 @@ unsafe fn load_or_generate_self_public_key(
_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;
static mut S_IN_KEY_CREATION: libc::c_int = 0;
let mut key = Key::from_self_public(context, &self_addr, &context.sql);
if key.is_some() {
@@ -499,11 +479,11 @@ unsafe fn load_or_generate_self_public_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 {
if 0 != S_IN_KEY_CREATION {
return None;
}
let key_creation_here = 1;
s_in_key_creation = 1;
S_IN_KEY_CREATION = 1;
let start = clock();
info!(
@@ -537,7 +517,7 @@ unsafe fn load_or_generate_self_public_key(
}
if 0 != key_creation_here {
s_in_key_creation = 0;
S_IN_KEY_CREATION = 0;
}
key
@@ -832,14 +812,14 @@ unsafe fn decrypt_recursive(
}
unsafe fn decrypt_part(
_context: &Context,
context: &Context,
mime: *mut mailmime,
private_keyring: &Keyring,
public_keyring_for_validate: &Keyring,
ret_valid_signatures: &mut HashSet<String>,
ret_decrypted_mime: *mut *mut mailmime,
) -> libc::c_int {
let current_block: u64;
let mut ok_to_continue = true;
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 */
@@ -887,9 +867,7 @@ unsafe fn decrypt_part(
decoded_data_bytes = (*mime_data).dt_data.dt_text.dt_length;
if decoded_data.is_null() || decoded_data_bytes <= 0 {
/* no error - but no data */
current_block = 2554982661806928548;
} else {
current_block = 4488286894823169796;
ok_to_continue = false;
}
} else {
let r: libc::c_int;
@@ -906,53 +884,49 @@ unsafe fn decrypt_part(
|| transfer_decoding_buffer.is_null()
|| decoded_data_bytes <= 0
{
current_block = 2554982661806928548;
ok_to_continue = false;
} else {
decoded_data = transfer_decoding_buffer;
current_block = 4488286894823169796;
}
}
match current_block {
2554982661806928548 => {}
_ => {
/* encrypted, decoded data in decoded_data now ... */
if !(0 == has_decrypted_pgp_armor(decoded_data, decoded_data_bytes as libc::c_int))
{
let add_signatures = if ret_valid_signatures.is_empty() {
Some(ret_valid_signatures)
} else {
None
};
if ok_to_continue {
/* encrypted, decoded data in decoded_data now ... */
if !(0 == has_decrypted_pgp_armor(decoded_data, decoded_data_bytes as libc::c_int)) {
let add_signatures = if ret_valid_signatures.is_empty() {
Some(ret_valid_signatures)
} else {
None
};
/*if we already have fingerprints, do not add more; this ensures, only the fingerprints from the outer-most part are collected */
if let Some(plain) = dc_pgp_pk_decrypt(
decoded_data as *const libc::c_void,
decoded_data_bytes,
&private_keyring,
&public_keyring_for_validate,
add_signatures,
) {
let plain_bytes = plain.len();
let plain_buf = plain.as_ptr() as *const libc::c_char;
/*if we already have fingerprints, do not add more; this ensures, only the fingerprints from the outer-most part are collected */
if let Some(plain) = dc_pgp_pk_decrypt(
decoded_data as *const libc::c_void,
decoded_data_bytes,
&private_keyring,
&public_keyring_for_validate,
add_signatures,
) {
let plain_bytes = plain.len();
let plain_buf = plain.as_ptr() as *const libc::c_char;
let mut index: size_t = 0i32 as size_t;
let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime;
if mailmime_parse(
plain_buf as *const _,
plain_bytes,
&mut index,
&mut decrypted_mime,
) != MAIL_NO_ERROR as libc::c_int
|| decrypted_mime.is_null()
{
if !decrypted_mime.is_null() {
mailmime_free(decrypted_mime);
}
} else {
*ret_decrypted_mime = decrypted_mime;
sth_decrypted = 1i32
let mut index: size_t = 0i32 as size_t;
let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime;
if mailmime_parse(
plain_buf as *const _,
plain_bytes,
&mut index,
&mut decrypted_mime,
) != MAIL_NO_ERROR as libc::c_int
|| decrypted_mime.is_null()
{
if !decrypted_mime.is_null() {
mailmime_free(decrypted_mime);
}
} else {
*ret_decrypted_mime = decrypted_mime;
sth_decrypted = 1i32
}
std::mem::forget(plain);
}
}
}
@@ -1082,3 +1056,69 @@ pub unsafe fn dc_ensure_secret_key_exists(context: &Context) -> libc::c_int {
success
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mailmime_parse() {
let plain = b"Chat-Disposition-Notification-To: holger@deltachat.de
Chat-Group-ID: CovhGgau8M-
Chat-Group-Name: Delta Chat Dev
Subject: =?utf-8?Q?Chat=3A?= Delta Chat =?utf-8?Q?Dev=3A?= sidenote for
=?utf-8?Q?all=3A?= rust core master ...
Content-Type: text/plain; charset=\"utf-8\"; protected-headers=\"v1\"
Content-Transfer-Encoding: quoted-printable
sidenote for all: rust core master is broken currently ... so dont recomm=
end to try to run with desktop or ios unless you are ready to hunt bugs
-- =20
Sent with my Delta Chat Messenger: https://delta.chat";
let plain_bytes = plain.len();
let plain_buf = plain.as_ptr() as *const libc::c_char;
let mut index = 0;
let mut decrypted_mime = std::ptr::null_mut();
let res = unsafe {
mailmime_parse(
plain_buf as *const _,
plain_bytes,
&mut index,
&mut decrypted_mime,
)
};
unsafe {
let msg1 = (*decrypted_mime).mm_data.mm_message.mm_msg_mime;
let mut decoded_data = 0 as *const libc::c_char;
let mut decoded_data_bytes = 0;
let mut transfer_decoding_buffer: *mut libc::c_char = 0 as *mut libc::c_char;
assert_eq!(
mailmime_transfer_decode(
msg1,
&mut decoded_data,
&mut decoded_data_bytes,
&mut transfer_decoding_buffer,
),
1
);
println!(
"{:?}",
String::from_utf8_lossy(std::slice::from_raw_parts(
decoded_data as *const u8,
decoded_data_bytes as usize,
))
);
free(decoded_data as *mut _);
}
assert_eq!(res, 0);
assert!(!decrypted_mime.is_null());
unsafe { free(decrypted_mime as *mut _) };
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ use std::time::Duration;
use rand::{thread_rng, Rng};
use crate::constants::Event;
use crate::constants::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_configure::*;
@@ -15,14 +15,16 @@ use crate::dc_location::*;
use crate::dc_loginparam::*;
use crate::dc_mimefactory::*;
use crate::dc_msg::*;
use crate::dc_param::*;
use crate::dc_tools::*;
use crate::imap::*;
use crate::keyhistory::*;
use crate::param::*;
use crate::sql;
use crate::types::*;
use crate::x::*;
const DC_IMAP_THREAD: libc::c_int = 100;
const DC_SMTP_THREAD: libc::c_int = 5000;
// thread IDs
// jobs in the INBOX-thread, range from DC_IMAP_THREAD..DC_IMAP_THREAD+999
// low priority ...
@@ -33,7 +35,7 @@ use crate::x::*;
// timeouts until actions are aborted.
// this may also affects IDLE to return, so a re-connect may take this time.
// mailcore2 uses 30 seconds, k-9 uses 10 seconds
#[derive(Copy, Clone)]
#[derive(Clone)]
#[repr(C)]
pub struct dc_job_t {
pub job_id: uint32_t,
@@ -42,7 +44,7 @@ pub struct dc_job_t {
pub desired_timestamp: i64,
pub added_timestamp: i64,
pub tries: libc::c_int,
pub param: *mut dc_param_t,
pub param: Params,
pub try_again: libc::c_int,
pub pending_error: *mut libc::c_char,
}
@@ -51,15 +53,15 @@ pub unsafe fn dc_perform_imap_jobs(context: &Context) {
info!(context, 0, "dc_perform_imap_jobs starting.",);
let probe_imap_network = *context.probe_imap_network.clone().read().unwrap();
*context.probe_imap_network.write().unwrap() = 0;
*context.perform_inbox_jobs_needed.write().unwrap() = 0;
*context.probe_imap_network.write().unwrap() = false;
*context.perform_inbox_jobs_needed.write().unwrap() = false;
dc_job_perform(context, 100, probe_imap_network);
dc_job_perform(context, DC_IMAP_THREAD, probe_imap_network);
info!(context, 0, "dc_perform_imap_jobs ended.",);
}
unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: libc::c_int) {
let query = if probe_network == 0 {
unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: bool) {
let query = if !probe_network {
// processing for first-try and after backoff-timeouts:
// process jobs in the order they were added.
"SELECT id, action, foreign_id, param, added_timestamp, desired_timestamp, tries \
@@ -74,7 +76,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
let params_no_probe = params![thread as i64, time()];
let params_probe = params![thread as i64];
let params: &[&dyn rusqlite::ToSql] = if probe_network == 0 {
let params: &[&dyn rusqlite::ToSql] = if !probe_network {
params_no_probe
} else {
params_probe
@@ -91,15 +93,11 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
desired_timestamp: row.get(5)?,
added_timestamp: row.get(4)?,
tries: row.get(6)?,
param: dc_param_new(),
param: row.get::<_, String>(3)?.parse().unwrap_or_default(),
try_again: 0,
pending_error: 0 as *mut libc::c_char,
};
let packed: String = row.get(3)?;
let packed_c = to_cstring(packed);
dc_param_set_packed(job.param, packed_c);
free(packed_c as *mut _);
Ok(job)
},
|jobs| {
@@ -120,7 +118,11 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
context,
0,
"{}-job #{}, action {} started...",
if thread == 100 { "INBOX" } else { "SMTP" },
if thread == DC_IMAP_THREAD {
"INBOX"
} else {
"SMTP"
},
job.job_id,
job.action,
);
@@ -133,7 +135,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
dc_job_kill_action(context, job.action);
dc_jobthread_suspend(context, &context.sentbox_thread.clone().read().unwrap(), 1);
dc_jobthread_suspend(context, &context.mvbox_thread.clone().read().unwrap(), 1);
dc_suspend_smtp_thread(context, 1);
dc_suspend_smtp_thread(context, true);
}
let mut tries = 0;
@@ -171,7 +173,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
&mut context.mvbox_thread.clone().read().unwrap(),
0,
);
dc_suspend_smtp_thread(context, 0);
dc_suspend_smtp_thread(context, false);
break;
} else if job.try_again == 2 {
// just try over next loop unconditionally, the ui typically interrupts idle when the file (video) is ready
@@ -179,7 +181,11 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
context,
0,
"{}-job #{} not yet ready and will be delayed.",
if thread == 100 { "INBOX" } else { "SMTP" },
if thread == DC_IMAP_THREAD {
"INBOX"
} else {
"SMTP"
},
job.job_id
);
} else if job.try_again == -1 || job.try_again == 3 {
@@ -193,13 +199,17 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
context,
0,
"{}-job #{} not succeeded on try #{}, retry in ADD_TIME+{} (in {} seconds).",
if thread == 100 { "INBOX" } else { "SMTP" },
if thread == DC_IMAP_THREAD {
"INBOX"
} else {
"SMTP"
},
job.job_id as libc::c_int,
tries,
time_offset,
job.added_timestamp + time_offset - time()
);
if thread == 5000 && tries < 17 - 1 {
if thread == DC_SMTP_THREAD && tries < 17 - 1 {
context
.smtp_state
.clone()
@@ -214,7 +224,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
}
dc_job_delete(context, &mut job);
}
if 0 == probe_network {
if !probe_network {
continue;
}
// on dc_maybe_network() we stop trying here;
@@ -225,7 +235,6 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
} else {
dc_job_delete(context, &mut job);
}
dc_param_unref(job.param);
free(job.pending_error as *mut libc::c_void);
}
}
@@ -240,7 +249,8 @@ fn dc_job_delete(context: &Context, job: &dc_job_t) -> bool {
/* ******************************************************************************
* Tools
******************************************************************************/
unsafe fn get_backoff_time_offset(c_tries: libc::c_int) -> i64 {
#[allow(non_snake_case)]
fn get_backoff_time_offset(c_tries: libc::c_int) -> i64 {
// results in ~3 weeks for the last backoff timespan
let mut N = 2_i32.pow((c_tries - 1) as u32);
N = N * 60;
@@ -261,30 +271,31 @@ fn dc_job_update(context: &Context, job: &dc_job_t) -> bool {
params![
job.desired_timestamp,
job.tries as i64,
as_str(unsafe { (*job.param).packed }),
job.param.to_string(),
job.job_id as i32,
],
)
.is_ok()
}
unsafe fn dc_suspend_smtp_thread(context: &Context, suspend: libc::c_int) {
unsafe fn dc_suspend_smtp_thread(context: &Context, suspend: bool) {
context.smtp_state.0.lock().unwrap().suspended = suspend;
if 0 != suspend {
if suspend {
loop {
if context.smtp_state.0.lock().unwrap().doing_jobs == 0 {
if !context.smtp_state.0.lock().unwrap().doing_jobs {
return;
}
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
}
}
}
#[allow(non_snake_case)]
unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) {
let mut current_block: u64;
let mut filename: *mut libc::c_char = 0 as *mut libc::c_char;
let mut buf: *mut libc::c_void = 0 as *mut libc::c_void;
let mut buf_bytes: size_t = 0i32 as size_t;
let mut recipients: *mut libc::c_char = 0 as *mut libc::c_char;
/* connect to SMTP server, if not yet done */
if !context.smtp.lock().unwrap().is_connected() {
@@ -302,20 +313,15 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) {
}
match current_block {
13109137661213826276 => {
filename = dc_param_get(job.param, DC_PARAM_FILE as i32, 0 as *const libc::c_char);
if filename.is_null() {
filename = job.param.get(Param::File).unwrap_or_default().strdup();
if strlen(filename) == 0 {
warn!(context, 0, "Missing file name for job {}", job.job_id,);
} else if !(0 == dc_read_file(context, filename, &mut buf, &mut buf_bytes)) {
recipients = dc_param_get(
job.param,
DC_PARAM_RECIPIENTS as i32,
0 as *const libc::c_char,
);
if recipients.is_null() {
let recipients = job.param.get(Param::Recipients);
if recipients.is_none() {
warn!(context, 0, "Missing recipients for job {}", job.job_id,);
} else {
let recipients_list = std::ffi::CStr::from_ptr(recipients)
.to_str()
let recipients_list = recipients
.unwrap()
.split("\x1e")
.filter_map(|addr| match lettre::EmailAddress::new(addr.to_string()) {
@@ -365,7 +371,11 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) {
} else {
dc_delete_file(context, filename);
if 0 != job.foreign_id {
dc_update_msg_state(context, job.foreign_id, 26i32);
dc_update_msg_state(
context,
job.foreign_id,
DC_STATE_OUT_DELIVERED,
);
let chat_id: i32 = context
.sql
.query_row_col(
@@ -389,7 +399,6 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) {
}
_ => {}
}
free(recipients as *mut libc::c_void);
free(buf);
free(filename as *mut libc::c_void);
}
@@ -405,6 +414,7 @@ pub unsafe fn dc_job_try_again_later(
job.pending_error = dc_strdup_keep_null(pending_error);
}
#[allow(non_snake_case)]
unsafe fn dc_job_do_DC_JOB_MOVE_MSG(context: &Context, job: &mut dc_job_t) {
let mut current_block: u64;
let msg = dc_msg_new_untyped(context);
@@ -501,21 +511,22 @@ fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int {
ret_connected
}
#[allow(non_snake_case)]
unsafe fn dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context: &Context, job: &mut dc_job_t) {
let current_block: u64;
let folder: *mut libc::c_char = dc_param_get(
job.param,
DC_PARAM_SERVER_FOLDER as i32,
0 as *const libc::c_char,
);
let uid: uint32_t = dc_param_get_int(job.param, DC_PARAM_SERVER_UID as i32, 0) as uint32_t;
let mut dest_uid: uint32_t = 0i32 as uint32_t;
let folder = job
.param
.get(Param::ServerFolder)
.unwrap_or_default()
.to_string();
let uid = job.param.get_int(Param::ServerUid).unwrap_or_default() as u32;
let mut dest_uid = 0;
let inbox = context.inbox.read().unwrap();
if !inbox.is_connected() {
connect_to_inbox(context, &inbox);
if !inbox.is_connected() {
dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char);
dc_job_try_again_later(job, 3, 0 as *const libc::c_char);
current_block = 2670689566614003383;
} else {
current_block = 11006700562992250127;
@@ -525,11 +536,10 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context: &Context, job: &mut dc_
}
match current_block {
11006700562992250127 => {
let folder = CStr::from_ptr(folder).to_str().unwrap();
if inbox.set_seen(context, folder, uid) as libc::c_uint == 0i32 as libc::c_uint {
if inbox.set_seen(context, &folder, uid) == 0 {
dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char);
}
if 0 != dc_param_get_int(job.param, DC_PARAM_ALSO_MOVE as i32, 0i32) {
if 0 != job.param.get_int(Param::AlsoMove).unwrap_or_default() {
if context
.sql
.get_config_int(context, "folders_configured")
@@ -550,9 +560,9 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context: &Context, job: &mut dc_
}
_ => {}
}
free(folder as *mut libc::c_void);
}
#[allow(non_snake_case)]
unsafe fn dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context: &Context, job: &mut dc_job_t) {
let mut current_block: u64;
let msg: *mut dc_msg_t = dc_msg_new_untyped(context);
@@ -582,12 +592,8 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context: &Context, job: &mut dc_
dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char);
}
_ => {
if 0 != dc_param_get_int(
(*msg).param,
DC_PARAM_WANTS_MDN as i32,
0i32,
) && 0
!= context
if 0 != (*msg).param.get_int(Param::WantsMdn).unwrap_or_default()
&& 0 != context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1)
@@ -640,7 +646,7 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context: &Context, job: &mut dc_
dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char);
}
_ => {
if 0 != dc_param_get_int((*msg).param, DC_PARAM_WANTS_MDN as i32, 0)
if 0 != (*msg).param.get_int(Param::WantsMdn).unwrap_or_default()
&& 0 != context
.sql
.get_config_int(context, "mdns_enabled")
@@ -737,6 +743,7 @@ unsafe fn dc_send_mdn(context: &Context, msg_id: uint32_t) {
* @param mimefactory An instance of dc_mimefactory_t with a loaded and rendered message or MDN
* @return 1=success, 0=error
*/
#[allow(non_snake_case)]
unsafe fn dc_add_smtp_job(
context: &Context,
action: libc::c_int,
@@ -745,7 +752,7 @@ unsafe fn dc_add_smtp_job(
let pathNfilename: *mut libc::c_char;
let mut success: libc::c_int = 0i32;
let mut recipients: *mut libc::c_char = 0 as *mut libc::c_char;
let param: *mut dc_param_t = dc_param_new();
let mut param = Params::new();
pathNfilename = dc_get_fine_pathNfilename(
context,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
@@ -778,8 +785,8 @@ unsafe fn dc_add_smtp_job(
(*mimefactory).recipients_addr,
b"\x1e\x00" as *const u8 as *const libc::c_char,
);
dc_param_set(param, DC_PARAM_FILE as i32, pathNfilename);
dc_param_set(param, DC_PARAM_RECIPIENTS as i32, recipients);
param.set(Param::File, as_str(pathNfilename));
param.set(Param::Recipients, as_str(recipients));
dc_job_add(
context,
action,
@@ -788,30 +795,30 @@ unsafe fn dc_add_smtp_job(
{
(*(*mimefactory).msg).id
} else {
0i32 as libc::c_uint
0
}) as libc::c_int,
(*param).packed,
0i32,
param,
0,
);
success = 1i32
}
dc_param_unref(param);
free(recipients as *mut libc::c_void);
free(pathNfilename as *mut libc::c_void);
return success;
}
pub unsafe fn dc_job_add(
context: &Context,
action: libc::c_int,
foreign_id: libc::c_int,
param: *const libc::c_char,
param: Params,
delay_seconds: libc::c_int,
) {
let timestamp = time();
let thread = if action >= 100 && action < 100 + 1000 {
100
} else if action >= 5000 && action < 5000 + 1000 {
5000
let thread = if action >= DC_IMAP_THREAD && action < DC_IMAP_THREAD + 1000 {
DC_IMAP_THREAD
} else if action >= DC_SMTP_THREAD && action < DC_SMTP_THREAD + 1000 {
DC_SMTP_THREAD
} else {
return;
};
@@ -825,16 +832,12 @@ pub unsafe fn dc_job_add(
thread,
action,
foreign_id,
if !param.is_null() {
as_str(param)
} else {
""
},
param.to_string(),
(timestamp + delay_seconds as i64)
]
).ok();
if thread == 100 {
if thread == DC_IMAP_THREAD {
dc_interrupt_imap_idle(context);
} else {
dc_interrupt_smtp_idle(context);
@@ -855,10 +858,11 @@ pub unsafe fn dc_interrupt_smtp_idle(context: &Context) {
pub unsafe fn dc_interrupt_imap_idle(context: &Context) {
info!(context, 0, "Interrupting IMAP-IDLE...",);
*context.perform_inbox_jobs_needed.write().unwrap() = 1;
*context.perform_inbox_jobs_needed.write().unwrap() = true;
context.inbox.read().unwrap().interrupt_idle();
}
#[allow(non_snake_case)]
unsafe fn dc_job_do_DC_JOB_DELETE_MSG_ON_IMAP(context: &Context, job: &mut dc_job_t) {
let mut current_block: u64;
let mut delete_from_server: libc::c_int = 1i32;
@@ -962,7 +966,7 @@ pub fn dc_perform_imap_idle(context: &Context) {
connect_to_inbox(context, &inbox);
if 0 != *context.perform_inbox_jobs_needed.clone().read().unwrap() {
if *context.perform_inbox_jobs_needed.clone().read().unwrap() {
info!(
context,
0, "INBOX-IDLE will not be started because of waiting jobs."
@@ -1037,26 +1041,26 @@ pub unsafe fn dc_perform_smtp_jobs(context: &Context) {
let mut state = lock.lock().unwrap();
let probe_smtp_network = state.probe_network;
state.probe_network = 0;
state.probe_network = false;
state.perform_jobs_needed = 0;
if 0 != state.suspended {
if state.suspended {
info!(context, 0, "SMTP-jobs suspended.",);
return;
}
state.doing_jobs = 1;
state.doing_jobs = true;
probe_smtp_network
};
info!(context, 0, "SMTP-jobs started...",);
dc_job_perform(context, 5000, probe_smtp_network);
dc_job_perform(context, DC_SMTP_THREAD, probe_smtp_network);
info!(context, 0, "SMTP-jobs ended.");
{
let &(ref lock, _) = &*context.smtp_state.clone();
let mut state = lock.lock().unwrap();
state.doing_jobs = 0;
state.doing_jobs = false;
}
}
@@ -1072,7 +1076,7 @@ pub unsafe fn dc_perform_smtp_idle(context: &Context) {
0, "SMTP-idle will not be started because of waiting jobs.",
);
} else {
let dur = get_next_wakeup_time(context, 5000);
let dur = get_next_wakeup_time(context, DC_SMTP_THREAD);
loop {
let res = cvar.wait_timeout(state, dur).unwrap();
@@ -1118,9 +1122,9 @@ pub unsafe fn dc_maybe_network(context: &Context) {
{
let &(ref lock, _) = &*context.smtp_state.clone();
let mut state = lock.lock().unwrap();
state.probe_network = 1;
state.probe_network = true;
*context.probe_imap_network.write().unwrap() = 1;
*context.probe_imap_network.write().unwrap() = true;
}
dc_interrupt_smtp_idle(context);
@@ -1137,6 +1141,7 @@ pub fn dc_job_action_exists(context: &Context, action: libc::c_int) -> bool {
}
/* special case for DC_JOB_SEND_MSG_TO_SMTP */
#[allow(non_snake_case)]
pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
let mut success = 0;
let mut mimefactory = dc_mimefactory_t {
@@ -1170,28 +1175,23 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in
);
} else {
// no redo, no IMAP. moreover, as the data does not exist, there is no need in calling dc_set_msg_failed()
if (*mimefactory.msg).type_0 == 20i32
|| (*mimefactory.msg).type_0 == 21i32
|| (*mimefactory.msg).type_0 == 40i32
|| (*mimefactory.msg).type_0 == 41i32
|| (*mimefactory.msg).type_0 == 50i32
|| (*mimefactory.msg).type_0 == 60i32
{
let pathNfilename = dc_param_get(
(*mimefactory.msg).param,
DC_PARAM_FILE as i32,
0 as *const libc::c_char,
);
if !pathNfilename.is_null() {
if ((*mimefactory.msg).type_0 == 20i32 || (*mimefactory.msg).type_0 == 21i32)
&& 0 == dc_param_exists((*mimefactory.msg).param, DC_PARAM_WIDTH as i32)
if msgtype_has_file((*mimefactory.msg).type_0) {
let pathNfilename = (*mimefactory.msg)
.param
.get(Param::File)
.unwrap_or_default()
.strdup();
if strlen(pathNfilename) > 0 {
if ((*mimefactory.msg).type_0 == Viewtype::Image
|| (*mimefactory.msg).type_0 == Viewtype::Gif)
&& !(*mimefactory.msg).param.exists(Param::Width)
{
let mut buf: *mut libc::c_uchar = 0 as *mut libc::c_uchar;
let mut buf = 0 as *mut libc::c_uchar;
let mut buf_bytes: size_t = 0;
let mut w: uint32_t = 0;
let mut h: uint32_t = 0;
dc_param_set_int((*mimefactory.msg).param, DC_PARAM_WIDTH as i32, 0);
dc_param_set_int((*mimefactory.msg).param, DC_PARAM_HEIGHT as i32, 0);
let mut w = 0;
let mut h = 0;
(*mimefactory.msg).param.set_int(Param::Width, 0);
(*mimefactory.msg).param.set_int(Param::Height, 0);
if 0 != dc_read_file(
context,
pathNfilename,
@@ -1204,16 +1204,8 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in
&mut w,
&mut h,
) {
dc_param_set_int(
(*mimefactory.msg).param,
DC_PARAM_WIDTH as i32,
w as int32_t,
);
dc_param_set_int(
(*mimefactory.msg).param,
DC_PARAM_HEIGHT as i32,
h as int32_t,
);
(*mimefactory.msg).param.set_int(Param::Width, w as i32);
(*mimefactory.msg).param.set_int(Param::Height, h as i32);
}
}
free(buf as *mut libc::c_void);
@@ -1225,15 +1217,19 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in
/* create message */
if 0 == dc_mimefactory_render(&mut mimefactory) {
dc_set_msg_failed(context, msg_id, mimefactory.error);
} else if 0 != dc_param_get_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0)
} else if 0
!= (*mimefactory.msg)
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
&& 0 == mimefactory.out_encrypted
{
warn!(
context,
0,
"e2e encryption unavailable {} - {}",
"e2e encryption unavailable {} - {:?}",
msg_id,
dc_param_get_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0),
(*mimefactory.msg).param.get_int(Param::GuranteeE2ee),
);
dc_set_msg_failed(
context,
@@ -1271,19 +1267,15 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in
}
}
if 0 != mimefactory.out_encrypted
&& dc_param_get_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0)
&& (*mimefactory.msg)
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
== 0
{
dc_param_set_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 1);
(*mimefactory.msg).param.set_int(Param::GuranteeE2ee, 1);
dc_msg_save_param_to_disk(mimefactory.msg);
}
dc_add_to_keyhistory(
context,
0 as *const libc::c_char,
0,
0 as *const libc::c_char,
0 as *const libc::c_char,
);
success = dc_add_smtp_job(context, 5901i32, &mut mimefactory);
}
}

View File

@@ -1,21 +1,24 @@
use std::ffi::CString;
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_param::*;
use crate::dc_saxparser::*;
use crate::dc_stock::*;
use crate::dc_tools::*;
use crate::param::*;
use crate::sql;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
// location handling
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_location_t {
#[derive(Clone, Default)]
#[allow(non_camel_case_types)]
pub struct dc_location {
pub location_id: uint32_t,
pub latitude: libc::c_double,
pub longitude: libc::c_double,
@@ -24,16 +27,45 @@ pub struct dc_location_t {
pub contact_id: uint32_t,
pub msg_id: uint32_t,
pub chat_id: uint32_t,
pub marker: *mut libc::c_char,
pub marker: Option<String>,
pub independent: uint32_t,
}
#[derive(Copy, Clone)]
#[repr(C)]
impl dc_location {
pub fn new() -> Self {
dc_location {
location_id: 0,
latitude: 0.0,
longitude: 0.0,
accuracy: 0.0,
timestamp: 0,
contact_id: 0,
msg_id: 0,
chat_id: 0,
marker: None,
independent: 0,
}
}
}
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub struct dc_kml_t {
pub addr: *mut libc::c_char,
pub locations: *mut dc_array_t,
pub locations: Option<Vec<dc_location>>,
pub tag: libc::c_int,
pub curr: dc_location_t,
pub curr: dc_location,
}
impl dc_kml_t {
pub fn new() -> Self {
dc_kml_t {
addr: std::ptr::null_mut(),
locations: None,
tag: 0,
curr: dc_location::new(),
}
}
}
// location streaming
@@ -44,7 +76,6 @@ pub unsafe fn dc_send_locations_to_chat(
) {
let now = time();
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
let mut stock_str: *mut libc::c_char = 0 as *mut libc::c_char;
let is_sending_locations_before: bool;
if !(seconds < 0i32 || chat_id <= 9i32 as libc::c_uint) {
is_sending_locations_before = dc_is_sending_locations_to_chat(context, chat_id);
@@ -68,25 +99,20 @@ pub unsafe fn dc_send_locations_to_chat(
.is_ok()
{
if 0 != seconds && !is_sending_locations_before {
msg = dc_msg_new(context, 10i32);
(*msg).text = dc_stock_system_msg(
context,
64,
0 as *const libc::c_char,
0 as *const libc::c_char,
0,
);
dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 8);
msg = dc_msg_new(context, Viewtype::Text);
(*msg).text =
Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0));
(*msg).param.set_int(Param::Cmd, 8);
dc_send_msg(context, chat_id, msg);
} else if 0 == seconds && is_sending_locations_before {
stock_str = dc_stock_system_msg(
context,
65i32,
0 as *const libc::c_char,
0 as *const libc::c_char,
0i32 as uint32_t,
);
dc_add_device_msg(context, chat_id, stock_str);
let stock_str = CString::new(context.stock_system_msg(
StockMessage::MsgLocationDisabled,
"",
"",
0,
))
.unwrap();
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
}
context.call_cb(
Event::CHAT_MODIFIED,
@@ -99,22 +125,22 @@ pub unsafe fn dc_send_locations_to_chat(
context,
5007i32,
chat_id as libc::c_int,
0 as *const libc::c_char,
Params::new(),
seconds + 1i32,
);
}
}
}
free(stock_str as *mut libc::c_void);
dc_msg_unref(msg);
}
/*******************************************************************************
* job to send locations out to all chats that want them
******************************************************************************/
#[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, 0 as *const libc::c_char, 60);
dc_job_add(context, 5005, 0, Params::new(), 60);
};
}
@@ -197,54 +223,43 @@ pub fn dc_get_locations(
timestamp_from,
timestamp_to,
],
|row| unsafe {
let mut loc: *mut _dc_location =
calloc(1, ::std::mem::size_of::<_dc_location>()) as *mut _dc_location;
assert!(!loc.is_null(), "allocation failed");
|row| {
let msg_id = row.get(6)?;
let txt: String = row.get(9)?;
let marker = if msg_id != 0 && is_marker(&txt) {
Some(txt)
} else {
None
};
(*loc).location_id = row.get(0)?;
(*loc).latitude = row.get(1)?;
(*loc).longitude = row.get(2)?;
(*loc).accuracy = row.get(3)?;
(*loc).timestamp = row.get(4)?;
(*loc).independent = row.get(5)?;
(*loc).msg_id = row.get(6)?;
(*loc).contact_id = row.get(7)?;
(*loc).chat_id = row.get(8)?;
if 0 != (*loc).msg_id {
let txt: String = row.get(9)?;
let txt_c = to_cstring(txt);
if 0 != is_marker(txt_c) {
(*loc).marker = txt_c;
} else {
free(txt_c as *mut _);
}
}
let loc = dc_location {
location_id: row.get(0)?,
latitude: row.get(1)?,
longitude: row.get(2)?,
accuracy: row.get(3)?,
timestamp: row.get(4)?,
independent: row.get(5)?,
msg_id: msg_id,
contact_id: row.get(7)?,
chat_id: row.get(8)?,
marker: marker,
};
Ok(loc)
},
|locations| {
let ret = unsafe { dc_array_new_typed(1, 500) };
let mut ret = Vec::new();
for location in locations {
unsafe { dc_array_add_ptr(ret, location? as *mut libc::c_void) };
ret.push(location?);
}
Ok(ret)
Ok(dc_array_t::from(ret).into_raw())
},
)
.unwrap_or_else(|_| std::ptr::null_mut())
}
// TODO should be bool /rtn
unsafe fn is_marker(txt: *const libc::c_char) -> libc::c_int {
if !txt.is_null() {
let len: libc::c_int = dc_utf8_strlen(txt) as libc::c_int;
if len == 1 && *txt.offset(0isize) as libc::c_int != ' ' as i32 {
return 1;
}
}
0
fn is_marker(txt: &str) -> bool {
txt.len() == 1 && txt.chars().next().unwrap() != ' '
}
pub fn dc_delete_all_locations(context: &Context) -> bool {
@@ -332,7 +347,7 @@ pub fn dc_get_location_kml(
}
if 0 != success {
unsafe { to_cstring(ret) }
unsafe { ret.strdup() }
} else {
std::ptr::null_mut()
}
@@ -346,7 +361,7 @@ unsafe fn get_kml_timestamp(utc: i64) -> *mut libc::c_char {
let res = chrono::NaiveDateTime::from_timestamp(utc, 0)
.format("%Y-%m-%dT%H:%M:%SZ")
.to_string();
to_cstring(res)
res.strdup()
}
pub unsafe fn dc_get_message_kml(
@@ -404,13 +419,14 @@ pub unsafe fn dc_save_locations(
context: &Context,
chat_id: u32,
contact_id: u32,
locations: *const dc_array_t,
locations_opt: &Option<Vec<dc_location>>,
independent: libc::c_int,
) -> u32 {
if chat_id <= 9 || locations.is_null() {
if chat_id <= 9 || locations_opt.is_none() {
return 0;
}
let locations = locations_opt.as_ref().unwrap();
context
.sql
.prepare2(
@@ -422,31 +438,29 @@ pub unsafe fn dc_save_locations(
let mut newest_timestamp = 0;
let mut newest_location_id = 0;
for i in 0..dc_array_get_cnt(locations) {
let location = dc_array_get_ptr(locations, i as size_t) as *mut dc_location_t;
for location in locations {
let exists =
stmt_test.exists(params![(*location).timestamp, contact_id as i32])?;
stmt_test.exists(params![location.timestamp, contact_id as i32])?;
if 0 != independent || !exists {
stmt_insert.execute(params![
(*location).timestamp,
location.timestamp,
contact_id as i32,
chat_id as i32,
(*location).latitude,
(*location).longitude,
(*location).accuracy,
location.latitude,
location.longitude,
location.accuracy,
independent,
])?;
if (*location).timestamp > newest_timestamp {
newest_timestamp = (*location).timestamp;
if location.timestamp > newest_timestamp {
newest_timestamp = location.timestamp;
newest_location_id = sql::get_rowid2_with_conn(
context,
conn,
"locations",
"timestamp",
(*location).timestamp,
location.timestamp,
"from_id",
contact_id as i32,
);
@@ -463,8 +477,8 @@ pub unsafe fn dc_kml_parse(
context: &Context,
content: *const libc::c_char,
content_bytes: size_t,
) -> *mut dc_kml_t {
let mut kml: *mut dc_kml_t = calloc(1, ::std::mem::size_of::<dc_kml_t>()) as *mut dc_kml_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,
@@ -481,8 +495,11 @@ pub unsafe fn dc_kml_parse(
} else {
content_nullterminated = dc_null_terminate(content, content_bytes as libc::c_int);
if !content_nullterminated.is_null() {
(*kml).locations = dc_array_new_typed(1, 100 as size_t);
dc_saxparser_init(&mut saxparser, kml as *mut libc::c_void);
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),
@@ -563,10 +580,8 @@ unsafe fn kml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
&& 0. != (*kml).curr.latitude
&& 0. != (*kml).curr.longitude
{
let location: *mut dc_location_t =
calloc(1, ::std::mem::size_of::<dc_location_t>()) as *mut dc_location_t;
*location = (*kml).curr;
dc_array_add_ptr((*kml).locations, location as *mut libc::c_void);
let location = (*kml).curr.clone();
((*kml).locations.as_mut().unwrap()).push(location);
}
(*kml).tag = 0
};
@@ -617,15 +632,11 @@ unsafe fn kml_starttag_cb(
};
}
pub unsafe fn dc_kml_unref(kml: *mut dc_kml_t) {
if kml.is_null() {
return;
}
dc_array_unref((*kml).locations);
pub unsafe fn dc_kml_unref(kml: &mut dc_kml_t) {
free((*kml).addr as *mut libc::c_void);
free(kml 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) {
let now = time();
let mut continue_streaming: libc::c_int = 1;
@@ -688,9 +699,9 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mu
// the easiest way to determine this, is to check for an empty message queue.
// (might not be 100%, however, as positions are sent combined later
// and dc_set_location() is typically called periodically, this is ok)
let mut msg = dc_msg_new(context, 10);
let mut msg = dc_msg_new(context, Viewtype::Text);
(*msg).hidden = 1;
dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 9);
(*msg).param.set_int(Param::Cmd, 9);
dc_send_msg(context, chat_id as u32, msg);
dc_msg_unref(msg);
}
@@ -706,13 +717,13 @@ 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) {
// 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.
let chat_id = (*job).foreign_id;
let mut stock_str = 0 as *mut libc::c_char;
if let Ok((send_begin, send_until)) = context.sql.query_row(
"SELECT locations_send_begin, locations_send_until FROM chats WHERE id=?",
@@ -729,14 +740,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() {
stock_str = dc_stock_system_msg(
context,
65,
0 as *const libc::c_char,
0 as *const libc::c_char,
0,
);
dc_add_device_msg(context, chat_id, stock_str);
let stock_str = CString::new(context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)).unwrap();
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
context.call_cb(
Event::CHAT_MODIFIED,
chat_id as usize,
@@ -746,5 +751,4 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut
}
}
}
free(stock_str as *mut libc::c_void);
}

View File

@@ -4,6 +4,7 @@ use crate::context::Context;
use crate::sql::Sql;
#[derive(Default, Debug)]
#[allow(non_camel_case_types)]
pub struct dc_loginparam_t {
pub addr: String,
pub mail_server: String,

View File

@@ -1,11 +1,13 @@
use crate::contact::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_contact::*;
use crate::dc_msg::*;
use crate::dc_stock::*;
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)]
@@ -29,7 +31,7 @@ pub struct 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 dc_chatlist_get_summary() or dc_msg_get_summary().
* eg. by chatlist.get_summary() or dc_msg_get_summary().
*
* NB: _Lot_ is used in the meaning _heap_ here.
*/
@@ -125,43 +127,59 @@ pub unsafe fn dc_lot_get_timestamp(lot: *const dc_lot_t) -> i64 {
/* in practice, the user additionally cuts the string himself pixel-accurate */
pub unsafe fn dc_lot_fill(
mut lot: *mut dc_lot_t,
msg: *const dc_msg_t,
msg: *mut dc_msg_t,
chat: *const Chat,
contact: *const dc_contact_t,
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 = dc_stock_str(context, 3i32);
(*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 = dc_stock_str(context, 2i32);
(*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_null() {
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 {
(*lot).text1 = dc_contact_get_display_name(contact)
if let Some(contact) = contact {
(*lot).text1 = contact.get_display_name().strdup();
} else {
(*lot).text1 = std::ptr::null_mut();
}
} else {
(*lot).text1 = dc_contact_get_first_name(contact)
if let Some(contact) = contact {
(*lot).text1 = contact.get_first_name().strdup();
} else {
(*lot).text1 = std::ptr::null_mut();
}
}
(*lot).text1_meaning = 2i32
(*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, (*msg).text, (*msg).param, 160i32, context);
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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@ use crate::constants::*;
use crate::context::*;
use crate::dc_job::*;
use crate::dc_msg::*;
use crate::param::Params;
pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
if context
@@ -31,13 +32,7 @@ pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u3
// 1 = dc message, 2 = reply to dc message
if 0 != (*msg).is_dc_message {
dc_job_add(
context,
200,
(*msg).id as libc::c_int,
0 as *const libc::c_char,
0,
);
dc_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);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,439 +0,0 @@
use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
/// for msgs and jobs
pub const DC_PARAM_FILE: char = 'f'; // string
/// for msgs
pub const DC_PARAM_WIDTH: char = 'w'; // int
/// for msgs
pub const DC_PARAM_HEIGHT: char = 'h'; // int
/// for msgs
pub const DC_PARAM_DURATION: char = 'd'; // int
/// for msgs
pub const DC_PARAM_MIMETYPE: char = 'm'; // string
/// for msgs: incoming: message is encryoted, outgoing: guarantee E2EE or the message is not send
pub const DC_PARAM_GUARANTEE_E2EE: char = 'c'; // int (bool?)
/// for msgs: decrypted with validation errors or without mutual set, if neither 'c' nor 'e' are preset, the messages is only transport encrypted
pub const DC_PARAM_ERRONEOUS_E2EE: char = 'e'; // int
/// for msgs: force unencrypted message, either DC_FP_ADD_AUTOCRYPT_HEADER (1), DC_FP_NO_AUTOCRYPT_HEADER (2) or 0
pub const DC_PARAM_FORCE_PLAINTEXT: char = 'u'; // int (bool?)
/// for msgs: an incoming message which requests a MDN (aka read receipt)
pub const DC_PARAM_WANTS_MDN: char = 'r'; // int (bool?)
/// for msgs
pub const DC_PARAM_FORWARDED: char = 'a'; // int (bool?)
/// for msgs
pub const DC_PARAM_CMD: char = 'S'; // int
/// for msgs
pub const DC_PARAM_CMD_ARG: char = 'E'; // string
/// for msgs
pub const DC_PARAM_CMD_ARG2: char = 'F'; // string
/// for msgs
pub const DC_PARAM_CMD_ARG3: char = 'G'; // string
/// for msgs
pub const DC_PARAM_CMD_ARG4: char = 'H'; // string
/// for msgs
pub const DC_PARAM_ERROR: char = 'L'; // string
/// for msgs in PREPARING: space-separated list of message IDs of forwarded copies
pub const DC_PARAM_PREP_FORWARDS: char = 'P'; // string
/// for msgs
pub const DC_PARAM_SET_LATITUDE: char = 'l'; // float
/// for msgs
pub const DC_PARAM_SET_LONGITUDE: char = 'n'; // float
/// for jobs
pub const DC_PARAM_SERVER_FOLDER: char = 'Z'; // string
/// for jobs
pub const DC_PARAM_SERVER_UID: char = 'z'; // int
/// for jobs
pub const DC_PARAM_ALSO_MOVE: char = 'M'; // int (bool?)
/// for jobs: space-separated list of message recipients
pub const DC_PARAM_RECIPIENTS: char = 'R'; // stringap
/// for groups
pub const DC_PARAM_UNPROMOTED: char = 'U'; // int (bool?)
/// for groups and contacts
pub const DC_PARAM_PROFILE_IMAGE: char = 'i'; // string (bytes?)
/// for chats
pub const DC_PARAM_SELFTALK: char = 'K';
// missing: 's', 'x', 'g'
// values for DC_PARAM_FORCE_PLAINTEXT
pub const DC_FP_ADD_AUTOCRYPT_HEADER: u8 = 1;
pub const DC_FP_NO_AUTOCRYPT_HEADER: u8 = 2;
/// An object for handling key=value parameter lists; for the key, currently only
/// a single character is allowed.
///
/// The object is used eg. by Chat or dc_msg_t, for readable parameter names,
/// these classes define some DC_PARAM_* constantats.
///
/// Only for library-internal use.
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_param_t {
pub packed: *mut libc::c_char,
}
// values for DC_PARAM_FORCE_PLAINTEXT
/* user functions */
pub unsafe fn dc_param_exists(param: *mut dc_param_t, key: libc::c_int) -> libc::c_int {
let mut p2: *mut libc::c_char = 0 as *mut libc::c_char;
if param.is_null() || key == 0i32 {
return 0i32;
}
return if !find_param((*param).packed, key, &mut p2).is_null() {
1i32
} else {
0i32
};
}
unsafe extern "C" fn find_param(
haystack: *mut libc::c_char,
key: libc::c_int,
ret_p2: *mut *mut libc::c_char,
) -> *mut libc::c_char {
let mut p1: *mut libc::c_char;
let mut p2: *mut libc::c_char;
p1 = haystack;
loop {
if p1.is_null() || *p1 as libc::c_int == 0i32 {
return 0 as *mut libc::c_char;
} else {
if *p1 as libc::c_int == key && *p1.offset(1isize) as libc::c_int == '=' as i32 {
break;
}
p1 = strchr(p1, '\n' as i32);
if !p1.is_null() {
p1 = p1.offset(1isize)
}
}
}
p2 = strchr(p1, '\n' as i32);
if p2.is_null() {
p2 = &mut *p1.offset(strlen(p1) as isize) as *mut libc::c_char
}
*ret_p2 = p2;
p1
}
/* the value may be an empty string, "def" is returned only if the value unset. The result must be free()'d in any case. */
pub unsafe fn dc_param_get(
param: *const dc_param_t,
key: libc::c_int,
def: *const libc::c_char,
) -> *mut libc::c_char {
let mut p1: *mut libc::c_char;
let mut p2: *mut libc::c_char = 0 as *mut libc::c_char;
let bak: libc::c_char;
let ret: *mut libc::c_char;
if param.is_null() || key == 0i32 {
return if !def.is_null() {
dc_strdup(def)
} else {
0 as *mut libc::c_char
};
}
p1 = find_param((*param).packed, key, &mut p2);
if p1.is_null() {
return if !def.is_null() {
dc_strdup(def)
} else {
0 as *mut libc::c_char
};
}
p1 = p1.offset(2isize);
bak = *p2;
*p2 = 0i32 as libc::c_char;
ret = dc_strdup(p1);
dc_rtrim(ret);
*p2 = bak;
ret
}
pub unsafe fn dc_param_get_int(
param: *const dc_param_t,
key: libc::c_int,
def: int32_t,
) -> int32_t {
if param.is_null() || key == 0i32 {
return def;
}
let s = dc_param_get(param, key, 0 as *const libc::c_char);
if s.is_null() {
return def;
}
let ret = as_str(s).parse().unwrap_or_default();
free(s as *mut libc::c_void);
ret
}
/**
* Get value of a parameter.
*
* @memberof dc_param_t
* @param param Parameter object to query.
* @param key Key of the parameter to get, one of the DC_PARAM_* constants.
* @param def Value to return if the parameter is not set.
* @return The stored value or the default value.
*/
pub unsafe fn dc_param_get_float(
param: *const dc_param_t,
key: libc::c_int,
def: libc::c_double,
) -> libc::c_double {
if param.is_null() || key == 0 {
return def;
}
let str = dc_param_get(param, key, std::ptr::null());
if str.is_null() {
return def;
}
let ret = dc_atof(str) as libc::c_double;
free(str as *mut libc::c_void);
ret
}
pub unsafe fn dc_param_set(
mut param: *mut dc_param_t,
key: libc::c_int,
value: *const libc::c_char,
) {
let mut old1: *mut libc::c_char;
let mut old2: *mut libc::c_char;
let new1: *mut libc::c_char;
if param.is_null() || key == 0i32 {
return;
}
old1 = (*param).packed;
old2 = 0 as *mut libc::c_char;
if !old1.is_null() {
let p1: *mut libc::c_char;
let mut p2: *mut libc::c_char = 0 as *mut libc::c_char;
p1 = find_param(old1, key, &mut p2);
if !p1.is_null() {
*p1 = 0i32 as libc::c_char;
old2 = p2
} else if value.is_null() {
return;
}
}
dc_rtrim(old1);
dc_ltrim(old2);
if !old1.is_null() && *old1.offset(0isize) as libc::c_int == 0i32 {
old1 = 0 as *mut libc::c_char
}
if !old2.is_null() && *old2.offset(0isize) as libc::c_int == 0i32 {
old2 = 0 as *mut libc::c_char
}
if !value.is_null() {
new1 = dc_mprintf(
b"%s%s%c=%s%s%s\x00" as *const u8 as *const libc::c_char,
if !old1.is_null() {
old1
} else {
b"\x00" as *const u8 as *const libc::c_char
},
if !old1.is_null() {
b"\n\x00" as *const u8 as *const libc::c_char
} else {
b"\x00" as *const u8 as *const libc::c_char
},
key,
value,
if !old2.is_null() {
b"\n\x00" as *const u8 as *const libc::c_char
} else {
b"\x00" as *const u8 as *const libc::c_char
},
if !old2.is_null() {
old2
} else {
b"\x00" as *const u8 as *const libc::c_char
},
)
} else {
new1 = dc_mprintf(
b"%s%s%s\x00" as *const u8 as *const libc::c_char,
if !old1.is_null() {
old1
} else {
b"\x00" as *const u8 as *const libc::c_char
},
if !old1.is_null() && !old2.is_null() {
b"\n\x00" as *const u8 as *const libc::c_char
} else {
b"\x00" as *const u8 as *const libc::c_char
},
if !old2.is_null() {
old2
} else {
b"\x00" as *const u8 as *const libc::c_char
},
)
}
free((*param).packed as *mut libc::c_void);
(*param).packed = new1;
}
pub unsafe fn dc_param_set_int(param: *mut dc_param_t, key: libc::c_int, value: int32_t) {
if param.is_null() || key == 0i32 {
return;
}
let value_str: *mut libc::c_char = dc_mprintf(
b"%i\x00" as *const u8 as *const libc::c_char,
value as libc::c_int,
);
if value_str.is_null() {
return;
}
dc_param_set(param, key, value_str);
free(value_str as *mut libc::c_void);
}
/* library-private */
pub unsafe fn dc_param_new() -> *mut dc_param_t {
let mut param: *mut dc_param_t;
param = calloc(1, ::std::mem::size_of::<dc_param_t>()) as *mut dc_param_t;
assert!(!param.is_null());
(*param).packed = calloc(1, 1) as *mut libc::c_char;
param
}
pub unsafe fn dc_param_empty(param: *mut dc_param_t) {
if param.is_null() {
return;
}
*(*param).packed.offset(0isize) = 0i32 as libc::c_char;
}
pub unsafe fn dc_param_unref(param: *mut dc_param_t) {
if param.is_null() {
return;
}
dc_param_empty(param);
free((*param).packed as *mut libc::c_void);
free(param as *mut libc::c_void);
}
pub unsafe fn dc_param_set_packed(mut param: *mut dc_param_t, packed: *const libc::c_char) {
if param.is_null() {
return;
}
dc_param_empty(param);
if !packed.is_null() {
free((*param).packed as *mut libc::c_void);
(*param).packed = dc_strdup(packed)
};
}
pub unsafe fn dc_param_set_urlencoded(mut param: *mut dc_param_t, urlencoded: *const libc::c_char) {
if param.is_null() {
return;
}
dc_param_empty(param);
if !urlencoded.is_null() {
free((*param).packed as *mut libc::c_void);
(*param).packed = dc_strdup(urlencoded);
dc_str_replace(
&mut (*param).packed,
b"&\x00" as *const u8 as *const libc::c_char,
b"\n\x00" as *const u8 as *const libc::c_char,
);
};
}
/**
* Set parameter to a float.
*
* @memberof dc_param_t
* @param param Parameter object to modify.
* @param key Key of the parameter to modify, one of the DC_PARAM_* constants.
* @param value Value to store for key.
* @return None.
*/
pub unsafe fn dc_param_set_float(param: *mut dc_param_t, key: libc::c_int, value: libc::c_double) {
if param.is_null() || key == 0 {
return;
}
let value_str = dc_ftoa(value);
if value_str.is_null() {
return;
}
dc_param_set(param, key, value_str);
free(value_str as *mut libc::c_void);
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CStr;
#[test]
fn test_dc_param() {
unsafe {
let p1: *mut dc_param_t = dc_param_new();
dc_param_set_packed(
p1,
b"\r\n\r\na=1\nb=2\n\nc = 3 \x00" as *const u8 as *const libc::c_char,
);
assert_eq!(dc_param_get_int(p1, 'a' as i32, 0), 1);
assert_eq!(dc_param_get_int(p1, 'b' as i32, 0), 2);
assert_eq!(dc_param_get_int(p1, 'c' as i32, 0), 0);
assert_eq!(dc_param_exists(p1, 'c' as i32), 0);
dc_param_set_int(p1, 'd' as i32, 4i32);
assert_eq!(dc_param_get_int(p1, 'd' as i32, 0), 4);
dc_param_empty(p1);
dc_param_set(
p1,
'a' as i32,
b"foo\x00" as *const u8 as *const libc::c_char,
);
dc_param_set_int(p1, 'b' as i32, 2i32);
dc_param_set(p1, 'c' as i32, 0 as *const libc::c_char);
dc_param_set_int(p1, 'd' as i32, 4i32);
assert_eq!(
CStr::from_ptr((*p1).packed as *const libc::c_char)
.to_str()
.unwrap(),
"a=foo\nb=2\nd=4"
);
dc_param_set(p1, 'b' as i32, 0 as *const libc::c_char);
assert_eq!(
CStr::from_ptr((*p1).packed as *const libc::c_char)
.to_str()
.unwrap(),
"a=foo\nd=4",
);
dc_param_set(p1, 'a' as i32, 0 as *const libc::c_char);
dc_param_set(p1, 'd' as i32, 0 as *const libc::c_char);
assert_eq!(
CStr::from_ptr((*p1).packed as *const libc::c_char)
.to_str()
.unwrap(),
"",
);
dc_param_unref(p1);
}
}
}

View File

@@ -1,11 +1,13 @@
use percent_encoding::percent_decode_str;
use crate::contact::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_contact::*;
use crate::dc_lot::*;
use crate::dc_param::*;
use crate::dc_strencode::*;
use crate::dc_tools::*;
use crate::key::*;
use crate::param::*;
use crate::peerstate::*;
use crate::types::*;
use crate::x::*;
@@ -54,36 +56,39 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
if !fragment.is_null() {
*fragment = 0i32 as libc::c_char;
fragment = fragment.offset(1isize);
let param: *mut dc_param_t = dc_param_new();
dc_param_set_urlencoded(param, fragment);
addr = dc_param_get(param, DC_PARAM_FORWARDED as i32, 0 as *const libc::c_char);
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() {
let mut urlencoded: *mut libc::c_char = dc_param_get(
param,
DC_PARAM_SET_LONGITUDE as i32,
0 as *const libc::c_char,
);
if !urlencoded.is_null() {
name = dc_urldecode(urlencoded);
dc_normalize_name(name);
free(urlencoded as *mut libc::c_void);
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 = dc_param_get(
param,
DC_PARAM_PROFILE_IMAGE as i32,
0 as *const libc::c_char,
);
auth = dc_param_get(param, 's' as i32, 0 as *const libc::c_char);
grpid = dc_param_get(param, 'x' as i32, 0 as *const libc::c_char);
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() {
urlencoded = dc_param_get(param, 'g' as i32, 0 as *const libc::c_char);
if !urlencoded.is_null() {
grpname = dc_urldecode(urlencoded);
free(urlencoded as *mut libc::c_void);
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();
}
}
}
dc_param_unref(param);
}
fingerprint = dc_normalize_fingerprint_c(payload);
current_block = 5023038348526654800;
@@ -146,11 +151,8 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
strlen(b"BEGIN:VCARD\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
let lines: *mut carray = dc_split_into_lines(qr);
let mut i: libc::c_int = 0i32;
while (i as libc::c_uint) < carray_count(lines) {
let key: *mut libc::c_char =
carray_get(lines, i as libc::c_uint) as *mut libc::c_char;
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() {
@@ -183,10 +185,9 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
b";\x00" as *const u8 as *const libc::c_char,
b",\x00" as *const u8 as *const libc::c_char,
);
dc_normalize_name(name);
name = normalize_name(as_str(name)).strdup();
}
}
i += 1
}
dc_free_splitted_lines(lines);
}
@@ -202,10 +203,10 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
let mut temp: *mut libc::c_char = dc_urldecode(addr);
free(addr as *mut libc::c_void);
addr = temp;
temp = dc_addr_normalize(addr);
temp = addr_normalize(as_str(addr)).strdup();
free(addr as *mut libc::c_void);
addr = temp;
if !dc_may_be_valid_addr(addr) {
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,
@@ -246,19 +247,19 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
if addr.is_null() || invitenumber.is_null() || auth.is_null() {
if let Some(peerstate) = peerstate {
(*qr_parsed).state = 210i32;
let addr_ptr = if let Some(ref addr) = peerstate.addr {
to_cstring(addr)
} else {
std::ptr::null()
};
(*qr_parsed).id = dc_add_or_lookup_contact(
let addr = peerstate
.addr
.as_ref()
.map(|s| s.as_str())
.unwrap_or_else(|| "");
(*qr_parsed).id = Contact::add_or_lookup(
context,
0 as *const libc::c_char,
addr_ptr,
0x80i32,
0 as *mut libc::c_int,
);
free(addr_ptr as *mut _);
"",
addr,
Origin::UnhandledQrScan,
)
.map(|(id, _)| id)
.unwrap_or_default();
dc_create_or_lookup_nchat_by_contact_id(
context,
(*qr_parsed).id,
@@ -284,26 +285,28 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
} else {
(*qr_parsed).state = 200i32
}
(*qr_parsed).id = dc_add_or_lookup_contact(
(*qr_parsed).id = Contact::add_or_lookup(
context,
name,
addr,
0x80i32,
0 as *mut libc::c_int,
);
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 = dc_add_or_lookup_contact(
(*qr_parsed).id = Contact::add_or_lookup(
context,
name,
addr,
0x80i32,
0 as *mut libc::c_int,
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,

View File

@@ -8,22 +8,22 @@ use mmime::other::*;
use sha2::{Digest, Sha256};
use crate::constants::*;
use crate::contact::*;
use crate::context::Context;
use crate::dc_array::*;
use crate::dc_chat::*;
use crate::dc_contact::*;
use crate::dc_job::*;
use crate::dc_location::*;
use crate::dc_mimeparser::*;
use crate::dc_move::*;
use crate::dc_msg::*;
use crate::dc_param::*;
use crate::dc_securejoin::*;
use crate::dc_stock::*;
use crate::dc_strencode::*;
use crate::dc_tools::*;
use crate::param::*;
use crate::peerstate::*;
use crate::sql;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
@@ -38,7 +38,7 @@ pub unsafe fn dc_receive_imf(
let mut current_block: u64;
/* the function returns the number of created messages in the database */
let mut incoming: libc::c_int = 1;
let mut incoming_origin: libc::c_int = 0;
let mut incoming_origin = Origin::Unknown;
let mut to_self: libc::c_int = 0;
let mut from_id: uint32_t = 0 as uint32_t;
let mut from_id_blocked: libc::c_int = 0;
@@ -51,8 +51,6 @@ pub unsafe fn dc_receive_imf(
let mut add_delete_job: libc::c_int = 0;
let mut insert_msg_id: uint32_t = 0 as uint32_t;
let mut i: size_t;
let mut icnt: size_t;
/* Message-ID from the header */
let mut rfc724_mid = 0 as *mut libc::c_char;
let mut sort_timestamp = 0;
@@ -105,7 +103,7 @@ pub unsafe fn dc_receive_imf(
dc_add_or_lookup_contacts_by_mailbox_list(
context,
(*fld_from).frm_mb_list,
0x10,
Origin::IncomingUnknownFrom,
from_list,
&mut check_self,
);
@@ -116,7 +114,8 @@ pub unsafe fn dc_receive_imf(
}
} else if dc_array_get_cnt(from_list) >= 1 {
from_id = dc_array_get_id(from_list, 0 as size_t);
incoming_origin = dc_get_contact_origin(context, from_id, &mut from_id_blocked)
incoming_origin =
Contact::get_origin_by_id(context, from_id, &mut from_id_blocked)
}
dc_array_unref(from_list);
}
@@ -129,18 +128,18 @@ pub unsafe fn dc_receive_imf(
context,
(*fld_to).to_addr_list,
if 0 == incoming {
0x4000
} else if incoming_origin >= 0x100 {
0x400
Origin::OutgoingTo
} else if incoming_origin.is_verified() {
Origin::IncomingTo
} else {
0x40
Origin::IncomingUnknownTo
},
to_ids,
&mut to_self,
);
}
}
if !dc_mimeparser_get_last_nonmeta(&mime_parser).is_null() {
if dc_mimeparser_get_last_nonmeta(&mut mime_parser).is_some() {
field = dc_mimeparser_lookup_field(&mime_parser, "Cc");
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_CC as libc::c_int {
let fld_cc: *mut mailimf_cc = (*field).fld_data.fld_cc;
@@ -149,11 +148,11 @@ pub unsafe fn dc_receive_imf(
context,
(*fld_cc).cc_addr_list,
if 0 == incoming {
0x2000
} else if incoming_origin >= 0x100 {
0x200
Origin::OutgoingCc
} else if incoming_origin.is_verified() {
Origin::IncomingCc
} else {
0x20
Origin::IncomingUnknownCc
},
to_ids,
0 as *mut libc::c_int,
@@ -255,7 +254,7 @@ pub unsafe fn dc_receive_imf(
if chat_id == 0 as libc::c_uint {
let create_blocked: libc::c_int = if 0 != test_normal_chat_id
&& test_normal_chat_id_blocked == 0
|| incoming_origin >= 0x7fffffff
|| incoming_origin.is_start_new_chat()
{
0
} else {
@@ -287,7 +286,7 @@ pub unsafe fn dc_receive_imf(
}
if chat_id == 0 as libc::c_uint {
let create_blocked_0: libc::c_int =
if incoming_origin >= 0x7fffffff || from_id == to_id {
if incoming_origin.is_start_new_chat() || from_id == to_id {
0
} else {
2
@@ -311,16 +310,20 @@ pub unsafe fn dc_receive_imf(
} else if 0
!= dc_is_reply_to_known_message(context, &mime_parser)
{
dc_scaleup_contact_origin(context, from_id, 0x100);
Contact::scaleup_origin_by_id(
context,
from_id,
Origin::IncomingReplyTo,
);
info!(
context,
0,
"Message is a reply to a known message, mark sender as known.",
);
incoming_origin = if incoming_origin > 0x100 {
incoming_origin = if incoming_origin.is_verified() {
incoming_origin
} else {
0x100
Origin::IncomingReplyTo
}
}
}
@@ -329,8 +332,8 @@ pub unsafe fn dc_receive_imf(
chat_id = 3 as uint32_t
}
if 0 != chat_id_blocked && state == 10 {
if incoming_origin < 0x100 && msgrmsg == 0 {
state = 13
if !incoming_origin.is_verified() && msgrmsg == 0 {
state = 13;
}
}
} else {
@@ -355,12 +358,13 @@ pub unsafe fn dc_receive_imf(
}
}
if chat_id == 0 as libc::c_uint && 0 != allow_creation {
let create_blocked_1: libc::c_int =
if 0 != msgrmsg && !dc_is_contact_blocked(context, to_id) {
0
} else {
2
};
let create_blocked_1: libc::c_int = if 0 != msgrmsg
&& !Contact::is_blocked_load(context, to_id)
{
0
} else {
2
};
dc_create_or_lookup_nchat_by_contact_id(
context,
to_id,
@@ -437,7 +441,7 @@ pub unsafe fn dc_receive_imf(
)
}
}
icnt = carray_count(mime_parser.parts) as size_t;
let icnt = mime_parser.parts.len();
context.sql.prepare(
"INSERT INTO msgs \
@@ -452,23 +456,23 @@ pub unsafe fn dc_receive_imf(
current_block = 2756754640271984560;
break;
}
let part = carray_get(mime_parser.parts, i as libc::c_uint) as *mut dc_mimepart_t;
if !(0 != (*part).is_meta) {
if !mime_parser.location_kml.is_null()
let part = &mut mime_parser.parts[i];
if part.is_meta == 0 {
if !mime_parser.location_kml.is_none()
&& icnt == 1
&& !(*part).msg.is_null()
&& !part.msg.is_null()
&& (strcmp(
(*part).msg,
part.msg,
b"-location-\x00" as *const u8 as *const libc::c_char,
) == 0
|| *(*part).msg.offset(0isize) as libc::c_int == 0)
|| *part.msg.offset(0isize) as libc::c_int == 0)
{
hidden = 1;
if state == 10 {
state = 13
}
}
if (*part).type_0 == 10 {
if part.type_0 == 10 {
txt_raw = dc_mprintf(
b"%s\n\n%s\x00" as *const u8 as *const libc::c_char,
if !mime_parser.subject.is_null() {
@@ -476,13 +480,12 @@ pub unsafe fn dc_receive_imf(
} else {
b"\x00" as *const u8 as *const libc::c_char
},
(*part).msg_raw,
part.msg_raw,
)
}
if 0 != mime_parser.is_system_message {
dc_param_set_int(
(*part).param,
'S' as i32,
part.param.set_int( Param::Cmd,
mime_parser.is_system_message,
);
}
@@ -497,11 +500,11 @@ pub unsafe fn dc_receive_imf(
sort_timestamp,
sent_timestamp,
rcvd_timestamp,
(*part).type_0,
part.type_0,
state,
msgrmsg,
if !(*part).msg.is_null() {
as_str((*part).msg)
if !part.msg.is_null() {
as_str(part.msg)
} else {
""
},
@@ -511,8 +514,8 @@ pub unsafe fn dc_receive_imf(
} else {
String::new()
},
as_str((*(*part).param).packed),
(*part).bytes,
part.param.to_string(),
part.bytes,
hidden,
if 0 != save_mime_headers {
let body_string = std::str::from_utf8(std::slice::from_raw_parts(imf_raw_not_terminated as *const u8, imf_raw_bytes)).unwrap();
@@ -589,17 +592,13 @@ pub unsafe fn dc_receive_imf(
match current_block {
16282941964262048061 => {}
_ => {
if carray_count(mime_parser.reports) > 0 as libc::c_uint {
if !mime_parser.reports.is_empty() {
let mdns_enabled = context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1);
icnt = carray_count(mime_parser.reports) as size_t;
i = 0 as size_t;
while i < icnt {
for report_root in mime_parser.reports {
let mut mdn_consumed: libc::c_int = 0;
let report_root: *mut mailmime =
carray_get(mime_parser.reports, i as libc::c_uint) as *mut mailmime;
let report_type: *mut mailmime_parameter = mailmime_find_ct_parameter(
report_root,
b"report-type\x00" as *const u8 as *const libc::c_char,
@@ -724,8 +723,7 @@ pub unsafe fn dc_receive_imf(
&mut msg_id,
) {
rr_event_to_send
.push((chat_id_0, 0));
rr_event_to_send.push((msg_id, 0));
.push((chat_id_0, msg_id));
}
mdn_consumed = (msg_id
!= 0 as libc::c_uint)
@@ -743,47 +741,35 @@ pub unsafe fn dc_receive_imf(
}
}
if 0 != mime_parser.is_send_by_messenger || 0 != mdn_consumed {
let param = dc_param_new();
let server_folder_c = to_cstring(server_folder.as_ref());
dc_param_set(
param,
DC_PARAM_SERVER_FOLDER as i32,
server_folder_c,
);
free(server_folder_c as *mut _);
dc_param_set_int(
param,
DC_PARAM_SERVER_UID as i32,
server_uid as i32,
);
let mut param = Params::new();
param.set(Param::ServerFolder, server_folder.as_ref());
param.set_int(Param::ServerUid, server_uid as i32);
if 0 != mime_parser.is_send_by_messenger
&& 0 != context
.sql
.get_config_int(context, "mvbox_move")
.unwrap_or_else(|| 1)
{
dc_param_set_int(param, DC_PARAM_ALSO_MOVE as i32, 1);
param.set_int(Param::AlsoMove, 1);
}
dc_job_add(context, 120, 0, (*param).packed, 0);
dc_param_unref(param);
dc_job_add(context, 120, 0, param, 0);
}
}
}
i = i.wrapping_add(1)
}
}
if !mime_parser.message_kml.is_null() && chat_id > 9 as libc::c_uint {
if !mime_parser.message_kml.is_none() && chat_id > 9 as libc::c_uint {
let mut location_id_written = false;
let mut send_event = false;
if !mime_parser.message_kml.is_null()
if !mime_parser.message_kml.is_none()
&& chat_id > DC_CHAT_ID_LAST_SPECIAL as libc::c_uint
{
let newest_location_id: uint32_t = dc_save_locations(
context,
chat_id,
from_id,
(*mime_parser.message_kml).locations,
&mime_parser.message_kml.unwrap().locations,
1,
);
if 0 != newest_location_id && 0 == hidden {
@@ -793,28 +779,37 @@ pub unsafe fn dc_receive_imf(
}
}
if !mime_parser.location_kml.is_null()
if !mime_parser.location_kml.is_none()
&& chat_id > DC_CHAT_ID_LAST_SPECIAL as libc::c_uint
{
let contact = dc_get_contact(context, from_id);
if !(*mime_parser.location_kml).addr.is_null()
&& !contact.is_null()
&& !(*contact).addr.is_null()
&& strcasecmp((*contact).addr, (*mime_parser.location_kml).addr) == 0
{
let newest_location_id = dc_save_locations(
context,
chat_id,
from_id,
(*mime_parser.location_kml).locations,
0,
);
if newest_location_id != 0 && hidden == 0 && !location_id_written {
dc_set_msg_location_id(context, insert_msg_id, newest_location_id);
if !mime_parser.location_kml.as_ref().unwrap().addr.is_null() {
if let Ok(contact) = Contact::get_by_id(context, from_id) {
if !contact.get_addr().is_empty()
&& contact.get_addr().to_lowercase()
== as_str(mime_parser.location_kml.as_ref().unwrap().addr)
.to_lowercase()
{
let newest_location_id = dc_save_locations(
context,
chat_id,
from_id,
&mime_parser.location_kml.as_ref().unwrap().locations,
0,
);
if newest_location_id != 0
&& hidden == 0
&& !location_id_written
{
dc_set_msg_location_id(
context,
insert_msg_id,
newest_location_id,
);
}
send_event = true;
}
}
send_event = true;
}
dc_contact_unref(contact);
}
if send_event {
context.call_cb(
@@ -830,7 +825,7 @@ pub unsafe fn dc_receive_imf(
context,
DC_JOB_DELETE_MSG_ON_IMAP,
created_db_entries[0].1 as i32,
0 as *const libc::c_char,
Params::new(),
0,
);
}
@@ -914,6 +909,7 @@ which tries to create or find out the chat_id by:
So when the function returns, the caller has the group id matching the current
state of the group. */
#[allow(non_snake_case)]
unsafe fn create_or_lookup_group(
context: &Context,
mime_parser: &mut dc_mimeparser_t,
@@ -941,18 +937,13 @@ unsafe fn create_or_lookup_group(
let mut X_MrAddToGrp: *mut libc::c_char = 0 as *mut libc::c_char;
let mut X_MrGrpNameChanged: libc::c_int = 0;
let mut X_MrGrpImageChanged: *const libc::c_char = 0 as *const libc::c_char;
let mut better_msg: *mut libc::c_char = 0 as *mut libc::c_char;
let mut better_msg: String = From::from("");
let mut failure_reason: *mut libc::c_char = 0 as *mut libc::c_char;
if mime_parser.is_system_message == 8 {
better_msg = dc_stock_system_msg(
context,
64,
0 as *const libc::c_char,
0 as *const libc::c_char,
from_id as uint32_t,
)
if mime_parser.is_system_message == 8i32 {
better_msg =
context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", from_id as u32)
}
set_better_msg(mime_parser, &mut better_msg);
set_better_msg(mime_parser, &better_msg);
/* search the grpid in the header */
let mut field: *mut mailimf_field;
let mut optional_field: *mut mailimf_optional_field;
@@ -968,7 +959,13 @@ unsafe fn create_or_lookup_group(
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_MESSAGE_ID as libc::c_int {
let fld_message_id: *mut mailimf_message_id = (*field).fld_data.fld_message_id;
if !fld_message_id.is_null() {
grpid = dc_extract_grpid_from_rfc724_mid((*fld_message_id).mid_value)
if let Some(extracted_grpid) =
dc_extract_grpid_from_rfc724_mid(as_str((*fld_message_id).mid_value))
{
grpid = extracted_grpid.strdup();
} else {
grpid = 0 as *mut libc::c_char;
}
}
}
if grpid.is_null() {
@@ -1028,15 +1025,17 @@ unsafe fn create_or_lookup_group(
if !optional_field.is_null() {
X_MrRemoveFromGrp = (*optional_field).fld_value;
mime_parser.is_system_message = 5;
let left_group: libc::c_int =
(dc_lookup_contact_id_by_addr(context, X_MrRemoveFromGrp)
== from_id as libc::c_uint) as libc::c_int;
better_msg = dc_stock_system_msg(
context,
if 0 != left_group { 19 } else { 18 },
X_MrRemoveFromGrp,
0 as *const libc::c_char,
from_id as uint32_t,
let left_group = (Contact::lookup_id_by_addr(context, as_str(X_MrRemoveFromGrp))
== from_id as u32) as libc::c_int;
better_msg = context.stock_system_msg(
if 0 != left_group {
StockMessage::MsgGroupLeft
} else {
StockMessage::MsgDelMember
},
as_str(X_MrRemoveFromGrp),
"",
from_id as u32,
)
} else {
optional_field = dc_mimeparser_lookup_optional_field(
@@ -1053,12 +1052,11 @@ unsafe fn create_or_lookup_group(
if !optional_field.is_null() {
X_MrGrpImageChanged = (*optional_field).fld_value
}
better_msg = dc_stock_system_msg(
context,
17,
X_MrAddToGrp,
0 as *const libc::c_char,
from_id as uint32_t,
better_msg = context.stock_system_msg(
StockMessage::MsgAddMember,
as_str(X_MrAddToGrp),
"",
from_id as u32,
)
} else {
optional_field = dc_mimeparser_lookup_optional_field(
@@ -1066,14 +1064,13 @@ unsafe fn create_or_lookup_group(
b"Chat-Group-Name-Changed\x00" as *const u8 as *const libc::c_char,
);
if !optional_field.is_null() {
X_MrGrpNameChanged = 1;
mime_parser.is_system_message = 2;
better_msg = dc_stock_system_msg(
context,
15,
(*optional_field).fld_value,
grpname,
from_id as uint32_t,
X_MrGrpNameChanged = 1i32;
mime_parser.is_system_message = 2i32;
better_msg = context.stock_system_msg(
StockMessage::MsgGrpName,
as_str((*optional_field).fld_value),
as_str(grpname),
from_id as u32,
)
} else {
optional_field = dc_mimeparser_lookup_optional_field(
@@ -1083,26 +1080,25 @@ unsafe fn create_or_lookup_group(
if !optional_field.is_null() {
X_MrGrpImageChanged = (*optional_field).fld_value;
mime_parser.is_system_message = 3;
better_msg = dc_stock_system_msg(
context,
better_msg = context.stock_system_msg(
if strcmp(
X_MrGrpImageChanged,
b"0\x00" as *const u8 as *const libc::c_char,
) == 0
{
33
StockMessage::MsgGrpImgDeleted
} else {
16
StockMessage::MsgGrpImgChanged
},
0 as *const libc::c_char,
0 as *const libc::c_char,
from_id as uint32_t,
"",
"",
from_id as u32,
)
}
}
}
}
set_better_msg(mime_parser, &mut better_msg);
set_better_msg(mime_parser, &better_msg);
chat_id = dc_get_chat_id_by_grpid(
context,
grpid,
@@ -1139,7 +1135,7 @@ unsafe fn create_or_lookup_group(
&& !grpname.is_null()
&& X_MrRemoveFromGrp.is_null()
&& (0 == group_explicitly_left
|| !X_MrAddToGrp.is_null() && dc_addr_cmp(&self_addr, as_str(X_MrAddToGrp)))
|| !X_MrAddToGrp.is_null() && addr_cmp(&self_addr, as_str(X_MrAddToGrp)))
{
/*otherwise, a pending "quit" message may pop up*/
/*re-create explicitly left groups only if ourself is re-added*/
@@ -1221,24 +1217,19 @@ unsafe fn create_or_lookup_group(
{
ok = 1
} else {
let mut i_0: libc::c_int = 0;
while (i_0 as libc::c_uint) < carray_count(mime_parser.parts) {
let part: *mut dc_mimepart_t =
carray_get(mime_parser.parts, i_0 as libc::c_uint)
as *mut dc_mimepart_t;
if (*part).type_0 == 20 {
grpimage = dc_param_get(
(*part).param,
DC_PARAM_FILE as i32,
0 as *const libc::c_char,
);
for part in &mut mime_parser.parts {
if part.type_0 == 20 {
grpimage = part
.param
.get(Param::File)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
ok = 1
}
i_0 += 1
}
}
if 0 != ok {
let chat: *mut Chat = dc_chat_new(context);
let chat = dc_chat_new(context);
info!(
context,
0,
@@ -1250,11 +1241,11 @@ unsafe fn create_or_lookup_group(
},
);
dc_chat_load_from_db(chat, chat_id);
dc_param_set(
(*chat).param,
DC_PARAM_PROFILE_IMAGE as i32,
grpimage,
);
if grpimage.is_null() {
(*chat).param.remove(Param::ProfileImage);
} else {
(*chat).param.set(Param::ProfileImage, as_str(grpimage));
}
dc_chat_update_param(chat);
dc_chat_unref(chat);
free(grpimage as *mut libc::c_void);
@@ -1274,17 +1265,20 @@ unsafe fn create_or_lookup_group(
params![chat_id as i32],
)
.ok();
if skip.is_null() || !dc_addr_cmp(&self_addr, as_str(skip)) {
if skip.is_null() || !addr_cmp(&self_addr, as_str(skip)) {
dc_add_to_chat_contacts_table(context, chat_id, 1);
}
if from_id > 9 {
if !dc_addr_equals_contact(context, &self_addr, from_id as u32)
&& (skip.is_null()
|| !dc_addr_equals_contact(
context,
to_string(skip),
from_id as u32,
))
if !Contact::addr_equals_contact(
context,
&self_addr,
from_id as u32,
) && (skip.is_null()
|| !Contact::addr_equals_contact(
context,
to_string(skip),
from_id as u32,
))
{
dc_add_to_chat_contacts_table(
context,
@@ -1296,9 +1290,13 @@ unsafe fn create_or_lookup_group(
i = 0;
while i < to_ids_cnt {
let to_id = dc_array_get_id(to_ids, i as size_t);
if !dc_addr_equals_contact(context, &self_addr, to_id)
if !Contact::addr_equals_contact(context, &self_addr, to_id)
&& (skip.is_null()
|| !dc_addr_equals_contact(context, to_string(skip), to_id))
|| !Contact::addr_equals_contact(
context,
to_string(skip),
to_id,
))
{
dc_add_to_chat_contacts_table(context, chat_id, to_id);
}
@@ -1342,8 +1340,6 @@ unsafe fn create_or_lookup_group(
}
free(grpid as *mut libc::c_void);
free(grpname as *mut libc::c_void);
free(better_msg as *mut libc::c_void);
free(failure_reason as *mut libc::c_void);
if !ret_chat_id.is_null() {
*ret_chat_id = chat_id
@@ -1429,11 +1425,12 @@ unsafe fn create_or_lookup_adhoc_group(
{
grpname = dc_strdup(mime_parser.subject)
} else {
grpname = dc_stock_str_repl_int(
context,
4,
dc_array_get_cnt(member_ids) as libc::c_int,
)
grpname = context
.stock_string_repl_int(
StockMessage::Member,
dc_array_get_cnt(member_ids) as libc::c_int,
)
.strdup();
}
chat_id =
create_group_record(context, grpid, grpname, create_blocked, 0);
@@ -1542,9 +1539,10 @@ fn hex_hash(s: impl AsRef<str>) -> *const libc::c_char {
let bytes = s.as_ref().as_bytes();
let result = Sha256::digest(bytes);
let result_hex = hex::encode(&result[..8]);
unsafe { to_cstring(result_hex) as *const _ }
unsafe { result_hex.strdup() as *const _ }
}
#[allow(non_snake_case)]
unsafe fn search_chat_ids_by_contact_ids(
context: &Context,
unsorted_contact_ids: *const dc_array_t,
@@ -1569,7 +1567,7 @@ unsafe fn search_chat_ids_by_contact_ids(
i += 1
}
if !(dc_array_get_cnt(contact_ids) == 0) {
dc_array_sort_ids(contact_ids);
(*contact_ids).sort_ids();
contact_ids_str =
dc_array_get_string(contact_ids, b",\x00" as *const u8 as *const libc::c_char);
@@ -1623,26 +1621,21 @@ unsafe fn check_verified_properties(
to_ids: *const dc_array_t,
failure_reason: *mut *mut libc::c_char,
) -> libc::c_int {
let contact = dc_contact_new(context);
let verify_fail = |reason: String| {
*failure_reason = to_cstring(format!("{}. See \"Info\" for details.", reason));
*failure_reason = format!("{}. See \"Info\" for details.", reason).strdup();
warn!(context, 0, "{}", reason);
};
let cleanup = || {
dc_contact_unref(contact);
let contact = match Contact::load_from_db(context, from_id) {
Ok(contact) => contact,
Err(_err) => {
verify_fail("Internal Error; cannot load contact".into());
return 0;
}
};
if !dc_contact_load_from_db(contact, &context.sql, from_id) {
verify_fail("Internal Error; cannot load contact".into());
cleanup();
return 0;
}
if 0 == mimeparser.e2ee_helper.encrypted {
verify_fail("This message is not encrypted".into());
cleanup();
return 0;
}
@@ -1651,18 +1644,18 @@ unsafe fn check_verified_properties(
// this check is skipped for SELF as there is no proper SELF-peerstate
// and results in group-splits otherwise.
if from_id != 1 {
let peerstate = Peerstate::from_addr(context, &context.sql, as_str((*contact).addr));
let peerstate = Peerstate::from_addr(context, &context.sql, contact.get_addr());
if peerstate.is_none() || dc_contact_is_verified_ex(contact, peerstate.as_ref()) != 2 {
if peerstate.is_none()
|| contact.is_verified_ex(peerstate.as_ref()) != VerifiedStatus::BidirectVerified
{
verify_fail("The sender of this message is not verified.".into());
cleanup();
return 0;
}
if let Some(peerstate) = peerstate {
if !peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures) {
verify_fail("The message was sent with non-verified encryption.".into());
cleanup();
return 0;
}
}
@@ -1684,7 +1677,6 @@ unsafe fn check_verified_properties(
);
if rows.is_err() {
cleanup();
return 0;
}
for (to_addr, mut is_verified) in rows.unwrap().into_iter() {
@@ -1705,7 +1697,7 @@ unsafe fn check_verified_properties(
context,
0,
"{} has verfied {}.",
as_str((*contact).addr),
contact.get_addr(),
to_addr,
);
let fp = peerstate.gossip_key_fingerprint.clone();
@@ -1721,7 +1713,6 @@ unsafe fn check_verified_properties(
"{} is not a member of this verified group",
to_addr
));
cleanup();
return 0;
}
}
@@ -1729,14 +1720,13 @@ unsafe fn check_verified_properties(
1
}
unsafe fn set_better_msg(mime_parser: &dc_mimeparser_t, better_msg: *mut *mut libc::c_char) {
if !(*better_msg).is_null() && carray_count((*mime_parser).parts) > 0 as libc::c_uint {
let mut part: *mut dc_mimepart_t =
carray_get(mime_parser.parts, 0 as libc::c_uint) as *mut dc_mimepart_t;
unsafe fn set_better_msg<T: AsRef<str>>(mime_parser: &mut dc_mimeparser_t, better_msg: T) {
let msg = better_msg.as_ref();
if msg.len() > 0 && !mime_parser.parts.is_empty() {
let part = &mut mime_parser.parts[0];
if (*part).type_0 == 10 {
free((*part).msg as *mut libc::c_void);
(*part).msg = *better_msg;
*better_msg = 0 as *mut libc::c_char
free(part.msg as *mut libc::c_void);
part.msg = msg.strdup();
}
};
}
@@ -1903,7 +1893,7 @@ fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) ->
unsafe fn dc_add_or_lookup_contacts_by_address_list(
context: &Context,
adr_list: *const mailimf_address_list,
origin: libc::c_int,
origin: Origin,
ids: *mut dc_array_t,
check_self: *mut libc::c_int,
) {
@@ -1953,7 +1943,7 @@ unsafe fn dc_add_or_lookup_contacts_by_address_list(
unsafe fn dc_add_or_lookup_contacts_by_mailbox_list(
context: &Context,
mb_list: *const mailimf_mailbox_list,
origin: libc::c_int,
origin: Origin,
ids: *mut dc_array_t,
check_self: *mut libc::c_int,
) {
@@ -1991,7 +1981,7 @@ unsafe fn add_or_lookup_contact_by_addr(
context: &Context,
display_name_enc: *const libc::c_char,
addr_spec: *const libc::c_char,
origin: libc::c_int,
origin: Origin,
ids: *mut dc_array_t,
mut check_self: *mut libc::c_int,
) {
@@ -2009,7 +1999,7 @@ unsafe fn add_or_lookup_contact_by_addr(
.get_config(context, "configured_addr")
.unwrap_or_default();
if dc_addr_cmp(self_addr, as_str(addr_spec)) {
if addr_cmp(self_addr, as_str(addr_spec)) {
*check_self = 1;
}
@@ -2017,20 +2007,15 @@ unsafe fn add_or_lookup_contact_by_addr(
return;
}
/* add addr_spec if missing, update otherwise */
let mut display_name_dec = 0 as *mut libc::c_char;
let mut display_name_dec = "".to_string();
if !display_name_enc.is_null() {
display_name_dec = dc_decode_header_words(display_name_enc);
dc_normalize_name(display_name_dec);
let tmp = as_str(dc_decode_header_words(display_name_enc));
display_name_dec = normalize_name(&tmp);
}
/*can be NULL*/
let row_id = dc_add_or_lookup_contact(
context,
display_name_dec,
addr_spec,
origin,
0 as *mut libc::c_int,
);
free(display_name_dec as *mut libc::c_void);
let row_id = Contact::add_or_lookup(context, display_name_dec, as_str(addr_spec), origin)
.map(|(id, _)| id)
.unwrap_or_default();
if 0 != row_id {
if !dc_array_search_id(ids, row_id, 0 as *mut size_t) {
dc_array_add_id(ids, row_id);

View File

@@ -12,10 +12,13 @@ pub struct dc_saxparser_t {
}
/* len is only informational, text is already null-terminated */
#[allow(non_camel_case_types)]
pub type dc_saxparser_text_cb_t =
Option<unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: libc::c_int) -> ()>;
#[allow(non_camel_case_types)]
pub type dc_saxparser_endtag_cb_t =
Option<unsafe fn(_: *mut libc::c_void, _: *const libc::c_char) -> ()>;
#[allow(non_camel_case_types)]
pub type dc_saxparser_starttag_cb_t = Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: *mut *mut libc::c_char) -> (),
>;
@@ -77,7 +80,7 @@ pub unsafe fn dc_saxparser_set_text_handler(
}
pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *const libc::c_char) {
let current_block: u64;
let mut is_valid = false;
let mut bak: libc::c_char;
let buf_start: *mut libc::c_char;
let mut last_text_start: *mut libc::c_char;
@@ -96,7 +99,7 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
p = buf_start;
loop {
if !(0 != *p) {
current_block = 13425230902034816933;
is_valid = true;
break;
}
if *p as libc::c_int == '<' as i32 {
@@ -110,7 +113,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
if strncmp(p, b"!--\x00" as *const u8 as *const libc::c_char, 3) == 0i32 {
p = strstr(p, b"-->\x00" as *const u8 as *const libc::c_char);
if p.is_null() {
current_block = 7627180618761592946;
break;
}
p = p.offset(3isize)
@@ -134,7 +136,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
strlen(text_beg),
'c' as i32 as libc::c_char,
);
current_block = 7627180618761592946;
break;
}
} else if strncmp(p, b"!DOCTYPE\x00" as *const u8 as *const libc::c_char, 8) == 0i32 {
@@ -146,13 +147,11 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
}
if *p as libc::c_int == 0i32 {
/* unclosed doctype */
current_block = 7627180618761592946;
break;
} else if *p as libc::c_int == '[' as i32 {
p = strstr(p, b"]>\x00" as *const u8 as *const libc::c_char);
if p.is_null() {
/* unclosed inline doctype */
current_block = 7627180618761592946;
break;
} else {
p = p.offset(2isize)
@@ -164,7 +163,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
p = strstr(p, b"?>\x00" as *const u8 as *const libc::c_char);
if p.is_null() {
/* unclosed processing instruction */
current_block = 7627180618761592946;
break;
} else {
p = p.offset(2isize)
@@ -325,7 +323,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
p = strchr(p, '>' as i32);
if p.is_null() {
/* unclosed start-tag or end-tag */
current_block = 7627180618761592946;
break;
} else {
p = p.offset(1isize)
@@ -336,16 +333,13 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
p = p.offset(1isize)
}
}
match current_block {
13425230902034816933 => {
call_text_cb(
saxparser,
last_text_start,
p.wrapping_offset_from(last_text_start) as size_t,
'&' as i32 as libc::c_char,
);
}
_ => {}
if is_valid {
call_text_cb(
saxparser,
last_text_start,
p.wrapping_offset_from(last_text_start) as size_t,
'&' as i32 as libc::c_char,
);
}
do_free_attr(attr.as_mut_ptr(), free_attr.as_mut_ptr());
free(buf_start as *mut libc::c_void);
@@ -492,19 +486,19 @@ unsafe fn xml_decode(mut s: *mut libc::c_char, type_0: libc::c_char) -> *mut lib
&& (type_0 as libc::c_int == '&' as i32 || type_0 as libc::c_int == ' ' as i32)
{
b = 0;
while !s_ent[b as usize].is_null()
while !S_ENT[b as usize].is_null()
&& 0 != strncmp(
s.offset(1isize),
s_ent[b as usize],
strlen(s_ent[b as usize]),
S_ENT[b as usize],
strlen(S_ENT[b as usize]),
)
{
b += 2;
}
let fresh5 = b;
b = b + 1;
if !s_ent[fresh5 as usize].is_null() {
c = strlen(s_ent[b as usize]) as isize;
if !S_ENT[fresh5 as usize].is_null() {
c = strlen(S_ENT[b as usize]) as isize;
e = strchr(s, ';' as i32);
if c - 1 > e.wrapping_offset_from(s) as isize {
d = s.wrapping_offset_from(r) as isize;
@@ -532,7 +526,7 @@ unsafe fn xml_decode(mut s: *mut libc::c_char, type_0: libc::c_char) -> *mut lib
e.offset(1isize) as *const libc::c_void,
strlen(e),
);
strncpy(s, s_ent[b as usize], c as usize);
strncpy(s, S_ENT[b as usize], c as usize);
} else {
s = s.offset(1isize)
}
@@ -566,7 +560,7 @@ NB: SAX = Simple API for XML */
/* ******************************************************************************
* Decoding text
******************************************************************************/
static mut s_ent: [*const libc::c_char; 508] = [
static mut S_ENT: [*const libc::c_char; 508] = [
b"lt;\x00" as *const u8 as *const libc::c_char,
b"<\x00" as *const u8 as *const libc::c_char,
b"gt;\x00" as *const u8 as *const libc::c_char,

View File

@@ -1,25 +1,27 @@
use std::ffi::CString;
use mmime::mailimf_types::*;
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use crate::aheader::EncryptPreference;
use crate::constants::Event;
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_contact::*;
use crate::dc_e2ee::*;
use crate::dc_lot::*;
use crate::dc_mimeparser::*;
use crate::dc_msg::*;
use crate::dc_param::*;
use crate::dc_qr::*;
use crate::dc_stock::*;
use crate::dc_strencode::*;
use crate::dc_token::*;
use crate::dc_tools::*;
use crate::key::*;
use crate::param::*;
use crate::peerstate::*;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
@@ -38,17 +40,17 @@ pub unsafe fn dc_get_securejoin_qr(
let mut chat = 0 as *mut Chat;
let mut group_name = 0 as *mut libc::c_char;
let mut group_name_urlencoded = 0 as *mut libc::c_char;
let mut qr = None;
let mut qr: Option<String> = None;
dc_ensure_secret_key_exists(context);
invitenumber = dc_token_lookup(context, DC_TOKEN_INVITENUMBER, group_chat_id);
if invitenumber.is_null() {
invitenumber = dc_create_id();
invitenumber = dc_create_id().strdup();
dc_token_save(context, DC_TOKEN_INVITENUMBER, group_chat_id, invitenumber);
}
auth = dc_token_lookup(context, DC_TOKEN_AUTH, group_chat_id);
if auth.is_null() {
auth = dc_create_id();
auth = dc_create_id().strdup();
dc_token_save(context, DC_TOKEN_AUTH, group_chat_id, auth);
}
let self_addr = context.sql.get_config(context, "configured_addr");
@@ -62,7 +64,7 @@ pub unsafe fn dc_get_securejoin_qr(
free(group_name_urlencoded as *mut libc::c_void);
if let Some(qr) = qr {
to_cstring(qr)
qr.strdup()
} else {
std::ptr::null_mut()
}
@@ -85,8 +87,8 @@ pub unsafe fn dc_get_securejoin_qr(
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
}
let self_addr_urlencoded = utf8_percent_encode(&self_addr, DEFAULT_ENCODE_SET).to_string();
let self_name_urlencoded = utf8_percent_encode(&self_name, DEFAULT_ENCODE_SET).to_string();
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);
@@ -261,29 +263,33 @@ unsafe fn send_handshake_msg(
grpid: *const libc::c_char,
) {
let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context);
(*msg).type_0 = 10i32;
(*msg).text = dc_mprintf(
b"Secure-Join: %s\x00" as *const u8 as *const libc::c_char,
step,
);
(*msg).hidden = 1i32;
dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 7);
dc_param_set((*msg).param, DC_PARAM_CMD_ARG as i32, step);
(*msg).type_0 = Viewtype::Text;
(*msg).text = Some(format!("Secure-Join: {}", to_string(step)));
(*msg).hidden = 1;
(*msg).param.set_int(Param::Cmd, 7);
if step.is_null() {
(*msg).param.remove(Param::Arg);
} else {
(*msg).param.set(Param::Arg, as_str(step));
}
if !param2.is_null() {
dc_param_set((*msg).param, DC_PARAM_CMD_ARG2 as i32, param2);
(*msg).param.set(Param::Arg2, as_str(param2));
}
if !fingerprint.is_null() {
dc_param_set((*msg).param, DC_PARAM_CMD_ARG3 as i32, fingerprint);
(*msg).param.set(Param::Arg3, as_str(fingerprint));
}
if !grpid.is_null() {
dc_param_set((*msg).param, DC_PARAM_CMD_ARG4 as i32, grpid);
(*msg).param.set(Param::Arg4, as_str(grpid));
}
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(step, b"vc-request\x00" as *const u8 as *const libc::c_char) == 0i32
{
dc_param_set_int((*msg).param, DC_PARAM_FORCE_PLAINTEXT as i32, 1);
(*msg).param.set_int(
Param::ForcePlaintext,
ForcePlaintext::AddAutocryptHeader as i32,
);
} else {
dc_param_set_int((*msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 1);
(*msg).param.set_int(Param::GuranteeE2ee, 1);
}
dc_send_msg(context, contact_chat_id, msg);
dc_msg_unref(msg);
@@ -309,30 +315,23 @@ unsafe fn fingerprint_equals_sender(
return 0;
}
let mut fingerprint_equal: libc::c_int = 0i32;
let contacts: *mut dc_array_t = dc_get_chat_contacts(context, contact_chat_id);
let contact: *mut dc_contact_t = dc_contact_new(context);
let contacts = dc_get_chat_contacts(context, contact_chat_id);
if !(dc_array_get_cnt(contacts) != 1) {
if !dc_contact_load_from_db(
contact,
&context.sql,
dc_array_get_id(contacts, 0i32 as size_t),
) {
if let Ok(contact) = Contact::load_from_db(context, dc_array_get_id(contacts, 0)) {
if let Some(peerstate) = Peerstate::from_addr(context, &context.sql, contact.get_addr())
{
let fingerprint_normalized = dc_normalize_fingerprint(as_str(fingerprint));
if peerstate.public_key_fingerprint.is_some()
&& &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap()
{
fingerprint_equal = 1;
}
}
} else {
return 0;
}
if let Some(peerstate) =
Peerstate::from_addr(context, &context.sql, as_str((*contact).addr))
{
let fingerprint_normalized = dc_normalize_fingerprint(as_str(fingerprint));
if peerstate.public_key_fingerprint.is_some()
&& &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap()
{
fingerprint_equal = 1;
}
}
}
dc_contact_unref(contact);
dc_array_unref(contacts);
fingerprint_equal
@@ -354,7 +353,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
let mut contact_chat_id_blocked: libc::c_int = 0i32;
let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char;
let mut ret: libc::c_int = 0i32;
let mut contact: *mut dc_contact_t = 0 as *mut dc_contact_t;
if !(contact_id <= 9i32 as libc::c_uint) {
step = lookup_field(mimeparser, "Secure-Join");
if !step.is_null() {
@@ -566,7 +565,11 @@ pub unsafe fn dc_handle_securejoin_handshake(
);
current_block = 4378276786830486580;
} else {
dc_scaleup_contact_origin(context, contact_id, 0x1000000i32);
Contact::scaleup_origin_by_id(
context,
contact_id,
Origin::SecurejoinInvited,
);
info!(context, 0, "Auth verified.",);
secure_connection_established(context, contact_chat_id);
context.call_cb(
@@ -693,16 +696,23 @@ pub unsafe fn dc_handle_securejoin_handshake(
);
current_block = 4378276786830486580;
} else {
dc_scaleup_contact_origin(context, contact_id, 0x2000000i32);
Contact::scaleup_origin_by_id(
context,
contact_id,
Origin::SecurejoinJoined,
);
context.call_cb(
Event::CONTACTS_CHANGED,
0i32 as uintptr_t,
0i32 as uintptr_t,
);
if 0 != join_vg {
if 0 == dc_addr_equals_self(
if !addr_equals_self(
context,
lookup_field(mimeparser, "Chat-Group-Member-Added"),
as_str(lookup_field(
mimeparser,
"Chat-Group-Member-Added",
)),
) {
info!(
context,
@@ -750,22 +760,26 @@ pub unsafe fn dc_handle_securejoin_handshake(
==== Alice - the inviter side ====
==== Step 8 in "Out-of-band verified groups" protocol ====
============================================================ */
contact = dc_get_contact(context, contact_id);
if contact.is_null() || 0 == dc_contact_is_verified(contact) {
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
if contact.is_verified() == VerifiedStatus::Unverified {
warn!(context, 0, "vg-member-added-received invalid.",);
current_block = 4378276786830486580;
} else {
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
contact_id as uintptr_t,
800i32 as uintptr_t,
);
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
contact_id as uintptr_t,
1000i32 as uintptr_t,
);
current_block = 10256747982273457880;
}
} else {
warn!(context, 0, "vg-member-added-received invalid.",);
current_block = 4378276786830486580;
} else {
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
contact_id as uintptr_t,
800i32 as uintptr_t,
);
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
contact_id as uintptr_t,
1000i32 as uintptr_t,
);
current_block = 10256747982273457880;
}
} else {
current_block = 10256747982273457880;
@@ -780,7 +794,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
}
}
}
dc_contact_unref(contact);
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);
@@ -796,24 +810,20 @@ unsafe fn end_bobs_joining(context: &Context, status: libc::c_int) {
unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint32_t) {
let contact_id: uint32_t = chat_id_2_contact_id(context, contact_chat_id);
let contact: *mut dc_contact_t = dc_get_contact(context, contact_id);
let msg: *mut libc::c_char = dc_stock_str_repl_string(
context,
35i32,
if !contact.is_null() {
(*contact).addr
} else {
b"?\x00" as *const u8 as *const libc::c_char
},
);
dc_add_device_msg(context, contact_chat_id, msg);
let contact = Contact::get_by_id(context, contact_id);
let addr = if let Ok(ref contact) = contact {
contact.get_addr()
} 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());
context.call_cb(
Event::CHAT_MODIFIED,
contact_chat_id as uintptr_t,
0i32 as uintptr_t,
);
free(msg as *mut libc::c_void);
dc_contact_unref(contact);
}
unsafe fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> *const libc::c_char {
@@ -839,20 +849,18 @@ unsafe fn could_not_establish_secure_connection(
details: *const libc::c_char,
) {
let contact_id: uint32_t = chat_id_2_contact_id(context, contact_chat_id);
let contact = dc_get_contact(context, contact_id);
let msg: *mut libc::c_char = dc_stock_str_repl_string(
context,
36i32,
if !contact.is_null() {
(*contact).addr
let contact = Contact::get_by_id(context, contact_id);
let msg = context.stock_string_repl_str(
StockMessage::ContactNotVerified,
if let Ok(ref contact) = contact {
contact.get_addr()
} else {
b"?\x00" as *const u8 as *const libc::c_char
"?"
},
);
dc_add_device_msg(context, contact_chat_id, msg);
error!(context, 0, "{} ({})", as_str(msg), to_string(details),);
free(msg as *mut libc::c_void);
dc_contact_unref(contact);
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));
}
unsafe fn mark_peer_as_verified(
@@ -939,15 +947,15 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate)
&mut contact_chat_id,
0 as *mut libc::c_int,
);
let c_addr_ptr = if let Some(ref addr) = peerstate.addr {
to_cstring(addr)
} else {
std::ptr::null_mut()
let peeraddr: &str = match peerstate.addr {
Some(ref addr) => &addr,
None => "",
};
let msg = dc_stock_str_repl_string(context, 37, c_addr_ptr);
dc_add_device_msg(context, contact_chat_id, msg);
free(msg as *mut libc::c_void);
free(c_addr_ptr as *mut _);
let msg = CString::new(
context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr),
)
.unwrap();
dc_add_device_msg(context, contact_chat_id, msg.as_ptr());
context.call_cb(
Event::CHAT_MODIFIED,
contact_chat_id as uintptr_t,

View File

@@ -1,6 +1,5 @@
use crate::dc_dehtml::*;
use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
#[derive(Copy, Clone)]
@@ -11,235 +10,214 @@ pub struct dc_simplify_t {
pub is_cut_at_end: libc::c_int,
}
pub unsafe fn dc_simplify_new() -> *mut dc_simplify_t {
let simplify: *mut dc_simplify_t;
simplify = calloc(1, ::std::mem::size_of::<dc_simplify_t>()) as *mut dc_simplify_t;
assert!(!simplify.is_null());
simplify
}
pub unsafe fn dc_simplify_unref(simplify: *mut dc_simplify_t) {
if simplify.is_null() {
return;
impl dc_simplify_t {
pub fn new() -> Self {
dc_simplify_t {
is_forwarded: 0,
is_cut_at_begin: 0,
is_cut_at_end: 0,
}
}
free(simplify as *mut libc::c_void);
}
/* Simplify and normalise text: Remove quotes, signatures, unnecessary
lineends etc.
The data returned from Simplify() must be free()'d when no longer used, private */
pub unsafe fn dc_simplify_simplify(
mut simplify: *mut dc_simplify_t,
in_unterminated: *const libc::c_char,
in_bytes: libc::c_int,
is_html: libc::c_int,
is_msgrmsg: libc::c_int,
) -> *mut libc::c_char {
/* create a copy of the given buffer */
let mut out: *mut libc::c_char;
let mut temp: *mut libc::c_char;
if simplify.is_null() || in_unterminated.is_null() || in_bytes <= 0i32 {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
(*simplify).is_forwarded = 0i32;
(*simplify).is_cut_at_begin = 0i32;
(*simplify).is_cut_at_end = 0i32;
out = strndup(
in_unterminated as *mut libc::c_char,
in_bytes as libc::c_ulong,
);
if out.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
if 0 != is_html {
temp = dc_dehtml(out);
/// Simplify and normalise text: Remove quotes, signatures, unnecessary
/// lineends etc.
/// The data returned from simplify() must be free()'d when no longer used.
pub unsafe fn simplify(
&mut self,
in_unterminated: *const libc::c_char,
in_bytes: libc::c_int,
is_html: libc::c_int,
is_msgrmsg: libc::c_int,
) -> *mut libc::c_char {
if in_bytes <= 0 {
return "".strdup();
}
/* 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;
out = strndup(
in_unterminated as *mut libc::c_char,
in_bytes as libc::c_ulong,
);
if out.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
if 0 != is_html {
temp = dc_dehtml(out);
if !temp.is_null() {
free(out as *mut libc::c_void);
out = temp
}
}
dc_remove_cr_chars(out);
temp = self.simplify_plain_text(out, is_msgrmsg);
if !temp.is_null() {
free(out as *mut libc::c_void);
out = temp
}
}
dc_remove_cr_chars(out);
temp = dc_simplify_simplify_plain_text(simplify, out, is_msgrmsg);
if !temp.is_null() {
free(out as *mut libc::c_void);
out = temp
}
dc_remove_cr_chars(out);
dc_remove_cr_chars(out);
out
}
out
}
/**
* Simplify Plain Text
*/
unsafe fn dc_simplify_simplify_plain_text(
mut simplify: *mut dc_simplify_t,
buf_terminated: *const libc::c_char,
is_msgrmsg: libc::c_int,
) -> *mut libc::c_char {
/* This function ...
... removes all text after the line `-- ` (footer mark)
... removes full quotes at the beginning and at the end of the text -
these are all lines starting with the character `>`
... remove a non-empty line before the removed quote (contains sth. like "On 2.9.2016, Bjoern wrote:" in different formats and lanugages) */
/* split the given buffer into lines */
let lines: *mut carray = dc_split_into_lines(buf_terminated);
let mut l: libc::c_int;
let mut l_first: libc::c_int = 0i32;
/* if l_last is -1, there are no lines */
let mut l_last: libc::c_int =
carray_count(lines).wrapping_sub(1i32 as libc::c_uint) as libc::c_int;
let mut line: *mut libc::c_char;
let mut footer_mark: libc::c_int = 0i32;
l = l_first;
while l <= l_last {
line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char;
if strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32
{
footer_mark = 1i32
}
if strcmp(line, b"--\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"---\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"----\x00" as *const u8 as *const libc::c_char) == 0i32
{
footer_mark = 1i32;
(*simplify).is_cut_at_end = 1i32
}
if 0 != footer_mark {
l_last = l - 1i32;
/* done */
break;
} else {
l += 1
}
}
if l_last - l_first + 1i32 >= 3i32 {
let line0: *mut libc::c_char =
carray_get(lines, l_first as libc::c_uint) as *mut libc::c_char;
let line1: *mut libc::c_char =
carray_get(lines, (l_first + 1i32) as libc::c_uint) as *mut libc::c_char;
let line2: *mut libc::c_char =
carray_get(lines, (l_first + 2i32) as libc::c_uint) as *mut libc::c_char;
if strcmp(
line0,
b"---------- Forwarded message ----------\x00" as *const u8 as *const libc::c_char,
) == 0i32
&& strncmp(line1, b"From: \x00" as *const u8 as *const libc::c_char, 6) == 0i32
&& *line2.offset(0isize) as libc::c_int == 0i32
{
(*simplify).is_forwarded = 1i32;
l_first += 3i32
}
}
l = l_first;
while l <= l_last {
line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char;
if strncmp(line, b"-----\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"_____\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"=====\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"*****\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"~~~~~\x00" as *const u8 as *const libc::c_char, 5) == 0i32
{
l_last = l - 1i32;
(*simplify).is_cut_at_end = 1i32;
/* done */
break;
} else {
l += 1
}
}
if 0 == is_msgrmsg {
let mut l_lastQuotedLine: libc::c_int = -1i32;
l = l_last;
while l >= l_first {
line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char;
if is_plain_quote(line) {
l_lastQuotedLine = l
} else if !is_empty_line(line) {
/**
* Simplify Plain Text
*/
#[allow(non_snake_case)]
unsafe fn simplify_plain_text(
&mut self,
buf_terminated: *const libc::c_char,
is_msgrmsg: libc::c_int,
) -> *mut libc::c_char {
/* This function ...
... removes all text after the line `-- ` (footer mark)
... removes full quotes at the beginning and at the end of the text -
these are all lines starting with the character `>`
... remove a non-empty line before the removed quote (contains sth. like "On 2.9.2016, Bjoern wrote:" in different formats and lanugages) */
/* split the given buffer into lines */
let lines = dc_split_into_lines(buf_terminated);
let mut l_first: usize = 0;
let mut l_last = lines.len();
let mut line: *mut libc::c_char;
let mut footer_mark: libc::c_int = 0i32;
for l in l_first..l_last {
line = lines[l];
if strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32
{
footer_mark = 1i32
}
if strcmp(line, b"--\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"---\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"----\x00" as *const u8 as *const libc::c_char) == 0i32
{
footer_mark = 1i32;
self.is_cut_at_end = 1i32
}
if 0 != footer_mark {
l_last = l;
/* done */
break;
}
l -= 1
}
if l_lastQuotedLine != -1i32 {
l_last = l_lastQuotedLine - 1i32;
(*simplify).is_cut_at_end = 1i32;
if l_last > 0i32 {
if is_empty_line(carray_get(lines, l_last as libc::c_uint) as *mut libc::c_char) {
l_last -= 1
}
}
if l_last > 0i32 {
line = carray_get(lines, l_last as libc::c_uint) as *mut libc::c_char;
if is_quoted_headline(line) {
l_last -= 1
}
if l_last > l_first + 2 {
let line0: *mut libc::c_char = lines[l_first];
let line1: *mut libc::c_char = lines[l_first + 1];
let line2: *mut libc::c_char = lines[l_first + 2];
if strcmp(
line0,
b"---------- Forwarded message ----------\x00" as *const u8 as *const libc::c_char,
) == 0i32
&& 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;
l_first += 3
}
}
}
if 0 == is_msgrmsg {
let mut l_lastQuotedLine_0: libc::c_int = -1i32;
let mut hasQuotedHeadline: libc::c_int = 0i32;
l = l_first;
while l <= l_last {
line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char;
if is_plain_quote(line) {
l_lastQuotedLine_0 = l
} else if !is_empty_line(line) {
if is_quoted_headline(line) && 0 == hasQuotedHeadline && l_lastQuotedLine_0 == -1i32
{
hasQuotedHeadline = 1i32
} else {
/* non-quoting line found */
for l in l_first..l_last {
line = lines[l];
if strncmp(line, b"-----\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"_____\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"=====\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"*****\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"~~~~~\x00" as *const u8 as *const libc::c_char, 5) == 0i32
{
l_last = l;
self.is_cut_at_end = 1i32;
/* done */
break;
}
}
if 0 == is_msgrmsg {
let mut l_lastQuotedLine = None;
for l in (l_first..l_last).rev() {
line = lines[l];
if is_plain_quote(line) {
l_lastQuotedLine = Some(l)
} else if !is_empty_line(line) {
break;
}
}
l += 1
}
if l_lastQuotedLine_0 != -1i32 {
l_first = l_lastQuotedLine_0 + 1i32;
(*simplify).is_cut_at_begin = 1i32
}
}
/* re-create buffer from the remaining lines */
let mut ret = String::new();
if 0 != (*simplify).is_cut_at_begin {
ret += "[...]";
}
/* we write empty lines only in case and non-empty line follows */
let mut pending_linebreaks: libc::c_int = 0i32;
let mut content_lines_added: libc::c_int = 0i32;
l = l_first;
while l <= l_last {
line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char;
if is_empty_line(line) {
pending_linebreaks += 1
} else {
if 0 != content_lines_added {
if pending_linebreaks > 2i32 {
pending_linebreaks = 2i32
if l_lastQuotedLine.is_some() {
l_last = l_lastQuotedLine.unwrap();
self.is_cut_at_end = 1i32;
if l_last > 1 {
if is_empty_line(lines[l_last - 1]) {
l_last -= 1
}
}
while 0 != pending_linebreaks {
ret += "\n";
pending_linebreaks -= 1
if l_last > 1 {
line = lines[l_last - 1];
if is_quoted_headline(line) {
l_last -= 1
}
}
}
// the incoming message might contain invalid UTF8
ret += &to_string_lossy(line);
content_lines_added += 1;
pending_linebreaks = 1i32
}
l += 1
}
if 0 != (*simplify).is_cut_at_end
&& (0 == (*simplify).is_cut_at_begin || 0 != content_lines_added)
{
ret += " [...]";
}
dc_free_splitted_lines(lines);
if 0 == is_msgrmsg {
let mut l_lastQuotedLine_0 = None;
let mut hasQuotedHeadline = 0;
for l in l_first..l_last {
line = lines[l];
if is_plain_quote(line) {
l_lastQuotedLine_0 = Some(l)
} else if !is_empty_line(line) {
if is_quoted_headline(line)
&& 0 == hasQuotedHeadline
&& l_lastQuotedLine_0.is_none()
{
hasQuotedHeadline = 1i32
} else {
/* non-quoting line found */
break;
}
}
}
if l_lastQuotedLine_0.is_some() {
l_first = l_lastQuotedLine_0.unwrap() + 1;
self.is_cut_at_begin = 1i32
}
}
/* re-create buffer from the remaining lines */
let mut ret = String::new();
if 0 != self.is_cut_at_begin {
ret += "[...]";
}
/* we write empty lines only in case and non-empty line follows */
let mut pending_linebreaks: libc::c_int = 0i32;
let mut content_lines_added: libc::c_int = 0i32;
for l in l_first..l_last {
line = lines[l];
if is_empty_line(line) {
pending_linebreaks += 1
} else {
if 0 != content_lines_added {
if pending_linebreaks > 2i32 {
pending_linebreaks = 2i32
}
while 0 != pending_linebreaks {
ret += "\n";
pending_linebreaks -= 1
}
}
// the incoming message might contain invalid UTF8
ret += &to_string_lossy(line);
content_lines_added += 1;
pending_linebreaks = 1i32
}
}
if 0 != self.is_cut_at_end && (0 == self.is_cut_at_begin || 0 != content_lines_added) {
ret += " [...]";
}
dc_free_splitted_lines(lines);
to_cstring(ret)
ret.strdup()
}
}
/**
@@ -290,11 +268,11 @@ mod tests {
#[test]
fn test_simplify_trim() {
unsafe {
let simplify: *mut dc_simplify_t = dc_simplify_new();
let mut simplify = dc_simplify_t::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 =
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
assert_eq!(
CStr::from_ptr(plain as *const libc::c_char)
@@ -304,18 +282,17 @@ mod tests {
);
free(plain as *mut libc::c_void);
dc_simplify_unref(simplify);
}
}
#[test]
fn test_simplify_parse_href() {
unsafe {
let simplify: *mut dc_simplify_t = dc_simplify_new();
let mut simplify = dc_simplify_t::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 =
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
assert_eq!(
CStr::from_ptr(plain as *const libc::c_char)
@@ -325,19 +302,18 @@ mod tests {
);
free(plain as *mut libc::c_void);
dc_simplify_unref(simplify);
}
}
#[test]
fn test_simplify_bold_text() {
unsafe {
let simplify: *mut dc_simplify_t = dc_simplify_new();
let mut simplify = dc_simplify_t::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 =
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
assert_eq!(
CStr::from_ptr(plain as *const libc::c_char)
@@ -347,19 +323,18 @@ mod tests {
);
free(plain as *mut libc::c_void);
dc_simplify_unref(simplify);
}
}
#[test]
fn test_simplify_html_encoded() {
unsafe {
let simplify: *mut dc_simplify_t = dc_simplify_new();
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"
as *const u8 as *const libc::c_char;
let plain: *mut libc::c_char =
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
assert_eq!(
strcmp(plain,
@@ -369,7 +344,6 @@ mod tests {
);
free(plain as *mut libc::c_void);
dc_simplify_unref(simplify);
}
}
}

View File

@@ -1,338 +0,0 @@
use crate::constants::Event;
use crate::context::Context;
use crate::dc_contact::*;
use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
/* Return the string with the given ID by calling DC_EVENT_GET_STRING.
The result must be free()'d! */
pub unsafe fn dc_stock_str(context: &Context, id: libc::c_int) -> *mut libc::c_char {
return get_string(context, id, 0i32);
}
unsafe fn get_string(context: &Context, id: libc::c_int, qty: libc::c_int) -> *mut libc::c_char {
let mut ret: *mut libc::c_char;
ret =
context.call_cb(Event::GET_STRING, id as uintptr_t, qty as uintptr_t) as *mut libc::c_char;
if ret.is_null() {
ret = default_string(id)
}
ret
}
/* Add translated strings that are used by the messager backend.
As the logging functions may use these strings, do not log any
errors from here. */
unsafe fn default_string(id: libc::c_int) -> *mut libc::c_char {
// TODO match on enum values /rtn
match id {
1 => {
return dc_strdup(b"No messages.\x00" as *const u8 as
*const libc::c_char)
}
2 => {
return dc_strdup(b"Me\x00" as *const u8 as *const libc::c_char)
}
3 => {
return dc_strdup(b"Draft\x00" as *const u8 as *const libc::c_char)
}
4 => {
return dc_strdup(b"%1$s member(s)\x00" as *const u8 as
*const libc::c_char)
}
6 => {
return dc_strdup(b"%1$s contact(s)\x00" as *const u8 as
*const libc::c_char)
}
7 => {
return dc_strdup(b"Voice message\x00" as *const u8 as
*const libc::c_char)
}
8 => {
return dc_strdup(b"Contact requests\x00" as *const u8 as
*const libc::c_char)
}
9 => {
return dc_strdup(b"Image\x00" as *const u8 as *const libc::c_char)
}
23 => {
return dc_strdup(b"GIF\x00" as *const u8 as *const libc::c_char)
}
10 => {
return dc_strdup(b"Video\x00" as *const u8 as *const libc::c_char)
}
11 => {
return dc_strdup(b"Audio\x00" as *const u8 as *const libc::c_char)
}
12 => {
return dc_strdup(b"File\x00" as *const u8 as *const libc::c_char)
}
66 => {
return dc_strdup(b"Location\x00" as *const u8 as
*const libc::c_char)
}
24 => {
return dc_strdup(b"Encrypted message\x00" as *const u8 as
*const libc::c_char)
}
13 => {
return dc_strdup(b"Sent with my Delta Chat Messenger: https://delta.chat\x00"
as *const u8 as *const libc::c_char)
}
14 => {
return dc_strdup(b"Hello, I\'ve just created the group \"%1$s\" for us.\x00"
as *const u8 as *const libc::c_char)
}
15 => {
return dc_strdup(b"Group name changed from \"%1$s\" to \"%2$s\".\x00"
as *const u8 as *const libc::c_char)
}
16 => {
return dc_strdup(b"Group image changed.\x00" as *const u8 as
*const libc::c_char)
}
17 => {
return dc_strdup(b"Member %1$s added.\x00" as *const u8 as
*const libc::c_char)
}
18 => {
return dc_strdup(b"Member %1$s removed.\x00" as *const u8 as
*const libc::c_char)
}
19 => {
return dc_strdup(b"Group left.\x00" as *const u8 as
*const libc::c_char)
}
64 => {
return dc_strdup(b"Location streaming enabled.\x00" as *const u8
as *const libc::c_char)
}
65 => {
return dc_strdup(b"Location streaming disabled.\x00" as *const u8
as *const libc::c_char)
}
62 => {
return dc_strdup(b"%1$s by %2$s.\x00" as *const u8 as
*const libc::c_char)
}
63 => {
return dc_strdup(b"%1$s by me.\x00" as *const u8 as
*const libc::c_char)
}
25 => {
return dc_strdup(b"End-to-end encryption available.\x00" as
*const u8 as *const libc::c_char)
}
27 => {
return dc_strdup(b"Transport-encryption.\x00" as *const u8 as
*const libc::c_char)
}
28 => {
return dc_strdup(b"No encryption.\x00" as *const u8 as
*const libc::c_char)
}
30 => {
return dc_strdup(b"Fingerprints\x00" as *const u8 as
*const libc::c_char)
}
31 => {
return dc_strdup(b"Return receipt\x00" as *const u8 as
*const libc::c_char)
}
32 => {
return dc_strdup(b"This is a return receipt for the message \"%1$s\".\x00"
as *const u8 as *const libc::c_char)
}
33 => {
return dc_strdup(b"Group image deleted.\x00" as *const u8 as
*const libc::c_char)
}
34 => {
return dc_strdup(b"End-to-end encryption preferred.\x00" as
*const u8 as *const libc::c_char)
}
35 => {
return dc_strdup(b"%1$s verified.\x00" as *const u8 as
*const libc::c_char)
}
36 => {
return dc_strdup(b"Cannot verifiy %1$s\x00" as *const u8 as
*const libc::c_char)
}
37 => {
return dc_strdup(b"Changed setup for %1$s\x00" as *const u8 as
*const libc::c_char)
}
40 => {
return dc_strdup(b"Archived chats\x00" as *const u8 as
*const libc::c_char)
}
41 => {
return dc_strdup(b"Starred messages\x00" as *const u8 as
*const libc::c_char)
}
42 => {
return dc_strdup(b"Autocrypt Setup Message\x00" as *const u8 as
*const libc::c_char)
}
43 => {
return dc_strdup(b"This is the Autocrypt Setup Message used to transfer your key between clients.\n\nTo decrypt and use your key, open the message in an Autocrypt-compliant client and enter the setup code presented on the generating device.\x00"
as *const u8 as *const libc::c_char)
}
50 => {
return dc_strdup(b"Messages I sent to myself\x00" as *const u8 as
*const libc::c_char)
}
29 => {
return dc_strdup(b"This message was encrypted for another setup.\x00"
as *const u8 as *const libc::c_char)
}
60 => {
return dc_strdup(b"Cannot login as %1$s.\x00" as *const u8 as
*const libc::c_char)
}
61 => {
return dc_strdup(b"Response from %1$s: %2$s\x00" as *const u8 as
*const libc::c_char)
}
_ => { }
}
dc_strdup(b"ErrStr\x00" as *const u8 as *const libc::c_char)
}
/* Replaces the first `%1$s` in the given String-ID by the given value.
The result must be free()'d! */
pub unsafe fn dc_stock_str_repl_string(
context: &Context,
id: libc::c_int,
to_insert: *const libc::c_char,
) -> *mut libc::c_char {
let mut ret: *mut libc::c_char = get_string(context, id, 0i32);
dc_str_replace(
&mut ret,
b"%1$s\x00" as *const u8 as *const libc::c_char,
to_insert,
);
dc_str_replace(
&mut ret,
b"%1$d\x00" as *const u8 as *const libc::c_char,
to_insert,
);
ret
}
pub unsafe fn dc_stock_str_repl_int(
context: &Context,
id: libc::c_int,
to_insert_int: libc::c_int,
) -> *mut libc::c_char {
let mut ret: *mut libc::c_char = get_string(context, id, to_insert_int);
let to_insert_str: *mut libc::c_char = dc_mprintf(
b"%i\x00" as *const u8 as *const libc::c_char,
to_insert_int as libc::c_int,
);
dc_str_replace(
&mut ret,
b"%1$s\x00" as *const u8 as *const libc::c_char,
to_insert_str,
);
dc_str_replace(
&mut ret,
b"%1$d\x00" as *const u8 as *const libc::c_char,
to_insert_str,
);
free(to_insert_str as *mut libc::c_void);
ret
}
/* Replaces the first `%1$s` and `%2$s` in the given String-ID by the two given strings.
The result must be free()'d! */
pub unsafe fn dc_stock_str_repl_string2(
context: &Context,
id: libc::c_int,
to_insert: *const libc::c_char,
to_insert2: *const libc::c_char,
) -> *mut libc::c_char {
let mut ret: *mut libc::c_char = get_string(context, id, 0i32);
dc_str_replace(
&mut ret,
b"%1$s\x00" as *const u8 as *const libc::c_char,
to_insert,
);
dc_str_replace(
&mut ret,
b"%1$d\x00" as *const u8 as *const libc::c_char,
to_insert,
);
dc_str_replace(
&mut ret,
b"%2$s\x00" as *const u8 as *const libc::c_char,
to_insert2,
);
dc_str_replace(
&mut ret,
b"%2$d\x00" as *const u8 as *const libc::c_char,
to_insert2,
);
ret
}
/* Misc. */
pub unsafe fn dc_stock_system_msg(
context: &Context,
str_id: libc::c_int,
mut param1: *const libc::c_char,
param2: *const libc::c_char,
from_id: uint32_t,
) -> *mut libc::c_char {
let ret: *mut libc::c_char;
let mut mod_contact: *mut dc_contact_t = 0 as *mut dc_contact_t;
let mut mod_displayname: *mut libc::c_char = 0 as *mut libc::c_char;
let mut from_contact: *mut dc_contact_t = 0 as *mut dc_contact_t;
let mut from_displayname: *mut libc::c_char = 0 as *mut libc::c_char;
if str_id == 17i32 || str_id == 18i32 {
let mod_contact_id: uint32_t = dc_lookup_contact_id_by_addr(context, param1);
if mod_contact_id != 0i32 as libc::c_uint {
mod_contact = dc_get_contact(context, mod_contact_id);
mod_displayname = dc_contact_get_name_n_addr(mod_contact);
param1 = mod_displayname
}
}
let action: *mut libc::c_char = dc_stock_str_repl_string2(context, str_id, param1, param2);
if 0 != from_id {
if 0 != strlen(action)
&& *action.offset(strlen(action).wrapping_sub(1) as isize) as libc::c_int == '.' as i32
{
*action.offset(strlen(action).wrapping_sub(1) as isize) = 0i32 as libc::c_char
}
from_contact = dc_get_contact(context, from_id);
from_displayname = dc_contact_get_display_name(from_contact);
ret = dc_stock_str_repl_string2(
context,
if from_id == 1i32 as libc::c_uint {
63i32
} else {
62i32
},
action,
from_displayname,
)
} else {
ret = dc_strdup(action)
}
free(action as *mut libc::c_void);
free(from_displayname as *mut libc::c_void);
free(mod_displayname as *mut libc::c_void);
dc_contact_unref(from_contact);
dc_contact_unref(mod_contact);
ret
}

View File

@@ -1,4 +1,4 @@
use std::ffi::CStr;
use std::ffi::{CStr, CString};
use charset::Charset;
use mmime::mailmime_decode::*;
@@ -64,11 +64,11 @@ pub unsafe fn dc_urlencode(to_encode: *const libc::c_char) -> *mut libc::c_char
* URL encoding and decoding, RFC 3986
******************************************************************************/
unsafe fn int_2_uppercase_hex(code: libc::c_char) -> libc::c_char {
static mut hex: [libc::c_char; 17] = [
static mut HEX: [libc::c_char; 17] = [
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 0,
];
hex[(code as libc::c_int & 15i32) as usize]
HEX[(code as libc::c_int & 15i32) as usize]
}
pub unsafe fn dc_urldecode(to_decode: *const libc::c_char) -> *mut libc::c_char {
@@ -120,97 +120,90 @@ fn hex_2_int(ch: libc::c_char) -> libc::c_char {
}
pub unsafe fn dc_encode_header_words(to_encode: *const libc::c_char) -> *mut libc::c_char {
let mut current_block: u64;
let mut ok_to_continue = true;
let mut ret_str: *mut libc::c_char = 0 as *mut libc::c_char;
let mut cur: *const libc::c_char = to_encode;
let mmapstr: *mut MMAPString = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
if to_encode.is_null() || mmapstr.is_null() {
current_block = 8550051112593613029;
} else {
current_block = 4644295000439058019;
ok_to_continue = false;
}
loop {
match current_block {
8550051112593613029 => {
if !mmapstr.is_null() {
mmap_string_free(mmapstr);
}
break;
if !ok_to_continue {
if !mmapstr.is_null() {
mmap_string_free(mmapstr);
}
_ => {
if *cur as libc::c_int != '\u{0}' as i32 {
let begin: *const libc::c_char;
let mut end: *const libc::c_char;
let mut do_quote: bool;
let mut quote_words: libc::c_int;
begin = cur;
end = begin;
quote_words = 0i32;
do_quote = true;
while *cur as libc::c_int != '\u{0}' as i32 {
get_word(cur, &mut cur, &mut do_quote);
if !do_quote {
break;
}
quote_words = 1i32;
end = cur;
if *cur as libc::c_int != '\u{0}' as i32 {
cur = cur.offset(1isize)
}
break;
} else {
if *cur as libc::c_int != '\u{0}' as i32 {
let begin: *const libc::c_char;
let mut end: *const libc::c_char;
let mut do_quote: bool;
let mut quote_words: libc::c_int;
begin = cur;
end = begin;
quote_words = 0i32;
do_quote = true;
while *cur as libc::c_int != '\u{0}' as i32 {
get_word(cur, &mut cur, &mut do_quote);
if !do_quote {
break;
}
if 0 != quote_words {
if !quote_word(
b"utf-8\x00" as *const u8 as *const libc::c_char,
mmapstr,
begin,
end.wrapping_offset_from(begin) as size_t,
) {
current_block = 8550051112593613029;
continue;
}
if *end as libc::c_int == ' ' as i32 || *end as libc::c_int == '\t' as i32 {
if mmap_string_append_c(mmapstr, *end).is_null() {
current_block = 8550051112593613029;
continue;
}
end = end.offset(1isize)
}
if *end as libc::c_int != '\u{0}' as i32 {
if mmap_string_append_len(
mmapstr,
end,
cur.wrapping_offset_from(end) as size_t,
)
.is_null()
{
current_block = 8550051112593613029;
continue;
}
}
} else if mmap_string_append_len(
quote_words = 1i32;
end = cur;
if *cur as libc::c_int != '\u{0}' as i32 {
cur = cur.offset(1isize)
}
}
if 0 != quote_words {
if !quote_word(
b"utf-8\x00" as *const u8 as *const libc::c_char,
mmapstr,
begin,
cur.wrapping_offset_from(begin) as size_t,
)
.is_null()
{
current_block = 8550051112593613029;
end.wrapping_offset_from(begin) as size_t,
) {
ok_to_continue = false;
continue;
}
if !(*cur as libc::c_int == ' ' as i32 || *cur as libc::c_int == '\t' as i32) {
current_block = 4644295000439058019;
continue;
if *end as libc::c_int == ' ' as i32 || *end as libc::c_int == '\t' as i32 {
if mmap_string_append_c(mmapstr, *end).is_null() {
ok_to_continue = false;
continue;
}
end = end.offset(1isize)
}
if mmap_string_append_c(mmapstr, *cur).is_null() {
current_block = 8550051112593613029;
continue;
if *end as libc::c_int != '\u{0}' as i32 {
if mmap_string_append_len(
mmapstr,
end,
cur.wrapping_offset_from(end) as size_t,
)
.is_null()
{
ok_to_continue = false;
continue;
}
}
cur = cur.offset(1isize);
current_block = 4644295000439058019;
} else {
ret_str = strdup((*mmapstr).str_0);
current_block = 8550051112593613029;
} else if mmap_string_append_len(
mmapstr,
begin,
cur.wrapping_offset_from(begin) as size_t,
)
.is_null()
{
ok_to_continue = false;
continue;
}
if !(*cur as libc::c_int == ' ' as i32 || *cur as libc::c_int == '\t' as i32) {
continue;
}
if mmap_string_append_c(mmapstr, *cur).is_null() {
ok_to_continue = false;
continue;
}
cur = cur.offset(1isize);
} else {
ret_str = strdup((*mmapstr).str_0);
ok_to_continue = false;
}
}
}
@@ -378,7 +371,7 @@ pub unsafe fn dc_encode_modified_utf7(
if 0 != bitstogo {
let fresh8 = dst;
dst = dst.offset(1);
*fresh8 = base64chars
*fresh8 = BASE64CHARS
[(bitbuf << (6i32 as libc::c_uint).wrapping_sub(bitstogo) & 0x3f) as usize]
}
let fresh9 = dst;
@@ -449,7 +442,7 @@ pub unsafe fn dc_encode_modified_utf7(
bitstogo = bitstogo.wrapping_sub(6i32 as libc::c_uint);
let fresh14 = dst;
dst = dst.offset(1);
*fresh14 = base64chars[(if 0 != bitstogo {
*fresh14 = BASE64CHARS[(if 0 != bitstogo {
bitbuf >> bitstogo
} else {
bitbuf
@@ -465,7 +458,7 @@ pub unsafe fn dc_encode_modified_utf7(
if 0 != bitstogo {
let fresh15 = dst;
dst = dst.offset(1);
*fresh15 = base64chars
*fresh15 = BASE64CHARS
[(bitbuf << (6i32 as libc::c_uint).wrapping_sub(bitstogo) & 0x3f) as usize]
}
let fresh16 = dst;
@@ -482,7 +475,7 @@ pub unsafe fn dc_encode_modified_utf7(
******************************************************************************/
// UTF7 modified base64 alphabet
static mut base64chars: [libc::c_char; 65] = [
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,
@@ -517,7 +510,7 @@ pub unsafe fn dc_decode_modified_utf7(
);
i = 0i32 as libc::c_uint;
while (i as libc::c_ulong) < ::std::mem::size_of::<[libc::c_char; 65]>() as libc::c_ulong {
base64[base64chars[i as usize] as libc::c_uint as usize] = i as libc::c_uchar;
base64[BASE64CHARS[i as usize] as libc::c_uint as usize] = i as libc::c_uchar;
i = i.wrapping_add(1)
}
while *src as libc::c_int != '\u{0}' as i32 {
@@ -710,9 +703,8 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
assert!(!cur.is_null());
let bytes = std::slice::from_raw_parts(cur as *const _, strlen(cur));
let raw = to_cstring(format!("={}", &hex::encode_upper(bytes)[..2]));
libc::memcpy(target as *mut _, raw as *const _, 4);
free(raw as *mut libc::c_void);
let raw = CString::yolo(format!("={}", &hex::encode_upper(bytes)[..2]));
libc::memcpy(target as *mut _, raw.as_ptr() as *const _, 4);
}
#[cfg(test)]

View File

@@ -3,6 +3,7 @@ use crate::dc_tools::*;
use crate::sql;
// Token namespaces
#[allow(non_camel_case_types)]
pub type dc_tokennamespc_t = usize;
pub const DC_TOKEN_AUTH: dc_tokennamespc_t = 110;
pub const DC_TOKEN_INVITENUMBER: dc_tokennamespc_t = 100;
@@ -41,7 +42,7 @@ pub fn dc_token_lookup(
params![namespc as i32, foreign_id as i32],
0,
)
.map(|s| unsafe { to_cstring(s) })
.map(|s| unsafe { s.strdup() })
.unwrap_or_else(|| std::ptr::null_mut())
}

View File

@@ -9,6 +9,7 @@ use rand::{thread_rng, Rng};
use crate::context::Context;
use crate::dc_array::*;
use crate::error::Error;
use crate::types::*;
use crate::x::*;
@@ -16,11 +17,10 @@ const ELLIPSE: &'static str = "[...]";
/* Some tools and enhancements to the used libraries, there should be
no references to Context and other "larger" classes here. */
// for carray etc.
/* ** library-private **********************************************************/
/* math tools */
pub unsafe fn dc_exactly_one_bit_set(v: libc::c_int) -> libc::c_int {
return (0 != v && 0 == v & v - 1i32) as libc::c_int;
pub fn dc_exactly_one_bit_set(v: libc::c_int) -> bool {
0 != v && 0 == v & v - 1i32
}
/* string tools */
@@ -177,14 +177,13 @@ pub unsafe fn dc_trim(buf: *mut libc::c_char) {
/* the result must be free()'d */
pub unsafe fn dc_strlower(in_0: *const libc::c_char) -> *mut libc::c_char {
to_cstring(to_string(in_0).to_lowercase())
to_string(in_0).to_lowercase().strdup()
}
pub unsafe fn dc_strlower_in_place(in_0: *mut libc::c_char) {
let raw = to_cstring(to_string(in_0).to_lowercase());
assert_eq!(strlen(in_0), strlen(raw));
memcpy(in_0 as *mut _, raw as *const _, strlen(in_0));
free(raw as *mut _);
let raw = CString::yolo(to_string(in_0).to_lowercase());
assert_eq!(strlen(in_0), strlen(raw.as_ptr()));
memcpy(in_0 as *mut _, raw.as_ptr() as *const _, strlen(in_0));
}
pub unsafe fn dc_str_contains(
@@ -232,7 +231,7 @@ pub unsafe fn dc_binary_to_uc_hex(buf: *const uint8_t, bytes: size_t) -> *mut li
let buf = std::slice::from_raw_parts(buf, bytes);
let raw = hex::encode_upper(buf);
to_cstring(raw)
raw.strdup()
}
/* remove all \r characters from string */
@@ -262,8 +261,9 @@ pub unsafe fn dc_unify_lineends(buf: *mut libc::c_char) {
}
/* replace bad UTF-8 characters by sequences of `_` (to avoid problems in filenames, we do not use eg. `?`) the function is useful if strings are unexpectingly encoded eg. as ISO-8859-1 */
#[allow(non_snake_case)]
pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) {
let current_block: u64;
let mut OK_TO_CONTINUE = true;
if buf.is_null() {
return;
}
@@ -279,7 +279,6 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) {
ix = p1len;
's_36: loop {
if !(i < ix) {
current_block = 13550086250199790493;
break;
}
c = *p1.offset(i as isize) as libc::c_int;
@@ -292,7 +291,7 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) {
&& *p1.offset((i + 1i32) as isize) as libc::c_int & 0xa0i32 == 0xa0i32
{
/* U+d800 to U+dfff */
current_block = 2775201239069267972;
OK_TO_CONTINUE = false;
break;
} else if c & 0xf0i32 == 0xe0i32 {
n = 2i32
@@ -301,7 +300,7 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) {
} else {
//else if ((c & 0xFC) == 0xF8) { n=4; } /* 111110bb - not valid in https://tools.ietf.org/html/rfc3629 */
//else if ((c & 0xFE) == 0xFC) { n=5; } /* 1111110b - not valid in https://tools.ietf.org/html/rfc3629 */
current_block = 2775201239069267972;
OK_TO_CONTINUE = false;
break;
}
j = 0i32;
@@ -309,25 +308,22 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) {
/* n bytes matching 10bbbbbb follow ? */
i += 1;
if i == ix || *p1.offset(i as isize) as libc::c_int & 0xc0i32 != 0x80i32 {
current_block = 2775201239069267972;
OK_TO_CONTINUE = false;
break 's_36;
}
j += 1
}
i += 1
}
match current_block {
13550086250199790493 => return,
_ => {
while 0 != *p1 {
if *p1 as libc::c_int > 0x7fi32 {
*p1 = '_' as i32 as libc::c_uchar
}
p1 = p1.offset(1isize)
if OK_TO_CONTINUE == false {
while 0 != *p1 {
if *p1 as libc::c_int > 0x7fi32 {
*p1 = '_' as i32 as libc::c_uchar
}
return;
p1 = p1.offset(1isize)
}
};
return;
}
}
pub unsafe fn dc_utf8_strlen(s: *const libc::c_char) -> size_t {
@@ -358,6 +354,7 @@ pub fn dc_truncate_str(buf: &str, approx_chars: usize) -> Cow<str> {
}
}
#[allow(non_snake_case)]
pub unsafe fn dc_truncate_n_unwrap_str(
buf: *mut libc::c_char,
approx_characters: libc::c_int,
@@ -419,47 +416,30 @@ unsafe fn dc_utf8_strnlen(s: *const libc::c_char, n: size_t) -> size_t {
}
/* split string into lines*/
pub unsafe fn dc_split_into_lines(buf_terminated: *const libc::c_char) -> *mut carray {
let lines: *mut carray = carray_new(1024i32 as libc::c_uint);
pub unsafe fn dc_split_into_lines(buf_terminated: *const libc::c_char) -> Vec<*mut libc::c_char> {
let mut lines = Vec::new();
let mut line_chars = 0;
let mut p1: *const libc::c_char = buf_terminated;
let mut line_start: *const libc::c_char = p1;
let mut l_indx: libc::c_uint = 0i32 as libc::c_uint;
while 0 != *p1 {
if *p1 as libc::c_int == '\n' as i32 {
carray_add(
lines,
strndup(line_start, line_chars) as *mut libc::c_void,
&mut l_indx,
);
lines.push(strndup(line_start, line_chars));
p1 = p1.offset(1isize);
line_start = p1;
line_chars = 0;
} else {
p1 = p1.offset(1isize);
line_chars = line_chars.wrapping_add(1)
line_chars += 1;
}
}
carray_add(
lines,
strndup(line_start, line_chars) as *mut libc::c_void,
&mut l_indx,
);
lines.push(strndup(line_start, line_chars));
lines
}
pub unsafe fn dc_free_splitted_lines(lines: *mut carray) {
if !lines.is_null() {
let mut i: libc::c_int;
let cnt: libc::c_int = carray_count(lines) as libc::c_int;
i = 0i32;
while i < cnt {
free(carray_get(lines, i as libc::c_uint));
i += 1
}
carray_free(lines);
};
pub unsafe fn dc_free_splitted_lines(lines: Vec<*mut libc::c_char>) {
for s in lines {
free(s as *mut libc::c_void);
}
}
/* insert a break every n characters, the return must be free()'d */
@@ -528,7 +508,7 @@ pub unsafe fn dc_str_from_clist(
}
}
to_cstring(res)
res.strdup()
}
pub unsafe fn dc_str_to_clist(
@@ -559,32 +539,32 @@ pub unsafe fn dc_str_to_clist(
list
}
/* the colors must fulfill some criterions as:
- contrast to black and to white
- work as a text-color
- being noticeable on a typical map
- harmonize together while being different enough
(therefore, we cannot just use random rgb colors :) */
const COLORS: [u32; 16] = [
0xe56555, 0xf28c48, 0x8e85ee, 0x76c84d, 0x5bb6cc, 0x549cdd, 0xd25c99, 0xb37800, 0xf23030,
0x39b249, 0xbb243b, 0x964078, 0x66874f, 0x308ab9, 0x127ed0, 0xbe450c,
];
pub fn dc_str_to_color_safe(s: impl AsRef<str>) -> u32 {
let str_lower = s.as_ref().to_lowercase();
let mut checksum = 0;
let bytes = str_lower.as_bytes();
for i in 0..str_lower.len() {
checksum += (i + 1) * bytes[i] as usize;
checksum %= 0xffffff;
}
let color_index = checksum % COLORS.len();
COLORS[color_index]
}
pub unsafe fn dc_str_to_color(str: *const libc::c_char) -> libc::c_int {
let str_lower: *mut libc::c_char = dc_strlower(str);
/* the colors must fulfill some criterions as:
- contrast to black and to white
- work as a text-color
- being noticeable on a typical map
- harmonize together while being different enough
(therefore, we cannot just use random rgb colors :) */
static mut colors: [uint32_t; 16] = [
0xe56555i32 as uint32_t,
0xf28c48i32 as uint32_t,
0x8e85eei32 as uint32_t,
0x76c84di32 as uint32_t,
0x5bb6cci32 as uint32_t,
0x549cddi32 as uint32_t,
0xd25c99i32 as uint32_t,
0xb37800i32 as uint32_t,
0xf23030i32 as uint32_t,
0x39b249i32 as uint32_t,
0xbb243bi32 as uint32_t,
0x964078i32 as uint32_t,
0x66874fi32 as uint32_t,
0x308ab9i32 as uint32_t,
0x127ed0i32 as uint32_t,
0xbe450ci32 as uint32_t,
];
let mut checksum: libc::c_int = 0i32;
let str_len: libc::c_int = strlen(str_lower) as libc::c_int;
let mut i: libc::c_int = 0i32;
@@ -599,7 +579,7 @@ pub unsafe fn dc_str_to_color(str: *const libc::c_char) -> libc::c_int {
) as libc::c_int;
free(str_lower as *mut libc::c_void);
colors[color_index as usize] as libc::c_int
COLORS[color_index as usize] as libc::c_int
}
/* clist tools */
@@ -667,13 +647,7 @@ pub unsafe fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 {
* date/time tools
******************************************************************************/
/* the return value must be free()'d */
pub unsafe fn dc_timestamp_to_str(wanted: i64) -> *mut libc::c_char {
let res = dc_timestamp_to_str_safe(wanted);
to_cstring(res)
}
pub fn dc_timestamp_to_str_safe(wanted: i64) -> String {
pub fn dc_timestamp_to_str(wanted: i64) -> String {
let ts = chrono::Utc.timestamp(wanted, 0);
ts.format("%Y.%m.%d %H:%M:%S").to_string()
}
@@ -724,7 +698,7 @@ pub unsafe fn dc_create_smeared_timestamps(context: &Context, count: libc::c_int
}
/* Message-ID tools */
pub unsafe fn dc_create_id() -> *mut libc::c_char {
pub fn dc_create_id() -> String {
/* generate an id. the generated ID should be as short and as unique as possible:
- short, because it may also used as part of Message-ID headers or in QR codes
- unique as two IDs generated on two devices should not be the same. However, collisions are not world-wide but only by the few contacts.
@@ -742,39 +716,26 @@ pub unsafe fn dc_create_id() -> *mut libc::c_char {
encode_66bits_as_base64(buf[0usize], buf[1usize], buf[2usize])
}
/* ******************************************************************************
* generate Message-IDs
******************************************************************************/
unsafe fn encode_66bits_as_base64(v1: uint32_t, v2: uint32_t, fill: uint32_t) -> *mut libc::c_char {
/* encode 66 bits as a base64 string. This is useful for ID generating with short strings as
we save 5 character in each id compared to 64 bit hex encoding, for a typical group ID, these are 10 characters (grpid+msgid):
hex: 64 bit, 4 bits/character, length = 64/4 = 16 characters
base64: 64 bit, 6 bits/character, length = 64/6 = 11 characters (plus 2 additional bits) */
let ret: *mut libc::c_char = malloc(12) as *mut libc::c_char;
assert!(!ret.is_null());
/// Encode 66 bits as a base64 string.
/// This is useful for ID generating with short strings as we save 5 character
/// in each id compared to 64 bit hex encoding. For a typical group ID, these
/// are 10 characters (grpid+msgid):
/// hex: 64 bit, 4 bits/character, length = 64/4 = 16 characters
/// base64: 64 bit, 6 bits/character, length = 64/6 = 11 characters (plus 2 additional bits)
/// Only the lower 2 bits of `fill` are used.
fn encode_66bits_as_base64(v1: u32, v2: u32, fill: u32) -> String {
use byteorder::{BigEndian, WriteBytesExt};
static mut chars: [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,
45, 95, 0,
];
*ret.offset(0isize) = chars[(v1 >> 26i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(1isize) = chars[(v1 >> 20i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(2isize) = chars[(v1 >> 14i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(3isize) = chars[(v1 >> 8i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(4isize) = chars[(v1 >> 2i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(5isize) = chars
[(v1 << 4i32 & 0x30i32 as libc::c_uint | v2 >> 28i32 & 0xfi32 as libc::c_uint) as usize];
*ret.offset(6isize) = chars[(v2 >> 22i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(7isize) = chars[(v2 >> 16i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(8isize) = chars[(v2 >> 10i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(9isize) = chars[(v2 >> 4i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(10isize) =
chars[(v2 << 2i32 & 0x3ci32 as libc::c_uint | fill & 0x3i32 as libc::c_uint) as usize];
*ret.offset(11isize) = 0i32 as libc::c_char;
ret
let mut wrapped_writer = Vec::new();
{
let mut enc = base64::write::EncoderWriter::new(&mut wrapped_writer, base64::URL_SAFE);
enc.write_u32::<BigEndian>(v1).unwrap();
enc.write_u32::<BigEndian>(v2).unwrap();
enc.write_u8(((fill & 0x3) as u8) << 6).unwrap();
enc.finish().unwrap();
}
assert_eq!(wrapped_writer.pop(), Some('A' as u8)); // Remove last "A"
String::from_utf8(wrapped_writer).unwrap()
}
pub unsafe fn dc_create_incoming_rfc724_mid(
@@ -814,7 +775,7 @@ pub unsafe fn dc_create_outgoing_rfc724_mid(
- the message ID should be globally unique
- do not add a counter or any private data as as this may give unneeded information to the receiver */
let mut rand1: *mut libc::c_char = 0 as *mut libc::c_char;
let rand2: *mut libc::c_char = dc_create_id();
let rand2: *mut libc::c_char = dc_create_id().strdup();
let ret: *mut libc::c_char;
let mut at_hostname: *const libc::c_char = strchr(from_addr, '@' as i32);
if at_hostname.is_null() {
@@ -828,7 +789,7 @@ pub unsafe fn dc_create_outgoing_rfc724_mid(
at_hostname,
)
} else {
rand1 = dc_create_id();
rand1 = dc_create_id().strdup();
ret = dc_mprintf(
b"Mr.%s.%s%s\x00" as *const u8 as *const libc::c_char,
rand1,
@@ -842,52 +803,49 @@ pub unsafe fn dc_create_outgoing_rfc724_mid(
ret
}
pub unsafe fn dc_extract_grpid_from_rfc724_mid(mid: *const libc::c_char) -> *mut libc::c_char {
/* extract our group ID from Message-IDs as `Gr.12345678901.morerandom@domain.de`; "12345678901" is the wanted ID in this example. */
let mut success: libc::c_int = 0i32;
let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char;
let p1: *mut libc::c_char;
let grpid_len: libc::c_int;
if !(mid.is_null()
|| strlen(mid) < 8
|| *mid.offset(0isize) as libc::c_int != 'G' as i32
|| *mid.offset(1isize) as libc::c_int != 'r' as i32
|| *mid.offset(2isize) as libc::c_int != '.' as i32)
{
grpid = dc_strdup(&*mid.offset(3isize));
p1 = strchr(grpid, '.' as i32);
if !p1.is_null() {
*p1 = 0i32 as libc::c_char;
grpid_len = strlen(grpid) as libc::c_int;
if !(grpid_len != 11i32 && grpid_len != 16i32) {
/* strict length comparison, the 'Gr.' magic is weak enough */
success = 1i32
/// Extract the group id (grpid) from a message id (mid)
///
/// # Arguments
///
/// * `mid` - A string that holds the message id
///
/// # Examples
///
/// ```
/// use deltachat::dc_tools::dc_extract_grpid_from_rfc724_mid;
/// let mid = "Gr.12345678901.morerandom@domain.de";
/// let grpid = dc_extract_grpid_from_rfc724_mid(mid);
/// assert_eq!(grpid, Some("12345678901"));
/// ```
pub fn dc_extract_grpid_from_rfc724_mid(mid: &str) -> Option<&str> {
if mid.len() < 9 || !mid.starts_with("Gr.") {
return None;
}
if let Some(mid_without_offset) = mid.get(3..) {
if let Some(grpid_len) = mid_without_offset.find('.') {
/* strict length comparison, the 'Gr.' magic is weak enough */
if grpid_len == 11 || grpid_len == 16 {
return Some(mid_without_offset.get(0..grpid_len).unwrap());
}
}
}
if success == 0i32 {
free(grpid as *mut libc::c_void);
grpid = 0 as *mut libc::c_char
}
return if 0 != success {
grpid
} else {
0 as *mut libc::c_char
};
None
}
pub unsafe fn dc_extract_grpid_from_rfc724_mid_list(list: *const clist) -> *mut libc::c_char {
if !list.is_null() {
let mut cur: *mut clistiter = (*list).first;
while !cur.is_null() {
let mid: *const libc::c_char = (if !cur.is_null() {
(*cur).data
let mid = if !cur.is_null() {
as_str((*cur).data as *const libc::c_char)
} else {
0 as *mut libc::c_void
}) as *const libc::c_char;
let grpid: *mut libc::c_char = dc_extract_grpid_from_rfc724_mid(mid);
if !grpid.is_null() {
return grpid;
""
};
if let Some(grpid) = dc_extract_grpid_from_rfc724_mid(mid) {
return grpid.strdup();
}
cur = if !cur.is_null() {
(*cur).next
@@ -901,6 +859,7 @@ pub unsafe fn dc_extract_grpid_from_rfc724_mid_list(list: *const clist) -> *mut
}
/* file tools */
#[allow(non_snake_case)]
pub unsafe fn dc_ensure_no_slash(pathNfilename: *mut libc::c_char) {
let path_len = strlen(pathNfilename);
if path_len > 0 {
@@ -933,6 +892,7 @@ pub unsafe fn dc_validate_filename(filename: *mut libc::c_char) {
}
}
#[allow(non_snake_case)]
pub unsafe fn dc_get_filename(pathNfilename: *const libc::c_char) -> *mut libc::c_char {
let mut p: *const libc::c_char = strrchr(pathNfilename, '/' as i32);
if p.is_null() {
@@ -947,6 +907,7 @@ pub unsafe fn dc_get_filename(pathNfilename: *const libc::c_char) -> *mut libc::
}
// the case of the suffix is preserved
#[allow(non_snake_case)]
pub unsafe fn dc_split_filename(
pathNfilename: *const libc::c_char,
ret_basename: *mut *mut libc::c_char,
@@ -979,6 +940,7 @@ pub unsafe fn dc_split_filename(
}
// the returned suffix is lower-case
#[allow(non_snake_case)]
pub unsafe fn dc_get_filesuffix_lc(pathNfilename: *const libc::c_char) -> *mut libc::c_char {
if !pathNfilename.is_null() {
let mut p: *const libc::c_char = strrchr(pathNfilename, '.' as i32);
@@ -1074,6 +1036,27 @@ pub unsafe fn dc_get_filemeta(
0
}
/// Expand paths relative to $BLOBDIR into absolute paths.
///
/// If `path` starts with "$BLOBDIR", replaces it with the blobdir path.
/// Otherwise, returns path as is.
pub fn dc_get_abs_path_safe<P: AsRef<std::path::Path>>(
context: &Context,
path: P,
) -> std::path::PathBuf {
let p: &std::path::Path = path.as_ref();
if let Ok(p) = p.strip_prefix("$BLOBDIR") {
assert!(
context.has_blobdir(),
"Expected context to have blobdir to substitute $BLOBDIR",
);
std::path::PathBuf::from(as_str(context.get_blobdir())).join(p)
} else {
p.into()
}
}
#[allow(non_snake_case)]
pub unsafe fn dc_get_abs_path(
context: &Context,
pathNfilename: *const libc::c_char,
@@ -1103,134 +1086,82 @@ pub unsafe fn dc_get_abs_path(
pathNfilename_abs
}
pub unsafe fn dc_file_exist(context: &Context, pathNfilename: *const libc::c_char) -> libc::c_int {
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
if pathNfilename_abs.is_null() {
return 0;
}
let exist = {
let p = std::path::Path::new(as_str(pathNfilename_abs));
p.exists()
};
free(pathNfilename_abs as *mut libc::c_void);
exist as libc::c_int
pub fn dc_file_exist(context: &Context, path: *const libc::c_char) -> libc::c_int {
dc_get_abs_path_safe(context, as_path(path)).exists() as libc::c_int
}
pub unsafe fn dc_get_filebytes(context: &Context, pathNfilename: *const libc::c_char) -> uint64_t {
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
if pathNfilename_abs.is_null() {
return 0;
pub fn dc_get_filebytes(context: &Context, path: *const libc::c_char) -> uint64_t {
let path_abs = dc_get_abs_path_safe(context, as_path(path));
match fs::metadata(&path_abs) {
Ok(meta) => meta.len() as uint64_t,
Err(_err) => 0,
}
let p = std::ffi::CStr::from_ptr(pathNfilename_abs)
.to_str()
.unwrap();
let filebytes = match fs::metadata(p) {
Ok(meta) => meta.len(),
Err(_err) => {
return 0;
}
};
free(pathNfilename_abs as *mut libc::c_void);
filebytes as uint64_t
}
pub unsafe fn dc_delete_file(context: &Context, pathNfilename: *const libc::c_char) -> libc::c_int {
let mut success: libc::c_int = 0i32;
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
if pathNfilename_abs.is_null() {
return 0;
}
let p = std::path::Path::new(
std::ffi::CStr::from_ptr(pathNfilename_abs)
.to_str()
.unwrap(),
);
let res = if p.is_file() {
fs::remove_file(p)
pub fn dc_delete_file(context: &Context, path: *const libc::c_char) -> libc::c_int {
let path = as_path(path);
let path_abs = dc_get_abs_path_safe(context, path);
let res = if path_abs.is_file() {
fs::remove_file(path_abs)
} else {
fs::remove_dir_all(p)
fs::remove_dir_all(path_abs)
};
match res {
Ok(_) => {
success = 1;
}
Ok(_) => 1,
Err(_err) => {
warn!(context, 0, "Cannot delete \"{}\".", as_str(pathNfilename),);
warn!(context, 0, "Cannot delete \"{}\".", path.display());
0
}
}
free(pathNfilename_abs as *mut libc::c_void);
success
}
pub unsafe fn dc_copy_file(
pub fn dc_copy_file(
context: &Context,
src: *const libc::c_char,
dest: *const libc::c_char,
) -> libc::c_int {
let mut success = 0;
let src_abs = dc_get_abs_path(context, src);
let dest_abs = dc_get_abs_path(context, dest);
if src_abs.is_null() || dest_abs.is_null() {
return 0;
}
let src_p = std::ffi::CStr::from_ptr(src_abs).to_str().unwrap();
let dest_p = std::ffi::CStr::from_ptr(dest_abs).to_str().unwrap();
match fs::copy(src_p, dest_p) {
Ok(_) => {
success = 1;
}
let src = as_path(src);
let dest = as_path(dest);
let src_abs = dc_get_abs_path_safe(context, src);
let dest_abs = dc_get_abs_path_safe(context, dest);
match fs::copy(&src_abs, &dest_abs) {
Ok(_) => 1,
Err(_) => {
error!(context, 0, "Cannot copy \"{}\" to \"{}\".", src_p, dest_p,);
error!(
context,
0,
"Cannot copy \"{}\" to \"{}\".",
src.display(),
dest.display(),
);
0
}
}
free(src_abs as *mut libc::c_void);
free(dest_abs as *mut libc::c_void);
success
}
pub unsafe fn dc_create_folder(
context: &Context,
pathNfilename: *const libc::c_char,
) -> libc::c_int {
let mut success = 0;
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
{
let p = std::path::Path::new(as_str(pathNfilename_abs));
if !p.exists() {
match fs::create_dir_all(p) {
Ok(_) => {
success = 1;
}
Err(_err) => {
warn!(
context,
0,
"Cannot create directory \"{}\".",
as_str(pathNfilename),
);
}
pub fn dc_create_folder(context: &Context, path: *const libc::c_char) -> libc::c_int {
let path = as_path(path);
let path_abs = dc_get_abs_path_safe(context, path);
if !path_abs.exists() {
match fs::create_dir_all(path_abs) {
Ok(_) => 1,
Err(_err) => {
warn!(
context,
0,
"Cannot create directory \"{}\".",
path.display(),
);
0
}
} else {
success = 1;
}
} else {
1
}
free(pathNfilename_abs as *mut libc::c_void);
success
}
#[allow(non_snake_case)]
pub unsafe fn dc_write_file(
context: &Context,
pathNfilename: *const libc::c_char,
@@ -1242,36 +1173,27 @@ pub unsafe fn dc_write_file(
dc_write_file_safe(context, as_str(pathNfilename), bytes) as libc::c_int
}
pub fn dc_write_file_safe(context: &Context, pathNfilename: impl AsRef<str>, buf: &[u8]) -> bool {
let pathNfilename_abs = unsafe {
let n = to_cstring(pathNfilename.as_ref());
let res = dc_get_abs_path(context, n);
free(n as *mut _);
res
};
if pathNfilename_abs.is_null() {
return false;
}
let p = as_str(pathNfilename_abs);
let success = if let Err(_err) = fs::write(p, buf) {
pub fn dc_write_file_safe<P: AsRef<std::path::Path>>(
context: &Context,
path: P,
buf: &[u8],
) -> bool {
let path_abs = dc_get_abs_path_safe(context, &path);
if let Err(_err) = fs::write(&path_abs, buf) {
warn!(
context,
0,
"Cannot write {} bytes to \"{}\".",
buf.len(),
pathNfilename.as_ref(),
path.as_ref().display(),
);
false
} else {
true
};
unsafe { free(pathNfilename_abs as *mut libc::c_void) };
success
}
}
#[allow(non_snake_case)]
pub unsafe fn dc_read_file(
context: &Context,
pathNfilename: *const libc::c_char,
@@ -1291,37 +1213,23 @@ pub unsafe fn dc_read_file(
}
}
pub fn dc_read_file_safe(context: &Context, pathNfilename: impl AsRef<str>) -> Option<Vec<u8>> {
let pathNfilename_abs = unsafe {
let n = to_cstring(pathNfilename.as_ref());
let p = dc_get_abs_path(context, n);
free(n as *mut _);
p
};
if pathNfilename_abs.is_null() {
return None;
}
let p = as_str(pathNfilename_abs);
let res = match fs::read(p) {
pub fn dc_read_file_safe<P: AsRef<std::path::Path>>(context: &Context, path: P) -> Option<Vec<u8>> {
let path_abs = dc_get_abs_path_safe(context, &path);
match fs::read(&path_abs) {
Ok(bytes) => Some(bytes),
Err(_err) => {
warn!(
context,
0,
"Cannot read \"{}\" or file is empty.",
pathNfilename.as_ref(),
path.as_ref().display()
);
None
}
};
unsafe { free(pathNfilename_abs as *mut libc::c_void) };
res
}
}
#[allow(non_snake_case)]
pub unsafe fn dc_get_fine_pathNfilename(
context: &Context,
pathNfolder: *const libc::c_char,
@@ -1375,15 +1283,9 @@ pub unsafe fn dc_get_fine_pathNfilename(
ret
}
// TODO should return bool /rtn
pub unsafe fn dc_is_blobdir_path(context: &Context, path: *const libc::c_char) -> libc::c_int {
if strncmp(path, context.get_blobdir(), strlen(context.get_blobdir())) == 0i32
|| strncmp(path, b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, 8) == 0i32
{
return 1i32;
}
0
pub unsafe fn dc_is_blobdir_path(context: &Context, path: *const libc::c_char) -> bool {
return strncmp(path, context.get_blobdir(), strlen(context.get_blobdir())) == 0
|| strncmp(path, b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, 8) == 0;
}
pub unsafe fn dc_make_rel_path(context: &Context, path: *mut *mut libc::c_char) {
@@ -1399,15 +1301,14 @@ pub unsafe fn dc_make_rel_path(context: &Context, path: *mut *mut libc::c_char)
};
}
// TODO should return bool /rtn
pub unsafe fn dc_make_rel_and_copy(context: &Context, path: *mut *mut libc::c_char) -> libc::c_int {
let mut success: libc::c_int = 0i32;
pub unsafe fn dc_make_rel_and_copy(context: &Context, path: *mut *mut libc::c_char) -> bool {
let mut success = false;
let mut filename: *mut libc::c_char = 0 as *mut libc::c_char;
let mut blobdir_path: *mut libc::c_char = 0 as *mut libc::c_char;
if !(path.is_null() || (*path).is_null()) {
if 0 != dc_is_blobdir_path(context, *path) {
if dc_is_blobdir_path(context, *path) {
dc_make_rel_path(context, path);
success = 1i32
success = true;
} else {
filename = dc_get_filename(*path);
if !(filename.is_null()
@@ -1425,7 +1326,7 @@ pub unsafe fn dc_make_rel_and_copy(context: &Context, path: *mut *mut libc::c_ch
*path = blobdir_path;
blobdir_path = 0 as *mut libc::c_char;
dc_make_rel_path(context, path);
success = 1i32
success = true;
}
}
}
@@ -1535,10 +1436,49 @@ fn os_str_to_c_string_unicode(
}
}
/// Needs to free the result after use!
pub unsafe fn to_cstring<S: AsRef<str>>(s: S) -> *mut libc::c_char {
let cstr = CString::new(s.as_ref()).expect("invalid string converted");
dc_strdup(cstr.as_ref().as_ptr())
/// Convenience methods/associated functions for working with [CString]
///
/// This is helps transitioning from unsafe code.
pub trait CStringExt {
/// Create a new [CString], yolo style
///
/// This unwrap the result, panicking when there are embedded NULL
/// bytes.
fn yolo<T: Into<Vec<u8>>>(t: T) -> CString {
CString::new(t).expect("String contains null byte, can not be CString")
}
}
impl CStringExt for CString {}
/// Convenience methods to make transitioning from raw C strings easier.
///
/// To interact with (legacy) C APIs we often need to convert from
/// Rust strings to raw C strings. This can be clumsy to do correctly
/// and the compiler sometimes allows it in an unsafe way. These
/// methods make it more succinct and help you get it right.
pub trait StrExt {
/// Allocate a new raw C `*char` version of this string.
///
/// This allocates a new raw C string which must be freed using
/// `free`. It takes care of some common pitfalls with using
/// [CString::as_ptr].
///
/// [CString::as_ptr]: std::ffi::CString::as_ptr
///
/// # Panics
///
/// This function will panic when the original string contains an
/// interior null byte as this can not be represented in raw C
/// strings.
unsafe fn strdup(&self) -> *mut libc::c_char;
}
impl<T: AsRef<str>> StrExt for T {
unsafe fn strdup(&self) -> *mut libc::c_char {
let tmp = CString::yolo(self.as_ref());
dc_strdup(tmp.as_ptr())
}
}
pub fn to_string(s: *const libc::c_char) -> String {
@@ -1570,13 +1510,16 @@ pub fn to_string_lossy(s: *const libc::c_char) -> String {
}
pub fn as_str<'a>(s: *const libc::c_char) -> &'a str {
as_str_safe(s).unwrap_or_else(|err| panic!("{}", err))
}
pub fn as_str_safe<'a>(s: *const libc::c_char) -> Result<&'a str, Error> {
assert!(!s.is_null(), "cannot be used on null pointers");
let cstr = unsafe { CStr::from_ptr(s) };
cstr.to_str().unwrap_or_else(|err| {
panic!("Non utf8 string: '{:?}' ({:?})", cstr.to_bytes(), err);
})
cstr.to_str()
.map_err(|err| format_err!("Non utf8 string: '{:?}' ({:?})", cstr.to_bytes(), err))
}
/// Convert a C `*char` pointer to a [std::path::Path] slice.
@@ -1974,11 +1917,28 @@ mod tests {
#[test]
fn test_dc_create_id() {
unsafe {
let buf = dc_create_id();
assert_eq!(strlen(buf), 11);
free(buf as *mut libc::c_void);
}
let buf = dc_create_id();
assert_eq!(buf.len(), 11);
}
#[test]
fn test_encode_66bits_as_base64() {
assert_eq!(
encode_66bits_as_base64(0x01234567, 0x89abcdef, 0),
"ASNFZ4mrze8"
);
assert_eq!(
encode_66bits_as_base64(0x01234567, 0x89abcdef, 1),
"ASNFZ4mrze9"
);
assert_eq!(
encode_66bits_as_base64(0x01234567, 0x89abcdef, 2),
"ASNFZ4mrze-"
);
assert_eq!(
encode_66bits_as_base64(0x01234567, 0x89abcdef, 3),
"ASNFZ4mrze_"
);
}
#[test]
@@ -2077,4 +2037,53 @@ mod tests {
let ptr = some_path.as_ptr();
assert_eq!(as_path_unicode(ptr), std::ffi::OsString::from("/some/path"));
}
#[test]
fn test_cstring_yolo() {
assert_eq!(CString::new("hello").unwrap(), CString::yolo("hello"));
}
#[test]
fn test_strdup_str() {
unsafe {
let s = "hello".strdup();
let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char);
free(s as *mut libc::c_void);
assert_eq!(cmp, 0);
}
}
#[test]
fn test_strdup_string() {
unsafe {
let s = String::from("hello").strdup();
let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char);
free(s as *mut libc::c_void);
assert_eq!(cmp, 0);
}
}
#[test]
fn test_dc_extract_grpid_from_rfc724_mid() {
// Should return None if we pass invalid mid
let mid = "foobar";
let grpid = dc_extract_grpid_from_rfc724_mid(mid);
assert_eq!(grpid, None);
// Should return None if grpid has a length which is not 11 or 16
let mid = "Gr.12345678.morerandom@domain.de";
let grpid = dc_extract_grpid_from_rfc724_mid(mid);
assert_eq!(grpid, None);
// Should return extracted grpid for grpid with length of 11
let mid = "Gr.12345678901.morerandom@domain.de";
let grpid = dc_extract_grpid_from_rfc724_mid(mid);
assert_eq!(grpid, Some("12345678901"));
// Should return extracted grpid for grpid with length of 11
let mid = "Gr.1234567890123456.morerandom@domain.de";
let grpid = dc_extract_grpid_from_rfc724_mid(mid);
assert_eq!(grpid, Some("1234567890123456"));
}
}

View File

@@ -16,6 +16,8 @@ pub enum Error {
SqlFailedToOpen,
#[fail(display = "{:?}", _0)]
Io(std::io::Error),
#[fail(display = "{:?}", _0)]
Message(String),
}
pub type Result<T> = std::result::Result<T, Error>;
@@ -43,3 +45,67 @@ impl From<std::io::Error> for Error {
Error::Io(err)
}
}
#[macro_export]
macro_rules! bail {
($e:expr) => {
return Err($crate::error::Error::Message($e.to_string()));
};
($fmt:expr, $($arg:tt)+) => {
return Err($crate::error::Error::Message(format!($fmt, $($arg)+)));
};
}
#[macro_export]
macro_rules! format_err {
($e:expr) => {
$crate::error::Error::Message($e.to_string());
};
($fmt:expr, $($arg:tt)+) => {
$crate::error::Error::Message(format!($fmt, $($arg)+));
};
}
#[macro_export(local_inner_macros)]
macro_rules! ensure {
($cond:expr, $e:expr) => {
if !($cond) {
bail!($e);
}
};
($cond:expr, $fmt:expr, $($arg:tt)+) => {
if !($cond) {
bail!($fmt, $($arg)+);
}
};
}
#[macro_export]
macro_rules! ensure_eq {
($left:expr, $right:expr) => ({
match (&$left, &$right) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
bail!(r#"assertion failed: `(left == right)`
left: `{:?}`,
right: `{:?}`"#, left_val, right_val)
}
}
}
});
($left:expr, $right:expr,) => ({
ensure_eq!($left, $right)
});
($left:expr, $right:expr, $($arg:tt)+) => ({
match (&($left), &($right)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
bail!(r#"assertion failed: `(left == right)`
left: `{:?}`,
right: `{:?}`: {}"#, left_val, right_val,
format_args!($($arg)+))
}
}
}
});
}

View File

@@ -1,3 +1,4 @@
use std::ffi::CString;
use std::net;
use std::sync::{Arc, Condvar, Mutex, RwLock};
use std::time::{Duration, SystemTime};
@@ -5,10 +6,9 @@ use std::time::{Duration, SystemTime};
use crate::constants::*;
use crate::context::Context;
use crate::dc_loginparam::*;
use crate::dc_tools::{as_str, to_cstring};
use crate::dc_tools::CStringExt;
use crate::oauth2::dc_get_oauth2_access_token;
use crate::types::*;
use crate::x::free;
pub const DC_IMAP_SEEN: usize = 0x0001;
pub const DC_REGENERATE: usize = 0x01;
@@ -705,26 +705,16 @@ impl Imap {
fn get_config_last_seen_uid<S: AsRef<str>>(&self, context: &Context, folder: S) -> (u32, u32) {
let key = format!("imap.mailbox.{}", folder.as_ref());
let val1 = unsafe {
let key_c = to_cstring(key);
let val = (self.get_config)(context, key_c, 0 as *const libc::c_char);
free(key_c as *mut _);
val
};
if val1.is_null() {
return (0, 0);
if let Some(entry) = (self.get_config)(context, &key) {
// the entry has the format `imap.mailbox.<folder>=<uidvalidity>:<lastseenuid>`
let mut parts = entry.split(':');
(
parts.next().unwrap().parse().unwrap_or_else(|_| 0),
parts.next().unwrap().parse().unwrap_or_else(|_| 0),
)
} else {
(0, 0)
}
let entry = as_str(val1);
if entry.is_empty() {
return (0, 0);
}
// the entry has the format `imap.mailbox.<folder>=<uidvalidity>:<lastseenuid>`
let mut parts = entry.split(':');
(
parts.next().unwrap().parse().unwrap_or_else(|_| 0),
parts.next().unwrap().parse().unwrap_or_else(|_| 0),
)
}
fn fetch_from_single_folder<S: AsRef<str>>(&self, context: &Context, folder: S) -> usize {
@@ -853,10 +843,8 @@ impl Imap {
.expect("missing message id");
if 0 == unsafe {
let message_id_c = to_cstring(message_id);
let res = (self.precheck_imf)(context, message_id_c, folder.as_ref(), cur_uid);
free(message_id_c as *mut _);
res
let message_id_c = CString::yolo(message_id);
(self.precheck_imf)(context, message_id_c.as_ptr(), folder.as_ref(), cur_uid)
} {
// check passed, go fetch the rest
if self.fetch_single_msg(context, &folder, cur_uid) == 0 {
@@ -924,13 +912,7 @@ impl Imap {
let key = format!("imap.mailbox.{}", folder.as_ref());
let val = format!("{}:{}", uidvalidity, lastseenuid);
unsafe {
let key_c = to_cstring(key);
let val_c = to_cstring(val);
(self.set_config)(context, key_c, val_c);
free(key_c as *mut _);
free(val_c as *mut _);
};
(self.set_config)(context, &key, Some(&val));
}
fn fetch_single_msg<S: AsRef<str>>(

View File

@@ -216,22 +216,16 @@ impl Key {
}
}
/// Each header line must be terminated by `\r\n`, the result must be freed.
pub fn to_asc_c(&self, header: Option<(&str, &str)>) -> *mut libc::c_char {
/// Each header line must be terminated by `\r\n`
pub fn to_asc(&self, header: Option<(&str, &str)>) -> String {
let headers = header.map(|(key, value)| {
let mut m = BTreeMap::new();
m.insert(key.to_string(), value.to_string());
m
});
let buf = self
.to_armored_string(headers.as_ref())
.expect("failed to serialize key");
let buf_c = CString::new(buf).unwrap();
// need to use strdup to allocate the result with malloc
// so it can be `free`d later.
unsafe { strdup(buf_c.as_ptr()) }
self.to_armored_string(headers.as_ref())
.expect("failed to serialize key")
}
pub fn write_asc_to_file(&self, file: *const libc::c_char, context: &Context) -> bool {
@@ -239,15 +233,16 @@ impl Key {
return false;
}
let file_content = self.to_asc_c(None);
let file_content = self.to_asc(None);
let file_content_c = CString::new(file_content).unwrap();
let success = if 0
== unsafe {
dc_write_file(
context,
file,
file_content as *const libc::c_void,
strlen(file_content),
file_content_c.as_ptr() as *const libc::c_void,
file_content_c.as_bytes().len(),
)
} {
error!(context, 0, "Cannot write key to {}", to_string(file));
@@ -256,8 +251,6 @@ impl Key {
true
};
unsafe { free(file_content as *mut libc::c_void) };
success
}

View File

@@ -1,13 +0,0 @@
use crate::context::Context;
/* yes: uppercase */
/* library private: key-history */
pub fn dc_add_to_keyhistory(
_context: &Context,
_rfc724_mid: *const libc::c_char,
_sending_time: u64,
_addr: *const libc::c_char,
_fingerprint: *const libc::c_char,
) {
}

View File

@@ -1,12 +1,4 @@
#![allow(
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
non_upper_case_globals,
non_camel_case_types,
non_snake_case
)]
#![feature(c_variadic, ptr_wrapping_offset_from)]
#![feature(c_variadic, ptr_wrapping_offset_from, ptr_cast)]
#[macro_use]
extern crate failure_derive;
@@ -16,32 +8,37 @@ extern crate num_derive;
extern crate smallvec;
#[macro_use]
extern crate rusqlite;
extern crate strum;
#[macro_use]
extern crate strum_macros;
#[macro_use]
mod log;
#[macro_use]
pub mod error;
pub mod aheader;
pub mod chatlist;
pub mod config;
pub mod constants;
pub mod contact;
pub mod context;
pub mod error;
pub mod imap;
pub mod key;
pub mod keyhistory;
pub mod keyring;
pub mod oauth2;
pub mod param;
pub mod peerstate;
pub mod pgp;
pub mod smtp;
pub mod sql;
pub mod stock;
pub mod types;
pub mod x;
pub mod dc_array;
pub mod dc_chat;
pub mod dc_chatlist;
pub mod dc_configure;
pub mod dc_contact;
pub mod dc_dehtml;
pub mod dc_e2ee;
pub mod dc_imex;
@@ -54,15 +51,16 @@ pub mod dc_mimefactory;
pub mod dc_mimeparser;
pub mod dc_move;
pub mod dc_msg;
pub mod dc_param;
pub mod dc_qr;
pub mod dc_receive_imf;
pub mod dc_saxparser;
pub mod dc_securejoin;
pub mod dc_simplify;
pub mod dc_stock;
pub mod dc_strencode;
pub mod dc_token;
pub mod dc_tools;
pub use self::constants::*;
#[cfg(test)]
pub mod test_utils;

View File

@@ -7,10 +7,9 @@ macro_rules! info {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
let formatted_c = std::ffi::CString::new(formatted).unwrap();
$ctx.call_cb($crate::constants::Event::INFO, $data1 as libc::uintptr_t,
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void);
formatted_c.as_ptr() as libc::uintptr_t);
}};
}
@@ -23,10 +22,9 @@ macro_rules! warn {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
let formatted_c = std::ffi::CString::new(formatted).unwrap();
$ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t,
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void) ;
formatted_c.as_ptr() as libc::uintptr_t);
}};
}
@@ -39,10 +37,9 @@ macro_rules! error {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
let formatted_c = std::ffi::CString::new(formatted).unwrap();
$ctx.call_cb($crate::constants::Event::ERROR, $data1 as libc::uintptr_t,
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void);
formatted_c.as_ptr() as libc::uintptr_t);
}};
}
@@ -55,9 +52,8 @@ macro_rules! log_event {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = $crate::dc_tools::to_cstring(formatted);
let formatted_c = std::ffi::CString::new(formatted).unwrap();
$ctx.call_cb($event, $data1 as libc::uintptr_t,
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void);
formatted_c.as_ptr() as libc::uintptr_t);
}};
}

View File

@@ -1,6 +1,6 @@
use std::collections::HashMap;
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use serde::Deserialize;
use crate::context::Context;
@@ -321,7 +321,7 @@ fn is_expired(context: &Context) -> bool {
}
fn replace_in_uri(uri: impl AsRef<str>, key: impl AsRef<str>, value: impl AsRef<str>) -> String {
let value_urlencoded = utf8_percent_encode(value.as_ref(), DEFAULT_ENCODE_SET).to_string();
let value_urlencoded = utf8_percent_encode(value.as_ref(), NON_ALPHANUMERIC).to_string();
uri.as_ref().replace(key.as_ref(), &value_urlencoded)
}
@@ -344,7 +344,7 @@ mod tests {
fn test_replace_in_uri() {
assert_eq!(
replace_in_uri("helloworld", "world", "a-b c"),
"helloa-b%20c"
"helloa%2Db%20c"
);
}

238
src/param.rs Normal file
View File

@@ -0,0 +1,238 @@
use std::collections::BTreeMap;
use std::fmt;
use std::str;
use num_traits::FromPrimitive;
use crate::error;
/// Available param keys.
#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash, PartialOrd, Ord, FromPrimitive)]
#[repr(u8)]
pub enum Param {
/// For messages and jobs
File = 'f' as u8,
/// For Messages
Width = 'w' as u8,
/// For Messages
Height = 'h' as u8,
/// For Messages
Duration = 'd' as u8,
/// For Messages
MimeType = 'm' as u8,
/// For Messages: message is encryoted, outgoing: guarantee E2EE or the message is not send
GuranteeE2ee = 'c' as u8,
/// For Messages: decrypted with validation errors or without mutual set, if neither
/// 'c' nor 'e' are preset, the messages is only transport encrypted.
ErroneousE2ee = 'e' as u8,
/// For Messages: force unencrypted message, either `ForcePlaintext::AddAutocryptHeader` (1),
/// `ForcePlaintext::NoAutocryptHeader` (2) or 0.
ForcePlaintext = 'u' as u8,
/// For Messages
WantsMdn = 'r' as u8,
/// For Messages
Forwarded = 'a' as u8,
/// For Messages
Cmd = 'S' as u8,
/// For Messages
Arg = 'E' as u8,
/// For Messages
Arg2 = 'F' as u8,
/// For Messages
Arg3 = 'G' as u8,
/// For Messages
Arg4 = 'H' as u8,
/// For Messages
Error = 'L' as u8,
/// For Messages: space-separated list of messaged IDs of forwarded copies.
PrepForwards = 'P' as u8,
/// For Jobs
SetLatitude = 'l' as u8,
/// For Jobs
SetLongitude = 'n' as u8,
/// For Jobs
ServerFolder = 'Z' as u8,
/// For Jobs
ServerUid = 'z' as u8,
/// For Jobs
AlsoMove = 'M' as u8,
/// For Jobs: space-separated list of message recipients
Recipients = 'R' as u8,
// For Groups
Unpromoted = 'U' as u8,
// For Groups and Contacts
ProfileImage = 'i' as u8,
// For Chats
Selftalk = 'K' as u8,
// For QR
Auth = 's' as u8,
// For QR
GroupId = 'x' as u8,
// For QR
GroupName = 'g' as u8,
}
/// Possible values for `Param::ForcePlaintext`.
#[derive(PartialEq, Eq, Debug, Clone, Copy, FromPrimitive)]
#[repr(u8)]
pub enum ForcePlaintext {
AddAutocryptHeader = 1,
NoAutocryptHeader = 2,
}
/// An object for handling key=value parameter lists.
///
/// The structure is serialized by calling `to_string()` on it.
///
/// Only for library-internal use.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Params {
inner: BTreeMap<Param, String>,
}
impl fmt::Display for Params {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, (key, value)) in self.inner.iter().enumerate() {
if i > 0 {
write!(f, "\n")?;
}
write!(f, "{}={}", *key as u8 as char, value)?;
}
Ok(())
}
}
impl str::FromStr for Params {
type Err = error::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let mut inner = BTreeMap::new();
for pair in s.trim().lines() {
let pair = pair.trim();
if pair.is_empty() {
continue;
}
// TODO: probably nicer using a regex
ensure!(pair.len() > 2, "Invalid key pair: '{}'", pair);
let mut split = pair.splitn(2, '=');
let key = split.next();
let value = split.next();
ensure!(key.is_some(), "Missing key");
ensure!(value.is_some(), "Missing value");
let key = key.unwrap().trim();
let value = value.unwrap().trim();
if let Some(key) = Param::from_u8(key.as_bytes()[0]) {
inner.insert(key, value.to_string());
} else {
bail!("Unknown key: {}", key);
}
}
Ok(Params { inner })
}
}
impl Params {
/// Create new empty params.
pub fn new() -> Self {
Default::default()
}
/// Get the value of the given key, return `None` if no value is set.
pub fn get(&self, key: Param) -> Option<&str> {
self.inner.get(&key).map(|s| s.as_str())
}
/// Check if the given key is set.
pub fn exists(&self, key: Param) -> bool {
self.inner.contains_key(&key)
}
/// Set the given key to the passed in value.
pub fn set(&mut self, key: Param, value: impl AsRef<str>) -> &mut Self {
self.inner.insert(key, value.as_ref().to_string());
self
}
/// Removes the given key, if it exists.
pub fn remove(&mut self, key: Param) -> &mut Self {
self.inner.remove(&key);
self
}
/// Check if there are any values in this.
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
/// Returns how many key-value pairs are set.
pub fn len(&self) -> usize {
self.inner.len()
}
/// Get the given parameter and parse as `i32`.
pub fn get_int(&self, key: Param) -> Option<i32> {
self.get(key).and_then(|s| s.parse().ok())
}
/// Get the given parameter and parse as `f64`.
pub fn get_float(&self, key: Param) -> Option<f64> {
self.get(key).and_then(|s| s.parse().ok())
}
/// Set the given paramter to the passed in `i32`.
pub fn set_int(&mut self, key: Param, value: i32) -> &mut Self {
self.set(key, format!("{}", value));
self
}
/// Set the given parameter to the passed in `f64` .
pub fn set_float(&mut self, key: Param, value: f64) -> &mut Self {
self.set(key, format!("{}", value));
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dc_param() {
let mut p1: Params = "\r\n\r\na=1\nf=2\n\nc = 3 ".parse().unwrap();
assert_eq!(p1.get_int(Param::Forwarded), Some(1));
assert_eq!(p1.get_int(Param::File), Some(2));
assert_eq!(p1.get_int(Param::Height), None);
assert!(!p1.exists(Param::Height));
p1.set_int(Param::Duration, 4);
assert_eq!(p1.get_int(Param::Duration), Some(4));
let mut p1 = Params::new();
p1.set(Param::Forwarded, "foo")
.set_int(Param::File, 2)
.remove(Param::GuranteeE2ee)
.set_int(Param::Duration, 4);
assert_eq!(p1.to_string(), "a=foo\nd=4\nf=2");
p1.remove(Param::File);
assert_eq!(p1.to_string(), "a=foo\nd=4",);
assert_eq!(p1.len(), 2);
p1.remove(Param::Forwarded);
p1.remove(Param::Duration);
assert_eq!(p1.to_string(), "",);
assert!(p1.is_empty());
assert_eq!(p1.len(), 0)
}
}

View File

@@ -462,16 +462,11 @@ mod tests {
use super::*;
use pretty_assertions::assert_eq;
use std::ffi::CStr;
use tempfile::{tempdir, TempDir};
use crate::context::*;
use crate::dc_tools::to_cstring;
use crate::x::free;
use tempfile::TempDir;
#[test]
fn test_peerstate_save_to_db() {
let ctx = unsafe { create_test_context() };
let ctx = crate::test_utils::dummy_context();
let addr = "hello@mail.com";
let pub_key = crate::key::Key::from_base64("xsBNBFztUVkBCADYaQl/UOUpRPd32nLRzx8eU0eI+jQEnG+g5anjYA+3oct1rROGl5SygjMULDKdaUy27O3o9Srsti0YjA7uxZnavIqhSopJhFidqY1M1wA9JZa/duucZdNwUGbjGIRsS/4Cjr5+3svscK24hVYub1dvDWXpwUTnj3K6xOEnJdoM+MhCqtSD5+zcJhFc9vyZm9ZTGWUxAhKh0iJTcCD8V6CQ3XZ2z9GruwzZT/FTFovWrz7m3TUI2OdSSHh0eZLRGEoxMCT/vzflAFGAr8ijCaRsEIfqP6FW8uQWnFTqkjxEUCZG6XkeFHB84aj5jqYG/1KCLjL5vEKwfl1tz/WnPhY7ABEBAAHNEDxoZWxsb0BtYWlsLmNvbT7CwIkEEAEIADMCGQEFAlztUVoCGwMECwkIBwYVCAkKCwIDFgIBFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7ijAwf+PTsuawUax9cNPn1bN90H+g9qyHZJMEwKXtUnNaXJxPW3iB7ThhpCiCzsZwP7+l7ArS8tmLeNDw2bENtcf1XCv4wovP2fdXOP3QOUUFX/GdakcTwv7DzC7CO0grB1HtaPhGw/6UX2o2cx2i9xiUf4Givq2MfCbgAW5zloH6WXGPb6yLQYJXxqDIphr4+uZDb+bMAyWHN/DUkAjHrV8nnVki7PMHqzzZpwglalxMX8RGeiGZE39ALJKL/Og87DMFah87/yoxQWGoS7Wqv0XDcCPKoTCPrpk8pOe2KEsq/lz215nefHd4aRpfUX5YCYa8HPvvfPQbGF73uvyQw5w7qjis7ATQRc7VFZAQgAt8ONdnX6KEEQ5Jw6ilJ+LBtY44SP5t0I3eK+goKepgIiKhjGDa+Mntyi4jdhH+HO6kvK5SHMh2sPp4rRO/WKHJwWFySyM1OdyiywhyH0J9R5rBY4vPHsJjf6vSKJdWLWT+ho1fNet2IIC+jVCYli91MAMbRvk6EKVj1nCc+67giOahXEkHt6xxkeCGlOvbw8hxGj1A8+AC1BLms/OR3oc4JMi9O3kq6uG0z9tlUEerac9HVwcjoO1XLe+hJhoT5H+TbnGjPuhuURP3pFiIKHpbRYgUfdSAY0dTObO7t4I5y/drPOrCTnWrBUg2wXAECUhpRKow9/ai2YemLv9KqhhwARAQABwsB2BBgBCAAgBQJc7VFaAhsMFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7jmyggAhs4QzCzIbT2OsAReBxkxtm0AI+g1HZ1KFKof5NDHfgv9C/Qu1I8mKEjlZzA4qFyPmLqntgwJ0RuFy6gLbljZBNCFO7vB478AhYtnWjuKZmA40HUPwcB1hEJ31c42akzfUbioY1TLLepngdsJg7Cm8O+rhI9+1WRA66haJDgFs793SVUDyJh8f9NX50l5zR87/bsV30CFSw0q4OSSy9VI/z+2g5khn1LnuuOrCfFnYIPYtJED1BfkXkosxGlgbzy79VvGmI9d23x4atDK7oBPCzIj+lP8sytJ0u3HOguXi9OgDitKy+Pt1r8gH8frdktMJr5Ts6DW+tIn2vR23KR8aA==", KeyType::Public).unwrap();
@@ -509,29 +504,4 @@ mod tests {
ctx: Context,
dir: TempDir,
}
unsafe extern "C" fn cb(
_context: &Context,
_event: Event,
_data1: libc::uintptr_t,
_data2: libc::uintptr_t,
) -> libc::uintptr_t {
0
}
unsafe fn create_test_context() -> TestContext {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
let dir = tempdir().unwrap();
let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap());
assert_eq!(
dc_open(&mut ctx, dbfile, std::ptr::null()),
1,
"Failed to open {}",
CStr::from_ptr(dbfile as *const _).to_str().unwrap()
);
free(dbfile as *mut _);
TestContext { ctx: ctx, dir: dir }
}
}

View File

@@ -17,15 +17,14 @@ use crate::keyring::*;
use crate::types::*;
use crate::x::*;
// TODO should return bool /rtn
pub unsafe fn dc_split_armored_data(
buf: *mut libc::c_char,
ret_headerline: *mut *const libc::c_char,
ret_setupcodebegin: *mut *const libc::c_char,
ret_preferencrypt: *mut *const libc::c_char,
ret_base64: *mut *const libc::c_char,
) -> libc::c_int {
let mut success: libc::c_int = 0i32;
) -> bool {
let mut success = false;
let mut line_chars: size_t = 0i32 as size_t;
let mut line: *mut libc::c_char = buf;
let mut p1: *mut libc::c_char = buf;
@@ -128,7 +127,7 @@ pub unsafe fn dc_split_armored_data(
if !ret_base64.is_null() {
*ret_base64 = base64
}
success = 1i32
success = true;
}
}
}

View File

@@ -6,11 +6,10 @@ use thread_local_object::ThreadLocal;
use crate::constants::*;
use crate::context::Context;
use crate::dc_param::*;
use crate::dc_tools::*;
use crate::error::{Error, Result};
use crate::param::*;
use crate::peerstate::*;
use crate::x::*;
const DC_OPEN_READONLY: usize = 0x01;
@@ -340,7 +339,7 @@ fn open(
info!(
context,
0,
"First time init: creating tables in \"{:?}\".",
"First time init: creating tables in {:?}.",
dbfile.as_ref(),
);
sql.execute(
@@ -816,7 +815,7 @@ fn open(
}
}
info!(context, 0, "Opened \"{:?}\".", dbfile.as_ref(),);
info!(context, 0, "Opened {:?}.", dbfile.as_ref(),);
Ok(())
}
@@ -954,25 +953,25 @@ pub fn housekeeping(context: &Context) {
context,
&mut files_in_use,
"SELECT param FROM msgs WHERE chat_id!=3 AND type!=10;",
'f' as i32,
Param::File,
);
maybe_add_from_param(
context,
&mut files_in_use,
"SELECT param FROM jobs;",
'f' as i32,
Param::File,
);
maybe_add_from_param(
context,
&mut files_in_use,
"SELECT param FROM chats;",
'i' as i32,
Param::ProfileImage,
);
maybe_add_from_param(
context,
&mut files_in_use,
"SELECT param FROM contacts;",
'i' as i32,
Param::ProfileImage,
);
context
@@ -1007,35 +1006,15 @@ pub fn housekeeping(context: &Context) {
}
let entry = entry.unwrap();
let name_f = entry.file_name();
let name_c = unsafe { to_cstring(name_f.to_string_lossy()) };
let name_s = name_f.to_string_lossy();
if unsafe { is_file_in_use(&mut files_in_use, 0 as *const libc::c_char, name_c) }
|| unsafe {
is_file_in_use(
&mut files_in_use,
b".increation\x00" as *const u8 as *const libc::c_char,
name_c,
)
}
|| unsafe {
is_file_in_use(
&mut files_in_use,
b".waveform\x00" as *const u8 as *const libc::c_char,
name_c,
)
}
|| unsafe {
is_file_in_use(
&mut files_in_use,
b"-preview.jpg\x00" as *const u8 as *const libc::c_char,
name_c,
)
}
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)
{
unsafe { free(name_c as *mut _) };
continue;
}
unsafe { free(name_c as *mut _) };
unreferenced_count += 1;
@@ -1068,11 +1047,8 @@ pub fn housekeeping(context: &Context) {
unreferenced_count,
entry.file_name()
);
unsafe {
let path = to_cstring(entry.path().to_str().unwrap());
dc_delete_file(context, path);
free(path as *mut _);
}
let path = entry.path().to_c_string().unwrap();
dc_delete_file(context, path.as_ptr());
}
}
Err(err) => {
@@ -1089,26 +1065,18 @@ pub fn housekeeping(context: &Context) {
info!(context, 0, "Housekeeping done.",);
}
unsafe fn is_file_in_use(
files_in_use: &HashSet<String>,
namespc: *const libc::c_char,
name: *const libc::c_char,
) -> bool {
let name_to_check = dc_strdup(name);
if !namespc.is_null() {
let name_len: libc::c_int = strlen(name) as libc::c_int;
let namespc_len: libc::c_int = strlen(namespc) as libc::c_int;
if name_len <= namespc_len
|| strcmp(&*name.offset((name_len - namespc_len) as isize), namespc) != 0
{
fn is_file_in_use(files_in_use: &HashSet<String>, namespc_opt: Option<&str>, name: &str) -> bool {
let name_to_check = if let Some(namespc) = namespc_opt {
let name_len = name.len();
let namespc_len = namespc.len();
if name_len <= namespc_len || !name.ends_with(namespc) {
return false;
}
*name_to_check.offset((name_len - namespc_len) as isize) = 0 as libc::c_char
}
let contains = files_in_use.contains(as_str(name_to_check));
free(name_to_check as *mut libc::c_void);
contains
&name[..name_len - namespc_len]
} else {
name
};
files_in_use.contains(name_to_check)
}
fn maybe_add_file(files_in_use: &mut HashSet<String>, file: impl AsRef<str>) {
@@ -1123,31 +1091,20 @@ fn maybe_add_from_param(
context: &Context,
files_in_use: &mut HashSet<String>,
query: &str,
param_id: libc::c_int,
param_id: Param,
) {
let param = unsafe { dc_param_new() };
context
.sql
.query_row(query, NO_PARAMS, |row| {
unsafe {
let v = to_cstring(row.get::<_, String>(0)?);
dc_param_set_packed(param, v as *const _);
let file = dc_param_get(param, param_id, 0 as *const _);
if !file.is_null() {
maybe_add_file(files_in_use, as_str(file));
free(file as *mut libc::c_void);
}
free(v as *mut _);
let param: Params = row.get::<_, String>(0)?.parse().unwrap_or_default();
if let Some(file) = param.get(param_id) {
maybe_add_file(files_in_use, file);
}
Ok(())
})
.unwrap_or_else(|err| {
warn!(context, 0, "sql: failed to add_from_param: {}", err);
});
unsafe { dc_param_unref(param) };
}
#[cfg(test)]
@@ -1173,26 +1130,12 @@ mod test {
maybe_add_file(&mut files, "$BLOBDIR/world.txt");
maybe_add_file(&mut files, "world2.txt");
assert!(unsafe {
is_file_in_use(
&mut files,
std::ptr::null(),
b"hello\x00" as *const u8 as *const _,
)
});
assert!(!unsafe {
is_file_in_use(
&mut files,
b".txt\x00" as *const u8 as *const _,
b"hello\x00" as *const u8 as *const _,
)
});
assert!(unsafe {
is_file_in_use(
&mut files,
b"-suffix\x00" as *const u8 as *const _,
b"world.txt-suffix\x00" as *const u8 as *const _,
)
});
assert!(is_file_in_use(&mut files, None, "hello"));
assert!(!is_file_in_use(&mut files, Some(".txt"), "hello"));
assert!(is_file_in_use(
&mut files,
Some("-suffix"),
"world.txt-suffix"
));
}
}

394
src/stock.rs Normal file
View File

@@ -0,0 +1,394 @@
use std::borrow::Cow;
use strum::EnumProperty;
use strum_macros::EnumProperty;
use crate::constants::Event;
use crate::contact::*;
use crate::context::Context;
use crate::dc_tools::*;
use libc::free;
/// Stock strings
///
/// These identify the string to return in [Context.stock_str]. The
/// numbers must stay in sync with `deltachat.h` `DC_STR_*` constants.
///
/// See the `stock_*` methods on [Context] to use these.
///
/// [Context]: crate::context::Context
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, EnumProperty)]
#[repr(u32)]
pub enum StockMessage {
#[strum(props(fallback = "No messages."))]
NoMessages = 1,
#[strum(props(fallback = "Me"))]
SelfMsg = 2,
#[strum(props(fallback = "Draft"))]
Draft = 3,
#[strum(props(fallback = "%1$s member(s)"))]
Member = 4,
#[strum(props(fallback = "%1$s contact(s)"))]
Contact = 6,
#[strum(props(fallback = "Voice message"))]
VoiceMessage = 7,
#[strum(props(fallback = "Contact requests"))]
DeadDrop = 8,
#[strum(props(fallback = "Image"))]
Image = 9,
#[strum(props(fallback = "Video"))]
Video = 10,
#[strum(props(fallback = "Audio"))]
Audio = 11,
#[strum(props(fallback = "File"))]
File = 12,
#[strum(props(fallback = "Sent with my Delta Chat Messenger: https://delta.chat"))]
StatusLine = 13,
#[strum(props(fallback = "Hello, I\'ve just created the group \"%1$s\" for us."))]
NewGroupDraft = 14,
#[strum(props(fallback = "Group name changed from \"%1$s\" to \"%2$s\"."))]
MsgGrpName = 15,
#[strum(props(fallback = "Group image changed."))]
MsgGrpImgChanged = 16,
#[strum(props(fallback = "Member %1$s added."))]
MsgAddMember = 17,
#[strum(props(fallback = "Member %1$s removed."))]
MsgDelMember = 18,
#[strum(props(fallback = "Group left."))]
MsgGroupLeft = 19,
#[strum(props(fallback = "GIF"))]
Gif = 23,
#[strum(props(fallback = "Encrypted message"))]
EncryptedMsg = 24,
#[strum(props(fallback = "End-to-end encryption available."))]
E2eAvailable = 25,
#[strum(props(fallback = "Transport-encryption."))]
EncrTransp = 27,
#[strum(props(fallback = "No encryption."))]
EncrNone = 28,
#[strum(props(fallback = "This message was encrypted for another setup."))]
CantDecryptMsgBody = 29,
#[strum(props(fallback = "Fingerprints"))]
FingerPrints = 30,
#[strum(props(fallback = "Return receipt"))]
ReadRcpt = 31,
#[strum(props(fallback = "This is a return receipt for the message \"%1$s\"."))]
ReadRcptMailBody = 32,
#[strum(props(fallback = "Group image deleted."))]
MsgGrpImgDeleted = 33,
#[strum(props(fallback = "End-to-end encryption preferred."))]
E2ePreferred = 34,
#[strum(props(fallback = "%1$s verified."))]
ContactVerified = 35,
#[strum(props(fallback = "Cannot verify %1$s"))]
ContactNotVerified = 36,
#[strum(props(fallback = "Changed setup for %1$s"))]
ContactSetupChanged = 37,
#[strum(props(fallback = "Archived chats"))]
ArchivedChats = 40,
#[strum(props(fallback = "Starred messages"))]
StarredMsgs = 41,
#[strum(props(fallback = "Autocrypt Setup Message"))]
AcSetupMsgSubject = 42,
#[strum(props(
fallback = "This is the Autocrypt Setup Message used to transfer your key between clients.\n\nTo decrypt and use your key, open the message in an Autocrypt-compliant client and enter the setup code presented on the generating device."
))]
AcSetupMsgBody = 43,
#[strum(props(fallback = "Messages I sent to myself"))]
SelfTalkSubTitle = 50,
#[strum(props(fallback = "Cannot login as %1$s."))]
CannotLogin = 60,
#[strum(props(fallback = "Response from %1$s: %2$s"))]
ServerResponse = 61,
#[strum(props(fallback = "%1$s by %2$s."))]
MsgActionByUser = 62,
#[strum(props(fallback = "%1$s by me."))]
MsgActionByMe = 63,
#[strum(props(fallback = "Location streaming enabled."))]
MsgLocationEnabled = 64,
#[strum(props(fallback = "Location streaming disabled."))]
MsgLocationDisabled = 65,
#[strum(props(fallback = "Location"))]
Location = 66,
}
impl StockMessage {
/// Default untranslated strings for stock messages.
///
/// These could be used in logging calls, so no logging here.
fn fallback(&self) -> &'static str {
self.get_str("fallback").unwrap()
}
}
impl Context {
/// Return the stock string for the [StockMessage].
///
/// If the context callback responds with a string to use, e.g. a
/// translation, then this string will be returned. Otherwise a
/// default (English) string is returned.
pub fn stock_str(&self, id: StockMessage) -> Cow<str> {
let ptr = self.call_cb(Event::GET_STRING, id as usize, 0) as *mut libc::c_char;
if ptr.is_null() {
Cow::Borrowed(id.fallback())
} else {
let ret = to_string(ptr);
unsafe { free(ptr as *mut libc::c_void) };
Cow::Owned(ret)
}
}
/// Return stock string, replacing placeholders with provided string.
///
/// This replaces both the *first* `%1$s` **and** `%1$d`
/// placeholders with the provided string.
pub fn stock_string_repl_str(&self, id: StockMessage, insert: impl AsRef<str>) -> String {
self.stock_str(id)
.replacen("%1$s", insert.as_ref(), 1)
.replacen("%1$d", insert.as_ref(), 1)
}
/// Return stock string, replacing placeholders with provided int.
///
/// Like [Context::stock_string_repl_str] but substitute the placeholders
/// with an integer.
pub fn stock_string_repl_int(&self, id: StockMessage, insert: i32) -> String {
self.stock_string_repl_str(id, format!("{}", insert).as_str())
}
/// Return stock string, replacing 2 placeholders with provided string.
///
/// This replaces both the *first* `%1$s` **and** `%1$d`
/// placeholders with the string in `insert` and does the same for
/// `%2$s` and `%2$d` for `insert2`.
fn stock_string_repl_str2(
&self,
id: StockMessage,
insert: impl AsRef<str>,
insert2: impl AsRef<str>,
) -> String {
self.stock_str(id)
.replacen("%1$s", insert.as_ref(), 1)
.replacen("%1$d", insert.as_ref(), 1)
.replacen("%2$s", insert2.as_ref(), 1)
.replacen("%2$d", insert2.as_ref(), 1)
}
/// Return some kind of stock message
///
/// If the `id` is [StockMessage::MsgAddMember] or
/// [StockMessage::MsgDelMember] then `param1` is considered to be the
/// contact address and will be replaced by that contact's display
/// name.
///
/// If `from_id` is not `0`, any trailing dot is removed from the
/// first stock string created so far. If the `from_id` contact is
/// the user itself, i.e. `DC_CONTACT_ID_SELF` the string is used
/// itself as param to the [StockMessage::MsgActionByMe] stock string
/// resulting in a string like "Member Alice added by me." (for
/// [StockMessage::MsgAddMember] as `id`). If the `from_id` contact
/// is any other user than the contact's display name is looked up and
/// used as the second parameter to [StockMessage::MsgActionByUser] with
/// again the original stock string being used as the first parameter,
/// resulting in a string like "Member Alice added by Bob.".
pub fn stock_system_msg(
&self,
id: StockMessage,
param1: impl AsRef<str>,
param2: impl AsRef<str>,
from_id: u32,
) -> String {
let insert1 = if id == StockMessage::MsgAddMember || id == StockMessage::MsgDelMember {
let contact_id = Contact::lookup_id_by_addr(self, param1.as_ref());
if contact_id != 0 {
Contact::get_by_id(self, contact_id)
.map(|contact| contact.get_name_n_addr())
.unwrap_or_default()
} else {
param1.as_ref().to_string()
}
} else {
param1.as_ref().to_string()
};
let action = self.stock_string_repl_str2(id, insert1, param2.as_ref().to_string());
let action1 = action.trim_end_matches('.');
match from_id {
0 => action,
1 => self.stock_string_repl_str(StockMessage::MsgActionByMe, action1), // DC_CONTACT_ID_SELF
_ => {
let displayname = Contact::get_by_id(self, from_id)
.map(|contact| contact.get_name_n_addr())
.unwrap_or_default();
self.stock_string_repl_str2(StockMessage::MsgActionByUser, action1, &displayname)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::*;
use std::ffi::CString;
use crate::constants::DC_CONTACT_ID_SELF;
use crate::context::dc_context_new;
use crate::types::uintptr_t;
use num_traits::ToPrimitive;
#[test]
fn test_enum_mapping() {
assert_eq!(StockMessage::NoMessages.to_usize().unwrap(), 1);
assert_eq!(StockMessage::SelfMsg.to_usize().unwrap(), 2);
}
#[test]
fn test_fallback() {
assert_eq!(StockMessage::NoMessages.fallback(), "No messages.");
}
#[test]
fn test_stock_str() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
assert_eq!(ctx.stock_str(StockMessage::NoMessages), "No messages.");
}
unsafe extern "C" fn test_stock_str_no_fallback_cb(
_ctx: &Context,
evt: Event,
d1: uintptr_t,
_d2: uintptr_t,
) -> uintptr_t {
if evt == Event::GET_STRING && d1 == StockMessage::NoMessages.to_usize().unwrap() {
let tmp = CString::new("Hello there").unwrap();
dc_strdup(tmp.as_ptr()) as usize
} else {
0
}
}
#[test]
fn test_stock_str_no_fallback() {
let t = test_context(Some(test_stock_str_no_fallback_cb));
assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "Hello there");
}
#[test]
fn test_stock_string_repl_str() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
// uses %1$s substitution
assert_eq!(
ctx.stock_string_repl_str(StockMessage::Member, "42"),
"42 member(s)"
);
// We have no string using %1$d to test...
}
#[test]
fn test_stock_string_repl_int() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
assert_eq!(
ctx.stock_string_repl_int(StockMessage::Member, 42),
"42 member(s)"
);
}
#[test]
fn test_stock_string_repl_str2() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
assert_eq!(
ctx.stock_string_repl_str2(StockMessage::ServerResponse, "foo", "bar"),
"Response from foo: bar"
);
}
#[test]
fn test_stock_system_msg_simple() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
assert_eq!(
ctx.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0),
"Location streaming enabled."
)
}
#[test]
fn test_stock_system_msg_add_member_by_me() {
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
assert_eq!(
ctx.stock_system_msg(
StockMessage::MsgAddMember,
"alice@example.com",
"",
DC_CONTACT_ID_SELF as u32
),
"Member alice@example.com added by me."
)
}
#[test]
fn test_stock_system_msg_add_member_by_me_with_displayname() {
let t = dummy_context();
Contact::create(&t.ctx, "Alice", "alice@example.com").expect("failed to create contact");
assert_eq!(
t.ctx.stock_system_msg(
StockMessage::MsgAddMember,
"alice@example.com",
"",
DC_CONTACT_ID_SELF as u32
),
"Member Alice (alice@example.com) added by me."
);
}
#[test]
fn test_stock_system_msg_add_member_by_other_with_displayname() {
let t = dummy_context();
let contact_id = {
Contact::create(&t.ctx, "Alice", "alice@example.com")
.expect("Failed to create contact Alice");
let id =
Contact::create(&t.ctx, "Bob", "bob@example.com").expect("failed to create bob");
id
};
assert_eq!(
t.ctx.stock_system_msg(
StockMessage::MsgAddMember,
"alice@example.com",
"",
contact_id,
),
"Member Alice (alice@example.com) added by Bob (bob@example.com)."
);
}
#[test]
fn test_stock_system_msg_grp_name() {
let t = dummy_context();
assert_eq!(
t.ctx.stock_system_msg(
StockMessage::MsgGrpName,
"Some chat",
"Other chat",
DC_CONTACT_ID_SELF as u32
),
"Group name changed from \"Some chat\" to \"Other chat\" by me."
)
}
#[test]
fn test_stock_system_msg_grp_name_other() {
let t = dummy_context();
let id = Contact::create(&t.ctx, "Alice", "alice@example.com")
.expect("failed to create contact");
assert_eq!(
t.ctx
.stock_system_msg(StockMessage::MsgGrpName, "Some chat", "Other chat", id,),
"Group name changed from \"Some chat\" to \"Other chat\" by Alice (alice@example.com)."
)
}
}

46
src/test_utils.rs Normal file
View File

@@ -0,0 +1,46 @@
//! Utilities to help writing tests.
//!
//! This module is only compiled for test runs.
use tempfile::{tempdir, TempDir};
use crate::context::{dc_context_new, dc_open, Context};
use crate::types::dc_callback_t;
/// A Context and temporary directory.
///
/// The temporary directory can be used to store the SQLite database,
/// see e.g. [test_context] which does this.
pub struct TestContext {
pub ctx: Context,
pub dir: TempDir,
}
/// Create a new, opened [TestContext] using given callback.
///
/// The [Context] will be opened with the SQLite database named
/// "db.sqlite" in the [TestContext.dir] directory.
///
/// [Context]: crate::context::Context
pub fn test_context(cb: Option<dc_callback_t>) -> TestContext {
unsafe {
let mut ctx = dc_context_new(cb, std::ptr::null_mut(), None);
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
assert!(
dc_open(&mut ctx, dbfile.to_str().unwrap(), None),
"Failed to open {}",
dbfile.display(),
);
TestContext { ctx: ctx, dir: dir }
}
}
/// Return a dummy [TestContext].
///
/// The context will be opened and use the SQLite database as
/// specified in [test_context] but there is no callback hooked up,
/// i.e. [Context::call_cb] will always return `0`.
pub fn dummy_context() -> TestContext {
test_context(None)
}

View File

@@ -2,12 +2,14 @@
import os
import re
if __name__ == "__main__":
filestats = []
for fn in os.listdir():
if fn.endswith(".rs"):
s = open(fn).read()
s = re.sub(r'(?m)///.*$', '', s) # remove comments
unsafe = s.count("unsafe")
free = s.count("free(")
gotoblocks = s.count("current_block =")

View File

@@ -1,7 +1,7 @@
#![allow(non_camel_case_types)]
use crate::constants::Event;
use crate::context::Context;
pub use mmime::carray::*;
pub use mmime::clist::*;
pub use rusqlite::ffi::*;
@@ -33,10 +33,8 @@ the online state. */
pub type dc_precheck_imf_t =
unsafe fn(_: &Context, _: *const libc::c_char, _: &str, _: u32) -> libc::c_int;
pub type dc_set_config_t =
unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char) -> ();
pub type dc_get_config_t =
unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char) -> *mut libc::c_char;
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;

View File

@@ -1,5 +1,3 @@
use crate::types::*;
pub use libc::{
calloc, exit, free, malloc, memcmp, memcpy, memmove, memset, realloc, strcat, strchr, strcmp,
strcpy, strcspn, strlen, strncmp, strncpy, strrchr, strspn, strstr, strtol, system,
@@ -37,14 +35,6 @@ pub fn strndup(s: *const libc::c_char, n: libc::c_ulong) -> *mut libc::c_char {
extern "C" {
pub fn clock() -> libc::clock_t;
pub fn qsort(
__base: *mut libc::c_void,
__nel: size_t,
__width: size_t,
__compar: Option<
unsafe extern "C" fn(_: *const libc::c_void, _: *const libc::c_void) -> libc::c_int,
>,
);
// -- DC Methods
pub fn dc_mprintf(format: *const libc::c_char, _: ...) -> *mut libc::c_char;

View File

@@ -1,17 +1,18 @@
//! Stress some functions for testing; if used as a lib, this file is obsolete.
use std::collections::HashSet;
use std::ffi::CString;
use mmime::mailimf_types::*;
use tempfile::{tempdir, TempDir};
use deltachat::config;
use deltachat::constants::*;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::dc_array::*;
use deltachat::dc_chat::*;
use deltachat::dc_configure::*;
use deltachat::dc_contact::*;
use deltachat::dc_imex::*;
use deltachat::dc_location::*;
use deltachat::dc_lot::*;
@@ -123,21 +124,15 @@ unsafe fn stress_functions(context: &Context) {
context.get_blobdir(),
b"foobar\x00" as *const u8 as *const libc::c_char,
);
assert_ne!(0, dc_is_blobdir_path(context, abs_path));
assert_ne!(
0,
dc_is_blobdir_path(
context,
b"$BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
)
);
assert_eq!(
0,
dc_is_blobdir_path(
context,
b"/BLOBDIR/fofo\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));
free(abs_path as *mut libc::c_void);
assert_ne!(
@@ -278,7 +273,6 @@ unsafe fn stress_functions(context: &Context) {
assert!(res.contains(" configured_send_port "));
assert!(res.contains(" configured_server_flags "));
let mut ok: libc::c_int;
let mut buf_0: *mut libc::c_char;
let mut headerline: *const libc::c_char = 0 as *const libc::c_char;
let mut setupcodebegin: *const libc::c_char = 0 as *const libc::c_char;
@@ -288,14 +282,14 @@ unsafe fn stress_functions(context: &Context) {
b"-----BEGIN PGP MESSAGE-----\nNoVal:\n\ndata\n-----END PGP MESSAGE-----\x00" as *const u8
as *const libc::c_char,
);
ok = dc_split_armored_data(
let ok = dc_split_armored_data(
buf_0,
&mut headerline,
&mut setupcodebegin,
0 as *mut *const libc::c_char,
&mut base64,
);
assert_eq!(ok, 1);
assert!(ok);
assert!(!headerline.is_null());
assert_eq!(
strcmp(
@@ -313,7 +307,7 @@ unsafe fn stress_functions(context: &Context) {
buf_0 =
strdup(b"-----BEGIN PGP MESSAGE-----\n\ndat1\n-----END PGP MESSAGE-----\n-----BEGIN PGP MESSAGE-----\n\ndat2\n-----END PGP MESSAGE-----\x00"
as *const u8 as *const libc::c_char);
ok = dc_split_armored_data(
let ok = dc_split_armored_data(
buf_0,
&mut headerline,
&mut setupcodebegin,
@@ -321,7 +315,7 @@ unsafe fn stress_functions(context: &Context) {
&mut base64,
);
assert_eq!(ok, 1);
assert!(ok);
assert!(!headerline.is_null());
assert_eq!(
strcmp(
@@ -340,7 +334,7 @@ unsafe fn stress_functions(context: &Context) {
b"foo \n -----BEGIN PGP MESSAGE----- \n base64-123 \n -----END PGP MESSAGE-----\x00"
as *const u8 as *const libc::c_char,
);
ok = dc_split_armored_data(
let ok = dc_split_armored_data(
buf_0,
&mut headerline,
&mut setupcodebegin,
@@ -348,7 +342,7 @@ unsafe fn stress_functions(context: &Context) {
&mut base64,
);
assert_eq!(ok, 1);
assert!(ok);
assert!(!headerline.is_null());
assert_eq!(
strcmp(
@@ -365,7 +359,7 @@ unsafe fn stress_functions(context: &Context) {
free(buf_0 as *mut libc::c_void);
buf_0 = strdup(b"foo-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char);
ok = dc_split_armored_data(
let ok = dc_split_armored_data(
buf_0,
&mut headerline,
&mut setupcodebegin,
@@ -373,19 +367,19 @@ unsafe fn stress_functions(context: &Context) {
&mut base64,
);
assert_eq!(ok, 0);
assert!(!ok);
free(buf_0 as *mut libc::c_void);
buf_0 =
strdup(b"foo \n -----BEGIN PGP MESSAGE-----\n Passphrase-BeGIN : 23 \n \n base64-567 \r\n abc \n -----END PGP MESSAGE-----\n\n\n\x00"
as *const u8 as *const libc::c_char);
ok = dc_split_armored_data(
let ok = dc_split_armored_data(
buf_0,
&mut headerline,
&mut setupcodebegin,
0 as *mut *const libc::c_char,
&mut base64,
);
assert_eq!(ok, 1);
assert!(ok);
assert!(!headerline.is_null());
assert_eq!(
strcmp(
@@ -412,14 +406,14 @@ unsafe fn stress_functions(context: &Context) {
buf_0 =
strdup(b"-----BEGIN PGP PRIVATE KEY BLOCK-----\n Autocrypt-Prefer-Encrypt : mutual \n\nbase64\n-----END PGP PRIVATE KEY BLOCK-----\x00"
as *const u8 as *const libc::c_char);
ok = dc_split_armored_data(
let ok = dc_split_armored_data(
buf_0,
&mut headerline,
0 as *mut *const libc::c_char,
&mut preferencrypt,
&mut base64,
);
assert_eq!(ok, 1);
assert!(ok);
assert!(!headerline.is_null());
assert_eq!(
strcmp(
@@ -475,16 +469,13 @@ unsafe fn stress_functions(context: &Context) {
let mut setupcodebegin_0: *const libc::c_char = 0 as *const libc::c_char;
let mut preferencrypt_0: *const libc::c_char = 0 as *const libc::c_char;
buf_1 = strdup(S_EM_SETUPFILE);
assert_ne!(
0,
dc_split_armored_data(
buf_1,
&mut headerline_0,
&mut setupcodebegin_0,
&mut preferencrypt_0,
0 as *mut *const libc::c_char,
)
);
assert!(dc_split_armored_data(
buf_1,
&mut headerline_0,
&mut setupcodebegin_0,
&mut preferencrypt_0,
0 as *mut *const libc::c_char,
));
assert!(!headerline_0.is_null());
assert_eq!(
0,
@@ -504,16 +495,13 @@ unsafe fn stress_functions(context: &Context) {
free(buf_1 as *mut libc::c_void);
buf_1 = dc_decrypt_setup_file(context, S_EM_SETUPCODE, S_EM_SETUPFILE);
assert!(!buf_1.is_null());
assert_ne!(
0,
dc_split_armored_data(
buf_1,
&mut headerline_0,
&mut setupcodebegin_0,
&mut preferencrypt_0,
0 as *mut *const libc::c_char,
)
);
assert!(dc_split_armored_data(
buf_1,
&mut headerline_0,
&mut setupcodebegin_0,
&mut preferencrypt_0,
0 as *mut *const libc::c_char,
));
assert!(!headerline_0.is_null());
assert_eq!(
strcmp(
@@ -533,65 +521,21 @@ unsafe fn stress_functions(context: &Context) {
);
free(buf_1 as *mut libc::c_void);
if 0 != dc_is_configured(context) {
let setupcode: *mut libc::c_char;
let setupfile: *mut libc::c_char;
setupcode = dc_create_setup_code(context);
assert!(!setupcode.is_null());
assert_eq!(strlen(setupcode), 44);
assert!(
0 != !(*setupcode.offset(4isize) as libc::c_int == '-' as i32
&& *setupcode.offset(9isize) as libc::c_int == '-' as i32
&& *setupcode.offset(14isize) as libc::c_int == '-' as i32
&& *setupcode.offset(19isize) as libc::c_int == '-' as i32
&& *setupcode.offset(24isize) as libc::c_int == '-' as i32
&& *setupcode.offset(29isize) as libc::c_int == '-' as i32
&& *setupcode.offset(34isize) as libc::c_int == '-' as i32
&& *setupcode.offset(39isize) as libc::c_int == '-' as i32)
as usize
);
setupfile = dc_render_setup_file(context, setupcode);
assert!(!setupfile.is_null());
let buf_2: *mut libc::c_char = dc_strdup(setupfile);
let mut headerline_1: *const libc::c_char = 0 as *const libc::c_char;
let mut setupcodebegin_1: *const libc::c_char = 0 as *const libc::c_char;
assert_eq!(
0,
dc_split_armored_data(
buf_2,
&mut headerline_1,
&mut setupcodebegin_1,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
)
);
assert!(!headerline_1.is_null());
assert_eq!(
strcmp(
headerline_1,
b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char,
),
0
);
assert!(
!(!setupcodebegin_1.is_null()
&& strlen(setupcodebegin_1) == 2
&& strncmp(setupcodebegin_1, setupcode, 2) == 0i32)
);
free(buf_2 as *mut libc::c_void);
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, setupfile);
payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr());
assert!(payload.is_null());
assert_eq!(
0,
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!(!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(
@@ -601,8 +545,6 @@ unsafe fn stress_functions(context: &Context) {
0
);
free(payload as *mut libc::c_void);
free(setupfile as *mut libc::c_void);
free(setupcode as *mut libc::c_void);
}
if 0 != dc_is_configured(context) {
@@ -690,7 +632,7 @@ fn test_encryption_decryption() {
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
let ctext_signed_bytes = ctext.len();
let ctext_signed = to_cstring(ctext);
let ctext_signed = CString::yolo(ctext);
let ctext = dc_pgp_pk_encrypt(
original_text as *const libc::c_void,
@@ -703,7 +645,7 @@ fn test_encryption_decryption() {
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
let ctext_unsigned_bytes = ctext.len();
let ctext_unsigned = to_cstring(ctext);
let ctext_unsigned = CString::yolo(ctext);
let mut keyring = Keyring::default();
keyring.add_owned(private_key);
@@ -717,7 +659,7 @@ fn test_encryption_decryption() {
let mut valid_signatures: HashSet<String> = Default::default();
let plain = dc_pgp_pk_decrypt(
ctext_signed as *const _,
ctext_signed.as_ptr() as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring,
@@ -732,7 +674,7 @@ fn test_encryption_decryption() {
let empty_keyring = Keyring::default();
let plain = dc_pgp_pk_decrypt(
ctext_signed as *const _,
ctext_signed.as_ptr() as *const _,
ctext_signed_bytes,
&keyring,
&empty_keyring,
@@ -745,7 +687,7 @@ fn test_encryption_decryption() {
valid_signatures.clear();
let plain = dc_pgp_pk_decrypt(
ctext_signed as *const _,
ctext_signed.as_ptr() as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring2,
@@ -760,7 +702,7 @@ fn test_encryption_decryption() {
public_keyring2.add_ref(&public_key);
let plain = dc_pgp_pk_decrypt(
ctext_signed as *const _,
ctext_signed.as_ptr() as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring2,
@@ -773,7 +715,7 @@ fn test_encryption_decryption() {
valid_signatures.clear();
let plain = dc_pgp_pk_decrypt(
ctext_unsigned as *const _,
ctext_unsigned.as_ptr() as *const _,
ctext_unsigned_bytes,
&keyring,
&public_keyring,
@@ -781,7 +723,6 @@ fn test_encryption_decryption() {
)
.unwrap();
free(ctext_unsigned as *mut _);
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
valid_signatures.clear();
@@ -792,7 +733,7 @@ fn test_encryption_decryption() {
public_keyring.add_ref(&public_key);
let plain = dc_pgp_pk_decrypt(
ctext_signed as *const _,
ctext_signed.as_ptr() as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring,
@@ -800,7 +741,6 @@ fn test_encryption_decryption() {
)
.unwrap();
free(ctext_signed as *mut _);
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
}
}
@@ -821,16 +761,14 @@ struct TestContext {
}
unsafe fn create_test_context() -> TestContext {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let dir = tempdir().unwrap();
let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap());
assert_eq!(
dc_open(&mut ctx, dbfile, std::ptr::null()),
1,
let dbfile = dir.path().join("db.sqlite");
assert!(
dc_open(&mut ctx, dbfile.to_str().unwrap(), None),
"Failed to open {}",
as_str(dbfile as *const libc::c_char)
dbfile.display()
);
free(dbfile as *mut _);
TestContext { ctx: ctx, dir: dir }
}
@@ -843,34 +781,31 @@ fn test_dc_kml_parse() {
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 kml: *mut dc_kml_t = dc_kml_parse(&context.ctx, xml, strlen(xml));
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",
);
assert!(!kml.addr.is_null());
assert_eq!(as_str(kml.addr as *const libc::c_char), "user@example.org",);
assert_eq!(dc_array_get_cnt((*kml).locations), 2);
let locations_ref = &kml.locations.as_ref().unwrap();
assert_eq!(locations_ref.len(), 2);
assert!(dc_array_get_latitude((*kml).locations, 0) > 53.6f64);
assert!(dc_array_get_latitude((*kml).locations, 0) < 53.8f64);
assert!(dc_array_get_longitude((*kml).locations, 0) > 9.3f64);
assert!(dc_array_get_longitude((*kml).locations, 0) < 9.5f64);
assert!(dc_array_get_accuracy((*kml).locations, 0) > 31.9f64);
assert!(dc_array_get_accuracy((*kml).locations, 0) < 32.1f64);
assert_eq!(dc_array_get_timestamp((*kml).locations, 0), 1551906597);
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!(dc_array_get_latitude((*kml).locations, 1) > 63.6f64);
assert!(dc_array_get_latitude((*kml).locations, 1) < 63.8f64);
assert!(dc_array_get_longitude((*kml).locations, 1) > 19.3f64);
assert!(dc_array_get_longitude((*kml).locations, 1) < 19.5f64);
assert!(dc_array_get_accuracy((*kml).locations, 1) > 2.4f64);
assert!(dc_array_get_accuracy((*kml).locations, 1) < 2.6f64);
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);
assert_eq!(dc_array_get_timestamp((*kml).locations, 1), 1544739072);
dc_kml_unref(kml);
dc_kml_unref(&mut kml);
}
}
@@ -907,7 +842,7 @@ fn test_dc_mimeparser_with_context() {
b"Chat-Version\x00" as *const u8 as *const libc::c_char,
);
assert_eq!(as_str((*of).fld_value as *const libc::c_char), "1.0",);
assert_eq!(carray_count(mimeparser.parts), 1);
assert_eq!(mimeparser.parts.len(), 1);
dc_mimeparser_unref(&mut mimeparser);
}
@@ -920,7 +855,7 @@ fn test_dc_get_oauth2_url() {
let redirect_uri = "chat.delta:/com.b44t.messenger";
let res = dc_get_oauth2_url(&ctx.ctx, addr, redirect_uri);
assert_eq!(res, Some("https://accounts.google.com/o/oauth2/auth?client_id=959970109878-4mvtgf6feshskf7695nfln6002mom908.apps.googleusercontent.com&redirect_uri=chat.delta:/com.b44t.messenger&response_type=code&scope=https%3A%2F%2Fmail.google.com%2F%20email&access_type=offline".into()));
assert_eq!(res, Some("https://accounts.google.com/o/oauth2/auth?client_id=959970109878%2D4mvtgf6feshskf7695nfln6002mom908%2Eapps%2Egoogleusercontent%2Ecom&redirect_uri=chat%2Edelta%3A%2Fcom%2Eb44t%2Emessenger&response_type=code&scope=https%3A%2F%2Fmail.google.com%2F%20email&access_type=offline".into()));
}
#[test]
@@ -955,29 +890,20 @@ fn test_stress_tests() {
fn test_get_contacts() {
unsafe {
let context = create_test_context();
let name = to_cstring("some2");
let contacts = dc_get_contacts(&context.ctx, 0, name);
let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap();
assert_eq!(dc_array_get_cnt(contacts), 0);
dc_array_unref(contacts);
free(name as *mut _);
let name = to_cstring("bob");
let email = to_cstring("bob@mail.de");
let id = dc_create_contact(&context.ctx, name, email);
let id = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
assert_ne!(id, 0);
let contacts = dc_get_contacts(&context.ctx, 0, name);
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).unwrap();
assert_eq!(dc_array_get_cnt(contacts), 1);
dc_array_unref(contacts);
let name2 = to_cstring("alice");
let contacts = dc_get_contacts(&context.ctx, 0, name2);
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap();
assert_eq!(dc_array_get_cnt(contacts), 0);
dc_array_unref(contacts);
free(name as *mut _);
free(name2 as *mut _);
free(email as *mut _);
}
}
@@ -985,12 +911,7 @@ fn test_get_contacts() {
fn test_chat() {
unsafe {
let context = create_test_context();
let name = to_cstring("bob");
let email = to_cstring("bob@mail.de");
let contact1 = dc_create_contact(&context.ctx, name, email);
free(name as *mut _);
free(email as *mut _);
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);
@@ -1010,28 +931,12 @@ fn test_chat() {
#[test]
fn test_wrong_db() {
unsafe {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap();
let dbfile_c = to_cstring(dbfile.to_str().unwrap());
let res = dc_open(&mut ctx, dbfile_c, std::ptr::null());
free(dbfile_c as *mut _);
assert_eq!(res, 0);
let res = dc_open(&mut ctx, dbfile.to_str().unwrap(), None);
assert!(!res);
}
}
#[test]
fn test_arr_to_string() {
let arr2: [uint32_t; 4] = [
0i32 as uint32_t,
12i32 as uint32_t,
133i32 as uint32_t,
1999999i32 as uint32_t,
];
let str_0 = unsafe { dc_arr_to_string(arr2.as_ptr(), 4i32) };
assert_eq!(to_string(str_0), "0,12,133,1999999");
unsafe { free(str_0 as *mut _) };
}