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