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