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