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.
22 #include "mutt_curses.h"
31 #include "mutt_crypt.h"
32 #include "mutt_idna.h"
43 #include <sys/utsname.h>
49 #ifdef HAVE_SYSEXITS_H
51 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
55 /* If you are debugging this file, comment out the following line. */
64 extern char RFC822Specials[];
66 static struct sysexits
74 { 0xff & EX_USAGE, "Bad usage." },
77 { 0xff & EX_DATAERR, "Data format error." },
80 { 0xff & EX_NOINPUT, "Cannot open input." },
83 { 0xff & EX_NOUSER, "User unknown." },
86 { 0xff & EX_NOHOST, "Host unknown." },
89 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
92 { 0xff & EX_SOFTWARE, "Internal error." },
95 { 0xff & EX_OSERR, "Operating system error." },
98 { 0xff & EX_OSFILE, "System file missing." },
101 { 0xff & EX_CANTCREAT, "Can't create output." },
104 { 0xff & EX_IOERR, "I/O error." },
107 { 0xff & EX_TEMPFAIL, "Deferred." },
110 { 0xff & EX_PROTOCOL, "Remote protocol error." },
113 { 0xff & EX_NOPERM, "Insufficient permission." },
116 { 0xff & EX_NOPERM, "Local configuration error." },
118 { S_ERR, "Exec error." },
124 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
126 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
128 char B64Chars[64] = {
129 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
130 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
131 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
132 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
136 static char MsgIdPfx = 'A';
138 static void transform_to_7bit (BODY *a, FILE *fpin);
140 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
143 char line[77], savechar;
145 while ((c = fgetconv (fc)) != EOF)
147 /* Wrap the line if needed. */
148 if (linelen == 76 && ((istext && c != '\n') || !istext))
150 /* If the last character is "quoted", then be sure to move all three
151 * characters to the next line. Otherwise, just move the last
154 if (line[linelen-3] == '=')
161 line[1] = line[linelen-2];
162 line[2] = line[linelen-1];
167 savechar = line[linelen-1];
168 line[linelen-1] = '=';
177 /* Escape lines that begin with/only contain "the message separator". */
178 if (linelen == 4 && !mutt_strncmp ("From", line, 4))
180 strfcpy (line, "=46rom", sizeof (line));
183 else if (linelen == 4 && !mutt_strncmp ("from", line, 4))
185 strfcpy (line, "=66rom", sizeof (line));
188 else if (linelen == 1 && line[0] == '.')
190 strfcpy (line, "=2E", sizeof (line));
195 # include "mutt_libesmtp.h"
196 #endif /* USE_LIBESMTP */
198 if (c == '\n' && istext)
200 /* Check to make sure there is no trailing space on this line. */
201 if (linelen > 0 && (line[linelen-1] == ' ' || line[linelen-1] == '\t'))
205 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
210 int savechar = line[linelen-1];
212 line[linelen-1] = '=';
215 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
226 else if (c != 9 && (c < 32 || c > 126 || c == '='))
228 /* Check to make sure there is enough room for the quoted character.
229 * If not, wrap to the next line.
233 line[linelen++] = '=';
239 sprintf (line+linelen,"=%2.2X", (unsigned char) c);
244 /* Don't worry about wrapping the line here. That will happen during
245 * the next iteration when I'll also know what the next character is.
251 /* Take care of anything left in the buffer */
254 if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
256 /* take care of trailing whitespace */
258 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
261 savechar = line[linelen-1];
262 line[linelen-1] = '=';
266 sprintf (line, "=%2.2X", (unsigned char) savechar);
275 static char b64_buffer[3];
276 static short b64_num;
277 static short b64_linelen;
279 static void b64_flush(FILE *fout)
286 if(b64_linelen >= 72)
292 for(i = b64_num; i < 3; i++)
293 b64_buffer[i] = '\0';
295 fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
297 fputc(B64Chars[((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf) ], fout);
302 fputc(B64Chars[((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3) ], fout);
306 fputc(B64Chars[b64_buffer[2] & 0x3f], fout);
311 while(b64_linelen % 4)
321 static void b64_putc(char c, FILE *fout)
326 b64_buffer[b64_num++] = c;
330 static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
334 b64_num = b64_linelen = 0;
336 while ((ch = fgetconv (fc)) != EOF)
338 if (istext && ch == '\n' && ch1 != '\r')
339 b64_putc('\r', fout);
347 static void encode_8bit (FGETCONV *fc, FILE *fout, int istext)
351 while ((ch = fgetconv (fc)) != EOF)
356 int mutt_write_mime_header (BODY *a, FILE *f)
366 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)
382 tmp = safe_strdup (p->value);
383 encode = rfc2231_encode_string (&tmp);
384 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
386 /* Dirty hack to make messages readable by Outlook Express
387 * for the Mac: force quotes around the boundary parameter
388 * even when they aren't needed.
391 if (!ascii_strcasecmp (p->attribute, "boundary") && !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)
409 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
417 fprintf(f, "Content-Description: %s\n", a->description);
419 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
423 if(!(fn = a->d_filename))
430 /* Strip off the leading path... */
431 if ((t = strrchr (fn, '/')))
437 tmp = safe_strdup (t);
438 encode = rfc2231_encode_string (&tmp);
439 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
441 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
447 if (a->encoding != ENC7BIT)
448 fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
450 /* Do NOT add the terminator here!!! */
451 return (ferror (f) ? -1 : 0);
454 # define write_as_text_part(a) (mutt_is_text_part(a) \
455 || ((WithCrypto & APPLICATION_PGP)\
456 && mutt_is_application_pgp(a)))
458 int mutt_write_mime_body (BODY *a, FILE *f)
460 char *p, boundary[SHORT_STRING];
461 char send_charset[SHORT_STRING];
466 if (a->type == TYPEMULTIPART)
468 /* First, find the boundary to use */
469 if (!(p = mutt_get_parameter ("boundary", a->parameter)))
471 dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
472 mutt_error _("No boundary parameter found! [report this error]");
475 strfcpy (boundary, p, sizeof (boundary));
477 for (t = a->parts; t ; t = t->next)
479 fprintf (f, "\n--%s\n", boundary);
480 if (mutt_write_mime_header (t, f) == -1)
483 if (mutt_write_mime_body (t, f) == -1)
486 fprintf (f, "\n--%s--\n", boundary);
487 return (ferror (f) ? -1 : 0);
490 /* This is pretty gross, but it's the best solution for now... */
491 if ((WithCrypto & APPLICATION_PGP)
492 && a->type == TYPEAPPLICATION
493 && mutt_strcmp (a->subtype, "pgp-encrypted") == 0)
495 fputs ("Version: 1\n", f);
499 if ((fpin = fopen (a->filename, "r")) == NULL)
501 dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
502 mutt_error (_("%s no longer exists!"), a->filename);
506 if (a->type == TYPETEXT && (!a->noconv))
507 fc = fgetconv_open (fpin, Charset,
508 mutt_get_body_charset (send_charset, sizeof (send_charset), a),
511 fc = fgetconv_open (fpin, 0, 0, 0);
513 if (a->encoding == ENCQUOTEDPRINTABLE)
514 encode_quoted (fc, f, write_as_text_part (a));
515 else if (a->encoding == ENCBASE64)
516 encode_base64 (fc, f, write_as_text_part (a));
517 else if (a->type == TYPETEXT && (!a->noconv))
518 encode_8bit (fc, f, write_as_text_part (a));
520 mutt_copy_stream (fpin, f);
522 fgetconv_close (&fc);
525 return (ferror (f) ? -1 : 0);
528 #undef write_as_text_part
530 #define BOUNDARYLEN 16
531 void mutt_generate_boundary (PARAMETER **parm)
533 char rs[BOUNDARYLEN + 1];
538 for (i=0;i<BOUNDARYLEN;i++)
539 *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
542 mutt_set_parameter ("boundary", rs, parm);
556 static void update_content_info (CONTENT *info, CONTENT_STATE *s, char *d, size_t dlen)
559 int whitespace = s->whitespace;
561 int linelen = s->linelen;
562 int was_cr = s->was_cr;
564 if (!d) /* This signals EOF */
568 if (linelen > info->linemax)
569 info->linemax = linelen;
574 for (; dlen; d++, dlen--)
587 if (whitespace) info->space = 1;
588 if (dot) info->dot = 1;
589 if (linelen > info->linemax) info->linemax = linelen;
601 if (whitespace) info->space = 1;
602 if (dot) info->dot = 1;
603 if (linelen > info->linemax) info->linemax = linelen;
617 else if (ch == '\t' || ch == '\f')
622 else if (ch < 32 || ch == 127)
628 if ((ch == 'F') || (ch == 'f'))
639 if (linelen == 2 && ch != 'r') from = 0;
640 else if (linelen == 3 && ch != 'o') from = 0;
641 else if (linelen == 4)
643 if (ch == 'm') info->from = 1;
647 if (ch == ' ') whitespace++;
651 if (linelen > 1) dot = 0;
652 if (ch != ' ' && ch != '\t') whitespace = 0;
656 s->whitespace = whitespace;
658 s->linelen = linelen;
663 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
664 #define BUGGY_ICONV 1
667 * Find the best charset conversion of the file from fromcode into one
668 * of the tocodes. If successful, set *tocode and CONTENT *info and
669 * return the number of characters converted inexactly. If no
670 * conversion was possible, return -1.
672 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
673 * which would otherwise prevent us from knowing the number of inexact
674 * conversions. Where the candidate target charset is UTF-8 we avoid
675 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
676 * fails with some libraries.
678 * We assume that the output from iconv is never more than 4 times as
679 * long as the input for any pair of charsets we might be interested
682 static size_t convert_file_to (FILE *file, const char *fromcode,
683 int ncodes, const char **tocodes,
684 int *tocode, CONTENT *info)
688 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
689 ICONV_CONST char *ib, *ub;
691 size_t ibl, obl, ubl, ubl1, n, ret;
694 CONTENT_STATE *states;
697 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
698 if (cd1 == (iconv_t)(-1))
701 cd = safe_calloc (ncodes, sizeof (iconv_t));
702 score = safe_calloc (ncodes, sizeof (size_t));
703 states = safe_calloc (ncodes, sizeof (CONTENT_STATE));
704 infos = safe_calloc (ncodes, sizeof (CONTENT));
706 for (i = 0; i < ncodes; i++)
707 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
708 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
710 /* Special case for conversion to UTF-8 */
711 cd[i] = (iconv_t)(-1), score[i] = (size_t)(-1);
718 /* Try to fill input buffer */
719 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
722 /* Convert to UTF-8 */
724 ob = bufu, obl = sizeof (bufu);
725 n = iconv (cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
726 assert (n == (size_t)(-1) || !n || ICONV_NONTRANS);
727 if (n == (size_t)(-1) &&
728 ((errno != EINVAL && errno != E2BIG) || ib == bufi))
730 assert (errno == EILSEQ ||
731 (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
737 /* Convert from UTF-8 */
738 for (i = 0; i < ncodes; i++)
739 if (cd[i] != (iconv_t)(-1) && score[i] != (size_t)(-1))
741 ub = bufu, ubl = ubl1;
742 ob = bufo, obl = sizeof (bufo);
743 n = iconv (cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
744 if (n == (size_t)(-1))
746 assert (errno == E2BIG ||
747 (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
748 score[i] = (size_t)(-1);
753 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
756 else if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
757 /* Special case for conversion to UTF-8 */
758 update_content_info (&infos[i], &states[i], bufu, ubl1);
761 /* Save unused input */
762 memmove (bufi, ib, ibl);
763 else if (!ubl1 && ib < bufi + sizeof (bufi))
772 /* Find best score */
774 for (i = 0; i < ncodes; i++)
776 if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
778 /* Special case for conversion to UTF-8 */
783 else if (cd[i] == (iconv_t)(-1) || score[i] == (size_t)(-1))
785 else if (ret == (size_t)(-1) || score[i] < ret)
793 if (ret != (size_t)(-1))
795 memcpy (info, &infos[*tocode], sizeof(CONTENT));
796 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
800 for (i = 0; i < ncodes; i++)
801 if (cd[i] != (iconv_t)(-1))
813 #endif /* !HAVE_ICONV */
817 * Find the first of the fromcodes that gives a valid conversion and
818 * the best charset conversion of the file into one of the tocodes. If
819 * successful, set *fromcode and *tocode to dynamically allocated
820 * strings, set CONTENT *info, and return the number of characters
821 * converted inexactly. If no conversion was possible, return -1.
823 * Both fromcodes and tocodes may be colon-separated lists of charsets.
824 * However, if fromcode is zero then fromcodes is assumed to be the
825 * name of a single charset even if it contains a colon.
827 static size_t convert_file_from_to (FILE *file,
828 const char *fromcodes, const char *tocodes,
829 char **fromcode, char **tocode, CONTENT *info)
837 /* Count the tocodes */
839 for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
841 if ((c1 = strchr (c, ':')) == c)
847 tcode = safe_malloc (ncodes * sizeof (char *));
848 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
850 if ((c1 = strchr (c, ':')) == c)
852 tcode[i] = mutt_substrdup (c, c1);
858 /* Try each fromcode in turn */
859 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
861 if ((c1 = strchr (c, ':')) == c)
863 fcode = mutt_substrdup (c, c1);
865 ret = convert_file_to (file, fcode, ncodes, (const char **)tcode,
867 if (ret != (size_t)(-1))
879 /* There is only one fromcode */
880 ret = convert_file_to (file, fromcodes, ncodes, (const char **)tcode,
882 if (ret != (size_t)(-1))
890 for (i = 0; i < ncodes; i++)
899 * Analyze the contents of a file to determine which MIME encoding to use.
900 * Also set the body charset, sometimes, or not.
902 CONTENT *mutt_get_content_info (const char *fname, BODY *b)
914 if(b && !fname) fname = b->filename;
916 if (stat (fname, &sb) == -1)
918 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
922 if (!S_ISREG(sb.st_mode))
924 mutt_error (_("%s isn't a regular file."), fname);
928 if ((fp = fopen (fname, "r")) == NULL)
930 dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
931 fname, strerror (errno), errno));
935 info = safe_calloc (1, sizeof (CONTENT));
936 memset (&state, 0, sizeof (state));
938 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
940 char *chs = mutt_get_parameter ("charset", b->parameter);
941 if (Charset && (chs || SendCharset) &&
942 convert_file_from_to (fp, Charset, chs ? chs : SendCharset,
943 0, &tocode, info) != (size_t)(-1))
947 mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
948 mutt_set_parameter ("charset", chsbuf, &b->parameter);
957 while ((r = fread (buffer, 1, sizeof(buffer), fp)))
958 update_content_info (info, &state, buffer, r);
959 update_content_info (info, &state, 0, 0);
963 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
964 mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
965 Charset && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
971 /* Given a file with path ``s'', see if there is a registered MIME type.
972 * returns the major MIME type, and copies the subtype to ``d''. First look
973 * for ~/.mime.types, then look in a system mime.types if we can find one.
974 * The longest match is used so that we can match `ps.gz' when `gz' also
978 int mutt_lookup_mime_type (BODY *att, const char *path)
982 char buf[LONG_STRING];
983 char subtype[STRING], xtype[STRING];
985 int szf, sze, cur_sze;
993 szf = mutt_strlen (path);
995 for (count = 0 ; count < 3 ; count++)
998 * can't use strtok() because we use it in an inner loop below, so use
999 * a switch statement here instead.
1004 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
1007 strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
1010 strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
1013 dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
1014 goto bye; /* shouldn't happen */
1017 if ((f = fopen (buf, "r")) != NULL)
1019 while (fgets (buf, sizeof (buf) - 1, f) != NULL)
1021 /* weed out any comments */
1022 if ((p = strchr (buf, '#')))
1025 /* remove any leading space. */
1029 /* position on the next field in this line */
1030 if ((p = strpbrk (ct, " \t")) == NULL)
1035 /* cycle through the file extensions */
1036 while ((p = strtok (p, " \t\n")))
1038 sze = mutt_strlen (p);
1039 if ((sze > cur_sze) && (szf >= sze) &&
1040 (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
1041 (szf == sze || path[szf - sze - 1] == '.'))
1043 /* get the content-type */
1045 if ((p = strchr (ct, '/')) == NULL)
1047 /* malformed line, just skip it. */
1052 for (q = p; *q && !ISSPACE (*q); q++)
1055 mutt_substrcpy (subtype, p, q, sizeof (subtype));
1057 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1058 strfcpy (xtype, ct, sizeof (xtype));
1071 if (type != TYPEOTHER || *xtype != '\0')
1074 mutt_str_replace (&att->subtype, subtype);
1075 mutt_str_replace (&att->xtype, xtype);
1081 void mutt_message_to_7bit (BODY *a, FILE *fp)
1083 char temp[_POSIX_PATH_MAX];
1089 if (!a->filename && fp)
1091 else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1093 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1099 if (stat (a->filename, &sb) == -1)
1101 mutt_perror ("stat");
1104 a->length = sb.st_size;
1108 if (!(fpout = safe_fopen (temp, "w+")))
1110 mutt_perror ("fopen");
1114 fseek (fpin, a->offset, 0);
1115 a->parts = mutt_parse_messageRFC822 (fpin, a);
1117 transform_to_7bit (a->parts, fpin);
1119 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1120 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1122 fputs ("Mime-Version: 1.0\n", fpout);
1123 mutt_write_mime_header (a->parts, fpout);
1124 fputc ('\n', fpout);
1125 mutt_write_mime_body (a->parts, fpout);
1137 a->encoding = ENC7BIT;
1138 a->d_filename = a->filename;
1139 if (a->filename && a->unlink)
1140 unlink (a->filename);
1141 a->filename = safe_strdup (temp);
1143 if(stat (a->filename, &sb) == -1)
1145 mutt_perror ("stat");
1148 a->length = sb.st_size;
1149 mutt_free_body (&a->parts);
1150 a->hdr->content = NULL;
1153 static void transform_to_7bit (BODY *a, FILE *fpin)
1155 char buff[_POSIX_PATH_MAX];
1159 memset (&s, 0, sizeof (s));
1160 for (; a; a = a->next)
1162 if (a->type == TYPEMULTIPART)
1164 if (a->encoding != ENC7BIT)
1165 a->encoding = ENC7BIT;
1167 transform_to_7bit (a->parts, fpin);
1169 else if (mutt_is_message_type(a->type, a->subtype))
1171 mutt_message_to_7bit (a, fpin);
1176 if ((s.fpout = safe_fopen (buff, "w")) == NULL)
1178 mutt_perror ("fopen");
1182 mutt_decode_attachment (a, &s);
1184 a->d_filename = a->filename;
1185 a->filename = safe_strdup (buff);
1187 if (stat (a->filename, &sb) == -1)
1189 mutt_perror ("stat");
1192 a->length = sb.st_size;
1194 mutt_update_encoding (a);
1195 if (a->encoding == ENC8BIT)
1196 a->encoding = ENCQUOTEDPRINTABLE;
1197 else if(a->encoding == ENCBINARY)
1198 a->encoding = ENCBASE64;
1203 /* determine which Content-Transfer-Encoding to use */
1204 static void mutt_set_encoding (BODY *b, CONTENT *info)
1206 char send_charset[SHORT_STRING];
1208 if (b->type == TYPETEXT)
1210 char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1211 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1212 b->encoding = ENCQUOTEDPRINTABLE;
1213 else if (info->hibin)
1214 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1216 b->encoding = ENC7BIT;
1218 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1220 if (info->lobin || info->hibin)
1222 if (option (OPTALLOW8BIT) && !info->lobin)
1223 b->encoding = ENC8BIT;
1225 mutt_message_to_7bit (b, NULL);
1228 b->encoding = ENC7BIT;
1230 else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1231 b->encoding = ENC7BIT;
1234 if (info->lobin || info->hibin || info->binary || info->linemax > 990
1235 || info->cr || (/* option (OPTENCODEFROM) && */ info->from))
1238 /* Determine which encoding is smaller */
1239 if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
1240 3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1241 b->encoding = ENCBASE64;
1243 b->encoding = ENCQUOTEDPRINTABLE;
1247 b->encoding = ENC7BIT;
1251 void mutt_stamp_attachment(BODY *a)
1253 a->stamp = time(NULL);
1256 /* Get a body's character set */
1258 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1262 if (b && b->type != TYPETEXT)
1266 p = mutt_get_parameter ("charset", b->parameter);
1269 mutt_canonical_charset (d, dlen, NONULL(p));
1271 strfcpy (d, "us-ascii", dlen);
1277 /* Assumes called from send mode where BODY->filename points to actual file */
1278 void mutt_update_encoding (BODY *a)
1281 char chsbuff[STRING];
1283 /* override noconv when it's us-ascii */
1284 if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1287 if (!a->force_charset && !a->noconv)
1288 mutt_delete_parameter ("charset", &a->parameter);
1290 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1293 mutt_set_encoding (a, info);
1294 mutt_stamp_attachment(a);
1301 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1303 char buffer[LONG_STRING];
1306 int cmflags, chflags;
1307 int pgp = WithCrypto? hdr->security : 0;
1311 if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1312 (hdr->security & ENCRYPT)) {
1313 if (!crypt_valid_passphrase(hdr->security))
1318 mutt_mktemp (buffer);
1319 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1322 body = mutt_new_body ();
1323 body->type = TYPEMESSAGE;
1324 body->subtype = safe_strdup ("rfc822");
1325 body->filename = safe_strdup (buffer);
1328 body->disposition = DISPINLINE;
1330 mutt_parse_mime_message (ctx, hdr);
1335 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1336 if (!attach_msg && option (OPTMIMEFORWDECODE))
1338 chflags |= CH_MIME | CH_TXTPLAIN;
1339 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1340 if ((WithCrypto & APPLICATION_PGP))
1342 if ((WithCrypto & APPLICATION_SMIME))
1343 pgp &= ~SMIMEENCRYPT;
1346 && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1348 if ((WithCrypto & APPLICATION_PGP)
1349 && mutt_is_multipart_encrypted (hdr->content))
1351 chflags |= CH_MIME | CH_NONEWLINE;
1352 cmflags = M_CM_DECODE_PGP;
1355 else if ((WithCrypto & APPLICATION_PGP)
1356 && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT))
1358 chflags |= CH_MIME | CH_TXTPLAIN;
1359 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1362 else if ((WithCrypto & APPLICATION_SMIME)
1363 && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT)
1365 chflags |= CH_MIME | CH_TXTPLAIN;
1366 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1367 pgp &= ~SMIMEENCRYPT;
1371 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1376 body->hdr = mutt_new_header();
1377 body->hdr->offset = 0;
1378 /* we don't need the user headers here */
1379 body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1381 body->hdr->security = pgp;
1382 mutt_update_encoding (body);
1383 body->parts = body->hdr->content;
1390 BODY *mutt_make_file_attach (const char *path)
1395 att = mutt_new_body ();
1396 att->filename = safe_strdup (path);
1398 /* Attempt to determine the appropriate content-type based on the filename
1404 if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER
1408 att->subtype = safe_strdup (buf);
1409 att->xtype = safe_strdup (xbuf);
1414 mutt_lookup_mime_type (att, path);
1418 if ((info = mutt_get_content_info (path, att)) == NULL)
1420 mutt_free_body (&att);
1426 if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
1429 * Statistically speaking, there should be more than 10% "lobin"
1430 * chars if this is really a binary file...
1432 att->type = TYPETEXT;
1433 att->subtype = safe_strdup ("plain");
1437 att->type = TYPEAPPLICATION;
1438 att->subtype = safe_strdup ("octet-stream");
1442 mutt_update_encoding (att);
1446 static int get_toplevel_encoding (BODY *a)
1450 for (; a; a = a->next)
1452 if (a->encoding == ENCBINARY)
1454 else if (a->encoding == ENC8BIT)
1461 BODY *mutt_make_multipart (BODY *b)
1465 new = mutt_new_body ();
1466 new->type = TYPEMULTIPART;
1467 new->subtype = safe_strdup ("mixed");
1468 new->encoding = get_toplevel_encoding (b);
1469 mutt_generate_boundary (&new->parameter);
1471 new->disposition = DISPINLINE;
1477 /* remove the multipart body if it exists */
1478 BODY *mutt_remove_multipart (BODY *b)
1487 mutt_free_body (&t);
1492 char *mutt_make_date (char *s, size_t len)
1494 time_t t = time (NULL);
1495 struct tm *l = localtime (&t);
1496 time_t tz = mutt_local_tz (t);
1500 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1501 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1502 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1503 (int) tz / 60, (int) abs (tz) % 60);
1507 /* wrapper around mutt_write_address() so we can handle very large
1508 recipient lists without needing a huge temporary buffer in memory */
1509 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1512 char buf[LONG_STRING];
1521 rfc822_write_address (buf, sizeof (buf), adr, display);
1522 len = mutt_strlen (buf);
1523 if (count && linelen + len > 74)
1526 linelen = len + 8; /* tab is usually about 8 spaces... */
1530 if (count && adr->mailbox)
1539 if (!adr->group && adr->next && adr->next->mailbox)
1550 /* arbitrary number of elements to grow the array by */
1555 /* need to write the list in reverse because they are stored in reverse order
1556 * when parsed to speed up threading
1558 void mutt_write_references (LIST *r, FILE *f)
1561 int refcnt = 0, refmax = 0;
1563 for ( ; (TrimRef == 0 || refcnt < TrimRef) && r ; r = r->next)
1565 if (refcnt == refmax)
1566 safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1570 while (refcnt-- > 0)
1573 fputs (ref[refcnt]->data, f);
1579 /* Note: all RFC2047 encoding should be done outside of this routine, except
1580 * for the "real name." This will allow this routine to be used more than
1581 * once, if necessary.
1583 * Likewise, all IDN processing should happen outside of this routine.
1585 * mode == 1 => "lite" mode (used for edit_hdrs)
1586 * mode == 0 => normal mode. write full header + MIME headers
1587 * mode == -1 => write just the envelope info (used for postponing messages)
1589 * privacy != 0 => will omit any headers which may identify the user.
1590 * Output generated is suitable for being sent through
1591 * anonymous remailer chains.
1595 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
1596 int mode, int privacy)
1598 char buffer[LONG_STRING];
1600 LIST *tmp = env->userhdrs;
1601 int has_agent = 0; /* user defined user-agent header field exists */
1604 if (!option (OPTNEWSSEND))
1606 if (mode == 0 && !privacy)
1607 fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
1609 /* OPTUSEFROM is not consulted here so that we can still write a From:
1610 * field if the user sets it with the `my_hdr' command
1612 if (env->from && !privacy)
1615 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1616 fprintf (fp, "From: %s\n", buffer);
1622 mutt_write_address_list (env->to, fp, 4, 0);
1626 if (!option (OPTNEWSSEND))
1628 fputs ("To: \n", fp);
1633 mutt_write_address_list (env->cc, fp, 4, 0);
1637 if (!option (OPTNEWSSEND))
1639 fputs ("Cc: \n", fp);
1643 if(mode != 0 || option(OPTWRITEBCC))
1645 fputs ("Bcc: ", fp);
1646 mutt_write_address_list (env->bcc, fp, 5, 0);
1651 if (!option (OPTNEWSSEND))
1653 fputs ("Bcc: \n", fp);
1656 if (env->newsgroups)
1657 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1658 else if (mode == 1 && option (OPTNEWSSEND))
1659 fputs ("Newsgroups: \n", fp);
1661 if (env->followup_to)
1662 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1663 else if (mode == 1 && option (OPTNEWSSEND))
1664 fputs ("Followup-To: \n", fp);
1666 if (env->x_comment_to)
1667 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1668 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO))
1669 fputs ("X-Comment-To: \n", fp);
1673 fprintf (fp, "Subject: %s\n", env->subject);
1675 fputs ("Subject: \n", fp);
1677 /* save message id if the user has set it */
1678 if (env->message_id && !privacy)
1679 fprintf (fp, "Message-ID: %s\n", env->message_id);
1683 fputs ("Reply-To: ", fp);
1684 mutt_write_address_list (env->reply_to, fp, 10, 0);
1687 fputs ("Reply-To: \n", fp);
1689 if (env->mail_followup_to)
1691 if (!option (OPTNEWSSEND))
1694 fputs ("Mail-Followup-To: ", fp);
1695 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1700 if (env->references)
1702 fputs ("References:", fp);
1703 mutt_write_references (env->references, fp);
1707 /* Add the MIME headers */
1708 fputs ("Mime-Version: 1.0\n", fp);
1709 mutt_write_mime_header (attach, fp);
1712 if (env->in_reply_to)
1714 fputs ("In-Reply-To:", fp);
1715 mutt_write_references (env->in_reply_to, fp);
1719 /* Add any user defined headers */
1720 for (; tmp; tmp = tmp->next)
1722 if ((p = strchr (tmp->data, ':')))
1725 if (!*p) continue; /* don't emit empty fields. */
1727 /* check to see if the user has overridden the user-agent field */
1728 if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
1735 fputs (tmp->data, fp);
1740 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent)
1744 if (OperatingSystem!=NULL) {
1745 os = OperatingSystem;
1747 if (uname(&un)==-1) {
1753 /* Add a vanity header */
1754 fprintf (fp, "User-Agent: mutt-ng %s (%s)\n", MUTT_VERSION,os);
1757 return (ferror (fp) == 0 ? 0 : -1);
1760 static void encode_headers (LIST *h)
1766 for (; h; h = h->next)
1768 if (!(p = strchr (h->data, ':')))
1773 tmp = safe_strdup (p);
1778 rfc2047_encode_string (&tmp);
1779 safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
1781 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
1787 const char *mutt_fqdn(short may_hide_host)
1791 if(Fqdn && Fqdn[0] != '@')
1795 if(may_hide_host && option(OPTHIDDENHOST))
1797 if((p = strchr(Fqdn, '.')))
1800 /* sanity check: don't hide the host if
1801 * the fqdn is something like detebe.org.
1804 if(!p || !(q = strchr(p, '.')))
1812 char *mutt_gen_msgid (void)
1814 char buf[SHORT_STRING];
1821 if(!(fqdn = mutt_fqdn(0)))
1822 fqdn = NONULL(Hostname);
1824 snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.G%c%d@%s>",
1825 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
1826 tm->tm_min, tm->tm_sec, MsgIdPfx, getpid (), fqdn);
1827 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1828 return (safe_strdup (buf));
1831 static RETSIGTYPE alarm_handler (int sig)
1836 /* invoke sendmail in a subshell
1837 path (in) path to program to execute
1838 args (in) arguments to pass to program
1839 msg (in) temp file containing message to send
1840 tempfile (out) if sendmail is put in the background, this points
1841 to the temporary file containing the stdout of the
1844 send_msg (const char *path, char **args, const char *msg, char **tempfile)
1850 mutt_block_signals_system ();
1853 /* we also don't want to be stopped right now */
1854 sigaddset (&set, SIGTSTP);
1855 sigprocmask (SIG_BLOCK, &set, NULL);
1857 if (SendmailWait >= 0)
1859 char tmp[_POSIX_PATH_MAX];
1862 *tempfile = safe_strdup (tmp);
1865 if ((pid = fork ()) == 0)
1867 struct sigaction act, oldalrm;
1869 /* save parent's ID before setsid() */
1872 /* we want the delivery to continue even after the main process dies,
1873 * so we put ourselves into another session right away
1877 /* next we close all open files */
1878 #if defined(OPEN_MAX)
1879 for (fd = 0; fd < OPEN_MAX; fd++)
1881 #elif defined(_POSIX_OPEN_MAX)
1882 for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1890 /* now the second fork() */
1891 if ((pid = fork ()) == 0)
1893 /* "msg" will be opened as stdin */
1894 if (open (msg, O_RDONLY, 0) < 0)
1901 if (SendmailWait >= 0)
1903 /* *tempfile will be opened as stdout */
1904 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
1906 /* redirect stderr to *tempfile too */
1912 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1914 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1928 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1929 * SendmailWait = 0: wait forever
1930 * SendmailWait < 0: don't wait
1932 if (SendmailWait > 0)
1935 act.sa_handler = alarm_handler;
1937 /* need to make sure waitpid() is interrupted on SIGALRM */
1938 act.sa_flags = SA_INTERRUPT;
1942 sigemptyset (&act.sa_mask);
1943 sigaction (SIGALRM, &act, &oldalrm);
1944 alarm (SendmailWait);
1946 else if (SendmailWait < 0)
1947 _exit (0xff & EX_OK);
1949 if (waitpid (pid, &st, 0) > 0)
1951 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1952 if (SendmailWait && st == (0xff & EX_OK))
1954 unlink (*tempfile); /* no longer needed */
1960 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
1962 if (SendmailWait > 0)
1969 /* reset alarm; not really needed, but... */
1971 sigaction (SIGALRM, &oldalrm, NULL);
1973 if (kill (ppid, 0) == -1 && errno == ESRCH)
1975 /* the parent is already dead */
1983 sigprocmask (SIG_UNBLOCK, &set, NULL);
1985 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1986 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1988 st = S_ERR; /* error */
1990 mutt_unblock_signals_system (1);
1996 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
1998 for (; addr; addr = addr->next)
2000 /* weed out group mailboxes, since those are for display only */
2001 if (addr->mailbox && !addr->group)
2003 if (*argslen == *argsmax)
2004 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2005 args[(*argslen)++] = addr->mailbox;
2012 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2014 if (*argslen == *argsmax)
2015 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2016 args[(*argslen)++] = s;
2025 for(i = 0; sysexits_h[i].str; i++)
2027 if(e == sysexits_h[i].v)
2031 return sysexits_h[i].str;
2036 mutt_invoke_sendmail (ADDRESS *from, /* the sender */
2037 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2038 const char *msg, /* file containing message */
2039 int eightbit) /* message contains 8bit chars */
2041 char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
2043 size_t argslen = 0, argsmax = 0;
2047 if (option (OPTNEWSSEND))
2049 char cmd[LONG_STRING];
2051 mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0, 0);
2054 i = nntp_post (msg);
2059 s = safe_strdup (cmd);
2063 s = safe_strdup (Sendmail);
2067 while ((ps = strtok (ps, " ")))
2069 if (argslen == argsmax)
2070 safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2073 args[argslen++] = ps;
2076 path = safe_strdup (ps);
2077 ps = strrchr (ps, '/');
2082 args[argslen++] = ps;
2089 if (!option (OPTNEWSSEND))
2092 if (eightbit && option (OPTUSE8BITMIME))
2093 args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2095 if (option (OPTENVFROM) && from && !from->next)
2097 args = add_option (args, &argslen, &argsmax, "-f");
2098 args = add_args (args, &argslen, &argsmax, from);
2102 args = add_option (args, &argslen, &argsmax, "-N");
2103 args = add_option (args, &argslen, &argsmax, DsnNotify);
2107 args = add_option (args, &argslen, &argsmax, "-R");
2108 args = add_option (args, &argslen, &argsmax, DsnReturn);
2110 args = add_option (args, &argslen, &argsmax, "--");
2111 args = add_args (args, &argslen, &argsmax, to);
2112 args = add_args (args, &argslen, &argsmax, cc);
2113 args = add_args (args, &argslen, &argsmax, bcc);
2118 if (argslen == argsmax)
2119 safe_realloc (&args, sizeof (char *) * (++argsmax));
2121 args[argslen++] = NULL;
2123 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff))
2127 const char *e = strsysexit (i);
2130 mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2135 if (stat (childout, &st) == 0 && st.st_size > 0)
2136 mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2148 if (i == (EX_OK & 0xff))
2150 else if (i == S_BKG)
2158 mutt_invoke_mta (ADDRESS *from, /* the sender */
2159 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2160 const char *msg, /* file containing message */
2161 int eightbit) /* message contains 8bit chars */
2165 return mutt_invoke_libesmtp(from, to, cc, bcc, msg, eightbit);
2168 return mutt_invoke_sendmail(from, to, cc, bcc, msg, eightbit);
2171 /* appends string 'b' to string 'a', and returns the pointer to the new
2173 char *mutt_append_string (char *a, const char *b)
2175 size_t la = mutt_strlen (a);
2176 safe_realloc (&a, la + mutt_strlen (b) + 1);
2177 strcpy (a + la, b); /* __STRCPY_CHECKED__ */
2181 /* returns 1 if char `c' needs to be quoted to protect from shell
2182 interpretation when executing commands in a subshell */
2183 #define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
2185 /* returns 1 if string `s' contains characters which could cause problems
2186 when used on a command line to execute a command */
2187 int mutt_needs_quote (const char *s)
2191 if (INVALID_CHAR (*s))
2198 /* Quote a string to prevent shell escapes when this string is used on the
2199 command line to send mail. */
2200 char *mutt_quote_string (const char *s)
2205 rlen = mutt_strlen (s) + 3;
2206 pr = r = (char *) safe_malloc (rlen);
2210 if (INVALID_CHAR (*s))
2213 safe_realloc (&r, ++rlen);
2224 /* For postponing (!final) do the necessary encodings only */
2225 void mutt_prepare_envelope (ENVELOPE *env, int final)
2227 char buffer[LONG_STRING];
2231 if (env->bcc && !(env->to || env->cc))
2233 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2234 * recipients if there is no To: or Cc: field, so attempt to suppress
2235 * it by using an empty To: field.
2237 env->to = rfc822_new_address ();
2239 env->to->next = rfc822_new_address ();
2242 rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2245 env->to->mailbox = safe_strdup (buffer);
2248 mutt_set_followup_to (env);
2250 if (!env->message_id)
2251 env->message_id = mutt_gen_msgid ();
2254 /* Take care of 8-bit => 7-bit conversion. */
2255 rfc2047_encode_adrlist (env->to, "To");
2256 rfc2047_encode_adrlist (env->cc, "Cc");
2257 rfc2047_encode_adrlist (env->from, "From");
2258 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2259 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2263 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2266 rfc2047_encode_string (&env->subject);
2268 encode_headers (env->userhdrs);
2271 void mutt_unprepare_envelope (ENVELOPE *env)
2275 for (item = env->userhdrs; item; item = item->next)
2276 rfc2047_decode (&item->data);
2278 rfc822_free_address (&env->mail_followup_to);
2280 /* back conversions */
2281 rfc2047_decode_adrlist (env->to);
2282 rfc2047_decode_adrlist (env->cc);
2283 rfc2047_decode_adrlist (env->from);
2284 rfc2047_decode_adrlist (env->reply_to);
2285 rfc2047_decode (&env->subject);
2288 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2293 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2294 MESSAGE *msg = NULL;
2298 /* Try to bounce each message out, aborting if we get any failures. */
2299 for (i=0; i<Context->msgcount; i++)
2300 if (Context->hdrs[i]->tagged)
2301 ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2305 /* If we failed to open a message, return with error */
2306 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2309 if (!fp) fp = msg->fp;
2311 mutt_mktemp (tempfile);
2312 if ((f = safe_fopen (tempfile, "w")) != NULL)
2314 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2316 if (!option (OPTBOUNCEDELIVERED))
2317 ch_flags |= CH_WEED_DELIVERED;
2319 fseek (fp, h->offset, 0);
2320 fprintf (f, "Resent-From: %s", resent_from);
2321 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2322 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2323 fputs ("Resent-To: ", f);
2324 mutt_write_address_list (to, f, 11, 0);
2325 mutt_copy_header (fp, h, f, ch_flags, NULL);
2327 mutt_copy_bytes (fp, f, h->content->length);
2330 ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2331 h->content->encoding == ENC8BIT);
2335 mx_close_message (&msg);
2340 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2343 const char *fqdn = mutt_fqdn (1);
2344 char resent_from[STRING];
2348 resent_from[0] = '\0';
2349 from = mutt_default_from ();
2352 rfc822_qualify (from, fqdn);
2354 rfc2047_encode_adrlist (from, "Resent-From");
2355 if (mutt_addrlist_to_idna (from, &err))
2357 mutt_error (_("Bad IDN %s while preparing resent-from."),
2361 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2364 unset_option (OPTNEWSSEND);
2367 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2369 rfc822_free_address (&from);
2375 /* given a list of addresses, return a list of unique addresses */
2376 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2378 ADDRESS *top = NULL;
2381 if ((top = addr) == NULL)
2389 if (addr->mailbox && tmp->mailbox &&
2390 !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2392 /* duplicate address, just ignore it */
2396 rfc822_free_address (&tmp);
2398 else if (!tmp->next)
2400 /* unique address. add it to the list */
2405 tmp = NULL; /* so we exit the loop */
2415 static void set_noconv_flags (BODY *b, short flag)
2417 for(; b; b = b->next)
2419 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2420 set_noconv_flags (b->parts, flag);
2421 else if (b->type == TYPETEXT && b->noconv)
2424 mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2426 mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2431 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc)
2435 char tempfile[_POSIX_PATH_MAX];
2436 FILE *tempfp = NULL;
2440 set_noconv_flags (hdr->content, 1);
2442 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
2444 dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2449 /* We need to add a Content-Length field to avoid problems where a line in
2450 * the message body begins with "From "
2452 if (f.magic == M_MMDF || f.magic == M_MBOX)
2454 mutt_mktemp (tempfile);
2455 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2457 mutt_perror (tempfile);
2458 mx_close_mailbox (&f, NULL);
2463 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2464 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
2466 mx_close_mailbox (&f, NULL);
2470 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2471 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2473 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 0);
2475 /* (postponment) if this was a reply of some sort, <msgid> contians the
2476 * Message-ID: of message replied to. Save it using a special X-Mutt-
2477 * header so it can be picked up if the message is recalled at a later
2478 * point in time. This will allow the message to be marked as replied if
2479 * the same mailbox is still open.
2482 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2484 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2485 * it can be picked up when the message is recalled
2488 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2489 fprintf (msg->fp, "Status: RO\n");
2493 /* (postponment) if the mail is to be signed or encrypted, save this info */
2494 if ((WithCrypto & APPLICATION_PGP)
2495 && post && (hdr->security & APPLICATION_PGP))
2497 fputs ("X-Mutt-PGP: ", msg->fp);
2498 if (hdr->security & ENCRYPT)
2499 fputc ('E', msg->fp);
2500 if (hdr->security & SIGN)
2502 fputc ('S', msg->fp);
2503 if (PgpSignAs && *PgpSignAs)
2504 fprintf (msg->fp, "<%s>", PgpSignAs);
2506 fputc ('\n', msg->fp);
2509 /* (postponment) if the mail is to be signed or encrypted, save this info */
2510 if ((WithCrypto & APPLICATION_SMIME)
2511 && post && (hdr->security & APPLICATION_SMIME))
2513 fputs ("X-Mutt-SMIME: ", msg->fp);
2514 if (hdr->security & ENCRYPT) {
2515 fputc ('E', msg->fp);
2516 if (SmimeCryptAlg && *SmimeCryptAlg)
2517 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2519 if (hdr->security & SIGN) {
2520 fputc ('S', msg->fp);
2521 if (SmimeDefaultKey && *SmimeDefaultKey)
2522 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2524 fputc ('\n', msg->fp);
2528 /* (postponement) if the mail is to be sent through a mixmaster
2529 * chain, save that information
2532 if (post && hdr->chain && hdr->chain)
2536 fputs ("X-Mutt-Mix:", msg->fp);
2537 for (p = hdr->chain; p; p = p->next)
2538 fprintf (msg->fp, " %s", (char *) p->data);
2540 fputc ('\n', msg->fp);
2546 char sasha[LONG_STRING];
2549 mutt_write_mime_body (hdr->content, tempfp);
2551 /* make sure the last line ends with a newline. Emacs doesn't ensure
2552 * this will happen, and it can cause problems parsing the mailbox
2555 fseek (tempfp, -1, 2);
2556 if (fgetc (tempfp) != '\n')
2558 fseek (tempfp, 0, 2);
2559 fputc ('\n', tempfp);
2563 if (ferror (tempfp))
2565 dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2568 mx_commit_message (msg, &f); /* XXX - really? */
2569 mx_close_message (&msg);
2570 mx_close_mailbox (&f, NULL);
2574 /* count the number of lines */
2576 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2578 fprintf (msg->fp, "Content-Length: %ld\n", (long) ftell (tempfp));
2579 fprintf (msg->fp, "Lines: %d\n\n", lines);
2581 /* copy the body and clean up */
2583 r = mutt_copy_stream (tempfp, msg->fp);
2584 if (fclose (tempfp) != 0)
2586 /* if there was an error, leave the temp version */
2592 fputc ('\n', msg->fp); /* finish off the header */
2593 r = mutt_write_mime_body (hdr->content, msg->fp);
2596 if (mx_commit_message (msg, &f) != 0)
2598 mx_close_message (&msg);
2599 mx_close_mailbox (&f, NULL);
2602 set_noconv_flags (hdr->content, 0);