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