mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-08 18:45:13 +00:00
take passwds from command instead of clear
This commit is contained in:
parent
84df58cf81
commit
34189ad8ad
|
@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- List command with pagination [#19]
|
- List command with pagination [#19]
|
||||||
- Icon in table when attachment is present [#16]
|
- Icon in table when attachment is present [#16]
|
||||||
- Multi-account [#17]
|
- Multi-account [#17]
|
||||||
|
- Password from command [#22]
|
||||||
|
|
||||||
[unreleased]: https://github.com/soywod/himalaya
|
[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
|
[#17]: https://github.com/soywod/himalaya/issues/17
|
||||||
[#19]: https://github.com/soywod/himalaya/issues/19
|
[#19]: https://github.com/soywod/himalaya/issues/19
|
||||||
[#21]: https://github.com/soywod/himalaya/issues/21
|
[#21]: https://github.com/soywod/himalaya/issues/21
|
||||||
|
[#22]: https://github.com/soywod/himalaya/issues/22
|
||||||
|
|
|
@ -10,6 +10,8 @@ use std::{
|
||||||
};
|
};
|
||||||
use toml;
|
use toml;
|
||||||
|
|
||||||
|
use crate::io::run_cmd;
|
||||||
|
|
||||||
// Error wrapper
|
// Error wrapper
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -21,11 +23,14 @@ pub enum Error {
|
||||||
GetPathNotFoundError,
|
GetPathNotFoundError,
|
||||||
GetAccountNotFoundError(String),
|
GetAccountNotFoundError(String),
|
||||||
GetAccountDefaultNotFoundError,
|
GetAccountDefaultNotFoundError,
|
||||||
|
ParseImapPasswdUtf8Error,
|
||||||
|
ParseSmtpPasswdUtf8Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "(config): ")?;
|
write!(f, "config: ")?;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Error::IoError(err) => err.fmt(f),
|
Error::IoError(err) => err.fmt(f),
|
||||||
Error::ParseTomlError(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::GetPathNotFoundError => write!(f, "path not found"),
|
||||||
Error::GetAccountNotFoundError(account) => write!(f, "account {} not found", account),
|
Error::GetAccountNotFoundError(account) => write!(f, "account {} not found", account),
|
||||||
Error::GetAccountDefaultNotFoundError => write!(f, "no default account found"),
|
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_host: String,
|
||||||
pub imap_port: u16,
|
pub imap_port: u16,
|
||||||
pub imap_login: String,
|
pub imap_login: String,
|
||||||
pub imap_password: String,
|
pub imap_passwd_cmd: String,
|
||||||
|
|
||||||
pub smtp_host: String,
|
pub smtp_host: String,
|
||||||
pub smtp_port: u16,
|
pub smtp_port: u16,
|
||||||
pub smtp_login: String,
|
pub smtp_login: String,
|
||||||
pub smtp_password: String,
|
pub smtp_passwd_cmd: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Account {
|
impl Account {
|
||||||
pub fn imap_addr(&self) -> (&str, u16) {
|
pub fn imap_passwd(&self) -> Result<String> {
|
||||||
(&self.imap_host, self.imap_port)
|
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 {
|
pub fn smtp_creds(&self) -> Result<SmtpCredentials> {
|
||||||
SmtpCredentials::new(self.smtp_login.to_owned(), self.smtp_password.to_owned())
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
src/imap.rs
15
src/imap.rs
|
@ -2,7 +2,7 @@ use imap;
|
||||||
use native_tls::{self, TlsConnector, TlsStream};
|
use native_tls::{self, TlsConnector, TlsStream};
|
||||||
use std::{fmt, net::TcpStream, result};
|
use std::{fmt, net::TcpStream, result};
|
||||||
|
|
||||||
use crate::config::Account;
|
use crate::config::{self, Account};
|
||||||
use crate::mbox::Mbox;
|
use crate::mbox::Mbox;
|
||||||
use crate::msg::Msg;
|
use crate::msg::Msg;
|
||||||
|
|
||||||
|
@ -16,15 +16,18 @@ pub enum Error {
|
||||||
ReadEmailNotFoundError(String),
|
ReadEmailNotFoundError(String),
|
||||||
ReadEmailEmptyPartError(String, String),
|
ReadEmailEmptyPartError(String, String),
|
||||||
ExtractAttachmentsEmptyError(String),
|
ExtractAttachmentsEmptyError(String),
|
||||||
|
ConfigError(config::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "(imap): ")?;
|
write!(f, "imap: ")?;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Error::CreateTlsConnectorError(err) => err.fmt(f),
|
Error::CreateTlsConnectorError(err) => err.fmt(f),
|
||||||
Error::CreateImapSession(err) => err.fmt(f),
|
Error::CreateImapSession(err) => err.fmt(f),
|
||||||
Error::ParseEmailError(err) => err.fmt(f),
|
Error::ParseEmailError(err) => err.fmt(f),
|
||||||
|
Error::ConfigError(err) => err.fmt(f),
|
||||||
Error::ReadEmailNotFoundError(uid) => {
|
Error::ReadEmailNotFoundError(uid) => {
|
||||||
write!(f, "no email found for uid {}", 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
|
// Result wrapper
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
@ -73,7 +82,7 @@ impl<'a> ImapConnector<'a> {
|
||||||
let tls = TlsConnector::new()?;
|
let tls = TlsConnector::new()?;
|
||||||
let client = imap::connect(account.imap_addr(), &account.imap_host, &tls)?;
|
let client = imap::connect(account.imap_addr(), &account.imap_host, &tls)?;
|
||||||
let sess = client
|
let sess = client
|
||||||
.login(&account.imap_login, &account.imap_password)
|
.login(&account.imap_login, &account.imap_passwd()?)
|
||||||
.map_err(|res| res.0)?;
|
.map_err(|res| res.0)?;
|
||||||
|
|
||||||
Ok(Self { account, sess })
|
Ok(Self { account, sess })
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::{
|
||||||
fmt,
|
fmt,
|
||||||
fs::{remove_file, File},
|
fs::{remove_file, File},
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
process::Command,
|
process::{Command, Output},
|
||||||
result,
|
result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@ pub enum Error {
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "(input): ")?;
|
write!(f, "input: ")?;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Error::IoError(err) => err.fmt(f),
|
Error::IoError(err) => err.fmt(f),
|
||||||
Error::AskForSendingConfirmationError => write!(f, "action cancelled"),
|
Error::AskForSendingConfirmationError => write!(f, "action cancelled"),
|
||||||
|
@ -68,3 +69,11 @@ pub fn ask_for_confirmation(prompt: &str) -> Result<()> {
|
||||||
_ => Err(Error::AskForSendingConfirmationError),
|
_ => 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()
|
||||||
|
}
|
||||||
|
}
|
26
src/main.rs
26
src/main.rs
|
@ -1,6 +1,6 @@
|
||||||
mod config;
|
mod config;
|
||||||
mod imap;
|
mod imap;
|
||||||
mod input;
|
mod io;
|
||||||
mod mbox;
|
mod mbox;
|
||||||
mod msg;
|
mod msg;
|
||||||
mod smtp;
|
mod smtp;
|
||||||
|
@ -20,7 +20,7 @@ const DEFAULT_PAGE: usize = 0;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
ConfigError(config::Error),
|
ConfigError(config::Error),
|
||||||
InputError(input::Error),
|
IoError(io::Error),
|
||||||
MsgError(msg::Error),
|
MsgError(msg::Error),
|
||||||
ImapError(imap::Error),
|
ImapError(imap::Error),
|
||||||
SmtpError(smtp::Error),
|
SmtpError(smtp::Error),
|
||||||
|
@ -30,7 +30,7 @@ impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::ConfigError(err) => err.fmt(f),
|
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::MsgError(err) => err.fmt(f),
|
||||||
Error::ImapError(err) => err.fmt(f),
|
Error::ImapError(err) => err.fmt(f),
|
||||||
Error::SmtpError(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 {
|
impl From<crate::io::Error> for Error {
|
||||||
fn from(err: input::Error) -> Error {
|
fn from(err: crate::io::Error) -> Error {
|
||||||
Error::InputError(err)
|
Error::IoError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,10 +329,10 @@ fn run() -> Result<()> {
|
||||||
let mut imap_conn = ImapConnector::new(&account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
|
|
||||||
let tpl = Msg::build_new_tpl(&config, &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);
|
let msg = Msg::from(content);
|
||||||
|
|
||||||
input::ask_for_confirmation("Send the message?")?;
|
io::ask_for_confirmation("Send the message?")?;
|
||||||
|
|
||||||
println!("Sending …");
|
println!("Sending …");
|
||||||
smtp::send(&account, &msg.to_sendable_msg()?)?;
|
smtp::send(&account, &msg.to_sendable_msg()?)?;
|
||||||
|
@ -357,10 +357,10 @@ fn run() -> Result<()> {
|
||||||
msg.build_reply_tpl(&config, &account)?
|
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);
|
let msg = Msg::from(content);
|
||||||
|
|
||||||
input::ask_for_confirmation("Send the message?")?;
|
io::ask_for_confirmation("Send the message?")?;
|
||||||
|
|
||||||
println!("Sending …");
|
println!("Sending …");
|
||||||
smtp::send(&account, &msg.to_sendable_msg()?)?;
|
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 msg = Msg::from(imap_conn.read_msg(&mbox, &uid)?);
|
||||||
let tpl = msg.build_forward_tpl(&config, &account)?;
|
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);
|
let msg = Msg::from(content);
|
||||||
|
|
||||||
input::ask_for_confirmation("Send the message?")?;
|
io::ask_for_confirmation("Send the message?")?;
|
||||||
|
|
||||||
println!("Sending …");
|
println!("Sending …");
|
||||||
smtp::send(&account, &msg.to_sendable_msg()?)?;
|
smtp::send(&account, &msg.to_sendable_msg()?)?;
|
||||||
|
@ -400,7 +400,7 @@ fn run() -> Result<()> {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(err) = run() {
|
if let Err(err) = run() {
|
||||||
eprintln!("Error {}", err);
|
eprintln!("Error: {}", err);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
src/smtp.rs
12
src/smtp.rs
|
@ -1,13 +1,14 @@
|
||||||
use lettre;
|
use lettre;
|
||||||
use std::{fmt, result};
|
use std::{fmt, result};
|
||||||
|
|
||||||
use crate::config::Account;
|
use crate::config::{self, Account};
|
||||||
|
|
||||||
// Error wrapper
|
// Error wrapper
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
TransportError(lettre::transport::smtp::Error),
|
TransportError(lettre::transport::smtp::Error),
|
||||||
|
ConfigError(config::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
|
@ -15,6 +16,7 @@ impl fmt::Display for Error {
|
||||||
write!(f, "(smtp): ")?;
|
write!(f, "(smtp): ")?;
|
||||||
match self {
|
match self {
|
||||||
Error::TransportError(err) => err.fmt(f),
|
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
|
// Result wrapper
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
@ -35,7 +43,7 @@ pub fn send(account: &Account, msg: &lettre::Message) -> Result<()> {
|
||||||
use lettre::Transport;
|
use lettre::Transport;
|
||||||
|
|
||||||
lettre::transport::smtp::SmtpTransport::relay(&account.smtp_host)?
|
lettre::transport::smtp::SmtpTransport::relay(&account.smtp_host)?
|
||||||
.credentials(account.smtp_creds())
|
.credentials(account.smtp_creds()?)
|
||||||
.build()
|
.build()
|
||||||
.send(msg)
|
.send(msg)
|
||||||
.map(|_| Ok(()))?
|
.map(|_| Ok(()))?
|
||||||
|
|
Loading…
Reference in a new issue