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>
24 #include "recvattach.h"
29 #include <lib-crypt/crypt.h>
30 #include "mutt_idna.h"
33 # include "mutt_libesmtp.h"
34 #endif /* USE_LIBESMTP */
40 #ifdef HAVE_SYSEXITS_H
42 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
46 /* If you are debugging this file, comment out the following line. */
55 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
57 static void transform_to_7bit (BODY * a, FILE * fpin);
59 static void encode_quoted (fgetconv_t * fc, FILE * fout, int istext)
62 char line[77], savechar;
64 while ((c = fgetconv (fc)) != EOF) {
65 /* Wrap the line if needed. */
66 if (linelen == 76 && ((istext && c != '\n') || !istext)) {
67 /* If the last character is "quoted", then be sure to move all three
68 * characters to the next line. Otherwise, just move the last
71 if (line[linelen - 3] == '=') {
72 line[linelen - 3] = 0;
77 line[1] = line[linelen - 2];
78 line[2] = line[linelen - 1];
82 savechar = line[linelen - 1];
83 line[linelen - 1] = '=';
92 /* Escape lines that begin with/only contain "the message separator". */
93 if (linelen == 4 && !m_strncmp("From", line, 4)) {
94 m_strcpy(line, sizeof(line), "=46rom");
97 else if (linelen == 4 && !m_strncmp("from", line, 4)) {
98 m_strcpy(line, sizeof(line), "=66rom");
101 else if (linelen == 1 && line[0] == '.') {
102 m_strcpy(line, sizeof(line), "=2E");
107 if (c == '\n' && istext) {
108 /* Check to make sure there is no trailing space on this line. */
110 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
112 sprintf (line + linelen - 1, "=%2.2X",
113 (unsigned char) line[linelen - 1]);
117 savechar = line[linelen - 1];
119 line[linelen - 1] = '=';
122 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
132 else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
133 /* Check to make sure there is enough room for the quoted character.
134 * If not, wrap to the next line.
137 line[linelen++] = '=';
143 sprintf (line + linelen, "=%2.2X", (unsigned char) c);
147 /* Don't worry about wrapping the line here. That will happen during
148 * the next iteration when I'll also know what the next character is.
154 /* Take care of anything left in the buffer */
156 if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
157 /* take care of trailing whitespace */
159 sprintf (line + linelen - 1, "=%2.2X",
160 (unsigned char) line[linelen - 1]);
162 savechar = line[linelen - 1];
163 line[linelen - 1] = '=';
167 sprintf (line, "=%2.2X", (unsigned char) savechar);
176 static char b64_buffer[3];
177 static short b64_num;
178 static short b64_linelen;
180 static void b64_flush (FILE * fout)
187 if (b64_linelen >= 72) {
192 for (i = b64_num; i < 3; i++)
193 b64_buffer[i] = '\0';
195 fputc(__m_b64chars[(b64_buffer[0] >> 2) & 0x3f], fout);
198 [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
203 [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
207 fputc (__m_b64chars[b64_buffer[2] & 0x3f], fout);
212 while (b64_linelen % 4) {
221 static void b64_putc (char c, FILE * fout)
226 b64_buffer[b64_num++] = c;
230 static void encode_base64 (fgetconv_t * fc, FILE * fout, int istext)
234 b64_num = b64_linelen = 0;
236 while ((ch = fgetconv (fc)) != EOF) {
237 if (istext && ch == '\n' && ch1 != '\r')
238 b64_putc ('\r', fout);
246 static void encode_8bit (fgetconv_t * fc, FILE * fout, int istext)
250 while ((ch = fgetconv (fc)) != EOF)
255 int mutt_write_mime_header (BODY * a, FILE * f)
264 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
269 len = 25 + m_strlen(a->subtype); /* approximate len. of content-type */
271 for (p = a->parameter; p; p = p->next) {
280 tmp = m_strdup(p->value);
281 encode = rfc2231_encode_string (&tmp);
282 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
284 /* Dirty hack to make messages readable by Outlook Express
285 * for the Mac: force quotes around the boundary parameter
286 * even when they aren't needed.
289 if (!ascii_strcasecmp (p->attribute, "boundary")
290 && !strcmp (buffer, tmp))
291 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
295 tmplen = m_strlen(buffer) + m_strlen(p->attribute) + 1;
297 if (len + tmplen + 2 > 76) {
306 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
314 fprintf (f, "Content-Description: %s\n", a->description);
316 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
319 if (!(fn = a->d_filename))
325 /* Strip off the leading path... */
326 if ((t = strrchr (fn, '/')))
333 encode = rfc2231_encode_string (&tmp);
334 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
336 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
342 if (a->encoding != ENC7BIT)
343 fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
345 /* Do NOT add the terminator here!!! */
346 return (ferror (f) ? -1 : 0);
349 int mutt_write_mime_body (BODY * a, FILE * f)
352 char boundary[SHORT_STRING];
353 char send_charset[SHORT_STRING];
358 if (a->type == TYPEMULTIPART) {
359 /* First, find the boundary to use */
360 if (!(p = parameter_getval(a->parameter, "boundary"))) {
361 mutt_error _("No boundary parameter found! [report this error]");
365 m_strcpy(boundary, sizeof(boundary), p);
367 for (t = a->parts; t; t = t->next) {
368 fprintf (f, "\n--%s\n", boundary);
369 if (mutt_write_mime_header (t, f) == -1)
372 if (mutt_write_mime_body (t, f) == -1)
375 fprintf (f, "\n--%s--\n", boundary);
376 return (ferror (f) ? -1 : 0);
379 /* This is pretty gross, but it's the best solution for now... */
380 if (a->type == TYPEAPPLICATION && !m_strcmp(a->subtype, "pgp-encrypted")) {
381 fputs ("Version: 1\n", f);
385 if ((fpin = fopen (a->filename, "r")) == NULL) {
386 mutt_error (_("%s no longer exists!"), a->filename);
390 if (a->type == TYPETEXT && (!a->noconv))
391 fc = fgetconv_open (fpin, a->file_charset,
392 mutt_get_body_charset (send_charset,
393 sizeof (send_charset), a), 0);
395 fc = fgetconv_open (fpin, 0, 0, 0);
397 #define write_as_text_part(a) (mutt_is_text_part(a) || mutt_is_application_pgp(a))
398 if (a->encoding == ENCQUOTEDPRINTABLE)
399 encode_quoted (fc, f, write_as_text_part (a));
400 else if (a->encoding == ENCBASE64)
401 encode_base64 (fc, f, write_as_text_part (a));
402 else if (a->type == TYPETEXT && (!a->noconv))
403 encode_8bit (fc, f, write_as_text_part (a));
405 mutt_copy_stream (fpin, f);
406 #undef write_as_text_part
408 fgetconv_close (&fc);
411 return (ferror (f) ? -1 : 0);
423 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
427 int whitespace = s->whitespace;
429 int linelen = s->linelen;
430 int was_cr = s->was_cr;
432 if (!d) { /* This signals EOF */
435 if (linelen > info->linemax)
436 info->linemax = linelen;
441 for (; dlen; d++, dlen--) {
454 if (linelen > info->linemax)
455 info->linemax = linelen;
470 if (linelen > info->linemax)
471 info->linemax = linelen;
476 else if (ch == '\r') {
484 else if (ch == '\t' || ch == '\f') {
488 else if (ch < 32 || ch == 127)
492 if ((ch == 'F') || (ch == 'f'))
502 if (linelen == 2 && ch != 'r')
504 else if (linelen == 3 && ch != 'o')
506 else if (linelen == 4) {
519 if (ch != ' ' && ch != '\t')
524 s->whitespace = whitespace;
526 s->linelen = linelen;
532 * Find the best charset conversion of the file from fromcode into one
533 * of the tocodes. If successful, set *tocode and CONTENT *info and
534 * return the number of characters converted inexactly. If no
535 * conversion was possible, return -1.
537 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
538 * which would otherwise prevent us from knowing the number of inexact
539 * conversions. Where the candidate target charset is UTF-8 we avoid
540 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
541 * fails with some libraries.
543 * We assume that the output from iconv is never more than 4 times as
544 * long as the input for any pair of charsets we might be interested
547 static ssize_t convert_file_to (FILE * file, const char *fromcode,
548 int ncodes, const char **tocodes,
549 int *tocode, CONTENT * info)
553 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
556 ssize_t ibl, obl, ubl, ubl1, n, ret;
559 CONTENT_STATE *states;
562 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
563 if (cd1 == MUTT_ICONV_ERROR)
566 cd = p_new(iconv_t, ncodes);
567 score = p_new(ssize_t, ncodes);
568 states = p_new(CONTENT_STATE, ncodes);
569 infos = p_new(CONTENT, ncodes);
571 for (i = 0; i < ncodes; i++)
572 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
573 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
575 /* Special case for conversion to UTF-8 */
576 cd[i] = MUTT_ICONV_ERROR, score[i] = -1;
582 /* Try to fill input buffer */
583 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
586 /* Convert to UTF-8 */
588 ob = bufu, obl = sizeof (bufu);
589 n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
590 assert (n == -1 || !n);
591 if (n == -1 && ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
592 assert (errno == EILSEQ ||
593 (errno == EINVAL && ib == bufi && ibl < ssizeof (bufi)));
599 /* Convert from UTF-8 */
600 for (i = 0; i < ncodes; i++)
601 if (cd[i] != MUTT_ICONV_ERROR && score[i] != -1) {
602 ub = bufu, ubl = ubl1;
603 ob = bufo, obl = sizeof (bufo);
604 n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
610 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
613 else if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1)
614 /* Special case for conversion to UTF-8 */
615 update_content_info (&infos[i], &states[i], bufu, ubl1);
618 /* Save unused input */
619 memmove (bufi, ib, ibl);
620 else if (!ubl1 && ib < bufi + sizeof (bufi)) {
627 /* Find best score */
629 for (i = 0; i < ncodes; i++) {
630 if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1) {
631 /* Special case for conversion to UTF-8 */
636 else if (cd[i] == MUTT_ICONV_ERROR || score[i] == -1)
638 else if (ret == -1 || score[i] < ret) {
646 memcpy (info, &infos[*tocode], sizeof (CONTENT));
647 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
651 for (i = 0; i < ncodes; i++)
652 if (cd[i] != MUTT_ICONV_ERROR)
664 #endif /* !HAVE_ICONV */
668 * Find the first of the fromcodes that gives a valid conversion and
669 * the best charset conversion of the file into one of the tocodes. If
670 * successful, set *fromcode and *tocode to dynamically allocated
671 * strings, set CONTENT *info, and return the number of characters
672 * converted inexactly. If no conversion was possible, return -1.
674 * Both fromcodes and tocodes may be colon-separated lists of charsets.
675 * However, if fromcode is zero then fromcodes is assumed to be the
676 * name of a single charset even if it contains a colon.
678 static ssize_t convert_file_from_to (FILE * file,
679 const char *fromcodes,
680 const char *tocodes, char **fromcode,
681 char **tocode, CONTENT * info)
689 /* Count the tocodes */
691 for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
692 if ((c1 = strchr (c, ':')) == c)
698 tcode = p_new(char *, ncodes);
699 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
700 if ((c1 = strchr (c, ':')) == c)
702 tcode[i] = m_substrdup(c, c1);
707 /* Try each fromcode in turn */
708 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
709 if ((c1 = strchr (c, ':')) == c)
711 fcode = m_substrdup(c, c1);
713 ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
725 /* There is only one fromcode */
726 ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
735 for (i = 0; i < ncodes; i++)
744 * Analyze the contents of a file to determine which MIME encoding to use.
745 * Also set the body charset, sometimes, or not.
747 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
752 char *fromcode = NULL;
763 if (stat (fname, &sb) == -1) {
764 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
768 if (!S_ISREG (sb.st_mode)) {
769 mutt_error (_("%s isn't a regular file."), fname);
773 if ((fp = fopen (fname, "r")) == NULL) {
777 info = p_new(CONTENT, 1);
780 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
781 const char *chs = parameter_getval(b->parameter, "charset");
782 char *fchs = b->use_disp ? ((FileCharset && *FileCharset) ?
783 FileCharset : Charset) : Charset;
784 if (Charset && (chs || SendCharset) &&
785 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
786 &fromcode, &tocode, info) != -1) {
788 charset_canonicalize (chsbuf, sizeof (chsbuf), tocode);
789 parameter_setval(&b->parameter, "charset", chsbuf);
791 b->file_charset = fromcode;
799 while ((r = fread (buffer, 1, sizeof (buffer), fp)))
800 update_content_info (info, &state, buffer, r);
801 update_content_info (info, &state, 0, 0);
805 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
806 parameter_setval(&b->parameter, "charset",
807 (!info->hibin ? "us-ascii"
808 : Charset && !charset_is_us_ascii(Charset) ? Charset : "unknown-8bit"));
813 /* Given a file with path ``s'', see if there is a registered MIME type.
814 * returns the major MIME type, and copies the subtype to ``d''. First look
815 * for ~/.mime.types, then look in a system mime.types if we can find one.
816 * The longest match is used so that we can match `ps.gz' when `gz' also
820 int mutt_lookup_mime_type (BODY * att, const char *path)
824 char buf[LONG_STRING];
825 char subtype[STRING], xtype[STRING];
827 int szf, sze, cur_sze;
835 szf = m_strlen(path);
837 for (count = 0; count < 4; count++) {
839 * can't use strtok() because we use it in an inner loop below, so use
840 * a switch statement here instead.
844 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL (Homedir));
847 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
850 m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
853 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
856 goto bye; /* shouldn't happen */
859 if ((f = fopen (buf, "r")) != NULL) {
860 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
861 /* weed out any comments */
862 if ((p = strchr (buf, '#')))
865 /* remove any leading space. */
866 ct = vskipspaces(buf);
868 /* position on the next field in this line */
869 if ((p = strpbrk (ct, " \t")) == NULL)
874 /* cycle through the file extensions */
875 while ((p = strtok (p, " \t\n"))) {
877 if ((sze > cur_sze) && (szf >= sze) &&
878 (m_strcasecmp(path + szf - sze, p) == 0
879 || ascii_strcasecmp (path + szf - sze, p) == 0)
880 && (szf == sze || path[szf - sze - 1] == '.'))
882 /* get the content-type */
884 if ((p = strchr (ct, '/')) == NULL) {
885 /* malformed line, just skip it. */
890 for (q = p; *q && !ISSPACE (*q); q++);
892 m_strncpy(subtype, sizeof(subtype), p, q - p);
894 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
895 m_strcpy(xtype, sizeof(xtype), ct);
908 if (type != TYPEOTHER || *xtype != '\0') {
910 m_strreplace(&att->subtype, subtype);
911 m_strreplace(&att->xtype, xtype);
917 void mutt_message_to_7bit (BODY * a, FILE * fp)
919 char temp[_POSIX_PATH_MAX];
925 if (!a->filename && fp)
927 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
928 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
933 if (stat (a->filename, &sb) == -1) {
934 mutt_perror ("stat");
937 a->length = sb.st_size;
941 if (!(fpout = safe_fopen (temp, "w+"))) {
942 mutt_perror ("fopen");
946 fseeko (fpin, a->offset, 0);
947 a->parts = mutt_parse_messageRFC822 (fpin, a);
949 transform_to_7bit (a->parts, fpin);
951 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
952 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
954 fputs ("MIME-Version: 1.0\n", fpout);
955 mutt_write_mime_header (a->parts, fpout);
957 mutt_write_mime_body (a->parts, fpout);
969 a->encoding = ENC7BIT;
970 a->d_filename = a->filename;
971 if (a->filename && a->unlink)
972 unlink (a->filename);
973 a->filename = m_strdup(temp);
975 if (stat (a->filename, &sb) == -1) {
976 mutt_perror ("stat");
979 a->length = sb.st_size;
980 body_list_wipe(&a->parts);
981 a->hdr->content = NULL;
984 static void transform_to_7bit (BODY * a, FILE * fpin)
986 char buff[_POSIX_PATH_MAX];
991 for (; a; a = a->next) {
992 if (a->type == TYPEMULTIPART) {
993 if (a->encoding != ENC7BIT)
994 a->encoding = ENC7BIT;
996 transform_to_7bit (a->parts, fpin);
998 else if (mutt_is_message_type (a->type, a->subtype)) {
999 mutt_message_to_7bit (a, fpin);
1003 a->force_charset = 1;
1006 if ((s.fpout = safe_fopen (buff, "w")) == NULL) {
1007 mutt_perror ("fopen");
1011 mutt_decode_attachment (a, &s);
1013 a->d_filename = a->filename;
1014 a->filename = m_strdup(buff);
1016 if (stat (a->filename, &sb) == -1) {
1017 mutt_perror ("stat");
1020 a->length = sb.st_size;
1022 mutt_update_encoding (a);
1023 if (a->encoding == ENC8BIT)
1024 a->encoding = ENCQUOTEDPRINTABLE;
1025 else if (a->encoding == ENCBINARY)
1026 a->encoding = ENCBASE64;
1031 /* determine which Content-Transfer-Encoding to use */
1032 static void mutt_set_encoding (BODY * b, CONTENT * info)
1034 char send_charset[SHORT_STRING];
1036 if (b->type == TYPETEXT) {
1038 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1039 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1040 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1041 b->encoding = ENCQUOTEDPRINTABLE;
1042 else if (info->hibin)
1043 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1045 b->encoding = ENC7BIT;
1047 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1048 if (info->lobin || info->hibin) {
1049 if (option (OPTALLOW8BIT) && !info->lobin)
1050 b->encoding = ENC8BIT;
1052 mutt_message_to_7bit (b, NULL);
1055 b->encoding = ENC7BIT;
1057 else if (b->type == TYPEAPPLICATION
1058 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1059 b->encoding = ENC7BIT;
1062 /* Determine which encoding is smaller */
1063 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1064 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1065 b->encoding = ENCBASE64;
1067 b->encoding = ENCQUOTEDPRINTABLE;
1071 void mutt_stamp_attachment (BODY * a)
1073 a->stamp = time (NULL);
1076 /* Get a body's character set */
1078 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1082 if (b && b->type != TYPETEXT)
1085 p = b ? parameter_getval(b->parameter, "charset") : NULL;
1086 charset_canonicalize(d, dlen, p);
1091 /* Assumes called from send mode where BODY->filename points to actual file */
1092 void mutt_update_encoding (BODY * a)
1095 char chsbuff[STRING];
1097 /* override noconv when it's us-ascii */
1098 if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1101 if (!a->force_charset && !a->noconv)
1102 parameter_delval(&a->parameter, "charset");
1104 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1107 mutt_set_encoding (a, info);
1108 mutt_stamp_attachment (a);
1110 p_delete(&a->content);
1115 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1117 char buffer[LONG_STRING];
1120 int cmflags, chflags;
1121 int pgp = hdr->security;
1123 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1124 (hdr->security & ENCRYPT)) {
1125 if (!crypt_valid_passphrase (hdr->security))
1129 mutt_mktemp (buffer);
1130 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1134 body->type = TYPEMESSAGE;
1135 body->subtype = m_strdup("rfc822");
1136 body->filename = m_strdup(buffer);
1139 body->disposition = DISPINLINE;
1142 mutt_parse_mime_message (ctx, hdr);
1147 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1148 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1149 chflags |= CH_MIME | CH_TXTPLAIN;
1150 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1151 pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1153 else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1154 if (mutt_is_multipart_encrypted (hdr->content)) {
1155 chflags |= CH_MIME | CH_NONEWLINE;
1156 cmflags = M_CM_DECODE_PGP;
1159 else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1160 chflags |= CH_MIME | CH_TXTPLAIN;
1161 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1164 else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1165 chflags |= CH_MIME | CH_TXTPLAIN;
1166 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1167 pgp &= ~SMIMEENCRYPT;
1171 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1176 body->hdr = header_new();
1177 body->hdr->offset = 0;
1178 /* we don't need the user headers here */
1179 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1180 body->hdr->security = pgp;
1181 mutt_update_encoding (body);
1182 body->parts = body->hdr->content;
1189 BODY *mutt_make_file_attach (const char *path)
1195 att->filename = m_strdup(path);
1197 /* Attempt to determine the appropriate content-type based on the filename
1200 mutt_lookup_mime_type (att, path);
1202 if ((info = mutt_get_content_info (path, att)) == NULL) {
1203 body_list_wipe(&att);
1207 if (!att->subtype) {
1208 if (info->lobin == 0
1209 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1211 * Statistically speaking, there should be more than 10% "lobin"
1212 * chars if this is really a binary file...
1214 att->type = TYPETEXT;
1215 att->subtype = m_strdup("plain");
1217 att->type = TYPEAPPLICATION;
1218 att->subtype = m_strdup("octet-stream");
1222 mutt_update_encoding (att);
1226 static int get_toplevel_encoding (BODY * a)
1230 for (; a; a = a->next) {
1231 if (a->encoding == ENCBINARY)
1233 else if (a->encoding == ENC8BIT)
1240 BODY *mutt_make_multipart (BODY * b)
1245 new->type = TYPEMULTIPART;
1246 new->subtype = m_strdup("mixed");
1247 new->encoding = get_toplevel_encoding (b);
1248 parameter_set_boundary(&new->parameter);
1250 new->disposition = DISPINLINE;
1256 /* remove the multipart body if it exists */
1257 BODY *mutt_remove_multipart (BODY * b)
1270 char *mutt_make_date (char *s, ssize_t len)
1272 time_t t = time (NULL);
1273 struct tm *l = localtime (&t);
1274 time_t tz = mutt_local_tz (t);
1278 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1279 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1280 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1281 (int) tz / 60, (int) abs (tz) % 60);
1285 /* wrapper around mutt_write_address() so we can handle very large
1286 recipient lists without needing a huge temporary buffer in memory */
1287 void mutt_write_address_list (address_t * adr, FILE * fp, int linelen,
1291 char buf[LONG_STRING];
1299 rfc822_write_address (buf, sizeof (buf), adr, display);
1300 len = m_strlen(buf);
1301 if (count && linelen + len > 74) {
1303 linelen = len + 8; /* tab is usually about 8 spaces... */
1306 if (count && adr->mailbox) {
1314 if (!adr->group && adr->next && adr->next->mailbox) {
1324 /* arbitrary number of elements to grow the array by */
1329 /* need to write the list in reverse because they are stored in reverse order
1330 * when parsed to speed up threading
1332 void mutt_write_references (string_list_t * r, FILE * f)
1334 string_list_t **ref = NULL;
1335 int refcnt = 0, refmax = 0;
1337 for (; (TrimRef == 0 || refcnt < TrimRef) && r; r = r->next) {
1338 if (refcnt == refmax)
1339 p_realloc(&ref, refmax += REF_INC);
1343 while (refcnt-- > 0) {
1345 fputs (ref[refcnt]->data, f);
1351 /* Note: all RFC2047 encoding should be done outside of this routine, except
1352 * for the "real name." This will allow this routine to be used more than
1353 * once, if necessary.
1355 * Likewise, all IDN processing should happen outside of this routine.
1357 * mode == 1 => "lite" mode (used for edit_hdrs)
1358 * mode == 0 => normal mode. write full header + MIME headers
1359 * mode == -1 => write just the envelope info (used for postponing messages)
1361 * privacy != 0 => will omit any headers which may identify the user.
1362 * Output generated is suitable for being sent through
1363 * anonymous remailer chains.
1367 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1368 int mode, int privacy)
1370 char buffer[LONG_STRING];
1372 string_list_t *tmp = env->userhdrs;
1373 int has_agent = 0; /* user defined user-agent header field exists */
1374 list2_t* hdrs = list_from_str (EditorHeaders, " ");
1377 if (!option (OPTNEWSSEND))
1379 if (mode == 0 && !privacy)
1380 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1382 #define EDIT_HEADER(x) (mode != 1 || option(OPTXMAILTO) || (mode == 1 && list_lookup(hdrs,(list_lookup_t*) ascii_strcasecmp,x) >= 0))
1384 /* OPTUSEFROM is not consulted here so that we can still write a From:
1385 * field if the user sets it with the `my_hdr' command
1387 if (env->from && !privacy) {
1389 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1390 fprintf (fp, "From: %s\n", buffer);
1395 mutt_write_address_list (env->to, fp, 4, 0);
1399 if (!option (OPTNEWSSEND))
1401 if (EDIT_HEADER("To:"))
1402 fputs ("To:\n", fp);
1406 mutt_write_address_list (env->cc, fp, 4, 0);
1410 if (!option (OPTNEWSSEND))
1412 if (EDIT_HEADER("Cc:"))
1413 fputs ("Cc:\n", fp);
1416 if (mode != 0 || option (OPTWRITEBCC)) {
1417 fputs ("Bcc: ", fp);
1418 mutt_write_address_list (env->bcc, fp, 5, 0);
1423 if (!option (OPTNEWSSEND))
1425 if (EDIT_HEADER("Bcc:"))
1426 fputs ("Bcc:\n", fp);
1429 if (env->newsgroups)
1430 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1431 else if (mode == 1 && option (OPTNEWSSEND) && EDIT_HEADER("Newsgroups:"))
1432 fputs ("Newsgroups:\n", fp);
1434 if (env->followup_to)
1435 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1436 else if (mode == 1 && option (OPTNEWSSEND) && EDIT_HEADER("Followup-To:"))
1437 fputs ("Followup-To:\n", fp);
1439 if (env->x_comment_to)
1440 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1441 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO) &&
1442 EDIT_HEADER("X-Comment-To:"))
1443 fputs ("X-Comment-To:\n", fp);
1447 fprintf (fp, "Subject: %s\n", env->subject);
1448 else if (mode == 1 && EDIT_HEADER("Subject:"))
1449 fputs ("Subject:\n", fp);
1451 /* save message id if the user has set it */
1452 if (env->message_id && !privacy)
1453 fprintf (fp, "Message-ID: %s\n", env->message_id);
1455 if (env->reply_to) {
1456 fputs ("Reply-To: ", fp);
1457 mutt_write_address_list (env->reply_to, fp, 10, 0);
1459 else if (mode > 0 && EDIT_HEADER("Reply-To:"))
1460 fputs ("Reply-To:\n", fp);
1462 if (env->mail_followup_to)
1464 if (!option (OPTNEWSSEND))
1467 fputs ("Mail-Followup-To: ", fp);
1468 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1472 if (env->references) {
1473 fputs ("References:", fp);
1474 mutt_write_references (env->references, fp);
1478 /* Add the MIME headers */
1479 fputs ("MIME-Version: 1.0\n", fp);
1480 mutt_write_mime_header (attach, fp);
1483 if (env->in_reply_to) {
1484 fputs ("In-Reply-To:", fp);
1485 mutt_write_references (env->in_reply_to, fp);
1491 /* Add any user defined headers */
1492 for (; tmp; tmp = tmp->next) {
1493 if ((p = strchr (tmp->data, ':'))) {
1494 p = vskipspaces(p + 1);
1496 continue; /* don't emit empty fields. */
1498 /* check to see if the user has overridden the user-agent field */
1499 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1505 fputs (tmp->data, fp);
1510 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1513 if (OperatingSystem != NULL) {
1514 os = OperatingSystem;
1517 os = (uname(&un) == -1) ? "UNIX" : un.sysname;
1519 /* Add a vanity header */
1520 fprintf (fp, "User-Agent: %s (%s)\n", mutt_make_version (0), os);
1523 list_del (&hdrs, (list_del_t*)xmemfree);
1525 return (ferror (fp) == 0 ? 0 : -1);
1528 static void encode_headers (string_list_t * h)
1534 for (; h; h = h->next) {
1535 if (!(p = strchr (h->data, ':')))
1539 p = vskipspaces(p + 1);
1545 rfc2047_encode_string (&tmp);
1546 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1548 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
1554 const char *mutt_fqdn (short may_hide_host)
1558 if (Fqdn && Fqdn[0] != '@') {
1561 if (may_hide_host && option (OPTHIDDENHOST)) {
1562 if ((p = strchr (Fqdn, '.')))
1565 /* sanity check: don't hide the host if
1566 * the fqdn is something like detebe.org.
1569 if (!p || !(q = strchr (p, '.')))
1577 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1578 static char mutt_normalized_char(char c)
1580 return (isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.';
1583 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1585 #define APPEND_FMT(fmt, arg) \
1587 int snlen = snprintf(buf, len, fmt, arg); \
1592 #define APPEND_BYTE(c) \
1606 static char MsgIdPfx = 'A';
1610 APPEND_BYTE(mutt_normalized_char(c));
1618 APPEND_FMT("%02d", tm->tm_mday);
1621 APPEND_FMT("%02d", tm->tm_hour);
1624 APPEND_FMT("%02d", tm->tm_mon + 1);
1627 APPEND_FMT("%02d", tm->tm_min);
1630 APPEND_FMT("%lo", (unsigned long)now);
1633 APPEND_FMT("%u", (unsigned int)getpid());
1636 APPEND_FMT("%c", MsgIdPfx);
1637 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1640 APPEND_FMT("%u", (unsigned int)rand());
1643 APPEND_FMT("%x", (unsigned int)rand());
1646 APPEND_FMT("%02d", tm->tm_sec);
1649 APPEND_FMT("%u", (unsigned int) now);
1652 APPEND_FMT("%x", (unsigned int) now);
1654 case 'Y': /* this will break in the year 10000 ;-) */
1655 APPEND_FMT("%04d", tm->tm_year + 1900);
1660 default: /* invalid formats are replaced by '.' */
1662 m_strncat(buf, len, ".", 1);
1672 char *mutt_gen_msgid (void)
1674 char buf[SHORT_STRING];
1675 char localpart[SHORT_STRING];
1676 unsigned int localpart_length;
1679 if (!(fqdn = mutt_fqdn (0)))
1680 fqdn = NONULL (Hostname);
1682 localpart_length = sizeof (buf) - m_strlen(fqdn) - 4; /* the 4 characters are '<', '@', '>' and '\0' */
1684 mutt_gen_localpart (localpart, localpart_length, MsgIdFormat);
1686 snprintf (buf, sizeof (buf), "<%s@%s>", localpart, fqdn);
1687 return (m_strdup(buf));
1690 static RETSIGTYPE alarm_handler (int sig)
1695 /* invoke sendmail in a subshell
1696 path (in) path to program to execute
1697 args (in) arguments to pass to program
1698 msg (in) temp file containing message to send
1699 tempfile (out) if sendmail is put in the background, this points
1700 to the temporary file containing the stdout of the
1703 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1709 mutt_block_signals_system ();
1712 /* we also don't want to be stopped right now */
1713 sigaddset (&set, SIGTSTP);
1714 sigprocmask (SIG_BLOCK, &set, NULL);
1716 if (SendmailWait >= 0) {
1717 char tmp[_POSIX_PATH_MAX];
1720 *tempfile = m_strdup(tmp);
1723 if ((pid = fork ()) == 0) {
1724 struct sigaction act, oldalrm;
1726 /* save parent's ID before setsid() */
1729 /* we want the delivery to continue even after the main process dies,
1730 * so we put ourselves into another session right away
1734 /* next we close all open files */
1735 for (fd = 0; fd < getdtablesize(); fd++)
1738 /* now the second fork() */
1739 if ((pid = fork ()) == 0) {
1740 /* "msg" will be opened as stdin */
1741 if (open (msg, O_RDONLY, 0) < 0) {
1747 if (SendmailWait >= 0) {
1748 /* *tempfile will be opened as stdout */
1749 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1752 /* redirect stderr to *tempfile too */
1757 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1759 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1763 execv (path, (char**)args);
1766 else if (pid == -1) {
1772 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1773 * SendmailWait = 0: wait forever
1774 * SendmailWait < 0: don't wait
1776 if (SendmailWait > 0) {
1778 act.sa_handler = alarm_handler;
1780 /* need to make sure waitpid() is interrupted on SIGALRM */
1781 act.sa_flags = SA_INTERRUPT;
1785 sigemptyset (&act.sa_mask);
1786 sigaction (SIGALRM, &act, &oldalrm);
1787 alarm (SendmailWait);
1789 else if (SendmailWait < 0)
1790 _exit (0xff & EX_OK);
1792 if (waitpid (pid, &st, 0) > 0) {
1793 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1794 if (SendmailWait && st == (0xff & EX_OK)) {
1795 unlink (*tempfile); /* no longer needed */
1800 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1801 if (SendmailWait > 0) {
1807 /* reset alarm; not really needed, but... */
1809 sigaction (SIGALRM, &oldalrm, NULL);
1811 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1812 /* the parent is already dead */
1820 sigprocmask (SIG_UNBLOCK, &set, NULL);
1822 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1823 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1825 st = S_ERR; /* error */
1827 mutt_unblock_signals_system (1);
1832 static const char **
1833 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1835 for (; addr; addr = addr->next) {
1836 /* weed out group mailboxes, since those are for display only */
1837 if (addr->mailbox && !addr->group) {
1838 if (*argslen == *argsmax)
1839 p_realloc(&args, *argsmax += 5);
1840 args[(*argslen)++] = addr->mailbox;
1846 static const char **
1847 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1849 if (*argslen == *argsmax) {
1850 p_realloc(&args, *argsmax += 5);
1852 args[(*argslen)++] = s;
1856 static int mutt_invoke_sendmail (address_t * from, /* the sender */
1857 address_t * to, address_t * cc, address_t * bcc, /* recips */
1858 const char *msg, /* file containing message */
1860 { /* message contains 8bit chars */
1861 char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
1862 const char **args = NULL;
1863 ssize_t argslen = 0, argsmax = 0;
1867 if (option (OPTNEWSSEND)) {
1868 char cmd[LONG_STRING];
1870 mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0,
1873 i = nntp_post (msg);
1882 s = m_strdup(Sendmail);
1886 while ((ps = strtok (ps, " "))) {
1887 if (argslen == argsmax)
1888 p_realloc(&args, argsmax += 5);
1891 args[argslen++] = ps;
1893 path = m_strdup(ps);
1894 ps = strrchr (ps, '/');
1899 args[argslen++] = ps;
1906 if (!option (OPTNEWSSEND)) {
1908 if (eightbit && option (OPTUSE8BITMIME))
1909 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1911 if (option (OPTENVFROM)) {
1912 address_t *f = NULL;
1915 else if (from && !from->next)
1918 args = add_option (args, &argslen, &argsmax, "-f");
1919 args = add_args (args, &argslen, &argsmax, f);
1923 args = add_option (args, &argslen, &argsmax, "-N");
1924 args = add_option (args, &argslen, &argsmax, DsnNotify);
1927 args = add_option (args, &argslen, &argsmax, "-R");
1928 args = add_option (args, &argslen, &argsmax, DsnReturn);
1930 args = add_option (args, &argslen, &argsmax, "--");
1931 args = add_args (args, &argslen, &argsmax, to);
1932 args = add_args (args, &argslen, &argsmax, cc);
1933 args = add_args (args, &argslen, &argsmax, bcc);
1938 if (argslen == argsmax)
1939 p_realloc(&args, ++argsmax);
1941 args[argslen++] = NULL;
1943 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1945 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1950 if (stat (childout, &st) == 0 && st.st_size > 0)
1951 mutt_do_pager (_("Output of the delivery process"), childout, 0,
1959 p_delete(&childout);
1964 if (i == (EX_OK & 0xff))
1966 else if (i == S_BKG)
1973 int mutt_invoke_mta (address_t * from, /* the sender */
1974 address_t * to, address_t * cc, address_t * bcc, /* recips */
1975 const char *msg, /* file containing message */
1977 { /* message contains 8bit chars */
1980 if (!option (OPTNEWSSEND))
1983 return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
1986 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
1989 /* For postponing (!final) do the necessary encodings only */
1990 void mutt_prepare_envelope (ENVELOPE * env, int final)
1992 char buffer[LONG_STRING];
1995 if (env->bcc && !(env->to || env->cc)) {
1996 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1997 * recipients if there is no To: or Cc: field, so attempt to suppress
1998 * it by using an empty To: field.
2000 env->to = address_new ();
2002 env->to->next = address_new ();
2005 rfc822_strcpy(buffer, sizeof(buffer), "undisclosed-recipients",
2008 env->to->mailbox = m_strdup(buffer);
2011 mutt_set_followup_to (env);
2013 if (!env->message_id && MsgIdFormat && *MsgIdFormat)
2014 env->message_id = mutt_gen_msgid ();
2017 /* Take care of 8-bit => 7-bit conversion. */
2018 rfc2047_encode_adrlist (env->to, "To");
2019 rfc2047_encode_adrlist (env->cc, "Cc");
2020 rfc2047_encode_adrlist (env->bcc, "Bcc");
2021 rfc2047_encode_adrlist (env->from, "From");
2022 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2023 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2027 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2030 rfc2047_encode_string (&env->subject);
2032 encode_headers (env->userhdrs);
2035 void mutt_unprepare_envelope (ENVELOPE * env)
2037 string_list_t *item;
2039 for (item = env->userhdrs; item; item = item->next)
2040 rfc2047_decode (&item->data);
2042 address_list_wipe(&env->mail_followup_to);
2044 /* back conversions */
2045 rfc2047_decode_adrlist (env->to);
2046 rfc2047_decode_adrlist (env->cc);
2047 rfc2047_decode_adrlist (env->bcc);
2048 rfc2047_decode_adrlist (env->from);
2049 rfc2047_decode_adrlist (env->reply_to);
2050 rfc2047_decode (&env->subject);
2053 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
2054 const char *resent_from, address_t * env_from)
2058 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2059 MESSAGE *msg = NULL;
2062 /* Try to bounce each message out, aborting if we get any failures. */
2063 for (i = 0; i < Context->msgcount; i++)
2064 if (Context->hdrs[i]->tagged)
2066 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2071 /* If we failed to open a message, return with error */
2072 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2078 mutt_mktemp (tempfile);
2079 if ((f = safe_fopen (tempfile, "w")) != NULL) {
2080 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2082 if (!option (OPTBOUNCEDELIVERED))
2083 ch_flags |= CH_WEED_DELIVERED;
2085 fseeko (fp, h->offset, 0);
2086 fprintf (f, "Resent-From: %s", resent_from);
2087 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2088 if (MsgIdFormat && *MsgIdFormat)
2089 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid ());
2090 fputs ("Resent-To: ", f);
2091 mutt_write_address_list (to, f, 11, 0);
2092 mutt_copy_header (fp, h, f, ch_flags, NULL);
2094 mutt_copy_bytes (fp, f, h->content->length);
2097 ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2098 h->content->encoding == ENC8BIT);
2102 mx_close_message (&msg);
2107 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2110 const char *fqdn = mutt_fqdn (1);
2111 char resent_from[STRING];
2115 resent_from[0] = '\0';
2116 from = mutt_default_from ();
2119 rfc822_qualify (from, fqdn);
2121 rfc2047_encode_adrlist (from, "Resent-From");
2122 if (mutt_addrlist_to_idna (from, &err)) {
2123 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2126 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2129 unset_option (OPTNEWSSEND);
2132 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2134 address_list_wipe(&from);
2139 static void set_noconv_flags (BODY * b, short flag)
2141 for (; b; b = b->next) {
2142 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2143 set_noconv_flags (b->parts, flag);
2144 else if (b->type == TYPETEXT && b->noconv) {
2145 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2150 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2151 int post, char *fcc)
2155 char tempfile[_POSIX_PATH_MAX];
2156 FILE *tempfp = NULL;
2160 set_noconv_flags (hdr->content, 1);
2162 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2166 /* We need to add a Content-Length field to avoid problems where a line in
2167 * the message body begins with "From "
2169 if (f.magic == M_MMDF || f.magic == M_MBOX) {
2170 mutt_mktemp (tempfile);
2171 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL) {
2172 mutt_perror (tempfile);
2173 mx_close_mailbox (&f, NULL);
2178 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2179 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2180 mx_close_mailbox (&f, NULL);
2184 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2185 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2187 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0,
2190 /* (postponment) if this was a reply of some sort, <msgid> contians the
2191 * Message-ID: of message replied to. Save it using a special X-Mutt-
2192 * header so it can be picked up if the message is recalled at a later
2193 * point in time. This will allow the message to be marked as replied if
2194 * the same mailbox is still open.
2197 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2199 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2200 * it can be picked up when the message is recalled
2203 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2204 fprintf (msg->fp, "Status: RO\n");
2208 /* (postponment) if the mail is to be signed or encrypted, save this info */
2209 if (post && (hdr->security & APPLICATION_PGP)) {
2210 fputs ("X-Mutt-PGP: ", msg->fp);
2211 if (hdr->security & ENCRYPT)
2212 fputc ('E', msg->fp);
2213 if (hdr->security & SIGN) {
2214 fputc ('S', msg->fp);
2215 if (PgpSignAs && *PgpSignAs)
2216 fprintf (msg->fp, "<%s>", PgpSignAs);
2218 if (hdr->security & INLINE)
2219 fputc ('I', msg->fp);
2220 fputc ('\n', msg->fp);
2223 /* (postponment) if the mail is to be signed or encrypted, save this info */
2224 if (post && (hdr->security & APPLICATION_SMIME)) {
2225 fputs ("X-Mutt-SMIME: ", msg->fp);
2226 if (hdr->security & ENCRYPT) {
2227 fputc ('E', msg->fp);
2228 if (SmimeCryptAlg && *SmimeCryptAlg)
2229 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2231 if (hdr->security & SIGN) {
2232 fputc ('S', msg->fp);
2233 if (SmimeDefaultKey && *SmimeDefaultKey)
2234 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2236 if (hdr->security & INLINE)
2237 fputc ('I', msg->fp);
2238 fputc ('\n', msg->fp);
2242 /* (postponement) if the mail is to be sent through a mixmaster
2243 * chain, save that information
2246 if (post && hdr->chain && hdr->chain) {
2249 fputs ("X-Mutt-Mix:", msg->fp);
2250 for (p = hdr->chain; p; p = p->next)
2251 fprintf (msg->fp, " %s", (char *) p->data);
2253 fputc ('\n', msg->fp);
2258 char sasha[LONG_STRING];
2261 mutt_write_mime_body (hdr->content, tempfp);
2263 /* make sure the last line ends with a newline. Emacs doesn't ensure
2264 * this will happen, and it can cause problems parsing the mailbox
2267 fseeko (tempfp, -1, 2);
2268 if (fgetc (tempfp) != '\n') {
2269 fseeko (tempfp, 0, 2);
2270 fputc ('\n', tempfp);
2274 if (ferror (tempfp)) {
2277 mx_commit_message (msg, &f); /* XXX - really? */
2278 mx_close_message (&msg);
2279 mx_close_mailbox (&f, NULL);
2283 /* count the number of lines */
2285 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2287 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2288 fprintf (msg->fp, "Lines: %d\n\n", lines);
2290 /* copy the body and clean up */
2292 r = mutt_copy_stream (tempfp, msg->fp);
2293 if (fclose (tempfp) != 0)
2295 /* if there was an error, leave the temp version */
2300 fputc ('\n', msg->fp); /* finish off the header */
2301 r = mutt_write_mime_body (hdr->content, msg->fp);
2304 if (mx_commit_message (msg, &f) != 0)
2306 mx_close_message (&msg);
2307 mx_close_mailbox (&f, NULL);
2310 set_noconv_flags (hdr->content, 0);