1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 1999-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Mailutils is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include <config.h>
18 #include <stdlib.h>
19 #include <mailutils/mailutils.h>
20 #include <mailutils/sys/envelope.h>
21 #include <muaux.h>
22 #include <sysexits.h>
23 #include <sys/time.h>
24 #include <sys/resource.h>
25 
26 int truncate_opt;
27 int from_filter;
28 int recode_charset;
29 char *charset;
30 int fd_err;
31 
32 static struct mu_option decodemail_options[] =
33 {
34   { "truncate", 't', NULL, MU_OPTION_DEFAULT,
35     N_("truncate the output mailbox, if it exists"),
36     mu_c_bool, &truncate_opt },
37   { "charset", 'c', N_("CHARSET"), MU_OPTION_DEFAULT,
38     N_("recode output to this charset"),
39     mu_c_string, &charset },
40   { "recode", 'R', NULL, MU_OPTION_DEFAULT,
41     N_("recode text parts to the current charset"),
42     mu_c_bool, &recode_charset },
43   MU_OPTION_END
44 }, *options[] = { decodemail_options, NULL };
45 
46 struct mu_cli_setup cli = {
47   .optv = options,
48   .prog_doc = N_("GNU decodemail -- decode messages."),
49   .prog_args = N_("[INBOX] [OUTBOX]")
50 };
51 
52 static char *decodemail_capa[] = {
53   "debug",
54   "mailbox",
55   "locking",
56   "mime",
57   NULL
58 };
59 
60 char *charset;
61 
62 static void
define_charset(void)63 define_charset (void)
64 {
65   struct mu_lc_all lc_all = { .flags = 0 };
66   char *ep = getenv ("LC_ALL");
67   if (!ep)
68     ep = getenv ("LANG");
69 
70   if (ep && mu_parse_lc_all (ep, &lc_all, MU_LC_CSET) == 0)
71     {
72       charset = mu_strdup (lc_all.charset);
73       mu_lc_all_free (&lc_all);
74     }
75   else
76     charset = mu_strdup ("us-ascii");
77 }
78 
79 static mu_message_t message_decode (mu_message_t, mu_coord_t *, size_t);
80 
81 static void message_store_mbox (mu_message_t, mu_mailbox_t);
82 static void message_store_stdout (mu_message_t, mu_mailbox_t);
83 
84 static void
enable_log_prefix(int on)85 enable_log_prefix (int on)
86 {
87   int mode;
88 
89   mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
90 		   MU_IOCTL_LOGSTREAM_GET_MODE, &mode);
91   if (on)
92     mode |= MU_LOGMODE_PREFIX;
93   else
94     mode &= ~MU_LOGMODE_PREFIX;
95 
96   mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
97 		   MU_IOCTL_LOGSTREAM_SET_MODE, &mode);
98 }
99 
100 static void
set_log_prefix(mu_coord_t crd,size_t dim)101 set_log_prefix (mu_coord_t crd, size_t dim)
102 {
103   char *prefix = mu_coord_part_string (crd, dim);
104   mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
105 		   MU_IOCTL_LOGSTREAM_SET_PREFIX, prefix);
106   free (prefix);
107 }
108 
109 void
abend(int code)110 abend (int code)
111 {
112   if (fd_err)
113     {
114       struct rlimit rlim;
115 
116       getrlimit (RLIMIT_NOFILE, &rlim);
117       rlim.rlim_cur += fd_err;
118       mu_error (_("at least %lu file descriptors are needed to process this message"),
119 		(unsigned long) rlim.rlim_cur);
120     }
121   exit (code);
122 }
123 
124 static void
mailbox_truncate(mu_mailbox_t mbox)125 mailbox_truncate (mu_mailbox_t mbox)
126 {
127   int rc;
128   mu_iterator_t itr;
129 
130   mu_mailbox_get_iterator (mbox, &itr);
131   for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
132        mu_iterator_next (itr))
133     {
134       mu_message_t m;
135       mu_attribute_t a;
136 
137       if ((rc = mu_iterator_current (itr, (void **)&m)) != 0)
138 	{
139 	  mu_error (_("can't get current message while truncating the mailbox: %s"),
140 		    mu_strerror (rc));
141 	  exit (EX_OSERR);
142 	}
143       mu_message_get_attribute (m, &a);
144       mu_attribute_set_deleted (a);
145     }
146   mu_iterator_destroy (&itr);
147   if ((rc = mu_mailbox_expunge (mbox)) != 0)
148     {
149       mu_error (_("error expunging destination mailbox: %s"),
150 		mu_strerror (rc));
151       exit (EX_OSERR);
152     }
153 }
154 
155 static void
output_mbox_cleanup(void * arg)156 output_mbox_cleanup (void *arg)
157 {
158   if (arg)
159     {
160       mu_mailbox_t mbox = arg;
161       mu_mailbox_unlock (mbox);
162     }
163 }
164 
165 int
main(int argc,char ** argv)166 main (int argc, char **argv)
167 {
168   int rc;
169   mu_mailbox_t imbox, ombox = NULL;
170   char *imbox_name = NULL, *ombox_name = NULL;
171   void (*message_store) (mu_message_t, mu_mailbox_t);
172   mu_iterator_t itr;
173   unsigned long i;
174   int err = 0;
175   mu_coord_t crd;
176 
177   /* Native Language Support */
178   MU_APP_INIT_NLS ();
179 
180   /* register the formats.  */
181   mu_register_all_mbox_formats ();
182   mu_register_extra_formats ();
183   mu_auth_register_module (&mu_auth_tls_module);
184   mu_cli_capa_register (&mu_cli_capa_mime);
185 
186   mu_cli (argc, argv, &cli, decodemail_capa, NULL, &argc, &argv);
187 
188   switch (argc)
189     {
190     case 2:
191       ombox_name = argv[1];
192     case 1:
193       imbox_name = argv[0];
194       break;
195     case 0:
196       break;
197     default:
198       mu_error (_("too many arguments; try %s --help for help"),
199 		mu_program_name);
200       exit (EX_USAGE);
201     }
202 
203   if (!charset && recode_charset)
204     define_charset ();
205 
206   /* Open input mailbox */
207   rc = mu_mailbox_create_default (&imbox, imbox_name);
208   if (rc != 0)
209     {
210       if (imbox_name)
211 	mu_error (_("could not create mailbox `%s': %s"),
212 		  imbox_name,
213 		  mu_strerror (rc));
214       else
215 	mu_error (_("could not create default mailbox: %s"),
216 		  mu_strerror (rc));
217       abend (EX_OSERR);
218     }
219 
220   rc = mu_mailbox_open (imbox, MU_STREAM_READ);
221   if (rc)
222     {
223       mu_url_t url = NULL;
224 
225       mu_mailbox_get_url (imbox, &url);
226       mu_error (_("could not open input mailbox `%s': %s"),
227 		mu_url_to_string (url), mu_strerror (rc));
228       abend (EX_NOINPUT);
229     }
230 
231   /* Create output mailbox */
232   if (ombox_name)
233     {
234       mu_property_t prop;
235       const char *type;
236 
237       rc = mu_mailbox_create_default (&ombox, ombox_name);
238       if (rc != 0)
239 	{
240 	  mu_error (_("could not create output mailbox `%s': %s"),
241 		    ombox_name,
242 		    mu_strerror (rc));
243 	  abend (EX_OSERR);
244 	}
245 
246       mu_onexit (output_mbox_cleanup, ombox);
247 
248       if (truncate_opt)
249 	{
250 	  rc = mu_mailbox_open (ombox, MU_STREAM_RDWR);
251 	  switch (rc)
252 	    {
253 	    case 0:
254 	      mailbox_truncate (ombox);
255 	      mu_mailbox_close (ombox);
256 	      break;
257 
258 	    case ENOENT:
259 	    case MU_ERR_NOENT:
260 	      break;
261 
262 	    default:
263 	      mu_error (_("could not open mailbox `%s': %s"),
264 			ombox_name, mu_strerror (rc));
265 	      abend (EX_OSERR);
266 	    }
267 	}
268 
269       rc = mu_mailbox_open (ombox, MU_STREAM_APPEND|MU_STREAM_CREAT);
270       if (rc)
271 	{
272 	  mu_error (_("could not open mailbox `%s': %s"),
273 		    ombox_name, mu_strerror (rc));
274 	  abend (EX_CANTCREAT);
275 	}
276 
277       if (mu_mailbox_get_property (ombox, &prop) == 0 &&
278 	  mu_property_sget_value (prop, "TYPE", &type) == 0 &&
279 	  strcmp (type, "MBOX") == 0)
280 	from_filter = 1;
281 
282       message_store = message_store_mbox;
283     }
284   else
285     {
286       message_store = message_store_stdout;
287       from_filter = 1;
288     }
289 
290   rc = mu_mailbox_get_iterator (imbox, &itr);
291   if (rc)
292     {
293       mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_iterator", NULL, rc);
294       abend (EX_SOFTWARE);
295     }
296 
297   rc = mu_coord_alloc (&crd, 1);
298   if (rc)
299     mu_alloc_die ();
300 
301   enable_log_prefix (1);
302   for (mu_iterator_first (itr), i = 1; !mu_iterator_is_done (itr);
303        mu_iterator_next (itr), i++)
304     {
305       mu_message_t msg, newmsg;
306 
307       rc = mu_iterator_current (itr, (void **)&msg);
308       if (rc)
309 	{
310 	  mu_error (_("cannot read message %lu: %s"),
311 		    i, mu_strerror (rc));
312 	  err = 1;
313 	  continue;
314 	}
315       crd[1] = i;
316       fd_err = 0;
317       newmsg = message_decode (msg, &crd, 1);
318       message_store (newmsg, ombox);
319       mu_message_unref (newmsg);
320       mu_message_unref (msg);
321     }
322   enable_log_prefix (0);
323 
324   mu_mailbox_destroy (&imbox);
325   mu_mailbox_sync (ombox);
326   mu_mailbox_destroy (&ombox);
327 
328   if (err)
329     abend (EX_UNAVAILABLE);
330   exit (EX_OK);
331 }
332 
333 static void
message_store_mbox(mu_message_t msg,mu_mailbox_t mbx)334 message_store_mbox (mu_message_t msg, mu_mailbox_t mbx)
335 {
336   int rc = mu_mailbox_append_message (mbx, msg);
337   if (rc)
338     {
339       mu_error (_("cannot store message: %s"), mu_strerror (rc));
340       switch (rc)
341 	{
342 	case MU_ERR_INVALID_EMAIL:
343 	case MU_ERR_EMPTY_ADDRESS:
344 	  break;
345 
346 	case EMFILE:
347 	  fd_err++;
348 	  /* FALLTHROUGH */
349 
350 	default:
351 	  abend (EX_IOERR);
352 	}
353     }
354 }
355 
356 static void
env_print(mu_message_t msg)357 env_print (mu_message_t msg)
358 {
359   mu_envelope_t env;
360   char const *buf;
361   size_t len;
362 
363   mu_message_get_envelope (msg, &env);
364   if (mu_envelope_sget_sender (env, &buf))
365     buf = "UNKNOWN";
366   mu_printf ("From %s ", buf);
367 
368   if (mu_envelope_sget_date (env, &buf))
369     {
370       char datebuf[MU_DATETIME_FROM_LENGTH+1];
371       time_t t;
372       struct tm *tm;
373 
374       t = time (NULL);
375       tm = gmtime (&t);
376       mu_strftime (datebuf, sizeof datebuf, MU_DATETIME_FROM, tm);
377       buf = datebuf;
378     }
379 
380   mu_printf ("%s", buf);
381   len = strlen (buf);
382   if (len > 1 && buf[len-1] != '\n')
383     mu_printf ("\n");
384 }
385 
386 static void
message_store_stdout(mu_message_t msg,mu_mailbox_t mbx)387 message_store_stdout (mu_message_t msg, mu_mailbox_t mbx)
388 {
389   mu_stream_t str;
390 
391   env_print (msg);
392   mu_message_get_streamref (msg, &str);
393   mu_stream_copy_nl (mu_strout, str, 0, NULL);
394   mu_stream_destroy (&str);
395   mu_printf ("\n");
396 }
397 
398 static inline int
is_address_header(char const * name)399 is_address_header (char const *name)
400 {
401   return !mu_c_strcasecmp (name, MU_HEADER_FROM) ||
402     !mu_c_strcasecmp (name, MU_HEADER_TO) ||
403     !mu_c_strcasecmp (name, MU_HEADER_CC) ||
404     !mu_c_strcasecmp (name, MU_HEADER_BCC);
405 }
406 
407 static int
qstring_needed(char const * s)408 qstring_needed (char const *s)
409 {
410   for (; *s; s++)
411     {
412       if (mu_isascii (*s) && !mu_istspec (*s))
413 	continue;
414       return 1;
415     }
416   return 0;
417 }
418 
419 static void
qstring_format(mu_stream_t stream,char const * s)420 qstring_format (mu_stream_t stream, char const *s)
421 {
422   if (!s)
423     return;
424   if (qstring_needed (s))
425     {
426       char const *cp;
427 
428       mu_stream_write (stream, "\"", 1, NULL);
429       while (*(cp = mu_str_skip_cset_comp (s, "\\\"")))
430 	{
431 	  mu_stream_write (stream, s, cp - s, NULL);
432 	  mu_stream_write (stream, "\\", 1, NULL);
433 	  mu_stream_write (stream, cp, 1, NULL);
434 	  s = cp + 1;
435 	}
436       if (*s)
437 	mu_stream_write (stream, s, strlen (s), NULL);
438       mu_stream_write (stream, "\"", 1, NULL);
439     }
440   else
441     mu_stream_write (stream, s, strlen (s), NULL);
442 }
443 
444 static int
address_decode(char const * name,char const * value,char const * charset,mu_header_t newhdr)445 address_decode (char const *name, char const *value, char const *charset,
446 		mu_header_t newhdr)
447 {
448   int rc;
449   mu_address_t addr;
450   mu_stream_t mstr;
451   mu_transport_t trans[2];
452 
453   rc = mu_memory_stream_create (&mstr, MU_STREAM_RDWR);
454   if (rc)
455     return rc;
456 
457   rc = mu_address_create (&addr, value);
458   if (rc == 0)
459     {
460       mu_address_t cur;
461       for (cur = addr; cur; cur = cur->next)
462 	{
463 	  char *s;
464 
465 	  rc = mu_rfc2047_decode (charset, cur->personal, &s);
466 	  if (rc == 0)
467 	    {
468 	      qstring_format (mstr, s);
469 	      free (s);
470 	    }
471 	  else
472 	    qstring_format (mstr, cur->personal);
473 	  mu_stream_printf (mstr, " <%s>", cur->email);
474 	  if (cur->next)
475 	    mu_stream_write (mstr, ", ", 2, NULL);
476 	}
477       mu_stream_write (mstr, "", 1, NULL);
478       rc = mu_stream_err (mstr);
479       if (rc == 0)
480 	{
481 	  mu_stream_ioctl (mstr, MU_IOCTL_TRANSPORT,
482 			   MU_IOCTL_OP_GET,
483 			   trans);
484 	  mu_header_append (newhdr, name, (char*)trans[0]);
485 	}
486       mu_stream_destroy (&mstr);
487       mu_address_destroy (&addr);
488     }
489   return rc;
490 }
491 
492 /*
493  * Decode a single message or message part.
494  *
495  * Arguments:
496  *   msg  -  Message or message part.
497  *   crd  -  Pointer to mu_coord_t object that keeps its location.
498  *   dim  -  Number of significant positions in crd.  If it is 1,
499  *           msg is the message.  If it is greater than 1, msg is
500  *           part of a MIME message.
501  *
502  * The function can reallocate crd to increase its actual dimension.
503  * It can modify the coordinate positions starting from dim+1 (inclusive).
504  */
505 static mu_message_t
message_decode_nomime(mu_message_t msg)506 message_decode_nomime (mu_message_t msg)
507 {
508   mu_message_t newmsg;
509   int rc;
510   mu_stream_t str;
511   mu_body_t body;
512   mu_stream_t bstr;
513   mu_header_t hdr, newhdr;
514   mu_iterator_t itr;
515   size_t i;
516   char *content_type = NULL;
517   mu_stream_stat_buffer stat;
518 
519   rc = message_body_stream (msg, from_filter, charset, &str);
520   if (rc)
521     return NULL;
522 
523   rc = mu_message_create (&newmsg, NULL);
524   if (rc)
525     {
526       mu_diag_funcall (MU_DIAG_ERROR, "mu_message_create", NULL, rc);
527       abend (EX_OSERR);
528     }
529 
530   rc = mu_message_get_body (newmsg, &body);
531   if (rc)
532     {
533       mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_body", NULL, rc);
534       goto end;
535     }
536 
537   rc = mu_body_get_streamref (body, &bstr);
538   if (rc)
539     {
540       if (rc == EMFILE)
541 	fd_err++;
542       mu_diag_funcall (MU_DIAG_ERROR, "mu_body_get_streamref", NULL, rc);
543       goto end;
544     }
545 
546   mu_stream_set_stat (bstr,
547 		      MU_STREAM_STAT_MASK (MU_STREAM_STAT_IN8BIT),
548 		      stat);
549   rc = mu_stream_copy (bstr, str, 0, NULL);
550   if (rc)
551     {
552       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_copy", NULL, rc);
553       if (mu_stream_err (bstr))
554 	{
555 	  abend (EX_IOERR);
556 	}
557       else
558 	{
559 	  mu_stream_printf (bstr,
560 			    "\n[decodemail: content decoding failed: %s]\n",
561 			    mu_strerror (rc));
562 	}
563     }
564   mu_stream_unref (bstr);
565   mu_stream_unref (str);
566 
567   rc = mu_message_get_header (msg, &hdr);
568   if (rc)
569     {
570       mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_header", "msg", rc);
571       goto end;
572     }
573 
574   rc = mu_message_get_header (newmsg, &newhdr);
575   if (rc)
576     {
577       mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_header", "newmsg", rc);
578       goto end;
579     }
580 
581   rc = mu_header_get_iterator (hdr, &itr);
582   if (rc)
583     {
584       mu_diag_funcall (MU_DIAG_ERROR, "mu_header_get_iterator", NULL, rc);
585       goto end;
586     }
587 
588   for (mu_iterator_first (itr), i = 1; !mu_iterator_is_done (itr);
589        mu_iterator_next (itr), i++)
590     {
591       const char *name;
592       const char *value;
593       char *s;
594 
595       rc = mu_iterator_current_kv (itr, (void const **) &name,
596 				   (void**)&value);
597       if (rc)
598 	{
599 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_iterator_current_kv", NULL, rc);
600 	  continue;
601 	}
602 
603       if (!mu_c_strcasecmp (name, MU_HEADER_CONTENT_TYPE))
604 	{
605 	  if (charset)
606 	    {
607 	      mu_content_type_t ct;
608 	      struct mu_mime_param **pparam;
609 	      char *vc = mu_strdup (value);
610 	      size_t len;
611 	      mu_string_unfold (vc, &len);
612 	      rc = mu_content_type_parse_ext (vc, NULL,
613 					      MU_CONTENT_TYPE_RELAXED |
614 					      MU_CONTENT_TYPE_PARAM,
615 					      &ct);
616 	      if (rc)
617 		{
618 		  mu_diag_funcall (MU_DIAG_ERROR,
619 				   "mu_content_type_parse_ext",
620 				   vc, rc);
621 		  free (vc);
622 		  continue;
623 		}
624 	      free (vc);
625 	      rc = mu_assoc_install_ref (ct->param, "charset", &pparam);
626 	      switch (rc)
627 		{
628 		case 0:
629 		  *pparam = mu_zalloc (sizeof **pparam);
630 		  break;
631 
632 		case MU_ERR_EXISTS:
633 		  free ((*pparam)->value);
634 		  break;
635 
636 		default:
637 		  mu_diag_funcall (MU_DIAG_ERROR,
638 				   "mu_assoc_install_ref",
639 				   NULL, rc);
640 		  abend (EX_IOERR);
641 		}
642 	      (*pparam)->value = mu_strdup (charset);
643 	      mu_content_type_format (ct, &content_type);
644 	      mu_content_type_destroy (&ct);
645 	      continue;
646 	    }
647 	}
648       else if (!mu_c_strcasecmp (name, MU_HEADER_CONTENT_TRANSFER_ENCODING))
649 	continue;
650       else if (is_address_header (name))
651 	{
652 	  if (address_decode (name, value, charset, newhdr))
653 	    mu_header_append (newhdr, name, value);
654 	  continue;
655 	}
656 
657       rc = mu_rfc2047_decode (charset, value, &s);
658       if (rc == 0)
659 	{
660 	  mu_header_append (newhdr, name, s);
661 	  free (s);
662 	}
663       else
664 	mu_header_append (newhdr, name, value);
665     }
666   mu_iterator_destroy (&itr);
667   rc = 0;
668 
669   mu_header_set_value (newhdr,
670 		       MU_HEADER_CONTENT_TRANSFER_ENCODING,
671 		       stat[MU_STREAM_STAT_IN8BIT] ? "8bit" : "7bit",
672 		       1);
673   if (charset)
674     {
675       if (!content_type)
676 	mu_asprintf (&content_type, "text/plain; charset=%s", charset);
677       mu_header_set_value (newhdr,
678 			   MU_HEADER_CONTENT_TYPE,
679 			   content_type,
680 			   1);
681       free (content_type);
682     }
683  end:
684   if (rc)
685     {
686       mu_message_unref (newmsg);
687       newmsg = NULL;
688     }
689   return newmsg;
690 }
691 
692 static mu_message_t
message_decode_mime(mu_message_t msg,mu_coord_t * crd,size_t dim)693 message_decode_mime (mu_message_t msg, mu_coord_t *crd, size_t dim)
694 {
695   int rc;
696   mu_message_t newmsg;
697   size_t nparts, i;
698   mu_mime_t mime;
699   mu_header_t hdr, newhdr;
700   mu_iterator_t itr;
701   char *s;
702   mu_content_type_t ct;
703 
704   /* FIXME: The following could be simplified if we could obtain
705      a mime object from the message */
706   rc = mu_message_get_header (msg, &hdr);
707   if (rc)
708     {
709       mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_header", "msg", rc);
710       return NULL;
711     }
712 
713   rc = mu_header_aget_value_unfold (hdr, MU_HEADER_CONTENT_TYPE, &s);
714   if (rc)
715     {
716       mu_diag_funcall (MU_DIAG_ERROR, "mu_header_aget_value_unfold",
717 		       MU_HEADER_CONTENT_TYPE, rc);
718       return NULL;
719     }
720 
721   rc = mu_content_type_parse_ext (s, NULL,
722 				  MU_CONTENT_TYPE_RELAXED |
723 				      MU_CONTENT_TYPE_PARAM, &ct);
724   if (rc)
725     {
726       mu_diag_funcall (MU_DIAG_ERROR, "mu_content_type_parse_ext", s, rc);
727       free (s);
728       return NULL;
729     }
730   free (s);
731 
732   if (!ct->subtype)
733     {
734       mu_content_type_destroy (&ct);
735       return NULL;
736     }
737 
738   rc = mu_mime_create_multipart (&mime, ct->subtype, ct->param);
739   mu_content_type_destroy (&ct);
740   if (rc)
741     {
742       mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_create_multipart", NULL, rc);
743       return NULL;
744     }
745 
746   rc = mu_message_get_num_parts (msg, &nparts);
747   if (rc)
748     {
749       mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_num_parts",
750 		       NULL, rc);
751       return NULL;
752     }
753 
754   ++dim;
755   if (dim > mu_coord_length (*crd))
756     {
757       rc = mu_coord_realloc (crd, dim);
758       if (rc)
759 	mu_alloc_die ();
760     }
761 
762   for (i = 1; i <= nparts; i++)
763     {
764       mu_message_t msgpart, msgdec;
765 
766       (*crd)[dim] = i;
767       rc = mu_message_get_part (msg, i, &msgpart);
768       if (rc)
769 	{
770 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_part",
771 			   NULL, rc);
772 	  mu_mime_unref (mime);
773 	  return NULL;
774 	}
775       msgdec = message_decode (msgpart, crd, dim);
776       rc = mu_mime_add_part (mime, msgdec);
777       mu_message_unref (msgdec);
778       if (rc)
779 	{
780 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_add_part", NULL, rc);
781 	  mu_mime_unref (mime);
782 	  return NULL;
783 	}
784     }
785 
786   --dim;
787 
788   rc = mu_mime_to_message (mime, &newmsg);
789   mu_mime_unref (mime);
790   if (rc)
791     {
792       mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_to_message", NULL, rc);
793       return NULL;
794     }
795 
796   /* Copy headers */
797   rc = mu_message_get_header (newmsg, &newhdr);
798   if (rc)
799     {
800       mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_header", "newmsg", rc);
801       goto end;
802     }
803 
804   rc = mu_header_get_iterator (hdr, &itr);
805   if (rc)
806     {
807       mu_diag_funcall (MU_DIAG_ERROR, "mu_header_get_iterator", NULL, rc);
808       goto end;
809     }
810 
811   for (mu_iterator_first (itr), i = 1; !mu_iterator_is_done (itr);
812        mu_iterator_next (itr), i++)
813     {
814       const char *name;
815       const char *value;
816       char *s;
817 
818       rc = mu_iterator_current_kv (itr, (void const **) &name,
819 				   (void**)&value);
820       if (rc)
821 	{
822 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_iterator_current_kv", NULL, rc);
823 	  continue;
824 	}
825 
826       if (mu_c_strcasecmp (name, MU_HEADER_MIME_VERSION) == 0 ||
827 	  mu_c_strcasecmp (name, MU_HEADER_CONTENT_TYPE) == 0)
828 	continue;
829       else if (is_address_header (name))
830 	{
831 	  if (address_decode (name, value, charset, newhdr))
832 	    mu_header_append (newhdr, name, value);
833 	  continue;
834 	}
835       rc = mu_rfc2047_decode (charset, value, &s);
836       if (rc == 0)
837 	{
838 	  mu_header_append (newhdr, name, s);
839 	  free (s);
840 	}
841       else
842 	mu_header_append (newhdr, name, value);
843     }
844   mu_iterator_destroy (&itr);
845   rc = 0;
846 
847  end:
848   if (rc)
849     {
850       mu_message_unref (newmsg);
851       newmsg = NULL;
852     }
853   return newmsg;
854 }
855 
856 static mu_message_t
message_decode(mu_message_t msg,mu_coord_t * crd,size_t dim)857 message_decode (mu_message_t msg, mu_coord_t *crd, size_t dim)
858 {
859   mu_message_t newmsg;
860   int ismime = 0;
861   int rc;
862 
863   set_log_prefix (*crd, dim);
864 
865   rc = mu_message_is_multipart (msg, &ismime);
866   if (rc)
867     {
868       mu_diag_funcall (MU_DIAG_ERROR, "mu_message_is_multipart", NULL, rc);
869       newmsg = NULL;
870     }
871   else if (!ismime)
872     {
873       newmsg = message_decode_nomime (msg);
874     }
875   else
876     {
877       newmsg = message_decode_mime (msg, crd, dim);
878     }
879 
880   if (!newmsg)
881     {
882       mu_message_ref (msg);
883       return msg;
884     }
885 
886   set_log_prefix (*crd, dim);
887 
888   if (dim == 1)
889     {
890       /* Copy envelope */
891       mu_envelope_t env, newenv;
892 
893       rc = mu_message_get_envelope (msg, &env);
894       if (rc == 0)
895 	{
896 	  char *sender = NULL, *date = NULL;
897 	  if ((rc = mu_envelope_aget_sender (env, &sender)) != 0)
898 	    {
899 	      mu_diag_funcall (MU_DIAG_ERROR, "mu_envelope_aget_sender",
900 			      NULL, rc);
901 	    }
902 	  else if ((rc = mu_envelope_aget_date (env, &date)) != 0)
903 	    {
904 	      free (sender);
905 	      sender = NULL;
906 	      mu_diag_funcall (MU_DIAG_ERROR, "mu_envelope_aget_date",
907 			       NULL, rc);
908 	    }
909 
910 	  if (sender)
911 	    {
912 	      if ((rc = mu_envelope_create (&newenv, newmsg)) == 0)
913 		{
914 		  newenv->sender = sender;
915 		  newenv->date = date;
916 		  mu_message_set_envelope (newmsg, newenv,
917 					   mu_message_get_owner (newmsg));
918 		}
919 	      else
920 		{
921 		  free (sender);
922 		  free (date);
923 		  mu_diag_funcall (MU_DIAG_ERROR, "mu_envelope_create",
924 				   NULL, rc);
925 		}
926 	    }
927 	}
928       else
929 	mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_envelope",
930 			 NULL, rc);
931     }
932 
933   return newmsg;
934 }
935 
936