2 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
26 #include "mutt_curses.h"
35 #include "mutt_crypt.h"
36 #include "mutt_idna.h"
47 #include <sys/utsname.h>
50 # include "mutt_libesmtp.h"
51 #endif /* USE_LIBESMTP */
57 #ifdef HAVE_SYSEXITS_H
59 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
63 /* If you are debugging this file, comment out the following line. */
72 extern char RFC822Specials[];
74 static struct sysexits
82 { 0xff & EX_USAGE, "Bad usage." },
85 { 0xff & EX_DATAERR, "Data format error." },
88 { 0xff & EX_NOINPUT, "Cannot open input." },
91 { 0xff & EX_NOUSER, "User unknown." },
94 { 0xff & EX_NOHOST, "Host unknown." },
97 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
100 { 0xff & EX_SOFTWARE, "Internal error." },
103 { 0xff & EX_OSERR, "Operating system error." },
106 { 0xff & EX_OSFILE, "System file missing." },
109 { 0xff & EX_CANTCREAT, "Can't create output." },
112 { 0xff & EX_IOERR, "I/O error." },
115 { 0xff & EX_TEMPFAIL, "Deferred." },
118 { 0xff & EX_PROTOCOL, "Remote protocol error." },
121 { 0xff & EX_NOPERM, "Insufficient permission." },
124 { 0xff & EX_NOPERM, "Local configuration error." },
126 { S_ERR, "Exec error." },
132 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
134 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
136 char B64Chars[64] = {
137 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
138 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
139 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
140 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
144 static char MsgIdPfx = 'A';
146 static void transform_to_7bit (BODY *a, FILE *fpin);
148 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
151 char line[77], savechar;
153 while ((c = fgetconv (fc)) != EOF)
155 /* Wrap the line if needed. */
156 if (linelen == 76 && ((istext && c != '\n') || !istext))
158 /* If the last character is "quoted", then be sure to move all three
159 * characters to the next line. Otherwise, just move the last
162 if (line[linelen-3] == '=')
169 line[1] = line[linelen-2];
170 line[2] = line[linelen-1];
175 savechar = line[linelen-1];
176 line[linelen-1] = '=';
185 /* Escape lines that begin with/only contain "the message separator". */
186 if (linelen == 4 && !mutt_strncmp ("From", line, 4))
188 strfcpy (line, "=46rom", sizeof (line));
191 else if (linelen == 4 && !mutt_strncmp ("from", line, 4))
193 strfcpy (line, "=66rom", sizeof (line));
196 else if (linelen == 1 && line[0] == '.')
198 strfcpy (line, "=2E", sizeof (line));
203 if (c == '\n' && istext)
205 /* Check to make sure there is no trailing space on this line. */
206 if (linelen > 0 && (line[linelen-1] == ' ' || line[linelen-1] == '\t'))
210 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
215 int savechar = line[linelen-1];
217 line[linelen-1] = '=';
220 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
231 else if (c != 9 && (c < 32 || c > 126 || c == '='))
233 /* Check to make sure there is enough room for the quoted character.
234 * If not, wrap to the next line.
238 line[linelen++] = '=';
244 sprintf (line+linelen,"=%2.2X", (unsigned char) c);
249 /* Don't worry about wrapping the line here. That will happen during
250 * the next iteration when I'll also know what the next character is.
256 /* Take care of anything left in the buffer */
259 if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
261 /* take care of trailing whitespace */
263 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
266 savechar = line[linelen-1];
267 line[linelen-1] = '=';
271 sprintf (line, "=%2.2X", (unsigned char) savechar);
280 static char b64_buffer[3];
281 static short b64_num;
282 static short b64_linelen;
284 static void b64_flush(FILE *fout)
291 if(b64_linelen >= 72)
297 for(i = b64_num; i < 3; i++)
298 b64_buffer[i] = '\0';
300 fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
302 fputc(B64Chars[((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf) ], fout);
307 fputc(B64Chars[((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3) ], fout);
311 fputc(B64Chars[b64_buffer[2] & 0x3f], fout);
316 while(b64_linelen % 4)
326 static void b64_putc(char c, FILE *fout)
331 b64_buffer[b64_num++] = c;
335 static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
339 b64_num = b64_linelen = 0;
341 while ((ch = fgetconv (fc)) != EOF)
343 if (istext && ch == '\n' && ch1 != '\r')
344 b64_putc('\r', fout);
352 static void encode_8bit (FGETCONV *fc, FILE *fout, int istext)
356 while ((ch = fgetconv (fc)) != EOF)
361 int mutt_write_mime_header (BODY *a, FILE *f)
371 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
375 len = 25 + mutt_strlen (a->subtype); /* approximate len. of content-type */
377 for(p = a->parameter; p; p = p->next)
387 tmp = safe_strdup (p->value);
388 encode = rfc2231_encode_string (&tmp);
389 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
391 /* Dirty hack to make messages readable by Outlook Express
392 * for the Mac: force quotes around the boundary parameter
393 * even when they aren't needed.
396 if (!ascii_strcasecmp (p->attribute, "boundary") && !strcmp (buffer, tmp))
397 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
401 tmplen = mutt_strlen (buffer) + mutt_strlen (p->attribute) + 1;
403 if (len + tmplen + 2 > 76)
414 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
422 fprintf(f, "Content-Description: %s\n", a->description);
424 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
428 if(!(fn = a->d_filename))
435 /* Strip off the leading path... */
436 if ((t = strrchr (fn, '/')))
442 tmp = safe_strdup (t);
443 encode = rfc2231_encode_string (&tmp);
444 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
446 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
452 if (a->encoding != ENC7BIT)
453 fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
455 /* Do NOT add the terminator here!!! */
456 return (ferror (f) ? -1 : 0);
459 # define write_as_text_part(a) (mutt_is_text_part(a) \
460 || ((WithCrypto & APPLICATION_PGP)\
461 && mutt_is_application_pgp(a)))
463 int mutt_write_mime_body (BODY *a, FILE *f)
465 char *p, boundary[SHORT_STRING];
466 char send_charset[SHORT_STRING];
471 if (a->type == TYPEMULTIPART)
473 /* First, find the boundary to use */
474 if (!(p = mutt_get_parameter ("boundary", a->parameter)))
476 dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
477 mutt_error _("No boundary parameter found! [report this error]");
480 strfcpy (boundary, p, sizeof (boundary));
482 for (t = a->parts; t ; t = t->next)
484 fprintf (f, "\n--%s\n", boundary);
485 if (mutt_write_mime_header (t, f) == -1)
488 if (mutt_write_mime_body (t, f) == -1)
491 fprintf (f, "\n--%s--\n", boundary);
492 return (ferror (f) ? -1 : 0);
495 /* This is pretty gross, but it's the best solution for now... */
496 if ((WithCrypto & APPLICATION_PGP)
497 && a->type == TYPEAPPLICATION
498 && mutt_strcmp (a->subtype, "pgp-encrypted") == 0)
500 fputs ("Version: 1\n", f);
504 if ((fpin = fopen (a->filename, "r")) == NULL)
506 dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
507 mutt_error (_("%s no longer exists!"), a->filename);
511 if (a->type == TYPETEXT && (!a->noconv))
512 fc = fgetconv_open (fpin, a->file_charset,
513 mutt_get_body_charset (send_charset, sizeof (send_charset), a),
516 fc = fgetconv_open (fpin, 0, 0, 0);
518 if (a->encoding == ENCQUOTEDPRINTABLE)
519 encode_quoted (fc, f, write_as_text_part (a));
520 else if (a->encoding == ENCBASE64)
521 encode_base64 (fc, f, write_as_text_part (a));
522 else if (a->type == TYPETEXT && (!a->noconv))
523 encode_8bit (fc, f, write_as_text_part (a));
525 mutt_copy_stream (fpin, f);
527 fgetconv_close (&fc);
530 return (ferror (f) ? -1 : 0);
533 #undef write_as_text_part
535 #define BOUNDARYLEN 16
536 void mutt_generate_boundary (PARAMETER **parm)
538 char rs[BOUNDARYLEN + 1];
543 for (i=0;i<BOUNDARYLEN;i++)
544 *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
547 mutt_set_parameter ("boundary", rs, parm);
561 static void update_content_info (CONTENT *info, CONTENT_STATE *s, char *d, size_t dlen)
564 int whitespace = s->whitespace;
566 int linelen = s->linelen;
567 int was_cr = s->was_cr;
569 if (!d) /* This signals EOF */
573 if (linelen > info->linemax)
574 info->linemax = linelen;
579 for (; dlen; d++, dlen--)
592 if (whitespace) info->space = 1;
593 if (dot) info->dot = 1;
594 if (linelen > info->linemax) info->linemax = linelen;
606 if (whitespace) info->space = 1;
607 if (dot) info->dot = 1;
608 if (linelen > info->linemax) info->linemax = linelen;
622 else if (ch == '\t' || ch == '\f')
627 else if (ch < 32 || ch == 127)
633 if ((ch == 'F') || (ch == 'f'))
644 if (linelen == 2 && ch != 'r') from = 0;
645 else if (linelen == 3 && ch != 'o') from = 0;
646 else if (linelen == 4)
648 if (ch == 'm') info->from = 1;
652 if (ch == ' ') whitespace++;
656 if (linelen > 1) dot = 0;
657 if (ch != ' ' && ch != '\t') whitespace = 0;
661 s->whitespace = whitespace;
663 s->linelen = linelen;
668 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
669 #define BUGGY_ICONV 1
672 * Find the best charset conversion of the file from fromcode into one
673 * of the tocodes. If successful, set *tocode and CONTENT *info and
674 * return the number of characters converted inexactly. If no
675 * conversion was possible, return -1.
677 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
678 * which would otherwise prevent us from knowing the number of inexact
679 * conversions. Where the candidate target charset is UTF-8 we avoid
680 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
681 * fails with some libraries.
683 * We assume that the output from iconv is never more than 4 times as
684 * long as the input for any pair of charsets we might be interested
687 static size_t convert_file_to (FILE *file, const char *fromcode,
688 int ncodes, const char **tocodes,
689 int *tocode, CONTENT *info)
693 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
694 ICONV_CONST char *ib, *ub;
696 size_t ibl, obl, ubl, ubl1, n, ret;
699 CONTENT_STATE *states;
702 cd1 = mutt_iconv_open ("UTF-8", fromcode, 0);
703 if (cd1 == (iconv_t)(-1))
706 cd = safe_calloc (ncodes, sizeof (iconv_t));
707 score = safe_calloc (ncodes, sizeof (size_t));
708 states = safe_calloc (ncodes, sizeof (CONTENT_STATE));
709 infos = safe_calloc (ncodes, sizeof (CONTENT));
711 for (i = 0; i < ncodes; i++)
712 if (ascii_strcasecmp (tocodes[i], "UTF-8"))
713 cd[i] = mutt_iconv_open (tocodes[i], "UTF-8", 0);
715 /* Special case for conversion to UTF-8 */
716 cd[i] = (iconv_t)(-1), score[i] = (size_t)(-1);
723 /* Try to fill input buffer */
724 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
727 /* Convert to UTF-8 */
729 ob = bufu, obl = sizeof (bufu);
730 n = iconv (cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
731 assert (n == (size_t)(-1) || !n || ICONV_NONTRANS);
732 if (n == (size_t)(-1) &&
733 ((errno != EINVAL && errno != E2BIG) || ib == bufi))
735 assert (errno == EILSEQ ||
736 (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
742 /* Convert from UTF-8 */
743 for (i = 0; i < ncodes; i++)
744 if (cd[i] != (iconv_t)(-1) && score[i] != (size_t)(-1))
746 ub = bufu, ubl = ubl1;
747 ob = bufo, obl = sizeof (bufo);
748 n = iconv (cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
749 if (n == (size_t)(-1))
751 assert (errno == E2BIG ||
752 (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
753 score[i] = (size_t)(-1);
758 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
761 else if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
762 /* Special case for conversion to UTF-8 */
763 update_content_info (&infos[i], &states[i], bufu, ubl1);
766 /* Save unused input */
767 memmove (bufi, ib, ibl);
768 else if (!ubl1 && ib < bufi + sizeof (bufi))
777 /* Find best score */
779 for (i = 0; i < ncodes; i++)
781 if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
783 /* Special case for conversion to UTF-8 */
788 else if (cd[i] == (iconv_t)(-1) || score[i] == (size_t)(-1))
790 else if (ret == (size_t)(-1) || score[i] < ret)
798 if (ret != (size_t)(-1))
800 memcpy (info, &infos[*tocode], sizeof(CONTENT));
801 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
805 for (i = 0; i < ncodes; i++)
806 if (cd[i] != (iconv_t)(-1))
818 #endif /* !HAVE_ICONV */
822 * Find the first of the fromcodes that gives a valid conversion and
823 * the best charset conversion of the file into one of the tocodes. If
824 * successful, set *fromcode and *tocode to dynamically allocated
825 * strings, set CONTENT *info, and return the number of characters
826 * converted inexactly. If no conversion was possible, return -1.
828 * Both fromcodes and tocodes may be colon-separated lists of charsets.
829 * However, if fromcode is zero then fromcodes is assumed to be the
830 * name of a single charset even if it contains a colon.
832 static size_t convert_file_from_to (FILE *file,
833 const char *fromcodes, const char *tocodes,
834 char **fromcode, char **tocode, CONTENT *info)
842 /* Count the tocodes */
844 for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
846 if ((c1 = strchr (c, ':')) == c)
852 tcode = safe_malloc (ncodes * sizeof (char *));
853 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
855 if ((c1 = strchr (c, ':')) == c)
857 tcode[i] = mutt_substrdup (c, c1);
863 /* Try each fromcode in turn */
864 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
866 if ((c1 = strchr (c, ':')) == c)
868 fcode = mutt_substrdup (c, c1);
870 ret = convert_file_to (file, fcode, ncodes, (const char **)tcode,
872 if (ret != (size_t)(-1))
884 /* There is only one fromcode */
885 ret = convert_file_to (file, fromcodes, ncodes, (const char **)tcode,
887 if (ret != (size_t)(-1))
895 for (i = 0; i < ncodes; i++)
904 * Analyze the contents of a file to determine which MIME encoding to use.
905 * Also set the body charset, sometimes, or not.
907 CONTENT *mutt_get_content_info (const char *fname, BODY *b)
920 if(b && !fname) fname = b->filename;
922 if (stat (fname, &sb) == -1)
924 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
928 if (!S_ISREG(sb.st_mode))
930 mutt_error (_("%s isn't a regular file."), fname);
934 if ((fp = fopen (fname, "r")) == NULL)
936 dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
937 fname, strerror (errno), errno));
941 info = safe_calloc (1, sizeof (CONTENT));
942 memset (&state, 0, sizeof (state));
944 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
946 char *chs = mutt_get_parameter ("charset", b->parameter);
947 char *fchs = b->use_disp ? ((FileCharset && *FileCharset) ?
948 FileCharset : Charset) : Charset;
949 if (Charset && (chs || SendCharset) &&
950 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
951 &fromcode, &tocode, info) != (size_t)(-1))
955 mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
956 mutt_set_parameter ("charset", chsbuf, &b->parameter);
958 b->file_charset = fromcode;
966 while ((r = fread (buffer, 1, sizeof(buffer), fp)))
967 update_content_info (info, &state, buffer, r);
968 update_content_info (info, &state, 0, 0);
972 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
973 mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
974 Charset && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
980 /* Given a file with path ``s'', see if there is a registered MIME type.
981 * returns the major MIME type, and copies the subtype to ``d''. First look
982 * for ~/.mime.types, then look in a system mime.types if we can find one.
983 * The longest match is used so that we can match `ps.gz' when `gz' also
987 int mutt_lookup_mime_type (BODY *att, const char *path)
991 char buf[LONG_STRING];
992 char subtype[STRING], xtype[STRING];
994 int szf, sze, cur_sze;
1002 szf = mutt_strlen (path);
1004 for (count = 0 ; count < 3 ; count++)
1007 * can't use strtok() because we use it in an inner loop below, so use
1008 * a switch statement here instead.
1013 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
1016 strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
1019 strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
1022 dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
1023 goto bye; /* shouldn't happen */
1026 if ((f = fopen (buf, "r")) != NULL)
1028 while (fgets (buf, sizeof (buf) - 1, f) != NULL)
1030 /* weed out any comments */
1031 if ((p = strchr (buf, '#')))
1034 /* remove any leading space. */
1038 /* position on the next field in this line */
1039 if ((p = strpbrk (ct, " \t")) == NULL)
1044 /* cycle through the file extensions */
1045 while ((p = strtok (p, " \t\n")))
1047 sze = mutt_strlen (p);
1048 if ((sze > cur_sze) && (szf >= sze) &&
1049 (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
1050 (szf == sze || path[szf - sze - 1] == '.'))
1052 /* get the content-type */
1054 if ((p = strchr (ct, '/')) == NULL)
1056 /* malformed line, just skip it. */
1061 for (q = p; *q && !ISSPACE (*q); q++)
1064 mutt_substrcpy (subtype, p, q, sizeof (subtype));
1066 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1067 strfcpy (xtype, ct, sizeof (xtype));
1080 if (type != TYPEOTHER || *xtype != '\0')
1083 mutt_str_replace (&att->subtype, subtype);
1084 mutt_str_replace (&att->xtype, xtype);
1090 void mutt_message_to_7bit (BODY *a, FILE *fp)
1092 char temp[_POSIX_PATH_MAX];
1098 if (!a->filename && fp)
1100 else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1102 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1108 if (stat (a->filename, &sb) == -1)
1110 mutt_perror ("stat");
1113 a->length = sb.st_size;
1117 if (!(fpout = safe_fopen (temp, "w+")))
1119 mutt_perror ("fopen");
1123 fseek (fpin, a->offset, 0);
1124 a->parts = mutt_parse_messageRFC822 (fpin, a);
1126 transform_to_7bit (a->parts, fpin);
1128 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1129 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1131 fputs ("Mime-Version: 1.0\n", fpout);
1132 mutt_write_mime_header (a->parts, fpout);
1133 fputc ('\n', fpout);
1134 mutt_write_mime_body (a->parts, fpout);
1146 a->encoding = ENC7BIT;
1147 a->d_filename = a->filename;
1148 if (a->filename && a->unlink)
1149 unlink (a->filename);
1150 a->filename = safe_strdup (temp);
1152 if(stat (a->filename, &sb) == -1)
1154 mutt_perror ("stat");
1157 a->length = sb.st_size;
1158 mutt_free_body (&a->parts);
1159 a->hdr->content = NULL;
1162 static void transform_to_7bit (BODY *a, FILE *fpin)
1164 char buff[_POSIX_PATH_MAX];
1168 memset (&s, 0, sizeof (s));
1169 for (; a; a = a->next)
1171 if (a->type == TYPEMULTIPART)
1173 if (a->encoding != ENC7BIT)
1174 a->encoding = ENC7BIT;
1176 transform_to_7bit (a->parts, fpin);
1178 else if (mutt_is_message_type(a->type, a->subtype))
1180 mutt_message_to_7bit (a, fpin);
1185 a->force_charset = 1;
1188 if ((s.fpout = safe_fopen (buff, "w")) == NULL)
1190 mutt_perror ("fopen");
1194 mutt_decode_attachment (a, &s);
1196 a->d_filename = a->filename;
1197 a->filename = safe_strdup (buff);
1199 if (stat (a->filename, &sb) == -1)
1201 mutt_perror ("stat");
1204 a->length = sb.st_size;
1206 mutt_update_encoding (a);
1207 if (a->encoding == ENC8BIT)
1208 a->encoding = ENCQUOTEDPRINTABLE;
1209 else if(a->encoding == ENCBINARY)
1210 a->encoding = ENCBASE64;
1215 /* determine which Content-Transfer-Encoding to use */
1216 static void mutt_set_encoding (BODY *b, CONTENT *info)
1218 char send_charset[SHORT_STRING];
1220 if (b->type == TYPETEXT)
1222 char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1223 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1224 b->encoding = ENCQUOTEDPRINTABLE;
1225 else if (info->hibin)
1226 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1228 b->encoding = ENC7BIT;
1230 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1232 if (info->lobin || info->hibin)
1234 if (option (OPTALLOW8BIT) && !info->lobin)
1235 b->encoding = ENC8BIT;
1237 mutt_message_to_7bit (b, NULL);
1240 b->encoding = ENC7BIT;
1242 else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1243 b->encoding = ENC7BIT;
1246 if (info->lobin || info->hibin || info->binary || info->linemax > 990
1247 || info->cr || (/* option (OPTENCODEFROM) && */ info->from))
1250 /* Determine which encoding is smaller */
1251 if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
1252 3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1253 b->encoding = ENCBASE64;
1255 b->encoding = ENCQUOTEDPRINTABLE;
1259 b->encoding = ENC7BIT;
1263 void mutt_stamp_attachment(BODY *a)
1265 a->stamp = time(NULL);
1268 /* Get a body's character set */
1270 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1274 if (b && b->type != TYPETEXT)
1278 p = mutt_get_parameter ("charset", b->parameter);
1281 mutt_canonical_charset (d, dlen, NONULL(p));
1283 strfcpy (d, "us-ascii", dlen);
1289 /* Assumes called from send mode where BODY->filename points to actual file */
1290 void mutt_update_encoding (BODY *a)
1293 char chsbuff[STRING];
1295 /* override noconv when it's us-ascii */
1296 if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1299 if (!a->force_charset && !a->noconv)
1300 mutt_delete_parameter ("charset", &a->parameter);
1302 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1305 mutt_set_encoding (a, info);
1306 mutt_stamp_attachment(a);
1313 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1315 char buffer[LONG_STRING];
1318 int cmflags, chflags;
1319 int pgp = WithCrypto? hdr->security : 0;
1323 if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1324 (hdr->security & ENCRYPT)) {
1325 if (!crypt_valid_passphrase(hdr->security))
1330 mutt_mktemp (buffer);
1331 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1334 body = mutt_new_body ();
1335 body->type = TYPEMESSAGE;
1336 body->subtype = safe_strdup ("rfc822");
1337 body->filename = safe_strdup (buffer);
1340 body->disposition = DISPINLINE;
1343 mutt_parse_mime_message (ctx, hdr);
1348 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1349 if (!attach_msg && option (OPTMIMEFORWDECODE))
1351 chflags |= CH_MIME | CH_TXTPLAIN;
1352 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1353 if ((WithCrypto & APPLICATION_PGP))
1355 if ((WithCrypto & APPLICATION_SMIME))
1356 pgp &= ~SMIMEENCRYPT;
1359 && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1361 if ((WithCrypto & APPLICATION_PGP)
1362 && mutt_is_multipart_encrypted (hdr->content))
1364 chflags |= CH_MIME | CH_NONEWLINE;
1365 cmflags = M_CM_DECODE_PGP;
1368 else if ((WithCrypto & APPLICATION_PGP)
1369 && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT))
1371 chflags |= CH_MIME | CH_TXTPLAIN;
1372 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1375 else if ((WithCrypto & APPLICATION_SMIME)
1376 && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT)
1378 chflags |= CH_MIME | CH_TXTPLAIN;
1379 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1380 pgp &= ~SMIMEENCRYPT;
1384 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1389 body->hdr = mutt_new_header();
1390 body->hdr->offset = 0;
1391 /* we don't need the user headers here */
1392 body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1394 body->hdr->security = pgp;
1395 mutt_update_encoding (body);
1396 body->parts = body->hdr->content;
1403 BODY *mutt_make_file_attach (const char *path)
1408 att = mutt_new_body ();
1409 att->filename = safe_strdup (path);
1411 /* Attempt to determine the appropriate content-type based on the filename
1417 if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER
1421 att->subtype = safe_strdup (buf);
1422 att->xtype = safe_strdup (xbuf);
1427 mutt_lookup_mime_type (att, path);
1431 if ((info = mutt_get_content_info (path, att)) == NULL)
1433 mutt_free_body (&att);
1439 if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
1442 * Statistically speaking, there should be more than 10% "lobin"
1443 * chars if this is really a binary file...
1445 att->type = TYPETEXT;
1446 att->subtype = safe_strdup ("plain");
1450 att->type = TYPEAPPLICATION;
1451 att->subtype = safe_strdup ("octet-stream");
1455 mutt_update_encoding (att);
1459 static int get_toplevel_encoding (BODY *a)
1463 for (; a; a = a->next)
1465 if (a->encoding == ENCBINARY)
1467 else if (a->encoding == ENC8BIT)
1474 BODY *mutt_make_multipart (BODY *b)
1478 new = mutt_new_body ();
1479 new->type = TYPEMULTIPART;
1480 new->subtype = safe_strdup ("mixed");
1481 new->encoding = get_toplevel_encoding (b);
1482 mutt_generate_boundary (&new->parameter);
1484 new->disposition = DISPINLINE;
1490 /* remove the multipart body if it exists */
1491 BODY *mutt_remove_multipart (BODY *b)
1500 mutt_free_body (&t);
1505 char *mutt_make_date (char *s, size_t len)
1507 time_t t = time (NULL);
1508 struct tm *l = localtime (&t);
1509 time_t tz = mutt_local_tz (t);
1513 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1514 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1515 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1516 (int) tz / 60, (int) abs (tz) % 60);
1520 /* wrapper around mutt_write_address() so we can handle very large
1521 recipient lists without needing a huge temporary buffer in memory */
1522 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1525 char buf[LONG_STRING];
1534 rfc822_write_address (buf, sizeof (buf), adr, display);
1535 len = mutt_strlen (buf);
1536 if (count && linelen + len > 74)
1539 linelen = len + 8; /* tab is usually about 8 spaces... */
1543 if (count && adr->mailbox)
1552 if (!adr->group && adr->next && adr->next->mailbox)
1563 /* arbitrary number of elements to grow the array by */
1568 /* need to write the list in reverse because they are stored in reverse order
1569 * when parsed to speed up threading
1571 void mutt_write_references (LIST *r, FILE *f)
1574 int refcnt = 0, refmax = 0;
1576 for ( ; (TrimRef == 0 || refcnt < TrimRef) && r ; r = r->next)
1578 if (refcnt == refmax)
1579 safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1583 while (refcnt-- > 0)
1586 fputs (ref[refcnt]->data, f);
1592 /* Note: all RFC2047 encoding should be done outside of this routine, except
1593 * for the "real name." This will allow this routine to be used more than
1594 * once, if necessary.
1596 * Likewise, all IDN processing should happen outside of this routine.
1598 * mode == 1 => "lite" mode (used for edit_hdrs)
1599 * mode == 0 => normal mode. write full header + MIME headers
1600 * mode == -1 => write just the envelope info (used for postponing messages)
1602 * privacy != 0 => will omit any headers which may identify the user.
1603 * Output generated is suitable for being sent through
1604 * anonymous remailer chains.
1608 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
1609 int mode, int privacy)
1611 char buffer[LONG_STRING];
1613 LIST *tmp = env->userhdrs;
1614 int has_agent = 0; /* user defined user-agent header field exists */
1617 if (!option (OPTNEWSSEND))
1619 if (mode == 0 && !privacy)
1620 fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
1622 /* OPTUSEFROM is not consulted here so that we can still write a From:
1623 * field if the user sets it with the `my_hdr' command
1625 if (env->from && !privacy)
1628 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1629 fprintf (fp, "From: %s\n", buffer);
1635 mutt_write_address_list (env->to, fp, 4, 0);
1639 if (!option (OPTNEWSSEND))
1641 fputs ("To: \n", fp);
1646 mutt_write_address_list (env->cc, fp, 4, 0);
1650 if (!option (OPTNEWSSEND))
1652 fputs ("Cc: \n", fp);
1656 if(mode != 0 || option(OPTWRITEBCC))
1658 fputs ("Bcc: ", fp);
1659 mutt_write_address_list (env->bcc, fp, 5, 0);
1664 if (!option (OPTNEWSSEND))
1666 fputs ("Bcc: \n", fp);
1669 if (env->newsgroups)
1670 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1671 else if (mode == 1 && option (OPTNEWSSEND))
1672 fputs ("Newsgroups: \n", fp);
1674 if (env->followup_to)
1675 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1676 else if (mode == 1 && option (OPTNEWSSEND))
1677 fputs ("Followup-To: \n", fp);
1679 if (env->x_comment_to)
1680 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1681 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO))
1682 fputs ("X-Comment-To: \n", fp);
1686 fprintf (fp, "Subject: %s\n", env->subject);
1688 fputs ("Subject: \n", fp);
1690 /* save message id if the user has set it */
1691 if (env->message_id && !privacy)
1692 fprintf (fp, "Message-ID: %s\n", env->message_id);
1696 fputs ("Reply-To: ", fp);
1697 mutt_write_address_list (env->reply_to, fp, 10, 0);
1700 fputs ("Reply-To: \n", fp);
1702 if (env->mail_followup_to)
1704 if (!option (OPTNEWSSEND))
1707 fputs ("Mail-Followup-To: ", fp);
1708 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1713 if (env->references)
1715 fputs ("References:", fp);
1716 mutt_write_references (env->references, fp);
1720 /* Add the MIME headers */
1721 fputs ("Mime-Version: 1.0\n", fp);
1722 mutt_write_mime_header (attach, fp);
1725 if (env->in_reply_to)
1727 fputs ("In-Reply-To:", fp);
1728 mutt_write_references (env->in_reply_to, fp);
1732 /* Add any user defined headers */
1733 for (; tmp; tmp = tmp->next)
1735 if ((p = strchr (tmp->data, ':')))
1738 if (!*p) continue; /* don't emit empty fields. */
1740 /* check to see if the user has overridden the user-agent field */
1741 if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
1748 fputs (tmp->data, fp);
1753 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent)
1757 if (OperatingSystem!=NULL) {
1758 os = OperatingSystem;
1760 if (uname(&un)==-1) {
1766 /* Add a vanity header */
1767 fprintf (fp, "User-Agent: mutt-ng %s (%s)\n", MUTT_VERSION,os);
1770 return (ferror (fp) == 0 ? 0 : -1);
1773 static void encode_headers (LIST *h)
1779 for (; h; h = h->next)
1781 if (!(p = strchr (h->data, ':')))
1786 tmp = safe_strdup (p);
1791 rfc2047_encode_string (&tmp);
1792 safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
1794 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
1800 const char *mutt_fqdn(short may_hide_host)
1804 if(Fqdn && Fqdn[0] != '@')
1808 if(may_hide_host && option(OPTHIDDENHOST))
1810 if((p = strchr(Fqdn, '.')))
1813 /* sanity check: don't hide the host if
1814 * the fqdn is something like detebe.org.
1817 if(!p || !(q = strchr(p, '.')))
1825 static char mutt_normalized_char(char c) {
1828 if (strchr(".!#$%&'*+-/=?^_`{|}~",c))
1830 return '.'; /* normalized character (we're stricter than RFC2822, 3.6.4) */
1833 static void mutt_gen_localpart(char * buf, unsigned int len, char * fmt) {
1836 char tmp[SHORT_STRING];
1849 snprintf(tmp,sizeof(tmp),"%02d",tm->tm_mday);
1850 safe_strncat(buf,len,tmp,2);
1853 snprintf(tmp,sizeof(tmp),"%02d",tm->tm_hour);
1854 safe_strncat(buf,len,tmp,2);
1857 snprintf(tmp,sizeof(tmp),"%02d",tm->tm_mon+1);
1858 safe_strncat(buf,len,tmp,2);
1861 snprintf(tmp,sizeof(tmp),"%02d",tm->tm_min);
1862 safe_strncat(buf,len,tmp,2);
1865 snprintf(tmp,sizeof(tmp),"%lo",(unsigned long)now);
1866 safe_strncat(buf,len,tmp,strlen(tmp));
1869 snprintf(tmp,sizeof(tmp),"%u",(unsigned int)getpid());
1870 safe_strncat(buf,len,tmp,strlen(tmp));
1873 snprintf(tmp,sizeof(tmp),"%c",MsgIdPfx);
1874 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1875 safe_strncat(buf,len,tmp,1);
1878 snprintf(tmp,sizeof(tmp),"%u",(unsigned int)rand());
1879 safe_strncat(buf,len,tmp,strlen(tmp));
1882 snprintf(tmp,sizeof(tmp),"%x",(unsigned int)rand());
1883 safe_strncat(buf,len,tmp,strlen(tmp));
1886 snprintf(tmp,sizeof(tmp),"%02d",tm->tm_sec);
1887 safe_strncat(buf,len,tmp,2);
1890 snprintf(tmp,sizeof(tmp),"%u",(unsigned int)now);
1891 safe_strncat(buf,len,tmp,strlen(tmp));
1894 snprintf(tmp,sizeof(tmp),"%x",(unsigned int)now);
1895 safe_strncat(buf,len,tmp,strlen(tmp));
1898 snprintf(tmp,sizeof(tmp),"%04d",tm->tm_year+1900); /* this will break in the year 10000 ;-) */
1899 safe_strncat(buf,len,tmp,4);
1902 safe_strncat(buf,len,"%",1);
1905 safe_strncat(buf,len,".",1); /* invalid formats are replaced by '.' */
1910 c = mutt_normalized_char(*fmt); /* @todo: filter out invalid characters */
1911 safe_strncat(buf,len,&c,1);
1916 char *mutt_gen_msgid (void)
1918 char buf[SHORT_STRING];
1919 char localpart[SHORT_STRING];
1920 unsigned int localpart_length;
1927 if(!(fqdn = mutt_fqdn(0)))
1928 fqdn = NONULL(Hostname);
1930 localpart_length = sizeof(buf) - strlen(fqdn) - 4; /* the 4 characters are '<', '@', '>' and '\0' */
1932 mutt_gen_localpart(localpart,localpart_length,MsgIdFormat);
1934 snprintf(buf,sizeof(buf),"<%s@%s>",localpart,fqdn);
1935 return (safe_strdup (buf));
1938 static RETSIGTYPE alarm_handler (int sig)
1943 /* invoke sendmail in a subshell
1944 path (in) path to program to execute
1945 args (in) arguments to pass to program
1946 msg (in) temp file containing message to send
1947 tempfile (out) if sendmail is put in the background, this points
1948 to the temporary file containing the stdout of the
1951 send_msg (const char *path, char **args, const char *msg, char **tempfile)
1957 mutt_block_signals_system ();
1960 /* we also don't want to be stopped right now */
1961 sigaddset (&set, SIGTSTP);
1962 sigprocmask (SIG_BLOCK, &set, NULL);
1964 if (SendmailWait >= 0)
1966 char tmp[_POSIX_PATH_MAX];
1969 *tempfile = safe_strdup (tmp);
1972 if ((pid = fork ()) == 0)
1974 struct sigaction act, oldalrm;
1976 /* save parent's ID before setsid() */
1979 /* we want the delivery to continue even after the main process dies,
1980 * so we put ourselves into another session right away
1984 /* next we close all open files */
1985 #if defined(OPEN_MAX)
1986 for (fd = 0; fd < OPEN_MAX; fd++)
1988 #elif defined(_POSIX_OPEN_MAX)
1989 for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1997 /* now the second fork() */
1998 if ((pid = fork ()) == 0)
2000 /* "msg" will be opened as stdin */
2001 if (open (msg, O_RDONLY, 0) < 0)
2008 if (SendmailWait >= 0)
2010 /* *tempfile will be opened as stdout */
2011 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2013 /* redirect stderr to *tempfile too */
2019 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
2021 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
2035 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
2036 * SendmailWait = 0: wait forever
2037 * SendmailWait < 0: don't wait
2039 if (SendmailWait > 0)
2042 act.sa_handler = alarm_handler;
2044 /* need to make sure waitpid() is interrupted on SIGALRM */
2045 act.sa_flags = SA_INTERRUPT;
2049 sigemptyset (&act.sa_mask);
2050 sigaction (SIGALRM, &act, &oldalrm);
2051 alarm (SendmailWait);
2053 else if (SendmailWait < 0)
2054 _exit (0xff & EX_OK);
2056 if (waitpid (pid, &st, 0) > 0)
2058 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
2059 if (SendmailWait && st == (0xff & EX_OK))
2061 unlink (*tempfile); /* no longer needed */
2067 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
2069 if (SendmailWait > 0)
2076 /* reset alarm; not really needed, but... */
2078 sigaction (SIGALRM, &oldalrm, NULL);
2080 if (kill (ppid, 0) == -1 && errno == ESRCH)
2082 /* the parent is already dead */
2090 sigprocmask (SIG_UNBLOCK, &set, NULL);
2092 if (pid != -1 && waitpid (pid, &st, 0) > 0)
2093 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
2095 st = S_ERR; /* error */
2097 mutt_unblock_signals_system (1);
2103 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
2105 for (; addr; addr = addr->next)
2107 /* weed out group mailboxes, since those are for display only */
2108 if (addr->mailbox && !addr->group)
2110 if (*argslen == *argsmax)
2111 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2112 args[(*argslen)++] = addr->mailbox;
2119 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2121 if (*argslen == *argsmax)
2122 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2123 args[(*argslen)++] = s;
2132 for(i = 0; sysexits_h[i].str; i++)
2134 if(e == sysexits_h[i].v)
2138 return sysexits_h[i].str;
2143 mutt_invoke_sendmail (ADDRESS *from, /* the sender */
2144 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2145 const char *msg, /* file containing message */
2146 int eightbit) /* message contains 8bit chars */
2148 char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
2150 size_t argslen = 0, argsmax = 0;
2154 if (option (OPTNEWSSEND))
2156 char cmd[LONG_STRING];
2158 mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0, 0);
2161 i = nntp_post (msg);
2166 s = safe_strdup (cmd);
2170 s = safe_strdup (Sendmail);
2174 while ((ps = strtok (ps, " ")))
2176 if (argslen == argsmax)
2177 safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2180 args[argslen++] = ps;
2183 path = safe_strdup (ps);
2184 ps = strrchr (ps, '/');
2189 args[argslen++] = ps;
2196 if (!option (OPTNEWSSEND))
2199 if (eightbit && option (OPTUSE8BITMIME))
2200 args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2202 if (option (OPTENVFROM) && from && !from->next)
2204 args = add_option (args, &argslen, &argsmax, "-f");
2205 args = add_args (args, &argslen, &argsmax, from);
2209 args = add_option (args, &argslen, &argsmax, "-N");
2210 args = add_option (args, &argslen, &argsmax, DsnNotify);
2214 args = add_option (args, &argslen, &argsmax, "-R");
2215 args = add_option (args, &argslen, &argsmax, DsnReturn);
2217 args = add_option (args, &argslen, &argsmax, "--");
2218 args = add_args (args, &argslen, &argsmax, to);
2219 args = add_args (args, &argslen, &argsmax, cc);
2220 args = add_args (args, &argslen, &argsmax, bcc);
2225 if (argslen == argsmax)
2226 safe_realloc (&args, sizeof (char *) * (++argsmax));
2228 args[argslen++] = NULL;
2230 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff))
2234 const char *e = strsysexit (i);
2237 mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2242 if (stat (childout, &st) == 0 && st.st_size > 0)
2243 mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2255 if (i == (EX_OK & 0xff))
2257 else if (i == S_BKG)
2265 mutt_invoke_mta (ADDRESS *from, /* the sender */
2266 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2267 const char *msg, /* file containing message */
2268 int eightbit) /* message contains 8bit chars */
2272 return mutt_invoke_libesmtp(from, to, cc, bcc, msg, eightbit);
2275 return mutt_invoke_sendmail(from, to, cc, bcc, msg, eightbit);
2278 /* appends string 'b' to string 'a', and returns the pointer to the new
2280 char *mutt_append_string (char *a, const char *b)
2282 size_t la = mutt_strlen (a);
2283 safe_realloc (&a, la + mutt_strlen (b) + 1);
2284 strcpy (a + la, b); /* __STRCPY_CHECKED__ */
2288 /* returns 1 if char `c' needs to be quoted to protect from shell
2289 interpretation when executing commands in a subshell */
2290 #define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
2292 /* returns 1 if string `s' contains characters which could cause problems
2293 when used on a command line to execute a command */
2294 int mutt_needs_quote (const char *s)
2298 if (INVALID_CHAR (*s))
2305 /* Quote a string to prevent shell escapes when this string is used on the
2306 command line to send mail. */
2307 char *mutt_quote_string (const char *s)
2312 rlen = mutt_strlen (s) + 3;
2313 pr = r = (char *) safe_malloc (rlen);
2317 if (INVALID_CHAR (*s))
2320 safe_realloc (&r, ++rlen);
2331 /* For postponing (!final) do the necessary encodings only */
2332 void mutt_prepare_envelope (ENVELOPE *env, int final)
2334 char buffer[LONG_STRING];
2338 if (env->bcc && !(env->to || env->cc))
2340 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2341 * recipients if there is no To: or Cc: field, so attempt to suppress
2342 * it by using an empty To: field.
2344 env->to = rfc822_new_address ();
2346 env->to->next = rfc822_new_address ();
2349 rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2352 env->to->mailbox = safe_strdup (buffer);
2355 mutt_set_followup_to (env);
2357 if (!env->message_id)
2358 env->message_id = mutt_gen_msgid ();
2361 /* Take care of 8-bit => 7-bit conversion. */
2362 rfc2047_encode_adrlist (env->to, "To");
2363 rfc2047_encode_adrlist (env->cc, "Cc");
2364 rfc2047_encode_adrlist (env->bcc, "Bcc");
2365 rfc2047_encode_adrlist (env->from, "From");
2366 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2367 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2371 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2374 rfc2047_encode_string (&env->subject);
2376 encode_headers (env->userhdrs);
2379 void mutt_unprepare_envelope (ENVELOPE *env)
2383 for (item = env->userhdrs; item; item = item->next)
2384 rfc2047_decode (&item->data);
2386 rfc822_free_address (&env->mail_followup_to);
2388 /* back conversions */
2389 rfc2047_decode_adrlist (env->to);
2390 rfc2047_decode_adrlist (env->cc);
2391 rfc2047_decode_adrlist (env->bcc);
2392 rfc2047_decode_adrlist (env->from);
2393 rfc2047_decode_adrlist (env->reply_to);
2394 rfc2047_decode (&env->subject);
2397 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2402 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2403 MESSAGE *msg = NULL;
2407 /* Try to bounce each message out, aborting if we get any failures. */
2408 for (i=0; i<Context->msgcount; i++)
2409 if (Context->hdrs[i]->tagged)
2410 ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2414 /* If we failed to open a message, return with error */
2415 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2418 if (!fp) fp = msg->fp;
2420 mutt_mktemp (tempfile);
2421 if ((f = safe_fopen (tempfile, "w")) != NULL)
2423 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2425 if (!option (OPTBOUNCEDELIVERED))
2426 ch_flags |= CH_WEED_DELIVERED;
2428 fseek (fp, h->offset, 0);
2429 fprintf (f, "Resent-From: %s", resent_from);
2430 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2431 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2432 fputs ("Resent-To: ", f);
2433 mutt_write_address_list (to, f, 11, 0);
2434 mutt_copy_header (fp, h, f, ch_flags, NULL);
2436 mutt_copy_bytes (fp, f, h->content->length);
2439 ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2440 h->content->encoding == ENC8BIT);
2444 mx_close_message (&msg);
2449 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2452 const char *fqdn = mutt_fqdn (1);
2453 char resent_from[STRING];
2457 resent_from[0] = '\0';
2458 from = mutt_default_from ();
2461 rfc822_qualify (from, fqdn);
2463 rfc2047_encode_adrlist (from, "Resent-From");
2464 if (mutt_addrlist_to_idna (from, &err))
2466 mutt_error (_("Bad IDN %s while preparing resent-from."),
2470 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2473 unset_option (OPTNEWSSEND);
2476 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2478 rfc822_free_address (&from);
2484 /* given a list of addresses, return a list of unique addresses */
2485 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2487 ADDRESS *top = addr;
2488 ADDRESS **last = ⊤
2494 for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next)
2496 if (tmp->mailbox && addr->mailbox &&
2497 !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2506 dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
2512 rfc822_free_address(&addr);
2526 static void set_noconv_flags (BODY *b, short flag)
2528 for(; b; b = b->next)
2530 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2531 set_noconv_flags (b->parts, flag);
2532 else if (b->type == TYPETEXT && b->noconv)
2535 mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2537 mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2542 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc)
2546 char tempfile[_POSIX_PATH_MAX];
2547 FILE *tempfp = NULL;
2551 set_noconv_flags (hdr->content, 1);
2553 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
2555 dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2560 /* We need to add a Content-Length field to avoid problems where a line in
2561 * the message body begins with "From "
2563 if (f.magic == M_MMDF || f.magic == M_MBOX)
2565 mutt_mktemp (tempfile);
2566 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2568 mutt_perror (tempfile);
2569 mx_close_mailbox (&f, NULL);
2574 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2575 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
2577 mx_close_mailbox (&f, NULL);
2581 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2582 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2584 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 0);
2586 /* (postponment) if this was a reply of some sort, <msgid> contians the
2587 * Message-ID: of message replied to. Save it using a special X-Mutt-
2588 * header so it can be picked up if the message is recalled at a later
2589 * point in time. This will allow the message to be marked as replied if
2590 * the same mailbox is still open.
2593 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2595 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2596 * it can be picked up when the message is recalled
2599 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2600 fprintf (msg->fp, "Status: RO\n");
2604 /* (postponment) if the mail is to be signed or encrypted, save this info */
2605 if ((WithCrypto & APPLICATION_PGP)
2606 && post && (hdr->security & APPLICATION_PGP))
2608 fputs ("X-Mutt-PGP: ", msg->fp);
2609 if (hdr->security & ENCRYPT)
2610 fputc ('E', msg->fp);
2611 if (hdr->security & SIGN)
2613 fputc ('S', msg->fp);
2614 if (PgpSignAs && *PgpSignAs)
2615 fprintf (msg->fp, "<%s>", PgpSignAs);
2617 if (hdr->security & INLINE)
2618 fputc ('I', msg->fp);
2619 fputc ('\n', msg->fp);
2622 /* (postponment) if the mail is to be signed or encrypted, save this info */
2623 if ((WithCrypto & APPLICATION_SMIME)
2624 && post && (hdr->security & APPLICATION_SMIME))
2626 fputs ("X-Mutt-SMIME: ", msg->fp);
2627 if (hdr->security & ENCRYPT) {
2628 fputc ('E', msg->fp);
2629 if (SmimeCryptAlg && *SmimeCryptAlg)
2630 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2632 if (hdr->security & SIGN) {
2633 fputc ('S', msg->fp);
2634 if (SmimeDefaultKey && *SmimeDefaultKey)
2635 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2637 if (hdr->security & INLINE)
2638 fputc ('I', msg->fp);
2639 fputc ('\n', msg->fp);
2643 /* (postponement) if the mail is to be sent through a mixmaster
2644 * chain, save that information
2647 if (post && hdr->chain && hdr->chain)
2651 fputs ("X-Mutt-Mix:", msg->fp);
2652 for (p = hdr->chain; p; p = p->next)
2653 fprintf (msg->fp, " %s", (char *) p->data);
2655 fputc ('\n', msg->fp);
2661 char sasha[LONG_STRING];
2664 mutt_write_mime_body (hdr->content, tempfp);
2666 /* make sure the last line ends with a newline. Emacs doesn't ensure
2667 * this will happen, and it can cause problems parsing the mailbox
2670 fseek (tempfp, -1, 2);
2671 if (fgetc (tempfp) != '\n')
2673 fseek (tempfp, 0, 2);
2674 fputc ('\n', tempfp);
2678 if (ferror (tempfp))
2680 dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2683 mx_commit_message (msg, &f); /* XXX - really? */
2684 mx_close_message (&msg);
2685 mx_close_mailbox (&f, NULL);
2689 /* count the number of lines */
2691 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2693 fprintf (msg->fp, "Content-Length: %ld\n", (long) ftell (tempfp));
2694 fprintf (msg->fp, "Lines: %d\n\n", lines);
2696 /* copy the body and clean up */
2698 r = mutt_copy_stream (tempfp, msg->fp);
2699 if (fclose (tempfp) != 0)
2701 /* if there was an error, leave the temp version */
2707 fputc ('\n', msg->fp); /* finish off the header */
2708 r = mutt_write_mime_body (hdr->content, msg->fp);
2711 if (mx_commit_message (msg, &f) != 0)
2713 mx_close_message (&msg);
2714 mx_close_mailbox (&f, NULL);
2717 set_noconv_flags (hdr->content, 0);