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