Revert "Remove libimagmail"

This reverts commit 74045e1800.

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
This commit is contained in:
Matthias Beyer 2019-02-17 11:49:27 +01:00
parent 27c0a30494
commit f84cc8169f
7 changed files with 353 additions and 0 deletions

View file

@ -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",

View file

@ -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.

View file

@ -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" }

View file

@ -0,0 +1 @@
../../../doc/src/05100-lib-mails.md

View file

@ -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)
}
}

View file

@ -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;

View file

@ -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
}
}