/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL * * Modified by Coffey Labs in 2026 for INBUXA. */ use super::server::tls::build_self_signed_cert; use crate::{ Caches, Data, DavResource, DavResources, MailboxCache, MessageStoreCache, MessageUidCache, TlsConnectors, auth::{AccessTokenInner, AccountCache, DomainCache, MailingListCache, RoleCache, TenantCache}, config::{ mailstore::spamfilter::SpamClassifier, server::tls::parse_certificates, smtp::{ auth::DkimSigners, resolver::{Policy, Tlsa}, }, }, manager::application::WebApplications, network::security::BlockedIps, }; use ahash::{AHashMap, AHashSet}; use arc_swap::ArcSwap; use mail_auth::{MX, Parameters, RecordSet, Txt}; use parking_lot::RwLock; use registry::schema::{prelude::ObjectType, structs}; use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr}, sync::Arc, }; use store::{LookupStores, registry::bootstrap::Bootstrap}; use utils::{ UnwrapFailure, cache::{Cache, CacheWithTtl}, snowflake::{MAX_NODE_ID, SnowflakeIdGenerator}, tls::build_tls_connector, }; impl Data { pub async fn parse(bp: &mut Bootstrap) -> Self { // Parse certificates let mut certificates = AHashMap::new(); let mut subject_names = AHashSet::new(); parse_certificates(bp, &mut certificates, &mut subject_names).await; if subject_names.is_empty() { subject_names.insert("localhost".into()); } // Build and test snowflake id generator let node_id = bp.node_id(); if node_id > MAX_NODE_ID { panic!("Node id {node_id} exceeds {MAX_NODE_ID}, panicking to avoid data corruption"); } SnowflakeIdGenerator::set_node_id(node_id as u64); let id_generator = SnowflakeIdGenerator::new(); if !id_generator.is_valid() { panic!("Invalid system time, panicking to avoid data corruption"); } // Initialize apps let applications = WebApplications::new(); applications.reload(bp).await; let blocked_ips = BlockedIps::parse(bp).await; let lookup_stores = LookupStores::build(bp).await; Data { spam_classifier: ArcSwap::from_pointee(SpamClassifier::default()), listener_control: Default::default(), tls_certificates: ArcSwap::from_pointee(certificates), tls_self_signed_cert: build_self_signed_cert( subject_names .into_iter() .map(Into::into) .collect::>(), ) .or_else(|err| { bp.build_error( ObjectType::Certificate.singleton(), format!("Failed to build self-signed TLS certificate: {err}"), ); build_self_signed_cert(vec!["localhost".to_string()]) }) .ok() .map(Arc::new), lookup_stores: ArcSwap::from_pointee(lookup_stores.stores), blocked_ips: RwLock::new(blocked_ips), jmap_id_gen: id_generator.clone(), queue_id_gen: id_generator.clone(), registry_id_gen: id_generator.clone(), span_id_gen: id_generator, queue_status: true.into(), applications, logos: Default::default(), smtp_connectors: TlsConnectors::try_new().failed("Failed to build TLS connectors"), asn_geo_data: Default::default(), } } } impl Caches { pub async fn parse(bp: &mut Bootstrap) -> Self { let cache = bp.setting_infallible::().await; Caches { access_tokens: Cache::new_single_shard( cache.access_tokens, (std::mem::size_of::() + 255) as u64, ), http_auth: Cache::new(cache.http_auth, (50 + std::mem::size_of::()) as u64), messages: Cache::new_single_shard( cache.messages, (std::mem::size_of::() + std::mem::size_of::>() + (1024 * std::mem::size_of::()) + (15 * (std::mem::size_of::() + 60))) as u64, ), files: Cache::new_single_shard( cache.files, (std::mem::size_of::() + (500 * std::mem::size_of::())) as u64, ), events: Cache::new_single_shard( cache.events, (std::mem::size_of::() + (500 * std::mem::size_of::())) as u64, ), contacts: Cache::new_single_shard( cache.contacts, (std::mem::size_of::() + (500 * std::mem::size_of::())) as u64, ), scheduling: Cache::new_single_shard( cache.scheduling, (std::mem::size_of::() + (500 * std::mem::size_of::())) as u64, ), emails: Cache::new(cache.email_addresses, 255u64), emails_negative: CacheWithTtl::new( cache.email_addresses_negative, (std::mem::size_of::() + 255) as u64, ), domain_names: Cache::new( cache.domain_names, (std::mem::size_of::() + 255) as u64, ), domain_names_negative: CacheWithTtl::new( cache.domain_names_negative, (std::mem::size_of::() + 255) as u64, ), domains: Cache::new( cache.domains, (std::mem::size_of::() + 255) as u64, ), accounts: Cache::new( cache.accounts, (std::mem::size_of::() + 255) as u64, ), roles: Cache::new(cache.roles, (std::mem::size_of::() + 255) as u64), tenants: Cache::new( cache.tenants, (std::mem::size_of::() + 255) as u64, ), lists: Cache::new( cache.mailing_lists, (std::mem::size_of::() + 255) as u64, ), dkim_signers: Cache::new( cache.dkim_signatures, (std::mem::size_of::() + 255) as u64, ), dns_txt: CacheWithTtl::new(cache.dns_txt, (std::mem::size_of::() + 255) as u64), dns_mx: CacheWithTtl::new(cache.dns_mx, ((std::mem::size_of::() + 255) * 2) as u64), dns_ptr: CacheWithTtl::new(cache.dns_ptr, (std::mem::size_of::() + 255) as u64), dns_ipv4: CacheWithTtl::new( cache.dns_ipv4, ((std::mem::size_of::() + 255) * 2) as u64, ), dns_ipv6: CacheWithTtl::new( cache.dns_ipv6, ((std::mem::size_of::() + 255) * 2) as u64, ), dns_tlsa: CacheWithTtl::new(cache.dns_tlsa, (std::mem::size_of::() + 255) as u64), dns_mta_sts: CacheWithTtl::new( cache.dns_mta_sts, (std::mem::size_of::() + 255) as u64, ), dns_rbl: CacheWithTtl::new( cache.dns_rbl, ((std::mem::size_of::() + 255) * 2) as u64, ), negative_cache_ttl: cache.negative_ttl.into_inner(), } } #[allow(clippy::type_complexity)] #[inline(always)] pub fn build_auth_parameters( &self, params: T, ) -> Parameters< '_, T, CacheWithTtl, Txt>, CacheWithTtl, RecordSet>, CacheWithTtl, RecordSet>, CacheWithTtl, RecordSet>, CacheWithTtl>>, > { Parameters { params, cache_txt: Some(&self.dns_txt), cache_mx: Some(&self.dns_mx), cache_ptr: Some(&self.dns_ptr), cache_ipv4: Some(&self.dns_ipv4), cache_ipv6: Some(&self.dns_ipv6), } } } impl Default for Data { fn default() -> Self { Self { spam_classifier: Default::default(), listener_control: Default::default(), tls_certificates: Default::default(), tls_self_signed_cert: Default::default(), blocked_ips: Default::default(), jmap_id_gen: Default::default(), queue_id_gen: Default::default(), span_id_gen: Default::default(), registry_id_gen: Default::default(), queue_status: true.into(), applications: WebApplications::new(), logos: Default::default(), smtp_connectors: TlsConnectors::try_new().unwrap(), asn_geo_data: Default::default(), lookup_stores: Default::default(), } } } impl TlsConnectors { fn try_new() -> Result { Ok(TlsConnectors { pki_verify: build_tls_connector(false)?, dummy_verify: build_tls_connector(true)?, }) } }