Compare commits

..

2 Commits

Author SHA1 Message Date
B. Petersen
ea9252925c re-enabling smtp_config_task - still fine 2020-10-22 00:03:28 +02:00
B. Petersen
7e3029aa9c this works with https://github.com/deltachat/deltachat-android/issues/1676 2020-10-21 23:38:33 +02:00
33 changed files with 294 additions and 726 deletions

View File

@@ -1,53 +1,5 @@
# Changelog
## 1.49.0
- add timestamps to image and video filenames #2068
- forbid quoting messages from another context #2069
- fix: preserve quotes in messages with attachments #2070
## 1.48.0
- `fetch_existing` renamed to `fetch_existing_msgs` and disabled by default
#2035 #2042
- skip fetch existing messages/contacts if config-option `bot` set #2017
- always log why a message is sorted to trash #2045
- display a quote if top posting is detected #2047
- add ephemeral task cancellation to `dc_stop_io()`;
before, there was no way to quickly terminate pending ephemeral tasks #2051
- when saved-messages chat is deleted,
a device-message about recreation is added #2050
- use `max_smtp_rcpt_to` from provider-db,
sending messages to many recipients in configurable chunks #2056
- fix handling of empty autoconfigure files #2027
- fix adding saved messages to wrong chats on multi-device #2034 #2039
- fix hang on android4.4 and other systems
by adding a workaround to executer-blocking-handling bug #2040
- fix secret key export/import roundtrip #2048
- fix mistakenly unarchived chats #2057
- fix outdated-reminder test that fails only 7 days a year,
including halloween :) #2059
- improve python bindings #2021 #2036 #2038
- update provider-database #2037
## 1.47.0
- breaking change: `dc_update_device_chats()` removed;
@@ -75,7 +27,6 @@
- configure now collects recent contacts and fetches last messages
unless disabled by `fetch_existing` config-option #1913 #2003
EDIT: `fetch_existing` renamed to `fetch_existing_msgs` in 1.48.0 #2042
- emit `DC_EVENT_CHAT_MODIFIED` on contact rename
and set contact-id on `DC_EVENT_CONTACTS_CHANGED` #1935 #1936 #1937
@@ -89,8 +40,6 @@
- mark all failed messages as failed when receiving an NDN #1993
- check some easy cases for bad system clock and outdated app #1901
- fix import temporary directory usage #1929
- fix forcing encryption for reset peers #1998

4
Cargo.lock generated
View File

