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/lib-ui.h>
19 #include <lib-mx/mx.h>
24 #include "recvattach.h"
28 #include "mutt_idna.h"
30 #ifdef HAVE_SYSEXITS_H
32 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
36 static void transform_to_7bit (BODY * a, FILE * fpin);
38 static void encode_quoted (fgetconv_t * fc, FILE * fout, int istext)
41 char line[77], savechar;
43 while ((c = fgetconv (fc)) != EOF) {
44 /* Wrap the line if needed. */
45 if (linelen == 76 && ((istext && c != '\n') || !istext)) {
46 /* If the last character is "quoted", then be sure to move all three
47 * characters to the next line. Otherwise, just move the last
50 if (line[linelen - 3] == '=') {
51 line[linelen - 3] = 0;
56 line[1] = line[linelen - 2];
57 line[2] = line[linelen - 1];
61 savechar = line[linelen - 1];
62 line[linelen - 1] = '=';
71 /* Escape lines that begin with/only contain "the message separator". */
72 if (linelen == 4 && !m_strncmp("From", line, 4)) {
73 m_strcpy(line, sizeof(line), "=46rom");
76 else if (linelen == 4 && !m_strncmp("from", line, 4)) {
77 m_strcpy(line, sizeof(line), "=66rom");
80 else if (linelen == 1 && line[0] == '.') {
81 m_strcpy(line, sizeof(line), "=2E");
86 if (c == '\n' && istext) {
87 /* Check to make sure there is no trailing space on this line. */
89 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
91 sprintf (line + linelen - 1, "=%2.2X",
92 (unsigned char) line[linelen - 1]);
96 savechar = line[linelen - 1];
98 line[linelen - 1] = '=';
101 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
111 else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
112 /* Check to make sure there is enough room for the quoted character.
113 * If not, wrap to the next line.
116 line[linelen++] = '=';
122 sprintf (line + linelen, "=%2.2X", (unsigned char) c);
126 /* Don't worry about wrapping the line here. That will happen during
127 * the next iteration when I'll also know what the next character is.
133 /* Take care of anything left in the buffer */
135 if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
136 /* take care of trailing whitespace */
138 sprintf (line + linelen - 1, "=%2.2X",
139 (unsigned char) line[linelen - 1]);
141 savechar = line[linelen - 1];
142 line[linelen - 1] = '=';
146 sprintf (line, "=%2.2X", (unsigned char) savechar);
155 static char b64_buffer[3];
156 static short b64_num;
157 static short b64_linelen;
159 static void b64_flush (FILE * fout)
166 if (b64_linelen >= 72) {
171 for (i = b64_num; i < 3; i++)
172 b64_buffer[i] = '\0';
174 fputc(__m_b64chars[(b64_buffer[0] >> 2) & 0x3f], fout);
177 [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
182 [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
186 fputc (__m_b64chars[b64_buffer[2] & 0x3f], fout);
191 while (b64_linelen % 4) {
200 static void b64_putc (char c, FILE * fout)
205 b64_buffer[b64_num++] = c;
209 static void encode_base64 (fgetconv_t * fc, FILE * fout, int istext)
213 b64_num = b64_linelen = 0;
215 while ((ch = fgetconv (fc)) != EOF) {
216 if (istext && ch == '\n' && ch1 != '\r')
217 b64_putc ('\r', fout);
225 static void encode_8bit (fgetconv_t * fc, FILE * fout,
226 int istext __attribute__ ((unused)))
230 while ((ch = fgetconv (fc)) != EOF)
235 int mutt_write_mime_header (BODY * a, FILE * f)
244 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
249 len = 25 + m_strlen(a->subtype); /* approximate len. of content-type */
251 for (p = a->parameter; p; p = p->next) {
260 tmp = m_strdup(p->value);
261 encode = rfc2231_encode_string (&tmp);
262 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
264 /* Dirty hack to make messages readable by Outlook Express
265 * for the Mac: force quotes around the boundary parameter
266 * even when they aren't needed.
269 if (!ascii_strcasecmp (p->attribute, "boundary")
270 && !strcmp (buffer, tmp))
271 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
275 tmplen = m_strlen(buffer) + m_strlen(p->attribute) + 1;
277 if (len + tmplen + 2 > 76) {
286 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
294 fprintf (f, "Content-Description: %s\n", a->description);
296 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
297 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
300 if (!(fn = a->d_filename))
306 /* Strip off the leading path... */
307 if ((t = strrchr (fn, '/')))
314 encode = rfc2231_encode_string (&tmp);
315 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
317 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
323 if (a->encoding != ENC7BIT)
324 fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
326 /* Do NOT add the terminator here!!! */
327 return ferror (f) ? -1 : 0;
330 int mutt_write_mime_body (BODY * a, FILE * f)
333 char boundary[STRING];
334 char send_charset[STRING];
339 if (a->type == TYPEMULTIPART) {
340 /* First, find the boundary to use */
341 if (!(p = parameter_getval(a->parameter, "boundary"))) {
342 mutt_error _("No boundary parameter found! [report this error]");
346 m_strcpy(boundary, sizeof(boundary), p);
348 for (t = a->parts; t; t = t->next) {
349 fprintf (f, "\n--%s\n", boundary);
350 if (mutt_write_mime_header (t, f) == -1)
353 if (mutt_write_mime_body (t, f) == -1)
356 fprintf (f, "\n--%s--\n", boundary);
357 return ferror (f) ? -1 : 0;
360 /* This is pretty gross, but it's the best solution for now... */
361 if (a->type == TYPEAPPLICATION && !m_strcmp(a->subtype, "pgp-encrypted")) {
362 fputs ("Version: 1\n", f);
366 if ((fpin = fopen (a->filename, "r")) == NULL) {
367 mutt_error (_("%s no longer exists!"), a->filename);
371 if (a->type == TYPETEXT && (!a->noconv))
372 fc = fgetconv_open (fpin, a->file_charset,
373 mutt_get_body_charset (send_charset,
374 sizeof (send_charset), a), 0);
376 fc = fgetconv_open (fpin, 0, 0, 0);
378 #define write_as_text_part(a) (mutt_is_text_part(a) || mutt_is_application_pgp(a))
379 if (a->encoding == ENCQUOTEDPRINTABLE)
380 encode_quoted (fc, f, write_as_text_part (a));
381 else if (a->encoding == ENCBASE64)
382 encode_base64 (fc, f, write_as_text_part (a));
383 else if (a->type == TYPETEXT && (!a->noconv))
384 encode_8bit (fc, f, write_as_text_part (a));
386 mutt_copy_stream (fpin, f);
387 #undef write_as_text_part
389 fgetconv_close (&fc);
392 return ferror (f) ? -1 : 0;
404 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
408 int whitespace = s->whitespace;
410 int linelen = s->linelen;
411 int was_cr = s->was_cr;
413 if (!d) { /* This signals EOF */
416 if (linelen > info->linemax)
417 info->linemax = linelen;
422 for (; dlen; d++, dlen--) {
435 if (linelen > info->linemax)
436 info->linemax = linelen;
451 if (linelen > info->linemax)
452 info->linemax = linelen;
457 else if (ch == '\r') {
465 else if (ch == '\t' || ch == '\f') {
469 else if (ch < 32 || ch == 127)
473 if ((ch == 'F') || (ch == 'f'))
483 if (linelen == 2 && ch != 'r')
485 else if (linelen == 3 && ch != 'o')
487 else if (linelen == 4) {
500 if (ch != ' ' && ch != '\t')
505 s->whitespace = whitespace;
507 s->linelen = linelen;
513 * Find the best charset conversion of the file from fromcode into one
514 * of the tocodes. If successful, set *tocode and CONTENT *info and
515 * return the number of characters converted inexactly. If no
516 * conversion was possible, return -1.
518 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
519 * which would otherwise prevent us from knowing the number of inexact
520 * conversions. Where the candidate target charset is UTF-8 we avoid
521 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
522 * fails with some libraries.
524 * We assume that the output from iconv is never more than 4 times as
525 * long as the input for any pair of charsets we might be interested
528 static ssize_t convert_file_to (FILE * file, const char *fromcode,
529 int ncodes, const char **tocodes,
530 int *tocode, CONTENT * info)
533 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
536 ssize_t ibl, obl, ubl, ubl1, n, ret;
539 CONTENT_STATE *states;
542 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
543 if (cd1 == MUTT_ICONV_ERROR)
546 cd = p_new(iconv_t, ncodes);
547 score = p_new(ssize_t, ncodes);
548 states = p_new(CONTENT_STATE, ncodes);
549 infos = p_new(CONTENT, ncodes);
551 for (i = 0; i < ncodes; i++)
552 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
553 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
555 /* Special case for conversion to UTF-8 */
556 cd[i] = MUTT_ICONV_ERROR, score[i] = -1;
562 /* Try to fill input buffer */
563 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
566 /* Convert to UTF-8 */
568 ob = bufu, obl = sizeof (bufu);
569 n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
570 if (n == -1 && ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
576 /* Convert from UTF-8 */
577 for (i = 0; i < ncodes; i++)
578 if (cd[i] != MUTT_ICONV_ERROR && score[i] != -1) {
579 ub = bufu, ubl = ubl1;
580 ob = bufo, obl = sizeof (bufo);
581 n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
587 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
590 else if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1)
591 /* Special case for conversion to UTF-8 */
592 update_content_info (&infos[i], &states[i], bufu, ubl1);
595 /* Save unused input */
596 memmove (bufi, ib, ibl);
597 else if (!ubl1 && ib < bufi + sizeof (bufi)) {
604 /* Find best score */
606 for (i = 0; i < ncodes; i++) {
607 if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1) {
608 /* Special case for conversion to UTF-8 */
613 else if (cd[i] == MUTT_ICONV_ERROR || score[i] == -1)
615 else if (ret == -1 || score[i] < ret) {
623 memcpy (info, &infos[*tocode], sizeof (CONTENT));
624 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
628 for (i = 0; i < ncodes; i++)
629 if (cd[i] != MUTT_ICONV_ERROR)
642 * Find the first of the fromcodes that gives a valid conversion and
643 * the best charset conversion of the file into one of the tocodes. If
644 * successful, set *fromcode and *tocode to dynamically allocated
645 * strings, set CONTENT *info, and return the number of characters
646 * converted inexactly. If no conversion was possible, return -1.
648 * Both fromcodes and tocodes may be colon-separated lists of charsets.
649 * However, if fromcode is zero then fromcodes is assumed to be the
650 * name of a single charset even if it contains a colon.
652 static ssize_t convert_file_from_to (FILE * file,
653 const char *fromcodes,
654 const char *tocodes, char **fromcode,
655 char **tocode, CONTENT * info)
663 /* Count the tocodes */
665 for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
666 if ((c1 = strchr (c, ':')) == c)
672 tcode = p_new(char *, ncodes);
673 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
674 if ((c1 = strchr (c, ':')) == c)
676 tcode[i] = m_substrdup(c, c1);
681 /* Try each fromcode in turn */
682 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
683 if ((c1 = strchr (c, ':')) == c)
685 fcode = m_substrdup(c, c1);
687 ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
699 /* There is only one fromcode */
700 ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
709 for (i = 0; i < ncodes; i++)
718 * Analyze the contents of a file to determine which MIME encoding to use.
719 * Also set the body charset, sometimes, or not.
721 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
726 char *fromcode = NULL;
737 if (stat (fname, &sb) == -1) {
738 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
742 if (!S_ISREG (sb.st_mode)) {
743 mutt_error (_("%s isn't a regular file."), fname);
747 if ((fp = fopen (fname, "r")) == NULL) {
751 info = p_new(CONTENT, 1);
754 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
755 const char *chs = parameter_getval(b->parameter, "charset");
756 char *fchs = b->use_disp && !m_strisempty(mod_cset.file_charset)
757 ? FileCharset : mod_cset.charset;
758 if (mod_cset.charset && (chs || mod_cset.send_charset) &&
759 convert_file_from_to (fp, fchs, chs ? chs : mod_cset.send_charset,
760 &fromcode, &tocode, info) != -1) {
762 charset_canonicalize (chsbuf, sizeof (chsbuf), tocode);
763 parameter_setval(&b->parameter, "charset", chsbuf);
765 b->file_charset = fromcode;
773 while ((r = fread (buffer, 1, sizeof (buffer), fp)))
774 update_content_info (info, &state, buffer, r);
775 update_content_info (info, &state, 0, 0);
779 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
780 parameter_setval(&b->parameter, "charset",
781 (!info->hibin ? "us-ascii"
782 : mod_cset.charset && !charset_is_us_ascii(mod_cset.charset)
783 ? mod_cset.charset : "unknown-8bit"));
788 /* Given a file with path ``s'', see if there is a registered MIME type.
789 * returns the major MIME type, and copies the subtype to ``d''. First look
790 * for ~/.mime.types, then look in a system mime.types if we can find one.
791 * The longest match is used so that we can match `ps.gz' when `gz' also
795 int mutt_lookup_mime_type (BODY * att, const char *path)
799 char buf[LONG_STRING];
800 char subtype[STRING], xtype[STRING];
802 int szf, sze, cur_sze;
810 szf = m_strlen(path);
812 for (count = 0; count < 4; count++) {
814 * can't use strtok() because we use it in an inner loop below, so use
815 * a switch statement here instead.
819 snprintf(buf, sizeof (buf), "%s/.mime.types", NONULL(mod_core.homedir));
822 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
825 m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
828 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
831 goto bye; /* shouldn't happen */
834 if ((f = fopen (buf, "r")) != NULL) {
835 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
836 /* weed out any comments */
837 if ((p = strchr (buf, '#')))
840 /* remove any leading space. */
841 ct = vskipspaces(buf);
843 /* position on the next field in this line */
844 if ((p = strpbrk (ct, " \t")) == NULL)
849 /* cycle through the file extensions */
850 while ((p = strtok (p, " \t\n"))) {
852 if ((sze > cur_sze) && (szf >= sze) &&
853 (m_strcasecmp(path + szf - sze, p) == 0
854 || ascii_strcasecmp (path + szf - sze, p) == 0)
855 && (szf == sze || path[szf - sze - 1] == '.'))
857 /* get the content-type */
859 if ((p = strchr (ct, '/')) == NULL) {
860 /* malformed line, just skip it. */
865 for (q = p; *q && !ISSPACE (*q); q++);
867 m_strncpy(subtype, sizeof(subtype), p, q - p);
869 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
870 m_strcpy(xtype, sizeof(xtype), ct);
883 if (type != TYPEOTHER || *xtype != '\0') {
885 m_strreplace(&att->subtype, subtype);
886 m_strreplace(&att->xtype, xtype);
892 void mutt_message_to_7bit (BODY * a, FILE * fp)
894 char temp[_POSIX_PATH_MAX];
900 if (!a->filename && fp)
902 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
903 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
908 if (stat (a->filename, &sb) == -1) {
909 mutt_perror ("stat");
912 a->length = sb.st_size;
915 fpout = m_tempfile(temp, sizeof(temp), NONULL(mod_core.tmpdir), NULL);
917 mutt_error(_("Could not create temporary file"));
921 fseeko (fpin, a->offset, 0);
922 a->parts = mutt_parse_messageRFC822 (fpin, a);
924 transform_to_7bit (a->parts, fpin);
926 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
927 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
929 fputs ("MIME-Version: 1.0\n", fpout);
930 mutt_write_mime_header (a->parts, fpout);
932 mutt_write_mime_body (a->parts, fpout);
944 a->encoding = ENC7BIT;
945 a->d_filename = a->filename;
946 if (a->filename && a->unlink)
947 unlink (a->filename);
948 a->filename = m_strdup(temp);
950 if (stat (a->filename, &sb) == -1) {
951 mutt_perror ("stat");
954 a->length = sb.st_size;
955 body_list_wipe(&a->parts);
956 a->hdr->content = NULL;
959 static void transform_to_7bit (BODY * a, FILE * fpin)
961 char buff[_POSIX_PATH_MAX];
966 for (; a; a = a->next) {
967 if (a->type == TYPEMULTIPART) {
968 if (a->encoding != ENC7BIT)
969 a->encoding = ENC7BIT;
971 transform_to_7bit (a->parts, fpin);
973 else if (mutt_is_message_type(a)) {
974 mutt_message_to_7bit (a, fpin);
978 a->force_charset = 1;
980 s.fpout = m_tempfile(buff, sizeof(buff), NONULL(mod_core.tmpdir), NULL);
982 mutt_error(_("Could not create temporary file"));
986 mutt_decode_attachment (a, &s);
988 a->d_filename = a->filename;
989 a->filename = m_strdup(buff);
991 if (stat (a->filename, &sb) == -1) {
992 mutt_perror ("stat");
995 a->length = sb.st_size;
997 mutt_update_encoding (a);
998 if (a->encoding == ENC8BIT)
999 a->encoding = ENCQUOTEDPRINTABLE;
1000 else if (a->encoding == ENCBINARY)
1001 a->encoding = ENCBASE64;
1006 /* determine which Content-Transfer-Encoding to use */
1007 static void mutt_set_encoding (BODY * b, CONTENT * info)
1009 char send_charset[STRING];
1011 if (b->type == TYPETEXT) {
1013 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1014 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1015 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1016 b->encoding = ENCQUOTEDPRINTABLE;
1017 else if (info->hibin)
1018 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1020 b->encoding = ENC7BIT;
1022 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1023 if (info->lobin || info->hibin) {
1024 if (option (OPTALLOW8BIT) && !info->lobin)
1025 b->encoding = ENC8BIT;
1027 mutt_message_to_7bit (b, NULL);
1030 b->encoding = ENC7BIT;
1032 else if (b->type == TYPEAPPLICATION
1033 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1034 b->encoding = ENC7BIT;
1037 /* Determine which encoding is smaller */
1038 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1039 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1040 b->encoding = ENCBASE64;
1042 b->encoding = ENCQUOTEDPRINTABLE;
1046 void mutt_stamp_attachment (BODY * a)
1048 a->stamp = time (NULL);
1051 /* Get a body's character set */
1053 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1057 if (b && b->type != TYPETEXT)
1060 p = b ? parameter_getval(b->parameter, "charset") : NULL;
1061 charset_canonicalize(d, dlen, p);
1066 /* Assumes called from send mode where BODY->filename points to actual file */
1067 void mutt_update_encoding (BODY * a)
1070 char chsbuff[STRING];
1072 /* override noconv when it's us-ascii */
1073 if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1076 if (!a->force_charset && !a->noconv)
1077 parameter_delval(&a->parameter, "charset");
1079 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1082 mutt_set_encoding (a, info);
1083 mutt_stamp_attachment (a);
1085 p_delete(&a->content);
1090 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1092 char buffer[LONG_STRING];
1095 int cmflags, chflags;
1096 int pgp = hdr->security;
1098 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1099 (hdr->security & ENCRYPT)) {
1102 fp = m_tempfile(buffer, sizeof(buffer), NONULL(mod_core.tmpdir), NULL);
1107 body->type = TYPEMESSAGE;
1108 body->subtype = m_strdup("rfc822");
1109 body->filename = m_strdup(buffer);
1112 body->disposition = DISPINLINE;
1115 mutt_parse_mime_message (ctx, hdr);
1120 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1121 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1122 chflags |= CH_MIME | CH_TXTPLAIN;
1123 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1124 pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1126 else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1127 if (mutt_is_multipart_encrypted (hdr->content)) {
1128 chflags |= CH_MIME | CH_NONEWLINE;
1129 cmflags = M_CM_DECODE_PGP;
1132 else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1133 chflags |= CH_MIME | CH_TXTPLAIN;
1134 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1137 else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1138 chflags |= CH_MIME | CH_TXTPLAIN;
1139 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1140 pgp &= ~SMIMEENCRYPT;
1144 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1149 body->hdr = header_new();
1150 body->hdr->offset = 0;
1151 /* we don't need the user headers here */
1152 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1153 body->hdr->security = pgp;
1154 mutt_update_encoding (body);
1155 body->parts = body->hdr->content;
1162 BODY *mutt_make_file_attach (const char *path)
1168 att->filename = m_strdup(path);
1170 /* Attempt to determine the appropriate content-type based on the filename
1173 mutt_lookup_mime_type (att, path);
1175 if ((info = mutt_get_content_info (path, att)) == NULL) {
1176 body_list_wipe(&att);
1180 if (!att->subtype) {
1181 if (info->lobin == 0
1182 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1184 * Statistically speaking, there should be more than 10% "lobin"
1185 * chars if this is really a binary file...
1187 att->type = TYPETEXT;
1188 att->subtype = m_strdup("plain");
1190 att->type = TYPEAPPLICATION;
1191 att->subtype = m_strdup("octet-stream");
1195 mutt_update_encoding (att);
1199 static int get_toplevel_encoding (BODY * a)
1203 for (; a; a = a->next) {
1204 if (a->encoding == ENCBINARY)
1207 if (a->encoding == ENC8BIT)
1214 BODY *mutt_make_multipart (BODY * b)
1219 new->type = TYPEMULTIPART;
1220 new->subtype = m_strdup("mixed");
1221 new->encoding = get_toplevel_encoding (b);
1222 parameter_set_boundary(&new->parameter);
1224 new->disposition = DISPINLINE;
1230 /* remove the multipart body if it exists */
1231 BODY *mutt_remove_multipart (BODY * b)
1244 char *mutt_make_date (char *s, ssize_t len)
1246 time_t t = time(NULL);
1249 loc = setlocale(LC_TIME, "C");
1250 strftime(s, len, "Date: %a, %d %b %Y %T %z\n", localtime(&t));
1251 setlocale(LC_TIME, loc);
1255 /* wrapper around mutt_write_address() so we can handle very large
1256 recipient lists without needing a huge temporary buffer in memory */
1258 mutt_write_address_list(address_t *addr, FILE *fp, int linelen, int display)
1263 char buf[LONG_STRING];
1264 int len = rfc822_addrcpy(buf, ssizeof(buf), addr, display);
1267 if (linelen + len > 74) {
1269 linelen = 8; /* tab is usually about 8 spaces... */
1271 if (addr->mailbox) {
1281 if (!addr->group && addr->next && addr->next->mailbox) {
1291 /* need to write the list in reverse because they are stored in reverse order
1292 * when parsed to speed up threading
1294 void mutt_write_references(string_list_t *r, FILE *f)
1296 string_list_t *refs[10];
1299 p_clear(refs, countof(refs));
1300 for (i = 0; i < countof(refs) && r; r = r->next) {
1305 fprintf(f, " %s", refs[i]->data);
1309 static int edit_header(int mode, const char *s)
1312 int slen = m_strlen(s);
1314 if (mode != 1 || option(OPTXMAILTO))
1317 p = skipspaces(EditorHeaders);
1319 if (!ascii_strncasecmp(p, s, slen) && p[slen - 1] == ':')
1321 p = skipspaces(p + slen);
1327 /* Note: all RFC2047 encoding should be done outside of this routine, except
1328 * for the "real name." This will allow this routine to be used more than
1329 * once, if necessary.
1331 * Likewise, all IDN processing should happen outside of this routine.
1333 * mode == 1 => "lite" mode (used for edit_hdrs)
1334 * mode == 0 => normal mode. write full header + MIME headers
1335 * mode == -1 => write just the envelope info (used for postponing messages)
1337 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1340 char buffer[LONG_STRING];
1342 string_list_t *tmp = env->userhdrs;
1343 int has_agent = 0; /* user defined user-agent header field exists */
1346 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1348 /* OPTUSEFROM is not consulted here so that we can still write a From:
1349 * field if the user sets it with the `my_hdr' command
1353 rfc822_addrcat(buffer, sizeof(buffer), env->from, 0);
1354 fprintf (fp, "From: %s\n", buffer);
1359 mutt_write_address_list (env->to, fp, 4, 0);
1362 if (edit_header(mode, "To:"))
1363 fputs ("To:\n", fp);
1367 mutt_write_address_list (env->cc, fp, 4, 0);
1370 if (edit_header(mode, "Cc:"))
1371 fputs ("Cc:\n", fp);
1374 if (mode != 0 || option (OPTWRITEBCC)) {
1375 fputs ("Bcc: ", fp);
1376 mutt_write_address_list (env->bcc, fp, 5, 0);
1380 if (edit_header(mode, "Bcc:"))
1381 fputs ("Bcc:\n", fp);
1384 fprintf (fp, "Subject: %s\n", env->subject);
1385 else if (mode == 1 && edit_header(mode, "Subject:"))
1386 fputs ("Subject:\n", fp);
1388 /* save message id if the user has set it */
1389 if (env->message_id)
1390 fprintf (fp, "Message-ID: %s\n", env->message_id);
1392 if (env->reply_to) {
1393 fputs ("Reply-To: ", fp);
1394 mutt_write_address_list (env->reply_to, fp, 10, 0);
1396 else if (mode > 0 && edit_header(mode, "Reply-To:"))
1397 fputs ("Reply-To:\n", fp);
1399 if (env->mail_followup_to) {
1400 fputs ("Mail-Followup-To: ", fp);
1401 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1405 if (env->references) {
1406 fputs ("References:", fp);
1407 mutt_write_references (env->references, fp);
1411 /* Add the MIME headers */
1412 fputs ("MIME-Version: 1.0\n", fp);
1413 mutt_write_mime_header (attach, fp);
1416 if (env->in_reply_to) {
1417 fputs ("In-Reply-To:", fp);
1418 mutt_write_references (env->in_reply_to, fp);
1422 /* Add any user defined headers */
1423 for (; tmp; tmp = tmp->next) {
1424 if ((p = strchr (tmp->data, ':'))) {
1425 p = vskipspaces(p + 1);
1427 continue; /* don't emit empty fields. */
1429 /* check to see if the user has overridden the user-agent field */
1430 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1434 fputs (tmp->data, fp);
1439 if (mode == 0 && option (OPTXMAILER) && !has_agent) {
1440 if (mod_core.operating_system) {
1441 fprintf(fp, "User-Agent: %s (%s)\n", madmutt_version,
1442 mod_core.operating_system);
1444 fprintf(fp, "User-Agent: %s\n", madmutt_version);
1448 return ferror (fp) == 0 ? 0 : -1;
1451 static void encode_headers (string_list_t * h)
1457 for (; h; h = h->next) {
1458 if (!(p = strchr (h->data, ':')))
1462 p = vskipspaces(p + 1);
1468 rfc2047_encode_string (&tmp);
1469 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1471 sprintf (h->data + i, ": %s", NONULL (tmp));
1477 const char *mutt_fqdn(short may_hide_host)
1481 if (mod_core.hostname && mod_core.hostname[0] != '@') {
1482 p = mod_core.hostname;
1484 if (may_hide_host && option (OPTHIDDENHOST)) {
1485 if ((p = strchr(mod_core.hostname, '.')))
1488 /* sanity check: don't hide the host if
1489 the fqdn is something like detebe.org. */
1491 if (!p || !(q = strchr(p, '.')))
1492 p = mod_core.hostname;
1499 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1501 #define APPEND_FMT(fmt, arg) \
1503 int snlen = snprintf(buf, len, fmt, arg); \
1508 #define APPEND_BYTE(c) \
1522 static char MsgIdPfx = 'A';
1526 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1527 APPEND_BYTE((isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.');
1535 APPEND_FMT("%02d", tm->tm_mday);
1538 APPEND_FMT("%02d", tm->tm_hour);
1541 APPEND_FMT("%02d", tm->tm_mon + 1);
1544 APPEND_FMT("%02d", tm->tm_min);
1547 APPEND_FMT("%lo", (unsigned long)now);
1550 APPEND_FMT("%u", (unsigned int)getpid());
1553 APPEND_FMT("%c", MsgIdPfx);
1554 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1557 APPEND_FMT("%u", (unsigned int)rand());
1560 APPEND_FMT("%x", (unsigned int)rand());
1563 APPEND_FMT("%02d", tm->tm_sec);
1566 APPEND_FMT("%u", (unsigned int) now);
1569 APPEND_FMT("%x", (unsigned int) now);
1571 case 'Y': /* this will break in the year 10000 ;-) */
1572 APPEND_FMT("%04d", tm->tm_year + 1900);
1577 default: /* invalid formats are replaced by '.' */
1588 static char *mutt_gen_msgid (void)
1591 char localpart[STRING];
1594 if (!(fqdn = mutt_fqdn(0)))
1595 fqdn = NONULL(mod_core.shorthost);
1597 mutt_gen_localpart(localpart, sizeof(localpart), MsgIdFormat);
1598 snprintf(buf, sizeof(buf), "<%s@%s>", localpart, fqdn);
1599 return m_strdup(buf);
1602 static void alarm_handler (int sig __attribute__ ((unused)))
1607 /* invoke sendmail in a subshell
1608 path (in) path to program to execute
1609 args (in) arguments to pass to program
1610 msg (in) temp file containing message to send
1611 tempfile (out) if sendmail is put in the background, this points
1612 to the temporary file containing the stdout of the
1615 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1621 mutt_block_signals_system ();
1624 /* we also don't want to be stopped right now */
1625 sigaddset (&set, SIGTSTP);
1626 sigprocmask (SIG_BLOCK, &set, NULL);
1628 if (MTransport.sendmail_wait >= 0) {
1629 char tmp[_POSIX_PATH_MAX];
1632 *tempfile = m_strdup(tmp);
1635 if ((pid = fork ()) == 0) {
1636 struct sigaction act, oldalrm;
1638 /* save parent's ID before setsid() */
1641 /* we want the delivery to continue even after the main process dies,
1642 * so we put ourselves into another session right away
1646 /* next we close all open files */
1647 for (fd = 0; fd < getdtablesize(); fd++)
1650 /* now the second fork() */
1651 if ((pid = fork ()) == 0) {
1652 /* "msg" will be opened as stdin */
1653 if (open (msg, O_RDONLY, 0) < 0) {
1659 if (MTransport.sendmail_wait >= 0) {
1660 /* *tempfile will be opened as stdout */
1661 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1664 /* redirect stderr to *tempfile too */
1668 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1670 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1674 execv (path, (char**)args);
1677 else if (pid == -1) {
1683 /* sendmail_wait > 0: interrupt waitpid() after sendmail_wait seconds
1684 * sendmail_wait = 0: wait forever
1685 * sendmail_wait < 0: don't wait
1687 if (MTransport.sendmail_wait > 0) {
1689 act.sa_handler = alarm_handler;
1691 /* need to make sure waitpid() is interrupted on SIGALRM */
1692 act.sa_flags = SA_INTERRUPT;
1696 sigemptyset (&act.sa_mask);
1697 sigaction (SIGALRM, &act, &oldalrm);
1698 alarm (MTransport.sendmail_wait);
1700 else if (MTransport.sendmail_wait < 0)
1701 _exit (0xff & EX_OK);
1703 if (waitpid (pid, &st, 0) > 0) {
1704 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1705 if (MTransport.sendmail_wait && st == (0xff & EX_OK)) {
1706 unlink (*tempfile); /* no longer needed */
1710 st = (MTransport.sendmail_wait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1711 if (MTransport.sendmail_wait > 0) {
1717 /* reset alarm; not really needed, but... */
1719 sigaction (SIGALRM, &oldalrm, NULL);
1721 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1722 /* the parent is already dead */
1730 sigprocmask (SIG_UNBLOCK, &set, NULL);
1732 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1733 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1735 st = S_ERR; /* error */
1737 mutt_unblock_signals_system (1);
1742 static const char **
1743 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1745 for (; addr; addr = addr->next) {
1746 /* weed out group mailboxes, since those are for display only */
1747 if (addr->mailbox && !addr->group) {
1748 if (*argslen == *argsmax)
1749 p_realloc(&args, *argsmax += 5);
1750 args[(*argslen)++] = addr->mailbox;
1756 static const char **
1757 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1759 if (*argslen == *argsmax) {
1760 p_realloc(&args, *argsmax += 5);
1762 args[(*argslen)++] = s;
1766 int mutt_invoke_mta(address_t *from, address_t *to, address_t *cc,
1767 address_t *bcc, const char *msg, int eightbit)
1769 char cmd[LONG_STRING];
1770 char *ps = NULL, *path = NULL, *childout = NULL;
1771 const char **args = NULL;
1772 ssize_t argslen = 0, argsmax = 0;
1775 m_strcpy(cmd, sizeof(cmd), MTransport.sendmail);
1779 while ((ps = strtok(ps, " "))) {
1780 if (argslen == argsmax)
1781 p_realloc(&args, argsmax += 5);
1784 args[argslen++] = ps;
1786 path = m_strdup(ps);
1787 ps = strrchr (ps, '/');
1792 args[argslen++] = ps;
1798 if (eightbit && MTransport.use_8bitmime)
1799 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1801 if (MTransport.use_envelope_from) {
1802 address_t *f = MTransport.envelope_from_address;
1803 if (!f && from && !from->next)
1806 args = add_option (args, &argslen, &argsmax, "-f");
1807 args = add_args (args, &argslen, &argsmax, f);
1810 if (MTransport.dsn_notify) {
1811 args = add_option (args, &argslen, &argsmax, "-N");
1812 args = add_option (args, &argslen, &argsmax, MTransport.dsn_notify);
1814 if (MTransport.dsn_return) {
1815 args = add_option (args, &argslen, &argsmax, "-R");
1816 args = add_option (args, &argslen, &argsmax, MTransport.dsn_return);
1818 args = add_option (args, &argslen, &argsmax, "--");
1819 args = add_args (args, &argslen, &argsmax, to);
1820 args = add_args (args, &argslen, &argsmax, cc);
1821 args = add_args (args, &argslen, &argsmax, bcc);
1823 if (argslen >= argsmax)
1824 p_realloc(&args, ++argsmax);
1826 args[argslen++] = NULL;
1828 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1830 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1835 if (!stat(childout, &st) && st.st_size > 0)
1836 mutt_pager(_("Output of the delivery process"), childout, 0, NULL);
1843 p_delete(&childout);
1847 if (i == (EX_OK & 0xff))
1849 else if (i == S_BKG)
1856 /* For postponing (!final) do the necessary encodings only */
1857 void mutt_prepare_envelope (ENVELOPE * env, int final)
1860 if (env->bcc && !(env->to || env->cc)) {
1861 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1862 * recipients if there is no To: or Cc: field, so attempt to suppress
1863 * it by using an empty To: field.
1865 env->to = address_new();
1867 env->to->next = address_new();
1868 env->to->mailbox = m_strdup("undisclosed-recipients");
1871 mutt_set_followup_to(env);
1873 if (!env->message_id && !m_strisempty(MsgIdFormat))
1874 env->message_id = mutt_gen_msgid();
1877 /* Take care of 8-bit => 7-bit conversion. */
1878 rfc2047_encode_adrlist(env->to, "To");
1879 rfc2047_encode_adrlist(env->cc, "Cc");
1880 rfc2047_encode_adrlist(env->bcc, "Bcc");
1881 rfc2047_encode_adrlist(env->from, "From");
1882 rfc2047_encode_adrlist(env->mail_followup_to, "Mail-Followup-To");
1883 rfc2047_encode_adrlist(env->reply_to, "Reply-To");
1886 rfc2047_encode_string (&env->subject);
1887 encode_headers (env->userhdrs);
1890 void mutt_unprepare_envelope (ENVELOPE * env)
1892 string_list_t *item;
1894 for (item = env->userhdrs; item; item = item->next)
1895 rfc2047_decode(&item->data);
1897 address_list_wipe(&env->mail_followup_to);
1899 /* back conversions */
1900 rfc2047_decode_adrlist(env->to);
1901 rfc2047_decode_adrlist(env->cc);
1902 rfc2047_decode_adrlist(env->bcc);
1903 rfc2047_decode_adrlist(env->from);
1904 rfc2047_decode_adrlist(env->reply_to);
1905 rfc2047_decode(&env->subject);
1908 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
1909 const char *resent_from, address_t * env_from)
1913 char date[STRING], tempfile[_POSIX_PATH_MAX];
1914 MESSAGE *msg = NULL;
1917 /* Try to bounce each message out, aborting if we get any failures. */
1918 for (i = 0; i < Context->msgcount; i++)
1919 if (Context->hdrs[i]->tagged)
1921 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
1926 /* If we failed to open a message, return with error */
1927 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
1933 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
1935 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
1937 if (!option (OPTBOUNCEDELIVERED))
1938 ch_flags |= CH_WEED_DELIVERED;
1940 fseeko (fp, h->offset, 0);
1941 fprintf (f, "Resent-From: %s", resent_from);
1942 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
1943 if (!m_strisempty(MsgIdFormat))
1944 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
1945 fputs ("Resent-To: ", f);
1946 mutt_write_address_list (to, f, 11, 0);
1947 mutt_copy_header (fp, h, f, ch_flags, NULL);
1949 mutt_copy_bytes (fp, f, h->content->length);
1952 ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
1953 h->content->encoding == ENC8BIT);
1957 mx_close_message (&msg);
1962 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
1965 char resent_from[STRING];
1969 resent_from[0] = '\0';
1970 from = mutt_default_from ();
1972 rfc822_qualify(from, mutt_fqdn(1));
1974 rfc2047_encode_adrlist(from, "Resent-From");
1975 if (mutt_addrlist_to_idna (from, &err)) {
1976 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
1979 rfc822_addrcat(resent_from, sizeof(resent_from), from, 0);
1980 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
1981 address_list_wipe(&from);
1985 static void set_noconv_flags (BODY * b, short flag)
1987 for (; b; b = b->next) {
1988 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1989 set_noconv_flags (b->parts, flag);
1990 else if (b->type == TYPETEXT && b->noconv) {
1991 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
1996 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
1997 int post, char *fcc)
2001 char tempfile[_POSIX_PATH_MAX];
2002 FILE *tempfp = NULL;
2006 set_noconv_flags (hdr->content, 1);
2008 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2012 /* We need to add a Content-Length field to avoid problems where a line in
2013 * the message body begins with "From "
2015 if (f.magic == M_MBOX) {
2016 tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2018 mutt_error(_("Could not create temporary file"));
2019 mx_close_mailbox (&f, NULL);
2024 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2025 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2026 mx_close_mailbox (&f, NULL);
2030 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2031 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2033 mutt_write_rfc822_header(msg->fp, hdr->env, hdr->content, -post);
2035 /* (postponment) if this was a reply of some sort, <msgid> contians the
2036 * Message-ID: of message replied to. Save it using a special X-Mutt-
2037 * header so it can be picked up if the message is recalled at a later
2038 * point in time. This will allow the message to be marked as replied if
2039 * the same mailbox is still open.
2042 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2044 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2045 * it can be picked up when the message is recalled
2048 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2049 fprintf (msg->fp, "Status: RO\n");
2051 /* (postponment) if the mail is to be signed or encrypted, save this info */
2052 if (post && (hdr->security & APPLICATION_PGP)) {
2053 fputs ("X-Mutt-PGP: ", msg->fp);
2054 if (hdr->security & ENCRYPT)
2055 fputc ('E', msg->fp);
2056 if (hdr->security & SIGN) {
2057 fputc ('S', msg->fp);
2058 if (PgpSignAs && *PgpSignAs)
2059 fprintf (msg->fp, "<%s>", PgpSignAs);
2061 if (hdr->security & INLINE)
2062 fputc ('I', msg->fp);
2063 fputc ('\n', msg->fp);
2066 /* (postponment) if the mail is to be signed or encrypted, save this info */
2067 if (post && (hdr->security & APPLICATION_SMIME)) {
2068 fputs ("X-Mutt-SMIME: ", msg->fp);
2069 if (hdr->security & ENCRYPT) {
2070 fputc ('E', msg->fp);
2071 if (SmimeCryptAlg && *SmimeCryptAlg)
2072 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2074 if (hdr->security & SIGN) {
2075 fputc ('S', msg->fp);
2076 if (SmimeDefaultKey && *SmimeDefaultKey)
2077 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2079 if (hdr->security & INLINE)
2080 fputc ('I', msg->fp);
2081 fputc ('\n', msg->fp);
2085 char sasha[LONG_STRING];
2088 mutt_write_mime_body (hdr->content, tempfp);
2090 /* make sure the last line ends with a newline. Emacs doesn't ensure
2091 * this will happen, and it can cause problems parsing the mailbox
2094 fseeko (tempfp, -1, 2);
2095 if (fgetc (tempfp) != '\n') {
2096 fseeko (tempfp, 0, 2);
2097 fputc ('\n', tempfp);
2101 if (ferror (tempfp)) {
2104 mx_commit_message (msg, &f); /* XXX - really? */
2105 mx_close_message (&msg);
2106 mx_close_mailbox (&f, NULL);
2110 /* count the number of lines */
2112 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2114 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2115 fprintf (msg->fp, "Lines: %d\n\n", lines);
2117 /* copy the body and clean up */
2119 r = mutt_copy_stream (tempfp, msg->fp);
2120 if (m_fclose(&tempfp) != 0)
2122 /* if there was an error, leave the temp version */
2126 fputc ('\n', msg->fp); /* finish off the header */
2127 r = mutt_write_mime_body (hdr->content, msg->fp);
2130 if (mx_commit_message (msg, &f) != 0)
2132 mx_close_message (&msg);
2133 mx_close_mailbox (&f, NULL);
2136 set_noconv_flags (hdr->content, 0);