Compare commits

..

5 Commits

Author SHA1 Message Date
holger krekel
e571750f96 make sure c-doc upload dirs exist even if branch name has / in it 2019-10-09 23:23:33 +02:00
dignifiedquire
0f22b75207 updates and fixes 2019-10-09 23:00:15 +02:00
dignifiedquire
13991d8dc5 unify naming in pgp 2019-10-09 22:55:16 +02:00
dignifiedquire
75d9eb08f7 fix and cleanup tests 2019-10-09 22:55:16 +02:00
dignifiedquire
8cf571c78a wip 2019-10-09 22:55:16 +02:00
32 changed files with 704 additions and 958 deletions

View File

@@ -1,24 +0,0 @@
# API changes
## 1.0.0-beta1
- first beta of the Delta Chat Rust core library. many fixes of crashes
and other issues compared to 1.0.0-alpha.5.
- Most code is now "rustified" and does not do manual memory allocation anymore.
- The `DC_EVENT_GET_STRING` event is not used anymore, removing the last
event where the core requested a return value from the event callback.
Please now use `dc_set_stock_translation()` API for core messages
to be properly localized.
- Deltachat FFI docs are automatically generated and available here:
https://c.delta.chat
- New events ImapMessageMoved and ImapMessageDeleted
For a full list of changes, please see our closed Pull Requests:
https://github.com/deltachat/deltachat-core-rust/pulls?q=is%3Apr+is%3Aclosed

388
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
[package]
name = "deltachat"
version = "1.0.0-beta.1"
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
version = "1.0.0-alpha.5"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
edition = "2018"
license = "MPL"
@@ -10,7 +10,7 @@ deltachat_derive = { path = "./deltachat_derive" }
mmime = { version = "0.1.2", path = "./mmime" }
libc = "0.2.51"
pgp = { version = "0.2.3", default-features = false }
pgp = "0.2.3"
hex = "0.3.2"
sha2 = "0.8.0"
rand = "0.6.5"
@@ -19,8 +19,8 @@ reqwest = "0.9.15"
num-derive = "0.2.5"
num-traits = "0.2.6"
native-tls = "0.2.3"
lettre = { git = "https://github.com/deltachat/lettre", branch = "master" }
imap = { git = "https://github.com/jonhoo/rust-imap", branch = "master" }
lettre = { git = "https://github.com/deltachat/lettre" }
imap = { git = "https://github.com/jonhoo/rust-imap", rev = "281d2eb8ab50dc656ceff2ae749ca5045f334e15" }
base64 = "0.10"
charset = "0.1"
percent-encoding = "2.0"

6
Xargo.toml Normal file
View File

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

View File

@@ -1,8 +1,8 @@
[package]
name = "deltachat_ffi"
version = "1.0.0-beta.1"
version = "1.0.0-alpha.5"
description = "Deltachat FFI"
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
edition = "2018"
readme = "README.md"
license = "MIT OR Apache-2.0"

View File

