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