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_pager(_("Output of the delivery process"), childout, 0, NULL);
1917 p_delete(&childout);
1921 if (i == (EX_OK & 0xff))
1923 else if (i == S_BKG)
1930 int mutt_invoke_mta (address_t * from, /* the sender */
1931 address_t * to, address_t * cc, address_t * bcc, /* recips */
1932 const char *msg, /* file containing message */
1934 { /* message contains 8bit chars */
1937 if (!option (OPTNEWSSEND))
1940 return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
1943 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
1946 /* For postponing (!final) do the necessary encodings only */
1947 void mutt_prepare_envelope (ENVELOPE * env, int final)
1950 if (env->bcc && !(env->to || env->cc)) {
1951 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1952 * recipients if there is no To: or Cc: field, so attempt to suppress
1953 * it by using an empty To: field.
1955 env->to = address_new();
1957 env->to->next = address_new();
1958 env->to->mailbox = m_strdup("undisclosed-recipients");
1961 mutt_set_followup_to(env);
1963 if (!env->message_id && !m_strisempty(MsgIdFormat))
1964 env->message_id = mutt_gen_msgid();
1967 /* Take care of 8-bit => 7-bit conversion. */
1968 rfc2047_encode_adrlist(env->to, "To");
1969 rfc2047_encode_adrlist(env->cc, "Cc");
1970 rfc2047_encode_adrlist(env->bcc, "Bcc");
1971 rfc2047_encode_adrlist(env->from, "From");
1972 rfc2047_encode_adrlist(env->mail_followup_to, "Mail-Followup-To");
1973 rfc2047_encode_adrlist(env->reply_to, "Reply-To");
1976 rfc2047_encode_string (&env->subject);
1977 encode_headers (env->userhdrs);
1980 void mutt_unprepare_envelope (ENVELOPE * env)
1982 string_list_t *item;
1984 for (item = env->userhdrs; item; item = item->next)
1985 rfc2047_decode(&item->data);
1987 address_list_wipe(&env->mail_followup_to);
1989 /* back conversions */
1990 rfc2047_decode_adrlist(env->to);
1991 rfc2047_decode_adrlist(env->cc);
1992 rfc2047_decode_adrlist(env->bcc);
1993 rfc2047_decode_adrlist(env->from);
1994 rfc2047_decode_adrlist(env->reply_to);
1995 rfc2047_decode(&env->subject);
1998 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
1999 const char *resent_from, address_t * env_from)
2003 char date[STRING], tempfile[_POSIX_PATH_MAX];
2004 MESSAGE *msg = NULL;
2007 /* Try to bounce each message out, aborting if we get any failures. */
2008 for (i = 0; i < Context->msgcount; i++)
2009 if (Context->hdrs[i]->tagged)
2011 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2016 /* If we failed to open a message, return with error */
2017 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2023 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2025 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2027 if (!option (OPTBOUNCEDELIVERED))
2028 ch_flags |= CH_WEED_DELIVERED;
2030 fseeko (fp, h->offset, 0);
2031 fprintf (f, "Resent-From: %s", resent_from);
2032 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2033 if (!m_strisempty(MsgIdFormat))
2034 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2035 fputs ("Resent-To: ", f);
2036 mutt_write_address_list (to, f, 11, 0);
2037 mutt_copy_header (fp, h, f, ch_flags, NULL);
2039 mutt_copy_bytes (fp, f, h->content->length);
2042 ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
2043 h->content->encoding == ENC8BIT);
2047 mx_close_message (&msg);
2052 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2055 char resent_from[STRING];
2059 resent_from[0] = '\0';
2060 from = mutt_default_from ();
2062 rfc822_qualify(from, mutt_fqdn(1));
2064 rfc2047_encode_adrlist(from, "Resent-From");
2065 if (mutt_addrlist_to_idna (from, &err)) {
2066 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2069 rfc822_addrcat(resent_from, sizeof(resent_from), from, 0);
2072 unset_option (OPTNEWSSEND);
2075 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2077 address_list_wipe(&from);
2082 static void set_noconv_flags (BODY * b, short flag)
2084 for (; b; b = b->next) {
2085 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2086 set_noconv_flags (b->parts, flag);
2087 else if (b->type == TYPETEXT && b->noconv) {
2088 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2093 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2094 int post, char *fcc)
2098 char tempfile[_POSIX_PATH_MAX];
2099 FILE *tempfp = NULL;
2103 set_noconv_flags (hdr->content, 1);
2105 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2109 /* We need to add a Content-Length field to avoid problems where a line in
2110 * the message body begins with "From "
2112 if (f.magic == M_MMDF || f.magic == M_MBOX) {
2113 tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2115 mutt_error(_("Could not create temporary file"));
2116 mx_close_mailbox (&f, NULL);
2121 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2122 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2123 mx_close_mailbox (&f, NULL);
2127 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2128 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2130 mutt_write_rfc822_header(msg->fp, hdr->env, hdr->content, -post, 0);
2132 /* (postponment) if this was a reply of some sort, <msgid> contians the
2133 * Message-ID: of message replied to. Save it using a special X-Mutt-
2134 * header so it can be picked up if the message is recalled at a later
2135 * point in time. This will allow the message to be marked as replied if
2136 * the same mailbox is still open.
2139 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2141 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2142 * it can be picked up when the message is recalled
2145 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2146 fprintf (msg->fp, "Status: RO\n");
2148 /* (postponment) if the mail is to be signed or encrypted, save this info */
2149 if (post && (hdr->security & APPLICATION_PGP)) {
2150 fputs ("X-Mutt-PGP: ", msg->fp);
2151 if (hdr->security & ENCRYPT)
2152 fputc ('E', msg->fp);
2153 if (hdr->security & SIGN) {
2154 fputc ('S', msg->fp);
2155 if (PgpSignAs && *PgpSignAs)
2156 fprintf (msg->fp, "<%s>", PgpSignAs);
2158 if (hdr->security & INLINE)
2159 fputc ('I', msg->fp);
2160 fputc ('\n', msg->fp);
2163 /* (postponment) if the mail is to be signed or encrypted, save this info */
2164 if (post && (hdr->security & APPLICATION_SMIME)) {
2165 fputs ("X-Mutt-SMIME: ", msg->fp);
2166 if (hdr->security & ENCRYPT) {
2167 fputc ('E', msg->fp);
2168 if (SmimeCryptAlg && *SmimeCryptAlg)
2169 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2171 if (hdr->security & SIGN) {
2172 fputc ('S', msg->fp);
2173 if (SmimeDefaultKey && *SmimeDefaultKey)
2174 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2176 if (hdr->security & INLINE)
2177 fputc ('I', msg->fp);
2178 fputc ('\n', msg->fp);
2181 /* (postponement) if the mail is to be sent through a mixmaster
2182 * chain, save that information
2184 if (post && hdr->chain && hdr->chain) {
2187 fputs ("X-Mutt-Mix:", msg->fp);
2188 for (p = hdr->chain; p; p = p->next)
2189 fprintf (msg->fp, " %s", (char *) p->data);
2191 fputc ('\n', msg->fp);
2195 char sasha[LONG_STRING];
2198 mutt_write_mime_body (hdr->content, tempfp);
2200 /* make sure the last line ends with a newline. Emacs doesn't ensure
2201 * this will happen, and it can cause problems parsing the mailbox
2204 fseeko (tempfp, -1, 2);
2205 if (fgetc (tempfp) != '\n') {
2206 fseeko (tempfp, 0, 2);
2207 fputc ('\n', tempfp);
2211 if (ferror (tempfp)) {
2214 mx_commit_message (msg, &f); /* XXX - really? */
2215 mx_close_message (&msg);
2216 mx_close_mailbox (&f, NULL);
2220 /* count the number of lines */
2222 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2224 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2225 fprintf (msg->fp, "Lines: %d\n\n", lines);
2227 /* copy the body and clean up */
2229 r = mutt_copy_stream (tempfp, msg->fp);
2230 if (m_fclose(&tempfp) != 0)
2232 /* if there was an error, leave the temp version */
2236 fputc ('\n', msg->fp); /* finish off the header */
2237 r = mutt_write_mime_body (hdr->content, msg->fp);
2240 if (mx_commit_message (msg, &f) != 0)
2242 mx_close_message (&msg);
2243 mx_close_mailbox (&f, NULL);
2246 set_noconv_flags (hdr->content, 0);