Andreas Krennmair:
[apps/madmutt.git] / sendlib.c
1 /*
2  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
3  * 
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  * 
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  * 
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
17  */ 
18
19 #define _SENDLIB_C 1
20
21 #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, a->file_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 *fromcode;
913   char *tocode;
914   char buffer[100];
915   char chsbuf[STRING];
916   size_t r;
917
918   struct stat sb;
919   
920   if(b && !fname) fname = b->filename;
921
922   if (stat (fname, &sb) == -1)
923   {
924     mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
925     return NULL;
926   }
927   
928   if (!S_ISREG(sb.st_mode))
929   {
930     mutt_error (_("%s isn't a regular file."), fname);
931     return NULL;
932   }
933   
934   if ((fp = fopen (fname, "r")) == NULL)
935   {
936     dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
937                 fname, strerror (errno), errno));
938     return (NULL);
939   }
940
941   info = safe_calloc (1, sizeof (CONTENT));
942   memset (&state, 0, sizeof (state));
943   
944   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
945   {
946     char *chs = mutt_get_parameter ("charset", b->parameter);
947     char *fchs = b->use_disp ? ((FileCharset && *FileCharset) ?
948                                 FileCharset : Charset) : Charset;
949     if (Charset && (chs || SendCharset) &&
950         convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
951                               &fromcode, &tocode, info) != (size_t)(-1))
952     {
953       if (!chs)
954       {
955         mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
956         mutt_set_parameter ("charset", chsbuf, &b->parameter);
957       }
958       b->file_charset = fromcode;
959       FREE (&tocode);
960       safe_fclose (&fp);
961       return info;
962     }
963   }
964
965   rewind (fp);
966   while ((r = fread (buffer, 1, sizeof(buffer), fp)))
967     update_content_info (info, &state, buffer, r);
968   update_content_info (info, &state, 0, 0);
969
970   safe_fclose (&fp);
971   
972   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
973     mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
974                                     Charset  && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
975                         &b->parameter);
976
977   return info;
978 }
979
980 /* Given a file with path ``s'', see if there is a registered MIME type.
981  * returns the major MIME type, and copies the subtype to ``d''.  First look
982  * for ~/.mime.types, then look in a system mime.types if we can find one.
983  * The longest match is used so that we can match `ps.gz' when `gz' also
984  * exists.
985  */
986
987 int mutt_lookup_mime_type (BODY *att, const char *path)
988 {
989   FILE *f;
990   char *p, *q, *ct;
991   char buf[LONG_STRING];
992   char subtype[STRING], xtype[STRING];
993   int count;
994   int szf, sze, cur_sze;
995   int type;
996
997   *subtype = '\0';
998   *xtype   = '\0';
999   type     = TYPEOTHER;
1000   cur_sze  = 0;
1001
1002   szf      = mutt_strlen (path);
1003
1004   for (count = 0 ; count < 3 ; count++)
1005   {
1006     /*
1007      * can't use strtok() because we use it in an inner loop below, so use
1008      * a switch statement here instead.
1009      */
1010     switch (count)
1011     {
1012       case 0:
1013         snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
1014         break;
1015       case 1:
1016         strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
1017         break;
1018       case 2:
1019         strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
1020         break;
1021       default:
1022         dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
1023         goto bye;       /* shouldn't happen */
1024     }
1025
1026     if ((f = fopen (buf, "r")) != NULL)
1027     {
1028       while (fgets (buf, sizeof (buf) - 1, f) != NULL)
1029       {
1030         /* weed out any comments */
1031         if ((p = strchr (buf, '#')))
1032           *p = 0;
1033
1034         /* remove any leading space. */
1035         ct = buf;
1036         SKIPWS (ct);
1037
1038         /* position on the next field in this line */
1039         if ((p = strpbrk (ct, " \t")) == NULL)
1040           continue;
1041         *p++ = 0;
1042         SKIPWS (p);
1043
1044         /* cycle through the file extensions */
1045         while ((p = strtok (p, " \t\n")))
1046         {
1047           sze = mutt_strlen (p);
1048           if ((sze > cur_sze) && (szf >= sze) &&
1049               (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
1050               (szf == sze || path[szf - sze - 1] == '.'))
1051           {
1052             /* get the content-type */
1053
1054             if ((p = strchr (ct, '/')) == NULL)
1055             {
1056               /* malformed line, just skip it. */
1057               break;
1058             }
1059             *p++ = 0;
1060
1061             for (q = p; *q && !ISSPACE (*q); q++)
1062               ;
1063             
1064             mutt_substrcpy (subtype, p, q, sizeof (subtype));
1065
1066             if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1067               strfcpy (xtype, ct, sizeof (xtype));
1068
1069             cur_sze = sze;
1070           }
1071           p = NULL;
1072         }
1073       }
1074       fclose (f);
1075     }
1076   }
1077   
1078  bye:
1079
1080   if (type != TYPEOTHER || *xtype != '\0')
1081   {
1082     att->type = type;
1083     mutt_str_replace (&att->subtype, subtype);
1084     mutt_str_replace (&att->xtype, xtype);
1085   }
1086   
1087   return (type);
1088 }
1089
1090 void mutt_message_to_7bit (BODY *a, FILE *fp)
1091 {
1092   char temp[_POSIX_PATH_MAX];
1093   char *line = NULL;
1094   FILE *fpin = NULL;
1095   FILE *fpout = NULL;
1096   struct stat sb;
1097
1098   if (!a->filename && fp)
1099     fpin = fp;
1100   else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1101   {
1102     mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1103     return;
1104   } 
1105   else
1106   {
1107     a->offset = 0;
1108     if (stat (a->filename, &sb) == -1)
1109     {
1110       mutt_perror ("stat");
1111       fclose (fpin);
1112     }
1113     a->length = sb.st_size;
1114   }
1115
1116   mutt_mktemp (temp);
1117   if (!(fpout = safe_fopen (temp, "w+")))
1118   {
1119     mutt_perror ("fopen");
1120     goto cleanup;
1121   }
1122
1123   fseek (fpin, a->offset, 0);
1124   a->parts = mutt_parse_messageRFC822 (fpin, a);
1125
1126   transform_to_7bit (a->parts, fpin);
1127   
1128   mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length, 
1129                  CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1130
1131   fputs ("Mime-Version: 1.0\n", fpout);
1132   mutt_write_mime_header (a->parts, fpout);
1133   fputc ('\n', fpout);
1134   mutt_write_mime_body (a->parts, fpout);
1135   
1136  cleanup:
1137   FREE (&line);
1138
1139   if (fpin && !fp)
1140     fclose (fpin);
1141   if (fpout)
1142     fclose (fpout);
1143   else
1144     return;
1145   
1146   a->encoding = ENC7BIT;
1147   a->d_filename = a->filename;
1148   if (a->filename && a->unlink)
1149     unlink (a->filename);
1150   a->filename = safe_strdup (temp);
1151   a->unlink = 1;
1152   if(stat (a->filename, &sb) == -1) 
1153   {
1154     mutt_perror ("stat");
1155     return;
1156   }
1157   a->length = sb.st_size;
1158   mutt_free_body (&a->parts);
1159   a->hdr->content = NULL;
1160 }
1161
1162 static void transform_to_7bit (BODY *a, FILE *fpin)
1163 {
1164   char buff[_POSIX_PATH_MAX];
1165   STATE s;
1166   struct stat sb;
1167
1168   memset (&s, 0, sizeof (s));
1169   for (; a; a = a->next)
1170   {
1171     if (a->type == TYPEMULTIPART)
1172     {
1173       if (a->encoding != ENC7BIT)
1174         a->encoding = ENC7BIT;
1175
1176       transform_to_7bit (a->parts, fpin);
1177     } 
1178     else if (mutt_is_message_type(a->type, a->subtype))
1179     {
1180       mutt_message_to_7bit (a, fpin);
1181     }
1182     else 
1183     {
1184       a->noconv = 1;
1185       a->force_charset = 1;
1186       
1187       mutt_mktemp (buff);
1188       if ((s.fpout = safe_fopen (buff, "w")) == NULL) 
1189       {
1190         mutt_perror ("fopen");
1191         return;
1192       }
1193       s.fpin = fpin;
1194       mutt_decode_attachment (a, &s);
1195       fclose (s.fpout);
1196       a->d_filename = a->filename;
1197       a->filename = safe_strdup (buff);
1198       a->unlink = 1;
1199       if (stat (a->filename, &sb) == -1) 
1200       {
1201         mutt_perror ("stat");
1202         return;
1203       }
1204       a->length = sb.st_size;
1205
1206       mutt_update_encoding (a);
1207       if (a->encoding == ENC8BIT)
1208         a->encoding = ENCQUOTEDPRINTABLE;
1209       else if(a->encoding == ENCBINARY)
1210         a->encoding = ENCBASE64;
1211     }
1212   }
1213 }
1214
1215 /* determine which Content-Transfer-Encoding to use */
1216 static void mutt_set_encoding (BODY *b, CONTENT *info)
1217 {
1218   char send_charset[SHORT_STRING];
1219
1220   if (b->type == TYPETEXT)
1221   {
1222     char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1223     if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1224       b->encoding = ENCQUOTEDPRINTABLE;
1225     else if (info->hibin)
1226       b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1227     else
1228       b->encoding = ENC7BIT;
1229   }
1230   else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1231   {
1232     if (info->lobin || info->hibin)
1233     {
1234       if (option (OPTALLOW8BIT) && !info->lobin)
1235         b->encoding = ENC8BIT; 
1236       else
1237         mutt_message_to_7bit (b, NULL);
1238     }
1239     else
1240       b->encoding = ENC7BIT;
1241   }
1242   else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1243     b->encoding = ENC7BIT;
1244   else
1245 #if 0
1246     if (info->lobin || info->hibin || info->binary || info->linemax > 990
1247            || info->cr || (/* option (OPTENCODEFROM) && */ info->from))
1248 #endif
1249   {
1250     /* Determine which encoding is smaller  */
1251     if (1.33 * (float)(info->lobin+info->hibin+info->ascii) < 
1252          3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1253       b->encoding = ENCBASE64;
1254     else
1255       b->encoding = ENCQUOTEDPRINTABLE;
1256   }
1257 #if 0
1258   else
1259     b->encoding = ENC7BIT;
1260 #endif
1261 }
1262
1263 void mutt_stamp_attachment(BODY *a)
1264 {
1265   a->stamp = time(NULL);
1266 }
1267
1268 /* Get a body's character set */
1269
1270 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1271 {
1272   char *p = NULL;
1273
1274   if (b && b->type != TYPETEXT)
1275     return NULL;
1276
1277   if (b) 
1278     p = mutt_get_parameter ("charset", b->parameter);
1279
1280   if (p)
1281     mutt_canonical_charset (d, dlen, NONULL(p));
1282   else
1283     strfcpy (d, "us-ascii", dlen);
1284
1285   return d;
1286 }
1287
1288
1289 /* Assumes called from send mode where BODY->filename points to actual file */
1290 void mutt_update_encoding (BODY *a)
1291 {
1292   CONTENT *info;
1293   char chsbuff[STRING];
1294
1295   /* override noconv when it's us-ascii */
1296   if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1297     a->noconv = 0;
1298
1299   if (!a->force_charset && !a->noconv)
1300     mutt_delete_parameter ("charset", &a->parameter);
1301
1302   if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1303     return;
1304
1305   mutt_set_encoding (a, info);
1306   mutt_stamp_attachment(a);
1307
1308   FREE (&a->content);
1309   a->content = info;
1310
1311 }
1312
1313 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1314 {
1315   char buffer[LONG_STRING];
1316   BODY *body;
1317   FILE *fp;
1318   int cmflags, chflags;
1319   int pgp = WithCrypto? hdr->security : 0;
1320
1321   if (WithCrypto)
1322   {
1323     if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1324         (hdr->security & ENCRYPT)) {
1325       if (!crypt_valid_passphrase(hdr->security))
1326         return (NULL);
1327     }
1328   }
1329
1330   mutt_mktemp (buffer);
1331   if ((fp = safe_fopen (buffer, "w+")) == NULL)
1332     return NULL;
1333
1334   body = mutt_new_body ();
1335   body->type = TYPEMESSAGE;
1336   body->subtype = safe_strdup ("rfc822");
1337   body->filename = safe_strdup (buffer);
1338   body->unlink = 1;
1339   body->use_disp = 0;
1340   body->disposition = DISPINLINE;
1341   body->noconv = 1;
1342
1343   mutt_parse_mime_message (ctx, hdr);
1344
1345   chflags = CH_XMIT;
1346   cmflags = 0;
1347
1348   /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1349   if (!attach_msg && option (OPTMIMEFORWDECODE))
1350   {
1351     chflags |= CH_MIME | CH_TXTPLAIN;
1352     cmflags = M_CM_DECODE | M_CM_CHARCONV;
1353     if ((WithCrypto & APPLICATION_PGP))
1354       pgp &= ~PGPENCRYPT;
1355     if ((WithCrypto & APPLICATION_SMIME))
1356       pgp &= ~SMIMEENCRYPT;
1357   }
1358   else if (WithCrypto
1359            && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1360   {
1361     if ((WithCrypto & APPLICATION_PGP)
1362         && mutt_is_multipart_encrypted (hdr->content))
1363     {
1364       chflags |= CH_MIME | CH_NONEWLINE;
1365       cmflags = M_CM_DECODE_PGP;
1366       pgp &= ~PGPENCRYPT;
1367     }
1368     else if ((WithCrypto & APPLICATION_PGP)
1369              && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT))
1370     {
1371       chflags |= CH_MIME | CH_TXTPLAIN;
1372       cmflags = M_CM_DECODE | M_CM_CHARCONV;
1373       pgp &= ~PGPENCRYPT;
1374     }
1375     else if ((WithCrypto & APPLICATION_SMIME)
1376               && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT)
1377     {
1378       chflags |= CH_MIME | CH_TXTPLAIN;
1379       cmflags = M_CM_DECODE | M_CM_CHARCONV;
1380       pgp &= ~SMIMEENCRYPT;
1381     }
1382   }
1383
1384   mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1385   
1386   fflush(fp);
1387   rewind(fp);
1388
1389   body->hdr = mutt_new_header();
1390   body->hdr->offset = 0;
1391   /* we don't need the user headers here */
1392   body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1393   if (WithCrypto)
1394     body->hdr->security = pgp;
1395   mutt_update_encoding (body);
1396   body->parts = body->hdr->content;
1397
1398   fclose(fp);
1399   
1400   return (body);
1401 }
1402
1403 BODY *mutt_make_file_attach (const char *path)
1404 {
1405   BODY *att;
1406   CONTENT *info;
1407
1408   att = mutt_new_body ();
1409   att->filename = safe_strdup (path);
1410
1411   /* Attempt to determine the appropriate content-type based on the filename
1412    * suffix.
1413    */
1414
1415 #if 0
1416   
1417   if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER 
1418       || *xbuf != '\0')
1419   {
1420     att->type = n;
1421     att->subtype = safe_strdup (buf);
1422     att->xtype = safe_strdup (xbuf);
1423   }
1424
1425 #else
1426   
1427   mutt_lookup_mime_type (att, path);
1428
1429 #endif
1430   
1431   if ((info = mutt_get_content_info (path, att)) == NULL)
1432   {
1433     mutt_free_body (&att);
1434     return NULL;
1435   }
1436
1437   if (!att->subtype)
1438   {
1439     if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
1440     {
1441       /*
1442        * Statistically speaking, there should be more than 10% "lobin" 
1443        * chars if this is really a binary file...
1444        */
1445       att->type = TYPETEXT;
1446       att->subtype = safe_strdup ("plain");
1447     }
1448     else
1449     {
1450       att->type = TYPEAPPLICATION;
1451       att->subtype = safe_strdup ("octet-stream");
1452     }
1453   } 
1454
1455   mutt_update_encoding (att);
1456   return (att);
1457 }
1458
1459 static int get_toplevel_encoding (BODY *a)
1460 {
1461   int e = ENC7BIT;
1462
1463   for (; a; a = a->next)
1464   {
1465     if (a->encoding == ENCBINARY)
1466       return (ENCBINARY);
1467     else if (a->encoding == ENC8BIT)
1468       e = ENC8BIT;
1469   }
1470
1471   return (e);
1472 }
1473
1474 BODY *mutt_make_multipart (BODY *b)
1475 {
1476   BODY *new;
1477
1478   new = mutt_new_body ();
1479   new->type = TYPEMULTIPART;
1480   new->subtype = safe_strdup ("mixed");
1481   new->encoding = get_toplevel_encoding (b);
1482   mutt_generate_boundary (&new->parameter);
1483   new->use_disp = 0;  
1484   new->disposition = DISPINLINE;
1485   new->parts = b;
1486
1487   return new;
1488 }
1489
1490 /* remove the multipart body if it exists */
1491 BODY *mutt_remove_multipart (BODY *b)
1492 {
1493   BODY *t;
1494
1495   if (b->parts)
1496   {
1497     t = b;
1498     b = b->parts;
1499     t->parts = NULL;
1500     mutt_free_body (&t);
1501   }
1502   return b;
1503 }
1504
1505 char *mutt_make_date (char *s, size_t len)
1506 {
1507   time_t t = time (NULL);
1508   struct tm *l = localtime (&t);
1509   time_t tz = mutt_local_tz (t);
1510
1511   tz /= 60;
1512
1513   snprintf (s, len,  "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1514             Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1515             l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1516             (int) tz / 60, (int) abs (tz) % 60);
1517   return (s);
1518 }
1519
1520 /* wrapper around mutt_write_address() so we can handle very large
1521    recipient lists without needing a huge temporary buffer in memory */
1522 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1523 {
1524   ADDRESS *tmp;
1525   char buf[LONG_STRING];
1526   int count = 0;
1527   int len;
1528
1529   while (adr)
1530   {
1531     tmp = adr->next;
1532     adr->next = NULL;
1533     buf[0] = 0;
1534     rfc822_write_address (buf, sizeof (buf), adr, display);
1535     len = mutt_strlen (buf);
1536     if (count && linelen + len > 74)
1537     {
1538       fputs ("\n\t", fp);
1539       linelen = len + 8; /* tab is usually about 8 spaces... */
1540     }
1541     else
1542     {
1543       if (count && adr->mailbox)
1544       {
1545         fputc (' ', fp);
1546         linelen++;
1547       }
1548       linelen += len;
1549     }
1550     fputs (buf, fp);
1551     adr->next = tmp;
1552     if (!adr->group && adr->next && adr->next->mailbox)
1553     {
1554       linelen++;
1555       fputc (',', fp);
1556     }
1557     adr = adr->next;
1558     count++;
1559   }
1560   fputc ('\n', fp);
1561 }
1562
1563 /* arbitrary number of elements to grow the array by */
1564 #define REF_INC 16
1565
1566 #define TrimRef 10
1567
1568 /* need to write the list in reverse because they are stored in reverse order
1569  * when parsed to speed up threading
1570  */
1571 void mutt_write_references (LIST *r, FILE *f)
1572 {
1573   LIST **ref = NULL;
1574   int refcnt = 0, refmax = 0;
1575
1576   for ( ; (TrimRef == 0 || refcnt < TrimRef) && r ; r = r->next)
1577   {
1578     if (refcnt == refmax)
1579       safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1580     ref[refcnt++] = r;
1581   }
1582
1583   while (refcnt-- > 0)
1584   {
1585     fputc (' ', f);
1586     fputs (ref[refcnt]->data, f);
1587   }
1588
1589   FREE (&ref);
1590 }
1591
1592 /* Note: all RFC2047 encoding should be done outside of this routine, except
1593  * for the "real name."  This will allow this routine to be used more than
1594  * once, if necessary.
1595  * 
1596  * Likewise, all IDN processing should happen outside of this routine.
1597  *
1598  * mode == 1  => "lite" mode (used for edit_hdrs)
1599  * mode == 0  => normal mode.  write full header + MIME headers
1600  * mode == -1 => write just the envelope info (used for postponing messages)
1601  * 
1602  * privacy != 0 => will omit any headers which may identify the user.
1603  *               Output generated is suitable for being sent through
1604  *               anonymous remailer chains.
1605  *
1606  */
1607
1608 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach, 
1609                               int mode, int privacy)
1610 {
1611   char buffer[LONG_STRING];
1612   char *p;
1613   LIST *tmp = env->userhdrs;
1614   int has_agent = 0; /* user defined user-agent header field exists */
1615   
1616 #ifdef USE_NNTP
1617   if (!option (OPTNEWSSEND))
1618 #endif
1619   if (mode == 0 && !privacy)
1620     fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
1621
1622   /* OPTUSEFROM is not consulted here so that we can still write a From:
1623    * field if the user sets it with the `my_hdr' command
1624    */
1625   if (env->from && !privacy)
1626   {
1627     buffer[0] = 0;
1628     rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1629     fprintf (fp, "From: %s\n", buffer);
1630   }
1631
1632   if (env->to)
1633   {
1634     fputs ("To: ", fp);
1635     mutt_write_address_list (env->to, fp, 4, 0);
1636   }
1637   else if (mode > 0)
1638 #ifdef USE_NNTP
1639   if (!option (OPTNEWSSEND))
1640 #endif
1641     fputs ("To: \n", fp);
1642
1643   if (env->cc)
1644   {
1645     fputs ("Cc: ", fp);
1646     mutt_write_address_list (env->cc, fp, 4, 0);
1647   }
1648   else if (mode > 0)
1649 #ifdef USE_NNTP
1650   if (!option (OPTNEWSSEND))
1651 #endif
1652     fputs ("Cc: \n", fp);
1653
1654   if (env->bcc)
1655   {
1656     if(mode != 0 || option(OPTWRITEBCC))
1657     {
1658       fputs ("Bcc: ", fp);
1659       mutt_write_address_list (env->bcc, fp, 5, 0);
1660     }
1661   }
1662   else if (mode > 0)
1663 #ifdef USE_NNTP
1664   if (!option (OPTNEWSSEND))
1665 #endif
1666     fputs ("Bcc: \n", fp);
1667
1668 #ifdef USE_NNTP
1669   if (env->newsgroups)
1670     fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1671   else if (mode == 1 && option (OPTNEWSSEND))
1672     fputs ("Newsgroups: \n", fp);
1673
1674   if (env->followup_to)
1675     fprintf (fp, "Followup-To: %s\n", env->followup_to);
1676   else if (mode == 1 && option (OPTNEWSSEND))
1677     fputs ("Followup-To: \n", fp);
1678
1679   if (env->x_comment_to)
1680     fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1681   else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO))
1682     fputs ("X-Comment-To: \n", fp);
1683 #endif
1684
1685   if (env->subject)
1686     fprintf (fp, "Subject: %s\n", env->subject);
1687   else if (mode == 1)
1688     fputs ("Subject: \n", fp);
1689
1690   /* save message id if the user has set it */
1691   if (env->message_id && !privacy)
1692     fprintf (fp, "Message-ID: %s\n", env->message_id);
1693
1694   if (env->reply_to)
1695   {
1696     fputs ("Reply-To: ", fp);
1697     mutt_write_address_list (env->reply_to, fp, 10, 0);
1698   }
1699   else if (mode > 0)
1700     fputs ("Reply-To: \n", fp);
1701
1702   if (env->mail_followup_to)
1703 #ifdef USE_NNTP
1704   if (!option (OPTNEWSSEND))
1705 #endif
1706   {
1707     fputs ("Mail-Followup-To: ", fp);
1708     mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1709   }
1710
1711   if (mode <= 0)
1712   {
1713     if (env->references)
1714     {
1715       fputs ("References:", fp);
1716       mutt_write_references (env->references, fp);
1717       fputc('\n', fp);
1718     }
1719
1720     /* Add the MIME headers */
1721     fputs ("Mime-Version: 1.0\n", fp);
1722     mutt_write_mime_header (attach, fp);
1723   }
1724
1725   if (env->in_reply_to)
1726   {
1727     fputs ("In-Reply-To:", fp);
1728     mutt_write_references (env->in_reply_to, fp);
1729     fputc ('\n', fp);
1730   }
1731   
1732   /* Add any user defined headers */
1733   for (; tmp; tmp = tmp->next)
1734   {
1735     if ((p = strchr (tmp->data, ':')))
1736     {
1737       p++; SKIPWS (p);
1738       if (!*p)  continue;  /* don't emit empty fields. */
1739
1740       /* check to see if the user has overridden the user-agent field */
1741       if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
1742       {
1743         has_agent = 1;
1744         if (privacy)
1745           continue;
1746       }
1747
1748       fputs (tmp->data, fp);
1749       fputc ('\n', fp);
1750     }
1751   }
1752
1753   if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent)
1754   {
1755     struct utsname un;
1756     char * os;
1757     if (OperatingSystem!=NULL) {
1758       os = OperatingSystem;
1759     } else {
1760       if (uname(&un)==-1) {
1761         os = "UNIX";
1762       } else {
1763         os = un.sysname;
1764       }
1765     }
1766     /* Add a vanity header */
1767     fprintf (fp, "User-Agent: mutt-ng %s (%s)\n", MUTT_VERSION,os);
1768   }
1769
1770   return (ferror (fp) == 0 ? 0 : -1);
1771 }
1772
1773 static void encode_headers (LIST *h)
1774 {
1775   char *tmp;
1776   char *p;
1777   int i;
1778   
1779   for (; h; h = h->next)
1780   {
1781     if (!(p = strchr (h->data, ':')))
1782       continue;
1783
1784     i = p - h->data;
1785     ++p; SKIPWS (p);
1786     tmp = safe_strdup (p);
1787
1788     if (!tmp)
1789       continue;
1790     
1791     rfc2047_encode_string (&tmp);
1792     safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
1793
1794     sprintf (h->data + i, ": %s", NONULL (tmp));  /* __SPRINTF_CHECKED__ */
1795     
1796     FREE (&tmp);
1797   }
1798 }
1799
1800 const char *mutt_fqdn(short may_hide_host)
1801 {
1802   char *p = NULL, *q;
1803   
1804   if(Fqdn && Fqdn[0] != '@')
1805   {
1806     p = Fqdn;
1807     
1808     if(may_hide_host && option(OPTHIDDENHOST))
1809     {
1810       if((p = strchr(Fqdn, '.')))
1811         p++;
1812
1813       /* sanity check: don't hide the host if
1814        * the fqdn is something like detebe.org.
1815        */
1816       
1817       if(!p || !(q = strchr(p, '.')))
1818         p = Fqdn;
1819     }
1820   }
1821
1822   return p;
1823 }
1824
1825 static char mutt_normalized_char(char c) {
1826   if (isalnum(c))
1827     return c;
1828   if (strchr(".!#$%&'*+-/=?^_`{|}~",c))
1829     return c;
1830   return '.'; /* normalized character (we're stricter than RFC2822, 3.6.4) */
1831 }
1832
1833 static void mutt_gen_localpart(char * buf, unsigned int len, char * fmt) {
1834   time_t now;
1835   struct tm *tm;
1836   char tmp[SHORT_STRING];
1837
1838   *buf = '\0';
1839
1840   now = time (NULL);
1841   tm = gmtime (&now);
1842
1843   for (;*fmt;++fmt) {
1844     if (*fmt == '%') {
1845       switch (fmt[1]) {
1846       case 0:
1847         return;
1848       case 'd':
1849         snprintf(tmp,sizeof(tmp),"%02d",tm->tm_mday);
1850         safe_strncat(buf,len,tmp,2);
1851         break;
1852       case 'h':
1853         snprintf(tmp,sizeof(tmp),"%02d",tm->tm_hour);
1854         safe_strncat(buf,len,tmp,2);
1855         break;
1856       case 'm':
1857         snprintf(tmp,sizeof(tmp),"%02d",tm->tm_mon+1);
1858         safe_strncat(buf,len,tmp,2);
1859         break;
1860       case 'M':
1861         snprintf(tmp,sizeof(tmp),"%02d",tm->tm_min);
1862         safe_strncat(buf,len,tmp,2);
1863         break;
1864       case 'O':
1865         snprintf(tmp,sizeof(tmp),"%lo",(unsigned long)now);
1866         safe_strncat(buf,len,tmp,strlen(tmp));
1867         break;
1868       case 'p':
1869         snprintf(tmp,sizeof(tmp),"%u",(unsigned int)getpid());
1870         safe_strncat(buf,len,tmp,strlen(tmp));
1871         break;
1872       case 'P':
1873         snprintf(tmp,sizeof(tmp),"%c",MsgIdPfx);
1874         MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1875         safe_strncat(buf,len,tmp,1);
1876         break;
1877       case 'r':
1878         snprintf(tmp,sizeof(tmp),"%u",(unsigned int)rand());
1879         safe_strncat(buf,len,tmp,strlen(tmp));
1880         break;
1881       case 'R':
1882         snprintf(tmp,sizeof(tmp),"%x",(unsigned int)rand());
1883         safe_strncat(buf,len,tmp,strlen(tmp));
1884         break;
1885       case 's':
1886         snprintf(tmp,sizeof(tmp),"%02d",tm->tm_sec);
1887         safe_strncat(buf,len,tmp,2);
1888         break;
1889       case 'T':
1890         snprintf(tmp,sizeof(tmp),"%u",(unsigned int)now);
1891         safe_strncat(buf,len,tmp,strlen(tmp));
1892         break;
1893       case 'X':
1894         snprintf(tmp,sizeof(tmp),"%x",(unsigned int)now);
1895         safe_strncat(buf,len,tmp,strlen(tmp));
1896         break;
1897       case 'Y':
1898         snprintf(tmp,sizeof(tmp),"%04d",tm->tm_year+1900); /* this will break in the year 10000 ;-) */
1899         safe_strncat(buf,len,tmp,4);
1900         break;
1901       case '%':
1902         safe_strncat(buf,len,"%",1);
1903         break;
1904       default:
1905         safe_strncat(buf,len,".",1); /* invalid formats are replaced by '.' */
1906       } /* switch */
1907       ++fmt;
1908     } else {
1909       char c;
1910       c = mutt_normalized_char(*fmt); /* @todo: filter out invalid characters */
1911       safe_strncat(buf,len,&c,1);
1912     }
1913   }
1914 }
1915
1916 char *mutt_gen_msgid (void)
1917 {
1918   char buf[SHORT_STRING];
1919   char localpart[SHORT_STRING];
1920   unsigned int localpart_length;
1921   time_t now;
1922   struct tm *tm;
1923   const char *fqdn;
1924
1925   now = time (NULL);
1926   tm = gmtime (&now);
1927   if(!(fqdn = mutt_fqdn(0)))
1928     fqdn = NONULL(Hostname);
1929
1930   localpart_length = sizeof(buf) - strlen(fqdn) - 4; /* the 4 characters are '<', '@', '>' and '\0' */
1931
1932   mutt_gen_localpart(localpart,localpart_length,MsgIdFormat);
1933
1934   snprintf(buf,sizeof(buf),"<%s@%s>",localpart,fqdn);
1935   return (safe_strdup (buf));
1936 }
1937
1938 static RETSIGTYPE alarm_handler (int sig)
1939 {
1940   SigAlrm = 1;
1941 }
1942
1943 /* invoke sendmail in a subshell
1944    path (in)            path to program to execute
1945    args (in)            arguments to pass to program
1946    msg (in)             temp file containing message to send
1947    tempfile (out)       if sendmail is put in the background, this points
1948                         to the temporary file containing the stdout of the
1949                         child process */
1950 static int
1951 send_msg (const char *path, char **args, const char *msg, char **tempfile)
1952 {
1953   sigset_t set;
1954   int fd, st;
1955   pid_t pid, ppid;
1956
1957   mutt_block_signals_system ();
1958
1959   sigemptyset (&set);
1960   /* we also don't want to be stopped right now */
1961   sigaddset (&set, SIGTSTP);
1962   sigprocmask (SIG_BLOCK, &set, NULL);
1963
1964   if (SendmailWait >= 0)
1965   {
1966     char tmp[_POSIX_PATH_MAX];
1967
1968     mutt_mktemp (tmp);
1969     *tempfile = safe_strdup (tmp);
1970   }
1971
1972   if ((pid = fork ()) == 0)
1973   {
1974     struct sigaction act, oldalrm;
1975
1976     /* save parent's ID before setsid() */
1977     ppid = getppid ();
1978
1979     /* we want the delivery to continue even after the main process dies,
1980      * so we put ourselves into another session right away
1981      */
1982     setsid ();
1983   
1984     /* next we close all open files */
1985 #if defined(OPEN_MAX)
1986     for (fd = 0; fd < OPEN_MAX; fd++)
1987       close (fd);
1988 #elif defined(_POSIX_OPEN_MAX)
1989     for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1990       close (fd);
1991 #else
1992     close (0);
1993     close (1);
1994     close (2);
1995 #endif
1996
1997     /* now the second fork() */
1998     if ((pid = fork ()) == 0)
1999     {
2000       /* "msg" will be opened as stdin */
2001       if (open (msg, O_RDONLY, 0) < 0)
2002       {
2003         unlink (msg);
2004         _exit (S_ERR);
2005       }
2006       unlink (msg);
2007
2008       if (SendmailWait >= 0)
2009       {
2010         /* *tempfile will be opened as stdout */
2011         if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2012           _exit (S_ERR);
2013         /* redirect stderr to *tempfile too */
2014         if (dup (1) < 0)
2015           _exit (S_ERR);
2016       }
2017       else 
2018       {
2019         if (open ("/dev/null", O_WRONLY | O_APPEND) < 0)        /* stdout */
2020           _exit (S_ERR);
2021         if (open ("/dev/null", O_RDWR | O_APPEND) < 0)          /* stderr */
2022           _exit (S_ERR);
2023       }
2024
2025       execv (path, args);
2026       _exit (S_ERR);
2027     }
2028     else if (pid == -1)
2029     {
2030       unlink (msg);
2031       FREE (tempfile);
2032       _exit (S_ERR);
2033     }
2034
2035     /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
2036      * SendmailWait = 0: wait forever
2037      * SendmailWait < 0: don't wait
2038      */
2039     if (SendmailWait > 0)
2040     {
2041       SigAlrm = 0;
2042       act.sa_handler = alarm_handler;
2043 #ifdef SA_INTERRUPT
2044       /* need to make sure waitpid() is interrupted on SIGALRM */
2045       act.sa_flags = SA_INTERRUPT;
2046 #else
2047       act.sa_flags = 0;
2048 #endif
2049       sigemptyset (&act.sa_mask);
2050       sigaction (SIGALRM, &act, &oldalrm);
2051       alarm (SendmailWait);
2052     }
2053     else if (SendmailWait < 0)
2054       _exit (0xff & EX_OK);
2055
2056     if (waitpid (pid, &st, 0) > 0)
2057     {
2058       st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
2059       if (SendmailWait && st == (0xff & EX_OK))
2060       {
2061         unlink (*tempfile); /* no longer needed */
2062         FREE (tempfile);
2063       }
2064     }
2065     else
2066     {
2067       st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
2068               S_BKG : S_ERR;
2069       if (SendmailWait > 0)
2070       {
2071         unlink (*tempfile);
2072         FREE (tempfile);
2073       }
2074     }
2075
2076     /* reset alarm; not really needed, but... */
2077     alarm (0);
2078     sigaction (SIGALRM, &oldalrm, NULL);
2079
2080     if (kill (ppid, 0) == -1 && errno == ESRCH)
2081     {
2082       /* the parent is already dead */
2083       unlink (*tempfile);
2084       FREE (tempfile);
2085     }
2086
2087     _exit (st);
2088   }
2089
2090   sigprocmask (SIG_UNBLOCK, &set, NULL);
2091
2092   if (pid != -1 && waitpid (pid, &st, 0) > 0)
2093     st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
2094   else
2095     st = S_ERR; /* error */
2096
2097   mutt_unblock_signals_system (1);
2098
2099   return (st);
2100 }
2101
2102 static char **
2103 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
2104 {
2105   for (; addr; addr = addr->next)
2106   {
2107     /* weed out group mailboxes, since those are for display only */
2108     if (addr->mailbox && !addr->group)
2109     {
2110       if (*argslen == *argsmax)
2111         safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2112       args[(*argslen)++] = addr->mailbox;
2113     }
2114   }
2115   return (args);
2116 }
2117
2118 static char **
2119 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2120 {
2121   if (*argslen == *argsmax)
2122     safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2123   args[(*argslen)++] = s;
2124   return (args);
2125 }
2126
2127 static const char *
2128 strsysexit(int e)
2129 {
2130   int i;
2131   
2132   for(i = 0; sysexits_h[i].str; i++)
2133   {
2134     if(e == sysexits_h[i].v)
2135       break;
2136   }
2137   
2138   return sysexits_h[i].str;
2139 }
2140
2141
2142 static int
2143 mutt_invoke_sendmail (ADDRESS *from,    /* the sender */
2144                  ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2145                  const char *msg, /* file containing message */
2146                  int eightbit) /* message contains 8bit chars */
2147 {
2148   char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
2149   char **args = NULL;
2150   size_t argslen = 0, argsmax = 0;
2151   int i;
2152
2153 #ifdef USE_NNTP
2154   if (option (OPTNEWSSEND))
2155   {
2156     char cmd[LONG_STRING];
2157
2158     mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0, 0);
2159     if (!*cmd)
2160     {
2161       i = nntp_post (msg);
2162       unlink (msg);
2163       return i;
2164     }
2165
2166     s = safe_strdup (cmd);
2167   }
2168   else
2169 #endif
2170     s = safe_strdup (Sendmail);
2171
2172   ps = s;
2173   i = 0;
2174   while ((ps = strtok (ps, " ")))
2175   {
2176     if (argslen == argsmax)
2177       safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2178
2179     if (i)
2180       args[argslen++] = ps;
2181     else
2182     {
2183       path = safe_strdup (ps);
2184       ps = strrchr (ps, '/');
2185       if (ps)
2186         ps++;
2187       else
2188         ps = path;
2189       args[argslen++] = ps;
2190     }
2191     ps = NULL;
2192     i++;
2193   }
2194
2195 #ifdef USE_NNTP
2196   if (!option (OPTNEWSSEND))
2197   {
2198 #endif
2199   if (eightbit && option (OPTUSE8BITMIME))
2200     args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2201
2202   if (option (OPTENVFROM) && from && !from->next)
2203   {
2204     args = add_option (args, &argslen, &argsmax, "-f");
2205     args = add_args   (args, &argslen, &argsmax, from);
2206   }
2207   if (DsnNotify)
2208   {
2209     args = add_option (args, &argslen, &argsmax, "-N");
2210     args = add_option (args, &argslen, &argsmax, DsnNotify);
2211   }
2212   if (DsnReturn)
2213   {
2214     args = add_option (args, &argslen, &argsmax, "-R");
2215     args = add_option (args, &argslen, &argsmax, DsnReturn);
2216   }
2217   args = add_option (args, &argslen, &argsmax, "--");
2218   args = add_args (args, &argslen, &argsmax, to);
2219   args = add_args (args, &argslen, &argsmax, cc);
2220   args = add_args (args, &argslen, &argsmax, bcc);
2221 #ifdef USE_NNTP
2222   }
2223 #endif
2224
2225   if (argslen == argsmax)
2226     safe_realloc (&args, sizeof (char *) * (++argsmax));
2227   
2228   args[argslen++] = NULL;
2229
2230   if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff))
2231   {
2232     if (i != S_BKG)
2233     {
2234       const char *e = strsysexit (i);
2235
2236       e = strsysexit (i);
2237       mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2238       if (childout)
2239       {
2240         struct stat st;
2241         
2242         if (stat (childout, &st) == 0 && st.st_size > 0)
2243           mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2244       }
2245     }
2246   }
2247   else
2248     unlink (childout);
2249
2250   FREE (&childout);
2251   FREE (&path);
2252   FREE (&s);
2253   FREE (&args);
2254
2255   if (i == (EX_OK & 0xff))
2256     i = 0;
2257   else if (i == S_BKG)
2258     i = 1;
2259   else
2260     i = -1;
2261   return (i);
2262 }
2263
2264 int
2265 mutt_invoke_mta (ADDRESS *from, /* the sender */
2266                  ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2267                  const char *msg, /* file containing message */
2268                  int eightbit) /* message contains 8bit chars */
2269 {
2270 #ifdef USE_LIBESMTP
2271   if (SmtpHost)
2272     return mutt_invoke_libesmtp(from, to, cc, bcc, msg, eightbit);
2273 #endif
2274   
2275   return mutt_invoke_sendmail(from, to, cc, bcc, msg, eightbit);
2276 }
2277
2278 /* appends string 'b' to string 'a', and returns the pointer to the new
2279    string. */
2280 char *mutt_append_string (char *a, const char *b)
2281 {
2282   size_t la = mutt_strlen (a);
2283   safe_realloc (&a, la + mutt_strlen (b) + 1);
2284   strcpy (a + la, b);   /* __STRCPY_CHECKED__ */
2285   return (a);
2286 }
2287
2288 /* returns 1 if char `c' needs to be quoted to protect from shell
2289    interpretation when executing commands in a subshell */
2290 #define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
2291
2292 /* returns 1 if string `s' contains characters which could cause problems
2293    when used on a command line to execute a command */
2294 int mutt_needs_quote (const char *s)
2295 {
2296   while (*s)
2297   {
2298     if (INVALID_CHAR (*s))
2299       return 1;
2300     s++;
2301   }
2302   return 0;
2303 }
2304
2305 /* Quote a string to prevent shell escapes when this string is used on the
2306    command line to send mail. */
2307 char *mutt_quote_string (const char *s)
2308 {
2309   char *r, *pr;
2310   size_t rlen;
2311
2312   rlen = mutt_strlen (s) + 3;
2313   pr = r = (char *) safe_malloc (rlen);
2314   *pr++ = '"';
2315   while (*s)
2316   {
2317     if (INVALID_CHAR (*s))
2318     {
2319       size_t o = pr - r;
2320       safe_realloc (&r, ++rlen);
2321       pr = r + o;
2322       *pr++ = '\\';
2323     }
2324     *pr++ = *s++;
2325   }
2326   *pr++ = '"';
2327   *pr = 0;
2328   return (r);
2329 }
2330
2331 /* For postponing (!final) do the necessary encodings only */
2332 void mutt_prepare_envelope (ENVELOPE *env, int final)
2333 {
2334   char buffer[LONG_STRING];
2335
2336   if (final)
2337   {
2338     if (env->bcc && !(env->to || env->cc))
2339     {
2340       /* some MTA's will put an Apparently-To: header field showing the Bcc:
2341        * recipients if there is no To: or Cc: field, so attempt to suppress
2342        * it by using an empty To: field.
2343        */
2344       env->to = rfc822_new_address ();
2345       env->to->group = 1;
2346       env->to->next = rfc822_new_address ();
2347
2348       buffer[0] = 0;
2349       rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2350                   RFC822Specials);
2351
2352       env->to->mailbox = safe_strdup (buffer);
2353     }
2354
2355     mutt_set_followup_to (env);
2356
2357     if (!env->message_id)
2358       env->message_id = mutt_gen_msgid ();
2359   }
2360
2361   /* Take care of 8-bit => 7-bit conversion. */
2362   rfc2047_encode_adrlist (env->to, "To");
2363   rfc2047_encode_adrlist (env->cc, "Cc");
2364   rfc2047_encode_adrlist (env->bcc, "Bcc");
2365   rfc2047_encode_adrlist (env->from, "From");
2366   rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2367   rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2368
2369   if (env->subject)
2370 #ifdef USE_NNTP
2371   if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2372 #endif
2373   {
2374     rfc2047_encode_string (&env->subject);
2375   }
2376   encode_headers (env->userhdrs);
2377 }
2378
2379 void mutt_unprepare_envelope (ENVELOPE *env)
2380 {
2381   LIST *item;
2382
2383   for (item = env->userhdrs; item; item = item->next)
2384     rfc2047_decode (&item->data);
2385
2386   rfc822_free_address (&env->mail_followup_to);
2387
2388   /* back conversions */
2389   rfc2047_decode_adrlist (env->to);
2390   rfc2047_decode_adrlist (env->cc);
2391   rfc2047_decode_adrlist (env->bcc);
2392   rfc2047_decode_adrlist (env->from);
2393   rfc2047_decode_adrlist (env->reply_to);
2394   rfc2047_decode (&env->subject);
2395 }
2396
2397 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2398                                   ADDRESS *env_from)
2399 {
2400   int i, ret = 0;
2401   FILE *f;
2402   char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2403   MESSAGE *msg = NULL;
2404
2405   if (!h)
2406   {
2407           /* Try to bounce each message out, aborting if we get any failures. */
2408     for (i=0; i<Context->msgcount; i++)
2409       if (Context->hdrs[i]->tagged)
2410         ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2411     return ret;
2412   }
2413
2414   /* If we failed to open a message, return with error */
2415   if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2416     return -1;
2417
2418   if (!fp) fp = msg->fp;
2419
2420   mutt_mktemp (tempfile);
2421   if ((f = safe_fopen (tempfile, "w")) != NULL)
2422   {
2423     int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2424     
2425     if (!option (OPTBOUNCEDELIVERED))
2426       ch_flags |= CH_WEED_DELIVERED;
2427     
2428     fseek (fp, h->offset, 0);
2429     fprintf (f, "Resent-From: %s", resent_from);
2430     fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2431     fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2432     fputs ("Resent-To: ", f);
2433     mutt_write_address_list (to, f, 11, 0);
2434     mutt_copy_header (fp, h, f, ch_flags, NULL);
2435     fputc ('\n', f);
2436     mutt_copy_bytes (fp, f, h->content->length);
2437     fclose (f);
2438
2439     ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2440                                 h->content->encoding == ENC8BIT);
2441   }
2442
2443   if (msg)
2444     mx_close_message (&msg);
2445
2446   return ret;
2447 }
2448
2449 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2450 {
2451   ADDRESS *from;
2452   const char *fqdn = mutt_fqdn (1);
2453   char resent_from[STRING];
2454   int ret;
2455   char *err;
2456   
2457   resent_from[0] = '\0';
2458   from = mutt_default_from ();
2459
2460   if (fqdn)
2461     rfc822_qualify (from, fqdn);
2462
2463   rfc2047_encode_adrlist (from, "Resent-From");
2464   if (mutt_addrlist_to_idna (from, &err))
2465   {
2466     mutt_error (_("Bad IDN %s while preparing resent-from."),
2467                 err);
2468     return -1;
2469   }
2470   rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2471
2472 #ifdef USE_NNTP
2473   unset_option (OPTNEWSSEND);
2474 #endif
2475
2476   ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2477
2478   rfc822_free_address (&from);
2479
2480   return ret;
2481 }
2482
2483
2484 /* given a list of addresses, return a list of unique addresses */
2485 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2486 {
2487   ADDRESS *top = addr;
2488   ADDRESS **last = &top;
2489   ADDRESS *tmp;
2490   int dup;
2491
2492   while (addr)
2493   {
2494     for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next)
2495     {
2496       if (tmp->mailbox && addr->mailbox && 
2497           !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2498       {
2499         dup = 1;
2500         break;
2501       }
2502     }
2503     
2504     if (dup)
2505     {
2506       dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
2507                   addr->mailbox));
2508       
2509       *last = addr->next;
2510
2511       addr->next = NULL;
2512       rfc822_free_address(&addr);
2513       
2514       addr = *last;
2515     }
2516     else 
2517     {
2518       last = &addr->next;
2519       addr = addr->next;
2520     }
2521   }
2522   
2523   return (top);
2524 }
2525
2526 static void set_noconv_flags (BODY *b, short flag)
2527 {
2528   for(; b; b = b->next)
2529   {
2530     if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2531       set_noconv_flags (b->parts, flag);
2532     else if (b->type == TYPETEXT && b->noconv)
2533     {
2534       if (flag)
2535         mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2536       else
2537         mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2538     }
2539   }
2540 }
2541
2542 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc)
2543 {
2544   CONTEXT f;
2545   MESSAGE *msg;
2546   char tempfile[_POSIX_PATH_MAX];
2547   FILE *tempfp = NULL;
2548   int r;
2549
2550   if (post)
2551     set_noconv_flags (hdr->content, 1);
2552   
2553   if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
2554   {
2555     dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2556                 path));
2557     return (-1);
2558   }
2559
2560   /* We need to add a Content-Length field to avoid problems where a line in
2561    * the message body begins with "From "   
2562    */
2563   if (f.magic == M_MMDF || f.magic == M_MBOX)
2564   {
2565     mutt_mktemp (tempfile);
2566     if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2567     {
2568       mutt_perror (tempfile);
2569       mx_close_mailbox (&f, NULL);
2570       return (-1);
2571     }
2572   }
2573
2574   hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2575   if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
2576   {
2577     mx_close_mailbox (&f, NULL);
2578     return (-1);
2579   }
2580
2581   /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2582    * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header() 
2583    * */
2584   mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 0);
2585
2586   /* (postponment) if this was a reply of some sort, <msgid> contians the
2587    * Message-ID: of message replied to.  Save it using a special X-Mutt-
2588    * header so it can be picked up if the message is recalled at a later
2589    * point in time.  This will allow the message to be marked as replied if
2590    * the same mailbox is still open.
2591    */
2592   if (post && msgid)
2593     fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2594   
2595   /* (postponment) save the Fcc: using a special X-Mutt- header so that
2596    * it can be picked up when the message is recalled 
2597    */
2598   if (post && fcc)
2599     fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2600   fprintf (msg->fp, "Status: RO\n");
2601
2602
2603
2604   /* (postponment) if the mail is to be signed or encrypted, save this info */
2605   if ((WithCrypto & APPLICATION_PGP)
2606       && post && (hdr->security & APPLICATION_PGP))
2607   {
2608     fputs ("X-Mutt-PGP: ", msg->fp);
2609     if (hdr->security & ENCRYPT) 
2610       fputc ('E', msg->fp);
2611     if (hdr->security & SIGN)
2612     {
2613       fputc ('S', msg->fp);
2614       if (PgpSignAs && *PgpSignAs)
2615         fprintf (msg->fp, "<%s>", PgpSignAs);
2616     }
2617     if (hdr->security & INLINE)
2618       fputc ('I', msg->fp);
2619     fputc ('\n', msg->fp);
2620   }
2621
2622   /* (postponment) if the mail is to be signed or encrypted, save this info */
2623   if ((WithCrypto & APPLICATION_SMIME)
2624       && post && (hdr->security & APPLICATION_SMIME))
2625   {
2626     fputs ("X-Mutt-SMIME: ", msg->fp);
2627     if (hdr->security & ENCRYPT) {
2628         fputc ('E', msg->fp);
2629         if (SmimeCryptAlg && *SmimeCryptAlg)
2630             fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2631     }
2632     if (hdr->security & SIGN) {
2633         fputc ('S', msg->fp);
2634         if (SmimeDefaultKey && *SmimeDefaultKey)
2635             fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2636     }
2637     if (hdr->security & INLINE)
2638       fputc ('I', msg->fp);
2639     fputc ('\n', msg->fp);
2640   }
2641
2642 #ifdef MIXMASTER
2643   /* (postponement) if the mail is to be sent through a mixmaster 
2644    * chain, save that information
2645    */
2646   
2647   if (post && hdr->chain && hdr->chain)
2648   {
2649     LIST *p;
2650
2651     fputs ("X-Mutt-Mix:", msg->fp);
2652     for (p = hdr->chain; p; p = p->next)
2653       fprintf (msg->fp, " %s", (char *) p->data);
2654     
2655     fputc ('\n', msg->fp);
2656   }
2657 #endif    
2658
2659   if (tempfp)
2660   {
2661     char sasha[LONG_STRING];
2662     int lines = 0;
2663
2664     mutt_write_mime_body (hdr->content, tempfp);
2665
2666     /* make sure the last line ends with a newline.  Emacs doesn't ensure
2667      * this will happen, and it can cause problems parsing the mailbox   
2668      * later.
2669      */
2670     fseek (tempfp, -1, 2);
2671     if (fgetc (tempfp) != '\n')
2672     {
2673       fseek (tempfp, 0, 2);
2674       fputc ('\n', tempfp);
2675     }
2676
2677     fflush (tempfp);
2678     if (ferror (tempfp))
2679     {
2680       dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2681       fclose (tempfp);
2682       unlink (tempfile);
2683       mx_commit_message (msg, &f);      /* XXX - really? */
2684       mx_close_message (&msg);
2685       mx_close_mailbox (&f, NULL);
2686       return -1;
2687     }
2688
2689     /* count the number of lines */
2690     rewind (tempfp);
2691     while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2692       lines++;
2693     fprintf (msg->fp, "Content-Length: %ld\n", (long) ftell (tempfp));
2694     fprintf (msg->fp, "Lines: %d\n\n", lines);
2695
2696     /* copy the body and clean up */
2697     rewind (tempfp);
2698     r = mutt_copy_stream (tempfp, msg->fp);
2699     if (fclose (tempfp) != 0)
2700       r = -1;
2701     /* if there was an error, leave the temp version */
2702     if (!r)
2703       unlink (tempfile);
2704   }
2705   else
2706   {
2707     fputc ('\n', msg->fp); /* finish off the header */
2708     r = mutt_write_mime_body (hdr->content, msg->fp);
2709   }
2710
2711   if (mx_commit_message (msg, &f) != 0)
2712     r = -1;
2713   mx_close_message (&msg);
2714   mx_close_mailbox (&f, NULL);
2715
2716   if (post)
2717     set_noconv_flags (hdr->content, 0);
2718   
2719   return r;
2720 }