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