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