diff --git a/cmdline/cmdline.c b/cmdline/cmdline.c
index a9f084a5..38d6938b 100644
--- a/cmdline/cmdline.c
+++ b/cmdline/cmdline.c
@@ -32,6 +32,108 @@ your library */
#include "../src/mrpgp.h"
+/*
+ * Reset database tables. This function is called from Core cmdline.
+ *
+ * Argument is a bitmask, executing single or multiple actions in one call.
+ *
+ * e.g. bitmask 7 triggers actions definded with bits 1, 2 and 4.
+ */
+int mrmailbox_reset_tables(mrmailbox_t* ths, int bits)
+{
+ if( ths == NULL || ths->m_magic != MR_MAILBOX_MAGIC ) {
+ return 0;
+ }
+
+ mrmailbox_log_info(ths, 0, "Resetting tables (%i)...", bits);
+
+ mrsqlite3_lock(ths->m_sql);
+
+ if( bits & 1 ) {
+ mrsqlite3_execute__(ths->m_sql, "DELETE FROM jobs;");
+ mrmailbox_log_info(ths, 0, "(1) Jobs reset.");
+ }
+
+ if( bits & 2 ) {
+ mrsqlite3_execute__(ths->m_sql, "DELETE FROM acpeerstates;");
+ mrmailbox_log_info(ths, 0, "(2) Peerstates reset.");
+ }
+
+ if( bits & 4 ) {
+ mrsqlite3_execute__(ths->m_sql, "DELETE FROM keypairs;");
+ mrmailbox_log_info(ths, 0, "(4) Private keypairs reset.");
+ }
+
+ if( bits & 8 ) {
+ mrsqlite3_execute__(ths->m_sql, "DELETE FROM contacts WHERE id>" MR_STRINGIFY(MR_CONTACT_ID_LAST_SPECIAL) ";"); /* the other IDs are reserved - leave these rows to make sure, the IDs are not used by normal contacts*/
+ mrsqlite3_execute__(ths->m_sql, "DELETE FROM chats WHERE id>" MR_STRINGIFY(MR_CHAT_ID_LAST_SPECIAL) ";");
+ mrsqlite3_execute__(ths->m_sql, "DELETE FROM chats_contacts;");
+ mrsqlite3_execute__(ths->m_sql, "DELETE FROM msgs WHERE id>" MR_STRINGIFY(MR_MSG_ID_LAST_SPECIAL) ";");
+ mrsqlite3_execute__(ths->m_sql, "DELETE FROM config WHERE keyname LIKE 'imap.%' OR keyname LIKE 'configured%';");
+ mrsqlite3_execute__(ths->m_sql, "DELETE FROM leftgrps;");
+ mrmailbox_log_info(ths, 0, "(8) Rest but server config reset.");
+ }
+
+ mrsqlite3_unlock(ths->m_sql);
+
+ ths->m_cb(ths, MR_EVENT_MSGS_CHANGED, 0, 0);
+
+ return 1;
+}
+
+
+/*
+ * Clean up the contacts table. This function is called from Core cmdline.
+ *
+ * All contacts not involved in a chat, not blocked and not being a deaddrop
+ * are removed.
+ *
+ * Deleted contacts from the OS address book normally stay in the contacts
+ * database. With this cleanup, they are also removed, as well as all
+ * auto-added contacts, unless they are used in a chat or for blocking purpose.
+ */
+static int mrmailbox_cleanup_contacts(mrmailbox_t* ths)
+{
+ if( ths == NULL || ths->m_magic != MR_MAILBOX_MAGIC ) {
+ return 0;
+ }
+
+ mrmailbox_log_info(ths, 0, "Cleaning up contacts ...");
+
+ mrsqlite3_lock(ths->m_sql);
+
+ mrsqlite3_execute__(ths->m_sql, "DELETE FROM contacts WHERE id>" MR_STRINGIFY(MR_CONTACT_ID_LAST_SPECIAL) " AND blocked=0 AND NOT EXISTS (SELECT contact_id FROM chats_contacts where contacts.id = chats_contacts.contact_id) AND NOT EXISTS (select from_id from msgs WHERE msgs.from_id = contacts.id);");
+
+ mrsqlite3_unlock(ths->m_sql);
+
+ return 1;
+}
+
+static int mrmailbox_poke_eml_file(mrmailbox_t* ths, const char* filename)
+{
+ /* mainly for testing, may be called by mrmailbox_import_spec() */
+ int success = 0;
+ char* data = NULL;
+ size_t data_bytes;
+
+ if( ths == NULL || ths->m_magic != MR_MAILBOX_MAGIC ) {
+ return 0;
+ }
+
+ if( mr_read_file(filename, (void**)&data, &data_bytes, ths) == 0 ) {
+ goto cleanup;
+ }
+
+ mrmailbox_receive_imf(ths, data, data_bytes, "import", 0, 0); /* this static function is the reason why this function is not moved to mrmailbox_imex.c */
+ success = 1;
+
+cleanup:
+ free(data);
+
+ return success;
+}
+
+
static int poke_public_key(mrmailbox_t* mailbox, const char* addr, const char* public_key_file)
{
/* mainly for testing: if the partner does not support Autocrypt,
diff --git a/deltachat-core.cbp b/deltachat-core.cbp
index adc3b914..cef16ce5 100644
--- a/deltachat-core.cbp
+++ b/deltachat-core.cbp
@@ -492,7 +492,7 @@
-
+
diff --git a/src/meson.build b/src/meson.build
index 75666c22..def26783 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -18,7 +18,7 @@ lib_src = [
'mrmailbox_e2ee.c',
'mrmailbox_imex.c',
'mrmailbox_log.c',
- 'mrmailbox_tools.c',
+ 'mrmailbox_receive_imf.c',
'mrmimefactory.c',
'mrmimeparser.c',
'mrmsg.c',
diff --git a/src/mrmailbox-private.h b/src/mrmailbox-private.h
index c8870b6e..0352ae3b 100644
--- a/src/mrmailbox-private.h
+++ b/src/mrmailbox-private.h
@@ -77,20 +77,12 @@ struct _mrmailbox
};
-
+void mrmailbox_receive_imf (mrmailbox_t*, const char* imf_raw_not_terminated, size_t imf_raw_bytes, const char* server_folder, uint32_t server_uid, uint32_t flags);
uint32_t mrmailbox_send_msg_object (mrmailbox_t*, uint32_t chat_id, mrmsg_t*);
void mrmailbox_connect_to_imap (mrmailbox_t*, mrjob_t*);
void mrmailbox_wake_lock (mrmailbox_t*);
void mrmailbox_wake_unlock (mrmailbox_t*);
-int mrmailbox_poke_eml_file (mrmailbox_t*, const char* file);
-int mrmailbox_is_reply_to_known_message__ (mrmailbox_t*, mrmimeparser_t*);
-int mrmailbox_is_reply_to_messenger_message__ (mrmailbox_t*, mrmimeparser_t*);
-time_t mrmailbox_correct_bad_timestamp__ (mrmailbox_t* ths, uint32_t chat_id, uint32_t from_id, time_t desired_timestamp, int is_fresh_msg);
-void mrmailbox_add_or_lookup_contacts_by_mailbox_list__(mrmailbox_t* ths, struct mailimf_mailbox_list* mb_list, int origin, mrarray_t* ids, int* check_self);
-void mrmailbox_add_or_lookup_contacts_by_address_list__(mrmailbox_t* ths, struct mailimf_address_list* adr_list, int origin, mrarray_t* ids, int* check_self);
int mrmailbox_get_archived_count__ (mrmailbox_t*);
-int mrmailbox_reset_tables (mrmailbox_t*, int bits); /* reset tables but leaves server configuration, 1=jobs, 2=e2ee, 8=rest but server config */
-int mrmailbox_cleanup_contacts (mrmailbox_t* ths); /* remove all contacts that are not used (e.g. in a chat, or blocked */
size_t mrmailbox_get_real_contact_cnt__ (mrmailbox_t*);
uint32_t mrmailbox_add_or_lookup_contact__ (mrmailbox_t*, const char* display_name /*can be NULL*/, const char* addr_spec, int origin, int* sth_modified);
int mrmailbox_get_contact_origin__ (mrmailbox_t*, uint32_t id, int* ret_blocked);
diff --git a/src/mrmailbox.c b/src/mrmailbox.c
index b1780973..f085e733 100644
--- a/src/mrmailbox.c
+++ b/src/mrmailbox.c
@@ -28,7 +28,6 @@
#include "mrmailbox_internal.h"
#include "mrimap.h"
#include "mrsmtp.h"
-#include "mrmimeparser.h"
#include "mrmimefactory.h"
#include "mrtools.h"
#include "mrjob.h"
@@ -38,830 +37,6 @@
#include "mrapeerstate.h"
-/*******************************************************************************
- * Handle groups for received messages
- ******************************************************************************/
-
-
-/* the function tries extracts the group-id from the message and returns the
-corresponding chat_id. If the chat_id is not existant, it is created.
-If the message contains groups commands (name, profile image, changed members),
-they are executed as well.
-
-So when the function returns, the caller has the group id matching the current
-state of the group. */
-static void create_or_lookup_group__(mrmailbox_t* mailbox, mrmimeparser_t* mime_parser, int create_blocked,
- int32_t from_id, mrarray_t* to_ids,
- uint32_t* ret_chat_id, int* ret_chat_blocked)
-{
- uint32_t chat_id = 0;
- int chat_blocked = 0;
- char* grpid = NULL;
- char* grpname = NULL;
- sqlite3_stmt* stmt;
- int i, to_ids_cnt = mrarray_get_cnt(to_ids);
- char* self_addr = NULL;
- int recreate_member_list = 0;
- int send_EVENT_CHAT_MODIFIED = 0;
-
- char* X_MrRemoveFromGrp = NULL; /* pointer somewhere into mime_parser, must not be freed */
- char* X_MrAddToGrp = NULL; /* pointer somewhere into mime_parser, must not be freed */
- int X_MrGrpNameChanged = 0;
- int X_MrGrpImageChanged = 0;
-
- /* search the grpid in the header */
- {
- struct mailimf_field* field = NULL;
- struct mailimf_optional_field* optional_field = NULL;
-
- if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Group-ID", "X-MrGrpId"))!=NULL ) {
- grpid = safe_strdup(optional_field->fld_value);
- }
-
- if( grpid == NULL )
- {
- if( (field=mrmimeparser_lookup_field(mime_parser, "Message-ID"))!=NULL && field->fld_type==MAILIMF_FIELD_MESSAGE_ID ) {
- struct mailimf_message_id* fld_message_id = field->fld_data.fld_message_id;
- if( fld_message_id ) {
- grpid = mr_extract_grpid_from_rfc724_mid(fld_message_id->mid_value);
- }
- }
-
- if( grpid == NULL )
- {
- if( (field=mrmimeparser_lookup_field(mime_parser, "In-Reply-To"))!=NULL && field->fld_type==MAILIMF_FIELD_IN_REPLY_TO ) {
- struct mailimf_in_reply_to* fld_in_reply_to = field->fld_data.fld_in_reply_to;
- if( fld_in_reply_to ) {
- grpid = mr_extract_grpid_from_rfc724_mid_list(fld_in_reply_to->mid_list);
- }
- }
-
- if( grpid == NULL )
- {
- if( (field=mrmimeparser_lookup_field(mime_parser, "References"))!=NULL && field->fld_type==MAILIMF_FIELD_REFERENCES ) {
- struct mailimf_references* fld_references = field->fld_data.fld_references;
- if( fld_references ) {
- grpid = mr_extract_grpid_from_rfc724_mid_list(fld_references->mid_list);
- }
- }
-
- if( grpid == NULL )
- {
- goto cleanup;
- }
- }
- }
- }
-
- if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Group-Name", "X-MrGrpName"))!=NULL ) {
- grpname = mr_decode_header_string(optional_field->fld_value); /* this is no changed groupname message */
- }
-
- if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Group-Member-Removed", "X-MrRemoveFromGrp"))!=NULL ) {
- X_MrRemoveFromGrp = optional_field->fld_value;
- mime_parser->m_is_system_message = MR_SYSTEM_MEMBER_REMOVED_FROM_GROUP;
- }
- else if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Group-Member-Added", "X-MrAddToGrp"))!=NULL ) {
- X_MrAddToGrp = optional_field->fld_value;
- mime_parser->m_is_system_message = MR_SYSTEM_MEMBER_ADDED_TO_GROUP;
- }
- else if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Group-Name-Changed", "X-MrGrpNameChanged"))!=NULL ) {
- X_MrGrpNameChanged = 1;
- mime_parser->m_is_system_message = MR_SYSTEM_GROUPNAME_CHANGED;
- }
- else if( (optional_field=mrmimeparser_lookup_optional_field(mime_parser, "Chat-Group-Image"))!=NULL ) {
- X_MrGrpImageChanged = 1;
- mime_parser->m_is_system_message = MR_SYSTEM_GROUPIMAGE_CHANGED;
- }
- }
-
- /* check, if we have a chat with this group ID */
- stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_CHATS_WHERE_grpid,
- "SELECT id, blocked FROM chats WHERE grpid=?;");
- sqlite3_bind_text (stmt, 1, grpid, -1, SQLITE_STATIC);
- if( sqlite3_step(stmt)==SQLITE_ROW ) {
- chat_id = sqlite3_column_int(stmt, 0);
- chat_blocked = sqlite3_column_int(stmt, 1);
- }
-
- /* check if the sender is a member of the existing group -
- if not, the message does not go to the group chat but to the normal chat with the sender */
- if( chat_id!=0 && !mrmailbox_is_contact_in_chat__(mailbox, chat_id, from_id) ) {
- chat_id = 0;
- goto cleanup;
- }
-
- /* check if the group does not exist but should be created */
- int group_explicitly_left = mrmailbox_group_explicitly_left__(mailbox, grpid);
-
- self_addr = mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", "");
- if( chat_id == 0
- && !mrmimeparser_is_mailinglist_message(mime_parser)
- && grpname
- && X_MrRemoveFromGrp==NULL /*otherwise, a pending "quit" message may pop up*/
- && (!group_explicitly_left || (X_MrAddToGrp&&strcasecmp(self_addr,X_MrAddToGrp)==0) ) /*re-create explicitly left groups only if ourself is re-added*/
- )
- {
- stmt = mrsqlite3_prepare_v2_(mailbox->m_sql,
- "INSERT INTO chats (type, name, grpid, blocked) VALUES(?, ?, ?, ?);");
- sqlite3_bind_int (stmt, 1, MR_CHAT_TYPE_GROUP);
- sqlite3_bind_text(stmt, 2, grpname, -1, SQLITE_STATIC);
- sqlite3_bind_text(stmt, 3, grpid, -1, SQLITE_STATIC);
- sqlite3_bind_int (stmt, 4, create_blocked);
- if( sqlite3_step(stmt)!=SQLITE_DONE ) {
- goto cleanup;
- }
- sqlite3_finalize(stmt);
- chat_id = sqlite3_last_insert_rowid(mailbox->m_sql->m_cobj);
- chat_blocked = create_blocked;
- recreate_member_list = 1;
- }
-
- /* again, check chat_id */
- if( chat_id <= MR_CHAT_ID_LAST_SPECIAL ) {
- chat_id = 0;
- if( group_explicitly_left ) {
- chat_id = MR_CHAT_ID_TRASH; /* we got a message for a chat we've deleted - do not show this even as a normal chat */
- }
- goto cleanup;
- }
-
- /* execute group commands */
- if( X_MrAddToGrp || X_MrRemoveFromGrp )
- {
- recreate_member_list = 1;
- }
- else if( X_MrGrpNameChanged && grpname && strlen(grpname) < 200 )
- {
- stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "UPDATE chats SET name=? WHERE id=?;");
- sqlite3_bind_text(stmt, 1, grpname, -1, SQLITE_STATIC);
- sqlite3_bind_int (stmt, 2, chat_id);
- sqlite3_step(stmt);
- sqlite3_finalize(stmt);
- mailbox->m_cb(mailbox, MR_EVENT_CHAT_MODIFIED, chat_id, 0);
- }
-
- if( X_MrGrpImageChanged )
- {
- int ok = 0;
- char* grpimage = NULL;
- if( carray_count(mime_parser->m_parts)>=1 ) {
- mrmimepart_t* textpart = (mrmimepart_t*)carray_get(mime_parser->m_parts, 0);
- if( textpart->m_type == MR_MSG_TEXT ) {
- if( carray_count(mime_parser->m_parts)>=2 ) {
- mrmimepart_t* imgpart = (mrmimepart_t*)carray_get(mime_parser->m_parts, 1);
- if( imgpart->m_type == MR_MSG_IMAGE ) {
- grpimage = mrparam_get(imgpart->m_param, MRP_FILE, NULL);
- ok = 1;
- }
- }
- else {
- ok = 1;
- }
- }
- }
-
- if( ok ) {
- mrchat_t* chat = mrchat_new(mailbox);
- mrmailbox_log_info(mailbox, 0, "New group image set to %s.", grpimage? "DELETED" : grpimage);
- mrchat_load_from_db__(chat, chat_id);
- mrparam_set(chat->m_param, MRP_PROFILE_IMAGE, grpimage/*may be NULL*/);
- mrchat_update_param__(chat);
- mrchat_unref(chat);
- free(grpimage);
- send_EVENT_CHAT_MODIFIED = 1;
- }
- }
-
- /* add members to group/check members
- for recreation: we should add a timestamp */
- if( recreate_member_list )
- {
- const char* skip = X_MrRemoveFromGrp? X_MrRemoveFromGrp : NULL;
-
- stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "DELETE FROM chats_contacts WHERE chat_id=?;");
- sqlite3_bind_int (stmt, 1, chat_id);
- sqlite3_step(stmt);
- sqlite3_finalize(stmt);
-
- if( skip==NULL || strcasecmp(self_addr, skip) != 0 ) {
- mrmailbox_add_contact_to_chat__(mailbox, chat_id, MR_CONTACT_ID_SELF);
- }
-
- if( from_id > MR_CONTACT_ID_LAST_SPECIAL ) {
- if( mrmailbox_contact_addr_equals__(mailbox, from_id, self_addr)==0
- && (skip==NULL || mrmailbox_contact_addr_equals__(mailbox, from_id, skip)==0) ) {
- mrmailbox_add_contact_to_chat__(mailbox, chat_id, from_id);
- }
- }
-
- for( i = 0; i < to_ids_cnt; i++ )
- {
- uint32_t to_id = mrarray_get_id(to_ids, i); /* to_id is only once in to_ids and is non-special */
- if( mrmailbox_contact_addr_equals__(mailbox, to_id, self_addr)==0
- && (skip==NULL || mrmailbox_contact_addr_equals__(mailbox, to_id, skip)==0) ) {
- mrmailbox_add_contact_to_chat__(mailbox, chat_id, to_id);
- }
- }
- send_EVENT_CHAT_MODIFIED = 1;
- }
-
- if( send_EVENT_CHAT_MODIFIED ) {
- mailbox->m_cb(mailbox, MR_EVENT_CHAT_MODIFIED, chat_id, 0);
- }
-
- /* check the number of receivers -
- the only critical situation is if the user hits "Reply" instead of "Reply all" in a non-messenger-client */
- if( to_ids_cnt == 1 && mime_parser->m_is_send_by_messenger==0 ) {
- int is_contact_cnt = mrmailbox_get_chat_contact_count__(mailbox, chat_id);
- if( is_contact_cnt > 3 /* to_ids_cnt==1 may be "From: A, To: B, SELF" as SELF is not counted in to_ids_cnt. So everything up to 3 is no error. */ ) {
- chat_id = 0;
- goto cleanup;
- }
- }
-
-cleanup:
- free(grpid);
- free(grpname);
- free(self_addr);
- if( ret_chat_id ) { *ret_chat_id = chat_id; }
- if( ret_chat_blocked ) { *ret_chat_blocked = chat_id? chat_blocked : 0; }
-}
-
-
-/*******************************************************************************
- * Receive a message and add it to the database
- ******************************************************************************/
-
-
-static void receive_imf(mrmailbox_t* ths, const char* imf_raw_not_terminated, size_t imf_raw_bytes,
- const char* server_folder, uint32_t server_uid, uint32_t flags)
-{
- /* the function returns the number of created messages in the database */
- int incoming = 0;
- int incoming_origin = MR_ORIGIN_UNSET;
- #define outgoing (!incoming)
-
- mrarray_t* to_ids = NULL;
- int to_self = 0;
-
- uint32_t from_id = 0;
- int from_id_blocked = 0;
- uint32_t to_id = 0;
- uint32_t chat_id = 0;
- int chat_id_blocked = 0;
- int state = MR_STATE_UNDEFINED;
-
- sqlite3_stmt* stmt;
- size_t i, icnt;
- uint32_t first_dblocal_id = 0;
- char* rfc724_mid = NULL; /* Message-ID from the header */
- time_t message_timestamp = MR_INVALID_TIMESTAMP;
- mrmimeparser_t* mime_parser = mrmimeparser_new(ths->m_blobdir, ths);
- int db_locked = 0;
- int transaction_pending = 0;
- const struct mailimf_field* field;
-
- carray* created_db_entries = carray_new(16);
- int create_event_to_send = MR_EVENT_MSGS_CHANGED;
-
- carray* rr_event_to_send = carray_new(16);
-
- int has_return_path = 0;
- char* txt_raw = NULL;
-
- mrmailbox_log_info(ths, 0, "Receive message #%lu from %s.", server_uid, server_folder? server_folder:"?");
-
- to_ids = mrarray_new(ths, 16);
- if( to_ids==NULL || created_db_entries==NULL || rr_event_to_send==NULL || mime_parser == NULL ) {
- mrmailbox_log_info(ths, 0, "Bad param.");
- goto cleanup;
- }
-
- /* parse the imf to mailimf_message {
- mailimf_fields* msg_fields {
- clist* fld_list; // list of mailimf_field
- }
- mailimf_body* msg_body { // != NULL
- const char * bd_text; // != NULL
- size_t bd_size;
- }
- };
- normally, this is done by mailimf_message_parse(), however, as we also need the MIME data,
- we use mailmime_parse() through MrMimeParser (both call mailimf_struct_multiple_parse() somewhen, I did not found out anything
- that speaks against this approach yet) */
- mrmimeparser_parse(mime_parser, imf_raw_not_terminated, imf_raw_bytes);
- if( mrhash_count(&mime_parser->m_header)==0 ) {
- mrmailbox_log_info(ths, 0, "No header.");
- goto cleanup; /* Error - even adding an empty record won't help as we do not know the message ID */
- }
-
- mrsqlite3_lock(ths->m_sql);
- db_locked = 1;
-
- mrsqlite3_begin_transaction__(ths->m_sql);
- transaction_pending = 1;
-
-
- /* Check, if the mail comes from extern, resp. is not sent by us. This is a _really_ important step
- as messages sent by us are used to validate other mail senders and receivers.
- For this purpose, we assume, the `Return-Path:`-header is never present if the message is sent by us.
- The `Received:`-header may be another idea, however, this is also set if mails are transfered from other accounts via IMAP.
- Using `From:` alone is no good idea, as mailboxes may use different sending-addresses - moreover, they may change over the years.
- However, we use `From:` as an additional hint below. */
- if( mrmimeparser_lookup_field(mime_parser, "Return-Path") ) {
- has_return_path = 1;
- }
-
- if( has_return_path ) {
- incoming = 1;
- }
-
- /* for incoming messages, get From: and check if it is known (for known From:'s we add the other To:/Cc:/Bcc: in the 3rd pass) */
- if( incoming
- && (field=mrmimeparser_lookup_field(mime_parser, "From"))!=NULL
- && field->fld_type==MAILIMF_FIELD_FROM)
- {
- struct mailimf_from* fld_from = field->fld_data.fld_from;
- if( fld_from )
- {
- int check_self;
- mrarray_t* from_list = mrarray_new(ths, 16);
- mrmailbox_add_or_lookup_contacts_by_mailbox_list__(ths, fld_from->frm_mb_list, MR_ORIGIN_INCOMING_UNKNOWN_FROM, from_list, &check_self);
- if( check_self )
- {
- if( mrmimeparser_sender_equals_recipient(mime_parser) )
- {
- from_id = MR_CONTACT_ID_SELF;
- }
- else
- {
- incoming = 0; /* The `Return-Path:`-approach above works well, however, there may be outgoing messages which we also receive -
- for these messages, the `Return-Path:` is set although we're the sender. To correct these cases, we add an
- additional From: check - which, however, will not work for older From:-addresses used on the mailbox. */
- }
- }
- else
- {
- if( mrarray_get_cnt(from_list)>=1 ) /* if there is no from given, from_id stays 0 which is just fine. These messages are very rare, however, we have to add them to the database (they go to the "deaddrop" chat) to avoid a re-download from the server. See also [**] */
- {
- from_id = mrarray_get_id(from_list, 0);
- incoming_origin = mrmailbox_get_contact_origin__(ths, from_id, &from_id_blocked);
- }
- }
- mrarray_unref(from_list);
- }
- }
-
- /* Make sure, to_ids starts with the first To:-address (Cc: and Bcc: are added in the loop below pass) */
- if( (field=mrmimeparser_lookup_field(mime_parser, "To"))!=NULL
- && field->fld_type==MAILIMF_FIELD_TO )
- {
- struct mailimf_to* fld_to = field->fld_data.fld_to; /* can be NULL */
- if( fld_to )
- {
- mrmailbox_add_or_lookup_contacts_by_address_list__(ths, fld_to->to_addr_list /*!= NULL*/,
- outgoing? MR_ORIGIN_OUTGOING_TO : (incoming_origin>=MR_ORIGIN_MIN_VERIFIED? MR_ORIGIN_INCOMING_TO : MR_ORIGIN_INCOMING_UNKNOWN_TO), to_ids, &to_self);
- }
- }
-
- if( mrmimeparser_has_nonmeta(mime_parser) )
- {
-
- /**********************************************************************
- * Add parts
- *********************************************************************/
-
- /* collect the rest information */
- if( (field=mrmimeparser_lookup_field(mime_parser, "Cc"))!=NULL && field->fld_type==MAILIMF_FIELD_CC )
- {
- struct mailimf_cc* fld_cc = field->fld_data.fld_cc;
- if( fld_cc ) {
- mrmailbox_add_or_lookup_contacts_by_address_list__(ths, fld_cc->cc_addr_list,
- outgoing? MR_ORIGIN_OUTGOING_CC : (incoming_origin>=MR_ORIGIN_MIN_VERIFIED? MR_ORIGIN_INCOMING_CC : MR_ORIGIN_INCOMING_UNKNOWN_CC), to_ids, NULL);
- }
- }
-
- if( (field=mrmimeparser_lookup_field(mime_parser, "Bcc"))!=NULL && field->fld_type==MAILIMF_FIELD_BCC )
- {
- struct mailimf_bcc* fld_bcc = field->fld_data.fld_bcc;
- if( outgoing && fld_bcc ) {
- mrmailbox_add_or_lookup_contacts_by_address_list__(ths, fld_bcc->bcc_addr_list,
- MR_ORIGIN_OUTGOING_BCC, to_ids, NULL);
- }
- }
-
- /* check if the message introduces a new chat:
- - outgoing messages introduce a chat with the first to: address if they are sent by a messenger
- - incoming messages introduce a chat only for known contacts if they are sent by a messenger
- (of course, the user can add other chats manually later) */
- if( incoming )
- {
- state = (flags&MR_IMAP_SEEN)? MR_STATE_IN_SEEN : MR_STATE_IN_FRESH;
- to_id = MR_CONTACT_ID_SELF;
-
- /* test if there is a normal chat with the sender - if so, this allows us to create groups in the next step */
- uint32_t test_normal_chat_id = 0;
- int test_normal_chat_id_blocked = 0;
- mrmailbox_lookup_real_nchat_by_contact_id__(ths, from_id, &test_normal_chat_id, &test_normal_chat_id_blocked);
-
- /* get the chat_id - a chat_id here is no indicator that the chat is displayed in the normal list, it might also be
- blocked and displayed in the deaddrop as a result */
- if( chat_id == 0 )
- {
- /* try to create a group */
- int create_blocked = ((test_normal_chat_id&&test_normal_chat_id_blocked==MR_CHAT_NOT_BLOCKED) || incoming_origin>=MR_ORIGIN_MIN_START_NEW_NCHAT/*always false, for now*/)? MR_CHAT_NOT_BLOCKED : MR_CHAT_DEADDROP_BLOCKED;
- create_or_lookup_group__(ths, mime_parser, create_blocked, from_id, to_ids, &chat_id, &chat_id_blocked);
- if( chat_id && chat_id_blocked && !create_blocked ) {
- mrmailbox_unblock_chat__(ths, chat_id);
- chat_id_blocked = 0;
- }
- }
-
- if( chat_id == 0 )
- {
- /* check if the message belongs to a mailing list */
- if( mrmimeparser_is_mailinglist_message(mime_parser) ) {
- chat_id = MR_CHAT_ID_TRASH;
- mrmailbox_log_info(ths, 0, "Message belongs to a mailing list and is ignored.");
- }
- }
-
- if( chat_id == 0 )
- {
- /* try to create a normal chat */
- int create_blocked = (incoming_origin>=MR_ORIGIN_MIN_START_NEW_NCHAT/*always false, for now*/ || from_id==to_id)? MR_CHAT_NOT_BLOCKED : MR_CHAT_DEADDROP_BLOCKED;
- if( test_normal_chat_id ) {
- chat_id = test_normal_chat_id;
- chat_id_blocked = test_normal_chat_id_blocked;
- }
- else {
- mrmailbox_create_or_lookup_nchat_by_contact_id__(ths, from_id, create_blocked, &chat_id, &chat_id_blocked);
- }
-
- if( chat_id && chat_id_blocked ) {
- if( !create_blocked ) {
- mrmailbox_unblock_chat__(ths, chat_id);
- chat_id_blocked = 0;
- }
- else if( mrmailbox_is_reply_to_known_message__(ths, mime_parser) ) {
- mrmailbox_scaleup_contact_origin__(ths, from_id, MR_ORIGIN_INCOMING_REPLY_TO); /* we do not want any chat to be created implicitly. Because of the origin-scale-up, the contact requests will pop up and this should be just fine. */
- mrmailbox_log_info(ths, 0, "Message is a reply to a known message, mark sender as known.");
- incoming_origin = MR_MAX(incoming_origin, MR_ORIGIN_INCOMING_REPLY_TO);
- }
- }
- }
-
- if( chat_id == 0 )
- {
- /* maybe from_id is null or sth. else is suspicious, move message to trash */
- chat_id = MR_CHAT_ID_TRASH;
- }
-
- /* degrade state for unknown senders and non-delta messages
- (the latter may be removed if we run into spam problems, currently this is fine)
- (noticed messages do count as being unread; therefore, the deaddrop will not popup in the chatlist) */
- if( chat_id_blocked && state == MR_STATE_IN_FRESH ) {
- if( incoming_originm_is_send_by_messenger==0 ) {
- state = MR_STATE_IN_NOTICED;
- }
- }
- }
- else /* outgoing */
- {
- state = MR_STATE_OUT_DELIVERED; /* the mail is on the IMAP server, probably it is also delivered. We cannot recreate other states (read, error). */
- from_id = MR_CONTACT_ID_SELF;
- if( mrarray_get_cnt(to_ids) >= 1 ) {
- to_id = mrarray_get_id(to_ids, 0);
-
- if( chat_id == 0 )
- {
- create_or_lookup_group__(ths, mime_parser, MR_CHAT_NOT_BLOCKED, from_id, to_ids, &chat_id, &chat_id_blocked);
- if( chat_id && chat_id_blocked ) {
- mrmailbox_unblock_chat__(ths, chat_id);
- chat_id_blocked = 0;
- }
- }
-
- if( chat_id == 0 )
- {
- int create_blocked = (mime_parser->m_is_send_by_messenger && !mrmailbox_is_contact_blocked__(ths, to_id))? MR_CHAT_NOT_BLOCKED : MR_CHAT_DEADDROP_BLOCKED;
- mrmailbox_create_or_lookup_nchat_by_contact_id__(ths, to_id, create_blocked, &chat_id, &chat_id_blocked);
- if( chat_id && chat_id_blocked && !create_blocked ) {
- mrmailbox_unblock_chat__(ths, chat_id);
- chat_id_blocked = 0;
- }
- }
- }
-
- if( chat_id == 0 ) {
- if( mrarray_get_cnt(to_ids) == 0 && to_self ) {
- /* from_id == to_id == MR_CONTACT_ID_SELF - this is a self-sent messages, maybe an Autocrypt Setup Message */
- mrmailbox_create_or_lookup_nchat_by_contact_id__(ths, MR_CONTACT_ID_SELF, MR_CHAT_NOT_BLOCKED, &chat_id, &chat_id_blocked);
- if( chat_id && chat_id_blocked ) {
- mrmailbox_unblock_chat__(ths, chat_id);
- chat_id_blocked = 0;
- }
- }
- }
-
- if( chat_id == 0 ) {
- chat_id = MR_CHAT_ID_TRASH;
- }
- }
-
- /* correct message_timestamp, it should not be used before,
- however, we cannot do this earlier as we need from_id to be set */
- if( (field=mrmimeparser_lookup_field(mime_parser, "Date"))!=NULL && field->fld_type==MAILIMF_FIELD_ORIG_DATE ) {
- struct mailimf_orig_date* orig_date = field->fld_data.fld_orig_date;
- if( orig_date ) {
- message_timestamp = mr_timestamp_from_date(orig_date->dt_date_time); /* is not yet checked against bad times! */
- }
- }
- message_timestamp = mrmailbox_correct_bad_timestamp__(ths, chat_id, from_id, message_timestamp, (flags&MR_IMAP_SEEN)? 0 : 1 /*fresh message?*/);
-
- /* unarchive chat */
- mrmailbox_unarchive_chat__(ths, chat_id);
-
- /* check, if the mail is already in our database - if so, there's nothing more to do
- (we may get a mail twice eg. if it is moved between folders) */
- if( (field=mrmimeparser_lookup_field(mime_parser, "Message-ID"))!=NULL && field->fld_type==MAILIMF_FIELD_MESSAGE_ID ) {
- struct mailimf_message_id* fld_message_id = field->fld_data.fld_message_id;
- if( fld_message_id ) {
- rfc724_mid = safe_strdup(fld_message_id->mid_value);
- }
- }
-
- if( rfc724_mid == NULL ) {
- /* header is lacking a Message-ID - this may be the case, if the message was sent from this account and the mail client
- the the SMTP-server set the ID (true eg. for the Webmailer used in all-inkl-KAS)
- in these cases, we build a message ID based on some useful header fields that do never change (date, to)
- we do not use the folder-local id, as this will change if the mail is moved to another folder. */
- rfc724_mid = mr_create_incoming_rfc724_mid(message_timestamp, from_id, to_ids);
- if( rfc724_mid == NULL ) {
- mrmailbox_log_info(ths, 0, "Cannot create Message-ID.");
- goto cleanup;
- }
- }
-
- {
- char* old_server_folder = NULL;
- uint32_t old_server_uid = 0;
- if( mrmailbox_rfc724_mid_exists__(ths, rfc724_mid, &old_server_folder, &old_server_uid) ) {
- /* The message is already added to our database; rollback. If needed, update the server_uid which may have changed if the message was moved around on the server. */
- if( strcmp(old_server_folder, server_folder)!=0 || old_server_uid!=server_uid ) {
- mrsqlite3_rollback__(ths->m_sql);
- transaction_pending = 0;
- mrmailbox_update_server_uid__(ths, rfc724_mid, server_folder, server_uid);
- }
- free(old_server_folder);
- mrmailbox_log_info(ths, 0, "Message already in DB.");
- goto cleanup;
- }
- }
-
- /* if the message is not sent by a messenger, check if it is sent at least as a reply to a messenger message
- (later, we move these replies to the Chats-folder) */
- int msgrmsg = mime_parser->m_is_send_by_messenger; /* 1 or 0 for yes/no */
- if( msgrmsg )
- {
- mrmailbox_log_info(ths, 0, "Message sent by another messenger (will be moved to Chats-folder).");
- }
- else
- {
- if( mrmailbox_is_reply_to_messenger_message__(ths, mime_parser) )
- {
- mrmailbox_log_info(ths, 0, "Message is a reply to a messenger message (will be moved to Chats-folder).");
- msgrmsg = 2; /* 2=no, but is reply to messenger message */
- }
- }
-
- /* fine, so far. now, split the message into simple parts usable as "short messages"
- and add them to the database (mails sent by other messenger clients should result
- into only one message; mails sent by other clients may result in several messages (eg. one per attachment)) */
- icnt = carray_count(mime_parser->m_parts); /* should be at least one - maybe empty - part */
- for( i = 0; i < icnt; i++ )
- {
- mrmimepart_t* part = (mrmimepart_t*)carray_get(mime_parser->m_parts, i);
- if( part->m_is_meta ) {
- continue;
- }
-
- if( part->m_type == MR_MSG_TEXT ) {
- txt_raw = mr_mprintf("%s\n\n%s", mime_parser->m_subject? mime_parser->m_subject : "", part->m_msg_raw);
- }
-
- if( mime_parser->m_is_system_message ) {
- mrparam_set_int(part->m_param, MRP_SYSTEM_CMD, mime_parser->m_is_system_message);
- }
-
- stmt = mrsqlite3_predefine__(ths->m_sql, INSERT_INTO_msgs_msscftttsmttpb,
- "INSERT INTO msgs (rfc724_mid,server_folder,server_uid,chat_id,from_id, to_id,timestamp,type, state,msgrmsg,txt,txt_raw,param,bytes)"
- " VALUES (?,?,?,?,?, ?,?,?, ?,?,?,?,?,?);");
- sqlite3_bind_text (stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
- sqlite3_bind_text (stmt, 2, server_folder, -1, SQLITE_STATIC);
- sqlite3_bind_int (stmt, 3, server_uid);
- sqlite3_bind_int (stmt, 4, chat_id);
- sqlite3_bind_int (stmt, 5, from_id);
- sqlite3_bind_int (stmt, 6, to_id);
- sqlite3_bind_int64(stmt, 7, message_timestamp);
- sqlite3_bind_int (stmt, 8, part->m_type);
- sqlite3_bind_int (stmt, 9, state);
- sqlite3_bind_int (stmt, 10, msgrmsg);
- sqlite3_bind_text (stmt, 11, part->m_msg? part->m_msg : "", -1, SQLITE_STATIC);
- sqlite3_bind_text (stmt, 12, txt_raw? txt_raw : "", -1, SQLITE_STATIC);
- sqlite3_bind_text (stmt, 13, part->m_param->m_packed, -1, SQLITE_STATIC);
- sqlite3_bind_int (stmt, 14, part->m_bytes);
- if( sqlite3_step(stmt) != SQLITE_DONE ) {
- mrmailbox_log_info(ths, 0, "Cannot write DB.");
- goto cleanup; /* i/o error - there is nothing more we can do - in other cases, we try to write at least an empty record */
- }
-
- free(txt_raw);
- txt_raw = NULL;
-
- if( first_dblocal_id == 0 ) {
- first_dblocal_id = sqlite3_last_insert_rowid(ths->m_sql->m_cobj);
- }
-
- carray_add(created_db_entries, (void*)(uintptr_t)chat_id, NULL);
- carray_add(created_db_entries, (void*)(uintptr_t)first_dblocal_id, NULL);
- }
-
- mrmailbox_log_info(ths, 0, "Message has %i parts and is moved to chat #%i.", icnt, chat_id);
-
- /* check event to send */
- if( chat_id == MR_CHAT_ID_TRASH )
- {
- create_event_to_send = 0;
- }
- else if( incoming && state==MR_STATE_IN_FRESH )
- {
- if( from_id_blocked ) {
- create_event_to_send = 0;
- }
- else if( chat_id_blocked ) {
- create_event_to_send = MR_EVENT_MSGS_CHANGED;
- /*if( mrsqlite3_get_config_int__(ths->m_sql, "show_deaddrop", 0)!=0 ) {
- create_event_to_send = MR_EVENT_INCOMING_MSG;
- }*/
- }
- else {
- create_event_to_send = MR_EVENT_INCOMING_MSG;
- }
- }
- }
-
-
- if( carray_count(mime_parser->m_reports) > 0 )
- {
- /******************************************************************
- * Handle reports (mainly MDNs)
- *****************************************************************/
-
- int mdns_enabled = mrsqlite3_get_config_int__(ths->m_sql, "mdns_enabled", MR_MDNS_DEFAULT_ENABLED);
- icnt = carray_count(mime_parser->m_reports);
- for( i = 0; i < icnt; i++ )
- {
- int mdn_consumed = 0;
- struct mailmime* report_root = carray_get(mime_parser->m_reports, i);
- struct mailmime_parameter* report_type = mailmime_find_ct_parameter(report_root, "report-type");
- if( report_root==NULL || report_type==NULL || report_type->pa_value==NULL ) {
- continue;
- }
-
- if( strcmp(report_type->pa_value, "disposition-notification") == 0
- && clist_count(report_root->mm_data.mm_multipart.mm_mp_list) >= 2 /* the first part is for humans, the second for machines */ )
- {
- if( mdns_enabled /*to get a clear functionality, do not show incoming MDNs if the options is disabled*/ )
- {
- struct mailmime* report_data = (struct mailmime*)clist_content(clist_next(clist_begin(report_root->mm_data.mm_multipart.mm_mp_list)));
- if( report_data
- && report_data->mm_content_type->ct_type->tp_type==MAILMIME_TYPE_COMPOSITE_TYPE
- && report_data->mm_content_type->ct_type->tp_data.tp_composite_type->ct_type==MAILMIME_COMPOSITE_TYPE_MESSAGE
- && strcmp(report_data->mm_content_type->ct_subtype, "disposition-notification")==0 )
- {
- /* we received a MDN (although the MDN is only a header, we parse it as a complete mail) */
- const char* report_body = NULL;
- size_t report_body_bytes = 0;
- char* to_mmap_string_unref = NULL;
- if( mailmime_transfer_decode(report_data, &report_body, &report_body_bytes, &to_mmap_string_unref) )
- {
- struct mailmime* report_parsed = NULL;
- size_t dummy = 0;
- if( mailmime_parse(report_body, report_body_bytes, &dummy, &report_parsed)==MAIL_NO_ERROR
- && report_parsed!=NULL )
- {
- struct mailimf_fields* report_fields = mailmime_find_mailimf_fields(report_parsed);
- if( report_fields )
- {
- struct mailimf_optional_field* of_disposition = mailimf_find_optional_field(report_fields, "Disposition"); /* MUST be preset, _if_ preset, we assume a sort of attribution and do not go into details */
- struct mailimf_optional_field* of_org_msgid = mailimf_find_optional_field(report_fields, "Original-Message-ID"); /* can't live without */
- if( of_disposition && of_disposition->fld_value && of_org_msgid && of_org_msgid->fld_value )
- {
- char* rfc724_mid = NULL;
- dummy = 0;
- if( mailimf_msg_id_parse(of_org_msgid->fld_value, strlen(of_org_msgid->fld_value), &dummy, &rfc724_mid)==MAIL_NO_ERROR
- && rfc724_mid!=NULL )
- {
- uint32_t chat_id = 0;
- uint32_t msg_id = 0;
- if( mrmailbox_mdn_from_ext__(ths, from_id, rfc724_mid, &chat_id, &msg_id) ) {
- carray_add(rr_event_to_send, (void*)(uintptr_t)chat_id, NULL);
- carray_add(rr_event_to_send, (void*)(uintptr_t)msg_id, NULL);
- }
- mdn_consumed = (msg_id!=0);
- free(rfc724_mid);
- }
- }
- }
- mailmime_free(report_parsed);
- }
-
- if( to_mmap_string_unref ) { mmap_string_unref(to_mmap_string_unref); }
- }
- }
- }
-
- /* Move the MDN away to the chats folder. We do this for:
- - Consumed or not consumed MDNs from other messengers
- - Consumed MDNs from normal MUAs
- Unconsumed MDNs from normal MUAs are _not_ moved.
- NB: we do not delete the MDN as it may be used by other clients
-
- CAVE: we rely on mrimap_markseen_msg() not to move messages that are already in the correct folder.
- otherwise, the moved message get a new server_uid and is "fresh" again and we will be here again to move it away -
- a classical deadlock, see also (***) in mrimap.c */
- if( mime_parser->m_is_send_by_messenger || mdn_consumed ) {
- char* jobparam = mr_mprintf("%c=%s\n%c=%lu", MRP_SERVER_FOLDER, server_folder, MRP_SERVER_UID, server_uid);
- mrjob_add__(ths, MRJ_MARKSEEN_MDN_ON_IMAP, 0, jobparam);
- free(jobparam);
- }
- }
-
- } /* for() */
-
- }
-
- /* debug print? */
- if( mrsqlite3_get_config_int__(ths->m_sql, "save_eml", 0) ) {
- char* emlname = mr_mprintf("%s/%s-%i.eml", ths->m_blobdir, server_folder, (int)first_dblocal_id /*may be 0 for MDNs*/);
- FILE* emlfileob = fopen(emlname, "w");
- if( emlfileob ) {
- fwrite(imf_raw_not_terminated, 1, imf_raw_bytes, emlfileob);
- fclose(emlfileob);
- }
- free(emlname);
- }
-
- /* end sql-transaction */
- mrsqlite3_commit__(ths->m_sql);
- transaction_pending = 0;
-
- /* done */
-cleanup:
- if( transaction_pending ) {
- mrsqlite3_rollback__(ths->m_sql);
- }
-
- if( db_locked ) {
- mrsqlite3_unlock(ths->m_sql);
- }
-
- if( mime_parser ) {
- mrmimeparser_unref(mime_parser);
- }
-
- if( rfc724_mid ) {
- free(rfc724_mid);
- }
-
- if( to_ids ) {
- mrarray_unref(to_ids);
- }
-
- if( created_db_entries ) {
- if( create_event_to_send ) {
- size_t i, icnt = carray_count(created_db_entries);
- for( i = 0; i < icnt; i += 2 ) {
- ths->m_cb(ths, create_event_to_send, (uintptr_t)carray_get(created_db_entries, i), (uintptr_t)carray_get(created_db_entries, i+1));
- }
- }
- carray_free(created_db_entries);
- }
-
- if( rr_event_to_send ) {
- size_t i, icnt = carray_count(rr_event_to_send);
- for( i = 0; i < icnt; i += 2 ) {
- ths->m_cb(ths, MR_EVENT_MSG_READ, (uintptr_t)carray_get(rr_event_to_send, i), (uintptr_t)carray_get(rr_event_to_send, i+1));
- }
- carray_free(rr_event_to_send);
- }
-
- free(txt_raw);
-}
-
-
/*******************************************************************************
* Main interface
******************************************************************************/
@@ -889,7 +64,7 @@ static void cb_set_config(mrimap_t* imap, const char* key, const char* value)
static void cb_receive_imf(mrimap_t* imap, const char* imf_raw_not_terminated, size_t imf_raw_bytes, const char* server_folder, uint32_t server_uid, uint32_t flags)
{
mrmailbox_t* mailbox = (mrmailbox_t*)imap->m_userData;
- receive_imf(mailbox, imf_raw_not_terminated, imf_raw_bytes, server_folder, server_uid, flags);
+ mrmailbox_receive_imf(mailbox, imf_raw_not_terminated, imf_raw_bytes, server_folder, server_uid, flags);
}
@@ -1188,31 +363,6 @@ char* mrmailbox_get_blobdir(mrmailbox_t* mailbox)
}
-int mrmailbox_poke_eml_file(mrmailbox_t* ths, const char* filename)
-{
- /* mainly for testing, may be called by mrmailbox_import_spec() */
- int success = 0;
- char* data = NULL;
- size_t data_bytes;
-
- if( ths == NULL || ths->m_magic != MR_MAILBOX_MAGIC ) {
- return 0;
- }
-
- if( mr_read_file(filename, (void**)&data, &data_bytes, ths) == 0 ) {
- goto cleanup;
- }
-
- receive_imf(ths, data, data_bytes, "import", 0, 0); /* this static function is the reason why this function is not moved to mrmailbox_imex.c */
- success = 1;
-
-cleanup:
- free(data);
-
- return success;
-}
-
-
/*******************************************************************************
* INI-handling, Information
******************************************************************************/
@@ -1496,86 +646,6 @@ int mrmailbox_get_archived_count__(mrmailbox_t* mailbox)
}
-/**
- * Reset database tables. This function is called from Core cmdline.
- *
- * Argument is a bitmask, executing single or multiple actions in one call.
- *
- * e.g. bitmask 7 triggers actions definded with bits 1, 2 and 4.
- */
-int mrmailbox_reset_tables(mrmailbox_t* ths, int bits)
-{
- if( ths == NULL || ths->m_magic != MR_MAILBOX_MAGIC ) {
- return 0;
- }
-
- mrmailbox_log_info(ths, 0, "Resetting tables (%i)...", bits);
-
- mrsqlite3_lock(ths->m_sql);
-
- if( bits & 1 ) {
- mrsqlite3_execute__(ths->m_sql, "DELETE FROM jobs;");
- mrmailbox_log_info(ths, 0, "(1) Jobs reset.");
- }
-
- if( bits & 2 ) {
- mrsqlite3_execute__(ths->m_sql, "DELETE FROM acpeerstates;");
- mrmailbox_log_info(ths, 0, "(2) Peerstates reset.");
- }
-
- if( bits & 4 ) {
- mrsqlite3_execute__(ths->m_sql, "DELETE FROM keypairs;");
- mrmailbox_log_info(ths, 0, "(4) Private keypairs reset.");
- }
-
- if( bits & 8 ) {
- mrsqlite3_execute__(ths->m_sql, "DELETE FROM contacts WHERE id>" MR_STRINGIFY(MR_CONTACT_ID_LAST_SPECIAL) ";"); /* the other IDs are reserved - leave these rows to make sure, the IDs are not used by normal contacts*/
- mrsqlite3_execute__(ths->m_sql, "DELETE FROM chats WHERE id>" MR_STRINGIFY(MR_CHAT_ID_LAST_SPECIAL) ";");
- mrsqlite3_execute__(ths->m_sql, "DELETE FROM chats_contacts;");
- mrsqlite3_execute__(ths->m_sql, "DELETE FROM msgs WHERE id>" MR_STRINGIFY(MR_MSG_ID_LAST_SPECIAL) ";");
- mrsqlite3_execute__(ths->m_sql, "DELETE FROM config WHERE keyname LIKE 'imap.%' OR keyname LIKE 'configured%';");
- mrsqlite3_execute__(ths->m_sql, "DELETE FROM leftgrps;");
- mrmailbox_log_info(ths, 0, "(8) Rest but server config reset.");
- }
-
- update_config_cache__(ths, NULL);
-
- mrsqlite3_unlock(ths->m_sql);
-
- ths->m_cb(ths, MR_EVENT_MSGS_CHANGED, 0, 0);
-
- return 1;
-}
-
-/**
- * Clean up the contacts table. This function is called from Core cmdline.
- *
- * All contacts not involved in a chat, not blocked and not being a deaddrop
- * are removed.
- *
- * Deleted contacts from the OS address book normally stay in the contacts
- * database. With this cleanup, they are also removed, as well as all
- * auto-added contacts, unless they are used in a chat or for blocking purpose.
- *
- */
-int mrmailbox_cleanup_contacts(mrmailbox_t* ths)
-{
- if( ths == NULL || ths->m_magic != MR_MAILBOX_MAGIC ) {
- return 0;
- }
-
- mrmailbox_log_info(ths, 0, "Cleaning up contacts ...");
-
- mrsqlite3_lock(ths->m_sql);
-
- mrsqlite3_execute__(ths->m_sql, "DELETE FROM contacts WHERE id>" MR_STRINGIFY(MR_CONTACT_ID_LAST_SPECIAL) " AND blocked=0 AND NOT EXISTS (SELECT contact_id FROM chats_contacts where contacts.id = chats_contacts.contact_id) AND NOT EXISTS (select from_id from msgs WHERE msgs.from_id = contacts.id);");
-
- mrsqlite3_unlock(ths->m_sql);
-
- return 1;
-}
-
-
/**
* Find out the version of the Delta Chat core library.
*
@@ -2217,7 +1287,6 @@ cleanup:
}
-
/**
* Returns the message IDs of all _fresh_ messages of any chat. Typically used for implementing
* notification summaries. The result must be free()'d.
@@ -2575,11 +1644,6 @@ int mrchat_set_draft(mrchat_t* chat, const char* msg) /* deprecated */
}
-
-#define IS_SELF_IN_GROUP__ (mrmailbox_is_contact_in_chat__(mailbox, chat_id, MR_CONTACT_ID_SELF)==1)
-#define DO_SEND_STATUS_MAILS (mrparam_get_int(chat->m_param, MRP_UNPROMOTED, 0)==0)
-
-
int mrmailbox_get_fresh_msg_count__(mrmailbox_t* mailbox, uint32_t chat_id)
{
sqlite3_stmt* stmt = NULL;
@@ -2744,19 +1808,10 @@ void mrmailbox_create_or_lookup_nchat_by_contact_id__(mrmailbox_t* mailbox, uint
sqlite3_finalize(stmt);
stmt = NULL;
- /* cleanup */
cleanup:
- if( q ) {
- sqlite3_free(q);
- }
-
- if( stmt ) {
- sqlite3_finalize(stmt);
- }
-
- if( contact ) {
- mrcontact_unref(contact);
- }
+ if( q ) { sqlite3_free(q); }
+ if( stmt ) { sqlite3_finalize(stmt); }
+ if( contact ) { mrcontact_unref(contact); }
if( ret_chat_id ) { *ret_chat_id = chat_id; }
if( ret_chat_blocked ) { *ret_chat_blocked = create_blocked; }
@@ -2874,20 +1929,12 @@ void mrmailbox_archive_chat(mrmailbox_t* mailbox, uint32_t chat_id, int archive)
******************************************************************************/
-/* _If_ deleting a group chat would implies to leave the group, things get complicated
-as this would require to send a message before the chat is deleted physically.
-To make things even more complicated, there may be other chat messages waiting to be send.
-We used the following approach:
-1. If we do not need to send a message, we delete the chat directly
-2. If we need to send a message, we set chats.blocked=1 and add the parameter
- MRP_DEL_AFTER_SEND with a random value to both, the last message to be sent and to the
- chat (we would use msg_id, however, we may not get this in time)
-3. When the message with the MRP_DEL_AFTER_SEND-value of the chat was sent to IMAP, we physically
- delete the chat.
-However, from 2017-11-02, we do not implicitly leave the group as this results in different behaviours to normal
-chat and _only_ leaving a group is also a valid usecase. */
+
+
+#define IS_SELF_IN_GROUP__ (mrmailbox_is_contact_in_chat__(mailbox, chat_id, MR_CONTACT_ID_SELF)==1)
+#define DO_SEND_STATUS_MAILS (mrparam_get_int(chat->m_param, MRP_UNPROMOTED, 0)==0)
int mrmailbox_delete_chat_part2(mrmailbox_t* mailbox, uint32_t chat_id)
diff --git a/src/mrmailbox_receive_imf.c b/src/mrmailbox_receive_imf.c
new file mode 100644
index 00000000..db025536
--- /dev/null
+++ b/src/mrmailbox_receive_imf.c
@@ -0,0 +1,1119 @@
+/*******************************************************************************
+ *
+ * Delta Chat Core
+ * Copyright (C) 2017 Björn Petersen
+ * Contact: r10s@b44t.com, http://b44t.com
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see http://www.gnu.org/licenses/ .
+ *
+ ******************************************************************************/
+
+
+#include "mrmailbox_internal.h"
+#include "mrmimeparser.h"
+#include "mrmimefactory.h"
+#include "mrimap.h"
+#include "mrjob.h"
+
+
+/*******************************************************************************
+ * Add contacts to database on receiving messages
+ ******************************************************************************/
+
+
+static void add_or_lookup_contact_by_addr__(mrmailbox_t* mailbox, const char* display_name_enc, const char* addr_spec, int origin, mrarray_t* ids, int* check_self)
+{
+ /* is addr_spec equal to SELF? */
+ int dummy;
+ if( check_self == NULL ) { check_self = &dummy; }
+
+ *check_self = 0;
+
+ char* self_addr = mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", "");
+ if( strcasecmp(self_addr, addr_spec)==0 ) {
+ *check_self = 1;
+ }
+ free(self_addr);
+
+ if( *check_self ) {
+ return;
+ }
+
+ /* add addr_spec if missing, update otherwise */
+ char* display_name_dec = NULL;
+ if( display_name_enc ) {
+ display_name_dec = mr_decode_header_string(display_name_enc);
+ mr_normalize_name(display_name_dec);
+ }
+
+ uint32_t row_id = mrmailbox_add_or_lookup_contact__(mailbox, display_name_dec /*can be NULL*/, addr_spec, origin, NULL);
+
+ free(display_name_dec);
+
+ if( row_id ) {
+ if( !mrarray_search_id(ids, row_id, NULL) ) {
+ mrarray_add_id(ids, row_id);
+ }
+ }
+}
+
+
+static void mrmailbox_add_or_lookup_contacts_by_mailbox_list__(mrmailbox_t* mailbox, struct mailimf_mailbox_list* mb_list, int origin, mrarray_t* ids, int* check_self)
+{
+ clistiter* cur;
+ for( cur = clist_begin(mb_list->mb_list); cur!=NULL ; cur=clist_next(cur) ) {
+ struct mailimf_mailbox* mb = (struct mailimf_mailbox*)clist_content(cur);
+ if( mb ) {
+ add_or_lookup_contact_by_addr__(mailbox, mb->mb_display_name, mb->mb_addr_spec, origin, ids, check_self);
+ }
+ }
+}
+
+
+static void mrmailbox_add_or_lookup_contacts_by_address_list__(mrmailbox_t* mailbox, struct mailimf_address_list* adr_list, int origin, mrarray_t* ids, int* check_self)
+{
+ clistiter* cur;
+ for( cur = clist_begin(adr_list->ad_list); cur!=NULL ; cur=clist_next(cur) ) {
+ struct mailimf_address* adr = (struct mailimf_address*)clist_content(cur);
+ if( adr ) {
+ if( adr->ad_type == MAILIMF_ADDRESS_MAILBOX ) {
+ struct mailimf_mailbox* mb = adr->ad_data.ad_mailbox; /* can be NULL */
+ if( mb ) {
+ add_or_lookup_contact_by_addr__(mailbox, mb->mb_display_name, mb->mb_addr_spec, origin, ids, check_self);
+ }
+ }
+ else if( adr->ad_type == MAILIMF_ADDRESS_GROUP ) {
+ struct mailimf_group* group = adr->ad_data.ad_group; /* can be NULL */
+ if( group && group->grp_mb_list /*can be NULL*/ ) {
+ mrmailbox_add_or_lookup_contacts_by_mailbox_list__(mailbox, group->grp_mb_list, origin, ids, check_self);
+ }
+ }
+ }
+ }
+}
+
+
+/*******************************************************************************
+ * Check if a message is a reply to a known message (messenger or non-messenger)
+ ******************************************************************************/
+
+
+static int is_known_rfc724_mid__(mrmailbox_t* mailbox, const char* rfc724_mid)
+{
+ if( rfc724_mid ) {
+ sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_msgs_WHERE_cm,
+ "SELECT m.id FROM msgs m "
+ " LEFT JOIN chats c ON m.chat_id=c.id "
+ " WHERE m.rfc724_mid=? "
+ " AND m.chat_id>" MR_STRINGIFY(MR_CHAT_ID_LAST_SPECIAL)
+ " AND c.blocked=0;");
+ sqlite3_bind_text(stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
+ if( sqlite3_step(stmt) == SQLITE_ROW ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static int is_known_rfc724_mid_in_list__(mrmailbox_t* mailbox, const clist* mid_list)
+{
+ if( mid_list ) {
+ clistiter* cur;
+ for( cur = clist_begin(mid_list); cur!=NULL ; cur=clist_next(cur) ) {
+ if( is_known_rfc724_mid__(mailbox, clist_content(cur)) ) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int mrmailbox_is_reply_to_known_message__(mrmailbox_t* mailbox, mrmimeparser_t* mime_parser)
+{
+ /* check if the message is a reply to a known message; the replies are identified by the Message-ID from
+ `In-Reply-To`/`References:` (to support non-Delta-Clients) or from `Chat-Predecessor:` (Delta clients, see comment in mrchat.c) */
+
+ struct mailimf_optional_field* optional_field;
+ if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Predecessor", "X-MrPredecessor")) != NULL )
+ {
+ if( is_known_rfc724_mid__(mailbox, optional_field->fld_value) ) {
+ return 1;
+ }
+ }
+
+ struct mailimf_field* field;
+ if( (field=mrmimeparser_lookup_field(mime_parser, "In-Reply-To"))!=NULL
+ && field->fld_type == MAILIMF_FIELD_IN_REPLY_TO )
+ {
+ struct mailimf_in_reply_to* fld_in_reply_to = field->fld_data.fld_in_reply_to;
+ if( fld_in_reply_to ) {
+ if( is_known_rfc724_mid_in_list__(mailbox, field->fld_data.fld_in_reply_to->mid_list) ) {
+ return 1;
+ }
+ }
+ }
+
+ if( (field=mrmimeparser_lookup_field(mime_parser, "References"))!=NULL
+ && field->fld_type == MAILIMF_FIELD_REFERENCES )
+ {
+ struct mailimf_references* fld_references = field->fld_data.fld_references;
+ if( fld_references ) {
+ if( is_known_rfc724_mid_in_list__(mailbox, field->fld_data.fld_references->mid_list) ) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*******************************************************************************
+ * Check if a message is a reply to any messenger message
+ ******************************************************************************/
+
+
+static int is_msgrmsg_rfc724_mid__(mrmailbox_t* mailbox, const char* rfc724_mid)
+{
+ if( rfc724_mid ) {
+ sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_msgs_WHERE_mcm,
+ "SELECT id FROM msgs "
+ " WHERE rfc724_mid=? "
+ " AND msgrmsg!=0 "
+ " AND chat_id>" MR_STRINGIFY(MR_CHAT_ID_LAST_SPECIAL) ";");
+ sqlite3_bind_text(stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
+ if( sqlite3_step(stmt) == SQLITE_ROW ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static int is_msgrmsg_rfc724_mid_in_list__(mrmailbox_t* mailbox, const clist* mid_list)
+{
+ if( mid_list ) {
+ clistiter* cur;
+ for( cur = clist_begin(mid_list); cur!=NULL ; cur=clist_next(cur) ) {
+ if( is_msgrmsg_rfc724_mid__(mailbox, clist_content(cur)) ) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int mrmailbox_is_reply_to_messenger_message__(mrmailbox_t* mailbox, mrmimeparser_t* mime_parser)
+{
+ /* function checks, if the message defined by mime_parser references a message send by us from Delta Chat.
+
+ This is similar to is_reply_to_known_message__() but
+ - checks also if any of the referenced IDs are send by a messenger
+ - it is okay, if the referenced messages are moved to trash here
+ - no check for the Chat-* headers (function is only called if it is no messenger message itself) */
+
+ struct mailimf_field* field;
+ if( (field=mrmimeparser_lookup_field(mime_parser, "In-Reply-To"))!=NULL
+ && field->fld_type==MAILIMF_FIELD_IN_REPLY_TO )
+ {
+ struct mailimf_in_reply_to* fld_in_reply_to = field->fld_data.fld_in_reply_to;
+ if( fld_in_reply_to ) {
+ if( is_msgrmsg_rfc724_mid_in_list__(mailbox, field->fld_data.fld_in_reply_to->mid_list) ) {
+ return 1;
+ }
+ }
+ }
+
+ if( (field=mrmimeparser_lookup_field(mime_parser, "References"))!=NULL
+ && field->fld_type==MAILIMF_FIELD_REFERENCES )
+ {
+ struct mailimf_references* fld_references = field->fld_data.fld_references;
+ if( fld_references ) {
+ if( is_msgrmsg_rfc724_mid_in_list__(mailbox, field->fld_data.fld_references->mid_list) ) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*******************************************************************************
+ * Misc. Tools
+ ******************************************************************************/
+
+
+static time_t mrmailbox_correct_bad_timestamp__(mrmailbox_t* mailbox, uint32_t chat_id, uint32_t from_id, time_t desired_timestamp, int is_fresh_msg)
+{
+ /* used for correcting timestamps of _received_ messages.
+ use the last message from another user (including SELF) as the MINIMUM
+ (we do this check only for fresh messages, other messages may pop up whereever, this may happen eg. when restoring old messages or synchronizing different clients) */
+ if( is_fresh_msg )
+ {
+ sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_timestamp_FROM_msgs_WHERE_timestamp,
+ "SELECT MAX(timestamp) FROM msgs WHERE chat_id=? and from_id!=? AND timestamp>=?");
+ sqlite3_bind_int (stmt, 1, chat_id);
+ sqlite3_bind_int (stmt, 2, from_id);
+ sqlite3_bind_int64(stmt, 3, desired_timestamp);
+ if( sqlite3_step(stmt)==SQLITE_ROW )
+ {
+ time_t last_msg_time = sqlite3_column_int64(stmt, 0);
+ if( last_msg_time > 0 /* may happen as we do not check against sqlite3_column_type()!=SQLITE_NULL */ ) {
+ if( desired_timestamp <= last_msg_time ) {
+ desired_timestamp = last_msg_time+1; /* this may result in several incoming messages having the same
+ one-second-after-the-last-other-message-timestamp. however, this is no big deal
+ as we do not try to recrete the order of bad-date-messages and as we always order by ID as second criterion */
+ }
+ }
+ }
+ }
+
+ /* use the (smeared) current time as the MAXIMUM */
+ if( desired_timestamp >= mr_smeared_time__() )
+ {
+ desired_timestamp = mr_create_smeared_timestamp__();
+ }
+
+ return desired_timestamp;
+}
+
+
+/*******************************************************************************
+ * Handle groups for received messages
+ ******************************************************************************/
+
+
+/* the function tries extracts the group-id from the message and returns the
+corresponding chat_id. If the chat_id is not existant, it is created.
+If the message contains groups commands (name, profile image, changed members),
+they are executed as well.
+
+So when the function returns, the caller has the group id matching the current
+state of the group. */
+static void create_or_lookup_group__(mrmailbox_t* mailbox, mrmimeparser_t* mime_parser, int create_blocked,
+ int32_t from_id, mrarray_t* to_ids,
+ uint32_t* ret_chat_id, int* ret_chat_blocked)
+{
+ uint32_t chat_id = 0;
+ int chat_blocked = 0;
+ char* grpid = NULL;
+ char* grpname = NULL;
+ sqlite3_stmt* stmt;
+ int i, to_ids_cnt = mrarray_get_cnt(to_ids);
+ char* self_addr = NULL;
+ int recreate_member_list = 0;
+ int send_EVENT_CHAT_MODIFIED = 0;
+
+ char* X_MrRemoveFromGrp = NULL; /* pointer somewhere into mime_parser, must not be freed */
+ char* X_MrAddToGrp = NULL; /* pointer somewhere into mime_parser, must not be freed */
+ int X_MrGrpNameChanged = 0;
+ int X_MrGrpImageChanged = 0;
+
+ /* search the grpid in the header */
+ {
+ struct mailimf_field* field = NULL;
+ struct mailimf_optional_field* optional_field = NULL;
+
+ if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Group-ID", "X-MrGrpId"))!=NULL ) {
+ grpid = safe_strdup(optional_field->fld_value);
+ }
+
+ if( grpid == NULL )
+ {
+ if( (field=mrmimeparser_lookup_field(mime_parser, "Message-ID"))!=NULL && field->fld_type==MAILIMF_FIELD_MESSAGE_ID ) {
+ struct mailimf_message_id* fld_message_id = field->fld_data.fld_message_id;
+ if( fld_message_id ) {
+ grpid = mr_extract_grpid_from_rfc724_mid(fld_message_id->mid_value);
+ }
+ }
+
+ if( grpid == NULL )
+ {
+ if( (field=mrmimeparser_lookup_field(mime_parser, "In-Reply-To"))!=NULL && field->fld_type==MAILIMF_FIELD_IN_REPLY_TO ) {
+ struct mailimf_in_reply_to* fld_in_reply_to = field->fld_data.fld_in_reply_to;
+ if( fld_in_reply_to ) {
+ grpid = mr_extract_grpid_from_rfc724_mid_list(fld_in_reply_to->mid_list);
+ }
+ }
+
+ if( grpid == NULL )
+ {
+ if( (field=mrmimeparser_lookup_field(mime_parser, "References"))!=NULL && field->fld_type==MAILIMF_FIELD_REFERENCES ) {
+ struct mailimf_references* fld_references = field->fld_data.fld_references;
+ if( fld_references ) {
+ grpid = mr_extract_grpid_from_rfc724_mid_list(fld_references->mid_list);
+ }
+ }
+
+ if( grpid == NULL )
+ {
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Group-Name", "X-MrGrpName"))!=NULL ) {
+ grpname = mr_decode_header_string(optional_field->fld_value); /* this is no changed groupname message */
+ }
+
+ if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Group-Member-Removed", "X-MrRemoveFromGrp"))!=NULL ) {
+ X_MrRemoveFromGrp = optional_field->fld_value;
+ mime_parser->m_is_system_message = MR_SYSTEM_MEMBER_REMOVED_FROM_GROUP;
+ }
+ else if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Group-Member-Added", "X-MrAddToGrp"))!=NULL ) {
+ X_MrAddToGrp = optional_field->fld_value;
+ mime_parser->m_is_system_message = MR_SYSTEM_MEMBER_ADDED_TO_GROUP;
+ }
+ else if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Group-Name-Changed", "X-MrGrpNameChanged"))!=NULL ) {
+ X_MrGrpNameChanged = 1;
+ mime_parser->m_is_system_message = MR_SYSTEM_GROUPNAME_CHANGED;
+ }
+ else if( (optional_field=mrmimeparser_lookup_optional_field(mime_parser, "Chat-Group-Image"))!=NULL ) {
+ X_MrGrpImageChanged = 1;
+ mime_parser->m_is_system_message = MR_SYSTEM_GROUPIMAGE_CHANGED;
+ }
+ }
+
+ /* check, if we have a chat with this group ID */
+ stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_CHATS_WHERE_grpid,
+ "SELECT id, blocked FROM chats WHERE grpid=?;");
+ sqlite3_bind_text (stmt, 1, grpid, -1, SQLITE_STATIC);
+ if( sqlite3_step(stmt)==SQLITE_ROW ) {
+ chat_id = sqlite3_column_int(stmt, 0);
+ chat_blocked = sqlite3_column_int(stmt, 1);
+ }
+
+ /* check if the sender is a member of the existing group -
+ if not, the message does not go to the group chat but to the normal chat with the sender */
+ if( chat_id!=0 && !mrmailbox_is_contact_in_chat__(mailbox, chat_id, from_id) ) {
+ chat_id = 0;
+ goto cleanup;
+ }
+
+ /* check if the group does not exist but should be created */
+ int group_explicitly_left = mrmailbox_group_explicitly_left__(mailbox, grpid);
+
+ self_addr = mrsqlite3_get_config__(mailbox->m_sql, "configured_addr", "");
+ if( chat_id == 0
+ && !mrmimeparser_is_mailinglist_message(mime_parser)
+ && grpname
+ && X_MrRemoveFromGrp==NULL /*otherwise, a pending "quit" message may pop up*/
+ && (!group_explicitly_left || (X_MrAddToGrp&&strcasecmp(self_addr,X_MrAddToGrp)==0) ) /*re-create explicitly left groups only if ourself is re-added*/
+ )
+ {
+ stmt = mrsqlite3_prepare_v2_(mailbox->m_sql,
+ "INSERT INTO chats (type, name, grpid, blocked) VALUES(?, ?, ?, ?);");
+ sqlite3_bind_int (stmt, 1, MR_CHAT_TYPE_GROUP);
+ sqlite3_bind_text(stmt, 2, grpname, -1, SQLITE_STATIC);
+ sqlite3_bind_text(stmt, 3, grpid, -1, SQLITE_STATIC);
+ sqlite3_bind_int (stmt, 4, create_blocked);
+ if( sqlite3_step(stmt)!=SQLITE_DONE ) {
+ goto cleanup;
+ }
+ sqlite3_finalize(stmt);
+ chat_id = sqlite3_last_insert_rowid(mailbox->m_sql->m_cobj);
+ chat_blocked = create_blocked;
+ recreate_member_list = 1;
+ }
+
+ /* again, check chat_id */
+ if( chat_id <= MR_CHAT_ID_LAST_SPECIAL ) {
+ chat_id = 0;
+ if( group_explicitly_left ) {
+ chat_id = MR_CHAT_ID_TRASH; /* we got a message for a chat we've deleted - do not show this even as a normal chat */
+ }
+ goto cleanup;
+ }
+
+ /* execute group commands */
+ if( X_MrAddToGrp || X_MrRemoveFromGrp )
+ {
+ recreate_member_list = 1;
+ }
+ else if( X_MrGrpNameChanged && grpname && strlen(grpname) < 200 )
+ {
+ stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "UPDATE chats SET name=? WHERE id=?;");
+ sqlite3_bind_text(stmt, 1, grpname, -1, SQLITE_STATIC);
+ sqlite3_bind_int (stmt, 2, chat_id);
+ sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+ mailbox->m_cb(mailbox, MR_EVENT_CHAT_MODIFIED, chat_id, 0);
+ }
+
+ if( X_MrGrpImageChanged )
+ {
+ int ok = 0;
+ char* grpimage = NULL;
+ if( carray_count(mime_parser->m_parts)>=1 ) {
+ mrmimepart_t* textpart = (mrmimepart_t*)carray_get(mime_parser->m_parts, 0);
+ if( textpart->m_type == MR_MSG_TEXT ) {
+ if( carray_count(mime_parser->m_parts)>=2 ) {
+ mrmimepart_t* imgpart = (mrmimepart_t*)carray_get(mime_parser->m_parts, 1);
+ if( imgpart->m_type == MR_MSG_IMAGE ) {
+ grpimage = mrparam_get(imgpart->m_param, MRP_FILE, NULL);
+ ok = 1;
+ }
+ }
+ else {
+ ok = 1;
+ }
+ }
+ }
+
+ if( ok ) {
+ mrchat_t* chat = mrchat_new(mailbox);
+ mrmailbox_log_info(mailbox, 0, "New group image set to %s.", grpimage? "DELETED" : grpimage);
+ mrchat_load_from_db__(chat, chat_id);
+ mrparam_set(chat->m_param, MRP_PROFILE_IMAGE, grpimage/*may be NULL*/);
+ mrchat_update_param__(chat);
+ mrchat_unref(chat);
+ free(grpimage);
+ send_EVENT_CHAT_MODIFIED = 1;
+ }
+ }
+
+ /* add members to group/check members
+ for recreation: we should add a timestamp */
+ if( recreate_member_list )
+ {
+ const char* skip = X_MrRemoveFromGrp? X_MrRemoveFromGrp : NULL;
+
+ stmt = mrsqlite3_prepare_v2_(mailbox->m_sql, "DELETE FROM chats_contacts WHERE chat_id=?;");
+ sqlite3_bind_int (stmt, 1, chat_id);
+ sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+
+ if( skip==NULL || strcasecmp(self_addr, skip) != 0 ) {
+ mrmailbox_add_contact_to_chat__(mailbox, chat_id, MR_CONTACT_ID_SELF);
+ }
+
+ if( from_id > MR_CONTACT_ID_LAST_SPECIAL ) {
+ if( mrmailbox_contact_addr_equals__(mailbox, from_id, self_addr)==0
+ && (skip==NULL || mrmailbox_contact_addr_equals__(mailbox, from_id, skip)==0) ) {
+ mrmailbox_add_contact_to_chat__(mailbox, chat_id, from_id);
+ }
+ }
+
+ for( i = 0; i < to_ids_cnt; i++ )
+ {
+ uint32_t to_id = mrarray_get_id(to_ids, i); /* to_id is only once in to_ids and is non-special */
+ if( mrmailbox_contact_addr_equals__(mailbox, to_id, self_addr)==0
+ && (skip==NULL || mrmailbox_contact_addr_equals__(mailbox, to_id, skip)==0) ) {
+ mrmailbox_add_contact_to_chat__(mailbox, chat_id, to_id);
+ }
+ }
+ send_EVENT_CHAT_MODIFIED = 1;
+ }
+
+ if( send_EVENT_CHAT_MODIFIED ) {
+ mailbox->m_cb(mailbox, MR_EVENT_CHAT_MODIFIED, chat_id, 0);
+ }
+
+ /* check the number of receivers -
+ the only critical situation is if the user hits "Reply" instead of "Reply all" in a non-messenger-client */
+ if( to_ids_cnt == 1 && mime_parser->m_is_send_by_messenger==0 ) {
+ int is_contact_cnt = mrmailbox_get_chat_contact_count__(mailbox, chat_id);
+ if( is_contact_cnt > 3 /* to_ids_cnt==1 may be "From: A, To: B, SELF" as SELF is not counted in to_ids_cnt. So everything up to 3 is no error. */ ) {
+ chat_id = 0;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ free(grpid);
+ free(grpname);
+ free(self_addr);
+ if( ret_chat_id ) { *ret_chat_id = chat_id; }
+ if( ret_chat_blocked ) { *ret_chat_blocked = chat_id? chat_blocked : 0; }
+}
+
+
+/*******************************************************************************
+ * Receive a message and add it to the database
+ ******************************************************************************/
+
+
+void mrmailbox_receive_imf(mrmailbox_t* mailbox, const char* imf_raw_not_terminated, size_t imf_raw_bytes,
+ const char* server_folder, uint32_t server_uid, uint32_t flags)
+{
+ /* the function returns the number of created messages in the database */
+ int incoming = 0;
+ int incoming_origin = MR_ORIGIN_UNSET;
+ #define outgoing (!incoming)
+
+ mrarray_t* to_ids = NULL;
+ int to_self = 0;
+
+ uint32_t from_id = 0;
+ int from_id_blocked = 0;
+ uint32_t to_id = 0;
+ uint32_t chat_id = 0;
+ int chat_id_blocked = 0;
+ int state = MR_STATE_UNDEFINED;
+
+ sqlite3_stmt* stmt;
+ size_t i, icnt;
+ uint32_t first_dblocal_id = 0;
+ char* rfc724_mid = NULL; /* Message-ID from the header */
+ time_t message_timestamp = MR_INVALID_TIMESTAMP;
+ mrmimeparser_t* mime_parser = mrmimeparser_new(mailbox->m_blobdir, mailbox);
+ int db_locked = 0;
+ int transaction_pending = 0;
+ const struct mailimf_field* field;
+
+ carray* created_db_entries = carray_new(16);
+ int create_event_to_send = MR_EVENT_MSGS_CHANGED;
+
+ carray* rr_event_to_send = carray_new(16);
+
+ int has_return_path = 0;
+ char* txt_raw = NULL;
+
+ mrmailbox_log_info(mailbox, 0, "Receive message #%lu from %s.", server_uid, server_folder? server_folder:"?");
+
+ to_ids = mrarray_new(mailbox, 16);
+ if( to_ids==NULL || created_db_entries==NULL || rr_event_to_send==NULL || mime_parser == NULL ) {
+ mrmailbox_log_info(mailbox, 0, "Bad param.");
+ goto cleanup;
+ }
+
+ /* parse the imf to mailimf_message {
+ mailimf_fields* msg_fields {
+ clist* fld_list; // list of mailimf_field
+ }
+ mailimf_body* msg_body { // != NULL
+ const char * bd_text; // != NULL
+ size_t bd_size;
+ }
+ };
+ normally, this is done by mailimf_message_parse(), however, as we also need the MIME data,
+ we use mailmime_parse() through MrMimeParser (both call mailimf_struct_multiple_parse() somewhen, I did not found out anything
+ that speaks against this approach yet) */
+ mrmimeparser_parse(mime_parser, imf_raw_not_terminated, imf_raw_bytes);
+ if( mrhash_count(&mime_parser->m_header)==0 ) {
+ mrmailbox_log_info(mailbox, 0, "No header.");
+ goto cleanup; /* Error - even adding an empty record won't help as we do not know the message ID */
+ }
+
+ mrsqlite3_lock(mailbox->m_sql);
+ db_locked = 1;
+
+ mrsqlite3_begin_transaction__(mailbox->m_sql);
+ transaction_pending = 1;
+
+
+ /* Check, if the mail comes from extern, resp. is not sent by us. This is a _really_ important step
+ as messages sent by us are used to validate other mail senders and receivers.
+ For this purpose, we assume, the `Return-Path:`-header is never present if the message is sent by us.
+ The `Received:`-header may be another idea, however, this is also set if mails are transfered from other accounts via IMAP.
+ Using `From:` alone is no good idea, as mailboxes may use different sending-addresses - moreover, they may change over the years.
+ However, we use `From:` as an additional hint below. */
+ if( mrmimeparser_lookup_field(mime_parser, "Return-Path") ) {
+ has_return_path = 1;
+ }
+
+ if( has_return_path ) {
+ incoming = 1;
+ }
+
+ /* for incoming messages, get From: and check if it is known (for known From:'s we add the other To:/Cc:/Bcc: in the 3rd pass) */
+ if( incoming
+ && (field=mrmimeparser_lookup_field(mime_parser, "From"))!=NULL
+ && field->fld_type==MAILIMF_FIELD_FROM)
+ {
+ struct mailimf_from* fld_from = field->fld_data.fld_from;
+ if( fld_from )
+ {
+ int check_self;
+ mrarray_t* from_list = mrarray_new(mailbox, 16);
+ mrmailbox_add_or_lookup_contacts_by_mailbox_list__(mailbox, fld_from->frm_mb_list, MR_ORIGIN_INCOMING_UNKNOWN_FROM, from_list, &check_self);
+ if( check_self )
+ {
+ if( mrmimeparser_sender_equals_recipient(mime_parser) )
+ {
+ from_id = MR_CONTACT_ID_SELF;
+ }
+ else
+ {
+ incoming = 0; /* The `Return-Path:`-approach above works well, however, there may be outgoing messages which we also receive -
+ for these messages, the `Return-Path:` is set although we're the sender. To correct these cases, we add an
+ additional From: check - which, however, will not work for older From:-addresses used on the mailbox. */
+ }
+ }
+ else
+ {
+ if( mrarray_get_cnt(from_list)>=1 ) /* if there is no from given, from_id stays 0 which is just fine. These messages are very rare, however, we have to add them to the database (they go to the "deaddrop" chat) to avoid a re-download from the server. See also [**] */
+ {
+ from_id = mrarray_get_id(from_list, 0);
+ incoming_origin = mrmailbox_get_contact_origin__(mailbox, from_id, &from_id_blocked);
+ }
+ }
+ mrarray_unref(from_list);
+ }
+ }
+
+ /* Make sure, to_ids starts with the first To:-address (Cc: and Bcc: are added in the loop below pass) */
+ if( (field=mrmimeparser_lookup_field(mime_parser, "To"))!=NULL
+ && field->fld_type==MAILIMF_FIELD_TO )
+ {
+ struct mailimf_to* fld_to = field->fld_data.fld_to; /* can be NULL */
+ if( fld_to )
+ {
+ mrmailbox_add_or_lookup_contacts_by_address_list__(mailbox, fld_to->to_addr_list /*!= NULL*/,
+ outgoing? MR_ORIGIN_OUTGOING_TO : (incoming_origin>=MR_ORIGIN_MIN_VERIFIED? MR_ORIGIN_INCOMING_TO : MR_ORIGIN_INCOMING_UNKNOWN_TO), to_ids, &to_self);
+ }
+ }
+
+ if( mrmimeparser_has_nonmeta(mime_parser) )
+ {
+
+ /**********************************************************************
+ * Add parts
+ *********************************************************************/
+
+ /* collect the rest information */
+ if( (field=mrmimeparser_lookup_field(mime_parser, "Cc"))!=NULL && field->fld_type==MAILIMF_FIELD_CC )
+ {
+ struct mailimf_cc* fld_cc = field->fld_data.fld_cc;
+ if( fld_cc ) {
+ mrmailbox_add_or_lookup_contacts_by_address_list__(mailbox, fld_cc->cc_addr_list,
+ outgoing? MR_ORIGIN_OUTGOING_CC : (incoming_origin>=MR_ORIGIN_MIN_VERIFIED? MR_ORIGIN_INCOMING_CC : MR_ORIGIN_INCOMING_UNKNOWN_CC), to_ids, NULL);
+ }
+ }
+
+ if( (field=mrmimeparser_lookup_field(mime_parser, "Bcc"))!=NULL && field->fld_type==MAILIMF_FIELD_BCC )
+ {
+ struct mailimf_bcc* fld_bcc = field->fld_data.fld_bcc;
+ if( outgoing && fld_bcc ) {
+ mrmailbox_add_or_lookup_contacts_by_address_list__(mailbox, fld_bcc->bcc_addr_list,
+ MR_ORIGIN_OUTGOING_BCC, to_ids, NULL);
+ }
+ }
+
+ /* check if the message introduces a new chat:
+ - outgoing messages introduce a chat with the first to: address if they are sent by a messenger
+ - incoming messages introduce a chat only for known contacts if they are sent by a messenger
+ (of course, the user can add other chats manually later) */
+ if( incoming )
+ {
+ state = (flags&MR_IMAP_SEEN)? MR_STATE_IN_SEEN : MR_STATE_IN_FRESH;
+ to_id = MR_CONTACT_ID_SELF;
+
+ /* test if there is a normal chat with the sender - if so, this allows us to create groups in the next step */
+ uint32_t test_normal_chat_id = 0;
+ int test_normal_chat_id_blocked = 0;
+ mrmailbox_lookup_real_nchat_by_contact_id__(mailbox, from_id, &test_normal_chat_id, &test_normal_chat_id_blocked);
+
+ /* get the chat_id - a chat_id here is no indicator that the chat is displayed in the normal list, it might also be
+ blocked and displayed in the deaddrop as a result */
+ if( chat_id == 0 )
+ {
+ /* try to create a group */
+ int create_blocked = ((test_normal_chat_id&&test_normal_chat_id_blocked==MR_CHAT_NOT_BLOCKED) || incoming_origin>=MR_ORIGIN_MIN_START_NEW_NCHAT/*always false, for now*/)? MR_CHAT_NOT_BLOCKED : MR_CHAT_DEADDROP_BLOCKED;
+ create_or_lookup_group__(mailbox, mime_parser, create_blocked, from_id, to_ids, &chat_id, &chat_id_blocked);
+ if( chat_id && chat_id_blocked && !create_blocked ) {
+ mrmailbox_unblock_chat__(mailbox, chat_id);
+ chat_id_blocked = 0;
+ }
+ }
+
+ if( chat_id == 0 )
+ {
+ /* check if the message belongs to a mailing list */
+ if( mrmimeparser_is_mailinglist_message(mime_parser) ) {
+ chat_id = MR_CHAT_ID_TRASH;
+ mrmailbox_log_info(mailbox, 0, "Message belongs to a mailing list and is ignored.");
+ }
+ }
+
+ if( chat_id == 0 )
+ {
+ /* try to create a normal chat */
+ int create_blocked = (incoming_origin>=MR_ORIGIN_MIN_START_NEW_NCHAT/*always false, for now*/ || from_id==to_id)? MR_CHAT_NOT_BLOCKED : MR_CHAT_DEADDROP_BLOCKED;
+ if( test_normal_chat_id ) {
+ chat_id = test_normal_chat_id;
+ chat_id_blocked = test_normal_chat_id_blocked;
+ }
+ else {
+ mrmailbox_create_or_lookup_nchat_by_contact_id__(mailbox, from_id, create_blocked, &chat_id, &chat_id_blocked);
+ }
+
+ if( chat_id && chat_id_blocked ) {
+ if( !create_blocked ) {
+ mrmailbox_unblock_chat__(mailbox, chat_id);
+ chat_id_blocked = 0;
+ }
+ else if( mrmailbox_is_reply_to_known_message__(mailbox, mime_parser) ) {
+ mrmailbox_scaleup_contact_origin__(mailbox, from_id, MR_ORIGIN_INCOMING_REPLY_TO); /* we do not want any chat to be created implicitly. Because of the origin-scale-up, the contact requests will pop up and this should be just fine. */
+ mrmailbox_log_info(mailbox, 0, "Message is a reply to a known message, mark sender as known.");
+ incoming_origin = MR_MAX(incoming_origin, MR_ORIGIN_INCOMING_REPLY_TO);
+ }
+ }
+ }
+
+ if( chat_id == 0 )
+ {
+ /* maybe from_id is null or sth. else is suspicious, move message to trash */
+ chat_id = MR_CHAT_ID_TRASH;
+ }
+
+ /* degrade state for unknown senders and non-delta messages
+ (the latter may be removed if we run into spam problems, currently this is fine)
+ (noticed messages do count as being unread; therefore, the deaddrop will not popup in the chatlist) */
+ if( chat_id_blocked && state == MR_STATE_IN_FRESH ) {
+ if( incoming_originm_is_send_by_messenger==0 ) {
+ state = MR_STATE_IN_NOTICED;
+ }
+ }
+ }
+ else /* outgoing */
+ {
+ state = MR_STATE_OUT_DELIVERED; /* the mail is on the IMAP server, probably it is also delivered. We cannot recreate other states (read, error). */
+ from_id = MR_CONTACT_ID_SELF;
+ if( mrarray_get_cnt(to_ids) >= 1 ) {
+ to_id = mrarray_get_id(to_ids, 0);
+
+ if( chat_id == 0 )
+ {
+ create_or_lookup_group__(mailbox, mime_parser, MR_CHAT_NOT_BLOCKED, from_id, to_ids, &chat_id, &chat_id_blocked);
+ if( chat_id && chat_id_blocked ) {
+ mrmailbox_unblock_chat__(mailbox, chat_id);
+ chat_id_blocked = 0;
+ }
+ }
+
+ if( chat_id == 0 )
+ {
+ int create_blocked = (mime_parser->m_is_send_by_messenger && !mrmailbox_is_contact_blocked__(mailbox, to_id))? MR_CHAT_NOT_BLOCKED : MR_CHAT_DEADDROP_BLOCKED;
+ mrmailbox_create_or_lookup_nchat_by_contact_id__(mailbox, to_id, create_blocked, &chat_id, &chat_id_blocked);
+ if( chat_id && chat_id_blocked && !create_blocked ) {
+ mrmailbox_unblock_chat__(mailbox, chat_id);
+ chat_id_blocked = 0;
+ }
+ }
+ }
+
+ if( chat_id == 0 ) {
+ if( mrarray_get_cnt(to_ids) == 0 && to_self ) {
+ /* from_id == to_id == MR_CONTACT_ID_SELF - this is a self-sent messages, maybe an Autocrypt Setup Message */
+ mrmailbox_create_or_lookup_nchat_by_contact_id__(mailbox, MR_CONTACT_ID_SELF, MR_CHAT_NOT_BLOCKED, &chat_id, &chat_id_blocked);
+ if( chat_id && chat_id_blocked ) {
+ mrmailbox_unblock_chat__(mailbox, chat_id);
+ chat_id_blocked = 0;
+ }
+ }
+ }
+
+ if( chat_id == 0 ) {
+ chat_id = MR_CHAT_ID_TRASH;
+ }
+ }
+
+ /* correct message_timestamp, it should not be used before,
+ however, we cannot do this earlier as we need from_id to be set */
+ if( (field=mrmimeparser_lookup_field(mime_parser, "Date"))!=NULL && field->fld_type==MAILIMF_FIELD_ORIG_DATE ) {
+ struct mailimf_orig_date* orig_date = field->fld_data.fld_orig_date;
+ if( orig_date ) {
+ message_timestamp = mr_timestamp_from_date(orig_date->dt_date_time); /* is not yet checked against bad times! */
+ }
+ }
+ message_timestamp = mrmailbox_correct_bad_timestamp__(mailbox, chat_id, from_id, message_timestamp, (flags&MR_IMAP_SEEN)? 0 : 1 /*fresh message?*/);
+
+ /* unarchive chat */
+ mrmailbox_unarchive_chat__(mailbox, chat_id);
+
+ /* check, if the mail is already in our database - if so, there's nothing more to do
+ (we may get a mail twice eg. if it is moved between folders) */
+ if( (field=mrmimeparser_lookup_field(mime_parser, "Message-ID"))!=NULL && field->fld_type==MAILIMF_FIELD_MESSAGE_ID ) {
+ struct mailimf_message_id* fld_message_id = field->fld_data.fld_message_id;
+ if( fld_message_id ) {
+ rfc724_mid = safe_strdup(fld_message_id->mid_value);
+ }
+ }
+
+ if( rfc724_mid == NULL ) {
+ /* header is lacking a Message-ID - this may be the case, if the message was sent from this account and the mail client
+ the the SMTP-server set the ID (true eg. for the Webmailer used in all-inkl-KAS)
+ in these cases, we build a message ID based on some useful header fields that do never change (date, to)
+ we do not use the folder-local id, as this will change if the mail is moved to another folder. */
+ rfc724_mid = mr_create_incoming_rfc724_mid(message_timestamp, from_id, to_ids);
+ if( rfc724_mid == NULL ) {
+ mrmailbox_log_info(mailbox, 0, "Cannot create Message-ID.");
+ goto cleanup;
+ }
+ }
+
+ {
+ char* old_server_folder = NULL;
+ uint32_t old_server_uid = 0;
+ if( mrmailbox_rfc724_mid_exists__(mailbox, rfc724_mid, &old_server_folder, &old_server_uid) ) {
+ /* The message is already added to our database; rollback. If needed, update the server_uid which may have changed if the message was moved around on the server. */
+ if( strcmp(old_server_folder, server_folder)!=0 || old_server_uid!=server_uid ) {
+ mrsqlite3_rollback__(mailbox->m_sql);
+ transaction_pending = 0;
+ mrmailbox_update_server_uid__(mailbox, rfc724_mid, server_folder, server_uid);
+ }
+ free(old_server_folder);
+ mrmailbox_log_info(mailbox, 0, "Message already in DB.");
+ goto cleanup;
+ }
+ }
+
+ /* if the message is not sent by a messenger, check if it is sent at least as a reply to a messenger message
+ (later, we move these replies to the Chats-folder) */
+ int msgrmsg = mime_parser->m_is_send_by_messenger; /* 1 or 0 for yes/no */
+ if( msgrmsg )
+ {
+ mrmailbox_log_info(mailbox, 0, "Message sent by another messenger (will be moved to Chats-folder).");
+ }
+ else
+ {
+ if( mrmailbox_is_reply_to_messenger_message__(mailbox, mime_parser) )
+ {
+ mrmailbox_log_info(mailbox, 0, "Message is a reply to a messenger message (will be moved to Chats-folder).");
+ msgrmsg = 2; /* 2=no, but is reply to messenger message */
+ }
+ }
+
+ /* fine, so far. now, split the message into simple parts usable as "short messages"
+ and add them to the database (mails sent by other messenger clients should result
+ into only one message; mails sent by other clients may result in several messages (eg. one per attachment)) */
+ icnt = carray_count(mime_parser->m_parts); /* should be at least one - maybe empty - part */
+ for( i = 0; i < icnt; i++ )
+ {
+ mrmimepart_t* part = (mrmimepart_t*)carray_get(mime_parser->m_parts, i);
+ if( part->m_is_meta ) {
+ continue;
+ }
+
+ if( part->m_type == MR_MSG_TEXT ) {
+ txt_raw = mr_mprintf("%s\n\n%s", mime_parser->m_subject? mime_parser->m_subject : "", part->m_msg_raw);
+ }
+
+ if( mime_parser->m_is_system_message ) {
+ mrparam_set_int(part->m_param, MRP_SYSTEM_CMD, mime_parser->m_is_system_message);
+ }
+
+ stmt = mrsqlite3_predefine__(mailbox->m_sql, INSERT_INTO_msgs_msscftttsmttpb,
+ "INSERT INTO msgs (rfc724_mid,server_folder,server_uid,chat_id,from_id, to_id,timestamp,type, state,msgrmsg,txt,txt_raw,param,bytes)"
+ " VALUES (?,?,?,?,?, ?,?,?, ?,?,?,?,?,?);");
+ sqlite3_bind_text (stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
+ sqlite3_bind_text (stmt, 2, server_folder, -1, SQLITE_STATIC);
+ sqlite3_bind_int (stmt, 3, server_uid);
+ sqlite3_bind_int (stmt, 4, chat_id);
+ sqlite3_bind_int (stmt, 5, from_id);
+ sqlite3_bind_int (stmt, 6, to_id);
+ sqlite3_bind_int64(stmt, 7, message_timestamp);
+ sqlite3_bind_int (stmt, 8, part->m_type);
+ sqlite3_bind_int (stmt, 9, state);
+ sqlite3_bind_int (stmt, 10, msgrmsg);
+ sqlite3_bind_text (stmt, 11, part->m_msg? part->m_msg : "", -1, SQLITE_STATIC);
+ sqlite3_bind_text (stmt, 12, txt_raw? txt_raw : "", -1, SQLITE_STATIC);
+ sqlite3_bind_text (stmt, 13, part->m_param->m_packed, -1, SQLITE_STATIC);
+ sqlite3_bind_int (stmt, 14, part->m_bytes);
+ if( sqlite3_step(stmt) != SQLITE_DONE ) {
+ mrmailbox_log_info(mailbox, 0, "Cannot write DB.");
+ goto cleanup; /* i/o error - there is nothing more we can do - in other cases, we try to write at least an empty record */
+ }
+
+ free(txt_raw);
+ txt_raw = NULL;
+
+ if( first_dblocal_id == 0 ) {
+ first_dblocal_id = sqlite3_last_insert_rowid(mailbox->m_sql->m_cobj);
+ }
+
+ carray_add(created_db_entries, (void*)(uintptr_t)chat_id, NULL);
+ carray_add(created_db_entries, (void*)(uintptr_t)first_dblocal_id, NULL);
+ }
+
+ mrmailbox_log_info(mailbox, 0, "Message has %i parts and is moved to chat #%i.", icnt, chat_id);
+
+ /* check event to send */
+ if( chat_id == MR_CHAT_ID_TRASH )
+ {
+ create_event_to_send = 0;
+ }
+ else if( incoming && state==MR_STATE_IN_FRESH )
+ {
+ if( from_id_blocked ) {
+ create_event_to_send = 0;
+ }
+ else if( chat_id_blocked ) {
+ create_event_to_send = MR_EVENT_MSGS_CHANGED;
+ /*if( mrsqlite3_get_config_int__(mailbox->m_sql, "show_deaddrop", 0)!=0 ) {
+ create_event_to_send = MR_EVENT_INCOMING_MSG;
+ }*/
+ }
+ else {
+ create_event_to_send = MR_EVENT_INCOMING_MSG;
+ }
+ }
+ }
+
+
+ if( carray_count(mime_parser->m_reports) > 0 )
+ {
+ /******************************************************************
+ * Handle reports (mainly MDNs)
+ *****************************************************************/
+
+ int mdns_enabled = mrsqlite3_get_config_int__(mailbox->m_sql, "mdns_enabled", MR_MDNS_DEFAULT_ENABLED);
+ icnt = carray_count(mime_parser->m_reports);
+ for( i = 0; i < icnt; i++ )
+ {
+ int mdn_consumed = 0;
+ struct mailmime* report_root = carray_get(mime_parser->m_reports, i);
+ struct mailmime_parameter* report_type = mailmime_find_ct_parameter(report_root, "report-type");
+ if( report_root==NULL || report_type==NULL || report_type->pa_value==NULL ) {
+ continue;
+ }
+
+ if( strcmp(report_type->pa_value, "disposition-notification") == 0
+ && clist_count(report_root->mm_data.mm_multipart.mm_mp_list) >= 2 /* the first part is for humans, the second for machines */ )
+ {
+ if( mdns_enabled /*to get a clear functionality, do not show incoming MDNs if the options is disabled*/ )
+ {
+ struct mailmime* report_data = (struct mailmime*)clist_content(clist_next(clist_begin(report_root->mm_data.mm_multipart.mm_mp_list)));
+ if( report_data
+ && report_data->mm_content_type->ct_type->tp_type==MAILMIME_TYPE_COMPOSITE_TYPE
+ && report_data->mm_content_type->ct_type->tp_data.tp_composite_type->ct_type==MAILMIME_COMPOSITE_TYPE_MESSAGE
+ && strcmp(report_data->mm_content_type->ct_subtype, "disposition-notification")==0 )
+ {
+ /* we received a MDN (although the MDN is only a header, we parse it as a complete mail) */
+ const char* report_body = NULL;
+ size_t report_body_bytes = 0;
+ char* to_mmap_string_unref = NULL;
+ if( mailmime_transfer_decode(report_data, &report_body, &report_body_bytes, &to_mmap_string_unref) )
+ {
+ struct mailmime* report_parsed = NULL;
+ size_t dummy = 0;
+ if( mailmime_parse(report_body, report_body_bytes, &dummy, &report_parsed)==MAIL_NO_ERROR
+ && report_parsed!=NULL )
+ {
+ struct mailimf_fields* report_fields = mailmime_find_mailimf_fields(report_parsed);
+ if( report_fields )
+ {
+ struct mailimf_optional_field* of_disposition = mailimf_find_optional_field(report_fields, "Disposition"); /* MUST be preset, _if_ preset, we assume a sort of attribution and do not go into details */
+ struct mailimf_optional_field* of_org_msgid = mailimf_find_optional_field(report_fields, "Original-Message-ID"); /* can't live without */
+ if( of_disposition && of_disposition->fld_value && of_org_msgid && of_org_msgid->fld_value )
+ {
+ char* rfc724_mid = NULL;
+ dummy = 0;
+ if( mailimf_msg_id_parse(of_org_msgid->fld_value, strlen(of_org_msgid->fld_value), &dummy, &rfc724_mid)==MAIL_NO_ERROR
+ && rfc724_mid!=NULL )
+ {
+ uint32_t chat_id = 0;
+ uint32_t msg_id = 0;
+ if( mrmailbox_mdn_from_ext__(mailbox, from_id, rfc724_mid, &chat_id, &msg_id) ) {
+ carray_add(rr_event_to_send, (void*)(uintptr_t)chat_id, NULL);
+ carray_add(rr_event_to_send, (void*)(uintptr_t)msg_id, NULL);
+ }
+ mdn_consumed = (msg_id!=0);
+ free(rfc724_mid);
+ }
+ }
+ }
+ mailmime_free(report_parsed);
+ }
+
+ if( to_mmap_string_unref ) { mmap_string_unref(to_mmap_string_unref); }
+ }
+ }
+ }
+
+ /* Move the MDN away to the chats folder. We do this for:
+ - Consumed or not consumed MDNs from other messengers
+ - Consumed MDNs from normal MUAs
+ Unconsumed MDNs from normal MUAs are _not_ moved.
+ NB: we do not delete the MDN as it may be used by other clients
+
+ CAVE: we rely on mrimap_markseen_msg() not to move messages that are already in the correct folder.
+ otherwise, the moved message get a new server_uid and is "fresh" again and we will be here again to move it away -
+ a classical deadlock, see also (***) in mrimap.c */
+ if( mime_parser->m_is_send_by_messenger || mdn_consumed ) {
+ char* jobparam = mr_mprintf("%c=%s\n%c=%lu", MRP_SERVER_FOLDER, server_folder, MRP_SERVER_UID, server_uid);
+ mrjob_add__(mailbox, MRJ_MARKSEEN_MDN_ON_IMAP, 0, jobparam);
+ free(jobparam);
+ }
+ }
+
+ } /* for() */
+
+ }
+
+ /* debug print? */
+ if( mrsqlite3_get_config_int__(mailbox->m_sql, "save_eml", 0) ) {
+ char* emlname = mr_mprintf("%s/%s-%i.eml", mailbox->m_blobdir, server_folder, (int)first_dblocal_id /*may be 0 for MDNs*/);
+ FILE* emlfileob = fopen(emlname, "w");
+ if( emlfileob ) {
+ fwrite(imf_raw_not_terminated, 1, imf_raw_bytes, emlfileob);
+ fclose(emlfileob);
+ }
+ free(emlname);
+ }
+
+ /* end sql-transaction */
+ mrsqlite3_commit__(mailbox->m_sql);
+ transaction_pending = 0;
+
+ /* done */
+cleanup:
+ if( transaction_pending ) {
+ mrsqlite3_rollback__(mailbox->m_sql);
+ }
+
+ if( db_locked ) {
+ mrsqlite3_unlock(mailbox->m_sql);
+ }
+
+ if( mime_parser ) {
+ mrmimeparser_unref(mime_parser);
+ }
+
+ if( rfc724_mid ) {
+ free(rfc724_mid);
+ }
+
+ if( to_ids ) {
+ mrarray_unref(to_ids);
+ }
+
+ if( created_db_entries ) {
+ if( create_event_to_send ) {
+ size_t i, icnt = carray_count(created_db_entries);
+ for( i = 0; i < icnt; i += 2 ) {
+ mailbox->m_cb(mailbox, create_event_to_send, (uintptr_t)carray_get(created_db_entries, i), (uintptr_t)carray_get(created_db_entries, i+1));
+ }
+ }
+ carray_free(created_db_entries);
+ }
+
+ if( rr_event_to_send ) {
+ size_t i, icnt = carray_count(rr_event_to_send);
+ for( i = 0; i < icnt; i += 2 ) {
+ mailbox->m_cb(mailbox, MR_EVENT_MSG_READ, (uintptr_t)carray_get(rr_event_to_send, i), (uintptr_t)carray_get(rr_event_to_send, i+1));
+ }
+ carray_free(rr_event_to_send);
+ }
+
+ free(txt_raw);
+}
diff --git a/src/mrmailbox_tools.c b/src/mrmailbox_tools.c
deleted file mode 100644
index fb7119a2..00000000
--- a/src/mrmailbox_tools.c
+++ /dev/null
@@ -1,292 +0,0 @@
-/*******************************************************************************
- *
- * Delta Chat Core
- * Copyright (C) 2017 Björn Petersen
- * Contact: r10s@b44t.com, http://b44t.com
- *
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see http://www.gnu.org/licenses/ .
- *
- ******************************************************************************/
-
-
-#include "mrmailbox_internal.h"
-#include "mrmimeparser.h"
-
-
-/*******************************************************************************
- * Add contacts to database on receiving messages
- ******************************************************************************/
-
-
-static void add_or_lookup_contact_by_addr__(mrmailbox_t* ths, const char* display_name_enc, const char* addr_spec, int origin, mrarray_t* ids, int* check_self)
-{
- /* is addr_spec equal to SELF? */
- int dummy;
- if( check_self == NULL ) { check_self = &dummy; }
-
- *check_self = 0;
-
- char* self_addr = mrsqlite3_get_config__(ths->m_sql, "configured_addr", "");
- if( strcasecmp(self_addr, addr_spec)==0 ) {
- *check_self = 1;
- }
- free(self_addr);
-
- if( *check_self ) {
- return;
- }
-
- /* add addr_spec if missing, update otherwise */
- char* display_name_dec = NULL;
- if( display_name_enc ) {
- display_name_dec = mr_decode_header_string(display_name_enc);
- mr_normalize_name(display_name_dec);
- }
-
- uint32_t row_id = mrmailbox_add_or_lookup_contact__(ths, display_name_dec /*can be NULL*/, addr_spec, origin, NULL);
-
- free(display_name_dec);
-
- if( row_id ) {
- if( !mrarray_search_id(ids, row_id, NULL) ) {
- mrarray_add_id(ids, row_id);
- }
- }
-}
-
-
-void mrmailbox_add_or_lookup_contacts_by_mailbox_list__(mrmailbox_t* ths, struct mailimf_mailbox_list* mb_list, int origin, mrarray_t* ids, int* check_self)
-{
- clistiter* cur;
- for( cur = clist_begin(mb_list->mb_list); cur!=NULL ; cur=clist_next(cur) ) {
- struct mailimf_mailbox* mb = (struct mailimf_mailbox*)clist_content(cur);
- if( mb ) {
- add_or_lookup_contact_by_addr__(ths, mb->mb_display_name, mb->mb_addr_spec, origin, ids, check_self);
- }
- }
-}
-
-
-void mrmailbox_add_or_lookup_contacts_by_address_list__(mrmailbox_t* ths, struct mailimf_address_list* adr_list, int origin, mrarray_t* ids, int* check_self)
-{
- clistiter* cur;
- for( cur = clist_begin(adr_list->ad_list); cur!=NULL ; cur=clist_next(cur) ) {
- struct mailimf_address* adr = (struct mailimf_address*)clist_content(cur);
- if( adr ) {
- if( adr->ad_type == MAILIMF_ADDRESS_MAILBOX ) {
- struct mailimf_mailbox* mb = adr->ad_data.ad_mailbox; /* can be NULL */
- if( mb ) {
- add_or_lookup_contact_by_addr__(ths, mb->mb_display_name, mb->mb_addr_spec, origin, ids, check_self);
- }
- }
- else if( adr->ad_type == MAILIMF_ADDRESS_GROUP ) {
- struct mailimf_group* group = adr->ad_data.ad_group; /* can be NULL */
- if( group && group->grp_mb_list /*can be NULL*/ ) {
- mrmailbox_add_or_lookup_contacts_by_mailbox_list__(ths, group->grp_mb_list, origin, ids, check_self);
- }
- }
- }
- }
-}
-
-
-/*******************************************************************************
- * Check if a message is a reply to a known message (messenger or non-messenger)
- ******************************************************************************/
-
-
-static int is_known_rfc724_mid__(mrmailbox_t* mailbox, const char* rfc724_mid)
-{
- if( rfc724_mid ) {
- sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_msgs_WHERE_cm,
- "SELECT m.id FROM msgs m "
- " LEFT JOIN chats c ON m.chat_id=c.id "
- " WHERE m.rfc724_mid=? "
- " AND m.chat_id>" MR_STRINGIFY(MR_CHAT_ID_LAST_SPECIAL)
- " AND c.blocked=0;");
- sqlite3_bind_text(stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
- if( sqlite3_step(stmt) == SQLITE_ROW ) {
- return 1;
- }
- }
- return 0;
-}
-
-
-static int is_known_rfc724_mid_in_list__(mrmailbox_t* mailbox, const clist* mid_list)
-{
- if( mid_list ) {
- clistiter* cur;
- for( cur = clist_begin(mid_list); cur!=NULL ; cur=clist_next(cur) ) {
- if( is_known_rfc724_mid__(mailbox, clist_content(cur)) ) {
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-
-int mrmailbox_is_reply_to_known_message__(mrmailbox_t* mailbox, mrmimeparser_t* mime_parser)
-{
- /* check if the message is a reply to a known message; the replies are identified by the Message-ID from
- `In-Reply-To`/`References:` (to support non-Delta-Clients) or from `Chat-Predecessor:` (Delta clients, see comment in mrchat.c) */
-
- struct mailimf_optional_field* optional_field;
- if( (optional_field=mrmimeparser_lookup_optional_field2(mime_parser, "Chat-Predecessor", "X-MrPredecessor")) != NULL )
- {
- if( is_known_rfc724_mid__(mailbox, optional_field->fld_value) ) {
- return 1;
- }
- }
-
- struct mailimf_field* field;
- if( (field=mrmimeparser_lookup_field(mime_parser, "In-Reply-To"))!=NULL
- && field->fld_type == MAILIMF_FIELD_IN_REPLY_TO )
- {
- struct mailimf_in_reply_to* fld_in_reply_to = field->fld_data.fld_in_reply_to;
- if( fld_in_reply_to ) {
- if( is_known_rfc724_mid_in_list__(mailbox, field->fld_data.fld_in_reply_to->mid_list) ) {
- return 1;
- }
- }
- }
-
- if( (field=mrmimeparser_lookup_field(mime_parser, "References"))!=NULL
- && field->fld_type == MAILIMF_FIELD_REFERENCES )
- {
- struct mailimf_references* fld_references = field->fld_data.fld_references;
- if( fld_references ) {
- if( is_known_rfc724_mid_in_list__(mailbox, field->fld_data.fld_references->mid_list) ) {
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-
-/*******************************************************************************
- * Check if a message is a reply to any messenger message
- ******************************************************************************/
-
-
-static int is_msgrmsg_rfc724_mid__(mrmailbox_t* mailbox, const char* rfc724_mid)
-{
- if( rfc724_mid ) {
- sqlite3_stmt* stmt = mrsqlite3_predefine__(mailbox->m_sql, SELECT_id_FROM_msgs_WHERE_mcm,
- "SELECT id FROM msgs "
- " WHERE rfc724_mid=? "
- " AND msgrmsg!=0 "
- " AND chat_id>" MR_STRINGIFY(MR_CHAT_ID_LAST_SPECIAL) ";");
- sqlite3_bind_text(stmt, 1, rfc724_mid, -1, SQLITE_STATIC);
- if( sqlite3_step(stmt) == SQLITE_ROW ) {
- return 1;
- }
- }
- return 0;
-}
-
-
-static int is_msgrmsg_rfc724_mid_in_list__(mrmailbox_t* mailbox, const clist* mid_list)
-{
- if( mid_list ) {
- clistiter* cur;
- for( cur = clist_begin(mid_list); cur!=NULL ; cur=clist_next(cur) ) {
- if( is_msgrmsg_rfc724_mid__(mailbox, clist_content(cur)) ) {
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-
-int mrmailbox_is_reply_to_messenger_message__(mrmailbox_t* mailbox, mrmimeparser_t* mime_parser)
-{
- /* function checks, if the message defined by mime_parser references a message send by us from Delta Chat.
-
- This is similar to is_reply_to_known_message__() but
- - checks also if any of the referenced IDs are send by a messenger
- - it is okay, if the referenced messages are moved to trash here
- - no check for the Chat-* headers (function is only called if it is no messenger message itself) */
-
- struct mailimf_field* field;
- if( (field=mrmimeparser_lookup_field(mime_parser, "In-Reply-To"))!=NULL
- && field->fld_type==MAILIMF_FIELD_IN_REPLY_TO )
- {
- struct mailimf_in_reply_to* fld_in_reply_to = field->fld_data.fld_in_reply_to;
- if( fld_in_reply_to ) {
- if( is_msgrmsg_rfc724_mid_in_list__(mailbox, field->fld_data.fld_in_reply_to->mid_list) ) {
- return 1;
- }
- }
- }
-
- if( (field=mrmimeparser_lookup_field(mime_parser, "References"))!=NULL
- && field->fld_type==MAILIMF_FIELD_REFERENCES )
- {
- struct mailimf_references* fld_references = field->fld_data.fld_references;
- if( fld_references ) {
- if( is_msgrmsg_rfc724_mid_in_list__(mailbox, field->fld_data.fld_references->mid_list) ) {
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-
-/*******************************************************************************
- * Misc.
- ******************************************************************************/
-
-
-time_t mrmailbox_correct_bad_timestamp__(mrmailbox_t* ths, uint32_t chat_id, uint32_t from_id, time_t desired_timestamp, int is_fresh_msg)
-{
- /* used for correcting timestamps of _received_ messages.
- use the last message from another user (including SELF) as the MINIMUM
- (we do this check only for fresh messages, other messages may pop up whereever, this may happen eg. when restoring old messages or synchronizing different clients) */
- if( is_fresh_msg )
- {
- sqlite3_stmt* stmt = mrsqlite3_predefine__(ths->m_sql, SELECT_timestamp_FROM_msgs_WHERE_timestamp,
- "SELECT MAX(timestamp) FROM msgs WHERE chat_id=? and from_id!=? AND timestamp>=?");
- sqlite3_bind_int (stmt, 1, chat_id);
- sqlite3_bind_int (stmt, 2, from_id);
- sqlite3_bind_int64(stmt, 3, desired_timestamp);
- if( sqlite3_step(stmt)==SQLITE_ROW )
- {
- time_t last_msg_time = sqlite3_column_int64(stmt, 0);
- if( last_msg_time > 0 /* may happen as we do not check against sqlite3_column_type()!=SQLITE_NULL */ ) {
- if( desired_timestamp <= last_msg_time ) {
- desired_timestamp = last_msg_time+1; /* this may result in several incoming messages having the same
- one-second-after-the-last-other-message-timestamp. however, this is no big deal
- as we do not try to recrete the order of bad-date-messages and as we always order by ID as second criterion */
- }
- }
- }
- }
-
- /* use the (smeared) current time as the MAXIMUM */
- if( desired_timestamp >= mr_smeared_time__() )
- {
- desired_timestamp = mr_create_smeared_timestamp__();
- }
-
- return desired_timestamp;
-}
\ No newline at end of file