]> granicus.if.org Git - mutt/blob - sendlib.c
Convert fcc to a buffer.
[mutt] / sendlib.c
1 /*
2  * Copyright (C) 1996-2002,2009-2012 Michael R. Elkins <me@mutt.org>
3  *
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  *
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  *
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18
19 #define _SENDLIB_C 1
20
21 #if HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include "version.h"
26 #include "mutt.h"
27 #include "mutt_curses.h"
28 #include "rfc2047.h"
29 #include "rfc2231.h"
30 #include "mx.h"
31 #include "mime.h"
32 #include "mailbox.h"
33 #include "copy.h"
34 #include "pager.h"
35 #include "charset.h"
36 #include "mutt_crypt.h"
37 #include "mutt_idna.h"
38 #include "buffy.h"
39
40 #ifdef USE_AUTOCRYPT
41 #include "autocrypt.h"
42 #endif
43
44 #include <string.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <errno.h>
48 #include <ctype.h>
49 #include <sys/stat.h>
50 #include <signal.h>
51 #include <sys/wait.h>
52 #include <fcntl.h>
53
54 #ifdef HAVE_SYSEXITS_H
55 #include <sysexits.h>
56 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
57 #define EX_OK 0
58 #endif
59
60 /* If you are debugging this file, comment out the following line. */
61 #define NDEBUG
62
63 #ifdef NDEBUG
64 #define assert(x)
65 #else
66 #include <assert.h>
67 #endif
68
69 extern char RFC822Specials[];
70
71 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
72
73 const char B64Chars[64] = {
74   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
75   'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
76   'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
77   't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
78   '8', '9', '+', '/'
79 };
80
81 static char MsgIdPfx = 'A';
82
83 static void transform_to_7bit (BODY *a, FILE *fpin);
84
85 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
86 {
87   int c, linelen = 0;
88   char line[77], savechar;
89
90   while ((c = fgetconv (fc)) != EOF)
91   {
92     /* Wrap the line if needed. */
93     if (linelen == 76 && ((istext && c != '\n') || !istext))
94     {
95       /* If the last character is "quoted", then be sure to move all three
96        * characters to the next line.  Otherwise, just move the last
97        * character...
98        */
99       if (line[linelen-3] == '=')
100       {
101         line[linelen-3] = 0;
102         fputs (line, fout);
103         fputs ("=\n", fout);
104         line[linelen] = 0;
105         line[0] = '=';
106         line[1] = line[linelen-2];
107         line[2] = line[linelen-1];
108         linelen = 3;
109       }
110       else
111       {
112         savechar = line[linelen-1];
113         line[linelen-1] = '=';
114         line[linelen] = 0;
115         fputs (line, fout);
116         fputc ('\n', fout);
117         line[0] = savechar;
118         linelen = 1;
119       }
120     }
121
122     /* Escape lines that begin with/only contain "the message separator". */
123     if (linelen == 4 && !mutt_strncmp ("From", line, 4))
124     {
125       strfcpy (line, "=46rom", sizeof (line));
126       linelen = 6;
127     }
128     else if (linelen == 4 && !mutt_strncmp ("from", line, 4))
129     {
130       strfcpy (line, "=66rom", sizeof (line));
131       linelen = 6;
132     }
133     else if (linelen == 1 && line[0] == '.')
134     {
135       strfcpy (line, "=2E", sizeof (line));
136       linelen = 3;
137     }
138
139
140     if (c == '\n' && istext)
141     {
142       /* Check to make sure there is no trailing space on this line. */
143       if (linelen > 0 && (line[linelen-1] == ' ' || line[linelen-1] == '\t'))
144       {
145         if (linelen < 74)
146         {
147           sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
148           fputs (line, fout);
149         }
150         else
151         {
152           int savechar = line[linelen-1];
153
154           line[linelen-1] = '=';
155           line[linelen] = 0;
156           fputs (line, fout);
157           fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
158         }
159       }
160       else
161       {
162         line[linelen] = 0;
163         fputs (line, fout);
164       }
165       fputc ('\n', fout);
166       linelen = 0;
167     }
168     else if (c != 9 && (c < 32 || c > 126 || c == '='))
169     {
170       /* Check to make sure there is enough room for the quoted character.
171        * If not, wrap to the next line.
172        */
173       if (linelen > 73)
174       {
175         line[linelen++] = '=';
176         line[linelen] = 0;
177         fputs (line, fout);
178         fputc ('\n', fout);
179         linelen = 0;
180       }
181       sprintf (line+linelen,"=%2.2X", (unsigned char) c);
182       linelen += 3;
183     }
184     else
185     {
186       /* Don't worry about wrapping the line here.  That will happen during
187        * the next iteration when I'll also know what the next character is.
188        */
189       line[linelen++] = c;
190     }
191   }
192
193   /* Take care of anything left in the buffer */
194   if (linelen > 0)
195   {
196     if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
197     {
198       /* take care of trailing whitespace */
199       if (linelen < 74)
200         sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
201       else
202       {
203         savechar = line[linelen-1];
204         line[linelen-1] = '=';
205         line[linelen] = 0;
206         fputs (line, fout);
207         fputc ('\n', fout);
208         sprintf (line, "=%2.2X", (unsigned char) savechar);
209       }
210     }
211     else
212       line[linelen] = 0;
213     fputs (line, fout);
214   }
215 }
216
217 static char b64_buffer[3];
218 static short b64_num;
219 static short b64_linelen;
220
221 static void b64_flush(FILE *fout)
222 {
223   short i;
224
225   if (!b64_num)
226     return;
227
228   if (b64_linelen >= 72)
229   {
230     fputc('\n', fout);
231     b64_linelen = 0;
232   }
233
234   for (i = b64_num; i < 3; i++)
235     b64_buffer[i] = '\0';
236
237   fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
238   b64_linelen++;
239   fputc(B64Chars[((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf) ], fout);
240   b64_linelen++;
241
242   if (b64_num > 1)
243   {
244     fputc(B64Chars[((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3) ], fout);
245     b64_linelen++;
246     if (b64_num > 2)
247     {
248       fputc(B64Chars[b64_buffer[2] & 0x3f], fout);
249       b64_linelen++;
250     }
251   }
252
253   while (b64_linelen % 4)
254   {
255     fputc('=', fout);
256     b64_linelen++;
257   }
258
259   b64_num = 0;
260 }
261
262
263 static void b64_putc(char c, FILE *fout)
264 {
265   if (b64_num == 3)
266     b64_flush(fout);
267
268   b64_buffer[b64_num++] = c;
269 }
270
271
272 static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
273 {
274   int ch, ch1 = EOF;
275
276   b64_num = b64_linelen = 0;
277
278   while ((ch = fgetconv (fc)) != EOF)
279   {
280     if (istext && ch == '\n' && ch1 != '\r')
281       b64_putc('\r', fout);
282     b64_putc(ch, fout);
283     ch1 = ch;
284   }
285   b64_flush(fout);
286   fputc('\n', fout);
287 }
288
289 static void encode_8bit (FGETCONV *fc, FILE *fout, int istext)
290 {
291   int ch;
292
293   while ((ch = fgetconv (fc)) != EOF)
294     fputc (ch, fout);
295 }
296
297
298 int mutt_write_mime_header (BODY *a, FILE *f)
299 {
300   PARAMETER *p;
301   PARAMETER *param_conts, *cont;
302   char buffer[STRING];
303   char *t;
304   char *fn;
305   int len;
306   int tmplen;
307
308   fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
309
310   if (a->parameter)
311   {
312     len = 25 + mutt_strlen (a->subtype); /* approximate len. of content-type */
313
314     for (p = a->parameter; p; p = p->next)
315     {
316       if (!(p->attribute && p->value))
317         continue;
318
319       param_conts = rfc2231_encode_string (p->attribute, p->value);
320       for (cont = param_conts; cont; cont = cont->next)
321       {
322         fputc (';', f);
323
324         buffer[0] = 0;
325         rfc822_cat (buffer, sizeof (buffer), cont->value, MimeSpecials);
326
327         /* Dirty hack to make messages readable by Outlook Express
328          * for the Mac: force quotes around the boundary parameter
329          * even when they aren't needed.
330          */
331         if (!ascii_strcasecmp (cont->attribute, "boundary") &&
332             !mutt_strcmp (buffer, cont->value))
333           snprintf (buffer, sizeof (buffer), "\"%s\"", cont->value);
334
335         tmplen = mutt_strlen (buffer) + mutt_strlen (cont->attribute) + 1;
336         if (len + tmplen + 2 > 76)
337         {
338           fputs ("\n\t", f);
339           len = tmplen + 1;
340         }
341         else
342         {
343           fputc (' ', f);
344           len += tmplen + 1;
345         }
346
347         fprintf (f, "%s=%s", cont->attribute, buffer);
348       }
349
350       mutt_free_parameter (&param_conts);
351     }
352   }
353
354   fputc ('\n', f);
355
356   if (a->description)
357     fprintf(f, "Content-Description: %s\n", a->description);
358
359   if (a->disposition != DISPNONE)
360   {
361     const char *dispstr[] = {
362       "inline",
363       "attachment",
364       "form-data"
365     };
366
367     if (a->disposition < sizeof(dispstr)/sizeof(char*))
368     {
369       fprintf (f, "Content-Disposition: %s", dispstr[a->disposition]);
370       len = 21 + mutt_strlen (dispstr[a->disposition]);
371
372       if (a->use_disp)
373       {
374         if (!(fn = a->d_filename))
375           fn = a->filename;
376
377         if (fn)
378         {
379           /* Strip off the leading path... */
380           if ((t = strrchr (fn, '/')))
381             t++;
382           else
383             t = fn;
384
385           param_conts = rfc2231_encode_string ("filename", t);
386           for (cont = param_conts; cont; cont = cont->next)
387           {
388             fputc (';', f);
389             buffer[0] = 0;
390             rfc822_cat (buffer, sizeof (buffer), cont->value, MimeSpecials);
391
392             tmplen = mutt_strlen (buffer) + mutt_strlen (cont->attribute) + 1;
393             if (len + tmplen + 2 > 76)
394             {
395               fputs ("\n\t", f);
396               len = tmplen + 1;
397             }
398             else
399             {
400               fputc (' ', f);
401               len += tmplen + 1;
402             }
403
404             fprintf (f, "%s=%s", cont->attribute, buffer);
405           }
406
407           mutt_free_parameter (&param_conts);
408         }
409       }
410
411       fputc ('\n', f);
412     }
413     else
414     {
415       dprint(1, (debugfile, "ERROR: invalid content-disposition %d\n", a->disposition));
416     }
417   }
418
419   if (a->encoding != ENC7BIT)
420     fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
421
422   if ((option (OPTCRYPTPROTHDRSWRITE)
423 #ifdef USE_AUTOCRYPT
424        || option (OPTAUTOCRYPT)
425 #endif
426         ) &&
427       a->mime_headers)
428   {
429     mutt_write_rfc822_header (f, a->mime_headers, NULL, MUTT_WRITE_HEADER_MIME, 0, 0);
430   }
431
432   /* Do NOT add the terminator here!!! */
433   return (ferror (f) ? -1 : 0);
434 }
435
436 #define write_as_text_part(a)                                           \
437   (mutt_is_text_part(a) ||                                              \
438    ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp(a)))
439
440 int mutt_write_mime_body (BODY *a, FILE *f)
441 {
442   char *p, boundary[SHORT_STRING];
443   char send_charset[SHORT_STRING];
444   FILE *fpin;
445   BODY *t;
446   FGETCONV *fc;
447
448   if (a->type == TYPEMULTIPART)
449   {
450     /* First, find the boundary to use */
451     if (!(p = mutt_get_parameter ("boundary", a->parameter)))
452     {
453       dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
454       mutt_error _("No boundary parameter found! [report this error]");
455       return (-1);
456     }
457     strfcpy (boundary, p, sizeof (boundary));
458
459     for (t = a->parts; t ; t = t->next)
460     {
461       fprintf (f, "\n--%s\n", boundary);
462       if (mutt_write_mime_header (t, f) == -1)
463         return -1;
464       fputc ('\n', f);
465       if (mutt_write_mime_body (t, f) == -1)
466         return -1;
467     }
468     fprintf (f, "\n--%s--\n", boundary);
469     return (ferror (f) ? -1 : 0);
470   }
471
472   /* This is pretty gross, but it's the best solution for now... */
473   if ((WithCrypto & APPLICATION_PGP)
474       && a->type == TYPEAPPLICATION
475       && mutt_strcmp (a->subtype, "pgp-encrypted") == 0
476       && !a->filename)
477   {
478     fputs ("Version: 1\n", f);
479     return 0;
480   }
481
482   if ((fpin = fopen (a->filename, "r")) == NULL)
483   {
484     dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
485     mutt_error (_("%s no longer exists!"), a->filename);
486     return -1;
487   }
488
489   if (a->type == TYPETEXT && (!a->noconv))
490     fc = fgetconv_open (fpin, a->charset,
491                         mutt_get_body_charset (send_charset, sizeof (send_charset), a),
492                         0);
493   else
494     fc = fgetconv_open (fpin, 0, 0, 0);
495
496   if (a->encoding == ENCQUOTEDPRINTABLE)
497     encode_quoted (fc, f, write_as_text_part (a));
498   else if (a->encoding == ENCBASE64)
499     encode_base64 (fc, f, write_as_text_part (a));
500   else if (a->type == TYPETEXT && (!a->noconv))
501     encode_8bit (fc, f, write_as_text_part (a));
502   else
503     mutt_copy_stream (fpin, f);
504
505   fgetconv_close (&fc);
506   safe_fclose (&fpin);
507
508   return (ferror (f) ? -1 : 0);
509 }
510
511 #undef write_as_text_part
512
513 #define BOUNDARYLEN 16
514 void mutt_generate_boundary (PARAMETER **parm)
515 {
516   char rs[BOUNDARYLEN + 1];
517   char *p = rs;
518   int i;
519
520   rs[BOUNDARYLEN] = 0;
521   for (i=0;i<BOUNDARYLEN;i++)
522     *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
523   *p = 0;
524
525   mutt_set_parameter ("boundary", rs, parm);
526 }
527
528 typedef struct
529 {
530   int from;
531   int whitespace;
532   int dot;
533   int linelen;
534   int was_cr;
535 }
536 CONTENT_STATE;
537
538
539 static void update_content_info (CONTENT *info, CONTENT_STATE *s, char *d, size_t dlen)
540 {
541   int from = s->from;
542   int whitespace = s->whitespace;
543   int dot = s->dot;
544   int linelen = s->linelen;
545   int was_cr = s->was_cr;
546
547   if (!d) /* This signals EOF */
548   {
549     if (was_cr)
550       info->binary = 1;
551     if (linelen > info->linemax)
552       info->linemax = linelen;
553
554     return;
555   }
556
557   for (; dlen; d++, dlen--)
558   {
559     char ch = *d;
560
561     if (was_cr)
562     {
563       was_cr = 0;
564       if (ch != '\n')
565       {
566         info->binary = 1;
567       }
568       else
569       {
570         if (whitespace) info->space = 1;
571         if (dot) info->dot = 1;
572         if (linelen > info->linemax) info->linemax = linelen;
573         whitespace = 0;
574         dot = 0;
575         linelen = 0;
576         continue;
577       }
578     }
579
580     linelen++;
581     if (ch == '\n')
582     {
583       info->crlf++;
584       if (whitespace) info->space = 1;
585       if (dot) info->dot = 1;
586       if (linelen > info->linemax) info->linemax = linelen;
587       whitespace = 0;
588       linelen = 0;
589       dot = 0;
590     }
591     else if (ch == '\r')
592     {
593       info->crlf++;
594       info->cr = 1;
595       was_cr = 1;
596       continue;
597     }
598     else if (ch & 0x80)
599       info->hibin++;
600     else if (ch == '\t' || ch == '\f')
601     {
602       info->ascii++;
603       whitespace++;
604     }
605     else if (ch == 0)
606     {
607       info->nulbin++;
608       info->lobin++;
609     }
610     else if (ch < 32 || ch == 127)
611       info->lobin++;
612     else
613     {
614       if (linelen == 1)
615       {
616         if ((ch == 'F') || (ch == 'f'))
617           from = 1;
618         else
619           from = 0;
620         if (ch == '.')
621           dot = 1;
622         else
623           dot = 0;
624       }
625       else if (from)
626       {
627         if (linelen == 2 && ch != 'r') from = 0;
628         else if (linelen == 3 && ch != 'o') from = 0;
629         else if (linelen == 4)
630         {
631           if (ch == 'm') info->from = 1;
632           from = 0;
633         }
634       }
635       if (ch == ' ') whitespace++;
636       info->ascii++;
637     }
638
639     if (linelen > 1) dot = 0;
640     if (ch != ' ' && ch != '\t') whitespace = 0;
641   }
642
643   s->from = from;
644   s->whitespace = whitespace;
645   s->dot = dot;
646   s->linelen = linelen;
647   s->was_cr = was_cr;
648
649 }
650
651 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
652 #define BUGGY_ICONV 1
653
654 /*
655  * Find the best charset conversion of the file from fromcode into one
656  * of the tocodes. If successful, set *tocode and CONTENT *info and
657  * return the number of characters converted inexactly. If no
658  * conversion was possible, return -1.
659  *
660  * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
661  * which would otherwise prevent us from knowing the number of inexact
662  * conversions. Where the candidate target charset is UTF-8 we avoid
663  * doing the second conversion because iconv_open("UTF-8", "UTF-8")
664  * fails with some libraries.
665  *
666  * We assume that the output from iconv is never more than 4 times as
667  * long as the input for any pair of charsets we might be interested
668  * in.
669  */
670 static size_t convert_file_to (FILE *file, const char *fromcode,
671                                int ncodes, const char **tocodes,
672                                int *tocode, CONTENT *info)
673 {
674 #ifdef HAVE_ICONV
675   iconv_t cd1, *cd;
676   char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
677   ICONV_CONST char *ib, *ub;
678   char *ob;
679   size_t ibl, obl, ubl, ubl1, n, ret;
680   int i;
681   CONTENT *infos;
682   CONTENT_STATE *states;
683   size_t *score;
684
685   cd1 = mutt_iconv_open ("utf-8", fromcode, 0);
686   if (cd1 == (iconv_t)(-1))
687     return -1;
688
689   cd     = safe_calloc (ncodes, sizeof (iconv_t));
690   score  = safe_calloc (ncodes, sizeof (size_t));
691   states = safe_calloc (ncodes, sizeof (CONTENT_STATE));
692   infos  = safe_calloc (ncodes, sizeof (CONTENT));
693
694   for (i = 0; i < ncodes; i++)
695     if (ascii_strcasecmp (tocodes[i], "utf-8"))
696       cd[i] = mutt_iconv_open (tocodes[i], "utf-8", 0);
697     else
698       /* Special case for conversion to UTF-8 */
699       cd[i] = (iconv_t)(-1), score[i] = (size_t)(-1);
700
701   rewind (file);
702   ibl = 0;
703   for (;;)
704   {
705
706     /* Try to fill input buffer */
707     n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
708     ibl += n;
709
710     /* Convert to UTF-8 */
711     ib = bufi;
712     ob = bufu, obl = sizeof (bufu);
713     n = iconv (cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
714     assert (n == (size_t)(-1) || !n || ICONV_NONTRANS);
715     if (n == (size_t)(-1) &&
716         ((errno != EINVAL && errno != E2BIG) || ib == bufi))
717     {
718       assert (errno == EILSEQ ||
719               (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
720       ret = (size_t)(-1);
721       break;
722     }
723     ubl1 = ob - bufu;
724
725     /* Convert from UTF-8 */
726     for (i = 0; i < ncodes; i++)
727       if (cd[i] != (iconv_t)(-1) && score[i] != (size_t)(-1))
728       {
729         ub = bufu, ubl = ubl1;
730         ob = bufo, obl = sizeof (bufo);
731         n = iconv (cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
732         if (n == (size_t)(-1))
733         {
734           assert (errno == E2BIG ||
735                   (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
736           score[i] = (size_t)(-1);
737         }
738         else
739         {
740           score[i] += n;
741           update_content_info (&infos[i], &states[i], bufo, ob - bufo);
742         }
743       }
744       else if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
745         /* Special case for conversion to UTF-8 */
746         update_content_info (&infos[i], &states[i], bufu, ubl1);
747
748     if (ibl)
749       /* Save unused input */
750       memmove (bufi, ib, ibl);
751     else if (!ubl1 && ib < bufi + sizeof (bufi))
752     {
753       ret = 0;
754       break;
755     }
756   }
757
758   if (!ret)
759   {
760     /* Find best score */
761     ret = (size_t)(-1);
762     for (i = 0; i < ncodes; i++)
763     {
764       if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
765       {
766         /* Special case for conversion to UTF-8 */
767         *tocode = i;
768         ret = 0;
769         break;
770       }
771       else if (cd[i] == (iconv_t)(-1) || score[i] == (size_t)(-1))
772         continue;
773       else if (ret == (size_t)(-1) || score[i] < ret)
774       {
775         *tocode = i;
776         ret = score[i];
777         if (!ret)
778           break;
779       }
780     }
781     if (ret != (size_t)(-1))
782     {
783       memcpy (info, &infos[*tocode], sizeof(CONTENT));
784       update_content_info (info, &states[*tocode], 0, 0); /* EOF */
785     }
786   }
787
788   for (i = 0; i < ncodes; i++)
789     if (cd[i] != (iconv_t)(-1))
790       iconv_close (cd[i]);
791
792   iconv_close (cd1);
793   FREE (&cd);
794   FREE (&infos);
795   FREE (&score);
796   FREE (&states);
797
798   return ret;
799 #else
800   return -1;
801 #endif /* !HAVE_ICONV */
802 }
803
804 /*
805  * Find the first of the fromcodes that gives a valid conversion and
806  * the best charset conversion of the file into one of the tocodes. If
807  * successful, set *fromcode and *tocode to dynamically allocated
808  * strings, set CONTENT *info, and return the number of characters
809  * converted inexactly. If no conversion was possible, return -1.
810  *
811  * Both fromcodes and tocodes may be colon-separated lists of charsets.
812  * However, if fromcode is zero then fromcodes is assumed to be the
813  * name of a single charset even if it contains a colon.
814  */
815 static size_t convert_file_from_to (FILE *file,
816                                     const char *fromcodes, const char *tocodes,
817                                     char **fromcode, char **tocode, CONTENT *info)
818 {
819   char *fcode = NULL;
820   char **tcode;
821   const char *c, *c1;
822   size_t ret;
823   int ncodes, i, cn;
824
825   /* Count the tocodes */
826   ncodes = 0;
827   for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
828   {
829     if ((c1 = strchr (c, ':')) == c)
830       continue;
831     ++ncodes;
832   }
833
834   /* Copy them */
835   tcode = safe_malloc (ncodes * sizeof (char *));
836   for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
837   {
838     if ((c1 = strchr (c, ':')) == c)
839       continue;
840     tcode[i] = mutt_substrdup (c, c1);
841   }
842
843   ret = (size_t)(-1);
844   if (fromcode)
845   {
846     /* Try each fromcode in turn */
847     for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
848     {
849       if ((c1 = strchr (c, ':')) == c)
850         continue;
851       fcode = mutt_substrdup (c, c1);
852
853       ret = convert_file_to (file, fcode, ncodes, (const char **)tcode,
854                              &cn, info);
855       if (ret != (size_t)(-1))
856       {
857         *fromcode = fcode;
858         *tocode = tcode[cn];
859         tcode[cn] = 0;
860         break;
861       }
862       FREE (&fcode);
863     }
864   }
865   else
866   {
867     /* There is only one fromcode */
868     ret = convert_file_to (file, fromcodes, ncodes, (const char **)tcode,
869                            &cn, info);
870     if (ret != (size_t)(-1))
871     {
872       *tocode = tcode[cn];
873       tcode[cn] = 0;
874     }
875   }
876
877   /* Free memory */
878   for (i = 0; i < ncodes; i++)
879     FREE (&tcode[i]);
880
881   FREE (&tcode);
882
883   return ret;
884 }
885
886 /*
887  * Analyze the contents of a file to determine which MIME encoding to use.
888  * Also set the body charset, sometimes, or not.
889  */
890 CONTENT *mutt_get_content_info (const char *fname, BODY *b)
891 {
892   CONTENT *info;
893   CONTENT_STATE state;
894   FILE *fp = NULL;
895   char *fromcode = NULL;
896   char *tocode;
897   char buffer[100];
898   char chsbuf[STRING];
899   size_t r;
900
901   struct stat sb;
902
903   if (b && !fname) fname = b->filename;
904
905   if (stat (fname, &sb) == -1)
906   {
907     mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
908     return NULL;
909   }
910
911   if (!S_ISREG(sb.st_mode))
912   {
913     mutt_error (_("%s isn't a regular file."), fname);
914     return NULL;
915   }
916
917   if ((fp = fopen (fname, "r")) == NULL)
918   {
919     dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
920                 fname, strerror (errno), errno));
921     return (NULL);
922   }
923
924   info = safe_calloc (1, sizeof (CONTENT));
925   memset (&state, 0, sizeof (state));
926
927   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
928   {
929     char *chs = mutt_get_parameter ("charset", b->parameter);
930     char *fchs = b->use_disp ? (AttachCharset ? AttachCharset : Charset) : Charset;
931     if (Charset && (chs || SendCharset) &&
932         convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
933                               &fromcode, &tocode, info) != (size_t)(-1))
934     {
935       if (!chs)
936       {
937         mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
938         mutt_set_parameter ("charset", chsbuf, &b->parameter);
939       }
940       FREE (&b->charset);
941       b->charset = fromcode;
942       FREE (&tocode);
943       safe_fclose (&fp);
944       return info;
945     }
946   }
947
948   rewind (fp);
949   while ((r = fread (buffer, 1, sizeof(buffer), fp)))
950     update_content_info (info, &state, buffer, r);
951   update_content_info (info, &state, 0, 0);
952
953   safe_fclose (&fp);
954
955   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
956     mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
957                                     Charset  && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
958                         &b->parameter);
959
960   return info;
961 }
962
963 /* Given a file with path ``s'', see if there is a registered MIME type.
964  * returns the major MIME type, and copies the subtype to ``d''.  First look
965  * for ~/.mime.types, then look in a system mime.types if we can find one.
966  * The longest match is used so that we can match `ps.gz' when `gz' also
967  * exists.
968  */
969
970 int mutt_lookup_mime_type (BODY *att, const char *path)
971 {
972   FILE *f;
973   char *p, *q, *ct;
974   char buf[LONG_STRING];
975   char subtype[STRING], xtype[STRING];
976   int count;
977   int szf, sze, cur_sze;
978   int type;
979
980   *subtype = '\0';
981   *xtype   = '\0';
982   type     = TYPEOTHER;
983   cur_sze  = 0;
984
985   szf      = mutt_strlen (path);
986
987   for (count = 0 ; count < 3 ; count++)
988   {
989     /*
990      * can't use strtok() because we use it in an inner loop below, so use
991      * a switch statement here instead.
992      */
993     switch (count)
994     {
995       case 0:
996         snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
997         break;
998       case 1:
999         strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
1000         break;
1001       case 2:
1002         strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
1003         break;
1004       default:
1005         dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
1006         goto bye;       /* shouldn't happen */
1007     }
1008
1009     if ((f = fopen (buf, "r")) != NULL)
1010     {
1011       while (fgets (buf, sizeof (buf) - 1, f) != NULL)
1012       {
1013         /* weed out any comments */
1014         if ((p = strchr (buf, '#')))
1015           *p = 0;
1016
1017         /* remove any leading space. */
1018         ct = buf;
1019         SKIPWS (ct);
1020
1021         /* position on the next field in this line */
1022         if ((p = strpbrk (ct, " \t")) == NULL)
1023           continue;
1024         *p++ = 0;
1025         SKIPWS (p);
1026
1027         /* cycle through the file extensions */
1028         while ((p = strtok (p, " \t\n")))
1029         {
1030           sze = mutt_strlen (p);
1031           if ((sze > cur_sze) && (szf >= sze) &&
1032               (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
1033               (szf == sze || path[szf - sze - 1] == '.'))
1034           {
1035             /* get the content-type */
1036
1037             if ((p = strchr (ct, '/')) == NULL)
1038             {
1039               /* malformed line, just skip it. */
1040               break;
1041             }
1042             *p++ = 0;
1043
1044             for (q = p; *q && !ISSPACE (*q); q++)
1045               ;
1046
1047             mutt_substrcpy (subtype, p, q, sizeof (subtype));
1048
1049             if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1050               strfcpy (xtype, ct, sizeof (xtype));
1051
1052             cur_sze = sze;
1053           }
1054           p = NULL;
1055         }
1056       }
1057       safe_fclose (&f);
1058     }
1059   }
1060
1061 bye:
1062
1063   if (type != TYPEOTHER || *xtype != '\0')
1064   {
1065     att->type = type;
1066     mutt_str_replace (&att->subtype, subtype);
1067     mutt_str_replace (&att->xtype, xtype);
1068   }
1069
1070   return (type);
1071 }
1072
1073 void mutt_message_to_7bit (BODY *a, FILE *fp)
1074 {
1075   char temp[_POSIX_PATH_MAX];
1076   char *line = NULL;
1077   FILE *fpin = NULL;
1078   FILE *fpout = NULL;
1079   struct stat sb;
1080
1081   if (!a->filename && fp)
1082     fpin = fp;
1083   else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1084   {
1085     mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1086     return;
1087   }
1088   else
1089   {
1090     a->offset = 0;
1091     if (stat (a->filename, &sb) == -1)
1092     {
1093       mutt_perror ("stat");
1094       safe_fclose (&fpin);
1095     }
1096     a->length = sb.st_size;
1097   }
1098
1099   mutt_mktemp (temp, sizeof (temp));
1100   if (!(fpout = safe_fopen (temp, "w+")))
1101   {
1102     mutt_perror ("fopen");
1103     goto cleanup;
1104   }
1105
1106   fseeko (fpin, a->offset, 0);
1107   a->parts = mutt_parse_messageRFC822 (fpin, a);
1108
1109   transform_to_7bit (a->parts, fpin);
1110
1111   mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1112                  CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1113
1114   fputs ("MIME-Version: 1.0\n", fpout);
1115   mutt_write_mime_header (a->parts, fpout);
1116   fputc ('\n', fpout);
1117   mutt_write_mime_body (a->parts, fpout);
1118
1119 cleanup:
1120   FREE (&line);
1121
1122   if (fpin && fpin != fp)
1123     safe_fclose (&fpin);
1124   if (fpout)
1125     safe_fclose (&fpout);
1126   else
1127     return;
1128
1129   a->encoding = ENC7BIT;
1130   FREE (&a->d_filename);
1131   a->d_filename = a->filename;
1132   if (a->filename && a->unlink)
1133     unlink (a->filename);
1134   a->filename = safe_strdup (temp);
1135   a->unlink = 1;
1136   if (stat (a->filename, &sb) == -1)
1137   {
1138     mutt_perror ("stat");
1139     return;
1140   }
1141   a->length = sb.st_size;
1142   mutt_free_body (&a->parts);
1143   a->hdr->content = NULL;
1144 }
1145
1146 static void transform_to_7bit (BODY *a, FILE *fpin)
1147 {
1148   char buff[_POSIX_PATH_MAX];
1149   STATE s;
1150   struct stat sb;
1151
1152   memset (&s, 0, sizeof (s));
1153   for (; a; a = a->next)
1154   {
1155     if (a->type == TYPEMULTIPART)
1156     {
1157       if (a->encoding != ENC7BIT)
1158         a->encoding = ENC7BIT;
1159
1160       transform_to_7bit (a->parts, fpin);
1161     }
1162     else if (mutt_is_message_type(a->type, a->subtype))
1163     {
1164       mutt_message_to_7bit (a, fpin);
1165     }
1166     else
1167     {
1168       a->noconv = 1;
1169       a->force_charset = 1;
1170
1171       mutt_mktemp (buff, sizeof (buff));
1172       if ((s.fpout = safe_fopen (buff, "w")) == NULL)
1173       {
1174         mutt_perror ("fopen");
1175         return;
1176       }
1177       s.fpin = fpin;
1178       mutt_decode_attachment (a, &s);
1179       safe_fclose (&s.fpout);
1180       FREE (&a->d_filename);
1181       a->d_filename = a->filename;
1182       a->filename = safe_strdup (buff);
1183       a->unlink = 1;
1184       if (stat (a->filename, &sb) == -1)
1185       {
1186         mutt_perror ("stat");
1187         return;
1188       }
1189       a->length = sb.st_size;
1190
1191       mutt_update_encoding (a);
1192       if (a->encoding == ENC8BIT)
1193         a->encoding = ENCQUOTEDPRINTABLE;
1194       else if (a->encoding == ENCBINARY)
1195         a->encoding = ENCBASE64;
1196     }
1197   }
1198 }
1199
1200 /* determine which Content-Transfer-Encoding to use */
1201 static void mutt_set_encoding (BODY *b, CONTENT *info)
1202 {
1203   char send_charset[SHORT_STRING];
1204
1205   if (b->type == TYPETEXT)
1206   {
1207     char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1208     if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1209       b->encoding = ENCQUOTEDPRINTABLE;
1210     else if (info->hibin)
1211       b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1212     else
1213       b->encoding = ENC7BIT;
1214   }
1215   else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1216   {
1217     if (info->lobin || info->hibin)
1218     {
1219       if (option (OPTALLOW8BIT) && !info->lobin)
1220         b->encoding = ENC8BIT;
1221       else
1222         mutt_message_to_7bit (b, NULL);
1223     }
1224     else
1225       b->encoding = ENC7BIT;
1226   }
1227   else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1228     b->encoding = ENC7BIT;
1229   else
1230   {
1231     /* Determine which encoding is smaller  */
1232     if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
1233         3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1234       b->encoding = ENCBASE64;
1235     else
1236       b->encoding = ENCQUOTEDPRINTABLE;
1237   }
1238 }
1239
1240 void mutt_stamp_attachment(BODY *a)
1241 {
1242   a->stamp = time(NULL);
1243 }
1244
1245 /* Get a body's character set */
1246
1247 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1248 {
1249   char *p = NULL;
1250
1251   if (b && b->type != TYPETEXT)
1252     return NULL;
1253
1254   if (b)
1255     p = mutt_get_parameter ("charset", b->parameter);
1256
1257   if (p)
1258     mutt_canonical_charset (d, dlen, NONULL(p));
1259   else
1260     strfcpy (d, "us-ascii", dlen);
1261
1262   return d;
1263 }
1264
1265
1266 /* Assumes called from send mode where BODY->filename points to actual file */
1267 void mutt_update_encoding (BODY *a)
1268 {
1269   CONTENT *info;
1270   char chsbuff[STRING];
1271
1272   /* override noconv when it's us-ascii */
1273   if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1274     a->noconv = 0;
1275
1276   if (!a->force_charset && !a->noconv)
1277     mutt_delete_parameter ("charset", &a->parameter);
1278
1279   if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1280     return;
1281
1282   mutt_set_encoding (a, info);
1283   mutt_stamp_attachment(a);
1284
1285   FREE (&a->content);
1286   a->content = info;
1287
1288 }
1289
1290 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1291 {
1292   char buffer[LONG_STRING];
1293   BODY *body;
1294   FILE *fp;
1295   int cmflags, chflags;
1296   int pgp = WithCrypto? hdr->security : 0;
1297
1298   if (WithCrypto)
1299   {
1300     if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1301         (hdr->security & ENCRYPT))
1302     {
1303       if (!crypt_valid_passphrase(hdr->security))
1304         return (NULL);
1305     }
1306   }
1307
1308   mutt_mktemp (buffer, sizeof (buffer));
1309   if ((fp = safe_fopen (buffer, "w+")) == NULL)
1310     return NULL;
1311
1312   body = mutt_new_body ();
1313   body->type = TYPEMESSAGE;
1314   body->subtype = safe_strdup ("rfc822");
1315   body->filename = safe_strdup (buffer);
1316   body->unlink = 1;
1317   body->use_disp = 0;
1318   body->disposition = DISPINLINE;
1319   body->noconv = 1;
1320
1321   mutt_parse_mime_message (ctx, hdr);
1322
1323   chflags = CH_XMIT;
1324   cmflags = 0;
1325
1326   /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1327   if (!attach_msg && option (OPTMIMEFORWDECODE))
1328   {
1329     chflags |= CH_MIME | CH_TXTPLAIN;
1330     cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1331     if ((WithCrypto & APPLICATION_PGP))
1332       pgp &= ~PGPENCRYPT;
1333     if ((WithCrypto & APPLICATION_SMIME))
1334       pgp &= ~SMIMEENCRYPT;
1335   }
1336   else if (WithCrypto
1337            && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1338   {
1339     if ((WithCrypto & APPLICATION_PGP)
1340         && mutt_is_multipart_encrypted (hdr->content))
1341     {
1342       chflags |= CH_MIME | CH_NONEWLINE;
1343       cmflags = MUTT_CM_DECODE_PGP;
1344       pgp &= ~PGPENCRYPT;
1345     }
1346     else if ((WithCrypto & APPLICATION_PGP) &&
1347              ((mutt_is_application_pgp (hdr->content) & PGPENCRYPT) == PGPENCRYPT))
1348     {
1349       chflags |= CH_MIME | CH_TXTPLAIN;
1350       cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1351       pgp &= ~PGPENCRYPT;
1352     }
1353     else if ((WithCrypto & APPLICATION_SMIME) &&
1354              ((mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) == SMIMEENCRYPT))
1355     {
1356       chflags |= CH_MIME | CH_TXTPLAIN;
1357       cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1358       pgp &= ~SMIMEENCRYPT;
1359     }
1360   }
1361
1362   mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1363
1364   fflush(fp);
1365   rewind(fp);
1366
1367   body->hdr = mutt_new_header();
1368   body->hdr->offset = 0;
1369   /* we don't need the user headers here */
1370   body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1371   if (WithCrypto)
1372     body->hdr->security = pgp;
1373   mutt_update_encoding (body);
1374   body->parts = body->hdr->content;
1375
1376   safe_fclose (&fp);
1377
1378   return (body);
1379 }
1380
1381 static void run_mime_type_query (BODY *att)
1382 {
1383   FILE *fp, *fperr;
1384   BUFFER *cmd = NULL;
1385   char *buf = NULL;
1386   size_t buflen;
1387   int dummy = 0;
1388   pid_t thepid;
1389
1390   cmd = mutt_buffer_pool_get ();
1391   mutt_expand_file_fmt (cmd, MimeTypeQueryCmd, att->filename);
1392
1393   if ((thepid = mutt_create_filter (mutt_b2s (cmd), NULL, &fp, &fperr)) < 0)
1394   {
1395     mutt_error (_("Error running \"%s\"!"), mutt_b2s (cmd));
1396     mutt_buffer_pool_release (&cmd);
1397     return;
1398   }
1399   mutt_buffer_pool_release (&cmd);
1400
1401   if ((buf = mutt_read_line (buf, &buflen, fp, &dummy, 0)) != NULL)
1402   {
1403     if (strchr(buf, '/'))
1404       mutt_parse_content_type (buf, att);
1405     FREE (&buf);
1406   }
1407
1408   safe_fclose (&fp);
1409   safe_fclose (&fperr);
1410   mutt_wait_filter (thepid);
1411 }
1412
1413 BODY *mutt_make_file_attach (const char *path)
1414 {
1415   BODY *att;
1416   CONTENT *info;
1417
1418   att = mutt_new_body ();
1419   att->filename = safe_strdup (path);
1420
1421   if (MimeTypeQueryCmd && option (OPTMIMETYPEQUERYFIRST))
1422     run_mime_type_query (att);
1423
1424   /* Attempt to determine the appropriate content-type based on the filename
1425    * suffix.
1426    */
1427   if (!att->subtype)
1428     mutt_lookup_mime_type (att, path);
1429
1430   if (!att->subtype &&
1431       MimeTypeQueryCmd &&
1432       !option (OPTMIMETYPEQUERYFIRST))
1433     run_mime_type_query (att);
1434
1435   if ((info = mutt_get_content_info (path, att)) == NULL)
1436   {
1437     mutt_free_body (&att);
1438     return NULL;
1439   }
1440
1441   if (!att->subtype)
1442   {
1443     if ((info->nulbin == 0) &&
1444         (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10))
1445     {
1446       /*
1447        * Statistically speaking, there should be more than 10% "lobin"
1448        * chars if this is really a binary file...
1449        */
1450       att->type = TYPETEXT;
1451       att->subtype = safe_strdup ("plain");
1452     }
1453     else
1454     {
1455       att->type = TYPEAPPLICATION;
1456       att->subtype = safe_strdup ("octet-stream");
1457     }
1458   }
1459
1460   FREE(&info);
1461   mutt_update_encoding (att);
1462   return (att);
1463 }
1464
1465 static int get_toplevel_encoding (BODY *a)
1466 {
1467   int e = ENC7BIT;
1468
1469   for (; a; a = a->next)
1470   {
1471     if (a->encoding == ENCBINARY)
1472       return (ENCBINARY);
1473     else if (a->encoding == ENC8BIT)
1474       e = ENC8BIT;
1475   }
1476
1477   return (e);
1478 }
1479
1480 /* check for duplicate boundary. return 1 if duplicate */
1481 static int mutt_check_boundary (const char* boundary, BODY *b)
1482 {
1483   char* p;
1484
1485   if (b->parts && mutt_check_boundary (boundary, b->parts))
1486     return 1;
1487
1488   if (b->next && mutt_check_boundary (boundary, b->next))
1489     return 1;
1490
1491   if ((p = mutt_get_parameter ("boundary", b->parameter))
1492       && !ascii_strcmp (p, boundary))
1493     return 1;
1494   return 0;
1495 }
1496
1497 BODY *mutt_make_multipart (BODY *b)
1498 {
1499   BODY *new;
1500
1501   new = mutt_new_body ();
1502   new->type = TYPEMULTIPART;
1503   new->subtype = safe_strdup ("mixed");
1504   new->encoding = get_toplevel_encoding (b);
1505   do
1506   {
1507     mutt_generate_boundary (&new->parameter);
1508     if (mutt_check_boundary (mutt_get_parameter ("boundary", new->parameter),
1509                              b))
1510       mutt_delete_parameter ("boundary", &new->parameter);
1511   }
1512   while (!mutt_get_parameter ("boundary", new->parameter));
1513   new->use_disp = 0;
1514   new->disposition = DISPINLINE;
1515   new->parts = b;
1516
1517   return new;
1518 }
1519
1520 /* remove the multipart body if it exists */
1521 BODY *mutt_remove_multipart (BODY *b)
1522 {
1523   BODY *t;
1524
1525   if (b->parts)
1526   {
1527     t = b;
1528     b = b->parts;
1529     t->parts = NULL;
1530     mutt_free_body (&t);
1531   }
1532   return b;
1533 }
1534
1535 char *mutt_make_date (char *s, size_t len)
1536 {
1537   time_t t = time (NULL);
1538   struct tm *l = localtime (&t);
1539   time_t tz = mutt_local_tz (t);
1540
1541   tz /= 60;
1542
1543   snprintf (s, len,  "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1544             Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1545             l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1546             (int) tz / 60, (int) abs ((int) tz) % 60);
1547   return (s);
1548 }
1549
1550 /* wrapper around mutt_write_address() so we can handle very large
1551    recipient lists without needing a huge temporary buffer in memory */
1552 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1553 {
1554   ADDRESS *tmp;
1555   char buf[LONG_STRING];
1556   int count = 0;
1557   int len;
1558
1559   while (adr)
1560   {
1561     tmp = adr->next;
1562     adr->next = NULL;
1563     buf[0] = 0;
1564     rfc822_write_address (buf, sizeof (buf), adr, display);
1565     len = mutt_strlen (buf);
1566     if (count && linelen + len > 74)
1567     {
1568       fputs ("\n\t", fp);
1569       linelen = len + 8; /* tab is usually about 8 spaces... */
1570     }
1571     else
1572     {
1573       if (count && adr->mailbox)
1574       {
1575         fputc (' ', fp);
1576         linelen++;
1577       }
1578       linelen += len;
1579     }
1580     fputs (buf, fp);
1581     adr->next = tmp;
1582     if (!adr->group && adr->next && adr->next->mailbox)
1583     {
1584       linelen++;
1585       fputc (',', fp);
1586     }
1587     adr = adr->next;
1588     count++;
1589   }
1590   fputc ('\n', fp);
1591 }
1592
1593 /* arbitrary number of elements to grow the array by */
1594 #define REF_INC 16
1595
1596 /* need to write the list in reverse because they are stored in reverse order
1597  * when parsed to speed up threading
1598  */
1599 void mutt_write_references (LIST *r, FILE *f, int trim)
1600 {
1601   LIST **ref = NULL;
1602   int refcnt = 0, refmax = 0;
1603
1604   for ( ; (trim == 0 || refcnt < trim) && r ; r = r->next)
1605   {
1606     if (refcnt == refmax)
1607       safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1608     ref[refcnt++] = r;
1609   }
1610
1611   while (refcnt-- > 0)
1612   {
1613     fputc (' ', f);
1614     fputs (ref[refcnt]->data, f);
1615     if (refcnt >= 1)
1616       fputc ('\n', f);
1617   }
1618
1619   FREE (&ref);
1620 }
1621
1622 static const char *find_word (const char *src)
1623 {
1624   const char *p = src;
1625
1626   while (p && *p && strchr (" \t\n", *p))
1627     p++;
1628   while (p && *p && !strchr (" \t\n", *p))
1629     p++;
1630   return p;
1631 }
1632
1633 /* like wcwidth(), but gets const char* not wchar_t* */
1634 static int my_width (const char *str, int col, int flags)
1635 {
1636   wchar_t wc;
1637   int l, w = 0, nl = 0;
1638   const char *p = str;
1639
1640   while (p && *p)
1641   {
1642     if (mbtowc (&wc, p, MB_CUR_MAX) >= 0)
1643     {
1644       l = wcwidth (wc);
1645       if (l < 0)
1646         l = 1;
1647       /* correctly calc tab stop, even for sending as the
1648        * line should look pretty on the receiving end */
1649       if (wc == L'\t' || (nl && wc == L' '))
1650       {
1651         nl = 0;
1652         l = 8 - (col % 8);
1653       }
1654       /* track newlines for display-case: if we have a space
1655        * after a newline, assume 8 spaces as for display we
1656        * always tab-fold */
1657       else if ((flags & CH_DISPLAY) && wc == '\n')
1658         nl = 1;
1659     }
1660     else
1661       l = 1;
1662     w += l;
1663     p++;
1664   }
1665   return w;
1666 }
1667
1668 static int print_val (FILE *fp, const char *pfx, const char *value,
1669                       int flags, size_t col)
1670 {
1671   while (value && *value)
1672   {
1673     if (fputc (*value, fp) == EOF)
1674       return -1;
1675     /* corner-case: break words longer than 998 chars by force,
1676      * mandated by RfC5322 */
1677     if (!(flags & CH_DISPLAY) && ++col >= 998)
1678     {
1679       if (fputs ("\n ", fp) < 0)
1680         return -1;
1681       col = 1;
1682     }
1683     if (*value == '\n')
1684     {
1685       if (*(value + 1) && pfx && *pfx && fputs (pfx, fp) == EOF)
1686         return -1;
1687       /* for display, turn folding spaces into folding tabs */
1688       if ((flags & CH_DISPLAY) && (*(value + 1) == ' ' || *(value + 1) == '\t'))
1689       {
1690         value++;
1691         while (*value && (*value == ' ' || *value == '\t'))
1692           value++;
1693         if (fputc ('\t', fp) == EOF)
1694           return -1;
1695         continue;
1696       }
1697     }
1698     value++;
1699   }
1700   return 0;
1701 }
1702
1703 static int fold_one_header (FILE *fp, const char *tag, const char *value,
1704                             const char *pfx, int wraplen, int flags)
1705 {
1706   const char *p = value, *next, *sp;
1707   char buf[HUGE_STRING] = "";
1708   int first = 1, enc, col = 0, w, l = 0, fold;
1709
1710   dprint(4,(debugfile,"mwoh: pfx=[%s], tag=[%s], flags=%d value=[%s]\n",
1711             NONULL (pfx), tag, flags, value));
1712
1713   if (tag && *tag && fprintf (fp, "%s%s: ", NONULL (pfx), tag) < 0)
1714     return -1;
1715   col = mutt_strlen (tag) + (tag && *tag ? 2 : 0) + mutt_strlen (pfx);
1716
1717   while (p && *p)
1718   {
1719     fold = 0;
1720
1721     /* find the next word and place it in `buf'. it may start with
1722      * whitespace we can fold before */
1723     next = find_word (p);
1724     l = MIN(sizeof (buf) - 1, next - p);
1725     memcpy (buf, p, l);
1726     buf[l] = 0;
1727
1728     /* determine width: character cells for display, bytes for sending
1729      * (we get pure ascii only) */
1730     w = my_width (buf, col, flags);
1731     enc = mutt_strncmp (buf, "=?", 2) == 0;
1732
1733     dprint(5,(debugfile,"mwoh: word=[%s], col=%d, w=%d, next=[0x0%x]\n",
1734               buf, col, w, *next));
1735
1736     /* insert a folding \n before the current word's lwsp except for
1737      * header name, first word on a line (word longer than wrap width)
1738      * and encoded words */
1739     if (!first && !enc && col && col + w >= wraplen)
1740     {
1741       col = mutt_strlen (pfx);
1742       fold = 1;
1743       if (fprintf (fp, "\n%s", NONULL(pfx)) <= 0)
1744         return -1;
1745     }
1746
1747     /* print the actual word; for display, ignore leading ws for word
1748      * and fold with tab for readability */
1749     if ((flags & CH_DISPLAY) && fold)
1750     {
1751       char *p = buf;
1752       while (*p && (*p == ' ' || *p == '\t'))
1753       {
1754         p++;
1755         col--;
1756       }
1757       if (fputc ('\t', fp) == EOF)
1758         return -1;
1759       if (print_val (fp, pfx, p, flags, col) < 0)
1760         return -1;
1761       col += 8;
1762     }
1763     else if (print_val (fp, pfx, buf, flags, col) < 0)
1764       return -1;
1765     col += w;
1766
1767     /* if the current word ends in \n, ignore all its trailing spaces
1768      * and reset column; this prevents us from putting only spaces (or
1769      * even none) on a line if the trailing spaces are located at our
1770      * current line width
1771      * XXX this covers ASCII space only, for display we probably
1772      * XXX want something like iswspace() here */
1773     sp = next;
1774     while (*sp && (*sp == ' ' || *sp == '\t'))
1775       sp++;
1776     if (*sp == '\n')
1777     {
1778       next = sp;
1779       col = 0;
1780     }
1781
1782     p = next;
1783     first = 0;
1784   }
1785
1786   /* if we have printed something but didn't \n-terminate it, do it
1787    * except the last word we printed ended in \n already */
1788   if (col && (l == 0 || buf[l - 1] != '\n'))
1789     if (putc ('\n', fp) == EOF)
1790       return -1;
1791
1792   return 0;
1793 }
1794
1795 static char *unfold_header (char *s)
1796 {
1797   char *p = s, *q = s;
1798
1799   while (p && *p)
1800   {
1801     /* remove CRLF prior to FWSP, turn \t into ' ' */
1802     if (*p == '\r' && *(p + 1) && *(p + 1) == '\n' && *(p + 2) &&
1803         (*(p + 2) == ' ' || *(p + 2) == '\t'))
1804     {
1805       *q++ = ' ';
1806       p += 3;
1807       continue;
1808     }
1809     /* remove LF prior to FWSP, turn \t into ' ' */
1810     else if (*p == '\n' && *(p + 1) && (*(p + 1) == ' ' || *(p + 1) == '\t'))
1811     {
1812       *q++ = ' ';
1813       p += 2;
1814       continue;
1815     }
1816     *q++ = *p++;
1817   }
1818   if (q)
1819     *q = 0;
1820
1821   return s;
1822 }
1823
1824 static int write_one_header (FILE *fp, int pfxw, int max, int wraplen,
1825                              const char *pfx, const char *start, const char *end,
1826                              int flags)
1827 {
1828   char *tagbuf, *valbuf, *t;
1829   int is_from = ((end - start) > 5 &&
1830                  ascii_strncasecmp (start, "from ", 5) == 0);
1831
1832   /* only pass through folding machinery if necessary for sending,
1833      never wrap From_ headers on sending */
1834   if (!(flags & CH_DISPLAY) && (pfxw + max <= wraplen || is_from))
1835   {
1836     valbuf = mutt_substrdup (start, end);
1837     dprint(4,(debugfile,"mwoh: buf[%s%s] short enough, "
1838               "max width = %d <= %d\n",
1839               NONULL(pfx), valbuf, max, wraplen));
1840     if (pfx && *pfx)
1841       if (fputs (pfx, fp) == EOF)
1842         return -1;
1843     if (!(t = strchr (valbuf, ':')))
1844     {
1845       dprint (1, (debugfile, "mwoh: warning: header not in "
1846                   "'key: value' format!\n"));
1847       return 0;
1848     }
1849     if (print_val (fp, pfx, valbuf, flags, mutt_strlen (pfx)) < 0)
1850     {
1851       FREE(&valbuf);
1852       return -1;
1853     }
1854     FREE(&valbuf);
1855   }
1856   else
1857   {
1858     t = strchr (start, ':');
1859     if (!t || t > end)
1860     {
1861       dprint (1, (debugfile, "mwoh: warning: header not in "
1862                   "'key: value' format!\n"));
1863       return 0;
1864     }
1865     if (is_from)
1866     {
1867       tagbuf = NULL;
1868       valbuf = mutt_substrdup (start, end);
1869     }
1870     else
1871     {
1872       tagbuf = mutt_substrdup (start, t);
1873       /* skip over the colon separating the header field name and value */
1874       ++t;
1875
1876       /* skip over any leading whitespace (WSP, as defined in RFC5322)
1877        * NOTE: skip_email_wsp() does the wrong thing here.
1878        *       See tickets 3609 and 3716. */
1879       while (*t == ' ' || *t == '\t')
1880         t++;
1881
1882       valbuf = mutt_substrdup (t, end);
1883     }
1884     dprint(4,(debugfile,"mwoh: buf[%s%s] too long, "
1885               "max width = %d > %d\n",
1886               NONULL(pfx), valbuf, max, wraplen));
1887     if (fold_one_header (fp, tagbuf, valbuf, pfx, wraplen, flags) < 0)
1888       return -1;
1889     FREE (&tagbuf);
1890     FREE (&valbuf);
1891   }
1892   return 0;
1893 }
1894
1895 /* split several headers into individual ones and call write_one_header
1896  * for each one */
1897 int mutt_write_one_header (FILE *fp, const char *tag, const char *value,
1898                            const char *pfx, int wraplen, int flags)
1899 {
1900   char *p = (char *)value, *last, *line;
1901   int max = 0, w, rc = -1;
1902   int pfxw = mutt_strwidth (pfx);
1903   char *v = safe_strdup (value);
1904
1905   if (!(flags & CH_DISPLAY) || option (OPTWEED))
1906     v = unfold_header (v);
1907
1908   /* when not displaying, use sane wrap value */
1909   if (!(flags & CH_DISPLAY))
1910   {
1911     if (WrapHeaders < 78 || WrapHeaders > 998)
1912       wraplen = 78;
1913     else
1914       wraplen = WrapHeaders;
1915   }
1916   else if (wraplen <= 0 || wraplen > MuttIndexWindow->cols)
1917     wraplen = MuttIndexWindow->cols;
1918
1919   if (tag)
1920   {
1921     /* if header is short enough, simply print it */
1922     if (!(flags & CH_DISPLAY) && mutt_strwidth (tag) + 2 + pfxw +
1923         mutt_strwidth (v) <= wraplen)
1924     {
1925       dprint(4,(debugfile,"mwoh: buf[%s%s: %s] is short enough\n",
1926                 NONULL(pfx), tag, v));
1927       if (fprintf (fp, "%s%s: %s\n", NONULL(pfx), tag, v) <= 0)
1928         goto out;
1929       rc = 0;
1930       goto out;
1931     }
1932     else
1933     {
1934       rc = fold_one_header (fp, tag, v, pfx, wraplen, flags);
1935       goto out;
1936     }
1937   }
1938
1939   p = last = line = (char *)v;
1940   while (p && *p)
1941   {
1942     p = strchr (p, '\n');
1943
1944     /* find maximum line width in current header */
1945     if (p)
1946       *p = 0;
1947     if ((w = my_width (line, 0, flags)) > max)
1948       max = w;
1949     if (p)
1950       *p = '\n';
1951
1952     if (!p)
1953       break;
1954
1955     line = ++p;
1956     if (*p != ' ' && *p != '\t')
1957     {
1958       if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
1959         goto out;
1960       last = p;
1961       max = 0;
1962     }
1963   }
1964
1965   if (last && *last)
1966     if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
1967       goto out;
1968
1969   rc = 0;
1970
1971 out:
1972   FREE (&v);
1973   return rc;
1974 }
1975
1976
1977 /* Note: all RFC2047 encoding should be done outside of this routine, except
1978  * for the "real name."  This will allow this routine to be used more than
1979  * once, if necessary.
1980  *
1981  * Likewise, all IDN processing should happen outside of this routine.
1982  *
1983  * mode == MUTT_WRITE_HEADER_EDITHDRS  => "lite" mode (used for edit_hdrs)
1984  * mode == MUTT_WRITE_HEADER_NORMAL    => normal mode.  write full header + MIME headers
1985  * mode == MUTT_WRITE_HEADER_POSTPONE  => write just the envelope info
1986  * mode == MUTT_WRITE_HEADER_MIME      => for writing protected headers
1987  *
1988  * privacy != 0 => will omit any headers which may identify the user.
1989  *               Output generated is suitable for being sent through
1990  *               anonymous remailer chains.
1991  *
1992  * hide_protected_subject: replaces the Subject header with
1993  * $crypt_protected_headers_subject in NORMAL or POSTPONE mode.
1994  *
1995  */
1996 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
1997                               mutt_write_header_mode mode, int privacy,
1998                               int hide_protected_subject)
1999 {
2000   char buffer[LONG_STRING];
2001   char *p, *q;
2002   LIST *tmp = env->userhdrs;
2003   int has_agent = 0; /* user defined user-agent header field exists */
2004
2005   if (mode == MUTT_WRITE_HEADER_NORMAL && !privacy)
2006     fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
2007
2008   /* OPTUSEFROM is not consulted here so that we can still write a From:
2009    * field if the user sets it with the `my_hdr' command
2010    */
2011   if (env->from && !privacy)
2012   {
2013     buffer[0] = 0;
2014     rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
2015     fprintf (fp, "From: %s\n", buffer);
2016   }
2017
2018   if (env->sender && !privacy)
2019   {
2020     buffer[0] = 0;
2021     rfc822_write_address (buffer, sizeof (buffer), env->sender, 0);
2022     fprintf (fp, "Sender: %s\n", buffer);
2023   }
2024
2025   if (env->to)
2026   {
2027     fputs ("To: ", fp);
2028     mutt_write_address_list (env->to, fp, 4, 0);
2029   }
2030   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2031     fputs ("To: \n", fp);
2032
2033   if (env->cc)
2034   {
2035     fputs ("Cc: ", fp);
2036     mutt_write_address_list (env->cc, fp, 4, 0);
2037   }
2038   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2039     fputs ("Cc: \n", fp);
2040
2041   if (env->bcc)
2042   {
2043     if (mode == MUTT_WRITE_HEADER_POSTPONE ||
2044         mode == MUTT_WRITE_HEADER_EDITHDRS ||
2045         (mode == MUTT_WRITE_HEADER_NORMAL && option(OPTWRITEBCC)))
2046     {
2047       fputs ("Bcc: ", fp);
2048       mutt_write_address_list (env->bcc, fp, 5, 0);
2049     }
2050   }
2051   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2052     fputs ("Bcc: \n", fp);
2053
2054   if (env->subject)
2055   {
2056     if (hide_protected_subject &&
2057         (mode == MUTT_WRITE_HEADER_NORMAL ||
2058          mode == MUTT_WRITE_HEADER_POSTPONE))
2059       mutt_write_one_header (fp, "Subject", ProtHdrSubject, NULL, 0, 0);
2060     else
2061       mutt_write_one_header (fp, "Subject", env->subject, NULL, 0, 0);
2062   }
2063   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2064     fputs ("Subject: \n", fp);
2065
2066   /* save message id if the user has set it */
2067   if (env->message_id && !privacy)
2068     fprintf (fp, "Message-ID: %s\n", env->message_id);
2069
2070   if (env->reply_to)
2071   {
2072     fputs ("Reply-To: ", fp);
2073     mutt_write_address_list (env->reply_to, fp, 10, 0);
2074   }
2075   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2076     fputs ("Reply-To: \n", fp);
2077
2078   if (env->mail_followup_to)
2079   {
2080     fputs ("Mail-Followup-To: ", fp);
2081     mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
2082   }
2083
2084   if (mode == MUTT_WRITE_HEADER_NORMAL ||
2085       mode == MUTT_WRITE_HEADER_POSTPONE)
2086   {
2087     if (env->references)
2088     {
2089       fputs ("References:", fp);
2090       mutt_write_references (env->references, fp, 10);
2091       fputc('\n', fp);
2092     }
2093
2094     /* Add the MIME headers */
2095     fputs ("MIME-Version: 1.0\n", fp);
2096     mutt_write_mime_header (attach, fp);
2097   }
2098
2099   if (env->in_reply_to)
2100   {
2101     fputs ("In-Reply-To:", fp);
2102     mutt_write_references (env->in_reply_to, fp, 0);
2103     fputc ('\n', fp);
2104   }
2105
2106 #ifdef USE_AUTOCRYPT
2107   if (option (OPTAUTOCRYPT))
2108   {
2109     if (mode == MUTT_WRITE_HEADER_NORMAL)
2110       mutt_autocrypt_write_autocrypt_header (env, fp);
2111     if (mode == MUTT_WRITE_HEADER_MIME)
2112       mutt_autocrypt_write_gossip_headers (env, fp);
2113   }
2114 #endif
2115
2116   /* Add any user defined headers */
2117   for (; tmp; tmp = tmp->next)
2118   {
2119     if ((p = strchr (tmp->data, ':')))
2120     {
2121       q = p;
2122
2123       *p = '\0';
2124
2125       p = skip_email_wsp(p + 1);
2126       if (!*p)
2127       {
2128         *q = ':';
2129         continue;  /* don't emit empty fields. */
2130       }
2131
2132       /* check to see if the user has overridden the user-agent field */
2133       if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
2134       {
2135         has_agent = 1;
2136         if (privacy)
2137         {
2138           *q = ':';
2139           continue;
2140         }
2141       }
2142
2143       mutt_write_one_header (fp, tmp->data, p, NULL, 0, 0);
2144       *q = ':';
2145     }
2146   }
2147
2148   if (mode == MUTT_WRITE_HEADER_NORMAL && !privacy &&
2149       option (OPTXMAILER) && !has_agent)
2150   {
2151     /* Add a vanity header */
2152     fprintf (fp, "User-Agent: Mutt/%s (%s)\n", MUTT_VERSION, ReleaseDate);
2153   }
2154
2155   return (ferror (fp) == 0 ? 0 : -1);
2156 }
2157
2158 static void encode_headers (LIST *h)
2159 {
2160   char *tmp;
2161   char *p;
2162   int i;
2163
2164   for (; h; h = h->next)
2165   {
2166     if (!(p = strchr (h->data, ':')))
2167       continue;
2168
2169     i = p - h->data;
2170     p = skip_email_wsp(p + 1);
2171     tmp = safe_strdup (p);
2172
2173     if (!tmp)
2174       continue;
2175
2176     rfc2047_encode_string (&tmp);
2177     safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
2178
2179     sprintf (h->data + i, ": %s", NONULL (tmp));  /* __SPRINTF_CHECKED__ */
2180
2181     FREE (&tmp);
2182   }
2183 }
2184
2185 const char *mutt_fqdn(short may_hide_host)
2186 {
2187   char *p = NULL;
2188
2189   if (Fqdn && Fqdn[0] != '@')
2190   {
2191     p = Fqdn;
2192
2193     if (may_hide_host && option(OPTHIDDENHOST))
2194     {
2195       if ((p = strchr(Fqdn, '.')))
2196         p++;
2197
2198       /* sanity check: don't hide the host if
2199        * the fqdn is something like detebe.org.
2200        */
2201
2202       if (!p || !strchr(p, '.'))
2203         p = Fqdn;
2204     }
2205   }
2206
2207   return p;
2208 }
2209
2210 char *mutt_gen_msgid (void)
2211 {
2212   char buf[SHORT_STRING];
2213   time_t now;
2214   struct tm *tm;
2215   const char *fqdn;
2216
2217   now = time (NULL);
2218   tm = gmtime (&now);
2219   if (!(fqdn = mutt_fqdn(0)))
2220     fqdn = NONULL(Hostname);
2221
2222   snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.G%c%u@%s>",
2223             tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
2224             tm->tm_min, tm->tm_sec, MsgIdPfx, (unsigned int)getpid (), fqdn);
2225   MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
2226   return (safe_strdup (buf));
2227 }
2228
2229 static void alarm_handler (int sig)
2230 {
2231   SigAlrm = 1;
2232 }
2233
2234 /* invoke sendmail in a subshell
2235    path (in)            path to program to execute
2236    args (in)            arguments to pass to program
2237    msg (in)             temp file containing message to send
2238    tempfile (out)       if sendmail is put in the background, this points
2239                         to the temporary file containing the stdout of the
2240                         child process. If it is NULL, stderr and stdout
2241                         are not redirected. */
2242 static int
2243 send_msg (const char *path, char **args, const char *msg, char **tempfile)
2244 {
2245   sigset_t set;
2246   int fd, st;
2247   pid_t pid, ppid;
2248
2249   mutt_block_signals_system ();
2250
2251   sigemptyset (&set);
2252   /* we also don't want to be stopped right now */
2253   sigaddset (&set, SIGTSTP);
2254   sigprocmask (SIG_BLOCK, &set, NULL);
2255
2256   if (SendmailWait >= 0 && tempfile)
2257   {
2258     char tmp[_POSIX_PATH_MAX];
2259
2260     mutt_mktemp (tmp, sizeof (tmp));
2261     *tempfile = safe_strdup (tmp);
2262   }
2263
2264   if ((pid = fork ()) == 0)
2265   {
2266     struct sigaction act, oldalrm;
2267
2268     /* save parent's ID before setsid() */
2269     ppid = getppid ();
2270
2271     /* we want the delivery to continue even after the main process dies,
2272      * so we put ourselves into another session right away
2273      */
2274     setsid ();
2275
2276     /* next we close all open files */
2277     close (0);
2278 #if defined(OPEN_MAX)
2279     for (fd = tempfile ? 1 : 3; fd < OPEN_MAX; fd++)
2280       close (fd);
2281 #elif defined(_POSIX_OPEN_MAX)
2282     for (fd = tempfile ? 1 : 3; fd < _POSIX_OPEN_MAX; fd++)
2283       close (fd);
2284 #else
2285     if (tempfile)
2286     {
2287       close (1);
2288       close (2);
2289     }
2290 #endif
2291
2292     /* now the second fork() */
2293     if ((pid = fork ()) == 0)
2294     {
2295       /* "msg" will be opened as stdin */
2296       if (open (msg, O_RDONLY, 0) < 0)
2297       {
2298         unlink (msg);
2299         _exit (S_ERR);
2300       }
2301       unlink (msg);
2302
2303       if (SendmailWait >= 0 && tempfile && *tempfile)
2304       {
2305         /* *tempfile will be opened as stdout */
2306         if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2307           _exit (S_ERR);
2308         /* redirect stderr to *tempfile too */
2309         if (dup (1) < 0)
2310           _exit (S_ERR);
2311       }
2312       else if (tempfile)
2313       {
2314         if (open ("/dev/null", O_WRONLY | O_APPEND) < 0)        /* stdout */
2315           _exit (S_ERR);
2316         if (open ("/dev/null", O_RDWR | O_APPEND) < 0)          /* stderr */
2317           _exit (S_ERR);
2318       }
2319
2320       /* execvpe is a glibc extension */
2321       /* execvpe (path, args, mutt_envlist ()); */
2322       execvp (path, args);
2323       _exit (S_ERR);
2324     }
2325     else if (pid == -1)
2326     {
2327       unlink (msg);
2328       if (tempfile)
2329         FREE (tempfile);                /* __FREE_CHECKED__ */
2330       _exit (S_ERR);
2331     }
2332
2333     /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
2334      * SendmailWait = 0: wait forever
2335      * SendmailWait < 0: don't wait
2336      */
2337     if (SendmailWait > 0)
2338     {
2339       SigAlrm = 0;
2340       act.sa_handler = alarm_handler;
2341 #ifdef SA_INTERRUPT
2342       /* need to make sure waitpid() is interrupted on SIGALRM */
2343       act.sa_flags = SA_INTERRUPT;
2344 #else
2345       act.sa_flags = 0;
2346 #endif
2347       sigemptyset (&act.sa_mask);
2348       sigaction (SIGALRM, &act, &oldalrm);
2349       alarm (SendmailWait);
2350     }
2351     else if (SendmailWait < 0)
2352       _exit (0xff & EX_OK);
2353
2354     if (waitpid (pid, &st, 0) > 0)
2355     {
2356       st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
2357       if (SendmailWait && st == (0xff & EX_OK) && tempfile && *tempfile)
2358       {
2359         unlink (*tempfile); /* no longer needed */
2360         FREE (tempfile);                /* __FREE_CHECKED__ */
2361       }
2362     }
2363     else
2364     {
2365       st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
2366         S_BKG : S_ERR;
2367       if (SendmailWait > 0 && tempfile && *tempfile)
2368       {
2369         unlink (*tempfile);
2370         FREE (tempfile);                /* __FREE_CHECKED__ */
2371       }
2372     }
2373
2374     /* reset alarm; not really needed, but... */
2375     alarm (0);
2376     sigaction (SIGALRM, &oldalrm, NULL);
2377
2378     if (kill (ppid, 0) == -1 && errno == ESRCH && tempfile && *tempfile)
2379     {
2380       /* the parent is already dead */
2381       unlink (*tempfile);
2382       FREE (tempfile);          /* __FREE_CHECKED__ */
2383     }
2384
2385     _exit (st);
2386   }
2387
2388   sigprocmask (SIG_UNBLOCK, &set, NULL);
2389
2390   if (pid != -1 && waitpid (pid, &st, 0) > 0)
2391     st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
2392   else
2393     st = S_ERR; /* error */
2394
2395   mutt_unblock_signals_system (1);
2396
2397   return (st);
2398 }
2399
2400 static char **
2401 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
2402 {
2403   for (; addr; addr = addr->next)
2404   {
2405     /* weed out group mailboxes, since those are for display only */
2406     if (addr->mailbox && !addr->group)
2407     {
2408       if (*argslen == *argsmax)
2409         safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2410       args[(*argslen)++] = addr->mailbox;
2411     }
2412   }
2413   return (args);
2414 }
2415
2416 static char **
2417 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2418 {
2419   if (*argslen == *argsmax)
2420     safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2421   args[(*argslen)++] = s;
2422   return (args);
2423 }
2424
2425 int
2426 mutt_invoke_sendmail (ADDRESS *from,    /* the sender */
2427                       ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2428                       const char *msg, /* file containing message */
2429                       int eightbit) /* message contains 8bit chars */
2430 {
2431   char *ps = NULL, *path = NULL, *s = safe_strdup (Sendmail), *childout = NULL;
2432   char **args = NULL;
2433   size_t argslen = 0, argsmax = 0;
2434   char **extra_args = NULL;
2435   size_t extra_argslen = 0, extra_argsmax = 0;
2436   int i;
2437
2438   /* ensure that $sendmail is set to avoid a crash. http://dev.mutt.org/trac/ticket/3548 */
2439   if (!s)
2440   {
2441     mutt_error(_("$sendmail must be set in order to send mail."));
2442     return -1;
2443   }
2444
2445   ps = s;
2446   i = 0;
2447   while ((ps = strtok (ps, " ")))
2448   {
2449     if (argslen == argsmax)
2450       safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2451
2452     if (i)
2453     {
2454       if (!mutt_strcmp (ps, "--"))
2455         break;
2456       args[argslen++] = ps;
2457     }
2458     else
2459     {
2460       path = safe_strdup (ps);
2461       ps = strrchr (ps, '/');
2462       if (ps)
2463         ps++;
2464       else
2465         ps = path;
2466       args[argslen++] = ps;
2467     }
2468     ps = NULL;
2469     i++;
2470   }
2471
2472   /* If Sendmail contained a "--", we save the recipients to append to
2473    * args after other possible options added below. */
2474   if (ps)
2475   {
2476     ps = NULL;
2477     while ((ps = strtok (ps, " ")))
2478     {
2479       if (extra_argslen == extra_argsmax)
2480         safe_realloc (&extra_args, sizeof (char *) * (extra_argsmax += 5));
2481
2482       extra_args[extra_argslen++] = ps;
2483       ps = NULL;
2484     }
2485   }
2486
2487   if (eightbit && option (OPTUSE8BITMIME))
2488     args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2489
2490   if (option (OPTENVFROM))
2491   {
2492     if (EnvFrom)
2493     {
2494       args = add_option (args, &argslen, &argsmax, "-f");
2495       args = add_args   (args, &argslen, &argsmax, EnvFrom);
2496     }
2497     else if (from && !from->next)
2498     {
2499       args = add_option (args, &argslen, &argsmax, "-f");
2500       args = add_args   (args, &argslen, &argsmax, from);
2501     }
2502   }
2503
2504   if (DsnNotify)
2505   {
2506     args = add_option (args, &argslen, &argsmax, "-N");
2507     args = add_option (args, &argslen, &argsmax, DsnNotify);
2508   }
2509   if (DsnReturn)
2510   {
2511     args = add_option (args, &argslen, &argsmax, "-R");
2512     args = add_option (args, &argslen, &argsmax, DsnReturn);
2513   }
2514   args = add_option (args, &argslen, &argsmax, "--");
2515   for (i = 0; i < extra_argslen; i++)
2516     args = add_option (args, &argslen, &argsmax, extra_args[i]);
2517   args = add_args (args, &argslen, &argsmax, to);
2518   args = add_args (args, &argslen, &argsmax, cc);
2519   args = add_args (args, &argslen, &argsmax, bcc);
2520
2521   if (argslen == argsmax)
2522     safe_realloc (&args, sizeof (char *) * (++argsmax));
2523
2524   args[argslen++] = NULL;
2525
2526   /* Some user's $sendmail command uses gpg for password decryption,
2527    * and is set up to prompt using ncurses pinentry.  If we
2528    * mutt_endwin() it leaves other users staring at a blank screen.
2529    * So instead, just force a hard redraw on the next refresh. */
2530   if (!option (OPTNOCURSES))
2531     mutt_need_hard_redraw ();
2532
2533   if ((i = send_msg (path, args, msg, option(OPTNOCURSES) ? NULL : &childout)) != (EX_OK & 0xff))
2534   {
2535     if (i != S_BKG)
2536     {
2537       const char *e;
2538
2539       e = mutt_strsysexit (i);
2540       mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2541       if (childout)
2542       {
2543         struct stat st;
2544
2545         if (stat (childout, &st) == 0 && st.st_size > 0)
2546           mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2547       }
2548     }
2549   }
2550   else if (childout)
2551     unlink (childout);
2552
2553   FREE (&childout);
2554   FREE (&path);
2555   FREE (&s);
2556   FREE (&args);
2557   FREE (&extra_args);
2558
2559   if (i == (EX_OK & 0xff))
2560     i = 0;
2561   else if (i == S_BKG)
2562     i = 1;
2563   else
2564     i = -1;
2565   return (i);
2566 }
2567
2568 /* For postponing (!final) do the necessary encodings only */
2569 void mutt_prepare_envelope (ENVELOPE *env, int final)
2570 {
2571   char buffer[LONG_STRING];
2572
2573   if (final)
2574   {
2575     if (env->bcc && !(env->to || env->cc))
2576     {
2577       /* some MTA's will put an Apparently-To: header field showing the Bcc:
2578        * recipients if there is no To: or Cc: field, so attempt to suppress
2579        * it by using an empty To: field.
2580        */
2581       env->to = rfc822_new_address ();
2582       env->to->group = 1;
2583       env->to->next = rfc822_new_address ();
2584
2585       buffer[0] = 0;
2586       rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2587                   RFC822Specials);
2588
2589       env->to->mailbox = safe_strdup (buffer);
2590     }
2591
2592     mutt_set_followup_to (env);
2593
2594     if (!env->message_id)
2595       env->message_id = mutt_gen_msgid ();
2596   }
2597
2598   /* Take care of 8-bit => 7-bit conversion. */
2599   rfc2047_encode_envelope (env);
2600   encode_headers (env->userhdrs);
2601 }
2602
2603 void mutt_unprepare_envelope (ENVELOPE *env)
2604 {
2605   LIST *item;
2606
2607   for (item = env->userhdrs; item; item = item->next)
2608     rfc2047_decode (&item->data);
2609
2610   rfc822_free_address (&env->mail_followup_to);
2611
2612   /* back conversions */
2613   rfc2047_decode_envelope (env);
2614 }
2615
2616 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2617                                  ADDRESS *env_from)
2618 {
2619   int i, ret = 0;
2620   FILE *f;
2621   char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2622   MESSAGE *msg = NULL;
2623
2624   if (!h)
2625   {
2626     /* Try to bounce each message out, aborting if we get any failures. */
2627     for (i=0; i<Context->msgcount; i++)
2628       if (Context->hdrs[i]->tagged)
2629         ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2630     return ret;
2631   }
2632
2633   /* If we failed to open a message, return with error */
2634   if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2635     return -1;
2636
2637   if (!fp) fp = msg->fp;
2638
2639   mutt_mktemp (tempfile, sizeof (tempfile));
2640   if ((f = safe_fopen (tempfile, "w")) != NULL)
2641   {
2642     int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2643     char* msgid_str;
2644
2645     if (!option (OPTBOUNCEDELIVERED))
2646       ch_flags |= CH_WEED_DELIVERED;
2647
2648     fseeko (fp, h->offset, 0);
2649     fprintf (f, "Resent-From: %s", resent_from);
2650     fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2651     msgid_str = mutt_gen_msgid();
2652     fprintf (f, "Resent-Message-ID: %s\n", msgid_str);
2653     fputs ("Resent-To: ", f);
2654     mutt_write_address_list (to, f, 11, 0);
2655     mutt_copy_header (fp, h, f, ch_flags, NULL);
2656     fputc ('\n', f);
2657     mutt_copy_bytes (fp, f, h->content->length);
2658     safe_fclose (&f);
2659     FREE (&msgid_str);
2660
2661 #if USE_SMTP
2662     if (SmtpUrl)
2663       ret = mutt_smtp_send (env_from, to, NULL, NULL, tempfile,
2664                             h->content->encoding == ENC8BIT);
2665     else
2666 #endif /* USE_SMTP */
2667       ret = mutt_invoke_sendmail (env_from, to, NULL, NULL, tempfile,
2668                                   h->content->encoding == ENC8BIT);
2669   }
2670
2671   if (msg)
2672     mx_close_message (Context, &msg);
2673
2674   return ret;
2675 }
2676
2677 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2678 {
2679   ADDRESS *from, *resent_to;
2680   const char *fqdn = mutt_fqdn (1);
2681   char resent_from[STRING];
2682   int ret;
2683   char *err = NULL;
2684
2685   resent_from[0] = '\0';
2686   from = mutt_default_from ();
2687
2688   /*
2689    * mutt_default_from() does not use $realname if the real name is not set
2690    * in $from, so we add it here.  The reason it is not added in
2691    * mutt_default_from() is that during normal sending, we execute
2692    * send-hooks and set the realname last so that it can be changed based
2693    * upon message criteria.
2694    */
2695   if (! from->personal)
2696     from->personal = safe_strdup(Realname);
2697
2698   if (fqdn)
2699     rfc822_qualify (from, fqdn);
2700
2701   rfc2047_encode_adrlist (from, "Resent-From");
2702   if (mutt_addrlist_to_intl (from, &err))
2703   {
2704     mutt_error (_("Bad IDN %s while preparing resent-from."),
2705                 err);
2706     FREE (&err);
2707     rfc822_free_address (&from);
2708     return -1;
2709   }
2710   rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2711
2712   /*
2713    * prepare recipient list. idna conversion appears to happen before this
2714    * function is called, since the user receives confirmation of the address
2715    * list being bounced to.
2716    */
2717   resent_to = rfc822_cpy_adr(to, 0);
2718   rfc2047_encode_adrlist(resent_to, "Resent-To");
2719
2720   ret = _mutt_bounce_message (fp, h, resent_to, resent_from, from);
2721
2722   rfc822_free_address (&resent_to);
2723   rfc822_free_address (&from);
2724
2725   return ret;
2726 }
2727
2728
2729 /* given a list of addresses, return a list of unique addresses */
2730 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2731 {
2732   ADDRESS *top = addr;
2733   ADDRESS **last = &top;
2734   ADDRESS *tmp;
2735   int dup;
2736
2737   while (addr)
2738   {
2739     for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next)
2740     {
2741       if (tmp->mailbox && addr->mailbox &&
2742           !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2743       {
2744         dup = 1;
2745         break;
2746       }
2747     }
2748
2749     if (dup)
2750     {
2751       dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
2752                   addr->mailbox));
2753
2754       *last = addr->next;
2755
2756       addr->next = NULL;
2757       rfc822_free_address(&addr);
2758
2759       addr = *last;
2760     }
2761     else
2762     {
2763       last = &addr->next;
2764       addr = addr->next;
2765     }
2766   }
2767
2768   return (top);
2769 }
2770
2771 static void set_noconv_flags (BODY *b, short flag)
2772 {
2773   for (; b; b = b->next)
2774   {
2775     if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2776       set_noconv_flags (b->parts, flag);
2777     else if (b->type == TYPETEXT && b->noconv)
2778     {
2779       if (flag)
2780         mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2781       else
2782         mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2783     }
2784   }
2785 }
2786
2787 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, const char *fcc)
2788 {
2789   CONTEXT f;
2790   MESSAGE *msg;
2791   char tempfile[_POSIX_PATH_MAX];
2792   FILE *tempfp = NULL;
2793   int r, need_buffy_cleanup = 0;
2794   struct stat st;
2795   char buf[SHORT_STRING];
2796   int onm_flags;
2797
2798   if (post)
2799     set_noconv_flags (hdr->content, 1);
2800
2801   if (mx_open_mailbox (path, MUTT_APPEND | MUTT_QUIET, &f) == NULL)
2802   {
2803     dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2804                 path));
2805     return (-1);
2806   }
2807
2808   /* We need to add a Content-Length field to avoid problems where a line in
2809    * the message body begins with "From "
2810    */
2811   if (f.magic == MUTT_MMDF || f.magic == MUTT_MBOX)
2812   {
2813     mutt_mktemp (tempfile, sizeof (tempfile));
2814     if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2815     {
2816       mutt_perror (tempfile);
2817       mx_close_mailbox (&f, NULL);
2818       return (-1);
2819     }
2820     /* remember new mail status before appending message */
2821     need_buffy_cleanup = 1;
2822     stat (path, &st);
2823   }
2824
2825   hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2826   onm_flags = MUTT_ADD_FROM;
2827   if (post)
2828     onm_flags |= MUTT_SET_DRAFT;
2829   if ((msg = mx_open_new_message (&f, hdr, onm_flags)) == NULL)
2830   {
2831     mx_close_mailbox (&f, NULL);
2832     return (-1);
2833   }
2834
2835   /* post == 1 => postpone message.
2836    * post == 0 => Normal mode.
2837    * */
2838   mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content,
2839                             post ? MUTT_WRITE_HEADER_POSTPONE : MUTT_WRITE_HEADER_NORMAL,
2840                             0,
2841                             option (OPTCRYPTPROTHDRSREAD) &&
2842                             mutt_should_hide_protected_subject (hdr));
2843
2844   /* (postponment) if this was a reply of some sort, <msgid> contains the
2845    * Message-ID: of message replied to.  Save it using a special X-Mutt-
2846    * header so it can be picked up if the message is recalled at a later
2847    * point in time.  This will allow the message to be marked as replied if
2848    * the same mailbox is still open.
2849    */
2850   if (post && msgid)
2851     fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2852
2853   /* (postponment) save the Fcc: using a special X-Mutt- header so that
2854    * it can be picked up when the message is recalled
2855    */
2856   if (post && fcc)
2857     fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2858
2859   if (f.magic == MUTT_MMDF || f.magic == MUTT_MBOX)
2860     fprintf (msg->fp, "Status: RO\n");
2861
2862   /* mutt_write_rfc822_header() only writes out a Date: header with
2863    * mode == 0, i.e. _not_ postponment; so write out one ourself */
2864   if (post)
2865     fprintf (msg->fp, "%s", mutt_make_date (buf, sizeof (buf)));
2866
2867   /* (postponment) if the mail is to be signed or encrypted, save this info */
2868   if ((WithCrypto & APPLICATION_PGP)
2869       && post && (hdr->security & APPLICATION_PGP))
2870   {
2871     fputs ("X-Mutt-PGP: ", msg->fp);
2872     if (hdr->security & ENCRYPT)
2873       fputc ('E', msg->fp);
2874     if (hdr->security & OPPENCRYPT)
2875       fputc ('O', msg->fp);
2876     if (hdr->security & SIGN)
2877     {
2878       fputc ('S', msg->fp);
2879       if (PgpSignAs)
2880         fprintf (msg->fp, "<%s>", PgpSignAs);
2881     }
2882     if (hdr->security & INLINE)
2883       fputc ('I', msg->fp);
2884 #ifdef USE_AUTOCRYPT
2885     if (hdr->security & AUTOCRYPT)
2886       fputc ('A', msg->fp);
2887     if (hdr->security & AUTOCRYPT_OVERRIDE)
2888       fputc ('Z', msg->fp);
2889 #endif
2890     fputc ('\n', msg->fp);
2891   }
2892
2893   /* (postponment) if the mail is to be signed or encrypted, save this info */
2894   if ((WithCrypto & APPLICATION_SMIME)
2895       && post && (hdr->security & APPLICATION_SMIME))
2896   {
2897     fputs ("X-Mutt-SMIME: ", msg->fp);
2898     if (hdr->security & ENCRYPT)
2899     {
2900       fputc ('E', msg->fp);
2901       if (SmimeCryptAlg)
2902         fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2903     }
2904     if (hdr->security & OPPENCRYPT)
2905       fputc ('O', msg->fp);
2906     if (hdr->security & SIGN)
2907     {
2908       fputc ('S', msg->fp);
2909       if (SmimeSignAs)
2910         fprintf (msg->fp, "<%s>", SmimeSignAs);
2911     }
2912     if (hdr->security & INLINE)
2913       fputc ('I', msg->fp);
2914     fputc ('\n', msg->fp);
2915   }
2916
2917 #ifdef MIXMASTER
2918   /* (postponement) if the mail is to be sent through a mixmaster
2919    * chain, save that information
2920    */
2921
2922   if (post && hdr->chain && hdr->chain)
2923   {
2924     LIST *p;
2925
2926     fputs ("X-Mutt-Mix:", msg->fp);
2927     for (p = hdr->chain; p; p = p->next)
2928       fprintf (msg->fp, " %s", (char *) p->data);
2929
2930     fputc ('\n', msg->fp);
2931   }
2932 #endif
2933
2934   if (tempfp)
2935   {
2936     char sasha[LONG_STRING];
2937     int lines = 0;
2938
2939     mutt_write_mime_body (hdr->content, tempfp);
2940
2941     /* make sure the last line ends with a newline.  Emacs doesn't ensure
2942      * this will happen, and it can cause problems parsing the mailbox
2943      * later.
2944      */
2945     fseek (tempfp, -1, 2);
2946     if (fgetc (tempfp) != '\n')
2947     {
2948       fseek (tempfp, 0, 2);
2949       fputc ('\n', tempfp);
2950     }
2951
2952     fflush (tempfp);
2953     if (ferror (tempfp))
2954     {
2955       dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2956       safe_fclose (&tempfp);
2957       unlink (tempfile);
2958       mx_commit_message (msg, &f);      /* XXX - really? */
2959       mx_close_message (&f, &msg);
2960       mx_close_mailbox (&f, NULL);
2961       return -1;
2962     }
2963
2964     /* count the number of lines */
2965     rewind (tempfp);
2966     while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2967       lines++;
2968     fprintf (msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftello (tempfp));
2969     fprintf (msg->fp, "Lines: %d\n\n", lines);
2970
2971     /* copy the body and clean up */
2972     rewind (tempfp);
2973     r = mutt_copy_stream (tempfp, msg->fp);
2974     if (fclose (tempfp) != 0)
2975       r = -1;
2976     /* if there was an error, leave the temp version */
2977     if (!r)
2978       unlink (tempfile);
2979   }
2980   else
2981   {
2982     fputc ('\n', msg->fp); /* finish off the header */
2983     r = mutt_write_mime_body (hdr->content, msg->fp);
2984   }
2985
2986   if (mx_commit_message (msg, &f) != 0)
2987     r = -1;
2988   mx_close_message (&f, &msg);
2989   mx_close_mailbox (&f, NULL);
2990
2991   if (!post && need_buffy_cleanup)
2992     mutt_buffy_cleanup (path, &st);
2993
2994   if (post)
2995     set_noconv_flags (hdr->content, 0);
2996
2997   return r;
2998 }