mod_mimepart/
headers.rs

1use crate::PartRef;
2use bstr::BString;
3use config::{SerdeWrappedValue, any_err};
4use mailparsing::{
5    AddressList, Header, HeaderMap, MailParsingError, Mailbox, MailboxList, MessageID,
6    MimeParameters,
7};
8use mlua::{
9    IntoLua, Lua, MetaMethod, MultiValue, UserData, UserDataFields, UserDataMethods, Value,
10};
11use serde::{Deserialize, Serialize};
12use std::collections::BTreeMap;
13
14#[derive(Clone)]
15pub struct HeaderMapRef(PartRef);
16
17impl HeaderMapRef {
18    pub fn new(part: PartRef) -> Self {
19        Self(part)
20    }
21}
22
23struct HeaderIterInner {
24    part: PartRef,
25    idx: usize,
26    name: Option<mlua::String>,
27}
28
29impl UserData for HeaderMapRef {
30    fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
31        methods.add_meta_method(MetaMethod::ToString, move |lua, this, ()| {
32            let part = this.0.resolve().map_err(any_err)?;
33            let headers = part.headers();
34            let mut out = vec![];
35            for hdr in headers.iter() {
36                hdr.write_header(&mut out).map_err(any_err)?;
37            }
38            lua.create_string(&out)
39        });
40
41        methods.add_method(
42            "append",
43            |_lua, this, (name, value): (String, mlua::String)| {
44                this.0
45                    .mutate(|part| {
46                        part.headers_mut()
47                            .append_header(&name, BString::new(value.as_bytes().to_vec()));
48                        Ok(())
49                    })
50                    .map_err(any_err)
51            },
52        );
53
54        methods.add_method(
55            "prepend",
56            |_lua, this, (name, value): (String, mlua::String)| {
57                this.0
58                    .mutate(|part| {
59                        part.headers_mut()
60                            .prepend(&name, BString::new(value.as_bytes().to_vec()));
61                        Ok(())
62                    })
63                    .map_err(any_err)
64            },
65        );
66
67        methods.add_method("remove_all_named", |_lua, this, name: String| {
68            this.0
69                .mutate(|part| {
70                    part.headers_mut().remove_all_named(&name);
71                    Ok(())
72                })
73                .map_err(any_err)
74        });
75
76        methods.add_method("get_first_named", |_lua, this, name: String| {
77            let part = this.0.resolve().map_err(any_err)?;
78            Ok(part
79                .headers()
80                .get_first(&name)
81                .map(|hdr| HeaderWrap(hdr.to_owned())))
82        });
83
84        methods.add_method("iter", |lua, this, name: Option<mlua::String>| {
85            let mut iter = HeaderIterInner {
86                part: this.0.clone(),
87                idx: 0,
88                name,
89            };
90
91            let iter_func =
92                lua.create_function_mut(move |lua: &Lua, (_state, _idx): (Value, Value)| {
93                    let part = iter.part.resolve().map_err(any_err)?;
94                    let headers = part.headers();
95                    let mut result = vec![];
96
97                    while let Some(hdr) = headers.get(iter.idx) {
98                        iter.idx += 1;
99
100                        let matched = iter
101                            .name
102                            .as_ref()
103                            .map(|name| {
104                                hdr.get_name()
105                                    .eq_ignore_ascii_case(name.as_bytes().as_ref())
106                            })
107                            .unwrap_or(true);
108
109                        if matched {
110                            result.push(HeaderWrap(hdr.to_owned()).into_lua(lua)?);
111                            break;
112                        }
113                    }
114
115                    Ok(MultiValue::from_vec(result))
116                })?;
117
118            Ok(iter_func)
119        });
120
121        fn getter<T, GET>(
122            get: GET,
123        ) -> impl Fn(&Lua, &HeaderMapRef, ()) -> mlua::Result<Option<SerdeWrappedValue<T>>>
124        where
125            T: serde::de::DeserializeOwned,
126            GET: Fn(&HeaderMap) -> Result<Option<T>, MailParsingError>,
127        {
128            move |_lua, this, ()| {
129                let part = this.0.resolve().map_err(any_err)?;
130                let value = (get)(part.headers())
131                    .map_err(any_err)?
132                    .map(SerdeWrappedValue);
133                Ok(value)
134            }
135        }
136
137        fn setter_unstructured<APPLY>(
138            apply: APPLY,
139        ) -> impl Fn(&Lua, &HeaderMapRef, mlua::String) -> mlua::Result<()>
140        where
141            APPLY: Fn(&mut HeaderMap, &[u8]) -> Result<(), MailParsingError>,
142        {
143            move |_lua, this, value: mlua::String| {
144                this.0
145                    .mutate(|part| Ok(apply(part.headers_mut(), &value.as_bytes())?))
146                    .map_err(any_err)
147            }
148        }
149
150        fn getter_unstructured<GET>(
151            get: GET,
152        ) -> impl Fn(&Lua, &HeaderMapRef, ()) -> mlua::Result<Option<BString>>
153        where
154            GET: Fn(&HeaderMap) -> Result<Option<BString>, MailParsingError>,
155        {
156            move |_lua, this, ()| {
157                let part = this.0.resolve().map_err(any_err)?;
158                let value = (get)(part.headers()).map_err(any_err)?;
159                Ok(value)
160            }
161        }
162
163        /// Helper to ensure that we picked a reasonably typed accessor
164        fn assert_type<T>(
165            value: Result<Option<T>, MailParsingError>,
166        ) -> Result<Option<T>, MailParsingError> {
167            value
168        }
169
170        macro_rules! accessor {
171            ($get_name:literal, $getter:ident, $set_name:literal, $setter:ident, $ty:path) => {
172                methods.add_method(
173                    $get_name,
174                    getter(|headers| assert_type::<$ty>(headers.$getter())),
175                );
176                methods.add_method($set_name, |lua, this, value: mlua::Value| match value {
177                    Value::String(s) => {
178                        let s = s.to_str()?;
179                        this.0
180                            .mutate(|part| {
181                                part.headers_mut().$setter(&*s)?;
182                                Ok(())
183                            })
184                            .map_err(any_err)
185                    }
186                    value => {
187                        let typed = config::from_lua_value::<$ty>(lua, value)?;
188                        this.0
189                            .mutate(|part| {
190                                part.headers_mut().$setter(typed)?;
191                                Ok(())
192                            })
193                            .map_err(any_err)
194                    }
195                });
196            };
197
198            ($get_name:literal, $getter:ident, $set_name:literal, $setter:ident, $ty:path, $via_ty:path) => {
199                methods.add_method(
200                    $get_name,
201                    getter(|headers| headers.$getter().map(|opt| opt.map(<$ty>::from))),
202                );
203                methods.add_method($set_name, |lua, this, value: mlua::Value| match value {
204                    Value::String(s) => {
205                        let s = s.to_str()?;
206                        this.0
207                            .mutate(|part| {
208                                part.headers_mut().$setter(&*s)?;
209                                Ok(())
210                            })
211                            .map_err(any_err)
212                    }
213                    value => {
214                        let typed: $via_ty = config::from_lua_value::<$ty>(lua, value)?.into();
215                        this.0
216                            .mutate(|part| {
217                                part.headers_mut().$setter(typed)?;
218                                Ok(())
219                            })
220                            .map_err(any_err)
221                    }
222                });
223            };
224        }
225
226        macro_rules! unstructured {
227            ($get_name:literal, $getter:ident, $set_name:literal, $setter:ident) => {
228                methods.add_method($get_name, getter_unstructured(|headers| headers.$getter()));
229                methods.add_method(
230                    $set_name,
231                    setter_unstructured(|headers, value| headers.$setter(value)),
232                );
233            };
234        }
235
236        accessor!("from", from, "set_from", set_from, MailboxList);
237        accessor!(
238            "resent_from",
239            resent_from,
240            "set_resent_from",
241            set_from,
242            MailboxList
243        );
244        accessor!("to", to, "set_to", set_to, AddressList);
245        accessor!(
246            "reply_to",
247            reply_to,
248            "set_reply_to",
249            set_reply_to,
250            AddressList
251        );
252        accessor!("cc", cc, "set_cc", set_cc, AddressList);
253        accessor!("bcc", bcc, "set_bcc", set_bcc, AddressList);
254        accessor!(
255            "resent_to",
256            resent_to,
257            "set_resent_to",
258            set_resent_to,
259            AddressList
260        );
261        accessor!(
262            "resent_cc",
263            resent_cc,
264            "set_resent_cc",
265            set_resent_cc,
266            AddressList
267        );
268        accessor!(
269            "resent_bcc",
270            resent_bcc,
271            "set_resent_bcc",
272            set_resent_bcc,
273            AddressList
274        );
275
276        // accessor!("date", date, "set_date", set_date, DateTime<FixedOffset>); FIXME: establish lua time type
277
278        accessor!("sender", sender, "set_sender", set_sender, Mailbox);
279        accessor!(
280            "resent_sender",
281            resent_sender,
282            "set_resent_sender",
283            set_resent_sender,
284            Mailbox
285        );
286
287        accessor!(
288            "message_id",
289            message_id,
290            "set_message_id",
291            set_message_id,
292            MessageID
293        );
294        accessor!(
295            "content_id",
296            content_id,
297            "set_content_id",
298            set_content_id,
299            MessageID
300        );
301        accessor!(
302            "references",
303            references,
304            "set_references",
305            set_references,
306            Vec<MessageID>
307        );
308
309        unstructured!("subject", subject, "set_subject", set_subject);
310        unstructured!("comments", comments, "set_comments", set_comments);
311        unstructured!(
312            "mime_version",
313            mime_version,
314            "set_mime_version",
315            set_mime_version
316        );
317
318        accessor!(
319            "content_transfer_encoding",
320            content_transfer_encoding,
321            "set_content_transfer_encoding",
322            set_content_transfer_encoding,
323            MimeParams,
324            MimeParameters
325        );
326        accessor!(
327            "content_disposition",
328            content_disposition,
329            "set_content_disposition",
330            set_content_disposition,
331            MimeParams,
332            MimeParameters
333        );
334        accessor!(
335            "content_type",
336            content_type,
337            "set_content_type",
338            set_content_type,
339            MimeParams,
340            MimeParameters
341        );
342    }
343}
344
345/// A fully-decoded representation of the underlying MimeParameters value,
346/// to make it more convenient to inspect from lua
347#[derive(Clone, Serialize, Deserialize)]
348#[serde(deny_unknown_fields)]
349pub struct MimeParams {
350    pub value: BString,
351    pub parameters: BTreeMap<BString, BString>,
352}
353
354impl From<MimeParameters> for MimeParams {
355    fn from(params: MimeParameters) -> MimeParams {
356        let map = params.parameter_map();
357
358        MimeParams {
359            value: params.value,
360            parameters: map,
361        }
362    }
363}
364
365impl From<MimeParams> for MimeParameters {
366    fn from(params: MimeParams) -> MimeParameters {
367        let mut result = MimeParameters::new(&params.value);
368        for (name, value) in params.parameters {
369            result.set(&name, &value);
370        }
371        result
372    }
373}
374
375#[derive(Clone)]
376pub struct HeaderWrap(Header<'static>);
377
378fn get_mailbox_list(lua: &Lua, header: &Header) -> mlua::Result<mlua::Value> {
379    SerdeWrappedValue(header.as_mailbox_list().map_err(any_err)?).to_lua_value(lua)
380}
381fn get_address_list(lua: &Lua, header: &Header) -> mlua::Result<mlua::Value> {
382    SerdeWrappedValue(header.as_address_list().map_err(any_err)?).to_lua_value(lua)
383}
384fn get_mailbox(lua: &Lua, header: &Header) -> mlua::Result<mlua::Value> {
385    SerdeWrappedValue(header.as_mailbox().map_err(any_err)?).to_lua_value(lua)
386}
387fn get_message_id(lua: &Lua, header: &Header) -> mlua::Result<mlua::Value> {
388    SerdeWrappedValue(header.as_message_id().map_err(any_err)?).to_lua_value(lua)
389}
390fn get_content_id(lua: &Lua, header: &Header) -> mlua::Result<mlua::Value> {
391    SerdeWrappedValue(header.as_content_id().map_err(any_err)?).to_lua_value(lua)
392}
393fn get_message_id_list(lua: &Lua, header: &Header) -> mlua::Result<mlua::Value> {
394    SerdeWrappedValue(header.as_message_id_list().map_err(any_err)?).to_lua_value(lua)
395}
396fn get_unstructured(lua: &Lua, header: &Header) -> mlua::Result<mlua::Value> {
397    SerdeWrappedValue(header.as_unstructured().map_err(any_err)?).to_lua_value(lua)
398}
399fn get_content_transfer_encoding(lua: &Lua, header: &Header) -> mlua::Result<mlua::Value> {
400    let params: MimeParams = header
401        .as_content_transfer_encoding()
402        .map_err(any_err)?
403        .into();
404    SerdeWrappedValue(params).to_lua_value(lua)
405}
406fn get_content_disposition(lua: &Lua, header: &Header) -> mlua::Result<mlua::Value> {
407    let params: MimeParams = header.as_content_disposition().map_err(any_err)?.into();
408    SerdeWrappedValue(params).to_lua_value(lua)
409}
410fn get_content_type(lua: &Lua, header: &Header) -> mlua::Result<mlua::Value> {
411    let params: MimeParams = header.as_content_type().map_err(any_err)?.into();
412    SerdeWrappedValue(params).to_lua_value(lua)
413}
414fn get_authentication_results(lua: &Lua, header: &Header) -> mlua::Result<mlua::Value> {
415    SerdeWrappedValue(header.as_authentication_results().map_err(any_err)?).to_lua_value(lua)
416}
417
418const NAME_GETTER: &[(&str, fn(&Lua, &Header) -> mlua::Result<mlua::Value>)] = &[
419    ("From", get_mailbox_list),
420    ("Reply-To", get_address_list),
421    ("To", get_address_list),
422    ("Cc", get_address_list),
423    ("Bcc", get_address_list),
424    ("Message-ID", get_message_id),
425    ("Subject", get_unstructured),
426    ("Mime-Version", get_unstructured),
427    ("Content-Transfer-Encoding", get_content_transfer_encoding),
428    ("Content-Type", get_content_type),
429    ("Content-Disposition", get_content_disposition),
430    ("Authentication-Results", get_authentication_results),
431    ("Resent-From", get_mailbox_list),
432    ("Resent-To", get_address_list),
433    ("Resent-Cc", get_address_list),
434    ("Resent-Bcc", get_address_list),
435    ("Resent-Sender", get_mailbox),
436    ("Sender", get_mailbox),
437    ("Content-ID", get_content_id),
438    ("References", get_message_id_list),
439    ("Comments", get_unstructured),
440];
441
442impl UserData for HeaderWrap {
443    fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
444        methods.add_meta_method(MetaMethod::ToString, |_lua, this, ()| {
445            Ok(this.0.to_header_string())
446        });
447    }
448
449    fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
450        fields.add_field_method_get("name", |lua, this| lua.create_string(this.0.get_name()));
451
452        fields.add_field_method_get("value", |lua, this| {
453            let name = this.0.get_name();
454            for (candidate, getter) in NAME_GETTER {
455                if candidate.as_bytes().eq_ignore_ascii_case(name) {
456                    return (getter)(lua, &this.0);
457                }
458            }
459            get_unstructured(lua, &this.0)
460        });
461
462        fields.add_field_method_get("raw_value", |lua, this| {
463            lua.create_string(this.0.get_raw_value())
464        });
465        fields.add_field_method_get("unstructured", |_lua, this| {
466            this.0.as_unstructured().map_err(any_err)
467        });
468        fields.add_field_method_get("mailbox_list", |_lua, this| {
469            Ok(SerdeWrappedValue(
470                this.0.as_mailbox_list().map_err(any_err)?,
471            ))
472        });
473        fields.add_field_method_get("address_list", |_lua, this| {
474            Ok(SerdeWrappedValue(
475                this.0.as_address_list().map_err(any_err)?,
476            ))
477        });
478        fields.add_field_method_get("message_id", |_lua, this| {
479            Ok(SerdeWrappedValue(this.0.as_message_id().map_err(any_err)?))
480        });
481        fields.add_field_method_get("message_id_list", |_lua, this| {
482            Ok(SerdeWrappedValue(
483                this.0.as_message_id_list().map_err(any_err)?,
484            ))
485        });
486        fields.add_field_method_get("mime_params", |_lua, this| {
487            Ok(SerdeWrappedValue(MimeParams::from(
488                this.0.as_content_transfer_encoding().map_err(any_err)?,
489            )))
490        });
491        fields.add_field_method_get("authentication_results", |_lua, this| {
492            Ok(SerdeWrappedValue(
493                this.0.as_authentication_results().map_err(any_err)?,
494            ))
495        });
496    }
497}