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"
31 #ifdef HAVE_SYSEXITS_H
33 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
37 static void transform_to_7bit (BODY * a, FILE * fpin);
39 static void encode_quoted (fgetconv_t * fc, FILE * fout, int istext)
42 char line[77], savechar;
44 while ((c = fgetconv (fc)) != EOF) {
45 /* Wrap the line if needed. */
46 if (linelen == 76 && ((istext && c != '\n') || !istext)) {
47 /* If the last character is "quoted", then be sure to move all three
48 * characters to the next line. Otherwise, just move the last
51 if (line[linelen - 3] == '=') {
52 line[linelen - 3] = 0;
57 line[1] = line[linelen - 2];
58 line[2] = line[linelen - 1];
62 savechar = line[linelen - 1];
63 line[linelen - 1] = '=';
72 /* Escape lines that begin with/only contain "the message separator". */
73 if (linelen == 4 && !m_strncmp("From", line, 4)) {
74 m_strcpy(line, sizeof(line), "=46rom");
77 else if (linelen == 4 && !m_strncmp("from", line, 4)) {
78 m_strcpy(line, sizeof(line), "=66rom");
81 else if (linelen == 1 && line[0] == '.') {
82 m_strcpy(line, sizeof(line), "=2E");
87 if (c == '\n' && istext) {
88 /* Check to make sure there is no trailing space on this line. */
90 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
92 sprintf (line + linelen - 1, "=%2.2X",
93 (unsigned char) line[linelen - 1]);
97 savechar = line[linelen - 1];
99 line[linelen - 1] = '=';
102 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
112 else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
113 /* Check to make sure there is enough room for the quoted character.
114 * If not, wrap to the next line.
117 line[linelen++] = '=';
123 sprintf (line + linelen, "=%2.2X", (unsigned char) c);
127 /* Don't worry about wrapping the line here. That will happen during
128 * the next iteration when I'll also know what the next character is.
134 /* Take care of anything left in the buffer */
136 if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
137 /* take care of trailing whitespace */
139 sprintf (line + linelen - 1, "=%2.2X",
140 (unsigned char) line[linelen - 1]);
142 savechar = line[linelen - 1];
143 line[linelen - 1] = '=';
147 sprintf (line, "=%2.2X", (unsigned char) savechar);
156 static char b64_buffer[3];
157 static short b64_num;
158 static short b64_linelen;
160 static void b64_flush (FILE * fout)
167 if (b64_linelen >= 72) {
172 for (i = b64_num; i < 3; i++)
173 b64_buffer[i] = '\0';
175 fputc(__m_b64chars[(b64_buffer[0] >> 2) & 0x3f], fout);
178 [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
183 [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
187 fputc (__m_b64chars[b64_buffer[2] & 0x3f], fout);
192 while (b64_linelen % 4) {
201 static void b64_putc (char c, FILE * fout)
206 b64_buffer[b64_num++] = c;
210 static void encode_base64 (fgetconv_t * fc, FILE * fout, int istext)
214 b64_num = b64_linelen = 0;
216 while ((ch = fgetconv (fc)) != EOF) {
217 if (istext && ch == '\n' && ch1 != '\r')
218 b64_putc ('\r', fout);
226 static void encode_8bit (fgetconv_t * fc, FILE * fout,
227 int istext __attribute__ ((unused)))
231 while ((ch = fgetconv (fc)) != EOF)
236 int mutt_write_mime_header (BODY * a, FILE * f)
245 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
250 len = 25 + m_strlen(a->subtype); /* approximate len. of content-type */
252 for (p = a->parameter; p; p = p->next) {
261 tmp = m_strdup(p->value);
262 encode = rfc2231_encode_string (&tmp);
263 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
265 /* Dirty hack to make messages readable by Outlook Express
266 * for the Mac: force quotes around the boundary parameter
267 * even when they aren't needed.
270 if (!ascii_strcasecmp (p->attribute, "boundary")
271 && !strcmp (buffer, tmp))
272 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
276 tmplen = m_strlen(buffer) + m_strlen(p->attribute) + 1;
278 if (len + tmplen + 2 > 76) {
287 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
295 fprintf (f, "Content-Description: %s\n", a->description);
297 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
298 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
301 if (!(fn = a->d_filename))
307 /* Strip off the leading path... */
308 if ((t = strrchr (fn, '/')))
315 encode = rfc2231_encode_string (&tmp);
316 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
318 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
324 if (a->encoding != ENC7BIT)
325 fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
327 /* Do NOT add the terminator here!!! */
328 return (ferror (f) ? -1 : 0);
331 int mutt_write_mime_body (BODY * a, FILE * f)
334 char boundary[STRING];
335 char send_charset[STRING];
340 if (a->type == TYPEMULTIPART) {
341 /* First, find the boundary to use */
342 if (!(p = parameter_getval(a->parameter, "boundary"))) {
343 mutt_error _("No boundary parameter found! [report this error]");
347 m_strcpy(boundary, sizeof(boundary), p);
349 for (t = a->parts; t; t = t->next) {
350 fprintf (f, "\n--%s\n", boundary);
351 if (mutt_write_mime_header (t, f) == -1)
354 if (mutt_write_mime_body (t, f) == -1)
357 fprintf (f, "\n--%s--\n", boundary);
358 return (ferror (f) ? -1 : 0);
361 /* This is pretty gross, but it's the best solution for now... */
362 if (a->type == TYPEAPPLICATION && !m_strcmp(a->subtype, "pgp-encrypted")) {
363 fputs ("Version: 1\n", f);
367 if ((fpin = fopen (a->filename, "r")) == NULL) {
368 mutt_error (_("%s no longer exists!"), a->filename);
372 if (a->type == TYPETEXT && (!a->noconv))
373 fc = fgetconv_open (fpin, a->file_charset,
374 mutt_get_body_charset (send_charset,
375 sizeof (send_charset), a), 0);
377 fc = fgetconv_open (fpin, 0, 0, 0);
379 #define write_as_text_part(a) (mutt_is_text_part(a) || mutt_is_application_pgp(a))
380 if (a->encoding == ENCQUOTEDPRINTABLE)
381 encode_quoted (fc, f, write_as_text_part (a));
382 else if (a->encoding == ENCBASE64)
383 encode_base64 (fc, f, write_as_text_part (a));
384 else if (a->type == TYPETEXT && (!a->noconv))
385 encode_8bit (fc, f, write_as_text_part (a));
387 mutt_copy_stream (fpin, f);
388 #undef write_as_text_part
390 fgetconv_close (&fc);
393 return (ferror (f) ? -1 : 0);
405 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
409 int whitespace = s->whitespace;
411 int linelen = s->linelen;
412 int was_cr = s->was_cr;
414 if (!d) { /* This signals EOF */
417 if (linelen > info->linemax)
418 info->linemax = linelen;
423 for (; dlen; d++, dlen--) {
436 if (linelen > info->linemax)
437 info->linemax = linelen;
452 if (linelen > info->linemax)
453 info->linemax = linelen;
458 else if (ch == '\r') {
466 else if (ch == '\t' || ch == '\f') {
470 else if (ch < 32 || ch == 127)
474 if ((ch == 'F') || (ch == 'f'))
484 if (linelen == 2 && ch != 'r')
486 else if (linelen == 3 && ch != 'o')
488 else if (linelen == 4) {
501 if (ch != ' ' && ch != '\t')
506 s->whitespace = whitespace;
508 s->linelen = linelen;
514 * Find the best charset conversion of the file from fromcode into one
515 * of the tocodes. If successful, set *tocode and CONTENT *info and
516 * return the number of characters converted inexactly. If no
517 * conversion was possible, return -1.
519 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
520 * which would otherwise prevent us from knowing the number of inexact
521 * conversions. Where the candidate target charset is UTF-8 we avoid
522 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
523 * fails with some libraries.
525 * We assume that the output from iconv is never more than 4 times as
526 * long as the input for any pair of charsets we might be interested
529 static ssize_t convert_file_to (FILE * file, const char *fromcode,
530 int ncodes, const char **tocodes,
531 int *tocode, CONTENT * info)
534 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
537 ssize_t ibl, obl, ubl, ubl1, n, ret;
540 CONTENT_STATE *states;
543 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
544 if (cd1 == MUTT_ICONV_ERROR)
547 cd = p_new(iconv_t, ncodes);
548 score = p_new(ssize_t, ncodes);
549 states = p_new(CONTENT_STATE, ncodes);
550 infos = p_new(CONTENT, ncodes);
552 for (i = 0; i < ncodes; i++)
553 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
554 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
556 /* Special case for conversion to UTF-8 */
557 cd[i] = MUTT_ICONV_ERROR, score[i] = -1;
563 /* Try to fill input buffer */
564 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
567 /* Convert to UTF-8 */
569 ob = bufu, obl = sizeof (bufu);
570 n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
571 if (n == -1 && ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
577 /* Convert from UTF-8 */
578 for (i = 0; i < ncodes; i++)
579 if (cd[i] != MUTT_ICONV_ERROR && score[i] != -1) {
580 ub = bufu, ubl = ubl1;
581 ob = bufo, obl = sizeof (bufo);
582 n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
588 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
591 else if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1)
592 /* Special case for conversion to UTF-8 */
593 update_content_info (&infos[i], &states[i], bufu, ubl1);
596 /* Save unused input */
597 memmove (bufi, ib, ibl);
598 else if (!ubl1 && ib < bufi + sizeof (bufi)) {
605 /* Find best score */
607 for (i = 0; i < ncodes; i++) {
608 if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1) {
609 /* Special case for conversion to UTF-8 */
614 else if (cd[i] == MUTT_ICONV_ERROR || score[i] == -1)
616 else if (ret == -1 || score[i] < ret) {
624 memcpy (info, &infos[*tocode], sizeof (CONTENT));
625 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
629 for (i = 0; i < ncodes; i++)
630 if (cd[i] != MUTT_ICONV_ERROR)
643 * Find the first of the fromcodes that gives a valid conversion and
644 * the best charset conversion of the file into one of the tocodes. If
645 * successful, set *fromcode and *tocode to dynamically allocated
646 * strings, set CONTENT *info, and return the number of characters
647 * converted inexactly. If no conversion was possible, return -1.
649 * Both fromcodes and tocodes may be colon-separated lists of charsets.
650 * However, if fromcode is zero then fromcodes is assumed to be the
651 * name of a single charset even if it contains a colon.
653 static ssize_t convert_file_from_to (FILE * file,
654 const char *fromcodes,
655 const char *tocodes, char **fromcode,
656 char **tocode, CONTENT * info)
664 /* Count the tocodes */
666 for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
667 if ((c1 = strchr (c, ':')) == c)
673 tcode = p_new(char *, ncodes);
674 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
675 if ((c1 = strchr (c, ':')) == c)
677 tcode[i] = m_substrdup(c, c1);
682 /* Try each fromcode in turn */
683 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
684 if ((c1 = strchr (c, ':')) == c)
686 fcode = m_substrdup(c, c1);
688 ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
700 /* There is only one fromcode */
701 ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
710 for (i = 0; i < ncodes; i++)
719 * Analyze the contents of a file to determine which MIME encoding to use.
720 * Also set the body charset, sometimes, or not.
722 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
727 char *fromcode = NULL;
738 if (stat (fname, &sb) == -1) {
739 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
743 if (!S_ISREG (sb.st_mode)) {
744 mutt_error (_("%s isn't a regular file."), fname);
748 if ((fp = fopen (fname, "r")) == NULL) {
752 info = p_new(CONTENT, 1);
755 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
756 const char *chs = parameter_getval(b->parameter, "charset");
757 char *fchs = b->use_disp && !m_strisempty(mod_cset.file_charset)
758 ? FileCharset : mod_cset.charset;
759 if (mod_cset.charset && (chs || mod_cset.send_charset) &&
760 convert_file_from_to (fp, fchs, chs ? chs : mod_cset.send_charset,
761 &fromcode, &tocode, info) != -1) {
763 charset_canonicalize (chsbuf, sizeof (chsbuf), tocode);
764 parameter_setval(&b->parameter, "charset", chsbuf);
766 b->file_charset = fromcode;
774 while ((r = fread (buffer, 1, sizeof (buffer), fp)))
775 update_content_info (info, &state, buffer, r);
776 update_content_info (info, &state, 0, 0);
780 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
781 parameter_setval(&b->parameter, "charset",
782 (!info->hibin ? "us-ascii"
783 : mod_cset.charset && !charset_is_us_ascii(mod_cset.charset)
784 ? mod_cset.charset : "unknown-8bit"));
789 /* Given a file with path ``s'', see if there is a registered MIME type.
790 * returns the major MIME type, and copies the subtype to ``d''. First look
791 * for ~/.mime.types, then look in a system mime.types if we can find one.
792 * The longest match is used so that we can match `ps.gz' when `gz' also
796 int mutt_lookup_mime_type (BODY * att, const char *path)
800 char buf[LONG_STRING];
801 char subtype[STRING], xtype[STRING];
803 int szf, sze, cur_sze;
811 szf = m_strlen(path);
813 for (count = 0; count < 4; count++) {
815 * can't use strtok() because we use it in an inner loop below, so use
816 * a switch statement here instead.
820 snprintf(buf, sizeof (buf), "%s/.mime.types", NONULL(mod_core.homedir));
823 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
826 m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
829 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
832 goto bye; /* shouldn't happen */
835 if ((f = fopen (buf, "r")) != NULL) {
836 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
837 /* weed out any comments */
838 if ((p = strchr (buf, '#')))
841 /* remove any leading space. */
842 ct = vskipspaces(buf);
844 /* position on the next field in this line */
845 if ((p = strpbrk (ct, " \t")) == NULL)
850 /* cycle through the file extensions */
851 while ((p = strtok (p, " \t\n"))) {
853 if ((sze > cur_sze) && (szf >= sze) &&
854 (m_strcasecmp(path + szf - sze, p) == 0
855 || ascii_strcasecmp (path + szf - sze, p) == 0)
856 && (szf == sze || path[szf - sze - 1] == '.'))
858 /* get the content-type */
860 if ((p = strchr (ct, '/')) == NULL) {
861 /* malformed line, just skip it. */
866 for (q = p; *q && !ISSPACE (*q); q++);
868 m_strncpy(subtype, sizeof(subtype), p, q - p);
870 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
871 m_strcpy(xtype, sizeof(xtype), ct);
884 if (type != TYPEOTHER || *xtype != '\0') {
886 m_strreplace(&att->subtype, subtype);
887 m_strreplace(&att->xtype, xtype);
893 void mutt_message_to_7bit (BODY * a, FILE * fp)
895 char temp[_POSIX_PATH_MAX];
901 if (!a->filename && fp)
903 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
904 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
909 if (stat (a->filename, &sb) == -1) {
910 mutt_perror ("stat");
913 a->length = sb.st_size;
916 fpout = m_tempfile(temp, sizeof(temp), NONULL(mod_core.tmpdir), NULL);
918 mutt_error(_("Could not create temporary file"));
922 fseeko (fpin, a->offset, 0);
923 a->parts = mutt_parse_messageRFC822 (fpin, a);
925 transform_to_7bit (a->parts, fpin);
927 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
928 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
930 fputs ("MIME-Version: 1.0\n", fpout);
931 mutt_write_mime_header (a->parts, fpout);
933 mutt_write_mime_body (a->parts, fpout);
945 a->encoding = ENC7BIT;
946 a->d_filename = a->filename;
947 if (a->filename && a->unlink)
948 unlink (a->filename);
949 a->filename = m_strdup(temp);
951 if (stat (a->filename, &sb) == -1) {
952 mutt_perror ("stat");
955 a->length = sb.st_size;
956 body_list_wipe(&a->parts);
957 a->hdr->content = NULL;
960 static void transform_to_7bit (BODY * a, FILE * fpin)
962 char buff[_POSIX_PATH_MAX];
967 for (; a; a = a->next) {
968 if (a->type == TYPEMULTIPART) {
969 if (a->encoding != ENC7BIT)
970 a->encoding = ENC7BIT;
972 transform_to_7bit (a->parts, fpin);
974 else if (mutt_is_message_type(a)) {
975 mutt_message_to_7bit (a, fpin);
979 a->force_charset = 1;
981 s.fpout = m_tempfile(buff, sizeof(buff), NONULL(mod_core.tmpdir), NULL);
983 mutt_error(_("Could not create temporary file"));
987 mutt_decode_attachment (a, &s);
989 a->d_filename = a->filename;
990 a->filename = m_strdup(buff);
992 if (stat (a->filename, &sb) == -1) {
993 mutt_perror ("stat");
996 a->length = sb.st_size;
998 mutt_update_encoding (a);
999 if (a->encoding == ENC8BIT)
1000 a->encoding = ENCQUOTEDPRINTABLE;
1001 else if (a->encoding == ENCBINARY)
1002 a->encoding = ENCBASE64;
1007 /* determine which Content-Transfer-Encoding to use */
1008 static void mutt_set_encoding (BODY * b, CONTENT * info)
1010 char send_charset[STRING];
1012 if (b->type == TYPETEXT) {
1014 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1015 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1016 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1017 b->encoding = ENCQUOTEDPRINTABLE;
1018 else if (info->hibin)
1019 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1021 b->encoding = ENC7BIT;
1023 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1024 if (info->lobin || info->hibin) {
1025 if (option (OPTALLOW8BIT) && !info->lobin)
1026 b->encoding = ENC8BIT;
1028 mutt_message_to_7bit (b, NULL);
1031 b->encoding = ENC7BIT;
1033 else if (b->type == TYPEAPPLICATION
1034 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1035 b->encoding = ENC7BIT;
1038 /* Determine which encoding is smaller */
1039 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1040 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1041 b->encoding = ENCBASE64;
1043 b->encoding = ENCQUOTEDPRINTABLE;
1047 void mutt_stamp_attachment (BODY * a)
1049 a->stamp = time (NULL);
1052 /* Get a body's character set */
1054 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1058 if (b && b->type != TYPETEXT)
1061 p = b ? parameter_getval(b->parameter, "charset") : NULL;
1062 charset_canonicalize(d, dlen, p);
1067 /* Assumes called from send mode where BODY->filename points to actual file */
1068 void mutt_update_encoding (BODY * a)
1071 char chsbuff[STRING];
1073 /* override noconv when it's us-ascii */
1074 if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1077 if (!a->force_charset && !a->noconv)
1078 parameter_delval(&a->parameter, "charset");
1080 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1083 mutt_set_encoding (a, info);
1084 mutt_stamp_attachment (a);
1086 p_delete(&a->content);
1091 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1093 char buffer[LONG_STRING];
1096 int cmflags, chflags;
1097 int pgp = hdr->security;
1099 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1100 (hdr->security & ENCRYPT)) {
1103 fp = m_tempfile(buffer, sizeof(buffer), NONULL(mod_core.tmpdir), NULL);
1108 body->type = TYPEMESSAGE;
1109 body->subtype = m_strdup("rfc822");
1110 body->filename = m_strdup(buffer);
1113 body->disposition = DISPINLINE;
1116 mutt_parse_mime_message (ctx, hdr);
1121 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1122 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1123 chflags |= CH_MIME | CH_TXTPLAIN;
1124 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1125 pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1127 else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1128 if (mutt_is_multipart_encrypted (hdr->content)) {
1129 chflags |= CH_MIME | CH_NONEWLINE;
1130 cmflags = M_CM_DECODE_PGP;
1133 else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1134 chflags |= CH_MIME | CH_TXTPLAIN;
1135 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1138 else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1139 chflags |= CH_MIME | CH_TXTPLAIN;
1140 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1141 pgp &= ~SMIMEENCRYPT;
1145 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1150 body->hdr = header_new();
1151 body->hdr->offset = 0;
1152 /* we don't need the user headers here */
1153 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1154 body->hdr->security = pgp;
1155 mutt_update_encoding (body);
1156 body->parts = body->hdr->content;
1163 BODY *mutt_make_file_attach (const char *path)
1169 att->filename = m_strdup(path);
1171 /* Attempt to determine the appropriate content-type based on the filename
1174 mutt_lookup_mime_type (att, path);
1176 if ((info = mutt_get_content_info (path, att)) == NULL) {
1177 body_list_wipe(&att);
1181 if (!att->subtype) {
1182 if (info->lobin == 0
1183 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1185 * Statistically speaking, there should be more than 10% "lobin"
1186 * chars if this is really a binary file...
1188 att->type = TYPETEXT;
1189 att->subtype = m_strdup("plain");
1191 att->type = TYPEAPPLICATION;
1192 att->subtype = m_strdup("octet-stream");
1196 mutt_update_encoding (att);
1200 static int get_toplevel_encoding (BODY * a)
1204 for (; a; a = a->next) {
1205 if (a->encoding == ENCBINARY)
1208 if (a->encoding == ENC8BIT)
1215 BODY *mutt_make_multipart (BODY * b)
1220 new->type = TYPEMULTIPART;
1221 new->subtype = m_strdup("mixed");
1222 new->encoding = get_toplevel_encoding (b);
1223 parameter_set_boundary(&new->parameter);
1225 new->disposition = DISPINLINE;
1231 /* remove the multipart body if it exists */
1232 BODY *mutt_remove_multipart (BODY * b)
1245 char *mutt_make_date (char *s, ssize_t len)
1247 time_t t = time(NULL);
1250 loc = setlocale(LC_TIME, "C");
1251 strftime(s, len, "Date: %a, %d %b %Y %T %z\n", localtime(&t));
1252 setlocale(LC_TIME, loc);
1256 /* wrapper around mutt_write_address() so we can handle very large
1257 recipient lists without needing a huge temporary buffer in memory */
1259 mutt_write_address_list(address_t *addr, FILE *fp, int linelen, int display)
1264 char buf[LONG_STRING];
1265 int len = rfc822_addrcpy(buf, ssizeof(buf), addr, display);
1268 if (linelen + len > 74) {
1270 linelen = 8; /* tab is usually about 8 spaces... */
1272 if (addr->mailbox) {
1282 if (!addr->group && addr->next && addr->next->mailbox) {
1292 /* need to write the list in reverse because they are stored in reverse order
1293 * when parsed to speed up threading
1295 void mutt_write_references(string_list_t *r, FILE *f)
1297 string_list_t *refs[10];
1300 p_clear(refs, countof(refs));
1301 for (i = 0; i < countof(refs) && r; r = r->next) {
1306 fprintf(f, " %s", refs[i]->data);
1310 static int edit_header(int mode, const char *s)
1313 int slen = m_strlen(s);
1315 if (mode != 1 || option(OPTXMAILTO))
1318 p = skipspaces(EditorHeaders);
1320 if (!ascii_strncasecmp(p, s, slen) && p[slen - 1] == ':')
1322 p = skipspaces(p + slen);
1328 /* Note: all RFC2047 encoding should be done outside of this routine, except
1329 * for the "real name." This will allow this routine to be used more than
1330 * once, if necessary.
1332 * Likewise, all IDN processing should happen outside of this routine.
1334 * mode == 1 => "lite" mode (used for edit_hdrs)
1335 * mode == 0 => normal mode. write full header + MIME headers
1336 * mode == -1 => write just the envelope info (used for postponing messages)
1338 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1341 char buffer[LONG_STRING];
1343 string_list_t *tmp = env->userhdrs;
1344 int has_agent = 0; /* user defined user-agent header field exists */
1347 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1349 /* OPTUSEFROM is not consulted here so that we can still write a From:
1350 * field if the user sets it with the `my_hdr' command
1354 rfc822_addrcat(buffer, sizeof(buffer), env->from, 0);
1355 fprintf (fp, "From: %s\n", buffer);
1360 mutt_write_address_list (env->to, fp, 4, 0);
1363 if (edit_header(mode, "To:"))
1364 fputs ("To:\n", fp);
1368 mutt_write_address_list (env->cc, fp, 4, 0);
1371 if (edit_header(mode, "Cc:"))
1372 fputs ("Cc:\n", fp);
1375 if (mode != 0 || option (OPTWRITEBCC)) {
1376 fputs ("Bcc: ", fp);
1377 mutt_write_address_list (env->bcc, fp, 5, 0);
1381 if (edit_header(mode, "Bcc:"))
1382 fputs ("Bcc:\n", fp);
1385 fprintf (fp, "Subject: %s\n", env->subject);
1386 else if (mode == 1 && edit_header(mode, "Subject:"))
1387 fputs ("Subject:\n", fp);
1389 /* save message id if the user has set it */
1390 if (env->message_id)
1391 fprintf (fp, "Message-ID: %s\n", env->message_id);
1393 if (env->reply_to) {
1394 fputs ("Reply-To: ", fp);
1395 mutt_write_address_list (env->reply_to, fp, 10, 0);
1397 else if (mode > 0 && edit_header(mode, "Reply-To:"))
1398 fputs ("Reply-To:\n", fp);
1400 if (env->mail_followup_to) {
1401 fputs ("Mail-Followup-To: ", fp);
1402 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1406 if (env->references) {
1407 fputs ("References:", fp);
1408 mutt_write_references (env->references, fp);
1412 /* Add the MIME headers */
1413 fputs ("MIME-Version: 1.0\n", fp);
1414 mutt_write_mime_header (attach, fp);
1417 if (env->in_reply_to) {
1418 fputs ("In-Reply-To:", fp);
1419 mutt_write_references (env->in_reply_to, fp);
1423 /* Add any user defined headers */
1424 for (; tmp; tmp = tmp->next) {
1425 if ((p = strchr (tmp->data, ':'))) {
1426 p = vskipspaces(p + 1);
1428 continue; /* don't emit empty fields. */
1430 /* check to see if the user has overridden the user-agent field */
1431 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1435 fputs (tmp->data, fp);
1440 if (mode == 0 && option (OPTXMAILER) && !has_agent) {
1441 if (mod_core.operating_system) {
1442 fprintf(fp, "User-Agent: %s (%s)\n", mutt_make_version(),
1443 mod_core.operating_system);
1445 fprintf(fp, "User-Agent: %s\n", mutt_make_version());
1449 return (ferror (fp) == 0 ? 0 : -1);
1452 static void encode_headers (string_list_t * h)
1458 for (; h; h = h->next) {
1459 if (!(p = strchr (h->data, ':')))
1463 p = vskipspaces(p + 1);
1469 rfc2047_encode_string (&tmp);
1470 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1472 sprintf (h->data + i, ": %s", NONULL (tmp));
1478 const char *mutt_fqdn(short may_hide_host)
1482 if (mod_core.hostname && mod_core.hostname[0] != '@') {
1483 p = mod_core.hostname;
1485 if (may_hide_host && option (OPTHIDDENHOST)) {
1486 if ((p = strchr(mod_core.hostname, '.')))
1489 /* sanity check: don't hide the host if
1490 the fqdn is something like detebe.org. */
1492 if (!p || !(q = strchr(p, '.')))
1493 p = mod_core.hostname;
1500 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1502 #define APPEND_FMT(fmt, arg) \
1504 int snlen = snprintf(buf, len, fmt, arg); \
1509 #define APPEND_BYTE(c) \
1523 static char MsgIdPfx = 'A';
1527 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1528 APPEND_BYTE((isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.');
1536 APPEND_FMT("%02d", tm->tm_mday);
1539 APPEND_FMT("%02d", tm->tm_hour);
1542 APPEND_FMT("%02d", tm->tm_mon + 1);
1545 APPEND_FMT("%02d", tm->tm_min);
1548 APPEND_FMT("%lo", (unsigned long)now);
1551 APPEND_FMT("%u", (unsigned int)getpid());
1554 APPEND_FMT("%c", MsgIdPfx);
1555 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1558 APPEND_FMT("%u", (unsigned int)rand());
1561 APPEND_FMT("%x", (unsigned int)rand());
1564 APPEND_FMT("%02d", tm->tm_sec);
1567 APPEND_FMT("%u", (unsigned int) now);
1570 APPEND_FMT("%x", (unsigned int) now);
1572 case 'Y': /* this will break in the year 10000 ;-) */
1573 APPEND_FMT("%04d", tm->tm_year + 1900);
1578 default: /* invalid formats are replaced by '.' */
1589 static char *mutt_gen_msgid (void)
1592 char localpart[STRING];
1595 if (!(fqdn = mutt_fqdn(0)))
1596 fqdn = NONULL(mod_core.shorthost);
1598 mutt_gen_localpart(localpart, sizeof(localpart), MsgIdFormat);
1599 snprintf(buf, sizeof(buf), "<%s@%s>", localpart, fqdn);
1600 return m_strdup(buf);
1603 static void alarm_handler (int sig __attribute__ ((unused)))
1608 /* invoke sendmail in a subshell
1609 path (in) path to program to execute
1610 args (in) arguments to pass to program
1611 msg (in) temp file containing message to send
1612 tempfile (out) if sendmail is put in the background, this points
1613 to the temporary file containing the stdout of the
1616 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1622 mutt_block_signals_system ();
1625 /* we also don't want to be stopped right now */
1626 sigaddset (&set, SIGTSTP);
1627 sigprocmask (SIG_BLOCK, &set, NULL);
1629 if (MTransport.sendmail_wait >= 0) {
1630 char tmp[_POSIX_PATH_MAX];
1633 *tempfile = m_strdup(tmp);
1636 if ((pid = fork ()) == 0) {
1637 struct sigaction act, oldalrm;
1639 /* save parent's ID before setsid() */
1642 /* we want the delivery to continue even after the main process dies,
1643 * so we put ourselves into another session right away
1647 /* next we close all open files */
1648 for (fd = 0; fd < getdtablesize(); fd++)
1651 /* now the second fork() */
1652 if ((pid = fork ()) == 0) {
1653 /* "msg" will be opened as stdin */
1654 if (open (msg, O_RDONLY, 0) < 0) {
1660 if (MTransport.sendmail_wait >= 0) {
1661 /* *tempfile will be opened as stdout */
1662 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1665 /* redirect stderr to *tempfile too */
1669 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1671 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1675 execv (path, (char**)args);
1678 else if (pid == -1) {
1684 /* sendmail_wait > 0: interrupt waitpid() after sendmail_wait seconds
1685 * sendmail_wait = 0: wait forever
1686 * sendmail_wait < 0: don't wait
1688 if (MTransport.sendmail_wait > 0) {
1690 act.sa_handler = alarm_handler;
1692 /* need to make sure waitpid() is interrupted on SIGALRM */
1693 act.sa_flags = SA_INTERRUPT;
1697 sigemptyset (&act.sa_mask);
1698 sigaction (SIGALRM, &act, &oldalrm);
1699 alarm (MTransport.sendmail_wait);
1701 else if (MTransport.sendmail_wait < 0)
1702 _exit (0xff & EX_OK);
1704 if (waitpid (pid, &st, 0) > 0) {
1705 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1706 if (MTransport.sendmail_wait && st == (0xff & EX_OK)) {
1707 unlink (*tempfile); /* no longer needed */
1711 st = (MTransport.sendmail_wait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1712 if (MTransport.sendmail_wait > 0) {
1718 /* reset alarm; not really needed, but... */
1720 sigaction (SIGALRM, &oldalrm, NULL);
1722 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1723 /* the parent is already dead */
1731 sigprocmask (SIG_UNBLOCK, &set, NULL);
1733 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1734 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1736 st = S_ERR; /* error */
1738 mutt_unblock_signals_system (1);
1743 static const char **
1744 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1746 for (; addr; addr = addr->next) {
1747 /* weed out group mailboxes, since those are for display only */
1748 if (addr->mailbox && !addr->group) {
1749 if (*argslen == *argsmax)
1750 p_realloc(&args, *argsmax += 5);
1751 args[(*argslen)++] = addr->mailbox;
1757 static const char **
1758 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1760 if (*argslen == *argsmax) {
1761 p_realloc(&args, *argsmax += 5);
1763 args[(*argslen)++] = s;
1767 int mutt_invoke_mta(address_t *from, address_t *to, address_t *cc,
1768 address_t *bcc, const char *msg, int eightbit)
1770 char cmd[LONG_STRING];
1771 char *ps = NULL, *path = NULL, *childout = NULL;
1772 const char **args = NULL;
1773 ssize_t argslen = 0, argsmax = 0;
1776 m_strcpy(cmd, sizeof(cmd), MTransport.sendmail);
1780 while ((ps = strtok(ps, " "))) {
1781 if (argslen == argsmax)
1782 p_realloc(&args, argsmax += 5);
1785 args[argslen++] = ps;
1787 path = m_strdup(ps);
1788 ps = strrchr (ps, '/');
1793 args[argslen++] = ps;
1799 if (eightbit && MTransport.use_8bitmime)
1800 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1802 if (MTransport.use_envelope_from) {
1803 address_t *f = MTransport.envelope_from_address;
1804 if (!f && from && !from->next)
1807 args = add_option (args, &argslen, &argsmax, "-f");
1808 args = add_args (args, &argslen, &argsmax, f);
1811 if (MTransport.dsn_notify) {
1812 args = add_option (args, &argslen, &argsmax, "-N");
1813 args = add_option (args, &argslen, &argsmax, MTransport.dsn_notify);
1815 if (MTransport.dsn_return) {
1816 args = add_option (args, &argslen, &argsmax, "-R");
1817 args = add_option (args, &argslen, &argsmax, MTransport.dsn_return);
1819 args = add_option (args, &argslen, &argsmax, "--");
1820 args = add_args (args, &argslen, &argsmax, to);
1821 args = add_args (args, &argslen, &argsmax, cc);
1822 args = add_args (args, &argslen, &argsmax, bcc);
1824 if (argslen >= argsmax)
1825 p_realloc(&args, ++argsmax);
1827 args[argslen++] = NULL;
1829 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1831 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1836 if (!stat(childout, &st) && st.st_size > 0)
1837 mutt_pager(_("Output of the delivery process"), childout, 0, NULL);
1844 p_delete(&childout);
1848 if (i == (EX_OK & 0xff))
1850 else if (i == S_BKG)
1857 /* For postponing (!final) do the necessary encodings only */
1858 void mutt_prepare_envelope (ENVELOPE * env, int final)
1861 if (env->bcc && !(env->to || env->cc)) {
1862 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1863 * recipients if there is no To: or Cc: field, so attempt to suppress
1864 * it by using an empty To: field.
1866 env->to = address_new();
1868 env->to->next = address_new();
1869 env->to->mailbox = m_strdup("undisclosed-recipients");
1872 mutt_set_followup_to(env);
1874 if (!env->message_id && !m_strisempty(MsgIdFormat))
1875 env->message_id = mutt_gen_msgid();
1878 /* Take care of 8-bit => 7-bit conversion. */
1879 rfc2047_encode_adrlist(env->to, "To");
1880 rfc2047_encode_adrlist(env->cc, "Cc");
1881 rfc2047_encode_adrlist(env->bcc, "Bcc");
1882 rfc2047_encode_adrlist(env->from, "From");
1883 rfc2047_encode_adrlist(env->mail_followup_to, "Mail-Followup-To");
1884 rfc2047_encode_adrlist(env->reply_to, "Reply-To");
1887 rfc2047_encode_string (&env->subject);
1888 encode_headers (env->userhdrs);
1891 void mutt_unprepare_envelope (ENVELOPE * env)
1893 string_list_t *item;
1895 for (item = env->userhdrs; item; item = item->next)
1896 rfc2047_decode(&item->data);
1898 address_list_wipe(&env->mail_followup_to);
1900 /* back conversions */
1901 rfc2047_decode_adrlist(env->to);
1902 rfc2047_decode_adrlist(env->cc);
1903 rfc2047_decode_adrlist(env->bcc);
1904 rfc2047_decode_adrlist(env->from);
1905 rfc2047_decode_adrlist(env->reply_to);
1906 rfc2047_decode(&env->subject);
1909 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
1910 const char *resent_from, address_t * env_from)
1914 char date[STRING], tempfile[_POSIX_PATH_MAX];
1915 MESSAGE *msg = NULL;
1918 /* Try to bounce each message out, aborting if we get any failures. */
1919 for (i = 0; i < Context->msgcount; i++)
1920 if (Context->hdrs[i]->tagged)
1922 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
1927 /* If we failed to open a message, return with error */
1928 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
1934 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
1936 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
1938 if (!option (OPTBOUNCEDELIVERED))
1939 ch_flags |= CH_WEED_DELIVERED;
1941 fseeko (fp, h->offset, 0);
1942 fprintf (f, "Resent-From: %s", resent_from);
1943 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
1944 if (!m_strisempty(MsgIdFormat))
1945 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
1946 fputs ("Resent-To: ", f);
1947 mutt_write_address_list (to, f, 11, 0);
1948 mutt_copy_header (fp, h, f, ch_flags, NULL);
1950 mutt_copy_bytes (fp, f, h->content->length);
1953 ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
1954 h->content->encoding == ENC8BIT);
1958 mx_close_message (&msg);
1963 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
1966 char resent_from[STRING];
1970 resent_from[0] = '\0';
1971 from = mutt_default_from ();
1973 rfc822_qualify(from, mutt_fqdn(1));
1975 rfc2047_encode_adrlist(from, "Resent-From");
1976 if (mutt_addrlist_to_idna (from, &err)) {
1977 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
1980 rfc822_addrcat(resent_from, sizeof(resent_from), from, 0);
1981 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
1982 address_list_wipe(&from);
1986 static void set_noconv_flags (BODY * b, short flag)
1988 for (; b; b = b->next) {
1989 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1990 set_noconv_flags (b->parts, flag);
1991 else if (b->type == TYPETEXT && b->noconv) {
1992 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
1997 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
1998 int post, char *fcc)
2002 char tempfile[_POSIX_PATH_MAX];
2003 FILE *tempfp = NULL;
2007 set_noconv_flags (hdr->content, 1);
2009 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2013 /* We need to add a Content-Length field to avoid problems where a line in
2014 * the message body begins with "From "
2016 if (f.magic == M_MBOX) {
2017 tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2019 mutt_error(_("Could not create temporary file"));
2020 mx_close_mailbox (&f, NULL);
2025 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2026 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2027 mx_close_mailbox (&f, NULL);
2031 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2032 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2034 mutt_write_rfc822_header(msg->fp, hdr->env, hdr->content, -post);
2036 /* (postponment) if this was a reply of some sort, <msgid> contians the
2037 * Message-ID: of message replied to. Save it using a special X-Mutt-
2038 * header so it can be picked up if the message is recalled at a later
2039 * point in time. This will allow the message to be marked as replied if
2040 * the same mailbox is still open.
2043 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2045 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2046 * it can be picked up when the message is recalled
2049 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2050 fprintf (msg->fp, "Status: RO\n");
2052 /* (postponment) if the mail is to be signed or encrypted, save this info */
2053 if (post && (hdr->security & APPLICATION_PGP)) {
2054 fputs ("X-Mutt-PGP: ", msg->fp);
2055 if (hdr->security & ENCRYPT)
2056 fputc ('E', msg->fp);
2057 if (hdr->security & SIGN) {
2058 fputc ('S', msg->fp);
2059 if (PgpSignAs && *PgpSignAs)
2060 fprintf (msg->fp, "<%s>", PgpSignAs);
2062 if (hdr->security & INLINE)
2063 fputc ('I', msg->fp);
2064 fputc ('\n', msg->fp);
2067 /* (postponment) if the mail is to be signed or encrypted, save this info */
2068 if (post && (hdr->security & APPLICATION_SMIME)) {
2069 fputs ("X-Mutt-SMIME: ", msg->fp);
2070 if (hdr->security & ENCRYPT) {
2071 fputc ('E', msg->fp);
2072 if (SmimeCryptAlg && *SmimeCryptAlg)
2073 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2075 if (hdr->security & SIGN) {
2076 fputc ('S', msg->fp);
2077 if (SmimeDefaultKey && *SmimeDefaultKey)
2078 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2080 if (hdr->security & INLINE)
2081 fputc ('I', msg->fp);
2082 fputc ('\n', msg->fp);
2086 char sasha[LONG_STRING];
2089 mutt_write_mime_body (hdr->content, tempfp);
2091 /* make sure the last line ends with a newline. Emacs doesn't ensure
2092 * this will happen, and it can cause problems parsing the mailbox
2095 fseeko (tempfp, -1, 2);
2096 if (fgetc (tempfp) != '\n') {
2097 fseeko (tempfp, 0, 2);
2098 fputc ('\n', tempfp);
2102 if (ferror (tempfp)) {
2105 mx_commit_message (msg, &f); /* XXX - really? */
2106 mx_close_message (&msg);
2107 mx_close_mailbox (&f, NULL);
2111 /* count the number of lines */
2113 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2115 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2116 fprintf (msg->fp, "Lines: %d\n\n", lines);
2118 /* copy the body and clean up */
2120 r = mutt_copy_stream (tempfp, msg->fp);
2121 if (m_fclose(&tempfp) != 0)
2123 /* if there was an error, leave the temp version */
2127 fputc ('\n', msg->fp); /* finish off the header */
2128 r = mutt_write_mime_body (hdr->content, msg->fp);
2131 if (mx_commit_message (msg, &f) != 0)
2133 mx_close_message (&msg);
2134 mx_close_mailbox (&f, NULL);
2137 set_noconv_flags (hdr->content, 0);