BackendOp: return future in as_bytes()

memfd
Manos Pitsidianakis 2020-07-04 17:38:57 +03:00
parent 4721073bc3
commit b3876113aa
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
19 changed files with 562 additions and 571 deletions

View File

@ -436,7 +436,7 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
/// let operation = Box::new(FooOp {});
/// ```
pub trait BackendOp: ::std::fmt::Debug + ::std::marker::Send {
fn as_bytes(&mut self) -> Result<&[u8]>;
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>>;
fn fetch_flags(&self) -> ResultFuture<Flag>;
fn set_flag(&mut self, flag: Flag, value: bool) -> ResultFuture<()>;
fn set_tag(&mut self, tag: String, value: bool) -> ResultFuture<()>;
@ -458,7 +458,7 @@ impl ReadOnlyOp {
}
impl BackendOp for ReadOnlyOp {
fn as_bytes(&mut self) -> Result<&[u8]> {
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
self.op.as_bytes()
}
fn fetch_flags(&self) -> ResultFuture<Flag> {

View File

@ -31,12 +31,7 @@ use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
pub struct ImapOp {
uid: usize,
bytes: Option<String>,
headers: Option<String>,
body: Option<String>,
mailbox_path: String,
mailbox_hash: MailboxHash,
flags: Arc<FutureMutex<Option<Flag>>>,
connection: Arc<Mutex<ImapConnection>>,
uid_store: Arc<UIDStore>,
}
@ -52,32 +47,21 @@ impl ImapOp {
ImapOp {
uid,
connection,
bytes: None,
headers: None,
body: None,
mailbox_path,
mailbox_hash,
flags: Arc::new(FutureMutex::new(None)),
uid_store,
}
}
}
impl BackendOp for ImapOp {
fn as_bytes(&mut self) -> Result<&[u8]> {
if self.bytes.is_none() {
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
let mut bytes_cache = self.uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(self.uid).or_default();
if cache.bytes.is_some() {
self.bytes = cache.bytes.clone();
} else {
drop(cache);
drop(bytes_cache);
if cache.bytes.is_none() {
let mut response = String::with_capacity(8 * 1024);
{
let mut conn =
try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?;
conn.examine_mailbox(self.mailbox_hash, &mut response, false)?;
let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?;
conn.examine_mailbox(self.mailbox_hash, &mut response)?;
conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes())?;
conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)?;
}
@ -96,12 +80,10 @@ impl BackendOp for ImapOp {
if let Some((flags, _)) = flags {
cache.flags = Some(flags);
}
cache.bytes =
Some(unsafe { std::str::from_utf8_unchecked(body.unwrap()).to_string() });
self.bytes = cache.bytes.clone();
cache.bytes = Some(unsafe { std::str::from_utf8_unchecked(body.unwrap()).to_string() });
}
}
Ok(self.bytes.as_ref().unwrap().as_bytes())
let ret = cache.bytes.clone().unwrap().into_bytes();
Ok(Box::pin(async move { Ok(ret) }))
}
fn fetch_flags(&self) -> ResultFuture<Flag> {
@ -109,13 +91,9 @@ impl BackendOp for ImapOp {
let mailbox_hash = self.mailbox_hash;
let uid = self.uid;
let uid_store = self.uid_store.clone();
let flags = self.flags.clone();
let mut response = String::with_capacity(8 * 1024);
Ok(Box::pin(async move {
if let Some(val) = *flags.lock().await {
return Ok(val);
}
let exists_in_cache = {
let mut bytes_cache = uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(uid).or_default();
@ -158,8 +136,6 @@ impl BackendOp for ImapOp {
let val = cache.flags;
val
};
let mut f = flags.lock().await;
*f = val;
Ok(val.unwrap())
}
}))

View File

@ -376,9 +376,6 @@ impl MailBackend for ImapType {
};
Ok(Box::new(ImapOp::new(
uid,
self.uid_store.mailboxes.read().unwrap()[&mailbox_hash]
.imap_path()
.to_string(),
mailbox_hash,
self.connection.clone(),
self.uid_store.clone(),

View File

@ -30,12 +30,7 @@ use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct ImapOp {
uid: usize,
bytes: Option<String>,
headers: Option<String>,
body: Option<String>,
mailbox_path: String,
mailbox_hash: MailboxHash,
flags: Arc<FutureMutex<Option<Flag>>>,
connection: Arc<FutureMutex<ImapConnection>>,
uid_store: Arc<UIDStore>,
}
@ -43,7 +38,6 @@ pub struct ImapOp {
impl ImapOp {
pub fn new(
uid: usize,
mailbox_path: String,
mailbox_hash: MailboxHash,
connection: Arc<FutureMutex<ImapConnection>>,
uid_store: Arc<UIDStore>,
@ -51,36 +45,32 @@ impl ImapOp {
ImapOp {
uid,
connection,
bytes: None,
headers: None,
body: None,
mailbox_path,
mailbox_hash,
flags: Arc::new(FutureMutex::new(None)),
uid_store,
}
}
}
impl BackendOp for ImapOp {
fn as_bytes(&mut self) -> Result<&[u8]> {
if self.bytes.is_none() {
let mut bytes_cache = self.uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(self.uid).or_default();
if cache.bytes.is_some() {
self.bytes = cache.bytes.clone();
} else {
drop(cache);
drop(bytes_cache);
let ret: Result<()> = futures::executor::block_on(async {
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
let mut response = String::with_capacity(8 * 1024);
let connection = self.connection.clone();
let mailbox_hash = self.mailbox_hash;
let uid = self.uid;
let uid_store = self.uid_store.clone();
Ok(Box::pin(async move {
let exists_in_cache = {
let mut bytes_cache = uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(uid).or_default();
cache.bytes.is_some()
};
if !exists_in_cache {
let mut response = String::with_capacity(8 * 1024);
{
let mut conn = self.connection.lock().await;
conn.examine_mailbox(self.mailbox_hash, &mut response, false)
let mut conn = connection.lock().await;
conn.examine_mailbox(mailbox_hash, &mut response, false)
.await?;
conn.send_command(
format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes(),
)
conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", uid).as_bytes())
.await?;
conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)
.await?;
@ -91,25 +81,27 @@ impl BackendOp for ImapOp {
response.lines().collect::<Vec<&str>>().len()
);
let UidFetchResponse {
uid, flags, body, ..
uid: _uid,
flags: _flags,
body,
..
} = protocol_parser::uid_fetch_response(&response)?.1;
assert_eq!(uid, self.uid);
assert_eq!(_uid, uid);
assert!(body.is_some());
let mut bytes_cache = self.uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(self.uid).or_default();
if let Some((flags, _)) = flags {
//self.flags.set(Some(flags));
cache.flags = Some(flags);
let mut bytes_cache = uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(uid).or_default();
if let Some((_flags, _)) = _flags {
//flags.lock().await.set(Some(_flags));
cache.flags = Some(_flags);
}
cache.bytes =
Some(unsafe { std::str::from_utf8_unchecked(body.unwrap()).to_string() });
self.bytes = cache.bytes.clone();
Ok(())
});
ret?;
}
}
Ok(self.bytes.as_ref().unwrap().as_bytes())
let mut bytes_cache = uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(uid).or_default();
let ret = cache.bytes.clone().unwrap().into_bytes();
Ok(ret)
}))
}
fn fetch_flags(&self) -> ResultFuture<Flag> {
@ -118,12 +110,8 @@ impl BackendOp for ImapOp {
let mailbox_hash = self.mailbox_hash;
let uid = self.uid;
let uid_store = self.uid_store.clone();
let flags = self.flags.clone();
Ok(Box::pin(async move {
if let Some(val) = *flags.lock().await {
return Ok(val);
}
let exists_in_cache = {
let mut bytes_cache = uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(uid).or_default();
@ -158,7 +146,6 @@ impl BackendOp for ImapOp {
}
let (_uid, (_flags, _)) = v[0];
assert_eq!(uid, uid);
*flags.lock().await = Some(_flags);
let mut bytes_cache = uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(uid).or_default();
cache.flags = Some(_flags);
@ -170,8 +157,6 @@ impl BackendOp for ImapOp {
let val = cache.flags;
val
};
let mut f = flags.lock().await;
*f = val;
Ok(val.unwrap())
}
}))

View File

@ -56,7 +56,7 @@ impl JmapOp {
}
impl BackendOp for JmapOp {
fn as_bytes(&mut self) -> Result<&[u8]> {
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
if self.bytes.is_none() {
let mut store_lck = self.store.write().unwrap();
if !(store_lck.byte_cache.contains_key(&self.hash)
@ -86,7 +86,8 @@ impl BackendOp for JmapOp {
}
self.bytes = store_lck.byte_cache[&self.hash].bytes.clone();
}
Ok(&self.bytes.as_ref().unwrap().as_bytes())
let ret = self.bytes.as_ref().unwrap().as_bytes().to_vec();
Ok(Box::pin(async move { Ok(ret) }))
}
fn fetch_flags(&self) -> ResultFuture<Flag> {

View File

@ -90,12 +90,13 @@ impl MaildirOp {
}
impl<'a> BackendOp for MaildirOp {
fn as_bytes(&mut self) -> Result<&[u8]> {
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
if self.slice.is_none() {
self.slice = Some(Mmap::open_path(self.path(), Protection::Read)?);
}
/* Unwrap is safe since we use ? above. */
Ok(unsafe { self.slice.as_ref().unwrap().as_slice() })
let ret = Ok((unsafe { self.slice.as_ref().unwrap().as_slice() }).to_vec());
Ok(Box::pin(async move { ret }))
}
fn fetch_flags(&self) -> ResultFuture<Flag> {

View File

@ -182,14 +182,16 @@ impl MboxOp {
}
impl BackendOp for MboxOp {
fn as_bytes(&mut self) -> Result<&[u8]> {
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
if self.slice.is_none() {
self.slice = Some(Mmap::open_path(&self.path, Protection::Read)?);
}
/* Unwrap is safe since we use ? above. */
Ok(unsafe {
let ret = Ok((unsafe {
&self.slice.as_ref().unwrap().as_slice()[self.offset..self.offset + self.length]
})
.to_vec());
Ok(Box::pin(async move { ret }))
}
fn fetch_flags(&self) -> ResultFuture<Flag> {

View File

@ -646,7 +646,7 @@ struct NotmuchOp {
}
impl BackendOp for NotmuchOp {
fn as_bytes(&mut self) -> Result<&[u8]> {
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
let mut message: *mut notmuch_message_t = std::ptr::null_mut();
let index_lck = self.index.write().unwrap();
unsafe {
@ -662,7 +662,8 @@ impl BackendOp for NotmuchOp {
let mut response = String::new();
f.read_to_string(&mut response)?;
self.bytes = Some(response);
Ok(self.bytes.as_ref().unwrap().as_bytes())
let ret = Ok(self.bytes.as_ref().unwrap().as_bytes().to_vec());
Ok(Box::pin(async move { ret }))
}
fn fetch_flags(&self) -> ResultFuture<Flag> {

View File

@ -229,8 +229,8 @@ impl Envelope {
pub fn from_token(mut operation: Box<dyn BackendOp>, hash: EnvelopeHash) -> Result<Envelope> {
let mut e = Envelope::new(hash);
e.flags = futures::executor::block_on(operation.fetch_flags()?)?;
let bytes = operation.as_bytes()?;
e.populate_headers(bytes)?;
let bytes = futures::executor::block_on(operation.as_bytes()?)?;
e.populate_headers(&bytes)?;
Ok(e)
}
@ -413,11 +413,6 @@ impl Envelope {
_strings.join(", ")
}
/// Requests bytes from backend and thus can fail
pub fn bytes(&self, mut operation: Box<dyn BackendOp>) -> Result<Vec<u8>> {
operation.as_bytes().map(|v| v.to_vec())
}
pub fn body_bytes(&self, bytes: &[u8]) -> Attachment {
let builder = AttachmentBuilder::new(bytes);
builder.build()
@ -439,8 +434,8 @@ impl Envelope {
/// Requests bytes from backend and thus can fail
pub fn body(&self, mut operation: Box<dyn BackendOp>) -> Result<Attachment> {
debug!("searching body for {:?}", self.message_id_display());
let file = operation.as_bytes()?;
Ok(self.body_bytes(file))
let bytes = futures::executor::block_on(operation.as_bytes()?)?;
Ok(self.body_bytes(&bytes))
}
pub fn subject(&self) -> Cow<str> {

View File

@ -138,8 +138,8 @@ impl Draft {
let mut ret = Draft::default();
//TODO: Inform user if error
{
let bytes = op.as_bytes().unwrap_or(&[]);
for (k, v) in envelope.headers(bytes).unwrap_or_else(|_| Vec::new()) {
let bytes = futures::executor::block_on(op.as_bytes()?)?;
for (k, v) in envelope.headers(&bytes).unwrap_or_else(|_| Vec::new()) {
if ignore_header(k.as_bytes()) {
continue;
}

View File

@ -238,9 +238,10 @@ impl Composer {
Some(NotificationType::ERROR),
));
}
Ok(mut op) => {
let parent_bytes = op.as_bytes();
ret.draft = Draft::new_reply(&parent_message, parent_bytes.unwrap());
Ok(op) => {
//FIXME
//let parent_bytes = op.as_bytes();
//ret.draft = Draft::new_reply(&parent_message, parent_bytes.unwrap());
}
}
let subject = parent_message.subject();

View File

@ -203,6 +203,8 @@ pub trait MailListingTrait: ListingTrait {
}
ListingAction::CopyTo(ref mailbox_path) => {
drop(envelope);
/*
* FIXME
match account
.mailbox_by_path(mailbox_path)
.and_then(|mailbox_hash| {
@ -219,10 +221,13 @@ pub trait MailListingTrait: ListingTrait {
}
Ok(fut) => {}
}
*/
continue;
}
ListingAction::CopyToOtherAccount(ref account_name, ref mailbox_path) => {
drop(envelope);
/*
* FIXME
if let Err(err) = op.as_bytes().map(|b| b.to_vec()).and_then(|bytes| {
let account_pos = context
.accounts
@ -245,10 +250,13 @@ pub trait MailListingTrait: ListingTrait {
));
return;
}
*/
continue;
}
ListingAction::MoveTo(ref mailbox_path) => {
drop(envelope);
/*
* FIXME
if let Err(err) =
account
.mailbox_by_path(mailbox_path)
@ -264,10 +272,12 @@ pub trait MailListingTrait: ListingTrait {
));
return;
}
*/
continue;
}
ListingAction::MoveToOtherAccount(ref account_name, ref mailbox_path) => {
drop(envelope);
/* FIXME
if let Err(err) = op
.as_bytes()
.map(|b| b.to_vec())
@ -302,10 +312,12 @@ pub trait MailListingTrait: ListingTrait {
));
return;
}
*/
continue;
}
ListingAction::Tag(Remove(ref tag_str)) => {
if let Err(err) = op.set_tag(tag_str.to_string(), false) {
match op.set_tag(tag_str.to_string(), false) {
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some("Could not set tag.".to_string()),
err.to_string(),
@ -313,9 +325,14 @@ pub trait MailListingTrait: ListingTrait {
));
return;
}
Ok(fut) => {
//FIXME
}
}
}
ListingAction::Tag(Add(ref tag_str)) => {
if let Err(err) = op.set_tag(tag_str.to_string(), true) {
match op.set_tag(tag_str.to_string(), true) {
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some("Could not set tag.".to_string()),
err.to_string(),
@ -323,6 +340,10 @@ pub trait MailListingTrait: ListingTrait {
));
return;
}
Ok(fut) => {
// FIXME
}
}
}
_ => unreachable!(),
}

View File

@ -214,7 +214,7 @@ impl MailListingTrait for PlainListing {
let env_hash = self.get_env_under_cursor(self.cursor_pos.2, context);
let temp = (self.new_cursor_pos.0, self.new_cursor_pos.1, env_hash);
if !force && old_cursor_pos == self.new_cursor_pos {
self.view.update(temp);
self.view.update(temp, context);
} else if self.unfocused {
self.view = MailView::new(temp, None, None, context);
}

View File

@ -632,7 +632,7 @@ impl Component for ThreadListing {
);
if let Some(ref mut v) = self.view {
v.update(coordinates);
v.update(coordinates, context);
} else {
self.view = Some(MailView::new(coordinates, None, None, context));
}

View File

@ -20,9 +20,13 @@
*/
use super::*;
use crate::conf::accounts::JobRequest;
use crate::jobs::{oneshot, JobId};
use melib::list_management;
use melib::parser::BytesExt;
use smallvec::SmallVec;
use std::collections::HashSet;
use std::io::Write;
use std::convert::TryFrom;
use std::process::{Command, Stdio};
@ -98,11 +102,31 @@ pub struct MailView {
headers_cursor: usize,
force_draw_headers: bool,
theme_default: ThemeAttribute,
active_jobs: HashSet<JobId>,
state: MailViewState,
cmd_buf: String,
id: ComponentId,
}
#[derive(Debug)]
pub enum MailViewState {
Init,
LoadingBody {
job_id: JobId,
chan: oneshot::Receiver<Result<Vec<u8>>>,
},
Loaded {
body: Result<Vec<u8>>,
},
}
impl Default for MailViewState {
fn default() -> Self {
MailViewState::Init
}
}
impl Clone for MailView {
fn clone(&self) -> Self {
MailView {
@ -111,6 +135,8 @@ impl Clone for MailView {
pager: self.pager.clone(),
mode: ViewMode::Normal,
attachment_tree: self.attachment_tree.clone(),
state: MailViewState::default(),
active_jobs: self.active_jobs.clone(),
..*self
}
}
@ -129,9 +155,9 @@ impl MailView {
coordinates: (usize, MailboxHash, EnvelopeHash),
pager: Option<Pager>,
subview: Option<Box<dyn Component>>,
context: &Context,
context: &mut Context,
) -> Self {
MailView {
let mut ret = MailView {
coordinates,
pager: pager.unwrap_or_default(),
subview,
@ -146,9 +172,61 @@ impl MailView {
force_draw_headers: false,
theme_default: crate::conf::value(context, "mail.view.body"),
active_jobs: Default::default(),
state: MailViewState::default(),
cmd_buf: String::with_capacity(4),
id: ComponentId::new_v4(),
};
ret.init_futures(context);
ret
}
fn init_futures(&mut self, context: &mut Context) {
debug!("init_futures");
let account = &mut context.accounts[self.coordinates.0];
if debug!(account.contains_key(self.coordinates.2)) {
{
match account
.operation(self.coordinates.2)
.and_then(|mut op| op.as_bytes())
{
Ok(fut) => {
let (chan, job_id) = account.job_executor.spawn_specialized(fut);
debug!(&job_id);
self.active_jobs.insert(job_id.clone());
account.active_jobs.insert(job_id, JobRequest::AsBytes);
self.state = MailViewState::LoadingBody { job_id, chan };
}
Err(err) => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!("Could not get message: {}", err)),
));
}
}
}
if !account.collection.get_env(self.coordinates.2).is_seen() {
match account
.operation(self.coordinates.2)
.and_then(|mut op| op.set_flag(Flag::SEEN, true))
{
Ok(fut) => {
let (rcvr, job_id) = account.job_executor.spawn_specialized(fut);
account
.active_jobs
.insert(job_id, JobRequest::SetFlags(self.coordinates.2, rcvr));
}
Err(e) => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"Could not set message as seen: {}",
e
)),
));
}
}
}
}
}
@ -164,8 +242,6 @@ impl MailView {
body,
Some(Box::new(move |a: &'closure Attachment, v: &mut Vec<u8>| {
if a.content_type().is_text_html() {
use std::io::Write;
/* FIXME: duplication with view/html.rs */
if let Some(filter_invocation) =
mailbox_settings!(context[coordinates.0][&coordinates.1].pager.html_filter)
@ -285,12 +361,187 @@ impl MailView {
}
}
pub fn update(&mut self, new_coordinates: (usize, MailboxHash, EnvelopeHash)) {
pub fn update(
&mut self,
new_coordinates: (usize, MailboxHash, EnvelopeHash),
context: &mut Context,
) {
self.coordinates = new_coordinates;
self.mode = ViewMode::Normal;
self.initialised = false;
self.init_futures(context);
self.set_dirty(true);
}
fn open_attachment(&mut self, lidx: usize, context: &mut Context, use_mailcap: bool) {
let attachments = if let MailViewState::Loaded { ref body } = self.state {
match body {
Ok(body) => AttachmentBuilder::new(body).build().attachments(),
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
err.to_string(),
Some(NotificationType::ERROR),
));
log(
format!("Failed to open envelope: {}", err.to_string()),
ERROR,
);
return;
}
}
} else {
return;
};
if let Some(u) = attachments.get(lidx) {
if use_mailcap {
if let Ok(()) = crate::mailcap::MailcapEntry::execute(u, context) {
self.set_dirty(true);
} else {
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(format!(
"no mailcap entry found for {}",
u.content_type()
))));
}
} else {
match u.content_type() {
ContentType::MessageRfc822 => {
match EnvelopeWrapper::new(u.body().to_vec()) {
Ok(wrapper) => {
context.replies.push_back(UIEvent::Action(Tab(New(Some(
Box::new(EnvelopeView::new(
wrapper,
None,
None,
self.coordinates.0,
)),
)))));
}
Err(e) => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!("{}", e)),
));
}
}
return;
}
ContentType::Text { .. } | ContentType::PGPSignature => {
self.mode = ViewMode::Attachment(lidx);
self.initialised = false;
self.dirty = true;
}
ContentType::Multipart { .. } => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(
"Multipart attachments are not supported yet.".to_string(),
),
));
return;
}
ContentType::Other { ref name, .. } => {
let attachment_type = u.mime_type();
let binary = query_default_app(&attachment_type);
let mut name_opt = name.as_ref().and_then(|n| {
melib::email::parser::encodings::phrase(n.as_bytes(), false)
.map(|(_, v)| v)
.ok()
.and_then(|n| String::from_utf8(n).ok())
});
if name_opt.is_none() {
name_opt = name.as_ref().map(|n| n.clone());
}
if let Ok(binary) = binary {
let p = create_temp_file(
&decode(u, None),
name_opt.as_ref().map(String::as_str),
None,
true,
);
match debug!(context.plugin_manager.activate_hook(
"attachment-view",
p.path().display().to_string().into_bytes()
)) {
Ok(crate::plugins::FilterResult::Ansi(s)) => {
if let Some(buf) = crate::terminal::ansi::ansi_to_cellbuffer(&s)
{
let raw_buf = RawBuffer::new(buf, name_opt);
self.mode = ViewMode::Ansi(raw_buf);
self.initialised = false;
self.dirty = true;
return;
}
}
Ok(crate::plugins::FilterResult::UiMessage(s)) => {
context.replies.push_back(UIEvent::Notification(
None,
s,
Some(NotificationType::ERROR),
));
}
_ => {}
}
match Command::new(&binary)
.arg(p.path())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
{
Ok(child) => {
context.temp_files.push(p);
context.children.push(child);
}
Err(err) => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"Failed to start {}: {}",
binary.display(),
err
)),
));
}
}
} else {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(if name.is_some() {
format!(
"Couldn't find a default application for file {} (type {})",
name.as_ref().unwrap(),
attachment_type
)
} else {
format!(
"Couldn't find a default application for type {}",
attachment_type
)
}),
));
return;
}
}
ContentType::OctetStream { ref name } => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"Failed to open {}. application/octet-stream isn't supported yet",
name.as_ref().map(|n| n.as_str()).unwrap_or("file")
)),
));
return;
}
}
}
} else {
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(format!(
"Attachment `{}` not found.",
lidx
))));
return;
}
}
}
impl Component for MailView {
@ -302,32 +553,13 @@ impl Component for MailView {
let bottom_right = bottom_right!(area);
let y: usize = {
let account = &mut context.accounts[self.coordinates.0];
let account = &context.accounts[self.coordinates.0];
if !account.contains_key(self.coordinates.2) {
/* The envelope has been renamed or removed, so wait for the appropriate event to
* arrive */
self.dirty = false;
return;
}
let (hash, is_seen) = {
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
(envelope.hash(), envelope.is_seen())
};
if !is_seen {
if let Err(e) = account.operation(hash).and_then(|op| {
let mut envelope: EnvelopeRefMut =
account.collection.get_env_mut(self.coordinates.2);
envelope.set_seen(op)
}) {
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(format!(
"Could not set message as seen: {}",
e
))));
}
}
let account = &context.accounts[self.coordinates.0];
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
let headers = crate::conf::value(context, "mail.view.headers");
@ -550,14 +782,8 @@ impl Component for MailView {
};
if !self.initialised {
self.initialised = true;
let body = {
let account = &mut context.accounts[self.coordinates.0];
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
match account
.operation(envelope.hash())
.and_then(|op| envelope.body(op))
{
let bytes = if let MailViewState::Loaded { ref body } = self.state {
match body {
Ok(body) => body,
Err(e) => {
clear_area(
@ -573,18 +799,23 @@ impl Component for MailView {
e.to_string(),
Some(NotificationType::ERROR),
));
log(
format!(
"Failed to open envelope {}: {}",
envelope.message_id_display(),
e.to_string()
),
ERROR,
);
log(format!("Failed to open envelope: {}", e.to_string()), ERROR);
return;
}
}
} else {
clear_area(
grid,
(set_y(upper_left, y), bottom_right),
self.theme_default,
);
context
.dirty_areas
.push_back((set_y(upper_left, y), bottom_right));
return;
};
self.initialised = true;
let body = AttachmentBuilder::new(bytes).build();
match self.mode {
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
let attachment = &body.attachments()[aidx];
@ -631,32 +862,13 @@ impl Component for MailView {
ViewMode::Subview | ViewMode::ContactSelector(_) => {}
ViewMode::Source(source) => {
let text = {
let account = &context.accounts[self.coordinates.0];
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
let mut op = match account.operation(envelope.hash()) {
Ok(op) => op,
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
err.to_string(),
Some(NotificationType::ERROR),
));
return;
}
};
if source == Source::Raw {
op.as_bytes()
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_else(|e| e.to_string())
String::from_utf8_lossy(bytes).into_owned()
} else {
/* Decode each header value */
let mut ret = op
.as_bytes()
.and_then(|b| {
melib::email::parser::headers::headers(b)
let mut ret = melib::email::parser::headers::headers(bytes)
.map(|(_, v)| v)
.map_err(|err| err.into())
})
.and_then(|headers| {
Ok(headers
.into_iter()
@ -675,9 +887,7 @@ impl Component for MailView {
.join(&b"\n"[..]))
})
.map(|v| String::from_utf8_lossy(&v).into_owned())
.unwrap_or_else(|e| e.to_string());
drop(envelope);
drop(account);
.unwrap_or_else(|err: MeliError| err.to_string());
ret.push_str("\n\n");
ret.extend(self.attachment_to_text(&body, context).chars());
ret
@ -810,6 +1020,27 @@ impl Component for MailView {
self.pager.set_dirty(true);
return true;
}
UIEvent::StatusEvent(StatusEvent::JobFinished(ref job_id))
if self.active_jobs.contains(job_id) =>
{
match self.state {
MailViewState::LoadingBody {
job_id: ref id,
ref mut chan,
} if job_id == id => {
let bytes_result = chan.try_recv().unwrap().unwrap();
debug!("bytes_result");
self.state = MailViewState::Loaded { body: bytes_result };
}
MailViewState::Init => {
self.init_futures(context);
}
_ => {}
}
self.active_jobs.remove(job_id);
self.set_dirty(true);
return true;
}
_ => {
if self.pager.process_event(event, context) {
return true;
@ -933,56 +1164,17 @@ impl Component for MailView {
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
{
let account = &mut context.accounts[self.coordinates.0];
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
let attachments = match account
.operation(envelope.hash())
.and_then(|op| envelope.body(op))
{
Ok(body) => body.attachments(),
Err(e) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
e.to_string(),
Some(NotificationType::ERROR),
));
log(
format!(
"Failed to open envelope {}: {}",
envelope.message_id_display(),
e.to_string()
),
ERROR,
);
return true;
match self.state {
MailViewState::LoadingBody { .. } => {}
MailViewState::Loaded { .. } => {
self.open_attachment(lidx, context, true);
}
};
drop(envelope);
drop(account);
if let Some(u) = attachments.get(lidx) {
if let Ok(()) = crate::mailcap::MailcapEntry::execute(u, context) {
self.set_dirty(true);
} else {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"no mailcap entry found for {}",
u.content_type()
)),
));
MailViewState::Init => {
self.init_futures(context);
}
} else {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"Attachment `{}` not found.",
lidx
)),
));
}
return true;
}
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[MailView::DESCRIPTION]["open_attachment"])
&& !self.cmd_buf.is_empty()
@ -993,171 +1185,17 @@ impl Component for MailView {
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
{
let account = &mut context.accounts[self.coordinates.0];
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
let attachments = match account
.operation(envelope.hash())
.and_then(|op| envelope.body(op))
{
Ok(body) => body.attachments(),
Err(e) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
e.to_string(),
Some(NotificationType::ERROR),
));
log(
format!(
"Failed to open envelope {}: {}",
envelope.message_id_display(),
e.to_string()
),
ERROR,
);
return true;
match self.state {
MailViewState::LoadingBody { .. } => {}
MailViewState::Loaded { .. } => {
self.open_attachment(lidx, context, false);
}
};
if let Some(u) = attachments.get(lidx) {
match u.content_type() {
ContentType::MessageRfc822 => {
match EnvelopeWrapper::new(u.body().to_vec()) {
Ok(wrapper) => {
context.replies.push_back(UIEvent::Action(Tab(New(Some(
Box::new(EnvelopeView::new(
wrapper,
None,
None,
self.coordinates.0,
)),
)))));
}
Err(e) => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!("{}", e)),
));
MailViewState::Init => {
self.init_futures(context);
}
}
return true;
}
ContentType::Text { .. } | ContentType::PGPSignature => {
self.mode = ViewMode::Attachment(lidx);
self.initialised = false;
self.dirty = true;
}
ContentType::Multipart { .. } => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(
"Multipart attachments are not supported yet.".to_string(),
),
));
return true;
}
ContentType::Other { ref name, .. } => {
let attachment_type = u.mime_type();
let binary = query_default_app(&attachment_type);
let mut name_opt = name.as_ref().and_then(|n| {
melib::email::parser::encodings::phrase(n.as_bytes(), false)
.map(|(_, v)| v)
.ok()
.and_then(|n| String::from_utf8(n).ok())
});
if name_opt.is_none() {
name_opt = name.as_ref().map(|n| n.clone());
}
if let Ok(binary) = binary {
let p = create_temp_file(
&decode(u, None),
name_opt.as_ref().map(String::as_str),
None,
true,
);
match debug!(context.plugin_manager.activate_hook(
"attachment-view",
p.path().display().to_string().into_bytes()
)) {
Ok(crate::plugins::FilterResult::Ansi(s)) => {
if let Some(buf) =
crate::terminal::ansi::ansi_to_cellbuffer(&s)
{
let raw_buf = RawBuffer::new(buf, name_opt);
self.mode = ViewMode::Ansi(raw_buf);
self.initialised = false;
self.dirty = true;
return true;
}
}
Ok(crate::plugins::FilterResult::UiMessage(s)) => {
context.replies.push_back(UIEvent::Notification(
None,
s,
Some(NotificationType::ERROR),
));
}
_ => {}
}
match Command::new(&binary)
.arg(p.path())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
{
Ok(child) => {
context.temp_files.push(p);
context.children.push(child);
}
Err(err) => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"Failed to start {}: {}",
binary.display(),
err
)),
));
}
}
} else {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(if name.is_some() {
format!(
"Couldn't find a default application for file {} (type {})",
name.as_ref().unwrap(), attachment_type
)
} else {
format!( "Couldn't find a default application for type {}", attachment_type)
}
,
)));
return true;
}
}
ContentType::OctetStream { ref name } => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(
format!(
"Failed to open {}. application/octet-stream isn't supported yet",
name.as_ref().map(|n| n.as_str()).unwrap_or("file")
)
),
));
return true;
}
}
} else {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"Attachment `{}` not found.",
lidx
)),
));
return true;
}
};
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[MailView::DESCRIPTION]["toggle_expand_headers"]) =>
{
@ -1175,29 +1213,25 @@ impl Component for MailView {
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
match self.state {
MailViewState::Init => {
self.init_futures(context);
}
MailViewState::LoadingBody { .. } => {}
MailViewState::Loaded { ref body } => {
let url = {
let account = &mut context.accounts[self.coordinates.0];
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
let finder = LinkFinder::new();
let t = match account
.operation(envelope.hash())
.and_then(|op| envelope.body(op))
{
Ok(body) => body.text().to_string(),
let t = match body {
Ok(body) => {
AttachmentBuilder::new(&body).build().text().to_string()
}
Err(e) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
e.to_string(),
Some(NotificationType::ERROR),
));
log(
format!(
"Failed to open envelope {}: {}",
envelope.message_id_display(),
e.to_string()
),
ERROR,
);
log(e.to_string(), ERROR);
return true;
}
};
@ -1206,7 +1240,10 @@ impl Component for MailView {
u.as_str().to_string()
} else {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!("Link `{}` not found.", lidx)),
StatusEvent::DisplayMessage(format!(
"Link `{}` not found.",
lidx
)),
));
return true;
}
@ -1229,6 +1266,8 @@ impl Component for MailView {
));
}
}
}
}
return true;
}
UIEvent::Input(ref key)
@ -1248,11 +1287,15 @@ impl Component for MailView {
self.coordinates.2 = new_hash;
}
UIEvent::Action(View(ViewAction::SaveAttachment(a_i, ref path))) => {
use std::io::Write;
let account = &mut context.accounts[self.coordinates.0];
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
let mut op = match account.operation(envelope.hash()) {
Ok(b) => b,
let account = &context.accounts[self.coordinates.0];
if !account.contains_key(self.coordinates.2) {
/* The envelope has been renamed or removed, so wait for the appropriate event to
* arrive */
return true;
}
let bytes = if let MailViewState::Loaded { ref body } = self.state {
match body {
Ok(body) => body,
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
@ -1260,42 +1303,23 @@ impl Component for MailView {
Some(NotificationType::ERROR),
));
log(
format!(
"Failed to open envelope {}: {}",
envelope.message_id_display(),
err.to_string()
),
format!("Failed to open envelope: {}", err.to_string()),
ERROR,
);
return true;
}
}
} else {
return true;
};
if a_i == 0 {
let mut path = std::path::Path::new(path).to_path_buf();
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
// Save entire message as eml
if path.is_dir() {
path.push(format!("{}.eml", envelope.message_id_display()));
}
let bytes = match op.as_bytes() {
Ok(b) => b,
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
err.to_string(),
Some(NotificationType::ERROR),
));
log(
format!(
"Failed to open envelope {}: {}",
envelope.message_id_display(),
err.to_string()
),
ERROR,
);
return true;
}
};
let mut f = match std::fs::File::create(&path) {
Err(err) => {
context.replies.push_back(UIEvent::Notification(
@ -1333,25 +1357,7 @@ impl Component for MailView {
return true;
}
let attachments = match envelope.body(op) {
Ok(body) => body.attachments(),
Err(e) => {
context.replies.push_back(UIEvent::Notification(
Some("Failed to open e-mail".to_string()),
e.to_string(),
Some(NotificationType::ERROR),
));
log(
format!(
"Failed to open envelope {}: {}",
envelope.message_id_display(),
e.to_string()
),
ERROR,
);
return true;
}
};
let attachments = AttachmentBuilder::new(bytes).build().attachments();
if let Some(u) = attachments.get(a_i) {
match u.content_type() {
ContentType::MessageRfc822
@ -1440,7 +1446,6 @@ impl Component for MailView {
}
}
UIEvent::Action(MailingListAction(ref e)) => {
let unsafe_context = context as *mut Context;
let account = &context.accounts[self.coordinates.0];
if !account.contains_key(self.coordinates.2) {
/* The envelope has been renamed or removed, so wait for the appropriate event to
@ -1448,13 +1453,14 @@ impl Component for MailView {
return true;
}
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
if let Some(actions) = list_management::ListActions::detect(&envelope) {
let detect = list_management::ListActions::detect(&envelope);
if let Some(ref actions) = detect {
match e {
MailingListAction::ListPost if actions.post.is_some() => {
/* open composer */
let mut failure = true;
if let list_management::ListAction::Email(list_post_addr) =
actions.post.unwrap()[0]
actions.post.as_ref().unwrap()[0]
{
if let Ok(mailto) = Mailto::try_from(list_post_addr) {
let draft: Draft = mailto.into();
@ -1476,12 +1482,12 @@ impl Component for MailView {
}
MailingListAction::ListUnsubscribe if actions.unsubscribe.is_some() => {
/* autosend or open unsubscribe option*/
let unsubscribe = actions.unsubscribe.unwrap();
for option in unsubscribe {
let unsubscribe = actions.unsubscribe.as_ref().unwrap();
for option in unsubscribe.iter() {
/* TODO: Ask for confirmation before proceding with an action */
match option {
list_management::ListAction::Email(email) => {
if let Ok(mailto) = Mailto::try_from(email) {
if let Ok(mailto) = Mailto::try_from(*email) {
let mut draft: Draft = mailto.into();
draft.headers_mut().insert(
"From".into(),
@ -1490,15 +1496,14 @@ impl Component for MailView {
self.coordinates.0,
),
);
/* Manually drop stuff because borrowck doesn't do it
* on its own */
drop(detect);
drop(envelope);
drop(account);
return super::compose::send_draft(
ToggleFlag::False,
/* FIXME: refactor to avoid unsafe.
*
* actions contains byte slices from the envelope's
* headers send_draft only needs a mut ref for
* context to push back replies and save the sent
* message */
unsafe { &mut *(unsafe_context) },
context,
self.coordinates.0,
draft,
SpecialUsageMailbox::Sent,
@ -1564,6 +1569,7 @@ impl Component for MailView {
}
false
}
fn is_dirty(&self) -> bool {
self.dirty
|| self.pager.is_dirty()
@ -1576,6 +1582,7 @@ impl Component for MailView {
false
}
}
fn set_dirty(&mut self, value: bool) {
self.dirty = value;
match self.mode {
@ -1590,6 +1597,7 @@ impl Component for MailView {
_ => {}
}
}
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
let mut map = if let Some(ref sbv) = self.subview {
sbv.get_shortcuts(context)

View File

@ -95,7 +95,6 @@ impl EnvelopeView {
&body,
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
if a.content_type().is_text_html() {
use std::io::Write;
let settings = &context.settings;
if let Some(filter_invocation) = settings.pager.html_filter.as_ref() {
let command_obj = Command::new("sh")

View File

@ -961,7 +961,7 @@ impl Component for ThreadView {
self.coordinates.1,
self.entries[self.current_pos()].msg_hash,
);
self.mailview.update(coordinates);
self.mailview.update(coordinates, context);
}
if self.entries.len() == 1 {

View File

@ -156,6 +156,7 @@ pub enum JobRequest {
DeleteMailbox(oneshot::Receiver<Result<HashMap<MailboxHash, Mailbox>>>),
//RenameMailbox,
Search,
AsBytes,
SetMailboxPermissions(MailboxHash, oneshot::Receiver<Result<()>>),
SetMailboxSubscription(MailboxHash, oneshot::Receiver<Result<()>>),
Watch(JoinHandle),
@ -175,6 +176,7 @@ impl core::fmt::Debug for JobRequest {
JobRequest::DeleteMailbox(_) => write!(f, "{}", "JobRequest::DeleteMailbox"),
//JobRequest::RenameMailbox,
JobRequest::Search => write!(f, "{}", "JobRequest::Search"),
JobRequest::AsBytes => write!(f, "{}", "JobRequest::AsBytes"),
JobRequest::SetMailboxPermissions(_, _) => {
write!(f, "{}", "JobRequest::SetMailboxPermissions")
}
@ -1599,7 +1601,7 @@ impl Account {
}
}
//JobRequest::RenameMailbox,
JobRequest::Search => {
JobRequest::Search | JobRequest::AsBytes => {
self.sender
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
StatusEvent::JobFinished(job_id.clone()),

View File

@ -296,22 +296,24 @@ struct PluginOp {
}
impl BackendOp for PluginOp {
fn as_bytes(&mut self) -> Result<&[u8]> {
if let Some(ref bytes) = self.bytes {
return Ok(bytes.as_bytes());
}
if let Ok(mut channel) = self.channel.try_lock() {
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
let hash = self.hash;
let channel = self.channel.clone();
Ok(Box::pin(async move {
if let Ok(mut channel) = channel.try_lock() {
channel.write_ref(&rmpv::ValueRef::Ext(BACKEND_OP_FN, b"as_bytes"))?;
debug!(channel.expect_ack())?;
channel.write_ref(&rmpv::ValueRef::Integer(self.hash.into()))?;
channel.write_ref(&rmpv::ValueRef::Integer(hash.into()))?;
debug!(channel.expect_ack())?;
let bytes: Result<PluginResult<String>> = channel.from_read();
self.bytes = Some(bytes.map(Into::into).and_then(std::convert::identity)?);
Ok(self.bytes.as_ref().map(String::as_bytes).unwrap())
Ok(bytes
.map(Into::into)
.and_then(std::convert::identity)?
.into_bytes())
} else {
Err(MeliError::new("busy"))
}
}))
}
fn fetch_flags(&self) -> ResultFuture<Flag> {