1 /* Operations on RFC-2231-compliant mail headers fields.
2 GNU Mailutils -- a suite of utilities for electronic mail
3 Copyright (C) 1999-2021 Free Software Foundation, Inc.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General
16 Public License along with this library. If not,
17 see <http://www.gnu.org/licenses/>. */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <mailutils/cctype.h>
28 #include <mailutils/cstr.h>
29 #include <mailutils/errno.h>
30 #include <mailutils/message.h>
31 #include <mailutils/header.h>
32 #include <mailutils/stream.h>
33 #include <mailutils/mime.h>
34 #include <mailutils/filter.h>
35 #include <mailutils/util.h>
36 #include <mailutils/wordsplit.h>
37 #include <mailutils/assoc.h>
38 #include <mailutils/iterator.h>
39 #include <mailutils/diag.h>
40 #include <mailutils/nls.h>
41
42 #define MU_MIMEHDR_MULTILINE 0x01 /* Parameter was multiline */
43 #define MU_MIMEHDR_CSINFO 0x02 /* Parameter contains charset/language
44 info */
45
46 /* Free the memory allocated for mu_mime_param. */
47 void
mu_mime_param_free(struct mu_mime_param * p)48 mu_mime_param_free (struct mu_mime_param *p)
49 {
50 if (p)
51 {
52 free (p->lang);
53 free (p->cset);
54 free (p->value);
55 free (p);
56 }
57 }
58
59 /* Treat ITEM as a pointer to struct mu_mime_param and reclaim all
60 memory associated with it.
61
62 This is intended for use as a destroy_item method of assoc tables. */
63 static void
_mu_mime_param_free_item(void * item)64 _mu_mime_param_free_item (void *item)
65 {
66 mu_mime_param_free (item);
67 }
68
69 /* Recode a string between two charsets.
70
71 Input:
72 TEXT - A string.
73 ICS - Charset of TEXT.
74 OCS - Charset to convert TEXT to.
75 Output:
76 PRESULT - On success, the pointer to the resulting string is stored here.
77 */
78 static int
_recode_string(char * text,const char * ics,const char * ocs,char ** presult)79 _recode_string (char *text, const char *ics, const char *ocs, char **presult)
80 {
81 mu_stream_t istr, ostr, cvt;
82 mu_off_t size;
83 char *decoded;
84 int rc;
85
86 rc = mu_static_memory_stream_create (&istr, text, strlen (text));
87 if (rc)
88 return rc;
89 rc = mu_memory_stream_create (&ostr, 0);
90 if (rc)
91 return rc;
92 rc = mu_decode_filter (&cvt, istr, NULL, ics, ocs);
93 mu_stream_unref (istr);
94 if (rc)
95 {
96 mu_stream_unref (ostr);
97 return rc;
98 }
99 rc = mu_stream_copy (ostr, cvt, 0, &size);
100 mu_stream_unref (cvt);
101 if (rc)
102 {
103 mu_stream_unref (ostr);
104 return rc;
105 }
106
107 decoded = malloc (size + 1);
108 if (!decoded)
109 {
110 mu_stream_unref (ostr);
111 return ENOMEM;
112 }
113
114 mu_stream_seek (ostr, 0, MU_SEEK_SET, NULL);
115 rc = mu_stream_read (ostr, decoded, size, NULL);
116 mu_stream_unref (ostr);
117 if (rc)
118 free (decoded);
119 else
120 {
121 decoded[size] = 0;
122 *presult = decoded;
123 }
124 return rc;
125 }
126
127 /* Structure for composing continued parameters.
128 See RFC 2231, Section 3, "Parameter Value Continuations" */
129 struct param_continuation
130 {
131 char *param_name; /* Parameter name */
132 size_t param_length; /* Length of param_name */
133 mu_stream_t param_value; /* Its value (memory stream) */
134 int param_cind; /* Expected continued parameter index. */
135 /* Language/character set information */
136 const char *param_lang;
137 const char *param_cset;
138 };
139
140 static void
free_param_continuation(struct param_continuation * p)141 free_param_continuation (struct param_continuation *p)
142 {
143 free (p->param_name);
144 mu_stream_destroy (&p->param_value);
145 /* param_lang and param_cset are handled separately */
146 memset (p, 0, sizeof (*p));
147 }
148
149 /* Auxiliary function to store the data collected in CONT into ASSOC.
150 If SUBSET is True, ASSOC is populated with empty mu_mime_param
151 structures. In this case data will be stored only if CONT->param_name
152 is already in ASSOC. If OUTCHARSET is not NULL, the value from
153 CONT->param_value will be recoded to that charset before storing it. */
154 static int
flush_param(struct param_continuation * cont,mu_assoc_t assoc,const char * outcharset)155 flush_param (struct param_continuation *cont, mu_assoc_t assoc,
156 const char *outcharset)
157 {
158 int rc;
159 struct mu_mime_param *param;
160 mu_off_t size;
161
162 param = calloc (1, sizeof *param);
163 if (!param)
164 return errno;
165
166 if (cont->param_lang)
167 {
168 param->lang = strdup (cont->param_lang);
169 if (!param->lang)
170 {
171 mu_mime_param_free (param);
172 return ENOMEM;
173 }
174 }
175 else
176 param->lang = NULL;
177
178 if (outcharset || cont->param_cset)
179 {
180 param->cset = strdup (outcharset ? outcharset : cont->param_cset);
181 if (!param->cset)
182 {
183 mu_mime_param_free (param);
184 return ENOMEM;
185 }
186 }
187
188 rc = mu_stream_size (cont->param_value, &size);
189 if (rc == 0)
190 {
191 param->value = malloc (size + 1);
192 if (!param->value)
193 {
194 mu_mime_param_free (param);
195 rc = ENOMEM;
196 }
197 }
198
199 if (rc == 0)
200 {
201 rc = mu_stream_seek (cont->param_value, 0, MU_SEEK_SET, NULL);
202 if (rc == 0)
203 rc = mu_stream_read (cont->param_value, param->value, size, NULL);
204 param->value[size] = 0;
205 }
206
207 if (rc)
208 {
209 mu_mime_param_free (param);
210 return rc;
211 }
212
213 if (cont->param_cset && outcharset &&
214 mu_c_strcasecmp (cont->param_cset, outcharset))
215 {
216 char *tmp;
217 rc = _recode_string (param->value, cont->param_cset, outcharset, &tmp);
218 free (param->value);
219 if (rc)
220 {
221 mu_mime_param_free (param);
222 return rc;
223 }
224 param->value = tmp;
225 }
226
227 rc = mu_assoc_install (assoc, cont->param_name, param);
228 if (rc)
229 mu_mime_param_free (param);
230
231 return rc;
232 }
233
234 /* Create and initialize an empty associative array for parameters. */
235 int
mu_mime_param_assoc_create(mu_assoc_t * paramtab)236 mu_mime_param_assoc_create (mu_assoc_t *paramtab)
237 {
238 mu_assoc_t assoc;
239 int rc = mu_assoc_create (&assoc, MU_ASSOC_ICASE);
240 if (rc == 0)
241 mu_assoc_set_destroy_item (assoc, _mu_mime_param_free_item);
242 *paramtab = assoc;
243 return rc;
244 }
245
246 /* Add an empty structure for the slot NAME in ASSOC. */
247 int
mu_mime_param_assoc_add(mu_assoc_t assoc,const char * name)248 mu_mime_param_assoc_add (mu_assoc_t assoc, const char *name)
249 {
250 return mu_assoc_install (assoc, name, NULL);
251 }
252
253 static inline char *
getword(struct mu_wordsplit * ws,size_t * pi)254 getword (struct mu_wordsplit *ws, size_t *pi)
255 {
256 if (*pi == ws->ws_wordc)
257 return NULL;
258 return ws->ws_wordv[(*pi)++];
259 }
260
261 static int
parse_param(struct mu_wordsplit * ws,size_t * pi,mu_assoc_t assoc,struct param_continuation * param_cont,const char * outcharset)262 parse_param (struct mu_wordsplit *ws, size_t *pi, mu_assoc_t assoc,
263 struct param_continuation *param_cont,
264 const char *outcharset)
265 {
266 size_t klen;
267 char *key;
268 char *val;
269 const char *lang = NULL;
270 const char *cset = NULL;
271 char *langp = NULL;
272 char *csetp = NULL;
273 char *p;
274 char *decoded;
275 int flags = 0;
276 struct mu_mime_param *param;
277 int rc;
278
279 key = getword (ws, pi);
280 if (key == NULL)
281 return MU_ERR_USER0;
282
283 if (strcmp (key, ";") == 0)
284 {
285 mu_assoc_tail_set_mark (assoc, 0);
286 /* Reportedly, some MUAs insert several semicolons */
287 do
288 {
289 key = getword (ws, pi);
290 if (key == NULL)
291 return MU_ERR_USER0;
292 }
293 while (strcmp (key, ";") == 0);
294 }
295 else
296 {
297 mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0,
298 (_("semicolon missing (found %s)"), key));
299 return MU_ERR_PARSE;
300 }
301
302 p = strchr (key, '=');
303 if (p)
304 {
305 *p++ = 0;
306 if (*p)
307 {
308 /* key=val */
309 val = p;
310 }
311 else if ((val = getword (ws, pi)) == NULL)
312 {
313 mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0,
314 (_("missing parameter value")));
315 return MU_ERR_PARSE;
316 }
317 /* key= WSP val */
318 }
319 else
320 {
321 p = getword (ws, pi);
322 if (p && p[0] == '=')
323 {
324 if (p[1])
325 {
326 /* key WSP =val */
327 val = p + 1;
328 }
329 else if ((val = getword (ws, pi)) == NULL)
330 {
331 mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0,
332 (_("missing parameter value")));
333 return MU_ERR_PARSE;
334 }
335 /* key WSP = WSP val */
336 }
337 else
338 {
339 mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0,
340 (_("missing = after parameter name")));
341 return MU_ERR_PARSE;
342 }
343 }
344
345 klen = strlen (key);
346 if (klen == 0)
347 /* Ignore empty parameter */
348 return 0;
349
350 p = strchr (key, '*');
351 if (p)
352 {
353 /* It is a parameter value continuation (RFC 2231, Section 3)
354 or parameter value character set and language information
355 (ibid., Section 4). */
356 klen = p - key;
357 if (p[1])
358 {
359 if (mu_isdigit (p[1]))
360 {
361 char *q;
362 unsigned long n = strtoul (p + 1, &q, 10);
363
364 if (*q && *q != '*')
365 {
366 mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0,
367 (_("malformed parameter name %s"),
368 key));
369 return MU_ERR_PARSE;
370 }
371
372 if (n != param_cont->param_cind)
373 {
374 mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0,
375 (_("continuation index out of sequence in %s: "
376 "skipping"),
377 key));
378 return MU_ERR_PARSE;
379 }
380
381 if (n == 0)
382 {
383 param_cont->param_name = malloc (klen + 1);
384 if (!param_cont->param_name)
385 return ENOMEM;
386 param_cont->param_length = klen;
387 memcpy (param_cont->param_name, key, klen);
388 param_cont->param_name[klen] = 0;
389
390 rc = mu_memory_stream_create (¶m_cont->param_value,
391 MU_STREAM_RDWR);
392 if (rc)
393 return rc;
394 }
395 else if (param_cont->param_length != klen ||
396 memcmp (param_cont->param_name, key, klen))
397 {
398 mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0,
399 (_("continuation name mismatch: %s"), key));
400 return MU_ERR_PARSE;
401 }
402
403 if (*q == '*')
404 flags |= MU_MIMEHDR_CSINFO;
405
406 param_cont->param_cind++;
407 flags |= MU_MIMEHDR_MULTILINE;
408 }
409 }
410 else
411 {
412 flags |= MU_MIMEHDR_CSINFO;
413 *p = 0;
414 }
415 }
416 else if (param_cont->param_name)
417 {
418 rc = flush_param (param_cont, assoc, outcharset);
419 free_param_continuation (param_cont);
420 if (rc)
421 return rc;
422 }
423
424 if (flags & MU_MIMEHDR_CSINFO)
425 {
426 p = strchr (val, '\'');
427 if (p)
428 {
429 char *q = strchr (p + 1, '\'');
430 if (q)
431 {
432 cset = val;
433 *p++ = 0;
434 lang = p;
435 *q++ = 0;
436 val = q;
437 }
438 }
439
440 if ((flags & MU_MIMEHDR_MULTILINE) && param_cont->param_cind == 1)
441 {
442 param_cont->param_lang = lang;
443 param_cont->param_cset = cset;
444 }
445 }
446
447 if (flags & MU_MIMEHDR_CSINFO)
448 {
449 char *tmp;
450
451 rc = mu_str_url_decode (&tmp, val);
452 if (rc)
453 return rc;
454 if (!(flags & MU_MIMEHDR_MULTILINE))
455 {
456 if (!outcharset || mu_c_strcasecmp (cset, outcharset) == 0)
457 decoded = tmp;
458 else
459 {
460 rc = _recode_string (tmp, cset, outcharset, &decoded);
461 free (tmp);
462 if (rc)
463 return rc;
464 }
465 }
466 else
467 decoded = tmp;
468 }
469 else
470 {
471 struct mu_mime_param *param;
472 rc = mu_rfc2047_decode_param (outcharset, val, ¶m);
473 if (rc)
474 return rc;
475 cset = csetp = param->cset;
476 lang = langp = param->lang;
477 decoded = param->value;
478 free (param);
479 }
480 val = decoded;
481
482 if (flags & MU_MIMEHDR_MULTILINE)
483 {
484 rc = mu_stream_write (param_cont->param_value, val, strlen (val), NULL);
485 free (decoded);
486 free (csetp);
487 free (langp);
488 return rc;
489 }
490
491 param = calloc (1, sizeof (*param));
492 if (!param)
493 rc = ENOMEM;
494 else
495 {
496 if (lang)
497 {
498 param->lang = strdup (lang);
499 if (!param->lang)
500 rc = ENOMEM;
501 }
502
503 if (rc == 0 && cset)
504 {
505 param->cset = strdup (cset);
506 if (!param->cset)
507 {
508 free (param->lang);
509 rc = ENOMEM;
510 }
511 }
512
513 free (csetp);
514 free (langp);
515 }
516
517 if (rc)
518 {
519 free (decoded);
520 return rc;
521 }
522
523 param->value = strdup (val);
524 free (decoded);
525 if (!param->value)
526 {
527 mu_mime_param_free (param);
528 return ENOMEM;
529 }
530
531 rc = mu_assoc_install (assoc, key, param);
532 switch (rc)
533 {
534 case 0:
535 break;
536
537 case MU_ERR_EXISTS:
538 mu_debug (MU_DEBCAT_MIME, MU_DEBUG_ERROR,
539 ("MIME parameter %s duplicated", key));
540 break;
541
542 default:
543 mu_mime_param_free (param);
544 return rc;
545 }
546 mu_assoc_tail_set_mark (assoc, 1);
547
548 return 0;
549 }
550
551
552 /* A working horse of this module. Parses input string, which should
553 be a header field value complying to RFCs 2045, 2183, 2231.3.
554
555 Input:
556 TEXT - The string.
557 ASSOC - Associative array of parameters indexed by their names.
558 SUBSET - If true, store only those parameters that are already
559 in ASSOC.
560 Output:
561 PVALUE - Unless NULL, a pointer to the field value is stored here on
562 success.
563 ASSOC - Unless NULL, parameters are stored here.
564
565 Both output pointers can be NULL, meaning that the corresponding data
566 are of no interest to the caller.
567
568 The value returned in PVALUE is the initial part of TEXT up to the
569 start of parameters (i.e. to the first semicolon) with leading and
570 trailing whitespace removed. No other syntactic checking is done on
571 the value. It is the responsibility of the caller to verify that it
572 complies to the syntax of the particular header.
573 */
574 static int
_mime_header_parse(const char * text,char ** pvalue,mu_assoc_t assoc,const char * outcharset,mu_assoc_t subset)575 _mime_header_parse (const char *text, char **pvalue,
576 mu_assoc_t assoc, const char *outcharset,
577 mu_assoc_t subset)
578 {
579 int rc = 0;
580 struct mu_wordsplit ws;
581 struct param_continuation cont;
582 size_t i;
583 char *value = NULL;
584 size_t val_len;
585
586 val_len = strcspn (text, ";");
587 if (pvalue)
588 {
589 value = malloc (val_len + 1);
590 if (!value)
591 return ENOMEM;
592 memcpy (value, text, val_len);
593 value[val_len] = 0;
594 mu_rtrim_class (value, MU_CTYPE_SPACE);
595 mu_ltrim_class (value, MU_CTYPE_SPACE);
596 if (value[0] == 0)
597 {
598 free (value);
599 return MU_ERR_PARSE;
600 }
601 }
602
603 text += val_len;
604
605 ws.ws_delim = " \t\r\n;";
606 ws.ws_escape[0] = ws.ws_escape[1] = "\\\\\"\"";
607 ws.ws_options = 0;
608 MU_WRDSO_ESC_SET (&ws, 0, MU_WRDSO_BSKEEP);
609 MU_WRDSO_ESC_SET (&ws, 1, MU_WRDSO_BSKEEP);
610 if (mu_wordsplit (text, &ws,
611 MU_WRDSF_DELIM | MU_WRDSF_ESCAPE |
612 MU_WRDSF_NOVAR | MU_WRDSF_NOCMD |
613 MU_WRDSF_DQUOTE | MU_WRDSF_SQUEEZE_DELIMS |
614 MU_WRDSF_RETURN_DELIMS | MU_WRDSF_WS | MU_WRDSF_OPTIONS))
615 {
616 mu_debug (MU_DEBCAT_MIME, MU_DEBUG_ERROR,
617 (_("wordsplit: %s"), mu_wordsplit_strerror (&ws)));
618 mu_wordsplit_free (&ws);
619 free (value);
620 return MU_ERR_PARSE;
621 }
622
623 if (!assoc)
624 {
625 if (pvalue)
626 *pvalue = value;
627 mu_wordsplit_free (&ws);
628 return 0;
629 }
630
631 memset (&cont, 0, sizeof (cont));
632 i = 0;
633 while (1)
634 {
635 rc = parse_param (&ws, &i, assoc, &cont, outcharset);
636 if (rc)
637 {
638 if (rc == MU_ERR_PARSE)
639 {
640 char *p;
641 mu_assoc_sweep (assoc);
642 /* Attempt error recovery */
643 do
644 p = getword (&ws, &i);
645 while (p && strcmp (p, ";"));
646 if (p)
647 {
648 mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0,
649 (_("finished error recovery at ; %s"),
650 ws.ws_wordv[i]));
651 /* put the semicolon back */
652 i--;
653 continue;
654 }
655 rc = 0;
656 }
657 else if (rc == MU_ERR_USER0)
658 rc = 0;
659 break;
660 }
661 }
662
663 if (rc == 0 && cont.param_name)
664 rc = flush_param (&cont, assoc, outcharset);
665 free_param_continuation (&cont);
666 mu_assoc_tail_set_mark (assoc, 0);
667
668 if (rc == 0)
669 {
670 if (pvalue)
671 *pvalue = value;
672 }
673 else
674 free (value);
675 mu_wordsplit_free (&ws);
676
677 if (subset)
678 mu_assoc_pull (subset, assoc);
679
680 return rc;
681 }
682
683 /* Parse header value from TEXT and return its value and a subset of
684 parameters.
685
686 Input:
687 TEXT - Header value.
688 CSET - Output charset. Can be NULL, in which case no conversions
689 take place.
690 ASSOC - Parameter array initialized with empty slots for those
691 parameters, which are wanted on output. It should be
692 created using mu_mime_param_assoc_create and populated
693 using mu_mime_param_assoc_add.
694 Output:
695 PVALUE - A pointer to the field value is stored here on success.
696 ASSOC - Receives available parameters matching the input subset.
697
698 Either PVALUE or ASSOC (but not both) can be NULL, meaning that the
699 corresponding data are of no interest to the caller.
700 */
701 int
mu_mime_header_parse_subset(const char * text,const char * cset,char ** pvalue,mu_assoc_t assoc)702 mu_mime_header_parse_subset (const char *text, const char *cset,
703 char **pvalue, mu_assoc_t assoc)
704 {
705 mu_assoc_t tmp;
706 int rc = mu_mime_param_assoc_create (&tmp);
707 if (rc == 0)
708 {
709 rc = _mime_header_parse (text, pvalue, tmp, cset, assoc);
710 mu_assoc_destroy (&tmp);
711 }
712 return rc;
713 }
714
715 /* Parse header value from TEXT and return its value and parameters.
716
717 Input:
718 TEXT - Header value.
719 CSET - Output charset. Can be NULL, in which case no conversions
720 take place.
721 Output:
722 PVALUE - A pointer to the field value is stored here on success.
723 PASSOC - Receives an associative array of parameters.
724
725 Either PVALUE or PASSOC (but not both) can be NULL, meaning that the
726 corresponding data are of no interest to the caller.
727 */
728 int
mu_mime_header_parse(const char * text,char const * cset,char ** pvalue,mu_assoc_t * passoc)729 mu_mime_header_parse (const char *text, char const *cset, char **pvalue,
730 mu_assoc_t *passoc)
731 {
732 int rc;
733 mu_assoc_t assoc;
734
735 rc = mu_mime_param_assoc_create (&assoc);
736 if (rc == 0)
737 {
738 rc = _mime_header_parse (text, pvalue, assoc, cset, NULL);
739 if (rc || !passoc)
740 mu_assoc_destroy (&assoc);
741 else
742 *passoc = assoc;
743 }
744
745 return rc;
746 }
747
748 /* TEXT is a value of a structured MIME header, e.g. Content-Type.
749 This function returns the `disposition part' of it. In other
750 words, it returns disposition, if TEXT is a Content-Disposition
751 value, and `type/subtype' part, if it is a Content-Type value.
752 */
753 int
mu_mimehdr_get_disp(const char * text,char * buf,size_t bufsz,size_t * retsz)754 mu_mimehdr_get_disp (const char *text, char *buf, size_t bufsz, size_t *retsz)
755 {
756 int rc;
757 char *value;
758
759 rc = mu_mime_header_parse (text, NULL, &value, NULL);
760 if (rc == 0)
761 {
762 size_t size = strlen (value);
763 if (size > bufsz)
764 size = bufsz;
765 if (buf)
766 size = mu_cpystr (buf, value, size);
767 if (retsz)
768 *retsz = size;
769 }
770 free (value);
771 return 0;
772 }
773
774 /* Same as mu_mimehdr_get_disp, but allocates memory */
775 int
mu_mimehdr_aget_disp(const char * text,char ** pvalue)776 mu_mimehdr_aget_disp (const char *text, char **pvalue)
777 {
778 return mu_mime_header_parse (text, NULL, pvalue, NULL);
779 }
780
781 /* Get the value of parameter NAME from STR, which must be
782 a value of a structured MIME header.
783 At most BUFSZ-1 of data are stored in BUF. A terminating NUL
784 character is appended to it.
785
786 Unless NULL, RETSZ is filled with the actual length of the
787 returned data (not including the NUL terminator).
788
789 BUF may be NULL, in which case the function will only fill
790 RETSZ, as described above. */
791 int
mu_mimehdr_get_param(const char * str,const char * name,char * buf,size_t bufsz,size_t * retsz)792 mu_mimehdr_get_param (const char *str, const char *name,
793 char *buf, size_t bufsz, size_t *retsz)
794 {
795 int rc;
796 char *value;
797
798 rc = mu_mimehdr_aget_param (str, name, &value);
799 if (rc == 0)
800 {
801 size_t size = strlen (value);
802 if (size > bufsz)
803 size = bufsz;
804 if (buf)
805 size = mu_cpystr (buf, value, size);
806 if (retsz)
807 *retsz = size;
808 }
809 free (value);
810 return rc;
811 }
812
813 /* Same as mu_mimehdr_get_param, but allocates memory. */
814 int
mu_mimehdr_aget_param(const char * str,const char * name,char ** pval)815 mu_mimehdr_aget_param (const char *str, const char *name, char **pval)
816 {
817 return mu_mimehdr_aget_decoded_param (str, name, NULL, pval, NULL);
818 }
819
820
821 /* Similar to mu_mimehdr_aget_param, but the returned value is decoded
822 according to the CHARSET. Unless PLANG is NULL, it receives malloc'ed
823 language name from STR. If there was no language name, *PLANG is set
824 to NULL.
825 */
826 int
mu_mimehdr_aget_decoded_param(const char * str,const char * name,const char * charset,char ** pval,char ** plang)827 mu_mimehdr_aget_decoded_param (const char *str, const char *name,
828 const char *charset,
829 char **pval, char **plang)
830 {
831 mu_assoc_t assoc;
832 int rc;
833
834 rc = mu_mime_param_assoc_create (&assoc);
835 if (rc == 0)
836 {
837 rc = mu_mime_param_assoc_add (assoc, name);
838 if (rc == 0)
839 {
840 rc = mu_mime_header_parse_subset (str, charset, NULL, assoc);
841 if (rc == 0)
842 {
843 struct mu_mime_param *param = mu_assoc_get (assoc, name);
844 if (!param)
845 rc = MU_ERR_NOENT;
846 else
847 {
848 *pval = param->value;
849 if (plang)
850 {
851 *plang = param->lang;
852 param->lang = NULL;
853 }
854 param->value = NULL;
855 }
856 }
857 }
858 mu_assoc_destroy (&assoc);
859 }
860 return rc;
861 }
862
863 /* Get the attachment name from a message.
864
865 Input:
866 MSG - The input message.
867 CHARSET - Character set to recode output values to. Can be NULL.
868 Output:
869 PBUF - Output value.
870 PSZ - Its size in bytes, not counting the terminating zero.
871 PLANG - Language the name is written in, if provided in the header.
872
873 Either PSZ or PLAN (or both) can be NULL.
874 */
875 static int
_get_attachment_name(mu_message_t msg,const char * charset,char ** pbuf,size_t * psz,char ** plang)876 _get_attachment_name (mu_message_t msg, const char *charset,
877 char **pbuf, size_t *psz, char **plang)
878 {
879 int ret = EINVAL;
880 mu_header_t hdr;
881 char *value = NULL;
882 mu_assoc_t assoc;
883
884 if (!msg)
885 return ret;
886
887 if ((ret = mu_message_get_header (msg, &hdr)) != 0)
888 return ret;
889
890 ret = mu_header_aget_value_unfold (hdr, MU_HEADER_CONTENT_DISPOSITION,
891 &value);
892
893 /* If the header wasn't there, we'll fall back to Content-Type, but
894 other errors are fatal. */
895 if (ret != 0 && ret != MU_ERR_NOENT)
896 return ret;
897
898 if (ret == 0 && value != NULL)
899 {
900 ret = mu_mime_param_assoc_create (&assoc);
901 if (ret)
902 return ret;
903 ret = mu_mime_param_assoc_add (assoc, "filename");
904 if (ret == 0)
905 {
906 char *disp;
907
908 ret = mu_mime_header_parse_subset (value, charset, &disp, assoc);
909 if (ret == 0)
910 {
911 struct mu_mime_param *param;
912 if (mu_c_strcasecmp (disp, "attachment") == 0 &&
913 (param = mu_assoc_get (assoc, "filename")))
914 {
915 *pbuf = param->value;
916 if (psz)
917 *psz = strlen (*pbuf);
918 param->value = NULL;
919 if (plang)
920 {
921 *plang = param->lang;
922 param->lang = NULL;
923 }
924 }
925 else
926 ret = MU_ERR_NOENT;
927 free (disp);
928 mu_assoc_destroy (&assoc);
929 }
930 }
931 }
932
933 free (value);
934
935 if (ret == 0)
936 return ret;
937
938 /* If we didn't get the name, we fall back on the Content-Type name
939 parameter. */
940
941 ret = mu_header_aget_value_unfold (hdr, MU_HEADER_CONTENT_TYPE, &value);
942 if (ret == 0)
943 {
944 ret = mu_mime_param_assoc_create (&assoc);
945 if (ret)
946 return ret;
947 ret = mu_mime_param_assoc_add (assoc, "name");
948 if (ret == 0)
949 {
950 ret = mu_mime_header_parse_subset (value, charset, NULL, assoc);
951 if (ret == 0)
952 {
953 struct mu_mime_param *param;
954 if ((param = mu_assoc_get (assoc, "name")))
955 {
956 *pbuf = param->value;
957 if (psz)
958 *psz = strlen (*pbuf);
959 param->value = NULL;
960 if (plang)
961 {
962 *plang = param->lang;
963 param->lang = NULL;
964 }
965 }
966 else
967 ret = MU_ERR_NOENT;
968 }
969 }
970 free (value);
971 }
972
973 return ret;
974 }
975
976 int
mu_message_aget_attachment_name(mu_message_t msg,char ** name)977 mu_message_aget_attachment_name (mu_message_t msg, char **name)
978 {
979 if (name == NULL)
980 return MU_ERR_OUT_PTR_NULL;
981 return _get_attachment_name (msg, NULL, name, NULL, NULL);
982 }
983
984 int
mu_message_aget_decoded_attachment_name(mu_message_t msg,const char * charset,char ** pval,char ** plang)985 mu_message_aget_decoded_attachment_name (mu_message_t msg,
986 const char *charset,
987 char **pval,
988 char **plang)
989 {
990 if (pval == NULL)
991 return MU_ERR_OUT_PTR_NULL;
992 return _get_attachment_name (msg, charset, pval, NULL, plang);
993 }
994
995 int
mu_message_get_attachment_name(mu_message_t msg,char * buf,size_t bufsz,size_t * sz)996 mu_message_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz,
997 size_t *sz)
998 {
999 char *tmp;
1000 size_t size;
1001 int rc = _get_attachment_name (msg, NULL, &tmp, &size, NULL);
1002 if (rc == 0)
1003 {
1004 if (size > bufsz)
1005 size = bufsz;
1006 if (buf)
1007 size = mu_cpystr (buf, tmp, size);
1008 if (sz)
1009 *sz = size;
1010 }
1011 free (tmp);
1012 return rc;
1013 }
1014
1015