use std::borrow::Cow;
use std::io::Read;
use chrono::prelude::*;
use super::defs::*;
use crate::account::mailbox::{StatefulMailbox, StatelessMailbox};
use crate::account::model::*;
use crate::imap::mailbox_name::MailboxName;
use crate::support::error::Error;
impl CommandProcessor {
pub fn cmd_append_start(
&mut self,
cmd: s::AppendCommandStart<'_>,
item_size: u32,
item_data: impl Read,
) -> PartialResult<()> {
let mailbox = cmd.mailbox.get_utf8(self.unicode_aware);
let dst =
account!(self)?
.mailbox(&mailbox, false)
.map_err(map_error! {
self,
NxMailbox =>
(No, Some(s::RespTextCode::TryCreate(()))),
MailboxUnselectable =>
(No, Some(s::RespTextCode::Nonexistent(()))),
UnsafeName =>
(No, Some(s::RespTextCode::Cannot(()))),
})?;
self.multiappend = Some(Multiappend {
dst,
request: AppendRequest { items: vec![] },
});
self.cmd_append_item(cmd.first_fragment, item_size, item_data)
}
pub fn cmd_append_item(
&mut self,
cmd: s::AppendFragment,
item_size: u32,
item_data: impl Read,
) -> PartialResult<()> {
if 0 == item_size {
return Err(s::Response::Cond(s::CondResponse {
cond: s::RespCondType::No,
code: None,
quip: Some(Cow::Borrowed("APPEND aborted by empty payload")),
}));
}
if cmd.utf8 && !self.utf8_enabled {
return Err(s::Response::Cond(s::CondResponse {
cond: s::RespCondType::No,
code: None,
quip: Some(Cow::Borrowed(
"UTF8 literal not allowed until ENABLE UTF8=ACCEPT",
)),
}));
}
let append = self
.multiappend
.as_mut()
.expect("cmd_append_item with no append in progress");
if append.request.items.len() >= 65536 {
return Err(s::Response::Cond(s::CondResponse {
cond: s::RespCondType::No,
code: Some(s::RespTextCode::Limit(())),
quip: Some(Cow::Borrowed(
"Maximum message count for MULTIAPPEND is 65536",
)),
}));
}
let buffered = append
.dst
.buffer_message(
cmd.internal_date.unwrap_or_else(|| {
FixedOffset::east(0)
.from_utc_datetime(&Utc::now().naive_local())
}),
item_data,
)
.map_err(map_error!(self))?;
append.request.items.push(AppendItem {
buffer_file: buffered,
flags: cmd.flags.unwrap_or_default(),
});
Ok(())
}
pub fn cmd_append_commit(
&mut self,
tag: Cow<'static, str>,
sender: SendResponse<'_>,
) -> s::ResponseLine<'static> {
let append = self
.multiappend
.take()
.expect("cmd_append_commit with no append in progress");
let response =
match append.dst.multiappend(append.request).map_err(map_error! {
self,
MailboxFull => (No, Some(s::RespTextCode::Limit(()))),
GaveUpInsertion => (No, Some(s::RespTextCode::Unavailable(()))),
BatchTooBig => (No, Some(s::RespTextCode::Limit(()))),
}) {
Ok(appended) => appended,
Err(response) => {
return s::ResponseLine {
tag: Some(tag),
response,
};
}
};
self.handle_command(
s::CommandLine {
tag: tag.clone(),
cmd: s::Command::Simple(s::SimpleCommand::XAppendFinishedNoop),
},
sender,
);
s::ResponseLine {
tag: Some(tag),
response: s::Response::Cond(s::CondResponse {
cond: s::RespCondType::Ok,
code: Some(s::RespTextCode::AppendUid(s::AppendUidData {
uid_validity: response.uid_validity,
uids: Cow::Owned(response.uids.to_string()),
})),
quip: None,
}),
}
}
pub fn cmd_append_abort(&mut self) {
self.multiappend = None;
}
pub(super) fn cmd_expunge(
&mut self,
_sender: SendResponse<'_>,
) -> CmdResult {
selected!(self)?.expunge_all_deleted().map_err(map_error! {
self,
MailboxReadOnly => (No, Some(s::RespTextCode::Cannot(()))),
})?;
success()
}
pub(super) fn cmd_uid_expunge(
&mut self,
uids: Cow<'_, str>,
_sender: SendResponse<'_>,
) -> CmdResult {
let uids = self.parse_uid_range(&uids)?;
selected!(self)?
.expunge_deleted(&uids)
.map_err(map_error! {
self,
MailboxReadOnly =>
(No, Some(s::RespTextCode::Cannot(()))),
NxMessage =>
(No, Some(s::RespTextCode::Nonexistent(()))),
UnaddressableMessage =>
(No, Some(s::RespTextCode::ClientBug(()))),
})?;
success()
}
pub(super) fn cmd_vanquish(
&mut self,
uids: Cow<'_, str>,
_sender: SendResponse<'_>,
) -> CmdResult {
let uids = self.parse_uid_range(&uids)?;
selected!(self)?.vanquish(&uids).map_err(map_error! {
self,
MailboxReadOnly => (No, Some(s::RespTextCode::Cannot(()))),
NxMessage => (No, Some(s::RespTextCode::Nonexistent(()))),
UnaddressableMessage => (No, Some(s::RespTextCode::ClientBug(()))),
})?;
success()
}
pub(super) fn cmd_xcry_purge(&mut self) -> CmdResult {
let n = selected!(self)?.purge_all();
Ok(s::Response::Cond(s::CondResponse {
cond: s::RespCondType::Ok,
code: None,
quip: Some(Cow::Owned(format!("{} messages purged", n))),
}))
}
pub(super) fn cmd_copy(
&mut self,
cmd: s::CopyCommand<'_>,
sender: SendResponse<'_>,
) -> CmdResult {
let messages = self.parse_seqnum_range(&cmd.messages)?;
let request = CopyRequest { ids: messages };
self.copy_or_move(
&cmd.dst,
request,
sender,
false,
StatefulMailbox::seqnum_copy,
)
}
pub(super) fn cmd_move(
&mut self,
cmd: s::MoveCommand<'_>,
sender: SendResponse<'_>,
) -> CmdResult {
let messages = self.parse_seqnum_range(&cmd.messages)?;
let request = CopyRequest { ids: messages };
self.copy_or_move(
&cmd.dst,
request,
sender,
true,
StatefulMailbox::seqnum_moove,
)
}
pub(super) fn cmd_uid_copy(
&mut self,
cmd: s::CopyCommand<'_>,
sender: SendResponse<'_>,
) -> CmdResult {
let messages = self.parse_uid_range(&cmd.messages)?;
let request = CopyRequest { ids: messages };
self.copy_or_move(
&cmd.dst,
request,
sender,
false,
StatefulMailbox::copy,
)
}
pub(super) fn cmd_uid_move(
&mut self,
cmd: s::MoveCommand<'_>,
sender: SendResponse<'_>,
) -> CmdResult {
let messages = self.parse_uid_range(&cmd.messages)?;
let request = CopyRequest { ids: messages };
self.copy_or_move(
&cmd.dst,
request,
sender,
true,
StatefulMailbox::moove,
)
}
fn copy_or_move<T>(
&mut self,
dst: &MailboxName<'_>,
request: T,
sender: SendResponse<'_>,
copyuid_in_separate_response: bool,
f: impl FnOnce(
&mut StatefulMailbox,
&T,
&StatelessMailbox,
) -> Result<CopyResponse, Error>,
) -> CmdResult {
let account = account!(self)?;
let _ = selected!(self)?;
let dst = dst.get_utf8(self.unicode_aware);
let dst = account.mailbox(&dst, false).map_err(map_error! {
self,
NxMailbox =>
(No, Some(s::RespTextCode::TryCreate(()))),
UnsafeName =>
(No, Some(s::RespTextCode::Cannot(()))),
MailboxUnselectable =>
(No, Some(s::RespTextCode::Nonexistent(()))),
})?;
let selected = selected!(self)?;
let response = f(selected, &request, &dst).map_err(map_error! {
self,
MailboxFull => (No, Some(s::RespTextCode::Limit(()))),
NxMessage => (No, Some(s::RespTextCode::Nonexistent(()))),
ExpungedMessage => (No, Some(s::RespTextCode::ExpungeIssued(()))),
GaveUpInsertion => (No, Some(s::RespTextCode::Unavailable(()))),
UnaddressableMessage => (No, Some(s::RespTextCode::ClientBug(()))),
BatchTooBig => (No, Some(s::RespTextCode::Limit(()))),
})?;
let mut copyuid_code = Some(s::RespTextCode::CopyUid(s::CopyUidData {
uid_validity: response.uid_validity,
from_uids: Cow::Owned(response.from_uids.to_string()),
to_uids: Cow::Owned(response.to_uids.to_string()),
}));
if copyuid_in_separate_response {
sender(s::Response::Cond(s::CondResponse {
cond: s::RespCondType::Ok,
code: copyuid_code.take(),
quip: None,
}));
}
Ok(s::Response::Cond(s::CondResponse {
cond: s::RespCondType::Ok,
code: copyuid_code,
quip: None,
}))
}
}