mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-22 02:50:19 +00:00
refactor config and account system
This commit is contained in:
parent
5a9481910f
commit
979c6ef1c9
18 changed files with 1130 additions and 1051 deletions
|
@ -1,2 +1 @@
|
|||
pub mod cli;
|
||||
pub mod model;
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
use clap;
|
||||
|
||||
use crate::{
|
||||
config::model::{Account, Config},
|
||||
output::model::Output,
|
||||
};
|
||||
use crate::{domain::config::entity::Config, output::model::Output};
|
||||
|
||||
/// `Ctx` stands for `Context` and includes the most "important" structs which are used quite often
|
||||
/// in this crate.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Ctx<'a> {
|
||||
pub config: Config,
|
||||
pub account: Account,
|
||||
pub output: Output,
|
||||
pub mbox: String,
|
||||
pub arg_matches: clap::ArgMatches<'a>,
|
||||
|
@ -19,7 +15,6 @@ pub struct Ctx<'a> {
|
|||
impl<'a> Ctx<'a> {
|
||||
pub fn new<S: ToString>(
|
||||
config: Config,
|
||||
account: Account,
|
||||
output: Output,
|
||||
mbox: S,
|
||||
arg_matches: clap::ArgMatches<'a>,
|
||||
|
@ -28,7 +23,6 @@ impl<'a> Ctx<'a> {
|
|||
|
||||
Self {
|
||||
config,
|
||||
account,
|
||||
output,
|
||||
mbox,
|
||||
arg_matches,
|
||||
|
|
296
src/domain/account/entity.rs
Normal file
296
src/domain/account/entity.rs
Normal file
|
@ -0,0 +1,296 @@
|
|||
use anyhow::{anyhow, Context, Error, Result};
|
||||
use lettre::transport::smtp::authentication::Credentials as SmtpCredentials;
|
||||
use log::debug;
|
||||
use std::{convert::TryFrom, env, fs, path::PathBuf};
|
||||
|
||||
use crate::{domain::config::entity::Config, output::utils::run_cmd};
|
||||
|
||||
const DEFAULT_PAGE_SIZE: usize = 10;
|
||||
const DEFAULT_SIG_DELIM: &str = "-- \n";
|
||||
|
||||
/// Representation of a user account.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Account {
|
||||
pub name: String,
|
||||
pub from: String,
|
||||
pub downloads_dir: PathBuf,
|
||||
pub signature: String,
|
||||
pub default_page_size: usize,
|
||||
pub watch_cmds: Vec<String>,
|
||||
|
||||
pub default: bool,
|
||||
pub email: String,
|
||||
|
||||
pub imap_host: String,
|
||||
pub imap_port: u16,
|
||||
pub imap_starttls: bool,
|
||||
pub imap_insecure: bool,
|
||||
pub imap_login: String,
|
||||
pub imap_passwd_cmd: String,
|
||||
|
||||
pub smtp_host: String,
|
||||
pub smtp_port: u16,
|
||||
pub smtp_starttls: bool,
|
||||
pub smtp_insecure: bool,
|
||||
pub smtp_login: String,
|
||||
pub smtp_passwd_cmd: String,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
/// This is a little helper-function like which uses the the name and email
|
||||
/// of the account to create a valid address for the header of the headers
|
||||
/// of a msg.
|
||||
///
|
||||
/// # Hint
|
||||
/// If the name includes some special characters like a whitespace, comma or semicolon, then
|
||||
/// the name will be automatically wrapped between two `"`.
|
||||
///
|
||||
/// # Exapmle
|
||||
/// ```
|
||||
/// use himalaya::config::model::{Account, Config};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let config = Config::default();
|
||||
///
|
||||
/// let normal_account = Account::new(Some("Acc1"), "acc1@mail.com");
|
||||
/// // notice the semicolon in the name!
|
||||
/// let special_account = Account::new(Some("TL;DR"), "acc2@mail.com");
|
||||
///
|
||||
/// // -- Expeced outputs --
|
||||
/// let expected_normal = Account {
|
||||
/// name: Some("Acc1".to_string()),
|
||||
/// email: "acc1@mail.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// let expected_special = Account {
|
||||
/// name: Some("\"TL;DR\"".to_string()),
|
||||
/// email: "acc2@mail.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(config.address(&normal_account), "Acc1 <acc1@mail.com>");
|
||||
/// assert_eq!(config.address(&special_account), "\"TL;DR\" <acc2@mail.com>");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn address(&self) -> String {
|
||||
let name = &self.from;
|
||||
let has_special_chars = "()<>[]:;@.,".contains(|special_char| name.contains(special_char));
|
||||
|
||||
if name.is_empty() {
|
||||
format!("{}", self.email)
|
||||
} else if has_special_chars {
|
||||
// so the name has special characters => Wrap it with '"'
|
||||
format!("\"{}\" <{}>", name, self.email)
|
||||
} else {
|
||||
format!("{} <{}>", name, self.email)
|
||||
}
|
||||
}
|
||||
/// Returns the imap-host address + the port usage of the account
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use himalaya::config::model::Account;
|
||||
/// fn main () {
|
||||
/// let account = Account {
|
||||
/// imap_host: String::from("hostExample"),
|
||||
/// imap_port: 42,
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// let expected_output = ("hostExample", 42);
|
||||
///
|
||||
/// assert_eq!(account.imap_addr(), expected_output);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn imap_addr(&self) -> (&str, u16) {
|
||||
debug!("host: {}", self.imap_host);
|
||||
debug!("port: {}", self.imap_port);
|
||||
(&self.imap_host, self.imap_port)
|
||||
}
|
||||
|
||||
/// Runs the given command in your password string and returns it.
|
||||
pub fn imap_passwd(&self) -> Result<String> {
|
||||
let passwd = run_cmd(&self.imap_passwd_cmd).context("cannot run IMAP passwd cmd")?;
|
||||
let passwd = passwd
|
||||
.trim_end_matches(|c| c == '\r' || c == '\n')
|
||||
.to_owned();
|
||||
|
||||
Ok(passwd)
|
||||
}
|
||||
|
||||
pub fn smtp_creds(&self) -> Result<SmtpCredentials> {
|
||||
let passwd = run_cmd(&self.smtp_passwd_cmd).context("cannot run SMTP passwd cmd")?;
|
||||
let passwd = passwd
|
||||
.trim_end_matches(|c| c == '\r' || c == '\n')
|
||||
.to_owned();
|
||||
|
||||
Ok(SmtpCredentials::new(self.smtp_login.to_owned(), passwd))
|
||||
}
|
||||
|
||||
/// Creates a new account with the given values and returns it. All other attributes of the
|
||||
/// account are gonna be empty/None.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use himalaya::config::model::Account;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let account1 = Account::new(Some("Name1"), "email@address.com");
|
||||
/// let account2 = Account::new(None, "email@address.com");
|
||||
///
|
||||
/// let expected1 = Account {
|
||||
/// name: Some("Name1".to_string()),
|
||||
/// email: "email@address.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// let expected2 = Account {
|
||||
/// email: "email@address.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(account1, expected1);
|
||||
/// assert_eq!(account2, expected2);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new<S: ToString + Default>(name: Option<S>, email_addr: S) -> Self {
|
||||
Self {
|
||||
name: name.unwrap_or_default().to_string(),
|
||||
email: email_addr.to_string(),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new account with a custom signature. Passing `None` to `signature` sets the
|
||||
/// signature to `Account Signature`.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use himalaya::config::model::Account;
|
||||
///
|
||||
/// fn main() {
|
||||
///
|
||||
/// // the testing accounts
|
||||
/// let account_with_custom_signature = Account::new_with_signature(
|
||||
/// Some("Email name"), "some@mail.com", Some("Custom signature! :)"));
|
||||
/// let account_with_default_signature = Account::new_with_signature(
|
||||
/// Some("Email name"), "some@mail.com", None);
|
||||
///
|
||||
/// // How they should look like
|
||||
/// let account_cmp1 = Account {
|
||||
/// name: Some("Email name".to_string()),
|
||||
/// email: "some@mail.com".to_string(),
|
||||
/// signature: Some("Custom signature! :)".to_string()),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// let account_cmp2 = Account {
|
||||
/// name: Some("Email name".to_string()),
|
||||
/// email: "some@mail.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(account_with_custom_signature, account_cmp1);
|
||||
/// assert_eq!(account_with_default_signature, account_cmp2);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new_with_signature<S: AsRef<str> + ToString + Default>(
|
||||
name: Option<S>,
|
||||
email_addr: S,
|
||||
signature: Option<S>,
|
||||
) -> Self {
|
||||
let mut account = Account::new(name, email_addr);
|
||||
account.signature = signature.unwrap_or_default().to_string();
|
||||
account
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<(&'a Config, Option<&str>)> for Account {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from((config, account_name): (&'a Config, Option<&str>)) -> Result<Self, Self::Error> {
|
||||
let (name, account) = match account_name {
|
||||
Some("") | None => config
|
||||
.accounts
|
||||
.iter()
|
||||
.find(|(_, account)| account.default.unwrap_or(false))
|
||||
.map(|(name, account)| (name.to_owned(), account))
|
||||
.ok_or_else(|| anyhow!("cannot find default account")),
|
||||
Some(name) => config
|
||||
.accounts
|
||||
.get(name)
|
||||
.map(|account| (name.to_owned(), account))
|
||||
.ok_or_else(|| anyhow!(format!("cannot find account `{}`", name))),
|
||||
}?;
|
||||
|
||||
let downloads_dir = account
|
||||
.downloads_dir
|
||||
.as_ref()
|
||||
.and_then(|dir| dir.to_str())
|
||||
.and_then(|dir| shellexpand::full(dir).ok())
|
||||
.map(|dir| PathBuf::from(dir.to_string()))
|
||||
.or_else(|| {
|
||||
config
|
||||
.downloads_dir
|
||||
.as_ref()
|
||||
.and_then(|dir| dir.to_str())
|
||||
.and_then(|dir| shellexpand::full(dir).ok())
|
||||
.map(|dir| PathBuf::from(dir.to_string()))
|
||||
})
|
||||
.unwrap_or_else(|| env::temp_dir());
|
||||
|
||||
let default_page_size = account
|
||||
.default_page_size
|
||||
.as_ref()
|
||||
.or_else(|| config.default_page_size.as_ref())
|
||||
.unwrap_or(&DEFAULT_PAGE_SIZE)
|
||||
.to_owned();
|
||||
|
||||
let default_sig_delim = DEFAULT_SIG_DELIM.to_string();
|
||||
let signature_delim = account
|
||||
.signature_delimiter
|
||||
.as_ref()
|
||||
.or_else(|| config.signature_delimiter.as_ref())
|
||||
.unwrap_or(&default_sig_delim);
|
||||
let signature = account
|
||||
.signature
|
||||
.as_ref()
|
||||
.or_else(|| config.signature.as_ref());
|
||||
let signature = signature
|
||||
.and_then(|sig| shellexpand::full(sig).ok())
|
||||
.map(|sig| sig.to_string())
|
||||
.and_then(|sig| fs::read_to_string(sig).ok())
|
||||
.or_else(|| signature.map(|sig| sig.to_owned()))
|
||||
.map(|sig| format!("\n{}{}", signature_delim, sig))
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(Account {
|
||||
name,
|
||||
from: account.name.as_ref().unwrap_or(&config.name).to_owned(),
|
||||
downloads_dir,
|
||||
signature,
|
||||
default_page_size,
|
||||
watch_cmds: account
|
||||
.watch_cmds
|
||||
.as_ref()
|
||||
.or_else(|| config.watch_cmds.as_ref())
|
||||
.unwrap_or(&vec![])
|
||||
.to_owned(),
|
||||
default: account.default.unwrap_or(false),
|
||||
email: account.email.to_owned(),
|
||||
imap_host: account.imap_host.to_owned(),
|
||||
imap_port: account.imap_port,
|
||||
imap_starttls: account.imap_starttls.unwrap_or_default(),
|
||||
imap_insecure: account.imap_insecure.unwrap_or_default(),
|
||||
imap_login: account.imap_login.to_owned(),
|
||||
imap_passwd_cmd: account.imap_passwd_cmd.to_owned(),
|
||||
smtp_host: account.smtp_host.to_owned(),
|
||||
smtp_port: account.smtp_port,
|
||||
smtp_starttls: account.smtp_starttls.unwrap_or_default(),
|
||||
smtp_insecure: account.smtp_insecure.unwrap_or_default(),
|
||||
smtp_login: account.smtp_login.to_owned(),
|
||||
smtp_passwd_cmd: account.smtp_passwd_cmd.to_owned(),
|
||||
})
|
||||
}
|
||||
}
|
3
src/domain/account/mod.rs
Normal file
3
src/domain/account/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
//! Modules related to the user's accounts.
|
||||
|
||||
pub mod entity;
|
|
@ -1,48 +1,32 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use lettre::transport::smtp::authentication::Credentials as SmtpCredentials;
|
||||
use anyhow::{anyhow, Context, Error, Result};
|
||||
use log::debug;
|
||||
use serde::Deserialize;
|
||||
use shellexpand;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env,
|
||||
fs::{self, File},
|
||||
io::Read,
|
||||
path::PathBuf,
|
||||
thread,
|
||||
};
|
||||
use std::{collections::HashMap, convert::TryFrom, env, fs, path::PathBuf, thread};
|
||||
use toml;
|
||||
|
||||
use crate::output::utils::run_cmd;
|
||||
|
||||
const DEFAULT_PAGE_SIZE: usize = 10;
|
||||
|
||||
// --- Account ---
|
||||
/// Represents an account section in your config file.
|
||||
///
|
||||
/// [account section]: https://github.com/soywod/himalaya/wiki/Configuration:config-file#account-specific-settings
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, Clone, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Account {
|
||||
// Override
|
||||
// TODO: rename with `from`
|
||||
pub name: Option<String>,
|
||||
pub downloads_dir: Option<PathBuf>,
|
||||
pub signature_delimiter: Option<String>,
|
||||
pub signature: Option<String>,
|
||||
pub default_page_size: Option<usize>,
|
||||
pub watch_cmds: Option<Vec<String>>,
|
||||
|
||||
// Specific
|
||||
pub default: Option<bool>,
|
||||
pub email: String,
|
||||
|
||||
pub imap_host: String,
|
||||
pub imap_port: u16,
|
||||
pub imap_starttls: Option<bool>,
|
||||
pub imap_insecure: Option<bool>,
|
||||
pub imap_login: String,
|
||||
pub imap_passwd_cmd: String,
|
||||
|
||||
pub smtp_host: String,
|
||||
pub smtp_port: u16,
|
||||
pub smtp_starttls: Option<bool>,
|
||||
|
@ -51,193 +35,13 @@ pub struct Account {
|
|||
pub smtp_passwd_cmd: String,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
/// Returns the imap-host address + the port usage of the account
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use himalaya::config::model::Account;
|
||||
/// fn main () {
|
||||
/// let account = Account {
|
||||
/// imap_host: String::from("hostExample"),
|
||||
/// imap_port: 42,
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// let expected_output = ("hostExample", 42);
|
||||
///
|
||||
/// assert_eq!(account.imap_addr(), expected_output);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn imap_addr(&self) -> (&str, u16) {
|
||||
debug!("host: {}", self.imap_host);
|
||||
debug!("port: {}", self.imap_port);
|
||||
(&self.imap_host, self.imap_port)
|
||||
}
|
||||
pub type AccountsMap = HashMap<String, Account>;
|
||||
|
||||
/// Runs the given command in your password string and returns it.
|
||||
pub fn imap_passwd(&self) -> Result<String> {
|
||||
let passwd = run_cmd(&self.imap_passwd_cmd).context("cannot run IMAP passwd cmd")?;
|
||||
let passwd = passwd
|
||||
.trim_end_matches(|c| c == '\r' || c == '\n')
|
||||
.to_owned();
|
||||
|
||||
Ok(passwd)
|
||||
}
|
||||
|
||||
pub fn imap_starttls(&self) -> bool {
|
||||
let starttls = match self.imap_starttls {
|
||||
Some(true) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
debug!("STARTTLS: {}", starttls);
|
||||
starttls
|
||||
}
|
||||
|
||||
pub fn imap_insecure(&self) -> bool {
|
||||
let insecure = match self.imap_insecure {
|
||||
Some(true) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
debug!("insecure: {}", insecure);
|
||||
insecure
|
||||
}
|
||||
|
||||
pub fn smtp_creds(&self) -> Result<SmtpCredentials> {
|
||||
let passwd = run_cmd(&self.smtp_passwd_cmd).context("cannot run SMTP passwd cmd")?;
|
||||
let passwd = passwd
|
||||
.trim_end_matches(|c| c == '\r' || c == '\n')
|
||||
.to_owned();
|
||||
|
||||
Ok(SmtpCredentials::new(self.smtp_login.to_owned(), passwd))
|
||||
}
|
||||
|
||||
pub fn smtp_starttls(&self) -> bool {
|
||||
match self.smtp_starttls {
|
||||
Some(true) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn smtp_insecure(&self) -> bool {
|
||||
match self.smtp_insecure {
|
||||
Some(true) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new account with the given values and returns it. All other attributes of the
|
||||
/// account are gonna be empty/None.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use himalaya::config::model::Account;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let account1 = Account::new(Some("Name1"), "email@address.com");
|
||||
/// let account2 = Account::new(None, "email@address.com");
|
||||
///
|
||||
/// let expected1 = Account {
|
||||
/// name: Some("Name1".to_string()),
|
||||
/// email: "email@address.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// let expected2 = Account {
|
||||
/// email: "email@address.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(account1, expected1);
|
||||
/// assert_eq!(account2, expected2);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new<S: ToString>(name: Option<S>, email_addr: S) -> Self {
|
||||
Self {
|
||||
name: name.and_then(|name| Some(name.to_string())),
|
||||
email: email_addr.to_string(),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new account with a custom signature. Passing `None` to `signature` sets the
|
||||
/// signature to `Account Signature`.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use himalaya::config::model::Account;
|
||||
///
|
||||
/// fn main() {
|
||||
///
|
||||
/// // the testing accounts
|
||||
/// let account_with_custom_signature = Account::new_with_signature(
|
||||
/// Some("Email name"), "some@mail.com", Some("Custom signature! :)"));
|
||||
/// let account_with_default_signature = Account::new_with_signature(
|
||||
/// Some("Email name"), "some@mail.com", None);
|
||||
///
|
||||
/// // How they should look like
|
||||
/// let account_cmp1 = Account {
|
||||
/// name: Some("Email name".to_string()),
|
||||
/// email: "some@mail.com".to_string(),
|
||||
/// signature: Some("Custom signature! :)".to_string()),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// let account_cmp2 = Account {
|
||||
/// name: Some("Email name".to_string()),
|
||||
/// email: "some@mail.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(account_with_custom_signature, account_cmp1);
|
||||
/// assert_eq!(account_with_default_signature, account_cmp2);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new_with_signature<S: AsRef<str> + ToString>(
|
||||
name: Option<S>,
|
||||
email_addr: S,
|
||||
signature: Option<S>,
|
||||
) -> Self {
|
||||
let mut account = Account::new(name, email_addr);
|
||||
account.signature = signature.and_then(|signature| Some(signature.to_string()));
|
||||
account
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Account {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: None,
|
||||
downloads_dir: None,
|
||||
signature_delimiter: None,
|
||||
signature: None,
|
||||
default_page_size: None,
|
||||
default: None,
|
||||
email: String::new(),
|
||||
watch_cmds: None,
|
||||
imap_host: String::new(),
|
||||
imap_port: 0,
|
||||
imap_starttls: None,
|
||||
imap_insecure: None,
|
||||
imap_login: String::new(),
|
||||
imap_passwd_cmd: String::new(),
|
||||
smtp_host: String::new(),
|
||||
smtp_port: 0,
|
||||
smtp_starttls: None,
|
||||
smtp_insecure: None,
|
||||
smtp_login: String::new(),
|
||||
smtp_passwd_cmd: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Config ---
|
||||
/// Represents the whole config file.
|
||||
#[derive(Debug, Default, Deserialize, Clone)]
|
||||
#[derive(Debug, Default, Clone, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Config {
|
||||
// TODO: rename with `from`
|
||||
pub name: String,
|
||||
pub downloads_dir: Option<PathBuf>,
|
||||
pub notify_cmd: Option<String>,
|
||||
|
@ -290,22 +94,13 @@ impl Config {
|
|||
Ok(path)
|
||||
}
|
||||
|
||||
/// Parses the config file by the given path and stores the values into the struct.
|
||||
pub fn new(path: Option<PathBuf>) -> Result<Self> {
|
||||
let path = match path {
|
||||
Some(path) => path,
|
||||
None => Self::path_from_xdg()
|
||||
.or_else(|_| Self::path_from_xdg_alt())
|
||||
.or_else(|_| Self::path_from_home())
|
||||
.context("cannot find config path")?,
|
||||
};
|
||||
pub fn path() -> Result<PathBuf> {
|
||||
let path = Self::path_from_xdg()
|
||||
.or_else(|_| Self::path_from_xdg_alt())
|
||||
.or_else(|_| Self::path_from_home())
|
||||
.context("cannot find config path")?;
|
||||
|
||||
let mut file = File::open(path).context("cannot open config file")?;
|
||||
let mut content = vec![];
|
||||
file.read_to_end(&mut content)
|
||||
.context("cannot read config file")?;
|
||||
|
||||
Ok(toml::from_slice(&content).context("cannot parse config file")?)
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Returns the account by the given name.
|
||||
|
@ -386,9 +181,7 @@ impl Config {
|
|||
/// ```
|
||||
pub fn address(&self, account: &Account) -> String {
|
||||
let name = account.name.as_ref().unwrap_or(&self.name);
|
||||
|
||||
let has_special_chars: bool =
|
||||
"()<>[]:;@.,".contains(|special_char| name.contains(special_char));
|
||||
let has_special_chars = "()<>[]:;@.,".contains(|special_char| name.contains(special_char));
|
||||
|
||||
if name.is_empty() {
|
||||
format!("{}", account.email)
|
||||
|
@ -491,57 +284,62 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
impl TryFrom<PathBuf> for Config {
|
||||
type Error = Error;
|
||||
|
||||
#[cfg(test)]
|
||||
mod config_test {
|
||||
|
||||
use crate::config::model::{Account, Config};
|
||||
|
||||
// a quick way to get a config instance for testing
|
||||
fn get_config() -> Config {
|
||||
Config {
|
||||
name: String::from("Config Name"),
|
||||
..Config::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_account_by_name() {
|
||||
let mut config = get_config();
|
||||
|
||||
let account1 = Account::new(None, "one@mail.com");
|
||||
let account2 = Account::new(Some("Two"), "two@mail.com");
|
||||
|
||||
// add some accounts
|
||||
config.accounts.insert("One".to_string(), account1.clone());
|
||||
config.accounts.insert("Two".to_string(), account2.clone());
|
||||
|
||||
let ret1 = config.find_account_by_name(Some("One")).unwrap();
|
||||
let ret2 = config.find_account_by_name(Some("Two")).unwrap();
|
||||
|
||||
assert_eq!(*ret1, account1);
|
||||
assert_eq!(*ret2, account2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address() {
|
||||
let config = get_config();
|
||||
|
||||
let account1 = Account::new(None, "one@mail.com");
|
||||
let account2 = Account::new(Some("Two"), "two@mail.com");
|
||||
let account3 = Account::new(Some("TL;DR"), "three@mail.com");
|
||||
let account4 = Account::new(Some("TL,DR"), "lol@mail.com");
|
||||
let account5 = Account::new(Some("TL:DR"), "rofl@mail.com");
|
||||
let account6 = Account::new(Some("TL.DR"), "rust@mail.com");
|
||||
|
||||
assert_eq!(&config.address(&account1), "Config Name <one@mail.com>");
|
||||
assert_eq!(&config.address(&account2), "Two <two@mail.com>");
|
||||
assert_eq!(&config.address(&account3), "\"TL;DR\" <three@mail.com>");
|
||||
assert_eq!(&config.address(&account4), "\"TL,DR\" <lol@mail.com>");
|
||||
assert_eq!(&config.address(&account5), "\"TL:DR\" <rofl@mail.com>");
|
||||
assert_eq!(&config.address(&account6), "\"TL.DR\" <rust@mail.com>");
|
||||
}
|
||||
fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
|
||||
let file_content = fs::read_to_string(path).context("cannot read config file")?;
|
||||
Ok(toml::from_str(&file_content).context("cannot parse config file")?)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: tests
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use crate::domain::{account::entity::Account, config::entity::Config};
|
||||
|
||||
// // a quick way to get a config instance for testing
|
||||
// fn get_config() -> Config {
|
||||
// Config {
|
||||
// name: String::from("Config Name"),
|
||||
// ..Config::default()
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn test_find_account_by_name() {
|
||||
// let mut config = get_config();
|
||||
|
||||
// let account1 = Account::new(None, "one@mail.com");
|
||||
// let account2 = Account::new(Some("Two"), "two@mail.com");
|
||||
|
||||
// // add some accounts
|
||||
// config.accounts.insert("One".to_string(), account1.clone());
|
||||
// config.accounts.insert("Two".to_string(), account2.clone());
|
||||
|
||||
// let ret1 = config.find_account_by_name(Some("One")).unwrap();
|
||||
// let ret2 = config.find_account_by_name(Some("Two")).unwrap();
|
||||
|
||||
// assert_eq!(*ret1, account1);
|
||||
// assert_eq!(*ret2, account2);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn test_address() {
|
||||
// let config = get_config();
|
||||
|
||||
// let account1 = Account::new(None, "one@mail.com");
|
||||
// let account2 = Account::new(Some("Two"), "two@mail.com");
|
||||
// let account3 = Account::new(Some("TL;DR"), "three@mail.com");
|
||||
// let account4 = Account::new(Some("TL,DR"), "lol@mail.com");
|
||||
// let account5 = Account::new(Some("TL:DR"), "rofl@mail.com");
|
||||
// let account6 = Account::new(Some("TL.DR"), "rust@mail.com");
|
||||
|
||||
// assert_eq!(&config.address(&account1), "Config Name <one@mail.com>");
|
||||
// assert_eq!(&config.address(&account2), "Two <two@mail.com>");
|
||||
// assert_eq!(&config.address(&account3), "\"TL;DR\" <three@mail.com>");
|
||||
// assert_eq!(&config.address(&account4), "\"TL,DR\" <lol@mail.com>");
|
||||
// assert_eq!(&config.address(&account5), "\"TL:DR\" <rofl@mail.com>");
|
||||
// assert_eq!(&config.address(&account6), "\"TL.DR\" <rust@mail.com>");
|
||||
// }
|
||||
// }
|
3
src/domain/config/mod.rs
Normal file
3
src/domain/config/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
//! Modules related to the user's configuration.
|
||||
|
||||
pub mod entity;
|
|
@ -1,3 +1,5 @@
|
|||
//! Domain-specific modules.
|
||||
|
||||
pub mod account;
|
||||
pub mod config;
|
||||
pub mod smtp;
|
||||
|
|
|
@ -5,9 +5,9 @@ use lettre::{
|
|||
Transport,
|
||||
};
|
||||
|
||||
use crate::config::model::Account;
|
||||
use crate::domain::account::entity::Account;
|
||||
|
||||
pub trait SMTPServiceInterface<'a> {
|
||||
pub trait SMTPServiceInterface {
|
||||
fn send(&self, msg: &lettre::Message) -> Result<()>;
|
||||
}
|
||||
|
||||
|
@ -16,24 +16,24 @@ pub struct SMTPService<'a> {
|
|||
}
|
||||
|
||||
impl<'a> SMTPService<'a> {
|
||||
pub fn init(account: &'a Account) -> Self {
|
||||
Self { account }
|
||||
pub fn new(account: &'a Account) -> Result<Self> {
|
||||
Ok(Self { account })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SMTPServiceInterface<'a> for SMTPService<'a> {
|
||||
impl<'a> SMTPServiceInterface for SMTPService<'a> {
|
||||
fn send(&self, msg: &lettre::Message) -> Result<()> {
|
||||
let smtp_relay = if self.account.smtp_starttls() {
|
||||
let smtp_relay = if self.account.smtp_starttls {
|
||||
SmtpTransport::starttls_relay
|
||||
} else {
|
||||
SmtpTransport::relay
|
||||
};
|
||||
|
||||
let tls = TlsParameters::builder(self.account.smtp_host.to_string())
|
||||
.dangerous_accept_invalid_hostnames(self.account.smtp_insecure())
|
||||
.dangerous_accept_invalid_certs(self.account.smtp_insecure())
|
||||
.dangerous_accept_invalid_hostnames(self.account.smtp_insecure)
|
||||
.dangerous_accept_invalid_certs(self.account.smtp_insecure)
|
||||
.build()?;
|
||||
let tls = if self.account.smtp_starttls() {
|
||||
let tls = if self.account.smtp_starttls {
|
||||
Tls::Required(tls)
|
||||
} else {
|
||||
Tls::Wrapper(tls)
|
||||
|
|
|
@ -2,7 +2,10 @@ use anyhow::Result;
|
|||
use clap;
|
||||
use log::debug;
|
||||
|
||||
use crate::{ctx::Ctx, flag::model::Flags, imap::model::ImapConnector, msg::cli::uid_arg};
|
||||
use crate::{
|
||||
ctx::Ctx, domain::account::entity::Account, flag::model::Flags, imap::model::ImapConnector,
|
||||
msg::cli::uid_arg,
|
||||
};
|
||||
|
||||
fn flags_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||
clap::Arg::with_name("flags")
|
||||
|
@ -36,7 +39,7 @@ pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
|||
)]
|
||||
}
|
||||
|
||||
pub fn matches(ctx: &Ctx) -> Result<bool> {
|
||||
pub fn matches(ctx: &Ctx, account: &Account) -> Result<bool> {
|
||||
if let Some(matches) = ctx.arg_matches.subcommand_matches("set") {
|
||||
debug!("set command matched");
|
||||
|
||||
|
@ -47,7 +50,7 @@ pub fn matches(ctx: &Ctx) -> Result<bool> {
|
|||
debug!("flags: {}", flags);
|
||||
let flags = Flags::from(flags);
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
imap_conn.set_flags(&ctx.mbox, uid, flags)?;
|
||||
|
||||
imap_conn.logout();
|
||||
|
@ -64,7 +67,7 @@ pub fn matches(ctx: &Ctx) -> Result<bool> {
|
|||
debug!("flags: {}", flags);
|
||||
let flags = Flags::from(flags);
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
imap_conn.add_flags(&ctx.mbox, uid, flags)?;
|
||||
|
||||
imap_conn.logout();
|
||||
|
@ -81,7 +84,7 @@ pub fn matches(ctx: &Ctx) -> Result<bool> {
|
|||
debug!("flags: {}", flags);
|
||||
let flags = Flags::from(flags);
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
imap_conn.remove_flags(&ctx.mbox, uid, flags)?;
|
||||
|
||||
imap_conn.logout();
|
||||
|
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use clap;
|
||||
use log::debug;
|
||||
|
||||
use crate::{ctx::Ctx, imap::model::ImapConnector};
|
||||
use crate::{ctx::Ctx, domain::account::entity::Account, imap::model::ImapConnector};
|
||||
|
||||
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||
vec![
|
||||
|
@ -30,14 +30,14 @@ pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
|||
]
|
||||
}
|
||||
|
||||
pub fn matches(ctx: &Ctx) -> Result<bool> {
|
||||
pub fn matches(ctx: &Ctx, account: &Account) -> Result<bool> {
|
||||
if let Some(matches) = ctx.arg_matches.subcommand_matches("notify") {
|
||||
debug!("notify command matched");
|
||||
|
||||
let keepalive = clap::value_t_or_exit!(matches.value_of("keepalive"), u64);
|
||||
debug!("keepalive: {}", &keepalive);
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
imap_conn.notify(&ctx, keepalive)?;
|
||||
|
||||
imap_conn.logout();
|
||||
|
@ -50,7 +50,7 @@ pub fn matches(ctx: &Ctx) -> Result<bool> {
|
|||
let keepalive = clap::value_t_or_exit!(matches.value_of("keepalive"), u64);
|
||||
debug!("keepalive: {}", &keepalive);
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
imap_conn.watch(&ctx, keepalive)?;
|
||||
|
||||
imap_conn.logout();
|
||||
|
|
|
@ -4,7 +4,7 @@ use log::{debug, trace};
|
|||
use native_tls::{self, TlsConnector, TlsStream};
|
||||
use std::{collections::HashSet, convert::TryFrom, iter::FromIterator, net::TcpStream};
|
||||
|
||||
use crate::{config::model::Account, ctx::Ctx, flag::model::Flags, msg::model::Msg};
|
||||
use crate::{ctx::Ctx, domain::account::entity::Account, flag::model::Flags, msg::model::Msg};
|
||||
|
||||
/// A little helper function to create a similiar error output. (to avoid duplicated code)
|
||||
fn format_err_msg(description: &str, account: &Account) -> String {
|
||||
|
@ -41,16 +41,15 @@ impl<'a> ImapConnector<'a> {
|
|||
/// to the server ;)
|
||||
pub fn new(account: &'a Account) -> Result<Self> {
|
||||
debug!("create TLS builder");
|
||||
let insecure = account.imap_insecure();
|
||||
let ssl_conn = TlsConnector::builder()
|
||||
.danger_accept_invalid_certs(insecure)
|
||||
.danger_accept_invalid_hostnames(insecure)
|
||||
.danger_accept_invalid_certs(account.imap_insecure)
|
||||
.danger_accept_invalid_hostnames(account.imap_insecure)
|
||||
.build()
|
||||
.context(format_err_msg("cannot create TLS connector", account))?;
|
||||
|
||||
debug!("create client");
|
||||
let mut client_builder = imap::ClientBuilder::new(&account.imap_host, account.imap_port);
|
||||
if account.imap_starttls() {
|
||||
if account.imap_starttls {
|
||||
debug!("enable STARTTLS");
|
||||
client_builder.starttls();
|
||||
}
|
||||
|
@ -260,7 +259,8 @@ impl<'a> ImapConnector<'a> {
|
|||
})
|
||||
})
|
||||
.context("cannot start the idle mode")?;
|
||||
ctx.config.exec_watch_cmds(&ctx.account)?;
|
||||
// FIXME
|
||||
// ctx.config.exec_watch_cmds(&ctx.account)?;
|
||||
debug!("end loop");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,8 +42,5 @@ pub mod msg;
|
|||
/// Handles the output. For example the JSON and HTML output.
|
||||
pub mod output;
|
||||
|
||||
/// This module takes care for sending your mails!
|
||||
pub mod smtp;
|
||||
|
||||
pub mod domain;
|
||||
pub mod ui;
|
||||
|
|
66
src/main.rs
66
src/main.rs
|
@ -1,16 +1,15 @@
|
|||
use anyhow::Result;
|
||||
use clap::{self, ArgMatches};
|
||||
use clap;
|
||||
use env_logger;
|
||||
use log::{debug, trace};
|
||||
use std::{env, path::PathBuf};
|
||||
use url::{self, Url};
|
||||
use std::{convert::TryFrom, env, path::PathBuf};
|
||||
|
||||
use himalaya::{
|
||||
comp,
|
||||
config::{cli::config_args, model::Config},
|
||||
config::cli::config_args,
|
||||
ctx::Ctx,
|
||||
domain, flag, imap, mbox,
|
||||
msg::{self, cli::msg_matches_mailto},
|
||||
domain::{account::entity::Account, config::entity::Config, smtp::service::SMTPService},
|
||||
flag, imap, mbox, msg,
|
||||
output::{cli::output_args, model::Output},
|
||||
};
|
||||
|
||||
|
@ -35,20 +34,20 @@ fn main() -> Result<()> {
|
|||
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "off"),
|
||||
);
|
||||
|
||||
let raw_args: Vec<String> = env::args().collect();
|
||||
// let raw_args: Vec<String> = env::args().collect();
|
||||
|
||||
// This is used if you click on a mailaddress in the webbrowser
|
||||
if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
|
||||
let config = Config::new(None)?;
|
||||
let account = config.find_account_by_name(None)?.clone();
|
||||
let output = Output::new("plain");
|
||||
let mbox = "INBOX";
|
||||
let arg_matches = ArgMatches::default();
|
||||
let app = Ctx::new(config, account, output, mbox, arg_matches);
|
||||
let url = Url::parse(&raw_args[1])?;
|
||||
let smtp = domain::smtp::service::SMTPService::init(&app.account);
|
||||
return Ok(msg_matches_mailto(&app, &url, smtp)?);
|
||||
}
|
||||
// // This is used if you click on a mailaddress in the webbrowser
|
||||
// if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
|
||||
// let config = Config::new(None)?;
|
||||
// let account = config.find_account_by_name(None)?.clone();
|
||||
// let output = Output::new("plain");
|
||||
// let mbox = "INBOX";
|
||||
// let arg_matches = ArgMatches::default();
|
||||
// let app = Ctx::new(config, output, mbox, arg_matches);
|
||||
// let url = Url::parse(&raw_args[1])?;
|
||||
// let smtp = domain::smtp::service::SMTPService::new(&app.account);
|
||||
// return Ok(msg_matches_mailto(&app, &url, smtp)?);
|
||||
// }
|
||||
|
||||
let args = parse_args();
|
||||
let arg_matches = args.get_matches();
|
||||
|
@ -63,27 +62,32 @@ fn main() -> Result<()> {
|
|||
|
||||
debug!("init config");
|
||||
|
||||
let custom_config: Option<PathBuf> = arg_matches.value_of("config").map(|s| s.into());
|
||||
debug!("custom config path: {:?}", custom_config);
|
||||
let config = Config::new(custom_config)?;
|
||||
let config_path: PathBuf = arg_matches
|
||||
.value_of("config")
|
||||
.map(|s| s.into())
|
||||
.unwrap_or(Config::path()?);
|
||||
debug!("config path: {:?}", config_path);
|
||||
|
||||
let config = Config::try_from(config_path.clone())?;
|
||||
trace!("config: {:?}", config);
|
||||
|
||||
let account_name = arg_matches.value_of("account");
|
||||
debug!("init account: {}", account_name.unwrap_or("default"));
|
||||
let account = config.find_account_by_name(account_name)?.clone();
|
||||
let account = Account::try_from((&config, account_name))?;
|
||||
let smtp_service = SMTPService::new(&account)?;
|
||||
debug!("account name: {}", account_name.unwrap_or("default"));
|
||||
trace!("account: {:?}", account);
|
||||
|
||||
let mbox = arg_matches.value_of("mailbox").unwrap().to_string();
|
||||
debug!("mailbox: {}", mbox);
|
||||
|
||||
debug!("begin matching");
|
||||
let ctx = Ctx::new(config, output, mbox, arg_matches);
|
||||
trace!("context: {:?}", ctx);
|
||||
|
||||
let app = Ctx::new(config, account, output, mbox, arg_matches);
|
||||
let smtp = domain::smtp::service::SMTPService::init(&app.account);
|
||||
let _matched = mbox::cli::matches(&app)?
|
||||
|| flag::cli::matches(&app)?
|
||||
|| imap::cli::matches(&app)?
|
||||
|| msg::cli::matches(&app, smtp)?;
|
||||
debug!("begin matching");
|
||||
let _matched = mbox::cli::matches(&ctx, &account)?
|
||||
|| flag::cli::matches(&ctx, &account)?
|
||||
|| imap::cli::matches(&ctx, &account)?
|
||||
|| msg::cli::matches(&ctx, &account, smtp_service)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ use anyhow::Result;
|
|||
use clap;
|
||||
use log::{debug, trace};
|
||||
|
||||
use crate::{ctx::Ctx, imap::model::ImapConnector, mbox::model::Mboxes};
|
||||
use crate::{
|
||||
ctx::Ctx, domain::account::entity::Account, imap::model::ImapConnector, mbox::model::Mboxes,
|
||||
};
|
||||
|
||||
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||
vec![clap::SubCommand::with_name("mailboxes")
|
||||
|
@ -10,11 +12,11 @@ pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
|||
.about("Lists all mailboxes")]
|
||||
}
|
||||
|
||||
pub fn matches(ctx: &Ctx) -> Result<bool> {
|
||||
pub fn matches(ctx: &Ctx, account: &Account) -> Result<bool> {
|
||||
if let Some(_) = ctx.arg_matches.subcommand_matches("mailboxes") {
|
||||
debug!("mailboxes command matched");
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let names = imap_conn.list_mboxes()?;
|
||||
let mboxes = Mboxes::from(&names);
|
||||
debug!("found {} mailboxes", mboxes.0.len());
|
||||
|
|
150
src/msg/cli.rs
150
src/msg/cli.rs
|
@ -19,7 +19,11 @@ use super::{
|
|||
model::{Msg, MsgSerialized, Msgs},
|
||||
};
|
||||
use crate::{
|
||||
ctx::Ctx, domain::smtp, flag::model::Flags, imap::model::ImapConnector, input,
|
||||
ctx::Ctx,
|
||||
domain::{account::entity::Account, smtp},
|
||||
flag::model::Flags,
|
||||
imap::model::ImapConnector,
|
||||
input,
|
||||
mbox::cli::mbox_target_arg,
|
||||
};
|
||||
|
||||
|
@ -123,26 +127,27 @@ pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
|||
]
|
||||
}
|
||||
|
||||
pub fn matches<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||
pub fn matches<SMTP: smtp::service::SMTPServiceInterface>(
|
||||
ctx: &Ctx,
|
||||
account: &Account,
|
||||
smtp: SMTP,
|
||||
) -> Result<bool> {
|
||||
match ctx.arg_matches.subcommand() {
|
||||
("attachments", Some(matches)) => msg_matches_attachments(ctx, matches),
|
||||
("copy", Some(matches)) => msg_matches_copy(ctx, matches),
|
||||
("delete", Some(matches)) => msg_matches_delete(ctx, matches),
|
||||
("forward", Some(matches)) => msg_matches_forward(ctx, matches, smtp),
|
||||
("move", Some(matches)) => msg_matches_move(ctx, matches),
|
||||
("read", Some(matches)) => msg_matches_read(ctx, matches),
|
||||
("reply", Some(matches)) => msg_matches_reply(ctx, matches, smtp),
|
||||
("save", Some(matches)) => msg_matches_save(ctx, matches),
|
||||
("search", Some(matches)) => msg_matches_search(ctx, matches),
|
||||
("send", Some(matches)) => msg_matches_send(ctx, matches, smtp),
|
||||
("write", Some(matches)) => msg_matches_write(ctx, matches, smtp),
|
||||
("attachments", Some(matches)) => msg_matches_attachments(&ctx, &account, &matches),
|
||||
("copy", Some(matches)) => msg_matches_copy(&ctx, &account, &matches),
|
||||
("delete", Some(matches)) => msg_matches_delete(&ctx, &account, &matches),
|
||||
("forward", Some(matches)) => msg_matches_forward(&ctx, &account, &matches, smtp),
|
||||
("move", Some(matches)) => msg_matches_move(&ctx, &account, &matches),
|
||||
("read", Some(matches)) => msg_matches_read(&ctx, &account, &matches),
|
||||
("reply", Some(matches)) => msg_matches_reply(&ctx, &account, &matches, smtp),
|
||||
("save", Some(matches)) => msg_matches_save(&ctx, &account, matches),
|
||||
("search", Some(matches)) => msg_matches_search(&ctx, &account, &matches),
|
||||
("send", Some(matches)) => msg_matches_send(&ctx, &account, &matches, smtp),
|
||||
("write", Some(matches)) => msg_matches_write(&ctx, &account, &matches, smtp),
|
||||
|
||||
("template", Some(matches)) => Ok(msg_matches_tpl(ctx, matches)?),
|
||||
("list", opt_matches) => msg_matches_list(ctx, opt_matches),
|
||||
(_other, opt_matches) => msg_matches_list(ctx, opt_matches),
|
||||
("template", Some(matches)) => Ok(msg_matches_tpl(&ctx, &account, &matches)?),
|
||||
("list", opt_matches) => msg_matches_list(&ctx, &account, opt_matches),
|
||||
(_other, opt_matches) => msg_matches_list(&ctx, &account, opt_matches),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,12 +244,16 @@ fn tpl_args<'a>() -> Vec<clap::Arg<'a, 'a>> {
|
|||
]
|
||||
}
|
||||
|
||||
fn msg_matches_list(ctx: &Ctx, opt_matches: Option<&clap::ArgMatches>) -> Result<bool> {
|
||||
fn msg_matches_list(
|
||||
ctx: &Ctx,
|
||||
account: &Account,
|
||||
opt_matches: Option<&clap::ArgMatches>,
|
||||
) -> Result<bool> {
|
||||
debug!("list command matched");
|
||||
|
||||
let page_size: usize = opt_matches
|
||||
.and_then(|matches| matches.value_of("page-size").and_then(|s| s.parse().ok()))
|
||||
.unwrap_or_else(|| ctx.config.default_page_size(&ctx.account));
|
||||
.unwrap_or(account.default_page_size);
|
||||
debug!("page size: {:?}", page_size);
|
||||
let page: usize = opt_matches
|
||||
.and_then(|matches| matches.value_of("page").unwrap().parse().ok())
|
||||
|
@ -252,7 +261,7 @@ fn msg_matches_list(ctx: &Ctx, opt_matches: Option<&clap::ArgMatches>) -> Result
|
|||
.unwrap_or_default();
|
||||
debug!("page: {}", &page);
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let msgs = imap_conn.list_msgs(&ctx.mbox, &page_size, &page)?;
|
||||
let msgs = if let Some(ref fetches) = msgs {
|
||||
Msgs::try_from(fetches)?
|
||||
|
@ -268,13 +277,13 @@ fn msg_matches_list(ctx: &Ctx, opt_matches: Option<&clap::ArgMatches>) -> Result
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn msg_matches_search(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
fn msg_matches_search(ctx: &Ctx, account: &Account, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
debug!("search command matched");
|
||||
|
||||
let page_size: usize = matches
|
||||
.value_of("page-size")
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(ctx.config.default_page_size(&ctx.account));
|
||||
.unwrap_or(account.default_page_size);
|
||||
debug!("page size: {}", &page_size);
|
||||
let page: usize = matches
|
||||
.value_of("page")
|
||||
|
@ -310,7 +319,7 @@ fn msg_matches_search(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
.join(" ");
|
||||
debug!("query: {}", &page);
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let msgs = imap_conn.search_msgs(&ctx.mbox, &query, &page_size, &page)?;
|
||||
let msgs = if let Some(ref fetches) = msgs {
|
||||
Msgs::try_from(fetches)?
|
||||
|
@ -324,7 +333,7 @@ fn msg_matches_search(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn msg_matches_read(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
fn msg_matches_read(ctx: &Ctx, account: &Account, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
debug!("read command matched");
|
||||
|
||||
let uid = matches.value_of("uid").unwrap();
|
||||
|
@ -334,7 +343,7 @@ fn msg_matches_read(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
let raw = matches.is_present("raw");
|
||||
debug!("raw: {}", raw);
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||
|
||||
if raw {
|
||||
|
@ -346,14 +355,18 @@ fn msg_matches_read(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn msg_matches_attachments(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
fn msg_matches_attachments(
|
||||
ctx: &Ctx,
|
||||
account: &Account,
|
||||
matches: &clap::ArgMatches,
|
||||
) -> Result<bool> {
|
||||
debug!("attachments command matched");
|
||||
|
||||
let uid = matches.value_of("uid").unwrap();
|
||||
debug!("uid: {}", &uid);
|
||||
|
||||
// get the msg and than it's attachments
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||
let attachments = msg.attachments.clone();
|
||||
|
||||
|
@ -366,12 +379,8 @@ fn msg_matches_attachments(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool
|
|||
// Iterate through all attachments and download them to the download
|
||||
// directory of the account.
|
||||
for attachment in &attachments {
|
||||
let filepath = ctx
|
||||
.config
|
||||
.downloads_filepath(&ctx.account, &attachment.filename);
|
||||
|
||||
let filepath = account.downloads_dir.join(&attachment.filename);
|
||||
debug!("downloading {}…", &attachment.filename);
|
||||
|
||||
fs::write(&filepath, &attachment.body_raw)
|
||||
.with_context(|| format!("cannot save attachment {:?}", filepath))?;
|
||||
}
|
||||
|
@ -390,19 +399,20 @@ fn msg_matches_attachments(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn msg_matches_write<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||
fn msg_matches_write<'a, SMTP: smtp::service::SMTPServiceInterface>(
|
||||
ctx: &Ctx,
|
||||
account: &Account,
|
||||
matches: &clap::ArgMatches,
|
||||
smtp: SMTP,
|
||||
) -> Result<bool> {
|
||||
debug!("write command matched");
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
|
||||
// create the new msg
|
||||
// TODO: Make the header starting customizeable like from template
|
||||
let mut msg = Msg::new_with_headers(
|
||||
&ctx,
|
||||
&account,
|
||||
Headers {
|
||||
subject: Some(String::new()),
|
||||
to: Vec::new(),
|
||||
|
@ -428,22 +438,23 @@ fn msg_matches_write<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn msg_matches_reply<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||
fn msg_matches_reply<'a, SMTP: smtp::service::SMTPServiceInterface>(
|
||||
ctx: &Ctx,
|
||||
account: &Account,
|
||||
matches: &clap::ArgMatches,
|
||||
smtp: SMTP,
|
||||
) -> Result<bool> {
|
||||
debug!("reply command matched");
|
||||
|
||||
// -- Preparations --
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let uid = matches.value_of("uid").unwrap();
|
||||
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||
|
||||
debug!("uid: {}", uid);
|
||||
|
||||
// Change the msg to a reply-msg.
|
||||
msg.change_to_reply(&ctx, matches.is_present("reply-all"))?;
|
||||
msg.change_to_reply(&account, matches.is_present("reply-all"))?;
|
||||
|
||||
// Apply the given attachments to the reply-msg.
|
||||
let attachments: Vec<&str> = matches
|
||||
|
@ -462,14 +473,15 @@ fn msg_matches_reply<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn msg_matches_mailto<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||
pub fn msg_matches_mailto<'a, SMTP: smtp::service::SMTPServiceInterface>(
|
||||
ctx: &Ctx,
|
||||
account: &Account,
|
||||
url: &Url,
|
||||
smtp: SMTP,
|
||||
) -> Result<()> {
|
||||
debug!("mailto command matched");
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
|
||||
let mut cc = Vec::new();
|
||||
let mut bcc = Vec::new();
|
||||
|
@ -495,17 +507,17 @@ pub fn msg_matches_mailto<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
|||
}
|
||||
|
||||
let headers = Headers {
|
||||
from: vec![ctx.config.address(&ctx.account)],
|
||||
from: vec![account.address()],
|
||||
to: vec![url.path().to_string()],
|
||||
encoding: ContentTransferEncoding::Base64,
|
||||
bcc: Some(bcc),
|
||||
cc: Some(cc),
|
||||
signature: ctx.config.signature(&ctx.account),
|
||||
signature: Some(account.signature.to_owned()),
|
||||
subject: Some(subject.into()),
|
||||
..Headers::default()
|
||||
};
|
||||
|
||||
let mut msg = Msg::new_with_headers(&ctx, headers);
|
||||
let mut msg = Msg::new_with_headers(&account, headers);
|
||||
msg.body = Body::new_with_text(body);
|
||||
msg_interaction(&ctx, &mut msg, &mut imap_conn, smtp)?;
|
||||
|
||||
|
@ -513,22 +525,23 @@ pub fn msg_matches_mailto<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn msg_matches_forward<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||
fn msg_matches_forward<'a, SMTP: smtp::service::SMTPServiceInterface>(
|
||||
ctx: &Ctx,
|
||||
account: &Account,
|
||||
matches: &clap::ArgMatches,
|
||||
smtp: SMTP,
|
||||
) -> Result<bool> {
|
||||
debug!("forward command matched");
|
||||
|
||||
// fetch the msg
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let uid = matches.value_of("uid").unwrap();
|
||||
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||
|
||||
debug!("uid: {}", uid);
|
||||
|
||||
// prepare to forward it
|
||||
msg.change_to_forwarding(&ctx);
|
||||
msg.change_to_forwarding(&account);
|
||||
|
||||
let attachments: Vec<&str> = matches
|
||||
.values_of("attachments")
|
||||
|
@ -548,11 +561,11 @@ fn msg_matches_forward<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn msg_matches_copy(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
fn msg_matches_copy(ctx: &Ctx, account: &Account, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
debug!("copy command matched");
|
||||
|
||||
// fetch the message to be copyied
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let uid = matches.value_of("uid").unwrap();
|
||||
let target = matches.value_of("target").unwrap();
|
||||
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||
|
@ -576,11 +589,11 @@ fn msg_matches_copy(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn msg_matches_move(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
fn msg_matches_move(ctx: &Ctx, account: &Account, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
debug!("move command matched");
|
||||
|
||||
// fetch the msg which should be moved
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let uid = matches.value_of("uid").unwrap();
|
||||
let target = matches.value_of("target").unwrap();
|
||||
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||
|
@ -607,10 +620,10 @@ fn msg_matches_move(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn msg_matches_delete(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
fn msg_matches_delete(ctx: &Ctx, account: &Account, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
debug!("delete command matched");
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
|
||||
// remove the message according to its UID
|
||||
let uid = matches.value_of("uid").unwrap();
|
||||
|
@ -626,14 +639,15 @@ fn msg_matches_delete(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn msg_matches_send<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||
fn msg_matches_send<'a, SMTP: smtp::service::SMTPServiceInterface>(
|
||||
ctx: &Ctx,
|
||||
account: &Account,
|
||||
matches: &clap::ArgMatches,
|
||||
smtp: SMTP,
|
||||
) -> Result<bool> {
|
||||
debug!("send command matched");
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
|
||||
let msg = if atty::is(Stream::Stdin) || ctx.output.is_json() {
|
||||
matches
|
||||
|
@ -667,10 +681,10 @@ fn msg_matches_send<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn msg_matches_save(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
fn msg_matches_save(ctx: &Ctx, account: &Account, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
debug!("save command matched");
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let msg: &str = matches.value_of("message").unwrap();
|
||||
|
||||
let mut msg = Msg::try_from(msg)?;
|
||||
|
@ -683,11 +697,11 @@ fn msg_matches_save(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn msg_matches_tpl(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
pub fn msg_matches_tpl(ctx: &Ctx, account: &Account, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
match matches.subcommand() {
|
||||
("new", Some(matches)) => tpl_matches_new(ctx, matches),
|
||||
("reply", Some(matches)) => tpl_matches_reply(ctx, matches),
|
||||
("forward", Some(matches)) => tpl_matches_forward(ctx, matches),
|
||||
("new", Some(matches)) => tpl_matches_new(&ctx, &account, matches),
|
||||
("reply", Some(matches)) => tpl_matches_reply(&ctx, &account, matches),
|
||||
("forward", Some(matches)) => tpl_matches_forward(&ctx, &account, matches),
|
||||
|
||||
// TODO: find a way to show the help message for template subcommand
|
||||
_ => Err(anyhow!("Subcommand not found")),
|
||||
|
@ -784,10 +798,10 @@ fn override_msg_with_args(msg: &mut Msg, matches: &clap::ArgMatches) {
|
|||
msg.body = body;
|
||||
}
|
||||
|
||||
fn tpl_matches_new(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
fn tpl_matches_new(ctx: &Ctx, account: &Account, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
debug!("new command matched");
|
||||
|
||||
let mut msg = Msg::new(&ctx);
|
||||
let mut msg = Msg::new(&account);
|
||||
|
||||
override_msg_with_args(&mut msg, &matches);
|
||||
|
||||
|
@ -797,16 +811,16 @@ fn tpl_matches_new(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn tpl_matches_reply(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
fn tpl_matches_reply(ctx: &Ctx, account: &Account, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
debug!("reply command matched");
|
||||
|
||||
let uid = matches.value_of("uid").unwrap();
|
||||
debug!("uid: {}", uid);
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||
|
||||
msg.change_to_reply(&ctx, matches.is_present("reply-all"))?;
|
||||
msg.change_to_reply(&account, matches.is_present("reply-all"))?;
|
||||
|
||||
override_msg_with_args(&mut msg, &matches);
|
||||
trace!("Message: {:?}", msg);
|
||||
|
@ -815,15 +829,15 @@ fn tpl_matches_reply(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn tpl_matches_forward(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
fn tpl_matches_forward(ctx: &Ctx, account: &Account, matches: &clap::ArgMatches) -> Result<bool> {
|
||||
debug!("forward command matched");
|
||||
|
||||
let uid = matches.value_of("uid").unwrap();
|
||||
debug!("uid: {}", uid);
|
||||
|
||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||
let mut imap_conn = ImapConnector::new(&account)?;
|
||||
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||
msg.change_to_forwarding(&ctx);
|
||||
msg.change_to_forwarding(&account);
|
||||
|
||||
override_msg_with_args(&mut msg, &matches);
|
||||
|
||||
|
@ -835,7 +849,7 @@ fn tpl_matches_forward(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
|||
|
||||
/// This function opens the prompt to do some actions to the msg like sending, editing it again and
|
||||
/// so on.
|
||||
fn msg_interaction<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||
fn msg_interaction<SMTP: smtp::service::SMTPServiceInterface>(
|
||||
ctx: &Ctx,
|
||||
msg: &mut Msg,
|
||||
imap_conn: &mut ImapConnector,
|
||||
|
|
958
src/msg/model.rs
958
src/msg/model.rs
File diff suppressed because it is too large
Load diff
35
src/smtp.rs
35
src/smtp.rs
|
@ -1,35 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use lettre::{
|
||||
self,
|
||||
transport::{smtp::client::Tls, smtp::client::TlsParameters, smtp::SmtpTransport},
|
||||
Transport,
|
||||
};
|
||||
|
||||
use crate::config::model::Account;
|
||||
|
||||
pub fn send(account: &Account, msg: &lettre::Message) -> Result<()> {
|
||||
let smtp_relay = if account.smtp_starttls() {
|
||||
SmtpTransport::starttls_relay
|
||||
} else {
|
||||
SmtpTransport::relay
|
||||
};
|
||||
|
||||
let tls = TlsParameters::builder(account.smtp_host.to_string())
|
||||
.dangerous_accept_invalid_hostnames(account.smtp_insecure())
|
||||
.dangerous_accept_invalid_certs(account.smtp_insecure())
|
||||
.build()?;
|
||||
let tls = if account.smtp_starttls() {
|
||||
Tls::Required(tls)
|
||||
} else {
|
||||
Tls::Wrapper(tls)
|
||||
};
|
||||
|
||||
smtp_relay(&account.smtp_host)?
|
||||
.port(account.smtp_port)
|
||||
.tls(tls)
|
||||
.credentials(account.smtp_creds()?)
|
||||
.build()
|
||||
.send(msg)?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,149 +1,150 @@
|
|||
use std::convert::TryFrom;
|
||||
// FIXME: fix tests
|
||||
// use std::convert::TryFrom;
|
||||
|
||||
use himalaya::{
|
||||
config::model::Account, flag::model::Flags, imap::model::ImapConnector, mbox::model::Mboxes,
|
||||
msg::model::Msgs, smtp,
|
||||
};
|
||||
// use himalaya::{
|
||||
// domain::account::entity::Account, flag::model::Flags, imap::model::ImapConnector,
|
||||
// mbox::model::Mboxes, msg::model::Msgs,
|
||||
// };
|
||||
|
||||
use imap::types::Flag;
|
||||
// use imap::types::Flag;
|
||||
|
||||
use lettre::message::SinglePart;
|
||||
use lettre::Message;
|
||||
// use lettre::message::SinglePart;
|
||||
// use lettre::Message;
|
||||
|
||||
fn get_account(addr: &str) -> Account {
|
||||
Account {
|
||||
name: None,
|
||||
downloads_dir: None,
|
||||
signature_delimiter: None,
|
||||
signature: None,
|
||||
default_page_size: None,
|
||||
default: Some(true),
|
||||
email: addr.into(),
|
||||
watch_cmds: None,
|
||||
imap_host: String::from("localhost"),
|
||||
imap_port: 3993,
|
||||
imap_starttls: Some(false),
|
||||
imap_insecure: Some(true),
|
||||
imap_login: addr.into(),
|
||||
imap_passwd_cmd: String::from("echo 'password'"),
|
||||
smtp_host: String::from("localhost"),
|
||||
smtp_port: 3465,
|
||||
smtp_starttls: Some(false),
|
||||
smtp_insecure: Some(true),
|
||||
smtp_login: addr.into(),
|
||||
smtp_passwd_cmd: String::from("echo 'password'"),
|
||||
}
|
||||
}
|
||||
// fn get_account(addr: &str) -> Account {
|
||||
// Account {
|
||||
// name: None,
|
||||
// downloads_dir: None,
|
||||
// signature_delimiter: None,
|
||||
// signature: None,
|
||||
// default_page_size: None,
|
||||
// default: Some(true),
|
||||
// email: addr.into(),
|
||||
// watch_cmds: None,
|
||||
// imap_host: String::from("localhost"),
|
||||
// imap_port: 3993,
|
||||
// imap_starttls: Some(false),
|
||||
// imap_insecure: Some(true),
|
||||
// imap_login: addr.into(),
|
||||
// imap_passwd_cmd: String::from("echo 'password'"),
|
||||
// smtp_host: String::from("localhost"),
|
||||
// smtp_port: 3465,
|
||||
// smtp_starttls: Some(false),
|
||||
// smtp_insecure: Some(true),
|
||||
// smtp_login: addr.into(),
|
||||
// smtp_passwd_cmd: String::from("echo 'password'"),
|
||||
// }
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn mbox() {
|
||||
let account = get_account("inbox@localhost");
|
||||
let mut imap_conn = ImapConnector::new(&account).unwrap();
|
||||
let names = imap_conn.list_mboxes().unwrap();
|
||||
let mboxes: Vec<String> = Mboxes::from(&names)
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|mbox| mbox.name)
|
||||
.collect();
|
||||
assert_eq!(mboxes, vec![String::from("INBOX")]);
|
||||
imap_conn.logout();
|
||||
}
|
||||
// #[test]
|
||||
// fn mbox() {
|
||||
// let account = get_account("inbox@localhost");
|
||||
// let mut imap_conn = ImapConnector::new(&account).unwrap();
|
||||
// let names = imap_conn.list_mboxes().unwrap();
|
||||
// let mboxes: Vec<String> = Mboxes::from(&names)
|
||||
// .0
|
||||
// .into_iter()
|
||||
// .map(|mbox| mbox.name)
|
||||
// .collect();
|
||||
// assert_eq!(mboxes, vec![String::from("INBOX")]);
|
||||
// imap_conn.logout();
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn msg() {
|
||||
// Preparations
|
||||
// #[test]
|
||||
// fn msg() {
|
||||
// // Preparations
|
||||
|
||||
// Get the test-account and clean up the server.
|
||||
let account = get_account("inbox@localhost");
|
||||
// // Get the test-account and clean up the server.
|
||||
// let account = get_account("inbox@localhost");
|
||||
|
||||
// Login
|
||||
let mut imap_conn = ImapConnector::new(&account).unwrap();
|
||||
// // Login
|
||||
// let mut imap_conn = ImapConnector::new(&account).unwrap();
|
||||
|
||||
// remove all previous mails first
|
||||
let fetches = imap_conn.list_msgs("INBOX", &10, &0).unwrap();
|
||||
let msgs = if let Some(ref fetches) = fetches {
|
||||
Msgs::try_from(fetches).unwrap()
|
||||
} else {
|
||||
Msgs::new()
|
||||
};
|
||||
// // remove all previous mails first
|
||||
// let fetches = imap_conn.list_msgs("INBOX", &10, &0).unwrap();
|
||||
// let msgs = if let Some(ref fetches) = fetches {
|
||||
// Msgs::try_from(fetches).unwrap()
|
||||
// } else {
|
||||
// Msgs::new()
|
||||
// };
|
||||
|
||||
// mark all mails as deleted
|
||||
for msg in msgs.0.iter() {
|
||||
imap_conn
|
||||
.add_flags(
|
||||
"INBOX",
|
||||
&msg.get_uid().unwrap().to_string(),
|
||||
Flags::from(vec![Flag::Deleted]),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
imap_conn.expunge("INBOX").unwrap();
|
||||
// // mark all mails as deleted
|
||||
// for msg in msgs.0.iter() {
|
||||
// imap_conn
|
||||
// .add_flags(
|
||||
// "INBOX",
|
||||
// &msg.get_uid().unwrap().to_string(),
|
||||
// Flags::from(vec![Flag::Deleted]),
|
||||
// )
|
||||
// .unwrap();
|
||||
// }
|
||||
// imap_conn.expunge("INBOX").unwrap();
|
||||
|
||||
// make sure, that they are *really* deleted
|
||||
assert!(imap_conn.list_msgs("INBOX", &10, &0).unwrap().is_none());
|
||||
// // make sure, that they are *really* deleted
|
||||
// assert!(imap_conn.list_msgs("INBOX", &10, &0).unwrap().is_none());
|
||||
|
||||
// == Testing ==
|
||||
// Add messages
|
||||
let message_a = Message::builder()
|
||||
.from("sender-a@localhost".parse().unwrap())
|
||||
.to("inbox@localhost".parse().unwrap())
|
||||
.subject("Subject A")
|
||||
.singlepart(SinglePart::builder().body("Body A".as_bytes().to_vec()))
|
||||
.unwrap();
|
||||
// // == Testing ==
|
||||
// // Add messages
|
||||
// let message_a = Message::builder()
|
||||
// .from("sender-a@localhost".parse().unwrap())
|
||||
// .to("inbox@localhost".parse().unwrap())
|
||||
// .subject("Subject A")
|
||||
// .singlepart(SinglePart::builder().body("Body A".as_bytes().to_vec()))
|
||||
// .unwrap();
|
||||
|
||||
let message_b = Message::builder()
|
||||
.from("Sender B <sender-b@localhost>".parse().unwrap())
|
||||
.to("inbox@localhost".parse().unwrap())
|
||||
.subject("Subject B")
|
||||
.singlepart(SinglePart::builder().body("Body B".as_bytes().to_vec()))
|
||||
.unwrap();
|
||||
// let message_b = Message::builder()
|
||||
// .from("Sender B <sender-b@localhost>".parse().unwrap())
|
||||
// .to("inbox@localhost".parse().unwrap())
|
||||
// .subject("Subject B")
|
||||
// .singlepart(SinglePart::builder().body("Body B".as_bytes().to_vec()))
|
||||
// .unwrap();
|
||||
|
||||
smtp::send(&account, &message_a).unwrap();
|
||||
smtp::send(&account, &message_b).unwrap();
|
||||
// smtp::send(&account, &message_a).unwrap();
|
||||
// smtp::send(&account, &message_b).unwrap();
|
||||
|
||||
// -- Get the messages --
|
||||
// TODO: check non-existance of \Seen flag
|
||||
let msgs = imap_conn.list_msgs("INBOX", &10, &0).unwrap();
|
||||
let msgs = if let Some(ref fetches) = msgs {
|
||||
Msgs::try_from(fetches).unwrap()
|
||||
} else {
|
||||
Msgs::new()
|
||||
};
|
||||
// // -- Get the messages --
|
||||
// // TODO: check non-existance of \Seen flag
|
||||
// let msgs = imap_conn.list_msgs("INBOX", &10, &0).unwrap();
|
||||
// let msgs = if let Some(ref fetches) = msgs {
|
||||
// Msgs::try_from(fetches).unwrap()
|
||||
// } else {
|
||||
// Msgs::new()
|
||||
// };
|
||||
|
||||
// make sure that there are both mails which we sended
|
||||
assert_eq!(msgs.0.len(), 2);
|
||||
// // make sure that there are both mails which we sended
|
||||
// assert_eq!(msgs.0.len(), 2);
|
||||
|
||||
let msg_a = msgs
|
||||
.0
|
||||
.iter()
|
||||
.find(|msg| msg.headers.subject.clone().unwrap() == "Subject A")
|
||||
.unwrap();
|
||||
// let msg_a = msgs
|
||||
// .0
|
||||
// .iter()
|
||||
// .find(|msg| msg.headers.subject.clone().unwrap() == "Subject A")
|
||||
// .unwrap();
|
||||
|
||||
let msg_b = msgs
|
||||
.0
|
||||
.iter()
|
||||
.find(|msg| msg.headers.subject.clone().unwrap() == "Subject B")
|
||||
.unwrap();
|
||||
// let msg_b = msgs
|
||||
// .0
|
||||
// .iter()
|
||||
// .find(|msg| msg.headers.subject.clone().unwrap() == "Subject B")
|
||||
// .unwrap();
|
||||
|
||||
// -- Checkup --
|
||||
// look, if we received the correct credentials of the msgs.
|
||||
assert_eq!(
|
||||
msg_a.headers.subject.clone().unwrap_or_default(),
|
||||
"Subject A"
|
||||
);
|
||||
assert_eq!(&msg_a.headers.from[0], "sender-a@localhost");
|
||||
// // -- Checkup --
|
||||
// // look, if we received the correct credentials of the msgs.
|
||||
// assert_eq!(
|
||||
// msg_a.headers.subject.clone().unwrap_or_default(),
|
||||
// "Subject A"
|
||||
// );
|
||||
// assert_eq!(&msg_a.headers.from[0], "sender-a@localhost");
|
||||
|
||||
assert_eq!(
|
||||
msg_b.headers.subject.clone().unwrap_or_default(),
|
||||
"Subject B"
|
||||
);
|
||||
assert_eq!(&msg_b.headers.from[0], "Sender B <sender-b@localhost>");
|
||||
// assert_eq!(
|
||||
// msg_b.headers.subject.clone().unwrap_or_default(),
|
||||
// "Subject B"
|
||||
// );
|
||||
// assert_eq!(&msg_b.headers.from[0], "Sender B <sender-b@localhost>");
|
||||
|
||||
// TODO: search messages
|
||||
// TODO: read message (+ \Seen flag)
|
||||
// TODO: list message attachments
|
||||
// TODO: add/set/remove flags
|
||||
// // TODO: search messages
|
||||
// // TODO: read message (+ \Seen flag)
|
||||
// // TODO: list message attachments
|
||||
// // TODO: add/set/remove flags
|
||||
|
||||
// Logout
|
||||
imap_conn.logout();
|
||||
}
|
||||
// // Logout
|
||||
// imap_conn.logout();
|
||||
// }
|
||||
|
|
Loading…
Reference in a new issue