From f84cc8169fffee3904cd44c14baec0bac7a05478 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 17 Feb 2019 11:49:27 +0100 Subject: [PATCH] Revert "Remove libimagmail" This reverts commit 74045e18007152d2fff63e83170e3406efcc73ac. Signed-off-by: Matthias Beyer --- Cargo.toml | 1 + doc/src/05100-lib-mails.md | 14 ++ lib/domain/libimagmail/Cargo.toml | 30 +++++ lib/domain/libimagmail/README.md | 1 + lib/domain/libimagmail/src/iter.rs | 55 ++++++++ lib/domain/libimagmail/src/lib.rs | 51 ++++++++ lib/domain/libimagmail/src/mail.rs | 201 +++++++++++++++++++++++++++++ 7 files changed, 353 insertions(+) create mode 100644 doc/src/05100-lib-mails.md create mode 100644 lib/domain/libimagmail/Cargo.toml create mode 120000 lib/domain/libimagmail/README.md create mode 100644 lib/domain/libimagmail/src/iter.rs create mode 100644 lib/domain/libimagmail/src/lib.rs create mode 100644 lib/domain/libimagmail/src/mail.rs diff --git a/Cargo.toml b/Cargo.toml index 4276aff5..96a18e29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ members = [ "lib/domain/libimagdiary", "lib/domain/libimaghabit", "lib/domain/libimaglog", + "lib/domain/libimagmail", "lib/domain/libimagnotes", "lib/domain/libimagtimetrack", "lib/domain/libimagtodo", diff --git a/doc/src/05100-lib-mails.md b/doc/src/05100-lib-mails.md new file mode 100644 index 00000000..fde0879f --- /dev/null +++ b/doc/src/05100-lib-mails.md @@ -0,0 +1,14 @@ +## libimagmails + +The mail library implements everything that is needed for beeing used to +implement a mail reader (MUA). + +It therefor providea reading mailboxes, getting related content or mails, saving +attachements to external locations, crafting new mails and responses,... + +It also offers, natively, ways to search for mails (which are represented as +imag entries) via tags, categories or even other metadata. + +For more information on the domain of the `imag-mail` command, look at the +documentation of the @sec:modules:mails module. + 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 "] + +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 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`, 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>> { + _marker: PhantomData, + i: I, +} + +impl<'a, I: Iterator>> MailIter<'a, I> { + + pub fn new(i: I) -> MailIter<'a, I> { + MailIter { _marker: PhantomData, i: i } + } + +} + +impl<'a, I: Iterator>> Iterator for MailIter<'a, I> { + type Item = Result>; + + fn next(&mut self) -> Option { + 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 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 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>(path: A) -> Result { + 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 = 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 { + Ok(sid) + } +} + +struct Buffer(String); + +impl Buffer { + pub fn parsed(&self) -> EmailParsingResult { + MimeMessage::parse(&self.0) + } +} + +impl From 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>(store: &Store, p: P) -> Result { + debug!("Importing Mail from path"); + store.retrieve_ref::(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>(store: &Store, hash: S) -> Result> { + debug!("Opening Mail by Hash"); + store.get_ref::(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> { + 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> { + 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> { + self.get_field("From") + } + + pub fn get_to(&self) -> Result> { + self.get_field("To") + } + + pub fn get_subject(&self) -> Result> { + self.get_field("Subject") + } + + pub fn get_message_id(&self) -> Result> { + self.get_field("Message-ID") + } + + pub fn get_in_reply_to(&self) -> Result> { + self.get_field("In-Reply-To") + } + + pub fn fle(&self) -> &FileLockEntry<'a> { + &self.0 + } + +}