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.
10 #include <lib-lib/lib-lib.h>
14 #include <lib-lua/lib-lua.h>
15 #include <lib-sys/exit.h>
16 #include <lib-sys/mutt_signal.h>
17 #include <lib-mime/mime.h>
18 #include <lib-ui/enter.h>
19 #include <lib-ui/lib-ui.h>
20 #include <lib-mx/mx.h>
25 #include "recvattach.h"
29 #include "mutt_idna.h"
35 #ifdef HAVE_SYSEXITS_H
37 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
41 static void transform_to_7bit (BODY * a, FILE * fpin);
43 static void encode_quoted (fgetconv_t * fc, FILE * fout, int istext)
46 char line[77], savechar;
48 while ((c = fgetconv (fc)) != EOF) {
49 /* Wrap the line if needed. */
50 if (linelen == 76 && ((istext && c != '\n') || !istext)) {
51 /* If the last character is "quoted", then be sure to move all three
52 * characters to the next line. Otherwise, just move the last
55 if (line[linelen - 3] == '=') {
56 line[linelen - 3] = 0;
61 line[1] = line[linelen - 2];
62 line[2] = line[linelen - 1];
66 savechar = line[linelen - 1];
67 line[linelen - 1] = '=';
76 /* Escape lines that begin with/only contain "the message separator". */
77 if (linelen == 4 && !m_strncmp("From", line, 4)) {
78 m_strcpy(line, sizeof(line), "=46rom");
81 else if (linelen == 4 && !m_strncmp("from", line, 4)) {
82 m_strcpy(line, sizeof(line), "=66rom");
85 else if (linelen == 1 && line[0] == '.') {
86 m_strcpy(line, sizeof(line), "=2E");
91 if (c == '\n' && istext) {
92 /* Check to make sure there is no trailing space on this line. */
94 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
96 sprintf (line + linelen - 1, "=%2.2X",
97 (unsigned char) line[linelen - 1]);
101 savechar = line[linelen - 1];
103 line[linelen - 1] = '=';
106 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
116 else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
117 /* Check to make sure there is enough room for the quoted character.
118 * If not, wrap to the next line.
121 line[linelen++] = '=';
127 sprintf (line + linelen, "=%2.2X", (unsigned char) c);
131 /* Don't worry about wrapping the line here. That will happen during
132 * the next iteration when I'll also know what the next character is.
138 /* Take care of anything left in the buffer */
140 if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
141 /* take care of trailing whitespace */
143 sprintf (line + linelen - 1, "=%2.2X",
144 (unsigned char) line[linelen - 1]);
146 savechar = line[linelen - 1];
147 line[linelen - 1] = '=';
151 sprintf (line, "=%2.2X", (unsigned char) savechar);
160 static char b64_buffer[3];
161 static short b64_num;
162 static short b64_linelen;
164 static void b64_flush (FILE * fout)
171 if (b64_linelen >= 72) {
176 for (i = b64_num; i < 3; i++)
177 b64_buffer[i] = '\0';
179 fputc(__m_b64chars[(b64_buffer[0] >> 2) & 0x3f], fout);
182 [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
187 [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
191 fputc (__m_b64chars[b64_buffer[2] & 0x3f], fout);
196 while (b64_linelen % 4) {
205 static void b64_putc (char c, FILE * fout)
210 b64_buffer[b64_num++] = c;
214 static void encode_base64 (fgetconv_t * fc, FILE * fout, int istext)
218 b64_num = b64_linelen = 0;
220 while ((ch = fgetconv (fc)) != EOF) {
221 if (istext && ch == '\n' && ch1 != '\r')
222 b64_putc ('\r', fout);
230 static void encode_8bit (fgetconv_t * fc, FILE * fout,
231 int istext __attribute__ ((unused)))
235 while ((ch = fgetconv (fc)) != EOF)
240 int mutt_write_mime_header (BODY * a, FILE * f)
249 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
254 len = 25 + m_strlen(a->subtype); /* approximate len. of content-type */
256 for (p = a->parameter; p; p = p->next) {
265 tmp = m_strdup(p->value);
266 encode = rfc2231_encode_string (&tmp);
267 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
269 /* Dirty hack to make messages readable by Outlook Express
270 * for the Mac: force quotes around the boundary parameter
271 * even when they aren't needed.
274 if (!ascii_strcasecmp (p->attribute, "boundary")
275 && !strcmp (buffer, tmp))
276 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
280 tmplen = m_strlen(buffer) + m_strlen(p->attribute) + 1;
282 if (len + tmplen + 2 > 76) {
291 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
299 fprintf (f, "Content-Description: %s\n", a->description);
301 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
302 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
305 if (!(fn = a->d_filename))
311 /* Strip off the leading path... */
312 if ((t = strrchr (fn, '/')))
319 encode = rfc2231_encode_string (&tmp);
320 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
322 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
328 if (a->encoding != ENC7BIT)
329 fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
331 /* Do NOT add the terminator here!!! */
332 return (ferror (f) ? -1 : 0);
335 int mutt_write_mime_body (BODY * a, FILE * f)
338 char boundary[STRING];
339 char send_charset[STRING];
344 if (a->type == TYPEMULTIPART) {
345 /* First, find the boundary to use */
346 if (!(p = parameter_getval(a->parameter, "boundary"))) {
347 mutt_error _("No boundary parameter found! [report this error]");
351 m_strcpy(boundary, sizeof(boundary), p);
353 for (t = a->parts; t; t = t->next) {
354 fprintf (f, "\n--%s\n", boundary);
355 if (mutt_write_mime_header (t, f) == -1)
358 if (mutt_write_mime_body (t, f) == -1)
361 fprintf (f, "\n--%s--\n", boundary);
362 return (ferror (f) ? -1 : 0);
365 /* This is pretty gross, but it's the best solution for now... */
366 if (a->type == TYPEAPPLICATION && !m_strcmp(a->subtype, "pgp-encrypted")) {
367 fputs ("Version: 1\n", f);
371 if ((fpin = fopen (a->filename, "r")) == NULL) {
372 mutt_error (_("%s no longer exists!"), a->filename);
376 if (a->type == TYPETEXT && (!a->noconv))
377 fc = fgetconv_open (fpin, a->file_charset,
378 mutt_get_body_charset (send_charset,
379 sizeof (send_charset), a), 0);
381 fc = fgetconv_open (fpin, 0, 0, 0);
383 #define write_as_text_part(a) (mutt_is_text_part(a) || mutt_is_application_pgp(a))
384 if (a->encoding == ENCQUOTEDPRINTABLE)
385 encode_quoted (fc, f, write_as_text_part (a));
386 else if (a->encoding == ENCBASE64)
387 encode_base64 (fc, f, write_as_text_part (a));
388 else if (a->type == TYPETEXT && (!a->noconv))
389 encode_8bit (fc, f, write_as_text_part (a));
391 mutt_copy_stream (fpin, f);
392 #undef write_as_text_part
394 fgetconv_close (&fc);
397 return (ferror (f) ? -1 : 0);
409 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
413 int whitespace = s->whitespace;
415 int linelen = s->linelen;
416 int was_cr = s->was_cr;
418 if (!d) { /* This signals EOF */
421 if (linelen > info->linemax)
422 info->linemax = linelen;
427 for (; dlen; d++, dlen--) {
440 if (linelen > info->linemax)
441 info->linemax = linelen;
456 if (linelen > info->linemax)
457 info->linemax = linelen;
462 else if (ch == '\r') {
470 else if (ch == '\t' || ch == '\f') {
474 else if (ch < 32 || ch == 127)
478 if ((ch == 'F') || (ch == 'f'))
488 if (linelen == 2 && ch != 'r')
490 else if (linelen == 3 && ch != 'o')
492 else if (linelen == 4) {
505 if (ch != ' ' && ch != '\t')
510 s->whitespace = whitespace;
512 s->linelen = linelen;
518 * Find the best charset conversion of the file from fromcode into one
519 * of the tocodes. If successful, set *tocode and CONTENT *info and
520 * return the number of characters converted inexactly. If no
521 * conversion was possible, return -1.
523 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
524 * which would otherwise prevent us from knowing the number of inexact
525 * conversions. Where the candidate target charset is UTF-8 we avoid
526 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
527 * fails with some libraries.
529 * We assume that the output from iconv is never more than 4 times as
530 * long as the input for any pair of charsets we might be interested
533 static ssize_t convert_file_to (FILE * file, const char *fromcode,
534 int ncodes, const char **tocodes,
535 int *tocode, CONTENT * info)
538 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
541 ssize_t ibl, obl, ubl, ubl1, n, ret;
544 CONTENT_STATE *states;
547 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
548 if (cd1 == MUTT_ICONV_ERROR)
551 cd = p_new(iconv_t, ncodes);
552 score = p_new(ssize_t, ncodes);
553 states = p_new(CONTENT_STATE, ncodes);
554 infos = p_new(CONTENT, ncodes);
556 for (i = 0; i < ncodes; i++)
557 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
558 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
560 /* Special case for conversion to UTF-8 */
561 cd[i] = MUTT_ICONV_ERROR, score[i] = -1;
567 /* Try to fill input buffer */
568 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
571 /* Convert to UTF-8 */
573 ob = bufu, obl = sizeof (bufu);
574 n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
575 if (n == -1 && ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
581 /* Convert from UTF-8 */
582 for (i = 0; i < ncodes; i++)
583 if (cd[i] != MUTT_ICONV_ERROR && score[i] != -1) {
584 ub = bufu, ubl = ubl1;
585 ob = bufo, obl = sizeof (bufo);
586 n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
592 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
595 else if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1)
596 /* Special case for conversion to UTF-8 */
597 update_content_info (&infos[i], &states[i], bufu, ubl1);
600 /* Save unused input */
601 memmove (bufi, ib, ibl);
602 else if (!ubl1 && ib < bufi + sizeof (bufi)) {
609 /* Find best score */
611 for (i = 0; i < ncodes; i++) {
612 if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1) {
613 /* Special case for conversion to UTF-8 */
618 else if (cd[i] == MUTT_ICONV_ERROR || score[i] == -1)
620 else if (ret == -1 || score[i] < ret) {
628 memcpy (info, &infos[*tocode], sizeof (CONTENT));
629 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
633 for (i = 0; i < ncodes; i++)
634 if (cd[i] != MUTT_ICONV_ERROR)
647 * Find the first of the fromcodes that gives a valid conversion and
648 * the best charset conversion of the file into one of the tocodes. If
649 * successful, set *fromcode and *tocode to dynamically allocated
650 * strings, set CONTENT *info, and return the number of characters
651 * converted inexactly. If no conversion was possible, return -1.
653 * Both fromcodes and tocodes may be colon-separated lists of charsets.
654 * However, if fromcode is zero then fromcodes is assumed to be the
655 * name of a single charset even if it contains a colon.
657 static ssize_t convert_file_from_to (FILE * file,
658 const char *fromcodes,
659 const char *tocodes, char **fromcode,
660 char **tocode, CONTENT * info)
668 /* Count the tocodes */
670 for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
671 if ((c1 = strchr (c, ':')) == c)
677 tcode = p_new(char *, ncodes);
678 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
679 if ((c1 = strchr (c, ':')) == c)
681 tcode[i] = m_substrdup(c, c1);
686 /* Try each fromcode in turn */
687 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
688 if ((c1 = strchr (c, ':')) == c)
690 fcode = m_substrdup(c, c1);
692 ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
704 /* There is only one fromcode */
705 ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
714 for (i = 0; i < ncodes; i++)
723 * Analyze the contents of a file to determine which MIME encoding to use.
724 * Also set the body charset, sometimes, or not.
726 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
731 char *fromcode = NULL;
742 if (stat (fname, &sb) == -1) {
743 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
747 if (!S_ISREG (sb.st_mode)) {
748 mutt_error (_("%s isn't a regular file."), fname);
752 if ((fp = fopen (fname, "r")) == NULL) {
756 info = p_new(CONTENT, 1);
759 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
760 const char *chs = parameter_getval(b->parameter, "charset");
761 char *fchs = b->use_disp && !m_strisempty(mod_cset.file_charset)
762 ? FileCharset : mod_cset.charset;
763 if (mod_cset.charset && (chs || mod_cset.send_charset) &&
764 convert_file_from_to (fp, fchs, chs ? chs : mod_cset.send_charset,
765 &fromcode, &tocode, info) != -1) {
767 charset_canonicalize (chsbuf, sizeof (chsbuf), tocode);
768 parameter_setval(&b->parameter, "charset", chsbuf);
770 b->file_charset = fromcode;
778 while ((r = fread (buffer, 1, sizeof (buffer), fp)))
779 update_content_info (info, &state, buffer, r);
780 update_content_info (info, &state, 0, 0);
784 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
785 parameter_setval(&b->parameter, "charset",
786 (!info->hibin ? "us-ascii"
787 : mod_cset.charset && !charset_is_us_ascii(mod_cset.charset)
788 ? mod_cset.charset : "unknown-8bit"));
793 /* Given a file with path ``s'', see if there is a registered MIME type.
794 * returns the major MIME type, and copies the subtype to ``d''. First look
795 * for ~/.mime.types, then look in a system mime.types if we can find one.
796 * The longest match is used so that we can match `ps.gz' when `gz' also
800 int mutt_lookup_mime_type (BODY * att, const char *path)
804 char buf[LONG_STRING];
805 char subtype[STRING], xtype[STRING];
807 int szf, sze, cur_sze;
815 szf = m_strlen(path);
817 for (count = 0; count < 4; count++) {
819 * can't use strtok() because we use it in an inner loop below, so use
820 * a switch statement here instead.
824 snprintf(buf, sizeof (buf), "%s/.mime.types", NONULL(mod_core.homedir));
827 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
830 m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
833 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
836 goto bye; /* shouldn't happen */
839 if ((f = fopen (buf, "r")) != NULL) {
840 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
841 /* weed out any comments */
842 if ((p = strchr (buf, '#')))
845 /* remove any leading space. */
846 ct = vskipspaces(buf);
848 /* position on the next field in this line */
849 if ((p = strpbrk (ct, " \t")) == NULL)
854 /* cycle through the file extensions */
855 while ((p = strtok (p, " \t\n"))) {
857 if ((sze > cur_sze) && (szf >= sze) &&
858 (m_strcasecmp(path + szf - sze, p) == 0
859 || ascii_strcasecmp (path + szf - sze, p) == 0)
860 && (szf == sze || path[szf - sze - 1] == '.'))
862 /* get the content-type */
864 if ((p = strchr (ct, '/')) == NULL) {
865 /* malformed line, just skip it. */
870 for (q = p; *q && !ISSPACE (*q); q++);
872 m_strncpy(subtype, sizeof(subtype), p, q - p);
874 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
875 m_strcpy(xtype, sizeof(xtype), ct);
888 if (type != TYPEOTHER || *xtype != '\0') {
890 m_strreplace(&att->subtype, subtype);
891 m_strreplace(&att->xtype, xtype);
897 void mutt_message_to_7bit (BODY * a, FILE * fp)
899 char temp[_POSIX_PATH_MAX];
905 if (!a->filename && fp)
907 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
908 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
913 if (stat (a->filename, &sb) == -1) {
914 mutt_perror ("stat");
917 a->length = sb.st_size;
920 fpout = m_tempfile(temp, sizeof(temp), NONULL(mod_core.tmpdir), NULL);
922 mutt_error(_("Could not create temporary file"));
926 fseeko (fpin, a->offset, 0);
927 a->parts = mutt_parse_messageRFC822 (fpin, a);
929 transform_to_7bit (a->parts, fpin);
931 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
932 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
934 fputs ("MIME-Version: 1.0\n", fpout);
935 mutt_write_mime_header (a->parts, fpout);
937 mutt_write_mime_body (a->parts, fpout);
949 a->encoding = ENC7BIT;
950 a->d_filename = a->filename;
951 if (a->filename && a->unlink)
952 unlink (a->filename);
953 a->filename = m_strdup(temp);
955 if (stat (a->filename, &sb) == -1) {
956 mutt_perror ("stat");
959 a->length = sb.st_size;
960 body_list_wipe(&a->parts);
961 a->hdr->content = NULL;
964 static void transform_to_7bit (BODY * a, FILE * fpin)
966 char buff[_POSIX_PATH_MAX];
971 for (; a; a = a->next) {
972 if (a->type == TYPEMULTIPART) {
973 if (a->encoding != ENC7BIT)
974 a->encoding = ENC7BIT;
976 transform_to_7bit (a->parts, fpin);
978 else if (mutt_is_message_type(a)) {
979 mutt_message_to_7bit (a, fpin);
983 a->force_charset = 1;
985 s.fpout = m_tempfile(buff, sizeof(buff), NONULL(mod_core.tmpdir), NULL);
987 mutt_error(_("Could not create temporary file"));
991 mutt_decode_attachment (a, &s);
993 a->d_filename = a->filename;
994 a->filename = m_strdup(buff);
996 if (stat (a->filename, &sb) == -1) {
997 mutt_perror ("stat");
1000 a->length = sb.st_size;
1002 mutt_update_encoding (a);
1003 if (a->encoding == ENC8BIT)
1004 a->encoding = ENCQUOTEDPRINTABLE;
1005 else if (a->encoding == ENCBINARY)
1006 a->encoding = ENCBASE64;
1011 /* determine which Content-Transfer-Encoding to use */
1012 static void mutt_set_encoding (BODY * b, CONTENT * info)
1014 char send_charset[STRING];
1016 if (b->type == TYPETEXT) {
1018 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1019 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1020 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1021 b->encoding = ENCQUOTEDPRINTABLE;
1022 else if (info->hibin)
1023 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1025 b->encoding = ENC7BIT;
1027 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1028 if (info->lobin || info->hibin) {
1029 if (option (OPTALLOW8BIT) && !info->lobin)
1030 b->encoding = ENC8BIT;
1032 mutt_message_to_7bit (b, NULL);
1035 b->encoding = ENC7BIT;
1037 else if (b->type == TYPEAPPLICATION
1038 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1039 b->encoding = ENC7BIT;
1042 /* Determine which encoding is smaller */
1043 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1044 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1045 b->encoding = ENCBASE64;
1047 b->encoding = ENCQUOTEDPRINTABLE;
1051 void mutt_stamp_attachment (BODY * a)
1053 a->stamp = time (NULL);
1056 /* Get a body's character set */
1058 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1062 if (b && b->type != TYPETEXT)
1065 p = b ? parameter_getval(b->parameter, "charset") : NULL;
1066 charset_canonicalize(d, dlen, p);
1071 /* Assumes called from send mode where BODY->filename points to actual file */
1072 void mutt_update_encoding (BODY * a)
1075 char chsbuff[STRING];
1077 /* override noconv when it's us-ascii */
1078 if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1081 if (!a->force_charset && !a->noconv)
1082 parameter_delval(&a->parameter, "charset");
1084 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1087 mutt_set_encoding (a, info);
1088 mutt_stamp_attachment (a);
1090 p_delete(&a->content);
1095 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1097 char buffer[LONG_STRING];
1100 int cmflags, chflags;
1101 int pgp = hdr->security;
1103 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1104 (hdr->security & ENCRYPT)) {
1107 fp = m_tempfile(buffer, sizeof(buffer), NONULL(mod_core.tmpdir), NULL);
1112 body->type = TYPEMESSAGE;
1113 body->subtype = m_strdup("rfc822");
1114 body->filename = m_strdup(buffer);
1117 body->disposition = DISPINLINE;
1120 mutt_parse_mime_message (ctx, hdr);
1125 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1126 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1127 chflags |= CH_MIME | CH_TXTPLAIN;
1128 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1129 pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1131 else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1132 if (mutt_is_multipart_encrypted (hdr->content)) {
1133 chflags |= CH_MIME | CH_NONEWLINE;
1134 cmflags = M_CM_DECODE_PGP;
1137 else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1138 chflags |= CH_MIME | CH_TXTPLAIN;
1139 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1142 else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1143 chflags |= CH_MIME | CH_TXTPLAIN;
1144 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1145 pgp &= ~SMIMEENCRYPT;
1149 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1154 body->hdr = header_new();
1155 body->hdr->offset = 0;
1156 /* we don't need the user headers here */
1157 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1158 body->hdr->security = pgp;
1159 mutt_update_encoding (body);
1160 body->parts = body->hdr->content;
1167 BODY *mutt_make_file_attach (const char *path)
1173 att->filename = m_strdup(path);
1175 /* Attempt to determine the appropriate content-type based on the filename
1178 mutt_lookup_mime_type (att, path);
1180 if ((info = mutt_get_content_info (path, att)) == NULL) {
1181 body_list_wipe(&att);
1185 if (!att->subtype) {
1186 if (info->lobin == 0
1187 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1189 * Statistically speaking, there should be more than 10% "lobin"
1190 * chars if this is really a binary file...
1192 att->type = TYPETEXT;
1193 att->subtype = m_strdup("plain");
1195 att->type = TYPEAPPLICATION;
1196 att->subtype = m_strdup("octet-stream");
1200 mutt_update_encoding (att);
1204 static int get_toplevel_encoding (BODY * a)
1208 for (; a; a = a->next) {
1209 if (a->encoding == ENCBINARY)
1212 if (a->encoding == ENC8BIT)
1219 BODY *mutt_make_multipart (BODY * b)
1224 new->type = TYPEMULTIPART;
1225 new->subtype = m_strdup("mixed");
1226 new->encoding = get_toplevel_encoding (b);
1227 parameter_set_boundary(&new->parameter);
1229 new->disposition = DISPINLINE;
1235 /* remove the multipart body if it exists */
1236 BODY *mutt_remove_multipart (BODY * b)
1249 char *mutt_make_date (char *s, ssize_t len)
1251 time_t t = time (NULL);
1252 struct tm *l = localtime (&t);
1253 time_t tz = mutt_local_tz (t);
1257 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1258 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1259 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1260 (int) tz / 60, (int) abs (tz) % 60);
1264 /* wrapper around mutt_write_address() so we can handle very large
1265 recipient lists without needing a huge temporary buffer in memory */
1267 mutt_write_address_list(address_t *addr, FILE *fp, int linelen, int display)
1272 char buf[LONG_STRING];
1273 int len = rfc822_addrcpy(buf, ssizeof(buf), addr, display);
1276 if (linelen + len > 74) {
1278 linelen = 8; /* tab is usually about 8 spaces... */
1280 if (addr->mailbox) {
1290 if (!addr->group && addr->next && addr->next->mailbox) {
1300 /* need to write the list in reverse because they are stored in reverse order
1301 * when parsed to speed up threading
1303 void mutt_write_references(string_list_t *r, FILE *f)
1305 string_list_t *refs[10];
1308 p_clear(refs, countof(refs));
1309 for (i = 0; i < countof(refs) && r; r = r->next) {
1314 fprintf(f, " %s", refs[i]->data);
1318 static int edit_header(int mode, const char *s)
1321 int slen = m_strlen(s);
1323 if (mode != 1 || option(OPTXMAILTO))
1326 p = skipspaces(EditorHeaders);
1328 if (!ascii_strncasecmp(p, s, slen) && p[slen - 1] == ':')
1330 p = skipspaces(p + slen);
1336 /* Note: all RFC2047 encoding should be done outside of this routine, except
1337 * for the "real name." This will allow this routine to be used more than
1338 * once, if necessary.
1340 * Likewise, all IDN processing should happen outside of this routine.
1342 * mode == 1 => "lite" mode (used for edit_hdrs)
1343 * mode == 0 => normal mode. write full header + MIME headers
1344 * mode == -1 => write just the envelope info (used for postponing messages)
1346 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1349 char buffer[LONG_STRING];
1351 string_list_t *tmp = env->userhdrs;
1352 int has_agent = 0; /* user defined user-agent header field exists */
1355 if (!option (OPTNEWSSEND))
1358 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1360 /* OPTUSEFROM is not consulted here so that we can still write a From:
1361 * field if the user sets it with the `my_hdr' command
1365 rfc822_addrcat(buffer, sizeof(buffer), env->from, 0);
1366 fprintf (fp, "From: %s\n", buffer);
1371 mutt_write_address_list (env->to, fp, 4, 0);
1375 if (!option (OPTNEWSSEND))
1377 if (edit_header(mode, "To:"))
1378 fputs ("To:\n", fp);
1382 mutt_write_address_list (env->cc, fp, 4, 0);
1386 if (!option (OPTNEWSSEND))
1388 if (edit_header(mode, "Cc:"))
1389 fputs ("Cc:\n", fp);
1392 if (mode != 0 || option (OPTWRITEBCC)) {
1393 fputs ("Bcc: ", fp);
1394 mutt_write_address_list (env->bcc, fp, 5, 0);
1399 if (!option (OPTNEWSSEND))
1401 if (edit_header(mode, "Bcc:"))
1402 fputs ("Bcc:\n", fp);
1405 if (env->newsgroups)
1406 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1407 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Newsgroups:"))
1408 fputs ("Newsgroups:\n", fp);
1410 if (env->followup_to)
1411 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1412 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Followup-To:"))
1413 fputs ("Followup-To:\n", fp);
1417 fprintf (fp, "Subject: %s\n", env->subject);
1418 else if (mode == 1 && edit_header(mode, "Subject:"))
1419 fputs ("Subject:\n", fp);
1421 /* save message id if the user has set it */
1422 if (env->message_id)
1423 fprintf (fp, "Message-ID: %s\n", env->message_id);
1425 if (env->reply_to) {
1426 fputs ("Reply-To: ", fp);
1427 mutt_write_address_list (env->reply_to, fp, 10, 0);
1429 else if (mode > 0 && edit_header(mode, "Reply-To:"))
1430 fputs ("Reply-To:\n", fp);
1432 if (env->mail_followup_to)
1434 if (!option (OPTNEWSSEND))
1437 fputs ("Mail-Followup-To: ", fp);
1438 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1442 if (env->references) {
1443 fputs ("References:", fp);
1444 mutt_write_references (env->references, fp);
1448 /* Add the MIME headers */
1449 fputs ("MIME-Version: 1.0\n", fp);
1450 mutt_write_mime_header (attach, fp);
1453 if (env->in_reply_to) {
1454 fputs ("In-Reply-To:", fp);
1455 mutt_write_references (env->in_reply_to, fp);
1459 /* Add any user defined headers */
1460 for (; tmp; tmp = tmp->next) {
1461 if ((p = strchr (tmp->data, ':'))) {
1462 p = vskipspaces(p + 1);
1464 continue; /* don't emit empty fields. */
1466 /* check to see if the user has overridden the user-agent field */
1467 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1471 fputs (tmp->data, fp);
1476 if (mode == 0 && option (OPTXMAILER) && !has_agent) {
1477 if (mod_core.operating_system) {
1478 fprintf(fp, "User-Agent: %s (%s)\n", mutt_make_version(),
1479 mod_core.operating_system);
1481 fprintf(fp, "User-Agent: %s\n", mutt_make_version());
1485 return (ferror (fp) == 0 ? 0 : -1);
1488 static void encode_headers (string_list_t * h)
1494 for (; h; h = h->next) {
1495 if (!(p = strchr (h->data, ':')))
1499 p = vskipspaces(p + 1);
1505 rfc2047_encode_string (&tmp);
1506 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1508 sprintf (h->data + i, ": %s", NONULL (tmp));
1514 const char *mutt_fqdn(short may_hide_host)
1518 if (mod_core.hostname && mod_core.hostname[0] != '@') {
1519 p = mod_core.hostname;
1521 if (may_hide_host && option (OPTHIDDENHOST)) {
1522 if ((p = strchr(mod_core.hostname, '.')))
1525 /* sanity check: don't hide the host if
1526 the fqdn is something like detebe.org. */
1528 if (!p || !(q = strchr(p, '.')))
1529 p = mod_core.hostname;
1536 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1538 #define APPEND_FMT(fmt, arg) \
1540 int snlen = snprintf(buf, len, fmt, arg); \
1545 #define APPEND_BYTE(c) \
1559 static char MsgIdPfx = 'A';
1563 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1564 APPEND_BYTE((isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.');
1572 APPEND_FMT("%02d", tm->tm_mday);
1575 APPEND_FMT("%02d", tm->tm_hour);
1578 APPEND_FMT("%02d", tm->tm_mon + 1);
1581 APPEND_FMT("%02d", tm->tm_min);
1584 APPEND_FMT("%lo", (unsigned long)now);
1587 APPEND_FMT("%u", (unsigned int)getpid());
1590 APPEND_FMT("%c", MsgIdPfx);
1591 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1594 APPEND_FMT("%u", (unsigned int)rand());
1597 APPEND_FMT("%x", (unsigned int)rand());
1600 APPEND_FMT("%02d", tm->tm_sec);
1603 APPEND_FMT("%u", (unsigned int) now);
1606 APPEND_FMT("%x", (unsigned int) now);
1608 case 'Y': /* this will break in the year 10000 ;-) */
1609 APPEND_FMT("%04d", tm->tm_year + 1900);
1614 default: /* invalid formats are replaced by '.' */
1625 static char *mutt_gen_msgid (void)
1628 char localpart[STRING];
1631 if (!(fqdn = mutt_fqdn(0)))
1632 fqdn = NONULL(mod_core.shorthost);
1634 mutt_gen_localpart(localpart, sizeof(localpart), MsgIdFormat);
1635 snprintf(buf, sizeof(buf), "<%s@%s>", localpart, fqdn);
1636 return m_strdup(buf);
1639 static void alarm_handler (int sig __attribute__ ((unused)))
1644 /* invoke sendmail in a subshell
1645 path (in) path to program to execute
1646 args (in) arguments to pass to program
1647 msg (in) temp file containing message to send
1648 tempfile (out) if sendmail is put in the background, this points
1649 to the temporary file containing the stdout of the
1652 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1658 mutt_block_signals_system ();
1661 /* we also don't want to be stopped right now */
1662 sigaddset (&set, SIGTSTP);
1663 sigprocmask (SIG_BLOCK, &set, NULL);
1665 if (MTransport.sendmail_wait >= 0) {
1666 char tmp[_POSIX_PATH_MAX];
1669 *tempfile = m_strdup(tmp);
1672 if ((pid = fork ()) == 0) {
1673 struct sigaction act, oldalrm;
1675 /* save parent's ID before setsid() */
1678 /* we want the delivery to continue even after the main process dies,
1679 * so we put ourselves into another session right away
1683 /* next we close all open files */
1684 for (fd = 0; fd < getdtablesize(); fd++)
1687 /* now the second fork() */
1688 if ((pid = fork ()) == 0) {
1689 /* "msg" will be opened as stdin */
1690 if (open (msg, O_RDONLY, 0) < 0) {
1696 if (MTransport.sendmail_wait >= 0) {
1697 /* *tempfile will be opened as stdout */
1698 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1701 /* redirect stderr to *tempfile too */
1705 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1707 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1711 execv (path, (char**)args);
1714 else if (pid == -1) {
1720 /* sendmail_wait > 0: interrupt waitpid() after sendmail_wait seconds
1721 * sendmail_wait = 0: wait forever
1722 * sendmail_wait < 0: don't wait
1724 if (MTransport.sendmail_wait > 0) {
1726 act.sa_handler = alarm_handler;
1728 /* need to make sure waitpid() is interrupted on SIGALRM */
1729 act.sa_flags = SA_INTERRUPT;
1733 sigemptyset (&act.sa_mask);
1734 sigaction (SIGALRM, &act, &oldalrm);
1735 alarm (MTransport.sendmail_wait);
1737 else if (MTransport.sendmail_wait < 0)
1738 _exit (0xff & EX_OK);
1740 if (waitpid (pid, &st, 0) > 0) {
1741 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1742 if (MTransport.sendmail_wait && st == (0xff & EX_OK)) {
1743 unlink (*tempfile); /* no longer needed */
1747 st = (MTransport.sendmail_wait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1748 if (MTransport.sendmail_wait > 0) {
1754 /* reset alarm; not really needed, but... */
1756 sigaction (SIGALRM, &oldalrm, NULL);
1758 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1759 /* the parent is already dead */
1767 sigprocmask (SIG_UNBLOCK, &set, NULL);
1769 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1770 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1772 st = S_ERR; /* error */
1774 mutt_unblock_signals_system (1);
1779 static const char **
1780 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1782 for (; addr; addr = addr->next) {
1783 /* weed out group mailboxes, since those are for display only */
1784 if (addr->mailbox && !addr->group) {
1785 if (*argslen == *argsmax)
1786 p_realloc(&args, *argsmax += 5);
1787 args[(*argslen)++] = addr->mailbox;
1793 static const char **
1794 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1796 if (*argslen == *argsmax) {
1797 p_realloc(&args, *argsmax += 5);
1799 args[(*argslen)++] = s;
1803 int mutt_invoke_mta(address_t *from, address_t *to, address_t *cc,
1804 address_t *bcc, const char *msg, int eightbit)
1806 char cmd[LONG_STRING];
1807 char *ps = NULL, *path = NULL, *childout = NULL;
1808 const char **args = NULL;
1809 ssize_t argslen = 0, argsmax = 0;
1813 if (option (OPTNEWSSEND)) {
1820 m_strcpy(cmd, sizeof(cmd), MTransport.sendmail);
1825 while ((ps = strtok(ps, " "))) {
1826 if (argslen == argsmax)
1827 p_realloc(&args, argsmax += 5);
1830 args[argslen++] = ps;
1832 path = m_strdup(ps);
1833 ps = strrchr (ps, '/');
1838 args[argslen++] = ps;
1845 if (!option (OPTNEWSSEND)) {
1847 if (eightbit && MTransport.use_8bitmime)
1848 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1850 if (MTransport.use_envelope_from) {
1851 address_t *f = MTransport.envelope_from_address;
1852 if (!f && from && !from->next)
1855 args = add_option (args, &argslen, &argsmax, "-f");
1856 args = add_args (args, &argslen, &argsmax, f);
1859 if (MTransport.dsn_notify) {
1860 args = add_option (args, &argslen, &argsmax, "-N");
1861 args = add_option (args, &argslen, &argsmax, MTransport.dsn_notify);
1863 if (MTransport.dsn_return) {
1864 args = add_option (args, &argslen, &argsmax, "-R");
1865 args = add_option (args, &argslen, &argsmax, MTransport.dsn_return);
1867 args = add_option (args, &argslen, &argsmax, "--");
1868 args = add_args (args, &argslen, &argsmax, to);
1869 args = add_args (args, &argslen, &argsmax, cc);
1870 args = add_args (args, &argslen, &argsmax, bcc);
1875 if (argslen >= argsmax)
1876 p_realloc(&args, ++argsmax);
1878 args[argslen++] = NULL;
1880 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1882 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1887 if (!stat(childout, &st) && st.st_size > 0)
1888 mutt_pager(_("Output of the delivery process"), childout, 0, NULL);
1895 p_delete(&childout);
1899 if (i == (EX_OK & 0xff))
1901 else if (i == S_BKG)
1908 /* For postponing (!final) do the necessary encodings only */
1909 void mutt_prepare_envelope (ENVELOPE * env, int final)
1912 if (env->bcc && !(env->to || env->cc)) {
1913 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1914 * recipients if there is no To: or Cc: field, so attempt to suppress
1915 * it by using an empty To: field.
1917 env->to = address_new();
1919 env->to->next = address_new();
1920 env->to->mailbox = m_strdup("undisclosed-recipients");
1923 mutt_set_followup_to(env);
1925 if (!env->message_id && !m_strisempty(MsgIdFormat))
1926 env->message_id = mutt_gen_msgid();
1929 /* Take care of 8-bit => 7-bit conversion. */
1930 rfc2047_encode_adrlist(env->to, "To");
1931 rfc2047_encode_adrlist(env->cc, "Cc");
1932 rfc2047_encode_adrlist(env->bcc, "Bcc");
1933 rfc2047_encode_adrlist(env->from, "From");
1934 rfc2047_encode_adrlist(env->mail_followup_to, "Mail-Followup-To");
1935 rfc2047_encode_adrlist(env->reply_to, "Reply-To");
1938 rfc2047_encode_string (&env->subject);
1939 encode_headers (env->userhdrs);
1942 void mutt_unprepare_envelope (ENVELOPE * env)
1944 string_list_t *item;
1946 for (item = env->userhdrs; item; item = item->next)
1947 rfc2047_decode(&item->data);
1949 address_list_wipe(&env->mail_followup_to);
1951 /* back conversions */
1952 rfc2047_decode_adrlist(env->to);
1953 rfc2047_decode_adrlist(env->cc);
1954 rfc2047_decode_adrlist(env->bcc);
1955 rfc2047_decode_adrlist(env->from);
1956 rfc2047_decode_adrlist(env->reply_to);
1957 rfc2047_decode(&env->subject);
1960 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
1961 const char *resent_from, address_t * env_from)
1965 char date[STRING], tempfile[_POSIX_PATH_MAX];
1966 MESSAGE *msg = NULL;
1969 /* Try to bounce each message out, aborting if we get any failures. */
1970 for (i = 0; i < Context->msgcount; i++)
1971 if (Context->hdrs[i]->tagged)
1973 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
1978 /* If we failed to open a message, return with error */
1979 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
1985 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
1987 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
1989 if (!option (OPTBOUNCEDELIVERED))
1990 ch_flags |= CH_WEED_DELIVERED;
1992 fseeko (fp, h->offset, 0);
1993 fprintf (f, "Resent-From: %s", resent_from);
1994 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
1995 if (!m_strisempty(MsgIdFormat))
1996 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
1997 fputs ("Resent-To: ", f);
1998 mutt_write_address_list (to, f, 11, 0);
1999 mutt_copy_header (fp, h, f, ch_flags, NULL);
2001 mutt_copy_bytes (fp, f, h->content->length);
2004 ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
2005 h->content->encoding == ENC8BIT);
2009 mx_close_message (&msg);
2014 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2017 char resent_from[STRING];
2021 resent_from[0] = '\0';
2022 from = mutt_default_from ();
2024 rfc822_qualify(from, mutt_fqdn(1));
2026 rfc2047_encode_adrlist(from, "Resent-From");
2027 if (mutt_addrlist_to_idna (from, &err)) {
2028 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2031 rfc822_addrcat(resent_from, sizeof(resent_from), from, 0);
2034 unset_option (OPTNEWSSEND);
2037 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2039 address_list_wipe(&from);
2044 static void set_noconv_flags (BODY * b, short flag)
2046 for (; b; b = b->next) {
2047 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2048 set_noconv_flags (b->parts, flag);
2049 else if (b->type == TYPETEXT && b->noconv) {
2050 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2055 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2056 int post, char *fcc)
2060 char tempfile[_POSIX_PATH_MAX];
2061 FILE *tempfp = NULL;
2065 set_noconv_flags (hdr->content, 1);
2067 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2071 /* We need to add a Content-Length field to avoid problems where a line in
2072 * the message body begins with "From "
2074 if (f.magic == M_MBOX) {
2075 tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2077 mutt_error(_("Could not create temporary file"));
2078 mx_close_mailbox (&f, NULL);
2083 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2084 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2085 mx_close_mailbox (&f, NULL);
2089 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2090 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2092 mutt_write_rfc822_header(msg->fp, hdr->env, hdr->content, -post);
2094 /* (postponment) if this was a reply of some sort, <msgid> contians the
2095 * Message-ID: of message replied to. Save it using a special X-Mutt-
2096 * header so it can be picked up if the message is recalled at a later
2097 * point in time. This will allow the message to be marked as replied if
2098 * the same mailbox is still open.
2101 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2103 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2104 * it can be picked up when the message is recalled
2107 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2108 fprintf (msg->fp, "Status: RO\n");
2110 /* (postponment) if the mail is to be signed or encrypted, save this info */
2111 if (post && (hdr->security & APPLICATION_PGP)) {
2112 fputs ("X-Mutt-PGP: ", msg->fp);
2113 if (hdr->security & ENCRYPT)
2114 fputc ('E', msg->fp);
2115 if (hdr->security & SIGN) {
2116 fputc ('S', msg->fp);
2117 if (PgpSignAs && *PgpSignAs)
2118 fprintf (msg->fp, "<%s>", PgpSignAs);
2120 if (hdr->security & INLINE)
2121 fputc ('I', msg->fp);
2122 fputc ('\n', msg->fp);
2125 /* (postponment) if the mail is to be signed or encrypted, save this info */
2126 if (post && (hdr->security & APPLICATION_SMIME)) {
2127 fputs ("X-Mutt-SMIME: ", msg->fp);
2128 if (hdr->security & ENCRYPT) {
2129 fputc ('E', msg->fp);
2130 if (SmimeCryptAlg && *SmimeCryptAlg)
2131 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2133 if (hdr->security & SIGN) {
2134 fputc ('S', msg->fp);
2135 if (SmimeDefaultKey && *SmimeDefaultKey)
2136 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2138 if (hdr->security & INLINE)
2139 fputc ('I', msg->fp);
2140 fputc ('\n', msg->fp);
2144 char sasha[LONG_STRING];
2147 mutt_write_mime_body (hdr->content, tempfp);
2149 /* make sure the last line ends with a newline. Emacs doesn't ensure
2150 * this will happen, and it can cause problems parsing the mailbox
2153 fseeko (tempfp, -1, 2);
2154 if (fgetc (tempfp) != '\n') {
2155 fseeko (tempfp, 0, 2);
2156 fputc ('\n', tempfp);
2160 if (ferror (tempfp)) {
2163 mx_commit_message (msg, &f); /* XXX - really? */
2164 mx_close_message (&msg);
2165 mx_close_mailbox (&f, NULL);
2169 /* count the number of lines */
2171 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2173 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2174 fprintf (msg->fp, "Lines: %d\n\n", lines);
2176 /* copy the body and clean up */
2178 r = mutt_copy_stream (tempfp, msg->fp);
2179 if (m_fclose(&tempfp) != 0)
2181 /* if there was an error, leave the temp version */
2185 fputc ('\n', msg->fp); /* finish off the header */
2186 r = mutt_write_mime_body (hdr->content, msg->fp);
2189 if (mx_commit_message (msg, &f) != 0)
2191 mx_close_message (&msg);
2192 mx_close_mailbox (&f, NULL);
2195 set_noconv_flags (hdr->content, 0);