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