Files
Stalwart/crates/common/src/config/mod.rs
2026-03-30 19:36:28 +02:00

138 lines
4.8 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use std::io::Cursor;
use self::{mailstore::jmap::JmapConfig, smtp::SmtpConfig, storage::Storage};
use crate::{
Core, Network,
auth::oauth::config::OAuthConfig,
config::mailstore::{
email::EmailConfig, imap::ImapConfig, scripts::Scripting, spamfilter::SpamFilterConfig,
},
};
use arc_swap::ArcSwap;
use groupware::GroupwareConfig;
use hyper::HeaderMap;
use ring::signature::{EcdsaKeyPair, RsaKeyPair};
use store::registry::bootstrap::Bootstrap;
use telemetry::Metrics;
pub mod groupware;
pub mod inner;
pub mod mailstore;
pub mod network;
pub mod server;
pub mod smtp;
pub mod storage;
pub mod telemetry;
impl Core {
pub async fn parse(bp: &mut Bootstrap, mut storage: Storage) -> Self {
// SPDX-SnippetBegin
// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
// SPDX-License-Identifier: LicenseRef-SEL
#[cfg(feature = "enterprise")]
let enterprise = {
let enterprise = crate::enterprise::Enterprise::parse(bp).await;
if enterprise.is_none() {
use registry::schema::prelude::ObjectType;
use store::Store;
if storage.data.is_enterprise() {
bp.build_error(
ObjectType::DataStore.singleton(),
"Disabling enterprise-only data store.",
);
storage.data = storage.data.downgrade_store();
}
if storage.blob.is_enterprise() {
bp.build_error(
ObjectType::BlobStore.singleton(),
"Disabling enterprise-only blob store.",
);
storage.blob = storage.blob.downgrade_store();
}
if storage.memory.is_enterprise() {
bp.build_error(
ObjectType::InMemoryStore.singleton(),
"Disabling enterprise-only in-memory store.",
);
storage.memory = storage.memory.downgrade_store();
}
storage.metrics = Store::None;
storage.metrics = Store::None;
storage.directories.clear();
}
enterprise
};
// SPDX-SnippetEnd
Self {
// SPDX-SnippetBegin
// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
// SPDX-License-Identifier: LicenseRef-SEL
#[cfg(feature = "enterprise")]
enterprise,
// SPDX-SnippetEnd
sieve: Scripting::parse(bp).await,
network: Network::parse(bp).await,
smtp: Box::pin(SmtpConfig::parse(bp)).await,
jmap: JmapConfig::parse(bp).await,
imap: ImapConfig::parse(bp).await,
oauth: OAuthConfig::parse(bp).await,
metrics: Metrics::parse(bp).await,
spam: SpamFilterConfig::parse(bp).await,
email: EmailConfig::parse(bp).await,
groupware: GroupwareConfig::parse(bp).await,
storage,
}
}
pub fn into_shared(self) -> ArcSwap<Self> {
ArcSwap::from_pointee(self)
}
}
pub fn build_rsa_keypair(pem: &str) -> Result<RsaKeyPair, String> {
for item in rustls_pemfile::read_all(&mut Cursor::new(pem)) {
match item.map_err(|err| format!("Failed to read private key PEM: {err}"))? {
rustls_pemfile::Item::Pkcs1Key(key) => {
return RsaKeyPair::from_der(key.secret_pkcs1_der())
.map_err(|err| format!("Failed to parse PKCS1 RSA key: {err}"));
}
rustls_pemfile::Item::Pkcs8Key(key) => {
return RsaKeyPair::from_pkcs8(key.secret_pkcs8_der())
.map_err(|err| format!("Failed to parse PKCS8 RSA key: {err}"));
}
_ => continue, // Skip certificates, DH params, etc.
}
}
Err("No RSA key found in PEM".to_string())
}
pub fn build_ecdsa_pem(
alg: &'static ring::signature::EcdsaSigningAlgorithm,
pem: &str,
) -> Result<EcdsaKeyPair, String> {
for item in rustls_pemfile::read_all(&mut Cursor::new(pem)) {
match item.map_err(|err| format!("Failed to read private key PEM: {err}"))? {
rustls_pemfile::Item::Pkcs8Key(key) => {
return EcdsaKeyPair::from_pkcs8(
alg,
key.secret_pkcs8_der(),
&ring::rand::SystemRandom::new(),
)
.map_err(|err| format!("Failed to parse PKCS8 ECDSA key: {err}"));
}
_ => continue, // Skip certificates, DH params, etc.
}
}
Err("No ECDSA key found in PEM".to_string())
}