diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 9e880859..b4971a69 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -#![warn(clippy::large_futures)] +#![deny(clippy::large_futures)] use crate::auth::{AccessTokenInner, EmailAddress}; use crate::manager::application::WebApplications; diff --git a/crates/common/src/manager/application.rs b/crates/common/src/manager/application.rs index 28d45604..d369cc7d 100644 --- a/crates/common/src/manager/application.rs +++ b/crates/common/src/manager/application.rs @@ -245,17 +245,18 @@ impl WebApplicationManager { resource }; - // Uncompress - let mut bundle = zip::ZipArchive::new(Cursor::new(bundle)).map_err(|err| { - trc::ResourceEvent::Error - .caused_by(trc::location!()) - .reason(err) - .ctx(Key::Url, self.url.clone()) - .details("Failed to decompress application bundle") - })?; - let mut routes = AHashMap::new(); - for i in 0..bundle.len() { - let (file_name, contents) = { + let url = self.url.clone(); + let bundle_path = self.bundle_path.path.clone(); + let routes = tokio::task::spawn_blocking(move || -> trc::Result<_> { + let mut bundle = zip::ZipArchive::new(Cursor::new(bundle)).map_err(|err| { + trc::ResourceEvent::Error + .caused_by(trc::location!()) + .reason(err) + .ctx(Key::Url, url.clone()) + .details("Failed to decompress application bundle") + })?; + let mut routes = AHashMap::new(); + for i in 0..bundle.len() { let mut file = bundle.by_index(i).map_err(|err| { trc::ResourceEvent::Error .caused_by(trc::location!()) @@ -268,35 +269,43 @@ impl WebApplicationManager { let mut contents = Vec::new(); file.read_to_end(&mut contents).map_err(unpack_error)?; - (file.name().to_string(), contents) - }; - let path = self.bundle_path.path.join(format!("{i:02}")); - tokio::fs::write(&path, contents) - .await - .map_err(unpack_error)?; + let file_name = file.name().to_string(); + drop(file); - let resource = Resource { - content_type: match file_name - .rsplit_once('.') - .map(|(_, ext)| ext) - .unwrap_or_default() - { - "html" => "text/html", - "css" => "text/css", - "wasm" => "application/wasm", - "js" => "application/javascript", - "json" => "application/json", - "png" => "image/png", - "svg" => "image/svg+xml", - "ico" => "image/x-icon", - _ => "application/octet-stream", - } - .into(), - contents: path, - }; + let path = bundle_path.join(format!("{i:02}")); + std::fs::write(&path, contents).map_err(unpack_error)?; - routes.insert(file_name, resource); - } + let resource = Resource { + content_type: match file_name + .rsplit_once('.') + .map(|(_, ext)| ext) + .unwrap_or_default() + { + "html" => "text/html", + "css" => "text/css", + "wasm" => "application/wasm", + "js" => "application/javascript", + "json" => "application/json", + "png" => "image/png", + "svg" => "image/svg+xml", + "ico" => "image/x-icon", + _ => "application/octet-stream", + } + .into(), + contents: path, + }; + + routes.insert(file_name, resource); + } + Ok(routes) + }) + .await + .map_err(|err| { + trc::ResourceEvent::Error + .caused_by(trc::location!()) + .reason(err) + .details("Bundle unpack task panicked") + })??; trc::event!( Resource(trc::ResourceEvent::ApplicationUnpacked), diff --git a/crates/common/src/manager/boot.rs b/crates/common/src/manager/boot.rs index a21b48f4..b4e125ab 100644 --- a/crates/common/src/manager/boot.rs +++ b/crates/common/src/manager/boot.rs @@ -153,7 +153,8 @@ impl BootManager { match import_export { StoreOp::None => { // Parse components - let core = Box::pin(Core::parse(&mut bootstrap, storage)).await; + let core: Box = + Box::new(Box::pin(Core::parse(&mut bootstrap, storage)).await); let data = Data::parse(&mut bootstrap).await; let cache = Caches::parse(&mut bootstrap).await; @@ -211,7 +212,7 @@ impl BootManager { ); let (ipc, ipc_rxs) = build_ipc(!core.storage.coordinator.is_none()); let inner = Arc::new(Inner { - shared_core: ArcSwap::from_pointee(core), + shared_core: ArcSwap::new(Arc::from(core)), data, ipc, cache, diff --git a/crates/coordinator/src/lib.rs b/crates/coordinator/src/lib.rs index 5a3cbf1f..9d9a5115 100644 --- a/crates/coordinator/src/lib.rs +++ b/crates/coordinator/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + #[allow(unused_imports)] use std::sync::Arc; diff --git a/crates/dav-proto/resources/requests/mkcol-002.json b/crates/dav-proto/resources/requests/mkcol-002.json index 05e212a0..e98a7dc6 100644 --- a/crates/dav-proto/resources/requests/mkcol-002.json +++ b/crates/dav-proto/resources/requests/mkcol-002.json @@ -175,7 +175,9 @@ 10 ], "bysetpos": [], - "wkst": null + "wkst": null, + "rscale": null, + "skip": null } } ] @@ -293,7 +295,9 @@ 4 ], "bysetpos": [], - "wkst": null + "wkst": null, + "rscale": null, + "skip": null } } ] diff --git a/crates/dav-proto/resources/requests/propertyupdate-001.json b/crates/dav-proto/resources/requests/propertyupdate-001.json index 7d6b32a2..d16de765 100644 --- a/crates/dav-proto/resources/requests/propertyupdate-001.json +++ b/crates/dav-proto/resources/requests/propertyupdate-001.json @@ -175,7 +175,9 @@ 10 ], "bysetpos": [], - "wkst": null + "wkst": null, + "rscale": null, + "skip": null } } ] @@ -293,7 +295,9 @@ 4 ], "bysetpos": [], - "wkst": null + "wkst": null, + "rscale": null, + "skip": null } } ] diff --git a/crates/dav-proto/src/schema/property.rs b/crates/dav-proto/src/schema/property.rs index 38d69238..9709923c 100644 --- a/crates/dav-proto/src/schema/property.rs +++ b/crates/dav-proto/src/schema/property.rs @@ -165,7 +165,7 @@ pub enum DavValue { Href(List), Acl(List), AclRestrictions(AclRestrictions), - Response(Response), + Response(Box), DeadProperty(DeadProperty), SupportedAddressData, SupportedCalendarData, diff --git a/crates/dav/src/common/propfind.rs b/crates/dav/src/common/propfind.rs index 3a66803b..009cad40 100644 --- a/crates/dav/src/common/propfind.rs +++ b/crates/dav/src/common/propfind.rs @@ -625,7 +625,7 @@ impl PropFindRequestHandler for Server { &query.propfind, ) .await? - .map(DavValue::Response) + .map(|r| DavValue::Response(Box::new(r))) .unwrap_or(DavValue::Null), )); } @@ -675,7 +675,7 @@ impl PropFindRequestHandler for Server { &query.propfind, ) .await? - .map(DavValue::Response) + .map(|r| DavValue::Response(Box::new(r))) .unwrap_or(DavValue::Null), )); } diff --git a/crates/dav/src/lib.rs b/crates/dav/src/lib.rs index 61b105d5..3adfc0ab 100644 --- a/crates/dav/src/lib.rs +++ b/crates/dav/src/lib.rs @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -#![warn(clippy::large_futures)] +#![deny(clippy::large_futures)] pub mod calendar; pub mod card; diff --git a/crates/directory/src/lib.rs b/crates/directory/src/lib.rs index 619dbecf..1e4450c9 100644 --- a/crates/directory/src/lib.rs +++ b/crates/directory/src/lib.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -#![warn(clippy::large_futures)] +#![deny(clippy::large_futures)] use crate::backend::oidc::OpenIdDirectory; use backend::{ldap::LdapDirectory, sql::SqlDirectory}; diff --git a/crates/email/src/lib.rs b/crates/email/src/lib.rs index 4112d120..42da9acb 100644 --- a/crates/email/src/lib.rs +++ b/crates/email/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + pub mod cache; pub mod identity; pub mod mailbox; diff --git a/crates/groupware/src/lib.rs b/crates/groupware/src/lib.rs index dcf75439..0001a113 100644 --- a/crates/groupware/src/lib.rs +++ b/crates/groupware/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + use calcard::common::timezone::Tz; use common::DavResources; use percent_encoding::{AsciiSet, CONTROLS}; diff --git a/crates/http-proto/src/lib.rs b/crates/http-proto/src/lib.rs index 19062a4a..f4fffffb 100644 --- a/crates/http-proto/src/lib.rs +++ b/crates/http-proto/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + pub mod context; pub mod request; pub mod response; diff --git a/crates/http/src/api/diagnose.rs b/crates/http/src/api/diagnose.rs index 5c6f3109..ee6bac87 100644 --- a/crates/http/src/api/diagnose.rs +++ b/crates/http/src/api/diagnose.rs @@ -649,7 +649,7 @@ async fn delivery_diagnose( if r.is_positive_completion() { Ok(r) } else { - Err(ClientError::UnexpectedReply(r)) + Err(ClientError::UnexpectedReply(Box::new(r))) } }) { Ok(_) => { @@ -669,7 +669,7 @@ async fn delivery_diagnose( if r.is_positive_completion() { Ok(r) } else { - Err(ClientError::UnexpectedReply(r)) + Err(ClientError::UnexpectedReply(Box::new(r))) } }) { Ok(_) => { diff --git a/crates/http/src/api/mod.rs b/crates/http/src/api/mod.rs index 9cfc4369..6fdcec55 100644 --- a/crates/http/src/api/mod.rs +++ b/crates/http/src/api/mod.rs @@ -71,10 +71,10 @@ impl ManagementApi for Server { "auth" if is_post => { self.is_http_anonymous_request_allowed(session.remote_ip) .await?; - self.handle_login_request( + Box::pin(self.handle_login_request( session, body.ok_or_else(|| trc::LimitEvent::SizeRequest.into_err())?, - ) + )) .await } "discover" => { diff --git a/crates/http/src/auth/authenticate.rs b/crates/http/src/auth/authenticate.rs index 5d9f2714..085c6495 100644 --- a/crates/http/src/auth/authenticate.rs +++ b/crates/http/src/auth/authenticate.rs @@ -82,13 +82,12 @@ impl Authenticator for Server { }; // Authenticate - let access_token = self - .authenticate(&AuthRequest::from_credentials( - credentials, - session.session_id, - session.remote_ip, - )) - .await?; + let access_token = Box::pin(self.authenticate(&AuthRequest::from_credentials( + credentials, + session.session_id, + session.remote_ip, + ))) + .await?; // Cache credentials self.inner.cache.http_auth.insert( diff --git a/crates/http/src/auth/oauth/registration.rs b/crates/http/src/auth/oauth/registration.rs index 9a64a83b..9e8a982b 100644 --- a/crates/http/src/auth/oauth/registration.rs +++ b/crates/http/src/auth/oauth/registration.rs @@ -49,7 +49,7 @@ impl ClientRegistrationHandler for Server { ) -> trc::Result { let tenant_id = if !self.core.oauth.allow_anonymous_client_registration { // Authenticate request - let (_, access_token) = self.authenticate_headers(req, &session).await?; + let (_, access_token) = Box::pin(self.authenticate_headers(req, &session)).await?; // Validate permissions access_token.enforce_permission(Permission::OAuthClientRegistration)?; diff --git a/crates/http/src/lib.rs b/crates/http/src/lib.rs index c76d33c4..10f30703 100644 --- a/crates/http/src/lib.rs +++ b/crates/http/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + pub mod api; pub mod auth; pub mod form; diff --git a/crates/imap/src/core/client.rs b/crates/imap/src/core/client.rs index 00284f85..62fabd1b 100644 --- a/crates/imap/src/core/client.rs +++ b/crates/imap/src/core/client.rs @@ -175,12 +175,10 @@ impl Session { .handle_namespace(request) .await .map(|_| SessionResult::Continue), - Command::Authenticate => self - .handle_authenticate(request) + Command::Authenticate => Box::pin(self.handle_authenticate(request)) .await .map(|_| SessionResult::Continue), - Command::Login => self - .handle_login(request) + Command::Login => Box::pin(self.handle_login(request)) .await .map(|_| SessionResult::Continue), Command::Capability => self diff --git a/crates/imap/src/lib.rs b/crates/imap/src/lib.rs index 6b17b786..4a56ead0 100644 --- a/crates/imap/src/lib.rs +++ b/crates/imap/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + use std::sync::LazyLock; use imap_proto::{ResponseCode, StatusResponse, protocol::capability::Capability}; diff --git a/crates/imap/src/op/authenticate.rs b/crates/imap/src/op/authenticate.rs index 3289c55f..57cd2c77 100644 --- a/crates/imap/src/op/authenticate.rs +++ b/crates/imap/src/op/authenticate.rs @@ -47,7 +47,7 @@ impl Session { .id(args.tag.clone()) })?; - self.authenticate(credentials, args.tag).await + Box::pin(self.authenticate(credentials, args.tag)).await } else { self.receiver.request = receiver::Request { tag: args.tag, diff --git a/crates/imap/src/op/login.rs b/crates/imap/src/op/login.rs index f3dec4ae..d5e52146 100644 --- a/crates/imap/src/op/login.rs +++ b/crates/imap/src/op/login.rs @@ -13,14 +13,14 @@ impl Session { pub async fn handle_login(&mut self, request: Request) -> trc::Result<()> { let arguments = request.parse_login()?; - self.authenticate( + Box::pin(self.authenticate( Credentials::Basic { username: arguments.username.to_string(), secret: arguments.password.to_string(), mfa_token: None, }, arguments.tag, - ) + )) .await } } diff --git a/crates/jmap-proto/src/references/mod.rs b/crates/jmap-proto/src/references/mod.rs index 39f8a98e..62ecb78b 100644 --- a/crates/jmap-proto/src/references/mod.rs +++ b/crates/jmap-proto/src/references/mod.rs @@ -310,7 +310,7 @@ mod tests { id: call.id, name: call.name, method: ResponseMethod::Changes(ChangesResponseMethod::Mailbox( - ChangesResponse { + Box::new(ChangesResponse { account_id: Id::new(1), old_state: Default::default(), new_state: Default::default(), @@ -322,7 +322,7 @@ mod tests { MailboxProperty::Name.into(), MailboxProperty::ParentId.into(), ]), - }, + }), )), }); } diff --git a/crates/jmap-proto/src/request/mod.rs b/crates/jmap-proto/src/request/mod.rs index 22c246cd..f894e3dd 100644 --- a/crates/jmap-proto/src/request/mod.rs +++ b/crates/jmap-proto/src/request/mod.rs @@ -61,110 +61,110 @@ pub struct Call { pub enum RequestMethod<'x> { Get(GetRequestMethod), Set(SetRequestMethod<'x>), - Changes(ChangesRequest), + Changes(Box), Copy(CopyRequestMethod<'x>), - ImportEmail(ImportEmailRequest), + ImportEmail(Box), Parse(ParseRequestMethod), Query(QueryRequestMethod), QueryChanges(QueryChangesRequestMethod), - SearchSnippet(GetSearchSnippetRequest), - ValidateScript(ValidateSieveScriptRequest), - LookupBlob(BlobLookupRequest), - UploadBlob(BlobUploadRequest), + SearchSnippet(Box), + ValidateScript(Box), + LookupBlob(Box), + UploadBlob(Box), Echo(Value<'x, Null, Null>), Error(trc::Error), } #[derive(Debug)] pub enum GetRequestMethod { - Email(GetRequest), - Mailbox(GetRequest), - Thread(GetRequest), - Identity(GetRequest), - EmailSubmission(GetRequest), - PushSubscription(GetRequest), - Sieve(GetRequest), - VacationResponse(GetRequest), - Principal(GetRequest), - PrincipalAvailability(GetAvailabilityRequest), - Quota(GetRequest), - Blob(GetRequest), - AddressBook(GetRequest), - ContactCard(GetRequest), - FileNode(GetRequest), - Calendar(GetRequest), - CalendarEvent(GetRequest), - CalendarEventNotification(GetRequest), - ParticipantIdentity(GetRequest), - ShareNotification(GetRequest), - Registry(GetRequest), + Email(Box>), + Mailbox(Box>), + Thread(Box>), + Identity(Box>), + EmailSubmission(Box>), + PushSubscription(Box>), + Sieve(Box>), + VacationResponse(Box>), + Principal(Box>), + PrincipalAvailability(Box), + Quota(Box>), + Blob(Box>), + AddressBook(Box>), + ContactCard(Box>), + FileNode(Box>), + Calendar(Box>), + CalendarEvent(Box>), + CalendarEventNotification(Box>), + ParticipantIdentity(Box>), + ShareNotification(Box>), + Registry(Box>), } #[derive(Debug)] pub enum SetRequestMethod<'x> { - Email(SetRequest<'x, Email>), - Mailbox(SetRequest<'x, Mailbox>), - Identity(SetRequest<'x, Identity>), - EmailSubmission(SetRequest<'x, EmailSubmission>), - PushSubscription(SetRequest<'x, PushSubscription>), - Sieve(SetRequest<'x, Sieve>), - VacationResponse(SetRequest<'x, VacationResponse>), - AddressBook(SetRequest<'x, AddressBook>), - ContactCard(SetRequest<'x, ContactCard>), - FileNode(SetRequest<'x, FileNode>), - ShareNotification(SetRequest<'x, ShareNotification>), - Calendar(SetRequest<'x, Calendar>), - CalendarEvent(SetRequest<'x, CalendarEvent>), - CalendarEventNotification(SetRequest<'x, CalendarEventNotification>), - ParticipantIdentity(SetRequest<'x, ParticipantIdentity>), - Registry(SetRequest<'x, Registry>), + Email(Box>), + Mailbox(Box>), + Identity(Box>), + EmailSubmission(Box>), + PushSubscription(Box>), + Sieve(Box>), + VacationResponse(Box>), + AddressBook(Box>), + ContactCard(Box>), + FileNode(Box>), + ShareNotification(Box>), + Calendar(Box>), + CalendarEvent(Box>), + CalendarEventNotification(Box>), + ParticipantIdentity(Box>), + Registry(Box>), } #[derive(Debug)] pub enum CopyRequestMethod<'x> { - Email(CopyRequest<'x, Email>), - ContactCard(CopyRequest<'x, ContactCard>), - CalendarEvent(CopyRequest<'x, CalendarEvent>), - Blob(CopyBlobRequest), + Email(Box>), + ContactCard(Box>), + CalendarEvent(Box>), + Blob(Box), } #[derive(Debug)] pub enum QueryRequestMethod { - Email(QueryRequest), - Mailbox(QueryRequest), - EmailSubmission(QueryRequest), - Sieve(QueryRequest), - Principal(QueryRequest), - Quota(QueryRequest), - AddressBook(QueryRequest), - ContactCard(QueryRequest), - FileNode(QueryRequest), - Calendar(QueryRequest), - CalendarEvent(QueryRequest), - CalendarEventNotification(QueryRequest), - ShareNotification(QueryRequest), - Registry(QueryRequest), + Email(Box>), + Mailbox(Box>), + EmailSubmission(Box>), + Sieve(Box>), + Principal(Box>), + Quota(Box>), + AddressBook(Box>), + ContactCard(Box>), + FileNode(Box>), + Calendar(Box>), + CalendarEvent(Box>), + CalendarEventNotification(Box>), + ShareNotification(Box>), + Registry(Box>), } #[derive(Debug)] pub enum QueryChangesRequestMethod { - Email(QueryChangesRequest), - Mailbox(QueryChangesRequest), - EmailSubmission(QueryChangesRequest), - Principal(QueryChangesRequest), - Quota(QueryChangesRequest), - ContactCard(QueryChangesRequest), - FileNode(QueryChangesRequest), - CalendarEvent(QueryChangesRequest), - CalendarEventNotification(QueryChangesRequest), - ShareNotification(QueryChangesRequest), + Email(Box>), + Mailbox(Box>), + EmailSubmission(Box>), + Principal(Box>), + Quota(Box>), + ContactCard(Box>), + FileNode(Box>), + CalendarEvent(Box>), + CalendarEventNotification(Box>), + ShareNotification(Box>), } #[derive(Debug)] pub enum ParseRequestMethod { - Email(ParseRequest), - ContactCard(ParseRequest), - CalendarEvent(ParseRequest), + Email(Box>), + ContactCard(Box>), + CalendarEvent(Box>), } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/jmap-proto/src/response/mod.rs b/crates/jmap-proto/src/response/mod.rs index c0c85ce2..a8b87636 100644 --- a/crates/jmap-proto/src/response/mod.rs +++ b/crates/jmap-proto/src/response/mod.rs @@ -103,40 +103,40 @@ pub enum GetResponseMethod { #[derive(Debug, serde::Serialize)] #[serde(untagged)] pub enum SetResponseMethod { - Email(SetResponse), - Mailbox(SetResponse), - Identity(SetResponse), - EmailSubmission(SetResponse), - PushSubscription(SetResponse), - Sieve(SetResponse), - VacationResponse(SetResponse), - AddressBook(SetResponse), - ContactCard(SetResponse), - FileNode(SetResponse), - ShareNotification(SetResponse), - Calendar(SetResponse), - CalendarEvent(SetResponse), - CalendarEventNotification(SetResponse), - ParticipantIdentity(SetResponse), - Registry(SetResponse), + Email(Box>), + Mailbox(Box>), + Identity(Box>), + EmailSubmission(Box>), + PushSubscription(Box>), + Sieve(Box>), + VacationResponse(Box>), + AddressBook(Box>), + ContactCard(Box>), + FileNode(Box>), + ShareNotification(Box>), + Calendar(Box>), + CalendarEvent(Box>), + CalendarEventNotification(Box>), + ParticipantIdentity(Box>), + Registry(Box>), } #[derive(Debug, serde::Serialize)] #[serde(untagged)] pub enum ChangesResponseMethod { - Email(ChangesResponse), - Mailbox(ChangesResponse), - Thread(ChangesResponse), - Identity(ChangesResponse), - EmailSubmission(ChangesResponse), - Quota(ChangesResponse), - AddressBook(ChangesResponse), - ContactCard(ChangesResponse), - FileNode(ChangesResponse), - Calendar(ChangesResponse), - CalendarEvent(ChangesResponse), - CalendarEventNotification(ChangesResponse), - ShareNotification(ChangesResponse), + Email(Box>), + Mailbox(Box>), + Thread(Box>), + Identity(Box>), + EmailSubmission(Box>), + Quota(Box>), + AddressBook(Box>), + ContactCard(Box>), + FileNode(Box>), + Calendar(Box>), + CalendarEvent(Box>), + CalendarEventNotification(Box>), + ShareNotification(Box>), } #[derive(Debug, serde::Serialize)] @@ -306,103 +306,103 @@ impl<'x> From> for ResponseMethod<'x> { impl<'x> From> for ResponseMethod<'x> { fn from(value: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::Email(value)) + ResponseMethod::Set(SetResponseMethod::Email(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::Mailbox(value)) + ResponseMethod::Set(SetResponseMethod::Mailbox(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::Identity(value)) + ResponseMethod::Set(SetResponseMethod::Identity(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::EmailSubmission(value)) + ResponseMethod::Set(SetResponseMethod::EmailSubmission(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::PushSubscription(value)) + ResponseMethod::Set(SetResponseMethod::PushSubscription(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::Sieve(value)) + ResponseMethod::Set(SetResponseMethod::Sieve(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::VacationResponse(value)) + ResponseMethod::Set(SetResponseMethod::VacationResponse(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::AddressBook(value)) + ResponseMethod::Set(SetResponseMethod::AddressBook(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::ContactCard(value)) + ResponseMethod::Set(SetResponseMethod::ContactCard(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::Registry(value)) + ResponseMethod::Set(SetResponseMethod::Registry(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: ChangesResponse) -> Self { - ResponseMethod::Changes(ChangesResponseMethod::Email(value)) + ResponseMethod::Changes(ChangesResponseMethod::Email(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: ChangesResponse) -> Self { - ResponseMethod::Changes(ChangesResponseMethod::Mailbox(value)) + ResponseMethod::Changes(ChangesResponseMethod::Mailbox(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: ChangesResponse) -> Self { - ResponseMethod::Changes(ChangesResponseMethod::Thread(value)) + ResponseMethod::Changes(ChangesResponseMethod::Thread(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: ChangesResponse) -> Self { - ResponseMethod::Changes(ChangesResponseMethod::Identity(value)) + ResponseMethod::Changes(ChangesResponseMethod::Identity(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: ChangesResponse) -> Self { - ResponseMethod::Changes(ChangesResponseMethod::EmailSubmission(value)) + ResponseMethod::Changes(ChangesResponseMethod::EmailSubmission(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: ChangesResponse) -> Self { - ResponseMethod::Changes(ChangesResponseMethod::Quota(value)) + ResponseMethod::Changes(ChangesResponseMethod::Quota(Box::new(value))) } } impl<'x> From> for ResponseMethod<'x> { fn from(value: ChangesResponse) -> Self { - ResponseMethod::Changes(ChangesResponseMethod::AddressBook(value)) + ResponseMethod::Changes(ChangesResponseMethod::AddressBook(Box::new(value))) } } @@ -498,13 +498,13 @@ impl From> for ResponseMethod<'_> { impl From> for ResponseMethod<'_> { fn from(response: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::FileNode(response)) + ResponseMethod::Set(SetResponseMethod::FileNode(Box::new(response))) } } impl From> for ResponseMethod<'_> { fn from(response: ChangesResponse) -> Self { - ResponseMethod::Changes(ChangesResponseMethod::FileNode(response)) + ResponseMethod::Changes(ChangesResponseMethod::FileNode(Box::new(response))) } } @@ -522,31 +522,31 @@ impl From> for ResponseMethod<'_> { impl From> for ResponseMethod<'_> { fn from(response: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::Calendar(response)) + ResponseMethod::Set(SetResponseMethod::Calendar(Box::new(response))) } } impl From> for ResponseMethod<'_> { fn from(response: ChangesResponse) -> Self { - ResponseMethod::Changes(ChangesResponseMethod::CalendarEvent(response)) + ResponseMethod::Changes(ChangesResponseMethod::CalendarEvent(Box::new(response))) } } impl From> for ResponseMethod<'_> { fn from(response: ChangesResponse) -> Self { - ResponseMethod::Changes(ChangesResponseMethod::CalendarEventNotification(response)) + ResponseMethod::Changes(ChangesResponseMethod::CalendarEventNotification(Box::new(response))) } } impl From> for ResponseMethod<'_> { fn from(response: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::CalendarEvent(response)) + ResponseMethod::Set(SetResponseMethod::CalendarEvent(Box::new(response))) } } impl From> for ResponseMethod<'_> { fn from(response: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::ParticipantIdentity(response)) + ResponseMethod::Set(SetResponseMethod::ParticipantIdentity(Box::new(response))) } } @@ -558,13 +558,13 @@ impl From> for ResponseMethod<'_> { impl From> for ResponseMethod<'_> { fn from(response: ChangesResponse) -> Self { - ResponseMethod::Changes(ChangesResponseMethod::ShareNotification(response)) + ResponseMethod::Changes(ChangesResponseMethod::ShareNotification(Box::new(response))) } } impl From> for ResponseMethod<'_> { fn from(response: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::ShareNotification(response)) + ResponseMethod::Set(SetResponseMethod::ShareNotification(Box::new(response))) } } @@ -600,6 +600,6 @@ impl From for ResponseMethod<'_> { impl From> for ResponseMethod<'_> { fn from(value: SetResponse) -> Self { - ResponseMethod::Set(SetResponseMethod::CalendarEventNotification(value)) + ResponseMethod::Set(SetResponseMethod::CalendarEventNotification(Box::new(value))) } } diff --git a/crates/jmap/src/api/request.rs b/crates/jmap/src/api/request.rs index a654c53e..5a7cb288 100644 --- a/crates/jmap/src/api/request.rs +++ b/crates/jmap/src/api/request.rs @@ -78,7 +78,6 @@ pub trait RequestHandler: Sync + Send { } impl RequestHandler for Server { - #![allow(clippy::large_futures)] async fn handle_jmap_request<'x>( &self, request: Request<'x>, @@ -236,102 +235,102 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Email)?; - self.email_get(req, access_token).await?.into() + self.email_get(*req, access_token).await?.into() } GetRequestMethod::Mailbox(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Mailbox)?; - self.mailbox_get(req, access_token).await?.into() + self.mailbox_get(*req, access_token).await?.into() } GetRequestMethod::Thread(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Email)?; - self.thread_get(req).await?.into() + self.thread_get(*req).await?.into() } GetRequestMethod::Identity(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.identity_get(req).await?.into() + self.identity_get(*req).await?.into() } GetRequestMethod::EmailSubmission(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.email_submission_get(req).await?.into() + self.email_submission_get(*req).await?.into() } GetRequestMethod::PushSubscription(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); - self.push_subscription_get(req, access_token).await?.into() + self.push_subscription_get(*req, access_token).await?.into() } GetRequestMethod::Sieve(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.sieve_script_get(req).await?.into() + self.sieve_script_get(*req).await?.into() } GetRequestMethod::VacationResponse(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.vacation_response_get(req).await?.into() + self.vacation_response_get(*req).await?.into() } GetRequestMethod::Principal(req) => { - self.principal_get(req, access_token).await?.into() + self.principal_get(*req, access_token).await?.into() } GetRequestMethod::Quota(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.quota_get(req, access_token).await?.into() + self.quota_get(*req, access_token).await?.into() } GetRequestMethod::Blob(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.blob_get(req, access_token).await?.into() + self.blob_get(*req, access_token).await?.into() } GetRequestMethod::AddressBook(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::AddressBook)?; - self.address_book_get(req, access_token).await?.into() + self.address_book_get(*req, access_token).await?.into() } GetRequestMethod::ContactCard(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::ContactCard)?; - self.contact_card_get(req, access_token).await?.into() + self.contact_card_get(*req, access_token).await?.into() } GetRequestMethod::FileNode(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::FileNode)?; - self.file_node_get(req, access_token).await?.into() + self.file_node_get(*req, access_token).await?.into() } GetRequestMethod::PrincipalAvailability(req) => self - .principal_get_availability(req, access_token) + .principal_get_availability(*req, access_token) .await? .into(), GetRequestMethod::Calendar(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Calendar)?; - self.calendar_get(req, access_token).await?.into() + self.calendar_get(*req, access_token).await?.into() } GetRequestMethod::CalendarEvent(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::CalendarEvent)?; - self.calendar_event_get(req, access_token).await?.into() + self.calendar_event_get(*req, access_token).await?.into() } GetRequestMethod::CalendarEventNotification(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.calendar_event_notification_get(req, access_token) + self.calendar_event_notification_get(*req, access_token) .await? .into() } @@ -339,13 +338,13 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.participant_identity_get(req).await?.into() + self.participant_identity_get(*req).await?.into() } GetRequestMethod::ShareNotification(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.share_notification_get(req).await?.into() + self.share_notification_get(*req).await?.into() } GetRequestMethod::Registry(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); @@ -353,7 +352,7 @@ impl RequestHandler for Server { Box::pin(self.registry_get( method_name.obj.unwrap_registry(), - req, + *req, access_token, )) .await? @@ -365,70 +364,70 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Email)?; - self.email_query(req, access_token).await?.into() + self.email_query(*req, access_token).await?.into() } QueryRequestMethod::Mailbox(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Mailbox)?; - self.mailbox_query(req, access_token).await?.into() + self.mailbox_query(*req, access_token).await?.into() } QueryRequestMethod::EmailSubmission(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.email_submission_query(req).await?.into() + self.email_submission_query(*req).await?.into() } QueryRequestMethod::Sieve(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.sieve_script_query(req).await?.into() + self.sieve_script_query(*req).await?.into() } QueryRequestMethod::Principal(req) => { - self.principal_query(req, access_token).await?.into() + self.principal_query(*req, access_token).await?.into() } QueryRequestMethod::Quota(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.quota_query(req, access_token).await?.into() + self.quota_query(*req, access_token).await?.into() } QueryRequestMethod::AddressBook(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::AddressBook)?; - self.address_book_query(req, access_token).await?.into() + self.address_book_query(*req, access_token).await?.into() } QueryRequestMethod::ContactCard(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::ContactCard)?; - self.contact_card_query(req, access_token).await?.into() + self.contact_card_query(*req, access_token).await?.into() } QueryRequestMethod::FileNode(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::FileNode)?; - self.file_node_query(req, access_token).await?.into() + self.file_node_query(*req, access_token).await?.into() } QueryRequestMethod::Calendar(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Calendar)?; - self.calendar_query(req, access_token).await?.into() + self.calendar_query(*req, access_token).await?.into() } QueryRequestMethod::CalendarEvent(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::CalendarEvent)?; - self.calendar_event_query(req, access_token).await?.into() + self.calendar_event_query(*req, access_token).await?.into() } QueryRequestMethod::CalendarEventNotification(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.calendar_event_notification_query(req, access_token) + self.calendar_event_notification_query(*req, access_token) .await? .into() } @@ -436,7 +435,7 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.share_notification_query(req).await?.into() + self.share_notification_query(*req).await?.into() } QueryRequestMethod::Registry(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); @@ -444,7 +443,7 @@ impl RequestHandler for Server { Box::pin(self.registry_query( method_name.obj.unwrap_registry(), - req, + *req, access_token, )) .await? @@ -456,37 +455,37 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Email)?; - self.email_set(req, access_token, session).await?.into() + self.email_set(*req, access_token, session).await?.into() } SetRequestMethod::Mailbox(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Mailbox)?; - self.mailbox_set(req, access_token).await?.into() + self.mailbox_set(*req, access_token).await?.into() } SetRequestMethod::Identity(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.identity_set(req).await?.into() + self.identity_set(*req).await?.into() } SetRequestMethod::EmailSubmission(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.email_submission_set(req, &session.instance, next_call) + self.email_submission_set(*req, &session.instance, next_call) .await? .into() } SetRequestMethod::PushSubscription(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); - self.push_subscription_set(req, access_token).await?.into() + self.push_subscription_set(*req, access_token).await?.into() } SetRequestMethod::Sieve(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.sieve_script_set(req, access_token, session) + self.sieve_script_set(*req, access_token, session) .await? .into() } @@ -494,13 +493,13 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.vacation_response_set(req, access_token).await?.into() + self.vacation_response_set(*req, access_token).await?.into() } SetRequestMethod::AddressBook(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::AddressBook)?; - self.address_book_set(req, access_token, session) + self.address_book_set(*req, access_token, session) .await? .into() } @@ -508,7 +507,7 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::ContactCard)?; - self.contact_card_set(req, access_token, session) + self.contact_card_set(*req, access_token, session) .await? .into() } @@ -516,25 +515,25 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::FileNode)?; - self.file_node_set(req, access_token, session).await?.into() + self.file_node_set(*req, access_token, session).await?.into() } SetRequestMethod::ShareNotification(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.share_notification_set(req).await?.into() + self.share_notification_set(*req).await?.into() } SetRequestMethod::Calendar(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Calendar)?; - self.calendar_set(req, access_token, session).await?.into() + self.calendar_set(*req, access_token, session).await?.into() } SetRequestMethod::CalendarEvent(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::CalendarEvent)?; - self.calendar_event_set(req, access_token, session) + self.calendar_event_set(*req, access_token, session) .await? .into() } @@ -542,7 +541,7 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.calendar_event_notification_set(req, access_token, session) + self.calendar_event_notification_set(*req, access_token, session) .await? .into() } @@ -550,7 +549,7 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.participant_identity_set(req).await?.into() + self.participant_identity_set(*req).await?.into() } SetRequestMethod::Registry(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); @@ -558,7 +557,7 @@ impl RequestHandler for Server { Box::pin(self.registry_set( method_name.obj.unwrap_registry(), - req, + *req, access_token, session, )) @@ -569,7 +568,7 @@ impl RequestHandler for Server { RequestMethod::Changes(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); - self.changes(req, method_name.obj, access_token) + self.changes(*req, method_name.obj, access_token) .await? .into_method_response() } @@ -582,7 +581,7 @@ impl RequestHandler for Server { .assert_has_access(req.account_id, Collection::Email)? .assert_has_access(req.from_account_id, Collection::Email)?; - self.email_copy(req, access_token, next_call, session) + self.email_copy(*req, access_token, next_call, session) .await? .into() } @@ -590,7 +589,7 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.blob_copy(req, access_token).await?.into() + self.blob_copy(*req, access_token).await?.into() } CopyRequestMethod::ContactCard(mut req) => { set_account_id_if_missing(&mut req.from_account_id, access_token); @@ -600,7 +599,7 @@ impl RequestHandler for Server { .assert_has_access(req.account_id, Collection::ContactCard)? .assert_has_access(req.from_account_id, Collection::ContactCard)?; - self.contact_card_copy(req, access_token, next_call, session) + self.contact_card_copy(*req, access_token, next_call, session) .await? .into() } @@ -612,7 +611,7 @@ impl RequestHandler for Server { .assert_has_access(req.account_id, Collection::CalendarEvent)? .assert_has_access(req.from_account_id, Collection::CalendarEvent)?; - self.calendar_event_copy(req, access_token, next_call, session) + self.calendar_event_copy(*req, access_token, next_call, session) .await? .into() } @@ -621,26 +620,26 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Email)?; - self.email_import(req, access_token, session).await?.into() + self.email_import(*req, access_token, session).await?.into() } RequestMethod::Parse(req) => match req { ParseRequestMethod::Email(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Email)?; - self.email_parse(req, access_token).await?.into() + self.email_parse(*req, access_token).await?.into() } ParseRequestMethod::ContactCard(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::ContactCard)?; - self.contact_card_parse(req, access_token).await?.into() + self.contact_card_parse(*req, access_token).await?.into() } ParseRequestMethod::CalendarEvent(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::CalendarEvent)?; - self.calendar_event_parse(req, access_token).await?.into() + self.calendar_event_parse(*req, access_token).await?.into() } }, RequestMethod::QueryChanges(req) => self.query_changes(req, access_token).await?.into(), @@ -648,25 +647,25 @@ impl RequestHandler for Server { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_has_access(req.account_id, Collection::Email)?; - self.email_search_snippet(req, access_token).await?.into() + self.email_search_snippet(*req, access_token).await?.into() } RequestMethod::ValidateScript(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.sieve_script_validate(req, access_token).await?.into() + self.sieve_script_validate(*req, access_token).await?.into() } RequestMethod::LookupBlob(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.blob_lookup(req).await?.into() + self.blob_lookup(*req).await?.into() } RequestMethod::UploadBlob(mut req) => { set_account_id_if_missing(&mut req.account_id, access_token); access_token.assert_is_member(req.account_id)?; - self.blob_upload_many(req, access_token).await?.into() + self.blob_upload_many(*req, access_token).await?.into() } RequestMethod::Echo(req) => req.into(), RequestMethod::Error(error) => return Err(error), diff --git a/crates/jmap/src/calendar_event/copy.rs b/crates/jmap/src/calendar_event/copy.rs index 31a4feca..9454c5d4 100644 --- a/crates/jmap/src/calendar_event/copy.rs +++ b/crates/jmap/src/calendar_event/copy.rs @@ -209,14 +209,16 @@ impl JmapCalendarEventCopy for Server { *next_call = Call { id: String::new(), name: MethodName::new(MethodObject::CalendarEvent, MethodFunction::Set), - method: RequestMethod::Set(SetRequestMethod::CalendarEvent(SetRequest { - account_id: request.from_account_id, - if_in_state: request.destroy_from_if_in_state, - create: None, - update: None, - destroy: MaybeResultReference::Value(destroy_ids).into(), - arguments: Default::default(), - })), + method: RequestMethod::Set(SetRequestMethod::CalendarEvent(Box::new( + SetRequest { + account_id: request.from_account_id, + if_in_state: request.destroy_from_if_in_state, + create: None, + update: None, + destroy: MaybeResultReference::Value(destroy_ids).into(), + arguments: Default::default(), + }, + ))), } .into(); } diff --git a/crates/jmap/src/changes/get.rs b/crates/jmap/src/changes/get.rs index 6ad4808c..65fe9482 100644 --- a/crates/jmap/src/changes/get.rs +++ b/crates/jmap/src/changes/get.rs @@ -335,8 +335,10 @@ impl IntermediateChangesResponse { } } -fn transmute_response(response: ChangesResponse) -> ChangesResponse { - ChangesResponse { +fn transmute_response( + response: ChangesResponse, +) -> Box> { + Box::new(ChangesResponse { account_id: response.account_id, old_state: response.old_state, new_state: response.new_state, @@ -345,5 +347,5 @@ fn transmute_response(response: ChangesResponse) -> C updated: response.updated, destroyed: response.destroyed, updated_properties: None, - } + }) } diff --git a/crates/jmap/src/changes/query.rs b/crates/jmap/src/changes/query.rs index 87f13d4f..78e97788 100644 --- a/crates/jmap/src/changes/query.rs +++ b/crates/jmap/src/changes/query.rs @@ -71,7 +71,7 @@ impl QueryChanges for Server { .as_ref() .is_some_and(|sort| sort.iter().any(|s| !s.is_immutable())); - results = self.email_query(request.into(), access_token).await?; + results = self.email_query((*request).into(), access_token).await?; } QueryChangesRequestMethod::Mailbox(mut request) => { // Query changes @@ -93,7 +93,7 @@ impl QueryChanges for Server { } up_to_id = request.up_to_id; - results = self.mailbox_query(request.into(), access_token).await?; + results = self.mailbox_query((*request).into(), access_token).await?; } QueryChangesRequestMethod::EmailSubmission(mut request) => { // Query changes @@ -115,7 +115,7 @@ impl QueryChanges for Server { } up_to_id = request.up_to_id; - results = self.email_submission_query(request.into()).await?; + results = self.email_submission_query((*request).into()).await?; } QueryChangesRequestMethod::ContactCard(mut request) => { // Query changes @@ -138,7 +138,7 @@ impl QueryChanges for Server { up_to_id = request.up_to_id; results = self - .contact_card_query(request.into(), access_token) + .contact_card_query((*request).into(), access_token) .await?; } QueryChangesRequestMethod::FileNode(mut request) => { @@ -161,7 +161,7 @@ impl QueryChanges for Server { } up_to_id = request.up_to_id; - results = self.file_node_query(request.into(), access_token).await?; + results = self.file_node_query((*request).into(), access_token).await?; } QueryChangesRequestMethod::CalendarEvent(mut request) => { // Query changes @@ -184,7 +184,7 @@ impl QueryChanges for Server { up_to_id = request.up_to_id; results = self - .calendar_event_query(request.into(), access_token) + .calendar_event_query((*request).into(), access_token) .await?; } QueryChangesRequestMethod::CalendarEventNotification(mut request) => { @@ -208,7 +208,7 @@ impl QueryChanges for Server { up_to_id = request.up_to_id; results = self - .calendar_event_notification_query(request.into(), access_token) + .calendar_event_notification_query((*request).into(), access_token) .await?; } QueryChangesRequestMethod::ShareNotification(mut request) => { @@ -231,7 +231,7 @@ impl QueryChanges for Server { } up_to_id = request.up_to_id; - results = self.share_notification_query(request.into()).await?; + results = self.share_notification_query((*request).into()).await?; } QueryChangesRequestMethod::Principal(_) => { return Err(trc::JmapEvent::CannotCalculateChanges.into_err()); diff --git a/crates/jmap/src/contact/copy.rs b/crates/jmap/src/contact/copy.rs index ed921f00..a6e68b41 100644 --- a/crates/jmap/src/contact/copy.rs +++ b/crates/jmap/src/contact/copy.rs @@ -186,14 +186,14 @@ impl JmapContactCardCopy for Server { *next_call = Call { id: String::new(), name: MethodName::new(MethodObject::ContactCard, MethodFunction::Set), - method: RequestMethod::Set(SetRequestMethod::ContactCard(SetRequest { + method: RequestMethod::Set(SetRequestMethod::ContactCard(Box::new(SetRequest { account_id: request.from_account_id, if_in_state: request.destroy_from_if_in_state, create: None, update: None, destroy: MaybeResultReference::Value(destroy_ids).into(), arguments: Default::default(), - })), + }))), } .into(); } diff --git a/crates/jmap/src/email/copy.rs b/crates/jmap/src/email/copy.rs index 6b923c12..824aa420 100644 --- a/crates/jmap/src/email/copy.rs +++ b/crates/jmap/src/email/copy.rs @@ -241,14 +241,14 @@ impl JmapEmailCopy for Server { *next_call = Call { id: String::new(), name: MethodName::new(MethodObject::Email, MethodFunction::Set), - method: RequestMethod::Set(SetRequestMethod::Email(SetRequest { + method: RequestMethod::Set(SetRequestMethod::Email(Box::new(SetRequest { account_id: request.from_account_id, if_in_state: request.destroy_from_if_in_state, create: None, update: None, destroy: MaybeResultReference::Value(destroy_ids).into(), arguments: Default::default(), - })), + }))), } .into(); } diff --git a/crates/jmap/src/lib.rs b/crates/jmap/src/lib.rs index 62fde961..94414cb6 100644 --- a/crates/jmap/src/lib.rs +++ b/crates/jmap/src/lib.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -#![warn(clippy::large_futures)] +#![deny(clippy::large_futures)] pub mod addressbook; pub mod api; diff --git a/crates/jmap/src/submission/set.rs b/crates/jmap/src/submission/set.rs index 8af68da3..8e448303 100644 --- a/crates/jmap/src/submission/set.rs +++ b/crates/jmap/src/submission/set.rs @@ -272,7 +272,7 @@ impl EmailSubmissionSet for Server { *next_call = Call { id: String::new(), name: MethodName::new(MethodObject::Email, MethodFunction::Set), - method: RequestMethod::Set(SetRequestMethod::Email(SetRequest { + method: RequestMethod::Set(SetRequestMethod::Email(Box::new(SetRequest { account_id: request.account_id, if_in_state: None, create: None, @@ -309,7 +309,7 @@ impl EmailSubmissionSet for Server { ) }), arguments: Default::default(), - })), + }))), } .into(); } diff --git a/crates/jmap/src/websocket/stream.rs b/crates/jmap/src/websocket/stream.rs index 96253aba..b31a5277 100644 --- a/crates/jmap/src/websocket/stream.rs +++ b/crates/jmap/src/websocket/stream.rs @@ -34,7 +34,6 @@ pub trait WebSocketHandler: Sync + Send { } impl WebSocketHandler for Server { - #![allow(clippy::large_futures)] async fn handle_websocket_stream( &self, mut stream: WebSocketStream>, diff --git a/crates/main/src/main.rs b/crates/main/src/main.rs index cbf2fbee..3c6bb3cd 100644 --- a/crates/main/src/main.rs +++ b/crates/main/src/main.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -#![warn(clippy::large_futures)] +#![deny(clippy::large_futures)] #![warn(clippy::cast_possible_truncation)] #![warn(clippy::cast_possible_wrap)] #![warn(clippy::cast_sign_loss)] diff --git a/crates/managesieve/src/core/client.rs b/crates/managesieve/src/core/client.rs index eb929726..cd130857 100644 --- a/crates/managesieve/src/core/client.rs +++ b/crates/managesieve/src/core/client.rs @@ -97,7 +97,7 @@ impl Session { Command::CheckScript => self.handle_checkscript(request).await, Command::HaveSpace => self.handle_havespace(request).await, Command::Capability => self.handle_capability("").await, - Command::Authenticate => self.handle_authenticate(request).await, + Command::Authenticate => Box::pin(self.handle_authenticate(request)).await, Command::StartTls => self.handle_start_tls().await, Command::Logout => self.handle_logout().await, Command::Noop => self.handle_noop(request).await, diff --git a/crates/managesieve/src/lib.rs b/crates/managesieve/src/lib.rs index 4f4bc8ba..fb665dff 100644 --- a/crates/managesieve/src/lib.rs +++ b/crates/managesieve/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + pub mod core; pub mod op; diff --git a/crates/migration/src/lib.rs b/crates/migration/src/lib.rs index 1d9ee380..145465f7 100644 --- a/crates/migration/src/lib.rs +++ b/crates/migration/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + use crate::v016::migrate_v0_16; use common::{DATABASE_SCHEMA_VERSION, Server}; use store::{ diff --git a/crates/pop3/src/client.rs b/crates/pop3/src/client.rs index 2a6c8eaa..a58847dd 100644 --- a/crates/pop3/src/client.rs +++ b/crates/pop3/src/client.rs @@ -100,11 +100,11 @@ impl Session { } else { unreachable!() }; - self.handle_auth(Credentials::Basic { + Box::pin(self.handle_auth(Credentials::Basic { username, secret: string, mfa_token: None, - }) + })) .await .map(|_| SessionResult::Continue) } @@ -147,10 +147,11 @@ impl Session { self.handle_stls().await.map(|_| SessionResult::UpgradeTls) } Command::Utf8 => self.handle_utf8().await.map(|_| SessionResult::Continue), - Command::Auth { mechanism, params } => self - .handle_sasl(mechanism, params) - .await - .map(|_| SessionResult::Continue), + Command::Auth { mechanism, params } => Box::pin( + self.handle_sasl(mechanism, params), + ) + .await + .map(|_| SessionResult::Continue), Command::Apop { .. } => Err(trc::Pop3Event::Error .into_err() .details("APOP not supported.")), diff --git a/crates/pop3/src/lib.rs b/crates/pop3/src/lib.rs index dda0ed92..3ca6c6d0 100644 --- a/crates/pop3/src/lib.rs +++ b/crates/pop3/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + use std::{net::IpAddr, sync::Arc}; use common::{ diff --git a/crates/pop3/src/op/authenticate.rs b/crates/pop3/src/op/authenticate.rs index 4b0b6a8b..cf423e34 100644 --- a/crates/pop3/src/op/authenticate.rs +++ b/crates/pop3/src/op/authenticate.rs @@ -39,7 +39,7 @@ impl Session { .details("Invalid SASL challenge") })?; - self.handle_auth(credentials).await + Box::pin(self.handle_auth(credentials)).await } else { // TODO: This hack is temporary until the SASL library is developed self.receiver.state = request::State::Argument { diff --git a/crates/registry/src/lib.rs b/crates/registry/src/lib.rs index 843b95b4..1710fae9 100644 --- a/crates/registry/src/lib.rs +++ b/crates/registry/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + pub mod jmap; pub mod pickle; pub mod schema; diff --git a/crates/services/src/lib.rs b/crates/services/src/lib.rs index 00323709..9032d87b 100644 --- a/crates/services/src/lib.rs +++ b/crates/services/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + use broadcast::publisher::spawn_broadcast_publisher; use common::{ BuildServer, Inner, diff --git a/crates/services/src/task_manager/manager.rs b/crates/services/src/task_manager/manager.rs index 8686ab8f..b8f89317 100644 --- a/crates/services/src/task_manager/manager.rs +++ b/crates/services/src/task_manager/manager.rs @@ -241,7 +241,7 @@ pub fn spawn_task_manager(inner: Arc) { server.store_maintenance(task).await } Task::SpamFilterMaintenance(task) => { - server.spam_filter_maintenance(task).await + Box::pin(server.spam_filter_maintenance(task)).await } Task::AcmeRenewal(task) => server.acme_management(task).await, Task::DkimManagement(task_dkim_rotation) => { diff --git a/crates/services/src/task_manager/scheduler.rs b/crates/services/src/task_manager/scheduler.rs index 632adb5e..fa1d2750 100644 --- a/crates/services/src/task_manager/scheduler.rs +++ b/crates/services/src/task_manager/scheduler.rs @@ -464,9 +464,10 @@ pub fn spawn_task_scheduler(inner: Arc) { Type = "validateLicense" ); - match server - .reload_registry(RegistryChange::Reload(ObjectType::Enterprise)) - .await + match Box::pin( + server.reload_registry(RegistryChange::Reload(ObjectType::Enterprise)), + ) + .await { Ok(result) => { if !result.has_errors() { diff --git a/crates/services/src/task_manager/spam_classifier.rs b/crates/services/src/task_manager/spam_classifier.rs index b0586e76..16b13238 100644 --- a/crates/services/src/task_manager/spam_classifier.rs +++ b/crates/services/src/task_manager/spam_classifier.rs @@ -56,12 +56,12 @@ async fn spam_filter_maintenance( match task.maintenance_type { TaskSpamFilterMaintenanceType::Train => { if !server.inner.ipc.train_task_controller.is_running() { - server.spam_train(false).await?; + Box::pin(server.spam_train(false)).await?; } } TaskSpamFilterMaintenanceType::Retrain => { if !server.inner.ipc.train_task_controller.is_running() { - server.spam_train(true).await?; + Box::pin(server.spam_train(true)).await?; } } TaskSpamFilterMaintenanceType::Reset => { @@ -247,9 +247,8 @@ async fn update_spam_rules(server: &Server) -> trc::Result { } if reload_settings { - if let Err(err) = server - .reload_registry(RegistryChange::Reload(ObjectType::SpamRule)) - .await + if let Err(err) = + Box::pin(server.reload_registry(RegistryChange::Reload(ObjectType::SpamRule))).await { trc::error!(err.details("Failed to reload registry after updating spam rules")); } @@ -261,9 +260,10 @@ async fn update_spam_rules(server: &Server) -> trc::Result { } if reload_lookups { - if let Err(err) = server - .reload_registry(RegistryChange::Reload(ObjectType::MemoryLookupKey)) - .await + if let Err(err) = Box::pin( + server.reload_registry(RegistryChange::Reload(ObjectType::MemoryLookupKey)), + ) + .await { trc::error!(err.details("Failed to reload registry after updating spam rules")); } diff --git a/crates/smtp/src/lib.rs b/crates/smtp/src/lib.rs index bf1cb5b0..1fde369d 100644 --- a/crates/smtp/src/lib.rs +++ b/crates/smtp/src/lib.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -#![warn(clippy::large_futures)] +#![deny(clippy::large_futures)] use common::{ Inner, diff --git a/crates/smtp/src/outbound/client.rs b/crates/smtp/src/outbound/client.rs index 4f3825ad..be81474d 100644 --- a/crates/smtp/src/outbound/client.rs +++ b/crates/smtp/src/outbound/client.rs @@ -115,12 +115,12 @@ impl SmtpClient { return Ok(()); } _ => { - return Err(ClientError::UnexpectedReply(reply)); + return Err(ClientError::UnexpectedReply(Box::new(reply))); } } } - Err(ClientError::UnexpectedReply(reply)) + Err(ClientError::UnexpectedReply(Box::new(reply))) } pub async fn read_greeting( @@ -327,7 +327,7 @@ impl SmtpClient { smtp_proto::Error::InvalidResponse { code } => { match ResponseReceiver::from_code(code).parse(&mut iter) { Ok(response) => { - return Err(ClientError::UnexpectedReply(response)); + return Err(ClientError::UnexpectedReply(Box::new(response))); } Err(smtp_proto::Error::NeedsMoreData { .. }) => { if buf_concat.is_empty() { diff --git a/crates/smtp/src/outbound/delivery.rs b/crates/smtp/src/outbound/delivery.rs index a79dcbee..3807db82 100644 --- a/crates/smtp/src/outbound/delivery.rs +++ b/crates/smtp/src/outbound/delivery.rs @@ -44,7 +44,6 @@ use trc::{DaneEvent, DeliveryEvent, MtaStsEvent, ServerEvent, TlsRptEvent}; impl QueuedMessage { pub fn try_deliver(self, server: Server) { - #![allow(clippy::large_futures)] tokio::spawn(async move { // Lock queue event let queue_id = self.queue_id; diff --git a/crates/smtp/src/outbound/error.rs b/crates/smtp/src/outbound/error.rs index ff88941e..ef62ea3d 100644 --- a/crates/smtp/src/outbound/error.rs +++ b/crates/smtp/src/outbound/error.rs @@ -26,10 +26,10 @@ pub enum ClientError { UnparseableReply, /// Unexpected SMTP reply. - UnexpectedReply(smtp_proto::Response), + UnexpectedReply(Box>), /// SMTP authentication failure. - AuthenticationFailed(smtp_proto::Response), + AuthenticationFailed(Box>), /// Invalid TLS name provided. InvalidTLSName, @@ -73,7 +73,7 @@ impl AssertReply for Response { if self.severity() == severity { Ok(()) } else { - Err(ClientError::UnexpectedReply(self)) + Err(ClientError::UnexpectedReply(Box::new(self))) } } @@ -83,7 +83,7 @@ impl AssertReply for Response { if (200..=299).contains(&self.code) { Ok(()) } else { - Err(ClientError::UnexpectedReply(self)) + Err(ClientError::UnexpectedReply(Box::new(self))) } } @@ -93,7 +93,7 @@ impl AssertReply for Response { if self.code() == code { Ok(()) } else { - Err(ClientError::UnexpectedReply(self)) + Err(ClientError::UnexpectedReply(Box::new(self))) } } } diff --git a/crates/smtp/src/outbound/session.rs b/crates/smtp/src/outbound/session.rs index 8836c777..cbacfa2c 100644 --- a/crates/smtp/src/outbound/session.rs +++ b/crates/smtp/src/outbound/session.rs @@ -126,7 +126,7 @@ impl MessageWrapper { if r.is_positive_completion() { Ok(r) } else { - Err(ClientError::UnexpectedReply(r)) + Err(ClientError::UnexpectedReply(Box::new(r))) } }) { Ok(response) => { @@ -301,7 +301,7 @@ impl MessageWrapper { Status::from_smtp_error( params.hostname, bdat_cmd.as_deref().unwrap_or("DATA"), - ClientError::UnexpectedReply(response), + ClientError::UnexpectedReply(Box::new(response)), ), rcpt_idxs, )); diff --git a/crates/smtp/src/reporting/analysis.rs b/crates/smtp/src/reporting/analysis.rs index 4b41b456..8c29f2c7 100644 --- a/crates/smtp/src/reporting/analysis.rs +++ b/crates/smtp/src/reporting/analysis.rs @@ -163,8 +163,34 @@ impl AnalyzeReport for Server { Cow::Owned(buf) } Compression::Zip => { - let mut archive = match zip::ZipArchive::new(Cursor::new(report.data)) { - Ok(archive) => archive, + let data = report.data.to_vec(); + let result = tokio::task::spawn_blocking( + move || -> Result, std::io::Error> { + let mut archive = zip::ZipArchive::new(Cursor::new(data)) + .map_err(std::io::Error::other)?; + let mut buf = Vec::new(); + if !archive.is_empty() { + let mut file = + archive.by_index(0).map_err(std::io::Error::other)?; + buf.reserve(file.compressed_size() as usize); + file.read_to_end(&mut buf)?; + } + Ok(buf) + }, + ) + .await; + match result { + Ok(Ok(buf)) => Cow::Owned(buf), + Ok(Err(err)) => { + trc::event!( + IncomingReport(IncomingReportEvent::DecompressError), + SpanId = session_id, + From = from.to_string(), + Reason = err.to_string(), + CausedBy = trc::location!() + ); + continue; + } Err(err) => { trc::event!( IncomingReport(IncomingReportEvent::DecompressError), @@ -173,38 +199,9 @@ impl AnalyzeReport for Server { Reason = err.to_string(), CausedBy = trc::location!() ); - continue; } - }; - let mut buf = Vec::with_capacity(0); - for i in 0..archive.len() { - match archive.by_index(i) { - Ok(mut file) => { - buf = Vec::with_capacity(file.compressed_size() as usize); - if let Err(err) = file.read_to_end(&mut buf) { - trc::event!( - IncomingReport(IncomingReportEvent::DecompressError), - SpanId = session_id, - From = from.to_string(), - Reason = err.to_string(), - CausedBy = trc::location!() - ); - } - break; - } - Err(err) => { - trc::event!( - IncomingReport(IncomingReportEvent::DecompressError), - SpanId = session_id, - From = from.to_string(), - Reason = err.to_string(), - CausedBy = trc::location!() - ); - } - } } - Cow::Owned(buf) } }; diff --git a/crates/spam-filter/src/lib.rs b/crates/spam-filter/src/lib.rs index 2710d35d..b4ca6df2 100644 --- a/crates/spam-filter/src/lib.rs +++ b/crates/spam-filter/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + pub mod analysis; pub mod modules; diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs index e98ac962..18bbe6a5 100644 --- a/crates/store/src/lib.rs +++ b/crates/store/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + pub mod backend; pub mod build; pub mod dispatch; diff --git a/crates/trc/src/lib.rs b/crates/trc/src/lib.rs index e4fb53e2..70923db5 100644 --- a/crates/trc/src/lib.rs +++ b/crates/trc/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + pub mod atomics; pub mod event; pub mod ipc; diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index bf1b17db..63476e94 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +#![deny(clippy::large_futures)] + pub mod bimap; pub mod cache; pub mod chained_bytes; diff --git a/resources/docker/Dockerfile.fdb b/resources/docker/Dockerfile.fdb index b19185f1..7fd3a811 100644 --- a/resources/docker/Dockerfile.fdb +++ b/resources/docker/Dockerfile.fdb @@ -37,7 +37,7 @@ COPY Cargo.lock . COPY crates/ crates/ COPY resources/ resources/ COPY tests/ tests/ -RUN cargo build -p stalwart --no-default-features --features "foundationdb elastic s3 redis azure nats enterprise" --release +RUN cargo build -p stalwart --no-default-features --features "foundationdb s3 redis azure nats enterprise" --release FROM debian:trixie-slim AS runtime diff --git a/resources/docker/entrypoint.sh b/resources/docker/entrypoint.sh index 692435aa..f98f7644 100644 --- a/resources/docker/entrypoint.sh +++ b/resources/docker/entrypoint.sh @@ -1,10 +1,5 @@ #!/usr/bin/env sh # shellcheck shell=dash -# If the configuration file does not exist initialize it. -if [ ! -f /opt/stalwart/etc/config.toml ]; then - /usr/local/bin/stalwart --init /opt/stalwart -fi - # If the configuration file exists, start the server. -exec /usr/local/bin/stalwart --config /opt/stalwart/etc/config.toml +exec /usr/local/bin/stalwart --config /opt/stalwart/etc/config.json diff --git a/resources/schema/schema.json.gz b/resources/schema/schema.json.gz index 1e6b0413..bdccce69 100644 Binary files a/resources/schema/schema.json.gz and b/resources/schema/schema.json.gz differ diff --git a/resources/schema/schema.json.sha256 b/resources/schema/schema.json.sha256 index a70b013e..faada3c8 100644 --- a/resources/schema/schema.json.sha256 +++ b/resources/schema/schema.json.sha256 @@ -1 +1 @@ -MLWAMu9TglkOY68vD-l5Yy7e37iSMMm6agiqGga9nxI \ No newline at end of file +PL8KZnGtpFbFHjUZNFp4Ety5oqiCIv7amrwZ2gnIHbA \ No newline at end of file diff --git a/tests/resources/acme/Docker.pebble b/tests/resources/acme/Docker.pebble deleted file mode 100644 index 25002ba1..00000000 --- a/tests/resources/acme/Docker.pebble +++ /dev/null @@ -1,28 +0,0 @@ -FROM golang:1.18-alpine as builder - -ENV CGO_ENABLED=0 - -WORKDIR /pebble-src - -RUN apk update && apk add --no-cache git -RUN git clone https://github.com/letsencrypt/pebble/ /pebble-src -RUN go build -o /go/bin/pebble ./cmd/pebble - -## main -FROM alpine:3.15.4 - -COPY --from=builder /go/bin/pebble /usr/bin/pebble -COPY --from=builder /pebble-src/test/ /test/ - -CMD [ "/usr/bin/pebble" ] - -EXPOSE 14000 -EXPOSE 15000 - -# Build: -# docker build -f Docker.pebble -t pebble - -# Run: -# docker run -d -p 14000:14000 -p 15000:15000 pebble -# docker run -d --name pebble -p 14000:14000 -p 15000:15000 pebble pebble -config /test/config/pebble-config.json -strict - diff --git a/tests/resources/acme/config.toml b/tests/resources/acme/config.toml deleted file mode 100644 index bdc13c50..00000000 --- a/tests/resources/acme/config.toml +++ /dev/null @@ -1,58 +0,0 @@ -acme.pebble.contact = "postmaster@example.org" -acme.pebble.directory = "https://localhost:14000/dir" -#acme.pebble.domains = "mail.example.org" -acme.pebble.renew-before = "30d" - -acme.pebble.challenge = "tls-alpn-01" -#acme.pebble.challenge = "http-01" -#acme.pebble.challenge = "dns-01" - -acme.pebble.domains = "*.example.org" -acme.pebble.provider = "cloudflare" -acme.pebble.secret = "" - -authentication.fallback-admin.secret = "secret" -authentication.fallback-admin.user = "admin" -config.local-keys.0 = "*" -directory.internal.store = "rocksdb" -directory.internal.type = "internal" -lookup.default.hostname = "mail.example.org" -lookup.default.domain = "example.org" -oauth.key = "0Wn7rO4UdmBoE8mp3cDcD9Qlpz3na74z7fGRoSuq8fVsGPelLl3KrHomBN8h2biA" -queue.quota.size.enable = true -queue.quota.size.messages = 100000 -queue.quota.size.size = 10737418240 -report.analysis.addresses = "postmaster@*" -server.http.permissive-cors = true -server.listener.http.bind = "[::]:5002" -server.listener.http.protocol = "http" -server.listener.https.bind = "[::]:5001" -server.listener.https.protocol = "http" -server.listener.https.tls.implicit = true -server.listener.imap.bind = "[::]:143" -server.listener.imap.protocol = "imap" -server.listener.imaptls.bind = "[::]:993" -server.listener.imaptls.protocol = "imap" -server.listener.imaptls.tls.implicit = true -server.listener.sieve.bind = "[::]:4190" -server.listener.sieve.protocol = "managesieve" -server.listener.smtp.bind = "[::]:25" -server.listener.smtp.protocol = "smtp" -server.listener.submission.bind = "[::]:587" -server.listener.submission.protocol = "smtp" -server.listener.submissions.bind = "[::]:465" -server.listener.submissions.protocol = "smtp" -server.listener.submissions.tls.implicit = true -storage.blob = "rocksdb" -storage.data = "rocksdb" -storage.directory = "internal" -storage.fts = "rocksdb" -storage.lookup = "rocksdb" -store.rocksdb.compression = "lz4" -store.rocksdb.path = "/tmp/stalwart-temp-data" -store.rocksdb.type = "rocksdb" -tracer.stdout.ansi = true -tracer.stdout.enable = true -tracer.stdout.level = "trace" -tracer.stdout.type = "stdout" -version.spam-filter = 1.0 diff --git a/tests/resources/acme/docker-compose-pebble.yaml b/tests/resources/acme/docker-compose-pebble.yaml deleted file mode 100644 index 5806fe59..00000000 --- a/tests/resources/acme/docker-compose-pebble.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# docker-compose -f docker-compose-pebble.yaml up -# curl --request POST --data '{"ip":"192.168.5.2"}' http://localhost:8055/set-default-ipv4 -# HTTPS port should be 5001 -# HTTP port should be 5002 -# Directory https://localhost:14000/dir - -version: '3' -services: - pebble: - image: letsencrypt/pebble:latest - command: pebble -config /test/config/pebble-config.json -strict -dnsserver 10.30.50.3:8053 #-dnsserver 8.8.8.8:53 - ports: - - 14000:14000 # HTTPS ACME API - - 15000:15000 # HTTPS Management API - networks: - acmenet: - ipv4_address: 10.30.50.2 - challtestsrv: - image: letsencrypt/pebble-challtestsrv:latest - command: pebble-challtestsrv -defaultIPv6 "" -defaultIPv4 10.30.50.3 - ports: - - 8055:8055 # HTTP Management API - networks: - acmenet: - ipv4_address: 10.30.50.3 - -networks: - acmenet: - driver: bridge - ipam: - driver: default - config: - - subnet: 10.30.50.0/24 diff --git a/tests/resources/acme/test_acme.sh b/tests/resources/acme/test_acme.sh deleted file mode 100644 index 66e908f2..00000000 --- a/tests/resources/acme/test_acme.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -rm -Rf /tmp/stalwart-temp-data -mkdir -p /tmp/stalwart-temp-data -cp ./tests/resources/acme/config.toml /tmp/stalwart-temp-data/config.toml - -curl --request POST --data '{"ip":"192.168.5.2"}' http://localhost:8055/set-default-ipv4 - -cargo run -p stalwart --no-default-features --features "sqlite foundationdb postgres mysql rocks elastic s3 redis" -- --config=/tmp/stalwart-temp-data/config.toml diff --git a/tests/resources/scripts/create_test_cluster.sh b/tests/resources/scripts/create_test_cluster.sh deleted file mode 100644 index c38866f9..00000000 --- a/tests/resources/scripts/create_test_cluster.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -BASE_DIR="/Users/me/Downloads/stalwart-cluster" -FEATURES="rocks" -NUM_NODES=5 - -# Kill previous processes -sudo pkill stalwart - -# Delete previous tests -rm -rf $BASE_DIR - -# Build the stalwart binary -cargo build -p stalwart --no-default-features --features "$FEATURES" - -for NUM in $(seq 1 $NUM_NODES); do - sudo ifconfig en0 alias 10.0.$NUM.1 netmask 255.255.255.0 - mkdir -p $BASE_DIR/data$NUM - cat < $BASE_DIR/config$NUM.toml -cluster.bind-addr = "10.0._N_.1" -cluster.key = "the cluster key" -cluster.seed-nodes = ["10.0.1.1", "10.0.2.1", "10.0.3.1"] -authentication.fallback-admin.secret = "secret" -authentication.fallback-admin.user = "admin" -directory.internal.store = "rocksdb" -directory.internal.type = "internal" -lookup.default.hostname = "mail_N_.example.org" -server.http.permissive-cors = true -server.listener.https.bind = "10.0._N_.1:1443" -server.listener.https.protocol = "http" -server.listener.https.tls.implicit = true -server.listener.imap.bind = "10.0._N_.1:1143" -server.listener.imap.protocol = "imap" -server.listener.smtp.bind = "10.0._N_.1:1125" -server.listener.smtp.protocol = "smtp" -storage.blob = "rocksdb" -storage.data = "rocksdb" -storage.directory = "internal" -storage.fts = "rocksdb" -storage.lookup = "rocksdb" -store.rocksdb.compression = "lz4" -store.rocksdb.path = "_D_/data_N_" -store.rocksdb.type = "rocksdb" -tracer.stdout.ansi = true -tracer.stdout.enable = true -tracer.stdout.level = "debug" -tracer.stdout.type = "stdout" -config.resource.spam-filter = "file:///dev/null" -config.resource.webadmin = "file:///dev/null" -EOF - - sudo ./target/debug/stalwart --config $BASE_DIR/config$NUM.toml & -done diff --git a/tests/resources/scripts/create_test_env.sh b/tests/resources/scripts/create_test_env.sh deleted file mode 100644 index b39415c2..00000000 --- a/tests/resources/scripts/create_test_env.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -BASE_DIR="/Users/me/Downloads/stalwart-test" -FEATURES="sqlite foundationdb postgres mysql rocks elastic s3 redis" - -# Delete previous tests -rm -rf $BASE_DIR - -# Create admin user -cargo run -p stalwart --no-default-features --features "$FEATURES" -- --init=$BASE_DIR - -printf "[server.http]\npermissive-cors = true\n" >> $BASE_DIR/etc/config.toml -printf "[tracer.stdout]\ntype = 'stdout'\nlevel = 'trace'\nansi = true\nenable = true\n" >> $BASE_DIR/etc/config.toml -sed -i '' 's/secret =/secret = "secret"\n#secret =/g' $BASE_DIR/etc/config.toml -#cargo run -p stalwart --no-default-features --features "$FEATURES" -- --config=$BASE_DIR/etc/config.toml diff --git a/tests/src/cluster/broadcast.rs b/tests/src/cluster/broadcast.rs index 945753ca..43641e16 100644 --- a/tests/src/cluster/broadcast.rs +++ b/tests/src/cluster/broadcast.rs @@ -30,189 +30,178 @@ use types::id::Id; pub const NUM_NODES: usize = 3; -#[test] -fn cluster_tests() { - tokio::runtime::Builder::new_multi_thread() - .thread_stack_size(8 * 1024 * 1024) // 8MB stack - .enable_all() +#[tokio::test(flavor = "multi_thread")] +pub async fn cluster_tests() { + println!("Running cluster broadcast tests..."); + let mut servers = Vec::with_capacity(NUM_NODES); + + let coordinator_id = std::env::var("COORDINATOR").expect(concat!( + "Missing coordinator type. Try running `STORE= ", + "COORDINATOR= cargo test`" + )); + let coordinator = match coordinator_id.as_str() { + "Nats" => Coordinator::Nats(NatsCoordinator { + addresses: Map::new(vec!["127.0.0.1:4222".to_string()]), + use_tls: false, + ..Default::default() + }), + "Redis" => Coordinator::Redis(RedisStore { + url: "redis://127.0.0.1".to_string(), + ..Default::default() + }), + _ => panic!("Unsupported coordinator type: {}", coordinator_id), + }; + + // Create initial server + let test = TestServerBuilder::new("cluster_test_0") + .await + .with_object(coordinator) + .await + .with_listener(NetworkListenerProtocol::Http, "http_0", 11000, true) + .await + .with_imap_listener(12000) + .await + .with_listener(NetworkListenerProtocol::Lmtp, "lmtp_0", 11200, false) + .await .build() - .unwrap() - .block_on(async { - println!("Running cluster broadcast tests..."); - let mut servers = Vec::with_capacity(NUM_NODES); + .await; + let admin = test.account("admin"); + admin.mta_no_auth().await; + let account = admin + .create_user_account( + "jdoe@example.com", + "this is john's secret", + "John's account", + &[], + vec![], + ) + .await; + admin.reload_settings().await; - let coordinator_id = std::env::var("COORDINATOR").expect(concat!( - "Missing coordinator type. Try running `STORE= ", - "COORDINATOR= cargo test`" - )); - let coordinator = match coordinator_id.as_str() { - "Nats" => Coordinator::Nats(NatsCoordinator { - addresses: Map::new(vec!["127.0.0.1:4222".to_string()]), - use_tls: false, - ..Default::default() + // Create listeners + let mut listeners = vec![ + test.server + .registry() + .query::>(RegistryQuery::new(ObjectType::NetworkListener)) + .await + .unwrap(), + ]; + for node_id in 1..NUM_NODES { + let http_listener_id = admin + .registry_create_object(NetworkListener { + name: format!("http_{}", node_id), + bind: Map::new(vec![ + SocketAddr::from_str(&format!("127.0.0.1:1100{node_id}")).unwrap(), + ]), + protocol: NetworkListenerProtocol::Http, + tls_implicit: true, + use_tls: true, + ..Default::default() + }) + .await; + let imap_listener_id = admin + .registry_create_object(NetworkListener { + name: format!("imap_{}", node_id), + bind: Map::new(vec![ + SocketAddr::from_str(&format!("127.0.0.1:1200{node_id}")).unwrap(), + ]), + protocol: NetworkListenerProtocol::Imap, + tls_implicit: false, + use_tls: true, + ..Default::default() + }) + .await; + listeners.push(vec![http_listener_id, imap_listener_id]); + } + + // Create node roles + for (role_id, listener_ids) in listeners.into_iter().enumerate() { + admin + .registry_create_object(ClusterRole { + name: format!("role_{role_id}"), + listeners: ClusterListenerGroup::EnableSome(ClusterListenerGroupProperties { + listener_ids: Map::new(listener_ids), }), - "Redis" => Coordinator::Redis(RedisStore { - url: "redis://127.0.0.1".to_string(), - ..Default::default() - }), - _ => panic!("Unsupported coordinator type: {}", coordinator_id), - }; + tasks: ClusterTaskGroup::EnableAll, + description: None, + }) + .await; + } + servers.push(test); - // Create initial server - let test = TestServerBuilder::new("cluster_test_0") - .await - .with_object(coordinator) - .await - .with_listener(NetworkListenerProtocol::Http, "http_0", 11000, true) - .await - .with_imap_listener(12000) - .await - .with_listener(NetworkListenerProtocol::Lmtp, "lmtp_0", 11200, false) - .await - .build() - .await; - let admin = test.account("admin"); - admin.mta_no_auth().await; - let account = admin - .create_user_account( - "jdoe@example.com", - "this is john's secret", - "John's account", - &[], - vec![], - ) - .await; - admin.reload_settings().await; + // Build additional servers + for node_id in 1..NUM_NODES { + let test = TestServerBuilder::new_with_role( + &format!("cluster_test_{node_id}"), + format!("mail-{node_id}.example.com"), + Some(format!("role_{node_id}")), + false, + ) + .await + .build_with_opts(false) + .await; - // Create listeners - let mut listeners = vec![ - test.server - .registry() - .query::>(RegistryQuery::new(ObjectType::NetworkListener)) - .await - .unwrap(), - ]; - for node_id in 1..NUM_NODES { - let http_listener_id = admin - .registry_create_object(NetworkListener { - name: format!("http_{}", node_id), - bind: Map::new(vec![ - SocketAddr::from_str(&format!("127.0.0.1:1100{node_id}")).unwrap(), - ]), - protocol: NetworkListenerProtocol::Http, - tls_implicit: true, - use_tls: true, - ..Default::default() - }) - .await; - let imap_listener_id = admin - .registry_create_object(NetworkListener { - name: format!("imap_{}", node_id), - bind: Map::new(vec![ - SocketAddr::from_str(&format!("127.0.0.1:1200{node_id}")).unwrap(), - ]), - protocol: NetworkListenerProtocol::Imap, - tls_implicit: false, - use_tls: true, - ..Default::default() - }) - .await; - listeners.push(vec![http_listener_id, imap_listener_id]); - } + // Verify that the server was assigned the correct node id + assert_eq!(test.server.registry().node_id(), node_id as u16); + servers.push(test); + } - // Create node roles - for (role_id, listener_ids) in listeners.into_iter().enumerate() { - admin - .registry_create_object(ClusterRole { - name: format!("role_{role_id}"), - listeners: ClusterListenerGroup::EnableSome( - ClusterListenerGroupProperties { - listener_ids: Map::new(listener_ids), - }, - ), - tasks: ClusterTaskGroup::EnableAll, - description: None, - }) - .await; - } - servers.push(test); + // Verify cross-cluster cache invalidations + let admin = servers[0].account("admin"); + let server1 = &servers[1].server; + let server2 = &servers[2].server; + let account_id = account.id().document_id(); + assert_eq!( + server1 + .account(account_id) + .await + .unwrap() + .description + .as_deref(), + Some("John's account") + ); + assert_eq!( + server2 + .account(account_id) + .await + .unwrap() + .description + .as_deref(), + Some("John's account") + ); + admin + .registry_update_object( + ObjectType::Account, + account.id(), + json!({ + Property::Description: "John Doe" + }), + ) + .await; + tokio::time::sleep(std::time::Duration::from_millis(200)).await; + assert_eq!( + server1 + .account(account_id) + .await + .unwrap() + .description + .as_deref(), + Some("John Doe") + ); + assert_eq!( + server2 + .account(account_id) + .await + .unwrap() + .description + .as_deref(), + Some("John Doe") + ); - // Build additional servers - for node_id in 1..NUM_NODES { - let test = TestServerBuilder::new_with_role( - &format!("cluster_test_{node_id}"), - format!("mail-{node_id}.example.com"), - Some(format!("role_{node_id}")), - false, - ) - .await - .build_with_opts(false) - .await; - - // Verify that the server was assigned the correct node id - assert_eq!(test.server.registry().node_id(), node_id as u16); - servers.push(test); - } - - // Verify cross-cluster cache invalidations - let admin = servers[0].account("admin"); - let server1 = &servers[1].server; - let server2 = &servers[2].server; - let account_id = account.id().document_id(); - assert_eq!( - server1 - .account(account_id) - .await - .unwrap() - .description - .as_deref(), - Some("John's account") - ); - assert_eq!( - server2 - .account(account_id) - .await - .unwrap() - .description - .as_deref(), - Some("John's account") - ); - admin - .registry_update_object( - ObjectType::Account, - account.id(), - json!({ - Property::Description: "John Doe" - }), - ) - .await; - tokio::time::sleep(std::time::Duration::from_millis(200)).await; - assert_eq!( - server1 - .account(account_id) - .await - .unwrap() - .description - .as_deref(), - Some("John Doe") - ); - assert_eq!( - server2 - .account(account_id) - .await - .unwrap() - .description - .as_deref(), - Some("John Doe") - ); - - // Run IMAP idle tests across nodes - let mut node1_client = - imap_client("jdoe@example.com", "this is john's secret", 1).await; - let mut node2_client = - imap_client("jdoe@example.com", "this is john's secret", 2).await; - idle::test(&mut node1_client, &mut node2_client, true).await; - }); + // Run IMAP idle tests across nodes + let mut node1_client = imap_client("jdoe@example.com", "this is john's secret", 1).await; + let mut node2_client = imap_client("jdoe@example.com", "this is john's secret", 2).await; + idle::test(&mut node1_client, &mut node2_client, true).await; } async fn imap_client(login: &str, secret: &str, node_id: u32) -> ImapConnection { diff --git a/tests/src/imap/mod.rs b/tests/src/imap/mod.rs index 243dec85..404c7607 100644 --- a/tests/src/imap/mod.rs +++ b/tests/src/imap/mod.rs @@ -41,251 +41,244 @@ use serde_json::json; use std::{path::PathBuf, time::Instant}; use utils::map::vec_map::VecMap; -#[test] -fn imap_tests() { - tokio::runtime::Builder::new_multi_thread() - .thread_stack_size(8 * 1024 * 1024) // 8MB stack - .enable_all() +#[tokio::test(flavor = "multi_thread")] +pub async fn imap_tests() { + let mut test = TestServerBuilder::new("imap_tests") + .await + .with_default_listeners() + .await .build() - .unwrap() - .block_on(async { - let mut test = TestServerBuilder::new("imap_tests") - .await - .with_default_listeners() - .await - .build() - .await; + .await; - // Create admin account - let admin = test.create_admin_account("admin@example.com").await; + // Create admin account + let admin = test.create_admin_account("admin@example.com").await; - // Create test users - for (name, secret, description, aliases) in [ - ( - "jdoe@example.com", - "12345 + extra safety", - "John Doe", - &["john.doe@example.com"][..], - ), - ( - "jane.smith@example.com", - "abcde + extra safety", - "Jane Smith", - &["jane@example.com"][..], - ), - ( - "foobar@example.com", - "098765 + extra safety", - "Bill Foobar", - &["bill.foobar@example.com"][..], - ), - ( - "popper@example.com", - "a_pop3_safe_secret_with_extra_safety", - "Karl Popper", - &["karl.popper@example.com"][..], - ), - ( - "sgd@example.com", - "secret2 + extra safety", - "Sigmund Gudmund Dudmundsson", - &[][..], - ), - ( - "spamtrap@example.com", - "secret3 + extra safety", - "Spam Trap", - &[][..], - ), - ] { - let account = admin - .create_user_account( - name, - secret, - description, - aliases, - vec![Permission::UnlimitedRequests, Permission::UnlimitedUploads], + // Create test users + for (name, secret, description, aliases) in [ + ( + "jdoe@example.com", + "12345 + extra safety", + "John Doe", + &["john.doe@example.com"][..], + ), + ( + "jane.smith@example.com", + "abcde + extra safety", + "Jane Smith", + &["jane@example.com"][..], + ), + ( + "foobar@example.com", + "098765 + extra safety", + "Bill Foobar", + &["bill.foobar@example.com"][..], + ), + ( + "popper@example.com", + "a_pop3_safe_secret_with_extra_safety", + "Karl Popper", + &["karl.popper@example.com"][..], + ), + ( + "sgd@example.com", + "secret2 + extra safety", + "Sigmund Gudmund Dudmundsson", + &[][..], + ), + ( + "spamtrap@example.com", + "secret3 + extra safety", + "Spam Trap", + &[][..], + ), + ] { + let account = admin + .create_user_account( + name, + secret, + description, + aliases, + vec![Permission::UnlimitedRequests, Permission::UnlimitedUploads], + ) + .await; + test.insert_account(account); + } + + // Create test group + test.insert_account( + admin + .create_group_account("support@example.com", "Support Group", &[]) + .await, + ); + + // Add Jane to the Support group + let support_id = test.account("support@example.com").id(); + admin + .registry_update_object( + ObjectType::Account, + test.account("jane.smith@example.com").id(), + json!({ + "memberGroupIds": { support_id: true }, + }), + ) + .await; + + // Add test settings + admin + .registry_create_object(Imap { + allow_plain_text_auth: true, + ..Default::default() + }) + .await; + admin + .registry_create_object(MtaStageAuth { + require: Expression { + else_: "false".to_string(), + ..Default::default() + }, + ..Default::default() + }) + .await; + admin + .registry_create_object(SpamClassifier { + min_ham_samples: 10, + min_spam_samples: 10, + ..Default::default() + }) + .await; + admin + .registry_create_object(Email { + default_folders: VecMap::from_iter( + [ + (SpecialUse::Inbox, "Inbox"), + (SpecialUse::Sent, "Sent Items"), + (SpecialUse::Trash, "Deleted Items"), + (SpecialUse::Junk, "Junk Mail"), + (SpecialUse::Drafts, "Drafts"), + ] + .into_iter() + .map(|(use_, name)| { + ( + use_, + EmailFolder { + name: name.into(), + subscribe: false, + ..Default::default() + }, ) - .await; - test.insert_account(account); - } + }), + ), + ..Default::default() + }) + .await; + admin + .registry_create_object(MtaStageData { + add_delivered_to_header: false, + enable_spam_filter: Expression { + else_: "recipients[0] != 'popper@example.com'".into(), + ..Default::default() + }, + ..Default::default() + }) + .await; + admin + .registry_create_object(SpamTag::Score(SpamTagScore { + score: Float::new(10.0), + tag: "PROB_SPAM_LOW".into(), + })) + .await; + admin + .registry_create_object(SpamTag::Score(SpamTagScore { + score: Float::new(10.0), + tag: "PROB_SPAM_HIGH".into(), + })) + .await; + admin + .registry_create_object(SpamTag::Score(SpamTagScore { + score: Float::new(100.0), + tag: "SPAM_TRAP".into(), + })) + .await; + admin + .registry_create_object(MemoryLookupKey { + is_glob_pattern: true, + key: "spamtrap@*".into(), + namespace: "spam-traps".into(), + }) + .await; + admin.reload_settings().await; + admin.reload_lookup_stores().await; - // Create test group - test.insert_account( - admin - .create_group_account("support@example.com", "Support Group", &[]) - .await, - ); + test.insert_account(admin); - // Add Jane to the Support group - let support_id = test.account("support@example.com").id(); - admin - .registry_update_object( - ObjectType::Account, - test.account("jane.smith@example.com").id(), - json!({ - "memberGroupIds": { support_id: true }, - }), - ) - .await; + let start_time = Instant::now(); - // Add test settings - admin - .registry_create_object(Imap { - allow_plain_text_auth: true, - ..Default::default() - }) - .await; - admin - .registry_create_object(MtaStageAuth { - require: Expression { - else_: "false".to_string(), - ..Default::default() - }, - ..Default::default() - }) - .await; - admin - .registry_create_object(SpamClassifier { - min_ham_samples: 10, - min_spam_samples: 10, - ..Default::default() - }) - .await; - admin - .registry_create_object(Email { - default_folders: VecMap::from_iter( - [ - (SpecialUse::Inbox, "Inbox"), - (SpecialUse::Sent, "Sent Items"), - (SpecialUse::Trash, "Deleted Items"), - (SpecialUse::Junk, "Junk Mail"), - (SpecialUse::Drafts, "Drafts"), - ] - .into_iter() - .map(|(use_, name)| { - ( - use_, - EmailFolder { - name: name.into(), - subscribe: false, - ..Default::default() - }, - ) - }), - ), - ..Default::default() - }) - .await; - admin - .registry_create_object(MtaStageData { - add_delivered_to_header: false, - enable_spam_filter: Expression { - else_: "recipients[0] != 'popper@example.com'".into(), - ..Default::default() - }, - ..Default::default() - }) - .await; - admin - .registry_create_object(SpamTag::Score(SpamTagScore { - score: Float::new(10.0), - tag: "PROB_SPAM_LOW".into(), - })) - .await; - admin - .registry_create_object(SpamTag::Score(SpamTagScore { - score: Float::new(10.0), - tag: "PROB_SPAM_HIGH".into(), - })) - .await; - admin - .registry_create_object(SpamTag::Score(SpamTagScore { - score: Float::new(100.0), - tag: "SPAM_TRAP".into(), - })) - .await; - admin - .registry_create_object(MemoryLookupKey { - is_glob_pattern: true, - key: "spamtrap@*".into(), - namespace: "spam-traps".into(), - }) - .await; - admin.reload_settings().await; - admin.reload_lookup_stores().await; + // Body structure tests + body_structure::test(); - test.insert_account(admin); + // Connect to IMAP server + let mut imap_check = ImapConnection::connect(b"_y ").await; + let mut imap = ImapConnection::connect(b"_x ").await; + for imap in [&mut imap, &mut imap_check] { + imap.assert_read(Type::Untagged, ResponseType::Ok).await; + } - let start_time = Instant::now(); + // Unauthenticated tests + basic::test(&mut imap, &mut imap_check).await; - // Body structure tests - body_structure::test(); + // Login + let account = test.account("jdoe@example.com"); + for imap in [&mut imap, &mut imap_check] { + imap.authenticate(account.name(), account.secret()).await; + } - // Connect to IMAP server - let mut imap_check = ImapConnection::connect(b"_y ").await; - let mut imap = ImapConnection::connect(b"_x ").await; - for imap in [&mut imap, &mut imap_check] { - imap.assert_read(Type::Untagged, ResponseType::Ok).await; - } + // Delete folders + for mailbox in ["Drafts", "Junk Mail", "Sent Items"] { + imap.send(&format!("DELETE \"{}\"", mailbox)).await; + imap.assert_read(Type::Tagged, ResponseType::Ok).await; + } - // Unauthenticated tests - basic::test(&mut imap, &mut imap_check).await; + mailbox::test(&mut imap, &mut imap_check, &test).await; + append::test(&mut imap, &mut imap_check, &test).await; + search::test(&mut imap, &mut imap_check, &test).await; + fetch::test(&mut imap, &mut imap_check).await; + store::test(&mut imap, &mut imap_check, &test).await; + copy_move::test(&mut imap, &mut imap_check).await; + thread::test(&mut imap, &mut imap_check, &test).await; + idle::test(&mut imap, &mut imap_check, false).await; + condstore::test(&mut imap, &mut imap_check).await; + acl::test(&mut imap, &mut imap_check, &test).await; - // Login - let account = test.account("jdoe@example.com"); - for imap in [&mut imap, &mut imap_check] { - imap.authenticate(account.name(), account.secret()).await; - } + // Logout + for imap in [&mut imap, &mut imap_check] { + imap.send("UNAUTHENTICATE").await; + imap.assert_read(Type::Tagged, ResponseType::Ok).await; - // Delete folders - for mailbox in ["Drafts", "Junk Mail", "Sent Items"] { - imap.send(&format!("DELETE \"{}\"", mailbox)).await; - imap.assert_read(Type::Tagged, ResponseType::Ok).await; - } + imap.send("LOGOUT").await; + imap.assert_read(Type::Untagged, ResponseType::Bye).await; + } - mailbox::test(&mut imap, &mut imap_check, &test).await; - append::test(&mut imap, &mut imap_check, &test).await; - search::test(&mut imap, &mut imap_check, &test).await; - fetch::test(&mut imap, &mut imap_check).await; - store::test(&mut imap, &mut imap_check, &test).await; - copy_move::test(&mut imap, &mut imap_check).await; - thread::test(&mut imap, &mut imap_check, &test).await; - idle::test(&mut imap, &mut imap_check, false).await; - condstore::test(&mut imap, &mut imap_check).await; - acl::test(&mut imap, &mut imap_check, &test).await; + // Antispam training + antispam::test(&test).await; - // Logout - for imap in [&mut imap, &mut imap_check] { - imap.send("UNAUTHENTICATE").await; - imap.assert_read(Type::Tagged, ResponseType::Ok).await; + // Run ManageSieve tests + managesieve::test(&test).await; - imap.send("LOGOUT").await; - imap.assert_read(Type::Untagged, ResponseType::Bye).await; - } + // Run POP3 tests + pop::test(&test).await; - // Antispam training - antispam::test(&test).await; + // Print elapsed time + let elapsed = start_time.elapsed(); + println!( + "Elapsed: {}.{:03}s", + elapsed.as_secs(), + elapsed.subsec_millis() + ); - // Run ManageSieve tests - managesieve::test(&test).await; - - // Run POP3 tests - pop::test(&test).await; - - // Print elapsed time - let elapsed = start_time.elapsed(); - println!( - "Elapsed: {}.{:03}s", - elapsed.as_secs(), - elapsed.subsec_millis() - ); - - // Remove test data - if test.is_reset() { - test.temp_dir.delete(); - } - }); + // Remove test data + if test.is_reset() { + test.temp_dir.delete(); + } } pub fn expand_uid_list(list: &str) -> AHashSet { diff --git a/tests/src/jmap/mod.rs b/tests/src/jmap/mod.rs index cf839b2c..2ad867d7 100644 --- a/tests/src/jmap/mod.rs +++ b/tests/src/jmap/mod.rs @@ -23,201 +23,194 @@ pub mod files; pub mod mail; pub mod principal; -#[test] -fn jmap_tests() { - tokio::runtime::Builder::new_multi_thread() - .thread_stack_size(8 * 1024 * 1024) // 8MB stack - .enable_all() +#[tokio::test(flavor = "multi_thread")] +pub async fn jmap_tests() { + let mut test = TestServerBuilder::new("jmap_tests") + .await + .with_default_listeners() + .await .build() - .unwrap() - .block_on(async { - let mut test = TestServerBuilder::new("jmap_tests") - .await - .with_default_listeners() - .await - .build() - .await; + .await; - // Create admin account - let admin = test.create_admin_account("admin@example.com").await; + // Create admin account + let admin = test.create_admin_account("admin@example.com").await; - // Create test users - for (name, secret, description, aliases) in [ - ( - "jdoe@example.com", - "12345 + extra safety", - "John Doe", - &["john.doe@example.com"][..], - ), - ( - "jane.smith@example.com", - "abcde + extra safety", - "Jane Smith", - &["jane@example.com"], - ), - ( - "bill@example.com", - "098765 + extra safety", - "Bill Foobar", - &["bill.foobar@example.com"], - ), - ( - "robert@example.com", - "aabbcc + extra safety", - "Robert Foobar", - &[][..], - ), - ] { - let account = admin - .create_user_account( - name, - secret, - description, - aliases, - vec![Permission::UnlimitedRequests, Permission::UnlimitedUploads], - ) - .await; - test.insert_account(account); - } + // Create test users + for (name, secret, description, aliases) in [ + ( + "jdoe@example.com", + "12345 + extra safety", + "John Doe", + &["john.doe@example.com"][..], + ), + ( + "jane.smith@example.com", + "abcde + extra safety", + "Jane Smith", + &["jane@example.com"], + ), + ( + "bill@example.com", + "098765 + extra safety", + "Bill Foobar", + &["bill.foobar@example.com"], + ), + ( + "robert@example.com", + "aabbcc + extra safety", + "Robert Foobar", + &[][..], + ), + ] { + let account = admin + .create_user_account( + name, + secret, + description, + aliases, + vec![Permission::UnlimitedRequests, Permission::UnlimitedUploads], + ) + .await; + test.insert_account(account); + } - // Create test group - test.insert_account( - admin - .create_group_account("sales@example.com", "Sales Group", &[]) - .await, - ); + // Create test group + test.insert_account( + admin + .create_group_account("sales@example.com", "Sales Group", &[]) + .await, + ); - // Add test settings - admin - .registry_create_object(Imap { - allow_plain_text_auth: true, - ..Default::default() - }) - .await; - admin - .registry_create_object(Jmap { - set_max_objects: 100_000, - get_max_results: 100_000, - event_source_throttle: 500u64.into(), - push_throttle: 500u64.into(), - websocket_throttle: 500u64.into(), - push_attempt_wait: 500u64.into(), - ..Default::default() - }) - .await; - admin - .registry_create_object(MtaStageAuth { - require: Expression { - else_: "false".to_string(), - ..Default::default() + // Add test settings + admin + .registry_create_object(Imap { + allow_plain_text_auth: true, + ..Default::default() + }) + .await; + admin + .registry_create_object(Jmap { + set_max_objects: 100_000, + get_max_results: 100_000, + event_source_throttle: 500u64.into(), + push_throttle: 500u64.into(), + websocket_throttle: 500u64.into(), + push_attempt_wait: 500u64.into(), + ..Default::default() + }) + .await; + admin + .registry_create_object(MtaStageAuth { + require: Expression { + else_: "false".to_string(), + ..Default::default() + }, + ..Default::default() + }) + .await; + admin + .registry_create_object(CalendarAlarm { + min_trigger_interval: 1000u64.into(), + ..Default::default() + }) + .await; + admin + .registry_create_object(Sharing { + allow_directory_queries: true, + ..Default::default() + }) + .await; + admin + .registry_create_object(MtaOutboundStrategy { + route: Expression { + match_: List::from_iter([ + ExpressionMatch { + if_: "rcpt_domain == 'example.com'".into(), + then: "'local'".into(), }, - ..Default::default() - }) - .await; - admin - .registry_create_object(CalendarAlarm { - min_trigger_interval: 1000u64.into(), - ..Default::default() - }) - .await; - admin - .registry_create_object(Sharing { - allow_directory_queries: true, - ..Default::default() - }) - .await; - admin - .registry_create_object(MtaOutboundStrategy { - route: Expression { - match_: List::from_iter([ - ExpressionMatch { - if_: "rcpt_domain == 'example.com'".into(), - then: "'local'".into(), - }, - ExpressionMatch { - if_: concat!( - "contains(['remote.org', 'foobar.com', ", - "'test.com', 'other_domain.com'], rcpt_domain)" - ) - .into(), - then: "'mock-smtp'".into(), - }, - ]), - else_: "'mx'".to_string(), + ExpressionMatch { + if_: concat!( + "contains(['remote.org', 'foobar.com', ", + "'test.com', 'other_domain.com'], rcpt_domain)" + ) + .into(), + then: "'mock-smtp'".into(), }, - ..Default::default() - }) - .await; - admin - .registry_create_object(MtaRoute::Relay(MtaRouteRelay { - address: "127.0.0.1".into(), - port: 9999, - allow_invalid_certs: true, - implicit_tls: false, - name: "mock-smtp".into(), - protocol: MtaProtocol::Smtp, - ..Default::default() - })) - .await; - admin - .registry_create_object(MtaExtensions { - future_release: Expression { - match_: List::from_iter([ExpressionMatch { - if_: "!is_empty(authenticated_as)".into(), - then: "99999999d".into(), - }]), - else_: "false".to_string(), - }, - ..Default::default() - }) - .await; - admin.reload_settings().await; + ]), + else_: "'mx'".to_string(), + }, + ..Default::default() + }) + .await; + admin + .registry_create_object(MtaRoute::Relay(MtaRouteRelay { + address: "127.0.0.1".into(), + port: 9999, + allow_invalid_certs: true, + implicit_tls: false, + name: "mock-smtp".into(), + protocol: MtaProtocol::Smtp, + ..Default::default() + })) + .await; + admin + .registry_create_object(MtaExtensions { + future_release: Expression { + match_: List::from_iter([ExpressionMatch { + if_: "!is_empty(authenticated_as)".into(), + then: "99999999d".into(), + }]), + else_: "false".to_string(), + }, + ..Default::default() + }) + .await; + admin.reload_settings().await; - test.insert_account(admin); + test.insert_account(admin); - /*mail::get::test(&test).await; - mail::set::test(&test).await; - mail::parse::test(&test).await; - mail::query::test(&test).await; - mail::search_snippet::test(&test).await; - mail::changes::test(&test).await; - mail::query_changes::test(&test).await; - mail::copy::test(&test).await; - mail::thread_get::test(&test).await;*/ - mail::thread_merge::test(&test).await; - mail::mailbox::test(&test).await; - mail::acl::test(&test).await; - mail::sieve_script::test(&test).await; - mail::vacation_response::test(&test).await; - mail::submission::test(&test).await; + mail::get::test(&test).await; + mail::set::test(&test).await; + mail::parse::test(&test).await; + mail::query::test(&test).await; + mail::search_snippet::test(&test).await; + mail::changes::test(&test).await; + mail::query_changes::test(&test).await; + mail::copy::test(&test).await; + mail::thread_get::test(&test).await; + mail::thread_merge::test(&test).await; + mail::mailbox::test(&test).await; + mail::acl::test(&test).await; + mail::sieve_script::test(&test).await; + mail::vacation_response::test(&test).await; + mail::submission::test(&test).await; - core::event_source::test(&test).await; - core::websocket::test(&test).await; - core::push_subscription::test(&test).await; - core::blob::test(&test).await; + core::event_source::test(&test).await; + core::websocket::test(&test).await; + core::push_subscription::test(&test).await; + core::blob::test(&test).await; - contacts::addressbook::test(&test).await; - contacts::contact::test(&test).await; - contacts::acl::test(&test).await; + contacts::addressbook::test(&test).await; + contacts::contact::test(&test).await; + contacts::acl::test(&test).await; - files::node::test(&test).await; - files::acl::test(&test).await; + files::node::test(&test).await; + files::acl::test(&test).await; - calendar::calendars::test(&test).await; - calendar::event::test(&test).await; - calendar::notification::test(&test).await; - calendar::alarm::test(&test).await; + calendar::calendars::test(&test).await; + calendar::event::test(&test).await; + calendar::notification::test(&test).await; + calendar::alarm::test(&test).await; - calendar::identity::test(&test).await; - calendar::acl::test(&test).await; + calendar::identity::test(&test).await; + calendar::acl::test(&test).await; - principal::get::test(&test).await; - principal::availability::test(&test).await; + principal::get::test(&test).await; + principal::availability::test(&test).await; - if test.is_reset() { - test.temp_dir.delete(); - } - }); + if test.is_reset() { + test.temp_dir.delete(); + } } pub fn find_values(string: &str, name: &str) -> Vec { diff --git a/tests/src/smtp/inbound/rewrite.rs b/tests/src/smtp/inbound/rewrite.rs index b97f1e3c..07198e9e 100644 --- a/tests/src/smtp/inbound/rewrite.rs +++ b/tests/src/smtp/inbound/rewrite.rs @@ -27,49 +27,43 @@ if allof( envelope :localpart :contains "to" ".", } "#; -#[test] -fn address_rewrite() { - tokio::runtime::Builder::new_multi_thread() - .thread_stack_size(8 * 1024 * 1024) // 8MB stack - .enable_all() +#[tokio::test(flavor = "multi_thread")] +pub async fn address_rewrite() { + let mut test = TestServerBuilder::new("smtp_rewrite_test") + .await + .with_http_listener(19007) + .await + .disable_services() .build() - .unwrap() - .block_on(async { - let mut test = TestServerBuilder::new("smtp_rewrite_test") - .await - .with_http_listener(19007) - .await - .disable_services() - .build() - .await; + .await; - // Add test settings - let admin = test.account("admin"); - admin.mta_no_auth().await; - admin - .registry_create_object(MtaStageMail { - rewrite: Expression { - match_: List::from_iter([ExpressionMatch { - if_: concat!( - "ends_with(sender_domain, '.foobar.net') & ", - "matches('^([^.]+)@([^.]+)\\.(.+)$', sender)" - ) - .into(), - then: "$1 + '+' + $2 + '@' + $3".into(), - }]), - else_: "false".into(), - }, - script: Expression { - match_: List::from_iter([ExpressionMatch { - if_: "sender_domain = 'foobar.org'".into(), - then: "'mail'".into(), - }]), - else_: "false".into(), - }, - ..Default::default() - }) - .await; - admin + // Add test settings + let admin = test.account("admin"); + admin.mta_no_auth().await; + admin + .registry_create_object(MtaStageMail { + rewrite: Expression { + match_: List::from_iter([ExpressionMatch { + if_: concat!( + "ends_with(sender_domain, '.foobar.net') & ", + "matches('^([^.]+)@([^.]+)\\.(.+)$', sender)" + ) + .into(), + then: "$1 + '+' + $2 + '@' + $3".into(), + }]), + else_: "false".into(), + }, + script: Expression { + match_: List::from_iter([ExpressionMatch { + if_: "sender_domain = 'foobar.org'".into(), + then: "'mail'".into(), + }]), + else_: "false".into(), + }, + ..Default::default() + }) + .await; + admin .registry_create_object(MtaStageRcpt { rewrite: Expression { match_: List::from_iter([ExpressionMatch { @@ -93,81 +87,80 @@ fn address_rewrite() { ..Default::default() }) .await; - admin - .registry_create_object(SieveSystemInterpreter { - default_from_address: Expression { - else_: "'sieve@foobar.org'".into(), - ..Default::default() - }, + admin + .registry_create_object(SieveSystemInterpreter { + default_from_address: Expression { + else_: "'sieve@foobar.org'".into(), + ..Default::default() + }, - default_from_name: Expression { - else_: "'Sieve Daemon'".into(), - ..Default::default() - }, - default_return_path: Expression { - else_: "''".into(), - ..Default::default() - }, - message_id_hostname: Some("'mx.foobar.org'".into()), - duplicate_expiry: (86_400u64 * 100 * 7).into(), - max_cpu_cycles: 10000, - max_nested_includes: 5, - max_out_messages: 5, - max_received_headers: 50, - max_redirects: 3, - ..Default::default() - }) - .await; - for (name, contents) in [("mail", MAIL_SCRIPT), ("rcpt", MAIL_RCPT)] { - admin - .registry_create_object(SieveSystemScript { - name: name.to_string(), - contents: contents.to_string(), - is_active: true, - ..Default::default() - }) - .await; - } - admin.reload_settings().await; - test.reload_core(); + default_from_name: Expression { + else_: "'Sieve Daemon'".into(), + ..Default::default() + }, + default_return_path: Expression { + else_: "''".into(), + ..Default::default() + }, + message_id_hostname: Some("'mx.foobar.org'".into()), + duplicate_expiry: (86_400u64 * 100 * 7).into(), + max_cpu_cycles: 10000, + max_nested_includes: 5, + max_out_messages: 5, + max_received_headers: 50, + max_redirects: 3, + ..Default::default() + }) + .await; + for (name, contents) in [("mail", MAIL_SCRIPT), ("rcpt", MAIL_RCPT)] { + admin + .registry_create_object(SieveSystemScript { + name: name.to_string(), + contents: contents.to_string(), + is_active: true, + ..Default::default() + }) + .await; + } + admin.reload_settings().await; + test.reload_core(); - // Init session - let mut session = test.new_mta_session(); - session.data.remote_ip_str = "10.0.0.1".into(); - session.eval_session_params().await; - session.ehlo("mx.doe.org").await; + // Init session + let mut session = test.new_mta_session(); + session.data.remote_ip_str = "10.0.0.1".into(); + session.eval_session_params().await; + session.ehlo("mx.doe.org").await; - // Sender rewrite using regex - session.mail_from("bill@doe.foobar.net", "250").await; - assert_eq!( - session.data.mail_from.as_ref().unwrap().address, - "bill+doe@foobar.net" - ); - session.reset(); + // Sender rewrite using regex + session.mail_from("bill@doe.foobar.net", "250").await; + assert_eq!( + session.data.mail_from.as_ref().unwrap().address, + "bill+doe@foobar.net" + ); + session.reset(); - // Sender rewrite using sieve - session.mail_from("this_is_admin@foobar.org", "250").await; - assert_eq!( - session.data.mail_from.as_ref().unwrap().address_lcase, - "mailer-daemon@foobar.org" - ); + // Sender rewrite using sieve + session.mail_from("this_is_admin@foobar.org", "250").await; + assert_eq!( + session.data.mail_from.as_ref().unwrap().address_lcase, + "mailer-daemon@foobar.org" + ); - // Recipient rewrite using regex - session.rcpt_to("mary.smith@foobar.net", "250").await; - assert_eq!( - session.data.rcpt_to.last().unwrap().address, - "mary+smith@foobar.net" - ); + // Recipient rewrite using regex + session.rcpt_to("mary.smith@foobar.net", "250").await; + assert_eq!( + session.data.rcpt_to.last().unwrap().address, + "mary+smith@foobar.net" + ); - // Remove duplicates - session.rcpt_to("mary.smith@foobar.net", "250").await; - assert_eq!(session.data.rcpt_to.len(), 1); + // Remove duplicates + session.rcpt_to("mary.smith@foobar.net", "250").await; + assert_eq!(session.data.rcpt_to.len(), 1); - // Recipient rewrite using sieve - session.rcpt_to("m.a.r.y.s.m.i.t.h@foobar.org", "250").await; - assert_eq!( - session.data.rcpt_to.last().unwrap().address, - "marysmith@foobar.org" - ); - }); + // Recipient rewrite using sieve + session.rcpt_to("m.a.r.y.s.m.i.t.h@foobar.org", "250").await; + assert_eq!( + session.data.rcpt_to.last().unwrap().address, + "marysmith@foobar.org" + ); } diff --git a/tests/src/system/mod.rs b/tests/src/system/mod.rs index 16218c22..037f165f 100644 --- a/tests/src/system/mod.rs +++ b/tests/src/system/mod.rs @@ -21,65 +21,58 @@ pub mod tenant; use crate::utils::server::TestServerBuilder; use registry::schema::structs::{Expression, Imap, MtaStageAuth}; -#[test] -fn system_tests() { - tokio::runtime::Builder::new_multi_thread() - .thread_stack_size(8 * 1024 * 1024) // 8MB stack - .enable_all() +#[tokio::test(flavor = "multi_thread")] +pub async fn system_tests() { + let mut test = TestServerBuilder::new("system_tests") + .await + .with_default_listeners() + .await + .with_object(Imap { + allow_plain_text_auth: true, + ..Default::default() + }) + .await + .with_object(MtaStageAuth { + require: Expression { + else_: "false".to_string(), + ..Default::default() + }, + ..Default::default() + }) + .await .build() - .unwrap() - .block_on(async { - let mut test = TestServerBuilder::new("system_tests") - .await - .with_default_listeners() - .await - .with_object(Imap { - allow_plain_text_auth: true, - ..Default::default() - }) - .await - .with_object(MtaStageAuth { - require: Expression { - else_: "false".to_string(), - ..Default::default() - }, - ..Default::default() - }) - .await - .build() - .await; + .await; - // Create admin account - let admin = test - .create_user_account( - "admin", - "admin@example.org", - "these_pretzels_are_making_me_thirsty", - &[], - "Admin", - ) - .await; - test.account("admin") - .assign_roles_to_account(admin.id(), &["user", "system"]) - .await; - test.insert_account(admin); + // Create admin account + let admin = test + .create_user_account( + "admin", + "admin@example.org", + "these_pretzels_are_making_me_thirsty", + &[], + "Admin", + ) + .await; + test.account("admin") + .assign_roles_to_account(admin.id(), &["user", "system"]) + .await; + test.insert_account(admin); - directory::test(&test).await; - authentication::test(&test).await; - oidc::test(&mut test).await; - authorization::test(&mut test).await; - tenant::test(&mut test).await; - security::test(&mut test).await; - quota::test(&mut test).await; - purge::test(&mut test).await; - delivery::test(&mut test).await; - crypto::test(&mut test).await; - antispam::test(&mut test).await; - archiving::test(&mut test).await; - task::test(&mut test).await; + directory::test(&test).await; + authentication::test(&test).await; + oidc::test(&mut test).await; + authorization::test(&mut test).await; + tenant::test(&mut test).await; + security::test(&mut test).await; + quota::test(&mut test).await; + purge::test(&mut test).await; + delivery::test(&mut test).await; + crypto::test(&mut test).await; + antispam::test(&mut test).await; + archiving::test(&mut test).await; + task::test(&mut test).await; - if test.is_reset() { - test.temp_dir.delete(); - } - }); + if test.is_reset() { + test.temp_dir.delete(); + } } diff --git a/tests/src/webdav/mod.rs b/tests/src/webdav/mod.rs index f54d53d8..f3286a35 100644 --- a/tests/src/webdav/mod.rs +++ b/tests/src/webdav/mod.rs @@ -40,180 +40,173 @@ pub mod prop; pub mod put_get; pub mod sync; -#[test] -fn webdav_tests() { - tokio::runtime::Builder::new_multi_thread() - .thread_stack_size(8 * 1024 * 1024) // 8MB stack - .enable_all() +#[tokio::test(flavor = "multi_thread")] +pub async fn webdav_tests() { + // Prepare settings + let assisted_discovery = std::env::var("ASSISTED_DISCOVERY").unwrap_or_default() == "1"; + + let mut test = TestServerBuilder::new("webdav_tests") + .await + .with_default_listeners() + .await .build() - .unwrap() - .block_on(async { - // Prepare settings - let assisted_discovery = std::env::var("ASSISTED_DISCOVERY").unwrap_or_default() == "1"; + .await; - let mut test = TestServerBuilder::new("webdav_tests") - .await - .with_default_listeners() - .await - .build() - .await; + // Create admin account + let admin = test.create_admin_account("admin@example.com").await; - // Create admin account - let admin = test.create_admin_account("admin@example.com").await; - - // Create test users - for (name, secret, description, aliases) in [ - ( - "john@example.com", - "secret2 + some more text", - "John Doe", - &["jdoe@example.com"], - ), - ( - "jane@example.com", - "secret3 + some more text", - "Jane Doe-Smith", - &["jane.smith@example.com"], - ), - ( - "bill@example.com", - "secret4 + some more text", - "Bill Foobar", - &["bill@example.com"], - ), - ( - "mike@example.com", - "secret5 + some more text", - "Mike Noquota", - &["mike@example.com"], - ), - ] { - let account = admin - .create_user_account( - name, - secret, - description, - aliases, - vec![ - Permission::UnlimitedRequests, - Permission::UnlimitedUploads, - Permission::DavPrincipalList, - Permission::DavPrincipalSearch, - ], - ) - .await; - if name == "mike@example.com" { - admin - .registry_update_object( - ObjectType::Account, - account.id(), - json!({ - Property::Quotas: { StorageQuota::MaxDiskQuota.as_str(): 1024} - }), - ) - .await; - } - - test.insert_account(account); - } - - // Create test group - test.insert_account( - admin - .create_group_account("support@example.com", "Support Group", &[]) - .await, - ); - - // Add Jane to the Support group - let support_id = test.account("support@example.com").id(); + // Create test users + for (name, secret, description, aliases) in [ + ( + "john@example.com", + "secret2 + some more text", + "John Doe", + &["jdoe@example.com"], + ), + ( + "jane@example.com", + "secret3 + some more text", + "Jane Doe-Smith", + &["jane.smith@example.com"], + ), + ( + "bill@example.com", + "secret4 + some more text", + "Bill Foobar", + &["bill@example.com"], + ), + ( + "mike@example.com", + "secret5 + some more text", + "Mike Noquota", + &["mike@example.com"], + ), + ] { + let account = admin + .create_user_account( + name, + secret, + description, + aliases, + vec![ + Permission::UnlimitedRequests, + Permission::UnlimitedUploads, + Permission::DavPrincipalList, + Permission::DavPrincipalSearch, + ], + ) + .await; + if name == "mike@example.com" { admin .registry_update_object( ObjectType::Account, - test.account("jane@example.com").id(), + account.id(), json!({ - "memberGroupIds": { support_id: true }, + Property::Quotas: { StorageQuota::MaxDiskQuota.as_str(): 1024} }), ) .await; + } - // Add test settings - admin - .registry_update_setting( - SystemSettings { - default_hostname: "webdav.example.org".to_string(), - ..Default::default() - }, - &[Property::DefaultHostname], - ) - .await; - admin - .registry_create_object(MtaStageAuth { - require: Expression { - else_: "false".to_string(), - ..Default::default() - }, - ..Default::default() - }) - .await; - admin - .registry_create_object(CalendarAlarm { - min_trigger_interval: 1000u64.into(), - ..Default::default() - }) - .await; - admin - .registry_create_object(Sharing { - allow_directory_queries: true, - ..Default::default() - }) - .await; - admin - .registry_create_object(CalendarScheduling { - auto_add_invitations: true, - ..Default::default() - }) - .await; - admin - .registry_create_object(WebDav { - enable_assisted_discovery: assisted_discovery, - ..Default::default() - }) - .await; - admin.reload_settings().await; + test.insert_account(account); + } - test.insert_account(admin); + // Create test group + test.insert_account( + admin + .create_group_account("support@example.com", "Support Group", &[]) + .await, + ); - let start_time = Instant::now(); - //test_build_itip_templates(&test).await; - basic::test(&test).await; - put_get::test(&test).await; - mkcol::test(&test).await; - copy_move::test(&test, assisted_discovery).await; - prop::test(&test, assisted_discovery).await; - multiget::test(&test).await; - sync::test(&test).await; - lock::test(&test).await; - principals::test(&test, assisted_discovery).await; - acl::test(&test).await; - card_query::test(&test).await; - cal_query::test(&test).await; - cal_alarm::test(&test).await; - cal_itip::test(); - cal_scheduling::test(&test).await; + // Add Jane to the Support group + let support_id = test.account("support@example.com").id(); + admin + .registry_update_object( + ObjectType::Account, + test.account("jane@example.com").id(), + json!({ + "memberGroupIds": { support_id: true }, + }), + ) + .await; - // Print elapsed time - let elapsed = start_time.elapsed(); - println!( - "Elapsed: {}.{:03}s", - elapsed.as_secs(), - elapsed.subsec_millis() - ); + // Add test settings + admin + .registry_update_setting( + SystemSettings { + default_hostname: "webdav.example.org".to_string(), + ..Default::default() + }, + &[Property::DefaultHostname], + ) + .await; + admin + .registry_create_object(MtaStageAuth { + require: Expression { + else_: "false".to_string(), + ..Default::default() + }, + ..Default::default() + }) + .await; + admin + .registry_create_object(CalendarAlarm { + min_trigger_interval: 1000u64.into(), + ..Default::default() + }) + .await; + admin + .registry_create_object(Sharing { + allow_directory_queries: true, + ..Default::default() + }) + .await; + admin + .registry_create_object(CalendarScheduling { + auto_add_invitations: true, + ..Default::default() + }) + .await; + admin + .registry_create_object(WebDav { + enable_assisted_discovery: assisted_discovery, + ..Default::default() + }) + .await; + admin.reload_settings().await; - // Remove test data - if test.is_reset() { - test.temp_dir.delete(); - } - }); + test.insert_account(admin); + + let start_time = Instant::now(); + //test_build_itip_templates(&test).await; + basic::test(&test).await; + put_get::test(&test).await; + mkcol::test(&test).await; + copy_move::test(&test, assisted_discovery).await; + prop::test(&test, assisted_discovery).await; + multiget::test(&test).await; + sync::test(&test).await; + lock::test(&test).await; + principals::test(&test, assisted_discovery).await; + acl::test(&test).await; + card_query::test(&test).await; + cal_query::test(&test).await; + cal_alarm::test(&test).await; + cal_itip::test(); + cal_scheduling::test(&test).await; + + // Print elapsed time + let elapsed = start_time.elapsed(); + println!( + "Elapsed: {}.{:03}s", + elapsed.as_secs(), + elapsed.subsec_millis() + ); + + // Remove test data + if test.is_reset() { + test.temp_dir.delete(); + } } pub trait DavResourcesTest {