From fde37b670f89e18bb2f2808400d07cbc9cbb6ff1 Mon Sep 17 00:00:00 2001 From: Maurus Decimus <11444311+mdecimus@users.noreply.github.com> Date: Sun, 5 Apr 2026 19:57:23 +0200 Subject: [PATCH] Login screen and api endpoint reorganization --- .../common/src/telemetry/tracers/journald.rs | 2 +- crates/common/src/telemetry/tracers/otel.rs | 4 +- crates/directory/src/backend/oidc/mod.rs | 17 +- crates/directory/src/core/dispatch.rs | 8 +- .../http/src/{management => api}/diagnose.rs | 0 crates/http/src/{management => api}/mod.rs | 108 +- .../http/src/{management => api}/telemetry.rs | 5 +- crates/http/src/auth/mod.rs | 1 + crates/http/src/auth/oauth/auth.rs | 80 +- crates/http/src/auth/oauth/openid.rs | 10 +- crates/http/src/auth/permissions.rs | 47 + crates/http/src/lib.rs | 5 +- crates/http/src/request.rs | 80 +- crates/registry/src/jmap/patch.rs | 2 +- crates/registry/src/jmap/ser.rs | 2 +- crates/registry/src/pickle.rs | 4 +- crates/trc/src/event/conv.rs | 2 +- crates/trc/src/event/enums.rs | 72 + crates/trc/src/event/enums_impl.rs | 1391 +++-------------- crates/trc/src/event/mod.rs | 145 -- crates/trc/src/lib.rs | 73 - crates/trc/src/serializers/binary.rs | 8 +- crates/trc/src/serializers/json.rs | 41 +- crates/trc/src/serializers/text.rs | 4 +- resources/html-templates/login.html | 427 ++++- resources/html-templates/login.html.min | 1 + resources/scripts/minify_html.sh | 56 + tests/src/directory/oidc.rs | 6 +- tests/src/system/oidc.rs | 10 +- 29 files changed, 1005 insertions(+), 1606 deletions(-) rename crates/http/src/{management => api}/diagnose.rs (100%) rename crates/http/src/{management => api}/mod.rs (66%) rename crates/http/src/{management => api}/telemetry.rs (98%) create mode 100644 crates/http/src/auth/permissions.rs create mode 100644 resources/html-templates/login.html.min create mode 100755 resources/scripts/minify_html.sh diff --git a/crates/common/src/telemetry/tracers/journald.rs b/crates/common/src/telemetry/tracers/journald.rs index 27f172bd..56e363e7 100644 --- a/crates/common/src/telemetry/tracers/journald.rs +++ b/crates/common/src/telemetry/tracers/journald.rs @@ -44,7 +44,7 @@ impl Subscriber { let mut seen_keys = AHashSet::new(); for (key, value) in &event.keys { if seen_keys.insert(*key) { - put_field_length_encoded(&mut buf, key.name(), |buf| { + put_field_length_encoded(&mut buf, key.as_str(), |buf| { write!(buf, "{value}").unwrap() }); } diff --git a/crates/common/src/telemetry/tracers/otel.rs b/crates/common/src/telemetry/tracers/otel.rs index 4b4ad8d1..1d6747e2 100644 --- a/crates/common/src/telemetry/tracers/otel.rs +++ b/crates/common/src/telemetry/tracers/otel.rs @@ -203,7 +203,7 @@ impl OtelTracer { record.set_timestamp(UNIX_EPOCH + Duration::from_secs(event.inner.timestamp)); record.set_observed_timestamp(SystemTime::now()); for (k, v) in &event.keys { - record.add_attribute(k.name(), build_any_value(v)); + record.add_attribute(k.as_str(), build_any_value(v)); } record } @@ -235,7 +235,7 @@ fn build_key_value(key_value: &(trc::Key, trc::Value)) -> Option { } fn build_key(key: &trc::Key) -> Key { - Key::from_static_str(key.name()) + Key::from_static_str(key.as_str()) } fn build_any_value(value: &trc::Value) -> AnyValue { diff --git a/crates/directory/src/backend/oidc/mod.rs b/crates/directory/src/backend/oidc/mod.rs index 4fcf8779..a8eb4568 100644 --- a/crates/directory/src/backend/oidc/mod.rs +++ b/crates/directory/src/backend/oidc/mod.rs @@ -6,7 +6,7 @@ use ahash::AHashMap; use jsonwebtoken::{Algorithm, DecodingKey}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::{fmt, sync::Arc, time::Instant}; use tokio::sync::RwLock; use utils::Client; @@ -24,14 +24,19 @@ pub struct OpenIdConfig { pub default_domain: Option, } -#[derive(Deserialize)] +#[derive(Deserialize, Serialize)] pub struct DiscoveryDocument { - issuer: String, - jwks_uri: String, + pub issuer: String, + pub jwks_uri: String, pub userinfo_endpoint: String, + pub token_endpoint: String, pub authorization_endpoint: String, - scopes_supported: Option>, - claims_supported: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub end_session_endpoint: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub scopes_supported: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub claims_supported: Option>, } struct CachedKey { diff --git a/crates/directory/src/core/dispatch.rs b/crates/directory/src/core/dispatch.rs index 59823c53..1b8c028f 100644 --- a/crates/directory/src/core/dispatch.rs +++ b/crates/directory/src/core/dispatch.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use crate::{Account, Credentials, Directory, Recipient}; +use crate::{Account, Credentials, Directory, Recipient, backend::oidc::DiscoveryDocument}; use trc::AddContext; impl Directory { @@ -34,11 +34,9 @@ impl Directory { !matches!(self, Directory::OpenId(_)) } - pub fn oidc_authorization_endpoint(&self) -> Option { + pub fn oidc_discovery_document(&self) -> Option<&DiscoveryDocument> { match &self { - Directory::OpenId(directory) => { - Some(directory.discovery.authorization_endpoint.clone()) - } + Directory::OpenId(directory) => Some(&directory.discovery), _ => None, } } diff --git a/crates/http/src/management/diagnose.rs b/crates/http/src/api/diagnose.rs similarity index 100% rename from crates/http/src/management/diagnose.rs rename to crates/http/src/api/diagnose.rs diff --git a/crates/http/src/management/mod.rs b/crates/http/src/api/mod.rs similarity index 66% rename from crates/http/src/management/mod.rs rename to crates/http/src/api/mod.rs index 0edf91ee..0051be2d 100644 --- a/crates/http/src/management/mod.rs +++ b/crates/http/src/api/mod.rs @@ -12,7 +12,13 @@ pub mod telemetry; // SPDX-SnippetEnd pub mod diagnose; -use crate::management::diagnose::{DeliveryStage, spawn_delivery_diagnose}; +use crate::{ + api::diagnose::{DeliveryStage, spawn_delivery_diagnose}, + auth::{ + authenticate::Authenticator, oauth::auth::OAuthApiHandler, + permissions::PermissionsApiHandler, + }, +}; use common::{ Server, auth::{AccessToken, oauth::GrantType}, @@ -31,27 +37,61 @@ use std::time::Duration; use utils::url_params::UrlParams; pub trait ManagementApi: Sync + Send { - fn handle_api_manage_request( + fn handle_api_request( &self, req: &mut HttpRequest, - access_token: &AccessToken, session: &HttpSessionData, ) -> impl Future> + Send; + + fn management_access_token( + &self, + req: &HttpRequest, + session: &HttpSessionData, + ) -> impl Future> + Send; } impl ManagementApi for Server { #[allow(unused_variables)] - async fn handle_api_manage_request( + async fn handle_api_request( &self, req: &mut HttpRequest, - access_token: &AccessToken, session: &HttpSessionData, ) -> trc::Result { - let body = fetch_body(req, 1024 * 1024, session.session_id).await; + let is_post = req.method() == Method::POST; + let body = if is_post { + fetch_body(req, 1024 * 1024, session.session_id).await + } else { + None + }; let path = req.uri().path().split('/').skip(2).collect::>(); match path.first().copied().unwrap_or_default() { + "auth" if is_post => { + self.is_http_anonymous_request_allowed(session.remote_ip) + .await?; + self.handle_login_request( + session, + body.ok_or_else(|| trc::LimitEvent::SizeRequest.into_err())?, + ) + .await + } + "discover" => { + if let Some(email) = path.get(2).copied() { + self.is_http_anonymous_request_allowed(session.remote_ip) + .await?; + self.handle_discover_request(req, session, decode_path_element(email).as_ref()) + .await + } else { + Err(trc::ResourceEvent::NotFound.into_err()) + } + } + "permissions" => { + // Authenticate request + let (_in_flight, access_token) = self.authenticate_headers(req, session).await?; + self.handle_permissions_request(&access_token).await + } "token" => { + let access_token = self.management_access_token(req, session).await?; let account_id = access_token.account_id(); match path.get(1).copied() { // SPDX-SnippetBegin @@ -98,6 +138,7 @@ impl ManagementApi for Server { } } "live" => { + let access_token = self.management_access_token(req, session).await?; let params = UrlParams::new(req.uri().query()); let account_id = access_token.account_id(); @@ -140,16 +181,16 @@ impl ManagementApi for Server { // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] ("traces", _, &Method::GET) if self.core.is_enterprise_edition() => { - use crate::management::telemetry::TelemetryApi; + use crate::api::telemetry::TelemetryApi; - self.handle_telemetry_api_request(req, true, access_token) + self.handle_telemetry_api_request(req, true, &access_token) .await } #[cfg(feature = "enterprise")] ("metrics", _, &Method::GET) if self.core.is_enterprise_edition() => { - use crate::management::telemetry::TelemetryApi; + use crate::api::telemetry::TelemetryApi; - self.handle_telemetry_api_request(req, false, access_token) + self.handle_telemetry_api_request(req, false, &access_token) .await } // SPDX-SnippetEnd @@ -160,10 +201,55 @@ impl ManagementApi for Server { _ => Err(trc::ResourceEvent::NotFound.into_err()), } } - _ => Err(trc::ResourceEvent::NotFound.into_err()), } } + + async fn management_access_token( + &self, + req: &HttpRequest, + session: &HttpSessionData, + ) -> trc::Result { + let params = UrlParams::new(req.uri().query()); + if let Some(token) = params.get("token") { + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL + #[cfg(feature = "enterprise")] + if self.core.is_enterprise_edition() { + let path = req.uri().path(); + let (grant_type, permissions) = if path.starts_with("/api/telemetry/traces") { + (GrantType::LiveTracing, Permission::LiveTracing) + } else if path.starts_with("/api/telemetry/metrics") { + (GrantType::LiveMetrics, Permission::LiveMetrics) + } else if path.starts_with("/api/diagnose") { + (GrantType::Diagnose, Permission::LiveDeliveryTest) + } else { + return Err(trc::ResourceEvent::NotFound.into_err()); + }; + self.validate_access_token(grant_type.into(), token) + .await + .map(|token_info| { + AccessToken::from_permissions(token_info.account_id, [permissions]) + }) + } else { + self.authenticate_headers(req, session) + .await + .map(|(_, token)| token) + } + // SPDX-SnippetEnd + #[cfg(not(feature = "enterprise"))] + { + self.authenticate_headers(req, session) + .await + .map(|(_, token)| token) + } + } else { + self.authenticate_headers(req, session) + .await + .map(|(_, token)| token) + } + } } pub trait ToManageHttpResponse { diff --git a/crates/http/src/management/telemetry.rs b/crates/http/src/api/telemetry.rs similarity index 98% rename from crates/http/src/management/telemetry.rs rename to crates/http/src/api/telemetry.rs index f5f33ce5..8f080bd4 100644 --- a/crates/http/src/management/telemetry.rs +++ b/crates/http/src/api/telemetry.rs @@ -57,7 +57,7 @@ impl TelemetryApi for Server { for (key, value) in params.into_inner() { if key == "filter" { filter = value.into_owned().into(); - } else if let Some(key) = Key::try_parse(key.to_ascii_lowercase().as_str()) { + } else if let Some(key) = Key::parse(key.to_ascii_lowercase().as_str()) { key_filters.insert(key, value.into_owned()); } } @@ -152,8 +152,7 @@ impl TelemetryApi for Server { "event: trace\ndata: {}\n\n", serde_json::to_string( &JsonEventSerializer::new(std::mem::take(&mut events)) - .with_description() - .with_explanation()).unwrap_or_default() + .with_description()).unwrap_or_default() )))); ping_interval diff --git a/crates/http/src/auth/mod.rs b/crates/http/src/auth/mod.rs index 14f93b89..b6089ed1 100644 --- a/crates/http/src/auth/mod.rs +++ b/crates/http/src/auth/mod.rs @@ -6,3 +6,4 @@ pub mod authenticate; pub mod oauth; +pub mod permissions; diff --git a/crates/http/src/auth/oauth/auth.rs b/crates/http/src/auth/oauth/auth.rs index a2bf3405..e14a752a 100644 --- a/crates/http/src/auth/oauth/auth.rs +++ b/crates/http/src/auth/oauth/auth.rs @@ -5,7 +5,7 @@ */ use super::{DeviceAuthResponse, FormData, MAX_POST_LEN, OAuthCode}; -use crate::auth::oauth::OAuthStatus; +use crate::auth::oauth::{OAuthStatus, openid::OpenIdHandler}; use common::{ KV_OAUTH, Server, auth::{ @@ -46,22 +46,29 @@ pub struct OAuthMetadata { } pub trait OAuthApiHandler: Sync + Send { + fn handle_discover_request( + &self, + req: &HttpRequest, + session: &HttpSessionData, + account_name: &str, + ) -> impl Future> + Send; + fn handle_login_request( &self, - session: HttpSessionData, + session: &HttpSessionData, body: Vec, ) -> impl Future> + Send; fn handle_device_auth( &self, req: &mut HttpRequest, - session: HttpSessionData, + session: &HttpSessionData, ) -> impl Future> + Send; fn handle_oauth_metadata( &self, req: HttpRequest, - session: HttpSessionData, + session: &HttpSessionData, ) -> impl Future> + Send; } @@ -69,9 +76,6 @@ pub trait OAuthApiHandler: Sync + Send { #[serde(tag = "type")] #[serde(rename_all = "camelCase")] pub enum LoginRequest { - Discovery { - account_name: String, - }, AuthCode { account_name: String, account_secret: String, @@ -83,6 +87,14 @@ pub enum LoginRequest { redirect_uri: Option, #[serde(default)] nonce: Option, + #[serde(default)] + scope: Option, + #[serde(default)] + code_challenge: Option, + #[serde(default)] + code_challenge_method: Option, + #[serde(default)] + state: Option, }, AuthDevice { account_name: String, @@ -98,8 +110,6 @@ pub enum LoginRequest { #[serde(tag = "type")] #[serde(rename_all = "camelCase")] pub enum LoginResponse { - Local, - External { endpoint: String }, Authenticated { client_code: String }, Verified, MfaRequired, @@ -107,9 +117,28 @@ pub enum LoginResponse { } impl OAuthApiHandler for Server { + async fn handle_discover_request( + &self, + req: &HttpRequest, + session: &HttpSessionData, + account_name: &str, + ) -> trc::Result { + let account_name = account_name.trim().to_lowercase(); + if let Some(domain_name) = account_name.try_domain_part() + && let Some(endpoint) = self + .get_directory_for_domain(domain_name) + .await? + .and_then(|directory| directory.oidc_discovery_document()) + { + Ok(JsonResponse::new(endpoint).no_cache().into_http_response()) + } else { + self.handle_oidc_metadata(req, session).await + } + } + async fn handle_login_request( &self, - session: HttpSessionData, + session: &HttpSessionData, body: Vec, ) -> trc::Result { let request = serde_json::from_slice::(&body).map_err(|err| { @@ -117,22 +146,6 @@ impl OAuthApiHandler for Server { })?; let response = match request { - LoginRequest::Discovery { account_name } => { - let account_name = account_name.trim().to_lowercase(); - if let Some(domain_name) = account_name.try_domain_part() { - if let Some(endpoint) = self - .get_directory_for_domain(domain_name) - .await? - .and_then(|directory| directory.oidc_authorization_endpoint()) - { - LoginResponse::External { endpoint } - } else { - LoginResponse::Local - } - } else { - LoginResponse::Local - } - } LoginRequest::AuthCode { account_name, account_secret, @@ -140,6 +153,7 @@ impl OAuthApiHandler for Server { client_id, redirect_uri, nonce, + .. } => { // Validate clientId if client_id.len() > CLIENT_ID_MAX_LEN { @@ -315,7 +329,7 @@ impl OAuthApiHandler for Server { async fn handle_device_auth( &self, req: &mut HttpRequest, - session: HttpSessionData, + session: &HttpSessionData, ) -> trc::Result { // Parse form let mut form_data = FormData::from_request(req, MAX_POST_LEN, session.session_id).await?; @@ -379,10 +393,10 @@ impl OAuthApiHandler for Server { .await?; // Build response - let base_url = HttpContext::new(&session, req).resolve_response_url(self); + let base_url = HttpContext::new(session, req).resolve_response_url(self); Ok(JsonResponse::new(DeviceAuthResponse { - verification_uri: format!("{base_url}/authorize"), - verification_uri_complete: format!("{base_url}/authorize/?code={user_code}"), + verification_uri: format!("{base_url}/device"), + verification_uri_complete: format!("{base_url}/device/?code={user_code}"), device_code, user_code, expires_in: self.core.oauth.oauth_expiry_user_code, @@ -395,12 +409,12 @@ impl OAuthApiHandler for Server { async fn handle_oauth_metadata( &self, req: HttpRequest, - session: HttpSessionData, + session: &HttpSessionData, ) -> trc::Result { - let base_url = HttpContext::new(&session, &req).resolve_response_url(self); + let base_url = HttpContext::new(session, &req).resolve_response_url(self); Ok(JsonResponse::new(OAuthMetadata { - authorization_endpoint: format!("{base_url}/authorize/code",), + authorization_endpoint: format!("{base_url}/login",), token_endpoint: format!("{base_url}/auth/token"), device_authorization_endpoint: format!("{base_url}/auth/device"), introspection_endpoint: format!("{base_url}/auth/introspect"), diff --git a/crates/http/src/auth/oauth/openid.rs b/crates/http/src/auth/oauth/openid.rs index eb3aff76..a11d36f1 100644 --- a/crates/http/src/auth/oauth/openid.rs +++ b/crates/http/src/auth/oauth/openid.rs @@ -34,8 +34,8 @@ pub trait OpenIdHandler: Sync + Send { fn handle_oidc_metadata( &self, - req: HttpRequest, - session: HttpSessionData, + req: &HttpRequest, + session: &HttpSessionData, ) -> impl Future> + Send; } @@ -57,10 +57,10 @@ impl OpenIdHandler for Server { async fn handle_oidc_metadata( &self, - req: HttpRequest, - session: HttpSessionData, + req: &HttpRequest, + session: &HttpSessionData, ) -> trc::Result { - let base_url = HttpContext::new(&session, &req).resolve_response_url(self); + let base_url = HttpContext::new(session, req).resolve_response_url(self); Ok(JsonResponse::new(OpenIdMetadata { authorization_endpoint: format!("{base_url}/authorize/code",), diff --git a/crates/http/src/auth/permissions.rs b/crates/http/src/auth/permissions.rs new file mode 100644 index 00000000..d7c81a83 --- /dev/null +++ b/crates/http/src/auth/permissions.rs @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL + */ + +use common::{Server, auth::AccessToken}; +use http_proto::{HttpResponse, JsonResponse, ToHttpResponse}; +use registry::schema::enums::Permission; +use serde::Serialize; + +#[derive(Debug, Clone, Serialize)] +pub struct Permissions { + pub permissions: Vec, + #[serde(rename = "isEnterprise")] + pub is_enterprise: bool, +} + +pub trait PermissionsApiHandler: Sync + Send { + fn handle_permissions_request( + &self, + access_token: &AccessToken, + ) -> impl Future> + Send; +} + +impl PermissionsApiHandler for Server { + async fn handle_permissions_request( + &self, + access_token: &AccessToken, + ) -> trc::Result { + #[cfg(not(feature = "enterprise"))] + let is_enterprise = false; + + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL + #[cfg(feature = "enterprise")] + let is_enterprise = self.core.is_enterprise_edition(); + // SPDX-SnippetEnd + + Ok(JsonResponse::new(Permissions { + permissions: access_token.permissions(), + is_enterprise, + }) + .into_http_response()) + } +} diff --git a/crates/http/src/lib.rs b/crates/http/src/lib.rs index f7a0dccf..c76d33c4 100644 --- a/crates/http/src/lib.rs +++ b/crates/http/src/lib.rs @@ -4,14 +4,13 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +pub mod api; pub mod auth; pub mod form; -pub mod management; pub mod request; -use std::sync::Arc; - use common::Inner; +use std::sync::Arc; #[derive(Clone)] pub struct HttpSessionManager { diff --git a/crates/http/src/request.rs b/crates/http/src/request.rs index 281ba64c..aacb044b 100644 --- a/crates/http/src/request.rs +++ b/crates/http/src/request.rs @@ -6,6 +6,7 @@ use crate::{ HttpSessionManager, + api::{ManagementApi, ToManageHttpResponse}, auth::{ authenticate::{Authenticator, HttpHeaders}, oauth::{ @@ -14,11 +15,9 @@ use crate::{ }, }, form::FormHandler, - management::{ManagementApi, ToManageHttpResponse}, }; use common::{ BuildServer, Inner, KV_ACME, Server, - auth::{AccessToken, oauth::GrantType}, ipc::PushEvent, manager::application::Resource, network::{SessionData, SessionManager, SessionStream}, @@ -50,7 +49,6 @@ use std::{net::IpAddr, str::FromStr, sync::Arc}; use store::dispatch::lookup::KeyValue; use trc::SecurityEvent; use types::{blob::BlobId, id::Id}; -use utils::url_params::UrlParams; pub trait ParseHttp: Sync + Send { fn parse_http_request( @@ -273,14 +271,14 @@ impl ParseHttp for Server { self.is_http_anonymous_request_allowed(session.remote_ip) .await?; - return self.handle_oauth_metadata(req, session).await; + return self.handle_oauth_metadata(req, &session).await; } ("openid-configuration", &Method::GET) => { // Limit anonymous requests self.is_http_anonymous_request_allowed(session.remote_ip) .await?; - return self.handle_oidc_metadata(req, session).await; + return self.handle_oidc_metadata(&req, &session).await; } ("acme-challenge", &Method::GET) if self.has_acme_http_providers() => { if let Some(token) = path.next() { @@ -347,21 +345,11 @@ impl ParseHttp for Server { _ => (), }, "auth" => match (path.next().unwrap_or_default(), req.method()) { - ("login", &Method::POST) => { - self.is_http_anonymous_request_allowed(session.remote_ip) - .await?; - - let bytes = fetch_body(&mut req, 4096, session.session_id) - .await - .ok_or_else(|| trc::LimitEvent::SizeRequest.into_err())?; - - return self.handle_login_request(session, bytes).await; - } ("device", &Method::POST) => { self.is_http_anonymous_request_allowed(session.remote_ip) .await?; - return self.handle_device_auth(&mut req, session).await; + return self.handle_device_auth(&mut req, &session).await; } ("token", &Method::POST) => { self.is_http_anonymous_request_allowed(session.remote_ip) @@ -410,45 +398,7 @@ impl ParseHttp for Server { return Ok(JsonProblemResponse(StatusCode::NO_CONTENT).into_http_response()); } - let params = UrlParams::new(req.uri().query()); - let access_token = if let Some(token) = params.get("token") { - // SPDX-SnippetBegin - // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC - // SPDX-License-Identifier: LicenseRef-SEL - #[cfg(feature = "enterprise")] - if self.core.is_enterprise_edition() { - let path = req.uri().path(); - let (grant_type, permissions) = if path.starts_with("/api/telemetry/traces") - { - (GrantType::LiveTracing, Permission::LiveTracing) - } else if path.starts_with("/api/telemetry/metrics") { - (GrantType::LiveMetrics, Permission::LiveMetrics) - } else if path.starts_with("/api/diagnose") { - (GrantType::Diagnose, Permission::LiveDeliveryTest) - } else { - return Err(trc::ResourceEvent::NotFound.into_err()); - }; - AccessToken::from_permissions( - self.validate_access_token(grant_type.into(), token) - .await? - .account_id, - [permissions], - ) - } else { - self.authenticate_headers(&req, &session).await?.1 - } - // SPDX-SnippetEnd - #[cfg(not(feature = "enterprise"))] - { - self.authenticate_headers(&req, &session).await?.1 - } - } else { - self.authenticate_headers(&req, &session).await?.1 - }; - - return self - .handle_api_manage_request(&mut req, &access_token, &session) - .await; + return self.handle_api_request(&mut req, &session).await; } "mail" => { if req.method() == Method::GET @@ -593,7 +543,7 @@ impl ParseHttp for Server { // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] - "logo.svg" if self.is_enterprise_edition() => { + "logo" if self.is_enterprise_edition() => { match self .logo_resource( req.headers() @@ -607,17 +557,13 @@ impl ParseHttp for Server { Ok(Some(resource)) => { return Ok(resource.into_http_response()); } - Ok(None) => (), + Ok(None) => { + return Err(trc::ResourceEvent::NotFound.into_err()); + } Err(err) => { trc::error!(err.span_id(session.session_id)); } } - - let resource = self.inner.data.applications.get("logo.svg").await?; - - if !resource.is_empty() { - return Ok(resource.into_http_response()); - } } // SPDX-SnippetEnd "form" => { @@ -642,6 +588,14 @@ impl ParseHttp for Server { } } } + "login" | "device" => { + let page = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../resources/html-templates/login.html.min" + )); + + return Ok(HtmlResponse::new(page.to_string()).into_http_response()); + } _ => { let path = req.uri().path(); let resource = self diff --git a/crates/registry/src/jmap/patch.rs b/crates/registry/src/jmap/patch.rs index 89906f90..9b5731b3 100644 --- a/crates/registry/src/jmap/patch.rs +++ b/crates/registry/src/jmap/patch.rs @@ -273,7 +273,7 @@ impl RegistryJsonPatch for trc::Key { pointer: JsonPointerPatch<'_>, value: super::JmapValue<'x>, ) -> PatchResult<'x> { - if let Some(new_value) = value.as_str().and_then(|v| trc::Key::try_parse(v.as_ref())) { + if let Some(new_value) = value.as_str().and_then(|v| trc::Key::parse(v.as_ref())) { *self = new_value; pointer.assert_eof() } else { diff --git a/crates/registry/src/jmap/ser.rs b/crates/registry/src/jmap/ser.rs index bef3bb10..3cdb64d0 100644 --- a/crates/registry/src/jmap/ser.rs +++ b/crates/registry/src/jmap/ser.rs @@ -86,6 +86,6 @@ impl IntoValue for VecMap { impl IntoValue for trc::Key { fn into_value(self) -> JmapValue<'static> { - JmapValue::Str(self.name().into()) + JmapValue::Str(self.as_str().into()) } } diff --git a/crates/registry/src/pickle.rs b/crates/registry/src/pickle.rs index ce5c8a70..e002fb30 100644 --- a/crates/registry/src/pickle.rs +++ b/crates/registry/src/pickle.rs @@ -220,10 +220,10 @@ where impl Pickle for trc::Key { fn pickle(&self, out: &mut Vec) { - self.code().pickle(out); + self.to_id().pickle(out); } fn unpickle(stream: &mut PickledStream<'_>) -> Option { - u64::unpickle(stream).and_then(Self::from_code) + u16::unpickle(stream).and_then(Self::from_id) } } diff --git a/crates/trc/src/event/conv.rs b/crates/trc/src/event/conv.rs index ecd1afda..cb5b25e8 100644 --- a/crates/trc/src/event/conv.rs +++ b/crates/trc/src/event/conv.rs @@ -436,6 +436,6 @@ impl FromStr for Key { type Err = (); fn from_str(s: &str) -> std::result::Result { - Key::try_parse(s).ok_or(()) + Key::parse(s).ok_or(()) } } diff --git a/crates/trc/src/event/enums.rs b/crates/trc/src/event/enums.rs index aa08118e..d09401e2 100644 --- a/crates/trc/src/event/enums.rs +++ b/crates/trc/src/event/enums.rs @@ -1215,3 +1215,75 @@ pub enum MetricType { TlsHandshakeError = 336, UserCount = 25, } + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)] +#[repr(u16)] +pub enum Key { + #[default] + AccountName = 0, + AccountId = 1, + BlobId = 2, + CausedBy = 3, + ChangeId = 4, + Code = 5, + Collection = 6, + Contents = 7, + Details = 8, + DkimFail = 9, + DkimNone = 10, + DkimPass = 11, + DmarcNone = 12, + DmarcPass = 13, + DmarcQuarantine = 14, + DmarcReject = 15, + DocumentId = 16, + Domain = 17, + Due = 18, + Elapsed = 19, + Expires = 20, + From = 21, + Hostname = 22, + Id = 23, + Key = 24, + Limit = 25, + ListenerId = 26, + LocalIp = 27, + LocalPort = 28, + MailboxName = 29, + MailboxId = 30, + MessageId = 31, + NextDsn = 32, + NextRetry = 33, + Path = 34, + Policy = 35, + QueueId = 36, + RangeFrom = 37, + RangeTo = 38, + Reason = 39, + RemoteIp = 40, + RemotePort = 41, + ReportId = 42, + Result = 43, + Size = 44, + Source = 45, + SpanId = 46, + SpfFail = 47, + SpfNone = 48, + SpfPass = 49, + Strict = 50, + Tls = 51, + To = 52, + Total = 53, + TotalFailures = 54, + TotalSuccesses = 55, + Type = 56, + Uid = 57, + UidNext = 58, + UidValidity = 59, + Url = 60, + ValidFrom = 61, + ValidTo = 62, + Value = 63, + Version = 64, + QueueName = 65, +} diff --git a/crates/trc/src/event/enums_impl.rs b/crates/trc/src/event/enums_impl.rs index 3baddfd5..70c5aaf3 100644 --- a/crates/trc/src/event/enums_impl.rs +++ b/crates/trc/src/event/enums_impl.rs @@ -6,7 +6,8 @@ // This file is auto-generated. Do not edit directly. -use crate::{event::enums::*, Level}; +use crate::{Level, event::enums::*}; +use std::borrow::Cow; impl EventType { pub fn parse(value: &str) -> Option { @@ -3700,1144 +3701,14 @@ impl EventType { } } - pub fn explain(&self) -> &'static str { - match self { - EventType::Acme(AcmeEvent::AuthStart) => "ACME authentication has started", - EventType::Acme(AcmeEvent::AuthPending) => "ACME authentication is pending", - EventType::Acme(AcmeEvent::AuthValid) => "ACME authentication is valid", - EventType::Acme(AcmeEvent::AuthCompleted) => "ACME authentication has completed", - EventType::Acme(AcmeEvent::AuthError) => "An error occurred with ACME authentication", - EventType::Acme(AcmeEvent::AuthTooManyAttempts) => { - "Too many ACME authentication attempts" - } - EventType::Acme(AcmeEvent::ProcessCert) => "Processing the ACME certificate", - EventType::Acme(AcmeEvent::OrderStart) => "ACME order has started", - EventType::Acme(AcmeEvent::OrderProcessing) => "ACME order is processing", - EventType::Acme(AcmeEvent::OrderCompleted) => "ACME order has completed", - EventType::Acme(AcmeEvent::OrderReady) => "ACME order is ready", - EventType::Acme(AcmeEvent::OrderValid) => "ACME order is valid", - EventType::Acme(AcmeEvent::OrderInvalid) => "ACME order is invalid", - EventType::Acme(AcmeEvent::RenewBackoff) => "ACME renew backoff", - EventType::Acme(AcmeEvent::ClientSuppliedSni) => "ACME client supplied SNI", - EventType::Acme(AcmeEvent::ClientMissingSni) => "ACME client missing SNI", - EventType::Acme(AcmeEvent::TlsAlpnReceived) => "ACME TLS ALPN received", - EventType::Acme(AcmeEvent::TlsAlpnError) => "ACME TLS ALPN error", - EventType::Acme(AcmeEvent::TokenNotFound) => "ACME token not found", - EventType::Acme(AcmeEvent::Error) => "An error occurred with ACME", - EventType::Ai(AiEvent::LlmResponse) => "An LLM response has been received", - EventType::Ai(AiEvent::ApiError) => "An AI API error occurred", - EventType::Arc(ArcEvent::ChainTooLong) => "The ARC chain is too long", - EventType::Arc(ArcEvent::InvalidInstance) => "The ARC instance is invalid", - EventType::Arc(ArcEvent::InvalidCv) => "The ARC CV is invalid", - EventType::Arc(ArcEvent::HasHeaderTag) => "The ARC has a header tag", - EventType::Arc(ArcEvent::BrokenChain) => "The ARC chain is broken", - EventType::Arc(ArcEvent::SealerNotFound) => "The ARC sealer was not found", - EventType::Auth(AuthEvent::Success) => "Successful authentication", - EventType::Auth(AuthEvent::Failed) => "Failed authentication", - EventType::Auth(AuthEvent::TokenExpired) => "OAuth authentication token has expired", - EventType::Auth(AuthEvent::MfaRequired) => { - "MFA token is required for authentication but was not provided" - } - EventType::Auth(AuthEvent::TooManyAttempts) => { - "Too many authentication attempts have been made" - } - EventType::Auth(AuthEvent::ClientRegistration) => { - "OAuth client successfully registered" - } - EventType::Auth(AuthEvent::Error) => "An error occurred with authentication", - EventType::Auth(AuthEvent::Warning) => "A warning occurred with authentication", - EventType::Auth(AuthEvent::CredentialExpired) => "A credential has expired", - EventType::Calendar(CalendarEvent::RuleExpansionError) => { - "An error occurred while expanding calendar recurrences" - } - EventType::Calendar(CalendarEvent::AlarmSent) => { - "A calendar alarm has been sent to the recipient" - } - EventType::Calendar(CalendarEvent::AlarmSkipped) => "A calendar alarm was skipped", - EventType::Calendar(CalendarEvent::AlarmRecipientOverride) => { - "A calendar alarm recipient was overridden" - } - EventType::Calendar(CalendarEvent::AlarmFailed) => { - "A calendar alarm could not be sent to the recipient" - } - EventType::Calendar(CalendarEvent::ItipMessageSent) => { - "A calendar iTIP message has been sent" - } - EventType::Calendar(CalendarEvent::ItipMessageReceived) => { - "A calendar iTIP/iMIP message has been received" - } - EventType::Calendar(CalendarEvent::ItipMessageError) => { - "An error occurred while processing an iTIP/iMIP message" - } - EventType::Cluster(ClusterEvent::Startup) => { - "The clustering subsystem has been initialized" - } - EventType::Cluster(ClusterEvent::SubscriberStart) => { - "The PubSub subscriber has started" - } - EventType::Cluster(ClusterEvent::SubscriberStop) => "The PubSub subscriber has stopped", - EventType::Cluster(ClusterEvent::SubscriberError) => { - "An error occurred while subscribing to PubSub" - } - EventType::Cluster(ClusterEvent::SubscriberDisconnected) => { - "The PubSub subscriber has disconnected" - } - EventType::Cluster(ClusterEvent::PublisherStart) => "The PubSub publisher has started", - EventType::Cluster(ClusterEvent::PublisherStop) => "The PubSub publisher has stopped", - EventType::Cluster(ClusterEvent::PublisherError) => { - "An error occurred while publishing to PubSub" - } - EventType::Cluster(ClusterEvent::MessageReceived) => { - "A message was received from the PubSub server" - } - EventType::Cluster(ClusterEvent::MessageSkipped) => { - "A message originating from this node was skipped" - } - EventType::Cluster(ClusterEvent::MessageInvalid) => { - "An invalid message was received from the PubSub server" - } - EventType::Cluster(ClusterEvent::NodeIdRenewed) => "The node ID lease has been renewed", - EventType::Dane(DaneEvent::AuthenticationSuccess) => "Successful DANE authentication", - EventType::Dane(DaneEvent::AuthenticationFailure) => "Failed DANE authentication", - EventType::Dane(DaneEvent::NoCertificatesFound) => { - "No certificates were found for DANE" - } - EventType::Dane(DaneEvent::CertificateParseError) => { - "An error occurred while parsing the certificate" - } - EventType::Dane(DaneEvent::TlsaRecordMatch) => "A TLSA record match was found", - EventType::Dane(DaneEvent::TlsaRecordFetch) => "The TLSA record has been fetched", - EventType::Dane(DaneEvent::TlsaRecordFetchError) => { - "An error occurred while fetching the TLSA record" - } - EventType::Dane(DaneEvent::TlsaRecordNotFound) => "The TLSA record was not found", - EventType::Dane(DaneEvent::TlsaRecordNotDnssecSigned) => { - "The TLSA record is not DNSSEC signed" - } - EventType::Dane(DaneEvent::TlsaRecordInvalid) => "The TLSA record is invalid", - EventType::Delivery(DeliveryEvent::AttemptStart) => { - "A new delivery attempt for the message has started" - } - EventType::Delivery(DeliveryEvent::AttemptEnd) => "The delivery attempt has ended", - EventType::Delivery(DeliveryEvent::Completed) => { - "Delivery was completed for all recipients" - } - EventType::Delivery(DeliveryEvent::Failed) => { - "Message delivery failed due to a temporary error" - } - EventType::Delivery(DeliveryEvent::DomainDeliveryStart) => { - "A new delivery attempt for a domain has started" - } - EventType::Delivery(DeliveryEvent::MxLookup) => "Looking up MX records for the domain", - EventType::Delivery(DeliveryEvent::MxLookupFailed) => { - "Failed to look up MX records for the domain" - } - EventType::Delivery(DeliveryEvent::IpLookup) => "Looking up IP address for the domain", - EventType::Delivery(DeliveryEvent::IpLookupFailed) => { - "Failed to look up IP address for the domain" - } - EventType::Delivery(DeliveryEvent::NullMx) => { - "The domain has a null MX record, delivery is impossible" - } - EventType::Delivery(DeliveryEvent::Connect) => "Connecting to the remote server", - EventType::Delivery(DeliveryEvent::ConnectError) => { - "Error connecting to the remote server" - } - EventType::Delivery(DeliveryEvent::MissingOutboundHostname) => { - "The outbound hostname is missing in the configuration" - } - EventType::Delivery(DeliveryEvent::GreetingFailed) => { - "Failed to read the SMTP greeting from the remote server" - } - EventType::Delivery(DeliveryEvent::Ehlo) => { - "The EHLO command was sent to the remote server" - } - EventType::Delivery(DeliveryEvent::EhloRejected) => { - "The remote server rejected the EHLO command" - } - EventType::Delivery(DeliveryEvent::Auth) => "Authenticating with the remote server", - EventType::Delivery(DeliveryEvent::AuthFailed) => { - "Authentication with the remote server failed" - } - EventType::Delivery(DeliveryEvent::MailFrom) => { - "The MAIL FROM command was sent to the remote server" - } - EventType::Delivery(DeliveryEvent::MailFromRejected) => { - "The remote server rejected the MAIL FROM command" - } - EventType::Delivery(DeliveryEvent::Delivered) => { - "The message was delivered to the recipient" - } - EventType::Delivery(DeliveryEvent::RcptTo) => { - "The RCPT TO command was sent to the remote server" - } - EventType::Delivery(DeliveryEvent::RcptToRejected) => { - "The remote server rejected the RCPT TO command" - } - EventType::Delivery(DeliveryEvent::RcptToFailed) => { - "Failed to send the RCPT TO command to the remote server" - } - EventType::Delivery(DeliveryEvent::MessageRejected) => { - "The remote server rejected the message" - } - EventType::Delivery(DeliveryEvent::StartTls) => { - "Requesting a TLS connection with the remote server" - } - EventType::Delivery(DeliveryEvent::StartTlsUnavailable) => { - "The remote server does not support STARTTLS" - } - EventType::Delivery(DeliveryEvent::StartTlsError) => { - "It was not possible to establish a TLS connection" - } - EventType::Delivery(DeliveryEvent::StartTlsDisabled) => { - "STARTTLS has been disabled in the configuration for this host" - } - EventType::Delivery(DeliveryEvent::ImplicitTlsError) => "Error starting implicit TLS", - EventType::Delivery(DeliveryEvent::ConcurrencyLimitExceeded) => { - "The concurrency limit was exceeded for the remote host" - } - EventType::Delivery(DeliveryEvent::RateLimitExceeded) => { - "The rate limit was exceeded for the remote host" - } - EventType::Delivery(DeliveryEvent::DoubleBounce) => { - "The message was discarded after a double bounce" - } - EventType::Delivery(DeliveryEvent::DsnSuccess) => { - "A success delivery status notification was created" - } - EventType::Delivery(DeliveryEvent::DsnTempFail) => { - "A temporary failure delivery status notification was created" - } - EventType::Delivery(DeliveryEvent::DsnPermFail) => { - "A permanent failure delivery status notification was created" - } - EventType::Delivery(DeliveryEvent::RawInput) => "Raw SMTP input received", - EventType::Delivery(DeliveryEvent::RawOutput) => "Raw SMTP output sent", - EventType::Dkim(DkimEvent::Pass) => "DKIM verification has passed", - EventType::Dkim(DkimEvent::Neutral) => "DKIM verification is neutral", - EventType::Dkim(DkimEvent::Fail) => "DKIM verification has failed", - EventType::Dkim(DkimEvent::PermError) => "A permanent error occurred with DKIM", - EventType::Dkim(DkimEvent::TempError) => "A temporary error occurred with DKIM", - EventType::Dkim(DkimEvent::None) => "No DKIM signature was found", - EventType::Dkim(DkimEvent::UnsupportedVersion) => "The DKIM version is unsupported", - EventType::Dkim(DkimEvent::UnsupportedAlgorithm) => "The DKIM algorithm is unsupported", - EventType::Dkim(DkimEvent::UnsupportedCanonicalization) => { - "The DKIM canonicalization is unsupported" - } - EventType::Dkim(DkimEvent::UnsupportedKeyType) => "The DKIM key type is unsupported", - EventType::Dkim(DkimEvent::FailedBodyHashMatch) => "The DKIM body hash does not match", - EventType::Dkim(DkimEvent::FailedVerification) => "The DKIM verification has failed", - EventType::Dkim(DkimEvent::FailedAuidMatch) => "The DKIM AUID does not match", - EventType::Dkim(DkimEvent::RevokedPublicKey) => "The DKIM public key has been revoked", - EventType::Dkim(DkimEvent::IncompatibleAlgorithms) => { - "The DKIM algorithms are incompatible" - } - EventType::Dkim(DkimEvent::SignatureExpired) => "The DKIM signature has expired", - EventType::Dkim(DkimEvent::SignatureLength) => "The DKIM signature length is incorrect", - EventType::Dkim(DkimEvent::SignerNotFound) => "The DKIM signer was not found", - EventType::Dkim(DkimEvent::BuildError) => { - "An error occurred while building DKIM signature" - } - EventType::Dkim(DkimEvent::SignatureCreated) => "A new DKIM signature has been created", - EventType::Dkim(DkimEvent::SignaturePublished) => { - "A DKIM signature has been published to DNS" - } - EventType::Dkim(DkimEvent::SignatureRetiring) => { - "A DKIM signature is retiring and will be removed from DNS soon" - } - EventType::Dkim(DkimEvent::SignatureRetired) => { - "A DKIM signature has been retired and removed from DNS" - } - EventType::Dkim(DkimEvent::SignatureDeleted) => { - "A DKIM signature has been deleted from the system" - } - EventType::Dmarc(DmarcEvent::Pass) => "The DMARC check has passed", - EventType::Dmarc(DmarcEvent::Fail) => "The DMARC check has failed", - EventType::Dmarc(DmarcEvent::PermError) => "A permanent error occurred with DMARC", - EventType::Dmarc(DmarcEvent::TempError) => "A temporary error occurred with DMARC", - EventType::Dmarc(DmarcEvent::None) => "No DMARC record was found", - EventType::Dns(DnsEvent::RecordCreated) => "DNS record has been created", - EventType::Dns(DnsEvent::RecordCreationFailed) => "Failed to create DNS record", - EventType::Dns(DnsEvent::RecordDeletionFailed) => "Failed to delete DNS record", - EventType::Dns(DnsEvent::RecordNotPropagated) => "DNS record has not propagated", - EventType::Dns(DnsEvent::RecordLookupFailed) => "Failed to look up DNS record", - EventType::Dns(DnsEvent::RecordPropagated) => "DNS record has propagated", - EventType::Dns(DnsEvent::RecordPropagationTimeout) => "DNS record propagation timeout", - EventType::Dns(DnsEvent::BuildError) => "An error occurred while building DNS updater", - EventType::Eval(EvalEvent::Result) => "The expression evaluation has a result", - EventType::Eval(EvalEvent::Error) => { - "An error occurred while evaluating the expression" - } - EventType::Eval(EvalEvent::DirectoryNotFound) => { - "The directory was not found while evaluating the expression" - } - EventType::Eval(EvalEvent::StoreNotFound) => { - "The store was not found while evaluating the expression" - } - EventType::Http(HttpEvent::ConnectionStart) => "An HTTP connection was started", - EventType::Http(HttpEvent::ConnectionEnd) => "An HTTP connection was ended", - EventType::Http(HttpEvent::Error) => "An error occurred during an HTTP request", - EventType::Http(HttpEvent::RequestUrl) => "The URL of an HTTP request", - EventType::Http(HttpEvent::RequestBody) => "The body of an HTTP request", - EventType::Http(HttpEvent::ResponseBody) => "The body of an HTTP response", - EventType::Http(HttpEvent::XForwardedMissing) => { - "The X-Forwarded-For header is missing" - } - EventType::Imap(ImapEvent::ConnectionStart) => "IMAP connection started", - EventType::Imap(ImapEvent::ConnectionEnd) => "IMAP connection ended", - EventType::Imap(ImapEvent::GetAcl) => "Client requested mailbox ACL", - EventType::Imap(ImapEvent::SetAcl) => "Client set mailbox ACL", - EventType::Imap(ImapEvent::MyRights) => "Client requested mailbox rights", - EventType::Imap(ImapEvent::ListRights) => "Client requested mailbox rights list", - EventType::Imap(ImapEvent::Append) => "Client appended a message to a mailbox", - EventType::Imap(ImapEvent::Capabilities) => "Client requested server capabilities", - EventType::Imap(ImapEvent::Id) => "Client sent an ID command", - EventType::Imap(ImapEvent::Close) => "Client closed a mailbox", - EventType::Imap(ImapEvent::Copy) => "Client copied messages between mailboxes", - EventType::Imap(ImapEvent::Move) => "Client moved messages between mailboxes", - EventType::Imap(ImapEvent::CreateMailbox) => "Client created a mailbox", - EventType::Imap(ImapEvent::DeleteMailbox) => "Client deleted a mailbox", - EventType::Imap(ImapEvent::RenameMailbox) => "Client renamed a mailbox", - EventType::Imap(ImapEvent::Enable) => "Client enabled an extension", - EventType::Imap(ImapEvent::Expunge) => "Client expunged messages", - EventType::Imap(ImapEvent::Fetch) => "Client fetched messages", - EventType::Imap(ImapEvent::IdleStart) => "Client started IDLE", - EventType::Imap(ImapEvent::IdleStop) => "Client stopped IDLE", - EventType::Imap(ImapEvent::List) => "Client listed mailboxes", - EventType::Imap(ImapEvent::Lsub) => "Client listed subscribed mailboxes", - EventType::Imap(ImapEvent::Logout) => "Client logged out", - EventType::Imap(ImapEvent::Namespace) => "Client requested namespace", - EventType::Imap(ImapEvent::Noop) => "Client sent a NOOP command", - EventType::Imap(ImapEvent::Search) => "Client searched for messages", - EventType::Imap(ImapEvent::Sort) => "Client sorted messages", - EventType::Imap(ImapEvent::Select) => "Client selected a mailbox", - EventType::Imap(ImapEvent::Status) => "Client requested mailbox status", - EventType::Imap(ImapEvent::Store) => "Client stored flags", - EventType::Imap(ImapEvent::Subscribe) => "Client subscribed to a mailbox", - EventType::Imap(ImapEvent::Unsubscribe) => "Client unsubscribed from a mailbox", - EventType::Imap(ImapEvent::Thread) => "Client requested message threads", - EventType::Imap(ImapEvent::GetQuota) => "Client requested mailbox quota", - EventType::Imap(ImapEvent::Error) => "An error occurred during an IMAP command", - EventType::Imap(ImapEvent::RawInput) => "Raw IMAP input received", - EventType::Imap(ImapEvent::RawOutput) => "Raw IMAP output sent", - EventType::IncomingReport(IncomingReportEvent::DmarcReport) => { - "A DMARC report has been received" - } - EventType::IncomingReport(IncomingReportEvent::DmarcReportWithWarnings) => { - "A DMARC report with warnings has been received" - } - EventType::IncomingReport(IncomingReportEvent::TlsReport) => { - "A TLS report has been received" - } - EventType::IncomingReport(IncomingReportEvent::TlsReportWithWarnings) => { - "A TLS report with warnings has been received" - } - EventType::IncomingReport(IncomingReportEvent::AbuseReport) => { - "An abuse report has been received" - } - EventType::IncomingReport(IncomingReportEvent::AuthFailureReport) => { - "An authentication failure report has been received" - } - EventType::IncomingReport(IncomingReportEvent::FraudReport) => { - "A fraud report has been received" - } - EventType::IncomingReport(IncomingReportEvent::NotSpamReport) => { - "A not spam report has been received" - } - EventType::IncomingReport(IncomingReportEvent::VirusReport) => { - "A virus report has been received" - } - EventType::IncomingReport(IncomingReportEvent::OtherReport) => { - "An unknown type of report has been received" - } - EventType::IncomingReport(IncomingReportEvent::MessageParseFailed) => { - "Failed to parse the incoming report message" - } - EventType::IncomingReport(IncomingReportEvent::DmarcParseFailed) => { - "Failed to parse the DMARC report" - } - EventType::IncomingReport(IncomingReportEvent::TlsRpcParseFailed) => { - "Failed to parse the TLS RPC report" - } - EventType::IncomingReport(IncomingReportEvent::ArfParseFailed) => { - "Failed to parse the ARF report" - } - EventType::IncomingReport(IncomingReportEvent::DecompressError) => { - "Error decompressing the report" - } - EventType::Iprev(IprevEvent::Pass) => "The IPREV check has passed", - EventType::Iprev(IprevEvent::Fail) => "The IPREV check has failed", - EventType::Iprev(IprevEvent::PermError) => "A permanent error occurred with IPREV", - EventType::Iprev(IprevEvent::TempError) => "A temporary error occurred with IPREV", - EventType::Iprev(IprevEvent::None) => "No IPREV record was found", - EventType::Jmap(JmapEvent::MethodCall) => "A JMAP method call has been made", - EventType::Jmap(JmapEvent::InvalidArguments) => "The JMAP arguments are invalid", - EventType::Jmap(JmapEvent::RequestTooLarge) => "The JMAP request is too large", - EventType::Jmap(JmapEvent::StateMismatch) => "The JMAP state is mismatched", - EventType::Jmap(JmapEvent::AnchorNotFound) => "The JMAP anchor was not found", - EventType::Jmap(JmapEvent::UnsupportedFilter) => "The JMAP filter is unsupported", - EventType::Jmap(JmapEvent::UnsupportedSort) => "The JMAP sort is unsupported", - EventType::Jmap(JmapEvent::UnknownMethod) => "The JMAP method is unknown", - EventType::Jmap(JmapEvent::InvalidResultReference) => { - "The JMAP result reference is invalid" - } - EventType::Jmap(JmapEvent::Forbidden) => "The JMAP operation is forbidden", - EventType::Jmap(JmapEvent::AccountNotFound) => "The JMAP account was not found", - EventType::Jmap(JmapEvent::AccountNotSupportedByMethod) => { - "The JMAP account is not supported by the method" - } - EventType::Jmap(JmapEvent::AccountReadOnly) => "The JMAP account is read-only", - EventType::Jmap(JmapEvent::NotFound) => "The JMAP resource was not found", - EventType::Jmap(JmapEvent::CannotCalculateChanges) => "Cannot calculate JMAP changes", - EventType::Jmap(JmapEvent::UnknownDataType) => "The JMAP data type is unknown", - EventType::Jmap(JmapEvent::UnknownCapability) => "The JMAP capability is unknown", - EventType::Jmap(JmapEvent::NotJson) => "The JMAP request is not JSON", - EventType::Jmap(JmapEvent::NotRequest) => "The JMAP input is not a request", - EventType::Jmap(JmapEvent::WebsocketStart) => { - "The JMAP WebSocket connection has started" - } - EventType::Jmap(JmapEvent::WebsocketStop) => { - "The JMAP WebSocket connection has stopped" - } - EventType::Jmap(JmapEvent::WebsocketError) => { - "An error occurred with the JMAP WebSocket connection" - } - EventType::Limit(LimitEvent::SizeRequest) => "The request size limit has been reached", - EventType::Limit(LimitEvent::SizeUpload) => "The upload size limit has been reached", - EventType::Limit(LimitEvent::CallsIn) => "The incoming calls limit has been reached", - EventType::Limit(LimitEvent::ConcurrentRequest) => { - "The concurrent request limit has been reached" - } - EventType::Limit(LimitEvent::ConcurrentUpload) => { - "The concurrent upload limit has been reached" - } - EventType::Limit(LimitEvent::ConcurrentConnection) => { - "The concurrent connection limit has been reached" - } - EventType::Limit(LimitEvent::Quota) => "The quota limit has been reached", - EventType::Limit(LimitEvent::BlobQuota) => "The blob quota limit has been reached", - EventType::Limit(LimitEvent::TenantQuota) => { - "One of the tenant quota limits has been reached" - } - EventType::Limit(LimitEvent::TooManyRequests) => "Too many requests have been made", - EventType::MailAuth(MailAuthEvent::ParseError) => { - "An error occurred while parsing mail authentication" - } - EventType::MailAuth(MailAuthEvent::MissingParameters) => { - "Mail authentication parameters are missing" - } - EventType::MailAuth(MailAuthEvent::NoHeadersFound) => { - "No headers were found in the message" - } - EventType::MailAuth(MailAuthEvent::Crypto) => { - "A crypto error occurred during mail authentication" - } - EventType::MailAuth(MailAuthEvent::Io) => { - "An I/O error occurred during mail authentication" - } - EventType::MailAuth(MailAuthEvent::Base64) => { - "A base64 error occurred during mail authentication" - } - EventType::MailAuth(MailAuthEvent::DnsError) => "A DNS error occurred", - EventType::MailAuth(MailAuthEvent::DnsRecordNotFound) => "The DNS record was not found", - EventType::MailAuth(MailAuthEvent::DnsInvalidRecordType) => { - "The DNS record type is invalid" - } - EventType::MailAuth(MailAuthEvent::PolicyNotAligned) => "The policy is not aligned", - EventType::ManageSieve(ManageSieveEvent::ConnectionStart) => { - "ManageSieve connection started" - } - EventType::ManageSieve(ManageSieveEvent::ConnectionEnd) => { - "ManageSieve connection ended" - } - EventType::ManageSieve(ManageSieveEvent::CreateScript) => "Client created a script", - EventType::ManageSieve(ManageSieveEvent::UpdateScript) => "Client updated a script", - EventType::ManageSieve(ManageSieveEvent::GetScript) => "Client fetched a script", - EventType::ManageSieve(ManageSieveEvent::DeleteScript) => "Client deleted a script", - EventType::ManageSieve(ManageSieveEvent::RenameScript) => "Client renamed a script", - EventType::ManageSieve(ManageSieveEvent::CheckScript) => "Client checked a script", - EventType::ManageSieve(ManageSieveEvent::HaveSpace) => "Client checked for space", - EventType::ManageSieve(ManageSieveEvent::ListScripts) => "Client listed scripts", - EventType::ManageSieve(ManageSieveEvent::SetActive) => "Client set an active script", - EventType::ManageSieve(ManageSieveEvent::Capabilities) => { - "Client requested server capabilities" - } - EventType::ManageSieve(ManageSieveEvent::StartTls) => "Client requested TLS", - EventType::ManageSieve(ManageSieveEvent::Unauthenticate) => "Client unauthenticated", - EventType::ManageSieve(ManageSieveEvent::Logout) => "Client logged out", - EventType::ManageSieve(ManageSieveEvent::Noop) => "Client sent a NOOP command", - EventType::ManageSieve(ManageSieveEvent::Error) => { - "An error occurred during a ManageSieve command" - } - EventType::ManageSieve(ManageSieveEvent::RawInput) => "Raw ManageSieve input received", - EventType::ManageSieve(ManageSieveEvent::RawOutput) => "Raw ManageSieve output sent", - EventType::MessageIngest(MessageIngestEvent::Ham) => "The message has been ingested", - EventType::MessageIngest(MessageIngestEvent::Spam) => { - "A possible spam message has been ingested" - } - EventType::MessageIngest(MessageIngestEvent::ImapAppend) => { - "The message has been appended via IMAP" - } - EventType::MessageIngest(MessageIngestEvent::JmapAppend) => { - "The message has been appended via JMAP" - } - EventType::MessageIngest(MessageIngestEvent::Duplicate) => { - "The message is a duplicate and has been skipped" - } - EventType::MessageIngest(MessageIngestEvent::Error) => { - "An error occurred while ingesting the message" - } - EventType::MessageIngest(MessageIngestEvent::SearchIndex) => { - "The search index has been updated" - } - EventType::Milter(MilterEvent::Read) => "Reading from the Milter", - EventType::Milter(MilterEvent::Write) => "Writing to the Milter", - EventType::Milter(MilterEvent::ActionAccept) => { - "The Milter requested to accept the message" - } - EventType::Milter(MilterEvent::ActionDiscard) => { - "The Milter requested to discard the message" - } - EventType::Milter(MilterEvent::ActionReject) => { - "The Milter requested to reject the message" - } - EventType::Milter(MilterEvent::ActionTempFail) => { - "The Milter requested to temporarily fail the message" - } - EventType::Milter(MilterEvent::ActionReplyCode) => "The Milter requested a reply code", - EventType::Milter(MilterEvent::ActionConnectionFailure) => { - "The Milter requested a connection failure" - } - EventType::Milter(MilterEvent::ActionShutdown) => "The Milter requested a shutdown", - EventType::Milter(MilterEvent::IoError) => "An I/O error occurred with the Milter", - EventType::Milter(MilterEvent::FrameTooLarge) => "The Milter frame was too large", - EventType::Milter(MilterEvent::FrameInvalid) => "The Milter frame was invalid", - EventType::Milter(MilterEvent::UnexpectedResponse) => { - "An unexpected response was received from the Milter" - } - EventType::Milter(MilterEvent::Timeout) => "A timeout occurred with the Milter", - EventType::Milter(MilterEvent::TlsInvalidName) => "The Milter TLS name is invalid", - EventType::Milter(MilterEvent::Disconnected) => "The Milter disconnected", - EventType::Milter(MilterEvent::ParseError) => { - "An error occurred while parsing the Milter response" - } - EventType::MtaHook(MtaHookEvent::ActionAccept) => { - "The MTA hook requested to accept the message" - } - EventType::MtaHook(MtaHookEvent::ActionDiscard) => { - "The MTA hook requested to discard the message" - } - EventType::MtaHook(MtaHookEvent::ActionReject) => { - "The MTA hook requested to reject the message" - } - EventType::MtaHook(MtaHookEvent::ActionQuarantine) => { - "The MTA hook requested to quarantine the message" - } - EventType::MtaHook(MtaHookEvent::Error) => "An error occurred with the MTA hook", - EventType::MtaSts(MtaStsEvent::Authorized) => { - "The host is authorized by the MTA-STS policy" - } - EventType::MtaSts(MtaStsEvent::NotAuthorized) => { - "The host is not authorized by the MTA-STS policy" - } - EventType::MtaSts(MtaStsEvent::PolicyFetch) => "The MTA-STS policy has been fetched", - EventType::MtaSts(MtaStsEvent::PolicyNotFound) => "An MTA-STS policy was not found", - EventType::MtaSts(MtaStsEvent::PolicyFetchError) => { - "An error occurred while fetching the MTA-STS policy" - } - EventType::MtaSts(MtaStsEvent::InvalidPolicy) => "The MTA-STS policy is invalid", - EventType::Network(NetworkEvent::ListenStart) => "The network listener has started", - EventType::Network(NetworkEvent::ListenStop) => "The network listener has stopped", - EventType::Network(NetworkEvent::ListenError) => { - "An error occurred with the network listener" - } - EventType::Network(NetworkEvent::BindError) => { - "An error occurred while binding the network listener" - } - EventType::Network(NetworkEvent::ReadError) => { - "An error occurred while reading from the network" - } - EventType::Network(NetworkEvent::WriteError) => { - "An error occurred while writing to the network" - } - EventType::Network(NetworkEvent::FlushError) => { - "An error occurred while flushing the network" - } - EventType::Network(NetworkEvent::AcceptError) => { - "An error occurred while accepting a network connection" - } - EventType::Network(NetworkEvent::SplitError) => { - "An error occurred while splitting the network connection" - } - EventType::Network(NetworkEvent::Timeout) => "A network timeout occurred", - EventType::Network(NetworkEvent::Closed) => "The network connection was closed", - EventType::Network(NetworkEvent::ProxyError) => { - "An error occurred with the proxy protocol" - } - EventType::Network(NetworkEvent::SetOptError) => { - "An error occurred while setting network options" - } - EventType::OutgoingReport(OutgoingReportEvent::SpfReport) => { - "An SPF report has been sent" - } - EventType::OutgoingReport(OutgoingReportEvent::SpfRateLimited) => { - "The SPF report was rate limited" - } - EventType::OutgoingReport(OutgoingReportEvent::DkimReport) => { - "A DKIM report has been sent" - } - EventType::OutgoingReport(OutgoingReportEvent::DkimRateLimited) => { - "The DKIM report was rate limited" - } - EventType::OutgoingReport(OutgoingReportEvent::DmarcReport) => { - "A DMARC report has been sent" - } - EventType::OutgoingReport(OutgoingReportEvent::DmarcRateLimited) => { - "The DMARC report was rate limited" - } - EventType::OutgoingReport(OutgoingReportEvent::DmarcAggregateReport) => { - "A DMARC aggregate report will be sent" - } - EventType::OutgoingReport(OutgoingReportEvent::TlsAggregate) => { - "A TLS aggregate report will be sent" - } - EventType::OutgoingReport(OutgoingReportEvent::HttpSubmission) => { - "The report was submitted via HTTP" - } - EventType::OutgoingReport(OutgoingReportEvent::UnauthorizedReportingAddress) => { - "The reporting address is not authorized to send reports" - } - EventType::OutgoingReport(OutgoingReportEvent::ReportingAddressValidationError) => { - "Error validating the reporting address" - } - EventType::OutgoingReport(OutgoingReportEvent::NotFound) => "The report was not found", - EventType::OutgoingReport(OutgoingReportEvent::SubmissionError) => { - "Error submitting the report" - } - EventType::OutgoingReport(OutgoingReportEvent::NoRecipientsFound) => { - "No recipients found for the report" - } - EventType::OutgoingReport(OutgoingReportEvent::Locked) => { - "The report is locked by another process" - } - EventType::OutgoingReport(OutgoingReportEvent::MaxSizeExceeded) => { - "The report size exceeds the maximum allowed size" - } - EventType::Pop3(Pop3Event::ConnectionStart) => "POP3 connection started", - EventType::Pop3(Pop3Event::ConnectionEnd) => "POP3 connection ended", - EventType::Pop3(Pop3Event::Delete) => "Client deleted a message", - EventType::Pop3(Pop3Event::Reset) => "Client reset the session", - EventType::Pop3(Pop3Event::Quit) => "Client quit the session", - EventType::Pop3(Pop3Event::Fetch) => "Client fetched a message", - EventType::Pop3(Pop3Event::List) => "Client listed messages", - EventType::Pop3(Pop3Event::ListMessage) => "Client listed a specific message", - EventType::Pop3(Pop3Event::Uidl) => "Client requested unique identifiers", - EventType::Pop3(Pop3Event::UidlMessage) => { - "Client requested a specific unique identifier" - } - EventType::Pop3(Pop3Event::Stat) => "Client requested mailbox status", - EventType::Pop3(Pop3Event::Noop) => "Client sent a NOOP command", - EventType::Pop3(Pop3Event::Capabilities) => "Client requested server capabilities", - EventType::Pop3(Pop3Event::StartTls) => "Client requested TLS", - EventType::Pop3(Pop3Event::Utf8) => "Client requested UTF-8 support", - EventType::Pop3(Pop3Event::Error) => "An error occurred during a POP3 command", - EventType::Pop3(Pop3Event::RawInput) => "Raw POP3 input received", - EventType::Pop3(Pop3Event::RawOutput) => "Raw POP3 output sent", - EventType::PushSubscription(PushSubscriptionEvent::Success) => { - "The push subscription was successful" - } - EventType::PushSubscription(PushSubscriptionEvent::Error) => { - "An error occurred with the push subscription" - } - EventType::PushSubscription(PushSubscriptionEvent::NotFound) => { - "The push subscription was not found" - } - EventType::Queue(QueueEvent::Started) => "The MTA queue has started", - EventType::Queue(QueueEvent::MessageQueued) => "A new message was queued for delivery", - EventType::Queue(QueueEvent::AuthenticatedMessageQueued) => { - "A new message was queued for delivery from an authenticated client" - } - EventType::Queue(QueueEvent::ReportQueued) => "A new report was queued for delivery", - EventType::Queue(QueueEvent::DsnQueued) => { - "A delivery status notification was queued for delivery" - } - EventType::Queue(QueueEvent::AutogeneratedQueued) => { - "A system generated message was queued for delivery" - } - EventType::Queue(QueueEvent::Rescheduled) => "The message was rescheduled for delivery", - EventType::Queue(QueueEvent::Locked) => "The queue event is locked by another process", - EventType::Queue(QueueEvent::BlobNotFound) => "The message blob was not found", - EventType::Queue(QueueEvent::RateLimitExceeded) => "The queue rate limit was exceeded", - EventType::Queue(QueueEvent::ConcurrencyLimitExceeded) => { - "The queue concurrency limit was exceeded" - } - EventType::Queue(QueueEvent::QuotaExceeded) => "The queue quota was exceeded", - EventType::Queue(QueueEvent::BackPressure) => { - "Queue congested, processing can't keep up with incoming message rate" - } - EventType::Registry(RegistryEvent::LocalReadError) => { - "An error occurred while reading the local registry file" - } - EventType::Registry(RegistryEvent::LocalWriteError) => { - "An error occurred while writing to the local registry file" - } - EventType::Registry(RegistryEvent::LocalParseError) => { - "An error occurred while parsing the local registry file" - } - EventType::Registry(RegistryEvent::ReadError) => { - "An error occurred while reading the registry file" - } - EventType::Registry(RegistryEvent::WriteError) => { - "An error occurred while writing to the registry file" - } - EventType::Registry(RegistryEvent::DeserializationError) => { - "An error occurred while deserializing a registry entry" - } - EventType::Registry(RegistryEvent::BuildError) => { - "An error occurred while building the configuration from the registry" - } - EventType::Registry(RegistryEvent::BuildWarning) => { - "A warning occurred while building the configuration from the registry" - } - EventType::Registry(RegistryEvent::NotSupported) => { - "The local registry does not support this operation" - } - EventType::Registry(RegistryEvent::ValidationError) => { - "An error occurred while validating a registry object" - } - EventType::Resource(ResourceEvent::NotFound) => "The resource was not found", - EventType::Resource(ResourceEvent::BadParameters) => "The resource parameters are bad", - EventType::Resource(ResourceEvent::Error) => "An error occurred with the resource", - EventType::Resource(ResourceEvent::DownloadExternal) => { - "The external resource is being downloaded" - } - EventType::Security(SecurityEvent::AuthenticationBan) => { - "IP address was banned due to multiple authentication errors" - } - EventType::Security(SecurityEvent::AbuseBan) => { - "IP address was banned due to abuse, such as RCPT TO attacks" - } - EventType::Security(SecurityEvent::ScanBan) => { - "IP address was banned due to exploit scanning" - } - EventType::Security(SecurityEvent::LoiterBan) => { - "IP address was banned due to multiple loitering events" - } - EventType::Security(SecurityEvent::IpBlocked) => { - "Rejected connection from blocked IP address" - } - EventType::Security(SecurityEvent::IpBlockExpired) => { - "A previously blocked IP address block has expired" - } - EventType::Security(SecurityEvent::IpAllowExpired) => { - "A previously allowed IP address allow has expired" - } - EventType::Security(SecurityEvent::IpUnauthorized) => { - "IP address is not authorized to authenticate using this credential" - } - EventType::Security(SecurityEvent::Unauthorized) => { - "Account does not have permission to access resource" - } - EventType::Server(ServerEvent::Startup) => "Stalwart Server has started", - EventType::Server(ServerEvent::Shutdown) => "Stalwart Server is shutting down", - EventType::Server(ServerEvent::StartupError) => { - "An error occurred while starting the server" - } - EventType::Server(ServerEvent::ThreadError) => "An error occurred with a server thread", - EventType::Server(ServerEvent::Licensing) => "A licensing event occurred", - EventType::Sieve(SieveEvent::ActionAccept) => { - "The Sieve script requested to accept the message" - } - EventType::Sieve(SieveEvent::ActionAcceptReplace) => { - "The Sieve script requested to accept the message and replace its contents" - } - EventType::Sieve(SieveEvent::ActionDiscard) => { - "The Sieve script requested to discard the message" - } - EventType::Sieve(SieveEvent::ActionReject) => { - "The Sieve script requested to reject the message" - } - EventType::Sieve(SieveEvent::SendMessage) => "The Sieve script is sending a message", - EventType::Sieve(SieveEvent::MessageTooLarge) => "The Sieve message is too large", - EventType::Sieve(SieveEvent::ScriptNotFound) => "The Sieve script was not found", - EventType::Sieve(SieveEvent::ListNotFound) => "The Sieve list was not found", - EventType::Sieve(SieveEvent::RuntimeError) => { - "A runtime error occurred with the Sieve script" - } - EventType::Sieve(SieveEvent::UnexpectedError) => { - "An unexpected error occurred with the Sieve script" - } - EventType::Sieve(SieveEvent::NotSupported) => "The Sieve action is not supported", - EventType::Sieve(SieveEvent::QuotaExceeded) => "The Sieve quota was exceeded", - EventType::Smtp(SmtpEvent::ConnectionStart) => "A new SMTP connection was started", - EventType::Smtp(SmtpEvent::ConnectionEnd) => "The SMTP connection was ended", - EventType::Smtp(SmtpEvent::Error) => "An error occurred during an SMTP command", - EventType::Smtp(SmtpEvent::IdNotFound) => { - "The strategy ID was not found in the configuration" - } - EventType::Smtp(SmtpEvent::ConcurrencyLimitExceeded) => { - "The concurrency limit was exceeded" - } - EventType::Smtp(SmtpEvent::TransferLimitExceeded) => { - "The remote host transferred more data than allowed" - } - EventType::Smtp(SmtpEvent::RateLimitExceeded) => "The rate limit was exceeded", - EventType::Smtp(SmtpEvent::TimeLimitExceeded) => { - "The remote host kept the SMTP session open too long" - } - EventType::Smtp(SmtpEvent::MissingAuthDirectory) => "The auth directory was missing", - EventType::Smtp(SmtpEvent::MessageParseFailed) => "Failed to parse the message", - EventType::Smtp(SmtpEvent::MessageTooLarge) => { - "The message was rejected because it was too large" - } - EventType::Smtp(SmtpEvent::LoopDetected) => { - "A mail loop was detected, the message contains too many Received headers" - } - EventType::Smtp(SmtpEvent::DkimPass) => "Successful DKIM verification", - EventType::Smtp(SmtpEvent::DkimFail) => "Failed to verify DKIM signature", - EventType::Smtp(SmtpEvent::ArcPass) => "Successful ARC verification", - EventType::Smtp(SmtpEvent::ArcFail) => "Failed to verify ARC signature", - EventType::Smtp(SmtpEvent::SpfEhloPass) => "EHLO identity passed SPF check", - EventType::Smtp(SmtpEvent::SpfEhloFail) => "EHLO identity failed SPF check", - EventType::Smtp(SmtpEvent::SpfFromPass) => "MAIL FROM identity passed SPF check", - EventType::Smtp(SmtpEvent::SpfFromFail) => "MAIL FROM identity failed SPF check", - EventType::Smtp(SmtpEvent::DmarcPass) => "Successful DMARC verification", - EventType::Smtp(SmtpEvent::DmarcFail) => "Failed to verify DMARC policy", - EventType::Smtp(SmtpEvent::IprevPass) => "Reverse IP check passed", - EventType::Smtp(SmtpEvent::IprevFail) => "Reverse IP check failed", - EventType::Smtp(SmtpEvent::TooManyMessages) => { - "The remote server exceeded the number of messages allowed per session" - } - EventType::Smtp(SmtpEvent::Ehlo) => "The remote server sent an EHLO command", - EventType::Smtp(SmtpEvent::InvalidEhlo) => { - "The remote server sent an invalid EHLO command" - } - EventType::Smtp(SmtpEvent::DidNotSayEhlo) => { - "The remote server did not send EHLO command" - } - EventType::Smtp(SmtpEvent::EhloExpected) => { - "The remote server sent a LHLO command while EHLO was expected" - } - EventType::Smtp(SmtpEvent::LhloExpected) => { - "The remote server sent an EHLO command while LHLO was expected" - } - EventType::Smtp(SmtpEvent::MailFromUnauthenticated) => { - "The remote client did not authenticate before sending MAIL FROM" - } - EventType::Smtp(SmtpEvent::MailFromUnauthorized) => { - "The remote client is not authorized to send mail from the given address" - } - EventType::Smtp(SmtpEvent::MailFromNotAllowed) => { - "The remote client is not allowed to send mail from this address" - } - EventType::Smtp(SmtpEvent::MailFromRewritten) => { - "The envelope sender address was rewritten" - } - EventType::Smtp(SmtpEvent::MailFromMissing) => { - "The remote client issued an RCPT TO command before MAIL FROM" - } - EventType::Smtp(SmtpEvent::MailFrom) => "The remote client sent a MAIL FROM command", - EventType::Smtp(SmtpEvent::MultipleMailFrom) => { - "The remote client already sent a MAIL FROM command" - } - EventType::Smtp(SmtpEvent::MailboxDoesNotExist) => { - "The mailbox does not exist on the server" - } - EventType::Smtp(SmtpEvent::RelayNotAllowed) => "The server does not allow relaying", - EventType::Smtp(SmtpEvent::RcptTo) => "The remote client sent an RCPT TO command", - EventType::Smtp(SmtpEvent::RcptToDuplicate) => { - "The remote client already sent an RCPT TO command for this recipient" - } - EventType::Smtp(SmtpEvent::RcptToRewritten) => { - "The envelope recipient address was rewritten" - } - EventType::Smtp(SmtpEvent::RcptToMissing) => { - "The remote client issued a DATA command before RCPT TO" - } - EventType::Smtp(SmtpEvent::RcptToGreylisted) => "The recipient was greylisted", - EventType::Smtp(SmtpEvent::TooManyRecipients) => { - "The remote client exceeded the number of recipients allowed" - } - EventType::Smtp(SmtpEvent::TooManyInvalidRcpt) => { - "The remote client exceeded the number of invalid RCPT TO commands allowed" - } - EventType::Smtp(SmtpEvent::RawInput) => "Raw SMTP input received", - EventType::Smtp(SmtpEvent::RawOutput) => "Raw SMTP output sent", - EventType::Smtp(SmtpEvent::MissingLocalHostname) => { - "The local hostname is missing in the configuration" - } - EventType::Smtp(SmtpEvent::Vrfy) => "The remote client sent a VRFY command", - EventType::Smtp(SmtpEvent::VrfyNotFound) => { - "The remote client sent a VRFY command for an address that was not found" - } - EventType::Smtp(SmtpEvent::VrfyDisabled) => "The VRFY command is disabled", - EventType::Smtp(SmtpEvent::Expn) => "The remote client sent an EXPN command", - EventType::Smtp(SmtpEvent::ExpnNotFound) => { - "The remote client sent an EXPN command for an address that was not found" - } - EventType::Smtp(SmtpEvent::ExpnDisabled) => "The EXPN command is disabled", - EventType::Smtp(SmtpEvent::RequireTlsDisabled) => { - "The REQUIRETLS extension is disabled" - } - EventType::Smtp(SmtpEvent::DeliverByDisabled) => "The DELIVERBY extension is disabled", - EventType::Smtp(SmtpEvent::DeliverByInvalid) => "The DELIVERBY parameter is invalid", - EventType::Smtp(SmtpEvent::FutureReleaseDisabled) => { - "The FUTURE RELEASE extension is disabled" - } - EventType::Smtp(SmtpEvent::FutureReleaseInvalid) => { - "The FUTURE RELEASE parameter is invalid" - } - EventType::Smtp(SmtpEvent::MtPriorityDisabled) => { - "The MT-PRIORITY extension is disabled" - } - EventType::Smtp(SmtpEvent::MtPriorityInvalid) => "The MT-PRIORITY parameter is invalid", - EventType::Smtp(SmtpEvent::DsnDisabled) => "The DSN extension is disabled", - EventType::Smtp(SmtpEvent::AuthNotAllowed) => { - "Authentication is not allowed on this listener" - } - EventType::Smtp(SmtpEvent::AuthMechanismNotSupported) => { - "The requested authentication mechanism is not supported" - } - EventType::Smtp(SmtpEvent::AuthExchangeTooLong) => { - "The authentication exchange was too long" - } - EventType::Smtp(SmtpEvent::AlreadyAuthenticated) => { - "The client is already authenticated" - } - EventType::Smtp(SmtpEvent::Noop) => "The remote client sent a NOOP command", - EventType::Smtp(SmtpEvent::StartTls) => "The remote client requested a TLS connection", - EventType::Smtp(SmtpEvent::StartTlsUnavailable) => { - "The remote client requested a TLS connection but it is not available" - } - EventType::Smtp(SmtpEvent::StartTlsAlready) => "TLS is already active", - EventType::Smtp(SmtpEvent::Rset) => "The remote client sent a RSET command", - EventType::Smtp(SmtpEvent::Quit) => "The remote client sent a QUIT command", - EventType::Smtp(SmtpEvent::Help) => "The remote client sent a HELP command", - EventType::Smtp(SmtpEvent::CommandNotImplemented) => { - "The server does not implement the requested command" - } - EventType::Smtp(SmtpEvent::InvalidCommand) => { - "The remote client sent an invalid command" - } - EventType::Smtp(SmtpEvent::InvalidSenderAddress) => { - "The specified sender address is invalid" - } - EventType::Smtp(SmtpEvent::InvalidRecipientAddress) => { - "The specified recipient address is invalid" - } - EventType::Smtp(SmtpEvent::InvalidParameter) => { - "The command contained an invalid parameter" - } - EventType::Smtp(SmtpEvent::UnsupportedParameter) => { - "The command contained an unsupported parameter" - } - EventType::Smtp(SmtpEvent::SyntaxError) => "The command contained a syntax error", - EventType::Smtp(SmtpEvent::RequestTooLarge) => "The request was too large", - EventType::Spam(SpamEvent::Pyzor) => "Pyzor query successful", - EventType::Spam(SpamEvent::PyzorError) => "An error occurred with Pyzor", - EventType::Spam(SpamEvent::Dnsbl) => "The DNSBL query was successful", - EventType::Spam(SpamEvent::DnsblError) => "An error occurred while querying the DNSBL", - EventType::Spam(SpamEvent::TrainStarted) => { - "SGD logistic regression training has started" - } - EventType::Spam(SpamEvent::TrainCompleted) => { - "SGD logistic regression training has completed" - } - EventType::Spam(SpamEvent::TrainSampleAdded) => "A new training sample has been added", - EventType::Spam(SpamEvent::TrainSampleNotFound) => "A training sample was not found", - EventType::Spam(SpamEvent::Classify) => "The message is being classified for spam", - EventType::Spam(SpamEvent::ModelLoaded) => "The spam classifier model has been loaded", - EventType::Spam(SpamEvent::ModelNotReady) => { - "The spam classifier model has not been trained with enough data" - } - EventType::Spam(SpamEvent::ModelNotFound) => { - "The spam classifier model has not been trained yet" - } - EventType::Spam(SpamEvent::RulesUpdated) => "The spam filter rules have been updated", - EventType::Spf(SpfEvent::Pass) => "The SPF check has passed", - EventType::Spf(SpfEvent::Fail) => "The SPF check has failed", - EventType::Spf(SpfEvent::SoftFail) => "The SPF check has soft failed", - EventType::Spf(SpfEvent::Neutral) => "The SPF result is neutral", - EventType::Spf(SpfEvent::TempError) => "A temporary error occurred with SPF", - EventType::Spf(SpfEvent::PermError) => "A permanent error occurred with SPF", - EventType::Spf(SpfEvent::None) => "No SPF record was found", - EventType::Store(StoreEvent::AssertValueFailed) => { - "Another process modified the record" - } - EventType::Store(StoreEvent::FoundationdbError) => "A FoundationDB error occurred", - EventType::Store(StoreEvent::MysqlError) => "A MySQL error occurred", - EventType::Store(StoreEvent::PostgresqlError) => "A PostgreSQL error occurred", - EventType::Store(StoreEvent::RocksdbError) => "A RocksDB error occurred", - EventType::Store(StoreEvent::SqliteError) => "An SQLite error occurred", - EventType::Store(StoreEvent::LdapError) => "An LDAP error occurred", - EventType::Store(StoreEvent::ElasticsearchError) => "An ElasticSearch error occurred", - EventType::Store(StoreEvent::MeilisearchError) => "A Meilisearch error occurred", - EventType::Store(StoreEvent::RedisError) => "A Redis error occurred", - EventType::Store(StoreEvent::S3Error) => "An S3 error occurred", - EventType::Store(StoreEvent::AzureError) => "An Azure error occurred", - EventType::Store(StoreEvent::FilesystemError) => "A filesystem error occurred", - EventType::Store(StoreEvent::PoolError) => "A connection pool error occurred", - EventType::Store(StoreEvent::DataCorruption) => "Data corruption was detected", - EventType::Store(StoreEvent::DecompressError) => "A decompression error occurred", - EventType::Store(StoreEvent::DeserializeError) => "A deserialization error occurred", - EventType::Store(StoreEvent::NotFound) => "The record was not found in the database", - EventType::Store(StoreEvent::NotConfigured) => "The store is not configured", - EventType::Store(StoreEvent::NotSupported) => { - "The operation is not supported by the store" - } - EventType::Store(StoreEvent::UnexpectedError) => "An unexpected store error occurred", - EventType::Store(StoreEvent::CryptoError) => "A store crypto error occurred", - EventType::Store(StoreEvent::HttpStoreError) => { - "An error occurred while updating the HTTP store" - } - EventType::Store(StoreEvent::CacheMiss) => "No cache entry found for the account", - EventType::Store(StoreEvent::CacheHit) => { - "Cache entry found for the account, no update needed" - } - EventType::Store(StoreEvent::CacheStale) => "Cache is too old, rebuilding", - EventType::Store(StoreEvent::CacheUpdate) => { - "Cache updated with latest database changes" - } - EventType::Store(StoreEvent::BlobMissingMarker) => "The blob is missing a marker", - EventType::Store(StoreEvent::DataWrite) => "A write batch operation was executed", - EventType::Store(StoreEvent::DataIterate) => { - "A data store iteration operation was executed" - } - EventType::Store(StoreEvent::BlobRead) => "A blob read operation was executed", - EventType::Store(StoreEvent::BlobWrite) => "A blob write operation was executed", - EventType::Store(StoreEvent::BlobDelete) => "A blob delete operation was executed", - EventType::Store(StoreEvent::SqlQuery) => "An SQL query was executed", - EventType::Store(StoreEvent::LdapQuery) => "An LDAP query was executed", - EventType::Store(StoreEvent::LdapWarning) => "An LDAP authentication warning occurred", - EventType::Store(StoreEvent::HttpStoreFetch) => "The HTTP store was updated", - EventType::Store(StoreEvent::AutoExpunge) => "Auto-expunge has been executed", - EventType::Store(StoreEvent::BlobStorePurged) => { - "The blob store purge process has completed" - } - EventType::Store(StoreEvent::DataStorePurged) => { - "The data store purge process has completed" - } - EventType::TaskManager(TaskManagerEvent::TaskAcquired) => { - "A task has been acquired from the queue" - } - EventType::TaskManager(TaskManagerEvent::TaskQueued) => { - "A task has been queued for processing" - } - EventType::TaskManager(TaskManagerEvent::TaskScheduled) => { - "A task has been scheduled for future execution" - } - EventType::TaskManager(TaskManagerEvent::TaskLocked) => { - "The task id is locked by another process" - } - EventType::TaskManager(TaskManagerEvent::TaskIgnored) => { - "The task was ignored based on the current server roles" - } - EventType::TaskManager(TaskManagerEvent::TaskFailed) => { - "The task failed during processing" - } - EventType::TaskManager(TaskManagerEvent::TaskRetry) => { - "The task will be retried after a failure" - } - EventType::TaskManager(TaskManagerEvent::BlobNotFound) => { - "The requested blob was not found for task" - } - EventType::TaskManager(TaskManagerEvent::MetadataNotFound) => { - "The metadata was not found for task" - } - EventType::TaskManager(TaskManagerEvent::SchedulerStarted) => { - "The task scheduler has started" - } - EventType::TaskManager(TaskManagerEvent::ManagerStarted) => { - "The task manager has started" - } - EventType::Telemetry(TelemetryEvent::AlertEvent) => "An alert event was triggered", - EventType::Telemetry(TelemetryEvent::AlertMessage) => { - "An alert message was sent via email" - } - EventType::Telemetry(TelemetryEvent::LogError) => { - "An error occurred with the log collector" - } - EventType::Telemetry(TelemetryEvent::WebhookError) => { - "An error occurred with the webhook collector" - } - EventType::Telemetry(TelemetryEvent::OtelExporterError) => { - "An error occurred with the OpenTelemetry exporter" - } - EventType::Telemetry(TelemetryEvent::OtelMetricsExporterError) => { - "An error occurred with the OpenTelemetry metrics exporter" - } - EventType::Telemetry(TelemetryEvent::PrometheusExporterError) => { - "An error occurred with the Prometheus exporter" - } - EventType::Telemetry(TelemetryEvent::JournalError) => { - "An error occurred with the journal collector" - } - EventType::Telemetry(TelemetryEvent::MetricsCollected) => "Metrics have been collected", - EventType::Telemetry(TelemetryEvent::MetricsStored) => "Metrics have been stored", - EventType::Telemetry(TelemetryEvent::MetricsPushed) => { - "Metrics have been pushed to the configured exporters" - } - EventType::Tls(TlsEvent::Handshake) => "Successful TLS handshake", - EventType::Tls(TlsEvent::HandshakeError) => { - "An error occurred during the TLS handshake" - } - EventType::Tls(TlsEvent::NotConfigured) => "TLS is not configured", - EventType::Tls(TlsEvent::CertificateNotFound) => "The TLS certificate was not found", - EventType::Tls(TlsEvent::NoCertificatesAvailable) => { - "No TLS certificates are available" - } - EventType::Tls(TlsEvent::MultipleCertificatesAvailable) => { - "Multiple TLS certificates are available" - } - EventType::Tls(TlsEvent::ExpiredCertificateRemoved) => { - "A TLS certificate has expired and was removed from the store" - } - EventType::TlsRpt(TlsRptEvent::RecordFetch) => "The TLS-RPT record has been fetched", - EventType::TlsRpt(TlsRptEvent::RecordFetchError) => { - "An error occurred while fetching the TLS-RPT record" - } - EventType::TlsRpt(TlsRptEvent::RecordNotFound) => "No TLS-RPT records were found", - EventType::WebDav(WebDavEvent::Propfind) => { - "A PROPFIND request has been made to the server" - } - EventType::WebDav(WebDavEvent::Proppatch) => { - "A PROPPATCH request has been made to the server" - } - EventType::WebDav(WebDavEvent::Get) => "A GET request has been made to the server", - EventType::WebDav(WebDavEvent::Head) => "A HEAD request has been made to the server", - EventType::WebDav(WebDavEvent::Report) => { - "A REPORT request has been made to the server" - } - EventType::WebDav(WebDavEvent::Mkcol) => "A MKCOL request has been made to the server", - EventType::WebDav(WebDavEvent::Mkcalendar) => { - "A MKCALENDAR request has been made to the server" - } - EventType::WebDav(WebDavEvent::Delete) => { - "A DELETE request has been made to the server" - } - EventType::WebDav(WebDavEvent::Put) => "A PUT request has been made to the server", - EventType::WebDav(WebDavEvent::Post) => "A POST request has been made to the server", - EventType::WebDav(WebDavEvent::Patch) => "A PATCH request has been made to the server", - EventType::WebDav(WebDavEvent::Copy) => "A COPY request has been made to the server", - EventType::WebDav(WebDavEvent::Move) => "A MOVE request has been made to the server", - EventType::WebDav(WebDavEvent::Lock) => "A LOCK request has been made to the server", - EventType::WebDav(WebDavEvent::Unlock) => { - "An UNLOCK request has been made to the server" - } - EventType::WebDav(WebDavEvent::Acl) => "An ACL request has been made to the server", - EventType::WebDav(WebDavEvent::Options) => { - "An OPTIONS request has been made to the server" - } - EventType::WebDav(WebDavEvent::Error) => "An error occurred with the WebDAV request", - } - } - pub fn message(&self) -> &'static str { match self { EventType::Auth(AuthEvent::Success) => "Authentication error", EventType::Auth(AuthEvent::Failed) => "Authentication failed", EventType::Auth(AuthEvent::TokenExpired) => "Authentication error", - EventType::Auth(AuthEvent::MfaRequired) => "This account requires multi-factor authentication. Alternatively, you can use an app password if your account has one.", + EventType::Auth(AuthEvent::MfaRequired) => { + "This account requires multi-factor authentication. Alternatively, you can use an app password if your account has one." + } EventType::Auth(AuthEvent::TooManyAttempts) => "Too many authentication attempts", EventType::Auth(AuthEvent::ClientRegistration) => "Authentication error", EventType::Auth(AuthEvent::Error) => "Authentication error", @@ -4890,7 +3761,9 @@ impl EventType { EventType::Jmap(JmapEvent::InvalidResultReference) => "Invalid result reference", EventType::Jmap(JmapEvent::Forbidden) => "Forbidden", EventType::Jmap(JmapEvent::AccountNotFound) => "Account not found", - EventType::Jmap(JmapEvent::AccountNotSupportedByMethod) => "Account not supported by method", + EventType::Jmap(JmapEvent::AccountNotSupportedByMethod) => { + "Account not supported by method" + } EventType::Jmap(JmapEvent::AccountReadOnly) => "Account read-only", EventType::Jmap(JmapEvent::NotFound) => "Not found", EventType::Jmap(JmapEvent::CannotCalculateChanges) => "Cannot calculate changes", @@ -5056,7 +3929,9 @@ impl EventType { EventType::Smtp(SmtpEvent::UnsupportedParameter) => "SMTP error", EventType::Smtp(SmtpEvent::SyntaxError) => "SMTP error", EventType::Smtp(SmtpEvent::RequestTooLarge) => "SMTP error", - EventType::Store(StoreEvent::AssertValueFailed) => "Another process has modified the value", + EventType::Store(StoreEvent::AssertValueFailed) => { + "Another process has modified the value" + } EventType::Store(StoreEvent::FoundationdbError) => "FoundationDB error", EventType::Store(StoreEvent::MysqlError) => "MySQL error", EventType::Store(StoreEvent::PostgresqlError) => "PostgreSQL error", @@ -8459,3 +7334,247 @@ impl MetricType { ] } } + +impl Key { + pub fn parse(value: &str) -> Option { + hashify::map! { + value.as_bytes(), + Key, + b"accountName" => Key::AccountName, + b"accountId" => Key::AccountId, + b"blobId" => Key::BlobId, + b"causedBy" => Key::CausedBy, + b"changeId" => Key::ChangeId, + b"code" => Key::Code, + b"collection" => Key::Collection, + b"contents" => Key::Contents, + b"details" => Key::Details, + b"dkimFail" => Key::DkimFail, + b"dkimNone" => Key::DkimNone, + b"dkimPass" => Key::DkimPass, + b"dmarcNone" => Key::DmarcNone, + b"dmarcPass" => Key::DmarcPass, + b"dmarcQuarantine" => Key::DmarcQuarantine, + b"dmarcReject" => Key::DmarcReject, + b"documentId" => Key::DocumentId, + b"domain" => Key::Domain, + b"due" => Key::Due, + b"elapsed" => Key::Elapsed, + b"expires" => Key::Expires, + b"from" => Key::From, + b"hostname" => Key::Hostname, + b"id" => Key::Id, + b"key" => Key::Key, + b"limit" => Key::Limit, + b"listenerId" => Key::ListenerId, + b"localIp" => Key::LocalIp, + b"localPort" => Key::LocalPort, + b"mailboxName" => Key::MailboxName, + b"mailboxId" => Key::MailboxId, + b"messageId" => Key::MessageId, + b"nextDsn" => Key::NextDsn, + b"nextRetry" => Key::NextRetry, + b"path" => Key::Path, + b"policy" => Key::Policy, + b"queueId" => Key::QueueId, + b"rangeFrom" => Key::RangeFrom, + b"rangeTo" => Key::RangeTo, + b"reason" => Key::Reason, + b"remoteIp" => Key::RemoteIp, + b"remotePort" => Key::RemotePort, + b"reportId" => Key::ReportId, + b"result" => Key::Result, + b"size" => Key::Size, + b"source" => Key::Source, + b"spanId" => Key::SpanId, + b"spfFail" => Key::SpfFail, + b"spfNone" => Key::SpfNone, + b"spfPass" => Key::SpfPass, + b"strict" => Key::Strict, + b"tls" => Key::Tls, + b"to" => Key::To, + b"total" => Key::Total, + b"totalFailures" => Key::TotalFailures, + b"totalSuccesses" => Key::TotalSuccesses, + b"type" => Key::Type, + b"uid" => Key::Uid, + b"uidNext" => Key::UidNext, + b"uidValidity" => Key::UidValidity, + b"url" => Key::Url, + b"validFrom" => Key::ValidFrom, + b"validTo" => Key::ValidTo, + b"value" => Key::Value, + b"version" => Key::Version, + b"queueName" => Key::QueueName, + } + .copied() + } + + pub fn as_str(&self) -> &'static str { + match self { + Key::AccountName => "accountName", + Key::AccountId => "accountId", + Key::BlobId => "blobId", + Key::CausedBy => "causedBy", + Key::ChangeId => "changeId", + Key::Code => "code", + Key::Collection => "collection", + Key::Contents => "contents", + Key::Details => "details", + Key::DkimFail => "dkimFail", + Key::DkimNone => "dkimNone", + Key::DkimPass => "dkimPass", + Key::DmarcNone => "dmarcNone", + Key::DmarcPass => "dmarcPass", + Key::DmarcQuarantine => "dmarcQuarantine", + Key::DmarcReject => "dmarcReject", + Key::DocumentId => "documentId", + Key::Domain => "domain", + Key::Due => "due", + Key::Elapsed => "elapsed", + Key::Expires => "expires", + Key::From => "from", + Key::Hostname => "hostname", + Key::Id => "id", + Key::Key => "key", + Key::Limit => "limit", + Key::ListenerId => "listenerId", + Key::LocalIp => "localIp", + Key::LocalPort => "localPort", + Key::MailboxName => "mailboxName", + Key::MailboxId => "mailboxId", + Key::MessageId => "messageId", + Key::NextDsn => "nextDsn", + Key::NextRetry => "nextRetry", + Key::Path => "path", + Key::Policy => "policy", + Key::QueueId => "queueId", + Key::RangeFrom => "rangeFrom", + Key::RangeTo => "rangeTo", + Key::Reason => "reason", + Key::RemoteIp => "remoteIp", + Key::RemotePort => "remotePort", + Key::ReportId => "reportId", + Key::Result => "result", + Key::Size => "size", + Key::Source => "source", + Key::SpanId => "spanId", + Key::SpfFail => "spfFail", + Key::SpfNone => "spfNone", + Key::SpfPass => "spfPass", + Key::Strict => "strict", + Key::Tls => "tls", + Key::To => "to", + Key::Total => "total", + Key::TotalFailures => "totalFailures", + Key::TotalSuccesses => "totalSuccesses", + Key::Type => "type", + Key::Uid => "uid", + Key::UidNext => "uidNext", + Key::UidValidity => "uidValidity", + Key::Url => "url", + Key::ValidFrom => "validFrom", + Key::ValidTo => "validTo", + Key::Value => "value", + Key::Version => "version", + Key::QueueName => "queueName", + } + } + + pub fn to_id(&self) -> u16 { + *self as u16 + } + + pub fn from_id(id: u16) -> Option { + match id { + 0 => Some(Key::AccountName), + 1 => Some(Key::AccountId), + 2 => Some(Key::BlobId), + 3 => Some(Key::CausedBy), + 4 => Some(Key::ChangeId), + 5 => Some(Key::Code), + 6 => Some(Key::Collection), + 7 => Some(Key::Contents), + 8 => Some(Key::Details), + 9 => Some(Key::DkimFail), + 10 => Some(Key::DkimNone), + 11 => Some(Key::DkimPass), + 12 => Some(Key::DmarcNone), + 13 => Some(Key::DmarcPass), + 14 => Some(Key::DmarcQuarantine), + 15 => Some(Key::DmarcReject), + 16 => Some(Key::DocumentId), + 17 => Some(Key::Domain), + 18 => Some(Key::Due), + 19 => Some(Key::Elapsed), + 20 => Some(Key::Expires), + 21 => Some(Key::From), + 22 => Some(Key::Hostname), + 23 => Some(Key::Id), + 24 => Some(Key::Key), + 25 => Some(Key::Limit), + 26 => Some(Key::ListenerId), + 27 => Some(Key::LocalIp), + 28 => Some(Key::LocalPort), + 29 => Some(Key::MailboxName), + 30 => Some(Key::MailboxId), + 31 => Some(Key::MessageId), + 32 => Some(Key::NextDsn), + 33 => Some(Key::NextRetry), + 34 => Some(Key::Path), + 35 => Some(Key::Policy), + 36 => Some(Key::QueueId), + 37 => Some(Key::RangeFrom), + 38 => Some(Key::RangeTo), + 39 => Some(Key::Reason), + 40 => Some(Key::RemoteIp), + 41 => Some(Key::RemotePort), + 42 => Some(Key::ReportId), + 43 => Some(Key::Result), + 44 => Some(Key::Size), + 45 => Some(Key::Source), + 46 => Some(Key::SpanId), + 47 => Some(Key::SpfFail), + 48 => Some(Key::SpfNone), + 49 => Some(Key::SpfPass), + 50 => Some(Key::Strict), + 51 => Some(Key::Tls), + 52 => Some(Key::To), + 53 => Some(Key::Total), + 54 => Some(Key::TotalFailures), + 55 => Some(Key::TotalSuccesses), + 56 => Some(Key::Type), + 57 => Some(Key::Uid), + 58 => Some(Key::UidNext), + 59 => Some(Key::UidValidity), + 60 => Some(Key::Url), + 61 => Some(Key::ValidFrom), + 62 => Some(Key::ValidTo), + 63 => Some(Key::Value), + 64 => Some(Key::Version), + 65 => Some(Key::QueueName), + _ => None, + } + } + + pub const COUNT: usize = 66; +} + +impl serde::Serialize for Key { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(self.as_str()) + } +} + +impl<'de> serde::Deserialize<'de> for Key { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = Cow::::deserialize(deserializer)?; + Self::parse(&s).ok_or_else(|| serde::de::Error::unknown_variant(&s, &[])) + } +} diff --git a/crates/trc/src/event/mod.rs b/crates/trc/src/event/mod.rs index 4ebaebac..2c053560 100644 --- a/crates/trc/src/event/mod.rs +++ b/crates/trc/src/event/mod.rs @@ -681,148 +681,3 @@ impl AsRef> for Event { self } } - -impl Key { - pub fn code(&self) -> u64 { - match self { - Key::AccountName => 0, - Key::AccountId => 1, - Key::BlobId => 2, - Key::CausedBy => 3, - Key::ChangeId => 4, - Key::Code => 5, - Key::Collection => 6, - Key::Contents => 7, - Key::Details => 8, - Key::DkimFail => 9, - Key::DkimNone => 10, - Key::DkimPass => 11, - Key::DmarcNone => 12, - Key::DmarcPass => 13, - Key::DmarcQuarantine => 14, - Key::DmarcReject => 15, - Key::DocumentId => 16, - Key::Domain => 17, - Key::Due => 18, - Key::Elapsed => 19, - Key::Expires => 20, - Key::From => 21, - Key::Hostname => 22, - Key::Id => 23, - Key::Key => 24, - Key::Limit => 25, - Key::ListenerId => 26, - Key::LocalIp => 27, - Key::LocalPort => 28, - Key::MailboxName => 29, - Key::MailboxId => 30, - Key::MessageId => 31, - Key::NextDsn => 32, - Key::NextRetry => 33, - Key::Path => 34, - Key::Policy => 35, - Key::QueueId => 36, - Key::RangeFrom => 37, - Key::RangeTo => 38, - Key::Reason => 39, - Key::RemoteIp => 40, - Key::RemotePort => 41, - Key::ReportId => 42, - Key::Result => 43, - Key::Size => 44, - Key::Source => 45, - Key::SpanId => 46, - Key::SpfFail => 47, - Key::SpfNone => 48, - Key::SpfPass => 49, - Key::Strict => 50, - Key::Tls => 51, - Key::To => 52, - Key::Total => 53, - Key::TotalFailures => 54, - Key::TotalSuccesses => 55, - Key::Type => 56, - Key::Uid => 57, - Key::UidNext => 58, - Key::UidValidity => 59, - Key::Url => 60, - Key::ValidFrom => 61, - Key::ValidTo => 62, - Key::Value => 63, - Key::Version => 64, - Key::QueueName => 65, - } - } - - pub fn from_code(code: u64) -> Option { - match code { - 0 => Some(Key::AccountName), - 1 => Some(Key::AccountId), - 2 => Some(Key::BlobId), - 3 => Some(Key::CausedBy), - 4 => Some(Key::ChangeId), - 5 => Some(Key::Code), - 6 => Some(Key::Collection), - 7 => Some(Key::Contents), - 8 => Some(Key::Details), - 9 => Some(Key::DkimFail), - 10 => Some(Key::DkimNone), - 11 => Some(Key::DkimPass), - 12 => Some(Key::DmarcNone), - 13 => Some(Key::DmarcPass), - 14 => Some(Key::DmarcQuarantine), - 15 => Some(Key::DmarcReject), - 16 => Some(Key::DocumentId), - 17 => Some(Key::Domain), - 18 => Some(Key::Due), - 19 => Some(Key::Elapsed), - 20 => Some(Key::Expires), - 21 => Some(Key::From), - 22 => Some(Key::Hostname), - 23 => Some(Key::Id), - 24 => Some(Key::Key), - 25 => Some(Key::Limit), - 26 => Some(Key::ListenerId), - 27 => Some(Key::LocalIp), - 28 => Some(Key::LocalPort), - 29 => Some(Key::MailboxName), - 30 => Some(Key::MailboxId), - 31 => Some(Key::MessageId), - 32 => Some(Key::NextDsn), - 33 => Some(Key::NextRetry), - 34 => Some(Key::Path), - 35 => Some(Key::Policy), - 36 => Some(Key::QueueId), - 37 => Some(Key::RangeFrom), - 38 => Some(Key::RangeTo), - 39 => Some(Key::Reason), - 40 => Some(Key::RemoteIp), - 41 => Some(Key::RemotePort), - 42 => Some(Key::ReportId), - 43 => Some(Key::Result), - 44 => Some(Key::Size), - 45 => Some(Key::Source), - 46 => Some(Key::SpanId), - 47 => Some(Key::SpfFail), - 48 => Some(Key::SpfNone), - 49 => Some(Key::SpfPass), - 50 => Some(Key::Strict), - 51 => Some(Key::Tls), - 52 => Some(Key::To), - 53 => Some(Key::Total), - 54 => Some(Key::TotalFailures), - 55 => Some(Key::TotalSuccesses), - 56 => Some(Key::Type), - 57 => Some(Key::Uid), - 58 => Some(Key::UidNext), - 59 => Some(Key::UidValidity), - 60 => Some(Key::Url), - 61 => Some(Key::ValidFrom), - 62 => Some(Key::ValidTo), - 63 => Some(Key::Value), - 64 => Some(Key::Version), - 65 => Some(Key::QueueName), - _ => None, - } - } -} diff --git a/crates/trc/src/lib.rs b/crates/trc/src/lib.rs index 3627e89c..e4fb53e2 100644 --- a/crates/trc/src/lib.rs +++ b/crates/trc/src/lib.rs @@ -14,7 +14,6 @@ pub use crate::event::enums::*; pub use crate::ipc::collector::Collector; use compact_str::CompactString; pub use event_macro::event; -use event_macro::key_names; use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr}, sync::Arc, @@ -69,78 +68,6 @@ pub enum Value { None, } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] -#[key_names] -pub enum Key { - AccountName, - AccountId, - BlobId, - #[default] - CausedBy, - ChangeId, - Code, - Collection, - Contents, - Details, - DkimFail, - DkimNone, - DkimPass, - DmarcNone, - DmarcPass, - DmarcQuarantine, - DmarcReject, - DocumentId, - Domain, - Due, - Elapsed, - Expires, - From, - Hostname, - Id, - Key, - Limit, - ListenerId, - LocalIp, - LocalPort, - MailboxName, - MailboxId, - MessageId, - NextDsn, - NextRetry, - Path, - Policy, - QueueId, - RangeFrom, - RangeTo, - Reason, - RemoteIp, - RemotePort, - ReportId, - Result, - Size, - Source, - SpanId, - SpfFail, - SpfNone, - SpfPass, - Strict, - Tls, - To, - Total, - TotalFailures, - TotalSuccesses, - Type, - Uid, - UidNext, - UidValidity, - Url, - ValidFrom, - ValidTo, - Value, - Version, - QueueName, -} - pub trait AddContext { fn caused_by(self, location: &'static str) -> Result; fn add_context(self, f: F) -> Result diff --git a/crates/trc/src/serializers/binary.rs b/crates/trc/src/serializers/binary.rs index bf469ac7..e75c7374 100644 --- a/crates/trc/src/serializers/binary.rs +++ b/crates/trc/src/serializers/binary.rs @@ -89,7 +89,7 @@ impl Event { buf.extend_from_slice(self.inner.timestamp.to_le_bytes().as_ref()); leb128_write(buf, self.keys.len() as u64); for (k, v) in &self.keys { - leb128_write(buf, k.code()); + leb128_write(buf, k.to_id() as u64); v.serialize(buf); } } @@ -108,7 +108,7 @@ impl Event { let keys_len = leb128_read(iter)?; let mut keys = Vec::with_capacity(keys_len as usize); for _ in 0..keys_len { - let key = Key::from_code(leb128_read(iter)?)?; + let key = Key::from_id(leb128_read(iter)? as u16)?; let value = Value::deserialize(iter)?; keys.push((key, value)); } @@ -176,7 +176,7 @@ impl Value { leb128_write(buf, v.0.inner.to_id() as u64); leb128_write(buf, v.0.keys.len() as u64); for (k, v) in &v.0.keys { - leb128_write(buf, k.code()); + leb128_write(buf, k.to_id() as u64); v.serialize(buf); } } @@ -253,7 +253,7 @@ impl Value { let keys_len = leb128_read(iter)?; let mut keys = Vec::with_capacity(keys_len as usize); for _ in 0..keys_len { - let key = Key::from_code(leb128_read(iter)?)?; + let key = Key::from_id(leb128_read(iter)? as u16)?; let value = Value::deserialize(iter)?; keys.push((key, value)); } diff --git a/crates/trc/src/serializers/json.rs b/crates/trc/src/serializers/json.rs index 58f5ab3d..fed7ff7a 100644 --- a/crates/trc/src/serializers/json.rs +++ b/crates/trc/src/serializers/json.rs @@ -23,7 +23,6 @@ pub struct JsonEventSerializer { with_id: bool, with_spans: bool, with_description: bool, - with_explanation: bool, } impl JsonEventSerializer { @@ -33,7 +32,6 @@ impl JsonEventSerializer { with_id: false, with_spans: false, with_description: false, - with_explanation: false, } } @@ -52,11 +50,6 @@ impl JsonEventSerializer { self } - pub fn with_explanation(mut self) -> Self { - self.with_explanation = true; - self - } - pub fn into_inner(self) -> T { self.inner } @@ -74,7 +67,6 @@ impl>> Serialize for JsonEventSerializer> { with_id: self.with_id, with_spans: self.with_spans, with_description: self.with_description, - with_explanation: self.with_explanation, })?; } seq.end() @@ -97,9 +89,6 @@ impl>> Serialize for JsonEventSerializer { if self.with_description { map.serialize_entry("text", event.inner.typ.description())?; } - if self.with_explanation { - map.serialize_entry("details", event.inner.typ.explain())?; - } map.serialize_entry( "createdAt", &DateTime::from_timestamp(event.inner.timestamp as i64).to_rfc3339(), @@ -119,7 +108,6 @@ impl>> Serialize for JsonEventSerializer { }, with_spans: self.with_spans, with_description: self.with_description, - with_explanation: self.with_explanation, with_id: self.with_id, }, )?; @@ -141,12 +129,11 @@ impl Serialize for JsonEventSerializer> { && seen_keys.insert(*key) { keys.serialize_entry( - key.name(), + key.as_str(), &JsonEventSerializer { inner: value, with_spans: self.with_spans, with_description: self.with_description, - with_explanation: self.with_explanation, with_id: self.with_id, }, )?; @@ -166,9 +153,6 @@ impl Serialize for JsonEventSerializer<&Error> { if self.with_description { map.serialize_entry("text", self.inner.0.inner.description())?; } - if self.with_explanation { - map.serialize_entry("details", self.inner.0.inner.explain())?; - } map.serialize_entry( "data", &JsonEventSerializer { @@ -178,7 +162,6 @@ impl Serialize for JsonEventSerializer<&Error> { }, with_spans: self.with_spans, with_description: self.with_description, - with_explanation: self.with_explanation, with_id: self.with_id, }, )?; @@ -208,7 +191,6 @@ impl Serialize for JsonEventSerializer<&Value> { inner: value, with_spans: self.with_spans, with_description: self.with_description, - with_explanation: self.with_explanation, with_id: self.with_id, } .serialize(serializer), @@ -216,7 +198,6 @@ impl Serialize for JsonEventSerializer<&Value> { inner: value, with_spans: self.with_spans, with_description: self.with_description, - with_explanation: self.with_explanation, with_id: self.with_id, } .serialize(serializer), @@ -236,7 +217,6 @@ impl Serialize for JsonEventSerializer<&Vec> { inner: value, with_spans: self.with_spans, with_description: self.with_description, - with_explanation: self.with_explanation, with_id: self.with_id, })?; } @@ -281,22 +261,3 @@ impl<'de> serde::Deserialize<'de> for MetricType { Self::parse(s).ok_or_else(|| serde::de::Error::unknown_variant(s, &[])) } } - -impl serde::Serialize for Key { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(self.name()) - } -} - -impl<'de> serde::Deserialize<'de> for Key { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let s = <&str>::deserialize(deserializer)?; - Self::try_parse(s).ok_or_else(|| serde::de::Error::unknown_variant(s, &[])) - } -} diff --git a/crates/trc/src/serializers/text.rs b/crates/trc/src/serializers/text.rs index 1ac88117..1e86b8bd 100644 --- a/crates/trc/src/serializers/text.rs +++ b/crates/trc/src/serializers/text.rs @@ -154,7 +154,7 @@ impl FmtWriter { .write_all(Color::Cyan.as_code().as_bytes()) .await?; } - self.writer.write_all(key.name().as_bytes()).await?; + self.writer.write_all(key.as_str().as_bytes()).await?; if self.ansi { self.writer.write_all(Color::reset().as_bytes()).await?; } @@ -363,7 +363,7 @@ impl Display for Error { if i > 0 { f.write_str(", ")?; } - key.name().fmt(f)?; + key.as_str().fmt(f)?; f.write_str(" = ")?; value.fmt(f)?; } diff --git a/resources/html-templates/login.html b/resources/html-templates/login.html index 0199074a..ae8b8477 100644 --- a/resources/html-templates/login.html +++ b/resources/html-templates/login.html @@ -4,6 +4,7 @@ + Sign in
- -

Sign in

-

Enter your credentials to continue

-