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