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