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