mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-17 05:07:55 +08:00
release v0.5.4 (#285)
* replace bsd3 license by bsd4 * add attachments with save and send commands (#284) * set up tpl save and send commands * improve msg save and send handlers * add vim msg#add_attachment fn * improve vim logs * update changelog * add attachment keybind vim doc * reverse range order fetch envelopes (#276) * bump version v0.5.4
This commit is contained in:
@@ -144,11 +144,11 @@ impl<'a> ImapServiceInterface<'a> for ImapService<'a> {
|
||||
let cursor = (page * page_size) as i64;
|
||||
let begin = 1.max(last_seq - cursor);
|
||||
let end = begin - begin.min(*page_size as i64) + 1;
|
||||
format!("{}:{}", begin, end)
|
||||
format!("{}:{}", end, begin)
|
||||
} else {
|
||||
String::from("1:*")
|
||||
};
|
||||
debug!("range: {:?}", range);
|
||||
debug!("range: {}", range);
|
||||
|
||||
let fetches = self
|
||||
.sess()?
|
||||
|
||||
@@ -23,7 +23,7 @@ type Raw = bool;
|
||||
type All = bool;
|
||||
type RawMsg<'a> = &'a str;
|
||||
type Query = String;
|
||||
type AttachmentsPaths<'a> = Vec<&'a str>;
|
||||
type AttachmentPaths<'a> = Vec<&'a str>;
|
||||
type MaxTableWidth = Option<usize>;
|
||||
|
||||
/// Message commands.
|
||||
@@ -31,15 +31,15 @@ pub enum Command<'a> {
|
||||
Attachments(Seq<'a>),
|
||||
Copy(Seq<'a>, Mbox<'a>),
|
||||
Delete(Seq<'a>),
|
||||
Forward(Seq<'a>, AttachmentsPaths<'a>),
|
||||
Forward(Seq<'a>, AttachmentPaths<'a>),
|
||||
List(MaxTableWidth, Option<PageSize>, Page),
|
||||
Move(Seq<'a>, Mbox<'a>),
|
||||
Read(Seq<'a>, TextMime<'a>, Raw),
|
||||
Reply(Seq<'a>, All, AttachmentsPaths<'a>),
|
||||
Reply(Seq<'a>, All, AttachmentPaths<'a>),
|
||||
Save(RawMsg<'a>),
|
||||
Search(Query, MaxTableWidth, Option<PageSize>, Page),
|
||||
Send(RawMsg<'a>),
|
||||
Write(AttachmentsPaths<'a>),
|
||||
Write(AttachmentPaths<'a>),
|
||||
|
||||
Flag(Option<flag_arg::Command<'a>>),
|
||||
Tpl(Option<tpl_arg::Command<'a>>),
|
||||
@@ -256,7 +256,7 @@ fn page_arg<'a>() -> Arg<'a, 'a> {
|
||||
}
|
||||
|
||||
/// Message attachment argument.
|
||||
fn attachment_arg<'a>() -> Arg<'a, 'a> {
|
||||
pub fn attachment_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("attachments")
|
||||
.help("Adds attachment to the message")
|
||||
.short("a")
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use anyhow::{Context, Result};
|
||||
use atty::Stream;
|
||||
use imap::types::Flag;
|
||||
use log::{debug, trace};
|
||||
use log::{debug, info, trace};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
convert::{TryFrom, TryInto},
|
||||
@@ -244,14 +244,25 @@ pub fn reply<
|
||||
imap.add_flags(seq, &flags)
|
||||
}
|
||||
|
||||
/// Save a raw message to the targetted mailbox.
|
||||
/// Saves a raw message to the targetted mailbox.
|
||||
pub fn save<'a, Printer: PrinterService, ImapService: ImapServiceInterface<'a>>(
|
||||
mbox: &Mbox,
|
||||
raw_msg: &str,
|
||||
printer: &mut Printer,
|
||||
imap: &mut ImapService,
|
||||
) -> Result<()> {
|
||||
let raw_msg = if atty::is(Stream::Stdin) || printer.is_json() {
|
||||
info!("entering save message handler");
|
||||
|
||||
debug!("mailbox: {}", mbox);
|
||||
let flags = Flags::try_from(vec![Flag::Seen])?;
|
||||
debug!("flags: {}", flags);
|
||||
|
||||
let is_tty = atty::is(Stream::Stdin);
|
||||
debug!("is tty: {}", is_tty);
|
||||
let is_json = printer.is_json();
|
||||
debug!("is json: {}", is_json);
|
||||
|
||||
let raw_msg = if is_tty || is_json {
|
||||
raw_msg.replace("\r", "").replace("\n", "\r\n")
|
||||
} else {
|
||||
io::stdin()
|
||||
@@ -261,8 +272,6 @@ pub fn save<'a, Printer: PrinterService, ImapService: ImapServiceInterface<'a>>(
|
||||
.collect::<Vec<String>>()
|
||||
.join("\r\n")
|
||||
};
|
||||
|
||||
let flags = Flags::try_from(vec![Flag::Seen])?;
|
||||
imap.append_raw_msg_with_flags(mbox, raw_msg.as_bytes(), flags)
|
||||
}
|
||||
|
||||
@@ -297,7 +306,19 @@ pub fn send<
|
||||
imap: &mut ImapService,
|
||||
smtp: &mut SmtpService,
|
||||
) -> Result<()> {
|
||||
let raw_msg = if atty::is(Stream::Stdin) || printer.is_json() {
|
||||
info!("entering send message handler");
|
||||
|
||||
let mbox = Mbox::new(&account.sent_folder);
|
||||
debug!("mailbox: {}", mbox);
|
||||
let flags = Flags::try_from(vec![Flag::Seen])?;
|
||||
debug!("flags: {}", flags);
|
||||
|
||||
let is_tty = atty::is(Stream::Stdin);
|
||||
debug!("is tty: {}", is_tty);
|
||||
let is_json = printer.is_json();
|
||||
debug!("is json: {}", is_json);
|
||||
|
||||
let raw_msg = if is_tty || is_json {
|
||||
raw_msg.replace("\r", "").replace("\n", "\r\n")
|
||||
} else {
|
||||
io::stdin()
|
||||
@@ -307,15 +328,11 @@ pub fn send<
|
||||
.collect::<Vec<String>>()
|
||||
.join("\r\n")
|
||||
};
|
||||
trace!("raw message: {:?}", raw_msg);
|
||||
let envelope: lettre::address::Envelope = Msg::from_tpl(&raw_msg)?.try_into()?;
|
||||
trace!("envelope: {:?}", envelope);
|
||||
|
||||
let msg = Msg::from_tpl(&raw_msg)?;
|
||||
let envelope: lettre::address::Envelope = msg.try_into()?;
|
||||
smtp.send_raw_msg(&envelope, raw_msg.as_bytes())?;
|
||||
debug!("message sent!");
|
||||
|
||||
// Save message to sent folder
|
||||
let mbox = Mbox::new(&account.sent_folder);
|
||||
let flags = Flags::try_from(vec![Flag::Seen])?;
|
||||
imap.append_raw_msg_with_flags(&mbox, raw_msg.as_bytes(), flags)
|
||||
}
|
||||
|
||||
|
||||
+65
-43
@@ -4,12 +4,14 @@
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{self, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||
use log::{debug, trace};
|
||||
use log::{debug, info, trace};
|
||||
|
||||
use crate::domain::msg::msg_arg;
|
||||
|
||||
type Seq<'a> = &'a str;
|
||||
type All = bool;
|
||||
type ReplyAll = bool;
|
||||
type AttachmentPaths<'a> = Vec<&'a str>;
|
||||
type Tpl<'a> = &'a str;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TplOverride<'a> {
|
||||
@@ -23,69 +25,77 @@ pub struct TplOverride<'a> {
|
||||
pub sig: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ArgMatches<'a>> for TplOverride<'a> {
|
||||
fn from(matches: &'a ArgMatches<'a>) -> Self {
|
||||
Self {
|
||||
subject: matches.value_of("subject"),
|
||||
from: matches.values_of("from").map(|v| v.collect()),
|
||||
to: matches.values_of("to").map(|v| v.collect()),
|
||||
cc: matches.values_of("cc").map(|v| v.collect()),
|
||||
bcc: matches.values_of("bcc").map(|v| v.collect()),
|
||||
headers: matches.values_of("headers").map(|v| v.collect()),
|
||||
body: matches.value_of("body"),
|
||||
sig: matches.value_of("signature"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Message template commands.
|
||||
pub enum Command<'a> {
|
||||
New(TplOverride<'a>),
|
||||
Reply(Seq<'a>, All, TplOverride<'a>),
|
||||
Reply(Seq<'a>, ReplyAll, TplOverride<'a>),
|
||||
Forward(Seq<'a>, TplOverride<'a>),
|
||||
Save(AttachmentPaths<'a>, Tpl<'a>),
|
||||
Send(AttachmentPaths<'a>, Tpl<'a>),
|
||||
}
|
||||
|
||||
/// Message template command matcher.
|
||||
pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Command<'a>>> {
|
||||
if let Some(m) = m.subcommand_matches("new") {
|
||||
debug!("new command matched");
|
||||
let tpl = TplOverride {
|
||||
subject: m.value_of("subject"),
|
||||
from: m.values_of("from").map(|v| v.collect()),
|
||||
to: m.values_of("to").map(|v| v.collect()),
|
||||
cc: m.values_of("cc").map(|v| v.collect()),
|
||||
bcc: m.values_of("bcc").map(|v| v.collect()),
|
||||
headers: m.values_of("headers").map(|v| v.collect()),
|
||||
body: m.value_of("body"),
|
||||
sig: m.value_of("signature"),
|
||||
};
|
||||
trace!(r#"template args: "{:?}""#, tpl);
|
||||
info!("new command matched");
|
||||
let tpl = TplOverride::from(m);
|
||||
trace!("template override: {:?}", tpl);
|
||||
return Ok(Some(Command::New(tpl)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("reply") {
|
||||
debug!("reply command matched");
|
||||
info!("reply command matched");
|
||||
let seq = m.value_of("seq").unwrap();
|
||||
trace!(r#"seq: "{}""#, seq);
|
||||
debug!("sequence: {}", seq);
|
||||
let all = m.is_present("reply-all");
|
||||
trace!("reply all: {}", all);
|
||||
let tpl = TplOverride {
|
||||
subject: m.value_of("subject"),
|
||||
from: m.values_of("from").map(|v| v.collect()),
|
||||
to: m.values_of("to").map(|v| v.collect()),
|
||||
cc: m.values_of("cc").map(|v| v.collect()),
|
||||
bcc: m.values_of("bcc").map(|v| v.collect()),
|
||||
headers: m.values_of("headers").map(|v| v.collect()),
|
||||
body: m.value_of("body"),
|
||||
sig: m.value_of("signature"),
|
||||
};
|
||||
trace!(r#"template args: "{:?}""#, tpl);
|
||||
debug!("reply all: {}", all);
|
||||
let tpl = TplOverride::from(m);
|
||||
trace!("template override: {:?}", tpl);
|
||||
return Ok(Some(Command::Reply(seq, all, tpl)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("forward") {
|
||||
debug!("forward command matched");
|
||||
info!("forward command matched");
|
||||
let seq = m.value_of("seq").unwrap();
|
||||
trace!(r#"seq: "{}""#, seq);
|
||||
let tpl = TplOverride {
|
||||
subject: m.value_of("subject"),
|
||||
from: m.values_of("from").map(|v| v.collect()),
|
||||
to: m.values_of("to").map(|v| v.collect()),
|
||||
cc: m.values_of("cc").map(|v| v.collect()),
|
||||
bcc: m.values_of("bcc").map(|v| v.collect()),
|
||||
headers: m.values_of("headers").map(|v| v.collect()),
|
||||
body: m.value_of("body"),
|
||||
sig: m.value_of("signature"),
|
||||
};
|
||||
trace!(r#"template args: "{:?}""#, tpl);
|
||||
debug!("sequence: {}", seq);
|
||||
let tpl = TplOverride::from(m);
|
||||
trace!("template args: {:?}", tpl);
|
||||
return Ok(Some(Command::Forward(seq, tpl)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("save") {
|
||||
info!("save command matched");
|
||||
let attachment_paths: Vec<&str> = m.values_of("attachments").unwrap_or_default().collect();
|
||||
trace!("attachments paths: {:?}", attachment_paths);
|
||||
let tpl = m.value_of("template").unwrap_or_default();
|
||||
trace!("template: {}", tpl);
|
||||
return Ok(Some(Command::Save(attachment_paths, tpl)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("send") {
|
||||
info!("send command matched");
|
||||
let attachment_paths: Vec<&str> = m.values_of("attachments").unwrap_or_default().collect();
|
||||
trace!("attachments paths: {:?}", attachment_paths);
|
||||
let tpl = m.value_of("template").unwrap_or_default();
|
||||
trace!("template: {}", tpl);
|
||||
return Ok(Some(Command::Send(attachment_paths, tpl)));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
@@ -154,7 +164,7 @@ pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("reply")
|
||||
.aliases(&["rep", "r"])
|
||||
.aliases(&["rep", "re", "r"])
|
||||
.about("Generates a reply message template")
|
||||
.arg(msg_arg::seq_arg())
|
||||
.arg(msg_arg::reply_all_arg())
|
||||
@@ -166,5 +176,17 @@ pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||
.about("Generates a forward message template")
|
||||
.arg(msg_arg::seq_arg())
|
||||
.args(&tpl_args()),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("save")
|
||||
.about("Saves a message based on the given template")
|
||||
.arg(&msg_arg::attachment_arg())
|
||||
.arg(Arg::with_name("template").raw(true)),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("send")
|
||||
.about("Sends a message based on the given template")
|
||||
.arg(&msg_arg::attachment_arg())
|
||||
.arg(Arg::with_name("template").raw(true)),
|
||||
)]
|
||||
}
|
||||
|
||||
@@ -3,12 +3,19 @@
|
||||
//! This module gathers all message template commands.
|
||||
|
||||
use anyhow::Result;
|
||||
use atty::Stream;
|
||||
use imap::types::Flag;
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
io::{self, BufRead},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
config::Account,
|
||||
domain::{
|
||||
imap::ImapServiceInterface,
|
||||
msg::{Msg, TplOverride},
|
||||
Flags, Mbox, SmtpServiceInterface,
|
||||
},
|
||||
output::PrinterService,
|
||||
};
|
||||
@@ -53,3 +60,59 @@ pub fn forward<'a, Printer: PrinterService, ImapService: ImapServiceInterface<'a
|
||||
.to_tpl(opts, account);
|
||||
printer.print(tpl)
|
||||
}
|
||||
|
||||
/// Saves a message based on a template.
|
||||
pub fn save<'a, Printer: PrinterService, ImapService: ImapServiceInterface<'a>>(
|
||||
mbox: &Mbox,
|
||||
attachments_paths: Vec<&str>,
|
||||
tpl: &str,
|
||||
printer: &mut Printer,
|
||||
imap: &mut ImapService,
|
||||
) -> Result<()> {
|
||||
let tpl = if atty::is(Stream::Stdin) || printer.is_json() {
|
||||
tpl.replace("\r", "")
|
||||
} else {
|
||||
io::stdin()
|
||||
.lock()
|
||||
.lines()
|
||||
.filter_map(Result::ok)
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
};
|
||||
let msg = Msg::from_tpl(&tpl)?.add_attachments(attachments_paths)?;
|
||||
let raw_msg: Vec<u8> = TryInto::try_into(&msg)?;
|
||||
let flags = Flags::try_from(vec![Flag::Seen])?;
|
||||
imap.append_raw_msg_with_flags(mbox, &raw_msg, flags)?;
|
||||
printer.print("Template successfully saved")
|
||||
}
|
||||
|
||||
/// Sends a message based on a template.
|
||||
pub fn send<
|
||||
'a,
|
||||
Printer: PrinterService,
|
||||
ImapService: ImapServiceInterface<'a>,
|
||||
SmtpService: SmtpServiceInterface,
|
||||
>(
|
||||
mbox: &Mbox,
|
||||
attachments_paths: Vec<&str>,
|
||||
tpl: &str,
|
||||
printer: &mut Printer,
|
||||
imap: &mut ImapService,
|
||||
smtp: &mut SmtpService,
|
||||
) -> Result<()> {
|
||||
let tpl = if atty::is(Stream::Stdin) || printer.is_json() {
|
||||
tpl.replace("\r", "")
|
||||
} else {
|
||||
io::stdin()
|
||||
.lock()
|
||||
.lines()
|
||||
.filter_map(Result::ok)
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
};
|
||||
let msg = Msg::from_tpl(&tpl)?.add_attachments(attachments_paths)?;
|
||||
let sent_msg = smtp.send_msg(&msg)?;
|
||||
let flags = Flags::try_from(vec![Flag::Seen])?;
|
||||
imap.append_raw_msg_with_flags(mbox, &sent_msg.formatted(), flags)?;
|
||||
printer.print("Template successfully sent")
|
||||
}
|
||||
|
||||
@@ -176,6 +176,12 @@ fn main() -> Result<()> {
|
||||
Some(tpl_arg::Command::Forward(seq, tpl)) => {
|
||||
return tpl_handler::forward(seq, tpl, &account, &mut printer, &mut imap);
|
||||
}
|
||||
Some(tpl_arg::Command::Save(atts, tpl)) => {
|
||||
return tpl_handler::save(&mbox, atts, tpl, &mut printer, &mut imap);
|
||||
}
|
||||
Some(tpl_arg::Command::Send(atts, tpl)) => {
|
||||
return tpl_handler::send(&mbox, atts, tpl, &mut printer, &mut imap, &mut smtp);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
|
||||
Reference in New Issue
Block a user