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