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