2 * Copyright (C) 1996-2002 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
26 #include "mutt_curses.h"
35 #include "mutt_crypt.h"
36 #include "mutt_idna.h"
47 #include <sys/utsname.h>
50 # include "mutt_libesmtp.h"
51 #endif /* USE_LIBESMTP */
57 #ifdef HAVE_SYSEXITS_H
59 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
63 /* If you are debugging this file, comment out the following line. */
72 extern char RFC822Specials[];
74 static struct sysexits {
80 0xff & EX_USAGE, "Bad usage."},
84 0xff & EX_DATAERR, "Data format error."},
88 0xff & EX_NOINPUT, "Cannot open input."},
92 0xff & EX_NOUSER, "User unknown."},
96 0xff & EX_NOHOST, "Host unknown."},
100 0xff & EX_UNAVAILABLE, "Service unavailable."},
104 0xff & EX_SOFTWARE, "Internal error."},
108 0xff & EX_OSERR, "Operating system error."},
112 0xff & EX_OSFILE, "System file missing."},
116 0xff & EX_CANTCREAT, "Can't create output."},
120 0xff & EX_IOERR, "I/O error."},
124 0xff & EX_TEMPFAIL, "Deferred."},
128 0xff & EX_PROTOCOL, "Remote protocol error."},
132 0xff & EX_NOPERM, "Insufficient permission."},
136 0xff & EX_NOPERM, "Local configuration error."},
139 S_ERR, "Exec error."}, {
145 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
147 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
149 char B64Chars[64] = {
150 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
151 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
152 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
153 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
157 static char MsgIdPfx = 'A';
159 static void transform_to_7bit (BODY * a, FILE * fpin);
161 static void encode_quoted (FGETCONV * fc, FILE * fout, int istext)
164 char line[77], savechar;
166 while ((c = fgetconv (fc)) != EOF) {
167 /* Wrap the line if needed. */
168 if (linelen == 76 && ((istext && c != '\n') || !istext)) {
169 /* If the last character is "quoted", then be sure to move all three
170 * characters to the next line. Otherwise, just move the last
173 if (line[linelen - 3] == '=') {
174 line[linelen - 3] = 0;
179 line[1] = line[linelen - 2];
180 line[2] = line[linelen - 1];
184 savechar = line[linelen - 1];
185 line[linelen - 1] = '=';
194 /* Escape lines that begin with/only contain "the message separator". */
195 if (linelen == 4 && !mutt_strncmp ("From", line, 4)) {
196 strfcpy (line, "=46rom", sizeof (line));
199 else if (linelen == 4 && !mutt_strncmp ("from", line, 4)) {
200 strfcpy (line, "=66rom", sizeof (line));
203 else if (linelen == 1 && line[0] == '.') {
204 strfcpy (line, "=2E", sizeof (line));
209 if (c == '\n' && istext) {
210 /* Check to make sure there is no trailing space on this line. */
212 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) {
214 sprintf (line + linelen - 1, "=%2.2X",
215 (unsigned char) line[linelen - 1]);
219 int savechar = line[linelen - 1];
221 line[linelen - 1] = '=';
224 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
234 else if (c != 9 && (c < 32 || c > 126 || c == '=')) {
235 /* Check to make sure there is enough room for the quoted character.
236 * If not, wrap to the next line.
239 line[linelen++] = '=';
245 sprintf (line + linelen, "=%2.2X", (unsigned char) c);
249 /* Don't worry about wrapping the line here. That will happen during
250 * the next iteration when I'll also know what the next character is.
256 /* Take care of anything left in the buffer */
258 if (line[linelen - 1] == ' ' || line[linelen - 1] == '\t') {
259 /* take care of trailing whitespace */
261 sprintf (line + linelen - 1, "=%2.2X",
262 (unsigned char) line[linelen - 1]);
264 savechar = line[linelen - 1];
265 line[linelen - 1] = '=';
269 sprintf (line, "=%2.2X", (unsigned char) savechar);
278 static char b64_buffer[3];
279 static short b64_num;
280 static short b64_linelen;
282 static void b64_flush (FILE * fout)
289 if (b64_linelen >= 72) {
294 for (i = b64_num; i < 3; i++)
295 b64_buffer[i] = '\0';
297 fputc (B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
300 [((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf)], fout);
305 [((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3)],
309 fputc (B64Chars[b64_buffer[2] & 0x3f], fout);
314 while (b64_linelen % 4) {
323 static void b64_putc (char c, FILE * fout)
328 b64_buffer[b64_num++] = c;
332 static void encode_base64 (FGETCONV * fc, FILE * fout, int istext)
336 b64_num = b64_linelen = 0;
338 while ((ch = fgetconv (fc)) != EOF) {
339 if (istext && ch == '\n' && ch1 != '\r')
340 b64_putc ('\r', fout);
348 static void encode_8bit (FGETCONV * fc, FILE * fout, int istext)
352 while ((ch = fgetconv (fc)) != EOF)
357 int mutt_write_mime_header (BODY * a, FILE * f)
367 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
370 len = 25 + mutt_strlen (a->subtype); /* approximate len. of content-type */
372 for (p = a->parameter; p; p = p->next) {
381 tmp = safe_strdup (p->value);
382 encode = rfc2231_encode_string (&tmp);
383 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
385 /* Dirty hack to make messages readable by Outlook Express
386 * for the Mac: force quotes around the boundary parameter
387 * even when they aren't needed.
390 if (!ascii_strcasecmp (p->attribute, "boundary")
391 && !strcmp (buffer, tmp))
392 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
396 tmplen = mutt_strlen (buffer) + mutt_strlen (p->attribute) + 1;
398 if (len + tmplen + 2 > 76) {
407 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
415 fprintf (f, "Content-Description: %s\n", a->description);
417 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
420 if (!(fn = a->d_filename))
426 /* Strip off the leading path... */
427 if ((t = strrchr (fn, '/')))
433 tmp = safe_strdup (t);
434 encode = rfc2231_encode_string (&tmp);
435 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
437 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
443 if (a->encoding != ENC7BIT)
444 fprintf (f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
446 /* Do NOT add the terminator here!!! */
447 return (ferror (f) ? -1 : 0);
450 # define write_as_text_part(a) (mutt_is_text_part(a) \
451 || ((WithCrypto & APPLICATION_PGP)\
452 && mutt_is_application_pgp(a)))
454 int mutt_write_mime_body (BODY * a, FILE * f)
456 char *p, boundary[SHORT_STRING];
457 char send_charset[SHORT_STRING];
462 if (a->type == TYPEMULTIPART) {
463 /* First, find the boundary to use */
464 if (!(p = mutt_get_parameter ("boundary", a->parameter))) {
467 "mutt_write_mime_body(): no boundary parameter found!\n"));
468 mutt_error _("No boundary parameter found! [report this error]");
472 strfcpy (boundary, p, sizeof (boundary));
474 for (t = a->parts; t; t = t->next) {
475 fprintf (f, "\n--%s\n", boundary);
476 if (mutt_write_mime_header (t, f) == -1)
479 if (mutt_write_mime_body (t, f) == -1)
482 fprintf (f, "\n--%s--\n", boundary);
483 return (ferror (f) ? -1 : 0);
486 /* This is pretty gross, but it's the best solution for now... */
487 if ((WithCrypto & APPLICATION_PGP)
488 && a->type == TYPEAPPLICATION
489 && mutt_strcmp (a->subtype, "pgp-encrypted") == 0) {
490 fputs ("Version: 1\n", f);
494 if ((fpin = fopen (a->filename, "r")) == NULL) {
496 (debugfile, "write_mime_body: %s no longer exists!\n",
498 mutt_error (_("%s no longer exists!"), a->filename);
502 if (a->type == TYPETEXT && (!a->noconv))
503 fc = fgetconv_open (fpin, a->file_charset,
504 mutt_get_body_charset (send_charset,
505 sizeof (send_charset), a), 0);
507 fc = fgetconv_open (fpin, 0, 0, 0);
509 if (a->encoding == ENCQUOTEDPRINTABLE)
510 encode_quoted (fc, f, write_as_text_part (a));
511 else if (a->encoding == ENCBASE64)
512 encode_base64 (fc, f, write_as_text_part (a));
513 else if (a->type == TYPETEXT && (!a->noconv))
514 encode_8bit (fc, f, write_as_text_part (a));
516 mutt_copy_stream (fpin, f);
518 fgetconv_close (&fc);
521 return (ferror (f) ? -1 : 0);
524 #undef write_as_text_part
526 #define BOUNDARYLEN 16
527 void mutt_generate_boundary (PARAMETER ** parm)
529 char rs[BOUNDARYLEN + 1];
534 for (i = 0; i < BOUNDARYLEN; i++)
535 *p++ = B64Chars[LRAND () % sizeof (B64Chars)];
538 mutt_set_parameter ("boundary", rs, parm);
550 static void update_content_info (CONTENT * info, CONTENT_STATE * s, char *d,
554 int whitespace = s->whitespace;
556 int linelen = s->linelen;
557 int was_cr = s->was_cr;
559 if (!d) { /* This signals EOF */
562 if (linelen > info->linemax)
563 info->linemax = linelen;
568 for (; dlen; d++, dlen--) {
581 if (linelen > info->linemax)
582 info->linemax = linelen;
597 if (linelen > info->linemax)
598 info->linemax = linelen;
603 else if (ch == '\r') {
611 else if (ch == '\t' || ch == '\f') {
615 else if (ch < 32 || ch == 127)
619 if ((ch == 'F') || (ch == 'f'))
629 if (linelen == 2 && ch != 'r')
631 else if (linelen == 3 && ch != 'o')
633 else if (linelen == 4) {
646 if (ch != ' ' && ch != '\t')
651 s->whitespace = whitespace;
653 s->linelen = linelen;
658 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
659 #define BUGGY_ICONV 1
662 * Find the best charset conversion of the file from fromcode into one
663 * of the tocodes. If successful, set *tocode and CONTENT *info and
664 * return the number of characters converted inexactly. If no
665 * conversion was possible, return -1.
667 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
668 * which would otherwise prevent us from knowing the number of inexact
669 * conversions. Where the candidate target charset is UTF-8 we avoid
670 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
671 * fails with some libraries.
673 * We assume that the output from iconv is never more than 4 times as
674 * long as the input for any pair of charsets we might be interested
677 static size_t convert_file_to (FILE * file, const char *fromcode,
678 int ncodes, const char **tocodes,
679 int *tocode, CONTENT * info)
683 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
684 ICONV_CONST char *ib, *ub;
686 size_t ibl, obl, ubl, ubl1, n, ret;
689 CONTENT_STATE *states;
692 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
693 if (cd1 == (iconv_t) (-1))
696 cd = safe_calloc (ncodes, sizeof (iconv_t));
697 score = safe_calloc (ncodes, sizeof (size_t));
698 states = safe_calloc (ncodes, sizeof (CONTENT_STATE));
699 infos = safe_calloc (ncodes, sizeof (CONTENT));
701 for (i = 0; i < ncodes; i++)
702 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
703 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
705 /* Special case for conversion to UTF-8 */
706 cd[i] = (iconv_t) (-1), score[i] = (size_t) (-1);
712 /* Try to fill input buffer */
713 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
716 /* Convert to UTF-8 */
718 ob = bufu, obl = sizeof (bufu);
719 n = iconv (cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
720 assert (n == (size_t) (-1) || !n || ICONV_NONTRANS);
721 if (n == (size_t) (-1) &&
722 ((errno != EINVAL && errno != E2BIG) || ib == bufi)) {
723 assert (errno == EILSEQ ||
724 (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
730 /* Convert from UTF-8 */
731 for (i = 0; i < ncodes; i++)
732 if (cd[i] != (iconv_t) (-1) && score[i] != (size_t) (-1)) {
733 ub = bufu, ubl = ubl1;
734 ob = bufo, obl = sizeof (bufo);
735 n = iconv (cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
736 if (n == (size_t) (-1)) {
737 assert (errno == E2BIG ||
738 (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
739 score[i] = (size_t) (-1);
743 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
746 else if (cd[i] == (iconv_t) (-1) && score[i] == (size_t) (-1))
747 /* Special case for conversion to UTF-8 */
748 update_content_info (&infos[i], &states[i], bufu, ubl1);
751 /* Save unused input */
752 memmove (bufi, ib, ibl);
753 else if (!ubl1 && ib < bufi + sizeof (bufi)) {
760 /* Find best score */
762 for (i = 0; i < ncodes; i++) {
763 if (cd[i] == (iconv_t) (-1) && score[i] == (size_t) (-1)) {
764 /* Special case for conversion to UTF-8 */
769 else if (cd[i] == (iconv_t) (-1) || score[i] == (size_t) (-1))
771 else if (ret == (size_t) (-1) || score[i] < ret) {
778 if (ret != (size_t) (-1)) {
779 memcpy (info, &infos[*tocode], sizeof (CONTENT));
780 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
784 for (i = 0; i < ncodes; i++)
785 if (cd[i] != (iconv_t) (-1))
797 #endif /* !HAVE_ICONV */
801 * Find the first of the fromcodes that gives a valid conversion and
802 * the best charset conversion of the file into one of the tocodes. If
803 * successful, set *fromcode and *tocode to dynamically allocated
804 * strings, set CONTENT *info, and return the number of characters
805 * converted inexactly. If no conversion was possible, return -1.
807 * Both fromcodes and tocodes may be colon-separated lists of charsets.
808 * However, if fromcode is zero then fromcodes is assumed to be the
809 * name of a single charset even if it contains a colon.
811 static size_t convert_file_from_to (FILE * file,
812 const char *fromcodes,
813 const char *tocodes, char **fromcode,
814 char **tocode, CONTENT * info)
822 /* Count the tocodes */
824 for (c = tocodes; c; c = c1 ? c1 + 1 : 0) {
825 if ((c1 = strchr (c, ':')) == c)
831 tcode = safe_malloc (ncodes * sizeof (char *));
832 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++) {
833 if ((c1 = strchr (c, ':')) == c)
835 tcode[i] = mutt_substrdup (c, c1);
840 /* Try each fromcode in turn */
841 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0) {
842 if ((c1 = strchr (c, ':')) == c)
844 fcode = mutt_substrdup (c, c1);
846 ret = convert_file_to (file, fcode, ncodes, (const char **) tcode,
848 if (ret != (size_t) (-1)) {
858 /* There is only one fromcode */
859 ret = convert_file_to (file, fromcodes, ncodes, (const char **) tcode,
861 if (ret != (size_t) (-1)) {
868 for (i = 0; i < ncodes; i++)
877 * Analyze the contents of a file to determine which MIME encoding to use.
878 * Also set the body charset, sometimes, or not.
880 CONTENT *mutt_get_content_info (const char *fname, BODY * b)
896 if (stat (fname, &sb) == -1) {
897 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
901 if (!S_ISREG (sb.st_mode)) {
902 mutt_error (_("%s isn't a regular file."), fname);
906 if ((fp = fopen (fname, "r")) == NULL) {
907 dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
908 fname, strerror (errno), errno));
912 info = safe_calloc (1, sizeof (CONTENT));
913 memset (&state, 0, sizeof (state));
915 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset)) {
916 char *chs = mutt_get_parameter ("charset", b->parameter);
917 char *fchs = b->use_disp ? ((FileCharset && *FileCharset) ?
918 FileCharset : Charset) : Charset;
919 if (Charset && (chs || SendCharset) &&
920 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
921 &fromcode, &tocode, info) != (size_t) (-1)) {
923 mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
924 mutt_set_parameter ("charset", chsbuf, &b->parameter);
926 b->file_charset = fromcode;
934 while ((r = fread (buffer, 1, sizeof (buffer), fp)))
935 update_content_info (info, &state, buffer, r);
936 update_content_info (info, &state, 0, 0);
940 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
941 mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
943 && !mutt_is_us_ascii (Charset) ? Charset :
944 "unknown-8bit"), &b->parameter);
949 /* Given a file with path ``s'', see if there is a registered MIME type.
950 * returns the major MIME type, and copies the subtype to ``d''. First look
951 * for ~/.mime.types, then look in a system mime.types if we can find one.
952 * The longest match is used so that we can match `ps.gz' when `gz' also
956 int mutt_lookup_mime_type (BODY * att, const char *path)
960 char buf[LONG_STRING];
961 char subtype[STRING], xtype[STRING];
963 int szf, sze, cur_sze;
971 szf = mutt_strlen (path);
973 for (count = 0; count < 3; count++) {
975 * can't use strtok() because we use it in an inner loop below, so use
976 * a switch statement here instead.
980 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL (Homedir));
983 strfcpy (buf, SYSCONFDIR "/mime.types", sizeof (buf));
986 strfcpy (buf, PKGDATADIR "/mime.types", sizeof (buf));
991 "mutt_lookup_mime_type: Internal error, count = %d.\n",
993 goto bye; /* shouldn't happen */
996 if ((f = fopen (buf, "r")) != NULL) {
997 while (fgets (buf, sizeof (buf) - 1, f) != NULL) {
998 /* weed out any comments */
999 if ((p = strchr (buf, '#')))
1002 /* remove any leading space. */
1006 /* position on the next field in this line */
1007 if ((p = strpbrk (ct, " \t")) == NULL)
1012 /* cycle through the file extensions */
1013 while ((p = strtok (p, " \t\n"))) {
1014 sze = mutt_strlen (p);
1015 if ((sze > cur_sze) && (szf >= sze) &&
1016 (mutt_strcasecmp (path + szf - sze, p) == 0
1017 || ascii_strcasecmp (path + szf - sze, p) == 0) && (szf == sze
1024 /* get the content-type */
1026 if ((p = strchr (ct, '/')) == NULL) {
1027 /* malformed line, just skip it. */
1032 for (q = p; *q && !ISSPACE (*q); q++);
1034 mutt_substrcpy (subtype, p, q, sizeof (subtype));
1036 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1037 strfcpy (xtype, ct, sizeof (xtype));
1050 if (type != TYPEOTHER || *xtype != '\0') {
1052 mutt_str_replace (&att->subtype, subtype);
1053 mutt_str_replace (&att->xtype, xtype);
1059 void mutt_message_to_7bit (BODY * a, FILE * fp)
1061 char temp[_POSIX_PATH_MAX];
1067 if (!a->filename && fp)
1069 else if (!a->filename || !(fpin = fopen (a->filename, "r"))) {
1070 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1075 if (stat (a->filename, &sb) == -1) {
1076 mutt_perror ("stat");
1079 a->length = sb.st_size;
1083 if (!(fpout = safe_fopen (temp, "w+"))) {
1084 mutt_perror ("fopen");
1088 fseek (fpin, a->offset, 0);
1089 a->parts = mutt_parse_messageRFC822 (fpin, a);
1091 transform_to_7bit (a->parts, fpin);
1093 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1094 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1096 fputs ("Mime-Version: 1.0\n", fpout);
1097 mutt_write_mime_header (a->parts, fpout);
1098 fputc ('\n', fpout);
1099 mutt_write_mime_body (a->parts, fpout);
1111 a->encoding = ENC7BIT;
1112 a->d_filename = a->filename;
1113 if (a->filename && a->unlink)
1114 unlink (a->filename);
1115 a->filename = safe_strdup (temp);
1117 if (stat (a->filename, &sb) == -1) {
1118 mutt_perror ("stat");
1121 a->length = sb.st_size;
1122 mutt_free_body (&a->parts);
1123 a->hdr->content = NULL;
1126 static void transform_to_7bit (BODY * a, FILE * fpin)
1128 char buff[_POSIX_PATH_MAX];
1132 memset (&s, 0, sizeof (s));
1133 for (; a; a = a->next) {
1134 if (a->type == TYPEMULTIPART) {
1135 if (a->encoding != ENC7BIT)
1136 a->encoding = ENC7BIT;
1138 transform_to_7bit (a->parts, fpin);
1140 else if (mutt_is_message_type (a->type, a->subtype)) {
1141 mutt_message_to_7bit (a, fpin);
1145 a->force_charset = 1;
1148 if ((s.fpout = safe_fopen (buff, "w")) == NULL) {
1149 mutt_perror ("fopen");
1153 mutt_decode_attachment (a, &s);
1155 a->d_filename = a->filename;
1156 a->filename = safe_strdup (buff);
1158 if (stat (a->filename, &sb) == -1) {
1159 mutt_perror ("stat");
1162 a->length = sb.st_size;
1164 mutt_update_encoding (a);
1165 if (a->encoding == ENC8BIT)
1166 a->encoding = ENCQUOTEDPRINTABLE;
1167 else if (a->encoding == ENCBINARY)
1168 a->encoding = ENCBASE64;
1173 /* determine which Content-Transfer-Encoding to use */
1174 static void mutt_set_encoding (BODY * b, CONTENT * info)
1176 char send_charset[SHORT_STRING];
1178 if (b->type == TYPETEXT) {
1180 mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1181 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8))
1182 || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1183 b->encoding = ENCQUOTEDPRINTABLE;
1184 else if (info->hibin)
1185 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1187 b->encoding = ENC7BIT;
1189 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART) {
1190 if (info->lobin || info->hibin) {
1191 if (option (OPTALLOW8BIT) && !info->lobin)
1192 b->encoding = ENC8BIT;
1194 mutt_message_to_7bit (b, NULL);
1197 b->encoding = ENC7BIT;
1199 else if (b->type == TYPEAPPLICATION
1200 && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1201 b->encoding = ENC7BIT;
1204 if (info->lobin || info->hibin || info->binary || info->linemax > 990
1205 || info->cr || ( /* option (OPTENCODEFROM) && */ info->from))
1208 /* Determine which encoding is smaller */
1209 if (1.33 * (float) (info->lobin + info->hibin + info->ascii) <
1210 3.0 * (float) (info->lobin + info->hibin) + (float) info->ascii)
1211 b->encoding = ENCBASE64;
1213 b->encoding = ENCQUOTEDPRINTABLE;
1217 b->encoding = ENC7BIT;
1221 void mutt_stamp_attachment (BODY * a)
1223 a->stamp = time (NULL);
1226 /* Get a body's character set */
1228 char *mutt_get_body_charset (char *d, size_t dlen, BODY * b)
1232 if (b && b->type != TYPETEXT)
1236 p = mutt_get_parameter ("charset", b->parameter);
1239 mutt_canonical_charset (d, dlen, NONULL (p));
1241 strfcpy (d, "us-ascii", dlen);
1247 /* Assumes called from send mode where BODY->filename points to actual file */
1248 void mutt_update_encoding (BODY * a)
1251 char chsbuff[STRING];
1253 /* override noconv when it's us-ascii */
1254 if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1257 if (!a->force_charset && !a->noconv)
1258 mutt_delete_parameter ("charset", &a->parameter);
1260 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1263 mutt_set_encoding (a, info);
1264 mutt_stamp_attachment (a);
1271 BODY *mutt_make_message_attach (CONTEXT * ctx, HEADER * hdr, int attach_msg)
1273 char buffer[LONG_STRING];
1276 int cmflags, chflags;
1277 int pgp = WithCrypto ? hdr->security : 0;
1280 if ((option (OPTMIMEFORWDECODE) || option (OPTFORWDECRYPT)) &&
1281 (hdr->security & ENCRYPT)) {
1282 if (!crypt_valid_passphrase (hdr->security))
1287 mutt_mktemp (buffer);
1288 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1291 body = mutt_new_body ();
1292 body->type = TYPEMESSAGE;
1293 body->subtype = safe_strdup ("rfc822");
1294 body->filename = safe_strdup (buffer);
1297 body->disposition = DISPINLINE;
1300 mutt_parse_mime_message (ctx, hdr);
1305 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1306 if (!attach_msg && option (OPTMIMEFORWDECODE)) {
1307 chflags |= CH_MIME | CH_TXTPLAIN;
1308 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1309 if ((WithCrypto & APPLICATION_PGP))
1311 if ((WithCrypto & APPLICATION_SMIME))
1312 pgp &= ~SMIMEENCRYPT;
1314 else if (WithCrypto && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT)) {
1315 if ((WithCrypto & APPLICATION_PGP)
1316 && mutt_is_multipart_encrypted (hdr->content)) {
1317 chflags |= CH_MIME | CH_NONEWLINE;
1318 cmflags = M_CM_DECODE_PGP;
1321 else if ((WithCrypto & APPLICATION_PGP)
1322 && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT)) {
1323 chflags |= CH_MIME | CH_TXTPLAIN;
1324 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1327 else if ((WithCrypto & APPLICATION_SMIME)
1328 && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT) {
1329 chflags |= CH_MIME | CH_TXTPLAIN;
1330 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1331 pgp &= ~SMIMEENCRYPT;
1335 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1340 body->hdr = mutt_new_header ();
1341 body->hdr->offset = 0;
1342 /* we don't need the user headers here */
1343 body->hdr->env = mutt_read_rfc822_header (fp, body->hdr, 0, 0);
1345 body->hdr->security = pgp;
1346 mutt_update_encoding (body);
1347 body->parts = body->hdr->content;
1354 BODY *mutt_make_file_attach (const char *path)
1359 att = mutt_new_body ();
1360 att->filename = safe_strdup (path);
1362 /* Attempt to determine the appropriate content-type based on the filename
1369 mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf),
1370 path)) != TYPEOTHER || *xbuf != '\0') {
1372 att->subtype = safe_strdup (buf);
1373 att->xtype = safe_strdup (xbuf);
1378 mutt_lookup_mime_type (att, path);
1382 if ((info = mutt_get_content_info (path, att)) == NULL) {
1383 mutt_free_body (&att);
1387 if (!att->subtype) {
1388 if (info->lobin == 0
1389 || (info->lobin + info->hibin + info->ascii) / info->lobin >= 10) {
1391 * Statistically speaking, there should be more than 10% "lobin"
1392 * chars if this is really a binary file...
1394 att->type = TYPETEXT;
1395 att->subtype = safe_strdup ("plain");
1398 att->type = TYPEAPPLICATION;
1399 att->subtype = safe_strdup ("octet-stream");
1403 mutt_update_encoding (att);
1407 static int get_toplevel_encoding (BODY * a)
1411 for (; a; a = a->next) {
1412 if (a->encoding == ENCBINARY)
1414 else if (a->encoding == ENC8BIT)
1421 BODY *mutt_make_multipart (BODY * b)
1425 new = mutt_new_body ();
1426 new->type = TYPEMULTIPART;
1427 new->subtype = safe_strdup ("mixed");
1428 new->encoding = get_toplevel_encoding (b);
1429 mutt_generate_boundary (&new->parameter);
1431 new->disposition = DISPINLINE;
1437 /* remove the multipart body if it exists */
1438 BODY *mutt_remove_multipart (BODY * b)
1446 mutt_free_body (&t);
1451 char *mutt_make_date (char *s, size_t len)
1453 time_t t = time (NULL);
1454 struct tm *l = localtime (&t);
1455 time_t tz = mutt_local_tz (t);
1459 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1460 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1461 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1462 (int) tz / 60, (int) abs (tz) % 60);
1466 /* wrapper around mutt_write_address() so we can handle very large
1467 recipient lists without needing a huge temporary buffer in memory */
1468 void mutt_write_address_list (ADDRESS * adr, FILE * fp, int linelen,
1472 char buf[LONG_STRING];
1480 rfc822_write_address (buf, sizeof (buf), adr, display);
1481 len = mutt_strlen (buf);
1482 if (count && linelen + len > 74) {
1484 linelen = len + 8; /* tab is usually about 8 spaces... */
1487 if (count && adr->mailbox) {
1495 if (!adr->group && adr->next && adr->next->mailbox) {
1505 /* arbitrary number of elements to grow the array by */
1510 /* need to write the list in reverse because they are stored in reverse order
1511 * when parsed to speed up threading
1513 void mutt_write_references (LIST * r, FILE * f)
1516 int refcnt = 0, refmax = 0;
1518 for (; (TrimRef == 0 || refcnt < TrimRef) && r; r = r->next) {
1519 if (refcnt == refmax)
1520 safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1524 while (refcnt-- > 0) {
1526 fputs (ref[refcnt]->data, f);
1532 /* Note: all RFC2047 encoding should be done outside of this routine, except
1533 * for the "real name." This will allow this routine to be used more than
1534 * once, if necessary.
1536 * Likewise, all IDN processing should happen outside of this routine.
1538 * mode == 1 => "lite" mode (used for edit_hdrs)
1539 * mode == 0 => normal mode. write full header + MIME headers
1540 * mode == -1 => write just the envelope info (used for postponing messages)
1542 * privacy != 0 => will omit any headers which may identify the user.
1543 * Output generated is suitable for being sent through
1544 * anonymous remailer chains.
1548 int mutt_write_rfc822_header (FILE * fp, ENVELOPE * env, BODY * attach,
1549 int mode, int privacy)
1551 char buffer[LONG_STRING];
1553 LIST *tmp = env->userhdrs;
1554 int has_agent = 0; /* user defined user-agent header field exists */
1557 if (!option (OPTNEWSSEND))
1559 if (mode == 0 && !privacy)
1560 fputs (mutt_make_date (buffer, sizeof (buffer)), fp);
1562 /* OPTUSEFROM is not consulted here so that we can still write a From:
1563 * field if the user sets it with the `my_hdr' command
1565 if (env->from && !privacy) {
1567 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1568 fprintf (fp, "From: %s\n", buffer);
1573 mutt_write_address_list (env->to, fp, 4, 0);
1577 if (!option (OPTNEWSSEND))
1579 fputs ("To: \n", fp);
1583 mutt_write_address_list (env->cc, fp, 4, 0);
1587 if (!option (OPTNEWSSEND))
1589 fputs ("Cc: \n", fp);
1592 if (mode != 0 || option (OPTWRITEBCC)) {
1593 fputs ("Bcc: ", fp);
1594 mutt_write_address_list (env->bcc, fp, 5, 0);
1599 if (!option (OPTNEWSSEND))
1601 fputs ("Bcc: \n", fp);
1604 if (env->newsgroups)
1605 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1606 else if (mode == 1 && option (OPTNEWSSEND))
1607 fputs ("Newsgroups: \n", fp);
1609 if (env->followup_to)
1610 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1611 else if (mode == 1 && option (OPTNEWSSEND))
1612 fputs ("Followup-To: \n", fp);
1614 if (env->x_comment_to)
1615 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1616 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO))
1617 fputs ("X-Comment-To: \n", fp);
1621 fprintf (fp, "Subject: %s\n", env->subject);
1623 fputs ("Subject: \n", fp);
1625 /* save message id if the user has set it */
1626 if (env->message_id && !privacy)
1627 fprintf (fp, "Message-ID: %s\n", env->message_id);
1629 if (env->reply_to) {
1630 fputs ("Reply-To: ", fp);
1631 mutt_write_address_list (env->reply_to, fp, 10, 0);
1634 fputs ("Reply-To: \n", fp);
1636 if (env->mail_followup_to)
1638 if (!option (OPTNEWSSEND))
1641 fputs ("Mail-Followup-To: ", fp);
1642 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1646 if (env->references) {
1647 fputs ("References:", fp);
1648 mutt_write_references (env->references, fp);
1652 /* Add the MIME headers */
1653 fputs ("Mime-Version: 1.0\n", fp);
1654 mutt_write_mime_header (attach, fp);
1657 if (env->in_reply_to) {
1658 fputs ("In-Reply-To:", fp);
1659 mutt_write_references (env->in_reply_to, fp);
1663 /* Add any user defined headers */
1664 for (; tmp; tmp = tmp->next) {
1665 if ((p = strchr (tmp->data, ':'))) {
1669 continue; /* don't emit empty fields. */
1671 /* check to see if the user has overridden the user-agent field */
1672 if (!ascii_strncasecmp ("user-agent", tmp->data, 10)) {
1678 fputs (tmp->data, fp);
1683 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent) {
1687 if (OperatingSystem != NULL) {
1688 os = OperatingSystem;
1691 if (uname (&un) == -1) {
1698 /* Add a vanity header */
1699 fprintf (fp, "User-Agent: mutt-ng %s (%s)\n", MUTT_VERSION, os);
1702 return (ferror (fp) == 0 ? 0 : -1);
1705 static void encode_headers (LIST * h)
1711 for (; h; h = h->next) {
1712 if (!(p = strchr (h->data, ':')))
1718 tmp = safe_strdup (p);
1723 rfc2047_encode_string (&tmp);
1724 safe_realloc (&h->data,
1725 mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
1727 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
1733 const char *mutt_fqdn (short may_hide_host)
1737 if (Fqdn && Fqdn[0] != '@') {
1740 if (may_hide_host && option (OPTHIDDENHOST)) {
1741 if ((p = strchr (Fqdn, '.')))
1744 /* sanity check: don't hide the host if
1745 * the fqdn is something like detebe.org.
1748 if (!p || !(q = strchr (p, '.')))
1756 static char mutt_normalized_char (char c)
1760 if (strchr (".!#$%&'*+-/=?^_`{|}~", c))
1762 return '.'; /* normalized character (we're stricter than RFC2822, 3.6.4) */
1765 static void mutt_gen_localpart (char *buf, unsigned int len, char *fmt)
1769 char tmp[SHORT_STRING];
1776 for (; *fmt; ++fmt) {
1782 snprintf (tmp, sizeof (tmp), "%02d", tm->tm_mday);
1783 safe_strncat (buf, len, tmp, 2);
1786 snprintf (tmp, sizeof (tmp), "%02d", tm->tm_hour);
1787 safe_strncat (buf, len, tmp, 2);
1790 snprintf (tmp, sizeof (tmp), "%02d", tm->tm_mon + 1);
1791 safe_strncat (buf, len, tmp, 2);
1794 snprintf (tmp, sizeof (tmp), "%02d", tm->tm_min);
1795 safe_strncat (buf, len, tmp, 2);
1798 snprintf (tmp, sizeof (tmp), "%lo", (unsigned long) now);
1799 safe_strncat (buf, len, tmp, strlen (tmp));
1802 snprintf (tmp, sizeof (tmp), "%u", (unsigned int) getpid ());
1803 safe_strncat (buf, len, tmp, strlen (tmp));
1806 snprintf (tmp, sizeof (tmp), "%c", MsgIdPfx);
1807 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1808 safe_strncat (buf, len, tmp, 1);
1811 snprintf (tmp, sizeof (tmp), "%u", (unsigned int) rand ());
1812 safe_strncat (buf, len, tmp, strlen (tmp));
1815 snprintf (tmp, sizeof (tmp), "%x", (unsigned int) rand ());
1816 safe_strncat (buf, len, tmp, strlen (tmp));
1819 snprintf (tmp, sizeof (tmp), "%02d", tm->tm_sec);
1820 safe_strncat (buf, len, tmp, 2);
1823 snprintf (tmp, sizeof (tmp), "%u", (unsigned int) now);
1824 safe_strncat (buf, len, tmp, strlen (tmp));
1827 snprintf (tmp, sizeof (tmp), "%x", (unsigned int) now);
1828 safe_strncat (buf, len, tmp, strlen (tmp));
1831 snprintf (tmp, sizeof (tmp), "%04d", tm->tm_year + 1900); /* this will break in the year 10000 ;-) */
1832 safe_strncat (buf, len, tmp, 4);
1835 safe_strncat (buf, len, "%", 1);
1838 safe_strncat (buf, len, ".", 1); /* invalid formats are replaced by '.' */
1845 c = mutt_normalized_char (*fmt); /* @todo: filter out invalid characters */
1846 safe_strncat (buf, len, &c, 1);
1851 char *mutt_gen_msgid (void)
1853 char buf[SHORT_STRING];
1854 char localpart[SHORT_STRING];
1855 unsigned int localpart_length;
1862 if (!(fqdn = mutt_fqdn (0)))
1863 fqdn = NONULL (Hostname);
1865 localpart_length = sizeof (buf) - strlen (fqdn) - 4; /* the 4 characters are '<', '@', '>' and '\0' */
1867 mutt_gen_localpart (localpart, localpart_length, MsgIdFormat);
1869 snprintf (buf, sizeof (buf), "<%s@%s>", localpart, fqdn);
1870 return (safe_strdup (buf));
1873 static RETSIGTYPE alarm_handler (int sig)
1878 /* invoke sendmail in a subshell
1879 path (in) path to program to execute
1880 args (in) arguments to pass to program
1881 msg (in) temp file containing message to send
1882 tempfile (out) if sendmail is put in the background, this points
1883 to the temporary file containing the stdout of the
1886 send_msg (const char *path, char **args, const char *msg, char **tempfile)
1892 mutt_block_signals_system ();
1895 /* we also don't want to be stopped right now */
1896 sigaddset (&set, SIGTSTP);
1897 sigprocmask (SIG_BLOCK, &set, NULL);
1899 if (SendmailWait >= 0) {
1900 char tmp[_POSIX_PATH_MAX];
1903 *tempfile = safe_strdup (tmp);
1906 if ((pid = fork ()) == 0) {
1907 struct sigaction act, oldalrm;
1909 /* save parent's ID before setsid() */
1912 /* we want the delivery to continue even after the main process dies,
1913 * so we put ourselves into another session right away
1917 /* next we close all open files */
1918 #if defined(OPEN_MAX)
1919 for (fd = 0; fd < OPEN_MAX; fd++)
1921 #elif defined(_POSIX_OPEN_MAX)
1922 for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1930 /* now the second fork() */
1931 if ((pid = fork ()) == 0) {
1932 /* "msg" will be opened as stdin */
1933 if (open (msg, O_RDONLY, 0) < 0) {
1939 if (SendmailWait >= 0) {
1940 /* *tempfile will be opened as stdout */
1941 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) <
1944 /* redirect stderr to *tempfile too */
1949 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1951 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1958 else if (pid == -1) {
1964 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1965 * SendmailWait = 0: wait forever
1966 * SendmailWait < 0: don't wait
1968 if (SendmailWait > 0) {
1970 act.sa_handler = alarm_handler;
1972 /* need to make sure waitpid() is interrupted on SIGALRM */
1973 act.sa_flags = SA_INTERRUPT;
1977 sigemptyset (&act.sa_mask);
1978 sigaction (SIGALRM, &act, &oldalrm);
1979 alarm (SendmailWait);
1981 else if (SendmailWait < 0)
1982 _exit (0xff & EX_OK);
1984 if (waitpid (pid, &st, 0) > 0) {
1985 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1986 if (SendmailWait && st == (0xff & EX_OK)) {
1987 unlink (*tempfile); /* no longer needed */
1992 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ? S_BKG : S_ERR;
1993 if (SendmailWait > 0) {
1999 /* reset alarm; not really needed, but... */
2001 sigaction (SIGALRM, &oldalrm, NULL);
2003 if (kill (ppid, 0) == -1 && errno == ESRCH) {
2004 /* the parent is already dead */
2012 sigprocmask (SIG_UNBLOCK, &set, NULL);
2014 if (pid != -1 && waitpid (pid, &st, 0) > 0)
2015 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
2017 st = S_ERR; /* error */
2019 mutt_unblock_signals_system (1);
2024 static char **add_args (char **args, size_t * argslen, size_t * argsmax,
2027 for (; addr; addr = addr->next) {
2028 /* weed out group mailboxes, since those are for display only */
2029 if (addr->mailbox && !addr->group) {
2030 if (*argslen == *argsmax)
2031 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2032 args[(*argslen)++] = addr->mailbox;
2038 static char **add_option (char **args, size_t * argslen, size_t * argsmax,
2041 if (*argslen == *argsmax)
2042 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2043 args[(*argslen)++] = s;
2047 static const char *strsysexit (int e)
2051 for (i = 0; sysexits_h[i].str; i++) {
2052 if (e == sysexits_h[i].v)
2056 return sysexits_h[i].str;
2060 static int mutt_invoke_sendmail (ADDRESS * from, /* the sender */
2061 ADDRESS * to, ADDRESS * cc, ADDRESS * bcc, /* recips */
2062 const char *msg, /* file containing message */
2064 { /* message contains 8bit chars */
2065 char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
2067 size_t argslen = 0, argsmax = 0;
2071 if (option (OPTNEWSSEND)) {
2072 char cmd[LONG_STRING];
2074 mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0,
2077 i = nntp_post (msg);
2082 s = safe_strdup (cmd);
2086 s = safe_strdup (Sendmail);
2090 while ((ps = strtok (ps, " "))) {
2091 if (argslen == argsmax)
2092 safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2095 args[argslen++] = ps;
2097 path = safe_strdup (ps);
2098 ps = strrchr (ps, '/');
2103 args[argslen++] = ps;
2110 if (!option (OPTNEWSSEND)) {
2112 if (eightbit && option (OPTUSE8BITMIME))
2113 args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2115 if (option (OPTENVFROM) && from && !from->next) {
2116 args = add_option (args, &argslen, &argsmax, "-f");
2117 args = add_args (args, &argslen, &argsmax, from);
2120 args = add_option (args, &argslen, &argsmax, "-N");
2121 args = add_option (args, &argslen, &argsmax, DsnNotify);
2124 args = add_option (args, &argslen, &argsmax, "-R");
2125 args = add_option (args, &argslen, &argsmax, DsnReturn);
2127 args = add_option (args, &argslen, &argsmax, "--");
2128 args = add_args (args, &argslen, &argsmax, to);
2129 args = add_args (args, &argslen, &argsmax, cc);
2130 args = add_args (args, &argslen, &argsmax, bcc);
2135 if (argslen == argsmax)
2136 safe_realloc (&args, sizeof (char *) * (++argsmax));
2138 args[argslen++] = NULL;
2140 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff)) {
2142 const char *e = strsysexit (i);
2145 mutt_error (_("Error sending message, child exited %d (%s)."), i,
2150 if (stat (childout, &st) == 0 && st.st_size > 0)
2151 mutt_do_pager (_("Output of the delivery process"), childout, 0,
2164 if (i == (EX_OK & 0xff))
2166 else if (i == S_BKG)
2173 int mutt_invoke_mta (ADDRESS * from, /* the sender */
2174 ADDRESS * to, ADDRESS * cc, ADDRESS * bcc, /* recips */
2175 const char *msg, /* file containing message */
2177 { /* message contains 8bit chars */
2180 return mutt_invoke_libesmtp (from, to, cc, bcc, msg, eightbit);
2183 return mutt_invoke_sendmail (from, to, cc, bcc, msg, eightbit);
2186 /* appends string 'b' to string 'a', and returns the pointer to the new
2188 char *mutt_append_string (char *a, const char *b)
2190 size_t la = mutt_strlen (a);
2192 safe_realloc (&a, la + mutt_strlen (b) + 1);
2193 strcpy (a + la, b); /* __STRCPY_CHECKED__ */
2197 /* returns 1 if char `c' needs to be quoted to protect from shell
2198 interpretation when executing commands in a subshell */
2199 #define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
2201 /* returns 1 if string `s' contains characters which could cause problems
2202 when used on a command line to execute a command */
2203 int mutt_needs_quote (const char *s)
2206 if (INVALID_CHAR (*s))
2213 /* Quote a string to prevent shell escapes when this string is used on the
2214 command line to send mail. */
2215 char *mutt_quote_string (const char *s)
2220 rlen = mutt_strlen (s) + 3;
2221 pr = r = (char *) safe_malloc (rlen);
2224 if (INVALID_CHAR (*s)) {
2227 safe_realloc (&r, ++rlen);
2238 /* For postponing (!final) do the necessary encodings only */
2239 void mutt_prepare_envelope (ENVELOPE * env, int final)
2241 char buffer[LONG_STRING];
2244 if (env->bcc && !(env->to || env->cc)) {
2245 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2246 * recipients if there is no To: or Cc: field, so attempt to suppress
2247 * it by using an empty To: field.
2249 env->to = rfc822_new_address ();
2251 env->to->next = rfc822_new_address ();
2254 rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2257 env->to->mailbox = safe_strdup (buffer);
2260 mutt_set_followup_to (env);
2262 if (!env->message_id)
2263 env->message_id = mutt_gen_msgid ();
2266 /* Take care of 8-bit => 7-bit conversion. */
2267 rfc2047_encode_adrlist (env->to, "To");
2268 rfc2047_encode_adrlist (env->cc, "Cc");
2269 rfc2047_encode_adrlist (env->bcc, "Bcc");
2270 rfc2047_encode_adrlist (env->from, "From");
2271 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2272 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2276 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2279 rfc2047_encode_string (&env->subject);
2281 encode_headers (env->userhdrs);
2284 void mutt_unprepare_envelope (ENVELOPE * env)
2288 for (item = env->userhdrs; item; item = item->next)
2289 rfc2047_decode (&item->data);
2291 rfc822_free_address (&env->mail_followup_to);
2293 /* back conversions */
2294 rfc2047_decode_adrlist (env->to);
2295 rfc2047_decode_adrlist (env->cc);
2296 rfc2047_decode_adrlist (env->bcc);
2297 rfc2047_decode_adrlist (env->from);
2298 rfc2047_decode_adrlist (env->reply_to);
2299 rfc2047_decode (&env->subject);
2302 static int _mutt_bounce_message (FILE * fp, HEADER * h, ADDRESS * to,
2303 const char *resent_from, ADDRESS * env_from)
2307 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2308 MESSAGE *msg = NULL;
2311 /* Try to bounce each message out, aborting if we get any failures. */
2312 for (i = 0; i < Context->msgcount; i++)
2313 if (Context->hdrs[i]->tagged)
2315 _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from,
2320 /* If we failed to open a message, return with error */
2321 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2327 mutt_mktemp (tempfile);
2328 if ((f = safe_fopen (tempfile, "w")) != NULL) {
2329 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2331 if (!option (OPTBOUNCEDELIVERED))
2332 ch_flags |= CH_WEED_DELIVERED;
2334 fseek (fp, h->offset, 0);
2335 fprintf (f, "Resent-From: %s", resent_from);
2336 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof (date)));
2337 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid ());
2338 fputs ("Resent-To: ", f);
2339 mutt_write_address_list (to, f, 11, 0);
2340 mutt_copy_header (fp, h, f, ch_flags, NULL);
2342 mutt_copy_bytes (fp, f, h->content->length);
2345 ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2346 h->content->encoding == ENC8BIT);
2350 mx_close_message (&msg);
2355 int mutt_bounce_message (FILE * fp, HEADER * h, ADDRESS * to)
2358 const char *fqdn = mutt_fqdn (1);
2359 char resent_from[STRING];
2363 resent_from[0] = '\0';
2364 from = mutt_default_from ();
2367 rfc822_qualify (from, fqdn);
2369 rfc2047_encode_adrlist (from, "Resent-From");
2370 if (mutt_addrlist_to_idna (from, &err)) {
2371 mutt_error (_("Bad IDN %s while preparing resent-from."), err);
2374 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2377 unset_option (OPTNEWSSEND);
2380 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2382 rfc822_free_address (&from);
2388 /* given a list of addresses, return a list of unique addresses */
2389 ADDRESS *mutt_remove_duplicates (ADDRESS * addr)
2391 ADDRESS *top = addr;
2392 ADDRESS **last = ⊤
2397 for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next) {
2398 if (tmp->mailbox && addr->mailbox &&
2399 !ascii_strcasecmp (addr->mailbox, tmp->mailbox)) {
2406 dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
2412 rfc822_free_address (&addr);
2425 static void set_noconv_flags (BODY * b, short flag)
2427 for (; b; b = b->next) {
2428 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2429 set_noconv_flags (b->parts, flag);
2430 else if (b->type == TYPETEXT && b->noconv) {
2432 mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2434 mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2439 int mutt_write_fcc (const char *path, HEADER * hdr, const char *msgid,
2440 int post, char *fcc)
2444 char tempfile[_POSIX_PATH_MAX];
2445 FILE *tempfp = NULL;
2449 set_noconv_flags (hdr->content, 1);
2451 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL) {
2454 "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2459 /* We need to add a Content-Length field to avoid problems where a line in
2460 * the message body begins with "From "
2462 if (f.magic == M_MMDF || f.magic == M_MBOX) {
2463 mutt_mktemp (tempfile);
2464 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL) {
2465 mutt_perror (tempfile);
2466 mx_close_mailbox (&f, NULL);
2471 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2472 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL) {
2473 mx_close_mailbox (&f, NULL);
2477 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2478 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2480 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0,
2483 /* (postponment) if this was a reply of some sort, <msgid> contians the
2484 * Message-ID: of message replied to. Save it using a special X-Mutt-
2485 * header so it can be picked up if the message is recalled at a later
2486 * point in time. This will allow the message to be marked as replied if
2487 * the same mailbox is still open.
2490 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2492 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2493 * it can be picked up when the message is recalled
2496 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2497 fprintf (msg->fp, "Status: RO\n");
2501 /* (postponment) if the mail is to be signed or encrypted, save this info */
2502 if ((WithCrypto & APPLICATION_PGP)
2503 && post && (hdr->security & APPLICATION_PGP)) {
2504 fputs ("X-Mutt-PGP: ", msg->fp);
2505 if (hdr->security & ENCRYPT)
2506 fputc ('E', msg->fp);
2507 if (hdr->security & SIGN) {
2508 fputc ('S', msg->fp);
2509 if (PgpSignAs && *PgpSignAs)
2510 fprintf (msg->fp, "<%s>", PgpSignAs);
2512 if (hdr->security & INLINE)
2513 fputc ('I', msg->fp);
2514 fputc ('\n', msg->fp);
2517 /* (postponment) if the mail is to be signed or encrypted, save this info */
2518 if ((WithCrypto & APPLICATION_SMIME)
2519 && post && (hdr->security & APPLICATION_SMIME)) {
2520 fputs ("X-Mutt-SMIME: ", msg->fp);
2521 if (hdr->security & ENCRYPT) {
2522 fputc ('E', msg->fp);
2523 if (SmimeCryptAlg && *SmimeCryptAlg)
2524 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2526 if (hdr->security & SIGN) {
2527 fputc ('S', msg->fp);
2528 if (SmimeDefaultKey && *SmimeDefaultKey)
2529 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2531 if (hdr->security & INLINE)
2532 fputc ('I', msg->fp);
2533 fputc ('\n', msg->fp);
2537 /* (postponement) if the mail is to be sent through a mixmaster
2538 * chain, save that information
2541 if (post && hdr->chain && hdr->chain) {
2544 fputs ("X-Mutt-Mix:", msg->fp);
2545 for (p = hdr->chain; p; p = p->next)
2546 fprintf (msg->fp, " %s", (char *) p->data);
2548 fputc ('\n', msg->fp);
2553 char sasha[LONG_STRING];
2556 mutt_write_mime_body (hdr->content, tempfp);
2558 /* make sure the last line ends with a newline. Emacs doesn't ensure
2559 * this will happen, and it can cause problems parsing the mailbox
2562 fseek (tempfp, -1, 2);
2563 if (fgetc (tempfp) != '\n') {
2564 fseek (tempfp, 0, 2);
2565 fputc ('\n', tempfp);
2569 if (ferror (tempfp)) {
2571 (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2574 mx_commit_message (msg, &f); /* XXX - really? */
2575 mx_close_message (&msg);
2576 mx_close_mailbox (&f, NULL);
2580 /* count the number of lines */
2582 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2584 fprintf (msg->fp, "Content-Length: %ld\n", (long) ftell (tempfp));
2585 fprintf (msg->fp, "Lines: %d\n\n", lines);
2587 /* copy the body and clean up */
2589 r = mutt_copy_stream (tempfp, msg->fp);
2590 if (fclose (tempfp) != 0)
2592 /* if there was an error, leave the temp version */
2597 fputc ('\n', msg->fp); /* finish off the header */
2598 r = mutt_write_mime_body (hdr->content, msg->fp);
2601 if (mx_commit_message (msg, &f) != 0)
2603 mx_close_message (&msg);
2604 mx_close_mailbox (&f, NULL);
2607 set_noconv_flags (hdr->content, 0);