Nico Golde:
[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   const char *fqdn;
1844
1845   if (!(fqdn = mutt_fqdn (0)))
1846     fqdn = NONULL (Hostname);
1847
1848   localpart_length = sizeof (buf) - safe_strlen (fqdn) - 4;  /* the 4 characters are '<', '@', '>' and '\0' */
1849
1850   mutt_gen_localpart (localpart, localpart_length, MsgIdFormat);
1851
1852   snprintf (buf, sizeof (buf), "<%s@%s>", localpart, fqdn);
1853   return (safe_strdup (buf));
1854 }
1855
1856 static RETSIGTYPE alarm_handler (int sig)
1857 {
1858   SigAlrm = 1;
1859 }
1860
1861 /* invoke sendmail in a subshell
1862    path (in)            path to program to execute
1863    args (in)            arguments to pass to program
1864    msg (in)             temp file containing message to send
1865    tempfile (out)       if sendmail is put in the background, this points
1866                         to the temporary file containing the stdout of the
1867                         child process */
1868 static int
1869 send_msg (const char *path, char **args, const char *msg, char **tempfile)
1870 {
1871   sigset_t set;
1872   int fd, st;
1873   pid_t pid, ppid;
1874
1875   mutt_block_signals_system ();
1876
1877   sigemptyset (&set);
1878   /* we also don't want to be stopped right now */
1879   sigaddset (&set, SIGTSTP);
1880   sigprocmask (SIG_BLOCK, &set, NULL);
1881
1882   if (SendmailWait >= 0) {
1883     char tmp[_POSIX_PATH_MAX];
1884
1885     mutt_mktemp (tmp);
1886     *tempfile = safe_strdup (tmp);
1887   }
1888
1889   if ((pid = fork ()) == 0) {
1890     struct sigaction act, oldalrm;
1891
1892     /* save parent's ID before setsid() */
1893     ppid = getppid ();
1894
1895     /* we want the delivery to continue even after the main process dies,
1896      * so we put ourselves into another session right away
1897      */
1898     setsid ();
1899
1900     /* next we close all open files */
1901 #if defined(OPEN_MAX)
1902     for (fd = 0; fd < OPEN_MAX; fd++)
1903       close (fd);
1904 #elif defined(_POSIX_OPEN_MAX)
1905     for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1906       close (fd);
1907 #else
1908     close (0);
1909     close (1);
1910     close (2);
1911 #endif
1912
1913     /* now the second fork() */
1914     if ((pid = fork ()) == 0) {
1915       /* "msg" will be opened as stdin */
1916       if (open (msg, O_RDONLY, 0) < 0) {
1917         unlink (msg);
1918         _exit (S_ERR);
1919       }
1920       unlink (msg);
1921
1922       if (SendmailWait >= 0) {
1923         /* *tempfile will be opened as stdout */
1924         if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1925             0)
1926           _exit (S_ERR);
1927         /* redirect stderr to *tempfile too */
1928         if (dup (1) < 0)
1929           _exit (S_ERR);
1930       }
1931       else {
1932         if (open ("/dev/null", O_WRONLY | O_APPEND) < 0)        /* stdout */
1933           _exit (S_ERR);
1934         if (open ("/dev/null", O_RDWR | O_APPEND) < 0)  /* stderr */
1935           _exit (S_ERR);
1936       }
1937
1938       execv (path, args);
1939       _exit (S_ERR);
1940     }
1941     else if (pid == -1) {
1942       unlink (msg);
1943       FREE (tempfile);
1944       _exit (S_ERR);
1945     }
1946
1947     /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1948      * SendmailWait = 0: wait forever
1949      * SendmailWait < 0: don't wait
1950      */
1951     if (SendmailWait > 0) {
1952       SigAlrm = 0;
1953       act.sa_handler = alarm_handler;
1954 #ifdef SA_INTERRUPT
1955       /* need to make sure waitpid() is interrupted on SIGALRM */
1956       act.sa_flags = SA_INTERRUPT;
1957 #else
1958       act.sa_flags = 0;
1959 #endif
1960       sigemptyset (&act.sa_mask);
1961       sigaction (SIGALRM, &act, &oldalrm);
1962       alarm (SendmailWait);
1963     }
1964     else if (SendmailWait < 0)
1965       _exit (0xff & EX_OK);
1966
1967     if (waitpid (pid, &st, 0) > 0) {
1968       st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1969       if (SendmailWait && st == (0xff & EX_OK)) {
1970         unlink (*tempfile);     /* no longer needed */
1971         FREE (tempfile);
1972       }
1973     }
1974     else {
1975       st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1976       if (SendmailWait > 0) {
1977         unlink (*tempfile);
1978         FREE (tempfile);
1979       }
1980     }
1981
1982     /* reset alarm; not really needed, but... */
1983     alarm (0);
1984     sigaction (SIGALRM, &oldalrm, NULL);
1985
1986     if (kill (ppid, 0) == -1 && errno == ESRCH) {
1987       /* the parent is already dead */
1988       unlink (*tempfile);
1989       FREE (tempfile);
1990     }
1991
1992     _exit (st);
1993   }
1994
1995   sigprocmask (SIG_UNBLOCK, &set, NULL);
1996
1997   if (pid != -1 && waitpid (pid, &st, 0) > 0)
1998     st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;     /* return child status */
1999   else
2000     st = S_ERR;                 /* error */
2001
2002   mutt_unblock_signals_system (1);
2003
2004   return (st);
2005 }
2006
2007 static char **add_args (char **args, size_t * argslen, size_t * argsmax,
2008                         ADDRESS * addr)
2009 {
2010   for (; addr; addr = addr->next) {
2011     /* weed out group mailboxes, since those are for display only */
2012     if (addr->mailbox && !addr->group) {
2013       if (*argslen == *argsmax)
2014         safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2015       args[(*argslen)++] = addr->mailbox;
2016     }
2017   }
2018   return (args);
2019 }
2020
2021 static char **add_option (char **args, size_t * argslen, size_t * argsmax,
2022                           char *s)
2023 {
2024   if (*argslen == *argsmax)
2025     safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2026   args[(*argslen)++] = s;
2027   return (args);
2028 }
2029
2030 static const char *strsysexit (int e)
2031 {
2032   int i;
2033
2034   for (i = 0; sysexits_h[i].str; i++) {
2035     if (e == sysexits_h[i].v)
2036       break;
2037   }
2038
2039   return sysexits_h[i].str;
2040 }
2041
2042
2043 static int mutt_invoke_sendmail (ADDRESS * from,        /* the sender */
2044                                  ADDRESS * to, ADDRESS * cc, ADDRESS * bcc,     /* recips */
2045                                  const char *msg,       /* file containing message */
2046                                  int eightbit)
2047 {                               /* message contains 8bit chars */
2048   char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
2049   char **args = NULL;
2050   size_t argslen = 0, argsmax = 0;
2051   int i;
2052
2053 #ifdef USE_NNTP
2054   if (option (OPTNEWSSEND)) {
2055     char cmd[LONG_STRING];
2056
2057     mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0,
2058                        0);
2059     if (!*cmd) {
2060       i = nntp_post (msg);
2061       unlink (msg);
2062       return i;
2063     }
2064
2065     s = safe_strdup (cmd);
2066   }
2067   else
2068 #endif
2069     s = safe_strdup (Sendmail);
2070
2071   ps = s;
2072   i = 0;
2073   while ((ps = strtok (ps, " "))) {
2074     if (argslen == argsmax)
2075       safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2076
2077     if (i)
2078       args[argslen++] = ps;
2079     else {
2080       path = safe_strdup (ps);
2081       ps = strrchr (ps, '/');
2082       if (ps)
2083         ps++;
2084       else
2085         ps = path;
2086       args[argslen++] = ps;
2087     }
2088     ps = NULL;
2089     i++;
2090   }
2091
2092 #ifdef USE_NNTP
2093   if (!option (OPTNEWSSEND)) {
2094 #endif
2095     if (eightbit && option (OPTUSE8BITMIME))
2096       args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2097
2098     if (option (OPTENVFROM) && from && !from->next) {
2099       args = add_option (args, &argslen, &argsmax, "-f");
2100       args = add_args (args, &argslen, &argsmax, from);
2101     }
2102     if (DsnNotify) {
2103       args = add_option (args, &argslen, &argsmax, "-N");
2104       args = add_option (args, &argslen, &argsmax, DsnNotify);
2105     }
2106     if (DsnReturn) {
2107       args = add_option (args, &argslen, &argsmax, "-R");
2108       args = add_option (args, &argslen, &argsmax, DsnReturn);
2109     }
2110     args = add_option (args, &argslen, &argsmax, "--");
2111     args = add_args (args, &argslen, &argsmax, to);
2112     args = add_args (args, &argslen, &argsmax, cc);
2113     args = add_args (args, &argslen, &argsmax, bcc);
2114 #ifdef USE_NNTP
2115   }
2116 #endif
2117
2118   if (argslen == argsmax)
2119     safe_realloc (&args, sizeof (char *) * (++argsmax));
2120
2121   args[argslen++] = NULL;
2122
2123   if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
2124     if (i != S_BKG) {
2125       const char *e = strsysexit (i);
2126
2127       e = strsysexit (i);
2128       mutt_error (_("Error sending message, child exited %d (%s)."), i,
2129                   NONULL (e));
2130       if (childout) {
2131         struct stat st;
2132
2133         if (stat (childout, &st) == 0 && st.st_size > 0)
2134           mutt_do_pager (_("Output of the delivery process"), childout, 0,
2135                          NULL);
2136       }
2137     }
2138   }
2139   else
2140     unlink (childout);
2141
2142   FREE (&childout);
2143   FREE (&path);
2144   FREE (&s);
2145   FREE (&args);
2146
2147   if (i == (EX_OK & 0xff))
2148     i = 0;
2149   else if (i == S_BKG)
2150     i = 1;
2151   else
2152     i = -1;
2153   return (i);
2154 }
2155
2156 int mutt_invoke_mta (ADDRESS * from,    /* the sender */
2157                      ADDRESS * to, ADDRESS * cc, ADDRESS * bcc, /* recips */
2158                      const char *msg,   /* file containing message */
2159                      int eightbit)
2160 {                               /* message contains 8bit chars */
2161 #ifdef USE_LIBESMTP
2162   if (SmtpHost)
2163     return mutt_invoke_libesmtp (from, to, cc, bcc, msg, eightbit);
2164 #endif
2165
2166   return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
2167 }
2168
2169 /* appends string 'b' to string 'a', and returns the pointer to the new
2170    string. */
2171 char *mutt_append_string (char *a, const char *b)
2172 {
2173   size_t la = safe_strlen (a);
2174
2175   safe_realloc (&a, la + safe_strlen (b) + 1);
2176   strcpy (a + la, b);           /* __STRCPY_CHECKED__ */
2177   return (a);
2178 }
2179
2180 /* returns 1 if char `c' needs to be quoted to protect from shell
2181    interpretation when executing commands in a subshell */
2182 #define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
2183
2184 /* returns 1 if string `s' contains characters which could cause problems
2185    when used on a command line to execute a command */
2186 int mutt_needs_quote (const char *s)
2187 {
2188   while (*s) {
2189     if (INVALID_CHAR (*s))
2190       return 1;
2191     s++;
2192   }
2193   return 0;
2194 }
2195
2196 /* Quote a string to prevent shell escapes when this string is used on the
2197    command line to send mail. */
2198 char *mutt_quote_string (const char *s)
2199 {
2200   char *r, *pr;
2201   size_t rlen;
2202
2203   rlen = safe_strlen (s) + 3;
2204   pr = r = (char *) safe_malloc (rlen);
2205   *pr++ = '"';
2206   while (*s) {
2207     if (INVALID_CHAR (*s)) {
2208       size_t o = pr - r;
2209
2210       safe_realloc (&r, ++rlen);
2211       pr = r + o;
2212       *pr++ = '\\';
2213     }
2214     *pr++ = *s++;
2215   }
2216   *pr++ = '"';
2217   *pr = 0;
2218   return (r);
2219 }
2220
2221 /* For postponing (!final) do the necessary encodings only */
2222 void mutt_prepare_envelope (ENVELOPE * env, int final)
2223 {
2224   char buffer[LONG_STRING];
2225
2226   if (final) {
2227     if (env->bcc && !(env->to || env->cc)) {
2228       /* some MTA's will put an Apparently-To: header field showing the Bcc:
2229        * recipients if there is no To: or Cc: field, so attempt to suppress
2230        * it by using an empty To: field.
2231        */
2232       env->to = rfc822_new_address ();
2233       env->to->group = 1;
2234       env->to->next = rfc822_new_address ();
2235
2236       buffer[0] = 0;
2237       rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2238                   RFC822Specials);
2239
2240       env->to->mailbox = safe_strdup (buffer);
2241     }
2242
2243     mutt_set_followup_to (env);
2244
2245     if (!env->message_id && MsgIdFormat && *MsgIdFormat)
2246       env->message_id = mutt_gen_msgid ();
2247   }
2248
2249   /* Take care of 8-bit => 7-bit conversion. */
2250   rfc2047_encode_adrlist (env->to, "To");
2251   rfc2047_encode_adrlist (env->cc, "Cc");
2252   rfc2047_encode_adrlist (env->bcc, "Bcc");
2253   rfc2047_encode_adrlist (env->from, "From");
2254   rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2255   rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2256
2257   if (env->subject)
2258 #ifdef USE_NNTP
2259     if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2260 #endif
2261     {
2262       rfc2047_encode_string (&env->subject);
2263     }
2264   encode_headers (env->userhdrs);
2265 }
2266
2267 void mutt_unprepare_envelope (ENVELOPE * env)
2268 {
2269   LIST *item;
2270
2271   for (item = env->userhdrs; item; item = item->next)
2272     rfc2047_decode (&item->data);
2273
2274   rfc822_free_address (&env->mail_followup_to);
2275
2276   /* back conversions */
2277   rfc2047_decode_adrlist (env->to);
2278   rfc2047_decode_adrlist (env->cc);
2279   rfc2047_decode_adrlist (env->bcc);
2280   rfc2047_decode_adrlist (env->from);
2281   rfc2047_decode_adrlist (env->reply_to);
2282   rfc2047_decode (&env->subject);
2283 }
2284
2285 static int _mutt_bounce_message (FILE * fp, HEADER * h, ADDRESS * to,
2286                                  const char *resent_from, ADDRESS * env_from)
2287 {
2288   int i, ret = 0;
2289   FILE *f;
2290   char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2291   MESSAGE *msg = NULL;
2292
2293   if (!h) {
2294     /* Try to bounce each message out, aborting if we get any failures. */
2295     for (i = 0; i < Context->msgcount; i++)
2296       if (Context->hdrs[i]->tagged)
2297         ret |=
2298           _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2299                                 env_from);
2300     return ret;
2301   }
2302
2303   /* If we failed to open a message, return with error */
2304   if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2305     return -1;
2306
2307   if (!fp)
2308     fp = msg->fp;
2309
2310   mutt_mktemp (tempfile);
2311   if ((f = safe_fopen (tempfile, "w")) != NULL) {
2312     int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2313
2314     if (!option (OPTBOUNCEDELIVERED))
2315       ch_flags |= CH_WEED_DELIVERED;
2316
2317     fseek (fp, h->offset, 0);
2318     fprintf (f, "Resent-From: %s", resent_from);
2319     fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2320     if (MsgIdFormat && *MsgIdFormat)
2321       fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid ());
2322     fputs ("Resent-To: ", f);
2323     mutt_write_address_list (to, f, 11, 0);
2324     mutt_copy_header (fp, h, f, ch_flags, NULL);
2325     fputc ('\n', f);
2326     mutt_copy_bytes (fp, f, h->content->length);
2327     fclose (f);
2328
2329     ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2330                            h->content->encoding == ENC8BIT);
2331   }
2332
2333   if (msg)
2334     mx_close_message (&msg);
2335
2336   return ret;
2337 }
2338
2339 int mutt_bounce_message (FILE * fp, HEADER * h, ADDRESS * to)
2340 {
2341   ADDRESS *from;
2342   const char *fqdn = mutt_fqdn (1);
2343   char resent_from[STRING];
2344   int ret;
2345   char *err;
2346
2347   resent_from[0] = '\0';
2348   from = mutt_default_from ();
2349
2350   if (fqdn)
2351     rfc822_qualify (from, fqdn);
2352
2353   rfc2047_encode_adrlist (from, "Resent-From");
2354   if (mutt_addrlist_to_idna (from, &err)) {
2355     mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2356     return -1;
2357   }
2358   rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2359
2360 #ifdef USE_NNTP
2361   unset_option (OPTNEWSSEND);
2362 #endif
2363
2364   ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2365
2366   rfc822_free_address (&from);
2367
2368   return ret;
2369 }
2370
2371
2372 /* given a list of addresses, return a list of unique addresses */
2373 ADDRESS *mutt_remove_duplicates (ADDRESS * addr)
2374 {
2375   ADDRESS *top = addr;
2376   ADDRESS **last = &top;
2377   ADDRESS *tmp;
2378   int dup;
2379
2380   while (addr) {
2381     for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next) {
2382       if (tmp->mailbox && addr->mailbox &&
2383           !ascii_strcasecmp (addr->mailbox, tmp->mailbox)) {
2384         dup = 1;
2385         break;
2386       }
2387     }
2388
2389     if (dup) {
2390       debug_print (2, ("Removing %s\n", addr->mailbox));
2391
2392       *last = addr->next;
2393
2394       addr->next = NULL;
2395       rfc822_free_address (&addr);
2396
2397       addr = *last;
2398     }
2399     else {
2400       last = &addr->next;
2401       addr = addr->next;
2402     }
2403   }
2404
2405   return (top);
2406 }
2407
2408 static void set_noconv_flags (BODY * b, short flag)
2409 {
2410   for (; b; b = b->next) {
2411     if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2412       set_noconv_flags (b->parts, flag);
2413     else if (b->type == TYPETEXT && b->noconv) {
2414       if (flag)
2415         mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2416       else
2417         mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2418     }
2419   }
2420 }
2421
2422 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2423                     int post, char *fcc)
2424 {
2425   CONTEXT f;
2426   MESSAGE *msg;
2427   char tempfile[_POSIX_PATH_MAX];
2428   FILE *tempfp = NULL;
2429   int r;
2430
2431   if (post)
2432     set_noconv_flags (hdr->content, 1);
2433
2434   if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2435     debug_print (1, ("unable to open mailbox %s in append-mode, aborting.\n", path));
2436     return (-1);
2437   }
2438
2439   /* We need to add a Content-Length field to avoid problems where a line in
2440    * the message body begins with "From "   
2441    */
2442   if (f.magic == M_MMDF || f.magic == M_MBOX) {
2443     mutt_mktemp (tempfile);
2444     if ((tempfp = safe_fopen (tempfile, "w+")) == NULL) {
2445       mutt_perror (tempfile);
2446       mx_close_mailbox (&f, NULL);
2447       return (-1);
2448     }
2449   }
2450
2451   hdr->read = !post;            /* make sure to put it in the `cur' directory (maildir) */
2452   if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2453     mx_close_mailbox (&f, NULL);
2454     return (-1);
2455   }
2456
2457   /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2458    * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header() 
2459    * */
2460   mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0,
2461                             0);
2462
2463   /* (postponment) if this was a reply of some sort, <msgid> contians the
2464    * Message-ID: of message replied to.  Save it using a special X-Mutt-
2465    * header so it can be picked up if the message is recalled at a later
2466    * point in time.  This will allow the message to be marked as replied if
2467    * the same mailbox is still open.
2468    */
2469   if (post && msgid)
2470     fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2471
2472   /* (postponment) save the Fcc: using a special X-Mutt- header so that
2473    * it can be picked up when the message is recalled 
2474    */
2475   if (post && fcc)
2476     fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2477   fprintf (msg->fp, "Status: RO\n");
2478
2479
2480
2481   /* (postponment) if the mail is to be signed or encrypted, save this info */
2482   if ((WithCrypto & APPLICATION_PGP)
2483       && post && (hdr->security & APPLICATION_PGP)) {
2484     fputs ("X-Mutt-PGP: ", msg->fp);
2485     if (hdr->security & ENCRYPT)
2486       fputc ('E', msg->fp);
2487     if (hdr->security & SIGN) {
2488       fputc ('S', msg->fp);
2489       if (PgpSignAs && *PgpSignAs)
2490         fprintf (msg->fp, "<%s>", PgpSignAs);
2491     }
2492     if (hdr->security & INLINE)
2493       fputc ('I', msg->fp);
2494     fputc ('\n', msg->fp);
2495   }
2496
2497   /* (postponment) if the mail is to be signed or encrypted, save this info */
2498   if ((WithCrypto & APPLICATION_SMIME)
2499       && post && (hdr->security & APPLICATION_SMIME)) {
2500     fputs ("X-Mutt-SMIME: ", msg->fp);
2501     if (hdr->security & ENCRYPT) {
2502       fputc ('E', msg->fp);
2503       if (SmimeCryptAlg && *SmimeCryptAlg)
2504         fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2505     }
2506     if (hdr->security & SIGN) {
2507       fputc ('S', msg->fp);
2508       if (SmimeDefaultKey && *SmimeDefaultKey)
2509         fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2510     }
2511     if (hdr->security & INLINE)
2512       fputc ('I', msg->fp);
2513     fputc ('\n', msg->fp);
2514   }
2515
2516 #ifdef MIXMASTER
2517   /* (postponement) if the mail is to be sent through a mixmaster 
2518    * chain, save that information
2519    */
2520
2521   if (post && hdr->chain && hdr->chain) {
2522     LIST *p;
2523
2524     fputs ("X-Mutt-Mix:", msg->fp);
2525     for (p = hdr->chain; p; p = p->next)
2526       fprintf (msg->fp, " %s", (char *) p->data);
2527
2528     fputc ('\n', msg->fp);
2529   }
2530 #endif
2531
2532   if (tempfp) {
2533     char sasha[LONG_STRING];
2534     int lines = 0;
2535
2536     mutt_write_mime_body (hdr->content, tempfp);
2537
2538     /* make sure the last line ends with a newline.  Emacs doesn't ensure
2539      * this will happen, and it can cause problems parsing the mailbox   
2540      * later.
2541      */
2542     fseek (tempfp, -1, 2);
2543     if (fgetc (tempfp) != '\n') {
2544       fseek (tempfp, 0, 2);
2545       fputc ('\n', tempfp);
2546     }
2547
2548     fflush (tempfp);
2549     if (ferror (tempfp)) {
2550       debug_print (1, ("%s: write failed.\n", tempfile));
2551       fclose (tempfp);
2552       unlink (tempfile);
2553       mx_commit_message (msg, &f);      /* XXX - really? */
2554       mx_close_message (&msg);
2555       mx_close_mailbox (&f, NULL);
2556       return -1;
2557     }
2558
2559     /* count the number of lines */
2560     rewind (tempfp);
2561     while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2562       lines++;
2563     fprintf (msg->fp, "Content-Length: %ld\n", (long) ftell (tempfp));
2564     fprintf (msg->fp, "Lines: %d\n\n", lines);
2565
2566     /* copy the body and clean up */
2567     rewind (tempfp);
2568     r = mutt_copy_stream (tempfp, msg->fp);
2569     if (fclose (tempfp) != 0)
2570       r = -1;
2571     /* if there was an error, leave the temp version */
2572     if (!r)
2573       unlink (tempfile);
2574   }
2575   else {
2576     fputc ('\n', msg->fp);      /* finish off the header */
2577     r = mutt_write_mime_body (hdr->content, msg->fp);
2578   }
2579
2580   if (mx_commit_message (msg, &f) != 0)
2581     r = -1;
2582   mx_close_message (&msg);
2583   mx_close_mailbox (&f, NULL);
2584
2585   if (post)
2586     set_noconv_flags (hdr->content, 0);
2587
2588   return r;
2589 }