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 "mutt_libesmtp.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)
539 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
542 ssize_t ibl, obl, ubl, ubl1, n, ret;
545 CONTENT_STATE *states;
548 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
549 if (cd1 == MUTT_ICONV_ERROR)
552 cd = p_new(iconv_t, ncodes);
553 score = p_new(ssize_t, ncodes);
554 states = p_new(CONTENT_STATE, ncodes);
555 infos = p_new(CONTENT, ncodes);
557 for (i = 0; i < ncodes; i++)
558 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
559 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
561 /* Special case for conversion to UTF-8 */
562 cd[i] = MUTT_ICONV_ERROR, score[i] = -1;
568 /* Try to fill input buffer */
569 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
572 /* Convert to UTF-8 */
574 ob = bufu, obl = sizeof (bufu);
575 n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
576 if (n == -1 && ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
582 /* Convert from UTF-8 */
583 for (i = 0; i < ncodes; i++)
584 if (cd[i] != MUTT_ICONV_ERROR && score[i] != -1) {
585 ub = bufu, ubl = ubl1;
586 ob = bufo, obl = sizeof (bufo);
587 n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
593 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
596 else if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1)
597 /* Special case for conversion to UTF-8 */
598 update_content_info (&infos[i], &states[i], bufu, ubl1);
601 /* Save unused input */
602 memmove (bufi, ib, ibl);
603 else if (!ubl1 && ib < bufi + sizeof (bufi)) {
610 /* Find best score */
612 for (i = 0; i < ncodes; i++) {
613 if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1) {
614 /* Special case for conversion to UTF-8 */
619 else if (cd[i] == MUTT_ICONV_ERROR || score[i] == -1)
621 else if (ret == -1 || score[i] < ret) {
629 memcpy (info, &infos[*tocode], sizeof (CONTENT));
630 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
634 for (i = 0; i < ncodes; i++)
635 if (cd[i] != MUTT_ICONV_ERROR)
647 #endif /* !HAVE_ICONV */
651 * Find the first of the fromcodes that gives a valid conversion and
652 * the best charset conversion of the file into one of the tocodes. If
653 * successful, set *fromcode and *tocode to dynamically allocated
654 * strings, set CONTENT *info, and return the number of characters
655 * converted inexactly. If no conversion was possible, return -1.
657 * Both fromcodes and tocodes may be colon-separated lists of charsets.
658 * However, if fromcode is zero then fromcodes is assumed to be the
659 * name of a single charset even if it contains a colon.
661 static ssize_t convert_file_from_to (FILE * file,
662 const char *fromcodes,
663 const char *tocodes, char **fromcode,
664 char **tocode, CONTENT * info)
672 /* Count the tocodes */
674 for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
675 if ((c1 = strchr (c, ':')) == c)
681 tcode = p_new(char *, ncodes);
682 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
683 if ((c1 = strchr (c, ':')) == c)
685 tcode[i] = m_substrdup(c, c1);
690 /* Try each fromcode in turn */
691 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
692 if ((c1 = strchr (c, ':')) == c)
694 fcode = m_substrdup(c, c1);
696 ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
708 /* There is only one fromcode */
709 ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
718 for (i = 0; i < ncodes; i++)
727 * Analyze the contents of a file to determine which MIME encoding to use.
728 * Also set the body charset, sometimes, or not.
730 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
735 char *fromcode = NULL;
746 if (stat (fname, &sb) == -1) {
747 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
751 if (!S_ISREG (sb.st_mode)) {
752 mutt_error (_("%s isn't a regular file."), fname);
756 if ((fp = fopen (fname, "r")) == NULL) {
760 info = p_new(CONTENT, 1);
763 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
764 const char *chs = parameter_getval(b->parameter, "charset");
765 char *fchs = b->use_disp && !m_strisempty(MCharset.file_charset)
766 ? FileCharset : MCharset.charset;
767 if (MCharset.charset && (chs || MCharset.send_charset) &&
768 convert_file_from_to (fp, fchs, chs ? chs : MCharset.send_charset,
769 &fromcode, &tocode, info) != -1) {
771 charset_canonicalize (chsbuf, sizeof (chsbuf), tocode);
772 parameter_setval(&b->parameter, "charset", chsbuf);
774 b->file_charset = fromcode;
782 while ((r = fread (buffer, 1, sizeof (buffer), fp)))
783 update_content_info (info, &state, buffer, r);
784 update_content_info (info, &state, 0, 0);
788 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
789 parameter_setval(&b->parameter, "charset",
790 (!info->hibin ? "us-ascii"
791 : MCharset.charset && !charset_is_us_ascii(MCharset.charset)
792 ? MCharset.charset : "unknown-8bit"));
797 /* Given a file with path ``s'', see if there is a registered MIME type.
798 * returns the major MIME type, and copies the subtype to ``d''. First look
799 * for ~/.mime.types, then look in a system mime.types if we can find one.
800 * The longest match is used so that we can match `ps.gz' when `gz' also
804 int mutt_lookup_mime_type (BODY * att, const char *path)
808 char buf[LONG_STRING];
809 char subtype[STRING], xtype[STRING];
811 int szf, sze, cur_sze;
819 szf = m_strlen(path);
821 for (count = 0; count < 4; count++) {
823 * can't use strtok() because we use it in an inner loop below, so use
824 * a switch statement here instead.
828 snprintf(buf, sizeof (buf), "%s/.mime.types", NONULL(MCore.homedir));
831 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
834 m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
837 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
840 goto bye; /* shouldn't happen */
843 if ((f = fopen (buf, "r")) != NULL) {
844 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
845 /* weed out any comments */
846 if ((p = strchr (buf, '#')))
849 /* remove any leading space. */
850 ct = vskipspaces(buf);
852 /* position on the next field in this line */
853 if ((p = strpbrk (ct, " \t")) == NULL)
858 /* cycle through the file extensions */
859 while ((p = strtok (p, " \t\n"))) {
861 if ((sze > cur_sze) && (szf >= sze) &&
862 (m_strcasecmp(path + szf - sze, p) == 0
863 || ascii_strcasecmp (path + szf - sze, p) == 0)
864 && (szf == sze || path[szf - sze - 1] == '.'))
866 /* get the content-type */
868 if ((p = strchr (ct, '/')) == NULL) {
869 /* malformed line, just skip it. */
874 for (q = p; *q && !ISSPACE (*q); q++);
876 m_strncpy(subtype, sizeof(subtype), p, q - p);
878 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
879 m_strcpy(xtype, sizeof(xtype), ct);
892 if (type != TYPEOTHER || *xtype != '\0') {
894 m_strreplace(&att->subtype, subtype);
895 m_strreplace(&att->xtype, xtype);
901 void mutt_message_to_7bit (BODY * a, FILE * fp)
903 char temp[_POSIX_PATH_MAX];
909 if (!a->filename && fp)
911 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
912 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
917 if (stat (a->filename, &sb) == -1) {
918 mutt_perror ("stat");
921 a->length = sb.st_size;
924 fpout = m_tempfile(temp, sizeof(temp), NONULL(MCore.tmpdir), NULL);
926 mutt_error(_("Could not create temporary file"));
930 fseeko (fpin, a->offset, 0);
931 a->parts = mutt_parse_messageRFC822 (fpin, a);
933 transform_to_7bit (a->parts, fpin);
935 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
936 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
938 fputs ("MIME-Version: 1.0\n", fpout);
939 mutt_write_mime_header (a->parts, fpout);
941 mutt_write_mime_body (a->parts, fpout);
953 a->encoding = ENC7BIT;
954 a->d_filename = a->filename;
955 if (a->filename && a->unlink)
956 unlink (a->filename);
957 a->filename = m_strdup(temp);
959 if (stat (a->filename, &sb) == -1) {
960 mutt_perror ("stat");
963 a->length = sb.st_size;
964 body_list_wipe(&a->parts);
965 a->hdr->content = NULL;
968 static void transform_to_7bit (BODY * a, FILE * fpin)
970 char buff[_POSIX_PATH_MAX];
975 for (; a; a = a->next) {
976 if (a->type == TYPEMULTIPART) {
977 if (a->encoding != ENC7BIT)
978 a->encoding = ENC7BIT;
980 transform_to_7bit (a->parts, fpin);
982 else if (mutt_is_message_type(a)) {
983 mutt_message_to_7bit (a, fpin);
987 a->force_charset = 1;
989 s.fpout = m_tempfile(buff, sizeof(buff), NONULL(MCore.tmpdir), NULL);
991 mutt_error(_("Could not create temporary file"));
995 mutt_decode_attachment (a, &s);
997 a->d_filename = a->filename;
998 a->filename = m_strdup(buff);
1000 if (stat (a->filename, &sb) == -1) {
1001 mutt_perror ("stat");
1004 a->length = sb.st_size;
1006 mutt_update_encoding (a);
1007 if (a->encoding == ENC8BIT)
1008 a->encoding = ENCQUOTEDPRINTABLE;
1009 else if (a->encoding == ENCBINARY)
1010 a->encoding = ENCBASE64;
1015 /* determine which Content-Transfer-Encoding to use */
1016 static void mutt_set_encoding (BODY * b, CONTENT * info)
1018 char send_charset[STRING];
1020 if (b->type == TYPETEXT) {
1022 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1023 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1024 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1025 b->encoding = ENCQUOTEDPRINTABLE;
1026 else if (info->hibin)
1027 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1029 b->encoding = ENC7BIT;
1031 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1032 if (info->lobin || info->hibin) {
1033 if (option (OPTALLOW8BIT) && !info->lobin)
1034 b->encoding = ENC8BIT;
1036 mutt_message_to_7bit (b, NULL);
1039 b->encoding = ENC7BIT;
1041 else if (b->type == TYPEAPPLICATION
1042 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1043 b->encoding = ENC7BIT;
1046 /* Determine which encoding is smaller */
1047 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1048 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1049 b->encoding = ENCBASE64;
1051 b->encoding = ENCQUOTEDPRINTABLE;
1055 void mutt_stamp_attachment (BODY * a)
1057 a->stamp = time (NULL);
1060 /* Get a body's character set */
1062 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1066 if (b && b->type != TYPETEXT)
1069 p = b ? parameter_getval(b->parameter, "charset") : NULL;
1070 charset_canonicalize(d, dlen, p);
1075 /* Assumes called from send mode where BODY->filename points to actual file */
1076 void mutt_update_encoding (BODY * a)
1079 char chsbuff[STRING];
1081 /* override noconv when it's us-ascii */
1082 if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1085 if (!a->force_charset && !a->noconv)
1086 parameter_delval(&a->parameter, "charset");
1088 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1091 mutt_set_encoding (a, info);
1092 mutt_stamp_attachment (a);
1094 p_delete(&a->content);
1099 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1101 char buffer[LONG_STRING];
1104 int cmflags, chflags;
1105 int pgp = hdr->security;
1107 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1108 (hdr->security & ENCRYPT)) {
1111 fp = m_tempfile(buffer, sizeof(buffer), NONULL(MCore.tmpdir), NULL);
1116 body->type = TYPEMESSAGE;
1117 body->subtype = m_strdup("rfc822");
1118 body->filename = m_strdup(buffer);
1121 body->disposition = DISPINLINE;
1124 mutt_parse_mime_message (ctx, hdr);
1129 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1130 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1131 chflags |= CH_MIME | CH_TXTPLAIN;
1132 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1133 pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1135 else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1136 if (mutt_is_multipart_encrypted (hdr->content)) {
1137 chflags |= CH_MIME | CH_NONEWLINE;
1138 cmflags = M_CM_DECODE_PGP;
1141 else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1142 chflags |= CH_MIME | CH_TXTPLAIN;
1143 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1146 else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1147 chflags |= CH_MIME | CH_TXTPLAIN;
1148 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1149 pgp &= ~SMIMEENCRYPT;
1153 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1158 body->hdr = header_new();
1159 body->hdr->offset = 0;
1160 /* we don't need the user headers here */
1161 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1162 body->hdr->security = pgp;
1163 mutt_update_encoding (body);
1164 body->parts = body->hdr->content;
1171 BODY *mutt_make_file_attach (const char *path)
1177 att->filename = m_strdup(path);
1179 /* Attempt to determine the appropriate content-type based on the filename
1182 mutt_lookup_mime_type (att, path);
1184 if ((info = mutt_get_content_info (path, att)) == NULL) {
1185 body_list_wipe(&att);
1189 if (!att->subtype) {
1190 if (info->lobin == 0
1191 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1193 * Statistically speaking, there should be more than 10% "lobin"
1194 * chars if this is really a binary file...
1196 att->type = TYPETEXT;
1197 att->subtype = m_strdup("plain");
1199 att->type = TYPEAPPLICATION;
1200 att->subtype = m_strdup("octet-stream");
1204 mutt_update_encoding (att);
1208 static int get_toplevel_encoding (BODY * a)
1212 for (; a; a = a->next) {
1213 if (a->encoding == ENCBINARY)
1216 if (a->encoding == ENC8BIT)
1223 BODY *mutt_make_multipart (BODY * b)
1228 new->type = TYPEMULTIPART;
1229 new->subtype = m_strdup("mixed");
1230 new->encoding = get_toplevel_encoding (b);
1231 parameter_set_boundary(&new->parameter);
1233 new->disposition = DISPINLINE;
1239 /* remove the multipart body if it exists */
1240 BODY *mutt_remove_multipart (BODY * b)
1253 char *mutt_make_date (char *s, ssize_t len)
1255 time_t t = time (NULL);
1256 struct tm *l = localtime (&t);
1257 time_t tz = mutt_local_tz (t);
1261 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1262 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1263 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1264 (int) tz / 60, (int) abs (tz) % 60);
1268 /* wrapper around mutt_write_address() so we can handle very large
1269 recipient lists without needing a huge temporary buffer in memory */
1271 mutt_write_address_list(address_t *addr, FILE *fp, int linelen, int display)
1276 char buf[LONG_STRING];
1277 int len = rfc822_addrcpy(buf, ssizeof(buf), addr, display);
1280 if (linelen + len > 74) {
1282 linelen = 8; /* tab is usually about 8 spaces... */
1284 if (addr->mailbox) {
1294 if (!addr->group && addr->next && addr->next->mailbox) {
1304 /* need to write the list in reverse because they are stored in reverse order
1305 * when parsed to speed up threading
1307 void mutt_write_references(string_list_t *r, FILE *f)
1309 string_list_t *refs[10];
1312 p_clear(refs, countof(refs));
1313 for (i = 0; i < countof(refs) && r; r = r->next) {
1318 fprintf(f, " %s", refs[i]->data);
1322 static int edit_header(int mode, const char *s)
1325 int slen = m_strlen(s);
1327 if (mode != 1 || option(OPTXMAILTO))
1330 p = skipspaces(EditorHeaders);
1332 if (!ascii_strncasecmp(p, s, slen) && p[slen - 1] == ':')
1334 p = skipspaces(p + slen);
1340 /* Note: all RFC2047 encoding should be done outside of this routine, except
1341 * for the "real name." This will allow this routine to be used more than
1342 * once, if necessary.
1344 * Likewise, all IDN processing should happen outside of this routine.
1346 * mode == 1 => "lite" mode (used for edit_hdrs)
1347 * mode == 0 => normal mode. write full header + MIME headers
1348 * mode == -1 => write just the envelope info (used for postponing messages)
1350 * privacy != 0 => will omit any headers which may identify the user.
1351 * Output generated is suitable for being sent through
1352 * anonymous remailer chains.
1355 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1356 int mode, int privacy)
1358 char buffer[LONG_STRING];
1360 string_list_t *tmp = env->userhdrs;
1361 int has_agent = 0; /* user defined user-agent header field exists */
1364 if (!option (OPTNEWSSEND))
1366 if (mode == 0 && !privacy)
1367 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1369 /* OPTUSEFROM is not consulted here so that we can still write a From:
1370 * field if the user sets it with the `my_hdr' command
1372 if (env->from && !privacy) {
1374 rfc822_addrcat(buffer, sizeof(buffer), env->from, 0);
1375 fprintf (fp, "From: %s\n", buffer);
1380 mutt_write_address_list (env->to, fp, 4, 0);
1384 if (!option (OPTNEWSSEND))
1386 if (edit_header(mode, "To:"))
1387 fputs ("To:\n", fp);
1391 mutt_write_address_list (env->cc, fp, 4, 0);
1395 if (!option (OPTNEWSSEND))
1397 if (edit_header(mode, "Cc:"))
1398 fputs ("Cc:\n", fp);
1401 if (mode != 0 || option (OPTWRITEBCC)) {
1402 fputs ("Bcc: ", fp);
1403 mutt_write_address_list (env->bcc, fp, 5, 0);
1408 if (!option (OPTNEWSSEND))
1410 if (edit_header(mode, "Bcc:"))
1411 fputs ("Bcc:\n", fp);
1414 if (env->newsgroups)
1415 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1416 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Newsgroups:"))
1417 fputs ("Newsgroups:\n", fp);
1419 if (env->followup_to)
1420 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1421 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Followup-To:"))
1422 fputs ("Followup-To:\n", fp);
1424 if (env->x_comment_to)
1425 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1426 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO) &&
1427 edit_header(mode, "X-Comment-To:"))
1428 fputs ("X-Comment-To:\n", fp);
1432 fprintf (fp, "Subject: %s\n", env->subject);
1433 else if (mode == 1 && edit_header(mode, "Subject:"))
1434 fputs ("Subject:\n", fp);
1436 /* save message id if the user has set it */
1437 if (env->message_id && !privacy)
1438 fprintf (fp, "Message-ID: %s\n", env->message_id);
1440 if (env->reply_to) {
1441 fputs ("Reply-To: ", fp);
1442 mutt_write_address_list (env->reply_to, fp, 10, 0);
1444 else if (mode > 0 && edit_header(mode, "Reply-To:"))
1445 fputs ("Reply-To:\n", fp);
1447 if (env->mail_followup_to)
1449 if (!option (OPTNEWSSEND))
1452 fputs ("Mail-Followup-To: ", fp);
1453 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1457 if (env->references) {
1458 fputs ("References:", fp);
1459 mutt_write_references (env->references, fp);
1463 /* Add the MIME headers */
1464 fputs ("MIME-Version: 1.0\n", fp);
1465 mutt_write_mime_header (attach, fp);
1468 if (env->in_reply_to) {
1469 fputs ("In-Reply-To:", fp);
1470 mutt_write_references (env->in_reply_to, fp);
1474 /* Add any user defined headers */
1475 for (; tmp; tmp = tmp->next) {
1476 if ((p = strchr (tmp->data, ':'))) {
1477 p = vskipspaces(p + 1);
1479 continue; /* don't emit empty fields. */
1481 /* check to see if the user has overridden the user-agent field */
1482 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1488 fputs (tmp->data, fp);
1493 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1494 if (MCore.operating_system) {
1495 fprintf(fp, "User-Agent: %s (%s)\n", mutt_make_version(),
1496 MCore.operating_system);
1498 fprintf(fp, "User-Agent: %s\n", mutt_make_version());
1502 return (ferror (fp) == 0 ? 0 : -1);
1505 static void encode_headers (string_list_t * h)
1511 for (; h; h = h->next) {
1512 if (!(p = strchr (h->data, ':')))
1516 p = vskipspaces(p + 1);
1522 rfc2047_encode_string (&tmp);
1523 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1525 sprintf (h->data + i, ": %s", NONULL (tmp));
1531 const char *mutt_fqdn(short may_hide_host)
1535 if (MCore.hostname && MCore.hostname[0] != '@') {
1538 if (may_hide_host && option (OPTHIDDENHOST)) {
1539 if ((p = strchr(MCore.hostname, '.')))
1542 /* sanity check: don't hide the host if
1543 the fqdn is something like detebe.org. */
1545 if (!p || !(q = strchr(p, '.')))
1553 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1555 #define APPEND_FMT(fmt, arg) \
1557 int snlen = snprintf(buf, len, fmt, arg); \
1562 #define APPEND_BYTE(c) \
1576 static char MsgIdPfx = 'A';
1580 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1581 APPEND_BYTE((isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.');
1589 APPEND_FMT("%02d", tm->tm_mday);
1592 APPEND_FMT("%02d", tm->tm_hour);
1595 APPEND_FMT("%02d", tm->tm_mon + 1);
1598 APPEND_FMT("%02d", tm->tm_min);
1601 APPEND_FMT("%lo", (unsigned long)now);
1604 APPEND_FMT("%u", (unsigned int)getpid());
1607 APPEND_FMT("%c", MsgIdPfx);
1608 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1611 APPEND_FMT("%u", (unsigned int)rand());
1614 APPEND_FMT("%x", (unsigned int)rand());
1617 APPEND_FMT("%02d", tm->tm_sec);
1620 APPEND_FMT("%u", (unsigned int) now);
1623 APPEND_FMT("%x", (unsigned int) now);
1625 case 'Y': /* this will break in the year 10000 ;-) */
1626 APPEND_FMT("%04d", tm->tm_year + 1900);
1631 default: /* invalid formats are replaced by '.' */
1642 static char *mutt_gen_msgid (void)
1645 char localpart[STRING];
1648 if (!(fqdn = mutt_fqdn(0)))
1649 fqdn = NONULL(MCore.shorthost);
1651 mutt_gen_localpart(localpart, sizeof(localpart), MsgIdFormat);
1652 snprintf(buf, sizeof(buf), "<%s@%s>", localpart, fqdn);
1653 return m_strdup(buf);
1656 static RETSIGTYPE alarm_handler (int sig __attribute__ ((unused)))
1661 /* invoke sendmail in a subshell
1662 path (in) path to program to execute
1663 args (in) arguments to pass to program
1664 msg (in) temp file containing message to send
1665 tempfile (out) if sendmail is put in the background, this points
1666 to the temporary file containing the stdout of the
1669 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1675 mutt_block_signals_system ();
1678 /* we also don't want to be stopped right now */
1679 sigaddset (&set, SIGTSTP);
1680 sigprocmask (SIG_BLOCK, &set, NULL);
1682 if (MTransport.sendmail_wait >= 0) {
1683 char tmp[_POSIX_PATH_MAX];
1686 *tempfile = m_strdup(tmp);
1689 if ((pid = fork ()) == 0) {
1690 struct sigaction act, oldalrm;
1692 /* save parent's ID before setsid() */
1695 /* we want the delivery to continue even after the main process dies,
1696 * so we put ourselves into another session right away
1700 /* next we close all open files */
1701 for (fd = 0; fd < getdtablesize(); fd++)
1704 /* now the second fork() */
1705 if ((pid = fork ()) == 0) {
1706 /* "msg" will be opened as stdin */
1707 if (open (msg, O_RDONLY, 0) < 0) {
1713 if (MTransport.sendmail_wait >= 0) {
1714 /* *tempfile will be opened as stdout */
1715 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1718 /* redirect stderr to *tempfile too */
1722 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1724 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1728 execv (path, (char**)args);
1731 else if (pid == -1) {
1737 /* sendmail_wait > 0: interrupt waitpid() after sendmail_wait seconds
1738 * sendmail_wait = 0: wait forever
1739 * sendmail_wait < 0: don't wait
1741 if (MTransport.sendmail_wait > 0) {
1743 act.sa_handler = alarm_handler;
1745 /* need to make sure waitpid() is interrupted on SIGALRM */
1746 act.sa_flags = SA_INTERRUPT;
1750 sigemptyset (&act.sa_mask);
1751 sigaction (SIGALRM, &act, &oldalrm);
1752 alarm (MTransport.sendmail_wait);
1754 else if (MTransport.sendmail_wait < 0)
1755 _exit (0xff & EX_OK);
1757 if (waitpid (pid, &st, 0) > 0) {
1758 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1759 if (MTransport.sendmail_wait && st == (0xff & EX_OK)) {
1760 unlink (*tempfile); /* no longer needed */
1764 st = (MTransport.sendmail_wait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1765 if (MTransport.sendmail_wait > 0) {
1771 /* reset alarm; not really needed, but... */
1773 sigaction (SIGALRM, &oldalrm, NULL);
1775 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1776 /* the parent is already dead */
1784 sigprocmask (SIG_UNBLOCK, &set, NULL);
1786 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1787 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1789 st = S_ERR; /* error */
1791 mutt_unblock_signals_system (1);
1796 static const char **
1797 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1799 for (; addr; addr = addr->next) {
1800 /* weed out group mailboxes, since those are for display only */
1801 if (addr->mailbox && !addr->group) {
1802 if (*argslen == *argsmax)
1803 p_realloc(&args, *argsmax += 5);
1804 args[(*argslen)++] = addr->mailbox;
1810 static const char **
1811 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1813 if (*argslen == *argsmax) {
1814 p_realloc(&args, *argsmax += 5);
1816 args[(*argslen)++] = s;
1820 static int mutt_invoke_sendmail (address_t * from, /* the sender */
1821 address_t * to, address_t * cc, address_t * bcc, /* recips */
1822 const char *msg, /* file containing message */
1824 { /* message contains 8bit chars */
1825 char cmd[LONG_STRING];
1826 char *ps = NULL, *path = NULL, *childout = NULL;
1827 const char **args = NULL;
1828 ssize_t argslen = 0, argsmax = 0;
1832 if (option (OPTNEWSSEND)) {
1833 m_strformat(cmd, sizeof(cmd), 0, Inews, nntp_format_str, 0, 0);
1834 if (m_strisempty(cmd)) {
1835 i = nntp_post (msg);
1842 m_strcpy(cmd, sizeof(cmd), MTransport.sendmail);
1847 while ((ps = strtok(ps, " "))) {
1848 if (argslen == argsmax)
1849 p_realloc(&args, argsmax += 5);
1852 args[argslen++] = ps;
1854 path = m_strdup(ps);
1855 ps = strrchr (ps, '/');
1860 args[argslen++] = ps;
1867 if (!option (OPTNEWSSEND)) {
1869 if (eightbit && MTransport.use_8bitmime)
1870 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1872 if (MTransport.use_envelope_from) {
1873 address_t *f = MTransport.envelope_from_address;
1874 if (!f && from && !from->next)
1877 args = add_option (args, &argslen, &argsmax, "-f");
1878 args = add_args (args, &argslen, &argsmax, f);
1881 if (MTransport.dsn_notify) {
1882 args = add_option (args, &argslen, &argsmax, "-N");
1883 args = add_option (args, &argslen, &argsmax, MTransport.dsn_notify);
1885 if (MTransport.dsn_return) {
1886 args = add_option (args, &argslen, &argsmax, "-R");
1887 args = add_option (args, &argslen, &argsmax, MTransport.dsn_return);
1889 args = add_option (args, &argslen, &argsmax, "--");
1890 args = add_args (args, &argslen, &argsmax, to);
1891 args = add_args (args, &argslen, &argsmax, cc);
1892 args = add_args (args, &argslen, &argsmax, bcc);
1897 if (argslen >= argsmax)
1898 p_realloc(&args, ++argsmax);
1900 args[argslen++] = NULL;
1902 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1904 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1909 if (!stat(childout, &st) && st.st_size > 0)
1910 mutt_do_pager(_("Output of the delivery process"), childout, 0,
1918 p_delete(&childout);
1922 if (i == (EX_OK & 0xff))
1924 else if (i == S_BKG)
1931 int mutt_invoke_mta (address_t * from, /* the sender */
1932 address_t * to, address_t * cc, address_t * bcc, /* recips */
1933 const char *msg, /* file containing message */
1935 { /* message contains 8bit chars */
1938 if (!option (OPTNEWSSEND))
1941 return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
1944 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
1947 /* For postponing (!final) do the necessary encodings only */
1948 void mutt_prepare_envelope (ENVELOPE * env, int final)
1951 if (env->bcc && !(env->to || env->cc)) {
1952 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1953 * recipients if there is no To: or Cc: field, so attempt to suppress
1954 * it by using an empty To: field.
1956 env->to = address_new();
1958 env->to->next = address_new();
1959 env->to->mailbox = m_strdup("undisclosed-recipients");
1962 mutt_set_followup_to(env);
1964 if (!env->message_id && !m_strisempty(MsgIdFormat))
1965 env->message_id = mutt_gen_msgid();
1968 /* Take care of 8-bit => 7-bit conversion. */
1969 rfc2047_encode_adrlist(env->to, "To");
1970 rfc2047_encode_adrlist(env->cc, "Cc");
1971 rfc2047_encode_adrlist(env->bcc, "Bcc");
1972 rfc2047_encode_adrlist(env->from, "From");
1973 rfc2047_encode_adrlist(env->mail_followup_to, "Mail-Followup-To");
1974 rfc2047_encode_adrlist(env->reply_to, "Reply-To");
1977 rfc2047_encode_string (&env->subject);
1978 encode_headers (env->userhdrs);
1981 void mutt_unprepare_envelope (ENVELOPE * env)
1983 string_list_t *item;
1985 for (item = env->userhdrs; item; item = item->next)
1986 rfc2047_decode(&item->data);
1988 address_list_wipe(&env->mail_followup_to);
1990 /* back conversions */
1991 rfc2047_decode_adrlist(env->to);
1992 rfc2047_decode_adrlist(env->cc);
1993 rfc2047_decode_adrlist(env->bcc);
1994 rfc2047_decode_adrlist(env->from);
1995 rfc2047_decode_adrlist(env->reply_to);
1996 rfc2047_decode(&env->subject);
1999 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
2000 const char *resent_from, address_t * env_from)
2004 char date[STRING], tempfile[_POSIX_PATH_MAX];
2005 MESSAGE *msg = NULL;
2008 /* Try to bounce each message out, aborting if we get any failures. */
2009 for (i = 0; i < Context->msgcount; i++)
2010 if (Context->hdrs[i]->tagged)
2012 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2017 /* If we failed to open a message, return with error */
2018 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2024 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2026 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2028 if (!option (OPTBOUNCEDELIVERED))
2029 ch_flags |= CH_WEED_DELIVERED;
2031 fseeko (fp, h->offset, 0);
2032 fprintf (f, "Resent-From: %s", resent_from);
2033 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2034 if (!m_strisempty(MsgIdFormat))
2035 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2036 fputs ("Resent-To: ", f);
2037 mutt_write_address_list (to, f, 11, 0);
2038 mutt_copy_header (fp, h, f, ch_flags, NULL);
2040 mutt_copy_bytes (fp, f, h->content->length);
2043 ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
2044 h->content->encoding == ENC8BIT);
2048 mx_close_message (&msg);
2053 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2056 char resent_from[STRING];
2060 resent_from[0] = '\0';
2061 from = mutt_default_from ();
2063 rfc822_qualify(from, mutt_fqdn(1));
2065 rfc2047_encode_adrlist(from, "Resent-From");
2066 if (mutt_addrlist_to_idna (from, &err)) {
2067 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2070 rfc822_addrcat(resent_from, sizeof(resent_from), from, 0);
2073 unset_option (OPTNEWSSEND);
2076 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2078 address_list_wipe(&from);
2083 static void set_noconv_flags (BODY * b, short flag)
2085 for (; b; b = b->next) {
2086 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2087 set_noconv_flags (b->parts, flag);
2088 else if (b->type == TYPETEXT && b->noconv) {
2089 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2094 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2095 int post, char *fcc)
2099 char tempfile[_POSIX_PATH_MAX];
2100 FILE *tempfp = NULL;
2104 set_noconv_flags (hdr->content, 1);
2106 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2110 /* We need to add a Content-Length field to avoid problems where a line in
2111 * the message body begins with "From "
2113 if (f.magic == M_MMDF || f.magic == M_MBOX) {
2114 tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2116 mutt_error(_("Could not create temporary file"));
2117 mx_close_mailbox (&f, NULL);
2122 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2123 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2124 mx_close_mailbox (&f, NULL);
2128 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2129 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2131 mutt_write_rfc822_header(msg->fp, hdr->env, hdr->content, -post, 0);
2133 /* (postponment) if this was a reply of some sort, <msgid> contians the
2134 * Message-ID: of message replied to. Save it using a special X-Mutt-
2135 * header so it can be picked up if the message is recalled at a later
2136 * point in time. This will allow the message to be marked as replied if
2137 * the same mailbox is still open.
2140 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2142 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2143 * it can be picked up when the message is recalled
2146 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2147 fprintf (msg->fp, "Status: RO\n");
2149 /* (postponment) if the mail is to be signed or encrypted, save this info */
2150 if (post && (hdr->security & APPLICATION_PGP)) {
2151 fputs ("X-Mutt-PGP: ", msg->fp);
2152 if (hdr->security & ENCRYPT)
2153 fputc ('E', msg->fp);
2154 if (hdr->security & SIGN) {
2155 fputc ('S', msg->fp);
2156 if (PgpSignAs && *PgpSignAs)
2157 fprintf (msg->fp, "<%s>", PgpSignAs);
2159 if (hdr->security & INLINE)
2160 fputc ('I', msg->fp);
2161 fputc ('\n', msg->fp);
2164 /* (postponment) if the mail is to be signed or encrypted, save this info */
2165 if (post && (hdr->security & APPLICATION_SMIME)) {
2166 fputs ("X-Mutt-SMIME: ", msg->fp);
2167 if (hdr->security & ENCRYPT) {
2168 fputc ('E', msg->fp);
2169 if (SmimeCryptAlg && *SmimeCryptAlg)
2170 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2172 if (hdr->security & SIGN) {
2173 fputc ('S', msg->fp);
2174 if (SmimeDefaultKey && *SmimeDefaultKey)
2175 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2177 if (hdr->security & INLINE)
2178 fputc ('I', msg->fp);
2179 fputc ('\n', msg->fp);
2182 /* (postponement) if the mail is to be sent through a mixmaster
2183 * chain, save that information
2185 if (post && hdr->chain && hdr->chain) {
2188 fputs ("X-Mutt-Mix:", msg->fp);
2189 for (p = hdr->chain; p; p = p->next)
2190 fprintf (msg->fp, " %s", (char *) p->data);
2192 fputc ('\n', msg->fp);
2196 char sasha[LONG_STRING];
2199 mutt_write_mime_body (hdr->content, tempfp);
2201 /* make sure the last line ends with a newline. Emacs doesn't ensure
2202 * this will happen, and it can cause problems parsing the mailbox
2205 fseeko (tempfp, -1, 2);
2206 if (fgetc (tempfp) != '\n') {
2207 fseeko (tempfp, 0, 2);
2208 fputc ('\n', tempfp);
2212 if (ferror (tempfp)) {
2215 mx_commit_message (msg, &f); /* XXX - really? */
2216 mx_close_message (&msg);
2217 mx_close_mailbox (&f, NULL);
2221 /* count the number of lines */
2223 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2225 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2226 fprintf (msg->fp, "Lines: %d\n\n", lines);
2228 /* copy the body and clean up */
2230 r = mutt_copy_stream (tempfp, msg->fp);
2231 if (m_fclose(&tempfp) != 0)
2233 /* if there was an error, leave the temp version */
2237 fputc ('\n', msg->fp); /* finish off the header */
2238 r = mutt_write_mime_body (hdr->content, msg->fp);
2241 if (mx_commit_message (msg, &f) != 0)
2243 mx_close_message (&msg);
2244 mx_close_mailbox (&f, NULL);
2247 set_noconv_flags (hdr->content, 0);