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