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