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