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 #if defined(OPEN_MAX)
1736 for (fd = 0; fd < OPEN_MAX; fd++)
1738 #elif defined(_POSIX_OPEN_MAX)
1739 for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1747 /* now the second fork() */
1748 if ((pid = fork ()) == 0) {
1749 /* "msg" will be opened as stdin */
1750 if (open (msg, O_RDONLY, 0) < 0) {
1756 if (SendmailWait >= 0) {
1757 /* *tempfile will be opened as stdout */
1758 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1761 /* redirect stderr to *tempfile too */
1766 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1768 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1772 execv (path, (char**)args);
1775 else if (pid == -1) {
1781 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1782 * SendmailWait = 0: wait forever
1783 * SendmailWait < 0: don't wait
1785 if (SendmailWait > 0) {
1787 act.sa_handler = alarm_handler;
1789 /* need to make sure waitpid() is interrupted on SIGALRM */
1790 act.sa_flags = SA_INTERRUPT;
1794 sigemptyset (&act.sa_mask);
1795 sigaction (SIGALRM, &act, &oldalrm);
1796 alarm (SendmailWait);
1798 else if (SendmailWait < 0)
1799 _exit (0xff & EX_OK);
1801 if (waitpid (pid, &st, 0) > 0) {
1802 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1803 if (SendmailWait && st == (0xff & EX_OK)) {
1804 unlink (*tempfile); /* no longer needed */
1809 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1810 if (SendmailWait > 0) {
1816 /* reset alarm; not really needed, but... */
1818 sigaction (SIGALRM, &oldalrm, NULL);
1820 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1821 /* the parent is already dead */
1829 sigprocmask (SIG_UNBLOCK, &set, NULL);
1831 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1832 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1834 st = S_ERR; /* error */
1836 mutt_unblock_signals_system (1);
1841 static const char **
1842 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1844 for (; addr; addr = addr->next) {
1845 /* weed out group mailboxes, since those are for display only */
1846 if (addr->mailbox && !addr->group) {
1847 if (*argslen == *argsmax)
1848 p_realloc(&args, *argsmax += 5);
1849 args[(*argslen)++] = addr->mailbox;
1855 static const char **
1856 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1858 if (*argslen == *argsmax) {
1859 p_realloc(&args, *argsmax += 5);
1861 args[(*argslen)++] = s;
1865 static int mutt_invoke_sendmail (address_t * from, /* the sender */
1866 address_t * to, address_t * cc, address_t * bcc, /* recips */
1867 const char *msg, /* file containing message */
1869 { /* message contains 8bit chars */
1870 char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
1871 const char **args = NULL;
1872 ssize_t argslen = 0, argsmax = 0;
1876 if (option (OPTNEWSSEND)) {
1877 char cmd[LONG_STRING];
1879 mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0,
1882 i = nntp_post (msg);
1891 s = m_strdup(Sendmail);
1895 while ((ps = strtok (ps, " "))) {
1896 if (argslen == argsmax)
1897 p_realloc(&args, argsmax += 5);
1900 args[argslen++] = ps;
1902 path = m_strdup(ps);
1903 ps = strrchr (ps, '/');
1908 args[argslen++] = ps;
1915 if (!option (OPTNEWSSEND)) {
1917 if (eightbit && option (OPTUSE8BITMIME))
1918 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1920 if (option (OPTENVFROM)) {
1921 address_t *f = NULL;
1924 else if (from && !from->next)
1927 args = add_option (args, &argslen, &argsmax, "-f");
1928 args = add_args (args, &argslen, &argsmax, f);
1932 args = add_option (args, &argslen, &argsmax, "-N");
1933 args = add_option (args, &argslen, &argsmax, DsnNotify);
1936 args = add_option (args, &argslen, &argsmax, "-R");
1937 args = add_option (args, &argslen, &argsmax, DsnReturn);
1939 args = add_option (args, &argslen, &argsmax, "--");
1940 args = add_args (args, &argslen, &argsmax, to);
1941 args = add_args (args, &argslen, &argsmax, cc);
1942 args = add_args (args, &argslen, &argsmax, bcc);
1947 if (argslen == argsmax)
1948 p_realloc(&args, ++argsmax);
1950 args[argslen++] = NULL;
1952 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1954 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1959 if (stat (childout, &st) == 0 && st.st_size > 0)
1960 mutt_do_pager (_("Output of the delivery process"), childout, 0,
1968 p_delete(&childout);
1973 if (i == (EX_OK & 0xff))
1975 else if (i == S_BKG)
1982 int mutt_invoke_mta (address_t * from, /* the sender */
1983 address_t * to, address_t * cc, address_t * bcc, /* recips */
1984 const char *msg, /* file containing message */
1986 { /* message contains 8bit chars */
1989 if (!option (OPTNEWSSEND))
1992 return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
1995 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
1998 /* For postponing (!final) do the necessary encodings only */
1999 void mutt_prepare_envelope (ENVELOPE * env, int final)
2001 char buffer[LONG_STRING];
2004 if (env->bcc && !(env->to || env->cc)) {
2005 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2006 * recipients if there is no To: or Cc: field, so attempt to suppress
2007 * it by using an empty To: field.
2009 env->to = address_new ();
2011 env->to->next = address_new ();
2014 rfc822_strcpy(buffer, sizeof(buffer), "undisclosed-recipients",
2017 env->to->mailbox = m_strdup(buffer);
2020 mutt_set_followup_to (env);
2022 if (!env->message_id && MsgIdFormat && *MsgIdFormat)
2023 env->message_id = mutt_gen_msgid ();
2026 /* Take care of 8-bit => 7-bit conversion. */
2027 rfc2047_encode_adrlist (env->to, "To");
2028 rfc2047_encode_adrlist (env->cc, "Cc");
2029 rfc2047_encode_adrlist (env->bcc, "Bcc");
2030 rfc2047_encode_adrlist (env->from, "From");
2031 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2032 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2036 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2039 rfc2047_encode_string (&env->subject);
2041 encode_headers (env->userhdrs);
2044 void mutt_unprepare_envelope (ENVELOPE * env)
2046 string_list_t *item;
2048 for (item = env->userhdrs; item; item = item->next)
2049 rfc2047_decode (&item->data);
2051 address_list_wipe(&env->mail_followup_to);
2053 /* back conversions */
2054 rfc2047_decode_adrlist (env->to);
2055 rfc2047_decode_adrlist (env->cc);
2056 rfc2047_decode_adrlist (env->bcc);
2057 rfc2047_decode_adrlist (env->from);
2058 rfc2047_decode_adrlist (env->reply_to);
2059 rfc2047_decode (&env->subject);
2062 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
2063 const char *resent_from, address_t * env_from)
2067 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2068 MESSAGE *msg = NULL;
2071 /* Try to bounce each message out, aborting if we get any failures. */
2072 for (i = 0; i < Context->msgcount; i++)
2073 if (Context->hdrs[i]->tagged)
2075 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2080 /* If we failed to open a message, return with error */
2081 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2087 mutt_mktemp (tempfile);
2088 if ((f = safe_fopen (tempfile, "w")) != NULL) {
2089 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2091 if (!option (OPTBOUNCEDELIVERED))
2092 ch_flags |= CH_WEED_DELIVERED;
2094 fseeko (fp, h->offset, 0);
2095 fprintf (f, "Resent-From: %s", resent_from);
2096 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2097 if (MsgIdFormat && *MsgIdFormat)
2098 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid ());
2099 fputs ("Resent-To: ", f);
2100 mutt_write_address_list (to, f, 11, 0);
2101 mutt_copy_header (fp, h, f, ch_flags, NULL);
2103 mutt_copy_bytes (fp, f, h->content->length);
2106 ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2107 h->content->encoding == ENC8BIT);
2111 mx_close_message (&msg);
2116 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2119 const char *fqdn = mutt_fqdn (1);
2120 char resent_from[STRING];
2124 resent_from[0] = '\0';
2125 from = mutt_default_from ();
2128 rfc822_qualify (from, fqdn);
2130 rfc2047_encode_adrlist (from, "Resent-From");
2131 if (mutt_addrlist_to_idna (from, &err)) {
2132 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2135 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2138 unset_option (OPTNEWSSEND);
2141 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2143 address_list_wipe(&from);
2148 static void set_noconv_flags (BODY * b, short flag)
2150 for (; b; b = b->next) {
2151 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2152 set_noconv_flags (b->parts, flag);
2153 else if (b->type == TYPETEXT && b->noconv) {
2154 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2159 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2160 int post, char *fcc)
2164 char tempfile[_POSIX_PATH_MAX];
2165 FILE *tempfp = NULL;
2169 set_noconv_flags (hdr->content, 1);
2171 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2175 /* We need to add a Content-Length field to avoid problems where a line in
2176 * the message body begins with "From "
2178 if (f.magic == M_MMDF || f.magic == M_MBOX) {
2179 mutt_mktemp (tempfile);
2180 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL) {
2181 mutt_perror (tempfile);
2182 mx_close_mailbox (&f, NULL);
2187 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2188 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2189 mx_close_mailbox (&f, NULL);
2193 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2194 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2196 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0,
2199 /* (postponment) if this was a reply of some sort, <msgid> contians the
2200 * Message-ID: of message replied to. Save it using a special X-Mutt-
2201 * header so it can be picked up if the message is recalled at a later
2202 * point in time. This will allow the message to be marked as replied if
2203 * the same mailbox is still open.
2206 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2208 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2209 * it can be picked up when the message is recalled
2212 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2213 fprintf (msg->fp, "Status: RO\n");
2217 /* (postponment) if the mail is to be signed or encrypted, save this info */
2218 if (post && (hdr->security & APPLICATION_PGP)) {
2219 fputs ("X-Mutt-PGP: ", msg->fp);
2220 if (hdr->security & ENCRYPT)
2221 fputc ('E', msg->fp);
2222 if (hdr->security & SIGN) {
2223 fputc ('S', msg->fp);
2224 if (PgpSignAs && *PgpSignAs)
2225 fprintf (msg->fp, "<%s>", PgpSignAs);
2227 if (hdr->security & INLINE)
2228 fputc ('I', msg->fp);
2229 fputc ('\n', msg->fp);
2232 /* (postponment) if the mail is to be signed or encrypted, save this info */
2233 if (post && (hdr->security & APPLICATION_SMIME)) {
2234 fputs ("X-Mutt-SMIME: ", msg->fp);
2235 if (hdr->security & ENCRYPT) {
2236 fputc ('E', msg->fp);
2237 if (SmimeCryptAlg && *SmimeCryptAlg)
2238 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2240 if (hdr->security & SIGN) {
2241 fputc ('S', msg->fp);
2242 if (SmimeDefaultKey && *SmimeDefaultKey)
2243 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2245 if (hdr->security & INLINE)
2246 fputc ('I', msg->fp);
2247 fputc ('\n', msg->fp);
2251 /* (postponement) if the mail is to be sent through a mixmaster
2252 * chain, save that information
2255 if (post && hdr->chain && hdr->chain) {
2258 fputs ("X-Mutt-Mix:", msg->fp);
2259 for (p = hdr->chain; p; p = p->next)
2260 fprintf (msg->fp, " %s", (char *) p->data);
2262 fputc ('\n', msg->fp);
2267 char sasha[LONG_STRING];
2270 mutt_write_mime_body (hdr->content, tempfp);
2272 /* make sure the last line ends with a newline. Emacs doesn't ensure
2273 * this will happen, and it can cause problems parsing the mailbox
2276 fseeko (tempfp, -1, 2);
2277 if (fgetc (tempfp) != '\n') {
2278 fseeko (tempfp, 0, 2);
2279 fputc ('\n', tempfp);
2283 if (ferror (tempfp)) {
2286 mx_commit_message (msg, &f); /* XXX - really? */
2287 mx_close_message (&msg);
2288 mx_close_mailbox (&f, NULL);
2292 /* count the number of lines */
2294 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2296 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2297 fprintf (msg->fp, "Lines: %d\n\n", lines);
2299 /* copy the body and clean up */
2301 r = mutt_copy_stream (tempfp, msg->fp);
2302 if (fclose (tempfp) != 0)
2304 /* if there was an error, leave the temp version */
2309 fputc ('\n', msg->fp); /* finish off the header */
2310 r = mutt_write_mime_body (hdr->content, msg->fp);
2313 if (mx_commit_message (msg, &f) != 0)
2315 mx_close_message (&msg);
2316 mx_close_mailbox (&f, NULL);
2319 set_noconv_flags (hdr->content, 0);