From 1f88b27468accf153d15cacefdb1b00b6007cf6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Sun, 26 Nov 2023 12:16:07 +0100 Subject: [PATCH] init backend override with list envelopes and send message --- config.sample.toml | 27 +++++-- src/backend.rs | 106 ++++++++++++++++++++-------- src/config/prelude.rs | 58 +++++---------- src/domain/account/config.rs | 10 ++- src/domain/email/envelope/config.rs | 13 ++++ src/domain/email/envelope/mod.rs | 1 + src/domain/email/message/config.rs | 13 ++++ src/domain/email/message/mod.rs | 1 + src/domain/email/mod.rs | 2 + 9 files changed, 154 insertions(+), 77 deletions(-) create mode 100644 src/domain/email/envelope/config.rs create mode 100644 src/domain/email/envelope/mod.rs create mode 100644 src/domain/email/message/config.rs create mode 100644 src/domain/email/message/mod.rs diff --git a/config.sample.toml b/config.sample.toml index 2fd521c..956e5e5 100644 --- a/config.sample.toml +++ b/config.sample.toml @@ -10,9 +10,26 @@ email = "example@localhost" # listing envelopes or copying messages. backend = "imap" -imap.host = "imap.gmail.com" -imap.port = 993 -imap.login = "example@localhost" -imap.auth = "passwd" -# imap.Some.passwd.cmd = "pass show gmail" +# Override the backend used for sending messages. +message.send.backend = "smtp" + +# IMAP config +imap.host = "localhost" +imap.port = 3143 +imap.login = "example@localhost" +imap.ssl = false +imap.starttls = false +imap.insecure = true +imap.auth = "passwd" +imap.passwd.raw = "example" + +# SMTP config +smtp.host = "localhost" +smtp.port = 3025 +smtp.login = "example@localhost" +smtp.ssl = false +smtp.starttls = false +smtp.insecure = true +smtp.auth = "passwd" +smtp.passwd.raw = "example" diff --git a/src/backend.rs b/src/backend.rs index 67d1882..ad2d19f 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -10,7 +10,10 @@ use email::smtp::{SmtpClientBuilder, SmtpClientSync}; use email::{ account::AccountConfig, config::Config, - folder::list::{imap::ListFoldersImap, maildir::ListFoldersMaildir}, + email::{ + envelope::list::{imap::ListEnvelopesImap, maildir::ListEnvelopesMaildir}, + message::send_raw::{sendmail::SendRawMessageSendmail, smtp::SendRawMessageSmtp}, + }, maildir::{MaildirSessionBuilder, MaildirSessionSync}, sendmail::SendmailContext, }; @@ -18,11 +21,9 @@ use serde::{Deserialize, Serialize}; use crate::config::DeserializedConfig; -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum BackendKind { - #[default] - None, Maildir, #[cfg(feature = "imap-backend")] Imap, @@ -35,8 +36,6 @@ pub enum BackendKind { #[derive(Clone, Default)] pub struct BackendContextBuilder { - account_config: AccountConfig, - #[cfg(feature = "imap-backend")] imap: Option, maildir: Option, @@ -93,7 +92,7 @@ pub struct BackendBuilder(pub email::backend::BackendBuilder) -> Result { - let (account_name, deserialized_account_config) = match account_name { + let (account_name, mut deserialized_account_config) = match account_name { Some("default") | Some("") | None => config .accounts .iter() @@ -111,6 +110,25 @@ impl BackendBuilder { .ok_or_else(|| anyhow!("cannot find account {name}")), }?; + println!( + "deserialized_account_config: {:#?}", + deserialized_account_config + ); + + #[cfg(feature = "imap-backend")] + if let Some(imap_config) = deserialized_account_config.imap.as_mut() { + imap_config + .auth + .replace_undefined_keyring_entries(&account_name); + } + + #[cfg(feature = "smtp-sender")] + if let Some(smtp_config) = deserialized_account_config.smtp.as_mut() { + smtp_config + .auth + .replace_undefined_keyring_entries(&account_name); + } + let config = Config { display_name: config.display_name, signature_delim: config.signature_delim, @@ -166,10 +184,9 @@ impl BackendBuilder { )), }; - let account_config = config.account(account_name)?; + let account_config = config.account(&account_name)?; let backend_ctx_builder = BackendContextBuilder { - account_config: account_config.clone(), maildir: deserialized_account_config .maildir .as_ref() @@ -199,28 +216,57 @@ impl BackendBuilder { ..Default::default() }; - let backend_builder = - email::backend::BackendBuilder::new(account_config.clone(), backend_ctx_builder) - .with_list_folders(move |ctx| { - println!( - "deserialized_account_config: {:#?}", - deserialized_account_config - ); - match deserialized_account_config.backend { - BackendKind::Maildir if ctx.maildir.is_some() => { - ListFoldersMaildir::new(ctx.maildir.as_ref().unwrap()) - } - #[cfg(feature = "imap-backend")] - BackendKind::Imap if ctx.imap.is_some() => { - ListFoldersImap::new(ctx.imap.as_ref().unwrap()) - } - #[cfg(feature = "notmuch-backend")] - BackendKind::Notmuch if ctx.notmuch.is_some() => { - ListFoldersNotmuch::new(ctx.notmuch.as_ref().unwrap()) - } - _ => None, - } + let mut backend_builder = + email::backend::BackendBuilder::new(account_config.clone(), backend_ctx_builder); + + let list_envelopes = deserialized_account_config + .envelope + .as_ref() + .and_then(|envelope| envelope.list.as_ref()) + .and_then(|send| send.backend.as_ref()) + .or_else(|| deserialized_account_config.backend.as_ref()); + + match list_envelopes { + Some(BackendKind::Maildir) => { + backend_builder = backend_builder.with_list_envelopes(|ctx| { + ctx.maildir.as_ref().and_then(ListEnvelopesMaildir::new) }); + } + #[cfg(feature = "imap-backend")] + Some(BackendKind::Imap) => { + backend_builder = backend_builder + .with_list_envelopes(|ctx| ctx.imap.as_ref().and_then(ListEnvelopesImap::new)); + } + #[cfg(feature = "notmuch-backend")] + Some(BackendKind::Notmuch) => { + backend_builder = backend_builder.with_list_envelopes(|ctx| { + ctx.notmuch.as_ref().and_then(ListEnvelopesNotmuch::new) + }); + } + _ => (), + } + + let send_msg = deserialized_account_config + .message + .as_ref() + .and_then(|msg| msg.send.as_ref()) + .and_then(|send| send.backend.as_ref()) + .or_else(|| deserialized_account_config.backend.as_ref()); + + match send_msg { + #[cfg(feature = "smtp-sender")] + Some(BackendKind::Smtp) => { + backend_builder = backend_builder.with_send_raw_message(|ctx| { + ctx.smtp.as_ref().and_then(SendRawMessageSmtp::new) + }); + } + Some(BackendKind::Sendmail) => { + backend_builder = backend_builder.with_send_raw_message(|ctx| { + ctx.sendmail.as_ref().and_then(SendRawMessageSendmail::new) + }); + } + _ => (), + } Ok(Self(backend_builder)) } diff --git a/src/config/prelude.rs b/src/config/prelude.rs index 861fb0a..0b24daa 100644 --- a/src/config/prelude.rs +++ b/src/config/prelude.rs @@ -160,7 +160,7 @@ pub enum ImapAuthConfigDef { #[serde(remote = "PasswdConfig")] pub struct ImapPasswdConfigDef { #[serde( - rename = "imap-passwd", + rename = "passwd", with = "SecretDef", default, skip_serializing_if = "Secret::is_undefined" @@ -335,19 +335,19 @@ pub enum EmailTextPlainFormatDef { pub struct OptionSmtpConfigDef; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum OptionSmtpConfig { - #[default] - #[serde(skip_serializing)] - None, - Some(#[serde(with = "SmtpConfigDef")] SmtpConfig), +pub struct OptionSmtpConfig { + #[serde(default, skip)] + is_none: bool, + #[serde(flatten, with = "SmtpConfigDef")] + inner: SmtpConfig, } impl From for Option { fn from(config: OptionSmtpConfig) -> Option { - match config { - OptionSmtpConfig::None => None, - OptionSmtpConfig::Some(config) => Some(config), + if config.is_none { + None + } else { + Some(config.inner) } } } @@ -356,17 +356,11 @@ impl From for Option { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(remote = "SmtpConfig")] struct SmtpConfigDef { - #[serde(rename = "smtp-host")] pub host: String, - #[serde(rename = "smtp-port")] pub port: u16, - #[serde(rename = "smtp-ssl")] pub ssl: Option, - #[serde(rename = "smtp-starttls")] pub starttls: Option, - #[serde(rename = "smtp-insecure")] pub insecure: Option, - #[serde(rename = "smtp-login")] pub login: String, #[serde(flatten, with = "SmtpAuthConfigDef")] pub auth: SmtpAuthConfig, @@ -374,7 +368,7 @@ struct SmtpConfigDef { #[cfg(feature = "smtp-sender")] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(remote = "SmtpAuthConfig", tag = "smtp-auth")] +#[serde(remote = "SmtpAuthConfig", tag = "auth")] pub enum SmtpAuthConfigDef { #[serde(rename = "passwd", alias = "password", with = "SmtpPasswdConfigDef")] Passwd(#[serde(default)] PasswdConfig), @@ -386,7 +380,7 @@ pub enum SmtpAuthConfigDef { #[serde(remote = "PasswdConfig", default)] pub struct SmtpPasswdConfigDef { #[serde( - rename = "smtp-passwd", + rename = "passwd", with = "SecretDef", default, skip_serializing_if = "Secret::is_undefined" @@ -395,32 +389,26 @@ pub struct SmtpPasswdConfigDef { } #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(remote = "OAuth2Config")] +#[serde(remote = "OAuth2Config", rename_all = "kebab-case")] pub struct SmtpOAuth2ConfigDef { - #[serde(rename = "smtp-oauth2-method", with = "OAuth2MethodDef", default)] + #[serde(with = "OAuth2MethodDef", default)] pub method: OAuth2Method, - #[serde(rename = "smtp-oauth2-client-id")] pub client_id: String, #[serde( - rename = "smtp-oauth2-client-secret", with = "SecretDef", default, skip_serializing_if = "Secret::is_undefined" )] pub client_secret: Secret, - #[serde(rename = "smtp-oauth2-auth-url")] pub auth_url: String, - #[serde(rename = "smtp-oauth2-token-url")] pub token_url: String, #[serde( - rename = "smtp-oauth2-access-token", with = "SecretDef", default, skip_serializing_if = "Secret::is_undefined" )] pub access_token: Secret, #[serde( - rename = "smtp-oauth2-refresh-token", with = "SecretDef", default, skip_serializing_if = "Secret::is_undefined" @@ -428,17 +416,11 @@ pub struct SmtpOAuth2ConfigDef { pub refresh_token: Secret, #[serde(flatten, with = "SmtpOAuth2ScopesDef")] pub scopes: OAuth2Scopes, - #[serde(rename = "smtp-oauth2-pkce", default)] + #[serde(default)] pub pkce: bool, - #[serde( - rename = "imap-oauth2-redirect-host", - default = "OAuth2Config::default_redirect_host" - )] + #[serde(default = "OAuth2Config::default_redirect_host")] pub redirect_host: String, - #[serde( - rename = "imap-oauth2-redirect-port", - default = "OAuth2Config::default_redirect_port" - )] + #[serde(default = "OAuth2Config::default_redirect_port")] pub redirect_port: u16, } @@ -476,11 +458,7 @@ impl From for Option { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(remote = "SendmailConfig", rename_all = "kebab-case")] pub struct SendmailConfigDef { - #[serde( - rename = "sendmail-cmd", - with = "CmdDef", - default = "sendmail_default_cmd" - )] + #[serde(with = "CmdDef", default = "sendmail_default_cmd")] cmd: Cmd, } diff --git a/src/domain/account/config.rs b/src/domain/account/config.rs index e574f4c..5639306 100644 --- a/src/domain/account/config.rs +++ b/src/domain/account/config.rs @@ -18,7 +18,10 @@ use email::{ use serde::{Deserialize, Serialize}; use std::{collections::HashMap, path::PathBuf}; -use crate::{backend::BackendKind, config::prelude::*}; +use crate::{ + backend::BackendKind, config::prelude::*, email::envelope::config::EnvelopeConfig, + message::config::MessageConfig, +}; /// Represents all existing kind of account config. #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] @@ -51,7 +54,10 @@ pub struct DeserializedAccountConfig { #[serde(default, with = "OptionFolderSyncStrategyDef")] pub sync_folders_strategy: Option, - pub backend: BackendKind, + pub backend: Option, + + pub envelope: Option, + pub message: Option, #[cfg(feature = "imap-backend")] #[serde(default, with = "OptionImapConfigDef")] diff --git a/src/domain/email/envelope/config.rs b/src/domain/email/envelope/config.rs new file mode 100644 index 0000000..a13476e --- /dev/null +++ b/src/domain/email/envelope/config.rs @@ -0,0 +1,13 @@ +use ::serde::{Deserialize, Serialize}; + +use crate::backend::BackendKind; + +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +pub struct EnvelopeConfig { + pub list: Option, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +pub struct EnvelopeListConfig { + pub backend: Option, +} diff --git a/src/domain/email/envelope/mod.rs b/src/domain/email/envelope/mod.rs new file mode 100644 index 0000000..ef68c36 --- /dev/null +++ b/src/domain/email/envelope/mod.rs @@ -0,0 +1 @@ +pub mod config; diff --git a/src/domain/email/message/config.rs b/src/domain/email/message/config.rs new file mode 100644 index 0000000..16eb198 --- /dev/null +++ b/src/domain/email/message/config.rs @@ -0,0 +1,13 @@ +use ::serde::{Deserialize, Serialize}; + +use crate::backend::BackendKind; + +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +pub struct MessageConfig { + pub send: Option, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +pub struct MessageSendConfig { + pub backend: Option, +} diff --git a/src/domain/email/message/mod.rs b/src/domain/email/message/mod.rs new file mode 100644 index 0000000..ef68c36 --- /dev/null +++ b/src/domain/email/message/mod.rs @@ -0,0 +1 @@ +pub mod config; diff --git a/src/domain/email/mod.rs b/src/domain/email/mod.rs index b0b957b..5de2dfe 100644 --- a/src/domain/email/mod.rs +++ b/src/domain/email/mod.rs @@ -1,2 +1,4 @@ pub mod args; +pub mod envelope; pub mod handlers; +pub mod message;