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