IMAP: Mailbox object-quota only enforced in JMAP

This commit is contained in:
Maurus Decimus
2026-07-19 07:52:31 +02:00
parent 45602a35ba
commit 6fb4d2b628
34 changed files with 144 additions and 62 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "common"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
build = "build.rs"

View File

@@ -1,6 +1,6 @@
[package]
name = "coordinator"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "dav-proto"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "dav"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "directory"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "email"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -7,6 +7,7 @@
use super::*;
use crate::cache::MessageCacheFetch;
use common::{Server, storage::index::ObjectIndexBuilder};
use registry::schema::enums::StorageQuota;
use std::future::Future;
use store::write::BatchBuilder;
use trc::AddContext;
@@ -129,6 +130,13 @@ impl MailboxFnc for Server {
return Ok(None);
}
let account = self.account(account_id).await.caused_by(trc::location!())?;
if cache.mailboxes.items.len() + create_paths.len()
> self.object_quota(account.object_quotas(), StorageQuota::MaxMailboxes) as usize
{
return Ok(None);
}
let mut next_document_id = self
.store()
.assign_document_ids(account_id, Collection::Mailbox, create_paths.len() as u64)

View File

@@ -1,6 +1,6 @@
[package]
name = "groupware"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "http_proto"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "http"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "imap_proto"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "imap"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -16,7 +16,7 @@ use imap_proto::{
protocol::{ObjectId, create::Arguments, list::Attribute},
receiver::Request,
};
use registry::schema::enums::Permission;
use registry::schema::enums::{Permission, StorageQuota};
use std::time::Instant;
use store::write::BatchBuilder;
use trc::AddContext;
@@ -71,6 +71,35 @@ impl<T: SessionStream> SessionData<T> {
.imap_ctx(&arguments.tag, trc::location!())?;
debug_assert!(!params.path.is_empty());
// Validate quota
let account = self
.server
.account(params.account_id)
.await
.imap_ctx(&arguments.tag, trc::location!())?;
let mailbox_count = self
.server
.get_cached_messages(params.account_id)
.await
.imap_ctx(&arguments.tag, trc::location!())?
.mailboxes
.items
.len();
if mailbox_count + params.path.len()
> self
.server
.object_quota(account.object_quotas(), StorageQuota::MaxMailboxes) as usize
{
return Err(trc::ImapEvent::Error
.into_err()
.details(concat!(
"There are too many mailboxes, ",
"please delete some before adding a new one."
))
.code(ResponseCode::OverQuota)
.id(arguments.tag.clone()));
}
// Build batch
let mut parent_id = params.parent_mailbox_id.map(|id| id + 1).unwrap_or(0);
let mut create_ids = Vec::with_capacity(params.path.len());

View File

@@ -14,7 +14,8 @@ use imap_proto::{
protocol::{ObjectId, rename::Arguments},
receiver::Request,
};
use registry::schema::enums::Permission;
use email::cache::MessageCacheFetch;
use registry::schema::enums::{Permission, StorageQuota};
use std::time::Instant;
use store::{
ValueKey,
@@ -134,6 +135,38 @@ impl<T: SessionStream> SessionData<T> {
// Get new mailbox name from path
let new_mailbox_name = params.path.pop().unwrap();
// Validate quota
if !params.path.is_empty() {
let account = self
.server
.account(params.account_id)
.await
.imap_ctx(&arguments.tag, trc::location!())?;
let mailbox_count = self
.server
.get_cached_messages(params.account_id)
.await
.imap_ctx(&arguments.tag, trc::location!())?
.mailboxes
.items
.len();
if mailbox_count + params.path.len()
> self
.server
.object_quota(account.object_quotas(), StorageQuota::MaxMailboxes)
as usize
{
return Err(trc::ImapEvent::Error
.into_err()
.details(concat!(
"There are too many mailboxes, ",
"please delete some before adding a new one."
))
.code(ResponseCode::OverQuota)
.id(arguments.tag.clone()));
}
}
// Build batch
let mut parent_id = params.parent_mailbox_id.map(|id| id + 1).unwrap_or(0);
let mut create_ids = Vec::with_capacity(params.path.len());

View File

@@ -1,6 +1,6 @@
[package]
name = "jmap_proto"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "jmap"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -7,7 +7,7 @@ homepage = "https://stalw.art"
keywords = ["imap", "jmap", "smtp", "email", "mail", "webdav", "server"]
categories = ["email"]
license = "AGPL-3.0-only OR LicenseRef-SEL"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[[bin]]

View File

@@ -1,6 +1,6 @@
[package]
name = "managesieve"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "migration"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "nlp"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "pop3"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "registry"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "services"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -7,7 +7,7 @@ homepage = "https://stalw.art/smtp"
keywords = ["smtp", "email", "mail", "server"]
categories = ["email"]
license = "AGPL-3.0-only OR LicenseRef-SEL"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "spam-filter"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "store"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "trc"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "event_macro"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[lib]

View File

@@ -1,6 +1,6 @@
[package]
name = "types"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "utils"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "proc_macros"
version = "0.16.13"
version = "0.16.14"
edition = "2024"
[lib]