less warnings
[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     const char *os;
1616
1617     if (OperatingSystem != NULL) {
1618       os = OperatingSystem;
1619     } else {
1620       struct utsname un;
1621       os = (uname(&un) == -1) ? "UNIX" : un.sysname;
1622     }
1623     /* Add a vanity header */
1624     fprintf (fp, "User-Agent: %s (%s)\n", mutt_make_version (0), os);
1625   }
1626
1627   list_del (&hdrs, (list_del_t*) _mem_free);
1628
1629   return (ferror (fp) == 0 ? 0 : -1);
1630 }
1631
1632 static void encode_headers (LIST * h)
1633 {
1634   char *tmp;
1635   char *p;
1636   int i;
1637
1638   for (; h; h = h->next) {
1639     if (!(p = strchr (h->data, ':')))
1640       continue;
1641
1642     i = p - h->data;
1643     ++p;
1644     SKIPWS (p);
1645     tmp = str_dup (p);
1646
1647     if (!tmp)
1648       continue;
1649
1650     rfc2047_encode_string (&tmp);
1651     mem_realloc (&h->data,
1652                   str_len (h->data) + 2 + str_len (tmp) + 1);
1653
1654     sprintf (h->data + i, ": %s", NONULL (tmp));        /* __SPRINTF_CHECKED__ */
1655
1656     mem_free (&tmp);
1657   }
1658 }
1659
1660 const char *mutt_fqdn (short may_hide_host)
1661 {
1662   char *p = NULL, *q;
1663
1664   if (Fqdn && Fqdn[0] != '@') {
1665     p = Fqdn;
1666
1667     if (may_hide_host && option (OPTHIDDENHOST)) {
1668       if ((p = strchr (Fqdn, '.')))
1669         p++;
1670
1671       /* sanity check: don't hide the host if
1672        * the fqdn is something like detebe.org.
1673        */
1674
1675       if (!p || !(q = strchr (p, '.')))
1676         p = Fqdn;
1677     }
1678   }
1679
1680   return p;
1681 }
1682
1683 static char mutt_normalized_char (char c)
1684 {
1685   if (isalnum (c))
1686     return c;
1687   if (strchr (".!#$%&'*+-/=?^_`{|}~", c))
1688     return c;
1689   return '.';                   /* normalized character (we're stricter than RFC2822, 3.6.4) */
1690 }
1691
1692 static void mutt_gen_localpart (char *buf, unsigned int len, char *fmt)
1693 {
1694   time_t now;
1695   struct tm *tm;
1696   char tmp[SHORT_STRING];
1697
1698   *buf = '\0';
1699
1700   now = time (NULL);
1701   tm = gmtime (&now);
1702
1703   for (; *fmt; ++fmt) {
1704     if (*fmt == '%') {
1705       switch (fmt[1]) {
1706       case 0:
1707         return;
1708       case 'd':
1709         snprintf (tmp, sizeof (tmp), "%02d", tm->tm_mday);
1710         str_ncat (buf, len, tmp, 2);
1711         break;
1712       case 'h':
1713         snprintf (tmp, sizeof (tmp), "%02d", tm->tm_hour);
1714         str_ncat (buf, len, tmp, 2);
1715         break;
1716       case 'm':
1717         snprintf (tmp, sizeof (tmp), "%02d", tm->tm_mon + 1);
1718         str_ncat (buf, len, tmp, 2);
1719         break;
1720       case 'M':
1721         snprintf (tmp, sizeof (tmp), "%02d", tm->tm_min);
1722         str_ncat (buf, len, tmp, 2);
1723         break;
1724       case 'O':
1725         snprintf (tmp, sizeof (tmp), "%lo", (unsigned long) now);
1726         str_ncat (buf, len, tmp, str_len (tmp));
1727         break;
1728       case 'p':
1729         snprintf (tmp, sizeof (tmp), "%u", (unsigned int) getpid ());
1730         str_ncat (buf, len, tmp, str_len (tmp));
1731         break;
1732       case 'P':
1733         snprintf (tmp, sizeof (tmp), "%c", MsgIdPfx);
1734         MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1735         str_ncat (buf, len, tmp, 1);
1736         break;
1737       case 'r':
1738         snprintf (tmp, sizeof (tmp), "%u", (unsigned int) rand ());
1739         str_ncat (buf, len, tmp, str_len (tmp));
1740         break;
1741       case 'R':
1742         snprintf (tmp, sizeof (tmp), "%x", (unsigned int) rand ());
1743         str_ncat (buf, len, tmp, str_len (tmp));
1744         break;
1745       case 's':
1746         snprintf (tmp, sizeof (tmp), "%02d", tm->tm_sec);
1747         str_ncat (buf, len, tmp, 2);
1748         break;
1749       case 'T':
1750         snprintf (tmp, sizeof (tmp), "%u", (unsigned int) now);
1751         str_ncat (buf, len, tmp, str_len (tmp));
1752         break;
1753       case 'X':
1754         snprintf (tmp, sizeof (tmp), "%x", (unsigned int) now);
1755         str_ncat (buf, len, tmp, str_len (tmp));
1756         break;
1757       case 'Y':
1758         snprintf (tmp, sizeof (tmp), "%04d", tm->tm_year + 1900);       /* this will break in the year 10000 ;-) */
1759         str_ncat (buf, len, tmp, 4);
1760         break;
1761       case '%':
1762         str_ncat (buf, len, "%", 1);
1763         break;
1764       default:
1765         str_ncat (buf, len, ".", 1);        /* invalid formats are replaced by '.' */
1766       }                         /* switch */
1767       ++fmt;
1768     }
1769     else {
1770       char c;
1771
1772       c = mutt_normalized_char (*fmt);  /* @todo: filter out invalid characters */
1773       str_ncat (buf, len, &c, 1);
1774     }
1775   }
1776 }
1777
1778 char *mutt_gen_msgid (void)
1779 {
1780   char buf[SHORT_STRING];
1781   char localpart[SHORT_STRING];
1782   unsigned int localpart_length;
1783   const char *fqdn;
1784
1785   if (!(fqdn = mutt_fqdn (0)))
1786     fqdn = NONULL (Hostname);
1787
1788   localpart_length = sizeof (buf) - str_len (fqdn) - 4;  /* the 4 characters are '<', '@', '>' and '\0' */
1789
1790   mutt_gen_localpart (localpart, localpart_length, MsgIdFormat);
1791
1792   snprintf (buf, sizeof (buf), "<%s@%s>", localpart, fqdn);
1793   return (str_dup (buf));
1794 }
1795
1796 static RETSIGTYPE alarm_handler (int sig)
1797 {
1798   SigAlrm = 1;
1799 }
1800
1801 /* invoke sendmail in a subshell
1802    path (in)            path to program to execute
1803    args (in)            arguments to pass to program
1804    msg (in)             temp file containing message to send
1805    tempfile (out)       if sendmail is put in the background, this points
1806                         to the temporary file containing the stdout of the
1807                         child process */
1808 static int
1809 send_msg (const char *path, char **args, const char *msg, char **tempfile)
1810 {
1811   sigset_t set;
1812   int fd, st;
1813   pid_t pid, ppid;
1814
1815   mutt_block_signals_system ();
1816
1817   sigemptyset (&set);
1818   /* we also don't want to be stopped right now */
1819   sigaddset (&set, SIGTSTP);
1820   sigprocmask (SIG_BLOCK, &set, NULL);
1821
1822   if (SendmailWait >= 0) {
1823     char tmp[_POSIX_PATH_MAX];
1824
1825     mutt_mktemp (tmp);
1826     *tempfile = str_dup (tmp);
1827   }
1828
1829   if ((pid = fork ()) == 0) {
1830     struct sigaction act, oldalrm;
1831
1832     /* save parent's ID before setsid() */
1833     ppid = getppid ();
1834
1835     /* we want the delivery to continue even after the main process dies,
1836      * so we put ourselves into another session right away
1837      */
1838     setsid ();
1839
1840     /* next we close all open files */
1841 #if defined(OPEN_MAX)
1842     for (fd = 0; fd < OPEN_MAX; fd++)
1843       close (fd);
1844 #elif defined(_POSIX_OPEN_MAX)
1845     for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1846       close (fd);
1847 #else
1848     close (0);
1849     close (1);
1850     close (2);
1851 #endif
1852
1853     /* now the second fork() */
1854     if ((pid = fork ()) == 0) {
1855       /* "msg" will be opened as stdin */
1856       if (open (msg, O_RDONLY, 0) < 0) {
1857         unlink (msg);
1858         _exit (S_ERR);
1859       }
1860       unlink (msg);
1861
1862       if (SendmailWait >= 0) {
1863         /* *tempfile will be opened as stdout */
1864         if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1865             0)
1866           _exit (S_ERR);
1867         /* redirect stderr to *tempfile too */
1868         if (dup (1) < 0)
1869           _exit (S_ERR);
1870       }
1871       else {
1872         if (open ("/dev/null", O_WRONLY | O_APPEND) < 0)        /* stdout */
1873           _exit (S_ERR);
1874         if (open ("/dev/null", O_RDWR | O_APPEND) < 0)  /* stderr */
1875           _exit (S_ERR);
1876       }
1877
1878       execv (path, args);
1879       _exit (S_ERR);
1880     }
1881     else if (pid == -1) {
1882       unlink (msg);
1883       mem_free (tempfile);
1884       _exit (S_ERR);
1885     }
1886
1887     /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1888      * SendmailWait = 0: wait forever
1889      * SendmailWait < 0: don't wait
1890      */
1891     if (SendmailWait > 0) {
1892       SigAlrm = 0;
1893       act.sa_handler = alarm_handler;
1894 #ifdef SA_INTERRUPT
1895       /* need to make sure waitpid() is interrupted on SIGALRM */
1896       act.sa_flags = SA_INTERRUPT;
1897 #else
1898       act.sa_flags = 0;
1899 #endif
1900       sigemptyset (&act.sa_mask);
1901       sigaction (SIGALRM, &act, &oldalrm);
1902       alarm (SendmailWait);
1903     }
1904     else if (SendmailWait < 0)
1905       _exit (0xff & EX_OK);
1906
1907     if (waitpid (pid, &st, 0) > 0) {
1908       st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1909       if (SendmailWait && st == (0xff & EX_OK)) {
1910         unlink (*tempfile);     /* no longer needed */
1911         mem_free (tempfile);
1912       }
1913     }
1914     else {
1915       st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1916       if (SendmailWait > 0) {
1917         unlink (*tempfile);
1918         mem_free (tempfile);
1919       }
1920     }
1921
1922     /* reset alarm; not really needed, but... */
1923     alarm (0);
1924     sigaction (SIGALRM, &oldalrm, NULL);
1925
1926     if (kill (ppid, 0) == -1 && errno == ESRCH) {
1927       /* the parent is already dead */
1928       unlink (*tempfile);
1929       mem_free (tempfile);
1930     }
1931
1932     _exit (st);
1933   }
1934
1935   sigprocmask (SIG_UNBLOCK, &set, NULL);
1936
1937   if (pid != -1 && waitpid (pid, &st, 0) > 0)
1938     st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;     /* return child status */
1939   else
1940     st = S_ERR;                 /* error */
1941
1942   mutt_unblock_signals_system (1);
1943
1944   return (st);
1945 }
1946
1947 static char **add_args (char **args, size_t * argslen, size_t * argsmax,
1948                         ADDRESS * addr)
1949 {
1950   for (; addr; addr = addr->next) {
1951     /* weed out group mailboxes, since those are for display only */
1952     if (addr->mailbox && !addr->group) {
1953       if (*argslen == *argsmax)
1954         mem_realloc (&args, (*argsmax += 5) * sizeof (char *));
1955       args[(*argslen)++] = addr->mailbox;
1956     }
1957   }
1958   return (args);
1959 }
1960
1961 static const char **
1962 add_option(const char **args, size_t *argslen, size_t *argsmax, const char *s)
1963 {
1964     if (*argslen == *argsmax) {
1965         mem_realloc(&args, (*argsmax += 5) * sizeof (char *));
1966     }
1967     args[(*argslen)++] = s;
1968     return (args);
1969 }
1970
1971 static int mutt_invoke_sendmail (ADDRESS * from,        /* the sender */
1972                                  ADDRESS * to, ADDRESS * cc, ADDRESS * bcc,     /* recips */
1973                                  const char *msg,       /* file containing message */
1974                                  int eightbit)
1975 {                               /* message contains 8bit chars */
1976   char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
1977   const char **args = NULL;
1978   size_t argslen = 0, argsmax = 0;
1979   int i;
1980
1981 #ifdef USE_NNTP
1982   if (option (OPTNEWSSEND)) {
1983     char cmd[LONG_STRING];
1984
1985     mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0,
1986                        0);
1987     if (!*cmd) {
1988       i = nntp_post (msg);
1989       unlink (msg);
1990       return i;
1991     }
1992
1993     s = str_dup (cmd);
1994   }
1995   else
1996 #endif
1997     s = str_dup (Sendmail);
1998
1999   ps = s;
2000   i = 0;
2001   while ((ps = strtok (ps, " "))) {
2002     if (argslen == argsmax)
2003       mem_realloc (&args, sizeof (char *) * (argsmax += 5));
2004
2005     if (i)
2006       args[argslen++] = ps;
2007     else {
2008       path = str_dup (ps);
2009       ps = strrchr (ps, '/');
2010       if (ps)
2011         ps++;
2012       else
2013         ps = path;
2014       args[argslen++] = ps;
2015     }
2016     ps = NULL;
2017     i++;
2018   }
2019
2020 #ifdef USE_NNTP
2021   if (!option (OPTNEWSSEND)) {
2022 #endif
2023     if (eightbit && option (OPTUSE8BITMIME))
2024       args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
2025
2026     if (option (OPTENVFROM)) {
2027       ADDRESS *f = NULL;
2028       if (EnvFrom)
2029         f = EnvFrom;
2030       else if (from && !from->next)
2031         f = from;
2032       if (f) {
2033         args = add_option (args, &argslen, &argsmax, "-f");
2034         args = add_args (args, &argslen, &argsmax, f);
2035       }
2036     }
2037     if (DsnNotify) {
2038       args = add_option (args, &argslen, &argsmax, "-N");
2039       args = add_option (args, &argslen, &argsmax, DsnNotify);
2040     }
2041     if (DsnReturn) {
2042       args = add_option (args, &argslen, &argsmax, "-R");
2043       args = add_option (args, &argslen, &argsmax, DsnReturn);
2044     }
2045     args = add_option (args, &argslen, &argsmax, "--");
2046     args = add_args (args, &argslen, &argsmax, to);
2047     args = add_args (args, &argslen, &argsmax, cc);
2048     args = add_args (args, &argslen, &argsmax, bcc);
2049 #ifdef USE_NNTP
2050   }
2051 #endif
2052
2053   if (argslen == argsmax)
2054     mem_realloc (&args, sizeof (char *) * (++argsmax));
2055
2056   args[argslen++] = NULL;
2057
2058   if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
2059     if (i != S_BKG) {
2060       const char *e = mutt_strsysexit (i);
2061
2062       e = mutt_strsysexit (i);
2063       mutt_error (_("Error sending message, child exited %d (%s)."), i,
2064                   NONULL (e));
2065       if (childout) {
2066         struct stat st;
2067
2068         if (stat (childout, &st) == 0 && st.st_size > 0)
2069           mutt_do_pager (_("Output of the delivery process"), childout, 0,
2070                          NULL);
2071       }
2072     }
2073   }
2074   else
2075     unlink (childout);
2076
2077   mem_free (&childout);
2078   mem_free (&path);
2079   mem_free (&s);
2080   mem_free (&args);
2081
2082   if (i == (EX_OK & 0xff))
2083     i = 0;
2084   else if (i == S_BKG)
2085     i = 1;
2086   else
2087     i = -1;
2088   return (i);
2089 }
2090
2091 int mutt_invoke_mta (ADDRESS * from,    /* the sender */
2092                      ADDRESS * to, ADDRESS * cc, ADDRESS * bcc, /* recips */
2093                      const char *msg,   /* file containing message */
2094                      int eightbit)
2095 {                               /* message contains 8bit chars */
2096 #ifdef USE_LIBESMTP
2097 #ifdef USE_NNTP
2098   if (!option (OPTNEWSSEND))
2099 #endif
2100     if (SmtpHost)
2101       return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
2102 #endif
2103
2104   return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
2105 }
2106
2107 /* appends string 'b' to string 'a', and returns the pointer to the new
2108    string. */
2109 char *mutt_append_string (char *a, const char *b)
2110 {
2111   size_t la = str_len (a);
2112
2113   mem_realloc (&a, la + str_len (b) + 1);
2114   strcpy (a + la, b);           /* __STRCPY_CHECKED__ */
2115   return (a);
2116 }
2117
2118 /* returns 1 if char `c' needs to be quoted to protect from shell
2119    interpretation when executing commands in a subshell */
2120 #define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
2121
2122 /* returns 1 if string `s' contains characters which could cause problems
2123    when used on a command line to execute a command */
2124 int mutt_needs_quote (const char *s)
2125 {
2126   while (*s) {
2127     if (INVALID_CHAR (*s))
2128       return 1;
2129     s++;
2130   }
2131   return 0;
2132 }
2133
2134 /* Quote a string to prevent shell escapes when this string is used on the
2135    command line to send mail. */
2136 char *mutt_quote_string (const char *s)
2137 {
2138   char *r, *pr;
2139   size_t rlen;
2140
2141   rlen = str_len (s) + 3;
2142   pr = r = (char *) mem_malloc (rlen);
2143   *pr++ = '"';
2144   while (*s) {
2145     if (INVALID_CHAR (*s)) {
2146       size_t o = pr - r;
2147
2148       mem_realloc (&r, ++rlen);
2149       pr = r + o;
2150       *pr++ = '\\';
2151     }
2152     *pr++ = *s++;
2153   }
2154   *pr++ = '"';
2155   *pr = 0;
2156   return (r);
2157 }
2158
2159 /* For postponing (!final) do the necessary encodings only */
2160 void mutt_prepare_envelope (ENVELOPE * env, int final)
2161 {
2162   char buffer[LONG_STRING];
2163
2164   if (final) {
2165     if (env->bcc && !(env->to || env->cc)) {
2166       /* some MTA's will put an Apparently-To: header field showing the Bcc:
2167        * recipients if there is no To: or Cc: field, so attempt to suppress
2168        * it by using an empty To: field.
2169        */
2170       env->to = rfc822_new_address ();
2171       env->to->group = 1;
2172       env->to->next = rfc822_new_address ();
2173
2174       buffer[0] = 0;
2175       rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2176                   RFC822Specials);
2177
2178       env->to->mailbox = str_dup (buffer);
2179     }
2180
2181     mutt_set_followup_to (env);
2182
2183     if (!env->message_id && MsgIdFormat && *MsgIdFormat)
2184       env->message_id = mutt_gen_msgid ();
2185   }
2186
2187   /* Take care of 8-bit => 7-bit conversion. */
2188   rfc2047_encode_adrlist (env->to, "To");
2189   rfc2047_encode_adrlist (env->cc, "Cc");
2190   rfc2047_encode_adrlist (env->bcc, "Bcc");
2191   rfc2047_encode_adrlist (env->from, "From");
2192   rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2193   rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2194
2195   if (env->subject)
2196 #ifdef USE_NNTP
2197     if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2198 #endif
2199     {
2200       rfc2047_encode_string (&env->subject);
2201     }
2202   encode_headers (env->userhdrs);
2203 }
2204
2205 void mutt_unprepare_envelope (ENVELOPE * env)
2206 {
2207   LIST *item;
2208
2209   for (item = env->userhdrs; item; item = item->next)
2210     rfc2047_decode (&item->data);
2211
2212   rfc822_free_address (&env->mail_followup_to);
2213
2214   /* back conversions */
2215   rfc2047_decode_adrlist (env->to);
2216   rfc2047_decode_adrlist (env->cc);
2217   rfc2047_decode_adrlist (env->bcc);
2218   rfc2047_decode_adrlist (env->from);
2219   rfc2047_decode_adrlist (env->reply_to);
2220   rfc2047_decode (&env->subject);
2221 }
2222
2223 static int _mutt_bounce_message (FILE * fp, HEADER * h, ADDRESS * to,
2224                                  const char *resent_from, ADDRESS * env_from)
2225 {
2226   int i, ret = 0;
2227   FILE *f;
2228   char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2229   MESSAGE *msg = NULL;
2230
2231   if (!h) {
2232     /* Try to bounce each message out, aborting if we get any failures. */
2233     for (i = 0; i < Context->msgcount; i++)
2234       if (Context->hdrs[i]->tagged)
2235         ret |=
2236           _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2237                                 env_from);
2238     return ret;
2239   }
2240
2241   /* If we failed to open a message, return with error */
2242   if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2243     return -1;
2244
2245   if (!fp)
2246     fp = msg->fp;
2247
2248   mutt_mktemp (tempfile);
2249   if ((f = safe_fopen (tempfile, "w")) != NULL) {
2250     int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2251
2252     if (!option (OPTBOUNCEDELIVERED))
2253       ch_flags |= CH_WEED_DELIVERED;
2254
2255     fseeko (fp, h->offset, 0);
2256     fprintf (f, "Resent-From: %s", resent_from);
2257     fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2258     if (MsgIdFormat && *MsgIdFormat)
2259       fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid ());
2260     fputs ("Resent-To: ", f);
2261     mutt_write_address_list (to, f, 11, 0);
2262     mutt_copy_header (fp, h, f, ch_flags, NULL);
2263     fputc ('\n', f);
2264     mutt_copy_bytes (fp, f, h->content->length);
2265     fclose (f);
2266
2267     ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2268                            h->content->encoding == ENC8BIT);
2269   }
2270
2271   if (msg)
2272     mx_close_message (&msg);
2273
2274   return ret;
2275 }
2276
2277 int mutt_bounce_message (FILE * fp, HEADER * h, ADDRESS * to)
2278 {
2279   ADDRESS *from;
2280   const char *fqdn = mutt_fqdn (1);
2281   char resent_from[STRING];
2282   int ret;
2283   char *err;
2284
2285   resent_from[0] = '\0';
2286   from = mutt_default_from ();
2287
2288   if (fqdn)
2289     rfc822_qualify (from, fqdn);
2290
2291   rfc2047_encode_adrlist (from, "Resent-From");
2292   if (mutt_addrlist_to_idna (from, &err)) {
2293     mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2294     return -1;
2295   }
2296   rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2297
2298 #ifdef USE_NNTP
2299   unset_option (OPTNEWSSEND);
2300 #endif
2301
2302   ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2303
2304   rfc822_free_address (&from);
2305
2306   return ret;
2307 }
2308
2309
2310 /* given a list of addresses, return a list of unique addresses */
2311 ADDRESS *mutt_remove_duplicates (ADDRESS * addr)
2312 {
2313   ADDRESS *top = addr;
2314   ADDRESS **last = &top;
2315   ADDRESS *tmp;
2316   int dup;
2317
2318   while (addr) {
2319     for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next) {
2320       if (tmp->mailbox && addr->mailbox &&
2321           !ascii_strcasecmp (addr->mailbox, tmp->mailbox)) {
2322         dup = 1;
2323         break;
2324       }
2325     }
2326
2327     if (dup) {
2328       debug_print (2, ("Removing %s\n", addr->mailbox));
2329
2330       *last = addr->next;
2331
2332       addr->next = NULL;
2333       rfc822_free_address (&addr);
2334
2335       addr = *last;
2336     }
2337     else {
2338       last = &addr->next;
2339       addr = addr->next;
2340     }
2341   }
2342
2343   return (top);
2344 }
2345
2346 static void set_noconv_flags (BODY * b, short flag)
2347 {
2348   for (; b; b = b->next) {
2349     if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2350       set_noconv_flags (b->parts, flag);
2351     else if (b->type == TYPETEXT && b->noconv) {
2352       if (flag)
2353         mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2354       else
2355         mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2356     }
2357   }
2358 }
2359
2360 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2361                     int post, char *fcc)
2362 {
2363   CONTEXT f;
2364   MESSAGE *msg;
2365   char tempfile[_POSIX_PATH_MAX];
2366   FILE *tempfp = NULL;
2367   int r;
2368
2369   if (post)
2370     set_noconv_flags (hdr->content, 1);
2371
2372   if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2373     debug_print (1, ("unable to open mailbox %s in append-mode, aborting.\n", path));
2374     return (-1);
2375   }
2376
2377   /* We need to add a Content-Length field to avoid problems where a line in
2378    * the message body begins with "From "   
2379    */
2380   if (f.magic == M_MMDF || f.magic == M_MBOX) {
2381     mutt_mktemp (tempfile);
2382     if ((tempfp = safe_fopen (tempfile, "w+")) == NULL) {
2383       mutt_perror (tempfile);
2384       mx_close_mailbox (&f, NULL);
2385       return (-1);
2386     }
2387   }
2388
2389   hdr->read = !post;            /* make sure to put it in the `cur' directory (maildir) */
2390   if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2391     mx_close_mailbox (&f, NULL);
2392     return (-1);
2393   }
2394
2395   /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2396    * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header() 
2397    * */
2398   mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0,
2399                             0);
2400
2401   /* (postponment) if this was a reply of some sort, <msgid> contians the
2402    * Message-ID: of message replied to.  Save it using a special X-Mutt-
2403    * header so it can be picked up if the message is recalled at a later
2404    * point in time.  This will allow the message to be marked as replied if
2405    * the same mailbox is still open.
2406    */
2407   if (post && msgid)
2408     fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2409
2410   /* (postponment) save the Fcc: using a special X-Mutt- header so that
2411    * it can be picked up when the message is recalled 
2412    */
2413   if (post && fcc)
2414     fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2415   fprintf (msg->fp, "Status: RO\n");
2416
2417
2418
2419   /* (postponment) if the mail is to be signed or encrypted, save this info */
2420   if ((WithCrypto & APPLICATION_PGP)
2421       && post && (hdr->security & APPLICATION_PGP)) {
2422     fputs ("X-Mutt-PGP: ", msg->fp);
2423     if (hdr->security & ENCRYPT)
2424       fputc ('E', msg->fp);
2425     if (hdr->security & SIGN) {
2426       fputc ('S', msg->fp);
2427       if (PgpSignAs && *PgpSignAs)
2428         fprintf (msg->fp, "<%s>", PgpSignAs);
2429     }
2430     if (hdr->security & INLINE)
2431       fputc ('I', msg->fp);
2432     fputc ('\n', msg->fp);
2433   }
2434
2435   /* (postponment) if the mail is to be signed or encrypted, save this info */
2436   if ((WithCrypto & APPLICATION_SMIME)
2437       && post && (hdr->security & APPLICATION_SMIME)) {
2438     fputs ("X-Mutt-SMIME: ", msg->fp);
2439     if (hdr->security & ENCRYPT) {
2440       fputc ('E', msg->fp);
2441       if (SmimeCryptAlg && *SmimeCryptAlg)
2442         fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2443     }
2444     if (hdr->security & SIGN) {
2445       fputc ('S', msg->fp);
2446       if (SmimeDefaultKey && *SmimeDefaultKey)
2447         fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2448     }
2449     if (hdr->security & INLINE)
2450       fputc ('I', msg->fp);
2451     fputc ('\n', msg->fp);
2452   }
2453
2454 #ifdef MIXMASTER
2455   /* (postponement) if the mail is to be sent through a mixmaster 
2456    * chain, save that information
2457    */
2458
2459   if (post && hdr->chain && hdr->chain) {
2460     LIST *p;
2461
2462     fputs ("X-Mutt-Mix:", msg->fp);
2463     for (p = hdr->chain; p; p = p->next)
2464       fprintf (msg->fp, " %s", (char *) p->data);
2465
2466     fputc ('\n', msg->fp);
2467   }
2468 #endif
2469
2470   if (tempfp) {
2471     char sasha[LONG_STRING];
2472     int lines = 0;
2473
2474     mutt_write_mime_body (hdr->content, tempfp);
2475
2476     /* make sure the last line ends with a newline.  Emacs doesn't ensure
2477      * this will happen, and it can cause problems parsing the mailbox   
2478      * later.
2479      */
2480     fseeko (tempfp, -1, 2);
2481     if (fgetc (tempfp) != '\n') {
2482       fseeko (tempfp, 0, 2);
2483       fputc ('\n', tempfp);
2484     }
2485
2486     fflush (tempfp);
2487     if (ferror (tempfp)) {
2488       debug_print (1, ("%s: write failed.\n", tempfile));
2489       fclose (tempfp);
2490       unlink (tempfile);
2491       mx_commit_message (msg, &f);      /* XXX - really? */
2492       mx_close_message (&msg);
2493       mx_close_mailbox (&f, NULL);
2494       return -1;
2495     }
2496
2497     /* count the number of lines */
2498     rewind (tempfp);
2499     while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2500       lines++;
2501     fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2502     fprintf (msg->fp, "Lines: %d\n\n", lines);
2503
2504     /* copy the body and clean up */
2505     rewind (tempfp);
2506     r = mutt_copy_stream (tempfp, msg->fp);
2507     if (fclose (tempfp) != 0)
2508       r = -1;
2509     /* if there was an error, leave the temp version */
2510     if (!r)
2511       unlink (tempfile);
2512   }
2513   else {
2514     fputc ('\n', msg->fp);      /* finish off the header */
2515     r = mutt_write_mime_body (hdr->content, msg->fp);
2516   }
2517
2518   if (mx_commit_message (msg, &f) != 0)
2519     r = -1;
2520   mx_close_message (&msg);
2521   mx_close_mailbox (&f, NULL);
2522
2523   if (post)
2524     set_noconv_flags (hdr->content, 0);
2525
2526   return r;
2527 }