Allow STATUS and ACL operations on virtual mailboxes

This commit is contained in:
mdecimus 2023-09-02 16:41:20 +02:00
parent 3fa5c769bd
commit 9860631691
4 changed files with 97 additions and 18 deletions

View file

@ -37,7 +37,8 @@ use super::{SelectedMailbox, Session, SessionData, State, IMAP};
impl<T: AsyncRead> Session<T> {
pub async fn ingest(&mut self, bytes: &[u8]) -> crate::Result<bool> {
/*for line in String::from_utf8_lossy(bytes).split("\r\n") {
let c = println!("<- {:?}", &line[..std::cmp::min(line.len(), 100)]);
//let c = println!("<- {:?}", &line[..std::cmp::min(line.len(), 100)]);
let c = println!("{}", line);
}*/
tracing::trace!(parent: &self.span,

View file

@ -57,12 +57,8 @@ pub fn spawn_writer(mut stream: Event, span: tracing::Span) -> mpsc::Sender<Even
data = std::str::from_utf8(bytes.as_ref()).unwrap_or_default(),
size = bytes.len()
);
/*let c = println!(
"<- {:?}",
String::from_utf8_lossy(
&bytes[..std::cmp::min(bytes.len(), 100)]
)
);*/
//let c = print!("{}", String::from_utf8_lossy(&bytes));
match stream_tx.write_all(bytes.as_ref()).await {
Ok(_) => {
@ -105,6 +101,8 @@ pub fn spawn_writer(mut stream: Event, span: tracing::Span) -> mpsc::Sender<Even
size = bytes.len()
);
//let c = print!("{}", String::from_utf8_lossy(&bytes));
match stream_tx.write_all(bytes.as_ref()).await {
Ok(_) => {
let _ = stream_tx.flush().await;

View file

@ -58,7 +58,7 @@ impl<T: AsyncRead> Session<T> {
tokio::spawn(async move {
match data.get_acl_mailbox(&arguments, true).await {
Ok((_, values, _)) => {
Ok(Some((_, values, _))) => {
let mut permissions = Vec::new();
if let Some(acls) = values
.inner
@ -137,6 +137,21 @@ impl<T: AsyncRead> Session<T> {
)
.await;
}
Ok(None) => {
// Response for "All Mail" folder
data.write_bytes(
StatusResponse::completed(Command::GetAcl)
.with_tag(arguments.tag)
.serialize(
GetAclResponse {
mailbox_name: arguments.mailbox_name,
permissions: vec![],
}
.into_bytes(is_rev2),
),
)
.await;
}
Err(response) => {
data.write_bytes(response.with_tag(arguments.tag).into_bytes())
.await;
@ -157,7 +172,7 @@ impl<T: AsyncRead> Session<T> {
tokio::spawn(async move {
match data.get_acl_mailbox(&arguments, false).await {
Ok((mailbox, values, access_token)) => {
Ok(Some((mailbox, values, access_token))) => {
data.write_bytes(
StatusResponse::completed(Command::MyRights)
.with_tag(arguments.tag)
@ -212,6 +227,30 @@ impl<T: AsyncRead> Session<T> {
)
.await;
}
Ok(None) => {
// Response for All mail folder
data.write_bytes(
StatusResponse::completed(Command::MyRights)
.with_tag(arguments.tag)
.serialize(
MyRightsResponse {
mailbox_name: arguments.mailbox_name,
rights: vec![
Rights::Read,
Rights::Lookup,
Rights::Insert,
Rights::DeleteMessages,
Rights::Expunge,
Rights::Seen,
Rights::Write,
Rights::Post,
],
}
.into_bytes(is_rev2),
),
)
.await;
}
Err(response) => {
data.write_bytes(response.with_tag(arguments.tag).into_bytes())
.await;
@ -233,7 +272,18 @@ impl<T: AsyncRead> Session<T> {
tokio::spawn(async move {
// Validate mailbox
let (mailbox, values, _) = match data.get_acl_mailbox(&arguments, true).await {
Ok(result) => result,
Ok(Some(result)) => result,
Ok(None) => {
data.write_bytes(
StatusResponse::no(
"ACL operations are not permitted on this mailbox.",
)
.with_tag(arguments.tag)
.into_bytes(),
)
.await;
return;
}
Err(response) => {
data.write_bytes(response.with_tag(arguments.tag).into_bytes())
.await;
@ -475,7 +525,7 @@ impl SessionData {
&self,
arguments: &Arguments,
validate: bool,
) -> crate::op::Result<(MailboxId, HashedValue<Object<Value>>, Arc<AccessToken>)> {
) -> crate::op::Result<Option<(MailboxId, HashedValue<Object<Value>>, Arc<AccessToken>)>> {
if let Some(mailbox) = self.get_mailbox_by_name(&arguments.mailbox_name) {
if let Some(mailbox_id) = mailbox.mailbox_id {
match (
@ -497,7 +547,7 @@ impl SessionData {
.effective_acl(&access_token)
.contains(Acl::Administer)
{
Ok((mailbox, values, access_token))
Ok(Some((mailbox, values, access_token)))
} else {
Err(StatusResponse::no(
"You do not have enough permissions to perform this operation.",
@ -509,9 +559,7 @@ impl SessionData {
_ => Err(StatusResponse::database_failure()),
}
} else {
Err(StatusResponse::no(
"ACL operations are not permitted on this mailbox.",
))
Ok(None)
}
} else {
Err(StatusResponse::no("Mailbox does not exist."))

View file

@ -87,9 +87,41 @@ impl SessionData {
let mailbox = if let Some(mailbox) = self.get_mailbox_by_name(&mailbox_name) {
mailbox
} else {
return Err(
StatusResponse::no("Mailbox does not exist.").with_code(ResponseCode::NonExistent)
);
// Some IMAP clients will try to get the status of a mailbox with the NoSelect flag
return if mailbox_name == self.imap.name_shared
|| mailbox_name
.split_once('/')
.map_or(false, |(base_name, path)| {
base_name == self.imap.name_shared && !path.contains('/')
})
{
Ok(StatusItem {
mailbox_name,
items: items
.iter()
.map(|item| {
(
*item,
match item {
Status::Messages
| Status::Size
| Status::Unseen
| Status::Recent
| Status::Deleted
| Status::HighestModSeq => StatusItemType::Number(0),
Status::UidNext | Status::UidValidity => {
StatusItemType::Number(1)
}
Status::MailboxId => StatusItemType::String("none".to_string()),
},
)
})
.collect(),
})
} else {
Err(StatusResponse::no("Mailbox does not exist.")
.with_code(ResponseCode::NonExistent))
};
};
// Make sure all requested fields are up to date