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 && !m_strisempty(MCharset.file_charset)
771 ? FileCharset : MCharset.charset;
772 if (MCharset.charset && (chs || MCharset.send_charset) &&
773 convert_file_from_to (fp, fchs, chs ? chs : MCharset.send_charset,
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 : MCharset.charset && !charset_is_us_ascii(MCharset.charset)
797 ? MCharset.charset : "unknown-8bit"));
802 /* Given a file with path ``s'', see if there is a registered MIME type.
803 * returns the major MIME type, and copies the subtype to ``d''. First look
804 * for ~/.mime.types, then look in a system mime.types if we can find one.
805 * The longest match is used so that we can match `ps.gz' when `gz' also
809 int mutt_lookup_mime_type (BODY * att, const char *path)
813 char buf[LONG_STRING];
814 char subtype[STRING], xtype[STRING];
816 int szf, sze, cur_sze;
824 szf = m_strlen(path);
826 for (count = 0; count < 4; count++) {
828 * can't use strtok() because we use it in an inner loop below, so use
829 * a switch statement here instead.
833 snprintf(buf, sizeof (buf), "%s/.mime.types", NONULL(MCore.homedir));
836 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
839 m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
842 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
845 goto bye; /* shouldn't happen */
848 if ((f = fopen (buf, "r")) != NULL) {
849 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
850 /* weed out any comments */
851 if ((p = strchr (buf, '#')))
854 /* remove any leading space. */
855 ct = vskipspaces(buf);
857 /* position on the next field in this line */
858 if ((p = strpbrk (ct, " \t")) == NULL)
863 /* cycle through the file extensions */
864 while ((p = strtok (p, " \t\n"))) {
866 if ((sze > cur_sze) && (szf >= sze) &&
867 (m_strcasecmp(path + szf - sze, p) == 0
868 || ascii_strcasecmp (path + szf - sze, p) == 0)
869 && (szf == sze || path[szf - sze - 1] == '.'))
871 /* get the content-type */
873 if ((p = strchr (ct, '/')) == NULL) {
874 /* malformed line, just skip it. */
879 for (q = p; *q && !ISSPACE (*q); q++);
881 m_strncpy(subtype, sizeof(subtype), p, q - p);
883 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
884 m_strcpy(xtype, sizeof(xtype), ct);
897 if (type != TYPEOTHER || *xtype != '\0') {
899 m_strreplace(&att->subtype, subtype);
900 m_strreplace(&att->xtype, xtype);
906 void mutt_message_to_7bit (BODY * a, FILE * fp)
908 char temp[_POSIX_PATH_MAX];
914 if (!a->filename && fp)
916 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
917 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
922 if (stat (a->filename, &sb) == -1) {
923 mutt_perror ("stat");
926 a->length = sb.st_size;
929 fpout = m_tempfile(temp, sizeof(temp), NONULL(MCore.tmpdir), NULL);
931 mutt_error(_("Could not create temporary file"));
935 fseeko (fpin, a->offset, 0);
936 a->parts = mutt_parse_messageRFC822 (fpin, a);
938 transform_to_7bit (a->parts, fpin);
940 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
941 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
943 fputs ("MIME-Version: 1.0\n", fpout);
944 mutt_write_mime_header (a->parts, fpout);
946 mutt_write_mime_body (a->parts, fpout);
958 a->encoding = ENC7BIT;
959 a->d_filename = a->filename;
960 if (a->filename && a->unlink)
961 unlink (a->filename);
962 a->filename = m_strdup(temp);
964 if (stat (a->filename, &sb) == -1) {
965 mutt_perror ("stat");
968 a->length = sb.st_size;
969 body_list_wipe(&a->parts);
970 a->hdr->content = NULL;
973 static void transform_to_7bit (BODY * a, FILE * fpin)
975 char buff[_POSIX_PATH_MAX];
980 for (; a; a = a->next) {
981 if (a->type == TYPEMULTIPART) {
982 if (a->encoding != ENC7BIT)
983 a->encoding = ENC7BIT;
985 transform_to_7bit (a->parts, fpin);
987 else if (mutt_is_message_type(a)) {
988 mutt_message_to_7bit (a, fpin);
992 a->force_charset = 1;
994 s.fpout = m_tempfile(buff, sizeof(buff), NONULL(MCore.tmpdir), NULL);
996 mutt_error(_("Could not create temporary file"));
1000 mutt_decode_attachment (a, &s);
1002 a->d_filename = a->filename;
1003 a->filename = m_strdup(buff);
1005 if (stat (a->filename, &sb) == -1) {
1006 mutt_perror ("stat");
1009 a->length = sb.st_size;
1011 mutt_update_encoding (a);
1012 if (a->encoding == ENC8BIT)
1013 a->encoding = ENCQUOTEDPRINTABLE;
1014 else if (a->encoding == ENCBINARY)
1015 a->encoding = ENCBASE64;
1020 /* determine which Content-Transfer-Encoding to use */
1021 static void mutt_set_encoding (BODY * b, CONTENT * info)
1023 char send_charset[STRING];
1025 if (b->type == TYPETEXT) {
1027 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1028 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1029 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1030 b->encoding = ENCQUOTEDPRINTABLE;
1031 else if (info->hibin)
1032 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1034 b->encoding = ENC7BIT;
1036 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1037 if (info->lobin || info->hibin) {
1038 if (option (OPTALLOW8BIT) && !info->lobin)
1039 b->encoding = ENC8BIT;
1041 mutt_message_to_7bit (b, NULL);
1044 b->encoding = ENC7BIT;
1046 else if (b->type == TYPEAPPLICATION
1047 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1048 b->encoding = ENC7BIT;
1051 /* Determine which encoding is smaller */
1052 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1053 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1054 b->encoding = ENCBASE64;
1056 b->encoding = ENCQUOTEDPRINTABLE;
1060 void mutt_stamp_attachment (BODY * a)
1062 a->stamp = time (NULL);
1065 /* Get a body's character set */
1067 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1071 if (b && b->type != TYPETEXT)
1074 p = b ? parameter_getval(b->parameter, "charset") : NULL;
1075 charset_canonicalize(d, dlen, p);
1080 /* Assumes called from send mode where BODY->filename points to actual file */
1081 void mutt_update_encoding (BODY * a)
1084 char chsbuff[STRING];
1086 /* override noconv when it's us-ascii */
1087 if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1090 if (!a->force_charset && !a->noconv)
1091 parameter_delval(&a->parameter, "charset");
1093 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1096 mutt_set_encoding (a, info);
1097 mutt_stamp_attachment (a);
1099 p_delete(&a->content);
1104 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1106 char buffer[LONG_STRING];
1109 int cmflags, chflags;
1110 int pgp = hdr->security;
1112 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1113 (hdr->security & ENCRYPT)) {
1114 if (!crypt_valid_passphrase (hdr->security))
1118 fp = m_tempfile(buffer, sizeof(buffer), NONULL(MCore.tmpdir), NULL);
1123 body->type = TYPEMESSAGE;
1124 body->subtype = m_strdup("rfc822");
1125 body->filename = m_strdup(buffer);
1128 body->disposition = DISPINLINE;
1131 mutt_parse_mime_message (ctx, hdr);
1136 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1137 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1138 chflags |= CH_MIME | CH_TXTPLAIN;
1139 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1140 pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1142 else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1143 if (mutt_is_multipart_encrypted (hdr->content)) {
1144 chflags |= CH_MIME | CH_NONEWLINE;
1145 cmflags = M_CM_DECODE_PGP;
1148 else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1149 chflags |= CH_MIME | CH_TXTPLAIN;
1150 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1153 else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1154 chflags |= CH_MIME | CH_TXTPLAIN;
1155 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1156 pgp &= ~SMIMEENCRYPT;
1160 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1165 body->hdr = header_new();
1166 body->hdr->offset = 0;
1167 /* we don't need the user headers here */
1168 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1169 body->hdr->security = pgp;
1170 mutt_update_encoding (body);
1171 body->parts = body->hdr->content;
1178 BODY *mutt_make_file_attach (const char *path)
1184 att->filename = m_strdup(path);
1186 /* Attempt to determine the appropriate content-type based on the filename
1189 mutt_lookup_mime_type (att, path);
1191 if ((info = mutt_get_content_info (path, att)) == NULL) {
1192 body_list_wipe(&att);
1196 if (!att->subtype) {
1197 if (info->lobin == 0
1198 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1200 * Statistically speaking, there should be more than 10% "lobin"
1201 * chars if this is really a binary file...
1203 att->type = TYPETEXT;
1204 att->subtype = m_strdup("plain");
1206 att->type = TYPEAPPLICATION;
1207 att->subtype = m_strdup("octet-stream");
1211 mutt_update_encoding (att);
1215 static int get_toplevel_encoding (BODY * a)
1219 for (; a; a = a->next) {
1220 if (a->encoding == ENCBINARY)
1223 if (a->encoding == ENC8BIT)
1230 BODY *mutt_make_multipart (BODY * b)
1235 new->type = TYPEMULTIPART;
1236 new->subtype = m_strdup("mixed");
1237 new->encoding = get_toplevel_encoding (b);
1238 parameter_set_boundary(&new->parameter);
1240 new->disposition = DISPINLINE;
1246 /* remove the multipart body if it exists */
1247 BODY *mutt_remove_multipart (BODY * b)
1260 char *mutt_make_date (char *s, ssize_t len)
1262 time_t t = time (NULL);
1263 struct tm *l = localtime (&t);
1264 time_t tz = mutt_local_tz (t);
1268 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1269 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1270 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1271 (int) tz / 60, (int) abs (tz) % 60);
1275 /* wrapper around mutt_write_address() so we can handle very large
1276 recipient lists without needing a huge temporary buffer in memory */
1278 mutt_write_address_list(address_t *addr, FILE *fp, int linelen, int display)
1283 char buf[LONG_STRING];
1284 int len = rfc822_addrcpy(buf, ssizeof(buf), addr, display);
1287 if (linelen + len > 74) {
1289 linelen = 8; /* tab is usually about 8 spaces... */
1291 if (addr->mailbox) {
1301 if (!addr->group && addr->next && addr->next->mailbox) {
1311 /* need to write the list in reverse because they are stored in reverse order
1312 * when parsed to speed up threading
1314 void mutt_write_references(string_list_t *r, FILE *f)
1316 string_list_t *refs[10];
1319 p_clear(refs, countof(refs));
1320 for (i = 0; i < countof(refs) && r; r = r->next) {
1325 fprintf(f, " %s", refs[i]->data);
1329 static int edit_header(int mode, const char *s)
1332 int slen = m_strlen(s);
1334 if (mode != 1 || option(OPTXMAILTO))
1337 p = skipspaces(EditorHeaders);
1339 if (!ascii_strncasecmp(p, s, slen) && p[slen - 1] == ':')
1341 p = skipspaces(p + slen);
1347 /* Note: all RFC2047 encoding should be done outside of this routine, except
1348 * for the "real name." This will allow this routine to be used more than
1349 * once, if necessary.
1351 * Likewise, all IDN processing should happen outside of this routine.
1353 * mode == 1 => "lite" mode (used for edit_hdrs)
1354 * mode == 0 => normal mode. write full header + MIME headers
1355 * mode == -1 => write just the envelope info (used for postponing messages)
1357 * privacy != 0 => will omit any headers which may identify the user.
1358 * Output generated is suitable for being sent through
1359 * anonymous remailer chains.
1362 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1363 int mode, int privacy)
1365 char buffer[LONG_STRING];
1367 string_list_t *tmp = env->userhdrs;
1368 int has_agent = 0; /* user defined user-agent header field exists */
1371 if (!option (OPTNEWSSEND))
1373 if (mode == 0 && !privacy)
1374 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1376 /* OPTUSEFROM is not consulted here so that we can still write a From:
1377 * field if the user sets it with the `my_hdr' command
1379 if (env->from && !privacy) {
1381 rfc822_addrcat(buffer, sizeof(buffer), env->from, 0);
1382 fprintf (fp, "From: %s\n", buffer);
1387 mutt_write_address_list (env->to, fp, 4, 0);
1391 if (!option (OPTNEWSSEND))
1393 if (edit_header(mode, "To:"))
1394 fputs ("To:\n", fp);
1398 mutt_write_address_list (env->cc, fp, 4, 0);
1402 if (!option (OPTNEWSSEND))
1404 if (edit_header(mode, "Cc:"))
1405 fputs ("Cc:\n", fp);
1408 if (mode != 0 || option (OPTWRITEBCC)) {
1409 fputs ("Bcc: ", fp);
1410 mutt_write_address_list (env->bcc, fp, 5, 0);
1415 if (!option (OPTNEWSSEND))
1417 if (edit_header(mode, "Bcc:"))
1418 fputs ("Bcc:\n", fp);
1421 if (env->newsgroups)
1422 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1423 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Newsgroups:"))
1424 fputs ("Newsgroups:\n", fp);
1426 if (env->followup_to)
1427 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1428 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Followup-To:"))
1429 fputs ("Followup-To:\n", fp);
1431 if (env->x_comment_to)
1432 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1433 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO) &&
1434 edit_header(mode, "X-Comment-To:"))
1435 fputs ("X-Comment-To:\n", fp);
1439 fprintf (fp, "Subject: %s\n", env->subject);
1440 else if (mode == 1 && edit_header(mode, "Subject:"))
1441 fputs ("Subject:\n", fp);
1443 /* save message id if the user has set it */
1444 if (env->message_id && !privacy)
1445 fprintf (fp, "Message-ID: %s\n", env->message_id);
1447 if (env->reply_to) {
1448 fputs ("Reply-To: ", fp);
1449 mutt_write_address_list (env->reply_to, fp, 10, 0);
1451 else if (mode > 0 && edit_header(mode, "Reply-To:"))
1452 fputs ("Reply-To:\n", fp);
1454 if (env->mail_followup_to)
1456 if (!option (OPTNEWSSEND))
1459 fputs ("Mail-Followup-To: ", fp);
1460 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1464 if (env->references) {
1465 fputs ("References:", fp);
1466 mutt_write_references (env->references, fp);
1470 /* Add the MIME headers */
1471 fputs ("MIME-Version: 1.0\n", fp);
1472 mutt_write_mime_header (attach, fp);
1475 if (env->in_reply_to) {
1476 fputs ("In-Reply-To:", fp);
1477 mutt_write_references (env->in_reply_to, fp);
1481 /* Add any user defined headers */
1482 for (; tmp; tmp = tmp->next) {
1483 if ((p = strchr (tmp->data, ':'))) {
1484 p = vskipspaces(p + 1);
1486 continue; /* don't emit empty fields. */
1488 /* check to see if the user has overridden the user-agent field */
1489 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1495 fputs (tmp->data, fp);
1500 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1503 if (OperatingSystem != NULL) {
1504 os = OperatingSystem;
1507 os = (uname(&un) == -1) ? "UNIX" : un.sysname;
1509 /* Add a vanity header */
1510 fprintf (fp, "User-Agent: %s (%s)\n", mutt_make_version(), os);
1513 return (ferror (fp) == 0 ? 0 : -1);
1516 static void encode_headers (string_list_t * h)
1522 for (; h; h = h->next) {
1523 if (!(p = strchr (h->data, ':')))
1527 p = vskipspaces(p + 1);
1533 rfc2047_encode_string (&tmp);
1534 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1536 sprintf (h->data + i, ": %s", NONULL (tmp));
1542 const char *mutt_fqdn (short may_hide_host)
1546 if (Fqdn && Fqdn[0] != '@') {
1549 if (may_hide_host && option (OPTHIDDENHOST)) {
1550 if ((p = strchr (Fqdn, '.')))
1553 /* sanity check: don't hide the host if
1554 the fqdn is something like detebe.org. */
1556 if (!p || !(q = strchr (p, '.')))
1564 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1566 #define APPEND_FMT(fmt, arg) \
1568 int snlen = snprintf(buf, len, fmt, arg); \
1573 #define APPEND_BYTE(c) \
1587 static char MsgIdPfx = 'A';
1591 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1592 APPEND_BYTE((isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.');
1600 APPEND_FMT("%02d", tm->tm_mday);
1603 APPEND_FMT("%02d", tm->tm_hour);
1606 APPEND_FMT("%02d", tm->tm_mon + 1);
1609 APPEND_FMT("%02d", tm->tm_min);
1612 APPEND_FMT("%lo", (unsigned long)now);
1615 APPEND_FMT("%u", (unsigned int)getpid());
1618 APPEND_FMT("%c", MsgIdPfx);
1619 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1622 APPEND_FMT("%u", (unsigned int)rand());
1625 APPEND_FMT("%x", (unsigned int)rand());
1628 APPEND_FMT("%02d", tm->tm_sec);
1631 APPEND_FMT("%u", (unsigned int) now);
1634 APPEND_FMT("%x", (unsigned int) now);
1636 case 'Y': /* this will break in the year 10000 ;-) */
1637 APPEND_FMT("%04d", tm->tm_year + 1900);
1642 default: /* invalid formats are replaced by '.' */
1653 static char *mutt_gen_msgid (void)
1656 char localpart[STRING];
1659 if (!(fqdn = mutt_fqdn(0)))
1660 fqdn = NONULL(Hostname);
1662 mutt_gen_localpart(localpart, sizeof(localpart), MsgIdFormat);
1663 snprintf(buf, sizeof(buf), "<%s@%s>", localpart, fqdn);
1664 return m_strdup(buf);
1667 static RETSIGTYPE alarm_handler (int sig __attribute__ ((unused)))
1672 /* invoke sendmail in a subshell
1673 path (in) path to program to execute
1674 args (in) arguments to pass to program
1675 msg (in) temp file containing message to send
1676 tempfile (out) if sendmail is put in the background, this points
1677 to the temporary file containing the stdout of the
1680 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1686 mutt_block_signals_system ();
1689 /* we also don't want to be stopped right now */
1690 sigaddset (&set, SIGTSTP);
1691 sigprocmask (SIG_BLOCK, &set, NULL);
1693 if (MTransport.sendmail_wait >= 0) {
1694 char tmp[_POSIX_PATH_MAX];
1697 *tempfile = m_strdup(tmp);
1700 if ((pid = fork ()) == 0) {
1701 struct sigaction act, oldalrm;
1703 /* save parent's ID before setsid() */
1706 /* we want the delivery to continue even after the main process dies,
1707 * so we put ourselves into another session right away
1711 /* next we close all open files */
1712 for (fd = 0; fd < getdtablesize(); fd++)
1715 /* now the second fork() */
1716 if ((pid = fork ()) == 0) {
1717 /* "msg" will be opened as stdin */
1718 if (open (msg, O_RDONLY, 0) < 0) {
1724 if (MTransport.sendmail_wait >= 0) {
1725 /* *tempfile will be opened as stdout */
1726 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1729 /* redirect stderr to *tempfile too */
1733 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1735 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1739 execv (path, (char**)args);
1742 else if (pid == -1) {
1748 /* sendmail_wait > 0: interrupt waitpid() after sendmail_wait seconds
1749 * sendmail_wait = 0: wait forever
1750 * sendmail_wait < 0: don't wait
1752 if (MTransport.sendmail_wait > 0) {
1754 act.sa_handler = alarm_handler;
1756 /* need to make sure waitpid() is interrupted on SIGALRM */
1757 act.sa_flags = SA_INTERRUPT;
1761 sigemptyset (&act.sa_mask);
1762 sigaction (SIGALRM, &act, &oldalrm);
1763 alarm (MTransport.sendmail_wait);
1765 else if (MTransport.sendmail_wait < 0)
1766 _exit (0xff & EX_OK);
1768 if (waitpid (pid, &st, 0) > 0) {
1769 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1770 if (MTransport.sendmail_wait && st == (0xff & EX_OK)) {
1771 unlink (*tempfile); /* no longer needed */
1775 st = (MTransport.sendmail_wait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1776 if (MTransport.sendmail_wait > 0) {
1782 /* reset alarm; not really needed, but... */
1784 sigaction (SIGALRM, &oldalrm, NULL);
1786 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1787 /* the parent is already dead */
1795 sigprocmask (SIG_UNBLOCK, &set, NULL);
1797 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1798 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1800 st = S_ERR; /* error */
1802 mutt_unblock_signals_system (1);
1807 static const char **
1808 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1810 for (; addr; addr = addr->next) {
1811 /* weed out group mailboxes, since those are for display only */
1812 if (addr->mailbox && !addr->group) {
1813 if (*argslen == *argsmax)
1814 p_realloc(&args, *argsmax += 5);
1815 args[(*argslen)++] = addr->mailbox;
1821 static const char **
1822 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1824 if (*argslen == *argsmax) {
1825 p_realloc(&args, *argsmax += 5);
1827 args[(*argslen)++] = s;
1831 static int mutt_invoke_sendmail (address_t * from, /* the sender */
1832 address_t * to, address_t * cc, address_t * bcc, /* recips */
1833 const char *msg, /* file containing message */
1835 { /* message contains 8bit chars */
1836 char cmd[LONG_STRING];
1837 char *ps = NULL, *path = NULL, *childout = NULL;
1838 const char **args = NULL;
1839 ssize_t argslen = 0, argsmax = 0;
1843 if (option (OPTNEWSSEND)) {
1844 m_strformat(cmd, sizeof(cmd), 0, Inews, nntp_format_str, 0, 0);
1845 if (m_strisempty(cmd)) {
1846 i = nntp_post (msg);
1853 m_strcpy(cmd, sizeof(cmd), MTransport.sendmail);
1858 while ((ps = strtok(ps, " "))) {
1859 if (argslen == argsmax)
1860 p_realloc(&args, argsmax += 5);
1863 args[argslen++] = ps;
1865 path = m_strdup(ps);
1866 ps = strrchr (ps, '/');
1871 args[argslen++] = ps;
1878 if (!option (OPTNEWSSEND)) {
1880 if (eightbit && MTransport.use_8bitmime)
1881 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1883 if (MTransport.use_envelope_from) {
1884 address_t *f = MTransport.envelope_from_address;
1885 if (!f && from && !from->next)
1888 args = add_option (args, &argslen, &argsmax, "-f");
1889 args = add_args (args, &argslen, &argsmax, f);
1892 if (MTransport.dsn_notify) {
1893 args = add_option (args, &argslen, &argsmax, "-N");
1894 args = add_option (args, &argslen, &argsmax, MTransport.dsn_notify);
1896 if (MTransport.dsn_return) {
1897 args = add_option (args, &argslen, &argsmax, "-R");
1898 args = add_option (args, &argslen, &argsmax, MTransport.dsn_return);
1900 args = add_option (args, &argslen, &argsmax, "--");
1901 args = add_args (args, &argslen, &argsmax, to);
1902 args = add_args (args, &argslen, &argsmax, cc);
1903 args = add_args (args, &argslen, &argsmax, bcc);
1908 if (argslen >= argsmax)
1909 p_realloc(&args, ++argsmax);
1911 args[argslen++] = NULL;
1913 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1915 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1920 if (!stat(childout, &st) && st.st_size > 0)
1921 mutt_do_pager(_("Output of the delivery process"), childout, 0,
1929 p_delete(&childout);
1933 if (i == (EX_OK & 0xff))
1935 else if (i == S_BKG)
1942 int mutt_invoke_mta (address_t * from, /* the sender */
1943 address_t * to, address_t * cc, address_t * bcc, /* recips */
1944 const char *msg, /* file containing message */
1946 { /* message contains 8bit chars */
1949 if (!option (OPTNEWSSEND))
1952 return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
1955 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
1958 /* For postponing (!final) do the necessary encodings only */
1959 void mutt_prepare_envelope (ENVELOPE * env, int final)
1962 if (env->bcc && !(env->to || env->cc)) {
1963 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1964 * recipients if there is no To: or Cc: field, so attempt to suppress
1965 * it by using an empty To: field.
1967 env->to = address_new();
1969 env->to->next = address_new();
1970 env->to->mailbox = m_strdup("undisclosed-recipients");
1973 mutt_set_followup_to(env);
1975 if (!env->message_id && !m_strisempty(MsgIdFormat))
1976 env->message_id = mutt_gen_msgid();
1979 /* Take care of 8-bit => 7-bit conversion. */
1980 rfc2047_encode_adrlist(env->to, "To");
1981 rfc2047_encode_adrlist(env->cc, "Cc");
1982 rfc2047_encode_adrlist(env->bcc, "Bcc");
1983 rfc2047_encode_adrlist(env->from, "From");
1984 rfc2047_encode_adrlist(env->mail_followup_to, "Mail-Followup-To");
1985 rfc2047_encode_adrlist(env->reply_to, "Reply-To");
1989 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
1992 rfc2047_encode_string (&env->subject);
1994 encode_headers (env->userhdrs);
1997 void mutt_unprepare_envelope (ENVELOPE * env)
1999 string_list_t *item;
2001 for (item = env->userhdrs; item; item = item->next)
2002 rfc2047_decode(&item->data);
2004 address_list_wipe(&env->mail_followup_to);
2006 /* back conversions */
2007 rfc2047_decode_adrlist(env->to);
2008 rfc2047_decode_adrlist(env->cc);
2009 rfc2047_decode_adrlist(env->bcc);
2010 rfc2047_decode_adrlist(env->from);
2011 rfc2047_decode_adrlist(env->reply_to);
2012 rfc2047_decode(&env->subject);
2015 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
2016 const char *resent_from, address_t * env_from)
2020 char date[STRING], tempfile[_POSIX_PATH_MAX];
2021 MESSAGE *msg = NULL;
2024 /* Try to bounce each message out, aborting if we get any failures. */
2025 for (i = 0; i < Context->msgcount; i++)
2026 if (Context->hdrs[i]->tagged)
2028 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2033 /* If we failed to open a message, return with error */
2034 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2040 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2042 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2044 if (!option (OPTBOUNCEDELIVERED))
2045 ch_flags |= CH_WEED_DELIVERED;
2047 fseeko (fp, h->offset, 0);
2048 fprintf (f, "Resent-From: %s", resent_from);
2049 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2050 if (!m_strisempty(MsgIdFormat))
2051 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2052 fputs ("Resent-To: ", f);
2053 mutt_write_address_list (to, f, 11, 0);
2054 mutt_copy_header (fp, h, f, ch_flags, NULL);
2056 mutt_copy_bytes (fp, f, h->content->length);
2059 ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
2060 h->content->encoding == ENC8BIT);
2064 mx_close_message (&msg);
2069 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2072 char resent_from[STRING];
2076 resent_from[0] = '\0';
2077 from = mutt_default_from ();
2079 rfc822_qualify(from, mutt_fqdn(1));
2081 rfc2047_encode_adrlist(from, "Resent-From");
2082 if (mutt_addrlist_to_idna (from, &err)) {
2083 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2086 rfc822_addrcat(resent_from, sizeof(resent_from), from, 0);
2089 unset_option (OPTNEWSSEND);
2092 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2094 address_list_wipe(&from);
2099 static void set_noconv_flags (BODY * b, short flag)
2101 for (; b; b = b->next) {
2102 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2103 set_noconv_flags (b->parts, flag);
2104 else if (b->type == TYPETEXT && b->noconv) {
2105 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2110 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2111 int post, char *fcc)
2115 char tempfile[_POSIX_PATH_MAX];
2116 FILE *tempfp = NULL;
2120 set_noconv_flags (hdr->content, 1);
2122 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2126 /* We need to add a Content-Length field to avoid problems where a line in
2127 * the message body begins with "From "
2129 if (f.magic == M_MMDF || f.magic == M_MBOX) {
2130 tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2132 mutt_error(_("Could not create temporary file"));
2133 mx_close_mailbox (&f, NULL);
2138 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2139 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2140 mx_close_mailbox (&f, NULL);
2144 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2145 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2147 mutt_write_rfc822_header(msg->fp, hdr->env, hdr->content, -post, 0);
2149 /* (postponment) if this was a reply of some sort, <msgid> contians the
2150 * Message-ID: of message replied to. Save it using a special X-Mutt-
2151 * header so it can be picked up if the message is recalled at a later
2152 * point in time. This will allow the message to be marked as replied if
2153 * the same mailbox is still open.
2156 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2158 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2159 * it can be picked up when the message is recalled
2162 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2163 fprintf (msg->fp, "Status: RO\n");
2165 /* (postponment) if the mail is to be signed or encrypted, save this info */
2166 if (post && (hdr->security & APPLICATION_PGP)) {
2167 fputs ("X-Mutt-PGP: ", msg->fp);
2168 if (hdr->security & ENCRYPT)
2169 fputc ('E', msg->fp);
2170 if (hdr->security & SIGN) {
2171 fputc ('S', msg->fp);
2172 if (PgpSignAs && *PgpSignAs)
2173 fprintf (msg->fp, "<%s>", PgpSignAs);
2175 if (hdr->security & INLINE)
2176 fputc ('I', msg->fp);
2177 fputc ('\n', msg->fp);
2180 /* (postponment) if the mail is to be signed or encrypted, save this info */
2181 if (post && (hdr->security & APPLICATION_SMIME)) {
2182 fputs ("X-Mutt-SMIME: ", msg->fp);
2183 if (hdr->security & ENCRYPT) {
2184 fputc ('E', msg->fp);
2185 if (SmimeCryptAlg && *SmimeCryptAlg)
2186 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2188 if (hdr->security & SIGN) {
2189 fputc ('S', msg->fp);
2190 if (SmimeDefaultKey && *SmimeDefaultKey)
2191 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2193 if (hdr->security & INLINE)
2194 fputc ('I', msg->fp);
2195 fputc ('\n', msg->fp);
2198 /* (postponement) if the mail is to be sent through a mixmaster
2199 * chain, save that information
2201 if (post && hdr->chain && hdr->chain) {
2204 fputs ("X-Mutt-Mix:", msg->fp);
2205 for (p = hdr->chain; p; p = p->next)
2206 fprintf (msg->fp, " %s", (char *) p->data);
2208 fputc ('\n', msg->fp);
2212 char sasha[LONG_STRING];
2215 mutt_write_mime_body (hdr->content, tempfp);
2217 /* make sure the last line ends with a newline. Emacs doesn't ensure
2218 * this will happen, and it can cause problems parsing the mailbox
2221 fseeko (tempfp, -1, 2);
2222 if (fgetc (tempfp) != '\n') {
2223 fseeko (tempfp, 0, 2);
2224 fputc ('\n', tempfp);
2228 if (ferror (tempfp)) {
2231 mx_commit_message (msg, &f); /* XXX - really? */
2232 mx_close_message (&msg);
2233 mx_close_mailbox (&f, NULL);
2237 /* count the number of lines */
2239 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2241 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2242 fprintf (msg->fp, "Lines: %d\n\n", lines);
2244 /* copy the body and clean up */
2246 r = mutt_copy_stream (tempfp, msg->fp);
2247 if (m_fclose(&tempfp) != 0)
2249 /* if there was an error, leave the temp version */
2253 fputc ('\n', msg->fp); /* finish off the header */
2254 r = mutt_write_mime_body (hdr->content, msg->fp);
2257 if (mx_commit_message (msg, &f) != 0)
2259 mx_close_message (&msg);
2260 mx_close_mailbox (&f, NULL);
2263 set_noconv_flags (hdr->content, 0);