Revert "Remove libimagmail"
This reverts commit 74045e1800
.
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
This commit is contained in:
parent
27c0a30494
commit
f84cc8169f
7 changed files with 353 additions and 0 deletions
|
@ -34,6 +34,7 @@ members = [
|
||||||
"lib/domain/libimagdiary",
|
"lib/domain/libimagdiary",
|
||||||
"lib/domain/libimaghabit",
|
"lib/domain/libimaghabit",
|
||||||
"lib/domain/libimaglog",
|
"lib/domain/libimaglog",
|
||||||
|
"lib/domain/libimagmail",
|
||||||
"lib/domain/libimagnotes",
|
"lib/domain/libimagnotes",
|
||||||
"lib/domain/libimagtimetrack",
|
"lib/domain/libimagtimetrack",
|
||||||
"lib/domain/libimagtodo",
|
"lib/domain/libimagtodo",
|
||||||
|
|
14
doc/src/05100-lib-mails.md
Normal file
14
doc/src/05100-lib-mails.md
Normal 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.
|
||||||
|
|
30
lib/domain/libimagmail/Cargo.toml
Normal file
30
lib/domain/libimagmail/Cargo.toml
Normal 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" }
|
1
lib/domain/libimagmail/README.md
Symbolic link
1
lib/domain/libimagmail/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../doc/src/05100-lib-mails.md
|
55
lib/domain/libimagmail/src/iter.rs
Normal file
55
lib/domain/libimagmail/src/iter.rs
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
51
lib/domain/libimagmail/src/lib.rs
Normal file
51
lib/domain/libimagmail/src/lib.rs
Normal 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;
|
||||||
|
|
201
lib/domain/libimagmail/src/mail.rs
Normal file
201
lib/domain/libimagmail/src/mail.rs
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue