diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 9e759ce0b..f3238a60b 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -4925,8 +4925,9 @@ void dc_event_unref(dc_event_t* event); #define DC_STR_VIDEOCHAT_INVITATION 82 #define DC_STR_VIDEOCHAT_INVITE_MSG_BODY 83 #define DC_STR_CONFIGURATION_FAILED 84 +#define DC_STR_BAD_TIME_MSG_BODY 85 -#define DC_STR_COUNT 84 +#define DC_STR_COUNT 85 /* * @} diff --git a/src/dc_tools.rs b/src/dc_tools.rs index ad22ac6f8..8a39195d5 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -15,9 +15,14 @@ use async_std::{fs, io}; use chrono::{Local, TimeZone}; use rand::{thread_rng, Rng}; +use crate::chat::add_device_msg_with_importance; +use crate::constants::Viewtype; use crate::context::Context; use crate::error::{bail, Error}; use crate::events::EventType; +use crate::message::Message; +use crate::provider::get_provider_update_timestamp; +use crate::stock::StockMessage; /// Shortens a string to a specified length and adds "[...]" to the /// end of the shortened string. @@ -151,6 +156,43 @@ pub(crate) async fn dc_create_smeared_timestamps(context: &Context, count: usize start } +// if the system time is not plausible, once a day, add a device message. +// for testing we're using time() as that is also used for message timestamps. +pub(crate) async fn maybe_add_time_based_warnings(context: &Context) { + maybe_warn_on_bad_time(context, time(), get_provider_update_timestamp()).await; +} + +async fn maybe_warn_on_bad_time(context: &Context, now: i64, known_past_timestamp: i64) { + if now < known_past_timestamp { + let mut msg = Message::new(Viewtype::Text); + msg.text = Some( + context + .stock_string_repl_str( + StockMessage::BadTimeMsgBody, + Local + .timestamp(now, 0) + .format("%Y-%m-%d %H:%M:%S") + .to_string(), + ) + .await, + ); + add_device_msg_with_importance( + context, + Some( + format!( + "bad-time-warning-{}", + chrono::NaiveDateTime::from_timestamp(now, 0).format("%Y-%m-%d") + ) + .as_str(), + ), + Some(&mut msg), + true, + ) + .await + .ok(); + } +} + /* Message-ID tools */ pub(crate) fn dc_create_id() -> String { /* generate an id. the generated ID should be as short and as unique as possible: @@ -800,6 +842,8 @@ mod tests { assert_eq!("@d.tt".parse::().is_ok(), false); } + use crate::chatlist::Chatlist; + use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; use proptest::prelude::*; proptest! { @@ -993,4 +1037,32 @@ mod tests { assert_eq!(improve_single_line_input("Hi\naiae "), "Hi aiae"); assert_eq!(improve_single_line_input("\r\nahte\n\r"), "ahte"); } + + #[async_std::test] + async fn test_maybe_warn_on_bad_time() { + let t = TestContext::new().await; + let timestamp_now = time(); + let timestamp_future = timestamp_now + 60 * 60 * 24 * 7; + let timestamp_past = NaiveDateTime::new( + NaiveDate::from_ymd(2020, 9, 1), + NaiveTime::from_hms(0, 0, 0), + ) + .timestamp_millis() + / 1_000; + + // a correct time must not add a device message + maybe_warn_on_bad_time(&t.ctx, timestamp_now, get_provider_update_timestamp()).await; + let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap(); + assert_eq!(chats.len(), 0); + + // we cannot find out if a date in the future is wrong - a device message is not added + maybe_warn_on_bad_time(&t.ctx, timestamp_future, get_provider_update_timestamp()).await; + let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap(); + assert_eq!(chats.len(), 0); + + // a date in the past must add a device message + maybe_warn_on_bad_time(&t.ctx, timestamp_past, get_provider_update_timestamp()).await; + let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap(); + assert_eq!(chats.len(), 1); + } } diff --git a/src/scheduler.rs b/src/scheduler.rs index 9688bbba1..6f2ea5ac5 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -3,6 +3,7 @@ use async_std::sync::{channel, Receiver, Sender}; use async_std::task; use crate::context::Context; +use crate::dc_tools::maybe_add_time_based_warnings; use crate::imap::Imap; use crate::job::{self, Thread}; use crate::{config::Config, message::MsgId, smtp::Smtp}; @@ -81,6 +82,8 @@ async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConne warn!(ctx, "failed to close folder: {:?}", err); } + maybe_add_time_based_warnings(&ctx).await; + info = if ctx.get_config_bool(Config::InboxWatch).await { fetch_idle(&ctx, &mut connection, Config::ConfiguredInboxFolder).await } else { diff --git a/src/stock.rs b/src/stock.rs index 226a0946c..dcb3ddac7 100644 --- a/src/stock.rs +++ b/src/stock.rs @@ -219,6 +219,12 @@ pub enum StockMessage { #[strum(props(fallback = "Configuration failed. Error: “%1$s”"))] ConfigurationFailed = 84, + + #[strum(props( + fallback = "⚠️ Date or time of your device seem to be inaccurate (%1$s).\n\n\ + Adjust your clock ⏰🔧 to ensure your messages are received correctly." + ))] + BadTimeMsgBody = 85, } /*