summaryrefslogtreecommitdiffstats
path: root/melib/src/backends
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2020-08-28 00:24:43 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2020-08-28 00:31:35 +0300
commitb4fe34eacf7ac41ad0983d2aaaa39d18e930f3a0 (patch)
treef00a8db294e846db3d3a5560837e93731bc3a324 /melib/src/backends
parente878c50af5f208557eae7099ea408aaa6fb4a5ae (diff)
melib/imap: add ImapCache trait
Diffstat (limited to 'melib/src/backends')
-rw-r--r--melib/src/backends/imap.rs57
-rw-r--r--melib/src/backends/imap/cache.rs469
-rw-r--r--melib/src/backends/imap/cache/sync.rs81
-rw-r--r--melib/src/backends/imap/connection.rs28
-rw-r--r--melib/src/backends/imap/mailbox.rs15
-rw-r--r--melib/src/backends/imap/operations.rs14
-rw-r--r--melib/src/backends/imap/protocol_parser.rs87
-rw-r--r--melib/src/backends/imap/untagged.rs26
-rw-r--r--melib/src/backends/imap/watch.rs28
9 files changed, 533 insertions, 272 deletions
diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs
index 2f7ef811..648ee2b6 100644
--- a/melib/src/backends/imap.rs
+++ b/melib/src/backends/imap.rs
@@ -51,12 +51,17 @@ use futures::stream::Stream;
use std::collections::{hash_map::DefaultHasher, BTreeMap};
use std::collections::{BTreeSet, HashMap, HashSet};
use std::convert::TryFrom;
+use std::convert::TryInto;
use std::hash::Hasher;
use std::pin::Pin;
use std::str::FromStr;
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant};
-pub type UID = usize;
+
+pub type ImapNum = usize;
+pub type UID = ImapNum;
+pub type UIDVALIDITY = UID;
+pub type MessageSequenceNumber = ImapNum;
pub static SUPPORTED_CAPABILITIES: &[&str] = &[
#[cfg(feature = "deflate_compression")]
@@ -202,7 +207,6 @@ pub struct ImapType {
connection: Arc<FutureMutex<ImapConnection>>,
server_conf: ImapServerConf,
uid_store: Arc<UIDStore>,
- can_create_flags: Arc<Mutex<bool>>,
}
impl MailBackend for ImapType {
@@ -299,7 +303,6 @@ impl MailBackend for ImapType {
},
connection: self.connection.clone(),
mailbox_hash,
- can_create_flags: self.can_create_flags.clone(),
uid_store: self.uid_store.clone(),
};
@@ -742,11 +745,7 @@ impl MailBackend for ImapType {
}
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
- if *self.can_create_flags.lock().unwrap() {
- Some(self.uid_store.tag_index.clone())
- } else {
- None
- }
+ Some(self.uid_store.tag_index.clone())
}
fn as_any(&self) -> &dyn Any {
@@ -1111,7 +1110,7 @@ impl MailBackend for ImapType {
l["* SEARCH".len()..]
.trim()
.split_whitespace()
- .map(usize::from_str)
+ .map(UID::from_str)
.filter_map(std::result::Result::ok)
.filter_map(|uid| uid_index.get(&(mailbox_hash, uid)))
.copied(),
@@ -1157,7 +1156,17 @@ impl ImapType {
let use_starttls = use_tls && get_conf_val!(s["use_starttls"], !(server_port == 993))?;
let danger_accept_invalid_certs: bool =
get_conf_val!(s["danger_accept_invalid_certs"], false)?;
+ #[cfg(feature = "sqlite3")]
let keep_offline_cache = get_conf_val!(s["offline_cache"], true)?;
+ #[cfg(not(feature = "sqlite3"))]
+ let keep_offline_cache = get_conf_val!(s["offline_cache"], false)?;
+ #[cfg(not(feature = "sqlite3"))]
+ if keep_offline_cache {
+ return Err(MeliError::new(format!(
+ "({}) keep_offline_cache is true but melib is not compiled with sqlite3",
+ s.name,
+ )));
+ }
let server_conf = ImapServerConf {
server_hostname: server_hostname.to_string(),
server_username: server_username.to_string(),
@@ -1190,7 +1199,6 @@ impl ImapType {
Ok(Box::new(ImapType {
server_conf,
is_subscribed: Arc::new(IsSubscribedFn(is_subscribed)),
- can_create_flags: Arc::new(Mutex::new(false)),
connection: Arc::new(FutureMutex::new(connection)),
uid_store,
}))
@@ -1199,14 +1207,18 @@ impl ImapType {
pub fn shell(&mut self) {
let mut conn = ImapConnection::new_connection(&self.server_conf, self.uid_store.clone());
- futures::executor::block_on(timeout(Duration::from_secs(3), conn.connect())).unwrap();
+ futures::executor::block_on(timeout(Duration::from_secs(3), conn.connect()))
+ .unwrap()
+ .unwrap();
let mut res = String::with_capacity(8 * 1024);
futures::executor::block_on(timeout(Duration::from_secs(3), conn.send_command(b"NOOP")))
+ .unwrap()
.unwrap();
futures::executor::block_on(timeout(
Duration::from_secs(3),
conn.read_response(&mut res, RequiredResponses::empty()),
))
+ .unwrap()
.unwrap();
let mut input = String::new();
@@ -1220,11 +1232,13 @@ impl ImapType {
Duration::from_secs(3),
conn.send_command(input.as_bytes()),
))
+ .unwrap()
.unwrap();
futures::executor::block_on(timeout(
Duration::from_secs(3),
conn.read_lines(&mut res, String::new()),
))
+ .unwrap()
.unwrap();
if input.trim().eq_ignore_ascii_case("logout") {
break;
@@ -1368,7 +1382,18 @@ impl ImapType {
)));
}
get_conf_val!(s["danger_accept_invalid_certs"], false)?;
+ #[cfg(feature = "sqlite3")]
get_conf_val!(s["offline_cache"], true)?;
+ #[cfg(not(feature = "sqlite3"))]
+ {
+ let keep_offline_cache = get_conf_val!(s["offline_cache"], false)?;
+ if keep_offline_cache {
+ return Err(MeliError::new(format!(
+ "({}) keep_offline_cache is true but melib is not compiled with sqlite3",
+ s.name,
+ )));
+ }
+ }
get_conf_val!(s["use_idle"], true)?;
get_conf_val!(s["use_condstore"], true)?;
#[cfg(feature = "deflate_compression")]
@@ -1399,7 +1424,7 @@ enum FetchStage {
InitialFresh,
InitialCache,
ResyncCache,
- FreshFetch { max_uid: usize },
+ FreshFetch { max_uid: UID },
Finished,
}
@@ -1408,7 +1433,6 @@ struct FetchState {
stage: FetchStage,
connection: Arc<FutureMutex<ImapConnection>>,
mailbox_hash: MailboxHash,
- can_create_flags: Arc<Mutex<bool>>,
uid_store: Arc<UIDStore>,
}
@@ -1423,7 +1447,6 @@ async fn fetch_hlpr(state: &mut FetchState) -> Result<Vec<Envelope>> {
.await
.init_mailbox(state.mailbox_hash)
.await?;
- *state.can_create_flags.lock().unwrap() = select_response.can_create_flags;
if select_response.exists == 0 {
state.stage = FetchStage::Finished;
return Ok(Vec::new());
@@ -1481,7 +1504,6 @@ async fn fetch_hlpr(state: &mut FetchState) -> Result<Vec<Envelope>> {
ref mut stage,
ref connection,
mailbox_hash,
- can_create_flags: _,
ref uid_store,
} = state;
let mailbox_hash = *mailbox_hash;
@@ -1565,8 +1587,9 @@ async fn fetch_hlpr(state: &mut FetchState) -> Result<Vec<Envelope>> {
}
}
}
+ #[cfg(feature = "sqlite3")]
if uid_store.keep_offline_cache {
- let mut cache_handle = cache::CacheHandle::get(uid_store.clone())?;
+ let mut cache_handle = cache::Sqlite3Cache::get(uid_store.clone())?;
debug!(cache_handle
.insert_envelopes(mailbox_hash, &v)
.chain_err_summary(|| {
@@ -1601,7 +1624,7 @@ async fn fetch_hlpr(state: &mut FetchState) -> Result<Vec<Envelope>> {
.unwrap()
.entry(mailbox_hash)
.or_default()
- .insert(message_sequence_number - 1, uid);
+ .insert((message_sequence_number - 1).try_into().unwrap(), uid);
uid_store
.hash_index
.lock()
diff --git a/melib/src/backends/imap/cache.rs b/melib/src/backends/imap/cache.rs
index b72515a8..53ec5d9b 100644
--- a/melib/src/backends/imap/cache.rs
+++ b/melib/src/backends/imap/cache.rs
@@ -26,7 +26,6 @@ use crate::{
email::{Envelope, EnvelopeHash},
error::*,
};
-
use std::convert::TryFrom;
#[derive(Debug, PartialEq, Hash, Eq, Ord, PartialOrd, Copy, Clone)]
@@ -55,10 +54,36 @@ pub struct CachedEnvelope {
pub modsequence: Option<ModSequence>,
}
-pub struct CacheHandle {
- #[cfg(feature = "sqlite3")]
- connection: crate::sqlite3::Connection,
- uid_store: Arc<UIDStore>,
+pub trait ImapCache: Send {
+ fn mailbox_state(&mut self, mailbox_hash: MailboxHash) -> Result<Option<()>>;
+
+ fn find_envelope(
+ &mut self,
+ identifier: std::result::Result<UID, EnvelopeHash>,
+ mailbox_hash: MailboxHash,
+ ) -> Result<Option<CachedEnvelope>>;
+
+ fn update(
+ &mut self,
+ mailbox_hash: MailboxHash,
+ refresh_events: &[(UID, RefreshEvent)],
+ ) -> Result<()>;
+
+ fn insert_envelopes(
+ &mut self,
+ mailbox_hash: MailboxHash,
+ fetches: &[FetchResponse<'_>],
+ ) -> Result<()>;
+
+ fn envelopes(&mut self, mailbox_hash: MailboxHash) -> Result<Option<Vec<EnvelopeHash>>>;
+
+ fn clear(&mut self, mailbox_hash: MailboxHash, select_response: &SelectResponse) -> Result<()>;
+
+ fn rfc822(
+ &mut self,
+ identifier: std::result::Result<UID, EnvelopeHash>,
+ mailbox_hash: MailboxHash,
+ ) -> Result<Option<Vec<u8>>>;
}
#[cfg(feature = "sqlite3")]
@@ -71,28 +96,43 @@ mod sqlite3_m {
FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput,
};
use crate::sqlite3::{self, DatabaseDescription};
+
+ type Sqlite3UID = i32;
+
+ pub struct Sqlite3Cache {
+ connection: crate::sqlite3::Connection,
+ loaded_mailboxes: BTreeSet<MailboxHash>,
+ uid_store: Arc<UIDStore>,
+ }
+
const DB_DESCRIPTION: DatabaseDescription = DatabaseDescription {
name: "header_cache.db",
- init_script: Some("PRAGMA foreign_keys = true;
+ init_script: Some(
+ "PRAGMA foreign_keys = true;
PRAGMA encoding = 'UTF-8';
CREATE TABLE IF NOT EXISTS envelopes (
- mailbox_hash INTEGER,
- uid INTEGER,
+ hash INTEGER NOT NULL,
+ mailbox_hash INTEGER NOT NULL,
+ uid INTEGER NOT NULL,
modsequence INTEGER,
+ rfc822 BLOB,
envelope BLOB NOT NULL,
PRIMARY KEY (mailbox_hash, uid),
- FOREIGN KEY (mailbox_hash) REFERENCES uidvalidity(mailbox_hash) ON DELETE CASCADE
+ FOREIGN KEY (mailbox_hash) REFERENCES mailbox(mailbox_hash) ON DELETE CASCADE
);
- CREATE TABLE IF NOT EXISTS uidvalidity (
- uid INTEGER UNIQUE,
+ CREATE TABLE IF NOT EXISTS mailbox (
mailbox_hash INTEGER UNIQUE,
+ uidvalidity INTEGER,
+ flags BLOB NOT NULL,
highestmodseq INTEGER,
- PRIMARY KEY (mailbox_hash, uid)
+ PRIMARY KEY (mailbox_hash)
);
- CREATE INDEX IF NOT EXISTS envelope_idx ON envelopes(mailbox_hash);
- CREATE INDEX IF NOT EXISTS uidvalidity_idx ON uidvalidity(mailbox_hash);"),
- version: 1,
+ CREATE INDEX IF NOT EXISTS envelope_uid_idx ON envelopes(mailbox_hash, uid);
+ CREATE INDEX IF NOT EXISTS envelope_idx ON envelopes(hash);
+ CREATE INDEX IF NOT EXISTS mailbox_idx ON mailbox(mailbox_hash);",
+ ),
+ version: 1,
};
impl ToSql for ModSequence {
@@ -111,47 +151,108 @@ mod sqlite3_m {
}
}
- impl CacheHandle {
- pub fn get(uid_store: Arc<UIDStore>) -> Result<Self> {
- Ok(Self {
+ impl Sqlite3Cache {
+ pub fn get(uid_store: Arc<UIDStore>) -> Result<Box<dyn ImapCache>> {
+ Ok(Box::new(Self {
connection: sqlite3::open_or_create_db(
&DB_DESCRIPTION,
Some(uid_store.account_name.as_str()),
)?,
+ loaded_mailboxes: BTreeSet::default(),
uid_store,
- })
+ }))
}
- pub fn mailbox_state(
- &self,
- mailbox_hash: MailboxHash,
- ) -> Result<Option<(UID, Option<ModSequence>)>> {
+ fn max_uid(&self, mailbox_hash: MailboxHash) -> Result<UID> {
let mut stmt = self
.connection
- .prepare("SELECT uid, highestmodseq FROM uidvalidity WHERE mailbox_hash = ?1;")?;
+ .prepare("SELECT MAX(uid) FROM envelopes WHERE mailbox_hash = ?1;")?;
+
+ let mut ret: Vec<UID> = stmt
+ .query_map(sqlite3::params![mailbox_hash as i64], |row| {
+ Ok(row.get(0).map(|i: Sqlite3UID| i as UID)?)
+ })?
+ .collect::<std::result::Result<_, _>>()?;
+ Ok(ret.pop().unwrap_or(0))
+ }
+ }
+
+ impl ImapCache for Sqlite3Cache {
+ fn mailbox_state(&mut self, mailbox_hash: MailboxHash) -> Result<Option<()>> {
+ if self.loaded_mailboxes.contains(&mailbox_hash) {
+ return Ok(Some(()));
+ }
+ debug!("loading mailbox state {} from cache", mailbox_hash);
+ let mut stmt = self.connection.prepare(
+ "SELECT uidvalidity, flags, highestmodseq FROM mailbox WHERE mailbox_hash = ?1;",
+ )?;
let mut ret = stmt.query_map(sqlite3::params![mailbox_hash as i64], |row| {
- Ok((row.get(0).map(|u: i64| u as usize)?, row.get(1)?))
+ Ok((
+ row.get(0).map(|u: Sqlite3UID| u as UID)?,
+ row.get(1)?,
+ row.get(2)?,
+ ))
})?;
- if let Some(row_res) = ret.next() {
- Ok(Some(row_res?))
+ if let Some(v) = ret.next() {
+ let (uidvalidity, flags, highestmodseq): (
+ UIDVALIDITY,
+ Vec<u8>,
+ Option<ModSequence>,
+ ) = v?;
+ debug!(
+ "mailbox state {} in cache uidvalidity {}",
+ mailbox_hash, uidvalidity
+ );
+ debug!(
+ "mailbox state {} in cache highestmodseq {:?}",
+ mailbox_hash, &highestmodseq
+ );
+ debug!(
+ "mailbox state {} inserting flags: {:?}",
+ mailbox_hash,
+ to_str!(&flags)
+ );
+ self.uid_store
+ .highestmodseqs
+ .lock()
+ .unwrap()
+ .entry(mailbox_hash)
+ .and_modify(|entry| *entry = highestmodseq.ok_or(()))
+ .or_insert(highestmodseq.ok_or(()));
+ self.uid_store
+ .uidvalidity
+ .lock()
+ .unwrap()
+ .entry(mailbox_hash)
+ .and_modify(|entry| *entry = uidvalidity)
+ .or_insert(uidvalidity);
+ let mut tag_lck = self.uid_store.tag_index.write().unwrap();
+ for f in to_str!(&flags).split('\0') {
+ let hash = tag_hash!(f);
+ //debug!("hash {} flag {}", hash, &f);
+ if !tag_lck.contains_key(&hash) {
+ tag_lck.insert(hash, f.to_string());
+ }
+ }
+ self.loaded_mailboxes.insert(mailbox_hash);
+ Ok(Some(()))
} else {
+ debug!("mailbox state {} not in cache", mailbox_hash);
Ok(None)
}
}
- pub fn clear(
- &self,
+ fn clear(
+ &mut self,
mailbox_hash: MailboxHash,
- new_uidvalidity: UID,
- highestmodseq: Option<ModSequence>,
+ select_response: &SelectResponse,
) -> Result<()> {
- debug!("clear mailbox_hash {}", mailbox_hash);
- debug!(new_uidvalidity);
- debug!(&highestmodseq);
+ debug!("clear mailbox_hash {} {:?}", mailbox_hash, select_response);
+ self.loaded_mailboxes.remove(&mailbox_hash);
self.connection
.execute(
- "DELETE FROM uidvalidity WHERE mailbox_hash = ?1",
+ "DELETE FROM mailbox WHERE mailbox_hash = ?1",
sqlite3::params![mailbox_hash as i64],
)
.chain_err_summary(|| {
@@ -161,20 +262,38 @@ mod sqlite3_m {
)
})?;
- self.connection.execute(
- "INSERT OR IGNORE INTO uidvalidity (uid, highestmodseq, mailbox_hash) VALUES (?1, ?2, ?3)",
- sqlite3::params![new_uidvalidity as i64, highestmodseq, mailbox_hash as i64],
+ if let Some(Ok(highestmodseq)) = select_response.highestmodseq {
+ self.connection.execute(
+ "INSERT OR IGNORE INTO mailbox (uidvalidity, flags, highestmodseq, mailbox_hash) VALUES (?1, ?2, ?3, ?4)",
+ sqlite3::params![select_response.uidvalidity as Sqlite3UID, select_response.flags.1.iter().map(|s| s.as_str()).collect::<Vec<&str>>().join("\0").as_bytes(), highestmodseq, mailbox_hash as i64],
)
.chain_err_summary(|| {
format!(
"Could not insert uidvalidity {} in header_cache of account {}",
- new_uidvalidity, self.uid_store.account_name
+ select_response.uidvalidity, self.uid_store.account_name
)
})?;
+ } else {
+ self.connection
+ .execute(
+ "INSERT OR IGNORE INTO mailbox (uidvalidity, flags, mailbox_hash) VALUES (?1, ?2, ?3)",
+ sqlite3::params![
+ select_response.uidvalidity as Sqlite3UID,
+ select_response.flags.1.iter().map(|s| s.as_str()).collect::<Vec<&str>>().join("\0").as_bytes(),
+ mailbox_hash as i64
+ ],
+ )
+ .chain_err_summary(|| {
+ format!(
+ "Could not insert mailbox {} in header_cache of account {}",
+ select_response.uidvalidity, self.uid_store.account_name
+ )
+ })?;
+ }
Ok(())
}
- pub fn envelopes(&self, mailbox_hash: MailboxHash) -> Result<Option<Vec<EnvelopeHash>>> {
+ fn envelopes(&mut self, mailbox_hash: MailboxHash) -> Result<Option<Vec<EnvelopeHash>>> {
debug!("envelopes mailbox_hash {}", mailbox_hash);
if debug!(self.mailbox_state(mailbox_hash)?.is_none()) {
return Ok(None);
@@ -187,7 +306,7 @@ mod sqlite3_m {
let ret: Vec<(UID, Envelope, Option<ModSequence>)> = stmt
.query_map(sqlite3::params![mailbox_hash as i64], |row| {
Ok((
- row.get(0).map(|i: i64| i as usize)?,
+ row.get(0).map(|i: Sqlite3UID| i as UID)?,
row.get(1)?,
row.get(2)?,
))
@@ -221,7 +340,7 @@ mod sqlite3_m {
Ok(Some(env_hashes))
}
- pub fn insert_envelopes(
+ fn insert_envelopes(
&mut self,
mailbox_hash: MailboxHash,
fetches: &[FetchResponse<'_>],
@@ -231,35 +350,22 @@ mod sqlite3_m {
mailbox_hash,
fetches.len()
);
+ let mut max_uid = self
+ .uid_store
+ .max_uids
+ .lock()
+ .unwrap()
+ .get(&mailbox_hash)
+ .cloned()
+ .unwrap_or_default();
if self.mailbox_state(mailbox_hash)?.is_none() {
debug!(self.mailbox_state(mailbox_hash)?.is_none());
- let uidvalidity = self
- .uid_store
- .uidvalidity
- .lock()
- .unwrap()
- .get(&mailbox_hash)
- .cloned();
- let highestmodseq = self
- .uid_store
- .highestmodseqs
- .lock()
- .unwrap()
- .get(&mailbox_hash)
- .cloned();
- debug!(&uidvalidity);
- debug!(&highestmodseq);
- if let Some(uidvalidity) = uidvalidity {
- debug!(self.clear(
- mailbox_hash,
- uidvalidity,
- highestmodseq.and_then(|v| v.ok()),
- ))?;
- }
+ return Err(MeliError::new("Mailbox is not in cache").set_kind(ErrorKind::Bug));
}
let Self {
ref mut connection,
ref uid_store,
+ loaded_mailboxes: _,
} = self;
let tx = connection.transaction()?;
for item in fetches {
@@ -272,17 +378,23 @@ mod sqlite3_m {
envelope: Some(envelope),
} = item
{
+ max_uid = std::cmp::max(max_uid, *uid);
tx.execute(
- "INSERT OR REPLACE INTO envelopes (uid, mailbox_hash, modsequence, envelope) VALUES (?1, ?2, ?3, ?4)",
- sqlite3::params![*uid as i64, mailbox_hash as i64, modseq, &envelope],
+ "INSERT OR REPLACE INTO envelopes (hash, uid, mailbox_hash, modsequence, envelope) VALUES (?1, ?2, ?3, ?4, ?5)",
+ sqlite3::params![envelope.hash() as i64, *uid as Sqlite3UID, mailbox_hash as i64, modseq, &envelope],
).chain_err_summary(|| format!("Could not insert envelope {} {} in header_cache of account {}", envelope.message_id(), envelope.hash(), uid_store.account_name))?;
}
}
tx.commit()?;
+ self.uid_store
+ .max_uids
+ .lock()
+ .unwrap()
+ .insert(mailbox_hash, max_uid);
Ok(())
}
- pub fn update(
+ fn update(
&mut self,
mailbox_hash: MailboxHash,
refresh_events: &[(UID, RefreshEvent)],
@@ -294,33 +406,12 @@ mod sqlite3_m {
);
if self.mailbox_state(mailbox_hash)?.is_none() {
debug!(self.mailbox_state(mailbox_hash)?.is_none());
- let uidvalidity = self
- .uid_store
- .uidvalidity
- .lock()
- .unwrap()
- .get(&mailbox_hash)
- .cloned();
- let highestmodseq = self
- .uid_store
- .highestmodseqs
- .lock()
- .unwrap()
- .get(&mailbox_hash)
- .cloned();
- debug!(&uidvalidity);
- debug!(&highestmodseq);
- if let Some(uidvalidity) = uidvalidity {
- debug!(self.clear(
- mailbox_hash,
- uidvalidity,
- highestmodseq.and_then(|v| v.ok()),
- ))?;
- }
+ return Err(MeliError::new("Mailbox is not in cache").set_kind(ErrorKind::Bug));
}
let Self {
ref mut connection,
ref uid_store,
+ loaded_mailboxes: _,
} = self;
let tx = connection.transaction()?;
let mut hash_index_lck = uid_store.hash_index.lock().unwrap();
@@ -330,7 +421,7 @@ mod sqlite3_m {
hash_index_lck.remove(&env_hash);
tx.execute(
"DELETE FROM envelopes WHERE mailbox_hash = ?1 AND uid = ?2;",
- sqlite3::params![mailbox_hash as i64, *uid as i64],
+ sqlite3::params![mailbox_hash as i64, *uid as Sqlite3UID],
)
.chain_err_summary(|| {
format!(
@@ -345,9 +436,10 @@ mod sqlite3_m {
)?;
let mut ret: Vec<Envelope> = stmt
- .query_map(sqlite3::params![mailbox_hash as i64, *uid as i64], |row| {
- Ok(row.get(0)?)
- })?
+ .query_map(
+ sqlite3::params![mailbox_hash as i64, *uid as Sqlite3UID],
+ |row| Ok(row.get(0)?),
+ )?
.collect::<std::result::Result<_, _>>()?;
if let Some(mut env) = ret.pop() {
env.set_flags(*flags);
@@ -355,7 +447,7 @@ mod sqlite3_m {
env.labels_mut().extend(tags.iter().map(|t| tag_hash!(t)));
tx.execute(
"UPDATE envelopes SET envelope = ?1 WHERE mailbox_hash = ?2 AND uid = ?3;",
- sqlite3::params![&env, mailbox_hash as i64, *uid as i64],
+ sqlite3::params![&env, mailbox_hash as i64, *uid as Sqlite3UID],
)
.chain_err_summary(|| {
format!(
@@ -377,48 +469,108 @@ mod sqlite3_m {
}
}
tx.commit()?;
+ let new_max_uid = self.max_uid(mailbox_hash)?;
+ self.uid_store
+ .max_uids
+ .lock()
+ .unwrap()
+ .insert(mailbox_hash, new_max_uid);
Ok(())
}
- }
-}
-
-#[cfg(not(feature = "sqlite3"))]
-pub use filesystem_m::*;
-
-#[cfg(not(feature = "sqlite3"))]
-mod filesystem_m {
- use super::*;
- impl CacheHandle {
- pub fn get(uid_store: Arc<UIDStore>) -> Result<Self> {
- Ok(Self { uid_store })
- }
- pub fn mailbox_state(
- &self,
- _mailbox_hash: MailboxHash,
- ) -> Result<Option<(UID, Option<ModSequence>)>> {
- Ok(None)
- }
+ fn find_envelope(
+ &mut self,
+ identifier: std::result::Result<UID, EnvelopeHash>,
+ mailbox_hash: MailboxHash,
+ ) -> Result<Option<CachedEnvelope>> {
+ let mut ret: Vec<(UID, Envelope, Option<ModSequence>)> = match identifier {
+ Ok(uid) => {
+ let mut stmt = self.connection.prepare(
+ "SELECT uid, envelope, modsequence FROM envelopes WHERE mailbox_hash = ?1 AND uid = ?2;",
+ )?;
- pub fn clear(
- &self,
- _mailbox_hash: MailboxHash,
- _new_uidvalidity: UID,
- _highestmodseq: Option<ModSequence>,
- ) -> Result<()> {
- Ok(())
- }
+ let x = stmt
+ .query_map(
+ sqlite3::params![mailbox_hash as i64, uid as Sqlite3UID],
+ |row| {
+ Ok((
+ row.get(0).map(|u: Sqlite3UID| u as UID)?,
+ row.get(1)?,
+ row.get(2)?,
+ ))
+ },
+ )?
+ .collect::<std::result::Result<_, _>>()?;
+ x
+ }
+ Err(env_hash) => {
+ let mut stmt = self.connection.prepare(
+ "SELECT uid, envelope, modsequence FROM envelopes WHERE mailbox_hash = ?1 AND hash = ?2;",
+ )?;
- pub fn envelopes(&self, _mailbox_hash: MailboxHash) -> Result<Option<Vec<EnvelopeHash>>> {
- Ok(None)
+ let x = stmt
+ .query_map(
+ sqlite3::params![mailbox_hash as i64, env_hash as i64],
+ |row| {
+ Ok((
+ row.get(0).map(|u: Sqlite3UID| u as UID)?,
+ row.get(1)?,
+ row.get(2)?,
+ ))
+ },
+ )?
+ .collect::<std::result::Result<_, _>>()?;
+ x
+ }
+ };
+ if ret.len() != 1 {
+ return Ok(None);
+ }
+ let (uid, inner, modsequence) = ret.pop().unwrap();
+ return Ok(Some(CachedEnvelope {
+ inner,
+ uid,
+ mailbox_hash,
+ modsequence,
+ }));
}
- pub fn insert_envelopes(
+ fn rfc822(
&mut self,
- _mailbox_hash: MailboxHash,
- _fetches: &[FetchResponse<'_>],
- ) -> Result<()> {
- Ok(())
+ identifier: std::result::Result<UID, EnvelopeHash>,
+ mailbox_hash: MailboxHash,
+ ) -> Result<Option<Vec<u8>>> {
+ let mut ret: Vec<Option<Vec<u8>>> = match identifier {
+ Ok(uid) => {
+ let mut stmt = self.connection.prepare(
+ "SELECT rfc822 FROM envelopes WHERE mailbox_hash = ?1 AND uid = ?2;",
+ )?;
+ let x = stmt
+ .query_map(
+ sqlite3::params![mailbox_hash as i64, uid as Sqlite3UID],
+ |row| Ok(row.get(0)?),
+ )?
+ .collect::<std::result::Result<_, _>>()?;
+ x
+ }
+ Err(env_hash) => {
+ let mut stmt = self.connection.prepare(
+ "SELECT rfc822 FROM envelopes WHERE mailbox_hash = ?1 AND hash = ?2;",
+ )?;
+ let x = stmt
+ .query_map(
+ sqlite3::params![mailbox_hash as i64, env_hash as i64],
+ |row| Ok(row.get(0)?),
+ )?
+ .collect::<std::result::Result<_, _>>()?;
+ x
+ }
+ };
+
+ if ret.len() != 1 {
+ return Ok(None);
+ }
+ Ok(ret.pop().unwrap())
}
}
}
@@ -428,7 +580,6 @@ pub(super) async fn fetch_cached_envs(state: &mut FetchState) -> Result<Option<V
stage: _,
ref mut connection,
mailbox_hash,
- can_create_flags: _,
ref uid_store,
} = state;
debug!(uid_store.keep_offline_cache);
@@ -481,3 +632,59 @@ pub(super) async fn fetch_cached_envs(state: &mut FetchState) -> Result<Option<V
}
}
}
+
+#[cfg(not(feature = "sqlite3"))]
+pub use default_m::*;
+
+#[cfg(not(feature = "sqlite3"))]
+mod default_m {
+ pub struct DefaultCache;
+
+ impl DefaultCache {
+ pub fn get(_uid_store: Arc<UIDStore>) -> Result<Box<dyn ImapCache>> {
+ Ok(Box::new(Self))
+ }
+ }
+
+ impl ImapCache for DefaultCache {
+ fn mailbox_state(&mut self, _mailbox_hash: MailboxHash) -> Result<Option<()>> {
+ Err(MeliError::new("melib is not built with any imap cache").set_kind(ErrorKind::Bug))
+ }
+
+ fn clear(
+ &mut self,
+ _mailbox_hash: MailboxHash,
+ _select_response: &SelectResponse,
+ ) -> Result<()> {
+ Err(MeliError::new("melib is not built with any imap cache").set_kind(ErrorKind::Bug))
+ }
+
+ fn envelopes(&mut self, _mailbox_hash: MailboxHash) -> Result<Option<Vec<EnvelopeHash>>> {
+ Err(MeliError::new("melib is not built with any imap cache").set_kind(ErrorKind::Bug))
+ }
+
+ fn insert_envelopes(
+ &mut self,
+ _mailbox_hash: MailboxHash,
+ _fetches: &[FetchResponse<'_>],
+ ) -> Result<()> {
+ Err(MeliError::new("melib is not built with any imap cache").set_kind(ErrorKind::Bug))
+ }
+
+ fn update(
+ &mut self,
+ _mailbox_hash: MailboxHash,
+ _refresh_events: &[(UID, RefreshEvent)],
+ ) -> Result<()> {
+ Err(MeliError::new("melib is not built with any imap cache").set_kind(ErrorKind::Bug))
+ }
+
+ fn find_envelope(
+ &mut self,
+ _identifier: std::result::Result<UID, EnvelopeHash>,
+ _mailbox_hash: MailboxHash,
+ ) -> Result<Option<CachedEnvelope>> {
+ Err(MeliError::new("melib is not built with any imap cache").set_kind(ErrorKind::Bug))
+ }
+ }
+}
diff --git a/melib/src/backends/imap/cache/sync.rs b/melib/src/backends/imap/cache/sync.rs
index f390a9d4..7569fc82 100644
--- a/melib/src/backends/imap/cache/sync.rs
+++ b/melib/src/backends/imap/cache/sync.rs
@@ -29,7 +29,10 @@ impl ImapConnection {
return Ok(None);
}
- let cache_handle = CacheHandle::get(self.uid_store.clone())?;
+ #[cfg(not(feature = "sqlite3"))]
+ let mut cache_handle = DefaultCache::get(self.uid_store.clone())?;
+ #[cfg(feature = "sqlite3")]
+ let mut cache_handle = Sqlite3Cache::get(self.uid_store.clone())?;
if cache_handle.mailbox_state(mailbox_hash)?.is_none() {
return Ok(None);
}
@@ -52,29 +55,23 @@ impl ImapConnection {
mailbox_hash: MailboxHash,
) -> Option<Result<Vec<EnvelopeHash>>> {
debug!("load_cache {}", mailbox_hash);
- let cache_handle = match CacheHandle::get(self.uid_store.clone()) {
+ #[cfg(not(feature = "sqlite3"))]
+ let mut cache_handle = match DefaultCache::get(self.ui