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