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);
1254 loc = setlocale(LC_TIME, "C");
1255 strftime(s, len, "Date: %a, %d %b %Y %T %z\n", localtime(&t));
1256 setlocale(LC_TIME, loc);
1260 /* wrapper around mutt_write_address() so we can handle very large
1261 recipient lists without needing a huge temporary buffer in memory */
1263 mutt_write_address_list(address_t *addr, FILE *fp, int linelen, int display)
1268 char buf[LONG_STRING];
1269 int len = rfc822_addrcpy(buf, ssizeof(buf), addr, display);
1272 if (linelen + len > 74) {
1274 linelen = 8; /* tab is usually about 8 spaces... */
1276 if (addr->mailbox) {
1286 if (!addr->group && addr->next && addr->next->mailbox) {
1296 /* need to write the list in reverse because they are stored in reverse order
1297 * when parsed to speed up threading
1299 void mutt_write_references(string_list_t *r, FILE *f)
1301 string_list_t *refs[10];
1304 p_clear(refs, countof(refs));
1305 for (i = 0; i < countof(refs) && r; r = r->next) {
1310 fprintf(f, " %s", refs[i]->data);
1314 static int edit_header(int mode, const char *s)
1317 int slen = m_strlen(s);
1319 if (mode != 1 || option(OPTXMAILTO))
1322 p = skipspaces(EditorHeaders);
1324 if (!ascii_strncasecmp(p, s, slen) && p[slen - 1] == ':')
1326 p = skipspaces(p + slen);
1332 /* Note: all RFC2047 encoding should be done outside of this routine, except
1333 * for the "real name." This will allow this routine to be used more than
1334 * once, if necessary.
1336 * Likewise, all IDN processing should happen outside of this routine.
1338 * mode == 1 => "lite" mode (used for edit_hdrs)
1339 * mode == 0 => normal mode. write full header + MIME headers
1340 * mode == -1 => write just the envelope info (used for postponing messages)
1342 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1345 char buffer[LONG_STRING];
1347 string_list_t *tmp = env->userhdrs;
1348 int has_agent = 0; /* user defined user-agent header field exists */
1351 if (!option (OPTNEWSSEND))
1354 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1356 /* OPTUSEFROM is not consulted here so that we can still write a From:
1357 * field if the user sets it with the `my_hdr' command
1361 rfc822_addrcat(buffer, sizeof(buffer), env->from, 0);
1362 fprintf (fp, "From: %s\n", buffer);
1367 mutt_write_address_list (env->to, fp, 4, 0);
1371 if (!option (OPTNEWSSEND))
1373 if (edit_header(mode, "To:"))
1374 fputs ("To:\n", fp);
1378 mutt_write_address_list (env->cc, fp, 4, 0);
1382 if (!option (OPTNEWSSEND))
1384 if (edit_header(mode, "Cc:"))
1385 fputs ("Cc:\n", fp);
1388 if (mode != 0 || option (OPTWRITEBCC)) {
1389 fputs ("Bcc: ", fp);
1390 mutt_write_address_list (env->bcc, fp, 5, 0);
1395 if (!option (OPTNEWSSEND))
1397 if (edit_header(mode, "Bcc:"))
1398 fputs ("Bcc:\n", fp);
1401 if (env->newsgroups)
1402 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1403 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Newsgroups:"))
1404 fputs ("Newsgroups:\n", fp);
1406 if (env->followup_to)
1407 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1408 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Followup-To:"))
1409 fputs ("Followup-To:\n", fp);
1413 fprintf (fp, "Subject: %s\n", env->subject);
1414 else if (mode == 1 && edit_header(mode, "Subject:"))
1415 fputs ("Subject:\n", fp);
1417 /* save message id if the user has set it */
1418 if (env->message_id)
1419 fprintf (fp, "Message-ID: %s\n", env->message_id);
1421 if (env->reply_to) {
1422 fputs ("Reply-To: ", fp);
1423 mutt_write_address_list (env->reply_to, fp, 10, 0);
1425 else if (mode > 0 && edit_header(mode, "Reply-To:"))
1426 fputs ("Reply-To:\n", fp);
1428 if (env->mail_followup_to)
1430 if (!option (OPTNEWSSEND))
1433 fputs ("Mail-Followup-To: ", fp);
1434 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1438 if (env->references) {
1439 fputs ("References:", fp);
1440 mutt_write_references (env->references, fp);
1444 /* Add the MIME headers */
1445 fputs ("MIME-Version: 1.0\n", fp);
1446 mutt_write_mime_header (attach, fp);
1449 if (env->in_reply_to) {
1450 fputs ("In-Reply-To:", fp);
1451 mutt_write_references (env->in_reply_to, fp);
1455 /* Add any user defined headers */
1456 for (; tmp; tmp = tmp->next) {
1457 if ((p = strchr (tmp->data, ':'))) {
1458 p = vskipspaces(p + 1);
1460 continue; /* don't emit empty fields. */
1462 /* check to see if the user has overridden the user-agent field */
1463 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1467 fputs (tmp->data, fp);
1472 if (mode == 0 && option (OPTXMAILER) && !has_agent) {
1473 if (mod_core.operating_system) {
1474 fprintf(fp, "User-Agent: %s (%s)\n", mutt_make_version(),
1475 mod_core.operating_system);
1477 fprintf(fp, "User-Agent: %s\n", mutt_make_version());
1481 return (ferror (fp) == 0 ? 0 : -1);
1484 static void encode_headers (string_list_t * h)
1490 for (; h; h = h->next) {
1491 if (!(p = strchr (h->data, ':')))
1495 p = vskipspaces(p + 1);
1501 rfc2047_encode_string (&tmp);
1502 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1504 sprintf (h->data + i, ": %s", NONULL (tmp));
1510 const char *mutt_fqdn(short may_hide_host)
1514 if (mod_core.hostname && mod_core.hostname[0] != '@') {
1515 p = mod_core.hostname;
1517 if (may_hide_host && option (OPTHIDDENHOST)) {
1518 if ((p = strchr(mod_core.hostname, '.')))
1521 /* sanity check: don't hide the host if
1522 the fqdn is something like detebe.org. */
1524 if (!p || !(q = strchr(p, '.')))
1525 p = mod_core.hostname;
1532 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1534 #define APPEND_FMT(fmt, arg) \
1536 int snlen = snprintf(buf, len, fmt, arg); \
1541 #define APPEND_BYTE(c) \
1555 static char MsgIdPfx = 'A';
1559 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1560 APPEND_BYTE((isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.');
1568 APPEND_FMT("%02d", tm->tm_mday);
1571 APPEND_FMT("%02d", tm->tm_hour);
1574 APPEND_FMT("%02d", tm->tm_mon + 1);
1577 APPEND_FMT("%02d", tm->tm_min);
1580 APPEND_FMT("%lo", (unsigned long)now);
1583 APPEND_FMT("%u", (unsigned int)getpid());
1586 APPEND_FMT("%c", MsgIdPfx);
1587 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1590 APPEND_FMT("%u", (unsigned int)rand());
1593 APPEND_FMT("%x", (unsigned int)rand());
1596 APPEND_FMT("%02d", tm->tm_sec);
1599 APPEND_FMT("%u", (unsigned int) now);
1602 APPEND_FMT("%x", (unsigned int) now);
1604 case 'Y': /* this will break in the year 10000 ;-) */
1605 APPEND_FMT("%04d", tm->tm_year + 1900);
1610 default: /* invalid formats are replaced by '.' */
1621 static char *mutt_gen_msgid (void)
1624 char localpart[STRING];
1627 if (!(fqdn = mutt_fqdn(0)))
1628 fqdn = NONULL(mod_core.shorthost);
1630 mutt_gen_localpart(localpart, sizeof(localpart), MsgIdFormat);
1631 snprintf(buf, sizeof(buf), "<%s@%s>", localpart, fqdn);
1632 return m_strdup(buf);
1635 static void alarm_handler (int sig __attribute__ ((unused)))
1640 /* invoke sendmail in a subshell
1641 path (in) path to program to execute
1642 args (in) arguments to pass to program
1643 msg (in) temp file containing message to send
1644 tempfile (out) if sendmail is put in the background, this points
1645 to the temporary file containing the stdout of the
1648 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1654 mutt_block_signals_system ();
1657 /* we also don't want to be stopped right now */
1658 sigaddset (&set, SIGTSTP);
1659 sigprocmask (SIG_BLOCK, &set, NULL);
1661 if (MTransport.sendmail_wait >= 0) {
1662 char tmp[_POSIX_PATH_MAX];
1665 *tempfile = m_strdup(tmp);
1668 if ((pid = fork ()) == 0) {
1669 struct sigaction act, oldalrm;
1671 /* save parent's ID before setsid() */
1674 /* we want the delivery to continue even after the main process dies,
1675 * so we put ourselves into another session right away
1679 /* next we close all open files */
1680 for (fd = 0; fd < getdtablesize(); fd++)
1683 /* now the second fork() */
1684 if ((pid = fork ()) == 0) {
1685 /* "msg" will be opened as stdin */
1686 if (open (msg, O_RDONLY, 0) < 0) {
1692 if (MTransport.sendmail_wait >= 0) {
1693 /* *tempfile will be opened as stdout */
1694 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1697 /* redirect stderr to *tempfile too */
1701 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1703 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1707 execv (path, (char**)args);
1710 else if (pid == -1) {
1716 /* sendmail_wait > 0: interrupt waitpid() after sendmail_wait seconds
1717 * sendmail_wait = 0: wait forever
1718 * sendmail_wait < 0: don't wait
1720 if (MTransport.sendmail_wait > 0) {
1722 act.sa_handler = alarm_handler;
1724 /* need to make sure waitpid() is interrupted on SIGALRM */
1725 act.sa_flags = SA_INTERRUPT;
1729 sigemptyset (&act.sa_mask);
1730 sigaction (SIGALRM, &act, &oldalrm);
1731 alarm (MTransport.sendmail_wait);
1733 else if (MTransport.sendmail_wait < 0)
1734 _exit (0xff & EX_OK);
1736 if (waitpid (pid, &st, 0) > 0) {
1737 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1738 if (MTransport.sendmail_wait && st == (0xff & EX_OK)) {
1739 unlink (*tempfile); /* no longer needed */
1743 st = (MTransport.sendmail_wait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1744 if (MTransport.sendmail_wait > 0) {
1750 /* reset alarm; not really needed, but... */
1752 sigaction (SIGALRM, &oldalrm, NULL);
1754 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1755 /* the parent is already dead */
1763 sigprocmask (SIG_UNBLOCK, &set, NULL);
1765 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1766 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1768 st = S_ERR; /* error */
1770 mutt_unblock_signals_system (1);
1775 static const char **
1776 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1778 for (; addr; addr = addr->next) {
1779 /* weed out group mailboxes, since those are for display only */
1780 if (addr->mailbox && !addr->group) {
1781 if (*argslen == *argsmax)
1782 p_realloc(&args, *argsmax += 5);
1783 args[(*argslen)++] = addr->mailbox;
1789 static const char **
1790 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1792 if (*argslen == *argsmax) {
1793 p_realloc(&args, *argsmax += 5);
1795 args[(*argslen)++] = s;
1799 int mutt_invoke_mta(address_t *from, address_t *to, address_t *cc,
1800 address_t *bcc, const char *msg, int eightbit)
1802 char cmd[LONG_STRING];
1803 char *ps = NULL, *path = NULL, *childout = NULL;
1804 const char **args = NULL;
1805 ssize_t argslen = 0, argsmax = 0;
1809 if (option (OPTNEWSSEND)) {
1816 m_strcpy(cmd, sizeof(cmd), MTransport.sendmail);
1821 while ((ps = strtok(ps, " "))) {
1822 if (argslen == argsmax)
1823 p_realloc(&args, argsmax += 5);
1826 args[argslen++] = ps;
1828 path = m_strdup(ps);
1829 ps = strrchr (ps, '/');
1834 args[argslen++] = ps;
1841 if (!option (OPTNEWSSEND)) {
1843 if (eightbit && MTransport.use_8bitmime)
1844 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1846 if (MTransport.use_envelope_from) {
1847 address_t *f = MTransport.envelope_from_address;
1848 if (!f && from && !from->next)
1851 args = add_option (args, &argslen, &argsmax, "-f");
1852 args = add_args (args, &argslen, &argsmax, f);
1855 if (MTransport.dsn_notify) {
1856 args = add_option (args, &argslen, &argsmax, "-N");
1857 args = add_option (args, &argslen, &argsmax, MTransport.dsn_notify);
1859 if (MTransport.dsn_return) {
1860 args = add_option (args, &argslen, &argsmax, "-R");
1861 args = add_option (args, &argslen, &argsmax, MTransport.dsn_return);
1863 args = add_option (args, &argslen, &argsmax, "--");
1864 args = add_args (args, &argslen, &argsmax, to);
1865 args = add_args (args, &argslen, &argsmax, cc);
1866 args = add_args (args, &argslen, &argsmax, bcc);
1871 if (argslen >= argsmax)
1872 p_realloc(&args, ++argsmax);
1874 args[argslen++] = NULL;
1876 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1878 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1883 if (!stat(childout, &st) && st.st_size > 0)
1884 mutt_pager(_("Output of the delivery process"), childout, 0, NULL);
1891 p_delete(&childout);
1895 if (i == (EX_OK & 0xff))
1897 else if (i == S_BKG)
1904 /* For postponing (!final) do the necessary encodings only */
1905 void mutt_prepare_envelope (ENVELOPE * env, int final)
1908 if (env->bcc && !(env->to || env->cc)) {
1909 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1910 * recipients if there is no To: or Cc: field, so attempt to suppress
1911 * it by using an empty To: field.
1913 env->to = address_new();
1915 env->to->next = address_new();
1916 env->to->mailbox = m_strdup("undisclosed-recipients");
1919 mutt_set_followup_to(env);
1921 if (!env->message_id && !m_strisempty(MsgIdFormat))
1922 env->message_id = mutt_gen_msgid();
1925 /* Take care of 8-bit => 7-bit conversion. */
1926 rfc2047_encode_adrlist(env->to, "To");
1927 rfc2047_encode_adrlist(env->cc, "Cc");
1928 rfc2047_encode_adrlist(env->bcc, "Bcc");
1929 rfc2047_encode_adrlist(env->from, "From");
1930 rfc2047_encode_adrlist(env->mail_followup_to, "Mail-Followup-To");
1931 rfc2047_encode_adrlist(env->reply_to, "Reply-To");
1934 rfc2047_encode_string (&env->subject);
1935 encode_headers (env->userhdrs);
1938 void mutt_unprepare_envelope (ENVELOPE * env)
1940 string_list_t *item;
1942 for (item = env->userhdrs; item; item = item->next)
1943 rfc2047_decode(&item->data);
1945 address_list_wipe(&env->mail_followup_to);
1947 /* back conversions */
1948 rfc2047_decode_adrlist(env->to);
1949 rfc2047_decode_adrlist(env->cc);
1950 rfc2047_decode_adrlist(env->bcc);
1951 rfc2047_decode_adrlist(env->from);
1952 rfc2047_decode_adrlist(env->reply_to);
1953 rfc2047_decode(&env->subject);
1956 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
1957 const char *resent_from, address_t * env_from)
1961 char date[STRING], tempfile[_POSIX_PATH_MAX];
1962 MESSAGE *msg = NULL;
1965 /* Try to bounce each message out, aborting if we get any failures. */
1966 for (i = 0; i < Context->msgcount; i++)
1967 if (Context->hdrs[i]->tagged)
1969 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
1974 /* If we failed to open a message, return with error */
1975 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
1981 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
1983 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
1985 if (!option (OPTBOUNCEDELIVERED))
1986 ch_flags |= CH_WEED_DELIVERED;
1988 fseeko (fp, h->offset, 0);
1989 fprintf (f, "Resent-From: %s", resent_from);
1990 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
1991 if (!m_strisempty(MsgIdFormat))
1992 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
1993 fputs ("Resent-To: ", f);
1994 mutt_write_address_list (to, f, 11, 0);
1995 mutt_copy_header (fp, h, f, ch_flags, NULL);
1997 mutt_copy_bytes (fp, f, h->content->length);
2000 ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
2001 h->content->encoding == ENC8BIT);
2005 mx_close_message (&msg);
2010 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2013 char resent_from[STRING];
2017 resent_from[0] = '\0';
2018 from = mutt_default_from ();
2020 rfc822_qualify(from, mutt_fqdn(1));
2022 rfc2047_encode_adrlist(from, "Resent-From");
2023 if (mutt_addrlist_to_idna (from, &err)) {
2024 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2027 rfc822_addrcat(resent_from, sizeof(resent_from), from, 0);
2030 unset_option (OPTNEWSSEND);
2033 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2035 address_list_wipe(&from);
2040 static void set_noconv_flags (BODY * b, short flag)
2042 for (; b; b = b->next) {
2043 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2044 set_noconv_flags (b->parts, flag);
2045 else if (b->type == TYPETEXT && b->noconv) {
2046 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2051 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2052 int post, char *fcc)
2056 char tempfile[_POSIX_PATH_MAX];
2057 FILE *tempfp = NULL;
2061 set_noconv_flags (hdr->content, 1);
2063 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2067 /* We need to add a Content-Length field to avoid problems where a line in
2068 * the message body begins with "From "
2070 if (f.magic == M_MBOX) {
2071 tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2073 mutt_error(_("Could not create temporary file"));
2074 mx_close_mailbox (&f, NULL);
2079 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2080 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2081 mx_close_mailbox (&f, NULL);
2085 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2086 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2088 mutt_write_rfc822_header(msg->fp, hdr->env, hdr->content, -post);
2090 /* (postponment) if this was a reply of some sort, <msgid> contians the
2091 * Message-ID: of message replied to. Save it using a special X-Mutt-
2092 * header so it can be picked up if the message is recalled at a later
2093 * point in time. This will allow the message to be marked as replied if
2094 * the same mailbox is still open.
2097 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2099 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2100 * it can be picked up when the message is recalled
2103 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2104 fprintf (msg->fp, "Status: RO\n");
2106 /* (postponment) if the mail is to be signed or encrypted, save this info */
2107 if (post && (hdr->security & APPLICATION_PGP)) {
2108 fputs ("X-Mutt-PGP: ", msg->fp);
2109 if (hdr->security & ENCRYPT)
2110 fputc ('E', msg->fp);
2111 if (hdr->security & SIGN) {
2112 fputc ('S', msg->fp);
2113 if (PgpSignAs && *PgpSignAs)
2114 fprintf (msg->fp, "<%s>", PgpSignAs);
2116 if (hdr->security & INLINE)
2117 fputc ('I', msg->fp);
2118 fputc ('\n', msg->fp);
2121 /* (postponment) if the mail is to be signed or encrypted, save this info */
2122 if (post && (hdr->security & APPLICATION_SMIME)) {
2123 fputs ("X-Mutt-SMIME: ", msg->fp);
2124 if (hdr->security & ENCRYPT) {
2125 fputc ('E', msg->fp);
2126 if (SmimeCryptAlg && *SmimeCryptAlg)
2127 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2129 if (hdr->security & SIGN) {
2130 fputc ('S', msg->fp);
2131 if (SmimeDefaultKey && *SmimeDefaultKey)
2132 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2134 if (hdr->security & INLINE)
2135 fputc ('I', msg->fp);
2136 fputc ('\n', msg->fp);
2140 char sasha[LONG_STRING];
2143 mutt_write_mime_body (hdr->content, tempfp);
2145 /* make sure the last line ends with a newline. Emacs doesn't ensure
2146 * this will happen, and it can cause problems parsing the mailbox
2149 fseeko (tempfp, -1, 2);
2150 if (fgetc (tempfp) != '\n') {
2151 fseeko (tempfp, 0, 2);
2152 fputc ('\n', tempfp);
2156 if (ferror (tempfp)) {
2159 mx_commit_message (msg, &f); /* XXX - really? */
2160 mx_close_message (&msg);
2161 mx_close_mailbox (&f, NULL);
2165 /* count the number of lines */
2167 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2169 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2170 fprintf (msg->fp, "Lines: %d\n\n", lines);
2172 /* copy the body and clean up */
2174 r = mutt_copy_stream (tempfp, msg->fp);
2175 if (m_fclose(&tempfp) != 0)
2177 /* if there was an error, leave the temp version */
2181 fputc ('\n', msg->fp); /* finish off the header */
2182 r = mutt_write_mime_body (hdr->content, msg->fp);
2185 if (mx_commit_message (msg, &f) != 0)
2187 mx_close_message (&msg);
2188 mx_close_mailbox (&f, NULL);
2191 set_noconv_flags (hdr->content, 0);