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