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.
12 #include <lib-lib/lib-lib.h>
15 #include <sys/utsname.h>
17 #include <lib-sys/exit.h>
18 #include <lib-sys/mutt_signal.h>
19 #include <lib-mime/mime.h>
20 #include <lib-ui/curses.h>
22 #include <lib-crypt/crypt.h>
26 #include "recvattach.h"
31 #include "mutt_idna.h"
34 # include "mutt_libesmtp.h"
35 #endif /* USE_LIBESMTP */
41 #ifdef HAVE_SYSEXITS_H
43 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
47 /* If you are debugging this file, comment out the following line. */
56 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
58 static void transform_to_7bit (BODY * a, FILE * fpin);
60 static void encode_quoted (fgetconv_t * fc, FILE * fout, int istext)
63 char line[77], savechar;
65 while ((c = fgetconv (fc)) != EOF) {
66 /* Wrap the line if needed. */
67 if (linelen == 76 && ((istext && c != '\n') || !istext)) {
68 /* If the last character is "quoted", then be sure to move all three
69 * characters to the next line. Otherwise, just move the last
72 if (line[linelen - 3] == '=') {
73 line[linelen - 3] = 0;
78 line[1] = line[linelen - 2];
79 line[2] = line[linelen - 1];
83 savechar = line[linelen - 1];
84 line[linelen - 1] = '=';
93 /* Escape lines that begin with/only contain "the message separator". */
94 if (linelen == 4 && !m_strncmp("From", line, 4)) {
95 m_strcpy(line, sizeof(line), "=46rom");
98 else if (linelen == 4 && !m_strncmp("from", line, 4)) {
99 m_strcpy(line, sizeof(line), "=66rom");
102 else if (linelen == 1 && line[0] == '.') {
103 m_strcpy(line, sizeof(line), "=2E");
108 if (c == '\n' && istext) {
109 /* Check to make sure there is no trailing space on this line. */
111 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
113 sprintf (line + linelen - 1, "=%2.2X",
114 (unsigned char) line[linelen - 1]);
118 savechar = line[linelen - 1];
120 line[linelen - 1] = '=';
123 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
133 else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
134 /* Check to make sure there is enough room for the quoted character.
135 * If not, wrap to the next line.
138 line[linelen++] = '=';
144 sprintf (line + linelen, "=%2.2X", (unsigned char) c);
148 /* Don't worry about wrapping the line here. That will happen during
149 * the next iteration when I'll also know what the next character is.
155 /* Take care of anything left in the buffer */
157 if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
158 /* take care of trailing whitespace */
160 sprintf (line + linelen - 1, "=%2.2X",
161 (unsigned char) line[linelen - 1]);
163 savechar = line[linelen - 1];
164 line[linelen - 1] = '=';
168 sprintf (line, "=%2.2X", (unsigned char) savechar);
177 static char b64_buffer[3];
178 static short b64_num;
179 static short b64_linelen;
181 static void b64_flush (FILE * fout)
188 if (b64_linelen >= 72) {
193 for (i = b64_num; i < 3; i++)
194 b64_buffer[i] = '\0';
196 fputc(__m_b64chars[(b64_buffer[0] >> 2) & 0x3f], fout);
199 [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
204 [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
208 fputc (__m_b64chars[b64_buffer[2] & 0x3f], fout);
213 while (b64_linelen % 4) {
222 static void b64_putc (char c, FILE * fout)
227 b64_buffer[b64_num++] = c;
231 static void encode_base64 (fgetconv_t * fc, FILE * fout, int istext)
235 b64_num = b64_linelen = 0;
237 while ((ch = fgetconv (fc)) != EOF) {
238 if (istext && ch == '\n' && ch1 != '\r')
239 b64_putc ('\r', fout);
247 static void encode_8bit (fgetconv_t * fc, FILE * fout, int istext)
251 while ((ch = fgetconv (fc)) != EOF)
256 int mutt_write_mime_header (BODY * a, FILE * f)
265 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
270 len = 25 + m_strlen(a->subtype); /* approximate len. of content-type */
272 for (p = a->parameter; p; p = p->next) {
281 tmp = m_strdup(p->value);
282 encode = rfc2231_encode_string (&tmp);
283 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
285 /* Dirty hack to make messages readable by Outlook Express
286 * for the Mac: force quotes around the boundary parameter
287 * even when they aren't needed.
290 if (!ascii_strcasecmp (p->attribute, "boundary")
291 && !strcmp (buffer, tmp))
292 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
296 tmplen = m_strlen(buffer) + m_strlen(p->attribute) + 1;
298 if (len + tmplen + 2 > 76) {
307 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
315 fprintf (f, "Content-Description: %s\n", a->description);
317 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
320 if (!(fn = a->d_filename))
326 /* Strip off the leading path... */
327 if ((t = strrchr (fn, '/')))
334 encode = rfc2231_encode_string (&tmp);
335 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
337 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
343 if (a->encoding != ENC7BIT)
344 fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
346 /* Do NOT add the terminator here!!! */
347 return (ferror (f) ? -1 : 0);
350 int mutt_write_mime_body (BODY * a, FILE * f)
353 char boundary[SHORT_STRING];
354 char send_charset[SHORT_STRING];
359 if (a->type == TYPEMULTIPART) {
360 /* First, find the boundary to use */
361 if (!(p = parameter_getval(a->parameter, "boundary"))) {
362 mutt_error _("No boundary parameter found! [report this error]");
366 m_strcpy(boundary, sizeof(boundary), p);
368 for (t = a->parts; t; t = t->next) {
369 fprintf (f, "\n--%s\n", boundary);
370 if (mutt_write_mime_header (t, f) == -1)
373 if (mutt_write_mime_body (t, f) == -1)
376 fprintf (f, "\n--%s--\n", boundary);
377 return (ferror (f) ? -1 : 0);
380 /* This is pretty gross, but it's the best solution for now... */
381 if (a->type == TYPEAPPLICATION && !m_strcmp(a->subtype, "pgp-encrypted")) {
382 fputs ("Version: 1\n", f);
386 if ((fpin = fopen (a->filename, "r")) == NULL) {
387 mutt_error (_("%s no longer exists!"), a->filename);
391 if (a->type == TYPETEXT && (!a->noconv))
392 fc = fgetconv_open (fpin, a->file_charset,
393 mutt_get_body_charset (send_charset,
394 sizeof (send_charset), a), 0);
396 fc = fgetconv_open (fpin, 0, 0, 0);
398 #define write_as_text_part(a) (mutt_is_text_part(a) || mutt_is_application_pgp(a))
399 if (a->encoding == ENCQUOTEDPRINTABLE)
400 encode_quoted (fc, f, write_as_text_part (a));
401 else if (a->encoding == ENCBASE64)
402 encode_base64 (fc, f, write_as_text_part (a));
403 else if (a->type == TYPETEXT && (!a->noconv))
404 encode_8bit (fc, f, write_as_text_part (a));
406 mutt_copy_stream (fpin, f);
407 #undef write_as_text_part
409 fgetconv_close (&fc);
412 return (ferror (f) ? -1 : 0);
424 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
428 int whitespace = s->whitespace;
430 int linelen = s->linelen;
431 int was_cr = s->was_cr;
433 if (!d) { /* This signals EOF */
436 if (linelen > info->linemax)
437 info->linemax = linelen;
442 for (; dlen; d++, dlen--) {
455 if (linelen > info->linemax)
456 info->linemax = linelen;
471 if (linelen > info->linemax)
472 info->linemax = linelen;
477 else if (ch == '\r') {
485 else if (ch == '\t' || ch == '\f') {
489 else if (ch < 32 || ch == 127)
493 if ((ch == 'F') || (ch == 'f'))
503 if (linelen == 2 && ch != 'r')
505 else if (linelen == 3 && ch != 'o')
507 else if (linelen == 4) {
520 if (ch != ' ' && ch != '\t')
525 s->whitespace = whitespace;
527 s->linelen = linelen;
533 * Find the best charset conversion of the file from fromcode into one
534 * of the tocodes. If successful, set *tocode and CONTENT *info and
535 * return the number of characters converted inexactly. If no
536 * conversion was possible, return -1.
538 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
539 * which would otherwise prevent us from knowing the number of inexact
540 * conversions. Where the candidate target charset is UTF-8 we avoid
541 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
542 * fails with some libraries.
544 * We assume that the output from iconv is never more than 4 times as
545 * long as the input for any pair of charsets we might be interested
548 static ssize_t convert_file_to (FILE * file, const char *fromcode,
549 int ncodes, const char **tocodes,
550 int *tocode, CONTENT * info)
554 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
557 ssize_t ibl, obl, ubl, ubl1, n, ret;
560 CONTENT_STATE *states;
563 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
564 if (cd1 == MUTT_ICONV_ERROR)
567 cd = p_new(iconv_t, ncodes);
568 score = p_new(ssize_t, ncodes);
569 states = p_new(CONTENT_STATE, ncodes);
570 infos = p_new(CONTENT, ncodes);
572 for (i = 0; i < ncodes; i++)
573 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
574 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
576 /* Special case for conversion to UTF-8 */
577 cd[i] = MUTT_ICONV_ERROR, score[i] = -1;
583 /* Try to fill input buffer */
584 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
587 /* Convert to UTF-8 */
589 ob = bufu, obl = sizeof (bufu);
590 n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
591 assert (n == -1 || !n);
592 if (n == -1 && ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
593 assert (errno == EILSEQ ||
594 (errno == EINVAL && ib == bufi && ibl < ssizeof (bufi)));
600 /* Convert from UTF-8 */
601 for (i = 0; i < ncodes; i++)
602 if (cd[i] != MUTT_ICONV_ERROR && score[i] != -1) {
603 ub = bufu, ubl = ubl1;
604 ob = bufo, obl = sizeof (bufo);
605 n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
611 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
614 else if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1)
615 /* Special case for conversion to UTF-8 */
616 update_content_info (&infos[i], &states[i], bufu, ubl1);
619 /* Save unused input */
620 memmove (bufi, ib, ibl);
621 else if (!ubl1 && ib < bufi + sizeof (bufi)) {
628 /* Find best score */
630 for (i = 0; i < ncodes; i++) {
631 if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1) {
632 /* Special case for conversion to UTF-8 */
637 else if (cd[i] == MUTT_ICONV_ERROR || score[i] == -1)
639 else if (ret == -1 || score[i] < ret) {
647 memcpy (info, &infos[*tocode], sizeof (CONTENT));
648 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
652 for (i = 0; i < ncodes; i++)
653 if (cd[i] != MUTT_ICONV_ERROR)
665 #endif /* !HAVE_ICONV */
669 * Find the first of the fromcodes that gives a valid conversion and
670 * the best charset conversion of the file into one of the tocodes. If
671 * successful, set *fromcode and *tocode to dynamically allocated
672 * strings, set CONTENT *info, and return the number of characters
673 * converted inexactly. If no conversion was possible, return -1.
675 * Both fromcodes and tocodes may be colon-separated lists of charsets.
676 * However, if fromcode is zero then fromcodes is assumed to be the
677 * name of a single charset even if it contains a colon.
679 static ssize_t convert_file_from_to (FILE * file,
680 const char *fromcodes,
681 const char *tocodes, char **fromcode,
682 char **tocode, CONTENT * info)
690 /* Count the tocodes */
692 for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
693 if ((c1 = strchr (c, ':')) == c)
699 tcode = p_new(char *, ncodes);
700 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
701 if ((c1 = strchr (c, ':')) == c)
703 tcode[i] = m_substrdup(c, c1);
708 /* Try each fromcode in turn */
709 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
710 if ((c1 = strchr (c, ':')) == c)
712 fcode = m_substrdup(c, c1);
714 ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
726 /* There is only one fromcode */
727 ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
736 for (i = 0; i < ncodes; i++)
745 * Analyze the contents of a file to determine which MIME encoding to use.
746 * Also set the body charset, sometimes, or not.
748 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
753 char *fromcode = NULL;
764 if (stat (fname, &sb) == -1) {
765 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
769 if (!S_ISREG (sb.st_mode)) {
770 mutt_error (_("%s isn't a regular file."), fname);
774 if ((fp = fopen (fname, "r")) == NULL) {
778 info = p_new(CONTENT, 1);
781 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
782 const char *chs = parameter_getval(b->parameter, "charset");
783 char *fchs = b->use_disp ? ((FileCharset && *FileCharset) ?
784 FileCharset : Charset) : Charset;
785 if (Charset && (chs || SendCharset) &&
786 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
787 &fromcode, &tocode, info) != -1) {
789 charset_canonicalize (chsbuf, sizeof (chsbuf), tocode);
790 parameter_setval(&b->parameter, "charset", chsbuf);
792 b->file_charset = fromcode;
800 while ((r = fread (buffer, 1, sizeof (buffer), fp)))
801 update_content_info (info, &state, buffer, r);
802 update_content_info (info, &state, 0, 0);
806 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
807 parameter_setval(&b->parameter, "charset",
808 (!info->hibin ? "us-ascii"
809 : Charset && !charset_is_us_ascii(Charset) ? Charset : "unknown-8bit"));
814 /* Given a file with path ``s'', see if there is a registered MIME type.
815 * returns the major MIME type, and copies the subtype to ``d''. First look
816 * for ~/.mime.types, then look in a system mime.types if we can find one.
817 * The longest match is used so that we can match `ps.gz' when `gz' also
821 int mutt_lookup_mime_type (BODY * att, const char *path)
825 char buf[LONG_STRING];
826 char subtype[STRING], xtype[STRING];
828 int szf, sze, cur_sze;
836 szf = m_strlen(path);
838 for (count = 0; count < 4; count++) {
840 * can't use strtok() because we use it in an inner loop below, so use
841 * a switch statement here instead.
845 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL (Homedir));
848 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
851 m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
854 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
857 goto bye; /* shouldn't happen */
860 if ((f = fopen (buf, "r")) != NULL) {
861 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
862 /* weed out any comments */
863 if ((p = strchr (buf, '#')))
866 /* remove any leading space. */
867 ct = vskipspaces(buf);
869 /* position on the next field in this line */
870 if ((p = strpbrk (ct, " \t")) == NULL)
875 /* cycle through the file extensions */
876 while ((p = strtok (p, " \t\n"))) {
878 if ((sze > cur_sze) && (szf >= sze) &&
879 (m_strcasecmp(path + szf - sze, p) == 0
880 || ascii_strcasecmp (path + szf - sze, p) == 0)
881 && (szf == sze || path[szf - sze - 1] == '.'))
883 /* get the content-type */
885 if ((p = strchr (ct, '/')) == NULL) {
886 /* malformed line, just skip it. */
891 for (q = p; *q && !ISSPACE (*q); q++);
893 m_strncpy(subtype, sizeof(subtype), p, q - p);
895 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
896 m_strcpy(xtype, sizeof(xtype), ct);
909 if (type != TYPEOTHER || *xtype != '\0') {
911 m_strreplace(&att->subtype, subtype);
912 m_strreplace(&att->xtype, xtype);
918 void mutt_message_to_7bit (BODY * a, FILE * fp)
920 char temp[_POSIX_PATH_MAX];
926 if (!a->filename && fp)
928 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
929 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
934 if (stat (a->filename, &sb) == -1) {
935 mutt_perror ("stat");
938 a->length = sb.st_size;
942 if (!(fpout = safe_fopen (temp, "w+"))) {
943 mutt_perror ("fopen");
947 fseeko (fpin, a->offset, 0);
948 a->parts = mutt_parse_messageRFC822 (fpin, a);
950 transform_to_7bit (a->parts, fpin);
952 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
953 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
955 fputs ("MIME-Version: 1.0\n", fpout);
956 mutt_write_mime_header (a->parts, fpout);
958 mutt_write_mime_body (a->parts, fpout);
970 a->encoding = ENC7BIT;
971 a->d_filename = a->filename;
972 if (a->filename && a->unlink)
973 unlink (a->filename);
974 a->filename = m_strdup(temp);
976 if (stat (a->filename, &sb) == -1) {
977 mutt_perror ("stat");
980 a->length = sb.st_size;
981 body_list_wipe(&a->parts);
982 a->hdr->content = NULL;
985 static void transform_to_7bit (BODY * a, FILE * fpin)
987 char buff[_POSIX_PATH_MAX];
992 for (; a; a = a->next) {
993 if (a->type == TYPEMULTIPART) {
994 if (a->encoding != ENC7BIT)
995 a->encoding = ENC7BIT;
997 transform_to_7bit (a->parts, fpin);
999 else if (mutt_is_message_type (a->type, a->subtype)) {
1000 mutt_message_to_7bit (a, fpin);
1004 a->force_charset = 1;
1007 if ((s.fpout = safe_fopen (buff, "w")) == NULL) {
1008 mutt_perror ("fopen");
1012 mutt_decode_attachment (a, &s);
1014 a->d_filename = a->filename;
1015 a->filename = m_strdup(buff);
1017 if (stat (a->filename, &sb) == -1) {
1018 mutt_perror ("stat");
1021 a->length = sb.st_size;
1023 mutt_update_encoding (a);
1024 if (a->encoding == ENC8BIT)
1025 a->encoding = ENCQUOTEDPRINTABLE;
1026 else if (a->encoding == ENCBINARY)
1027 a->encoding = ENCBASE64;
1032 /* determine which Content-Transfer-Encoding to use */
1033 static void mutt_set_encoding (BODY * b, CONTENT * info)
1035 char send_charset[SHORT_STRING];
1037 if (b->type == TYPETEXT) {
1039 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1040 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1041 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1042 b->encoding = ENCQUOTEDPRINTABLE;
1043 else if (info->hibin)
1044 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1046 b->encoding = ENC7BIT;
1048 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1049 if (info->lobin || info->hibin) {
1050 if (option (OPTALLOW8BIT) && !info->lobin)
1051 b->encoding = ENC8BIT;
1053 mutt_message_to_7bit (b, NULL);
1056 b->encoding = ENC7BIT;
1058 else if (b->type == TYPEAPPLICATION
1059 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1060 b->encoding = ENC7BIT;
1063 /* Determine which encoding is smaller */
1064 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1065 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1066 b->encoding = ENCBASE64;
1068 b->encoding = ENCQUOTEDPRINTABLE;
1072 void mutt_stamp_attachment (BODY * a)
1074 a->stamp = time (NULL);
1077 /* Get a body's character set */
1079 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1083 if (b && b->type != TYPETEXT)
1086 p = b ? parameter_getval(b->parameter, "charset") : NULL;
1087 charset_canonicalize(d, dlen, p);
1092 /* Assumes called from send mode where BODY->filename points to actual file */
1093 void mutt_update_encoding (BODY * a)
1096 char chsbuff[STRING];
1098 /* override noconv when it's us-ascii */
1099 if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1102 if (!a->force_charset && !a->noconv)
1103 parameter_delval(&a->parameter, "charset");
1105 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1108 mutt_set_encoding (a, info);
1109 mutt_stamp_attachment (a);
1111 p_delete(&a->content);
1116 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1118 char buffer[LONG_STRING];
1121 int cmflags, chflags;
1122 int pgp = hdr->security;
1124 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1125 (hdr->security & ENCRYPT)) {
1126 if (!crypt_valid_passphrase (hdr->security))
1130 mutt_mktemp (buffer);
1131 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1135 body->type = TYPEMESSAGE;
1136 body->subtype = m_strdup("rfc822");
1137 body->filename = m_strdup(buffer);
1140 body->disposition = DISPINLINE;
1143 mutt_parse_mime_message (ctx, hdr);
1148 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1149 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1150 chflags |= CH_MIME | CH_TXTPLAIN;
1151 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1152 pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1154 else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1155 if (mutt_is_multipart_encrypted (hdr->content)) {
1156 chflags |= CH_MIME | CH_NONEWLINE;
1157 cmflags = M_CM_DECODE_PGP;
1160 else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1161 chflags |= CH_MIME | CH_TXTPLAIN;
1162 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1165 else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1166 chflags |= CH_MIME | CH_TXTPLAIN;
1167 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1168 pgp &= ~SMIMEENCRYPT;
1172 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1177 body->hdr = header_new();
1178 body->hdr->offset = 0;
1179 /* we don't need the user headers here */
1180 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1181 body->hdr->security = pgp;
1182 mutt_update_encoding (body);
1183 body->parts = body->hdr->content;
1190 BODY *mutt_make_file_attach (const char *path)
1196 att->filename = m_strdup(path);
1198 /* Attempt to determine the appropriate content-type based on the filename
1201 mutt_lookup_mime_type (att, path);
1203 if ((info = mutt_get_content_info (path, att)) == NULL) {
1204 body_list_wipe(&att);
1208 if (!att->subtype) {
1209 if (info->lobin == 0
1210 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1212 * Statistically speaking, there should be more than 10% "lobin"
1213 * chars if this is really a binary file...
1215 att->type = TYPETEXT;
1216 att->subtype = m_strdup("plain");
1218 att->type = TYPEAPPLICATION;
1219 att->subtype = m_strdup("octet-stream");
1223 mutt_update_encoding (att);
1227 static int get_toplevel_encoding (BODY * a)
1231 for (; a; a = a->next) {
1232 if (a->encoding == ENCBINARY)
1234 else if (a->encoding == ENC8BIT)
1241 BODY *mutt_make_multipart (BODY * b)
1246 new->type = TYPEMULTIPART;
1247 new->subtype = m_strdup("mixed");
1248 new->encoding = get_toplevel_encoding (b);
1249 parameter_set_boundary(&new->parameter);
1251 new->disposition = DISPINLINE;
1257 /* remove the multipart body if it exists */
1258 BODY *mutt_remove_multipart (BODY * b)
1271 char *mutt_make_date (char *s, ssize_t len)
1273 time_t t = time (NULL);
1274 struct tm *l = localtime (&t);
1275 time_t tz = mutt_local_tz (t);
1279 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1280 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1281 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1282 (int) tz / 60, (int) abs (tz) % 60);
1286 /* wrapper around mutt_write_address() so we can handle very large
1287 recipient lists without needing a huge temporary buffer in memory */
1288 void mutt_write_address_list (address_t * adr, FILE * fp, int linelen,
1292 char buf[LONG_STRING];
1300 rfc822_write_address (buf, sizeof (buf), adr, display);
1301 len = m_strlen(buf);
1302 if (count && linelen + len > 74) {
1304 linelen = len + 8; /* tab is usually about 8 spaces... */
1307 if (count && adr->mailbox) {
1315 if (!adr->group && adr->next && adr->next->mailbox) {
1325 /* arbitrary number of elements to grow the array by */
1330 /* need to write the list in reverse because they are stored in reverse order
1331 * when parsed to speed up threading
1333 void mutt_write_references (string_list_t * r, FILE * f)
1335 string_list_t **ref = NULL;
1336 int refcnt = 0, refmax = 0;
1338 for (; (TrimRef == 0 || refcnt < TrimRef) && r; r = r->next) {
1339 if (refcnt == refmax)
1340 p_realloc(&ref, refmax += REF_INC);
1344 while (refcnt-- > 0) {
1346 fputs (ref[refcnt]->data, f);
1352 static int edit_header(int mode, const char *s)
1355 int slen = m_strlen(s);
1357 if (mode != 1 || option(OPTXMAILTO))
1360 p = skipspaces(EditorHeaders);
1362 if (!ascii_strncasecmp(p, s, slen) && p[slen - 1] == ':')
1364 p = skipspaces(p + slen);
1370 /* Note: all RFC2047 encoding should be done outside of this routine, except
1371 * for the "real name." This will allow this routine to be used more than
1372 * once, if necessary.
1374 * Likewise, all IDN processing should happen outside of this routine.
1376 * mode == 1 => "lite" mode (used for edit_hdrs)
1377 * mode == 0 => normal mode. write full header + MIME headers
1378 * mode == -1 => write just the envelope info (used for postponing messages)
1380 * privacy != 0 => will omit any headers which may identify the user.
1381 * Output generated is suitable for being sent through
1382 * anonymous remailer chains.
1386 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1387 int mode, int privacy)
1389 char buffer[LONG_STRING];
1391 string_list_t *tmp = env->userhdrs;
1392 int has_agent = 0; /* user defined user-agent header field exists */
1395 if (!option (OPTNEWSSEND))
1397 if (mode == 0 && !privacy)
1398 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1400 /* OPTUSEFROM is not consulted here so that we can still write a From:
1401 * field if the user sets it with the `my_hdr' command
1403 if (env->from && !privacy) {
1405 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1406 fprintf (fp, "From: %s\n", buffer);
1411 mutt_write_address_list (env->to, fp, 4, 0);
1415 if (!option (OPTNEWSSEND))
1417 if (edit_header(mode, "To:"))
1418 fputs ("To:\n", fp);
1422 mutt_write_address_list (env->cc, fp, 4, 0);
1426 if (!option (OPTNEWSSEND))
1428 if (edit_header(mode, "Cc:"))
1429 fputs ("Cc:\n", fp);
1432 if (mode != 0 || option (OPTWRITEBCC)) {
1433 fputs ("Bcc: ", fp);
1434 mutt_write_address_list (env->bcc, fp, 5, 0);
1439 if (!option (OPTNEWSSEND))
1441 if (edit_header(mode, "Bcc:"))
1442 fputs ("Bcc:\n", fp);
1445 if (env->newsgroups)
1446 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1447 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Newsgroups:"))
1448 fputs ("Newsgroups:\n", fp);
1450 if (env->followup_to)
1451 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1452 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Followup-To:"))
1453 fputs ("Followup-To:\n", fp);
1455 if (env->x_comment_to)
1456 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1457 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO) &&
1458 edit_header(mode, "X-Comment-To:"))
1459 fputs ("X-Comment-To:\n", fp);
1463 fprintf (fp, "Subject: %s\n", env->subject);
1464 else if (mode == 1 && edit_header(mode, "Subject:"))
1465 fputs ("Subject:\n", fp);
1467 /* save message id if the user has set it */
1468 if (env->message_id && !privacy)
1469 fprintf (fp, "Message-ID: %s\n", env->message_id);
1471 if (env->reply_to) {
1472 fputs ("Reply-To: ", fp);
1473 mutt_write_address_list (env->reply_to, fp, 10, 0);
1475 else if (mode > 0 && edit_header(mode, "Reply-To:"))
1476 fputs ("Reply-To:\n", fp);
1478 if (env->mail_followup_to)
1480 if (!option (OPTNEWSSEND))
1483 fputs ("Mail-Followup-To: ", fp);
1484 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1488 if (env->references) {
1489 fputs ("References:", fp);
1490 mutt_write_references (env->references, fp);
1494 /* Add the MIME headers */
1495 fputs ("MIME-Version: 1.0\n", fp);
1496 mutt_write_mime_header (attach, fp);
1499 if (env->in_reply_to) {
1500 fputs ("In-Reply-To:", fp);
1501 mutt_write_references (env->in_reply_to, fp);
1505 /* Add any user defined headers */
1506 for (; tmp; tmp = tmp->next) {
1507 if ((p = strchr (tmp->data, ':'))) {
1508 p = vskipspaces(p + 1);
1510 continue; /* don't emit empty fields. */
1512 /* check to see if the user has overridden the user-agent field */
1513 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1519 fputs (tmp->data, fp);
1524 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1527 if (OperatingSystem != NULL) {
1528 os = OperatingSystem;
1531 os = (uname(&un) == -1) ? "UNIX" : un.sysname;
1533 /* Add a vanity header */
1534 fprintf (fp, "User-Agent: %s (%s)\n", mutt_make_version (0), os);
1537 return (ferror (fp) == 0 ? 0 : -1);
1540 static void encode_headers (string_list_t * h)
1546 for (; h; h = h->next) {
1547 if (!(p = strchr (h->data, ':')))
1551 p = vskipspaces(p + 1);
1557 rfc2047_encode_string (&tmp);
1558 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1560 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
1566 const char *mutt_fqdn (short may_hide_host)
1570 if (Fqdn && Fqdn[0] != '@') {
1573 if (may_hide_host && option (OPTHIDDENHOST)) {
1574 if ((p = strchr (Fqdn, '.')))
1577 /* sanity check: don't hide the host if
1578 * the fqdn is something like detebe.org.
1581 if (!p || !(q = strchr (p, '.')))
1589 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1590 static char mutt_normalized_char(char c)
1592 return (isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.';
1595 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1597 #define APPEND_FMT(fmt, arg) \
1599 int snlen = snprintf(buf, len, fmt, arg); \
1604 #define APPEND_BYTE(c) \
1618 static char MsgIdPfx = 'A';
1622 APPEND_BYTE(mutt_normalized_char(c));
1630 APPEND_FMT("%02d", tm->tm_mday);
1633 APPEND_FMT("%02d", tm->tm_hour);
1636 APPEND_FMT("%02d", tm->tm_mon + 1);
1639 APPEND_FMT("%02d", tm->tm_min);
1642 APPEND_FMT("%lo", (unsigned long)now);
1645 APPEND_FMT("%u", (unsigned int)getpid());
1648 APPEND_FMT("%c", MsgIdPfx);
1649 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1652 APPEND_FMT("%u", (unsigned int)rand());
1655 APPEND_FMT("%x", (unsigned int)rand());
1658 APPEND_FMT("%02d", tm->tm_sec);
1661 APPEND_FMT("%u", (unsigned int) now);
1664 APPEND_FMT("%x", (unsigned int) now);
1666 case 'Y': /* this will break in the year 10000 ;-) */
1667 APPEND_FMT("%04d", tm->tm_year + 1900);
1672 default: /* invalid formats are replaced by '.' */
1674 m_strncat(buf, len, ".", 1);
1684 static char *mutt_gen_msgid (void)
1686 char buf[SHORT_STRING];
1687 char localpart[SHORT_STRING];
1688 unsigned int localpart_length;
1691 if (!(fqdn = mutt_fqdn (0)))
1692 fqdn = NONULL (Hostname);
1694 localpart_length = sizeof (buf) - m_strlen(fqdn) - 4; /* the 4 characters are '<', '@', '>' and '\0' */
1696 mutt_gen_localpart (localpart, localpart_length, MsgIdFormat);
1698 snprintf (buf, sizeof (buf), "<%s@%s>", localpart, fqdn);
1699 return (m_strdup(buf));
1702 static RETSIGTYPE alarm_handler (int sig)
1707 /* invoke sendmail in a subshell
1708 path (in) path to program to execute
1709 args (in) arguments to pass to program
1710 msg (in) temp file containing message to send
1711 tempfile (out) if sendmail is put in the background, this points
1712 to the temporary file containing the stdout of the
1715 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1721 mutt_block_signals_system ();
1724 /* we also don't want to be stopped right now */
1725 sigaddset (&set, SIGTSTP);
1726 sigprocmask (SIG_BLOCK, &set, NULL);
1728 if (SendmailWait >= 0) {
1729 char tmp[_POSIX_PATH_MAX];
1732 *tempfile = m_strdup(tmp);
1735 if ((pid = fork ()) == 0) {
1736 struct sigaction act, oldalrm;
1738 /* save parent's ID before setsid() */
1741 /* we want the delivery to continue even after the main process dies,
1742 * so we put ourselves into another session right away
1746 /* next we close all open files */
1747 for (fd = 0; fd < getdtablesize(); fd++)
1750 /* now the second fork() */
1751 if ((pid = fork ()) == 0) {
1752 /* "msg" will be opened as stdin */
1753 if (open (msg, O_RDONLY, 0) < 0) {
1759 if (SendmailWait >= 0) {
1760 /* *tempfile will be opened as stdout */
1761 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1764 /* redirect stderr to *tempfile too */
1769 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1771 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1775 execv (path, (char**)args);
1778 else if (pid == -1) {
1784 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1785 * SendmailWait = 0: wait forever
1786 * SendmailWait < 0: don't wait
1788 if (SendmailWait > 0) {
1790 act.sa_handler = alarm_handler;
1792 /* need to make sure waitpid() is interrupted on SIGALRM */
1793 act.sa_flags = SA_INTERRUPT;
1797 sigemptyset (&act.sa_mask);
1798 sigaction (SIGALRM, &act, &oldalrm);
1799 alarm (SendmailWait);
1801 else if (SendmailWait < 0)
1802 _exit (0xff & EX_OK);
1804 if (waitpid (pid, &st, 0) > 0) {
1805 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1806 if (SendmailWait && st == (0xff & EX_OK)) {
1807 unlink (*tempfile); /* no longer needed */
1812 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1813 if (SendmailWait > 0) {
1819 /* reset alarm; not really needed, but... */
1821 sigaction (SIGALRM, &oldalrm, NULL);
1823 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1824 /* the parent is already dead */
1832 sigprocmask (SIG_UNBLOCK, &set, NULL);
1834 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1835 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1837 st = S_ERR; /* error */
1839 mutt_unblock_signals_system (1);
1844 static const char **
1845 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1847 for (; addr; addr = addr->next) {
1848 /* weed out group mailboxes, since those are for display only */
1849 if (addr->mailbox && !addr->group) {
1850 if (*argslen == *argsmax)
1851 p_realloc(&args, *argsmax += 5);
1852 args[(*argslen)++] = addr->mailbox;
1858 static const char **
1859 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1861 if (*argslen == *argsmax) {
1862 p_realloc(&args, *argsmax += 5);
1864 args[(*argslen)++] = s;
1868 static int mutt_invoke_sendmail (address_t * from, /* the sender */
1869 address_t * to, address_t * cc, address_t * bcc, /* recips */
1870 const char *msg, /* file containing message */
1872 { /* message contains 8bit chars */
1873 char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
1874 const char **args = NULL;
1875 ssize_t argslen = 0, argsmax = 0;
1879 if (option (OPTNEWSSEND)) {
1880 char cmd[LONG_STRING];
1882 mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0,
1885 i = nntp_post (msg);
1894 s = m_strdup(Sendmail);
1898 while ((ps = strtok (ps, " "))) {
1899 if (argslen == argsmax)
1900 p_realloc(&args, argsmax += 5);
1903 args[argslen++] = ps;
1905 path = m_strdup(ps);
1906 ps = strrchr (ps, '/');
1911 args[argslen++] = ps;
1918 if (!option (OPTNEWSSEND)) {
1920 if (eightbit && option (OPTUSE8BITMIME))
1921 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1923 if (option (OPTENVFROM)) {
1924 address_t *f = NULL;
1927 else if (from && !from->next)
1930 args = add_option (args, &argslen, &argsmax, "-f");
1931 args = add_args (args, &argslen, &argsmax, f);
1935 args = add_option (args, &argslen, &argsmax, "-N");
1936 args = add_option (args, &argslen, &argsmax, DsnNotify);
1939 args = add_option (args, &argslen, &argsmax, "-R");
1940 args = add_option (args, &argslen, &argsmax, DsnReturn);
1942 args = add_option (args, &argslen, &argsmax, "--");
1943 args = add_args (args, &argslen, &argsmax, to);
1944 args = add_args (args, &argslen, &argsmax, cc);
1945 args = add_args (args, &argslen, &argsmax, bcc);
1950 if (argslen == argsmax)
1951 p_realloc(&args, ++argsmax);
1953 args[argslen++] = NULL;
1955 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1957 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1962 if (stat (childout, &st) == 0 && st.st_size > 0)
1963 mutt_do_pager (_("Output of the delivery process"), childout, 0,
1971 p_delete(&childout);
1976 if (i == (EX_OK & 0xff))
1978 else if (i == S_BKG)
1985 int mutt_invoke_mta (address_t * from, /* the sender */
1986 address_t * to, address_t * cc, address_t * bcc, /* recips */
1987 const char *msg, /* file containing message */
1989 { /* message contains 8bit chars */
1992 if (!option (OPTNEWSSEND))
1995 return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
1998 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
2001 /* For postponing (!final) do the necessary encodings only */
2002 void mutt_prepare_envelope (ENVELOPE * env, int final)
2004 char buffer[LONG_STRING];
2007 if (env->bcc && !(env->to || env->cc)) {
2008 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2009 * recipients if there is no To: or Cc: field, so attempt to suppress
2010 * it by using an empty To: field.
2012 env->to = address_new ();
2014 env->to->next = address_new ();
2017 rfc822_strcpy(buffer, sizeof(buffer), "undisclosed-recipients",
2020 env->to->mailbox = m_strdup(buffer);
2023 mutt_set_followup_to (env);
2025 if (!env->message_id && MsgIdFormat && *MsgIdFormat)
2026 env->message_id = mutt_gen_msgid ();
2029 /* Take care of 8-bit => 7-bit conversion. */
2030 rfc2047_encode_adrlist (env->to, "To");
2031 rfc2047_encode_adrlist (env->cc, "Cc");
2032 rfc2047_encode_adrlist (env->bcc, "Bcc");
2033 rfc2047_encode_adrlist (env->from, "From");
2034 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2035 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2039 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2042 rfc2047_encode_string (&env->subject);
2044 encode_headers (env->userhdrs);
2047 void mutt_unprepare_envelope (ENVELOPE * env)
2049 string_list_t *item;
2051 for (item = env->userhdrs; item; item = item->next)
2052 rfc2047_decode (&item->data);
2054 address_list_wipe(&env->mail_followup_to);
2056 /* back conversions */
2057 rfc2047_decode_adrlist (env->to);
2058 rfc2047_decode_adrlist (env->cc);
2059 rfc2047_decode_adrlist (env->bcc);
2060 rfc2047_decode_adrlist (env->from);
2061 rfc2047_decode_adrlist (env->reply_to);
2062 rfc2047_decode (&env->subject);
2065 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
2066 const char *resent_from, address_t * env_from)
2070 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2071 MESSAGE *msg = NULL;
2074 /* Try to bounce each message out, aborting if we get any failures. */
2075 for (i = 0; i < Context->msgcount; i++)
2076 if (Context->hdrs[i]->tagged)
2078 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2083 /* If we failed to open a message, return with error */
2084 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2090 mutt_mktemp (tempfile);
2091 if ((f = safe_fopen (tempfile, "w")) != NULL) {
2092 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2094 if (!option (OPTBOUNCEDELIVERED))
2095 ch_flags |= CH_WEED_DELIVERED;
2097 fseeko (fp, h->offset, 0);
2098 fprintf (f, "Resent-From: %s", resent_from);
2099 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2100 if (MsgIdFormat && *MsgIdFormat)
2101 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid ());
2102 fputs ("Resent-To: ", f);
2103 mutt_write_address_list (to, f, 11, 0);
2104 mutt_copy_header (fp, h, f, ch_flags, NULL);
2106 mutt_copy_bytes (fp, f, h->content->length);
2109 ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2110 h->content->encoding == ENC8BIT);
2114 mx_close_message (&msg);
2119 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2122 const char *fqdn = mutt_fqdn (1);
2123 char resent_from[STRING];
2127 resent_from[0] = '\0';
2128 from = mutt_default_from ();
2131 rfc822_qualify (from, fqdn);
2133 rfc2047_encode_adrlist (from, "Resent-From");
2134 if (mutt_addrlist_to_idna (from, &err)) {
2135 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2138 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2141 unset_option (OPTNEWSSEND);
2144 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2146 address_list_wipe(&from);
2151 static void set_noconv_flags (BODY * b, short flag)
2153 for (; b; b = b->next) {
2154 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2155 set_noconv_flags (b->parts, flag);
2156 else if (b->type == TYPETEXT && b->noconv) {
2157 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2162 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2163 int post, char *fcc)
2167 char tempfile[_POSIX_PATH_MAX];
2168 FILE *tempfp = NULL;
2172 set_noconv_flags (hdr->content, 1);
2174 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2178 /* We need to add a Content-Length field to avoid problems where a line in
2179 * the message body begins with "From "
2181 if (f.magic == M_MMDF || f.magic == M_MBOX) {
2182 mutt_mktemp (tempfile);
2183 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL) {
2184 mutt_perror (tempfile);
2185 mx_close_mailbox (&f, NULL);
2190 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2191 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2192 mx_close_mailbox (&f, NULL);
2196 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2197 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2199 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0,
2202 /* (postponment) if this was a reply of some sort, <msgid> contians the
2203 * Message-ID: of message replied to. Save it using a special X-Mutt-
2204 * header so it can be picked up if the message is recalled at a later
2205 * point in time. This will allow the message to be marked as replied if
2206 * the same mailbox is still open.
2209 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2211 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2212 * it can be picked up when the message is recalled
2215 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2216 fprintf (msg->fp, "Status: RO\n");
2220 /* (postponment) if the mail is to be signed or encrypted, save this info */
2221 if (post && (hdr->security & APPLICATION_PGP)) {
2222 fputs ("X-Mutt-PGP: ", msg->fp);
2223 if (hdr->security & ENCRYPT)
2224 fputc ('E', msg->fp);
2225 if (hdr->security & SIGN) {
2226 fputc ('S', msg->fp);
2227 if (PgpSignAs && *PgpSignAs)
2228 fprintf (msg->fp, "<%s>", PgpSignAs);
2230 if (hdr->security & INLINE)
2231 fputc ('I', msg->fp);
2232 fputc ('\n', msg->fp);
2235 /* (postponment) if the mail is to be signed or encrypted, save this info */
2236 if (post && (hdr->security & APPLICATION_SMIME)) {
2237 fputs ("X-Mutt-SMIME: ", msg->fp);
2238 if (hdr->security & ENCRYPT) {
2239 fputc ('E', msg->fp);
2240 if (SmimeCryptAlg && *SmimeCryptAlg)
2241 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2243 if (hdr->security & SIGN) {
2244 fputc ('S', msg->fp);
2245 if (SmimeDefaultKey && *SmimeDefaultKey)
2246 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2248 if (hdr->security & INLINE)
2249 fputc ('I', msg->fp);
2250 fputc ('\n', msg->fp);
2254 /* (postponement) if the mail is to be sent through a mixmaster
2255 * chain, save that information
2258 if (post && hdr->chain && hdr->chain) {
2261 fputs ("X-Mutt-Mix:", msg->fp);
2262 for (p = hdr->chain; p; p = p->next)
2263 fprintf (msg->fp, " %s", (char *) p->data);
2265 fputc ('\n', msg->fp);
2270 char sasha[LONG_STRING];
2273 mutt_write_mime_body (hdr->content, tempfp);
2275 /* make sure the last line ends with a newline. Emacs doesn't ensure
2276 * this will happen, and it can cause problems parsing the mailbox
2279 fseeko (tempfp, -1, 2);
2280 if (fgetc (tempfp) != '\n') {
2281 fseeko (tempfp, 0, 2);
2282 fputc ('\n', tempfp);
2286 if (ferror (tempfp)) {
2289 mx_commit_message (msg, &f); /* XXX - really? */
2290 mx_close_message (&msg);
2291 mx_close_mailbox (&f, NULL);
2295 /* count the number of lines */
2297 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2299 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2300 fprintf (msg->fp, "Lines: %d\n\n", lines);
2302 /* copy the body and clean up */
2304 r = mutt_copy_stream (tempfp, msg->fp);
2305 if (fclose (tempfp) != 0)
2307 /* if there was an error, leave the temp version */
2312 fputc ('\n', msg->fp); /* finish off the header */
2313 r = mutt_write_mime_body (hdr->content, msg->fp);
2316 if (mx_commit_message (msg, &f) != 0)
2318 mx_close_message (&msg);
2319 mx_close_mailbox (&f, NULL);
2322 set_noconv_flags (hdr->content, 0);