take passwds from command instead of clear

This commit is contained in:
Clément DOUIN 2021-01-17 11:52:31 +01:00
parent 84df58cf81
commit 34189ad8ad
No known key found for this signature in database
GPG key ID: 69C9B9CFFDEE2DEF
6 changed files with 76 additions and 27 deletions

View file

@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- List command with pagination [#19]
- Icon in table when attachment is present [#16]
- Multi-account [#17]
- Password from command [#22]
[unreleased]: https://github.com/soywod/himalaya
@ -45,3 +46,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#17]: https://github.com/soywod/himalaya/issues/17
[#19]: https://github.com/soywod/himalaya/issues/19
[#21]: https://github.com/soywod/himalaya/issues/21
[#22]: https://github.com/soywod/himalaya/issues/22

View file

@ -10,6 +10,8 @@ use std::{
};
use toml;
use crate::io::run_cmd;
// Error wrapper
#[derive(Debug)]
@ -21,11 +23,14 @@ pub enum Error {
GetPathNotFoundError,
GetAccountNotFoundError(String),
GetAccountDefaultNotFoundError,
ParseImapPasswdUtf8Error,
ParseSmtpPasswdUtf8Error,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(config): ")?;
write!(f, "config: ")?;
match self {
Error::IoError(err) => err.fmt(f),
Error::ParseTomlError(err) => err.fmt(f),
@ -34,6 +39,8 @@ impl fmt::Display for Error {
Error::GetPathNotFoundError => write!(f, "path not found"),
Error::GetAccountNotFoundError(account) => write!(f, "account {} not found", account),
Error::GetAccountDefaultNotFoundError => write!(f, "no default account found"),
Error::ParseImapPasswdUtf8Error => write!(f, "imap passwd invalid utf8"),
Error::ParseSmtpPasswdUtf8Error => write!(f, "smtp passwd invalid utf8"),
}
}
}
@ -75,21 +82,35 @@ pub struct Account {
pub imap_host: String,
pub imap_port: u16,
pub imap_login: String,
pub imap_password: String,
pub imap_passwd_cmd: String,
pub smtp_host: String,
pub smtp_port: u16,
pub smtp_login: String,
pub smtp_password: String,
pub smtp_passwd_cmd: String,
}
impl Account {
pub fn imap_addr(&self) -> (&str, u16) {
(&self.imap_host, self.imap_port)
pub fn imap_passwd(&self) -> Result<String> {
let cmd = run_cmd(&self.imap_passwd_cmd)?;
let passwd = String::from_utf8(cmd.stdout);
let passwd = passwd.map_err(|_| Error::ParseImapPasswdUtf8Error)?;
let passwd = passwd.trim_end_matches("\n").to_owned();
Ok(passwd)
}
pub fn smtp_creds(&self) -> SmtpCredentials {
SmtpCredentials::new(self.smtp_login.to_owned(), self.smtp_password.to_owned())
pub fn smtp_creds(&self) -> Result<SmtpCredentials> {
let cmd = run_cmd(&self.smtp_passwd_cmd)?;
let passwd = String::from_utf8(cmd.stdout);
let passwd = passwd.map_err(|_| Error::ParseImapPasswdUtf8Error)?;
let passwd = passwd.trim_end_matches("\n").to_owned();
Ok(SmtpCredentials::new(self.smtp_login.to_owned(), passwd))
}
pub fn imap_addr(&self) -> (&str, u16) {
(&self.imap_host, self.imap_port)
}
}

View file

@ -2,7 +2,7 @@ use imap;
use native_tls::{self, TlsConnector, TlsStream};
use std::{fmt, net::TcpStream, result};
use crate::config::Account;
use crate::config::{self, Account};
use crate::mbox::Mbox;
use crate::msg::Msg;
@ -16,15 +16,18 @@ pub enum Error {
ReadEmailNotFoundError(String),
ReadEmailEmptyPartError(String, String),
ExtractAttachmentsEmptyError(String),
ConfigError(config::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(imap): ")?;
write!(f, "imap: ")?;
match self {
Error::CreateTlsConnectorError(err) => err.fmt(f),
Error::CreateImapSession(err) => err.fmt(f),
Error::ParseEmailError(err) => err.fmt(f),
Error::ConfigError(err) => err.fmt(f),
Error::ReadEmailNotFoundError(uid) => {
write!(f, "no email found for uid {}", uid)
}
@ -56,6 +59,12 @@ impl From<mailparse::MailParseError> for Error {
}
}
impl From<config::Error> for Error {
fn from(err: config::Error) -> Error {
Error::ConfigError(err)
}
}
// Result wrapper
type Result<T> = result::Result<T, Error>;
@ -73,7 +82,7 @@ impl<'a> ImapConnector<'a> {
let tls = TlsConnector::new()?;
let client = imap::connect(account.imap_addr(), &account.imap_host, &tls)?;
let sess = client
.login(&account.imap_login, &account.imap_password)
.login(&account.imap_login, &account.imap_passwd()?)
.map_err(|res| res.0)?;
Ok(Self { account, sess })

View file

@ -3,7 +3,7 @@ use std::{
fmt,
fs::{remove_file, File},
io::{self, Read, Write},
process::Command,
process::{Command, Output},
result,
};
@ -17,7 +17,8 @@ pub enum Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(input): ")?;
write!(f, "input: ")?;
match self {
Error::IoError(err) => err.fmt(f),
Error::AskForSendingConfirmationError => write!(f, "action cancelled"),
@ -68,3 +69,11 @@ pub fn ask_for_confirmation(prompt: &str) -> Result<()> {
_ => Err(Error::AskForSendingConfirmationError),
}
}
pub fn run_cmd(cmd: &str) -> io::Result<Output> {
if cfg!(target_os = "windows") {
Command::new("cmd").args(&["/C", cmd]).output()
} else {
Command::new("sh").arg("-c").arg(cmd).output()
}
}

View file

@ -1,6 +1,6 @@
mod config;
mod imap;
mod input;
mod io;
mod mbox;
mod msg;
mod smtp;
@ -20,7 +20,7 @@ const DEFAULT_PAGE: usize = 0;
#[derive(Debug)]
pub enum Error {
ConfigError(config::Error),
InputError(input::Error),
IoError(io::Error),
MsgError(msg::Error),
ImapError(imap::Error),
SmtpError(smtp::Error),
@ -30,7 +30,7 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::ConfigError(err) => err.fmt(f),
Error::InputError(err) => err.fmt(f),
Error::IoError(err) => err.fmt(f),
Error::MsgError(err) => err.fmt(f),
Error::ImapError(err) => err.fmt(f),
Error::SmtpError(err) => err.fmt(f),
@ -44,9 +44,9 @@ impl From<config::Error> for Error {
}
}
impl From<input::Error> for Error {
fn from(err: input::Error) -> Error {
Error::InputError(err)
impl From<crate::io::Error> for Error {
fn from(err: crate::io::Error) -> Error {
Error::IoError(err)
}
}
@ -329,10 +329,10 @@ fn run() -> Result<()> {
let mut imap_conn = ImapConnector::new(&account)?;
let tpl = Msg::build_new_tpl(&config, &account)?;
let content = input::open_editor_with_tpl(&tpl.as_bytes())?;
let content = io::open_editor_with_tpl(&tpl.as_bytes())?;
let msg = Msg::from(content);
input::ask_for_confirmation("Send the message?")?;
io::ask_for_confirmation("Send the message?")?;
println!("Sending …");
smtp::send(&account, &msg.to_sendable_msg()?)?;
@ -357,10 +357,10 @@ fn run() -> Result<()> {
msg.build_reply_tpl(&config, &account)?
};
let content = input::open_editor_with_tpl(&tpl.as_bytes())?;
let content = io::open_editor_with_tpl(&tpl.as_bytes())?;
let msg = Msg::from(content);
input::ask_for_confirmation("Send the message?")?;
io::ask_for_confirmation("Send the message?")?;
println!("Sending …");
smtp::send(&account, &msg.to_sendable_msg()?)?;
@ -380,10 +380,10 @@ fn run() -> Result<()> {
let msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
let tpl = msg.build_forward_tpl(&config, &account)?;
let content = input::open_editor_with_tpl(&tpl.as_bytes())?;
let content = io::open_editor_with_tpl(&tpl.as_bytes())?;
let msg = Msg::from(content);
input::ask_for_confirmation("Send the message?")?;
io::ask_for_confirmation("Send the message?")?;
println!("Sending …");
smtp::send(&account, &msg.to_sendable_msg()?)?;
@ -400,7 +400,7 @@ fn run() -> Result<()> {
fn main() {
if let Err(err) = run() {
eprintln!("Error {}", err);
eprintln!("Error: {}", err);
exit(1);
}
}

View file

@ -1,13 +1,14 @@
use lettre;
use std::{fmt, result};
use crate::config::Account;
use crate::config::{self, Account};
// Error wrapper
#[derive(Debug)]
pub enum Error {
TransportError(lettre::transport::smtp::Error),
ConfigError(config::Error),
}
impl fmt::Display for Error {
@ -15,6 +16,7 @@ impl fmt::Display for Error {
write!(f, "(smtp): ")?;
match self {
Error::TransportError(err) => err.fmt(f),
Error::ConfigError(err) => err.fmt(f),
}
}
}
@ -25,6 +27,12 @@ impl From<lettre::transport::smtp::Error> for Error {
}
}
impl From<config::Error> for Error {
fn from(err: config::Error) -> Error {
Error::ConfigError(err)
}
}
// Result wrapper
type Result<T> = result::Result<T, Error>;
@ -35,7 +43,7 @@ pub fn send(account: &Account, msg: &lettre::Message) -> Result<()> {
use lettre::Transport;
lettre::transport::smtp::SmtpTransport::relay(&account.smtp_host)?
.credentials(account.smtp_creds())
.credentials(account.smtp_creds()?)
.build()
.send(msg)
.map(|_| Ok(()))?