mirror of
https://github.com/soywod/himalaya.git
synced 2024-09-28 20:21:13 +00:00
fix cargo features
This commit is contained in:
parent
f9b92e6e7a
commit
d26314cd48
1710
Cargo.lock
generated
1710
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
34
Cargo.toml
34
Cargo.toml
|
@ -1,12 +1,12 @@
|
|||
[package]
|
||||
name = "himalaya"
|
||||
description = "CLI to manage emails"
|
||||
version = "1.0.0-beta.4"
|
||||
version = "1.0.0"
|
||||
authors = ["soywod <clement.douin@posteo.net>"]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
categories = ["command-line-interface", "command-line-utilities", "email"]
|
||||
keywords = ["cli", "email", "imap", "smtp", "sync"]
|
||||
categories = ["command-line-utilities", "email"]
|
||||
keywords = ["cli", "email", "imap", "maildir", "smtp"]
|
||||
homepage = "https://pimalaya.org/"
|
||||
documentation = "https://pimalaya.org/himalaya/cli/latest/"
|
||||
repository = "https://github.com/soywod/himalaya/"
|
||||
|
@ -23,8 +23,9 @@ default = [
|
|||
"smtp",
|
||||
"sendmail",
|
||||
|
||||
"account-discovery",
|
||||
"account-sync",
|
||||
"wizard",
|
||||
# "keyring",
|
||||
# "oauth2",
|
||||
|
||||
# "pgp-commands",
|
||||
# "pgp-gpg",
|
||||
|
@ -37,8 +38,9 @@ notmuch = ["email-lib/notmuch"]
|
|||
smtp = ["email-lib/smtp"]
|
||||
sendmail = ["email-lib/sendmail"]
|
||||
|
||||
account-discovery = ["email-lib/account-discovery"]
|
||||
account-sync = ["email-lib/account-sync", "maildir"]
|
||||
keyring = ["email-lib/keyring", "secret-lib?/keyring-tokio"]
|
||||
oauth2 = ["dep:oauth-lib", "email-lib/oauth2"]
|
||||
wizard = ["dep:secret-lib", "email-lib/autoconfig"]
|
||||
|
||||
pgp = []
|
||||
pgp-commands = ["email-lib/pgp-commands", "mml-lib/pgp-commands", "pgp"]
|
||||
|
@ -48,7 +50,7 @@ pgp-native = ["email-lib/pgp-native", "mml-lib/pgp-native", "pgp"]
|
|||
[dependencies]
|
||||
ariadne = "0.2"
|
||||
async-trait = "0.1"
|
||||
clap = { version = "4.4", features = ["derive", "wrap_help", "env"] }
|
||||
clap = { version = "4.4", features = ["derive", "env", "wrap_help"] }
|
||||
clap_complete = "4.4"
|
||||
clap_mangen = "0.2"
|
||||
color-eyre = "0.6.3"
|
||||
|
@ -56,19 +58,19 @@ comfy-table = "7.1.1"
|
|||
console = "0.15.2"
|
||||
crossterm = "0.27"
|
||||
dirs = "4"
|
||||
email-lib = { version = "=0.24.1", default-features = false, features = ["derive", "tracing"] }
|
||||
email-lib = { version = "=0.24.1", default-features = false, features = ["derive", "thread", "tracing"] }
|
||||
email_address = "0.2.4"
|
||||
erased-serde = "0.3"
|
||||
indicatif = "0.17"
|
||||
inquire = "0.7.4"
|
||||
mail-builder = "0.3"
|
||||
md5 = "0.7"
|
||||
mml-lib = { version = "=1.0.12", default-features = false, features = ["derive"] }
|
||||
oauth-lib = "=0.1.1"
|
||||
mml-lib = { version = "=1.0.13", default-features = false, features = ["derive"] }
|
||||
oauth-lib = { version = "=0.1.1", optional = true }
|
||||
once_cell = "1.16"
|
||||
petgraph = "0.6"
|
||||
process-lib = { version = "=0.4.2", features = ["derive"] }
|
||||
secret-lib = { version = "=0.4.4", features = ["derive"] }
|
||||
secret-lib = { version = "=0.4.5", default-features = false, features = ["command", "derive"], optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde-toml-merge = "0.3"
|
||||
serde_json = "1"
|
||||
|
@ -86,8 +88,6 @@ url = "2.2"
|
|||
uuid = { version = "0.8", features = ["v4"] }
|
||||
|
||||
[patch.crates-io]
|
||||
# WIP: transition from `imap` to `imap-{types,codec,client}`
|
||||
email-lib = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||
imap-client = { git = "https://github.com/soywod/imap-client.git" }
|
||||
imap-codec = { git = "https://github.com/duesee/imap-codec.git" }
|
||||
imap-types = { git = "https://github.com/duesee/imap-codec.git" }
|
||||
email-lib = { path = "/home/soywod/sourcehut/pimalaya/email" }
|
||||
secret-lib = { path = "/home/soywod/sourcehut/pimalaya/secret" }
|
||||
mml-lib = { path = "/home/soywod/sourcehut/pimalaya/mml" }
|
||||
|
|
|
@ -26,11 +26,7 @@ impl AccountCheckUpCommand {
|
|||
|
||||
printer.log("Checking configuration integrity…")?;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
account,
|
||||
#[cfg(feature = "account-sync")]
|
||||
true,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(account)?;
|
||||
let used_backends = toml_account_config.get_used_backends();
|
||||
|
||||
printer.log("Checking backend context integrity…")?;
|
||||
|
@ -76,7 +72,7 @@ impl AccountCheckUpCommand {
|
|||
|
||||
#[cfg(feature = "notmuch")]
|
||||
{
|
||||
printer.print("Checking Notmuch integrity…")?;
|
||||
printer.log("Checking Notmuch integrity…")?;
|
||||
|
||||
let notmuch = ctx_builder
|
||||
.notmuch
|
||||
|
|
|
@ -42,6 +42,7 @@ impl AccountConfigureCommand {
|
|||
if let Some(ref config) = account_config.imap {
|
||||
let reset = match &config.auth {
|
||||
ImapAuthConfig::Passwd(config) => config.reset().await,
|
||||
#[cfg(feature = "oauth2")]
|
||||
ImapAuthConfig::OAuth2(config) => config.reset().await,
|
||||
};
|
||||
if let Err(err) = reset {
|
||||
|
@ -54,6 +55,7 @@ impl AccountConfigureCommand {
|
|||
if let Some(ref config) = account_config.smtp {
|
||||
let reset = match &config.auth {
|
||||
SmtpAuthConfig::Passwd(config) => config.reset().await,
|
||||
#[cfg(feature = "oauth2")]
|
||||
SmtpAuthConfig::OAuth2(config) => config.reset().await,
|
||||
};
|
||||
if let Err(err) = reset {
|
||||
|
@ -74,6 +76,7 @@ impl AccountConfigureCommand {
|
|||
ImapAuthConfig::Passwd(config) => {
|
||||
config.configure(|| prompt::passwd("IMAP password")).await
|
||||
}
|
||||
#[cfg(feature = "oauth2")]
|
||||
ImapAuthConfig::OAuth2(config) => {
|
||||
config
|
||||
.configure(|| prompt::secret("IMAP OAuth 2.0 client secret"))
|
||||
|
@ -88,6 +91,7 @@ impl AccountConfigureCommand {
|
|||
SmtpAuthConfig::Passwd(config) => {
|
||||
config.configure(|| prompt::passwd("SMTP password")).await
|
||||
}
|
||||
#[cfg(feature = "oauth2")]
|
||||
SmtpAuthConfig::OAuth2(config) => {
|
||||
config
|
||||
.configure(|| prompt::secret("SMTP OAuth 2.0 client secret"))
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
mod check_up;
|
||||
mod configure;
|
||||
mod list;
|
||||
#[cfg(feature = "account-sync")]
|
||||
mod sync;
|
||||
|
||||
use color_eyre::Result;
|
||||
use clap::Subcommand;
|
||||
use color_eyre::Result;
|
||||
|
||||
use crate::{config::TomlConfig, printer::Printer};
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use self::sync::AccountSyncCommand;
|
||||
use self::{
|
||||
check_up::AccountCheckUpCommand, configure::AccountConfigureCommand, list::AccountListCommand,
|
||||
};
|
||||
|
@ -30,10 +26,6 @@ pub enum AccountSubcommand {
|
|||
|
||||
#[command(alias = "lst")]
|
||||
List(AccountListCommand),
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(alias = "synchronize", alias = "synchronise")]
|
||||
Sync(AccountSyncCommand),
|
||||
}
|
||||
|
||||
impl AccountSubcommand {
|
||||
|
@ -43,8 +35,6 @@ impl AccountSubcommand {
|
|||
Self::CheckUp(cmd) => cmd.execute(printer, config).await,
|
||||
Self::Configure(cmd) => cmd.execute(printer, config).await,
|
||||
Self::List(cmd) => cmd.execute(printer, config).await,
|
||||
#[cfg(feature = "account-sync")]
|
||||
Self::Sync(cmd) => cmd.execute(printer, config).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,268 +0,0 @@
|
|||
use clap::{ArgAction, Parser};
|
||||
use color_eyre::{eyre::bail, eyre::eyre, Result};
|
||||
use email::backend::context::BackendContextBuilder;
|
||||
#[cfg(feature = "imap")]
|
||||
use email::imap::ImapContextBuilder;
|
||||
use email::{
|
||||
account::sync::AccountSyncBuilder,
|
||||
backend::BackendBuilder,
|
||||
folder::sync::config::FolderSyncStrategy,
|
||||
sync::{hash::SyncHash, SyncEvent},
|
||||
};
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{
|
||||
collections::{BTreeSet, HashMap},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
account::arg::name::OptionalAccountNameArg, backend::BackendKind, config::TomlConfig,
|
||||
printer::Printer,
|
||||
};
|
||||
|
||||
static MAIN_PROGRESS_STYLE: Lazy<ProgressStyle> = Lazy::new(|| {
|
||||
ProgressStyle::with_template(" {spinner:.dim} {msg:.dim}\n {wide_bar:.cyan/blue} \n").unwrap()
|
||||
});
|
||||
|
||||
static SUB_PROGRESS_STYLE: Lazy<ProgressStyle> = Lazy::new(|| {
|
||||
ProgressStyle::with_template(
|
||||
" {prefix:.bold} — {wide_msg:.dim} \n {wide_bar:.black/black} {percent}% ",
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
static SUB_PROGRESS_DONE_STYLE: Lazy<ProgressStyle> = Lazy::new(|| {
|
||||
ProgressStyle::with_template(" {prefix:.bold} \n {wide_bar:.green} {percent}% ").unwrap()
|
||||
});
|
||||
|
||||
/// Synchronize an account.
|
||||
///
|
||||
/// This command allows you to synchronize all folders and emails
|
||||
/// (including envelopes and messages) of a given account into a local
|
||||
/// Maildir folder.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct AccountSyncCommand {
|
||||
#[command(flatten)]
|
||||
pub account: OptionalAccountNameArg,
|
||||
|
||||
/// Run the synchronization without applying any changes.
|
||||
///
|
||||
/// Instead, a report will be printed to stdout containing all the
|
||||
/// changes the synchronization plan to do.
|
||||
#[arg(long, short)]
|
||||
pub dry_run: bool,
|
||||
|
||||
/// Synchronize only specific folders.
|
||||
///
|
||||
/// Only the given folders will be synchronized (including
|
||||
/// associated envelopes and messages). Useful when you need to
|
||||
/// speed up the synchronization process. A good usecase is to
|
||||
/// synchronize only the INBOX in order to quickly check for new
|
||||
/// messages.
|
||||
#[arg(long, short = 'f')]
|
||||
#[arg(value_name = "FOLDER", action = ArgAction::Append)]
|
||||
#[arg(conflicts_with = "exclude_folder", conflicts_with = "all_folders")]
|
||||
pub include_folder: Vec<String>,
|
||||
|
||||
/// Omit specific folders from the synchronization.
|
||||
///
|
||||
/// The given folders will be excluded from the synchronization
|
||||
/// (including associated envelopes and messages). Useful when you
|
||||
/// have heavy folders that you do not want to take care of, or to
|
||||
/// speed up the synchronization process.
|
||||
#[arg(long, short = 'x')]
|
||||
#[arg(value_name = "FOLDER", action = ArgAction::Append)]
|
||||
#[arg(conflicts_with = "include_folder", conflicts_with = "all_folders")]
|
||||
pub exclude_folder: Vec<String>,
|
||||
|
||||
/// Synchronize all exsting folders.
|
||||
#[arg(long, short = 'A')]
|
||||
#[arg(conflicts_with = "include_folder", conflicts_with = "exclude_folder")]
|
||||
pub all_folders: bool,
|
||||
}
|
||||
|
||||
impl AccountSyncCommand {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing sync account command");
|
||||
|
||||
let account = self.account.name.as_deref();
|
||||
let (toml_account_config, account_config) =
|
||||
config.clone().into_account_configs(account, true)?;
|
||||
let account_name = account_config.name.as_str();
|
||||
|
||||
match toml_account_config.sync_kind() {
|
||||
Some(BackendKind::Imap) | Some(BackendKind::ImapCache) => {
|
||||
let imap_config = toml_account_config
|
||||
.imap
|
||||
.as_ref()
|
||||
.map(Clone::clone)
|
||||
.map(Arc::new)
|
||||
.ok_or_else(|| eyre!("imap config not found"))?;
|
||||
let imap_ctx = ImapContextBuilder::new(account_config.clone(), imap_config)
|
||||
.with_prebuilt_credentials()
|
||||
.await?;
|
||||
let imap = BackendBuilder::new(account_config.clone(), imap_ctx);
|
||||
self.sync(printer, account_name, imap).await
|
||||
}
|
||||
Some(backend) => bail!("backend {backend:?} not supported for synchronization"),
|
||||
None => bail!("no backend configured for synchronization"),
|
||||
}
|
||||
}
|
||||
|
||||
async fn sync(
|
||||
self,
|
||||
printer: &mut impl Printer,
|
||||
account_name: &str,
|
||||
right: BackendBuilder<impl BackendContextBuilder + SyncHash + 'static>,
|
||||
) -> Result<()> {
|
||||
let included_folders = BTreeSet::from_iter(self.include_folder);
|
||||
let excluded_folders = BTreeSet::from_iter(self.exclude_folder);
|
||||
|
||||
let folder_filters = if !included_folders.is_empty() {
|
||||
Some(FolderSyncStrategy::Include(included_folders))
|
||||
} else if !excluded_folders.is_empty() {
|
||||
Some(FolderSyncStrategy::Exclude(excluded_folders))
|
||||
} else if self.all_folders {
|
||||
Some(FolderSyncStrategy::All)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let sync_builder =
|
||||
AccountSyncBuilder::try_new(right)?.with_some_folder_filters(folder_filters);
|
||||
|
||||
if self.dry_run {
|
||||
let report = sync_builder.with_dry_run(true).sync().await?;
|
||||
let mut hunks_count = report.folder.patch.len();
|
||||
|
||||
if !report.folder.patch.is_empty() {
|
||||
printer.log("Folders patch:")?;
|
||||
for (hunk, _) in report.folder.patch {
|
||||
printer.log(format!(" - {hunk}"))?;
|
||||
}
|
||||
printer.log("")?;
|
||||
}
|
||||
|
||||
if !report.email.patch.is_empty() {
|
||||
printer.log("Envelopes patch:")?;
|
||||
for (hunk, _) in report.email.patch {
|
||||
hunks_count += 1;
|
||||
printer.log(format!(" - {hunk}"))?;
|
||||
}
|
||||
printer.log("")?;
|
||||
}
|
||||
|
||||
printer.out(format!(
|
||||
"Estimated patch length for account {account_name} to be synchronized: {hunks_count}"
|
||||
))?;
|
||||
} else if printer.is_json() {
|
||||
sync_builder.sync().await?;
|
||||
printer.out(format!("Account {account_name} successfully synchronized!"))?;
|
||||
} else {
|
||||
let multi = MultiProgress::new();
|
||||
let sub_progresses = Mutex::new(HashMap::new());
|
||||
let main_progress = multi.add(
|
||||
ProgressBar::new(100)
|
||||
.with_style(MAIN_PROGRESS_STYLE.clone())
|
||||
.with_message("Listing folders…"),
|
||||
);
|
||||
|
||||
main_progress.tick();
|
||||
|
||||
let report = sync_builder
|
||||
.with_handler(move |evt| {
|
||||
match evt {
|
||||
SyncEvent::ListedAllFolders => {
|
||||
main_progress.set_message("Synchronizing folders…");
|
||||
}
|
||||
SyncEvent::ProcessedAllFolderHunks => {
|
||||
main_progress.set_message("Listing envelopes…");
|
||||
}
|
||||
SyncEvent::GeneratedEmailPatch(patches) => {
|
||||
let patches_len = patches.values().flatten().count();
|
||||
main_progress.set_length(patches_len as u64);
|
||||
main_progress.set_position(0);
|
||||
main_progress.set_message("Synchronizing emails…");
|
||||
|
||||
let mut envelopes_progresses = sub_progresses.lock().unwrap();
|
||||
for (folder, patch) in patches {
|
||||
let progress = ProgressBar::new(patch.len() as u64)
|
||||
.with_style(SUB_PROGRESS_STYLE.clone())
|
||||
.with_prefix(folder.clone())
|
||||
.with_finish(ProgressFinish::AndClear);
|
||||
let progress = multi.add(progress);
|
||||
envelopes_progresses.insert(folder, progress.clone());
|
||||
}
|
||||
}
|
||||
SyncEvent::ProcessedEmailHunk(hunk) => {
|
||||
main_progress.inc(1);
|
||||
let mut progresses = sub_progresses.lock().unwrap();
|
||||
if let Some(progress) = progresses.get_mut(hunk.folder()) {
|
||||
progress.inc(1);
|
||||
if progress.position() == (progress.length().unwrap() - 1) {
|
||||
progress.set_style(SUB_PROGRESS_DONE_STYLE.clone())
|
||||
} else {
|
||||
progress.set_message(format!("{hunk}…"));
|
||||
}
|
||||
}
|
||||
}
|
||||
SyncEvent::ProcessedAllEmailHunks => {
|
||||
let mut progresses = sub_progresses.lock().unwrap();
|
||||
for progress in progresses.values() {
|
||||
progress.finish_and_clear()
|
||||
}
|
||||
progresses.clear();
|
||||
|
||||
main_progress.set_length(100);
|
||||
main_progress.set_position(100);
|
||||
main_progress.set_message("Expunging folders…");
|
||||
}
|
||||
SyncEvent::ExpungedAllFolders => {
|
||||
main_progress.finish_and_clear();
|
||||
}
|
||||
_ => {
|
||||
main_progress.tick();
|
||||
}
|
||||
};
|
||||
|
||||
async { Ok(()) }
|
||||
})
|
||||
.sync()
|
||||
.await?;
|
||||
|
||||
let folders_patch_err = report
|
||||
.folder
|
||||
.patch
|
||||
.iter()
|
||||
.filter_map(|(hunk, err)| err.as_ref().map(|err| (hunk, err)))
|
||||
.collect::<Vec<_>>();
|
||||
if !folders_patch_err.is_empty() {
|
||||
printer.log("")?;
|
||||
printer.log("Errors occurred while applying the folders patch:")?;
|
||||
folders_patch_err
|
||||
.iter()
|
||||
.try_for_each(|(hunk, err)| printer.log(format!(" - {hunk}: {err}")))?;
|
||||
}
|
||||
|
||||
let envelopes_patch_err = report
|
||||
.email
|
||||
.patch
|
||||
.iter()
|
||||
.filter_map(|(hunk, err)| err.as_ref().map(|err| (hunk, err)))
|
||||
.collect::<Vec<_>>();
|
||||
if !envelopes_patch_err.is_empty() {
|
||||
printer.log("")?;
|
||||
printer.log("Errors occurred while applying the envelopes patch:")?;
|
||||
for (hunk, err) in envelopes_patch_err {
|
||||
printer.log(format!(" - {hunk}: {err}"))?;
|
||||
}
|
||||
}
|
||||
|
||||
printer.out(format!("Account {account_name} successfully synchronized!"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -24,25 +24,6 @@ use crate::{
|
|||
folder::config::FolderConfig, message::config::MessageConfig,
|
||||
};
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
pub struct SyncConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
pub enable: Option<bool>,
|
||||
pub dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl From<SyncConfig> for email::account::sync::config::SyncConfig {
|
||||
fn from(config: SyncConfig) -> Self {
|
||||
Self {
|
||||
enable: config.enable,
|
||||
dir: config.dir,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents all existing kind of account config.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
|
@ -55,8 +36,6 @@ pub struct TomlAccountConfig {
|
|||
pub downloads_dir: Option<PathBuf>,
|
||||
pub backend: Option<BackendKind>,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
pub sync: Option<SyncConfig>,
|
||||
#[cfg(feature = "pgp")]
|
||||
pub pgp: Option<PgpConfig>,
|
||||
|
||||
|
@ -79,13 +58,6 @@ pub struct TomlAccountConfig {
|
|||
}
|
||||
|
||||
impl TomlAccountConfig {
|
||||
pub fn sync_kind(&self) -> Option<&BackendKind> {
|
||||
self.sync
|
||||
.as_ref()
|
||||
.and_then(|sync| sync.backend.as_ref())
|
||||
.or(self.backend.as_ref())
|
||||
}
|
||||
|
||||
pub fn add_folder_kind(&self) -> Option<&BackendKind> {
|
||||
self.folder
|
||||
.as_ref()
|
||||
|
@ -150,14 +122,6 @@ impl TomlAccountConfig {
|
|||
.or(self.backend.as_ref())
|
||||
}
|
||||
|
||||
pub fn watch_envelopes_kind(&self) -> Option<&BackendKind> {
|
||||
self.envelope
|
||||
.as_ref()
|
||||
.and_then(|envelope| envelope.watch.as_ref())
|
||||
.and_then(|watch| watch.backend.as_ref())
|
||||
.or(self.backend.as_ref())
|
||||
}
|
||||
|
||||
pub fn add_flags_kind(&self) -> Option<&BackendKind> {
|
||||
self.flag
|
||||
.as_ref()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
pub mod arg;
|
||||
pub mod command;
|
||||
pub mod config;
|
||||
#[cfg(feature = "wizard")]
|
||||
pub(crate) mod wizard;
|
||||
|
||||
use comfy_table::{presets, Attribute, Cell, Color, ContentArrangement, Row, Table};
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
#[cfg(feature = "account-sync")]
|
||||
use crate::account::config::SyncConfig;
|
||||
use color_eyre::{eyre::OptionExt, Result};
|
||||
#[cfg(feature = "account-sync")]
|
||||
use email_address::EmailAddress;
|
||||
use inquire::validator::{ErrorMessage, Validation};
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
|
||||
#[cfg(feature = "account-discovery")]
|
||||
use crate::wizard_warn;
|
||||
use crate::{
|
||||
backend::{self, config::BackendConfig, BackendKind},
|
||||
|
@ -34,14 +30,11 @@ pub(crate) async fn configure() -> Result<Option<(String, TomlAccountConfig)>> {
|
|||
|
||||
let addr = EmailAddress::from_str(&config.email).unwrap();
|
||||
|
||||
#[cfg(feature = "account-discovery")]
|
||||
#[cfg(feature = "wizard")]
|
||||
let autoconfig_email = config.email.to_owned();
|
||||
#[cfg(feature = "account-discovery")]
|
||||
let autoconfig = tokio::spawn(async move {
|
||||
email::account::discover::from_addr(&autoconfig_email)
|
||||
.await
|
||||
.ok()
|
||||
});
|
||||
#[cfg(feature = "wizard")]
|
||||
let autoconfig =
|
||||
tokio::spawn(async move { email::autoconfig::from_addr(&autoconfig_email).await.ok() });
|
||||
|
||||
let account_name = inquire::Text::new("Account name: ")
|
||||
.with_default(
|
||||
|
@ -65,12 +58,12 @@ pub(crate) async fn configure() -> Result<Option<(String, TomlAccountConfig)>> {
|
|||
));
|
||||
|
||||
let email = &config.email;
|
||||
#[cfg(feature = "account-discovery")]
|
||||
#[cfg(feature = "wizard")]
|
||||
let autoconfig = autoconfig.await?;
|
||||
#[cfg(feature = "account-discovery")]
|
||||
#[cfg(feature = "wizard")]
|
||||
let autoconfig = autoconfig.as_ref();
|
||||
|
||||
#[cfg(feature = "account-discovery")]
|
||||
#[cfg(feature = "wizard")]
|
||||
if let Some(config) = autoconfig {
|
||||
if config.is_gmail() {
|
||||
println!();
|
||||
|
@ -83,7 +76,7 @@ pub(crate) async fn configure() -> Result<Option<(String, TomlAccountConfig)>> {
|
|||
match backend::wizard::configure(
|
||||
&account_name,
|
||||
email,
|
||||
#[cfg(feature = "account-discovery")]
|
||||
#[cfg(feature = "wizard")]
|
||||
autoconfig,
|
||||
)
|
||||
.await?
|
||||
|
@ -109,7 +102,7 @@ pub(crate) async fn configure() -> Result<Option<(String, TomlAccountConfig)>> {
|
|||
match backend::wizard::configure_sender(
|
||||
&account_name,
|
||||
email,
|
||||
#[cfg(feature = "account-discovery")]
|
||||
#[cfg(feature = "wizard")]
|
||||
autoconfig,
|
||||
)
|
||||
.await?
|
||||
|
@ -139,21 +132,5 @@ pub(crate) async fn configure() -> Result<Option<(String, TomlAccountConfig)>> {
|
|||
_ => (),
|
||||
};
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
{
|
||||
let should_configure_sync =
|
||||
inquire::Confirm::new("Do you need offline access for your account?")
|
||||
.with_default(false)
|
||||
.prompt_skippable()?
|
||||
.unwrap_or_default();
|
||||
|
||||
if should_configure_sync {
|
||||
config.sync = Some(SyncConfig {
|
||||
enable: Some(true),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some((account_name, config)))
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
pub mod config;
|
||||
|
||||
#[cfg(feature = "wizard")]
|
||||
pub(crate) mod wizard;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use color_eyre::Result;
|
||||
use std::{fmt::Display, ops::Deref, sync::Arc};
|
||||
use tracing::instrument;
|
||||
|
||||
#[cfg(feature = "imap")]
|
||||
use email::imap::{ImapContextBuilder, ImapContextSync};
|
||||
|
@ -24,7 +27,6 @@ use email::{
|
|||
get::GetEnvelope,
|
||||
list::{ListEnvelopes, ListEnvelopesOptions},
|
||||
thread::ThreadEnvelopes,
|
||||
watch::WatchEnvelopes,
|
||||
Id, SingleId,
|
||||
},
|
||||
flag::{add::AddFlags, remove::RemoveFlags, set::SetFlags, Flag, Flags},
|
||||
|
@ -59,8 +61,6 @@ pub enum BackendKind {
|
|||
|
||||
#[cfg(feature = "imap")]
|
||||
Imap,
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
ImapCache,
|
||||
|
||||
#[cfg(feature = "maildir")]
|
||||
Maildir,
|
||||
|
@ -85,8 +85,6 @@ impl Display for BackendKind {
|
|||
|
||||
#[cfg(feature = "imap")]
|
||||
Self::Imap => "IMAP",
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Self::ImapCache => "IMAP cache",
|
||||
|
||||
#[cfg(feature = "maildir")]
|
||||
Self::Maildir => "Maildir",
|
||||
|
@ -112,9 +110,6 @@ pub struct BackendContextBuilder {
|
|||
#[cfg(feature = "imap")]
|
||||
pub imap: Option<ImapContextBuilder>,
|
||||
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
pub imap_cache: Option<MaildirContextBuilder>,
|
||||
|
||||
#[cfg(feature = "maildir")]
|
||||
pub maildir: Option<MaildirContextBuilder>,
|
||||
|
||||
|
@ -156,26 +151,6 @@ impl BackendContextBuilder {
|
|||
}
|
||||
},
|
||||
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
imap_cache: {
|
||||
let builder = toml_account_config
|
||||
.imap
|
||||
.as_ref()
|
||||
.filter(|_| kinds.contains(&&BackendKind::ImapCache))
|
||||
.map(Clone::clone)
|
||||
.map(Arc::new)
|
||||
.map(|imap_config| {
|
||||
email::backend::context::BackendContextBuilder::try_to_sync_cache_builder(
|
||||
&ImapContextBuilder::new(account_config.clone(), imap_config),
|
||||
&account_config,
|
||||
)
|
||||
});
|
||||
match builder {
|
||||
Some(builder) => Some(builder?),
|
||||
None => None,
|
||||
}
|
||||
},
|
||||
|
||||
#[cfg(feature = "maildir")]
|
||||
maildir: toml_account_config
|
||||
.maildir
|
||||
|
@ -227,11 +202,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.add_folder_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.add_folder_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.add_folder()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.add_folder_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -244,11 +214,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.list_folders_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.list_folders_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.list_folders()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.list_folders_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -261,11 +226,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.expunge_folder_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.expunge_folder_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.expunge_folder()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.expunge_folder_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -278,11 +238,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.purge_folder_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.purge_folder_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.purge_folder()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.purge_folder_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -295,11 +250,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.delete_folder_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.delete_folder_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.delete_folder()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.delete_folder_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -312,11 +262,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.get_envelope_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.get_envelope_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.get_envelope()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.get_envelope_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -329,11 +274,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.list_envelopes_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.list_envelopes_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.list_envelopes()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.list_envelopes_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -346,11 +286,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.thread_envelopes_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.thread_envelopes_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.thread_envelopes()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.thread_envelopes_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -359,32 +294,10 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
fn watch_envelopes(&self) -> Option<BackendFeature<Self::Context, dyn WatchEnvelopes>> {
|
||||
match self.toml_account_config.watch_envelopes_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.watch_envelopes_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.watch_envelopes()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.watch_envelopes_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
Some(BackendKind::Notmuch) => self.watch_envelopes_with_some(&self.notmuch),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_flags(&self) -> Option<BackendFeature<Self::Context, dyn AddFlags>> {
|
||||
match self.toml_account_config.add_flags_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.add_flags_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.add_flags()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.add_flags_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -397,11 +310,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.set_flags_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.set_flags_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.set_flags()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.set_flags_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -414,11 +322,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.remove_flags_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.remove_flags_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.remove_flags()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.remove_flags_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -431,11 +334,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.add_message_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.add_message_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.add_message()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.add_message_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -458,11 +356,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.peek_messages_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.peek_messages_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.peek_messages()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.peek_messages_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -475,11 +368,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.get_messages_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.get_messages_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.get_messages()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.get_messages_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -492,11 +380,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.copy_messages_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.copy_messages_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.copy_messages()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.copy_messages_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -509,11 +392,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.move_messages_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.move_messages_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.move_messages()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.move_messages_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -526,11 +404,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
match self.toml_account_config.delete_messages_kind() {
|
||||
#[cfg(feature = "imap")]
|
||||
Some(BackendKind::Imap) => self.delete_messages_with_some(&self.imap),
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
let f = self.imap_cache.as_ref()?.delete_messages()?;
|
||||
Some(Arc::new(move |ctx| f(ctx.imap_cache.as_ref()?)))
|
||||
}
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => self.delete_messages_with_some(&self.maildir),
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -547,11 +420,6 @@ impl email::backend::context::BackendContextBuilder for BackendContextBuilder {
|
|||
ctx.imap = Some(imap.build().await?);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
if let Some(maildir) = self.imap_cache {
|
||||
ctx.imap_cache = Some(maildir.build().await?);
|
||||
}
|
||||
|
||||
#[cfg(feature = "maildir")]
|
||||
if let Some(maildir) = self.maildir {
|
||||
ctx.maildir = Some(maildir.build().await?);
|
||||
|
@ -581,9 +449,6 @@ pub struct BackendContext {
|
|||
#[cfg(feature = "imap")]
|
||||
pub imap: Option<ImapContextSync>,
|
||||
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
pub imap_cache: Option<MaildirContextSync>,
|
||||
|
||||
#[cfg(feature = "maildir")]
|
||||
pub maildir: Option<MaildirContextSync>,
|
||||
|
||||
|
@ -663,37 +528,28 @@ impl Backend {
|
|||
})
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
fn build_id_mapper(
|
||||
&self,
|
||||
folder: &str,
|
||||
backend_kind: Option<&BackendKind>,
|
||||
) -> Result<IdMapper> {
|
||||
#[allow(unused_mut)]
|
||||
let mut id_mapper = IdMapper::Dummy;
|
||||
|
||||
match backend_kind {
|
||||
#[cfg(feature = "maildir")]
|
||||
Some(BackendKind::Maildir) => {
|
||||
if let Some(BackendKind::Maildir) = backend_kind {
|
||||
if let Some(_) = &self.toml_account_config.maildir {
|
||||
id_mapper = IdMapper::new(&self.backend.account_config, folder)?;
|
||||
return Ok(IdMapper::new(&self.backend.account_config, folder)?);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "imap", feature = "account-sync"))]
|
||||
Some(BackendKind::ImapCache) => {
|
||||
id_mapper = IdMapper::new(&self.backend.account_config, folder)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "notmuch")]
|
||||
Some(BackendKind::Notmuch) => {
|
||||
if let Some(BackendKind::Notmuch) = backend_kind {
|
||||
if let Some(_) = &self.toml_account_config.notmuch {
|
||||
id_mapper = IdMapper::new(&self.backend.account_config, folder)?;
|
||||
return Ok(IdMapper::new(&self.backend.account_config, folder)?);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
|
||||
Ok(id_mapper)
|
||||
Ok(IdMapper::Dummy)
|
||||
}
|
||||
|
||||
pub async fn list_envelopes(
|
||||
|
@ -868,11 +724,6 @@ impl Backend {
|
|||
self.backend.send_message_then_save_copy(msg).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn watch_envelopes(&self, folder: &str) -> Result<()> {
|
||||
self.backend.watch_envelopes(folder).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Backend {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use color_eyre::Result;
|
||||
#[cfg(feature = "account-discovery")]
|
||||
use email::account::discover::config::AutoConfig;
|
||||
use email::autoconfig::config::AutoConfig;
|
||||
use inquire::Select;
|
||||
|
||||
#[cfg(feature = "imap")]
|
||||
|
@ -35,7 +34,7 @@ const SEND_MESSAGE_BACKEND_KINDS: &[BackendKind] = &[
|
|||
pub(crate) async fn configure(
|
||||
account_name: &str,
|
||||
email: &str,
|
||||
#[cfg(feature = "account-discovery")] autoconfig: Option<&AutoConfig>,
|
||||
autoconfig: Option<&AutoConfig>,
|
||||
) -> Result<Option<BackendConfig>> {
|
||||
let kind = Select::new("Default email backend", DEFAULT_BACKEND_KINDS.to_vec())
|
||||
.with_starting_cursor(0)
|
||||
|
@ -47,7 +46,7 @@ pub(crate) async fn configure(
|
|||
imap::wizard::configure(
|
||||
account_name,
|
||||
email,
|
||||
#[cfg(feature = "account-discovery")]
|
||||
#[cfg(feature = "wizard")]
|
||||
autoconfig,
|
||||
)
|
||||
.await?,
|
||||
|
@ -65,7 +64,7 @@ pub(crate) async fn configure(
|
|||
pub(crate) async fn configure_sender(
|
||||
account_name: &str,
|
||||
email: &str,
|
||||
#[cfg(feature = "account-discovery")] autoconfig: Option<&AutoConfig>,
|
||||
autoconfig: Option<&AutoConfig>,
|
||||
) -> Result<Option<BackendConfig>> {
|
||||
let kind = Select::new(
|
||||
"Backend for sending messages",
|
||||
|
@ -80,7 +79,7 @@ pub(crate) async fn configure_sender(
|
|||
smtp::wizard::configure(
|
||||
account_name,
|
||||
email,
|
||||
#[cfg(feature = "account-discovery")]
|
||||
#[cfg(feature = "wizard")]
|
||||
autoconfig,
|
||||
)
|
||||
.await?,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#[cfg(feature = "wizard")]
|
||||
pub mod wizard;
|
||||
|
||||
use color_eyre::{
|
||||
|
@ -7,7 +8,7 @@ use color_eyre::{
|
|||
use dirs::{config_dir, home_dir};
|
||||
use email::{
|
||||
account::config::AccountConfig, config::Config, envelope::config::EnvelopeConfig,
|
||||
flag::config::FlagConfig, folder::config::FolderConfig, message::config::MessageConfig,
|
||||
folder::config::FolderConfig, message::config::MessageConfig,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_toml_merge::merge;
|
||||
|
@ -16,9 +17,9 @@ use std::{collections::HashMap, fs, path::PathBuf, sync::Arc};
|
|||
use toml::{self, Value};
|
||||
use tracing::debug;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::backend::BackendKind;
|
||||
use crate::{account::config::TomlAccountConfig, wizard_warn};
|
||||
use crate::account::config::TomlAccountConfig;
|
||||
#[cfg(feature = "wizard")]
|
||||
use crate::wizard_warn;
|
||||
|
||||
/// Represents the user config file.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
|
@ -84,9 +85,8 @@ impl TomlConfig {
|
|||
/// program stops.
|
||||
///
|
||||
/// NOTE: the wizard can only be used with interactive shells.
|
||||
#[cfg(feature = "wizard")]
|
||||
async fn from_wizard(path: &PathBuf) -> Result<Self> {
|
||||
use std::process;
|
||||
|
||||
wizard_warn!("Cannot find existing configuration at {path:?}.");
|
||||
|
||||
let confirm = inquire::Confirm::new("Would you like to create one with the wizard? ")
|
||||
|
@ -95,10 +95,15 @@ impl TomlConfig {
|
|||
.unwrap_or_default();
|
||||
|
||||
if !confirm {
|
||||
process::exit(0);
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
wizard::configure(path).await
|
||||
return wizard::configure(path).await;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "wizard"))]
|
||||
async fn from_wizard(path: &PathBuf) -> Result<Self> {
|
||||
bail!("Cannot find existing configuration at {path:?}.");
|
||||
}
|
||||
|
||||
/// Read and parse the TOML configuration from default paths.
|
||||
|
@ -182,14 +187,14 @@ impl TomlConfig {
|
|||
.ok_or_else(|| eyre!("cannot find account {name}")),
|
||||
}?;
|
||||
|
||||
#[cfg(feature = "imap")]
|
||||
#[cfg(all(feature = "imap", feature = "keyring"))]
|
||||
if let Some(imap_config) = toml_account_config.imap.as_mut() {
|
||||
imap_config
|
||||
.auth
|
||||
.replace_undefined_keyring_entries(&account_name)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "smtp")]
|
||||
#[cfg(all(feature = "smtp", feature = "keyring"))]
|
||||
if let Some(smtp_config) = toml_account_config.smtp.as_mut() {
|
||||
smtp_config
|
||||
.auth
|
||||
|
@ -203,21 +208,8 @@ impl TomlConfig {
|
|||
pub fn into_account_configs(
|
||||
self,
|
||||
account_name: Option<&str>,
|
||||
#[cfg(feature = "account-sync")] disable_cache: bool,
|
||||
) -> Result<(Arc<TomlAccountConfig>, Arc<AccountConfig>)> {
|
||||
#[cfg_attr(not(feature = "account-sync"), allow(unused_mut))]
|
||||
let (account_name, mut toml_account_config) =
|
||||
self.into_toml_account_config(account_name)?;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
if let Some(true) = toml_account_config.sync.as_ref().and_then(|c| c.enable) {
|
||||
if !disable_cache {
|
||||
toml_account_config.backend = match toml_account_config.backend {
|
||||
Some(BackendKind::Imap) => Some(BackendKind::ImapCache),
|
||||
backend => backend,
|
||||
}
|
||||
}
|
||||
}
|
||||
let (account_name, toml_account_config) = self.into_toml_account_config(account_name)?;
|
||||
|
||||
let config = Config {
|
||||
display_name: self.display_name,
|
||||
|
@ -239,31 +231,19 @@ impl TomlConfig {
|
|||
folder: config.folder.map(|c| FolderConfig {
|
||||
aliases: c.alias,
|
||||
list: c.list.map(|c| c.remote),
|
||||
#[cfg(feature = "account-sync")]
|
||||
sync: c.sync,
|
||||
}),
|
||||
envelope: config.envelope.map(|c| EnvelopeConfig {
|
||||
list: c.list.map(|c| c.remote),
|
||||
thread: c.thread.map(|c| c.remote),
|
||||
watch: c.watch.map(|c| c.remote),
|
||||
#[cfg(feature = "account-sync")]
|
||||
sync: c.sync,
|
||||
}),
|
||||
flag: config.flag.map(|c| FlagConfig {
|
||||
#[cfg(feature = "account-sync")]
|
||||
sync: c.sync,
|
||||
}),
|
||||
flag: None,
|
||||
message: config.message.map(|c| MessageConfig {
|
||||
read: c.read.map(|c| c.remote),
|
||||
write: c.write.map(|c| c.remote),
|
||||
send: c.send.map(|c| c.remote),
|
||||
delete: c.delete.map(Into::into),
|
||||
#[cfg(feature = "account-sync")]
|
||||
sync: c.sync,
|
||||
}),
|
||||
template: config.template,
|
||||
#[cfg(feature = "account-sync")]
|
||||
sync: config.sync.map(Into::into),
|
||||
#[cfg(feature = "pgp")]
|
||||
pgp: config.pgp,
|
||||
},
|
||||
|
|
|
@ -152,9 +152,6 @@ fn pretty_serialize(config: &TomlConfig) -> Result<String> {
|
|||
#[cfg(feature = "sendmail")]
|
||||
set_table_dotted(item, "sendmail");
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
set_table_dotted(item, "sync");
|
||||
|
||||
#[cfg(feature = "pgp")]
|
||||
set_table_dotted(item, "pgp");
|
||||
})
|
||||
|
@ -214,68 +211,6 @@ fn set_tables_dotted<'a>(item: &'a mut Item, keys: impl IntoIterator<Item = &'a
|
|||
// )
|
||||
// }
|
||||
|
||||
// #[cfg(feature = "account-sync")]
|
||||
// #[test]
|
||||
// fn pretty_serialize_sync_all() {
|
||||
// use email::account::sync::config::SyncConfig;
|
||||
|
||||
// assert_eq(
|
||||
// TomlAccountConfig {
|
||||
// email: "test@localhost".into(),
|
||||
// sync: Some(SyncConfig {
|
||||
// enable: Some(false),
|
||||
// dir: Some("/tmp/test".into()),
|
||||
// ..Default::default()
|
||||
// }),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// r#"[accounts.test]
|
||||
// email = "test@localhost"
|
||||
// sync.enable = false
|
||||
// sync.dir = "/tmp/test"
|
||||
// "#,
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[cfg(feature = "account-sync")]
|
||||
// #[test]
|
||||
// fn pretty_serialize_sync_include() {
|
||||
// use email::{
|
||||
// account::sync::config::SyncConfig,
|
||||
// folder::sync::config::{FolderSyncConfig, FolderSyncStrategy},
|
||||
// };
|
||||
// use std::collections::BTreeSet;
|
||||
|
||||
// use crate::folder::config::FolderConfig;
|
||||
|
||||
// assert_eq(
|
||||
// TomlAccountConfig {
|
||||
// email: "test@localhost".into(),
|
||||
// sync: Some(SyncConfig {
|
||||
// enable: Some(true),
|
||||
// dir: Some("/tmp/test".into()),
|
||||
// ..Default::default()
|
||||
// }),
|
||||
// folder: Some(FolderConfig {
|
||||
// sync: Some(FolderSyncConfig {
|
||||
// filter: FolderSyncStrategy::Include(BTreeSet::from_iter(["test".into()])),
|
||||
// ..Default::default()
|
||||
// }),
|
||||
// ..Default::default()
|
||||
// }),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// r#"[accounts.test]
|
||||
// email = "test@localhost"
|
||||
// sync.enable = true
|
||||
// sync.dir = "/tmp/test"
|
||||
// folder.sync.filter.include = ["test"]
|
||||
// folder.sync.permissions.create = true
|
||||
// folder.sync.permissions.delete = true
|
||||
// "#,
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[cfg(feature = "imap")]
|
||||
// #[test]
|
||||
// fn pretty_serialize_imap_passwd_cmd() {
|
||||
|
|
|
@ -8,8 +8,6 @@ use email::{
|
|||
use std::process::exit;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
envelope::EnvelopesTable, folder::arg::name::FolderNameOptionalFlag, printer::Printer,
|
||||
|
@ -37,10 +35,6 @@ pub struct ListEnvelopesCommand {
|
|||
#[arg(long, short = 's', value_name = "NUMBER")]
|
||||
pub page_size: Option<usize>,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
|
||||
|
@ -130,8 +124,6 @@ impl Default for ListEnvelopesCommand {
|
|||
folder: Default::default(),
|
||||
page: 1,
|
||||
page_size: Default::default(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
cache: Default::default(),
|
||||
account: Default::default(),
|
||||
query: Default::default(),
|
||||
table_max_width: Default::default(),
|
||||
|
@ -143,11 +135,9 @@ impl ListEnvelopesCommand {
|
|||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing list envelopes command");
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let folder = &self.folder.name;
|
||||
let page = 1.max(self.page) - 1;
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
pub mod list;
|
||||
pub mod thread;
|
||||
pub mod watch;
|
||||
|
||||
use clap::Subcommand;
|
||||
use color_eyre::Result;
|
||||
|
||||
use crate::{config::TomlConfig, printer::Printer};
|
||||
|
||||
use self::{
|
||||
list::ListEnvelopesCommand, thread::ThreadEnvelopesCommand, watch::WatchEnvelopesCommand,
|
||||
};
|
||||
use self::{list::ListEnvelopesCommand, thread::ThreadEnvelopesCommand};
|
||||
|
||||
/// Manage envelopes.
|
||||
///
|
||||
|
@ -24,9 +21,6 @@ pub enum EnvelopeSubcommand {
|
|||
|
||||
#[command()]
|
||||
Thread(ThreadEnvelopesCommand),
|
||||
|
||||
#[command()]
|
||||
Watch(WatchEnvelopesCommand),
|
||||
}
|
||||
|
||||
impl EnvelopeSubcommand {
|
||||
|
@ -35,7 +29,6 @@ impl EnvelopeSubcommand {
|
|||
match self {
|
||||
Self::List(cmd) => cmd.execute(printer, config).await,
|
||||
Self::Thread(cmd) => cmd.execute(printer, config).await,
|
||||
Self::Watch(cmd) => cmd.execute(printer, config).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ use email::{
|
|||
use std::process::exit;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
envelope::EnvelopesTree, folder::arg::name::FolderNameOptionalFlag, printer::Printer,
|
||||
|
@ -24,10 +22,6 @@ pub struct ThreadEnvelopesCommand {
|
|||
#[command(flatten)]
|
||||
pub folder: FolderNameOptionalFlag,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
|
||||
|
@ -43,11 +37,9 @@ impl ThreadEnvelopesCommand {
|
|||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing thread envelopes command");
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let folder = &self.folder.name;
|
||||
let thread_envelopes_kind = toml_account_config.thread_envelopes_kind();
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
use email::backend::feature::BackendFeatureSource;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
folder::arg::name::FolderNameOptionalFlag, printer::Printer,
|
||||
};
|
||||
|
||||
/// Watch envelopes for changes.
|
||||
///
|
||||
/// This command allows you to watch a folder and execute hooks when
|
||||
/// changes occur on envelopes.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct WatchEnvelopesCommand {
|
||||
#[command(flatten)]
|
||||
pub folder: FolderNameOptionalFlag,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
||||
impl WatchEnvelopesCommand {
|
||||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing watch envelopes command");
|
||||
|
||||
let folder = &self.folder.name;
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
|
||||
let watch_envelopes_kind = toml_account_config.watch_envelopes_kind();
|
||||
|
||||
let backend = Backend::new(
|
||||
toml_account_config.clone(),
|
||||
account_config,
|
||||
watch_envelopes_kind,
|
||||
|builder| builder.set_watch_envelopes(BackendFeatureSource::Context),
|
||||
)
|
||||
.await?;
|
||||
|
||||
printer.out(format!(
|
||||
"Start watching folder {folder} for envelopes changes…"
|
||||
))?;
|
||||
|
||||
backend.watch_envelopes(folder).await
|
||||
}
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
#[cfg(feature = "account-sync")]
|
||||
use email::envelope::sync::config::EnvelopeSyncConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
|
||||
|
@ -9,10 +7,7 @@ use crate::backend::BackendKind;
|
|||
pub struct EnvelopeConfig {
|
||||
pub list: Option<ListEnvelopesConfig>,
|
||||
pub thread: Option<ThreadEnvelopesConfig>,
|
||||
pub watch: Option<WatchEnvelopesConfig>,
|
||||
pub get: Option<GetEnvelopeConfig>,
|
||||
#[cfg(feature = "account-sync")]
|
||||
pub sync: Option<EnvelopeSyncConfig>,
|
||||
}
|
||||
|
||||
impl EnvelopeConfig {
|
||||
|
@ -23,10 +18,6 @@ impl EnvelopeConfig {
|
|||
kinds.extend(list.get_used_backends());
|
||||
}
|
||||
|
||||
if let Some(watch) = &self.watch {
|
||||
kinds.extend(watch.get_used_backends());
|
||||
}
|
||||
|
||||
if let Some(get) = &self.get {
|
||||
kinds.extend(get.get_used_backends());
|
||||
}
|
||||
|
@ -75,26 +66,6 @@ impl ThreadEnvelopesConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct WatchEnvelopesConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub remote: email::envelope::watch::config::WatchEnvelopeConfig,
|
||||
}
|
||||
|
||||
impl WatchEnvelopesConfig {
|
||||
pub fn get_used_backends(&self) -> HashSet<&BackendKind> {
|
||||
let mut kinds = HashSet::default();
|
||||
|
||||
if let Some(kind) = &self.backend {
|
||||
kinds.insert(kind);
|
||||
}
|
||||
|
||||
kinds
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct GetEnvelopeConfig {
|
||||
pub backend: Option<BackendKind>,
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::Result;
|
|||
use email::backend::feature::BackendFeatureSource;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
backend::Backend,
|
||||
|
@ -26,10 +24,6 @@ pub struct FlagAddCommand {
|
|||
#[command(flatten)]
|
||||
pub args: IdsAndFlagsArgs,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -40,11 +34,9 @@ impl FlagAddCommand {
|
|||
|
||||
let folder = &self.folder.name;
|
||||
let (ids, flags) = into_tuple(&self.args.ids_and_flags);
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let add_flags_kind = toml_account_config.add_flags_kind();
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::Result;
|
|||
use email::backend::feature::BackendFeatureSource;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
backend::Backend,
|
||||
|
@ -26,10 +24,6 @@ pub struct FlagRemoveCommand {
|
|||
#[command(flatten)]
|
||||
pub args: IdsAndFlagsArgs,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -40,11 +34,9 @@ impl FlagRemoveCommand {
|
|||
|
||||
let folder = &self.folder.name;
|
||||
let (ids, flags) = into_tuple(&self.args.ids_and_flags);
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let remove_flags_kind = toml_account_config.remove_flags_kind();
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::Result;
|
|||
use email::backend::feature::BackendFeatureSource;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
backend::Backend,
|
||||
|
@ -26,10 +24,6 @@ pub struct FlagSetCommand {
|
|||
#[command(flatten)]
|
||||
pub args: IdsAndFlagsArgs,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -40,11 +34,9 @@ impl FlagSetCommand {
|
|||
|
||||
let folder = &self.folder.name;
|
||||
let (ids, flags) = into_tuple(&self.args.ids_and_flags);
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let set_flags_kind = toml_account_config.set_flags_kind();
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#[cfg(feature = "account-sync")]
|
||||
use email::flag::sync::config::FlagSyncConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
|
||||
|
@ -10,8 +8,6 @@ pub struct FlagConfig {
|
|||
pub add: Option<FlagAddConfig>,
|
||||
pub set: Option<FlagSetConfig>,
|
||||
pub remove: Option<FlagRemoveConfig>,
|
||||
#[cfg(feature = "account-sync")]
|
||||
pub sync: Option<FlagSyncConfig>,
|
||||
}
|
||||
|
||||
impl FlagConfig {
|
||||
|
|
|
@ -5,8 +5,6 @@ use std::{fs, path::PathBuf};
|
|||
use tracing::info;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameOptionalFlag,
|
||||
|
@ -25,10 +23,6 @@ pub struct AttachmentDownloadCommand {
|
|||
#[command(flatten)]
|
||||
pub envelopes: EnvelopeIdsArgs,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -40,11 +34,9 @@ impl AttachmentDownloadCommand {
|
|||
let folder = &self.folder.name;
|
||||
let ids = &self.envelopes.ids;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let get_messages_kind = toml_account_config.get_messages_kind();
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::Result;
|
|||
use email::backend::feature::BackendFeatureSource;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
backend::Backend,
|
||||
|
@ -26,10 +24,6 @@ pub struct MessageCopyCommand {
|
|||
#[command(flatten)]
|
||||
pub envelopes: EnvelopeIdsArgs,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -42,11 +36,9 @@ impl MessageCopyCommand {
|
|||
let target = &self.target_folder.name;
|
||||
let ids = &self.envelopes.ids;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let copy_messages_kind = toml_account_config.copy_messages_kind();
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::Result;
|
|||
use email::backend::feature::BackendFeatureSource;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
envelope::arg::ids::EnvelopeIdsArgs, folder::arg::name::FolderNameOptionalFlag,
|
||||
|
@ -25,10 +23,6 @@ pub struct MessageDeleteCommand {
|
|||
#[command(flatten)]
|
||||
pub envelopes: EnvelopeIdsArgs,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -40,11 +34,9 @@ impl MessageDeleteCommand {
|
|||
let folder = &self.folder.name;
|
||||
let ids = &self.envelopes.ids;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let delete_messages_kind = toml_account_config.delete_messages_kind();
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::{eyre::eyre, Result};
|
|||
use email::backend::feature::BackendFeatureSource;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
backend::Backend,
|
||||
|
@ -36,10 +34,6 @@ pub struct MessageForwardCommand {
|
|||
#[command(flatten)]
|
||||
pub body: MessageRawBodyArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -50,11 +44,9 @@ impl MessageForwardCommand {
|
|||
|
||||
let folder = &self.folder.name;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let add_message_kind = toml_account_config.add_message_kind();
|
||||
let send_message_kind = toml_account_config.send_message_kind();
|
||||
|
|
|
@ -5,8 +5,6 @@ use mail_builder::MessageBuilder;
|
|||
use tracing::info;
|
||||
use url::Url;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig, printer::Printer,
|
||||
ui::editor,
|
||||
|
@ -24,10 +22,6 @@ pub struct MessageMailtoCommand {
|
|||
#[arg()]
|
||||
pub url: Url,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -36,8 +30,6 @@ impl MessageMailtoCommand {
|
|||
pub fn new(url: &str) -> Result<Self> {
|
||||
Ok(Self {
|
||||
url: Url::parse(url)?,
|
||||
#[cfg(feature = "account-sync")]
|
||||
cache: Default::default(),
|
||||
account: Default::default(),
|
||||
})
|
||||
}
|
||||
|
@ -45,11 +37,9 @@ impl MessageMailtoCommand {
|
|||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing mailto message command");
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let add_message_kind = toml_account_config.add_message_kind();
|
||||
let send_message_kind = toml_account_config.send_message_kind();
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::Result;
|
|||
use email::backend::feature::BackendFeatureSource;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
#[allow(unused)]
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
|
@ -27,10 +25,6 @@ pub struct MessageMoveCommand {
|
|||
#[command(flatten)]
|
||||
pub envelopes: EnvelopeIdsArgs,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -43,11 +37,9 @@ impl MessageMoveCommand {
|
|||
let target = &self.target_folder.name;
|
||||
let ids = &self.envelopes.ids;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let move_messages_kind = toml_account_config.move_messages_kind();
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ use email::backend::feature::BackendFeatureSource;
|
|||
use mml::message::FilterParts;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
#[allow(unused)]
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
|
@ -70,10 +68,6 @@ pub struct MessageReadCommand {
|
|||
#[arg(conflicts_with = "no_headers")]
|
||||
pub headers: Vec<String>,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -85,11 +79,9 @@ impl MessageReadCommand {
|
|||
let folder = &self.folder.name;
|
||||
let ids = &self.envelopes.ids;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let get_messages_kind = toml_account_config.get_messages_kind();
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::{eyre::eyre, Result};
|
|||
use email::backend::feature::BackendFeatureSource;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
backend::Backend,
|
||||
|
@ -39,10 +37,6 @@ pub struct MessageReplyCommand {
|
|||
#[command(flatten)]
|
||||
pub body: MessageRawBodyArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -52,11 +46,9 @@ impl MessageReplyCommand {
|
|||
info!("executing reply message command");
|
||||
|
||||
let folder = &self.folder.name;
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let add_message_kind = toml_account_config.add_message_kind();
|
||||
let send_message_kind = toml_account_config.send_message_kind();
|
||||
|
|
|
@ -4,8 +4,6 @@ use email::backend::feature::BackendFeatureSource;
|
|||
use std::io::{self, BufRead, IsTerminal};
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
#[allow(unused)]
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
|
@ -23,10 +21,6 @@ pub struct MessageSaveCommand {
|
|||
#[command(flatten)]
|
||||
pub message: MessageRawArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -37,11 +31,9 @@ impl MessageSaveCommand {
|
|||
|
||||
let folder = &self.folder.name;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let add_message_kind = toml_account_config.add_message_kind();
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ use email::backend::feature::BackendFeatureSource;
|
|||
use std::io::{self, BufRead, IsTerminal};
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
message::arg::MessageRawArg, printer::Printer,
|
||||
|
@ -20,10 +18,6 @@ pub struct MessageSendCommand {
|
|||
#[command(flatten)]
|
||||
pub message: MessageRawArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -32,11 +26,9 @@ impl MessageSendCommand {
|
|||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing send message command");
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let send_message_kind = toml_account_config.send_message_kind().into_iter().chain(
|
||||
toml_account_config
|
||||
|
|
|
@ -4,8 +4,6 @@ use email::backend::feature::BackendFeatureSource;
|
|||
use mml::message::FilterParts;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::envelope::arg::ids::EnvelopeIdArg;
|
||||
#[allow(unused)]
|
||||
use crate::{
|
||||
|
@ -71,10 +69,6 @@ pub struct MessageThreadCommand {
|
|||
#[arg(conflicts_with = "no_headers")]
|
||||
pub headers: Vec<String>,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -86,11 +80,9 @@ impl MessageThreadCommand {
|
|||
let folder = &self.folder.name;
|
||||
let id = &self.envelope.id;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let get_messages_kind = toml_account_config.get_messages_kind();
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::Result;
|
|||
use email::{backend::feature::BackendFeatureSource, message::Message};
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
backend::Backend,
|
||||
|
@ -28,10 +26,6 @@ pub struct MessageWriteCommand {
|
|||
#[command(flatten)]
|
||||
pub body: MessageRawBodyArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -40,11 +34,9 @@ impl MessageWriteCommand {
|
|||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing write message command");
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let add_message_kind = toml_account_config.add_message_kind();
|
||||
let send_message_kind = toml_account_config.send_message_kind();
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use email::message::delete::config::DeleteMessageStyle;
|
||||
#[cfg(feature = "account-sync")]
|
||||
use email::message::sync::config::MessageSyncConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
|
||||
|
@ -15,8 +13,6 @@ pub struct MessageConfig {
|
|||
pub copy: Option<MessageCopyConfig>,
|
||||
pub r#move: Option<MessageMoveConfig>,
|
||||
pub delete: Option<DeleteMessageConfig>,
|
||||
#[cfg(feature = "account-sync")]
|
||||
pub sync: Option<MessageSyncConfig>,
|
||||
}
|
||||
|
||||
impl MessageConfig {
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::{eyre::eyre, Result};
|
|||
use email::backend::feature::BackendFeatureSource;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
backend::Backend,
|
||||
|
@ -34,10 +32,6 @@ pub struct TemplateForwardCommand {
|
|||
#[command(flatten)]
|
||||
pub body: MessageRawBodyArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -48,11 +42,9 @@ impl TemplateForwardCommand {
|
|||
|
||||
let folder = &self.folder.name;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let get_messages_kind = toml_account_config.get_messages_kind();
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::{eyre::eyre, Result};
|
|||
use email::backend::feature::BackendFeatureSource;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
backend::Backend,
|
||||
|
@ -38,10 +36,6 @@ pub struct TemplateReplyCommand {
|
|||
#[command(flatten)]
|
||||
pub body: MessageRawBodyArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -53,11 +47,9 @@ impl TemplateReplyCommand {
|
|||
let folder = &self.folder.name;
|
||||
let id = self.envelope.id;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let get_messages_kind = toml_account_config.get_messages_kind();
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ use mml::MmlCompilerBuilder;
|
|||
use std::io::{self, BufRead, IsTerminal};
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
email::template::arg::TemplateRawArg, folder::arg::name::FolderNameOptionalFlag,
|
||||
|
@ -27,10 +25,6 @@ pub struct TemplateSaveCommand {
|
|||
#[command(flatten)]
|
||||
pub template: TemplateRawArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -41,11 +35,9 @@ impl TemplateSaveCommand {
|
|||
|
||||
let folder = &self.folder.name;
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let add_message_kind = toml_account_config.add_message_kind();
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ use mml::MmlCompilerBuilder;
|
|||
use std::io::{self, BufRead, IsTerminal};
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
email::template::arg::TemplateRawArg, printer::Printer,
|
||||
|
@ -23,10 +21,6 @@ pub struct TemplateSendCommand {
|
|||
#[command(flatten)]
|
||||
pub template: TemplateRawArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -35,11 +29,9 @@ impl TemplateSendCommand {
|
|||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing send template command");
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let send_message_kind = toml_account_config.send_message_kind().into_iter().chain(
|
||||
toml_account_config
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::Result;
|
|||
use email::message::Message;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, config::TomlConfig,
|
||||
email::template::arg::body::TemplateRawBodyArg, message::arg::header::HeaderRawArgs,
|
||||
|
@ -23,10 +21,6 @@ pub struct TemplateWriteCommand {
|
|||
#[command(flatten)]
|
||||
pub body: TemplateRawBodyArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -35,11 +29,9 @@ impl TemplateWriteCommand {
|
|||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing write template command");
|
||||
|
||||
let (_, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (_, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let tpl = Message::new_tpl_builder(account_config)
|
||||
.with_headers(self.headers.raw)
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::Result;
|
|||
use email::{backend::feature::BackendFeatureSource, folder::add::AddFolder};
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
folder::arg::name::FolderNameArg, printer::Printer,
|
||||
|
@ -19,10 +17,6 @@ pub struct AddFolderCommand {
|
|||
#[command(flatten)]
|
||||
pub folder: FolderNameArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -32,11 +26,9 @@ impl AddFolderCommand {
|
|||
info!("executing create folder command");
|
||||
|
||||
let folder = &self.folder.name;
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let add_folder_kind = toml_account_config.add_folder_kind();
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ use inquire::Confirm;
|
|||
use std::process;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
folder::arg::name::FolderNameArg, printer::Printer,
|
||||
|
@ -21,10 +19,6 @@ pub struct FolderDeleteCommand {
|
|||
#[command(flatten)]
|
||||
pub folder: FolderNameArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -42,11 +36,9 @@ impl FolderDeleteCommand {
|
|||
process::exit(0);
|
||||
};
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let delete_folder_kind = toml_account_config.delete_folder_kind();
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::Result;
|
|||
use email::{backend::feature::BackendFeatureSource, folder::expunge::ExpungeFolder};
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
folder::arg::name::FolderNameArg, printer::Printer,
|
||||
|
@ -20,10 +18,6 @@ pub struct FolderExpungeCommand {
|
|||
#[command(flatten)]
|
||||
pub folder: FolderNameArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -33,11 +27,9 @@ impl FolderExpungeCommand {
|
|||
info!("executing expunge folder command");
|
||||
|
||||
let folder = &self.folder.name;
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let expunge_folder_kind = toml_account_config.expunge_folder_kind();
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ use color_eyre::Result;
|
|||
use email::{backend::feature::BackendFeatureSource, folder::list::ListFolders};
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag,
|
||||
backend::Backend,
|
||||
|
@ -18,10 +16,6 @@ use crate::{
|
|||
/// This command allows you to list all exsting folders.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct FolderListCommand {
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
|
||||
|
@ -38,11 +32,9 @@ impl FolderListCommand {
|
|||
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
|
||||
info!("executing list folders command");
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let list_folders_kind = toml_account_config.list_folders_kind();
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ use email::{backend::feature::BackendFeatureSource, folder::purge::PurgeFolder};
|
|||
use std::process;
|
||||
use tracing::info;
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
use crate::cache::arg::disable::CacheDisableFlag;
|
||||
use crate::{
|
||||
account::arg::name::AccountNameFlag, backend::Backend, config::TomlConfig,
|
||||
folder::arg::name::FolderNameArg, printer::Printer,
|
||||
|
@ -20,10 +18,6 @@ pub struct FolderPurgeCommand {
|
|||
#[command(flatten)]
|
||||
pub folder: FolderNameArg,
|
||||
|
||||
#[cfg(feature = "account-sync")]
|
||||
#[command(flatten)]
|
||||
pub cache: CacheDisableFlag,
|
||||
|
||||
#[command(flatten)]
|
||||
pub account: AccountNameFlag,
|
||||
}
|
||||
|
@ -42,11 +36,9 @@ impl FolderPurgeCommand {
|
|||
process::exit(0);
|
||||
};
|
||||
|
||||
let (toml_account_config, account_config) = config.clone().into_account_configs(
|
||||
self.account.name.as_deref(),
|
||||
#[cfg(feature = "account-sync")]
|
||||
self.cache.disable,
|
||||
)?;
|
||||
let (toml_account_config, account_config) = config
|
||||
.clone()
|
||||
.into_account_configs(self.account.name.as_deref())?;
|
||||
|
||||
let purge_folder_kind = toml_account_config.purge_folder_kind();
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#[cfg(feature = "account-sync")]
|
||||
use email::folder::sync::config::FolderSyncConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
|
@ -14,8 +12,6 @@ pub struct FolderConfig {
|
|||
pub expunge: Option<FolderExpungeConfig>,
|
||||
pub purge: Option<FolderPurgeConfig>,
|
||||
pub delete: Option<FolderDeleteConfig>,
|
||||
#[cfg(feature = "account-sync")]
|
||||
pub sync: Option<FolderSyncConfig>,
|
||||
}
|
||||
|
||||
impl FolderConfig {
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
#[cfg(feature = "wizard")]
|
||||
pub(crate) mod wizard;
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
use color_eyre::Result;
|
||||
#[cfg(feature = "account-discovery")]
|
||||
use email::account::discover::config::{AuthenticationType, AutoConfig, SecurityType, ServerType};
|
||||
use email::autoconfig::config::{AutoConfig, SecurityType, ServerType};
|
||||
#[cfg(feature = "oauth2")]
|
||||
use email::{
|
||||
account::config::{
|
||||
oauth2::{OAuth2Config, OAuth2Method, OAuth2Scopes},
|
||||
passwd::PasswdConfig,
|
||||
},
|
||||
account::config::oauth2::{OAuth2Config, OAuth2Method, OAuth2Scopes},
|
||||
autoconfig::config::AuthenticationType,
|
||||
};
|
||||
use email::{
|
||||
account::config::passwd::PasswdConfig,
|
||||
imap::config::{ImapAuthConfig, ImapConfig, ImapEncryptionKind},
|
||||
};
|
||||
use inquire::validator::{ErrorMessage, StringValidator, Validation};
|
||||
#[cfg(feature = "oauth2")]
|
||||
use oauth::v2_0::{AuthorizationCodeGrant, Client};
|
||||
use secret::Secret;
|
||||
|
||||
use crate::{backend::config::BackendConfig, ui::prompt, wizard_log};
|
||||
use crate::{backend::config::BackendConfig, ui::prompt};
|
||||
|
||||
const ENCRYPTIONS: &[ImapEncryptionKind] = &[
|
||||
ImapEncryptionKind::Tls,
|
||||
|
@ -20,11 +22,13 @@ const ENCRYPTIONS: &[ImapEncryptionKind] = &[
|
|||
ImapEncryptionKind::None,
|
||||
];
|
||||
|
||||
const XOAUTH2: &str = "XOAUTH2";
|
||||
const OAUTHBEARER: &str = "OAUTHBEARER";
|
||||
const OAUTH2_MECHANISMS: &[&str] = &[XOAUTH2, OAUTHBEARER];
|
||||
|
||||
const SECRETS: &[&str] = &[KEYRING, RAW, CMD];
|
||||
const SECRETS: &[&str] = &[
|
||||
#[cfg(feature = "keyring")]
|
||||
KEYRING,
|
||||
RAW,
|
||||
CMD,
|
||||
];
|
||||
#[cfg(feature = "keyring")]
|
||||
const KEYRING: &str = "Ask my password, then save it in my system's global keyring";
|
||||
const RAW: &str = "Ask my password, then save it in the configuration file (not safe)";
|
||||
const CMD: &str = "Ask me a shell command that exposes my password";
|
||||
|
@ -49,16 +53,14 @@ impl StringValidator for U16Validator {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "account-discovery")]
|
||||
pub(crate) async fn configure(
|
||||
account_name: &str,
|
||||
email: &str,
|
||||
autoconfig: Option<&AutoConfig>,
|
||||
) -> Result<BackendConfig> {
|
||||
use color_eyre::eyre::OptionExt as _;
|
||||
use inquire::{validator::MinLengthValidator, Confirm, Password, Select, Text};
|
||||
use inquire::{validator, Select, Text};
|
||||
|
||||
let autoconfig_oauth2 = autoconfig.and_then(|c| c.oauth2());
|
||||
let autoconfig_server = autoconfig.and_then(|c| {
|
||||
c.email_provider()
|
||||
.incoming_servers()
|
||||
|
@ -93,7 +95,7 @@ pub(crate) async fn configure(
|
|||
ImapEncryptionKind::None => 2,
|
||||
};
|
||||
|
||||
let encryption_idx = Select::new("IMAP encryption", ENCRYPTIONS.to_vec())
|
||||
let encryption_kind = Select::new("IMAP encryption", ENCRYPTIONS.to_vec())
|
||||
.with_starting_cursor(default_encryption_idx)
|
||||
.prompt_skippable()?;
|
||||
|
||||
|
@ -101,28 +103,28 @@ pub(crate) async fn configure(
|
|||
.and_then(|s| s.port())
|
||||
.map(ToOwned::to_owned)
|
||||
.unwrap_or_else(|| match &autoconfig_encryption {
|
||||
ImapEncryptionKind::Tls => 993,
|
||||
ImapEncryptionKind::StartTls => 143,
|
||||
ImapEncryptionKind::None => 143,
|
||||
ImapEncryptionKind::Tls => 465,
|
||||
ImapEncryptionKind::StartTls => 587,
|
||||
ImapEncryptionKind::None => 25,
|
||||
});
|
||||
|
||||
let (encryption, default_port) = match encryption_idx {
|
||||
Some(enc_kind)
|
||||
if &enc_kind
|
||||
let (encryption, default_port) = match encryption_kind {
|
||||
Some(idx)
|
||||
if &idx
|
||||
== ENCRYPTIONS.get(default_encryption_idx).ok_or_eyre(
|
||||
"something impossible happened while selecting the encryption of imap.",
|
||||
"something impossible happened during finding default match for encryption.",
|
||||
)? =>
|
||||
{
|
||||
(Some(autoconfig_encryption), autoconfig_port)
|
||||
}
|
||||
Some(ImapEncryptionKind::Tls) => (Some(ImapEncryptionKind::Tls), 993),
|
||||
Some(ImapEncryptionKind::StartTls) => (Some(ImapEncryptionKind::StartTls), 143),
|
||||
_ => (Some(ImapEncryptionKind::None), 143),
|
||||
Some(ImapEncryptionKind::Tls) => (Some(ImapEncryptionKind::Tls), 465),
|
||||
Some(ImapEncryptionKind::StartTls) => (Some(ImapEncryptionKind::StartTls), 587),
|
||||
_ => (Some(ImapEncryptionKind::None), 25),
|
||||
};
|
||||
|
||||
let port = Text::new("IMAP port")
|
||||
.with_validators(&[
|
||||
Box::new(MinLengthValidator::new(1)),
|
||||
Box::new(validator::MinLengthValidator::new(1)),
|
||||
Box::new(U16Validator {}),
|
||||
])
|
||||
.with_default(&default_port.to_string())
|
||||
|
@ -141,6 +143,16 @@ pub(crate) async fn configure(
|
|||
.with_default(&default_login)
|
||||
.prompt()?;
|
||||
|
||||
#[cfg(feature = "oauth2")]
|
||||
let auth = {
|
||||
use inquire::{Confirm, Password};
|
||||
|
||||
const XOAUTH2: &str = "XOAUTH2";
|
||||
const OAUTHBEARER: &str = "OAUTHBEARER";
|
||||
const OAUTH2_MECHANISMS: &[&str] = &[XOAUTH2, OAUTHBEARER];
|
||||
|
||||
let autoconfig_oauth2 = autoconfig.and_then(|c| c.oauth2());
|
||||
|
||||
let default_oauth2_enabled = autoconfig_server
|
||||
.and_then(|imap| {
|
||||
imap.authentication_type()
|
||||
|
@ -155,9 +167,9 @@ pub(crate) async fn configure(
|
|||
.prompt_skippable()?
|
||||
.unwrap_or_default();
|
||||
|
||||
let auth = if oauth2_enabled {
|
||||
if oauth2_enabled {
|
||||
let mut config = OAuth2Config::default();
|
||||
let redirect_host = OAuth2Config::LOCALHOST.to_owned();
|
||||
let redirect_host = OAuth2Config::LOCALHOST;
|
||||
let redirect_port = OAuth2Config::get_first_available_port()?;
|
||||
|
||||
let method_idx = Select::new("IMAP OAuth 2.0 mechanism", OAUTH2_MECHANISMS.to_vec())
|
||||
|
@ -165,14 +177,16 @@ pub(crate) async fn configure(
|
|||
.prompt_skippable()?;
|
||||
|
||||
config.method = match method_idx {
|
||||
Some(XOAUTH2) => OAuth2Method::XOAuth2,
|
||||
Some(OAUTHBEARER) => OAuth2Method::OAuthBearer,
|
||||
Some(choice) if choice == XOAUTH2 => OAuth2Method::XOAuth2,
|
||||
Some(choice) if choice == OAUTHBEARER => OAuth2Method::OAuthBearer,
|
||||
_ => OAuth2Method::XOAuth2,
|
||||
};
|
||||
|
||||
config.client_id = Text::new("IMAP OAuth 2.0 client id").prompt()?;
|
||||
|
||||
let client_secret: String = Password::new("IMAP OAuth 2.0 client secret").prompt()?;
|
||||
let client_secret: String = Password::new("IMAP OAuth 2.0 client secret")
|
||||
.with_display_mode(inquire::PasswordDisplayMode::Masked)
|
||||
.prompt()?;
|
||||
config.client_secret =
|
||||
Secret::try_new_keyring_entry(format!("{account_name}-imap-oauth2-client-secret"))?;
|
||||
config
|
||||
|
@ -202,9 +216,7 @@ pub(crate) async fn configure(
|
|||
.with_starting_cursor(0)
|
||||
.prompt_skippable()?
|
||||
.map(ToOwned::to_owned),
|
||||
None => {
|
||||
Some(Text::new(prompt).prompt()?.to_owned()).filter(|scope| !scope.is_empty())
|
||||
}
|
||||
None => Some(Text::new(prompt).prompt()?).filter(|scope| !scope.is_empty()),
|
||||
})
|
||||
};
|
||||
|
||||
|
@ -239,7 +251,7 @@ pub(crate) async fn configure(
|
|||
.prompt_skippable()?
|
||||
.unwrap_or(true);
|
||||
|
||||
wizard_log!("To complete your OAuth 2.0 setup, click on the following link:");
|
||||
crate::wizard_log!("To complete your OAuth 2.0 setup, click on the following link:");
|
||||
|
||||
let client = Client::new(
|
||||
config.client_id.clone(),
|
||||
|
@ -277,36 +289,20 @@ pub(crate) async fn configure(
|
|||
config.access_token.set_only_keyring(access_token).await?;
|
||||
|
||||
if let Some(refresh_token) = &refresh_token {
|
||||
config.refresh_token =
|
||||
Secret::try_new_keyring_entry(format!("{account_name}-imap-oauth2-refresh-token"))?;
|
||||
config.refresh_token = Secret::try_new_keyring_entry(format!(
|
||||
"{account_name}-imap-oauth2-refresh-token"
|
||||
))?;
|
||||
config.refresh_token.set_only_keyring(refresh_token).await?;
|
||||
}
|
||||
|
||||
ImapAuthConfig::OAuth2(config)
|
||||
} else {
|
||||
let secret_idx = Select::new("IMAP authentication strategy", SECRETS.to_vec())
|
||||
.with_starting_cursor(0)
|
||||
.prompt_skippable()?;
|
||||
|
||||
let secret = match secret_idx {
|
||||
Some(KEYRING) => {
|
||||
let secret = Secret::try_new_keyring_entry(format!("{account_name}-imap-passwd"))?;
|
||||
secret
|
||||
.set_only_keyring(prompt::passwd("IMAP password")?)
|
||||
.await?;
|
||||
secret
|
||||
configure_passwd(account_name).await?
|
||||
}
|
||||
Some(RAW) => Secret::new_raw(prompt::passwd("IMAP password")?),
|
||||
Some(CMD) => Secret::new_command(
|
||||
Text::new("Shell command")
|
||||
.with_default(&format!("pass show {account_name}-imap-passwd"))
|
||||
.prompt()?,
|
||||
),
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
ImapAuthConfig::Passwd(PasswdConfig(secret))
|
||||
};
|
||||
#[cfg(not(feature = "oauth2"))]
|
||||
let auth = configure_passwd(account_name).await?;
|
||||
|
||||
let config = ImapConfig {
|
||||
host,
|
||||
|
@ -320,173 +316,24 @@ pub(crate) async fn configure(
|
|||
Ok(BackendConfig::Imap(config))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "account-discovery"))]
|
||||
pub(crate) async fn configure(account_name: &str, email: &str) -> Result<BackendConfig> {
|
||||
use inquire::{
|
||||
validator::MinLengthValidator, Confirm, Password, PasswordDisplayMode, Select, Text,
|
||||
};
|
||||
pub(crate) async fn configure_passwd(account_name: &str) -> Result<ImapAuthConfig> {
|
||||
use inquire::{Select, Text};
|
||||
|
||||
let default_host = format!("imap.{}", email.rsplit_once('@').unwrap().1);
|
||||
|
||||
let host = Text::new("IMAP hostname")
|
||||
.with_default(&default_host)
|
||||
.prompt()?;
|
||||
|
||||
let encryption_idx = Select::new("IMAP encryption", ENCRYPTIONS.to_vec())
|
||||
.with_starting_cursor(0)
|
||||
.prompt_skippable()?;
|
||||
|
||||
let (encryption, default_port) = match encryption_idx {
|
||||
Some(ImapEncryptionKind::Tls) => (Some(ImapEncryptionKind::Tls), 993),
|
||||
Some(ImapEncryptionKind::StartTls) => (Some(ImapEncryptionKind::StartTls), 143),
|
||||
_ => (Some(ImapEncryptionKind::None), 143),
|
||||
};
|
||||
|
||||
let port = Text::new("IMAP port")
|
||||
.with_validators(&[
|
||||
Box::new(MinLengthValidator::new(1)),
|
||||
Box::new(U16Validator {}),
|
||||
])
|
||||
.with_default(&default_port.to_string())
|
||||
.prompt()
|
||||
.map(|input| input.parse::<u16>().unwrap())?;
|
||||
|
||||
let default_login = email.to_owned();
|
||||
|
||||
let login = Text::new("IMAP login")
|
||||
.with_default(&default_login)
|
||||
.prompt()?;
|
||||
|
||||
let oauth2_enabled = Confirm::new("Would you like to enable OAuth 2.0?")
|
||||
.with_default(false)
|
||||
.prompt_skippable()?
|
||||
.unwrap_or_default();
|
||||
|
||||
let auth = if oauth2_enabled {
|
||||
let mut config = OAuth2Config::default();
|
||||
let redirect_host = OAuth2Config::LOCALHOST.to_owned();
|
||||
let redirect_port = OAuth2Config::get_first_available_port()?;
|
||||
|
||||
let method_idx = Select::new("IMAP OAuth 2.0 mechanism", OAUTH2_MECHANISMS.to_vec())
|
||||
.with_starting_cursor(0)
|
||||
.prompt_skippable()?;
|
||||
|
||||
config.method = match method_idx {
|
||||
Some(XOAUTH2) => OAuth2Method::XOAuth2,
|
||||
Some(OAUTHBEARER) => OAuth2Method::OAuthBearer,
|
||||
_ => OAuth2Method::XOAuth2,
|
||||
};
|
||||
|
||||
config.client_id = Text::new("IMAP OAuth 2.0 client id").prompt()?;
|
||||
|
||||
let client_secret: String = Password::new("IMAP OAuth 2.0 client secret")
|
||||
.with_display_mode(PasswordDisplayMode::Masked)
|
||||
.prompt()?;
|
||||
config.client_secret =
|
||||
Secret::try_new_keyring_entry(format!("{account_name}-imap-oauth2-client-secret"))?;
|
||||
config
|
||||
.client_secret
|
||||
.set_only_keyring(&client_secret)
|
||||
.await?;
|
||||
|
||||
config.auth_url = Text::new("IMAP OAuth 2.0 authorization URL").prompt()?;
|
||||
|
||||
config.token_url = Text::new("IMAP OAuth 2.0 token URL").prompt()?;
|
||||
|
||||
let prompt_scope = |prompt: &str| -> Result<Option<String>> {
|
||||
Ok(Some(Text::new(prompt).prompt()?.to_owned()).filter(|scope| !scope.is_empty()))
|
||||
};
|
||||
|
||||
if let Some(scope) = prompt_scope("IMAP OAuth 2.0 main scope")? {
|
||||
config.scopes = OAuth2Scopes::Scope(scope);
|
||||
}
|
||||
|
||||
let confirm_additional_scope = || -> Result<bool> {
|
||||
let confirm = Confirm::new("Would you like to add more IMAP OAuth 2.0 scopes?")
|
||||
.with_default(false)
|
||||
.prompt_skippable()?
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(confirm)
|
||||
};
|
||||
|
||||
while confirm_additional_scope()? {
|
||||
let mut scopes = match config.scopes {
|
||||
OAuth2Scopes::Scope(scope) => vec![scope],
|
||||
OAuth2Scopes::Scopes(scopes) => scopes,
|
||||
};
|
||||
|
||||
if let Some(scope) = prompt_scope("Additional IMAP OAuth 2.0 scope")? {
|
||||
scopes.push(scope)
|
||||
}
|
||||
|
||||
config.scopes = OAuth2Scopes::Scopes(scopes);
|
||||
}
|
||||
|
||||
config.pkce = Confirm::new("Would you like to enable PKCE verification?")
|
||||
.with_default(true)
|
||||
.prompt_skippable()?
|
||||
.unwrap_or(true);
|
||||
|
||||
wizard_log!("To complete your OAuth 2.0 setup, click on the following link:");
|
||||
|
||||
let client = Client::new(
|
||||
config.client_id.clone(),
|
||||
client_secret,
|
||||
config.auth_url.clone(),
|
||||
config.token_url.clone(),
|
||||
)?
|
||||
.with_redirect_host(redirect_host.to_owned())
|
||||
.with_redirect_port(redirect_port)
|
||||
.build()?;
|
||||
|
||||
let mut auth_code_grant = AuthorizationCodeGrant::new()
|
||||
.with_redirect_host(redirect_host.to_owned())
|
||||
.with_redirect_port(redirect_port);
|
||||
|
||||
if config.pkce {
|
||||
auth_code_grant = auth_code_grant.with_pkce();
|
||||
}
|
||||
|
||||
for scope in config.scopes.clone() {
|
||||
auth_code_grant = auth_code_grant.with_scope(scope);
|
||||
}
|
||||
|
||||
let (redirect_url, csrf_token) = auth_code_grant.get_redirect_url(&client);
|
||||
|
||||
println!("{redirect_url}");
|
||||
println!();
|
||||
|
||||
let (access_token, refresh_token) = auth_code_grant
|
||||
.wait_for_redirection(&client, csrf_token)
|
||||
.await?;
|
||||
|
||||
config.access_token =
|
||||
Secret::try_new_keyring_entry(format!("{account_name}-imap-oauth2-access-token"))?;
|
||||
config.access_token.set_only_keyring(access_token).await?;
|
||||
|
||||
if let Some(refresh_token) = &refresh_token {
|
||||
config.refresh_token =
|
||||
Secret::try_new_keyring_entry(format!("{account_name}-imap-oauth2-refresh-token"))?;
|
||||
config.refresh_token.set_only_keyring(refresh_token).await?;
|
||||
}
|
||||
|
||||
ImapAuthConfig::OAuth2(config)
|
||||
} else {
|
||||
let secret_idx = Select::new("IMAP authentication strategy", SECRETS.to_vec())
|
||||
.with_starting_cursor(0)
|
||||
.prompt_skippable()?;
|
||||
|
||||
let secret = match secret_idx {
|
||||
Some(KEYRING) => {
|
||||
#[cfg(feature = "keyring")]
|
||||
Some(sec) if sec == KEYRING => {
|
||||
let secret = Secret::try_new_keyring_entry(format!("{account_name}-imap-passwd"))?;
|
||||
secret
|
||||
.set_only_keyring(prompt::passwd("IMAP password")?)
|
||||
.await?;
|
||||
secret
|
||||
}
|
||||
Some(RAW) => Secret::new_raw(prompt::passwd("IMAP password")?),
|
||||
Some(CMD) => Secret::new_command(
|
||||
Some(sec) if sec == RAW => Secret::new_raw(prompt::passwd("IMAP password")?),
|
||||
Some(sec) if sec == CMD => Secret::new_command(
|
||||
Text::new("Shell command")
|
||||
.with_default(&format!("pass show {account_name}-imap-passwd"))
|
||||
.prompt()?,
|
||||
|
@ -494,17 +341,5 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
|
|||
_ => Default::default(),
|
||||
};
|
||||
|
||||
ImapAuthConfig::Passwd(PasswdConfig(secret))
|
||||
};
|
||||
|
||||
let config = ImapConfig {
|
||||
host,
|
||||
port,
|
||||
encryption,
|
||||
login,
|
||||
auth,
|
||||
watch: None,
|
||||
};
|
||||
|
||||
Ok(BackendConfig::Imap(config))
|
||||
Ok(ImapAuthConfig::Passwd(PasswdConfig(secret)))
|
||||
}
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
#[cfg(feature = "wizard")]
|
||||
pub(crate) mod wizard;
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
#[cfg(feature = "wizard")]
|
||||
pub(crate) mod wizard;
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
use color_eyre::Result;
|
||||
#[cfg(feature = "account-discovery")]
|
||||
use email::account::discover::config::{AuthenticationType, AutoConfig, SecurityType, ServerType};
|
||||
use email::autoconfig::config::{AutoConfig, SecurityType, ServerType};
|
||||
#[cfg(feature = "oauth2")]
|
||||
use email::{
|
||||
account::config::{
|
||||
oauth2::{OAuth2Config, OAuth2Method, OAuth2Scopes},
|
||||
passwd::PasswdConfig,
|
||||
},
|
||||
account::config::oauth2::{OAuth2Config, OAuth2Method, OAuth2Scopes},
|
||||
autoconfig::config::AuthenticationType,
|
||||
};
|
||||
use email::{
|
||||
account::config::passwd::PasswdConfig,
|
||||
smtp::config::{SmtpAuthConfig, SmtpConfig, SmtpEncryptionKind},
|
||||
};
|
||||
use inquire::validator::{ErrorMessage, StringValidator, Validation};
|
||||
#[cfg(feature = "oauth2")]
|
||||
use oauth::v2_0::{AuthorizationCodeGrant, Client};
|
||||
use secret::Secret;
|
||||
|
||||
use crate::{backend::config::BackendConfig, ui::prompt, wizard_log};
|
||||
use crate::{backend::config::BackendConfig, ui::prompt};
|
||||
|
||||
const ENCRYPTIONS: &[SmtpEncryptionKind] = &[
|
||||
SmtpEncryptionKind::Tls,
|
||||
|
@ -20,11 +22,13 @@ const ENCRYPTIONS: &[SmtpEncryptionKind] = &[
|
|||
SmtpEncryptionKind::None,
|
||||
];
|
||||
|
||||
const XOAUTH2: &str = "XOAUTH2";
|
||||
const OAUTHBEARER: &str = "OAUTHBEARER";
|
||||
const OAUTH2_MECHANISMS: &[&str] = &[XOAUTH2, OAUTHBEARER];
|
||||
|
||||
const SECRETS: &[&str] = &[KEYRING, RAW, CMD];
|
||||
const SECRETS: &[&str] = &[
|
||||
#[cfg(feature = "keyring")]
|
||||
KEYRING,
|
||||
RAW,
|
||||
CMD,
|
||||
];
|
||||
#[cfg(feature = "keyring")]
|
||||
const KEYRING: &str = "Ask my password, then save it in my system's global keyring";
|
||||
const RAW: &str = "Ask my password, then save it in the configuration file (not safe)";
|
||||
const CMD: &str = "Ask me a shell command that exposes my password";
|
||||
|
@ -49,16 +53,14 @@ impl StringValidator for U16Validator {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "account-discovery")]
|
||||
pub(crate) async fn configure(
|
||||
account_name: &str,
|
||||
email: &str,
|
||||
autoconfig: Option<&AutoConfig>,
|
||||
) -> Result<BackendConfig> {
|
||||
use color_eyre::eyre::OptionExt as _;
|
||||
use inquire::{validator, Confirm, Password, Select, Text};
|
||||
use inquire::{validator, Select, Text};
|
||||
|
||||
let autoconfig_oauth2 = autoconfig.and_then(|c| c.oauth2());
|
||||
let autoconfig_server = autoconfig.and_then(|c| {
|
||||
c.email_provider()
|
||||
.outgoing_servers()
|
||||
|
@ -141,6 +143,16 @@ pub(crate) async fn configure(
|
|||
.with_default(&default_login)
|
||||
.prompt()?;
|
||||
|
||||
#[cfg(feature = "oauth2")]
|
||||
let auth = {
|
||||
use inquire::{Confirm, Password};
|
||||
|
||||
const XOAUTH2: &str = "XOAUTH2";
|
||||
const OAUTHBEARER: &str = "OAUTHBEARER";
|
||||
const OAUTH2_MECHANISMS: &[&str] = &[XOAUTH2, OAUTHBEARER];
|
||||
|
||||
let autoconfig_oauth2 = autoconfig.and_then(|c| c.oauth2());
|
||||
|
||||
let default_oauth2_enabled = autoconfig_server
|
||||
.and_then(|smtp| {
|
||||
smtp.authentication_type()
|
||||
|
@ -155,7 +167,7 @@ pub(crate) async fn configure(
|
|||
.prompt_skippable()?
|
||||
.unwrap_or_default();
|
||||
|
||||
let auth = if oauth2_enabled {
|
||||
if oauth2_enabled {
|
||||
let mut config = OAuth2Config::default();
|
||||
let redirect_host = OAuth2Config::LOCALHOST;
|
||||
let redirect_port = OAuth2Config::get_first_available_port()?;
|
||||
|
@ -239,7 +251,7 @@ pub(crate) async fn configure(
|
|||
.prompt_skippable()?
|
||||
.unwrap_or(true);
|
||||
|
||||
wizard_log!("To complete your OAuth 2.0 setup, click on the following link:");
|
||||
crate::wizard_log!("To complete your OAuth 2.0 setup, click on the following link:");
|
||||
|
||||
let client = Client::new(
|
||||
config.client_id.clone(),
|
||||
|
@ -277,18 +289,41 @@ pub(crate) async fn configure(
|
|||
config.access_token.set_only_keyring(access_token).await?;
|
||||
|
||||
if let Some(refresh_token) = &refresh_token {
|
||||
config.refresh_token =
|
||||
Secret::try_new_keyring_entry(format!("{account_name}-smtp-oauth2-refresh-token"))?;
|
||||
config.refresh_token = Secret::try_new_keyring_entry(format!(
|
||||
"{account_name}-smtp-oauth2-refresh-token"
|
||||
))?;
|
||||
config.refresh_token.set_only_keyring(refresh_token).await?;
|
||||
}
|
||||
|
||||
SmtpAuthConfig::OAuth2(config)
|
||||
} else {
|
||||
configure_passwd(account_name).await?
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "oauth2"))]
|
||||
let auth = configure_passwd(account_name).await?;
|
||||
|
||||
let config = SmtpConfig {
|
||||
host,
|
||||
port,
|
||||
encryption,
|
||||
login,
|
||||
auth,
|
||||
};
|
||||
|
||||
Ok(BackendConfig::Smtp(config))
|
||||
}
|
||||
|
||||
pub(crate) async fn configure_passwd(account_name: &str) -> Result<SmtpAuthConfig> {
|
||||
use inquire::{Select, Text};
|
||||
|
||||
let secret_idx = Select::new("SMTP authentication strategy", SECRETS.to_vec())
|
||||
.with_starting_cursor(0)
|
||||
.prompt_skippable()?;
|
||||
|
||||
let secret = match secret_idx {
|
||||
#[cfg(feature = "keyring")]
|
||||
Some(sec) if sec == KEYRING => {
|
||||
let secret = Secret::try_new_keyring_entry(format!("{account_name}-smtp-passwd"))?;
|
||||
secret
|
||||
|
@ -305,202 +340,5 @@ pub(crate) async fn configure(
|
|||
_ => Default::default(),
|
||||
};
|
||||
|
||||
SmtpAuthConfig::Passwd(PasswdConfig(secret))
|
||||
};
|
||||
|
||||
let config = SmtpConfig {
|
||||
host,
|
||||
port,
|
||||
encryption,
|
||||
login,
|
||||
auth,
|
||||
};
|
||||
|
||||
Ok(BackendConfig::Smtp(config))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "account-discovery"))]
|
||||
pub(crate) async fn configure(account_name: &str, email: &str) -> Result<BackendConfig> {
|
||||
use inquire::{validator::MinLengthValidator, Confirm, Password, Select, Text};
|
||||
|
||||
let default_host = format!("smtp.{}", email.rsplit_once('@').unwrap().1);
|
||||
|
||||
let host = Text::new("SMTP hostname")
|
||||
.with_default(&default_host)
|
||||
.prompt()?;
|
||||
|
||||
let encryption_idx = Select::new("SMTP encryption", ENCRYPTIONS.to_vec())
|
||||
.with_starting_cursor(0)
|
||||
.prompt_skippable()?;
|
||||
|
||||
let (encryption, default_port) = match encryption_idx {
|
||||
Some(SmtpEncryptionKind::Tls) => (Some(SmtpEncryptionKind::Tls), 465),
|
||||
Some(SmtpEncryptionKind::StartTls) => (Some(SmtpEncryptionKind::StartTls), 587),
|
||||
_ => (Some(SmtpEncryptionKind::None), 25),
|
||||
};
|
||||
|
||||
let port = Text::new("SMTP port")
|
||||
.with_validators(&[
|
||||
Box::new(MinLengthValidator::new(1)),
|
||||
Box::new(U16Validator {}),
|
||||
])
|
||||
.with_default(&default_port.to_string())
|
||||
.prompt()
|
||||
.map(|input| input.parse::<u16>().unwrap())?;
|
||||
|
||||
let default_login = email.to_owned();
|
||||
|
||||
let login = Text::new("SMTP login")
|
||||
.with_default(&default_login)
|
||||
.prompt()?;
|
||||
|
||||
let oauth2_enabled = Confirm::new("Would you like to enable OAuth 2.0?")
|
||||
.with_default(false)
|
||||
.prompt_skippable()?
|
||||
.unwrap_or_default();
|
||||
|
||||
let auth = if oauth2_enabled {
|
||||
let mut config = OAuth2Config::default();
|
||||
let redirect_host = OAuth2Config::LOCALHOST.to_owned();
|
||||
let redirect_port = OAuth2Config::get_first_available_port()?;
|
||||
|
||||
let method_idx = Select::new("SMTP OAuth 2.0 mechanism", OAUTH2_MECHANISMS.to_vec())
|
||||
.with_starting_cursor(0)
|
||||
.prompt_skippable()?;
|
||||
|
||||
config.method = match method_idx {
|
||||
Some(XOAUTH2) => OAuth2Method::XOAuth2,
|
||||
Some(OAUTHBEARER) => OAuth2Method::OAuthBearer,
|
||||
_ => OAuth2Method::XOAuth2,
|
||||
};
|
||||
|
||||
config.client_id = Text::new("SMTP OAuth 2.0 client id").prompt()?;
|
||||
|
||||
let client_secret: String = Password::new("SMTP OAuth 2.0 client secret")
|
||||
.with_display_mode(inquire::PasswordDisplayMode::Masked)
|
||||
.prompt()?;
|
||||
config.client_secret =
|
||||
Secret::try_new_keyring_entry(format!("{account_name}-smtp-oauth2-client-secret"))?;
|
||||
config
|
||||
.client_secret
|
||||
.set_only_keyring(&client_secret)
|
||||
.await?;
|
||||
|
||||
config.auth_url = Text::new("SMTP OAuth 2.0 authorization URL").prompt()?;
|
||||
|
||||
config.token_url = Text::new("SMTP OAuth 2.0 token URL").prompt()?;
|
||||
|
||||
let prompt_scope = |prompt: &str| -> Result<Option<String>> {
|
||||
Ok(Some(Text::new(prompt).prompt()?.to_owned()).filter(|scope| !scope.is_empty()))
|
||||
};
|
||||
|
||||
if let Some(scope) = prompt_scope("SMTP OAuth 2.0 main scope")? {
|
||||
config.scopes = OAuth2Scopes::Scope(scope);
|
||||
}
|
||||
|
||||
let confirm_additional_scope = || -> Result<bool> {
|
||||
let confirm = Confirm::new("Would you like to add more SMTP OAuth 2.0 scopes?")
|
||||
.with_default(false)
|
||||
.prompt_skippable()?
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(confirm)
|
||||
};
|
||||
|
||||
while confirm_additional_scope()? {
|
||||
let mut scopes = match config.scopes {
|
||||
OAuth2Scopes::Scope(scope) => vec![scope],
|
||||
OAuth2Scopes::Scopes(scopes) => scopes,
|
||||
};
|
||||
|
||||
if let Some(scope) = prompt_scope("Additional SMTP OAuth 2.0 scope")? {
|
||||
scopes.push(scope)
|
||||
}
|
||||
|
||||
config.scopes = OAuth2Scopes::Scopes(scopes);
|
||||
}
|
||||
|
||||
config.pkce = Confirm::new("Would you like to enable PKCE verification?")
|
||||
.with_default(true)
|
||||
.prompt_skippable()?
|
||||
.unwrap_or(true);
|
||||
|
||||
wizard_log!("To complete your OAuth 2.0 setup, click on the following link:");
|
||||
|
||||
let client = Client::new(
|
||||
config.client_id.clone(),
|
||||
client_secret,
|
||||
config.auth_url.clone(),
|
||||
config.token_url.clone(),
|
||||
)?
|
||||
.with_redirect_host(redirect_host.to_owned())
|
||||
.with_redirect_port(redirect_port)
|
||||
.build()?;
|
||||
|
||||
let mut auth_code_grant = AuthorizationCodeGrant::new()
|
||||
.with_redirect_host(redirect_host.to_owned())
|
||||
.with_redirect_port(redirect_port);
|
||||
|
||||
if config.pkce {
|
||||
auth_code_grant = auth_code_grant.with_pkce();
|
||||
}
|
||||
|
||||
for scope in config.scopes.clone() {
|
||||
auth_code_grant = auth_code_grant.with_scope(scope);
|
||||
}
|
||||
|
||||
let (redirect_url, csrf_token) = auth_code_grant.get_redirect_url(&client);
|
||||
|
||||
println!("{redirect_url}");
|
||||
println!();
|
||||
|
||||
let (access_token, refresh_token) = auth_code_grant
|
||||
.wait_for_redirection(&client, csrf_token)
|
||||
.await?;
|
||||
|
||||
config.access_token =
|
||||
Secret::try_new_keyring_entry(format!("{account_name}-smtp-oauth2-access-token"))?;
|
||||
config.access_token.set_only_keyring(access_token).await?;
|
||||
|
||||
if let Some(refresh_token) = &refresh_token {
|
||||
config.refresh_token =
|
||||
Secret::try_new_keyring_entry(format!("{account_name}-smtp-oauth2-refresh-token"))?;
|
||||
config.refresh_token.set_only_keyring(refresh_token).await?;
|
||||
}
|
||||
|
||||
SmtpAuthConfig::OAuth2(config)
|
||||
} else {
|
||||
let secret_idx = Select::new("SMTP authentication strategy", SECRETS.to_vec())
|
||||
.with_starting_cursor(0)
|
||||
.prompt_skippable()?;
|
||||
|
||||
let secret = match secret_idx {
|
||||
Some(KEYRING) => {
|
||||
let secret = Secret::try_new_keyring_entry(format!("{account_name}-smtp-passwd"))?;
|
||||
secret
|
||||
.set_only_keyring(prompt::passwd("SMTP password")?)
|
||||
.await?;
|
||||
secret
|
||||
}
|
||||
Some(RAW) => Secret::new_raw(prompt::passwd("SMTP password")?),
|
||||
Some(CMD) => Secret::new_command(
|
||||
Text::new("Shell command")
|
||||
.with_default(&format!("pass show {account_name}-smtp-passwd"))
|
||||
.prompt()?,
|
||||
),
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
SmtpAuthConfig::Passwd(PasswdConfig(secret))
|
||||
};
|
||||
|
||||
let config = SmtpConfig {
|
||||
host,
|
||||
port,
|
||||
encryption,
|
||||
login,
|
||||
auth,
|
||||
};
|
||||
|
||||
Ok(BackendConfig::Smtp(config))
|
||||
Ok(SmtpAuthConfig::Passwd(PasswdConfig(secret)))
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ pub(crate) fn passwd(prompt: &str) -> io::Result<String> {
|
|||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "oauth2")]
|
||||
pub(crate) fn secret(prompt: &str) -> io::Result<String> {
|
||||
inquire::Password::new(prompt)
|
||||
.with_display_mode(inquire::PasswordDisplayMode::Masked)
|
||||
|
|
Loading…
Reference in a new issue