license statements.
[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 <lib-lib/mem.h>
17 #include <lib-lib/ascii.h>
18 #include <lib-lib/str.h>
19 #include <lib-lib/macros.h>
20
21 #include "mutt.h"
22 #include "handler.h"
23 #include "recvattach.h"
24 #include "mutt_curses.h"
25 #include "rfc2047.h"
26 #include "rfc2231.h"
27 #include "mx.h"
28 #include "mime.h"
29 #include "copy.h"
30 #include "pager.h"
31 #include "charset.h"
32 #include "mutt_crypt.h"
33 #include "mutt_idna.h"
34
35 #include "lib/debug.h"
36
37 #include <string.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <ctype.h>
42 #include <sys/stat.h>
43 #include <signal.h>
44 #include <sys/wait.h>
45 #include <fcntl.h>
46 #include <sys/utsname.h>
47
48 #ifdef USE_LIBESMTP
49 # include "mutt_libesmtp.h"
50 #endif /* USE_LIBESMTP */
51
52 #ifdef USE_NNTP
53 #include <nntp.h>
54 #endif
55
56 #ifdef HAVE_SYSEXITS_H
57 #include <sysexits.h>
58 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
59 #define EX_OK 0
60 #endif
61
62 /* If you are debugging this file, comment out the following line. */
63 /*#define NDEBUG*/
64
65 #ifdef NDEBUG
66 #define assert(x)
67 #else
68 #include <assert.h>
69 #endif
70
71 extern char RFC822Specials[];
72
73 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
74
75 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
76
77 char B64Chars[64] = {
78   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
79   'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
80   'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
81   't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
82   '8', '9', '+', '/'
83 };
84
85 static char MsgIdPfx = 'A';
86
87 static void transform_to_7bit (BODY * a, FILE * fpin);
88
89 static void encode_quoted (FGETCONV * fc, FILE * fout, int istext)
90 {
91   int c, linelen = 0;
92   char line[77], savechar;
93
94   while ((c = fgetconv (fc)) != EOF) {
95     /* Wrap the line if needed. */
96     if (linelen == 76 && ((istext && c != '\n') || !istext)) {
97       /* If the last character is "quoted", then be sure to move all three
98        * characters to the next line.  Otherwise, just move the last
99        * character...
100        */
101       if (line[linelen - 3] == '=') {
102         line[linelen - 3] = 0;
103         fputs (line, fout);
104         fputs ("=\n", fout);
105         line[linelen] = 0;
106         line[0] = '=';
107         line[1] = line[linelen - 2];
108         line[2] = line[linelen - 1];
109         linelen = 3;
110       }
111       else {
112         savechar = line[linelen - 1];
113         line[linelen - 1] = '=';
114         line[linelen] = 0;
115         fputs (line, fout);
116         fputc ('\n', fout);
117         line[0] = savechar;
118         linelen = 1;
119       }
120     }
121
122     /* Escape lines that begin with/only contain "the message separator". */
123     if (linelen == 4 && !str_ncmp ("From", line, 4)) {
124       strfcpy (line, "=46rom", sizeof (line));
125       linelen = 6;
126     }
127     else if (linelen == 4 && !str_ncmp ("from", line, 4)) {
128       strfcpy (line, "=66rom", sizeof (line));
129       linelen = 6;
130     }
131     else if (linelen == 1 && line[0] == '.') {
132       strfcpy (line, "=2E", sizeof (line));
133       linelen = 3;
134     }
135
136
137     if (c == '\n' && istext) {
138       /* Check to make sure there is no trailing space on this line. */
139       if (linelen > 0
140           && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
141         if (linelen < 74) {
142           sprintf (line + linelen - 1, "=%2.2X",
143                    (unsigned char) line[linelen - 1]);
144           fputs (line, fout);
145         }
146         else {
147           int savechar = line[linelen - 1];
148
149           line[linelen - 1] = '=';
150           line[linelen] = 0;
151           fputs (line, fout);
152           fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
153         }
154       }
155       else {
156         line[linelen] = 0;
157         fputs (line, fout);
158       }
159       fputc ('\n', fout);
160       linelen = 0;
161     }
162     else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
163       /* Check to make sure there is enough room for the quoted character.
164        * If not, wrap to the next line.
165        */
166       if (linelen > 73) {
167         line[linelen++] = '=';
168         line[linelen] = 0;
169         fputs (line, fout);
170         fputc ('\n', fout);
171         linelen = 0;
172       }
173       sprintf (line + linelen, "=%2.2X", (unsigned char) c);
174       linelen += 3;
175     }
176     else {
177       /* Don't worry about wrapping the line here.  That will happen during
178        * the next iteration when I'll also know what the next character is.
179        */
180       line[linelen++] = c;
181     }
182   }
183
184   /* Take care of anything left in the buffer */
185   if (linelen > 0) {
186     if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
187       /* take care of trailing whitespace */
188       if (linelen < 74)
189         sprintf (line + linelen - 1, "=%2.2X",
190                  (unsigned char) line[linelen - 1]);
191       else {
192         savechar = line[linelen - 1];
193         line[linelen - 1] = '=';
194         line[linelen] = 0;
195         fputs (line, fout);
196         fputc ('\n', fout);
197         sprintf (line, "=%2.2X", (unsigned char) savechar);
198       }
199     }
200     else
201       line[linelen] = 0;
202     fputs (line, fout);
203   }
204 }
205
206 static char b64_buffer[3];
207 static short b64_num;
208 static short b64_linelen;
209
210 static void b64_flush (FILE * fout)
211 {
212   short i;
213
214   if (!b64_num)
215     return;
216
217   if (b64_linelen >= 72) {
218     fputc ('\n', fout);
219     b64_linelen = 0;
220   }
221
222   for (i = b64_num; i < 3; i++)
223     b64_buffer[i] = '\0';
224
225   fputc (B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
226   b64_linelen++;
227   fputc (B64Chars
228          [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
229   b64_linelen++;
230
231   if (b64_num > 1) {
232     fputc (B64Chars
233            [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
234            fout);
235     b64_linelen++;
236     if (b64_num > 2) {
237       fputc (B64Chars[b64_buffer[2] & 0x3f], fout);
238       b64_linelen++;
239     }
240   }
241
242   while (b64_linelen % 4) {
243     fputc ('=', fout);
244     b64_linelen++;
245   }
246
247   b64_num = 0;
248 }
249
250
251 static void b64_putc (char c, FILE * fout)
252 {
253   if (b64_num == 3)
254     b64_flush (fout);
255
256   b64_buffer[b64_num++] = c;
257 }
258
259
260 static void encode_base64 (FGETCONV * fc, FILE * fout, int istext)
261 {
262   int ch, ch1 = EOF;
263
264   b64_num = b64_linelen = 0;
265
266   while ((ch = fgetconv (fc)) != EOF) {
267     if (istext && ch == '\n' && ch1 != '\r')
268       b64_putc ('\r', fout);
269     b64_putc (ch, fout);
270     ch1 = ch;
271   }
272   b64_flush (fout);
273   fputc ('\n', fout);
274 }
275
276 static void encode_8bit (FGETCONV * fc, FILE * fout, int istext)
277 {
278   int ch;
279
280   while ((ch = fgetconv (fc)) != EOF)
281     fputc (ch, fout);
282 }
283
284
285 int mutt_write_mime_header (BODY * a, FILE * f)
286 {
287   PARAMETER *p;
288   char buffer[STRING];
289   char *t;
290   char *fn;
291   int len;
292   int tmplen;
293   int encode;
294
295   fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
296
297   if (a->parameter) {
298     len = 25 + m_strlen(a->subtype);        /* approximate len. of content-type */
299
300     for (p = a->parameter; p; p = p->next) {
301       char *tmp;
302
303       if (!p->value)
304         continue;
305
306       fputc (';', f);
307
308       buffer[0] = 0;
309       tmp = m_strdup(p->value);
310       encode = rfc2231_encode_string (&tmp);
311       rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
312
313       /* Dirty hack to make messages readable by Outlook Express 
314        * for the Mac: force quotes around the boundary parameter
315        * even when they aren't needed.
316        */
317
318       if (!ascii_strcasecmp (p->attribute, "boundary")
319           && !strcmp (buffer, tmp))
320         snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
321
322       p_delete(&tmp);
323
324       tmplen = m_strlen(buffer) + m_strlen(p->attribute) + 1;
325
326       if (len + tmplen + 2 > 76) {
327         fputs ("\n\t", f);
328         len = tmplen + 8;
329       }
330       else {
331         fputc (' ', f);
332         len += tmplen + 1;
333       }
334
335       fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
336
337     }
338   }
339
340   fputc ('\n', f);
341
342   if (a->description)
343     fprintf (f, "Content-Description: %s\n", a->description);
344
345   fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
346
347   if (a->use_disp) {
348     if (!(fn = a->d_filename))
349       fn = a->filename;
350
351     if (fn) {
352       char *tmp;
353
354       /* Strip off the leading path... */
355       if ((t = strrchr (fn, '/')))
356         t++;
357       else
358         t = fn;
359
360       buffer[0] = 0;
361       tmp = m_strdup(t);
362       encode = rfc2231_encode_string (&tmp);
363       rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
364       p_delete(&tmp);
365       fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
366     }
367   }
368
369   fputc ('\n', f);
370
371   if (a->encoding != ENC7BIT)
372     fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
373
374   /* Do NOT add the terminator here!!! */
375   return (ferror (f) ? -1 : 0);
376 }
377
378 # define write_as_text_part(a)  (mutt_is_text_part(a) \
379                                  || ((WithCrypto & APPLICATION_PGP)\
380                                       && mutt_is_application_pgp(a)))
381
382 int mutt_write_mime_body (BODY * a, FILE * f)
383 {
384   char *p, boundary[SHORT_STRING];
385   char send_charset[SHORT_STRING];
386   FILE *fpin;
387   BODY *t;
388   FGETCONV *fc;
389
390   if (a->type == TYPEMULTIPART) {
391     /* First, find the boundary to use */
392     if (!(p = mutt_get_parameter ("boundary", a->parameter))) {
393       debug_print (1, ("no boundary parameter found!\n"));
394       mutt_error _("No boundary parameter found! [report this error]");
395
396       return (-1);
397     }
398     strfcpy (boundary, p, sizeof (boundary));
399
400     for (t = a->parts; t; t = t->next) {
401       fprintf (f, "\n--%s\n", boundary);
402       if (mutt_write_mime_header (t, f) == -1)
403         return -1;
404       fputc ('\n', f);
405       if (mutt_write_mime_body (t, f) == -1)
406         return -1;
407     }
408     fprintf (f, "\n--%s--\n", boundary);
409     return (ferror (f) ? -1 : 0);
410   }
411
412   /* This is pretty gross, but it's the best solution for now... */
413   if ((WithCrypto & APPLICATION_PGP)
414       && a->type == TYPEAPPLICATION
415       && str_cmp (a->subtype, "pgp-encrypted") == 0) {
416     fputs ("Version: 1\n", f);
417     return 0;
418   }
419
420   if ((fpin = fopen (a->filename, "r")) == NULL) {
421     debug_print (1, ("%s no longer exists!\n", a->filename));
422     mutt_error (_("%s no longer exists!"), a->filename);
423     return -1;
424   }
425
426   if (a->type == TYPETEXT && (!a->noconv))
427     fc = fgetconv_open (fpin, a->file_charset,
428                         mutt_get_body_charset (send_charset,
429                                                sizeof (send_charset), a), 0);
430   else
431     fc = fgetconv_open (fpin, 0, 0, 0);
432
433   if (a->encoding == ENCQUOTEDPRINTABLE)
434     encode_quoted (fc, f, write_as_text_part (a));
435   else if (a->encoding == ENCBASE64)
436     encode_base64 (fc, f, write_as_text_part (a));
437   else if (a->type == TYPETEXT && (!a->noconv))
438     encode_8bit (fc, f, write_as_text_part (a));
439   else
440     mutt_copy_stream (fpin, f);
441
442   fgetconv_close (&fc);
443   fclose (fpin);
444
445   return (ferror (f) ? -1 : 0);
446 }
447
448 #undef write_as_text_part
449
450 #define BOUNDARYLEN 16
451 void mutt_generate_boundary (PARAMETER ** parm)
452 {
453   char rs[BOUNDARYLEN + 1];
454   char *p = rs;
455   int i;
456
457   rs[BOUNDARYLEN] = 0;
458   for (i = 0; i < BOUNDARYLEN; i++)
459     *p++ = B64Chars[LRAND () % sizeof (B64Chars)];
460   *p = 0;
461
462   mutt_set_parameter ("boundary", rs, parm);
463 }
464
465 typedef struct {
466   int from;
467   int whitespace;
468   int dot;
469   int linelen;
470   int was_cr;
471 } CONTENT_STATE;
472
473
474 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
475                                  size_t dlen)
476 {
477   int from = s->from;
478   int whitespace = s->whitespace;
479   int dot = s->dot;
480   int linelen = s->linelen;
481   int was_cr = s->was_cr;
482
483   if (!d) {                     /* This signals EOF */
484     if (was_cr)
485       info->binary = 1;
486     if (linelen > info->linemax)
487       info->linemax = linelen;
488
489     return;
490   }
491
492   for (; dlen; d++, dlen--) {
493     char ch = *d;
494
495     if (was_cr) {
496       was_cr = 0;
497       if (ch != '\n') {
498         info->binary = 1;
499       }
500       else {
501         if (whitespace)
502           info->space = 1;
503         if (dot)
504           info->dot = 1;
505         if (linelen > info->linemax)
506           info->linemax = linelen;
507         whitespace = 0;
508         dot = 0;
509         linelen = 0;
510         continue;
511       }
512     }
513
514     linelen++;
515     if (ch == '\n') {
516       info->crlf++;
517       if (whitespace)
518         info->space = 1;
519       if (dot)
520         info->dot = 1;
521       if (linelen > info->linemax)
522         info->linemax = linelen;
523       whitespace = 0;
524       linelen = 0;
525       dot = 0;
526     }
527     else if (ch == '\r') {
528       info->crlf++;
529       info->cr = 1;
530       was_cr = 1;
531       continue;
532     }
533     else if (ch & 0x80)
534       info->hibin++;
535     else if (ch == '\t' || ch == '\f') {
536       info->ascii++;
537       whitespace++;
538     }
539     else if (ch < 32 || ch == 127)
540       info->lobin++;
541     else {
542       if (linelen == 1) {
543         if ((ch == 'F') || (ch == 'f'))
544           from = 1;
545         else
546           from = 0;
547         if (ch == '.')
548           dot = 1;
549         else
550           dot = 0;
551       }
552       else if (from) {
553         if (linelen == 2 && ch != 'r')
554           from = 0;
555         else if (linelen == 3 && ch != 'o')
556           from = 0;
557         else if (linelen == 4) {
558           if (ch == 'm')
559             info->from = 1;
560           from = 0;
561         }
562       }
563       if (ch == ' ')
564         whitespace++;
565       info->ascii++;
566     }
567
568     if (linelen > 1)
569       dot = 0;
570     if (ch != ' ' && ch != '\t')
571       whitespace = 0;
572   }
573
574   s->from = from;
575   s->whitespace = whitespace;
576   s->dot = dot;
577   s->linelen = linelen;
578   s->was_cr = was_cr;
579
580 }
581
582 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
583 #define BUGGY_ICONV 1
584
585 /*
586  * Find the best charset conversion of the file from fromcode into one
587  * of the tocodes. If successful, set *tocode and CONTENT *info and
588  * return the number of characters converted inexactly. If no
589  * conversion was possible, return -1.
590  *
591  * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
592  * which would otherwise prevent us from knowing the number of inexact
593  * conversions. Where the candidate target charset is UTF-8 we avoid
594  * doing the second conversion because iconv_open("UTF-8", "UTF-8")
595  * fails with some libraries.
596  *
597  * We assume that the output from iconv is never more than 4 times as
598  * long as the input for any pair of charsets we might be interested
599  * in.
600  */
601 static size_t convert_file_to (FILE * file, const char *fromcode,
602                                int ncodes, const char **tocodes,
603                                int *tocode, CONTENT * info)
604 {
605 #ifdef HAVE_ICONV
606   iconv_t cd1, *cd;
607   char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
608   const char *ib, *ub;
609   char *ob;
610   size_t ibl, obl, ubl, ubl1, n, ret;
611   int i;
612   CONTENT *infos;
613   CONTENT_STATE *states;
614   size_t *score;
615
616   cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
617   if (cd1 == (iconv_t) (-1))
618     return -1;
619
620   cd = p_new(iconv_t, ncodes);
621   score = p_new(size_t, ncodes);
622   states = p_new(CONTENT_STATE, ncodes);
623   infos = p_new(CONTENT, ncodes);
624
625   for (i = 0; i < ncodes; i++)
626     if (ascii_strcasecmp (tocodes[i], "UTF-8"))
627       cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
628     else
629       /* Special case for conversion to UTF-8 */
630       cd[i] = (iconv_t) (-1), score[i] = (size_t) (-1);
631
632   rewind (file);
633   ibl = 0;
634   for (;;) {
635
636     /* Try to fill input buffer */
637     n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
638     ibl += n;
639
640     /* Convert to UTF-8 */
641     ib = bufi;
642     ob = bufu, obl = sizeof (bufu);
643     n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
644     assert (n == (size_t) (-1) || !n || ICONV_NONTRANS);
645     if (n == (size_t) (-1) &&
646         ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
647       assert (errno == EILSEQ ||
648               (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
649       ret = (size_t) (-1);
650       break;
651     }
652     ubl1 = ob - bufu;
653
654     /* Convert from UTF-8 */
655     for (i = 0; i < ncodes; i++)
656       if (cd[i] != (iconv_t) (-1) && score[i] != (size_t) (-1)) {
657         ub = bufu, ubl = ubl1;
658         ob = bufo, obl = sizeof (bufo);
659         n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
660         if (n == (size_t) (-1)) {
661           assert (errno == E2BIG ||
662                   (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
663           score[i] = (size_t) (-1);
664         }
665         else {
666           score[i] += n;
667           update_content_info (&infos[i], &states[i], bufo, ob - bufo);
668         }
669       }
670       else if (cd[i] == (iconv_t) (-1) && score[i] == (size_t) (-1))
671         /* Special case for conversion to UTF-8 */
672         update_content_info (&infos[i], &states[i], bufu, ubl1);
673
674     if (ibl)
675       /* Save unused input */
676       memmove (bufi, ib, ibl);
677     else if (!ubl1 && ib < bufi + sizeof (bufi)) {
678       ret = 0;
679       break;
680     }
681   }
682
683   if (!ret) {
684     /* Find best score */
685     ret = (size_t) (-1);
686     for (i = 0; i < ncodes; i++) {
687       if (cd[i] == (iconv_t) (-1) && score[i] == (size_t) (-1)) {
688         /* Special case for conversion to UTF-8 */
689         *tocode = i;
690         ret = 0;
691         break;
692       }
693       else if (cd[i] == (iconv_t) (-1) || score[i] == (size_t) (-1))
694         continue;
695       else if (ret == (size_t) (-1) || score[i] < ret) {
696         *tocode = i;
697         ret = score[i];
698         if (!ret)
699           break;
700       }
701     }
702     if (ret != (size_t) (-1)) {
703       memcpy (info, &infos[*tocode], sizeof (CONTENT));
704       update_content_info (info, &states[*tocode], 0, 0);       /* EOF */
705     }
706   }
707
708   for (i = 0; i < ncodes; i++)
709     if (cd[i] != (iconv_t) (-1))
710       iconv_close (cd[i]);
711
712   iconv_close (cd1);
713   p_delete(&cd);
714   p_delete(&infos);
715   p_delete(&score);
716   p_delete(&states);
717
718   return ret;
719 #else
720   return -1;
721 #endif /* !HAVE_ICONV */
722 }
723
724 /*
725  * Find the first of the fromcodes that gives a valid conversion and
726  * the best charset conversion of the file into one of the tocodes. If
727  * successful, set *fromcode and *tocode to dynamically allocated
728  * strings, set CONTENT *info, and return the number of characters
729  * converted inexactly. If no conversion was possible, return -1.
730  *
731  * Both fromcodes and tocodes may be colon-separated lists of charsets.
732  * However, if fromcode is zero then fromcodes is assumed to be the
733  * name of a single charset even if it contains a colon.
734  */
735 static size_t convert_file_from_to (FILE * file,
736                                     const char *fromcodes,
737                                     const char *tocodes, char **fromcode,
738                                     char **tocode, CONTENT * info)
739 {
740   char *fcode;
741   char **tcode;
742   const char *c, *c1;
743   size_t ret;
744   int ncodes, i, cn;
745
746   /* Count the tocodes */
747   ncodes = 0;
748   for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
749     if ((c1 = strchr (c, ':')) == c)
750       continue;
751     ++ncodes;
752   }
753
754   /* Copy them */
755   tcode = p_new(char *, ncodes);
756   for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
757     if ((c1 = strchr (c, ':')) == c)
758       continue;
759     tcode[i] = str_substrdup (c, c1);
760   }
761
762   ret = (size_t) (-1);
763   if (fromcode) {
764     /* Try each fromcode in turn */
765     for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
766       if ((c1 = strchr (c, ':')) == c)
767         continue;
768       fcode = str_substrdup (c, c1);
769
770       ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
771                              &cn, info);
772       if (ret != (size_t) (-1)) {
773         *fromcode = fcode;
774         *tocode = tcode[cn];
775         tcode[cn] = 0;
776         break;
777       }
778       p_delete(&fcode);
779     }
780   }
781   else {
782     /* There is only one fromcode */
783     ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
784                            &cn, info);
785     if (ret != (size_t) (-1)) {
786       *tocode = tcode[cn];
787       tcode[cn] = 0;
788     }
789   }
790
791   /* Free memory */
792   for (i = 0; i < ncodes; i++)
793     p_delete(&tcode[i]);
794
795   p_delete(tcode);
796
797   return ret;
798 }
799
800 /* 
801  * Analyze the contents of a file to determine which MIME encoding to use.
802  * Also set the body charset, sometimes, or not.
803  */
804 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
805 {
806   CONTENT *info;
807   CONTENT_STATE state;
808   FILE *fp = NULL;
809   char *fromcode = NULL;
810   char *tocode;
811   char buffer[100];
812   char chsbuf[STRING];
813   size_t r;
814
815   struct stat sb;
816
817   if (b && !fname)
818     fname = b->filename;
819
820   if (stat (fname, &sb) == -1) {
821     mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
822     return NULL;
823   }
824
825   if (!S_ISREG (sb.st_mode)) {
826     mutt_error (_("%s isn't a regular file."), fname);
827     return NULL;
828   }
829
830   if ((fp = fopen (fname, "r")) == NULL) {
831     debug_print (1, ("%s: %s (errno %d).\n", fname, strerror (errno), errno));
832     return (NULL);
833   }
834
835   info = p_new(CONTENT, 1);
836   memset (&state, 0, sizeof (state));
837
838   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
839     char *chs = mutt_get_parameter ("charset", b->parameter);
840     char *fchs = b->use_disp ? ((FileCharset && *FileCharset) ?
841                                 FileCharset : Charset) : Charset;
842     if (Charset && (chs || SendCharset) &&
843         convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
844                               &fromcode, &tocode, info) != (size_t) (-1)) {
845       if (!chs) {
846         mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
847         mutt_set_parameter ("charset", chsbuf, &b->parameter);
848       }
849       b->file_charset = fromcode;
850       p_delete(&tocode);
851       safe_fclose (&fp);
852       return info;
853     }
854   }
855
856   rewind (fp);
857   while ((r = fread (buffer, 1, sizeof (buffer), fp)))
858     update_content_info (info, &state, buffer, r);
859   update_content_info (info, &state, 0, 0);
860
861   safe_fclose (&fp);
862
863   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
864     mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
865                                     Charset
866                                     && !mutt_is_us_ascii (Charset) ? Charset :
867                                     "unknown-8bit"), &b->parameter);
868
869   return info;
870 }
871
872 /* Given a file with path ``s'', see if there is a registered MIME type.
873  * returns the major MIME type, and copies the subtype to ``d''.  First look
874  * for ~/.mime.types, then look in a system mime.types if we can find one.
875  * The longest match is used so that we can match `ps.gz' when `gz' also
876  * exists.
877  */
878
879 int mutt_lookup_mime_type (BODY * att, const char *path)
880 {
881   FILE *f;
882   char *p, *q, *ct;
883   char buf[LONG_STRING];
884   char subtype[STRING], xtype[STRING];
885   int count;
886   int szf, sze, cur_sze;
887   int type;
888
889   *subtype = '\0';
890   *xtype = '\0';
891   type = TYPEOTHER;
892   cur_sze = 0;
893
894   szf = m_strlen(path);
895
896   for (count = 0; count < 4; count++) {
897     /*
898      * can't use strtok() because we use it in an inner loop below, so use
899      * a switch statement here instead.
900      */
901     switch (count) {
902     case 0:
903       snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL (Homedir));
904       break;
905     case 1:
906       strfcpy (buf, SYSCONFDIR "/muttng-mime.types", sizeof (buf));
907       break;
908     case 2:
909       strfcpy (buf, PKGDATADIR "/mime.types", sizeof (buf));
910       break;
911     case 3:
912       strfcpy (buf, SYSCONFDIR "/mime.types", sizeof (buf));
913       break;
914     default:
915       debug_print (1, ("Internal error, count = %d.\n", count));
916       goto bye;                 /* shouldn't happen */
917     }
918
919     if ((f = fopen (buf, "r")) != NULL) {
920       while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
921         /* weed out any comments */
922         if ((p = strchr (buf, '#')))
923           *p = 0;
924
925         /* remove any leading space. */
926         ct = buf;
927         SKIPWS (ct);
928
929         /* position on the next field in this line */
930         if ((p = strpbrk (ct, " \t")) == NULL)
931           continue;
932         *p++ = 0;
933         SKIPWS (p);
934
935         /* cycle through the file extensions */
936         while ((p = strtok (p, " \t\n"))) {
937           sze = m_strlen(p);
938           if ((sze > cur_sze) && (szf >= sze) &&
939               (str_casecmp (path + szf - sze, p) == 0
940                || ascii_strcasecmp (path + szf - sze, p) == 0) && (szf == sze
941                                                                    || path[szf
942                                                                            -
943                                                                            sze
944                                                                            -
945                                                                            1]
946                                                                    == '.')) {
947             /* get the content-type */
948
949             if ((p = strchr (ct, '/')) == NULL) {
950               /* malformed line, just skip it. */
951               break;
952             }
953             *p++ = 0;
954
955             for (q = p; *q && !ISSPACE (*q); q++);
956
957             str_substrcpy (subtype, p, q, sizeof (subtype));
958
959             if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
960               strfcpy (xtype, ct, sizeof (xtype));
961
962             cur_sze = sze;
963           }
964           p = NULL;
965         }
966       }
967       fclose (f);
968     }
969   }
970
971 bye:
972
973   if (type != TYPEOTHER || *xtype != '\0') {
974     att->type = type;
975     str_replace (&att->subtype, subtype);
976     str_replace (&att->xtype, xtype);
977   }
978
979   return (type);
980 }
981
982 void mutt_message_to_7bit (BODY * a, FILE * fp)
983 {
984   char temp[_POSIX_PATH_MAX];
985   char *line = NULL;
986   FILE *fpin = NULL;
987   FILE *fpout = NULL;
988   struct stat sb;
989
990   if (!a->filename && fp)
991     fpin = fp;
992   else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
993     mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
994     return;
995   }
996   else {
997     a->offset = 0;
998     if (stat (a->filename, &sb) == -1) {
999       mutt_perror ("stat");
1000       fclose (fpin);
1001     }
1002     a->length = sb.st_size;
1003   }
1004
1005   mutt_mktemp (temp);
1006   if (!(fpout = safe_fopen (temp, "w+"))) {
1007     mutt_perror ("fopen");
1008     goto cleanup;
1009   }
1010
1011   fseeko (fpin, a->offset, 0);
1012   a->parts = mutt_parse_messageRFC822 (fpin, a);
1013
1014   transform_to_7bit (a->parts, fpin);
1015
1016   mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1017                  CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1018
1019   fputs ("MIME-Version: 1.0\n", fpout);
1020   mutt_write_mime_header (a->parts, fpout);
1021   fputc ('\n', fpout);
1022   mutt_write_mime_body (a->parts, fpout);
1023
1024 cleanup:
1025   p_delete(&line);
1026
1027   if (fpin && !fp)
1028     fclose (fpin);
1029   if (fpout)
1030     fclose (fpout);
1031   else
1032     return;
1033
1034   a->encoding = ENC7BIT;
1035   a->d_filename = a->filename;
1036   if (a->filename && a->unlink)
1037     unlink (a->filename);
1038   a->filename = m_strdup(temp);
1039   a->unlink = 1;
1040   if (stat (a->filename, &sb) == -1) {
1041     mutt_perror ("stat");
1042     return;
1043   }
1044   a->length = sb.st_size;
1045   mutt_free_body (&a->parts);
1046   a->hdr->content = NULL;
1047 }
1048
1049 static void transform_to_7bit (BODY * a, FILE * fpin)
1050 {
1051   char buff[_POSIX_PATH_MAX];
1052   STATE s;
1053   struct stat sb;
1054
1055   memset (&s, 0, sizeof (s));
1056   for (; a; a = a->next) {
1057     if (a->type == TYPEMULTIPART) {
1058       if (a->encoding != ENC7BIT)
1059         a->encoding = ENC7BIT;
1060
1061       transform_to_7bit (a->parts, fpin);
1062     }
1063     else if (mutt_is_message_type (a->type, a->subtype)) {
1064       mutt_message_to_7bit (a, fpin);
1065     }
1066     else {
1067       a->noconv = 1;
1068       a->force_charset = 1;
1069
1070       mutt_mktemp (buff);
1071       if ((s.fpout = safe_fopen (buff, "w")) == NULL) {
1072         mutt_perror ("fopen");
1073         return;
1074       }
1075       s.fpin = fpin;
1076       mutt_decode_attachment (a, &s);
1077       fclose (s.fpout);
1078       a->d_filename = a->filename;
1079       a->filename = m_strdup(buff);
1080       a->unlink = 1;
1081       if (stat (a->filename, &sb) == -1) {
1082         mutt_perror ("stat");
1083         return;
1084       }
1085       a->length = sb.st_size;
1086
1087       mutt_update_encoding (a);
1088       if (a->encoding == ENC8BIT)
1089         a->encoding = ENCQUOTEDPRINTABLE;
1090       else if (a->encoding == ENCBINARY)
1091         a->encoding = ENCBASE64;
1092     }
1093   }
1094 }
1095
1096 /* determine which Content-Transfer-Encoding to use */
1097 static void mutt_set_encoding (BODY * b, CONTENT * info)
1098 {
1099   char send_charset[SHORT_STRING];
1100
1101   if (b->type == TYPETEXT) {
1102     char *chsname =
1103       mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1104     if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1105         || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1106       b->encoding = ENCQUOTEDPRINTABLE;
1107     else if (info->hibin)
1108       b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1109     else
1110       b->encoding = ENC7BIT;
1111   }
1112   else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1113     if (info->lobin || info->hibin) {
1114       if (option (OPTALLOW8BIT) && !info->lobin)
1115         b->encoding = ENC8BIT;
1116       else
1117         mutt_message_to_7bit (b, NULL);
1118     }
1119     else
1120       b->encoding = ENC7BIT;
1121   }
1122   else if (b->type == TYPEAPPLICATION
1123            && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1124     b->encoding = ENC7BIT;
1125   else
1126 #if 0
1127   if (info->lobin || info->hibin || info->binary || info->linemax > 990
1128         || info->cr || ( /* option (OPTENCODEFROM) && */ info->from))
1129 #endif
1130   {
1131     /* Determine which encoding is smaller  */
1132     if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1133         3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1134       b->encoding = ENCBASE64;
1135     else
1136       b->encoding = ENCQUOTEDPRINTABLE;
1137   }
1138 #if 0
1139   else
1140     b->encoding = ENC7BIT;
1141 #endif
1142 }
1143
1144 void mutt_stamp_attachment (BODY * a)
1145 {
1146   a->stamp = time (NULL);
1147 }
1148
1149 /* Get a body's character set */
1150
1151 char *mutt_get_body_charset (char *d, size_t dlen, BODY * b)
1152 {
1153   char *p = NULL;
1154
1155   if (b && b->type != TYPETEXT)
1156     return NULL;
1157
1158   if (b)
1159     p = mutt_get_parameter ("charset", b->parameter);
1160
1161   if (p)
1162     mutt_canonical_charset (d, dlen, NONULL (p));
1163   else
1164     strfcpy (d, "us-ascii", dlen);
1165
1166   return d;
1167 }
1168
1169
1170 /* Assumes called from send mode where BODY->filename points to actual file */
1171 void mutt_update_encoding (BODY * a)
1172 {
1173   CONTENT *info;
1174   char chsbuff[STRING];
1175
1176   /* override noconv when it's us-ascii */
1177   if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1178     a->noconv = 0;
1179
1180   if (!a->force_charset && !a->noconv)
1181     mutt_delete_parameter ("charset", &a->parameter);
1182
1183   if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1184     return;
1185
1186   mutt_set_encoding (a, info);
1187   mutt_stamp_attachment (a);
1188
1189   p_delete(&a->content);
1190   a->content = info;
1191
1192 }
1193
1194 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1195 {
1196   char buffer[LONG_STRING];
1197   BODY *body;
1198   FILE *fp;
1199   int cmflags, chflags;
1200   int pgp = WithCrypto ? hdr->security : 0;
1201
1202   if (WithCrypto) {
1203     if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1204         (hdr->security & ENCRYPT)) {
1205       if (!crypt_valid_passphrase (hdr->security))
1206         return (NULL);
1207     }
1208   }
1209
1210   mutt_mktemp (buffer);
1211   if ((fp = safe_fopen (buffer, "w+")) == NULL)
1212     return NULL;
1213
1214   body = mutt_new_body ();
1215   body->type = TYPEMESSAGE;
1216   body->subtype = m_strdup("rfc822");
1217   body->filename = m_strdup(buffer);
1218   body->unlink = 1;
1219   body->use_disp = 0;
1220   body->disposition = DISPINLINE;
1221   body->noconv = 1;
1222
1223   mutt_parse_mime_message (ctx, hdr);
1224
1225   chflags = CH_XMIT;
1226   cmflags = 0;
1227
1228   /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1229   if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1230     chflags |= CH_MIME | CH_TXTPLAIN;
1231     cmflags = M_CM_DECODE | M_CM_CHARCONV;
1232     if ((WithCrypto & APPLICATION_PGP))
1233       pgp &= ~PGPENCRYPT;
1234     if ((WithCrypto & APPLICATION_SMIME))
1235       pgp &= ~SMIMEENCRYPT;
1236   }
1237   else if (WithCrypto && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1238     if ((WithCrypto & APPLICATION_PGP)
1239         && mutt_is_multipart_encrypted (hdr->content)) {
1240       chflags |= CH_MIME | CH_NONEWLINE;
1241       cmflags = M_CM_DECODE_PGP;
1242       pgp &= ~PGPENCRYPT;
1243     }
1244     else if ((WithCrypto & APPLICATION_PGP)
1245              && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT)) {
1246       chflags |= CH_MIME | CH_TXTPLAIN;
1247       cmflags = M_CM_DECODE | M_CM_CHARCONV;
1248       pgp &= ~PGPENCRYPT;
1249     }
1250     else if ((WithCrypto & APPLICATION_SMIME)
1251              && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1252       chflags |= CH_MIME | CH_TXTPLAIN;
1253       cmflags = M_CM_DECODE | M_CM_CHARCONV;
1254       pgp &= ~SMIMEENCRYPT;
1255     }
1256   }
1257
1258   mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1259
1260   fflush (fp);
1261   rewind (fp);
1262
1263   body->hdr = mutt_new_header ();
1264   body->hdr->offset = 0;
1265   /* we don't need the user headers here */
1266   body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1267   if (WithCrypto)
1268     body->hdr->security = pgp;
1269   mutt_update_encoding (body);
1270   body->parts = body->hdr->content;
1271
1272   fclose (fp);
1273
1274   return (body);
1275 }
1276
1277 BODY *mutt_make_file_attach (const char *path)
1278 {
1279   BODY *att;
1280   CONTENT *info;
1281
1282   att = mutt_new_body ();
1283   att->filename = m_strdup(path);
1284
1285   /* Attempt to determine the appropriate content-type based on the filename
1286    * suffix.
1287    */
1288
1289 #if 0
1290
1291   if ((n =
1292        mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf),
1293                               path)) != TYPEOTHER || *xbuf != '\0') {
1294     att->type = n;
1295     att->subtype = m_strdup(buf);
1296     att->xtype = m_strdup(xbuf);
1297   }
1298
1299 #else
1300
1301   mutt_lookup_mime_type (att, path);
1302
1303 #endif
1304
1305   if ((info = mutt_get_content_info (path, att)) == NULL) {
1306     mutt_free_body (&att);
1307     return NULL;
1308   }
1309
1310   if (!att->subtype) {
1311     if (info->lobin == 0
1312         || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1313       /*
1314        * Statistically speaking, there should be more than 10% "lobin" 
1315        * chars if this is really a binary file...
1316        */
1317       att->type = TYPETEXT;
1318       att->subtype = m_strdup("plain");
1319     }
1320     else {
1321       att->type = TYPEAPPLICATION;
1322       att->subtype = m_strdup("octet-stream");
1323     }
1324   }
1325
1326   mutt_update_encoding (att);
1327   return (att);
1328 }
1329
1330 static int get_toplevel_encoding (BODY * a)
1331 {
1332   int e = ENC7BIT;
1333
1334   for (; a; a = a->next) {
1335     if (a->encoding == ENCBINARY)
1336       return (ENCBINARY);
1337     else if (a->encoding == ENC8BIT)
1338       e = ENC8BIT;
1339   }
1340
1341   return (e);
1342 }
1343
1344 BODY *mutt_make_multipart (BODY * b)
1345 {
1346   BODY *new;
1347
1348   new = mutt_new_body ();
1349   new->type = TYPEMULTIPART;
1350   new->subtype = m_strdup("mixed");
1351   new->encoding = get_toplevel_encoding (b);
1352   mutt_generate_boundary (&new->parameter);
1353   new->use_disp = 0;
1354   new->disposition = DISPINLINE;
1355   new->parts = b;
1356
1357   return new;
1358 }
1359
1360 /* remove the multipart body if it exists */
1361 BODY *mutt_remove_multipart (BODY * b)
1362 {
1363   BODY *t;
1364
1365   if (b->parts) {
1366     t = b;
1367     b = b->parts;
1368     t->parts = NULL;
1369     mutt_free_body (&t);
1370   }
1371   return b;
1372 }
1373
1374 char *mutt_make_date (char *s, size_t len)
1375 {
1376   time_t t = time (NULL);
1377   struct tm *l = localtime (&t);
1378   time_t tz = mutt_local_tz (t);
1379
1380   tz /= 60;
1381
1382   snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1383             Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1384             l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1385             (int) tz / 60, (int) abs (tz) % 60);
1386   return (s);
1387 }
1388
1389 /* wrapper around mutt_write_address() so we can handle very large
1390    recipient lists without needing a huge temporary buffer in memory */
1391 void mutt_write_address_list (ADDRESS * adr, FILE * fp, int linelen,
1392                               int display)
1393 {
1394   ADDRESS *tmp;
1395   char buf[LONG_STRING];
1396   int count = 0;
1397   int len;
1398
1399   while (adr) {
1400     tmp = adr->next;
1401     adr->next = NULL;
1402     buf[0] = 0;
1403     rfc822_write_address (buf, sizeof (buf), adr, display);
1404     len = m_strlen(buf);
1405     if (count && linelen + len > 74) {
1406       fputs ("\n\t", fp);
1407       linelen = len + 8;        /* tab is usually about 8 spaces... */
1408     }
1409     else {
1410       if (count && adr->mailbox) {
1411         fputc (' ', fp);
1412         linelen++;
1413       }
1414       linelen += len;
1415     }
1416     fputs (buf, fp);
1417     adr->next = tmp;
1418     if (!adr->group && adr->next && adr->next->mailbox) {
1419       linelen++;
1420       fputc (',', fp);
1421     }
1422     adr = adr->next;
1423     count++;
1424   }
1425   fputc ('\n', fp);
1426 }
1427
1428 /* arbitrary number of elements to grow the array by */
1429 #define REF_INC 16
1430
1431 #define TrimRef 10
1432
1433 /* need to write the list in reverse because they are stored in reverse order
1434  * when parsed to speed up threading
1435  */
1436 void mutt_write_references (LIST * r, FILE * f)
1437 {
1438   LIST **ref = NULL;
1439   int refcnt = 0, refmax = 0;
1440
1441   for (; (TrimRef == 0 || refcnt < TrimRef) && r; r = r->next) {
1442     if (refcnt == refmax)
1443       p_realloc(&ref, refmax += REF_INC);
1444     ref[refcnt++] = r;
1445   }
1446
1447   while (refcnt-- > 0) {
1448     fputc (' ', f);
1449     fputs (ref[refcnt]->data, f);
1450   }
1451
1452   p_delete(&ref);
1453 }
1454
1455 /* Note: all RFC2047 encoding should be done outside of this routine, except
1456  * for the "real name."  This will allow this routine to be used more than
1457  * once, if necessary.
1458  * 
1459  * Likewise, all IDN processing should happen outside of this routine.
1460  *
1461  * mode == 1  => "lite" mode (used for edit_hdrs)
1462  * mode == 0  => normal mode.  write full header + MIME headers
1463  * mode == -1 => write just the envelope info (used for postponing messages)
1464  * 
1465  * privacy != 0 => will omit any headers which may identify the user.
1466  *               Output generated is suitable for being sent through
1467  *               anonymous remailer chains.
1468  *
1469  */
1470
1471 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1472                               int mode, int privacy)
1473 {
1474   char buffer[LONG_STRING];
1475   char *p;
1476   LIST *tmp = env->userhdrs;
1477   int has_agent = 0;            /* user defined user-agent header field exists */
1478   list2_t* hdrs = list_from_str (EditorHeaders, " ");
1479
1480 #ifdef USE_NNTP
1481   if (!option (OPTNEWSSEND))
1482 #endif
1483     if (mode == 0 && !privacy)
1484       fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1485
1486 #define EDIT_HEADER(x) (mode != 1 || option(OPTXMAILTO) || (mode == 1 && list_lookup(hdrs,(list_lookup_t*) ascii_strcasecmp,x) >= 0))
1487
1488   /* OPTUSEFROM is not consulted here so that we can still write a From:
1489    * field if the user sets it with the `my_hdr' command
1490    */
1491   if (env->from && !privacy) {
1492     buffer[0] = 0;
1493     rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1494     fprintf (fp, "From: %s\n", buffer);
1495   }
1496
1497   if (env->to) {
1498     fputs ("To: ", fp);
1499     mutt_write_address_list (env->to, fp, 4, 0);
1500   }
1501   else if (mode > 0)
1502 #ifdef USE_NNTP
1503     if (!option (OPTNEWSSEND))
1504 #endif
1505       if (EDIT_HEADER("To:"))
1506         fputs ("To:\n", fp);
1507
1508   if (env->cc) {
1509     fputs ("Cc: ", fp);
1510     mutt_write_address_list (env->cc, fp, 4, 0);
1511   }
1512   else if (mode > 0)
1513 #ifdef USE_NNTP
1514     if (!option (OPTNEWSSEND))
1515 #endif
1516       if (EDIT_HEADER("Cc:"))
1517         fputs ("Cc:\n", fp);
1518
1519   if (env->bcc) {
1520     if (mode != 0 || option (OPTWRITEBCC)) {
1521       fputs ("Bcc: ", fp);
1522       mutt_write_address_list (env->bcc, fp, 5, 0);
1523     }
1524   }
1525   else if (mode > 0)
1526 #ifdef USE_NNTP
1527     if (!option (OPTNEWSSEND))
1528 #endif
1529       if (EDIT_HEADER("Bcc:"))
1530         fputs ("Bcc:\n", fp);
1531
1532 #ifdef USE_NNTP
1533   if (env->newsgroups)
1534     fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1535   else if (mode == 1 && option (OPTNEWSSEND) && EDIT_HEADER("Newsgroups:"))
1536     fputs ("Newsgroups:\n", fp);
1537
1538   if (env->followup_to)
1539     fprintf (fp, "Followup-To: %s\n", env->followup_to);
1540   else if (mode == 1 && option (OPTNEWSSEND) && EDIT_HEADER("Followup-To:"))
1541     fputs ("Followup-To:\n", fp);
1542
1543   if (env->x_comment_to)
1544     fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1545   else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO) &&
1546            EDIT_HEADER("X-Comment-To:"))
1547     fputs ("X-Comment-To:\n", fp);
1548 #endif
1549
1550   if (env->subject)
1551     fprintf (fp, "Subject: %s\n", env->subject);
1552   else if (mode == 1 && EDIT_HEADER("Subject:"))
1553     fputs ("Subject:\n", fp);
1554
1555   /* save message id if the user has set it */
1556   if (env->message_id && !privacy)
1557     fprintf (fp, "Message-ID: %s\n", env->message_id);
1558
1559   if (env->reply_to) {
1560     fputs ("Reply-To: ", fp);
1561     mutt_write_address_list (env->reply_to, fp, 10, 0);
1562   }
1563   else if (mode > 0 && EDIT_HEADER("Reply-To:"))
1564     fputs ("Reply-To:\n", fp);
1565
1566   if (env->mail_followup_to)
1567 #ifdef USE_NNTP
1568     if (!option (OPTNEWSSEND))
1569 #endif
1570     {
1571       fputs ("Mail-Followup-To: ", fp);
1572       mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1573     }
1574
1575   if (mode <= 0) {
1576     if (env->references) {
1577       fputs ("References:", fp);
1578       mutt_write_references (env->references, fp);
1579       fputc ('\n', fp);
1580     }
1581
1582     /* Add the MIME headers */
1583     fputs ("MIME-Version: 1.0\n", fp);
1584     mutt_write_mime_header (attach, fp);
1585   }
1586
1587   if (env->in_reply_to) {
1588     fputs ("In-Reply-To:", fp);
1589     mutt_write_references (env->in_reply_to, fp);
1590     fputc ('\n', fp);
1591   }
1592
1593 #undef EDIT_HEADER
1594
1595   /* Add any user defined headers */
1596   for (; tmp; tmp = tmp->next) {
1597     if ((p = strchr (tmp->data, ':'))) {
1598       p++;
1599       SKIPWS (p);
1600       if (!*p)
1601         continue;               /* don't emit empty fields. */
1602
1603       /* check to see if the user has overridden the user-agent field */
1604       if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1605         has_agent = 1;
1606         if (privacy)
1607           continue;
1608       }
1609
1610       fputs (tmp->data, fp);
1611       fputc ('\n', fp);
1612     }
1613   }
1614
1615   if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1616     const char *os;
1617
1618     if (OperatingSystem != NULL) {
1619       os = OperatingSystem;
1620     } else {
1621       struct utsname un;
1622       os = (uname(&un) == -1) ? "UNIX" : un.sysname;
1623     }
1624     /* Add a vanity header */
1625     fprintf (fp, "User-Agent: %s (%s)\n", mutt_make_version (0), os);
1626   }
1627
1628   list_del (&hdrs, (list_del_t*)xmemfree);
1629
1630   return (ferror (fp) == 0 ? 0 : -1);
1631 }
1632
1633 static void encode_headers (LIST * h)
1634 {
1635   char *tmp;
1636   char *p;
1637   int i;
1638
1639   for (; h; h = h->next) {
1640     if (!(p = strchr (h->data, ':')))
1641       continue;
1642
1643     i = p - h->data;
1644     ++p;
1645     SKIPWS (p);
1646     tmp = m_strdup(p);
1647
1648     if (!tmp)
1649       continue;
1650
1651     rfc2047_encode_string (&tmp);
1652     p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1653
1654     sprintf (h->data + i, ": %s", NONULL (tmp));        /* __SPRINTF_CHECKED__ */
1655
1656     p_delete(&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, m_strlen(tmp));
1727         break;
1728       case 'p':
1729         snprintf (tmp, sizeof (tmp), "%u", (unsigned int) getpid ());
1730         str_ncat (buf, len, tmp, m_strlen(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, m_strlen(tmp));
1740         break;
1741       case 'R':
1742         snprintf (tmp, sizeof (tmp), "%x", (unsigned int) rand ());
1743         str_ncat (buf, len, tmp, m_strlen(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, m_strlen(tmp));
1752         break;
1753       case 'X':
1754         snprintf (tmp, sizeof (tmp), "%x", (unsigned int) now);
1755         str_ncat (buf, len, tmp, m_strlen(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) - m_strlen(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 (m_strdup(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, const 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 = m_strdup(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       p_delete(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         p_delete(tempfile);
1912       }
1913     }
1914     else {
1915       st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1916       if (SendmailWait > 0) {
1917         unlink (*tempfile);
1918         p_delete(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       p_delete(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 const char **
1948 add_args(const char **args, size_t *argslen, size_t *argsmax, 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         p_realloc(&args, *argsmax += 5);
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         p_realloc(&args, *argsmax += 5);
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 = m_strdup(cmd);
1994   }
1995   else
1996 #endif
1997     s = m_strdup(Sendmail);
1998
1999   ps = s;
2000   i = 0;
2001   while ((ps = strtok (ps, " "))) {
2002     if (argslen == argsmax)
2003       p_realloc(&args, argsmax += 5);
2004
2005     if (i)
2006       args[argslen++] = ps;
2007     else {
2008       path = m_strdup(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     p_realloc(&args, ++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   p_delete(&childout);
2078   p_delete(&path);
2079   p_delete(&s);
2080   p_delete(&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 = m_strlen(a);
2112
2113   p_realloc(&a, la + m_strlen(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 = m_strlen(s) + 3;
2142   pr = r = p_new(char, rlen);
2143   *pr++ = '"';
2144   while (*s) {
2145     if (INVALID_CHAR (*s)) {
2146       size_t o = pr - r;
2147
2148       p_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 = m_strdup(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 }