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