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