Rocco Rutte:
[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 #include "svnrev.h"
31
32 #include "lib/mem.h"
33 #include "lib/str.h"
34 #include "lib/intl.h"
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 + str_len (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 = str_dup (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       mem_free (&tmp);
323
324       tmplen = str_len (buffer) + str_len (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 = str_dup (t);
362       encode = rfc2231_encode_string (&tmp);
363       rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
364       mem_free (&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   ICONV_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 = mem_calloc (ncodes, sizeof (iconv_t));
621   score = mem_calloc (ncodes, sizeof (size_t));
622   states = mem_calloc (ncodes, sizeof (CONTENT_STATE));
623   infos = mem_calloc (ncodes, sizeof (CONTENT));
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 = 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 = 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   mem_free (&cd);
714   mem_free (&infos);
715   mem_free (&score);
716   mem_free (&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 = mem_malloc (ncodes * sizeof (char *));
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       mem_free (&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     mem_free (&tcode[i]);
794
795   mem_free (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 = mem_calloc (1, sizeof (CONTENT));
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       mem_free (&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 = str_len (path);
895
896   for (count = 0; count < 3; 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     default:
912       debug_print (1, ("Internal error, count = %d.\n", count));
913       goto bye;                 /* shouldn't happen */
914     }
915
916     if ((f = fopen (buf, "r")) != NULL) {
917       while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
918         /* weed out any comments */
919         if ((p = strchr (buf, '#')))
920           *p = 0;
921
922         /* remove any leading space. */
923         ct = buf;
924         SKIPWS (ct);
925
926         /* position on the next field in this line */
927         if ((p = strpbrk (ct, " \t")) == NULL)
928           continue;
929         *p++ = 0;
930         SKIPWS (p);
931
932         /* cycle through the file extensions */
933         while ((p = strtok (p, " \t\n"))) {
934           sze = str_len (p);
935           if ((sze > cur_sze) && (szf >= sze) &&
936               (str_casecmp (path + szf - sze, p) == 0
937                || ascii_strcasecmp (path + szf - sze, p) == 0) && (szf == sze
938                                                                    || path[szf
939                                                                            -
940                                                                            sze
941                                                                            -
942                                                                            1]
943                                                                    == '.')) {
944             /* get the content-type */
945
946             if ((p = strchr (ct, '/')) == NULL) {
947               /* malformed line, just skip it. */
948               break;
949             }
950             *p++ = 0;
951
952             for (q = p; *q && !ISSPACE (*q); q++);
953
954             str_substrcpy (subtype, p, q, sizeof (subtype));
955
956             if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
957               strfcpy (xtype, ct, sizeof (xtype));
958
959             cur_sze = sze;
960           }
961           p = NULL;
962         }
963       }
964       fclose (f);
965     }
966   }
967
968 bye:
969
970   if (type != TYPEOTHER || *xtype != '\0') {
971     att->type = type;
972     str_replace (&att->subtype, subtype);
973     str_replace (&att->xtype, xtype);
974   }
975
976   return (type);
977 }
978
979 void mutt_message_to_7bit (BODY * a, FILE * fp)
980 {
981   char temp[_POSIX_PATH_MAX];
982   char *line = NULL;
983   FILE *fpin = NULL;
984   FILE *fpout = NULL;
985   struct stat sb;
986
987   if (!a->filename && fp)
988     fpin = fp;
989   else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
990     mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
991     return;
992   }
993   else {
994     a->offset = 0;
995     if (stat (a->filename, &sb) == -1) {
996       mutt_perror ("stat");
997       fclose (fpin);
998     }
999     a->length = sb.st_size;
1000   }
1001
1002   mutt_mktemp (temp);
1003   if (!(fpout = safe_fopen (temp, "w+"))) {
1004     mutt_perror ("fopen");
1005     goto cleanup;
1006   }
1007
1008   fseek (fpin, a->offset, 0);
1009   a->parts = mutt_parse_messageRFC822 (fpin, a);
1010
1011   transform_to_7bit (a->parts, fpin);
1012
1013   mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1014                  CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1015
1016   fputs ("Mime-Version: 1.0\n", fpout);
1017   mutt_write_mime_header (a->parts, fpout);
1018   fputc ('\n', fpout);
1019   mutt_write_mime_body (a->parts, fpout);
1020
1021 cleanup:
1022   mem_free (&line);
1023
1024   if (fpin && !fp)
1025     fclose (fpin);
1026   if (fpout)
1027     fclose (fpout);
1028   else
1029     return;
1030
1031   a->encoding = ENC7BIT;
1032   a->d_filename = a->filename;
1033   if (a->filename && a->unlink)
1034     unlink (a->filename);
1035   a->filename = str_dup (temp);
1036   a->unlink = 1;
1037   if (stat (a->filename, &sb) == -1) {
1038     mutt_perror ("stat");
1039     return;
1040   }
1041   a->length = sb.st_size;
1042   mutt_free_body (&a->parts);
1043   a->hdr->content = NULL;
1044 }
1045
1046 static void transform_to_7bit (BODY * a, FILE * fpin)
1047 {
1048   char buff[_POSIX_PATH_MAX];
1049   STATE s;
1050   struct stat sb;
1051
1052   memset (&s, 0, sizeof (s));
1053   for (; a; a = a->next) {
1054     if (a->type == TYPEMULTIPART) {
1055       if (a->encoding != ENC7BIT)
1056         a->encoding = ENC7BIT;
1057
1058       transform_to_7bit (a->parts, fpin);
1059     }
1060     else if (mutt_is_message_type (a->type, a->subtype)) {
1061       mutt_message_to_7bit (a, fpin);
1062     }
1063     else {
1064       a->noconv = 1;
1065       a->force_charset = 1;
1066
1067       mutt_mktemp (buff);
1068       if ((s.fpout = safe_fopen (buff, "w")) == NULL) {
1069         mutt_perror ("fopen");
1070         return;
1071       }
1072       s.fpin = fpin;
1073       mutt_decode_attachment (a, &s);
1074       fclose (s.fpout);
1075       a->d_filename = a->filename;
1076       a->filename = str_dup (buff);
1077       a->unlink = 1;
1078       if (stat (a->filename, &sb) == -1) {
1079         mutt_perror ("stat");
1080         return;
1081       }
1082       a->length = sb.st_size;
1083
1084       mutt_update_encoding (a);
1085       if (a->encoding == ENC8BIT)
1086         a->encoding = ENCQUOTEDPRINTABLE;
1087       else if (a->encoding == ENCBINARY)
1088         a->encoding = ENCBASE64;
1089     }
1090   }
1091 }
1092
1093 /* determine which Content-Transfer-Encoding to use */
1094 static void mutt_set_encoding (BODY * b, CONTENT * info)
1095 {
1096   char send_charset[SHORT_STRING];
1097
1098   if (b->type == TYPETEXT) {
1099     char *chsname =
1100       mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1101     if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1102         || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1103       b->encoding = ENCQUOTEDPRINTABLE;
1104     else if (info->hibin)
1105       b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1106     else
1107       b->encoding = ENC7BIT;
1108   }
1109   else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1110     if (info->lobin || info->hibin) {
1111       if (option (OPTALLOW8BIT) && !info->lobin)
1112         b->encoding = ENC8BIT;
1113       else
1114         mutt_message_to_7bit (b, NULL);
1115     }
1116     else
1117       b->encoding = ENC7BIT;
1118   }
1119   else if (b->type == TYPEAPPLICATION
1120            && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1121     b->encoding = ENC7BIT;
1122   else
1123 #if 0
1124   if (info->lobin || info->hibin || info->binary || info->linemax > 990
1125         || info->cr || ( /* option (OPTENCODEFROM) && */ info->from))
1126 #endif
1127   {
1128     /* Determine which encoding is smaller  */
1129     if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1130         3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1131       b->encoding = ENCBASE64;
1132     else
1133       b->encoding = ENCQUOTEDPRINTABLE;
1134   }
1135 #if 0
1136   else
1137     b->encoding = ENC7BIT;
1138 #endif
1139 }
1140
1141 void mutt_stamp_attachment (BODY * a)
1142 {
1143   a->stamp = time (NULL);
1144 }
1145
1146 /* Get a body's character set */
1147
1148 char *mutt_get_body_charset (char *d, size_t dlen, BODY * b)
1149 {
1150   char *p = NULL;
1151
1152   if (b && b->type != TYPETEXT)
1153     return NULL;
1154
1155   if (b)
1156     p = mutt_get_parameter ("charset", b->parameter);
1157
1158   if (p)
1159     mutt_canonical_charset (d, dlen, NONULL (p));
1160   else
1161     strfcpy (d, "us-ascii", dlen);
1162
1163   return d;
1164 }
1165
1166
1167 /* Assumes called from send mode where BODY->filename points to actual file */
1168 void mutt_update_encoding (BODY * a)
1169 {
1170   CONTENT *info;
1171   char chsbuff[STRING];
1172
1173   /* override noconv when it's us-ascii */
1174   if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1175     a->noconv = 0;
1176
1177   if (!a->force_charset && !a->noconv)
1178     mutt_delete_parameter ("charset", &a->parameter);
1179
1180   if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1181     return;
1182
1183   mutt_set_encoding (a, info);
1184   mutt_stamp_attachment (a);
1185
1186   mem_free (&a->content);
1187   a->content = info;
1188
1189 }
1190
1191 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1192 {
1193   char buffer[LONG_STRING];
1194   BODY *body;
1195   FILE *fp;
1196   int cmflags, chflags;
1197   int pgp = WithCrypto ? hdr->security : 0;
1198
1199   if (WithCrypto) {
1200     if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1201         (hdr->security & ENCRYPT)) {
1202       if (!crypt_valid_passphrase (hdr->security))
1203         return (NULL);
1204     }
1205   }
1206
1207   mutt_mktemp (buffer);
1208   if ((fp = safe_fopen (buffer, "w+")) == NULL)
1209     return NULL;
1210
1211   body = mutt_new_body ();
1212   body->type = TYPEMESSAGE;
1213   body->subtype = str_dup ("rfc822");
1214   body->filename = str_dup (buffer);
1215   body->unlink = 1;
1216   body->use_disp = 0;
1217   body->disposition = DISPINLINE;
1218   body->noconv = 1;
1219
1220   mutt_parse_mime_message (ctx, hdr);
1221
1222   chflags = CH_XMIT;
1223   cmflags = 0;
1224
1225   /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1226   if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1227     chflags |= CH_MIME | CH_TXTPLAIN;
1228     cmflags = M_CM_DECODE | M_CM_CHARCONV;
1229     if ((WithCrypto & APPLICATION_PGP))
1230       pgp &= ~PGPENCRYPT;
1231     if ((WithCrypto & APPLICATION_SMIME))
1232       pgp &= ~SMIMEENCRYPT;
1233   }
1234   else if (WithCrypto && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1235     if ((WithCrypto & APPLICATION_PGP)
1236         && mutt_is_multipart_encrypted (hdr->content)) {
1237       chflags |= CH_MIME | CH_NONEWLINE;
1238       cmflags = M_CM_DECODE_PGP;
1239       pgp &= ~PGPENCRYPT;
1240     }
1241     else if ((WithCrypto & APPLICATION_PGP)
1242              && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT)) {
1243       chflags |= CH_MIME | CH_TXTPLAIN;
1244       cmflags = M_CM_DECODE | M_CM_CHARCONV;
1245       pgp &= ~PGPENCRYPT;
1246     }
1247     else if ((WithCrypto & APPLICATION_SMIME)
1248              && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1249       chflags |= CH_MIME | CH_TXTPLAIN;
1250       cmflags = M_CM_DECODE | M_CM_CHARCONV;
1251       pgp &= ~SMIMEENCRYPT;
1252     }
1253   }
1254
1255   mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1256
1257   fflush (fp);
1258   rewind (fp);
1259
1260   body->hdr = mutt_new_header ();
1261   body->hdr->offset = 0;
1262   /* we don't need the user headers here */
1263   body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1264   if (WithCrypto)
1265     body->hdr->security = pgp;
1266   mutt_update_encoding (body);
1267   body->parts = body->hdr->content;
1268
1269   fclose (fp);
1270
1271   return (body);
1272 }
1273
1274 BODY *mutt_make_file_attach (const char *path)
1275 {
1276   BODY *att;
1277   CONTENT *info;
1278
1279   att = mutt_new_body ();
1280   att->filename = str_dup (path);
1281
1282   /* Attempt to determine the appropriate content-type based on the filename
1283    * suffix.
1284    */
1285
1286 #if 0
1287
1288   if ((n =
1289        mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf),
1290                               path)) != TYPEOTHER || *xbuf != '\0') {
1291     att->type = n;
1292     att->subtype = str_dup (buf);
1293     att->xtype = str_dup (xbuf);
1294   }
1295
1296 #else
1297
1298   mutt_lookup_mime_type (att, path);
1299
1300 #endif
1301
1302   if ((info = mutt_get_content_info (path, att)) == NULL) {
1303     mutt_free_body (&att);
1304     return NULL;
1305   }
1306
1307   if (!att->subtype) {
1308     if (info->lobin == 0
1309         || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1310       /*
1311        * Statistically speaking, there should be more than 10% "lobin" 
1312        * chars if this is really a binary file...
1313        */
1314       att->type = TYPETEXT;
1315       att->subtype = str_dup ("plain");
1316     }
1317     else {
1318       att->type = TYPEAPPLICATION;
1319       att->subtype = str_dup ("octet-stream");
1320     }
1321   }
1322
1323   mutt_update_encoding (att);
1324   return (att);
1325 }
1326
1327 static int get_toplevel_encoding (BODY * a)
1328 {
1329   int e = ENC7BIT;
1330
1331   for (; a; a = a->next) {
1332     if (a->encoding == ENCBINARY)
1333       return (ENCBINARY);
1334     else if (a->encoding == ENC8BIT)
1335       e = ENC8BIT;
1336   }
1337
1338   return (e);
1339 }
1340
1341 BODY *mutt_make_multipart (BODY * b)
1342 {
1343   BODY *new;
1344
1345   new = mutt_new_body ();
1346   new->type = TYPEMULTIPART;
1347   new->subtype = str_dup ("mixed");
1348   new->encoding = get_toplevel_encoding (b);
1349   mutt_generate_boundary (&new->parameter);
1350   new->use_disp = 0;
1351   new->disposition = DISPINLINE;
1352   new->parts = b;
1353
1354   return new;
1355 }
1356
1357 /* remove the multipart body if it exists */
1358 BODY *mutt_remove_multipart (BODY * b)
1359 {
1360   BODY *t;
1361
1362   if (b->parts) {
1363     t = b;
1364     b = b->parts;
1365     t->parts = NULL;
1366     mutt_free_body (&t);
1367   }
1368   return b;
1369 }
1370
1371 char *mutt_make_date (char *s, size_t len)
1372 {
1373   time_t t = time (NULL);
1374   struct tm *l = localtime (&t);
1375   time_t tz = mutt_local_tz (t);
1376
1377   tz /= 60;
1378
1379   snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1380             Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1381             l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1382             (int) tz / 60, (int) abs (tz) % 60);
1383   return (s);
1384 }
1385
1386 /* wrapper around mutt_write_address() so we can handle very large
1387    recipient lists without needing a huge temporary buffer in memory */
1388 void mutt_write_address_list (ADDRESS * adr, FILE * fp, int linelen,
1389                               int display)
1390 {
1391   ADDRESS *tmp;
1392   char buf[LONG_STRING];
1393   int count = 0;
1394   int len;
1395
1396   while (adr) {
1397     tmp = adr->next;
1398     adr->next = NULL;
1399     buf[0] = 0;
1400     rfc822_write_address (buf, sizeof (buf), adr, display);
1401     len = str_len (buf);
1402     if (count && linelen + len > 74) {
1403       fputs ("\n\t", fp);
1404       linelen = len + 8;        /* tab is usually about 8 spaces... */
1405     }
1406     else {
1407       if (count && adr->mailbox) {
1408         fputc (' ', fp);
1409         linelen++;
1410       }
1411       linelen += len;
1412     }
1413     fputs (buf, fp);
1414     adr->next = tmp;
1415     if (!adr->group && adr->next && adr->next->mailbox) {
1416       linelen++;
1417       fputc (',', fp);
1418     }
1419     adr = adr->next;
1420     count++;
1421   }
1422   fputc ('\n', fp);
1423 }
1424
1425 /* arbitrary number of elements to grow the array by */
1426 #define REF_INC 16
1427
1428 #define TrimRef 10
1429
1430 /* need to write the list in reverse because they are stored in reverse order
1431  * when parsed to speed up threading
1432  */
1433 void mutt_write_references (LIST * r, FILE * f)
1434 {
1435   LIST **ref = NULL;
1436   int refcnt = 0, refmax = 0;
1437
1438   for (; (TrimRef == 0 || refcnt < TrimRef) && r; r = r->next) {
1439     if (refcnt == refmax)
1440       mem_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1441     ref[refcnt++] = r;
1442   }
1443
1444   while (refcnt-- > 0) {
1445     fputc (' ', f);
1446     fputs (ref[refcnt]->data, f);
1447   }
1448
1449   mem_free (&ref);
1450 }
1451
1452 /* Note: all RFC2047 encoding should be done outside of this routine, except
1453  * for the "real name."  This will allow this routine to be used more than
1454  * once, if necessary.
1455  * 
1456  * Likewise, all IDN processing should happen outside of this routine.
1457  *
1458  * mode == 1  => "lite" mode (used for edit_hdrs)
1459  * mode == 0  => normal mode.  write full header + MIME headers
1460  * mode == -1 => write just the envelope info (used for postponing messages)
1461  * 
1462  * privacy != 0 => will omit any headers which may identify the user.
1463  *               Output generated is suitable for being sent through
1464  *               anonymous remailer chains.
1465  *
1466  */
1467
1468 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1469                               int mode, int privacy)
1470 {
1471   char buffer[LONG_STRING];
1472   char *p;
1473   LIST *tmp = env->userhdrs;
1474   int has_agent = 0;            /* user defined user-agent header field exists */
1475
1476 #ifdef USE_NNTP
1477   if (!option (OPTNEWSSEND))
1478 #endif
1479     if (mode == 0 && !privacy)
1480       fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1481
1482   /* OPTUSEFROM is not consulted here so that we can still write a From:
1483    * field if the user sets it with the `my_hdr' command
1484    */
1485   if (env->from && !privacy) {
1486     buffer[0] = 0;
1487     rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1488     fprintf (fp, "From: %s\n", buffer);
1489   }
1490
1491   if (env->to) {
1492     fputs ("To: ", fp);
1493     mutt_write_address_list (env->to, fp, 4, 0);
1494   }
1495   else if (mode > 0)
1496 #ifdef USE_NNTP
1497     if (!option (OPTNEWSSEND))
1498 #endif
1499       fputs ("To: \n", fp);
1500
1501   if (env->cc) {
1502     fputs ("Cc: ", fp);
1503     mutt_write_address_list (env->cc, fp, 4, 0);
1504   }
1505   else if (mode > 0)
1506 #ifdef USE_NNTP
1507     if (!option (OPTNEWSSEND))
1508 #endif
1509       fputs ("Cc: \n", fp);
1510
1511   if (env->bcc) {
1512     if (mode != 0 || option (OPTWRITEBCC)) {
1513       fputs ("Bcc: ", fp);
1514       mutt_write_address_list (env->bcc, fp, 5, 0);
1515     }
1516   }
1517   else if (mode > 0)
1518 #ifdef USE_NNTP
1519     if (!option (OPTNEWSSEND))
1520 #endif
1521       fputs ("Bcc: \n", fp);
1522
1523 #ifdef USE_NNTP
1524   if (env->newsgroups)
1525     fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1526   else if (mode == 1 && option (OPTNEWSSEND))
1527     fputs ("Newsgroups: \n", fp);
1528
1529   if (env->followup_to)
1530     fprintf (fp, "Followup-To: %s\n", env->followup_to);
1531   else if (mode == 1 && option (OPTNEWSSEND))
1532     fputs ("Followup-To: \n", fp);
1533
1534   if (env->x_comment_to)
1535     fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1536   else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO))
1537     fputs ("X-Comment-To: \n", fp);
1538 #endif
1539
1540   if (env->subject)
1541     fprintf (fp, "Subject: %s\n", env->subject);
1542   else if (mode == 1)
1543     fputs ("Subject: \n", fp);
1544
1545   /* save message id if the user has set it */
1546   if (env->message_id && !privacy)
1547     fprintf (fp, "Message-ID: %s\n", env->message_id);
1548
1549   if (env->reply_to) {
1550     fputs ("Reply-To: ", fp);
1551     mutt_write_address_list (env->reply_to, fp, 10, 0);
1552   }
1553   else if (mode > 0)
1554     fputs ("Reply-To: \n", fp);
1555
1556   if (env->mail_followup_to)
1557 #ifdef USE_NNTP
1558     if (!option (OPTNEWSSEND))
1559 #endif
1560     {
1561       fputs ("Mail-Followup-To: ", fp);
1562       mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1563     }
1564
1565   if (mode <= 0) {
1566     if (env->references) {
1567       fputs ("References:", fp);
1568       mutt_write_references (env->references, fp);
1569       fputc ('\n', fp);
1570     }
1571
1572     /* Add the MIME headers */
1573     fputs ("Mime-Version: 1.0\n", fp);
1574     mutt_write_mime_header (attach, fp);
1575   }
1576
1577   if (env->in_reply_to) {
1578     fputs ("In-Reply-To:", fp);
1579     mutt_write_references (env->in_reply_to, fp);
1580     fputc ('\n', fp);
1581   }
1582
1583   /* Add any user defined headers */
1584   for (; tmp; tmp = tmp->next) {
1585     if ((p = strchr (tmp->data, ':'))) {
1586       p++;
1587       SKIPWS (p);
1588       if (!*p)
1589         continue;               /* don't emit empty fields. */
1590
1591       /* check to see if the user has overridden the user-agent field */
1592       if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1593         has_agent = 1;
1594         if (privacy)
1595           continue;
1596       }
1597
1598       fputs (tmp->data, fp);
1599       fputc ('\n', fp);
1600     }
1601   }
1602
1603   if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1604     struct utsname un;
1605     char *os;
1606
1607     if (OperatingSystem != NULL) {
1608       os = OperatingSystem;
1609     }
1610     else {
1611       if (uname (&un) == -1) {
1612         os = "UNIX";
1613       }
1614       else {
1615         os = un.sysname;
1616       }
1617     }
1618     /* Add a vanity header */
1619     fprintf (fp, "User-Agent: %s (%s)\n", mutt_make_version (0), os);
1620   }
1621
1622   return (ferror (fp) == 0 ? 0 : -1);
1623 }
1624
1625 static void encode_headers (LIST * h)
1626 {
1627   char *tmp;
1628   char *p;
1629   int i;
1630
1631   for (; h; h = h->next) {
1632     if (!(p = strchr (h->data, ':')))
1633       continue;
1634
1635     i = p - h->data;
1636     ++p;
1637     SKIPWS (p);
1638     tmp = str_dup (p);
1639
1640     if (!tmp)
1641       continue;
1642
1643     rfc2047_encode_string (&tmp);
1644     mem_realloc (&h->data,
1645                   str_len (h->data) + 2 + str_len (tmp) + 1);
1646
1647     sprintf (h->data + i, ": %s", NONULL (tmp));        /* __SPRINTF_CHECKED__ */
1648
1649     mem_free (&tmp);
1650   }
1651 }
1652
1653 const char *mutt_fqdn (short may_hide_host)
1654 {
1655   char *p = NULL, *q;
1656
1657   if (Fqdn && Fqdn[0] != '@') {
1658     p = Fqdn;
1659
1660     if (may_hide_host && option (OPTHIDDENHOST)) {
1661       if ((p = strchr (Fqdn, '.')))
1662         p++;
1663
1664       /* sanity check: don't hide the host if
1665        * the fqdn is something like detebe.org.
1666        */
1667
1668       if (!p || !(q = strchr (p, '.')))
1669         p = Fqdn;
1670     }
1671   }
1672
1673   return p;
1674 }
1675
1676 static char mutt_normalized_char (char c)
1677 {
1678   if (isalnum (c))
1679     return c;
1680   if (strchr (".!#$%&'*+-/=?^_`{|}~", c))
1681     return c;
1682   return '.';                   /* normalized character (we're stricter than RFC2822, 3.6.4) */
1683 }
1684
1685 static void mutt_gen_localpart (char *buf, unsigned int len, char *fmt)
1686 {
1687   time_t now;
1688   struct tm *tm;
1689   char tmp[SHORT_STRING];
1690
1691   *buf = '\0';
1692
1693   now = time (NULL);
1694   tm = gmtime (&now);
1695
1696   for (; *fmt; ++fmt) {
1697     if (*fmt == '%') {
1698       switch (fmt[1]) {
1699       case 0:
1700         return;
1701       case 'd':
1702         snprintf (tmp, sizeof (tmp), "%02d", tm->tm_mday);
1703         str_ncat (buf, len, tmp, 2);
1704         break;
1705       case 'h':
1706         snprintf (tmp, sizeof (tmp), "%02d", tm->tm_hour);
1707         str_ncat (buf, len, tmp, 2);
1708         break;
1709       case 'm':
1710         snprintf (tmp, sizeof (tmp), "%02d", tm->tm_mon + 1);
1711         str_ncat (buf, len, tmp, 2);
1712         break;
1713       case 'M':
1714         snprintf (tmp, sizeof (tmp), "%02d", tm->tm_min);
1715         str_ncat (buf, len, tmp, 2);
1716         break;
1717       case 'O':
1718         snprintf (tmp, sizeof (tmp), "%lo", (unsigned long) now);
1719         str_ncat (buf, len, tmp, str_len (tmp));
1720         break;
1721       case 'p':
1722         snprintf (tmp, sizeof (tmp), "%u", (unsigned int) getpid ());
1723         str_ncat (buf, len, tmp, str_len (tmp));
1724         break;
1725       case 'P':
1726         snprintf (tmp, sizeof (tmp), "%c", MsgIdPfx);
1727         MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1728         str_ncat (buf, len, tmp, 1);
1729         break;
1730       case 'r':
1731         snprintf (tmp, sizeof (tmp), "%u", (unsigned int) rand ());
1732         str_ncat (buf, len, tmp, str_len (tmp));
1733         break;
1734       case 'R':
1735         snprintf (tmp, sizeof (tmp), "%x", (unsigned int) rand ());
1736         str_ncat (buf, len, tmp, str_len (tmp));
1737         break;
1738       case 's':
1739         snprintf (tmp, sizeof (tmp), "%02d", tm->tm_sec);
1740         str_ncat (buf, len, tmp, 2);
1741         break;
1742       case 'T':
1743         snprintf (tmp, sizeof (tmp), "%u", (unsigned int) now);
1744         str_ncat (buf, len, tmp, str_len (tmp));
1745         break;
1746       case 'X':
1747         snprintf (tmp, sizeof (tmp), "%x", (unsigned int) now);
1748         str_ncat (buf, len, tmp, str_len (tmp));
1749         break;
1750       case 'Y':
1751         snprintf (tmp, sizeof (tmp), "%04d", tm->tm_year + 1900);       /* this will break in the year 10000 ;-) */
1752         str_ncat (buf, len, tmp, 4);
1753         break;
1754       case '%':
1755         str_ncat (buf, len, "%", 1);
1756         break;
1757       default:
1758         str_ncat (buf, len, ".", 1);        /* invalid formats are replaced by '.' */
1759       }                         /* switch */
1760       ++fmt;
1761     }
1762     else {
1763       char c;
1764
1765       c = mutt_normalized_char (*fmt);  /* @todo: filter out invalid characters */
1766       str_ncat (buf, len, &c, 1);
1767     }
1768   }
1769 }
1770
1771 char *mutt_gen_msgid (void)
1772 {
1773   char buf[SHORT_STRING];
1774   char localpart[SHORT_STRING];
1775   unsigned int localpart_length;
1776   const char *fqdn;
1777
1778   if (!(fqdn = mutt_fqdn (0)))
1779     fqdn = NONULL (Hostname);
1780
1781   localpart_length = sizeof (buf) - str_len (fqdn) - 4;  /* the 4 characters are '<', '@', '>' and '\0' */
1782
1783   mutt_gen_localpart (localpart, localpart_length, MsgIdFormat);
1784
1785   snprintf (buf, sizeof (buf), "<%s@%s>", localpart, fqdn);
1786   return (str_dup (buf));
1787 }
1788
1789 static RETSIGTYPE alarm_handler (int sig)
1790 {
1791   SigAlrm = 1;
1792 }
1793
1794 /* invoke sendmail in a subshell
1795    path (in)            path to program to execute
1796    args (in)            arguments to pass to program
1797    msg (in)             temp file containing message to send
1798    tempfile (out)       if sendmail is put in the background, this points
1799                         to the temporary file containing the stdout of the
1800                         child process */
1801 static int
1802 send_msg (const char *path, char **args, const char *msg, char **tempfile)
1803 {
1804   sigset_t set;
1805   int fd, st;
1806   pid_t pid, ppid;
1807
1808   mutt_block_signals_system ();
1809
1810   sigemptyset (&set);
1811   /* we also don't want to be stopped right now */
1812   sigaddset (&set, SIGTSTP);
1813   sigprocmask (SIG_BLOCK, &set, NULL);
1814
1815   if (SendmailWait >= 0) {
1816     char tmp[_POSIX_PATH_MAX];
1817
1818     mutt_mktemp (tmp);
1819     *tempfile = str_dup (tmp);
1820   }
1821
1822   if ((pid = fork ()) == 0) {
1823     struct sigaction act, oldalrm;
1824
1825     /* save parent's ID before setsid() */
1826     ppid = getppid ();
1827
1828     /* we want the delivery to continue even after the main process dies,
1829      * so we put ourselves into another session right away
1830      */
1831     setsid ();
1832
1833     /* next we close all open files */
1834 #if defined(OPEN_MAX)
1835     for (fd = 0; fd < OPEN_MAX; fd++)
1836       close (fd);
1837 #elif defined(_POSIX_OPEN_MAX)
1838     for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1839       close (fd);
1840 #else
1841     close (0);
1842     close (1);
1843     close (2);
1844 #endif
1845
1846     /* now the second fork() */
1847     if ((pid = fork ()) == 0) {
1848       /* "msg" will be opened as stdin */
1849       if (open (msg, O_RDONLY, 0) < 0) {
1850         unlink (msg);
1851         _exit (S_ERR);
1852       }
1853       unlink (msg);
1854
1855       if (SendmailWait >= 0) {
1856         /* *tempfile will be opened as stdout */
1857         if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1858             0)
1859           _exit (S_ERR);
1860         /* redirect stderr to *tempfile too */
1861         if (dup (1) < 0)
1862           _exit (S_ERR);
1863       }
1864       else {
1865         if (open ("/dev/null", O_WRONLY | O_APPEND) < 0)        /* stdout */
1866           _exit (S_ERR);
1867         if (open ("/dev/null", O_RDWR | O_APPEND) < 0)  /* stderr */
1868           _exit (S_ERR);
1869       }
1870
1871       execv (path, args);
1872       _exit (S_ERR);
1873     }
1874     else if (pid == -1) {
1875       unlink (msg);
1876       mem_free (tempfile);
1877       _exit (S_ERR);
1878     }
1879
1880     /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1881      * SendmailWait = 0: wait forever
1882      * SendmailWait < 0: don't wait
1883      */
1884     if (SendmailWait > 0) {
1885       SigAlrm = 0;
1886       act.sa_handler = alarm_handler;
1887 #ifdef SA_INTERRUPT
1888       /* need to make sure waitpid() is interrupted on SIGALRM */
1889       act.sa_flags = SA_INTERRUPT;
1890 #else
1891       act.sa_flags = 0;
1892 #endif
1893       sigemptyset (&act.sa_mask);
1894       sigaction (SIGALRM, &act, &oldalrm);
1895       alarm (SendmailWait);
1896     }
1897     else if (SendmailWait < 0)
1898       _exit (0xff & EX_OK);
1899
1900     if (waitpid (pid, &st, 0) > 0) {
1901       st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1902       if (SendmailWait && st == (0xff & EX_OK)) {
1903         unlink (*tempfile);     /* no longer needed */
1904         mem_free (tempfile);
1905       }
1906     }
1907     else {
1908       st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1909       if (SendmailWait > 0) {
1910         unlink (*tempfile);
1911         mem_free (tempfile);
1912       }
1913     }
1914
1915     /* reset alarm; not really needed, but... */
1916     alarm (0);
1917     sigaction (SIGALRM, &oldalrm, NULL);
1918
1919     if (kill (ppid, 0) == -1 && errno == ESRCH) {
1920       /* the parent is already dead */
1921       unlink (*tempfile);
1922       mem_free (tempfile);
1923     }
1924
1925     _exit (st);
1926   }
1927
1928   sigprocmask (SIG_UNBLOCK, &set, NULL);
1929
1930   if (pid != -1 && waitpid (pid, &st, 0) > 0)
1931     st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;     /* return child status */
1932   else
1933     st = S_ERR;                 /* error */
1934
1935   mutt_unblock_signals_system (1);
1936
1937   return (st);
1938 }
1939
1940 static char **add_args (char **args, size_t * argslen, size_t * argsmax,
1941                         ADDRESS * addr)
1942 {
1943   for (; addr; addr = addr->next) {
1944     /* weed out group mailboxes, since those are for display only */
1945     if (addr->mailbox && !addr->group) {
1946       if (*argslen == *argsmax)
1947         mem_realloc (&args, (*argsmax += 5) * sizeof (char *));
1948       args[(*argslen)++] = addr->mailbox;
1949     }
1950   }
1951   return (args);
1952 }
1953
1954 static char **add_option (char **args, size_t * argslen, size_t * argsmax,
1955                           char *s)
1956 {
1957   if (*argslen == *argsmax)
1958     mem_realloc (&args, (*argsmax += 5) * sizeof (char *));
1959   args[(*argslen)++] = s;
1960   return (args);
1961 }
1962
1963 static int mutt_invoke_sendmail (ADDRESS * from,        /* the sender */
1964                                  ADDRESS * to, ADDRESS * cc, ADDRESS * bcc,     /* recips */
1965                                  const char *msg,       /* file containing message */
1966                                  int eightbit)
1967 {                               /* message contains 8bit chars */
1968   char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
1969   char **args = NULL;
1970   size_t argslen = 0, argsmax = 0;
1971   int i;
1972
1973 #ifdef USE_NNTP
1974   if (option (OPTNEWSSEND)) {
1975     char cmd[LONG_STRING];
1976
1977     mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0,
1978                        0);
1979     if (!*cmd) {
1980       i = nntp_post (msg);
1981       unlink (msg);
1982       return i;
1983     }
1984
1985     s = str_dup (cmd);
1986   }
1987   else
1988 #endif
1989     s = str_dup (Sendmail);
1990
1991   ps = s;
1992   i = 0;
1993   while ((ps = strtok (ps, " "))) {
1994     if (argslen == argsmax)
1995       mem_realloc (&args, sizeof (char *) * (argsmax += 5));
1996
1997     if (i)
1998       args[argslen++] = ps;
1999     else {
2000       path = str_dup (ps);
2001       ps = strrchr (ps, '/');
2002       if (ps)
2003         ps++;
2004       else
2005         ps = path;
2006       args[argslen++] = ps;
2007     }
2008     ps = NULL;
2009     i++;
2010   }
2011
2012 #ifdef USE_NNTP
2013   if (!option (OPTNEWSSEND)) {
2014 #endif
2015     if (eightbit && option (OPTUSE8BITMIME))
2016       args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2017
2018     if (option (OPTENVFROM) && from && !from->next) {
2019       args = add_option (args, &argslen, &argsmax, "-f");
2020       args = add_args (args, &argslen, &argsmax, from);
2021     }
2022     if (DsnNotify) {
2023       args = add_option (args, &argslen, &argsmax, "-N");
2024       args = add_option (args, &argslen, &argsmax, DsnNotify);
2025     }
2026     if (DsnReturn) {
2027       args = add_option (args, &argslen, &argsmax, "-R");
2028       args = add_option (args, &argslen, &argsmax, DsnReturn);
2029     }
2030     args = add_option (args, &argslen, &argsmax, "--");
2031     args = add_args (args, &argslen, &argsmax, to);
2032     args = add_args (args, &argslen, &argsmax, cc);
2033     args = add_args (args, &argslen, &argsmax, bcc);
2034 #ifdef USE_NNTP
2035   }
2036 #endif
2037
2038   if (argslen == argsmax)
2039     mem_realloc (&args, sizeof (char *) * (++argsmax));
2040
2041   args[argslen++] = NULL;
2042
2043   if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
2044     if (i != S_BKG) {
2045       const char *e = mutt_strsysexit (i);
2046
2047       e = mutt_strsysexit (i);
2048       mutt_error (_("Error sending message, child exited %d (%s)."), i,
2049                   NONULL (e));
2050       if (childout) {
2051         struct stat st;
2052
2053         if (stat (childout, &st) == 0 && st.st_size > 0)
2054           mutt_do_pager (_("Output of the delivery process"), childout, 0,
2055                          NULL);
2056       }
2057     }
2058   }
2059   else
2060     unlink (childout);
2061
2062   mem_free (&childout);
2063   mem_free (&path);
2064   mem_free (&s);
2065   mem_free (&args);
2066
2067   if (i == (EX_OK & 0xff))
2068     i = 0;
2069   else if (i == S_BKG)
2070     i = 1;
2071   else
2072     i = -1;
2073   return (i);
2074 }
2075
2076 int mutt_invoke_mta (ADDRESS * from,    /* the sender */
2077                      ADDRESS * to, ADDRESS * cc, ADDRESS * bcc, /* recips */
2078                      const char *msg,   /* file containing message */
2079                      int eightbit)
2080 {                               /* message contains 8bit chars */
2081 #ifdef USE_LIBESMTP
2082   if (SmtpHost)
2083     return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
2084 #endif
2085
2086   return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
2087 }
2088
2089 /* appends string 'b' to string 'a', and returns the pointer to the new
2090    string. */
2091 char *mutt_append_string (char *a, const char *b)
2092 {
2093   size_t la = str_len (a);
2094
2095   mem_realloc (&a, la + str_len (b) + 1);
2096   strcpy (a + la, b);           /* __STRCPY_CHECKED__ */
2097   return (a);
2098 }
2099
2100 /* returns 1 if char `c' needs to be quoted to protect from shell
2101    interpretation when executing commands in a subshell */
2102 #define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
2103
2104 /* returns 1 if string `s' contains characters which could cause problems
2105    when used on a command line to execute a command */
2106 int mutt_needs_quote (const char *s)
2107 {
2108   while (*s) {
2109     if (INVALID_CHAR (*s))
2110       return 1;
2111     s++;
2112   }
2113   return 0;
2114 }
2115
2116 /* Quote a string to prevent shell escapes when this string is used on the
2117    command line to send mail. */
2118 char *mutt_quote_string (const char *s)
2119 {
2120   char *r, *pr;
2121   size_t rlen;
2122
2123   rlen = str_len (s) + 3;
2124   pr = r = (char *) mem_malloc (rlen);
2125   *pr++ = '"';
2126   while (*s) {
2127     if (INVALID_CHAR (*s)) {
2128       size_t o = pr - r;
2129
2130       mem_realloc (&r, ++rlen);
2131       pr = r + o;
2132       *pr++ = '\\';
2133     }
2134     *pr++ = *s++;
2135   }
2136   *pr++ = '"';
2137   *pr = 0;
2138   return (r);
2139 }
2140
2141 /* For postponing (!final) do the necessary encodings only */
2142 void mutt_prepare_envelope (ENVELOPE * env, int final)
2143 {
2144   char buffer[LONG_STRING];
2145
2146   if (final) {
2147     if (env->bcc && !(env->to || env->cc)) {
2148       /* some MTA's will put an Apparently-To: header field showing the Bcc:
2149        * recipients if there is no To: or Cc: field, so attempt to suppress
2150        * it by using an empty To: field.
2151        */
2152       env->to = rfc822_new_address ();
2153       env->to->group = 1;
2154       env->to->next = rfc822_new_address ();
2155
2156       buffer[0] = 0;
2157       rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2158                   RFC822Specials);
2159
2160       env->to->mailbox = str_dup (buffer);
2161     }
2162
2163     mutt_set_followup_to (env);
2164
2165     if (!env->message_id && MsgIdFormat && *MsgIdFormat)
2166       env->message_id = mutt_gen_msgid ();
2167   }
2168
2169   /* Take care of 8-bit => 7-bit conversion. */
2170   rfc2047_encode_adrlist (env->to, "To");
2171   rfc2047_encode_adrlist (env->cc, "Cc");
2172   rfc2047_encode_adrlist (env->bcc, "Bcc");
2173   rfc2047_encode_adrlist (env->from, "From");
2174   rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2175   rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2176
2177   if (env->subject)
2178 #ifdef USE_NNTP
2179     if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2180 #endif
2181     {
2182       rfc2047_encode_string (&env->subject);
2183     }
2184   encode_headers (env->userhdrs);
2185 }
2186
2187 void mutt_unprepare_envelope (ENVELOPE * env)
2188 {
2189   LIST *item;
2190
2191   for (item = env->userhdrs; item; item = item->next)
2192     rfc2047_decode (&item->data);
2193
2194   rfc822_free_address (&env->mail_followup_to);
2195
2196   /* back conversions */
2197   rfc2047_decode_adrlist (env->to);
2198   rfc2047_decode_adrlist (env->cc);
2199   rfc2047_decode_adrlist (env->bcc);
2200   rfc2047_decode_adrlist (env->from);
2201   rfc2047_decode_adrlist (env->reply_to);
2202   rfc2047_decode (&env->subject);
2203 }
2204
2205 static int _mutt_bounce_message (FILE * fp, HEADER * h, ADDRESS * to,
2206                                  const char *resent_from, ADDRESS * env_from)
2207 {
2208   int i, ret = 0;
2209   FILE *f;
2210   char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2211   MESSAGE *msg = NULL;
2212
2213   if (!h) {
2214     /* Try to bounce each message out, aborting if we get any failures. */
2215     for (i = 0; i < Context->msgcount; i++)
2216       if (Context->hdrs[i]->tagged)
2217         ret |=
2218           _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2219                                 env_from);
2220     return ret;
2221   }
2222
2223   /* If we failed to open a message, return with error */
2224   if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2225     return -1;
2226
2227   if (!fp)
2228     fp = msg->fp;
2229
2230   mutt_mktemp (tempfile);
2231   if ((f = safe_fopen (tempfile, "w")) != NULL) {
2232     int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2233
2234     if (!option (OPTBOUNCEDELIVERED))
2235       ch_flags |= CH_WEED_DELIVERED;
2236
2237     fseek (fp, h->offset, 0);
2238     fprintf (f, "Resent-From: %s", resent_from);
2239     fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2240     if (MsgIdFormat && *MsgIdFormat)
2241       fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid ());
2242     fputs ("Resent-To: ", f);
2243     mutt_write_address_list (to, f, 11, 0);
2244     mutt_copy_header (fp, h, f, ch_flags, NULL);
2245     fputc ('\n', f);
2246     mutt_copy_bytes (fp, f, h->content->length);
2247     fclose (f);
2248
2249     ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2250                            h->content->encoding == ENC8BIT);
2251   }
2252
2253   if (msg)
2254     mx_close_message (&msg);
2255
2256   return ret;
2257 }
2258
2259 int mutt_bounce_message (FILE * fp, HEADER * h, ADDRESS * to)
2260 {
2261   ADDRESS *from;
2262   const char *fqdn = mutt_fqdn (1);
2263   char resent_from[STRING];
2264   int ret;
2265   char *err;
2266
2267   resent_from[0] = '\0';
2268   from = mutt_default_from ();
2269
2270   if (fqdn)
2271     rfc822_qualify (from, fqdn);
2272
2273   rfc2047_encode_adrlist (from, "Resent-From");
2274   if (mutt_addrlist_to_idna (from, &err)) {
2275     mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2276     return -1;
2277   }
2278   rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2279
2280 #ifdef USE_NNTP
2281   unset_option (OPTNEWSSEND);
2282 #endif
2283
2284   ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2285
2286   rfc822_free_address (&from);
2287
2288   return ret;
2289 }
2290
2291
2292 /* given a list of addresses, return a list of unique addresses */
2293 ADDRESS *mutt_remove_duplicates (ADDRESS * addr)
2294 {
2295   ADDRESS *top = addr;
2296   ADDRESS **last = &top;
2297   ADDRESS *tmp;
2298   int dup;
2299
2300   while (addr) {
2301     for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next) {
2302       if (tmp->mailbox && addr->mailbox &&
2303           !ascii_strcasecmp (addr->mailbox, tmp->mailbox)) {
2304         dup = 1;
2305         break;
2306       }
2307     }
2308
2309     if (dup) {
2310       debug_print (2, ("Removing %s\n", addr->mailbox));
2311
2312       *last = addr->next;
2313
2314       addr->next = NULL;
2315       rfc822_free_address (&addr);
2316
2317       addr = *last;
2318     }
2319     else {
2320       last = &addr->next;
2321       addr = addr->next;
2322     }
2323   }
2324
2325   return (top);
2326 }
2327
2328 static void set_noconv_flags (BODY * b, short flag)
2329 {
2330   for (; b; b = b->next) {
2331     if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2332       set_noconv_flags (b->parts, flag);
2333     else if (b->type == TYPETEXT && b->noconv) {
2334       if (flag)
2335         mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2336       else
2337         mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2338     }
2339   }
2340 }
2341
2342 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2343                     int post, char *fcc)
2344 {
2345   CONTEXT f;
2346   MESSAGE *msg;
2347   char tempfile[_POSIX_PATH_MAX];
2348   FILE *tempfp = NULL;
2349   int r;
2350
2351   if (post)
2352     set_noconv_flags (hdr->content, 1);
2353
2354   if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2355     debug_print (1, ("unable to open mailbox %s in append-mode, aborting.\n", path));
2356     return (-1);
2357   }
2358
2359   /* We need to add a Content-Length field to avoid problems where a line in
2360    * the message body begins with "From "   
2361    */
2362   if (f.magic == M_MMDF || f.magic == M_MBOX) {
2363     mutt_mktemp (tempfile);
2364     if ((tempfp = safe_fopen (tempfile, "w+")) == NULL) {
2365       mutt_perror (tempfile);
2366       mx_close_mailbox (&f, NULL);
2367       return (-1);
2368     }
2369   }
2370
2371   hdr->read = !post;            /* make sure to put it in the `cur' directory (maildir) */
2372   if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2373     mx_close_mailbox (&f, NULL);
2374     return (-1);
2375   }
2376
2377   /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2378    * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header() 
2379    * */
2380   mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0,
2381                             0);
2382
2383   /* (postponment) if this was a reply of some sort, <msgid> contians the
2384    * Message-ID: of message replied to.  Save it using a special X-Mutt-
2385    * header so it can be picked up if the message is recalled at a later
2386    * point in time.  This will allow the message to be marked as replied if
2387    * the same mailbox is still open.
2388    */
2389   if (post && msgid)
2390     fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2391
2392   /* (postponment) save the Fcc: using a special X-Mutt- header so that
2393    * it can be picked up when the message is recalled 
2394    */
2395   if (post && fcc)
2396     fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2397   fprintf (msg->fp, "Status: RO\n");
2398
2399
2400
2401   /* (postponment) if the mail is to be signed or encrypted, save this info */
2402   if ((WithCrypto & APPLICATION_PGP)
2403       && post && (hdr->security & APPLICATION_PGP)) {
2404     fputs ("X-Mutt-PGP: ", msg->fp);
2405     if (hdr->security & ENCRYPT)
2406       fputc ('E', msg->fp);
2407     if (hdr->security & SIGN) {
2408       fputc ('S', msg->fp);
2409       if (PgpSignAs && *PgpSignAs)
2410         fprintf (msg->fp, "<%s>", PgpSignAs);
2411     }
2412     if (hdr->security & INLINE)
2413       fputc ('I', msg->fp);
2414     fputc ('\n', msg->fp);
2415   }
2416
2417   /* (postponment) if the mail is to be signed or encrypted, save this info */
2418   if ((WithCrypto & APPLICATION_SMIME)
2419       && post && (hdr->security & APPLICATION_SMIME)) {
2420     fputs ("X-Mutt-SMIME: ", msg->fp);
2421     if (hdr->security & ENCRYPT) {
2422       fputc ('E', msg->fp);
2423       if (SmimeCryptAlg && *SmimeCryptAlg)
2424         fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2425     }
2426     if (hdr->security & SIGN) {
2427       fputc ('S', msg->fp);
2428       if (SmimeDefaultKey && *SmimeDefaultKey)
2429         fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2430     }
2431     if (hdr->security & INLINE)
2432       fputc ('I', msg->fp);
2433     fputc ('\n', msg->fp);
2434   }
2435
2436 #ifdef MIXMASTER
2437   /* (postponement) if the mail is to be sent through a mixmaster 
2438    * chain, save that information
2439    */
2440
2441   if (post && hdr->chain && hdr->chain) {
2442     LIST *p;
2443
2444     fputs ("X-Mutt-Mix:", msg->fp);
2445     for (p = hdr->chain; p; p = p->next)
2446       fprintf (msg->fp, " %s", (char *) p->data);
2447
2448     fputc ('\n', msg->fp);
2449   }
2450 #endif
2451
2452   if (tempfp) {
2453     char sasha[LONG_STRING];
2454     int lines = 0;
2455
2456     mutt_write_mime_body (hdr->content, tempfp);
2457
2458     /* make sure the last line ends with a newline.  Emacs doesn't ensure
2459      * this will happen, and it can cause problems parsing the mailbox   
2460      * later.
2461      */
2462     fseek (tempfp, -1, 2);
2463     if (fgetc (tempfp) != '\n') {
2464       fseek (tempfp, 0, 2);
2465       fputc ('\n', tempfp);
2466     }
2467
2468     fflush (tempfp);
2469     if (ferror (tempfp)) {
2470       debug_print (1, ("%s: write failed.\n", tempfile));
2471       fclose (tempfp);
2472       unlink (tempfile);
2473       mx_commit_message (msg, &f);      /* XXX - really? */
2474       mx_close_message (&msg);
2475       mx_close_mailbox (&f, NULL);
2476       return -1;
2477     }
2478
2479     /* count the number of lines */
2480     rewind (tempfp);
2481     while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2482       lines++;
2483     fprintf (msg->fp, "Content-Length: %ld\n", (long) ftell (tempfp));
2484     fprintf (msg->fp, "Lines: %d\n\n", lines);
2485
2486     /* copy the body and clean up */
2487     rewind (tempfp);
2488     r = mutt_copy_stream (tempfp, msg->fp);
2489     if (fclose (tempfp) != 0)
2490       r = -1;
2491     /* if there was an error, leave the temp version */
2492     if (!r)
2493       unlink (tempfile);
2494   }
2495   else {
2496     fputc ('\n', msg->fp);      /* finish off the header */
2497     r = mutt_write_mime_body (hdr->content, msg->fp);
2498   }
2499
2500   if (mx_commit_message (msg, &f) != 0)
2501     r = -1;
2502   mx_close_message (&msg);
2503   mx_close_mailbox (&f, NULL);
2504
2505   if (post)
2506     set_noconv_flags (hdr->content, 0);
2507
2508   return r;
2509 }