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