1use config::any_err;
2use mailparsing::{Address, AddressList, EncodeHeaderValue, Mailbox};
3#[cfg(feature = "impl")]
4use mlua::{MetaMethod, UserData, UserDataFields, UserDataMethods};
5use rfc5321::{ForwardPath, ReversePath};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq)]
9#[serde(transparent)]
10pub struct EnvelopeAddress(String);
11
12impl EnvelopeAddress {
13 pub fn parse(text: &str) -> anyhow::Result<Self> {
14 if text.is_empty() {
15 Ok(Self::null_sender())
16 } else {
17 let fields: Vec<&str> = text.split('@').collect();
18 anyhow::ensure!(fields.len() == 2, "expected user@domain");
19 Ok(Self(text.to_string()))
21 }
22 }
23
24 pub fn user(&self) -> &str {
25 match self.0.find('@') {
26 Some(at) => &self.0[..at],
27 None => "",
28 }
29 }
30
31 pub fn domain(&self) -> &str {
32 match self.0.find('@') {
33 Some(at) => &self.0[at + 1..],
34 None => "",
35 }
36 }
37
38 pub fn null_sender() -> Self {
39 Self("".to_string())
40 }
41
42 pub fn to_string(&self) -> String {
43 self.0.to_string()
44 }
45}
46
47impl TryInto<ForwardPath> for EnvelopeAddress {
48 type Error = String;
49 fn try_into(self) -> Result<ForwardPath, Self::Error> {
50 ForwardPath::try_from(self.0.as_str())
51 }
52}
53
54impl TryInto<ReversePath> for EnvelopeAddress {
55 type Error = String;
56 fn try_into(self) -> Result<ReversePath, Self::Error> {
57 ReversePath::try_from(self.0.as_str())
58 }
59}
60
61#[cfg(feature = "impl")]
62impl UserData for EnvelopeAddress {
63 fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
64 fields.add_field_method_get("user", |_, this| Ok(this.user().to_string()));
65 fields.add_field_method_get("domain", |_, this| Ok(this.domain().to_string()));
66 fields.add_field_method_get("email", |_, this| Ok(this.0.to_string()));
67 }
68
69 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
70 methods.add_meta_method(MetaMethod::ToString, |_, this, _: ()| {
71 Ok(this.0.to_string())
72 });
73 }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
77pub struct HeaderAddressList(Vec<HeaderAddressEntry>);
78
79impl HeaderAddressList {
80 pub fn domain(&self) -> anyhow::Result<&str> {
83 let (_local, domain) = self.single_address_cracked()?;
84 Ok(domain)
85 }
86
87 pub fn user(&self) -> anyhow::Result<&str> {
90 let (user, _domain) = self.single_address_cracked()?;
91 Ok(user)
92 }
93
94 pub fn name(&self) -> anyhow::Result<Option<&str>> {
97 let addr = self.single_address()?;
98 Ok(addr.name.as_deref())
99 }
100
101 pub fn email(&self) -> anyhow::Result<Option<&str>> {
102 let addr = self.single_address()?;
103 Ok(addr.address.as_deref())
104 }
105
106 pub fn flatten(&self) -> Vec<&HeaderAddress> {
109 let mut res = vec![];
110 for entry in &self.0 {
111 match entry {
112 HeaderAddressEntry::Address(a) => res.push(a),
113 HeaderAddressEntry::Group(group) => {
114 for addr in &group.addresses {
115 res.push(addr);
116 }
117 }
118 }
119 }
120 res
121 }
122
123 pub fn single_address_cracked(&self) -> anyhow::Result<(&str, &str)> {
124 let addr = self.single_address_string()?;
125 let tuple = addr
126 .split_once('@')
127 .ok_or_else(|| anyhow::anyhow!("no @ in address"))?;
128 Ok(tuple)
129 }
130
131 pub fn single_address_string(&self) -> anyhow::Result<&str> {
132 let addr = self.single_address()?;
133 match &addr.address {
134 None => anyhow::bail!("no address"),
135 Some(addr) => Ok(addr),
136 }
137 }
138
139 pub fn single_address(&self) -> anyhow::Result<&HeaderAddress> {
140 match self.0.len() {
141 0 => anyhow::bail!("no addresses"),
142 1 => match &self.0[0] {
143 HeaderAddressEntry::Address(a) => Ok(a),
144 _ => anyhow::bail!("is not a simple address"),
145 },
146 _ => anyhow::bail!("is not a simple single address"),
147 }
148 }
149}
150
151impl From<AddressList> for HeaderAddressList {
152 fn from(input: AddressList) -> HeaderAddressList {
153 let addresses: Vec<HeaderAddressEntry> = input.0.iter().map(Into::into).collect();
154 HeaderAddressList(addresses)
155 }
156}
157
158#[cfg(feature = "impl")]
159impl UserData for HeaderAddressList {
160 fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
161 fields.add_field_method_get("user", |_, this| {
162 Ok(this.user().map_err(any_err)?.to_string())
163 });
164 fields.add_field_method_get("domain", |_, this| {
165 Ok(this.domain().map_err(any_err)?.to_string())
166 });
167 fields.add_field_method_get("email", |_, this| {
168 Ok(this.email().map_err(any_err)?.map(|s| s.to_string()))
169 });
170 fields.add_field_method_get("name", |_, this| {
171 Ok(this.name().map_err(any_err)?.map(|s| s.to_string()))
172 });
173 fields.add_field_method_get("list", |_, this| {
174 Ok(this
175 .flatten()
176 .into_iter()
177 .cloned()
178 .collect::<Vec<HeaderAddress>>())
179 });
180 }
181
182 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
183 methods.add_meta_method(MetaMethod::ToString, |_, this, _: ()| {
184 let json = serde_json::to_string(&this.0).map_err(any_err)?;
185 Ok(json)
186 });
187 }
188}
189
190#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
191pub enum HeaderAddressEntry {
192 Address(HeaderAddress),
193 Group(AddressGroup),
194}
195
196impl From<&Address> for HeaderAddressEntry {
197 fn from(addr: &Address) -> HeaderAddressEntry {
198 match addr {
199 Address::Mailbox(mbox) => HeaderAddressEntry::Address(mbox.into()),
200 Address::Group { name, entries } => {
201 let addresses = entries.0.iter().map(Into::into).collect();
202 HeaderAddressEntry::Group(AddressGroup {
203 name: if name.is_empty() {
204 None
205 } else {
206 Some(name.to_string())
207 },
208 addresses,
209 })
210 }
211 }
212 }
213}
214
215#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
216pub struct HeaderAddress {
217 pub name: Option<String>,
218 pub address: Option<String>,
219}
220
221impl From<&Mailbox> for HeaderAddress {
222 fn from(mbox: &Mailbox) -> HeaderAddress {
223 Self {
224 name: mbox.name.clone(),
225 address: Some(mbox.address.encode_value().to_string()),
226 }
227 }
228}
229
230impl HeaderAddress {
231 pub fn user(&self) -> anyhow::Result<&str> {
232 let (user, _domain) = self.crack_address().map_err(any_err)?;
233 Ok(user)
234 }
235 pub fn domain(&self) -> anyhow::Result<&str> {
236 let (_user, domain) = self.crack_address().map_err(any_err)?;
237 Ok(domain)
238 }
239 pub fn email(&self) -> Option<&str> {
240 self.address.as_deref()
241 }
242 pub fn name(&self) -> Option<&str> {
243 self.name.as_deref()
244 }
245
246 pub fn crack_address(&self) -> anyhow::Result<(&str, &str)> {
247 let address = self
248 .address
249 .as_ref()
250 .ok_or_else(|| anyhow::anyhow!("no address"))?;
251
252 address
253 .split_once('@')
254 .ok_or_else(|| anyhow::anyhow!("no @ in address"))
255 }
256}
257
258#[cfg(feature = "impl")]
259impl UserData for HeaderAddress {
260 fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
261 fields.add_field_method_get("user", |_, this| {
262 Ok(this.user().map_err(any_err)?.to_string())
263 });
264 fields.add_field_method_get("domain", |_, this| {
265 Ok(this.domain().map_err(any_err)?.to_string())
266 });
267 fields.add_field_method_get("email", |_, this| Ok(this.email().map(|s| s.to_string())));
268 fields.add_field_method_get("name", |_, this| Ok(this.name().map(|s| s.to_string())));
269 }
270 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
271 methods.add_meta_method(MetaMethod::ToString, |_, this, _: ()| {
272 let json = serde_json::to_string(&this).map_err(any_err)?;
273 Ok(json)
274 });
275 }
276}
277
278#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
279pub struct AddressGroup {
280 pub name: Option<String>,
281 pub addresses: Vec<HeaderAddress>,
282}