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