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