mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-18 05:47:54 +08:00
refactor: use imap and smtp streams from toolbox
This commit is contained in:
+3
-3
@@ -5,8 +5,8 @@ use anyhow::Result;
|
||||
use comfy_table::{presets, ContentArrangement};
|
||||
use dirs::download_dir;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Account<B> {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Account<B: Clone> {
|
||||
pub backend: B,
|
||||
pub downloads_dir: PathBuf,
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Account<B> {
|
||||
pub table_arrangement: ContentArrangement,
|
||||
}
|
||||
|
||||
impl<B> Account<B> {
|
||||
impl<B: Clone> Account<B> {
|
||||
pub fn new(config: Config, account_config: AccountConfig, backend: B) -> Result<Self> {
|
||||
Ok(Self {
|
||||
backend,
|
||||
|
||||
+3
-3
@@ -65,7 +65,7 @@ pub enum BackendCommand {
|
||||
}
|
||||
|
||||
impl BackendCommand {
|
||||
pub fn exec(
|
||||
pub fn execute(
|
||||
self,
|
||||
printer: &mut impl Printer,
|
||||
config_paths: &[PathBuf],
|
||||
@@ -86,7 +86,7 @@ impl BackendCommand {
|
||||
|
||||
let account = Account::new(config, account_config, imap_config)?;
|
||||
|
||||
cmd.exec(printer, account)
|
||||
cmd.execute(printer, account)
|
||||
}
|
||||
#[cfg(feature = "smtp")]
|
||||
Self::Smtp(cmd) => {
|
||||
@@ -99,7 +99,7 @@ impl BackendCommand {
|
||||
|
||||
let account = Account::new(config, account_config, smtp_config)?;
|
||||
|
||||
cmd.exec(printer, account)
|
||||
cmd.execute(printer, account)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+71
-65
@@ -1,10 +1,13 @@
|
||||
use std::{collections::HashMap, fmt, path::PathBuf, process::Command};
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use comfy_table::ContentArrangement;
|
||||
use pimalaya_toolbox::config::TomlConfig;
|
||||
use secrecy::SecretString;
|
||||
use serde::{de::Visitor, Deserialize, Deserializer};
|
||||
use pimalaya_toolbox::{
|
||||
config::{shell_expanded_string, TomlConfig},
|
||||
sasl::{Sasl, SaslAnonymous, SaslLogin, SaslMechanism, SaslPlain},
|
||||
secret::{Secret, SecretError},
|
||||
stream::{Rustls, RustlsCrypto, Tls, TlsProvider},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use url::Url;
|
||||
|
||||
/// Global configuration.
|
||||
@@ -133,6 +136,32 @@ pub enum RustlsCryptoConfig {
|
||||
Ring,
|
||||
}
|
||||
|
||||
impl TryFrom<TlsConfig> for Tls {
|
||||
type Error = SecretError;
|
||||
|
||||
fn try_from(config: TlsConfig) -> Result<Self, Self::Error> {
|
||||
Ok(Tls {
|
||||
provider: match config.provider {
|
||||
None => None,
|
||||
Some(config) => Some(match config {
|
||||
TlsProviderConfig::Rustls => TlsProvider::Rustls,
|
||||
TlsProviderConfig::NativeTls => TlsProvider::NativeTls,
|
||||
}),
|
||||
},
|
||||
rustls: Rustls {
|
||||
crypto: match config.rustls.crypto {
|
||||
None => None,
|
||||
Some(config) => Some(match config {
|
||||
RustlsCryptoConfig::Aws => RustlsCrypto::Aws,
|
||||
RustlsCryptoConfig::Ring => RustlsCrypto::Ring,
|
||||
}),
|
||||
},
|
||||
},
|
||||
cert: config.cert,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// SASL configuration.
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
|
||||
@@ -163,7 +192,7 @@ pub enum SaslMechanismConfig {
|
||||
pub struct SaslLoginConfig {
|
||||
#[serde(deserialize_with = "shell_expanded_string")]
|
||||
pub username: String,
|
||||
pub password: SecretConfig,
|
||||
pub password: Secret,
|
||||
}
|
||||
|
||||
/// SASL PLAIN configuration.
|
||||
@@ -173,7 +202,7 @@ pub struct SaslPlainConfig {
|
||||
pub authzid: Option<String>,
|
||||
#[serde(deserialize_with = "shell_expanded_string")]
|
||||
pub authcid: String,
|
||||
pub passwd: SecretConfig,
|
||||
pub passwd: Secret,
|
||||
}
|
||||
|
||||
/// SASL ANONYMOUS configuration.
|
||||
@@ -183,64 +212,41 @@ pub struct SaslAnonymousConfig {
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
/// Secret configuration.
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum SecretConfig {
|
||||
Raw(SecretString),
|
||||
Command(Vec<String>),
|
||||
}
|
||||
impl TryFrom<SaslConfig> for Sasl {
|
||||
type Error = SecretError;
|
||||
|
||||
impl SecretConfig {
|
||||
pub fn get(&self) -> Result<SecretString> {
|
||||
match self {
|
||||
Self::Raw(secret) => Ok(secret.clone()),
|
||||
Self::Command(args) => {
|
||||
let Some((program, args)) = args.split_first() else {
|
||||
bail!("Secret command cannot be empty")
|
||||
};
|
||||
|
||||
let mut cmd = Command::new(program);
|
||||
cmd.args(args);
|
||||
let out = cmd.output()?;
|
||||
|
||||
if !out.status.success() {
|
||||
let err = String::from_utf8_lossy(&out.stderr);
|
||||
bail!("Cannot read secret from command: {err}");
|
||||
}
|
||||
|
||||
let secret = String::from_utf8_lossy(&out.stdout);
|
||||
let secret = secret.trim_matches(['\r', '\n']);
|
||||
let secret = match secret.split_once('\n') {
|
||||
Some((secret, _)) => secret.trim_matches(['\r', '\n']),
|
||||
None => secret,
|
||||
};
|
||||
|
||||
Ok(SecretString::from(secret))
|
||||
}
|
||||
}
|
||||
fn try_from(config: SaslConfig) -> Result<Self, Self::Error> {
|
||||
Ok(Sasl {
|
||||
mechanisms: config
|
||||
.mechanisms
|
||||
.into_iter()
|
||||
.map(|m| match m {
|
||||
SaslMechanismConfig::Anonymous => SaslMechanism::Anonymous,
|
||||
SaslMechanismConfig::Plain => SaslMechanism::Plain,
|
||||
SaslMechanismConfig::Login => SaslMechanism::Login,
|
||||
})
|
||||
.collect(),
|
||||
anonymous: match config.anonymous {
|
||||
None => None,
|
||||
Some(config) => Some(SaslAnonymous {
|
||||
message: config.message,
|
||||
}),
|
||||
},
|
||||
plain: match config.plain {
|
||||
None => None,
|
||||
Some(config) => Some(SaslPlain {
|
||||
authzid: config.authzid,
|
||||
authcid: config.authcid,
|
||||
passwd: config.passwd.get()?,
|
||||
}),
|
||||
},
|
||||
login: match config.login {
|
||||
None => None,
|
||||
Some(config) => Some(SaslLogin {
|
||||
username: config.username,
|
||||
password: config.password.get()?,
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ShellExpandedStringVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for ShellExpandedStringVisitor {
|
||||
type Value = String;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("an string containing environment variable(s)")
|
||||
}
|
||||
|
||||
fn visit_string<E: serde::de::Error>(self, v: String) -> Result<Self::Value, E> {
|
||||
match shellexpand::full(&v) {
|
||||
Ok(v) => Ok(v.to_string()),
|
||||
Err(_) => Ok(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shell_expanded_string<'de, D: Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<String, D::Error> {
|
||||
deserializer.deserialize_string(ShellExpandedStringVisitor)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
use anyhow::Result;
|
||||
use pimalaya_toolbox::stream::imap::ImapSession;
|
||||
|
||||
use crate::{account::Account, config::ImapConfig};
|
||||
|
||||
pub type ImapAccount = Account<ImapConfig>;
|
||||
|
||||
impl ImapAccount {
|
||||
pub fn new_imap_session(&self) -> Result<ImapSession> {
|
||||
ImapSession::new(
|
||||
self.backend.url.clone(),
|
||||
self.backend.tls.clone().try_into()?,
|
||||
self.backend.starttls,
|
||||
self.backend.sasl.clone().try_into()?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -29,14 +29,14 @@ pub enum ImapCommand {
|
||||
}
|
||||
|
||||
impl ImapCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
match self {
|
||||
Self::Id(cmd) => cmd.exec(printer, account),
|
||||
Self::Id(cmd) => cmd.execute(printer, account),
|
||||
|
||||
Self::Envelopes(cmd) => cmd.exec(printer, account),
|
||||
Self::Flags(cmd) => cmd.exec(printer, account),
|
||||
Self::Mailboxes(cmd) => cmd.exec(printer, account),
|
||||
Self::Messages(cmd) => cmd.exec(printer, account),
|
||||
Self::Envelopes(cmd) => cmd.execute(printer, account),
|
||||
Self::Flags(cmd) => cmd.execute(printer, account),
|
||||
Self::Mailboxes(cmd) => cmd.execute(printer, account),
|
||||
Self::Messages(cmd) => cmd.execute(printer, account),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,13 +25,13 @@ pub enum EnvelopeCommand {
|
||||
}
|
||||
|
||||
impl EnvelopeCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
match self {
|
||||
Self::Get(cmd) => cmd.exec(printer, account),
|
||||
Self::List(cmd) => cmd.exec(printer, account),
|
||||
Self::Search(cmd) => cmd.exec(printer, account),
|
||||
Self::Sort(cmd) => cmd.exec(printer, account),
|
||||
Self::Thread(cmd) => cmd.exec(printer, account),
|
||||
Self::Get(cmd) => cmd.execute(printer, account),
|
||||
Self::List(cmd) => cmd.execute(printer, account),
|
||||
Self::Search(cmd) => cmd.execute(printer, account),
|
||||
Self::Sort(cmd) => cmd.execute(printer, account),
|
||||
Self::Thread(cmd) => cmd.execute(printer, account),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ use crate::imap::{
|
||||
account::ImapAccount,
|
||||
envelope::list::{decode_mime, format_address},
|
||||
mailbox::arg::{MailboxNameOptionalFlag, MailboxSelectFlag},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Get a single IMAP envelope.
|
||||
@@ -42,18 +41,17 @@ pub struct GetEnvelopeCommand {
|
||||
}
|
||||
|
||||
impl GetEnvelopeCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -68,11 +66,11 @@ impl GetEnvelopeCommand {
|
||||
MacroOrMessageDataItemNames::MessageDataItemNames(vec![MessageDataItemName::Envelope]);
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapFetchFirst::new(context, id, item_names, !self.seq);
|
||||
let mut coroutine = ImapFetchFirst::new(imap.context, id, item_names, !self.seq);
|
||||
|
||||
let items = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapFetchFirstResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapFetchFirstResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapFetchFirstResult::Ok { items, .. } => break items,
|
||||
ImapFetchFirstResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ use serde::Serialize;
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameOptionalArg, MailboxSelectFlag},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// List IMAP envelopes from the given mailbox.
|
||||
@@ -45,18 +44,17 @@ pub struct ListEnvelopesCommand {
|
||||
}
|
||||
|
||||
impl ListEnvelopesCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -68,11 +66,11 @@ impl ListEnvelopesCommand {
|
||||
MacroOrMessageDataItemNames::MessageDataItemNames(vec![MessageDataItemName::Envelope]);
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapFetch::new(context, sequence_set, item_names, !self.seq);
|
||||
let mut coroutine = ImapFetch::new(imap.context, sequence_set, item_names, !self.seq);
|
||||
|
||||
let data = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapFetchResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapFetchResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapFetchResult::Ok { data, .. } => break data,
|
||||
ImapFetchResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ use serde::Serialize;
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameOptionalFlag, MailboxSelectFlag},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Search IMAP messages by criteria.
|
||||
@@ -63,18 +62,17 @@ pub struct SearchEnvelopesCommand {
|
||||
}
|
||||
|
||||
impl SearchEnvelopesCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -84,11 +82,11 @@ impl SearchEnvelopesCommand {
|
||||
let criteria = parse_query(&self.query)?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSearch::new(context, criteria, !self.seq);
|
||||
let mut coroutine = ImapSearch::new(imap.context, criteria, !self.seq);
|
||||
|
||||
let ids = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSearchResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSearchResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSearchResult::Ok { ids, .. } => break ids,
|
||||
ImapSearchResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ use serde::{Serialize, Serializer};
|
||||
|
||||
use crate::imap::{
|
||||
account::ImapAccount, envelope::search::parse_query, mailbox::arg::MailboxNameOptionalArg,
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Sort messages by criteria.
|
||||
@@ -56,18 +55,17 @@ pub struct SortEnvelopesCommand {
|
||||
}
|
||||
|
||||
impl SortEnvelopesCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
// SELECT mailbox
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
let context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -89,7 +87,7 @@ impl SortEnvelopesCommand {
|
||||
|
||||
let ids = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSortResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSortResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSortResult::Ok { ids, .. } => break ids,
|
||||
ImapSortResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
+17
-18
@@ -3,7 +3,6 @@ use std::{collections::HashMap, fmt, num::NonZeroU32};
|
||||
use anyhow::{bail, Result};
|
||||
use clap::Parser;
|
||||
use io_imap::{
|
||||
context::ImapContext,
|
||||
coroutines::{fetch::*, select::*, thread::*},
|
||||
types::{
|
||||
extensions::thread::{Thread, ThreadingAlgorithm},
|
||||
@@ -12,14 +11,13 @@ use io_imap::{
|
||||
},
|
||||
};
|
||||
use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::Printer;
|
||||
use pimalaya_toolbox::{stream::imap::ImapSession, terminal::printer::Printer};
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
envelope::{list::decode_mime, search::parse_query},
|
||||
mailbox::arg::{MailboxNameOptionalFlag, MailboxSelectFlag},
|
||||
stream::{self, Stream},
|
||||
};
|
||||
|
||||
/// Thread IMAP messages by algorithm.
|
||||
@@ -51,18 +49,17 @@ pub struct ThreadEnvelopesCommand {
|
||||
}
|
||||
|
||||
impl ThreadEnvelopesCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -73,14 +70,17 @@ impl ThreadEnvelopesCommand {
|
||||
let search_criteria = parse_query(&self.query)?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapThread::new(context, algorithm, search_criteria, !self.seq);
|
||||
let mut coroutine = ImapThread::new(imap.context, algorithm, search_criteria, !self.seq);
|
||||
|
||||
let (context, threads) = loop {
|
||||
let threads = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapThreadResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapThreadResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapThreadResult::Ok {
|
||||
context, threads, ..
|
||||
} => break (context, threads),
|
||||
} => {
|
||||
imap.context = context;
|
||||
break threads;
|
||||
}
|
||||
ImapThreadResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
};
|
||||
@@ -90,7 +90,7 @@ impl ThreadEnvelopesCommand {
|
||||
|
||||
// Fetch subjects for all messages in threads
|
||||
let subjects = if !all_ids.is_empty() {
|
||||
fetch_subjects(&mut stream, context, &all_ids, !self.seq)?
|
||||
fetch_subjects(imap, &all_ids, !self.seq)?
|
||||
} else {
|
||||
HashMap::new()
|
||||
};
|
||||
@@ -138,8 +138,7 @@ fn collect_thread_ids_recursive(thread: &Thread, ids: &mut Vec<NonZeroU32>) {
|
||||
}
|
||||
|
||||
fn fetch_subjects(
|
||||
stream: &mut Stream,
|
||||
context: ImapContext,
|
||||
mut imap: ImapSession,
|
||||
ids: &[NonZeroU32],
|
||||
uid: bool,
|
||||
) -> Result<HashMap<u32, String>> {
|
||||
@@ -161,11 +160,11 @@ fn fetch_subjects(
|
||||
]);
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapFetch::new(context, sequence_set, item_names, uid);
|
||||
let mut coroutine = ImapFetch::new(imap.context, sequence_set, item_names, uid);
|
||||
|
||||
let data = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapFetchResult::Io { io } => arg = Some(handle(&mut *stream, io)?),
|
||||
ImapFetchResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapFetchResult::Ok { data, .. } => break data,
|
||||
ImapFetchResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameOptionalFlag, MailboxSelectFlag},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Add IMAP flag(s) to message(s).
|
||||
@@ -40,18 +39,17 @@ pub struct AddFlagsCommand {
|
||||
}
|
||||
|
||||
impl AddFlagsCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -67,11 +65,11 @@ impl AddFlagsCommand {
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine =
|
||||
ImapStoreSilent::new(context, sequence_set, StoreType::Add, flags, !self.seq);
|
||||
ImapStoreSilent::new(imap.context, sequence_set, StoreType::Add, flags, !self.seq);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapStoreSilentResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapStoreSilentResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapStoreSilentResult::Ok { .. } => break,
|
||||
ImapStoreSilentResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ pub enum FlagCommand {
|
||||
}
|
||||
|
||||
impl FlagCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
match self {
|
||||
Self::List(cmd) => cmd.exec(printer, account),
|
||||
Self::Add(cmd) => cmd.exec(printer, account),
|
||||
Self::Set(cmd) => cmd.exec(printer, account),
|
||||
Self::Remove(cmd) => cmd.exec(printer, account),
|
||||
Self::List(cmd) => cmd.execute(printer, account),
|
||||
Self::Add(cmd) => cmd.execute(printer, account),
|
||||
Self::Set(cmd) => cmd.execute(printer, account),
|
||||
Self::Remove(cmd) => cmd.execute(printer, account),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::Printer;
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream};
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg};
|
||||
|
||||
/// List available IMAP flags for the given mailbox.
|
||||
///
|
||||
@@ -25,17 +25,16 @@ pub struct ListFlagsCommand {
|
||||
}
|
||||
|
||||
impl ListFlagsCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
let (flags, permanent_flags) = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { data, .. } => {
|
||||
break (
|
||||
data.flags.unwrap_or_default(),
|
||||
|
||||
+13
-10
@@ -13,7 +13,6 @@ use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameOptionalFlag, MailboxSelectFlag},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Remove IMAP flag(s) from message(s).
|
||||
@@ -40,18 +39,17 @@ pub struct RemoveFlagsCommand {
|
||||
}
|
||||
|
||||
impl RemoveFlagsCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -66,12 +64,17 @@ impl RemoveFlagsCommand {
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine =
|
||||
ImapStoreSilent::new(context, sequence_set, StoreType::Remove, flags, !self.seq);
|
||||
let mut coroutine = ImapStoreSilent::new(
|
||||
imap.context,
|
||||
sequence_set,
|
||||
StoreType::Remove,
|
||||
flags,
|
||||
!self.seq,
|
||||
);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapStoreSilentResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapStoreSilentResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapStoreSilentResult::Ok { .. } => break,
|
||||
ImapStoreSilentResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
+13
-10
@@ -13,7 +13,6 @@ use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameOptionalFlag, MailboxSelectFlag},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Set IMAP flag(s) on message(s), replacing any existing flags.
|
||||
@@ -40,18 +39,17 @@ pub struct SetFlagsCommand {
|
||||
}
|
||||
|
||||
impl SetFlagsCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -66,12 +64,17 @@ impl SetFlagsCommand {
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine =
|
||||
ImapStoreSilent::new(context, sequence_set, StoreType::Replace, flags, !self.seq);
|
||||
let mut coroutine = ImapStoreSilent::new(
|
||||
imap.context,
|
||||
sequence_set,
|
||||
StoreType::Replace,
|
||||
flags,
|
||||
!self.seq,
|
||||
);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapStoreSilentResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapStoreSilentResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapStoreSilentResult::Ok { .. } => break,
|
||||
ImapStoreSilentResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
+5
-6
@@ -14,7 +14,7 @@ use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::Printer;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::imap::{account::ImapAccount, stream};
|
||||
use crate::imap::account::ImapAccount;
|
||||
|
||||
/// Get information about the IMAP server.
|
||||
///
|
||||
@@ -31,9 +31,8 @@ pub struct IdCommand {
|
||||
}
|
||||
|
||||
impl IdCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mut params = HashMap::new();
|
||||
|
||||
params.extend([
|
||||
@@ -60,11 +59,11 @@ impl IdCommand {
|
||||
}
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapId::new(context, Some(params.into_iter().collect()));
|
||||
let mut coroutine = ImapId::new(imap.context, Some(params.into_iter().collect()));
|
||||
|
||||
let params = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapIdResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapIdResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapIdResult::Ok { server_id, .. } => break server_id,
|
||||
ImapIdResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use io_imap::coroutines::close::*;
|
||||
use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, stream};
|
||||
use crate::imap::account::ImapAccount;
|
||||
|
||||
/// Close the current, selected mailbox.
|
||||
///
|
||||
@@ -19,15 +19,15 @@ use crate::imap::{account::ImapAccount, stream};
|
||||
pub struct CloseMailboxCommand;
|
||||
|
||||
impl CloseMailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut close_coroutine = ImapClose::new(context);
|
||||
let mut close_coroutine = ImapClose::new(imap.context);
|
||||
|
||||
loop {
|
||||
match close_coroutine.resume(arg.take()) {
|
||||
ImapCloseResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapCloseResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapCloseResult::Ok { .. } => break,
|
||||
ImapCloseResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
+13
-13
@@ -37,20 +37,20 @@ pub enum MailboxCommand {
|
||||
}
|
||||
|
||||
impl MailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
match self {
|
||||
Self::Close(cmd) => cmd.exec(printer, account),
|
||||
Self::Create(cmd) => cmd.exec(printer, account),
|
||||
Self::Delete(cmd) => cmd.exec(printer, account),
|
||||
Self::Expunge(cmd) => cmd.exec(printer, account),
|
||||
Self::List(cmd) => cmd.exec(printer, account),
|
||||
Self::Purge(cmd) => cmd.exec(printer, account),
|
||||
Self::Rename(cmd) => cmd.exec(printer, account),
|
||||
Self::Select(cmd) => cmd.exec(printer, account),
|
||||
Self::Status(cmd) => cmd.exec(printer, account),
|
||||
Self::Subscribe(cmd) => cmd.exec(printer, account),
|
||||
Self::Unselect(cmd) => cmd.exec(printer, account),
|
||||
Self::Unsubscribe(cmd) => cmd.exec(printer, account),
|
||||
Self::Close(cmd) => cmd.execute(printer, account),
|
||||
Self::Create(cmd) => cmd.execute(printer, account),
|
||||
Self::Delete(cmd) => cmd.execute(printer, account),
|
||||
Self::Expunge(cmd) => cmd.execute(printer, account),
|
||||
Self::List(cmd) => cmd.execute(printer, account),
|
||||
Self::Purge(cmd) => cmd.execute(printer, account),
|
||||
Self::Rename(cmd) => cmd.execute(printer, account),
|
||||
Self::Select(cmd) => cmd.execute(printer, account),
|
||||
Self::Status(cmd) => cmd.execute(printer, account),
|
||||
Self::Subscribe(cmd) => cmd.execute(printer, account),
|
||||
Self::Unselect(cmd) => cmd.execute(printer, account),
|
||||
Self::Unsubscribe(cmd) => cmd.execute(printer, account),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use io_imap::coroutines::create::*;
|
||||
use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream};
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg};
|
||||
|
||||
/// Create the given mailbox.
|
||||
///
|
||||
@@ -17,17 +17,17 @@ pub struct CreateMailboxCommand {
|
||||
}
|
||||
|
||||
impl CreateMailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapCreate::new(context, mailbox);
|
||||
let mut coroutine = ImapCreate::new(imap.context, mailbox);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapCreateResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapCreateResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapCreateResult::Ok { .. } => break,
|
||||
ImapCreateResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use io_imap::coroutines::delete::*;
|
||||
use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream};
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg};
|
||||
|
||||
/// Delete the given mailbox.
|
||||
///
|
||||
@@ -17,17 +17,16 @@ pub struct DeleteMailboxCommand {
|
||||
}
|
||||
|
||||
impl DeleteMailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapDelete::new(context, mailbox);
|
||||
let mut coroutine = ImapDelete::new(imap.context, mailbox);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapDeleteResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapDeleteResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapDeleteResult::Ok { .. } => break,
|
||||
ImapDeleteResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameArg, MailboxSelectFlag},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Expunge the given mailbox.
|
||||
@@ -23,18 +22,17 @@ pub struct ExpungeMailboxCommand {
|
||||
}
|
||||
|
||||
impl ExpungeMailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -42,11 +40,11 @@ impl ExpungeMailboxCommand {
|
||||
}
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapExpunge::new(context);
|
||||
let mut coroutine = ImapExpunge::new(imap.context);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapExpungeResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapExpungeResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapExpungeResult::Ok { .. } => break,
|
||||
ImapExpungeResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::Printer;
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, stream};
|
||||
use crate::imap::account::ImapAccount;
|
||||
|
||||
/// List, search and filter mailboxes.
|
||||
///
|
||||
@@ -34,30 +34,29 @@ pub struct ListMailboxesCommand {
|
||||
}
|
||||
|
||||
impl ListMailboxesCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let reference = self.reference.try_into()?;
|
||||
let pattern = self.pattern.try_into()?;
|
||||
|
||||
let mailboxes = if self.all {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapList::new(context, reference, pattern);
|
||||
let mut coroutine = ImapList::new(imap.context, reference, pattern);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapListResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapListResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapListResult::Ok { mailboxes, .. } => break mailboxes,
|
||||
ImapListResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapLsub::new(context, reference, pattern);
|
||||
let mut coroutine = ImapLsub::new(imap.context, reference, pattern);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapLsubResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapLsubResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapLsubResult::Ok { mailboxes, .. } => break mailboxes,
|
||||
ImapLsubResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
+10
-12
@@ -10,7 +10,6 @@ use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameArg, MailboxSelectFlag},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Shortcut for marking as deleted all envelopes then expunging the
|
||||
@@ -27,18 +26,17 @@ pub struct PurgeMailboxCommand {
|
||||
}
|
||||
|
||||
impl PurgeMailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -47,27 +45,27 @@ impl PurgeMailboxCommand {
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapStoreSilent::new(
|
||||
context,
|
||||
imap.context,
|
||||
"1:*".try_into()?,
|
||||
StoreType::Add,
|
||||
vec![Flag::Deleted],
|
||||
false,
|
||||
);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapStoreSilentResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapStoreSilentResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapStoreSilentResult::Ok { context, .. } => break context,
|
||||
ImapStoreSilentResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
};
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapExpunge::new(context);
|
||||
let mut coroutine = ImapExpunge::new(imap.context);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapExpungeResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapExpungeResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapExpungeResult::Ok { .. } => break,
|
||||
ImapExpungeResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameArg, TargetMailboxNameArg},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Rename the given mailbox.
|
||||
@@ -23,18 +22,17 @@ pub struct RenameMailboxCommand {
|
||||
}
|
||||
|
||||
impl RenameMailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let from = self.from.name.try_into()?;
|
||||
let to = self.to.name.try_into()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapRename::new(context, from, to);
|
||||
let mut coroutine = ImapRename::new(imap.context, from, to);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapRenameResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapRenameResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapRenameResult::Ok { .. } => break,
|
||||
ImapRenameResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use io_imap::coroutines::select::*;
|
||||
use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream};
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg};
|
||||
|
||||
/// Select the given mailbox.
|
||||
///
|
||||
@@ -21,17 +21,16 @@ pub struct SelectMailboxCommand {
|
||||
}
|
||||
|
||||
impl SelectMailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { .. } => break,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::Printer;
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream};
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg};
|
||||
|
||||
/// Get the status of the given mailbox.
|
||||
///
|
||||
@@ -24,9 +24,8 @@ pub struct StatusMailboxCommand {
|
||||
}
|
||||
|
||||
impl StatusMailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
let item_names = vec![
|
||||
StatusDataItemName::Messages,
|
||||
@@ -37,11 +36,11 @@ impl StatusMailboxCommand {
|
||||
];
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapStatus::new(context, mailbox, item_names);
|
||||
let mut coroutine = ImapStatus::new(imap.context, mailbox, item_names);
|
||||
|
||||
let items = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapStatusResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapStatusResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapStatusResult::Ok { items, .. } => break items,
|
||||
ImapStatusResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use io_imap::coroutines::subscribe::*;
|
||||
use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream};
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg};
|
||||
|
||||
/// Subscribe to the given mailbox.
|
||||
///
|
||||
@@ -17,17 +17,16 @@ pub struct SubscribeMailboxCommand {
|
||||
}
|
||||
|
||||
impl SubscribeMailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSubscribe::new(context, mailbox);
|
||||
let mut coroutine = ImapSubscribe::new(imap.context, mailbox);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSubscribeResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSubscribeResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSubscribeResult::Ok { .. } => break,
|
||||
ImapSubscribeResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use io_imap::coroutines::unselect::*;
|
||||
use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, stream};
|
||||
use crate::imap::account::ImapAccount;
|
||||
|
||||
/// Unselect a current, selected mailbox.
|
||||
///
|
||||
@@ -18,15 +18,14 @@ use crate::imap::{account::ImapAccount, stream};
|
||||
pub struct UnselectMailboxCommand;
|
||||
|
||||
impl UnselectMailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mut arg = None;
|
||||
let mut unselect_coroutine = ImapUnselect::new(context);
|
||||
let mut unselect_coroutine = ImapUnselect::new(imap.context);
|
||||
|
||||
loop {
|
||||
match unselect_coroutine.resume(arg.take()) {
|
||||
ImapUnselectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapUnselectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapUnselectResult::Ok { .. } => break,
|
||||
ImapUnselectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use io_imap::coroutines::unsubscribe::*;
|
||||
use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream};
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg};
|
||||
|
||||
/// Unsubscribe from the given mailbox.
|
||||
///
|
||||
@@ -17,17 +17,16 @@ pub struct UnsubscribeMailboxCommand {
|
||||
}
|
||||
|
||||
impl UnsubscribeMailboxCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapUnsubscribe::new(context, mailbox);
|
||||
let mut coroutine = ImapUnsubscribe::new(imap.context, mailbox);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapUnsubscribeResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapUnsubscribeResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapUnsubscribeResult::Ok { .. } => break,
|
||||
ImapUnsubscribeResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -26,14 +26,14 @@ pub enum MessageCommand {
|
||||
}
|
||||
|
||||
impl MessageCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
match self {
|
||||
Self::Save(cmd) => cmd.exec(printer, account),
|
||||
Self::Get(cmd) => cmd.exec(printer, account),
|
||||
Self::Read(cmd) => cmd.exec(printer, account),
|
||||
Self::Export(cmd) => cmd.exec(printer, account),
|
||||
Self::Copy(cmd) => cmd.exec(printer, account),
|
||||
Self::Move(cmd) => cmd.exec(printer, account),
|
||||
Self::Save(cmd) => cmd.execute(printer, account),
|
||||
Self::Get(cmd) => cmd.execute(printer, account),
|
||||
Self::Read(cmd) => cmd.execute(printer, account),
|
||||
Self::Export(cmd) => cmd.execute(printer, account),
|
||||
Self::Copy(cmd) => cmd.execute(printer, account),
|
||||
Self::Move(cmd) => cmd.execute(printer, account),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameOptionalFlag, MailboxSelectFlag, TargetMailboxNameArg},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Copy IMAP message(s) to the given mailbox.
|
||||
@@ -36,18 +35,17 @@ pub struct CopyMessageCommand {
|
||||
}
|
||||
|
||||
impl CopyMessageCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -58,11 +56,11 @@ impl CopyMessageCommand {
|
||||
let destination: Mailbox = self.destination.name.try_into()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapCopy::new(context, sequence_set, destination, !self.seq);
|
||||
let mut coroutine = ImapCopy::new(imap.context, sequence_set, destination, !self.seq);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapCopyResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapCopyResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapCopyResult::Ok { .. } => break,
|
||||
ImapCopyResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use io_stream::runtimes::std::handle;
|
||||
use mail_parser::{MessageParser, MimeHeaders};
|
||||
use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalFlag, stream};
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameOptionalFlag};
|
||||
|
||||
/// Export type for message export.
|
||||
#[derive(Debug, Clone, clap::ValueEnum)]
|
||||
@@ -61,18 +61,17 @@ pub struct ExportMessageCommand {
|
||||
}
|
||||
|
||||
impl ExportMessageCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
// SELECT mailbox
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
let context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -93,7 +92,7 @@ impl ExportMessageCommand {
|
||||
|
||||
let items = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapFetchFirstResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapFetchFirstResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapFetchFirstResult::Ok { items, .. } => break items,
|
||||
ImapFetchFirstResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ use serde::Serialize;
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameOptionalFlag, MailboxSelectFlag},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Get a message and display its structure.
|
||||
@@ -37,9 +36,8 @@ pub struct GetMessageCommand {
|
||||
}
|
||||
|
||||
impl GetMessageCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
let Some(id) = NonZeroU32::new(self.id) else {
|
||||
bail!("ID must be non-zero");
|
||||
@@ -47,11 +45,11 @@ impl GetMessageCommand {
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -66,11 +64,11 @@ impl GetMessageCommand {
|
||||
}]);
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapFetchFirst::new(context, id, item_names, !self.seq);
|
||||
let mut coroutine = ImapFetchFirst::new(imap.context, id, item_names, !self.seq);
|
||||
|
||||
let items = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapFetchFirstResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapFetchFirstResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapFetchFirstResult::Ok { items, .. } => break items,
|
||||
ImapFetchFirstResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameOptionalFlag, MailboxSelectFlag, TargetMailboxNameArg},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Move message(s) to the given mailbox.
|
||||
@@ -37,18 +36,17 @@ pub struct MoveMessageCommand {
|
||||
}
|
||||
|
||||
impl MoveMessageCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -59,11 +57,11 @@ impl MoveMessageCommand {
|
||||
let destination: Mailbox<'static> = self.destination.name.try_into()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapMove::new(context, sequence_set, destination, !self.seq);
|
||||
let mut coroutine = ImapMove::new(imap.context, sequence_set, destination, !self.seq);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapMoveResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapMoveResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapMoveResult::Ok { .. } => break,
|
||||
ImapMoveResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ use serde::Serialize;
|
||||
use crate::imap::{
|
||||
account::ImapAccount,
|
||||
mailbox::arg::{MailboxNameOptionalFlag, MailboxSelectFlag},
|
||||
stream,
|
||||
};
|
||||
|
||||
/// Read message content.
|
||||
@@ -46,18 +45,17 @@ pub struct ReadMessageCommand {
|
||||
}
|
||||
|
||||
impl ReadMessageCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (mut context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox = self.mailbox.name.try_into()?;
|
||||
|
||||
if self.select.r#true {
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapSelect::new(context, mailbox);
|
||||
let mut coroutine = ImapSelect::new(imap.context, mailbox);
|
||||
|
||||
context = loop {
|
||||
imap.context = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapSelectResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapSelectResult::Ok { context, .. } => break context,
|
||||
ImapSelectResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
@@ -76,11 +74,11 @@ impl ReadMessageCommand {
|
||||
}]);
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapFetchFirst::new(context, id, item_names, !self.seq);
|
||||
let mut coroutine = ImapFetchFirst::new(imap.context, id, item_names, !self.seq);
|
||||
|
||||
let items = loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapFetchFirstResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapFetchFirstResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapFetchFirstResult::Ok { items, .. } => break items,
|
||||
ImapFetchFirstResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use io_imap::{
|
||||
use io_stream::runtimes::std::handle;
|
||||
use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg, stream};
|
||||
use crate::imap::{account::ImapAccount, mailbox::arg::MailboxNameArg};
|
||||
|
||||
/// Save a message to a mailbox.
|
||||
///
|
||||
@@ -34,11 +34,9 @@ pub struct SaveMessageCommand {
|
||||
}
|
||||
|
||||
impl SaveMessageCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
|
||||
pub fn execute(self, printer: &mut impl Printer, account: ImapAccount) -> Result<()> {
|
||||
let mut imap = account.new_imap_session()?;
|
||||
let mailbox: Mailbox<'static> = self.mailbox.name.try_into()?;
|
||||
|
||||
let message = if stdin().is_terminal() || printer.is_json() {
|
||||
self.message
|
||||
.join(" ")
|
||||
@@ -63,11 +61,11 @@ impl SaveMessageCommand {
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = ImapAppend::new(context, mailbox, flags, None, message);
|
||||
let mut coroutine = ImapAppend::new(imap.context, mailbox, flags, None, message);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
ImapAppendResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
ImapAppendResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
ImapAppendResult::Ok { .. } => break,
|
||||
ImapAppendResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -5,4 +5,3 @@ pub mod flag;
|
||||
pub mod id;
|
||||
pub mod mailbox;
|
||||
pub mod message;
|
||||
pub mod stream;
|
||||
|
||||
+3
-1
@@ -20,7 +20,9 @@ fn main() {
|
||||
let config_paths = cli.config_paths.as_ref();
|
||||
let account_name = cli.account.name.as_deref();
|
||||
|
||||
let result = cli.command.exec(&mut printer, config_paths, account_name);
|
||||
let result = cli
|
||||
.command
|
||||
.execute(&mut printer, config_paths, account_name);
|
||||
|
||||
ErrorReport::eval(&mut printer, result)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
use anyhow::Result;
|
||||
use pimalaya_toolbox::stream::smtp::SmtpSession;
|
||||
|
||||
use crate::{account::Account, config::SmtpConfig};
|
||||
|
||||
pub type SmtpAccount = Account<SmtpConfig>;
|
||||
|
||||
impl SmtpAccount {
|
||||
pub fn new_smtp_session(&self) -> Result<SmtpSession> {
|
||||
SmtpSession::new(
|
||||
self.backend.url.clone(),
|
||||
self.backend.tls.clone().try_into()?,
|
||||
self.backend.starttls,
|
||||
self.backend.sasl.clone().try_into()?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -18,9 +18,9 @@ pub enum SmtpCommand {
|
||||
}
|
||||
|
||||
impl SmtpCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: SmtpAccount) -> Result<()> {
|
||||
pub fn execute(self, printer: &mut impl Printer, account: SmtpAccount) -> Result<()> {
|
||||
match self {
|
||||
Self::Messages(cmd) => cmd.exec(printer, account),
|
||||
Self::Messages(cmd) => cmd.execute(printer, account),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ pub enum MessageCommand {
|
||||
}
|
||||
|
||||
impl MessageCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: SmtpAccount) -> Result<()> {
|
||||
pub fn execute(self, printer: &mut impl Printer, account: SmtpAccount) -> Result<()> {
|
||||
match self {
|
||||
Self::Send(cmd) => cmd.exec(printer, account),
|
||||
Self::Send(cmd) => cmd.execute(printer, account),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use io_stream::runtimes::std::handle;
|
||||
use mail_parser::{Addr, Address, HeaderName, HeaderValue, MessageParser};
|
||||
use pimalaya_toolbox::terminal::printer::{Message, Printer};
|
||||
|
||||
use crate::smtp::{account::SmtpAccount, stream};
|
||||
use crate::smtp::account::SmtpAccount;
|
||||
|
||||
/// Send a message to a mailbox.
|
||||
///
|
||||
@@ -28,8 +28,8 @@ pub struct SendMessageCommand {
|
||||
}
|
||||
|
||||
impl SendMessageCommand {
|
||||
pub fn exec(self, printer: &mut impl Printer, account: SmtpAccount) -> Result<()> {
|
||||
let (context, mut stream) = stream::connect(account.backend)?;
|
||||
pub fn execute(self, printer: &mut impl Printer, account: SmtpAccount) -> Result<()> {
|
||||
let mut imap = account.new_smtp_session()?;
|
||||
|
||||
let message = if stdin().is_terminal() || printer.is_json() {
|
||||
self.message
|
||||
@@ -48,12 +48,16 @@ impl SendMessageCommand {
|
||||
let (reverse_path, forward_paths) = into_smtp_msg(message.as_bytes())?;
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine =
|
||||
SendSmtpMessage::new(context, reverse_path, forward_paths, message.into_bytes());
|
||||
let mut coroutine = SendSmtpMessage::new(
|
||||
imap.context,
|
||||
reverse_path,
|
||||
forward_paths,
|
||||
message.into_bytes(),
|
||||
);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
SendSmtpMessageResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
SendSmtpMessageResult::Io { io } => arg = Some(handle(&mut imap.stream, io)?),
|
||||
SendSmtpMessageResult::Ok { .. } => break,
|
||||
SendSmtpMessageResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod account;
|
||||
pub mod command;
|
||||
pub mod message;
|
||||
pub mod stream;
|
||||
|
||||
@@ -1,381 +0,0 @@
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Read, Write},
|
||||
net::TcpStream,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use gethostname::gethostname;
|
||||
use io_smtp::{
|
||||
context::SmtpContext,
|
||||
coroutines::{authenticate::*, ehlo::*, greeting_with_capability::*, starttls::*},
|
||||
types::{auth::AuthMechanism, core::EhloDomain, response::Capability, IntoStatic},
|
||||
};
|
||||
use io_stream::runtimes::std::handle;
|
||||
use log::{debug, info};
|
||||
#[cfg(feature = "native-tls")]
|
||||
use native_tls::TlsConnector;
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
use rustls::{
|
||||
crypto::{self, CryptoProvider},
|
||||
pki_types::{pem::PemObject, CertificateDer},
|
||||
ClientConfig, ClientConnection, StreamOwned,
|
||||
};
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
use rustls_platform_verifier::{ConfigVerifierExt, Verifier};
|
||||
#[cfg(windows)]
|
||||
use uds_windows::UnixStream;
|
||||
|
||||
use crate::config::{RustlsCryptoConfig, SaslMechanismConfig, SmtpConfig, TlsProviderConfig};
|
||||
|
||||
pub enum Stream {
|
||||
Tcp(TcpStream),
|
||||
Unix(UnixStream),
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
Rustls(StreamOwned<ClientConnection, TcpStream>),
|
||||
#[cfg(feature = "native-tls")]
|
||||
NativeTls(native_tls::TlsStream<TcpStream>),
|
||||
}
|
||||
|
||||
impl Read for Stream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Self::Tcp(s) => s.read(buf),
|
||||
Self::Unix(s) => s.read(buf),
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
Self::Rustls(s) => s.read(buf),
|
||||
#[cfg(feature = "native-tls")]
|
||||
Self::NativeTls(s) => s.read(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Stream {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Self::Tcp(s) => s.write(buf),
|
||||
Self::Unix(s) => s.write(buf),
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
Self::Rustls(s) => s.write(buf),
|
||||
#[cfg(feature = "native-tls")]
|
||||
Self::NativeTls(s) => s.write(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match self {
|
||||
Self::Tcp(s) => s.flush(),
|
||||
Self::Unix(s) => s.flush(),
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
Self::Rustls(s) => s.flush(),
|
||||
#[cfg(feature = "native-tls")]
|
||||
Self::NativeTls(s) => s.flush(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect(mut config: SmtpConfig) -> Result<(SmtpContext, Stream)> {
|
||||
info!("connecting to SMTP server using {}", config.url);
|
||||
|
||||
let mut context = SmtpContext::new();
|
||||
let host = config.url.host_str().unwrap_or("127.0.0.1");
|
||||
let domain = EhloDomain::Domain(gethostname().as_encoded_bytes().try_into()?).into_static();
|
||||
|
||||
let (mut context, mut stream) = match config.url.scheme() {
|
||||
scheme if scheme.eq_ignore_ascii_case("smtp") => {
|
||||
let port = config.url.port().unwrap_or(25);
|
||||
let mut stream = TcpStream::connect((host, port))?;
|
||||
|
||||
let mut coroutine = GetSmtpGreetingWithCapability::new(context, domain.clone());
|
||||
let mut arg = None;
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
GetSmtpGreetingWithCapabilityResult::Io { io } => {
|
||||
arg = Some(handle(&mut stream, io)?)
|
||||
}
|
||||
GetSmtpGreetingWithCapabilityResult::Ok { context: c } => break context = c,
|
||||
GetSmtpGreetingWithCapabilityResult::Err { err, .. } => Err(err)?,
|
||||
}
|
||||
}
|
||||
|
||||
(context, Stream::Tcp(stream))
|
||||
}
|
||||
scheme if scheme.eq_ignore_ascii_case("smtps") => {
|
||||
let default_port = if config.starttls { 587 } else { 465 };
|
||||
let port = config.url.port().unwrap_or(default_port);
|
||||
let mut stream = TcpStream::connect((host, port))?;
|
||||
|
||||
if config.starttls {
|
||||
let mut coroutine = SmtpStartTls::new(context);
|
||||
let mut arg = None;
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
SmtpStartTlsResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
SmtpStartTlsResult::Ok { context: c } => break context = c,
|
||||
SmtpStartTlsResult::Err { err, .. } => Err(err)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let tls_provider = match config.tls.provider {
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
Some(TlsProviderConfig::Rustls) => TlsProviderConfig::Rustls,
|
||||
#[cfg(not(feature = "rustls-aws"))]
|
||||
#[cfg(not(feature = "rustls-ring"))]
|
||||
Some(TlsProviderConfig::Rustls) => {
|
||||
bail!("Required cargo feature: `rustls-aws` or `rustls-ring`")
|
||||
}
|
||||
#[cfg(feature = "native-tls")]
|
||||
Some(TlsProviderConfig::NativeTls) => TlsProviderConfig::NativeTls,
|
||||
#[cfg(not(feature = "native-tls"))]
|
||||
Some(TlsProviderConfig::NativeTls) => {
|
||||
bail!("Required cargo feature: `native-tls`")
|
||||
}
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
None => TlsProviderConfig::Rustls,
|
||||
#[cfg(not(feature = "rustls-aws"))]
|
||||
#[cfg(not(feature = "rustls-ring"))]
|
||||
#[cfg(feature = "native-tls")]
|
||||
None => TlsProviderConfig::NativeTls,
|
||||
#[cfg(not(feature = "rustls-aws"))]
|
||||
#[cfg(not(feature = "rustls-ring"))]
|
||||
#[cfg(not(feature = "native-tls"))]
|
||||
None => {
|
||||
bail!("Required cargo feature: `rustls-aws`, `rustls-ring` or `native-tls`")
|
||||
}
|
||||
};
|
||||
|
||||
debug!("using TLS provider: {tls_provider:?}");
|
||||
|
||||
let mut stream = match tls_provider {
|
||||
#[cfg(any(feature = "rustls-aws", feature = "rustls-ring"))]
|
||||
TlsProviderConfig::Rustls => {
|
||||
let crypto_provider = match config.tls.rustls.crypto {
|
||||
#[cfg(feature = "rustls-aws")]
|
||||
Some(RustlsCryptoConfig::Aws) => RustlsCryptoConfig::Aws,
|
||||
#[cfg(not(feature = "rustls-aws"))]
|
||||
Some(RustlsCryptoConfig::Aws) => {
|
||||
bail!("Required cargo feature: `rustls-aws`");
|
||||
}
|
||||
#[cfg(feature = "rustls-ring")]
|
||||
Some(RustlsCryptoConfig::Ring) => RustlsCryptoConfig::Ring,
|
||||
#[cfg(not(feature = "rustls-ring"))]
|
||||
Some(RustlsCryptoConfig::Ring) => {
|
||||
bail!("Required cargo feature: `rustls-ring`");
|
||||
}
|
||||
#[cfg(feature = "rustls-ring")]
|
||||
None => RustlsCryptoConfig::Ring,
|
||||
#[cfg(not(feature = "rustls-ring"))]
|
||||
#[cfg(feature = "rustls-aws")]
|
||||
None => RustlsCryptoConfig::Aws,
|
||||
#[cfg(not(feature = "rustls-aws"))]
|
||||
#[cfg(not(feature = "rustls-ring"))]
|
||||
None => {
|
||||
bail!("Required cargo feature: `rustls-aws` or `rustls-ring`");
|
||||
}
|
||||
};
|
||||
|
||||
debug!("using rustls crypto provider: {crypto_provider:?}");
|
||||
|
||||
let crypto_provider = match crypto_provider {
|
||||
#[cfg(feature = "rustls-aws")]
|
||||
RustlsCryptoConfig::Aws => crypto::aws_lc_rs::default_provider(),
|
||||
#[cfg(feature = "rustls-ring")]
|
||||
RustlsCryptoConfig::Ring => crypto::ring::default_provider(),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let crypto_provider = match crypto_provider.install_default() {
|
||||
Ok(()) => CryptoProvider::get_default().unwrap().clone(),
|
||||
Err(crypto_provider) => crypto_provider,
|
||||
};
|
||||
|
||||
let mut config = if let Some(pem_path) = &config.tls.cert {
|
||||
debug!("using TLS cert at {}", pem_path.display());
|
||||
let pem = fs::read(pem_path)?;
|
||||
|
||||
let Some(cert) = CertificateDer::pem_slice_iter(&pem).next() else {
|
||||
bail!("empty TLS cert at {}", pem_path.display())
|
||||
};
|
||||
|
||||
let verifier =
|
||||
Verifier::new_with_extra_roots(vec![cert?], crypto_provider)?;
|
||||
|
||||
ClientConfig::builder()
|
||||
.dangerous()
|
||||
.with_custom_certificate_verifier(Arc::new(verifier))
|
||||
.with_no_client_auth()
|
||||
} else {
|
||||
debug!("using OS TLS certs");
|
||||
ClientConfig::with_platform_verifier()?
|
||||
};
|
||||
|
||||
config.alpn_protocols = vec![b"smtp".to_vec()];
|
||||
|
||||
let server_name = host.to_string().try_into()?;
|
||||
let conn = ClientConnection::new(Arc::new(config), server_name)?;
|
||||
Stream::Rustls(StreamOwned::new(conn, stream))
|
||||
}
|
||||
#[cfg(feature = "native-tls")]
|
||||
TlsProviderConfig::NativeTls => {
|
||||
let mut builder = TlsConnector::builder();
|
||||
|
||||
if let Some(pem_path) = &config.tls.cert {
|
||||
debug!("using TLS cert at {}", pem_path.display());
|
||||
let pem = fs::read(pem_path)?;
|
||||
let cert = native_tls::Certificate::from_pem(&pem)?;
|
||||
builder.add_root_certificate(cert);
|
||||
}
|
||||
|
||||
let connector = builder.build()?;
|
||||
Stream::NativeTls(connector.connect(host, stream)?)
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if config.starttls {
|
||||
let mut coroutine = SmtpEhlo::new(context, domain.clone());
|
||||
let mut arg = None;
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
SmtpEhloResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
SmtpEhloResult::Ok { context: c } => break context = c,
|
||||
SmtpEhloResult::Err { err, .. } => Err(err)?,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut coroutine = GetSmtpGreetingWithCapability::new(context, domain.clone());
|
||||
let mut arg = None;
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
GetSmtpGreetingWithCapabilityResult::Io { io } => {
|
||||
arg = Some(handle(&mut stream, io)?)
|
||||
}
|
||||
GetSmtpGreetingWithCapabilityResult::Ok { context: c } => {
|
||||
break context = c
|
||||
}
|
||||
GetSmtpGreetingWithCapabilityResult::Err { err, .. } => Err(err)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(context, stream)
|
||||
}
|
||||
scheme if scheme.eq_ignore_ascii_case("unix") => {
|
||||
let sock_path = config.url.path();
|
||||
let mut stream = UnixStream::connect(&sock_path)?;
|
||||
|
||||
let mut coroutine = GetSmtpGreetingWithCapability::new(context, domain.clone());
|
||||
let mut arg = None;
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
GetSmtpGreetingWithCapabilityResult::Io { io } => {
|
||||
arg = Some(handle(&mut stream, io)?)
|
||||
}
|
||||
GetSmtpGreetingWithCapabilityResult::Ok { context: c } => break context = c,
|
||||
GetSmtpGreetingWithCapabilityResult::Err { err, .. } => Err(err)?,
|
||||
}
|
||||
}
|
||||
|
||||
(context, Stream::Unix(stream))
|
||||
}
|
||||
scheme => {
|
||||
bail!("Unknown scheme {scheme}, expected smtp, smtps or unix");
|
||||
}
|
||||
};
|
||||
|
||||
if !context.authenticated {
|
||||
let mut candidates = vec![];
|
||||
|
||||
for mechanism in config.sasl.mechanisms {
|
||||
match mechanism {
|
||||
SaslMechanismConfig::Login => {
|
||||
let Some(auth) = config.sasl.login.take() else {
|
||||
debug!("missing SASL LOGIN configuration, skipping it");
|
||||
continue;
|
||||
};
|
||||
|
||||
for capability in &context.capability {
|
||||
match capability {
|
||||
Capability::Auth(mechanisms) => {
|
||||
for m in mechanisms {
|
||||
match m {
|
||||
AuthMechanism::Login => {
|
||||
candidates.push(SmtpAuthenticateCandidate::Login {
|
||||
login: auth.username.clone(),
|
||||
password: auth.password.get()?,
|
||||
domain: domain.clone(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
debug!("SASL LOGIN disabled by the server, skipping it");
|
||||
continue;
|
||||
}
|
||||
SaslMechanismConfig::Plain => {
|
||||
let Some(auth) = config.sasl.plain.take() else {
|
||||
debug!("missing SASL PLAIN configuration, skipping it");
|
||||
continue;
|
||||
};
|
||||
|
||||
for capability in &context.capability {
|
||||
match capability {
|
||||
Capability::Auth(mechanisms) => {
|
||||
for m in mechanisms {
|
||||
match m {
|
||||
AuthMechanism::Plain => {
|
||||
candidates.push(SmtpAuthenticateCandidate::Plain {
|
||||
login: auth.authcid.clone(),
|
||||
password: auth.passwd.get()?,
|
||||
domain: domain.clone(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
debug!("SASL PLAIN disabled by the server, skipping it");
|
||||
continue;
|
||||
}
|
||||
SaslMechanismConfig::Anonymous => {
|
||||
unimplemented!("ANONYMOUS SASL mechanism not yet implemented")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut arg = None;
|
||||
let mut coroutine = SmtpAuthenticate::new(context, candidates);
|
||||
|
||||
loop {
|
||||
match coroutine.resume(arg.take()) {
|
||||
SmtpAuthenticateResult::Io { io } => arg = Some(handle(&mut stream, io)?),
|
||||
SmtpAuthenticateResult::Ok { context: c, .. } => break context = c,
|
||||
SmtpAuthenticateResult::Err { err, .. } => bail!(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((context, stream))
|
||||
}
|
||||
Reference in New Issue
Block a user