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