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>
13 #include <sys/utsname.h>
15 #include <lib-lua/lib-lua.h>
16 #include <lib-sys/exit.h>
17 #include <lib-sys/mutt_signal.h>
18 #include <lib-mime/mime.h>
19 #include <lib-ui/curses.h>
20 #include <lib-mx/mx.h>
22 #include <lib-crypt/crypt.h>
26 #include "recvattach.h"
30 #include "mutt_idna.h"
33 # include "mutt_libesmtp.h"
34 #endif /* USE_LIBESMTP */
37 #include <nntp/nntp.h>
40 #ifdef HAVE_SYSEXITS_H
42 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
46 static void transform_to_7bit (BODY * a, FILE * fpin);
48 static void encode_quoted (fgetconv_t * fc, FILE * fout, int istext)
51 char line[77], savechar;
53 while ((c = fgetconv (fc)) != EOF) {
54 /* Wrap the line if needed. */
55 if (linelen == 76 && ((istext && c != '\n') || !istext)) {
56 /* If the last character is "quoted", then be sure to move all three
57 * characters to the next line. Otherwise, just move the last
60 if (line[linelen - 3] == '=') {
61 line[linelen - 3] = 0;
66 line[1] = line[linelen - 2];
67 line[2] = line[linelen - 1];
71 savechar = line[linelen - 1];
72 line[linelen - 1] = '=';
81 /* Escape lines that begin with/only contain "the message separator". */
82 if (linelen == 4 && !m_strncmp("From", line, 4)) {
83 m_strcpy(line, sizeof(line), "=46rom");
86 else if (linelen == 4 && !m_strncmp("from", line, 4)) {
87 m_strcpy(line, sizeof(line), "=66rom");
90 else if (linelen == 1 && line[0] == '.') {
91 m_strcpy(line, sizeof(line), "=2E");
96 if (c == '\n' && istext) {
97 /* Check to make sure there is no trailing space on this line. */
99 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
101 sprintf (line + linelen - 1, "=%2.2X",
102 (unsigned char) line[linelen - 1]);
106 savechar = line[linelen - 1];
108 line[linelen - 1] = '=';
111 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
121 else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
122 /* Check to make sure there is enough room for the quoted character.
123 * If not, wrap to the next line.
126 line[linelen++] = '=';
132 sprintf (line + linelen, "=%2.2X", (unsigned char) c);
136 /* Don't worry about wrapping the line here. That will happen during
137 * the next iteration when I'll also know what the next character is.
143 /* Take care of anything left in the buffer */
145 if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
146 /* take care of trailing whitespace */
148 sprintf (line + linelen - 1, "=%2.2X",
149 (unsigned char) line[linelen - 1]);
151 savechar = line[linelen - 1];
152 line[linelen - 1] = '=';
156 sprintf (line, "=%2.2X", (unsigned char) savechar);
165 static char b64_buffer[3];
166 static short b64_num;
167 static short b64_linelen;
169 static void b64_flush (FILE * fout)
176 if (b64_linelen >= 72) {
181 for (i = b64_num; i < 3; i++)
182 b64_buffer[i] = '\0';
184 fputc(__m_b64chars[(b64_buffer[0] >> 2) & 0x3f], fout);
187 [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
192 [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
196 fputc (__m_b64chars[b64_buffer[2] & 0x3f], fout);
201 while (b64_linelen % 4) {
210 static void b64_putc (char c, FILE * fout)
215 b64_buffer[b64_num++] = c;
219 static void encode_base64 (fgetconv_t * fc, FILE * fout, int istext)
223 b64_num = b64_linelen = 0;
225 while ((ch = fgetconv (fc)) != EOF) {
226 if (istext && ch == '\n' && ch1 != '\r')
227 b64_putc ('\r', fout);
235 static void encode_8bit (fgetconv_t * fc, FILE * fout,
236 int istext __attribute__ ((unused)))
240 while ((ch = fgetconv (fc)) != EOF)
245 int mutt_write_mime_header (BODY * a, FILE * f)
254 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
259 len = 25 + m_strlen(a->subtype); /* approximate len. of content-type */
261 for (p = a->parameter; p; p = p->next) {
270 tmp = m_strdup(p->value);
271 encode = rfc2231_encode_string (&tmp);
272 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
274 /* Dirty hack to make messages readable by Outlook Express
275 * for the Mac: force quotes around the boundary parameter
276 * even when they aren't needed.
279 if (!ascii_strcasecmp (p->attribute, "boundary")
280 && !strcmp (buffer, tmp))
281 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
285 tmplen = m_strlen(buffer) + m_strlen(p->attribute) + 1;
287 if (len + tmplen + 2 > 76) {
296 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
304 fprintf (f, "Content-Description: %s\n", a->description);
306 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
307 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
310 if (!(fn = a->d_filename))
316 /* Strip off the leading path... */
317 if ((t = strrchr (fn, '/')))
324 encode = rfc2231_encode_string (&tmp);
325 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
327 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
333 if (a->encoding != ENC7BIT)
334 fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
336 /* Do NOT add the terminator here!!! */
337 return (ferror (f) ? -1 : 0);
340 int mutt_write_mime_body (BODY * a, FILE * f)
343 char boundary[STRING];
344 char send_charset[STRING];
349 if (a->type == TYPEMULTIPART) {
350 /* First, find the boundary to use */
351 if (!(p = parameter_getval(a->parameter, "boundary"))) {
352 mutt_error _("No boundary parameter found! [report this error]");
356 m_strcpy(boundary, sizeof(boundary), p);
358 for (t = a->parts; t; t = t->next) {
359 fprintf (f, "\n--%s\n", boundary);
360 if (mutt_write_mime_header (t, f) == -1)
363 if (mutt_write_mime_body (t, f) == -1)
366 fprintf (f, "\n--%s--\n", boundary);
367 return (ferror (f) ? -1 : 0);
370 /* This is pretty gross, but it's the best solution for now... */
371 if (a->type == TYPEAPPLICATION && !m_strcmp(a->subtype, "pgp-encrypted")) {
372 fputs ("Version: 1\n", f);
376 if ((fpin = fopen (a->filename, "r")) == NULL) {
377 mutt_error (_("%s no longer exists!"), a->filename);
381 if (a->type == TYPETEXT && (!a->noconv))
382 fc = fgetconv_open (fpin, a->file_charset,
383 mutt_get_body_charset (send_charset,
384 sizeof (send_charset), a), 0);
386 fc = fgetconv_open (fpin, 0, 0, 0);
388 #define write_as_text_part(a) (mutt_is_text_part(a) || mutt_is_application_pgp(a))
389 if (a->encoding == ENCQUOTEDPRINTABLE)
390 encode_quoted (fc, f, write_as_text_part (a));
391 else if (a->encoding == ENCBASE64)
392 encode_base64 (fc, f, write_as_text_part (a));
393 else if (a->type == TYPETEXT && (!a->noconv))
394 encode_8bit (fc, f, write_as_text_part (a));
396 mutt_copy_stream (fpin, f);
397 #undef write_as_text_part
399 fgetconv_close (&fc);
402 return (ferror (f) ? -1 : 0);
414 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
418 int whitespace = s->whitespace;
420 int linelen = s->linelen;
421 int was_cr = s->was_cr;
423 if (!d) { /* This signals EOF */
426 if (linelen > info->linemax)
427 info->linemax = linelen;
432 for (; dlen; d++, dlen--) {
445 if (linelen > info->linemax)
446 info->linemax = linelen;
461 if (linelen > info->linemax)
462 info->linemax = linelen;
467 else if (ch == '\r') {
475 else if (ch == '\t' || ch == '\f') {
479 else if (ch < 32 || ch == 127)
483 if ((ch == 'F') || (ch == 'f'))
493 if (linelen == 2 && ch != 'r')
495 else if (linelen == 3 && ch != 'o')
497 else if (linelen == 4) {
510 if (ch != ' ' && ch != '\t')
515 s->whitespace = whitespace;
517 s->linelen = linelen;
523 * Find the best charset conversion of the file from fromcode into one
524 * of the tocodes. If successful, set *tocode and CONTENT *info and
525 * return the number of characters converted inexactly. If no
526 * conversion was possible, return -1.
528 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
529 * which would otherwise prevent us from knowing the number of inexact
530 * conversions. Where the candidate target charset is UTF-8 we avoid
531 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
532 * fails with some libraries.
534 * We assume that the output from iconv is never more than 4 times as
535 * long as the input for any pair of charsets we might be interested
538 static ssize_t convert_file_to (FILE * file, const char *fromcode,
539 int ncodes, const char **tocodes,
540 int *tocode, CONTENT * info)
544 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
547 ssize_t ibl, obl, ubl, ubl1, n, ret;
550 CONTENT_STATE *states;
553 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
554 if (cd1 == MUTT_ICONV_ERROR)
557 cd = p_new(iconv_t, ncodes);
558 score = p_new(ssize_t, ncodes);
559 states = p_new(CONTENT_STATE, ncodes);
560 infos = p_new(CONTENT, ncodes);
562 for (i = 0; i < ncodes; i++)
563 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
564 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
566 /* Special case for conversion to UTF-8 */
567 cd[i] = MUTT_ICONV_ERROR, score[i] = -1;
573 /* Try to fill input buffer */
574 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
577 /* Convert to UTF-8 */
579 ob = bufu, obl = sizeof (bufu);
580 n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
581 if (n == -1 && ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
587 /* Convert from UTF-8 */
588 for (i = 0; i < ncodes; i++)
589 if (cd[i] != MUTT_ICONV_ERROR && score[i] != -1) {
590 ub = bufu, ubl = ubl1;
591 ob = bufo, obl = sizeof (bufo);
592 n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
598 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
601 else if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1)
602 /* Special case for conversion to UTF-8 */
603 update_content_info (&infos[i], &states[i], bufu, ubl1);
606 /* Save unused input */
607 memmove (bufi, ib, ibl);
608 else if (!ubl1 && ib < bufi + sizeof (bufi)) {
615 /* Find best score */
617 for (i = 0; i < ncodes; i++) {
618 if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1) {
619 /* Special case for conversion to UTF-8 */
624 else if (cd[i] == MUTT_ICONV_ERROR || score[i] == -1)
626 else if (ret == -1 || score[i] < ret) {
634 memcpy (info, &infos[*tocode], sizeof (CONTENT));
635 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
639 for (i = 0; i < ncodes; i++)
640 if (cd[i] != MUTT_ICONV_ERROR)
652 #endif /* !HAVE_ICONV */
656 * Find the first of the fromcodes that gives a valid conversion and
657 * the best charset conversion of the file into one of the tocodes. If
658 * successful, set *fromcode and *tocode to dynamically allocated
659 * strings, set CONTENT *info, and return the number of characters
660 * converted inexactly. If no conversion was possible, return -1.
662 * Both fromcodes and tocodes may be colon-separated lists of charsets.
663 * However, if fromcode is zero then fromcodes is assumed to be the
664 * name of a single charset even if it contains a colon.
666 static ssize_t convert_file_from_to (FILE * file,
667 const char *fromcodes,
668 const char *tocodes, char **fromcode,
669 char **tocode, CONTENT * info)
677 /* Count the tocodes */
679 for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
680 if ((c1 = strchr (c, ':')) == c)
686 tcode = p_new(char *, ncodes);
687 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
688 if ((c1 = strchr (c, ':')) == c)
690 tcode[i] = m_substrdup(c, c1);
695 /* Try each fromcode in turn */
696 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
697 if ((c1 = strchr (c, ':')) == c)
699 fcode = m_substrdup(c, c1);
701 ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
713 /* There is only one fromcode */
714 ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
723 for (i = 0; i < ncodes; i++)
732 * Analyze the contents of a file to determine which MIME encoding to use.
733 * Also set the body charset, sometimes, or not.
735 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
740 char *fromcode = NULL;
751 if (stat (fname, &sb) == -1) {
752 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
756 if (!S_ISREG (sb.st_mode)) {
757 mutt_error (_("%s isn't a regular file."), fname);
761 if ((fp = fopen (fname, "r")) == NULL) {
765 info = p_new(CONTENT, 1);
768 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
769 const char *chs = parameter_getval(b->parameter, "charset");
770 char *fchs = b->use_disp ? ((FileCharset && *FileCharset) ?
771 FileCharset : Charset) : Charset;
772 if (Charset && (chs || SendCharset) &&
773 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
774 &fromcode, &tocode, info) != -1) {
776 charset_canonicalize (chsbuf, sizeof (chsbuf), tocode);
777 parameter_setval(&b->parameter, "charset", chsbuf);
779 b->file_charset = fromcode;
787 while ((r = fread (buffer, 1, sizeof (buffer), fp)))
788 update_content_info (info, &state, buffer, r);
789 update_content_info (info, &state, 0, 0);
793 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
794 parameter_setval(&b->parameter, "charset",
795 (!info->hibin ? "us-ascii"
796 : Charset && !charset_is_us_ascii(Charset) ? Charset : "unknown-8bit"));
801 /* Given a file with path ``s'', see if there is a registered MIME type.
802 * returns the major MIME type, and copies the subtype to ``d''. First look
803 * for ~/.mime.types, then look in a system mime.types if we can find one.
804 * The longest match is used so that we can match `ps.gz' when `gz' also
808 int mutt_lookup_mime_type (BODY * att, const char *path)
812 char buf[LONG_STRING];
813 char subtype[STRING], xtype[STRING];
815 int szf, sze, cur_sze;
823 szf = m_strlen(path);
825 for (count = 0; count < 4; count++) {
827 * can't use strtok() because we use it in an inner loop below, so use
828 * a switch statement here instead.
832 snprintf(buf, sizeof (buf), "%s/.mime.types", NONULL(MCore.homedir));
835 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
838 m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
841 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
844 goto bye; /* shouldn't happen */
847 if ((f = fopen (buf, "r")) != NULL) {
848 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
849 /* weed out any comments */
850 if ((p = strchr (buf, '#')))
853 /* remove any leading space. */
854 ct = vskipspaces(buf);
856 /* position on the next field in this line */
857 if ((p = strpbrk (ct, " \t")) == NULL)
862 /* cycle through the file extensions */
863 while ((p = strtok (p, " \t\n"))) {
865 if ((sze > cur_sze) && (szf >= sze) &&
866 (m_strcasecmp(path + szf - sze, p) == 0
867 || ascii_strcasecmp (path + szf - sze, p) == 0)
868 && (szf == sze || path[szf - sze - 1] == '.'))
870 /* get the content-type */
872 if ((p = strchr (ct, '/')) == NULL) {
873 /* malformed line, just skip it. */
878 for (q = p; *q && !ISSPACE (*q); q++);
880 m_strncpy(subtype, sizeof(subtype), p, q - p);
882 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
883 m_strcpy(xtype, sizeof(xtype), ct);
896 if (type != TYPEOTHER || *xtype != '\0') {
898 m_strreplace(&att->subtype, subtype);
899 m_strreplace(&att->xtype, xtype);
905 void mutt_message_to_7bit (BODY * a, FILE * fp)
907 char temp[_POSIX_PATH_MAX];
913 if (!a->filename && fp)
915 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
916 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
921 if (stat (a->filename, &sb) == -1) {
922 mutt_perror ("stat");
925 a->length = sb.st_size;
928 fpout = m_tempfile(temp, sizeof(temp), NONULL(MCore.tmpdir), NULL);
930 mutt_error(_("Could not create temporary file"));
934 fseeko (fpin, a->offset, 0);
935 a->parts = mutt_parse_messageRFC822 (fpin, a);
937 transform_to_7bit (a->parts, fpin);
939 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
940 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
942 fputs ("MIME-Version: 1.0\n", fpout);
943 mutt_write_mime_header (a->parts, fpout);
945 mutt_write_mime_body (a->parts, fpout);
957 a->encoding = ENC7BIT;
958 a->d_filename = a->filename;
959 if (a->filename && a->unlink)
960 unlink (a->filename);
961 a->filename = m_strdup(temp);
963 if (stat (a->filename, &sb) == -1) {
964 mutt_perror ("stat");
967 a->length = sb.st_size;
968 body_list_wipe(&a->parts);
969 a->hdr->content = NULL;
972 static void transform_to_7bit (BODY * a, FILE * fpin)
974 char buff[_POSIX_PATH_MAX];
979 for (; a; a = a->next) {
980 if (a->type == TYPEMULTIPART) {
981 if (a->encoding != ENC7BIT)
982 a->encoding = ENC7BIT;
984 transform_to_7bit (a->parts, fpin);
986 else if (mutt_is_message_type(a)) {
987 mutt_message_to_7bit (a, fpin);
991 a->force_charset = 1;
993 s.fpout = m_tempfile(buff, sizeof(buff), NONULL(MCore.tmpdir), NULL);
995 mutt_error(_("Could not create temporary file"));
999 mutt_decode_attachment (a, &s);
1001 a->d_filename = a->filename;
1002 a->filename = m_strdup(buff);
1004 if (stat (a->filename, &sb) == -1) {
1005 mutt_perror ("stat");
1008 a->length = sb.st_size;
1010 mutt_update_encoding (a);
1011 if (a->encoding == ENC8BIT)
1012 a->encoding = ENCQUOTEDPRINTABLE;
1013 else if (a->encoding == ENCBINARY)
1014 a->encoding = ENCBASE64;
1019 /* determine which Content-Transfer-Encoding to use */
1020 static void mutt_set_encoding (BODY * b, CONTENT * info)
1022 char send_charset[STRING];
1024 if (b->type == TYPETEXT) {
1026 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1027 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1028 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1029 b->encoding = ENCQUOTEDPRINTABLE;
1030 else if (info->hibin)
1031 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1033 b->encoding = ENC7BIT;
1035 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1036 if (info->lobin || info->hibin) {
1037 if (option (OPTALLOW8BIT) && !info->lobin)
1038 b->encoding = ENC8BIT;
1040 mutt_message_to_7bit (b, NULL);
1043 b->encoding = ENC7BIT;
1045 else if (b->type == TYPEAPPLICATION
1046 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1047 b->encoding = ENC7BIT;
1050 /* Determine which encoding is smaller */
1051 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1052 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1053 b->encoding = ENCBASE64;
1055 b->encoding = ENCQUOTEDPRINTABLE;
1059 void mutt_stamp_attachment (BODY * a)
1061 a->stamp = time (NULL);
1064 /* Get a body's character set */
1066 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1070 if (b && b->type != TYPETEXT)
1073 p = b ? parameter_getval(b->parameter, "charset") : NULL;
1074 charset_canonicalize(d, dlen, p);
1079 /* Assumes called from send mode where BODY->filename points to actual file */
1080 void mutt_update_encoding (BODY * a)
1083 char chsbuff[STRING];
1085 /* override noconv when it's us-ascii */
1086 if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1089 if (!a->force_charset && !a->noconv)
1090 parameter_delval(&a->parameter, "charset");
1092 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1095 mutt_set_encoding (a, info);
1096 mutt_stamp_attachment (a);
1098 p_delete(&a->content);
1103 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1105 char buffer[LONG_STRING];
1108 int cmflags, chflags;
1109 int pgp = hdr->security;
1111 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1112 (hdr->security & ENCRYPT)) {
1113 if (!crypt_valid_passphrase (hdr->security))
1117 fp = m_tempfile(buffer, sizeof(buffer), NONULL(MCore.tmpdir), NULL);
1122 body->type = TYPEMESSAGE;
1123 body->subtype = m_strdup("rfc822");
1124 body->filename = m_strdup(buffer);
1127 body->disposition = DISPINLINE;
1130 mutt_parse_mime_message (ctx, hdr);
1135 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1136 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1137 chflags |= CH_MIME | CH_TXTPLAIN;
1138 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1139 pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1141 else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1142 if (mutt_is_multipart_encrypted (hdr->content)) {
1143 chflags |= CH_MIME | CH_NONEWLINE;
1144 cmflags = M_CM_DECODE_PGP;
1147 else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1148 chflags |= CH_MIME | CH_TXTPLAIN;
1149 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1152 else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1153 chflags |= CH_MIME | CH_TXTPLAIN;
1154 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1155 pgp &= ~SMIMEENCRYPT;
1159 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1164 body->hdr = header_new();
1165 body->hdr->offset = 0;
1166 /* we don't need the user headers here */
1167 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1168 body->hdr->security = pgp;
1169 mutt_update_encoding (body);
1170 body->parts = body->hdr->content;
1177 BODY *mutt_make_file_attach (const char *path)
1183 att->filename = m_strdup(path);
1185 /* Attempt to determine the appropriate content-type based on the filename
1188 mutt_lookup_mime_type (att, path);
1190 if ((info = mutt_get_content_info (path, att)) == NULL) {
1191 body_list_wipe(&att);
1195 if (!att->subtype) {
1196 if (info->lobin == 0
1197 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1199 * Statistically speaking, there should be more than 10% "lobin"
1200 * chars if this is really a binary file...
1202 att->type = TYPETEXT;
1203 att->subtype = m_strdup("plain");
1205 att->type = TYPEAPPLICATION;
1206 att->subtype = m_strdup("octet-stream");
1210 mutt_update_encoding (att);
1214 static int get_toplevel_encoding (BODY * a)
1218 for (; a; a = a->next) {
1219 if (a->encoding == ENCBINARY)
1222 if (a->encoding == ENC8BIT)
1229 BODY *mutt_make_multipart (BODY * b)
1234 new->type = TYPEMULTIPART;
1235 new->subtype = m_strdup("mixed");
1236 new->encoding = get_toplevel_encoding (b);
1237 parameter_set_boundary(&new->parameter);
1239 new->disposition = DISPINLINE;
1245 /* remove the multipart body if it exists */
1246 BODY *mutt_remove_multipart (BODY * b)
1259 char *mutt_make_date (char *s, ssize_t len)
1261 time_t t = time (NULL);
1262 struct tm *l = localtime (&t);
1263 time_t tz = mutt_local_tz (t);
1267 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1268 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1269 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1270 (int) tz / 60, (int) abs (tz) % 60);
1274 /* wrapper around mutt_write_address() so we can handle very large
1275 recipient lists without needing a huge temporary buffer in memory */
1277 mutt_write_address_list(address_t *addr, FILE *fp, int linelen, int display)
1282 char buf[LONG_STRING];
1283 int len = rfc822_addrcpy(buf, ssizeof(buf), addr, display);
1286 if (linelen + len > 74) {
1288 linelen = 8; /* tab is usually about 8 spaces... */
1290 if (addr->mailbox) {
1300 if (!addr->group && addr->next && addr->next->mailbox) {
1310 /* need to write the list in reverse because they are stored in reverse order
1311 * when parsed to speed up threading
1313 void mutt_write_references(string_list_t *r, FILE *f)
1315 string_list_t *refs[10];
1318 p_clear(refs, countof(refs));
1319 for (i = 0; i < countof(refs) && r; r = r->next) {
1324 fprintf(f, " %s", refs[i]->data);
1328 static int edit_header(int mode, const char *s)
1331 int slen = m_strlen(s);
1333 if (mode != 1 || option(OPTXMAILTO))
1336 p = skipspaces(EditorHeaders);
1338 if (!ascii_strncasecmp(p, s, slen) && p[slen - 1] == ':')
1340 p = skipspaces(p + slen);
1346 /* Note: all RFC2047 encoding should be done outside of this routine, except
1347 * for the "real name." This will allow this routine to be used more than
1348 * once, if necessary.
1350 * Likewise, all IDN processing should happen outside of this routine.
1352 * mode == 1 => "lite" mode (used for edit_hdrs)
1353 * mode == 0 => normal mode. write full header + MIME headers
1354 * mode == -1 => write just the envelope info (used for postponing messages)
1356 * privacy != 0 => will omit any headers which may identify the user.
1357 * Output generated is suitable for being sent through
1358 * anonymous remailer chains.
1361 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1362 int mode, int privacy)
1364 char buffer[LONG_STRING];
1366 string_list_t *tmp = env->userhdrs;
1367 int has_agent = 0; /* user defined user-agent header field exists */
1370 if (!option (OPTNEWSSEND))
1372 if (mode == 0 && !privacy)
1373 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1375 /* OPTUSEFROM is not consulted here so that we can still write a From:
1376 * field if the user sets it with the `my_hdr' command
1378 if (env->from && !privacy) {
1380 rfc822_addrcat(buffer, sizeof(buffer), env->from, 0);
1381 fprintf (fp, "From: %s\n", buffer);
1386 mutt_write_address_list (env->to, fp, 4, 0);
1390 if (!option (OPTNEWSSEND))
1392 if (edit_header(mode, "To:"))
1393 fputs ("To:\n", fp);
1397 mutt_write_address_list (env->cc, fp, 4, 0);
1401 if (!option (OPTNEWSSEND))
1403 if (edit_header(mode, "Cc:"))
1404 fputs ("Cc:\n", fp);
1407 if (mode != 0 || option (OPTWRITEBCC)) {
1408 fputs ("Bcc: ", fp);
1409 mutt_write_address_list (env->bcc, fp, 5, 0);
1414 if (!option (OPTNEWSSEND))
1416 if (edit_header(mode, "Bcc:"))
1417 fputs ("Bcc:\n", fp);
1420 if (env->newsgroups)
1421 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1422 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Newsgroups:"))
1423 fputs ("Newsgroups:\n", fp);
1425 if (env->followup_to)
1426 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1427 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Followup-To:"))
1428 fputs ("Followup-To:\n", fp);
1430 if (env->x_comment_to)
1431 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1432 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO) &&
1433 edit_header(mode, "X-Comment-To:"))
1434 fputs ("X-Comment-To:\n", fp);
1438 fprintf (fp, "Subject: %s\n", env->subject);
1439 else if (mode == 1 && edit_header(mode, "Subject:"))
1440 fputs ("Subject:\n", fp);
1442 /* save message id if the user has set it */
1443 if (env->message_id && !privacy)
1444 fprintf (fp, "Message-ID: %s\n", env->message_id);
1446 if (env->reply_to) {
1447 fputs ("Reply-To: ", fp);
1448 mutt_write_address_list (env->reply_to, fp, 10, 0);
1450 else if (mode > 0 && edit_header(mode, "Reply-To:"))
1451 fputs ("Reply-To:\n", fp);
1453 if (env->mail_followup_to)
1455 if (!option (OPTNEWSSEND))
1458 fputs ("Mail-Followup-To: ", fp);
1459 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1463 if (env->references) {
1464 fputs ("References:", fp);
1465 mutt_write_references (env->references, fp);
1469 /* Add the MIME headers */
1470 fputs ("MIME-Version: 1.0\n", fp);
1471 mutt_write_mime_header (attach, fp);
1474 if (env->in_reply_to) {
1475 fputs ("In-Reply-To:", fp);
1476 mutt_write_references (env->in_reply_to, fp);
1480 /* Add any user defined headers */
1481 for (; tmp; tmp = tmp->next) {
1482 if ((p = strchr (tmp->data, ':'))) {
1483 p = vskipspaces(p + 1);
1485 continue; /* don't emit empty fields. */
1487 /* check to see if the user has overridden the user-agent field */
1488 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1494 fputs (tmp->data, fp);
1499 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1502 if (OperatingSystem != NULL) {
1503 os = OperatingSystem;
1506 os = (uname(&un) == -1) ? "UNIX" : un.sysname;
1508 /* Add a vanity header */
1509 fprintf (fp, "User-Agent: %s (%s)\n", mutt_make_version(), os);
1512 return (ferror (fp) == 0 ? 0 : -1);
1515 static void encode_headers (string_list_t * h)
1521 for (; h; h = h->next) {
1522 if (!(p = strchr (h->data, ':')))
1526 p = vskipspaces(p + 1);
1532 rfc2047_encode_string (&tmp);
1533 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1535 sprintf (h->data + i, ": %s", NONULL (tmp));
1541 const char *mutt_fqdn (short may_hide_host)
1545 if (Fqdn && Fqdn[0] != '@') {
1548 if (may_hide_host && option (OPTHIDDENHOST)) {
1549 if ((p = strchr (Fqdn, '.')))
1552 /* sanity check: don't hide the host if
1553 the fqdn is something like detebe.org. */
1555 if (!p || !(q = strchr (p, '.')))
1563 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1565 #define APPEND_FMT(fmt, arg) \
1567 int snlen = snprintf(buf, len, fmt, arg); \
1572 #define APPEND_BYTE(c) \
1586 static char MsgIdPfx = 'A';
1590 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1591 APPEND_BYTE((isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.');
1599 APPEND_FMT("%02d", tm->tm_mday);
1602 APPEND_FMT("%02d", tm->tm_hour);
1605 APPEND_FMT("%02d", tm->tm_mon + 1);
1608 APPEND_FMT("%02d", tm->tm_min);
1611 APPEND_FMT("%lo", (unsigned long)now);
1614 APPEND_FMT("%u", (unsigned int)getpid());
1617 APPEND_FMT("%c", MsgIdPfx);
1618 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1621 APPEND_FMT("%u", (unsigned int)rand());
1624 APPEND_FMT("%x", (unsigned int)rand());
1627 APPEND_FMT("%02d", tm->tm_sec);
1630 APPEND_FMT("%u", (unsigned int) now);
1633 APPEND_FMT("%x", (unsigned int) now);
1635 case 'Y': /* this will break in the year 10000 ;-) */
1636 APPEND_FMT("%04d", tm->tm_year + 1900);
1641 default: /* invalid formats are replaced by '.' */
1652 static char *mutt_gen_msgid (void)
1655 char localpart[STRING];
1658 if (!(fqdn = mutt_fqdn(0)))
1659 fqdn = NONULL(Hostname);
1661 mutt_gen_localpart(localpart, sizeof(localpart), MsgIdFormat);
1662 snprintf(buf, sizeof(buf), "<%s@%s>", localpart, fqdn);
1663 return m_strdup(buf);
1666 static RETSIGTYPE alarm_handler (int sig __attribute__ ((unused)))
1671 /* invoke sendmail in a subshell
1672 path (in) path to program to execute
1673 args (in) arguments to pass to program
1674 msg (in) temp file containing message to send
1675 tempfile (out) if sendmail is put in the background, this points
1676 to the temporary file containing the stdout of the
1679 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1685 mutt_block_signals_system ();
1688 /* we also don't want to be stopped right now */
1689 sigaddset (&set, SIGTSTP);
1690 sigprocmask (SIG_BLOCK, &set, NULL);
1692 if (SendmailWait >= 0) {
1693 char tmp[_POSIX_PATH_MAX];
1696 *tempfile = m_strdup(tmp);
1699 if ((pid = fork ()) == 0) {
1700 struct sigaction act, oldalrm;
1702 /* save parent's ID before setsid() */
1705 /* we want the delivery to continue even after the main process dies,
1706 * so we put ourselves into another session right away
1710 /* next we close all open files */
1711 for (fd = 0; fd < getdtablesize(); fd++)
1714 /* now the second fork() */
1715 if ((pid = fork ()) == 0) {
1716 /* "msg" will be opened as stdin */
1717 if (open (msg, O_RDONLY, 0) < 0) {
1723 if (SendmailWait >= 0) {
1724 /* *tempfile will be opened as stdout */
1725 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1728 /* redirect stderr to *tempfile too */
1732 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1734 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1738 execv (path, (char**)args);
1741 else if (pid == -1) {
1747 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1748 * SendmailWait = 0: wait forever
1749 * SendmailWait < 0: don't wait
1751 if (SendmailWait > 0) {
1753 act.sa_handler = alarm_handler;
1755 /* need to make sure waitpid() is interrupted on SIGALRM */
1756 act.sa_flags = SA_INTERRUPT;
1760 sigemptyset (&act.sa_mask);
1761 sigaction (SIGALRM, &act, &oldalrm);
1762 alarm (SendmailWait);
1764 else if (SendmailWait < 0)
1765 _exit (0xff & EX_OK);
1767 if (waitpid (pid, &st, 0) > 0) {
1768 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1769 if (SendmailWait && st == (0xff & EX_OK)) {
1770 unlink (*tempfile); /* no longer needed */
1774 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1775 if (SendmailWait > 0) {
1781 /* reset alarm; not really needed, but... */
1783 sigaction (SIGALRM, &oldalrm, NULL);
1785 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1786 /* the parent is already dead */
1794 sigprocmask (SIG_UNBLOCK, &set, NULL);
1796 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1797 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1799 st = S_ERR; /* error */
1801 mutt_unblock_signals_system (1);
1806 static const char **
1807 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1809 for (; addr; addr = addr->next) {
1810 /* weed out group mailboxes, since those are for display only */
1811 if (addr->mailbox && !addr->group) {
1812 if (*argslen == *argsmax)
1813 p_realloc(&args, *argsmax += 5);
1814 args[(*argslen)++] = addr->mailbox;
1820 static const char **
1821 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1823 if (*argslen == *argsmax) {
1824 p_realloc(&args, *argsmax += 5);
1826 args[(*argslen)++] = s;
1830 static int mutt_invoke_sendmail (address_t * from, /* the sender */
1831 address_t * to, address_t * cc, address_t * bcc, /* recips */
1832 const char *msg, /* file containing message */
1834 { /* message contains 8bit chars */
1835 char cmd[LONG_STRING];
1836 char *ps = NULL, *path = NULL, *childout = NULL;
1837 const char **args = NULL;
1838 ssize_t argslen = 0, argsmax = 0;
1842 if (option (OPTNEWSSEND)) {
1843 m_strformat(cmd, sizeof(cmd), 0, Inews, nntp_format_str, 0, 0);
1844 if (m_strisempty(cmd)) {
1845 i = nntp_post (msg);
1852 m_strcpy(cmd, sizeof(cmd), MTransport.sendmail);
1857 while ((ps = strtok(ps, " "))) {
1858 if (argslen == argsmax)
1859 p_realloc(&args, argsmax += 5);
1862 args[argslen++] = ps;
1864 path = m_strdup(ps);
1865 ps = strrchr (ps, '/');
1870 args[argslen++] = ps;
1877 if (!option (OPTNEWSSEND)) {
1879 if (eightbit && option (OPTUSE8BITMIME))
1880 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1882 if (option (OPTENVFROM)) {
1883 address_t *f = EnvFrom;
1886 else if (from && !from->next)
1889 args = add_option (args, &argslen, &argsmax, "-f");
1890 args = add_args (args, &argslen, &argsmax, f);
1893 if (MTransport.dsn_notify) {
1894 args = add_option (args, &argslen, &argsmax, "-N");
1895 args = add_option (args, &argslen, &argsmax, MTransport.dsn_notify);
1897 if (MTransport.dsn_return) {
1898 args = add_option (args, &argslen, &argsmax, "-R");
1899 args = add_option (args, &argslen, &argsmax, MTransport.dsn_return);
1901 args = add_option (args, &argslen, &argsmax, "--");
1902 args = add_args (args, &argslen, &argsmax, to);
1903 args = add_args (args, &argslen, &argsmax, cc);
1904 args = add_args (args, &argslen, &argsmax, bcc);
1909 if (argslen >= argsmax)
1910 p_realloc(&args, ++argsmax);
1912 args[argslen++] = NULL;
1914 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1916 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1921 if (!stat(childout, &st) && st.st_size > 0)
1922 mutt_do_pager(_("Output of the delivery process"), childout, 0,
1930 p_delete(&childout);
1934 if (i == (EX_OK & 0xff))
1936 else if (i == S_BKG)
1943 int mutt_invoke_mta (address_t * from, /* the sender */
1944 address_t * to, address_t * cc, address_t * bcc, /* recips */
1945 const char *msg, /* file containing message */
1947 { /* message contains 8bit chars */
1950 if (!option (OPTNEWSSEND))
1953 return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
1956 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
1959 /* For postponing (!final) do the necessary encodings only */
1960 void mutt_prepare_envelope (ENVELOPE * env, int final)
1963 if (env->bcc && !(env->to || env->cc)) {
1964 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1965 * recipients if there is no To: or Cc: field, so attempt to suppress
1966 * it by using an empty To: field.
1968 env->to = address_new();
1970 env->to->next = address_new();
1971 env->to->mailbox = m_strdup("undisclosed-recipients");
1974 mutt_set_followup_to(env);
1976 if (!env->message_id && !m_strisempty(MsgIdFormat))
1977 env->message_id = mutt_gen_msgid();
1980 /* Take care of 8-bit => 7-bit conversion. */
1981 rfc2047_encode_adrlist(env->to, "To");
1982 rfc2047_encode_adrlist(env->cc, "Cc");
1983 rfc2047_encode_adrlist(env->bcc, "Bcc");
1984 rfc2047_encode_adrlist(env->from, "From");
1985 rfc2047_encode_adrlist(env->mail_followup_to, "Mail-Followup-To");
1986 rfc2047_encode_adrlist(env->reply_to, "Reply-To");
1990 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
1993 rfc2047_encode_string (&env->subject);
1995 encode_headers (env->userhdrs);
1998 void mutt_unprepare_envelope (ENVELOPE * env)
2000 string_list_t *item;
2002 for (item = env->userhdrs; item; item = item->next)
2003 rfc2047_decode(&item->data);
2005 address_list_wipe(&env->mail_followup_to);
2007 /* back conversions */
2008 rfc2047_decode_adrlist(env->to);
2009 rfc2047_decode_adrlist(env->cc);
2010 rfc2047_decode_adrlist(env->bcc);
2011 rfc2047_decode_adrlist(env->from);
2012 rfc2047_decode_adrlist(env->reply_to);
2013 rfc2047_decode(&env->subject);
2016 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
2017 const char *resent_from, address_t * env_from)
2021 char date[STRING], tempfile[_POSIX_PATH_MAX];
2022 MESSAGE *msg = NULL;
2025 /* Try to bounce each message out, aborting if we get any failures. */
2026 for (i = 0; i < Context->msgcount; i++)
2027 if (Context->hdrs[i]->tagged)
2029 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2034 /* If we failed to open a message, return with error */
2035 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2041 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2043 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2045 if (!option (OPTBOUNCEDELIVERED))
2046 ch_flags |= CH_WEED_DELIVERED;
2048 fseeko (fp, h->offset, 0);
2049 fprintf (f, "Resent-From: %s", resent_from);
2050 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2051 if (!m_strisempty(MsgIdFormat))
2052 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2053 fputs ("Resent-To: ", f);
2054 mutt_write_address_list (to, f, 11, 0);
2055 mutt_copy_header (fp, h, f, ch_flags, NULL);
2057 mutt_copy_bytes (fp, f, h->content->length);
2060 ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
2061 h->content->encoding == ENC8BIT);
2065 mx_close_message (&msg);
2070 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2073 char resent_from[STRING];
2077 resent_from[0] = '\0';
2078 from = mutt_default_from ();
2080 rfc822_qualify(from, mutt_fqdn(1));
2082 rfc2047_encode_adrlist(from, "Resent-From");
2083 if (mutt_addrlist_to_idna (from, &err)) {
2084 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2087 rfc822_addrcat(resent_from, sizeof(resent_from), from, 0);
2090 unset_option (OPTNEWSSEND);
2093 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2095 address_list_wipe(&from);
2100 static void set_noconv_flags (BODY * b, short flag)
2102 for (; b; b = b->next) {
2103 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2104 set_noconv_flags (b->parts, flag);
2105 else if (b->type == TYPETEXT && b->noconv) {
2106 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2111 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2112 int post, char *fcc)
2116 char tempfile[_POSIX_PATH_MAX];
2117 FILE *tempfp = NULL;
2121 set_noconv_flags (hdr->content, 1);
2123 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2127 /* We need to add a Content-Length field to avoid problems where a line in
2128 * the message body begins with "From "
2130 if (f.magic == M_MMDF || f.magic == M_MBOX) {
2131 tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2133 mutt_error(_("Could not create temporary file"));
2134 mx_close_mailbox (&f, NULL);
2139 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2140 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2141 mx_close_mailbox (&f, NULL);
2145 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2146 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2148 mutt_write_rfc822_header(msg->fp, hdr->env, hdr->content, -post, 0);
2150 /* (postponment) if this was a reply of some sort, <msgid> contians the
2151 * Message-ID: of message replied to. Save it using a special X-Mutt-
2152 * header so it can be picked up if the message is recalled at a later
2153 * point in time. This will allow the message to be marked as replied if
2154 * the same mailbox is still open.
2157 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2159 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2160 * it can be picked up when the message is recalled
2163 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2164 fprintf (msg->fp, "Status: RO\n");
2166 /* (postponment) if the mail is to be signed or encrypted, save this info */
2167 if (post && (hdr->security & APPLICATION_PGP)) {
2168 fputs ("X-Mutt-PGP: ", msg->fp);
2169 if (hdr->security & ENCRYPT)
2170 fputc ('E', msg->fp);
2171 if (hdr->security & SIGN) {
2172 fputc ('S', msg->fp);
2173 if (PgpSignAs && *PgpSignAs)
2174 fprintf (msg->fp, "<%s>", PgpSignAs);
2176 if (hdr->security & INLINE)
2177 fputc ('I', msg->fp);
2178 fputc ('\n', msg->fp);
2181 /* (postponment) if the mail is to be signed or encrypted, save this info */
2182 if (post && (hdr->security & APPLICATION_SMIME)) {
2183 fputs ("X-Mutt-SMIME: ", msg->fp);
2184 if (hdr->security & ENCRYPT) {
2185 fputc ('E', msg->fp);
2186 if (SmimeCryptAlg && *SmimeCryptAlg)
2187 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2189 if (hdr->security & SIGN) {
2190 fputc ('S', msg->fp);
2191 if (SmimeDefaultKey && *SmimeDefaultKey)
2192 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2194 if (hdr->security & INLINE)
2195 fputc ('I', msg->fp);
2196 fputc ('\n', msg->fp);
2199 /* (postponement) if the mail is to be sent through a mixmaster
2200 * chain, save that information
2202 if (post && hdr->chain && hdr->chain) {
2205 fputs ("X-Mutt-Mix:", msg->fp);
2206 for (p = hdr->chain; p; p = p->next)
2207 fprintf (msg->fp, " %s", (char *) p->data);
2209 fputc ('\n', msg->fp);
2213 char sasha[LONG_STRING];
2216 mutt_write_mime_body (hdr->content, tempfp);
2218 /* make sure the last line ends with a newline. Emacs doesn't ensure
2219 * this will happen, and it can cause problems parsing the mailbox
2222 fseeko (tempfp, -1, 2);
2223 if (fgetc (tempfp) != '\n') {
2224 fseeko (tempfp, 0, 2);
2225 fputc ('\n', tempfp);
2229 if (ferror (tempfp)) {
2232 mx_commit_message (msg, &f); /* XXX - really? */
2233 mx_close_message (&msg);
2234 mx_close_mailbox (&f, NULL);
2238 /* count the number of lines */
2240 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2242 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2243 fprintf (msg->fp, "Lines: %d\n\n", lines);
2245 /* copy the body and clean up */
2247 r = mutt_copy_stream (tempfp, msg->fp);
2248 if (m_fclose(&tempfp) != 0)
2250 /* if there was an error, leave the temp version */
2254 fputc ('\n', msg->fp); /* finish off the header */
2255 r = mutt_write_mime_body (hdr->content, msg->fp);
2258 if (mx_commit_message (msg, &f) != 0)
2260 mx_close_message (&msg);
2261 mx_close_mailbox (&f, NULL);
2264 set_noconv_flags (hdr->content, 0);