From c8da6d2049d0f9db1902e088a2205500f9a9f1d1 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sun, 5 Sep 2021 12:09:29 +0300 Subject: [PATCH] melib/nntp: implement refresh --- melib/src/backends/nntp.rs | 94 +++++++++++++++++++++++++++++- melib/src/backends/nntp/mailbox.rs | 3 + 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/melib/src/backends/nntp.rs b/melib/src/backends/nntp.rs index 8486af7e..9a0cfc65 100644 --- a/melib/src/backends/nntp.rs +++ b/melib/src/backends/nntp.rs @@ -79,6 +79,14 @@ pub static SUPPORTED_CAPABILITIES: &[&str] = &[ #[cfg(feature = "deflate_compression")] "COMPRESS DEFLATE", "VERSION 2", + "NEWNEWS", + "POST", + "OVER", + "OVER MSGID", + "READER", + "STARTTLS", + "HDR", + "AUTHINFO USER", ]; #[derive(Debug, Clone)] @@ -102,6 +110,7 @@ pub struct UIDStore { account_name: Arc, offline_cache: bool, capabilities: Arc>, + message_id_index: Arc>>, hash_index: Arc>>, uid_index: Arc>>, @@ -123,6 +132,7 @@ impl UIDStore { event_consumer, offline_cache: false, capabilities: Default::default(), + message_id_index: Default::default(), hash_index: Default::default(), uid_index: Default::default(), mailboxes: Arc::new(FutureMutex::new(Default::default())), @@ -234,8 +244,77 @@ impl MailBackend for NntpType { })) } - fn refresh(&mut self, _mailbox_hash: MailboxHash) -> ResultFuture<()> { - Err(MeliError::new("Unimplemented.")) + fn refresh(&mut self, mailbox_hash: MailboxHash) -> ResultFuture<()> { + let uid_store = self.uid_store.clone(); + let connection = self.connection.clone(); + Ok(Box::pin(async move { + /* To get updates, either issue NEWNEWS if it's supported by the server, and fallback + * to OVER otherwise */ + let mbox: NntpMailbox = uid_store.mailboxes.lock().await.get(&mailbox_hash).map(std::clone::Clone::clone).ok_or_else(|| MeliError::new(format!("Mailbox with hash {} not found in NNTP connection, this could possibly be a bug or it was deleted.", mailbox_hash)))?; + let mut latest_article: Option = + mbox.latest_article.lock().unwrap().clone(); + let (over_msgid_support, newnews_support): (bool, bool) = { + let caps = uid_store.capabilities.lock().unwrap(); + + ( + caps.iter().any(|c| c.eq_ignore_ascii_case("OVER MSGID")), + caps.iter().any(|c| c.eq_ignore_ascii_case("NEWNEWS")), + ) + }; + let mut res = String::with_capacity(8 * 1024); + let mut conn = timeout(Some(Duration::from_secs(60 * 16)), connection.lock()).await?; + if let Some(ref mut latest_article) = latest_article { + let timestamp = *latest_article - 10 * 60; + let datetime_str = + crate::datetime::timestamp_to_string(timestamp, Some("%Y%m%d %H%M%S"), true); + + if newnews_support { + conn.send_command( + format!("NEWNEWS {} {}", &mbox.nntp_path, datetime_str).as_bytes(), + ) + .await?; + conn.read_response(&mut res, true, &["230 "]).await?; + let message_ids = { + let message_id_lck = uid_store.message_id_index.lock().unwrap(); + res.split_rn() + .skip(1) + .map(|s| s.trim()) + .filter(|msg_id| !message_id_lck.contains_key(*msg_id)) + .map(str::to_string) + .collect::>() + }; + if message_ids.is_empty() || !over_msgid_support { + return Ok(()); + } + for msg_id in message_ids { + conn.send_command(format!("OVER {}", msg_id).as_bytes()) + .await?; + conn.read_response(&mut res, true, &["224 "]).await?; + let mut message_id_lck = uid_store.message_id_index.lock().unwrap(); + let mut hash_index_lck = uid_store.hash_index.lock().unwrap(); + let mut uid_index_lck = uid_store.uid_index.lock().unwrap(); + for l in res.split_rn().skip(1) { + let (_, (num, env)) = protocol_parser::over_article(&l)?; + message_id_lck.insert(env.message_id_display().to_string(), env.hash()); + hash_index_lck.insert(env.hash(), (num, mailbox_hash)); + uid_index_lck.insert((mailbox_hash, num), env.hash()); + *latest_article = std::cmp::max(*latest_article, env.timestamp); + (uid_store.event_consumer)( + uid_store.account_hash, + crate::backends::BackendEvent::Refresh(RefreshEvent { + mailbox_hash, + account_hash: uid_store.account_hash, + kind: RefreshEventKind::Create(Box::new(env)), + }), + ); + } + } + return Ok(()); + } + } + //conn.select_group(mailbox_hash, false, &mut res).await?; + Ok(()) + })) } fn mailboxes(&self) -> ResultFuture> { @@ -496,6 +575,7 @@ impl NntpType { nntp_path: k.to_string(), high_watermark: Arc::new(Mutex::new(0)), low_watermark: Arc::new(Mutex::new(0)), + latest_article: Arc::new(Mutex::new(None)), exists: Default::default(), unseen: Default::default(), }, @@ -705,6 +785,7 @@ impl FetchState { let new_low = std::cmp::max(low, high.saturating_sub(CHUNK_SIZE)); high_low_total.as_mut().unwrap().0 = new_low; + // FIXME: server might not implement OVER capability conn.send_command(format!("OVER {}-{}", new_low, high).as_bytes()) .await?; conn.read_response(&mut res, true, command_to_replycodes("OVER")) @@ -718,19 +799,28 @@ impl FetchState { let mut ret = Vec::with_capacity(high - new_low); //hash_index: Arc>>, //uid_index: Arc>>, + let mut latest_article: Option = None; { + let mut message_id_lck = uid_store.message_id_index.lock().unwrap(); let mut hash_index_lck = uid_store.hash_index.lock().unwrap(); let mut uid_index_lck = uid_store.uid_index.lock().unwrap(); for l in res.split_rn().skip(1) { let (_, (num, env)) = protocol_parser::over_article(&l)?; + message_id_lck.insert(env.message_id_display().to_string(), env.hash()); hash_index_lck.insert(env.hash(), (num, mailbox_hash)); uid_index_lck.insert((mailbox_hash, num), env.hash()); + if let Some(ref mut v) = latest_article { + *v = std::cmp::max(*v, env.timestamp); + } else { + latest_article = Some(env.timestamp); + } ret.push(env); } } { let hash_set: BTreeSet = ret.iter().map(|env| env.hash()).collect(); let f = &uid_store.mailboxes.lock().await[&mailbox_hash]; + *f.latest_article.lock().unwrap() = latest_article; f.exists .lock() .unwrap() diff --git a/melib/src/backends/nntp/mailbox.rs b/melib/src/backends/nntp/mailbox.rs index 9ab5503a..e85778ef 100644 --- a/melib/src/backends/nntp/mailbox.rs +++ b/melib/src/backends/nntp/mailbox.rs @@ -22,6 +22,7 @@ use crate::backends::{ BackendMailbox, LazyCountSet, Mailbox, MailboxHash, MailboxPermissions, SpecialUsageMailbox, }; use crate::error::*; +use crate::UnixTimestamp; use std::sync::{Arc, Mutex}; #[derive(Debug, Default, Clone)] @@ -34,6 +35,8 @@ pub struct NntpMailbox { pub exists: Arc>, pub unseen: Arc>, + + pub latest_article: Arc>>, } impl NntpMailbox {