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