diff --git a/src/mrlot.c b/src/mrlot.c index f3af7ad3..3f18175e 100644 --- a/src/mrlot.c +++ b/src/mrlot.c @@ -213,6 +213,6 @@ void mrlot_fill(mrlot_t* ths, const mrmsg_t* msg, const mrchat_t* chat, const mr } ths->m_text2 = mrmsg_get_summarytext_by_raw(msg->m_type, msg->m_text, msg->m_param, MR_SUMMARY_CHARACTERS); - ths->m_timestamp = msg->m_timestamp; + ths->m_timestamp = mrmsg_get_timestamp(msg); ths->m_state = msg->m_state; } diff --git a/src/mrmailbox.c b/src/mrmailbox.c index dc175e67..8242b59b 100644 --- a/src/mrmailbox.c +++ b/src/mrmailbox.c @@ -4389,10 +4389,16 @@ char* mrmailbox_get_msg_info(mrmailbox_t* mailbox, uint32_t msg_id) locked = 0; /* add time */ - mrstrbuilder_cat(&ret, "Date: "); - p = mr_timestamp_to_str(msg->m_timestamp); mrstrbuilder_cat(&ret, p); free(p); + mrstrbuilder_cat(&ret, "Sent: "); + p = mr_timestamp_to_str(mrmsg_get_timestamp(msg)); mrstrbuilder_cat(&ret, p); free(p); mrstrbuilder_cat(&ret, "\n"); + if( msg->m_from_id != MR_CONTACT_ID_SELF ) { + mrstrbuilder_cat(&ret, "Received: "); + p = mr_timestamp_to_str(msg->m_timestamp_rcvd? msg->m_timestamp_rcvd : msg->m_timestamp); mrstrbuilder_cat(&ret, p); free(p); + mrstrbuilder_cat(&ret, "\n"); + } + /* add encryption state */ int e2ee_errors; if( (e2ee_errors=mrparam_get_int(msg->m_param, MRP_ERRONEOUS_E2EE, 0)) ) { diff --git a/src/mrmailbox_receive_imf.c b/src/mrmailbox_receive_imf.c index ae4ce9f0..a99275f9 100644 --- a/src/mrmailbox_receive_imf.c +++ b/src/mrmailbox_receive_imf.c @@ -262,10 +262,20 @@ static int mrmailbox_is_reply_to_messenger_message__(mrmailbox_t* mailbox, mrmim ******************************************************************************/ -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) +static void mrmailbox_calc_timestamps__(mrmailbox_t* mailbox, uint32_t chat_id, uint32_t from_id, time_t message_timestamp, int is_fresh_msg, + time_t* sort_timestamp, time_t* sent_timestamp, time_t* rcvd_timestamp) { - /* used for correcting timestamps of _received_ messages. - use the last message from another user (including SELF) as the MINIMUM + *rcvd_timestamp = time(NULL); + + *sent_timestamp = message_timestamp; + if( *sent_timestamp > *rcvd_timestamp /* no sending times in the future */ ) { + *sent_timestamp = *rcvd_timestamp; + } + + *sort_timestamp = message_timestamp; /* truncatd below to smeared time (not to _now_ to keep the order) */ + + /* use the last message from another user (including SELF) as the MINIMUM for sort_timestamp; + this is to force fresh messages popping up at the end of the list. (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 ) { @@ -273,13 +283,13 @@ static time_t mrmailbox_correct_bad_timestamp__(mrmailbox_t* mailbox, uint32_t c "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); + sqlite3_bind_int64(stmt, 3, *sort_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 + if( *sort_timestamp <= last_msg_time ) { + *sort_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 */ } @@ -288,12 +298,9 @@ static time_t mrmailbox_correct_bad_timestamp__(mrmailbox_t* mailbox, uint32_t c } /* use the (smeared) current time as the MAXIMUM */ - if( desired_timestamp >= mr_smeared_time__() ) - { - desired_timestamp = mr_create_smeared_timestamp__(); + if( *sort_timestamp >= mr_smeared_time__() ) { + *sort_timestamp = mr_create_smeared_timestamp__(); } - - return desired_timestamp; } @@ -823,7 +830,9 @@ void mrmailbox_receive_imf(mrmailbox_t* mailbox, const char* imf_raw_not_termina 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; + time_t sort_timestamp = MR_INVALID_TIMESTAMP; + time_t sent_timestamp = MR_INVALID_TIMESTAMP; + time_t rcvd_timestamp = MR_INVALID_TIMESTAMP; mrmimeparser_t* mime_parser = mrmimeparser_new(mailbox->m_blobdir, mailbox); int db_locked = 0; int transaction_pending = 0; @@ -1082,10 +1091,11 @@ void mrmailbox_receive_imf(mrmailbox_t* mailbox, const char* imf_raw_not_termina 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! */ + sent_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?*/); + mrmailbox_calc_timestamps__(mailbox, chat_id, from_id, sent_timestamp, (flags&MR_IMAP_SEEN)? 0 : 1 /*fresh message?*/, + &sort_timestamp, &sent_timestamp, &rcvd_timestamp); /* unarchive chat */ mrmailbox_unarchive_chat__(mailbox, chat_id); @@ -1104,7 +1114,7 @@ void mrmailbox_receive_imf(mrmailbox_t* mailbox, const char* imf_raw_not_termina 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); + rfc724_mid = mr_create_incoming_rfc724_mid(sort_timestamp, from_id, to_ids); if( rfc724_mid == NULL ) { mrmailbox_log_info(mailbox, 0, "Cannot create Message-ID."); goto cleanup; @@ -1163,22 +1173,24 @@ void mrmailbox_receive_imf(mrmailbox_t* mailbox, const char* imf_raw_not_termina } 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 (?,?,?,?,?, ?,?,?, ?,?,?,?,?,?);"); + "INSERT INTO msgs (rfc724_mid,server_folder,server_uid,chat_id,from_id, to_id,timestamp,timestamp_sent,timestamp_rcvd,type, state,msgrmsg,txt,txt_raw,param,bytes)" + " 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); + sqlite3_bind_int64(stmt, 7, sort_timestamp); + sqlite3_bind_int64(stmt, 8, sent_timestamp); + sqlite3_bind_int64(stmt, 9, rcvd_timestamp); + sqlite3_bind_int (stmt, 10, part->m_type); + sqlite3_bind_int (stmt, 11, state); + sqlite3_bind_int (stmt, 12, msgrmsg); + sqlite3_bind_text (stmt, 13, part->m_msg? part->m_msg : "", -1, SQLITE_STATIC); + sqlite3_bind_text (stmt, 14, txt_raw? txt_raw : "", -1, SQLITE_STATIC); + sqlite3_bind_text (stmt, 15, part->m_param->m_packed, -1, SQLITE_STATIC); + sqlite3_bind_int (stmt, 16, 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 */ diff --git a/src/mrmsg-private.h b/src/mrmsg-private.h index 150806c8..e6a63ee5 100644 --- a/src/mrmsg-private.h +++ b/src/mrmsg-private.h @@ -75,7 +75,10 @@ struct _mrmsg int m_state; /**< Message state. It is recommended to use mrmsg_get_state() to access this field. */ - time_t m_timestamp; /**< Unix time the message was sended or received. 0 if unset. */ + time_t m_timestamp; /**< Unix time for sorting. 0 if unset. */ + time_t m_timestamp_sent; /**< Unix time the message was sent. 0 if unset. */ + time_t m_timestamp_rcvd; /**< Unix time the message was recveived. 0 if unset. */ + char* m_text; /**< Message text. NULL if unset. It is recommended to use mrmsg_set_text() and mrmsg_get_text() to access this field. */ mrmailbox_t* m_mailbox; /**< may be NULL, set on loading from database and on sending */ diff --git a/src/mrmsg.c b/src/mrmsg.c index 71214751..e1b3a38e 100644 --- a/src/mrmsg.c +++ b/src/mrmsg.c @@ -231,7 +231,10 @@ int mrmsg_get_state(mrmsg_t* msg) /** - * Get message time. Unix time the message was sended or received. + * Get message sending time. The sending time is returned by a unix timestamp. + * Note that the message list is not sorted by the _sending_ time but by the _receiving_ time. + * Cave: the message list is sorted by receiving time (otherwise new messages would non pop up at the expected place), + * however, if a message is delayed for any reason, the correct sending time will be displayed. * * @memberof mrmsg_t * @@ -244,7 +247,8 @@ time_t mrmsg_get_timestamp(mrmsg_t* msg) if( msg == NULL || msg->m_magic != MR_MSG_MAGIC ) { return 0; } - return msg->m_timestamp; + + return msg->m_timestamp_sent? msg->m_timestamp_sent : msg->m_timestamp; } @@ -848,7 +852,7 @@ cleanup: #define MR_MSG_FIELDS " m.id,rfc724_mid,m.server_folder,m.server_uid,m.chat_id, " \ - " m.from_id,m.to_id,m.timestamp, m.type,m.state,m.msgrmsg,m.txt, " \ + " m.from_id,m.to_id,m.timestamp,m.timestamp_sent,m.timestamp_rcvd, m.type,m.state,m.msgrmsg,m.txt, " \ " m.param,m.starred,c.blocked " @@ -865,6 +869,8 @@ static int mrmsg_set_from_stmt__(mrmsg_t* ths, sqlite3_stmt* row, int row_offset ths->m_from_id = (uint32_t)sqlite3_column_int (row, row_offset++); ths->m_to_id = (uint32_t)sqlite3_column_int (row, row_offset++); ths->m_timestamp = (time_t)sqlite3_column_int64(row, row_offset++); + ths->m_timestamp_sent = (time_t)sqlite3_column_int64(row, row_offset++); + ths->m_timestamp_rcvd = (time_t)sqlite3_column_int64(row, row_offset++); ths->m_type = sqlite3_column_int (row, row_offset++); ths->m_state = sqlite3_column_int (row, row_offset++); diff --git a/src/mrsqlite3.c b/src/mrsqlite3.c index 90900e71..a0a61827 100644 --- a/src/mrsqlite3.c +++ b/src/mrsqlite3.c @@ -355,6 +355,17 @@ int mrsqlite3_open__(mrsqlite3_t* ths, const char* dbfile, int flags) mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION); } #undef NEW_DB_VERSION + + #define NEW_DB_VERSION 22 + if( dbversion < NEW_DB_VERSION ) + { + mrsqlite3_execute__(ths, "ALTER TABLE msgs ADD COLUMN timestamp_sent INTEGER DEFAULT 0;"); + mrsqlite3_execute__(ths, "ALTER TABLE msgs ADD COLUMN timestamp_rcvd INTEGER DEFAULT 0;"); + + dbversion = NEW_DB_VERSION; + mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION); + } + #undef NEW_DB_VERSION } mrmailbox_log_info(ths->m_mailbox, 0, "Opened \"%s\" successfully.", dbfile);