use some lists for score_t.
[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[STRING];
343   char send_charset[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[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 = rfc822_addrcpy(buf, ssizeof(buf), addr, display);
1283
1284         if (!first) {
1285             if (linelen + len > 74) {
1286                 fputs("\n\t", fp);
1287                 linelen = 8;        /* tab is usually about 8 spaces... */
1288             } else
1289             if (addr->mailbox) {
1290                 fputc(' ', fp);
1291                 linelen++;
1292             }
1293         }
1294         first = 0;
1295
1296         linelen += len + 1;
1297         fputs(buf, fp);
1298
1299         if (!addr->group && addr->next && addr->next->mailbox) {
1300             fputc(',', fp);
1301             linelen++;
1302         }
1303
1304         addr = addr->next;
1305     }
1306     fputc ('\n', fp);
1307 }
1308
1309 /* need to write the list in reverse because they are stored in reverse order
1310  * when parsed to speed up threading
1311  */
1312 void mutt_write_references(string_list_t *r, FILE *f)
1313 {
1314     string_list_t *refs[10];
1315     int i;
1316
1317     p_clear(refs, countof(refs));
1318     for (i = 0; i < countof(refs) && r; r = r->next) {
1319         refs[i++] = r;
1320     }
1321
1322     while (i-- > 0) {
1323         fprintf(f, " %s", refs[i]->data);
1324     }
1325 }
1326
1327 static int edit_header(int mode, const char *s)
1328 {
1329     const char *p;
1330     int slen = m_strlen(s);
1331
1332     if (mode != 1 || option(OPTXMAILTO))
1333         return 1;
1334
1335     p = skipspaces(EditorHeaders);
1336     while (*p) {
1337         if (!ascii_strncasecmp(p, s, slen) && p[slen - 1] == ':')
1338             return 1;
1339         p = skipspaces(p + slen);
1340     }
1341
1342     return 0;
1343 }
1344
1345 /* Note: all RFC2047 encoding should be done outside of this routine, except
1346  * for the "real name."  This will allow this routine to be used more than
1347  * once, if necessary.
1348  * 
1349  * Likewise, all IDN processing should happen outside of this routine.
1350  *
1351  * mode == 1  => "lite" mode (used for edit_hdrs)
1352  * mode == 0  => normal mode.  write full header + MIME headers
1353  * mode == -1 => write just the envelope info (used for postponing messages)
1354  * 
1355  * privacy != 0 => will omit any headers which may identify the user.
1356  *               Output generated is suitable for being sent through
1357  *               anonymous remailer chains.
1358  *
1359  */
1360 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1361                               int mode, int privacy)
1362 {
1363   char buffer[LONG_STRING];
1364   char *p;
1365   string_list_t *tmp = env->userhdrs;
1366   int has_agent = 0;            /* user defined user-agent header field exists */
1367
1368 #ifdef USE_NNTP
1369   if (!option (OPTNEWSSEND))
1370 #endif
1371     if (mode == 0 && !privacy)
1372       fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1373
1374   /* OPTUSEFROM is not consulted here so that we can still write a From:
1375    * field if the user sets it with the `my_hdr' command
1376    */
1377   if (env->from && !privacy) {
1378     buffer[0] = 0;
1379     rfc822_addrcat(buffer, sizeof(buffer), env->from, 0);
1380     fprintf (fp, "From: %s\n", buffer);
1381   }
1382
1383   if (env->to) {
1384     fputs ("To: ", fp);
1385     mutt_write_address_list (env->to, fp, 4, 0);
1386   }
1387   else if (mode > 0)
1388 #ifdef USE_NNTP
1389     if (!option (OPTNEWSSEND))
1390 #endif
1391       if (edit_header(mode, "To:"))
1392         fputs ("To:\n", fp);
1393
1394   if (env->cc) {
1395     fputs ("Cc: ", fp);
1396     mutt_write_address_list (env->cc, fp, 4, 0);
1397   }
1398   else if (mode > 0)
1399 #ifdef USE_NNTP
1400     if (!option (OPTNEWSSEND))
1401 #endif
1402       if (edit_header(mode, "Cc:"))
1403         fputs ("Cc:\n", fp);
1404
1405   if (env->bcc) {
1406     if (mode != 0 || option (OPTWRITEBCC)) {
1407       fputs ("Bcc: ", fp);
1408       mutt_write_address_list (env->bcc, fp, 5, 0);
1409     }
1410   }
1411   else if (mode > 0)
1412 #ifdef USE_NNTP
1413     if (!option (OPTNEWSSEND))
1414 #endif
1415       if (edit_header(mode, "Bcc:"))
1416         fputs ("Bcc:\n", fp);
1417
1418 #ifdef USE_NNTP
1419   if (env->newsgroups)
1420     fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1421   else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Newsgroups:"))
1422     fputs ("Newsgroups:\n", fp);
1423
1424   if (env->followup_to)
1425     fprintf (fp, "Followup-To: %s\n", env->followup_to);
1426   else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Followup-To:"))
1427     fputs ("Followup-To:\n", fp);
1428
1429   if (env->x_comment_to)
1430     fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1431   else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO) &&
1432            edit_header(mode, "X-Comment-To:"))
1433     fputs ("X-Comment-To:\n", fp);
1434 #endif
1435
1436   if (env->subject)
1437     fprintf (fp, "Subject: %s\n", env->subject);
1438   else if (mode == 1 && edit_header(mode, "Subject:"))
1439     fputs ("Subject:\n", fp);
1440
1441   /* save message id if the user has set it */
1442   if (env->message_id && !privacy)
1443     fprintf (fp, "Message-ID: %s\n", env->message_id);
1444
1445   if (env->reply_to) {
1446     fputs ("Reply-To: ", fp);
1447     mutt_write_address_list (env->reply_to, fp, 10, 0);
1448   }
1449   else if (mode > 0 && edit_header(mode, "Reply-To:"))
1450     fputs ("Reply-To:\n", fp);
1451
1452   if (env->mail_followup_to)
1453 #ifdef USE_NNTP
1454     if (!option (OPTNEWSSEND))
1455 #endif
1456     {
1457       fputs ("Mail-Followup-To: ", fp);
1458       mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1459     }
1460
1461   if (mode <= 0) {
1462     if (env->references) {
1463       fputs ("References:", fp);
1464       mutt_write_references (env->references, fp);
1465       fputc ('\n', fp);
1466     }
1467
1468     /* Add the MIME headers */
1469     fputs ("MIME-Version: 1.0\n", fp);
1470     mutt_write_mime_header (attach, fp);
1471   }
1472
1473   if (env->in_reply_to) {
1474     fputs ("In-Reply-To:", fp);
1475     mutt_write_references (env->in_reply_to, fp);
1476     fputc ('\n', fp);
1477   }
1478
1479   /* Add any user defined headers */
1480   for (; tmp; tmp = tmp->next) {
1481     if ((p = strchr (tmp->data, ':'))) {
1482       p = vskipspaces(p + 1);
1483       if (!*p)
1484         continue;               /* don't emit empty fields. */
1485
1486       /* check to see if the user has overridden the user-agent field */
1487       if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1488         has_agent = 1;
1489         if (privacy)
1490           continue;
1491       }
1492
1493       fputs (tmp->data, fp);
1494       fputc ('\n', fp);
1495     }
1496   }
1497
1498   if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1499     const char *os;
1500
1501     if (OperatingSystem != NULL) {
1502       os = OperatingSystem;
1503     } else {
1504       struct utsname un;
1505       os = (uname(&un) == -1) ? "UNIX" : un.sysname;
1506     }
1507     /* Add a vanity header */
1508     fprintf (fp, "User-Agent: %s (%s)\n", mutt_make_version (0), os);
1509   }
1510
1511   return (ferror (fp) == 0 ? 0 : -1);
1512 }
1513
1514 static void encode_headers (string_list_t * h)
1515 {
1516   char *tmp;
1517   char *p;
1518   int i;
1519
1520   for (; h; h = h->next) {
1521     if (!(p = strchr (h->data, ':')))
1522       continue;
1523
1524     i = p - h->data;
1525     p = vskipspaces(p + 1);
1526     tmp = m_strdup(p);
1527
1528     if (!tmp)
1529       continue;
1530
1531     rfc2047_encode_string (&tmp);
1532     p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1533
1534     sprintf (h->data + i, ": %s", NONULL (tmp));
1535
1536     p_delete(&tmp);
1537   }
1538 }
1539
1540 const char *mutt_fqdn (short may_hide_host)
1541 {
1542   char *p = NULL, *q;
1543
1544   if (Fqdn && Fqdn[0] != '@') {
1545     p = Fqdn;
1546
1547     if (may_hide_host && option (OPTHIDDENHOST)) {
1548       if ((p = strchr (Fqdn, '.')))
1549         p++;
1550
1551       /* sanity check: don't hide the host if
1552          the fqdn is something like detebe.org.  */
1553
1554       if (!p || !(q = strchr (p, '.')))
1555         p = Fqdn;
1556     }
1557   }
1558
1559   return p;
1560 }
1561
1562 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1563 {
1564 #define APPEND_FMT(fmt, arg) \
1565         if (len > 1) {                                  \
1566             int snlen = snprintf(buf, len, fmt, arg);   \
1567             buf += snlen;                               \
1568             len -= snlen;                               \
1569         }
1570
1571 #define APPEND_BYTE(c) \
1572         if (len > 1) {                                  \
1573             *buf++ = c;                                 \
1574             *buf   = '\0';                              \
1575             len--;                                      \
1576         }
1577
1578     time_t now;
1579     struct tm *tm;
1580
1581     now = time (NULL);
1582     tm = gmtime (&now);
1583
1584     while (*fmt) {
1585         static char MsgIdPfx = 'A';
1586         int c = *fmt++;
1587
1588         if (c != '%') {
1589             /* normalized character (we're stricter than RFC2822, 3.6.4) */
1590             APPEND_BYTE((isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.');
1591             continue;
1592         }
1593
1594         switch (*fmt++) {
1595           case 0:
1596             return;
1597           case 'd':
1598             APPEND_FMT("%02d", tm->tm_mday);
1599             break;
1600           case 'h':
1601             APPEND_FMT("%02d", tm->tm_hour);
1602             break;
1603           case 'm':
1604             APPEND_FMT("%02d", tm->tm_mon + 1);
1605             break;
1606           case 'M':
1607             APPEND_FMT("%02d", tm->tm_min);
1608             break;
1609           case 'O':
1610             APPEND_FMT("%lo", (unsigned long)now);
1611             break;
1612           case 'p':
1613             APPEND_FMT("%u", (unsigned int)getpid());
1614             break;
1615           case 'P':
1616             APPEND_FMT("%c", MsgIdPfx);
1617             MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1618             break;
1619           case 'r':
1620             APPEND_FMT("%u", (unsigned int)rand());
1621             break;
1622           case 'R':
1623             APPEND_FMT("%x", (unsigned int)rand());
1624             break;
1625           case 's':
1626             APPEND_FMT("%02d", tm->tm_sec);
1627             break;
1628           case 'T':
1629             APPEND_FMT("%u", (unsigned int) now);
1630             break;
1631           case 'X':
1632             APPEND_FMT("%x", (unsigned int) now);
1633             break;
1634           case 'Y':       /* this will break in the year 10000 ;-) */
1635             APPEND_FMT("%04d", tm->tm_year + 1900);
1636             break;
1637           case '%':
1638             APPEND_BYTE('%');
1639             break;
1640           default:       /* invalid formats are replaced by '.' */
1641             APPEND_BYTE('.');
1642         }
1643     }
1644
1645     *buf = '\0';
1646
1647 #undef APPEND_BYTE
1648 #undef APPEND_FMT
1649 }
1650
1651 static char *mutt_gen_msgid (void)
1652 {
1653     char buf[STRING];
1654     char localpart[STRING];
1655     const char *fqdn;
1656
1657     if (!(fqdn = mutt_fqdn(0)))
1658         fqdn = NONULL(Hostname);
1659
1660     mutt_gen_localpart(localpart, sizeof(localpart), MsgIdFormat);
1661     snprintf(buf, sizeof(buf), "<%s@%s>", localpart, fqdn);
1662     return m_strdup(buf);
1663 }
1664
1665 static RETSIGTYPE alarm_handler (int sig __attribute__ ((unused)))
1666 {
1667   SigAlrm = 1;
1668 }
1669
1670 /* invoke sendmail in a subshell
1671    path (in)            path to program to execute
1672    args (in)            arguments to pass to program
1673    msg (in)             temp file containing message to send
1674    tempfile (out)       if sendmail is put in the background, this points
1675                         to the temporary file containing the stdout of the
1676                         child process */
1677 static int
1678 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1679 {
1680   sigset_t set;
1681   int fd, st;
1682   pid_t pid, ppid;
1683
1684   mutt_block_signals_system ();
1685
1686   sigemptyset (&set);
1687   /* we also don't want to be stopped right now */
1688   sigaddset (&set, SIGTSTP);
1689   sigprocmask (SIG_BLOCK, &set, NULL);
1690
1691   if (SendmailWait >= 0) {
1692     char tmp[_POSIX_PATH_MAX];
1693
1694     mutt_mktemp (tmp);
1695     *tempfile = m_strdup(tmp);
1696   }
1697
1698   if ((pid = fork ()) == 0) {
1699     struct sigaction act, oldalrm;
1700
1701     /* save parent's ID before setsid() */
1702     ppid = getppid ();
1703
1704     /* we want the delivery to continue even after the main process dies,
1705      * so we put ourselves into another session right away
1706      */
1707     setsid ();
1708
1709     /* next we close all open files */
1710     for (fd = 0; fd < getdtablesize(); fd++)
1711       close (fd);
1712
1713     /* now the second fork() */
1714     if ((pid = fork ()) == 0) {
1715       /* "msg" will be opened as stdin */
1716       if (open (msg, O_RDONLY, 0) < 0) {
1717         unlink (msg);
1718         _exit (S_ERR);
1719       }
1720       unlink (msg);
1721
1722       if (SendmailWait >= 0) {
1723         /* *tempfile will be opened as stdout */
1724         if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1725             0)
1726           _exit (S_ERR);
1727         /* redirect stderr to *tempfile too */
1728         if (dup (1) < 0)
1729           _exit (S_ERR);
1730       } else {
1731         if (open ("/dev/null", O_WRONLY | O_APPEND) < 0)        /* stdout */
1732           _exit (S_ERR);
1733         if (open ("/dev/null", O_RDWR | O_APPEND) < 0)  /* stderr */
1734           _exit (S_ERR);
1735       }
1736
1737       execv (path, (char**)args);
1738       _exit (S_ERR);
1739     }
1740     else if (pid == -1) {
1741       unlink (msg);
1742       p_delete(tempfile);
1743       _exit (S_ERR);
1744     }
1745
1746     /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1747      * SendmailWait = 0: wait forever
1748      * SendmailWait < 0: don't wait
1749      */
1750     if (SendmailWait > 0) {
1751       SigAlrm = 0;
1752       act.sa_handler = alarm_handler;
1753 #ifdef SA_INTERRUPT
1754       /* need to make sure waitpid() is interrupted on SIGALRM */
1755       act.sa_flags = SA_INTERRUPT;
1756 #else
1757       act.sa_flags = 0;
1758 #endif
1759       sigemptyset (&act.sa_mask);
1760       sigaction (SIGALRM, &act, &oldalrm);
1761       alarm (SendmailWait);
1762     }
1763     else if (SendmailWait < 0)
1764       _exit (0xff & EX_OK);
1765
1766     if (waitpid (pid, &st, 0) > 0) {
1767       st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1768       if (SendmailWait && st == (0xff & EX_OK)) {
1769         unlink (*tempfile);     /* no longer needed */
1770         p_delete(tempfile);
1771       }
1772     } else {
1773       st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1774       if (SendmailWait > 0) {
1775         unlink (*tempfile);
1776         p_delete(tempfile);
1777       }
1778     }
1779
1780     /* reset alarm; not really needed, but... */
1781     alarm (0);
1782     sigaction (SIGALRM, &oldalrm, NULL);
1783
1784     if (kill (ppid, 0) == -1 && errno == ESRCH) {
1785       /* the parent is already dead */
1786       unlink (*tempfile);
1787       p_delete(tempfile);
1788     }
1789
1790     _exit (st);
1791   }
1792
1793   sigprocmask (SIG_UNBLOCK, &set, NULL);
1794
1795   if (pid != -1 && waitpid (pid, &st, 0) > 0)
1796     st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;     /* return child status */
1797   else
1798     st = S_ERR;                 /* error */
1799
1800   mutt_unblock_signals_system (1);
1801
1802   return (st);
1803 }
1804
1805 static const char **
1806 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1807 {
1808   for (; addr; addr = addr->next) {
1809     /* weed out group mailboxes, since those are for display only */
1810     if (addr->mailbox && !addr->group) {
1811       if (*argslen == *argsmax)
1812         p_realloc(&args, *argsmax += 5);
1813       args[(*argslen)++] = addr->mailbox;
1814     }
1815   }
1816   return (args);
1817 }
1818
1819 static const char **
1820 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1821 {
1822     if (*argslen == *argsmax) {
1823         p_realloc(&args, *argsmax += 5);
1824     }
1825     args[(*argslen)++] = s;
1826     return (args);
1827 }
1828
1829 static int mutt_invoke_sendmail (address_t * from,        /* the sender */
1830                                  address_t * to, address_t * cc, address_t * bcc,     /* recips */
1831                                  const char *msg,       /* file containing message */
1832                                  int eightbit)
1833 {                               /* message contains 8bit chars */
1834   char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
1835   const char **args = NULL;
1836   ssize_t argslen = 0, argsmax = 0;
1837   int i;
1838
1839 #ifdef USE_NNTP
1840   if (option (OPTNEWSSEND)) {
1841     char cmd[LONG_STRING];
1842
1843     m_strformat(cmd, sizeof(cmd), 0, Inews, nntp_format_str, 0, 0);
1844     if (m_strisempty(cmd)) {
1845       i = nntp_post (msg);
1846       unlink (msg);
1847       return i;
1848     }
1849
1850     s = m_strdup(cmd);
1851   }
1852   else
1853 #endif
1854     s = m_strdup(Sendmail);
1855
1856   ps = s;
1857   i = 0;
1858   while ((ps = strtok (ps, " "))) {
1859     if (argslen == argsmax)
1860       p_realloc(&args, argsmax += 5);
1861
1862     if (i)
1863       args[argslen++] = ps;
1864     else {
1865       path = m_strdup(ps);
1866       ps = strrchr (ps, '/');
1867       if (ps)
1868         ps++;
1869       else
1870         ps = path;
1871       args[argslen++] = ps;
1872     }
1873     ps = NULL;
1874     i++;
1875   }
1876
1877 #ifdef USE_NNTP
1878   if (!option (OPTNEWSSEND)) {
1879 #endif
1880     if (eightbit && option (OPTUSE8BITMIME))
1881       args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1882
1883     if (option (OPTENVFROM)) {
1884       address_t *f = EnvFrom;
1885       if (EnvFrom)
1886         f = EnvFrom;
1887       else if (from && !from->next)
1888         f = from;
1889       if (f) {
1890         args = add_option (args, &argslen, &argsmax, "-f");
1891         args = add_args (args, &argslen, &argsmax, f);
1892       }
1893     }
1894     if (DsnNotify) {
1895       args = add_option (args, &argslen, &argsmax, "-N");
1896       args = add_option (args, &argslen, &argsmax, DsnNotify);
1897     }
1898     if (DsnReturn) {
1899       args = add_option (args, &argslen, &argsmax, "-R");
1900       args = add_option (args, &argslen, &argsmax, DsnReturn);
1901     }
1902     args = add_option (args, &argslen, &argsmax, "--");
1903     args = add_args (args, &argslen, &argsmax, to);
1904     args = add_args (args, &argslen, &argsmax, cc);
1905     args = add_args (args, &argslen, &argsmax, bcc);
1906 #ifdef USE_NNTP
1907   }
1908 #endif
1909
1910   if (argslen >= argsmax)
1911     p_realloc(&args, ++argsmax);
1912
1913   args[argslen++] = NULL;
1914
1915   if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1916     if (i != S_BKG) {
1917       mutt_error (_("Error sending message, child exited %d (%s)."), i,
1918                   m_strsysexit(i));
1919       if (childout) {
1920         struct stat st;
1921
1922         if (!stat(childout, &st) && st.st_size > 0)
1923           mutt_do_pager(_("Output of the delivery process"), childout, 0,
1924                         NULL);
1925       }
1926     }
1927   } else {
1928     unlink (childout);
1929   }
1930
1931   p_delete(&childout);
1932   p_delete(&path);
1933   p_delete(&s);
1934   p_delete(&args);
1935
1936   if (i == (EX_OK & 0xff))
1937     i = 0;
1938   else if (i == S_BKG)
1939     i = 1;
1940   else
1941     i = -1;
1942   return (i);
1943 }
1944
1945 int mutt_invoke_mta (address_t * from,    /* the sender */
1946                      address_t * to, address_t * cc, address_t * bcc, /* recips */
1947                      const char *msg,   /* file containing message */
1948                      int eightbit)
1949 {                               /* message contains 8bit chars */
1950 #ifdef USE_LIBESMTP
1951 #ifdef USE_NNTP
1952   if (!option (OPTNEWSSEND))
1953 #endif
1954     if (SmtpHost)
1955       return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
1956 #endif
1957
1958   return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
1959 }
1960
1961 /* For postponing (!final) do the necessary encodings only */
1962 void mutt_prepare_envelope (ENVELOPE * env, int final)
1963 {
1964   if (final) {
1965     if (env->bcc && !(env->to || env->cc)) {
1966       /* some MTA's will put an Apparently-To: header field showing the Bcc:
1967        * recipients if there is no To: or Cc: field, so attempt to suppress
1968        * it by using an empty To: field.
1969        */
1970       env->to = address_new();
1971       env->to->group = 1;
1972       env->to->next  = address_new();
1973       env->to->mailbox = m_strdup("undisclosed-recipients");
1974     }
1975
1976     mutt_set_followup_to(env);
1977
1978     if (!env->message_id && !m_strisempty(MsgIdFormat))
1979       env->message_id = mutt_gen_msgid();
1980   }
1981
1982   /* Take care of 8-bit => 7-bit conversion. */
1983   rfc2047_encode_adrlist(env->to, "To");
1984   rfc2047_encode_adrlist(env->cc, "Cc");
1985   rfc2047_encode_adrlist(env->bcc, "Bcc");
1986   rfc2047_encode_adrlist(env->from, "From");
1987   rfc2047_encode_adrlist(env->mail_followup_to, "Mail-Followup-To");
1988   rfc2047_encode_adrlist(env->reply_to, "Reply-To");
1989
1990   if (env->subject)
1991 #ifdef USE_NNTP
1992     if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
1993 #endif
1994     {
1995       rfc2047_encode_string (&env->subject);
1996     }
1997   encode_headers (env->userhdrs);
1998 }
1999
2000 void mutt_unprepare_envelope (ENVELOPE * env)
2001 {
2002     string_list_t *item;
2003
2004     for (item = env->userhdrs; item; item = item->next)
2005         rfc2047_decode(&item->data);
2006
2007     address_list_wipe(&env->mail_followup_to);
2008
2009     /* back conversions */
2010     rfc2047_decode_adrlist(env->to);
2011     rfc2047_decode_adrlist(env->cc);
2012     rfc2047_decode_adrlist(env->bcc);
2013     rfc2047_decode_adrlist(env->from);
2014     rfc2047_decode_adrlist(env->reply_to);
2015     rfc2047_decode(&env->subject);
2016 }
2017
2018 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
2019                                  const char *resent_from, address_t * env_from)
2020 {
2021   int i, ret = 0;
2022   FILE *f;
2023   char date[STRING], tempfile[_POSIX_PATH_MAX];
2024   MESSAGE *msg = NULL;
2025
2026   if (!h) {
2027     /* Try to bounce each message out, aborting if we get any failures. */
2028     for (i = 0; i < Context->msgcount; i++)
2029       if (Context->hdrs[i]->tagged)
2030         ret |=
2031           _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2032                                 env_from);
2033     return ret;
2034   }
2035
2036   /* If we failed to open a message, return with error */
2037   if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2038     return -1;
2039
2040   if (!fp)
2041     fp = msg->fp;
2042
2043   f = m_tempfile(tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
2044   if (f) {
2045     int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2046
2047     if (!option (OPTBOUNCEDELIVERED))
2048       ch_flags |= CH_WEED_DELIVERED;
2049
2050     fseeko (fp, h->offset, 0);
2051     fprintf (f, "Resent-From: %s", resent_from);
2052     fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2053     if (!m_strisempty(MsgIdFormat))
2054       fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2055     fputs ("Resent-To: ", f);
2056     mutt_write_address_list (to, f, 11, 0);
2057     mutt_copy_header (fp, h, f, ch_flags, NULL);
2058     fputc ('\n', f);
2059     mutt_copy_bytes (fp, f, h->content->length);
2060     m_fclose(&f);
2061
2062     ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
2063                           h->content->encoding == ENC8BIT);
2064   }
2065
2066   if (msg)
2067     mx_close_message (&msg);
2068
2069   return ret;
2070 }
2071
2072 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2073 {
2074   address_t *from;
2075   char resent_from[STRING];
2076   int ret;
2077   char *err;
2078
2079   resent_from[0] = '\0';
2080   from = mutt_default_from ();
2081
2082   rfc822_qualify(from, mutt_fqdn(1));
2083
2084   rfc2047_encode_adrlist(from, "Resent-From");
2085   if (mutt_addrlist_to_idna (from, &err)) {
2086     mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2087     return -1;
2088   }
2089   rfc822_addrcat(resent_from, sizeof(resent_from), from, 0);
2090
2091 #ifdef USE_NNTP
2092   unset_option (OPTNEWSSEND);
2093 #endif
2094
2095   ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2096
2097   address_list_wipe(&from);
2098
2099   return ret;
2100 }
2101
2102 static void set_noconv_flags (BODY * b, short flag)
2103 {
2104   for (; b; b = b->next) {
2105     if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2106       set_noconv_flags (b->parts, flag);
2107     else if (b->type == TYPETEXT && b->noconv) {
2108       parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2109     }
2110   }
2111 }
2112
2113 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2114                     int post, char *fcc)
2115 {
2116   CONTEXT f;
2117   MESSAGE *msg;
2118   char tempfile[_POSIX_PATH_MAX];
2119   FILE *tempfp = NULL;
2120   int r;
2121
2122   if (post)
2123     set_noconv_flags (hdr->content, 1);
2124
2125   if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2126     return (-1);
2127   }
2128
2129   /* We need to add a Content-Length field to avoid problems where a line in
2130    * the message body begins with "From "   
2131    */
2132   if (f.magic == M_MMDF || f.magic == M_MBOX) {
2133     tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
2134     if (!tempfp) {
2135       mutt_error(_("Could not create temporary file"));
2136       mx_close_mailbox (&f, NULL);
2137       return -1;
2138     }
2139   }
2140
2141   hdr->read = !post;            /* make sure to put it in the `cur' directory (maildir) */
2142   if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2143     mx_close_mailbox (&f, NULL);
2144     return (-1);
2145   }
2146
2147   /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2148    * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header() 
2149    * */
2150   mutt_write_rfc822_header(msg->fp, hdr->env, hdr->content, -post, 0);
2151
2152   /* (postponment) if this was a reply of some sort, <msgid> contians the
2153    * Message-ID: of message replied to.  Save it using a special X-Mutt-
2154    * header so it can be picked up if the message is recalled at a later
2155    * point in time.  This will allow the message to be marked as replied if
2156    * the same mailbox is still open.
2157    */
2158   if (post && msgid)
2159     fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2160
2161   /* (postponment) save the Fcc: using a special X-Mutt- header so that
2162    * it can be picked up when the message is recalled 
2163    */
2164   if (post && fcc)
2165     fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2166   fprintf (msg->fp, "Status: RO\n");
2167
2168   /* (postponment) if the mail is to be signed or encrypted, save this info */
2169   if (post && (hdr->security & APPLICATION_PGP)) {
2170     fputs ("X-Mutt-PGP: ", msg->fp);
2171     if (hdr->security & ENCRYPT)
2172       fputc ('E', msg->fp);
2173     if (hdr->security & SIGN) {
2174       fputc ('S', msg->fp);
2175       if (PgpSignAs && *PgpSignAs)
2176         fprintf (msg->fp, "<%s>", PgpSignAs);
2177     }
2178     if (hdr->security & INLINE)
2179       fputc ('I', msg->fp);
2180     fputc ('\n', msg->fp);
2181   }
2182
2183   /* (postponment) if the mail is to be signed or encrypted, save this info */
2184   if (post && (hdr->security & APPLICATION_SMIME)) {
2185     fputs ("X-Mutt-SMIME: ", msg->fp);
2186     if (hdr->security & ENCRYPT) {
2187       fputc ('E', msg->fp);
2188       if (SmimeCryptAlg && *SmimeCryptAlg)
2189         fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2190     }
2191     if (hdr->security & SIGN) {
2192       fputc ('S', msg->fp);
2193       if (SmimeDefaultKey && *SmimeDefaultKey)
2194         fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2195     }
2196     if (hdr->security & INLINE)
2197       fputc ('I', msg->fp);
2198     fputc ('\n', msg->fp);
2199   }
2200
2201   /* (postponement) if the mail is to be sent through a mixmaster 
2202    * chain, save that information
2203    */
2204   if (post && hdr->chain && hdr->chain) {
2205     string_list_t *p;
2206
2207     fputs ("X-Mutt-Mix:", msg->fp);
2208     for (p = hdr->chain; p; p = p->next)
2209       fprintf (msg->fp, " %s", (char *) p->data);
2210
2211     fputc ('\n', msg->fp);
2212   }
2213
2214   if (tempfp) {
2215     char sasha[LONG_STRING];
2216     int lines = 0;
2217
2218     mutt_write_mime_body (hdr->content, tempfp);
2219
2220     /* make sure the last line ends with a newline.  Emacs doesn't ensure
2221      * this will happen, and it can cause problems parsing the mailbox   
2222      * later.
2223      */
2224     fseeko (tempfp, -1, 2);
2225     if (fgetc (tempfp) != '\n') {
2226       fseeko (tempfp, 0, 2);
2227       fputc ('\n', tempfp);
2228     }
2229
2230     fflush (tempfp);
2231     if (ferror (tempfp)) {
2232       m_fclose(&tempfp);
2233       unlink (tempfile);
2234       mx_commit_message (msg, &f);      /* XXX - really? */
2235       mx_close_message (&msg);
2236       mx_close_mailbox (&f, NULL);
2237       return -1;
2238     }
2239
2240     /* count the number of lines */
2241     rewind (tempfp);
2242     while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2243       lines++;
2244     fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2245     fprintf (msg->fp, "Lines: %d\n\n", lines);
2246
2247     /* copy the body and clean up */
2248     rewind (tempfp);
2249     r = mutt_copy_stream (tempfp, msg->fp);
2250     if (m_fclose(&tempfp) != 0)
2251       r = -1;
2252     /* if there was an error, leave the temp version */
2253     if (!r)
2254       unlink (tempfile);
2255   } else {
2256     fputc ('\n', msg->fp);      /* finish off the header */
2257     r = mutt_write_mime_body (hdr->content, msg->fp);
2258   }
2259
2260   if (mx_commit_message (msg, &f) != 0)
2261     r = -1;
2262   mx_close_message (&msg);
2263   mx_close_mailbox (&f, NULL);
2264
2265   if (post)
2266     set_noconv_flags (hdr->content, 0);
2267
2268   return r;
2269 }