various fixes
- Clippy fixes - Rewrite header value parser - Rewrite string allocations in header encodings - Thread mail parsing for maildir.rs - Split crate to lib and binembed
parent
63670259f8
commit
04ff21a55f
12
Cargo.toml
12
Cargo.toml
|
@ -3,6 +3,14 @@ name = "meli"
|
|||
version = "0.1.0"
|
||||
authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"]
|
||||
|
||||
[lib]
|
||||
name = "melib"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "meli"
|
||||
path = "src/bin.rs"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4"
|
||||
xdg = "2.1.0"
|
||||
|
@ -12,6 +20,7 @@ serde = "^1.0.8"
|
|||
nom = "3.2.0"
|
||||
memmap = "*"
|
||||
base64 = "*"
|
||||
crossbeam = "^0.3.0"
|
||||
|
||||
[dependencies.ncurses]
|
||||
features = ["wide"]
|
||||
|
@ -19,5 +28,4 @@ optional = false
|
|||
version = "5.86.0"
|
||||
|
||||
[profile.release]
|
||||
#lto = true
|
||||
opt-level = 2
|
||||
lto = true
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
#![feature(test)]
|
||||
extern crate melib;
|
||||
use melib::mailbox::backends::MailBackend;
|
||||
use melib::mailbox::backends::maildir::*;
|
||||
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_threads_1(b: &mut Bencher) {
|
||||
b.iter(|| MaildirType::new("").get_multicore(1));
|
||||
}
|
||||
#[bench]
|
||||
fn bench_threads_2(b: &mut Bencher) {
|
||||
b.iter(|| MaildirType::new("").get_multicore(2));
|
||||
}
|
||||
#[bench]
|
||||
fn bench_threads_3(b: &mut Bencher) {
|
||||
b.iter(|| MaildirType::new("").get_multicore(3));
|
||||
}
|
||||
#[bench]
|
||||
fn bench_threads_4(b: &mut Bencher) {
|
||||
b.iter(|| MaildirType::new("").get_multicore(4));
|
||||
}
|
||||
#[bench]
|
||||
fn bench_threads_6(b: &mut Bencher) {
|
||||
b.iter(|| MaildirType::new("").get_multicore(6));
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* meli - main.rs
|
||||
* meli - bin.rs
|
||||
*
|
||||
* Copyright 2017 Manos Pitsidianakis
|
||||
*
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
|
@ -18,41 +18,31 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
extern crate ncurses;
|
||||
|
||||
pub mod mailbox;
|
||||
mod ui;
|
||||
mod conf;
|
||||
mod error;
|
||||
|
||||
use ui::index::*;
|
||||
|
||||
extern crate melib;
|
||||
use melib::*;
|
||||
use mailbox::*;
|
||||
use conf::*;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
/* parser */
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
extern crate chrono;
|
||||
extern crate base64;
|
||||
extern crate memmap;
|
||||
extern crate ncurses;
|
||||
|
||||
fn main() {
|
||||
let locale_conf = ncurses::LcCategory::all;
|
||||
ncurses::setlocale(locale_conf, "en_US.UTF-8");
|
||||
let set = Settings::new();
|
||||
let ui = ui::TUI::initialize();
|
||||
//let mailbox = Mailbox::new("/home/epilys/Downloads/rust/nutt/Inbox4");
|
||||
let mut j = 0;
|
||||
let folder_length = set.accounts.get("norn").unwrap().folders.len();
|
||||
let folder_length = set.accounts["norn"].folders.len();
|
||||
'main : loop {
|
||||
ncurses::touchwin(ncurses::stdscr());
|
||||
ncurses::mv(0,0);
|
||||
let mailbox = Mailbox::new(&set.accounts.get("norn").unwrap().folders[j],
|
||||
Some(&set.accounts.get("norn").unwrap().sent_folder));
|
||||
let mailbox = Mailbox::new(&set.accounts["norn"].folders[j],
|
||||
Some(&set.accounts["norn"].sent_folder));
|
||||
let mut index: Box<Window> = match mailbox {
|
||||
Ok(v) => {
|
||||
Ok(v) => {
|
||||
Box::new(Index::new(v))
|
||||
},
|
||||
Err(v) => {
|
||||
|
@ -93,6 +83,11 @@ fn main() {
|
|||
break 'inner;
|
||||
}
|
||||
},
|
||||
Some(ncurses::WchResult::KeyCode(ncurses::KEY_RESIZE)) => {
|
||||
eprintln!("key_resize");
|
||||
index.redraw();
|
||||
continue;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
* meli - configuration module.
|
||||
*
|
||||
* Copyright 2017 Manos Pitsidianakis
|
||||
*
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
|
@ -21,7 +21,6 @@
|
|||
|
||||
extern crate xdg;
|
||||
extern crate config;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::fs;
|
||||
|
@ -34,10 +33,13 @@ enum MailFormat {
|
|||
|
||||
impl MailFormat {
|
||||
pub fn from_str(x: &str) -> MailFormat {
|
||||
match x {
|
||||
"maildir" | "Maildir" |
|
||||
"MailDir" => { MailFormat::Maildir },
|
||||
_ => { panic!("Unrecognizable mail format");}
|
||||
match x.to_lowercase().as_ref() {
|
||||
"maildir" => {
|
||||
MailFormat::Maildir
|
||||
},
|
||||
_ => {
|
||||
panic!("Unrecognizable mail format")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,13 +80,13 @@ impl FileSettings {
|
|||
let mut s = Config::new();
|
||||
let s = s.merge(File::new(config_path.to_str().unwrap(), FileFormat::Toml));
|
||||
|
||||
match s.is_ok() { //.unwrap_or(Settings { });
|
||||
true => { s.unwrap().deserialize().unwrap() },
|
||||
false => {
|
||||
eprintln!("{:?}",s.err().unwrap());
|
||||
let mut buf = String::new();
|
||||
io::stdin().read_line(&mut buf).expect("Failed to read line");
|
||||
FileSettings { ..Default::default() } },
|
||||
if s.is_ok() {
|
||||
s.unwrap().deserialize().unwrap()
|
||||
} else {
|
||||
eprintln!("{:?}",s.err().unwrap());
|
||||
let mut buf = String::new();
|
||||
io::stdin().read_line(&mut buf).expect("Failed to read line");
|
||||
FileSettings { ..Default::default() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,13 +94,13 @@ impl FileSettings {
|
|||
impl Settings {
|
||||
pub fn new() -> Settings {
|
||||
let fs = FileSettings::new();
|
||||
let mut s: HashMap<String, Account> = HashMap::new();
|
||||
|
||||
let mut s: HashMap<String, Account> = HashMap::new();
|
||||
|
||||
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().next() {
|
||||
for f in f.iter_mut() {
|
||||
{
|
||||
let path = f.path();
|
||||
if path.ends_with("cur") || path.ends_with("new") ||
|
||||
|
@ -110,8 +112,8 @@ impl Settings {
|
|||
recurse_folders(folders, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
let path = PathBuf::from(&x.folders);
|
||||
|
|
20
src/error.rs
20
src/error.rs
|
@ -1,3 +1,23 @@
|
|||
/*
|
||||
* meli - error 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 std::error::Error;
|
||||
use std::fmt;
|
||||
use std::result;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* meli - lib.rs
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
pub mod mailbox;
|
||||
pub mod conf;
|
||||
pub mod error;
|
||||
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
/* parser */
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
extern crate chrono;
|
||||
extern crate base64;
|
||||
extern crate memmap;
|
|
@ -2,7 +2,7 @@
|
|||
* meli - mailbox module.
|
||||
*
|
||||
* Copyright 2017 Manos Pitsidianakis
|
||||
*
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
|
@ -18,20 +18,24 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//use std::io::prelude::*;
|
||||
//use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use mailbox::email::Mail;
|
||||
use error::{MeliError, Result};
|
||||
|
||||
use mailbox::backends::MailBackend;
|
||||
|
||||
extern crate crossbeam;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
pub struct MaildirType {
|
||||
path: String,
|
||||
}
|
||||
|
||||
impl MailBackend for MaildirType {
|
||||
fn get(&self) -> Result<Vec<Mail>> {
|
||||
self.get_multicore(4)
|
||||
/*
|
||||
|
||||
MaildirType::is_valid(&self.path)?;
|
||||
let mut path = PathBuf::from(&self.path);
|
||||
path.push("cur");
|
||||
|
@ -44,10 +48,48 @@ impl MailBackend for MaildirType {
|
|||
let path = x.path();
|
||||
Ok(path.to_str().unwrap().to_string())
|
||||
})?;
|
||||
match Mail::from(e) {
|
||||
match Mail::from(&e) {
|
||||
Some(e) => {r.push(e);},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
Ok(r)
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
impl MaildirType {
|
||||
pub fn new(path: &str) -> Self {
|
||||
MaildirType {
|
||||
path: path.to_string()
|
||||
}
|
||||
}
|
||||
fn is_valid(path: &str) -> Result<()> {
|
||||
let mut p = PathBuf::from(path);
|
||||
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)));
|
||||
}
|
||||
p.pop();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn get_multicore(&self, cores: usize) -> Result<Vec<Mail>> {
|
||||
MaildirType::is_valid(&self.path)?;
|
||||
let mut path = PathBuf::from(&self.path);
|
||||
path.push("cur");
|
||||
let iter = path.read_dir()?;
|
||||
let count = path.read_dir()?.count();
|
||||
let mut files: Vec<String> = Vec::with_capacity(count);
|
||||
let mut r = Vec::with_capacity(count);
|
||||
for e in iter {
|
||||
//eprintln!("{:?}", e);
|
||||
let e = e.and_then(|x| {
|
||||
let path = x.path();
|
||||
Ok(path.to_str().unwrap().to_string())
|
||||
})?;
|
||||
files.push(e);
|
||||
/*
|
||||
|
||||
f.read_to_end(&mut buffer)?;
|
||||
|
@ -66,25 +108,32 @@ panic!("didn't parse"); },
|
|||
*/
|
||||
|
||||
}
|
||||
let mut threads = Vec::with_capacity(cores);
|
||||
if !files.is_empty() {
|
||||
crossbeam::scope(|scope| {
|
||||
let chunk_size = if count / cores > 0 {
|
||||
count / cores
|
||||
} else {
|
||||
count
|
||||
};
|
||||
for chunk in files.chunks(chunk_size) {
|
||||
let s = scope.spawn(move || {
|
||||
let mut local_r:Vec<Mail> = Vec::with_capacity(chunk.len());
|
||||
for e in chunk {
|
||||
if let Some(e) = Mail::from(e) {
|
||||
local_r.push(e);
|
||||
}
|
||||
}
|
||||
local_r
|
||||
});
|
||||
threads.push(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
for t in threads {
|
||||
let mut result = t.join();
|
||||
r.append(&mut result);
|
||||
}
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl MaildirType {
|
||||
pub fn new(path: &str) -> Self {
|
||||
MaildirType {
|
||||
path: path.to_string()
|
||||
}
|
||||
}
|
||||
fn is_valid(path: &str) -> Result<()> {
|
||||
let mut p = PathBuf::from(path);
|
||||
for d in ["cur", "new", "tmp"].iter() {
|
||||
p.push(d);
|
||||
if !p.is_dir() {
|
||||
return Err(MeliError::new(format!("{} is not a valid maildir folder", path)));
|
||||
}
|
||||
p.pop();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
/*
|
||||
* meli - backends 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/>.
|
||||
*/
|
||||
pub mod maildir;
|
||||
|
||||
use mailbox::email::Mail;
|
||||
|
|
|
@ -1,4 +1,26 @@
|
|||
use super::parser;
|
||||
/*
|
||||
* meli - attachments 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::email::parser;
|
||||
|
||||
use std::fmt::{Display, Formatter, Result};
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -18,7 +40,7 @@ pub enum MultipartType {
|
|||
pub enum AttachmentType {
|
||||
Data { tag: String },
|
||||
Text { content: String },
|
||||
Multipart { of_type: MultipartType, subattachments: Vec<Box<Attachment>>, }
|
||||
Multipart { of_type: MultipartType, subattachments: Vec<Attachment>, }
|
||||
}
|
||||
#[derive(Clone,Debug)]
|
||||
pub enum ContentType {
|
||||
|
@ -27,13 +49,13 @@ pub enum ContentType {
|
|||
Unsupported { tag: String },
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for ContentType {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
impl Display for ContentType {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
match *self {
|
||||
ContentType::Text => {
|
||||
write!(f, "text")
|
||||
},
|
||||
ContentType::Multipart { boundary: _ } => {
|
||||
ContentType::Multipart { .. } => {
|
||||
write!(f, "multipart")
|
||||
},
|
||||
ContentType::Unsupported { tag: ref t } => {
|
||||
|
@ -47,8 +69,8 @@ pub enum ContentSubType {
|
|||
Plain,
|
||||
Other { tag: String },
|
||||
}
|
||||
impl ::std::fmt::Display for ContentSubType {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
impl Display for ContentSubType {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
match *self {
|
||||
ContentSubType::Plain => {
|
||||
write!(f, "plain")
|
||||
|
@ -72,7 +94,7 @@ pub struct AttachmentBuilder {
|
|||
content_type: (ContentType, ContentSubType),
|
||||
content_transfer_encoding: ContentTransferEncoding,
|
||||
|
||||
raw: Box<Vec<u8>>,
|
||||
raw: Vec<u8>,
|
||||
}
|
||||
|
||||
impl AttachmentBuilder {
|
||||
|
@ -80,18 +102,18 @@ impl AttachmentBuilder {
|
|||
AttachmentBuilder {
|
||||
content_type: (ContentType::Text, ContentSubType::Plain),
|
||||
content_transfer_encoding: ContentTransferEncoding::_7Bit,
|
||||
raw: Box::new(content.to_vec()),
|
||||
raw: content.to_vec(),
|
||||
}
|
||||
}
|
||||
pub fn content_type(&mut self, value: &str) -> &Self {
|
||||
match parser::content_type(value.as_bytes()).to_full_result() {
|
||||
Ok((ct, cst, params)) => {
|
||||
Ok((ct, cst, params)) => {
|
||||
match ct.to_lowercase().as_ref() {
|
||||
"multipart" => {
|
||||
let mut boundary = None;
|
||||
for (n, v) in params {
|
||||
if n.to_lowercase() == "boundary" {
|
||||
boundary = Some(format!("--{}", v).to_string());
|
||||
boundary = Some(format!("--{}--", v).to_string());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -114,9 +136,10 @@ impl AttachmentBuilder {
|
|||
self.content_type.1 = ContentSubType::Other { tag: cst.to_string() };
|
||||
},
|
||||
}
|
||||
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
Err(v) => {
|
||||
eprintln!("parsing error in content_type: {:?} {:?}", value, v);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
@ -156,10 +179,7 @@ impl AttachmentBuilder {
|
|||
ContentTransferEncoding::QuotedPrintable => {
|
||||
parser::quoted_printable_text(&self.raw).to_full_result().unwrap()
|
||||
},
|
||||
ContentTransferEncoding::_7Bit | ContentTransferEncoding::_8Bit => {
|
||||
String::from_utf8_lossy(&self.raw).into_owned()
|
||||
},
|
||||
ContentTransferEncoding::Other { tag: _ } => {
|
||||
ContentTransferEncoding::_7Bit | ContentTransferEncoding::_8Bit | ContentTransferEncoding::Other { .. } => {
|
||||
String::from_utf8_lossy(&self.raw).into_owned()
|
||||
}
|
||||
}
|
||||
|
@ -190,12 +210,12 @@ impl AttachmentBuilder {
|
|||
}
|
||||
},
|
||||
_ => {
|
||||
panic!();
|
||||
panic!()
|
||||
}
|
||||
};
|
||||
AttachmentType::Multipart {
|
||||
of_type: multipart_type,
|
||||
subattachments: Attachment::subattachments(&self.raw, &b),
|
||||
subattachments: Attachment::subattachments(&self.raw, b),
|
||||
}
|
||||
},
|
||||
ContentType::Unsupported { ref tag } => {
|
||||
|
@ -219,7 +239,7 @@ pub struct Attachment {
|
|||
content_type: (ContentType, ContentSubType),
|
||||
content_transfer_encoding: ContentTransferEncoding,
|
||||
|
||||
raw: Box<Vec<u8>>,
|
||||
raw: Vec<u8>,
|
||||
|
||||
attachment_type: AttachmentType,
|
||||
}
|
||||
|
@ -227,7 +247,7 @@ pub struct Attachment {
|
|||
impl Attachment {
|
||||
fn get_text_recursive(&self, text: &mut String) {
|
||||
match self.attachment_type {
|
||||
AttachmentType::Data { tag: _ } => {
|
||||
AttachmentType::Data { .. } => {
|
||||
text.push_str(&format!("Data attachment of type {}", self.get_tag()));
|
||||
},
|
||||
AttachmentType::Text { content: ref t } => {
|
||||
|
@ -264,8 +284,9 @@ impl Attachment {
|
|||
pub fn get_tag(&self) -> String {
|
||||
format!("{}/{}", self.content_type.0, self.content_type.1).to_string()
|
||||
}
|
||||
pub fn subattachments(raw: &[u8], boundary: &str) -> Vec<Box<Attachment>> {
|
||||
match parser::attachments(raw, boundary, &format!("{}--", boundary)).to_full_result() {
|
||||
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() {
|
||||
Ok(attachments) => {
|
||||
let mut vec = Vec::with_capacity(attachments.len());
|
||||
for a in attachments {
|
||||
|
@ -277,24 +298,23 @@ 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 {
|
||||
match name.to_lowercase().as_ref(){
|
||||
"content-type" => {
|
||||
let value = value.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc });
|
||||
builder.content_type(&value);
|
||||
builder.content_type(value);
|
||||
},
|
||||
"content-transfer-encoding" => {
|
||||
let value = value.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc });
|
||||
builder.content_transfer_encoding(&value);
|
||||
builder.content_transfer_encoding(value);
|
||||
},
|
||||
_ => {
|
||||
},
|
||||
}
|
||||
}
|
||||
vec.push(Box::new(builder.build()));
|
||||
vec.push(builder.build());
|
||||
}
|
||||
vec
|
||||
},
|
||||
|
|
|
@ -1,15 +1,35 @@
|
|||
/*
|
||||
* meli - email 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/>.
|
||||
*/
|
||||
|
||||
mod parser;
|
||||
mod attachments;
|
||||
use self::attachments::*;
|
||||
|
||||
use std::string::String;
|
||||
use memmap::{Mmap, Protection};
|
||||
use std;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::option::Option;
|
||||
|
||||
use std::io::prelude::*;
|
||||
mod parser;
|
||||
mod attachments;
|
||||
|
||||
use self::attachments::*;
|
||||
|
||||
use chrono;
|
||||
use chrono::TimeZone;
|
||||
|
@ -73,7 +93,7 @@ struct References {
|
|||
}
|
||||
|
||||
/* A very primitive mail object */
|
||||
#[derive(Clone,Debug)]
|
||||
#[derive(Clone,Debug,Default)]
|
||||
pub struct Mail {
|
||||
date: String,
|
||||
from: Option<String>,
|
||||
|
@ -96,7 +116,7 @@ impl Mail {
|
|||
}
|
||||
}
|
||||
pub fn get_datetime(&self) -> chrono::DateTime<chrono::FixedOffset> {
|
||||
self.datetime.unwrap_or(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
|
||||
|
@ -209,9 +229,9 @@ impl Mail {
|
|||
}
|
||||
}
|
||||
}
|
||||
pub fn get_references<'a>(&'a self) -> Vec<&'a MessageID> {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
@ -243,21 +263,21 @@ impl Mail {
|
|||
thread: 0,
|
||||
}
|
||||
}
|
||||
pub fn from(path: std::string::String) -> Option<Self> {
|
||||
let f = Mmap::open_path(path.clone(), Protection::Read).unwrap();
|
||||
pub fn from(path: &str) -> Option<Self> {
|
||||
let f = Mmap::open_path(path.to_string(), Protection::Read).unwrap();
|
||||
let file = unsafe { f.as_slice() };
|
||||
let (headers, body) = match parser::mail(file).to_full_result() {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
eprintln!("error in parsing");
|
||||
let path = std::path::PathBuf::from(&path);
|
||||
Err(_) => {
|
||||
eprintln!("error in parsing mail");
|
||||
let path = std::path::PathBuf::from(path);
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
let _ = std::fs::File::open(path).unwrap().read_to_end(&mut buffer);
|
||||
eprintln!("\n-------------------------------");
|
||||
eprintln!("{}\n", std::string::String::from_utf8_lossy(&buffer));
|
||||
eprintln!("{}\n", std::string::String::from_utf8_lossy(&buffer));
|
||||
eprintln!("-------------------------------\n");
|
||||
|
||||
|
||||
return None; }
|
||||
};
|
||||
let mut mail = Mail::new();
|
||||
|
@ -266,96 +286,73 @@ impl Mail {
|
|||
|
||||
let mut builder = AttachmentBuilder::new(body);
|
||||
for (name, value) in headers {
|
||||
if value.len() == 1 && value[0].is_empty() {
|
||||
if value.len() == 1 && value.is_empty() {
|
||||
continue;
|
||||
}
|
||||
match name {
|
||||
"To" => {
|
||||
let value = value.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc });
|
||||
let parse_result = parser::subject(value.as_bytes());
|
||||
let value = match parse_result.is_done() {
|
||||
true => {
|
||||
parse_result.to_full_result().unwrap()
|
||||
},
|
||||
false => {
|
||||
"".to_string()
|
||||
},
|
||||
let value = if parse_result.is_done() {
|
||||
parse_result.to_full_result().unwrap()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
mail.set_to(value);
|
||||
},
|
||||
"From" => {
|
||||
let value = value.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc });
|
||||
let parse_result = parser::subject(value.as_bytes());
|
||||
let value = match parse_result.is_done() {
|
||||
true => {
|
||||
parse_result.to_full_result().unwrap()
|
||||
},
|
||||
false => {
|
||||
"".to_string()
|
||||
},
|
||||
let value = if parse_result.is_done() {
|
||||
parse_result.to_full_result().unwrap()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
mail.set_from(value);
|
||||
},
|
||||
"Subject" => {
|
||||
let value = value.iter().fold(String::new(), |mut acc, x| { acc.push_str(" "); acc.push_str(x); acc });
|
||||
let parse_result = parser::subject(value.trim().as_bytes());
|
||||
let value = match parse_result.is_done() {
|
||||
true => {
|
||||
parse_result.to_full_result().unwrap()
|
||||
},
|
||||
false => {
|
||||
"".to_string()
|
||||
},
|
||||
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.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc }));
|
||||
mail.set_message_id(value);
|
||||
},
|
||||
"References" => {
|
||||
let folded_value = value.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc });
|
||||
{
|
||||
let parse_result = parser::references(&folded_value.as_bytes());
|
||||
match parse_result.is_done() {
|
||||
true => {
|
||||
for v in parse_result.to_full_result().unwrap() {
|
||||
mail.push_references(v);
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
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(folded_value);
|
||||
mail.set_references(value.to_string());
|
||||
},
|
||||
"In-Reply-To" | "In-reply-to" | "In-Reply-to" | "in-reply-to" => {
|
||||
let value = value.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc });
|
||||
mail.set_in_reply_to(&value);
|
||||
mail.set_in_reply_to(value);
|
||||
in_reply_to = Some(value); },
|
||||
"Date" => {
|
||||
let value = value.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc });
|
||||
mail.set_date(value.clone());
|
||||
datetime = Some(value);
|
||||
mail.set_date(value.to_string());
|
||||
datetime = Some(value.to_string());
|
||||
},
|
||||
"Content-Transfer-Encoding" => {
|
||||
let value = value.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc });
|
||||
builder.content_transfer_encoding(&value);
|
||||
builder.content_transfer_encoding(value);
|
||||
},
|
||||
"Content-Type" => {
|
||||
let value = value.iter().fold(String::new(), |mut acc, x| { acc.push_str(x); acc });
|
||||
builder.content_type(&value);
|
||||
builder.content_type(value);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
};
|
||||
match in_reply_to {
|
||||
Some(ref mut x) => {
|
||||
mail.push_references(x);
|
||||
},
|
||||
None => {},
|
||||
if let Some(ref mut x) = in_reply_to {
|
||||
mail.push_references(x);
|
||||
};
|
||||
mail.set_body(builder.build());
|
||||
if datetime.is_some() {
|
||||
mail.set_datetime(parser::date(&datetime.unwrap()));
|
||||
if let Some(ref mut d) = datetime {
|
||||
mail.set_datetime(parser::date(d));
|
||||
}
|
||||
|
||||
Some(mail)
|
||||
|
|
|
@ -1,12 +1,32 @@
|
|||
//use memmap::{Mmap, Protection};
|
||||
/*
|
||||
* meli - parser 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 std;
|
||||
use std::str::from_utf8;
|
||||
use base64;
|
||||
use chrono;
|
||||
use nom::le_u8;
|
||||
use nom::{le_u8, is_hex_digit};
|
||||
|
||||
/* Wow this sucks! */
|
||||
named!(quoted_printable_byte<u8>, do_parse!(
|
||||
p: map_res!(preceded!(tag!("="), verify!(complete!(take!(2)), |s: &[u8]| { ::nom::is_hex_digit(s[0]) && ::nom::is_hex_digit(s[1]) })), std::str::from_utf8) >>
|
||||
p: map_res!(preceded!(tag!("="), verify!(complete!(take!(2)), |s: &[u8]| is_hex_digit(s[0]) && is_hex_digit(s[1]) )), from_utf8) >>
|
||||
( {
|
||||
u8::from_str_radix(p, 16).unwrap()
|
||||
} )));
|
||||
|
@ -22,51 +42,55 @@ named!(quoted_printable_byte<u8>, do_parse!(
|
|||
* Tue, 5 Jan 2016 21:30:44 +0100 (CET)
|
||||
*/
|
||||
|
||||
/*
|
||||
* if a header value is a Vec<&str>, this is the tail of that Vector
|
||||
*/
|
||||
named!(valuelist<&str>,
|
||||
map_res!(delimited!(alt_complete!(tag!("\t") | tag!(" ")), take_until!("\n"), tag!("\n")), std::str::from_utf8)
|
||||
);
|
||||
use nom::{IResult,Needed,ErrorKind};
|
||||
|
||||
/* Parse the value part of the header -> Vec<&str> */
|
||||
named!(value<Vec<&str>>,
|
||||
do_parse!(
|
||||
head: map_res!(terminated!(take_until!("\n"), tag!("\n")), std::str::from_utf8) >>
|
||||
tail: many0!(valuelist) >>
|
||||
( {
|
||||
let tail_len = tail.len();
|
||||
let tail: Vec<&str> = tail.iter().map(|v| { v.trim()}).collect();
|
||||
let mut result = Vec::with_capacity(1 + tail.len());
|
||||
result.push(head.trim());
|
||||
if tail_len == 1 && tail[0] == "" {
|
||||
result
|
||||
} else {
|
||||
tail.iter().fold(result, |mut acc, x| { acc.push(x); acc})
|
||||
fn header_value(input: &[u8]) -> IResult<&[u8], &str> {
|
||||
if input.is_empty() || input[0] == b'\n' {
|
||||
IResult::Incomplete(Needed::Size(1))
|
||||
} else {
|
||||
let input_len = input.len();
|
||||
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));
|
||||
}
|
||||
}
|
||||
} )
|
||||
));
|
||||
}
|
||||
IResult::Error(error_code!(ErrorKind::Custom(43)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Parse the name part of the header -> &str */
|
||||
named!(name<&str>,
|
||||
terminated!(verify!(map_res!(take_until1!(":"), std::str::from_utf8), | v: &str | { !v.contains("\n")} ), tag!(":")));
|
||||
named!(name<&str>,
|
||||
verify!(map_res!(take_until1!(":"), from_utf8), | v: &str | !v.contains('\n') ));
|
||||
|
||||
/* Parse a single header as a tuple -> (&str, Vec<&str>) */
|
||||
named!(header<(&str, std::vec::Vec<&str>)>,
|
||||
pair!(complete!(name), complete!(value)));
|
||||
named!(header<(&str, &str)>,
|
||||
separated_pair!(complete!(name), ws!(tag!(":")), complete!(header_value)));
|
||||
/* Parse all headers -> Vec<(&str, Vec<&str>)> */
|
||||
named!(headers<std::vec::Vec<(&str, std::vec::Vec<&str>)>>,
|
||||
named!(headers<std::vec::Vec<(&str, &str)>>,
|
||||
many1!(complete!(header)));
|
||||
|
||||
named!(pub mail<(std::vec::Vec<(&str, std::vec::Vec<&str>)>, &[u8])>,
|
||||
named!(pub mail<(std::vec::Vec<(&str, &str)>, &[u8])>,
|
||||
separated_pair!(headers, tag!("\n"), take_while!(call!(|_| { true }))));
|
||||
named!(pub attachment<(std::vec::Vec<(&str, std::vec::Vec<&str>)>, &[u8])>,
|
||||
named!(pub attachment<(std::vec::Vec<(&str, &str)>, &[u8])>,
|
||||
do_parse!(
|
||||
opt!(is_a!(" \n\t\r")) >>
|
||||
pair: pair!(many0!(complete!(header)), take_while!(call!(|_| { true }))) >>
|
||||
( { pair } )));
|
||||
|
||||
/* try chrono parse_from_str with several formats
|
||||
/* try chrono parse_from_str with several formats
|
||||
* https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.parse_from_str
|
||||
*/
|
||||
|
||||
|
@ -75,55 +99,69 @@ named!(pub attachment<(std::vec::Vec<(&str, std::vec::Vec<&str>)>, &[u8])>,
|
|||
/* Encoded words
|
||||
*"=?charset?encoding?encoded text?=".
|
||||
*/
|
||||
named!(utf8_token_base64<String>, do_parse!(
|
||||
named!(utf8_token_base64<Vec<u8>>, do_parse!(
|
||||
encoded: complete!(delimited!(tag_no_case!("=?UTF-8?B?"), take_until1!("?="), tag!("?="))) >>
|
||||
( {
|
||||
match base64::decode(encoded) {
|
||||
Ok(ref v) => { String::from_utf8_lossy(v).into_owned()
|
||||
Ok(v) => {
|
||||
v
|
||||
},
|
||||
Err(_) => {
|
||||
encoded.to_vec()
|
||||
},
|
||||
Err(_) => { String::from_utf8_lossy(encoded).into_owned() }
|
||||
}
|
||||
} )
|
||||
));
|
||||
|
||||
named!(utf8_token_quoted_p_raw<&[u8], &[u8]>,
|
||||
named!(utf8_token_quoted_p_raw<&[u8], &[u8]>,
|
||||
complete!(delimited!(tag_no_case!("=?UTF-8?q?"), take_until1!("?="), tag!("?="))));
|
||||
|
||||
//named!(utf8_token_quoted_p<String>, escaped_transform!(call!(alpha), '=', quoted_printable_byte));
|
||||
|
||||
named!(utf8_token_quoted_p<String>, do_parse!(
|
||||
named!(qp_underscore_header<u8>,
|
||||
do_parse!(tag!("_") >> ( { b' ' } )));
|
||||
|
||||
named!(utf8_token_quoted_p<Vec<u8>>, do_parse!(
|
||||
raw: call!(utf8_token_quoted_p_raw) >>
|
||||
( {
|
||||
named!(get_bytes<Vec<u8>>, dbg!(many0!(alt!(quoted_printable_byte | le_u8))));
|
||||
let bytes = get_bytes(raw).to_full_result().unwrap();
|
||||
String::from_utf8_lossy(&bytes).into_owned()
|
||||
named!(get_bytes<Vec<u8>>, many0!(alt_complete!(quoted_printable_byte | qp_underscore_header | le_u8)));
|
||||
get_bytes(raw).to_full_result().unwrap()
|
||||
} )));
|
||||
|
||||
named!(utf8_token<String>, alt_complete!(
|
||||
named!(utf8_token<Vec<u8>>, alt_complete!(
|
||||
utf8_token_base64 |
|
||||
call!(utf8_token_quoted_p)));
|
||||
|
||||
named!(utf8_token_list<String>, ws!(do_parse!(
|
||||
list: separated_nonempty_list!(complete!(tag!(" ")), utf8_token) >>
|
||||
( {
|
||||
( {
|
||||
let list_len = list.iter().fold(0, |mut acc, x| { acc+=x.len(); acc });
|
||||
list.iter().fold(String::with_capacity(list_len), |mut acc, x| { acc.push_str(x); acc})
|
||||
let bytes = list.iter().fold(Vec::with_capacity(list_len), |mut acc, x| { acc.append(&mut x.clone()); acc});
|
||||
String::from_utf8_lossy(&bytes).into_owned()
|
||||
} )
|
||||
)));
|
||||
named!(ascii_token<String>, do_parse!(
|
||||
word: alt!(terminated!(take_until1!("=?"), peek!(tag_no_case!("=?UTF-8?"))) | take_while!(call!(|_| { true }))) >>
|
||||
( {
|
||||
String::from_utf8_lossy(word).into_owned()
|
||||
|
||||
} )));
|
||||
|
||||
/* Lots of copying here. TODO: fix it */
|
||||
named!(pub subject<String>, ws!(do_parse!(
|
||||
list: many0!(alt_complete!( utf8_token_list | ascii_token)) >>
|
||||
( {
|
||||
let list_len = list.iter().fold(0, |mut acc, x| { acc+=x.len(); acc });
|
||||
let s = list.iter().fold(String::with_capacity(list_len), |mut acc, x| { acc.push_str(x); acc.push_str(" "); acc});
|
||||
s.trim().to_string()
|
||||
( {
|
||||
let string_len = list.iter().fold(0, |mut acc, x| { acc+=x.len(); acc }) + list.len() - 1;
|
||||
let list_len = list.len();
|
||||
let mut i = 0;
|
||||
list.iter().fold(String::with_capacity(string_len),
|
||||
|acc, x| {
|
||||
let mut acc = acc + &x.replace("\n", "");
|
||||
if i != list_len - 1 {
|
||||
acc.push_str(" ");
|
||||
i+=1;
|
||||
}
|
||||
acc
|
||||
})
|
||||
} )
|
||||
|
||||
)));
|
||||
|
@ -159,27 +197,33 @@ fn test_eat_comments() {
|
|||
let s = "Thu, 31 Aug 2017 13:43:37 +0000 (UTC)";
|
||||
assert_eq!(eat_comments(s), "Thu, 31 Aug 2017 13:43:37 +0000 ");
|
||||
}
|
||||
/* Date should tokenize input and convert the tokens, right now we expect input will have no extra
|
||||
* spaces in between tokens */
|
||||
/*
|
||||
* Date should tokenize input and convert the tokens,
|
||||
* right now we expect input will have no extra spaces in between tokens
|
||||
*
|
||||
* We should use a custom parser here*/
|
||||
pub fn date(input: &str) -> Option<chrono::DateTime<chrono::FixedOffset>> {
|
||||
chrono::DateTime::parse_from_rfc2822(eat_comments(input).trim()).ok()
|
||||
let parsed_result = subject(eat_comments(input).as_bytes()).to_full_result().unwrap().replace("-", "+");
|
||||
chrono::DateTime::parse_from_rfc2822(parsed_result.trim()).ok()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date() {
|
||||
let s = "Thu, 31 Aug 2017 13:43:37 +0000 (UTC)";
|
||||
let _s = "Thu, 31 Aug 2017 13:43:37 +0000";
|
||||
let __s = "=?utf-8?q?Thu=2C_31_Aug_2017_13=3A43=3A37_-0000?=";
|
||||
assert_eq!(date(s).unwrap(), date(_s).unwrap());
|
||||
assert_eq!(date(_s).unwrap(), date(__s).unwrap());
|
||||
}
|
||||
|
||||
named!(pub message_id<&str>,
|
||||
map_res!(complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">"))), std::str::from_utf8)
|
||||
map_res!(complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">"))), from_utf8)
|
||||
);
|
||||
|
||||
named!(pub references<Vec<&str>>, many0!(preceded!(is_not!("<"), message_id)));
|
||||
|
||||
named_args!(pub attachments<'a>(boundary: &'a str, boundary_end: &'a str) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
|
||||
dbg!(alt_complete!(do_parse!(
|
||||
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!(
|
||||
take_until!(boundary) >>
|
||||
vecs: many0!(complete!(do_parse!(
|
||||
tag!(boundary) >>
|
||||
|
@ -196,33 +240,41 @@ named_args!(pub attachments<'a>(boundary: &'a str, boundary_end: &'a str) < Vec<
|
|||
take_until!(boundary_end) >>
|
||||
tag!(boundary_end) >>
|
||||
( { Vec::<&[u8]>::new() } ))
|
||||
)));
|
||||
));
|
||||
#[test]
|
||||
fn test_attachments() {
|
||||
use std::io::Read;
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
let _ = std::fs::File::open("test/attachment_test").unwrap().read_to_end(&mut buffer);
|
||||
let boundary = "--b1_4382d284f0c601a737bb32aaeda53160";
|
||||
let boundary = "--b1_4382d284f0c601a737bb32aaeda53160--";
|
||||
let boundary_len = boundary.len();
|
||||
let (_, body) = match mail(&buffer).to_full_result() {
|
||||
Ok(v) => v,
|
||||
Err(_) => { panic!() }
|
||||
};
|
||||
//eprintln!("{:?}",std::str::from_utf8(body));
|
||||
let attachments = attachments(body, boundary).to_full_result().unwrap();
|
||||
let attachments = attachments(body, &boundary[0..boundary_len-2], &boundary).to_full_result().unwrap();
|
||||
assert_eq!(attachments.len(), 4);
|
||||
}
|
||||
|
||||
|
||||
named!(pub content_type< (&str, &str, Vec<(&str, &str)>) >,
|
||||
named!(content_type_parameter< (&str, &str) >,
|
||||
do_parse!(
|
||||
_type: map_res!(take_until!("/"), std::str::from_utf8) >>
|
||||
tag!(";") >>
|
||||
name: terminated!(map_res!(ws!(take_until!("=")), from_utf8), tag!("=")) >>
|
||||
value: map_res!(ws!(
|
||||
alt_complete!(delimited!(tag!("\""), take_until!("\""), tag!("\"")) | is_not!(";"))),
|
||||
from_utf8) >>
|
||||
( {
|
||||
(name, value)
|
||||
} )
|
||||
));
|
||||
|
||||
|
||||
named!(pub content_type< (&str, &str, Vec<(&str, &str)>) >,
|
||||
do_parse!(
|
||||
_type: map_res!(take_until!("/"), from_utf8) >>
|
||||
tag!("/") >>
|
||||
_subtype: map_res!(is_not!(";"), std::str::from_utf8) >>
|
||||
parameters: many0!(preceded!(tag!(";"), pair!(
|
||||
terminated!(map_res!(ws!(take_until!("=")), std::str::from_utf8), tag!("=")),
|
||||
map_res!(ws!(alt_complete!(
|
||||
delimited!(tag!("\""), take_until!("\""), tag!("\"")) | is_not!(";")
|
||||
)), std::str::from_utf8)))) >>
|
||||
_subtype: map_res!(is_not!(";"), from_utf8) >>
|
||||
parameters: many0!(complete!(content_type_parameter)) >>
|
||||
( {
|
||||
(_type, _subtype, parameters)
|
||||
} )
|
||||
|
|
|
@ -19,32 +19,25 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//use std::cmp::Ordering;
|
||||
//use std::fmt;
|
||||
pub mod email;
|
||||
pub use self::email::*;
|
||||
/* Mail backends. Currently only maildir is supported */
|
||||
pub mod backends;
|
||||
use mailbox::backends::MailBackend;
|
||||
use mailbox::backends::maildir;
|
||||
use error::Result;
|
||||
|
||||
use std::option::Option;
|
||||
use std::collections::HashMap;
|
||||
use std;
|
||||
pub mod email;
|
||||
pub use self::email::*;
|
||||
|
||||
/* Mail backends. Currently only maildir is supported */
|
||||
mod backends;
|
||||
use mailbox::backends::MailBackend;
|
||||
|
||||
use mailbox::backends::maildir;
|
||||
|
||||
use error::Result;
|
||||
|
||||
|
||||
|
||||
type UnixTimestamp = i64;
|
||||
|
||||
|
||||
/*a Mailbox represents a folder of mail. Currently only Maildir is supported.*/
|
||||
#[derive(Debug)]
|
||||
pub struct Mailbox{
|
||||
pub path: String,
|
||||
pub collection: Box<Vec<Mail>>,
|
||||
pub collection: Vec<Mail>,
|
||||
pub threaded_collection: Vec<usize>,
|
||||
threads: Vec<Thread>,
|
||||
length: usize,
|
||||
|
@ -102,27 +95,22 @@ impl Thread {
|
|||
pub fn get_indentation(&self) -> usize {
|
||||
self.indentation
|
||||
}
|
||||
fn is_descendant(&self, threads: &Vec<Thread>, other: &Thread) -> bool {
|
||||
fn is_descendant(&self, threads: &[Thread], other: &Thread) -> bool {
|
||||
if self == other {
|
||||
return true;
|
||||
}
|
||||
match self.first_child {
|
||||
Some(v) => {
|
||||
if threads[v].is_descendant(threads, other) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
|
||||
if let Some(v) = self.first_child {
|
||||
if threads[v].is_descendant(threads, other) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
match self.next_sibling {
|
||||
Some(v) => {
|
||||
if threads[v].is_descendant(threads, other) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
if let Some(v) = self.next_sibling {
|
||||
if threads[v].is_descendant(threads, other) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return false;
|
||||
false
|
||||
}
|
||||
fn set_show_subject(&mut self, set: bool) -> () {
|
||||
self.show_subject = set;
|
||||
|
@ -145,12 +133,12 @@ impl PartialEq for Thread {
|
|||
}
|
||||
}
|
||||
|
||||
fn build_collection(threads: &mut Vec<Thread>, id_table: &mut HashMap<std::string::String, usize>, collection: &mut Box<Vec<Mail>>) -> () {
|
||||
fn build_collection(threads: &mut Vec<Thread>, id_table: &mut HashMap<std::string::String, usize>, collection: &mut [Mail]) -> () {
|
||||
for (i, x) in collection.iter_mut().enumerate() {
|
||||
let x_index; /* x's index in threads */
|
||||
let m_id = x.get_message_id_raw().to_string();
|
||||
if id_table.contains_key(&m_id) {
|
||||
let t = *(id_table.get(&m_id).unwrap());
|
||||
let t = id_table[&m_id];
|
||||
/* the already existing Thread should be empty, since we're
|
||||
* seeing this message for the first time */
|
||||
if threads[t].message.is_some() {
|
||||
|
@ -190,12 +178,12 @@ fn build_collection(threads: &mut Vec<Thread>, id_table: &mut HashMap<std::strin
|
|||
* Do not add a link if adding that link would introduce a loop: that is, before asserting A->B, search down the children of B to see if A is reachable, and also search down the children of A to see if B is reachable. If either is already reachable as a child of the other, don't add the link.
|
||||
*/
|
||||
let mut curr_ref = x_index;
|
||||
'ref_loop: for &r in x.get_references().iter().rev() {
|
||||
for &r in x.get_references().iter().rev() {
|
||||
let parent_id =
|
||||
if id_table.contains_key(r.get_raw()) {
|
||||
let p = *(id_table.get(r.get_raw()).unwrap());
|
||||
if !(threads[p].is_descendant(&threads, &threads[curr_ref]) ||
|
||||
threads[curr_ref].is_descendant(&threads, &threads[p])) {
|
||||
let p = id_table[r.get_raw()];
|
||||
if !(threads[p].is_descendant(threads, &threads[curr_ref]) ||
|
||||
threads[curr_ref].is_descendant(threads, &threads[p])) {
|
||||
threads[curr_ref].parent = Some(p);
|
||||
if threads[p].first_child.is_none() {
|
||||
threads[p].first_child = Some(curr_ref);
|
||||
|
@ -229,7 +217,7 @@ fn build_collection(threads: &mut Vec<Thread>, id_table: &mut HashMap<std::strin
|
|||
/* update thread date */
|
||||
let mut parent_iter = parent_id;
|
||||
'date: loop {
|
||||
let mut p = &mut threads[parent_iter];
|
||||
let p = &mut threads[parent_iter];
|
||||
if p.date < x.get_date() {
|
||||
p.date = x.get_date();
|
||||
}
|
||||
|
@ -248,7 +236,7 @@ fn build_collection(threads: &mut Vec<Thread>, id_table: &mut HashMap<std::strin
|
|||
|
||||
impl Mailbox {
|
||||
pub fn new(path: &str, sent_folder: Option<&str>) -> Result<Mailbox> {
|
||||
let mut collection: Box<Vec<Mail>> = Box::new(maildir::MaildirType::new(path).get()?);
|
||||
let mut collection: Vec<Mail> = maildir::MaildirType::new(path).get()?;
|
||||
/* To reconstruct thread information from the mails we need: */
|
||||
|
||||
/* a vector to hold thread members */
|
||||
|
@ -278,7 +266,7 @@ impl Mailbox {
|
|||
idx += 1;
|
||||
}
|
||||
if id_table.contains_key(x.get_message_id_raw()) {
|
||||
let c = *(id_table.get(x.get_message_id_raw()).unwrap());
|
||||
let c = id_table[x.get_message_id_raw()];
|
||||
if threads[c].message.is_some() {
|
||||
/* skip duplicate message-id, but this should be handled instead */
|
||||
continue;
|
||||
|
@ -289,7 +277,7 @@ impl Mailbox {
|
|||
x.set_thread(c);
|
||||
}
|
||||
if !x.get_in_reply_to_raw().is_empty() && id_table.contains_key(x.get_in_reply_to_raw()) {
|
||||
let p = *(id_table.get(x.get_in_reply_to_raw()).unwrap());
|
||||
let p = id_table[x.get_in_reply_to_raw()];
|
||||
let c = if !id_table.contains_key(x.get_message_id_raw()) {
|
||||
threads.push(
|
||||
Thread {
|
||||
|
@ -307,7 +295,7 @@ impl Mailbox {
|
|||
tidx += 1;
|
||||
tidx - 1
|
||||
} else {
|
||||
*(id_table.get(x.get_message_id_raw()).unwrap())
|
||||
id_table[x.get_message_id_raw()]
|
||||
};
|
||||
threads[c].parent = Some(p);
|
||||
if threads[p].is_descendant(&threads, &threads[c]) ||
|
||||
|
@ -328,7 +316,7 @@ impl Mailbox {
|
|||
/* update thread date */
|
||||
let mut parent_iter = p;
|
||||
'date: loop {
|
||||
let mut p = &mut threads[parent_iter];
|
||||
let p = &mut threads[parent_iter];
|
||||
if p.date < x.get_date() {
|
||||
p.date = x.get_date();
|
||||
}
|
||||
|
@ -343,7 +331,7 @@ impl Mailbox {
|
|||
/* 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 */
|
||||
let mut root_set = Vec::with_capacity(collection.len());
|
||||
'root_set: for (_,v) in id_table.iter() {
|
||||
'root_set: for v in id_table.values() {
|
||||
if threads[*v].parent.is_none() {
|
||||
if !threads[*v].has_message() && threads[*v].has_children() && !threads[threads[*v].first_child.unwrap()].has_sibling() {
|
||||
/* Do not promote the children if doing so would promote them to the root set
|
||||
|
@ -352,13 +340,13 @@ impl Mailbox {
|
|||
continue 'root_set;
|
||||
}
|
||||
root_set.push(*v);
|
||||
}
|
||||
}
|
||||
}
|
||||
root_set.sort_by(|a, b| threads[*b].date.cmp(&threads[*a].date));
|
||||
|
||||
/* 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());
|
||||
fn build_threaded(threads: &mut Vec<Thread>, indentation: usize, threaded: &mut Vec<usize>, i: usize, root_subject_idx: usize, collection: &Vec<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];
|
||||
if threads[root_subject_idx].has_message() {
|
||||
let root_subject = collection[threads[root_subject_idx].get_message().unwrap()].get_subject();
|
||||
|
@ -375,7 +363,7 @@ impl Mailbox {
|
|||
if thread.has_parent() && !threads[thread.get_parent().unwrap()].has_message() {
|
||||
threads[i].parent = None;
|
||||
}
|
||||
let indentation =
|
||||
let indentation =
|
||||
if thread.has_message() {
|
||||
threads[i].set_indentation(indentation);
|
||||
if !threaded.contains(&i) {
|
||||
|
@ -421,8 +409,7 @@ impl Mailbox {
|
|||
thread.get_message().unwrap()
|
||||
}
|
||||
pub fn get_mail_and_thread(&mut self, i: usize) -> (&mut Mail, Thread) {
|
||||
|
||||
let ref mut x = self.collection.as_mut_slice()[i];
|
||||
let x = &mut self.collection.as_mut_slice()[i];
|
||||
let thread = self.threads[x.get_thread()];
|
||||
(x, thread)
|
||||
}
|
||||
|
|
127
src/ui/index.rs
127
src/ui/index.rs
|
@ -2,7 +2,7 @@
|
|||
* meli - ui module.
|
||||
*
|
||||
* Copyright 2017 Manos Pitsidianakis
|
||||
*
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
|
@ -18,17 +18,16 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
extern crate ncurses;
|
||||
extern crate chrono;
|
||||
use mailbox::email::Mail;
|
||||
use mailbox::*;
|
||||
use error::MeliError;
|
||||
use std::error::Error;
|
||||
|
||||
//use self::chrono::NaiveDateTime;
|
||||
extern crate ncurses;
|
||||
use std::error::Error;
|
||||
|
||||
pub trait Window {
|
||||
fn draw(&mut self) -> ();
|
||||
fn redraw(&mut self) -> ();
|
||||
fn handle_input(&mut self, input: i32) -> ();
|
||||
}
|
||||
|
||||
|
@ -43,6 +42,10 @@ impl Window for ErrorWindow {
|
|||
ncurses::waddstr(self.win, &self.description);
|
||||
ncurses::wrefresh(self.win);
|
||||
}
|
||||
fn redraw(&mut self) -> () {
|
||||
ncurses::waddstr(self.win, &self.description);
|
||||
ncurses::wrefresh(self.win);
|
||||
}
|
||||
fn handle_input(&mut self, _: i32) -> () {
|
||||
|
||||
|
||||
|
@ -98,43 +101,38 @@ impl Window for Index {
|
|||
/* Draw threaded view. */
|
||||
let mut iter = self.mailbox.threaded_collection.iter().enumerate().peekable();
|
||||
/* This is just a desugared for loop so that we can use .peek() */
|
||||
loop {
|
||||
match iter.next() {
|
||||
Some((idx, i)) => {
|
||||
let container = self.mailbox.get_thread(*i);
|
||||
let indentation = container.get_indentation();
|
||||
while let Some((idx, i)) = iter.next() {
|
||||
let container = self.mailbox.get_thread(*i);
|
||||
let indentation = container.get_indentation();
|
||||
|
||||
assert_eq!(container.has_message(), true);
|
||||
match iter.peek() {
|
||||
Some(&(_, x)) if self.mailbox.get_thread(*x).get_indentation() == indentation => {
|
||||
indentations.pop();
|
||||
indentations.push(true);
|
||||
},
|
||||
_ => {
|
||||
indentations.pop();
|
||||
indentations.push(false);
|
||||
}
|
||||
}
|
||||
if container.has_sibling() {
|
||||
assert_eq!(container.has_message(), true);
|
||||
match iter.peek() {
|
||||
Some(&(_, x)) if self.mailbox.get_thread(*x).get_indentation() == indentation => {
|
||||
indentations.pop();
|
||||
indentations.push(true);
|
||||
},
|
||||
_ => {
|
||||
indentations.pop();
|
||||
indentations.push(false);
|
||||
}
|
||||
}
|
||||
if container.has_sibling() {
|
||||
indentations.pop();
|
||||
indentations.push(true);
|
||||
}
|
||||
let x = &self.mailbox.collection[container.get_message().unwrap()];
|
||||
Index::draw_entry(self.pad, x, idx, indentation, container.has_sibling(), container.has_parent(), idx == self.cursor_idx, container.get_show_subject(), Some(&indentations));
|
||||
match iter.peek() {
|
||||
Some(&(_, x)) if self.mailbox.get_thread(*x).get_indentation() > indentation => {
|
||||
indentations.push(false);
|
||||
},
|
||||
Some(&(_, x)) if self.mailbox.get_thread(*x).get_indentation() < indentation => {
|
||||
for _ in 0..(indentation - self.mailbox.get_thread(*x).get_indentation()) {
|
||||
indentations.pop();
|
||||
indentations.push(true);
|
||||
}
|
||||
let x = &self.mailbox.collection[container.get_message().unwrap()];
|
||||
Index::draw_entry(self.pad, x, idx, indentation, container.has_sibling(), container.has_parent(), idx == self.cursor_idx, container.get_show_subject(), Some(&indentations));
|
||||
match iter.peek() {
|
||||
Some(&(_, x)) if self.mailbox.get_thread(*x).get_indentation() > indentation => {
|
||||
indentations.push(false);
|
||||
},
|
||||
Some(&(_, x)) if self.mailbox.get_thread(*x).get_indentation() < indentation => {
|
||||
for _ in 0..(indentation - self.mailbox.get_thread(*x).get_indentation()) {
|
||||
indentations.pop();
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
}
|
||||
}
|
||||
},
|
||||
None => break,
|
||||
_ => {
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
@ -173,7 +171,27 @@ impl Window for Index {
|
|||
self.screen_width - 1,
|
||||
);
|
||||
}
|
||||
|
||||
fn redraw(&mut self) -> () {
|
||||
if self.mailbox.get_length() == 0 {
|
||||
return;
|
||||
}
|
||||
ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width);
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
ncurses::getbegyx(self.win, &mut y, &mut x);
|
||||
let pminrow =
|
||||
(self.cursor_idx as i32).wrapping_div(self.screen_height) * self.screen_height;
|
||||
ncurses::touchline(self.pad, 1, 1); ncurses::prefresh(
|
||||
self.pad,
|
||||
pminrow,
|
||||
0,
|
||||
y,
|
||||
x,
|
||||
self.screen_height - 1,
|
||||
self.screen_width - 1,
|
||||
);
|
||||
ncurses::wrefresh(self.win);
|
||||
}
|
||||
fn handle_input(&mut self, motion: i32) {
|
||||
if self.mailbox.get_length() == 0 {
|
||||
return;
|
||||
|
@ -203,7 +221,7 @@ impl Window for Index {
|
|||
},
|
||||
10 => {
|
||||
self.show_pager();
|
||||
self.draw();
|
||||
self.redraw();
|
||||
},
|
||||
_ => {
|
||||
return;
|
||||
|
@ -212,18 +230,17 @@ impl Window for Index {
|
|||
|
||||
/* Draw newly highlighted entry */
|
||||
ncurses::wmove(self.pad, self.cursor_idx as i32, 0);
|
||||
/* Borrow x from self.mailbox in separate scopes or else borrow checker complains */
|
||||
{
|
||||
let pair = super::COLOR_PAIR_CURSOR;
|
||||
ncurses::wchgat(self.pad, -1, 0, pair);
|
||||
}
|
||||
let pair = super::COLOR_PAIR_CURSOR;
|
||||
ncurses::wchgat(self.pad, -1, 0, pair);
|
||||
/* Draw previous highlighted entry normally */
|
||||
ncurses::wmove(self.pad, prev_idx as i32, 0);
|
||||
{
|
||||
let pair = match self.threaded {
|
||||
true if prev_idx % 2 == 0 => super::COLOR_PAIR_THREAD_EVEN,
|
||||
true => super::COLOR_PAIR_THREAD_ODD,
|
||||
false => super::COLOR_PAIR_DEFAULT,
|
||||
let pair = if self.threaded && prev_idx % 2 == 0 {
|
||||
super::COLOR_PAIR_THREAD_EVEN
|
||||
} else if self.threaded {
|
||||
super::COLOR_PAIR_THREAD_ODD
|
||||
} else {
|
||||
super::COLOR_PAIR_DEFAULT
|
||||
};
|
||||
ncurses::wchgat(self.pad, 32, 0, pair);
|
||||
ncurses::wmove(self.pad, prev_idx as i32, 32);
|
||||
|
@ -299,7 +316,7 @@ impl Index {
|
|||
ncurses::getmaxyx(win, &mut screen_height, &mut screen_width);
|
||||
//eprintln!("length is {}\n", length);
|
||||
let mailbox_length = mailbox.get_length();
|
||||
let pad = ncurses::newpad(mailbox_length as i32, screen_width);
|
||||
let pad = ncurses::newpad(mailbox_length as i32, 1500);
|
||||
ncurses::wbkgd(
|
||||
pad,
|
||||
' ' as ncurses::chtype |
|
||||
|
@ -403,14 +420,12 @@ impl Index {
|
|||
}
|
||||
ncurses::getmaxyx(self.win,
|
||||
&mut self.screen_height, &mut self.screen_width);
|
||||
let x: &mut Mail;
|
||||
|
||||
if self.threaded {
|
||||
let x: &mut Mail = if self.threaded {
|
||||
let i = self.mailbox.get_threaded_mail(self.cursor_idx);
|
||||
x = &mut self.mailbox.collection[i];
|
||||
&mut self.mailbox.collection[i]
|
||||
} else {
|
||||
x = &mut self.mailbox.collection[self.cursor_idx];
|
||||
}
|
||||
&mut self.mailbox.collection[self.cursor_idx]
|
||||
};
|
||||
let mut pager = super::pager::Pager::new(self.win, x);
|
||||
pager.scroll(ncurses::KEY_DOWN);
|
||||
pager.scroll(ncurses::KEY_UP);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* meli - ui module.
|
||||
*
|
||||
* Copyright 2017 Manos Pitsidianakis
|
||||
*
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
|
@ -18,9 +18,12 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
extern crate ncurses;
|
||||
|
||||
pub mod index;
|
||||
pub mod pager;
|
||||
|
||||
extern crate ncurses;
|
||||
|
||||
/* Color pairs; foreground && background. */
|
||||
pub static COLOR_PAIR_DEFAULT: i16 = 1;
|
||||
pub static COLOR_PAIR_CURSOR: i16 = 2;
|
||||
|
@ -32,29 +35,29 @@ pub static COLOR_PAIR_THREAD_EVEN: i16 = 6;
|
|||
pub struct TUI;
|
||||
|
||||
impl TUI {
|
||||
pub fn initialize() -> Self {
|
||||
/* start ncurses */
|
||||
ncurses::initscr();
|
||||
ncurses::keypad(ncurses::stdscr(), true);
|
||||
ncurses::noecho();
|
||||
ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
|
||||
/* Start colors. */
|
||||
pub fn initialize() -> Self {
|
||||
/* start ncurses */
|
||||
ncurses::initscr();
|
||||
ncurses::keypad(ncurses::stdscr(), true);
|
||||
ncurses::noecho();
|
||||
ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
|
||||
/* Start colors. */
|
||||
|
||||
ncurses::start_color();
|
||||
ncurses::start_color();
|
||||
|
||||
ncurses::init_pair(COLOR_PAIR_DEFAULT, 15, 0);
|
||||
ncurses::init_pair(COLOR_PAIR_CURSOR, 251, 235);
|
||||
ncurses::init_pair(COLOR_PAIR_HEADERS, 33, 0);
|
||||
ncurses::init_pair(COLOR_PAIR_THREAD_INDENT, 5, 0);
|
||||
ncurses::init_pair(COLOR_PAIR_THREAD_ODD, 15, 0);
|
||||
ncurses::init_pair(COLOR_PAIR_THREAD_EVEN, 15, 233);
|
||||
ncurses::init_pair(COLOR_PAIR_DEFAULT, 15, 0);
|
||||
ncurses::init_pair(COLOR_PAIR_CURSOR, 251, 235);
|
||||
ncurses::init_pair(COLOR_PAIR_HEADERS, 33, 0);
|
||||
ncurses::init_pair(COLOR_PAIR_THREAD_INDENT, 5, 0);
|
||||
ncurses::init_pair(COLOR_PAIR_THREAD_ODD, 15, 0);
|
||||
ncurses::init_pair(COLOR_PAIR_THREAD_EVEN, 15, 233);
|
||||
|
||||
/* Set the window's background color. */
|
||||
ncurses::bkgd(
|
||||
' ' as ncurses::chtype | ncurses::COLOR_PAIR(COLOR_PAIR_DEFAULT) as ncurses::chtype,
|
||||
);
|
||||
TUI {}
|
||||
}
|
||||
/* Set the window's background color. */
|
||||
ncurses::bkgd(
|
||||
' ' as ncurses::chtype | ncurses::COLOR_PAIR(COLOR_PAIR_DEFAULT) as ncurses::chtype,
|
||||
);
|
||||
TUI {}
|
||||
}
|
||||
}
|
||||
impl Drop for TUI {
|
||||
fn drop(&mut self) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* meli - ui module.
|
||||
*
|
||||
* Copyright 2017 Manos Pitsidianakis
|
||||
*
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
|
@ -18,9 +18,10 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
extern crate ncurses;
|
||||
|
||||
use super::super::mailbox;
|
||||
use mailbox;
|
||||
|
||||
extern crate ncurses;
|
||||
|
||||
/* Pager represents the part of the UI that shows the mail headers and body for
|
||||
* viewing */
|
||||
|
@ -148,29 +149,29 @@ impl Pager {
|
|||
ncurses::waddstr(win, "From: ");
|
||||
ncurses::waddstr(
|
||||
win,
|
||||
&mail.get_from(),
|
||||
mail.get_from(),
|
||||
);
|
||||
ncurses::waddstr(win, "\n");
|
||||
i += 1;
|
||||
ncurses::waddstr(win, "To: ");
|
||||
ncurses::waddstr(
|
||||
win,
|
||||
&mail.get_to(),
|
||||
mail.get_to(),
|
||||
);
|
||||
ncurses::waddstr(win, "\n");
|
||||
i += 1;
|
||||
ncurses::waddstr(win, "Subject: ");
|
||||
ncurses::waddstr(
|
||||
win,
|
||||
&mail.get_subject(),
|
||||
mail.get_subject(),
|
||||
);
|
||||
ncurses::waddstr(win, "\n");
|
||||
i += 1;
|
||||
ncurses::waddstr(win, "Message-ID: ");
|
||||
ncurses::waddstr(
|
||||
win,
|
||||
&mail.get_message_id_raw(),
|
||||
//&mail.get_message_id(),
|
||||
mail.get_message_id_raw(),
|
||||
//mail.get_message_id(),
|
||||
);
|
||||
ncurses::waddstr(win, "\n");
|
||||
i += 1;
|
||||
|
@ -184,7 +185,7 @@ impl Pager {
|
|||
ncurses::waddstr(win, "In-Reply-To: ");
|
||||
ncurses::waddstr(
|
||||
win,
|
||||
&mail.get_in_reply_to_raw(),
|
||||
mail.get_in_reply_to_raw(),
|
||||
);
|
||||
ncurses::waddstr(win, "\n");
|
||||
i += 1;
|
||||
|
@ -204,7 +205,7 @@ impl Pager {
|
|||
let mut y = 0;
|
||||
/* y,x coordinates of upper left corner of win */
|
||||
ncurses::getparyx(win, &mut y, &mut x);
|
||||
|
||||
|
||||
let text = mail.get_body().get_text();
|
||||
let lines: Vec<&str> = text.trim().split('\n').collect();
|
||||
let lines_length = lines.len();
|
||||
|
@ -226,7 +227,7 @@ impl Pager {
|
|||
* β ββββββββββw β β
|
||||
*/
|
||||
ncurses::pnoutrefresh(pad, 0, 0, y + height, x, y + height - 1, w - 1);
|
||||
return (pad, lines_length as i32, height);
|
||||
(pad, lines_length as i32, height)
|
||||
}
|
||||
fn print_entry(
|
||||
win: ncurses::WINDOW,
|
||||
|
|
Loadingβ¦
Reference in New Issue