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