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