@@ -1003,7 +1003,7 @@ dependencies = [
[[package]]
name = "deltachat"
version = "1.49.0"
version = "1.47.0"
dependencies = [
"ansi_term 0.12.1",
"anyhow",
@@ -1079,7 +1079,7 @@ dependencies = [
[[package]]
name = "deltachat_ffi"
version = "1.49.0"
version = "1.47.0"
dependencies = [
"anyhow",
"async-std",

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat"
version = "1.49.0"
version = "1.47.0"
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
edition = "2018"
license = "MPL-2.0"

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat_ffi"
version = "1.49.0"
version = "1.47.0"
description = "Deltachat FFI"
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
edition = "2018"

View File

@@ -36,18 +36,16 @@ typedef struct _dc_accounts_event_emitter dc_accounts_event_emitter_t;
*
* First of all, you have to **create a context object**
* bound to a database.
* The database is a normal SQLite file with a "blob directory" beside it.
* This will create "example.db" database and "example.db-blobs"
* directory if they don't exist already:
* The database is a normal sqlite-file and is created as needed:
*
* ~~~
* dc_context_t* context = dc_context_new(NULL, "example.db", NULL);
* ~~~
*
* After that, make sure you can **receive events from the context**.
* After that, make sure, you can **receive events from the context**.
* For that purpose, create an event emitter you can ask for events.
* If there is no event, the emitter will wait until there is one,
* so, in many situations, you will do this in a thread:
* If there are no event, the emitter will wait until there is one,
* so, in many situations you will do this in a thread:
*
* ~~~
* void* event_handler(void* context)
@@ -68,7 +66,7 @@ typedef struct _dc_accounts_event_emitter dc_accounts_event_emitter_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.
* All deltachat-core-functions, unless stated otherwise, are thread-safe.
*
* Now you can **configure the context:**
*
@@ -79,13 +77,13 @@ typedef struct _dc_accounts_event_emitter dc_accounts_event_emitter_t;
* dc_configure(context);
* ~~~
*
* dc_configure() returns immediately.
* The configuration itself runs in the background and may take a while.
* dc_configure() returns immediately,
* the configuration itself runs in background and may take a while.
* Once done, the #DC_EVENT_CONFIGURE_PROGRESS reports success
* to the event_handler() you've defined above.
*
* The configuration result is saved in the database.
* On subsequent starts it is not needed to call dc_configure()
* The configuration result is saved in the database,
* on subsequent starts it is not needed to call dc_configure()
* (you can check this using dc_is_configured()).
*
* On a successfully configured context,
@@ -107,12 +105,12 @@ typedef struct _dc_accounts_event_emitter dc_accounts_event_emitter_t;
*
* dc_send_text_msg() returns immediately;
* the sending itself is done in the background.
* If you check the testing address (bob),
* you should receive a normal email.
* Answer this email in any email program with "Got it!",
* If you check the testing address (bob)
* and you should have received a normal email.
* Answer this email in any email program with "Got it!"
* and the IO you started above will **receive the message**.
*
* You can then **list all messages** of a chat as follows:
* You can then **list all messages** of a chat as follow:
*
* ~~~
* dc_array_t* msglist = dc_get_chat_msgs(context, chat_id, 0, 0);
@@ -183,12 +181,18 @@ typedef struct _dc_accounts_event_emitter dc_accounts_event_emitter_t;
* opened, connected and mails are fetched.
*
* @memberof dc_context_t
* @param os_name is only for decorative use.
* @param os_name is only for decorative use
* and is shown eg. in the `X-Mailer:` header
* in the form "Delta Chat Core <version>/<os_name>".
* You can give the name of the app, the operating system,
* the used environment and/or the version here.
* It is okay to give NULL, in this case `X-Mailer:` header
* is set to "Delta Chat Core <version>".
* @param dbfile The file to use to store the database,
* something like `~/file` won't work, use absolute paths.
* @param blobdir Deprecated, pass NULL or an empty string here.
* @param blobdir A directory to store the blobs in; a trailing slash is not needed.
* If you pass NULL or the empty string, deltachat-core creates a directory
* beside _dbfile_ with the same name and the suffix `-blobs`.
* @return A context object with some public members.
* The object must be passed to the other context functions
* and must be freed using dc_context_unref() after usage.
@@ -201,9 +205,14 @@ dc_context_t* dc_context_new (const char* os_name, const char* d
/**
* Free a context object.
* If app runs can only be terminated by a forced kill, this may be superfluous.
* Before the context object is freed, connections to SMTP, IMAP and database
* are closed. You can also do this explicitly by calling dc_close() on your own
* before calling dc_context_unref().
*
* You have to call this function
* also for accounts returned by dc_accounts_get_account() or dc_accounts_get_selected_account().
* also for accounts returned by dc_accounts_get_account() or dc_accounts_get_selected_account(),
* however, in this case, the context is not shut down but just a reference counter is decreased
*
* @memberof dc_context_t
* @param context The context object as created by dc_context_new(),
@@ -221,7 +230,7 @@ void dc_context_unref (dc_context_t* context);
* If the context was created by dc_context_new(), a random ID is assigned.
*
* @memberof dc_context_t
* @param context The context object as created e.g. by dc_accounts_get_account() or dc_context_new().
* @param context The context object as created eg. by dc_accounts_get_account() or dc_context_new().
* @return The context-id.
*/
uint32_t dc_get_id (dc_context_t* context);
@@ -229,7 +238,7 @@ uint32_t dc_get_id (dc_context_t* context);
/**
* Create the event emitter that is used to receive events.
* The library will emit various @ref DC_EVENT events, such as "new message", "message read" etc.
* The library will emit various @ref DC_EVENT events as "new message", "message read" etc.
* To get these events, you have to create an event emitter using this function
* and call dc_get_next_event() on the emitter.
*
@@ -240,7 +249,7 @@ uint32_t dc_get_id (dc_context_t* context);
*
* Note: Use only one event emitter per context.
* Having more than one event emitter running at the same time on the same context
* will result in events being randomly delivered to one of the emitters.
* will result in events randomly delivered to the one or to the other.
*/
dc_event_emitter_t* dc_get_event_emitter(dc_context_t* context);
@@ -341,7 +350,7 @@ char* dc_get_blobdir (const dc_context_t* context);
* The type `jitsi:` may be handled by external apps.
* If no type is prefixed, the videochat is handled completely in a browser.
* - `bot` = Set to "1" if this is a bot. E.g. prevents adding the "Device messages" and "Saved messages" chats.
* - `fetch_existing_msgs` = 1=fetch most recent existing messages on configure (default),
* - `fetch_existing` = 1=fetch most recent existing messages on configure (default),
* 0=do not fetch existing messages on configure.
* In both cases, existing recipients are added to the contact database.
*
@@ -465,7 +474,7 @@ char* dc_get_oauth2_url (dc_context_t* context, const char*
/**
* Configure a context.
* During configuration IO must not be started, if needed stop IO using dc_stop_io() first.
* While configuration IO must not be started, if needed stop IO using dc_stop_io() first.
* If the context is already configured,
* this function will try to change the configuration.
*
@@ -553,12 +562,9 @@ void dc_start_io (dc_context_t* context);
int dc_is_io_running(const dc_context_t* context);
/**
* Stop job, IMAP, SMTP and other tasks and return when they
* are finished.
* Stop job and IMAP/SMTP tasks and return when they are finished.
* If IO is not running, nothing happens.
* To check the current IO state, use dc_is_io_running().
* Even if IO is not running, there may be pending tasks,
* so this function should always be called before releasing
* context to ensure clean termination of event loop.
*
* If the context was created by the dc_accounts_t account manager,
* use dc_accounts_stop_io() instead of this function.
@@ -571,7 +577,7 @@ void dc_stop_io(dc_context_t* context);
/**
* This function should be called when there is a hint
* that the network is available again,
* e.g. as a response to system event reporting network availability.
* eg. as a response to system event reporting network availability.
* The library will try to send pending messages out immediately.
*
* Moreover, to have a reliable state
@@ -632,7 +638,7 @@ int dc_preconfigure_keypair (dc_context_t* context, const cha
* 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 e.g. dc_chatlist_get_summary().
* To get information about each entry, use eg. dc_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 dc_chatlist_get_chat_id():
@@ -664,7 +670,7 @@ int dc_preconfigure_keypair (dc_context_t* context, const cha
* typically used on forwarding, may be combined with DC_GCL_NO_SPECIALS
* to also hide the archive link.
* - if the flag DC_GCL_NO_SPECIALS is set, deaddrop and archive link are not added
* to the list (may be used e.g. for selecting chats on forwarding, the flag is
* 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.
@@ -716,7 +722,7 @@ uint32_t dc_create_chat_by_msg_id (dc_context_t* context, uint32_t ms
* see dc_create_group_chat().
*
* If a chat already exists, this ID is returned, otherwise a new chat is created;
* this new chat may already contain messages, e.g. from the deaddrop, to get the
* this new chat may already contain messages, eg. from the deaddrop, to get the
* chat messages, use dc_get_chat_msgs().
*
* @memberof dc_context_t
@@ -795,7 +801,7 @@ uint32_t dc_prepare_msg (dc_context_t* context, uint32_t ch
*
* Sends the event #DC_EVENT_MSGS_CHANGED on succcess.
* However, this does not imply, the message really reached the recipient -
* sending may be delayed e.g. due to network problems. However, from your
* sending may be delayed eg. due to network problems. However, from your
* view, you're done with the message. Sooner or later it will find its way.
*
* Example:
@@ -852,7 +858,7 @@ uint32_t dc_send_msg_sync (dc_context_t* context, uint32
*
* Sends the event #DC_EVENT_MSGS_CHANGED on succcess.
* However, this does not imply, the message really reached the recipient -
* sending may be delayed e.g. due to network problems. However, from your
* sending may be delayed eg. due to network problems. However, from your
* view, you're done with the message. Sooner or later it will find its way.
*
* See also dc_send_msg().
@@ -883,7 +889,7 @@ uint32_t dc_send_text_msg (dc_context_t* context, uint32_t ch
* and a url that can be opened in a supported browser to join the videochat
*
* - delta-clients can get all information needed from
* the message object, using e.g.
* the message object, using eg.
* dc_msg_get_videochat_url() and check dc_msg_get_viewtype() for #DC_MSG_VIDEOCHAT_INVITATION
*
* dc_send_videochat_invitation() is blocking and may take a while,
@@ -892,9 +898,9 @@ uint32_t dc_send_text_msg (dc_context_t* context, uint32_t ch
* for this purpose, the function returns the message-id directly.
*
* As for other messages sent, this function
* sends the event #DC_EVENT_MSGS_CHANGED on success, the message has a delivery state, and so on.
* sends the event #DC_EVENT_MSGS_CHANGED on succcess, the message has a delivery state, and so on.
* The recipient will get noticed by the call as usual by #DC_EVENT_INCOMING_MSG or #DC_EVENT_MSGS_CHANGED,
* However, UIs might some things differently, e.g. play a different sound.
* However, UIs might some things differently, eg. play a different sound.
*
* @memberof dc_context_t
* @param context The context object
@@ -915,7 +921,7 @@ uint32_t dc_send_videochat_invitation (dc_context_t* context, uint32_t chat_id);
* allowing the user to continue editing and sending.
*
* Drafts are considered when sorting messages
* and are also returned e.g. by dc_chatlist_get_summary().
* and are also returned eg. by dc_chatlist_get_summary().
*
* Each chat can have its own draft but only one draft per chat is possible.
*
@@ -1163,7 +1169,7 @@ uint32_t dc_get_next_media (dc_context_t* context, uint32_t ms
* @param context The context object as returned from dc_context_new().
* @param chat_id The ID of the chat to change the protection for.
* @param protect 1=protect chat, 0=unprotect chat
* @return 1=success, 0=error, e.g. some members may be unverified
* @return 1=success, 0=error, eg. some members may be unverified
*/
int dc_set_chat_protection (dc_context_t* context, uint32_t chat_id, int protect);
@@ -1418,7 +1424,7 @@ int dc_set_chat_profile_image (dc_context_t* context, uint32_t ch
/**
* Set mute duration of a chat.
*
* The UI can then call dc_chat_is_muted() when receiving a new message to decide whether it should trigger an notification.
* The ui can then call dc_chat_is_muted() when receiving a new message to decide whether it should trigger an notification.
*
* Sends out #DC_EVENT_CHAT_MODIFIED.
*
@@ -1434,7 +1440,7 @@ int dc_set_chat_mute_duration (dc_context_t* context, ui
/**
* Get an informational text for a single message. The text is multiline and may
* contain e.g. the raw text of the message.
* contain eg. the raw text of the message.
*
* The max. text returned is typically longer (about 100000 characters) than the
* max. text returned by dc_msg_get_text() (about 30000 characters).
@@ -1458,7 +1464,7 @@ char* dc_get_msg_info (dc_context_t* context, uint32_t ms
* @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.
* Returns NULL if there are no headers saved for the given message,
* e.g. because of save_mime_headers is not set
* eg. because of save_mime_headers is not set
* or the message is not incoming.
*/
char* dc_get_mime_headers (dc_context_t* context, uint32_t msg_id);
@@ -1506,7 +1512,7 @@ void dc_marknoticed_contact (dc_context_t* context, uint32_t co
/**
* Mark a message as _seen_, updates the IMAP state and
* sends MDNs. If the message is not in a real chat (e.g. a contact request), the
* sends MDNs. If the message is not in a real chat (eg. a contact request), the
* message is only marked as NOTICED and no IMAP/MDNs is done. See also
* dc_marknoticed_chat() and dc_marknoticed_contact()
*
@@ -1680,7 +1686,7 @@ void dc_block_contact (dc_context_t* context, uint32_t co
/**
* Get encryption info for a contact.
* Get a multi-line encryption info, containing your fingerprint and the
* fingerprint of the contact, used e.g. to compare the fingerprints for a simple out-of-band verification.
* fingerprint of the contact, used eg. to compare the fingerprints for a simple out-of-band verification.
*
* @memberof dc_context_t
* @param context The context object.
@@ -1705,7 +1711,7 @@ int dc_delete_contact (dc_context_t* context, uint32_t co
/**
* Get a single contact object. For a list, see e.g. dc_get_contacts().
* Get a single contact object. For a list, see eg. dc_get_contacts().
*
* For contact DC_CONTACT_ID_SELF (1), the function returns sth.
* like "Me" in the selected language and the email address
@@ -1774,7 +1780,7 @@ void dc_imex (dc_context_t* context, int what, c
/**
* Check if there is a backup file.
* May only be used on fresh installations (e.g. dc_is_configured() returns 0).
* May only be used on fresh installations (eg. dc_is_configured() returns 0).
*
* Example:
*
@@ -1867,7 +1873,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.
* On errors, e.g. if the message could not be sent, NULL is returned.
* On errors, eg. if the message could not be sent, NULL is returned.
*/
char* dc_initiate_key_transfer (dc_context_t* context);
@@ -1890,7 +1896,7 @@ char* dc_initiate_key_transfer (dc_context_t* context);
* There is no need to format the string correctly, the function will remove all spaces and other characters and
* insert the `-` characters at the correct places.
* @return 1=key successfully decrypted and imported; both devices will use the same key now;
* 0=key transfer failed e.g. due to a bad setup code.
* 0=key transfer failed eg. due to a bad setup code.
*/
int dc_continue_key_transfer (dc_context_t* context, uint32_t msg_id, const char* setup_code);
@@ -1961,7 +1967,7 @@ dc_lot_t* dc_check_qr (dc_context_t* context, const char*
/**
* Get QR code text that will offer an Setup-Contact or Verified-Group invitation.
* The QR code is compatible to the OPENPGP4FPR format
* so that a basic fingerprint comparison also works e.g. with OpenKeychain.
* so that a basic fingerprint comparison also works eg. with OpenKeychain.
*
* The scanning device will pass the scanned content to dc_check_qr() then;
* if dc_check_qr() returns DC_QR_ASK_VERIFYCONTACT or DC_QR_ASK_VERIFYGROUP
@@ -2242,7 +2248,7 @@ void dc_accounts_unref (dc_accounts_t* accounts);
* Internally, dc_context_new() is called using a unique database-name
* in the directory specified at dc_accounts_new().
*
* If the function succeeds,
* If the function succceeds,
* dc_accounts_get_all() will return one more account
* and you can access the newly created account using dc_accounts_get_account().
* Moreover, the newly created account will be the selected one.
@@ -2271,7 +2277,7 @@ uint32_t dc_accounts_import_account (dc_accounts_t* accounts, const
/**
* Migrate independent accounts into accounts managed by the account manager.
* Migrate independent accounts into accounts managed by the account mangager.
* This will _move_ the database-file and all blob-files to the directory managed
* by the account-manager
* (to save disk-space on small devices, the files are not _copied_
@@ -2295,10 +2301,9 @@ uint32_t dc_accounts_migrate_account (dc_accounts_t* accounts, const
*
* @memberof dc_accounts_t
* @param accounts Account manager as created by dc_accounts_new().
* @param account_id The account-id as returned e.g. by dc_accounts_add_account().
* @return 1=success, 0=error
*/
int dc_accounts_remove_account (dc_accounts_t* accounts, uint32_t account_id);
int dc_accounts_remove_account (dc_accounts_t* accounts, uint32_t);
/**
@@ -2317,7 +2322,7 @@ dc_array_t* dc_accounts_get_all (dc_accounts_t* accounts);
*
* @memberof dc_accounts_t
* @param accounts Account manager as created by dc_accounts_new().
* @param account_id The account-id as returned e.g. by dc_accounts_get_all() or dc_accounts_add_account().
* @param account_id The account-id as returned eg. by dc_accounts_get_all() or dc_accounts_add_account().
* @return The account-context, this can be used most similar as a normal,
* unmanaged account-context as created by dc_context_new().
* Once you do no longer need the context-object, you have to call dc_context_unref() on it,
@@ -2348,7 +2353,7 @@ dc_context_t* dc_accounts_get_selected_account (dc_accounts_t* accounts);
*
* @memberof dc_accounts_t
* @param accounts Account manager as created by dc_accounts_new().
* @param account_id The account-id as returned e.g. by dc_accounts_get_all() or dc_accounts_add_account().
* @param account_id The account-id as returned eg. by dc_accounts_get_all() or dc_accounts_add_account().
* @return 1=success, 0=error
*/
int dc_accounts_select_account (dc_accounts_t* accounts, uint32_t account_id);
@@ -2368,6 +2373,7 @@ void dc_accounts_start_io (dc_accounts_t* accounts);
/**
* Stop job and IMAP/SMTP tasks for all accounts and return when they are finished.
* If IO is not running, nothing happens.
* This is similar to dc_stop_io(), which, however,
* must not be called for accounts handled by the account manager.
*
@@ -2426,7 +2432,7 @@ dc_accounts_event_emitter_t* dc_accounts_get_event_emitter (dc_accounts_t* accou
*
* @memberof dc_array_t
* @param array The array object to free,
* created e.g. by dc_get_chatlist(), dc_get_contacts() and so on.
* created eg. by dc_get_chatlist(), dc_get_contacts() and so on.
* If NULL is given, nothing is done.
*/
void dc_array_unref (dc_array_t* array);
@@ -2624,7 +2630,7 @@ int dc_array_search_id (const dc_array_t* array, uint32_t
* Free a chatlist object.
*
* @memberof dc_chatlist_t
* @param chatlist The chatlist object to free, created e.g. by dc_get_chatlist(), dc_search_msgs().
* @param chatlist The chatlist object to free, created eg. by dc_get_chatlist(), dc_search_msgs().
* If NULL is given, nothing is done.
*/
void dc_chatlist_unref (dc_chatlist_t* chatlist);
@@ -2634,7 +2640,7 @@ void dc_chatlist_unref (dc_chatlist_t* chatlist);
* Find out the number of chats in a chatlist.
*
* @memberof dc_chatlist_t
* @param chatlist The chatlist object as created e.g. by dc_get_chatlist().
* @param chatlist The chatlist object as created eg. by dc_get_chatlist().
* @return Returns the number of items in a dc_chatlist_t object. 0 on errors or if the list is empty.
*/
size_t dc_chatlist_get_cnt (const dc_chatlist_t* chatlist);
@@ -2646,7 +2652,7 @@ size_t dc_chatlist_get_cnt (const dc_chatlist_t* chatlist);
* To get the message object from the message ID, use dc_get_chat().
*
* @memberof dc_chatlist_t
* @param chatlist The chatlist object as created e.g. by dc_get_chatlist().
* @param chatlist The chatlist object as created eg. by dc_get_chatlist().
* @param index The index to get the chat ID for.
* @return Returns the chat_id of the item at the given index. Index must be between
* 0 and dc_chatlist_get_cnt()-1.
@@ -2660,10 +2666,10 @@ uint32_t dc_chatlist_get_chat_id (const dc_chatlist_t* chatlist, siz
* To get the message object from the message ID, use dc_get_msg().
*
* @memberof dc_chatlist_t
* @param chatlist The chatlist object as created e.g. by dc_get_chatlist().
* @param chatlist The chatlist object as created eg. by dc_get_chatlist().
* @param index The index to get the chat ID for.
* @return Returns the message_id of the item at the given index. Index must be between
* 0 and dc_chatlist_get_cnt()-1. If there is no message at the given index (e.g. the chat may be empty), 0 is returned.
* 0 and dc_chatlist_get_cnt()-1. If there is no message at the given index (eg. the chat may be empty), 0 is returned.
*/
uint32_t dc_chatlist_get_msg_id (const dc_chatlist_t* chatlist, size_t index);
@@ -2681,14 +2687,14 @@ uint32_t dc_chatlist_get_msg_id (const dc_chatlist_t* chatlist, siz
* 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 (e.g. for the archive link)
* "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.
*
* @memberof dc_chatlist_t
* @param chatlist The chatlist to query as returned e.g. from dc_get_chatlist().
* @param chatlist The chatlist to query as returned eg. from dc_get_chatlist().
* @param index The index to query in the chatlist.
* @param chat To speed up things, pass an already available chat object here.
* If the chat object is not yet available, it is faster to pass NULL.
@@ -2705,7 +2711,7 @@ dc_lot_t* dc_chatlist_get_summary (const dc_chatlist_t* chatlist, siz
* as arguments. The chatlist object itself is not needed directly.
*
* This maybe useful if you convert the complete object into a different represenation
* as done e.g. in the node-bindings.
* as done eg. in the node-bindings.
* If you have access to the chatlist object in some way, using this function is not recommended,
* use dc_chatlist_get_summary() in this case instead.
*
@@ -2730,9 +2736,9 @@ dc_context_t* dc_chatlist_get_context (dc_chatlist_t* chatlist);
/**
* Get info summary for a chat, in JSON format.
* Get info summary for a chat, in json format.
*
* The returned JSON string has the following key/values:
* The returned json string has the following key/values:
*
* id: chat id
* name: chat/group name
@@ -2743,7 +2749,7 @@ dc_context_t* dc_chatlist_get_context (dc_chatlist_t* chatlist);
* last-message-date:
* avatar-path: path-to-blobfile
* is_verified: yes/no
* @return a UTF8-encoded JSON string containing all requested info. Must be freed using dc_str_unref(). NULL is never returned.
* @return a utf8-encoded json string containing all requested info. Must be freed using dc_str_unref(). NULL is never returned.
*/
char* dc_chat_get_info_json (dc_context_t* context, size_t chat_id);
@@ -2751,7 +2757,7 @@ char* dc_chat_get_info_json (dc_context_t* context, size_t chat
* @class dc_chat_t
*
* An object representing a single chat in memory.
* Chat objects are created using e.g. dc_get_chat()
* Chat objects are created using eg. dc_get_chat()
* and are not updated on database changes;
* if you want an update, you have to recreate the object.
*/
@@ -2773,7 +2779,7 @@ char* dc_chat_get_info_json (dc_context_t* context, size_t chat
* Free a chat object.
*
* @memberof dc_chat_t
* @param chat Chat object are returned e.g. by dc_get_chat().
* @param chat Chat object are returned eg. by dc_get_chat().
* If NULL is given, nothing is done.
*/
void dc_chat_unref (dc_chat_t* chat);
@@ -2816,7 +2822,7 @@ int dc_chat_get_type (const dc_chat_t* chat);
/**
* Get name of a chat. For one-to-one chats, this is the name of the contact.
* For group chats, this is the name given e.g. to dc_create_group_chat() or
* For group chats, this is the name given eg. to dc_create_group_chat() or
* received by a group-creation message.
*
* To change the name, use dc_set_chat_name()
@@ -2909,7 +2915,7 @@ int dc_chat_is_self_talk (const dc_chat_t* chat);
* Device-talks contain update information
* and some hints that are added during the program runs, multi-device etc.
*
* From the UI view, device-talks are not very special,
* From the ui view, device-talks are not very special,
* the user can delete and forward messages, archive the chat, set notifications etc.
*
* Messages can be added to the device-talk using dc_add_device_msg()
@@ -2923,10 +2929,10 @@ int dc_chat_is_device_talk (const dc_chat_t* chat);
/**
* Check if messages can be sent to a give chat.
* This is not true e.g. for the deaddrop or for the device-talk, cmp. dc_chat_is_device_talk().
* This is not true eg. for the deaddrop or for the device-talk, cmp. dc_chat_is_device_talk().
*
* Calling dc_send_msg() for these chats will fail
* and the UI may decide to hide input controls therefore.
* and the ui may decide to hide input controls therefore.
*
* @memberof dc_chat_t
* @param chat The chat object.
@@ -3012,8 +3018,8 @@ int64_t dc_chat_get_remaining_mute_duration (const dc_chat_t* chat);
/**
* Create new message object. Message objects are needed e.g. for sending messages using
* dc_send_msg(). Moreover, they are returned e.g. from dc_get_msg(),
* Create new message object. Message objects are needed eg. for sending messages using
* dc_send_msg(). Moreover, they are returned eg. from dc_get_msg(),
* set up with the current state of a message. The message object is not updated;
* to achieve this, you have to recreate it.
*
@@ -3027,7 +3033,7 @@ dc_msg_t* dc_msg_new (dc_context_t* context, int viewty
/**
* Free a message object. Message objects are created e.g. by dc_get_msg().
* Free a message object. Message objects are created eg. by dc_get_msg().
*
* @memberof dc_msg_t
* @param msg The message object to free.
@@ -3109,12 +3115,12 @@ int dc_msg_get_viewtype (const dc_msg_t* msg);
* If a sent message changes to this state, you'll receive the event #DC_EVENT_MSG_READ.
* Also messages already read by some recipients
* may get into the state DC_STATE_OUT_FAILED at a later point,
* e.g. when in a group, delivery fails for some recipients.
* eg. when in a group, delivery fails for some recipients.
*
* If you just want to check if a message is sent or not, please use dc_msg_is_sent() which regards all states accordingly.
*
* The state of just created message objects is DC_STATE_UNDEFINED (0).
* The state is always set by the core-library, users of the library cannot set the state directly, but it is changed implicitly e.g.
* The state is always set by the core-library, users of the library cannot set the state directly, but it is changed implicitly eg.
* when calling dc_marknoticed_chat() or dc_markseen_msgs().
*
* @memberof dc_msg_t
@@ -3128,7 +3134,7 @@ int dc_msg_get_state (const dc_msg_t* msg);
* Get message sending time.
* The sending time is returned as a unix timestamp in seconds.
*
* Note that the message lists returned e.g. by dc_get_chat_msgs()
* Note that the message lists returned eg. by dc_get_chat_msgs()
* are not sorted by the _sending_ time but by the _receiving_ time.
* This ensures newly received messages always pop up at the end of the list,
* however, for delayed messages, the correct sending time will be displayed.
@@ -3160,7 +3166,7 @@ int64_t dc_msg_get_received_timestamp (const dc_msg_t* msg);
/**
* Get message time used for sorting.
* This function returns the timestamp that is used for sorting the message
* into lists as returned e.g. by dc_get_chat_msgs().
* into lists as returned eg. by dc_get_chat_msgs().
* This may be the reveived time, the sending time or another time.
*
* To get the receiving time, use dc_msg_get_received_timestamp().
@@ -3183,7 +3189,7 @@ int64_t dc_msg_get_sort_timestamp (const dc_msg_t* msg);
* it does not make sense to show more text in the message list and typical controls
* will have problems with showing much more text.
* This max. length is to avoid passing _lots_ of data to the frontend which may
* result e.g. from decoding errors (assume some bytes missing in a mime structure, forcing
* result eg. from decoding errors (assume some bytes missing in a mime structure, forcing
* an attachment to be plain text).
*
* To get information about the message and more/raw text, use dc_get_msg_info().
@@ -3241,7 +3247,7 @@ char* dc_msg_get_filemime (const dc_msg_t* msg);
* Get the size of the file. Returns the size of the file associated with a
* message, if applicable.
*
* Typically, this is used to show the size of document messages, e.g. a PDF.
* Typically, this is used to show the size of document messages, eg. a PDF.
*
* @memberof dc_msg_t
* @param msg The message object.
@@ -3440,7 +3446,7 @@ int dc_msg_is_forwarded (const dc_msg_t* msg);
* Check if the message is an informational message, created by the
* device or by another users. Such messages are not "typed" by the user but
* created due to other actions,
* e.g. dc_set_chat_name(), dc_set_chat_profile_image(), dc_set_chat_protection()
* eg. dc_set_chat_name(), dc_set_chat_profile_image(), dc_set_chat_protection()
* or dc_add_contact_to_chat().
*
* These messages are typically shown in the center of the chat view,
@@ -3459,7 +3465,7 @@ int dc_msg_is_info (const dc_msg_t* msg);
/**
* Get the type of an informational message.
* If dc_msg_is_info() returns 1, this function returns the type of the informational message.
* UIs can display e.g. an icon based upon the type.
* UIs can display eg. an icon based upon the type.
*
* Currently, the following types are defined:
* - DC_INFO_PROTECTION_ENABLED (11) - Info-message for "Chat is now protected"
@@ -3500,7 +3506,7 @@ int dc_msg_is_increation (const dc_msg_t* msg);
/**
* Check if the message is an Autocrypt Setup Message.
*
* Setup messages should be shown in an unique way e.g. using a different text color.
* Setup messages should be shown in an unique way eg. using a different text color.
* On a click or another action, the user should be prompted for the setup code
* which is forwarded to dc_continue_key_transfer() then.
*
@@ -3742,7 +3748,7 @@ char* dc_msg_get_quoted_text (const dc_msg_t* msg);
* To check if a message has a quote, use dc_msg_get_quoted_text().
*
* To display the quote in the chat, use dc_msg_get_quoted_text() as a primary source,
* however, one might add information from the message object (e.g. an image).
* however, one might add information from the message object (eg. an image).
*
* It is not guaranteed that the message belong to the same chat.
*
@@ -3768,7 +3774,7 @@ dc_msg_t* dc_msg_get_quoted_msg (const dc_msg_t* msg);
* authorized-name and given-name.
* By default, these names are equal,
* but functions working with contact names
* (e.g. dc_contact_get_name(), dc_contact_get_display_name(),
* (eg. dc_contact_get_name(), dc_contact_get_display_name(),
* dc_contact_get_name_n_addr(), dc_contact_get_first_name(),
* dc_create_contact() or dc_add_address_book())
* only affect the given-name.
@@ -3785,7 +3791,7 @@ dc_msg_t* dc_msg_get_quoted_msg (const dc_msg_t* msg);
* Free a contact object.
*
* @memberof dc_contact_t
* @param contact The contact object as created e.g. by dc_get_contact().
* @param contact The contact object as created eg. by dc_get_contact().
* If NULL is given, nothing is done.
*/
void dc_contact_unref (dc_contact_t* contact);
@@ -3849,7 +3855,7 @@ char* dc_contact_get_display_name (const dc_contact_t* contact);
* "email@domain.com" if the name is unset.
*
* The summary is typically used when asking the user something about the contact.
* The attached email address makes the question unique, e.g. "Chat with Alan Miller (am@uniquedomain.com)?"
* The attached email address makes the question unique, eg. "Chat with Alan Miller (am@uniquedomain.com)?"
*
* @memberof dc_contact_t
* @param contact The contact object.
@@ -3965,9 +3971,9 @@ char* dc_provider_get_overview_page (const dc_provider_t* prov
/**
* Get hints to be shown to the user on the login screen.
* Depending on the @ref DC_PROVIDER_STATUS returned by dc_provider_get_status(),
* the UI may want to highlight the hint.
* the ui may want to highlight the hint.
*
* Moreover, the UI should display a "More information" link
* Moreover, the ui should display a "More information" link
* that forwards to the url returned by dc_provider_get_overview_page().
*
* @memberof dc_provider_t
@@ -4007,7 +4013,7 @@ void dc_provider_unref (dc_provider_t* provider);
* An object containing a set of values.
* The meaning of the values is defined by the function returning the object.
* Lot objects are created
* e.g. by dc_chatlist_get_summary() or dc_msg_get_summary().
* eg. by dc_chatlist_get_summary() or dc_msg_get_summary().
*
* NB: _Lot_ is used in the meaning _heap_ here.
*/
@@ -4021,7 +4027,7 @@ void dc_provider_unref (dc_provider_t* provider);
/**
* Frees an object containing a set of parameters.
* If the set object contains strings, the strings are also freed with this function.
* Set objects are created e.g. by dc_chatlist_get_summary() or dc_msg_get_summary().
* Set objects are created eg. by dc_chatlist_get_summary() or dc_msg_get_summary().
*
* @memberof dc_lot_t
* @param lot The object to free.
@@ -4055,7 +4061,7 @@ char* dc_lot_get_text2 (const dc_lot_t* lot);
/**
* Get the meaning of the first string. Posssible meanings of the string are defined by the creator of the object and may be returned e.g.
* Get the meaning of the first string. Posssible meanings of the string are defined by the creator of the object and may be returned eg.
* as DC_TEXT1_DRAFT, DC_TEXT1_USERNAME or DC_TEXT1_SELF.
*
* @memberof dc_lot_t
@@ -4105,9 +4111,9 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*
* From the view of the library,
* all types are primary types of the same level,
* e.g. the library does not regard #DC_MSG_GIF as a subtype for #DC_MSG_IMAGE
* eg. the library does not regard #DC_MSG_GIF as a subtype for #DC_MSG_IMAGE
* and it's up to the UI to decide whether a GIF is shown
* e.g. in an IMAGE or in a VIDEO container.
* eg. in an IMAGE or in a VIDEO container.
*
* If you want to define the type of a dc_msg_t object for sending,
* use dc_msg_new().
@@ -4146,7 +4152,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
/**
* Message containing a sticker, similar to image.
* If possible, the UI should display the image without borders in a transparent way.
* If possible, the ui should display the image without borders in a transparent way.
* A click on a sticker will offer to install the sticker set in some future.
*/
#define DC_MSG_STICKER 23
@@ -4181,7 +4187,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
/**
* Message containing any file, e.g. a PDF.
* Message containing any file, eg. a PDF.
* The file is set via dc_msg_set_file()
* and retrieved via dc_msg_get_file().
*/
@@ -4193,7 +4199,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
* The message was created via dc_send_videochat_invitation() on this or a remote device.
*
* Typically, such messages are rendered differently by the UIs,
* e.g. contain a button to join the videochat.
* eg. contain a button to join the videochat.
* The url for joining can be retrieved using dc_msg_get_videochat_url().
*/
#define DC_MSG_VIDEOCHAT_INVITATION 70
@@ -4327,7 +4333,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
* @return An event as an dc_event_t object.
* You can query the event for information using dc_event_get_id(), dc_event_get_data1_int() and so on;
* if you are done with the event, you have to free the event using dc_event_unref().
* If NULL is returned, the context belonging to the event emitter is unref'd and no more events will come;
* If NULL is returned, the context belonging to the event emitter is unref'd and the no more events will come;
* in this case, free the event emitter using dc_event_emitter_unref().
*/
dc_event_t* dc_get_next_event(dc_event_emitter_t* emitter);
@@ -4360,7 +4366,7 @@ void dc_event_emitter_unref(dc_event_emitter_t* emitter);
* @return An event as an dc_event_t object.
* You can query the event for information using dc_event_get_id(), dc_event_get_data1_int() and so on;
* if you are done with the event, you have to free the event using dc_event_unref().
* If NULL is returned, the contexts belonging to the event emitter are unref'd and no more events will come;
* If NULL is returned, the context belonging to the event emitter is unref'd and the no more events will come;
* in this case, free the event emitter using dc_accounts_event_emitter_unref().
*/
dc_event_t* dc_accounts_get_next_event (dc_accounts_event_emitter_t* emitter);
@@ -4482,7 +4488,7 @@ void dc_event_unref(dc_event_t* event);
* This event should not be reported to the end-user using a popup or something like that.
*
* @param data1 0
* @param data2 (char*) Info string in English language.
* @param data2 (char*) Info string in english language.
*/
#define DC_EVENT_INFO 100
@@ -4491,7 +4497,7 @@ void dc_event_unref(dc_event_t* event);
* Emitted when SMTP connection is established and login was successful.
*
* @param data1 0
* @param data2 (char*) Info string in English language.
* @param data2 (char*) Info string in english language.
*/
#define DC_EVENT_SMTP_CONNECTED 101
@@ -4500,7 +4506,7 @@ void dc_event_unref(dc_event_t* event);
* Emitted when IMAP connection is established and login was successful.
*
* @param data1 0
* @param data2 (char*) Info string in English language.
* @param data2 (char*) Info string in english language.
*/
#define DC_EVENT_IMAP_CONNECTED 102
@@ -4508,7 +4514,7 @@ void dc_event_unref(dc_event_t* event);
* Emitted when a message was successfully sent to the SMTP server.
*
* @param data1 0
* @param data2 (char*) Info string in English language.
* @param data2 (char*) Info string in english language.
*/
#define DC_EVENT_SMTP_MESSAGE_SENT 103
@@ -4516,7 +4522,7 @@ void dc_event_unref(dc_event_t* event);
* Emitted when a message was successfully marked as deleted on the IMAP server.
*
* @param data1 0
* @param data2 (char*) Info string in English language.
* @param data2 (char*) Info string in english language.
*/
#define DC_EVENT_IMAP_MESSAGE_DELETED 104
@@ -4524,7 +4530,7 @@ void dc_event_unref(dc_event_t* event);
* Emitted when a message was successfully moved on IMAP.
*
* @param data1 0
* @param data2 (char*) Info string in English language.
* @param data2 (char*) Info string in english language.
*/
#define DC_EVENT_IMAP_MESSAGE_MOVED 105
@@ -4550,7 +4556,7 @@ void dc_event_unref(dc_event_t* event);
* This event should not be reported to the end-user using a popup or something like that.
*
* @param data1 0
* @param data2 (char*) Warning string in English language.
* @param data2 (char*) Warning string in english language.
*/
#define DC_EVENT_WARNING 300
@@ -4561,16 +4567,16 @@ void dc_event_unref(dc_event_t* event);
* As most things are asynchronous, things may go wrong at any time and the user
* should not be disturbed by a dialog or so. Instead, use a bubble or so.
*
* However, for ongoing processes (e.g. dc_configure())
* or for functions that are expected to fail (e.g. dc_continue_key_transfer())
* However, for ongoing processes (eg. dc_configure())
* or for functions that are expected to fail (eg. dc_continue_key_transfer())
* it might be better to delay showing these events until the function has really
* failed (returned false). It should be sufficient to report only the _last_ error
* in a message box then.
* in a messasge box then.
*
* @param data1 0
* @param data2 (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.
* however, most error strings will be in english language.
*/
#define DC_EVENT_ERROR 400
@@ -4597,13 +4603,13 @@ void dc_event_unref(dc_event_t* event);
/**
* An action cannot be performed because the user is not in the group.
* Reported e.g. after a call to
* Reported eg. after a call to
* dc_set_chat_name(), dc_set_chat_profile_image(),
* dc_add_contact_to_chat(), dc_remove_contact_from_chat(),
* dc_send_text_msg() or another sending function.
*
* @param data1 0
* @param data2 (char*) Info string in English language.
* @param data2 (char*) Info string in english language.
*/
#define DC_EVENT_ERROR_SELF_NOT_IN_GROUP 410
@@ -4635,12 +4641,12 @@ void dc_event_unref(dc_event_t* event);
/**
* Messages were marked noticed or seen.
* The UI may update badge counters or stop showing a chatlist-item with a bold font.
* The ui may update badge counters or stop showing a chatlist-item with a bold font.
*
* This event is emitted e.g. when calling dc_markseen_msgs(), dc_marknoticed_chat() or dc_marknoticed_contact()
* This event is emitted eg. when calling dc_markseen_msgs(), dc_marknoticed_chat() or dc_marknoticed_contact()
* or when a chat is answered on another device.
* Do not try to derive the state of an item from just the fact you received the event;
* use e.g. dc_msg_get_state() or dc_get_fresh_msg_cnt() for this purpose.
* use eg. dc_msg_get_state() or dc_get_fresh_msg_cnt() for this purpose.
*
* @param data1 (int) chat_id
* @param data2 0
@@ -4711,7 +4717,7 @@ void dc_event_unref(dc_event_t* event);
*
* @param data1 (int) contact_id of the contact for which the location has changed.
* If the locations of several contacts have been changed,
* e.g. after calling dc_delete_all_locations(), this parameter is set to 0.
* eg. after calling dc_delete_all_locations(), this parameter is set to 0.
* @param data2 0
*/
#define DC_EVENT_LOCATION_CHANGED 2035
@@ -4826,7 +4832,7 @@ void dc_event_unref(dc_event_t* event);
* works by just entering the name or the email-address.
*
* - There is no need for the user to do any special things
* (enable IMAP or so) in the provider's web interface or at other places.
* (enable IMAP or so) in the provider's webinterface or at other places.
* - There is no need for the user to enter advanced settings;
* server, port etc. are known by the core.
*
@@ -4837,8 +4843,8 @@ void dc_event_unref(dc_event_t* event);
/**
* Provider works, but there are preparations needed.
*
* - The user has to do some special things as "Enable IMAP in the web interface",
* what exactly, is described in the string returned by dc_provider_get_before_login_hints()
* - The user has to do some special things as "Enable IMAP in the Webinterface",
* what exactly, is described in the string returnd by dc_provider_get_before_login_hints()
* and, typically more detailed, in the page linked by dc_provider_get_overview_page().
* - There is no need for the user to enter advanced settings;
* server, port etc. should be known by the core.
@@ -4851,7 +4857,7 @@ void dc_event_unref(dc_event_t* event);
* Provider is not working.
* This provider status is returned for providers
* that are known to not work with Delta Chat.
* The UI should block logging in with this provider.
* The ui should block logging in with this provider.
*
* More information about that is typically provided
* in the string returned by dc_provider_get_before_login_hints()
@@ -4886,10 +4892,10 @@ void dc_event_unref(dc_event_t* event);
/**
* Archived chats are not included in the default chatlist returned by dc_get_chatlist().
* Instead, if there are _any_ archived chats, the pseudo-chat
* with the chat_id DC_CHAT_ID_ARCHIVED_LINK will be added at the end of the chatlist.
* with the chat_id DC_CHAT_ID_ARCHIVED_LINK will be added the the end of the chatlist.
*
* The UI typically shows a little icon or chats beside archived chats in the chatlist,
* this is needed as e.g. the search will also return archived chats.
* this is needed as eg. the search will also return archived chats.
*
* If archived chats receive new messages, they become normal chats again.
*
@@ -4909,7 +4915,7 @@ void dc_event_unref(dc_event_t* event);
/*
* TODO: Strings need some documentation about used placeholders.
* TODO: Strings need some doumentation about used placeholders.
*
* @defgroup DC_STR DC_STR
*
@@ -4981,8 +4987,7 @@ void dc_event_unref(dc_event_t* event);
#define DC_STR_ERROR_NO_NETWORK 87
#define DC_STR_PROTECTION_ENABLED 88
#define DC_STR_PROTECTION_DISABLED 89
#define DC_STR_REPLY_NOUN 90 /* e.g. "Reply", used in summaries, a noun, not a verb (not: "to reply") */
#define DC_STR_SELF_DELETED_MSG_BODY 91
#define DC_STR_REPLY_NOUN 90 /* eg. "Reply", used in summaries, a noun, not a verb (not: "to reply") */
/*
* @}

View File

@@ -1727,15 +1727,13 @@ pub unsafe extern "C" fn dc_imex(
let ctx = &*context;
if let Some(param1) = to_opt_string_lossy(param1) {
spawn(async move {
imex::imex(&ctx, what, &param1)
.await
.log_err(ctx, "IMEX failed")
});
} else {
eprintln!("dc_imex called without a valid directory");
}
let param1 = to_opt_string_lossy(param1);
spawn(async move {
imex::imex(&ctx, what, param1)
.await
.log_err(ctx, "IMEX failed")
});
}
#[no_mangle]
@@ -3008,11 +3006,6 @@ pub unsafe extern "C" fn dc_msg_set_quote(msg: *mut dc_msg_t, quote: *const dc_m
let ffi_msg = &mut *msg;
let ffi_quote = &*quote;
if ffi_msg.context != ffi_quote.context {
eprintln!("ignoring attempt to quote message from a different context");
return;
}
block_on(async move {
ffi_msg
.message

View File

@@ -443,20 +443,20 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
}
"export-backup" => {
let dir = dirs::home_dir().unwrap_or_default();
imex(&context, ImexMode::ExportBackup, &dir).await?;
imex(&context, ImexMode::ExportBackup, Some(&dir)).await?;
println!("Exported to {}.", dir.to_string_lossy());
}
"import-backup" => {
ensure!(!arg1.is_empty(), "Argument <backup-file> missing.");
imex(&context, ImexMode::ImportBackup, arg1).await?;
imex(&context, ImexMode::ImportBackup, Some(arg1)).await?;
}
"export-keys" => {
let dir = dirs::home_dir().unwrap_or_default();
imex(&context, ImexMode::ExportSelfKeys, &dir).await?;
imex(&context, ImexMode::ExportSelfKeys, Some(&dir)).await?;
println!("Exported to {}.", dir.to_string_lossy());
}
"import-keys" => {
imex(&context, ImexMode::ImportSelfKeys, arg1).await?;
imex(&context, ImexMode::ImportSelfKeys, Some(arg1)).await?;
}
"export-setup" => {
let setup_code = create_setup_code(&context);

View File

@@ -197,12 +197,14 @@ const CHAT_COMMANDS: [&str; 27] = [
"unpin",
"delchat",
];
const MESSAGE_COMMANDS: [&str; 6] = [
const MESSAGE_COMMANDS: [&str; 8] = [
"listmsgs",
"msginfo",
"listfresh",
"forward",
"markseen",
"star",
"unstar",
"delmsg",
];
const CONTACT_COMMANDS: [&str; 6] = [

View File

@@ -214,19 +214,6 @@ class Account(object):
:param name: (optional) display name for this contact
:returns: :class:`deltachat.contact.Contact` instance.
"""
(name, addr) = self.get_contact_addr_and_name(obj, name)
name = as_dc_charpointer(name)
addr = as_dc_charpointer(addr)
contact_id = lib.dc_create_contact(self._dc_context, name, addr)
return Contact(self, contact_id)
def get_contact(self, obj):
if isinstance(obj, Contact):
return obj
(_, addr) = self.get_contact_addr_and_name(obj)
return self.get_contact_by_addr(addr)
def get_contact_addr_and_name(self, obj, name=None):
if isinstance(obj, Account):
if not obj.is_configured():
raise ValueError("can only add addresses from configured accounts")
@@ -242,7 +229,13 @@ class Account(object):
if name is None and displayname:
name = displayname
return (name, addr)
return self._create_contact(addr, name)
def _create_contact(self, addr, name):
addr = as_dc_charpointer(addr)
name = as_dc_charpointer(name)
contact_id = lib.dc_create_contact(self._dc_context, name, addr)
return Contact(self, contact_id)
def delete_contact(self, contact):
""" delete a Contact.

View File

@@ -371,7 +371,7 @@ class Chat(object):
:raises ValueError: if contact could not be removed
:returns: None
"""
contact = self.account.get_contact(obj)
contact = self.account.create_contact(obj)
ret = lib.dc_remove_contact_from_chat(self.account._dc_context, self.id, contact.id)
if ret != 1:
raise ValueError("could not remove contact {!r} from chat".format(contact))

View File

@@ -103,14 +103,6 @@ class FFIEventTracker:
if rex.search(ev.data2):
return ev
def get_info_regex_groups(self, regex, check_error=True):
rex = re.compile(regex)
while 1:
ev = self.get_matching("DC_EVENT_INFO", check_error=check_error)
m = rex.match(ev.data2)
if m is not None:
return m.groups()
def ensure_event_not_queued(self, event_name_regex):
__tracebackhide__ = True
rex = re.compile("(?:{}).*".format(event_name_regex))

View File

@@ -330,13 +330,6 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig, data):
return accounts
def clone_online_account(self, account, pre_generated_key=True):
""" Clones addr, mail_pw, mvbox_watch, mvbox_move, sentbox_watch and the
direct_imap object of an online account. This simulates the user setting
up a new device without importing a backup.
`pre_generated_key` only means that a key from python/tests/data/key is
used in order to speed things up.
"""
self.live_count += 1
tmpdb = tmpdir.join("livedb%d" % self.live_count)
ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count))
@@ -510,9 +503,6 @@ def lp():
def step(self, msg):
print("-" * 5, "step " + msg, "-" * 5)
def indent(self, msg):
print(" " + msg)
return Printer()

View File

@@ -288,28 +288,6 @@ class TestOfflineChat:
qr = chat.get_join_qr()
assert ac2.check_qr(qr).is_ask_verifygroup
def test_removing_blocked_user_from_group(self, ac1, lp):
"""
Test that blocked contact is not unblocked when removed from a group.
See https://github.com/deltachat/deltachat-core-rust/issues/2030
"""
lp.sec("Create a group chat with a contact")
contact = ac1.create_contact("some1@example.org")
group = ac1.create_group_chat("title", contacts=[contact])
group.send_text("First group message")
lp.sec("ac1 blocks contact")
contact.block()
assert contact.is_blocked()
lp.sec("ac1 removes contact from their group")
group.remove_contact(contact)
assert contact.is_blocked()
lp.sec("ac1 adding blocked contact unblocks it")
group.add_contact(contact)
assert not contact.is_blocked()
def test_get_set_profile_image_simple(self, ac1, data):
chat = ac1.create_group_chat(name="title1")
p = data.get_path("d.png")
@@ -668,7 +646,7 @@ class TestOnlineAccount:
except Exception:
pass
def test_export_import_self_keys(self, acfactory, tmpdir, lp):
def test_export_import_self_keys(self, acfactory, tmpdir):
ac1, ac2 = acfactory.get_two_online_accounts()
dir = tmpdir.mkdir("exportdir")
@@ -676,17 +654,8 @@ class TestOnlineAccount:
assert len(export_files) == 2
for x in export_files:
assert x.startswith(dir.strpath)
key_id, = ac1._evtracker.get_info_regex_groups(r".*xporting.*KeyId\((.*)\).*")
ac1._evtracker.consume_events()
lp.sec("exported keys (private and public)")
for name in os.listdir(dir.strpath):
lp.indent(dir.strpath + os.sep + name)
lp.sec("importing into existing account")
ac2.import_self_keys(dir.strpath)
key_id2, = ac2._evtracker.get_info_regex_groups(
r".*stored.*KeyId\((.*)\).*", check_error=False)
assert key_id2 == key_id
def test_one_account_send_bcc_setting(self, acfactory, lp):
ac1 = acfactory.get_online_configuring_account()
@@ -969,30 +938,6 @@ class TestOnlineAccount:
except queue.Empty:
pass # mark_seen_messages() has generated events before it returns
def test_reply_privately(self, acfactory):
ac1, ac2 = acfactory.get_two_online_accounts()
group1 = ac1.create_group_chat("group")
group1.add_contact(ac2)
group1.send_text("hello")
msg2 = ac2._evtracker.wait_next_messages_changed()
group2 = msg2.create_chat()
assert group2.get_name() == group1.get_name()
msg_reply = Message.new_empty(ac2, "text")
msg_reply.set_text("message reply")
msg_reply.quote = msg2
private_chat1 = ac1.create_chat(ac2)
private_chat2 = ac2.create_chat(ac1)
private_chat2.send_msg(msg_reply)
msg_reply1 = ac1._evtracker.wait_next_incoming_message()
assert msg_reply1.quoted_text == "hello"
assert not msg_reply1.chat.is_group()
assert msg_reply1.chat.id == private_chat1.id
def test_mdn_asymetric(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts(move=True)
@@ -1161,8 +1106,8 @@ class TestOnlineAccount:
# Majority prefers encryption now
assert msg5.is_encrypted()
def test_quote_encrypted(self, acfactory, lp):
"""Test that replies to encrypted messages with quotes are encrypted."""
def test_reply_encrypted(self, acfactory, lp):
"""Test that replies to encrypted messages are encrypted."""
ac1, ac2 = acfactory.get_two_online_accounts()
lp.sec("ac1: create chat with ac2")
@@ -1211,39 +1156,6 @@ class TestOnlineAccount:
assert msg_in.quoted_text == quoted_msg.text
assert msg_in.is_encrypted() == quoted_msg.is_encrypted()
def test_quote_attachment(self, tmpdir, acfactory, lp):
"""Test that replies with an attachment and a quote are received correctly."""
ac1, ac2 = acfactory.get_two_online_accounts()
lp.sec("ac1 creates chat with ac2")
chat1 = ac1.create_chat(ac2)
lp.sec("ac1 sends text message to ac2")
chat1.send_text("hi")
lp.sec("ac2 receives contact request from ac1")
received_message = ac2._evtracker.wait_next_messages_changed()
assert received_message.text == "hi"
basename = "attachment.txt"
p = os.path.join(tmpdir.strpath, basename)
with open(p, "w") as f:
f.write("data to send")
lp.sec("ac2 sends a reply to ac1")
chat2 = received_message.create_chat()
reply = Message.new_empty(ac2, "file")
reply.set_text("message reply")
reply.set_file(p)
reply.quote = received_message
chat2.send_msg(reply)
lp.sec("ac1 receives a reply from ac2")
received_reply = ac1._evtracker.wait_next_incoming_message()
assert received_reply.text == "message reply"
assert received_reply.quoted_text == received_message.text
assert open(received_reply.filename).read() == "data to send"
def test_saved_mime_on_received_message(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
@@ -1609,7 +1521,7 @@ class TestOnlineAccount:
lp.sec("ac1 blocks ac2")
contact = ac1.create_contact(ac2)
contact.block()
contact.set_blocked()
assert contact.is_blocked()
ev = ac1._evtracker.get_matching("DC_EVENT_CONTACTS_CHANGED")
assert ev.data1 == contact.id
@@ -2063,7 +1975,6 @@ class TestOnlineAccount:
assert ac1.direct_imap.idle_wait_for_seen()
ac1_clone = acfactory.clone_online_account(ac1)
ac1_clone.set_config("fetch_existing_msgs", "1")
ac1_clone._configtracker.wait_finish()
ac1_clone.start_io()

View File

@@ -188,7 +188,7 @@ impl Accounts {
let id = self.add_account().await?;
let ctx = self.get_account(id).await.expect("just added");
match crate::imex::imex(&ctx, crate::imex::ImexMode::ImportBackup, &file).await {
match crate::imex::imex(&ctx, crate::imex::ImexMode::ImportBackup, Some(file)).await {
Ok(_) => Ok(id),
Err(err) => {
// remove temp account

View File

@@ -63,12 +63,6 @@ impl<'a> BlobObject<'a> {
blobname: name.clone(),
cause: err.into(),
})?;
// workaround a bug in async-std
// (the executor does not handle blocking operation in Drop correctly,
// see https://github.com/async-rs/async-std/issues/900 )
let _ = file.flush().await;
let blob = BlobObject {
blobdir,
name: format!("$BLOBDIR/{}", name),
@@ -157,10 +151,6 @@ impl<'a> BlobObject<'a> {
cause: err,
});
}
// workaround, see create() for details
let _ = dst_file.flush().await;
let blob = BlobObject {
blobdir: context.get_blobdir(),
name: format!("$BLOBDIR/{}", name),

View File

@@ -337,7 +337,7 @@ impl ChatId {
);
/* Up to 2017-11-02 deleting a group also implied leaving it, see above why we have changed this. */
let chat = Chat::load_from_db(context, self).await?;
let _chat = Chat::load_from_db(context, self).await?;
context
.sql
.execute(
@@ -373,17 +373,6 @@ impl ChatId {
let j = job::Job::new(Action::Housekeeping, 0, Params::new(), 10);
job::add(context, j).await;
if chat.is_self_talk() {
let mut msg = Message::new(Viewtype::Text);
msg.text = Some(
context
.stock_str(StockMessage::SelfDeletedMsgBody)
.await
.into(),
);
add_device_msg(&context, None, Some(&mut msg)).await?;
}
Ok(())
}

View File

@@ -69,12 +69,8 @@ pub enum Config {
#[strum(props(default = "0"))] // also change MediaQuality.default() on changes
MediaQuality,
/// If set to "1", on the first time `start_io()` is called after configuring,
/// the newest existing messages are fetched.
/// Existing recipients are added to the contact database regardless of this setting.
#[strum(props(default = "0"))]
// disabled for now, we'll set this back to "1" at some point
FetchExistingMsgs,
#[strum(props(default = "1"))]
FetchExisting,
#[strum(props(default = "0"))]
KeyGenType,

View File

@@ -163,8 +163,8 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
DC_LP_AUTH_NORMAL as i32
};
let ctx2 = ctx.clone();
let update_device_chats_handle = task::spawn(async move { ctx2.update_device_chats().await });
//let ctx2 = ctx.clone();
//let update_device_chats_handle = task::spawn(async move { ctx2.update_device_chats().await });
// Step 1: Load the parameters and check email-address and password
@@ -364,7 +364,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
.await;
progress!(ctx, 940);
update_device_chats_handle.await?;
//update_device_chats_handle.await?;
Ok(())
}

View File

@@ -205,11 +205,6 @@ pub const WORSE_IMAGE_SIZE: u32 = 640;
// this value can be increased if the folder configuration is changed and must be redone on next program start
pub const DC_FOLDERS_CONFIGURED_VERSION: i32 = 3;
// if more recipients are needed in SMTP's `RCPT TO:` header, recipient-list is splitted to chunks.
// this does not affect MIME'e `To:` header.
// can be overwritten by the setting `max_smtp_rcpt_to` in provider-db.
pub const DEFAULT_MAX_SMTP_RCPT_TO: usize = 50;
#[derive(
Debug,
Display,

View File

@@ -163,6 +163,10 @@ impl Context {
/// Stops the IO scheduler.
pub async fn stop_io(&self) {
info!(self, "stopping IO");
if !self.is_io_running().await {
info!(self, "IO is not running");
return;
}
self.inner.stop_io().await;
}
@@ -479,19 +483,14 @@ impl InnerContext {
}
async fn stop_io(&self) {
if self.is_io_running().await {
let token = {
let lock = &*self.scheduler.read().await;
lock.pre_stop().await
};
{
let lock = &mut *self.scheduler.write().await;
lock.stop(token).await;
}
}
if let Some(ephemeral_task) = self.ephemeral_task.write().await.take() {
ephemeral_task.cancel().await;
assert!(self.is_io_running().await, "context is already stopped");
let token = {
let lock = &*self.scheduler.read().await;
lock.pre_stop().await
};
{
let lock = &mut *self.scheduler.write().await;
lock.stop(token).await;
}
}
}

View File

@@ -387,7 +387,6 @@ async fn add_parts(
// this message is a classic email not a chat-message nor a reply to one
match show_emails {
ShowEmails::Off => {
info!(context, "Classical email not shown (TRASH)");
*chat_id = ChatId::new(DC_CHAT_ID_TRASH);
allow_creation = false;
}
@@ -446,7 +445,10 @@ async fn add_parts(
// it might also be blocked and displayed in the deaddrop as a result
if chat_id.is_unset() && mime_parser.failure_report.is_some() {
*chat_id = ChatId::new(DC_CHAT_ID_TRASH);
info!(context, "Message belongs to an NDN (TRASH)",);
info!(
context,
"Message belongs to an NDN and is not shown in a chat.",
);
}
if chat_id.is_unset() {
@@ -487,7 +489,7 @@ async fn add_parts(
// check if the message belongs to a mailing list
if mime_parser.is_mailinglist_message() {
*chat_id = ChatId::new(DC_CHAT_ID_TRASH);
info!(context, "Message belongs to a mailing list (TRASH)");
info!(context, "Message belongs to a mailing list and is ignored.",);
}
}
@@ -531,7 +533,6 @@ async fn add_parts(
if chat_id.is_unset() {
// maybe from_id is null or sth. else is suspicious, move message to trash
*chat_id = ChatId::new(DC_CHAT_ID_TRASH);
info!(context, "No chat id for incoming msg (TRASH)")
}
// if the chat_id is blocked,
@@ -641,14 +642,13 @@ async fn add_parts(
}
if chat_id.is_unset() {
*chat_id = ChatId::new(DC_CHAT_ID_TRASH);
info!(context, "No chat id for outgoing message (TRASH)")
}
}
if fetching_existing_messages && mime_parser.decrypting_failed {
*chat_id = ChatId::new(DC_CHAT_ID_TRASH);
// We are only gathering old messages on first start. We do not want to add loads of non-decryptable messages to the chats.
info!(context, "Existing non-decipherable message. (TRASH)");
info!(context, "Dropping existing non-decipherable message.");
}
// Extract ephemeral timer from the message.
@@ -772,6 +772,9 @@ async fn add_parts(
*sent_timestamp = std::cmp::min(*sent_timestamp, rcvd_timestamp);
// unarchive chat
chat_id.unarchive(context).await?;
// if the mime-headers should be saved, find out its size
// (the mime-header ends with an empty line)
let save_mime_headers = context.get_config_bool(Config::SaveMimeHeaders).await;
@@ -893,10 +896,6 @@ async fn add_parts(
*insert_msg_id = *id;
}
if !is_hidden {
chat_id.unarchive(context).await?;
}
*hidden = is_hidden;
created_db_entries.extend(ids.iter().map(|id| (chat_id, *id)));
mime_parser.parts = new_parts;
@@ -1080,24 +1079,37 @@ async fn create_or_lookup_group(
set_better_msg(mime_parser, &better_msg);
}
let grpid = try_getting_grpid(mime_parser);
if grpid.is_empty() {
return create_or_lookup_adhoc_group(
context,
mime_parser,
allow_creation,
create_blocked,
from_id,
to_ids,
)
.await
.map_err(|err| {
info!(context, "could not create adhoc-group: {:?}", err);
err
});
let mut grpid = "".to_string();
if let Some(optional_field) = mime_parser.get(HeaderDef::ChatGroupId) {
grpid = optional_field.clone();
}
if grpid.is_empty() {
if let Some(extracted_grpid) = mime_parser
.get(HeaderDef::MessageId)
.and_then(|value| dc_extract_grpid_from_rfc724_mid(&value))
{
grpid = extracted_grpid.to_string();
} else if let Some(extracted_grpid) = extract_grpid(mime_parser, HeaderDef::InReplyTo) {
grpid = extracted_grpid.to_string();
} else if let Some(extracted_grpid) = extract_grpid(mime_parser, HeaderDef::References) {
grpid = extracted_grpid.to_string();
} else {
return create_or_lookup_adhoc_group(
context,
mime_parser,
allow_creation,
create_blocked,
from_id,
to_ids,
)
.await
.map_err(|err| {
info!(context, "could not create adhoc-group: {:?}", err);
err
});
}
}
// now we have a grpid that is non-empty
// but we might not know about this group
@@ -1267,7 +1279,6 @@ async fn create_or_lookup_group(
} else {
// The message was decrypted successfully, but contains a late "quit" or otherwise
// unwanted message.
info!(context, "message belongs to unwanted group (TRASH)");
return Ok((ChatId::new(DC_CHAT_ID_TRASH), chat_id_blocked));
}
}
@@ -1367,27 +1378,6 @@ async fn create_or_lookup_group(
Ok((chat_id, chat_id_blocked))
}
fn try_getting_grpid(mime_parser: &MimeMessage) -> String {
if let Some(optional_field) = mime_parser.get(HeaderDef::ChatGroupId) {
return optional_field.clone();
}
if let Some(extracted_grpid) = mime_parser
.get(HeaderDef::MessageId)
.and_then(|value| dc_extract_grpid_from_rfc724_mid(&value))
{
return extracted_grpid.to_string();
}
if !mime_parser.has_chat_version() {
if let Some(extracted_grpid) = extract_grpid(mime_parser, HeaderDef::InReplyTo) {
return extracted_grpid.to_string();
} else if let Some(extracted_grpid) = extract_grpid(mime_parser, HeaderDef::References) {
return extracted_grpid.to_string();
}
}
"".to_string()
}
/// try extract a grpid from a message-id list header value
fn extract_grpid(mime_parser: &MimeMessage, headerdef: HeaderDef) -> Option<&str> {
let header = mime_parser.get(headerdef)?;
@@ -2228,7 +2218,7 @@ mod tests {
let t = TestContext::new_alice().await;
// create one-to-one with bob, archive one-to-one
let bob_id = Contact::create(&t.ctx, "bob", "bob@example.com")
let bob_id = Contact::create(&t.ctx, "bob", "bob@exampel.org")
.await
.unwrap();
let one2one_id = chat::create_by_contact_id(&t.ctx, bob_id).await.unwrap();

View File

@@ -1174,31 +1174,22 @@ mod tests {
assert_eq!(msgs.len(), 1);
// do not repeat the warning every day ...
// (we test that for the 2 subsequent days, this may be the next month, so the result should be 1 or 2 device message)
maybe_warn_on_outdated(
&t.ctx,
timestamp_now + (365 + 1) * 24 * 60 * 60,
get_provider_update_timestamp(),
)
.await;
maybe_warn_on_outdated(
&t.ctx,
timestamp_now + (365 + 2) * 24 * 60 * 60,
get_provider_update_timestamp(),
)
.await;
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 1);
let device_chat_id = chats.get_chat_id(0);
let msgs = chat::get_chat_msgs(&t.ctx, device_chat_id, 0, None).await;
let test_len = msgs.len();
assert!(test_len == 1 || test_len == 2);
assert_eq!(msgs.len(), 1);
// ... but every month
// (forward generous 33 days to avoid being in the same month as in the previous check)
maybe_warn_on_outdated(
&t.ctx,
timestamp_now + (365 + 33) * 24 * 60 * 60,
timestamp_now + (365 + 31) * 24 * 60 * 60,
get_provider_update_timestamp(),
)
.await;
@@ -1206,6 +1197,6 @@ mod tests {
assert_eq!(chats.len(), 1);
let device_chat_id = chats.get_chat_id(0);
let msgs = chat::get_chat_msgs(&t.ctx, device_chat_id, 0, None).await;
assert_eq!(msgs.len(), test_len + 1);
assert_eq!(msgs.len(), 2);
}
}

View File

@@ -31,7 +31,6 @@ use crate::param::*;
use crate::pgp;
use crate::sql::{self, Sql};
use crate::stock::StockMessage;
use ::pgp::types::KeyTrait;
use async_tar::Archive;
// Name of the database file in the backup.
@@ -79,7 +78,11 @@ pub enum ImexMode {
///
/// Only one import-/export-progress can run at the same time.
/// To cancel an import-/export-progress, drop the future returned by this function.
pub async fn imex(context: &Context, what: ImexMode, param1: impl AsRef<Path>) -> Result<()> {
pub async fn imex(
context: &Context,
what: ImexMode,
param1: Option<impl AsRef<Path>>,
) -> Result<()> {
let cancel = context.alloc_ongoing().await?;
let res = async {
@@ -410,8 +413,6 @@ async fn set_self_key(
},
)
.await?;
info!(context, "stored self key: {:?}", keypair.secret.key_id());
Ok(())
}
@@ -438,11 +439,19 @@ pub fn normalize_setup_code(s: &str) -> String {
out
}
async fn imex_inner(context: &Context, what: ImexMode, path: impl AsRef<Path>) -> Result<()> {
info!(context, "Import/export dir: {}", path.as_ref().display());
ensure!(context.sql.is_open().await, "Database not opened.");
async fn imex_inner(
context: &Context,
what: ImexMode,
param: Option<impl AsRef<Path>>,
) -> Result<()> {
ensure!(param.is_some(), "No Import/export dir/file given.");
info!(context, "Import/export process started.");
context.emit_event(EventType::ImexProgress(10));
ensure!(context.sql.is_open().await, "Database not opened.");
let path = param.ok_or_else(|| format_err!("Imex: Param was None"))?;
if what == ImexMode::ExportBackup || what == ImexMode::ExportSelfKeys {
// before we export anything, make sure the private key exists
if e2ee::ensure_secret_key_exists(context).await.is_err() {
@@ -866,12 +875,6 @@ async fn import_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()
continue;
}
}
info!(
context,
"considering key file: {}",
path_plus_name.display()
);
match dc_read_file(context, &path_plus_name).await {
Ok(buf) => {
let armored = std::string::String::from_utf8_lossy(&buf);
@@ -961,7 +964,7 @@ where
let any_key = key as &dyn Any;
let kind = if any_key.downcast_ref::<SignedPublicKey>().is_some() {
"public"
} else if any_key.downcast_ref::<SignedSecretKey>().is_some() {
} else if any_key.downcast_ref::<SignedPublicKey>().is_some() {
"private"
} else {
"unknown"
@@ -969,12 +972,7 @@ where
let id = id.map_or("default".into(), |i| i.to_string());
dir.as_ref().join(format!("{}-key-{}.asc", kind, &id))
};
info!(
context,
"Exporting key {:?} to {}",
key.key_id(),
file_name.display()
);
info!(context, "Exporting key {}", file_name.display());
dc_delete_file(context, &file_name).await;
let content = key.to_asc(None).into_bytes();
@@ -1042,7 +1040,7 @@ mod tests {
}
#[async_std::test]
async fn test_export_public_key_to_asc_file() {
async fn test_export_key_to_asc_file() {
let context = TestContext::new().await;
let key = alice_keypair().public;
let blobdir = "$BLOBDIR";
@@ -1056,35 +1054,19 @@ mod tests {
assert_eq!(bytes, key.to_asc(None).into_bytes());
}
#[async_std::test]
async fn test_export_private_key_to_asc_file() {
let context = TestContext::new().await;
let key = alice_keypair().secret;
let blobdir = "$BLOBDIR";
assert!(export_key_to_asc_file(&context.ctx, blobdir, None, &key)
.await
.is_ok());
let blobdir = context.ctx.get_blobdir().to_str().unwrap();
let filename = format!("{}/private-key-default.asc", blobdir);
let bytes = async_std::fs::read(&filename).await.unwrap();
assert_eq!(bytes, key.to_asc(None).into_bytes());
}
#[async_std::test]
async fn test_export_and_import_key() {
let context = TestContext::new().await;
context.configure_alice().await;
let blobdir = context.ctx.get_blobdir().to_str().unwrap();
if let Err(err) = imex(&context.ctx, ImexMode::ExportSelfKeys, blobdir).await {
panic!("got error on export: {:?}", err);
}
let blobdir = "$BLOBDIR";
assert!(imex(&context.ctx, ImexMode::ExportSelfKeys, Some(blobdir))
.await
.is_ok());
let context2 = TestContext::new().await;
context2.configure_alice().await;
if let Err(err) = imex(&context2.ctx, ImexMode::ImportSelfKeys, blobdir).await {
panic!("got error on import: {:?}", err);
}
let blobdir = context.ctx.get_blobdir().to_str().unwrap();
assert!(imex(&context.ctx, ImexMode::ImportSelfKeys, Some(blobdir))
.await
.is_ok());
}
#[test]

View File

@@ -639,7 +639,7 @@ impl Job {
add_all_recipients_as_contacts(context, imap, Config::ConfiguredMvboxFolder).await;
add_all_recipients_as_contacts(context, imap, Config::ConfiguredInboxFolder).await;
if context.get_config_bool(Config::FetchExistingMsgs).await {
if context.get_config_bool(Config::FetchExisting).await {
for config in &[
Config::ConfiguredMvboxFolder,
Config::ConfiguredInboxFolder,

View File

@@ -1141,23 +1141,13 @@ async fn build_body_file(
Viewtype::Image | Viewtype::Gif => format!(
"{}.{}",
if base_name.is_empty() {
chrono::Utc
.timestamp(msg.timestamp_sort as i64, 0)
.format("image_%Y-%m-%d_%H-%M-%S")
.to_string()
"image"
} else {
base_name.to_string()
base_name
},
&suffix,
),
Viewtype::Video => format!(
"video_{}.{}",
chrono::Utc
.timestamp(msg.timestamp_sort as i64, 0)
.format("%Y-%m-%d_%H-%M-%S")
.to_string(),
&suffix
),
Viewtype::Video => format!("video.{}", &suffix),
_ => blob.as_file_name().to_string(),
};

View File

@@ -295,8 +295,7 @@ impl MimeMessage {
/// Squashes mutlipart chat messages with attachment into single-part messages.
///
/// Delta Chat sends attachments, such as images, in two-part messages, with the first message
/// containing a description. If such a message is detected, text from the first part can be
/// moved to the second part, and the first part dropped.
/// containing an explanation. If such a message is detected, first part can be safely dropped.
#[allow(clippy::indexing_slicing)]
fn squash_attachment_parts(&mut self) {
if let [textpart, filepart] = &self.parts[..] {
@@ -316,9 +315,6 @@ impl MimeMessage {
// insert new one
filepart.msg = self.parts[0].msg.clone();
if let Some(quote) = self.parts[0].param.get(Param::Quote) {
filepart.param.set(Param::Quote, quote);
}
// forget the one we use now
self.parts[0].msg = "".to_string();
@@ -2272,53 +2268,4 @@ From: alice <alice@example.org>
);
assert_eq!(message.parts[0].msg, "");
}
#[async_std::test]
async fn parse_quote_top_posting() {
let context = TestContext::new().await;
let raw = br##"Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
Subject: Re: top posting
MIME-Version: 1.0
In-Reply-To: <bar@example.org>
Message-ID: <foo@example.org>
To: bob <bob@example.org>
From: alice <alice@example.org>
A reply.
On 2020-10-25, Bob wrote:
> A quote.
"##;
let message = MimeMessage::from_bytes(&context.ctx, &raw[..])
.await
.unwrap();
assert_eq!(message.get_subject(), Some("Re: top posting".to_string()));
assert_eq!(message.parts.len(), 1);
assert_eq!(message.parts[0].typ, Viewtype::Text);
assert_eq!(
message.parts[0].param.get(Param::Quote).unwrap(),
"A quote."
);
assert_eq!(message.parts[0].msg, "A reply.");
}
#[async_std::test]
async fn test_attachment_quote() {
let context = TestContext::new().await;
let raw = include_bytes!("../test-data/message/quote_attach.eml");
let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..])
.await
.unwrap();
assert_eq!(mimeparser.get_subject().unwrap(), "Message from Alice");
assert_eq!(mimeparser.parts.len(), 1);
assert_eq!(mimeparser.parts[0].msg, "Reply");
assert_eq!(
mimeparser.parts[0].param.get(Param::Quote).unwrap(),
"Quote"
);
assert_eq!(mimeparser.parts[0].typ, Viewtype::File);
}
}

View File

@@ -32,7 +32,6 @@ static P_AKTIVIX_ORG: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -47,7 +46,6 @@ static P_AOL: Lazy<Provider> = Lazy::new(|| {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
}
});
@@ -76,7 +74,6 @@ static P_ARCOR_DE: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -104,7 +101,6 @@ static P_AUTISTICI_ORG: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -132,7 +128,6 @@ static P_BLUEWIN_CH: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -146,21 +141,20 @@ static P_BUZON_UY: Lazy<Provider> = Lazy::new(|| Provider {
Server {
protocol: IMAP,
socket: STARTTLS,
hostname: "mail.buzon.uy",
hostname: "buzon.uy",
port: 143,
username_pattern: EMAIL,
},
Server {
protocol: SMTP,
socket: STARTTLS,
hostname: "mail.buzon.uy",
hostname: "buzon.uy",
port: 587,
username_pattern: EMAIL,
},
],
config_defaults: None,
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -188,7 +182,6 @@ static P_CHELLO_AT: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -201,7 +194,6 @@ static P_COMCAST: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -214,7 +206,6 @@ static P_DISMAIL_DE: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -227,7 +218,6 @@ static P_DISROOT: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -279,7 +269,6 @@ static P_DUBBY_ORG: Lazy<Provider> = Lazy::new(|| Provider {
},
]),
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -296,7 +285,6 @@ static P_EXAMPLE_COM: Lazy<Provider> = Lazy::new(|| {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
}
});
@@ -311,7 +299,6 @@ static P_FASTMAIL: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -326,7 +313,6 @@ static P_FIREMAIL_DE: Lazy<Provider> = Lazy::new(|| {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
}
});
@@ -357,7 +343,6 @@ static P_FIVE_CHAT: Lazy<Provider> = Lazy::new(|| Provider {
},
]),
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -385,7 +370,6 @@ static P_FREENET_DE: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -402,7 +386,6 @@ static P_GMAIL: Lazy<Provider> = Lazy::new(|| {
],
config_defaults: None,
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: Some(Oauth2Authorizer::Gmail),
}
});
@@ -438,7 +421,6 @@ static P_GMX_NET: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -468,7 +450,6 @@ static P_HERMES_RADIO: Lazy<Provider> = Lazy::new(|| Provider {
},
]),
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -483,7 +464,6 @@ static P_HEY_COM: Lazy<Provider> = Lazy::new(|| {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
}
});
@@ -497,7 +477,6 @@ static P_I_UA: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -526,7 +505,6 @@ static P_ICLOUD: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -539,7 +517,6 @@ static P_KOLST_COM: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -552,7 +529,6 @@ static P_KONTENT_COM: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -565,7 +541,6 @@ static P_MAIL_RU: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -578,7 +553,6 @@ static P_MAILBOX_ORG: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -601,10 +575,9 @@ static P_NAUTA_CU: Lazy<Provider> = Lazy::new(|| {
ConfigDefault { key: Config::MvboxMove, value: "0" },
ConfigDefault { key: Config::E2eeEnabled, value: "0" },
ConfigDefault { key: Config::MediaQuality, value: "1" },
ConfigDefault { key: Config::FetchExistingMsgs, value: "0" },
ConfigDefault { key: Config::FetchExisting, value: "0" },
]),
strict_tls: false,
max_smtp_rcpt_to: Some(20),
oauth2_authorizer: None,
}
});
@@ -622,7 +595,6 @@ static P_OUTLOOK_COM: Lazy<Provider> = Lazy::new(|| {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
}
});
@@ -651,7 +623,6 @@ static P_POSTEO: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -666,7 +637,6 @@ static P_PROTONMAIL: Lazy<Provider> = Lazy::new(|| {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
}
});
@@ -680,7 +650,6 @@ static P_RISEUP_NET: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -693,7 +662,6 @@ static P_ROGERS_COM: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -706,7 +674,6 @@ static P_SYSTEMLI_ORG: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -721,7 +688,6 @@ static P_T_ONLINE: Lazy<Provider> = Lazy::new(|| {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
}
});
@@ -774,7 +740,6 @@ static P_TESTRUN: Lazy<Provider> = Lazy::new(|| Provider {
},
]),
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -802,7 +767,6 @@ static P_TISCALI_IT: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -815,7 +779,6 @@ static P_UKR_NET: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -843,7 +806,6 @@ static P_UNDERNET_UY: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -856,7 +818,6 @@ static P_VFEMAIL: Lazy<Provider> = Lazy::new(|| Provider {
server: vec![],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -884,7 +845,6 @@ static P_VODAFONE_DE: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -902,7 +862,6 @@ static P_WEB_DE: Lazy<Provider> = Lazy::new(|| {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
}
});
@@ -920,7 +879,6 @@ static P_YAHOO: Lazy<Provider> = Lazy::new(|| {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
}
});
@@ -949,7 +907,6 @@ static P_YANDEX_RU: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: true,
max_smtp_rcpt_to: None,
oauth2_authorizer: Some(Oauth2Authorizer::Yandex),
});
@@ -977,7 +934,6 @@ static P_ZIGGO_NL: Lazy<Provider> = Lazy::new(|| Provider {
],
config_defaults: None,
strict_tls: false,
max_smtp_rcpt_to: None,
oauth2_authorizer: None,
});
@@ -1153,4 +1109,4 @@ pub static PROVIDER_DATA: Lazy<HashMap<&'static str, &'static Provider>> = Lazy:
});
pub static PROVIDER_UPDATED: Lazy<chrono::NaiveDate> =
Lazy::new(|| chrono::NaiveDate::from_ymd(2020, 10, 30));
Lazy::new(|| chrono::NaiveDate::from_ymd(2020, 10, 17));

View File

@@ -75,7 +75,6 @@ pub struct Provider {
pub server: Vec<Server>,
pub config_defaults: Option<Vec<ConfigDefault>>,
pub strict_tls: bool,
pub max_smtp_rcpt_to: Option<u16>,
pub oauth2_authorizer: Option<Oauth2Authorizer>,
}

View File

@@ -104,9 +104,6 @@ def process_data(data, file):
strict_tls = data.get("strict_tls", False)
strict_tls = "true" if strict_tls else "false"
max_smtp_rcpt_to = data.get("max_smtp_rcpt_to", 0)
max_smtp_rcpt_to = "Some(" + str(max_smtp_rcpt_to) + ")" if max_smtp_rcpt_to != 0 else "None"
oauth2 = data.get("oauth2", "")
oauth2 = "Some(Oauth2Authorizer::" + camel(oauth2) + ")" if oauth2 != "" else "None"
@@ -122,7 +119,6 @@ def process_data(data, file):
provider += " server: vec![\n" + server + " ],\n"
provider += " config_defaults: " + config_defaults + ",\n"
provider += " strict_tls: " + strict_tls + ",\n"
provider += " max_smtp_rcpt_to: " + max_smtp_rcpt_to + ",\n"
provider += " oauth2_authorizer: " + oauth2 + ",\n"
provider += "});\n\n"
else:

View File

@@ -71,7 +71,7 @@ pub fn simplify(mut input: String, is_chat_message: bool) -> (String, bool, Opti
let lines = split_lines(&input);
let (lines, is_forwarded) = skip_forward_header(&lines);
let (lines, mut top_quote) = remove_top_quote(lines);
let (lines, top_quote) = remove_top_quote(lines);
let original_lines = &lines;
let lines = remove_message_footer(lines);
@@ -79,16 +79,12 @@ pub fn simplify(mut input: String, is_chat_message: bool) -> (String, bool, Opti
render_message(lines, false)
} else {
let (lines, has_nonstandard_footer) = remove_nonstandard_footer(lines);
let (lines, mut bottom_quote) = remove_bottom_quote(lines);
if top_quote.is_none() && bottom_quote.is_some() {
std::mem::swap(&mut top_quote, &mut bottom_quote);
}
let (lines, has_bottom_quote) = remove_bottom_quote(lines);
if lines.iter().all(|it| it.trim().is_empty()) {
render_message(original_lines, false)
} else {
render_message(lines, has_nonstandard_footer || bottom_quote.is_some())
render_message(lines, has_nonstandard_footer || has_bottom_quote)
}
};
(text, is_forwarded, top_quote)
@@ -109,27 +105,16 @@ fn skip_forward_header<'a>(lines: &'a [&str]) -> (&'a [&'a str], bool) {
}
#[allow(clippy::indexing_slicing)]
fn remove_bottom_quote<'a>(lines: &'a [&str]) -> (&'a [&'a str], Option<String>) {
let mut first_quoted_line = lines.len();
fn remove_bottom_quote<'a>(lines: &'a [&str]) -> (&'a [&'a str], bool) {
let mut last_quoted_line = None;
for (l, line) in lines.iter().enumerate().rev() {
if is_plain_quote(line) {
if last_quoted_line.is_none() {
first_quoted_line = l + 1;
}
last_quoted_line = Some(l)
} else if !is_empty_line(line) {
break;
}
}
if let Some(mut l_last) = last_quoted_line {
let quoted_text = lines[l_last..first_quoted_line]
.iter()
.map(|s| {
s.strip_prefix(">")
.map_or(*s, |u| u.strip_prefix(" ").unwrap_or(u))
})
.join("\n");
if l_last > 1 && is_empty_line(lines[l_last - 1]) {
l_last -= 1
}
@@ -139,9 +124,9 @@ fn remove_bottom_quote<'a>(lines: &'a [&str]) -> (&'a [&'a str], Option<String>)
l_last -= 1
}
}
(&lines[..l_last], Some(quoted_text))
(&lines[..l_last], true)
} else {
(lines, None)
(lines, false)
}
}

View File

@@ -3,11 +3,8 @@
use super::Smtp;
use async_smtp::*;
use crate::config::Config;
use crate::constants::DEFAULT_MAX_SMTP_RCPT_TO;
use crate::context::Context;
use crate::events::EventType;
use crate::provider::get_provider_info;
use itertools::Itertools;
use std::time::Duration;
@@ -37,51 +34,37 @@ impl Smtp {
) -> Result<()> {
let message_len_bytes = message.len();
let mut chunk_size = DEFAULT_MAX_SMTP_RCPT_TO;
if let Some(provider) = get_provider_info(
&context
.get_config(Config::ConfiguredAddr)
let recipients_display = recipients.iter().map(|x| x.to_string()).join(",");
let envelope =
Envelope::new(self.from.clone(), recipients).map_err(Error::EnvelopeError)?;
let mail = SendableEmail::new(
envelope,
format!("{}", job_id), // only used for internal logging
message,
);
if let Some(ref mut transport) = self.transport {
// The timeout is 1min + 3min per MB.
let timeout = 60 + (180 * message_len_bytes / 1_000_000) as u64;
transport
.send_with_timeout(mail, Some(&Duration::from_secs(timeout)))
.await
.unwrap_or_default(),
) {
if let Some(max_smtp_rcpt_to) = provider.max_smtp_rcpt_to {
chunk_size = max_smtp_rcpt_to as usize;
}
}
.map_err(Error::SendError)?;
for recipients_chunk in recipients.chunks(chunk_size).into_iter() {
let recipients = recipients_chunk.to_vec();
let recipients_display = recipients.iter().map(|x| x.to_string()).join(",");
context.emit_event(EventType::SmtpMessageSent(format!(
"Message len={} was smtp-sent to {}",
message_len_bytes, recipients_display
)));
self.last_success = Some(std::time::SystemTime::now());
let envelope =
Envelope::new(self.from.clone(), recipients).map_err(Error::EnvelopeError)?;
let mail = SendableEmail::new(
envelope,
format!("{}", job_id), // only used for internal logging
&message,
Ok(())
} else {
warn!(
context,
"uh? SMTP has no transport, failed to send to {}", recipients_display
);
if let Some(ref mut transport) = self.transport {
// The timeout is 1min + 3min per MB.
let timeout = 60 + (180 * message_len_bytes / 1_000_000) as u64;
transport
.send_with_timeout(mail, Some(&Duration::from_secs(timeout)))
.await
.map_err(Error::SendError)?;
context.emit_event(EventType::SmtpMessageSent(format!(
"Message len={} was smtp-sent to {}",
message_len_bytes, recipients_display
)));
self.last_success = Some(std::time::SystemTime::now());
} else {
warn!(
context,
"uh? SMTP has no transport, failed to send to {}", recipients_display
);
return Err(Error::NoTransport);
}
Err(Error::NoTransport)
}
Ok(())
}
}

View File

@@ -244,10 +244,6 @@ pub enum StockMessage {
// used in summaries, a noun, not a verb (not: "to reply")
#[strum(props(fallback = "Reply"))]
ReplyNoun = 90,
#[strum(props(fallback = "You deleted the \"Saved messages\" chat.\n\n\
To use the \"Saved messages\" feature again, create a new chat with yourself."))]
SelfDeletedMsgBody = 91,
}
/*
@@ -467,7 +463,6 @@ mod tests {
use crate::constants::DC_CONTACT_ID_SELF;
use crate::chat::Chat;
use crate::chatlist::Chatlist;
use num_traits::ToPrimitive;
@@ -659,31 +654,8 @@ mod tests {
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 2);
let chat0 = Chat::load_from_db(&t.ctx, chats.get_chat_id(0))
.await
.unwrap();
let (self_talk_id, device_chat_id) = if chat0.is_self_talk() {
(chats.get_chat_id(0), chats.get_chat_id(1))
} else {
(chats.get_chat_id(1), chats.get_chat_id(0))
};
// delete self-talk first; this adds a message to device-chat about how self-talk can be restored
let device_chat_msgs_before = chat::get_chat_msgs(&t.ctx, device_chat_id, 0, None)
.await
.len();
self_talk_id.delete(&t.ctx).await.ok();
assert_eq!(
chat::get_chat_msgs(&t.ctx, device_chat_id, 0, None)
.await
.len(),
device_chat_msgs_before + 1
);
// delete device chat
device_chat_id.delete(&t.ctx).await.ok();
// check, that the chatlist is empty
chats.get_chat_id(0).delete(&t.ctx).await.ok();
chats.get_chat_id(1).delete(&t.ctx).await.ok();
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
assert_eq!(chats.len(), 0);

View File

@@ -1,27 +0,0 @@
Subject: Message from Alice
MIME-Version: 1.0
In-Reply-To: <Mr.7h_HyOuM3Dz.xcp4v8QiQua@testrun.org>
Date: Sun, 08 Nov 2020 01:16:26 +0000
Chat-Version: 1.0
Message-ID: <Mr.126R7OsBKvk.dGGBC5WcsLF@testrun.org>
To: Bob <bob@example.org>
From: Alice <alice@testrun.org>
Content-Type: multipart/mixed; boundary="uWbWY2IyEtJ8wZmp282Na11hxBBXlV"
--uWbWY2IyEtJ8wZmp282Na11hxBBXlV
Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no
> Quote
Reply
--uWbWY2IyEtJ8wZmp282Na11hxBBXlV
Content-Type: text/plain
Content-Disposition: attachment; filename="attachment.txt"
Content-Transfer-Encoding: base64
ZGF0YSB0byBzZW5k
--uWbWY2IyEtJ8wZmp282Na11hxBBXlV--