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