mirror of
https://github.com/soywod/himalaya.git
synced 2024-09-04 18:21:11 +00:00
refactor config and account system
This commit is contained in:
parent
5a9481910f
commit
979c6ef1c9
|
@ -1,2 +1 @@
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod model;
|
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
use clap;
|
use clap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{domain::config::entity::Config, output::model::Output};
|
||||||
config::model::{Account, Config},
|
|
||||||
output::model::Output,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// `Ctx` stands for `Context` and includes the most "important" structs which are used quite often
|
/// `Ctx` stands for `Context` and includes the most "important" structs which are used quite often
|
||||||
/// in this crate.
|
/// in this crate.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Ctx<'a> {
|
pub struct Ctx<'a> {
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
pub account: Account,
|
|
||||||
pub output: Output,
|
pub output: Output,
|
||||||
pub mbox: String,
|
pub mbox: String,
|
||||||
pub arg_matches: clap::ArgMatches<'a>,
|
pub arg_matches: clap::ArgMatches<'a>,
|
||||||
|
@ -19,7 +15,6 @@ pub struct Ctx<'a> {
|
||||||
impl<'a> Ctx<'a> {
|
impl<'a> Ctx<'a> {
|
||||||
pub fn new<S: ToString>(
|
pub fn new<S: ToString>(
|
||||||
config: Config,
|
config: Config,
|
||||||
account: Account,
|
|
||||||
output: Output,
|
output: Output,
|
||||||
mbox: S,
|
mbox: S,
|
||||||
arg_matches: clap::ArgMatches<'a>,
|
arg_matches: clap::ArgMatches<'a>,
|
||||||
|
@ -28,7 +23,6 @@ impl<'a> Ctx<'a> {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
account,
|
|
||||||
output,
|
output,
|
||||||
mbox,
|
mbox,
|
||||||
arg_matches,
|
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 anyhow::{anyhow, Context, Error, Result};
|
||||||
use lettre::transport::smtp::authentication::Credentials as SmtpCredentials;
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use shellexpand;
|
use shellexpand;
|
||||||
use std::{
|
use std::{collections::HashMap, convert::TryFrom, env, fs, path::PathBuf, thread};
|
||||||
collections::HashMap,
|
|
||||||
env,
|
|
||||||
fs::{self, File},
|
|
||||||
io::Read,
|
|
||||||
path::PathBuf,
|
|
||||||
thread,
|
|
||||||
};
|
|
||||||
use toml;
|
use toml;
|
||||||
|
|
||||||
use crate::output::utils::run_cmd;
|
use crate::output::utils::run_cmd;
|
||||||
|
|
||||||
const DEFAULT_PAGE_SIZE: usize = 10;
|
const DEFAULT_PAGE_SIZE: usize = 10;
|
||||||
|
|
||||||
// --- Account ---
|
#[derive(Debug, Default, Clone, PartialEq, Deserialize)]
|
||||||
/// 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)]
|
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Account {
|
pub struct Account {
|
||||||
// Override
|
// TODO: rename with `from`
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub downloads_dir: Option<PathBuf>,
|
pub downloads_dir: Option<PathBuf>,
|
||||||
pub signature_delimiter: Option<String>,
|
pub signature_delimiter: Option<String>,
|
||||||
pub signature: Option<String>,
|
pub signature: Option<String>,
|
||||||
pub default_page_size: Option<usize>,
|
pub default_page_size: Option<usize>,
|
||||||
pub watch_cmds: Option<Vec<String>>,
|
pub watch_cmds: Option<Vec<String>>,
|
||||||
|
|
||||||
// Specific
|
|
||||||
pub default: Option<bool>,
|
pub default: Option<bool>,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
|
|
||||||
pub imap_host: String,
|
pub imap_host: String,
|
||||||
pub imap_port: u16,
|
pub imap_port: u16,
|
||||||
pub imap_starttls: Option<bool>,
|
pub imap_starttls: Option<bool>,
|
||||||
pub imap_insecure: Option<bool>,
|
pub imap_insecure: Option<bool>,
|
||||||
pub imap_login: String,
|
pub imap_login: String,
|
||||||
pub imap_passwd_cmd: String,
|
pub imap_passwd_cmd: String,
|
||||||
|
|
||||||
pub smtp_host: String,
|
pub smtp_host: String,
|
||||||
pub smtp_port: u16,
|
pub smtp_port: u16,
|
||||||
pub smtp_starttls: Option<bool>,
|
pub smtp_starttls: Option<bool>,
|
||||||
|
@ -51,193 +35,13 @@ pub struct Account {
|
||||||
pub smtp_passwd_cmd: String,
|
pub smtp_passwd_cmd: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Account {
|
pub type AccountsMap = HashMap<String, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
/// Represents the whole config file.
|
||||||
#[derive(Debug, Default, Deserialize, Clone)]
|
#[derive(Debug, Default, Clone, Deserialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
// TODO: rename with `from`
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub downloads_dir: Option<PathBuf>,
|
pub downloads_dir: Option<PathBuf>,
|
||||||
pub notify_cmd: Option<String>,
|
pub notify_cmd: Option<String>,
|
||||||
|
@ -290,22 +94,13 @@ impl Config {
|
||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the config file by the given path and stores the values into the struct.
|
pub fn path() -> Result<PathBuf> {
|
||||||
pub fn new(path: Option<PathBuf>) -> Result<Self> {
|
let path = Self::path_from_xdg()
|
||||||
let path = match path {
|
|
||||||
Some(path) => path,
|
|
||||||
None => Self::path_from_xdg()
|
|
||||||
.or_else(|_| Self::path_from_xdg_alt())
|
.or_else(|_| Self::path_from_xdg_alt())
|
||||||
.or_else(|_| Self::path_from_home())
|
.or_else(|_| Self::path_from_home())
|
||||||
.context("cannot find config path")?,
|
.context("cannot find config path")?;
|
||||||
};
|
|
||||||
|
|
||||||
let mut file = File::open(path).context("cannot open config file")?;
|
Ok(path)
|
||||||
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")?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the account by the given name.
|
/// Returns the account by the given name.
|
||||||
|
@ -386,9 +181,7 @@ impl Config {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn address(&self, account: &Account) -> String {
|
pub fn address(&self, account: &Account) -> String {
|
||||||
let name = account.name.as_ref().unwrap_or(&self.name);
|
let name = account.name.as_ref().unwrap_or(&self.name);
|
||||||
|
let has_special_chars = "()<>[]:;@.,".contains(|special_char| name.contains(special_char));
|
||||||
let has_special_chars: bool =
|
|
||||||
"()<>[]:;@.,".contains(|special_char| name.contains(special_char));
|
|
||||||
|
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
format!("{}", account.email)
|
format!("{}", account.email)
|
||||||
|
@ -491,57 +284,62 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
impl TryFrom<PathBuf> for Config {
|
||||||
mod tests {
|
type Error = Error;
|
||||||
|
|
||||||
#[cfg(test)]
|
fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
|
||||||
mod config_test {
|
let file_content = fs::read_to_string(path).context("cannot read config file")?;
|
||||||
|
Ok(toml::from_str(&file_content).context("cannot parse config file")?)
|
||||||
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>");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
//! Domain-specific modules.
|
||||||
|
|
||||||
|
pub mod account;
|
||||||
|
pub mod config;
|
||||||
pub mod smtp;
|
pub mod smtp;
|
||||||
|
|
|
@ -5,9 +5,9 @@ use lettre::{
|
||||||
Transport,
|
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<()>;
|
fn send(&self, msg: &lettre::Message) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,24 +16,24 @@ pub struct SMTPService<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SMTPService<'a> {
|
impl<'a> SMTPService<'a> {
|
||||||
pub fn init(account: &'a Account) -> Self {
|
pub fn new(account: &'a Account) -> Result<Self> {
|
||||||
Self { account }
|
Ok(Self { account })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SMTPServiceInterface<'a> for SMTPService<'a> {
|
impl<'a> SMTPServiceInterface for SMTPService<'a> {
|
||||||
fn send(&self, msg: &lettre::Message) -> Result<()> {
|
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
|
SmtpTransport::starttls_relay
|
||||||
} else {
|
} else {
|
||||||
SmtpTransport::relay
|
SmtpTransport::relay
|
||||||
};
|
};
|
||||||
|
|
||||||
let tls = TlsParameters::builder(self.account.smtp_host.to_string())
|
let tls = TlsParameters::builder(self.account.smtp_host.to_string())
|
||||||
.dangerous_accept_invalid_hostnames(self.account.smtp_insecure())
|
.dangerous_accept_invalid_hostnames(self.account.smtp_insecure)
|
||||||
.dangerous_accept_invalid_certs(self.account.smtp_insecure())
|
.dangerous_accept_invalid_certs(self.account.smtp_insecure)
|
||||||
.build()?;
|
.build()?;
|
||||||
let tls = if self.account.smtp_starttls() {
|
let tls = if self.account.smtp_starttls {
|
||||||
Tls::Required(tls)
|
Tls::Required(tls)
|
||||||
} else {
|
} else {
|
||||||
Tls::Wrapper(tls)
|
Tls::Wrapper(tls)
|
||||||
|
|
|
@ -2,7 +2,10 @@ use anyhow::Result;
|
||||||
use clap;
|
use clap;
|
||||||
use log::debug;
|
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> {
|
fn flags_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||||
clap::Arg::with_name("flags")
|
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") {
|
if let Some(matches) = ctx.arg_matches.subcommand_matches("set") {
|
||||||
debug!("set command matched");
|
debug!("set command matched");
|
||||||
|
|
||||||
|
@ -47,7 +50,7 @@ pub fn matches(ctx: &Ctx) -> Result<bool> {
|
||||||
debug!("flags: {}", flags);
|
debug!("flags: {}", flags);
|
||||||
let flags = Flags::from(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.set_flags(&ctx.mbox, uid, flags)?;
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
|
@ -64,7 +67,7 @@ pub fn matches(ctx: &Ctx) -> Result<bool> {
|
||||||
debug!("flags: {}", flags);
|
debug!("flags: {}", flags);
|
||||||
let flags = Flags::from(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.add_flags(&ctx.mbox, uid, flags)?;
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
|
@ -81,7 +84,7 @@ pub fn matches(ctx: &Ctx) -> Result<bool> {
|
||||||
debug!("flags: {}", flags);
|
debug!("flags: {}", flags);
|
||||||
let flags = Flags::from(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.remove_flags(&ctx.mbox, uid, flags)?;
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
|
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use clap;
|
use clap;
|
||||||
use log::debug;
|
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>> {
|
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||||
vec![
|
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") {
|
if let Some(matches) = ctx.arg_matches.subcommand_matches("notify") {
|
||||||
debug!("notify command matched");
|
debug!("notify command matched");
|
||||||
|
|
||||||
let keepalive = clap::value_t_or_exit!(matches.value_of("keepalive"), u64);
|
let keepalive = clap::value_t_or_exit!(matches.value_of("keepalive"), u64);
|
||||||
debug!("keepalive: {}", &keepalive);
|
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.notify(&ctx, keepalive)?;
|
||||||
|
|
||||||
imap_conn.logout();
|
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);
|
let keepalive = clap::value_t_or_exit!(matches.value_of("keepalive"), u64);
|
||||||
debug!("keepalive: {}", &keepalive);
|
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.watch(&ctx, keepalive)?;
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
|
|
|
@ -4,7 +4,7 @@ use log::{debug, trace};
|
||||||
use native_tls::{self, TlsConnector, TlsStream};
|
use native_tls::{self, TlsConnector, TlsStream};
|
||||||
use std::{collections::HashSet, convert::TryFrom, iter::FromIterator, net::TcpStream};
|
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)
|
/// A little helper function to create a similiar error output. (to avoid duplicated code)
|
||||||
fn format_err_msg(description: &str, account: &Account) -> String {
|
fn format_err_msg(description: &str, account: &Account) -> String {
|
||||||
|
@ -41,16 +41,15 @@ impl<'a> ImapConnector<'a> {
|
||||||
/// to the server ;)
|
/// to the server ;)
|
||||||
pub fn new(account: &'a Account) -> Result<Self> {
|
pub fn new(account: &'a Account) -> Result<Self> {
|
||||||
debug!("create TLS builder");
|
debug!("create TLS builder");
|
||||||
let insecure = account.imap_insecure();
|
|
||||||
let ssl_conn = TlsConnector::builder()
|
let ssl_conn = TlsConnector::builder()
|
||||||
.danger_accept_invalid_certs(insecure)
|
.danger_accept_invalid_certs(account.imap_insecure)
|
||||||
.danger_accept_invalid_hostnames(insecure)
|
.danger_accept_invalid_hostnames(account.imap_insecure)
|
||||||
.build()
|
.build()
|
||||||
.context(format_err_msg("cannot create TLS connector", account))?;
|
.context(format_err_msg("cannot create TLS connector", account))?;
|
||||||
|
|
||||||
debug!("create client");
|
debug!("create client");
|
||||||
let mut client_builder = imap::ClientBuilder::new(&account.imap_host, account.imap_port);
|
let mut client_builder = imap::ClientBuilder::new(&account.imap_host, account.imap_port);
|
||||||
if account.imap_starttls() {
|
if account.imap_starttls {
|
||||||
debug!("enable STARTTLS");
|
debug!("enable STARTTLS");
|
||||||
client_builder.starttls();
|
client_builder.starttls();
|
||||||
}
|
}
|
||||||
|
@ -260,7 +259,8 @@ impl<'a> ImapConnector<'a> {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.context("cannot start the idle mode")?;
|
.context("cannot start the idle mode")?;
|
||||||
ctx.config.exec_watch_cmds(&ctx.account)?;
|
// FIXME
|
||||||
|
// ctx.config.exec_watch_cmds(&ctx.account)?;
|
||||||
debug!("end loop");
|
debug!("end loop");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,5 @@ pub mod msg;
|
||||||
/// Handles the output. For example the JSON and HTML output.
|
/// Handles the output. For example the JSON and HTML output.
|
||||||
pub mod output;
|
pub mod output;
|
||||||
|
|
||||||
/// This module takes care for sending your mails!
|
|
||||||
pub mod smtp;
|
|
||||||
|
|
||||||
pub mod domain;
|
pub mod domain;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
66
src/main.rs
66
src/main.rs
|
@ -1,16 +1,15 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{self, ArgMatches};
|
use clap;
|
||||||
use env_logger;
|
use env_logger;
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use std::{env, path::PathBuf};
|
use std::{convert::TryFrom, env, path::PathBuf};
|
||||||
use url::{self, Url};
|
|
||||||
|
|
||||||
use himalaya::{
|
use himalaya::{
|
||||||
comp,
|
comp,
|
||||||
config::{cli::config_args, model::Config},
|
config::cli::config_args,
|
||||||
ctx::Ctx,
|
ctx::Ctx,
|
||||||
domain, flag, imap, mbox,
|
domain::{account::entity::Account, config::entity::Config, smtp::service::SMTPService},
|
||||||
msg::{self, cli::msg_matches_mailto},
|
flag, imap, mbox, msg,
|
||||||
output::{cli::output_args, model::Output},
|
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"),
|
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
|
// // This is used if you click on a mailaddress in the webbrowser
|
||||||
if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
|
// if raw_args.len() > 1 && raw_args[1].starts_with("mailto:") {
|
||||||
let config = Config::new(None)?;
|
// let config = Config::new(None)?;
|
||||||
let account = config.find_account_by_name(None)?.clone();
|
// let account = config.find_account_by_name(None)?.clone();
|
||||||
let output = Output::new("plain");
|
// let output = Output::new("plain");
|
||||||
let mbox = "INBOX";
|
// let mbox = "INBOX";
|
||||||
let arg_matches = ArgMatches::default();
|
// let arg_matches = ArgMatches::default();
|
||||||
let app = Ctx::new(config, account, output, mbox, arg_matches);
|
// let app = Ctx::new(config, output, mbox, arg_matches);
|
||||||
let url = Url::parse(&raw_args[1])?;
|
// let url = Url::parse(&raw_args[1])?;
|
||||||
let smtp = domain::smtp::service::SMTPService::init(&app.account);
|
// let smtp = domain::smtp::service::SMTPService::new(&app.account);
|
||||||
return Ok(msg_matches_mailto(&app, &url, smtp)?);
|
// return Ok(msg_matches_mailto(&app, &url, smtp)?);
|
||||||
}
|
// }
|
||||||
|
|
||||||
let args = parse_args();
|
let args = parse_args();
|
||||||
let arg_matches = args.get_matches();
|
let arg_matches = args.get_matches();
|
||||||
|
@ -63,27 +62,32 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
debug!("init config");
|
debug!("init config");
|
||||||
|
|
||||||
let custom_config: Option<PathBuf> = arg_matches.value_of("config").map(|s| s.into());
|
let config_path: PathBuf = arg_matches
|
||||||
debug!("custom config path: {:?}", custom_config);
|
.value_of("config")
|
||||||
let config = Config::new(custom_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);
|
trace!("config: {:?}", config);
|
||||||
|
|
||||||
let account_name = arg_matches.value_of("account");
|
let account_name = arg_matches.value_of("account");
|
||||||
debug!("init account: {}", account_name.unwrap_or("default"));
|
let account = Account::try_from((&config, account_name))?;
|
||||||
let account = config.find_account_by_name(account_name)?.clone();
|
let smtp_service = SMTPService::new(&account)?;
|
||||||
|
debug!("account name: {}", account_name.unwrap_or("default"));
|
||||||
trace!("account: {:?}", account);
|
trace!("account: {:?}", account);
|
||||||
|
|
||||||
let mbox = arg_matches.value_of("mailbox").unwrap().to_string();
|
let mbox = arg_matches.value_of("mailbox").unwrap().to_string();
|
||||||
debug!("mailbox: {}", mbox);
|
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);
|
debug!("begin matching");
|
||||||
let smtp = domain::smtp::service::SMTPService::init(&app.account);
|
let _matched = mbox::cli::matches(&ctx, &account)?
|
||||||
let _matched = mbox::cli::matches(&app)?
|
|| flag::cli::matches(&ctx, &account)?
|
||||||
|| flag::cli::matches(&app)?
|
|| imap::cli::matches(&ctx, &account)?
|
||||||
|| imap::cli::matches(&app)?
|
|| msg::cli::matches(&ctx, &account, smtp_service)?;
|
||||||
|| msg::cli::matches(&app, smtp)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@ use anyhow::Result;
|
||||||
use clap;
|
use clap;
|
||||||
use log::{debug, trace};
|
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>> {
|
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||||
vec![clap::SubCommand::with_name("mailboxes")
|
vec![clap::SubCommand::with_name("mailboxes")
|
||||||
|
@ -10,11 +12,11 @@ pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||||
.about("Lists all mailboxes")]
|
.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") {
|
if let Some(_) = ctx.arg_matches.subcommand_matches("mailboxes") {
|
||||||
debug!("mailboxes command matched");
|
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 names = imap_conn.list_mboxes()?;
|
||||||
let mboxes = Mboxes::from(&names);
|
let mboxes = Mboxes::from(&names);
|
||||||
debug!("found {} mailboxes", mboxes.0.len());
|
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},
|
model::{Msg, MsgSerialized, Msgs},
|
||||||
};
|
};
|
||||||
use crate::{
|
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,
|
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,
|
ctx: &Ctx,
|
||||||
|
account: &Account,
|
||||||
smtp: SMTP,
|
smtp: SMTP,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
match ctx.arg_matches.subcommand() {
|
match ctx.arg_matches.subcommand() {
|
||||||
("attachments", Some(matches)) => msg_matches_attachments(ctx, matches),
|
("attachments", Some(matches)) => msg_matches_attachments(&ctx, &account, &matches),
|
||||||
("copy", Some(matches)) => msg_matches_copy(ctx, matches),
|
("copy", Some(matches)) => msg_matches_copy(&ctx, &account, &matches),
|
||||||
("delete", Some(matches)) => msg_matches_delete(ctx, matches),
|
("delete", Some(matches)) => msg_matches_delete(&ctx, &account, &matches),
|
||||||
("forward", Some(matches)) => msg_matches_forward(ctx, matches, smtp),
|
("forward", Some(matches)) => msg_matches_forward(&ctx, &account, &matches, smtp),
|
||||||
("move", Some(matches)) => msg_matches_move(ctx, matches),
|
("move", Some(matches)) => msg_matches_move(&ctx, &account, &matches),
|
||||||
("read", Some(matches)) => msg_matches_read(ctx, matches),
|
("read", Some(matches)) => msg_matches_read(&ctx, &account, &matches),
|
||||||
("reply", Some(matches)) => msg_matches_reply(ctx, matches, smtp),
|
("reply", Some(matches)) => msg_matches_reply(&ctx, &account, &matches, smtp),
|
||||||
("save", Some(matches)) => msg_matches_save(ctx, matches),
|
("save", Some(matches)) => msg_matches_save(&ctx, &account, matches),
|
||||||
("search", Some(matches)) => msg_matches_search(ctx, matches),
|
("search", Some(matches)) => msg_matches_search(&ctx, &account, &matches),
|
||||||
("send", Some(matches)) => msg_matches_send(ctx, matches, smtp),
|
("send", Some(matches)) => msg_matches_send(&ctx, &account, &matches, smtp),
|
||||||
("write", Some(matches)) => msg_matches_write(ctx, matches, smtp),
|
("write", Some(matches)) => msg_matches_write(&ctx, &account, &matches, smtp),
|
||||||
|
|
||||||
("template", Some(matches)) => Ok(msg_matches_tpl(ctx, matches)?),
|
("template", Some(matches)) => Ok(msg_matches_tpl(&ctx, &account, &matches)?),
|
||||||
("list", opt_matches) => msg_matches_list(ctx, opt_matches),
|
("list", opt_matches) => msg_matches_list(&ctx, &account, opt_matches),
|
||||||
(_other, opt_matches) => msg_matches_list(ctx, 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");
|
debug!("list command matched");
|
||||||
|
|
||||||
let page_size: usize = opt_matches
|
let page_size: usize = opt_matches
|
||||||
.and_then(|matches| matches.value_of("page-size").and_then(|s| s.parse().ok()))
|
.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);
|
debug!("page size: {:?}", page_size);
|
||||||
let page: usize = opt_matches
|
let page: usize = opt_matches
|
||||||
.and_then(|matches| matches.value_of("page").unwrap().parse().ok())
|
.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();
|
.unwrap_or_default();
|
||||||
debug!("page: {}", &page);
|
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 = imap_conn.list_msgs(&ctx.mbox, &page_size, &page)?;
|
||||||
let msgs = if let Some(ref fetches) = msgs {
|
let msgs = if let Some(ref fetches) = msgs {
|
||||||
Msgs::try_from(fetches)?
|
Msgs::try_from(fetches)?
|
||||||
|
@ -268,13 +277,13 @@ fn msg_matches_list(ctx: &Ctx, opt_matches: Option<&clap::ArgMatches>) -> Result
|
||||||
Ok(true)
|
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");
|
debug!("search command matched");
|
||||||
|
|
||||||
let page_size: usize = matches
|
let page_size: usize = matches
|
||||||
.value_of("page-size")
|
.value_of("page-size")
|
||||||
.and_then(|s| s.parse().ok())
|
.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);
|
debug!("page size: {}", &page_size);
|
||||||
let page: usize = matches
|
let page: usize = matches
|
||||||
.value_of("page")
|
.value_of("page")
|
||||||
|
@ -310,7 +319,7 @@ fn msg_matches_search(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
.join(" ");
|
.join(" ");
|
||||||
debug!("query: {}", &page);
|
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 = imap_conn.search_msgs(&ctx.mbox, &query, &page_size, &page)?;
|
||||||
let msgs = if let Some(ref fetches) = msgs {
|
let msgs = if let Some(ref fetches) = msgs {
|
||||||
Msgs::try_from(fetches)?
|
Msgs::try_from(fetches)?
|
||||||
|
@ -324,7 +333,7 @@ fn msg_matches_search(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
Ok(true)
|
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");
|
debug!("read command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
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");
|
let raw = matches.is_present("raw");
|
||||||
debug!("raw: {}", 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)?;
|
let msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||||
|
|
||||||
if raw {
|
if raw {
|
||||||
|
@ -346,14 +355,18 @@ fn msg_matches_read(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
Ok(true)
|
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");
|
debug!("attachments command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("uid: {}", &uid);
|
debug!("uid: {}", &uid);
|
||||||
|
|
||||||
// get the msg and than it's attachments
|
// 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 msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||||
let attachments = msg.attachments.clone();
|
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
|
// Iterate through all attachments and download them to the download
|
||||||
// directory of the account.
|
// directory of the account.
|
||||||
for attachment in &attachments {
|
for attachment in &attachments {
|
||||||
let filepath = ctx
|
let filepath = account.downloads_dir.join(&attachment.filename);
|
||||||
.config
|
|
||||||
.downloads_filepath(&ctx.account, &attachment.filename);
|
|
||||||
|
|
||||||
debug!("downloading {}…", &attachment.filename);
|
debug!("downloading {}…", &attachment.filename);
|
||||||
|
|
||||||
fs::write(&filepath, &attachment.body_raw)
|
fs::write(&filepath, &attachment.body_raw)
|
||||||
.with_context(|| format!("cannot save attachment {:?}", filepath))?;
|
.with_context(|| format!("cannot save attachment {:?}", filepath))?;
|
||||||
}
|
}
|
||||||
|
@ -390,19 +399,20 @@ fn msg_matches_attachments(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msg_matches_write<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
fn msg_matches_write<'a, SMTP: smtp::service::SMTPServiceInterface>(
|
||||||
ctx: &Ctx,
|
ctx: &Ctx,
|
||||||
|
account: &Account,
|
||||||
matches: &clap::ArgMatches,
|
matches: &clap::ArgMatches,
|
||||||
smtp: SMTP,
|
smtp: SMTP,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
debug!("write command matched");
|
debug!("write command matched");
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
|
|
||||||
// create the new msg
|
// create the new msg
|
||||||
// TODO: Make the header starting customizeable like from template
|
// TODO: Make the header starting customizeable like from template
|
||||||
let mut msg = Msg::new_with_headers(
|
let mut msg = Msg::new_with_headers(
|
||||||
&ctx,
|
&account,
|
||||||
Headers {
|
Headers {
|
||||||
subject: Some(String::new()),
|
subject: Some(String::new()),
|
||||||
to: Vec::new(),
|
to: Vec::new(),
|
||||||
|
@ -428,22 +438,23 @@ fn msg_matches_write<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msg_matches_reply<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
fn msg_matches_reply<'a, SMTP: smtp::service::SMTPServiceInterface>(
|
||||||
ctx: &Ctx,
|
ctx: &Ctx,
|
||||||
|
account: &Account,
|
||||||
matches: &clap::ArgMatches,
|
matches: &clap::ArgMatches,
|
||||||
smtp: SMTP,
|
smtp: SMTP,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
debug!("reply command matched");
|
debug!("reply command matched");
|
||||||
|
|
||||||
// -- Preparations --
|
// -- Preparations --
|
||||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
let mut imap_conn = ImapConnector::new(&account)?;
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||||
|
|
||||||
debug!("uid: {}", uid);
|
debug!("uid: {}", uid);
|
||||||
|
|
||||||
// Change the msg to a reply-msg.
|
// 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.
|
// Apply the given attachments to the reply-msg.
|
||||||
let attachments: Vec<&str> = matches
|
let attachments: Vec<&str> = matches
|
||||||
|
@ -462,14 +473,15 @@ fn msg_matches_reply<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||||
Ok(true)
|
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,
|
ctx: &Ctx,
|
||||||
|
account: &Account,
|
||||||
url: &Url,
|
url: &Url,
|
||||||
smtp: SMTP,
|
smtp: SMTP,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
debug!("mailto command matched");
|
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 cc = Vec::new();
|
||||||
let mut bcc = 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 {
|
let headers = Headers {
|
||||||
from: vec![ctx.config.address(&ctx.account)],
|
from: vec![account.address()],
|
||||||
to: vec![url.path().to_string()],
|
to: vec![url.path().to_string()],
|
||||||
encoding: ContentTransferEncoding::Base64,
|
encoding: ContentTransferEncoding::Base64,
|
||||||
bcc: Some(bcc),
|
bcc: Some(bcc),
|
||||||
cc: Some(cc),
|
cc: Some(cc),
|
||||||
signature: ctx.config.signature(&ctx.account),
|
signature: Some(account.signature.to_owned()),
|
||||||
subject: Some(subject.into()),
|
subject: Some(subject.into()),
|
||||||
..Headers::default()
|
..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.body = Body::new_with_text(body);
|
||||||
msg_interaction(&ctx, &mut msg, &mut imap_conn, smtp)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msg_matches_forward<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
fn msg_matches_forward<'a, SMTP: smtp::service::SMTPServiceInterface>(
|
||||||
ctx: &Ctx,
|
ctx: &Ctx,
|
||||||
|
account: &Account,
|
||||||
matches: &clap::ArgMatches,
|
matches: &clap::ArgMatches,
|
||||||
smtp: SMTP,
|
smtp: SMTP,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
debug!("forward command matched");
|
debug!("forward command matched");
|
||||||
|
|
||||||
// fetch the msg
|
// 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 uid = matches.value_of("uid").unwrap();
|
||||||
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
||||||
|
|
||||||
debug!("uid: {}", uid);
|
debug!("uid: {}", uid);
|
||||||
|
|
||||||
// prepare to forward it
|
// prepare to forward it
|
||||||
msg.change_to_forwarding(&ctx);
|
msg.change_to_forwarding(&account);
|
||||||
|
|
||||||
let attachments: Vec<&str> = matches
|
let attachments: Vec<&str> = matches
|
||||||
.values_of("attachments")
|
.values_of("attachments")
|
||||||
|
@ -548,11 +561,11 @@ fn msg_matches_forward<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||||
Ok(true)
|
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");
|
debug!("copy command matched");
|
||||||
|
|
||||||
// fetch the message to be copyied
|
// 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 uid = matches.value_of("uid").unwrap();
|
||||||
let target = matches.value_of("target").unwrap();
|
let target = matches.value_of("target").unwrap();
|
||||||
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
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)
|
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");
|
debug!("move command matched");
|
||||||
|
|
||||||
// fetch the msg which should be moved
|
// 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 uid = matches.value_of("uid").unwrap();
|
||||||
let target = matches.value_of("target").unwrap();
|
let target = matches.value_of("target").unwrap();
|
||||||
let mut msg = imap_conn.get_msg(&ctx.mbox, &uid)?;
|
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)
|
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");
|
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
|
// remove the message according to its UID
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
|
@ -626,14 +639,15 @@ fn msg_matches_delete(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msg_matches_send<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
fn msg_matches_send<'a, SMTP: smtp::service::SMTPServiceInterface>(
|
||||||
ctx: &Ctx,
|
ctx: &Ctx,
|
||||||
|
account: &Account,
|
||||||
matches: &clap::ArgMatches,
|
matches: &clap::ArgMatches,
|
||||||
smtp: SMTP,
|
smtp: SMTP,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
debug!("send command matched");
|
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() {
|
let msg = if atty::is(Stream::Stdin) || ctx.output.is_json() {
|
||||||
matches
|
matches
|
||||||
|
@ -667,10 +681,10 @@ fn msg_matches_send<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||||
Ok(true)
|
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");
|
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 msg: &str = matches.value_of("message").unwrap();
|
||||||
|
|
||||||
let mut msg = Msg::try_from(msg)?;
|
let mut msg = Msg::try_from(msg)?;
|
||||||
|
@ -683,11 +697,11 @@ fn msg_matches_save(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
Ok(true)
|
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() {
|
match matches.subcommand() {
|
||||||
("new", Some(matches)) => tpl_matches_new(ctx, matches),
|
("new", Some(matches)) => tpl_matches_new(&ctx, &account, matches),
|
||||||
("reply", Some(matches)) => tpl_matches_reply(ctx, matches),
|
("reply", Some(matches)) => tpl_matches_reply(&ctx, &account, matches),
|
||||||
("forward", Some(matches)) => tpl_matches_forward(ctx, matches),
|
("forward", Some(matches)) => tpl_matches_forward(&ctx, &account, matches),
|
||||||
|
|
||||||
// TODO: find a way to show the help message for template subcommand
|
// TODO: find a way to show the help message for template subcommand
|
||||||
_ => Err(anyhow!("Subcommand not found")),
|
_ => Err(anyhow!("Subcommand not found")),
|
||||||
|
@ -784,10 +798,10 @@ fn override_msg_with_args(msg: &mut Msg, matches: &clap::ArgMatches) {
|
||||||
msg.body = body;
|
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");
|
debug!("new command matched");
|
||||||
|
|
||||||
let mut msg = Msg::new(&ctx);
|
let mut msg = Msg::new(&account);
|
||||||
|
|
||||||
override_msg_with_args(&mut msg, &matches);
|
override_msg_with_args(&mut msg, &matches);
|
||||||
|
|
||||||
|
@ -797,16 +811,16 @@ fn tpl_matches_new(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
Ok(true)
|
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");
|
debug!("reply command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("uid: {}", uid);
|
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)?;
|
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);
|
override_msg_with_args(&mut msg, &matches);
|
||||||
trace!("Message: {:?}", msg);
|
trace!("Message: {:?}", msg);
|
||||||
|
@ -815,15 +829,15 @@ fn tpl_matches_reply(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
Ok(true)
|
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");
|
debug!("forward command matched");
|
||||||
|
|
||||||
let uid = matches.value_of("uid").unwrap();
|
let uid = matches.value_of("uid").unwrap();
|
||||||
debug!("uid: {}", uid);
|
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)?;
|
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);
|
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
|
/// This function opens the prompt to do some actions to the msg like sending, editing it again and
|
||||||
/// so on.
|
/// so on.
|
||||||
fn msg_interaction<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
fn msg_interaction<SMTP: smtp::service::SMTPServiceInterface>(
|
||||||
ctx: &Ctx,
|
ctx: &Ctx,
|
||||||
msg: &mut Msg,
|
msg: &mut Msg,
|
||||||
imap_conn: &mut ImapConnector,
|
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::{
|
// use himalaya::{
|
||||||
config::model::Account, flag::model::Flags, imap::model::ImapConnector, mbox::model::Mboxes,
|
// domain::account::entity::Account, flag::model::Flags, imap::model::ImapConnector,
|
||||||
msg::model::Msgs, smtp,
|
// mbox::model::Mboxes, msg::model::Msgs,
|
||||||
};
|
// };
|
||||||
|
|
||||||
use imap::types::Flag;
|
// use imap::types::Flag;
|
||||||
|
|
||||||
use lettre::message::SinglePart;
|
// use lettre::message::SinglePart;
|
||||||
use lettre::Message;
|
// use lettre::Message;
|
||||||
|
|
||||||
fn get_account(addr: &str) -> Account {
|
// fn get_account(addr: &str) -> Account {
|
||||||
Account {
|
// Account {
|
||||||
name: None,
|
// name: None,
|
||||||
downloads_dir: None,
|
// downloads_dir: None,
|
||||||
signature_delimiter: None,
|
// signature_delimiter: None,
|
||||||
signature: None,
|
// signature: None,
|
||||||
default_page_size: None,
|
// default_page_size: None,
|
||||||
default: Some(true),
|
// default: Some(true),
|
||||||
email: addr.into(),
|
// email: addr.into(),
|
||||||
watch_cmds: None,
|
// watch_cmds: None,
|
||||||
imap_host: String::from("localhost"),
|
// imap_host: String::from("localhost"),
|
||||||
imap_port: 3993,
|
// imap_port: 3993,
|
||||||
imap_starttls: Some(false),
|
// imap_starttls: Some(false),
|
||||||
imap_insecure: Some(true),
|
// imap_insecure: Some(true),
|
||||||
imap_login: addr.into(),
|
// imap_login: addr.into(),
|
||||||
imap_passwd_cmd: String::from("echo 'password'"),
|
// imap_passwd_cmd: String::from("echo 'password'"),
|
||||||
smtp_host: String::from("localhost"),
|
// smtp_host: String::from("localhost"),
|
||||||
smtp_port: 3465,
|
// smtp_port: 3465,
|
||||||
smtp_starttls: Some(false),
|
// smtp_starttls: Some(false),
|
||||||
smtp_insecure: Some(true),
|
// smtp_insecure: Some(true),
|
||||||
smtp_login: addr.into(),
|
// smtp_login: addr.into(),
|
||||||
smtp_passwd_cmd: String::from("echo 'password'"),
|
// smtp_passwd_cmd: String::from("echo 'password'"),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn mbox() {
|
// fn mbox() {
|
||||||
let account = get_account("inbox@localhost");
|
// let account = get_account("inbox@localhost");
|
||||||
let mut imap_conn = ImapConnector::new(&account).unwrap();
|
// let mut imap_conn = ImapConnector::new(&account).unwrap();
|
||||||
let names = imap_conn.list_mboxes().unwrap();
|
// let names = imap_conn.list_mboxes().unwrap();
|
||||||
let mboxes: Vec<String> = Mboxes::from(&names)
|
// let mboxes: Vec<String> = Mboxes::from(&names)
|
||||||
.0
|
// .0
|
||||||
.into_iter()
|
// .into_iter()
|
||||||
.map(|mbox| mbox.name)
|
// .map(|mbox| mbox.name)
|
||||||
.collect();
|
// .collect();
|
||||||
assert_eq!(mboxes, vec![String::from("INBOX")]);
|
// assert_eq!(mboxes, vec![String::from("INBOX")]);
|
||||||
imap_conn.logout();
|
// imap_conn.logout();
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn msg() {
|
// fn msg() {
|
||||||
// Preparations
|
// // Preparations
|
||||||
|
|
||||||
// Get the test-account and clean up the server.
|
// // Get the test-account and clean up the server.
|
||||||
let account = get_account("inbox@localhost");
|
// let account = get_account("inbox@localhost");
|
||||||
|
|
||||||
// Login
|
// // Login
|
||||||
let mut imap_conn = ImapConnector::new(&account).unwrap();
|
// let mut imap_conn = ImapConnector::new(&account).unwrap();
|
||||||
|
|
||||||
// remove all previous mails first
|
// // remove all previous mails first
|
||||||
let fetches = imap_conn.list_msgs("INBOX", &10, &0).unwrap();
|
// let fetches = imap_conn.list_msgs("INBOX", &10, &0).unwrap();
|
||||||
let msgs = if let Some(ref fetches) = fetches {
|
// let msgs = if let Some(ref fetches) = fetches {
|
||||||
Msgs::try_from(fetches).unwrap()
|
// Msgs::try_from(fetches).unwrap()
|
||||||
} else {
|
// } else {
|
||||||
Msgs::new()
|
// Msgs::new()
|
||||||
};
|
// };
|
||||||
|
|
||||||
// mark all mails as deleted
|
// // mark all mails as deleted
|
||||||
for msg in msgs.0.iter() {
|
// for msg in msgs.0.iter() {
|
||||||
imap_conn
|
// imap_conn
|
||||||
.add_flags(
|
// .add_flags(
|
||||||
"INBOX",
|
// "INBOX",
|
||||||
&msg.get_uid().unwrap().to_string(),
|
// &msg.get_uid().unwrap().to_string(),
|
||||||
Flags::from(vec![Flag::Deleted]),
|
// Flags::from(vec![Flag::Deleted]),
|
||||||
)
|
// )
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
}
|
// }
|
||||||
imap_conn.expunge("INBOX").unwrap();
|
// imap_conn.expunge("INBOX").unwrap();
|
||||||
|
|
||||||
// make sure, that they are *really* deleted
|
// // make sure, that they are *really* deleted
|
||||||
assert!(imap_conn.list_msgs("INBOX", &10, &0).unwrap().is_none());
|
// assert!(imap_conn.list_msgs("INBOX", &10, &0).unwrap().is_none());
|
||||||
|
|
||||||
// == Testing ==
|
// // == Testing ==
|
||||||
// Add messages
|
// // Add messages
|
||||||
let message_a = Message::builder()
|
// let message_a = Message::builder()
|
||||||
.from("sender-a@localhost".parse().unwrap())
|
// .from("sender-a@localhost".parse().unwrap())
|
||||||
.to("inbox@localhost".parse().unwrap())
|
// .to("inbox@localhost".parse().unwrap())
|
||||||
.subject("Subject A")
|
// .subject("Subject A")
|
||||||
.singlepart(SinglePart::builder().body("Body A".as_bytes().to_vec()))
|
// .singlepart(SinglePart::builder().body("Body A".as_bytes().to_vec()))
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
let message_b = Message::builder()
|
// let message_b = Message::builder()
|
||||||
.from("Sender B <sender-b@localhost>".parse().unwrap())
|
// .from("Sender B <sender-b@localhost>".parse().unwrap())
|
||||||
.to("inbox@localhost".parse().unwrap())
|
// .to("inbox@localhost".parse().unwrap())
|
||||||
.subject("Subject B")
|
// .subject("Subject B")
|
||||||
.singlepart(SinglePart::builder().body("Body B".as_bytes().to_vec()))
|
// .singlepart(SinglePart::builder().body("Body B".as_bytes().to_vec()))
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
smtp::send(&account, &message_a).unwrap();
|
// smtp::send(&account, &message_a).unwrap();
|
||||||
smtp::send(&account, &message_b).unwrap();
|
// smtp::send(&account, &message_b).unwrap();
|
||||||
|
|
||||||
// -- Get the messages --
|
// // -- Get the messages --
|
||||||
// TODO: check non-existance of \Seen flag
|
// // TODO: check non-existance of \Seen flag
|
||||||
let msgs = imap_conn.list_msgs("INBOX", &10, &0).unwrap();
|
// let msgs = imap_conn.list_msgs("INBOX", &10, &0).unwrap();
|
||||||
let msgs = if let Some(ref fetches) = msgs {
|
// let msgs = if let Some(ref fetches) = msgs {
|
||||||
Msgs::try_from(fetches).unwrap()
|
// Msgs::try_from(fetches).unwrap()
|
||||||
} else {
|
// } else {
|
||||||
Msgs::new()
|
// Msgs::new()
|
||||||
};
|
// };
|
||||||
|
|
||||||
// make sure that there are both mails which we sended
|
// // make sure that there are both mails which we sended
|
||||||
assert_eq!(msgs.0.len(), 2);
|
// assert_eq!(msgs.0.len(), 2);
|
||||||
|
|
||||||
let msg_a = msgs
|
// let msg_a = msgs
|
||||||
.0
|
// .0
|
||||||
.iter()
|
// .iter()
|
||||||
.find(|msg| msg.headers.subject.clone().unwrap() == "Subject A")
|
// .find(|msg| msg.headers.subject.clone().unwrap() == "Subject A")
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
let msg_b = msgs
|
// let msg_b = msgs
|
||||||
.0
|
// .0
|
||||||
.iter()
|
// .iter()
|
||||||
.find(|msg| msg.headers.subject.clone().unwrap() == "Subject B")
|
// .find(|msg| msg.headers.subject.clone().unwrap() == "Subject B")
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
// -- Checkup --
|
// // -- Checkup --
|
||||||
// look, if we received the correct credentials of the msgs.
|
// // look, if we received the correct credentials of the msgs.
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
msg_a.headers.subject.clone().unwrap_or_default(),
|
// msg_a.headers.subject.clone().unwrap_or_default(),
|
||||||
"Subject A"
|
// "Subject A"
|
||||||
);
|
// );
|
||||||
assert_eq!(&msg_a.headers.from[0], "sender-a@localhost");
|
// assert_eq!(&msg_a.headers.from[0], "sender-a@localhost");
|
||||||
|
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
msg_b.headers.subject.clone().unwrap_or_default(),
|
// msg_b.headers.subject.clone().unwrap_or_default(),
|
||||||
"Subject B"
|
// "Subject B"
|
||||||
);
|
// );
|
||||||
assert_eq!(&msg_b.headers.from[0], "Sender B <sender-b@localhost>");
|
// assert_eq!(&msg_b.headers.from[0], "Sender B <sender-b@localhost>");
|
||||||
|
|
||||||
// TODO: search messages
|
// // TODO: search messages
|
||||||
// TODO: read message (+ \Seen flag)
|
// // TODO: read message (+ \Seen flag)
|
||||||
// TODO: list message attachments
|
// // TODO: list message attachments
|
||||||
// TODO: add/set/remove flags
|
// // TODO: add/set/remove flags
|
||||||
|
|
||||||
// Logout
|
// // Logout
|
||||||
imap_conn.logout();
|
// imap_conn.logout();
|
||||||
}
|
// }
|
||||||
|
|
Loading…
Reference in a new issue