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"
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 static struct sysexits
73 { 0xff & EX_USAGE, "Bad usage." },
76 { 0xff & EX_DATAERR, "Data format error." },
79 { 0xff & EX_NOINPUT, "Cannot open input." },
82 { 0xff & EX_NOUSER, "User unknown." },
85 { 0xff & EX_NOHOST, "Host unknown." },
88 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
91 { 0xff & EX_SOFTWARE, "Internal error." },
94 { 0xff & EX_OSERR, "Operating system error." },
97 { 0xff & EX_OSFILE, "System file missing." },
100 { 0xff & EX_CANTCREAT, "Can't create output." },
103 { 0xff & EX_IOERR, "I/O error." },
106 { 0xff & EX_TEMPFAIL, "Deferred." },
109 { 0xff & EX_PROTOCOL, "Remote protocol error." },
112 { 0xff & EX_NOPERM, "Insufficient permission." },
115 { 0xff & EX_NOPERM, "Local configuration error." },
117 { S_ERR, "Exec error." },
123 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
125 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
127 char B64Chars[64] = {
128 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
129 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
130 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
131 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
135 static char MsgIdPfx = 'A';
137 static void transform_to_7bit (BODY *a, FILE *fpin);
139 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
142 char line[77], savechar;
144 while ((c = fgetconv (fc)) != EOF)
146 /* Wrap the line if needed. */
147 if (linelen == 76 && ((istext && c != '\n') || !istext))
149 /* If the last character is "quoted", then be sure to move all three
150 * characters to the next line. Otherwise, just move the last
153 if (line[linelen-3] == '=')
160 line[1] = line[linelen-2];
161 line[2] = line[linelen-1];
166 savechar = line[linelen-1];
167 line[linelen-1] = '=';
176 /* Escape lines that begin with/only contain "the message separator". */
177 if (linelen == 4 && !mutt_strncmp ("From", line, 4))
179 strfcpy (line, "=46rom", sizeof (line));
182 else if (linelen == 4 && !mutt_strncmp ("from", line, 4))
184 strfcpy (line, "=66rom", sizeof (line));
187 else if (linelen == 1 && line[0] == '.')
189 strfcpy (line, "=2E", sizeof (line));
194 # include "mutt_libesmtp.h"
195 #endif /* USE_LIBESMTP */
197 if (c == '\n' && istext)
199 /* Check to make sure there is no trailing space on this line. */
200 if (linelen > 0 && (line[linelen-1] == ' ' || line[linelen-1] == '\t'))
204 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
209 int savechar = line[linelen-1];
211 line[linelen-1] = '=';
214 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
225 else if (c != 9 && (c < 32 || c > 126 || c == '='))
227 /* Check to make sure there is enough room for the quoted character.
228 * If not, wrap to the next line.
232 line[linelen++] = '=';
238 sprintf (line+linelen,"=%2.2X", (unsigned char) c);
243 /* Don't worry about wrapping the line here. That will happen during
244 * the next iteration when I'll also know what the next character is.
250 /* Take care of anything left in the buffer */
253 if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
255 /* take care of trailing whitespace */
257 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
260 savechar = line[linelen-1];
261 line[linelen-1] = '=';
265 sprintf (line, "=%2.2X", (unsigned char) savechar);
274 static char b64_buffer[3];
275 static short b64_num;
276 static short b64_linelen;
278 static void b64_flush(FILE *fout)
285 if(b64_linelen >= 72)
291 for(i = b64_num; i < 3; i++)
292 b64_buffer[i] = '\0';
294 fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
296 fputc(B64Chars[((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf) ], fout);
301 fputc(B64Chars[((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3) ], fout);
305 fputc(B64Chars[b64_buffer[2] & 0x3f], fout);
310 while(b64_linelen % 4)
320 static void b64_putc(char c, FILE *fout)
325 b64_buffer[b64_num++] = c;
329 static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
333 b64_num = b64_linelen = 0;
335 while ((ch = fgetconv (fc)) != EOF)
337 if (istext && ch == '\n' && ch1 != '\r')
338 b64_putc('\r', fout);
346 static void encode_8bit (FGETCONV *fc, FILE *fout, int istext)
350 while ((ch = fgetconv (fc)) != EOF)
355 int mutt_write_mime_header (BODY *a, FILE *f)
365 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
369 len = 25 + mutt_strlen (a->subtype); /* approximate len. of content-type */
371 for(p = a->parameter; p; p = p->next)
381 tmp = safe_strdup (p->value);
382 encode = rfc2231_encode_string (&tmp);
383 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
385 /* Dirty hack to make messages readable by Outlook Express
386 * for the Mac: force quotes around the boundary parameter
387 * even when they aren't needed.
390 if (!ascii_strcasecmp (p->attribute, "boundary") && !strcmp (buffer, tmp))
391 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
395 tmplen = mutt_strlen (buffer) + mutt_strlen (p->attribute) + 1;
397 if (len + tmplen + 2 > 76)
408 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
416 fprintf(f, "Content-Description: %s\n", a->description);
418 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
422 if(!(fn = a->d_filename))
429 /* Strip off the leading path... */
430 if ((t = strrchr (fn, '/')))
436 tmp = safe_strdup (t);
437 encode = rfc2231_encode_string (&tmp);
438 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
440 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
446 if (a->encoding != ENC7BIT)
447 fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
449 /* Do NOT add the terminator here!!! */
450 return (ferror (f) ? -1 : 0);
453 # define write_as_text_part(a) (mutt_is_text_part(a) \
454 || ((WithCrypto & APPLICATION_PGP)\
455 && mutt_is_application_pgp(a)))
457 int mutt_write_mime_body (BODY *a, FILE *f)
459 char *p, boundary[SHORT_STRING];
460 char send_charset[SHORT_STRING];
465 if (a->type == TYPEMULTIPART)
467 /* First, find the boundary to use */
468 if (!(p = mutt_get_parameter ("boundary", a->parameter)))
470 dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
471 mutt_error _("No boundary parameter found! [report this error]");
474 strfcpy (boundary, p, sizeof (boundary));
476 for (t = a->parts; t ; t = t->next)
478 fprintf (f, "\n--%s\n", boundary);
479 if (mutt_write_mime_header (t, f) == -1)
482 if (mutt_write_mime_body (t, f) == -1)
485 fprintf (f, "\n--%s--\n", boundary);
486 return (ferror (f) ? -1 : 0);
489 /* This is pretty gross, but it's the best solution for now... */
490 if ((WithCrypto & APPLICATION_PGP)
491 && a->type == TYPEAPPLICATION
492 && mutt_strcmp (a->subtype, "pgp-encrypted") == 0)
494 fputs ("Version: 1\n", f);
498 if ((fpin = fopen (a->filename, "r")) == NULL)
500 dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
501 mutt_error (_("%s no longer exists!"), a->filename);
505 if (a->type == TYPETEXT && (!a->noconv))
506 fc = fgetconv_open (fpin, Charset,
507 mutt_get_body_charset (send_charset, sizeof (send_charset), a),
510 fc = fgetconv_open (fpin, 0, 0, 0);
512 if (a->encoding == ENCQUOTEDPRINTABLE)
513 encode_quoted (fc, f, write_as_text_part (a));
514 else if (a->encoding == ENCBASE64)
515 encode_base64 (fc, f, write_as_text_part (a));
516 else if (a->type == TYPETEXT && (!a->noconv))
517 encode_8bit (fc, f, write_as_text_part (a));
519 mutt_copy_stream (fpin, f);
521 fgetconv_close (&fc);
524 return (ferror (f) ? -1 : 0);
527 #undef write_as_text_part
529 #define BOUNDARYLEN 16
530 void mutt_generate_boundary (PARAMETER **parm)
532 char rs[BOUNDARYLEN + 1];
537 for (i=0;i<BOUNDARYLEN;i++)
538 *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
541 mutt_set_parameter ("boundary", rs, parm);
555 static void update_content_info (CONTENT *info, CONTENT_STATE *s, char *d, size_t dlen)
558 int whitespace = s->whitespace;
560 int linelen = s->linelen;
561 int was_cr = s->was_cr;
563 if (!d) /* This signals EOF */
567 if (linelen > info->linemax)
568 info->linemax = linelen;
573 for (; dlen; d++, dlen--)
586 if (whitespace) info->space = 1;
587 if (dot) info->dot = 1;
588 if (linelen > info->linemax) info->linemax = linelen;
600 if (whitespace) info->space = 1;
601 if (dot) info->dot = 1;
602 if (linelen > info->linemax) info->linemax = linelen;
616 else if (ch == '\t' || ch == '\f')
621 else if (ch < 32 || ch == 127)
627 if ((ch == 'F') || (ch == 'f'))
638 if (linelen == 2 && ch != 'r') from = 0;
639 else if (linelen == 3 && ch != 'o') from = 0;
640 else if (linelen == 4)
642 if (ch == 'm') info->from = 1;
646 if (ch == ' ') whitespace++;
650 if (linelen > 1) dot = 0;
651 if (ch != ' ' && ch != '\t') whitespace = 0;
655 s->whitespace = whitespace;
657 s->linelen = linelen;
662 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
663 #define BUGGY_ICONV 1
666 * Find the best charset conversion of the file from fromcode into one
667 * of the tocodes. If successful, set *tocode and CONTENT *info and
668 * return the number of characters converted inexactly. If no
669 * conversion was possible, return -1.
671 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
672 * which would otherwise prevent us from knowing the number of inexact
673 * conversions. Where the candidate target charset is UTF-8 we avoid
674 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
675 * fails with some libraries.
677 * We assume that the output from iconv is never more than 4 times as
678 * long as the input for any pair of charsets we might be interested
681 static size_t convert_file_to (FILE *file, const char *fromcode,
682 int ncodes, const char **tocodes,
683 int *tocode, CONTENT *info)
687 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
688 ICONV_CONST char *ib, *ub;
690 size_t ibl, obl, ubl, ubl1, n, ret;
693 CONTENT_STATE *states;
696 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
697 if (cd1 == (iconv_t)(-1))
700 cd = safe_calloc (ncodes, sizeof (iconv_t));
701 score = safe_calloc (ncodes, sizeof (size_t));
702 states = safe_calloc (ncodes, sizeof (CONTENT_STATE));
703 infos = safe_calloc (ncodes, sizeof (CONTENT));
705 for (i = 0; i < ncodes; i++)
706 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
707 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
709 /* Special case for conversion to UTF-8 */
710 cd[i] = (iconv_t)(-1), score[i] = (size_t)(-1);
717 /* Try to fill input buffer */
718 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
721 /* Convert to UTF-8 */
723 ob = bufu, obl = sizeof (bufu);
724 n = iconv (cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
725 assert (n == (size_t)(-1) || !n || ICONV_NONTRANS);
726 if (n == (size_t)(-1) &&
727 ((errno != EINVAL && errno != E2BIG) || ib == bufi))
729 assert (errno == EILSEQ ||
730 (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
736 /* Convert from UTF-8 */
737 for (i = 0; i < ncodes; i++)
738 if (cd[i] != (iconv_t)(-1) && score[i] != (size_t)(-1))
740 ub = bufu, ubl = ubl1;
741 ob = bufo, obl = sizeof (bufo);
742 n = iconv (cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
743 if (n == (size_t)(-1))
745 assert (errno == E2BIG ||
746 (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
747 score[i] = (size_t)(-1);
752 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
755 else if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
756 /* Special case for conversion to UTF-8 */
757 update_content_info (&infos[i], &states[i], bufu, ubl1);
760 /* Save unused input */
761 memmove (bufi, ib, ibl);
762 else if (!ubl1 && ib < bufi + sizeof (bufi))
771 /* Find best score */
773 for (i = 0; i < ncodes; i++)
775 if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
777 /* Special case for conversion to UTF-8 */
782 else if (cd[i] == (iconv_t)(-1) || score[i] == (size_t)(-1))
784 else if (ret == (size_t)(-1) || score[i] < ret)
792 if (ret != (size_t)(-1))
794 memcpy (info, &infos[*tocode], sizeof(CONTENT));
795 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
799 for (i = 0; i < ncodes; i++)
800 if (cd[i] != (iconv_t)(-1))
812 #endif /* !HAVE_ICONV */
816 * Find the first of the fromcodes that gives a valid conversion and
817 * the best charset conversion of the file into one of the tocodes. If
818 * successful, set *fromcode and *tocode to dynamically allocated
819 * strings, set CONTENT *info, and return the number of characters
820 * converted inexactly. If no conversion was possible, return -1.
822 * Both fromcodes and tocodes may be colon-separated lists of charsets.
823 * However, if fromcode is zero then fromcodes is assumed to be the
824 * name of a single charset even if it contains a colon.
826 static size_t convert_file_from_to (FILE *file,
827 const char *fromcodes, const char *tocodes,
828 char **fromcode, char **tocode, CONTENT *info)
836 /* Count the tocodes */
838 for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
840 if ((c1 = strchr (c, ':')) == c)
846 tcode = safe_malloc (ncodes * sizeof (char *));
847 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
849 if ((c1 = strchr (c, ':')) == c)
851 tcode[i] = mutt_substrdup (c, c1);
857 /* Try each fromcode in turn */
858 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
860 if ((c1 = strchr (c, ':')) == c)
862 fcode = mutt_substrdup (c, c1);
864 ret = convert_file_to (file, fcode, ncodes, (const char **)tcode,
866 if (ret != (size_t)(-1))
878 /* There is only one fromcode */
879 ret = convert_file_to (file, fromcodes, ncodes, (const char **)tcode,
881 if (ret != (size_t)(-1))
889 for (i = 0; i < ncodes; i++)
898 * Analyze the contents of a file to determine which MIME encoding to use.
899 * Also set the body charset, sometimes, or not.
901 CONTENT *mutt_get_content_info (const char *fname, BODY *b)
913 if(b && !fname) fname = b->filename;
915 if (stat (fname, &sb) == -1)
917 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
921 if (!S_ISREG(sb.st_mode))
923 mutt_error (_("%s isn't a regular file."), fname);
927 if ((fp = fopen (fname, "r")) == NULL)
929 dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
930 fname, strerror (errno), errno));
934 info = safe_calloc (1, sizeof (CONTENT));
935 memset (&state, 0, sizeof (state));
937 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
939 char *chs = mutt_get_parameter ("charset", b->parameter);
940 if (Charset && (chs || SendCharset) &&
941 convert_file_from_to (fp, Charset, chs ? chs : SendCharset,
942 0, &tocode, info) != (size_t)(-1))
946 mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
947 mutt_set_parameter ("charset", chsbuf, &b->parameter);
956 while ((r = fread (buffer, 1, sizeof(buffer), fp)))
957 update_content_info (info, &state, buffer, r);
958 update_content_info (info, &state, 0, 0);
962 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
963 mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
964 Charset && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
970 /* Given a file with path ``s'', see if there is a registered MIME type.
971 * returns the major MIME type, and copies the subtype to ``d''. First look
972 * for ~/.mime.types, then look in a system mime.types if we can find one.
973 * The longest match is used so that we can match `ps.gz' when `gz' also
977 int mutt_lookup_mime_type (BODY *att, const char *path)
981 char buf[LONG_STRING];
982 char subtype[STRING], xtype[STRING];
984 int szf, sze, cur_sze;
992 szf = mutt_strlen (path);
994 for (count = 0 ; count < 3 ; count++)
997 * can't use strtok() because we use it in an inner loop below, so use
998 * a switch statement here instead.
1003 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
1006 strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
1009 strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
1012 dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
1013 goto bye; /* shouldn't happen */
1016 if ((f = fopen (buf, "r")) != NULL)
1018 while (fgets (buf, sizeof (buf) - 1, f) != NULL)
1020 /* weed out any comments */
1021 if ((p = strchr (buf, '#')))
1024 /* remove any leading space. */
1028 /* position on the next field in this line */
1029 if ((p = strpbrk (ct, " \t")) == NULL)
1034 /* cycle through the file extensions */
1035 while ((p = strtok (p, " \t\n")))
1037 sze = mutt_strlen (p);
1038 if ((sze > cur_sze) && (szf >= sze) &&
1039 (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
1040 (szf == sze || path[szf - sze - 1] == '.'))
1042 /* get the content-type */
1044 if ((p = strchr (ct, '/')) == NULL)
1046 /* malformed line, just skip it. */
1051 for (q = p; *q && !ISSPACE (*q); q++)
1054 mutt_substrcpy (subtype, p, q, sizeof (subtype));
1056 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1057 strfcpy (xtype, ct, sizeof (xtype));
1070 if (type != TYPEOTHER || *xtype != '\0')
1073 mutt_str_replace (&att->subtype, subtype);
1074 mutt_str_replace (&att->xtype, xtype);
1080 void mutt_message_to_7bit (BODY *a, FILE *fp)
1082 char temp[_POSIX_PATH_MAX];
1088 if (!a->filename && fp)
1090 else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1092 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1098 if (stat (a->filename, &sb) == -1)
1100 mutt_perror ("stat");
1103 a->length = sb.st_size;
1107 if (!(fpout = safe_fopen (temp, "w+")))
1109 mutt_perror ("fopen");
1113 fseek (fpin, a->offset, 0);
1114 a->parts = mutt_parse_messageRFC822 (fpin, a);
1116 transform_to_7bit (a->parts, fpin);
1118 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1119 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1121 fputs ("Mime-Version: 1.0\n", fpout);
1122 mutt_write_mime_header (a->parts, fpout);
1123 fputc ('\n', fpout);
1124 mutt_write_mime_body (a->parts, fpout);
1136 a->encoding = ENC7BIT;
1137 a->d_filename = a->filename;
1138 if (a->filename && a->unlink)
1139 unlink (a->filename);
1140 a->filename = safe_strdup (temp);
1142 if(stat (a->filename, &sb) == -1)
1144 mutt_perror ("stat");
1147 a->length = sb.st_size;
1148 mutt_free_body (&a->parts);
1149 a->hdr->content = NULL;
1152 static void transform_to_7bit (BODY *a, FILE *fpin)
1154 char buff[_POSIX_PATH_MAX];
1158 memset (&s, 0, sizeof (s));
1159 for (; a; a = a->next)
1161 if (a->type == TYPEMULTIPART)
1163 if (a->encoding != ENC7BIT)
1164 a->encoding = ENC7BIT;
1166 transform_to_7bit (a->parts, fpin);
1168 else if (mutt_is_message_type(a->type, a->subtype))
1170 mutt_message_to_7bit (a, fpin);
1175 if ((s.fpout = safe_fopen (buff, "w")) == NULL)
1177 mutt_perror ("fopen");
1181 mutt_decode_attachment (a, &s);
1183 a->d_filename = a->filename;
1184 a->filename = safe_strdup (buff);
1186 if (stat (a->filename, &sb) == -1)
1188 mutt_perror ("stat");
1191 a->length = sb.st_size;
1193 mutt_update_encoding (a);
1194 if (a->encoding == ENC8BIT)
1195 a->encoding = ENCQUOTEDPRINTABLE;
1196 else if(a->encoding == ENCBINARY)
1197 a->encoding = ENCBASE64;
1202 /* determine which Content-Transfer-Encoding to use */
1203 static void mutt_set_encoding (BODY *b, CONTENT *info)
1205 char send_charset[SHORT_STRING];
1207 if (b->type == TYPETEXT)
1209 char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1210 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1211 b->encoding = ENCQUOTEDPRINTABLE;
1212 else if (info->hibin)
1213 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1215 b->encoding = ENC7BIT;
1217 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1219 if (info->lobin || info->hibin)
1221 if (option (OPTALLOW8BIT) && !info->lobin)
1222 b->encoding = ENC8BIT;
1224 mutt_message_to_7bit (b, NULL);
1227 b->encoding = ENC7BIT;
1229 else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1230 b->encoding = ENC7BIT;
1233 if (info->lobin || info->hibin || info->binary || info->linemax > 990
1234 || info->cr || (/* option (OPTENCODEFROM) && */ info->from))
1237 /* Determine which encoding is smaller */
1238 if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
1239 3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1240 b->encoding = ENCBASE64;
1242 b->encoding = ENCQUOTEDPRINTABLE;
1246 b->encoding = ENC7BIT;
1250 void mutt_stamp_attachment(BODY *a)
1252 a->stamp = time(NULL);
1255 /* Get a body's character set */
1257 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1261 if (b && b->type != TYPETEXT)
1265 p = mutt_get_parameter ("charset", b->parameter);
1268 mutt_canonical_charset (d, dlen, NONULL(p));
1270 strfcpy (d, "us-ascii", dlen);
1276 /* Assumes called from send mode where BODY->filename points to actual file */
1277 void mutt_update_encoding (BODY *a)
1280 char chsbuff[STRING];
1282 /* override noconv when it's us-ascii */
1283 if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1286 if (!a->force_charset && !a->noconv)
1287 mutt_delete_parameter ("charset", &a->parameter);
1289 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1292 mutt_set_encoding (a, info);
1293 mutt_stamp_attachment(a);
1300 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1302 char buffer[LONG_STRING];
1305 int cmflags, chflags;
1306 int pgp = WithCrypto? hdr->security : 0;
1310 if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1311 (hdr->security & ENCRYPT)) {
1312 if (!crypt_valid_passphrase(hdr->security))
1317 mutt_mktemp (buffer);
1318 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1321 body = mutt_new_body ();
1322 body->type = TYPEMESSAGE;
1323 body->subtype = safe_strdup ("rfc822");
1324 body->filename = safe_strdup (buffer);
1327 body->disposition = DISPINLINE;
1329 mutt_parse_mime_message (ctx, hdr);
1334 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1335 if (!attach_msg && option (OPTMIMEFORWDECODE))
1337 chflags |= CH_MIME | CH_TXTPLAIN;
1338 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1339 if ((WithCrypto & APPLICATION_PGP))
1341 if ((WithCrypto & APPLICATION_SMIME))
1342 pgp &= ~SMIMEENCRYPT;
1345 && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1347 if ((WithCrypto & APPLICATION_PGP)
1348 && mutt_is_multipart_encrypted (hdr->content))
1350 chflags |= CH_MIME | CH_NONEWLINE;
1351 cmflags = M_CM_DECODE_PGP;
1354 else if ((WithCrypto & APPLICATION_PGP)
1355 && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT))
1357 chflags |= CH_MIME | CH_TXTPLAIN;
1358 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1361 else if ((WithCrypto & APPLICATION_SMIME)
1362 && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT)
1364 chflags |= CH_MIME | CH_TXTPLAIN;
1365 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1366 pgp &= ~SMIMEENCRYPT;
1370 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1375 body->hdr = mutt_new_header();
1376 body->hdr->offset = 0;
1377 /* we don't need the user headers here */
1378 body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1380 body->hdr->security = pgp;
1381 mutt_update_encoding (body);
1382 body->parts = body->hdr->content;
1389 BODY *mutt_make_file_attach (const char *path)
1394 att = mutt_new_body ();
1395 att->filename = safe_strdup (path);
1397 /* Attempt to determine the appropriate content-type based on the filename
1403 if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER
1407 att->subtype = safe_strdup (buf);
1408 att->xtype = safe_strdup (xbuf);
1413 mutt_lookup_mime_type (att, path);
1417 if ((info = mutt_get_content_info (path, att)) == NULL)
1419 mutt_free_body (&att);
1425 if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
1428 * Statistically speaking, there should be more than 10% "lobin"
1429 * chars if this is really a binary file...
1431 att->type = TYPETEXT;
1432 att->subtype = safe_strdup ("plain");
1436 att->type = TYPEAPPLICATION;
1437 att->subtype = safe_strdup ("octet-stream");
1441 mutt_update_encoding (att);
1445 static int get_toplevel_encoding (BODY *a)
1449 for (; a; a = a->next)
1451 if (a->encoding == ENCBINARY)
1453 else if (a->encoding == ENC8BIT)
1460 BODY *mutt_make_multipart (BODY *b)
1464 new = mutt_new_body ();
1465 new->type = TYPEMULTIPART;
1466 new->subtype = safe_strdup ("mixed");
1467 new->encoding = get_toplevel_encoding (b);
1468 mutt_generate_boundary (&new->parameter);
1470 new->disposition = DISPINLINE;
1476 /* remove the multipart body if it exists */
1477 BODY *mutt_remove_multipart (BODY *b)
1486 mutt_free_body (&t);
1491 char *mutt_make_date (char *s, size_t len)
1493 time_t t = time (NULL);
1494 struct tm *l = localtime (&t);
1495 time_t tz = mutt_local_tz (t);
1499 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1500 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1501 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1502 (int) tz / 60, (int) abs (tz) % 60);
1506 /* wrapper around mutt_write_address() so we can handle very large
1507 recipient lists without needing a huge temporary buffer in memory */
1508 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1511 char buf[LONG_STRING];
1520 rfc822_write_address (buf, sizeof (buf), adr, display);
1521 len = mutt_strlen (buf);
1522 if (count && linelen + len > 74)
1525 linelen = len + 8; /* tab is usually about 8 spaces... */
1529 if (count && adr->mailbox)
1538 if (!adr->group && adr->next && adr->next->mailbox)
1549 /* arbitrary number of elements to grow the array by */
1554 /* need to write the list in reverse because they are stored in reverse order
1555 * when parsed to speed up threading
1557 void mutt_write_references (LIST *r, FILE *f)
1560 int refcnt = 0, refmax = 0;
1562 for ( ; (TrimRef == 0 || refcnt < TrimRef) && r ; r = r->next)
1564 if (refcnt == refmax)
1565 safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1569 while (refcnt-- > 0)
1572 fputs (ref[refcnt]->data, f);
1578 /* Note: all RFC2047 encoding should be done outside of this routine, except
1579 * for the "real name." This will allow this routine to be used more than
1580 * once, if necessary.
1582 * Likewise, all IDN processing should happen outside of this routine.
1584 * mode == 1 => "lite" mode (used for edit_hdrs)
1585 * mode == 0 => normal mode. write full header + MIME headers
1586 * mode == -1 => write just the envelope info (used for postponing messages)
1588 * privacy != 0 => will omit any headers which may identify the user.
1589 * Output generated is suitable for being sent through
1590 * anonymous remailer chains.
1594 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
1595 int mode, int privacy)
1597 char buffer[LONG_STRING];
1599 LIST *tmp = env->userhdrs;
1600 int has_agent = 0; /* user defined user-agent header field exists */
1603 if (!option (OPTNEWSSEND))
1605 if (mode == 0 && !privacy)
1606 fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
1608 /* OPTUSEFROM is not consulted here so that we can still write a From:
1609 * field if the user sets it with the `my_hdr' command
1611 if (env->from && !privacy)
1614 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1615 fprintf (fp, "From: %s\n", buffer);
1621 mutt_write_address_list (env->to, fp, 4, 0);
1625 if (!option (OPTNEWSSEND))
1627 fputs ("To: \n", fp);
1632 mutt_write_address_list (env->cc, fp, 4, 0);
1636 if (!option (OPTNEWSSEND))
1638 fputs ("Cc: \n", fp);
1642 if(mode != 0 || option(OPTWRITEBCC))
1644 fputs ("Bcc: ", fp);
1645 mutt_write_address_list (env->bcc, fp, 5, 0);
1650 if (!option (OPTNEWSSEND))
1652 fputs ("Bcc: \n", fp);
1655 if (env->newsgroups)
1656 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1657 else if (mode == 1 && option (OPTNEWSSEND))
1658 fputs ("Newsgroups: \n", fp);
1660 if (env->followup_to)
1661 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1662 else if (mode == 1 && option (OPTNEWSSEND))
1663 fputs ("Followup-To: \n", fp);
1665 if (env->x_comment_to)
1666 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1667 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO))
1668 fputs ("X-Comment-To: \n", fp);
1672 fprintf (fp, "Subject: %s\n", env->subject);
1674 fputs ("Subject: \n", fp);
1676 /* save message id if the user has set it */
1677 if (env->message_id && !privacy)
1678 fprintf (fp, "Message-ID: %s\n", env->message_id);
1682 fputs ("Reply-To: ", fp);
1683 mutt_write_address_list (env->reply_to, fp, 10, 0);
1686 fputs ("Reply-To: \n", fp);
1688 if (env->mail_followup_to)
1690 if (!option (OPTNEWSSEND))
1693 fputs ("Mail-Followup-To: ", fp);
1694 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1699 if (env->references)
1701 fputs ("References:", fp);
1702 mutt_write_references (env->references, fp);
1706 /* Add the MIME headers */
1707 fputs ("Mime-Version: 1.0\n", fp);
1708 mutt_write_mime_header (attach, fp);
1711 if (env->in_reply_to)
1713 fputs ("In-Reply-To:", fp);
1714 mutt_write_references (env->in_reply_to, fp);
1718 /* Add any user defined headers */
1719 for (; tmp; tmp = tmp->next)
1721 if ((p = strchr (tmp->data, ':')))
1724 if (!*p) continue; /* don't emit empty fields. */
1726 /* check to see if the user has overridden the user-agent field */
1727 if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
1734 fputs (tmp->data, fp);
1739 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent)
1741 /* Add a vanity header */
1742 fprintf (fp, "User-Agent: Mutt/%s\n", MUTT_VERSION);
1745 return (ferror (fp) == 0 ? 0 : -1);
1748 static void encode_headers (LIST *h)
1754 for (; h; h = h->next)
1756 if (!(p = strchr (h->data, ':')))
1761 tmp = safe_strdup (p);
1766 rfc2047_encode_string (&tmp);
1767 safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
1769 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
1775 const char *mutt_fqdn(short may_hide_host)
1779 if(Fqdn && Fqdn[0] != '@')
1783 if(may_hide_host && option(OPTHIDDENHOST))
1785 if((p = strchr(Fqdn, '.')))
1788 /* sanity check: don't hide the host if
1789 * the fqdn is something like detebe.org.
1792 if(!p || !(q = strchr(p, '.')))
1800 char *mutt_gen_msgid (void)
1802 char buf[SHORT_STRING];
1809 if(!(fqdn = mutt_fqdn(0)))
1810 fqdn = NONULL(Hostname);
1812 snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.G%c%d@%s>",
1813 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
1814 tm->tm_min, tm->tm_sec, MsgIdPfx, getpid (), fqdn);
1815 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1816 return (safe_strdup (buf));
1819 static RETSIGTYPE alarm_handler (int sig)
1824 /* invoke sendmail in a subshell
1825 path (in) path to program to execute
1826 args (in) arguments to pass to program
1827 msg (in) temp file containing message to send
1828 tempfile (out) if sendmail is put in the background, this points
1829 to the temporary file containing the stdout of the
1832 send_msg (const char *path, char **args, const char *msg, char **tempfile)
1838 mutt_block_signals_system ();
1841 /* we also don't want to be stopped right now */
1842 sigaddset (&set, SIGTSTP);
1843 sigprocmask (SIG_BLOCK, &set, NULL);
1845 if (SendmailWait >= 0)
1847 char tmp[_POSIX_PATH_MAX];
1850 *tempfile = safe_strdup (tmp);
1853 if ((pid = fork ()) == 0)
1855 struct sigaction act, oldalrm;
1857 /* save parent's ID before setsid() */
1860 /* we want the delivery to continue even after the main process dies,
1861 * so we put ourselves into another session right away
1865 /* next we close all open files */
1866 #if defined(OPEN_MAX)
1867 for (fd = 0; fd < OPEN_MAX; fd++)
1869 #elif defined(_POSIX_OPEN_MAX)
1870 for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1878 /* now the second fork() */
1879 if ((pid = fork ()) == 0)
1881 /* "msg" will be opened as stdin */
1882 if (open (msg, O_RDONLY, 0) < 0)
1889 if (SendmailWait >= 0)
1891 /* *tempfile will be opened as stdout */
1892 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
1894 /* redirect stderr to *tempfile too */
1900 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
1902 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
1916 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
1917 * SendmailWait = 0: wait forever
1918 * SendmailWait < 0: don't wait
1920 if (SendmailWait > 0)
1923 act.sa_handler = alarm_handler;
1925 /* need to make sure waitpid() is interrupted on SIGALRM */
1926 act.sa_flags = SA_INTERRUPT;
1930 sigemptyset (&act.sa_mask);
1931 sigaction (SIGALRM, &act, &oldalrm);
1932 alarm (SendmailWait);
1934 else if (SendmailWait < 0)
1935 _exit (0xff & EX_OK);
1937 if (waitpid (pid, &st, 0) > 0)
1939 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
1940 if (SendmailWait && st == (0xff & EX_OK))
1942 unlink (*tempfile); /* no longer needed */
1948 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
1950 if (SendmailWait > 0)
1957 /* reset alarm; not really needed, but... */
1959 sigaction (SIGALRM, &oldalrm, NULL);
1961 if (kill (ppid, 0) == -1 && errno == ESRCH)
1963 /* the parent is already dead */
1971 sigprocmask (SIG_UNBLOCK, &set, NULL);
1973 if (pid != -1 && waitpid (pid, &st, 0) > 0)
1974 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
1976 st = S_ERR; /* error */
1978 mutt_unblock_signals_system (1);
1984 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
1986 for (; addr; addr = addr->next)
1988 /* weed out group mailboxes, since those are for display only */
1989 if (addr->mailbox && !addr->group)
1991 if (*argslen == *argsmax)
1992 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
1993 args[(*argslen)++] = addr->mailbox;
2000 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2002 if (*argslen == *argsmax)
2003 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2004 args[(*argslen)++] = s;
2013 for(i = 0; sysexits_h[i].str; i++)
2015 if(e == sysexits_h[i].v)
2019 return sysexits_h[i].str;
2024 mutt_invoke_sendmail (ADDRESS *from, /* the sender */
2025 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2026 const char *msg, /* file containing message */
2027 int eightbit) /* message contains 8bit chars */
2029 char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
2031 size_t argslen = 0, argsmax = 0;
2035 if (option (OPTNEWSSEND))
2037 char cmd[LONG_STRING];
2039 mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0, 0);
2042 i = nntp_post (msg);
2047 s = safe_strdup (cmd);
2051 s = safe_strdup (Sendmail);
2055 while ((ps = strtok (ps, " ")))
2057 if (argslen == argsmax)
2058 safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2061 args[argslen++] = ps;
2064 path = safe_strdup (ps);
2065 ps = strrchr (ps, '/');
2070 args[argslen++] = ps;
2077 if (!option (OPTNEWSSEND))
2080 if (eightbit && option (OPTUSE8BITMIME))
2081 args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2083 if (option (OPTENVFROM) && from && !from->next)
2085 args = add_option (args, &argslen, &argsmax, "-f");
2086 args = add_args (args, &argslen, &argsmax, from);
2090 args = add_option (args, &argslen, &argsmax, "-N");
2091 args = add_option (args, &argslen, &argsmax, DsnNotify);
2095 args = add_option (args, &argslen, &argsmax, "-R");
2096 args = add_option (args, &argslen, &argsmax, DsnReturn);
2098 args = add_option (args, &argslen, &argsmax, "--");
2099 args = add_args (args, &argslen, &argsmax, to);
2100 args = add_args (args, &argslen, &argsmax, cc);
2101 args = add_args (args, &argslen, &argsmax, bcc);
2106 if (argslen == argsmax)
2107 safe_realloc (&args, sizeof (char *) * (++argsmax));
2109 args[argslen++] = NULL;
2111 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff))
2115 const char *e = strsysexit (i);
2118 mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2123 if (stat (childout, &st) == 0 && st.st_size > 0)
2124 mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2136 if (i == (EX_OK & 0xff))
2138 else if (i == S_BKG)
2146 mutt_invoke_mta (ADDRESS *from, /* the sender */
2147 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2148 const char *msg, /* file containing message */
2149 int eightbit) /* message contains 8bit chars */
2153 return mutt_invoke_libesmtp(from, to, cc, bcc, msg, eightbit);
2156 return mutt_invoke_sendmail(from, to, cc, bcc, msg, eightbit);
2159 /* appends string 'b' to string 'a', and returns the pointer to the new
2161 char *mutt_append_string (char *a, const char *b)
2163 size_t la = mutt_strlen (a);
2164 safe_realloc (&a, la + mutt_strlen (b) + 1);
2165 strcpy (a + la, b); /* __STRCPY_CHECKED__ */
2169 /* returns 1 if char `c' needs to be quoted to protect from shell
2170 interpretation when executing commands in a subshell */
2171 #define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
2173 /* returns 1 if string `s' contains characters which could cause problems
2174 when used on a command line to execute a command */
2175 int mutt_needs_quote (const char *s)
2179 if (INVALID_CHAR (*s))
2186 /* Quote a string to prevent shell escapes when this string is used on the
2187 command line to send mail. */
2188 char *mutt_quote_string (const char *s)
2193 rlen = mutt_strlen (s) + 3;
2194 pr = r = (char *) safe_malloc (rlen);
2198 if (INVALID_CHAR (*s))
2201 safe_realloc (&r, ++rlen);
2212 /* For postponing (!final) do the necessary encodings only */
2213 void mutt_prepare_envelope (ENVELOPE *env, int final)
2215 char buffer[LONG_STRING];
2219 if (env->bcc && !(env->to || env->cc))
2221 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2222 * recipients if there is no To: or Cc: field, so attempt to suppress
2223 * it by using an empty To: field.
2225 env->to = rfc822_new_address ();
2227 env->to->next = rfc822_new_address ();
2230 rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2233 env->to->mailbox = safe_strdup (buffer);
2236 mutt_set_followup_to (env);
2238 if (!env->message_id)
2239 env->message_id = mutt_gen_msgid ();
2242 /* Take care of 8-bit => 7-bit conversion. */
2243 rfc2047_encode_adrlist (env->to, "To");
2244 rfc2047_encode_adrlist (env->cc, "Cc");
2245 rfc2047_encode_adrlist (env->from, "From");
2246 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2247 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2251 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2254 rfc2047_encode_string (&env->subject);
2256 encode_headers (env->userhdrs);
2259 void mutt_unprepare_envelope (ENVELOPE *env)
2263 for (item = env->userhdrs; item; item = item->next)
2264 rfc2047_decode (&item->data);
2266 rfc822_free_address (&env->mail_followup_to);
2268 /* back conversions */
2269 rfc2047_decode_adrlist (env->to);
2270 rfc2047_decode_adrlist (env->cc);
2271 rfc2047_decode_adrlist (env->from);
2272 rfc2047_decode_adrlist (env->reply_to);
2273 rfc2047_decode (&env->subject);
2276 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2281 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2282 MESSAGE *msg = NULL;
2286 /* Try to bounce each message out, aborting if we get any failures. */
2287 for (i=0; i<Context->msgcount; i++)
2288 if (Context->hdrs[i]->tagged)
2289 ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2293 /* If we failed to open a message, return with error */
2294 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2297 if (!fp) fp = msg->fp;
2299 mutt_mktemp (tempfile);
2300 if ((f = safe_fopen (tempfile, "w")) != NULL)
2302 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2304 if (!option (OPTBOUNCEDELIVERED))
2305 ch_flags |= CH_WEED_DELIVERED;
2307 fseek (fp, h->offset, 0);
2308 fprintf (f, "Resent-From: %s", resent_from);
2309 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2310 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2311 fputs ("Resent-To: ", f);
2312 mutt_write_address_list (to, f, 11, 0);
2313 mutt_copy_header (fp, h, f, ch_flags, NULL);
2315 mutt_copy_bytes (fp, f, h->content->length);
2318 ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2319 h->content->encoding == ENC8BIT);
2323 mx_close_message (&msg);
2328 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2331 const char *fqdn = mutt_fqdn (1);
2332 char resent_from[STRING];
2336 resent_from[0] = '\0';
2337 from = mutt_default_from ();
2340 rfc822_qualify (from, fqdn);
2342 rfc2047_encode_adrlist (from, "Resent-From");
2343 if (mutt_addrlist_to_idna (from, &err))
2345 mutt_error (_("Bad IDN %s while preparing resent-from."),
2349 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2352 unset_option (OPTNEWSSEND);
2355 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2357 rfc822_free_address (&from);
2363 /* given a list of addresses, return a list of unique addresses */
2364 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2366 ADDRESS *top = NULL;
2369 if ((top = addr) == NULL)
2377 if (addr->mailbox && tmp->mailbox &&
2378 !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2380 /* duplicate address, just ignore it */
2384 rfc822_free_address (&tmp);
2386 else if (!tmp->next)
2388 /* unique address. add it to the list */
2393 tmp = NULL; /* so we exit the loop */
2403 static void set_noconv_flags (BODY *b, short flag)
2405 for(; b; b = b->next)
2407 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2408 set_noconv_flags (b->parts, flag);
2409 else if (b->type == TYPETEXT && b->noconv)
2412 mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2414 mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2419 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc)
2423 char tempfile[_POSIX_PATH_MAX];
2424 FILE *tempfp = NULL;
2428 set_noconv_flags (hdr->content, 1);
2430 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
2432 dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2437 /* We need to add a Content-Length field to avoid problems where a line in
2438 * the message body begins with "From "
2440 if (f.magic == M_MMDF || f.magic == M_MBOX)
2442 mutt_mktemp (tempfile);
2443 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2445 mutt_perror (tempfile);
2446 mx_close_mailbox (&f, NULL);
2451 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2452 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
2454 mx_close_mailbox (&f, NULL);
2458 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2459 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2461 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 0);
2463 /* (postponment) if this was a reply of some sort, <msgid> contians the
2464 * Message-ID: of message replied to. Save it using a special X-Mutt-
2465 * header so it can be picked up if the message is recalled at a later
2466 * point in time. This will allow the message to be marked as replied if
2467 * the same mailbox is still open.
2470 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2472 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2473 * it can be picked up when the message is recalled
2476 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2477 fprintf (msg->fp, "Status: RO\n");
2481 /* (postponment) if the mail is to be signed or encrypted, save this info */
2482 if ((WithCrypto & APPLICATION_PGP)
2483 && post && (hdr->security & APPLICATION_PGP))
2485 fputs ("X-Mutt-PGP: ", msg->fp);
2486 if (hdr->security & ENCRYPT)
2487 fputc ('E', msg->fp);
2488 if (hdr->security & SIGN)
2490 fputc ('S', msg->fp);
2491 if (PgpSignAs && *PgpSignAs)
2492 fprintf (msg->fp, "<%s>", PgpSignAs);
2494 fputc ('\n', msg->fp);
2497 /* (postponment) if the mail is to be signed or encrypted, save this info */
2498 if ((WithCrypto & APPLICATION_SMIME)
2499 && post && (hdr->security & APPLICATION_SMIME))
2501 fputs ("X-Mutt-SMIME: ", msg->fp);
2502 if (hdr->security & ENCRYPT) {
2503 fputc ('E', msg->fp);
2504 if (SmimeCryptAlg && *SmimeCryptAlg)
2505 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2507 if (hdr->security & SIGN) {
2508 fputc ('S', msg->fp);
2509 if (SmimeDefaultKey && *SmimeDefaultKey)
2510 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2512 fputc ('\n', msg->fp);
2516 /* (postponement) if the mail is to be sent through a mixmaster
2517 * chain, save that information
2520 if (post && hdr->chain && hdr->chain)
2524 fputs ("X-Mutt-Mix:", msg->fp);
2525 for (p = hdr->chain; p; p = p->next)
2526 fprintf (msg->fp, " %s", (char *) p->data);
2528 fputc ('\n', msg->fp);
2534 char sasha[LONG_STRING];
2537 mutt_write_mime_body (hdr->content, tempfp);
2539 /* make sure the last line ends with a newline. Emacs doesn't ensure
2540 * this will happen, and it can cause problems parsing the mailbox
2543 fseek (tempfp, -1, 2);
2544 if (fgetc (tempfp) != '\n')
2546 fseek (tempfp, 0, 2);
2547 fputc ('\n', tempfp);
2551 if (ferror (tempfp))
2553 dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2556 mx_commit_message (msg, &f); /* XXX - really? */
2557 mx_close_message (&msg);
2558 mx_close_mailbox (&f, NULL);
2562 /* count the number of lines */
2564 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2566 fprintf (msg->fp, "Content-Length: %ld\n", (long) ftell (tempfp));
2567 fprintf (msg->fp, "Lines: %d\n\n", lines);
2569 /* copy the body and clean up */
2571 r = mutt_copy_stream (tempfp, msg->fp);
2572 if (fclose (tempfp) != 0)
2574 /* if there was an error, leave the temp version */
2580 fputc ('\n', msg->fp); /* finish off the header */
2581 r = mutt_write_mime_body (hdr->content, msg->fp);
2584 if (mx_commit_message (msg, &f) != 0)
2586 mx_close_message (&msg);
2587 mx_close_mailbox (&f, NULL);
2590 set_noconv_flags (hdr->content, 0);