diff options
Diffstat (limited to 'src/imap/command')
-rw-r--r-- | src/imap/command/anonymous.rs | 20 | ||||
-rw-r--r-- | src/imap/command/anystate.rs | 27 | ||||
-rw-r--r-- | src/imap/command/authenticated.rs | 150 | ||||
-rw-r--r-- | src/imap/command/examined.rs | 40 | ||||
-rw-r--r-- | src/imap/command/selected.rs | 60 |
5 files changed, 155 insertions, 142 deletions
diff --git a/src/imap/command/anonymous.rs b/src/imap/command/anonymous.rs index 42e2a87..4de5fbd 100644 --- a/src/imap/command/anonymous.rs +++ b/src/imap/command/anonymous.rs @@ -13,11 +13,11 @@ use crate::mail::user::User; //--- dispatching pub struct AnonymousContext<'a> { - pub req: &'a Command<'static>, + pub req: &'a Command<'a>, pub login_provider: &'a ArcLoginProvider, } -pub async fn dispatch(ctx: AnonymousContext<'_>) -> Result<(Response, flow::Transition)> { +pub async fn dispatch<'a>(ctx: AnonymousContext<'a>) -> Result<(Response<'a>, flow::Transition)> { match &ctx.req.body { // Any State CommandBody::Noop => anystate::noop_nothing(ctx.req.tag.clone()), @@ -39,14 +39,14 @@ pub async fn dispatch(ctx: AnonymousContext<'_>) -> Result<(Response, flow::Tran //--- Command controllers, private impl<'a> AnonymousContext<'a> { - async fn capability(self) -> Result<(Response, flow::Transition)> { + async fn capability(self) -> Result<(Response<'a>, flow::Transition)> { let capabilities: NonEmptyVec<Capability> = (vec![Capability::Imap4Rev1, Capability::Idle]).try_into()?; - let res = Response::ok() + let res = Response::build() .to_req(self.req) .message("Server capabilities") .data(Data::Capability(capabilities)) - .build()?; + .ok()?; Ok((res, flow::Transition::None)) } @@ -54,7 +54,7 @@ impl<'a> AnonymousContext<'a> { self, username: &AString<'a>, password: &Secret<AString<'a>>, - ) -> Result<(Response, flow::Transition)> { + ) -> Result<(Response<'a>, flow::Transition)> { let (u, p) = ( std::str::from_utf8(username.as_ref())?, std::str::from_utf8(password.declassify().as_ref())?, @@ -65,10 +65,10 @@ impl<'a> AnonymousContext<'a> { Err(e) => { tracing::debug!(error=%e, "authentication failed"); return Ok(( - Response::no() + Response::build() .to_req(self.req) .message("Authentication failed") - .build()?, + .no()?, flow::Transition::None, )); } @@ -79,10 +79,10 @@ impl<'a> AnonymousContext<'a> { tracing::info!(username=%u, "connected"); Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("Completed") - .build()?, + .ok()?, flow::Transition::Authenticate(user), )) } diff --git a/src/imap/command/anystate.rs b/src/imap/command/anystate.rs index 2d10ad8..ea3bc16 100644 --- a/src/imap/command/anystate.rs +++ b/src/imap/command/anystate.rs @@ -5,45 +5,48 @@ use imap_codec::imap_types::response::{Capability, Data}; use crate::imap::flow; use crate::imap::response::Response; -pub(crate) fn capability(tag: Tag) -> Result<(Response, flow::Transition)> { +pub(crate) fn capability<'a>(tag: Tag<'a>) -> Result<(Response<'a>, flow::Transition)> { let capabilities: NonEmptyVec<Capability> = (vec![Capability::Imap4Rev1, Capability::Idle]).try_into()?; - let res = Response::ok() + let res = Response::build() .tag(tag) .message("Server capabilities") .data(Data::Capability(capabilities)) - .build()?; + .ok()?; Ok((res, flow::Transition::None)) } -pub(crate) fn noop_nothing(tag: Tag) -> Result<(Response, flow::Transition)> { +pub(crate) fn noop_nothing<'a>(tag: Tag<'a>) -> Result<(Response<'a>, flow::Transition)> { Ok(( - Response::ok().tag(tag).message("Noop completed.").build()?, + Response::build().tag(tag).message("Noop completed.").ok()?, flow::Transition::None, )) } -pub(crate) fn logout() -> Result<(Response, flow::Transition)> { +pub(crate) fn logout() -> Result<(Response<'static>, flow::Transition)> { Ok((Response::bye()?, flow::Transition::Logout)) } -pub(crate) fn not_implemented(tag: Tag, what: &str) -> Result<(Response, flow::Transition)> { +pub(crate) fn not_implemented<'a>( + tag: Tag<'a>, + what: &str, +) -> Result<(Response<'a>, flow::Transition)> { Ok(( - Response::bad() + Response::build() .tag(tag) .message(format!("Command not implemented {}", what)) - .build()?, + .bad()?, flow::Transition::None, )) } -pub(crate) fn wrong_state(tag: Tag) -> Result<(Response, flow::Transition)> { +pub(crate) fn wrong_state<'a>(tag: Tag<'a>) -> Result<(Response<'a>, flow::Transition)> { Ok(( - Response::bad() + Response::build() .tag(tag) .message("Command not authorized in this state") - .build()?, + .bad()?, flow::Transition::None, )) } diff --git a/src/imap/command/authenticated.rs b/src/imap/command/authenticated.rs index ca4ad03..c9f9ff7 100644 --- a/src/imap/command/authenticated.rs +++ b/src/imap/command/authenticated.rs @@ -20,14 +20,14 @@ use crate::mail::uidindex::*; use crate::mail::user::{User, MAILBOX_HIERARCHY_DELIMITER as MBX_HIER_DELIM_RAW}; use crate::mail::IMF; -static MAILBOX_HIERARCHY_DELIMITER: QuotedChar = QuotedChar::unvalidated(MBX_HIER_DELIM_RAW); - pub struct AuthenticatedContext<'a> { - pub req: &'a Command<'static>, + pub req: &'a Command<'a>, pub user: &'a Arc<User>, } -pub async fn dispatch(ctx: AuthenticatedContext<'_>) -> Result<(Response, flow::Transition)> { +pub async fn dispatch<'a>( + ctx: AuthenticatedContext<'a>, +) -> Result<(Response<'a>, flow::Transition)> { match &ctx.req.body { // Any state CommandBody::Noop => anystate::noop_nothing(ctx.req.tag.clone()), @@ -68,14 +68,14 @@ pub async fn dispatch(ctx: AuthenticatedContext<'_>) -> Result<(Response, flow:: // --- PRIVATE --- impl<'a> AuthenticatedContext<'a> { - async fn create(self, mailbox: &MailboxCodec<'a>) -> Result<(Response, flow::Transition)> { + async fn create(self, mailbox: &MailboxCodec<'a>) -> Result<(Response<'a>, flow::Transition)> { let name = match mailbox { MailboxCodec::Inbox => { return Ok(( - Response::bad() + Response::build() .to_req(self.req) .message("Cannot create INBOX") - .build()?, + .bad()?, flow::Transition::None, )); } @@ -84,38 +84,38 @@ impl<'a> AuthenticatedContext<'a> { match self.user.create_mailbox(&name).await { Ok(()) => Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("CREATE complete") - .build()?, + .ok()?, flow::Transition::None, )), Err(e) => Ok(( - Response::no() + Response::build() .to_req(self.req) .message(&e.to_string()) - .build()?, + .no()?, flow::Transition::None, )), } } - async fn delete(self, mailbox: &MailboxCodec<'a>) -> Result<(Response, flow::Transition)> { + async fn delete(self, mailbox: &MailboxCodec<'a>) -> Result<(Response<'a>, flow::Transition)> { let name: &str = MailboxName(mailbox).try_into()?; match self.user.delete_mailbox(&name).await { Ok(()) => Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("DELETE complete") - .build()?, + .ok()?, flow::Transition::None, )), Err(e) => Ok(( - Response::no() + Response::build() .to_req(self.req) .message(e.to_string()) - .build()?, + .no()?, flow::Transition::None, )), } @@ -125,23 +125,23 @@ impl<'a> AuthenticatedContext<'a> { self, from: &MailboxCodec<'a>, to: &MailboxCodec<'a>, - ) -> Result<(Response, flow::Transition)> { + ) -> Result<(Response<'a>, flow::Transition)> { let name: &str = MailboxName(from).try_into()?; let new_name: &str = MailboxName(to).try_into()?; match self.user.rename_mailbox(&name, &new_name).await { Ok(()) => Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("RENAME complete") - .build()?, + .ok()?, flow::Transition::None, )), Err(e) => Ok(( - Response::no() + Response::build() .to_req(self.req) .message(e.to_string()) - .build()?, + .no()?, flow::Transition::None, )), } @@ -152,14 +152,16 @@ impl<'a> AuthenticatedContext<'a> { reference: &MailboxCodec<'a>, mailbox_wildcard: &ListMailbox<'a>, is_lsub: bool, - ) -> Result<(Response, flow::Transition)> { + ) -> Result<(Response<'a>, flow::Transition)> { + let mbx_hier_delim: QuotedChar = QuotedChar::unvalidated(MBX_HIER_DELIM_RAW); + let reference: &str = MailboxName(reference).try_into()?; if !reference.is_empty() { return Ok(( - Response::bad() + Response::build() .to_req(self.req) .message("References not supported") - .build()?, + .bad()?, flow::Transition::None, )); } @@ -172,28 +174,28 @@ impl<'a> AuthenticatedContext<'a> { if wildcard.is_empty() { if is_lsub { return Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("LSUB complete") .data(Data::Lsub { items: vec![], - delimiter: Some(MAILBOX_HIERARCHY_DELIMITER), + delimiter: Some(mbx_hier_delim), mailbox: "".try_into().unwrap(), }) - .build()?, + .ok()?, flow::Transition::None, )); } else { return Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("LIST complete") .data(Data::List { items: vec![], - delimiter: Some(MAILBOX_HIERARCHY_DELIMITER), + delimiter: Some(mbx_hier_delim), mailbox: "".try_into().unwrap(), }) - .build()?, + .ok()?, flow::Transition::None, )); } @@ -227,13 +229,13 @@ impl<'a> AuthenticatedContext<'a> { if is_lsub { ret.push(Data::Lsub { items, - delimiter: Some(MAILBOX_HIERARCHY_DELIMITER), + delimiter: Some(mbx_hier_delim), mailbox, }); } else { ret.push(Data::List { items, - delimiter: Some(MAILBOX_HIERARCHY_DELIMITER), + delimiter: Some(mbx_hier_delim), mailbox, }); } @@ -246,11 +248,11 @@ impl<'a> AuthenticatedContext<'a> { "LIST completed" }; Ok(( - Response::ok() + Response::build() .to_req(self.req) .message(msg) - .set_data(ret) - .build()?, + .many_data(ret) + .ok()?, flow::Transition::None, )) } @@ -259,23 +261,23 @@ impl<'a> AuthenticatedContext<'a> { self, mailbox: &MailboxCodec<'a>, attributes: &[StatusDataItemName], - ) -> Result<(Response, flow::Transition)> { + ) -> Result<(Response<'a>, 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::no() + Response::build() .to_req(self.req) .message("Mailbox does not exist") - .build()?, + .no()?, flow::Transition::None, )) } }; - let (view, _data) = MailboxView::new(mb).await?; + let view = MailboxView::new(mb).await; let mut ret_attrs = vec![]; for attr in attributes.iter() { @@ -302,57 +304,63 @@ impl<'a> AuthenticatedContext<'a> { }; Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("STATUS completed") .data(data) - .build()?, + .ok()?, flow::Transition::None, )) } - async fn subscribe(self, mailbox: &MailboxCodec<'a>) -> Result<(Response, flow::Transition)> { + async fn subscribe( + self, + mailbox: &MailboxCodec<'a>, + ) -> Result<(Response<'a>, flow::Transition)> { let name: &str = MailboxName(mailbox).try_into()?; if self.user.has_mailbox(&name).await? { Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("SUBSCRIBE complete") - .build()?, + .ok()?, flow::Transition::None, )) } else { Ok(( - Response::bad() + Response::build() .to_req(self.req) .message(format!("Mailbox {} does not exist", name)) - .build()?, + .bad()?, flow::Transition::None, )) } } - async fn unsubscribe(self, mailbox: &MailboxCodec<'a>) -> Result<(Response, flow::Transition)> { + async fn unsubscribe( + self, + mailbox: &MailboxCodec<'a>, + ) -> Result<(Response<'a>, flow::Transition)> { let name: &str = MailboxName(mailbox).try_into()?; if self.user.has_mailbox(&name).await? { Ok(( - Response::bad() + Response::build() .to_req(self.req) .message(format!( "Cannot unsubscribe from mailbox {}: not supported by Aerogramme", name )) - .build()?, + .bad()?, flow::Transition::None, )) } else { Ok(( - Response::no() + Response::build() .to_req(self.req) .message(format!("Mailbox {} does not exist", name)) - .build()?, + .no()?, flow::Transition::None, )) } @@ -391,7 +399,7 @@ impl<'a> AuthenticatedContext<'a> { * TRACE END --- */ - async fn select(self, mailbox: &MailboxCodec<'a>) -> Result<(Response, flow::Transition)> { + async fn select(self, mailbox: &MailboxCodec<'a>) -> Result<(Response<'a>, flow::Transition)> { let name: &str = MailboxName(mailbox).try_into()?; let mb_opt = self.user.open_mailbox(&name).await?; @@ -399,29 +407,30 @@ impl<'a> AuthenticatedContext<'a> { Some(mb) => mb, None => { return Ok(( - Response::no() + Response::build() .to_req(self.req) .message("Mailbox does not exist") - .build()?, + .no()?, flow::Transition::None, )) } }; tracing::info!(username=%self.user.username, mailbox=%name, "mailbox.selected"); - let (mb, data) = MailboxView::new(mb).await?; + let mb = MailboxView::new(mb).await; + let data = mb.summary()?; Ok(( - Response::ok() + Response::build() .message("Select completed") .code(Code::ReadWrite) - .data(data) - .build()?, + .set_body(data) + .ok()?, flow::Transition::Select(mb), )) } - async fn examine(self, mailbox: &MailboxCodec<'a>) -> Result<(Response, flow::Transition)> { + async fn examine(self, mailbox: &MailboxCodec<'a>) -> Result<(Response<'a>, flow::Transition)> { let name: &str = MailboxName(mailbox).try_into()?; let mb_opt = self.user.open_mailbox(&name).await?; @@ -429,25 +438,26 @@ impl<'a> AuthenticatedContext<'a> { Some(mb) => mb, None => { return Ok(( - Response::no() + Response::build() .to_req(self.req) .message("Mailbox does not exist") - .build()?, + .no()?, flow::Transition::None, )) } }; tracing::info!(username=%self.user.username, mailbox=%name, "mailbox.examined"); - let (mb, data) = MailboxView::new(mb).await?; + let mb = MailboxView::new(mb).await; + let data = mb.summary()?; Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("Examine completed") .code(Code::ReadOnly) - .data(data) - .build()?, + .set_body(data) + .ok()?, flow::Transition::Examine(mb), )) } @@ -458,24 +468,24 @@ impl<'a> AuthenticatedContext<'a> { flags: &[Flag<'a>], date: &Option<DateTime>, message: &Literal<'a>, - ) -> Result<(Response, flow::Transition)> { + ) -> Result<(Response<'a>, flow::Transition)> { let append_tag = self.req.tag.clone(); match self.append_internal(mailbox, flags, date, message).await { Ok((_mb, uidvalidity, uid)) => Ok(( - Response::ok() + Response::build() .tag(append_tag) .message("APPEND completed") .code(Code::Other(CodeOther::unvalidated( format!("APPENDUID {} {}", uidvalidity, uid).into_bytes(), ))) - .build()?, + .ok()?, flow::Transition::None, )), Err(e) => Ok(( - Response::no() + Response::build() .tag(append_tag) .message(e.to_string()) - .build()?, + .no()?, flow::Transition::None, )), } diff --git a/src/imap/command/examined.rs b/src/imap/command/examined.rs index cab3fdd..7f9c39c 100644 --- a/src/imap/command/examined.rs +++ b/src/imap/command/examined.rs @@ -19,7 +19,7 @@ pub struct ExaminedContext<'a> { pub mailbox: &'a mut MailboxView, } -pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response, flow::Transition)> { +pub async fn dispatch<'a>(ctx: ExaminedContext<'a>) -> Result<(Response<'a>, flow::Transition)> { match &ctx.req.body { // Any State // noop is specific to this state @@ -41,10 +41,10 @@ pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response, flow::Trans } => ctx.search(charset, criteria, uid).await, CommandBody::Noop | CommandBody::Check => ctx.noop().await, CommandBody::Expunge { .. } | CommandBody::Store { .. } => Ok(( - Response::bad() + Response::build() .to_req(ctx.req) .message("Forbidden command: can't write in read-only mode (EXAMINE)") - .build()?, + .bad()?, flow::Transition::None, )), @@ -58,12 +58,12 @@ pub async fn dispatch(ctx: ExaminedContext<'_>) -> Result<(Response, flow::Trans impl<'a> ExaminedContext<'a> { /// CLOSE in examined state is not the same as in selected state /// (in selected state it also does an EXPUNGE, here it doesn't) - async fn close(self) -> Result<(Response, flow::Transition)> { + async fn close(self) -> Result<(Response<'a>, flow::Transition)> { Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("CLOSE completed") - .build()?, + .ok()?, flow::Transition::Unselect, )) } @@ -71,23 +71,23 @@ impl<'a> ExaminedContext<'a> { pub async fn fetch( self, sequence_set: &SequenceSet, - attributes: &MacroOrMessageDataItemNames<'a>, + attributes: &'a MacroOrMessageDataItemNames<'a>, uid: &bool, - ) -> Result<(Response, flow::Transition)> { + ) -> Result<(Response<'a>, flow::Transition)> { match self.mailbox.fetch(sequence_set, attributes, uid).await { Ok(resp) => Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("FETCH completed") - .set_data(resp) - .build()?, + .set_body(resp) + .ok()?, flow::Transition::None, )), Err(e) => Ok(( - Response::no() + Response::build() .to_req(self.req) .message(e.to_string()) - .build()?, + .no()?, flow::Transition::None, )), } @@ -98,26 +98,26 @@ impl<'a> ExaminedContext<'a> { _charset: &Option<Charset<'a>>, _criteria: &SearchKey<'a>, _uid: &bool, - ) -> Result<(Response, flow::Transition)> { + ) -> Result<(Response<'a>, flow::Transition)> { Ok(( - Response::bad() + Response::build() .to_req(self.req) .message("Not implemented") - .build()?, + .bad()?, flow::Transition::None, )) } - pub async fn noop(self) -> Result<(Response, flow::Transition)> { + pub async fn noop(self) -> Result<(Response<'a>, flow::Transition)> { self.mailbox.mailbox.force_sync().await?; let updates = self.mailbox.update().await?; Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("NOOP completed.") - .set_data(updates) - .build()?, + .set_body(updates) + .ok()?, flow::Transition::None, )) } diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs index 148901d..cd5d221 100644 --- a/src/imap/command/selected.rs +++ b/src/imap/command/selected.rs @@ -23,7 +23,7 @@ pub struct SelectedContext<'a> { pub mailbox: &'a mut MailboxView, } -pub async fn dispatch(ctx: SelectedContext<'_>) -> Result<(Response, flow::Transition)> { +pub async fn dispatch<'a>(ctx: SelectedContext<'a>) -> Result<(Response<'a>, flow::Transition)> { match &ctx.req.body { // Any State // noop is specific to this state @@ -65,13 +65,13 @@ pub async fn dispatch(ctx: SelectedContext<'_>) -> Result<(Response, flow::Trans // --- PRIVATE --- impl<'a> SelectedContext<'a> { - async fn close(self) -> Result<(Response, flow::Transition)> { + async fn close(self) -> Result<(Response<'a>, flow::Transition)> { // We expunge messages, // but we don't send the untagged EXPUNGE responses let tag = self.req.tag.clone(); self.expunge().await?; Ok(( - Response::ok().tag(tag).message("CLOSE completed").build()?, + Response::build().tag(tag).message("CLOSE completed").ok()?, flow::Transition::Unselect, )) } @@ -79,23 +79,23 @@ impl<'a> SelectedContext<'a> { pub async fn fetch( self, sequence_set: &SequenceSet, - attributes: &MacroOrMessageDataItemNames<'a>, + attributes: &'a MacroOrMessageDataItemNames<'a>, uid: &bool, - ) -> Result<(Response, flow::Transition)> { + ) -> Result<(Response<'a>, flow::Transition)> { match self.mailbox.fetch(sequence_set, attributes, uid).await { Ok(resp) => Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("FETCH completed") - .set_data(resp) - .build()?, + .set_body(resp) + .ok()?, flow::Transition::None, )), Err(e) => Ok(( - Response::no() + Response::build() .to_req(self.req) .message(e.to_string()) - .build()?, + .no()?, flow::Transition::None, )), } @@ -106,40 +106,40 @@ impl<'a> SelectedContext<'a> { _charset: &Option<Charset<'a>>, _criteria: &SearchKey<'a>, _uid: &bool, - ) -> Result<(Response, flow::Transition)> { + ) -> Result<(Response<'a>, flow::Transition)> { Ok(( - Response::bad() + Response::build() .to_req(self.req) .message("Not implemented") - .build()?, + .bad()?, flow::Transition::None, )) } - pub async fn noop(self) -> Result<(Response, flow::Transition)> { + pub async fn noop(self) -> Result<(Response<'a>, flow::Transition)> { self.mailbox.mailbox.force_sync().await?; let updates = self.mailbox.update().await?; Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("NOOP completed.") - .set_data(updates) - .build()?, + .set_body(updates) + .ok()?, flow::Transition::None, )) } - async fn expunge(self) -> Result<(Response, flow::Transition)> { + async fn expunge(self) -> Result<(Response<'a>, flow::Transition)> { let tag = self.req.tag.clone(); let data = self.mailbox.expunge().await?; Ok(( - Response::ok() + Response::build() .tag(tag) .message("EXPUNGE completed") - .data(data) - .build()?, + .set_body(data) + .ok()?, flow::Transition::None, )) } @@ -151,18 +151,18 @@ impl<'a> SelectedContext<'a> { response: &StoreResponse, flags: &[Flag<'a>], uid: &bool, - ) -> Result<(Response, flow::Transition)> { + ) -> Result<(Response<'a>, flow::Transition)> { let data = self .mailbox .store(sequence_set, kind, response, flags, uid) .await?; Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("STORE completed") - .set_data(data) - .build()?, + .set_body(data) + .ok()?, flow::Transition::None, )) } @@ -172,7 +172,7 @@ impl<'a> SelectedContext<'a> { sequence_set: &SequenceSet, mailbox: &MailboxCodec<'a>, uid: &bool, - ) -> Result<(Response, flow::Transition)> { + ) -> Result<(Response<'a>, flow::Transition)> { let name: &str = MailboxName(mailbox).try_into()?; let mb_opt = self.user.open_mailbox(&name).await?; @@ -180,11 +180,11 @@ impl<'a> SelectedContext<'a> { Some(mb) => mb, None => { return Ok(( - Response::no() + Response::build() .to_req(self.req) .message("Destination mailbox does not exist") .code(Code::TryCreate) - .build()?, + .no()?, flow::Transition::None, )) } @@ -208,13 +208,13 @@ impl<'a> SelectedContext<'a> { ); Ok(( - Response::ok() + Response::build() .to_req(self.req) .message("COPY completed") .code(Code::Other(CodeOther::unvalidated( format!("COPYUID {}", copyuid_str).into_bytes(), ))) - .build()?, + .ok()?, flow::Transition::None, )) } |