diff --git a/crates/common/src/auth/access_token.rs b/crates/common/src/auth/access_token.rs index 106828c8..32d0ab3d 100644 --- a/crates/common/src/auth/access_token.rs +++ b/crates/common/src/auth/access_token.rs @@ -62,12 +62,12 @@ impl Server { // Apply principal permissions let mut permissions = role_permissions.finalize(); + let mut tenant = None; // SPDX-SnippetBegin // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC // SPDX-License-Identifier: LicenseRef-SEL - let mut tenant = None; #[cfg(feature = "enterprise")] if self.is_enterprise_edition() { if let Some(tenant_id) = principal.tenant { diff --git a/crates/common/src/config/mod.rs b/crates/common/src/config/mod.rs index 34ae7b5b..7c73ed2d 100644 --- a/crates/common/src/config/mod.rs +++ b/crates/common/src/config/mod.rs @@ -189,8 +189,12 @@ impl Core { } Self { + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] enterprise, + // SPDX-SnippetEnd sieve: Scripting::parse(config, &stores).await, network: Network::parse(config), smtp: SmtpConfig::parse(config).await, diff --git a/crates/common/src/config/telemetry.rs b/crates/common/src/config/telemetry.rs index 662a0bab..db395f1c 100644 --- a/crates/common/src/config/telemetry.rs +++ b/crates/common/src/config/telemetry.rs @@ -40,8 +40,12 @@ pub enum TelemetrySubscriberType { Webhook(WebhookTracer), #[cfg(unix)] JournalTracer(crate::telemetry::tracers::journald::Subscriber), + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] StoreTracer(StoreTracer), + // SPDX-SnippetEnd } #[derive(Debug)] @@ -88,11 +92,15 @@ pub struct WebhookTracer { pub headers: HeaderMap, } +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL #[derive(Debug)] #[cfg(feature = "enterprise")] pub struct StoreTracer { pub store: store::Store, } +// SPDX-SnippetEnd #[derive(Debug)] pub enum RotationStrategy { @@ -477,8 +485,12 @@ impl Tracers { TelemetrySubscriberType::JournalTracer(_) => { EventType::Telemetry(TelemetryEvent::JournalError).into() } + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] TelemetrySubscriberType::StoreTracer(_) => None, + // SPDX-SnippetEnd }; // Parse disabled events @@ -509,6 +521,10 @@ impl Tracers { } } + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL + // Parse tracing history #[cfg(feature = "enterprise")] { @@ -540,6 +556,7 @@ impl Tracers { } } } + // SPDX-SnippetEnd // Parse webhooks for id in config.sub_keys("webhook", ".url") { diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 36b35e14..ae8cb1d6 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -51,8 +51,6 @@ pub mod auth; pub mod config; pub mod core; pub mod dns; -#[cfg(feature = "enterprise")] -pub mod enterprise; pub mod expr; pub mod i18n; pub mod ipc; @@ -63,6 +61,15 @@ pub mod sharing; pub mod storage; pub mod telemetry; +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL + +#[cfg(feature = "enterprise")] +pub mod enterprise; + +// SPDX-SnippetEnd + pub use psl; pub static VERSION_PRIVATE: &str = env!("CARGO_PKG_VERSION"); @@ -338,8 +345,13 @@ pub struct Core { pub spam: SpamFilterConfig, pub imap: ImapConfig, pub metrics: Metrics, + + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] pub enterprise: Option, + // SPDX-SnippetEnd } impl CacheItemWeight for CacheSwap { diff --git a/crates/common/src/manager/boot.rs b/crates/common/src/manager/boot.rs index 6bf4e01a..ef9c8c07 100644 --- a/crates/common/src/manager/boot.rs +++ b/crates/common/src/manager/boot.rs @@ -427,8 +427,14 @@ impl BootManager { let cache = Caches::parse(&mut config); // Enable telemetry + + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] telemetry.enable(core.is_enterprise_edition()); + // SPDX-SnippetEnd + #[cfg(not(feature = "enterprise"))] telemetry.enable(false); diff --git a/crates/common/src/manager/mod.rs b/crates/common/src/manager/mod.rs index 4f8cf89e..a2374cf4 100644 --- a/crates/common/src/manager/mod.rs +++ b/crates/common/src/manager/mod.rs @@ -4,14 +4,11 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use std::time::Duration; - -use hyper::HeaderMap; -use utils::HttpLimitResponse; - -use crate::USER_AGENT; - use self::config::ConfigManager; +use crate::USER_AGENT; +use hyper::HeaderMap; +use std::time::Duration; +use utils::HttpLimitResponse; pub mod backup; pub mod boot; @@ -23,9 +20,19 @@ pub mod webadmin; const DEFAULT_SPAMFILTER_URL: &str = "https://github.com/stalwartlabs/spam-filter/releases/latest/download/spam-filter.toml"; +pub const WEBADMIN_KEY: &[u8] = "STALWART_WEBADMIN".as_bytes(); + +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL +#[cfg(feature = "enterprise")] const DEFAULT_WEBADMIN_URL: &str = "https://github.com/stalwartlabs/webadmin/releases/latest/download/webadmin.zip"; -pub const WEBADMIN_KEY: &[u8] = "STALWART_WEBADMIN".as_bytes(); +// SPDX-SnippetEnd + +#[cfg(not(feature = "enterprise"))] +const DEFAULT_WEBADMIN_URL: &str = + "https://github.com/stalwartlabs/webadmin/releases/latest/download/webadmin-oss.zip"; impl ConfigManager { pub async fn fetch_resource(&self, resource_id: &str) -> Result, String> { diff --git a/crates/common/src/telemetry/metrics/mod.rs b/crates/common/src/telemetry/metrics/mod.rs index e7399567..38289598 100644 --- a/crates/common/src/telemetry/metrics/mod.rs +++ b/crates/common/src/telemetry/metrics/mod.rs @@ -7,5 +7,9 @@ pub mod otel; pub mod prometheus; +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] pub mod store; +// SPDX-SnippetEnd diff --git a/crates/common/src/telemetry/metrics/prometheus.rs b/crates/common/src/telemetry/metrics/prometheus.rs index 9747b5af..cdd52436 100644 --- a/crates/common/src/telemetry/metrics/prometheus.rs +++ b/crates/common/src/telemetry/metrics/prometheus.rs @@ -16,8 +16,12 @@ impl Server { pub async fn export_prometheus_metrics(&self) -> trc::Result { let mut metrics = Vec::new(); + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] let is_enterprise = self.is_enterprise_edition(); + // SPDX-SnippetEnd #[cfg(not(feature = "enterprise"))] let is_enterprise = false; diff --git a/crates/common/src/telemetry/mod.rs b/crates/common/src/telemetry/mod.rs index 1c5584cd..64c0348e 100644 --- a/crates/common/src/telemetry/mod.rs +++ b/crates/common/src/telemetry/mod.rs @@ -108,12 +108,15 @@ impl TelemetrySubscriberType { TelemetrySubscriberType::JournalTracer(subscriber) => { tracers::journald::spawn_journald_tracer(builder, subscriber) } + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] TelemetrySubscriberType::StoreTracer(subscriber) => { if is_enterprise { tracers::store::spawn_store_tracer(builder, subscriber) } - } + } // SPDX-SnippetEnd } } } diff --git a/crates/common/src/telemetry/tracers/mod.rs b/crates/common/src/telemetry/tracers/mod.rs index ab1a20f6..02d20bfa 100644 --- a/crates/common/src/telemetry/tracers/mod.rs +++ b/crates/common/src/telemetry/tracers/mod.rs @@ -10,5 +10,9 @@ pub mod log; pub mod otel; pub mod stdout; +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] pub mod store; +// SPDX-SnippetEnd diff --git a/crates/directory/src/backend/internal/manage.rs b/crates/directory/src/backend/internal/manage.rs index 538988a3..5d19134b 100644 --- a/crates/directory/src/backend/internal/manage.rs +++ b/crates/directory/src/backend/internal/manage.rs @@ -646,12 +646,13 @@ impl ManageDirectory for Store { let mut batch = BatchBuilder::new(); batch.with_account_id(u32::MAX); + let tenant = principal.tenant.as_ref().map(|t| t.to_native()); + // SPDX-SnippetBegin // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC // SPDX-License-Identifier: LicenseRef-SEL // Make sure tenant has no data - let tenant = principal.tenant.as_ref().map(|t| t.to_native()); #[cfg(feature = "enterprise")] match typ { Type::Individual | Type::Group => { diff --git a/crates/directory/src/backend/internal/mod.rs b/crates/directory/src/backend/internal/mod.rs index 4de6b193..f413df89 100644 --- a/crates/directory/src/backend/internal/mod.rs +++ b/crates/directory/src/backend/internal/mod.rs @@ -20,24 +20,20 @@ pub struct PrincipalInfo { pub tenant: Option, } -#[cfg(feature = "enterprise")] impl PrincipalInfo { // SPDX-SnippetBegin // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC // SPDX-License-Identifier: LicenseRef-SEL - + #[cfg(feature = "enterprise")] pub fn has_tenant_access(&self, tenant_id: Option) -> bool { tenant_id.is_none_or(|tenant_id| { self.tenant.is_some_and(|t| tenant_id == t) || (self.typ == Type::Tenant && self.id == tenant_id) }) } - // SPDX-SnippetEnd -} -#[cfg(not(feature = "enterprise"))] -impl PrincipalInfo { + #[cfg(not(feature = "enterprise"))] pub fn has_tenant_access(&self, _tenant_id: Option) -> bool { true } diff --git a/crates/directory/src/core/config.rs b/crates/directory/src/core/config.rs index 5d67576b..5203ccda 100644 --- a/crates/directory/src/core/config.rs +++ b/crates/directory/src/core/config.rs @@ -91,6 +91,9 @@ impl Directories { // Build directory if let Some(store) = store { + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] if store.is_enterprise_directory() && !is_enterprise { let message = @@ -98,6 +101,7 @@ impl Directories { config.new_parse_error(("directory", id, "type"), message); continue; } + // SPDX-SnippetEnd let directory = Arc::new(Directory { store, diff --git a/crates/directory/src/core/principal.rs b/crates/directory/src/core/principal.rs index a3d75f6e..7e9d6f67 100644 --- a/crates/directory/src/core/principal.rs +++ b/crates/directory/src/core/principal.rs @@ -75,11 +75,18 @@ impl Principal { // SPDX-SnippetBegin // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC // SPDX-License-Identifier: LicenseRef-SEL + + #[cfg(feature = "enterprise")] pub fn tenant(&self) -> Option { self.tenant } // SPDX-SnippetEnd + #[cfg(not(feature = "enterprise"))] + pub fn tenant(&self) -> Option { + None + } + pub fn description(&self) -> Option<&str> { self.description.as_deref() } diff --git a/crates/groupware/src/calendar/itip.rs b/crates/groupware/src/calendar/itip.rs index 63fe3aa2..4fcdc006 100644 --- a/crates/groupware/src/calendar/itip.rs +++ b/crates/groupware/src/calendar/itip.rs @@ -507,6 +507,9 @@ enum Response { } fn render_response(server: &Server, response: Response, language: &str) -> String { + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] let template = server .core @@ -514,6 +517,8 @@ fn render_response(server: &Server, response: Response, language: &str) -> Strin .as_ref() .and_then(|e| e.template_scheduling_web.as_ref()) .unwrap_or(&server.core.groupware.itip_template); + // SPDX-SnippetEnd + #[cfg(not(feature = "enterprise"))] let template = &server.core.groupware.itip_template; let locale = i18n::locale_or_default(language); diff --git a/crates/http/src/auth/oauth/auth.rs b/crates/http/src/auth/oauth/auth.rs index e70f9c6f..5addd0fc 100644 --- a/crates/http/src/auth/oauth/auth.rs +++ b/crates/http/src/auth/oauth/auth.rs @@ -130,8 +130,13 @@ impl OAuthApiHandler for Server { #[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 json!({ "data": { diff --git a/crates/http/src/management/mod.rs b/crates/http/src/management/mod.rs index 58d44d0c..24c194b5 100644 --- a/crates/http/src/management/mod.rs +++ b/crates/http/src/management/mod.rs @@ -7,8 +7,6 @@ pub mod crypto; pub mod dkim; pub mod dns; -#[cfg(feature = "enterprise")] -pub mod enterprise; pub mod log; pub mod principal; pub mod queue; @@ -19,15 +17,23 @@ pub mod spam; pub mod stores; pub mod troubleshoot; -use std::{str::FromStr, sync::Arc}; +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL +#[cfg(feature = "enterprise")] +pub mod enterprise; +#[cfg(feature = "enterprise")] +use enterprise::telemetry::TelemetryApi; +// SPDX-SnippetEnd + +use crate::auth::oauth::auth::OAuthApiHandler; use common::{Server, auth::AccessToken}; use crypto::CryptoHandler; use directory::{Permission, backend::internal::manage}; use dkim::DkimManagement; use dns::DnsManagement; -#[cfg(feature = "enterprise")] -use enterprise::telemetry::TelemetryApi; +use http_proto::{request::fetch_body, *}; use hyper::{Method, StatusCode, header}; use jmap::api::{ToJmapHttpResponse, ToRequestError}; use jmap_proto::error::request::RequestError; @@ -40,15 +46,12 @@ use report::ManageReports; use serde::Serialize; use settings::ManageSettings; use spam::ManageSpamHandler; +use std::future::Future; +use std::{str::FromStr, sync::Arc}; use store::write::now; use stores::ManageStore; use troubleshoot::TroubleshootApi; -use crate::auth::oauth::auth::OAuthApiHandler; - -use http_proto::{request::fetch_body, *}; -use std::future::Future; - #[derive(Serialize)] #[serde(tag = "error")] #[serde(rename_all = "camelCase")] diff --git a/crates/http/src/management/principal.rs b/crates/http/src/management/principal.rs index eae36e3d..58e37f80 100644 --- a/crates/http/src/management/principal.rs +++ b/crates/http/src/management/principal.rs @@ -252,12 +252,11 @@ impl PrincipalManager for Server { })?; } + let mut tenant = access_token.tenant.map(|t| t.id); + // SPDX-SnippetBegin // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC // SPDX-License-Identifier: LicenseRef-SEL - - let mut tenant = access_token.tenant.map(|t| t.id); - #[cfg(feature = "enterprise")] if self.core.is_enterprise_edition() { if tenant.is_none() { @@ -276,7 +275,6 @@ impl PrincipalManager for Server { } else if types.contains(&Type::Tenant) { return Err(manage::enterprise()); } - // SPDX-SnippetEnd let principals = self @@ -347,6 +345,9 @@ impl PrincipalManager for Server { let mut tenant = access_token.tenant.map(|t| t.id); + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] if self.core.is_enterprise_edition() { if tenant.is_none() { @@ -365,6 +366,7 @@ impl PrincipalManager for Server { } else if typ == Type::Tenant { return Err(manage::enterprise()); } + // SPDX-SnippetEnd let principals = self .store() diff --git a/crates/http/src/management/queue.rs b/crates/http/src/management/queue.rs index 3bc2d459..5e496b12 100644 --- a/crates/http/src/management/queue.rs +++ b/crates/http/src/management/queue.rs @@ -137,13 +137,13 @@ impl QueueManagement for Server { access_token: &AccessToken, ) -> trc::Result { let params = UrlParams::new(req.uri().query()); + let mut tenant_domains: Option> = None; // SPDX-SnippetBegin // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC // SPDX-License-Identifier: LicenseRef-SEL // Limit to tenant domains - let mut tenant_domains: Option> = None; #[cfg(feature = "enterprise")] if self.core.is_enterprise_edition() { if let Some(tenant) = access_token.tenant { diff --git a/crates/http/src/management/reload.rs b/crates/http/src/management/reload.rs index 46f5d624..1763f32d 100644 --- a/crates/http/src/management/reload.rs +++ b/crates/http/src/management/reload.rs @@ -83,8 +83,13 @@ impl ManageReload for Server { if let Some(tracers) = result.tracers { // Update tracers + + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] tracers.update(self.inner.shared_core.load().is_enterprise_edition()); + // SPDX-SnippetEnd #[cfg(not(feature = "enterprise"))] tracers.update(false); } diff --git a/crates/http/src/management/report.rs b/crates/http/src/management/report.rs index bb80d21f..8f755f47 100644 --- a/crates/http/src/management/report.rs +++ b/crates/http/src/management/report.rs @@ -46,12 +46,12 @@ impl ManageReports for Server { path: Vec<&str>, access_token: &AccessToken, ) -> trc::Result { + let mut tenant_domains: Option> = None; // SPDX-SnippetBegin // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC // SPDX-License-Identifier: LicenseRef-SEL // Limit to tenant domains - let mut tenant_domains: Option> = None; #[cfg(feature = "enterprise")] if self.core.is_enterprise_edition() { if let Some(tenant) = access_token.tenant { diff --git a/crates/http/src/management/stores.rs b/crates/http/src/management/stores.rs index c1d4b96c..15b76478 100644 --- a/crates/http/src/management/stores.rs +++ b/crates/http/src/management/stores.rs @@ -17,10 +17,12 @@ use directory::{ backend::internal::manage::{self, ManageDirectory}, }; use email::message::{ingest::EmailIngest, metadata::MessageData}; +use http_proto::{request::decode_path_element, *}; use hyper::Method; use jmap_proto::types::{collection::Collection, property::Property}; use serde_json::json; use services::task_manager::fts::FtsIndexTask; +use std::future::Future; use store::{ Serialize, rand, write::{Archiver, BatchBuilder, ValueClass}, @@ -28,11 +30,12 @@ use store::{ use trc::AddContext; use utils::url_params::UrlParams; -use http_proto::{request::decode_path_element, *}; - +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] use super::enterprise::undelete::UndeleteApi; -use std::future::Future; +// SPDX-SnippetEnd pub trait ManageStore: Sync + Send { fn handle_manage_store( diff --git a/crates/http/src/request.rs b/crates/http/src/request.rs index 72968eec..d6a3d16d 100644 --- a/crates/http/src/request.rs +++ b/crates/http/src/request.rs @@ -581,12 +581,11 @@ impl ParseHttp for Server { } _ => (), }, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] "logo.svg" if self.is_enterprise_edition() => { - // SPDX-SnippetBegin - // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC - // SPDX-License-Identifier: LicenseRef-SEL - match self .logo_resource( req.headers() @@ -611,9 +610,8 @@ impl ParseHttp for Server { if !resource.is_empty() { return Ok(resource.into_http_response()); } - - // SPDX-SnippetEnd } + // SPDX-SnippetEnd "form" => { if let Some(form) = &self.core.network.contact_form { match *req.method() { diff --git a/crates/main/Cargo.toml b/crates/main/Cargo.toml index a5ce675f..aaba1a49 100644 --- a/crates/main/Cargo.toml +++ b/crates/main/Cargo.toml @@ -65,4 +65,5 @@ enterprise = [ "jmap/enterprise", "http/enterprise", "dav/enterprise", "groupware/enterprise", + "trc/enterprise", "services/enterprise" ] diff --git a/crates/main/src/main.rs b/crates/main/src/main.rs index 95e8dfe7..7b8ec878 100644 --- a/crates/main/src/main.rs +++ b/crates/main/src/main.rs @@ -50,9 +50,13 @@ async fn main() -> std::io::Result<()> { init.config.log_errors(); init.config.log_warnings(); + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL // Log licensing information #[cfg(feature = "enterprise")] init.inner.build_server().log_license_details(); + // SPDX-SnippetEnd // Spawn servers let (shutdown_tx, shutdown_rx) = init.servers.spawn(|server, acceptor, shutdown_rx| { diff --git a/crates/services/src/housekeeper/mod.rs b/crates/services/src/housekeeper/mod.rs index b0b997a3..37281ba5 100644 --- a/crates/services/src/housekeeper/mod.rs +++ b/crates/services/src/housekeeper/mod.rs @@ -4,31 +4,33 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use std::{ - collections::BinaryHeap, - future::Future, - sync::Arc, - time::{Duration, Instant, SystemTime}, -}; - use common::{ Inner, KV_LOCK_HOUSEKEEPER, LONG_1D_SLUMBER, Server, config::telemetry::OtelMetrics, core::BuildServer, ipc::{BroadcastEvent, HousekeeperEvent, PurgeType}, }; +use email::message::delete::EmailDeletion; +use smtp::reporting::SmtpReporting; +use std::{ + collections::BinaryHeap, + future::Future, + sync::Arc, + time::{Duration, Instant, SystemTime}, +}; +use store::{PurgeStore, write::now}; +use tokio::sync::mpsc; +use trc::{Collector, MetricType, PurgeEvent}; +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] use common::telemetry::{ metrics::store::{MetricsStore, SharedMetricHistory}, tracers::store::TracingStore, }; - -use email::message::delete::EmailDeletion; -use smtp::reporting::SmtpReporting; -use store::{PurgeStore, write::now}; -use tokio::sync::mpsc; -use trc::{Collector, MetricType, PurgeEvent}; +// SPDX-SnippetEnd #[derive(PartialEq, Eq)] struct Action { @@ -42,13 +44,17 @@ enum ActionClass { Store(usize), Acme(String), OtelMetrics, + CalculateMetrics, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] InternalMetrics, - CalculateMetrics, #[cfg(feature = "enterprise")] AlertMetrics, #[cfg(feature = "enterprise")] RenewLicense, + // SPDX-SnippetEnd } #[derive(Default)] @@ -56,8 +62,12 @@ struct Queue { heap: BinaryHeap, } +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] const METRIC_ALERTS_INTERVAL: Duration = Duration::from_secs(5 * 60); +// SPDX-SnippetEnd pub fn spawn_housekeeper(inner: Arc, mut rx: mpsc::Receiver) { tokio::spawn(async move { @@ -143,12 +153,18 @@ pub fn spawn_housekeeper(inner: Arc, mut rx: mpsc::Receiver + // SPDX-License-Identifier: LicenseRef-SEL // Metrics history #[cfg(feature = "enterprise")] let metrics_history = SharedMetricHistory::default(); + // SPDX-SnippetEnd + let mut next_metric_update = Instant::now(); loop { @@ -383,8 +399,12 @@ pub fn spawn_housekeeper(inner: Arc, mut rx: mpsc::Receiver + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] let is_enterprise = server.is_enterprise_edition(); + // SPDX-SnippetEnd #[cfg(not(feature = "enterprise"))] let is_enterprise = false; @@ -417,6 +437,9 @@ pub fn spawn_housekeeper(inner: Arc, mut rx: mpsc::Receiver + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] if server.is_enterprise_edition() { // Obtain queue size @@ -434,6 +457,7 @@ pub fn spawn_housekeeper(inner: Arc, mut rx: mpsc::Receiver + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] let template = server .core @@ -438,6 +441,8 @@ async fn build_template( .as_ref() .and_then(|e| e.template_calendar_alarm.as_ref()) .unwrap_or(&server.core.groupware.alarms_template); + // SPDX-SnippetEnd + #[cfg(not(feature = "enterprise"))] let template = &server.core.groupware.alarms_template; let locale = i18n::locale_or_default(access_token.locale.as_deref().unwrap_or("en")); diff --git a/crates/services/src/task_manager/imip.rs b/crates/services/src/task_manager/imip.rs index 5d1f1158..c3342948 100644 --- a/crates/services/src/task_manager/imip.rs +++ b/crates/services/src/task_manager/imip.rs @@ -310,6 +310,9 @@ pub async fn build_itip_template( summary: &ArchivedItipSummary, logo_cid: &str, ) -> Details { + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] let template = server .core @@ -317,6 +320,7 @@ pub async fn build_itip_template( .as_ref() .and_then(|e| e.template_scheduling_email.as_ref()) .unwrap_or(&server.core.groupware.itip_template); + // SPDX-SnippetEnd #[cfg(not(feature = "enterprise"))] let template = &server.core.groupware.itip_template; let locale = i18n::locale_or_default(access_token.locale.as_deref().unwrap_or("en")); diff --git a/crates/spam-filter/src/analysis/mod.rs b/crates/spam-filter/src/analysis/mod.rs index b7cc1ba0..abde7882 100644 --- a/crates/spam-filter/src/analysis/mod.rs +++ b/crates/spam-filter/src/analysis/mod.rs @@ -27,8 +27,6 @@ pub mod headers; pub mod html; pub mod init; pub mod ip; -#[cfg(feature = "enterprise")] -pub mod llm; pub mod messageid; pub mod mime; pub mod pyzor; @@ -42,6 +40,13 @@ pub mod subject; pub mod trusted_reply; pub mod url; +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL +#[cfg(feature = "enterprise")] +pub mod llm; +// SPDX-SnippetEnd + impl SpamFilterInput<'_> { pub fn header_as_address(&self, header: &Header<'_>) -> Option> { self.message diff --git a/crates/spam-filter/src/analysis/score.rs b/crates/spam-filter/src/analysis/score.rs index e61b762e..b14e6841 100644 --- a/crates/spam-filter/src/analysis/score.rs +++ b/crates/spam-filter/src/analysis/score.rs @@ -4,9 +4,6 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use common::{Server, config::spamfilter::SpamFilterAction}; -use std::{fmt::Write, future::Future, vec}; - use crate::{ SpamFilterContext, analysis::{ @@ -22,9 +19,15 @@ use crate::{ }, modules::bayes::BayesClassifier, }; +use common::{Server, config::spamfilter::SpamFilterAction}; +use std::{fmt::Write, future::Future, vec}; +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] use crate::analysis::llm::SpamFilterAnalyzeLlm; +// SPDX-SnippetEnd pub trait SpamFilterAnalyzeScore: Sync + Send { fn spam_filter_score( @@ -190,10 +193,16 @@ impl SpamFilterAnalyzeScore for Server { // HTML content analysis self.spam_filter_analyze_html(ctx).await; + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL + // LLM classification #[cfg(feature = "enterprise")] self.spam_filter_analyze_llm(ctx).await; + // SPDX-SnippetEnd + // Trusted reply analysis self.spam_filter_analyze_reply_in(ctx).await; diff --git a/crates/store/src/backend/composite/sharded_blob.rs b/crates/store/src/backend/composite/sharded_blob.rs index 7bd558c6..99f20114 100644 --- a/crates/store/src/backend/composite/sharded_blob.rs +++ b/crates/store/src/backend/composite/sharded_blob.rs @@ -71,11 +71,15 @@ impl ShardedBlob { Store::MySQL(store) => store.get_blob(key, read_range).await, #[cfg(feature = "rocks")] Store::RocksDb(store) => store.get_blob(key, read_range).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all( feature = "enterprise", any(feature = "postgres", feature = "mysql") ))] Store::SQLReadReplica(store) => store.get_blob(key, read_range).await, + // SPDX-SnippetEnd Store::None => Err(trc::StoreEvent::NotConfigured.into()), }, BlobBackend::Fs(store) => store.get_blob(key, read_range).await, @@ -103,10 +107,14 @@ impl ShardedBlob { Store::MySQL(store) => store.put_blob(key, data).await, #[cfg(feature = "rocks")] Store::RocksDb(store) => store.put_blob(key, data).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all( feature = "enterprise", any(feature = "postgres", feature = "mysql") ))] + // SPDX-SnippetEnd Store::SQLReadReplica(store) => store.put_blob(key, data).await, Store::None => Err(trc::StoreEvent::NotConfigured.into()), }, @@ -135,11 +143,15 @@ impl ShardedBlob { Store::MySQL(store) => store.delete_blob(key).await, #[cfg(feature = "rocks")] Store::RocksDb(store) => store.delete_blob(key).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all( feature = "enterprise", any(feature = "postgres", feature = "mysql") ))] Store::SQLReadReplica(store) => store.delete_blob(key).await, + // SPDX-SnippetEnd Store::None => Err(trc::StoreEvent::NotConfigured.into()), }, BlobBackend::Fs(store) => store.delete_blob(key).await, diff --git a/crates/store/src/backend/mod.rs b/crates/store/src/backend/mod.rs index b3462b27..bac97ce4 100644 --- a/crates/store/src/backend/mod.rs +++ b/crates/store/src/backend/mod.rs @@ -6,8 +6,6 @@ #[cfg(feature = "azure")] pub mod azure; -#[cfg(feature = "enterprise")] -pub mod composite; #[cfg(feature = "elastic")] pub mod elastic; #[cfg(feature = "foundation")] @@ -34,6 +32,13 @@ pub mod sqlite; #[cfg(feature = "zenoh")] pub mod zenoh; +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL +#[cfg(feature = "enterprise")] +pub mod composite; +// SPDX-SnippetEnd + pub const MAX_TOKEN_LENGTH: usize = (u8::MAX >> 1) as usize; pub const MAX_TOKEN_MASK: usize = MAX_TOKEN_LENGTH - 1; diff --git a/crates/store/src/config.rs b/crates/store/src/config.rs index 9f4d63e2..7bd2cded 100644 --- a/crates/store/src/config.rs +++ b/crates/store/src/config.rs @@ -10,6 +10,9 @@ use crate::{ }; use utils::config::{Config, cron::SimpleCron, utils::ParseValue}; +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] enum CompositeStore { #[cfg(any(feature = "postgres", feature = "mysql"))] @@ -17,6 +20,7 @@ enum CompositeStore { ShardedBlob(String), ShardedInMemory(String), } +// SPDX-SnippetEnd impl Stores { pub async fn parse_all(config: &mut Config, is_reload: bool) -> Self { @@ -33,8 +37,13 @@ impl Stores { pub async fn parse_stores(&mut self, config: &mut Config) { let is_reload = !self.stores.is_empty(); + + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] let mut composite_stores = Vec::new(); + // SPDX-SnippetEnd for store_id in config.sub_keys("store", ".type") { let id = store_id.as_str(); @@ -239,6 +248,9 @@ impl Stores { .insert(store_id, crate::PubSubStore::Kafka(db)); } } + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] "sql-read-replica" => { #[cfg(any(feature = "postgres", feature = "mysql"))] @@ -252,6 +264,7 @@ impl Stores { "sharded-in-memory" => { composite_stores.push(CompositeStore::ShardedInMemory(store_id)); } + // SPDX-SnippetEnd #[cfg(feature = "azure")] "azure" => { if let Some(db) = crate::backend::azure::AzureStore::open(config, prefix) @@ -271,6 +284,9 @@ impl Stores { } } + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] for composite_store in composite_stores { match composite_store { @@ -332,6 +348,8 @@ impl Stores { } } } + + // SPDX-SnippetEnd } pub async fn parse_in_memory(&mut self, config: &mut Config, is_reload: bool) { diff --git a/crates/store/src/dispatch/blob.rs b/crates/store/src/dispatch/blob.rs index 5aacf48a..858f5e27 100644 --- a/crates/store/src/dispatch/blob.rs +++ b/crates/store/src/dispatch/blob.rs @@ -30,8 +30,12 @@ impl BlobStore { Store::MySQL(store) => store.get_blob(key, read_range).await, #[cfg(feature = "rocks")] Store::RocksDb(store) => store.get_blob(key, read_range).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Store::SQLReadReplica(store) => store.get_blob(key, read_range).await, + // SPDX-SnippetEnd Store::None => Err(trc::StoreEvent::NotConfigured.into()), }, BlobBackend::Fs(store) => store.get_blob(key, read_range).await, @@ -39,8 +43,12 @@ impl BlobStore { BlobBackend::S3(store) => store.get_blob(key, read_range).await, #[cfg(feature = "azure")] BlobBackend::Azure(store) => store.get_blob(key, read_range).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] BlobBackend::Sharded(store) => store.get_blob(key, read_range).await, + // SPDX-SnippetEnd }; trc::event!( @@ -112,8 +120,12 @@ impl BlobStore { Store::MySQL(store) => store.put_blob(key, data.as_ref()).await, #[cfg(feature = "rocks")] Store::RocksDb(store) => store.put_blob(key, data.as_ref()).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Store::SQLReadReplica(store) => store.put_blob(key, data.as_ref()).await, + // SPDX-SnippetEnd Store::None => Err(trc::StoreEvent::NotConfigured.into()), }, BlobBackend::Fs(store) => store.put_blob(key, data.as_ref()).await, @@ -121,8 +133,12 @@ impl BlobStore { BlobBackend::S3(store) => store.put_blob(key, data.as_ref()).await, #[cfg(feature = "azure")] BlobBackend::Azure(store) => store.put_blob(key, data.as_ref()).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] BlobBackend::Sharded(store) => store.put_blob(key, data.as_ref()).await, + // SPDX-SnippetEnd } .caused_by(trc::location!()); @@ -150,8 +166,12 @@ impl BlobStore { Store::MySQL(store) => store.delete_blob(key).await, #[cfg(feature = "rocks")] Store::RocksDb(store) => store.delete_blob(key).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Store::SQLReadReplica(store) => store.delete_blob(key).await, + // SPDX-SnippetEnd Store::None => Err(trc::StoreEvent::NotConfigured.into()), }, BlobBackend::Fs(store) => store.delete_blob(key).await, @@ -159,8 +179,12 @@ impl BlobStore { BlobBackend::S3(store) => store.delete_blob(key).await, #[cfg(feature = "azure")] BlobBackend::Azure(store) => store.delete_blob(key).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] BlobBackend::Sharded(store) => store.delete_blob(key).await, + // SPDX-SnippetEnd } .caused_by(trc::location!()); diff --git a/crates/store/src/dispatch/lookup.rs b/crates/store/src/dispatch/lookup.rs index d0d36559..fc4d6c2f 100644 --- a/crates/store/src/dispatch/lookup.rs +++ b/crates/store/src/dispatch/lookup.rs @@ -49,8 +49,12 @@ impl InMemoryStore { } #[cfg(feature = "redis")] InMemoryStore::Redis(store) => store.key_set(&kv.key, &kv.value, kv.expires).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] InMemoryStore::Sharded(store) => store.key_set(kv).await, + // SPDX-SnippetEnd InMemoryStore::Static(_) | InMemoryStore::Http(_) => { Err(trc::StoreEvent::NotSupported.into_err()) } @@ -97,8 +101,12 @@ impl InMemoryStore { } #[cfg(feature = "redis")] InMemoryStore::Redis(store) => store.key_incr(&kv.key, kv.value, kv.expires).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] InMemoryStore::Sharded(store) => store.counter_incr(kv).await, + // SPDX-SnippetEnd InMemoryStore::Static(_) | InMemoryStore::Http(_) => { Err(trc::StoreEvent::NotSupported.into_err()) } @@ -118,8 +126,12 @@ impl InMemoryStore { } #[cfg(feature = "redis")] InMemoryStore::Redis(store) => store.key_delete(key.into().as_bytes()).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] InMemoryStore::Sharded(store) => store.key_delete(key).await, + // SPDX-SnippetEnd InMemoryStore::Static(_) | InMemoryStore::Http(_) => { Err(trc::StoreEvent::NotSupported.into_err()) } @@ -139,8 +151,12 @@ impl InMemoryStore { } #[cfg(feature = "redis")] InMemoryStore::Redis(store) => store.key_delete(key.into().as_bytes()).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] InMemoryStore::Sharded(store) => store.counter_delete(key).await, + // SPDX-SnippetEnd InMemoryStore::Static(_) | InMemoryStore::Http(_) => { Err(trc::StoreEvent::NotSupported.into_err()) } @@ -180,8 +196,12 @@ impl InMemoryStore { } #[cfg(feature = "redis")] InMemoryStore::Redis(store) => store.key_delete_prefix(prefix).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] InMemoryStore::Sharded(store) => store.key_delete_prefix(prefix).await, + // SPDX-SnippetEnd InMemoryStore::Static(_) | InMemoryStore::Http(_) => { Err(trc::StoreEvent::NotSupported.into_err()) } @@ -202,8 +222,12 @@ impl InMemoryStore { .map(|value| value.and_then(|v| v.into())), #[cfg(feature = "redis")] InMemoryStore::Redis(store) => store.key_get(key.into().as_bytes()).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] InMemoryStore::Sharded(store) => store.key_get(key).await, + // SPDX-SnippetEnd InMemoryStore::Static(store) => Ok(store .get(key.into().as_str()) .map(|value| T::from(value.clone()))), @@ -225,8 +249,12 @@ impl InMemoryStore { } #[cfg(feature = "redis")] InMemoryStore::Redis(store) => store.counter_get(key.into().as_bytes()).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] InMemoryStore::Sharded(store) => store.counter_get(key).await, + // SPDX-SnippetEnd InMemoryStore::Static(_) | InMemoryStore::Http(_) => { Err(trc::StoreEvent::NotSupported.into_err()) } @@ -244,8 +272,12 @@ impl InMemoryStore { .map(|value| matches!(value, Some(LookupValue::Value(())))), #[cfg(feature = "redis")] InMemoryStore::Redis(store) => store.key_exists(key.into().as_bytes()).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] InMemoryStore::Sharded(store) => store.key_exists(key).await, + // SPDX-SnippetEnd InMemoryStore::Static(store) => Ok(store.get(key.into().as_str()).is_some()), InMemoryStore::Http(store) => Ok(store.contains(key.into().as_str())), } @@ -345,11 +377,15 @@ impl InMemoryStore { .key_incr(&KeyValue::<()>::build_key(prefix, key), 1, duration.into()) .await .map(|count| count == 1), + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] InMemoryStore::Sharded(store) => store .counter_incr(KeyValue::with_prefix(prefix, key, 1).expires(duration)) .await .map(|count| count == 1), + // SPDX-SnippetEnd InMemoryStore::Static(_) | InMemoryStore::Http(_) => { Err(trc::StoreEvent::NotSupported.into_err()) } @@ -443,8 +479,12 @@ impl InMemoryStore { } #[cfg(feature = "redis")] InMemoryStore::Redis(_) => {} + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] InMemoryStore::Sharded(_) => {} + // SPDX-SnippetEnd InMemoryStore::Static(_) | InMemoryStore::Http(_) => {} } diff --git a/crates/store/src/dispatch/mod.rs b/crates/store/src/dispatch/mod.rs index a796b0bc..77d1cde6 100644 --- a/crates/store/src/dispatch/mod.rs +++ b/crates/store/src/dispatch/mod.rs @@ -27,8 +27,12 @@ impl Store { Self::MySQL(_) => "mysql", #[cfg(feature = "rocks")] Self::RocksDb(_) => "rocksdb", + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(_) => "read_replica", + // SPDX-SnippetEnd Self::None => "none", } } diff --git a/crates/store/src/dispatch/store.rs b/crates/store/src/dispatch/store.rs index 0485da17..8b398048 100644 --- a/crates/store/src/dispatch/store.rs +++ b/crates/store/src/dispatch/store.rs @@ -53,8 +53,12 @@ impl Store { Self::MySQL(store) => store.get_value(key).await, #[cfg(feature = "rocks")] Self::RocksDb(store) => store.get_value(key).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(store) => store.get_value(key).await, + // SPDX-SnippetEnd Self::None => Err(trc::StoreEvent::NotConfigured.into()), } .caused_by(trc::location!()) @@ -75,8 +79,12 @@ impl Store { Self::MySQL(store) => store.get_bitmap(key).await, #[cfg(feature = "rocks")] Self::RocksDb(store) => store.get_bitmap(key).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(store) => store.get_bitmap(key).await, + // SPDX-SnippetEnd Self::None => Err(trc::StoreEvent::NotConfigured.into()), } .caused_by(trc::location!()) @@ -121,8 +129,12 @@ impl Store { Self::MySQL(store) => store.iterate(params, cb).await, #[cfg(feature = "rocks")] Self::RocksDb(store) => store.iterate(params, cb).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(store) => store.iterate(params, cb).await, + // SPDX-SnippetEnd Self::None => Err(trc::StoreEvent::NotConfigured.into()), } .caused_by(trc::location!()); @@ -150,8 +162,12 @@ impl Store { Self::MySQL(store) => store.get_counter(key).await, #[cfg(feature = "rocks")] Self::RocksDb(store) => store.get_counter(key).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(store) => store.get_counter(key).await, + // SPDX-SnippetEnd Self::None => Err(trc::StoreEvent::NotConfigured.into()), } .caused_by(trc::location!()) @@ -199,8 +215,12 @@ impl Store { Self::MySQL(store) => store.write(batch).await, #[cfg(feature = "rocks")] Self::RocksDb(store) => store.write(batch).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(store) => store.write(batch).await, + // SPDX-SnippetEnd Self::None => Err(trc::StoreEvent::NotConfigured.into()), }; @@ -275,8 +295,12 @@ impl Store { Self::MySQL(store) => store.purge_store().await, #[cfg(feature = "rocks")] Self::RocksDb(store) => store.purge_store().await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(store) => store.purge_store().await, + // SPDX-SnippetEnd Self::None => Err(trc::StoreEvent::NotConfigured.into()), } .caused_by(trc::location!()) @@ -294,8 +318,12 @@ impl Store { Self::MySQL(store) => store.delete_range(from, to).await, #[cfg(feature = "rocks")] Self::RocksDb(store) => store.delete_range(from, to).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(store) => store.delete_range(from, to).await, + // SPDX-SnippetEnd Self::None => Err(trc::StoreEvent::NotConfigured.into()), } .caused_by(trc::location!()) @@ -450,8 +478,12 @@ impl Store { Self::MySQL(store) => store.get_blob(key, range).await, #[cfg(feature = "rocks")] Self::RocksDb(store) => store.get_blob(key, range).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(store) => store.get_blob(key, range).await, + // SPDX-SnippetEnd Self::None => Err(trc::StoreEvent::NotConfigured.into()), } .caused_by(trc::location!()) @@ -469,8 +501,12 @@ impl Store { Self::MySQL(store) => store.put_blob(key, data).await, #[cfg(feature = "rocks")] Self::RocksDb(store) => store.put_blob(key, data).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(store) => store.put_blob(key, data).await, + // SPDX-SnippetEnd Self::None => Err(trc::StoreEvent::NotConfigured.into()), } .caused_by(trc::location!()) @@ -488,8 +524,12 @@ impl Store { Self::MySQL(store) => store.delete_blob(key).await, #[cfg(feature = "rocks")] Self::RocksDb(store) => store.delete_blob(key).await, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(store) => store.delete_blob(key).await, + // SPDX-SnippetEnd Self::None => Err(trc::StoreEvent::NotConfigured.into()), } .caused_by(trc::location!()) diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs index efb1ed5f..1c0e74b5 100644 --- a/crates/store/src/lib.rs +++ b/crates/store/src/lib.rs @@ -173,8 +173,12 @@ pub enum Store { MySQL(Arc), #[cfg(feature = "rocks")] RocksDb(Arc), + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] SQLReadReplica(Arc), + // SPDX-SnippetEnd #[default] None, } @@ -199,8 +203,12 @@ pub enum BlobBackend { S3(Arc), #[cfg(feature = "azure")] Azure(Arc), + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] Sharded(Arc), + // SPDX-SnippetEnd } #[derive(Clone)] @@ -217,8 +225,12 @@ pub enum InMemoryStore { Redis(Arc), Http(Arc), Static(Arc), + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] Sharded(Arc), + // SPDX-SnippetEnd } #[derive(Clone, Default)] @@ -706,8 +718,12 @@ impl Store { Store::PostgreSQL(_) => true, #[cfg(feature = "mysql")] Store::MySQL(_) => true, + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Store::SQLReadReplica(_) => true, + // SPDX-SnippetEnd _ => false, } } @@ -722,6 +738,9 @@ impl Store { } } + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] pub fn is_enterprise_store(&self) -> bool { match self { @@ -730,6 +749,7 @@ impl Store { _ => false, } } + // SPDX-SnippetEnd #[cfg(not(feature = "enterprise"))] pub fn is_enterprise_store(&self) -> bool { @@ -750,8 +770,13 @@ impl std::fmt::Debug for Store { Self::MySQL(_) => f.debug_tuple("MySQL").finish(), #[cfg(feature = "rocks")] Self::RocksDb(_) => f.debug_tuple("RocksDb").finish(), + + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(all(feature = "enterprise", any(feature = "postgres", feature = "mysql")))] Self::SQLReadReplica(_) => f.debug_tuple("SQLReadReplica").finish(), + // SPDX-SnippetEnd Self::None => f.debug_tuple("None").finish(), } } @@ -781,6 +806,9 @@ impl From> for () { impl Stores { pub fn disable_enterprise_only(&mut self) { + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs LLC + // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] { #[cfg(any(feature = "postgres", feature = "mysql"))] @@ -789,5 +817,6 @@ impl Stores { self.blob_stores .retain(|_, store| !matches!(store.backend, BlobBackend::Sharded(_))); } + // SPDX-SnippetEnd } } diff --git a/crates/trc/Cargo.toml b/crates/trc/Cargo.toml index 14386ef4..283c9def 100644 --- a/crates/trc/Cargo.toml +++ b/crates/trc/Cargo.toml @@ -21,5 +21,6 @@ compact_str = "0.9.0" [features] test_mode = [] +enterprise = [] [dev-dependencies] diff --git a/crates/trc/src/serializers/binary.rs b/crates/trc/src/serializers/binary.rs index 1a9d8fdd..c0795bbe 100644 --- a/crates/trc/src/serializers/binary.rs +++ b/crates/trc/src/serializers/binary.rs @@ -8,11 +8,9 @@ * */ -use std::net::{Ipv4Addr, Ipv6Addr}; - -use compact_str::format_compact; - use crate::*; +use compact_str::format_compact; +use std::net::{Ipv4Addr, Ipv6Addr}; const VERSION: u8 = 1; diff --git a/crates/trc/src/serializers/mod.rs b/crates/trc/src/serializers/mod.rs index 0a076497..4eb40bbe 100644 --- a/crates/trc/src/serializers/mod.rs +++ b/crates/trc/src/serializers/mod.rs @@ -4,6 +4,12 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -pub mod binary; pub mod json; pub mod text; + +// SPDX-SnippetBegin +// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC +// SPDX-License-Identifier: LicenseRef-SEL +#[cfg(feature = "enterprise")] +pub mod binary; +// SPDX-SnippetEnd diff --git a/resources/scripts/ossify.py b/resources/scripts/ossify.py new file mode 100644 index 00000000..035a2841 --- /dev/null +++ b/resources/scripts/ossify.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python3 +""" +Stalwart SEL code remover + +This script removes SEL code from the Stalwart codebase by: +1. Removing entire .rs files that contain "SPDX-License-Identifier: LicenseRef-SEL" in their first comment +2. Removing SEL snippets marked with SPDX-SnippetBegin/End from mixed files + +Usage: python ossify.py /crates +""" + +import os +import sys +import re +import argparse +from pathlib import Path +from typing import List, Tuple, Optional + + +def find_first_comment_block(content: str) -> Optional[str]: + """ + Find the first comment block in a Rust file. + Returns the comment content or None if no comment block is found. + """ + # Remove leading whitespace and find the first comment + lines = content.strip().split('\n') + + if not lines: + return None + + first_line = lines[0].strip() + + # Check for block comment starting with /* + if first_line.startswith('/*'): + comment_lines = [] + in_comment = True + + for line in lines: + if in_comment: + comment_lines.append(line) + if '*/' in line: + break + + return '\n'.join(comment_lines) + + # Check for line comments starting with // + elif first_line.startswith('//'): + comment_lines = [] + + for line in lines: + stripped = line.strip() + if stripped.startswith('//'): + comment_lines.append(line) + elif stripped == '': + comment_lines.append(line) # Keep empty lines within comment block + else: + break # Stop at first non-comment, non-empty line + + return '\n'.join(comment_lines) + + return None + + +def should_remove_file(file_path: str) -> bool: + """ + Check if a .rs file should be completely removed based on its first comment. + Returns True if the file contains "SPDX-License-Identifier: LicenseRef-SEL" in the first comment. + """ + try: + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + first_comment = find_first_comment_block(content) + if first_comment and 'SPDX-License-Identifier: LicenseRef-SEL' in first_comment: + return True + + except Exception as e: + print(f"Error reading file {file_path}: {e}") + + return False + + +def remove_proprietary_snippets(content: str) -> Tuple[str, int]: + """ + Remove proprietary snippets from file content. + Returns tuple of (modified_content, number_of_snippets_removed) + """ + snippets_removed = 0 + + # Pattern to match SPDX snippets that contain LicenseRef-SEL + # We look for SPDX-SnippetBegin, then check if the snippet contains LicenseRef-SEL, + # and if so, remove everything until SPDX-SnippetEnd + + lines = content.split('\n') + result_lines = [] + i = 0 + + while i < len(lines): + line = lines[i] + + # Check if this line starts a snippet + if '// SPDX-SnippetBegin' in line: + # Look ahead to see if this snippet contains LicenseRef-SEL + snippet_start = i + snippet_lines = [] + j = i + + # Collect the snippet lines until we find SnippetEnd or reach end of file + while j < len(lines): + snippet_lines.append(lines[j]) + if '// SPDX-SnippetEnd' in lines[j]: + break + j += 1 + + # Check if this snippet contains LicenseRef-SEL + snippet_content = '\n'.join(snippet_lines) + if 'SPDX-License-Identifier: LicenseRef-SEL' in snippet_content: + # Remove this snippet + snippets_removed += 1 + i = j + 1 # Skip past the SnippetEnd line + continue + else: + # Keep this snippet as it's not proprietary + result_lines.append(line) + i += 1 + else: + result_lines.append(line) + i += 1 + + return '\n'.join(result_lines), snippets_removed + + +def process_rust_file(file_path: str, dry_run: bool = False) -> dict: + """ + Process a single Rust file, removing proprietary content. + Returns a dictionary with processing results. + """ + result = { + 'file': file_path, + 'action': 'none', + 'snippets_removed': 0, + 'error': None + } + + try: + # Check if the entire file should be removed + if should_remove_file(file_path): + result['action'] = 'file_removed' + if not dry_run: + os.remove(file_path) + return result + + # Process snippets in the file + with open(file_path, 'r', encoding='utf-8') as f: + original_content = f.read() + + modified_content, snippets_removed = remove_proprietary_snippets(original_content) + + if snippets_removed > 0: + result['action'] = 'snippets_removed' + result['snippets_removed'] = snippets_removed + + if not dry_run: + with open(file_path, 'w', encoding='utf-8') as f: + f.write(modified_content) + + except Exception as e: + result['error'] = str(e) + + return result + + +def find_rust_files(directory: str) -> List[str]: + """Find all .rs files in the given directory recursively.""" + rust_files = [] + + for root, dirs, files in os.walk(directory): + for file in files: + if file.endswith('.rs'): + rust_files.append(os.path.join(root, file)) + + return rust_files + + +def main(): + parser = argparse.ArgumentParser( + description='Remove Enterprise licensed code from Stalwart codebase' + ) + parser.add_argument( + 'directory', + help='Directory containing Stalwart code to process' + ) + parser.add_argument( + '--dry-run', + action='store_true', + help='Show what would be done without making changes' + ) + parser.add_argument( + '--verbose', + action='store_true', + help='Show detailed output for each file' + ) + + args = parser.parse_args() + + if not os.path.isdir(args.directory): + print(f"Error: {args.directory} is not a valid directory") + sys.exit(1) + + print(f"Processing Rust files in: {args.directory}") + if args.dry_run: + print("DRY RUN MODE - No changes will be made") + print() + + rust_files = find_rust_files(args.directory) + + if not rust_files: + print("No .rs files found in the specified directory") + return + + print(f"Found {len(rust_files)} Rust files") + print() + + files_removed = 0 + files_with_snippets_removed = 0 + total_snippets_removed = 0 + errors = [] + + for file_path in rust_files: + result = process_rust_file(file_path, args.dry_run) + + if result['error']: + errors.append(f"{file_path}: {result['error']}") + continue + + if result['action'] == 'file_removed': + files_removed += 1 + if args.verbose or args.dry_run: + action_text = "Would remove" if args.dry_run else "Removed" + print(f"{action_text} file: {file_path}") + + elif result['action'] == 'snippets_removed': + files_with_snippets_removed += 1 + total_snippets_removed += result['snippets_removed'] + if args.verbose or args.dry_run: + action_text = "Would remove" if args.dry_run else "Removed" + print(f"{action_text} {result['snippets_removed']} snippet(s) from: {file_path}") + + # Summary + print("\nSummary:") + action_text = "Would be" if args.dry_run else "Were" + print(f"- {files_removed} files {action_text.lower()} completely removed") + print(f"- {total_snippets_removed} proprietary snippets {action_text.lower()} removed from {files_with_snippets_removed} files") + + if errors: + print(f"- {len(errors)} errors occurred:") + for error in errors: + print(f" {error}") + + if args.dry_run: + print("\nRun without --dry-run to apply changes") + + +if __name__ == '__main__': + main() diff --git a/tests/Cargo.toml b/tests/Cargo.toml index c754c33b..45094713 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -40,7 +40,7 @@ common = { path = "../crates/common", features = ["test_mode", "enterprise"] } email = { path = "../crates/email", features = ["test_mode", "enterprise"] } spam-filter = { path = "../crates/spam-filter", features = ["test_mode", "enterprise"] } migration = { path = "../crates/migration", features = ["test_mode", "enterprise"] } -trc = { path = "../crates/trc" } +trc = { path = "../crates/trc", features = ["enterprise"] } managesieve = { path = "../crates/managesieve", features = ["test_mode", "enterprise"] } smtp-proto = { version = "0.1" } mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }