aboutsummaryrefslogtreecommitdiff
path: root/src/imap
diff options
context:
space:
mode:
Diffstat (limited to 'src/imap')
-rw-r--r--src/imap/command/authenticated.rs63
-rw-r--r--src/imap/command/examined.rs43
-rw-r--r--src/imap/mailbox_view.rs2
3 files changed, 104 insertions, 4 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)),
+ }
+ }
}
diff --git a/src/imap/mailbox_view.rs b/src/imap/mailbox_view.rs
index db6f490..e4df3d0 100644
--- a/src/imap/mailbox_view.rs
+++ b/src/imap/mailbox_view.rs
@@ -37,7 +37,7 @@ const DEFAULT_FLAGS: [Flag; 5] = [
/// what the client knows, and produces IMAP messages to be sent to the
/// client that go along updates to `known_state`.
pub struct MailboxView {
- mailbox: Arc<Mailbox>,
+ pub(crate) mailbox: Arc<Mailbox>,
known_state: UidIndex,
}