mirror of
https://github.com/soywod/himalaya.git
synced 2024-09-05 10:31:13 +00:00
turn smtp into an interfaced service
This commit is contained in:
parent
c619f06206
commit
5a9481910f
3
src/domain/mod.rs
Normal file
3
src/domain/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
//! Domain-specific modules.
|
||||||
|
|
||||||
|
pub mod smtp;
|
3
src/domain/smtp/mod.rs
Normal file
3
src/domain/smtp/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
//! Modules related to SMTP.
|
||||||
|
|
||||||
|
pub mod service;
|
51
src/domain/smtp/service.rs
Normal file
51
src/domain/smtp/service.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use lettre::{
|
||||||
|
self,
|
||||||
|
transport::{smtp::client::Tls, smtp::client::TlsParameters, smtp::SmtpTransport},
|
||||||
|
Transport,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::config::model::Account;
|
||||||
|
|
||||||
|
pub trait SMTPServiceInterface<'a> {
|
||||||
|
fn send(&self, msg: &lettre::Message) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SMTPService<'a> {
|
||||||
|
account: &'a Account,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SMTPService<'a> {
|
||||||
|
pub fn init(account: &'a Account) -> Self {
|
||||||
|
Self { account }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SMTPServiceInterface<'a> for SMTPService<'a> {
|
||||||
|
fn send(&self, msg: &lettre::Message) -> Result<()> {
|
||||||
|
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())
|
||||||
|
.build()?;
|
||||||
|
let tls = if self.account.smtp_starttls() {
|
||||||
|
Tls::Required(tls)
|
||||||
|
} else {
|
||||||
|
Tls::Wrapper(tls)
|
||||||
|
};
|
||||||
|
|
||||||
|
smtp_relay(&self.account.smtp_host)?
|
||||||
|
.port(self.account.smtp_port)
|
||||||
|
.tls(tls)
|
||||||
|
.credentials(self.account.smtp_creds()?)
|
||||||
|
.build()
|
||||||
|
.send(msg)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,4 +45,5 @@ pub mod output;
|
||||||
/// This module takes care for sending your mails!
|
/// This module takes care for sending your mails!
|
||||||
pub mod smtp;
|
pub mod smtp;
|
||||||
|
|
||||||
|
pub mod domain;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
|
@ -9,7 +9,7 @@ use himalaya::{
|
||||||
comp,
|
comp,
|
||||||
config::{cli::config_args, model::Config},
|
config::{cli::config_args, model::Config},
|
||||||
ctx::Ctx,
|
ctx::Ctx,
|
||||||
flag, imap, mbox,
|
domain, flag, imap, mbox,
|
||||||
msg::{self, cli::msg_matches_mailto},
|
msg::{self, cli::msg_matches_mailto},
|
||||||
output::{cli::output_args, model::Output},
|
output::{cli::output_args, model::Output},
|
||||||
};
|
};
|
||||||
|
@ -46,7 +46,8 @@ fn main() -> Result<()> {
|
||||||
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, account, output, mbox, arg_matches);
|
||||||
let url = Url::parse(&raw_args[1])?;
|
let url = Url::parse(&raw_args[1])?;
|
||||||
return Ok(msg_matches_mailto(&app, &url)?);
|
let smtp = domain::smtp::service::SMTPService::init(&app.account);
|
||||||
|
return Ok(msg_matches_mailto(&app, &url, smtp)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let args = parse_args();
|
let args = parse_args();
|
||||||
|
@ -78,10 +79,11 @@ fn main() -> Result<()> {
|
||||||
debug!("begin matching");
|
debug!("begin matching");
|
||||||
|
|
||||||
let app = Ctx::new(config, account, output, mbox, arg_matches);
|
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)?
|
let _matched = mbox::cli::matches(&app)?
|
||||||
|| flag::cli::matches(&app)?
|
|| flag::cli::matches(&app)?
|
||||||
|| imap::cli::matches(&app)?
|
|| imap::cli::matches(&app)?
|
||||||
|| msg::cli::matches(&app)?;
|
|| msg::cli::matches(&app, smtp)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@ use super::{
|
||||||
model::{Msg, MsgSerialized, Msgs},
|
model::{Msg, MsgSerialized, Msgs},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
ctx::Ctx, flag::model::Flags, imap::model::ImapConnector, input, mbox::cli::mbox_target_arg,
|
ctx::Ctx, domain::smtp, flag::model::Flags, imap::model::ImapConnector, input,
|
||||||
smtp,
|
mbox::cli::mbox_target_arg,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||||
|
@ -123,19 +123,22 @@ pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn matches(ctx: &Ctx) -> Result<bool> {
|
pub fn matches<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||||
|
ctx: &Ctx,
|
||||||
|
smtp: SMTP,
|
||||||
|
) -> 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, matches),
|
||||||
("copy", Some(matches)) => msg_matches_copy(ctx, matches),
|
("copy", Some(matches)) => msg_matches_copy(ctx, matches),
|
||||||
("delete", Some(matches)) => msg_matches_delete(ctx, matches),
|
("delete", Some(matches)) => msg_matches_delete(ctx, matches),
|
||||||
("forward", Some(matches)) => msg_matches_forward(ctx, matches),
|
("forward", Some(matches)) => msg_matches_forward(ctx, matches, smtp),
|
||||||
("move", Some(matches)) => msg_matches_move(ctx, matches),
|
("move", Some(matches)) => msg_matches_move(ctx, matches),
|
||||||
("read", Some(matches)) => msg_matches_read(ctx, matches),
|
("read", Some(matches)) => msg_matches_read(ctx, matches),
|
||||||
("reply", Some(matches)) => msg_matches_reply(ctx, matches),
|
("reply", Some(matches)) => msg_matches_reply(ctx, matches, smtp),
|
||||||
("save", Some(matches)) => msg_matches_save(ctx, matches),
|
("save", Some(matches)) => msg_matches_save(ctx, matches),
|
||||||
("search", Some(matches)) => msg_matches_search(ctx, matches),
|
("search", Some(matches)) => msg_matches_search(ctx, matches),
|
||||||
("send", Some(matches)) => msg_matches_send(ctx, matches),
|
("send", Some(matches)) => msg_matches_send(ctx, matches, smtp),
|
||||||
("write", Some(matches)) => msg_matches_write(ctx, matches),
|
("write", Some(matches)) => msg_matches_write(ctx, matches, smtp),
|
||||||
|
|
||||||
("template", Some(matches)) => Ok(msg_matches_tpl(ctx, matches)?),
|
("template", Some(matches)) => Ok(msg_matches_tpl(ctx, matches)?),
|
||||||
("list", opt_matches) => msg_matches_list(ctx, opt_matches),
|
("list", opt_matches) => msg_matches_list(ctx, opt_matches),
|
||||||
|
@ -236,7 +239,6 @@ fn tpl_args<'a>() -> Vec<clap::Arg<'a, 'a>> {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// == Match functions ==
|
|
||||||
fn msg_matches_list(ctx: &Ctx, opt_matches: Option<&clap::ArgMatches>) -> Result<bool> {
|
fn msg_matches_list(ctx: &Ctx, opt_matches: Option<&clap::ArgMatches>) -> Result<bool> {
|
||||||
debug!("list command matched");
|
debug!("list command matched");
|
||||||
|
|
||||||
|
@ -388,7 +390,11 @@ fn msg_matches_attachments(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msg_matches_write(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
fn msg_matches_write<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||||
|
ctx: &Ctx,
|
||||||
|
matches: &clap::ArgMatches,
|
||||||
|
smtp: SMTP,
|
||||||
|
) -> Result<bool> {
|
||||||
debug!("write command matched");
|
debug!("write command matched");
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||||
|
@ -414,7 +420,7 @@ fn msg_matches_write(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|path| msg.add_attachment(path));
|
.for_each(|path| msg.add_attachment(path));
|
||||||
|
|
||||||
msg_interaction(&ctx, &mut msg, &mut imap_conn)?;
|
msg_interaction(&ctx, &mut msg, &mut imap_conn, smtp)?;
|
||||||
|
|
||||||
// let's be nice to the server and say "bye" to the server
|
// let's be nice to the server and say "bye" to the server
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
|
@ -422,7 +428,11 @@ fn msg_matches_write(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msg_matches_reply(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
fn msg_matches_reply<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||||
|
ctx: &Ctx,
|
||||||
|
matches: &clap::ArgMatches,
|
||||||
|
smtp: SMTP,
|
||||||
|
) -> Result<bool> {
|
||||||
debug!("reply command matched");
|
debug!("reply command matched");
|
||||||
|
|
||||||
// -- Preparations --
|
// -- Preparations --
|
||||||
|
@ -446,13 +456,17 @@ fn msg_matches_reply(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
debug!("found {} attachments", attachments.len());
|
debug!("found {} attachments", attachments.len());
|
||||||
trace!("attachments: {:?}", attachments);
|
trace!("attachments: {:?}", attachments);
|
||||||
|
|
||||||
msg_interaction(&ctx, &mut msg, &mut imap_conn)?;
|
msg_interaction(&ctx, &mut msg, &mut imap_conn, smtp)?;
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn msg_matches_mailto(ctx: &Ctx, url: &Url) -> Result<()> {
|
pub fn msg_matches_mailto<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||||
|
ctx: &Ctx,
|
||||||
|
url: &Url,
|
||||||
|
smtp: SMTP,
|
||||||
|
) -> Result<()> {
|
||||||
debug!("mailto command matched");
|
debug!("mailto command matched");
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||||
|
@ -493,13 +507,17 @@ pub fn msg_matches_mailto(ctx: &Ctx, url: &Url) -> Result<()> {
|
||||||
|
|
||||||
let mut msg = Msg::new_with_headers(&ctx, headers);
|
let mut msg = Msg::new_with_headers(&ctx, headers);
|
||||||
msg.body = Body::new_with_text(body);
|
msg.body = Body::new_with_text(body);
|
||||||
msg_interaction(&ctx, &mut msg, &mut imap_conn)?;
|
msg_interaction(&ctx, &mut msg, &mut imap_conn, smtp)?;
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msg_matches_forward(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
fn msg_matches_forward<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||||
|
ctx: &Ctx,
|
||||||
|
matches: &clap::ArgMatches,
|
||||||
|
smtp: SMTP,
|
||||||
|
) -> Result<bool> {
|
||||||
debug!("forward command matched");
|
debug!("forward command matched");
|
||||||
|
|
||||||
// fetch the msg
|
// fetch the msg
|
||||||
|
@ -523,7 +541,7 @@ fn msg_matches_forward(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
trace!("attachments: {:?}", attachments);
|
trace!("attachments: {:?}", attachments);
|
||||||
|
|
||||||
// apply changes
|
// apply changes
|
||||||
msg_interaction(&ctx, &mut msg, &mut imap_conn)?;
|
msg_interaction(&ctx, &mut msg, &mut imap_conn, smtp)?;
|
||||||
|
|
||||||
imap_conn.logout();
|
imap_conn.logout();
|
||||||
|
|
||||||
|
@ -608,7 +626,11 @@ fn msg_matches_delete(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn msg_matches_send(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
fn msg_matches_send<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||||
|
ctx: &Ctx,
|
||||||
|
matches: &clap::ArgMatches,
|
||||||
|
smtp: SMTP,
|
||||||
|
) -> Result<bool> {
|
||||||
debug!("send command matched");
|
debug!("send command matched");
|
||||||
|
|
||||||
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
let mut imap_conn = ImapConnector::new(&ctx.account)?;
|
||||||
|
@ -633,7 +655,7 @@ fn msg_matches_send(ctx: &Ctx, matches: &clap::ArgMatches) -> Result<bool> {
|
||||||
|
|
||||||
// send the message/msg
|
// send the message/msg
|
||||||
let sendable = msg.to_sendable_msg()?;
|
let sendable = msg.to_sendable_msg()?;
|
||||||
smtp::send(&ctx.account, &sendable)?;
|
smtp.send(&sendable)?;
|
||||||
debug!("message sent!");
|
debug!("message sent!");
|
||||||
|
|
||||||
// add the message/msg to the Sent-Mailbox of the user
|
// add the message/msg to the Sent-Mailbox of the user
|
||||||
|
@ -813,7 +835,12 @@ 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(ctx: &Ctx, msg: &mut Msg, imap_conn: &mut ImapConnector) -> Result<bool> {
|
fn msg_interaction<'a, SMTP: smtp::service::SMTPServiceInterface<'a>>(
|
||||||
|
ctx: &Ctx,
|
||||||
|
msg: &mut Msg,
|
||||||
|
imap_conn: &mut ImapConnector,
|
||||||
|
smtp: SMTP,
|
||||||
|
) -> Result<bool> {
|
||||||
// let the user change the body a little bit first, before opening the prompt
|
// let the user change the body a little bit first, before opening the prompt
|
||||||
msg.edit_body()?;
|
msg.edit_body()?;
|
||||||
|
|
||||||
|
@ -836,7 +863,7 @@ fn msg_interaction(ctx: &Ctx, msg: &mut Msg, imap_conn: &mut ImapConnector) -> R
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
smtp::send(&ctx.account, &sendable)?;
|
smtp.send(&sendable)?;
|
||||||
|
|
||||||
// TODO: Gmail sent mailboxes are called `[Gmail]/Sent`
|
// TODO: Gmail sent mailboxes are called `[Gmail]/Sent`
|
||||||
// which creates a conflict, fix this!
|
// which creates a conflict, fix this!
|
||||||
|
|
Loading…
Reference in a new issue