message/
address.rs

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            // TODO: stronger validation of local part and domain
20            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    /// If the address list is comprised of a single entry,
81    /// returns just the email domain from that entry
82    pub fn domain(&self) -> anyhow::Result<&str> {
83        let (_local, domain) = self.single_address_cracked()?;
84        Ok(domain)
85    }
86
87    /// If the address list is comprised of a single entry,
88    /// returns just the email domain from that entry
89    pub fn user(&self) -> anyhow::Result<&str> {
90        let (user, _domain) = self.single_address_cracked()?;
91        Ok(user)
92    }
93
94    /// If the address list is comprised of a single entry,
95    /// returns just the display name portion, if any
96    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    /// Flattens the groups and list and returns a simple list
107    /// of addresses
108    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}