aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2024-01-03 15:00:05 +0100
committerQuentin Dufour <quentin@deuxfleurs.fr>2024-01-03 15:00:05 +0100
commitef257e286a5377031bfde9645629825d3223b3d4 (patch)
tree78853c708949b709061e5b1f24f16343ceaee895
parent6d37924399dd5d04f5be2506e3e044dd165f6399 (diff)
downloadaerogramme-ef257e286a5377031bfde9645629825d3223b3d4.tar.gz
aerogramme-ef257e286a5377031bfde9645629825d3223b3d4.zip
implement move
-rw-r--r--src/imap/command/anonymous.rs8
-rw-r--r--src/imap/command/anystate.rs7
-rw-r--r--src/imap/command/authenticated.rs8
-rw-r--r--src/imap/command/examined.rs9
-rw-r--r--src/imap/command/selected.rs68
-rw-r--r--src/imap/mailbox_view.rs31
-rw-r--r--src/imap/mod.rs10
-rw-r--r--src/imap/session.rs9
8 files changed, 123 insertions, 27 deletions
diff --git a/src/imap/command/anonymous.rs b/src/imap/command/anonymous.rs
index fda78c2..0582b06 100644
--- a/src/imap/command/anonymous.rs
+++ b/src/imap/command/anonymous.rs
@@ -4,9 +4,9 @@ use imap_codec::imap_types::core::AString;
use imap_codec::imap_types::response::Code;
use imap_codec::imap_types::secret::Secret;
+use crate::imap::capability::ServerCapability;
use crate::imap::command::anystate;
use crate::imap::flow;
-use crate::imap::capability::ServerCapability;
use crate::imap::response::Response;
use crate::login::ArcLoginProvider;
use crate::mail::user::User;
@@ -23,9 +23,9 @@ pub async fn dispatch(ctx: AnonymousContext<'_>) -> Result<(Response<'static>, f
match &ctx.req.body {
// Any State
CommandBody::Noop => anystate::noop_nothing(ctx.req.tag.clone()),
- CommandBody::Capability => anystate::capability(
- ctx.req.tag.clone(),
- ctx.server_capabilities),
+ CommandBody::Capability => {
+ anystate::capability(ctx.req.tag.clone(), ctx.server_capabilities)
+ }
CommandBody::Logout => anystate::logout(),
// Specific to anonymous context (3 commands)
diff --git a/src/imap/command/anystate.rs b/src/imap/command/anystate.rs
index 7d7c0b2..718ba3f 100644
--- a/src/imap/command/anystate.rs
+++ b/src/imap/command/anystate.rs
@@ -2,11 +2,14 @@ use anyhow::Result;
use imap_codec::imap_types::core::Tag;
use imap_codec::imap_types::response::Data;
-use crate::imap::flow;
use crate::imap::capability::ServerCapability;
+use crate::imap::flow;
use crate::imap::response::Response;
-pub(crate) fn capability(tag: Tag<'static>, cap: &ServerCapability) -> Result<(Response<'static>, flow::Transition)> {
+pub(crate) fn capability(
+ tag: Tag<'static>,
+ cap: &ServerCapability,
+) -> Result<(Response<'static>, flow::Transition)> {
let res = Response::build()
.tag(tag)
.message("Server capabilities")
diff --git a/src/imap/command/authenticated.rs b/src/imap/command/authenticated.rs
index a6a5203..2970b63 100644
--- a/src/imap/command/authenticated.rs
+++ b/src/imap/command/authenticated.rs
@@ -10,11 +10,11 @@ use imap_codec::imap_types::mailbox::{ListMailbox, Mailbox as MailboxCodec};
use imap_codec::imap_types::response::{Code, CodeOther, Data};
use imap_codec::imap_types::status::{StatusDataItem, StatusDataItemName};
+use crate::imap::capability::ServerCapability;
use crate::imap::command::{anystate, MailboxName};
use crate::imap::flow;
use crate::imap::mailbox_view::MailboxView;
use crate::imap::response::Response;
-use crate::imap::capability::ServerCapability;
use crate::mail::mailbox::Mailbox;
use crate::mail::uidindex::*;
@@ -33,9 +33,9 @@ pub async fn dispatch<'a>(
match &ctx.req.body {
// Any state
CommandBody::Noop => anystate::noop_nothing(ctx.req.tag.clone()),
- CommandBody::Capability => anystate::capability(
- ctx.req.tag.clone(),
- ctx.server_capabilities),
+ CommandBody::Capability => {
+ anystate::capability(ctx.req.tag.clone(), ctx.server_capabilities)
+ }
CommandBody::Logout => anystate::logout(),
// Specific to this state (11 commands)
diff --git a/src/imap/command/examined.rs b/src/imap/command/examined.rs
index 7cccf7b..bddd0f9 100644
--- a/src/imap/command/examined.rs
+++ b/src/imap/command/examined.rs
@@ -7,11 +7,11 @@ use imap_codec::imap_types::fetch::MacroOrMessageDataItemNames;
use imap_codec::imap_types::search::SearchKey;
use imap_codec::imap_types::sequence::SequenceSet;
+use crate::imap::capability::ServerCapability;
use crate::imap::command::{anystate, authenticated};
use crate::imap::flow;
use crate::imap::mailbox_view::MailboxView;
use crate::imap::response::Response;
-use crate::imap::capability::ServerCapability;
use crate::mail::user::User;
pub struct ExaminedContext<'a> {
@@ -25,10 +25,9 @@ pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response<'static>, fl
match &ctx.req.body {
// Any State
// noop is specific to this state
- CommandBody::Capability => anystate::capability(
- ctx.req.tag.clone(),
- ctx.server_capabilities,
- ),
+ CommandBody::Capability => {
+ anystate::capability(ctx.req.tag.clone(), ctx.server_capabilities)
+ }
CommandBody::Logout => anystate::logout(),
// Specific to the EXAMINE state (specialization of the SELECTED state)
diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs
index 681b509..28ebbe8 100644
--- a/src/imap/command/selected.rs
+++ b/src/imap/command/selected.rs
@@ -10,11 +10,11 @@ use imap_codec::imap_types::response::{Code, CodeOther};
use imap_codec::imap_types::search::SearchKey;
use imap_codec::imap_types::sequence::SequenceSet;
+use crate::imap::capability::ServerCapability;
use crate::imap::command::{anystate, authenticated, MailboxName};
use crate::imap::flow;
use crate::imap::mailbox_view::MailboxView;
use crate::imap::response::Response;
-use crate::imap::capability::ServerCapability;
use crate::mail::user::User;
@@ -31,10 +31,9 @@ pub async fn dispatch<'a>(
match &ctx.req.body {
// Any State
// noop is specific to this state
- CommandBody::Capability => anystate::capability(
- ctx.req.tag.clone(),
- ctx.server_capabilities,
- ),
+ CommandBody::Capability => {
+ anystate::capability(ctx.req.tag.clone(), ctx.server_capabilities)
+ }
CommandBody::Logout => anystate::logout(),
// Specific to this state (7 commands + NOOP)
@@ -63,6 +62,11 @@ pub async fn dispatch<'a>(
mailbox,
uid,
} => ctx.copy(sequence_set, mailbox, uid).await,
+ CommandBody::Move {
+ sequence_set,
+ mailbox,
+ uid,
+ } => ctx.r#move(sequence_set, mailbox, uid).await,
// UNSELECT extension (rfc3691)
CommandBody::Unselect => ctx.unselect().await,
@@ -245,4 +249,58 @@ impl<'a> SelectedContext<'a> {
flow::Transition::None,
))
}
+
+ async fn r#move(
+ self,
+ sequence_set: &SequenceSet,
+ mailbox: &MailboxCodec<'a>,
+ uid: &bool,
+ ) -> Result<(Response<'static>, flow::Transition)> {
+ let name: &str = MailboxName(mailbox).try_into()?;
+
+ let mb_opt = self.user.open_mailbox(&name).await?;
+ let mb = match mb_opt {
+ Some(mb) => mb,
+ None => {
+ return Ok((
+ Response::build()
+ .to_req(self.req)
+ .message("Destination mailbox does not exist")
+ .code(Code::TryCreate)
+ .no()?,
+ flow::Transition::None,
+ ))
+ }
+ };
+
+ let (uidval, uid_map, data) = self.mailbox.r#move(sequence_set, mb, uid).await?;
+
+ // compute code
+ let copyuid_str = format!(
+ "{} {} {}",
+ uidval,
+ uid_map
+ .iter()
+ .map(|(sid, _)| format!("{}", sid))
+ .collect::<Vec<_>>()
+ .join(","),
+ uid_map
+ .iter()
+ .map(|(_, tuid)| format!("{}", tuid))
+ .collect::<Vec<_>>()
+ .join(",")
+ );
+
+ Ok((
+ Response::build()
+ .to_req(self.req)
+ .message("COPY completed")
+ .code(Code::Other(CodeOther::unvalidated(
+ format!("COPYUID {}", copyuid_str).into_bytes(),
+ )))
+ .set_body(data)
+ .ok()?,
+ flow::Transition::None,
+ ))
+ }
}
diff --git a/src/imap/mailbox_view.rs b/src/imap/mailbox_view.rs
index 7434512..206b905 100644
--- a/src/imap/mailbox_view.rs
+++ b/src/imap/mailbox_view.rs
@@ -600,6 +600,37 @@ impl MailboxView {
Ok((to_state.uidvalidity, ret))
}
+ pub async fn r#move(
+ &mut self,
+ sequence_set: &SequenceSet,
+ to: Arc<Mailbox>,
+ is_uid_copy: &bool,
+ ) -> Result<(ImapUidvalidity, Vec<(ImapUid, ImapUid)>, Vec<Body<'static>>)> {
+ let mails = self.get_mail_ids(sequence_set, *is_uid_copy)?;
+
+ let mut new_uuids = vec![];
+ for mi in mails.iter() {
+ let copy_action = to.copy_from(&self.mailbox, mi.uuid).await?;
+ new_uuids.push(copy_action);
+ self.mailbox.delete(mi.uuid).await?
+ }
+
+ let mut ret = vec![];
+ let to_state = to.current_uid_index().await;
+ for (mi, new_uuid) in mails.iter().zip(new_uuids.iter()) {
+ let dest_uid = to_state
+ .table
+ .get(new_uuid)
+ .ok_or(anyhow!("moved mail not in destination mailbox"))?
+ .0;
+ ret.push((mi.uid, dest_uid));
+ }
+
+ let update = self.update().await?;
+
+ Ok((to_state.uidvalidity, ret, update))
+ }
+
/// Looks up state changes in the mailbox and produces a set of IMAP
/// responses describing the new state.
pub async fn fetch<'b>(
diff --git a/src/imap/mod.rs b/src/imap/mod.rs
index 693c99a..0b5555a 100644
--- a/src/imap/mod.rs
+++ b/src/imap/mod.rs
@@ -1,9 +1,9 @@
+mod capability;
mod command;
mod flow;
mod mailbox_view;
mod response;
mod session;
-mod capability;
use std::net::SocketAddr;
@@ -102,14 +102,18 @@ async fn client(mut ctx: ClientContext) -> Result<()> {
let (mut server, _) = ServerFlow::send_greeting(
ctx.stream,
ServerFlowOptions::default(),
- Greeting::ok(Some(Code::Capability(ctx.server_capabilities.to_vec())), "Aerogramme").unwrap(),
+ Greeting::ok(
+ Some(Code::Capability(ctx.server_capabilities.to_vec())),
+ "Aerogramme",
+ )
+ .unwrap(),
)
.await?;
use crate::imap::response::{Body, Response as MyResponse};
use crate::imap::session::Instance;
use imap_codec::imap_types::command::Command;
- use imap_codec::imap_types::response::{Response, Code, Status};
+ use imap_codec::imap_types::response::{Code, Response, Status};
use tokio::sync::mpsc;
let (cmd_tx, mut cmd_rx) = mpsc::channel::<Command<'static>>(10);
diff --git a/src/imap/session.rs b/src/imap/session.rs
index c4f062f..55026b9 100644
--- a/src/imap/session.rs
+++ b/src/imap/session.rs
@@ -1,7 +1,7 @@
+use crate::imap::capability::ServerCapability;
use crate::imap::command::{anonymous, authenticated, examined, selected};
use crate::imap::flow;
use crate::imap::response::Response;
-use crate::imap::capability::ServerCapability;
use crate::login::ArcLoginProvider;
use imap_codec::imap_types::command::Command;
@@ -33,10 +33,11 @@ impl Instance {
anonymous::dispatch(ctx).await
}
flow::State::Authenticated(ref user) => {
- let ctx = authenticated::AuthenticatedContext {
- req: &cmd,
+ let ctx = authenticated::AuthenticatedContext {
+ req: &cmd,
server_capabilities: &self.server_capabilities,
- user };
+ user,
+ };
authenticated::dispatch(ctx).await
}
flow::State::Selected(ref user, ref mut mailbox) => {