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