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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "mutt_curses.h"
35 #include "mutt_crypt.h"
36 #include "mutt_idna.h"
48 #ifdef HAVE_SYSEXITS_H
50 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
54 /* If you are debugging this file, comment out the following line. */
63 extern char RFC822Specials[];
65 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
67 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
70 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
71 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
72 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
73 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
77 static char MsgIdPfx = 'A';
79 static void transform_to_7bit (BODY *a, FILE *fpin);
81 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
84 char line[77], savechar;
86 while ((c = fgetconv (fc)) != EOF)
88 /* Wrap the line if needed. */
89 if (linelen == 76 && ((istext && c != '\n') || !istext))
91 /* If the last character is "quoted", then be sure to move all three
92 * characters to the next line. Otherwise, just move the last
95 if (line[linelen-3] == '=')
102 line[1] = line[linelen-2];
103 line[2] = line[linelen-1];
108 savechar = line[linelen-1];
109 line[linelen-1] = '=';
118 /* Escape lines that begin with/only contain "the message separator". */
119 if (linelen == 4 && !mutt_strncmp ("From", line, 4))
121 strfcpy (line, "=46rom", sizeof (line));
124 else if (linelen == 4 && !mutt_strncmp ("from", line, 4))
126 strfcpy (line, "=66rom", sizeof (line));
129 else if (linelen == 1 && line[0] == '.')
131 strfcpy (line, "=2E", sizeof (line));
136 if (c == '\n' && istext)
138 /* Check to make sure there is no trailing space on this line. */
139 if (linelen > 0 && (line[linelen-1] == ' ' || line[linelen-1] == '\t'))
143 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
148 int savechar = line[linelen-1];
150 line[linelen-1] = '=';
153 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
164 else if (c != 9 && (c < 32 || c > 126 || c == '='))
166 /* Check to make sure there is enough room for the quoted character.
167 * If not, wrap to the next line.
171 line[linelen++] = '=';
177 sprintf (line+linelen,"=%2.2X", (unsigned char) c);
182 /* Don't worry about wrapping the line here. That will happen during
183 * the next iteration when I'll also know what the next character is.
189 /* Take care of anything left in the buffer */
192 if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
194 /* take care of trailing whitespace */
196 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
199 savechar = line[linelen-1];
200 line[linelen-1] = '=';
204 sprintf (line, "=%2.2X", (unsigned char) savechar);
213 static char b64_buffer[3];
214 static short b64_num;
215 static short b64_linelen;
217 static void b64_flush(FILE *fout)
224 if(b64_linelen >= 72)
230 for(i = b64_num; i < 3; i++)
231 b64_buffer[i] = '\0';
233 fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
235 fputc(B64Chars[((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf) ], fout);
240 fputc(B64Chars[((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3) ], fout);
244 fputc(B64Chars[b64_buffer[2] & 0x3f], fout);
249 while(b64_linelen % 4)
259 static void b64_putc(char c, FILE *fout)
264 b64_buffer[b64_num++] = c;
268 static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
272 b64_num = b64_linelen = 0;
274 while ((ch = fgetconv (fc)) != EOF)
276 if (istext && ch == '\n' && ch1 != '\r')
277 b64_putc('\r', fout);
285 static void encode_8bit (FGETCONV *fc, FILE *fout, int istext)
289 while ((ch = fgetconv (fc)) != EOF)
294 int mutt_write_mime_header (BODY *a, FILE *f)
304 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
308 len = 25 + mutt_strlen (a->subtype); /* approximate len. of content-type */
310 for(p = a->parameter; p; p = p->next)
320 tmp = safe_strdup (p->value);
321 encode = rfc2231_encode_string (&tmp);
322 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
324 /* Dirty hack to make messages readable by Outlook Express
325 * for the Mac: force quotes around the boundary parameter
326 * even when they aren't needed.
329 if (!ascii_strcasecmp (p->attribute, "boundary") && !strcmp (buffer, tmp))
330 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
334 tmplen = mutt_strlen (buffer) + mutt_strlen (p->attribute) + 1;
336 if (len + tmplen + 2 > 76)
347 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
355 fprintf(f, "Content-Description: %s\n", a->description);
357 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
361 if(!(fn = a->d_filename))
368 /* Strip off the leading path... */
369 if ((t = strrchr (fn, '/')))
375 tmp = safe_strdup (t);
376 encode = rfc2231_encode_string (&tmp);
377 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
379 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
385 if (a->encoding != ENC7BIT)
386 fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
388 /* Do NOT add the terminator here!!! */
389 return (ferror (f) ? -1 : 0);
392 # define write_as_text_part(a) (mutt_is_text_part(a) \
393 || ((WithCrypto & APPLICATION_PGP)\
394 && mutt_is_application_pgp(a)))
396 int mutt_write_mime_body (BODY *a, FILE *f)
398 char *p, boundary[SHORT_STRING];
399 char send_charset[SHORT_STRING];
404 if (a->type == TYPEMULTIPART)
406 /* First, find the boundary to use */
407 if (!(p = mutt_get_parameter ("boundary", a->parameter)))
409 dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
410 mutt_error _("No boundary parameter found! [report this error]");
413 strfcpy (boundary, p, sizeof (boundary));
415 for (t = a->parts; t ; t = t->next)
417 fprintf (f, "\n--%s\n", boundary);
418 if (mutt_write_mime_header (t, f) == -1)
421 if (mutt_write_mime_body (t, f) == -1)
424 fprintf (f, "\n--%s--\n", boundary);
425 return (ferror (f) ? -1 : 0);
428 /* This is pretty gross, but it's the best solution for now... */
429 if ((WithCrypto & APPLICATION_PGP)
430 && a->type == TYPEAPPLICATION
431 && mutt_strcmp (a->subtype, "pgp-encrypted") == 0)
433 fputs ("Version: 1\n", f);
437 if ((fpin = fopen (a->filename, "r")) == NULL)
439 dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
440 mutt_error (_("%s no longer exists!"), a->filename);
444 if (a->type == TYPETEXT && (!a->noconv))
445 fc = fgetconv_open (fpin, a->charset,
446 mutt_get_body_charset (send_charset, sizeof (send_charset), a),
449 fc = fgetconv_open (fpin, 0, 0, 0);
451 if (a->encoding == ENCQUOTEDPRINTABLE)
452 encode_quoted (fc, f, write_as_text_part (a));
453 else if (a->encoding == ENCBASE64)
454 encode_base64 (fc, f, write_as_text_part (a));
455 else if (a->type == TYPETEXT && (!a->noconv))
456 encode_8bit (fc, f, write_as_text_part (a));
458 mutt_copy_stream (fpin, f);
460 fgetconv_close (&fc);
463 return (ferror (f) ? -1 : 0);
466 #undef write_as_text_part
468 #define BOUNDARYLEN 16
469 void mutt_generate_boundary (PARAMETER **parm)
471 char rs[BOUNDARYLEN + 1];
476 for (i=0;i<BOUNDARYLEN;i++)
477 *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
480 mutt_set_parameter ("boundary", rs, parm);
494 static void update_content_info (CONTENT *info, CONTENT_STATE *s, char *d, size_t dlen)
497 int whitespace = s->whitespace;
499 int linelen = s->linelen;
500 int was_cr = s->was_cr;
502 if (!d) /* This signals EOF */
506 if (linelen > info->linemax)
507 info->linemax = linelen;
512 for (; dlen; d++, dlen--)
525 if (whitespace) info->space = 1;
526 if (dot) info->dot = 1;
527 if (linelen > info->linemax) info->linemax = linelen;
539 if (whitespace) info->space = 1;
540 if (dot) info->dot = 1;
541 if (linelen > info->linemax) info->linemax = linelen;
555 else if (ch == '\t' || ch == '\f')
560 else if (ch < 32 || ch == 127)
566 if ((ch == 'F') || (ch == 'f'))
577 if (linelen == 2 && ch != 'r') from = 0;
578 else if (linelen == 3 && ch != 'o') from = 0;
579 else if (linelen == 4)
581 if (ch == 'm') info->from = 1;
585 if (ch == ' ') whitespace++;
589 if (linelen > 1) dot = 0;
590 if (ch != ' ' && ch != '\t') whitespace = 0;
594 s->whitespace = whitespace;
596 s->linelen = linelen;
601 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
602 #define BUGGY_ICONV 1
605 * Find the best charset conversion of the file from fromcode into one
606 * of the tocodes. If successful, set *tocode and CONTENT *info and
607 * return the number of characters converted inexactly. If no
608 * conversion was possible, return -1.
610 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
611 * which would otherwise prevent us from knowing the number of inexact
612 * conversions. Where the candidate target charset is UTF-8 we avoid
613 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
614 * fails with some libraries.
616 * We assume that the output from iconv is never more than 4 times as
617 * long as the input for any pair of charsets we might be interested
620 static size_t convert_file_to (FILE *file, const char *fromcode,
621 int ncodes, const char **tocodes,
622 int *tocode, CONTENT *info)
626 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
627 ICONV_CONST char *ib, *ub;
629 size_t ibl, obl, ubl, ubl1, n, ret;
632 CONTENT_STATE *states;
635 cd1 = mutt_iconv_open ("utf-8", fromcode, 0);
636 if (cd1 == (iconv_t)(-1))
639 cd = safe_calloc (ncodes, sizeof (iconv_t));
640 score = safe_calloc (ncodes, sizeof (size_t));
641 states = safe_calloc (ncodes, sizeof (CONTENT_STATE));
642 infos = safe_calloc (ncodes, sizeof (CONTENT));
644 for (i = 0; i < ncodes; i++)
645 if (ascii_strcasecmp (tocodes[i], "utf-8"))
646 cd[i] = mutt_iconv_open (tocodes[i], "utf-8", 0);
648 /* Special case for conversion to UTF-8 */
649 cd[i] = (iconv_t)(-1), score[i] = (size_t)(-1);
656 /* Try to fill input buffer */
657 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
660 /* Convert to UTF-8 */
662 ob = bufu, obl = sizeof (bufu);
663 n = iconv (cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
664 assert (n == (size_t)(-1) || !n || ICONV_NONTRANS);
665 if (n == (size_t)(-1) &&
666 ((errno != EINVAL && errno != E2BIG) || ib == bufi))
668 assert (errno == EILSEQ ||
669 (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
675 /* Convert from UTF-8 */
676 for (i = 0; i < ncodes; i++)
677 if (cd[i] != (iconv_t)(-1) && score[i] != (size_t)(-1))
679 ub = bufu, ubl = ubl1;
680 ob = bufo, obl = sizeof (bufo);
681 n = iconv (cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
682 if (n == (size_t)(-1))
684 assert (errno == E2BIG ||
685 (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
686 score[i] = (size_t)(-1);
691 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
694 else if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
695 /* Special case for conversion to UTF-8 */
696 update_content_info (&infos[i], &states[i], bufu, ubl1);
699 /* Save unused input */
700 memmove (bufi, ib, ibl);
701 else if (!ubl1 && ib < bufi + sizeof (bufi))
710 /* Find best score */
712 for (i = 0; i < ncodes; i++)
714 if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
716 /* Special case for conversion to UTF-8 */
721 else if (cd[i] == (iconv_t)(-1) || score[i] == (size_t)(-1))
723 else if (ret == (size_t)(-1) || score[i] < ret)
731 if (ret != (size_t)(-1))
733 memcpy (info, &infos[*tocode], sizeof(CONTENT));
734 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
738 for (i = 0; i < ncodes; i++)
739 if (cd[i] != (iconv_t)(-1))
751 #endif /* !HAVE_ICONV */
755 * Find the first of the fromcodes that gives a valid conversion and
756 * the best charset conversion of the file into one of the tocodes. If
757 * successful, set *fromcode and *tocode to dynamically allocated
758 * strings, set CONTENT *info, and return the number of characters
759 * converted inexactly. If no conversion was possible, return -1.
761 * Both fromcodes and tocodes may be colon-separated lists of charsets.
762 * However, if fromcode is zero then fromcodes is assumed to be the
763 * name of a single charset even if it contains a colon.
765 static size_t convert_file_from_to (FILE *file,
766 const char *fromcodes, const char *tocodes,
767 char **fromcode, char **tocode, CONTENT *info)
775 /* Count the tocodes */
777 for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
779 if ((c1 = strchr (c, ':')) == c)
785 tcode = safe_malloc (ncodes * sizeof (char *));
786 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
788 if ((c1 = strchr (c, ':')) == c)
790 tcode[i] = mutt_substrdup (c, c1);
796 /* Try each fromcode in turn */
797 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
799 if ((c1 = strchr (c, ':')) == c)
801 fcode = mutt_substrdup (c, c1);
803 ret = convert_file_to (file, fcode, ncodes, (const char **)tcode,
805 if (ret != (size_t)(-1))
817 /* There is only one fromcode */
818 ret = convert_file_to (file, fromcodes, ncodes, (const char **)tcode,
820 if (ret != (size_t)(-1))
828 for (i = 0; i < ncodes; i++)
831 FREE (tcode); /* __FREE_CHECKED__ */
837 * Analyze the contents of a file to determine which MIME encoding to use.
838 * Also set the body charset, sometimes, or not.
840 CONTENT *mutt_get_content_info (const char *fname, BODY *b)
845 char *fromcode = NULL;
853 if(b && !fname) fname = b->filename;
855 if (stat (fname, &sb) == -1)
857 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
861 if (!S_ISREG(sb.st_mode))
863 mutt_error (_("%s isn't a regular file."), fname);
867 if ((fp = fopen (fname, "r")) == NULL)
869 dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
870 fname, strerror (errno), errno));
874 info = safe_calloc (1, sizeof (CONTENT));
875 memset (&state, 0, sizeof (state));
877 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
879 char *chs = mutt_get_parameter ("charset", b->parameter);
880 char *fchs = b->use_disp ? ((AttachCharset && *AttachCharset) ?
881 AttachCharset : Charset) : Charset;
882 if (Charset && (chs || SendCharset) &&
883 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
884 &fromcode, &tocode, info) != (size_t)(-1))
888 mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
889 mutt_set_parameter ("charset", chsbuf, &b->parameter);
891 b->charset = fromcode;
899 while ((r = fread (buffer, 1, sizeof(buffer), fp)))
900 update_content_info (info, &state, buffer, r);
901 update_content_info (info, &state, 0, 0);
905 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
906 mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
907 Charset && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
913 /* Given a file with path ``s'', see if there is a registered MIME type.
914 * returns the major MIME type, and copies the subtype to ``d''. First look
915 * for ~/.mime.types, then look in a system mime.types if we can find one.
916 * The longest match is used so that we can match `ps.gz' when `gz' also
920 int mutt_lookup_mime_type (BODY *att, const char *path)
924 char buf[LONG_STRING];
925 char subtype[STRING], xtype[STRING];
927 int szf, sze, cur_sze;
935 szf = mutt_strlen (path);
937 for (count = 0 ; count < 3 ; count++)
940 * can't use strtok() because we use it in an inner loop below, so use
941 * a switch statement here instead.
946 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
949 strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
952 strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
955 dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
956 goto bye; /* shouldn't happen */
959 if ((f = fopen (buf, "r")) != NULL)
961 while (fgets (buf, sizeof (buf) - 1, f) != NULL)
963 /* weed out any comments */
964 if ((p = strchr (buf, '#')))
967 /* remove any leading space. */
971 /* position on the next field in this line */
972 if ((p = strpbrk (ct, " \t")) == NULL)
977 /* cycle through the file extensions */
978 while ((p = strtok (p, " \t\n")))
980 sze = mutt_strlen (p);
981 if ((sze > cur_sze) && (szf >= sze) &&
982 (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
983 (szf == sze || path[szf - sze - 1] == '.'))
985 /* get the content-type */
987 if ((p = strchr (ct, '/')) == NULL)
989 /* malformed line, just skip it. */
994 for (q = p; *q && !ISSPACE (*q); q++)
997 mutt_substrcpy (subtype, p, q, sizeof (subtype));
999 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1000 strfcpy (xtype, ct, sizeof (xtype));
1013 if (type != TYPEOTHER || *xtype != '\0')
1016 mutt_str_replace (&att->subtype, subtype);
1017 mutt_str_replace (&att->xtype, xtype);
1023 void mutt_message_to_7bit (BODY *a, FILE *fp)
1025 char temp[_POSIX_PATH_MAX];
1031 if (!a->filename && fp)
1033 else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1035 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1041 if (stat (a->filename, &sb) == -1)
1043 mutt_perror ("stat");
1046 a->length = sb.st_size;
1050 if (!(fpout = safe_fopen (temp, "w+")))
1052 mutt_perror ("fopen");
1056 fseeko (fpin, a->offset, 0);
1057 a->parts = mutt_parse_messageRFC822 (fpin, a);
1059 transform_to_7bit (a->parts, fpin);
1061 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1062 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1064 fputs ("MIME-Version: 1.0\n", fpout);
1065 mutt_write_mime_header (a->parts, fpout);
1066 fputc ('\n', fpout);
1067 mutt_write_mime_body (a->parts, fpout);
1079 a->encoding = ENC7BIT;
1080 a->d_filename = a->filename;
1081 if (a->filename && a->unlink)
1082 unlink (a->filename);
1083 a->filename = safe_strdup (temp);
1085 if(stat (a->filename, &sb) == -1)
1087 mutt_perror ("stat");
1090 a->length = sb.st_size;
1091 mutt_free_body (&a->parts);
1092 a->hdr->content = NULL;
1095 static void transform_to_7bit (BODY *a, FILE *fpin)
1097 char buff[_POSIX_PATH_MAX];
1101 memset (&s, 0, sizeof (s));
1102 for (; a; a = a->next)
1104 if (a->type == TYPEMULTIPART)
1106 if (a->encoding != ENC7BIT)
1107 a->encoding = ENC7BIT;
1109 transform_to_7bit (a->parts, fpin);
1111 else if (mutt_is_message_type(a->type, a->subtype))
1113 mutt_message_to_7bit (a, fpin);
1118 a->force_charset = 1;
1121 if ((s.fpout = safe_fopen (buff, "w")) == NULL)
1123 mutt_perror ("fopen");
1127 mutt_decode_attachment (a, &s);
1129 a->d_filename = a->filename;
1130 a->filename = safe_strdup (buff);
1132 if (stat (a->filename, &sb) == -1)
1134 mutt_perror ("stat");
1137 a->length = sb.st_size;
1139 mutt_update_encoding (a);
1140 if (a->encoding == ENC8BIT)
1141 a->encoding = ENCQUOTEDPRINTABLE;
1142 else if(a->encoding == ENCBINARY)
1143 a->encoding = ENCBASE64;
1148 /* determine which Content-Transfer-Encoding to use */
1149 static void mutt_set_encoding (BODY *b, CONTENT *info)
1151 char send_charset[SHORT_STRING];
1153 if (b->type == TYPETEXT)
1155 char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1156 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1157 b->encoding = ENCQUOTEDPRINTABLE;
1158 else if (info->hibin)
1159 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1161 b->encoding = ENC7BIT;
1163 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1165 if (info->lobin || info->hibin)
1167 if (option (OPTALLOW8BIT) && !info->lobin)
1168 b->encoding = ENC8BIT;
1170 mutt_message_to_7bit (b, NULL);
1173 b->encoding = ENC7BIT;
1175 else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1176 b->encoding = ENC7BIT;
1179 if (info->lobin || info->hibin || info->binary || info->linemax > 990
1180 || info->cr || (/* option (OPTENCODEFROM) && */ info->from))
1183 /* Determine which encoding is smaller */
1184 if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
1185 3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1186 b->encoding = ENCBASE64;
1188 b->encoding = ENCQUOTEDPRINTABLE;
1192 b->encoding = ENC7BIT;
1196 void mutt_stamp_attachment(BODY *a)
1198 a->stamp = time(NULL);
1201 /* Get a body's character set */
1203 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1207 if (b && b->type != TYPETEXT)
1211 p = mutt_get_parameter ("charset", b->parameter);
1214 mutt_canonical_charset (d, dlen, NONULL(p));
1216 strfcpy (d, "us-ascii", dlen);
1222 /* Assumes called from send mode where BODY->filename points to actual file */
1223 void mutt_update_encoding (BODY *a)
1226 char chsbuff[STRING];
1228 /* override noconv when it's us-ascii */
1229 if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1232 if (!a->force_charset && !a->noconv)
1233 mutt_delete_parameter ("charset", &a->parameter);
1235 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1238 mutt_set_encoding (a, info);
1239 mutt_stamp_attachment(a);
1246 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1248 char buffer[LONG_STRING];
1251 int cmflags, chflags;
1252 int pgp = WithCrypto? hdr->security : 0;
1256 if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1257 (hdr->security & ENCRYPT)) {
1258 if (!crypt_valid_passphrase(hdr->security))
1263 mutt_mktemp (buffer);
1264 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1267 body = mutt_new_body ();
1268 body->type = TYPEMESSAGE;
1269 body->subtype = safe_strdup ("rfc822");
1270 body->filename = safe_strdup (buffer);
1273 body->disposition = DISPINLINE;
1276 mutt_parse_mime_message (ctx, hdr);
1281 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1282 if (!attach_msg && option (OPTMIMEFORWDECODE))
1284 chflags |= CH_MIME | CH_TXTPLAIN;
1285 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1286 if ((WithCrypto & APPLICATION_PGP))
1288 if ((WithCrypto & APPLICATION_SMIME))
1289 pgp &= ~SMIMEENCRYPT;
1292 && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1294 if ((WithCrypto & APPLICATION_PGP)
1295 && mutt_is_multipart_encrypted (hdr->content))
1297 chflags |= CH_MIME | CH_NONEWLINE;
1298 cmflags = M_CM_DECODE_PGP;
1301 else if ((WithCrypto & APPLICATION_PGP)
1302 && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT))
1304 chflags |= CH_MIME | CH_TXTPLAIN;
1305 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1308 else if ((WithCrypto & APPLICATION_SMIME)
1309 && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT)
1311 chflags |= CH_MIME | CH_TXTPLAIN;
1312 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1313 pgp &= ~SMIMEENCRYPT;
1317 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1322 body->hdr = mutt_new_header();
1323 body->hdr->offset = 0;
1324 /* we don't need the user headers here */
1325 body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1327 body->hdr->security = pgp;
1328 mutt_update_encoding (body);
1329 body->parts = body->hdr->content;
1336 BODY *mutt_make_file_attach (const char *path)
1341 att = mutt_new_body ();
1342 att->filename = safe_strdup (path);
1344 /* Attempt to determine the appropriate content-type based on the filename
1350 if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER
1354 att->subtype = safe_strdup (buf);
1355 att->xtype = safe_strdup (xbuf);
1360 mutt_lookup_mime_type (att, path);
1364 if ((info = mutt_get_content_info (path, att)) == NULL)
1366 mutt_free_body (&att);
1372 if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
1375 * Statistically speaking, there should be more than 10% "lobin"
1376 * chars if this is really a binary file...
1378 att->type = TYPETEXT;
1379 att->subtype = safe_strdup ("plain");
1383 att->type = TYPEAPPLICATION;
1384 att->subtype = safe_strdup ("octet-stream");
1388 mutt_update_encoding (att);
1392 static int get_toplevel_encoding (BODY *a)
1396 for (; a; a = a->next)
1398 if (a->encoding == ENCBINARY)
1400 else if (a->encoding == ENC8BIT)
1407 /* check for duplicate boundary. return 1 if duplicate */
1408 static int mutt_check_boundary (const char* boundary, BODY *b)
1412 if (b->parts && mutt_check_boundary (boundary, b->parts))
1415 if (b->next && mutt_check_boundary (boundary, b->next))
1418 if ((p = mutt_get_parameter ("boundary", b->parameter))
1419 && !ascii_strcmp (p, boundary))
1424 BODY *mutt_make_multipart (BODY *b)
1428 new = mutt_new_body ();
1429 new->type = TYPEMULTIPART;
1430 new->subtype = safe_strdup ("mixed");
1431 new->encoding = get_toplevel_encoding (b);
1434 mutt_generate_boundary (&new->parameter);
1435 if (mutt_check_boundary (mutt_get_parameter ("boundary", new->parameter),
1437 mutt_delete_parameter ("boundary", &new->parameter);
1439 while (!mutt_get_parameter ("boundary", new->parameter));
1441 new->disposition = DISPINLINE;
1447 /* remove the multipart body if it exists */
1448 BODY *mutt_remove_multipart (BODY *b)
1457 mutt_free_body (&t);
1462 char *mutt_make_date (char *s, size_t len)
1464 time_t t = time (NULL);
1465 struct tm *l = localtime (&t);
1466 time_t tz = mutt_local_tz (t);
1470 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1471 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1472 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1473 (int) tz / 60, (int) abs (tz) % 60);
1477 /* wrapper around mutt_write_address() so we can handle very large
1478 recipient lists without needing a huge temporary buffer in memory */
1479 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1482 char buf[LONG_STRING];
1491 rfc822_write_address (buf, sizeof (buf), adr, display);
1492 len = mutt_strlen (buf);
1493 if (count && linelen + len > 74)
1496 linelen = len + 8; /* tab is usually about 8 spaces... */
1500 if (count && adr->mailbox)
1509 if (!adr->group && adr->next && adr->next->mailbox)
1520 /* arbitrary number of elements to grow the array by */
1523 /* need to write the list in reverse because they are stored in reverse order
1524 * when parsed to speed up threading
1526 void mutt_write_references (LIST *r, FILE *f, int trim)
1529 int refcnt = 0, refmax = 0;
1531 for ( ; (trim == 0 || refcnt < trim) && r ; r = r->next)
1533 if (refcnt == refmax)
1534 safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1538 while (refcnt-- > 0)
1541 fputs (ref[refcnt]->data, f);
1548 static void foldingstrfcpy (char *d, const char *s, int n)
1550 while (--n >= 0 && *s)
1555 if (!(d[0] == '\n' && (*s == '\t' || *s == ' ')))
1561 int mutt_write_one_header (FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen)
1566 char buf [HUGE_STRING];
1567 wchar_t w = (wchar_t) -1;
1568 wchar_t last = (wchar_t) '\n';
1572 int in_encoded_word = 0;
1579 if (fprintf (fp, "%s%s: ", NONULL (pfx), tag) < 0)
1582 col = mutt_strlen (tag) + 2 + mutt_strlen (pfx);
1594 if (fputs (NONULL (pfx), fp) == EOF)
1596 col = mutt_strlen (pfx);
1598 /* Space padding, but only if necessary */
1599 if (!first && *cp != '\t' && *cp != ' ')
1601 if (fputc ('\t', fp) == EOF)
1603 col += 8 - (col % 8);
1615 * i is our running pointer, and always points to the *beginning* of an mb character.
1616 * k is the pointer to the beginning of the last white-space character we have seen.
1617 * n is the pointer to the beginning of the first character after white-space.
1622 for (i = 0, k = 0, l = 0, n = 0; i + MB_CUR_MAX < sizeof (buf)
1623 && cp[i] != '\0' && (col < wraplen || in_encoded_word);
1627 /* Brief look at the last character we had... */
1628 if (iswspace (last))
1630 /* ... and if the next thing is an encoded word ... */
1631 if (strncmp (&cp[i], "=?", 2) == 0)
1632 in_encoded_word = 1;
1634 in_encoded_word = 0;
1637 /* If there is a line break in the header, honor it. */
1640 in_encoded_word = 0;
1642 if (cp[i+1] != ' ' && cp[i+1] != '\t')
1645 if (first || !wrapped)
1655 /* Eat the current character; cannot be '\0' */
1657 if ((l = mbtowc (&w, &cp[i], MB_CUR_MAX)) <= 0)
1659 dprint (1, (debugfile, "mutt_write_one_header: encoutered bad multi-byte character at %d.\n", i));
1660 l = 1; /* if bad, move on by one character */
1665 if (wcwidth (w) >= 0)
1669 (!k || col <= wraplen))
1678 * As long as we haven't seen whitespace, we advance at least by one character.
1684 /* If no whitespace was found, copy as much as we can */
1688 /* If we're done, we're done. */
1692 if (k < i) /* we had to go back to an earlier wrapping point */
1696 foldingstrfcpy (buf + 1, cp + 1, k - 1);
1698 if (fprintf (fp, "%s\n", buf) < 0)
1707 if ((l = mbtowc (&w, cp, MB_CUR_MAX)) > 0 && iswspace (w))
1716 if (fputc ('\n', fp) == EOF)
1725 /* Note: all RFC2047 encoding should be done outside of this routine, except
1726 * for the "real name." This will allow this routine to be used more than
1727 * once, if necessary.
1729 * Likewise, all IDN processing should happen outside of this routine.
1731 * mode == 1 => "lite" mode (used for edit_hdrs)
1732 * mode == 0 => normal mode. write full header + MIME headers
1733 * mode == -1 => write just the envelope info (used for postponing messages)
1735 * privacy != 0 => will omit any headers which may identify the user.
1736 * Output generated is suitable for being sent through
1737 * anonymous remailer chains.
1743 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
1744 int mode, int privacy)
1746 char buffer[LONG_STRING];
1748 LIST *tmp = env->userhdrs;
1749 int has_agent = 0; /* user defined user-agent header field exists */
1751 if (mode == 0 && !privacy)
1752 fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
1754 /* OPTUSEFROM is not consulted here so that we can still write a From:
1755 * field if the user sets it with the `my_hdr' command
1757 if (env->from && !privacy)
1760 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1761 fprintf (fp, "From: %s\n", buffer);
1764 if (env->sender && !privacy)
1767 rfc822_write_address (buffer, sizeof (buffer), env->sender, 0);
1768 fprintf (fp, "Sender: %s\n", buffer);
1774 mutt_write_address_list (env->to, fp, 4, 0);
1777 fputs ("To: \n", fp);
1782 mutt_write_address_list (env->cc, fp, 4, 0);
1785 fputs ("Cc: \n", fp);
1789 if(mode != 0 || option(OPTWRITEBCC))
1791 fputs ("Bcc: ", fp);
1792 mutt_write_address_list (env->bcc, fp, 5, 0);
1796 fputs ("Bcc: \n", fp);
1799 mutt_write_one_header (fp, "Subject", env->subject, NULL, 0);
1801 fputs ("Subject: \n", fp);
1803 /* save message id if the user has set it */
1804 if (env->message_id && !privacy)
1805 fprintf (fp, "Message-ID: %s\n", env->message_id);
1809 fputs ("Reply-To: ", fp);
1810 mutt_write_address_list (env->reply_to, fp, 10, 0);
1813 fputs ("Reply-To: \n", fp);
1815 if (env->mail_followup_to)
1817 fputs ("Mail-Followup-To: ", fp);
1818 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1823 if (env->references)
1825 fputs ("References:", fp);
1826 mutt_write_references (env->references, fp, 10);
1830 /* Add the MIME headers */
1831 fputs ("MIME-Version: 1.0\n", fp);
1832 mutt_write_mime_header (attach, fp);
1835 if (env->in_reply_to)
1837 fputs ("In-Reply-To:", fp);
1838 mutt_write_references (env->in_reply_to, fp, 1);
1842 /* Add any user defined headers */
1843 for (; tmp; tmp = tmp->next)
1845 if ((p = strchr (tmp->data, ':')))
1855 continue; /* don't emit empty fields. */
1858 /* check to see if the user has overridden the user-agent field */
1859 if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
1869 mutt_write_one_header (fp, tmp->data, p, NULL, 0);
1874 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent)
1876 /* Add a vanity header */
1877 fprintf (fp, "User-Agent: Mutt/%s (%s)\n", MUTT_VERSION, ReleaseDate);
1880 return (ferror (fp) == 0 ? 0 : -1);
1883 static void encode_headers (LIST *h)
1889 for (; h; h = h->next)
1891 if (!(p = strchr (h->data, ':')))
1896 tmp = safe_strdup (p);
1901 rfc2047_encode_string (&tmp);
1902 safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
1904 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
1910 const char *mutt_fqdn(short may_hide_host)
1914 if(Fqdn && Fqdn[0] != '@')
1918 if(may_hide_host && option(OPTHIDDENHOST))
1920 if((p = strchr(Fqdn, '.')))
1923 /* sanity check: don't hide the host if
1924 * the fqdn is something like detebe.org.
1927 if(!p || !(q = strchr(p, '.')))
1935 char *mutt_gen_msgid (void)
1937 char buf[SHORT_STRING];
1944 if(!(fqdn = mutt_fqdn(0)))
1945 fqdn = NONULL(Hostname);
1947 snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.G%c%u@%s>",
1948 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
1949 tm->tm_min, tm->tm_sec, MsgIdPfx, (unsigned int)getpid (), fqdn);
1950 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1951 return (safe_strdup (buf));
1954 static RETSIGTYPE alarm_handler (int sig)
1959 /* invoke sendmail in a subshell
1960 path (in) path to program to execute
1961 args (in) arguments to pass to program
1962 msg (in) temp file containing message to send
1963 tempfile (out) if sendmail is put in the background, this points
1964 to the temporary file containing the stdout of the
1965 child process. If it is NULL, stderr and stdout
1966 are not redirected. */
1968 send_msg (const char *path, char **args, const char *msg, char **tempfile)
1974 mutt_block_signals_system ();
1977 /* we also don't want to be stopped right now */
1978 sigaddset (&set, SIGTSTP);
1979 sigprocmask (SIG_BLOCK, &set, NULL);
1981 if (SendmailWait >= 0 && tempfile)
1983 char tmp[_POSIX_PATH_MAX];
1986 *tempfile = safe_strdup (tmp);
1989 if ((pid = fork ()) == 0)
1991 struct sigaction act, oldalrm;
1993 /* save parent's ID before setsid() */
1996 /* we want the delivery to continue even after the main process dies,
1997 * so we put ourselves into another session right away
2001 /* next we close all open files */
2003 #if defined(OPEN_MAX)
2004 for (fd = tempfile ? 1 : 3; fd < OPEN_MAX; fd++)
2006 #elif defined(_POSIX_OPEN_MAX)
2007 for (fd = tempfile ? 1 : 3; fd < _POSIX_OPEN_MAX; fd++)
2017 /* now the second fork() */
2018 if ((pid = fork ()) == 0)
2020 /* "msg" will be opened as stdin */
2021 if (open (msg, O_RDONLY, 0) < 0)
2028 if (SendmailWait >= 0 && tempfile)
2030 /* *tempfile will be opened as stdout */
2031 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2033 /* redirect stderr to *tempfile too */
2039 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
2041 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
2045 execvp (path, args);
2052 FREE (tempfile); /* __FREE_CHECKED__ */
2056 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
2057 * SendmailWait = 0: wait forever
2058 * SendmailWait < 0: don't wait
2060 if (SendmailWait > 0)
2063 act.sa_handler = alarm_handler;
2065 /* need to make sure waitpid() is interrupted on SIGALRM */
2066 act.sa_flags = SA_INTERRUPT;
2070 sigemptyset (&act.sa_mask);
2071 sigaction (SIGALRM, &act, &oldalrm);
2072 alarm (SendmailWait);
2074 else if (SendmailWait < 0)
2075 _exit (0xff & EX_OK);
2077 if (waitpid (pid, &st, 0) > 0)
2079 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
2080 if (SendmailWait && st == (0xff & EX_OK) && tempfile)
2082 unlink (*tempfile); /* no longer needed */
2083 FREE (tempfile); /* __FREE_CHECKED__ */
2088 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
2090 if (SendmailWait > 0 && tempfile)
2093 FREE (tempfile); /* __FREE_CHECKED__ */
2097 /* reset alarm; not really needed, but... */
2099 sigaction (SIGALRM, &oldalrm, NULL);
2101 if (kill (ppid, 0) == -1 && errno == ESRCH && tempfile)
2103 /* the parent is already dead */
2105 FREE (tempfile); /* __FREE_CHECKED__ */
2111 sigprocmask (SIG_UNBLOCK, &set, NULL);
2113 if (pid != -1 && waitpid (pid, &st, 0) > 0)
2114 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
2116 st = S_ERR; /* error */
2118 mutt_unblock_signals_system (1);
2124 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
2126 for (; addr; addr = addr->next)
2128 /* weed out group mailboxes, since those are for display only */
2129 if (addr->mailbox && !addr->group)
2131 if (*argslen == *argsmax)
2132 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2133 args[(*argslen)++] = addr->mailbox;
2140 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2142 if (*argslen == *argsmax)
2143 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2144 args[(*argslen)++] = s;
2149 mutt_invoke_sendmail (ADDRESS *from, /* the sender */
2150 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2151 const char *msg, /* file containing message */
2152 int eightbit) /* message contains 8bit chars */
2154 char *ps = NULL, *path = NULL, *s = safe_strdup (Sendmail), *childout = NULL;
2156 size_t argslen = 0, argsmax = 0;
2161 while ((ps = strtok (ps, " ")))
2163 if (argslen == argsmax)
2164 safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2167 args[argslen++] = ps;
2170 path = safe_strdup (ps);
2171 ps = strrchr (ps, '/');
2176 args[argslen++] = ps;
2182 if (eightbit && option (OPTUSE8BITMIME))
2183 args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2185 if (option (OPTENVFROM))
2189 args = add_option (args, &argslen, &argsmax, "-f");
2190 args = add_args (args, &argslen, &argsmax, EnvFrom);
2192 else if (from && !from->next)
2194 args = add_option (args, &argslen, &argsmax, "-f");
2195 args = add_args (args, &argslen, &argsmax, from);
2201 args = add_option (args, &argslen, &argsmax, "-N");
2202 args = add_option (args, &argslen, &argsmax, DsnNotify);
2206 args = add_option (args, &argslen, &argsmax, "-R");
2207 args = add_option (args, &argslen, &argsmax, DsnReturn);
2209 args = add_option (args, &argslen, &argsmax, "--");
2210 args = add_args (args, &argslen, &argsmax, to);
2211 args = add_args (args, &argslen, &argsmax, cc);
2212 args = add_args (args, &argslen, &argsmax, bcc);
2214 if (argslen == argsmax)
2215 safe_realloc (&args, sizeof (char *) * (++argsmax));
2217 args[argslen++] = NULL;
2219 if ((i = send_msg (path, args, msg, option(OPTNOCURSES) ? NULL : &childout)) != (EX_OK & 0xff))
2223 const char *e = mutt_strsysexit (i);
2225 e = mutt_strsysexit (i);
2226 mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2231 if (stat (childout, &st) == 0 && st.st_size > 0)
2232 mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2244 if (i == (EX_OK & 0xff))
2246 else if (i == S_BKG)
2253 /* For postponing (!final) do the necessary encodings only */
2254 void mutt_prepare_envelope (ENVELOPE *env, int final)
2256 char buffer[LONG_STRING];
2260 if (env->bcc && !(env->to || env->cc))
2262 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2263 * recipients if there is no To: or Cc: field, so attempt to suppress
2264 * it by using an empty To: field.
2266 env->to = rfc822_new_address ();
2268 env->to->next = rfc822_new_address ();
2271 rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2274 env->to->mailbox = safe_strdup (buffer);
2277 mutt_set_followup_to (env);
2279 if (!env->message_id)
2280 env->message_id = mutt_gen_msgid ();
2283 /* Take care of 8-bit => 7-bit conversion. */
2284 rfc2047_encode_adrlist (env->to, "To");
2285 rfc2047_encode_adrlist (env->cc, "Cc");
2286 rfc2047_encode_adrlist (env->bcc, "Bcc");
2287 rfc2047_encode_adrlist (env->from, "From");
2288 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2289 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2290 rfc2047_encode_string (&env->x_label);
2294 rfc2047_encode_string (&env->subject);
2296 encode_headers (env->userhdrs);
2299 void mutt_unprepare_envelope (ENVELOPE *env)
2303 for (item = env->userhdrs; item; item = item->next)
2304 rfc2047_decode (&item->data);
2306 rfc822_free_address (&env->mail_followup_to);
2308 /* back conversions */
2309 rfc2047_decode_adrlist (env->to);
2310 rfc2047_decode_adrlist (env->cc);
2311 rfc2047_decode_adrlist (env->bcc);
2312 rfc2047_decode_adrlist (env->from);
2313 rfc2047_decode_adrlist (env->reply_to);
2314 rfc2047_decode (&env->subject);
2315 rfc2047_decode (&env->x_label);
2318 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2323 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2324 MESSAGE *msg = NULL;
2328 /* Try to bounce each message out, aborting if we get any failures. */
2329 for (i=0; i<Context->msgcount; i++)
2330 if (Context->hdrs[i]->tagged)
2331 ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2335 /* If we failed to open a message, return with error */
2336 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2339 if (!fp) fp = msg->fp;
2341 mutt_mktemp (tempfile);
2342 if ((f = safe_fopen (tempfile, "w")) != NULL)
2344 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2347 if (!option (OPTBOUNCEDELIVERED))
2348 ch_flags |= CH_WEED_DELIVERED;
2350 fseeko (fp, h->offset, 0);
2351 fprintf (f, "Resent-From: %s", resent_from);
2352 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2353 msgid_str = mutt_gen_msgid();
2354 fprintf (f, "Resent-Message-ID: %s\n", msgid_str);
2355 fputs ("Resent-To: ", f);
2356 mutt_write_address_list (to, f, 11, 0);
2357 mutt_copy_header (fp, h, f, ch_flags, NULL);
2359 mutt_copy_bytes (fp, f, h->content->length);
2365 ret = mutt_smtp_send (env_from, to, NULL, NULL, tempfile,
2366 h->content->encoding == ENC8BIT);
2368 #endif /* USE_SMTP */
2369 ret = mutt_invoke_sendmail (env_from, to, NULL, NULL, tempfile,
2370 h->content->encoding == ENC8BIT);
2374 mx_close_message (&msg);
2379 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2382 const char *fqdn = mutt_fqdn (1);
2383 char resent_from[STRING];
2387 resent_from[0] = '\0';
2388 from = mutt_default_from ();
2391 rfc822_qualify (from, fqdn);
2393 rfc2047_encode_adrlist (from, "Resent-From");
2394 if (mutt_addrlist_to_idna (from, &err))
2396 mutt_error (_("Bad IDN %s while preparing resent-from."),
2400 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2402 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2404 rfc822_free_address (&from);
2410 /* given a list of addresses, return a list of unique addresses */
2411 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2413 ADDRESS *top = addr;
2414 ADDRESS **last = ⊤
2420 for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next)
2422 if (tmp->mailbox && addr->mailbox &&
2423 !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2432 dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
2438 rfc822_free_address(&addr);
2452 static void set_noconv_flags (BODY *b, short flag)
2454 for(; b; b = b->next)
2456 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2457 set_noconv_flags (b->parts, flag);
2458 else if (b->type == TYPETEXT && b->noconv)
2461 mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2463 mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2468 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc)
2472 char tempfile[_POSIX_PATH_MAX];
2473 FILE *tempfp = NULL;
2477 set_noconv_flags (hdr->content, 1);
2479 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
2481 dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2486 /* We need to add a Content-Length field to avoid problems where a line in
2487 * the message body begins with "From "
2489 if (f.magic == M_MMDF || f.magic == M_MBOX)
2491 mutt_mktemp (tempfile);
2492 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2494 mutt_perror (tempfile);
2495 mx_close_mailbox (&f, NULL);
2500 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2501 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
2503 mx_close_mailbox (&f, NULL);
2507 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2508 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2510 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 0);
2512 /* (postponment) if this was a reply of some sort, <msgid> contians the
2513 * Message-ID: of message replied to. Save it using a special X-Mutt-
2514 * header so it can be picked up if the message is recalled at a later
2515 * point in time. This will allow the message to be marked as replied if
2516 * the same mailbox is still open.
2519 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2521 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2522 * it can be picked up when the message is recalled
2525 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2526 fprintf (msg->fp, "Status: RO\n");
2530 /* (postponment) if the mail is to be signed or encrypted, save this info */
2531 if ((WithCrypto & APPLICATION_PGP)
2532 && post && (hdr->security & APPLICATION_PGP))
2534 fputs ("X-Mutt-PGP: ", msg->fp);
2535 if (hdr->security & ENCRYPT)
2536 fputc ('E', msg->fp);
2537 if (hdr->security & SIGN)
2539 fputc ('S', msg->fp);
2540 if (PgpSignAs && *PgpSignAs)
2541 fprintf (msg->fp, "<%s>", PgpSignAs);
2543 if (hdr->security & INLINE)
2544 fputc ('I', msg->fp);
2545 fputc ('\n', msg->fp);
2548 /* (postponment) if the mail is to be signed or encrypted, save this info */
2549 if ((WithCrypto & APPLICATION_SMIME)
2550 && post && (hdr->security & APPLICATION_SMIME))
2552 fputs ("X-Mutt-SMIME: ", msg->fp);
2553 if (hdr->security & ENCRYPT) {
2554 fputc ('E', msg->fp);
2555 if (SmimeCryptAlg && *SmimeCryptAlg)
2556 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2558 if (hdr->security & SIGN) {
2559 fputc ('S', msg->fp);
2560 if (SmimeDefaultKey && *SmimeDefaultKey)
2561 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2563 if (hdr->security & INLINE)
2564 fputc ('I', msg->fp);
2565 fputc ('\n', msg->fp);
2569 /* (postponement) if the mail is to be sent through a mixmaster
2570 * chain, save that information
2573 if (post && hdr->chain && hdr->chain)
2577 fputs ("X-Mutt-Mix:", msg->fp);
2578 for (p = hdr->chain; p; p = p->next)
2579 fprintf (msg->fp, " %s", (char *) p->data);
2581 fputc ('\n', msg->fp);
2587 char sasha[LONG_STRING];
2590 mutt_write_mime_body (hdr->content, tempfp);
2592 /* make sure the last line ends with a newline. Emacs doesn't ensure
2593 * this will happen, and it can cause problems parsing the mailbox
2596 fseek (tempfp, -1, 2);
2597 if (fgetc (tempfp) != '\n')
2599 fseek (tempfp, 0, 2);
2600 fputc ('\n', tempfp);
2604 if (ferror (tempfp))
2606 dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2609 mx_commit_message (msg, &f); /* XXX - really? */
2610 mx_close_message (&msg);
2611 mx_close_mailbox (&f, NULL);
2615 /* count the number of lines */
2617 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2619 fprintf (msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftello (tempfp));
2620 fprintf (msg->fp, "Lines: %d\n\n", lines);
2622 /* copy the body and clean up */
2624 r = mutt_copy_stream (tempfp, msg->fp);
2625 if (fclose (tempfp) != 0)
2627 /* if there was an error, leave the temp version */
2633 fputc ('\n', msg->fp); /* finish off the header */
2634 r = mutt_write_mime_body (hdr->content, msg->fp);
2637 if (mx_commit_message (msg, &f) != 0)
2639 mx_close_message (&msg);
2640 mx_close_mailbox (&f, NULL);
2643 set_noconv_flags (hdr->content, 0);