aboutsummaryrefslogtreecommitdiff
path: root/src/imap/command
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2022-07-12 16:35:11 +0200
committerAlex Auvolat <alex@adnab.me>2022-07-12 16:35:11 +0200
commit46d952598474e851ee528515d7a9ffab88d3ad49 (patch)
treea062bc94e8a1841f8d017c97cccbebc1d44cf1a4 /src/imap/command
parentd4e0e66581ff785e89edd15e2b8d68640f370a0e (diff)
downloadaerogramme-46d952598474e851ee528515d7a9ffab88d3ad49.tar.gz
aerogramme-46d952598474e851ee528515d7a9ffab88d3ad49.zip
Implement APPEND
Diffstat (limited to 'src/imap/command')
-rw-r--r--src/imap/command/authenticated.rs63
-rw-r--r--src/imap/command/examined.rs43
2 files changed, 103 insertions, 3 deletions
diff --git a/src/imap/command/authenticated.rs b/src/imap/command/authenticated.rs
index 6208290..32a8e1e 100644
--- a/src/imap/command/authenticated.rs
+++ b/src/imap/command/authenticated.rs
@@ -1,11 +1,13 @@
use std::collections::BTreeMap;
use std::sync::Arc;
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, bail, Result};
use boitalettres::proto::res::body::Data as Body;
use boitalettres::proto::{Request, Response};
use imap_codec::types::command::{CommandBody, StatusAttribute};
-use imap_codec::types::flag::FlagNameAttribute;
+use imap_codec::types::core::NonZeroBytes;
+use imap_codec::types::datetime::MyDateTime;
+use imap_codec::types::flag::{Flag, FlagNameAttribute};
use imap_codec::types::mailbox::{ListMailbox, Mailbox as MailboxCodec};
use imap_codec::types::response::{Code, Data, StatusAttributeValue};
@@ -13,7 +15,10 @@ use crate::imap::command::anonymous;
use crate::imap::flow;
use crate::imap::mailbox_view::MailboxView;
+use crate::mail::mailbox::Mailbox;
+use crate::mail::uidindex::*;
use crate::mail::user::{User, INBOX, MAILBOX_HIERARCHY_DELIMITER};
+use crate::mail::IMF;
pub struct AuthenticatedContext<'a> {
pub req: &'a Request,
@@ -44,6 +49,12 @@ pub async fn dispatch<'a>(ctx: AuthenticatedContext<'a>) -> Result<(Response, fl
CommandBody::Unsubscribe { mailbox } => ctx.unsubscribe(mailbox).await,
CommandBody::Select { mailbox } => ctx.select(mailbox).await,
CommandBody::Examine { mailbox } => ctx.examine(mailbox).await,
+ CommandBody::Append {
+ mailbox,
+ flags,
+ date,
+ message,
+ } => ctx.append(mailbox, flags, date, message).await,
_ => {
let ctx = anonymous::AnonymousContext {
req: ctx.req,
@@ -316,4 +327,52 @@ impl<'a> AuthenticatedContext<'a> {
flow::Transition::Examine(mb),
))
}
+
+ async fn append(
+ self,
+ mailbox: &MailboxCodec,
+ flags: &[Flag],
+ date: &Option<MyDateTime>,
+ message: &NonZeroBytes,
+ ) -> Result<(Response, flow::Transition)> {
+ match self.append_internal(mailbox, flags, date, message).await {
+ Ok((_mb, uidvalidity, uid)) => Ok((
+ Response::ok("APPEND completed")?.with_extra_code(Code::Other(
+ "APPENDUID".try_into().unwrap(),
+ Some(format!("{} {}", uidvalidity, uid)),
+ )),
+ flow::Transition::None,
+ )),
+ Err(e) => Ok((Response::no(&e.to_string())?, flow::Transition::None)),
+ }
+ }
+
+ pub(crate) async fn append_internal(
+ self,
+ mailbox: &MailboxCodec,
+ flags: &[Flag],
+ date: &Option<MyDateTime>,
+ message: &NonZeroBytes,
+ ) -> Result<(Arc<Mailbox>, ImapUidvalidity, ImapUidvalidity)> {
+ 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"),
+ };
+
+ if date.is_some() {
+ bail!("Cannot set date when appending message");
+ }
+
+ let msg = IMF::try_from(message.as_slice())
+ .map_err(|_| anyhow!("Could not parse e-mail message"))?;
+ let flags = flags.iter().map(|x| x.to_string()).collect::<Vec<_>>();
+ // TODO: filter allowed flags? ping @Quentin
+
+ let (uidvalidity, uid) = mb.append(msg, None, &flags[..]).await?;
+
+ Ok((mb, uidvalidity, uid))
+ }
}
diff --git a/src/imap/command/examined.rs b/src/imap/command/examined.rs
index 9dba680..ea773de 100644
--- a/src/imap/command/examined.rs
+++ b/src/imap/command/examined.rs
@@ -5,14 +5,19 @@ use boitalettres::proto::Request;
use boitalettres::proto::Response;
use imap_codec::types::command::{CommandBody, SearchKey};
use imap_codec::types::core::Charset;
+use imap_codec::types::core::NonZeroBytes;
+use imap_codec::types::datetime::MyDateTime;
use imap_codec::types::fetch_attributes::MacroOrFetchAttributes;
-
+use imap_codec::types::flag::{Flag, FlagNameAttribute};
+use imap_codec::types::mailbox::{ListMailbox, Mailbox as MailboxCodec};
+use imap_codec::types::response::{Code, Data, StatusAttributeValue};
use imap_codec::types::sequence::SequenceSet;
use crate::imap::command::authenticated;
use crate::imap::flow;
use crate::imap::mailbox_view::MailboxView;
+use crate::mail::uidindex::*;
use crate::mail::user::User;
pub struct ExaminedContext<'a> {
@@ -37,6 +42,12 @@ pub async fn dispatch<'a>(ctx: ExaminedContext<'a>) -> Result<(Response, flow::T
uid,
} => ctx.search(charset, criteria, uid).await,
CommandBody::Noop => ctx.noop().await,
+ CommandBody::Append {
+ mailbox,
+ flags,
+ date,
+ message,
+ } => ctx.append(mailbox, flags, date, message).await,
_ => {
let ctx = authenticated::AuthenticatedContext {
req: ctx.req,
@@ -85,4 +96,34 @@ impl<'a> ExaminedContext<'a> {
flow::Transition::None,
))
}
+
+ async fn append(
+ self,
+ mailbox: &MailboxCodec,
+ flags: &[Flag],
+ date: &Option<MyDateTime>,
+ message: &NonZeroBytes,
+ ) -> Result<(Response, flow::Transition)> {
+ let ctx2 = authenticated::AuthenticatedContext {
+ req: self.req,
+ user: self.user,
+ };
+
+ match ctx2.append_internal(mailbox, flags, date, message).await {
+ Ok((mb, uidvalidity, uid)) => {
+ let resp = Response::ok("APPEND completed")?.with_extra_code(Code::Other(
+ "APPENDUID".try_into().unwrap(),
+ Some(format!("{} {}", uidvalidity, uid)),
+ ));
+
+ if Arc::ptr_eq(&mb, &self.mailbox.mailbox) {
+ let data = self.mailbox.update().await?;
+ Ok((resp.with_body(data), flow::Transition::None))
+ } else {
+ Ok((resp, flow::Transition::None))
+ }
+ }
+ Err(e) => Ok((Response::no(&e.to_string())?, flow::Transition::None)),
+ }
+ }
}