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