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"
49 #ifdef HAVE_SYSEXITS_H
51 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
55 /* If you are debugging this file, comment out the following line. */
64 extern char RFC822Specials[];
66 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
69 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
70 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
71 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
72 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
76 static char MsgIdPfx = 'A';
78 static void transform_to_7bit (BODY *a, FILE *fpin);
80 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
83 char line[77], savechar;
85 while ((c = fgetconv (fc)) != EOF)
87 /* Wrap the line if needed. */
88 if (linelen == 76 && ((istext && c != '\n') || !istext))
90 /* If the last character is "quoted", then be sure to move all three
91 * characters to the next line. Otherwise, just move the last
94 if (line[linelen-3] == '=')
101 line[1] = line[linelen-2];
102 line[2] = line[linelen-1];
107 savechar = line[linelen-1];
108 line[linelen-1] = '=';
117 /* Escape lines that begin with/only contain "the message separator". */
118 if (linelen == 4 && !mutt_strncmp ("From", line, 4))
120 strfcpy (line, "=46rom", sizeof (line));
123 else if (linelen == 4 && !mutt_strncmp ("from", line, 4))
125 strfcpy (line, "=66rom", sizeof (line));
128 else if (linelen == 1 && line[0] == '.')
130 strfcpy (line, "=2E", sizeof (line));
135 if (c == '\n' && istext)
137 /* Check to make sure there is no trailing space on this line. */
138 if (linelen > 0 && (line[linelen-1] == ' ' || line[linelen-1] == '\t'))
142 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
147 int savechar = line[linelen-1];
149 line[linelen-1] = '=';
152 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
163 else if (c != 9 && (c < 32 || c > 126 || c == '='))
165 /* Check to make sure there is enough room for the quoted character.
166 * If not, wrap to the next line.
170 line[linelen++] = '=';
176 sprintf (line+linelen,"=%2.2X", (unsigned char) c);
181 /* Don't worry about wrapping the line here. That will happen during
182 * the next iteration when I'll also know what the next character is.
188 /* Take care of anything left in the buffer */
191 if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
193 /* take care of trailing whitespace */
195 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
198 savechar = line[linelen-1];
199 line[linelen-1] = '=';
203 sprintf (line, "=%2.2X", (unsigned char) savechar);
212 static char b64_buffer[3];
213 static short b64_num;
214 static short b64_linelen;
216 static void b64_flush(FILE *fout)
223 if(b64_linelen >= 72)
229 for(i = b64_num; i < 3; i++)
230 b64_buffer[i] = '\0';
232 fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
234 fputc(B64Chars[((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf) ], fout);
239 fputc(B64Chars[((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3) ], fout);
243 fputc(B64Chars[b64_buffer[2] & 0x3f], fout);
248 while(b64_linelen % 4)
258 static void b64_putc(char c, FILE *fout)
263 b64_buffer[b64_num++] = c;
267 static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
271 b64_num = b64_linelen = 0;
273 while ((ch = fgetconv (fc)) != EOF)
275 if (istext && ch == '\n' && ch1 != '\r')
276 b64_putc('\r', fout);
284 static void encode_8bit (FGETCONV *fc, FILE *fout, int istext)
288 while ((ch = fgetconv (fc)) != EOF)
293 int mutt_write_mime_header (BODY *a, FILE *f)
303 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
307 len = 25 + mutt_strlen (a->subtype); /* approximate len. of content-type */
309 for(p = a->parameter; p; p = p->next)
319 tmp = safe_strdup (p->value);
320 encode = rfc2231_encode_string (&tmp);
321 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
323 /* Dirty hack to make messages readable by Outlook Express
324 * for the Mac: force quotes around the boundary parameter
325 * even when they aren't needed.
328 if (!ascii_strcasecmp (p->attribute, "boundary") && !strcmp (buffer, tmp))
329 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
333 tmplen = mutt_strlen (buffer) + mutt_strlen (p->attribute) + 1;
335 if (len + tmplen + 2 > 76)
346 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
354 fprintf(f, "Content-Description: %s\n", a->description);
356 if (a->disposition != DISPNONE)
358 const char *dispstr[] = {
364 if (a->disposition < sizeof(dispstr)/sizeof(char*))
366 fprintf (f, "Content-Disposition: %s", dispstr[a->disposition]);
370 if (!(fn = a->d_filename))
377 /* Strip off the leading path... */
378 if ((t = strrchr (fn, '/')))
384 tmp = safe_strdup (t);
385 encode = rfc2231_encode_string (&tmp);
386 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
388 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
396 dprint(1, (debugfile, "ERROR: invalid content-disposition %d\n", a->disposition));
400 if (a->encoding != ENC7BIT)
401 fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
403 /* Do NOT add the terminator here!!! */
404 return (ferror (f) ? -1 : 0);
407 # define write_as_text_part(a) (mutt_is_text_part(a) \
408 || ((WithCrypto & APPLICATION_PGP)\
409 && mutt_is_application_pgp(a)))
411 int mutt_write_mime_body (BODY *a, FILE *f)
413 char *p, boundary[SHORT_STRING];
414 char send_charset[SHORT_STRING];
419 if (a->type == TYPEMULTIPART)
421 /* First, find the boundary to use */
422 if (!(p = mutt_get_parameter ("boundary", a->parameter)))
424 dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
425 mutt_error _("No boundary parameter found! [report this error]");
428 strfcpy (boundary, p, sizeof (boundary));
430 for (t = a->parts; t ; t = t->next)
432 fprintf (f, "\n--%s\n", boundary);
433 if (mutt_write_mime_header (t, f) == -1)
436 if (mutt_write_mime_body (t, f) == -1)
439 fprintf (f, "\n--%s--\n", boundary);
440 return (ferror (f) ? -1 : 0);
443 /* This is pretty gross, but it's the best solution for now... */
444 if ((WithCrypto & APPLICATION_PGP)
445 && a->type == TYPEAPPLICATION
446 && mutt_strcmp (a->subtype, "pgp-encrypted") == 0)
448 fputs ("Version: 1\n", f);
452 if ((fpin = fopen (a->filename, "r")) == NULL)
454 dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
455 mutt_error (_("%s no longer exists!"), a->filename);
459 if (a->type == TYPETEXT && (!a->noconv))
460 fc = fgetconv_open (fpin, a->charset,
461 mutt_get_body_charset (send_charset, sizeof (send_charset), a),
464 fc = fgetconv_open (fpin, 0, 0, 0);
466 if (a->encoding == ENCQUOTEDPRINTABLE)
467 encode_quoted (fc, f, write_as_text_part (a));
468 else if (a->encoding == ENCBASE64)
469 encode_base64 (fc, f, write_as_text_part (a));
470 else if (a->type == TYPETEXT && (!a->noconv))
471 encode_8bit (fc, f, write_as_text_part (a));
473 mutt_copy_stream (fpin, f);
475 fgetconv_close (&fc);
478 return (ferror (f) ? -1 : 0);
481 #undef write_as_text_part
483 #define BOUNDARYLEN 16
484 void mutt_generate_boundary (PARAMETER **parm)
486 char rs[BOUNDARYLEN + 1];
491 for (i=0;i<BOUNDARYLEN;i++)
492 *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
495 mutt_set_parameter ("boundary", rs, parm);
509 static void update_content_info (CONTENT *info, CONTENT_STATE *s, char *d, size_t dlen)
512 int whitespace = s->whitespace;
514 int linelen = s->linelen;
515 int was_cr = s->was_cr;
517 if (!d) /* This signals EOF */
521 if (linelen > info->linemax)
522 info->linemax = linelen;
527 for (; dlen; d++, dlen--)
540 if (whitespace) info->space = 1;
541 if (dot) info->dot = 1;
542 if (linelen > info->linemax) info->linemax = linelen;
554 if (whitespace) info->space = 1;
555 if (dot) info->dot = 1;
556 if (linelen > info->linemax) info->linemax = linelen;
570 else if (ch == '\t' || ch == '\f')
575 else if (ch < 32 || ch == 127)
581 if ((ch == 'F') || (ch == 'f'))
592 if (linelen == 2 && ch != 'r') from = 0;
593 else if (linelen == 3 && ch != 'o') from = 0;
594 else if (linelen == 4)
596 if (ch == 'm') info->from = 1;
600 if (ch == ' ') whitespace++;
604 if (linelen > 1) dot = 0;
605 if (ch != ' ' && ch != '\t') whitespace = 0;
609 s->whitespace = whitespace;
611 s->linelen = linelen;
616 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
617 #define BUGGY_ICONV 1
620 * Find the best charset conversion of the file from fromcode into one
621 * of the tocodes. If successful, set *tocode and CONTENT *info and
622 * return the number of characters converted inexactly. If no
623 * conversion was possible, return -1.
625 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
626 * which would otherwise prevent us from knowing the number of inexact
627 * conversions. Where the candidate target charset is UTF-8 we avoid
628 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
629 * fails with some libraries.
631 * We assume that the output from iconv is never more than 4 times as
632 * long as the input for any pair of charsets we might be interested
635 static size_t convert_file_to (FILE *file, const char *fromcode,
636 int ncodes, const char **tocodes,
637 int *tocode, CONTENT *info)
641 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
642 ICONV_CONST char *ib, *ub;
644 size_t ibl, obl, ubl, ubl1, n, ret;
647 CONTENT_STATE *states;
650 cd1 = mutt_iconv_open ("utf-8", fromcode, 0);
651 if (cd1 == (iconv_t)(-1))
654 cd = safe_calloc (ncodes, sizeof (iconv_t));
655 score = safe_calloc (ncodes, sizeof (size_t));
656 states = safe_calloc (ncodes, sizeof (CONTENT_STATE));
657 infos = safe_calloc (ncodes, sizeof (CONTENT));
659 for (i = 0; i < ncodes; i++)
660 if (ascii_strcasecmp (tocodes[i], "utf-8"))
661 cd[i] = mutt_iconv_open (tocodes[i], "utf-8", 0);
663 /* Special case for conversion to UTF-8 */
664 cd[i] = (iconv_t)(-1), score[i] = (size_t)(-1);
671 /* Try to fill input buffer */
672 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
675 /* Convert to UTF-8 */
677 ob = bufu, obl = sizeof (bufu);
678 n = iconv (cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
679 assert (n == (size_t)(-1) || !n || ICONV_NONTRANS);
680 if (n == (size_t)(-1) &&
681 ((errno != EINVAL && errno != E2BIG) || ib == bufi))
683 assert (errno == EILSEQ ||
684 (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
690 /* Convert from UTF-8 */
691 for (i = 0; i < ncodes; i++)
692 if (cd[i] != (iconv_t)(-1) && score[i] != (size_t)(-1))
694 ub = bufu, ubl = ubl1;
695 ob = bufo, obl = sizeof (bufo);
696 n = iconv (cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
697 if (n == (size_t)(-1))
699 assert (errno == E2BIG ||
700 (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
701 score[i] = (size_t)(-1);
706 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
709 else if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
710 /* Special case for conversion to UTF-8 */
711 update_content_info (&infos[i], &states[i], bufu, ubl1);
714 /* Save unused input */
715 memmove (bufi, ib, ibl);
716 else if (!ubl1 && ib < bufi + sizeof (bufi))
725 /* Find best score */
727 for (i = 0; i < ncodes; i++)
729 if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
731 /* Special case for conversion to UTF-8 */
736 else if (cd[i] == (iconv_t)(-1) || score[i] == (size_t)(-1))
738 else if (ret == (size_t)(-1) || score[i] < ret)
746 if (ret != (size_t)(-1))
748 memcpy (info, &infos[*tocode], sizeof(CONTENT));
749 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
753 for (i = 0; i < ncodes; i++)
754 if (cd[i] != (iconv_t)(-1))
766 #endif /* !HAVE_ICONV */
770 * Find the first of the fromcodes that gives a valid conversion and
771 * the best charset conversion of the file into one of the tocodes. If
772 * successful, set *fromcode and *tocode to dynamically allocated
773 * strings, set CONTENT *info, and return the number of characters
774 * converted inexactly. If no conversion was possible, return -1.
776 * Both fromcodes and tocodes may be colon-separated lists of charsets.
777 * However, if fromcode is zero then fromcodes is assumed to be the
778 * name of a single charset even if it contains a colon.
780 static size_t convert_file_from_to (FILE *file,
781 const char *fromcodes, const char *tocodes,
782 char **fromcode, char **tocode, CONTENT *info)
790 /* Count the tocodes */
792 for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
794 if ((c1 = strchr (c, ':')) == c)
800 tcode = safe_malloc (ncodes * sizeof (char *));
801 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
803 if ((c1 = strchr (c, ':')) == c)
805 tcode[i] = mutt_substrdup (c, c1);
811 /* Try each fromcode in turn */
812 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
814 if ((c1 = strchr (c, ':')) == c)
816 fcode = mutt_substrdup (c, c1);
818 ret = convert_file_to (file, fcode, ncodes, (const char **)tcode,
820 if (ret != (size_t)(-1))
832 /* There is only one fromcode */
833 ret = convert_file_to (file, fromcodes, ncodes, (const char **)tcode,
835 if (ret != (size_t)(-1))
843 for (i = 0; i < ncodes; i++)
852 * Analyze the contents of a file to determine which MIME encoding to use.
853 * Also set the body charset, sometimes, or not.
855 CONTENT *mutt_get_content_info (const char *fname, BODY *b)
860 char *fromcode = NULL;
868 if(b && !fname) fname = b->filename;
870 if (stat (fname, &sb) == -1)
872 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
876 if (!S_ISREG(sb.st_mode))
878 mutt_error (_("%s isn't a regular file."), fname);
882 if ((fp = fopen (fname, "r")) == NULL)
884 dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
885 fname, strerror (errno), errno));
889 info = safe_calloc (1, sizeof (CONTENT));
890 memset (&state, 0, sizeof (state));
892 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
894 char *chs = mutt_get_parameter ("charset", b->parameter);
895 char *fchs = b->use_disp ? ((AttachCharset && *AttachCharset) ?
896 AttachCharset : Charset) : Charset;
897 if (Charset && (chs || SendCharset) &&
898 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
899 &fromcode, &tocode, info) != (size_t)(-1))
903 mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
904 mutt_set_parameter ("charset", chsbuf, &b->parameter);
906 b->charset = fromcode;
914 while ((r = fread (buffer, 1, sizeof(buffer), fp)))
915 update_content_info (info, &state, buffer, r);
916 update_content_info (info, &state, 0, 0);
920 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
921 mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
922 Charset && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
928 /* Given a file with path ``s'', see if there is a registered MIME type.
929 * returns the major MIME type, and copies the subtype to ``d''. First look
930 * for ~/.mime.types, then look in a system mime.types if we can find one.
931 * The longest match is used so that we can match `ps.gz' when `gz' also
935 int mutt_lookup_mime_type (BODY *att, const char *path)
939 char buf[LONG_STRING];
940 char subtype[STRING], xtype[STRING];
942 int szf, sze, cur_sze;
950 szf = mutt_strlen (path);
952 for (count = 0 ; count < 3 ; count++)
955 * can't use strtok() because we use it in an inner loop below, so use
956 * a switch statement here instead.
961 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
964 strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
967 strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
970 dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
971 goto bye; /* shouldn't happen */
974 if ((f = fopen (buf, "r")) != NULL)
976 while (fgets (buf, sizeof (buf) - 1, f) != NULL)
978 /* weed out any comments */
979 if ((p = strchr (buf, '#')))
982 /* remove any leading space. */
986 /* position on the next field in this line */
987 if ((p = strpbrk (ct, " \t")) == NULL)
992 /* cycle through the file extensions */
993 while ((p = strtok (p, " \t\n")))
995 sze = mutt_strlen (p);
996 if ((sze > cur_sze) && (szf >= sze) &&
997 (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
998 (szf == sze || path[szf - sze - 1] == '.'))
1000 /* get the content-type */
1002 if ((p = strchr (ct, '/')) == NULL)
1004 /* malformed line, just skip it. */
1009 for (q = p; *q && !ISSPACE (*q); q++)
1012 mutt_substrcpy (subtype, p, q, sizeof (subtype));
1014 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1015 strfcpy (xtype, ct, sizeof (xtype));
1028 if (type != TYPEOTHER || *xtype != '\0')
1031 mutt_str_replace (&att->subtype, subtype);
1032 mutt_str_replace (&att->xtype, xtype);
1038 void mutt_message_to_7bit (BODY *a, FILE *fp)
1040 char temp[_POSIX_PATH_MAX];
1046 if (!a->filename && fp)
1048 else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1050 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1056 if (stat (a->filename, &sb) == -1)
1058 mutt_perror ("stat");
1059 safe_fclose (&fpin);
1061 a->length = sb.st_size;
1064 mutt_mktemp (temp, sizeof (temp));
1065 if (!(fpout = safe_fopen (temp, "w+")))
1067 mutt_perror ("fopen");
1071 fseeko (fpin, a->offset, 0);
1072 a->parts = mutt_parse_messageRFC822 (fpin, a);
1074 transform_to_7bit (a->parts, fpin);
1076 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1077 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1079 fputs ("MIME-Version: 1.0\n", fpout);
1080 mutt_write_mime_header (a->parts, fpout);
1081 fputc ('\n', fpout);
1082 mutt_write_mime_body (a->parts, fpout);
1087 if (fpin && fpin != fp)
1088 safe_fclose (&fpin);
1090 safe_fclose (&fpout);
1094 a->encoding = ENC7BIT;
1095 a->d_filename = a->filename;
1096 if (a->filename && a->unlink)
1097 unlink (a->filename);
1098 a->filename = safe_strdup (temp);
1100 if(stat (a->filename, &sb) == -1)
1102 mutt_perror ("stat");
1105 a->length = sb.st_size;
1106 mutt_free_body (&a->parts);
1107 a->hdr->content = NULL;
1110 static void transform_to_7bit (BODY *a, FILE *fpin)
1112 char buff[_POSIX_PATH_MAX];
1116 memset (&s, 0, sizeof (s));
1117 for (; a; a = a->next)
1119 if (a->type == TYPEMULTIPART)
1121 if (a->encoding != ENC7BIT)
1122 a->encoding = ENC7BIT;
1124 transform_to_7bit (a->parts, fpin);
1126 else if (mutt_is_message_type(a->type, a->subtype))
1128 mutt_message_to_7bit (a, fpin);
1133 a->force_charset = 1;
1135 mutt_mktemp (buff, sizeof (buff));
1136 if ((s.fpout = safe_fopen (buff, "w")) == NULL)
1138 mutt_perror ("fopen");
1142 mutt_decode_attachment (a, &s);
1143 safe_fclose (&s.fpout);
1144 a->d_filename = a->filename;
1145 a->filename = safe_strdup (buff);
1147 if (stat (a->filename, &sb) == -1)
1149 mutt_perror ("stat");
1152 a->length = sb.st_size;
1154 mutt_update_encoding (a);
1155 if (a->encoding == ENC8BIT)
1156 a->encoding = ENCQUOTEDPRINTABLE;
1157 else if(a->encoding == ENCBINARY)
1158 a->encoding = ENCBASE64;
1163 /* determine which Content-Transfer-Encoding to use */
1164 static void mutt_set_encoding (BODY *b, CONTENT *info)
1166 char send_charset[SHORT_STRING];
1168 if (b->type == TYPETEXT)
1170 char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1171 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1172 b->encoding = ENCQUOTEDPRINTABLE;
1173 else if (info->hibin)
1174 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1176 b->encoding = ENC7BIT;
1178 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1180 if (info->lobin || info->hibin)
1182 if (option (OPTALLOW8BIT) && !info->lobin)
1183 b->encoding = ENC8BIT;
1185 mutt_message_to_7bit (b, NULL);
1188 b->encoding = ENC7BIT;
1190 else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1191 b->encoding = ENC7BIT;
1194 if (info->lobin || info->hibin || info->binary || info->linemax > 990
1195 || info->cr || (/* option (OPTENCODEFROM) && */ info->from))
1198 /* Determine which encoding is smaller */
1199 if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
1200 3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1201 b->encoding = ENCBASE64;
1203 b->encoding = ENCQUOTEDPRINTABLE;
1207 b->encoding = ENC7BIT;
1211 void mutt_stamp_attachment(BODY *a)
1213 a->stamp = time(NULL);
1216 /* Get a body's character set */
1218 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1222 if (b && b->type != TYPETEXT)
1226 p = mutt_get_parameter ("charset", b->parameter);
1229 mutt_canonical_charset (d, dlen, NONULL(p));
1231 strfcpy (d, "us-ascii", dlen);
1237 /* Assumes called from send mode where BODY->filename points to actual file */
1238 void mutt_update_encoding (BODY *a)
1241 char chsbuff[STRING];
1243 /* override noconv when it's us-ascii */
1244 if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1247 if (!a->force_charset && !a->noconv)
1248 mutt_delete_parameter ("charset", &a->parameter);
1250 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1253 mutt_set_encoding (a, info);
1254 mutt_stamp_attachment(a);
1261 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1263 char buffer[LONG_STRING];
1266 int cmflags, chflags;
1267 int pgp = WithCrypto? hdr->security : 0;
1271 if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1272 (hdr->security & ENCRYPT)) {
1273 if (!crypt_valid_passphrase(hdr->security))
1278 mutt_mktemp (buffer, sizeof (buffer));
1279 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1282 body = mutt_new_body ();
1283 body->type = TYPEMESSAGE;
1284 body->subtype = safe_strdup ("rfc822");
1285 body->filename = safe_strdup (buffer);
1288 body->disposition = DISPINLINE;
1291 mutt_parse_mime_message (ctx, hdr);
1296 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1297 if (!attach_msg && option (OPTMIMEFORWDECODE))
1299 chflags |= CH_MIME | CH_TXTPLAIN;
1300 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1301 if ((WithCrypto & APPLICATION_PGP))
1303 if ((WithCrypto & APPLICATION_SMIME))
1304 pgp &= ~SMIMEENCRYPT;
1307 && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1309 if ((WithCrypto & APPLICATION_PGP)
1310 && mutt_is_multipart_encrypted (hdr->content))
1312 chflags |= CH_MIME | CH_NONEWLINE;
1313 cmflags = M_CM_DECODE_PGP;
1316 else if ((WithCrypto & APPLICATION_PGP)
1317 && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT))
1319 chflags |= CH_MIME | CH_TXTPLAIN;
1320 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1323 else if ((WithCrypto & APPLICATION_SMIME)
1324 && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT)
1326 chflags |= CH_MIME | CH_TXTPLAIN;
1327 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1328 pgp &= ~SMIMEENCRYPT;
1332 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1337 body->hdr = mutt_new_header();
1338 body->hdr->offset = 0;
1339 /* we don't need the user headers here */
1340 body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1342 body->hdr->security = pgp;
1343 mutt_update_encoding (body);
1344 body->parts = body->hdr->content;
1351 BODY *mutt_make_file_attach (const char *path)
1356 att = mutt_new_body ();
1357 att->filename = safe_strdup (path);
1359 /* Attempt to determine the appropriate content-type based on the filename
1365 if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER
1369 att->subtype = safe_strdup (buf);
1370 att->xtype = safe_strdup (xbuf);
1375 mutt_lookup_mime_type (att, path);
1379 if ((info = mutt_get_content_info (path, att)) == NULL)
1381 mutt_free_body (&att);
1387 if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
1390 * Statistically speaking, there should be more than 10% "lobin"
1391 * chars if this is really a binary file...
1393 att->type = TYPETEXT;
1394 att->subtype = safe_strdup ("plain");
1398 att->type = TYPEAPPLICATION;
1399 att->subtype = safe_strdup ("octet-stream");
1404 mutt_update_encoding (att);
1408 static int get_toplevel_encoding (BODY *a)
1412 for (; a; a = a->next)
1414 if (a->encoding == ENCBINARY)
1416 else if (a->encoding == ENC8BIT)
1423 /* check for duplicate boundary. return 1 if duplicate */
1424 static int mutt_check_boundary (const char* boundary, BODY *b)
1428 if (b->parts && mutt_check_boundary (boundary, b->parts))
1431 if (b->next && mutt_check_boundary (boundary, b->next))
1434 if ((p = mutt_get_parameter ("boundary", b->parameter))
1435 && !ascii_strcmp (p, boundary))
1440 BODY *mutt_make_multipart (BODY *b)
1444 new = mutt_new_body ();
1445 new->type = TYPEMULTIPART;
1446 new->subtype = safe_strdup ("mixed");
1447 new->encoding = get_toplevel_encoding (b);
1450 mutt_generate_boundary (&new->parameter);
1451 if (mutt_check_boundary (mutt_get_parameter ("boundary", new->parameter),
1453 mutt_delete_parameter ("boundary", &new->parameter);
1455 while (!mutt_get_parameter ("boundary", new->parameter));
1457 new->disposition = DISPINLINE;
1463 /* remove the multipart body if it exists */
1464 BODY *mutt_remove_multipart (BODY *b)
1473 mutt_free_body (&t);
1478 char *mutt_make_date (char *s, size_t len)
1480 time_t t = time (NULL);
1481 struct tm *l = localtime (&t);
1482 time_t tz = mutt_local_tz (t);
1486 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1487 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1488 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1489 (int) tz / 60, (int) abs (tz) % 60);
1493 /* wrapper around mutt_write_address() so we can handle very large
1494 recipient lists without needing a huge temporary buffer in memory */
1495 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1498 char buf[LONG_STRING];
1507 rfc822_write_address (buf, sizeof (buf), adr, display);
1508 len = mutt_strlen (buf);
1509 if (count && linelen + len > 74)
1512 linelen = len + 8; /* tab is usually about 8 spaces... */
1516 if (count && adr->mailbox)
1525 if (!adr->group && adr->next && adr->next->mailbox)
1536 /* arbitrary number of elements to grow the array by */
1539 /* need to write the list in reverse because they are stored in reverse order
1540 * when parsed to speed up threading
1542 void mutt_write_references (LIST *r, FILE *f, int trim)
1545 int refcnt = 0, refmax = 0;
1547 for ( ; (trim == 0 || refcnt < trim) && r ; r = r->next)
1549 if (refcnt == refmax)
1550 safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1554 while (refcnt-- > 0)
1557 fputs (ref[refcnt]->data, f);
1565 static const char *find_word (const char *src)
1567 const char *p = src;
1569 while (p && *p && strchr (" \t\n", *p))
1571 while (p && *p && !strchr (" \t\n", *p))
1576 /* like wcwidth(), but gets const char* not wchar_t* */
1577 static int my_width (const char *str, int col, int flags)
1580 int l, w = 0, nl = 0;
1581 const char *p = str;
1585 if (mbtowc (&wc, p, MB_CUR_MAX) >= 0)
1590 /* correctly calc tab stop, even for sending as the
1591 * line should look pretty on the receiving end */
1592 if (wc == L'\t' || (nl && wc == L' '))
1597 /* track newlines for display-case: if we have a space
1598 * after a newline, assume 8 spaces as for display we
1599 * always tab-fold */
1600 else if ((flags & CH_DISPLAY) && wc == '\n')
1611 static int print_val (FILE *fp, const char *pfx, const char *value,
1612 int flags, size_t col)
1614 while (value && *value)
1616 if (fputc (*value, fp) == EOF)
1618 /* corner-case: break words longer than 998 chars by force,
1619 * mandated by RfC5322 */
1620 if (!(flags & CH_DISPLAY) && ++col >= 998)
1622 if (fputs ("\n ", fp) < 0)
1628 if (*(value + 1) && pfx && *pfx && fputs (pfx, fp) == EOF)
1630 /* for display, turn folding spaces into folding tabs */
1631 if ((flags & CH_DISPLAY) && (*(value + 1) == ' ' || *(value + 1) == '\t'))
1634 while (*value && (*value == ' ' || *value == '\t'))
1636 if (fputc ('\t', fp) == EOF)
1646 static int fold_one_header (FILE *fp, const char *tag, const char *value,
1647 const char *pfx, int wraplen, int flags)
1649 const char *p = value, *next, *sp;
1650 char buf[HUGE_STRING] = "";
1651 int first = 1, enc, col = 0, w, l = 0, fold;
1653 dprint(4,(debugfile,"mwoh: pfx=[%s], tag=[%s], flags=%d value=[%s]\n",
1654 pfx, tag, flags, value));
1656 if (tag && *tag && fprintf (fp, "%s%s: ", NONULL (pfx), tag) < 0)
1658 col = mutt_strlen (tag) + (tag && *tag ? 2 : 0) + mutt_strlen (pfx);
1664 /* find the next word and place it in `buf'. it may start with
1665 * whitespace we can fold before */
1666 next = find_word (p);
1667 l = MIN(sizeof (buf), next - p);
1671 /* determine width: character cells for display, bytes for sending
1672 * (we get pure ascii only) */
1673 w = my_width (buf, col, flags);
1674 enc = mutt_strncmp (buf, "=?", 2) == 0;
1676 dprint(5,(debugfile,"mwoh: word=[%s], col=%d, w=%d, next=[0x0%x]\n",
1677 buf, col, w, *next));
1679 /* insert a folding \n before the current word's lwsp except for
1680 * header name, first word on a line (word longer than wrap width)
1681 * and encoded words */
1682 if (!first && !enc && col && col + w >= wraplen)
1684 col = mutt_strlen (pfx);
1686 if (fprintf (fp, "\n%s", NONULL(pfx)) <= 0)
1690 /* print the actual word; for display, ignore leading ws for word
1691 * and fold with tab for readability */
1692 if ((flags & CH_DISPLAY) && fold)
1695 while (*p && (*p == ' ' || *p == '\t'))
1700 if (fputc ('\t', fp) == EOF)
1702 if (print_val (fp, pfx, p, flags, col) < 0)
1706 else if (print_val (fp, pfx, buf, flags, col) < 0)
1710 /* if the current word ends in \n, ignore all its trailing spaces
1711 * and reset column; this prevents us from putting only spaces (or
1712 * even none) on a line if the trailing spaces are located at our
1713 * current line width
1714 * XXX this covers ASCII space only, for display we probably
1715 * XXX want something like iswspace() here */
1717 while (*sp && (*sp == ' ' || *sp == '\t'))
1729 /* if we have printed something but didn't \n-terminate it, do it
1730 * except the last word we printed ended in \n already */
1731 if (col && buf[l - 1] != '\n')
1732 if (putc ('\n', fp) == EOF)
1738 static char *unfold_header (char *s)
1740 char *p = s, *q = s;
1744 /* remove CRLF prior to FWSP, turn \t into ' ' */
1745 if (*p == '\r' && *(p + 1) && *(p + 1) == '\n' && *(p + 2) &&
1746 (*(p + 2) == ' ' || *(p + 2) == '\t'))
1752 /* remove LF prior to FWSP, turn \t into ' ' */
1753 else if (*p == '\n' && *(p + 1) && (*(p + 1) == ' ' || *(p + 1) == '\t'))
1767 static int write_one_header (FILE *fp, int pfxw, int max, int wraplen,
1768 const char *pfx, const char *start, const char *end,
1771 char *tagbuf, *valbuf, *t;
1772 int is_from = ((end - start) > 5 &&
1773 ascii_strncasecmp (start, "from ", 5) == 0);
1775 /* only pass through folding machinery if necessary for sending,
1776 never wrap From_ headers on sending */
1777 if (!(flags & CH_DISPLAY) && (pfxw + max <= wraplen || is_from))
1779 valbuf = mutt_substrdup (start, end);
1780 dprint(4,(debugfile,"mwoh: buf[%s%s] short enough, "
1781 "max width = %d <= %d\n",
1782 NONULL(pfx), valbuf, max, wraplen));
1784 if (fputs (pfx, fp) == EOF)
1786 if (!(t = strchr (valbuf, ':')))
1788 dprint (1, (debugfile, "mwoh: warning: header not in "
1789 "'key: value' format!\n"));
1792 if (print_val (fp, pfx, valbuf, flags, mutt_strlen (pfx)) < 0)
1801 t = strchr (start, ':');
1804 dprint (1, (debugfile, "mwoh: warning: header not in "
1805 "'key: value' format!\n"));
1811 valbuf = mutt_substrdup (start, end);
1815 tagbuf = mutt_substrdup (start, t);
1816 ++t; /* skip over the colon separating the header field name and value */
1817 SKIPWS(t); /* skip over any leading whitespace */
1818 valbuf = mutt_substrdup (t, end);
1820 dprint(4,(debugfile,"mwoh: buf[%s%s] too long, "
1821 "max width = %d > %d\n",
1822 NONULL(pfx), valbuf, max, wraplen));
1823 if (fold_one_header (fp, tagbuf, valbuf, pfx, wraplen, flags) < 0)
1831 /* split several headers into individual ones and call write_one_header
1833 int mutt_write_one_header (FILE *fp, const char *tag, const char *value,
1834 const char *pfx, int wraplen, int flags)
1836 char *p = (char *)value, *last, *line;
1837 int max = 0, w, rc = -1;
1838 int pfxw = mutt_strwidth (pfx);
1839 char *v = safe_strdup (value);
1841 if (!(flags & CH_DISPLAY) || option (OPTWEED))
1842 v = unfold_header (v);
1844 /* when not displaying, use sane wrap value */
1845 if (!(flags & CH_DISPLAY))
1847 if (WrapHeaders < 78 || WrapHeaders > 998)
1850 wraplen = WrapHeaders;
1852 else if (wraplen <= 0 || wraplen > COLS)
1857 /* if header is short enough, simply print it */
1858 if (!(flags & CH_DISPLAY) && mutt_strwidth (tag) + 2 + pfxw +
1859 mutt_strwidth (v) <= wraplen)
1861 dprint(4,(debugfile,"mwoh: buf[%s%s: %s] is short enough\n",
1862 NONULL(pfx), tag, v));
1863 if (fprintf (fp, "%s%s: %s\n", NONULL(pfx), tag, v) <= 0)
1870 rc = fold_one_header (fp, tag, v, pfx, wraplen, flags);
1875 p = last = line = (char *)v;
1878 p = strchr (p, '\n');
1880 /* find maximum line width in current header */
1883 if ((w = my_width (line, 0, flags)) > max)
1892 if (*p != ' ' && *p != '\t')
1894 if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
1902 if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
1913 /* Note: all RFC2047 encoding should be done outside of this routine, except
1914 * for the "real name." This will allow this routine to be used more than
1915 * once, if necessary.
1917 * Likewise, all IDN processing should happen outside of this routine.
1919 * mode == 1 => "lite" mode (used for edit_hdrs)
1920 * mode == 0 => normal mode. write full header + MIME headers
1921 * mode == -1 => write just the envelope info (used for postponing messages)
1923 * privacy != 0 => will omit any headers which may identify the user.
1924 * Output generated is suitable for being sent through
1925 * anonymous remailer chains.
1931 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
1932 int mode, int privacy)
1934 char buffer[LONG_STRING];
1936 LIST *tmp = env->userhdrs;
1937 int has_agent = 0; /* user defined user-agent header field exists */
1939 if (mode == 0 && !privacy)
1940 fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
1942 /* OPTUSEFROM is not consulted here so that we can still write a From:
1943 * field if the user sets it with the `my_hdr' command
1945 if (env->from && !privacy)
1948 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1949 fprintf (fp, "From: %s\n", buffer);
1952 if (env->sender && !privacy)
1955 rfc822_write_address (buffer, sizeof (buffer), env->sender, 0);
1956 fprintf (fp, "Sender: %s\n", buffer);
1962 mutt_write_address_list (env->to, fp, 4, 0);
1965 fputs ("To: \n", fp);
1970 mutt_write_address_list (env->cc, fp, 4, 0);
1973 fputs ("Cc: \n", fp);
1977 if(mode != 0 || option(OPTWRITEBCC))
1979 fputs ("Bcc: ", fp);
1980 mutt_write_address_list (env->bcc, fp, 5, 0);
1984 fputs ("Bcc: \n", fp);
1987 mutt_write_one_header (fp, "Subject", env->subject, NULL, 0, 0);
1989 fputs ("Subject: \n", fp);
1991 /* save message id if the user has set it */
1992 if (env->message_id && !privacy)
1993 fprintf (fp, "Message-ID: %s\n", env->message_id);
1997 fputs ("Reply-To: ", fp);
1998 mutt_write_address_list (env->reply_to, fp, 10, 0);
2001 fputs ("Reply-To: \n", fp);
2003 if (env->mail_followup_to)
2005 fputs ("Mail-Followup-To: ", fp);
2006 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
2011 if (env->references)
2013 fputs ("References:", fp);
2014 mutt_write_references (env->references, fp, 10);
2018 /* Add the MIME headers */
2019 fputs ("MIME-Version: 1.0\n", fp);
2020 mutt_write_mime_header (attach, fp);
2023 if (env->in_reply_to)
2025 fputs ("In-Reply-To:", fp);
2026 mutt_write_references (env->in_reply_to, fp, 0);
2030 /* Add any user defined headers */
2031 for (; tmp; tmp = tmp->next)
2033 if ((p = strchr (tmp->data, ':')))
2043 continue; /* don't emit empty fields. */
2046 /* check to see if the user has overridden the user-agent field */
2047 if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
2057 mutt_write_one_header (fp, tmp->data, p, NULL, 0, 0);
2062 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent)
2064 /* Add a vanity header */
2065 fprintf (fp, "User-Agent: Mutt/%s (%s)\n", MUTT_VERSION, ReleaseDate);
2068 return (ferror (fp) == 0 ? 0 : -1);
2071 static void encode_headers (LIST *h)
2077 for (; h; h = h->next)
2079 if (!(p = strchr (h->data, ':')))
2084 tmp = safe_strdup (p);
2089 rfc2047_encode_string (&tmp);
2090 safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
2092 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
2098 const char *mutt_fqdn(short may_hide_host)
2102 if(Fqdn && Fqdn[0] != '@')
2106 if(may_hide_host && option(OPTHIDDENHOST))
2108 if((p = strchr(Fqdn, '.')))
2111 /* sanity check: don't hide the host if
2112 * the fqdn is something like detebe.org.
2115 if(!p || !strchr(p, '.'))
2123 char *mutt_gen_msgid (void)
2125 char buf[SHORT_STRING];
2132 if(!(fqdn = mutt_fqdn(0)))
2133 fqdn = NONULL(Hostname);
2135 snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.G%c%u@%s>",
2136 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
2137 tm->tm_min, tm->tm_sec, MsgIdPfx, (unsigned int)getpid (), fqdn);
2138 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
2139 return (safe_strdup (buf));
2142 static RETSIGTYPE alarm_handler (int sig)
2147 /* invoke sendmail in a subshell
2148 path (in) path to program to execute
2149 args (in) arguments to pass to program
2150 msg (in) temp file containing message to send
2151 tempfile (out) if sendmail is put in the background, this points
2152 to the temporary file containing the stdout of the
2153 child process. If it is NULL, stderr and stdout
2154 are not redirected. */
2156 send_msg (const char *path, char **args, const char *msg, char **tempfile)
2162 mutt_block_signals_system ();
2165 /* we also don't want to be stopped right now */
2166 sigaddset (&set, SIGTSTP);
2167 sigprocmask (SIG_BLOCK, &set, NULL);
2169 if (SendmailWait >= 0 && tempfile)
2171 char tmp[_POSIX_PATH_MAX];
2173 mutt_mktemp (tmp, sizeof (tmp));
2174 *tempfile = safe_strdup (tmp);
2177 if ((pid = fork ()) == 0)
2179 struct sigaction act, oldalrm;
2181 /* save parent's ID before setsid() */
2184 /* we want the delivery to continue even after the main process dies,
2185 * so we put ourselves into another session right away
2189 /* next we close all open files */
2191 #if defined(OPEN_MAX)
2192 for (fd = tempfile ? 1 : 3; fd < OPEN_MAX; fd++)
2194 #elif defined(_POSIX_OPEN_MAX)
2195 for (fd = tempfile ? 1 : 3; fd < _POSIX_OPEN_MAX; fd++)
2205 /* now the second fork() */
2206 if ((pid = fork ()) == 0)
2208 /* "msg" will be opened as stdin */
2209 if (open (msg, O_RDONLY, 0) < 0)
2216 if (SendmailWait >= 0 && tempfile && *tempfile)
2218 /* *tempfile will be opened as stdout */
2219 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2221 /* redirect stderr to *tempfile too */
2227 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
2229 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
2233 execvp (path, args);
2240 FREE (tempfile); /* __FREE_CHECKED__ */
2244 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
2245 * SendmailWait = 0: wait forever
2246 * SendmailWait < 0: don't wait
2248 if (SendmailWait > 0)
2251 act.sa_handler = alarm_handler;
2253 /* need to make sure waitpid() is interrupted on SIGALRM */
2254 act.sa_flags = SA_INTERRUPT;
2258 sigemptyset (&act.sa_mask);
2259 sigaction (SIGALRM, &act, &oldalrm);
2260 alarm (SendmailWait);
2262 else if (SendmailWait < 0)
2263 _exit (0xff & EX_OK);
2265 if (waitpid (pid, &st, 0) > 0)
2267 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
2268 if (SendmailWait && st == (0xff & EX_OK) && tempfile && *tempfile)
2270 unlink (*tempfile); /* no longer needed */
2271 FREE (tempfile); /* __FREE_CHECKED__ */
2276 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
2278 if (SendmailWait > 0 && tempfile && *tempfile)
2281 FREE (tempfile); /* __FREE_CHECKED__ */
2285 /* reset alarm; not really needed, but... */
2287 sigaction (SIGALRM, &oldalrm, NULL);
2289 if (kill (ppid, 0) == -1 && errno == ESRCH && tempfile && *tempfile)
2291 /* the parent is already dead */
2293 FREE (tempfile); /* __FREE_CHECKED__ */
2299 sigprocmask (SIG_UNBLOCK, &set, NULL);
2301 if (pid != -1 && waitpid (pid, &st, 0) > 0)
2302 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
2304 st = S_ERR; /* error */
2306 mutt_unblock_signals_system (1);
2312 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
2314 for (; addr; addr = addr->next)
2316 /* weed out group mailboxes, since those are for display only */
2317 if (addr->mailbox && !addr->group)
2319 if (*argslen == *argsmax)
2320 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2321 args[(*argslen)++] = addr->mailbox;
2328 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2330 if (*argslen == *argsmax)
2331 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2332 args[(*argslen)++] = s;
2337 mutt_invoke_sendmail (ADDRESS *from, /* the sender */
2338 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2339 const char *msg, /* file containing message */
2340 int eightbit) /* message contains 8bit chars */
2342 char *ps = NULL, *path = NULL, *s = safe_strdup (Sendmail), *childout = NULL;
2344 size_t argslen = 0, argsmax = 0;
2349 while ((ps = strtok (ps, " ")))
2351 if (argslen == argsmax)
2352 safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2355 args[argslen++] = ps;
2358 path = safe_strdup (ps);
2359 ps = strrchr (ps, '/');
2364 args[argslen++] = ps;
2370 if (eightbit && option (OPTUSE8BITMIME))
2371 args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2373 if (option (OPTENVFROM))
2377 args = add_option (args, &argslen, &argsmax, "-f");
2378 args = add_args (args, &argslen, &argsmax, EnvFrom);
2380 else if (from && !from->next)
2382 args = add_option (args, &argslen, &argsmax, "-f");
2383 args = add_args (args, &argslen, &argsmax, from);
2389 args = add_option (args, &argslen, &argsmax, "-N");
2390 args = add_option (args, &argslen, &argsmax, DsnNotify);
2394 args = add_option (args, &argslen, &argsmax, "-R");
2395 args = add_option (args, &argslen, &argsmax, DsnReturn);
2397 args = add_option (args, &argslen, &argsmax, "--");
2398 args = add_args (args, &argslen, &argsmax, to);
2399 args = add_args (args, &argslen, &argsmax, cc);
2400 args = add_args (args, &argslen, &argsmax, bcc);
2402 if (argslen == argsmax)
2403 safe_realloc (&args, sizeof (char *) * (++argsmax));
2405 args[argslen++] = NULL;
2407 if ((i = send_msg (path, args, msg, option(OPTNOCURSES) ? NULL : &childout)) != (EX_OK & 0xff))
2411 const char *e = mutt_strsysexit (i);
2413 e = mutt_strsysexit (i);
2414 mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2419 if (stat (childout, &st) == 0 && st.st_size > 0)
2420 mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2432 if (i == (EX_OK & 0xff))
2434 else if (i == S_BKG)
2441 /* For postponing (!final) do the necessary encodings only */
2442 void mutt_prepare_envelope (ENVELOPE *env, int final)
2444 char buffer[LONG_STRING];
2448 if (env->bcc && !(env->to || env->cc))
2450 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2451 * recipients if there is no To: or Cc: field, so attempt to suppress
2452 * it by using an empty To: field.
2454 env->to = rfc822_new_address ();
2456 env->to->next = rfc822_new_address ();
2459 rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2462 env->to->mailbox = safe_strdup (buffer);
2465 mutt_set_followup_to (env);
2467 if (!env->message_id)
2468 env->message_id = mutt_gen_msgid ();
2471 /* Take care of 8-bit => 7-bit conversion. */
2472 rfc2047_encode_adrlist (env->to, "To");
2473 rfc2047_encode_adrlist (env->cc, "Cc");
2474 rfc2047_encode_adrlist (env->bcc, "Bcc");
2475 rfc2047_encode_adrlist (env->from, "From");
2476 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2477 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2478 rfc2047_encode_string (&env->x_label);
2482 rfc2047_encode_string (&env->subject);
2484 encode_headers (env->userhdrs);
2487 void mutt_unprepare_envelope (ENVELOPE *env)
2491 for (item = env->userhdrs; item; item = item->next)
2492 rfc2047_decode (&item->data);
2494 rfc822_free_address (&env->mail_followup_to);
2496 /* back conversions */
2497 rfc2047_decode_adrlist (env->to);
2498 rfc2047_decode_adrlist (env->cc);
2499 rfc2047_decode_adrlist (env->bcc);
2500 rfc2047_decode_adrlist (env->from);
2501 rfc2047_decode_adrlist (env->reply_to);
2502 rfc2047_decode (&env->subject);
2503 rfc2047_decode (&env->x_label);
2506 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2511 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2512 MESSAGE *msg = NULL;
2516 /* Try to bounce each message out, aborting if we get any failures. */
2517 for (i=0; i<Context->msgcount; i++)
2518 if (Context->hdrs[i]->tagged)
2519 ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2523 /* If we failed to open a message, return with error */
2524 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2527 if (!fp) fp = msg->fp;
2529 mutt_mktemp (tempfile, sizeof (tempfile));
2530 if ((f = safe_fopen (tempfile, "w")) != NULL)
2532 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2535 if (!option (OPTBOUNCEDELIVERED))
2536 ch_flags |= CH_WEED_DELIVERED;
2538 fseeko (fp, h->offset, 0);
2539 fprintf (f, "Resent-From: %s", resent_from);
2540 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2541 msgid_str = mutt_gen_msgid();
2542 fprintf (f, "Resent-Message-ID: %s\n", msgid_str);
2543 fputs ("Resent-To: ", f);
2544 mutt_write_address_list (to, f, 11, 0);
2545 mutt_copy_header (fp, h, f, ch_flags, NULL);
2547 mutt_copy_bytes (fp, f, h->content->length);
2553 ret = mutt_smtp_send (env_from, to, NULL, NULL, tempfile,
2554 h->content->encoding == ENC8BIT);
2556 #endif /* USE_SMTP */
2557 ret = mutt_invoke_sendmail (env_from, to, NULL, NULL, tempfile,
2558 h->content->encoding == ENC8BIT);
2562 mx_close_message (&msg);
2567 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2570 const char *fqdn = mutt_fqdn (1);
2571 char resent_from[STRING];
2575 resent_from[0] = '\0';
2576 from = mutt_default_from ();
2579 * mutt_default_from() does not use $realname if the real name is not set
2580 * in $from, so we add it here. The reason it is not added in
2581 * mutt_default_from() is that during normal sending, we execute
2582 * send-hooks and set the realname last so that it can be changed based
2583 * upon message criteria.
2585 if (! from->personal)
2586 from->personal = safe_strdup(Realname);
2589 rfc822_qualify (from, fqdn);
2591 rfc2047_encode_adrlist (from, "Resent-From");
2592 if (mutt_addrlist_to_idna (from, &err))
2594 mutt_error (_("Bad IDN %s while preparing resent-from."),
2596 rfc822_free_address (&from);
2599 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2601 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2603 rfc822_free_address (&from);
2609 /* given a list of addresses, return a list of unique addresses */
2610 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2612 ADDRESS *top = addr;
2613 ADDRESS **last = ⊤
2619 for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next)
2621 if (tmp->mailbox && addr->mailbox &&
2622 !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2631 dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
2637 rfc822_free_address(&addr);
2651 static void set_noconv_flags (BODY *b, short flag)
2653 for(; b; b = b->next)
2655 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2656 set_noconv_flags (b->parts, flag);
2657 else if (b->type == TYPETEXT && b->noconv)
2660 mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2662 mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2667 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc)
2671 char tempfile[_POSIX_PATH_MAX];
2672 FILE *tempfp = NULL;
2673 int r, need_buffy_cleanup = 0;
2675 char buf[SHORT_STRING];
2678 set_noconv_flags (hdr->content, 1);
2680 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
2682 dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2687 /* We need to add a Content-Length field to avoid problems where a line in
2688 * the message body begins with "From "
2690 if (f.magic == M_MMDF || f.magic == M_MBOX)
2692 mutt_mktemp (tempfile, sizeof (tempfile));
2693 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2695 mutt_perror (tempfile);
2696 mx_close_mailbox (&f, NULL);
2699 /* remember new mail status before appending message */
2700 need_buffy_cleanup = 1;
2704 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2705 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
2707 mx_close_mailbox (&f, NULL);
2711 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2712 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2714 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 0);
2716 /* (postponment) if this was a reply of some sort, <msgid> contians the
2717 * Message-ID: of message replied to. Save it using a special X-Mutt-
2718 * header so it can be picked up if the message is recalled at a later
2719 * point in time. This will allow the message to be marked as replied if
2720 * the same mailbox is still open.
2723 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2725 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2726 * it can be picked up when the message is recalled
2729 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2731 if (f.magic == M_MMDF || f.magic == M_MBOX)
2732 fprintf (msg->fp, "Status: RO\n");
2734 /* mutt_write_rfc822_header() only writes out a Date: header with
2735 * mode == 0, i.e. _not_ postponment; so write out one ourself */
2737 fprintf (msg->fp, "%s", mutt_make_date (buf, sizeof (buf)));
2739 /* (postponment) if the mail is to be signed or encrypted, save this info */
2740 if ((WithCrypto & APPLICATION_PGP)
2741 && post && (hdr->security & APPLICATION_PGP))
2743 fputs ("X-Mutt-PGP: ", msg->fp);
2744 if (hdr->security & ENCRYPT)
2745 fputc ('E', msg->fp);
2746 if (hdr->security & SIGN)
2748 fputc ('S', msg->fp);
2749 if (PgpSignAs && *PgpSignAs)
2750 fprintf (msg->fp, "<%s>", PgpSignAs);
2752 if (hdr->security & INLINE)
2753 fputc ('I', msg->fp);
2754 fputc ('\n', msg->fp);
2757 /* (postponment) if the mail is to be signed or encrypted, save this info */
2758 if ((WithCrypto & APPLICATION_SMIME)
2759 && post && (hdr->security & APPLICATION_SMIME))
2761 fputs ("X-Mutt-SMIME: ", msg->fp);
2762 if (hdr->security & ENCRYPT) {
2763 fputc ('E', msg->fp);
2764 if (SmimeCryptAlg && *SmimeCryptAlg)
2765 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2767 if (hdr->security & SIGN) {
2768 fputc ('S', msg->fp);
2769 if (SmimeDefaultKey && *SmimeDefaultKey)
2770 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2772 if (hdr->security & INLINE)
2773 fputc ('I', msg->fp);
2774 fputc ('\n', msg->fp);
2778 /* (postponement) if the mail is to be sent through a mixmaster
2779 * chain, save that information
2782 if (post && hdr->chain && hdr->chain)
2786 fputs ("X-Mutt-Mix:", msg->fp);
2787 for (p = hdr->chain; p; p = p->next)
2788 fprintf (msg->fp, " %s", (char *) p->data);
2790 fputc ('\n', msg->fp);
2796 char sasha[LONG_STRING];
2799 mutt_write_mime_body (hdr->content, tempfp);
2801 /* make sure the last line ends with a newline. Emacs doesn't ensure
2802 * this will happen, and it can cause problems parsing the mailbox
2805 fseek (tempfp, -1, 2);
2806 if (fgetc (tempfp) != '\n')
2808 fseek (tempfp, 0, 2);
2809 fputc ('\n', tempfp);
2813 if (ferror (tempfp))
2815 dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2816 safe_fclose (&tempfp);
2818 mx_commit_message (msg, &f); /* XXX - really? */
2819 mx_close_message (&msg);
2820 mx_close_mailbox (&f, NULL);
2824 /* count the number of lines */
2826 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2828 fprintf (msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftello (tempfp));
2829 fprintf (msg->fp, "Lines: %d\n\n", lines);
2831 /* copy the body and clean up */
2833 r = mutt_copy_stream (tempfp, msg->fp);
2834 if (fclose (tempfp) != 0)
2836 /* if there was an error, leave the temp version */
2842 fputc ('\n', msg->fp); /* finish off the header */
2843 r = mutt_write_mime_body (hdr->content, msg->fp);
2846 if (mx_commit_message (msg, &f) != 0)
2848 mx_close_message (&msg);
2849 mx_close_mailbox (&f, NULL);
2851 if (!post && need_buffy_cleanup)
2852 mutt_buffy_cleanup (path, &st);
2855 set_noconv_flags (hdr->content, 0);