Browse Source

rustfmt

Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
embed
Manos Pitsidianakis 5 years ago
parent
commit
fb745be27f
Signed by untrusted user: epilys GPG Key ID: 73627C2F690DF710
  1. 7
      benches/parse.rs
  2. 46
      src/bin.rs
  3. 82
      src/conf/mod.rs
  4. 15
      src/error.rs
  5. 46
      src/mailbox/accounts.rs
  6. 27
      src/mailbox/backends/maildir.rs
  7. 5
      src/mailbox/backends/mod.rs
  8. 252
      src/mailbox/email/attachments.rs
  9. 109
      src/mailbox/email/mod.rs
  10. 255
      src/mailbox/email/parser.rs
  11. 16
      src/mailbox/mod.rs
  12. 426
      src/mailbox/thread.rs
  13. 144
      src/ui/index.rs
  14. 2
      src/ui/mod.rs
  15. 68
      src/ui/pager.rs

7
benches/parse.rs

@ -10,6 +10,9 @@ use self::test::Bencher;
#[bench]
fn mail_parse(b: &mut Bencher) {
b.iter(|| Envelope::from(Box::new(BackendOpGenerator::new(Box::new(move || {
Box::new(MaildirOp::new("test/attachment_test".to_string()))})))) );
b.iter(|| {
Envelope::from(Box::new(BackendOpGenerator::new(Box::new(move || {
Box::new(MaildirOp::new("test/attachment_test".to_string()))
}))))
});
}

46
src/bin.rs

@ -37,17 +37,13 @@ fn main() {
let mut j = 0;
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::mv(0,0);
ncurses::mv(0, 0);
let mailbox = &mut account[j];
let mut index: Box<Window> = match *mailbox.as_ref().unwrap() {
Ok(ref v) => {
Box::new(Index::new(v))
},
Err(ref v) => {
Box::new(ErrorWindow::new((*v).clone()))
}
Ok(ref v) => Box::new(Index::new(v)),
Err(ref v) => Box::new(ErrorWindow::new((*v).clone())),
};
//eprintln!("{:?}", set);
ncurses::refresh();
@ -55,39 +51,35 @@ fn main() {
index.draw();
let mut ch;
'inner : loop {
'inner: loop {
ch = ncurses::get_wch();
match ch {
Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_UP)) |
Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_DOWN)) => {
index.handle_input(k);
continue;
}
Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_DOWN)) => {
index.handle_input(k);
continue;
}
Some(ncurses::WchResult::Char(k @ 10)) => {
index.handle_input(k as i32);
continue;
}
Some(ncurses::WchResult::KeyCode(ncurses::KEY_F1)) |
Some(ncurses::WchResult::Char(113)) => {
break 'main;
},
Some(ncurses::WchResult::Char(74)) => {
if j < folder_length - 1 {
j += 1;
break 'inner;
}
Some(ncurses::WchResult::Char(113)) => {
break 'main;
}
Some(ncurses::WchResult::Char(74)) => if j < folder_length - 1 {
j += 1;
break 'inner;
},
Some(ncurses::WchResult::Char(75)) => {
if j > 0 {
j -= 1;
break 'inner;
}
Some(ncurses::WchResult::Char(75)) => if j > 0 {
j -= 1;
break 'inner;
},
Some(ncurses::WchResult::KeyCode(ncurses::KEY_RESIZE)) => {
eprintln!("key_resize");
index.redraw();
continue;
},
}
_ => {}
}
}

82
src/conf/mod.rs

