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 (MTransport.sendmail_wait >= 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 (MTransport.sendmail_wait >= 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 /* sendmail_wait > 0: interrupt waitpid() after sendmail_wait seconds
1748 * sendmail_wait = 0: wait forever
1749 * sendmail_wait < 0: don't wait
1751 if (MTransport.sendmail_wait > 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 (MTransport.sendmail_wait);
1764 else if (MTransport.sendmail_wait < 0)
1765 _exit (0xff & EX_OK);
1767 if (waitpid (pid, &st, 0) > 0) {
1768 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1769 if (MTransport.sendmail_wait && st == (0xff & EX_OK)) {
1770 unlink (*tempfile); /* no longer needed */
1774 st = (MTransport.sendmail_wait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1775 if (MTransport.sendmail_wait > 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 && MTransport.use_8bitmime)
1880 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1882 if (MTransport.use_envelope_from) {
1883 address_t *f = MTransport.envelope_from_address;
1884 if (!f && from && !from->next)
1887 args = add_option (args, &argslen, &argsmax, "-f");
1888 args = add_args (args, &argslen, &argsmax, f);
1891 if (MTransport.dsn_notify) {
1892 args = add_option (args, &argslen, &argsmax, "-N");
1893 args = add_option (args, &argslen, &argsmax, MTransport.dsn_notify);
1895 if (MTransport.dsn_return) {
1896 args = add_option (args, &argslen, &argsmax, "-R");
1897 args = add_option (args, &argslen, &argsmax, MTransport.dsn_return);
1899 args = add_option (args, &argslen, &argsmax, "--");
1900 args = add_args (args, &argslen, &argsmax, to);
1901 args = add_args (args, &argslen, &argsmax, cc);
1902 args = add_args (args, &argslen, &argsmax, bcc);
1907 if (argslen >= argsmax)
1908 p_realloc(&args, ++argsmax);
1910 args[argslen++] = NULL;
1912 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1914 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1919 if (!stat(childout, &st) && st.st_size > 0)
1920 mutt_do_pager(_("Output of the delivery process"), childout, 0,
1928 p_delete(&childout);
1932 if (i == (EX_OK & 0xff))
1934 else if (i == S_BKG)
1941 int mutt_invoke_mta (address_t * from, /* the sender */
1942 address_t * to, address_t * cc, address_t * bcc, /* recips */
1943 const char *msg, /* file containing message */
1945 { /* message contains 8bit chars */
1948 if (!option (OPTNEWSSEND))
1951 return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
1954 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
1957 /* For postponing (!final) do the necessary encodings only */
1958 void mutt_prepare_envelope (ENVELOPE * env, int final)
1961 if (env->bcc && !(env->to || env->cc)) {
1962 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1963 * recipients if there is no To: or Cc: field, so attempt to suppress
1964 * it by using an empty To: field.
1966 env->to = address_new();
1968 env->to->next = address_new();
1969 env->to->mailbox = m_strdup("undisclosed-recipients");
1972 mutt_set_followup_to(env);
1974 if (!env->message_id && !m_strisempty(MsgIdFormat))
1975 env->message_id = mutt_gen_msgid();
1978 /* Take care of 8-bit => 7-bit conversion. */
1979 rfc2047_encode_adrlist(env->to, "To");
1980 rfc2047_encode_adrlist(env->cc, "Cc");
1981 rfc2047_encode_adrlist(env->bcc, "Bcc");
1982 rfc2047_encode_adrlist(env->from, "From");
1983 rfc2047_encode_adrlist(env->mail_followup_to, "Mail-Followup-To");
1984 rfc2047_encode_adrlist(env->reply_to, "Reply-To");
1988 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
1991 rfc2047_encode_string (&env->subject);
1993 encode_headers (env->userhdrs);
1996 void mutt_unprepare_envelope (ENVELOPE * env)
1998 string_list_t *item;
2000 for (item = env->userhdrs; item; item = item->next)
2001 rfc2047_decode(&item->data);
2003 address_list_wipe(&env->mail_followup_to);
2005 /* back conversions */
2006 rfc2047_decode_adrlist(env->to);
2007 rfc2047_decode_adrlist(env->cc);
2008 rfc2047_decode_adrlist(env->bcc);
2009 rfc2047_decode_adrlist(env->from);
2010 rfc2047_decode_adrlist(env->reply_to);
2011 rfc2047_decode(&env->subject);
2014 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
2015 const char *resent_from, address_t * env_from)
2019 char date[STRING], tempfile[_POSIX_PATH_MAX];
2020 MESSAGE *msg = NULL;
2023 /* Try to bounce each message out, aborting if we get any failures. */
2024 for (i = 0; i < Context->msgcount; i++)
2025 if (Context->hdrs[i]->tagged)
2027 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2032 /* If we failed to open a message, return with error */
2033 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2039 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2041 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2043 if (!option (OPTBOUNCEDELIVERED))
2044 ch_flags |= CH_WEED_DELIVERED;
2046 fseeko (fp, h->offset, 0);
2047 fprintf (f, "Resent-From: %s", resent_from);
2048 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2049 if (!m_strisempty(MsgIdFormat))
2050 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2051 fputs ("Resent-To: ", f);
2052 mutt_write_address_list (to, f, 11, 0);
2053 mutt_copy_header (fp, h, f, ch_flags, NULL);
2055 mutt_copy_bytes (fp, f, h->content->length);
2058 ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
2059 h->content->encoding == ENC8BIT);
2063 mx_close_message (&msg);
2068 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2071 char resent_from[STRING];
2075 resent_from[0] = '\0';
2076 from = mutt_default_from ();
2078 rfc822_qualify(from, mutt_fqdn(1));
2080 rfc2047_encode_adrlist(from, "Resent-From");
2081 if (mutt_addrlist_to_idna (from, &err)) {
2082 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2085 rfc822_addrcat(resent_from, sizeof(resent_from), from, 0);
2088 unset_option (OPTNEWSSEND);
2091 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2093 address_list_wipe(&from);
2098 static void set_noconv_flags (BODY * b, short flag)
2100 for (; b; b = b->next) {
2101 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2102 set_noconv_flags (b->parts, flag);
2103 else if (b->type == TYPETEXT && b->noconv) {
2104 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2109 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2110 int post, char *fcc)
2114 char tempfile[_POSIX_PATH_MAX];
2115 FILE *tempfp = NULL;
2119 set_noconv_flags (hdr->content, 1);
2121 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2125 /* We need to add a Content-Length field to avoid problems where a line in
2126 * the message body begins with "From "
2128 if (f.magic == M_MMDF || f.magic == M_MBOX) {
2129 tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2131 mutt_error(_("Could not create temporary file"));
2132 mx_close_mailbox (&f, NULL);
2137 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2138 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2139 mx_close_mailbox (&f, NULL);
2143 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2144 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2146 mutt_write_rfc822_header(msg->fp, hdr->env, hdr->content, -post, 0);
2148 /* (postponment) if this was a reply of some sort, <msgid> contians the
2149 * Message-ID: of message replied to. Save it using a special X-Mutt-
2150 * header so it can be picked up if the message is recalled at a later
2151 * point in time. This will allow the message to be marked as replied if
2152 * the same mailbox is still open.
2155 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2157 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2158 * it can be picked up when the message is recalled
2161 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2162 fprintf (msg->fp, "Status: RO\n");
2164 /* (postponment) if the mail is to be signed or encrypted, save this info */
2165 if (post && (hdr->security & APPLICATION_PGP)) {
2166 fputs ("X-Mutt-PGP: ", msg->fp);
2167 if (hdr->security & ENCRYPT)
2168 fputc ('E', msg->fp);
2169 if (hdr->security & SIGN) {
2170 fputc ('S', msg->fp);
2171 if (PgpSignAs && *PgpSignAs)
2172 fprintf (msg->fp, "<%s>", PgpSignAs);
2174 if (hdr->security & INLINE)
2175 fputc ('I', msg->fp);
2176 fputc ('\n', msg->fp);
2179 /* (postponment) if the mail is to be signed or encrypted, save this info */
2180 if (post && (hdr->security & APPLICATION_SMIME)) {
2181 fputs ("X-Mutt-SMIME: ", msg->fp);
2182 if (hdr->security & ENCRYPT) {
2183 fputc ('E', msg->fp);
2184 if (SmimeCryptAlg && *SmimeCryptAlg)
2185 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2187 if (hdr->security & SIGN) {
2188 fputc ('S', msg->fp);
2189 if (SmimeDefaultKey && *SmimeDefaultKey)
2190 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2192 if (hdr->security & INLINE)
2193 fputc ('I', msg->fp);
2194 fputc ('\n', msg->fp);
2197 /* (postponement) if the mail is to be sent through a mixmaster
2198 * chain, save that information
2200 if (post && hdr->chain && hdr->chain) {
2203 fputs ("X-Mutt-Mix:", msg->fp);
2204 for (p = hdr->chain; p; p = p->next)
2205 fprintf (msg->fp, " %s", (char *) p->data);
2207 fputc ('\n', msg->fp);
2211 char sasha[LONG_STRING];
2214 mutt_write_mime_body (hdr->content, tempfp);
2216 /* make sure the last line ends with a newline. Emacs doesn't ensure
2217 * this will happen, and it can cause problems parsing the mailbox
2220 fseeko (tempfp, -1, 2);
2221 if (fgetc (tempfp) != '\n') {
2222 fseeko (tempfp, 0, 2);
2223 fputc ('\n', tempfp);
2227 if (ferror (tempfp)) {
2230 mx_commit_message (msg, &f); /* XXX - really? */
2231 mx_close_message (&msg);
2232 mx_close_mailbox (&f, NULL);
2236 /* count the number of lines */
2238 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2240 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2241 fprintf (msg->fp, "Lines: %d\n\n", lines);
2243 /* copy the body and clean up */
2245 r = mutt_copy_stream (tempfp, msg->fp);
2246 if (m_fclose(&tempfp) != 0)
2248 /* if there was an error, leave the temp version */
2252 fputc ('\n', msg->fp); /* finish off the header */
2253 r = mutt_write_mime_body (hdr->content, msg->fp);
2256 if (mx_commit_message (msg, &f) != 0)
2258 mx_close_message (&msg);
2259 mx_close_mailbox (&f, NULL);
2262 set_noconv_flags (hdr->content, 0);