Merge pull request #657 from matthiasbeyer/libimagmail/init
Libimagmail/init
This commit is contained in:
commit
1d3666eef4
8 changed files with 283 additions and 1 deletions
21
libimagmail/Cargo.toml
Normal file
21
libimagmail/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "libimagmail"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.3"
|
||||||
|
mailparse = "0.2.0"
|
||||||
|
semver = "0.2"
|
||||||
|
toml = "0.2.*"
|
||||||
|
filters = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies.libimagstore]
|
||||||
|
path = "../libimagstore"
|
||||||
|
|
||||||
|
[dependencies.libimagerror]
|
||||||
|
path = "../libimagerror"
|
||||||
|
|
||||||
|
[dependencies.libimagref]
|
||||||
|
path = "../libimagref"
|
||||||
|
|
16
libimagmail/src/error.rs
Normal file
16
libimagmail/src/error.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
generate_error_module!(
|
||||||
|
generate_error_types!(MailError, MailErrorKind,
|
||||||
|
RefCreationError => "Error creating a reference to a file/directory",
|
||||||
|
RefHandlingError => "Error while handling the internal reference object",
|
||||||
|
MailParsingError => "Error while parsing mail",
|
||||||
|
|
||||||
|
FetchByHashError => "Error fetching mail from Store by hash",
|
||||||
|
FetchError => "Error fetching mail from Store",
|
||||||
|
IOError => "IO Error"
|
||||||
|
);
|
||||||
|
);
|
||||||
|
|
||||||
|
pub use self::error::MailError;
|
||||||
|
pub use self::error::MailErrorKind;
|
||||||
|
pub use self::error::MapErrInto;
|
||||||
|
|
64
libimagmail/src/hasher.rs
Normal file
64
libimagmail/src/hasher.rs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use mailparse::{MailHeader, parse_mail};
|
||||||
|
|
||||||
|
use libimagref::hasher::Hasher;
|
||||||
|
use libimagref::hasher::DefaultHasher;
|
||||||
|
use libimagref::error::RefErrorKind as REK;
|
||||||
|
use libimagref::error::MapErrInto;
|
||||||
|
use libimagref::result::Result as RResult;
|
||||||
|
use libimagerror::into::IntoError;
|
||||||
|
|
||||||
|
use error::MailErrorKind as MEK;
|
||||||
|
|
||||||
|
pub struct MailHasher {
|
||||||
|
defaulthasher: DefaultHasher,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MailHasher {
|
||||||
|
|
||||||
|
pub fn new() -> MailHasher {
|
||||||
|
MailHasher { defaulthasher: DefaultHasher::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hasher for MailHasher {
|
||||||
|
|
||||||
|
fn hash_name(&self) -> &'static str {
|
||||||
|
"default_mail_hasher"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_hash<R: Read>(&mut self, pb: &PathBuf, c: &mut R) -> RResult<String> {
|
||||||
|
use filters::filter::Filter;
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
try!(c.read_to_string(&mut s).map_err_into(REK::UTF8Error).map_err_into(REK::IOError));
|
||||||
|
|
||||||
|
parse_mail(&s.as_bytes())
|
||||||
|
.map_err(Box::new)
|
||||||
|
.map_err(|e| MEK::MailParsingError.into_error_with_cause(e))
|
||||||
|
.map_err_into(REK::RefHashingError)
|
||||||
|
.and_then(|mail| {
|
||||||
|
let has_key = |hdr: &MailHeader, exp: &str|
|
||||||
|
hdr.get_key().map(|s| s == exp).unwrap_or(false);
|
||||||
|
|
||||||
|
let subject_filter = |hdr: &MailHeader| has_key(hdr, "Subject");
|
||||||
|
let from_filter = |hdr: &MailHeader| has_key(hdr, "From");
|
||||||
|
let to_filter = |hdr: &MailHeader| has_key(hdr, "To");
|
||||||
|
|
||||||
|
let filter = subject_filter.or(from_filter).or(to_filter);
|
||||||
|
|
||||||
|
let s : String = mail.headers
|
||||||
|
.iter()
|
||||||
|
.filter(|item| filter.filter(item))
|
||||||
|
.filter_map(|hdr| hdr.get_value().ok()) // TODO: Do not hide error here
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
self.defaulthasher.create_hash(pb, &mut s.as_bytes())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
libimagmail/src/iter.rs
Normal file
37
libimagmail/src/iter.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
//! Module for the MailIter
|
||||||
|
//!
|
||||||
|
//! MailIter is a iterator which takes an Iterator that yields `Ref` and yields itself
|
||||||
|
//! `Result<Mail>`, where `Err(_)` is returned if the Ref is not a Mail or parsing of the
|
||||||
|
//! referenced mail file failed.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use mail::Mail;
|
||||||
|
use result::Result;
|
||||||
|
|
||||||
|
use libimagref::reference::Ref;
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
struct MailIter<'a, I: 'a + Iterator<Item = Ref<'a>>> {
|
||||||
|
_marker: PhantomData<&'a I>,
|
||||||
|
i: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I: Iterator<Item = Ref<'a>>> MailIter<'a, I> {
|
||||||
|
|
||||||
|
pub fn new(i: I) -> MailIter<'a, I> {
|
||||||
|
MailIter { _marker: PhantomData, i: i }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I: Iterator<Item = Ref<'a>>> Iterator for MailIter<'a, I> {
|
||||||
|
|
||||||
|
type Item = Result<Mail<'a>>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Result<Mail<'a>>> {
|
||||||
|
self.i.next().map(Mail::from_ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
16
libimagmail/src/lib.rs
Normal file
16
libimagmail/src/lib.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
extern crate mailparse;
|
||||||
|
extern crate semver;
|
||||||
|
extern crate toml;
|
||||||
|
extern crate filters;
|
||||||
|
|
||||||
|
#[macro_use] extern crate libimagerror;
|
||||||
|
extern crate libimagstore;
|
||||||
|
extern crate libimagref;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod hasher;
|
||||||
|
pub mod iter;
|
||||||
|
pub mod mail;
|
||||||
|
pub mod result;
|
||||||
|
|
120
libimagmail/src/mail.rs
Normal file
120
libimagmail/src/mail.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
use std::result::Result as RResult;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use libimagstore::store::{FileLockEntry, Store};
|
||||||
|
use libimagref::reference::Ref;
|
||||||
|
use libimagref::flags::RefFlags;
|
||||||
|
|
||||||
|
use mailparse::{MailParseError, ParsedMail, parse_mail};
|
||||||
|
|
||||||
|
use hasher::MailHasher;
|
||||||
|
use result::Result;
|
||||||
|
use error::{MapErrInto, MailErrorKind as MEK};
|
||||||
|
|
||||||
|
struct Buffer(String);
|
||||||
|
|
||||||
|
impl Buffer {
|
||||||
|
pub fn parsed<'a>(&'a self) -> RResult<ParsedMail<'a>, MailParseError> {
|
||||||
|
parse_mail(self.0.as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Buffer {
|
||||||
|
fn from(data: String) -> Buffer {
|
||||||
|
Buffer(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mail<'a>(Ref<'a>, Buffer);
|
||||||
|
|
||||||
|
impl<'a> Mail<'a> {
|
||||||
|
|
||||||
|
/// Imports a mail from the Path passed
|
||||||
|
pub fn import_from_path<P: AsRef<Path>>(store: &Store, p: P) -> Result<Mail> {
|
||||||
|
let h = MailHasher::new();
|
||||||
|
let f = RefFlags::default().with_content_hashing(true).with_permission_tracking(false);
|
||||||
|
let p = PathBuf::from(p.as_ref());
|
||||||
|
|
||||||
|
Ref::create_with_hasher(store, p, f, h)
|
||||||
|
.map_err_into(MEK::RefCreationError)
|
||||||
|
.and_then(|reference| {
|
||||||
|
reference.fs_file()
|
||||||
|
.map_err_into(MEK::RefHandlingError)
|
||||||
|
.and_then(|path| File::open(path).map_err_into(MEK::IOError))
|
||||||
|
.and_then(|mut file| {
|
||||||
|
let mut s = String::new();
|
||||||
|
file.read_to_string(&mut s)
|
||||||
|
.map(|_| s)
|
||||||
|
.map_err_into(MEK::IOError)
|
||||||
|
})
|
||||||
|
.map(Buffer::from)
|
||||||
|
.map(|buffer| Mail(reference, buffer))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens a mail by the passed hash
|
||||||
|
pub fn open<S: AsRef<str>>(store: &Store, hash: S) -> Result<Option<Mail>> {
|
||||||
|
Ref::get_by_hash(store, String::from(hash.as_ref()))
|
||||||
|
.map_err_into(MEK::FetchByHashError)
|
||||||
|
.map_err_into(MEK::FetchError)
|
||||||
|
.and_then(|o| match o {
|
||||||
|
Some(r) => Mail::from_ref(r).map(Some),
|
||||||
|
None => Ok(None),
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement me as TryFrom as soon as it is stable
|
||||||
|
pub fn from_ref(r: Ref<'a>) -> Result<Mail> {
|
||||||
|
r.fs_file()
|
||||||
|
.map_err_into(MEK::RefHandlingError)
|
||||||
|
.and_then(|path| File::open(path).map_err_into(MEK::IOError))
|
||||||
|
.and_then(|mut file| {
|
||||||
|
let mut s = String::new();
|
||||||
|
file.read_to_string(&mut s)
|
||||||
|
.map(|_| s)
|
||||||
|
.map_err_into(MEK::IOError)
|
||||||
|
})
|
||||||
|
.map(Buffer::from)
|
||||||
|
.map(|buffer| Mail(r, buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_field(&self, field: &str) -> Result<Option<String>> {
|
||||||
|
use mailparse::MailHeader;
|
||||||
|
|
||||||
|
self.1
|
||||||
|
.parsed()
|
||||||
|
.map_err_into(MEK::MailParsingError)
|
||||||
|
.map(|parsed| {
|
||||||
|
parsed.headers
|
||||||
|
.iter()
|
||||||
|
.filter(|hdr| hdr.get_key().map(|n| n == field).unwrap_or(false))
|
||||||
|
.next()
|
||||||
|
.and_then(|field| field.get_value().ok())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_from(&self) -> Result<Option<String>> {
|
||||||
|
self.get_field("From")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_to(&self) -> Result<Option<String>> {
|
||||||
|
self.get_field("To")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_subject(&self) -> Result<Option<String>> {
|
||||||
|
self.get_field("Subject")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_message_id(&self) -> Result<Option<String>> {
|
||||||
|
self.get_field("Message-ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_in_reply_to(&self) -> Result<Option<String>> {
|
||||||
|
self.get_field("In-Reply-To")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
6
libimagmail/src/result.rs
Normal file
6
libimagmail/src/result.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
use std::result::Result as RResult;
|
||||||
|
|
||||||
|
use error::MailError;
|
||||||
|
|
||||||
|
pub type Result<T> = RResult<T, MailError>;
|
||||||
|
|
|
@ -41,7 +41,9 @@ generate_error_module!(
|
||||||
RefTargetPermissionError => "Ref Target permissions insufficient for referencing",
|
RefTargetPermissionError => "Ref Target permissions insufficient for referencing",
|
||||||
RefTargetCannotBeHashed => "Ref Target cannot be hashed (is it a directory?)",
|
RefTargetCannotBeHashed => "Ref Target cannot be hashed (is it a directory?)",
|
||||||
RefTargetFileCannotBeOpened => "Ref Target File cannot be open()ed",
|
RefTargetFileCannotBeOpened => "Ref Target File cannot be open()ed",
|
||||||
RefTargetCannotReadPermissions => "Ref Target: Cannot read permissions"
|
RefTargetCannotReadPermissions => "Ref Target: Cannot read permissions",
|
||||||
|
|
||||||
|
RefHashingError => "Error while hashing"
|
||||||
);
|
);
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue