mirror of
https://git.meli.delivery/meli/meli
synced 2024-11-15 06:12:47 +00:00
Add tools subcommand with smtp shell for debugging
Add tools subcommand, with options to launch imap, smtp or jmap shells. Currently only smtp is implemented, imap is working but with quirks. To use, run $ meli tools smtp-shell <account-name> Where account name is what you have in your config file. For convenience, typing an invalid name will list all the valid names: $ meli tools smtp-shell "asdf" The configuration file does not contain the account `asdf`. It contains the following: user@example.com work personal account Try again with a valid account name. Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
This commit is contained in:
parent
74a3539f88
commit
e187bb3f0d
@ -63,19 +63,21 @@ pub enum SubCommand {
|
|||||||
#[structopt(value_name = "CONFIG_PATH", parse(from_os_str))]
|
#[structopt(value_name = "CONFIG_PATH", parse(from_os_str))]
|
||||||
path: Option<PathBuf>,
|
path: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
#[structopt(visible_alias="docs", aliases=&["docs", "manpage", "manpages"])]
|
|
||||||
#[structopt(display_order = 3)]
|
#[structopt(display_order = 3)]
|
||||||
|
/// Testing tools such as IMAP, SMTP shells for debugging.
|
||||||
|
Tools(ToolOpt),
|
||||||
|
#[structopt(visible_alias="docs", aliases=&["docs", "manpage", "manpages"])]
|
||||||
|
#[structopt(display_order = 4)]
|
||||||
/// print documentation page and exit (Piping to a pager is recommended.).
|
/// print documentation page and exit (Piping to a pager is recommended.).
|
||||||
Man(ManOpt),
|
Man(ManOpt),
|
||||||
|
#[structopt(display_order = 5)]
|
||||||
#[structopt(display_order = 4)]
|
|
||||||
/// Install manual pages to the first location provided by `$MANPATH` /
|
/// Install manual pages to the first location provided by `$MANPATH` /
|
||||||
/// `manpath(1)`, unless you specify the directory as an argument.
|
/// `manpath(1)`, unless you specify the directory as an argument.
|
||||||
InstallMan {
|
InstallMan {
|
||||||
#[structopt(value_name = "DESTINATION_PATH", parse(from_os_str))]
|
#[structopt(value_name = "DESTINATION_PATH", parse(from_os_str))]
|
||||||
destination_path: Option<PathBuf>,
|
destination_path: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
#[structopt(display_order = 5)]
|
#[structopt(display_order = 6)]
|
||||||
/// Print compile time feature flags of this binary
|
/// Print compile time feature flags of this binary
|
||||||
CompiledWith,
|
CompiledWith,
|
||||||
/// Print log file location.
|
/// Print log file location.
|
||||||
@ -101,3 +103,21 @@ pub struct ManOpt {
|
|||||||
)]
|
)]
|
||||||
pub no_raw: Option<Option<bool>>,
|
pub no_raw: Option<Option<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub enum ToolOpt {
|
||||||
|
ImapShell {
|
||||||
|
#[structopt(value_name = "CONFIG_TOML_ACCOUNT_NAME")]
|
||||||
|
account: String,
|
||||||
|
},
|
||||||
|
#[cfg(feature = "smtp")]
|
||||||
|
SmtpShell {
|
||||||
|
#[structopt(value_name = "CONFIG_TOML_ACCOUNT_NAME")]
|
||||||
|
account: String,
|
||||||
|
},
|
||||||
|
#[cfg(feature = "jmap")]
|
||||||
|
JmapShell {
|
||||||
|
#[structopt(value_name = "CONFIG_TOML_ACCOUNT_NAME")]
|
||||||
|
account: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@ -95,6 +95,9 @@ fn run_app(opt: Opt) -> Result<()> {
|
|||||||
Some(SubCommand::TestConfig { path }) => {
|
Some(SubCommand::TestConfig { path }) => {
|
||||||
return subcommands::test_config(path);
|
return subcommands::test_config(path);
|
||||||
}
|
}
|
||||||
|
Some(SubCommand::Tools(toolopt)) => {
|
||||||
|
return subcommands::tool(opt.config, toolopt);
|
||||||
|
}
|
||||||
Some(SubCommand::CreateConfig { path }) => {
|
Some(SubCommand::CreateConfig { path }) => {
|
||||||
return subcommands::create_config(path);
|
return subcommands::create_config(path);
|
||||||
}
|
}
|
||||||
|
@ -172,3 +172,113 @@ pub fn view(
|
|||||||
)));
|
)));
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tool(path: Option<PathBuf>, opt: crate::args::ToolOpt) -> Result<()> {
|
||||||
|
use melib::utils::futures::timeout;
|
||||||
|
|
||||||
|
use crate::{args::ToolOpt, conf::composing::SendMail};
|
||||||
|
|
||||||
|
let config_path = if let Some(path) = path {
|
||||||
|
path.expand()
|
||||||
|
} else {
|
||||||
|
crate::conf::get_config_file()?
|
||||||
|
};
|
||||||
|
let conf = conf::FileSettings::validate(config_path, true, false)?;
|
||||||
|
let account = match opt {
|
||||||
|
ToolOpt::ImapShell { ref account } => account,
|
||||||
|
#[cfg(feature = "smtp")]
|
||||||
|
ToolOpt::SmtpShell { ref account } => account,
|
||||||
|
#[cfg(feature = "jmap")]
|
||||||
|
ToolOpt::JmapShell { ref account } => account,
|
||||||
|
};
|
||||||
|
if !conf.accounts.contains_key(&account.clone()) {
|
||||||
|
println!(
|
||||||
|
"The configuration file does not contain the account `{}`. It contains the following:",
|
||||||
|
account
|
||||||
|
);
|
||||||
|
for a in conf.accounts.keys() {
|
||||||
|
println!("{}", a);
|
||||||
|
}
|
||||||
|
return Err("Try again with a valid account name.".into());
|
||||||
|
}
|
||||||
|
let file_account_conf = conf.accounts[account].clone();
|
||||||
|
let account_conf: conf::AccountConf = file_account_conf.clone().into();
|
||||||
|
match opt {
|
||||||
|
#[cfg(feature = "smtp")]
|
||||||
|
ToolOpt::SmtpShell { .. } => {
|
||||||
|
let send_mail = file_account_conf
|
||||||
|
.conf_override
|
||||||
|
.composing
|
||||||
|
.send_mail
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&conf.composing.send_mail)
|
||||||
|
.clone();
|
||||||
|
let SendMail::Smtp(smtp_conf) = send_mail else {
|
||||||
|
panic!(
|
||||||
|
"smtp shell requires an smtp configuration for account {}",
|
||||||
|
account
|
||||||
|
);
|
||||||
|
};
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let ex = melib::smol::Executor::new();
|
||||||
|
futures::executor::block_on(ex.run(futures::future::pending::<()>()));
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut conn = futures::executor::block_on(
|
||||||
|
melib::smtp::SmtpConnection::new_connection(smtp_conf),
|
||||||
|
)?;
|
||||||
|
let mut input = String::new();
|
||||||
|
let mut res = String::with_capacity(8 * 1024);
|
||||||
|
println!(
|
||||||
|
"Connected. Type a valid SMTP command such as EHLO and hit Return. Exit with \
|
||||||
|
Ctrl+C or with the command QUIT."
|
||||||
|
);
|
||||||
|
loop {
|
||||||
|
use std::io;
|
||||||
|
input.clear();
|
||||||
|
res.clear();
|
||||||
|
|
||||||
|
match io::stdin().read_line(&mut input) {
|
||||||
|
Ok(_) => {
|
||||||
|
futures::executor::block_on(timeout(
|
||||||
|
None,
|
||||||
|
conn.send_command(&[input.trim().as_bytes()]),
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
futures::executor::block_on(timeout(None, conn.read_lines(&mut res, None)))
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
println!("\rC: {}", input.trim());
|
||||||
|
print!("S: {}", res);
|
||||||
|
if input.trim().eq_ignore_ascii_case("quit") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => println!("error: {}", error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolOpt::ImapShell { .. } => {
|
||||||
|
let mut imap = melib::imap::ImapType::new(
|
||||||
|
&account_conf.account,
|
||||||
|
Box::new(|_| true),
|
||||||
|
melib::BackendEventConsumer::new(std::sync::Arc::new(|_, _| ())),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let ex = melib::smol::Executor::new();
|
||||||
|
futures::executor::block_on(ex.run(futures::future::pending::<()>()));
|
||||||
|
});
|
||||||
|
(imap.as_any_mut())
|
||||||
|
.downcast_mut::<melib::imap::ImapType>()
|
||||||
|
.unwrap()
|
||||||
|
.shell();
|
||||||
|
}
|
||||||
|
#[cfg(feature = "jmap")]
|
||||||
|
ToolOpt::JmapShell { .. } => {
|
||||||
|
unimplemented!("Sorry, no JMAP shell yet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user