init backend override with list envelopes and send message

This commit is contained in:
Clément DOUIN 2023-11-26 12:16:07 +01:00
parent cec658aff4
commit 1f88b27468
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
9 changed files with 154 additions and 77 deletions

View file

@ -10,9 +10,26 @@ email = "example@localhost"
# listing envelopes or copying messages. # listing envelopes or copying messages.
backend = "imap" backend = "imap"
imap.host = "imap.gmail.com" # Override the backend used for sending messages.
imap.port = 993 message.send.backend = "smtp"
imap.login = "example@localhost"
imap.auth = "passwd" # IMAP config
# imap.Some.passwd.cmd = "pass show gmail" 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"

View file

@ -10,7 +10,10 @@ use email::smtp::{SmtpClientBuilder, SmtpClientSync};
use email::{ use email::{
account::AccountConfig, account::AccountConfig,
config::Config, 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}, maildir::{MaildirSessionBuilder, MaildirSessionSync},
sendmail::SendmailContext, sendmail::SendmailContext,
}; };
@ -18,11 +21,9 @@ use serde::{Deserialize, Serialize};
use crate::config::DeserializedConfig; use crate::config::DeserializedConfig;
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
pub enum BackendKind { pub enum BackendKind {
#[default]
None,
Maildir, Maildir,
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
Imap, Imap,
@ -35,8 +36,6 @@ pub enum BackendKind {
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct BackendContextBuilder { pub struct BackendContextBuilder {
account_config: AccountConfig,
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
imap: Option<ImapSessionBuilder>, imap: Option<ImapSessionBuilder>,
maildir: Option<MaildirSessionBuilder>, maildir: Option<MaildirSessionBuilder>,
@ -93,7 +92,7 @@ pub struct BackendBuilder(pub email::backend::BackendBuilder<BackendContextBuild
impl BackendBuilder { impl BackendBuilder {
pub async fn new(config: DeserializedConfig, account_name: Option<&str>) -> Result<Self> { pub async fn new(config: DeserializedConfig, account_name: Option<&str>) -> Result<Self> {
let (account_name, deserialized_account_config) = match account_name { let (account_name, mut deserialized_account_config) = match account_name {
Some("default") | Some("") | None => config Some("default") | Some("") | None => config
.accounts .accounts
.iter() .iter()
@ -111,6 +110,25 @@ impl BackendBuilder {
.ok_or_else(|| anyhow!("cannot find account {name}")), .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 { let config = Config {
display_name: config.display_name, display_name: config.display_name,
signature_delim: config.signature_delim, 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 { let backend_ctx_builder = BackendContextBuilder {
account_config: account_config.clone(),
maildir: deserialized_account_config maildir: deserialized_account_config
.maildir .maildir
.as_ref() .as_ref()
@ -199,28 +216,57 @@ impl BackendBuilder {
..Default::default() ..Default::default()
}; };
let backend_builder = let mut backend_builder =
email::backend::BackendBuilder::new(account_config.clone(), backend_ctx_builder) email::backend::BackendBuilder::new(account_config.clone(), backend_ctx_builder);
.with_list_folders(move |ctx| {
println!( let list_envelopes = deserialized_account_config
"deserialized_account_config: {:#?}", .envelope
deserialized_account_config .as_ref()
); .and_then(|envelope| envelope.list.as_ref())
match deserialized_account_config.backend { .and_then(|send| send.backend.as_ref())
BackendKind::Maildir if ctx.maildir.is_some() => { .or_else(|| deserialized_account_config.backend.as_ref());
ListFoldersMaildir::new(ctx.maildir.as_ref().unwrap())
} match list_envelopes {
#[cfg(feature = "imap-backend")] Some(BackendKind::Maildir) => {
BackendKind::Imap if ctx.imap.is_some() => { backend_builder = backend_builder.with_list_envelopes(|ctx| {
ListFoldersImap::new(ctx.imap.as_ref().unwrap()) ctx.maildir.as_ref().and_then(ListEnvelopesMaildir::new)
}
#[cfg(feature = "notmuch-backend")]
BackendKind::Notmuch if ctx.notmuch.is_some() => {
ListFoldersNotmuch::new(ctx.notmuch.as_ref().unwrap())
}
_ => None,
}
}); });
}
#[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)) Ok(Self(backend_builder))
} }

View file

@ -160,7 +160,7 @@ pub enum ImapAuthConfigDef {
#[serde(remote = "PasswdConfig")] #[serde(remote = "PasswdConfig")]
pub struct ImapPasswdConfigDef { pub struct ImapPasswdConfigDef {
#[serde( #[serde(
rename = "imap-passwd", rename = "passwd",
with = "SecretDef", with = "SecretDef",
default, default,
skip_serializing_if = "Secret::is_undefined" skip_serializing_if = "Secret::is_undefined"
@ -335,19 +335,19 @@ pub enum EmailTextPlainFormatDef {
pub struct OptionSmtpConfigDef; pub struct OptionSmtpConfigDef;
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)] pub struct OptionSmtpConfig {
pub enum OptionSmtpConfig { #[serde(default, skip)]
#[default] is_none: bool,
#[serde(skip_serializing)] #[serde(flatten, with = "SmtpConfigDef")]
None, inner: SmtpConfig,
Some(#[serde(with = "SmtpConfigDef")] SmtpConfig),
} }
impl From<OptionSmtpConfig> for Option<SmtpConfig> { impl From<OptionSmtpConfig> for Option<SmtpConfig> {
fn from(config: OptionSmtpConfig) -> Option<SmtpConfig> { fn from(config: OptionSmtpConfig) -> Option<SmtpConfig> {
match config { if config.is_none {
OptionSmtpConfig::None => None, None
OptionSmtpConfig::Some(config) => Some(config), } else {
Some(config.inner)
} }
} }
} }
@ -356,17 +356,11 @@ impl From<OptionSmtpConfig> for Option<SmtpConfig> {
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(remote = "SmtpConfig")] #[serde(remote = "SmtpConfig")]
struct SmtpConfigDef { struct SmtpConfigDef {
#[serde(rename = "smtp-host")]
pub host: String, pub host: String,
#[serde(rename = "smtp-port")]
pub port: u16, pub port: u16,
#[serde(rename = "smtp-ssl")]
pub ssl: Option<bool>, pub ssl: Option<bool>,
#[serde(rename = "smtp-starttls")]
pub starttls: Option<bool>, pub starttls: Option<bool>,
#[serde(rename = "smtp-insecure")]
pub insecure: Option<bool>, pub insecure: Option<bool>,
#[serde(rename = "smtp-login")]
pub login: String, pub login: String,
#[serde(flatten, with = "SmtpAuthConfigDef")] #[serde(flatten, with = "SmtpAuthConfigDef")]
pub auth: SmtpAuthConfig, pub auth: SmtpAuthConfig,
@ -374,7 +368,7 @@ struct SmtpConfigDef {
#[cfg(feature = "smtp-sender")] #[cfg(feature = "smtp-sender")]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(remote = "SmtpAuthConfig", tag = "smtp-auth")] #[serde(remote = "SmtpAuthConfig", tag = "auth")]
pub enum SmtpAuthConfigDef { pub enum SmtpAuthConfigDef {
#[serde(rename = "passwd", alias = "password", with = "SmtpPasswdConfigDef")] #[serde(rename = "passwd", alias = "password", with = "SmtpPasswdConfigDef")]
Passwd(#[serde(default)] PasswdConfig), Passwd(#[serde(default)] PasswdConfig),
@ -386,7 +380,7 @@ pub enum SmtpAuthConfigDef {
#[serde(remote = "PasswdConfig", default)] #[serde(remote = "PasswdConfig", default)]
pub struct SmtpPasswdConfigDef { pub struct SmtpPasswdConfigDef {
#[serde( #[serde(
rename = "smtp-passwd", rename = "passwd",
with = "SecretDef", with = "SecretDef",
default, default,
skip_serializing_if = "Secret::is_undefined" skip_serializing_if = "Secret::is_undefined"
@ -395,32 +389,26 @@ pub struct SmtpPasswdConfigDef {
} }
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(remote = "OAuth2Config")] #[serde(remote = "OAuth2Config", rename_all = "kebab-case")]
pub struct SmtpOAuth2ConfigDef { pub struct SmtpOAuth2ConfigDef {
#[serde(rename = "smtp-oauth2-method", with = "OAuth2MethodDef", default)] #[serde(with = "OAuth2MethodDef", default)]
pub method: OAuth2Method, pub method: OAuth2Method,
#[serde(rename = "smtp-oauth2-client-id")]
pub client_id: String, pub client_id: String,
#[serde( #[serde(
rename = "smtp-oauth2-client-secret",
with = "SecretDef", with = "SecretDef",
default, default,
skip_serializing_if = "Secret::is_undefined" skip_serializing_if = "Secret::is_undefined"
)] )]
pub client_secret: Secret, pub client_secret: Secret,
#[serde(rename = "smtp-oauth2-auth-url")]
pub auth_url: String, pub auth_url: String,
#[serde(rename = "smtp-oauth2-token-url")]
pub token_url: String, pub token_url: String,
#[serde( #[serde(
rename = "smtp-oauth2-access-token",
with = "SecretDef", with = "SecretDef",
default, default,
skip_serializing_if = "Secret::is_undefined" skip_serializing_if = "Secret::is_undefined"
)] )]
pub access_token: Secret, pub access_token: Secret,
#[serde( #[serde(
rename = "smtp-oauth2-refresh-token",
with = "SecretDef", with = "SecretDef",
default, default,
skip_serializing_if = "Secret::is_undefined" skip_serializing_if = "Secret::is_undefined"
@ -428,17 +416,11 @@ pub struct SmtpOAuth2ConfigDef {
pub refresh_token: Secret, pub refresh_token: Secret,
#[serde(flatten, with = "SmtpOAuth2ScopesDef")] #[serde(flatten, with = "SmtpOAuth2ScopesDef")]
pub scopes: OAuth2Scopes, pub scopes: OAuth2Scopes,
#[serde(rename = "smtp-oauth2-pkce", default)] #[serde(default)]
pub pkce: bool, pub pkce: bool,
#[serde( #[serde(default = "OAuth2Config::default_redirect_host")]
rename = "imap-oauth2-redirect-host",
default = "OAuth2Config::default_redirect_host"
)]
pub redirect_host: String, pub redirect_host: String,
#[serde( #[serde(default = "OAuth2Config::default_redirect_port")]
rename = "imap-oauth2-redirect-port",
default = "OAuth2Config::default_redirect_port"
)]
pub redirect_port: u16, pub redirect_port: u16,
} }
@ -476,11 +458,7 @@ impl From<OptionSendmailConfig> for Option<SendmailConfig> {
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(remote = "SendmailConfig", rename_all = "kebab-case")] #[serde(remote = "SendmailConfig", rename_all = "kebab-case")]
pub struct SendmailConfigDef { pub struct SendmailConfigDef {
#[serde( #[serde(with = "CmdDef", default = "sendmail_default_cmd")]
rename = "sendmail-cmd",
with = "CmdDef",
default = "sendmail_default_cmd"
)]
cmd: Cmd, cmd: Cmd,
} }

View file

@ -18,7 +18,10 @@ use email::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path::PathBuf}; 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. /// Represents all existing kind of account config.
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
@ -51,7 +54,10 @@ pub struct DeserializedAccountConfig {
#[serde(default, with = "OptionFolderSyncStrategyDef")] #[serde(default, with = "OptionFolderSyncStrategyDef")]
pub sync_folders_strategy: Option<FolderSyncStrategy>, pub sync_folders_strategy: Option<FolderSyncStrategy>,
pub backend: BackendKind, pub backend: Option<BackendKind>,
pub envelope: Option<EnvelopeConfig>,
pub message: Option<MessageConfig>,
#[cfg(feature = "imap-backend")] #[cfg(feature = "imap-backend")]
#[serde(default, with = "OptionImapConfigDef")] #[serde(default, with = "OptionImapConfigDef")]

View file

@ -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<EnvelopeListConfig>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct EnvelopeListConfig {
pub backend: Option<BackendKind>,
}

View file

@ -0,0 +1 @@
pub mod config;

View file

@ -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<MessageSendConfig>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct MessageSendConfig {
pub backend: Option<BackendKind>,
}

View file

@ -0,0 +1 @@
pub mod config;

View file

@ -1,2 +1,4 @@
pub mod args; pub mod args;
pub mod envelope;
pub mod handlers; pub mod handlers;
pub mod message;