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