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 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!("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#[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(¶ms.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}