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