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