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