summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2020-01-05 13:49:25 +0100
committerMatthias Beyer <mail@beyermatthias.de>2020-01-05 16:11:13 +0100
commit24e21a5b9c2ee3bd2b3ac72644e16fb1e937967c (patch)
treed33ea6de22d8f96838da86dc96c8acc33cb865ed
parent93d3bda837122bfc7a05ec20690bac2b49fbaf15 (diff)
Start rewriting backend to use notmuch
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r--doc/src/05100-lib-mails.md16
-rw-r--r--lib/domain/libimagmail/Cargo.toml3
-rw-r--r--lib/domain/libimagmail/src/config.rs131
-rw-r--r--lib/domain/libimagmail/src/fetch.rs117
-rw-r--r--lib/domain/libimagmail/src/iter.rs83
-rw-r--r--lib/domain/libimagmail/src/lib.rs11
-rw-r--r--lib/domain/libimagmail/src/mail.rs357
-rw-r--r--lib/domain/libimagmail/src/mailflags.rs93
-rw-r--r--lib/domain/libimagmail/src/mid.rs59
-rw-r--r--lib/domain/libimagmail/src/notmuch/connection.rs77
-rw-r--r--lib/domain/libimagmail/src/notmuch/mod.rs (renamed from lib/domain/libimagmail/src/hasher.rs)25
-rw-r--r--lib/domain/libimagmail/src/send.rs111
-rw-r--r--lib/domain/libimagmail/src/store.rs211
-rw-r--r--lib/domain/libimagmail/src/util.rs96
14 files changed, 176 insertions, 1214 deletions
diff --git a/doc/src/05100-lib-mails.md b/doc/src/05100-lib-mails.md
index 9a42a47a..b108f364 100644
--- a/doc/src/05100-lib-mails.md
+++ b/doc/src/05100-lib-mails.md
@@ -1,14 +1,10 @@
-## libimagmails
+## libimagmail
-The mail library implements everything that is needed for being used to
-implement a mail reader (MUA).
+This library implements an integration from notmuch for imag.
-It therefor provides reading mailboxes, getting related content or mails, saving
-attachments to external locations, crafting new mails and responses,...
+The library does not (yet) provide functionality to alter the database of
+notmuch and maybe never will.
-It also offers, natively, ways to search for mails (which are represented as
-imag entries).
-
-For more information on the domain of the `imag-mail` command, look at the
-documentation of the @sec:modules:mails module.
+It creates one imag entry for one notmuch message id. It only stores the message
+id in the entry, all other data can be fetched from notmuch at runtime.
diff --git a/lib/domain/libimagmail/Cargo.toml b/lib/domain/libimagmail/Cargo.toml
index e0b1a891..daedf129 100644
--- a/lib/domain/libimagmail/Cargo.toml
+++ b/lib/domain/libimagmail/Cargo.toml
@@ -27,8 +27,7 @@ mailparse = "0.8.0"
filters = "0.3.0"
failure = "0.1.5"
resiter = "0.4.0"
-serde = "1.0.94"
-serde_derive = "1.0.94"
+notmuch = "0.6"
libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" }
diff --git a/lib/domain/libimagmail/src/config.rs b/lib/domain/libimagmail/src/config.rs
deleted file mode 100644
index 4c59cb87..00000000
--- a/lib/domain/libimagmail/src/config.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-//
-// imag - the personal information management suite for the commandline
-// Copyright (C) 2015-2020 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::PathBuf;
-
-/// A struct representing a full mail configuration, required for working with this library
-///
-/// For convenience reasons, this implements Serialize and Deserialize, so it can be fetched from a
-/// configuration file for example
-///
-/// # TODO
-///
-/// Figure out how to use handlebars with variables on this. Right now the support for that is not
-/// implemented yet.
-///
-#[derive(Serialize, Deserialize, Debug)]
-pub struct MailConfig {
- default_account : String,
- accounts : Vec<MailAccountConfig>,
- fetchcommand : MailCommand,
- postfetchcommand : Option<MailCommand>,
- sendcommand : MailCommand,
- postsendcommand : Option<MailCommand>,
-}
-
-impl MailConfig {
- pub fn default_account(&self) -> &String {
- &self.default_account
- }
-
- pub fn accounts(&self) -> &Vec<MailAccountConfig> {
- &self.accounts
- }
-
- pub fn account(&self, name: &str) -> Option<&MailAccountConfig> {
- self.accounts()
- .iter()
- .find(|a| a.name == name)
- }
-
- pub fn fetchcommand(&self) -> &MailCommand {
- &self.fetchcommand
- }
-
- pub fn postfetchcommand(&self) -> Option<&MailCommand> {
- self.postfetchcommand.as_ref()
- }
-
- pub fn sendcommand(&self) -> &MailCommand {
- &self.sendcommand
- }
-
- pub fn postsendcommand(&self) -> Option<&MailCommand> {
- self.postsendcommand.as_ref()
- }
-
- pub fn fetchcommand_for_account(&self, account_name: &str) -> &MailCommand {
- self.accounts()
- .iter()
- .find(|a| a.name == account_name)
- .and_then(|a| a.fetchcommand.as_ref())
- .unwrap_or_else(|| self.fetchcommand())
- }
-
- pub fn postfetchcommand_for_account(&self, account_name: &str) -> Option<&MailCommand> {
- self.accounts()
- .iter()
- .find(|a| a.name == account_name)
- .and_then(|a| a.postfetchcommand.as_ref())
- .or_else(|| self.postfetchcommand())
- }
-
- pub fn sendcommand_for_account(&self, account_name: &str) -> &MailCommand {
- self.accounts()
- .iter()
- .find(|a| a.name == account_name)
- .and_then(|a| a.sendcommand.as_ref())
- .unwrap_or_else(|| self.sendcommand())
- }
-
- pub fn postsendcommand_for_account(&self, account_name: &str) -> Option<&MailCommand> {
- self.accounts()
- .iter()
- .find(|a| a.name == account_name)
- .and_then(|a| a.postsendcommand.as_ref())
- .or_else(|| self.postsendcommand())
- }
-
-}
-
-/// A configuration for a single mail accounts
-///
-/// If one of the keys `fetchcommand`, `postfetchcommand`, `sendcommand` or `postsendcommand` is
-/// not available, the implementation of the `MailConfig` will automatically use the global
-/// configuration if applicable.
-#[derive(Serialize, Deserialize, Debug)]
-pub struct MailAccountConfig {
- pub name : String,
- pub outgoingbox : PathBuf,
- pub draftbox : PathBuf,
- pub sentbox : PathBuf,
- pub maildirroot : PathBuf,
- pub fetchcommand : Option<MailCommand>,
- pub postfetchcommand : Option<MailCommand>,
- pub sendcommand : Option<MailCommand>,
- pub postsendcommand : Option<MailCommand>,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct MailCommand {
- command: String,
- env: Vec<String>,
- args: Vec<String>,
-}
-
diff --git a/lib/domain/libimagmail/src/fetch.rs b/lib/domain/libimagmail/src/fetch.rs
deleted file mode 100644
index fa7106d5..00000000
--- a/lib/domain/libimagmail/src/fetch.rs
+++ /dev/null
@@ -1,117 +0,0 @@
-//
-// imag - the personal information management suite for the commandline
-// Copyright (C) 2015-2020 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 config::MailConfig;
-
-pub struct MailFetcher<'a> {
- config: &'a MailConfig,
- account_name_to_fetch: Option<String>,
- boxes: Vec<String>,
-
- rescan_maildirs: bool,
-}
-
-impl MailFetcher {
- pub fn new(config: &MailConfig) -> Self {
- MailFetcher {
- config,
- account_name_to_fetch: None,
- rescan_maildirs: false
- }
- }
-
- pub fn fetch_account(mut self, name: String) -> Self {
- self.account_name_to_fetch = Some(name);
- self
- }
-
- pub fn fetch_box(mut self, name: String) -> Self {
- self.boxes.push(name);
- self
- }
-
- pub fn fetch_boxes<I>(mut self, names: I) -> Self
- where I: IntoIterator<Item = String>
- {
- self.boxes.append(names.into_iter().collect())
- self
- }
-
- pub fn rescan_maildirs(mut self, b: bool) -> Self {
- self.rescan_maildirs = b;
- self
- }
-
- pub fn run(&self, store: &Store) -> Result<()> {
- let fetchcommand = match self.account_name_to_fetch {
- Some(name) => self.config.fetchcommand_for_account(name),
- None => self.confnig.fetchcommand(),
- };
-
- let postfetchcommand = match self.account_name_to_fetch {
- Some(name) => self.config.postfetchcommand_for_account(name),
- None => self.confnig.postfetchcommand(),
- };
-
- let account = config
- .account(self.account_name_to_fetch)
- .ok_or_else(|| format_err!("Account '{}' does not exist", self.account_name_to_fetch))?;
-
- if fetchcommand.contains(" ") {
- // error on whitespace in command
- }
-
- if postfetchcommand.contains(" ") {
- // error on whitespace in command
- }
-
- // fetchcommand
-
- let mut output = Command::new(fetchcommand)
- // TODO: Add argument support
- // TODO: Add support for passing config variables
- // TODO: Add support for passing environment
- .args(self.boxes)
- .wait_with_output()
- .context("Mail fetching")?;
-
- write!(rt.stdout(), "{}", output.stdout)?;
- write!(rt.stderr(), "{}", output.stderr)?;
-
- // postfetchcommand
-
- let output = Command::new(postfetchcommand)
- // TODO: Add argument support
- // TODO: Add support for passing config variables
- .wait_with_output()
- .context("Post 'Mail fetching' command")?;
-
- write!(rt.stdout(), "{}", output.stdout)?;
- write!(rt.stderr(), "{}", output.stderr)?;
-
- if self.rescan_maildirs {
- // scan
- // account.maildirroot
- // recursively for new mail and store them in imag
- }
- }
-
-}
-
-
diff --git a/lib/domain/libimagmail/src/iter.rs b/lib/domain/libimagmail/src/iter.rs
deleted file mode 100644
index bba6b690..00000000
--- a/lib/domain/libimagmail/src/iter.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-//
-// imag - the personal information management suite for the commandline
-// Copyright (C) 2015-2020 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 failure::Fallible as Result;
-use failure::ResultExt;
-use failure::Error;
-use failure::err_msg;
-
-use libimagstore::iter::get::StoreGetIterator;
-use libimagstore::store::FileLockEntry;
-
-use crate::mail::Mail;
-
-pub struct MailIterator<'a> {
- inner: StoreGetIterator<'a>,
- ignore_ungetable: bool,
-}
-
-impl<'a> MailIterator<'a> {
- pub fn new(sgi: StoreGetIterator<'a>) -> Self {
- MailIterator { inner: sgi, ignore_ungetable: true }
- }
-
- pub fn ignore_ungetable(mut self, b: bool) -> Self {
- self.ignore_ungetable = b;
- self
- }
-}
-
-pub trait IntoMailIterator<'a> {
- fn into_mail_iterator(self) -> MailIterator<'a>;
-}
-
-impl<'a> IntoMailIterator<'a> for StoreGetIterator<'a> {
- fn into_mail_iterator(self) -> MailIterator<'a> {
- MailIterator::new(self)
- }
-}
-
-impl<'a> Iterator for MailIterator<'a> {
- type Item = Result<FileLockEntry<'a>>;
-
- fn next(&mut self) -> Option<Self::Item> {
- while let Some(n) = self.inner.next() {
- match n {
- Ok(Some(fle)) => {
- match fle.is_mail().context("Checking whether entry is a Mail").map_err(Error::from) {
- Err(e) => return Some(Err(e)),
- Ok(true) => return Some(Ok(fle)),
- Ok(false) => continue,
- }
- },
-
- Ok(None) => if self.ignore_ungetable {
- continue
- } else {
- return Some(Err(err_msg("Failed to get one entry")))
- },
-
- Err(e) => return Some(Err(e)),
- }
- }
-
- None
- }
-}
-
diff --git a/lib/domain/libimagmail/src/lib.rs b/lib/domain/libimagmail/src/lib.rs
index 913acfc9..010ec10c 100644
--- a/lib/domain/libimagmail/src/lib.rs
+++ b/lib/domain/libimagmail/src/lib.rs
@@ -44,8 +44,8 @@ extern crate toml_query;
extern crate filters;
#[macro_use] extern crate failure;
extern crate resiter;
-extern crate serde;
-#[macro_use] extern crate serde_derive;
+extern crate chrono;
+extern crate notmuch as notmuch_rs;
extern crate libimagerror;
#[macro_use] extern crate libimagstore;
@@ -55,12 +55,7 @@ extern crate libimagentrylink;
module_entry_path_mod!("mail");
-pub mod config;
-pub mod hasher;
-pub mod iter;
+pub mod notmuch;
pub mod mail;
-pub mod mailflags;
-pub mod mid;
pub mod store;
-pub mod util;
diff --git a/lib/domain/libimagmail/src/mail.rs b/lib/domain/libimagmail/src/mail.rs
index f24f5506..dd2b93db 100644
--- a/lib/domain/libimagmail/src/mail.rs
+++ b/lib/domain/libimagmail/src/mail.rs
@@ -17,371 +17,22 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
-use std::str::FromStr;
-
use failure::Fallible as Result;
-use failure::ResultExt;
use failure::Error;
-use toml_query::read::TomlValueReadExt;
-use resiter::Filter;
use libimagstore::store::Entry;
-use libimagentryutil::isa::Is;
use libimagentryutil::isa::IsKindHeaderPathProvider;
-use libimagentryref::reference::Config as RefConfig;
-use libimagentryref::reference::{Ref, RefFassade};
-use libimagentrylink::linkable::Linkable;
-use libimagstore::store::Store;
-use libimagstore::storeid::StoreId;
-use libimagstore::storeid::StoreIdIterator;
-use libimagstore::iter::get::StoreIdGetIteratorExtension;
+use libimagentryutil::isa::Is;
-use crate::mid::MessageId;
-use crate::mailflags::MailFlag;
-use crate::hasher::MailHasher;
-use crate::iter::MailIterator;
-use crate::iter::IntoMailIterator;
provide_kindflag_path!(pub IsMail, "mail.is_mail");
-pub trait Mail : RefFassade + Linkable {
- fn is_mail(&self) -> Result<bool>;
- fn get_field(&self, refconfig: &RefConfig, field: &str) -> Result<Option<String>>;
- fn get_from(&self, refconfig: &RefConfig) -> Result<Option<String>>;
- fn get_to(&self, refconfig: &RefConfig) -> Result<Option<String>>;
- fn get_subject(&self, refconfig: &RefConfig) -> Result<Option<String>>;
- fn get_message_id(&self, refconfig: &RefConfig) -> Result<Option<MessageId>>;
- fn get_in_reply_to(&self, refconfig: &RefConfig) -> Result<Option<MessageId>>;
-
- fn flags(&self, refconfig: &RefConfig) -> Result<Vec<MailFlag>>;
- fn is_passed(&self, refconfig: &RefConfig) -> Result<bool>;
- fn is_replied(&self, refconfig: &RefConfig) -> Result<bool>;
- fn is_seen(&self, refconfig: &RefConfig) -> Result<bool>;
- fn is_trashed(&self, refconfig: &RefConfig) -> Result<bool>;
- fn is_draft(&self, refconfig: &RefConfig) -> Result<bool>;
- fn is_flagged(&self, refconfig: &RefConfig) -> Result<bool>;
-
- fn neighbors(&self) -> Result<StoreIdIterator>;
- fn get_neighbors<'a>(&self, store: &'a Store) -> Result<MailIterator<'a>>;
- fn get_thread<'a>(&self, store: &'a Store) -> Result<MailIterator<'a>>;
-
+pub trait Mail {
+ fn is_mail(&self) -> Result<bool>;
}
impl Mail for Entry {
-
fn is_mail(&self) -> Result<bool> {
- self.is::<IsMail>()
- }
-
- /// Get a value of a single field of the mail file
- fn get_field(&self, refconfig: &RefConfig, field: &str) -> Result<Option<String>> {
- use std::fs::read_to_string;
-
- debug!("Getting field in mail: {:?}", field);
- let mail_file_location = self.as_ref_with_hasher::<MailHasher>().get_path(refconfig)?;
-
- match ::mailparse::parse_mail(read_to_string(mail_file_location.as_path())?.as_bytes())
- .context(format_err!("Cannot parse Email {}", mail_file_location.display()))?
- .headers
- .into_iter()
- .filter_map(|hdr| {
- match hdr.get_key()
- .context(format_err!("Cannot fetch key '{}' from Email {}", field, mail_file_location.display()))
- .map_err(Error::from)
- {
- Ok(k) => if k == field {
- Some(Ok(hdr))
- } else {
- None
- },
- Err(e) => Some(Err(e)),
- }
- })
- .next()
- {
- None => Ok(None),
- Some(Err(e)) => Err(e),
- Some(Ok(hdr)) => Ok(Some(hdr.get_value()?))
- }
- }
-
- /// Get a value of the `From` field of the mail file
- ///
- /// # Note
- ///
- /// Use `Mail::mail_header()` if you need to read more than one field.
- fn get_from(&self, refconfig: &RefConfig) -> Result<Option<String>> {
- self.get_field(refconfig, "From")
- }
-
- /// Get a value of the `To` field of the mail file
- ///
- /// # Note
- ///
- /// Use `Mail::mail_header()` if you need to read more than one field.
- fn get_to(&self, refconfig: &RefConfig) -> Result<Option<String>> {
- self.get_field(refconfig, "To")
- }
-
- /// Get a value of the `Subject` field of the mail file
- ///
- /// # Note
- ///
- /// Use `Mail::mail_header()` if you need to read more than one field.
- fn get_subject(&self, refconfig: &RefConfig) -> Result<Option<String>> {
- self.get_field(refconfig, "Subject")
- }
-
- /// Get a value of the `Message-ID` field of the mail file
- ///
- /// # Note
- ///
- /// Use `Mail::mail_header()` if you need to read more than one field.
- fn get_message_id(&self, refconfig: &RefConfig) -> Result<Option<MessageId>> {
- if let Some(s) = self.get_header().read("mail.message-id")? {
- let s = s.as_str()
- .ok_or_else(|| format_err!("'mail.message-id' is not a String in {}", self.get_location()))?;
- Ok(Some(MessageId::from(String::from(s))))
- } else {
- self.get_field(refconfig, "Message-ID")
- .map(|o| o.map(crate::util::strip_message_delimiters).map(MessageId::from))
- }
- }
-
- /// Get a value of the `In-Reply-To` field of the mail file
- ///
- /// # Note
- ///
- /// Use `Mail::mail_header()` if you need to read more than one field.
- fn get_in_reply_to(&self, refconfig: &RefConfig) -> Result<Option<MessageId>> {
- self.get_field(refconfig, "In-Reply-To")
- .map(|o| o.map(crate::util::strip_message_delimiters).map(MessageId::from))
- }
-
- /// Get the flags of the message
- fn flags(&self, refconfig: &RefConfig) -> Result<Vec<MailFlag>> {
- let path = self.as_ref_with_hasher::<MailHasher>().get_path(refconfig)?;
-
- if !path.exists() {
- return Err(format_err!("Path {} does not exist", path.display()))
- }
-
- {
- // Now parse mail flags
- path.to_str()
- .ok_or_else(|| format_err!("Path is not UTF-8: {}", path.display()))?
- .split("2,")
- .map(String::from)
- .collect::<Vec<String>>()
- .split_last()
- .ok_or_else(|| format_err!("Splitting path into prefix and flags failed: {}", path.display()))?
- .0
- .chars()
- .map(|c| c.to_string())
- .map(|c| MailFlag::from_str(&c))
- .collect::<Result<Vec<_>>>()
- }
- }
-
- /// Check whether the mail is passed
- fn is_passed(&self, refconfig: &RefConfig) -> Result<bool> {
- self.flags(refconfig).map(|fs| fs.into_iter().any(|f| MailFlag::Passed == f))
- }
-
- /// Check whether the mail is replied
- fn is_replied(&self, refconfig: &RefConfig) -> Result<bool> {
- self.flags(refconfig).map(|fs| fs.into_iter().any(|f| MailFlag::Replied == f))
- }
-
- /// Check whether the mail is seen
- fn is_seen(&self, refconfig: &RefConfig) -> Result<bool> {
- self.flags(refconfig).map(|fs| fs.into_iter().any(|f| MailFlag::Seen == f))
- }
-
- /// Check whether the mail is trashed
- fn is_trashed(&self, refconfig: &RefConfig) -> Result<bool> {
- self.flags(refconfig).map(|fs| fs.into_iter().any(|f| MailFlag::Trashed == f))
- }
-
- /// Check whether the mail is draft
- fn is_draft(&self, refconfig: &RefConfig) -> Result<bool> {
- self.flags(refconfig).map(|fs| fs.into_iter().any(|f| MailFlag::Draft == f))
- }
-
- /// Check whether the mail is flagged
- fn is_flagged(&self, refconfig: &RefConfig) -> Result<bool> {
- self.flags(refconfig).map(|fs| fs.into_iter().any(|f| MailFlag::Flagged == f))
- }
-
- /// Get all direct neighbors for the Mail
- ///
- /// # Note
- ///
- /// This fetches only the neighbors which are linked. So it basically only checks the entries
- /// which this entry is linked to and filters them for Mail::is_mail()
- ///
- /// # Warning
- ///
- /// Might yield store entries which are not a Mail in the Mail::is_mail() sence but are simply
- /// stored in /mail in the store.
- ///
- /// To be sure, you should filter this iterator after getting the FileLockEntries from Store.
- /// Or use `Mail::get_neighbors(&store)`.
- ///
- fn neighbors(&self) -> Result<StoreIdIterator> {
- let iter = self
- .links()?
- .map(|link| link.into())
- .filter(|id: &StoreId| id.is_in_collection(&["mail"]))
- .map(Ok);
-
- Ok(StoreIdIterator::new(Box::new(iter)))
- }
-
- /// Get alldirect neighbors for the Mail (as FileLockEntry)
- ///
- /// # See also
- ///
- /// Documentation of `Mail::neighbors()`.
- fn get_neighbors<'a>(&self, store: &'a Store) -> Result<MailIterator<'a>> {
- self.links()
- .map(|iter| {
- iter.map(|link| link.into())
- .map(Ok)
- .into_get_iter(store)
- .into_mail_iterator()
- })
- }
-
- /// Get the full thread starting from this Mail
- ///
- /// This function recursively traverses the linked mails, assumes them all to be in the same
- /// thread and returns an iterator over all Mails it finds in this way.
- ///
- /// # Warning
- ///
- /// If a Mail is linked to this mail (even transitively!) but is _not_ in the same thread, it
- /// is considered to be in the same thread.
- ///
- /// This function works recursively. Keep that in mind for large threads. Because it needs to
- /// collect() internally, it might take a lot of memory for large threads.
- ///
- /// # Return value
- ///
- /// This function returns an Iterator over StoreIds in the same thread as this mail itself.
- /// It does not yield any qualification about the distance between a mail in this thread and
- /// this very mail.
- ///
- fn get_thread<'a>(&self, store: &'a Store) -> Result<MailIterator<'a>> {
- trace!("Getting thread, starting point at: {}", self.get_location());
- let mut thread = vec![self.get_location().clone()];
-
- fn traverse<'a>(entry: &'a Entry, thread: &mut Vec<StoreId>, store: &Store) -> Result<()> {
- // Helper function to get neighbors of a Mail, but filtered
- fn get_filtered_neighbors<'a>(entry: &'a Entry, skiplist: &[StoreId]) -> Result<Vec<StoreId>> {
- trace!("Getting filtered neighbors of {}", entry.get_location());
- entry.neighbors()?.filter_ok(|id| !skiplist.contains(id)).collect()
- }
-
- // Get the neighbors, filtered by StoreIds which are already in the thread
- // Then iterate over them
- for n in get_filtered_neighbors(entry, thread)? {
- trace!("Fetching {}", n);
-
- // Get the FileLockEntry for the StoreId, or fail if it cannot be found
- let next_entry = store.get(n.clone())?.ok_or_else(|| format_err!("Cannot find {}", n))?;
-
- // if the FileLockEntry is a Mail
- if next_entry.is_mail()? {
- trace!("{} is a Mail", n);
- thread.push(n); // it belongs to the thread
-
- // And then traverse further starting from the current Mail
- traverse(&next_entry, thread, store)?;
- }
- }
-
- Ok(())
- }
-
- trace!("Starting traversing...");
- traverse(self, &mut thread, store)?;
- trace!("Finished traversing.");
- trace!("Found {} entries in thread", thread.len());
-
- let iter = StoreIdIterator::new(Box::new(thread.into_iter().map(Ok)))
- .into_get_iter(store)
- .into_mail_iterator();
-
- Ok(iter)
- }
-
-}
-
-#[derive(Debug)]
-pub struct MailHeader<'a>(Vec<::mailparse::MailHeader<'a>>);
-
-impl<'a> From<Vec<::mailparse::MailHeader<'a>>> for MailHeader<'a> {
- fn from(mh: Vec<::mailparse::MailHeader<'a>>) -> Self {
- MailHeader(mh)
- }
-}
-
-impl<'a> MailHeader<'a> {
- /// Get a value of a single field of the mail file
- pub fn get_field(&self, field: &str) -> Result<Option<String>> {
- match self.0
- .iter()
- .filter_map(|hdr| {
- match hdr.get_key()
- .context(format_err!("Cannot get field {}", field))
- .map_err(Error::from)
- {
- Ok(key) => if key == field {
- Some(Ok(hdr))
- } else {
- None
- },
- Err(e) => Some(Err(e))
- }
- })
- .next()
- {
- None => Ok(None),
- Some(Err(e)) => Err(e),
- Some(Ok(hdr)) => Ok(Some(hdr.get_value()?))
- }
- }
-
- /// Get a value of the `From` field of the mail file
- pub fn get_from(&self) -> Result<Option<String>> {
- self.get_field("From")
+ self.is::<IsMail>().map_err(Error::from)
}
-
- /// Get a value of the `To` field of the mail file
- pub fn get_to(&self) -> Result<Option<String>> {
- self.get_field("To")
- }
-
- /// Get a value of the `Subject` field of the mail file
- pub fn get_subject(&self) -> Result<Option<String>> {
- self.get_field("Subject")
- }
-
- /// Get a value of the `Message-ID` field of the mail file
- pub fn get_message_id(&self) -> Result<Option<String>> {
- self.get_field("Message-ID")
- }
-
- /// Get a value of the `In-Reply-To` field of the mail file
- pub fn get_in_reply_to(&self) -> Result<Option<MessageId>> {
- self.get_field("In-Reply-To")
- .map(|o| o.map(crate::util::strip_message_delimiters).map(MessageId::from))
- }
-
- // TODO: Offer functionality to load and parse mail _once_ from disk, and then use helper object
- // to offer access to header fields and content.
- //
- // With the existing functionality, one has to open-parse-close the file all the time, which is
- // _NOT_ optimal.
}
diff --git a/lib/domain/libimagmail/src/mailflags.rs b/lib/domain/libimagmail/src/mailflags.rs
deleted file mode 100644
index 5fd52761..00000000
--- a/lib/domain/libimagmail/src/mailflags.rs
+++ /dev/null
@@ -1,93 +0,0 @@
-//
-// imag - the personal information management suite for the commandline
-// Copyright (C) 2015-2020 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::fmt::{Display, Result as FmtResult, Formatter};
-use std::str::FromStr;
-
-use failure::Fallible as Result;
-use failure::Error;
-
-/// Message flags
-///
-/// As defined by https://cr.yp.to/proto/maildir.html with strong typing
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub enum MailFlag {
- /// Flag "P" (passed): the user has resent/forwarded/bounced this message to someone else.
- Passed,
-
- /// Flag "R" (replied): the user has replied to this message.
- Replied,
-
- /// Flag "S" (seen): the user has viewed this message, though perhaps he didn't read all the way through it.
- Seen,
-
- /// Flag "T" (trashed): the user has moved this message to the trash; the trash will be emptied by a later user action.
- Trashed,
-
- /// Flag "D" (draft): the user considers this message a draft; toggled at user discretion.
- Draft,
-
- /// Flag "F" (flagged): user-defined flag; toggled at user discretion.
- Flagged,
-}
-
-impl MailFlag {
- pub fn as_char(self) -> char {
- match self {
- MailFlag::Passed => 'P',
- MailFlag::Replied => 'R',
- MailFlag::Seen => 'S',
- MailFlag::Trashed => 'T',
- MailFlag::Draft => 'D',
- MailFlag::Flagged =&