add accounts and BackendOps
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>embed
parent
8dfba2c51c
commit
4119a4285d
|
@ -0,0 +1,12 @@
|
||||||
|
#![feature(test)]
|
||||||
|
extern crate melib;
|
||||||
|
|
||||||
|
use melib::mailbox::email::Mail;
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
use self::test::Bencher;
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn mail_parse(b: &mut Bencher) {
|
||||||
|
b.iter(|| Mail::from("test/attachment_test") );
|
||||||
|
}
|
12
src/bin.rs
12
src/bin.rs
|
@ -36,17 +36,17 @@ fn main() {
|
||||||
let ui = ui::TUI::initialize();
|
let ui = ui::TUI::initialize();
|
||||||
let mut j = 0;
|
let mut j = 0;
|
||||||
let folder_length = set.accounts["norn"].folders.len();
|
let folder_length = set.accounts["norn"].folders.len();
|
||||||
|
let mut account = Account::new("norn".to_string(), set.accounts["norn"].clone());
|
||||||
'main : loop {
|
'main : loop {
|
||||||
ncurses::touchwin(ncurses::stdscr());
|
ncurses::touchwin(ncurses::stdscr());
|
||||||
ncurses::mv(0,0);
|
ncurses::mv(0,0);
|
||||||
let mailbox = Mailbox::new(&set.accounts["norn"].folders[j],
|
let mailbox = &mut account[j];
|
||||||
Some(&set.accounts["norn"].sent_folder));
|
let mut index: Box<Window> = match mailbox.as_ref().unwrap() {
|
||||||
let mut index: Box<Window> = match mailbox {
|
&Ok(ref v) => {
|
||||||
Ok(v) => {
|
|
||||||
Box::new(Index::new(v))
|
Box::new(Index::new(v))
|
||||||
},
|
},
|
||||||
Err(v) => {
|
&Err(ref v) => {
|
||||||
Box::new(ErrorWindow::new(v))
|
Box::new(ErrorWindow::new((*v).clone()))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//eprintln!("{:?}", set);
|
//eprintln!("{:?}", set);
|
||||||
|
|
|
@ -26,8 +26,8 @@ use std::io;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{PathBuf, Path};
|
use std::path::{PathBuf, Path};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug,Clone)]
|
||||||
enum MailFormat {
|
pub enum MailFormat {
|
||||||
Maildir
|
Maildir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,8 +57,8 @@ struct FileSettings {
|
||||||
accounts: HashMap<String, FileAccount>,
|
accounts: HashMap<String, FileAccount>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug,Clone)]
|
||||||
pub struct Account {
|
pub struct AccountSettings {
|
||||||
pub folders: Vec<String>,
|
pub folders: Vec<String>,
|
||||||
format: MailFormat,
|
format: MailFormat,
|
||||||
pub sent_folder: String,
|
pub sent_folder: String,
|
||||||
|
@ -66,7 +66,7 @@ pub struct Account {
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub accounts: HashMap<String, Account>,
|
pub accounts: HashMap<String, AccountSettings>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ impl FileSettings {
|
||||||
impl Settings {
|
impl Settings {
|
||||||
pub fn new() -> Settings {
|
pub fn new() -> Settings {
|
||||||
let fs = FileSettings::new();
|
let fs = FileSettings::new();
|
||||||
let mut s: HashMap<String, Account> = HashMap::new();
|
let mut s: HashMap<String, AccountSettings> = HashMap::new();
|
||||||
|
|
||||||
for (id, x) in fs.accounts {
|
for (id, x) in fs.accounts {
|
||||||
let mut folders = Vec::new();
|
let mut folders = Vec::new();
|
||||||
|
@ -121,7 +121,7 @@ impl Settings {
|
||||||
folders.push(path.to_str().unwrap().to_string());
|
folders.push(path.to_str().unwrap().to_string());
|
||||||
}
|
}
|
||||||
recurse_folders(&mut folders, &x.folders);
|
recurse_folders(&mut folders, &x.folders);
|
||||||
s.insert(id.clone(), Account {
|
s.insert(id.clone(), AccountSettings {
|
||||||
folders: folders,
|
folders: folders,
|
||||||
format: MailFormat::from_str(&x.format),
|
format: MailFormat::from_str(&x.format),
|
||||||
sent_folder: x.sent_folder.clone(),
|
sent_folder: x.sent_folder.clone(),
|
||||||
|
|
|
@ -23,6 +23,8 @@ use std::fmt;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
use nom;
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, MeliError>;
|
pub type Result<T> = result::Result<T, MeliError>;
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug,Clone)]
|
||||||
|
@ -55,3 +57,9 @@ impl From<io::Error> for MeliError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<nom::IError> for MeliError {
|
||||||
|
#[inline]
|
||||||
|
fn from(kind: nom::IError) -> MeliError {
|
||||||
|
MeliError::new(format!("{:?}", kind))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* meli - accounts module.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Manos Pitsidianakis
|
||||||
|
*
|
||||||
|
* This file is part of meli.
|
||||||
|
*
|
||||||
|
* meli is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* meli is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use mailbox::*;
|
||||||
|
use conf::AccountSettings;
|
||||||
|
use std::ops::{IndexMut, Index};
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Account {
|
||||||
|
name: String,
|
||||||
|
folders: Vec<Option<Result<Mailbox>>>,
|
||||||
|
|
||||||
|
sent_folder: Option<usize>,
|
||||||
|
|
||||||
|
|
||||||
|
settings: AccountSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Account {
|
||||||
|
pub fn new(name: String, settings: AccountSettings) -> Self {
|
||||||
|
eprintln!("new acc" );
|
||||||
|
let sent_folder = settings.folders.iter().position(|ref x| **x == settings.sent_folder);
|
||||||
|
let mut folders = Vec::with_capacity(settings.folders.len());
|
||||||
|
for _ in 0..settings.folders.len() {
|
||||||
|
folders.push(None);
|
||||||
|
}
|
||||||
|
Account {
|
||||||
|
name: name,
|
||||||
|
folders: folders,
|
||||||
|
|
||||||
|
sent_folder: sent_folder,
|
||||||
|
|
||||||
|
settings: settings,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<usize> for Account {
|
||||||
|
type Output = Option<Result<Mailbox>>;
|
||||||
|
fn index(&self, index: usize) -> &Option<Result<Mailbox>> {
|
||||||
|
&self.folders[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<usize> for Account
|
||||||
|
{
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Option<Result<Mailbox>> {
|
||||||
|
if self.folders[index].is_none() {
|
||||||
|
eprintln!("building folder {:?}", self.settings.folders[index]);
|
||||||
|
let path = self.settings.folders[index].clone();
|
||||||
|
if self.sent_folder.is_some() {
|
||||||
|
let id = self.sent_folder.unwrap();
|
||||||
|
if id == index {
|
||||||
|
eprintln!("building sent_folder..");
|
||||||
|
self.folders[index] = Some(Mailbox::new(&path, &None));
|
||||||
|
eprintln!("Done!");
|
||||||
|
} else {
|
||||||
|
eprintln!("Now building folder {:?} with sent_folder", self.settings.folders[index]);
|
||||||
|
let (sent, cur) =
|
||||||
|
{
|
||||||
|
let ptr = self.folders.as_mut_ptr();
|
||||||
|
unsafe {
|
||||||
|
use std::slice::from_raw_parts_mut;
|
||||||
|
(from_raw_parts_mut(ptr.offset(id as isize), id+1),
|
||||||
|
from_raw_parts_mut(ptr.offset(index as isize), index+1))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let sent_path = self.settings.folders[id].clone();
|
||||||
|
if sent[0].is_none() {
|
||||||
|
eprintln!("\tbuilding sent_folder..");
|
||||||
|
sent[0] = Some(Mailbox::new(&sent_path, &None));
|
||||||
|
eprintln!("\tDone!");
|
||||||
|
}
|
||||||
|
cur[0] = Some(Mailbox::new(&path, &sent[0]));
|
||||||
|
eprintln!("Done!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("Now building folder {:?} without sent_folder", self.settings.folders[index]);
|
||||||
|
self.folders[index] = Some(Mailbox::new(&path, &None));
|
||||||
|
eprintln!("Done!");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
&mut self.folders[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -21,16 +21,65 @@
|
||||||
|
|
||||||
use mailbox::email::Mail;
|
use mailbox::email::Mail;
|
||||||
use error::{MeliError, Result};
|
use error::{MeliError, Result};
|
||||||
use mailbox::backends::MailBackend;
|
use mailbox::backends::{MailBackend, BackendOp, BackendOpGenerator};
|
||||||
|
|
||||||
extern crate crossbeam;
|
extern crate crossbeam;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use memmap::{Mmap, Protection};
|
||||||
|
|
||||||
|
|
||||||
pub struct MaildirType {
|
pub struct MaildirType {
|
||||||
path: String,
|
path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Default)]
|
||||||
|
pub struct MaildirOp {
|
||||||
|
path: String,
|
||||||
|
slice: Option<Mmap>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for MaildirOp {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
MaildirOp {
|
||||||
|
path: self.path.clone(),
|
||||||
|
slice: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MaildirOp {
|
||||||
|
fn new(path: String) -> Self {
|
||||||
|
MaildirOp {
|
||||||
|
path: path,
|
||||||
|
slice: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackendOp for MaildirOp {
|
||||||
|
fn description(&self) -> String {
|
||||||
|
format!("Path of file: {}", self.path)
|
||||||
|
}
|
||||||
|
fn as_bytes(&mut self) -> Result<&[u8]> {
|
||||||
|
if self.slice.is_none() {
|
||||||
|
self.slice = Some(Mmap::open_path(self.path.to_string(), Protection::Read).unwrap());
|
||||||
|
}
|
||||||
|
Ok(unsafe { self.slice.as_ref().unwrap().as_slice() })
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
fn fetch_headers(&mut self) -> Result<&[u8]> {
|
||||||
|
let raw = self.as_bytes()?;
|
||||||
|
let result = parser::headers_raw(raw).to_full_result()?;
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
fn fetch_body(&mut self) -> Result<&[u8]> {
|
||||||
|
let raw = self.as_bytes()?;
|
||||||
|
let result = parser::headers_raw(raw).to_full_result()?;
|
||||||
|
Ok(result)
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl MailBackend for MaildirType {
|
impl MailBackend for MaildirType {
|
||||||
fn get(&self) -> Result<Vec<Mail>> {
|
fn get(&self) -> Result<Vec<Mail>> {
|
||||||
self.get_multicore(4)
|
self.get_multicore(4)
|
||||||
|
@ -120,7 +169,10 @@ panic!("didn't parse"); },
|
||||||
let s = scope.spawn(move || {
|
let s = scope.spawn(move || {
|
||||||
let mut local_r:Vec<Mail> = Vec::with_capacity(chunk.len());
|
let mut local_r:Vec<Mail> = Vec::with_capacity(chunk.len());
|
||||||
for e in chunk {
|
for e in chunk {
|
||||||
if let Some(e) = Mail::from(e) {
|
let e_copy = e.to_string();
|
||||||
|
if let Some(e) = Mail::from(Box::new(BackendOpGenerator::new(Box::new(move || {
|
||||||
|
Box::new(MaildirOp::new(e_copy.clone()))
|
||||||
|
} )))) {
|
||||||
local_r.push(e);
|
local_r.push(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,39 @@ pub mod maildir;
|
||||||
use mailbox::email::Mail;
|
use mailbox::email::Mail;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
pub trait MailBackend {
|
pub trait MailBackend {
|
||||||
fn get(&self) -> Result<Vec<Mail>>;
|
fn get(&self) -> Result<Vec<Mail>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* A BackendOp manages common operations for the various mail backends. They should be created when
|
||||||
|
* needed */
|
||||||
|
pub trait BackendOp: ::std::fmt::Debug + ::std::marker::Send {
|
||||||
|
fn description(&self) -> String;
|
||||||
|
fn as_bytes(&mut self) -> Result<&[u8]>;
|
||||||
|
//fn delete(&self) -> ();
|
||||||
|
//fn copy(&self
|
||||||
|
//fn fetch_headers(&mut self) -> Result<&[u8]>;
|
||||||
|
//fn fetch_body(&mut self) -> Result<&[u8]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BackendOpGenerator is a wrapper for a closure that returns a BackendOp object */
|
||||||
|
pub struct BackendOpGenerator(Box<Fn() -> Box<BackendOp>>);
|
||||||
|
impl BackendOpGenerator {
|
||||||
|
pub fn new(b: Box<Fn() -> Box<BackendOp>>) -> Self {
|
||||||
|
BackendOpGenerator(b)
|
||||||
|
}
|
||||||
|
pub fn generate(&self) -> Box<BackendOp> {
|
||||||
|
self.0()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe impl Send for BackendOpGenerator {}
|
||||||
|
unsafe impl Sync for BackendOpGenerator {}
|
||||||
|
impl fmt::Debug for BackendOpGenerator {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "BackendOpGenerator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,17 +19,18 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mod parser;
|
pub mod parser;
|
||||||
mod attachments;
|
mod attachments;
|
||||||
|
use mailbox::backends::BackendOpGenerator;
|
||||||
use self::attachments::*;
|
use self::attachments::*;
|
||||||
|
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use memmap::{Mmap, Protection};
|
//use std;
|
||||||
use std;
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::option::Option;
|
use std::option::Option;
|
||||||
use std::io::prelude::*;
|
//use std::io::prelude::*;
|
||||||
|
use std::ascii::AsciiExt;
|
||||||
|
|
||||||
use chrono;
|
use chrono;
|
||||||
use chrono::TimeZone;
|
use chrono::TimeZone;
|
||||||
|
@ -92,9 +93,11 @@ struct References {
|
||||||
refs: Vec<MessageID>,
|
refs: Vec<MessageID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
/* A very primitive mail object */
|
/* A very primitive mail object */
|
||||||
#[derive(Clone,Debug,Default)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Mail {
|
pub struct Mail
|
||||||
|
{
|
||||||
date: String,
|
date: String,
|
||||||
from: Option<String>,
|
from: Option<String>,
|
||||||
to: Option<String>,
|
to: Option<String>,
|
||||||
|
@ -106,9 +109,13 @@ pub struct Mail {
|
||||||
|
|
||||||
datetime: Option<chrono::DateTime<chrono::FixedOffset>>,
|
datetime: Option<chrono::DateTime<chrono::FixedOffset>>,
|
||||||
thread: usize,
|
thread: usize,
|
||||||
|
|
||||||
|
operation_token: Arc<Box<BackendOpGenerator>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mail {
|
|
||||||
|
impl Mail
|
||||||
|
{
|
||||||
pub fn get_date(&self) -> i64 {
|
pub fn get_date(&self) -> i64 {
|
||||||
match self.datetime {
|
match self.datetime {
|
||||||
Some(v) => v.timestamp(),
|
Some(v) => v.timestamp(),
|
||||||
|
@ -247,7 +254,7 @@ impl Mail {
|
||||||
pub fn set_datetime(&mut self, new_val: Option<chrono::DateTime<chrono::FixedOffset>>) -> () {
|
pub fn set_datetime(&mut self, new_val: Option<chrono::DateTime<chrono::FixedOffset>>) -> () {
|
||||||
self.datetime = new_val;
|
self.datetime = new_val;
|
||||||
}
|
}
|
||||||
pub fn new() -> Self {
|
pub fn new(token: Box<BackendOpGenerator>) -> Self {
|
||||||
Mail {
|
Mail {
|
||||||
date: "".to_string(),
|
date: "".to_string(),
|
||||||
from: None,
|
from: None,
|
||||||
|
@ -261,106 +268,99 @@ impl Mail {
|
||||||
datetime: None,
|
datetime: None,
|
||||||
|
|
||||||
thread: 0,
|
thread: 0,
|
||||||
}
|
|
||||||
|
operation_token: Arc::new(token),
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn from(path: &str) -> Option<Self> {
|
pub fn from(operation_token: Box<BackendOpGenerator>) -> Option<Mail> {
|
||||||
let f = Mmap::open_path(path.to_string(), Protection::Read).unwrap();
|
let mut operation = operation_token.generate();
|
||||||
let file = unsafe { f.as_slice() };
|
let file = operation.as_bytes();
|
||||||
let (headers, body) = match parser::mail(file).to_full_result() {
|
if file.is_err() {
|
||||||
Ok(v) => v,
|
return None;
|
||||||
Err(_) => {
|
}
|
||||||
eprintln!("error in parsing mail");
|
let (headers, body) = match parser::mail(file.unwrap()).to_full_result() {
|
||||||
let path = std::path::PathBuf::from(path);
|
Ok(v) => v,
|
||||||
|
Err(_) => {
|
||||||
|
let operation = operation_token.generate();
|
||||||
|
eprintln!("error in parsing mail\n{}", operation.description());
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut mail = Mail::new(operation_token);
|
||||||
|
let mut in_reply_to = None;
|
||||||
|
let mut datetime = None;
|
||||||
|
|
||||||
let mut buffer = Vec::new();
|
let mut builder = AttachmentBuilder::new(body);
|
||||||
let _ = std::fs::File::open(path).unwrap().read_to_end(&mut buffer);
|
for (name, value) in headers {
|
||||||
eprintln!("\n-------------------------------");
|
if value.len() == 1 && value.is_empty() {
|
||||||
eprintln!("{}\n", std::string::String::from_utf8_lossy(&buffer));
|
continue;
|
||||||
eprintln!("-------------------------------\n");
|
}
|
||||||
|
if name.eq_ignore_ascii_case("to") {
|
||||||
|
let parse_result = parser::subject(value.as_bytes());
|
||||||
|
let value = if parse_result.is_done() {
|
||||||
|
parse_result.to_full_result().unwrap()
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
|
mail.set_to(value);
|
||||||
|
} else if name.eq_ignore_ascii_case("from") {
|
||||||
|
let parse_result = parser::subject(value.as_bytes());
|
||||||
|
let value = if parse_result.is_done() {
|
||||||
|
parse_result.to_full_result().unwrap()
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
|
mail.set_from(value);
|
||||||
|
} else if name.eq_ignore_ascii_case("subject") {
|
||||||
|
let parse_result = parser::subject(value.trim().as_bytes());
|
||||||
|
let value = if parse_result.is_done() {
|
||||||
|
parse_result.to_full_result().unwrap()
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
|
mail.set_subject(value);
|
||||||
|
} else if name.eq_ignore_ascii_case("message-id") {
|
||||||
|
mail.set_message_id(value);
|
||||||
|
} else if name.eq_ignore_ascii_case("references") {
|
||||||
|
{
|
||||||
|
let parse_result = parser::references(value.as_bytes());
|
||||||
|
eprintln!("{:?}", value);
|
||||||
|
if parse_result.is_done() {
|
||||||
|
for v in parse_result.to_full_result().unwrap() {
|
||||||
|
eprintln!("pushing {:?}", v);
|
||||||
|
mail.push_references(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eprintln!("\n\n");
|
||||||
|
}
|
||||||
|
mail.set_references(value.to_string());
|
||||||
|
} else if name.eq_ignore_ascii_case("in-reply-to") {
|
||||||
|
mail.set_in_reply_to(value);
|
||||||
|
in_reply_to = Some(value);
|
||||||
|
} else if name.eq_ignore_ascii_case("date") {
|
||||||
|
mail.set_date(value.to_string());
|
||||||
|
datetime = Some(value.to_string());
|
||||||
|
} else if name.eq_ignore_ascii_case("content-transfer-encoding") {
|
||||||
|
builder.content_transfer_encoding(value);
|
||||||
|
} else if name.eq_ignore_ascii_case("content-type") {
|
||||||
|
builder.content_type(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ref mut x) = in_reply_to {
|
||||||
|
mail.push_references(x);
|
||||||
|
}
|
||||||
|
mail.set_body(builder.build());
|
||||||
|
if let Some(ref mut d) = datetime {
|
||||||
|
mail.set_datetime(parser::date(d));
|
||||||
|
}
|
||||||
|
|
||||||
return None; }
|
Some(mail)
|
||||||
};
|
|
||||||
let mut mail = Mail::new();
|
|
||||||
let mut in_reply_to = None;
|
|
||||||
let mut datetime = None;
|
|
||||||
|
|
||||||
let mut builder = AttachmentBuilder::new(body);
|
|
||||||
for (name, value) in headers {
|
|
||||||
if value.len() == 1 && value.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
match name {
|
|
||||||
"To" => {
|
|
||||||
let parse_result = parser::subject(value.as_bytes());
|
|
||||||
let value = if parse_result.is_done() {
|
|
||||||
parse_result.to_full_result().unwrap()
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
};
|
|
||||||
mail.set_to(value);
|
|
||||||
},
|
|
||||||
"From" => {
|
|
||||||
let parse_result = parser::subject(value.as_bytes());
|
|
||||||
let value = if parse_result.is_done() {
|
|
||||||
parse_result.to_full_result().unwrap()
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
};
|
|
||||||
mail.set_from(value);
|
|
||||||
},
|
|
||||||
"Subject" => {
|
|
||||||
let parse_result = parser::subject(value.trim().as_bytes());
|
|
||||||
let value = if parse_result.is_done() {
|
|
||||||
parse_result.to_full_result().unwrap()
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
};
|
|
||||||
mail.set_subject(value);
|
|
||||||
},
|
|
||||||
"Message-ID" | "Message-Id" | "Message-id" | "message-id" => {
|
|
||||||
mail.set_message_id(value);
|
|
||||||
},
|
|
||||||
"References" => {
|
|
||||||
{
|
|
||||||
let parse_result = parser::references(value.as_bytes());
|
|
||||||
if parse_result.is_done() {
|
|
||||||
for v in parse_result.to_full_result().unwrap() {
|
|
||||||
mail.push_references(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mail.set_references(value.to_string());
|
|
||||||
},
|
|
||||||
"In-Reply-To" | "In-reply-to" | "In-Reply-to" | "in-reply-to" => {
|
|
||||||
mail.set_in_reply_to(value);
|
|
||||||
in_reply_to = Some(value); },
|
|
||||||
"Date" => {
|
|
||||||
mail.set_date(value.to_string());
|
|
||||||
datetime = Some(value.to_string());
|
|
||||||
},
|
|
||||||
"Content-Transfer-Encoding" => {
|
|
||||||
builder.content_transfer_encoding(value);
|
|
||||||
},
|
|
||||||
"Content-Type" => {
|
|
||||||
builder.content_type(value);
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(ref mut x) = in_reply_to {
|
|
||||||
mail.push_references(x);
|
|
||||||
};
|
|
||||||
mail.set_body(builder.build());
|
|
||||||
if let Some(ref mut d) = datetime {
|
|
||||||
mail.set_datetime(parser::date(d));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(mail)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Mail {}
|
impl Eq for Mail {}
|
||||||
impl Ord for Mail {
|
impl Ord for Mail {
|
||||||
fn cmp(&self, other: &Mail) -> Ordering {
|
fn cmp(&self, other: &Mail) -> Ordering {
|
||||||
self.get_datetime().cmp(&other.get_datetime())
|
self.get_datetime().cmp(&other.get_datetime())
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,12 +110,22 @@ named!(header<(&str, &str)>,
|
||||||
named!(headers<std::vec::Vec<(&str, &str)>>,
|
named!(headers<std::vec::Vec<(&str, &str)>>,
|
||||||
many1!(complete!(header)));
|
many1!(complete!(header)));
|
||||||
|
|
||||||
|
named!(pub headers_raw<&[u8]>,
|
||||||
|
take_until1!("\n\n"));
|
||||||
|
|
||||||
|
named!(pub body_raw<&[u8]>,
|
||||||
|
do_parse!(
|
||||||
|
take_until1!("\n\n") >>
|
||||||
|
body: take_while!(call!(|_| true)) >>
|
||||||
|
( { body } )));
|
||||||
|
|
||||||
|
|
||||||
named!(pub mail<(std::vec::Vec<(&str, &str)>, &[u8])>,
|
named!(pub mail<(std::vec::Vec<(&str, &str)>, &[u8])>,
|
||||||
separated_pair!(headers, tag!("\n"), take_while!(call!(|_| { true }))));
|
separated_pair!(headers, tag!("\n"), take_while!(call!(|_| true))));
|
||||||
named!(pub attachment<(std::vec::Vec<(&str, &str)>, &[u8])>,
|
named!(pub attachment<(std::vec::Vec<(&str, &str)>, &[u8])>,
|
||||||
do_parse!(
|
do_parse!(
|
||||||
opt!(is_a!(" \n\t\r")) >>
|
opt!(is_a!(" \n\t\r")) >>
|
||||||
pair: pair!(many0!(complete!(header)), take_while!(call!(|_| { true }))) >>
|
pair: pair!(many0!(complete!(header)), take_while!(call!(|_| true))) >>
|
||||||
( { pair } )));
|
( { pair } )));
|
||||||
|
|
||||||
/* Header parsers */
|
/* Header parsers */
|
||||||
|
@ -250,7 +260,23 @@ named!(pub message_id<&str>,
|
||||||
map_res!(complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">"))), from_utf8)
|
map_res!(complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">"))), from_utf8)
|
||||||
);
|
);
|
||||||
|
|
||||||
named!(pub references<Vec<&str>>, many0!(preceded!(is_not!("<"), message_id)));
|
fn message_id_peek(input: &[u8]) -> IResult<&[u8],&str> {
|
||||||
|
let input_length = input.len();
|
||||||
|
if input.is_empty() {
|
||||||
|
IResult::Incomplete(Needed::Size(1))
|
||||||
|
} else if input_length == 2 || input[0] != b'<' {
|
||||||
|
IResult::Error(error_code!(ErrorKind::Custom(43)))
|
||||||
|
} else {
|
||||||
|
for i in 1..input_length {
|
||||||
|
if input[i] == b'>' {
|
||||||
|
return IResult::Done(&input[i+1..], from_utf8(&input[0..i+1]).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IResult::Incomplete(Needed::Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
named!(pub references<Vec<&str>>, separated_list!(complete!(is_a!(" \n\t\r")), message_id_peek));
|
||||||
|
|
||||||
named_args!(pub attachments<'a>(boundary: &'a str, boundary_end: &'a str) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
|
named_args!(pub attachments<'a>(boundary: &'a str, boundary_end: &'a str) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
|
||||||
alt_complete!(do_parse!(
|
alt_complete!(do_parse!(
|
||||||
|
|
|
@ -26,6 +26,8 @@ pub mod backends;
|
||||||
use mailbox::backends::MailBackend;
|
use mailbox::backends::MailBackend;
|
||||||
use mailbox::backends::maildir;
|
use mailbox::backends::maildir;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
|
pub mod accounts;
|
||||||
|
pub use mailbox::accounts::Account;
|
||||||
|
|
||||||
extern crate fnv;
|
extern crate fnv;
|
||||||
|
|
||||||
|
@ -36,7 +38,7 @@ use std;
|
||||||
type UnixTimestamp = i64;
|
type UnixTimestamp = i64;
|
||||||
|
|
||||||
/*a Mailbox represents a folder of mail. Currently only Maildir is supported.*/
|
/*a Mailbox represents a folder of mail. Currently only Maildir is supported.*/
|
||||||
#[derive(Debug)]
|
#[derive(Debug,Clone)]
|
||||||
pub struct Mailbox{
|
pub struct Mailbox{
|
||||||
pub path: String,
|
pub path: String,
|
||||||
pub collection: Vec<Mail>,
|
pub collection: Vec<Mail>,
|
||||||
|
@ -135,7 +137,8 @@ impl PartialEq for Thread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_collection(threads: &mut Vec<Thread>, id_table: &mut FnvHashMap<std::string::String, usize>, collection: &mut [Mail]) -> () {
|
fn build_collection(threads: &mut Vec<Thread>, id_table: &mut FnvHashMap<std::string::String, usize>, collection: &mut [Mail]) -> ()
|
||||||
|
{
|
||||||
for (i, x) in collection.iter_mut().enumerate() {
|
for (i, x) in collection.iter_mut().enumerate() {
|
||||||
let x_index; /* x's index in threads */
|
let x_index; /* x's index in threads */
|
||||||
let m_id = x.get_message_id_raw().to_string();
|
let m_id = x.get_message_id_raw().to_string();
|
||||||
|
@ -173,7 +176,7 @@ fn build_collection(threads: &mut Vec<Thread>, id_table: &mut FnvHashMap<std::st
|
||||||
*
|
*
|
||||||
* Find a Thread object for the given Message-ID:
|
* Find a Thread object for the given Message-ID:
|
||||||
* If there's one in id_table use that;
|
* If there's one in id_table use that;
|
||||||
* Otherwise, make (and index) one with a null Message
|
* Otherwise, make (and index) one with a null Message
|
||||||
*
|
*
|
||||||
* Link the References field's Threads together in the order implied by the References header.
|
* Link the References field's Threads together in the order implied by the References header.
|
||||||
* If they are already linked, don't change the existing links.
|
* If they are already linked, don't change the existing links.
|
||||||
|
@ -228,16 +231,14 @@ fn build_collection(threads: &mut Vec<Thread>, id_table: &mut FnvHashMap<std::st
|
||||||
None => { break 'date; },
|
None => { break 'date; },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if threads[curr_ref].parent.is_none() {
|
|
||||||
threads[curr_ref].parent = Some(parent_id);
|
|
||||||
}
|
|
||||||
curr_ref = parent_id;
|
curr_ref = parent_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mailbox {
|
impl Mailbox
|
||||||
pub fn new(path: &str, sent_folder: Option<&str>) -> Result<Mailbox> {
|
{
|
||||||
|
pub fn new(path: &str, sent_folder: &Option<Result<Mailbox>>) -> Result<Mailbox> {
|
||||||
let mut collection: Vec<Mail> = maildir::MaildirType::new(path).get()?;
|
let mut collection: Vec<Mail> = maildir::MaildirType::new(path).get()?;
|
||||||
/* To reconstruct thread information from the mails we need: */
|
/* To reconstruct thread information from the mails we need: */
|
||||||
|
|
||||||
|
@ -260,78 +261,84 @@ impl Mailbox {
|
||||||
* - if a message in this folder is a reply to a message we sent, we will add it to the
|
* - if a message in this folder is a reply to a message we sent, we will add it to the
|
||||||
* threading
|
* threading
|
||||||
*/
|
*/
|
||||||
if sent_folder.is_some() {
|
|
||||||
for mut x in maildir::MaildirType::new(sent_folder.unwrap()).get().unwrap() {
|
if let &Some(ref sent_box) = sent_folder {
|
||||||
if id_table.contains_key(x.get_message_id_raw()) ||
|
if sent_box.is_ok() {
|
||||||
(!x.get_in_reply_to_raw().is_empty() && id_table.contains_key(x.get_in_reply_to_raw())) {
|
let sent_mailbox = sent_box.as_ref();
|
||||||
collection.push(x.clone());
|
let sent_mailbox = sent_mailbox.unwrap();;
|
||||||
idx += 1;
|
|
||||||
}
|
for ref x in &sent_mailbox.collection {
|
||||||
if id_table.contains_key(x.get_message_id_raw()) {
|
if id_table.contains_key(x.get_message_id_raw()) ||
|
||||||
let c = id_table[x.get_message_id_raw()];
|
(!x.get_in_reply_to_raw().is_empty() && id_table.contains_key(x.get_in_reply_to_raw())) {
|
||||||
if threads[c].message.is_some() {
|
let mut x: Mail = (*x).clone();
|
||||||
/* skip duplicate message-id, but this should be handled instead */
|
if id_table.contains_key(x.get_message_id_raw()) {
|
||||||
continue;
|
let c = id_table[x.get_message_id_raw()];
|
||||||
}
|
if threads[c].message.is_some() {
|
||||||
threads[c].message = Some(idx-1);
|
/* skip duplicate message-id, but this should be handled instead */
|
||||||
assert!(threads[c].has_children());
|
continue;
|
||||||
threads[c].date = x.get_date();
|
}
|
||||||
x.set_thread(c);
|
threads[c].message = Some(idx);
|
||||||
}
|
assert!(threads[c].has_children());
|
||||||
if !x.get_in_reply_to_raw().is_empty() && id_table.contains_key(x.get_in_reply_to_raw()) {
|
threads[c].date = x.get_date();
|
||||||
let p = id_table[x.get_in_reply_to_raw()];
|
x.set_thread(c);
|
||||||
let c = if !id_table.contains_key(x.get_message_id_raw()) {
|
} else if !x.get_in_reply_to_raw().is_empty() && id_table.contains_key(x.get_in_reply_to_raw()) {
|
||||||
threads.push(
|
let p = id_table[x.get_in_reply_to_raw()];
|
||||||
Thread {
|
let c = if id_table.contains_key(x.get_message_id_raw()) {
|
||||||
message: Some(idx-1),
|
id_table[x.get_message_id_raw()]
|
||||||
id: tidx,
|
} else {
|
||||||
parent: Some(p),
|
threads.push(
|
||||||
first_child: None,
|
Thread {
|
||||||
next_sibling: None,
|
message: Some(idx),
|
||||||
date: x.get_date(),
|
id: tidx,
|
||||||
indentation: 0,
|
parent: Some(p),
|
||||||
show_subject: true,
|
first_child: None,
|
||||||
});
|
next_sibling: None,
|
||||||
id_table.insert(x.get_message_id_raw().to_string(), tidx);
|
date: x.get_date(),
|
||||||
x.set_thread(tidx);
|
indentation: 0,
|
||||||
tidx += 1;
|
show_subject: true,
|
||||||
tidx - 1
|
});
|
||||||
} else {
|
id_table.insert(x.get_message_id_raw().to_string(), tidx);
|
||||||
id_table[x.get_message_id_raw()]
|
x.set_thread(tidx);
|
||||||
};
|
tidx += 1;
|
||||||
threads[c].parent = Some(p);
|
tidx - 1
|
||||||
if threads[p].is_descendant(&threads, &threads[c]) ||
|
};
|
||||||
threads[c].is_descendant(&threads, &threads[p]) {
|
threads[c].parent = Some(p);
|
||||||
continue;
|
if threads[p].is_descendant(&threads, &threads[c]) ||
|
||||||
}
|
threads[c].is_descendant(&threads, &threads[p]) {
|
||||||
if threads[p].first_child.is_none() {
|
continue;
|
||||||
threads[p].first_child = Some(c);
|
}
|
||||||
} else {
|
if threads[p].first_child.is_none() {
|
||||||
let mut fc = threads[p].first_child.unwrap();
|
threads[p].first_child = Some(c);
|
||||||
while threads[fc].next_sibling.is_some() {
|
} else {
|
||||||
threads[fc].parent = Some(p);
|
let mut fc = threads[p].first_child.unwrap();
|
||||||
fc = threads[fc].next_sibling.unwrap();
|
while threads[fc].next_sibling.is_some() {
|
||||||
|
threads[fc].parent = Some(p);
|
||||||
|
fc = threads[fc].next_sibling.unwrap();
|
||||||
|
}
|
||||||
|
threads[fc].next_sibling = Some(c);
|
||||||
|
threads[fc].parent = Some(p);
|
||||||
|
}
|
||||||
|
/* update thread date */
|
||||||
|
let mut parent_iter = p;
|
||||||
|
'date: loop {
|
||||||
|
let p = &mut threads[parent_iter];
|
||||||
|
if p.date < x.get_date() {
|
||||||
|
p.date = x.get_date();
|
||||||
|
}
|
||||||
|
match p.parent {
|
||||||
|
Some(p) => { parent_iter = p; },
|
||||||
|
None => { break 'date; },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collection.push(x);
|
||||||
|
idx += 1;
|
||||||
}
|
}
|
||||||
threads[fc].next_sibling = Some(c);
|
|
||||||
threads[fc].parent = Some(p);
|
|
||||||
}
|
|
||||||
/* update thread date */
|
|
||||||
let mut parent_iter = p;
|
|
||||||
'date: loop {
|
|
||||||
let p = &mut threads[parent_iter];
|
|
||||||
if p.date < x.get_date() {
|
|
||||||
p.date = x.get_date();
|
|
||||||
}
|
|
||||||
match p.parent {
|
|
||||||
Some(p) => { parent_iter = p; },
|
|
||||||
None => { break 'date; },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Walk over the elements of id_table, and gather a list of the Thread objects that have
|
/* Walk over the elements of id_table, and gather a list of the Thread objects that have
|
||||||
* no parents. These are the root messages of each thread */
|
* no parents. These are the root messages of each thread */
|
||||||
let mut root_set = Vec::with_capacity(collection.len());
|
let mut root_set = Vec::with_capacity(collection.len());
|
||||||
'root_set: for v in id_table.values() {
|
'root_set: for v in id_table.values() {
|
||||||
if threads[*v].parent.is_none() {
|
if threads[*v].parent.is_none() {
|
||||||
|
@ -348,7 +355,8 @@ impl Mailbox {
|
||||||
|
|
||||||
/* Group messages together by thread in a collection so we can print them together */
|
/* Group messages together by thread in a collection so we can print them together */
|
||||||
let mut threaded_collection: Vec<usize> = Vec::with_capacity(collection.len());
|
let mut threaded_collection: Vec<usize> = Vec::with_capacity(collection.len());
|
||||||
fn build_threaded(threads: &mut Vec<Thread>, indentation: usize, threaded: &mut Vec<usize>, i: usize, root_subject_idx: usize, collection: &[Mail]) {
|
fn build_threaded(threads: &mut Vec<Thread>, indentation: usize, threaded: &mut Vec<usize>, i: usize, root_subject_idx: usize, collection: &[Mail])
|
||||||
|
{
|
||||||
let thread = threads[i];
|
let thread = threads[i];
|
||||||
if threads[root_subject_idx].has_message() {
|
if threads[root_subject_idx].has_message() {
|
||||||
let root_subject = collection[threads[root_subject_idx].get_message().unwrap()].get_subject();
|
let root_subject = collection[threads[root_subject_idx].get_message().unwrap()].get_subject();
|
||||||
|
|
|
@ -313,7 +313,8 @@ impl Window for Index {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Index {
|
impl Index {
|
||||||
pub fn new(mailbox: Mailbox) -> Index {
|
pub fn new(mailbox: &Mailbox) -> Index {
|
||||||
|
let mailbox = (*mailbox).clone();
|
||||||
let mut screen_height = 0;
|
let mut screen_height = 0;
|
||||||
let mut screen_width = 0;
|
let mut screen_width = 0;
|
||||||
/* Get the screen bounds. */
|
/* Get the screen bounds. */
|
||||||
|
|
Loading…
Reference in New Issue