From 34189ad8ad2e181febaba5c4de5788b3927ba5c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Sun, 17 Jan 2021 11:52:31 +0100 Subject: [PATCH] take passwds from command instead of clear --- CHANGELOG.md | 2 ++ src/config.rs | 35 ++++++++++++++++++++++++++++------- src/imap.rs | 15 ++++++++++++--- src/{input.rs => io.rs} | 13 +++++++++++-- src/main.rs | 26 +++++++++++++------------- src/smtp.rs | 12 ++++++++++-- 6 files changed, 76 insertions(+), 27 deletions(-) rename src/{input.rs => io.rs} (84%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab4b4fa..6bea3e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/config.rs b/src/config.rs index 366081f..b8915d8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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 { + 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 { + 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) } } diff --git a/src/imap.rs b/src/imap.rs index 6a90c31..0f229d5 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -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 for Error { } } +impl From for Error { + fn from(err: config::Error) -> Error { + Error::ConfigError(err) + } +} + // Result wrapper type Result = result::Result; @@ -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 }) diff --git a/src/input.rs b/src/io.rs similarity index 84% rename from src/input.rs rename to src/io.rs index 02f7d19..7bc69d6 100644 --- a/src/input.rs +++ b/src/io.rs @@ -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 { + if cfg!(target_os = "windows") { + Command::new("cmd").args(&["/C", cmd]).output() + } else { + Command::new("sh").arg("-c").arg(cmd).output() + } +} diff --git a/src/main.rs b/src/main.rs index fde3fe8..1455990 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 for Error { } } -impl From for Error { - fn from(err: input::Error) -> Error { - Error::InputError(err) +impl From 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); } } diff --git a/src/smtp.rs b/src/smtp.rs index 3c3f0c5..fd4a338 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -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 for Error { } } +impl From for Error { + fn from(err: config::Error) -> Error { + Error::ConfigError(err) + } +} + // Result wrapper type Result = result::Result; @@ -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(()))?