From 705762c3125d976a9d6b589df4c9430fead10435 Mon Sep 17 00:00:00 2001 From: mdecimus Date: Fri, 7 Jul 2023 19:08:07 +0200 Subject: [PATCH] Fixed logging, clippy suggestions and organized configuration directives. --- crates/cli/src/modules/export.rs | 4 +- crates/cli/src/modules/import.rs | 16 +- crates/directory/src/sql/lookup.rs | 2 +- crates/imap/src/core/client.rs | 4 +- crates/imap/src/core/writer.rs | 8 +- crates/imap/src/lib.rs | 2 +- crates/imap/src/op/acl.rs | 3 +- crates/imap/src/op/authenticate.rs | 2 +- crates/imap/src/op/fetch.rs | 3 +- crates/imap/src/op/idle.rs | 2 +- crates/imap/src/op/search.rs | 11 +- crates/imap/src/op/thread.rs | 2 +- crates/jmap/src/api/config.rs | 17 +-- crates/jmap/src/lib.rs | 14 +- crates/jmap/src/services/housekeeper.rs | 19 +-- crates/jmap/src/services/ingest.rs | 4 +- crates/main/Cargo.toml | 4 +- crates/main/src/main.rs | 20 +-- crates/managesieve/src/core/client.rs | 20 ++- crates/managesieve/src/core/mod.rs | 9 +- crates/managesieve/src/op/authenticate.rs | 1 - crates/smtp/src/config/auth.rs | 54 +++---- crates/smtp/src/config/mod.rs | 1 - crates/smtp/src/config/session.rs | 12 -- crates/smtp/src/queue/dsn.rs | 2 +- crates/store/src/backend/sqlite/read.rs | 28 +++- crates/utils/src/config/listener.rs | 4 +- crates/utils/src/lib.rs | 20 ++- resources/config/common.toml | 53 +++++++ resources/config/directory.toml | 139 ++++++++++++++++++ resources/config/imap.toml | 33 +++++ resources/config/jmap.toml | 154 ++++++++++++++++++++ resources/config/{config.toml => smtp.toml} | 122 +++------------- tests/Cargo.toml | 4 +- tests/resources/imap/000.imap | 34 +++++ tests/resources/imap/001.imap | 9 ++ tests/resources/imap/002.imap | 26 ++++ tests/resources/imap/003.imap | 12 ++ tests/resources/imap/004.imap | 12 ++ tests/resources/imap/005.imap | 9 ++ tests/resources/imap/006.imap | 10 ++ tests/resources/imap/007.imap | 30 ++++ tests/resources/imap/008.imap | 17 +++ tests/resources/imap/009.imap | 9 ++ tests/resources/imap/010.imap | 7 + tests/resources/imap/011.imap | 17 +++ tests/resources/imap/012.imap | 12 ++ tests/resources/imap/013.imap | 16 ++ tests/resources/smtp/config/servers.toml | 4 +- tests/resources/smtp/dsn/delay.eml | 6 +- tests/resources/smtp/dsn/failure.eml | 6 +- tests/resources/smtp/dsn/mixed.eml | 8 +- tests/resources/smtp/dsn/success.eml | 6 +- tests/resources/test_config.toml | 9 +- tests/src/directory/imap.rs | 2 +- tests/src/directory/sql.rs | 2 +- tests/src/imap/mod.rs | 11 +- tests/src/jmap/auth_oauth.rs | 15 +- tests/src/jmap/email_changes.rs | 2 +- tests/src/jmap/email_query_changes.rs | 1 - tests/src/jmap/mod.rs | 8 +- tests/src/smtp/inbound/data.rs | 3 - tests/src/smtp/inbound/dmarc.rs | 3 - tests/src/smtp/inbound/rcpt.rs | 3 - tests/src/smtp/inbound/sign.rs | 3 - tests/src/smtp/lookup/sql.rs | 4 - tests/src/smtp/mod.rs | 1 - tests/src/smtp/outbound/mod.rs | 2 +- tests/src/smtp/queue/dsn.rs | 12 +- 69 files changed, 827 insertions(+), 297 deletions(-) create mode 100644 resources/config/common.toml create mode 100644 resources/config/directory.toml create mode 100644 resources/config/imap.toml create mode 100644 resources/config/jmap.toml rename resources/config/{config.toml => smtp.toml} (77%) diff --git a/crates/cli/src/modules/export.rs b/crates/cli/src/modules/export.rs index 29bc9849..8497a256 100644 --- a/crates/cli/src/modules/export.rs +++ b/crates/cli/src/modules/export.rs @@ -62,7 +62,7 @@ pub async fn cmd_export(mut client: Client, command: ExportCommands) { }); } let client = Arc::new(client); - let num_concurrent = num_concurrent.unwrap_or_else(|| num_cpus::get()); + let num_concurrent = num_concurrent.unwrap_or_else(num_cpus::get); let mut futures = FuturesUnordered::new(); eprintln!("Exporting {} blobs...", blobs.len()); for blob_id in blobs { @@ -93,7 +93,7 @@ pub async fn cmd_export(mut client: Client, command: ExportCommands) { } // Wait for remaining futures - while let Some(_) = futures.next().await {} + while futures.next().await.is_some() {} } } } diff --git a/crates/cli/src/modules/import.rs b/crates/cli/src/modules/import.rs index e18e4aea..f8a62eb8 100644 --- a/crates/cli/src/modules/import.rs +++ b/crates/cli/src/modules/import.rs @@ -271,7 +271,7 @@ pub async fn cmd_import(mut client: Client, command: ImportCommands) { let client = Arc::new(client); let total_imported = Arc::new(AtomicUsize::from(0)); let m = MultiProgress::new(); - let num_concurrent = num_concurrent.unwrap_or_else(|| num_cpus::get()); + let num_concurrent = num_concurrent.unwrap_or_else(num_cpus::get); let spinner_style = ProgressStyle::with_template("{prefix:.bold.dim} {spinner} {wide_msg}") .unwrap() @@ -307,7 +307,7 @@ pub async fn cmd_import(mut client: Client, command: ImportCommands) { "Inbox".to_string() }); - while let Some(result) = mailbox.next() { + for result in mailbox.by_ref() { match result { Ok(message) => { message_num += 1; @@ -385,7 +385,7 @@ pub async fn cmd_import(mut client: Client, command: ImportCommands) { } // Wait for remaining futures - while let Some(_) = futures.next().await {} + while futures.next().await.is_some() {} } // Done @@ -417,7 +417,7 @@ pub async fn cmd_import(mut client: Client, command: ImportCommands) { eprintln!("Path '{}' does not exist.", path.display()); return; } - let num_concurrent = num_concurrent.unwrap_or_else(|| num_cpus::get()); + let num_concurrent = num_concurrent.unwrap_or_else(num_cpus::get); // Import objects import_emails( @@ -673,7 +673,7 @@ async fn import_emails( } // Wait for remaining futures - while let Some(_) = futures.next().await {} + while futures.next().await.is_some() {} // Done eprintln!( @@ -779,7 +779,7 @@ async fn import_sieve_scripts(client: &Client, path: &Path, num_concurrent: usiz } // Wait for remaining futures - while let Some(_) = futures.next().await {} + while futures.next().await.is_some() {} // Done eprintln!( @@ -842,7 +842,7 @@ async fn import_identities(client: &Client, path: &Path) { match request.send_set_identity().await { Ok(mut response) => { for id in create_ids { - if let Err(err) = response.created(&id) { + if let Err(err) = response.created(id) { eprintln!("Failed to import identity {id}: {err}"); } else { total_imported += 1; @@ -963,7 +963,7 @@ fn build_mailbox_tree( async fn read_json(path: &Path, filename: &str) -> Vec { let mut path = PathBuf::from(path); - path.push(&filename); + path.push(filename); if path.exists() { let mut file = File::open(path).await.unwrap_result("open file"); let mut contents = String::new(); diff --git a/crates/directory/src/sql/lookup.rs b/crates/directory/src/sql/lookup.rs index 5c21fd6d..b4df99a0 100644 --- a/crates/directory/src/sql/lookup.rs +++ b/crates/directory/src/sql/lookup.rs @@ -17,7 +17,7 @@ impl Directory for SqlDirectory { Credentials::XOauth2 { username, secret } => (username, secret), }; - match self.principal(&username).await { + match self.principal(username).await { Ok(Some(principal)) if principal.verify_secret(secret).await => Ok(Some(principal)), Ok(_) => Ok(None), Err(err) => Err(err), diff --git a/crates/imap/src/core/client.rs b/crates/imap/src/core/client.rs index cd501f9e..088e985a 100644 --- a/crates/imap/src/core/client.rs +++ b/crates/imap/src/core/client.rs @@ -36,9 +36,9 @@ use super::{SelectedMailbox, Session, SessionData, State, IMAP}; impl Session { pub async fn ingest(&mut self, bytes: &[u8]) -> crate::Result { - for line in String::from_utf8_lossy(bytes).split("\r\n") { + /*for line in String::from_utf8_lossy(bytes).split("\r\n") { let c = println!("<- {:?}", &line[..std::cmp::min(line.len(), 100)]); - } + }*/ tracing::trace!(parent: &self.span, event = "read", diff --git a/crates/imap/src/core/writer.rs b/crates/imap/src/core/writer.rs index 79f88de7..7ef98cb2 100644 --- a/crates/imap/src/core/writer.rs +++ b/crates/imap/src/core/writer.rs @@ -118,10 +118,10 @@ impl Session { pub async fn write_bytes(&self, bytes: impl Into>) -> crate::OpResult { let bytes = bytes.into(); - let c = println!( + /*let c = println!( "-> {:?}", String::from_utf8_lossy(&bytes[..std::cmp::min(bytes.len(), 100)]) - ); + );*/ if let Err(err) = self.writer.send(Event::Bytes(bytes)).await { debug!("Failed to send bytes: {}", err); @@ -135,10 +135,10 @@ impl Session { impl SessionData { pub async fn write_bytes(&self, bytes: impl Into>) -> bool { let bytes = bytes.into(); - let c = println!( + /*let c = println!( "-> {:?}", String::from_utf8_lossy(&bytes[..std::cmp::min(bytes.len(), 100)]) - ); + );*/ if let Err(err) = self.writer.send(Event::Bytes(bytes)).await { debug!("Failed to send bytes: {}", err); diff --git a/crates/imap/src/lib.rs b/crates/imap/src/lib.rs index d64f087f..5b511e95 100644 --- a/crates/imap/src/lib.rs +++ b/crates/imap/src/lib.rs @@ -49,7 +49,7 @@ impl IMAP { .unwrap_or(32) .next_power_of_two() as usize, ), - rate_requests: config.property_or_static("imap.rate-limit.rate", "1000/1m")?, + rate_requests: config.property_or_static("imap.rate-limit", "1000/1m")?, rate_concurrent: config.property("imap.rate-limit.concurrent")?.unwrap_or(4), allow_plain_auth: config.property_or_static("imap.auth.allow-plain-text", "false")?, })) diff --git a/crates/imap/src/op/acl.rs b/crates/imap/src/op/acl.rs index bf95bd40..3eb27f55 100644 --- a/crates/imap/src/op/acl.rs +++ b/crates/imap/src/op/acl.rs @@ -140,7 +140,6 @@ impl Session { Err(response) => { data.write_bytes(response.with_tag(arguments.tag).into_bytes()) .await; - return; } } }); @@ -250,7 +249,7 @@ impl Session { .await { Ok(Some(principal)) => { - match data.jmap.get_account_id(&principal.name()).await { + match data.jmap.get_account_id(principal.name()).await { Ok(account_id) => (account_id, Value::Id(Id::from(account_id))), Err(_) => { data.write_bytes( diff --git a/crates/imap/src/op/authenticate.rs b/crates/imap/src/op/authenticate.rs index ec598057..92f7bae0 100644 --- a/crates/imap/src/op/authenticate.rs +++ b/crates/imap/src/op/authenticate.rs @@ -174,7 +174,7 @@ impl Session { event = "disconnect", "Too many concurrent connections, disconnecting.", ); - return Err(()); + Err(()) } } else { self.write_bytes( diff --git a/crates/imap/src/op/fetch.rs b/crates/imap/src/op/fetch.rs index fce4a483..87df717e 100644 --- a/crates/imap/src/op/fetch.rs +++ b/crates/imap/src/op/fetch.rs @@ -586,6 +586,7 @@ impl SessionData { } } +#[allow(clippy::result_unit_err)] pub trait AsImapDataItem<'x> { fn body_structure(&self, is_extended: bool) -> BodyPart; fn body_section<'z: 'x>( @@ -1046,7 +1047,7 @@ impl<'x> AsImapDataItem<'x> for Message<'x> { fn envelope(&self) -> Envelope { Envelope { - date: self.date().map(|d| d.clone()), + date: self.date().cloned(), subject: self.subject().map(|s| s.into()), from: self .header_values(RfcHeader::From) diff --git a/crates/imap/src/op/idle.rs b/crates/imap/src/op/idle.rs index 246f3298..1bfb6ecd 100644 --- a/crates/imap/src/op/idle.rs +++ b/crates/imap/src/op/idle.rs @@ -91,7 +91,7 @@ impl Session { match result { Ok(Ok(bytes_read)) => { if bytes_read > 0 { - if (&buf[..bytes_read]).windows(4).any(|w| w == b"DONE") { + if (buf[..bytes_read]).windows(4).any(|w| w == b"DONE") { tracing::debug!(parent: &self.span, event = "stop", context = "idle", "Stopping IDLE."); return self.write_bytes(StatusResponse::completed(Command::Idle) .with_tag(request.tag) diff --git a/crates/imap/src/op/search.rs b/crates/imap/src/op/search.rs index fd81ef66..0a72555a 100644 --- a/crates/imap/src/op/search.rs +++ b/crates/imap/src/op/search.rs @@ -214,7 +214,7 @@ impl SessionData { // Save results if let (Some(results_tx), Some(saved_results)) = (results_tx, saved_results) { - let saved_results = Arc::new(saved_results.clone()); + let saved_results = Arc::new(saved_results); *mailbox.saved_search.lock() = SavedSearch::Results { items: saved_results.clone(), }; @@ -666,6 +666,7 @@ impl SelectedMailbox { Some(v.clone()) } + #[allow(clippy::too_many_arguments)] pub fn map_search_results( &self, ids: impl Iterator, @@ -703,7 +704,9 @@ impl SelectedMailbox { } } else { imap_ids.push(id); - saved_results.as_mut().map(|r| r.push(imap_id)); + if let Some(r) = saved_results.as_mut() { + r.push(imap_id) + } } *total += 1; } @@ -711,7 +714,9 @@ impl SelectedMailbox { if find_min || find_max { for (id, imap_id) in [min, max].into_iter().flatten() { imap_ids.push(*id); - saved_results.as_mut().map(|r| r.push(*imap_id)); + if let Some(r) = saved_results.as_mut() { + r.push(*imap_id) + } } } } diff --git a/crates/imap/src/op/thread.rs b/crates/imap/src/op/thread.rs index e3682ee2..2f205a83 100644 --- a/crates/imap/src/op/thread.rs +++ b/crates/imap/src/op/thread.rs @@ -121,7 +121,7 @@ impl SessionData { { threads .entry(thread_id) - .or_insert_with(|| Vec::new()) + .or_insert_with(Vec::new) .push(imap_id); } } diff --git a/crates/jmap/src/api/config.rs b/crates/jmap/src/api/config.rs index 7c8b758d..a28f5802 100644 --- a/crates/jmap/src/api/config.rs +++ b/crates/jmap/src/api/config.rs @@ -69,7 +69,7 @@ impl crate::Config { .unwrap_or(50000000), upload_tmp_quota_amount: settings .property("jmap.protocol.upload.quota.files")? - .unwrap_or(50000000), + .unwrap_or(1000), upload_tmp_ttl: settings .property_or_static::("jmap.protocol.upload.ttl", "1h")? .as_secs(), @@ -85,23 +85,22 @@ impl crate::Config { .unwrap_or(75000000), mail_parse_max_items: settings .property("jmap.email.parse.max-items")? - .unwrap_or(50000000), + .unwrap_or(10), sieve_max_script_name: settings - .property("jmap.sieve.max-name-length")? + .property("jmap.sieve.limits.name-length")? .unwrap_or(512), sieve_max_scripts: settings - .property("jmap.protocol.max-scripts")? + .property("jmap.sieve.limits.max-scripts")? .unwrap_or(256), capabilities: BaseCapabilities::default(), session_cache_ttl: settings .property("jmap.session.cache.ttl")? .unwrap_or(Duration::from_secs(3600)), rate_authenticated: settings - .property_or_static("jmap.rate-limit.account.rate", "1000/1m")?, + .property_or_static("jmap.rate-limit.account", "1000/1m")?, rate_authenticate_req: settings - .property_or_static("jmap.rate-limit.authentication.rate", "10/1m")?, - rate_anonymous: settings - .property_or_static("jmap.rate-limit.anonymous.rate", "100/1m")?, + .property_or_static("jmap.rate-limit.authentication", "10/1m")?, + rate_anonymous: settings.property_or_static("jmap.rate-limit.anonymous", "100/1m")?, rate_use_forwarded: settings .property("jmap.rate-limit.use-forwarded")? .unwrap_or(false), @@ -130,7 +129,7 @@ impl crate::Config { oauth_expiry_refresh_token_renew: settings .property_or_static::("oauth.expiry.refresh-token-renew", "4d")? .as_secs(), - oauth_max_auth_attempts: settings.property_or_static("oauth.max-auth-attempts", "3")?, + oauth_max_auth_attempts: settings.property_or_static("oauth.auth.max-attempts", "3")?, event_source_throttle: settings .property_or_static("jmap.event-source.throttle", "1s")?, web_socket_throttle: settings.property_or_static("jmap.web-socket.throttle", "1s")?, diff --git a/crates/jmap/src/lib.rs b/crates/jmap/src/lib.rs index a7721de4..8f20ebcf 100644 --- a/crates/jmap/src/lib.rs +++ b/crates/jmap/src/lib.rs @@ -203,20 +203,20 @@ impl JMAP { ), rate_limit_auth: DashMap::with_capacity_and_hasher_and_shard_amount( config - .property("jmap.rate-limit.account.size")? + .property("jmap.rate-limit.cache.size")? .unwrap_or(1024), RandomState::default(), shard_amount, ), rate_limit_unauth: DashMap::with_capacity_and_hasher_and_shard_amount( config - .property("jmap.rate-limit.anonymous.size")? - .unwrap_or(2048), + .property("jmap.rate-limit.cache.size")? + .unwrap_or(1024), RandomState::default(), shard_amount, ), oauth_codes: TtlDashMap::with_capacity( - config.property("oauth.code.cache-size")?.unwrap_or(128), + config.property("oauth.cache.size")?.unwrap_or(128), shard_amount, ), state_tx, @@ -230,12 +230,12 @@ impl JMAP { ) .with_max_string_size( config - .property("jmap.sieve.limits.string-size")? + .property("jmap.sieve.limits.string-length")? .unwrap_or(4096), ) .with_max_variable_name_size( config - .property("jmap.sieve.limits.variable-name-size")? + .property("jmap.sieve.limits.variable-name-length")? .unwrap_or(32), ) .with_max_nested_blocks( @@ -275,7 +275,7 @@ impl JMAP { .property("jmap.sieve.limits.nested-includes")? .unwrap_or(3), ) - .with_cpu_limit(config.property("jmap.sieve.cpu-limit")?.unwrap_or(5000)) + .with_cpu_limit(config.property("jmap.sieve.limits.cpu")?.unwrap_or(5000)) .with_max_variable_size( config .property("jmap.sieve.limits.variable-size")? diff --git a/crates/jmap/src/services/housekeeper.rs b/crates/jmap/src/services/housekeeper.rs index 929554b0..74fd3497 100644 --- a/crates/jmap/src/services/housekeeper.rs +++ b/crates/jmap/src/services/housekeeper.rs @@ -37,7 +37,7 @@ use super::IPC_CHANNEL_BUFFER; pub enum Event { PurgeDb, PurgeBlobs, - PurgeCache, + PurgeSessions, Exit, } @@ -49,22 +49,19 @@ enum SimpleCron { const TASK_PURGE_DB: usize = 0; const TASK_PURGE_BLOBS: usize = 1; -const TASK_PURGE_CACHE: usize = 2; +const TASK_PURGE_SESSIONS: usize = 2; pub fn spawn_housekeeper(core: Arc, settings: &Config, mut rx: mpsc::Receiver) { - let purge_db_at = SimpleCron::parse( - settings - .value("jmap.house-keeper.purge-db") - .unwrap_or("0 3 *"), - ); + let purge_db_at = + SimpleCron::parse(settings.value("jmap.purge.schedule.db").unwrap_or("0 3 *")); let purge_blobs_at = SimpleCron::parse( settings - .value("jmap.house-keeper.purge-blobs") + .value("jmap.purge.schedule.blobs") .unwrap_or("30 3 *"), ); let purge_cache = SimpleCron::parse( settings - .value("jmap.house-keeper.purge-cache") + .value("jmap.purge.schedule.sessions") .unwrap_or("15 * *"), ); @@ -84,7 +81,7 @@ pub fn spawn_housekeeper(core: Arc, settings: &Config, mut rx: mpsc::Recei Ok(Some(event)) => match event { Event::PurgeDb => tasks_to_run[TASK_PURGE_DB] = true, Event::PurgeBlobs => tasks_to_run[TASK_PURGE_BLOBS] = true, - Event::PurgeCache => tasks_to_run[TASK_PURGE_CACHE] = true, + Event::PurgeSessions => tasks_to_run[TASK_PURGE_SESSIONS] = true, Event::Exit => { tracing::debug!("Housekeeper task exiting."); return; @@ -129,7 +126,7 @@ pub fn spawn_housekeeper(core: Arc, settings: &Config, mut rx: mpsc::Recei tracing::error!("Error while purging bitmaps: {}", err); } } - TASK_PURGE_CACHE => { + TASK_PURGE_SESSIONS => { tracing::info!("Purging session cache."); core.sessions.cleanup(); core.access_tokens.cleanup(); diff --git a/crates/jmap/src/services/ingest.rs b/crates/jmap/src/services/ingest.rs index 2508135c..d87dc5e8 100644 --- a/crates/jmap/src/services/ingest.rs +++ b/crates/jmap/src/services/ingest.rs @@ -60,7 +60,7 @@ impl JMAP { // Deliver to each recipient for (name, (status, rcpt)) in &mut deliver_names { // Obtain account id - let uid = match self.get_account_id(&name).await { + let uid = match self.get_account_id(name).await { Ok(uid) => uid, Err(_) => { *status = DeliveryResult::TemporaryFailure { @@ -84,7 +84,7 @@ impl JMAP { .await } Ok(None) => { - let account_quota = match self.directory.principal(&name).await { + let account_quota = match self.directory.principal(name).await { Ok(Some(p)) => p.quota as i64, Ok(None) => 0, Err(_) => { diff --git a/crates/main/Cargo.toml b/crates/main/Cargo.toml index 48506a1e..69bcb90c 100644 --- a/crates/main/Cargo.toml +++ b/crates/main/Cargo.toml @@ -31,8 +31,8 @@ tracing = "0.1" jemallocator = "0.5.0" [features] -#default = ["sqlite"] -default = ["foundationdb"] +default = ["sqlite"] +#default = ["foundationdb"] sqlite = ["store/sqlite"] foundationdb = ["store/foundation"] diff --git a/crates/main/src/main.rs b/crates/main/src/main.rs index ae0f55c0..12f1d002 100644 --- a/crates/main/src/main.rs +++ b/crates/main/src/main.rs @@ -51,12 +51,14 @@ async fn main() -> std::io::Result<()> { servers.bind(&config); // Enable tracing - let _tracer = enable_tracing(&config).failed("Failed to enable tracing"); - tracing::info!( - "Starting Stalwart Mail Server v{}...", - env!("CARGO_PKG_VERSION") - ); - let todo = "fix logging"; + let _tracer = enable_tracing( + &config, + &format!( + "Starting Stalwart Mail Server v{}...", + env!("CARGO_PKG_VERSION"), + ), + ) + .failed("Failed to enable tracing"); // Init servers let (delivery_tx, delivery_rx) = mpsc::channel(IPC_CHANNEL_BUFFER); @@ -94,11 +96,11 @@ async fn main() -> std::io::Result<()> { }); // Wait for shutdown signal - wait_for_shutdown().await; - tracing::info!( + wait_for_shutdown(&format!( "Shutting down Stalwart Mail Server v{}...", env!("CARGO_PKG_VERSION") - ); + )) + .await; // Stop services let _ = shutdown_tx.send(true); diff --git a/crates/managesieve/src/core/client.rs b/crates/managesieve/src/core/client.rs index 4315f710..995c7535 100644 --- a/crates/managesieve/src/core/client.rs +++ b/crates/managesieve/src/core/client.rs @@ -20,7 +20,7 @@ impl Session { loop { match self.receiver.parse(&mut bytes) { Ok(request) => { - match request.is_allowed(&self.imap, &self.state, self.stream.is_tls()) { + match request.validate_request(&self.imap, &self.state, self.stream.is_tls()) { Ok(request) => { requests.push(request); } @@ -154,12 +154,22 @@ impl Session { } } -trait IsAllowed: Sized { - fn is_allowed(self, imap: &IMAP, state: &State, is_tls: bool) -> Result; +trait ValidateRequest: Sized { + fn validate_request( + self, + imap: &IMAP, + state: &State, + is_tls: bool, + ) -> Result; } -impl IsAllowed for Request { - fn is_allowed(self, imap: &IMAP, state: &State, is_tls: bool) -> Result { +impl ValidateRequest for Request { + fn validate_request( + self, + imap: &IMAP, + state: &State, + is_tls: bool, + ) -> Result { match &self.command { Command::Capability | Command::Logout | Command::Noop => Ok(self), Command::Authenticate => { diff --git a/crates/managesieve/src/core/mod.rs b/crates/managesieve/src/core/mod.rs index 1590ae79..b205b252 100644 --- a/crates/managesieve/src/core/mod.rs +++ b/crates/managesieve/src/core/mod.rs @@ -59,7 +59,7 @@ impl ManageSieveSessionManager { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum Command { Authenticate, StartTls, @@ -73,6 +73,7 @@ pub enum Command { DeleteScript, RenameScript, CheckScript, + #[default] Noop, Unauthenticate, } @@ -119,12 +120,6 @@ impl CommandParser for Command { } } -impl Default for Command { - fn default() -> Self { - Command::Noop - } -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct StatusResponse { pub code: Option, diff --git a/crates/managesieve/src/op/authenticate.rs b/crates/managesieve/src/op/authenticate.rs index 6de89943..57e6ecf6 100644 --- a/crates/managesieve/src/op/authenticate.rs +++ b/crates/managesieve/src/op/authenticate.rs @@ -44,7 +44,6 @@ impl Session { let mechanism = Mechanism::parse(&tokens.next().unwrap().unwrap_bytes()).map_err(StatusResponse::no)?; let mut params: Vec = tokens - .into_iter() .filter_map(|token| token.unwrap_string().ok()) .collect(); diff --git a/crates/smtp/src/config/auth.rs b/crates/smtp/src/config/auth.rs index 33ac64b7..d9314cd9 100644 --- a/crates/smtp/src/config/auth.rs +++ b/crates/smtp/src/config/auth.rs @@ -166,36 +166,30 @@ impl ConfigAuth for Config { let (signer, sealer) = match self.property_require::(("signature", id, "algorithm"))? { Algorithm::RsaSha256 => { - let key = RsaKey::::from_rsa_pem( - &String::from_utf8(self.file_contents(( - "signature", - id, - "private-key", - ))?) - .unwrap_or_default(), - ) - .map_err(|err| { - format!( - "Failed to build RSA key for {}: {}", - ("signature", id, "private-key",).as_key(), - err - ) - })?; - let key_clone = RsaKey::::from_rsa_pem( - &String::from_utf8(self.file_contents(( - "signature", - id, - "private-key", - ))?) - .unwrap_or_default(), - ) - .map_err(|err| { - format!( - "Failed to build RSA key for {}: {}", - ("signature", id, "private-key",).as_key(), - err - ) - })?; + let pk = String::from_utf8(self.file_contents(( + "signature", + id, + "private-key", + ))?) + .unwrap_or_default(); + let key = RsaKey::::from_rsa_pem(&pk) + .or_else(|_| RsaKey::::from_pkcs8_pem(&pk)) + .map_err(|err| { + format!( + "Failed to build RSA key for {}: {}", + ("signature", id, "private-key",).as_key(), + err + ) + })?; + let key_clone = RsaKey::::from_rsa_pem(&pk) + .or_else(|_| RsaKey::::from_pkcs8_pem(&pk)) + .map_err(|err| { + format!( + "Failed to build RSA key for {}: {}", + ("signature", id, "private-key",).as_key(), + err + ) + })?; let (signer, sealer) = parse_signature(self, id, key_clone, key)?; (DkimSigner::RsaSha256(signer), ArcSealer::RsaSha256(sealer)) } diff --git a/crates/smtp/src/config/mod.rs b/crates/smtp/src/config/mod.rs index 1abc2ed9..6562103c 100644 --- a/crates/smtp/src/config/mod.rs +++ b/crates/smtp/src/config/mod.rs @@ -234,7 +234,6 @@ pub struct Mail { pub struct Rcpt { pub script: IfBlock>>, pub relay: IfBlock, - pub lookup_domains: IfBlock>>, pub directory: IfBlock>>, // Errors diff --git a/crates/smtp/src/config/session.rs b/crates/smtp/src/config/session.rs index 992e4410..dfb04b63 100644 --- a/crates/smtp/src/config/session.rs +++ b/crates/smtp/src/config/session.rs @@ -312,18 +312,6 @@ impl ConfigSession for Config { relay: self .parse_if_block("session.rcpt.relay", ctx, &available_keys)? .unwrap_or_else(|| IfBlock::new(false)), - lookup_domains: self - .parse_if_block::>( - "session.rcpt.directory.domains", - ctx, - &available_keys, - )? - .unwrap_or_default() - .map_if_block( - &ctx.directory.lookups, - "session.rcpt.directory.domains", - "lookup list", - )?, directory: self .parse_if_block::>("session.rcpt.directory", ctx, &available_keys)? .unwrap_or_default() diff --git a/crates/smtp/src/queue/dsn.rs b/crates/smtp/src/queue/dsn.rs index 5bc39850..62f9b5fd 100644 --- a/crates/smtp/src/queue/dsn.rs +++ b/crates/smtp/src/queue/dsn.rs @@ -195,7 +195,7 @@ impl DeliveryAttempt { if has_success { if is_mixed { txt.push_str( - " ----- Delivery to the following addresses was succesful -----\r\n", + " ----- Delivery to the following addresses was successful -----\r\n", ); } diff --git a/crates/store/src/backend/sqlite/read.rs b/crates/store/src/backend/sqlite/read.rs index 8e187b63..03397fa3 100644 --- a/crates/store/src/backend/sqlite/read.rs +++ b/crates/store/src/backend/sqlite/read.rs @@ -368,13 +368,15 @@ impl Store { // Values let mut query = conn.conn.prepare_cached("SELECT k, v FROM v").unwrap(); let mut rows = query.query([]).unwrap(); + let mut has_errors = false; while let Some(row) = rows.next().unwrap() { let key = row.get_ref(0).unwrap().as_bytes().unwrap(); let value = row.get_ref(1).unwrap().as_bytes().unwrap(); - if &key[0..4] != u32::MAX.to_be_bytes() { - panic!("Table values is not empty: {key:?} {value:?}"); + if key[0..4] != u32::MAX.to_be_bytes() { + eprintln!("Table values is not empty: {key:?} {value:?}"); + has_errors = true; } } @@ -385,7 +387,7 @@ impl Store { while let Some(row) = rows.next().unwrap() { let key = row.get_ref(0).unwrap().as_bytes().unwrap(); - panic!( + eprintln!( "Table index is not empty, account {}, collection {}, document {}, property {}, value {:?}: {:?}", u32::from_be_bytes(key[0..4].try_into().unwrap()), key[4], @@ -394,6 +396,7 @@ impl Store { String::from_utf8_lossy(&key[6..key.len()-4]), key ); + has_errors = true; } // Bitmaps @@ -404,16 +407,20 @@ impl Store { .unwrap(); let mut rows = query.query([]).unwrap(); - while let Some(row) = rows.next().unwrap() { + 'outer: while let Some(row) = rows.next().unwrap() { let key = row.get_ref(0).unwrap().as_bytes().unwrap(); - if &key[0..4] != u32::MAX.to_be_bytes() { + if key[0..4] != u32::MAX.to_be_bytes() { for bit_pos in 1..=16 { let bit_value = row.get::<_, i64>(bit_pos).unwrap() as u64; if bit_value != 0 { - panic!("Table bitmaps is not empty: {key:?} {bit_pos} {bit_value}"); + eprintln!("Table bitmaps is not empty: {key:?} {bit_pos} {bit_value}"); + has_errors = true; + + continue 'outer; } } - panic!("Table bitmaps failed to purge, found key: {key:?}"); + eprintln!("Table bitmaps failed to purge, found key: {key:?}"); + has_errors = true; } } @@ -425,16 +432,21 @@ impl Store { let key = row.get::<_, i64>(0).unwrap(); let value = row.get::<_, i64>(1).unwrap(); if value != 0 { - panic!( + eprintln!( "Table quota is not empty, account {}, quota: {}", key, value, ); + has_errors = true; } } // Delete logs conn.conn.execute("DELETE FROM l", []).unwrap(); + if has_errors { + panic!("Database is not empty"); + } + self.id_assigner.lock().clear(); } } diff --git a/crates/utils/src/config/listener.rs b/crates/utils/src/config/listener.rs index d41e5235..72b283de 100644 --- a/crates/utils/src/config/listener.rs +++ b/crates/utils/src/config/listener.rs @@ -266,9 +266,7 @@ impl Config { return Err(format!("No 'bind' directive found for listener id {id:?}")); } - let protocol = self - .property_or_default(("server.listener", id, "protocol"), "server.protocol")? - .unwrap_or(ServerProtocol::Smtp); + let protocol = self.property_require(("server.listener", id, "protocol"))?; Ok(Server { id: id.to_string(), diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index e642d06e..0481294f 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -82,12 +82,14 @@ pub fn failed(message: &str) -> ! { std::process::exit(1); } -pub fn enable_tracing(config: &Config) -> config::Result> { +pub fn enable_tracing(config: &Config, message: &str) -> config::Result> { let level = config.value("global.tracing.level").unwrap_or("info"); let env_filter = EnvFilter::builder() - .parse(format!("stalwart_smtp={}", level)) + .parse(format!( + "smtp={level},imap={level},jmap={level},store={level},utils={level},directory={level}" + )) .failed("Failed to log level"); - match config.value("global.tracing.method").unwrap_or_default() { + let result = match config.value("global.tracing.method").unwrap_or_default() { "log" => { let path = config.value_require("global.tracing.path")?; let prefix = config.value_require("global.tracing.prefix")?; @@ -114,7 +116,7 @@ pub fn enable_tracing(config: &Config) -> config::Result> { "stdout" => { tracing::subscriber::set_global_default( tracing_subscriber::FmtSubscriber::builder() - //.with_env_filter(env_filter) + .with_env_filter(env_filter) .finish(), ) .failed("Failed to set subscriber"); @@ -178,10 +180,14 @@ pub fn enable_tracing(config: &Config) -> config::Result> { Ok(None) } _ => Ok(None), - } + }; + + tracing::info!(message); + + result } -pub async fn wait_for_shutdown() { +pub async fn wait_for_shutdown(message: &str) { #[cfg(not(target_env = "msvc"))] { use tokio::signal::unix::{signal, SignalKind}; @@ -204,4 +210,6 @@ pub async fn wait_for_shutdown() { } } } + + tracing::info!(message); } diff --git a/resources/config/common.toml b/resources/config/common.toml new file mode 100644 index 00000000..7cd34aae --- /dev/null +++ b/resources/config/common.toml @@ -0,0 +1,53 @@ +[server] +hostname = "__HOST__" +max-connections = 8192 + +[server.run-as] +user = "__RUN_AS_USER__" +group = "__RUN_AS_GROUP__" + +[server.tls] +enable = true +implicit = false +timeout = "1m" +certificate = "default" +#sni = [{subject = "", certificate = ""}] +#protocols = ["TLSv1.2", TLSv1.3"] +#ciphers = [] +ignore-client-order = true + +[server.socket] +reuse-addr = true +#reuse-port = true +backlog = 1024 +#ttl = 3600 +#send-buffer-size = 65535 +#recv-buffer-size = 65535 +#linger = 1 +#tos = 1 + +[global] +shared-map = {shard = 32, capacity = 10} +#thread-pool = 8 + +#[global.tracing] +#method = "stdout" +#level = "trace" + +#[global.tracing] +#method = "open-telemetry" +#transport = "http" +#endpoint = "https://127.0.0.1/otel" +#headers = ["Authorization: "] +#level = "debug" + +[global.tracing] +method = "log" +path = "__PATH__/logs" +prefix = "smtp.log" +rotate = "daily" +level = "info" + +[certificate."default"] +cert = "file://__PATH__/etc/certs/tls.crt" +private-key = "file://__PATH__/etc/private/tls.key" diff --git a/resources/config/directory.toml b/resources/config/directory.toml new file mode 100644 index 00000000..962b0376 --- /dev/null +++ b/resources/config/directory.toml @@ -0,0 +1,139 @@ +[directory."sql"] +type = "sql" +address = "sqlite::memory:" + +[directory."sql".options] +catch-all = true +subaddressing = true + +[directory."sql".pool] +max-connections = 10 + +[directory."sql".query] +name = "SELECT name, type, secret, description, quota FROM accounts WHERE name = ? AND active = true" +members = "SELECT member_of FROM group_members WHERE name = ?" +recipients = "SELECT name FROM emails WHERE address = ?" +emails = "SELECT address FROM emails WHERE name = ? AND type != 'list' ORDER BY type DESC, address ASC" +verify = "SELECT address FROM emails WHERE address LIKE '%' || ? || '%' AND type = 'primary' ORDER BY address LIMIT 5" +expand = "SELECT p.address FROM emails AS p JOIN emails AS l ON p.name = l.name WHERE p.type = 'primary' AND l.address = ? AND l.type = 'list' ORDER BY p.address LIMIT 50" +domains = "SELECT 1 FROM emails WHERE address LIKE '%@' || ? LIMIT 1" + +[directory."sql".columns] +name = "name" +description = "description" +secret = "secret" +email = "address" +quota = "quota" +type = "type" + +[directory."ldap"] +type = "ldap" +address = "ldap://localhost:3893" +base-dn = "dc=example,dc=org" + +[directory."ldap".bind] +dn = "cn=serviceuser,ou=svcaccts,dc=example,dc=org" +secret = "mysecret" + +[directory."ldap".options] +catch-all = true +subaddressing = true + +[directory."ldap".filter] +name = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(uid=?))" +email = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=?)(mailAlias=?)))" +verify = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=*?*)(mailAlias=*?*)))" +expand = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(sn=?))" +domains = "(&(|(objectClass=posixAccount)(objectClass=posixGroup))(|(mail=*@?)(mailAlias=*@?)))" + +[directory."ldap".object-classes] +user = "posixAccount" +group = "posixGroup" + +[directory."ldap".attributes] +name = "uid" +description = ["principalName", "description"] +secret = "userPassword" +groups = ["memberOf", "otherGroups"] +email = "mail" +email-alias = "mailAlias" +quota = "diskQuota" + +[directory."imap"] +type = "imap" +address = "127.0.0.1" +port = 9198 + +[directory."imap".pool] +max-connections = 5 + +[directory."imap".tls] +implicit = true +allow-invalid-certs = true + +[directory."imap".lookup] +domains = ["example.org"] + +[directory."smtp"] +type = "lmtp" +address = "127.0.0.1" +port = 9199 + +[directory."smtp".limits] +auth-errors = 3 +rcpt = 5 + +[directory."smtp".pool] +max-connections = 5 + +[directory."smtp".tls] +implicit = true +allow-invalid-certs = true + +[directory."smtp".cache] +entries = 500 +ttl = {positive = '10s', negative = '5s'} + +[directory."smtp".lookup] +domains = ["example.org"] + +[directory."memory"] +type = "memory" + +[directory."memory".options] +catch-all = true +subaddressing = true + +[[directory."memory".users]] +name = "admin" +description = "Superuser" +secret = "changeme" +email = ["admin@example.org"] +member-of = ["superusers"] + +[[directory."memory".users]] +name = "jane" +description = "Jane Doe" +secret = "abcde" +email = ["jane@example.org", "jane.doe@example.org"] +email-list = ["info@example.org"] +member-of = ["sales", "support"] + +[[directory."memory".users]] +name = "bill" +description = "Bill Foobar" +secret = "$2y$05$bvIG6Nmid91Mu9RcmmWZfO5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe" +quota = 500000 +email = "bill@example.org" +email-list = ["info@example.org"] + +[[directory."memory".groups]] +name = "sales" +description = "Sales Team" + +[[directory."memory".groups]] +name = "support" +description = "Support Team" + +[directory."memory".lookup] +domains = ["example.org"] diff --git a/resources/config/imap.toml b/resources/config/imap.toml new file mode 100644 index 00000000..9567f156 --- /dev/null +++ b/resources/config/imap.toml @@ -0,0 +1,33 @@ +[server.listener."imap"] +bind = ["0.0.0.0:143"] +protocol = "imap" + +[server.listener."imaptls"] +bind = ["0.0.0.0:9993"] +protocol = "imap" +tls.implicit = true + +[server.listener."sieve"] +bind = ["0.0.0.0:4190"] +protocol = "managesieve" +tls.implicit = true + +[imap.request] +max-size = 52428800 + +[imap.auth] +max-failures = 3 +allow-plain-text = false + +[imap.folders.name] +shared = "Shared Folders" +all = "All Mail" + +[imap.timeout] +authenticated = "30m" +anonymous = "1m" +idle = "30m" + +[imap.rate-limit] +requests = "2000/1m" +concurrent = 4 diff --git a/resources/config/jmap.toml b/resources/config/jmap.toml new file mode 100644 index 00000000..a765b795 --- /dev/null +++ b/resources/config/jmap.toml @@ -0,0 +1,154 @@ +[server.listener."jmap"] +bind = ["0.0.0.0:__BIND_PORT__"] +url = "https://127.0.0.1:__BIND_PORT__" +protocol = "jmap" + +[store.db] +path = "__PATH__/db" + +[store.blob] +type = "local" + +[store.blob.local] +path = "__PATH__/blobs" + +[store.blob.s3] +bucket = "stalwart" +region = "eu-central-1" +access-key = "minioadmin" +secret-key = "minioadmin" +#endpoint = "" +#security-token = "" +#profile = "" +timeout = "30s" + +[jmap] +directory = "sql" + +[jmap.session.cache] +ttl = "1h" +size = 100 + +[jmap.protocol.get] +max-objects = 500 + +[jmap.protocol.set] +max-objects = 500 + +[jmap.protocol.request] +max-concurrent = 4 +max-size = 10000000 +max-calls = 16 + +[jmap.protocol.query] +max-results = 5000 + +[jmap.protocol.upload] +max-size = 50000000 +max-concurrent = 4 +ttl = "1h" + +[jmap.protocol.upload.quota] +files = 1000 +size = 50000000 + +[jmap.protocol.changes] +max-results = 5000 + +[jmap.rate-limit] +account = "1000/1m" +authentication = "10/1m" +anonymous = "100/1m" + +[jmap.rate-limit.cache] +size = 1024 + +[jmap.mailbox] +max-depth = 10 +max-name-length = 255 + +[jmap.email] +max-attachment-size = 50000000 +max-size = 75000000 + +[jmap.email.parse] +max-items = 10 + +[jmap.sieve] +disable-capabilities = [] +notification-uris = ["mailto"] +protected-headers = ["Original-Subject", "Original-From", "Received", "Auto-Submitted"] + +[jmap.sieve.limits] +name-length = 512 +max-scripts = 256 +script-size = 102400 +string-length = 4096 +variable-name-length = 32 +variable-size = 4096 +nested-blocks = 15 +nested-tests = 15 +nested-foreverypart = 3 +match-variables = 30 +local-variables = 128 +header-size = 1024 +includes = 3 +nested-includes = 3 +cpu = 5000 +redirects = 1 +received-headers = 10 +outgoing-messages = 3 + +[jmap.sieve.vacation] +default-subject = "Automated reply" +subject-prefix = "Auto: " + +[jmap.sieve.default-expiry] +vacation = "30d" +duplicate = "7d" + +[jmap.event-source] +throttle = "1s" + +[jmap.web-sockets] +throttle = "1s" +timeout = "10m" +heartbeat = "1m" + +[jmap.push] +max-total = 100 +throttle = "1ms" + +[jmap.push.attempts] +interval = "1m" +max = 3 + +[jmap.push.retry] +interval = "1s" + +[jmap.push.timeout] +request = "10s" +verify = "1s" + +[jmap.fts] +default-language = "en" + +[oauth] +key = "__OAUTH_KEY__" + +[oauth.auth] +max-attempts = 3 + +[oauth.expiry] +user-code = "30m" +token = "1h" +refresh-token = "30d" +refresh-token-renew = "4d" + +[oauth.cache] +size = 128 + +[jmap.purge.schedule] +db = "0 3 *" +blobs = "30 3 *" +sessions = "15 * *" diff --git a/resources/config/config.toml b/resources/config/smtp.toml similarity index 77% rename from resources/config/config.toml rename to resources/config/smtp.toml index f5c7e540..993dbbc0 100644 --- a/resources/config/config.toml +++ b/resources/config/smtp.toml @@ -1,72 +1,21 @@ -[server] -hostname = "__HOST__" -#greeting = "Stalwart SMTP at your service" -protocol = "smtp" - -[server.run-as] -user = "stalwart-smtp" -group = "stalwart-smtp" - [server.listener."smtp"] bind = ["0.0.0.0:25"] -max-connections = 8192 +greeting = "Stalwart SMTP at your service" +protocol = "smtp" [server.listener."submission"] bind = ["0.0.0.0:587"] -max-connections = 8192 +protocol = "smtp" [server.listener."submissions"] bind = ["0.0.0.0:465"] -max-connections = 8192 +protocol = "smtp" tls.implicit = true - [server.listener."management"] bind = ["127.0.0.1:8686"] protocol = "http" -[server.tls] -enable = true -implicit = false -timeout = "1m" -certificate = "default" -#sni = [{subject = "", certificate = ""}] -#protocols = ["TLSv1.2", TLSv1.3"] -#ciphers = [] -ignore-client-order = true - -[server.socket] -reuse-addr = true -#reuse-port = true -backlog = 1024 -#ttl = 3600 -#send-buffer-size = 65535 -#recv-buffer-size = 65535 -#linger = 1 -#tos = 1 - -[global] -shared-map = {shard = 32, capacity = 10} -#thread-pool = 8 - -#[global.tracing] -#method = "stdout" -#level = "trace" - -#[global.tracing] -#method = "open-telemetry" -#transport = "http" -#endpoint = "https://127.0.0.1/otel" -#headers = ["Authorization: "] -#level = "debug" - -[global.tracing] -method = "log" -path = "/usr/local/stalwart-smtp/logs" -prefix = "smtp.log" -rotate = "daily" -level = "info" - [session] timeout = "5m" transfer-limit = 262144000 # 250 MB @@ -88,6 +37,10 @@ requiretls = true no-soliciting = "" dsn = [ { if = "authenticated-as", ne = "", then = true}, { else = false } ] +expn = [ { if = "authenticated-as", ne = "", then = true}, + { else = false } ] +vrfy = [ { if = "authenticated-as", ne = "", then = true}, + { else = false } ] future-release = [ { if = "authenticated-as", ne = "", then = "7d"}, { else = false } ] deliver-by = [ { if = "authenticated-as", ne = "", then = "15d"}, @@ -98,7 +51,7 @@ mt-priority = [ { if = "authenticated-as", ne = "", then = "mixer"}, [session.auth] mechanisms = [ { if = "listener", ne = "smtp", then = ["plain", "login"]}, { else = [] } ] -directory = [ { if = "listener", ne = "smtp", then = "remote/imap" }, +directory = [ { if = "listener", ne = "smtp", then = "local" }, { else = false } ] require = [ { if = "listener", ne = "smtp", then = true}, { else = false } ] @@ -115,9 +68,8 @@ wait = "5s" relay = [ { if = "authenticated-as", ne = "", then = true }, { else = false } ] max-recipients = 25 -directory = [ { if = "authenticated-as", ne = "", then = "remote/lmtp" }, +directory = [ { if = "authenticated-as", ne = "", then = "local" }, { else = false } ] -domains = "list/domains" [session.rcpt.cache] entries = 1000 @@ -166,6 +118,7 @@ rate = "25/1h" [auth.dnsbl] verify = [ { if = "listener", eq = "smtp", then = ["ip", "iprev", "ehlo", "return-path", "from"] }, { else = [] } ] + [auth.dnsbl.lookup] ip = ["zen.spamhaus.org", "bl.spamcop.net", "b.barracudacentral.org"] domain = ["dbl.spamhaus.org"] @@ -194,7 +147,7 @@ verify = [ { if = "listener", eq = "smtp", then = "relaxed" }, { else = "disable" } ] [queue] -path = "/usr/local/stalwart-smtp/queue" +path = "__PATH__/queue" hash = 64 [queue.schedule] @@ -204,7 +157,7 @@ expire = "5d" [queue.outbound] #hostname = "__HOST__" -next-hop = [ { if = "rcpt-domain", in-list = "list/domains", then = "lmtp" }, +next-hop = [ { if = "rcpt-domain", in-list = "local/domains", then = "lmtp" }, { else = false } ] ip-strategy = "ipv4-then-ipv6" @@ -260,14 +213,14 @@ tlsa = 1024 mta-sts = 1024 [report] -path = "/usr/local/stalwart-smtp/reports" +path = "__PATH__/reports" hash = 64 #submitter = "mx.domain.org" [report.analysis] addresses = ["dmarc@*", "abuse@*"] forward = true -#store = "/usr/local/stalwart-smtp/incoming" +#store = "__PATH__/incoming" [report.dsn] from-name = "Mail Delivery Subsystem" @@ -314,8 +267,8 @@ max-size = 26214400 # 25 mb sign = ["rsa"] [signature."rsa"] -#public-key = "file:///usr/local/stalwart-smtp/etc/certs/dkim.crt" -private-key = "file:///usr/local/stalwart-smtp/etc/private/dkim.key" +#public-key = "file://__PATH__/etc/certs/dkim.crt" +private-key = "file://__PATH__/etc/private/dkim.key" domain = "__DOMAIN__" selector = "stalwart_smtp" headers = ["From", "To", "Date", "Subject", "Message-ID"] @@ -334,11 +287,6 @@ port = __LMTP_PORT__ protocol = "lmtp" concurrency = 10 timeout = "1m" -lookup = true - -[remote."lmtp".cache] -entries = 1000 -ttl = {positive = "1d", negative = "1h"} [remote."lmtp".tls] implicit = false @@ -348,28 +296,8 @@ allow-invalid-certs = true #username = "" #secret = "" -[remote."lmtp".limits] -errors = 3 -requests = 50 - -[remote."imap"] -address = "localhost" -port = 143 -protocol = "imap" -concurrency = 10 -timeout = "1m" -lookup = true - -[remote."imap".cache] -entries = 1000 -ttl = {positive = "1d", negative = "1h"} - -[remote."imap".tls] -implicit = false -allow-invalid-certs = true - [database."sql"] -#address = "sqlite:///usr/local/stalwart-smtp/etc/sqlite.db?mode=rwc" +#address = "sqlite://__PATH__/etc/sqlite.db?mode=rwc" address = "postgres://postgres:password@localhost/test" max-connections = 10 min-connections = 0 @@ -409,14 +337,14 @@ duplicate-expiry = "7d" connect = ''' require ["variables", "extlists", "reject"]; - if string :list "${env.remote_ip}" "list/blocked-ips" { + if string :list "${env.remote_ip}" "local/blocked-ips" { reject "Your IP '${env.remote_ip}' is not welcomed here."; } ''' ehlo = ''' require ["variables", "extlists", "reject"]; - if string :list "${env.helo_domain}" "list/blocked-domains" { + if string :list "${env.helo_domain}" "local/blocked-domains" { reject "551 5.1.1 Your domain '${env.helo_domain}' has been blacklisted."; } ''' @@ -457,13 +385,3 @@ data = ''' [management] directory = "local" -[list] -domains = ["__DOMAIN__"] -admin = ["admin:__ADMIN_PASS__"] -#blocked-ips = ["10.0.0.1"] -#blocked-domains = ["mail.spammer.com"] -#users = "file:///usr/local/stalwart-smtp/etc/users.txt" - -[certificate."default"] -cert = "file:///usr/local/stalwart-smtp/etc/certs/tls.crt" -private-key = "file:///usr/local/stalwart-smtp/etc/private/tls.key" diff --git a/tests/Cargo.toml b/tests/Cargo.toml index dce7d3b2..a7fb151e 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" resolver = "2" [features] -#default = ["sqlite"] -default = ["foundationdb"] +default = ["sqlite"] +#default = ["foundationdb"] sqlite = ["store/sqlite"] foundationdb = ["store/foundation"] diff --git a/tests/resources/imap/000.imap b/tests/resources/imap/000.imap index 80509a74..08b8ba83 100644 --- a/tests/resources/imap/000.imap +++ b/tests/resources/imap/000.imap @@ -281,6 +281,16 @@ given and this is text in the US-ASCII character set. It could have been done with explicit typing as in the next part.] +BINARY[1.1] {262} +... Some text appears here ... + +[Note that the blank between the boundary and the start +of the text in this part means no header fields were +given and this is text in the US-ASCII character set. +It could have been done with explicit typing as in the +next part.] + +BINARY.SIZE[1.1] 262 ---------------------------------- BODY[2] {111} This could have been part of the previous part, but @@ -315,6 +325,12 @@ This could have been part of the previous part, but illustrates explicit versus implicit typing of body parts. +BINARY[2.1] {111} +This could have been part of the previous part, but +illustrates explicit versus implicit typing of body +parts. + +BINARY.SIZE[2.1] 111 ---------------------------------- BODY[3] {314} --unique-boundary-2 @@ -392,6 +408,9 @@ BODY[3.1.1] {85} ... base64-encoded 8000 Hz single-channel mu-law-format audio data goes here ... +* NO [UNKNOWN-CTE] Failed to decode part 3.1.1 of message 0. + +BINARY.SIZE[3.1.1] 85 ---------------------------------- BODY[3.2] {44} ... base64-encoded image data goes here ... @@ -419,6 +438,9 @@ Content-Transfer-Encoding: base64 BODY[3.2.1] {44} ... base64-encoded image data goes here ... +* NO [UNKNOWN-CTE] Failed to decode part 3.2.1 of message 0. + +BINARY.SIZE[3.2.1] 44 ---------------------------------- BODY[4] {140} This is enriched. @@ -461,6 +483,14 @@ This is enriched. Isn't it cool? +BINARY[4.1] {140} +This is enriched. +as defined in RFC 1896 + +Isn't it +cool? + +BINARY.SIZE[4.1] 140 ---------------------------------- BODY[5] {223} From: (mailbox in US-ASCII) @@ -496,6 +526,10 @@ Content-Type: message/rfc822 BODY[5.1] {48} ... Additional text in ISO-8859-1 goes here ... +BINARY[5.1] {48} +... Additional text in ISO-8859-1 goes here ... + +BINARY.SIZE[5.1] 48 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {79} From: Nathaniel Borenstein diff --git a/tests/resources/imap/001.imap b/tests/resources/imap/001.imap index 10b84c65..5809b254 100644 --- a/tests/resources/imap/001.imap +++ b/tests/resources/imap/001.imap @@ -165,6 +165,9 @@ BODY[1.1] {79} Content-type: application/postscript Content-ID: +BINARY[1.1] {16} +[binary content] +BINARY.SIZE[1.1] 79 ---------------------------------- BODY[2] {79} Content-type: application/postscript @@ -199,6 +202,9 @@ BODY[2.1] {79} Content-type: application/postscript Content-ID: +BINARY[2.1] {16} +[binary content] +BINARY.SIZE[2.1] 79 ---------------------------------- BODY[3] {97} Content-type: application/postscript @@ -239,6 +245,9 @@ Content-ID: get RFC-MIME.DOC +BINARY[3.1] {16} +[binary content] +BINARY.SIZE[3.1] 97 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {29} From: Whomever diff --git a/tests/resources/imap/002.imap b/tests/resources/imap/002.imap index 7b6c19aa..f9f04b27 100644 --- a/tests/resources/imap/002.imap +++ b/tests/resources/imap/002.imap @@ -293,6 +293,14 @@ Fire up Air Force One! We're going South! Thanks, Al +BINARY[1.1] {61} +Fred, + +Fire up Air Force One! We're going South! + +Thanks, +Al +BINARY.SIZE[1.1] 61 ---------------------------------- BODY[2] {1979} Return-Path: @@ -478,6 +486,21 @@ Argentina. Try this for a much better map: Then again, shouldn't the CIA have something like that? Bill +BINARY[2.1.1] {355} +Hi A1, + +I finally figured out this MIME thing. Pretty cool. I'll send you +some sax music in .au files next week! + +Anyway, the attached image is really too small to get a good look at +Argentina. Try this for a much better map: + + http://www.1one1yp1anet.com/dest/sam/graphics/map-arg.htm + +Then again, shouldn't the CIA have something like that? + +Bill +BINARY.SIZE[2.1.1] 355 ---------------------------------- BODY[2.2] {389} R01GOD1hJQA1AKIAAP/////78P/omn19fQAAAAAAAAAAAAAAACwAAAAAJQA1AAAD7Qi63P5w @@ -519,6 +542,9 @@ GugmRu3CmiBt57fsVq3Y0VFKnpYdxPC6M7Ze4crnnHum4oN6LFJ1bn5NXTN7OF5fQkN5WYow BEN2dkGQGWJtSzqGTICJgnQuTJN/WJsojad9qXMuhIWdjXKjY4tenjo6tjVssk2gaWq3uGNX U6ZGxseyk8SasGw3J9GRzdTQky1iHNvcPNNI4TLeKdfMvy0vMqLrItvuxfDW8ubjueDtJufz 7itICBxISKDBgwgTKjyYAAA7 +BINARY[2.2.1] {16} +[binary content] +BINARY.SIZE[2.2.1] 288 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {125} From: Al Gore diff --git a/tests/resources/imap/003.imap b/tests/resources/imap/003.imap index 3067fffe..aa340a5c 100644 --- a/tests/resources/imap/003.imap +++ b/tests/resources/imap/003.imap @@ -120,6 +120,10 @@ Content-Type: text/plain; charset=us-ascii BODY[1.1] {48} ... plain text version of message goes here ... +BINARY[1.1] {48} +... plain text version of message goes here ... + +BINARY.SIZE[1.1] 48 ---------------------------------- BODY[2] {69} ... RFC 1896 text/enriched version of same message @@ -150,6 +154,11 @@ BODY[2.1] {69} ... RFC 1896 text/enriched version of same message goes here ... +BINARY[2.1] {69} +... RFC 1896 text/enriched version of same message + goes here ... + +BINARY.SIZE[2.1] 69 ---------------------------------- BODY[3] {51} ... fanciest version of same message goes here ... @@ -175,6 +184,9 @@ Content-Type: application/x-whatever BODY[3.1] {51} ... fanciest version of same message goes here ... +BINARY[3.1] {16} +[binary content] +BINARY.SIZE[3.1] 51 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {81} From: Nathaniel Borenstein diff --git a/tests/resources/imap/004.imap b/tests/resources/imap/004.imap index 3047b6f4..bbfee525 100644 --- a/tests/resources/imap/004.imap +++ b/tests/resources/imap/004.imap @@ -167,6 +167,10 @@ BODY[1.MIME] {2} BODY[1.1] {45} ...Introductory text or table of contents... +BINARY[1.1] {45} +...Introductory text or table of contents... + +BINARY.SIZE[1.1] 45 ---------------------------------- BODY[2] {306} ------ next message ---- @@ -252,6 +256,10 @@ BODY[2.1.MIME] {2} BODY[2.1.1] {22} ...body goes here ... +BINARY[2.1.1] {22} +...body goes here ... + +BINARY.SIZE[2.1.1] 22 ---------------------------------- BODY[2.2] {125} From: someone-else-again @@ -282,6 +290,10 @@ BODY[2.2.MIME] {2} BODY[2.2.1] {31} ... another body goes here ... +BINARY[2.2.1] {31} +... another body goes here ... + +BINARY.SIZE[2.2.1] 31 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {45} From: Moderator-Address diff --git a/tests/resources/imap/005.imap b/tests/resources/imap/005.imap index 383d10ee..46660c71 100644 --- a/tests/resources/imap/005.imap +++ b/tests/resources/imap/005.imap @@ -114,6 +114,10 @@ BODY[1.MIME] {2} BODY[1.1] {79} This is implicitly typed plain US-ASCII text. It does NOT end with a linebreak. +BINARY[1.1] {79} +This is implicitly typed plain US-ASCII text. +It does NOT end with a linebreak. +BINARY.SIZE[1.1] 79 ---------------------------------- BODY[2] {76} This is explicitly typed plain US-ASCII text. @@ -144,6 +148,11 @@ BODY[2.1] {76} This is explicitly typed plain US-ASCII text. It DOES end with a linebreak. +BINARY[2.1] {76} +This is explicitly typed plain US-ASCII text. +It DOES end with a linebreak. + +BINARY.SIZE[2.1] 76 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {81} From: Nathaniel Borenstein diff --git a/tests/resources/imap/006.imap b/tests/resources/imap/006.imap index 0ebb0bb2..6e5b1a31 100644 --- a/tests/resources/imap/006.imap +++ b/tests/resources/imap/006.imap @@ -128,6 +128,11 @@ BODY[1.1] {87} Plain text email goes here! This is the fallback if email client does not support HTML +BINARY[1.1] {87} +Plain text email goes here! +This is the fallback if email client does not support HTML + +BINARY.SIZE[1.1] 87 ---------------------------------- BODY[2] {93}

This is the HTML Section!

@@ -162,6 +167,11 @@ BODY[2.1] {93}

This is the HTML Section!

This is what displays in most modern email clients

+BINARY[2.1] {93} +

This is the HTML Section!

+

This is what displays in most modern email clients

+ +BINARY.SIZE[2.1] 93 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {53} From: sender@example.com diff --git a/tests/resources/imap/007.imap b/tests/resources/imap/007.imap index 071ea9ca..fc6e3520 100644 --- a/tests/resources/imap/007.imap +++ b/tests/resources/imap/007.imap @@ -314,6 +314,9 @@ Content-Disposition: inline ---------------------------------- BODY[1.1] {1} A +BINARY[1.1] {1} +A +BINARY.SIZE[1.1] 1 ---------------------------------- BODY[2] {608} --2 @@ -603,6 +606,9 @@ Content-Disposition: inline ---------------------------------- BODY[2.1.1.1.1] {1} B +BINARY[2.1.1.1.1] {1} +B +BINARY.SIZE[2.1.1.1.1] 1 ---------------------------------- BODY[2.1.1.2] {1} C @@ -627,6 +633,9 @@ Content-Disposition: inline ---------------------------------- BODY[2.1.1.2.1] {1} C +BINARY[2.1.1.2.1] {16} +[binary content] +BINARY.SIZE[2.1.1.2.1] 1 ---------------------------------- BODY[2.1.1.3] {1} D @@ -651,6 +660,9 @@ Content-Disposition: inline ---------------------------------- BODY[2.1.1.3.1] {1} D +BINARY[2.1.1.3.1] {1} +D +BINARY.SIZE[2.1.1.3.1] 1 ---------------------------------- BODY[2.1.2] {86} --5 @@ -710,6 +722,9 @@ Content-Type: text/html ---------------------------------- BODY[2.1.2.1.1] {14} E +BINARY[2.1.2.1.1] {14} +E +BINARY.SIZE[2.1.2.1.1] 14 ---------------------------------- BODY[2.1.2.2] {1} F @@ -732,6 +747,9 @@ Content-Type: image/jpeg ---------------------------------- BODY[2.1.2.2.1] {1} F +BINARY[2.1.2.2.1] {16} +[binary content] +BINARY.SIZE[2.1.2.2.1] 1 ---------------------------------- BODY[2.2] {1} G @@ -756,6 +774,9 @@ Content-Disposition: attachment ---------------------------------- BODY[2.2.1] {1} G +BINARY[2.2.1] {16} +[binary content] +BINARY.SIZE[2.2.1] 1 ---------------------------------- BODY[2.3] {1} H @@ -778,6 +799,9 @@ Content-Type: application/x-excel ---------------------------------- BODY[2.3.1] {1} H +BINARY[2.3.1] {16} +[binary content] +BINARY.SIZE[2.3.1] 1 ---------------------------------- BODY[2.4] {13} Subject: J @@ -802,6 +826,9 @@ Content-Type: message/rfc822 ---------------------------------- BODY[2.4.1] {1} J +BINARY[2.4.1] {1} +J +BINARY.SIZE[2.4.1] 1 ---------------------------------- BODY[3] {1} K @@ -826,6 +853,9 @@ Content-Disposition: inline ---------------------------------- BODY[3.1] {1} K +BINARY[3.1] {1} +K +BINARY.SIZE[3.1] 1 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {2} diff --git a/tests/resources/imap/008.imap b/tests/resources/imap/008.imap index 271c33f0..5fbfff2b 100644 --- a/tests/resources/imap/008.imap +++ b/tests/resources/imap/008.imap @@ -183,6 +183,9 @@ Content-Transfer-Encoding: 7bit ---------------------------------- BODY[1.1] {54} This is a message with a base64 encoded attached email +BINARY[1.1] {54} +This is a message with a base64 encoded attached email +BINARY.SIZE[1.1] 54 ---------------------------------- BODY[2] {1179} VG86ICJlbWFpbEBleGFtcGxlLmNvbSIgPGVtYWlsQGV4YW1wbGUuY29tPg0KRnJvbTogTmFtZSA8 @@ -271,6 +274,9 @@ Content-Transfer-Encoding: 7bit ---------------------------------- BODY[2.1.1] {30} This is an *HTML* test message +BINARY[2.1.1] {30} +This is an *HTML* test message +BINARY.SIZE[2.1.1] 30 ---------------------------------- BODY[2.2] {173} @@ -327,6 +333,17 @@ BODY[2.2.1] {173} +BINARY[2.2.1] {173} + + + + + + This is an HTML test message + + + +BINARY.SIZE[2.2.1] 173 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {2} diff --git a/tests/resources/imap/009.imap b/tests/resources/imap/009.imap index 05a2cd00..b1c0d45b 100644 --- a/tests/resources/imap/009.imap +++ b/tests/resources/imap/009.imap @@ -201,6 +201,9 @@ PGh0bWw+PHA+SSB3YXMgdGhpbmtpbmcgYWJvdXQgcXVpdHRpbmcgdGhlICZsZHF1bztle HBvcnRpbmcmcmRxdW87IHRvIGZvY3VzIGp1c3Qgb24gdGhlICZsZHF1bztpbXBvcnRpbm cmcmRxdW87LDwvcD48cD5idXQgdGhlbiBJIHRob3VnaHQsIHdoeSBub3QgZG8gYm90aD8 gJiN4MjYzQTs8L3A+PC9odG1sPg== +BINARY[1.1] {175} +

I was thinking about quitting the “exporting” to focus just on the “importing”,

but then I thought, why not do both? ☺

+BINARY.SIZE[1.1] 175 ---------------------------------- BODY[2] {723} From: "Cosmo Kramer" @@ -289,6 +292,9 @@ BODY[2.1.1] {228} =DD5=D85=DD5=D8-=DD5=D8,=DD5=D8/=DD5=D81=DD =005=D8*=DD5=D86=DD = =005=D8=1F=DD5=D8,=DD5=D8,=DD5=D8(=DD =005=D8-=DD5=D8)=DD5=D8"= =DD5=D8=1E=DD5=D80=DD5=D8"=DD!=00 +BINARY[2.1.1] {101} +ℌ𝔢𝔩𝔭 𝔪𝔢 𝔢𝔵𝔭𝔬𝔯𝔱 𝔪𝔶 𝔟𝔬𝔬𝔨 𝔭𝔩𝔢𝔞𝔰𝔢! +BINARY.SIZE[2.1.1] 101 ---------------------------------- BODY[2.2] {56} R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7 @@ -317,6 +323,9 @@ Content-Disposition: attachment ---------------------------------- BODY[2.2.1] {56} R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7 +BINARY[2.2.1] {16} +[binary content] +BINARY.SIZE[2.2.1] 42 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {196} From: Art Vandelay (Vandelay Industries) diff --git a/tests/resources/imap/010.imap b/tests/resources/imap/010.imap index ad798e19..eeb82cec 100644 --- a/tests/resources/imap/010.imap +++ b/tests/resources/imap/010.imap @@ -86,6 +86,9 @@ Subject: submsg Hello world +BINARY[1] {16} +[binary content] +BINARY.SIZE[1] 88 ---------------------------------- BODY[1.HEADER] {76} From: sub@domain.org @@ -106,6 +109,10 @@ Content-Type: message/rfc822 BODY[1.1] {12} Hello world +BINARY[1.1] {12} +Hello world + +BINARY.SIZE[1.1] 12 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {24} From: user@domain.org diff --git a/tests/resources/imap/011.imap b/tests/resources/imap/011.imap index f21cc311..90048eff 100644 --- a/tests/resources/imap/011.imap +++ b/tests/resources/imap/011.imap @@ -224,6 +224,9 @@ m2 body epilogue +BINARY[1] {16} +[binary content] +BINARY.SIZE[1] 260 ---------------------------------- BODY[1.HEADER] {123} From: sub@domain.org @@ -264,6 +267,9 @@ Subject: m1 m1 body +BINARY[1.1] {16} +[binary content] +BINARY.SIZE[1.1] 42 ---------------------------------- BODY[1.1.HEADER] {34} From: m1@example.com @@ -282,6 +288,10 @@ BODY[1.1.MIME] {2} BODY[1.1.1] {8} m1 body +BINARY[1.1.1] {8} +m1 body + +BINARY.SIZE[1.1.1] 8 ---------------------------------- BODY[1.2] {42} From: m2@example.com @@ -289,6 +299,9 @@ Subject: m2 m2 body +BINARY[1.2] {16} +[binary content] +BINARY.SIZE[1.2] 42 ---------------------------------- BODY[1.2.HEADER] {34} From: m2@example.com @@ -307,6 +320,10 @@ BODY[1.2.MIME] {2} BODY[1.2.1] {8} m2 body +BINARY[1.2.1] {8} +m2 body + +BINARY.SIZE[1.2.1] 8 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {24} From: user@domain.org diff --git a/tests/resources/imap/012.imap b/tests/resources/imap/012.imap index 96cee191..2144f6c2 100644 --- a/tests/resources/imap/012.imap +++ b/tests/resources/imap/012.imap @@ -187,6 +187,10 @@ Content-Type: text/x-myown; charset=us-ascii BODY[1.1] {6} hello +BINARY[1.1] {6} +hello + +BINARY.SIZE[1.1] 6 ---------------------------------- BODY[2] {280} From: sub@domain.org @@ -266,6 +270,10 @@ Content-Type: text/html BODY[2.1.1] {19}

Hello world

+BINARY[2.1.1] {19} +

Hello world

+ +BINARY.SIZE[2.1.1] 19 ---------------------------------- BODY[2.2] {20} Hello another world @@ -292,6 +300,10 @@ Content-Type: text/plain BODY[2.2.1] {20} Hello another world +BINARY[2.2.1] {20} +Hello another world + +BINARY.SIZE[2.2.1] 20 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {24} From: user@domain.org diff --git a/tests/resources/imap/013.imap b/tests/resources/imap/013.imap index 0dea734a..1fcac204 100644 --- a/tests/resources/imap/013.imap +++ b/tests/resources/imap/013.imap @@ -100,6 +100,22 @@ Then again, shouldn't the CIA have something like that? Bill +BINARY[1] {356} +Hi A1, + +I finally figured out this MIME thing. Pretty cool. I'll send you +some sax music in .au files next week! + +Anyway, the attached image is really too small to get a good look at +Argentina. Try this for a much better map: + + http://www.1one1yp1anet.com/dest/sam/graphics/map-arg.htm + +Then again, shouldn't the CIA have something like that? + +Bill + +BINARY.SIZE[1] 356 ---------------------------------- BODY[HEADER.FIELDS (FROM TO)] {107} From: Bill Clinton diff --git a/tests/resources/smtp/config/servers.toml b/tests/resources/smtp/config/servers.toml index c01a8e47..dede2d1e 100644 --- a/tests/resources/smtp/config/servers.toml +++ b/tests/resources/smtp/config/servers.toml @@ -1,14 +1,15 @@ [server] hostname = "mx.example.org" greeting = "Stalwart SMTP - hi there!" -protocol = "smtp" [server.listener."smtp"] bind = ["127.0.0.1:9925"] +protocol = "smtp" tls.implicit = false [server.listener."smtps"] bind = ["127.0.0.1:9465", "127.0.0.1:9466"] +protocol = "smtp" max-connections = 1024 tls.implicit = true tls.ciphers = ["TLS13_CHACHA20_POLY1305_SHA256", "TLS13_AES_256_GCM_SHA384"] @@ -16,6 +17,7 @@ socket.ttl = 4096 [server.listener."submission"] greeting = "Stalwart SMTP submission at your service" +protocol = "smtp" hostname = "submit.example.org" bind = "127.0.0.1:9991" #tls.sni = [{subject = "submit.example.org", certificate = "other"}, diff --git a/tests/resources/smtp/dsn/delay.eml b/tests/resources/smtp/dsn/delay.eml index f63fc195..607f8147 100644 --- a/tests/resources/smtp/dsn/delay.eml +++ b/tests/resources/smtp/dsn/delay.eml @@ -7,7 +7,7 @@ Content-Type: multipart/report; report-type="delivery-status"; --mime_boundary -Content-Type: text/plain +Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit There was a temporary problem delivering your message to the following recipients: @@ -16,7 +16,7 @@ There was a temporary problem delivering your message to the following recipient --mime_boundary -Content-Type: message/delivery-status +Content-Type: message/delivery-status; charset="utf-8" Content-Transfer-Encoding: 7bit Reporting-MTA: dns;mx.example.org @@ -31,7 +31,7 @@ Will-Retry-Until: --mime_boundary -Content-Type: message/rfc822 +Content-Type: message/rfc822; charset="utf-8" Content-Transfer-Encoding: 7bit Disclose-recipients: prohibited diff --git a/tests/resources/smtp/dsn/failure.eml b/tests/resources/smtp/dsn/failure.eml index 5c6b9a66..813b32ae 100644 --- a/tests/resources/smtp/dsn/failure.eml +++ b/tests/resources/smtp/dsn/failure.eml @@ -7,7 +7,7 @@ Content-Type: multipart/report; report-type="delivery-status"; --mime_boundary -Content-Type: text/plain +Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Your message could not be delivered to the following recipients: @@ -16,7 +16,7 @@ Your message could not be delivered to the following recipients: --mime_boundary -Content-Type: message/delivery-status +Content-Type: message/delivery-status; charset="utf-8" Content-Transfer-Encoding: 7bit Reporting-MTA: dns;mx.example.org @@ -30,7 +30,7 @@ Remote-MTA: dns;mx.example.org --mime_boundary -Content-Type: message/rfc822 +Content-Type: message/rfc822; charset="utf-8" Content-Transfer-Encoding: 7bit Disclose-recipients: prohibited diff --git a/tests/resources/smtp/dsn/mixed.eml b/tests/resources/smtp/dsn/mixed.eml index 979d1ada..a4b7595a 100644 --- a/tests/resources/smtp/dsn/mixed.eml +++ b/tests/resources/smtp/dsn/mixed.eml @@ -7,12 +7,12 @@ Content-Type: multipart/report; report-type="delivery-status"; --mime_boundary -Content-Type: text/plain +Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Your message has been partially delivered: - ----- Delivery to the following addresses was succesful ----- + ----- Delivery to the following addresses was successful ----- (delivered to 'mx2.example.org' with code 250 (2.1.5) 'Message accepted for delivery') ----- There was a temporary problem delivering to these addresses ----- @@ -23,7 +23,7 @@ Your message has been partially delivered: --mime_boundary -Content-Type: message/delivery-status +Content-Type: message/delivery-status; charset="utf-8" Content-Transfer-Encoding: 7bit Reporting-MTA: dns;mx.example.org @@ -49,7 +49,7 @@ Will-Retry-Until: --mime_boundary -Content-Type: message/rfc822 +Content-Type: message/rfc822; charset="utf-8" Content-Transfer-Encoding: 7bit Disclose-recipients: prohibited diff --git a/tests/resources/smtp/dsn/success.eml b/tests/resources/smtp/dsn/success.eml index 73ad5ae7..15c631be 100644 --- a/tests/resources/smtp/dsn/success.eml +++ b/tests/resources/smtp/dsn/success.eml @@ -7,7 +7,7 @@ Content-Type: multipart/report; report-type="delivery-status"; --mime_boundary -Content-Type: text/plain +Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Your message has been successfully delivered to the following recipients: @@ -16,7 +16,7 @@ Your message has been successfully delivered to the following recipients: --mime_boundary -Content-Type: message/delivery-status +Content-Type: message/delivery-status; charset="utf-8" Content-Transfer-Encoding: 7bit Reporting-MTA: dns;mx.example.org @@ -29,7 +29,7 @@ Remote-MTA: dns;mx2.example.org --mime_boundary -Content-Type: message/rfc822 +Content-Type: message/rfc822; charset="utf-8" Content-Transfer-Encoding: 7bit Disclose-recipients: prohibited diff --git a/tests/resources/test_config.toml b/tests/resources/test_config.toml index b032f3ce..bc4f5822 100644 --- a/tests/resources/test_config.toml +++ b/tests/resources/test_config.toml @@ -46,6 +46,7 @@ certificate = "default" [global.tracing] method = "stdout" +level = "trace" [session.ehlo] reject-non-fqdn = false @@ -119,9 +120,9 @@ files = 3 size = 50000 [jmap.rate-limit] -account.rate = "1000/1m" -authentication.rate = "100/2s" -anonymous.rate = "100/1m" +account = "1000/1m" +authentication = "100/2s" +anonymous = "100/1m" [jmap.event-source] throttle = "500ms" @@ -187,7 +188,7 @@ description = "Superusers" [oauth] key = "parerga_und_paralipomena" -max-auth-attempts = 1 +oauth.auth.max-attempts = 1 [oauth.expiry] user-code = "1s" diff --git a/tests/src/directory/imap.rs b/tests/src/directory/imap.rs index d00835ae..d15f288b 100644 --- a/tests/src/directory/imap.rs +++ b/tests/src/directory/imap.rs @@ -82,7 +82,7 @@ async fn imap_directory() { item_clone, expected.append(n), )); - let fix = "true"; + // FOX: This is a workaround for a bb8 bug, see: https://github.com/djc/bb8/issues/167 tokio::time::sleep(std::time::Duration::from_millis(100)).await; } for (result, item, expected_result) in requests { diff --git a/tests/src/directory/sql.rs b/tests/src/directory/sql.rs index bcca7ee9..f52d71a0 100644 --- a/tests/src/directory/sql.rs +++ b/tests/src/directory/sql.rs @@ -271,7 +271,7 @@ pub async fn create_test_group_with_email(handle: &dyn Directory, login: &str, n pub async fn link_test_address(handle: &dyn Directory, login: &str, address: &str, typ: &str) { handle .query( - &format!("INSERT OR IGNORE INTO emails (name, address, type) VALUES (?, ?, ?)",), + "INSERT OR IGNORE INTO emails (name, address, type) VALUES (?, ?, ?)", &[login, address, typ], ) .await diff --git a/tests/src/imap/mod.rs b/tests/src/imap/mod.rs index 669fbe42..7084302c 100644 --- a/tests/src/imap/mod.rs +++ b/tests/src/imap/mod.rs @@ -160,9 +160,9 @@ files = 3 size = 50000 [jmap.rate-limit] -account.rate = "1000/1m" -authentication.rate = "100/2s" -anonymous.rate = "100/1m" +account = "1000/1m" +authentication = "100/2s" +anonymous = "100/1m" [jmap.event-source] throttle = "500ms" @@ -207,7 +207,7 @@ remote-domains = ["remote.org", "foobar.com", "test.com", "other_domain.com"] [oauth] key = "parerga_und_paralipomena" -max-auth-attempts = 1 +oauth.auth.max-attempts = 1 [oauth.expiry] user-code = "1s" @@ -293,6 +293,9 @@ async fn init_imap_tests(delete_if_exists: bool) -> IMAPTest { jmap.store.destroy().await; } + // Assign Id 0 to admin (required for some tests) + jmap.get_account_id("admin").await.unwrap(); + IMAPTest { jmap, imap, diff --git a/tests/src/jmap/auth_oauth.rs b/tests/src/jmap/auth_oauth.rs index 22ebebf3..89e315a2 100644 --- a/tests/src/jmap/auth_oauth.rs +++ b/tests/src/jmap/auth_oauth.rs @@ -21,7 +21,10 @@ * for more details. */ -use std::{sync::Arc, time::Duration}; +use std::{ + sync::Arc, + time::{Duration, Instant}, +}; use bytes::Bytes; use jmap::{ @@ -216,6 +219,7 @@ pub async fn test(server: Arc, admin_client: &mut Client) { assert_client_auth("jdoe@example.com", "12345", &device_response, "successful").await; // Obtain token + let time_first_token = Instant::now(); let (token, refresh_token, _) = unwrap_token_response(post(&metadata.token_endpoint, &token_params).await); let refresh_token = refresh_token.unwrap(); @@ -268,9 +272,16 @@ pub async fn test(server: Arc, admin_client: &mut Client) { ("grant_type".to_string(), "refresh_token".to_string()), ("refresh_token".to_string(), refresh_token), ]); + let time_before_post: Instant = Instant::now(); let (token, new_refresh_token, _) = unwrap_token_response(post(&metadata.token_endpoint, &refresh_params).await); - assert_eq!(new_refresh_token, None); + assert_eq!( + new_refresh_token, + None, + "Refreshed token in {:?}, since start {:?}", + time_before_post.elapsed(), + time_first_token.elapsed() + ); // Wait 1 second and make sure the access token expired tokio::time::sleep(Duration::from_secs(1)).await; diff --git a/tests/src/jmap/email_changes.rs b/tests/src/jmap/email_changes.rs index 478f631d..3308306d 100644 --- a/tests/src/jmap/email_changes.rs +++ b/tests/src/jmap/email_changes.rs @@ -37,7 +37,6 @@ use store::{ pub async fn test(server: Arc, client: &mut Client) { println!("Running Email Changes tests..."); - server.store.destroy().await; client.set_default_account_id(Id::new(1)); let mut states = vec![State::Initial]; @@ -316,6 +315,7 @@ pub async fn test(server: Arc, client: &mut Client) { assert_eq!(created, vec![2, 3, 11, 12]); assert_eq!(changes.updated(), Vec::::new()); assert_eq!(changes.destroyed(), Vec::::new()); + server.store.assert_is_empty().await; } #[derive(Debug, Clone, Copy)] diff --git a/tests/src/jmap/email_query_changes.rs b/tests/src/jmap/email_query_changes.rs index c75da2bb..1bb6f09d 100644 --- a/tests/src/jmap/email_query_changes.rs +++ b/tests/src/jmap/email_query_changes.rs @@ -43,7 +43,6 @@ use crate::jmap::{ pub async fn test(server: Arc, client: &mut Client) { println!("Running Email QueryChanges tests..."); - server.store.destroy().await; let mailbox1_id = client .set_default_account_id(Id::new(1).to_string()) .mailbox_create("JMAP Changes 1", None::, Role::None) diff --git a/tests/src/jmap/mod.rs b/tests/src/jmap/mod.rs index 7d70e6df..98a66c5a 100644 --- a/tests/src/jmap/mod.rs +++ b/tests/src/jmap/mod.rs @@ -158,9 +158,9 @@ files = 3 size = 50000 [jmap.rate-limit] -account.rate = "1000/1m" -authentication.rate = "100/2s" -anonymous.rate = "100/1m" +account = "1000/1m" +authentication = "100/2s" +anonymous = "100/1m" [jmap.event-source] throttle = "500ms" @@ -205,7 +205,7 @@ remote-domains = ["remote.org", "foobar.com", "test.com", "other_domain.com"] [oauth] key = "parerga_und_paralipomena" -max-auth-attempts = 1 +oauth.auth.max-attempts = 1 [oauth.expiry] user-code = "1s" diff --git a/tests/src/smtp/inbound/data.rs b/tests/src/smtp/inbound/data.rs index 4a369423..0153be85 100644 --- a/tests/src/smtp/inbound/data.rs +++ b/tests/src/smtp/inbound/data.rs @@ -81,9 +81,6 @@ async fn data() { let mut qr = core.init_test_queue("smtp_data_test"); let directory = Config::parse(DIRECTORY).unwrap().parse_directory().unwrap(); let mut config = &mut core.session.config.rcpt; - config.lookup_domains = IfBlock::new(Some( - directory.lookups.get("local/domains").unwrap().clone(), - )); config.directory = IfBlock::new(Some(directory.directories.get("local").unwrap().clone())); let mut config = &mut core.session.config; diff --git a/tests/src/smtp/inbound/dmarc.rs b/tests/src/smtp/inbound/dmarc.rs index 456f6f38..913d598e 100644 --- a/tests/src/smtp/inbound/dmarc.rs +++ b/tests/src/smtp/inbound/dmarc.rs @@ -134,9 +134,6 @@ async fn dmarc() { let mut rr = core.init_test_report(); let directory = Config::parse(DIRECTORY).unwrap().parse_directory().unwrap(); let mut config = &mut core.session.config.rcpt; - config.lookup_domains = IfBlock::new(Some( - directory.lookups.get("local/domains").unwrap().clone(), - )); config.directory = IfBlock::new(Some(directory.directories.get("local").unwrap().clone())); let mut config = &mut core.session.config; diff --git a/tests/src/smtp/inbound/rcpt.rs b/tests/src/smtp/inbound/rcpt.rs index 5b9f2161..f12f8caa 100644 --- a/tests/src/smtp/inbound/rcpt.rs +++ b/tests/src/smtp/inbound/rcpt.rs @@ -75,9 +75,6 @@ async fn rcpt() { let mut config_ext = &mut core.session.config.extensions; let directory = Config::parse(DIRECTORY).unwrap().parse_directory().unwrap(); let mut config = &mut core.session.config.rcpt; - config.lookup_domains = IfBlock::new(Some( - directory.lookups.get("local/domains").unwrap().clone(), - )); config.directory = IfBlock::new(Some(directory.directories.get("local").unwrap().clone())); config.max_recipients = r"[{if = 'remote-ip', eq = '10.0.0.1', then = 3}, {else = 5}]" diff --git a/tests/src/smtp/inbound/sign.rs b/tests/src/smtp/inbound/sign.rs index 11f8ac71..28d30e38 100644 --- a/tests/src/smtp/inbound/sign.rs +++ b/tests/src/smtp/inbound/sign.rs @@ -152,9 +152,6 @@ async fn sign_and_seal() { let directory = Config::parse(DIRECTORY).unwrap().parse_directory().unwrap(); let mut config = &mut core.session.config.rcpt; - config.lookup_domains = IfBlock::new(Some( - directory.lookups.get("local/domains").unwrap().clone(), - )); config.directory = IfBlock::new(Some(directory.directories.get("local").unwrap().clone())); let mut config = &mut core.session.config; diff --git a/tests/src/smtp/lookup/sql.rs b/tests/src/smtp/lookup/sql.rs index ea00f1bc..a0e955f1 100644 --- a/tests/src/smtp/lookup/sql.rs +++ b/tests/src/smtp/lookup/sql.rs @@ -127,10 +127,6 @@ async fn lookup_sql() { .parse_if::>(&ctx) .map_if_block(&ctx.directory.directories, "", "") .unwrap(); - config.lookup_domains = r"'sql/domains'" - .parse_if::>(&ctx) - .map_if_block(&ctx.directory.lookups, "", "") - .unwrap(); config.relay = IfBlock::new(false); config.errors_wait = IfBlock::new(Duration::from_millis(5)); diff --git a/tests/src/smtp/mod.rs b/tests/src/smtp/mod.rs index 29e7f5e4..60818abf 100644 --- a/tests/src/smtp/mod.rs +++ b/tests/src/smtp/mod.rs @@ -220,7 +220,6 @@ impl TestConfig for SessionConfig { rcpt: Rcpt { script: IfBlock::new(None), relay: IfBlock::new(false), - lookup_domains: IfBlock::new(None), directory: IfBlock::new(None), errors_max: IfBlock::new(3), errors_wait: IfBlock::new(Duration::from_secs(1)), diff --git a/tests/src/smtp/outbound/mod.rs b/tests/src/smtp/outbound/mod.rs index 4c7ace86..d85c30c5 100644 --- a/tests/src/smtp/outbound/mod.rs +++ b/tests/src/smtp/outbound/mod.rs @@ -41,10 +41,10 @@ const SERVER: &str = " [server] hostname = 'mx.example.org' greeting = 'Test SMTP instance' -protocol = 'smtp' [server.listener.smtp-debug] bind = ['127.0.0.1:9925'] +protocol = 'smtp' [server.listener.lmtp-debug] bind = ['127.0.0.1:9924'] diff --git a/tests/src/smtp/queue/dsn.rs b/tests/src/smtp/queue/dsn.rs index 58588a16..e8204730 100644 --- a/tests/src/smtp/queue/dsn.rs +++ b/tests/src/smtp/queue/dsn.rs @@ -187,8 +187,16 @@ async fn compare_dsn(message: Box, test: &str) { let dsn = remove_ids(bytes); let dsn_expected = fs::read_to_string(&path).unwrap(); - //fs::write(&path, dsn.as_bytes()).unwrap(); - assert_eq!(dsn, dsn_expected, "Failed for {}", path.display()); + if dsn != dsn_expected { + let mut failed = PathBuf::from(&path); + failed.set_extension("failed"); + fs::write(&failed, dsn.as_bytes()).unwrap(); + panic!( + "Failed for {}, ouput saved to {}", + path.display(), + failed.display() + ); + } } fn remove_ids(message: Vec) -> String {