2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
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.
25 #include <sys/utsname.h>
27 #include <lib-lib/lib-lib.h>
29 #include <lib-sys/exit.h>
30 #include <lib-sys/mutt_signal.h>
32 #include <lib-mime/mime.h>
34 #include <lib-ui/curses.h>
38 #include "recvattach.h"
43 #include <lib-crypt/crypt.h>
44 #include "mutt_idna.h"
47 # include "mutt_libesmtp.h"
48 #endif /* USE_LIBESMTP */
54 #ifdef HAVE_SYSEXITS_H
56 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
60 /* If you are debugging this file, comment out the following line. */
69 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
71 static void transform_to_7bit (BODY * a, FILE * fpin);
73 static void encode_quoted (fgetconv_t * fc, FILE * fout, int istext)
76 char line[77], savechar;
78 while ((c = fgetconv (fc)) != EOF) {
79 /* Wrap the line if needed. */
80 if (linelen == 76 && ((istext && c != '\n') || !istext)) {
81 /* If the last character is "quoted", then be sure to move all three
82 * characters to the next line. Otherwise, just move the last
85 if (line[linelen - 3] == '=') {
86 line[linelen - 3] = 0;
91 line[1] = line[linelen - 2];
92 line[2] = line[linelen - 1];
96 savechar = line[linelen - 1];
97 line[linelen - 1] = '=';
106 /* Escape lines that begin with/only contain "the message separator". */
107 if (linelen == 4 && !m_strncmp("From", line, 4)) {
108 m_strcpy(line, sizeof(line), "=46rom");
111 else if (linelen == 4 && !m_strncmp("from", line, 4)) {
112 m_strcpy(line, sizeof(line), "=66rom");
115 else if (linelen == 1 && line[0] == '.') {
116 m_strcpy(line, sizeof(line), "=2E");
121 if (c == '\n' && istext) {
122 /* Check to make sure there is no trailing space on this line. */
124 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
126 sprintf (line + linelen - 1, "=%2.2X",
127 (unsigned char) line[linelen - 1]);
131 savechar = line[linelen - 1];
133 line[linelen - 1] = '=';
136 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
146 else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
147 /* Check to make sure there is enough room for the quoted character.
148 * If not, wrap to the next line.
151 line[linelen++] = '=';
157 sprintf (line + linelen, "=%2.2X", (unsigned char) c);
161 /* Don't worry about wrapping the line here. That will happen during
162 * the next iteration when I'll also know what the next character is.
168 /* Take care of anything left in the buffer */
170 if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
171 /* take care of trailing whitespace */
173 sprintf (line + linelen - 1, "=%2.2X",
174 (unsigned char) line[linelen - 1]);
176 savechar = line[linelen - 1];
177 line[linelen - 1] = '=';
181 sprintf (line, "=%2.2X", (unsigned char) savechar);
190 static char b64_buffer[3];
191 static short b64_num;
192 static short b64_linelen;
194 static void b64_flush (FILE * fout)
201 if (b64_linelen >= 72) {
206 for (i = b64_num; i < 3; i++)
207 b64_buffer[i] = '\0';
209 fputc(__m_b64chars[(b64_buffer[0] >> 2) & 0x3f], fout);
212 [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
217 [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
221 fputc (__m_b64chars[b64_buffer[2] & 0x3f], fout);
226 while (b64_linelen % 4) {
235 static void b64_putc (char c, FILE * fout)
240 b64_buffer[b64_num++] = c;
244 static void encode_base64 (fgetconv_t * fc, FILE * fout, int istext)
248 b64_num = b64_linelen = 0;
250 while ((ch = fgetconv (fc)) != EOF) {
251 if (istext && ch == '\n' && ch1 != '\r')
252 b64_putc ('\r', fout);
260 static void encode_8bit (fgetconv_t * fc, FILE * fout, int istext)
264 while ((ch = fgetconv (fc)) != EOF)
269 int mutt_write_mime_header (BODY * a, FILE * f)
278 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
283 len = 25 + m_strlen(a->subtype); /* approximate len. of content-type */
285 for (p = a->parameter; p; p = p->next) {
294 tmp = m_strdup(p->value);
295 encode = rfc2231_encode_string (&tmp);
296 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
298 /* Dirty hack to make messages readable by Outlook Express
299 * for the Mac: force quotes around the boundary parameter
300 * even when they aren't needed.
303 if (!ascii_strcasecmp (p->attribute, "boundary")
304 && !strcmp (buffer, tmp))
305 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
309 tmplen = m_strlen(buffer) + m_strlen(p->attribute) + 1;
311 if (len + tmplen + 2 > 76) {
320 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
328 fprintf (f, "Content-Description: %s\n", a->description);
330 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
333 if (!(fn = a->d_filename))
339 /* Strip off the leading path... */
340 if ((t = strrchr (fn, '/')))
347 encode = rfc2231_encode_string (&tmp);
348 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
350 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
356 if (a->encoding != ENC7BIT)
357 fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
359 /* Do NOT add the terminator here!!! */
360 return (ferror (f) ? -1 : 0);
363 int mutt_write_mime_body (BODY * a, FILE * f)
366 char boundary[SHORT_STRING];
367 char send_charset[SHORT_STRING];
372 if (a->type == TYPEMULTIPART) {
373 /* First, find the boundary to use */
374 if (!(p = parameter_getval(a->parameter, "boundary"))) {
375 mutt_error _("No boundary parameter found! [report this error]");
379 m_strcpy(boundary, sizeof(boundary), p);
381 for (t = a->parts; t; t = t->next) {
382 fprintf (f, "\n--%s\n", boundary);
383 if (mutt_write_mime_header (t, f) == -1)
386 if (mutt_write_mime_body (t, f) == -1)
389 fprintf (f, "\n--%s--\n", boundary);
390 return (ferror (f) ? -1 : 0);
393 /* This is pretty gross, but it's the best solution for now... */
394 if (a->type == TYPEAPPLICATION && !m_strcmp(a->subtype, "pgp-encrypted")) {
395 fputs ("Version: 1\n", f);
399 if ((fpin = fopen (a->filename, "r")) == NULL) {
400 mutt_error (_("%s no longer exists!"), a->filename);
404 if (a->type == TYPETEXT && (!a->noconv))
405 fc = fgetconv_open (fpin, a->file_charset,
406 mutt_get_body_charset (send_charset,
407 sizeof (send_charset), a), 0);
409 fc = fgetconv_open (fpin, 0, 0, 0);
411 #define write_as_text_part(a) (mutt_is_text_part(a) || mutt_is_application_pgp(a))
412 if (a->encoding == ENCQUOTEDPRINTABLE)
413 encode_quoted (fc, f, write_as_text_part (a));
414 else if (a->encoding == ENCBASE64)
415 encode_base64 (fc, f, write_as_text_part (a));
416 else if (a->type == TYPETEXT && (!a->noconv))
417 encode_8bit (fc, f, write_as_text_part (a));
419 mutt_copy_stream (fpin, f);
420 #undef write_as_text_part
422 fgetconv_close (&fc);
425 return (ferror (f) ? -1 : 0);
437 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
441 int whitespace = s->whitespace;
443 int linelen = s->linelen;
444 int was_cr = s->was_cr;
446 if (!d) { /* This signals EOF */
449 if (linelen > info->linemax)
450 info->linemax = linelen;
455 for (; dlen; d++, dlen--) {
468 if (linelen > info->linemax)
469 info->linemax = linelen;
484 if (linelen > info->linemax)
485 info->linemax = linelen;
490 else if (ch == '\r') {
498 else if (ch == '\t' || ch == '\f') {
502 else if (ch < 32 || ch == 127)
506 if ((ch == 'F') || (ch == 'f'))
516 if (linelen == 2 && ch != 'r')
518 else if (linelen == 3 && ch != 'o')
520 else if (linelen == 4) {
533 if (ch != ' ' && ch != '\t')
538 s->whitespace = whitespace;
540 s->linelen = linelen;
546 * Find the best charset conversion of the file from fromcode into one
547 * of the tocodes. If successful, set *tocode and CONTENT *info and
548 * return the number of characters converted inexactly. If no
549 * conversion was possible, return -1.
551 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
552 * which would otherwise prevent us from knowing the number of inexact
553 * conversions. Where the candidate target charset is UTF-8 we avoid
554 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
555 * fails with some libraries.
557 * We assume that the output from iconv is never more than 4 times as
558 * long as the input for any pair of charsets we might be interested
561 static ssize_t convert_file_to (FILE * file, const char *fromcode,
562 int ncodes, const char **tocodes,
563 int *tocode, CONTENT * info)
567 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
570 ssize_t ibl, obl, ubl, ubl1, n, ret;
573 CONTENT_STATE *states;
576 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
577 if (cd1 == MUTT_ICONV_ERROR)
580 cd = p_new(iconv_t, ncodes);
581 score = p_new(ssize_t, ncodes);
582 states = p_new(CONTENT_STATE, ncodes);
583 infos = p_new(CONTENT, ncodes);
585 for (i = 0; i < ncodes; i++)
586 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
587 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
589 /* Special case for conversion to UTF-8 */
590 cd[i] = MUTT_ICONV_ERROR, score[i] = -1;
596 /* Try to fill input buffer */
597 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
600 /* Convert to UTF-8 */
602 ob = bufu, obl = sizeof (bufu);
603 n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
604 assert (n == -1 || !n);
605 if (n == -1 && ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
606 assert (errno == EILSEQ ||
607 (errno == EINVAL && ib == bufi && ibl < ssizeof (bufi)));
613 /* Convert from UTF-8 */
614 for (i = 0; i < ncodes; i++)
615 if (cd[i] != MUTT_ICONV_ERROR && score[i] != -1) {
616 ub = bufu, ubl = ubl1;
617 ob = bufo, obl = sizeof (bufo);
618 n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
624 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
627 else if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1)
628 /* Special case for conversion to UTF-8 */
629 update_content_info (&infos[i], &states[i], bufu, ubl1);
632 /* Save unused input */
633 memmove (bufi, ib, ibl);
634 else if (!ubl1 && ib < bufi + sizeof (bufi)) {
641 /* Find best score */
643 for (i = 0; i < ncodes; i++) {
644 if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1) {
645 /* Special case for conversion to UTF-8 */
650 else if (cd[i] == MUTT_ICONV_ERROR || score[i] == -1)
652 else if (ret == -1 || score[i] < ret) {
660 memcpy (info, &infos[*tocode], sizeof (CONTENT));
661 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
665 for (i = 0; i < ncodes; i++)
666 if (cd[i] != MUTT_ICONV_ERROR)
678 #endif /* !HAVE_ICONV */
682 * Find the first of the fromcodes that gives a valid conversion and
683 * the best charset conversion of the file into one of the tocodes. If
684 * successful, set *fromcode and *tocode to dynamically allocated
685 * strings, set CONTENT *info, and return the number of characters
686 * converted inexactly. If no conversion was possible, return -1.
688 * Both fromcodes and tocodes may be colon-separated lists of charsets.
689 * However, if fromcode is zero then fromcodes is assumed to be the
690 * name of a single charset even if it contains a colon.
692 static ssize_t convert_file_from_to (FILE * file,
693 const char *fromcodes,
694 const char *tocodes, char **fromcode,
695 char **tocode, CONTENT * info)
703 /* Count the tocodes */
705 for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
706 if ((c1 = strchr (c, ':')) == c)
712 tcode = p_new(char *, ncodes);
713 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
714 if ((c1 = strchr (c, ':')) == c)
716 tcode[i] = m_substrdup(c, c1);
721 /* Try each fromcode in turn */
722 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
723 if ((c1 = strchr (c, ':')) == c)
725 fcode = m_substrdup(c, c1);
727 ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
739 /* There is only one fromcode */
740 ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
749 for (i = 0; i < ncodes; i++)
758 * Analyze the contents of a file to determine which MIME encoding to use.
759 * Also set the body charset, sometimes, or not.
761 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
766 char *fromcode = NULL;
777 if (stat (fname, &sb) == -1) {
778 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
782 if (!S_ISREG (sb.st_mode)) {
783 mutt_error (_("%s isn't a regular file."), fname);
787 if ((fp = fopen (fname, "r")) == NULL) {
791 info = p_new(CONTENT, 1);
794 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
795 const char *chs = parameter_getval(b->parameter, "charset");
796 char *fchs = b->use_disp ? ((FileCharset && *FileCharset) ?
797 FileCharset : Charset) : Charset;
798 if (Charset && (chs || SendCharset) &&
799 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
800 &fromcode, &tocode, info) != -1) {
802 charset_canonicalize (chsbuf, sizeof (chsbuf), tocode);
803 parameter_setval(&b->parameter, "charset", chsbuf);
805 b->file_charset = fromcode;
813 while ((r = fread (buffer, 1, sizeof (buffer), fp)))
814 update_content_info (info, &state, buffer, r);
815 update_content_info (info, &state, 0, 0);
819 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
820 parameter_setval(&b->parameter, "charset",
821 (!info->hibin ? "us-ascii"
822 : Charset && !charset_is_us_ascii(Charset) ? Charset : "unknown-8bit"));
827 /* Given a file with path ``s'', see if there is a registered MIME type.
828 * returns the major MIME type, and copies the subtype to ``d''. First look
829 * for ~/.mime.types, then look in a system mime.types if we can find one.
830 * The longest match is used so that we can match `ps.gz' when `gz' also
834 int mutt_lookup_mime_type (BODY * att, const char *path)
838 char buf[LONG_STRING];
839 char subtype[STRING], xtype[STRING];
841 int szf, sze, cur_sze;
849 szf = m_strlen(path);
851 for (count = 0; count < 4; count++) {
853 * can't use strtok() because we use it in an inner loop below, so use
854 * a switch statement here instead.
858 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL (Homedir));
861 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
864 m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
867 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
870 goto bye; /* shouldn't happen */
873 if ((f = fopen (buf, "r")) != NULL) {
874 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
875 /* weed out any comments */
876 if ((p = strchr (buf, '#')))
879 /* remove any leading space. */
880 ct = vskipspaces(buf);
882 /* position on the next field in this line */
883 if ((p = strpbrk (ct, " \t")) == NULL)
888 /* cycle through the file extensions */
889 while ((p = strtok (p, " \t\n"))) {
891 if ((sze > cur_sze) && (szf >= sze) &&
892 (m_strcasecmp(path + szf - sze, p) == 0
893 || ascii_strcasecmp (path + szf - sze, p) == 0)
894 && (szf == sze || path[szf - sze - 1] == '.'))
896 /* get the content-type */
898 if ((p = strchr (ct, '/')) == NULL) {
899 /* malformed line, just skip it. */
904 for (q = p; *q && !ISSPACE (*q); q++);
906 m_strncpy(subtype, sizeof(subtype), p, q - p);
908 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
909 m_strcpy(xtype, sizeof(xtype), ct);
922 if (type != TYPEOTHER || *xtype != '\0') {
924 m_strreplace(&att->subtype, subtype);
925 m_strreplace(&att->xtype, xtype);
931 void mutt_message_to_7bit (BODY * a, FILE * fp)
933 char temp[_POSIX_PATH_MAX];
939 if (!a->filename && fp)
941 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
942 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
947 if (stat (a->filename, &sb) == -1) {
948 mutt_perror ("stat");
951 a->length = sb.st_size;
955 if (!(fpout = safe_fopen (temp, "w+"))) {
956 mutt_perror ("fopen");
960 fseeko (fpin, a->offset, 0);
961 a->parts = mutt_parse_messageRFC822 (fpin, a);
963 transform_to_7bit (a->parts, fpin);
965 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
966 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
968 fputs ("MIME-Version: 1.0\n", fpout);
969 mutt_write_mime_header (a->parts, fpout);
971 mutt_write_mime_body (a->parts, fpout);
983 a->encoding = ENC7BIT;
984 a->d_filename = a->filename;
985 if (a->filename && a->unlink)
986 unlink (a->filename);
987 a->filename = m_strdup(temp);
989 if (stat (a->filename, &sb) == -1) {
990 mutt_perror ("stat");
993 a->length = sb.st_size;
994 body_list_wipe(&a->parts);
995 a->hdr->content = NULL;
998 static void transform_to_7bit (BODY * a, FILE * fpin)
1000 char buff[_POSIX_PATH_MAX];
1005 for (; a; a = a->next) {
1006 if (a->type == TYPEMULTIPART) {
1007 if (a->encoding != ENC7BIT)
1008 a->encoding = ENC7BIT;
1010 transform_to_7bit (a->parts, fpin);
1012 else if (mutt_is_message_type (a->type, a->subtype)) {
1013 mutt_message_to_7bit (a, fpin);
1017 a->force_charset = 1;
1020 if ((s.fpout = safe_fopen (buff, "w")) == NULL) {
1021 mutt_perror ("fopen");
1025 mutt_decode_attachment (a, &s);
1027 a->d_filename = a->filename;
1028 a->filename = m_strdup(buff);
1030 if (stat (a->filename, &sb) == -1) {
1031 mutt_perror ("stat");
1034 a->length = sb.st_size;
1036 mutt_update_encoding (a);
1037 if (a->encoding == ENC8BIT)
1038 a->encoding = ENCQUOTEDPRINTABLE;
1039 else if (a->encoding == ENCBINARY)
1040 a->encoding = ENCBASE64;
1045 /* determine which Content-Transfer-Encoding to use */
1046 static void mutt_set_encoding (BODY * b, CONTENT * info)
1048 char send_charset[SHORT_STRING];
1050 if (b->type == TYPETEXT) {
1052 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1053 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1054 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1055 b->encoding = ENCQUOTEDPRINTABLE;
1056 else if (info->hibin)
1057 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1059 b->encoding = ENC7BIT;
1061 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1062 if (info->lobin || info->hibin) {
1063 if (option (OPTALLOW8BIT) && !info->lobin)
1064 b->encoding = ENC8BIT;
1066 mutt_message_to_7bit (b, NULL);
1069 b->encoding = ENC7BIT;
1071 else if (b->type == TYPEAPPLICATION
1072 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1073 b->encoding = ENC7BIT;
1076 /* Determine which encoding is smaller */
1077 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1078 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1079 b->encoding = ENCBASE64;
1081 b->encoding = ENCQUOTEDPRINTABLE;
1085 void mutt_stamp_attachment (BODY * a)
1087 a->stamp = time (NULL);
1090 /* Get a body's character set */
1092 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1096 if (b && b->type != TYPETEXT)
1099 p = b ? parameter_getval(b->parameter, "charset") : NULL;
1100 charset_canonicalize(d, dlen, p);
1105 /* Assumes called from send mode where BODY->filename points to actual file */
1106 void mutt_update_encoding (BODY * a)
1109 char chsbuff[STRING];
1111 /* override noconv when it's us-ascii */
1112 if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1115 if (!a->force_charset && !a->noconv)
1116 parameter_delval(&a->parameter, "charset");
1118 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1121 mutt_set_encoding (a, info);
1122 mutt_stamp_attachment (a);
1124 p_delete(&a->content);
1129 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1131 char buffer[LONG_STRING];
1134 int cmflags, chflags;
1135 int pgp = hdr->security;
1137 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1138 (hdr->security & ENCRYPT)) {
1139 if (!crypt_valid_passphrase (hdr->security))
1143 mutt_mktemp (buffer);
1144 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1148 body->type = TYPEMESSAGE;
1149 body->subtype = m_strdup("rfc822");
1150 body->filename = m_strdup(buffer);
1153 body->disposition = DISPINLINE;
1156 mutt_parse_mime_message (ctx, hdr);
1161 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1162 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1163 chflags |= CH_MIME | CH_TXTPLAIN;
1164 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1165 pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1167 else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1168 if (mutt_is_multipart_encrypted (hdr->content)) {
1169 chflags |= CH_MIME | CH_NONEWLINE;
1170 cmflags = M_CM_DECODE_PGP;
1173 else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1174 chflags |= CH_MIME | CH_TXTPLAIN;
1175 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1178 else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1179 chflags |= CH_MIME | CH_TXTPLAIN;
1180 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1181 pgp &= ~SMIMEENCRYPT;
1185 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1190 body->hdr = header_new();
1191 body->hdr->offset = 0;
1192 /* we don't need the user headers here */
1193 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1194 body->hdr->security = pgp;
1195 mutt_update_encoding (body);
1196 body->parts = body->hdr->content;
1203 BODY *mutt_make_file_attach (const char *path)
1209 att->filename = m_strdup(path);
1211 /* Attempt to determine the appropriate content-type based on the filename
1214 mutt_lookup_mime_type (att, path);
1216 if ((info = mutt_get_content_info (path, att)) == NULL) {
1217 body_list_wipe(&att);
1221 if (!att->subtype) {
1222 if (info->lobin == 0
1223 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1225 * Statistically speaking, there should be more than 10% "lobin"
1226 * chars if this is really a binary file...
1228 att->type = TYPETEXT;
1229 att->subtype = m_strdup("plain");
1231 att->type = TYPEAPPLICATION;
1232 att->subtype = m_strdup("octet-stream");
1236 mutt_update_encoding (att);
1240 static int get_toplevel_encoding (BODY * a)
1244 for (; a; a = a->next) {
1245 if (a->encoding == ENCBINARY)
1247 else if (a->encoding == ENC8BIT)
1254 BODY *mutt_make_multipart (BODY * b)
1259 new->type = TYPEMULTIPART;
1260 new->subtype = m_strdup("mixed");
1261 new->encoding = get_toplevel_encoding (b);
1262 parameter_set_boundary(&new->parameter);
1264 new->disposition = DISPINLINE;
1270 /* remove the multipart body if it exists */
1271 BODY *mutt_remove_multipart (BODY * b)
1284 char *mutt_make_date (char *s, ssize_t len)
1286 time_t t = time (NULL);
1287 struct tm *l = localtime (&t);
1288 time_t tz = mutt_local_tz (t);
1292 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1293 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1294 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1295 (int) tz / 60, (int) abs (tz) % 60);
1299 /* wrapper around mutt_write_address() so we can handle very large
1300 recipient lists without needing a huge temporary buffer in memory */
1301 void mutt_write_address_list (address_t * adr, FILE * fp, int linelen,
1305 char buf[LONG_STRING];
1313 rfc822_write_address (buf, sizeof (buf), adr, display);
1314 len = m_strlen(buf);
1315 if (count && linelen + len > 74) {
1317 linelen = len + 8; /* tab is usually about 8 spaces... */
1320 if (count && adr->mailbox) {
1328 if (!adr->group && adr->next && adr->next->mailbox) {
1338 /* arbitrary number of elements to grow the array by */
1343 /* need to write the list in reverse because they are stored in reverse order
1344 * when parsed to speed up threading
1346 void mutt_write_references (string_list_t * r, FILE * f)
1348 string_list_t **ref = NULL;
1349 int refcnt = 0, refmax = 0;
1351 for (; (TrimRef == 0 || refcnt < TrimRef) && r; r = r->next) {
1352 if (refcnt == refmax)
1353 p_realloc(&ref, refmax += REF_INC);
1357 while (refcnt-- > 0) {
1359 fputs (ref[refcnt]->data, f);
1365 /* Note: all RFC2047 encoding should be done outside of this routine, except
1366 * for the "real name." This will allow this routine to be used more than
1367 * once, if necessary.
1369 * Likewise, all IDN processing should happen outside of this routine.
1371 * mode == 1 => "lite" mode (used for edit_hdrs)
1372 * mode == 0 => normal mode. write full header + MIME headers
1373 * mode == -1 => write just the envelope info (used for postponing messages)
1375 * privacy != 0 => will omit any headers which may identify the user.
1376 * Output generated is suitable for being sent through
1377 * anonymous remailer chains.
1381 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1382 int mode, int privacy)
1384 char buffer[LONG_STRING];
1386 string_list_t *tmp = env->userhdrs;
1387 int has_agent = 0; /* user defined user-agent header field exists */
1388 list2_t* hdrs = list_from_str (EditorHeaders, " ");
1391 if (!option (OPTNEWSSEND))
1393 if (mode == 0 && !privacy)
1394 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1396 #define EDIT_HEADER(x) (mode != 1 || option(OPTXMAILTO) || (mode == 1 && list_lookup(hdrs,(list_lookup_t*) ascii_strcasecmp,x) >= 0))
1398 /* OPTUSEFROM is not consulted here so that we can still write a From:
1399 * field if the user sets it with the `my_hdr' command
1401 if (env->from && !privacy) {
1403 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1404 fprintf (fp, "From: %s\n", buffer);
1409 mutt_write_address_list (env->to, fp, 4, 0);
1413 if (!option (OPTNEWSSEND))
1415 if (EDIT_HEADER("To:"))
1416 fputs ("To:\n", fp);
1420 mutt_write_address_list (env->cc, fp, 4, 0);
1424 if (!option (OPTNEWSSEND))
1426 if (EDIT_HEADER("Cc:"))
1427 fputs ("Cc:\n", fp);
1430 if (mode != 0 || option (OPTWRITEBCC)) {
1431 fputs ("Bcc: ", fp);
1432 mutt_write_address_list (env->bcc, fp, 5, 0);
1437 if (!option (OPTNEWSSEND))
1439 if (EDIT_HEADER("Bcc:"))
1440 fputs ("Bcc:\n", fp);
1443 if (env->newsgroups)
1444 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1445 else if (mode == 1 && option (OPTNEWSSEND) && EDIT_HEADER("Newsgroups:"))
1446 fputs ("Newsgroups:\n", fp);
1448 if (env->followup_to)
1449 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1450 else if (mode == 1 && option (OPTNEWSSEND) && EDIT_HEADER("Followup-To:"))
1451 fputs ("Followup-To:\n", fp);
1453 if (env->x_comment_to)
1454 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1455 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO) &&
1456 EDIT_HEADER("X-Comment-To:"))
1457 fputs ("X-Comment-To:\n", fp);
1461 fprintf (fp, "Subject: %s\n", env->subject);
1462 else if (mode == 1 && EDIT_HEADER("Subject:"))
1463 fputs ("Subject:\n", fp);
1465 /* save message id if the user has set it */
1466 if (env->message_id && !privacy)
1467 fprintf (fp, "Message-ID: %s\n", env->message_id);
1469 if (env->reply_to) {
1470 fputs ("Reply-To: ", fp);
1471 mutt_write_address_list (env->reply_to, fp, 10, 0);
1473 else if (mode > 0 && EDIT_HEADER("Reply-To:"))
1474 fputs ("Reply-To:\n", fp);
1476 if (env->mail_followup_to)
1478 if (!option (OPTNEWSSEND))
1481 fputs ("Mail-Followup-To: ", fp);
1482 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1486 if (env->references) {
1487 fputs ("References:", fp);
1488 mutt_write_references (env->references, fp);
1492 /* Add the MIME headers */
1493 fputs ("MIME-Version: 1.0\n", fp);
1494 mutt_write_mime_header (attach, fp);
1497 if (env->in_reply_to) {
1498 fputs ("In-Reply-To:", fp);
1499 mutt_write_references (env->in_reply_to, fp);
1505 /* Add any user defined headers */
1506 for (; tmp; tmp = tmp->next) {
1507 if ((p = strchr (tmp->data, ':'))) {
1508 p = vskipspaces(p + 1);
1510 continue; /* don't emit empty fields. */
1512 /* check to see if the user has overridden the user-agent field */
1513 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1519 fputs (tmp->data, fp);
1524 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1527 if (OperatingSystem != NULL) {
1528 os = OperatingSystem;
1531 os = (uname(&un) == -1) ? "UNIX" : un.sysname;
1533 /* Add a vanity header */
1534 fprintf (fp, "User-Agent: %s (%s)\n", mutt_make_version (0), os);
1537 list_del (&hdrs, (list_del_t*)xmemfree);
1539 return (ferror (fp) == 0 ? 0 : -1);
1542 static void encode_headers (string_list_t * h)
1548 for (; h; h = h->next) {
1549 if (!(p = strchr (h->data, ':')))
1553 p = vskipspaces(p + 1);
1559 rfc2047_encode_string (&tmp);
1560 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1562 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
1568 const char *mutt_fqdn (short may_hide_host)
1572 if (Fqdn && Fqdn[0] != '@') {
1575 if (may_hide_host && option (OPTHIDDENHOST)) {
1576 if ((p = strchr (Fqdn, '.')))
1579 /* sanity check: don't hide the host if
1580 * the fqdn is something like detebe.org.
1583 if (!p || !(q = strchr (p, '.')))
1591 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1592 static char mutt_normalized_char(char c)
1594 return (isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.';
1597 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1599 #define APPEND_FMT(fmt, arg) \
1601 int snlen = snprintf(buf, len, fmt, arg); \
1606 #define APPEND_BYTE(c) \
1620 static char MsgIdPfx = 'A';
1624 APPEND_BYTE(mutt_normalized_char(c));
1632 APPEND_FMT("%02d", tm->tm_mday);
1635 APPEND_FMT("%02d", tm->tm_hour);
1638 APPEND_FMT("%02d", tm->tm_mon + 1);
1641 APPEND_FMT("%02d", tm->tm_min);
1644 APPEND_FMT("%lo", (unsigned long)now);
1647 APPEND_FMT("%u", (unsigned int)getpid());
1650 APPEND_FMT("%c", MsgIdPfx);
1651 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1654 APPEND_FMT("%u", (unsigned int)rand());
1657 APPEND_FMT("%x", (unsigned int)rand());
1660 APPEND_FMT("%02d", tm->tm_sec);
1663 APPEND_FMT("%u", (unsigned int) now);
1666 APPEND_FMT("%x", (unsigned int) now);
1668 case 'Y': /* this will break in the year 10000 ;-) */
1669 APPEND_FMT("%04d", tm->tm_year + 1900);
1674 default: /* invalid formats are replaced by '.' */
1676 m_strncat(buf, len, ".", 1);
1686 char *mutt_gen_msgid (void)
1688 char buf[SHORT_STRING];
1689 char localpart[SHORT_STRING];
1690 unsigned int localpart_length;
1693 if (!(fqdn = mutt_fqdn (0)))
1694 fqdn = NONULL (Hostname);
1696 localpart_length = sizeof (buf) - m_strlen(fqdn) - 4; /* the 4 characters are '<', '@', '>' and '\0' */
1698 mutt_gen_localpart (localpart, localpart_length, MsgIdFormat);
1700 snprintf (buf, sizeof (buf), "<%s@%s>", localpart, fqdn);
1701 return (m_strdup(buf));
1704 static RETSIGTYPE alarm_handler (int sig)
1709 /* invoke sendmail in a subshell
1710 path (in) path to program to execute
1711 args (in) arguments to pass to program
1712 msg (in) temp file containing message to send
1713 tempfile (out) if sendmail is put in the background, this points
1714 to the temporary file containing the stdout of the
1717 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1723 mutt_block_signals_system ();
1726 /* we also don't want to be stopped right now */
1727 sigaddset (&set, SIGTSTP);
1728 sigprocmask (SIG_BLOCK, &set, NULL);
1730 if (SendmailWait >= 0) {
1731 char tmp[_POSIX_PATH_MAX];
1734 *tempfile = m_strdup(tmp);
1737 if ((pid = fork ()) == 0) {
1738 struct sigaction act, oldalrm;
1740 /* save parent's ID before setsid() */
1743 /* we want the delivery to continue even after the main process dies,
1744 * so we put ourselves into another session right away
1748 /* next we close all open files */
1749 #if defined(OPEN_MAX)
1750 for (fd = 0; fd < OPEN_MAX; fd++)
1752 #elif defined(_POSIX_OPEN_MAX)
1753 for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1761 /* now the second fork() */
1762 if ((pid = fork ()) == 0) {
1763 /* "msg" will be opened as stdin */
1764 if (open (msg, O_RDONLY, 0) < 0) {
1770 if (SendmailWait >= 0) {
1771 /* *tempfile will be opened as stdout */
1772 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1775 /* redirect stderr to *tempfile too */
1780 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1782 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1786 execv (path, (char**)args);
1789 else if (pid == -1) {
1795 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1796 * SendmailWait = 0: wait forever
1797 * SendmailWait < 0: don't wait
1799 if (SendmailWait > 0) {
1801 act.sa_handler = alarm_handler;
1803 /* need to make sure waitpid() is interrupted on SIGALRM */
1804 act.sa_flags = SA_INTERRUPT;
1808 sigemptyset (&act.sa_mask);
1809 sigaction (SIGALRM, &act, &oldalrm);
1810 alarm (SendmailWait);
1812 else if (SendmailWait < 0)
1813 _exit (0xff & EX_OK);
1815 if (waitpid (pid, &st, 0) > 0) {
1816 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1817 if (SendmailWait && st == (0xff & EX_OK)) {
1818 unlink (*tempfile); /* no longer needed */
1823 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1824 if (SendmailWait > 0) {
1830 /* reset alarm; not really needed, but... */
1832 sigaction (SIGALRM, &oldalrm, NULL);
1834 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1835 /* the parent is already dead */
1843 sigprocmask (SIG_UNBLOCK, &set, NULL);
1845 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1846 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1848 st = S_ERR; /* error */
1850 mutt_unblock_signals_system (1);
1855 static const char **
1856 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1858 for (; addr; addr = addr->next) {
1859 /* weed out group mailboxes, since those are for display only */
1860 if (addr->mailbox && !addr->group) {
1861 if (*argslen == *argsmax)
1862 p_realloc(&args, *argsmax += 5);
1863 args[(*argslen)++] = addr->mailbox;
1869 static const char **
1870 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1872 if (*argslen == *argsmax) {
1873 p_realloc(&args, *argsmax += 5);
1875 args[(*argslen)++] = s;
1879 static int mutt_invoke_sendmail (address_t * from, /* the sender */
1880 address_t * to, address_t * cc, address_t * bcc, /* recips */
1881 const char *msg, /* file containing message */
1883 { /* message contains 8bit chars */
1884 char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
1885 const char **args = NULL;
1886 ssize_t argslen = 0, argsmax = 0;
1890 if (option (OPTNEWSSEND)) {
1891 char cmd[LONG_STRING];
1893 mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0,
1896 i = nntp_post (msg);
1905 s = m_strdup(Sendmail);
1909 while ((ps = strtok (ps, " "))) {
1910 if (argslen == argsmax)
1911 p_realloc(&args, argsmax += 5);
1914 args[argslen++] = ps;
1916 path = m_strdup(ps);
1917 ps = strrchr (ps, '/');
1922 args[argslen++] = ps;
1929 if (!option (OPTNEWSSEND)) {
1931 if (eightbit && option (OPTUSE8BITMIME))
1932 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1934 if (option (OPTENVFROM)) {
1935 address_t *f = NULL;
1938 else if (from && !from->next)
1941 args = add_option (args, &argslen, &argsmax, "-f");
1942 args = add_args (args, &argslen, &argsmax, f);
1946 args = add_option (args, &argslen, &argsmax, "-N");
1947 args = add_option (args, &argslen, &argsmax, DsnNotify);
1950 args = add_option (args, &argslen, &argsmax, "-R");
1951 args = add_option (args, &argslen, &argsmax, DsnReturn);
1953 args = add_option (args, &argslen, &argsmax, "--");
1954 args = add_args (args, &argslen, &argsmax, to);
1955 args = add_args (args, &argslen, &argsmax, cc);
1956 args = add_args (args, &argslen, &argsmax, bcc);
1961 if (argslen == argsmax)
1962 p_realloc(&args, ++argsmax);
1964 args[argslen++] = NULL;
1966 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1968 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1973 if (stat (childout, &st) == 0 && st.st_size > 0)
1974 mutt_do_pager (_("Output of the delivery process"), childout, 0,
1982 p_delete(&childout);
1987 if (i == (EX_OK & 0xff))
1989 else if (i == S_BKG)
1996 int mutt_invoke_mta (address_t * from, /* the sender */
1997 address_t * to, address_t * cc, address_t * bcc, /* recips */
1998 const char *msg, /* file containing message */
2000 { /* message contains 8bit chars */
2003 if (!option (OPTNEWSSEND))
2006 return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
2009 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
2012 /* For postponing (!final) do the necessary encodings only */
2013 void mutt_prepare_envelope (ENVELOPE * env, int final)
2015 char buffer[LONG_STRING];
2018 if (env->bcc && !(env->to || env->cc)) {
2019 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2020 * recipients if there is no To: or Cc: field, so attempt to suppress
2021 * it by using an empty To: field.
2023 env->to = address_new ();
2025 env->to->next = address_new ();
2028 rfc822_strcpy(buffer, sizeof(buffer), "undisclosed-recipients",
2031 env->to->mailbox = m_strdup(buffer);
2034 mutt_set_followup_to (env);
2036 if (!env->message_id && MsgIdFormat && *MsgIdFormat)
2037 env->message_id = mutt_gen_msgid ();
2040 /* Take care of 8-bit => 7-bit conversion. */
2041 rfc2047_encode_adrlist (env->to, "To");
2042 rfc2047_encode_adrlist (env->cc, "Cc");
2043 rfc2047_encode_adrlist (env->bcc, "Bcc");
2044 rfc2047_encode_adrlist (env->from, "From");
2045 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2046 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2050 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2053 rfc2047_encode_string (&env->subject);
2055 encode_headers (env->userhdrs);
2058 void mutt_unprepare_envelope (ENVELOPE * env)
2060 string_list_t *item;
2062 for (item = env->userhdrs; item; item = item->next)
2063 rfc2047_decode (&item->data);
2065 address_list_wipe(&env->mail_followup_to);
2067 /* back conversions */
2068 rfc2047_decode_adrlist (env->to);
2069 rfc2047_decode_adrlist (env->cc);
2070 rfc2047_decode_adrlist (env->bcc);
2071 rfc2047_decode_adrlist (env->from);
2072 rfc2047_decode_adrlist (env->reply_to);
2073 rfc2047_decode (&env->subject);
2076 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
2077 const char *resent_from, address_t * env_from)
2081 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2082 MESSAGE *msg = NULL;
2085 /* Try to bounce each message out, aborting if we get any failures. */
2086 for (i = 0; i < Context->msgcount; i++)
2087 if (Context->hdrs[i]->tagged)
2089 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2094 /* If we failed to open a message, return with error */
2095 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2101 mutt_mktemp (tempfile);
2102 if ((f = safe_fopen (tempfile, "w")) != NULL) {
2103 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2105 if (!option (OPTBOUNCEDELIVERED))
2106 ch_flags |= CH_WEED_DELIVERED;
2108 fseeko (fp, h->offset, 0);
2109 fprintf (f, "Resent-From: %s", resent_from);
2110 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2111 if (MsgIdFormat && *MsgIdFormat)
2112 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid ());
2113 fputs ("Resent-To: ", f);
2114 mutt_write_address_list (to, f, 11, 0);
2115 mutt_copy_header (fp, h, f, ch_flags, NULL);
2117 mutt_copy_bytes (fp, f, h->content->length);
2120 ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2121 h->content->encoding == ENC8BIT);
2125 mx_close_message (&msg);
2130 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2133 const char *fqdn = mutt_fqdn (1);
2134 char resent_from[STRING];
2138 resent_from[0] = '\0';
2139 from = mutt_default_from ();
2142 rfc822_qualify (from, fqdn);
2144 rfc2047_encode_adrlist (from, "Resent-From");
2145 if (mutt_addrlist_to_idna (from, &err)) {
2146 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2149 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2152 unset_option (OPTNEWSSEND);
2155 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2157 address_list_wipe(&from);
2162 static void set_noconv_flags (BODY * b, short flag)
2164 for (; b; b = b->next) {
2165 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2166 set_noconv_flags (b->parts, flag);
2167 else if (b->type == TYPETEXT && b->noconv) {
2168 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2173 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2174 int post, char *fcc)
2178 char tempfile[_POSIX_PATH_MAX];
2179 FILE *tempfp = NULL;
2183 set_noconv_flags (hdr->content, 1);
2185 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2189 /* We need to add a Content-Length field to avoid problems where a line in
2190 * the message body begins with "From "
2192 if (f.magic == M_MMDF || f.magic == M_MBOX) {
2193 mutt_mktemp (tempfile);
2194 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL) {
2195 mutt_perror (tempfile);
2196 mx_close_mailbox (&f, NULL);
2201 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2202 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2203 mx_close_mailbox (&f, NULL);
2207 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2208 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2210 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0,
2213 /* (postponment) if this was a reply of some sort, <msgid> contians the
2214 * Message-ID: of message replied to. Save it using a special X-Mutt-
2215 * header so it can be picked up if the message is recalled at a later
2216 * point in time. This will allow the message to be marked as replied if
2217 * the same mailbox is still open.
2220 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2222 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2223 * it can be picked up when the message is recalled
2226 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2227 fprintf (msg->fp, "Status: RO\n");
2231 /* (postponment) if the mail is to be signed or encrypted, save this info */
2232 if (post && (hdr->security & APPLICATION_PGP)) {
2233 fputs ("X-Mutt-PGP: ", msg->fp);
2234 if (hdr->security & ENCRYPT)
2235 fputc ('E', msg->fp);
2236 if (hdr->security & SIGN) {
2237 fputc ('S', msg->fp);
2238 if (PgpSignAs && *PgpSignAs)
2239 fprintf (msg->fp, "<%s>", PgpSignAs);
2241 if (hdr->security & INLINE)
2242 fputc ('I', msg->fp);
2243 fputc ('\n', msg->fp);
2246 /* (postponment) if the mail is to be signed or encrypted, save this info */
2247 if (post && (hdr->security & APPLICATION_SMIME)) {
2248 fputs ("X-Mutt-SMIME: ", msg->fp);
2249 if (hdr->security & ENCRYPT) {
2250 fputc ('E', msg->fp);
2251 if (SmimeCryptAlg && *SmimeCryptAlg)
2252 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2254 if (hdr->security & SIGN) {
2255 fputc ('S', msg->fp);
2256 if (SmimeDefaultKey && *SmimeDefaultKey)
2257 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2259 if (hdr->security & INLINE)
2260 fputc ('I', msg->fp);
2261 fputc ('\n', msg->fp);
2265 /* (postponement) if the mail is to be sent through a mixmaster
2266 * chain, save that information
2269 if (post && hdr->chain && hdr->chain) {
2272 fputs ("X-Mutt-Mix:", msg->fp);
2273 for (p = hdr->chain; p; p = p->next)
2274 fprintf (msg->fp, " %s", (char *) p->data);
2276 fputc ('\n', msg->fp);
2281 char sasha[LONG_STRING];
2284 mutt_write_mime_body (hdr->content, tempfp);
2286 /* make sure the last line ends with a newline. Emacs doesn't ensure
2287 * this will happen, and it can cause problems parsing the mailbox
2290 fseeko (tempfp, -1, 2);
2291 if (fgetc (tempfp) != '\n') {
2292 fseeko (tempfp, 0, 2);
2293 fputc ('\n', tempfp);
2297 if (ferror (tempfp)) {
2300 mx_commit_message (msg, &f); /* XXX - really? */
2301 mx_close_message (&msg);
2302 mx_close_mailbox (&f, NULL);
2306 /* count the number of lines */
2308 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2310 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2311 fprintf (msg->fp, "Lines: %d\n\n", lines);
2313 /* copy the body and clean up */
2315 r = mutt_copy_stream (tempfp, msg->fp);
2316 if (fclose (tempfp) != 0)
2318 /* if there was an error, leave the temp version */
2323 fputc ('\n', msg->fp); /* finish off the header */
2324 r = mutt_write_mime_body (hdr->content, msg->fp);
2327 if (mx_commit_message (msg, &f) != 0)
2329 mx_close_message (&msg);
2330 mx_close_mailbox (&f, NULL);
2333 set_noconv_flags (hdr->content, 0);