@ -19,27 +19,23 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
extern crate xdg;
extern crate config;
extern crate xdg;
use std::collections::HashMap;
use std::io;
use std::fs;
use std::path::{PathBuf, Path};
use std::path::{Path, PathBuf};
#[derive(Debug,Clone)]
#[derive(Debug, Clone)]
pub enum MailFormat {
Maildir
Maildir,
}
impl MailFormat {
pub fn from_str(x: &str) -> MailFormat {
match x.to_lowercase().as_ref() {
"maildir" => {
MailFormat::Maildir
},
_ => {
panic!("Unrecognizable mail format")
}
"maildir" => MailFormat::Maildir,
_ => panic!("Unrecognizable mail format"),
}
}
}
@ -49,7 +45,7 @@ struct FileAccount {
folders: String,
format: String,
sent_folder: String,
threaded : bool,
threaded: bool,
}
#[derive(Debug, Deserialize, Default)]
@ -57,16 +53,16 @@ struct FileSettings {
accounts: HashMap<String, FileAccount>,
}
#[derive(Debug,Clone)]
#[derive(Debug, Clone)]
pub struct AccountSettings {
pub folders: Vec<String>,
format: MailFormat,
pub sent_folder: String,
threaded : bool,
threaded: bool,
}
#[derive(Debug)]
pub struct Settings {
pub accounts: HashMap<String, AccountSettings>,
pub accounts: HashMap<String, AccountSettings>,
}
@ -74,8 +70,9 @@ use self::config::{Config, File, FileFormat};
impl FileSettings {
pub fn new() -> FileSettings {
let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").unwrap();
let config_path = xdg_dirs.place_config_file("config")
.expect("cannot create configuration directory");
let config_path = xdg_dirs
.place_config_file("config")
.expect("cannot create configuration directory");
//let setts = Config::default().merge(File::new(config_path.to_str().unwrap_or_default(), config::FileFormat::Toml)).unwrap();
let mut s = Config::new();
let s = s.merge(File::new(config_path.to_str().unwrap(), FileFormat::Toml));
@ -83,10 +80,14 @@ impl FileSettings {
if s.is_ok() {
s.unwrap().deserialize().unwrap()
} else {
eprintln!("{:?}",s.err().unwrap());
eprintln!("{:?}", s.err().unwrap());
let mut buf = String::new();
io::stdin().read_line(&mut buf).expect("Failed to read line");
FileSettings { ..Default::default() }
io::stdin()
.read_line(&mut buf)
.expect("Failed to read line");
FileSettings {
..Default::default()
}
}
}
}
@ -99,40 +100,39 @@ impl Settings {
for (id, x) in fs.accounts {
let mut folders = Vec::new();
fn recurse_folders<P: AsRef<Path>>(folders: &mut Vec<String>, p: P) {
for mut f in fs::read_dir(p).unwrap() {
for f in f.iter_mut() {
{
let path = f.path();
if path.ends_with("cur") || path.ends_with("new") ||
path.ends_with("tmp") {
for mut f in fs::read_dir(p).unwrap() {
for f in f.iter_mut() {
{
let path = f.path();
if path.ends_with("cur") || path.ends_with("new") ||
path.ends_with("tmp")
{
continue;
}
if path.is_dir() {
folders.push(path.to_str().unwrap().to_string());
recurse_folders(folders, path);
}
if path.is_dir() {
folders.push(path.to_str().unwrap().to_string());
recurse_folders(folders, path);
}
}
}
}
}
};
let path = PathBuf::from(&x.folders);
if path.is_dir() {
folders.push(path.to_str().unwrap().to_string());
}
recurse_folders(&mut folders, &x.folders);
s.insert(id.clone(), AccountSettings {
folders: folders,
format: MailFormat::from_str(&x.format),
sent_folder: x.sent_folder.clone(),
threaded: x.threaded,
});
s.insert(
id.clone(),
AccountSettings {
folders: folders,
format: MailFormat::from_str(&x.format),
sent_folder: x.sent_folder.clone(),
threaded: x.threaded,
},
);
}
Settings { accounts: s }
}
}

15
src/error.rs

@ -27,20 +27,25 @@ use nom;
pub type Result<T> = result::Result<T, MeliError>;
#[derive(Debug,Clone)]
#[derive(Debug, Clone)]
pub struct MeliError {
details: String
details: String,
}
impl MeliError {
pub fn new<M>(msg: M) -> MeliError where M: Into<String> {
MeliError{details: msg.into()}
pub fn new<M>(msg: M) -> MeliError
where
M: Into<String>,
{
MeliError {
details: msg.into(),
}
}
}
impl fmt::Display for MeliError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,"{}",self.details)
write!(f, "{}", self.details)
}
}

46
src/mailbox/accounts.rs

@ -21,7 +21,7 @@
use mailbox::*;
use conf::AccountSettings;
use std::ops::{IndexMut, Index};
use std::ops::{Index, IndexMut};
#[derive(Debug)]
pub struct Account {
name: String,
@ -29,19 +29,21 @@ pub struct Account {
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(|x| *x == settings.sent_folder);
let mut folders = Vec::with_capacity(settings.folders.len());
for _ in 0..settings.folders.len() {
folders.push(None);
}
eprintln!("new acc");
let sent_folder = settings
.folders
.iter()
.position(|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,
@ -60,8 +62,7 @@ impl Index<usize> for Account {
}
}
impl IndexMut<usize> for Account
{
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]);
@ -73,14 +74,18 @@ impl IndexMut<usize> for Account
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) =
{
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))
(
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();
@ -93,13 +98,14 @@ impl IndexMut<usize> for Account
eprintln!("Done!");
}
} else {
eprintln!("Now building folder {:?} without sent_folder", self.settings.folders[index]);
self.folders[index] = Some(Mailbox::new(&path, &None));
eprintln!("Done!");
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]
}
}

27
src/mailbox/backends/maildir.rs

