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/curses.h>
19 #include <lib-mx/mx.h>
24 #include "recvattach.h"
28 #include "mutt_idna.h"
29 #include "send_smtp.h"
32 #include <nntp/nntp.h>
35 #ifdef HAVE_SYSEXITS_H
37 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
41 static void transform_to_7bit (BODY * a, FILE * fpin);
43 static void encode_quoted (fgetconv_t * fc, FILE * fout, int istext)
46 char line[77], savechar;
48 while ((c = fgetconv (fc)) != EOF) {
49 /* Wrap the line if needed. */
50 if (linelen == 76 && ((istext && c != '\n') || !istext)) {
51 /* If the last character is "quoted", then be sure to move all three
52 * characters to the next line. Otherwise, just move the last
55 if (line[linelen - 3] == '=') {
56 line[linelen - 3] = 0;
61 line[1] = line[linelen - 2];
62 line[2] = line[linelen - 1];
66 savechar = line[linelen - 1];
67 line[linelen - 1] = '=';
76 /* Escape lines that begin with/only contain "the message separator". */
77 if (linelen == 4 && !m_strncmp("From", line, 4)) {
78 m_strcpy(line, sizeof(line), "=46rom");
81 else if (linelen == 4 && !m_strncmp("from", line, 4)) {
82 m_strcpy(line, sizeof(line), "=66rom");
85 else if (linelen == 1 && line[0] == '.') {
86 m_strcpy(line, sizeof(line), "=2E");
91 if (c == '\n' && istext) {
92 /* Check to make sure there is no trailing space on this line. */
94 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
96 sprintf (line + linelen - 1, "=%2.2X",
97 (unsigned char) line[linelen - 1]);
101 savechar = line[linelen - 1];
103 line[linelen - 1] = '=';
106 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
116 else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
117 /* Check to make sure there is enough room for the quoted character.
118 * If not, wrap to the next line.
121 line[linelen++] = '=';
127 sprintf (line + linelen, "=%2.2X", (unsigned char) c);
131 /* Don't worry about wrapping the line here. That will happen during
132 * the next iteration when I'll also know what the next character is.
138 /* Take care of anything left in the buffer */
140 if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
141 /* take care of trailing whitespace */
143 sprintf (line + linelen - 1, "=%2.2X",
144 (unsigned char) line[linelen - 1]);
146 savechar = line[linelen - 1];
147 line[linelen - 1] = '=';
151 sprintf (line, "=%2.2X", (unsigned char) savechar);
160 static char b64_buffer[3];
161 static short b64_num;
162 static short b64_linelen;
164 static void b64_flush (FILE * fout)
171 if (b64_linelen >= 72) {
176 for (i = b64_num; i < 3; i++)
177 b64_buffer[i] = '\0';
179 fputc(__m_b64chars[(b64_buffer[0] >> 2) & 0x3f], fout);
182 [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
187 [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
191 fputc (__m_b64chars[b64_buffer[2] & 0x3f], fout);
196 while (b64_linelen % 4) {
205 static void b64_putc (char c, FILE * fout)
210 b64_buffer[b64_num++] = c;
214 static void encode_base64 (fgetconv_t * fc, FILE * fout, int istext)
218 b64_num = b64_linelen = 0;
220 while ((ch = fgetconv (fc)) != EOF) {
221 if (istext && ch == '\n' && ch1 != '\r')
222 b64_putc ('\r', fout);
230 static void encode_8bit (fgetconv_t * fc, FILE * fout,
231 int istext __attribute__ ((unused)))
235 while ((ch = fgetconv (fc)) != EOF)
240 int mutt_write_mime_header (BODY * a, FILE * f)
249 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
254 len = 25 + m_strlen(a->subtype); /* approximate len. of content-type */
256 for (p = a->parameter; p; p = p->next) {
265 tmp = m_strdup(p->value);
266 encode = rfc2231_encode_string (&tmp);
267 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
269 /* Dirty hack to make messages readable by Outlook Express
270 * for the Mac: force quotes around the boundary parameter
271 * even when they aren't needed.
274 if (!ascii_strcasecmp (p->attribute, "boundary")
275 && !strcmp (buffer, tmp))
276 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
280 tmplen = m_strlen(buffer) + m_strlen(p->attribute) + 1;
282 if (len + tmplen + 2 > 76) {
291 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
299 fprintf (f, "Content-Description: %s\n", a->description);
301 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
302 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
305 if (!(fn = a->d_filename))
311 /* Strip off the leading path... */
312 if ((t = strrchr (fn, '/')))
319 encode = rfc2231_encode_string (&tmp);
320 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
322 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
328 if (a->encoding != ENC7BIT)
329 fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
331 /* Do NOT add the terminator here!!! */
332 return (ferror (f) ? -1 : 0);
335 int mutt_write_mime_body (BODY * a, FILE * f)
338 char boundary[STRING];
339 char send_charset[STRING];
344 if (a->type == TYPEMULTIPART) {
345 /* First, find the boundary to use */
346 if (!(p = parameter_getval(a->parameter, "boundary"))) {
347 mutt_error _("No boundary parameter found! [report this error]");
351 m_strcpy(boundary, sizeof(boundary), p);
353 for (t = a->parts; t; t = t->next) {
354 fprintf (f, "\n--%s\n", boundary);
355 if (mutt_write_mime_header (t, f) == -1)
358 if (mutt_write_mime_body (t, f) == -1)
361 fprintf (f, "\n--%s--\n", boundary);
362 return (ferror (f) ? -1 : 0);
365 /* This is pretty gross, but it's the best solution for now... */
366 if (a->type == TYPEAPPLICATION && !m_strcmp(a->subtype, "pgp-encrypted")) {
367 fputs ("Version: 1\n", f);
371 if ((fpin = fopen (a->filename, "r")) == NULL) {
372 mutt_error (_("%s no longer exists!"), a->filename);
376 if (a->type == TYPETEXT && (!a->noconv))
377 fc = fgetconv_open (fpin, a->file_charset,
378 mutt_get_body_charset (send_charset,
379 sizeof (send_charset), a), 0);
381 fc = fgetconv_open (fpin, 0, 0, 0);
383 #define write_as_text_part(a) (mutt_is_text_part(a) || mutt_is_application_pgp(a))
384 if (a->encoding == ENCQUOTEDPRINTABLE)
385 encode_quoted (fc, f, write_as_text_part (a));
386 else if (a->encoding == ENCBASE64)
387 encode_base64 (fc, f, write_as_text_part (a));
388 else if (a->type == TYPETEXT && (!a->noconv))
389 encode_8bit (fc, f, write_as_text_part (a));
391 mutt_copy_stream (fpin, f);
392 #undef write_as_text_part
394 fgetconv_close (&fc);
397 return (ferror (f) ? -1 : 0);
409 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
413 int whitespace = s->whitespace;
415 int linelen = s->linelen;
416 int was_cr = s->was_cr;
418 if (!d) { /* This signals EOF */
421 if (linelen > info->linemax)
422 info->linemax = linelen;
427 for (; dlen; d++, dlen--) {
440 if (linelen > info->linemax)
441 info->linemax = linelen;
456 if (linelen > info->linemax)
457 info->linemax = linelen;
462 else if (ch == '\r') {
470 else if (ch == '\t' || ch == '\f') {
474 else if (ch < 32 || ch == 127)
478 if ((ch == 'F') || (ch == 'f'))
488 if (linelen == 2 && ch != 'r')
490 else if (linelen == 3 && ch != 'o')
492 else if (linelen == 4) {
505 if (ch != ' ' && ch != '\t')
510 s->whitespace = whitespace;
512 s->linelen = linelen;
518 * Find the best charset conversion of the file from fromcode into one
519 * of the tocodes. If successful, set *tocode and CONTENT *info and
520 * return the number of characters converted inexactly. If no
521 * conversion was possible, return -1.
523 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
524 * which would otherwise prevent us from knowing the number of inexact
525 * conversions. Where the candidate target charset is UTF-8 we avoid
526 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
527 * fails with some libraries.
529 * We assume that the output from iconv is never more than 4 times as
530 * long as the input for any pair of charsets we might be interested
533 static ssize_t convert_file_to (FILE * file, const char *fromcode,
534 int ncodes, const char **tocodes,
535 int *tocode, CONTENT * info)
538 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
541 ssize_t ibl, obl, ubl, ubl1, n, ret;
544 CONTENT_STATE *states;
547 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
548 if (cd1 == MUTT_ICONV_ERROR)
551 cd = p_new(iconv_t, ncodes);
552 score = p_new(ssize_t, ncodes);
553 states = p_new(CONTENT_STATE, ncodes);
554 infos = p_new(CONTENT, ncodes);
556 for (i = 0; i < ncodes; i++)
557 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
558 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
560 /* Special case for conversion to UTF-8 */
561 cd[i] = MUTT_ICONV_ERROR, score[i] = -1;
567 /* Try to fill input buffer */
568 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
571 /* Convert to UTF-8 */
573 ob = bufu, obl = sizeof (bufu);
574 n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
575 if (n == -1 && ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
581 /* Convert from UTF-8 */
582 for (i = 0; i < ncodes; i++)
583 if (cd[i] != MUTT_ICONV_ERROR && score[i] != -1) {
584 ub = bufu, ubl = ubl1;
585 ob = bufo, obl = sizeof (bufo);
586 n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
592 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
595 else if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1)
596 /* Special case for conversion to UTF-8 */
597 update_content_info (&infos[i], &states[i], bufu, ubl1);
600 /* Save unused input */
601 memmove (bufi, ib, ibl);
602 else if (!ubl1 && ib < bufi + sizeof (bufi)) {
609 /* Find best score */
611 for (i = 0; i < ncodes; i++) {
612 if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1) {
613 /* Special case for conversion to UTF-8 */
618 else if (cd[i] == MUTT_ICONV_ERROR || score[i] == -1)
620 else if (ret == -1 || score[i] < ret) {
628 memcpy (info, &infos[*tocode], sizeof (CONTENT));
629 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
633 for (i = 0; i < ncodes; i++)
634 if (cd[i] != MUTT_ICONV_ERROR)
647 * Find the first of the fromcodes that gives a valid conversion and
648 * the best charset conversion of the file into one of the tocodes. If
649 * successful, set *fromcode and *tocode to dynamically allocated
650 * strings, set CONTENT *info, and return the number of characters
651 * converted inexactly. If no conversion was possible, return -1.
653 * Both fromcodes and tocodes may be colon-separated lists of charsets.
654 * However, if fromcode is zero then fromcodes is assumed to be the
655 * name of a single charset even if it contains a colon.
657 static ssize_t convert_file_from_to (FILE * file,
658 const char *fromcodes,
659 const char *tocodes, char **fromcode,
660 char **tocode, CONTENT * info)
668 /* Count the tocodes */
670 for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
671 if ((c1 = strchr (c, ':')) == c)
677 tcode = p_new(char *, ncodes);
678 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
679 if ((c1 = strchr (c, ':')) == c)
681 tcode[i] = m_substrdup(c, c1);
686 /* Try each fromcode in turn */
687 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
688 if ((c1 = strchr (c, ':')) == c)
690 fcode = m_substrdup(c, c1);
692 ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
704 /* There is only one fromcode */
705 ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
714 for (i = 0; i < ncodes; i++)
723 * Analyze the contents of a file to determine which MIME encoding to use.
724 * Also set the body charset, sometimes, or not.
726 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
731 char *fromcode = NULL;
742 if (stat (fname, &sb) == -1) {
743 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
747 if (!S_ISREG (sb.st_mode)) {
748 mutt_error (_("%s isn't a regular file."), fname);
752 if ((fp = fopen (fname, "r")) == NULL) {
756 info = p_new(CONTENT, 1);
759 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
760 const char *chs = parameter_getval(b->parameter, "charset");
761 char *fchs = b->use_disp && !m_strisempty(mod_cset.file_charset)
762 ? FileCharset : mod_cset.charset;
763 if (mod_cset.charset && (chs || mod_cset.send_charset) &&
764 convert_file_from_to (fp, fchs, chs ? chs : mod_cset.send_charset,
765 &fromcode, &tocode, info) != -1) {
767 charset_canonicalize (chsbuf, sizeof (chsbuf), tocode);
768 parameter_setval(&b->parameter, "charset", chsbuf);
770 b->file_charset = fromcode;
778 while ((r = fread (buffer, 1, sizeof (buffer), fp)))
779 update_content_info (info, &state, buffer, r);
780 update_content_info (info, &state, 0, 0);
784 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
785 parameter_setval(&b->parameter, "charset",
786 (!info->hibin ? "us-ascii"
787 : mod_cset.charset && !charset_is_us_ascii(mod_cset.charset)
788 ? mod_cset.charset : "unknown-8bit"));
793 /* Given a file with path ``s'', see if there is a registered MIME type.
794 * returns the major MIME type, and copies the subtype to ``d''. First look
795 * for ~/.mime.types, then look in a system mime.types if we can find one.
796 * The longest match is used so that we can match `ps.gz' when `gz' also
800 int mutt_lookup_mime_type (BODY * att, const char *path)
804 char buf[LONG_STRING];
805 char subtype[STRING], xtype[STRING];
807 int szf, sze, cur_sze;
815 szf = m_strlen(path);
817 for (count = 0; count < 4; count++) {
819 * can't use strtok() because we use it in an inner loop below, so use
820 * a switch statement here instead.
824 snprintf(buf, sizeof (buf), "%s/.mime.types", NONULL(mod_core.homedir));
827 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
830 m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
833 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
836 goto bye; /* shouldn't happen */
839 if ((f = fopen (buf, "r")) != NULL) {
840 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
841 /* weed out any comments */
842 if ((p = strchr (buf, '#')))
845 /* remove any leading space. */
846 ct = vskipspaces(buf);
848 /* position on the next field in this line */
849 if ((p = strpbrk (ct, " \t")) == NULL)
854 /* cycle through the file extensions */
855 while ((p = strtok (p, " \t\n"))) {
857 if ((sze > cur_sze) && (szf >= sze) &&
858 (m_strcasecmp(path + szf - sze, p) == 0
859 || ascii_strcasecmp (path + szf - sze, p) == 0)
860 && (szf == sze || path[szf - sze - 1] == '.'))
862 /* get the content-type */
864 if ((p = strchr (ct, '/')) == NULL) {
865 /* malformed line, just skip it. */
870 for (q = p; *q && !ISSPACE (*q); q++);
872 m_strncpy(subtype, sizeof(subtype), p, q - p);
874 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
875 m_strcpy(xtype, sizeof(xtype), ct);
888 if (type != TYPEOTHER || *xtype != '\0') {
890 m_strreplace(&att->subtype, subtype);
891 m_strreplace(&att->xtype, xtype);
897 void mutt_message_to_7bit (BODY * a, FILE * fp)
899 char temp[_POSIX_PATH_MAX];
905 if (!a->filename && fp)
907 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
908 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
913 if (stat (a->filename, &sb) == -1) {
914 mutt_perror ("stat");
917 a->length = sb.st_size;
920 fpout = m_tempfile(temp, sizeof(temp), NONULL(mod_core.tmpdir), NULL);
922 mutt_error(_("Could not create temporary file"));
926 fseeko (fpin, a->offset, 0);
927 a->parts = mutt_parse_messageRFC822 (fpin, a);
929 transform_to_7bit (a->parts, fpin);
931 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
932 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
934 fputs ("MIME-Version: 1.0\n", fpout);
935 mutt_write_mime_header (a->parts, fpout);
937 mutt_write_mime_body (a->parts, fpout);
949 a->encoding = ENC7BIT;
950 a->d_filename = a->filename;
951 if (a->filename && a->unlink)
952 unlink (a->filename);
953 a->filename = m_strdup(temp);
955 if (stat (a->filename, &sb) == -1) {
956 mutt_perror ("stat");
959 a->length = sb.st_size;
960 body_list_wipe(&a->parts);
961 a->hdr->content = NULL;
964 static void transform_to_7bit (BODY * a, FILE * fpin)
966 char buff[_POSIX_PATH_MAX];
971 for (; a; a = a->next) {
972 if (a->type == TYPEMULTIPART) {
973 if (a->encoding != ENC7BIT)
974 a->encoding = ENC7BIT;
976 transform_to_7bit (a->parts, fpin);
978 else if (mutt_is_message_type(a)) {
979 mutt_message_to_7bit (a, fpin);
983 a->force_charset = 1;
985 s.fpout = m_tempfile(buff, sizeof(buff), NONULL(mod_core.tmpdir), NULL);
987 mutt_error(_("Could not create temporary file"));
991 mutt_decode_attachment (a, &s);
993 a->d_filename = a->filename;
994 a->filename = m_strdup(buff);
996 if (stat (a->filename, &sb) == -1) {
997 mutt_perror ("stat");
1000 a->length = sb.st_size;
1002 mutt_update_encoding (a);
1003 if (a->encoding == ENC8BIT)
1004 a->encoding = ENCQUOTEDPRINTABLE;
1005 else if (a->encoding == ENCBINARY)
1006 a->encoding = ENCBASE64;
1011 /* determine which Content-Transfer-Encoding to use */
1012 static void mutt_set_encoding (BODY * b, CONTENT * info)
1014 char send_charset[STRING];
1016 if (b->type == TYPETEXT) {
1018 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1019 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1020 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1021 b->encoding = ENCQUOTEDPRINTABLE;
1022 else if (info->hibin)
1023 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1025 b->encoding = ENC7BIT;
1027 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1028 if (info->lobin || info->hibin) {
1029 if (option (OPTALLOW8BIT) && !info->lobin)
1030 b->encoding = ENC8BIT;
1032 mutt_message_to_7bit (b, NULL);
1035 b->encoding = ENC7BIT;
1037 else if (b->type == TYPEAPPLICATION
1038 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1039 b->encoding = ENC7BIT;
1042 /* Determine which encoding is smaller */
1043 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1044 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1045 b->encoding = ENCBASE64;
1047 b->encoding = ENCQUOTEDPRINTABLE;
1051 void mutt_stamp_attachment (BODY * a)
1053 a->stamp = time (NULL);
1056 /* Get a body's character set */
1058 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1062 if (b && b->type != TYPETEXT)
1065 p = b ? parameter_getval(b->parameter, "charset") : NULL;
1066 charset_canonicalize(d, dlen, p);
1071 /* Assumes called from send mode where BODY->filename points to actual file */
1072 void mutt_update_encoding (BODY * a)
1075 char chsbuff[STRING];
1077 /* override noconv when it's us-ascii */
1078 if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1081 if (!a->force_charset && !a->noconv)
1082 parameter_delval(&a->parameter, "charset");
1084 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1087 mutt_set_encoding (a, info);
1088 mutt_stamp_attachment (a);
1090 p_delete(&a->content);
1095 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1097 char buffer[LONG_STRING];
1100 int cmflags, chflags;
1101 int pgp = hdr->security;
1103 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1104 (hdr->security & ENCRYPT)) {
1107 fp = m_tempfile(buffer, sizeof(buffer), NONULL(mod_core.tmpdir), NULL);
1112 body->type = TYPEMESSAGE;
1113 body->subtype = m_strdup("rfc822");
1114 body->filename = m_strdup(buffer);
1117 body->disposition = DISPINLINE;
1120 mutt_parse_mime_message (ctx, hdr);
1125 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1126 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1127 chflags |= CH_MIME | CH_TXTPLAIN;
1128 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1129 pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1131 else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1132 if (mutt_is_multipart_encrypted (hdr->content)) {
1133 chflags |= CH_MIME | CH_NONEWLINE;
1134 cmflags = M_CM_DECODE_PGP;
1137 else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1138 chflags |= CH_MIME | CH_TXTPLAIN;
1139 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1142 else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1143 chflags |= CH_MIME | CH_TXTPLAIN;
1144 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1145 pgp &= ~SMIMEENCRYPT;
1149 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1154 body->hdr = header_new();
1155 body->hdr->offset = 0;
1156 /* we don't need the user headers here */
1157 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1158 body->hdr->security = pgp;
1159 mutt_update_encoding (body);
1160 body->parts = body->hdr->content;
1167 BODY *mutt_make_file_attach (const char *path)
1173 att->filename = m_strdup(path);
1175 /* Attempt to determine the appropriate content-type based on the filename
1178 mutt_lookup_mime_type (att, path);
1180 if ((info = mutt_get_content_info (path, att)) == NULL) {
1181 body_list_wipe(&att);
1185 if (!att->subtype) {
1186 if (info->lobin == 0
1187 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1189 * Statistically speaking, there should be more than 10% "lobin"
1190 * chars if this is really a binary file...
1192 att->type = TYPETEXT;
1193 att->subtype = m_strdup("plain");
1195 att->type = TYPEAPPLICATION;
1196 att->subtype = m_strdup("octet-stream");
1200 mutt_update_encoding (att);
1204 static int get_toplevel_encoding (BODY * a)
1208 for (; a; a = a->next) {
1209 if (a->encoding == ENCBINARY)
1212 if (a->encoding == ENC8BIT)
1219 BODY *mutt_make_multipart (BODY * b)
1224 new->type = TYPEMULTIPART;
1225 new->subtype = m_strdup("mixed");
1226 new->encoding = get_toplevel_encoding (b);
1227 parameter_set_boundary(&new->parameter);
1229 new->disposition = DISPINLINE;
1235 /* remove the multipart body if it exists */
1236 BODY *mutt_remove_multipart (BODY * b)
1249 char *mutt_make_date (char *s, ssize_t len)
1251 time_t t = time (NULL);
1252 struct tm *l = localtime (&t);
1253 time_t tz = mutt_local_tz (t);
1257 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1258 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1259 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1260 (int) tz / 60, (int) abs (tz) % 60);
1264 /* wrapper around mutt_write_address() so we can handle very large
1265 recipient lists without needing a huge temporary buffer in memory */
1267 mutt_write_address_list(address_t *addr, FILE *fp, int linelen, int display)
1272 char buf[LONG_STRING];
1273 int len = rfc822_addrcpy(buf, ssizeof(buf), addr, display);
1276 if (linelen + len > 74) {
1278 linelen = 8; /* tab is usually about 8 spaces... */
1280 if (addr->mailbox) {
1290 if (!addr->group && addr->next && addr->next->mailbox) {
1300 /* need to write the list in reverse because they are stored in reverse order
1301 * when parsed to speed up threading
1303 void mutt_write_references(string_list_t *r, FILE *f)
1305 string_list_t *refs[10];
1308 p_clear(refs, countof(refs));
1309 for (i = 0; i < countof(refs) && r; r = r->next) {
1314 fprintf(f, " %s", refs[i]->data);
1318 static int edit_header(int mode, const char *s)
1321 int slen = m_strlen(s);
1323 if (mode != 1 || option(OPTXMAILTO))
1326 p = skipspaces(EditorHeaders);
1328 if (!ascii_strncasecmp(p, s, slen) && p[slen - 1] == ':')
1330 p = skipspaces(p + slen);
1336 /* Note: all RFC2047 encoding should be done outside of this routine, except
1337 * for the "real name." This will allow this routine to be used more than
1338 * once, if necessary.
1340 * Likewise, all IDN processing should happen outside of this routine.
1342 * mode == 1 => "lite" mode (used for edit_hdrs)
1343 * mode == 0 => normal mode. write full header + MIME headers
1344 * mode == -1 => write just the envelope info (used for postponing messages)
1346 * privacy != 0 => will omit any headers which may identify the user.
1347 * Output generated is suitable for being sent through
1348 * anonymous remailer chains.
1351 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1352 int mode, int privacy)
1354 char buffer[LONG_STRING];
1356 string_list_t *tmp = env->userhdrs;
1357 int has_agent = 0; /* user defined user-agent header field exists */
1360 if (!option (OPTNEWSSEND))
1362 if (mode == 0 && !privacy)
1363 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1365 /* OPTUSEFROM is not consulted here so that we can still write a From:
1366 * field if the user sets it with the `my_hdr' command
1368 if (env->from && !privacy) {
1370 rfc822_addrcat(buffer, sizeof(buffer), env->from, 0);
1371 fprintf (fp, "From: %s\n", buffer);
1376 mutt_write_address_list (env->to, fp, 4, 0);
1380 if (!option (OPTNEWSSEND))
1382 if (edit_header(mode, "To:"))
1383 fputs ("To:\n", fp);
1387 mutt_write_address_list (env->cc, fp, 4, 0);
1391 if (!option (OPTNEWSSEND))
1393 if (edit_header(mode, "Cc:"))
1394 fputs ("Cc:\n", fp);
1397 if (mode != 0 || option (OPTWRITEBCC)) {
1398 fputs ("Bcc: ", fp);
1399 mutt_write_address_list (env->bcc, fp, 5, 0);
1404 if (!option (OPTNEWSSEND))
1406 if (edit_header(mode, "Bcc:"))
1407 fputs ("Bcc:\n", fp);
1410 if (env->newsgroups)
1411 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1412 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Newsgroups:"))
1413 fputs ("Newsgroups:\n", fp);
1415 if (env->followup_to)
1416 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1417 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Followup-To:"))
1418 fputs ("Followup-To:\n", fp);
1422 fprintf (fp, "Subject: %s\n", env->subject);
1423 else if (mode == 1 && edit_header(mode, "Subject:"))
1424 fputs ("Subject:\n", fp);
1426 /* save message id if the user has set it */
1427 if (env->message_id && !privacy)
1428 fprintf (fp, "Message-ID: %s\n", env->message_id);
1430 if (env->reply_to) {
1431 fputs ("Reply-To: ", fp);
1432 mutt_write_address_list (env->reply_to, fp, 10, 0);
1434 else if (mode > 0 && edit_header(mode, "Reply-To:"))
1435 fputs ("Reply-To:\n", fp);
1437 if (env->mail_followup_to)
1439 if (!option (OPTNEWSSEND))
1442 fputs ("Mail-Followup-To: ", fp);
1443 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1447 if (env->references) {
1448 fputs ("References:", fp);
1449 mutt_write_references (env->references, fp);
1453 /* Add the MIME headers */
1454 fputs ("MIME-Version: 1.0\n", fp);
1455 mutt_write_mime_header (attach, fp);
1458 if (env->in_reply_to) {
1459 fputs ("In-Reply-To:", fp);
1460 mutt_write_references (env->in_reply_to, fp);
1464 /* Add any user defined headers */
1465 for (; tmp; tmp = tmp->next) {
1466 if ((p = strchr (tmp->data, ':'))) {
1467 p = vskipspaces(p + 1);
1469 continue; /* don't emit empty fields. */
1471 /* check to see if the user has overridden the user-agent field */
1472 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1478 fputs (tmp->data, fp);
1483 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1484 if (mod_core.operating_system) {
1485 fprintf(fp, "User-Agent: %s (%s)\n", mutt_make_version(),
1486 mod_core.operating_system);
1488 fprintf(fp, "User-Agent: %s\n", mutt_make_version());
1492 return (ferror (fp) == 0 ? 0 : -1);
1495 static void encode_headers (string_list_t * h)
1501 for (; h; h = h->next) {
1502 if (!(p = strchr (h->data, ':')))
1506 p = vskipspaces(p + 1);
1512 rfc2047_encode_string (&tmp);
1513 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1515 sprintf (h->data + i, ": %s", NONULL (tmp));
1521 const char *mutt_fqdn(short may_hide_host)
1525 if (mod_core.hostname && mod_core.hostname[0] != '@') {
1526 p = mod_core.hostname;
1528 if (may_hide_host && option (OPTHIDDENHOST)) {
1529 if ((p = strchr(mod_core.hostname, '.')))
1532 /* sanity check: don't hide the host if
1533 the fqdn is something like detebe.org. */
1535 if (!p || !(q = strchr(p, '.')))
1536 p = mod_core.hostname;
1543 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1545 #define APPEND_FMT(fmt, arg) \
1547 int snlen = snprintf(buf, len, fmt, arg); \
1552 #define APPEND_BYTE(c) \
1566 static char MsgIdPfx = 'A';
1570 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1571 APPEND_BYTE((isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.');
1579 APPEND_FMT("%02d", tm->tm_mday);
1582 APPEND_FMT("%02d", tm->tm_hour);
1585 APPEND_FMT("%02d", tm->tm_mon + 1);
1588 APPEND_FMT("%02d", tm->tm_min);
1591 APPEND_FMT("%lo", (unsigned long)now);
1594 APPEND_FMT("%u", (unsigned int)getpid());
1597 APPEND_FMT("%c", MsgIdPfx);
1598 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1601 APPEND_FMT("%u", (unsigned int)rand());
1604 APPEND_FMT("%x", (unsigned int)rand());
1607 APPEND_FMT("%02d", tm->tm_sec);
1610 APPEND_FMT("%u", (unsigned int) now);
1613 APPEND_FMT("%x", (unsigned int) now);
1615 case 'Y': /* this will break in the year 10000 ;-) */
1616 APPEND_FMT("%04d", tm->tm_year + 1900);
1621 default: /* invalid formats are replaced by '.' */
1632 static char *mutt_gen_msgid (void)
1635 char localpart[STRING];
1638 if (!(fqdn = mutt_fqdn(0)))
1639 fqdn = NONULL(mod_core.shorthost);
1641 mutt_gen_localpart(localpart, sizeof(localpart), MsgIdFormat);
1642 snprintf(buf, sizeof(buf), "<%s@%s>", localpart, fqdn);
1643 return m_strdup(buf);
1646 static void alarm_handler (int sig __attribute__ ((unused)))
1651 /* invoke sendmail in a subshell
1652 path (in) path to program to execute
1653 args (in) arguments to pass to program
1654 msg (in) temp file containing message to send
1655 tempfile (out) if sendmail is put in the background, this points
1656 to the temporary file containing the stdout of the
1659 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1665 mutt_block_signals_system ();
1668 /* we also don't want to be stopped right now */
1669 sigaddset (&set, SIGTSTP);
1670 sigprocmask (SIG_BLOCK, &set, NULL);
1672 if (MTransport.sendmail_wait >= 0) {
1673 char tmp[_POSIX_PATH_MAX];
1676 *tempfile = m_strdup(tmp);
1679 if ((pid = fork ()) == 0) {
1680 struct sigaction act, oldalrm;
1682 /* save parent's ID before setsid() */
1685 /* we want the delivery to continue even after the main process dies,
1686 * so we put ourselves into another session right away
1690 /* next we close all open files */
1691 for (fd = 0; fd < getdtablesize(); fd++)
1694 /* now the second fork() */
1695 if ((pid = fork ()) == 0) {
1696 /* "msg" will be opened as stdin */
1697 if (open (msg, O_RDONLY, 0) < 0) {
1703 if (MTransport.sendmail_wait >= 0) {
1704 /* *tempfile will be opened as stdout */
1705 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1708 /* redirect stderr to *tempfile too */
1712 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1714 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1718 execv (path, (char**)args);
1721 else if (pid == -1) {
1727 /* sendmail_wait > 0: interrupt waitpid() after sendmail_wait seconds
1728 * sendmail_wait = 0: wait forever
1729 * sendmail_wait < 0: don't wait
1731 if (MTransport.sendmail_wait > 0) {
1733 act.sa_handler = alarm_handler;
1735 /* need to make sure waitpid() is interrupted on SIGALRM */
1736 act.sa_flags = SA_INTERRUPT;
1740 sigemptyset (&act.sa_mask);
1741 sigaction (SIGALRM, &act, &oldalrm);
1742 alarm (MTransport.sendmail_wait);
1744 else if (MTransport.sendmail_wait < 0)
1745 _exit (0xff & EX_OK);
1747 if (waitpid (pid, &st, 0) > 0) {
1748 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1749 if (MTransport.sendmail_wait && st == (0xff & EX_OK)) {
1750 unlink (*tempfile); /* no longer needed */
1754 st = (MTransport.sendmail_wait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1755 if (MTransport.sendmail_wait > 0) {
1761 /* reset alarm; not really needed, but... */
1763 sigaction (SIGALRM, &oldalrm, NULL);
1765 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1766 /* the parent is already dead */
1774 sigprocmask (SIG_UNBLOCK, &set, NULL);
1776 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1777 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1779 st = S_ERR; /* error */
1781 mutt_unblock_signals_system (1);
1786 static const char **
1787 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1789 for (; addr; addr = addr->next) {
1790 /* weed out group mailboxes, since those are for display only */
1791 if (addr->mailbox && !addr->group) {
1792 if (*argslen == *argsmax)
1793 p_realloc(&args, *argsmax += 5);
1794 args[(*argslen)++] = addr->mailbox;
1800 static const char **
1801 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1803 if (*argslen == *argsmax) {
1804 p_realloc(&args, *argsmax += 5);
1806 args[(*argslen)++] = s;
1810 static int mutt_invoke_sendmail (address_t * from, /* the sender */
1811 address_t * to, address_t * cc, address_t * bcc, /* recips */
1812 const char *msg, /* file containing message */
1814 { /* message contains 8bit chars */
1815 char cmd[LONG_STRING];
1816 char *ps = NULL, *path = NULL, *childout = NULL;
1817 const char **args = NULL;
1818 ssize_t argslen = 0, argsmax = 0;
1822 if (option (OPTNEWSSEND)) {
1823 m_strformat(cmd, sizeof(cmd), 0, Inews, nntp_format_str, 0, 0);
1824 if (m_strisempty(cmd)) {
1825 i = nntp_post (msg);
1832 m_strcpy(cmd, sizeof(cmd), MTransport.sendmail);
1837 while ((ps = strtok(ps, " "))) {
1838 if (argslen == argsmax)
1839 p_realloc(&args, argsmax += 5);
1842 args[argslen++] = ps;
1844 path = m_strdup(ps);
1845 ps = strrchr (ps, '/');
1850 args[argslen++] = ps;
1857 if (!option (OPTNEWSSEND)) {
1859 if (eightbit && MTransport.use_8bitmime)
1860 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1862 if (MTransport.use_envelope_from) {
1863 address_t *f = MTransport.envelope_from_address;
1864 if (!f && from && !from->next)
1867 args = add_option (args, &argslen, &argsmax, "-f");
1868 args = add_args (args, &argslen, &argsmax, f);
1871 if (MTransport.dsn_notify) {
1872 args = add_option (args, &argslen, &argsmax, "-N");
1873 args = add_option (args, &argslen, &argsmax, MTransport.dsn_notify);
1875 if (MTransport.dsn_return) {
1876 args = add_option (args, &argslen, &argsmax, "-R");
1877 args = add_option (args, &argslen, &argsmax, MTransport.dsn_return);
1879 args = add_option (args, &argslen, &argsmax, "--");
1880 args = add_args (args, &argslen, &argsmax, to);
1881 args = add_args (args, &argslen, &argsmax, cc);
1882 args = add_args (args, &argslen, &argsmax, bcc);
1887 if (argslen >= argsmax)
1888 p_realloc(&args, ++argsmax);
1890 args[argslen++] = NULL;
1892 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1894 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1899 if (!stat(childout, &st) && st.st_size > 0)
1900 mutt_pager(_("Output of the delivery process"), childout, 0, NULL);
1907 p_delete(&childout);
1911 if (i == (EX_OK & 0xff))
1913 else if (i == S_BKG)
1920 int mutt_invoke_mta (address_t * from, /* the sender */
1921 address_t * to, address_t * cc, address_t * bcc, /* recips */
1922 const char *msg, /* file containing message */
1924 { /* message contains 8bit chars */
1927 if (!option (OPTNEWSSEND))
1930 return send_smtp_invoke (from, to, cc, bcc, msg, eightbit);
1933 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
1936 /* For postponing (!final) do the necessary encodings only */
1937 void mutt_prepare_envelope (ENVELOPE * env, int final)
1940 if (env->bcc && !(env->to || env->cc)) {
1941 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1942 * recipients if there is no To: or Cc: field, so attempt to suppress
1943 * it by using an empty To: field.
1945 env->to = address_new();
1947 env->to->next = address_new();
1948 env->to->mailbox = m_strdup("undisclosed-recipients");
1951 mutt_set_followup_to(env);
1953 if (!env->message_id && !m_strisempty(MsgIdFormat))
1954 env->message_id = mutt_gen_msgid();
1957 /* Take care of 8-bit => 7-bit conversion. */
1958 rfc2047_encode_adrlist(env->to, "To");
1959 rfc2047_encode_adrlist(env->cc, "Cc");
1960 rfc2047_encode_adrlist(env->bcc, "Bcc");
1961 rfc2047_encode_adrlist(env->from, "From");
1962 rfc2047_encode_adrlist(env->mail_followup_to, "Mail-Followup-To");
1963 rfc2047_encode_adrlist(env->reply_to, "Reply-To");
1966 rfc2047_encode_string (&env->subject);
1967 encode_headers (env->userhdrs);
1970 void mutt_unprepare_envelope (ENVELOPE * env)
1972 string_list_t *item;
1974 for (item = env->userhdrs; item; item = item->next)
1975 rfc2047_decode(&item->data);
1977 address_list_wipe(&env->mail_followup_to);
1979 /* back conversions */
1980 rfc2047_decode_adrlist(env->to);
1981 rfc2047_decode_adrlist(env->cc);
1982 rfc2047_decode_adrlist(env->bcc);
1983 rfc2047_decode_adrlist(env->from);
1984 rfc2047_decode_adrlist(env->reply_to);
1985 rfc2047_decode(&env->subject);
1988 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
1989 const char *resent_from, address_t * env_from)
1993 char date[STRING], tempfile[_POSIX_PATH_MAX];
1994 MESSAGE *msg = NULL;
1997 /* Try to bounce each message out, aborting if we get any failures. */
1998 for (i = 0; i < Context->msgcount; i++)
1999 if (Context->hdrs[i]->tagged)
2001 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2006 /* If we failed to open a message, return with error */
2007 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2013 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2015 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2017 if (!option (OPTBOUNCEDELIVERED))
2018 ch_flags |= CH_WEED_DELIVERED;
2020 fseeko (fp, h->offset, 0);
2021 fprintf (f, "Resent-From: %s", resent_from);
2022 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2023 if (!m_strisempty(MsgIdFormat))
2024 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2025 fputs ("Resent-To: ", f);
2026 mutt_write_address_list (to, f, 11, 0);
2027 mutt_copy_header (fp, h, f, ch_flags, NULL);
2029 mutt_copy_bytes (fp, f, h->content->length);
2032 ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
2033 h->content->encoding == ENC8BIT);
2037 mx_close_message (&msg);
2042 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2045 char resent_from[STRING];
2049 resent_from[0] = '\0';
2050 from = mutt_default_from ();
2052 rfc822_qualify(from, mutt_fqdn(1));
2054 rfc2047_encode_adrlist(from, "Resent-From");
2055 if (mutt_addrlist_to_idna (from, &err)) {
2056 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2059 rfc822_addrcat(resent_from, sizeof(resent_from), from, 0);
2062 unset_option (OPTNEWSSEND);
2065 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2067 address_list_wipe(&from);
2072 static void set_noconv_flags (BODY * b, short flag)
2074 for (; b; b = b->next) {
2075 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2076 set_noconv_flags (b->parts, flag);
2077 else if (b->type == TYPETEXT && b->noconv) {
2078 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2083 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2084 int post, char *fcc)
2088 char tempfile[_POSIX_PATH_MAX];
2089 FILE *tempfp = NULL;
2093 set_noconv_flags (hdr->content, 1);
2095 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2099 /* We need to add a Content-Length field to avoid problems where a line in
2100 * the message body begins with "From "
2102 if (f.magic == M_MBOX) {
2103 tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2105 mutt_error(_("Could not create temporary file"));
2106 mx_close_mailbox (&f, NULL);
2111 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2112 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2113 mx_close_mailbox (&f, NULL);
2117 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2118 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2120 mutt_write_rfc822_header(msg->fp, hdr->env, hdr->content, -post, 0);
2122 /* (postponment) if this was a reply of some sort, <msgid> contians the
2123 * Message-ID: of message replied to. Save it using a special X-Mutt-
2124 * header so it can be picked up if the message is recalled at a later
2125 * point in time. This will allow the message to be marked as replied if
2126 * the same mailbox is still open.
2129 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2131 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2132 * it can be picked up when the message is recalled
2135 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2136 fprintf (msg->fp, "Status: RO\n");
2138 /* (postponment) if the mail is to be signed or encrypted, save this info */
2139 if (post && (hdr->security & APPLICATION_PGP)) {
2140 fputs ("X-Mutt-PGP: ", msg->fp);
2141 if (hdr->security & ENCRYPT)
2142 fputc ('E', msg->fp);
2143 if (hdr->security & SIGN) {
2144 fputc ('S', msg->fp);
2145 if (PgpSignAs && *PgpSignAs)
2146 fprintf (msg->fp, "<%s>", PgpSignAs);
2148 if (hdr->security & INLINE)
2149 fputc ('I', msg->fp);
2150 fputc ('\n', msg->fp);
2153 /* (postponment) if the mail is to be signed or encrypted, save this info */
2154 if (post && (hdr->security & APPLICATION_SMIME)) {
2155 fputs ("X-Mutt-SMIME: ", msg->fp);
2156 if (hdr->security & ENCRYPT) {
2157 fputc ('E', msg->fp);
2158 if (SmimeCryptAlg && *SmimeCryptAlg)
2159 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2161 if (hdr->security & SIGN) {
2162 fputc ('S', msg->fp);
2163 if (SmimeDefaultKey && *SmimeDefaultKey)
2164 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2166 if (hdr->security & INLINE)
2167 fputc ('I', msg->fp);
2168 fputc ('\n', msg->fp);
2171 /* (postponement) if the mail is to be sent through a mixmaster
2172 * chain, save that information
2174 if (post && hdr->chain && hdr->chain) {
2177 fputs ("X-Mutt-Mix:", msg->fp);
2178 for (p = hdr->chain; p; p = p->next)
2179 fprintf (msg->fp, " %s", (char *) p->data);
2181 fputc ('\n', msg->fp);
2185 char sasha[LONG_STRING];
2188 mutt_write_mime_body (hdr->content, tempfp);
2190 /* make sure the last line ends with a newline. Emacs doesn't ensure
2191 * this will happen, and it can cause problems parsing the mailbox
2194 fseeko (tempfp, -1, 2);
2195 if (fgetc (tempfp) != '\n') {
2196 fseeko (tempfp, 0, 2);
2197 fputc ('\n', tempfp);
2201 if (ferror (tempfp)) {
2204 mx_commit_message (msg, &f); /* XXX - really? */
2205 mx_close_message (&msg);
2206 mx_close_mailbox (&f, NULL);
2210 /* count the number of lines */
2212 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2214 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2215 fprintf (msg->fp, "Lines: %d\n\n", lines);
2217 /* copy the body and clean up */
2219 r = mutt_copy_stream (tempfp, msg->fp);
2220 if (m_fclose(&tempfp) != 0)
2222 /* if there was an error, leave the temp version */
2226 fputc ('\n', msg->fp); /* finish off the header */
2227 r = mutt_write_mime_body (hdr->content, msg->fp);
2230 if (mx_commit_message (msg, &f) != 0)
2232 mx_close_message (&msg);
2233 mx_close_mailbox (&f, NULL);
2236 set_noconv_flags (hdr->content, 0);