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