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