From 763334d0aa2c39e516c9ef8260f95435a6f3db3a Mon Sep 17 00:00:00 2001 From: Alexander Krotov Date: Sun, 16 Aug 2020 12:00:00 +0300 Subject: [PATCH] Sort message replies after parent message --- src/dc_receive_imf.rs | 14 +++++++++++ src/mimeparser.rs | 55 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 003baded5..f48986199 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -698,6 +698,20 @@ async fn add_parts( let in_fresh = state == MessageState::InFresh; let rcvd_timestamp = time(); let sort_timestamp = calc_sort_timestamp(context, *sent_timestamp, *chat_id, in_fresh).await; + + // Ensure replies to messages are sorted after the parent message. + // + // This is useful in a case where sender clocks are not + // synchronized and parent message has a Date: header with a + // timestamp higher than reply timestamp. + // + // This does not help if parent message arrives later than the + // reply. + let parent_timestamp = mime_parser.get_parent_timestamp(context).await?; + let sort_timestamp = parent_timestamp.map_or(sort_timestamp, |parent_timestamp| { + std::cmp::max(sort_timestamp, parent_timestamp) + }); + *sent_timestamp = std::cmp::min(*sent_timestamp, rcvd_timestamp); // unarchive chat diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 90ef747e2..d49f9ed66 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -1044,6 +1044,28 @@ impl MimeMessage { message::handle_ndn(context, failure_report, error).await } } + + /// Returns timestamp of the parent message. + /// + /// If there is no parent message or it is not found in the + /// database, returns None. + pub async fn get_parent_timestamp(&self, context: &Context) -> Result> { + let parent_timestamp = if let Some(field) = self + .get(HeaderDef::InReplyTo) + .and_then(|msgid| parse_message_id(msgid).ok()) + { + context + .sql + .query_get_value_result( + "SELECT timestamp FROM msgs WHERE rfc724_mid=?", + paramsv![field], + ) + .await? + } else { + None + }; + Ok(parent_timestamp) + } } async fn update_gossip_peerstates( @@ -1413,6 +1435,39 @@ mod tests { assert!(mimeparser.chat_disposition_notification_to.is_none()); } + #[async_std::test] + async fn test_get_parent_timestamp() { + let context = TestContext::new().await; + let raw = b"From: foo@example.org\n\ + Content-Type: text/plain\n\ + Chat-Version: 1.0\n\ + In-Reply-To: \n\ + \n\ + Some reply\n\ + "; + let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]) + .await + .unwrap(); + assert_eq!( + mimeparser.get_parent_timestamp(&context.ctx).await.unwrap(), + None + ); + let timestamp = 1570435529; + context + .ctx + .sql + .execute( + "INSERT INTO msgs (rfc724_mid, timestamp) VALUES(?,?)", + paramsv!["Gr.beZgAF2Nn0-.oyaJOpeuT70@example.org", timestamp], + ) + .await + .expect("Failed to write to the database"); + assert_eq!( + mimeparser.get_parent_timestamp(&context.ctx).await.unwrap(), + Some(timestamp) + ); + } + #[async_std::test] async fn test_mimeparser_with_context() { let context = TestContext::new().await;