diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs index 1978729..40e75e2 100644 --- a/src/imap/command/selected.rs +++ b/src/imap/command/selected.rs @@ -1,13 +1,12 @@ use std::sync::Arc; -use anyhow::Result; +use anyhow::{bail, Result}; use boitalettres::proto::Request; use boitalettres::proto::Response; use imap_codec::types::command::CommandBody; - use imap_codec::types::flag::{Flag, StoreResponse, StoreType}; use imap_codec::types::mailbox::Mailbox as MailboxCodec; - +use imap_codec::types::response::Code; use imap_codec::types::sequence::SequenceSet; use crate::imap::command::examined; @@ -91,10 +90,41 @@ impl<'a> SelectedContext<'a> { async fn copy( self, - _sequence_set: &SequenceSet, - _mailbox: &MailboxCodec, - _uid: &bool, + sequence_set: &SequenceSet, + mailbox: &MailboxCodec, + uid: &bool, ) -> Result<(Response, flow::Transition)> { - Ok((Response::bad("Not implemented")?, flow::Transition::None)) + let name = String::try_from(mailbox.clone())?; + + let mb_opt = self.user.open_mailbox(&name).await?; + let mb = match mb_opt { + Some(mb) => mb, + None => bail!("Mailbox does not exist"), + }; + + let (uidval, uid_map) = self.mailbox.copy(sequence_set, mb, uid).await?; + + let copyuid_str = format!( + "{} {} {}", + uidval, + uid_map + .iter() + .map(|(sid, _)| format!("{}", sid)) + .collect::>() + .join(","), + uid_map + .iter() + .map(|(_, tuid)| format!("{}", tuid)) + .collect::>() + .join(",") + ); + + Ok(( + Response::ok("COPY completed")?.with_extra_code(Code::Other( + "COPYUID".try_into().unwrap(), + Some(copyuid_str), + )), + flow::Transition::None, + )) } } diff --git a/src/imap/mailbox_view.rs b/src/imap/mailbox_view.rs index a886c8d..13ea9a7 100644 --- a/src/imap/mailbox_view.rs +++ b/src/imap/mailbox_view.rs @@ -168,6 +168,7 @@ impl MailboxView { } } + // @TODO: handle _response self.update().await } @@ -189,6 +190,33 @@ impl MailboxView { self.update().await } + pub async fn copy( + &self, + sequence_set: &SequenceSet, + to: Arc, + is_uid_copy: &bool, + ) -> Result<(ImapUidvalidity, Vec<(ImapUid, ImapUid)>)> { + let mails = self.get_mail_ids(sequence_set, *is_uid_copy)?; + + let mut new_uuids = vec![]; + for (_i, _uid, uuid) in mails.iter() { + new_uuids.push(to.copy_from(&self.mailbox, *uuid).await?); + } + + let mut ret = vec![]; + let to_state = to.current_uid_index().await; + for ((_i, uid, _uuid), new_uuid) in mails.iter().zip(new_uuids.iter()) { + let dest_uid = to_state + .table + .get(new_uuid) + .ok_or(anyhow!("copied mail not in destination mailbox"))? + .0; + ret.push((*uid, dest_uid)); + } + + Ok((to_state.uidvalidity, ret)) + } + /// Looks up state changes in the mailbox and produces a set of IMAP /// responses describing the new state. pub async fn fetch( @@ -841,21 +869,6 @@ fn build_imap_email_struct<'a>(msg: &Message<'a>, part: &MessagePart<'a>) -> Res } } -fn count_lines(mut text: &[u8]) -> Result { - while text.first().map(u8::is_ascii_whitespace).unwrap_or(false) { - text = &text[1..]; - } - while text.last().map(u8::is_ascii_whitespace).unwrap_or(false) { - text = &text[..text.len() - 1]; - } - if text.is_empty() { - Ok(0) - } else { - let nlf = text.iter().filter(|x| **x == b'\n').count(); - Ok(u32::try_from(1 + nlf)?) - } -} - fn try_collect_shime(acc: Result>, elem: Result) -> Result> { match (acc, elem) { (Err(e), _) | (_, Err(e)) => Err(e), diff --git a/src/mail/mailbox.rs b/src/mail/mailbox.rs index 44ffe20..df7ede9 100644 --- a/src/mail/mailbox.rs +++ b/src/mail/mailbox.rs @@ -138,7 +138,7 @@ impl Mailbox { /// Copy an email from an other Mailbox to this mailbox /// (use this when possible, as it allows for a certain number of storage optimizations) - pub async fn copy_from(&self, from: &Mailbox, uuid: UniqueIdent) -> Result<()> { + pub async fn copy_from(&self, from: &Mailbox, uuid: UniqueIdent) -> Result { if self.id == from.id { bail!("Cannot copy into same mailbox"); } @@ -412,9 +412,14 @@ impl MailboxInternal { Ok(()) } - async fn copy_from(&mut self, from: &MailboxInternal, source_id: UniqueIdent) -> Result<()> { + async fn copy_from( + &mut self, + from: &MailboxInternal, + source_id: UniqueIdent, + ) -> Result { let new_id = gen_ident(); - self.copy_internal(from, source_id, new_id).await + self.copy_internal(from, source_id, new_id).await?; + Ok(new_id) } async fn move_from(&mut self, from: &mut MailboxInternal, id: UniqueIdent) -> Result<()> {