@@ -78,7 +78,8 @@ typedef struct _dc_provider dc_provider_t;
*
* The example above uses "pthreads",
* however, you can also use anything else for thread handling.
* All deltachat-core-functions, unless stated otherwise, are thread-safe.
* NB: The deltachat-core library itself does not create any threads on its own,
* however, functions, unless stated otherwise, are thread-safe.
*
* After that you can **define and open a database.**
* The database is a normal sqlite-file and is created as needed:
@@ -134,7 +135,7 @@ typedef struct _dc_provider dc_provider_t;
*
* printf("Message %i: %s\n", i+1, text);
*
* dc_str_unref(text);
* free(text);
* dc_msg_unref(msg);
* }
* dc_array_unref(msglist);
@@ -320,7 +321,7 @@ int dc_is_open (const dc_context_t* context);
* @memberof dc_context_t
* @param context The context object as created by dc_context_new().
* @return Blob directory associated with the context object, empty string if unset or on errors. NULL is never returned.
* The returned string must be released using dc_str_unref().
* The returned string must be free()'d.
*/
char* dc_get_blobdir (const dc_context_t* context);
@@ -396,7 +397,7 @@ int dc_set_config (dc_context_t* context, const char*
* @param context The context object as created by dc_context_new(). For querying system values, this can be NULL.
* @param key The key to query.
* @return Returns current value of "key", if "key" is unset, the default
* value is returned. The returned value must be released using dc_str_unref(), NULL is never
* value is returned. The returned value must be free()'d, NULL is never
* returned. If there is an error an empty string will be returned.
*/
char* dc_get_config (dc_context_t* context, const char* key);
@@ -406,13 +407,12 @@ char* dc_get_config (dc_context_t* context, const char*
*
* The function will emit warnings if it returns an error state.
*
* @memberof dc_context_t
* @param context The context object
* @param stock_id the integer id of the stock message (DC_STR_*)
* @param stock_msg the message to be used
* @return int (==0 on error, 1 on success)
*/
int dc_set_stock_translation(dc_context_t* context, uint32_t stock_id, const char* stock_msg);
int dc_set_stock_translation(dc_context_t* context, uint32_t, const char* value);
/**
@@ -427,7 +427,7 @@ int dc_set_stock_translation(dc_context_t* context, uint32_t stock_i
*
* @memberof dc_context_t
* @param context The context as created by dc_context_new().
* @return String which must be released using dc_str_unref() after usage. Never returns NULL.
* @return String which must be free()'d after usage. Never returns NULL.
*/
char* dc_get_info (dc_context_t* context);
@@ -458,7 +458,6 @@ char* dc_get_info (dc_context_t* context);
* `https://localhost:PORT/PATH`, `urn:ietf:wg:oauth:2.0:oob`
* (the latter just displays the code the user can copy+paste then)
* @return URL that can be opened in the browser to start OAuth2.
* Returned strings must be released using dc_str_unref().
* If OAuth2 is not possible for the given e-mail-address, NULL is returned.
*/
char* dc_get_oauth2_url (dc_context_t* context, const char* addr, const char* redirect_uri);
@@ -707,7 +706,6 @@ void dc_perform_mvbox_idle (dc_context_t* context);
*/
void dc_interrupt_mvbox_idle (dc_context_t* context);
/**
* Execute pending sentbox-jobs.
* This function and dc_perform_sentbox_fetch() and dc_perform_sentbox_idle()
@@ -1479,7 +1477,7 @@ int dc_set_chat_profile_image (dc_context_t* context, uint32_t ch
* @memberof dc_context_t
* @param context The context object as created by dc_context_new().
* @param msg_id The message id for which information should be generated
* @return Text string, must be released using dc_str_unref() after usage
* @return Text string, must be free()'d after usage
*/
char* dc_get_msg_info (dc_context_t* context, uint32_t msg_id);
@@ -1493,7 +1491,7 @@ char* dc_get_msg_info (dc_context_t* context, uint32_t ms
* @memberof dc_context_t
* @param context The context object as created by dc_context_new().
* @param msg_id The message id, must be the id of an incoming message.
* @return Raw headers as a multi-line string, must be released using dc_str_unref() after usage.
* @return Raw headers as a multi-line string, must be free()'d after usage.
* Returns NULL if there are no headers saved for the given message,
* eg. because of save_mime_headers is not set
* or the message is not incoming.
@@ -1734,7 +1732,7 @@ void dc_block_contact (dc_context_t* context, uint32_t co
* @memberof dc_context_t
* @param context The context object as created by dc_context_new().
* @param contact_id ID of the contact to get the encryption info for.
* @return Multi-line text, must be released using dc_str_unref() after usage.
* @return Multi-line text, must be free()'d after usage.
*/
char* dc_get_contact_encrinfo (dc_context_t* context, uint32_t contact_id);
@@ -1870,8 +1868,7 @@ void dc_imex (dc_context_t* context, int what, c
* @memberof dc_context_t
* @param context The context as created by dc_context_new().
* @param dir Directory to search backups in.
* @return String with the backup file, typically given to dc_imex(),
* returned strings must be released using dc_str_unref().
* @return String with the backup file, typically given to dc_imex(), returned strings must be free()'d.
* The function returns NULL if no backup was found.
*/
char* dc_imex_has_backup (dc_context_t* context, const char* dir);
@@ -1919,7 +1916,7 @@ char* dc_imex_has_backup (dc_context_t* context, const char*
*
* @memberof dc_context_t
* @param context The context object.
* @return The setup code. Must be released using dc_str_unref() after usage.
* @return The setup code. Must be free()'d after usage.
* On errors, eg. if the message could not be sent, NULL is returned.
*/
char* dc_initiate_key_transfer (dc_context_t* context);
@@ -2024,7 +2021,7 @@ dc_lot_t* dc_check_qr (dc_context_t* context, const char*
* If set to 0, the setup-Verified-contact-protocol is offered in the QR code.
* @return Text that should go to the QR code,
* On errors, an empty QR code is returned, NULL is never returned.
* The returned string must be released using dc_str_unref() after usage.
* The returned string must be free()'d after usage.
*/
char* dc_get_securejoin_qr (dc_context_t* context, uint32_t chat_id);
@@ -2194,21 +2191,6 @@ dc_array_t* dc_get_locations (dc_context_t* context, uint32_t cha
void dc_delete_all_locations (dc_context_t* context);
/**
* Release a string returned by another deltachat-core function.
* - Strings returned by any deltachat-core-function
* MUST NOT be released by the standard free() function;
* always use dc_str_unref() for this purpose.
* - dc_str_unref() MUST NOT be called for strings not returned by deltachat-core.
* - dc_str_unref() MUST NOT be called for other objectes returned by deltachat-core.
*
* @memberof dc_context_t
* @param str The string to release.
* @return None.
*/
void dc_str_unref (char* str);
/**
* @class dc_array_t
*
@@ -2349,7 +2331,7 @@ uint32_t dc_array_get_msg_id (const dc_array_t* array, size_t in
* @param index Index of the item. Must be between 0 and dc_array_get_cnt()-1.
* @return Marker-character of the item at the given index.
* NULL if there is no marker-character bound to the given item.
* The returned value must be released using dc_str_unref() after usage.
* The returned value must be free()'d after usage.
*/
char* dc_array_get_marker (const dc_array_t* array, size_t index);
@@ -2604,7 +2586,7 @@ int dc_chat_get_type (const dc_chat_t* chat);
*
* @memberof dc_chat_t
* @param chat The chat object.
* @return Chat name as a string. Must be released using dc_str_unref() after usage. Never NULL.
* @return Chat name as a string. Must be free()'d after usage. Never NULL.
*/
char* dc_chat_get_name (const dc_chat_t* chat);
@@ -2617,7 +2599,7 @@ char* dc_chat_get_name (const dc_chat_t* chat);
*
* @memberof dc_chat_t
* @param chat The chat object to calulate the subtitle for.
* @return Subtitle as a string. Must be released using dc_str_unref() after usage. Never NULL.
* @return Subtitle as a string. Must be free()'d after usage. Never NULL.
*/
char* dc_chat_get_subtitle (const dc_chat_t* chat);
@@ -2633,7 +2615,7 @@ char* dc_chat_get_subtitle (const dc_chat_t* chat);
* @param chat The chat object.
* @return Path and file if the profile image, if any.
* NULL otherwise.
* Must be released using dc_str_unref() after usage.
* Must be free()'d after usage.
*/
char* dc_chat_get_profile_image (const dc_chat_t* chat);
@@ -2937,7 +2919,7 @@ int64_t dc_msg_get_sort_timestamp (const dc_msg_t* msg);
*
* @memberof dc_msg_t
* @param msg The message object.
* @return Message text. The result must be released using dc_str_unref(). Never returns NULL.
* @return Message text. The result must be free()'d. Never returns NULL.
*/
char* dc_msg_get_text (const dc_msg_t* msg);
@@ -2951,9 +2933,9 @@ char* dc_msg_get_text (const dc_msg_t* msg);
*
* @memberof dc_msg_t
* @param msg The message object.
* @return Full path, file name and extension of the file associated with the message.
* If there is no file associated with the message, an emtpy string is returned.
* NULL is never returned and the returned value must be released using dc_str_unref().
* @return Full path, file name and extension of the file associated with the
* message. If there is no file associated with the message, an emtpy
* string is returned. NULL is never returned and the returned value must be free()'d.
*/
char* dc_msg_get_file (const dc_msg_t* msg);
@@ -2966,7 +2948,7 @@ char* dc_msg_get_file (const dc_msg_t* msg);
* @param msg The message object.
* @return Base file name plus extension without part. If there is no file
* associated with the message, an empty string is returned. The returned
* value must be released using dc_str_unref().
* value must be free()'d.
*/
char* dc_msg_get_filename (const dc_msg_t* msg);
@@ -2978,8 +2960,7 @@ char* dc_msg_get_filename (const dc_msg_t* msg);
*
* @memberof dc_msg_t
* @param msg The message object.
* @return String containing the mime type.
* Must be released using dc_str_unref() after usage. NULL is never returned.
* @return String containing the mime type. Must be free()'d after usage. NULL is never returned.
*/
char* dc_msg_get_filemime (const dc_msg_t* msg);
@@ -3087,8 +3068,7 @@ dc_lot_t* dc_msg_get_summary (const dc_msg_t* msg, const dc_cha
* @memberof dc_msg_t
* @param msg The message object.
* @param approx_characters Rough length of the expected string.
* @return A summary for the given messages.
* The returned string must be released using dc_str_unref().
* @return A summary for the given messages. The returned string must be free()'d.
* Returns an empty string on errors, never returns NULL.
*/
char* dc_msg_get_summarytext (const dc_msg_t* msg, int approx_characters);
@@ -3233,7 +3213,7 @@ int dc_msg_is_setupmessage (const dc_msg_t* msg);
* @memberof dc_msg_t
* @param msg The message object.
* @return Typically, the first two digits of the setup code or an empty string if unknown.
* NULL is never returned. Must be released using dc_str_unref() when done.
* NULL is never returned. Must be free()'d when done.
*/
char* dc_msg_get_setupcodebegin (const dc_msg_t* msg);
@@ -3390,8 +3370,7 @@ uint32_t dc_contact_get_id (const dc_contact_t* contact);
*
* @memberof dc_contact_t
* @param contact The contact object.
* @return String with the email address,
* must be released using dc_str_unref(). Never returns NULL.
* @return String with the email address, must be free()'d. Never returns NULL.
*/
char* dc_contact_get_addr (const dc_contact_t* contact);
@@ -3405,8 +3384,7 @@ char* dc_contact_get_addr (const dc_contact_t* contact);
*
* @memberof dc_contact_t
* @param contact The contact object.
* @return String with the name to display, must be released using dc_str_unref().
* Empty string if unset, never returns NULL.
* @return String with the name to display, must be free()'d. Empty string if unset, never returns NULL.
*/
char* dc_contact_get_name (const dc_contact_t* contact);
@@ -3420,8 +3398,7 @@ char* dc_contact_get_name (const dc_contact_t* contact);
*
* @memberof dc_contact_t
* @param contact The contact object.
* @return String with the name to display, must be released using dc_str_unref().
* Never returns NULL.
* @return String with the name to display, must be free()'d. Never returns NULL.
*/
char* dc_contact_get_display_name (const dc_contact_t* contact);
@@ -3437,8 +3414,7 @@ char* dc_contact_get_display_name (const dc_contact_t* contact);
*
* @memberof dc_contact_t
* @param contact The contact object.
* @return Summary string, must be released using dc_str_unref().
* Never returns NULL.
* @return Summary string, must be free()'d. Never returns NULL.
*/
char* dc_contact_get_name_n_addr (const dc_contact_t* contact);
@@ -3450,8 +3426,7 @@ char* dc_contact_get_name_n_addr (const dc_contact_t* contact);
*
* @memberof dc_contact_t
* @param contact The contact object.
* @return String with the name to display, must be released using dc_str_unref().
* Never returns NULL.
* @return String with the name to display, must be free()'d. Never returns NULL.
*/
char* dc_contact_get_first_name (const dc_contact_t* contact);
@@ -3465,7 +3440,7 @@ char* dc_contact_get_first_name (const dc_contact_t* contact);
* @param contact The contact object.
* @return Path and file if the profile image, if any.
* NULL otherwise.
* Must be released using dc_str_unref() after usage.
* Must be free()'d after usage.
*/
char* dc_contact_get_profile_image (const dc_contact_t* contact);
@@ -3546,7 +3521,7 @@ dc_provider_t* dc_provider_new_from_email (const char* email);
*
* @memberof dc_provider_t
* @param provider The dc_provider_t struct.
* @return A string which must be released using dc_str_unref().
* @return A string which must be free()d.
*/
char* dc_provider_get_overview_page (const dc_provider_t* provider);
@@ -3558,7 +3533,7 @@ char* dc_provider_get_overview_page (const dc_provider_t* prov
*
* @memberof dc_provider_t
* @param provider The dc_provider_t struct.
* @return A string which must be released using dc_str_unref().
* @return A string which must be free()d.
*/
char* dc_provider_get_name (const dc_provider_t* provider);
@@ -3571,7 +3546,7 @@ char* dc_provider_get_name (const dc_provider_t* prov
*
* @memberof dc_provider_t
* @param provider The dc_provider_t struct.
* @return A string which must be released using dc_str_unref().
* @return A string which must be free()d.
*/
char* dc_provider_get_markdown (const dc_provider_t* provider);
@@ -3583,7 +3558,7 @@ char* dc_provider_get_markdown (const dc_provider_t* prov
*
* @memberof dc_provider_t
* @param provider The dc_provider_t struct.
* @return A string which must be released using dc_str_unref().
* @return A string which must be free()d.
*/
char* dc_provider_get_status_date (const dc_provider_t* provider);
@@ -3645,9 +3620,7 @@ void dc_lot_unref (dc_lot_t* lot);
*
* @memberof dc_lot_t
* @param lot The lot object.
* @return A string, the string may be empty
* and the returned value must be released using dc_str_unref().
* NULL if there is no such string.
* @return A string, the string may be empty and the returned value must be free()'d. NULL if there is no such string.
*/
char* dc_lot_get_text1 (const dc_lot_t* lot);
@@ -3656,10 +3629,10 @@ char* dc_lot_get_text1 (const dc_lot_t* lot);
* Get second string. The meaning of the string is defined by the creator of the object.
*
* @memberof dc_lot_t
*
* @param lot The lot object.
* @return A string, the string may be empty
* and the returned value must be released using dc_str_unref().
* NULL if there is no such string.
*
* @return A string, the string may be empty and the returned value must be free()'d . NULL if there is no such string.
*/
char* dc_lot_get_text2 (const dc_lot_t* lot);
@@ -3680,7 +3653,9 @@ int dc_lot_get_text1_meaning (const dc_lot_t* lot);
* Get the associated state. The meaning of the state is defined by the creator of the object.
*
* @memberof dc_lot_t
*
* @param lot The lot object.
*
* @return The state as defined by the creator of the object. 0 if there is not state or on errors.
*/
int dc_lot_get_state (const dc_lot_t* lot);
@@ -3702,7 +3677,9 @@ uint32_t dc_lot_get_id (const dc_lot_t* lot);
* The meaning of the timestamp is defined by the creator of the object.
*
* @memberof dc_lot_t
*
* @param lot The lot object.
*
* @return The timestamp as defined by the creator of the object. 0 if there is not timestamp or on errors.
*/
int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
@@ -3902,7 +3879,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*
* @param data1 0
* @param data2 (const char*) Info string in english language.
* Must not be unref'd or modified and is valid only until the callback returns.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_INFO 100
@@ -3913,7 +3890,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*
* @param data1 0
* @param data2 (const char*) Info string in english language.
* Must not be unref'd or modified and is valid only until the callback returns.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_SMTP_CONNECTED 101
@@ -3924,7 +3901,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*
* @param data1 0
* @param data2 (const char*) Info string in english language.
* Must not be unref'd or modified and is valid only until the callback returns.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_IMAP_CONNECTED 102
@@ -3934,7 +3911,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*
* @param data1 0
* @param data2 (const char*) Info string in english language.
* Must not be unref'd or modified and is valid only until the callback returns.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_SMTP_MESSAGE_SENT 103
@@ -3944,7 +3921,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*
* @param data1 0
* @param data2 (const char*) Info string in english language.
* Must not be unref'd or modified and is valid only until the callback returns.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_IMAP_MESSAGE_DELETED 104
@@ -3954,7 +3931,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*
* @param data1 0
* @param data2 (const char*) Info string in english language.
* Must not be unref'd or modified and is valid only until the callback returns.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_IMAP_MESSAGE_MOVED 105
@@ -3964,7 +3941,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*
* @param data1 0
* @param data2 (const char*) path name
* Must not be unref'd or modified and is valid only until the callback returns.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_NEW_BLOB_FILE 150
@@ -3974,7 +3951,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*
* @param data1 0
* @param data2 (const char*) path name
* Must not be unref'd or modified and is valid only until the callback returns.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_DELETED_BLOB_FILE 151
@@ -3987,7 +3964,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*
* @param data1 0
* @param data2 (const char*) Warning string in english language.
* Must not be unref'd or modified and is valid only until the callback returns.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_WARNING 300
@@ -4007,10 +3984,9 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
* in a messasge box then.
*
* @param data1 0
* @param data2 (const char*) Error string, always set, never NULL.
* Some error strings are taken from dc_set_stock_translation(),
* however, most error strings will be in english language.
* Must not be unref'd or modified and is valid only until the callback returns.
* @param data2 (const char*) Error string, always set, never NULL. Frequent error strings are
* localized using #DC_EVENT_GET_STRING, however, most error strings will be in english language.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_ERROR 400
@@ -4034,7 +4010,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
* @param data1 (int) 1=first/new network error, should be reported the user;
* 0=subsequent network error, should be logged only
* @param data2 (const char*) Error string, always set, never NULL.
* Must not be unref'd or modified and is valid only until the callback returns.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_ERROR_NETWORK 401
@@ -4049,7 +4025,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*
* @param data1 0
* @param data2 (const char*) Info string in english language.
* Must not be unref'd or modified
* Must not be free()'d or modified
* and is valid only until the callback returns.
* @return 0
*/
@@ -4180,7 +4156,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
* services.
*
* @param data1 (const char*) Path and file name.
* Must not be unref'd or modified and is valid only until the callback returns.
* Must not be free()'d or modified and is valid only until the callback returns.
* @param data2 0
* @return 0
*/
@@ -4221,21 +4197,30 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
#define DC_EVENT_SECUREJOIN_JOINER_PROGRESS 2061
// the following events are functions that should be provided by the frontends
/**
* (DEPRECATED, DC_EVENT_GET_STRING is not emmitted anymore. Use
* dc_set_stock_translation() to pre-fill translations for stock
* messages.
*
*/
#define DC_EVENT_GET_STRING 2091
/**
* @}
*/
#define DC_EVENT_FILE_COPIED 2055 // not used anymore
#define DC_EVENT_IS_OFFLINE 2081 // not used anymore
#define DC_EVENT_GET_STRING 2091 // not used anymore, use dc_set_stock_translation()
#define DC_ERROR_SEE_STRING 0 // not used anymore
#define DC_ERROR_SELF_NOT_IN_GROUP 1 // not used anymore
#define DC_STR_SELFNOTINGRP 21 // not used anymore
#define DC_EVENT_FILE_COPIED 2055 // deprecated
#define DC_EVENT_IS_OFFLINE 2081 // deprecated
#define DC_ERROR_SEE_STRING 0 // deprecated
#define DC_ERROR_SELF_NOT_IN_GROUP 1 // deprecated
#define DC_STR_SELFNOTINGRP 21 // deprecated
#define DC_EVENT_DATA1_IS_STRING(e) ((e)==DC_EVENT_IMEX_FILE_WRITTEN || (e)==DC_EVENT_FILE_COPIED)
#define DC_EVENT_DATA2_IS_STRING(e) ((e)>=100 && (e)<=499)
#define DC_EVENT_RETURNS_INT(e) ((e)==DC_EVENT_IS_OFFLINE) // not used anymore
#define DC_EVENT_RETURNS_STRING(e) ((e)==DC_EVENT_GET_STRING) // not used anymore
#define DC_EVENT_RETURNS_INT(e) ((e)==DC_EVENT_IS_OFFLINE)
#define DC_EVENT_RETURNS_STRING(e) ((e)==DC_EVENT_GET_STRING)
char* dc_get_version_str (void); // deprecated
void dc_array_add_id (dc_array_t*, uint32_t); // deprecated
@@ -4340,6 +4325,9 @@ void dc_array_add_id (dc_array_t*, uint32_t); // depreca
#define DC_STR_STICKER 67
#define DC_STR_COUNT 67
void dc_str_unref (char*);
/*
* @}
*/

View File

@@ -24,9 +24,7 @@ use num_traits::{FromPrimitive, ToPrimitive};
use deltachat::contact::Contact;
use deltachat::context::Context;
use deltachat::dc_tools::{
as_path, dc_strdup, to_opt_string_lossy, to_string_lossy, OsStrExt, StrExt,
};
use deltachat::dc_tools::{as_path, as_str, dc_strdup, to_string_lossy, OsStrExt, StrExt};
use deltachat::stock::StockMessage;
use deltachat::*;
@@ -305,14 +303,11 @@ pub unsafe extern "C" fn dc_set_config(
return 0;
}
let ffi_context = &*context;
match config::Config::from_str(&to_string_lossy(key)) {
match config::Config::from_str(as_str(key)) {
// When ctx.set_config() fails it already logged the error.
// TODO: Context::set_config() should not log this
Ok(key) => ffi_context
.with_inner(|ctx| {
ctx.set_config(key, to_opt_string_lossy(value).as_ref().map(|x| x.as_str()))
.is_ok() as libc::c_int
})
.with_inner(|ctx| ctx.set_config(key, as_opt_str(value)).is_ok() as libc::c_int)
.unwrap_or(0),
Err(_) => {
ffi_context.error("dc_set_config(): invalid key");
@@ -331,7 +326,7 @@ pub unsafe extern "C" fn dc_get_config(
return "".strdup();
}
let ffi_context = &*context;
match config::Config::from_str(&to_string_lossy(key)) {
match config::Config::from_str(as_str(key)) {
Ok(key) => ffi_context
.with_inner(|ctx| ctx.get_config(key).unwrap_or_default().strdup())
.unwrap_or_else(|_| "".strdup()),
@@ -352,7 +347,7 @@ pub unsafe extern "C" fn dc_set_stock_translation(
eprintln!("ignoring careless call to dc_set_stock_string");
return 0;
}
let msg = to_string_lossy(stock_msg);
let msg = as_str(stock_msg).to_string();
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| match StockMessage::from_u32(stock_id) {
@@ -651,24 +646,22 @@ pub unsafe extern "C" fn dc_get_chatlist(
return ptr::null_mut();
}
let ffi_context = &*context;
let qs = to_opt_string_lossy(query_str);
let qs = if query_str.is_null() {
None
} else {
Some(as_str(query_str))
};
let qi = if query_id == 0 { None } else { Some(query_id) };
ffi_context
.with_inner(|ctx| {
match chatlist::Chatlist::try_load(
ctx,
flags as usize,
qs.as_ref().map(|x| x.as_str()),
qi,
) {
.with_inner(
|ctx| match chatlist::Chatlist::try_load(ctx, flags as usize, qs, qi) {
Ok(list) => {
let ffi_list = ChatlistWrapper { context, list };
Box::into_raw(Box::new(ffi_list))
}
Err(_) => ptr::null_mut(),
}
})
},
)
.unwrap_or_else(|_| ptr::null_mut())
}
@@ -1059,7 +1052,7 @@ pub unsafe extern "C" fn dc_search_msgs(
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| {
let arr = dc_array_t::from(ctx.search_msgs(chat_id, to_string_lossy(query)));
let arr = dc_array_t::from(ctx.search_msgs(chat_id, as_str(query)));
Box::into_raw(Box::new(arr))
})
.unwrap_or_else(|_| ptr::null_mut())
@@ -1101,7 +1094,7 @@ pub unsafe extern "C" fn dc_create_group_chat(
};
ffi_context
.with_inner(|ctx| {
chat::create_group_chat(ctx, verified, to_string_lossy(name))
chat::create_group_chat(ctx, verified, as_str(name))
.unwrap_or_log_default(ctx, "Failed to create group chat")
})
.unwrap_or(0)
@@ -1173,7 +1166,7 @@ pub unsafe extern "C" fn dc_set_chat_name(
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| {
chat::set_chat_name(ctx, chat_id, to_string_lossy(name))
chat::set_chat_name(ctx, chat_id, as_str(name))
.map(|_| 1)
.unwrap_or_log_default(ctx, "Failed to set chat name")
})
@@ -1193,9 +1186,15 @@ pub unsafe extern "C" fn dc_set_chat_profile_image(
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| {
chat::set_chat_profile_image(ctx, chat_id, to_string_lossy(image))
.map(|_| 1)
.unwrap_or_log_default(ctx, "Failed to set profile image")
chat::set_chat_profile_image(ctx, chat_id, {
if image.is_null() {
""
} else {
as_str(image)
}
})
.map(|_| 1)
.unwrap_or_log_default(ctx, "Failed to set profile image")
})
.unwrap_or(0)
}
@@ -1358,7 +1357,7 @@ pub unsafe extern "C" fn dc_may_be_valid_addr(addr: *const libc::c_char) -> libc
return 0;
}
contact::may_be_valid_addr(&to_string_lossy(addr)) as libc::c_int
contact::may_be_valid_addr(as_str(addr)) as libc::c_int
}
#[no_mangle]
@@ -1372,7 +1371,7 @@ pub unsafe extern "C" fn dc_lookup_contact_id_by_addr(
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| Contact::lookup_id_by_addr(ctx, to_string_lossy(addr)))
.with_inner(|ctx| Contact::lookup_id_by_addr(ctx, as_str(addr)))
.unwrap_or(0)
}
@@ -1387,14 +1386,12 @@ pub unsafe extern "C" fn dc_create_contact(
return 0;
}
let ffi_context = &*context;
let name = to_string_lossy(name);
let name = if name.is_null() { "" } else { as_str(name) };
ffi_context
.with_inner(
|ctx| match Contact::create(ctx, name, to_string_lossy(addr)) {
Ok(id) => id,
Err(_) => 0,
},
)
.with_inner(|ctx| match Contact::create(ctx, name, as_str(addr)) {
Ok(id) => id,
Err(_) => 0,
})
.unwrap_or(0)
}
@@ -1410,7 +1407,7 @@ pub unsafe extern "C" fn dc_add_address_book(
let ffi_context = &*context;
ffi_context
.with_inner(
|ctx| match Contact::add_address_book(ctx, to_string_lossy(addr_book)) {
|ctx| match Contact::add_address_book(ctx, as_str(addr_book)) {
Ok(cnt) => cnt as libc::c_int,
Err(_) => 0,
},
@@ -1429,7 +1426,11 @@ pub unsafe extern "C" fn dc_get_contacts(
return ptr::null_mut();
}
let ffi_context = &*context;
let query = to_opt_string_lossy(query);
let query = if query.is_null() {
None
} else {
Some(as_str(query))
};
ffi_context
.with_inner(|ctx| match Contact::get_all(ctx, flags, query) {
Ok(contacts) => Box::into_raw(Box::new(dc_array_t::from(contacts))),
@@ -1566,7 +1567,7 @@ pub unsafe extern "C" fn dc_imex(
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| imex::imex(ctx, what, to_opt_string_lossy(param1)))
.with_inner(|ctx| imex::imex(ctx, what, as_opt_str(param1)))
.ok();
}
@@ -1581,7 +1582,7 @@ pub unsafe extern "C" fn dc_imex_has_backup(
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| match imex::has_backup(ctx, to_string_lossy(dir)) {
.with_inner(|ctx| match imex::has_backup(ctx, as_str(dir)) {
Ok(res) => res.strdup(),
Err(err) => {
error!(ctx, "dc_imex_has_backup: {}", err);
@@ -1624,15 +1625,15 @@ pub unsafe extern "C" fn dc_continue_key_transfer(
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| {
match imex::continue_key_transfer(ctx, msg_id, &to_string_lossy(setup_code)) {
.with_inner(
|ctx| match imex::continue_key_transfer(ctx, msg_id, as_str(setup_code)) {
Ok(()) => 1,
Err(err) => {
error!(ctx, "dc_continue_key_transfer: {}", err);
0
}
}
})
},
)
.unwrap_or(0)
}
@@ -1658,7 +1659,7 @@ pub unsafe extern "C" fn dc_check_qr(
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| {
let lot = qr::check_qr(ctx, to_string_lossy(qr));
let lot = qr::check_qr(ctx, as_str(qr));
Box::into_raw(Box::new(lot))
})
.unwrap_or_else(|_| ptr::null_mut())
@@ -1694,7 +1695,7 @@ pub unsafe extern "C" fn dc_join_securejoin(
}
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| securejoin::dc_join_securejoin(ctx, &to_string_lossy(qr)))
.with_inner(|ctx| securejoin::dc_join_securejoin(ctx, as_str(qr)))
.unwrap_or(0)
}
@@ -1743,7 +1744,7 @@ pub unsafe extern "C" fn dc_set_location(
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| location::set(ctx, latitude, longitude, accuracy))
.unwrap_or(false) as _
.unwrap_or(0)
}
#[no_mangle]
@@ -2609,7 +2610,8 @@ pub unsafe extern "C" fn dc_msg_set_text(msg: *mut dc_msg_t, text: *const libc::
return;
}
let ffi_msg = &mut *msg;
ffi_msg.message.set_text(to_opt_string_lossy(text))
// TODO: {text} equal to NULL is treated as "", which is strange. Does anyone rely on it?
ffi_msg.message.set_text(as_opt_str(text).map(Into::into))
}
#[no_mangle]
@@ -2623,10 +2625,7 @@ pub unsafe extern "C" fn dc_msg_set_file(
return;
}
let ffi_msg = &mut *msg;
ffi_msg.message.set_file(
to_string_lossy(file),
to_opt_string_lossy(filemime).as_ref().map(|x| x.as_str()),
)
ffi_msg.message.set_file(as_str(file), as_opt_str(filemime))
}
#[no_mangle]
@@ -2921,6 +2920,14 @@ pub unsafe extern "C" fn dc_str_unref(s: *mut libc::c_char) {
libc::free(s as *mut _)
}
fn as_opt_str<'a>(s: *const libc::c_char) -> Option<&'a str> {
if s.is_null() {
return None;
}
Some(as_str(s))
}
pub mod providers;
pub trait ResultExt<T> {

View File

@@ -192,7 +192,7 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
let msgtext = msg.get_text();
info!(
context,
"{}#{}{}{}: {} (Contact#{}): {} {}{}{}{}{} [{}]",
"{}#{}{}{}: {} (Contact#{}): {} {}{}{}{} [{}]",
prefix.as_ref(),
msg.get_id() as libc::c_int,
if msg.get_showpadlock() { "🔒" } else { "" },
@@ -211,11 +211,6 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
"[FRESH]"
},
if msg.is_info() { "[INFO]" } else { "" },
if msg.is_forwarded() {
"[FORWARDED]"
} else {
""
},
statestr,
&temp2,
);
@@ -746,7 +741,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let longitude = arg2.parse()?;
let continue_streaming = location::set(context, latitude, longitude, 0.);
if continue_streaming {
if 0 != continue_streaming {
println!("Success, streaming should be continued.");
} else {
println!("Success, streaming can be stoppped.");

View File

@@ -8,7 +8,7 @@ use crate::mailmime::types_helper::*;
pub(crate) use libc::{
calloc, close, free, isalpha, isdigit, malloc, memcmp, memcpy, memmove, memset, realloc,
strcpy, strlen, strncmp, strncpy, strnlen,
strcpy, strlen, strncmp, strncpy,
};
pub(crate) unsafe fn strcasecmp(s1: *const libc::c_char, s2: *const libc::c_char) -> libc::c_int {
@@ -40,10 +40,8 @@ pub(crate) unsafe fn strncasecmp(
// s1 and s2 might not be null terminated.
let s1_slice =
std::slice::from_raw_parts(s1 as *const u8, strnlen(s1 as *const libc::c_char, n));
let s2_slice =
std::slice::from_raw_parts(s2 as *const u8, strnlen(s2 as *const libc::c_char, n));
let s1_slice = std::slice::from_raw_parts(s1 as *const u8, n);
let s2_slice = std::slice::from_raw_parts(s2 as *const u8, n);
let s1 = std::ffi::CStr::from_bytes_with_nul_unchecked(s1_slice)
.to_string_lossy()
@@ -1695,14 +1693,6 @@ mod tests {
4,
)
});
assert_eq!(0, unsafe {
strncasecmp(
CString::new("hell").unwrap().as_ptr(),
CString::new("Hell").unwrap().as_ptr(),
100_000_000,
)
});
}
#[test]

View File

@@ -57,14 +57,6 @@ DC_MSG_AUDIO = 40
DC_MSG_VOICE = 41
DC_MSG_VIDEO = 50
DC_MSG_FILE = 60
DC_LP_AUTH_OAUTH2 = 0x2
DC_LP_AUTH_NORMAL = 0x4
DC_LP_IMAP_SOCKET_STARTTLS = 0x100
DC_LP_IMAP_SOCKET_SSL = 0x200
DC_LP_IMAP_SOCKET_PLAIN = 0x400
DC_LP_SMTP_SOCKET_STARTTLS = 0x10000
DC_LP_SMTP_SOCKET_SSL = 0x20000
DC_LP_SMTP_SOCKET_PLAIN = 0x40000
DC_EVENT_INFO = 100
DC_EVENT_SMTP_CONNECTED = 101
DC_EVENT_IMAP_CONNECTED = 102
@@ -90,9 +82,9 @@ DC_EVENT_IMEX_PROGRESS = 2051
DC_EVENT_IMEX_FILE_WRITTEN = 2052
DC_EVENT_SECUREJOIN_INVITER_PROGRESS = 2060
DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061
DC_EVENT_GET_STRING = 2091
DC_EVENT_FILE_COPIED = 2055
DC_EVENT_IS_OFFLINE = 2081
DC_EVENT_GET_STRING = 2091
DC_STR_SELFNOTINGRP = 21
DC_PROVIDER_STATUS_OK = 1
DC_PROVIDER_STATUS_PREPARATION = 2
@@ -147,7 +139,7 @@ DC_STR_COUNT = 67
def read_event_defines(f):
rex = re.compile(r'#define\s+((?:DC_EVENT_|DC_QR|DC_MSG|DC_LP|DC_STATE_|DC_STR|'
rex = re.compile(r'#define\s+((?:DC_EVENT_|DC_QR|DC_MSG|DC_STATE_|DC_STR|'
r'DC_CONTACT_ID_|DC_GCL|DC_CHAT|DC_PROVIDER)\S+)\s+([x\d]+).*')
for line in f:
m = rex.match(line)

View File

@@ -109,10 +109,6 @@ class Message(object):
""" return True if this message was encrypted. """
return bool(lib.dc_msg_get_showpadlock(self._dc_msg))
def is_forwarded(self):
""" return True if this message was forwarded. """
return bool(lib.dc_msg_is_forwarded(self._dc_msg))
def get_message_info(self):
""" Return informational text for a single message.

View File

@@ -150,11 +150,6 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
lib.dc_set_config(ac._dc_context, b"configured", b"1")
return ac
def peek_online_config(self):
if not session_liveconfig:
pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")
return session_liveconfig.get(self.live_count)
def get_online_config(self):
if not session_liveconfig:
pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")

View File

@@ -365,12 +365,10 @@ class TestOfflineChat:
class TestOnlineAccount:
def get_chat(self, ac1, ac2, both_created=False):
def get_chat(self, ac1, ac2):
c2 = ac1.create_contact(email=ac2.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL
if both_created:
ac2.create_chat_by_contact(ac2.create_contact(email=ac1.get_config("addr")))
return chat
def test_configure_canceled(self, acfactory):
@@ -391,8 +389,7 @@ class TestOnlineAccount:
def test_one_account_send_bcc_setting(self, acfactory, lp):
ac1 = acfactory.get_online_configuring_account()
ac2_config = acfactory.peek_online_config()
c2 = ac1.create_contact(email=ac2_config["addr"])
c2 = ac1.create_contact(email="notexists@testrun.org")
chat = ac1.create_chat_by_contact(c2)
assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL
wait_successful_IMAP_SMTP_connection(ac1)
@@ -413,7 +410,6 @@ class TestOnlineAccount:
lp.sec("send out message without bcc")
ac1.set_config("bcc_self", "0")
msg_out = chat.send_text("message3")
assert not msg_out.is_forwarded()
ev = ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
ev = ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
@@ -456,7 +452,6 @@ class TestOnlineAccount:
# check the message arrived in contact-requests/deaddrop
chat2 = msg_in.chat
assert msg_in in chat2.get_messages()
assert not msg_in.is_forwarded()
assert chat2.is_deaddrop()
assert chat2 == ac2.get_deaddrop_chat()
chat3 = ac2.create_group_chat("newgroup")
@@ -464,35 +459,9 @@ class TestOnlineAccount:
ac2.forward_messages([msg_in], chat3)
assert chat3.is_promoted()
messages = chat3.get_messages()
msg = messages[-1]
assert msg.is_forwarded()
ac2.delete_messages(messages)
assert not chat3.get_messages()
def test_forward_own_message(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
chat = self.get_chat(ac1, ac2, both_created=True)
lp.sec("sending message")
msg_out = chat.send_text("message2")
lp.sec("receiving message")
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
msg_in = ac2.get_message_by_id(ev[2])
assert msg_in.text == "message2"
assert not msg_in.is_forwarded()
lp.sec("ac1: creating group chat, and forward own message")
group = ac1.create_group_chat("newgroup2")
group.add_contact(ac1.create_contact(ac2.get_config("addr")))
ac1.forward_messages([msg_out], group)
# wait for other account to receive
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
msg_in = ac2.get_message_by_id(ev[2])
assert msg_in.text == "message2"
assert msg_in.is_forwarded()
def test_send_and_receive_message_markseen(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
@@ -512,7 +481,6 @@ class TestOnlineAccount:
assert ev[2] == msg_out.id
msg_in = ac2.get_message_by_id(msg_out.id)
assert msg_in.text == "message1"
assert not msg_in.is_forwarded()
lp.sec("check the message arrived in contact-requets/deaddrop")
chat2 = msg_in.chat

View File

@@ -334,7 +334,9 @@ only on image changes.
# Miscellaneous
Messengers SHOULD use the header `In-Reply-To` as usual.
Messengers SHOULD use the header `Chat-Predecessor`
instead of `In-Reply-To` as the latter one results
in infinite threads on typical MUAs.
Messengers SHOULD add a `Chat-Voice-message: 1` header
if an attached audio file is a voice message.
@@ -344,7 +346,7 @@ to specify the duration of attached audio or video files.
The value MUST be the duration in milliseconds.
This allows the receiver to show the time without knowing the file format.
In-Reply-To: Gr.12345uvwxyZ.0005@domain
Chat-Predecessor: foo123@domain
Chat-Voice-Message: 1
Chat-Duration: 10000

View File

@@ -1761,12 +1761,9 @@ pub fn forward_msgs(context: &Context, msg_ids: &[u32], chat_id: u32) -> Result<
}
let mut msg = msg.unwrap();
let original_param = msg.param.clone();
// we tested a sort of broadcast
// by not marking own forwarded messages as such,
// however, this turned out to be to confusing and unclear.
msg.param.set_int(Param::Forwarded, 1);
if msg.from_id != DC_CONTACT_ID_SELF {
msg.param.set_int(Param::Forwarded, 1);
}
msg.param.remove(Param::GuranteeE2ee);
msg.param.remove(Param::ForcePlaintext);
msg.param.remove(Param::Cmd);

View File

@@ -270,7 +270,7 @@ impl Chatlist {
let lastmsg = if 0 != lastmsg_id {
if let Ok(lastmsg) = Message::load_from_db(context, lastmsg_id) {
if lastmsg.from_id != 1
if lastmsg.from_id != 1 as libc::c_uint
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
{
lastcontact = Contact::load_from_db(context, lastmsg.from_id).ok();

View File

@@ -3,7 +3,6 @@ use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::constants::*;
use crate::context::Context;
use crate::error::Error;
use crate::login_param::LoginParam;
use super::read_autoconf_file;
@@ -12,7 +11,7 @@ use super::read_autoconf_file;
******************************************************************************/
/* documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
struct MozAutoconfigure<'a> {
pub in_emailaddr: &'a str,
pub in_0: &'a LoginParam,
pub in_emaildomain: &'a str,
pub in_emaillocalpart: &'a str,
pub out: LoginParam,
@@ -22,7 +21,6 @@ struct MozAutoconfigure<'a> {
pub tag_config: MozConfigTag,
}
#[derive(PartialEq)]
enum MozServer {
Undefined,
Imap,
@@ -37,20 +35,25 @@ enum MozConfigTag {
Username,
}
pub fn moz_parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam, Error> {
let mut reader = quick_xml::Reader::from_str(xml_raw);
reader.trim_text(true);
pub fn moz_autoconfigure(
context: &Context,
url: &str,
param_in: &LoginParam,
) -> Option<LoginParam> {
let xml_raw = read_autoconf_file(context, url)?;
// Split address into local part and domain part.
let p = match in_emailaddr.find('@') {
Some(i) => i,
None => bail!("Email address {} does not contain @", in_emailaddr),
};
let (in_emaillocalpart, in_emaildomain) = in_emailaddr.split_at(p);
let p = param_in.addr.find('@')?;
let (in_emaillocalpart, in_emaildomain) = param_in.addr.split_at(p);
let in_emaildomain = &in_emaildomain[1..];
let mut reader = quick_xml::Reader::from_str(&xml_raw);
reader.trim_text(true);
let mut buf = Vec::new();
let mut moz_ac = MozAutoconfigure {
in_emailaddr,
in_0: param_in,
in_emaildomain,
in_emaillocalpart,
out: LoginParam::new(),
@@ -59,8 +62,6 @@ pub fn moz_parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam, Er
tag_server: MozServer::Undefined,
tag_config: MozConfigTag::Undefined,
};
let mut buf = Vec::new();
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
@@ -71,7 +72,8 @@ pub fn moz_parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam, Er
moz_autoconfigure_text_cb(e, &mut moz_ac, &reader)
}
Err(e) => {
bail!(
warn!(
context,
"Configure xml: Error at position {}: {:?}",
reader.buffer_position(),
e
@@ -89,26 +91,11 @@ pub fn moz_parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam, Er
|| moz_ac.out.send_port == 0
{
let r = moz_ac.out.to_string();
bail!("Bad or incomplete autoconfig: {}", r,);
warn!(context, "Bad or incomplete autoconfig: {}", r,);
return None;
}
Ok(moz_ac.out)
}
pub fn moz_autoconfigure(
context: &Context,
url: &str,
param_in: &LoginParam,
) -> Option<LoginParam> {
let xml_raw = read_autoconf_file(context, url)?;
match moz_parse_xml(&param_in.addr, &xml_raw) {
Err(err) => {
warn!(context, "{}", err);
None
}
Ok(lp) => Some(lp),
}
Some(moz_ac.out)
}
fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
@@ -118,7 +105,7 @@ fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
) {
let val = event.unescape_and_decode(reader).unwrap_or_default();
let addr = moz_ac.in_emailaddr;
let addr = &moz_ac.in_0.addr;
let email_local = moz_ac.in_emaillocalpart;
let email_domain = moz_ac.in_emaildomain;
@@ -173,17 +160,13 @@ fn moz_autoconfigure_endtag_cb(event: &BytesEnd, moz_ac: &mut MozAutoconfigure)
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
if tag == "incomingserver" {
if moz_ac.tag_server == MozServer::Imap {
moz_ac.out_imap_set = true;
}
moz_ac.tag_server = MozServer::Undefined;
moz_ac.tag_config = MozConfigTag::Undefined;
moz_ac.out_imap_set = true;
} else if tag == "outgoingserver" {
if moz_ac.tag_server == MozServer::Smtp {
moz_ac.out_smtp_set = true;
}
moz_ac.tag_server = MozServer::Undefined;
moz_ac.tag_config = MozConfigTag::Undefined;
moz_ac.out_smtp_set = true;
} else {
moz_ac.tag_config = MozConfigTag::Undefined;
}
@@ -234,89 +217,3 @@ fn moz_autoconfigure_starttag_cb<B: std::io::BufRead>(
moz_ac.tag_config = MozConfigTag::Username;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_outlook_autoconfig() {
// Copied from https://autoconfig.thunderbird.net/v1.1/outlook.com on 2019-10-11
let xml_raw =
"<clientConfig version=\"1.1\">
<emailProvider id=\"outlook.com\">
<domain>hotmail.com</domain>
<domain>hotmail.co.uk</domain>
<domain>hotmail.co.jp</domain>
<domain>hotmail.com.br</domain>
<domain>hotmail.de</domain>
<domain>hotmail.fr</domain>
<domain>hotmail.it</domain>
<domain>hotmail.es</domain>
<domain>live.com</domain>
<domain>live.co.uk</domain>
<domain>live.co.jp</domain>
<domain>live.de</domain>
<domain>live.fr</domain>
<domain>live.it</domain>
<domain>live.jp</domain>
<domain>msn.com</domain>
<domain>outlook.com</domain>
<displayName>Outlook.com (Microsoft)</displayName>
<displayShortName>Outlook</displayShortName>
<incomingServer type=\"exchange\">
<hostname>outlook.office365.com</hostname>
<port>443</port>
<username>%EMAILADDRESS%</username>
<socketType>SSL</socketType>
<authentication>OAuth2</authentication>
<owaURL>https://outlook.office365.com/owa/</owaURL>
<ewsURL>https://outlook.office365.com/ews/exchange.asmx</ewsURL>
<useGlobalPreferredServer>true</useGlobalPreferredServer>
</incomingServer>
<incomingServer type=\"imap\">
<hostname>outlook.office365.com</hostname>
<port>993</port>
<socketType>SSL</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
</incomingServer>
<incomingServer type=\"pop3\">
<hostname>outlook.office365.com</hostname>
<port>995</port>
<socketType>SSL</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
<pop3>
<leaveMessagesOnServer>true</leaveMessagesOnServer>
<!-- Outlook.com docs specifically mention that POP3 deletes have effect on the main inbox on webmail and IMAP -->
</pop3>
</incomingServer>
<outgoingServer type=\"smtp\">
<hostname>smtp.office365.com</hostname>
<port>587</port>
<socketType>STARTTLS</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
</outgoingServer>
<documentation url=\"http://windows.microsoft.com/en-US/windows/outlook/send-receive-from-app\">
<descr lang=\"en\">Set up an email app with Outlook.com</descr>
</documentation>
</emailProvider>
<webMail>
<loginPage url=\"https://www.outlook.com/\"/>
<loginPageInfo url=\"https://www.outlook.com/\">
<username>%EMAILADDRESS%</username>
<usernameField id=\"i0116\" name=\"login\"/>
<passwordField id=\"i0118\" name=\"passwd\"/>
<loginButton id=\"idSIButton9\" name=\"SI\"/>
</loginPageInfo>
</webMail>
</clientConfig>";
let res = moz_parse_xml("example@outlook.com", xml_raw).expect("XML parsing failed");
assert_eq!(res.mail_server, "outlook.office365.com");
assert_eq!(res.mail_port, 993);
assert_eq!(res.send_server, "smtp.office365.com");
assert_eq!(res.send_port, 587);
}
}

View File

@@ -3,7 +3,6 @@ use quick_xml::events::BytesEnd;
use crate::constants::*;
use crate::context::Context;
use crate::error::Error;
use crate::login_param::LoginParam;
use super::read_autoconf_file;
@@ -20,100 +19,6 @@ struct OutlookAutodiscover {
pub config_redirecturl: Option<String>,
}
enum ParsingResult {
LoginParam(LoginParam),
RedirectUrl(String),
}
fn outlk_parse_xml(xml_raw: &str) -> Result<ParsingResult, Error> {
let mut outlk_ad = OutlookAutodiscover {
out: LoginParam::new(),
out_imap_set: false,
out_smtp_set: false,
config_type: None,
config_server: String::new(),
config_port: 0,
config_ssl: String::new(),
config_redirecturl: None,
};
let mut reader = quick_xml::Reader::from_str(&xml_raw);
reader.trim_text(true);
let mut buf = Vec::new();
let mut current_tag: Option<String> = None;
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
let tag = String::from_utf8_lossy(e.name()).trim().to_lowercase();
if tag == "protocol" {
outlk_ad.config_type = None;
outlk_ad.config_server = String::new();
outlk_ad.config_port = 0;
outlk_ad.config_ssl = String::new();
outlk_ad.config_redirecturl = None;
current_tag = None;
} else {
current_tag = Some(tag);
}
}
Ok(quick_xml::events::Event::End(ref e)) => {
outlk_autodiscover_endtag_cb(e, &mut outlk_ad);
current_tag = None;
}
Ok(quick_xml::events::Event::Text(ref e)) => {
let val = e.unescape_and_decode(&reader).unwrap_or_default();
if let Some(ref tag) = current_tag {
match tag.as_str() {
"type" => {
outlk_ad.config_type = Some(val.trim().to_lowercase().to_string())
}
"server" => outlk_ad.config_server = val.trim().to_string(),
"port" => outlk_ad.config_port = val.trim().parse().unwrap_or_default(),
"ssl" => outlk_ad.config_ssl = val.trim().to_string(),
"redirecturl" => outlk_ad.config_redirecturl = Some(val.trim().to_string()),
_ => {}
};
}
}
Err(e) => {
bail!(
"Configure xml: Error at position {}: {:?}",
reader.buffer_position(),
e
);
}
Ok(quick_xml::events::Event::Eof) => break,
_ => (),
}
buf.clear();
}
// XML redirect via redirecturl
if outlk_ad.config_redirecturl.is_none()
|| outlk_ad.config_redirecturl.as_ref().unwrap().is_empty()
{
if outlk_ad.out.mail_server.is_empty()
|| outlk_ad.out.mail_port == 0
|| outlk_ad.out.send_server.is_empty()
|| outlk_ad.out.send_port == 0
{
let r = outlk_ad.out.to_string();
bail!("Bad or incomplete autoconfig: {}", r,);
}
Ok(ParsingResult::LoginParam(outlk_ad.out))
} else {
Ok(ParsingResult::RedirectUrl(
outlk_ad.config_redirecturl.unwrap(),
))
}
}
pub fn outlk_autodiscover(
context: &Context,
url: &str,
@@ -122,16 +27,94 @@ pub fn outlk_autodiscover(
let mut url = url.to_string();
/* Follow up to 10 xml-redirects (http-redirects are followed in read_autoconf_file() */
for _i in 0..10 {
let mut outlk_ad = OutlookAutodiscover {
out: LoginParam::new(),
out_imap_set: false,
out_smtp_set: false,
config_type: None,
config_server: String::new(),
config_port: 0,
config_ssl: String::new(),
config_redirecturl: None,
};
if let Some(xml_raw) = read_autoconf_file(context, &url) {
match outlk_parse_xml(&xml_raw) {
Err(err) => {
warn!(context, "{}", err);
let mut reader = quick_xml::Reader::from_str(&xml_raw);
reader.trim_text(true);
let mut buf = Vec::new();
let mut current_tag: Option<String> = None;
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
let tag = String::from_utf8_lossy(e.name()).trim().to_lowercase();
if tag == "protocol" {
outlk_ad.config_type = None;
outlk_ad.config_server = String::new();
outlk_ad.config_port = 0;
outlk_ad.config_ssl = String::new();
outlk_ad.config_redirecturl = None;
current_tag = None;
} else {
current_tag = Some(tag);
}
}
Ok(quick_xml::events::Event::End(ref e)) => {
outlk_autodiscover_endtag_cb(e, &mut outlk_ad);
current_tag = None;
}
Ok(quick_xml::events::Event::Text(ref e)) => {
let val = e.unescape_and_decode(&reader).unwrap_or_default();
if let Some(ref tag) = current_tag {
match tag.as_str() {
"type" => outlk_ad.config_type = Some(val.trim().to_string()),
"server" => outlk_ad.config_server = val.trim().to_string(),
"port" => {
outlk_ad.config_port = val.trim().parse().unwrap_or_default()
}
"ssl" => outlk_ad.config_ssl = val.trim().to_string(),
"redirecturl" => {
outlk_ad.config_redirecturl = Some(val.trim().to_string())
}
_ => {}
};
}
}
Err(e) => {
error!(
context,
"Configure xml: Error at position {}: {:?}",
reader.buffer_position(),
e
);
}
Ok(quick_xml::events::Event::Eof) => break,
_ => (),
}
buf.clear();
}
// XML redirect via redirecturl
if outlk_ad.config_redirecturl.is_none()
|| outlk_ad.config_redirecturl.as_ref().unwrap().is_empty()
{
if outlk_ad.out.mail_server.is_empty()
|| outlk_ad.out.mail_port == 0
|| outlk_ad.out.send_server.is_empty()
|| outlk_ad.out.send_port == 0
{
let r = outlk_ad.out.to_string();
warn!(context, "Bad or incomplete autoconfig: {}", r,);
return None;
}
Ok(res) => match res {
ParsingResult::RedirectUrl(redirect_url) => url = redirect_url,
ParsingResult::LoginParam(login_param) => return Some(login_param),
},
return Some(outlk_ad.out);
} else {
url = outlk_ad.config_redirecturl.unwrap();
}
} else {
return None;
@@ -172,78 +155,3 @@ fn outlk_autodiscover_endtag_cb(event: &BytesEnd, outlk_ad: &mut OutlookAutodisc
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_redirect() {
let res = outlk_parse_xml("
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">
<Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">
<Account>
<AccountType>email</AccountType>
<Action>redirectUrl</Action>
<RedirectUrl>https://mail.example.com/autodiscover/autodiscover.xml</RedirectUrl>
</Account>
</Response>
</Autodiscover>
").expect("XML is not parsed successfully");
match res {
ParsingResult::LoginParam(_lp) => {
panic!("redirecturl is not found");
}
ParsingResult::RedirectUrl(url) => {
assert_eq!(
url,
"https://mail.example.com/autodiscover/autodiscover.xml"
);
}
}
}
#[test]
fn test_parse_loginparam() {
let res = outlk_parse_xml(
"\
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">
<Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">
<Account>
<AccountType>email</AccountType>
<Action>settings</Action>
<Protocol>
<Type>IMAP</Type>
<Server>example.com</Server>
<Port>993</Port>
<SSL>on</SSL>
<AuthRequired>on</AuthRequired>
</Protocol>
<Protocol>
<Type>SMTP</Type>
<Server>smtp.example.com</Server>
<Port>25</Port>
<SSL>off</SSL>
<AuthRequired>on</AuthRequired>
</Protocol>
</Account>
</Response>
</Autodiscover>",
)
.expect("XML is not parsed successfully");
match res {
ParsingResult::LoginParam(lp) => {
assert_eq!(lp.mail_server, "example.com");
assert_eq!(lp.mail_port, 993);
assert_eq!(lp.send_server, "smtp.example.com");
assert_eq!(lp.send_port, 25);
}
ParsingResult::RedirectUrl(_) => {
panic!("RedirectUrl is not expected");
}
}
}
}

View File

@@ -112,7 +112,6 @@ pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
dc_get_oauth2_addr(context, &param.addr, &param.mail_pw)
.and_then(|e| e.parse().ok())
{
info!(context, "Authorized address is {}", oauth2_addr);
param.addr = oauth2_addr;
context
.sql

View File

@@ -150,7 +150,7 @@ impl Context {
};
ensure!(
ctx.sql.open(&ctx, &ctx.dbfile, false),
ctx.sql.open(&ctx, &ctx.dbfile, 0),
"Failed opening sqlite database"
);

View File

@@ -607,8 +607,12 @@ impl<'a> MimeParser<'a> {
/* regard `Content-Transfer-Encoding:` */
let mut desired_filename = String::default();
let mut simplifier: Option<Simplify> = None;
match mime_type {
DC_MIMETYPE_TEXT_PLAIN | DC_MIMETYPE_TEXT_HTML => {
if simplifier.is_none() {
simplifier = Some(Simplify::new());
}
/* get from `Content-Type: text/...; charset=utf-8`; must not be free()'d */
let charset = mailmime_content_charset_get((*mime).mm_content_type);
if !charset.is_null()
@@ -636,14 +640,13 @@ impl<'a> MimeParser<'a> {
/* check header directly as is_send_by_messenger is not yet set up */
let is_msgrmsg = self.lookup_optional_field("Chat-Version").is_some();
let mut simplifier = Simplify::new();
let simplified_txt = if decoded_data.is_empty() {
"".into()
} else {
let input = std::string::String::from_utf8_lossy(&decoded_data);
let is_html = mime_type == 70;
simplifier.simplify(&input, is_html, is_msgrmsg)
simplifier.unwrap().simplify(&input, is_html, is_msgrmsg)
};
if !simplified_txt.is_empty() {
let mut part = Part::default();
@@ -655,7 +658,7 @@ impl<'a> MimeParser<'a> {
self.do_add_single_part(part);
}
if simplifier.is_forwarded {
if simplifier.unwrap().is_forwarded {
self.is_forwarded = true;
}
}

View File

@@ -1,7 +1,7 @@
use std::ptr;
use itertools::join;
use libc::strcmp;
use libc::{free, strcmp};
use mmime::clist::*;
use mmime::mailimf::types::*;
use mmime::mailmime::content::*;
@@ -319,8 +319,13 @@ unsafe fn add_parts(
let mut chat_id_blocked = Blocked::Not;
let mut sort_timestamp = 0;
let mut rcvd_timestamp = 0;
let mut mime_in_reply_to = String::new();
let mut mime_references = String::new();
let mut mime_in_reply_to = std::ptr::null_mut();
let mut mime_references = std::ptr::null_mut();
let cleanup = |mime_in_reply_to: *mut libc::c_char, mime_references: *mut libc::c_char| {
free(mime_in_reply_to.cast());
free(mime_references.cast());
};
// collect the rest information, CC: is added to the to-list, BCC: is ignored
// (we should not add BCC to groups as this would split groups. We could add them as "known contacts",
@@ -354,6 +359,7 @@ unsafe fn add_parts(
message::update_server_uid(context, &rfc724_mid, server_folder.as_ref(), server_uid);
}
cleanup(mime_in_reply_to, mime_references);
bail!("Message already in DB");
}
@@ -582,14 +588,20 @@ unsafe fn add_parts(
if let Some(field) = mime_parser.lookup_field_typ("In-Reply-To", MAILIMF_FIELD_IN_REPLY_TO) {
let fld_in_reply_to = (*field).fld_data.fld_in_reply_to;
if !fld_in_reply_to.is_null() {
mime_in_reply_to = dc_str_from_clist((*(*field).fld_data.fld_in_reply_to).mid_list, " ")
mime_in_reply_to = dc_str_from_clist(
(*(*field).fld_data.fld_in_reply_to).mid_list,
b" \x00" as *const u8 as *const libc::c_char,
)
}
}
if let Some(field) = mime_parser.lookup_field_typ("References", MAILIMF_FIELD_REFERENCES) {
let fld_references = (*field).fld_data.fld_references;
if !fld_references.is_null() {
mime_references = dc_str_from_clist((*(*field).fld_data.fld_references).mid_list, " ")
mime_references = dc_str_from_clist(
(*(*field).fld_data.fld_references).mid_list,
b" \x00" as *const u8 as *const libc::c_char,
)
}
}
@@ -600,91 +612,97 @@ unsafe fn add_parts(
let icnt = mime_parser.parts.len();
let mut txt_raw = None;
context.sql.prepare(
"INSERT INTO msgs \
(rfc724_mid, server_folder, server_uid, chat_id, from_id, to_id, timestamp, \
timestamp_sent, timestamp_rcvd, type, state, msgrmsg, txt, txt_raw, param, \
bytes, hidden, mime_headers, mime_in_reply_to, mime_references) \
VALUES (?,?,?,?,?,?, ?,?,?,?,?,?, ?,?,?,?,?,?, ?,?);",
|mut stmt, conn| {
for i in 0..icnt {
let part = &mut mime_parser.parts[i];
if part.is_meta {
continue;
}
context
.sql
.prepare(
"INSERT INTO msgs \
(rfc724_mid, server_folder, server_uid, chat_id, from_id, to_id, timestamp, \
timestamp_sent, timestamp_rcvd, type, state, msgrmsg, txt, txt_raw, param, \
bytes, hidden, mime_headers, mime_in_reply_to, mime_references) \
VALUES (?,?,?,?,?,?, ?,?,?,?,?,?, ?,?,?,?,?,?, ?,?);",
|mut stmt, conn| {
for i in 0..icnt {
let part = &mut mime_parser.parts[i];
if part.is_meta {
continue;
}
if let Some(ref msg) = part.msg {
if mime_parser.location_kml.is_some()
&& icnt == 1
&& (msg == "-location-" || msg.is_empty())
{
*hidden = 1;
if state == MessageState::InFresh {
state = MessageState::InNoticed;
if let Some(ref msg) = part.msg {
if mime_parser.location_kml.is_some()
&& icnt == 1
&& (msg == "-location-" || msg.is_empty())
{
*hidden = 1;
if state == MessageState::InFresh {
state = MessageState::InNoticed;
}
}
}
}
if part.typ == Viewtype::Text {
let msg_raw = part.msg_raw.as_ref().cloned().unwrap_or_default();
let subject = mime_parser
.subject
.as_ref()
.map(|s| s.to_string())
.unwrap_or("".into());
txt_raw = Some(format!("{}\n\n{}", subject, msg_raw));
}
if mime_parser.is_system_message != SystemMessage::Unknown {
part.param
.set_int(Param::Cmd, mime_parser.is_system_message as i32);
}
if part.typ == Viewtype::Text {
let msg_raw = part.msg_raw.as_ref().cloned().unwrap_or_default();
let subject = mime_parser
.subject
.as_ref()
.map(|s| s.to_string())
.unwrap_or("".into());
txt_raw = Some(format!("{}\n\n{}", subject, msg_raw));
}
if mime_parser.is_system_message != SystemMessage::Unknown {
part.param
.set_int(Param::Cmd, mime_parser.is_system_message as i32);
}
/*
info!(
context,
"received mime message {:?}",
String::from_utf8_lossy(std::slice::from_raw_parts(
imf_raw_not_terminated as *const u8,
imf_raw_bytes,
))
);
*/
/*
info!(
context,
"received mime message {:?}",
String::from_utf8_lossy(std::slice::from_raw_parts(
imf_raw_not_terminated as *const u8,
imf_raw_bytes,
))
);
*/
stmt.execute(params![
rfc724_mid,
server_folder.as_ref(),
server_uid as libc::c_int,
*chat_id as libc::c_int,
*from_id as libc::c_int,
*to_id as libc::c_int,
sort_timestamp,
*sent_timestamp,
rcvd_timestamp,
part.typ,
state,
msgrmsg,
part.msg.as_ref().map_or("", String::as_str),
// txt_raw might contain invalid utf8
txt_raw.unwrap_or_default(),
part.param.to_string(),
part.bytes,
*hidden,
if save_mime_headers {
Some(String::from_utf8_lossy(imf_raw))
} else {
None
},
mime_in_reply_to,
mime_references,
])?;
stmt.execute(params![
rfc724_mid,
server_folder.as_ref(),
server_uid as libc::c_int,
*chat_id as libc::c_int,
*from_id as libc::c_int,
*to_id as libc::c_int,
sort_timestamp,
*sent_timestamp,
rcvd_timestamp,
part.typ,
state,
msgrmsg,
part.msg.as_ref().map_or("", String::as_str),
// txt_raw might contain invalid utf8
txt_raw.unwrap_or_default(),
part.param.to_string(),
part.bytes,
*hidden,
if save_mime_headers {
Some(String::from_utf8_lossy(imf_raw))
} else {
None
},
to_string_lossy(mime_in_reply_to),
to_string_lossy(mime_references),
])?;
txt_raw = None;
*insert_msg_id =
sql::get_rowid_with_conn(context, conn, "msgs", "rfc724_mid", &rfc724_mid);
created_db_entries.push((*chat_id as usize, *insert_msg_id as usize));
}
Ok(())
},
)?;
txt_raw = None;
*insert_msg_id =
sql::get_rowid_with_conn(context, conn, "msgs", "rfc724_mid", &rfc724_mid);
created_db_entries.push((*chat_id as usize, *insert_msg_id as usize));
}
Ok(())
},
)
.map_err(|err| {
cleanup(mime_in_reply_to, mime_references);
err
})?;
info!(
context,
@@ -704,6 +722,8 @@ unsafe fn add_parts(
}
}
cleanup(mime_in_reply_to, mime_references);
Ok(())
}

View File

@@ -142,8 +142,8 @@ impl Simplify {
ret += "[...]";
}
/* we write empty lines only in case and non-empty line follows */
let mut pending_linebreaks = 0;
let mut content_lines_added = 0;
let mut pending_linebreaks: libc::c_int = 0i32;
let mut content_lines_added: libc::c_int = 0i32;
for l in l_first..l_last {
let line = lines[l];
if is_empty_line(line) {

View File

@@ -74,18 +74,36 @@ pub(crate) fn dc_truncate(buf: &str, approx_chars: usize, do_unwrap: bool) -> Co
}
}
pub(crate) fn dc_str_from_clist(list: *const clist, delimiter: &str) -> String {
pub(crate) unsafe fn dc_str_from_clist(
list: *const clist,
delimiter: *const libc::c_char,
) -> *mut libc::c_char {
let mut res = String::new();
if !list.is_null() {
for rfc724_mid in unsafe { (*list).into_iter() } {
if !res.is_empty() {
res += delimiter;
let mut cur: *mut clistiter = (*list).first;
while !cur.is_null() {
let rfc724_mid = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *const libc::c_char;
if !rfc724_mid.is_null() {
if !res.is_empty() && !delimiter.is_null() {
res += as_str(delimiter);
}
res += as_str(rfc724_mid);
}
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
res += as_str(rfc724_mid as *const libc::c_char);
}
}
res
res.strdup()
}
pub(crate) fn dc_str_to_clist(str: &str, delimiter: &str) -> *mut clist {
@@ -735,14 +753,6 @@ pub fn to_string_lossy(s: *const libc::c_char) -> String {
cstr.to_string_lossy().to_string()
}
pub fn to_opt_string_lossy(s: *const libc::c_char) -> Option<String> {
if s.is_null() {
return None;
}
Some(to_string_lossy(s))
}
pub fn as_str<'a>(s: *const libc::c_char) -> &'a str {
as_str_safe(s).unwrap_or_else(|err| panic!("{}", err))
}
@@ -1020,6 +1030,21 @@ mod tests {
}
}
fn strndup(s: *const libc::c_char, n: libc::c_ulong) -> *mut libc::c_char {
if s.is_null() {
return std::ptr::null_mut();
}
let end = std::cmp::min(n as usize, unsafe { strlen(s) });
unsafe {
let result = libc::malloc(end + 1);
memcpy(result, s as *const _, end);
std::ptr::write_bytes(result.offset(end as isize), b'\x00', 1);
result as *mut _
}
}
#[test]
fn test_dc_str_to_clist_1() {
unsafe {
@@ -1035,11 +1060,17 @@ mod tests {
unsafe {
let list: *mut clist = dc_str_to_clist("foo bar test", " ");
assert_eq!((*list).count, 3);
let str = dc_str_from_clist(list, " ");
assert_eq!(str, "foo bar test");
let str: *mut libc::c_char =
dc_str_from_clist(list, b" \x00" as *const u8 as *const libc::c_char);
assert_eq!(
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
"foo bar test"
);
clist_free_content(list);
clist_free(list);
free(str as *mut libc::c_void);
}
}
@@ -1298,6 +1329,19 @@ mod tests {
assert_eq!(foo, format!("$BLOBDIR{}foo", std::path::MAIN_SEPARATOR));
}
#[test]
fn test_strndup() {
unsafe {
let res = strndup(b"helloworld\x00" as *const u8 as *const libc::c_char, 4);
assert_eq!(
to_string_lossy(res),
to_string_lossy(b"hell\x00" as *const u8 as *const libc::c_char)
);
assert_eq!(strlen(res), 4);
free(res as *mut _);
}
}
#[test]
fn test_file_get_safe_basename() {
assert_eq!(get_safe_basename("12312/hello"), "hello");

View File

@@ -116,8 +116,8 @@ impl Client {
let s = stream.try_clone().expect("cloning the stream failed");
let tls_stream = native_tls::TlsConnector::connect(&tls, domain.as_ref(), s)?;
let mut client = imap::Client::new(tls_stream);
client.read_greeting()?;
let client = imap::Client::new(tls_stream);
// TODO: Read greeting
Ok(Client::Secure(client, stream))
}
@@ -125,8 +125,8 @@ impl Client {
pub fn connect_insecure<A: net::ToSocketAddrs>(addr: A) -> imap::error::Result<Self> {
let stream = net::TcpStream::connect(addr)?;
let mut client = imap::Client::new(stream.try_clone().unwrap());
client.read_greeting()?;
let client = imap::Client::new(stream.try_clone().unwrap());
// TODO: Read greeting
Ok(Client::Insecure(client, stream))
}
@@ -598,9 +598,9 @@ impl Imap {
self.config.write().unwrap().watch_folder = Some(watch_folder);
}
pub fn fetch(&self, context: &Context) -> bool {
pub fn fetch(&self, context: &Context) -> libc::c_int {
if !self.is_connected() || !context.sql.is_open() {
return false;
return 0;
}
self.setup_handle_if_needed(context);
@@ -616,9 +616,9 @@ impl Imap {
break;
}
}
true
1
} else {
false
0
}
}
@@ -1025,7 +1025,7 @@ impl Imap {
info!(context, "IMAP-IDLE has data.");
}
Err(err) => match err {
imap::error::Error::Io(_) | imap::error::Error::ConnectionLost => {
imap::error::Error::ConnectionLost => {
info!(context, "IMAP-IDLE wait cancelled, we will reconnect soon.");
self.should_reconnect.store(true, Ordering::Relaxed);
}

View File

@@ -87,7 +87,7 @@ pub fn has_backup(context: &Context, dir_name: impl AsRef<Path>) -> Result<Strin
let name = name.to_string_lossy();
if name.starts_with("delta-chat") && name.ends_with(".bak") {
let sql = Sql::new();
if sql.open(context, &path, true) {
if sql.open(context, &path, 0x1) {
let curr_backup_time =
sql.get_raw_config_int(context, "backup_time")
.unwrap_or_default() as u64;
@@ -413,7 +413,7 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) -> Resul
/* error already logged */
/* re-open copied database file */
ensure!(
context.sql.open(&context, &context.get_dbfile(), false),
context.sql.open(&context, &context.get_dbfile(), 0),
"could not re-open db"
);
@@ -496,7 +496,7 @@ fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
dest_path_filename.display(),
);
let copied = dc_copy_file(context, context.get_dbfile(), &dest_path_filename);
context.sql.open(&context, &context.get_dbfile(), false);
context.sql.open(&context, &context.get_dbfile(), 0);
if !copied {
let s = dest_path_filename.to_string_lossy().to_string();
bail!(
@@ -526,7 +526,7 @@ fn add_files_to_export(context: &Context, dest_path_filename: &PathBuf) -> Resul
// the source to be locked, neigher the destination as it is used only here)
let sql = Sql::new();
ensure!(
sql.open(context, &dest_path_filename, false),
sql.open(context, &dest_path_filename, 0),
"could not open db"
);
if !sql.table_exists("backup_blobs") {

View File

@@ -133,7 +133,7 @@ impl Job {
let connected = context.smtp.lock().unwrap().connect(context, &loginparam);
if !connected {
self.try_again_later(3, None);
self.try_again_later(3i32, None);
return;
}
}
@@ -172,7 +172,7 @@ impl Job {
Err(err) => {
sock.disconnect();
warn!(context, "smtp failed: {}", err);
self.try_again_later(-1, Some(err.to_string()));
self.try_again_later(-1i32, Some(err.to_string()));
}
Ok(()) => {
// smtp success, update db ASAP, then delete smtp file
@@ -207,7 +207,7 @@ impl Job {
}
// this value does not increase the number of tries
fn try_again_later(&mut self, try_again: i32, pending_error: Option<String>) {
fn try_again_later(&mut self, try_again: libc::c_int, pending_error: Option<String>) {
self.try_again = try_again;
self.pending_error = pending_error;
}

View File

@@ -197,7 +197,7 @@ pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) {
let now = time();
let mut msg: Message;
let is_sending_locations_before: bool;
if !(seconds < 0 || chat_id <= DC_CHAT_ID_LAST_SPECIAL) {
if !(seconds < 0 || chat_id <= 9i32 as libc::c_uint) {
is_sending_locations_before = is_sending_locations_to_chat(context, chat_id);
if sql::execute(
context,
@@ -257,9 +257,9 @@ pub fn is_sending_locations_to_chat(context: &Context, chat_id: u32) -> bool {
.unwrap_or_default()
}
pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> bool {
pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> libc::c_int {
if latitude == 0.0 && longitude == 0.0 {
return true;
return 1;
}
let mut continue_streaming = false;
@@ -293,7 +293,7 @@ pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> b
schedule_MAYBE_SEND_LOCATIONS(context, 0);
}
continue_streaming
continue_streaming as libc::c_int
}
pub fn get_range(

View File

@@ -7,7 +7,6 @@ use crate::context::Context;
use crate::dc_tools::*;
const OAUTH2_GMAIL: Oauth2 = Oauth2 {
// see https://developers.google.com/identity/protocols/OAuth2InstalledApp
client_id: "959970109878-4mvtgf6feshskf7695nfln6002mom908.apps.googleusercontent.com",
get_code: "https://accounts.google.com/o/oauth2/auth?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&response_type=code&scope=https%3A%2F%2Fmail.google.com%2F%20email&access_type=offline",
init_token: "https://accounts.google.com/o/oauth2/token?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&code=$CODE&grant_type=authorization_code",
@@ -16,7 +15,6 @@ const OAUTH2_GMAIL: Oauth2 = Oauth2 {
};
const OAUTH2_YANDEX: Oauth2 = Oauth2 {
// see https://tech.yandex.com/oauth/doc/dg/reference/auto-code-client-docpage/
client_id: "c4d0b6735fc8420a816d7e1303469341",
get_code: "https://oauth.yandex.com/authorize?client_id=$CLIENT_ID&response_type=code&scope=mail%3Aimap_full%20mail%3Asmtp&force_confirm=true",
init_token: "https://oauth.yandex.com/token?grant_type=authorization_code&code=$CODE&client_id=$CLIENT_ID&client_secret=58b8c6e94cf44fbe952da8511955dacf",
@@ -91,7 +89,6 @@ pub fn dc_get_oauth2_access_token(
}
}
// generate new token: build & call auth url
let refresh_token = context.sql.get_raw_config(context, "oauth2_refresh_token");
let refresh_token_for = context
.sql
@@ -123,37 +120,14 @@ pub fn dc_get_oauth2_access_token(
false,
)
};
// to allow easier specification of different configurations,
// token_url is in GET-method-format, sth. as https://domain?param1=val1&param2=val2 -
// convert this to POST-format ...
let mut parts = token_url.splitn(2, '?');
let post_url = parts.next().unwrap_or_default();
let post_args = parts.next().unwrap_or_default();
let mut post_param = HashMap::new();
for key_value_pair in post_args.split('&') {
let mut parts = key_value_pair.splitn(2, '=');
let key = parts.next().unwrap_or_default();
let mut value = parts.next().unwrap_or_default();
if value == "$CLIENT_ID" {
value = oauth2.client_id;
} else if value == "$REDIRECT_URI" {
value = &redirect_uri;
} else if value == "$CODE" {
value = code.as_ref();
} else if value == "$REFRESH_TOKEN" && refresh_token.is_some() {
value = refresh_token.as_ref().unwrap();
}
post_param.insert(key, value);
let mut token_url = replace_in_uri(&token_url, "$CLIENT_ID", oauth2.client_id);
token_url = replace_in_uri(&token_url, "$REDIRECT_URI", &redirect_uri);
token_url = replace_in_uri(&token_url, "$CODE", code.as_ref());
if let Some(ref token) = refresh_token {
token_url = replace_in_uri(&token_url, "$REFRESH_TOKEN", token);
}
// ... and POST
let response = reqwest::Client::new()
.post(post_url)
.form(&post_param)
.send();
let response = reqwest::Client::new().post(&token_url).send();
if response.is_err() {
warn!(
context,
@@ -165,14 +139,13 @@ pub fn dc_get_oauth2_access_token(
if !response.status().is_success() {
warn!(
context,
"Unsuccessful response when calling OAuth2 at {}: {:?}",
"Error calling OAuth2 at {}: {:?}",
token_url,
response.status()
);
return None;
}
// generate new token: parse returned json
let parsed: reqwest::Result<Response> = response.json();
if parsed.is_err() {
warn!(
@@ -182,8 +155,6 @@ pub fn dc_get_oauth2_access_token(
return None;
}
println!("response: {:?}", &parsed);
// update refresh_token if given, typically on the first round, but we update it later as well.
let response = parsed.unwrap();
if let Some(ref token) = response.refresh_token {
context
@@ -297,7 +268,7 @@ impl Oauth2 {
return None;
}
let parsed: reqwest::Result<HashMap<String, serde_json::Value>> = response.json();
let parsed: reqwest::Result<HashMap<String, String>> = response.json();
if parsed.is_err() {
warn!(
context,
@@ -306,13 +277,11 @@ impl Oauth2 {
return None;
}
if let Ok(response) = parsed {
// serde_json::Value.as_str() removes the quotes of json-strings
let addr = response.get("email");
if addr.is_none() {
warn!(context, "E-mail missing in userinfo.");
return None;
}
let addr = addr.unwrap().as_str();
addr.map(|addr| addr.to_string())
} else {
warn!(context, "Failed to parse userinfo.");

View File

@@ -71,7 +71,7 @@ impl Smtp {
let tls = dc_build_tls(lp.smtp_certificate_checks).unwrap();
let tls_parameters = ClientTlsParameters::new(domain.to_string(), tls);
let (creds, mechanism) = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) {
let creds = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) {
// oauth2
let addr = &lp.addr;
let send_pw = &lp.send_pw;
@@ -81,24 +81,15 @@ impl Smtp {
}
let user = &lp.send_user;
(
lettre::smtp::authentication::Credentials::new(
user.to_string(),
access_token.unwrap_or_default(),
),
vec![lettre::smtp::authentication::Mechanism::Xoauth2],
lettre::smtp::authentication::Credentials::new(
user.to_string(),
access_token.unwrap_or_default(),
)
} else {
// plain
let user = lp.send_user.clone();
let pw = lp.send_pw.clone();
(
lettre::smtp::authentication::Credentials::new(user, pw),
vec![
lettre::smtp::authentication::Mechanism::Plain,
lettre::smtp::authentication::Mechanism::Login,
],
)
lettre::smtp::authentication::Credentials::new(user, pw)
};
let security = if 0
@@ -114,29 +105,19 @@ impl Smtp {
let client = client
.smtp_utf8(true)
.credentials(creds)
.authentication_mechanism(mechanism)
.connection_reuse(lettre::smtp::ConnectionReuseParameters::ReuseUnlimited);
let mut trans = client.transport();
match trans.connect() {
Ok(()) => {
self.transport = Some(trans);
self.transport_connected = true;
context.call_cb(Event::SmtpConnected(format!(
"SMTP-LOGIN as {} ok",
lp.send_user,
)));
return true;
}
Err(err) => {
warn!(context, "SMTP: failed to connect {:?}", err);
}
}
self.transport = Some(client.transport());
context.call_cb(Event::SmtpConnected(format!(
"SMTP-LOGIN as {} ok",
lp.send_user,
)));
true
}
Err(err) => {
warn!(context, "SMTP: failed to setup connection {:?}", err);
warn!(context, "SMTP: failed to establish connection {:?}", err);
false
}
}
false
}
/// SMTP-Send a prepared mail to recipients.

View File

@@ -11,6 +11,8 @@ use crate::error::{Error, Result};
use crate::param::*;
use crate::peerstate::*;
const DC_OPEN_READONLY: usize = 0x01;
/// A wrapper around the underlying Sqlite3 object.
#[derive(DebugStub)]
pub struct Sql {
@@ -40,8 +42,8 @@ impl Sql {
}
// return true on success, false on failure
pub fn open(&self, context: &Context, dbfile: &std::path::Path, readonly: bool) -> bool {
match open(context, self, dbfile, readonly) {
pub fn open(&self, context: &Context, dbfile: &std::path::Path, flags: libc::c_int) -> bool {
match open(context, self, dbfile, flags) {
Ok(_) => true,
Err(Error::SqlAlreadyOpen) => false,
Err(_) => {
@@ -318,7 +320,7 @@ fn open(
context: &Context,
sql: &Sql,
dbfile: impl AsRef<std::path::Path>,
readonly: bool,
flags: libc::c_int,
) -> Result<()> {
if sql.is_open() {
error!(
@@ -330,7 +332,7 @@ fn open(
}
let mut open_flags = OpenFlags::SQLITE_OPEN_NO_MUTEX;
if readonly {
if 0 != (flags & DC_OPEN_READONLY as i32) {
open_flags.insert(OpenFlags::SQLITE_OPEN_READ_ONLY);
} else {
open_flags.insert(OpenFlags::SQLITE_OPEN_READ_WRITE);
@@ -349,7 +351,7 @@ fn open(
*sql.pool.write().unwrap() = Some(pool);
}
if !readonly {
if 0 == flags & DC_OPEN_READONLY as i32 {
let mut exists_before_update = 0;
let mut dbversion_before_update = 0;
/* Init tables to dbversion=0 */

View File

@@ -16,20 +16,18 @@ if __name__ == "__main__":
free = s.count("free(")
unsafe_fn = s.count("unsafe fn")
chars = s.count("c_char") + s.count("CStr")
libc = s.count("libc")
filestats.append((fn, unsafe, free, unsafe_fn, chars, libc))
filestats.append((fn, unsafe, free, unsafe_fn, chars))
sum_unsafe, sum_free, sum_unsafe_fn, sum_chars, sum_libc = 0, 0, 0, 0, 0
sum_unsafe, sum_free, sum_unsafe_fn, sum_chars = 0, 0, 0, 0
for fn, unsafe, free, unsafe_fn, chars, libc in reversed(sorted(filestats, key=lambda x: sum(x[1:]))):
if unsafe + free + unsafe_fn + chars + libc == 0:
for fn, unsafe, free, unsafe_fn, chars in reversed(sorted(filestats, key=lambda x: sum(x[1:]))):
if unsafe + free + unsafe_fn + chars == 0:
continue
print("{0: <25} unsafe: {1: >3} free: {2: >3} unsafe-fn: {3: >3} chars: {4: >3} libc: {5: >3}".format(str(fn), unsafe, free, unsafe_fn, chars, libc))
print("{0: <25} unsafe: {1: >3} free: {2: >3} unsafe-fn: {3: >3} chars: {4: >3}".format(str(fn), unsafe, free, unsafe_fn, chars))
sum_unsafe += unsafe
sum_free += free
sum_unsafe_fn += unsafe_fn
sum_chars += chars
sum_libc += libc
print()
@@ -37,4 +35,3 @@ if __name__ == "__main__":
print("total free:", sum_free)
print("total unsafe-fn:", sum_unsafe_fn)
print("total c_chars:", sum_chars)
print("total libc:", sum_libc)