diff options
Diffstat (limited to 'lib/domain/libimagmail')
-rw-r--r-- | lib/domain/libimagmail/Cargo.toml | 30 | ||||
l--------- | lib/domain/libimagmail/README.md | 1 | ||||
-rw-r--r-- | lib/domain/libimagmail/src/iter.rs | 55 | ||||
-rw-r--r-- | lib/domain/libimagmail/src/lib.rs | 51 | ||||
-rw-r--r-- | lib/domain/libimagmail/src/mail.rs | 201 |
5 files changed, 338 insertions, 0 deletions
diff --git a/lib/domain/libimagmail/Cargo.toml b/lib/domain/libimagmail/Cargo.toml new file mode 100644 index 00000000..294040f3 --- /dev/null +++ b/lib/domain/libimagmail/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "libimagmail" +version = "0.10.0" +authors = ["Matthias Beyer <mail@beyermatthias.de>"] + +description = "Library for the imag core distribution" + +keywords = ["imag", "PIM", "personal", "information", "management"] +readme = "../../../README.md" +license = "LGPL-2.1" + +documentation = "https://imag-pim.org/doc/" +repository = "https://github.com/matthiasbeyer/imag" +homepage = "http://imag-pim.org" + +[badges] +travis-ci = { repository = "matthiasbeyer/imag" } +is-it-maintained-issue-resolution = { repository = "matthiasbeyer/imag" } +is-it-maintained-open-issues = { repository = "matthiasbeyer/imag" } +maintenance = { status = "actively-developed" } + +[dependencies] +log = "0.4.0" +email = "0.0.20" +filters = "0.3" +failure = "0.1" + +libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" } +libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" } +libimagentryref = { version = "0.10.0", path = "../../../lib/entry/libimagentryref" } diff --git a/lib/domain/libimagmail/README.md b/lib/domain/libimagmail/README.md new file mode 120000 index 00000000..9aeb65d2 --- /dev/null +++ b/lib/domain/libimagmail/README.md @@ -0,0 +1 @@ +../../../doc/src/05100-lib-mails.md
\ No newline at end of file diff --git a/lib/domain/libimagmail/src/iter.rs b/lib/domain/libimagmail/src/iter.rs new file mode 100644 index 00000000..e4d375cd --- /dev/null +++ b/lib/domain/libimagmail/src/iter.rs @@ -0,0 +1,55 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2019 Matthias Beyer <mail@beyermatthias.de> and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +//! 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 failure::Fallible as Result; + +use libimagstore::store::FileLockEntry; + +use std::marker::PhantomData; + +pub struct MailIter<'a, I: Iterator<Item = FileLockEntry<'a>>> { + _marker: PhantomData<I>, + i: I, +} + +impl<'a, I: Iterator<Item = FileLockEntry<'a>>> MailIter<'a, I> { + + pub fn new(i: I) -> MailIter<'a, I> { + MailIter { _marker: PhantomData, i: i } + } + +} + +impl<'a, I: Iterator<Item = FileLockEntry<'a>>> Iterator for MailIter<'a, I> { + type Item = Result<Mail<'a>>; + + fn next(&mut self) -> Option<Self::Item> { + self.i.next().map(Mail::from_fle) + } + +} + diff --git a/lib/domain/libimagmail/src/lib.rs b/lib/domain/libimagmail/src/lib.rs new file mode 100644 index 00000000..3ba7397f --- /dev/null +++ b/lib/domain/libimagmail/src/lib.rs @@ -0,0 +1,51 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2019 Matthias Beyer <mail@beyermatthias.de> and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +#![forbid(unsafe_code)] + +#![recursion_limit="256"] + +#![deny( + dead_code, + non_camel_case_types, + non_snake_case, + path_statements, + trivial_numeric_casts, + unstable_features, + unused_allocation, + unused_import_braces, + unused_imports, + unused_must_use, + unused_mut, + unused_qualifications, + while_true, +)] + +#[macro_use] extern crate log; +extern crate email; +extern crate filters; +extern crate failure; + +extern crate libimagerror; +extern crate libimagstore; +extern crate libimagentryref; + +pub mod iter; +pub mod mail; + diff --git a/lib/domain/libimagmail/src/mail.rs b/lib/domain/libimagmail/src/mail.rs new file mode 100644 index 00000000..ab59a694 --- /dev/null +++ b/lib/domain/libimagmail/src/mail.rs @@ -0,0 +1,201 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2019 Matthias Beyer <mail@beyermatthias.de> and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use std::path::Path; +use std::fs::File; +use std::io::Read; +use std::fs::OpenOptions; + +use libimagstore::store::Store; +use libimagstore::storeid::StoreId; +use libimagstore::store::FileLockEntry; +use libimagentryref::reference::Ref; +use libimagentryref::refstore::RefStore; +use libimagentryref::refstore::UniqueRefPathGenerator; +use libimagerror::errors::ErrorMsg as EM; + +use email::MimeMessage; +use email::results::ParsingResult as EmailParsingResult; + +use failure::Fallible as Result; +use failure::ResultExt; +use failure::Error; +use failure::err_msg; + +struct UniqueMailRefGenerator; +impl UniqueRefPathGenerator for UniqueMailRefGenerator { + /// The collection the `StoreId` should be created for + fn collection() -> &'static str { + "mail" + } + + /// A function which should generate a unique string for a Path + fn unique_hash<A: AsRef<Path>>(path: A) -> Result<String> { + use filters::filter::Filter; + use email::Header; + + let mut s = String::new(); + let _ = OpenOptions::new() + .read(true) + .write(false) + .create(false) + .open(path)? + .read_to_string(&mut s)?; + + MimeMessage::parse(&s) + .context(err_msg("Error creating ref")) + .map_err(Error::from) + .and_then(|mail| { + let has_key = |hdr: &Header, exp: &str| hdr.name == exp; + + let subject_filter = |hdr: &Header| has_key(hdr, "Subject"); + let from_filter = |hdr: &Header| has_key(hdr, "From"); + let to_filter = |hdr: &Header| has_key(hdr, "To"); + + let filter = subject_filter.or(from_filter).or(to_filter); + + let mut v : Vec<String> = vec![]; + for hdr in mail.headers.iter().filter(|item| filter.filter(item)) { + let s = hdr + .get_value() + .context(err_msg("Ref creation error"))?; + + v.push(s); + } + let s : String = v.join(""); + Ok(s) + }) + } + + /// Postprocess the generated `StoreId` object + fn postprocess_storeid(sid: StoreId) -> Result<StoreId> { + Ok(sid) + } +} + +struct Buffer(String); + +impl Buffer { + pub fn parsed(&self) -> EmailParsingResult<MimeMessage> { + MimeMessage::parse(&self.0) + } +} + +impl From<String> for Buffer { + fn from(data: String) -> Buffer { + Buffer(data) + } +} + +pub struct Mail<'a>(FileLockEntry<'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> { + debug!("Importing Mail from path"); + store.retrieve_ref::<UniqueMailRefGenerator, P>(p) + .and_then(|reference| { + debug!("Build reference file: {:?}", reference); + reference.get_path() + .context(err_msg("Ref handling error")) + .map_err(Error::from) + .and_then(|path| File::open(path).context(EM::IO).map_err(Error::from)) + .and_then(|mut file| { + let mut s = String::new(); + file.read_to_string(&mut s) + .map(|_| s) + .context(EM::IO) + .map_err(Error::from) + }) + .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>> { + debug!("Opening Mail by Hash"); + store.get_ref::<UniqueMailRefGenerator, S>(hash) + .context(err_msg("Fetch by hash error")) + .context(err_msg("Fetch error")) + .map_err(Error::from) + .and_then(|o| match o { + Some(r) => Mail::from_fle(r).map(Some), + None => Ok(None), + }) + } + + /// Implement me as TryFrom as soon as it is stable + pub fn from_fle(fle: FileLockEntry<'a>) -> Result<Mail<'a>> { + fle.get_path() + .context(err_msg("Ref handling error")) + .map_err(Error::from) + .and_then(|path| File::open(path).context(EM::IO).map_err(Error::from)) + .and_then(|mut file| { + let mut s = String::new(); + file.read_to_string(&mut s) + .map(|_| s) + .context(EM::IO) + .map_err(Error::from) + }) + .map(Buffer::from) + .map(|buffer| Mail(fle, buffer)) + } + + pub fn get_field(&self, field: &str) -> Result<Option<String>> { + debug!("Getting field in mail: {:?}", field); + self.1 + .parsed() + .context(err_msg("Mail parsing error")) + .map_err(Error::from) + .map(|parsed| { + parsed.headers + .iter() + .filter(|hdr| hdr.name == field) + .nth(0) + .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") + } + + pub fn fle(&self) -> &FileLockEntry<'a> { + &self.0 + } + +} |