@ -21,7 +21,7 @@
use mailbox::email::Envelope;
use error::{MeliError, Result};
use mailbox::backends::{MailBackend, BackendOp, BackendOpGenerator};
use mailbox::backends::{BackendOp, BackendOpGenerator, MailBackend};
use mailbox::email::parser;
extern crate crossbeam;
@ -33,7 +33,7 @@ pub struct MaildirType {
path: String,
}
#[derive(Debug,Default)]
#[derive(Debug, Default)]
pub struct MaildirOp {
path: String,
slice: Option<Mmap>,
@ -59,11 +59,13 @@ impl MaildirOp {
impl BackendOp for MaildirOp {
fn description(&self) -> String {
format!("Path of file: {}", self.path)
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());
self.slice = Some(
Mmap::open_path(self.path.to_string(), Protection::Read).unwrap(),
);
}
Ok(unsafe { self.slice.as_ref().unwrap().as_slice() })
}
@ -110,7 +112,7 @@ impl MailBackend for MaildirType {
impl MaildirType {
pub fn new(path: &str) -> Self {
MaildirType {
path: path.to_string()
path: path.to_string(),
}
}
fn is_valid(path: &str) -> Result<()> {
@ -118,7 +120,9 @@ impl MaildirType {
for d in &["cur", "new", "tmp"] {
p.push(d);
if !p.is_dir() {
return Err(MeliError::new(format!("{} is not a valid maildir folder", path)));
return Err(MeliError::new(
format!("{} is not a valid maildir folder", path),
));
}
p.pop();
}
@ -155,7 +159,6 @@ panic!("didn't parse"); },
r.push(m);
*/
}
let mut threads = Vec::with_capacity(cores);
if !files.is_empty() {
@ -163,16 +166,16 @@ panic!("didn't parse"); },
let chunk_size = if count / cores > 0 {
count / cores
} else {
count
count
};
for chunk in files.chunks(chunk_size) {
let s = scope.spawn(move || {
let mut local_r:Vec<Envelope> = Vec::with_capacity(chunk.len());
let mut local_r: Vec<Envelope> = Vec::with_capacity(chunk.len());
for e in chunk {
let e_copy = e.to_string();
if let Some(e) = Envelope::from(Box::new(BackendOpGenerator::new(Box::new(move || {
Box::new(MaildirOp::new(e_copy.clone()))
} )))) {
if let Some(e) = Envelope::from(Box::new(BackendOpGenerator::new(
Box::new(move || Box::new(MaildirOp::new(e_copy.clone()))),
))) {
local_r.push(e);
}
}

5
src/mailbox/backends/mod.rs

@ -75,9 +75,9 @@ pub trait BackendOp: ::std::fmt::Debug + ::std::marker::Send {
fn fetch_body(&mut self) -> Result<&[u8]>;
}
/// `BackendOpGenerator` is a wrapper for a closure that returns a `BackendOp` object
/// `BackendOpGenerator` is a wrapper for a closure that returns a `BackendOp` object
/// See `BackendOp` for details.
/*
/*
* I know this sucks, but that's the best way I found that rustc deems safe.
* */
pub struct BackendOpGenerator(Box<Fn() -> Box<BackendOp>>);
@ -97,4 +97,3 @@ impl fmt::Debug for BackendOpGenerator {
write!(f, "BackendOpGenerator: {}", op.description())
}
}

252
src/mailbox/email/attachments.rs

@ -30,20 +30,23 @@ use std::ascii::AsciiExt;
* Multipart
*/
#[derive(Clone,Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum MultipartType {
Mixed,
Alternative,
Digest,
Unsupported { tag: String },
}
#[derive(Clone,Debug)]
#[derive(Clone, Debug)]
pub enum AttachmentType {
Data { tag: String },
Text { content: String },
Multipart { of_type: MultipartType, subattachments: Vec<Attachment>, }
Multipart {
of_type: MultipartType,
subattachments: Vec<Attachment>,
},
}
#[derive(Clone,Debug)]
#[derive(Clone, Debug)]
pub enum ContentType {
Text,
Multipart { boundary: String },
@ -53,19 +56,13 @@ pub enum ContentType {
impl Display for ContentType {
fn fmt(&self, f: &mut Formatter) -> Result {
match *self {
ContentType::Text => {
write!(f, "text")
},
ContentType::Multipart { .. } => {
write!(f, "multipart")
},
ContentType::Unsupported { tag: ref t } => {
write!(f, "{}", t)
},
ContentType::Text => write!(f, "text"),
ContentType::Multipart { .. } => write!(f, "multipart"),
ContentType::Unsupported { tag: ref t } => write!(f, "{}", t),
}
}
}
#[derive(Clone,Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum ContentSubType {
Plain,
Other { tag: String },
@ -73,16 +70,12 @@ pub enum ContentSubType {
impl Display for ContentSubType {
fn fmt(&self, f: &mut Formatter) -> Result {
match *self {
ContentSubType::Plain => {
write!(f, "plain")
},
ContentSubType::Other { tag: ref t } => {
write!(f, "{}", t)
},
ContentSubType::Plain => write!(f, "plain"),
ContentSubType::Other { tag: ref t } => write!(f, "{}", t),
}
}
}
#[derive(Clone,Debug)]
#[derive(Clone, Debug)]
pub enum ContentTransferEncoding {
_8Bit,
_7Bit,
@ -110,111 +103,111 @@ impl AttachmentBuilder {
}
pub fn content_type(&mut self, value: &str) -> &Self {
match parser::content_type(value.as_bytes()).to_full_result() {
Ok((ct, cst, params)) => {
if ct.eq_ignore_ascii_case("multipart") {
let mut boundary = None;
for (n, v) in params {
if n.eq_ignore_ascii_case("boundary") {
boundary = Some(format!("--{}--", v).to_string());
break;
}
Ok((ct, cst, params)) => if ct.eq_ignore_ascii_case("multipart") {
let mut boundary = None;
for (n, v) in params {
if n.eq_ignore_ascii_case("boundary") {
boundary = Some(format!("--{}--", v).to_string());
break;
}
assert!(boundary.is_some());
self.content_type.0 = ContentType::Multipart { boundary: boundary.unwrap() };
self.content_type.1 = ContentSubType::Other { tag: cst.to_string() };
} else if ct.eq_ignore_ascii_case("text") {
self.content_type.0 = ContentType::Text;
if !cst.eq_ignore_ascii_case("plain") {
self.content_type.1 = ContentSubType::Other { tag: cst.to_ascii_lowercase() };
}
} else {
self.content_type.0 = ContentType::Unsupported { tag: ct.to_ascii_lowercase() };
self.content_type.1 = ContentSubType::Other { tag: cst.to_ascii_lowercase() };
}
assert!(boundary.is_some());
self.content_type.0 = ContentType::Multipart {
boundary: boundary.unwrap(),
};
self.content_type.1 = ContentSubType::Other {
tag: cst.to_string(),
};
} else if ct.eq_ignore_ascii_case("text") {
self.content_type.0 = ContentType::Text;
if !cst.eq_ignore_ascii_case("plain") {
self.content_type.1 = ContentSubType::Other {
tag: cst.to_ascii_lowercase(),
};
}
} else {
self.content_type.0 = ContentType::Unsupported {
tag: ct.to_ascii_lowercase(),
};
self.content_type.1 = ContentSubType::Other {
tag: cst.to_ascii_lowercase(),
};
},
Err(v) => {
eprintln!("parsing error in content_type: {:?} {:?}", value, v);
}
}
self
Err(v) => {
eprintln!("parsing error in content_type: {:?} {:?}", value, v);
}
}
self
}
pub fn content_transfer_encoding(&mut self, value: &str) -> &Self {
self.content_transfer_encoding =
if value.eq_ignore_ascii_case("base64") {
ContentTransferEncoding::Base64
} else if value.eq_ignore_ascii_case("7bit") {
ContentTransferEncoding::_7Bit
} else if value.eq_ignore_ascii_case("8bit") {
ContentTransferEncoding::_8Bit
} else if value.eq_ignore_ascii_case("quoted-printable") {
ContentTransferEncoding::QuotedPrintable
} else {
ContentTransferEncoding::Other { tag: value.to_ascii_lowercase() }
};
self.content_transfer_encoding = if value.eq_ignore_ascii_case("base64") {
ContentTransferEncoding::Base64
} else if value.eq_ignore_ascii_case("7bit") {
ContentTransferEncoding::_7Bit
} else if value.eq_ignore_ascii_case("8bit") {
ContentTransferEncoding::_8Bit
} else if value.eq_ignore_ascii_case("quoted-printable") {
ContentTransferEncoding::QuotedPrintable
} else {
ContentTransferEncoding::Other {
tag: value.to_ascii_lowercase(),
}
};
self
}
fn decode(&self) -> String {
match self.content_transfer_encoding {
ContentTransferEncoding::Base64 => {
match ::base64::decode(&::std::str::from_utf8(&self.raw).unwrap().trim().lines().fold(String::with_capacity(self.raw.len()), |mut acc, x| { acc.push_str(x); acc })) {
Ok( ref v ) => {
let s = String::from_utf8_lossy(v);
if s.find("\r\n").is_some() {
s.replace("\r\n","\n")
} else {
s.into_owned()
}
},
_ => {
String::from_utf8_lossy(&self.raw).into_owned()
match self.content_transfer_encoding {
ContentTransferEncoding::Base64 => {
match ::base64::decode(&::std::str::from_utf8(&self.raw)
.unwrap()
.trim()
.lines()
.fold(String::with_capacity(self.raw.len()), |mut acc, x| {
acc.push_str(x);
acc
})) {
Ok(ref v) => {
let s = String::from_utf8_lossy(v);
if s.find("\r\n").is_some() {
s.replace("\r\n", "\n")
} else {
s.into_owned()
}
}
},
ContentTransferEncoding::QuotedPrintable => {
parser::quoted_printable_text(&self.raw).to_full_result().unwrap()
},
ContentTransferEncoding::_7Bit | ContentTransferEncoding::_8Bit | ContentTransferEncoding::Other { .. } => {
String::from_utf8_lossy(&self.raw).into_owned()
_ => String::from_utf8_lossy(&self.raw).into_owned(),
}
}
ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_text(&self.raw)
.to_full_result()
.unwrap(),
ContentTransferEncoding::_7Bit |
ContentTransferEncoding::_8Bit |
ContentTransferEncoding::Other { .. } => {
String::from_utf8_lossy(&self.raw).into_owned()
}
}
}
pub fn build(self) -> Attachment {
let attachment_type =
match self.content_type.0 {
ContentType::Text => {
AttachmentType::Text { content: self.decode() }
let attachment_type = match self.content_type.0 {
ContentType::Text => AttachmentType::Text {
content: self.decode(),
},
ContentType::Multipart { boundary: ref b } => {
let multipart_type =
match self.content_type.1 {
ContentSubType::Other { ref tag } => {
match tag.as_ref() {
"mixed" => {
MultipartType::Mixed
},
"alternative" => {
MultipartType::Alternative
},
"digest" => {
MultipartType::Digest
},
t => {
MultipartType::Unsupported { tag: t.to_string() }
},
}
},
_ => {
panic!()
}
};
let multipart_type = match self.content_type.1 {
ContentSubType::Other { ref tag } => match tag.as_ref() {
"mixed" => MultipartType::Mixed,
"alternative" => MultipartType::Alternative,
"digest" => MultipartType::Digest,
t => MultipartType::Unsupported { tag: t.to_string() },
},
_ => panic!(),
};
AttachmentType::Multipart {
of_type: multipart_type,
subattachments: Attachment::subattachments(&self.raw, b),
}
},
ContentType::Unsupported { ref tag } => {
AttachmentType::Data { tag: tag.clone() }
},
}
ContentType::Unsupported { ref tag } => AttachmentType::Data { tag: tag.clone() },
};
Attachment {
content_type: self.content_type,
@ -222,13 +215,11 @@ self
raw: self.raw,
attachment_type: attachment_type,
}
}
}
#[derive(Clone,Debug)]
#[derive(Clone, Debug)]
pub struct Attachment {
content_type: (ContentType, ContentSubType),
content_transfer_encoding: ContentTransferEncoding,
@ -243,27 +234,25 @@ impl Attachment {
match self.attachment_type {
AttachmentType::Data { .. } => {
text.push_str(&format!("Data attachment of type {}", self.get_tag()));
},
}
AttachmentType::Text { content: ref t } => {
text.push_str(t);
},
}
AttachmentType::Multipart {
of_type: ref multipart_type,
subattachments: ref sub_att_vec,
} => {
if *multipart_type == MultipartType::Alternative {
for a in sub_att_vec {
if a.content_type.1 == ContentSubType::Plain {
a.get_text_recursive(text);
break;
}
}
} else {
for a in sub_att_vec {
} => if *multipart_type == MultipartType::Alternative {
for a in sub_att_vec {
if a.content_type.1 == ContentSubType::Plain {
a.get_text_recursive(text);
text.push_str("\n\n");
break;
}
}
} else {
for a in sub_att_vec {
a.get_text_recursive(text);
text.push_str("\n\n");
}
},
}
}
@ -280,7 +269,8 @@ impl Attachment {
}
pub fn subattachments(raw: &[u8], boundary: &str) -> Vec<Attachment> {
let boundary_length = boundary.len();
match parser::attachments(raw, &boundary[0..boundary_length - 2], boundary).to_full_result() {
match parser::attachments(raw, &boundary[0..boundary_length - 2], boundary).to_full_result()
{
Ok(attachments) => {
let mut vec = Vec::with_capacity(attachments.len());
for a in attachments {
@ -292,26 +282,30 @@ impl Attachment {
eprintln!("{}\n", ::std::string::String::from_utf8_lossy(a));
eprintln!("-------------------------------\n");
continue
continue;
}
};
let mut builder = AttachmentBuilder::new(body);
for (name, value) in headers {
if name.eq_ignore_ascii_case("content-type") {
builder.content_type(value);
} else if name.eq_ignore_ascii_case("content-transfer-encoding") {
} else if name.eq_ignore_ascii_case("content-transfer-encoding") {
builder.content_transfer_encoding(value);
}
}
vec.push(builder.build());
}
vec
},
}
a => {
eprintln!("error in 469 {:?}\n\traw: {:?}\n\tboundary: {:?}", a, ::std::str::from_utf8(raw).unwrap(), boundary);
eprintln!(
"error in 469 {:?}\n\traw: {:?}\n\tboundary: {:?}",
a,
::std::str::from_utf8(raw).unwrap(),
boundary
);
Vec::new()
},
}
}
}
}

109
src/mailbox/email/mod.rs

@ -25,6 +25,7 @@ use mailbox::backends::BackendOpGenerator;
use self::attachments::*;
use std::string::String;
use std::sync::Arc;
//use std;
use std::cmp::Ordering;
use std::fmt;
@ -36,7 +37,7 @@ use chrono;
use chrono::TimeZone;
/* Helper struct to return slices from a struct on demand */
#[derive(Clone,Debug)]
#[derive(Clone, Debug)]
struct StrBuilder {
offset: usize,
length: usize,
@ -49,15 +50,18 @@ pub trait StrBuild {
}
#[derive(Clone)]
pub struct MessageID (String, StrBuilder);
pub struct MessageID(String, StrBuilder);
impl StrBuild for MessageID {
fn new(string: &str, slice: &str) -> Self {
let offset = string.find(slice).unwrap();
MessageID (string.to_string(), StrBuilder {
offset: offset,
length: slice.len() + 1,
})
MessageID(
string.to_string(),
StrBuilder {
offset: offset,
length: slice.len() + 1,
},
)
}
fn get_raw(&self) -> &str {
let offset = self.1.offset;
@ -73,7 +77,16 @@ impl StrBuild for MessageID {
fn test_strbuilder() {
let m_id = "<20170825132332.6734-1-el13635@mail.ntua.gr>";
let (_, slice) = parser::message_id(m_id.as_bytes()).unwrap();
assert_eq!(MessageID::new(m_id, slice), MessageID (m_id.to_string(), StrBuilder{offset: 1, length: 43}));
assert_eq!(
MessageID::new(m_id, slice),
MessageID(
m_id.to_string(),
StrBuilder {
offset: 1,
length: 43,
}
)
);
}
impl PartialEq for MessageID {
@ -87,17 +100,15 @@ impl fmt::Debug for MessageID {
}
}
#[derive(Clone,Debug)]
#[derive(Clone, Debug)]
struct References {
raw: String,
refs: Vec<MessageID>,
}
use std::sync::Arc;
/* A very primitive mail object */
#[derive(Debug, Clone)]
pub struct Envelope
{
pub struct Envelope {
date: String,
from: Option<String>,
to: Option<String>,
@ -115,13 +126,16 @@ pub struct Envelope
}
impl Envelope
{
impl Envelope {
pub fn get_date(&self) -> i64 {
self.timestamp
self.timestamp
}
pub fn get_datetime(&self) -> chrono::DateTime<chrono::FixedOffset> {
self.datetime.unwrap_or_else(|| { chrono::FixedOffset::west(0).ymd(1970, 1, 1).and_hms(0, 0, 0)})
self.datetime.unwrap_or_else(|| {
chrono::FixedOffset::west(0)
.ymd(1970, 1, 1)
.and_hms(0, 0, 0)
})
}
pub fn get_date_as_str(&self) -> &str {
&self.date
@ -147,7 +161,7 @@ impl Envelope
let operation = self.operation_token.generate();
eprintln!("error in parsing mail\n{}", operation.description());
panic!()
},
}
};
let mut builder = AttachmentBuilder::new(body);
for (name, value) in headers {
@ -165,19 +179,19 @@ impl Envelope
pub fn get_subject(&self) -> &str {
match self.subject {
Some(ref s) => s,
_ => ""
_ => "",
}
}
pub fn get_in_reply_to(&self) -> &str {
match self.in_reply_to {
Some(ref s) => s.get_val(),
_ => ""
_ => "",
}
}
pub fn get_in_reply_to_raw(&self) -> &str {
match self.in_reply_to {
Some(ref s) => s.get_raw(),
_ => ""
_ => "",
}
}
pub fn get_message_id(&self) -> &str {
@ -203,10 +217,12 @@ impl Envelope
}
fn set_in_reply_to(&mut self, new_val: &str) -> () {
let slice = match parser::message_id(new_val.as_bytes()).to_full_result() {
Ok(v) => { v },
Err(v) => { eprintln!("{} {:?}",new_val, v);
Ok(v) => v,
Err(v) => {
eprintln!("{} {:?}", new_val, v);
self.in_reply_to = None;
return; }
return;
}
};
self.in_reply_to = Some(MessageID::new(new_val, slice));
}
@ -215,23 +231,27 @@ impl Envelope
}
fn set_message_id(&mut self, new_val: &str) -> () {
let slice = match parser::message_id(new_val.as_bytes()).to_full_result() {
Ok(v) => { v },
Err(v) => { eprintln!("{} {:?}",new_val, v);
Ok(v) => v,
Err(v) => {
eprintln!("{} {:?}", new_val, v);
self.message_id = None;
return; }
return;
}
};
self.message_id = Some(MessageID::new(new_val, slice));
}
fn push_references(&mut self, new_val: &str) -> () {
let slice = match parser::message_id(new_val.as_bytes()).to_full_result() {
Ok(v) => { v },
Err(v) => { eprintln!("{} {:?}",new_val, v);
return; }
Ok(v) => v,
Err(v) => {
eprintln!("{} {:?}", new_val, v);
return;
}
};
let new_ref = MessageID::new(new_val, slice);
match self.references {
Some(ref mut s) => {
if s.refs.contains(&new_ref) {
if s.refs.contains(&new_ref) {
if s.refs[s.refs.len() - 1] != new_ref {
if let Some(index) = s.refs.iter().position(|x| *x == new_ref) {
s.refs.remove(index);
@ -243,29 +263,39 @@ impl Envelope
}
}
s.refs.push(new_ref);
},
}
None => {
let mut v = Vec::new();
v.push(new_ref);
self.references = Some(References { raw: "".to_string(), refs: v, });
self.references = Some(References {
raw: "".to_string(),
refs: v,
});
}
}
}
fn set_references(&mut self, new_val: String) -> () {
match self.references {
Some(ref mut s) => {
s.raw = new_val;
},
}
None => {
let v = Vec::new();
self.references = Some(References { raw: new_val, refs: v, });
self.references = Some(References {
raw: new_val,
refs: v,
});
}
}
}
pub fn get_references(&self) -> Vec<&MessageID> {
match self.references {
Some(ref s) => s.refs.iter().fold(Vec::with_capacity(s.refs.len()), |mut acc, x| { acc.push(x); acc }),
Some(ref s) => s.refs
.iter()
.fold(Vec::with_capacity(s.refs.len()), |mut acc, x| {
acc.push(x);
acc
}),
None => Vec::new(),
}
}
@ -276,7 +306,7 @@ impl Envelope
self.thread
}
pub fn set_thread(&mut self, new_val: usize) -> () {
self.thread = new_val;
self.thread = new_val;
}
pub fn set_datetime(&mut self, new_val: Option<chrono::DateTime<chrono::FixedOffset>>) -> () {
self.datetime = new_val;
@ -301,20 +331,17 @@ impl Envelope
thread: 0,
operation_token: Arc::new(token),
}
}
pub fn from(operation_token: Box<BackendOpGenerator>) -> Option<Envelope> {
let mut operation = operation_token.generate();
let headers = match parser::headers(operation.fetch_headers().unwrap()).to_full_result() {
Ok(v) => {
v
},
Ok(v) => v,
_ => {
let operation = operation_token.generate();
eprintln!("error in parsing mail\n{}", operation.description());
return None;
},
}
};
let mut mail = Envelope::new(operation_token);

255
src/mailbox/email/parser.rs

@ -22,12 +22,12 @@ use std;
use std::str::from_utf8;
use base64;
use chrono;
use nom::{le_u8, is_hex_digit};
use nom::{IResult,Needed,ErrorKind};
use nom::{is_hex_digit, le_u8};
use nom::{ErrorKind, IResult, Needed};
use nom::{Compare, CompareResult};
use encoding::{Encoding, DecoderTrap};
use encoding::{DecoderTrap, Encoding};
fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8],u8> {
fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8], u8> {
if input.is_empty() || input.len() < 3 {
IResult::Incomplete(Needed::Size(1))
} else if input[0] == b'=' && is_hex_digit(input[1]) && is_hex_digit(input[2]) {
@ -45,7 +45,7 @@ fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8],u8> {
} else {
input[2] - 87
};
IResult::Done(&input[3..], a*16+b)
IResult::Done(&input[3..], a * 16 + b)
} else {
IResult::Error(error_code!(ErrorKind::Custom(43)))
}
@ -70,18 +70,15 @@ fn header_value(input: &[u8]) -> IResult<&[u8], &str> {
for (i, x) in input.iter().enumerate() {
if *x == b'\n' {
if (i + 1) < input_len &&
((input[i+1] != b' ' && input[i+1] != b'\t') || input[i+1] == b'\n') {
return match from_utf8(&input[0..i]) {
Ok(v) => {
IResult::Done(&input[(i+1)..], v)
},
Err(_) => {
IResult::Error(error_code!(ErrorKind::Custom(43)))
},
}
} else if i + 1 > input_len {
return IResult::Incomplete(Needed::Size(1));
}
((input[i + 1] != b' ' && input[i + 1] != b'\t') || input[i + 1] == b'\n')
{
return match from_utf8(&input[0..i]) {
Ok(v) => IResult::Done(&input[(i + 1)..], v),
Err(_) => IResult::Error(error_code!(ErrorKind::Custom(43))),
};
} else if i + 1 > input_len {
return IResult::Incomplete(Needed::Size(1));
}
}
}
IResult::Error(error_code!(ErrorKind::Custom(43)))
@ -90,12 +87,13 @@ fn header_value(input: &[u8]) -> IResult<&[u8], &str> {
/* Parse the name part of the header -> &str */
named!(name<&str>,
map_res!(is_not!(":\n"), from_utf8));
named!(name<&str>, map_res!(is_not!(":\n"), from_utf8));
/* Parse a single header as a tuple -> (&str, Vec<&str>) */
named!(header<(&str, &str)>,
separated_pair!(complete!(name), ws!(tag!(":")), complete!(header_value)));
named!(
header<(&str, &str)>,
separated_pair!(complete!(name), ws!(tag!(":")), complete!(header_value))
);
/* Parse all headers -> Vec<(&str, Vec<&str>)> */
named!(pub headers<std::vec::Vec<(&str, &str)>>,
many1!(complete!(header)));
@ -126,103 +124,95 @@ named!(pub attachment<(std::vec::Vec<(&str, &str)>, &[u8])>,
/* TODO: make a map of encodings and decoding functions so that they can be reused and easily
* extended */
use encoding::all::{ISO_8859_1,ISO_8859_2, ISO_8859_7, WINDOWS_1253, GBK};
use encoding::all::{ISO_8859_1, ISO_8859_2, ISO_8859_7, WINDOWS_1253, GBK};
fn encoded_word(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
if input.len() < 5 {
return IResult::Incomplete(Needed::Unknown);
} else if input[0] != b'=' || input[1] != b'?' {
return IResult::Error(error_code!(ErrorKind::Custom(43)))
return IResult::Error(error_code!(ErrorKind::Custom(43)));
}
for tag in &["UTF-8", "iso-8859-7", "windows-1253", "iso-8859-1", "iso-8859-2", "gbk"] {
for tag in &[
"UTF-8",
"iso-8859-7",
"windows-1253",
"iso-8859-1",
"iso-8859-2",
"gbk",
] {
if let CompareResult::Ok = (&input[2..]).compare_no_case(*tag) {
let tag_len = tag.len();
/* tag must end with ?_? where _ is either Q or B, eg: =?UTF-8?B? */
if input[2+tag_len] != b'?' || input[2+tag_len+2] != b'?' {
return IResult::Error(error_code!(ErrorKind::Custom(43)))
if input[2 + tag_len] != b'?' || input[2 + tag_len + 2] != b'?' {
return IResult::Error(error_code!(ErrorKind::Custom(43)));
}
/* See if input ends with "?=" and get ending index */
let mut encoded_idx = None;
for i in (5+tag_len)..input.len() {
if input[i] == b'?' && i < input.len() && input[i+1] == b'=' {
for i in (5 + tag_len)..input.len() {
if input[i] == b'?' && i < input.len() && input[i + 1] == b'=' {
encoded_idx = Some(i);
break;
}
};
}
if encoded_idx.is_none() {
return IResult::Error(error_code!(ErrorKind::Custom(43)))
return IResult::Error(error_code!(ErrorKind::Custom(43)));
}
let encoded = &input[5+tag_len..encoded_idx.unwrap()];
let s:Vec<u8> = match input[2+tag_len+1] {
b'b' | b'B' => {
match base64::decode(encoded) {
Ok(v) => {
v
},
Err(_) => {
encoded.to_vec()
},
}
},
b'q' | b'Q' => {
match get_quoted_printed_bytes(encoded) {
IResult::Done(b"", s) => {
s
},
_ => {
return IResult::Error(error_code!(ErrorKind::Custom(43)))
},
}
let encoded = &input[5 + tag_len..encoded_idx.unwrap()];
let s: Vec<u8> = match input[2 + tag_len + 1] {
b'b' | b'B' => match base64::decode(encoded) {
Ok(v) => v,
Err(_) => encoded.to_vec(),
},
_ => {
return IResult::Error(error_code!(ErrorKind::Custom(43)))
b'q' | b'Q' => match get_quoted_printed_bytes(encoded) {
IResult::Done(b"", s) => s,
_ => return IResult::Error(error_code!(ErrorKind::Custom(43))),
},
_ => return IResult::Error(error_code!(ErrorKind::Custom(43))),
};
match *tag {
"UTF-8" =>