]> granicus.if.org Git - neomutt/blob - sendlib.c
added: localtime_r/gmtime_r wrappers
[neomutt] / sendlib.c
1 /**
2  * @file
3  * Miscellaneous functions for sending an email
4  *
5  * @authors
6  * Copyright (C) 1996-2002,2009-2012 Michael R. Elkins <me@mutt.org>
7  *
8  * @copyright
9  * This program is free software: you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation, either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23 /**
24  * @page sendlib Miscellaneous functions for sending an email
25  *
26  * Miscellaneous functions for sending an email
27  */
28
29 #include "config.h"
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <iconv.h>
33 #include <inttypes.h>
34 #include <limits.h>
35 #include <signal.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <sys/wait.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include "mutt/mutt.h"
45 #include "config/lib.h"
46 #include "email/lib.h"
47 #include "mutt.h"
48 #include "sendlib.h"
49 #include "context.h"
50 #include "copy.h"
51 #include "curs_lib.h"
52 #include "filter.h"
53 #include "globals.h"
54 #include "handler.h"
55 #include "hook.h"
56 #include "mailbox.h"
57 #include "mutt_parse.h"
58 #include "mutt_window.h"
59 #include "muttlib.h"
60 #include "mx.h"
61 #include "ncrypt/ncrypt.h"
62 #include "options.h"
63 #include "send.h"
64 #include "smtp.h"
65 #include "state.h"
66 #ifdef USE_NNTP
67 #include "nntp/nntp.h"
68 #endif
69 #ifdef HAVE_SYSEXITS_H
70 #include <sysexits.h>
71 #else
72 #define EX_OK 0
73 #endif
74
75 /* These Config Variables are only used in sendlib.c */
76 bool C_Allow8bit; ///< Config: Allow 8-bit messages, don't use quoted-printable or base64
77 char *C_AttachCharset; ///< Config: When attaching files, use one of these character sets
78 bool C_BounceDelivered; ///< Config: Add 'Delivered-To' to bounced messages
79 bool C_EncodeFrom; ///< Config: Encode 'From ' as 'quote-printable' at the beginning of lines
80 bool C_ForwardDecrypt; ///< Config: Decrypt the message when forwarding it
81 bool C_HiddenHost; ///< Config: Don't use the hostname, just the domain, when generating the message id
82 char *C_Inews;     ///< Config: (nntp) External command to post news articles
83 bool C_MimeForwardDecode; ///< Config: Decode the forwarded message before attaching it
84 bool C_MimeSubject; ///< Config: (nntp) Encode the article subject in base64
85 char *C_MimeTypeQueryCommand; ///< Config: External command to determine the MIME type of an attachment
86 bool C_MimeTypeQueryFirst; ///< Config: Run the #C_MimeTypeQueryCommand before the mime.types lookup
87 char *C_Sendmail;     ///< Config: External command to send email
88 short C_SendmailWait; ///< Config: Time to wait for sendmail to finish
89 bool C_Use8bitmime;   ///< Config: Use 8-bit messages and ESMTP to send messages
90 bool C_UseEnvelopeFrom; ///< Config: Set the envelope sender of the message
91 bool C_UserAgent;       ///< Config: Add a 'User-Agent' head to outgoing mail
92 short C_WrapHeaders;    ///< Config: Width to wrap headers in outgoing messages
93
94 /**
95  * encode_quoted - Encode text as quoted printable
96  * @param fc     Cursor for converting a file's encoding
97  * @param fp_out File to store the result
98  * @param istext Is the input text?
99  */
100 static void encode_quoted(struct FgetConv *fc, FILE *fp_out, bool istext)
101 {
102   int c, linelen = 0;
103   char line[77], savechar;
104
105   while ((c = mutt_ch_fgetconv(fc)) != EOF)
106   {
107     /* Wrap the line if needed. */
108     if ((linelen == 76) && ((istext && (c != '\n')) || !istext))
109     {
110       /* If the last character is "quoted", then be sure to move all three
111        * characters to the next line.  Otherwise, just move the last
112        * character...  */
113       if (line[linelen - 3] == '=')
114       {
115         line[linelen - 3] = 0;
116         fputs(line, fp_out);
117         fputs("=\n", fp_out);
118         line[linelen] = 0;
119         line[0] = '=';
120         line[1] = line[linelen - 2];
121         line[2] = line[linelen - 1];
122         linelen = 3;
123       }
124       else
125       {
126         savechar = line[linelen - 1];
127         line[linelen - 1] = '=';
128         line[linelen] = 0;
129         fputs(line, fp_out);
130         fputc('\n', fp_out);
131         line[0] = savechar;
132         linelen = 1;
133       }
134     }
135
136     /* Escape lines that begin with/only contain "the message separator". */
137     if ((linelen == 4) && mutt_str_startswith(line, "From", CASE_MATCH))
138     {
139       mutt_str_strfcpy(line, "=46rom", sizeof(line));
140       linelen = 6;
141     }
142     else if ((linelen == 4) && mutt_str_startswith(line, "from", CASE_MATCH))
143     {
144       mutt_str_strfcpy(line, "=66rom", sizeof(line));
145       linelen = 6;
146     }
147     else if ((linelen == 1) && (line[0] == '.'))
148     {
149       mutt_str_strfcpy(line, "=2E", sizeof(line));
150       linelen = 3;
151     }
152
153     if ((c == '\n') && istext)
154     {
155       /* Check to make sure there is no trailing space on this line. */
156       if ((linelen > 0) && ((line[linelen - 1] == ' ') || (line[linelen - 1] == '\t')))
157       {
158         if (linelen < 74)
159         {
160           sprintf(line + linelen - 1, "=%2.2X", (unsigned char) line[linelen - 1]);
161           fputs(line, fp_out);
162         }
163         else
164         {
165           int savechar2 = line[linelen - 1];
166
167           line[linelen - 1] = '=';
168           line[linelen] = 0;
169           fputs(line, fp_out);
170           fprintf(fp_out, "\n=%2.2X", (unsigned char) savechar2);
171         }
172       }
173       else
174       {
175         line[linelen] = 0;
176         fputs(line, fp_out);
177       }
178       fputc('\n', fp_out);
179       linelen = 0;
180     }
181     else if ((c != 9) && ((c < 32) || (c > 126) || (c == '=')))
182     {
183       /* Check to make sure there is enough room for the quoted character.
184        * If not, wrap to the next line.  */
185       if (linelen > 73)
186       {
187         line[linelen++] = '=';
188         line[linelen] = 0;
189         fputs(line, fp_out);
190         fputc('\n', fp_out);
191         linelen = 0;
192       }
193       sprintf(line + linelen, "=%2.2X", (unsigned char) c);
194       linelen += 3;
195     }
196     else
197     {
198       /* Don't worry about wrapping the line here.  That will happen during
199        * the next iteration when I'll also know what the next character is.  */
200       line[linelen++] = c;
201     }
202   }
203
204   /* Take care of anything left in the buffer */
205   if (linelen > 0)
206   {
207     if ((line[linelen - 1] == ' ') || (line[linelen - 1] == '\t'))
208     {
209       /* take care of trailing whitespace */
210       if (linelen < 74)
211         sprintf(line + linelen - 1, "=%2.2X", (unsigned char) line[linelen - 1]);
212       else
213       {
214         savechar = line[linelen - 1];
215         line[linelen - 1] = '=';
216         line[linelen] = 0;
217         fputs(line, fp_out);
218         fputc('\n', fp_out);
219         sprintf(line, "=%2.2X", (unsigned char) savechar);
220       }
221     }
222     else
223       line[linelen] = 0;
224     fputs(line, fp_out);
225   }
226 }
227
228 /**
229  * struct B64Context - Cursor for the Base64 conversion
230  */
231 struct B64Context
232 {
233   char buffer[3];
234   short size;
235   short linelen;
236 };
237
238 /**
239  * b64_init - Set up the base64 conversion
240  * @param bctx Cursor for the base64 conversion
241  * @retval 0 Always
242  */
243 static int b64_init(struct B64Context *bctx)
244 {
245   memset(bctx->buffer, '\0', sizeof(bctx->buffer));
246   bctx->size = 0;
247   bctx->linelen = 0;
248
249   return 0;
250 }
251
252 /**
253  * b64_flush - Save the bytes to the file
254  * @param bctx   Cursor for the base64 conversion
255  * @param fp_out File to save the output
256  */
257 static void b64_flush(struct B64Context *bctx, FILE *fp_out)
258 {
259   /* for some reasons, mutt_b64_encode expects the
260    * output buffer to be larger than 10B */
261   char encoded[11];
262   size_t ret;
263
264   if (!bctx->size)
265     return;
266
267   if (bctx->linelen >= 72)
268   {
269     fputc('\n', fp_out);
270     bctx->linelen = 0;
271   }
272
273   /* ret should always be equal to 4 here, because bctx->size
274    * is a value between 1 and 3 (included), but let's not hardcode it
275    * and prefer the return value of the function */
276   ret = mutt_b64_encode(bctx->buffer, bctx->size, encoded, sizeof(encoded));
277   for (size_t i = 0; i < ret; i++)
278   {
279     fputc(encoded[i], fp_out);
280     bctx->linelen++;
281   }
282
283   bctx->size = 0;
284 }
285
286 /**
287  * b64_putc - Base64-encode one character
288  * @param bctx   Cursor for the base64 conversion
289  * @param c      Character to encode
290  * @param fp_out File to save the output
291  */
292 static void b64_putc(struct B64Context *bctx, char c, FILE *fp_out)
293 {
294   if (bctx->size == 3)
295     b64_flush(bctx, fp_out);
296
297   bctx->buffer[bctx->size++] = c;
298 }
299
300 /**
301  * encode_base64 - Base64-encode some data
302  * @param fc     Cursor for converting a file's encoding
303  * @param fp_out File to store the result
304  * @param istext Is the input text?
305  */
306 static void encode_base64(struct FgetConv *fc, FILE *fp_out, int istext)
307 {
308   struct B64Context bctx;
309   int ch, ch1 = EOF;
310
311   b64_init(&bctx);
312
313   while ((ch = mutt_ch_fgetconv(fc)) != EOF)
314   {
315     if (SigInt == 1)
316     {
317       SigInt = 0;
318       return;
319     }
320     if (istext && (ch == '\n') && (ch1 != '\r'))
321       b64_putc(&bctx, '\r', fp_out);
322     b64_putc(&bctx, ch, fp_out);
323     ch1 = ch;
324   }
325   b64_flush(&bctx, fp_out);
326   fputc('\n', fp_out);
327 }
328
329 /**
330  * encode_8bit - Write the data as raw 8-bit data
331  * @param fc     Cursor for converting a file's encoding
332  * @param fp_out File to store the result
333  */
334 static void encode_8bit(struct FgetConv *fc, FILE *fp_out)
335 {
336   int ch;
337
338   while ((ch = mutt_ch_fgetconv(fc)) != EOF)
339   {
340     if (SigInt == 1)
341     {
342       SigInt = 0;
343       return;
344     }
345     fputc(ch, fp_out);
346   }
347 }
348
349 /**
350  * mutt_write_mime_header - Create a MIME header
351  * @param a  Body part
352  * @param fp File to write to
353  * @retval  0 Success
354  * @retval -1 Failure
355  */
356 int mutt_write_mime_header(struct Body *a, FILE *fp)
357 {
358   fprintf(fp, "Content-Type: %s/%s", TYPE(a), a->subtype);
359
360   if (!TAILQ_EMPTY(&a->parameter))
361   {
362     size_t len = 25 + mutt_str_strlen(a->subtype); /* approximate len. of content-type */
363
364     struct Parameter *np = NULL;
365     TAILQ_FOREACH(np, &a->parameter, entries)
366     {
367       char *tmp = NULL;
368
369       if (!np->value)
370         continue;
371
372       fputc(';', fp);
373
374       char buf[256];
375       buf[0] = '\0';
376       tmp = mutt_str_strdup(np->value);
377       const int encode = rfc2231_encode_string(&tmp);
378       mutt_addr_cat(buf, sizeof(buf), tmp, MimeSpecials);
379
380       /* Dirty hack to make messages readable by Outlook Express for the Mac:
381        * force quotes around the boundary parameter even when they aren't
382        * needed.  */
383
384       if ((mutt_str_strcasecmp(np->attribute, "boundary") == 0) && (strcmp(buf, tmp) == 0))
385       {
386         snprintf(buf, sizeof(buf), "\"%s\"", tmp);
387       }
388
389       FREE(&tmp);
390
391       const int tmplen = mutt_str_strlen(buf) + mutt_str_strlen(np->attribute) + 1;
392
393       if (len + tmplen + 2 > 76)
394       {
395         fputs("\n\t", fp);
396         len = tmplen + 8;
397       }
398       else
399       {
400         fputc(' ', fp);
401         len += tmplen + 1;
402       }
403
404       fprintf(fp, "%s%s=%s", np->attribute, encode ? "*" : "", buf);
405     }
406   }
407
408   fputc('\n', fp);
409
410   if (a->language)
411     fprintf(fp, "Content-Language: %s\n", a->language);
412
413   if (a->description)
414     fprintf(fp, "Content-Description: %s\n", a->description);
415
416   if (a->disposition != DISP_NONE)
417   {
418     const char *dispstr[] = { "inline", "attachment", "form-data" };
419
420     if (a->disposition < sizeof(dispstr) / sizeof(char *))
421     {
422       fprintf(fp, "Content-Disposition: %s", dispstr[a->disposition]);
423
424       if (a->use_disp && (a->disposition != DISP_INLINE))
425       {
426         char *fn = a->d_filename;
427         if (!fn)
428           fn = a->filename;
429
430         if (fn)
431         {
432           char *tmp = NULL;
433
434           /* Strip off the leading path... */
435           char *t = strrchr(fn, '/');
436           if (t)
437             t++;
438           else
439             t = fn;
440
441           char buf[256];
442           buf[0] = '\0';
443           tmp = mutt_str_strdup(t);
444           const int encode = rfc2231_encode_string(&tmp);
445           mutt_addr_cat(buf, sizeof(buf), tmp, MimeSpecials);
446           FREE(&tmp);
447           fprintf(fp, "; filename%s=%s", encode ? "*" : "", buf);
448         }
449       }
450
451       fputc('\n', fp);
452     }
453     else
454     {
455       mutt_debug(LL_DEBUG1, "ERROR: invalid content-disposition %d\n", a->disposition);
456     }
457   }
458
459   if (a->encoding != ENC_7BIT)
460     fprintf(fp, "Content-Transfer-Encoding: %s\n", ENCODING(a->encoding));
461
462   if (C_CryptProtectedHeadersWrite && a->mime_headers)
463     mutt_rfc822_write_header(fp, a->mime_headers, NULL, MUTT_WRITE_HEADER_MIME, false, false);
464
465   /* Do NOT add the terminator here!!! */
466   return ferror(fp) ? -1 : 0;
467 }
468
469 /**
470  * write_as_text_part - Should the Body be written as a text MIME part
471  * @param b Email to examine
472  * @retval true If the Body should be written as text
473  */
474 static bool write_as_text_part(struct Body *b)
475 {
476   return mutt_is_text_part(b) ||
477          (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_application_pgp(b));
478 }
479
480 /**
481  * mutt_write_mime_body - Write a MIME part
482  * @param a  Body to use
483  * @param fp File to write to
484  * @retval  0 Success
485  * @retval -1 Failure
486  */
487 int mutt_write_mime_body(struct Body *a, FILE *fp)
488 {
489   FILE *fp_in = NULL;
490   struct FgetConv *fc = NULL;
491
492   if (a->type == TYPE_MULTIPART)
493   {
494     /* First, find the boundary to use */
495     const char *p = mutt_param_get(&a->parameter, "boundary");
496     if (!p)
497     {
498       mutt_debug(LL_DEBUG1, "no boundary parameter found\n");
499       mutt_error(_("No boundary parameter found [report this error]"));
500       return -1;
501     }
502     char boundary[128];
503     mutt_str_strfcpy(boundary, p, sizeof(boundary));
504
505     for (struct Body *t = a->parts; t; t = t->next)
506     {
507       fprintf(fp, "\n--%s\n", boundary);
508       if (mutt_write_mime_header(t, fp) == -1)
509         return -1;
510       fputc('\n', fp);
511       if (mutt_write_mime_body(t, fp) == -1)
512         return -1;
513     }
514     fprintf(fp, "\n--%s--\n", boundary);
515     return ferror(fp) ? -1 : 0;
516   }
517
518   /* This is pretty gross, but it's the best solution for now... */
519   if (((WithCrypto & APPLICATION_PGP) != 0) && (a->type == TYPE_APPLICATION) &&
520       (mutt_str_strcmp(a->subtype, "pgp-encrypted") == 0))
521   {
522     fputs("Version: 1\n", fp);
523     return 0;
524   }
525
526   fp_in = fopen(a->filename, "r");
527   if (!fp_in)
528   {
529     mutt_debug(LL_DEBUG1, "%s no longer exists\n", a->filename);
530     mutt_error(_("%s no longer exists"), a->filename);
531     return -1;
532   }
533
534   if ((a->type == TYPE_TEXT) && (!a->noconv))
535   {
536     char send_charset[128];
537     fc = mutt_ch_fgetconv_open(
538         fp_in, a->charset,
539         mutt_body_get_charset(a, send_charset, sizeof(send_charset)), 0);
540   }
541   else
542     fc = mutt_ch_fgetconv_open(fp_in, 0, 0, 0);
543
544   mutt_sig_allow_interrupt(1);
545   if (a->encoding == ENC_QUOTED_PRINTABLE)
546     encode_quoted(fc, fp, write_as_text_part(a));
547   else if (a->encoding == ENC_BASE64)
548     encode_base64(fc, fp, write_as_text_part(a));
549   else if ((a->type == TYPE_TEXT) && (!a->noconv))
550     encode_8bit(fc, fp);
551   else
552     mutt_file_copy_stream(fp_in, fp);
553   mutt_sig_allow_interrupt(0);
554
555   mutt_ch_fgetconv_close(&fc);
556   mutt_file_fclose(&fp_in);
557
558   if (SigInt == 1)
559   {
560     SigInt = 0;
561     return -1;
562   }
563   return ferror(fp) ? -1 : 0;
564 }
565
566 /**
567  * mutt_generate_boundary - Create a unique boundary id for a MIME part
568  * @param parm MIME part
569  */
570 void mutt_generate_boundary(struct ParameterList *parm)
571 {
572   char rs[MUTT_RANDTAG_LEN + 1];
573
574   mutt_rand_base32(rs, sizeof(rs) - 1);
575   rs[MUTT_RANDTAG_LEN] = 0;
576   mutt_param_set(parm, "boundary", rs);
577 }
578
579 /**
580  * struct ContentState - Info about the body of an email
581  */
582 struct ContentState
583 {
584   bool from;
585   int whitespace;
586   bool dot;
587   int linelen;
588   bool was_cr;
589 };
590
591 /**
592  * update_content_info - Cache some info about an email
593  * @param info   Info about an Attachment
594  * @param s      Info about the Body of an email
595  * @param buf    Buffer for the result
596  * @param buflen Length of the buffer
597  */
598 static void update_content_info(struct Content *info, struct ContentState *s,
599                                 char *buf, size_t buflen)
600 {
601   bool from = s->from;
602   int whitespace = s->whitespace;
603   bool dot = s->dot;
604   int linelen = s->linelen;
605   bool was_cr = s->was_cr;
606
607   if (!buf) /* This signals EOF */
608   {
609     if (was_cr)
610       info->binary = true;
611     if (linelen > info->linemax)
612       info->linemax = linelen;
613
614     return;
615   }
616
617   for (; buflen; buf++, buflen--)
618   {
619     char ch = *buf;
620
621     if (was_cr)
622     {
623       was_cr = false;
624       if (ch != '\n')
625       {
626         info->binary = true;
627       }
628       else
629       {
630         if (whitespace)
631           info->space = true;
632         if (dot)
633           info->dot = true;
634         if (linelen > info->linemax)
635           info->linemax = linelen;
636         whitespace = 0;
637         dot = false;
638         linelen = 0;
639         continue;
640       }
641     }
642
643     linelen++;
644     if (ch == '\n')
645     {
646       info->crlf++;
647       if (whitespace)
648         info->space = true;
649       if (dot)
650         info->dot = true;
651       if (linelen > info->linemax)
652         info->linemax = linelen;
653       whitespace = 0;
654       linelen = 0;
655       dot = false;
656     }
657     else if (ch == '\r')
658     {
659       info->crlf++;
660       info->cr = true;
661       was_cr = true;
662       continue;
663     }
664     else if (ch & 0x80)
665       info->hibin++;
666     else if ((ch == '\t') || (ch == '\f'))
667     {
668       info->ascii++;
669       whitespace++;
670     }
671     else if (ch == 0)
672     {
673       info->nulbin++;
674       info->lobin++;
675     }
676     else if ((ch < 32) || (ch == 127))
677       info->lobin++;
678     else
679     {
680       if (linelen == 1)
681       {
682         if ((ch == 'F') || (ch == 'f'))
683           from = true;
684         else
685           from = false;
686         if (ch == '.')
687           dot = true;
688         else
689           dot = false;
690       }
691       else if (from)
692       {
693         if ((linelen == 2) && (ch != 'r'))
694           from = false;
695         else if ((linelen == 3) && (ch != 'o'))
696           from = false;
697         else if (linelen == 4)
698         {
699           if (ch == 'm')
700             info->from = true;
701           from = false;
702         }
703       }
704       if (ch == ' ')
705         whitespace++;
706       info->ascii++;
707     }
708
709     if (linelen > 1)
710       dot = false;
711     if ((ch != ' ') && (ch != '\t'))
712       whitespace = 0;
713   }
714
715   s->from = from;
716   s->whitespace = whitespace;
717   s->dot = dot;
718   s->linelen = linelen;
719   s->was_cr = was_cr;
720 }
721
722 /**
723  * convert_file_to - Change the encoding of a file
724  * @param[in]  fp         File to convert
725  * @param[in]  fromcode   Original encoding
726  * @param[in]  ncodes     Number of target encodings
727  * @param[in]  tocodes    List of target encodings
728  * @param[out] tocode     Chosen encoding
729  * @param[in]  info       Encoding information
730  * @retval -1 Error, no conversion was possible
731  * @retval >0 Success, number of bytes converted
732  *
733  * Find the best charset conversion of the file from fromcode into one
734  * of the tocodes. If successful, set *tocode and Content *info and
735  * return the number of characters converted inexactly.
736  *
737  * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
738  * which would otherwise prevent us from knowing the number of inexact
739  * conversions. Where the candidate target charset is UTF-8 we avoid
740  * doing the second conversion because iconv_open("UTF-8", "UTF-8")
741  * fails with some libraries.
742  *
743  * We assume that the output from iconv is never more than 4 times as
744  * long as the input for any pair of charsets we might be interested
745  * in.
746  */
747 static size_t convert_file_to(FILE *fp, const char *fromcode, int ncodes,
748                               const char **tocodes, int *tocode, struct Content *info)
749 {
750   char bufi[256], bufu[512], bufo[4 * sizeof(bufi)];
751   size_t ret;
752
753   const iconv_t cd1 = mutt_ch_iconv_open("utf-8", fromcode, 0);
754   if (cd1 == (iconv_t)(-1))
755     return -1;
756
757   iconv_t *cd = mutt_mem_calloc(ncodes, sizeof(iconv_t));
758   size_t *score = mutt_mem_calloc(ncodes, sizeof(size_t));
759   struct ContentState *states = mutt_mem_calloc(ncodes, sizeof(struct ContentState));
760   struct Content *infos = mutt_mem_calloc(ncodes, sizeof(struct Content));
761
762   for (int i = 0; i < ncodes; i++)
763   {
764     if (mutt_str_strcasecmp(tocodes[i], "utf-8") != 0)
765       cd[i] = mutt_ch_iconv_open(tocodes[i], "utf-8", 0);
766     else
767     {
768       /* Special case for conversion to UTF-8 */
769       cd[i] = (iconv_t)(-1);
770       score[i] = (size_t)(-1);
771     }
772   }
773
774   rewind(fp);
775   size_t ibl = 0;
776   while (true)
777   {
778     /* Try to fill input buffer */
779     size_t n = fread(bufi + ibl, 1, sizeof(bufi) - ibl, fp);
780     ibl += n;
781
782     /* Convert to UTF-8 */
783     const char *ib = bufi;
784     char *ob = bufu;
785     size_t obl = sizeof(bufu);
786     n = iconv(cd1, (ICONV_CONST char **) (ibl ? &ib : 0), &ibl, &ob, &obl);
787     /* assert(n == (size_t)(-1) || !n); */
788     if ((n == (size_t)(-1)) && (((errno != EINVAL) && (errno != E2BIG)) || (ib == bufi)))
789     {
790       /* assert(errno == EILSEQ || (errno == EINVAL && ib == bufi && ibl < sizeof(bufi))); */
791       ret = (size_t)(-1);
792       break;
793     }
794     const size_t ubl1 = ob - bufu;
795
796     /* Convert from UTF-8 */
797     for (int i = 0; i < ncodes; i++)
798     {
799       if ((cd[i] != (iconv_t)(-1)) && (score[i] != (size_t)(-1)))
800       {
801         const char *ub = bufu;
802         size_t ubl = ubl1;
803         ob = bufo;
804         obl = sizeof(bufo);
805         n = iconv(cd[i], (ICONV_CONST char **) ((ibl || ubl) ? &ub : 0), &ubl, &ob, &obl);
806         if (n == (size_t)(-1))
807         {
808           /* assert(errno == E2BIG || (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT))); */
809           score[i] = (size_t)(-1);
810         }
811         else
812         {
813           score[i] += n;
814           update_content_info(&infos[i], &states[i], bufo, ob - bufo);
815         }
816       }
817       else if ((cd[i] == (iconv_t)(-1)) && (score[i] == (size_t)(-1)))
818       {
819         /* Special case for conversion to UTF-8 */
820         update_content_info(&infos[i], &states[i], bufu, ubl1);
821       }
822     }
823
824     if (ibl)
825     {
826       /* Save unused input */
827       memmove(bufi, ib, ibl);
828     }
829     else if (!ubl1 && (ib < bufi + sizeof(bufi)))
830     {
831       ret = 0;
832       break;
833     }
834   }
835
836   if (!ret)
837   {
838     /* Find best score */
839     ret = (size_t)(-1);
840     for (int i = 0; i < ncodes; i++)
841     {
842       if ((cd[i] == (iconv_t)(-1)) && (score[i] == (size_t)(-1)))
843       {
844         /* Special case for conversion to UTF-8 */
845         *tocode = i;
846         ret = 0;
847         break;
848       }
849       else if ((cd[i] == (iconv_t)(-1)) || (score[i] == (size_t)(-1)))
850         continue;
851       else if ((ret == (size_t)(-1)) || (score[i] < ret))
852       {
853         *tocode = i;
854         ret = score[i];
855         if (!ret)
856           break;
857       }
858     }
859     if (ret != (size_t)(-1))
860     {
861       memcpy(info, &infos[*tocode], sizeof(struct Content));
862       update_content_info(info, &states[*tocode], 0, 0); /* EOF */
863     }
864   }
865
866   for (int i = 0; i < ncodes; i++)
867     if (cd[i] != (iconv_t)(-1))
868       iconv_close(cd[i]);
869
870   iconv_close(cd1);
871   FREE(&cd);
872   FREE(&infos);
873   FREE(&score);
874   FREE(&states);
875
876   return ret;
877 }
878
879 /**
880  * convert_file_from_to - Convert a file between encodings
881  * @param[in]  fp        File to read from
882  * @param[in]  fromcodes Charsets to try converting FROM
883  * @param[in]  tocodes   Charsets to try converting TO
884  * @param[out] fromcode  From charset selected
885  * @param[out] tocode    To charset selected
886  * @param[out] info      Info about the file
887  * @retval num Characters converted
888  * @retval -1  Error (as a size_t)
889  *
890  * Find the first of the fromcodes that gives a valid conversion and the best
891  * charset conversion of the file into one of the tocodes. If successful, set
892  * *fromcode and *tocode to dynamically allocated strings, set Content *info,
893  * and return the number of characters converted inexactly. If no conversion
894  * was possible, return -1.
895  *
896  * Both fromcodes and tocodes may be colon-separated lists of charsets.
897  * However, if fromcode is zero then fromcodes is assumed to be the name of a
898  * single charset even if it contains a colon.
899  */
900 static size_t convert_file_from_to(FILE *fp, const char *fromcodes, const char *tocodes,
901                                    char **fromcode, char **tocode, struct Content *info)
902 {
903   char *fcode = NULL;
904   char **tcode = NULL;
905   const char *c = NULL, *c1 = NULL;
906   size_t ret;
907   int ncodes, i, cn;
908
909   /* Count the tocodes */
910   ncodes = 0;
911   for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
912   {
913     c1 = strchr(c, ':');
914     if (c1 == c)
915       continue;
916     ncodes++;
917   }
918
919   /* Copy them */
920   tcode = mutt_mem_malloc(ncodes * sizeof(char *));
921   for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
922   {
923     c1 = strchr(c, ':');
924     if (c1 == c)
925       continue;
926     tcode[i] = mutt_str_substr_dup(c, c1);
927   }
928
929   ret = (size_t)(-1);
930   if (fromcode)
931   {
932     /* Try each fromcode in turn */
933     for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
934     {
935       c1 = strchr(c, ':');
936       if (c1 == c)
937         continue;
938       fcode = mutt_str_substr_dup(c, c1);
939
940       ret = convert_file_to(fp, fcode, ncodes, (const char **) tcode, &cn, info);
941       if (ret != (size_t)(-1))
942       {
943         *fromcode = fcode;
944         *tocode = tcode[cn];
945         tcode[cn] = 0;
946         break;
947       }
948       FREE(&fcode);
949     }
950   }
951   else
952   {
953     /* There is only one fromcode */
954     ret = convert_file_to(fp, fromcodes, ncodes, (const char **) tcode, &cn, info);
955     if (ret != (size_t)(-1))
956     {
957       *tocode = tcode[cn];
958       tcode[cn] = 0;
959     }
960   }
961
962   /* Free memory */
963   for (i = 0; i < ncodes; i++)
964     FREE(&tcode[i]);
965
966   FREE(&tcode);
967
968   return ret;
969 }
970
971 /**
972  * mutt_get_content_info - Analyze file to determine MIME encoding to use
973  * @param fname File to examine
974  * @param b     Body to update
975  * @retval ptr Newly allocated Content
976  *
977  * Also set the body charset, sometimes, or not.
978  */
979 struct Content *mutt_get_content_info(const char *fname, struct Body *b)
980 {
981   struct Content *info = NULL;
982   struct ContentState state = { 0 };
983   FILE *fp = NULL;
984   char *fromcode = NULL;
985   char *tocode = NULL;
986   char buf[100];
987   size_t r;
988
989   struct stat sb;
990
991   if (b && !fname)
992     fname = b->filename;
993
994   if (stat(fname, &sb) == -1)
995   {
996     mutt_error(_("Can't stat %s: %s"), fname, strerror(errno));
997     return NULL;
998   }
999
1000   if (!S_ISREG(sb.st_mode))
1001   {
1002     mutt_error(_("%s isn't a regular file"), fname);
1003     return NULL;
1004   }
1005
1006   fp = fopen(fname, "r");
1007   if (!fp)
1008   {
1009     mutt_debug(LL_DEBUG1, "%s: %s (errno %d).\n", fname, strerror(errno), errno);
1010     return NULL;
1011   }
1012
1013   info = mutt_mem_calloc(1, sizeof(struct Content));
1014
1015   if (b && (b->type == TYPE_TEXT) && (!b->noconv && !b->force_charset))
1016   {
1017     char *chs = mutt_param_get(&b->parameter, "charset");
1018     char *fchs = b->use_disp ?
1019                      ((C_AttachCharset && *C_AttachCharset) ? C_AttachCharset : C_Charset) :
1020                      C_Charset;
1021     if (C_Charset && (chs || C_SendCharset) &&
1022         (convert_file_from_to(fp, fchs, chs ? chs : C_SendCharset, &fromcode,
1023                               &tocode, info) != (size_t)(-1)))
1024     {
1025       if (!chs)
1026       {
1027         char chsbuf[256];
1028         mutt_ch_canonical_charset(chsbuf, sizeof(chsbuf), tocode);
1029         mutt_param_set(&b->parameter, "charset", chsbuf);
1030       }
1031       FREE(&b->charset);
1032       b->charset = fromcode;
1033       FREE(&tocode);
1034       mutt_file_fclose(&fp);
1035       return info;
1036     }
1037   }
1038
1039   rewind(fp);
1040   while ((r = fread(buf, 1, sizeof(buf), fp)))
1041     update_content_info(info, &state, buf, r);
1042   update_content_info(info, &state, 0, 0);
1043
1044   mutt_file_fclose(&fp);
1045
1046   if (b && (b->type == TYPE_TEXT) && (!b->noconv && !b->force_charset))
1047   {
1048     mutt_param_set(&b->parameter, "charset",
1049                    (!info->hibin ?
1050                         "us-ascii" :
1051                         C_Charset && !mutt_ch_is_us_ascii(C_Charset) ? C_Charset : "unknown-8bit"));
1052   }
1053
1054   return info;
1055 }
1056
1057 /**
1058  * mutt_lookup_mime_type - Find the MIME type for an attachment
1059  * @param att  Email with attachment
1060  * @param path Path to attachment
1061  * @retval num MIME type, e.g. #TYPE_IMAGE
1062  *
1063  * Given a file at 'path', see if there is a registered MIME type.
1064  * Returns the major MIME type, and copies the subtype to "d".  First look
1065  * in a system mime.types if we can find one, then look for ~/.mime.types.
1066  * The longest match is used so that we can match 'ps.gz' when 'gz' also
1067  * exists.
1068  */
1069 int mutt_lookup_mime_type(struct Body *att, const char *path)
1070 {
1071   FILE *fp = NULL;
1072   char *p = NULL, *q = NULL, *ct = NULL;
1073   char buf[PATH_MAX];
1074   char subtype[256], xtype[256];
1075   int szf, sze, cur_sze;
1076   int type;
1077   bool found_mimetypes = false;
1078
1079   *subtype = '\0';
1080   *xtype = '\0';
1081   type = TYPE_OTHER;
1082   cur_sze = 0;
1083
1084   szf = mutt_str_strlen(path);
1085
1086   for (int count = 0; count < 4; count++)
1087   {
1088     /* can't use strtok() because we use it in an inner loop below, so use
1089      * a switch statement here instead.  */
1090     switch (count)
1091     {
1092       /* last file with last entry to match wins type/xtype */
1093       case 0:
1094         /* check default unix mimetypes location first */
1095         mutt_str_strfcpy(buf, "/etc/mime.types", sizeof(buf));
1096         break;
1097       case 1:
1098         mutt_str_strfcpy(buf, SYSCONFDIR "/mime.types", sizeof(buf));
1099         break;
1100       case 2:
1101         mutt_str_strfcpy(buf, PKGDATADIR "/mime.types", sizeof(buf));
1102         break;
1103       case 3:
1104         snprintf(buf, sizeof(buf), "%s/.mime.types", NONULL(HomeDir));
1105         break;
1106       default:
1107         mutt_debug(LL_DEBUG1, "Internal error, count = %d.\n", count);
1108         goto bye; /* shouldn't happen */
1109     }
1110
1111     fp = fopen(buf, "r");
1112     if (fp)
1113     {
1114       found_mimetypes = true;
1115
1116       while (fgets(buf, sizeof(buf) - 1, fp))
1117       {
1118         /* weed out any comments */
1119         p = strchr(buf, '#');
1120         if (p)
1121           *p = '\0';
1122
1123         /* remove any leading space. */
1124         ct = buf;
1125         SKIPWS(ct);
1126
1127         /* position on the next field in this line */
1128         p = strpbrk(ct, " \t");
1129         if (!p)
1130           continue;
1131         *p++ = 0;
1132         SKIPWS(p);
1133
1134         /* cycle through the file extensions */
1135         while ((p = strtok(p, " \t\n")))
1136         {
1137           sze = mutt_str_strlen(p);
1138           if ((sze > cur_sze) && (szf >= sze) &&
1139               ((mutt_str_strcasecmp(path + szf - sze, p) == 0) ||
1140                (mutt_str_strcasecmp(path + szf - sze, p) == 0)) &&
1141               ((szf == sze) || (path[szf - sze - 1] == '.')))
1142           {
1143             /* get the content-type */
1144
1145             p = strchr(ct, '/');
1146             if (!p)
1147             {
1148               /* malformed line, just skip it. */
1149               break;
1150             }
1151             *p++ = 0;
1152
1153             for (q = p; *q && !ISSPACE(*q); q++)
1154               ;
1155
1156             mutt_str_substr_cpy(subtype, p, q, sizeof(subtype));
1157
1158             type = mutt_check_mime_type(ct);
1159             if (type == TYPE_OTHER)
1160               mutt_str_strfcpy(xtype, ct, sizeof(xtype));
1161
1162             cur_sze = sze;
1163           }
1164           p = NULL;
1165         }
1166       }
1167       mutt_file_fclose(&fp);
1168     }
1169   }
1170
1171 bye:
1172
1173   /* no mime.types file found */
1174   if (!found_mimetypes)
1175   {
1176     mutt_error(_("Could not find any mime.types file."));
1177   }
1178
1179   if ((type != TYPE_OTHER) || (*xtype != '\0'))
1180   {
1181     att->type = type;
1182     mutt_str_replace(&att->subtype, subtype);
1183     mutt_str_replace(&att->xtype, xtype);
1184   }
1185
1186   return type;
1187 }
1188
1189 /**
1190  * transform_to_7bit - Convert MIME parts to 7-bit
1191  * @param a    Body of the email
1192  * @param fp_in File to read
1193  */
1194 static void transform_to_7bit(struct Body *a, FILE *fp_in)
1195 {
1196   char buf[PATH_MAX];
1197   struct State s = { 0 };
1198   struct stat sb;
1199
1200   for (; a; a = a->next)
1201   {
1202     if (a->type == TYPE_MULTIPART)
1203     {
1204       if (a->encoding != ENC_7BIT)
1205         a->encoding = ENC_7BIT;
1206
1207       transform_to_7bit(a->parts, fp_in);
1208     }
1209     else if (mutt_is_message_type(a->type, a->subtype))
1210     {
1211       mutt_message_to_7bit(a, fp_in);
1212     }
1213     else
1214     {
1215       a->noconv = true;
1216       a->force_charset = true;
1217
1218       mutt_mktemp(buf, sizeof(buf));
1219       s.fp_out = mutt_file_fopen(buf, "w");
1220       if (!s.fp_out)
1221       {
1222         mutt_perror("fopen");
1223         return;
1224       }
1225       s.fp_in = fp_in;
1226       mutt_decode_attachment(a, &s);
1227       mutt_file_fclose(&s.fp_out);
1228       FREE(&a->d_filename);
1229       a->d_filename = a->filename;
1230       a->filename = mutt_str_strdup(buf);
1231       a->unlink = true;
1232       if (stat(a->filename, &sb) == -1)
1233       {
1234         mutt_perror("stat");
1235         return;
1236       }
1237       a->length = sb.st_size;
1238
1239       mutt_update_encoding(a);
1240       if (a->encoding == ENC_8BIT)
1241         a->encoding = ENC_QUOTED_PRINTABLE;
1242       else if (a->encoding == ENC_BINARY)
1243         a->encoding = ENC_BASE64;
1244     }
1245   }
1246 }
1247
1248 /**
1249  * mutt_message_to_7bit - Convert an email's MIME parts to 7-bit
1250  * @param a  Body of the email
1251  * @param fp File to read (OPTIONAL)
1252  */
1253 void mutt_message_to_7bit(struct Body *a, FILE *fp)
1254 {
1255   char temp[PATH_MAX];
1256   char *line = NULL;
1257   FILE *fp_in = NULL;
1258   FILE *fp_out = NULL;
1259   struct stat sb;
1260
1261   if (!a->filename && fp)
1262     fp_in = fp;
1263   else if (!a->filename || !(fp_in = fopen(a->filename, "r")))
1264   {
1265     mutt_error(_("Could not open %s"), a->filename ? a->filename : "(null)");
1266     return;
1267   }
1268   else
1269   {
1270     a->offset = 0;
1271     if (stat(a->filename, &sb) == -1)
1272     {
1273       mutt_perror("stat");
1274       mutt_file_fclose(&fp_in);
1275     }
1276     a->length = sb.st_size;
1277   }
1278
1279   mutt_mktemp(temp, sizeof(temp));
1280   fp_out = mutt_file_fopen(temp, "w+");
1281   if (!fp_out)
1282   {
1283     mutt_perror("fopen");
1284     goto cleanup;
1285   }
1286
1287   if (!fp_in)
1288     goto cleanup;
1289
1290   fseeko(fp_in, a->offset, SEEK_SET);
1291   a->parts = mutt_rfc822_parse_message(fp_in, a);
1292
1293   transform_to_7bit(a->parts, fp_in);
1294
1295   mutt_copy_hdr(fp_in, fp_out, a->offset, a->offset + a->length,
1296                 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1297
1298   fputs("MIME-Version: 1.0\n", fp_out);
1299   mutt_write_mime_header(a->parts, fp_out);
1300   fputc('\n', fp_out);
1301   mutt_write_mime_body(a->parts, fp_out);
1302
1303 cleanup:
1304   FREE(&line);
1305
1306   if (fp_in && (fp_in != fp))
1307     mutt_file_fclose(&fp_in);
1308   if (fp_out)
1309     mutt_file_fclose(&fp_out);
1310   else
1311     return;
1312
1313   a->encoding = ENC_7BIT;
1314   FREE(&a->d_filename);
1315   a->d_filename = a->filename;
1316   if (a->filename && a->unlink)
1317     unlink(a->filename);
1318   a->filename = mutt_str_strdup(temp);
1319   a->unlink = true;
1320   if (stat(a->filename, &sb) == -1)
1321   {
1322     mutt_perror("stat");
1323     return;
1324   }
1325   a->length = sb.st_size;
1326   mutt_body_free(&a->parts);
1327   a->email->content = NULL;
1328 }
1329
1330 /**
1331  * set_encoding - determine which Content-Transfer-Encoding to use
1332  * @param[in]  b    Body of email
1333  * @param[out] info Info about the email
1334  */
1335 static void set_encoding(struct Body *b, struct Content *info)
1336 {
1337   if (b->type == TYPE_TEXT)
1338   {
1339     char send_charset[128];
1340     char *chsname = mutt_body_get_charset(b, send_charset, sizeof(send_charset));
1341     if ((info->lobin && !mutt_str_startswith(chsname, "iso-2022", CASE_IGNORE)) ||
1342         (info->linemax > 990) || (info->from && C_EncodeFrom))
1343     {
1344       b->encoding = ENC_QUOTED_PRINTABLE;
1345     }
1346     else if (info->hibin)
1347     {
1348       b->encoding = C_Allow8bit ? ENC_8BIT : ENC_QUOTED_PRINTABLE;
1349     }
1350     else
1351     {
1352       b->encoding = ENC_7BIT;
1353     }
1354   }
1355   else if ((b->type == TYPE_MESSAGE) || (b->type == TYPE_MULTIPART))
1356   {
1357     if (info->lobin || info->hibin)
1358     {
1359       if (C_Allow8bit && !info->lobin)
1360         b->encoding = ENC_8BIT;
1361       else
1362         mutt_message_to_7bit(b, NULL);
1363     }
1364     else
1365       b->encoding = ENC_7BIT;
1366   }
1367   else if ((b->type == TYPE_APPLICATION) &&
1368            (mutt_str_strcasecmp(b->subtype, "pgp-keys") == 0))
1369   {
1370     b->encoding = ENC_7BIT;
1371   }
1372   else
1373   {
1374     /* Determine which encoding is smaller  */
1375     if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1376         3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1377     {
1378       b->encoding = ENC_BASE64;
1379     }
1380     else
1381     {
1382       b->encoding = ENC_QUOTED_PRINTABLE;
1383     }
1384   }
1385 }
1386
1387 /**
1388  * mutt_stamp_attachment - Timestamp an Attachment
1389  * @param a Attachment
1390  */
1391 void mutt_stamp_attachment(struct Body *a)
1392 {
1393   a->stamp = time(NULL);
1394 }
1395
1396 /**
1397  * mutt_body_get_charset - Get a body's character set
1398  * @param b      Body to examine
1399  * @param buf    Buffer for the result
1400  * @param buflen Length of the buffer
1401  * @retval ptr  Buffer containing character set
1402  * @retval NULL On error, or if not a text type
1403  */
1404 char *mutt_body_get_charset(struct Body *b, char *buf, size_t buflen)
1405 {
1406   char *p = NULL;
1407
1408   if (b && (b->type != TYPE_TEXT))
1409     return NULL;
1410
1411   if (b)
1412     p = mutt_param_get(&b->parameter, "charset");
1413
1414   if (p)
1415     mutt_ch_canonical_charset(buf, buflen, p);
1416   else
1417     mutt_str_strfcpy(buf, "us-ascii", buflen);
1418
1419   return buf;
1420 }
1421
1422 /**
1423  * mutt_update_encoding - Update the encoding type
1424  * @param a Body to update
1425  *
1426  * Assumes called from send mode where Body->filename points to actual file
1427  */
1428 void mutt_update_encoding(struct Body *a)
1429 {
1430   struct Content *info = NULL;
1431   char chsbuf[256];
1432
1433   /* override noconv when it's us-ascii */
1434   if (mutt_ch_is_us_ascii(mutt_body_get_charset(a, chsbuf, sizeof(chsbuf))))
1435     a->noconv = false;
1436
1437   if (!a->force_charset && !a->noconv)
1438     mutt_param_delete(&a->parameter, "charset");
1439
1440   info = mutt_get_content_info(a->filename, a);
1441   if (!info)
1442     return;
1443
1444   set_encoding(a, info);
1445   mutt_stamp_attachment(a);
1446
1447   FREE(&a->content);
1448   a->content = info;
1449 }
1450
1451 /**
1452  * mutt_make_message_attach - Create a message attachment
1453  * @param m          Mailbox
1454  * @param e          Email
1455  * @param attach_msg true if attaching a message
1456  * @retval ptr  Newly allocated Body
1457  * @retval NULL Error
1458  */
1459 struct Body *mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool attach_msg)
1460 {
1461   char buf[1024];
1462   struct Body *body = NULL;
1463   FILE *fp = NULL;
1464   CopyMessageFlags cmflags;
1465   SecurityFlags pgp = WithCrypto ? e->security : SEC_NO_FLAGS;
1466
1467   if (WithCrypto)
1468   {
1469     if ((C_MimeForwardDecode || C_ForwardDecrypt) && (e->security & SEC_ENCRYPT))
1470     {
1471       if (!crypt_valid_passphrase(e->security))
1472         return NULL;
1473     }
1474   }
1475
1476   mutt_mktemp(buf, sizeof(buf));
1477   fp = mutt_file_fopen(buf, "w+");
1478   if (!fp)
1479     return NULL;
1480
1481   body = mutt_body_new();
1482   body->type = TYPE_MESSAGE;
1483   body->subtype = mutt_str_strdup("rfc822");
1484   body->filename = mutt_str_strdup(buf);
1485   body->unlink = true;
1486   body->use_disp = false;
1487   body->disposition = DISP_INLINE;
1488   body->noconv = true;
1489
1490   mutt_parse_mime_message(m, e);
1491
1492   CopyHeaderFlags chflags = CH_XMIT;
1493   cmflags = MUTT_CM_NO_FLAGS;
1494
1495   /* If we are attaching a message, ignore C_MimeForwardDecode */
1496   if (!attach_msg && C_MimeForwardDecode)
1497   {
1498     chflags |= CH_MIME | CH_TXTPLAIN;
1499     cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1500     if (WithCrypto & APPLICATION_PGP)
1501       pgp &= ~PGP_ENCRYPT;
1502     if (WithCrypto & APPLICATION_SMIME)
1503       pgp &= ~SMIME_ENCRYPT;
1504   }
1505   else if ((WithCrypto != 0) && C_ForwardDecrypt && (e->security & SEC_ENCRYPT))
1506   {
1507     if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_is_multipart_encrypted(e->content))
1508     {
1509       chflags |= CH_MIME | CH_NONEWLINE;
1510       cmflags = MUTT_CM_DECODE_PGP;
1511       pgp &= ~PGP_ENCRYPT;
1512     }
1513     else if (((WithCrypto & APPLICATION_PGP) != 0) &&
1514              ((mutt_is_application_pgp(e->content) & PGP_ENCRYPT) == PGP_ENCRYPT))
1515     {
1516       chflags |= CH_MIME | CH_TXTPLAIN;
1517       cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1518       pgp &= ~PGP_ENCRYPT;
1519     }
1520     else if (((WithCrypto & APPLICATION_SMIME) != 0) &&
1521              ((mutt_is_application_smime(e->content) & SMIME_ENCRYPT) == SMIME_ENCRYPT))
1522     {
1523       chflags |= CH_MIME | CH_TXTPLAIN;
1524       cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1525       pgp &= ~SMIME_ENCRYPT;
1526     }
1527   }
1528
1529   mutt_copy_message_ctx(fp, m, e, cmflags, chflags);
1530
1531   fflush(fp);
1532   rewind(fp);
1533
1534   body->email = mutt_email_new();
1535   body->email->offset = 0;
1536   /* we don't need the user headers here */
1537   body->email->env = mutt_rfc822_read_header(fp, body->email, false, false);
1538   if (WithCrypto)
1539     body->email->security = pgp;
1540   mutt_update_encoding(body);
1541   body->parts = body->email->content;
1542
1543   mutt_file_fclose(&fp);
1544
1545   return body;
1546 }
1547
1548 /**
1549  * run_mime_type_query - Run an external command to determine the MIME type
1550  * @param att Attachment
1551  *
1552  * The command in $mime_type_query_command is run.
1553  */
1554 static void run_mime_type_query(struct Body *att)
1555 {
1556   FILE *fp, *fp_err;
1557   char cmd[STR_COMMAND];
1558   char *buf = NULL;
1559   size_t buflen;
1560   int dummy = 0;
1561   pid_t pid;
1562
1563   mutt_file_expand_fmt_quote(cmd, sizeof(cmd), C_MimeTypeQueryCommand, att->filename);
1564
1565   pid = mutt_create_filter(cmd, NULL, &fp, &fp_err);
1566   if (pid < 0)
1567   {
1568     mutt_error(_("Error running \"%s\""), cmd);
1569     return;
1570   }
1571
1572   buf = mutt_file_read_line(buf, &buflen, fp, &dummy, 0);
1573   if (buf)
1574   {
1575     if (strchr(buf, '/'))
1576       mutt_parse_content_type(buf, att);
1577     FREE(&buf);
1578   }
1579
1580   mutt_file_fclose(&fp);
1581   mutt_file_fclose(&fp_err);
1582   mutt_wait_filter(pid);
1583 }
1584
1585 /**
1586  * mutt_make_file_attach - Create a file attachment
1587  * @param path File to attach
1588  * @retval ptr  Newly allocated Body
1589  * @retval NULL Error
1590  */
1591 struct Body *mutt_make_file_attach(const char *path)
1592 {
1593   struct Body *att = mutt_body_new();
1594   att->filename = mutt_str_strdup(path);
1595
1596   if (C_MimeTypeQueryCommand && *C_MimeTypeQueryCommand && C_MimeTypeQueryFirst)
1597     run_mime_type_query(att);
1598
1599   /* Attempt to determine the appropriate content-type based on the filename
1600    * suffix.  */
1601   if (!att->subtype)
1602     mutt_lookup_mime_type(att, path);
1603
1604   if (!att->subtype && C_MimeTypeQueryCommand && *C_MimeTypeQueryCommand && !C_MimeTypeQueryFirst)
1605   {
1606     run_mime_type_query(att);
1607   }
1608
1609   struct Content *info = mutt_get_content_info(path, att);
1610   if (!info)
1611   {
1612     mutt_body_free(&att);
1613     return NULL;
1614   }
1615
1616   if (!att->subtype)
1617   {
1618     if ((info->nulbin == 0) &&
1619         ((info->lobin == 0) || ((info->lobin + info->hibin + info->ascii) / info->lobin >= 10)))
1620     {
1621       /* Statistically speaking, there should be more than 10% "lobin"
1622        * chars if this is really a binary file...  */
1623       att->type = TYPE_TEXT;
1624       att->subtype = mutt_str_strdup("plain");
1625     }
1626     else
1627     {
1628       att->type = TYPE_APPLICATION;
1629       att->subtype = mutt_str_strdup("octet-stream");
1630     }
1631   }
1632
1633   FREE(&info);
1634   mutt_update_encoding(att);
1635   return att;
1636 }
1637
1638 /**
1639  * get_toplevel_encoding - Find the most restrictive encoding type
1640  * @param a Body to examine
1641  * @retval num Encoding type, e.g. #ENC_7BIT
1642  */
1643 static int get_toplevel_encoding(struct Body *a)
1644 {
1645   int e = ENC_7BIT;
1646
1647   for (; a; a = a->next)
1648   {
1649     if (a->encoding == ENC_BINARY)
1650       return ENC_BINARY;
1651     else if (a->encoding == ENC_8BIT)
1652       e = ENC_8BIT;
1653   }
1654
1655   return e;
1656 }
1657
1658 /**
1659  * check_boundary - check for duplicate boundary
1660  * @param boundary Boundary to look for
1661  * @param b        Body parts to check
1662  * @retval true if duplicate found
1663  */
1664 static bool check_boundary(const char *boundary, struct Body *b)
1665 {
1666   char *p = NULL;
1667
1668   if (b->parts && check_boundary(boundary, b->parts))
1669     return true;
1670
1671   if (b->next && check_boundary(boundary, b->next))
1672     return true;
1673
1674   p = mutt_param_get(&b->parameter, "boundary");
1675   if (p && (mutt_str_strcmp(p, boundary) == 0))
1676   {
1677     return true;
1678   }
1679   return false;
1680 }
1681
1682 /**
1683  * mutt_make_multipart - Create a multipart email
1684  * @param b Body of email to start
1685  * @retval ptr Newly allocated Body
1686  */
1687 struct Body *mutt_make_multipart(struct Body *b)
1688 {
1689   struct Body *new = mutt_body_new();
1690   new->type = TYPE_MULTIPART;
1691   new->subtype = mutt_str_strdup("mixed");
1692   new->encoding = get_toplevel_encoding(b);
1693   do
1694   {
1695     mutt_generate_boundary(&new->parameter);
1696     if (check_boundary(mutt_param_get(&new->parameter, "boundary"), b))
1697       mutt_param_delete(&new->parameter, "boundary");
1698   } while (!mutt_param_get(&new->parameter, "boundary"));
1699   new->use_disp = false;
1700   new->disposition = DISP_INLINE;
1701   new->parts = b;
1702
1703   return new;
1704 }
1705
1706 /**
1707  * mutt_remove_multipart - Extract the multipart body if it exists
1708  * @param b Body to alter
1709  * @retval ptr The parts of the Body
1710  *
1711  * @note The original Body is freed
1712  */
1713 struct Body *mutt_remove_multipart(struct Body *b)
1714 {
1715   struct Body *t = NULL;
1716
1717   if (b->parts)
1718   {
1719     t = b;
1720     b = b->parts;
1721     t->parts = NULL;
1722     mutt_body_free(&t);
1723   }
1724   return b;
1725 }
1726
1727 /**
1728  * mutt_write_address_list - wrapper around mutt_write_address()
1729  * @param addr    Address list
1730  * @param fp      File to write to
1731  * @param linelen Line length to use
1732  * @param display True if these addresses will be displayed to the user
1733  *
1734  * So we can handle very large recipient lists without needing a huge temporary
1735  * buffer in memory
1736  */
1737 void mutt_write_address_list(struct Address *addr, FILE *fp, int linelen, bool display)
1738 {
1739   struct Address *tmp = NULL;
1740   char buf[1024];
1741   int count = 0;
1742
1743   while (addr)
1744   {
1745     tmp = addr->next;
1746     addr->next = NULL;
1747     buf[0] = '\0';
1748     mutt_addr_write(buf, sizeof(buf), addr, display);
1749     size_t len = mutt_str_strlen(buf);
1750     if (count && (linelen + len > 74))
1751     {
1752       fputs("\n\t", fp);
1753       linelen = len + 8; /* tab is usually about 8 spaces... */
1754     }
1755     else
1756     {
1757       if (count && addr->mailbox)
1758       {
1759         fputc(' ', fp);
1760         linelen++;
1761       }
1762       linelen += len;
1763     }
1764     fputs(buf, fp);
1765     addr->next = tmp;
1766     if (!addr->group && addr->next && addr->next->mailbox)
1767     {
1768       linelen++;
1769       fputc(',', fp);
1770     }
1771     addr = addr->next;
1772     count++;
1773   }
1774   fputc('\n', fp);
1775 }
1776
1777 /**
1778  * mutt_write_references - Add the message references to a list
1779  * @param r    String List of references
1780  * @param fp   File to write to
1781  * @param trim Trim the list to at most this many items
1782  *
1783  * Write the list in reverse because they are stored in reverse order when
1784  * parsed to speed up threading.
1785  */
1786 void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim)
1787 {
1788   struct ListNode *np = NULL;
1789   size_t length = 0;
1790
1791   STAILQ_FOREACH(np, r, entries)
1792   {
1793     if (++length == trim)
1794       break;
1795   }
1796
1797   struct ListNode **ref = mutt_mem_calloc(length, sizeof(struct ListNode *));
1798
1799   // store in reverse order
1800   size_t tmp = length;
1801   STAILQ_FOREACH(np, r, entries)
1802   {
1803     ref[--tmp] = np;
1804     if (tmp == 0)
1805       break;
1806   }
1807
1808   for (size_t i = 0; i < length; i++)
1809   {
1810     fputc(' ', fp);
1811     fputs(ref[i]->data, fp);
1812     if (i != length - 1)
1813       fputc('\n', fp);
1814   }
1815
1816   FREE(&ref);
1817 }
1818
1819 /**
1820  * print_val - Add pieces to an email header, wrapping where necessary
1821  * @param fp      File to write to
1822  * @param pfx     Prefix for headers
1823  * @param value   Text to be added
1824  * @param chflags Flags, see #CopyHeaderFlags
1825  * @param col     Column that this text starts at
1826  * @retval  0 Success
1827  * @retval -1 Failure
1828  */
1829 static int print_val(FILE *fp, const char *pfx, const char *value,
1830                      CopyHeaderFlags chflags, size_t col)
1831 {
1832   while (value && *value)
1833   {
1834     if (fputc(*value, fp) == EOF)
1835       return -1;
1836     /* corner-case: break words longer than 998 chars by force,
1837      * mandated by RFC5322 */
1838     if (!(chflags & CH_DISPLAY) && (++col >= 998))
1839     {
1840       if (fputs("\n ", fp) < 0)
1841         return -1;
1842       col = 1;
1843     }
1844     if (*value == '\n')
1845     {
1846       if (*(value + 1) && pfx && *pfx && (fputs(pfx, fp) == EOF))
1847         return -1;
1848       /* for display, turn folding spaces into folding tabs */
1849       if ((chflags & CH_DISPLAY) && ((*(value + 1) == ' ') || (*(value + 1) == '\t')))
1850       {
1851         value++;
1852         while (*value && (*value == ' ' || *value == '\t'))
1853           value++;
1854         if (fputc('\t', fp) == EOF)
1855           return -1;
1856         continue;
1857       }
1858     }
1859     value++;
1860   }
1861   return 0;
1862 }
1863
1864 /**
1865  * fold_one_header - Fold one header line
1866  * @param fp      File to write to
1867  * @param tag     Header key, e.g. "From"
1868  * @param value   Header value
1869  * @param pfx     Prefix for header
1870  * @param wraplen Column to wrap at
1871  * @param chflags Flags, see #CopyHeaderFlags
1872  * @retval  0 Success
1873  * @retval -1 Failure
1874  */
1875 static int fold_one_header(FILE *fp, const char *tag, const char *value,
1876                            const char *pfx, int wraplen, CopyHeaderFlags chflags)
1877 {
1878   const char *p = value;
1879   char buf[8192] = "";
1880   int first = 1, col = 0, l = 0;
1881   const bool display = (chflags & CH_DISPLAY);
1882
1883   mutt_debug(5, "pfx=[%s], tag=[%s], flags=%d value=[%s]\n", pfx, tag, chflags,
1884              NONULL(value));
1885
1886   if (tag && *tag && (fprintf(fp, "%s%s: ", NONULL(pfx), tag) < 0))
1887     return -1;
1888   col = mutt_str_strlen(tag) + (tag && *tag ? 2 : 0) + mutt_str_strlen(pfx);
1889
1890   while (p && *p)
1891   {
1892     int fold = 0;
1893
1894     /* find the next word and place it in 'buf'. it may start with
1895      * whitespace we can fold before */
1896     const char *next = mutt_str_find_word(p);
1897     l = MIN(sizeof(buf) - 1, next - p);
1898     memcpy(buf, p, l);
1899     buf[l] = '\0';
1900
1901     /* determine width: character cells for display, bytes for sending
1902      * (we get pure ascii only) */
1903     const int w = mutt_mb_width(buf, col, display);
1904     const int enc = mutt_str_startswith(buf, "=?", CASE_MATCH);
1905
1906     mutt_debug(5, "word=[%s], col=%d, w=%d, next=[0x0%x]\n", buf, col, w, *next);
1907
1908     /* insert a folding \n before the current word's lwsp except for
1909      * header name, first word on a line (word longer than wrap width)
1910      * and encoded words */
1911     if (!first && !enc && col && (col + w >= wraplen))
1912     {
1913       col = mutt_str_strlen(pfx);
1914       fold = 1;
1915       if (fprintf(fp, "\n%s", NONULL(pfx)) <= 0)
1916         return -1;
1917     }
1918
1919     /* print the actual word; for display, ignore leading ws for word
1920      * and fold with tab for readability */
1921     if (display && fold)
1922     {
1923       char *pc = buf;
1924       while (*pc && (*pc == ' ' || *pc == '\t'))
1925       {
1926         pc++;
1927         col--;
1928       }
1929       if (fputc('\t', fp) == EOF)
1930         return -1;
1931       if (print_val(fp, pfx, pc, chflags, col) < 0)
1932         return -1;
1933       col += 8;
1934     }
1935     else if (print_val(fp, pfx, buf, chflags, col) < 0)
1936       return -1;
1937     col += w;
1938
1939     /* if the current word ends in \n, ignore all its trailing spaces
1940      * and reset column; this prevents us from putting only spaces (or
1941      * even none) on a line if the trailing spaces are located at our
1942      * current line width
1943      * XXX this covers ASCII space only, for display we probably
1944      * want something like iswspace() here */
1945     const char *sp = next;
1946     while (*sp && (*sp == ' ' || *sp == '\t'))
1947       sp++;
1948     if (*sp == '\n')
1949     {
1950       next = sp;
1951       col = 0;
1952     }
1953
1954     p = next;
1955     first = 0;
1956   }
1957
1958   /* if we have printed something but didn't \n-terminate it, do it
1959    * except the last word we printed ended in \n already */
1960   if (col && ((l == 0) || (buf[l - 1] != '\n')))
1961     if (putc('\n', fp) == EOF)
1962       return -1;
1963
1964   return 0;
1965 }
1966
1967 /**
1968  * unfold_header - Unfold a wrapped email header
1969  * @param s String to process
1970  * @retval ptr Unfolded string
1971  *
1972  * @note The string is altered in-place
1973  */
1974 static char *unfold_header(char *s)
1975 {
1976   char *p = s, *q = s;
1977
1978   while (p && *p)
1979   {
1980     /* remove CRLF prior to FWSP, turn \t into ' ' */
1981     if ((*p == '\r') && *(p + 1) && (*(p + 1) == '\n') && *(p + 2) &&
1982         ((*(p + 2) == ' ') || (*(p + 2) == '\t')))
1983     {
1984       *q++ = ' ';
1985       p += 3;
1986       continue;
1987     }
1988     /* remove LF prior to FWSP, turn \t into ' ' */
1989     else if ((*p == '\n') && *(p + 1) && ((*(p + 1) == ' ') || (*(p + 1) == '\t')))
1990     {
1991       *q++ = ' ';
1992       p += 2;
1993       continue;
1994     }
1995     *q++ = *p++;
1996   }
1997   if (q)
1998     *q = '\0';
1999
2000   return s;
2001 }
2002
2003 /**
2004  * write_one_header - Write out one header line
2005  * @param fp      File to write to
2006  * @param pfxw    Width of prefix string
2007  * @param max     Max width
2008  * @param wraplen Column to wrap at
2009  * @param pfx     Prefix for header
2010  * @param start   Start of header line
2011  * @param end     End of header line
2012  * @param chflags Flags, see #CopyHeaderFlags
2013  * @retval  0 Success
2014  * @retval -1 Failure
2015  */
2016 static int write_one_header(FILE *fp, int pfxw, int max, int wraplen, const char *pfx,
2017                             const char *start, const char *end, CopyHeaderFlags chflags)
2018 {
2019   char *tagbuf = NULL, *valbuf = NULL, *t = NULL;
2020   bool is_from = ((end - start) > 5) && mutt_str_startswith(start, "from ", CASE_IGNORE);
2021
2022   /* only pass through folding machinery if necessary for sending,
2023    * never wrap From_ headers on sending */
2024   if (!(chflags & CH_DISPLAY) && ((pfxw + max <= wraplen) || is_from))
2025   {
2026     valbuf = mutt_str_substr_dup(start, end);
2027     mutt_debug(5, "buf[%s%s] short enough, max width = %d <= %d\n", NONULL(pfx),
2028                valbuf, max, wraplen);
2029     if (pfx && *pfx)
2030     {
2031       if (fputs(pfx, fp) == EOF)
2032       {
2033         FREE(&valbuf);
2034         return -1;
2035       }
2036     }
2037
2038     t = strchr(valbuf, ':');
2039     if (!t)
2040     {
2041       mutt_debug(LL_DEBUG1, "#1 warning: header not in 'key: value' format!\n");
2042       FREE(&valbuf);
2043       return 0;
2044     }
2045     if (print_val(fp, pfx, valbuf, chflags, mutt_str_strlen(pfx)) < 0)
2046     {
2047       FREE(&valbuf);
2048       return -1;
2049     }
2050     FREE(&valbuf);
2051   }
2052   else
2053   {
2054     t = strchr(start, ':');
2055     if (!t || (t > end))
2056     {
2057       mutt_debug(LL_DEBUG1, "#2 warning: header not in 'key: value' format!\n");
2058       return 0;
2059     }
2060     if (is_from)
2061     {
2062       tagbuf = NULL;
2063       valbuf = mutt_str_substr_dup(start, end);
2064     }
2065     else
2066     {
2067       tagbuf = mutt_str_substr_dup(start, t);
2068       /* skip over the colon separating the header field name and value */
2069       t++;
2070
2071       /* skip over any leading whitespace (WSP, as defined in RFC5322)
2072        * NOTE: mutt_str_skip_email_wsp() does the wrong thing here.
2073        *       See tickets 3609 and 3716. */
2074       while (*t == ' ' || *t == '\t')
2075         t++;
2076
2077       valbuf = mutt_str_substr_dup(t, end);
2078     }
2079     mutt_debug(LL_DEBUG2, "buf[%s%s] too long, max width = %d > %d\n",
2080                NONULL(pfx), NONULL(valbuf), max, wraplen);
2081     if (fold_one_header(fp, tagbuf, valbuf, pfx, wraplen, chflags) < 0)
2082     {
2083       FREE(&valbuf);
2084       FREE(&tagbuf);
2085       return -1;
2086     }
2087     FREE(&tagbuf);
2088     FREE(&valbuf);
2089   }
2090   return 0;
2091 }
2092
2093 /**
2094  * mutt_write_one_header - Write one header line to a file
2095  * @param fp      File to write to
2096  * @param tag     Header key, e.g. "From"
2097  * @param value   Header value
2098  * @param pfx     Prefix for header
2099  * @param wraplen Column to wrap at
2100  * @param chflags Flags, see #CopyHeaderFlags
2101  * @retval  0 Success
2102  * @retval -1 Failure
2103  *
2104  * split several headers into individual ones and call write_one_header
2105  * for each one
2106  */
2107 int mutt_write_one_header(FILE *fp, const char *tag, const char *value,
2108                           const char *pfx, int wraplen, CopyHeaderFlags chflags)
2109 {
2110   char *p = (char *) value, *last = NULL, *line = NULL;
2111   int max = 0, w, rc = -1;
2112   int pfxw = mutt_strwidth(pfx);
2113   char *v = mutt_str_strdup(value);
2114   bool display = (chflags & CH_DISPLAY);
2115
2116   if (!display || C_Weed)
2117     v = unfold_header(v);
2118
2119   /* when not displaying, use sane wrap value */
2120   if (!display)
2121   {
2122     if ((C_WrapHeaders < 78) || (C_WrapHeaders > 998))
2123       wraplen = 78;
2124     else
2125       wraplen = C_WrapHeaders;
2126   }
2127   else if ((wraplen <= 0) || (wraplen > MuttIndexWindow->cols))
2128     wraplen = MuttIndexWindow->cols;
2129
2130   if (tag)
2131   {
2132     /* if header is short enough, simply print it */
2133     if (!display && (mutt_strwidth(tag) + 2 + pfxw + mutt_strwidth(v) <= wraplen))
2134     {
2135       mutt_debug(5, "buf[%s%s: %s] is short enough\n", NONULL(pfx), tag, v);
2136       if (fprintf(fp, "%s%s: %s\n", NONULL(pfx), tag, v) <= 0)
2137         goto out;
2138       rc = 0;
2139       goto out;
2140     }
2141     else
2142     {
2143       rc = fold_one_header(fp, tag, v, pfx, wraplen, chflags);
2144       goto out;
2145     }
2146   }
2147
2148   p = v;
2149   last = v;
2150   line = v;
2151   while (p && *p)
2152   {
2153     p = strchr(p, '\n');
2154
2155     /* find maximum line width in current header */
2156     if (p)
2157       *p = '\0';
2158     w = mutt_mb_width(line, 0, display);
2159     if (w > max)
2160       max = w;
2161     if (p)
2162       *p = '\n';
2163
2164     if (!p)
2165       break;
2166
2167     line = ++p;
2168     if ((*p != ' ') && (*p != '\t'))
2169     {
2170       if (write_one_header(fp, pfxw, max, wraplen, pfx, last, p, chflags) < 0)
2171         goto out;
2172       last = p;
2173       max = 0;
2174     }
2175   }
2176
2177   if (last && *last)
2178     if (write_one_header(fp, pfxw, max, wraplen, pfx, last, p, chflags) < 0)
2179       goto out;
2180
2181   rc = 0;
2182
2183 out:
2184   FREE(&v);
2185   return rc;
2186 }
2187
2188 /**
2189  * mutt_rfc822_write_header - Write out one RFC822 header line
2190  * @param fp      File to write to
2191  * @param env     Envelope of email
2192  * @param attach  Attachment
2193  * @param mode    Mode, see #MuttWriteHeaderMode
2194  * @param privacy If true, remove headers that might identify the user
2195  * @param hide_protected_subject If true, replace subject header
2196  * @retval  0 Success
2197  * @retval -1 Failure
2198  *
2199  * Note: all RFC2047 encoding should be done outside of this routine, except
2200  * for the "real name."  This will allow this routine to be used more than
2201  * once, if necessary.
2202  *
2203  * Likewise, all IDN processing should happen outside of this routine.
2204  *
2205  * privacy true => will omit any headers which may identify the user.
2206  *               Output generated is suitable for being sent through
2207  *               anonymous remailer chains.
2208  *
2209  * hide_protected_subject: replaces the Subject header with
2210  * $crypt_protected_headers_subject in NORMAL or POSTPONE mode.
2211  */
2212 int mutt_rfc822_write_header(FILE *fp, struct Envelope *env,
2213                              struct Body *attach, enum MuttWriteHeaderMode mode,
2214                              bool privacy, bool hide_protected_subject)
2215 {
2216   char buf[1024];
2217   char *p = NULL, *q = NULL;
2218   bool has_agent = false; /* user defined user-agent header field exists */
2219
2220   if ((mode == MUTT_WRITE_HEADER_NORMAL) && !privacy)
2221     fputs(mutt_date_make_date(buf, sizeof(buf)), fp);
2222
2223   /* UseFrom is not consulted here so that we can still write a From:
2224    * field if the user sets it with the 'my_hdr' command */
2225   if (env->from && !privacy)
2226   {
2227     buf[0] = '\0';
2228     mutt_addr_write(buf, sizeof(buf), env->from, false);
2229     fprintf(fp, "From: %s\n", buf);
2230   }
2231
2232   if (env->sender && !privacy)
2233   {
2234     buf[0] = '\0';
2235     mutt_addr_write(buf, sizeof(buf), env->sender, false);
2236     fprintf(fp, "Sender: %s\n", buf);
2237   }
2238
2239   if (env->to)
2240   {
2241     fputs("To: ", fp);
2242     mutt_write_address_list(env->to, fp, 4, 0);
2243   }
2244   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2245 #ifdef USE_NNTP
2246     if (!OptNewsSend)
2247 #endif
2248       fputs("To:\n", fp);
2249
2250   if (env->cc)
2251   {
2252     fputs("Cc: ", fp);
2253     mutt_write_address_list(env->cc, fp, 4, 0);
2254   }
2255   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2256 #ifdef USE_NNTP
2257     if (!OptNewsSend)
2258 #endif
2259       fputs("Cc:\n", fp);
2260
2261   if (env->bcc)
2262   {
2263     if ((mode == MUTT_WRITE_HEADER_POSTPONE) || (mode == MUTT_WRITE_HEADER_EDITHDRS) ||
2264         ((mode == MUTT_WRITE_HEADER_NORMAL) && C_WriteBcc))
2265     {
2266       fputs("Bcc: ", fp);
2267       mutt_write_address_list(env->bcc, fp, 5, 0);
2268     }
2269   }
2270   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2271 #ifdef USE_NNTP
2272     if (!OptNewsSend)
2273 #endif
2274       fputs("Bcc:\n", fp);
2275
2276 #ifdef USE_NNTP
2277   if (env->newsgroups)
2278     fprintf(fp, "Newsgroups: %s\n", env->newsgroups);
2279   else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
2280     fputs("Newsgroups:\n", fp);
2281
2282   if (env->followup_to)
2283     fprintf(fp, "Followup-To: %s\n", env->followup_to);
2284   else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend)
2285     fputs("Followup-To:\n", fp);
2286
2287   if (env->x_comment_to)
2288     fprintf(fp, "X-Comment-To: %s\n", env->x_comment_to);
2289   else if ((mode == MUTT_WRITE_HEADER_EDITHDRS) && OptNewsSend && C_XCommentTo)
2290     fputs("X-Comment-To:\n", fp);
2291 #endif
2292
2293   if (env->subject)
2294   {
2295     if (hide_protected_subject &&
2296         ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_POSTPONE)))
2297       mutt_write_one_header(fp, "Subject", C_CryptProtectedHeadersSubject, NULL, 0, 0);
2298     else
2299       mutt_write_one_header(fp, "Subject", env->subject, NULL, 0, 0);
2300   }
2301   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2302     fputs("Subject:\n", fp);
2303
2304   /* save message id if the user has set it */
2305   if (env->message_id && !privacy)
2306     fprintf(fp, "Message-ID: %s\n", env->message_id);
2307
2308   if (env->reply_to)
2309   {
2310     fputs("Reply-To: ", fp);
2311     mutt_write_address_list(env->reply_to, fp, 10, 0);
2312   }
2313   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2314     fputs("Reply-To:\n", fp);
2315
2316   if (env->mail_followup_to)
2317 #ifdef USE_NNTP
2318     if (!OptNewsSend)
2319 #endif
2320     {
2321       fputs("Mail-Followup-To: ", fp);
2322       mutt_write_address_list(env->mail_followup_to, fp, 18, 0);
2323     }
2324
2325   if ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_POSTPONE))
2326   {
2327     if (!STAILQ_EMPTY(&env->references))
2328     {
2329       fputs("References:", fp);
2330       mutt_write_references(&env->references, fp, 10);
2331       fputc('\n', fp);
2332     }
2333
2334     /* Add the MIME headers */
2335     fputs("MIME-Version: 1.0\n", fp);
2336     mutt_write_mime_header(attach, fp);
2337   }
2338
2339   if (!STAILQ_EMPTY(&env->in_reply_to))
2340   {
2341     fputs("In-Reply-To:", fp);
2342     mutt_write_references(&env->in_reply_to, fp, 0);
2343     fputc('\n', fp);
2344   }
2345
2346   /* Add any user defined headers */
2347   struct ListNode *tmp = NULL;
2348   STAILQ_FOREACH(tmp, &env->userhdrs, entries)
2349   {
2350     p = strchr(tmp->data, ':');
2351     if (p)
2352     {
2353       q = p;
2354
2355       *p = '\0';
2356
2357       p = mutt_str_skip_email_wsp(p + 1);
2358       if (!*p)
2359       {
2360         *q = ':';
2361         continue; /* don't emit empty fields. */
2362       }
2363
2364       /* check to see if the user has overridden the user-agent field */
2365       if (mutt_str_startswith(tmp->data, "user-agent", CASE_IGNORE))
2366       {
2367         has_agent = true;
2368         if (privacy)
2369         {
2370           *q = ':';
2371           continue;
2372         }
2373       }
2374
2375       mutt_write_one_header(fp, tmp->data, p, NULL, 0, 0);
2376       *q = ':';
2377     }
2378   }
2379
2380   if ((mode == MUTT_WRITE_HEADER_NORMAL) && !privacy && C_UserAgent && !has_agent)
2381   {
2382     /* Add a vanity header */
2383     fprintf(fp, "User-Agent: NeoMutt/%s%s\n", PACKAGE_VERSION, GitVer);
2384   }
2385
2386   return ferror(fp) == 0 ? 0 : -1;
2387 }
2388
2389 /**
2390  * encode_headers - RFC2047-encode a list of headers
2391  * @param h String List of headers
2392  *
2393  * The strings are encoded in-place.
2394  */
2395 static void encode_headers(struct ListHead *h)
2396 {
2397   char *tmp = NULL;
2398   char *p = NULL;
2399   int i;
2400
2401   struct ListNode *np = NULL;
2402   STAILQ_FOREACH(np, h, entries)
2403   {
2404     p = strchr(np->data, ':');
2405     if (!p)
2406       continue;
2407
2408     i = p - np->data;
2409     p = mutt_str_skip_email_wsp(p + 1);
2410     tmp = mutt_str_strdup(p);
2411
2412     if (!tmp)
2413       continue;
2414
2415     rfc2047_encode(&tmp, NULL, i + 2, C_SendCharset);
2416     mutt_mem_realloc(&np->data, i + 2 + mutt_str_strlen(tmp) + 1);
2417
2418     sprintf(np->data + i + 2, "%s", tmp);
2419
2420     FREE(&tmp);
2421   }
2422 }
2423
2424 /**
2425  * mutt_fqdn - Get the Fully-Qualified Domain Name
2426  * @param may_hide_host If true, hide the hostname (leaving just the domain)
2427  * @retval ptr  string pointer into Hostname
2428  * @retval NULL Error, e.g no Hostname
2429  *
2430  * @warning Do not free the returned pointer
2431  */
2432 const char *mutt_fqdn(bool may_hide_host)
2433 {
2434   if (!C_Hostname || (C_Hostname[0] == '@'))
2435     return NULL;
2436
2437   char *p = C_Hostname;
2438
2439   if (may_hide_host && C_HiddenHost)
2440   {
2441     p = strchr(C_Hostname, '.');
2442     if (p)
2443       p++;
2444
2445     // sanity check: don't hide the host if the fqdn is something like example.com
2446     if (!p || !strchr(p, '.'))
2447       p = C_Hostname;
2448   }
2449
2450   return p;
2451 }
2452
2453 /**
2454  * gen_msgid - Generate a unique Message ID
2455  * @retval ptr Message ID
2456  *
2457  * @note The caller should free the string
2458  */
2459 static char *gen_msgid(void)
2460 {
2461   char buf[128];
2462   unsigned char rndid[MUTT_RANDTAG_LEN + 1];
2463
2464   mutt_rand_base32(rndid, sizeof(rndid) - 1);
2465   rndid[MUTT_RANDTAG_LEN] = 0;
2466   const char *fqdn = mutt_fqdn(false);
2467   if (!fqdn)
2468     fqdn = NONULL(ShortHostname);
2469
2470   struct tm tm = mutt_date_gmtime(MUTT_DATE_NOW);
2471   snprintf(buf, sizeof(buf), "<%d%02d%02d%02d%02d%02d.%s@%s>", tm.tm_year + 1900,
2472            tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, rndid, fqdn);
2473   return mutt_str_strdup(buf);
2474 }
2475
2476 /**
2477  * alarm_handler - Async notification of an alarm signal
2478  * @param sig Signal, (SIGALRM)
2479  */
2480 static void alarm_handler(int sig)
2481 {
2482   SigAlrm = 1;
2483 }
2484
2485 /**
2486  * send_msg - invoke sendmail in a subshell
2487  * @param[in]  path     Path to program to execute
2488  * @param[in]  args     Arguments to pass to program
2489  * @param[in]  msg      Temp file containing message to send
2490  * @param[out] tempfile If sendmail is put in the background, this points
2491  *                      to the temporary file containing the stdout of the
2492  *                      child process. If it is NULL, stderr and stdout
2493  *                      are not redirected.
2494  * @retval  0 Success
2495  * @retval >0 Failure, return code from sendmail
2496  */
2497 static int send_msg(const char *path, char **args, const char *msg, char **tempfile)
2498 {
2499   sigset_t set;
2500   int st;
2501
2502   mutt_sig_block_system();
2503
2504   sigemptyset(&set);
2505   /* we also don't want to be stopped right now */
2506   sigaddset(&set, SIGTSTP);
2507   sigprocmask(SIG_BLOCK, &set, NULL);
2508
2509   if ((C_SendmailWait >= 0) && tempfile)
2510   {
2511     char tmp[PATH_MAX];
2512
2513     mutt_mktemp(tmp, sizeof(tmp));
2514     *tempfile = mutt_str_strdup(tmp);
2515   }
2516
2517   pid_t pid = fork();
2518   if (pid == 0)
2519   {
2520     struct sigaction act, oldalrm;
2521
2522     /* save parent's ID before setsid() */
2523     pid_t ppid = getppid();
2524
2525     /* we want the delivery to continue even after the main process dies,
2526      * so we put ourselves into another session right away */
2527     setsid();
2528
2529     /* next we close all open files */
2530     close(0);
2531 #ifdef OPEN_MAX
2532     for (int fd = tempfile ? 1 : 3; fd < OPEN_MAX; fd++)
2533       close(fd);
2534 #elif defined(_POSIX_OPEN_MAX)
2535     for (int fd = tempfile ? 1 : 3; fd < _POSIX_OPEN_MAX; fd++)
2536       close(fd);
2537 #else
2538     if (tempfile)
2539     {
2540       close(1);
2541       close(2);
2542     }
2543 #endif
2544
2545     /* now the second fork() */
2546     pid = fork();
2547     if (pid == 0)
2548     {
2549       /* "msg" will be opened as stdin */
2550       if (open(msg, O_RDONLY, 0) < 0)
2551       {
2552         unlink(msg);
2553         _exit(S_ERR);
2554       }
2555       unlink(msg);
2556
2557       if ((C_SendmailWait >= 0) && tempfile && *tempfile)
2558       {
2559         /* *tempfile will be opened as stdout */
2560         if (open(*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2561           _exit(S_ERR);
2562         /* redirect stderr to *tempfile too */
2563         if (dup(1) < 0)
2564           _exit(S_ERR);
2565       }
2566       else if (tempfile)
2567       {
2568         if (open("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
2569           _exit(S_ERR);
2570         if (open("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
2571           _exit(S_ERR);
2572       }
2573
2574       /* execvpe is a glibc extension */
2575       /* execvpe (path, args, mutt_envlist_getlist()); */
2576       execvp(path, args);
2577       _exit(S_ERR);
2578     }
2579     else if (pid == -1)
2580     {
2581       unlink(msg);
2582       FREE(tempfile);
2583       _exit(S_ERR);
2584     }
2585
2586     /* C_SendmailWait > 0: interrupt waitpid() after C_SendmailWait seconds
2587      * C_SendmailWait = 0: wait forever
2588      * C_SendmailWait < 0: don't wait */
2589     if (C_SendmailWait > 0)
2590     {
2591       SigAlrm = 0;
2592       act.sa_handler = alarm_handler;
2593 #ifdef SA_INTERRUPT
2594       /* need to make sure waitpid() is interrupted on SIGALRM */
2595       act.sa_flags = SA_INTERRUPT;
2596 #else
2597       act.sa_flags = 0;
2598 #endif
2599       sigemptyset(&act.sa_mask);
2600       sigaction(SIGALRM, &act, &oldalrm);
2601       alarm(C_SendmailWait);
2602     }
2603     else if (C_SendmailWait < 0)
2604       _exit(0xff & EX_OK);
2605
2606     if (waitpid(pid, &st, 0) > 0)
2607     {
2608       st = WIFEXITED(st) ? WEXITSTATUS(st) : S_ERR;
2609       if (C_SendmailWait && (st == (0xff & EX_OK)) && tempfile && *tempfile)
2610       {
2611         unlink(*tempfile); /* no longer needed */
2612         FREE(tempfile);
2613       }
2614     }
2615     else
2616     {
2617       st = (C_SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
2618       if ((C_SendmailWait > 0) && tempfile && *tempfile)
2619       {
2620         unlink(*tempfile);
2621         FREE(tempfile);
2622       }
2623     }
2624
2625     if (C_SendmailWait > 0)
2626     {
2627       /* reset alarm; not really needed, but... */
2628       alarm(0);
2629       sigaction(SIGALRM, &oldalrm, NULL);
2630     }
2631
2632     if ((kill(ppid, 0) == -1) && (errno == ESRCH) && tempfile && *tempfile)
2633     {
2634       /* the parent is already dead */
2635       unlink(*tempfile);
2636       FREE(tempfile);
2637     }
2638
2639     _exit(st);
2640   }
2641
2642   sigprocmask(SIG_UNBLOCK, &set, NULL);
2643
2644   if ((pid != -1) && (waitpid(pid, &st, 0) > 0))
2645     st = WIFEXITED(st) ? WEXITSTATUS(st) : S_ERR; /* return child status */
2646   else
2647     st = S_ERR; /* error */
2648
2649   mutt_sig_unblock_system(true);
2650
2651   return st;
2652 }
2653
2654 /**
2655  * add_args - Add an Address to a dynamic array
2656  * @param[out] args    Array to add to
2657  * @param[out] argslen Number of entries in array
2658  * @param[out] argsmax Allocated size of the array
2659  * @param[in]  addr    Address to add
2660  * @retval ptr Updated array
2661  */
2662 static char **add_args(char **args, size_t *argslen, size_t *argsmax, struct Address *addr)
2663 {
2664   for (; addr; addr = addr->next)
2665   {
2666     /* weed out group mailboxes, since those are for display only */
2667     if (addr->mailbox && !addr->group)
2668     {
2669       if (*argslen == *argsmax)
2670         mutt_mem_realloc(&args, (*argsmax += 5) * sizeof(char *));
2671       args[(*argslen)++] = addr->mailbox;
2672     }
2673   }
2674   return args;
2675 }
2676
2677 /**
2678  * add_option - Add a string to a dynamic array
2679  * @param[out] args    Array to add to
2680  * @param[out] argslen Number of entries in array
2681  * @param[out] argsmax Allocated size of the array
2682  * @param[in]  s       string to add
2683  * @retval ptr Updated array
2684  *
2685  * @note The array may be realloc()'d
2686  */
2687 static char **add_option(char **args, size_t *argslen, size_t *argsmax, char *s)
2688 {
2689   if (*argslen == *argsmax)
2690     mutt_mem_realloc(&args, (*argsmax += 5) * sizeof(char *));
2691   args[(*argslen)++] = s;
2692   return args;
2693 }
2694
2695 /**
2696  * mutt_invoke_sendmail - Run sendmail
2697  * @param from     The sender
2698  * @param to       Recipients
2699  * @param cc       Recipients
2700  * @param bcc      Recipients
2701  * @param msg      File containing message
2702  * @param eightbit Message contains 8bit chars
2703  * @retval  0 Success
2704  * @retval -1 Failure
2705  */
2706 int mutt_invoke_sendmail(struct Address *from, struct Address *to, struct Address *cc,
2707                          struct Address *bcc, const char *msg, int eightbit)
2708 {
2709   char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
2710   char **args = NULL;
2711   size_t argslen = 0, argsmax = 0;
2712   char **extra_args = NULL;
2713   int i;
2714
2715 #ifdef USE_NNTP
2716   if (OptNewsSend)
2717   {
2718     char cmd[1024];
2719
2720     mutt_expando_format(cmd, sizeof(cmd), 0, MuttIndexWindow->cols,
2721                         NONULL(C_Inews), nntp_format_str, 0, 0);
2722     if (!*cmd)
2723     {
2724       i = nntp_post(Context->mailbox, msg);
2725       unlink(msg);
2726       return i;
2727     }
2728
2729     s = mutt_str_strdup(cmd);
2730   }
2731   else
2732 #endif
2733     s = mutt_str_strdup(C_Sendmail);
2734
2735   /* ensure that $sendmail is set to avoid a crash. http://dev.mutt.org/trac/ticket/3548 */
2736   if (!s)
2737   {
2738     mutt_error(_("$sendmail must be set in order to send mail"));
2739     return -1;
2740   }
2741
2742   ps = s;
2743   i = 0;
2744   while ((ps = strtok(ps, " ")))
2745   {
2746     if (argslen == argsmax)
2747       mutt_mem_realloc(&args, sizeof(char *) * (argsmax += 5));
2748
2749     if (i)
2750     {
2751       if (mutt_str_strcmp(ps, "--") == 0)
2752         break;
2753       args[argslen++] = ps;
2754     }
2755     else
2756     {
2757       path = mutt_str_strdup(ps);
2758       ps = strrchr(ps, '/');
2759       if (ps)
2760         ps++;
2761       else
2762         ps = path;
2763       args[argslen++] = ps;
2764     }
2765     ps = NULL;
2766     i++;
2767   }
2768
2769 #ifdef USE_NNTP
2770   if (!OptNewsSend)
2771   {
2772 #endif
2773     size_t extra_argslen = 0;
2774     /* If C_Sendmail contained a "--", we save the recipients to append to
2775    * args after other possible options added below. */
2776     if (ps)
2777     {
2778       ps = NULL;
2779       size_t extra_argsmax = 0;
2780       while ((ps = strtok(ps, " ")))
2781       {
2782         if (extra_argslen == extra_argsmax)
2783           mutt_mem_realloc(&extra_args, sizeof(char *) * (extra_argsmax += 5));
2784
2785         extra_args[extra_argslen++] = ps;
2786         ps = NULL;
2787       }
2788     }
2789
2790     if (eightbit && C_Use8bitmime)
2791       args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
2792
2793     if (C_UseEnvelopeFrom)
2794     {
2795       if (C_EnvelopeFromAddress)
2796       {
2797         args = add_option(args, &argslen, &argsmax, "-f");
2798         args = add_args(args, &argslen, &argsmax, C_EnvelopeFromAddress);
2799       }
2800       else if (from && !from->next)
2801       {
2802         args = add_option(args, &argslen, &argsmax, "-f");
2803         args = add_args(args, &argslen, &argsmax, from);
2804       }
2805     }
2806
2807     if (C_DsnNotify)
2808     {
2809       args = add_option(args, &argslen, &argsmax, "-N");
2810       args = add_option(args, &argslen, &argsmax, C_DsnNotify);
2811     }
2812     if (C_DsnReturn)
2813     {
2814       args = add_option(args, &argslen, &argsmax, "-R");
2815       args = add_option(args, &argslen, &argsmax, C_DsnReturn);
2816     }
2817     args = add_option(args, &argslen, &argsmax, "--");
2818     for (i = 0; i < extra_argslen; i++)
2819       args = add_option(args, &argslen, &argsmax, extra_args[i]);
2820     args = add_args(args, &argslen, &argsmax, to);
2821     args = add_args(args, &argslen, &argsmax, cc);
2822     args = add_args(args, &argslen, &argsmax, bcc);
2823 #ifdef USE_NNTP
2824   }
2825 #endif
2826
2827   if (argslen == argsmax)
2828     mutt_mem_realloc(&args, sizeof(char *) * (++argsmax));
2829
2830   args[argslen++] = NULL;
2831
2832   /* Some user's $sendmail command uses gpg for password decryption,
2833    * and is set up to prompt using ncurses pinentry.  If we
2834    * mutt_endwin() it leaves other users staring at a blank screen.
2835    * So instead, just force a hard redraw on the next refresh. */
2836   if (!OptNoCurses)
2837     mutt_need_hard_redraw();
2838
2839   i = send_msg(path, args, msg, OptNoCurses ? NULL : &childout);
2840   if (i != (EX_OK & 0xff))
2841   {
2842     if (i != S_BKG)
2843     {
2844       const char *e = mutt_str_sysexit(i);
2845       mutt_error(_("Error sending message, child exited %d (%s)"), i, NONULL(e));
2846       if (childout)
2847       {
2848         struct stat st;
2849
2850         if ((stat(childout, &st) == 0) && (st.st_size > 0))
2851           mutt_do_pager(_("Output of the delivery process"), childout, 0, NULL);
2852       }
2853     }
2854   }
2855   else if (childout)
2856     unlink(childout);
2857
2858   FREE(&childout);
2859   FREE(&path);
2860   FREE(&s);
2861   FREE(&args);
2862   FREE(&extra_args);
2863
2864   if (i == (EX_OK & 0xff))
2865     i = 0;
2866   else if (i == S_BKG)
2867     i = 1;
2868   else
2869     i = -1;
2870   return i;
2871 }
2872
2873 /**
2874  * mutt_prepare_envelope - Prepare an email header
2875  * @param env   Envelope to prepare
2876  * @param final true if this email is going to be sent (not postponed)
2877  *
2878  * Encode all the headers prior to sending the email.
2879  *
2880  * For postponing (!final) do the necessary encodings only
2881  */
2882 void mutt_prepare_envelope(struct Envelope *env, bool final)
2883 {
2884   if (final)
2885   {
2886     if (env->bcc && !(env->to || env->cc))
2887     {
2888       /* some MTA's will put an Apparently-To: header field showing the Bcc:
2889        * recipients if there is no To: or Cc: field, so attempt to suppress
2890        * it by using an empty To: field.  */
2891       env->to = mutt_addr_new();
2892       env->to->group = 1;
2893       env->to->next = mutt_addr_new();
2894
2895       char buf[1024];
2896       buf[0] = '\0';
2897       mutt_addr_cat(buf, sizeof(buf), "undisclosed-recipients", AddressSpecials);
2898
2899       env->to->mailbox = mutt_str_strdup(buf);
2900     }
2901
2902     mutt_set_followup_to(env);
2903
2904     if (!env->message_id)
2905       env->message_id = gen_msgid();
2906   }
2907
2908   /* Take care of 8-bit => 7-bit conversion. */
2909   rfc2047_encode_envelope(env);
2910   encode_headers(&env->userhdrs);
2911 }
2912
2913 /**
2914  * mutt_unprepare_envelope - Undo the encodings of mutt_prepare_envelope()
2915  * @param env Envelope to unprepare
2916  *
2917  * Decode all the headers of an email, e.g. when the sending failed or was
2918  * aborted.
2919  */
2920 void mutt_unprepare_envelope(struct Envelope *env)
2921 {
2922   struct ListNode *item = NULL;
2923   STAILQ_FOREACH(item, &env->userhdrs, entries)
2924   {
2925     rfc2047_decode(&item->data);
2926   }
2927
2928   mutt_addr_free(&env->mail_followup_to);
2929
2930   /* back conversions */
2931   rfc2047_decode_envelope(env);
2932 }
2933
2934 /**
2935  * bounce_message - Bounce an email message
2936  * @param fp          Handle of message
2937  * @param e           Email
2938  * @param to          Address to bounce to
2939  * @param resent_from Address of new sender
2940  * @param env_from    Envelope of original sender
2941  * @retval  0 Success
2942  * @retval -1 Failure
2943  */
2944 static int bounce_message(FILE *fp, struct Email *e, struct Address *to,
2945                           const char *resent_from, struct Address *env_from)
2946 {
2947   if (!e)
2948     return -1;
2949
2950   int rc = 0;
2951   char tempfile[PATH_MAX];
2952
2953   mutt_mktemp(tempfile, sizeof(tempfile));
2954   FILE *fp_tmp = mutt_file_fopen(tempfile, "w");
2955   if (fp_tmp)
2956   {
2957     char date[128];
2958     CopyHeaderFlags chflags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2959
2960     if (!C_BounceDelivered)
2961       chflags |= CH_WEED_DELIVERED;
2962
2963     fseeko(fp, e->offset, SEEK_SET);
2964     fprintf(fp_tmp, "Resent-From: %s", resent_from);
2965     fprintf(fp_tmp, "\nResent-%s", mutt_date_make_date(date, sizeof(date)));
2966     char *msgid_str = gen_msgid();
2967     fprintf(fp_tmp, "Resent-Message-ID: %s\n", msgid_str);
2968     FREE(&msgid_str);
2969     fputs("Resent-To: ", fp_tmp);
2970     mutt_write_address_list(to, fp_tmp, 11, 0);
2971     mutt_copy_header(fp, e, fp_tmp, chflags, NULL);
2972     fputc('\n', fp_tmp);
2973     mutt_file_copy_bytes(fp, fp_tmp, e->content->length);
2974     if (mutt_file_fclose(&fp_tmp) != 0)
2975     {
2976       mutt_perror(tempfile);
2977       unlink(tempfile);
2978       return -1;
2979     }
2980 #ifdef USE_SMTP
2981     if (C_SmtpUrl)
2982       rc = mutt_smtp_send(env_from, to, NULL, NULL, tempfile, e->content->encoding == ENC_8BIT);
2983     else
2984 #endif
2985       rc = mutt_invoke_sendmail(env_from, to, NULL, NULL, tempfile,
2986                                 e->content->encoding == ENC_8BIT);
2987   }
2988
2989   return rc;
2990 }
2991
2992 /**
2993  * mutt_bounce_message - Bounce an email message
2994  * @param fp Handle of message
2995  * @param e  Email
2996  * @param to Address to bounce to
2997  * @retval  0 Success
2998  * @retval -1 Failure
2999  */
3000 int mutt_bounce_message(FILE *fp, struct Email *e, struct Address *to)
3001 {
3002   if (!fp || !e || !to)
3003     return -1;
3004
3005   const char *fqdn = mutt_fqdn(true);
3006   char resent_from[256];
3007   char *err = NULL;
3008
3009   resent_from[0] = '\0';
3010   struct Address *from = mutt_default_from();
3011
3012   /* mutt_default_from() does not use $realname if the real name is not set
3013    * in $from, so we add it here.  The reason it is not added in
3014    * mutt_default_from() is that during normal sending, we execute
3015    * send-hooks and set the realname last so that it can be changed based
3016    * upon message criteria.  */
3017   if (!from->personal)
3018     from->personal = mutt_str_strdup(C_Realname);
3019
3020   if (fqdn)
3021     mutt_addr_qualify(from, fqdn);
3022
3023   rfc2047_encode_addrlist(from, "Resent-From");
3024   if (mutt_addrlist_to_intl(from, &err))
3025   {
3026     mutt_error(_("Bad IDN %s while preparing resent-from"), err);
3027     FREE(&err);
3028     mutt_addr_free(&from);
3029     return -1;
3030   }
3031   mutt_addr_write(resent_from, sizeof(resent_from), from, false);
3032
3033 #ifdef USE_NNTP
3034   OptNewsSend = false;
3035 #endif
3036
3037   /* prepare recipient list. idna conversion appears to happen before this
3038    * function is called, since the user receives confirmation of the address
3039    * list being bounced to.  */
3040   struct Address *resent_to = mutt_addr_copy_list(to, false);
3041   rfc2047_encode_addrlist(resent_to, "Resent-To");
3042
3043   int rc = bounce_message(fp, e, resent_to, resent_from, from);
3044
3045   mutt_addr_free(&resent_to);
3046   mutt_addr_free(&from);
3047
3048   return rc;
3049 }
3050
3051 /**
3052  * set_noconv_flags - Set/reset the "x-mutt-noconv" flag
3053  * @param b    Body of email
3054  * @param flag If true, set the flag, otherwise remove it
3055  */
3056 static void set_noconv_flags(struct Body *b, bool flag)
3057 {
3058   for (; b; b = b->next)
3059   {
3060     if ((b->type == TYPE_MESSAGE) || (b->type == TYPE_MULTIPART))
3061       set_noconv_flags(b->parts, flag);
3062     else if ((b->type == TYPE_TEXT) && b->noconv)
3063     {
3064       if (flag)
3065         mutt_param_set(&b->parameter, "x-mutt-noconv", "yes");
3066       else
3067         mutt_param_delete(&b->parameter, "x-mutt-noconv");
3068     }
3069   }
3070 }
3071
3072 /**
3073  * mutt_write_multiple_fcc - Handle FCC with multiple, comma separated entries
3074  * @param[in]  path      Path to mailboxes (comma separated)
3075  * @param[in]  e       Email
3076  * @param[in]  msgid     Message id
3077  * @param[in]  post      If true, postpone message
3078  * @param[in]  fcc       fcc setting to save (postpone only)
3079  * @param[out] finalpath Final path of email
3080  * @retval  0 Success
3081  * @retval -1 Failure
3082  */
3083 int mutt_write_multiple_fcc(const char *path, struct Email *e, const char *msgid,
3084                             bool post, char *fcc, char **finalpath)
3085 {
3086   char fcc_tok[PATH_MAX];
3087   char fcc_expanded[PATH_MAX];
3088
3089   mutt_str_strfcpy(fcc_tok, path, sizeof(fcc_tok));
3090
3091   char *tok = strtok(fcc_tok, ",");
3092   if (!tok)
3093     return -1;
3094
3095   mutt_debug(LL_DEBUG1, "Fcc: initial mailbox = '%s'\n", tok);
3096   /* mutt_expand_path already called above for the first token */
3097   int status = mutt_write_fcc(tok, e, msgid, post, fcc, finalpath);
3098   if (status != 0)
3099     return status;
3100
3101   while ((tok = strtok(NULL, ",")))
3102   {
3103     if (!*tok)
3104       continue;
3105
3106     /* Only call mutt_expand_path if tok has some data */
3107     mutt_debug(LL_DEBUG1, "Fcc: additional mailbox token = '%s'\n", tok);
3108     mutt_str_strfcpy(fcc_expanded, tok, sizeof(fcc_expanded));
3109     mutt_expand_path(fcc_expanded, sizeof(fcc_expanded));
3110     mutt_debug(LL_DEBUG1, "     Additional mailbox expanded = '%s'\n", fcc_expanded);
3111     status = mutt_write_fcc(fcc_expanded, e, msgid, post, fcc, finalpath);
3112     if (status != 0)
3113       return status;
3114   }
3115
3116   return 0;
3117 }
3118
3119 /**
3120  * mutt_write_fcc - Write email to FCC mailbox
3121  * @param[in]  path      Path to mailbox
3122  * @param[in]  e       Email
3123  * @param[in]  msgid     Message id
3124  * @param[in]  post      If true, postpone message
3125  * @param[in]  fcc       fcc setting to save (postpone only)
3126  * @param[out] finalpath Final path of email
3127  * @retval  0 Success
3128  * @retval -1 Failure
3129  */
3130 int mutt_write_fcc(const char *path, struct Email *e, const char *msgid,
3131                    bool post, char *fcc, char **finalpath)
3132 {
3133   struct Message *msg = NULL;
3134   char tempfile[PATH_MAX];
3135   FILE *fp_tmp = NULL;
3136   int rc = -1;
3137   bool need_mailbox_cleanup = false;
3138   struct stat st;
3139   char buf[128];
3140   MsgOpenFlags onm_flags;
3141
3142   if (post)
3143     set_noconv_flags(e->content, true);
3144
3145 #ifdef RECORD_FOLDER_HOOK
3146   mutt_folder_hook(path, NULL);
3147 #endif
3148   struct Mailbox *m_fcc = mx_path_resolve(path);
3149   struct Context *ctx_fcc = mx_mbox_open(m_fcc, MUTT_APPEND | MUTT_QUIET);
3150   if (!ctx_fcc)
3151   {
3152     mutt_debug(LL_DEBUG1, "unable to open mailbox %s in append-mode, aborting.\n", path);
3153     mailbox_free(&m_fcc);
3154     goto done;
3155   }
3156
3157   /* We need to add a Content-Length field to avoid problems where a line in
3158    * the message body begins with "From " */
3159   if ((ctx_fcc->mailbox->magic == MUTT_MMDF) || (ctx_fcc->mailbox->magic == MUTT_MBOX))
3160   {
3161     mutt_mktemp(tempfile, sizeof(tempfile));
3162     fp_tmp = mutt_file_fopen(tempfile, "w+");
3163     if (!fp_tmp)
3164     {
3165       mutt_perror(tempfile);
3166       mx_mbox_close(&ctx_fcc);
3167       goto done;
3168     }
3169     /* remember new mail status before appending message */
3170     need_mailbox_cleanup = true;
3171     stat(path, &st);
3172   }
3173
3174   e->read = !post; /* make sure to put it in the 'cur' directory (maildir) */
3175   onm_flags = MUTT_ADD_FROM;
3176   if (post)
3177     onm_flags |= MUTT_SET_DRAFT;
3178   msg = mx_msg_open_new(ctx_fcc->mailbox, e, onm_flags);
3179   if (!msg)
3180   {
3181     mutt_file_fclose(&fp_tmp);
3182     mx_mbox_close(&ctx_fcc);
3183     goto done;
3184   }
3185
3186   /* post == 1 => postpone message.
3187    * post == 0 => Normal mode.  */
3188   mutt_rfc822_write_header(
3189       msg->fp, e->env, e->content, post ? MUTT_WRITE_HEADER_POSTPONE : MUTT_WRITE_HEADER_NORMAL,
3190       false, C_CryptProtectedHeadersRead && mutt_should_hide_protected_subject(e));
3191
3192   /* (postponement) if this was a reply of some sort, <msgid> contains the
3193    * Message-ID: of message replied to.  Save it using a special X-Mutt-
3194    * header so it can be picked up if the message is recalled at a later
3195    * point in time.  This will allow the message to be marked as replied if
3196    * the same mailbox is still open.  */
3197   if (post && msgid)
3198     fprintf(msg->fp, "X-Mutt-References: %s\n", msgid);
3199
3200   /* (postponement) save the Fcc: using a special X-Mutt- header so that
3201    * it can be picked up when the message is recalled */
3202   if (post && fcc)
3203     fprintf(msg->fp, "X-Mutt-Fcc: %s\n", fcc);
3204
3205   if ((ctx_fcc->mailbox->magic == MUTT_MMDF) || (ctx_fcc->mailbox->magic == MUTT_MBOX))
3206     fprintf(msg->fp, "Status: RO\n");
3207
3208   /* mutt_rfc822_write_header() only writes out a Date: header with
3209    * mode == 0, i.e. _not_ postponement; so write out one ourself */
3210   if (post)
3211     fprintf(msg->fp, "%s", mutt_date_make_date(buf, sizeof(buf)));
3212
3213   /* (postponement) if the mail is to be signed or encrypted, save this info */
3214   if (((WithCrypto & APPLICATION_PGP) != 0) && post && (e->security & APPLICATION_PGP))
3215   {
3216     fputs("X-Mutt-PGP: ", msg->fp);
3217     if (e->security & SEC_ENCRYPT)
3218       fputc('E', msg->fp);
3219     if (e->security & SEC_OPPENCRYPT)
3220       fputc('O', msg->fp);
3221     if (e->security & SEC_SIGN)
3222     {
3223       fputc('S', msg->fp);
3224       if (C_PgpSignAs && *C_PgpSignAs)
3225         fprintf(msg->fp, "<%s>", C_PgpSignAs);
3226     }
3227     if (e->security & SEC_INLINE)
3228       fputc('I', msg->fp);
3229     fputc('\n', msg->fp);
3230   }
3231
3232   /* (postponement) if the mail is to be signed or encrypted, save this info */
3233   if (((WithCrypto & APPLICATION_SMIME) != 0) && post && (e->security & APPLICATION_SMIME))
3234   {
3235     fputs("X-Mutt-SMIME: ", msg->fp);
3236     if (e->security & SEC_ENCRYPT)
3237     {
3238       fputc('E', msg->fp);
3239       if (C_SmimeEncryptWith && *C_SmimeEncryptWith)
3240         fprintf(msg->fp, "C<%s>", C_SmimeEncryptWith);
3241     }
3242     if (e->security & SEC_OPPENCRYPT)
3243       fputc('O', msg->fp);
3244     if (e->security & SEC_SIGN)
3245     {
3246       fputc('S', msg->fp);
3247       if (C_SmimeSignAs && *C_SmimeSignAs)
3248         fprintf(msg->fp, "<%s>", C_SmimeSignAs);
3249     }
3250     if (e->security & SEC_INLINE)
3251       fputc('I', msg->fp);
3252     fputc('\n', msg->fp);
3253   }
3254
3255 #ifdef MIXMASTER
3256   /* (postponement) if the mail is to be sent through a mixmaster
3257    * chain, save that information */
3258
3259   if (post && !STAILQ_EMPTY(&e->chain))
3260   {
3261     fputs("X-Mutt-Mix:", msg->fp);
3262     struct ListNode *p = NULL;
3263     STAILQ_FOREACH(p, &e->chain, entries)
3264     {
3265       fprintf(msg->fp, " %s", (char *) p->data);
3266     }
3267
3268     fputc('\n', msg->fp);
3269   }
3270 #endif
3271
3272   if (fp_tmp)
3273   {
3274     char sasha[1024];
3275     int lines = 0;
3276
3277     mutt_write_mime_body(e->content, fp_tmp);
3278
3279     /* make sure the last line ends with a newline.  Emacs doesn't ensure this
3280      * will happen, and it can cause problems parsing the mailbox later.  */
3281     fseek(fp_tmp, -1, SEEK_END);
3282     if (fgetc(fp_tmp) != '\n')
3283     {
3284       fseek(fp_tmp, 0, SEEK_END);
3285       fputc('\n', fp_tmp);
3286     }
3287
3288     fflush(fp_tmp);
3289     if (ferror(fp_tmp))
3290     {
3291       mutt_debug(LL_DEBUG1, "%s: write failed.\n", tempfile);
3292       mutt_file_fclose(&fp_tmp);
3293       unlink(tempfile);
3294       mx_msg_commit(ctx_fcc->mailbox, msg); /* XXX really? */
3295       mx_msg_close(ctx_fcc->mailbox, &msg);
3296       mx_mbox_close(&ctx_fcc);
3297       goto done;
3298     }
3299
3300     /* count the number of lines */
3301     rewind(fp_tmp);
3302     while (fgets(sasha, sizeof(sasha), fp_tmp))
3303       lines++;
3304     fprintf(msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftello(fp_tmp));
3305     fprintf(msg->fp, "Lines: %d\n\n", lines);
3306
3307     /* copy the body and clean up */
3308     rewind(fp_tmp);
3309     rc = mutt_file_copy_stream(fp_tmp, msg->fp);
3310     if (fclose(fp_tmp) != 0)
3311       rc = -1;
3312     /* if there was an error, leave the temp version */
3313     if (!rc)
3314       unlink(tempfile);
3315   }
3316   else
3317   {
3318     fputc('\n', msg->fp); /* finish off the header */
3319     rc = mutt_write_mime_body(e->content, msg->fp);
3320   }
3321
3322   if (mx_msg_commit(ctx_fcc->mailbox, msg) != 0)
3323     rc = -1;
3324   else if (finalpath)
3325     *finalpath = mutt_str_strdup(msg->committed_path);
3326   mx_msg_close(ctx_fcc->mailbox, &msg);
3327   mx_mbox_close(&ctx_fcc);
3328
3329   if (!post && need_mailbox_cleanup)
3330     mutt_mailbox_cleanup(path, &st);
3331
3332   if (post)
3333     set_noconv_flags(e->content, false);
3334
3335 done:
3336 #ifdef RECORD_FOLDER_HOOK
3337   /* We ran a folder hook for the destination mailbox,
3338    * now we run it for the user's current mailbox */
3339   if (Context && Context->mailbox->path)
3340     mutt_folder_hook(Context->mailbox->path, Context->mailbox->desc);
3341 #endif
3342
3343   return rc;
3344 }