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