imap_async: add operations

async
Manos Pitsidianakis 2020-06-29 00:16:07 +03:00
parent c82367e00d
commit 42419327f8
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 154 additions and 150 deletions

View File

@ -26,8 +26,8 @@ mod protocol_parser;
pub use protocol_parser::{UntaggedResponse::*, *}; pub use protocol_parser::{UntaggedResponse::*, *};
mod mailbox; mod mailbox;
pub use mailbox::*; pub use mailbox::*;
//mod operations; mod operations;
//pub use operations::*; pub use operations::*;
mod connection; mod connection;
pub use connection::*; pub use connection::*;
mod watch; mod watch;
@ -195,7 +195,7 @@ impl MailBackend for ImapType {
loop { loop {
let res = get_hlpr(&connection, mailbox_hash,&cached_hash_set, &can_create_flags, &mut our_unseen, &mut valid_hash_set, &uid_store, &mut max_uid).await?; let res = get_hlpr(&connection, mailbox_hash,&cached_hash_set, &can_create_flags, &mut our_unseen, &mut valid_hash_set, &uid_store, &mut max_uid).await?;
yield res; yield res;
if max_uid == Some(1) { if max_uid == Some(1) || max_uid == Some(0) {
return; return;
} }
@ -789,8 +789,6 @@ impl MailBackend for ImapType {
} }
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> { fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
unimplemented!()
/*
let (uid, mailbox_hash) = if let Some(v) = let (uid, mailbox_hash) = if let Some(v) =
self.uid_store.hash_index.lock().unwrap().get(&hash) self.uid_store.hash_index.lock().unwrap().get(&hash)
{ {
@ -809,7 +807,6 @@ impl MailBackend for ImapType {
self.connection.clone(), self.connection.clone(),
self.uid_store.clone(), self.uid_store.clone(),
))) )))
*/
} }
fn save(&self, bytes: &[u8], mailbox_hash: MailboxHash, flags: Option<Flag>) -> Result<()> { fn save(&self, bytes: &[u8], mailbox_hash: MailboxHash, flags: Option<Flag>) -> Result<()> {
@ -1639,15 +1636,13 @@ async fn get_hlpr(
} }
if examine_response.exists == 0 { if examine_response.exists == 0 {
if uid_store.cache_headers { if uid_store.cache_headers {
/* for &env_hash in cached_hash_set {
for &env_hash in &cached_hash_set {
conn.add_refresh_event(RefreshEvent { conn.add_refresh_event(RefreshEvent {
account_hash: uid_store.account_hash, account_hash: uid_store.account_hash,
mailbox_hash, mailbox_hash,
kind: RefreshEventKind::Remove(env_hash), kind: RefreshEventKind::Remove(env_hash),
}); });
} }
*/
let _ = cache::save_envelopes( let _ = cache::save_envelopes(
uid_store.account_hash, uid_store.account_hash,
mailbox_hash, mailbox_hash,
@ -1713,7 +1708,6 @@ async fn get_hlpr(
mailbox_path mailbox_path
) )
})?; })?;
drop(conn);
debug!( debug!(
"fetch response is {} bytes and {} lines", "fetch response is {} bytes and {} lines",
response.len(), response.len(),
@ -1778,11 +1772,10 @@ async fn get_hlpr(
debug!("sending payload for {}", mailbox_hash); debug!("sending payload for {}", mailbox_hash);
if uid_store.cache_headers { if uid_store.cache_headers {
//FIXME //FIXME
/*
cache::save_envelopes( cache::save_envelopes(
uid_store.account_hash, uid_store.account_hash,
mailbox_hash, mailbox_hash,
examine_response.uidvalidity, uid_store.uidvalidity.lock().unwrap()[&mailbox_hash],
&envelopes &envelopes
.iter() .iter()
.map(|(uid, env)| (*uid, env)) .map(|(uid, env)| (*uid, env))
@ -1794,9 +1787,7 @@ async fn get_hlpr(
mailbox_path mailbox_path
) )
})?; })?;
*/
} }
/*
for &env_hash in cached_hash_set.difference(&valid_hash_set) { for &env_hash in cached_hash_set.difference(&valid_hash_set) {
conn.add_refresh_event(RefreshEvent { conn.add_refresh_event(RefreshEvent {
account_hash: uid_store.account_hash, account_hash: uid_store.account_hash,
@ -1804,7 +1795,7 @@ async fn get_hlpr(
kind: RefreshEventKind::Remove(env_hash), kind: RefreshEventKind::Remove(env_hash),
}); });
} }
*/ drop(conn);
unseen unseen
.lock() .lock()
.unwrap() .unwrap()

View File

@ -21,7 +21,7 @@
use super::*; use super::*;
use crate::backends::BackendOp; use crate::backends::*;
use crate::email::*; use crate::email::*;
use crate::error::{MeliError, Result}; use crate::error::{MeliError, Result};
use std::cell::Cell; use std::cell::Cell;
@ -37,7 +37,7 @@ pub struct ImapOp {
mailbox_path: String, mailbox_path: String,
mailbox_hash: MailboxHash, mailbox_hash: MailboxHash,
flags: Cell<Option<Flag>>, flags: Cell<Option<Flag>>,
connection: Arc<Mutex<ImapConnection>>, connection: Arc<FutureMutex<ImapConnection>>,
uid_store: Arc<UIDStore>, uid_store: Arc<UIDStore>,
} }
@ -46,7 +46,7 @@ impl ImapOp {
uid: usize, uid: usize,
mailbox_path: String, mailbox_path: String,
mailbox_hash: MailboxHash, mailbox_hash: MailboxHash,
connection: Arc<Mutex<ImapConnection>>, connection: Arc<FutureMutex<ImapConnection>>,
uid_store: Arc<UIDStore>, uid_store: Arc<UIDStore>,
) -> Self { ) -> Self {
ImapOp { ImapOp {
@ -77,164 +77,177 @@ impl BackendOp for ImapOp {
} else { } else {
drop(cache); drop(cache);
drop(bytes_cache); drop(bytes_cache);
let mut response = String::with_capacity(8 * 1024); let ret: Result<()> = futures::executor::block_on(async {
{ let mut response = String::with_capacity(8 * 1024);
let mut conn = {
try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; let mut conn = self.connection.lock().await;
conn.examine_mailbox(self.mailbox_hash, &mut response)?; conn.examine_mailbox(self.mailbox_hash, &mut response)
conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes())?; .await?;
conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)?; conn.send_command(
} format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes(),
debug!( )
"fetch response is {} bytes and {} lines", .await?;
response.len(), conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)
response.lines().collect::<Vec<&str>>().len() .await?;
); }
let UidFetchResponse { debug!(
uid, flags, body, .. "fetch response is {} bytes and {} lines",
} = protocol_parser::uid_fetch_response(&response)?.1; response.len(),
assert_eq!(uid, self.uid); response.lines().collect::<Vec<&str>>().len()
assert!(body.is_some()); );
let mut bytes_cache = self.uid_store.byte_cache.lock()?; let UidFetchResponse {
let cache = bytes_cache.entry(self.uid).or_default(); uid, flags, body, ..
if let Some((flags, _)) = flags { } = protocol_parser::uid_fetch_response(&response)?.1;
self.flags.set(Some(flags)); assert_eq!(uid, self.uid);
cache.flags = Some(flags); assert!(body.is_some());
} let mut bytes_cache = self.uid_store.byte_cache.lock()?;
cache.bytes = let cache = bytes_cache.entry(self.uid).or_default();
Some(unsafe { std::str::from_utf8_unchecked(body.unwrap()).to_string() }); if let Some((flags, _)) = flags {
self.bytes = cache.bytes.clone(); self.flags.set(Some(flags));
cache.flags = Some(flags);
}
cache.bytes =
Some(unsafe { std::str::from_utf8_unchecked(body.unwrap()).to_string() });
self.bytes = cache.bytes.clone();
Ok(())
});
ret?;
} }
} }
Ok(self.bytes.as_ref().unwrap().as_bytes()) Ok(self.bytes.as_ref().unwrap().as_bytes())
} }
fn fetch_flags(&self) -> Flag { fn fetch_flags(&self) -> Result<Flag> {
macro_rules! or_return_default {
($expr:expr) => {
match $expr {
Ok(ok) => ok,
Err(_) => return Default::default(),
}
};
}
if self.flags.get().is_some() { if self.flags.get().is_some() {
return self.flags.get().unwrap(); return Ok(self.flags.get().unwrap());
} }
let mut bytes_cache = or_return_default!(self.uid_store.byte_cache.lock()); let mut bytes_cache = self.uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(self.uid).or_default(); let cache = bytes_cache.entry(self.uid).or_default();
if cache.flags.is_some() { if cache.flags.is_some() {
self.flags.set(cache.flags); self.flags.set(cache.flags);
} else { } else {
let mut response = String::with_capacity(8 * 1024); futures::executor::block_on(async {
let mut conn = or_return_default!(try_lock( let mut response = String::with_capacity(8 * 1024);
&self.connection, let mut conn = self.connection.lock().await;
Some(std::time::Duration::new(2, 0)) conn.examine_mailbox(self.mailbox_hash, &mut response)
)); .await?;
or_return_default!(conn.examine_mailbox(self.mailbox_hash, &mut response));
or_return_default!(
conn.send_command(format!("UID FETCH {} FLAGS", self.uid).as_bytes()) conn.send_command(format!("UID FETCH {} FLAGS", self.uid).as_bytes())
); .await?;
or_return_default!(conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)); conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)
debug!( .await?;
"fetch response is {} bytes and {} lines", debug!(
response.len(), "fetch response is {} bytes and {} lines",
response.lines().collect::<Vec<&str>>().len() response.len(),
); response.lines().collect::<Vec<&str>>().len()
);
let v = protocol_parser::uid_fetch_flags_response(response.as_bytes())
.map(|(_, v)| v)
.map_err(MeliError::from)?;
if v.len() != 1 {
debug!("responses len is {}", v.len());
debug!(&response);
/* TODO: Trigger cache invalidation here. */
debug!(format!("message with UID {} was not found", self.uid));
return Err(MeliError::new(format!(
"Invalid/unexpected response: {:?}",
response
))
.set_summary(format!("message with UID {} was not found?", self.uid)));
}
let (uid, (flags, _)) = v[0];
assert_eq!(uid, self.uid);
cache.flags = Some(flags);
self.flags.set(Some(flags));
Ok(())
})?;
}
Ok(self.flags.get().unwrap())
}
fn set_flag(
&mut self,
flag: Flag,
value: bool,
) -> Result<Pin<Box<dyn Future<Output = Result<()>> + Send>>> {
let mut flags = self.fetch_flags()?;
flags.set(flag, value);
let mut response = String::with_capacity(8 * 1024);
let connection = self.connection.clone();
let mailbox_hash = self.mailbox_hash;
let uid = self.uid;
let uid_store = self.uid_store.clone();
Ok(Box::pin(async move {
let mut conn = connection.lock().await;
conn.select_mailbox(mailbox_hash, &mut response).await?;
debug!(&response);
conn.send_command(
format!(
"UID STORE {} FLAGS.SILENT ({})",
uid,
flags_to_imap_list!(flags)
)
.as_bytes(),
)
.await?;
conn.read_response(&mut response, RequiredResponses::STORE_REQUIRED)
.await?;
debug!(&response);
match protocol_parser::uid_fetch_flags_response(response.as_bytes()) match protocol_parser::uid_fetch_flags_response(response.as_bytes())
.map(|(_, v)| v) .map(|(_, v)| v)
.map_err(MeliError::from) .map_err(MeliError::from)
{ {
Ok(v) => { Ok(v) => {
if v.len() != 1 { if v.len() == 1 {
debug!("responses len is {}", v.len()); debug!("responses len is {}", v.len());
debug!(response); let (_uid, (flags, _)) = v[0];
/* TODO: Trigger cache invalidation here. */ assert_eq!(_uid, uid);
debug!(format!("message with UID {} was not found", self.uid));
return Flag::default();
} }
let (uid, (flags, _)) = v[0];
assert_eq!(uid, self.uid);
cache.flags = Some(flags);
self.flags.set(Some(flags));
} }
Err(e) => or_return_default!(Err(e)), Err(e) => Err(e)?,
} }
} let mut bytes_cache = uid_store.byte_cache.lock()?;
self.flags.get().unwrap() let cache = bytes_cache.entry(uid).or_default();
cache.flags = Some(flags);
Ok(())
}))
} }
fn set_flag(&mut self, f: Flag, value: bool) -> Result<()> { fn set_tag(
let mut flags = self.fetch_flags(); &mut self,
flags.set(f, value); tag: String,
value: bool,
) -> Result<Pin<Box<dyn Future<Output = Result<()>> + Send>>> {
let mut response = String::with_capacity(8 * 1024); let mut response = String::with_capacity(8 * 1024);
let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; let connection = self.connection.clone();
conn.select_mailbox(self.mailbox_hash, &mut response)?; let mailbox_hash = self.mailbox_hash;
debug!(&response); let uid = self.uid;
conn.send_command( let uid_store = self.uid_store.clone();
format!( Ok(Box::pin(async move {
"UID STORE {} FLAGS.SILENT ({})", let mut conn = connection.lock().await;
self.uid, conn.select_mailbox(mailbox_hash, &mut response).await?;
flags_to_imap_list!(flags) conn.send_command(
format!(
"UID STORE {} {}FLAGS.SILENT ({})",
uid,
if value { "+" } else { "-" },
&tag
)
.as_bytes(),
) )
.as_bytes(), .await?;
)?; conn.read_response(&mut response, RequiredResponses::STORE_REQUIRED)
conn.read_response(&mut response, RequiredResponses::STORE_REQUIRED)?; .await?;
debug!(&response); protocol_parser::uid_fetch_flags_response(response.as_bytes())
match protocol_parser::uid_fetch_flags_response(response.as_bytes()) .map(|(_, v)| v)
.map(|(_, v)| v) .map_err(MeliError::from)?;
.map_err(MeliError::from) let hash = tag_hash!(tag);
{
Ok(v) => {
if v.len() == 1 {
debug!("responses len is {}", v.len());
let (uid, (flags, _)) = v[0];
assert_eq!(uid, self.uid);
self.flags.set(Some(flags));
}
}
Err(e) => Err(e)?,
}
let mut bytes_cache = self.uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(self.uid).or_default();
cache.flags = Some(flags);
Ok(())
}
fn set_tag(&mut self, tag: String, value: bool) -> Result<()> {
let mut response = String::with_capacity(8 * 1024);
let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?;
conn.select_mailbox(self.mailbox_hash, &mut response)?;
conn.send_command(
format!(
"UID STORE {} {}FLAGS.SILENT ({})",
self.uid,
if value { "+" } else { "-" },
&tag
)
.as_bytes(),
)?;
conn.read_response(&mut response, RequiredResponses::STORE_REQUIRED)?;
protocol_parser::uid_fetch_flags_response(response.as_bytes())
.map(|(_, v)| v)
.map_err(MeliError::from)?;
let hash = tag_hash!(tag);
if value {
self.uid_store.tag_index.write().unwrap().insert(hash, tag);
} else {
self.uid_store.tag_index.write().unwrap().remove(&hash);
}
if !envelope.labels().iter().any(|&h_| h_ == hash) {
if value { if value {
envelope.labels_mut().push(hash); uid_store.tag_index.write().unwrap().insert(hash, tag);
} else {
uid_store.tag_index.write().unwrap().remove(&hash);
} }
} Ok(())
if !value { }))
if let Some(pos) = envelope.labels().iter().position(|&h_| h_ == hash) {
envelope.labels_mut().remove(pos);
}
}
Ok(())
} }
} }