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