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