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