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