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