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