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, 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)
919 if(b && !fname) fname = b->filename;
921 if (stat (fname, &sb) == -1)
923 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
927 if (!S_ISREG(sb.st_mode))
929 mutt_error (_("%s isn't a regular file."), fname);
933 if ((fp = fopen (fname, "r")) == NULL)
935 dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
936 fname, strerror (errno), errno));
940 info = safe_calloc (1, sizeof (CONTENT));
941 memset (&state, 0, sizeof (state));
943 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
945 char *chs = mutt_get_parameter ("charset", b->parameter);
946 if (Charset && (chs || SendCharset) &&
947 convert_file_from_to (fp, Charset, chs ? chs : SendCharset,
948 0, &tocode, info) != (size_t)(-1))
952 mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
953 mutt_set_parameter ("charset", chsbuf, &b->parameter);
962 while ((r = fread (buffer, 1, sizeof(buffer), fp)))
963 update_content_info (info, &state, buffer, r);
964 update_content_info (info, &state, 0, 0);
968 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
969 mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
970 Charset && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
976 /* Given a file with path ``s'', see if there is a registered MIME type.
977 * returns the major MIME type, and copies the subtype to ``d''. First look
978 * for ~/.mime.types, then look in a system mime.types if we can find one.
979 * The longest match is used so that we can match `ps.gz' when `gz' also
983 int mutt_lookup_mime_type (BODY *att, const char *path)
987 char buf[LONG_STRING];
988 char subtype[STRING], xtype[STRING];
990 int szf, sze, cur_sze;
998 szf = mutt_strlen (path);
1000 for (count = 0 ; count < 3 ; count++)
1003 * can't use strtok() because we use it in an inner loop below, so use
1004 * a switch statement here instead.
1009 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
1012 strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
1015 strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
1018 dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
1019 goto bye; /* shouldn't happen */
1022 if ((f = fopen (buf, "r")) != NULL)
1024 while (fgets (buf, sizeof (buf) - 1, f) != NULL)
1026 /* weed out any comments */
1027 if ((p = strchr (buf, '#')))
1030 /* remove any leading space. */
1034 /* position on the next field in this line */
1035 if ((p = strpbrk (ct, " \t")) == NULL)
1040 /* cycle through the file extensions */
1041 while ((p = strtok (p, " \t\n")))
1043 sze = mutt_strlen (p);
1044 if ((sze > cur_sze) && (szf >= sze) &&
1045 (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
1046 (szf == sze || path[szf - sze - 1] == '.'))
1048 /* get the content-type */
1050 if ((p = strchr (ct, '/')) == NULL)
1052 /* malformed line, just skip it. */
1057 for (q = p; *q && !ISSPACE (*q); q++)
1060 mutt_substrcpy (subtype, p, q, sizeof (subtype));
1062 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1063 strfcpy (xtype, ct, sizeof (xtype));
1076 if (type != TYPEOTHER || *xtype != '\0')
1079 mutt_str_replace (&att->subtype, subtype);
1080 mutt_str_replace (&att->xtype, xtype);
1086 void mutt_message_to_7bit (BODY *a, FILE *fp)
1088 char temp[_POSIX_PATH_MAX];
1094 if (!a->filename && fp)
1096 else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1098 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1104 if (stat (a->filename, &sb) == -1)
1106 mutt_perror ("stat");
1109 a->length = sb.st_size;
1113 if (!(fpout = safe_fopen (temp, "w+")))
1115 mutt_perror ("fopen");
1119 fseek (fpin, a->offset, 0);
1120 a->parts = mutt_parse_messageRFC822 (fpin, a);
1122 transform_to_7bit (a->parts, fpin);
1124 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1125 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1127 fputs ("Mime-Version: 1.0\n", fpout);
1128 mutt_write_mime_header (a->parts, fpout);
1129 fputc ('\n', fpout);
1130 mutt_write_mime_body (a->parts, fpout);
1142 a->encoding = ENC7BIT;
1143 a->d_filename = a->filename;
1144 if (a->filename && a->unlink)
1145 unlink (a->filename);
1146 a->filename = safe_strdup (temp);
1148 if(stat (a->filename, &sb) == -1)
1150 mutt_perror ("stat");
1153 a->length = sb.st_size;
1154 mutt_free_body (&a->parts);
1155 a->hdr->content = NULL;
1158 static void transform_to_7bit (BODY *a, FILE *fpin)
1160 char buff[_POSIX_PATH_MAX];
1164 memset (&s, 0, sizeof (s));
1165 for (; a; a = a->next)
1167 if (a->type == TYPEMULTIPART)
1169 if (a->encoding != ENC7BIT)
1170 a->encoding = ENC7BIT;
1172 transform_to_7bit (a->parts, fpin);
1174 else if (mutt_is_message_type(a->type, a->subtype))
1176 mutt_message_to_7bit (a, fpin);
1181 a->force_charset = 1;
1184 if ((s.fpout = safe_fopen (buff, "w")) == NULL)
1186 mutt_perror ("fopen");
1190 mutt_decode_attachment (a, &s);
1192 a->d_filename = a->filename;
1193 a->filename = safe_strdup (buff);
1195 if (stat (a->filename, &sb) == -1)
1197 mutt_perror ("stat");
1200 a->length = sb.st_size;
1202 mutt_update_encoding (a);
1203 if (a->encoding == ENC8BIT)
1204 a->encoding = ENCQUOTEDPRINTABLE;
1205 else if(a->encoding == ENCBINARY)
1206 a->encoding = ENCBASE64;
1211 /* determine which Content-Transfer-Encoding to use */
1212 static void mutt_set_encoding (BODY *b, CONTENT *info)
1214 char send_charset[SHORT_STRING];
1216 if (b->type == TYPETEXT)
1218 char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1219 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1220 b->encoding = ENCQUOTEDPRINTABLE;
1221 else if (info->hibin)
1222 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1224 b->encoding = ENC7BIT;
1226 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1228 if (info->lobin || info->hibin)
1230 if (option (OPTALLOW8BIT) && !info->lobin)
1231 b->encoding = ENC8BIT;
1233 mutt_message_to_7bit (b, NULL);
1236 b->encoding = ENC7BIT;
1238 else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1239 b->encoding = ENC7BIT;
1242 if (info->lobin || info->hibin || info->binary || info->linemax > 990
1243 || info->cr || (/* option (OPTENCODEFROM) && */ info->from))
1246 /* Determine which encoding is smaller */
1247 if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
1248 3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1249 b->encoding = ENCBASE64;
1251 b->encoding = ENCQUOTEDPRINTABLE;
1255 b->encoding = ENC7BIT;
1259 void mutt_stamp_attachment(BODY *a)
1261 a->stamp = time(NULL);
1264 /* Get a body's character set */
1266 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1270 if (b && b->type != TYPETEXT)
1274 p = mutt_get_parameter ("charset", b->parameter);
1277 mutt_canonical_charset (d, dlen, NONULL(p));
1279 strfcpy (d, "us-ascii", dlen);
1285 /* Assumes called from send mode where BODY->filename points to actual file */
1286 void mutt_update_encoding (BODY *a)
1289 char chsbuff[STRING];
1291 /* override noconv when it's us-ascii */
1292 if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1295 if (!a->force_charset && !a->noconv)
1296 mutt_delete_parameter ("charset", &a->parameter);
1298 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1301 mutt_set_encoding (a, info);
1302 mutt_stamp_attachment(a);
1309 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1311 char buffer[LONG_STRING];
1314 int cmflags, chflags;
1315 int pgp = WithCrypto? hdr->security : 0;
1319 if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1320 (hdr->security & ENCRYPT)) {
1321 if (!crypt_valid_passphrase(hdr->security))
1326 mutt_mktemp (buffer);
1327 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1330 body = mutt_new_body ();
1331 body->type = TYPEMESSAGE;
1332 body->subtype = safe_strdup ("rfc822");
1333 body->filename = safe_strdup (buffer);
1336 body->disposition = DISPINLINE;
1338 mutt_parse_mime_message (ctx, hdr);
1343 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1344 if (!attach_msg && option (OPTMIMEFORWDECODE))
1346 chflags |= CH_MIME | CH_TXTPLAIN;
1347 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1348 if ((WithCrypto & APPLICATION_PGP))
1350 if ((WithCrypto & APPLICATION_SMIME))
1351 pgp &= ~SMIMEENCRYPT;
1354 && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1356 if ((WithCrypto & APPLICATION_PGP)
1357 && mutt_is_multipart_encrypted (hdr->content))
1359 chflags |= CH_MIME | CH_NONEWLINE;
1360 cmflags = M_CM_DECODE_PGP;
1363 else if ((WithCrypto & APPLICATION_PGP)
1364 && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT))
1366 chflags |= CH_MIME | CH_TXTPLAIN;
1367 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1370 else if ((WithCrypto & APPLICATION_SMIME)
1371 && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT)
1373 chflags |= CH_MIME | CH_TXTPLAIN;
1374 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1375 pgp &= ~SMIMEENCRYPT;
1379 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1384 body->hdr = mutt_new_header();
1385 body->hdr->offset = 0;
1386 /* we don't need the user headers here */
1387 body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1389 body->hdr->security = pgp;
1390 mutt_update_encoding (body);
1391 body->parts = body->hdr->content;
1398 BODY *mutt_make_file_attach (const char *path)
1403 att = mutt_new_body ();
1404 att->filename = safe_strdup (path);
1406 /* Attempt to determine the appropriate content-type based on the filename
1412 if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER
1416 att->subtype = safe_strdup (buf);
1417 att->xtype = safe_strdup (xbuf);
1422 mutt_lookup_mime_type (att, path);
1426 if ((info = mutt_get_content_info (path, att)) == NULL)
1428 mutt_free_body (&att);
1434 if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
1437 * Statistically speaking, there should be more than 10% "lobin"
1438 * chars if this is really a binary file...
1440 att->type = TYPETEXT;
1441 att->subtype = safe_strdup ("plain");
1445 att->type = TYPEAPPLICATION;
1446 att->subtype = safe_strdup ("octet-stream");
1450 mutt_update_encoding (att);
1454 static int get_toplevel_encoding (BODY *a)
1458 for (; a; a = a->next)
1460 if (a->encoding == ENCBINARY)
1462 else if (a->encoding == ENC8BIT)
1469 BODY *mutt_make_multipart (BODY *b)
1473 new = mutt_new_body ();
1474 new->type = TYPEMULTIPART;
1475 new->subtype = safe_strdup ("mixed");
1476 new->encoding = get_toplevel_encoding (b);
1477 mutt_generate_boundary (&new->parameter);
1479 new->disposition = DISPINLINE;
1485 /* remove the multipart body if it exists */
1486 BODY *mutt_remove_multipart (BODY *b)
1495 mutt_free_body (&t);
1500 char *mutt_make_date (char *s, size_t len)
1502 time_t t = time (NULL);
1503 struct tm *l = localtime (&t);
1504 time_t tz = mutt_local_tz (t);
1508 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1509 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1510 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1511 (int) tz / 60, (int) abs (tz) % 60);
1515 /* wrapper around mutt_write_address() so we can handle very large
1516 recipient lists without needing a huge temporary buffer in memory */
1517 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1520 char buf[LONG_STRING];
1529 rfc822_write_address (buf, sizeof (buf), adr, display);
1530 len = mutt_strlen (buf);
1531 if (count && linelen + len > 74)
1534 linelen = len + 8; /* tab is usually about 8 spaces... */
1538 if (count && adr->mailbox)
1547 if (!adr->group && adr->next && adr->next->mailbox)
1558 /* arbitrary number of elements to grow the array by */
1563 /* need to write the list in reverse because they are stored in reverse order
1564 * when parsed to speed up threading
1566 void mutt_write_references (LIST *r, FILE *f)
1569 int refcnt = 0, refmax = 0;
1571 for ( ; (TrimRef == 0 || refcnt < TrimRef) && r ; r = r->next)
1573 if (refcnt == refmax)
1574 safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1578 while (refcnt-- > 0)
1581 fputs (ref[refcnt]->data, f);
1587 /* Note: all RFC2047 encoding should be done outside of this routine, except
1588 * for the "real name." This will allow this routine to be used more than
1589 * once, if necessary.
1591 * Likewise, all IDN processing should happen outside of this routine.
1593 * mode == 1 => "lite" mode (used for edit_hdrs)
1594 * mode == 0 => normal mode. write full header + MIME headers
1595 * mode == -1 => write just the envelope info (used for postponing messages)
1597 * privacy != 0 => will omit any headers which may identify the user.
1598 * Output generated is suitable for being sent through
1599 * anonymous remailer chains.
1603 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
1604 int mode, int privacy)
1606 char buffer[LONG_STRING];
1608 LIST *tmp = env->userhdrs;
1609 int has_agent = 0; /* user defined user-agent header field exists */
1612 if (!option (OPTNEWSSEND))
1614 if (mode == 0 && !privacy)
1615 fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
1617 /* OPTUSEFROM is not consulted here so that we can still write a From:
1618 * field if the user sets it with the `my_hdr' command
1620 if (env->from && !privacy)
1623 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1624 fprintf (fp, "From: %s\n", buffer);
1630 mutt_write_address_list (env->to, fp, 4, 0);
1634 if (!option (OPTNEWSSEND))
1636 fputs ("To: \n", fp);
1641 mutt_write_address_list (env->cc, fp, 4, 0);
1645 if (!option (OPTNEWSSEND))
1647 fputs ("Cc: \n", fp);
1651 if(mode != 0 || option(OPTWRITEBCC))
1653 fputs ("Bcc: ", fp);
1654 mutt_write_address_list (env->bcc, fp, 5, 0);
1659 if (!option (OPTNEWSSEND))
1661 fputs ("Bcc: \n", fp);
1664 if (env->newsgroups)
1665 fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
1666 else if (mode == 1 && option (OPTNEWSSEND))
1667 fputs ("Newsgroups: \n", fp);
1669 if (env->followup_to)
1670 fprintf (fp, "Followup-To: %s\n", env->followup_to);
1671 else if (mode == 1 && option (OPTNEWSSEND))
1672 fputs ("Followup-To: \n", fp);
1674 if (env->x_comment_to)
1675 fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
1676 else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO))
1677 fputs ("X-Comment-To: \n", fp);
1681 fprintf (fp, "Subject: %s\n", env->subject);
1683 fputs ("Subject: \n", fp);
1685 /* save message id if the user has set it */
1686 if (env->message_id && !privacy)
1687 fprintf (fp, "Message-ID: %s\n", env->message_id);
1691 fputs ("Reply-To: ", fp);
1692 mutt_write_address_list (env->reply_to, fp, 10, 0);
1695 fputs ("Reply-To: \n", fp);
1697 if (env->mail_followup_to)
1699 if (!option (OPTNEWSSEND))
1702 fputs ("Mail-Followup-To: ", fp);
1703 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1708 if (env->references)
1710 fputs ("References:", fp);
1711 mutt_write_references (env->references, fp);
1715 /* Add the MIME headers */
1716 fputs ("Mime-Version: 1.0\n", fp);
1717 mutt_write_mime_header (attach, fp);
1720 if (env->in_reply_to)
1722 fputs ("In-Reply-To:", fp);
1723 mutt_write_references (env->in_reply_to, fp);
1727 /* Add any user defined headers */
1728 for (; tmp; tmp = tmp->next)
1730 if ((p = strchr (tmp->data, ':')))
1733 if (!*p) continue; /* don't emit empty fields. */
1735 /* check to see if the user has overridden the user-agent field */
1736 if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
1743 fputs (tmp->data, fp);
1748 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent)
1752 if (OperatingSystem!=NULL) {
1753 os = OperatingSystem;
1755 if (uname(&un)==-1) {
1761 /* Add a vanity header */
1762 fprintf (fp, "User-Agent: mutt-ng %s (%s)\n", MUTT_VERSION,os);
1765 return (ferror (fp) == 0 ? 0 : -1);
1768 static void encode_headers (LIST *h)
1774 for (; h; h = h->next)
1776 if (!(p = strchr (h->data, ':')))
1781 tmp = safe_strdup (p);
1786 rfc2047_encode_string (&tmp);
1787 safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
1789 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
1795 const char *mutt_fqdn(short may_hide_host)
1799 if(Fqdn && Fqdn[0] != '@')
1803 if(may_hide_host && option(OPTHIDDENHOST))
1805 if((p = strchr(Fqdn, '.')))
1808 /* sanity check: don't hide the host if
1809 * the fqdn is something like detebe.org.
1812 if(!p || !(q = strchr(p, '.')))
1820 static char mutt_normalized_char(char c) {
1823 if (strchr(".!#$%&'*+-/=?^_`{|}~",c))
1825 return '.'; /* normalized character (we're stricter than RFC2822, 3.6.4) */
1828 static void mutt_gen_localpart(char * buf, unsigned int len, char * fmt) {
1831 char tmp[SHORT_STRING];
1844 snprintf(tmp,sizeof(tmp),"%02d",tm->tm_mday);
1845 safe_strncat(buf,len,tmp,2);
1848 snprintf(tmp,sizeof(tmp),"%02d",tm->tm_hour);
1849 safe_strncat(buf,len,tmp,2);
1852 snprintf(tmp,sizeof(tmp),"%02d",tm->tm_mon+1);
1853 safe_strncat(buf,len,tmp,2);
1856 snprintf(tmp,sizeof(tmp),"%02d",tm->tm_min);
1857 safe_strncat(buf,len,tmp,2);
1860 snprintf(tmp,sizeof(tmp),"%lo",(unsigned long)now);
1861 safe_strncat(buf,len,tmp,strlen(tmp));
1864 snprintf(tmp,sizeof(tmp),"%u",(unsigned int)getpid());
1865 safe_strncat(buf,len,tmp,strlen(tmp));
1868 snprintf(tmp,sizeof(tmp),"%c",MsgIdPfx);
1869 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
1870 safe_strncat(buf,len,tmp,1);
1873 snprintf(tmp,sizeof(tmp),"%u",(unsigned int)rand());
1874 safe_strncat(buf,len,tmp,strlen(tmp));
1877 snprintf(tmp,sizeof(tmp),"%x",(unsigned int)rand());
1878 safe_strncat(buf,len,tmp,strlen(tmp));
1881 snprintf(tmp,sizeof(tmp),"%02d",tm->tm_sec);
1882 safe_strncat(buf,len,tmp,2);
1885 snprintf(tmp,sizeof(tmp),"%u",(unsigned int)now);
1886 safe_strncat(buf,len,tmp,strlen(tmp));
1889 snprintf(tmp,sizeof(tmp),"%x",(unsigned int)now);
1890 safe_strncat(buf,len,tmp,strlen(tmp));
1893 snprintf(tmp,sizeof(tmp),"%04d",tm->tm_year+1900); /* this will break in the year 10000 ;-) */
1894 safe_strncat(buf,len,tmp,4);
1897 safe_strncat(buf,len,"%",1);
1900 safe_strncat(buf,len,".",1); /* invalid formats are replaced by '.' */
1905 c = mutt_normalized_char(*fmt); /* @todo: filter out invalid characters */
1906 safe_strncat(buf,len,&c,1);
1911 char *mutt_gen_msgid (void)
1913 char buf[SHORT_STRING];
1914 char localpart[SHORT_STRING];
1915 unsigned int localpart_length;
1922 if(!(fqdn = mutt_fqdn(0)))
1923 fqdn = NONULL(Hostname);
1925 localpart_length = sizeof(buf) - strlen(fqdn) - 4; /* the 4 characters are '<', '@', '>' and '\0' */
1927 mutt_gen_localpart(localpart,localpart_length,MsgIdFormat);
1929 snprintf(buf,sizeof(buf),"<%s@%s>",localpart,fqdn);
1930 return (safe_strdup (buf));
1933 static RETSIGTYPE alarm_handler (int sig)
1938 /* invoke sendmail in a subshell
1939 path (in) path to program to execute
1940 args (in) arguments to pass to program
1941 msg (in) temp file containing message to send
1942 tempfile (out) if sendmail is put in the background, this points
1943 to the temporary file containing the stdout of the
1946 send_msg (const char *path, char **args, const char *msg, char **tempfile)
1952 mutt_block_signals_system ();
1955 /* we also don't want to be stopped right now */
1956 sigaddset (&set, SIGTSTP);
1957 sigprocmask (SIG_BLOCK, &set, NULL);
1959 if (SendmailWait >= 0)
1961 char tmp[_POSIX_PATH_MAX];
1964 *tempfile = safe_strdup (tmp);
1967 if ((pid = fork ()) == 0)
1969 struct sigaction act, oldalrm;
1971 /* save parent's ID before setsid() */
1974 /* we want the delivery to continue even after the main process dies,
1975 * so we put ourselves into another session right away
1979 /* next we close all open files */
1980 #if defined(OPEN_MAX)
1981 for (fd = 0; fd < OPEN_MAX; fd++)
1983 #elif defined(_POSIX_OPEN_MAX)
1984 for (fd = 0; fd < _POSIX_OPEN_MAX; fd++)
1992 /* now the second fork() */
1993 if ((pid = fork ()) == 0)
1995 /* "msg" will be opened as stdin */
1996 if (open (msg, O_RDONLY, 0) < 0)
2003 if (SendmailWait >= 0)
2005 /* *tempfile will be opened as stdout */
2006 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2008 /* redirect stderr to *tempfile too */
2014 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
2016 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
2030 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
2031 * SendmailWait = 0: wait forever
2032 * SendmailWait < 0: don't wait
2034 if (SendmailWait > 0)
2037 act.sa_handler = alarm_handler;
2039 /* need to make sure waitpid() is interrupted on SIGALRM */
2040 act.sa_flags = SA_INTERRUPT;
2044 sigemptyset (&act.sa_mask);
2045 sigaction (SIGALRM, &act, &oldalrm);
2046 alarm (SendmailWait);
2048 else if (SendmailWait < 0)
2049 _exit (0xff & EX_OK);
2051 if (waitpid (pid, &st, 0) > 0)
2053 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
2054 if (SendmailWait && st == (0xff & EX_OK))
2056 unlink (*tempfile); /* no longer needed */
2062 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
2064 if (SendmailWait > 0)
2071 /* reset alarm; not really needed, but... */
2073 sigaction (SIGALRM, &oldalrm, NULL);
2075 if (kill (ppid, 0) == -1 && errno == ESRCH)
2077 /* the parent is already dead */
2085 sigprocmask (SIG_UNBLOCK, &set, NULL);
2087 if (pid != -1 && waitpid (pid, &st, 0) > 0)
2088 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
2090 st = S_ERR; /* error */
2092 mutt_unblock_signals_system (1);
2098 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
2100 for (; addr; addr = addr->next)
2102 /* weed out group mailboxes, since those are for display only */
2103 if (addr->mailbox && !addr->group)
2105 if (*argslen == *argsmax)
2106 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2107 args[(*argslen)++] = addr->mailbox;
2114 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2116 if (*argslen == *argsmax)
2117 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2118 args[(*argslen)++] = s;
2127 for(i = 0; sysexits_h[i].str; i++)
2129 if(e == sysexits_h[i].v)
2133 return sysexits_h[i].str;
2138 mutt_invoke_sendmail (ADDRESS *from, /* the sender */
2139 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2140 const char *msg, /* file containing message */
2141 int eightbit) /* message contains 8bit chars */
2143 char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
2145 size_t argslen = 0, argsmax = 0;
2149 if (option (OPTNEWSSEND))
2151 char cmd[LONG_STRING];
2153 mutt_FormatString (cmd, sizeof (cmd), NONULL (Inews), nntp_format_str, 0, 0);
2156 i = nntp_post (msg);
2161 s = safe_strdup (cmd);
2165 s = safe_strdup (Sendmail);
2169 while ((ps = strtok (ps, " ")))
2171 if (argslen == argsmax)
2172 safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2175 args[argslen++] = ps;
2178 path = safe_strdup (ps);
2179 ps = strrchr (ps, '/');
2184 args[argslen++] = ps;
2191 if (!option (OPTNEWSSEND))
2194 if (eightbit && option (OPTUSE8BITMIME))
2195 args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2197 if (option (OPTENVFROM) && from && !from->next)
2199 args = add_option (args, &argslen, &argsmax, "-f");
2200 args = add_args (args, &argslen, &argsmax, from);
2204 args = add_option (args, &argslen, &argsmax, "-N");
2205 args = add_option (args, &argslen, &argsmax, DsnNotify);
2209 args = add_option (args, &argslen, &argsmax, "-R");
2210 args = add_option (args, &argslen, &argsmax, DsnReturn);
2212 args = add_option (args, &argslen, &argsmax, "--");
2213 args = add_args (args, &argslen, &argsmax, to);
2214 args = add_args (args, &argslen, &argsmax, cc);
2215 args = add_args (args, &argslen, &argsmax, bcc);
2220 if (argslen == argsmax)
2221 safe_realloc (&args, sizeof (char *) * (++argsmax));
2223 args[argslen++] = NULL;
2225 if ((i = send_msg (path, args, msg, &childout)) != (EX_OK & 0xff))
2229 const char *e = strsysexit (i);
2232 mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2237 if (stat (childout, &st) == 0 && st.st_size > 0)
2238 mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2250 if (i == (EX_OK & 0xff))
2252 else if (i == S_BKG)
2260 mutt_invoke_mta (ADDRESS *from, /* the sender */
2261 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2262 const char *msg, /* file containing message */
2263 int eightbit) /* message contains 8bit chars */
2267 return mutt_invoke_libesmtp(from, to, cc, bcc, msg, eightbit);
2270 return mutt_invoke_sendmail(from, to, cc, bcc, msg, eightbit);
2273 /* appends string 'b' to string 'a', and returns the pointer to the new
2275 char *mutt_append_string (char *a, const char *b)
2277 size_t la = mutt_strlen (a);
2278 safe_realloc (&a, la + mutt_strlen (b) + 1);
2279 strcpy (a + la, b); /* __STRCPY_CHECKED__ */
2283 /* returns 1 if char `c' needs to be quoted to protect from shell
2284 interpretation when executing commands in a subshell */
2285 #define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
2287 /* returns 1 if string `s' contains characters which could cause problems
2288 when used on a command line to execute a command */
2289 int mutt_needs_quote (const char *s)
2293 if (INVALID_CHAR (*s))
2300 /* Quote a string to prevent shell escapes when this string is used on the
2301 command line to send mail. */
2302 char *mutt_quote_string (const char *s)
2307 rlen = mutt_strlen (s) + 3;
2308 pr = r = (char *) safe_malloc (rlen);
2312 if (INVALID_CHAR (*s))
2315 safe_realloc (&r, ++rlen);
2326 /* For postponing (!final) do the necessary encodings only */
2327 void mutt_prepare_envelope (ENVELOPE *env, int final)
2329 char buffer[LONG_STRING];
2333 if (env->bcc && !(env->to || env->cc))
2335 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2336 * recipients if there is no To: or Cc: field, so attempt to suppress
2337 * it by using an empty To: field.
2339 env->to = rfc822_new_address ();
2341 env->to->next = rfc822_new_address ();
2344 rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2347 env->to->mailbox = safe_strdup (buffer);
2350 mutt_set_followup_to (env);
2352 if (!env->message_id)
2353 env->message_id = mutt_gen_msgid ();
2356 /* Take care of 8-bit => 7-bit conversion. */
2357 rfc2047_encode_adrlist (env->to, "To");
2358 rfc2047_encode_adrlist (env->cc, "Cc");
2359 rfc2047_encode_adrlist (env->bcc, "Bcc");
2360 rfc2047_encode_adrlist (env->from, "From");
2361 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2362 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2366 if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
2369 rfc2047_encode_string (&env->subject);
2371 encode_headers (env->userhdrs);
2374 void mutt_unprepare_envelope (ENVELOPE *env)
2378 for (item = env->userhdrs; item; item = item->next)
2379 rfc2047_decode (&item->data);
2381 rfc822_free_address (&env->mail_followup_to);
2383 /* back conversions */
2384 rfc2047_decode_adrlist (env->to);
2385 rfc2047_decode_adrlist (env->cc);
2386 rfc2047_decode_adrlist (env->bcc);
2387 rfc2047_decode_adrlist (env->from);
2388 rfc2047_decode_adrlist (env->reply_to);
2389 rfc2047_decode (&env->subject);
2392 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2397 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2398 MESSAGE *msg = NULL;
2402 /* Try to bounce each message out, aborting if we get any failures. */
2403 for (i=0; i<Context->msgcount; i++)
2404 if (Context->hdrs[i]->tagged)
2405 ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2409 /* If we failed to open a message, return with error */
2410 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2413 if (!fp) fp = msg->fp;
2415 mutt_mktemp (tempfile);
2416 if ((f = safe_fopen (tempfile, "w")) != NULL)
2418 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2420 if (!option (OPTBOUNCEDELIVERED))
2421 ch_flags |= CH_WEED_DELIVERED;
2423 fseek (fp, h->offset, 0);
2424 fprintf (f, "Resent-From: %s", resent_from);
2425 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2426 fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
2427 fputs ("Resent-To: ", f);
2428 mutt_write_address_list (to, f, 11, 0);
2429 mutt_copy_header (fp, h, f, ch_flags, NULL);
2431 mutt_copy_bytes (fp, f, h->content->length);
2434 ret = mutt_invoke_mta (env_from, to, NULL, NULL, tempfile,
2435 h->content->encoding == ENC8BIT);
2439 mx_close_message (&msg);
2444 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2447 const char *fqdn = mutt_fqdn (1);
2448 char resent_from[STRING];
2452 resent_from[0] = '\0';
2453 from = mutt_default_from ();
2456 rfc822_qualify (from, fqdn);
2458 rfc2047_encode_adrlist (from, "Resent-From");
2459 if (mutt_addrlist_to_idna (from, &err))
2461 mutt_error (_("Bad IDN %s while preparing resent-from."),
2465 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2468 unset_option (OPTNEWSSEND);
2471 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2473 rfc822_free_address (&from);
2479 /* given a list of addresses, return a list of unique addresses */
2480 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2482 ADDRESS *top = addr;
2483 ADDRESS **last = ⊤
2489 for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next)
2491 if (tmp->mailbox && addr->mailbox &&
2492 !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2501 dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
2507 rfc822_free_address(&addr);
2521 static void set_noconv_flags (BODY *b, short flag)
2523 for(; b; b = b->next)
2525 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2526 set_noconv_flags (b->parts, flag);
2527 else if (b->type == TYPETEXT && b->noconv)
2530 mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2532 mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2537 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc)
2541 char tempfile[_POSIX_PATH_MAX];
2542 FILE *tempfp = NULL;
2546 set_noconv_flags (hdr->content, 1);
2548 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
2550 dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2555 /* We need to add a Content-Length field to avoid problems where a line in
2556 * the message body begins with "From "
2558 if (f.magic == M_MMDF || f.magic == M_MBOX)
2560 mutt_mktemp (tempfile);
2561 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2563 mutt_perror (tempfile);
2564 mx_close_mailbox (&f, NULL);
2569 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2570 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
2572 mx_close_mailbox (&f, NULL);
2576 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2577 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2579 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 0);
2581 /* (postponment) if this was a reply of some sort, <msgid> contians the
2582 * Message-ID: of message replied to. Save it using a special X-Mutt-
2583 * header so it can be picked up if the message is recalled at a later
2584 * point in time. This will allow the message to be marked as replied if
2585 * the same mailbox is still open.
2588 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2590 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2591 * it can be picked up when the message is recalled
2594 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2595 fprintf (msg->fp, "Status: RO\n");
2599 /* (postponment) if the mail is to be signed or encrypted, save this info */
2600 if ((WithCrypto & APPLICATION_PGP)
2601 && post && (hdr->security & APPLICATION_PGP))
2603 fputs ("X-Mutt-PGP: ", msg->fp);
2604 if (hdr->security & ENCRYPT)
2605 fputc ('E', msg->fp);
2606 if (hdr->security & SIGN)
2608 fputc ('S', msg->fp);
2609 if (PgpSignAs && *PgpSignAs)
2610 fprintf (msg->fp, "<%s>", PgpSignAs);
2612 if (hdr->security & INLINE)
2613 fputc ('I', msg->fp);
2614 fputc ('\n', msg->fp);
2617 /* (postponment) if the mail is to be signed or encrypted, save this info */
2618 if ((WithCrypto & APPLICATION_SMIME)
2619 && post && (hdr->security & APPLICATION_SMIME))
2621 fputs ("X-Mutt-SMIME: ", msg->fp);
2622 if (hdr->security & ENCRYPT) {
2623 fputc ('E', msg->fp);
2624 if (SmimeCryptAlg && *SmimeCryptAlg)
2625 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2627 if (hdr->security & SIGN) {
2628 fputc ('S', msg->fp);
2629 if (SmimeDefaultKey && *SmimeDefaultKey)
2630 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2632 if (hdr->security & INLINE)
2633 fputc ('I', msg->fp);
2634 fputc ('\n', msg->fp);
2638 /* (postponement) if the mail is to be sent through a mixmaster
2639 * chain, save that information
2642 if (post && hdr->chain && hdr->chain)
2646 fputs ("X-Mutt-Mix:", msg->fp);
2647 for (p = hdr->chain; p; p = p->next)
2648 fprintf (msg->fp, " %s", (char *) p->data);
2650 fputc ('\n', msg->fp);
2656 char sasha[LONG_STRING];
2659 mutt_write_mime_body (hdr->content, tempfp);
2661 /* make sure the last line ends with a newline. Emacs doesn't ensure
2662 * this will happen, and it can cause problems parsing the mailbox
2665 fseek (tempfp, -1, 2);
2666 if (fgetc (tempfp) != '\n')
2668 fseek (tempfp, 0, 2);
2669 fputc ('\n', tempfp);
2673 if (ferror (tempfp))
2675 dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2678 mx_commit_message (msg, &f); /* XXX - really? */
2679 mx_close_message (&msg);
2680 mx_close_mailbox (&f, NULL);
2684 /* count the number of lines */
2686 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2688 fprintf (msg->fp, "Content-Length: %ld\n", (long) ftell (tempfp));
2689 fprintf (msg->fp, "Lines: %d\n\n", lines);
2691 /* copy the body and clean up */
2693 r = mutt_copy_stream (tempfp, msg->fp);
2694 if (fclose (tempfp) != 0)
2696 /* if there was an error, leave the temp version */
2702 fputc ('\n', msg->fp); /* finish off the header */
2703 r = mutt_write_mime_body (hdr->content, msg->fp);
2706 if (mx_commit_message (msg, &f) != 0)
2708 mx_close_message (&msg);
2709 mx_close_mailbox (&f, NULL);
2712 set_noconv_flags (hdr->content, 0);