implement STORE
This commit is contained in:
parent
23e0eff42b
commit
7959adb8e9
2 changed files with 119 additions and 29 deletions
|
@ -67,13 +67,21 @@ impl<'a> SelectedContext<'a> {
|
||||||
|
|
||||||
async fn store(
|
async fn store(
|
||||||
self,
|
self,
|
||||||
_sequence_set: &SequenceSet,
|
sequence_set: &SequenceSet,
|
||||||
_kind: &StoreType,
|
kind: &StoreType,
|
||||||
_response: &StoreResponse,
|
response: &StoreResponse,
|
||||||
_flags: &[Flag],
|
flags: &[Flag],
|
||||||
_uid: &bool,
|
uid: &bool,
|
||||||
) -> Result<(Response, flow::Transition)> {
|
) -> Result<(Response, flow::Transition)> {
|
||||||
Ok((Response::bad("Not implemented")?, flow::Transition::None))
|
let data = self
|
||||||
|
.mailbox
|
||||||
|
.store(sequence_set, kind, response, flags, uid)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Response::ok("STORE completed")?.with_body(data),
|
||||||
|
flow::Transition::None,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn copy(
|
async fn copy(
|
||||||
|
|
|
@ -13,13 +13,14 @@ use imap_codec::types::core::{Atom, IString, NString};
|
||||||
use imap_codec::types::datetime::MyDateTime;
|
use imap_codec::types::datetime::MyDateTime;
|
||||||
use imap_codec::types::envelope::Envelope;
|
use imap_codec::types::envelope::Envelope;
|
||||||
use imap_codec::types::fetch_attributes::{FetchAttribute, MacroOrFetchAttributes};
|
use imap_codec::types::fetch_attributes::{FetchAttribute, MacroOrFetchAttributes};
|
||||||
use imap_codec::types::flag::Flag;
|
use imap_codec::types::flag::{Flag, StoreResponse, StoreType};
|
||||||
use imap_codec::types::response::{Code, Data, MessageAttribute, Status};
|
use imap_codec::types::response::{Code, Data, MessageAttribute, Status};
|
||||||
use imap_codec::types::sequence::{self, SequenceSet};
|
use imap_codec::types::sequence::{self, SequenceSet};
|
||||||
use mail_parser::*;
|
use mail_parser::*;
|
||||||
|
|
||||||
use crate::mail::mailbox::Mailbox;
|
use crate::mail::mailbox::Mailbox;
|
||||||
use crate::mail::uidindex::{ImapUid, ImapUidvalidity, UidIndex};
|
use crate::mail::uidindex::{ImapUid, ImapUidvalidity, UidIndex};
|
||||||
|
use crate::mail::unique_ident::UniqueIdent;
|
||||||
|
|
||||||
const DEFAULT_FLAGS: [Flag; 5] = [
|
const DEFAULT_FLAGS: [Flag; 5] = [
|
||||||
Flag::Seen,
|
Flag::Seen,
|
||||||
|
@ -144,6 +145,60 @@ impl MailboxView {
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn store(
|
||||||
|
&mut self,
|
||||||
|
sequence_set: &SequenceSet,
|
||||||
|
kind: &StoreType,
|
||||||
|
_response: &StoreResponse,
|
||||||
|
flags: &[Flag],
|
||||||
|
uid: &bool,
|
||||||
|
) -> Result<Vec<Body>> {
|
||||||
|
if *uid {
|
||||||
|
bail!("UID STORE not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
let flags = flags.iter().map(|x| x.to_string()).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mails = self.get_mail_ids(sequence_set)?;
|
||||||
|
for (i, uid, uuid) in mails.iter() {
|
||||||
|
match kind {
|
||||||
|
StoreType::Add => {
|
||||||
|
self.mailbox.add_flags(*uuid, &flags[..]).await?;
|
||||||
|
}
|
||||||
|
StoreType::Remove => {
|
||||||
|
self.mailbox.del_flags(*uuid, &flags[..]).await?;
|
||||||
|
}
|
||||||
|
StoreType::Replace => {
|
||||||
|
let old_flags = &self
|
||||||
|
.known_state
|
||||||
|
.table
|
||||||
|
.get(uuid)
|
||||||
|
.ok_or(anyhow!(
|
||||||
|
"Missing message: {} (UID {}, UUID {})",
|
||||||
|
i,
|
||||||
|
uid,
|
||||||
|
uuid
|
||||||
|
))?
|
||||||
|
.1;
|
||||||
|
let to_remove = old_flags
|
||||||
|
.iter()
|
||||||
|
.filter(|x| !flags.contains(&x))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let to_add = flags
|
||||||
|
.iter()
|
||||||
|
.filter(|x| !old_flags.contains(&x))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
self.mailbox.add_flags(*uuid, &to_add[..]).await?;
|
||||||
|
self.mailbox.del_flags(*uuid, &to_remove[..]).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update().await
|
||||||
|
}
|
||||||
|
|
||||||
/// Looks up state changes in the mailbox and produces a set of IMAP
|
/// Looks up state changes in the mailbox and produces a set of IMAP
|
||||||
/// responses describing the new state.
|
/// responses describing the new state.
|
||||||
pub async fn fetch(
|
pub async fn fetch(
|
||||||
|
@ -156,28 +211,11 @@ impl MailboxView {
|
||||||
bail!("UID FETCH not implemented");
|
bail!("UID FETCH not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mail_vec = self
|
let mails = self.get_mail_ids(sequence_set)?;
|
||||||
.known_state
|
|
||||||
.idx_by_uid
|
|
||||||
.iter()
|
|
||||||
.map(|(uid, uuid)| (*uid, *uuid))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut mails = vec![];
|
|
||||||
let iter_strat = sequence::Strategy::Naive {
|
|
||||||
largest: NonZeroU32::try_from((self.known_state.idx_by_uid.len() + 1) as u32).unwrap(),
|
|
||||||
};
|
|
||||||
for i in sequence_set.iter(iter_strat) {
|
|
||||||
if let Some(mail) = mail_vec.get(i.get() as usize - 1) {
|
|
||||||
mails.push((i, *mail));
|
|
||||||
} else {
|
|
||||||
bail!("No such mail: {}", i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mails_uuid = mails
|
let mails_uuid = mails
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_i, (_uid, uuid))| *uuid)
|
.map(|(_i, _uid, uuid)| *uuid)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let mails_meta = self.mailbox.fetch_meta(&mails_uuid).await?;
|
let mails_meta = self.mailbox.fetch_meta(&mails_uuid).await?;
|
||||||
|
|
||||||
|
@ -200,7 +238,7 @@ impl MailboxView {
|
||||||
let mut iter = mails
|
let mut iter = mails
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(mails_meta.into_iter())
|
.zip(mails_meta.into_iter())
|
||||||
.map(|((i, (uid, uuid)), meta)| async move {
|
.map(|((i, uid, uuid), meta)| async move {
|
||||||
let body = self.mailbox.fetch_full(uuid, &meta.message_key).await?;
|
let body = self.mailbox.fetch_full(uuid, &meta.message_key).await?;
|
||||||
Ok::<_, anyhow::Error>((i, uid, uuid, meta, Some(body)))
|
Ok::<_, anyhow::Error>((i, uid, uuid, meta, Some(body)))
|
||||||
})
|
})
|
||||||
|
@ -214,7 +252,7 @@ impl MailboxView {
|
||||||
mails
|
mails
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(mails_meta.into_iter())
|
.zip(mails_meta.into_iter())
|
||||||
.map(|((i, (uid, uuid)), meta)| (i, uid, uuid, meta, None))
|
.map(|((i, uid, uuid), meta)| (i, uid, uuid, meta, None))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -314,6 +352,36 @@ impl MailboxView {
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
|
|
||||||
|
// Gets the UIDs and UUIDs of mails identified by a SequenceSet of
|
||||||
|
// sequence numbers
|
||||||
|
fn get_mail_ids(
|
||||||
|
&self,
|
||||||
|
sequence_set: &SequenceSet,
|
||||||
|
) -> Result<Vec<(NonZeroU32, ImapUid, UniqueIdent)>> {
|
||||||
|
let mail_vec = self
|
||||||
|
.known_state
|
||||||
|
.idx_by_uid
|
||||||
|
.iter()
|
||||||
|
.map(|(uid, uuid)| (*uid, *uuid))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut mails = vec![];
|
||||||
|
let iter_strat = sequence::Strategy::Naive {
|
||||||
|
largest: NonZeroU32::try_from((self.known_state.idx_by_uid.len() + 1) as u32).unwrap(),
|
||||||
|
};
|
||||||
|
for i in sequence_set.iter(iter_strat) {
|
||||||
|
if let Some(mail) = mail_vec.get(i.get() as usize - 1) {
|
||||||
|
mails.push((i, mail.0, mail.1));
|
||||||
|
} else {
|
||||||
|
bail!("No such mail: {}", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(mails)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
|
||||||
/// Produce an OK [UIDVALIDITY _] message corresponding to `known_state`
|
/// Produce an OK [UIDVALIDITY _] message corresponding to `known_state`
|
||||||
fn uidvalidity_status(&self) -> Result<Body> {
|
fn uidvalidity_status(&self) -> Result<Body> {
|
||||||
let uid_validity = Status::ok(
|
let uid_validity = Status::ok(
|
||||||
|
@ -420,7 +488,21 @@ impl MailboxView {
|
||||||
|
|
||||||
fn string_to_flag(f: &str) -> Option<Flag> {
|
fn string_to_flag(f: &str) -> Option<Flag> {
|
||||||
match f.chars().next() {
|
match f.chars().next() {
|
||||||
Some('\\') => None,
|
Some('\\') => match f {
|
||||||
|
"\\Seen" => Some(Flag::Seen),
|
||||||
|
"\\Answered" => Some(Flag::Answered),
|
||||||
|
"\\Flagged" => Some(Flag::Flagged),
|
||||||
|
"\\Deleted" => Some(Flag::Deleted),
|
||||||
|
"\\Draft" => Some(Flag::Draft),
|
||||||
|
"\\Recent" => Some(Flag::Recent),
|
||||||
|
_ => match Atom::try_from(f.strip_prefix('\\').unwrap().clone()) {
|
||||||
|
Err(_) => {
|
||||||
|
tracing::error!(flag=%f, "Unable to encode flag as IMAP atom");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Ok(a) => Some(Flag::Extension(a)),
|
||||||
|
},
|
||||||
|
},
|
||||||
Some('$') if f == "$unseen" => None,
|
Some('$') if f == "$unseen" => None,
|
||||||
Some(_) => match Atom::try_from(f.clone()) {
|
Some(_) => match Atom::try_from(f.clone()) {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
|
Loading…
Reference in a new issue