From 20bf41b4e69232ae8186896d4ad5e27ae6108fc0 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sat, 7 Aug 2021 14:36:04 +0000 Subject: [PATCH] Set timestamps for system messages Previously system messages were always added to the end of the chat, even if the message triggering them was sent earlier. This is especially important for messages about disappearing timer reset triggered by classic email messages, as they should be placed right after the message resetting the timer. --- src/chat.rs | 27 ++++++++++++++++++++++----- src/dc_receive_imf.rs | 14 ++++++++------ src/e2ee.rs | 4 +++- src/location.rs | 9 +++++---- src/message.rs | 12 +++++++++--- src/mimeparser.rs | 4 +++- src/peerstate.rs | 8 ++++++-- src/qr.rs | 9 ++++++++- src/securejoin.rs | 5 +++-- 9 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/chat.rs b/src/chat.rs index 02f4d44ae..e6245c480 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -381,7 +381,14 @@ impl ChatId { msg.param.set_cmd(cmd); send_msg(context, self, &mut msg).await?; } else { - add_info_msg_with_cmd(context, self, msg_text, cmd).await?; + add_info_msg_with_cmd( + context, + self, + msg_text, + cmd, + dc_create_smeared_timestamp(context).await, + ) + .await?; } Ok(()) @@ -2981,6 +2988,7 @@ pub(crate) async fn add_info_msg_with_cmd( chat_id: ChatId, text: impl AsRef, cmd: SystemMessage, + timestamp: i64, ) -> Result { let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device"); let ephemeral_timer = chat_id.get_ephemeral_timer(context).await?; @@ -2997,7 +3005,7 @@ pub(crate) async fn add_info_msg_with_cmd( chat_id, DC_CONTACT_ID_INFO, DC_CONTACT_ID_INFO, - dc_create_smeared_timestamp(context).await, + timestamp, Viewtype::Text, MessageState::InNoticed, text.as_ref().to_string(), @@ -3012,8 +3020,16 @@ pub(crate) async fn add_info_msg_with_cmd( Ok(msg_id) } -pub(crate) async fn add_info_msg(context: &Context, chat_id: ChatId, text: impl AsRef) { - if let Err(e) = add_info_msg_with_cmd(context, chat_id, text, SystemMessage::Unknown).await { +/// Adds info message with a given text and `timestamp` to the chat. +pub(crate) async fn add_info_msg( + context: &Context, + chat_id: ChatId, + text: impl AsRef, + timestamp: i64, +) { + if let Err(e) = + add_info_msg_with_cmd(context, chat_id, text, SystemMessage::Unknown, timestamp).await + { warn!(context, "Could not add info msg: {}", e); } } @@ -3637,7 +3653,7 @@ mod tests { let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo") .await .unwrap(); - add_info_msg(&t, chat_id, "foo info").await; + add_info_msg(&t, chat_id, "foo info", 200000).await; let msg = t.get_last_msg_in(chat_id).await; assert_eq!(msg.get_chat_id(), chat_id); @@ -3658,6 +3674,7 @@ mod tests { chat_id, "foo bar info", SystemMessage::EphemeralTimerChanged, + 10000, ) .await .unwrap(); diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index e35703645..b0f210c98 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -798,6 +798,12 @@ async fn add_parts( let location_kml_is = mime_parser.location_kml.is_some(); + // correct message_timestamp, it should not be used before, + // however, we cannot do this earlier as we need from_id to be set + let in_fresh = state == MessageState::InFresh; + let rcvd_timestamp = time(); + let sort_timestamp = calc_sort_timestamp(context, *sent_timestamp, chat_id, in_fresh).await?; + // Apply ephemeral timer changes to the chat. // // Only non-hidden timers are applied now. Timers from hidden @@ -825,6 +831,7 @@ async fn add_parts( context, chat_id, stock_ephemeral_timer_changed(context, ephemeral_timer, from_id).await, + sort_timestamp, ) .await; } @@ -868,6 +875,7 @@ async fn add_parts( context, chat_id, format!("Cannot set protection: {}", e), + sort_timestamp, ) .await; return Ok(chat_id); // do not return an error as this would result in retrying the message @@ -883,12 +891,6 @@ async fn add_parts( } } - // correct message_timestamp, it should not be used before, - // however, we cannot do this earlier as we need from_id to be set - 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 diff --git a/src/e2ee.rs b/src/e2ee.rs index 5257a4358..702e44dd0 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -178,7 +178,9 @@ pub async fn try_decrypt( let mut signatures = HashSet::default(); if let Some(ref mut peerstate) = peerstate { - peerstate.handle_fingerprint_change(context).await?; + peerstate + .handle_fingerprint_change(context, message_time) + .await?; if let Some(key) = &peerstate.public_key { public_keyring_for_validate.add(key.clone()); } else if let Some(key) = &peerstate.gossip_key { diff --git a/src/location.rs b/src/location.rs index cc7ffc746..e09e81202 100644 --- a/src/location.rs +++ b/src/location.rs @@ -190,7 +190,7 @@ impl Kml { } } -// location streaming +/// Enables location streaming in chat identified by `chat_id` for `seconds` seconds. pub async fn send_locations_to_chat(context: &Context, chat_id: ChatId, seconds: i64) { let now = time(); if !(seconds < 0 || chat_id.is_special()) { @@ -221,7 +221,7 @@ pub async fn send_locations_to_chat(context: &Context, chat_id: ChatId, seconds: .unwrap_or_default(); } else if 0 == seconds && is_sending_locations_before { let stock_str = stock_str::msg_location_disabled(context).await; - chat::add_info_msg(context, chat_id, stock_str).await; + chat::add_info_msg(context, chat_id, stock_str, now).await; } context.emit_event(EventType::ChatModified(chat_id)); if 0 != seconds { @@ -716,7 +716,8 @@ pub(crate) async fn job_maybe_send_locations_ended( .await ); - if !(send_begin != 0 && time() <= send_until) { + let now = time(); + if !(send_begin != 0 && now <= send_until) { // still streaming - // may happen as several calls to dc_send_locations_to_chat() // do not un-schedule pending DC_MAYBE_SEND_LOC_ENDED jobs @@ -735,7 +736,7 @@ pub(crate) async fn job_maybe_send_locations_ended( ); let stock_str = stock_str::msg_location_disabled(context).await; - chat::add_info_msg(context, chat_id, stock_str).await; + chat::add_info_msg(context, chat_id, stock_str, now).await; context.emit_event(EventType::ChatModified(chat_id)); } } diff --git a/src/message.rs b/src/message.rs index 2e348d7cb..9f293bb03 100644 --- a/src/message.rs +++ b/src/message.rs @@ -19,8 +19,8 @@ use crate::constants::{ use crate::contact::{Contact, Origin}; use crate::context::Context; use crate::dc_tools::{ - dc_get_filebytes, dc_get_filemeta, dc_gm2local_offset, dc_read_file, dc_timestamp_to_str, - dc_truncate, time, + dc_create_smeared_timestamp, dc_get_filebytes, dc_get_filemeta, dc_gm2local_offset, + dc_read_file, dc_timestamp_to_str, dc_truncate, time, }; use crate::ephemeral::Timer as EphemeralTimer; use crate::events::EventType; @@ -1805,7 +1805,13 @@ async fn ndn_maybe_add_info_msg( // Tell the user which of the recipients failed if we know that (because in // a group, this might otherwise be unclear) let text = stock_str::failed_sending_to(context, contact.get_display_name()).await; - chat::add_info_msg(context, chat_id, text).await; + chat::add_info_msg( + context, + chat_id, + text, + dc_create_smeared_timestamp(context).await, + ) + .await; context.emit_event(EventType::ChatModified(chat_id)); } } diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 595580ecb..c06ab5c13 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -1347,7 +1347,9 @@ async fn update_gossip_peerstates( peerstate = Some(p); } if let Some(peerstate) = peerstate { - peerstate.handle_fingerprint_change(context).await?; + peerstate + .handle_fingerprint_change(context, message_time) + .await?; } gossipped_addr.insert(header.addr.clone()); diff --git a/src/peerstate.rs b/src/peerstate.rs index ecf0a7bf1..669f35bb5 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -260,7 +260,11 @@ impl Peerstate { } /// Adds a warning to the chat corresponding to peerstate if fingerprint has changed. - pub(crate) async fn handle_fingerprint_change(&self, context: &Context) -> Result<()> { + pub(crate) async fn handle_fingerprint_change( + &self, + context: &Context, + timestamp: i64, + ) -> Result<()> { if self.fingerprint_changed { if let Some(contact_id) = context .sql @@ -273,7 +277,7 @@ impl Peerstate { let msg = stock_str::contact_setup_changed(context, self.addr.clone()).await; - chat::add_info_msg(context, chat_id, msg).await; + chat::add_info_msg(context, chat_id, msg, timestamp).await; emit_event!(context, EventType::ChatModified(chat_id)); } else { bail!("contact with peerstate.addr {:?} not found", &self.addr); diff --git a/src/qr.rs b/src/qr.rs index 3170e20d0..866df1fdd 100644 --- a/src/qr.rs +++ b/src/qr.rs @@ -11,6 +11,7 @@ use crate::config::Config; use crate::constants::Blocked; use crate::contact::{addr_normalize, may_be_valid_addr, Contact, Origin}; use crate::context::Context; +use crate::dc_tools::time; use crate::key::Fingerprint; use crate::log::LogExt; use crate::lot::{Lot, LotState}; @@ -160,7 +161,13 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Lot { .await .log_err(context, "Failed to create (new) chat for contact") { - chat::add_info_msg(context, chat.id, format!("{} verified.", peerstate.addr)).await; + chat::add_info_msg( + context, + chat.id, + format!("{} verified.", peerstate.addr), + time(), + ) + .await; } } else if let Some(addr) = addr { lot.state = LotState::QrFprMismatch; diff --git a/src/securejoin.rs b/src/securejoin.rs index e6f254403..bdb56a131 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -14,6 +14,7 @@ use crate::config::Config; use crate::constants::{Blocked, Viewtype, DC_CONTACT_ID_LAST_SPECIAL}; use crate::contact::{Contact, Origin, VerifiedStatus}; use crate::context::Context; +use crate::dc_tools::time; use crate::e2ee::ensure_secret_key_exists; use crate::events::EventType; use crate::headerdef::HeaderDef; @@ -859,7 +860,7 @@ async fn secure_connection_established( "?" }; let msg = stock_str::contact_verified(context, addr).await; - chat::add_info_msg(context, contact_chat_id, msg).await; + chat::add_info_msg(context, contact_chat_id, msg, time()).await; emit_event!(context, EventType::ChatModified(contact_chat_id)); info!(context, "StockMessage::ContactVerified posted to 1:1 chat"); @@ -883,7 +884,7 @@ async fn could_not_establish_secure_connection( ) .await; - chat::add_info_msg(context, contact_chat_id, &msg).await; + chat::add_info_msg(context, contact_chat_id, &msg, time()).await; error!( context, "StockMessage::ContactNotVerified posted to 1:1 chat ({})", details