1use failure::Fallible as Result;
21use failure::ResultExt;
22use failure::Error;
23
24use libimagstore::store::Entry;
25use libimagentryutil::isa::Is;
26use libimagentryutil::isa::IsKindHeaderPathProvider;
27use libimagentryref::reference::Config as RefConfig;
28use libimagentryref::reference::{Ref, RefFassade};
29
30provide_kindflag_path!(pub IsMail, "mail.is_mail");
31
32pub trait Mail : RefFassade {
33 fn is_mail(&self) -> Result<bool>;
34 fn get_field(&self, refconfig: &RefConfig, field: &str) -> Result<Option<String>>;
35 fn get_from(&self, refconfig: &RefConfig) -> Result<Option<String>>;
36 fn get_to(&self, refconfig: &RefConfig) -> Result<Option<String>>;
37 fn get_subject(&self, refconfig: &RefConfig) -> Result<Option<String>>;
38 fn get_message_id(&self, refconfig: &RefConfig) -> Result<Option<String>>;
39 fn get_in_reply_to(&self, refconfig: &RefConfig) -> Result<Option<String>>;
40}
41
42impl Mail for Entry {
43
44 fn is_mail(&self) -> Result<bool> {
45 self.is::<IsMail>()
46 }
47
48 fn get_field(&self, refconfig: &RefConfig, field: &str) -> Result<Option<String>> {
50 use std::fs::read_to_string;
51 use crate::hasher::MailHasher;
52
53 debug!("Getting field in mail: {:?}", field);
54 let mail_file_location = self.as_ref_with_hasher::<MailHasher>().get_path(refconfig)?;
55
56 match ::mailparse::parse_mail(read_to_string(mail_file_location.as_path())?.as_bytes())
57 .context(format_err!("Cannot parse Email {}", mail_file_location.display()))?
58 .headers
59 .into_iter()
60 .filter_map(|hdr| {
61 match hdr.get_key()
62 .context(format_err!("Cannot fetch key '{}' from Email {}", field, mail_file_location.display()))
63 .map_err(Error::from)
64 {
65 Ok(k) => if k == field {
66 Some(Ok(hdr))
67 } else {
68 None
69 },
70 Err(e) => Some(Err(e)),
71 }
72 })
73 .next()
74 {
75 None => Ok(None),
76 Some(Err(e)) => Err(e),
77 Some(Ok(hdr)) => Ok(Some(hdr.get_value()?))
78 }
79 }
80
81 fn get_from(&self, refconfig: &RefConfig) -> Result<Option<String>> {
87 self.get_field(refconfig, "From")
88 }
89
90 fn get_to(&self, refconfig: &RefConfig) -> Result<Option<String>> {
96 self.get_field(refconfig, "To")
97 }
98
99 fn get_subject(&self, refconfig: &RefConfig) -> Result<Option<String>> {
105 self.get_field(refconfig, "Subject")
106 }
107
108 fn get_message_id(&self, refconfig: &RefConfig) -> Result<Option<String>> {
114 self.get_field(refconfig, "Message-ID")
115 .map(|o| o.map(crate::util::strip_message_delimiters))
116 }
117
118 fn get_in_reply_to(&self, refconfig: &RefConfig) -> Result<Option<String>> {
124 self.get_field(refconfig, "In-Reply-To")
125 }
126
127}
128
129#[derive(Debug)]
130pub struct MailHeader<'a>(Vec<::mailparse::MailHeader<'a>>);
131
132impl<'a> From<Vec<::mailparse::MailHeader<'a>>> for MailHeader<'a> {
133 fn from(mh: Vec<::mailparse::MailHeader<'a>>) -> Self {
134 MailHeader(mh)
135 }
136}
137
138impl<'a> MailHeader<'a> {
139 pub fn get_field(&self, field: &str) -> Result<Option<String>> {
141 match self.0
142 .iter()
143 .filter_map(|hdr| {
144 match hdr.get_key()
145 .context(format_err!("Cannot get field {}", field))
146 .map_err(Error::from)
147 {
148 Ok(key) => if key == field {
149 Some(Ok(hdr))
150 } else {
151 None
152 },
153 Err(e) => Some(Err(e))
154 }
155 })
156 .next()
157 {
158 None => Ok(None),
159 Some(Err(e)) => Err(e),
160 Some(Ok(hdr)) => Ok(Some(hdr.get_value()?))
161 }
162 }
163
164 pub fn get_from(&self) -> Result<Option<String>> {
166 self.get_field("From")
167 }
168
169 pub fn get_to(&self) -> Result<Option<String>> {
171 self.get_field("To")
172 }
173
174 pub fn get_subject(&self) -> Result<Option<String>> {
176 self.get_field("Subject")
177 }
178
179 pub fn get_message_id(&self) -> Result<Option<String>> {
181 self.get_field("Message-ID")
182 }
183
184 pub fn get_in_reply_to(&self) -> Result<Option<String>> {
186 self.get_field("In-Reply-To")
187 }
188
189 }