sort out some prototypes, put them where they belong.
[apps/madmutt.git] / sendlib.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #define _SENDLIB_C 1
11
12 #if HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #include <string.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <ctype.h>
21 #include <sys/stat.h>
22 #include <signal.h>
23 #include <sys/wait.h>
24 #include <fcntl.h>
25 #include <sys/utsname.h>
26
27 #include <lib-lib/lib-lib.h>
28
29 #include <lib-sys/exit.h>
30 #include <lib-sys/mutt_signal.h>
31
32 #include <lib-mime/mime.h>
33
34 #include <lib-ui/curses.h>
35
36 #include "mutt.h"
37 #include "handler.h"
38 #include "recvattach.h"
39 #include "mx.h"
40 #include "copy.h"
41 #include "pager.h"
42 #include "charset.h"
43 #include <lib-crypt/crypt.h>
44 #include "mutt_idna.h"
45
46 #ifdef USE_LIBESMTP
47 # include "mutt_libesmtp.h"
48 #endif /* USE_LIBESMTP */
49
50 #ifdef USE_NNTP
51 #include <nntp.h>
52 #endif
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 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
70
71 static void transform_to_7bit (BODY * a, FILE * fpin);
72
73 static void encode_quoted (fgetconv_t * fc, FILE * fout, int istext)
74 {
75   int c, linelen = 0;
76   char line[77], savechar;
77
78   while ((c = fgetconv (fc)) != EOF) {
79     /* Wrap the line if needed. */
80     if (linelen == 76 && ((istext && c != '\n') || !istext)) {
81       /* If the last character is "quoted", then be sure to move all three
82        * characters to the next line.  Otherwise, just move the last
83        * character...
84        */
85       if (line[linelen - 3] == '=') {
86         line[linelen - 3] = 0;
87         fputs (line, fout);
88         fputs ("=\n", fout);
89         line[linelen] = 0;
90         line[0] = '=';
91         line[1] = line[linelen - 2];
92         line[2] = line[linelen - 1];
93         linelen = 3;
94       }
95       else {
96         savechar = line[linelen - 1];
97         line[linelen - 1] = '=';
98         line[linelen] = 0;
99         fputs (line, fout);
100         fputc ('\n', fout);
101         line[0] = savechar;
102         linelen = 1;
103       }
104     }
105
106     /* Escape lines that begin with/only contain "the message separator". */
107     if (linelen == 4 && !m_strncmp("From", line, 4)) {
108       m_strcpy(line, sizeof(line), "=46rom");
109       linelen = 6;
110     }
111     else if (linelen == 4 && !m_strncmp("from", line, 4)) {
112       m_strcpy(line, sizeof(line), "=66rom");
113       linelen = 6;
114     }
115     else if (linelen == 1 && line[0] == '.') {
116       m_strcpy(line, sizeof(line), "=2E");
117       linelen = 3;
118     }
119
120
121     if (c == '\n' && istext) {
122       /* Check to make sure there is no trailing space on this line. */
123       if (linelen > 0
124           && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
125         if (linelen < 74) {
126           sprintf (line + linelen - 1, "=%2.2X",
127                    (unsigned char) line[linelen - 1]);
128           fputs (line, fout);
129         }
130         else {
131           savechar = line[linelen - 1];
132
133           line[linelen - 1] = '=';
134           line[linelen] = 0;
135           fputs (line, fout);
136           fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
137         }
138       }
139       else {
140         line[linelen] = 0;
141         fputs (line, fout);
142       }
143       fputc ('\n', fout);
144       linelen = 0;
145     }
146     else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
147       /* Check to make sure there is enough room for the quoted character.
148        * If not, wrap to the next line.
149        */
150       if (linelen > 73) {
151         line[linelen++] = '=';
152         line[linelen] = 0;
153         fputs (line, fout);
154         fputc ('\n', fout);
155         linelen = 0;
156       }
157       sprintf (line + linelen, "=%2.2X", (unsigned char) c);
158       linelen += 3;
159     }
160     else {
161       /* Don't worry about wrapping the line here.  That will happen during
162        * the next iteration when I'll also know what the next character is.
163        */
164       line[linelen++] = c;
165     }
166   }
167
168   /* Take care of anything left in the buffer */
169   if (linelen > 0) {
170     if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
171       /* take care of trailing whitespace */
172       if (linelen < 74)
173         sprintf (line + linelen - 1, "=%2.2X",
174                  (unsigned char) line[linelen - 1]);
175       else {
176         savechar = line[linelen - 1];
177         line[linelen - 1] = '=';
178         line[linelen] = 0;
179         fputs (line, fout);
180         fputc ('\n', fout);
181         sprintf (line, "=%2.2X", (unsigned char) savechar);
182       }
183     }
184     else
185       line[linelen] = 0;
186     fputs (line, fout);
187   }
188 }
189
190 static char b64_buffer[3];
191 static short b64_num;
192 static short b64_linelen;
193
194 static void b64_flush (FILE * fout)
195 {
196   short i;
197
198   if (!b64_num)
199     return;
200
201   if (b64_linelen >= 72) {
202     fputc ('\n', fout);
203     b64_linelen = 0;
204   }
205
206   for (i = b64_num; i < 3; i++)
207     b64_buffer[i] = '\0';
208
209   fputc(__m_b64chars[(b64_buffer[0] >> 2) & 0x3f], fout);
210   b64_linelen++;
211   fputc(__m_b64chars
212          [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
213   b64_linelen++;
214
215   if (b64_num > 1) {
216     fputc (__m_b64chars
217            [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
218            fout);
219     b64_linelen++;
220     if (b64_num > 2) {
221       fputc (__m_b64chars[b64_buffer[2] & 0x3f], fout);
222       b64_linelen++;
223     }
224   }
225
226   while (b64_linelen % 4) {
227     fputc ('=', fout);
228     b64_linelen++;
229   }
230
231   b64_num = 0;
232 }
233
234
235 static void b64_putc (char c, FILE * fout)
236 {
237   if (b64_num == 3)
238     b64_flush (fout);
239
240   b64_buffer[b64_num++] = c;
241 }
242
243
244 static void encode_base64 (fgetconv_t * fc, FILE * fout, int istext)
245 {
246   int ch, ch1 = EOF;
247
248   b64_num = b64_linelen = 0;
249
250   while ((ch = fgetconv (fc)) != EOF) {
251     if (istext && ch == '\n' && ch1 != '\r')
252       b64_putc ('\r', fout);
253     b64_putc (ch, fout);
254     ch1 = ch;
255   }
256   b64_flush (fout);
257   fputc ('\n', fout);
258 }
259
260 static void encode_8bit (fgetconv_t * fc, FILE * fout, int istext)
261 {
262   int ch;
263
264   while ((ch = fgetconv (fc)) != EOF)
265     fputc (ch, fout);
266 }
267
268
269 int mutt_write_mime_header (BODY * a, FILE * f)
270 {
271   char buffer[STRING];
272   char *t;
273   char *fn;
274   int len;
275   int tmplen;
276   int encode;
277
278   fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
279
280   if (a->parameter) {
281     parameter_t *p;
282
283     len = 25 + m_strlen(a->subtype);        /* approximate len. of content-type */
284
285     for (p = a->parameter; p; p = p->next) {
286       char *tmp;
287
288       if (!p->value)
289         continue;
290
291       fputc (';', f);
292
293       buffer[0] = 0;
294       tmp = m_strdup(p->value);
295       encode = rfc2231_encode_string (&tmp);
296       rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
297
298       /* Dirty hack to make messages readable by Outlook Express 
299        * for the Mac: force quotes around the boundary parameter
300        * even when they aren't needed.
301        */
302
303       if (!ascii_strcasecmp (p->attribute, "boundary")
304           && !strcmp (buffer, tmp))
305         snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
306
307       p_delete(&tmp);
308
309       tmplen = m_strlen(buffer) + m_strlen(p->attribute) + 1;
310
311       if (len + tmplen + 2 > 76) {
312         fputs ("\n\t", f);
313         len = tmplen + 8;
314       }
315       else {
316         fputc (' ', f);
317         len += tmplen + 1;
318       }
319
320       fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
321
322     }
323   }
324
325   fputc ('\n', f);
326
327   if (a->description)
328     fprintf (f, "Content-Description: %s\n", a->description);
329
330   fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
331
332   if (a->use_disp) {
333     if (!(fn = a->d_filename))
334       fn = a->filename;
335
336     if (fn) {
337       char *tmp;
338
339       /* Strip off the leading path... */
340       if ((t = strrchr (fn, '/')))
341         t++;
342       else
343         t = fn;
344
345       buffer[0] = 0;
346       tmp = m_strdup(t);
347       encode = rfc2231_encode_string (&tmp);
348       rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
349       p_delete(&tmp);
350       fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
351     }
352   }
353
354   fputc ('\n', f);
355
356   if (a->encoding != ENC7BIT)
357     fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
358
359   /* Do NOT add the terminator here!!! */
360   return (ferror (f) ? -1 : 0);
361 }
362
363 int mutt_write_mime_body (BODY * a, FILE * f)
364 {
365   const char *p;
366   char boundary[SHORT_STRING];
367   char send_charset[SHORT_STRING];
368   FILE *fpin;
369   BODY *t;
370   fgetconv_t *fc;
371
372   if (a->type == TYPEMULTIPART) {
373     /* First, find the boundary to use */
374     if (!(p = parameter_getval(a->parameter, "boundary"))) {
375       mutt_error _("No boundary parameter found! [report this error]");
376
377       return (-1);
378     }
379     m_strcpy(boundary, sizeof(boundary), p);
380
381     for (t = a->parts; t; t = t->next) {
382       fprintf (f, "\n--%s\n", boundary);
383       if (mutt_write_mime_header (t, f) == -1)
384         return -1;
385       fputc ('\n', f);
386       if (mutt_write_mime_body (t, f) == -1)
387         return -1;
388     }
389     fprintf (f, "\n--%s--\n", boundary);
390     return (ferror (f) ? -1 : 0);
391   }
392
393   /* This is pretty gross, but it's the best solution for now... */
394   if (a->type == TYPEAPPLICATION && !m_strcmp(a->subtype, "pgp-encrypted")) {
395     fputs ("Version: 1\n", f);
396     return 0;
397   }
398
399   if ((fpin = fopen (a->filename, "r")) == NULL) {
400     mutt_error (_("%s no longer exists!"), a->filename);
401     return -1;
402   }
403
404   if (a->type == TYPETEXT && (!a->noconv))
405     fc = fgetconv_open (fpin, a->file_charset,
406                         mutt_get_body_charset (send_charset,
407                                                sizeof (send_charset), a), 0);
408   else
409     fc = fgetconv_open (fpin, 0, 0, 0);
410
411 #define write_as_text_part(a)  (mutt_is_text_part(a) || mutt_is_application_pgp(a))
412   if (a->encoding == ENCQUOTEDPRINTABLE)
413     encode_quoted (fc, f, write_as_text_part (a));
414   else if (a->encoding == ENCBASE64)
415     encode_base64 (fc, f, write_as_text_part (a));
416   else if (a->type == TYPETEXT && (!a->noconv))
417     encode_8bit (fc, f, write_as_text_part (a));
418   else
419     mutt_copy_stream (fpin, f);
420 #undef write_as_text_part
421
422   fgetconv_close (&fc);
423   fclose (fpin);
424
425   return (ferror (f) ? -1 : 0);
426 }
427
428 typedef struct {
429   int from;
430   int whitespace;
431   int dot;
432   int linelen;
433   int was_cr;
434 } CONTENT_STATE;
435
436
437 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
438                                  ssize_t dlen)
439 {
440   int from = s->from;
441   int whitespace = s->whitespace;
442   int dot = s->dot;
443   int linelen = s->linelen;
444   int was_cr = s->was_cr;
445
446   if (!d) {                     /* This signals EOF */
447     if (was_cr)
448       info->binary = 1;
449     if (linelen > info->linemax)
450       info->linemax = linelen;
451
452     return;
453   }
454
455   for (; dlen; d++, dlen--) {
456     char ch = *d;
457
458     if (was_cr) {
459       was_cr = 0;
460       if (ch != '\n') {
461         info->binary = 1;
462       }
463       else {
464         if (whitespace)
465           info->space = 1;
466         if (dot)
467           info->dot = 1;
468         if (linelen > info->linemax)
469           info->linemax = linelen;
470         whitespace = 0;
471         dot = 0;
472         linelen = 0;
473         continue;
474       }
475     }
476
477     linelen++;
478     if (ch == '\n') {
479       info->crlf++;
480       if (whitespace)
481         info->space = 1;
482       if (dot)
483         info->dot = 1;
484       if (linelen > info->linemax)
485         info->linemax = linelen;
486       whitespace = 0;
487       linelen = 0;
488       dot = 0;
489     }
490     else if (ch == '\r') {
491       info->crlf++;
492       info->cr = 1;
493       was_cr = 1;
494       continue;
495     }
496     else if (ch & 0x80)
497       info->hibin++;
498     else if (ch == '\t' || ch == '\f') {
499       info->ascii++;
500       whitespace++;
501     }
502     else if (ch < 32 || ch == 127)
503       info->lobin++;
504     else {
505       if (linelen == 1) {
506         if ((ch == 'F') || (ch == 'f'))
507           from = 1;
508         else
509           from = 0;
510         if (ch == '.')
511           dot = 1;
512         else
513           dot = 0;
514       }
515       else if (from) {
516         if (linelen == 2 && ch != 'r')
517           from = 0;
518         else if (linelen == 3 && ch != 'o')
519           from = 0;
520         else if (linelen == 4) {
521           if (ch == 'm')
522             info->from = 1;
523           from = 0;
524         }
525       }
526       if (ch == ' ')
527         whitespace++;
528       info->ascii++;
529     }
530
531     if (linelen > 1)
532       dot = 0;
533     if (ch != ' ' && ch != '\t')
534       whitespace = 0;
535   }
536
537   s->from = from;
538   s->whitespace = whitespace;
539   s->dot = dot;
540   s->linelen = linelen;
541   s->was_cr = was_cr;
542
543 }
544
545 /*
546  * Find the best charset conversion of the file from fromcode into one
547  * of the tocodes. If successful, set *tocode and CONTENT *info and
548  * return the number of characters converted inexactly. If no
549  * conversion was possible, return -1.
550  *
551  * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
552  * which would otherwise prevent us from knowing the number of inexact
553  * conversions. Where the candidate target charset is UTF-8 we avoid
554  * doing the second conversion because iconv_open("UTF-8", "UTF-8")
555  * fails with some libraries.
556  *
557  * We assume that the output from iconv is never more than 4 times as
558  * long as the input for any pair of charsets we might be interested
559  * in.
560  */
561 static ssize_t convert_file_to (FILE * file, const char *fromcode,
562                                int ncodes, const char **tocodes,
563                                int *tocode, CONTENT * info)
564 {
565 #ifdef HAVE_ICONV
566   iconv_t cd1, *cd;
567   char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
568   const char *ib, *ub;
569   char *ob;
570   ssize_t ibl, obl, ubl, ubl1, n, ret;
571   int i;
572   CONTENT *infos;
573   CONTENT_STATE *states;
574   ssize_t *score;
575
576   cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
577   if (cd1 == MUTT_ICONV_ERROR)
578     return -1;
579
580   cd = p_new(iconv_t, ncodes);
581   score = p_new(ssize_t, ncodes);
582   states = p_new(CONTENT_STATE, ncodes);
583   infos = p_new(CONTENT, ncodes);
584
585   for (i = 0; i < ncodes; i++)
586     if (ascii_strcasecmp (tocodes[i], "UTF-8"))
587       cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
588     else
589       /* Special case for conversion to UTF-8 */
590       cd[i] = MUTT_ICONV_ERROR, score[i] = -1;
591
592   rewind (file);
593   ibl = 0;
594   for (;;) {
595
596     /* Try to fill input buffer */
597     n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
598     ibl += n;
599
600     /* Convert to UTF-8 */
601     ib = bufi;
602     ob = bufu, obl = sizeof (bufu);
603     n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
604     assert (n == -1 || !n);
605     if (n == -1 && ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
606       assert (errno == EILSEQ ||
607               (errno == EINVAL && ib == bufi && ibl < ssizeof (bufi)));
608       ret = -1;
609       break;
610     }
611     ubl1 = ob - bufu;
612
613     /* Convert from UTF-8 */
614     for (i = 0; i < ncodes; i++)
615       if (cd[i] != MUTT_ICONV_ERROR && score[i] != -1) {
616         ub = bufu, ubl = ubl1;
617         ob = bufo, obl = sizeof (bufo);
618         n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
619         if (n == -1) {
620           score[i] = -1;
621         }
622         else {
623           score[i] += n;
624           update_content_info (&infos[i], &states[i], bufo, ob - bufo);
625         }
626       }
627       else if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1)
628         /* Special case for conversion to UTF-8 */
629         update_content_info (&infos[i], &states[i], bufu, ubl1);
630
631     if (ibl)
632       /* Save unused input */
633       memmove (bufi, ib, ibl);
634     else if (!ubl1 && ib < bufi + sizeof (bufi)) {
635       ret = 0;
636       break;
637     }
638   }
639
640   if (!ret) {
641     /* Find best score */
642     ret = -1;
643     for (i = 0; i < ncodes; i++) {
644       if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1) {
645         /* Special case for conversion to UTF-8 */
646         *tocode = i;
647         ret = 0;
648         break;
649       }
650       else if (cd[i] == MUTT_ICONV_ERROR || score[i] == -1)
651         continue;
652       else if (ret == -1 || score[i] < ret) {
653         *tocode = i;
654         ret = score[i];
655         if (!ret)
656           break;
657       }
658     }
659     if (ret != -1) {
660       memcpy (info, &infos[*tocode], sizeof (CONTENT));
661       update_content_info (info, &states[*tocode], 0, 0);       /* EOF */
662     }
663   }
664
665   for (i = 0; i < ncodes; i++)
666     if (cd[i] != MUTT_ICONV_ERROR)
667       iconv_close (cd[i]);
668
669   iconv_close (cd1);
670   p_delete(&cd);
671   p_delete(&infos);
672   p_delete(&score);
673   p_delete(&states);
674
675   return ret;
676 #else
677   return -1;
678 #endif /* !HAVE_ICONV */
679 }
680
681 /*
682  * Find the first of the fromcodes that gives a valid conversion and
683  * the best charset conversion of the file into one of the tocodes. If
684  * successful, set *fromcode and *tocode to dynamically allocated
685  * strings, set CONTENT *info, and return the number of characters
686  * converted inexactly. If no conversion was possible, return -1.
687  *
688  * Both fromcodes and tocodes may be colon-separated lists of charsets.
689  * However, if fromcode is zero then fromcodes is assumed to be the
690  * name of a single charset even if it contains a colon.
691  */
692 static ssize_t convert_file_from_to (FILE * file,
693                                     const char *fromcodes,
694                                     const char *tocodes, char **fromcode,
695                                     char **tocode, CONTENT * info)
696 {
697   char *fcode;
698   char **tcode;
699   const char *c, *c1;
700   ssize_t ret;
701   int ncodes, i, cn;
702
703   /* Count the tocodes */
704   ncodes = 0;
705   for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
706     if ((c1 = strchr (c, ':')) == c)
707       continue;
708     ++ncodes;
709   }
710
711   /* Copy them */
712   tcode = p_new(char *, ncodes);
713   for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
714     if ((c1 = strchr (c, ':')) == c)
715       continue;
716     tcode[i] = m_substrdup(c, c1);
717   }
718
719   ret = -1;
720   if (fromcode) {
721     /* Try each fromcode in turn */
722     for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
723       if ((c1 = strchr (c, ':')) == c)
724         continue;
725       fcode = m_substrdup(c, c1);
726
727       ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
728                              &cn, info);
729       if (ret != -1) {
730         *fromcode = fcode;
731         *tocode = tcode[cn];
732         tcode[cn] = 0;
733         break;
734       }
735       p_delete(&fcode);
736     }
737   }
738   else {
739     /* There is only one fromcode */
740     ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
741                            &cn, info);
742     if (ret != -1) {
743       *tocode = tcode[cn];
744       tcode[cn] = 0;
745     }
746   }
747
748   /* Free memory */
749   for (i = 0; i < ncodes; i++)
750     p_delete(&tcode[i]);
751
752   p_delete(tcode);
753
754   return ret;
755 }
756
757 /* 
758  * Analyze the contents of a file to determine which MIME encoding to use.
759  * Also set the body charset, sometimes, or not.
760  */
761 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
762 {
763   CONTENT *info;
764   CONTENT_STATE state;
765   FILE *fp = NULL;
766   char *fromcode = NULL;
767   char *tocode = NULL;
768   char buffer[100];
769   char chsbuf[STRING];
770   ssize_t r;
771
772   struct stat sb;
773
774   if (b && !fname)
775     fname = b->filename;
776
777   if (stat (fname, &sb) == -1) {
778     mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
779     return NULL;
780   }
781
782   if (!S_ISREG (sb.st_mode)) {
783     mutt_error (_("%s isn't a regular file."), fname);
784     return NULL;
785   }
786
787   if ((fp = fopen (fname, "r")) == NULL) {
788     return (NULL);
789   }
790
791   info = p_new(CONTENT, 1);
792   p_clear(&state, 1);
793
794   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
795     const char *chs = parameter_getval(b->parameter, "charset");
796     char *fchs = b->use_disp ? ((FileCharset && *FileCharset) ?
797                                 FileCharset : Charset) : Charset;
798     if (Charset && (chs || SendCharset) &&
799         convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
800                               &fromcode, &tocode, info) != -1) {
801       if (!chs) {
802         charset_canonicalize (chsbuf, sizeof (chsbuf), tocode);
803         parameter_setval(&b->parameter, "charset", chsbuf);
804       }
805       b->file_charset = fromcode;
806       p_delete(&tocode);
807       safe_fclose (&fp);
808       return info;
809     }
810   }
811
812   rewind (fp);
813   while ((r = fread (buffer, 1, sizeof (buffer), fp)))
814     update_content_info (info, &state, buffer, r);
815   update_content_info (info, &state, 0, 0);
816
817   safe_fclose (&fp);
818
819   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
820     parameter_setval(&b->parameter, "charset",
821                      (!info->hibin ? "us-ascii"
822                       : Charset && !charset_is_us_ascii(Charset) ? Charset : "unknown-8bit"));
823
824   return info;
825 }
826
827 /* Given a file with path ``s'', see if there is a registered MIME type.
828  * returns the major MIME type, and copies the subtype to ``d''.  First look
829  * for ~/.mime.types, then look in a system mime.types if we can find one.
830  * The longest match is used so that we can match `ps.gz' when `gz' also
831  * exists.
832  */
833
834 int mutt_lookup_mime_type (BODY * att, const char *path)
835 {
836   FILE *f;
837   char *p, *q, *ct;
838   char buf[LONG_STRING];
839   char subtype[STRING], xtype[STRING];
840   int count;
841   int szf, sze, cur_sze;
842   int type;
843
844   *subtype = '\0';
845   *xtype = '\0';
846   type = TYPEOTHER;
847   cur_sze = 0;
848
849   szf = m_strlen(path);
850
851   for (count = 0; count < 4; count++) {
852     /*
853      * can't use strtok() because we use it in an inner loop below, so use
854      * a switch statement here instead.
855      */
856     switch (count) {
857     case 0:
858       snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL (Homedir));
859       break;
860     case 1:
861       m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
862       break;
863     case 2:
864       m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
865       break;
866     case 3:
867       m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
868       break;
869     default:
870       goto bye;                 /* shouldn't happen */
871     }
872
873     if ((f = fopen (buf, "r")) != NULL) {
874       while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
875         /* weed out any comments */
876         if ((p = strchr (buf, '#')))
877           *p = 0;
878
879         /* remove any leading space. */
880         ct = vskipspaces(buf);
881
882         /* position on the next field in this line */
883         if ((p = strpbrk (ct, " \t")) == NULL)
884           continue;
885         *p++ = 0;
886         p = vskipspaces(p);
887
888         /* cycle through the file extensions */
889         while ((p = strtok (p, " \t\n"))) {
890           sze = m_strlen(p);
891           if ((sze > cur_sze) && (szf >= sze) &&
892               (m_strcasecmp(path + szf - sze, p) == 0
893                || ascii_strcasecmp (path + szf - sze, p) == 0)
894               && (szf == sze || path[szf - sze - 1] == '.'))
895           {
896             /* get the content-type */
897
898             if ((p = strchr (ct, '/')) == NULL) {
899               /* malformed line, just skip it. */
900               break;
901             }
902             *p++ = 0;
903
904             for (q = p; *q && !ISSPACE (*q); q++);
905
906             m_strncpy(subtype, sizeof(subtype), p, q - p);
907
908             if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
909               m_strcpy(xtype, sizeof(xtype), ct);
910
911             cur_sze = sze;
912           }
913           p = NULL;
914         }
915       }
916       fclose (f);
917     }
918   }
919
920 bye:
921
922   if (type != TYPEOTHER || *xtype != '\0') {
923     att->type = type;
924     m_strreplace(&att->subtype, subtype);
925     m_strreplace(&att->xtype, xtype);
926   }
927
928   return (type);
929 }
930
931 void mutt_message_to_7bit (BODY * a, FILE * fp)
932 {
933   char temp[_POSIX_PATH_MAX];
934   char *line = NULL;
935   FILE *fpin = NULL;
936   FILE *fpout = NULL;
937   struct stat sb;
938
939   if (!a->filename && fp)
940     fpin = fp;
941   else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
942     mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
943     return;
944   }
945   else {
946     a->offset = 0;
947     if (stat (a->filename, &sb) == -1) {
948       mutt_perror ("stat");
949       fclose (fpin);
950     }
951     a->length = sb.st_size;
952   }
953
954   mutt_mktemp (temp);
955   if (!(fpout = safe_fopen (temp, "w+"))) {
956     mutt_perror ("fopen");
957     goto cleanup;
958   }
959
960   fseeko (fpin, a->offset, 0);
961   a->parts = mutt_parse_messageRFC822 (fpin, a);
962
963   transform_to_7bit (a->parts, fpin);
964
965   mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
966                  CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
967
968   fputs ("MIME-Version: 1.0\n", fpout);
969   mutt_write_mime_header (a->parts, fpout);
970   fputc ('\n', fpout);
971   mutt_write_mime_body (a->parts, fpout);
972
973 cleanup:
974   p_delete(&line);
975
976   if (fpin && !fp)
977     fclose (fpin);
978   if (fpout)
979     fclose (fpout);
980   else
981     return;
982
983   a->encoding = ENC7BIT;
984   a->d_filename = a->filename;
985   if (a->filename && a->unlink)
986     unlink (a->filename);
987   a->filename = m_strdup(temp);
988   a->unlink = 1;
989   if (stat (a->filename, &sb) == -1) {
990     mutt_perror ("stat");
991     return;
992   }
993   a->length = sb.st_size;
994   body_list_wipe(&a->parts);
995   a->hdr->content = NULL;
996 }
997
998 static void transform_to_7bit (BODY * a, FILE * fpin)
999 {
1000   char buff[_POSIX_PATH_MAX];
1001   STATE s;
1002   struct stat sb;
1003
1004   p_clear(&s, 1);
1005   for (; a; a = a->next) {
1006     if (a->type == TYPEMULTIPART) {
1007       if (a->encoding != ENC7BIT)
1008         a->encoding = ENC7BIT;
1009
1010       transform_to_7bit (a->parts, fpin);
1011     }
1012     else if (mutt_is_message_type (a->type, a->subtype)) {
1013       mutt_message_to_7bit (a, fpin);
1014     }
1015     else {
1016       a->noconv = 1;
1017       a->force_charset = 1;
1018
1019       mutt_mktemp (buff);
1020       if ((s.fpout = safe_fopen (buff, "w")) == NULL) {
1021         mutt_perror ("fopen");
1022         return;
1023       }
1024       s.fpin = fpin;
1025       mutt_decode_attachment (a, &s);
1026       fclose (s.fpout);
1027       a->d_filename = a->filename;
1028       a->filename = m_strdup(buff);
1029       a->unlink = 1;
1030       if (stat (a->filename, &sb) == -1) {
1031         mutt_perror ("stat");
1032         return;
1033       }
1034       a->length = sb.st_size;
1035
1036       mutt_update_encoding (a);
1037       if (a->encoding == ENC8BIT)
1038         a->encoding = ENCQUOTEDPRINTABLE;
1039       else if (a->encoding == ENCBINARY)
1040         a->encoding = ENCBASE64;
1041     }
1042   }
1043 }
1044
1045 /* determine which Content-Transfer-Encoding to use */
1046 static void mutt_set_encoding (BODY * b, CONTENT * info)
1047 {
1048   char send_charset[SHORT_STRING];
1049
1050   if (b->type == TYPETEXT) {
1051     char *chsname =
1052       mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1053     if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1054         || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1055       b->encoding = ENCQUOTEDPRINTABLE;
1056     else if (info->hibin)
1057       b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1058     else
1059       b->encoding = ENC7BIT;
1060   }
1061   else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1062     if (info->lobin || info->hibin) {
1063       if (option (OPTALLOW8BIT) && !info->lobin)
1064         b->encoding = ENC8BIT;
1065       else
1066         mutt_message_to_7bit (b, NULL);
1067     }
1068     else
1069       b->encoding = ENC7BIT;
1070   }
1071   else if (b->type == TYPEAPPLICATION
1072            && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1073     b->encoding = ENC7BIT;
1074   else
1075   {
1076     /* Determine which encoding is smaller  */
1077     if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1078         3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1079       b->encoding = ENCBASE64;
1080     else
1081       b->encoding = ENCQUOTEDPRINTABLE;
1082   }
1083 }
1084
1085 void mutt_stamp_attachment (BODY * a)
1086 {
1087   a->stamp = time (NULL);
1088 }
1089
1090 /* Get a body's character set */
1091
1092 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1093 {
1094     const char *p;
1095
1096     if (b && b->type != TYPETEXT)
1097         return NULL;
1098
1099     p = b ? parameter_getval(b->parameter, "charset") : NULL;
1100     charset_canonicalize(d, dlen, p);
1101     return d;
1102 }
1103
1104
1105 /* Assumes called from send mode where BODY->filename points to actual file */
1106 void mutt_update_encoding (BODY * a)
1107 {
1108   CONTENT *info;
1109   char chsbuff[STRING];
1110
1111   /* override noconv when it's us-ascii */
1112   if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1113     a->noconv = 0;
1114
1115   if (!a->force_charset && !a->noconv)
1116     parameter_delval(&a->parameter, "charset");
1117
1118   if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1119     return;
1120
1121   mutt_set_encoding (a, info);
1122   mutt_stamp_attachment (a);
1123
1124   p_delete(&a->content);
1125   a->content = info;
1126
1127 }
1128
1129 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1130 {
1131   char buffer[LONG_STRING];
1132   BODY *body;
1133   FILE *fp;
1134   int cmflags, chflags;
1135   int pgp = hdr->security;
1136
1137   if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1138       (hdr->security & ENCRYPT)) {
1139     if (!crypt_valid_passphrase (hdr->security))
1140       return (NULL);
1141   }
1142
1143   mutt_mktemp (buffer);
1144   if ((fp = safe_fopen (buffer, "w+")) == NULL)
1145     return NULL;
1146
1147   body = body_new();
1148   body->type = TYPEMESSAGE;
1149   body->subtype = m_strdup("rfc822");
1150   body->filename = m_strdup(buffer);
1151   body->unlink = 1;
1152   body->use_disp = 0;
1153   body->disposition = DISPINLINE;
1154   body->noconv = 1;
1155
1156   mutt_parse_mime_message (ctx, hdr);
1157
1158   chflags = CH_XMIT;
1159   cmflags = 0;
1160
1161   /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1162   if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1163     chflags |= CH_MIME | CH_TXTPLAIN;
1164     cmflags = M_CM_DECODE | M_CM_CHARCONV;
1165     pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1166   }
1167   else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1168     if (mutt_is_multipart_encrypted (hdr->content)) {
1169       chflags |= CH_MIME | CH_NONEWLINE;
1170       cmflags = M_CM_DECODE_PGP;
1171       pgp &= ~PGPENCRYPT;
1172     }
1173     else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1174       chflags |= CH_MIME | CH_TXTPLAIN;
1175       cmflags = M_CM_DECODE | M_CM_CHARCONV;
1176       pgp &= ~PGPENCRYPT;
1177     }
1178     else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1179       chflags |= CH_MIME | CH_TXTPLAIN;
1180       cmflags = M_CM_DECODE | M_CM_CHARCONV;
1181       pgp &= ~SMIMEENCRYPT;
1182     }
1183   }
1184
1185   mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1186
1187   fflush (fp);
1188   rewind (fp);
1189
1190   body->hdr = header_new();
1191   body->hdr->offset = 0;
1192   /* we don't need the user headers here */
1193   body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1194   body->hdr->security = pgp;
1195   mutt_update_encoding (body);
1196   body->parts = body->hdr->content;
1197
1198   fclose (fp);
1199
1200   return (body);
1201 }
1202
1203 BODY *mutt_make_file_attach (const char *path)
1204 {
1205   BODY *att;
1206   CONTENT *info;
1207
1208   att = body_new();
1209   att->filename = m_strdup(path);
1210
1211   /* Attempt to determine the appropriate content-type based on the filename
1212    * suffix.
1213    */
1214   mutt_lookup_mime_type (att, path);
1215
1216   if ((info = mutt_get_content_info (path, att)) == NULL) {
1217     body_list_wipe(&att);
1218     return NULL;
1219   }
1220
1221   if (!att->subtype) {
1222     if (info->lobin == 0
1223         || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1224       /*
1225        * Statistically speaking, there should be more than 10% "lobin" 
1226        * chars if this is really a binary file...
1227        */
1228       att->type = TYPETEXT;
1229       att->subtype = m_strdup("plain");
1230     } else {
1231       att->type = TYPEAPPLICATION;
1232       att->subtype = m_strdup("octet-stream");
1233     }
1234   }
1235
1236   mutt_update_encoding (att);
1237   return att;
1238 }
1239
1240 static int get_toplevel_encoding (BODY * a)
1241 {
1242   int e = ENC7BIT;
1243
1244   for (; a; a = a->next) {
1245     if (a->encoding == ENCBINARY)
1246       return (ENCBINARY);
1247     else if (a->encoding == ENC8BIT)
1248       e = ENC8BIT;
1249   }
1250
1251   return (e);
1252 }
1253
1254 BODY *mutt_make_multipart (BODY * b)
1255 {
1256   BODY *new;
1257
1258   new = body_new();
1259   new->type = TYPEMULTIPART;
1260   new->subtype = m_strdup("mixed");
1261   new->encoding = get_toplevel_encoding (b);
1262   parameter_set_boundary(&new->parameter);
1263   new->use_disp = 0;
1264   new->disposition = DISPINLINE;
1265   new->parts = b;
1266
1267   return new;
1268 }
1269
1270 /* remove the multipart body if it exists */
1271 BODY *mutt_remove_multipart (BODY * b)
1272 {
1273   BODY *t;
1274
1275   if (b->parts) {
1276     t = b;
1277     b = b->parts;
1278     t->parts = NULL;
1279     body_list_wipe(&t);
1280   }
1281   return b;
1282 }
1283
1284 char *mutt_make_date (char *s, ssize_t len)
1285 {
1286   time_t t = time (NULL);
1287   struct tm *l = localtime (&t);
1288   time_t tz = mutt_local_tz (t);
1289
1290   tz /= 60;
1291
1292   snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1293             Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1294             l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1295             (int) tz / 60, (int) abs (tz) % 60);
1296   return (s);
1297 }
1298
1299 /* wrapper around mutt_write_address() so we can handle very large
1300    recipient lists without needing a huge temporary buffer in memory */
1301 void mutt_write_address_list (address_t * adr, FILE * fp, int linelen,
1302                               int display)
1303 {
1304   address_t *tmp;
1305   char buf[LONG_STRING];
1306   int count = 0;
1307   int len;
1308
1309   while (adr) {
1310     tmp = adr->next;
1311     adr->next = NULL;
1312     buf[0] = 0;
1313     rfc822_write_address (buf, sizeof (buf), adr, display);
1314     len = m_strlen(buf);
1315     if (count && linelen + len > 74) {
1316       fputs ("\n\t", fp);
1317       linelen = len + 8;        /* tab is usually about 8 spaces... */
1318     }
1319     else {
1320       if (count && adr->mailbox) {
1321         fputc (' ', fp);
1322         linelen++;
1323       }
1324       linelen += len;
1325     }
1326     fputs (buf, fp);
1327     adr->next = tmp;
1328     if (!adr->group && adr->next && adr->next->mailbox) {
1329       linelen++;
1330       fputc (',', fp);
1331     }
1332     adr = adr->next;
1333     count++;
1334   }
1335   fputc ('\n', fp);
1336 }
1337
1338 /* arbitrary number of elements to grow the array by */
1339 #define REF_INC 16
1340
1341 #define TrimRef 10
1342
1343 /* need to write the list in reverse because they are stored in reverse order
1344  * when parsed to speed up threading
1345  */
1346 void mutt_write_references (string_list_t * r, FILE * f)
1347 {
1348   string_list_t **ref = NULL;
1349   int refcnt = 0, refmax = 0;
1350
1351   for (; (TrimRef == 0 || refcnt < TrimRef) && r; r = r->next) {
1352     if (refcnt == refmax)
1353       p_realloc(&ref, refmax += REF_INC);
1354     ref[refcnt++] = r;
1355   }
1356
1357   while (refcnt-- > 0) {
1358     fputc (' ', f);
1359     fputs (ref[refcnt]->data, f);
1360   }
1361
1362   p_delete(&ref);
1363 }
1364
1365 /* Note: all RFC2047 encoding should be done outside of this routine, except
1366  * for the "real name."  This will allow this routine to be used more than
1367  * once, if necessary.
1368  * 
1369  * Likewise, all IDN processing should happen outside of this routine.
1370  *
1371  * mode == 1  => "lite" mode (used for edit_hdrs)
1372  * mode == 0  => normal mode.  write full header + MIME headers
1373  * mode == -1 => write just the envelope info (used for postponing messages)
1374  * 
1375  * privacy != 0 => will omit any headers which may identify the user.
1376  *               Output generated is suitable for being sent through
1377  *               anonymous remailer chains.
1378  *
1379  */
1380
1381 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1382                               int mode, int privacy)
1383 {
1384   char buffer[LONG_STRING];
1385   char *p;
1386   string_list_t *tmp = env->userhdrs;
1387   int has_agent = 0;            /* user defined user-agent header field exists */
1388   list2_t* hdrs = list_from_str (EditorHeaders, " ");
1389
1390 #ifdef USE_NNTP
1391   if (!option (OPTNEWSSEND))
1392 #endif
1393     if (mode == 0 && !privacy)
1394       fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1395
1396 #define EDIT_HEADER(x) (mode != 1 || option(OPTXMAILTO) || (mode == 1 && list_lookup(hdrs,(list_lookup_t*) ascii_strcasecmp,x) >= 0))
1397
1398   /* OPTUSEFROM is not consulted here so that we can still write a From:
1399    * field if the user sets it with the `my_hdr' command
1400    */
1401   if (env->from && !privacy) {
1402     buffer[0] = 0;
1403     rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1404     fprintf (fp, "From: %s\n", buffer);
1405   }
1406
1407   if (env->to) {
1408     fputs ("To: ", fp);
1409     mutt_write_address_list (env->to, fp, 4, 0);
1410   }
1411   else if (mode > 0)
1412 #ifdef USE_NNTP
1413     if (!option (OPTNEWSSEND))
1414 #endif
1415       if (EDIT_HEADER("To:"))
1416         fputs ("To:\n", fp);
1417
1418   if (env->cc) {
1419     fputs ("Cc: ", fp);
1420     mutt_write_address_list (env->cc, fp, 4, 0);
1421   }
1422   else if (mode > 0)
1423 #ifdef USE_NNTP
1424     if (!option (OPTNEWSSEND))
1425 #endif
1426       if (EDIT_HEADER("Cc:"))
1427         fputs ("Cc:\n", fp);
1428
1429   if (env->bcc) {
1430     if (mode != 0 || option (OPTWRITEBCC)) {
1431       fputs ("Bcc: ", fp);
1432       mutt_write_address_list (env->bcc, fp, 5, 0);
1433     }
1434   }
1435   else if (mode > 0)
1436 #ifdef USE_NNTP
1437     if (!option (OPTNEWSSEND))
1438 #endif
1439       if (EDIT_HEADER("Bcc:"))
1440         fputs ("Bcc:\n", fp);
1441
1442 #ifdef USE_NNTP
1443   if (env->newsgroups)
1444     fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1445   else if (mode == 1 && option (OPTNEWSSEND) && EDIT_HEADER("Newsgroups:"))
1446     fputs ("Newsgroups:\n", fp);
1447
1448   if (env->followup_to)
1449     fprintf (fp, "Followup-To: %s\n", env->followup_to);
1450   else if (mode == 1 && option (OPTNEWSSEND) && EDIT_HEADER("Followup-To:"))
1451     fputs ("Followup-To:\n", fp);
1452
1453   if (env->x_comment_to)
1454     fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1455   else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO) &&
1456            EDIT_HEADER("X-Comment-To:"))
1457     fputs ("X-Comment-To:\n", fp);
1458 #endif
1459
1460   if (env->subject)
1461     fprintf (fp, "Subject: %s\n", env->subject);
1462   else if (mode == 1 && EDIT_HEADER("Subject:"))
1463     fputs ("Subject:\n", fp);
1464
1465   /* save message id if the user has set it */
1466   if (env->message_id && !privacy)
1467     fprintf (fp, "Message-ID: %s\n", env->message_id);
1468
1469   if (env->reply_to) {
1470     fputs ("Reply-To: ", fp);
1471     mutt_write_address_list (env->reply_to, fp, 10, 0);
1472   }
1473   else if (mode > 0 && EDIT_HEADER("Reply-To:"))
1474     fputs ("Reply-To:\n", fp);
1475
1476   if (env->mail_followup_to)
1477 #ifdef USE_NNTP
1478     if (!option (OPTNEWSSEND))
1479 #endif
1480     {
1481       fputs ("Mail-Followup-To: ", fp);
1482       mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1483     }
1484
1485   if (mode <= 0) {
1486     if (env->references) {
1487       fputs ("References:", fp);
1488       mutt_write_references (env->references, fp);
1489       fputc ('\n', fp);
1490     }
1491
1492     /* Add the MIME headers */
1493     fputs ("MIME-Version: 1.0\n", fp);
1494     mutt_write_mime_header (attach, fp);
1495   }
1496
1497   if (env->in_reply_to) {
1498     fputs ("In-Reply-To:", fp);
1499     mutt_write_references (env->in_reply_to, fp);
1500     fputc ('\n', fp);
1501   }
1502
1503 #undef EDIT_HEADER
1504
1505   /* Add any user defined headers */
1506   for (; tmp; tmp = tmp->next) {
1507     if ((p = strchr (tmp->data, ':'))) {
1508       p = vskipspaces(p + 1);
1509       if (!*p)
1510         continue;               /* don't emit empty fields. */
1511
1512       /* check to see if the user has overridden the user-agent field */
1513       if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1514         has_agent = 1;
1515         if (privacy)
1516           continue;
1517       }
1518
1519       fputs (tmp->data, fp);
1520       fputc ('\n', fp);
1521     }
1522   }
1523
1524   if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1525     const char *os;
1526
1527     if (OperatingSystem != NULL) {
1528       os = OperatingSystem;
1529     } else {
1530       struct utsname un;
1531       os = (uname(&un) == -1) ? "UNIX" : un.sysname;
1532     }
1533     /* Add a vanity header */
1534     fprintf (fp, "User-Agent: %s (%s)\n", mutt_make_version (0), os);
1535   }
1536
1537   list_del (&hdrs, (list_del_t*)xmemfree);
1538
1539   return (ferror (fp) == 0 ? 0 : -1);
1540 }
1541
1542 static void encode_headers (string_list_t * h)
1543 {
1544   char *tmp;
1545   char *p;
1546   int i;
1547
1548   for (; h; h = h->next) {
1549     if (!(p = strchr (h->data, ':')))
1550       continue;
1551
1552     i = p - h->data;
1553     p = vskipspaces(p + 1);
1554     tmp = m_strdup(p);
1555
1556     if (!tmp)
1557       continue;
1558
1559     rfc2047_encode_string (&tmp);
1560     p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1561
1562     sprintf (h->data + i, ": %s", NONULL (tmp));        /* __SPRINTF_CHECKED__ */
1563
1564     p_delete(&tmp);
1565   }
1566 }
1567
1568 const char *mutt_fqdn (short may_hide_host)
1569 {
1570   char *p = NULL, *q;
1571
1572   if (Fqdn && Fqdn[0] != '@') {
1573     p = Fqdn;
1574
1575     if (may_hide_host && option (OPTHIDDENHOST)) {
1576       if ((p = strchr (Fqdn, '.')))
1577         p++;
1578
1579       /* sanity check: don't hide the host if
1580        * the fqdn is something like detebe.org.
1581        */
1582
1583       if (!p || !(q = strchr (p, '.')))
1584         p = Fqdn;
1585     }
1586   }
1587
1588   return p;
1589 }
1590
1591 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1592 static char mutt_normalized_char(char c)
1593 {
1594     return (isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.';
1595 }
1596
1597 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1598 {
1599 #define APPEND_FMT(fmt, arg) \
1600         if (len > 1) {                                  \
1601             int snlen = snprintf(buf, len, fmt, arg);   \
1602             buf += snlen;                               \
1603             len -= snlen;                               \
1604         }
1605
1606 #define APPEND_BYTE(c) \
1607         if (len > 1) {                                  \
1608             *buf++ = c;                                 \
1609             *buf   = '\0';                              \
1610             len--;                                      \
1611         }
1612
1613     time_t now;
1614     struct tm *tm;
1615
1616     now = time (NULL);
1617     tm = gmtime (&now);
1618
1619     while (*fmt) {
1620         static char MsgIdPfx = 'A';
1621         int c = *fmt++;
1622
1623         if (c != '%') {
1624             APPEND_BYTE(mutt_normalized_char(c));
1625             continue;
1626         }
1627
1628         switch (*fmt++) {
1629           case 0:
1630             return;
1631           case 'd':
1632             APPEND_FMT("%02d", tm->tm_mday);
1633             break;
1634           case 'h':
1635             APPEND_FMT("%02d", tm->tm_hour);
1636             break;
1637           case 'm':
1638             APPEND_FMT("%02d", tm->tm_mon + 1);
1639             break;
1640           case 'M':
1641             APPEND_FMT("%02d", tm->tm_min);
1642             break;
1643           case 'O':
1644             APPEND_FMT("%lo", (unsigned long)now);
1645             break;
1646           case 'p':
1647             APPEND_FMT("%u", (unsigned int)getpid());
1648             break;
1649           case 'P':
1650             APPEND_FMT("%c", MsgIdPfx);
1651             MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1652             break;
1653           case 'r':
1654             APPEND_FMT("%u", (unsigned int)rand());
1655             break;
1656           case 'R':
1657             APPEND_FMT("%x", (unsigned int)rand());
1658             break;
1659           case 's':
1660             APPEND_FMT("%02d", tm->tm_sec);
1661             break;
1662           case 'T':
1663             APPEND_FMT("%u", (unsigned int) now);
1664             break;
1665           case 'X':
1666             APPEND_FMT("%x", (unsigned int) now);
1667             break;
1668           case 'Y':       /* this will break in the year 10000 ;-) */
1669             APPEND_FMT("%04d", tm->tm_year + 1900);
1670             break;
1671           case '%':
1672             APPEND_BYTE('%');
1673             break;
1674           default:       /* invalid formats are replaced by '.' */
1675             APPEND_BYTE('.');
1676             m_strncat(buf, len, ".", 1); 
1677         }
1678     }
1679
1680     *buf = '\0';
1681
1682 #undef APPEND_BYTE
1683 #undef APPEND_FMT
1684 }
1685
1686 char *mutt_gen_msgid (void)
1687 {
1688   char buf[SHORT_STRING];
1689   char localpart[SHORT_STRING];
1690   unsigned int localpart_length;
1691   const char *fqdn;
1692
1693   if (!(fqdn = mutt_fqdn (0)))
1694     fqdn = NONULL (Hostname);
1695
1696   localpart_length = sizeof (buf) - m_strlen(fqdn) - 4;  /* the 4 characters are '<', '@', '>' and '\0' */
1697
1698   mutt_gen_localpart (localpart, localpart_length, MsgIdFormat);
1699
1700   snprintf (buf, sizeof (buf), "<%s@%s>", localpart, fqdn);
1701   return (m_strdup(buf));
1702 }
1703
1704 static RETSIGTYPE alarm_handler (int sig)
1705 {
1706   SigAlrm = 1;
1707 }
1708
1709 /* invoke sendmail in a subshell
1710    path (in)            path to program to execute
1711    args (in)            arguments to pass to program
1712    msg (in)             temp file containing message to send
1713    tempfile (out)       if sendmail is put in the background, this points
1714                         to the temporary file containing the stdout of the
1715                         child process */
1716 static int
1717 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1718 {
1719   sigset_t set;
1720   int fd, st;
1721   pid_t pid, ppid;
1722
1723   mutt_block_signals_system ();
1724
1725   sigemptyset (&set);
1726   /* we also don't want to be stopped right now */
1727   sigaddset (&set, SIGTSTP);
1728   sigprocmask (SIG_BLOCK, &set, NULL);
1729
1730   if (SendmailWait >= 0) {
1731     char tmp[_POSIX_PATH_MAX];
1732
1733     mutt_mktemp (tmp);
1734     *tempfile = m_strdup(tmp);
1735   }
1736
1737   if ((pid = fork ()) == 0) {
1738     struct sigaction act, oldalrm;
1739
1740     /* save parent's ID before setsid() */
1741     ppid = getppid ();
1742
1743     /* we want the delivery to continue even after the main process dies,
1744      * so we put ourselves into another session right away
1745      */
1746     setsid ();
1747
1748     /* next we close all open files */
1749 #if defined(OPEN_MAX)
1750     for (fd = 0; fd < OPEN_MAX; fd++)
1751       close (fd);
1752 #elif defined(_POSIX_OPEN_MAX)
1753     for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1754       close (fd);
1755 #else
1756     close (0);
1757     close (1);
1758     close (2);
1759 #endif
1760
1761     /* now the second fork() */
1762     if ((pid = fork ()) == 0) {
1763       /* "msg" will be opened as stdin */
1764       if (open (msg, O_RDONLY, 0) < 0) {
1765         unlink (msg);
1766         _exit (S_ERR);
1767       }
1768       unlink (msg);
1769
1770       if (SendmailWait >= 0) {
1771         /* *tempfile will be opened as stdout */
1772         if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1773             0)
1774           _exit (S_ERR);
1775         /* redirect stderr to *tempfile too */
1776         if (dup (1) < 0)
1777           _exit (S_ERR);
1778       }
1779       else {
1780         if (open ("/dev/null", O_WRONLY | O_APPEND) < 0)        /* stdout */
1781           _exit (S_ERR);
1782         if (open ("/dev/null", O_RDWR | O_APPEND) < 0)  /* stderr */
1783           _exit (S_ERR);
1784       }
1785
1786       execv (path, (char**)args);
1787       _exit (S_ERR);
1788     }
1789     else if (pid == -1) {
1790       unlink (msg);
1791       p_delete(tempfile);
1792       _exit (S_ERR);
1793     }
1794
1795     /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1796      * SendmailWait = 0: wait forever
1797      * SendmailWait < 0: don't wait
1798      */
1799     if (SendmailWait > 0) {
1800       SigAlrm = 0;
1801       act.sa_handler = alarm_handler;
1802 #ifdef SA_INTERRUPT
1803       /* need to make sure waitpid() is interrupted on SIGALRM */
1804       act.sa_flags = SA_INTERRUPT;
1805 #else
1806       act.sa_flags = 0;
1807 #endif
1808       sigemptyset (&act.sa_mask);
1809       sigaction (SIGALRM, &act, &oldalrm);
1810       alarm (SendmailWait);
1811     }
1812     else if (SendmailWait < 0)
1813       _exit (0xff & EX_OK);
1814
1815     if (waitpid (pid, &st, 0) > 0) {
1816       st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1817       if (SendmailWait && st == (0xff & EX_OK)) {
1818         unlink (*tempfile);     /* no longer needed */
1819         p_delete(tempfile);
1820       }
1821     }
1822     else {
1823       st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1824       if (SendmailWait > 0) {
1825         unlink (*tempfile);
1826         p_delete(tempfile);
1827       }
1828     }
1829
1830     /* reset alarm; not really needed, but... */
1831     alarm (0);
1832     sigaction (SIGALRM, &oldalrm, NULL);
1833
1834     if (kill (ppid, 0) == -1 && errno == ESRCH) {
1835       /* the parent is already dead */
1836       unlink (*tempfile);
1837       p_delete(tempfile);
1838     }
1839
1840     _exit (st);
1841   }
1842
1843   sigprocmask (SIG_UNBLOCK, &set, NULL);
1844
1845   if (pid != -1 && waitpid (pid, &st, 0) > 0)
1846     st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;     /* return child status */
1847   else
1848     st = S_ERR;                 /* error */
1849
1850   mutt_unblock_signals_system (1);
1851
1852   return (st);
1853 }
1854
1855 static const char **
1856 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1857 {
1858   for (; addr; addr = addr->next) {
1859     /* weed out group mailboxes, since those are for display only */
1860     if (addr->mailbox && !addr->group) {
1861       if (*argslen == *argsmax)
1862         p_realloc(&args, *argsmax += 5);
1863       args[(*argslen)++] = addr->mailbox;
1864     }
1865   }
1866   return (args);
1867 }
1868
1869 static const char **
1870 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1871 {
1872     if (*argslen == *argsmax) {
1873         p_realloc(&args, *argsmax += 5);
1874     }
1875     args[(*argslen)++] = s;
1876     return (args);
1877 }
1878
1879 static int mutt_invoke_sendmail (address_t * from,        /* the sender */
1880                                  address_t * to, address_t * cc, address_t * bcc,     /* recips */
1881                                  const char *msg,       /* file containing message */
1882                                  int eightbit)
1883 {                               /* message contains 8bit chars */
1884   char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
1885   const char **args = NULL;
1886   ssize_t argslen = 0, argsmax = 0;
1887   int i;
1888
1889 #ifdef USE_NNTP
1890   if (option (OPTNEWSSEND)) {
1891     char cmd[LONG_STRING];
1892
1893     mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0,
1894                        0);
1895     if (!*cmd) {
1896       i = nntp_post (msg);
1897       unlink (msg);
1898       return i;
1899     }
1900
1901     s = m_strdup(cmd);
1902   }
1903   else
1904 #endif
1905     s = m_strdup(Sendmail);
1906
1907   ps = s;
1908   i = 0;
1909   while ((ps = strtok (ps, " "))) {
1910     if (argslen == argsmax)
1911       p_realloc(&args, argsmax += 5);
1912
1913     if (i)
1914       args[argslen++] = ps;
1915     else {
1916       path = m_strdup(ps);
1917       ps = strrchr (ps, '/');
1918       if (ps)
1919         ps++;
1920       else
1921         ps = path;
1922       args[argslen++] = ps;
1923     }
1924     ps = NULL;
1925     i++;
1926   }
1927
1928 #ifdef USE_NNTP
1929   if (!option (OPTNEWSSEND)) {
1930 #endif
1931     if (eightbit && option (OPTUSE8BITMIME))
1932       args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1933
1934     if (option (OPTENVFROM)) {
1935       address_t *f = NULL;
1936       if (EnvFrom)
1937         f = EnvFrom;
1938       else if (from && !from->next)
1939         f = from;
1940       if (f) {
1941         args = add_option (args, &argslen, &argsmax, "-f");
1942         args = add_args (args, &argslen, &argsmax, f);
1943       }
1944     }
1945     if (DsnNotify) {
1946       args = add_option (args, &argslen, &argsmax, "-N");
1947       args = add_option (args, &argslen, &argsmax, DsnNotify);
1948     }
1949     if (DsnReturn) {
1950       args = add_option (args, &argslen, &argsmax, "-R");
1951       args = add_option (args, &argslen, &argsmax, DsnReturn);
1952     }
1953     args = add_option (args, &argslen, &argsmax, "--");
1954     args = add_args (args, &argslen, &argsmax, to);
1955     args = add_args (args, &argslen, &argsmax, cc);
1956     args = add_args (args, &argslen, &argsmax, bcc);
1957 #ifdef USE_NNTP
1958   }
1959 #endif
1960
1961   if (argslen == argsmax)
1962     p_realloc(&args, ++argsmax);
1963
1964   args[argslen++] = NULL;
1965
1966   if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1967     if (i != S_BKG) {
1968       mutt_error (_("Error sending message, child exited %d (%s)."), i,
1969                   m_strsysexit(i));
1970       if (childout) {
1971         struct stat st;
1972
1973         if (stat (childout, &st) == 0 && st.st_size > 0)
1974           mutt_do_pager (_("Output of the delivery process"), childout, 0,
1975                          NULL);
1976       }
1977     }
1978   }
1979   else
1980     unlink (childout);
1981
1982   p_delete(&childout);
1983   p_delete(&path);
1984   p_delete(&s);
1985   p_delete(&args);
1986
1987   if (i == (EX_OK & 0xff))
1988     i = 0;
1989   else if (i == S_BKG)
1990     i = 1;
1991   else
1992     i = -1;
1993   return (i);
1994 }
1995
1996 int mutt_invoke_mta (address_t * from,    /* the sender */
1997                      address_t * to, address_t * cc, address_t * bcc, /* recips */
1998                      const char *msg,   /* file containing message */
1999                      int eightbit)
2000 {                               /* message contains 8bit chars */
2001 #ifdef USE_LIBESMTP
2002 #ifdef USE_NNTP
2003   if (!option (OPTNEWSSEND))
2004 #endif
2005     if (SmtpHost)
2006       return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
2007 #endif
2008
2009   return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
2010 }
2011
2012 /* For postponing (!final) do the necessary encodings only */
2013 void mutt_prepare_envelope (ENVELOPE * env, int final)
2014 {
2015   char buffer[LONG_STRING];
2016
2017   if (final) {
2018     if (env->bcc && !(env->to || env->cc)) {
2019       /* some MTA's will put an Apparently-To: header field showing the Bcc:
2020        * recipients if there is no To: or Cc: field, so attempt to suppress
2021        * it by using an empty To: field.
2022        */
2023       env->to = address_new ();
2024       env->to->group = 1;
2025       env->to->next = address_new ();
2026
2027       buffer[0] = 0;
2028       rfc822_strcpy(buffer, sizeof(buffer), "undisclosed-recipients",
2029                     RFC822Specials);
2030
2031       env->to->mailbox = m_strdup(buffer);
2032     }
2033
2034     mutt_set_followup_to (env);
2035
2036     if (!env->message_id && MsgIdFormat && *MsgIdFormat)
2037       env->message_id = mutt_gen_msgid ();
2038   }
2039
2040   /* Take care of 8-bit => 7-bit conversion. */
2041   rfc2047_encode_adrlist (env->to, "To");
2042   rfc2047_encode_adrlist (env->cc, "Cc");
2043   rfc2047_encode_adrlist (env->bcc, "Bcc");
2044   rfc2047_encode_adrlist (env->from, "From");
2045   rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2046   rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2047
2048   if (env->subject)
2049 #ifdef USE_NNTP
2050     if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2051 #endif
2052     {
2053       rfc2047_encode_string (&env->subject);
2054     }
2055   encode_headers (env->userhdrs);
2056 }
2057
2058 void mutt_unprepare_envelope (ENVELOPE * env)
2059 {
2060   string_list_t *item;
2061
2062   for (item = env->userhdrs; item; item = item->next)
2063     rfc2047_decode (&item->data);
2064
2065   address_list_wipe(&env->mail_followup_to);
2066
2067   /* back conversions */
2068   rfc2047_decode_adrlist (env->to);
2069   rfc2047_decode_adrlist (env->cc);
2070   rfc2047_decode_adrlist (env->bcc);
2071   rfc2047_decode_adrlist (env->from);
2072   rfc2047_decode_adrlist (env->reply_to);
2073   rfc2047_decode (&env->subject);
2074 }
2075
2076 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
2077                                  const char *resent_from, address_t * env_from)
2078 {
2079   int i, ret = 0;
2080   FILE *f;
2081   char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2082   MESSAGE *msg = NULL;
2083
2084   if (!h) {
2085     /* Try to bounce each message out, aborting if we get any failures. */
2086     for (i = 0; i < Context->msgcount; i++)
2087       if (Context->hdrs[i]->tagged)
2088         ret |=
2089           _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2090                                 env_from);
2091     return ret;
2092   }
2093
2094   /* If we failed to open a message, return with error */
2095   if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2096     return -1;
2097
2098   if (!fp)
2099     fp = msg->fp;
2100
2101   mutt_mktemp (tempfile);
2102   if ((f = safe_fopen (tempfile, "w")) != NULL) {
2103     int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2104
2105     if (!option (OPTBOUNCEDELIVERED))
2106       ch_flags |= CH_WEED_DELIVERED;
2107
2108     fseeko (fp, h->offset, 0);
2109     fprintf (f, "Resent-From: %s", resent_from);
2110     fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2111     if (MsgIdFormat && *MsgIdFormat)
2112       fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid ());
2113     fputs ("Resent-To: ", f);
2114     mutt_write_address_list (to, f, 11, 0);
2115     mutt_copy_header (fp, h, f, ch_flags, NULL);
2116     fputc ('\n', f);
2117     mutt_copy_bytes (fp, f, h->content->length);
2118     fclose (f);
2119
2120     ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2121                            h->content->encoding == ENC8BIT);
2122   }
2123
2124   if (msg)
2125     mx_close_message (&msg);
2126
2127   return ret;
2128 }
2129
2130 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2131 {
2132   address_t *from;
2133   const char *fqdn = mutt_fqdn (1);
2134   char resent_from[STRING];
2135   int ret;
2136   char *err;
2137
2138   resent_from[0] = '\0';
2139   from = mutt_default_from ();
2140
2141   if (fqdn)
2142     rfc822_qualify (from, fqdn);
2143
2144   rfc2047_encode_adrlist (from, "Resent-From");
2145   if (mutt_addrlist_to_idna (from, &err)) {
2146     mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2147     return -1;
2148   }
2149   rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2150
2151 #ifdef USE_NNTP
2152   unset_option (OPTNEWSSEND);
2153 #endif
2154
2155   ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2156
2157   address_list_wipe(&from);
2158
2159   return ret;
2160 }
2161
2162 static void set_noconv_flags (BODY * b, short flag)
2163 {
2164   for (; b; b = b->next) {
2165     if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2166       set_noconv_flags (b->parts, flag);
2167     else if (b->type == TYPETEXT && b->noconv) {
2168       parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2169     }
2170   }
2171 }
2172
2173 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2174                     int post, char *fcc)
2175 {
2176   CONTEXT f;
2177   MESSAGE *msg;
2178   char tempfile[_POSIX_PATH_MAX];
2179   FILE *tempfp = NULL;
2180   int r;
2181
2182   if (post)
2183     set_noconv_flags (hdr->content, 1);
2184
2185   if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2186     return (-1);
2187   }
2188
2189   /* We need to add a Content-Length field to avoid problems where a line in
2190    * the message body begins with "From "   
2191    */
2192   if (f.magic == M_MMDF || f.magic == M_MBOX) {
2193     mutt_mktemp (tempfile);
2194     if ((tempfp = safe_fopen (tempfile, "w+")) == NULL) {
2195       mutt_perror (tempfile);
2196       mx_close_mailbox (&f, NULL);
2197       return (-1);
2198     }
2199   }
2200
2201   hdr->read = !post;            /* make sure to put it in the `cur' directory (maildir) */
2202   if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2203     mx_close_mailbox (&f, NULL);
2204     return (-1);
2205   }
2206
2207   /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2208    * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header() 
2209    * */
2210   mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0,
2211                             0);
2212
2213   /* (postponment) if this was a reply of some sort, <msgid> contians the
2214    * Message-ID: of message replied to.  Save it using a special X-Mutt-
2215    * header so it can be picked up if the message is recalled at a later
2216    * point in time.  This will allow the message to be marked as replied if
2217    * the same mailbox is still open.
2218    */
2219   if (post && msgid)
2220     fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2221
2222   /* (postponment) save the Fcc: using a special X-Mutt- header so that
2223    * it can be picked up when the message is recalled 
2224    */
2225   if (post && fcc)
2226     fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2227   fprintf (msg->fp, "Status: RO\n");
2228
2229
2230
2231   /* (postponment) if the mail is to be signed or encrypted, save this info */
2232   if (post && (hdr->security & APPLICATION_PGP)) {
2233     fputs ("X-Mutt-PGP: ", msg->fp);
2234     if (hdr->security & ENCRYPT)
2235       fputc ('E', msg->fp);
2236     if (hdr->security & SIGN) {
2237       fputc ('S', msg->fp);
2238       if (PgpSignAs && *PgpSignAs)
2239         fprintf (msg->fp, "<%s>", PgpSignAs);
2240     }
2241     if (hdr->security & INLINE)
2242       fputc ('I', msg->fp);
2243     fputc ('\n', msg->fp);
2244   }
2245
2246   /* (postponment) if the mail is to be signed or encrypted, save this info */
2247   if (post && (hdr->security & APPLICATION_SMIME)) {
2248     fputs ("X-Mutt-SMIME: ", msg->fp);
2249     if (hdr->security & ENCRYPT) {
2250       fputc ('E', msg->fp);
2251       if (SmimeCryptAlg && *SmimeCryptAlg)
2252         fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2253     }
2254     if (hdr->security & SIGN) {
2255       fputc ('S', msg->fp);
2256       if (SmimeDefaultKey && *SmimeDefaultKey)
2257         fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2258     }
2259     if (hdr->security & INLINE)
2260       fputc ('I', msg->fp);
2261     fputc ('\n', msg->fp);
2262   }
2263
2264 #ifdef MIXMASTER
2265   /* (postponement) if the mail is to be sent through a mixmaster 
2266    * chain, save that information
2267    */
2268
2269   if (post && hdr->chain && hdr->chain) {
2270     string_list_t *p;
2271
2272     fputs ("X-Mutt-Mix:", msg->fp);
2273     for (p = hdr->chain; p; p = p->next)
2274       fprintf (msg->fp, " %s", (char *) p->data);
2275
2276     fputc ('\n', msg->fp);
2277   }
2278 #endif
2279
2280   if (tempfp) {
2281     char sasha[LONG_STRING];
2282     int lines = 0;
2283
2284     mutt_write_mime_body (hdr->content, tempfp);
2285
2286     /* make sure the last line ends with a newline.  Emacs doesn't ensure
2287      * this will happen, and it can cause problems parsing the mailbox   
2288      * later.
2289      */
2290     fseeko (tempfp, -1, 2);
2291     if (fgetc (tempfp) != '\n') {
2292       fseeko (tempfp, 0, 2);
2293       fputc ('\n', tempfp);
2294     }
2295
2296     fflush (tempfp);
2297     if (ferror (tempfp)) {
2298       fclose (tempfp);
2299       unlink (tempfile);
2300       mx_commit_message (msg, &f);      /* XXX - really? */
2301       mx_close_message (&msg);
2302       mx_close_mailbox (&f, NULL);
2303       return -1;
2304     }
2305
2306     /* count the number of lines */
2307     rewind (tempfp);
2308     while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2309       lines++;
2310     fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2311     fprintf (msg->fp, "Lines: %d\n\n", lines);
2312
2313     /* copy the body and clean up */
2314     rewind (tempfp);
2315     r = mutt_copy_stream (tempfp, msg->fp);
2316     if (fclose (tempfp) != 0)
2317       r = -1;
2318     /* if there was an error, leave the temp version */
2319     if (!r)
2320       unlink (tempfile);
2321   }
2322   else {
2323     fputc ('\n', msg->fp);      /* finish off the header */
2324     r = mutt_write_mime_body (hdr->content, msg->fp);
2325   }
2326
2327   if (mx_commit_message (msg, &f) != 0)
2328     r = -1;
2329   mx_close_message (&msg);
2330   mx_close_mailbox (&f, NULL);
2331
2332   if (post)
2333     set_noconv_flags (hdr->content, 0);
2334
2335   return r;
2336 }