Move backend logic to backend and keep Envelope abstract
parent
e316640f68
commit
5889494e9e
|
@ -48,7 +48,7 @@ pub struct Account {
|
|||
|
||||
impl Account {
|
||||
pub fn new(name: String, settings: AccountSettings, map: &Backends) -> Self {
|
||||
let backend = map.get(settings.format())(&settings);
|
||||
let mut backend = map.get(settings.format())(&settings);
|
||||
let ref_folders: Vec<Folder> = backend.folders();
|
||||
let mut folders: Vec<Option<Result<Mailbox>>> = Vec::with_capacity(ref_folders.len());
|
||||
let mut workers: Vec<Worker> = Vec::new();
|
||||
|
|
|
@ -23,7 +23,7 @@ use async::*;
|
|||
use conf::AccountSettings;
|
||||
use error::{MeliError, Result};
|
||||
use mailbox::backends::{
|
||||
BackendFolder, BackendOp, BackendOpGenerator, Folder, MailBackend, RefreshEvent,
|
||||
BackendFolder, BackendOp, Folder, MailBackend, RefreshEvent,
|
||||
RefreshEventConsumer,
|
||||
};
|
||||
use mailbox::email::parser;
|
||||
|
@ -43,41 +43,52 @@ extern crate crossbeam;
|
|||
use memmap::{Mmap, Protection};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::fs;
|
||||
use std::sync::{Mutex, Arc};
|
||||
use std::hash::Hasher;
|
||||
use std::path::{Path, PathBuf};
|
||||
extern crate fnv;
|
||||
use self::fnv::FnvHashMap;
|
||||
|
||||
/// `BackendOp` implementor for Maildir
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug)]
|
||||
pub struct MaildirOp {
|
||||
path: String,
|
||||
hash_index: Arc<Mutex<FnvHashMap<u64, (usize, String)>>>,
|
||||
hash: u64,
|
||||
slice: Option<Mmap>,
|
||||
}
|
||||
|
||||
impl Clone for MaildirOp {
|
||||
fn clone(&self) -> Self {
|
||||
MaildirOp {
|
||||
path: self.path.clone(),
|
||||
hash_index: self.hash_index.clone(),
|
||||
hash: self.hash.clone(),
|
||||
slice: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaildirOp {
|
||||
pub fn new(path: String) -> Self {
|
||||
pub fn new(hash: u64, hash_index: Arc<Mutex<FnvHashMap<u64, (usize, String)>>>) -> Self {
|
||||
MaildirOp {
|
||||
path: path,
|
||||
hash_index,
|
||||
hash,
|
||||
slice: None,
|
||||
}
|
||||
}
|
||||
fn path(&self) -> String {
|
||||
let hash_index = self.hash_index.clone();
|
||||
let map = hash_index.lock().unwrap();
|
||||
map.get(&self.hash).unwrap().1.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendOp for MaildirOp {
|
||||
impl<'a> BackendOp for MaildirOp {
|
||||
fn description(&self) -> String {
|
||||
format!("Path of file: {}", self.path)
|
||||
format!("Path of file:")// self.0ipath)
|
||||
}
|
||||
fn as_bytes(&mut self) -> Result<&[u8]> {
|
||||
if self.slice.is_none() {
|
||||
self.slice = Some(Mmap::open_path(self.path.to_string(), Protection::Read)?);
|
||||
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() })
|
||||
|
@ -94,13 +105,12 @@ impl BackendOp for MaildirOp {
|
|||
}
|
||||
fn fetch_flags(&self) -> Flag {
|
||||
let mut flag = Flag::default();
|
||||
let path = PathBuf::from(&self.path);
|
||||
let filename = path.file_name().unwrap().to_str().unwrap();
|
||||
if !filename.contains(":2,") {
|
||||
let path = self.path();
|
||||
if !path.contains(":2,") {
|
||||
return flag;
|
||||
}
|
||||
|
||||
for f in filename.chars().rev() {
|
||||
for f in path.chars().rev() {
|
||||
match f {
|
||||
',' => break,
|
||||
'D' => flag |= Flag::DRAFT,
|
||||
|
@ -115,13 +125,18 @@ impl BackendOp for MaildirOp {
|
|||
|
||||
flag
|
||||
}
|
||||
|
||||
fn set_flag(&mut self, envelope: &mut Envelope, f: &Flag) -> Result<()> {
|
||||
let idx: usize = self.path.rfind(":2,").ok_or(MeliError::new(format!(
|
||||
"Invalid email filename: {:?}",
|
||||
self
|
||||
)))? + 3;
|
||||
let mut new_name: String = self.path[..idx].to_string();
|
||||
let path = self.path();
|
||||
let idx: usize = path.rfind(":2,").ok_or(MeliError::new(format!(
|
||||
"Invalid email filename: {:?}",
|
||||
self
|
||||
)))? + 3;
|
||||
let mut new_name: String = path[..idx].to_string();
|
||||
let mut flags = self.fetch_flags();
|
||||
if !(flags & *f).is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
flags.toggle(*f);
|
||||
if !(flags & Flag::DRAFT).is_empty() {
|
||||
new_name.push('D');
|
||||
|
@ -142,10 +157,11 @@ impl BackendOp for MaildirOp {
|
|||
new_name.push('T');
|
||||
}
|
||||
|
||||
fs::rename(&self.path, &new_name)?;
|
||||
envelope.set_operation_token(Box::new(BackendOpGenerator::new(Box::new(move || {
|
||||
Box::new(MaildirOp::new(new_name.clone()))
|
||||
}))));
|
||||
fs::rename(&path, &new_name)?;
|
||||
let hash = envelope.hash();
|
||||
let hash_index = self.hash_index.clone();
|
||||
let mut map = hash_index.lock().unwrap();
|
||||
map.get_mut(&hash).unwrap().1 = new_name;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -154,6 +170,8 @@ impl BackendOp for MaildirOp {
|
|||
#[derive(Debug)]
|
||||
pub struct MaildirType {
|
||||
folders: Vec<MaildirFolder>,
|
||||
hash_index: Arc<Mutex<FnvHashMap<u64, (usize, String)>>>,
|
||||
|
||||
path: String,
|
||||
}
|
||||
|
||||
|
@ -161,7 +179,7 @@ impl MailBackend for MaildirType {
|
|||
fn folders(&self) -> Vec<Folder> {
|
||||
self.folders.iter().map(|f| f.clone()).collect()
|
||||
}
|
||||
fn get(&self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
self.multicore(4, folder)
|
||||
}
|
||||
fn watch(&self, sender: RefreshEventConsumer) -> Result<()> {
|
||||
|
@ -188,25 +206,25 @@ impl MailBackend for MaildirType {
|
|||
match rx.recv() {
|
||||
Ok(event) => match event {
|
||||
DebouncedEvent::Create(mut pathbuf)
|
||||
| DebouncedEvent::Remove(mut pathbuf) => {
|
||||
let path = if pathbuf.is_dir() {
|
||||
if pathbuf.ends_with("cur") | pathbuf.ends_with("new") {
|
||||
| DebouncedEvent::Remove(mut pathbuf) => {
|
||||
let path = if pathbuf.is_dir() {
|
||||
if pathbuf.ends_with("cur") | pathbuf.ends_with("new") {
|
||||
pathbuf.pop();
|
||||
}
|
||||
pathbuf.to_str().unwrap()
|
||||
} else {
|
||||
pathbuf.pop();
|
||||
}
|
||||
pathbuf.to_str().unwrap()
|
||||
} else {
|
||||
pathbuf.pop();
|
||||
pathbuf.parent().unwrap().to_str().unwrap()
|
||||
};
|
||||
eprintln!(" got event in {}", path);
|
||||
pathbuf.parent().unwrap().to_str().unwrap()
|
||||
};
|
||||
eprintln!(" got event in {}", path);
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
hasher.write(path.as_bytes());
|
||||
sender.send(RefreshEvent {
|
||||
folder: format!("{}", path),
|
||||
hash: hasher.finish(),
|
||||
});
|
||||
}
|
||||
let mut hasher = DefaultHasher::new();
|
||||
hasher.write(path.as_bytes());
|
||||
sender.send(RefreshEvent {
|
||||
folder: format!("{}", path),
|
||||
hash: hasher.finish(),
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Err(e) => eprintln!("watch error: {:?}", e),
|
||||
|
@ -215,6 +233,9 @@ impl MailBackend for MaildirType {
|
|||
})?;
|
||||
Ok(())
|
||||
}
|
||||
fn operation(&self, hash: u64) -> Box<BackendOp> {
|
||||
Box::new(MaildirOp::new(hash, self.hash_index.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl MaildirType {
|
||||
|
@ -235,7 +256,7 @@ impl MaildirType {
|
|||
path.to_str().unwrap().to_string(),
|
||||
path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
path_children,
|
||||
) {
|
||||
) {
|
||||
folders.push(f);
|
||||
children.push(folders.len() - 1);
|
||||
}
|
||||
|
@ -251,13 +272,14 @@ impl MaildirType {
|
|||
path.to_str().unwrap().to_string(),
|
||||
path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
Vec::with_capacity(0),
|
||||
) {
|
||||
) {
|
||||
folders.push(f);
|
||||
}
|
||||
}
|
||||
folders[0].children = recurse_folders(&mut folders, &path);
|
||||
MaildirType {
|
||||
folders,
|
||||
hash_index: Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher(0, Default::default()))),
|
||||
path: f.root_folder().to_string(),
|
||||
}
|
||||
}
|
||||
|
@ -270,7 +292,7 @@ impl MaildirType {
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn multicore(&self, cores: usize, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
pub fn multicore(&mut self, cores: usize, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
let mut w = AsyncBuilder::new();
|
||||
let handle = {
|
||||
let tx = w.tx();
|
||||
|
@ -278,6 +300,8 @@ impl MaildirType {
|
|||
let folder: &MaildirFolder = &self.folders[self.owned_folder_idx(folder)];
|
||||
let path = folder.path().to_string();
|
||||
let name = format!("parsing {:?}", folder.name());
|
||||
let map = self.hash_index.clone();
|
||||
let map2 = self.hash_index.clone();
|
||||
|
||||
thread::Builder::new()
|
||||
.name(name)
|
||||
|
@ -305,25 +329,48 @@ impl MaildirType {
|
|||
};
|
||||
for chunk in files.chunks(chunk_size) {
|
||||
let mut tx = tx.clone();
|
||||
let map = map.clone();
|
||||
let s = scope.spawn(move || {
|
||||
let len = chunk.len();
|
||||
let size = if len <= 100 { 100 } else { (len / 100) * 100 };
|
||||
let mut local_r: Vec<
|
||||
Envelope,
|
||||
> = Vec::with_capacity(chunk.len());
|
||||
let mut local_r: Vec<Envelope> = Vec::with_capacity(chunk.len());
|
||||
for c in chunk.chunks(size) {
|
||||
let map = map.clone();
|
||||
let len = c.len();
|
||||
for e in c {
|
||||
let e_copy = e.to_string();
|
||||
if let Some(mut e) = Envelope::from_token(Box::new(
|
||||
BackendOpGenerator::new(Box::new(move || {
|
||||
Box::new(MaildirOp::new(e_copy.clone()))
|
||||
})),
|
||||
)) {
|
||||
if e.populate_headers().is_err() {
|
||||
continue;
|
||||
}
|
||||
local_r.push(e);
|
||||
for file in c {
|
||||
let e_copy = file.to_string();
|
||||
/*
|
||||
* get hash
|
||||
* lock map
|
||||
* see if its inside otherwise add it
|
||||
* check cache
|
||||
* generate Envelope
|
||||
* add to local_r
|
||||
*/
|
||||
{
|
||||
let mut hasher = DefaultHasher::new();
|
||||
let hash = {
|
||||
let slice = Mmap::open_path(&e_copy, Protection::Read).unwrap();
|
||||
/* Unwrap is safe since we use ? above. */
|
||||
hasher.write(
|
||||
unsafe { slice.as_slice() });
|
||||
hasher.finish()
|
||||
};
|
||||
{
|
||||
let mut map = map.lock().unwrap();
|
||||
if (*map).contains_key(&hash) {
|
||||
continue;
|
||||
}
|
||||
(*map).insert(hash, (0, e_copy));
|
||||
}
|
||||
// TODO: Check cache
|
||||
let op = Box::new(MaildirOp::new(hash, map.clone()));
|
||||
if let Some(mut e) = Envelope::from_token(op, hash) {
|
||||
local_r.push(e);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
tx.send(AsyncStatus::ProgressReport(len));
|
||||
|
@ -338,13 +385,20 @@ impl MaildirType {
|
|||
let mut result = t.join();
|
||||
r.append(&mut result);
|
||||
}
|
||||
let mut map = map2.lock().unwrap();
|
||||
for (idx, e) in r.iter().enumerate() {
|
||||
let mut y = (*map)[&e.hash()].clone();
|
||||
y.0 = idx;
|
||||
(*map).insert(e.hash(),y);
|
||||
}
|
||||
tx.send(AsyncStatus::Finished);
|
||||
Ok(r)
|
||||
})
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
};
|
||||
w.build(handle)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -379,9 +433,9 @@ impl MaildirFolder {
|
|||
p.push(d);
|
||||
if !p.is_dir() {
|
||||
return Err(MeliError::new(format!(
|
||||
"{} is not a valid maildir folder",
|
||||
path
|
||||
)));
|
||||
"{} is not a valid maildir folder",
|
||||
path
|
||||
)));
|
||||
}
|
||||
p.pop();
|
||||
}
|
||||
|
|
|
@ -92,9 +92,10 @@ impl RefreshEventConsumer {
|
|||
}
|
||||
}
|
||||
pub trait MailBackend: ::std::fmt::Debug {
|
||||
fn get(&self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
|
||||
fn watch(&self, sender: RefreshEventConsumer) -> Result<()>;
|
||||
fn folders(&self) -> Vec<Folder>;
|
||||
fn operation(&self, hash: u64) -> Box<BackendOp>;
|
||||
//login function
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use mailbox::email::parser::BytesExt;
|
|||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
use std::str;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
|
||||
pub enum Charset {
|
||||
Ascii,
|
||||
UTF8,
|
||||
|
@ -52,7 +52,7 @@ impl<'a> From<&'a [u8]> for Charset {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
pub enum MultipartType {
|
||||
Mixed,
|
||||
Alternative,
|
||||
|
@ -73,7 +73,7 @@ impl Display for MultipartType {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub enum ContentType {
|
||||
Text { charset: Charset },
|
||||
Multipart { boundary: Vec<u8> },
|
||||
|
@ -108,7 +108,7 @@ impl ContentType {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
pub enum ContentSubType {
|
||||
Plain,
|
||||
Html,
|
||||
|
@ -134,7 +134,7 @@ impl Display for ContentSubType {
|
|||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub enum ContentTransferEncoding {
|
||||
_8Bit,
|
||||
_7Bit,
|
||||
|
|
|
@ -25,7 +25,7 @@ use std::str;
|
|||
|
||||
pub use mailbox::email::attachment_types::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Serialize)]
|
||||
pub enum AttachmentType {
|
||||
Data {
|
||||
tag: Vec<u8>,
|
||||
|
@ -71,7 +71,7 @@ pub struct AttachmentBuilder {
|
|||
raw: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Serialize)]
|
||||
pub struct Attachment {
|
||||
content_type: (ContentType, ContentSubType),
|
||||
content_transfer_encoding: ContentTransferEncoding,
|
||||
|
|
|
@ -29,7 +29,7 @@ pub mod parser;
|
|||
use parser::BytesExt;
|
||||
|
||||
use error::{MeliError, Result};
|
||||
use mailbox::backends::BackendOpGenerator;
|
||||
use mailbox::backends::BackendOp;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
|
@ -38,26 +38,25 @@ use std::fmt;
|
|||
use std::hash::Hasher;
|
||||
use std::option::Option;
|
||||
use std::string::String;
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono;
|
||||
use chrono::TimeZone;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct GroupAddress {
|
||||
raw: Vec<u8>,
|
||||
display_name: StrBuilder,
|
||||
mailbox_list: Vec<Address>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct MailboxAddress {
|
||||
raw: Vec<u8>,
|
||||
display_name: StrBuilder,
|
||||
address_spec: StrBuilder,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub enum Address {
|
||||
Mailbox(MailboxAddress),
|
||||
Group(GroupAddress),
|
||||
|
@ -109,7 +108,7 @@ impl fmt::Display for Address {
|
|||
}
|
||||
|
||||
/// Helper struct to return slices from a struct field on demand.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
struct StrBuilder {
|
||||
offset: usize,
|
||||
length: usize,
|
||||
|
@ -134,7 +133,7 @@ impl StrBuilder {
|
|||
}
|
||||
|
||||
/// `MessageID` is accessed through the `StrBuild` trait.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Serialize)]
|
||||
pub struct MessageID(Vec<u8>, StrBuilder);
|
||||
|
||||
impl StrBuild for MessageID {
|
||||
|
@ -185,14 +184,14 @@ impl fmt::Debug for MessageID {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
struct References {
|
||||
raw: Vec<u8>,
|
||||
refs: Vec<MessageID>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Serialize)]
|
||||
pub struct Flag: u8 {
|
||||
const PASSED = 0b00000001;
|
||||
const REPLIED = 0b00000010;
|
||||
|
@ -222,7 +221,7 @@ impl EnvelopeBuilder {
|
|||
|
||||
/*
|
||||
* 1. Check for date. Default is now
|
||||
* 2.
|
||||
* 2.
|
||||
Envelope {
|
||||
|
||||
|
||||
|
@ -237,7 +236,7 @@ impl EnvelopeBuilder {
|
|||
/// Access to the underlying email object in the account's backend (for example the file or the
|
||||
/// entry in an IMAP server) is given through `operation_token`. For more information see
|
||||
/// `BackendOp`.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Envelope {
|
||||
date: String,
|
||||
from: Vec<Address>,
|
||||
|
@ -248,19 +247,18 @@ pub struct Envelope {
|
|||
in_reply_to: Option<MessageID>,
|
||||
references: Option<References>,
|
||||
|
||||
datetime: Option<chrono::DateTime<chrono::FixedOffset>>,
|
||||
timestamp: u64,
|
||||
thread: usize,
|
||||
|
||||
operation_token: Arc<Box<BackendOpGenerator>>,
|
||||
hash: u64,
|
||||
|
||||
flags: Flag,
|
||||
}
|
||||
|
||||
impl Envelope {
|
||||
pub fn new(token: Box<BackendOpGenerator>) -> Self {
|
||||
pub fn new(hash: u64) -> Self {
|
||||
Envelope {
|
||||
date: "".to_string(),
|
||||
date: String::new(),
|
||||
from: Vec::new(),
|
||||
to: Vec::new(),
|
||||
body: None,
|
||||
|
@ -269,103 +267,105 @@ impl Envelope {
|
|||
in_reply_to: None,
|
||||
references: None,
|
||||
|
||||
datetime: None,
|
||||
timestamp: 0,
|
||||
|
||||
thread: 0,
|
||||
|
||||
operation_token: Arc::new(token),
|
||||
hash,
|
||||
flags: Flag::default(),
|
||||
}
|
||||
}
|
||||
pub fn from_token(operation_token: Box<BackendOpGenerator>) -> Option<Envelope> {
|
||||
let operation = operation_token.generate();
|
||||
let mut e = Envelope::new(operation_token);
|
||||
pub fn from_token(operation: Box<BackendOp>, hash: u64) -> Option<Envelope> {
|
||||
let mut e = Envelope::new(hash);
|
||||
e.flags = operation.fetch_flags();
|
||||
Some(e)
|
||||
let res = e.populate_headers(operation).ok();
|
||||
if res.is_some() {
|
||||
Some(e)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn set_operation_token(&mut self, operation_token: Box<BackendOpGenerator>) {
|
||||
self.operation_token = Arc::new(operation_token);
|
||||
pub fn hash(&self) -> u64 {
|
||||
self.hash
|
||||
}
|
||||
pub fn populate_headers(&mut self, mut operation: Box<BackendOp>) -> Result<()> {
|
||||
{
|
||||
let headers = match parser::headers(operation.fetch_headers()?).to_full_result() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
eprintln!("error in parsing mail\n");
|
||||
return Err(MeliError::from(e));
|
||||
}
|
||||
};
|
||||
|
||||
pub fn populate_headers(&mut self) -> Result<()> {
|
||||
let mut operation = self.operation_token.generate();
|
||||
let headers = match parser::headers(operation.fetch_headers()?).to_full_result() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
let operation = self.operation_token.generate();
|
||||
eprintln!("error in parsing mail\n{}", operation.description());
|
||||
return Err(MeliError::from(e));
|
||||
}
|
||||
};
|
||||
let mut in_reply_to = None;
|
||||
let mut datetime = None;
|
||||
|
||||
let mut in_reply_to = None;
|
||||
let mut datetime = None;
|
||||
|
||||
for (name, value) in headers {
|
||||
if value.len() == 1 && value.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if name.eq_ignore_ascii_case(b"to") {
|
||||
let parse_result = parser::rfc2822address_list(value);
|
||||
let value = if parse_result.is_done() {
|
||||
parse_result.to_full_result().unwrap()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
self.set_to(value);
|
||||
} else if name.eq_ignore_ascii_case(b"from") {
|
||||
let parse_result = parser::rfc2822address_list(value);
|
||||
let value = if parse_result.is_done() {
|
||||
parse_result.to_full_result().unwrap()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
self.set_from(value);
|
||||
} else if name.eq_ignore_ascii_case(b"subject") {
|
||||
let parse_result = parser::phrase(value.trim());
|
||||
let value = if parse_result.is_done() {
|
||||
parse_result.to_full_result().unwrap()
|
||||
} else {
|
||||
"".into()
|
||||
};
|
||||
self.set_subject(value);
|
||||
} else if name.eq_ignore_ascii_case(b"message-id") {
|
||||
self.set_message_id(value);
|
||||
} else if name.eq_ignore_ascii_case(b"references") {
|
||||
{
|
||||
let parse_result = parser::references(value);
|
||||
if parse_result.is_done() {
|
||||
for v in parse_result.to_full_result().unwrap() {
|
||||
self.push_references(v);
|
||||
for (name, value) in headers {
|
||||
if value.len() == 1 && value.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if name.eq_ignore_ascii_case(b"to") {
|
||||
let parse_result = parser::rfc2822address_list(value);
|
||||
let value = if parse_result.is_done() {
|
||||
parse_result.to_full_result().unwrap()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
self.set_to(value);
|
||||
} else if name.eq_ignore_ascii_case(b"from") {
|
||||
let parse_result = parser::rfc2822address_list(value);
|
||||
let value = if parse_result.is_done() {
|
||||
parse_result.to_full_result().unwrap()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
self.set_from(value);
|
||||
} else if name.eq_ignore_ascii_case(b"subject") {
|
||||
let parse_result = parser::phrase(value.trim());
|
||||
let value = if parse_result.is_done() {
|
||||
parse_result.to_full_result().unwrap()
|
||||
} else {
|
||||
"".into()
|
||||
};
|
||||
self.set_subject(value);
|
||||
} else if name.eq_ignore_ascii_case(b"message-id") {
|
||||
self.set_message_id(value);
|
||||
} else if name.eq_ignore_ascii_case(b"references") {
|
||||
{
|
||||
let parse_result = parser::references(value);
|
||||
if parse_result.is_done() {
|
||||
for v in parse_result.to_full_result().unwrap() {
|
||||
self.push_references(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.set_references(value);
|
||||
} else if name.eq_ignore_ascii_case(b"in-reply-to") {
|
||||
self.set_in_reply_to(value);
|
||||
in_reply_to = Some(value);
|
||||
} else if name.eq_ignore_ascii_case(b"date") {
|
||||
self.set_date(value);
|
||||
datetime = Some(value);
|
||||
}
|
||||
self.set_references(value);
|
||||
} else if name.eq_ignore_ascii_case(b"in-reply-to") {
|
||||
self.set_in_reply_to(value);
|
||||
in_reply_to = Some(value);
|
||||
} else if name.eq_ignore_ascii_case(b"date") {
|
||||
self.set_date(value);
|
||||
datetime = Some(value);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* https://tools.ietf.org/html/rfc5322#section-3.6.4
|
||||
*
|
||||
* if self.message_id.is_none() ...
|
||||
*/
|
||||
if let Some(ref mut x) = in_reply_to {
|
||||
self.push_references(x);
|
||||
}
|
||||
if let Some(ref mut d) = datetime {
|
||||
if let Some(d) = parser::date(d) {
|
||||
self.set_datetime(d);
|
||||
/*
|
||||
* https://tools.ietf.org/html/rfc5322#section-3.6.4
|
||||
*
|
||||
* if self.message_id.is_none() ...
|
||||
*/
|
||||
if let Some(ref mut x) = in_reply_to {
|
||||
self.push_references(x);
|
||||
}
|
||||
}
|
||||
if let Some(ref mut d) = datetime {
|
||||
if let Some(d) = parser::date(d) {
|
||||
self.set_datetime(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.message_id.is_none() {
|
||||
let mut h = DefaultHasher::new();
|
||||
h.write(&self.bytes());
|
||||
h.write(&self.bytes(operation));
|
||||
self.set_message_id(format!("<{:x}>", h.finish()).as_bytes());
|
||||
}
|
||||
Ok(())
|
||||
|
@ -375,11 +375,12 @@ impl Envelope {
|
|||
}
|
||||
|
||||
pub fn datetime(&self) -> chrono::DateTime<chrono::FixedOffset> {
|
||||
self.datetime.unwrap_or_else(|| {
|
||||
if let Some(d) = parser::date(&self.date.as_bytes()) {
|
||||
return d;
|
||||
}
|
||||
chrono::FixedOffset::west(0)
|
||||
.ymd(1970, 1, 1)
|
||||
.and_hms(0, 0, 0)
|
||||
})
|
||||
}
|
||||
pub fn date_as_str(&self) -> &str {
|
||||
&self.date
|
||||
|
@ -399,21 +400,18 @@ impl Envelope {
|
|||
let _strings: Vec<String> = self.to.iter().map(|a| format!("{}", a)).collect();
|
||||
_strings.join(", ")
|
||||
}
|
||||
pub fn bytes(&self) -> Vec<u8> {
|
||||
let mut operation = self.operation_token.generate();
|
||||
pub fn bytes(&self, mut operation: Box<BackendOp>) -> Vec<u8> {
|
||||
operation
|
||||
.as_bytes()
|
||||
.map(|v| v.into())
|
||||
.unwrap_or_else(|_| Vec::new())
|
||||
}
|
||||
pub fn body(&self) -> Attachment {
|
||||
let mut operation = self.operation_token.generate();
|
||||
pub fn body(&self, mut operation: Box<BackendOp>) -> Attachment {
|
||||
let file = operation.as_bytes();
|
||||
let (headers, body) = match parser::mail(file.unwrap()).to_full_result() {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
let operation = self.operation_token.generate();
|
||||
eprintln!("error in parsing mail\n{}", operation.description());
|
||||
eprintln!("error in parsing mail\n");
|
||||
let error_msg = b"Mail cannot be shown because of errors.";
|
||||
let mut builder = AttachmentBuilder::new(error_msg);
|
||||
return builder.build();
|
||||
|
@ -564,11 +562,9 @@ impl Envelope {
|
|||
self.thread = new_val;
|
||||
}
|
||||
pub fn set_datetime(&mut self, new_val: chrono::DateTime<chrono::FixedOffset>) -> () {
|
||||
self.datetime = Some(new_val);
|
||||
self.timestamp = new_val.timestamp() as u64;
|
||||
}
|
||||
pub fn set_flag(&mut self, f: Flag) -> Result<()> {
|
||||
let mut operation = self.operation_token.generate();
|
||||
pub fn set_flag(&mut self, f: Flag, mut operation: Box<BackendOp>) -> Result<()> {
|
||||
operation.set_flag(self, &f)?;
|
||||
self.flags |= f;
|
||||
Ok(())
|
||||
|
@ -576,11 +572,11 @@ impl Envelope {
|
|||
pub fn flags(&self) -> Flag {
|
||||
self.flags
|
||||
}
|
||||
pub fn set_seen(&mut self) -> Result<()> {
|
||||
self.set_flag(Flag::SEEN)
|
||||
pub fn set_seen(&mut self, operation: Box<BackendOp>) -> Result<()> {
|
||||
self.set_flag(Flag::SEEN, operation)
|
||||
}
|
||||
pub fn is_seen(&self) -> bool {
|
||||
!(self.flags & Flag::SEEN).is_empty()
|
||||
self.flags.contains(Flag::SEEN)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -128,9 +128,6 @@ named!(
|
|||
named!(pub headers<std::vec::Vec<(&[u8], &[u8])>>,
|
||||
many1!(complete!(header)));
|
||||
|
||||
//named!(pub headers_raw<&[u8]>,
|
||||
//take_until1!("\n\n"));
|
||||
|
||||
pub fn headers_raw(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
if input.is_empty() {
|
||||
return IResult::Incomplete(Needed::Unknown);
|
||||
|
@ -417,22 +414,19 @@ named!(
|
|||
named!(mailbox_list<Vec<Address>>, many0!(mailbox));
|
||||
|
||||
#[test]
|
||||
fn test_mailbox() {
|
||||
fn test_addresses() {
|
||||
{
|
||||
let s = b"epilys@postretch";
|
||||
let s = b"user@domain";
|
||||
let r = mailbox(s).unwrap().1;
|
||||
match r {
|
||||
Address::Mailbox(ref m) => {
|
||||
println!(
|
||||
"----\n`{}`, `{}`\n----",
|
||||
m.display_name.display(&m.raw),
|
||||
m.address_spec.display(&m.raw)
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
assert!(m.display_name == b"" && m.address_spec == b"user@domain");
|
||||
},
|
||||
_ => assert!(false),
|
||||
}
|
||||
}
|
||||
let s = b"Manos <epilys@postretch>";
|
||||
{
|
||||
let s = b"Name <user@domain>";
|
||||
eprintln!("{:?}", display_addr(s).unwrap());
|
||||
let r = display_addr(s).unwrap().1;
|
||||
match r {
|
||||
|
@ -446,17 +440,25 @@ fn test_mailbox() {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
{
|
||||
let s = b"user@domain";
|
||||
let r = mailbox(s).unwrap().1;
|
||||
match r {
|
||||
Address::Mailbox(ref m) => {
|
||||
println!(
|
||||
"----\n`{}`, `{}`\n----",
|
||||
m.display_name.display(&m.raw),
|
||||
m.address_spec.display(&m.raw)
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//named!(group_t<GroupAddress>, ws!( do_parse!(
|
||||
// display_name: take_until1!(":") >>
|
||||
// mailbox_list: many0!(mailbox) >>
|
||||
// end: is_a!(";") >>
|
||||
// ({
|
||||
//
|
||||
// })
|
||||
// )));
|
||||
//
|
||||
|
||||
/*
|
||||
* group of recipients eg. undisclosed-recipients;
|
||||
*/
|
||||
fn group(input: &[u8]) -> IResult<&[u8], Address> {
|
||||
let mut flag = false;
|
||||
let mut dlength = 0;
|
||||
|
@ -499,9 +501,9 @@ named!(address<Address>, ws!(alt_complete!(mailbox | group)));
|
|||
|
||||
#[test]
|
||||
fn test_address() {
|
||||
let s = b"Manos Pitsidianakis <el13635@mail.ntua.gr>,
|
||||
qemu-devel <qemu-devel@nongnu.org>, qemu-block <qemu-block@nongnu.org>,
|
||||
Alberto Garcia <berto@igalia.com>, Stefan Hajnoczi <stefanha@redhat.com>";
|
||||
let s = b"Obit Oppidum <user@domain>,
|
||||
list <list@domain.tld>, list2 <list2@domain.tld>,
|
||||
Bobit Boppidum <user@otherdomain.com>, Cobit Coppidum <user2@otherdomain.com>";
|
||||
println!("{:?}", rfc2822address_list(s).unwrap());
|
||||
}
|
||||
|
||||
|
@ -552,11 +554,11 @@ named!(pub phrase<Vec<u8>>, ws!(do_parse!(
|
|||
|
||||
#[test]
|
||||
fn test_phrase() {
|
||||
let phrase_s = "list.free.de mailing list memberships reminder".as_bytes();
|
||||
let phrase_s = "mailing list memberships reminder".as_bytes();
|
||||
assert_eq!(
|
||||
(
|
||||
&b""[..],
|
||||
"list.free.de mailing list memberships reminder".to_string()
|
||||
"mailing list memberships reminder".to_string()
|
||||
),
|
||||
phrase(phrase_s).unwrap()
|
||||
);
|
||||
|
@ -739,16 +741,3 @@ named!(pub content_type< (&[u8], &[u8], Vec<(&[u8], &[u8])>) >,
|
|||
(_type, _subtype, parameters)
|
||||
} )
|
||||
));
|
||||
|
||||
//named!(pub quoted_printable_text<Vec<u8>>,
|
||||
// do_parse!(
|
||||
// bytes: many0!(alt_complete!(
|
||||
// preceded!(tag!("=\n"), quoted_printable_byte) |
|
||||
// preceded!(tag!("=\n"), le_u8) |
|
||||
// quoted_printable_byte |
|
||||
// le_u8)) >>
|
||||
// ( {
|
||||
// bytes
|
||||
// } )
|
||||
// )
|
||||
//);
|
||||
|
|
|
@ -182,7 +182,7 @@ fn main() {
|
|||
},
|
||||
ThreadEvent::RefreshMailbox { hash : h } => {
|
||||
state.hash_to_folder(h);
|
||||
//state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Notification(n.clone())});
|
||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Notification(String::from("Update in mailbox"))});
|
||||
state.redraw();
|
||||
},
|
||||
ThreadEvent::UIEvent(UIEventType::ChangeMode(f)) => {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
|
||||
use super::*;
|
||||
use melib::mailbox::backends::BackendOp;
|
||||
const MAX_COLS: usize = 500;
|
||||
|
||||
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the thread's content in a
|
||||
|
@ -89,7 +90,7 @@ impl CompactMailListing {
|
|||
break;
|
||||
}
|
||||
}
|
||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
|
@ -188,6 +189,7 @@ impl CompactMailListing {
|
|||
container,
|
||||
&indentations,
|
||||
len,
|
||||
context.accounts[self.cursor_pos.0].backend.operation(envelope.hash()),
|
||||
),
|
||||
&mut content,
|
||||
fg_color,
|
||||
|
@ -347,6 +349,7 @@ impl CompactMailListing {
|
|||
container: &Container,
|
||||
indentations: &[bool],
|
||||
idx_width: usize,
|
||||
op: Box<BackendOp>,
|
||||
) -> String {
|
||||
let has_sibling = container.has_sibling();
|
||||
let has_parent = container.has_parent();
|
||||
|
@ -383,7 +386,7 @@ impl CompactMailListing {
|
|||
if show_subject {
|
||||
s.push_str(&format!("{:.85}", envelope.subject()));
|
||||
}
|
||||
let attach_count = envelope.body().count_attachments();
|
||||
let attach_count = envelope.body(op).count_attachments();
|
||||
if attach_count > 1 {
|
||||
s.push_str(&format!(" {}∞ ", attach_count - 1));
|
||||
}
|
||||
|
@ -439,17 +442,34 @@ impl Component for CompactMailListing {
|
|||
let threaded = context.accounts[self.cursor_pos.0]
|
||||
.runtime_settings
|
||||
.threaded;
|
||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let envelope: &mut Envelope = if threaded {
|
||||
let i = mailbox.threaded_mail(idx);
|
||||
&mut mailbox.collection[i]
|
||||
} else {
|
||||
&mut mailbox.collection[idx]
|
||||
let account = &mut context.accounts[self.cursor_pos.0];
|
||||
let (hash, is_seen) = {
|
||||
let mailbox = &mut account[self.cursor_pos.1]
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let envelope: &mut Envelope = if threaded {
|
||||
let i = mailbox.threaded_mail(idx);
|
||||
&mut mailbox.collection[i]
|
||||
} else {
|
||||
&mut mailbox.collection[idx]
|
||||
};
|
||||
(envelope.hash(), envelope.is_seen())
|
||||
};
|
||||
if !envelope.is_seen() {
|
||||
envelope.set_seen().unwrap();
|
||||
if is_seen {
|
||||
let op = {
|
||||
let backend = &account.backend;
|
||||
backend.operation(hash)
|
||||
};
|
||||
let mailbox = &mut account[self.cursor_pos.1]
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let envelope: &mut Envelope = if threaded {
|
||||
let i = mailbox.threaded_mail(idx);
|
||||
&mut mailbox.collection[i]
|
||||
} else {
|
||||
&mut mailbox.collection[idx]
|
||||
};
|
||||
envelope.set_seen(op).unwrap();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
use super::*;
|
||||
|
||||
use melib::mailbox::backends::BackendOp;
|
||||
mod compact;
|
||||
pub use self::compact::*;
|
||||
|
||||
|
@ -107,7 +108,7 @@ impl MailListing {
|
|||
break;
|
||||
}
|
||||
}
|
||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
|
@ -139,10 +140,18 @@ impl MailListing {
|
|||
let threads: &Vec<Container> = &mailbox.threads;
|
||||
local_collection.sort_by(|a, b| match self.sort {
|
||||
(SortField::Date, SortOrder::Desc) => {
|
||||
mailbox.thread(*b).date().cmp(&mailbox.thread(*a).date())
|
||||
let a = mailbox.thread(*a);
|
||||
let b = mailbox.thread(*b);
|
||||
let ma = &mailbox.collection[*a.message().as_ref().unwrap()];
|
||||
let mb = &mailbox.collection[*b.message().as_ref().unwrap()];
|
||||
mb.date().cmp(&ma.date())
|
||||
}
|
||||
(SortField::Date, SortOrder::Asc) => {
|
||||
mailbox.thread(*a).date().cmp(&mailbox.thread(*b).date())
|
||||
let a = mailbox.thread(*a);
|
||||
let b = mailbox.thread(*b);
|
||||
let ma = &mailbox.collection[*a.message().as_ref().unwrap()];
|
||||
let mb = &mailbox.collection[*b.message().as_ref().unwrap()];
|
||||
ma.date().cmp(&mb.date())
|
||||
}
|
||||
(SortField::Subject, SortOrder::Desc) => {
|
||||
let a = mailbox.thread(*a);
|
||||
|
@ -211,6 +220,7 @@ impl MailListing {
|
|||
container,
|
||||
&indentations,
|
||||
len,
|
||||
context.accounts[self.cursor_pos.0].backend.operation(envelope.hash())
|
||||
),
|
||||
&mut content,
|
||||
fg_color,
|
||||
|
@ -411,6 +421,7 @@ impl MailListing {
|
|||
container: &Container,
|
||||
indentations: &[bool],
|
||||
idx_width: usize,
|
||||
op: Box<BackendOp>,
|
||||
) -> String {
|
||||
let has_sibling = container.has_sibling();
|
||||
let has_parent = container.has_parent();
|
||||
|
@ -447,7 +458,7 @@ impl MailListing {
|
|||
if show_subject {
|
||||
s.push_str(&format!("{:.85}", envelope.subject()));
|
||||
}
|
||||
let attach_count = envelope.body().count_attachments();
|
||||
let attach_count = envelope.body(op).count_attachments();
|
||||
if attach_count > 1 {
|
||||
s.push_str(&format!(" {}∞ ", attach_count - 1));
|
||||
}
|
||||
|
@ -503,17 +514,34 @@ impl Component for MailListing {
|
|||
let threaded = context.accounts[self.cursor_pos.0]
|
||||
.runtime_settings
|
||||
.threaded;
|
||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let envelope: &mut Envelope = if threaded {
|
||||
let i = mailbox.threaded_mail(idx);
|
||||
&mut mailbox.collection[i]
|
||||
} else {
|
||||
&mut mailbox.collection[idx]
|
||||
let account = &mut context.accounts[self.cursor_pos.0];
|
||||
let (hash, is_seen) = {
|
||||
let mailbox = &mut account[self.cursor_pos.1]
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let envelope: &mut Envelope = if threaded {
|
||||
let i = mailbox.threaded_mail(idx);
|
||||
&mut mailbox.collection[i]
|
||||
} else {
|
||||
&mut mailbox.collection[idx]
|
||||
};
|
||||
(envelope.hash(), envelope.is_seen())
|
||||
};
|
||||
if !envelope.is_seen() {
|
||||
envelope.set_seen().unwrap();
|
||||
if !is_seen {
|
||||
let op = {
|
||||
let backend = &account.backend;
|
||||
backend.operation(hash)
|
||||
};
|
||||
let mailbox = &mut account[self.cursor_pos.1]
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let envelope: &mut Envelope = if threaded {
|
||||
let i = mailbox.threaded_mail(idx);
|
||||
&mut mailbox.collection[i]
|
||||
} else {
|
||||
&mut mailbox.collection[idx]
|
||||
};
|
||||
envelope.set_seen(op).unwrap();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -303,11 +303,12 @@ impl Component for MailView {
|
|||
|
||||
if self.dirty {
|
||||
let mailbox_idx = self.coordinates; // coordinates are mailbox idxs
|
||||
let mailbox = &mut context.accounts[mailbox_idx.0][mailbox_idx.1]
|
||||
let mailbox = &context.accounts[mailbox_idx.0][mailbox_idx.1]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let envelope: &Envelope = &mailbox.collection[envelope_idx];
|
||||
let body = envelope.body();
|
||||
let op = context.accounts[mailbox_idx.0].backend.operation(envelope.hash());
|
||||
let body = envelope.body(op);
|
||||
match self.mode {
|
||||
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
|
||||
self.subview = Some(Box::new(HtmlView::new(decode(
|
||||
|
@ -372,9 +373,9 @@ impl Component for MailView {
|
|||
self.cmd_buf.clear();
|
||||
|
||||
{
|
||||
let accounts = &mut context.accounts;
|
||||
let accounts = &context.accounts;
|
||||
let threaded = accounts[self.coordinates.0].runtime_settings.threaded;
|
||||
let mailbox = &mut accounts[self.coordinates.0][self.coordinates.1]
|
||||
let mailbox = &accounts[self.coordinates.0][self.coordinates.1]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let envelope_idx: usize = if threaded {
|
||||
|
@ -384,7 +385,8 @@ impl Component for MailView {
|
|||
};
|
||||
|
||||
let envelope: &Envelope = &mailbox.collection[envelope_idx];
|
||||
if let Some(u) = envelope.body().attachments().get(lidx) {
|
||||
let op = context.accounts[self.coordinates.0].backend.operation(envelope.hash());
|
||||
if let Some(u) = envelope.body(op).attachments().get(lidx) {
|
||||
match u.content_type().0 {
|
||||
ContentType::Text { .. } => {
|
||||
self.mode = ViewMode::Attachment(lidx);
|
||||
|
@ -443,9 +445,9 @@ impl Component for MailView {
|
|||
let lidx = self.cmd_buf.parse::<usize>().unwrap();
|
||||
self.cmd_buf.clear();
|
||||
let url = {
|
||||
let accounts = &mut context.accounts;
|
||||
let accounts = &context.accounts;
|
||||
let threaded = accounts[self.coordinates.0].runtime_settings.threaded;
|
||||
let mailbox = &mut accounts[self.coordinates.0][self.coordinates.1]
|
||||
let mailbox = &accounts[self.coordinates.0][self.coordinates.1]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let envelope_idx: usize = if threaded {
|
||||
|
@ -456,7 +458,8 @@ impl Component for MailView {
|
|||
|
||||
let envelope: &Envelope = &mailbox.collection[envelope_idx];
|
||||
let finder = LinkFinder::new();
|
||||
let mut t = envelope.body().text().to_string();
|
||||
let op = context.accounts[self.coordinates.0].backend.operation(envelope.hash());
|
||||
let mut t = envelope.body(op).text().to_string();
|
||||
let links: Vec<Link> = finder.links(&t).collect();
|
||||
if let Some(u) = links.get(lidx) {
|
||||
u.as_str().to_string()
|
||||
|
|
Loading…
Reference in New Issue