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