2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
10 #include <lib-lib/lib-lib.h>
13 #include <sys/utsname.h>
15 #include <lib-sys/exit.h>
16 #include <lib-sys/mutt_signal.h>
17 #include <lib-mime/mime.h>
18 #include <lib-ui/curses.h>
19 #include <lib-mx/mx.h>
21 #include <lib-crypt/crypt.h>
25 #include "recvattach.h"
29 #include "mutt_idna.h"
32 # include "mutt_libesmtp.h"
33 #endif /* USE_LIBESMTP */
36 #include <nntp/nntp.h>
39 #ifdef HAVE_SYSEXITS_H
41 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
45 static void transform_to_7bit (BODY * a, FILE * fpin);
47 static void encode_quoted (fgetconv_t * fc, FILE * fout, int istext)
50 char line[77], savechar;
52 while ((c = fgetconv (fc)) != EOF) {
53 /* Wrap the line if needed. */
54 if (linelen == 76 && ((istext && c != '\n') || !istext)) {
55 /* If the last character is "quoted", then be sure to move all three
56 * characters to the next line. Otherwise, just move the last
59 if (line[linelen - 3] == '=') {
60 line[linelen - 3] = 0;
65 line[1] = line[linelen - 2];
66 line[2] = line[linelen - 1];
70 savechar = line[linelen - 1];
71 line[linelen - 1] = '=';
80 /* Escape lines that begin with/only contain "the message separator". */
81 if (linelen == 4 && !m_strncmp("From", line, 4)) {
82 m_strcpy(line, sizeof(line), "=46rom");
85 else if (linelen == 4 && !m_strncmp("from", line, 4)) {
86 m_strcpy(line, sizeof(line), "=66rom");
89 else if (linelen == 1 && line[0] == '.') {
90 m_strcpy(line, sizeof(line), "=2E");
95 if (c == '\n' && istext) {
96 /* Check to make sure there is no trailing space on this line. */
98 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
100 sprintf (line + linelen - 1, "=%2.2X",
101 (unsigned char) line[linelen - 1]);
105 savechar = line[linelen - 1];
107 line[linelen - 1] = '=';
110 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
120 else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
121 /* Check to make sure there is enough room for the quoted character.
122 * If not, wrap to the next line.
125 line[linelen++] = '=';
131 sprintf (line + linelen, "=%2.2X", (unsigned char) c);
135 /* Don't worry about wrapping the line here. That will happen during
136 * the next iteration when I'll also know what the next character is.
142 /* Take care of anything left in the buffer */
144 if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
145 /* take care of trailing whitespace */
147 sprintf (line + linelen - 1, "=%2.2X",
148 (unsigned char) line[linelen - 1]);
150 savechar = line[linelen - 1];
151 line[linelen - 1] = '=';
155 sprintf (line, "=%2.2X", (unsigned char) savechar);
164 static char b64_buffer[3];
165 static short b64_num;
166 static short b64_linelen;
168 static void b64_flush (FILE * fout)
175 if (b64_linelen >= 72) {
180 for (i = b64_num; i < 3; i++)
181 b64_buffer[i] = '\0';
183 fputc(__m_b64chars[(b64_buffer[0] >> 2) & 0x3f], fout);
186 [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
191 [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
195 fputc (__m_b64chars[b64_buffer[2] & 0x3f], fout);
200 while (b64_linelen % 4) {
209 static void b64_putc (char c, FILE * fout)
214 b64_buffer[b64_num++] = c;
218 static void encode_base64 (fgetconv_t * fc, FILE * fout, int istext)
222 b64_num = b64_linelen = 0;
224 while ((ch = fgetconv (fc)) != EOF) {
225 if (istext && ch == '\n' && ch1 != '\r')
226 b64_putc ('\r', fout);
234 static void encode_8bit (fgetconv_t * fc, FILE * fout,
235 int istext __attribute__ ((unused)))
239 while ((ch = fgetconv (fc)) != EOF)
244 int mutt_write_mime_header (BODY * a, FILE * f)
253 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
258 len = 25 + m_strlen(a->subtype); /* approximate len. of content-type */
260 for (p = a->parameter; p; p = p->next) {
269 tmp = m_strdup(p->value);
270 encode = rfc2231_encode_string (&tmp);
271 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
273 /* Dirty hack to make messages readable by Outlook Express
274 * for the Mac: force quotes around the boundary parameter
275 * even when they aren't needed.
278 if (!ascii_strcasecmp (p->attribute, "boundary")
279 && !strcmp (buffer, tmp))
280 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
284 tmplen = m_strlen(buffer) + m_strlen(p->attribute) + 1;
286 if (len + tmplen + 2 > 76) {
295 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
303 fprintf (f, "Content-Description: %s\n", a->description);
305 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
306 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
309 if (!(fn = a->d_filename))
315 /* Strip off the leading path... */
316 if ((t = strrchr (fn, '/')))
323 encode = rfc2231_encode_string (&tmp);
324 rfc822_strcpy(buffer, sizeof(buffer), tmp, MimeSpecials);
326 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
332 if (a->encoding != ENC7BIT)
333 fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
335 /* Do NOT add the terminator here!!! */
336 return (ferror (f) ? -1 : 0);
339 int mutt_write_mime_body (BODY * a, FILE * f)
342 char boundary[SHORT_STRING];
343 char send_charset[SHORT_STRING];
348 if (a->type == TYPEMULTIPART) {
349 /* First, find the boundary to use */
350 if (!(p = parameter_getval(a->parameter, "boundary"))) {
351 mutt_error _("No boundary parameter found! [report this error]");
355 m_strcpy(boundary, sizeof(boundary), p);
357 for (t = a->parts; t; t = t->next) {
358 fprintf (f, "\n--%s\n", boundary);
359 if (mutt_write_mime_header (t, f) == -1)
362 if (mutt_write_mime_body (t, f) == -1)
365 fprintf (f, "\n--%s--\n", boundary);
366 return (ferror (f) ? -1 : 0);
369 /* This is pretty gross, but it's the best solution for now... */
370 if (a->type == TYPEAPPLICATION && !m_strcmp(a->subtype, "pgp-encrypted")) {
371 fputs ("Version: 1\n", f);
375 if ((fpin = fopen (a->filename, "r")) == NULL) {
376 mutt_error (_("%s no longer exists!"), a->filename);
380 if (a->type == TYPETEXT && (!a->noconv))
381 fc = fgetconv_open (fpin, a->file_charset,
382 mutt_get_body_charset (send_charset,
383 sizeof (send_charset), a), 0);
385 fc = fgetconv_open (fpin, 0, 0, 0);
387 #define write_as_text_part(a) (mutt_is_text_part(a) || mutt_is_application_pgp(a))
388 if (a->encoding == ENCQUOTEDPRINTABLE)
389 encode_quoted (fc, f, write_as_text_part (a));
390 else if (a->encoding == ENCBASE64)
391 encode_base64 (fc, f, write_as_text_part (a));
392 else if (a->type == TYPETEXT && (!a->noconv))
393 encode_8bit (fc, f, write_as_text_part (a));
395 mutt_copy_stream (fpin, f);
396 #undef write_as_text_part
398 fgetconv_close (&fc);
401 return (ferror (f) ? -1 : 0);
413 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
417 int whitespace = s->whitespace;
419 int linelen = s->linelen;
420 int was_cr = s->was_cr;
422 if (!d) { /* This signals EOF */
425 if (linelen > info->linemax)
426 info->linemax = linelen;
431 for (; dlen; d++, dlen--) {
444 if (linelen > info->linemax)
445 info->linemax = linelen;
460 if (linelen > info->linemax)
461 info->linemax = linelen;
466 else if (ch == '\r') {
474 else if (ch == '\t' || ch == '\f') {
478 else if (ch < 32 || ch == 127)
482 if ((ch == 'F') || (ch == 'f'))
492 if (linelen == 2 && ch != 'r')
494 else if (linelen == 3 && ch != 'o')
496 else if (linelen == 4) {
509 if (ch != ' ' && ch != '\t')
514 s->whitespace = whitespace;
516 s->linelen = linelen;
522 * Find the best charset conversion of the file from fromcode into one
523 * of the tocodes. If successful, set *tocode and CONTENT *info and
524 * return the number of characters converted inexactly. If no
525 * conversion was possible, return -1.
527 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
528 * which would otherwise prevent us from knowing the number of inexact
529 * conversions. Where the candidate target charset is UTF-8 we avoid
530 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
531 * fails with some libraries.
533 * We assume that the output from iconv is never more than 4 times as
534 * long as the input for any pair of charsets we might be interested
537 static ssize_t convert_file_to (FILE * file, const char *fromcode,
538 int ncodes, const char **tocodes,
539 int *tocode, CONTENT * info)
543 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
546 ssize_t ibl, obl, ubl, ubl1, n, ret;
549 CONTENT_STATE *states;
552 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
553 if (cd1 == MUTT_ICONV_ERROR)
556 cd = p_new(iconv_t, ncodes);
557 score = p_new(ssize_t, ncodes);
558 states = p_new(CONTENT_STATE, ncodes);
559 infos = p_new(CONTENT, ncodes);
561 for (i = 0; i < ncodes; i++)
562 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
563 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
565 /* Special case for conversion to UTF-8 */
566 cd[i] = MUTT_ICONV_ERROR, score[i] = -1;
572 /* Try to fill input buffer */
573 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
576 /* Convert to UTF-8 */
578 ob = bufu, obl = sizeof (bufu);
579 n = my_iconv(cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
580 if (n == -1 && ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
586 /* Convert from UTF-8 */
587 for (i = 0; i < ncodes; i++)
588 if (cd[i] != MUTT_ICONV_ERROR && score[i] != -1) {
589 ub = bufu, ubl = ubl1;
590 ob = bufo, obl = sizeof (bufo);
591 n = my_iconv(cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
597 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
600 else if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1)
601 /* Special case for conversion to UTF-8 */
602 update_content_info (&infos[i], &states[i], bufu, ubl1);
605 /* Save unused input */
606 memmove (bufi, ib, ibl);
607 else if (!ubl1 && ib < bufi + sizeof (bufi)) {
614 /* Find best score */
616 for (i = 0; i < ncodes; i++) {
617 if (cd[i] == MUTT_ICONV_ERROR && score[i] == -1) {
618 /* Special case for conversion to UTF-8 */
623 else if (cd[i] == MUTT_ICONV_ERROR || score[i] == -1)
625 else if (ret == -1 || score[i] < ret) {
633 memcpy (info, &infos[*tocode], sizeof (CONTENT));
634 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
638 for (i = 0; i < ncodes; i++)
639 if (cd[i] != MUTT_ICONV_ERROR)
651 #endif /* !HAVE_ICONV */
655 * Find the first of the fromcodes that gives a valid conversion and
656 * the best charset conversion of the file into one of the tocodes. If
657 * successful, set *fromcode and *tocode to dynamically allocated
658 * strings, set CONTENT *info, and return the number of characters
659 * converted inexactly. If no conversion was possible, return -1.
661 * Both fromcodes and tocodes may be colon-separated lists of charsets.
662 * However, if fromcode is zero then fromcodes is assumed to be the
663 * name of a single charset even if it contains a colon.
665 static ssize_t convert_file_from_to (FILE * file,
666 const char *fromcodes,
667 const char *tocodes, char **fromcode,
668 char **tocode, CONTENT * info)
676 /* Count the tocodes */
678 for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
679 if ((c1 = strchr (c, ':')) == c)
685 tcode = p_new(char *, ncodes);
686 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
687 if ((c1 = strchr (c, ':')) == c)
689 tcode[i] = m_substrdup(c, c1);
694 /* Try each fromcode in turn */
695 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
696 if ((c1 = strchr (c, ':')) == c)
698 fcode = m_substrdup(c, c1);
700 ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
712 /* There is only one fromcode */
713 ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
722 for (i = 0; i < ncodes; i++)
731 * Analyze the contents of a file to determine which MIME encoding to use.
732 * Also set the body charset, sometimes, or not.
734 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
739 char *fromcode = NULL;
750 if (stat (fname, &sb) == -1) {
751 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
755 if (!S_ISREG (sb.st_mode)) {
756 mutt_error (_("%s isn't a regular file."), fname);
760 if ((fp = fopen (fname, "r")) == NULL) {
764 info = p_new(CONTENT, 1);
767 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
768 const char *chs = parameter_getval(b->parameter, "charset");
769 char *fchs = b->use_disp ? ((FileCharset && *FileCharset) ?
770 FileCharset : Charset) : Charset;
771 if (Charset && (chs || SendCharset) &&
772 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
773 &fromcode, &tocode, info) != -1) {
775 charset_canonicalize (chsbuf, sizeof (chsbuf), tocode);
776 parameter_setval(&b->parameter, "charset", chsbuf);
778 b->file_charset = fromcode;
786 while ((r = fread (buffer, 1, sizeof (buffer), fp)))
787 update_content_info (info, &state, buffer, r);
788 update_content_info (info, &state, 0, 0);
792 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
793 parameter_setval(&b->parameter, "charset",
794 (!info->hibin ? "us-ascii"
795 : Charset && !charset_is_us_ascii(Charset) ? Charset : "unknown-8bit"));
800 /* Given a file with path ``s'', see if there is a registered MIME type.
801 * returns the major MIME type, and copies the subtype to ``d''. First look
802 * for ~/.mime.types, then look in a system mime.types if we can find one.
803 * The longest match is used so that we can match `ps.gz' when `gz' also
807 int mutt_lookup_mime_type (BODY * att, const char *path)
811 char buf[LONG_STRING];
812 char subtype[STRING], xtype[STRING];
814 int szf, sze, cur_sze;
822 szf = m_strlen(path);
824 for (count = 0; count < 4; count++) {
826 * can't use strtok() because we use it in an inner loop below, so use
827 * a switch statement here instead.
831 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL (Homedir));
834 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/madmutt-mime.types");
837 m_strcpy(buf, sizeof(buf), PKGDATADIR "/mime.types");
840 m_strcpy(buf, sizeof(buf), SYSCONFDIR "/mime.types");
843 goto bye; /* shouldn't happen */
846 if ((f = fopen (buf, "r")) != NULL) {
847 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
848 /* weed out any comments */
849 if ((p = strchr (buf, '#')))
852 /* remove any leading space. */
853 ct = vskipspaces(buf);
855 /* position on the next field in this line */
856 if ((p = strpbrk (ct, " \t")) == NULL)
861 /* cycle through the file extensions */
862 while ((p = strtok (p, " \t\n"))) {
864 if ((sze > cur_sze) && (szf >= sze) &&
865 (m_strcasecmp(path + szf - sze, p) == 0
866 || ascii_strcasecmp (path + szf - sze, p) == 0)
867 && (szf == sze || path[szf - sze - 1] == '.'))
869 /* get the content-type */
871 if ((p = strchr (ct, '/')) == NULL) {
872 /* malformed line, just skip it. */
877 for (q = p; *q && !ISSPACE (*q); q++);
879 m_strncpy(subtype, sizeof(subtype), p, q - p);
881 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
882 m_strcpy(xtype, sizeof(xtype), ct);
895 if (type != TYPEOTHER || *xtype != '\0') {
897 m_strreplace(&att->subtype, subtype);
898 m_strreplace(&att->xtype, xtype);
904 void mutt_message_to_7bit (BODY * a, FILE * fp)
906 char temp[_POSIX_PATH_MAX];
912 if (!a->filename && fp)
914 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
915 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
920 if (stat (a->filename, &sb) == -1) {
921 mutt_perror ("stat");
924 a->length = sb.st_size;
927 fpout = m_tempfile(temp, sizeof(temp), NONULL(Tempdir), NULL);
929 mutt_error(_("Could not create temporary file"));
933 fseeko (fpin, a->offset, 0);
934 a->parts = mutt_parse_messageRFC822 (fpin, a);
936 transform_to_7bit (a->parts, fpin);
938 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
939 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
941 fputs ("MIME-Version: 1.0\n", fpout);
942 mutt_write_mime_header (a->parts, fpout);
944 mutt_write_mime_body (a->parts, fpout);
956 a->encoding = ENC7BIT;
957 a->d_filename = a->filename;
958 if (a->filename && a->unlink)
959 unlink (a->filename);
960 a->filename = m_strdup(temp);
962 if (stat (a->filename, &sb) == -1) {
963 mutt_perror ("stat");
966 a->length = sb.st_size;
967 body_list_wipe(&a->parts);
968 a->hdr->content = NULL;
971 static void transform_to_7bit (BODY * a, FILE * fpin)
973 char buff[_POSIX_PATH_MAX];
978 for (; a; a = a->next) {
979 if (a->type == TYPEMULTIPART) {
980 if (a->encoding != ENC7BIT)
981 a->encoding = ENC7BIT;
983 transform_to_7bit (a->parts, fpin);
985 else if (mutt_is_message_type (a->type, a->subtype)) {
986 mutt_message_to_7bit (a, fpin);
990 a->force_charset = 1;
992 s.fpout = m_tempfile(buff, sizeof(buff), NONULL(Tempdir), NULL);
994 mutt_error(_("Could not create temporary file"));
998 mutt_decode_attachment (a, &s);
1000 a->d_filename = a->filename;
1001 a->filename = m_strdup(buff);
1003 if (stat (a->filename, &sb) == -1) {
1004 mutt_perror ("stat");
1007 a->length = sb.st_size;
1009 mutt_update_encoding (a);
1010 if (a->encoding == ENC8BIT)
1011 a->encoding = ENCQUOTEDPRINTABLE;
1012 else if (a->encoding == ENCBINARY)
1013 a->encoding = ENCBASE64;
1018 /* determine which Content-Transfer-Encoding to use */
1019 static void mutt_set_encoding (BODY * b, CONTENT * info)
1021 char send_charset[SHORT_STRING];
1023 if (b->type == TYPETEXT) {
1025 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1026 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1027 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1028 b->encoding = ENCQUOTEDPRINTABLE;
1029 else if (info->hibin)
1030 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1032 b->encoding = ENC7BIT;
1034 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1035 if (info->lobin || info->hibin) {
1036 if (option (OPTALLOW8BIT) && !info->lobin)
1037 b->encoding = ENC8BIT;
1039 mutt_message_to_7bit (b, NULL);
1042 b->encoding = ENC7BIT;
1044 else if (b->type == TYPEAPPLICATION
1045 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1046 b->encoding = ENC7BIT;
1049 /* Determine which encoding is smaller */
1050 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1051 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1052 b->encoding = ENCBASE64;
1054 b->encoding = ENCQUOTEDPRINTABLE;
1058 void mutt_stamp_attachment (BODY * a)
1060 a->stamp = time (NULL);
1063 /* Get a body's character set */
1065 char *mutt_get_body_charset(char *d, ssize_t dlen, BODY * b)
1069 if (b && b->type != TYPETEXT)
1072 p = b ? parameter_getval(b->parameter, "charset") : NULL;
1073 charset_canonicalize(d, dlen, p);
1078 /* Assumes called from send mode where BODY->filename points to actual file */
1079 void mutt_update_encoding (BODY * a)
1082 char chsbuff[STRING];
1084 /* override noconv when it's us-ascii */
1085 if (charset_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1088 if (!a->force_charset && !a->noconv)
1089 parameter_delval(&a->parameter, "charset");
1091 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1094 mutt_set_encoding (a, info);
1095 mutt_stamp_attachment (a);
1097 p_delete(&a->content);
1102 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1104 char buffer[LONG_STRING];
1107 int cmflags, chflags;
1108 int pgp = hdr->security;
1110 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1111 (hdr->security & ENCRYPT)) {
1112 if (!crypt_valid_passphrase (hdr->security))
1116 fp = m_tempfile(buffer, sizeof(buffer), NONULL(Tempdir), NULL);
1121 body->type = TYPEMESSAGE;
1122 body->subtype = m_strdup("rfc822");
1123 body->filename = m_strdup(buffer);
1126 body->disposition = DISPINLINE;
1129 mutt_parse_mime_message (ctx, hdr);
1134 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1135 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1136 chflags |= CH_MIME | CH_TXTPLAIN;
1137 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1138 pgp &= ~(PGPENCRYPT|SMIMEENCRYPT);
1140 else if (option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1141 if (mutt_is_multipart_encrypted (hdr->content)) {
1142 chflags |= CH_MIME | CH_NONEWLINE;
1143 cmflags = M_CM_DECODE_PGP;
1146 else if (mutt_is_application_pgp (hdr->content) & PGPENCRYPT) {
1147 chflags |= CH_MIME | CH_TXTPLAIN;
1148 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1151 else if (mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1152 chflags |= CH_MIME | CH_TXTPLAIN;
1153 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1154 pgp &= ~SMIMEENCRYPT;
1158 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1163 body->hdr = header_new();
1164 body->hdr->offset = 0;
1165 /* we don't need the user headers here */
1166 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1167 body->hdr->security = pgp;
1168 mutt_update_encoding (body);
1169 body->parts = body->hdr->content;
1176 BODY *mutt_make_file_attach (const char *path)
1182 att->filename = m_strdup(path);
1184 /* Attempt to determine the appropriate content-type based on the filename
1187 mutt_lookup_mime_type (att, path);
1189 if ((info = mutt_get_content_info (path, att)) == NULL) {
1190 body_list_wipe(&att);
1194 if (!att->subtype) {
1195 if (info->lobin == 0
1196 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1198 * Statistically speaking, there should be more than 10% "lobin"
1199 * chars if this is really a binary file...
1201 att->type = TYPETEXT;
1202 att->subtype = m_strdup("plain");
1204 att->type = TYPEAPPLICATION;
1205 att->subtype = m_strdup("octet-stream");
1209 mutt_update_encoding (att);
1213 static int get_toplevel_encoding (BODY * a)
1217 for (; a; a = a->next) {
1218 if (a->encoding == ENCBINARY)
1220 else if (a->encoding == ENC8BIT)
1227 BODY *mutt_make_multipart (BODY * b)
1232 new->type = TYPEMULTIPART;
1233 new->subtype = m_strdup("mixed");
1234 new->encoding = get_toplevel_encoding (b);
1235 parameter_set_boundary(&new->parameter);
1237 new->disposition = DISPINLINE;
1243 /* remove the multipart body if it exists */
1244 BODY *mutt_remove_multipart (BODY * b)
1257 char *mutt_make_date (char *s, ssize_t len)
1259 time_t t = time (NULL);
1260 struct tm *l = localtime (&t);
1261 time_t tz = mutt_local_tz (t);
1265 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1266 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1267 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1268 (int) tz / 60, (int) abs (tz) % 60);
1272 /* wrapper around mutt_write_address() so we can handle very large
1273 recipient lists without needing a huge temporary buffer in memory */
1274 void mutt_write_address_list (address_t * adr, FILE * fp, int linelen,
1278 char buf[LONG_STRING];
1286 rfc822_write_address (buf, sizeof (buf), adr, display);
1287 len = m_strlen(buf);
1288 if (count && linelen + len > 74) {
1290 linelen = len + 8; /* tab is usually about 8 spaces... */
1293 if (count && adr->mailbox) {
1301 if (!adr->group && adr->next && adr->next->mailbox) {
1311 /* arbitrary number of elements to grow the array by */
1316 /* need to write the list in reverse because they are stored in reverse order
1317 * when parsed to speed up threading
1319 void mutt_write_references (string_list_t * r, FILE * f)
1321 string_list_t **ref = NULL;
1322 int refcnt = 0, refmax = 0;
1324 for (; (TrimRef == 0 || refcnt < TrimRef) && r; r = r->next) {
1325 if (refcnt == refmax)
1326 p_realloc(&ref, refmax += REF_INC);
1330 while (refcnt-- > 0) {
1332 fputs (ref[refcnt]->data, f);
1338 static int edit_header(int mode, const char *s)
1341 int slen = m_strlen(s);
1343 if (mode != 1 || option(OPTXMAILTO))
1346 p = skipspaces(EditorHeaders);
1348 if (!ascii_strncasecmp(p, s, slen) && p[slen - 1] == ':')
1350 p = skipspaces(p + slen);
1356 /* Note: all RFC2047 encoding should be done outside of this routine, except
1357 * for the "real name." This will allow this routine to be used more than
1358 * once, if necessary.
1360 * Likewise, all IDN processing should happen outside of this routine.
1362 * mode == 1 => "lite" mode (used for edit_hdrs)
1363 * mode == 0 => normal mode. write full header + MIME headers
1364 * mode == -1 => write just the envelope info (used for postponing messages)
1366 * privacy != 0 => will omit any headers which may identify the user.
1367 * Output generated is suitable for being sent through
1368 * anonymous remailer chains.
1372 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1373 int mode, int privacy)
1375 char buffer[LONG_STRING];
1377 string_list_t *tmp = env->userhdrs;
1378 int has_agent = 0; /* user defined user-agent header field exists */
1381 if (!option (OPTNEWSSEND))
1383 if (mode == 0 && !privacy)
1384 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1386 /* OPTUSEFROM is not consulted here so that we can still write a From:
1387 * field if the user sets it with the `my_hdr' command
1389 if (env->from && !privacy) {
1391 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1392 fprintf (fp, "From: %s\n", buffer);
1397 mutt_write_address_list (env->to, fp, 4, 0);
1401 if (!option (OPTNEWSSEND))
1403 if (edit_header(mode, "To:"))
1404 fputs ("To:\n", fp);
1408 mutt_write_address_list (env->cc, fp, 4, 0);
1412 if (!option (OPTNEWSSEND))
1414 if (edit_header(mode, "Cc:"))
1415 fputs ("Cc:\n", fp);
1418 if (mode != 0 || option (OPTWRITEBCC)) {
1419 fputs ("Bcc: ", fp);
1420 mutt_write_address_list (env->bcc, fp, 5, 0);
1425 if (!option (OPTNEWSSEND))
1427 if (edit_header(mode, "Bcc:"))
1428 fputs ("Bcc:\n", fp);
1431 if (env->newsgroups)
1432 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1433 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Newsgroups:"))
1434 fputs ("Newsgroups:\n", fp);
1436 if (env->followup_to)
1437 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1438 else if (mode == 1 && option (OPTNEWSSEND) && edit_header(mode, "Followup-To:"))
1439 fputs ("Followup-To:\n", fp);
1441 if (env->x_comment_to)
1442 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1443 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO) &&
1444 edit_header(mode, "X-Comment-To:"))
1445 fputs ("X-Comment-To:\n", fp);
1449 fprintf (fp, "Subject: %s\n", env->subject);
1450 else if (mode == 1 && edit_header(mode, "Subject:"))
1451 fputs ("Subject:\n", fp);
1453 /* save message id if the user has set it */
1454 if (env->message_id && !privacy)
1455 fprintf (fp, "Message-ID: %s\n", env->message_id);
1457 if (env->reply_to) {
1458 fputs ("Reply-To: ", fp);
1459 mutt_write_address_list (env->reply_to, fp, 10, 0);
1461 else if (mode > 0 && edit_header(mode, "Reply-To:"))
1462 fputs ("Reply-To:\n", fp);
1464 if (env->mail_followup_to)
1466 if (!option (OPTNEWSSEND))
1469 fputs ("Mail-Followup-To: ", fp);
1470 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1474 if (env->references) {
1475 fputs ("References:", fp);
1476 mutt_write_references (env->references, fp);
1480 /* Add the MIME headers */
1481 fputs ("MIME-Version: 1.0\n", fp);
1482 mutt_write_mime_header (attach, fp);
1485 if (env->in_reply_to) {
1486 fputs ("In-Reply-To:", fp);
1487 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 return (ferror (fp) == 0 ? 0 : -1);
1526 static void encode_headers (string_list_t * h)
1532 for (; h; h = h->next) {
1533 if (!(p = strchr (h->data, ':')))
1537 p = vskipspaces(p + 1);
1543 rfc2047_encode_string (&tmp);
1544 p_realloc(&h->data, m_strlen(h->data) + 2 + m_strlen(tmp) + 1);
1546 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
1552 const char *mutt_fqdn (short may_hide_host)
1556 if (Fqdn && Fqdn[0] != '@') {
1559 if (may_hide_host && option (OPTHIDDENHOST)) {
1560 if ((p = strchr (Fqdn, '.')))
1563 /* sanity check: don't hide the host if
1564 * the fqdn is something like detebe.org.
1567 if (!p || !(q = strchr (p, '.')))
1575 /* normalized character (we're stricter than RFC2822, 3.6.4) */
1576 static char mutt_normalized_char(char c)
1578 return (isalnum(c) || strchr(".!#$%&'*+-/=?^_`{|}~", c)) ? c : '.';
1581 static void mutt_gen_localpart(char *buf, unsigned int len, const char *fmt)
1583 #define APPEND_FMT(fmt, arg) \
1585 int snlen = snprintf(buf, len, fmt, arg); \
1590 #define APPEND_BYTE(c) \
1604 static char MsgIdPfx = 'A';
1608 APPEND_BYTE(mutt_normalized_char(c));
1616 APPEND_FMT("%02d", tm->tm_mday);
1619 APPEND_FMT("%02d", tm->tm_hour);
1622 APPEND_FMT("%02d", tm->tm_mon + 1);
1625 APPEND_FMT("%02d", tm->tm_min);
1628 APPEND_FMT("%lo", (unsigned long)now);
1631 APPEND_FMT("%u", (unsigned int)getpid());
1634 APPEND_FMT("%c", MsgIdPfx);
1635 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1638 APPEND_FMT("%u", (unsigned int)rand());
1641 APPEND_FMT("%x", (unsigned int)rand());
1644 APPEND_FMT("%02d", tm->tm_sec);
1647 APPEND_FMT("%u", (unsigned int) now);
1650 APPEND_FMT("%x", (unsigned int) now);
1652 case 'Y': /* this will break in the year 10000 ;-) */
1653 APPEND_FMT("%04d", tm->tm_year + 1900);
1658 default: /* invalid formats are replaced by '.' */
1660 m_strncat(buf, len, ".", 1);
1670 static char *mutt_gen_msgid (void)
1672 char buf[SHORT_STRING];
1673 char localpart[SHORT_STRING];
1676 if (!(fqdn = mutt_fqdn(0)))
1677 fqdn = NONULL(Hostname);
1679 mutt_gen_localpart(localpart, sizeof(localpart), MsgIdFormat);
1680 snprintf(buf, sizeof(buf), "<%s@%s>", localpart, fqdn);
1681 return m_strdup(buf);
1684 static RETSIGTYPE alarm_handler (int sig __attribute__ ((unused)))
1689 /* invoke sendmail in a subshell
1690 path (in) path to program to execute
1691 args (in) arguments to pass to program
1692 msg (in) temp file containing message to send
1693 tempfile (out) if sendmail is put in the background, this points
1694 to the temporary file containing the stdout of the
1697 send_msg(const char *path, const char **args, const char *msg, char **tempfile)
1703 mutt_block_signals_system ();
1706 /* we also don't want to be stopped right now */
1707 sigaddset (&set, SIGTSTP);
1708 sigprocmask (SIG_BLOCK, &set, NULL);
1710 if (SendmailWait >= 0) {
1711 char tmp[_POSIX_PATH_MAX];
1714 *tempfile = m_strdup(tmp);
1717 if ((pid = fork ()) == 0) {
1718 struct sigaction act, oldalrm;
1720 /* save parent's ID before setsid() */
1723 /* we want the delivery to continue even after the main process dies,
1724 * so we put ourselves into another session right away
1728 /* next we close all open files */
1729 for (fd = 0; fd < getdtablesize(); fd++)
1732 /* now the second fork() */
1733 if ((pid = fork ()) == 0) {
1734 /* "msg" will be opened as stdin */
1735 if (open (msg, O_RDONLY, 0) < 0) {
1741 if (SendmailWait >= 0) {
1742 /* *tempfile will be opened as stdout */
1743 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1746 /* redirect stderr to *tempfile too */
1751 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1753 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1757 execv (path, (char**)args);
1760 else if (pid == -1) {
1766 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1767 * SendmailWait = 0: wait forever
1768 * SendmailWait < 0: don't wait
1770 if (SendmailWait > 0) {
1772 act.sa_handler = alarm_handler;
1774 /* need to make sure waitpid() is interrupted on SIGALRM */
1775 act.sa_flags = SA_INTERRUPT;
1779 sigemptyset (&act.sa_mask);
1780 sigaction (SIGALRM, &act, &oldalrm);
1781 alarm (SendmailWait);
1783 else if (SendmailWait < 0)
1784 _exit (0xff & EX_OK);
1786 if (waitpid (pid, &st, 0) > 0) {
1787 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1788 if (SendmailWait && st == (0xff & EX_OK)) {
1789 unlink (*tempfile); /* no longer needed */
1794 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1795 if (SendmailWait > 0) {
1801 /* reset alarm; not really needed, but... */
1803 sigaction (SIGALRM, &oldalrm, NULL);
1805 if (kill (ppid, 0) == -1 && errno == ESRCH) {
1806 /* the parent is already dead */
1814 sigprocmask (SIG_UNBLOCK, &set, NULL);
1816 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1817 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1819 st = S_ERR; /* error */
1821 mutt_unblock_signals_system (1);
1826 static const char **
1827 add_args(const char **args, ssize_t *argslen, ssize_t *argsmax, address_t * addr)
1829 for (; addr; addr = addr->next) {
1830 /* weed out group mailboxes, since those are for display only */
1831 if (addr->mailbox && !addr->group) {
1832 if (*argslen == *argsmax)
1833 p_realloc(&args, *argsmax += 5);
1834 args[(*argslen)++] = addr->mailbox;
1840 static const char **
1841 add_option(const char **args, ssize_t *argslen, ssize_t *argsmax, const char *s)
1843 if (*argslen == *argsmax) {
1844 p_realloc(&args, *argsmax += 5);
1846 args[(*argslen)++] = s;
1850 static int mutt_invoke_sendmail (address_t * from, /* the sender */
1851 address_t * to, address_t * cc, address_t * bcc, /* recips */
1852 const char *msg, /* file containing message */
1854 { /* message contains 8bit chars */
1855 char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
1856 const char **args = NULL;
1857 ssize_t argslen = 0, argsmax = 0;
1861 if (option (OPTNEWSSEND)) {
1862 char cmd[LONG_STRING];
1864 mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0,
1867 i = nntp_post (msg);
1876 s = m_strdup(Sendmail);
1880 while ((ps = strtok (ps, " "))) {
1881 if (argslen == argsmax)
1882 p_realloc(&args, argsmax += 5);
1885 args[argslen++] = ps;
1887 path = m_strdup(ps);
1888 ps = strrchr (ps, '/');
1893 args[argslen++] = ps;
1900 if (!option (OPTNEWSSEND)) {
1902 if (eightbit && option (OPTUSE8BITMIME))
1903 args = add_option(args, &argslen, &argsmax, "-B8BITMIME");
1905 if (option (OPTENVFROM)) {
1906 address_t *f = NULL;
1909 else if (from && !from->next)
1912 args = add_option (args, &argslen, &argsmax, "-f");
1913 args = add_args (args, &argslen, &argsmax, f);
1917 args = add_option (args, &argslen, &argsmax, "-N");
1918 args = add_option (args, &argslen, &argsmax, DsnNotify);
1921 args = add_option (args, &argslen, &argsmax, "-R");
1922 args = add_option (args, &argslen, &argsmax, DsnReturn);
1924 args = add_option (args, &argslen, &argsmax, "--");
1925 args = add_args (args, &argslen, &argsmax, to);
1926 args = add_args (args, &argslen, &argsmax, cc);
1927 args = add_args (args, &argslen, &argsmax, bcc);
1932 if (argslen == argsmax)
1933 p_realloc(&args, ++argsmax);
1935 args[argslen++] = NULL;
1937 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
1939 mutt_error (_("Error sending message, child exited %d (%s)."), i,
1944 if (stat (childout, &st) == 0 && st.st_size > 0)
1945 mutt_do_pager (_("Output of the delivery process"), childout, 0,
1953 p_delete(&childout);
1958 if (i == (EX_OK & 0xff))
1960 else if (i == S_BKG)
1967 int mutt_invoke_mta (address_t * from, /* the sender */
1968 address_t * to, address_t * cc, address_t * bcc, /* recips */
1969 const char *msg, /* file containing message */
1971 { /* message contains 8bit chars */
1974 if (!option (OPTNEWSSEND))
1977 return mutt_libesmtp_invoke (from, to, cc, bcc, msg, eightbit);
1980 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
1983 /* For postponing (!final) do the necessary encodings only */
1984 void mutt_prepare_envelope (ENVELOPE * env, int final)
1986 char buffer[LONG_STRING];
1989 if (env->bcc && !(env->to || env->cc)) {
1990 /* some MTA's will put an Apparently-To: header field showing the Bcc:
1991 * recipients if there is no To: or Cc: field, so attempt to suppress
1992 * it by using an empty To: field.
1994 env->to = address_new ();
1996 env->to->next = address_new ();
1999 rfc822_strcpy(buffer, sizeof(buffer), "undisclosed-recipients",
2002 env->to->mailbox = m_strdup(buffer);
2005 mutt_set_followup_to (env);
2007 if (!env->message_id && MsgIdFormat && *MsgIdFormat)
2008 env->message_id = mutt_gen_msgid ();
2011 /* Take care of 8-bit => 7-bit conversion. */
2012 rfc2047_encode_adrlist (env->to, "To");
2013 rfc2047_encode_adrlist (env->cc, "Cc");
2014 rfc2047_encode_adrlist (env->bcc, "Bcc");
2015 rfc2047_encode_adrlist (env->from, "From");
2016 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2017 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2021 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2024 rfc2047_encode_string (&env->subject);
2026 encode_headers (env->userhdrs);
2029 void mutt_unprepare_envelope (ENVELOPE * env)
2031 string_list_t *item;
2033 for (item = env->userhdrs; item; item = item->next)
2034 rfc2047_decode (&item->data);
2036 address_list_wipe(&env->mail_followup_to);
2038 /* back conversions */
2039 rfc2047_decode_adrlist (env->to);
2040 rfc2047_decode_adrlist (env->cc);
2041 rfc2047_decode_adrlist (env->bcc);
2042 rfc2047_decode_adrlist (env->from);
2043 rfc2047_decode_adrlist (env->reply_to);
2044 rfc2047_decode (&env->subject);
2047 static int _mutt_bounce_message (FILE * fp, HEADER * h, address_t * to,
2048 const char *resent_from, address_t * env_from)
2052 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2053 MESSAGE *msg = NULL;
2056 /* Try to bounce each message out, aborting if we get any failures. */
2057 for (i = 0; i < Context->msgcount; i++)
2058 if (Context->hdrs[i]->tagged)
2060 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2065 /* If we failed to open a message, return with error */
2066 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2072 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
2074 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2076 if (!option (OPTBOUNCEDELIVERED))
2077 ch_flags |= CH_WEED_DELIVERED;
2079 fseeko (fp, h->offset, 0);
2080 fprintf (f, "Resent-From: %s", resent_from);
2081 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2082 if (MsgIdFormat && *MsgIdFormat)
2083 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid ());
2084 fputs ("Resent-To: ", f);
2085 mutt_write_address_list (to, f, 11, 0);
2086 mutt_copy_header (fp, h, f, ch_flags, NULL);
2088 mutt_copy_bytes (fp, f, h->content->length);
2091 ret = mutt_invoke_mta(env_from, to, NULL, NULL, tempfile,
2092 h->content->encoding == ENC8BIT);
2096 mx_close_message (&msg);
2101 int mutt_bounce_message (FILE * fp, HEADER * h, address_t * to)
2104 const char *fqdn = mutt_fqdn (1);
2105 char resent_from[STRING];
2109 resent_from[0] = '\0';
2110 from = mutt_default_from ();
2113 rfc822_qualify (from, fqdn);
2115 rfc2047_encode_adrlist (from, "Resent-From");
2116 if (mutt_addrlist_to_idna (from, &err)) {
2117 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2120 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2123 unset_option (OPTNEWSSEND);
2126 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2128 address_list_wipe(&from);
2133 static void set_noconv_flags (BODY * b, short flag)
2135 for (; b; b = b->next) {
2136 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2137 set_noconv_flags (b->parts, flag);
2138 else if (b->type == TYPETEXT && b->noconv) {
2139 parameter_setval(&b->parameter, "x-mutt-noconv", flag ? "yes" : NULL);
2144 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2145 int post, char *fcc)
2149 char tempfile[_POSIX_PATH_MAX];
2150 FILE *tempfp = NULL;
2154 set_noconv_flags (hdr->content, 1);
2156 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2160 /* We need to add a Content-Length field to avoid problems where a line in
2161 * the message body begins with "From "
2163 if (f.magic == M_MMDF || f.magic == M_MBOX) {
2164 tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
2166 mutt_error(_("Could not create temporary file"));
2167 mx_close_mailbox (&f, NULL);
2172 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2173 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2174 mx_close_mailbox (&f, NULL);
2178 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2179 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2181 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0,
2184 /* (postponment) if this was a reply of some sort, <msgid> contians the
2185 * Message-ID: of message replied to. Save it using a special X-Mutt-
2186 * header so it can be picked up if the message is recalled at a later
2187 * point in time. This will allow the message to be marked as replied if
2188 * the same mailbox is still open.
2191 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2193 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2194 * it can be picked up when the message is recalled
2197 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2198 fprintf (msg->fp, "Status: RO\n");
2202 /* (postponment) if the mail is to be signed or encrypted, save this info */
2203 if (post && (hdr->security & APPLICATION_PGP)) {
2204 fputs ("X-Mutt-PGP: ", msg->fp);
2205 if (hdr->security & ENCRYPT)
2206 fputc ('E', msg->fp);
2207 if (hdr->security & SIGN) {
2208 fputc ('S', msg->fp);
2209 if (PgpSignAs && *PgpSignAs)
2210 fprintf (msg->fp, "<%s>", PgpSignAs);
2212 if (hdr->security & INLINE)
2213 fputc ('I', msg->fp);
2214 fputc ('\n', msg->fp);
2217 /* (postponment) if the mail is to be signed or encrypted, save this info */
2218 if (post && (hdr->security & APPLICATION_SMIME)) {
2219 fputs ("X-Mutt-SMIME: ", msg->fp);
2220 if (hdr->security & ENCRYPT) {
2221 fputc ('E', msg->fp);
2222 if (SmimeCryptAlg && *SmimeCryptAlg)
2223 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2225 if (hdr->security & SIGN) {
2226 fputc ('S', msg->fp);
2227 if (SmimeDefaultKey && *SmimeDefaultKey)
2228 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2230 if (hdr->security & INLINE)
2231 fputc ('I', msg->fp);
2232 fputc ('\n', msg->fp);
2236 /* (postponement) if the mail is to be sent through a mixmaster
2237 * chain, save that information
2240 if (post && hdr->chain && hdr->chain) {
2243 fputs ("X-Mutt-Mix:", msg->fp);
2244 for (p = hdr->chain; p; p = p->next)
2245 fprintf (msg->fp, " %s", (char *) p->data);
2247 fputc ('\n', msg->fp);
2252 char sasha[LONG_STRING];
2255 mutt_write_mime_body (hdr->content, tempfp);
2257 /* make sure the last line ends with a newline. Emacs doesn't ensure
2258 * this will happen, and it can cause problems parsing the mailbox
2261 fseeko (tempfp, -1, 2);
2262 if (fgetc (tempfp) != '\n') {
2263 fseeko (tempfp, 0, 2);
2264 fputc ('\n', tempfp);
2268 if (ferror (tempfp)) {
2271 mx_commit_message (msg, &f); /* XXX - really? */
2272 mx_close_message (&msg);
2273 mx_close_mailbox (&f, NULL);
2277 /* count the number of lines */
2279 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2281 fprintf (msg->fp, "Content-Length: %zd\n", ftello (tempfp));
2282 fprintf (msg->fp, "Lines: %d\n\n", lines);
2284 /* copy the body and clean up */
2286 r = mutt_copy_stream (tempfp, msg->fp);
2287 if (m_fclose(&tempfp) != 0)
2289 /* if there was an error, leave the temp version */
2294 fputc ('\n', msg->fp); /* finish off the header */
2295 r = mutt_write_mime_body (hdr->content, msg->fp);
2298 if (mx_commit_message (msg, &f) != 0)
2300 mx_close_message (&msg);
2301 mx_close_mailbox (&f, NULL);
2304 set_noconv_flags (hdr->content, 0);