2 * Copyright (C) 1996-2002,2009-2012 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include "mutt_curses.h"
36 #include "mutt_crypt.h"
37 #include "mutt_idna.h"
41 #include "autocrypt.h"
54 #ifdef HAVE_SYSEXITS_H
56 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
60 /* If you are debugging this file, comment out the following line. */
69 extern char RFC822Specials[];
71 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
73 const char B64Chars[64] = {
74 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
75 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
76 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
77 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
81 static char MsgIdPfx = 'A';
83 static void transform_to_7bit (BODY *a, FILE *fpin);
85 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
88 char line[77], savechar;
90 while ((c = fgetconv (fc)) != EOF)
92 /* Wrap the line if needed. */
93 if (linelen == 76 && ((istext && c != '\n') || !istext))
95 /* If the last character is "quoted", then be sure to move all three
96 * characters to the next line. Otherwise, just move the last
99 if (line[linelen-3] == '=')
106 line[1] = line[linelen-2];
107 line[2] = line[linelen-1];
112 savechar = line[linelen-1];
113 line[linelen-1] = '=';
122 /* Escape lines that begin with/only contain "the message separator". */
123 if (linelen == 4 && !mutt_strncmp ("From", line, 4))
125 strfcpy (line, "=46rom", sizeof (line));
128 else if (linelen == 4 && !mutt_strncmp ("from", line, 4))
130 strfcpy (line, "=66rom", sizeof (line));
133 else if (linelen == 1 && line[0] == '.')
135 strfcpy (line, "=2E", sizeof (line));
140 if (c == '\n' && istext)
142 /* Check to make sure there is no trailing space on this line. */
143 if (linelen > 0 && (line[linelen-1] == ' ' || line[linelen-1] == '\t'))
147 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
152 int savechar = line[linelen-1];
154 line[linelen-1] = '=';
157 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
168 else if (c != 9 && (c < 32 || c > 126 || c == '='))
170 /* Check to make sure there is enough room for the quoted character.
171 * If not, wrap to the next line.
175 line[linelen++] = '=';
181 sprintf (line+linelen,"=%2.2X", (unsigned char) c);
186 /* Don't worry about wrapping the line here. That will happen during
187 * the next iteration when I'll also know what the next character is.
193 /* Take care of anything left in the buffer */
196 if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
198 /* take care of trailing whitespace */
200 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
203 savechar = line[linelen-1];
204 line[linelen-1] = '=';
208 sprintf (line, "=%2.2X", (unsigned char) savechar);
217 static char b64_buffer[3];
218 static short b64_num;
219 static short b64_linelen;
221 static void b64_flush(FILE *fout)
228 if (b64_linelen >= 72)
234 for (i = b64_num; i < 3; i++)
235 b64_buffer[i] = '\0';
237 fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
239 fputc(B64Chars[((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf) ], fout);
244 fputc(B64Chars[((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3) ], fout);
248 fputc(B64Chars[b64_buffer[2] & 0x3f], fout);
253 while (b64_linelen % 4)
263 static void b64_putc(char c, FILE *fout)
268 b64_buffer[b64_num++] = c;
272 static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
276 b64_num = b64_linelen = 0;
278 while ((ch = fgetconv (fc)) != EOF)
280 if (istext && ch == '\n' && ch1 != '\r')
281 b64_putc('\r', fout);
289 static void encode_8bit (FGETCONV *fc, FILE *fout, int istext)
293 while ((ch = fgetconv (fc)) != EOF)
298 int mutt_write_mime_header (BODY *a, FILE *f)
301 PARAMETER *param_conts, *cont;
308 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
312 len = 25 + mutt_strlen (a->subtype); /* approximate len. of content-type */
314 for (p = a->parameter; p; p = p->next)
316 if (!(p->attribute && p->value))
319 param_conts = rfc2231_encode_string (p->attribute, p->value);
320 for (cont = param_conts; cont; cont = cont->next)
325 rfc822_cat (buffer, sizeof (buffer), cont->value, MimeSpecials);
327 /* Dirty hack to make messages readable by Outlook Express
328 * for the Mac: force quotes around the boundary parameter
329 * even when they aren't needed.
331 if (!ascii_strcasecmp (cont->attribute, "boundary") &&
332 !mutt_strcmp (buffer, cont->value))
333 snprintf (buffer, sizeof (buffer), "\"%s\"", cont->value);
335 tmplen = mutt_strlen (buffer) + mutt_strlen (cont->attribute) + 1;
336 if (len + tmplen + 2 > 76)
347 fprintf (f, "%s=%s", cont->attribute, buffer);
350 mutt_free_parameter (¶m_conts);
357 fprintf(f, "Content-Description: %s\n", a->description);
359 if (a->disposition != DISPNONE)
361 const char *dispstr[] = {
367 if (a->disposition < sizeof(dispstr)/sizeof(char*))
369 fprintf (f, "Content-Disposition: %s", dispstr[a->disposition]);
370 len = 21 + mutt_strlen (dispstr[a->disposition]);
374 if (!(fn = a->d_filename))
379 /* Strip off the leading path... */
380 if ((t = strrchr (fn, '/')))
385 param_conts = rfc2231_encode_string ("filename", t);
386 for (cont = param_conts; cont; cont = cont->next)
390 rfc822_cat (buffer, sizeof (buffer), cont->value, MimeSpecials);
392 tmplen = mutt_strlen (buffer) + mutt_strlen (cont->attribute) + 1;
393 if (len + tmplen + 2 > 76)
404 fprintf (f, "%s=%s", cont->attribute, buffer);
407 mutt_free_parameter (¶m_conts);
415 dprint(1, (debugfile, "ERROR: invalid content-disposition %d\n", a->disposition));
419 if (a->encoding != ENC7BIT)
420 fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
422 if ((option (OPTCRYPTPROTHDRSWRITE)
424 || option (OPTAUTOCRYPT)
429 mutt_write_rfc822_header (f, a->mime_headers, NULL, MUTT_WRITE_HEADER_MIME, 0, 0);
432 /* Do NOT add the terminator here!!! */
433 return (ferror (f) ? -1 : 0);
436 #define write_as_text_part(a) \
437 (mutt_is_text_part(a) || \
438 ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp(a)))
440 int mutt_write_mime_body (BODY *a, FILE *f)
442 char *p, boundary[SHORT_STRING];
443 char send_charset[SHORT_STRING];
448 if (a->type == TYPEMULTIPART)
450 /* First, find the boundary to use */
451 if (!(p = mutt_get_parameter ("boundary", a->parameter)))
453 dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
454 mutt_error _("No boundary parameter found! [report this error]");
457 strfcpy (boundary, p, sizeof (boundary));
459 for (t = a->parts; t ; t = t->next)
461 fprintf (f, "\n--%s\n", boundary);
462 if (mutt_write_mime_header (t, f) == -1)
465 if (mutt_write_mime_body (t, f) == -1)
468 fprintf (f, "\n--%s--\n", boundary);
469 return (ferror (f) ? -1 : 0);
472 /* This is pretty gross, but it's the best solution for now... */
473 if ((WithCrypto & APPLICATION_PGP)
474 && a->type == TYPEAPPLICATION
475 && mutt_strcmp (a->subtype, "pgp-encrypted") == 0
478 fputs ("Version: 1\n", f);
482 if ((fpin = fopen (a->filename, "r")) == NULL)
484 dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
485 mutt_error (_("%s no longer exists!"), a->filename);
489 if (a->type == TYPETEXT && (!a->noconv))
490 fc = fgetconv_open (fpin, a->charset,
491 mutt_get_body_charset (send_charset, sizeof (send_charset), a),
494 fc = fgetconv_open (fpin, 0, 0, 0);
496 if (a->encoding == ENCQUOTEDPRINTABLE)
497 encode_quoted (fc, f, write_as_text_part (a));
498 else if (a->encoding == ENCBASE64)
499 encode_base64 (fc, f, write_as_text_part (a));
500 else if (a->type == TYPETEXT && (!a->noconv))
501 encode_8bit (fc, f, write_as_text_part (a));
503 mutt_copy_stream (fpin, f);
505 fgetconv_close (&fc);
508 return (ferror (f) ? -1 : 0);
511 #undef write_as_text_part
513 #define BOUNDARYLEN 16
514 void mutt_generate_boundary (PARAMETER **parm)
516 char rs[BOUNDARYLEN + 1];
521 for (i=0;i<BOUNDARYLEN;i++)
522 *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
525 mutt_set_parameter ("boundary", rs, parm);
539 static void update_content_info (CONTENT *info, CONTENT_STATE *s, char *d, size_t dlen)
542 int whitespace = s->whitespace;
544 int linelen = s->linelen;
545 int was_cr = s->was_cr;
547 if (!d) /* This signals EOF */
551 if (linelen > info->linemax)
552 info->linemax = linelen;
557 for (; dlen; d++, dlen--)
570 if (whitespace) info->space = 1;
571 if (dot) info->dot = 1;
572 if (linelen > info->linemax) info->linemax = linelen;
584 if (whitespace) info->space = 1;
585 if (dot) info->dot = 1;
586 if (linelen > info->linemax) info->linemax = linelen;
600 else if (ch == '\t' || ch == '\f')
610 else if (ch < 32 || ch == 127)
616 if ((ch == 'F') || (ch == 'f'))
627 if (linelen == 2 && ch != 'r') from = 0;
628 else if (linelen == 3 && ch != 'o') from = 0;
629 else if (linelen == 4)
631 if (ch == 'm') info->from = 1;
635 if (ch == ' ') whitespace++;
639 if (linelen > 1) dot = 0;
640 if (ch != ' ' && ch != '\t') whitespace = 0;
644 s->whitespace = whitespace;
646 s->linelen = linelen;
651 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
652 #define BUGGY_ICONV 1
655 * Find the best charset conversion of the file from fromcode into one
656 * of the tocodes. If successful, set *tocode and CONTENT *info and
657 * return the number of characters converted inexactly. If no
658 * conversion was possible, return -1.
660 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
661 * which would otherwise prevent us from knowing the number of inexact
662 * conversions. Where the candidate target charset is UTF-8 we avoid
663 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
664 * fails with some libraries.
666 * We assume that the output from iconv is never more than 4 times as
667 * long as the input for any pair of charsets we might be interested
670 static size_t convert_file_to (FILE *file, const char *fromcode,
671 int ncodes, const char **tocodes,
672 int *tocode, CONTENT *info)
676 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
677 ICONV_CONST char *ib, *ub;
679 size_t ibl, obl, ubl, ubl1, n, ret;
682 CONTENT_STATE *states;
685 cd1 = mutt_iconv_open ("utf-8", fromcode, 0);
686 if (cd1 == (iconv_t)(-1))
689 cd = safe_calloc (ncodes, sizeof (iconv_t));
690 score = safe_calloc (ncodes, sizeof (size_t));
691 states = safe_calloc (ncodes, sizeof (CONTENT_STATE));
692 infos = safe_calloc (ncodes, sizeof (CONTENT));
694 for (i = 0; i < ncodes; i++)
695 if (ascii_strcasecmp (tocodes[i], "utf-8"))
696 cd[i] = mutt_iconv_open (tocodes[i], "utf-8", 0);
698 /* Special case for conversion to UTF-8 */
699 cd[i] = (iconv_t)(-1), score[i] = (size_t)(-1);
706 /* Try to fill input buffer */
707 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
710 /* Convert to UTF-8 */
712 ob = bufu, obl = sizeof (bufu);
713 n = iconv (cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
714 assert (n == (size_t)(-1) || !n || ICONV_NONTRANS);
715 if (n == (size_t)(-1) &&
716 ((errno != EINVAL && errno != E2BIG) || ib == bufi))
718 assert (errno == EILSEQ ||
719 (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
725 /* Convert from UTF-8 */
726 for (i = 0; i < ncodes; i++)
727 if (cd[i] != (iconv_t)(-1) && score[i] != (size_t)(-1))
729 ub = bufu, ubl = ubl1;
730 ob = bufo, obl = sizeof (bufo);
731 n = iconv (cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
732 if (n == (size_t)(-1))
734 assert (errno == E2BIG ||
735 (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
736 score[i] = (size_t)(-1);
741 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
744 else if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
745 /* Special case for conversion to UTF-8 */
746 update_content_info (&infos[i], &states[i], bufu, ubl1);
749 /* Save unused input */
750 memmove (bufi, ib, ibl);
751 else if (!ubl1 && ib < bufi + sizeof (bufi))
760 /* Find best score */
762 for (i = 0; i < ncodes; i++)
764 if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
766 /* Special case for conversion to UTF-8 */
771 else if (cd[i] == (iconv_t)(-1) || score[i] == (size_t)(-1))
773 else if (ret == (size_t)(-1) || score[i] < ret)
781 if (ret != (size_t)(-1))
783 memcpy (info, &infos[*tocode], sizeof(CONTENT));
784 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
788 for (i = 0; i < ncodes; i++)
789 if (cd[i] != (iconv_t)(-1))
801 #endif /* !HAVE_ICONV */
805 * Find the first of the fromcodes that gives a valid conversion and
806 * the best charset conversion of the file into one of the tocodes. If
807 * successful, set *fromcode and *tocode to dynamically allocated
808 * strings, set CONTENT *info, and return the number of characters
809 * converted inexactly. If no conversion was possible, return -1.
811 * Both fromcodes and tocodes may be colon-separated lists of charsets.
812 * However, if fromcode is zero then fromcodes is assumed to be the
813 * name of a single charset even if it contains a colon.
815 static size_t convert_file_from_to (FILE *file,
816 const char *fromcodes, const char *tocodes,
817 char **fromcode, char **tocode, CONTENT *info)
825 /* Count the tocodes */
827 for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
829 if ((c1 = strchr (c, ':')) == c)
835 tcode = safe_malloc (ncodes * sizeof (char *));
836 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
838 if ((c1 = strchr (c, ':')) == c)
840 tcode[i] = mutt_substrdup (c, c1);
846 /* Try each fromcode in turn */
847 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
849 if ((c1 = strchr (c, ':')) == c)
851 fcode = mutt_substrdup (c, c1);
853 ret = convert_file_to (file, fcode, ncodes, (const char **)tcode,
855 if (ret != (size_t)(-1))
867 /* There is only one fromcode */
868 ret = convert_file_to (file, fromcodes, ncodes, (const char **)tcode,
870 if (ret != (size_t)(-1))
878 for (i = 0; i < ncodes; i++)
887 * Analyze the contents of a file to determine which MIME encoding to use.
888 * Also set the body charset, sometimes, or not.
890 CONTENT *mutt_get_content_info (const char *fname, BODY *b)
895 char *fromcode = NULL;
903 if (b && !fname) fname = b->filename;
905 if (stat (fname, &sb) == -1)
907 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
911 if (!S_ISREG(sb.st_mode))
913 mutt_error (_("%s isn't a regular file."), fname);
917 if ((fp = fopen (fname, "r")) == NULL)
919 dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
920 fname, strerror (errno), errno));
924 info = safe_calloc (1, sizeof (CONTENT));
925 memset (&state, 0, sizeof (state));
927 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
929 char *chs = mutt_get_parameter ("charset", b->parameter);
930 char *fchs = b->use_disp ? (AttachCharset ? AttachCharset : Charset) : Charset;
931 if (Charset && (chs || SendCharset) &&
932 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
933 &fromcode, &tocode, info) != (size_t)(-1))
937 mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
938 mutt_set_parameter ("charset", chsbuf, &b->parameter);
941 b->charset = fromcode;
949 while ((r = fread (buffer, 1, sizeof(buffer), fp)))
950 update_content_info (info, &state, buffer, r);
951 update_content_info (info, &state, 0, 0);
955 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
956 mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
957 Charset && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
963 /* Given a file with path ``s'', see if there is a registered MIME type.
964 * returns the major MIME type, and copies the subtype to ``d''. First look
965 * for ~/.mime.types, then look in a system mime.types if we can find one.
966 * The longest match is used so that we can match `ps.gz' when `gz' also
970 int mutt_lookup_mime_type (BODY *att, const char *path)
974 char buf[LONG_STRING];
975 char subtype[STRING], xtype[STRING];
977 int szf, sze, cur_sze;
985 szf = mutt_strlen (path);
987 for (count = 0 ; count < 3 ; count++)
990 * can't use strtok() because we use it in an inner loop below, so use
991 * a switch statement here instead.
996 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
999 strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
1002 strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
1005 dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
1006 goto bye; /* shouldn't happen */
1009 if ((f = fopen (buf, "r")) != NULL)
1011 while (fgets (buf, sizeof (buf) - 1, f) != NULL)
1013 /* weed out any comments */
1014 if ((p = strchr (buf, '#')))
1017 /* remove any leading space. */
1021 /* position on the next field in this line */
1022 if ((p = strpbrk (ct, " \t")) == NULL)
1027 /* cycle through the file extensions */
1028 while ((p = strtok (p, " \t\n")))
1030 sze = mutt_strlen (p);
1031 if ((sze > cur_sze) && (szf >= sze) &&
1032 (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
1033 (szf == sze || path[szf - sze - 1] == '.'))
1035 /* get the content-type */
1037 if ((p = strchr (ct, '/')) == NULL)
1039 /* malformed line, just skip it. */
1044 for (q = p; *q && !ISSPACE (*q); q++)
1047 mutt_substrcpy (subtype, p, q, sizeof (subtype));
1049 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1050 strfcpy (xtype, ct, sizeof (xtype));
1063 if (type != TYPEOTHER || *xtype != '\0')
1066 mutt_str_replace (&att->subtype, subtype);
1067 mutt_str_replace (&att->xtype, xtype);
1073 void mutt_message_to_7bit (BODY *a, FILE *fp)
1075 char temp[_POSIX_PATH_MAX];
1081 if (!a->filename && fp)
1083 else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1085 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1091 if (stat (a->filename, &sb) == -1)
1093 mutt_perror ("stat");
1094 safe_fclose (&fpin);
1096 a->length = sb.st_size;
1099 mutt_mktemp (temp, sizeof (temp));
1100 if (!(fpout = safe_fopen (temp, "w+")))
1102 mutt_perror ("fopen");
1106 fseeko (fpin, a->offset, 0);
1107 a->parts = mutt_parse_messageRFC822 (fpin, a);
1109 transform_to_7bit (a->parts, fpin);
1111 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1112 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1114 fputs ("MIME-Version: 1.0\n", fpout);
1115 mutt_write_mime_header (a->parts, fpout);
1116 fputc ('\n', fpout);
1117 mutt_write_mime_body (a->parts, fpout);
1122 if (fpin && fpin != fp)
1123 safe_fclose (&fpin);
1125 safe_fclose (&fpout);
1129 a->encoding = ENC7BIT;
1130 FREE (&a->d_filename);
1131 a->d_filename = a->filename;
1132 if (a->filename && a->unlink)
1133 unlink (a->filename);
1134 a->filename = safe_strdup (temp);
1136 if (stat (a->filename, &sb) == -1)
1138 mutt_perror ("stat");
1141 a->length = sb.st_size;
1142 mutt_free_body (&a->parts);
1143 a->hdr->content = NULL;
1146 static void transform_to_7bit (BODY *a, FILE *fpin)
1148 char buff[_POSIX_PATH_MAX];
1152 memset (&s, 0, sizeof (s));
1153 for (; a; a = a->next)
1155 if (a->type == TYPEMULTIPART)
1157 if (a->encoding != ENC7BIT)
1158 a->encoding = ENC7BIT;
1160 transform_to_7bit (a->parts, fpin);
1162 else if (mutt_is_message_type(a->type, a->subtype))
1164 mutt_message_to_7bit (a, fpin);
1169 a->force_charset = 1;
1171 mutt_mktemp (buff, sizeof (buff));
1172 if ((s.fpout = safe_fopen (buff, "w")) == NULL)
1174 mutt_perror ("fopen");
1178 mutt_decode_attachment (a, &s);
1179 safe_fclose (&s.fpout);
1180 FREE (&a->d_filename);
1181 a->d_filename = a->filename;
1182 a->filename = safe_strdup (buff);
1184 if (stat (a->filename, &sb) == -1)
1186 mutt_perror ("stat");
1189 a->length = sb.st_size;
1191 mutt_update_encoding (a);
1192 if (a->encoding == ENC8BIT)
1193 a->encoding = ENCQUOTEDPRINTABLE;
1194 else if (a->encoding == ENCBINARY)
1195 a->encoding = ENCBASE64;
1200 /* determine which Content-Transfer-Encoding to use */
1201 static void mutt_set_encoding (BODY *b, CONTENT *info)
1203 char send_charset[SHORT_STRING];
1205 if (b->type == TYPETEXT)
1207 char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1208 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1209 b->encoding = ENCQUOTEDPRINTABLE;
1210 else if (info->hibin)
1211 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1213 b->encoding = ENC7BIT;
1215 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1217 if (info->lobin || info->hibin)
1219 if (option (OPTALLOW8BIT) && !info->lobin)
1220 b->encoding = ENC8BIT;
1222 mutt_message_to_7bit (b, NULL);
1225 b->encoding = ENC7BIT;
1227 else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1228 b->encoding = ENC7BIT;
1231 /* Determine which encoding is smaller */
1232 if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
1233 3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1234 b->encoding = ENCBASE64;
1236 b->encoding = ENCQUOTEDPRINTABLE;
1240 void mutt_stamp_attachment(BODY *a)
1242 a->stamp = time(NULL);
1245 /* Get a body's character set */
1247 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1251 if (b && b->type != TYPETEXT)
1255 p = mutt_get_parameter ("charset", b->parameter);
1258 mutt_canonical_charset (d, dlen, NONULL(p));
1260 strfcpy (d, "us-ascii", dlen);
1266 /* Assumes called from send mode where BODY->filename points to actual file */
1267 void mutt_update_encoding (BODY *a)
1270 char chsbuff[STRING];
1272 /* override noconv when it's us-ascii */
1273 if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1276 if (!a->force_charset && !a->noconv)
1277 mutt_delete_parameter ("charset", &a->parameter);
1279 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1282 mutt_set_encoding (a, info);
1283 mutt_stamp_attachment(a);
1290 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1292 char buffer[LONG_STRING];
1295 int cmflags, chflags;
1296 int pgp = WithCrypto? hdr->security : 0;
1300 if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1301 (hdr->security & ENCRYPT))
1303 if (!crypt_valid_passphrase(hdr->security))
1308 mutt_mktemp (buffer, sizeof (buffer));
1309 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1312 body = mutt_new_body ();
1313 body->type = TYPEMESSAGE;
1314 body->subtype = safe_strdup ("rfc822");
1315 body->filename = safe_strdup (buffer);
1318 body->disposition = DISPINLINE;
1321 mutt_parse_mime_message (ctx, hdr);
1326 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1327 if (!attach_msg && option (OPTMIMEFORWDECODE))
1329 chflags |= CH_MIME | CH_TXTPLAIN;
1330 cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1331 if ((WithCrypto & APPLICATION_PGP))
1333 if ((WithCrypto & APPLICATION_SMIME))
1334 pgp &= ~SMIMEENCRYPT;
1337 && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1339 if ((WithCrypto & APPLICATION_PGP)
1340 && mutt_is_multipart_encrypted (hdr->content))
1342 chflags |= CH_MIME | CH_NONEWLINE;
1343 cmflags = MUTT_CM_DECODE_PGP;
1346 else if ((WithCrypto & APPLICATION_PGP) &&
1347 ((mutt_is_application_pgp (hdr->content) & PGPENCRYPT) == PGPENCRYPT))
1349 chflags |= CH_MIME | CH_TXTPLAIN;
1350 cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1353 else if ((WithCrypto & APPLICATION_SMIME) &&
1354 ((mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) == SMIMEENCRYPT))
1356 chflags |= CH_MIME | CH_TXTPLAIN;
1357 cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV;
1358 pgp &= ~SMIMEENCRYPT;
1362 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1367 body->hdr = mutt_new_header();
1368 body->hdr->offset = 0;
1369 /* we don't need the user headers here */
1370 body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1372 body->hdr->security = pgp;
1373 mutt_update_encoding (body);
1374 body->parts = body->hdr->content;
1381 static void run_mime_type_query (BODY *att)
1390 cmd = mutt_buffer_pool_get ();
1391 mutt_expand_file_fmt (cmd, MimeTypeQueryCmd, att->filename);
1393 if ((thepid = mutt_create_filter (mutt_b2s (cmd), NULL, &fp, &fperr)) < 0)
1395 mutt_error (_("Error running \"%s\"!"), mutt_b2s (cmd));
1396 mutt_buffer_pool_release (&cmd);
1399 mutt_buffer_pool_release (&cmd);
1401 if ((buf = mutt_read_line (buf, &buflen, fp, &dummy, 0)) != NULL)
1403 if (strchr(buf, '/'))
1404 mutt_parse_content_type (buf, att);
1409 safe_fclose (&fperr);
1410 mutt_wait_filter (thepid);
1413 BODY *mutt_make_file_attach (const char *path)
1418 att = mutt_new_body ();
1419 att->filename = safe_strdup (path);
1421 if (MimeTypeQueryCmd && option (OPTMIMETYPEQUERYFIRST))
1422 run_mime_type_query (att);
1424 /* Attempt to determine the appropriate content-type based on the filename
1428 mutt_lookup_mime_type (att, path);
1430 if (!att->subtype &&
1432 !option (OPTMIMETYPEQUERYFIRST))
1433 run_mime_type_query (att);
1435 if ((info = mutt_get_content_info (path, att)) == NULL)
1437 mutt_free_body (&att);
1443 if ((info->nulbin == 0) &&
1444 (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10))
1447 * Statistically speaking, there should be more than 10% "lobin"
1448 * chars if this is really a binary file...
1450 att->type = TYPETEXT;
1451 att->subtype = safe_strdup ("plain");
1455 att->type = TYPEAPPLICATION;
1456 att->subtype = safe_strdup ("octet-stream");
1461 mutt_update_encoding (att);
1465 static int get_toplevel_encoding (BODY *a)
1469 for (; a; a = a->next)
1471 if (a->encoding == ENCBINARY)
1473 else if (a->encoding == ENC8BIT)
1480 /* check for duplicate boundary. return 1 if duplicate */
1481 static int mutt_check_boundary (const char* boundary, BODY *b)
1485 if (b->parts && mutt_check_boundary (boundary, b->parts))
1488 if (b->next && mutt_check_boundary (boundary, b->next))
1491 if ((p = mutt_get_parameter ("boundary", b->parameter))
1492 && !ascii_strcmp (p, boundary))
1497 BODY *mutt_make_multipart (BODY *b)
1501 new = mutt_new_body ();
1502 new->type = TYPEMULTIPART;
1503 new->subtype = safe_strdup ("mixed");
1504 new->encoding = get_toplevel_encoding (b);
1507 mutt_generate_boundary (&new->parameter);
1508 if (mutt_check_boundary (mutt_get_parameter ("boundary", new->parameter),
1510 mutt_delete_parameter ("boundary", &new->parameter);
1512 while (!mutt_get_parameter ("boundary", new->parameter));
1514 new->disposition = DISPINLINE;
1520 /* remove the multipart body if it exists */
1521 BODY *mutt_remove_multipart (BODY *b)
1530 mutt_free_body (&t);
1535 char *mutt_make_date (char *s, size_t len)
1537 time_t t = time (NULL);
1538 struct tm *l = localtime (&t);
1539 time_t tz = mutt_local_tz (t);
1543 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1544 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1545 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1546 (int) tz / 60, (int) abs ((int) tz) % 60);
1550 /* wrapper around mutt_write_address() so we can handle very large
1551 recipient lists without needing a huge temporary buffer in memory */
1552 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1555 char buf[LONG_STRING];
1564 rfc822_write_address (buf, sizeof (buf), adr, display);
1565 len = mutt_strlen (buf);
1566 if (count && linelen + len > 74)
1569 linelen = len + 8; /* tab is usually about 8 spaces... */
1573 if (count && adr->mailbox)
1582 if (!adr->group && adr->next && adr->next->mailbox)
1593 /* arbitrary number of elements to grow the array by */
1596 /* need to write the list in reverse because they are stored in reverse order
1597 * when parsed to speed up threading
1599 void mutt_write_references (LIST *r, FILE *f, int trim)
1602 int refcnt = 0, refmax = 0;
1604 for ( ; (trim == 0 || refcnt < trim) && r ; r = r->next)
1606 if (refcnt == refmax)
1607 safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1611 while (refcnt-- > 0)
1614 fputs (ref[refcnt]->data, f);
1622 static const char *find_word (const char *src)
1624 const char *p = src;
1626 while (p && *p && strchr (" \t\n", *p))
1628 while (p && *p && !strchr (" \t\n", *p))
1633 /* like wcwidth(), but gets const char* not wchar_t* */
1634 static int my_width (const char *str, int col, int flags)
1637 int l, w = 0, nl = 0;
1638 const char *p = str;
1642 if (mbtowc (&wc, p, MB_CUR_MAX) >= 0)
1647 /* correctly calc tab stop, even for sending as the
1648 * line should look pretty on the receiving end */
1649 if (wc == L'\t' || (nl && wc == L' '))
1654 /* track newlines for display-case: if we have a space
1655 * after a newline, assume 8 spaces as for display we
1656 * always tab-fold */
1657 else if ((flags & CH_DISPLAY) && wc == '\n')
1668 static int print_val (FILE *fp, const char *pfx, const char *value,
1669 int flags, size_t col)
1671 while (value && *value)
1673 if (fputc (*value, fp) == EOF)
1675 /* corner-case: break words longer than 998 chars by force,
1676 * mandated by RfC5322 */
1677 if (!(flags & CH_DISPLAY) && ++col >= 998)
1679 if (fputs ("\n ", fp) < 0)
1685 if (*(value + 1) && pfx && *pfx && fputs (pfx, fp) == EOF)
1687 /* for display, turn folding spaces into folding tabs */
1688 if ((flags & CH_DISPLAY) && (*(value + 1) == ' ' || *(value + 1) == '\t'))
1691 while (*value && (*value == ' ' || *value == '\t'))
1693 if (fputc ('\t', fp) == EOF)
1703 static int fold_one_header (FILE *fp, const char *tag, const char *value,
1704 const char *pfx, int wraplen, int flags)
1706 const char *p = value, *next, *sp;
1707 char buf[HUGE_STRING] = "";
1708 int first = 1, enc, col = 0, w, l = 0, fold;
1710 dprint(4,(debugfile,"mwoh: pfx=[%s], tag=[%s], flags=%d value=[%s]\n",
1711 NONULL (pfx), tag, flags, value));
1713 if (tag && *tag && fprintf (fp, "%s%s: ", NONULL (pfx), tag) < 0)
1715 col = mutt_strlen (tag) + (tag && *tag ? 2 : 0) + mutt_strlen (pfx);
1721 /* find the next word and place it in `buf'. it may start with
1722 * whitespace we can fold before */
1723 next = find_word (p);
1724 l = MIN(sizeof (buf) - 1, next - p);
1728 /* determine width: character cells for display, bytes for sending
1729 * (we get pure ascii only) */
1730 w = my_width (buf, col, flags);
1731 enc = mutt_strncmp (buf, "=?", 2) == 0;
1733 dprint(5,(debugfile,"mwoh: word=[%s], col=%d, w=%d, next=[0x0%x]\n",
1734 buf, col, w, *next));
1736 /* insert a folding \n before the current word's lwsp except for
1737 * header name, first word on a line (word longer than wrap width)
1738 * and encoded words */
1739 if (!first && !enc && col && col + w >= wraplen)
1741 col = mutt_strlen (pfx);
1743 if (fprintf (fp, "\n%s", NONULL(pfx)) <= 0)
1747 /* print the actual word; for display, ignore leading ws for word
1748 * and fold with tab for readability */
1749 if ((flags & CH_DISPLAY) && fold)
1752 while (*p && (*p == ' ' || *p == '\t'))
1757 if (fputc ('\t', fp) == EOF)
1759 if (print_val (fp, pfx, p, flags, col) < 0)
1763 else if (print_val (fp, pfx, buf, flags, col) < 0)
1767 /* if the current word ends in \n, ignore all its trailing spaces
1768 * and reset column; this prevents us from putting only spaces (or
1769 * even none) on a line if the trailing spaces are located at our
1770 * current line width
1771 * XXX this covers ASCII space only, for display we probably
1772 * XXX want something like iswspace() here */
1774 while (*sp && (*sp == ' ' || *sp == '\t'))
1786 /* if we have printed something but didn't \n-terminate it, do it
1787 * except the last word we printed ended in \n already */
1788 if (col && (l == 0 || buf[l - 1] != '\n'))
1789 if (putc ('\n', fp) == EOF)
1795 static char *unfold_header (char *s)
1797 char *p = s, *q = s;
1801 /* remove CRLF prior to FWSP, turn \t into ' ' */
1802 if (*p == '\r' && *(p + 1) && *(p + 1) == '\n' && *(p + 2) &&
1803 (*(p + 2) == ' ' || *(p + 2) == '\t'))
1809 /* remove LF prior to FWSP, turn \t into ' ' */
1810 else if (*p == '\n' && *(p + 1) && (*(p + 1) == ' ' || *(p + 1) == '\t'))
1824 static int write_one_header (FILE *fp, int pfxw, int max, int wraplen,
1825 const char *pfx, const char *start, const char *end,
1828 char *tagbuf, *valbuf, *t;
1829 int is_from = ((end - start) > 5 &&
1830 ascii_strncasecmp (start, "from ", 5) == 0);
1832 /* only pass through folding machinery if necessary for sending,
1833 never wrap From_ headers on sending */
1834 if (!(flags & CH_DISPLAY) && (pfxw + max <= wraplen || is_from))
1836 valbuf = mutt_substrdup (start, end);
1837 dprint(4,(debugfile,"mwoh: buf[%s%s] short enough, "
1838 "max width = %d <= %d\n",
1839 NONULL(pfx), valbuf, max, wraplen));
1841 if (fputs (pfx, fp) == EOF)
1843 if (!(t = strchr (valbuf, ':')))
1845 dprint (1, (debugfile, "mwoh: warning: header not in "
1846 "'key: value' format!\n"));
1849 if (print_val (fp, pfx, valbuf, flags, mutt_strlen (pfx)) < 0)
1858 t = strchr (start, ':');
1861 dprint (1, (debugfile, "mwoh: warning: header not in "
1862 "'key: value' format!\n"));
1868 valbuf = mutt_substrdup (start, end);
1872 tagbuf = mutt_substrdup (start, t);
1873 /* skip over the colon separating the header field name and value */
1876 /* skip over any leading whitespace (WSP, as defined in RFC5322)
1877 * NOTE: skip_email_wsp() does the wrong thing here.
1878 * See tickets 3609 and 3716. */
1879 while (*t == ' ' || *t == '\t')
1882 valbuf = mutt_substrdup (t, end);
1884 dprint(4,(debugfile,"mwoh: buf[%s%s] too long, "
1885 "max width = %d > %d\n",
1886 NONULL(pfx), valbuf, max, wraplen));
1887 if (fold_one_header (fp, tagbuf, valbuf, pfx, wraplen, flags) < 0)
1895 /* split several headers into individual ones and call write_one_header
1897 int mutt_write_one_header (FILE *fp, const char *tag, const char *value,
1898 const char *pfx, int wraplen, int flags)
1900 char *p = (char *)value, *last, *line;
1901 int max = 0, w, rc = -1;
1902 int pfxw = mutt_strwidth (pfx);
1903 char *v = safe_strdup (value);
1905 if (!(flags & CH_DISPLAY) || option (OPTWEED))
1906 v = unfold_header (v);
1908 /* when not displaying, use sane wrap value */
1909 if (!(flags & CH_DISPLAY))
1911 if (WrapHeaders < 78 || WrapHeaders > 998)
1914 wraplen = WrapHeaders;
1916 else if (wraplen <= 0 || wraplen > MuttIndexWindow->cols)
1917 wraplen = MuttIndexWindow->cols;
1921 /* if header is short enough, simply print it */
1922 if (!(flags & CH_DISPLAY) && mutt_strwidth (tag) + 2 + pfxw +
1923 mutt_strwidth (v) <= wraplen)
1925 dprint(4,(debugfile,"mwoh: buf[%s%s: %s] is short enough\n",
1926 NONULL(pfx), tag, v));
1927 if (fprintf (fp, "%s%s: %s\n", NONULL(pfx), tag, v) <= 0)
1934 rc = fold_one_header (fp, tag, v, pfx, wraplen, flags);
1939 p = last = line = (char *)v;
1942 p = strchr (p, '\n');
1944 /* find maximum line width in current header */
1947 if ((w = my_width (line, 0, flags)) > max)
1956 if (*p != ' ' && *p != '\t')
1958 if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
1966 if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
1977 /* Note: all RFC2047 encoding should be done outside of this routine, except
1978 * for the "real name." This will allow this routine to be used more than
1979 * once, if necessary.
1981 * Likewise, all IDN processing should happen outside of this routine.
1983 * mode == MUTT_WRITE_HEADER_EDITHDRS => "lite" mode (used for edit_hdrs)
1984 * mode == MUTT_WRITE_HEADER_NORMAL => normal mode. write full header + MIME headers
1985 * mode == MUTT_WRITE_HEADER_POSTPONE => write just the envelope info
1986 * mode == MUTT_WRITE_HEADER_MIME => for writing protected headers
1988 * privacy != 0 => will omit any headers which may identify the user.
1989 * Output generated is suitable for being sent through
1990 * anonymous remailer chains.
1992 * hide_protected_subject: replaces the Subject header with
1993 * $crypt_protected_headers_subject in NORMAL or POSTPONE mode.
1996 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
1997 mutt_write_header_mode mode, int privacy,
1998 int hide_protected_subject)
2000 char buffer[LONG_STRING];
2002 LIST *tmp = env->userhdrs;
2003 int has_agent = 0; /* user defined user-agent header field exists */
2005 if (mode == MUTT_WRITE_HEADER_NORMAL && !privacy)
2006 fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
2008 /* OPTUSEFROM is not consulted here so that we can still write a From:
2009 * field if the user sets it with the `my_hdr' command
2011 if (env->from && !privacy)
2014 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
2015 fprintf (fp, "From: %s\n", buffer);
2018 if (env->sender && !privacy)
2021 rfc822_write_address (buffer, sizeof (buffer), env->sender, 0);
2022 fprintf (fp, "Sender: %s\n", buffer);
2028 mutt_write_address_list (env->to, fp, 4, 0);
2030 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2031 fputs ("To: \n", fp);
2036 mutt_write_address_list (env->cc, fp, 4, 0);
2038 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2039 fputs ("Cc: \n", fp);
2043 if (mode == MUTT_WRITE_HEADER_POSTPONE ||
2044 mode == MUTT_WRITE_HEADER_EDITHDRS ||
2045 (mode == MUTT_WRITE_HEADER_NORMAL && option(OPTWRITEBCC)))
2047 fputs ("Bcc: ", fp);
2048 mutt_write_address_list (env->bcc, fp, 5, 0);
2051 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2052 fputs ("Bcc: \n", fp);
2056 if (hide_protected_subject &&
2057 (mode == MUTT_WRITE_HEADER_NORMAL ||
2058 mode == MUTT_WRITE_HEADER_POSTPONE))
2059 mutt_write_one_header (fp, "Subject", ProtHdrSubject, NULL, 0, 0);
2061 mutt_write_one_header (fp, "Subject", env->subject, NULL, 0, 0);
2063 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2064 fputs ("Subject: \n", fp);
2066 /* save message id if the user has set it */
2067 if (env->message_id && !privacy)
2068 fprintf (fp, "Message-ID: %s\n", env->message_id);
2072 fputs ("Reply-To: ", fp);
2073 mutt_write_address_list (env->reply_to, fp, 10, 0);
2075 else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
2076 fputs ("Reply-To: \n", fp);
2078 if (env->mail_followup_to)
2080 fputs ("Mail-Followup-To: ", fp);
2081 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
2084 if (mode == MUTT_WRITE_HEADER_NORMAL ||
2085 mode == MUTT_WRITE_HEADER_POSTPONE)
2087 if (env->references)
2089 fputs ("References:", fp);
2090 mutt_write_references (env->references, fp, 10);
2094 /* Add the MIME headers */
2095 fputs ("MIME-Version: 1.0\n", fp);
2096 mutt_write_mime_header (attach, fp);
2099 if (env->in_reply_to)
2101 fputs ("In-Reply-To:", fp);
2102 mutt_write_references (env->in_reply_to, fp, 0);
2106 #ifdef USE_AUTOCRYPT
2107 if (option (OPTAUTOCRYPT))
2109 if (mode == MUTT_WRITE_HEADER_NORMAL)
2110 mutt_autocrypt_write_autocrypt_header (env, fp);
2111 if (mode == MUTT_WRITE_HEADER_MIME)
2112 mutt_autocrypt_write_gossip_headers (env, fp);
2116 /* Add any user defined headers */
2117 for (; tmp; tmp = tmp->next)
2119 if ((p = strchr (tmp->data, ':')))
2125 p = skip_email_wsp(p + 1);
2129 continue; /* don't emit empty fields. */
2132 /* check to see if the user has overridden the user-agent field */
2133 if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
2143 mutt_write_one_header (fp, tmp->data, p, NULL, 0, 0);
2148 if (mode == MUTT_WRITE_HEADER_NORMAL && !privacy &&
2149 option (OPTXMAILER) && !has_agent)
2151 /* Add a vanity header */
2152 fprintf (fp, "User-Agent: Mutt/%s (%s)\n", MUTT_VERSION, ReleaseDate);
2155 return (ferror (fp) == 0 ? 0 : -1);
2158 static void encode_headers (LIST *h)
2164 for (; h; h = h->next)
2166 if (!(p = strchr (h->data, ':')))
2170 p = skip_email_wsp(p + 1);
2171 tmp = safe_strdup (p);
2176 rfc2047_encode_string (&tmp);
2177 safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
2179 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
2185 const char *mutt_fqdn(short may_hide_host)
2189 if (Fqdn && Fqdn[0] != '@')
2193 if (may_hide_host && option(OPTHIDDENHOST))
2195 if ((p = strchr(Fqdn, '.')))
2198 /* sanity check: don't hide the host if
2199 * the fqdn is something like detebe.org.
2202 if (!p || !strchr(p, '.'))
2210 char *mutt_gen_msgid (void)
2212 char buf[SHORT_STRING];
2219 if (!(fqdn = mutt_fqdn(0)))
2220 fqdn = NONULL(Hostname);
2222 snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.G%c%u@%s>",
2223 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
2224 tm->tm_min, tm->tm_sec, MsgIdPfx, (unsigned int)getpid (), fqdn);
2225 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
2226 return (safe_strdup (buf));
2229 static void alarm_handler (int sig)
2234 /* invoke sendmail in a subshell
2235 path (in) path to program to execute
2236 args (in) arguments to pass to program
2237 msg (in) temp file containing message to send
2238 tempfile (out) if sendmail is put in the background, this points
2239 to the temporary file containing the stdout of the
2240 child process. If it is NULL, stderr and stdout
2241 are not redirected. */
2243 send_msg (const char *path, char **args, const char *msg, char **tempfile)
2249 mutt_block_signals_system ();
2252 /* we also don't want to be stopped right now */
2253 sigaddset (&set, SIGTSTP);
2254 sigprocmask (SIG_BLOCK, &set, NULL);
2256 if (SendmailWait >= 0 && tempfile)
2258 char tmp[_POSIX_PATH_MAX];
2260 mutt_mktemp (tmp, sizeof (tmp));
2261 *tempfile = safe_strdup (tmp);
2264 if ((pid = fork ()) == 0)
2266 struct sigaction act, oldalrm;
2268 /* save parent's ID before setsid() */
2271 /* we want the delivery to continue even after the main process dies,
2272 * so we put ourselves into another session right away
2276 /* next we close all open files */
2278 #if defined(OPEN_MAX)
2279 for (fd = tempfile ? 1 : 3; fd < OPEN_MAX; fd++)
2281 #elif defined(_POSIX_OPEN_MAX)
2282 for (fd = tempfile ? 1 : 3; fd < _POSIX_OPEN_MAX; fd++)
2292 /* now the second fork() */
2293 if ((pid = fork ()) == 0)
2295 /* "msg" will be opened as stdin */
2296 if (open (msg, O_RDONLY, 0) < 0)
2303 if (SendmailWait >= 0 && tempfile && *tempfile)
2305 /* *tempfile will be opened as stdout */
2306 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2308 /* redirect stderr to *tempfile too */
2314 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
2316 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
2320 /* execvpe is a glibc extension */
2321 /* execvpe (path, args, mutt_envlist ()); */
2322 execvp (path, args);
2329 FREE (tempfile); /* __FREE_CHECKED__ */
2333 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
2334 * SendmailWait = 0: wait forever
2335 * SendmailWait < 0: don't wait
2337 if (SendmailWait > 0)
2340 act.sa_handler = alarm_handler;
2342 /* need to make sure waitpid() is interrupted on SIGALRM */
2343 act.sa_flags = SA_INTERRUPT;
2347 sigemptyset (&act.sa_mask);
2348 sigaction (SIGALRM, &act, &oldalrm);
2349 alarm (SendmailWait);
2351 else if (SendmailWait < 0)
2352 _exit (0xff & EX_OK);
2354 if (waitpid (pid, &st, 0) > 0)
2356 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
2357 if (SendmailWait && st == (0xff & EX_OK) && tempfile && *tempfile)
2359 unlink (*tempfile); /* no longer needed */
2360 FREE (tempfile); /* __FREE_CHECKED__ */
2365 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
2367 if (SendmailWait > 0 && tempfile && *tempfile)
2370 FREE (tempfile); /* __FREE_CHECKED__ */
2374 /* reset alarm; not really needed, but... */
2376 sigaction (SIGALRM, &oldalrm, NULL);
2378 if (kill (ppid, 0) == -1 && errno == ESRCH && tempfile && *tempfile)
2380 /* the parent is already dead */
2382 FREE (tempfile); /* __FREE_CHECKED__ */
2388 sigprocmask (SIG_UNBLOCK, &set, NULL);
2390 if (pid != -1 && waitpid (pid, &st, 0) > 0)
2391 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
2393 st = S_ERR; /* error */
2395 mutt_unblock_signals_system (1);
2401 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
2403 for (; addr; addr = addr->next)
2405 /* weed out group mailboxes, since those are for display only */
2406 if (addr->mailbox && !addr->group)
2408 if (*argslen == *argsmax)
2409 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2410 args[(*argslen)++] = addr->mailbox;
2417 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2419 if (*argslen == *argsmax)
2420 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2421 args[(*argslen)++] = s;
2426 mutt_invoke_sendmail (ADDRESS *from, /* the sender */
2427 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2428 const char *msg, /* file containing message */
2429 int eightbit) /* message contains 8bit chars */
2431 char *ps = NULL, *path = NULL, *s = safe_strdup (Sendmail), *childout = NULL;
2433 size_t argslen = 0, argsmax = 0;
2434 char **extra_args = NULL;
2435 size_t extra_argslen = 0, extra_argsmax = 0;
2438 /* ensure that $sendmail is set to avoid a crash. http://dev.mutt.org/trac/ticket/3548 */
2441 mutt_error(_("$sendmail must be set in order to send mail."));
2447 while ((ps = strtok (ps, " ")))
2449 if (argslen == argsmax)
2450 safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2454 if (!mutt_strcmp (ps, "--"))
2456 args[argslen++] = ps;
2460 path = safe_strdup (ps);
2461 ps = strrchr (ps, '/');
2466 args[argslen++] = ps;
2472 /* If Sendmail contained a "--", we save the recipients to append to
2473 * args after other possible options added below. */
2477 while ((ps = strtok (ps, " ")))
2479 if (extra_argslen == extra_argsmax)
2480 safe_realloc (&extra_args, sizeof (char *) * (extra_argsmax += 5));
2482 extra_args[extra_argslen++] = ps;
2487 if (eightbit && option (OPTUSE8BITMIME))
2488 args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2490 if (option (OPTENVFROM))
2494 args = add_option (args, &argslen, &argsmax, "-f");
2495 args = add_args (args, &argslen, &argsmax, EnvFrom);
2497 else if (from && !from->next)
2499 args = add_option (args, &argslen, &argsmax, "-f");
2500 args = add_args (args, &argslen, &argsmax, from);
2506 args = add_option (args, &argslen, &argsmax, "-N");
2507 args = add_option (args, &argslen, &argsmax, DsnNotify);
2511 args = add_option (args, &argslen, &argsmax, "-R");
2512 args = add_option (args, &argslen, &argsmax, DsnReturn);
2514 args = add_option (args, &argslen, &argsmax, "--");
2515 for (i = 0; i < extra_argslen; i++)
2516 args = add_option (args, &argslen, &argsmax, extra_args[i]);
2517 args = add_args (args, &argslen, &argsmax, to);
2518 args = add_args (args, &argslen, &argsmax, cc);
2519 args = add_args (args, &argslen, &argsmax, bcc);
2521 if (argslen == argsmax)
2522 safe_realloc (&args, sizeof (char *) * (++argsmax));
2524 args[argslen++] = NULL;
2526 /* Some user's $sendmail command uses gpg for password decryption,
2527 * and is set up to prompt using ncurses pinentry. If we
2528 * mutt_endwin() it leaves other users staring at a blank screen.
2529 * So instead, just force a hard redraw on the next refresh. */
2530 if (!option (OPTNOCURSES))
2531 mutt_need_hard_redraw ();
2533 if ((i = send_msg (path, args, msg, option(OPTNOCURSES) ? NULL : &childout)) != (EX_OK & 0xff))
2539 e = mutt_strsysexit (i);
2540 mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2545 if (stat (childout, &st) == 0 && st.st_size > 0)
2546 mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2559 if (i == (EX_OK & 0xff))
2561 else if (i == S_BKG)
2568 /* For postponing (!final) do the necessary encodings only */
2569 void mutt_prepare_envelope (ENVELOPE *env, int final)
2571 char buffer[LONG_STRING];
2575 if (env->bcc && !(env->to || env->cc))
2577 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2578 * recipients if there is no To: or Cc: field, so attempt to suppress
2579 * it by using an empty To: field.
2581 env->to = rfc822_new_address ();
2583 env->to->next = rfc822_new_address ();
2586 rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2589 env->to->mailbox = safe_strdup (buffer);
2592 mutt_set_followup_to (env);
2594 if (!env->message_id)
2595 env->message_id = mutt_gen_msgid ();
2598 /* Take care of 8-bit => 7-bit conversion. */
2599 rfc2047_encode_envelope (env);
2600 encode_headers (env->userhdrs);
2603 void mutt_unprepare_envelope (ENVELOPE *env)
2607 for (item = env->userhdrs; item; item = item->next)
2608 rfc2047_decode (&item->data);
2610 rfc822_free_address (&env->mail_followup_to);
2612 /* back conversions */
2613 rfc2047_decode_envelope (env);
2616 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2621 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2622 MESSAGE *msg = NULL;
2626 /* Try to bounce each message out, aborting if we get any failures. */
2627 for (i=0; i<Context->msgcount; i++)
2628 if (Context->hdrs[i]->tagged)
2629 ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2633 /* If we failed to open a message, return with error */
2634 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2637 if (!fp) fp = msg->fp;
2639 mutt_mktemp (tempfile, sizeof (tempfile));
2640 if ((f = safe_fopen (tempfile, "w")) != NULL)
2642 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2645 if (!option (OPTBOUNCEDELIVERED))
2646 ch_flags |= CH_WEED_DELIVERED;
2648 fseeko (fp, h->offset, 0);
2649 fprintf (f, "Resent-From: %s", resent_from);
2650 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2651 msgid_str = mutt_gen_msgid();
2652 fprintf (f, "Resent-Message-ID: %s\n", msgid_str);
2653 fputs ("Resent-To: ", f);
2654 mutt_write_address_list (to, f, 11, 0);
2655 mutt_copy_header (fp, h, f, ch_flags, NULL);
2657 mutt_copy_bytes (fp, f, h->content->length);
2663 ret = mutt_smtp_send (env_from, to, NULL, NULL, tempfile,
2664 h->content->encoding == ENC8BIT);
2666 #endif /* USE_SMTP */
2667 ret = mutt_invoke_sendmail (env_from, to, NULL, NULL, tempfile,
2668 h->content->encoding == ENC8BIT);
2672 mx_close_message (Context, &msg);
2677 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2679 ADDRESS *from, *resent_to;
2680 const char *fqdn = mutt_fqdn (1);
2681 char resent_from[STRING];
2685 resent_from[0] = '\0';
2686 from = mutt_default_from ();
2689 * mutt_default_from() does not use $realname if the real name is not set
2690 * in $from, so we add it here. The reason it is not added in
2691 * mutt_default_from() is that during normal sending, we execute
2692 * send-hooks and set the realname last so that it can be changed based
2693 * upon message criteria.
2695 if (! from->personal)
2696 from->personal = safe_strdup(Realname);
2699 rfc822_qualify (from, fqdn);
2701 rfc2047_encode_adrlist (from, "Resent-From");
2702 if (mutt_addrlist_to_intl (from, &err))
2704 mutt_error (_("Bad IDN %s while preparing resent-from."),
2707 rfc822_free_address (&from);
2710 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2713 * prepare recipient list. idna conversion appears to happen before this
2714 * function is called, since the user receives confirmation of the address
2715 * list being bounced to.
2717 resent_to = rfc822_cpy_adr(to, 0);
2718 rfc2047_encode_adrlist(resent_to, "Resent-To");
2720 ret = _mutt_bounce_message (fp, h, resent_to, resent_from, from);
2722 rfc822_free_address (&resent_to);
2723 rfc822_free_address (&from);
2729 /* given a list of addresses, return a list of unique addresses */
2730 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2732 ADDRESS *top = addr;
2733 ADDRESS **last = ⊤
2739 for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next)
2741 if (tmp->mailbox && addr->mailbox &&
2742 !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2751 dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
2757 rfc822_free_address(&addr);
2771 static void set_noconv_flags (BODY *b, short flag)
2773 for (; b; b = b->next)
2775 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2776 set_noconv_flags (b->parts, flag);
2777 else if (b->type == TYPETEXT && b->noconv)
2780 mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2782 mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2787 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, const char *fcc)
2791 char tempfile[_POSIX_PATH_MAX];
2792 FILE *tempfp = NULL;
2793 int r, need_buffy_cleanup = 0;
2795 char buf[SHORT_STRING];
2799 set_noconv_flags (hdr->content, 1);
2801 if (mx_open_mailbox (path, MUTT_APPEND | MUTT_QUIET, &f) == NULL)
2803 dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2808 /* We need to add a Content-Length field to avoid problems where a line in
2809 * the message body begins with "From "
2811 if (f.magic == MUTT_MMDF || f.magic == MUTT_MBOX)
2813 mutt_mktemp (tempfile, sizeof (tempfile));
2814 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2816 mutt_perror (tempfile);
2817 mx_close_mailbox (&f, NULL);
2820 /* remember new mail status before appending message */
2821 need_buffy_cleanup = 1;
2825 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2826 onm_flags = MUTT_ADD_FROM;
2828 onm_flags |= MUTT_SET_DRAFT;
2829 if ((msg = mx_open_new_message (&f, hdr, onm_flags)) == NULL)
2831 mx_close_mailbox (&f, NULL);
2835 /* post == 1 => postpone message.
2836 * post == 0 => Normal mode.
2838 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content,
2839 post ? MUTT_WRITE_HEADER_POSTPONE : MUTT_WRITE_HEADER_NORMAL,
2841 option (OPTCRYPTPROTHDRSREAD) &&
2842 mutt_should_hide_protected_subject (hdr));
2844 /* (postponment) if this was a reply of some sort, <msgid> contains the
2845 * Message-ID: of message replied to. Save it using a special X-Mutt-
2846 * header so it can be picked up if the message is recalled at a later
2847 * point in time. This will allow the message to be marked as replied if
2848 * the same mailbox is still open.
2851 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2853 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2854 * it can be picked up when the message is recalled
2857 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2859 if (f.magic == MUTT_MMDF || f.magic == MUTT_MBOX)
2860 fprintf (msg->fp, "Status: RO\n");
2862 /* mutt_write_rfc822_header() only writes out a Date: header with
2863 * mode == 0, i.e. _not_ postponment; so write out one ourself */
2865 fprintf (msg->fp, "%s", mutt_make_date (buf, sizeof (buf)));
2867 /* (postponment) if the mail is to be signed or encrypted, save this info */
2868 if ((WithCrypto & APPLICATION_PGP)
2869 && post && (hdr->security & APPLICATION_PGP))
2871 fputs ("X-Mutt-PGP: ", msg->fp);
2872 if (hdr->security & ENCRYPT)
2873 fputc ('E', msg->fp);
2874 if (hdr->security & OPPENCRYPT)
2875 fputc ('O', msg->fp);
2876 if (hdr->security & SIGN)
2878 fputc ('S', msg->fp);
2880 fprintf (msg->fp, "<%s>", PgpSignAs);
2882 if (hdr->security & INLINE)
2883 fputc ('I', msg->fp);
2884 #ifdef USE_AUTOCRYPT
2885 if (hdr->security & AUTOCRYPT)
2886 fputc ('A', msg->fp);
2887 if (hdr->security & AUTOCRYPT_OVERRIDE)
2888 fputc ('Z', msg->fp);
2890 fputc ('\n', msg->fp);
2893 /* (postponment) if the mail is to be signed or encrypted, save this info */
2894 if ((WithCrypto & APPLICATION_SMIME)
2895 && post && (hdr->security & APPLICATION_SMIME))
2897 fputs ("X-Mutt-SMIME: ", msg->fp);
2898 if (hdr->security & ENCRYPT)
2900 fputc ('E', msg->fp);
2902 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2904 if (hdr->security & OPPENCRYPT)
2905 fputc ('O', msg->fp);
2906 if (hdr->security & SIGN)
2908 fputc ('S', msg->fp);
2910 fprintf (msg->fp, "<%s>", SmimeSignAs);
2912 if (hdr->security & INLINE)
2913 fputc ('I', msg->fp);
2914 fputc ('\n', msg->fp);
2918 /* (postponement) if the mail is to be sent through a mixmaster
2919 * chain, save that information
2922 if (post && hdr->chain && hdr->chain)
2926 fputs ("X-Mutt-Mix:", msg->fp);
2927 for (p = hdr->chain; p; p = p->next)
2928 fprintf (msg->fp, " %s", (char *) p->data);
2930 fputc ('\n', msg->fp);
2936 char sasha[LONG_STRING];
2939 mutt_write_mime_body (hdr->content, tempfp);
2941 /* make sure the last line ends with a newline. Emacs doesn't ensure
2942 * this will happen, and it can cause problems parsing the mailbox
2945 fseek (tempfp, -1, 2);
2946 if (fgetc (tempfp) != '\n')
2948 fseek (tempfp, 0, 2);
2949 fputc ('\n', tempfp);
2953 if (ferror (tempfp))
2955 dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2956 safe_fclose (&tempfp);
2958 mx_commit_message (msg, &f); /* XXX - really? */
2959 mx_close_message (&f, &msg);
2960 mx_close_mailbox (&f, NULL);
2964 /* count the number of lines */
2966 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2968 fprintf (msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftello (tempfp));
2969 fprintf (msg->fp, "Lines: %d\n\n", lines);
2971 /* copy the body and clean up */
2973 r = mutt_copy_stream (tempfp, msg->fp);
2974 if (fclose (tempfp) != 0)
2976 /* if there was an error, leave the temp version */
2982 fputc ('\n', msg->fp); /* finish off the header */
2983 r = mutt_write_mime_body (hdr->content, msg->fp);
2986 if (mx_commit_message (msg, &f) != 0)
2988 mx_close_message (&f, &msg);
2989 mx_close_mailbox (&f, NULL);
2991 if (!post && need_buffy_cleanup)
2992 mutt_buffy_cleanup (path, &st);
2995 set_noconv_flags (hdr->content, 0);