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