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