2 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "mutt_curses.h"
35 #include "mutt_crypt.h"
36 #include "mutt_idna.h"
48 #ifdef HAVE_SYSEXITS_H
50 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
54 /* If you are debugging this file, comment out the following line. */
63 extern char RFC822Specials[];
65 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
67 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
70 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
71 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
72 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
73 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
77 static char MsgIdPfx = 'A';
79 static void transform_to_7bit (BODY *a, FILE *fpin);
81 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
84 char line[77], savechar;
86 while ((c = fgetconv (fc)) != EOF)
88 /* Wrap the line if needed. */
89 if (linelen == 76 && ((istext && c != '\n') || !istext))
91 /* If the last character is "quoted", then be sure to move all three
92 * characters to the next line. Otherwise, just move the last
95 if (line[linelen-3] == '=')
102 line[1] = line[linelen-2];
103 line[2] = line[linelen-1];
108 savechar = line[linelen-1];
109 line[linelen-1] = '=';
118 /* Escape lines that begin with/only contain "the message separator". */
119 if (linelen == 4 && !mutt_strncmp ("From", line, 4))
121 strfcpy (line, "=46rom", sizeof (line));
124 else if (linelen == 4 && !mutt_strncmp ("from", line, 4))
126 strfcpy (line, "=66rom", sizeof (line));
129 else if (linelen == 1 && line[0] == '.')
131 strfcpy (line, "=2E", sizeof (line));
136 if (c == '\n' && istext)
138 /* Check to make sure there is no trailing space on this line. */
139 if (linelen > 0 && (line[linelen-1] == ' ' || line[linelen-1] == '\t'))
143 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
148 int savechar = line[linelen-1];
150 line[linelen-1] = '=';
153 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
164 else if (c != 9 && (c < 32 || c > 126 || c == '='))
166 /* Check to make sure there is enough room for the quoted character.
167 * If not, wrap to the next line.
171 line[linelen++] = '=';
177 sprintf (line+linelen,"=%2.2X", (unsigned char) c);
182 /* Don't worry about wrapping the line here. That will happen during
183 * the next iteration when I'll also know what the next character is.
189 /* Take care of anything left in the buffer */
192 if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
194 /* take care of trailing whitespace */
196 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
199 savechar = line[linelen-1];
200 line[linelen-1] = '=';
204 sprintf (line, "=%2.2X", (unsigned char) savechar);
213 static char b64_buffer[3];
214 static short b64_num;
215 static short b64_linelen;
217 static void b64_flush(FILE *fout)
224 if(b64_linelen >= 72)
230 for(i = b64_num; i < 3; i++)
231 b64_buffer[i] = '\0';
233 fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
235 fputc(B64Chars[((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf) ], fout);
240 fputc(B64Chars[((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3) ], fout);
244 fputc(B64Chars[b64_buffer[2] & 0x3f], fout);
249 while(b64_linelen % 4)
259 static void b64_putc(char c, FILE *fout)
264 b64_buffer[b64_num++] = c;
268 static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
272 b64_num = b64_linelen = 0;
274 while ((ch = fgetconv (fc)) != EOF)
276 if (istext && ch == '\n' && ch1 != '\r')
277 b64_putc('\r', fout);
285 static void encode_8bit (FGETCONV *fc, FILE *fout, int istext)
289 while ((ch = fgetconv (fc)) != EOF)
294 int mutt_write_mime_header (BODY *a, FILE *f)
304 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
308 len = 25 + mutt_strlen (a->subtype); /* approximate len. of content-type */
310 for(p = a->parameter; p; p = p->next)
320 tmp = safe_strdup (p->value);
321 encode = rfc2231_encode_string (&tmp);
322 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
324 /* Dirty hack to make messages readable by Outlook Express
325 * for the Mac: force quotes around the boundary parameter
326 * even when they aren't needed.
329 if (!ascii_strcasecmp (p->attribute, "boundary") && !strcmp (buffer, tmp))
330 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
334 tmplen = mutt_strlen (buffer) + mutt_strlen (p->attribute) + 1;
336 if (len + tmplen + 2 > 76)
347 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
355 fprintf(f, "Content-Description: %s\n", a->description);
357 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
361 if(!(fn = a->d_filename))
368 /* Strip off the leading path... */
369 if ((t = strrchr (fn, '/')))
375 tmp = safe_strdup (t);
376 encode = rfc2231_encode_string (&tmp);
377 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
379 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
385 if (a->encoding != ENC7BIT)
386 fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
388 /* Do NOT add the terminator here!!! */
389 return (ferror (f) ? -1 : 0);
392 # define write_as_text_part(a) (mutt_is_text_part(a) \
393 || ((WithCrypto & APPLICATION_PGP)\
394 && mutt_is_application_pgp(a)))
396 int mutt_write_mime_body (BODY *a, FILE *f)
398 char *p, boundary[SHORT_STRING];
399 char send_charset[SHORT_STRING];
404 if (a->type == TYPEMULTIPART)
406 /* First, find the boundary to use */
407 if (!(p = mutt_get_parameter ("boundary", a->parameter)))
409 dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
410 mutt_error _("No boundary parameter found! [report this error]");
413 strfcpy (boundary, p, sizeof (boundary));
415 for (t = a->parts; t ; t = t->next)
417 fprintf (f, "\n--%s\n", boundary);
418 if (mutt_write_mime_header (t, f) == -1)
421 if (mutt_write_mime_body (t, f) == -1)
424 fprintf (f, "\n--%s--\n", boundary);
425 return (ferror (f) ? -1 : 0);
428 /* This is pretty gross, but it's the best solution for now... */
429 if ((WithCrypto & APPLICATION_PGP)
430 && a->type == TYPEAPPLICATION
431 && mutt_strcmp (a->subtype, "pgp-encrypted") == 0)
433 fputs ("Version: 1\n", f);
437 if ((fpin = fopen (a->filename, "r")) == NULL)
439 dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
440 mutt_error (_("%s no longer exists!"), a->filename);
444 if (a->type == TYPETEXT && (!a->noconv))
445 fc = fgetconv_open (fpin, a->charset,
446 mutt_get_body_charset (send_charset, sizeof (send_charset), a),
449 fc = fgetconv_open (fpin, 0, 0, 0);
451 if (a->encoding == ENCQUOTEDPRINTABLE)
452 encode_quoted (fc, f, write_as_text_part (a));
453 else if (a->encoding == ENCBASE64)
454 encode_base64 (fc, f, write_as_text_part (a));
455 else if (a->type == TYPETEXT && (!a->noconv))
456 encode_8bit (fc, f, write_as_text_part (a));
458 mutt_copy_stream (fpin, f);
460 fgetconv_close (&fc);
463 return (ferror (f) ? -1 : 0);
466 #undef write_as_text_part
468 #define BOUNDARYLEN 16
469 void mutt_generate_boundary (PARAMETER **parm)
471 char rs[BOUNDARYLEN + 1];
476 for (i=0;i<BOUNDARYLEN;i++)
477 *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
480 mutt_set_parameter ("boundary", rs, parm);
494 static void update_content_info (CONTENT *info, CONTENT_STATE *s, char *d, size_t dlen)
497 int whitespace = s->whitespace;
499 int linelen = s->linelen;
500 int was_cr = s->was_cr;
502 if (!d) /* This signals EOF */
506 if (linelen > info->linemax)
507 info->linemax = linelen;
512 for (; dlen; d++, dlen--)
525 if (whitespace) info->space = 1;
526 if (dot) info->dot = 1;
527 if (linelen > info->linemax) info->linemax = linelen;
539 if (whitespace) info->space = 1;
540 if (dot) info->dot = 1;
541 if (linelen > info->linemax) info->linemax = linelen;
555 else if (ch == '\t' || ch == '\f')
560 else if (ch < 32 || ch == 127)
566 if ((ch == 'F') || (ch == 'f'))
577 if (linelen == 2 && ch != 'r') from = 0;
578 else if (linelen == 3 && ch != 'o') from = 0;
579 else if (linelen == 4)
581 if (ch == 'm') info->from = 1;
585 if (ch == ' ') whitespace++;
589 if (linelen > 1) dot = 0;
590 if (ch != ' ' && ch != '\t') whitespace = 0;
594 s->whitespace = whitespace;
596 s->linelen = linelen;
601 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
602 #define BUGGY_ICONV 1
605 * Find the best charset conversion of the file from fromcode into one
606 * of the tocodes. If successful, set *tocode and CONTENT *info and
607 * return the number of characters converted inexactly. If no
608 * conversion was possible, return -1.
610 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
611 * which would otherwise prevent us from knowing the number of inexact
612 * conversions. Where the candidate target charset is UTF-8 we avoid
613 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
614 * fails with some libraries.
616 * We assume that the output from iconv is never more than 4 times as
617 * long as the input for any pair of charsets we might be interested
620 static size_t convert_file_to (FILE *file, const char *fromcode,
621 int ncodes, const char **tocodes,
622 int *tocode, CONTENT *info)
626 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
627 ICONV_CONST char *ib, *ub;
629 size_t ibl, obl, ubl, ubl1, n, ret;
632 CONTENT_STATE *states;
635 cd1 = mutt_iconv_open ("utf-8", fromcode, 0);
636 if (cd1 == (iconv_t)(-1))
639 cd = safe_calloc (ncodes, sizeof (iconv_t));
640 score = safe_calloc (ncodes, sizeof (size_t));
641 states = safe_calloc (ncodes, sizeof (CONTENT_STATE));
642 infos = safe_calloc (ncodes, sizeof (CONTENT));
644 for (i = 0; i < ncodes; i++)
645 if (ascii_strcasecmp (tocodes[i], "utf-8"))
646 cd[i] = mutt_iconv_open (tocodes[i], "utf-8", 0);
648 /* Special case for conversion to UTF-8 */
649 cd[i] = (iconv_t)(-1), score[i] = (size_t)(-1);
656 /* Try to fill input buffer */
657 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
660 /* Convert to UTF-8 */
662 ob = bufu, obl = sizeof (bufu);
663 n = iconv (cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
664 assert (n == (size_t)(-1) || !n || ICONV_NONTRANS);
665 if (n == (size_t)(-1) &&
666 ((errno != EINVAL && errno != E2BIG) || ib == bufi))
668 assert (errno == EILSEQ ||
669 (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
675 /* Convert from UTF-8 */
676 for (i = 0; i < ncodes; i++)
677 if (cd[i] != (iconv_t)(-1) && score[i] != (size_t)(-1))
679 ub = bufu, ubl = ubl1;
680 ob = bufo, obl = sizeof (bufo);
681 n = iconv (cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
682 if (n == (size_t)(-1))
684 assert (errno == E2BIG ||
685 (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
686 score[i] = (size_t)(-1);
691 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
694 else if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
695 /* Special case for conversion to UTF-8 */
696 update_content_info (&infos[i], &states[i], bufu, ubl1);
699 /* Save unused input */
700 memmove (bufi, ib, ibl);
701 else if (!ubl1 && ib < bufi + sizeof (bufi))
710 /* Find best score */
712 for (i = 0; i < ncodes; i++)
714 if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
716 /* Special case for conversion to UTF-8 */
721 else if (cd[i] == (iconv_t)(-1) || score[i] == (size_t)(-1))
723 else if (ret == (size_t)(-1) || score[i] < ret)
731 if (ret != (size_t)(-1))
733 memcpy (info, &infos[*tocode], sizeof(CONTENT));
734 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
738 for (i = 0; i < ncodes; i++)
739 if (cd[i] != (iconv_t)(-1))
751 #endif /* !HAVE_ICONV */
755 * Find the first of the fromcodes that gives a valid conversion and
756 * the best charset conversion of the file into one of the tocodes. If
757 * successful, set *fromcode and *tocode to dynamically allocated
758 * strings, set CONTENT *info, and return the number of characters
759 * converted inexactly. If no conversion was possible, return -1.
761 * Both fromcodes and tocodes may be colon-separated lists of charsets.
762 * However, if fromcode is zero then fromcodes is assumed to be the
763 * name of a single charset even if it contains a colon.
765 static size_t convert_file_from_to (FILE *file,
766 const char *fromcodes, const char *tocodes,
767 char **fromcode, char **tocode, CONTENT *info)
775 /* Count the tocodes */
777 for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
779 if ((c1 = strchr (c, ':')) == c)
785 tcode = safe_malloc (ncodes * sizeof (char *));
786 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
788 if ((c1 = strchr (c, ':')) == c)
790 tcode[i] = mutt_substrdup (c, c1);
796 /* Try each fromcode in turn */
797 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
799 if ((c1 = strchr (c, ':')) == c)
801 fcode = mutt_substrdup (c, c1);
803 ret = convert_file_to (file, fcode, ncodes, (const char **)tcode,
805 if (ret != (size_t)(-1))
817 /* There is only one fromcode */
818 ret = convert_file_to (file, fromcodes, ncodes, (const char **)tcode,
820 if (ret != (size_t)(-1))
828 for (i = 0; i < ncodes; i++)
831 FREE (tcode); /* __FREE_CHECKED__ */
837 * Analyze the contents of a file to determine which MIME encoding to use.
838 * Also set the body charset, sometimes, or not.
840 CONTENT *mutt_get_content_info (const char *fname, BODY *b)
845 char *fromcode = NULL;
853 if(b && !fname) fname = b->filename;
855 if (stat (fname, &sb) == -1)
857 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
861 if (!S_ISREG(sb.st_mode))
863 mutt_error (_("%s isn't a regular file."), fname);
867 if ((fp = fopen (fname, "r")) == NULL)
869 dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
870 fname, strerror (errno), errno));
874 info = safe_calloc (1, sizeof (CONTENT));
875 memset (&state, 0, sizeof (state));
877 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
879 char *chs = mutt_get_parameter ("charset", b->parameter);
880 char *fchs = b->use_disp ? ((AttachCharset && *AttachCharset) ?
881 AttachCharset : Charset) : Charset;
882 if (Charset && (chs || SendCharset) &&
883 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
884 &fromcode, &tocode, info) != (size_t)(-1))
888 mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
889 mutt_set_parameter ("charset", chsbuf, &b->parameter);
891 b->charset = fromcode;
899 while ((r = fread (buffer, 1, sizeof(buffer), fp)))
900 update_content_info (info, &state, buffer, r);
901 update_content_info (info, &state, 0, 0);
905 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
906 mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
907 Charset && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
913 /* Given a file with path ``s'', see if there is a registered MIME type.
914 * returns the major MIME type, and copies the subtype to ``d''. First look
915 * for ~/.mime.types, then look in a system mime.types if we can find one.
916 * The longest match is used so that we can match `ps.gz' when `gz' also
920 int mutt_lookup_mime_type (BODY *att, const char *path)
924 char buf[LONG_STRING];
925 char subtype[STRING], xtype[STRING];
927 int szf, sze, cur_sze;
935 szf = mutt_strlen (path);
937 for (count = 0 ; count < 3 ; count++)
940 * can't use strtok() because we use it in an inner loop below, so use
941 * a switch statement here instead.
946 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
949 strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
952 strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
955 dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
956 goto bye; /* shouldn't happen */
959 if ((f = fopen (buf, "r")) != NULL)
961 while (fgets (buf, sizeof (buf) - 1, f) != NULL)
963 /* weed out any comments */
964 if ((p = strchr (buf, '#')))
967 /* remove any leading space. */
971 /* position on the next field in this line */
972 if ((p = strpbrk (ct, " \t")) == NULL)
977 /* cycle through the file extensions */
978 while ((p = strtok (p, " \t\n")))
980 sze = mutt_strlen (p);
981 if ((sze > cur_sze) && (szf >= sze) &&
982 (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
983 (szf == sze || path[szf - sze - 1] == '.'))
985 /* get the content-type */
987 if ((p = strchr (ct, '/')) == NULL)
989 /* malformed line, just skip it. */
994 for (q = p; *q && !ISSPACE (*q); q++)
997 mutt_substrcpy (subtype, p, q, sizeof (subtype));
999 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1000 strfcpy (xtype, ct, sizeof (xtype));
1013 if (type != TYPEOTHER || *xtype != '\0')
1016 mutt_str_replace (&att->subtype, subtype);
1017 mutt_str_replace (&att->xtype, xtype);
1023 void mutt_message_to_7bit (BODY *a, FILE *fp)
1025 char temp[_POSIX_PATH_MAX];
1031 if (!a->filename && fp)
1033 else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1035 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1041 if (stat (a->filename, &sb) == -1)
1043 mutt_perror ("stat");
1046 a->length = sb.st_size;
1050 if (!(fpout = safe_fopen (temp, "w+")))
1052 mutt_perror ("fopen");
1056 fseeko (fpin, a->offset, 0);
1057 a->parts = mutt_parse_messageRFC822 (fpin, a);
1059 transform_to_7bit (a->parts, fpin);
1061 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1062 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1064 fputs ("MIME-Version: 1.0\n", fpout);
1065 mutt_write_mime_header (a->parts, fpout);
1066 fputc ('\n', fpout);
1067 mutt_write_mime_body (a->parts, fpout);
1079 a->encoding = ENC7BIT;
1080 a->d_filename = a->filename;
1081 if (a->filename && a->unlink)
1082 unlink (a->filename);
1083 a->filename = safe_strdup (temp);
1085 if(stat (a->filename, &sb) == -1)
1087 mutt_perror ("stat");
1090 a->length = sb.st_size;
1091 mutt_free_body (&a->parts);
1092 a->hdr->content = NULL;
1095 static void transform_to_7bit (BODY *a, FILE *fpin)
1097 char buff[_POSIX_PATH_MAX];
1101 memset (&s, 0, sizeof (s));
1102 for (; a; a = a->next)
1104 if (a->type == TYPEMULTIPART)
1106 if (a->encoding != ENC7BIT)
1107 a->encoding = ENC7BIT;
1109 transform_to_7bit (a->parts, fpin);
1111 else if (mutt_is_message_type(a->type, a->subtype))
1113 mutt_message_to_7bit (a, fpin);
1118 a->force_charset = 1;
1121 if ((s.fpout = safe_fopen (buff, "w")) == NULL)
1123 mutt_perror ("fopen");
1127 mutt_decode_attachment (a, &s);
1129 a->d_filename = a->filename;
1130 a->filename = safe_strdup (buff);
1132 if (stat (a->filename, &sb) == -1)
1134 mutt_perror ("stat");
1137 a->length = sb.st_size;
1139 mutt_update_encoding (a);
1140 if (a->encoding == ENC8BIT)
1141 a->encoding = ENCQUOTEDPRINTABLE;
1142 else if(a->encoding == ENCBINARY)
1143 a->encoding = ENCBASE64;
1148 /* determine which Content-Transfer-Encoding to use */
1149 static void mutt_set_encoding (BODY *b, CONTENT *info)
1151 char send_charset[SHORT_STRING];
1153 if (b->type == TYPETEXT)
1155 char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1156 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1157 b->encoding = ENCQUOTEDPRINTABLE;
1158 else if (info->hibin)
1159 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1161 b->encoding = ENC7BIT;
1163 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1165 if (info->lobin || info->hibin)
1167 if (option (OPTALLOW8BIT) && !info->lobin)
1168 b->encoding = ENC8BIT;
1170 mutt_message_to_7bit (b, NULL);
1173 b->encoding = ENC7BIT;
1175 else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1176 b->encoding = ENC7BIT;
1179 if (info->lobin || info->hibin || info->binary || info->linemax > 990
1180 || info->cr || (/* option (OPTENCODEFROM) && */ info->from))
1183 /* Determine which encoding is smaller */
1184 if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
1185 3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1186 b->encoding = ENCBASE64;
1188 b->encoding = ENCQUOTEDPRINTABLE;
1192 b->encoding = ENC7BIT;
1196 void mutt_stamp_attachment(BODY *a)
1198 a->stamp = time(NULL);
1201 /* Get a body's character set */
1203 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1207 if (b && b->type != TYPETEXT)
1211 p = mutt_get_parameter ("charset", b->parameter);
1214 mutt_canonical_charset (d, dlen, NONULL(p));
1216 strfcpy (d, "us-ascii", dlen);
1222 /* Assumes called from send mode where BODY->filename points to actual file */
1223 void mutt_update_encoding (BODY *a)
1226 char chsbuff[STRING];
1228 /* override noconv when it's us-ascii */
1229 if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1232 if (!a->force_charset && !a->noconv)
1233 mutt_delete_parameter ("charset", &a->parameter);
1235 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1238 mutt_set_encoding (a, info);
1239 mutt_stamp_attachment(a);
1246 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1248 char buffer[LONG_STRING];
1251 int cmflags, chflags;
1252 int pgp = WithCrypto? hdr->security : 0;
1256 if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1257 (hdr->security & ENCRYPT)) {
1258 if (!crypt_valid_passphrase(hdr->security))
1263 mutt_mktemp (buffer);
1264 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1267 body = mutt_new_body ();
1268 body->type = TYPEMESSAGE;
1269 body->subtype = safe_strdup ("rfc822");
1270 body->filename = safe_strdup (buffer);
1273 body->disposition = DISPINLINE;
1276 mutt_parse_mime_message (ctx, hdr);
1281 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1282 if (!attach_msg && option (OPTMIMEFORWDECODE))
1284 chflags |= CH_MIME | CH_TXTPLAIN;
1285 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1286 if ((WithCrypto & APPLICATION_PGP))
1288 if ((WithCrypto & APPLICATION_SMIME))
1289 pgp &= ~SMIMEENCRYPT;
1292 && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1294 if ((WithCrypto & APPLICATION_PGP)
1295 && mutt_is_multipart_encrypted (hdr->content))
1297 chflags |= CH_MIME | CH_NONEWLINE;
1298 cmflags = M_CM_DECODE_PGP;
1301 else if ((WithCrypto & APPLICATION_PGP)
1302 && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT))
1304 chflags |= CH_MIME | CH_TXTPLAIN;
1305 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1308 else if ((WithCrypto & APPLICATION_SMIME)
1309 && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT)
1311 chflags |= CH_MIME | CH_TXTPLAIN;
1312 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1313 pgp &= ~SMIMEENCRYPT;
1317 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1322 body->hdr = mutt_new_header();
1323 body->hdr->offset = 0;
1324 /* we don't need the user headers here */
1325 body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1327 body->hdr->security = pgp;
1328 mutt_update_encoding (body);
1329 body->parts = body->hdr->content;
1336 BODY *mutt_make_file_attach (const char *path)
1341 att = mutt_new_body ();
1342 att->filename = safe_strdup (path);
1344 /* Attempt to determine the appropriate content-type based on the filename
1350 if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER
1354 att->subtype = safe_strdup (buf);
1355 att->xtype = safe_strdup (xbuf);
1360 mutt_lookup_mime_type (att, path);
1364 if ((info = mutt_get_content_info (path, att)) == NULL)
1366 mutt_free_body (&att);
1372 if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
1375 * Statistically speaking, there should be more than 10% "lobin"
1376 * chars if this is really a binary file...
1378 att->type = TYPETEXT;
1379 att->subtype = safe_strdup ("plain");
1383 att->type = TYPEAPPLICATION;
1384 att->subtype = safe_strdup ("octet-stream");
1388 mutt_update_encoding (att);
1392 static int get_toplevel_encoding (BODY *a)
1396 for (; a; a = a->next)
1398 if (a->encoding == ENCBINARY)
1400 else if (a->encoding == ENC8BIT)
1407 /* check for duplicate boundary. return 1 if duplicate */
1408 static int mutt_check_boundary (const char* boundary, BODY *b)
1412 if (b->parts && mutt_check_boundary (boundary, b->parts))
1415 if (b->next && mutt_check_boundary (boundary, b->next))
1418 if ((p = mutt_get_parameter ("boundary", b->parameter))
1419 && !ascii_strcmp (p, boundary))
1424 BODY *mutt_make_multipart (BODY *b)
1428 new = mutt_new_body ();
1429 new->type = TYPEMULTIPART;
1430 new->subtype = safe_strdup ("mixed");
1431 new->encoding = get_toplevel_encoding (b);
1434 mutt_generate_boundary (&new->parameter);
1435 if (mutt_check_boundary (mutt_get_parameter ("boundary", new->parameter),
1437 mutt_delete_parameter ("boundary", &new->parameter);
1439 while (!mutt_get_parameter ("boundary", new->parameter));
1441 new->disposition = DISPINLINE;
1447 /* remove the multipart body if it exists */
1448 BODY *mutt_remove_multipart (BODY *b)
1457 mutt_free_body (&t);
1462 char *mutt_make_date (char *s, size_t len)
1464 time_t t = time (NULL);
1465 struct tm *l = localtime (&t);
1466 time_t tz = mutt_local_tz (t);
1470 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1471 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1472 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1473 (int) tz / 60, (int) abs (tz) % 60);
1477 /* wrapper around mutt_write_address() so we can handle very large
1478 recipient lists without needing a huge temporary buffer in memory */
1479 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1482 char buf[LONG_STRING];
1491 rfc822_write_address (buf, sizeof (buf), adr, display);
1492 len = mutt_strlen (buf);
1493 if (count && linelen + len > 74)
1496 linelen = len + 8; /* tab is usually about 8 spaces... */
1500 if (count && adr->mailbox)
1509 if (!adr->group && adr->next && adr->next->mailbox)
1520 /* arbitrary number of elements to grow the array by */
1525 /* need to write the list in reverse because they are stored in reverse order
1526 * when parsed to speed up threading
1528 static void write_references (LIST *r, FILE *f)
1531 int refcnt = 0, refmax = 0;
1533 for ( ; (TrimRef == 0 || refcnt < TrimRef) && r ; r = r->next)
1535 if (refcnt == refmax)
1536 safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1540 while (refcnt-- > 0)
1543 fputs (ref[refcnt]->data, f);
1550 static void foldingstrfcpy (char *d, const char *s, int n)
1552 while (--n >= 0 && *s)
1557 if (!(d[0] == '\n' && (*s == '\t' || *s == ' ')))
1563 int mutt_write_one_header (FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen)
1568 char buf [HUGE_STRING];
1569 wchar_t w = (wchar_t) -1;
1570 wchar_t last = (wchar_t) '\n';
1574 int in_encoded_word = 0;
1581 if (fprintf (fp, "%s%s: ", NONULL (pfx), tag) < 0)
1584 col = mutt_strlen (tag) + 2 + mutt_strlen (pfx);
1596 if (fputs (NONULL (pfx), fp) == EOF)
1598 col = mutt_strlen (pfx);
1600 /* Space padding, but only if necessary */
1601 if (!first && *cp != '\t' && *cp != ' ')
1603 if (fputc ('\t', fp) == EOF)
1605 col += 8 - (col % 8);
1617 * i is our running pointer, and always points to the *beginning* of an mb character.
1618 * k is the pointer to the beginning of the last white-space character we have seen.
1619 * n is the pointer to the beginning of the first character after white-space.
1624 for (i = 0, k = 0, l = 0, n = 0; i + MB_CUR_MAX < sizeof (buf)
1625 && cp[i] != '\0' && (col < wraplen || in_encoded_word);
1629 /* Brief look at the last character we had... */
1630 if (iswspace (last))
1632 /* ... and if the next thing is an encoded word ... */
1633 if (strncmp (&cp[i], "=?", 2) == 0)
1634 in_encoded_word = 1;
1636 in_encoded_word = 0;
1639 /* If there is a line break in the header, honor it. */
1642 in_encoded_word = 0;
1644 if (cp[i+1] != ' ' && cp[i+1] != '\t')
1647 if (first || !wrapped)
1657 /* Eat the current character; cannot be '\0' */
1659 if ((l = mbtowc (&w, &cp[i], MB_CUR_MAX)) <= 0)
1661 dprint (1, (debugfile, "mutt_write_one_header: encoutered bad multi-byte character at %d.\n", i));
1662 l = 1; /* if bad, move on by one character */
1667 if (wcwidth (w) >= 0)
1671 (!k || col <= wraplen))
1680 * As long as we haven't seen whitespace, we advance at least by one character.
1686 /* If no whitespace was found, copy as much as we can */
1690 /* If we're done, we're done. */
1694 if (k < i) /* we had to go back to an earlier wrapping point */
1698 foldingstrfcpy (buf + 1, cp + 1, k - 1);
1700 if (fprintf (fp, "%s\n", buf) < 0)
1709 if ((l = mbtowc (&w, cp, MB_CUR_MAX)) > 0 && iswspace (w))
1718 if (fputc ('\n', fp) == EOF)
1727 /* Note: all RFC2047 encoding should be done outside of this routine, except
1728 * for the "real name." This will allow this routine to be used more than
1729 * once, if necessary.
1731 * Likewise, all IDN processing should happen outside of this routine.
1733 * mode == 1 => "lite" mode (used for edit_hdrs)
1734 * mode == 0 => normal mode. write full header + MIME headers
1735 * mode == -1 => write just the envelope info (used for postponing messages)
1737 * privacy != 0 => will omit any headers which may identify the user.
1738 * Output generated is suitable for being sent through
1739 * anonymous remailer chains.
1745 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
1746 int mode, int privacy)
1748 char buffer[LONG_STRING];
1750 LIST *tmp = env->userhdrs;
1751 int has_agent = 0; /* user defined user-agent header field exists */
1753 if (mode == 0 && !privacy)
1754 fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
1756 /* OPTUSEFROM is not consulted here so that we can still write a From:
1757 * field if the user sets it with the `my_hdr' command
1759 if (env->from && !privacy)
1762 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1763 fprintf (fp, "From: %s\n", buffer);
1766 if (env->sender && !privacy)
1769 rfc822_write_address (buffer, sizeof (buffer), env->sender, 0);
1770 fprintf (fp, "Sender: %s\n", buffer);
1776 mutt_write_address_list (env->to, fp, 4, 0);
1779 fputs ("To: \n", fp);
1784 mutt_write_address_list (env->cc, fp, 4, 0);
1787 fputs ("Cc: \n", fp);
1791 if(mode != 0 || option(OPTWRITEBCC))
1793 fputs ("Bcc: ", fp);
1794 mutt_write_address_list (env->bcc, fp, 5, 0);
1798 fputs ("Bcc: \n", fp);
1801 mutt_write_one_header (fp, "Subject", env->subject, NULL, 0);
1803 fputs ("Subject: \n", fp);
1805 /* save message id if the user has set it */
1806 if (env->message_id && !privacy)
1807 mutt_write_one_header (fp, "Message-ID", env->message_id, NULL, 0);
1811 fputs ("Reply-To: ", fp);
1812 mutt_write_address_list (env->reply_to, fp, 10, 0);
1815 fputs ("Reply-To: \n", fp);
1817 if (env->mail_followup_to)
1819 fputs ("Mail-Followup-To: ", fp);
1820 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1825 if (env->references)
1827 fputs ("References:", fp);
1828 write_references (env->references, fp);
1832 /* Add the MIME headers */
1833 fputs ("MIME-Version: 1.0\n", fp);
1834 mutt_write_mime_header (attach, fp);
1837 if (env->in_reply_to)
1839 fputs ("In-Reply-To:", fp);
1840 write_references (env->in_reply_to, fp);
1844 /* Add any user defined headers */
1845 for (; tmp; tmp = tmp->next)
1847 if ((p = strchr (tmp->data, ':')))
1857 continue; /* don't emit empty fields. */
1860 /* check to see if the user has overridden the user-agent field */
1861 if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
1871 mutt_write_one_header (fp, tmp->data, p, NULL, 0);
1876 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent)
1878 /* Add a vanity header */
1879 fprintf (fp, "User-Agent: Mutt/%s (%s)\n", MUTT_VERSION, ReleaseDate);
1882 return (ferror (fp) == 0 ? 0 : -1);
1885 static void encode_headers (LIST *h)
1891 for (; h; h = h->next)
1893 if (!(p = strchr (h->data, ':')))
1898 tmp = safe_strdup (p);
1903 rfc2047_encode_string (&tmp);
1904 safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
1906 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
1912 const char *mutt_fqdn(short may_hide_host)
1916 if(Fqdn && Fqdn[0] != '@')
1920 if(may_hide_host && option(OPTHIDDENHOST))
1922 if((p = strchr(Fqdn, '.')))
1925 /* sanity check: don't hide the host if
1926 * the fqdn is something like detebe.org.
1929 if(!p || !(q = strchr(p, '.')))
1937 char *mutt_gen_msgid (void)
1939 char buf[SHORT_STRING];
1946 if(!(fqdn = mutt_fqdn(0)))
1947 fqdn = NONULL(Hostname);
1949 snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.G%c%u@%s>",
1950 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
1951 tm->tm_min, tm->tm_sec, MsgIdPfx, (unsigned int)getpid (), fqdn);
1952 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1953 return (safe_strdup (buf));
1956 static RETSIGTYPE alarm_handler (int sig)
1961 /* invoke sendmail in a subshell
1962 path (in) path to program to execute
1963 args (in) arguments to pass to program
1964 msg (in) temp file containing message to send
1965 tempfile (out) if sendmail is put in the background, this points
1966 to the temporary file containing the stdout of the
1967 child process. If it is NULL, stderr and stdout
1968 are not redirected. */
1970 send_msg (const char *path, char **args, const char *msg, char **tempfile)
1976 mutt_block_signals_system ();
1979 /* we also don't want to be stopped right now */
1980 sigaddset (&set, SIGTSTP);
1981 sigprocmask (SIG_BLOCK, &set, NULL);
1983 if (SendmailWait >= 0 && tempfile)
1985 char tmp[_POSIX_PATH_MAX];
1988 *tempfile = safe_strdup (tmp);
1991 if ((pid = fork ()) == 0)
1993 struct sigaction act, oldalrm;
1995 /* save parent's ID before setsid() */
1998 /* we want the delivery to continue even after the main process dies,
1999 * so we put ourselves into another session right away
2003 /* next we close all open files */
2005 #if defined(OPEN_MAX)
2006 for (fd = tempfile ? 1 : 3; fd < OPEN_MAX; fd++)
2008 #elif defined(_POSIX_OPEN_MAX)
2009 for (fd = tempfile ? 1 : 3; fd < _POSIX_OPEN_MAX; fd++)
2019 /* now the second fork() */
2020 if ((pid = fork ()) == 0)
2022 /* "msg" will be opened as stdin */
2023 if (open (msg, O_RDONLY, 0) < 0)
2030 if (SendmailWait >= 0 && tempfile)
2032 /* *tempfile will be opened as stdout */
2033 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2035 /* redirect stderr to *tempfile too */
2041 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
2043 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
2047 execvp (path, args);
2054 FREE (tempfile); /* __FREE_CHECKED__ */
2058 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
2059 * SendmailWait = 0: wait forever
2060 * SendmailWait < 0: don't wait
2062 if (SendmailWait > 0)
2065 act.sa_handler = alarm_handler;
2067 /* need to make sure waitpid() is interrupted on SIGALRM */
2068 act.sa_flags = SA_INTERRUPT;
2072 sigemptyset (&act.sa_mask);
2073 sigaction (SIGALRM, &act, &oldalrm);
2074 alarm (SendmailWait);
2076 else if (SendmailWait < 0)
2077 _exit (0xff & EX_OK);
2079 if (waitpid (pid, &st, 0) > 0)
2081 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
2082 if (SendmailWait && st == (0xff & EX_OK) && tempfile)
2084 unlink (*tempfile); /* no longer needed */
2085 FREE (tempfile); /* __FREE_CHECKED__ */
2090 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
2092 if (SendmailWait > 0 && tempfile)
2095 FREE (tempfile); /* __FREE_CHECKED__ */
2099 /* reset alarm; not really needed, but... */
2101 sigaction (SIGALRM, &oldalrm, NULL);
2103 if (kill (ppid, 0) == -1 && errno == ESRCH && tempfile)
2105 /* the parent is already dead */
2107 FREE (tempfile); /* __FREE_CHECKED__ */
2113 sigprocmask (SIG_UNBLOCK, &set, NULL);
2115 if (pid != -1 && waitpid (pid, &st, 0) > 0)
2116 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
2118 st = S_ERR; /* error */
2120 mutt_unblock_signals_system (1);
2126 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
2128 for (; addr; addr = addr->next)
2130 /* weed out group mailboxes, since those are for display only */
2131 if (addr->mailbox && !addr->group)
2133 if (*argslen == *argsmax)
2134 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2135 args[(*argslen)++] = addr->mailbox;
2142 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2144 if (*argslen == *argsmax)
2145 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2146 args[(*argslen)++] = s;
2151 mutt_invoke_sendmail (ADDRESS *from, /* the sender */
2152 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2153 const char *msg, /* file containing message */
2154 int eightbit) /* message contains 8bit chars */
2156 char *ps = NULL, *path = NULL, *s = safe_strdup (Sendmail), *childout = NULL;
2158 size_t argslen = 0, argsmax = 0;
2163 while ((ps = strtok (ps, " ")))
2165 if (argslen == argsmax)
2166 safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2169 args[argslen++] = ps;
2172 path = safe_strdup (ps);
2173 ps = strrchr (ps, '/');
2178 args[argslen++] = ps;
2184 if (eightbit && option (OPTUSE8BITMIME))
2185 args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2187 if (option (OPTENVFROM))
2191 args = add_option (args, &argslen, &argsmax, "-f");
2192 args = add_args (args, &argslen, &argsmax, EnvFrom);
2194 else if (from && !from->next)
2196 args = add_option (args, &argslen, &argsmax, "-f");
2197 args = add_args (args, &argslen, &argsmax, from);
2203 args = add_option (args, &argslen, &argsmax, "-N");
2204 args = add_option (args, &argslen, &argsmax, DsnNotify);
2208 args = add_option (args, &argslen, &argsmax, "-R");
2209 args = add_option (args, &argslen, &argsmax, DsnReturn);
2211 args = add_option (args, &argslen, &argsmax, "--");
2212 args = add_args (args, &argslen, &argsmax, to);
2213 args = add_args (args, &argslen, &argsmax, cc);
2214 args = add_args (args, &argslen, &argsmax, bcc);
2216 if (argslen == argsmax)
2217 safe_realloc (&args, sizeof (char *) * (++argsmax));
2219 args[argslen++] = NULL;
2221 if ((i = send_msg (path, args, msg, option(OPTNOCURSES) ? NULL : &childout)) != (EX_OK & 0xff))
2225 const char *e = mutt_strsysexit (i);
2227 e = mutt_strsysexit (i);
2228 mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2233 if (stat (childout, &st) == 0 && st.st_size > 0)
2234 mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2246 if (i == (EX_OK & 0xff))
2248 else if (i == S_BKG)
2255 /* appends string 'b' to string 'a', and returns the pointer to the new
2257 char *mutt_append_string (char *a, const char *b)
2259 size_t la = mutt_strlen (a);
2260 safe_realloc (&a, la + mutt_strlen (b) + 1);
2261 strcpy (a + la, b); /* __STRCPY_CHECKED__ */
2265 /* returns 1 if char `c' needs to be quoted to protect from shell
2266 interpretation when executing commands in a subshell */
2267 #define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
2269 /* returns 1 if string `s' contains characters which could cause problems
2270 when used on a command line to execute a command */
2271 int mutt_needs_quote (const char *s)
2275 if (INVALID_CHAR (*s))
2282 /* Quote a string to prevent shell escapes when this string is used on the
2283 command line to send mail. */
2284 char *mutt_quote_string (const char *s)
2289 rlen = mutt_strlen (s) + 3;
2290 pr = r = (char *) safe_malloc (rlen);
2294 if (INVALID_CHAR (*s))
2297 safe_realloc (&r, ++rlen);
2308 /* For postponing (!final) do the necessary encodings only */
2309 void mutt_prepare_envelope (ENVELOPE *env, int final)
2311 char buffer[LONG_STRING];
2315 if (env->bcc && !(env->to || env->cc))
2317 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2318 * recipients if there is no To: or Cc: field, so attempt to suppress
2319 * it by using an empty To: field.
2321 env->to = rfc822_new_address ();
2323 env->to->next = rfc822_new_address ();
2326 rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2329 env->to->mailbox = safe_strdup (buffer);
2332 mutt_set_followup_to (env);
2334 if (!env->message_id)
2335 env->message_id = mutt_gen_msgid ();
2338 /* Take care of 8-bit => 7-bit conversion. */
2339 rfc2047_encode_adrlist (env->to, "To");
2340 rfc2047_encode_adrlist (env->cc, "Cc");
2341 rfc2047_encode_adrlist (env->bcc, "Bcc");
2342 rfc2047_encode_adrlist (env->from, "From");
2343 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2344 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2345 rfc2047_encode_string (&env->x_label);
2349 rfc2047_encode_string (&env->subject);
2351 encode_headers (env->userhdrs);
2354 void mutt_unprepare_envelope (ENVELOPE *env)
2358 for (item = env->userhdrs; item; item = item->next)
2359 rfc2047_decode (&item->data);
2361 rfc822_free_address (&env->mail_followup_to);
2363 /* back conversions */
2364 rfc2047_decode_adrlist (env->to);
2365 rfc2047_decode_adrlist (env->cc);
2366 rfc2047_decode_adrlist (env->bcc);
2367 rfc2047_decode_adrlist (env->from);
2368 rfc2047_decode_adrlist (env->reply_to);
2369 rfc2047_decode (&env->subject);
2370 rfc2047_decode (&env->x_label);
2373 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2378 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2379 MESSAGE *msg = NULL;
2383 /* Try to bounce each message out, aborting if we get any failures. */
2384 for (i=0; i<Context->msgcount; i++)
2385 if (Context->hdrs[i]->tagged)
2386 ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2390 /* If we failed to open a message, return with error */
2391 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2394 if (!fp) fp = msg->fp;
2396 mutt_mktemp (tempfile);
2397 if ((f = safe_fopen (tempfile, "w")) != NULL)
2399 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2401 if (!option (OPTBOUNCEDELIVERED))
2402 ch_flags |= CH_WEED_DELIVERED;
2404 fseeko (fp, h->offset, 0);
2405 fprintf (f, "Resent-From: %s", resent_from);
2406 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2407 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2408 fputs ("Resent-To: ", f);
2409 mutt_write_address_list (to, f, 11, 0);
2410 mutt_copy_header (fp, h, f, ch_flags, NULL);
2412 mutt_copy_bytes (fp, f, h->content->length);
2417 ret = mutt_smtp_send (env_from, to, NULL, NULL, tempfile,
2418 h->content->encoding == ENC8BIT);
2420 #endif /* USE_SMTP */
2421 ret = mutt_invoke_sendmail (env_from, to, NULL, NULL, tempfile,
2422 h->content->encoding == ENC8BIT);
2426 mx_close_message (&msg);
2431 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2434 const char *fqdn = mutt_fqdn (1);
2435 char resent_from[STRING];
2439 resent_from[0] = '\0';
2440 from = mutt_default_from ();
2443 rfc822_qualify (from, fqdn);
2445 rfc2047_encode_adrlist (from, "Resent-From");
2446 if (mutt_addrlist_to_idna (from, &err))
2448 mutt_error (_("Bad IDN %s while preparing resent-from."),
2452 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2454 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2456 rfc822_free_address (&from);
2462 /* given a list of addresses, return a list of unique addresses */
2463 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2465 ADDRESS *top = addr;
2466 ADDRESS **last = ⊤
2472 for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next)
2474 if (tmp->mailbox && addr->mailbox &&
2475 !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2484 dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
2490 rfc822_free_address(&addr);
2504 static void set_noconv_flags (BODY *b, short flag)
2506 for(; b; b = b->next)
2508 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2509 set_noconv_flags (b->parts, flag);
2510 else if (b->type == TYPETEXT && b->noconv)
2513 mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2515 mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2520 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc)
2524 char tempfile[_POSIX_PATH_MAX];
2525 FILE *tempfp = NULL;
2529 set_noconv_flags (hdr->content, 1);
2531 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
2533 dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2538 /* We need to add a Content-Length field to avoid problems where a line in
2539 * the message body begins with "From "
2541 if (f.magic == M_MMDF || f.magic == M_MBOX)
2543 mutt_mktemp (tempfile);
2544 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2546 mutt_perror (tempfile);
2547 mx_close_mailbox (&f, NULL);
2552 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2553 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
2555 mx_close_mailbox (&f, NULL);
2559 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2560 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2562 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 0);
2564 /* (postponment) if this was a reply of some sort, <msgid> contians the
2565 * Message-ID: of message replied to. Save it using a special X-Mutt-
2566 * header so it can be picked up if the message is recalled at a later
2567 * point in time. This will allow the message to be marked as replied if
2568 * the same mailbox is still open.
2571 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2573 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2574 * it can be picked up when the message is recalled
2577 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2578 fprintf (msg->fp, "Status: RO\n");
2582 /* (postponment) if the mail is to be signed or encrypted, save this info */
2583 if ((WithCrypto & APPLICATION_PGP)
2584 && post && (hdr->security & APPLICATION_PGP))
2586 fputs ("X-Mutt-PGP: ", msg->fp);
2587 if (hdr->security & ENCRYPT)
2588 fputc ('E', msg->fp);
2589 if (hdr->security & SIGN)
2591 fputc ('S', msg->fp);
2592 if (PgpSignAs && *PgpSignAs)
2593 fprintf (msg->fp, "<%s>", PgpSignAs);
2595 if (hdr->security & INLINE)
2596 fputc ('I', msg->fp);
2597 fputc ('\n', msg->fp);
2600 /* (postponment) if the mail is to be signed or encrypted, save this info */
2601 if ((WithCrypto & APPLICATION_SMIME)
2602 && post && (hdr->security & APPLICATION_SMIME))
2604 fputs ("X-Mutt-SMIME: ", msg->fp);
2605 if (hdr->security & ENCRYPT) {
2606 fputc ('E', msg->fp);
2607 if (SmimeCryptAlg && *SmimeCryptAlg)
2608 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2610 if (hdr->security & SIGN) {
2611 fputc ('S', msg->fp);
2612 if (SmimeDefaultKey && *SmimeDefaultKey)
2613 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2615 if (hdr->security & INLINE)
2616 fputc ('I', msg->fp);
2617 fputc ('\n', msg->fp);
2621 /* (postponement) if the mail is to be sent through a mixmaster
2622 * chain, save that information
2625 if (post && hdr->chain && hdr->chain)
2629 fputs ("X-Mutt-Mix:", msg->fp);
2630 for (p = hdr->chain; p; p = p->next)
2631 fprintf (msg->fp, " %s", (char *) p->data);
2633 fputc ('\n', msg->fp);
2639 char sasha[LONG_STRING];
2642 mutt_write_mime_body (hdr->content, tempfp);
2644 /* make sure the last line ends with a newline. Emacs doesn't ensure
2645 * this will happen, and it can cause problems parsing the mailbox
2648 fseek (tempfp, -1, 2);
2649 if (fgetc (tempfp) != '\n')
2651 fseek (tempfp, 0, 2);
2652 fputc ('\n', tempfp);
2656 if (ferror (tempfp))
2658 dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2661 mx_commit_message (msg, &f); /* XXX - really? */
2662 mx_close_message (&msg);
2663 mx_close_mailbox (&f, NULL);
2667 /* count the number of lines */
2669 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2671 fprintf (msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftell (tempfp));
2672 fprintf (msg->fp, "Lines: %d\n\n", lines);
2674 /* copy the body and clean up */
2676 r = mutt_copy_stream (tempfp, msg->fp);
2677 if (fclose (tempfp) != 0)
2679 /* if there was an error, leave the temp version */
2685 fputc ('\n', msg->fp); /* finish off the header */
2686 r = mutt_write_mime_body (hdr->content, msg->fp);
2689 if (mx_commit_message (msg, &f) != 0)
2691 mx_close_message (&msg);
2692 mx_close_mailbox (&f, NULL);
2695 set_noconv_flags (hdr->content, 0);