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