diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index c93fced32..70fad4937 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -20,6 +20,7 @@ use std::fmt::Write; use std::ptr; use std::str::FromStr; use std::sync::RwLock; +use std::time::{Duration, SystemTime}; use libc::uintptr_t; use num_traits::{FromPrimitive, ToPrimitive}; @@ -34,9 +35,6 @@ use deltachat::message::MsgId; use deltachat::stock::StockMessage; use deltachat::*; -mod tools; -use crate::tools::time; - mod dc_array; mod string; @@ -1421,14 +1419,18 @@ pub unsafe extern "C" fn dc_set_chat_mute_duration( eprintln!("ignoring careless call to dc_set_chat_mute_duration()"); return 0; } - + let ffi_context = &*context; let muteDuration = match duration { 0 => MuteDuration::NotMuted, -1 => MuteDuration::Forever, - _ => MuteDuration::Until(time() + duration), + n if n > 0 => MuteDuration::Until(SystemTime::now() + Duration::from_secs(duration as u64)), + _ => { + ffi_context.warning( + "dc_chat_set_mute_duration(): Can not use negative duration other than -1", + ); + return 0; + } }; - - let ffi_context = &*context; ffi_context .with_inner(|ctx| { chat::set_muted(ctx, ChatId::new(chat_id), muteDuration) @@ -2532,10 +2534,14 @@ pub unsafe extern "C" fn dc_chat_get_remaining_mute_duration(chat: *mut dc_chat_ if !ffi_chat.chat.is_muted() { return 0; } + // If the chat was muted to before the epoch, it is not muted. match ffi_chat.chat.mute_duration { MuteDuration::NotMuted => 0, MuteDuration::Forever => -1, - MuteDuration::Until(timestamp) => timestamp - time(), + MuteDuration::Until(when) => when + .duration_since(SystemTime::UNIX_EPOCH) + .map(|d| d.as_secs() as i64) + .unwrap_or(0), } } diff --git a/deltachat-ffi/src/tools.rs b/deltachat-ffi/src/tools.rs deleted file mode 100644 index e7f7e8bb7..000000000 --- a/deltachat-ffi/src/tools.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::time::SystemTime; - -pub(crate) fn time() -> i64 { - SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap_or_default() - .as_secs() as i64 -} diff --git a/src/chat.rs b/src/chat.rs index 13fc7a4fc..02f81a661 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1,6 +1,8 @@ //! # Chat module +use std::convert::TryFrom; use std::path::{Path, PathBuf}; +use std::time::{Duration, SystemTime}; use itertools::Itertools; use num_traits::FromPrimitive; @@ -691,7 +693,7 @@ impl Chat { match self.mute_duration { MuteDuration::NotMuted => false, MuteDuration::Forever => true, - MuteDuration::Until(timestamp) => timestamp > time(), + MuteDuration::Until(when) => when > SystemTime::now(), } } @@ -1921,17 +1923,23 @@ pub fn shall_attach_selfavatar(context: &Context, chat_id: ChatId) -> Result rusqlite::Result { - let duration = match &self { + let duration: i64 = match &self { MuteDuration::NotMuted => 0, MuteDuration::Forever => -1, - MuteDuration::Until(timestamp) => *timestamp as i64, + MuteDuration::Until(when) => { + let duration = when + .duration_since(SystemTime::UNIX_EPOCH) + .map_err(|err| rusqlite::Error::ToSqlConversionFailure(Box::new(err)))?; + i64::try_from(duration.as_secs()) + .map_err(|err| rusqlite::Error::ToSqlConversionFailure(Box::new(err)))? + } }; - let val = rusqlite::types::Value::Integer(duration as i64); + let val = rusqlite::types::Value::Integer(duration); let out = rusqlite::types::ToSqlOutput::Owned(val); Ok(out) } @@ -1939,22 +1947,17 @@ impl rusqlite::types::ToSql for MuteDuration { impl rusqlite::types::FromSql for MuteDuration { fn column_result(value: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult { - // Would be nice if we could use match here, but alas. - i64::column_result(value).and_then(|val| { - Ok({ - match val { - 0 => MuteDuration::NotMuted, - -1 => MuteDuration::Forever, - _ => { - if val <= time() { - MuteDuration::NotMuted - } else { - MuteDuration::Until(val) - } - } - } - }) - }) + // Negative values other than -1 should not be in the + // database. If found they'll be NotMuted. + match i64::column_result(value)? { + 0 => Ok(MuteDuration::NotMuted), + -1 => Ok(MuteDuration::Forever), + n if n > 0 => match SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(n as u64)) { + Some(t) => Ok(MuteDuration::Until(t)), + None => Err(rusqlite::types::FromSqlError::OutOfRange(n)), + }, + _ => Ok(MuteDuration::NotMuted), + } } } @@ -1973,7 +1976,6 @@ pub fn set_muted(context: &Context, chat_id: ChatId, duration: MuteDuration) -> } else { bail!("Failed to set name"); } - Ok(()) } @@ -2877,13 +2879,23 @@ mod tests { false ); // Timed in the future - set_muted(&t.ctx, chat_id, MuteDuration::Until(time() + 3600)).unwrap(); + set_muted( + &t.ctx, + chat_id, + MuteDuration::Until(SystemTime::now() + Duration::from_secs(3600)), + ) + .unwrap(); assert_eq!( Chat::load_from_db(&t.ctx, chat_id).unwrap().is_muted(), true ); // Time in the past - set_muted(&t.ctx, chat_id, MuteDuration::Until(time() - 3600)).unwrap(); + set_muted( + &t.ctx, + chat_id, + MuteDuration::Until(SystemTime::now() - Duration::from_secs(3600)), + ) + .unwrap(); assert_eq!( Chat::load_from_db(&t.ctx, chat_id).unwrap().is_muted(), false