2 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "mutt_curses.h"
35 #include "mutt_crypt.h"
36 #include "mutt_idna.h"
49 #ifdef HAVE_SYSEXITS_H
51 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
55 /* If you are debugging this file, comment out the following line. */
64 extern char RFC822Specials[];
66 #define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
68 const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t";
71 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
72 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
73 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
74 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
78 static char MsgIdPfx = 'A';
80 static void transform_to_7bit (BODY *a, FILE *fpin);
82 static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
85 char line[77], savechar;
87 while ((c = fgetconv (fc)) != EOF)
89 /* Wrap the line if needed. */
90 if (linelen == 76 && ((istext && c != '\n') || !istext))
92 /* If the last character is "quoted", then be sure to move all three
93 * characters to the next line. Otherwise, just move the last
96 if (line[linelen-3] == '=')
103 line[1] = line[linelen-2];
104 line[2] = line[linelen-1];
109 savechar = line[linelen-1];
110 line[linelen-1] = '=';
119 /* Escape lines that begin with/only contain "the message separator". */
120 if (linelen == 4 && !mutt_strncmp ("From", line, 4))
122 strfcpy (line, "=46rom", sizeof (line));
125 else if (linelen == 4 && !mutt_strncmp ("from", line, 4))
127 strfcpy (line, "=66rom", sizeof (line));
130 else if (linelen == 1 && line[0] == '.')
132 strfcpy (line, "=2E", sizeof (line));
137 if (c == '\n' && istext)
139 /* Check to make sure there is no trailing space on this line. */
140 if (linelen > 0 && (line[linelen-1] == ' ' || line[linelen-1] == '\t'))
144 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
149 int savechar = line[linelen-1];
151 line[linelen-1] = '=';
154 fprintf (fout, "\n=%2.2X", (unsigned char) savechar);
165 else if (c != 9 && (c < 32 || c > 126 || c == '='))
167 /* Check to make sure there is enough room for the quoted character.
168 * If not, wrap to the next line.
172 line[linelen++] = '=';
178 sprintf (line+linelen,"=%2.2X", (unsigned char) c);
183 /* Don't worry about wrapping the line here. That will happen during
184 * the next iteration when I'll also know what the next character is.
190 /* Take care of anything left in the buffer */
193 if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
195 /* take care of trailing whitespace */
197 sprintf (line+linelen-1, "=%2.2X", (unsigned char) line[linelen-1]);
200 savechar = line[linelen-1];
201 line[linelen-1] = '=';
205 sprintf (line, "=%2.2X", (unsigned char) savechar);
214 static char b64_buffer[3];
215 static short b64_num;
216 static short b64_linelen;
218 static void b64_flush(FILE *fout)
225 if(b64_linelen >= 72)
231 for(i = b64_num; i < 3; i++)
232 b64_buffer[i] = '\0';
234 fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
236 fputc(B64Chars[((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf) ], fout);
241 fputc(B64Chars[((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3) ], fout);
245 fputc(B64Chars[b64_buffer[2] & 0x3f], fout);
250 while(b64_linelen % 4)
260 static void b64_putc(char c, FILE *fout)
265 b64_buffer[b64_num++] = c;
269 static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
273 b64_num = b64_linelen = 0;
275 while ((ch = fgetconv (fc)) != EOF)
277 if (istext && ch == '\n' && ch1 != '\r')
278 b64_putc('\r', fout);
286 static void encode_8bit (FGETCONV *fc, FILE *fout, int istext)
290 while ((ch = fgetconv (fc)) != EOF)
295 int mutt_write_mime_header (BODY *a, FILE *f)
305 fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
309 len = 25 + mutt_strlen (a->subtype); /* approximate len. of content-type */
311 for(p = a->parameter; p; p = p->next)
321 tmp = safe_strdup (p->value);
322 encode = rfc2231_encode_string (&tmp);
323 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
325 /* Dirty hack to make messages readable by Outlook Express
326 * for the Mac: force quotes around the boundary parameter
327 * even when they aren't needed.
330 if (!ascii_strcasecmp (p->attribute, "boundary") && !strcmp (buffer, tmp))
331 snprintf (buffer, sizeof (buffer), "\"%s\"", tmp);
335 tmplen = mutt_strlen (buffer) + mutt_strlen (p->attribute) + 1;
337 if (len + tmplen + 2 > 76)
348 fprintf (f, "%s%s=%s", p->attribute, encode ? "*" : "", buffer);
356 fprintf(f, "Content-Description: %s\n", a->description);
358 fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
362 if(!(fn = a->d_filename))
369 /* Strip off the leading path... */
370 if ((t = strrchr (fn, '/')))
376 tmp = safe_strdup (t);
377 encode = rfc2231_encode_string (&tmp);
378 rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
380 fprintf (f, "; filename%s=%s", encode ? "*" : "", buffer);
386 if (a->encoding != ENC7BIT)
387 fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
389 /* Do NOT add the terminator here!!! */
390 return (ferror (f) ? -1 : 0);
393 # define write_as_text_part(a) (mutt_is_text_part(a) \
394 || ((WithCrypto & APPLICATION_PGP)\
395 && mutt_is_application_pgp(a)))
397 int mutt_write_mime_body (BODY *a, FILE *f)
399 char *p, boundary[SHORT_STRING];
400 char send_charset[SHORT_STRING];
405 if (a->type == TYPEMULTIPART)
407 /* First, find the boundary to use */
408 if (!(p = mutt_get_parameter ("boundary", a->parameter)))
410 dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
411 mutt_error _("No boundary parameter found! [report this error]");
414 strfcpy (boundary, p, sizeof (boundary));
416 for (t = a->parts; t ; t = t->next)
418 fprintf (f, "\n--%s\n", boundary);
419 if (mutt_write_mime_header (t, f) == -1)
422 if (mutt_write_mime_body (t, f) == -1)
425 fprintf (f, "\n--%s--\n", boundary);
426 return (ferror (f) ? -1 : 0);
429 /* This is pretty gross, but it's the best solution for now... */
430 if ((WithCrypto & APPLICATION_PGP)
431 && a->type == TYPEAPPLICATION
432 && mutt_strcmp (a->subtype, "pgp-encrypted") == 0)
434 fputs ("Version: 1\n", f);
438 if ((fpin = fopen (a->filename, "r")) == NULL)
440 dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
441 mutt_error (_("%s no longer exists!"), a->filename);
445 if (a->type == TYPETEXT && (!a->noconv))
446 fc = fgetconv_open (fpin, a->charset,
447 mutt_get_body_charset (send_charset, sizeof (send_charset), a),
450 fc = fgetconv_open (fpin, 0, 0, 0);
452 if (a->encoding == ENCQUOTEDPRINTABLE)
453 encode_quoted (fc, f, write_as_text_part (a));
454 else if (a->encoding == ENCBASE64)
455 encode_base64 (fc, f, write_as_text_part (a));
456 else if (a->type == TYPETEXT && (!a->noconv))
457 encode_8bit (fc, f, write_as_text_part (a));
459 mutt_copy_stream (fpin, f);
461 fgetconv_close (&fc);
464 return (ferror (f) ? -1 : 0);
467 #undef write_as_text_part
469 #define BOUNDARYLEN 16
470 void mutt_generate_boundary (PARAMETER **parm)
472 char rs[BOUNDARYLEN + 1];
477 for (i=0;i<BOUNDARYLEN;i++)
478 *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
481 mutt_set_parameter ("boundary", rs, parm);
495 static void update_content_info (CONTENT *info, CONTENT_STATE *s, char *d, size_t dlen)
498 int whitespace = s->whitespace;
500 int linelen = s->linelen;
501 int was_cr = s->was_cr;
503 if (!d) /* This signals EOF */
507 if (linelen > info->linemax)
508 info->linemax = linelen;
513 for (; dlen; d++, dlen--)
526 if (whitespace) info->space = 1;
527 if (dot) info->dot = 1;
528 if (linelen > info->linemax) info->linemax = linelen;
540 if (whitespace) info->space = 1;
541 if (dot) info->dot = 1;
542 if (linelen > info->linemax) info->linemax = linelen;
556 else if (ch == '\t' || ch == '\f')
561 else if (ch < 32 || ch == 127)
567 if ((ch == 'F') || (ch == 'f'))
578 if (linelen == 2 && ch != 'r') from = 0;
579 else if (linelen == 3 && ch != 'o') from = 0;
580 else if (linelen == 4)
582 if (ch == 'm') info->from = 1;
586 if (ch == ' ') whitespace++;
590 if (linelen > 1) dot = 0;
591 if (ch != ' ' && ch != '\t') whitespace = 0;
595 s->whitespace = whitespace;
597 s->linelen = linelen;
602 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
603 #define BUGGY_ICONV 1
606 * Find the best charset conversion of the file from fromcode into one
607 * of the tocodes. If successful, set *tocode and CONTENT *info and
608 * return the number of characters converted inexactly. If no
609 * conversion was possible, return -1.
611 * We convert via UTF-8 in order to avoid the condition -1(EINVAL),
612 * which would otherwise prevent us from knowing the number of inexact
613 * conversions. Where the candidate target charset is UTF-8 we avoid
614 * doing the second conversion because iconv_open("UTF-8", "UTF-8")
615 * fails with some libraries.
617 * We assume that the output from iconv is never more than 4 times as
618 * long as the input for any pair of charsets we might be interested
621 static size_t convert_file_to (FILE *file, const char *fromcode,
622 int ncodes, const char **tocodes,
623 int *tocode, CONTENT *info)
627 char bufi[256], bufu[512], bufo[4 * sizeof (bufi)];
628 ICONV_CONST char *ib, *ub;
630 size_t ibl, obl, ubl, ubl1, n, ret;
633 CONTENT_STATE *states;
636 cd1 = mutt_iconv_open ("utf-8", fromcode, 0);
637 if (cd1 == (iconv_t)(-1))
640 cd = safe_calloc (ncodes, sizeof (iconv_t));
641 score = safe_calloc (ncodes, sizeof (size_t));
642 states = safe_calloc (ncodes, sizeof (CONTENT_STATE));
643 infos = safe_calloc (ncodes, sizeof (CONTENT));
645 for (i = 0; i < ncodes; i++)
646 if (ascii_strcasecmp (tocodes[i], "utf-8"))
647 cd[i] = mutt_iconv_open (tocodes[i], "utf-8", 0);
649 /* Special case for conversion to UTF-8 */
650 cd[i] = (iconv_t)(-1), score[i] = (size_t)(-1);
657 /* Try to fill input buffer */
658 n = fread (bufi + ibl, 1, sizeof (bufi) - ibl, file);
661 /* Convert to UTF-8 */
663 ob = bufu, obl = sizeof (bufu);
664 n = iconv (cd1, ibl ? &ib : 0, &ibl, &ob, &obl);
665 assert (n == (size_t)(-1) || !n || ICONV_NONTRANS);
666 if (n == (size_t)(-1) &&
667 ((errno != EINVAL && errno != E2BIG) || ib == bufi))
669 assert (errno == EILSEQ ||
670 (errno == EINVAL && ib == bufi && ibl < sizeof (bufi)));
676 /* Convert from UTF-8 */
677 for (i = 0; i < ncodes; i++)
678 if (cd[i] != (iconv_t)(-1) && score[i] != (size_t)(-1))
680 ub = bufu, ubl = ubl1;
681 ob = bufo, obl = sizeof (bufo);
682 n = iconv (cd[i], (ibl || ubl) ? &ub : 0, &ubl, &ob, &obl);
683 if (n == (size_t)(-1))
685 assert (errno == E2BIG ||
686 (BUGGY_ICONV && (errno == EILSEQ || errno == ENOENT)));
687 score[i] = (size_t)(-1);
692 update_content_info (&infos[i], &states[i], bufo, ob - bufo);
695 else if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
696 /* Special case for conversion to UTF-8 */
697 update_content_info (&infos[i], &states[i], bufu, ubl1);
700 /* Save unused input */
701 memmove (bufi, ib, ibl);
702 else if (!ubl1 && ib < bufi + sizeof (bufi))
711 /* Find best score */
713 for (i = 0; i < ncodes; i++)
715 if (cd[i] == (iconv_t)(-1) && score[i] == (size_t)(-1))
717 /* Special case for conversion to UTF-8 */
722 else if (cd[i] == (iconv_t)(-1) || score[i] == (size_t)(-1))
724 else if (ret == (size_t)(-1) || score[i] < ret)
732 if (ret != (size_t)(-1))
734 memcpy (info, &infos[*tocode], sizeof(CONTENT));
735 update_content_info (info, &states[*tocode], 0, 0); /* EOF */
739 for (i = 0; i < ncodes; i++)
740 if (cd[i] != (iconv_t)(-1))
752 #endif /* !HAVE_ICONV */
756 * Find the first of the fromcodes that gives a valid conversion and
757 * the best charset conversion of the file into one of the tocodes. If
758 * successful, set *fromcode and *tocode to dynamically allocated
759 * strings, set CONTENT *info, and return the number of characters
760 * converted inexactly. If no conversion was possible, return -1.
762 * Both fromcodes and tocodes may be colon-separated lists of charsets.
763 * However, if fromcode is zero then fromcodes is assumed to be the
764 * name of a single charset even if it contains a colon.
766 static size_t convert_file_from_to (FILE *file,
767 const char *fromcodes, const char *tocodes,
768 char **fromcode, char **tocode, CONTENT *info)
776 /* Count the tocodes */
778 for (c = tocodes; c; c = c1 ? c1 + 1 : 0)
780 if ((c1 = strchr (c, ':')) == c)
786 tcode = safe_malloc (ncodes * sizeof (char *));
787 for (c = tocodes, i = 0; c; c = c1 ? c1 + 1 : 0, i++)
789 if ((c1 = strchr (c, ':')) == c)
791 tcode[i] = mutt_substrdup (c, c1);
797 /* Try each fromcode in turn */
798 for (c = fromcodes; c; c = c1 ? c1 + 1 : 0)
800 if ((c1 = strchr (c, ':')) == c)
802 fcode = mutt_substrdup (c, c1);
804 ret = convert_file_to (file, fcode, ncodes, (const char **)tcode,
806 if (ret != (size_t)(-1))
818 /* There is only one fromcode */
819 ret = convert_file_to (file, fromcodes, ncodes, (const char **)tcode,
821 if (ret != (size_t)(-1))
829 for (i = 0; i < ncodes; i++)
838 * Analyze the contents of a file to determine which MIME encoding to use.
839 * Also set the body charset, sometimes, or not.
841 CONTENT *mutt_get_content_info (const char *fname, BODY *b)
846 char *fromcode = NULL;
854 if(b && !fname) fname = b->filename;
856 if (stat (fname, &sb) == -1)
858 mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
862 if (!S_ISREG(sb.st_mode))
864 mutt_error (_("%s isn't a regular file."), fname);
868 if ((fp = fopen (fname, "r")) == NULL)
870 dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
871 fname, strerror (errno), errno));
875 info = safe_calloc (1, sizeof (CONTENT));
876 memset (&state, 0, sizeof (state));
878 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
880 char *chs = mutt_get_parameter ("charset", b->parameter);
881 char *fchs = b->use_disp ? ((AttachCharset && *AttachCharset) ?
882 AttachCharset : Charset) : Charset;
883 if (Charset && (chs || SendCharset) &&
884 convert_file_from_to (fp, fchs, chs ? chs : SendCharset,
885 &fromcode, &tocode, info) != (size_t)(-1))
889 mutt_canonical_charset (chsbuf, sizeof (chsbuf), tocode);
890 mutt_set_parameter ("charset", chsbuf, &b->parameter);
892 b->charset = fromcode;
900 while ((r = fread (buffer, 1, sizeof(buffer), fp)))
901 update_content_info (info, &state, buffer, r);
902 update_content_info (info, &state, 0, 0);
906 if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
907 mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
908 Charset && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
914 /* Given a file with path ``s'', see if there is a registered MIME type.
915 * returns the major MIME type, and copies the subtype to ``d''. First look
916 * for ~/.mime.types, then look in a system mime.types if we can find one.
917 * The longest match is used so that we can match `ps.gz' when `gz' also
921 int mutt_lookup_mime_type (BODY *att, const char *path)
925 char buf[LONG_STRING];
926 char subtype[STRING], xtype[STRING];
928 int szf, sze, cur_sze;
936 szf = mutt_strlen (path);
938 for (count = 0 ; count < 3 ; count++)
941 * can't use strtok() because we use it in an inner loop below, so use
942 * a switch statement here instead.
947 snprintf (buf, sizeof (buf), "%s/.mime.types", NONULL(Homedir));
950 strfcpy (buf, SYSCONFDIR"/mime.types", sizeof(buf));
953 strfcpy (buf, PKGDATADIR"/mime.types", sizeof (buf));
956 dprint (1, (debugfile, "mutt_lookup_mime_type: Internal error, count = %d.\n", count));
957 goto bye; /* shouldn't happen */
960 if ((f = fopen (buf, "r")) != NULL)
962 while (fgets (buf, sizeof (buf) - 1, f) != NULL)
964 /* weed out any comments */
965 if ((p = strchr (buf, '#')))
968 /* remove any leading space. */
972 /* position on the next field in this line */
973 if ((p = strpbrk (ct, " \t")) == NULL)
978 /* cycle through the file extensions */
979 while ((p = strtok (p, " \t\n")))
981 sze = mutt_strlen (p);
982 if ((sze > cur_sze) && (szf >= sze) &&
983 (mutt_strcasecmp (path + szf - sze, p) == 0 || ascii_strcasecmp (path + szf - sze, p) == 0) &&
984 (szf == sze || path[szf - sze - 1] == '.'))
986 /* get the content-type */
988 if ((p = strchr (ct, '/')) == NULL)
990 /* malformed line, just skip it. */
995 for (q = p; *q && !ISSPACE (*q); q++)
998 mutt_substrcpy (subtype, p, q, sizeof (subtype));
1000 if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
1001 strfcpy (xtype, ct, sizeof (xtype));
1014 if (type != TYPEOTHER || *xtype != '\0')
1017 mutt_str_replace (&att->subtype, subtype);
1018 mutt_str_replace (&att->xtype, xtype);
1024 void mutt_message_to_7bit (BODY *a, FILE *fp)
1026 char temp[_POSIX_PATH_MAX];
1032 if (!a->filename && fp)
1034 else if (!a->filename || !(fpin = fopen (a->filename, "r")))
1036 mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
1042 if (stat (a->filename, &sb) == -1)
1044 mutt_perror ("stat");
1045 safe_fclose (&fpin);
1047 a->length = sb.st_size;
1051 if (!(fpout = safe_fopen (temp, "w+")))
1053 mutt_perror ("fopen");
1057 fseeko (fpin, a->offset, 0);
1058 a->parts = mutt_parse_messageRFC822 (fpin, a);
1060 transform_to_7bit (a->parts, fpin);
1062 mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
1063 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
1065 fputs ("MIME-Version: 1.0\n", fpout);
1066 mutt_write_mime_header (a->parts, fpout);
1067 fputc ('\n', fpout);
1068 mutt_write_mime_body (a->parts, fpout);
1073 if (fpin && fpin != fp)
1074 safe_fclose (&fpin);
1076 safe_fclose (&fpout);
1080 a->encoding = ENC7BIT;
1081 a->d_filename = a->filename;
1082 if (a->filename && a->unlink)
1083 unlink (a->filename);
1084 a->filename = safe_strdup (temp);
1086 if(stat (a->filename, &sb) == -1)
1088 mutt_perror ("stat");
1091 a->length = sb.st_size;
1092 mutt_free_body (&a->parts);
1093 a->hdr->content = NULL;
1096 static void transform_to_7bit (BODY *a, FILE *fpin)
1098 char buff[_POSIX_PATH_MAX];
1102 memset (&s, 0, sizeof (s));
1103 for (; a; a = a->next)
1105 if (a->type == TYPEMULTIPART)
1107 if (a->encoding != ENC7BIT)
1108 a->encoding = ENC7BIT;
1110 transform_to_7bit (a->parts, fpin);
1112 else if (mutt_is_message_type(a->type, a->subtype))
1114 mutt_message_to_7bit (a, fpin);
1119 a->force_charset = 1;
1122 if ((s.fpout = safe_fopen (buff, "w")) == NULL)
1124 mutt_perror ("fopen");
1128 mutt_decode_attachment (a, &s);
1129 safe_fclose (&s.fpout);
1130 a->d_filename = a->filename;
1131 a->filename = safe_strdup (buff);
1133 if (stat (a->filename, &sb) == -1)
1135 mutt_perror ("stat");
1138 a->length = sb.st_size;
1140 mutt_update_encoding (a);
1141 if (a->encoding == ENC8BIT)
1142 a->encoding = ENCQUOTEDPRINTABLE;
1143 else if(a->encoding == ENCBINARY)
1144 a->encoding = ENCBASE64;
1149 /* determine which Content-Transfer-Encoding to use */
1150 static void mutt_set_encoding (BODY *b, CONTENT *info)
1152 char send_charset[SHORT_STRING];
1154 if (b->type == TYPETEXT)
1156 char *chsname = mutt_get_body_charset (send_charset, sizeof (send_charset), b);
1157 if ((info->lobin && ascii_strncasecmp (chsname, "iso-2022", 8)) || info->linemax > 990 || (info->from && option (OPTENCODEFROM)))
1158 b->encoding = ENCQUOTEDPRINTABLE;
1159 else if (info->hibin)
1160 b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
1162 b->encoding = ENC7BIT;
1164 else if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
1166 if (info->lobin || info->hibin)
1168 if (option (OPTALLOW8BIT) && !info->lobin)
1169 b->encoding = ENC8BIT;
1171 mutt_message_to_7bit (b, NULL);
1174 b->encoding = ENC7BIT;
1176 else if (b->type == TYPEAPPLICATION && ascii_strcasecmp (b->subtype, "pgp-keys") == 0)
1177 b->encoding = ENC7BIT;
1180 if (info->lobin || info->hibin || info->binary || info->linemax > 990
1181 || info->cr || (/* option (OPTENCODEFROM) && */ info->from))
1184 /* Determine which encoding is smaller */
1185 if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
1186 3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
1187 b->encoding = ENCBASE64;
1189 b->encoding = ENCQUOTEDPRINTABLE;
1193 b->encoding = ENC7BIT;
1197 void mutt_stamp_attachment(BODY *a)
1199 a->stamp = time(NULL);
1202 /* Get a body's character set */
1204 char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
1208 if (b && b->type != TYPETEXT)
1212 p = mutt_get_parameter ("charset", b->parameter);
1215 mutt_canonical_charset (d, dlen, NONULL(p));
1217 strfcpy (d, "us-ascii", dlen);
1223 /* Assumes called from send mode where BODY->filename points to actual file */
1224 void mutt_update_encoding (BODY *a)
1227 char chsbuff[STRING];
1229 /* override noconv when it's us-ascii */
1230 if (mutt_is_us_ascii (mutt_get_body_charset (chsbuff, sizeof (chsbuff), a)))
1233 if (!a->force_charset && !a->noconv)
1234 mutt_delete_parameter ("charset", &a->parameter);
1236 if ((info = mutt_get_content_info (a->filename, a)) == NULL)
1239 mutt_set_encoding (a, info);
1240 mutt_stamp_attachment(a);
1247 BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
1249 char buffer[LONG_STRING];
1252 int cmflags, chflags;
1253 int pgp = WithCrypto? hdr->security : 0;
1257 if ((option(OPTMIMEFORWDECODE) || option(OPTFORWDECRYPT)) &&
1258 (hdr->security & ENCRYPT)) {
1259 if (!crypt_valid_passphrase(hdr->security))
1264 mutt_mktemp (buffer);
1265 if ((fp = safe_fopen (buffer, "w+")) == NULL)
1268 body = mutt_new_body ();
1269 body->type = TYPEMESSAGE;
1270 body->subtype = safe_strdup ("rfc822");
1271 body->filename = safe_strdup (buffer);
1274 body->disposition = DISPINLINE;
1277 mutt_parse_mime_message (ctx, hdr);
1282 /* If we are attaching a message, ignore OPTMIMEFORWDECODE */
1283 if (!attach_msg && option (OPTMIMEFORWDECODE))
1285 chflags |= CH_MIME | CH_TXTPLAIN;
1286 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1287 if ((WithCrypto & APPLICATION_PGP))
1289 if ((WithCrypto & APPLICATION_SMIME))
1290 pgp &= ~SMIMEENCRYPT;
1293 && option (OPTFORWDECRYPT) && (hdr->security & ENCRYPT))
1295 if ((WithCrypto & APPLICATION_PGP)
1296 && mutt_is_multipart_encrypted (hdr->content))
1298 chflags |= CH_MIME | CH_NONEWLINE;
1299 cmflags = M_CM_DECODE_PGP;
1302 else if ((WithCrypto & APPLICATION_PGP)
1303 && (mutt_is_application_pgp (hdr->content) & PGPENCRYPT))
1305 chflags |= CH_MIME | CH_TXTPLAIN;
1306 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1309 else if ((WithCrypto & APPLICATION_SMIME)
1310 && mutt_is_application_smime (hdr->content) & SMIMEENCRYPT)
1312 chflags |= CH_MIME | CH_TXTPLAIN;
1313 cmflags = M_CM_DECODE | M_CM_CHARCONV;
1314 pgp &= ~SMIMEENCRYPT;
1318 mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
1323 body->hdr = mutt_new_header();
1324 body->hdr->offset = 0;
1325 /* we don't need the user headers here */
1326 body->hdr->env = mutt_read_rfc822_header(fp, body->hdr, 0, 0);
1328 body->hdr->security = pgp;
1329 mutt_update_encoding (body);
1330 body->parts = body->hdr->content;
1337 BODY *mutt_make_file_attach (const char *path)
1342 att = mutt_new_body ();
1343 att->filename = safe_strdup (path);
1345 /* Attempt to determine the appropriate content-type based on the filename
1351 if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER
1355 att->subtype = safe_strdup (buf);
1356 att->xtype = safe_strdup (xbuf);
1361 mutt_lookup_mime_type (att, path);
1365 if ((info = mutt_get_content_info (path, att)) == NULL)
1367 mutt_free_body (&att);
1373 if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
1376 * Statistically speaking, there should be more than 10% "lobin"
1377 * chars if this is really a binary file...
1379 att->type = TYPETEXT;
1380 att->subtype = safe_strdup ("plain");
1384 att->type = TYPEAPPLICATION;
1385 att->subtype = safe_strdup ("octet-stream");
1390 mutt_update_encoding (att);
1394 static int get_toplevel_encoding (BODY *a)
1398 for (; a; a = a->next)
1400 if (a->encoding == ENCBINARY)
1402 else if (a->encoding == ENC8BIT)
1409 /* check for duplicate boundary. return 1 if duplicate */
1410 static int mutt_check_boundary (const char* boundary, BODY *b)
1414 if (b->parts && mutt_check_boundary (boundary, b->parts))
1417 if (b->next && mutt_check_boundary (boundary, b->next))
1420 if ((p = mutt_get_parameter ("boundary", b->parameter))
1421 && !ascii_strcmp (p, boundary))
1426 BODY *mutt_make_multipart (BODY *b)
1430 new = mutt_new_body ();
1431 new->type = TYPEMULTIPART;
1432 new->subtype = safe_strdup ("mixed");
1433 new->encoding = get_toplevel_encoding (b);
1436 mutt_generate_boundary (&new->parameter);
1437 if (mutt_check_boundary (mutt_get_parameter ("boundary", new->parameter),
1439 mutt_delete_parameter ("boundary", &new->parameter);
1441 while (!mutt_get_parameter ("boundary", new->parameter));
1443 new->disposition = DISPINLINE;
1449 /* remove the multipart body if it exists */
1450 BODY *mutt_remove_multipart (BODY *b)
1459 mutt_free_body (&t);
1464 char *mutt_make_date (char *s, size_t len)
1466 time_t t = time (NULL);
1467 struct tm *l = localtime (&t);
1468 time_t tz = mutt_local_tz (t);
1472 snprintf (s, len, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
1473 Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
1474 l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec,
1475 (int) tz / 60, (int) abs (tz) % 60);
1479 /* wrapper around mutt_write_address() so we can handle very large
1480 recipient lists without needing a huge temporary buffer in memory */
1481 void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
1484 char buf[LONG_STRING];
1493 rfc822_write_address (buf, sizeof (buf), adr, display);
1494 len = mutt_strlen (buf);
1495 if (count && linelen + len > 74)
1498 linelen = len + 8; /* tab is usually about 8 spaces... */
1502 if (count && adr->mailbox)
1511 if (!adr->group && adr->next && adr->next->mailbox)
1522 /* arbitrary number of elements to grow the array by */
1525 /* need to write the list in reverse because they are stored in reverse order
1526 * when parsed to speed up threading
1528 void mutt_write_references (LIST *r, FILE *f, int trim)
1531 int refcnt = 0, refmax = 0;
1533 for ( ; (trim == 0 || refcnt < trim) && r ; r = r->next)
1535 if (refcnt == refmax)
1536 safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
1540 while (refcnt-- > 0)
1543 fputs (ref[refcnt]->data, f);
1551 static const char *find_word (const char *src)
1553 const char *p = src;
1555 while (p && *p && strchr (" \t\n", *p))
1557 while (p && *p && !strchr (" \t\n", *p))
1562 /* like wcwidth(), but gets const char* not wchar_t* */
1563 static int my_width (const char *str, int col, int flags)
1566 int l, w = 0, nl = 0;
1567 const char *p = str;
1571 if (mbtowc (&wc, p, MB_CUR_MAX) >= 0)
1576 /* correctly calc tab stop, even for sending as the
1577 * line should look pretty on the receiving end */
1578 if (wc == L'\t' || (nl && wc == L' '))
1583 /* track newlines for display-case: if we have a space
1584 * after a newline, assume 8 spaces as for display we
1585 * always tab-fold */
1586 else if ((flags & CH_DISPLAY) && wc == '\n')
1597 static int print_val (FILE *fp, const char *pfx, const char *value, int flags)
1599 while (value && *value)
1601 if (fputc (*value, fp) == EOF)
1605 if (*(value + 1) && pfx && *pfx && fputs (pfx, fp) == EOF)
1607 /* for display, turn folding spaces into folding tabs */
1608 if ((flags & CH_DISPLAY) && (*(value + 1) == ' ' || *(value + 1) == '\t'))
1611 while (*value && (*value == ' ' || *value == '\t'))
1613 if (fputc ('\t', fp) == EOF)
1623 static int fold_one_header (FILE *fp, const char *tag, const char *value,
1624 const char *pfx, int wraplen, int flags)
1626 const char *p = value, *next, *sp;
1627 char buf[HUGE_STRING] = "";
1628 int first = 1, enc, col = 0, w, l = 0, fold;
1630 dprint(4,(debugfile,"mwoh: pfx=[%s], tag=[%s], flags=%d value=[%s]\n",
1631 pfx, tag, flags, value));
1633 if (fprintf (fp, "%s%s: ", NONULL (pfx), tag) < 0)
1635 col = mutt_strlen (tag) + 2 + mutt_strlen (pfx);
1641 /* find the next word and place it in `buf'. it may start with
1642 * whitespace we can fold before */
1643 next = find_word (p);
1644 l = MIN(sizeof (buf), next - p);
1648 /* determine width: character cells for display, bytes for sending
1649 * (we get pure ascii only) */
1650 w = my_width (buf, col, flags);
1651 enc = mutt_strncmp (buf, "=?", 2) == 0;
1653 dprint(5,(debugfile,"mwoh: word=[%s], col=%d, w=%d, next=[0x0%x]\n",
1654 buf, col, w, *next));
1656 /* insert a folding \n before the current word's lwsp except for
1657 * header name, first word on a line (word longer than wrap width)
1658 * and encoded words */
1659 if (!first && !enc && col && col + w >= wraplen)
1661 col = mutt_strlen (pfx);
1663 if (fprintf (fp, "\n%s", NONULL(pfx)) <= 0)
1667 /* print the actual word; for display, ignore leading ws for word
1668 * and fold with tab for readability */
1669 if ((flags & CH_DISPLAY) && fold)
1672 while (*p && (*p == ' ' || *p == '\t'))
1677 if (fputc ('\t', fp) == EOF)
1679 if (print_val (fp, pfx, p, flags) < 0)
1683 else if (print_val (fp, pfx, buf, flags) < 0)
1687 /* if the current word ends in \n, ignore all its trailing spaces
1688 * and reset column; this prevents us from putting only spaces (or
1689 * even none) on a line if the trailing spaces are located at our
1690 * current line width
1691 * XXX this covers ASCII space only, for display we probably
1692 * XXX want something like iswspace() here */
1694 while (*sp && (*sp == ' ' || *sp == '\t'))
1706 /* if we have printed something but didn't \n-terminate it, do it
1707 * except the last word we printed ended in \n already */
1708 if (col && buf[l - 1] != '\n')
1709 if (putc ('\n', fp) == EOF)
1715 static int write_one_header (FILE *fp, int pfxw, int max, int wraplen,
1716 const char *pfx, const char *start, const char *end,
1719 char *tagbuf, *valbuf, *t;
1721 /* only pass through folding machinery if necessary for sending */
1722 if (!(flags & CH_DISPLAY) && pfxw + max <= wraplen)
1724 valbuf = mutt_substrdup (start, end);
1725 dprint(4,(debugfile,"mwoh: buf[%s%s] short enough, "
1726 "max width = %d <= %d\n",
1727 NONULL(pfx), valbuf, max, wraplen));
1729 if (fputs (pfx, fp) == EOF)
1731 if (print_val (fp, pfx, valbuf, flags) < 0)
1740 t = strchr (start, ':');
1741 tagbuf = mutt_substrdup (start, t);
1742 valbuf = mutt_substrdup (t + 2, end);
1743 dprint(4,(debugfile,"mwoh: buf[%s%s] too long, "
1744 "max width = %d > %dn",
1745 NONULL(pfx), valbuf, max, wraplen));
1746 if (fold_one_header (fp, tagbuf, valbuf, pfx, wraplen, flags) < 0)
1754 /* split several headers into individual ones and call write_one_header
1756 int mutt_write_one_header (FILE *fp, const char *tag, const char *value,
1757 const char *pfx, int wraplen, int flags)
1759 char *p = (char *)value, *last, *line;
1761 int pfxw = mutt_strwidth (pfx);
1763 /* when not displaying, use sane wrap value */
1764 if (!(flags & CH_DISPLAY))
1766 else if (wraplen <= 0 || wraplen > COLS)
1771 /* if header is short enough, simply print it */
1772 if (!(flags & CH_DISPLAY) && mutt_strwidth (tag) + 2 + pfxw +
1773 mutt_strwidth (value) <= wraplen)
1775 dprint(4,(debugfile,"mwoh: buf[%s%s: %s] is short enough\n",
1776 NONULL(pfx), tag, value));
1777 if (fprintf (fp, "%s%s: %s\n", NONULL(pfx), tag, value) <= 0)
1782 return fold_one_header (fp, tag, value, pfx, wraplen, flags);
1785 p = last = line = (char *)value;
1788 p = strchr (p, '\n');
1790 /* find maximum line width in current header */
1793 if ((w = my_width (line, 0, flags)) > max)
1802 if (*p != ' ' && *p != '\t')
1804 if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
1812 if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
1819 /* Note: all RFC2047 encoding should be done outside of this routine, except
1820 * for the "real name." This will allow this routine to be used more than
1821 * once, if necessary.
1823 * Likewise, all IDN processing should happen outside of this routine.
1825 * mode == 1 => "lite" mode (used for edit_hdrs)
1826 * mode == 0 => normal mode. write full header + MIME headers
1827 * mode == -1 => write just the envelope info (used for postponing messages)
1829 * privacy != 0 => will omit any headers which may identify the user.
1830 * Output generated is suitable for being sent through
1831 * anonymous remailer chains.
1837 int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
1838 int mode, int privacy)
1840 char buffer[LONG_STRING];
1842 LIST *tmp = env->userhdrs;
1843 int has_agent = 0; /* user defined user-agent header field exists */
1845 if (mode == 0 && !privacy)
1846 fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
1848 /* OPTUSEFROM is not consulted here so that we can still write a From:
1849 * field if the user sets it with the `my_hdr' command
1851 if (env->from && !privacy)
1854 rfc822_write_address (buffer, sizeof (buffer), env->from, 0);
1855 fprintf (fp, "From: %s\n", buffer);
1858 if (env->sender && !privacy)
1861 rfc822_write_address (buffer, sizeof (buffer), env->sender, 0);
1862 fprintf (fp, "Sender: %s\n", buffer);
1868 mutt_write_address_list (env->to, fp, 4, 0);
1871 fputs ("To: \n", fp);
1876 mutt_write_address_list (env->cc, fp, 4, 0);
1879 fputs ("Cc: \n", fp);
1883 if(mode != 0 || option(OPTWRITEBCC))
1885 fputs ("Bcc: ", fp);
1886 mutt_write_address_list (env->bcc, fp, 5, 0);
1890 fputs ("Bcc: \n", fp);
1893 mutt_write_one_header (fp, "Subject", env->subject, NULL, 0, 0);
1895 fputs ("Subject: \n", fp);
1897 /* save message id if the user has set it */
1898 if (env->message_id && !privacy)
1899 fprintf (fp, "Message-ID: %s\n", env->message_id);
1903 fputs ("Reply-To: ", fp);
1904 mutt_write_address_list (env->reply_to, fp, 10, 0);
1907 fputs ("Reply-To: \n", fp);
1909 if (env->mail_followup_to)
1911 fputs ("Mail-Followup-To: ", fp);
1912 mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
1917 if (env->references)
1919 fputs ("References:", fp);
1920 mutt_write_references (env->references, fp, 10);
1924 /* Add the MIME headers */
1925 fputs ("MIME-Version: 1.0\n", fp);
1926 mutt_write_mime_header (attach, fp);
1929 if (env->in_reply_to)
1931 fputs ("In-Reply-To:", fp);
1932 mutt_write_references (env->in_reply_to, fp, 0);
1936 /* Add any user defined headers */
1937 for (; tmp; tmp = tmp->next)
1939 if ((p = strchr (tmp->data, ':')))
1949 continue; /* don't emit empty fields. */
1952 /* check to see if the user has overridden the user-agent field */
1953 if (!ascii_strncasecmp ("user-agent", tmp->data, 10))
1963 mutt_write_one_header (fp, tmp->data, p, NULL, 0, 0);
1968 if (mode == 0 && !privacy && option (OPTXMAILER) && !has_agent)
1970 /* Add a vanity header */
1971 fprintf (fp, "User-Agent: Mutt/%s (%s)\n", MUTT_VERSION, ReleaseDate);
1974 return (ferror (fp) == 0 ? 0 : -1);
1977 static void encode_headers (LIST *h)
1983 for (; h; h = h->next)
1985 if (!(p = strchr (h->data, ':')))
1990 tmp = safe_strdup (p);
1995 rfc2047_encode_string (&tmp);
1996 safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
1998 sprintf (h->data + i, ": %s", NONULL (tmp)); /* __SPRINTF_CHECKED__ */
2004 const char *mutt_fqdn(short may_hide_host)
2008 if(Fqdn && Fqdn[0] != '@')
2012 if(may_hide_host && option(OPTHIDDENHOST))
2014 if((p = strchr(Fqdn, '.')))
2017 /* sanity check: don't hide the host if
2018 * the fqdn is something like detebe.org.
2021 if(!p || !strchr(p, '.'))
2029 char *mutt_gen_msgid (void)
2031 char buf[SHORT_STRING];
2038 if(!(fqdn = mutt_fqdn(0)))
2039 fqdn = NONULL(Hostname);
2041 snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.G%c%u@%s>",
2042 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
2043 tm->tm_min, tm->tm_sec, MsgIdPfx, (unsigned int)getpid (), fqdn);
2044 MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
2045 return (safe_strdup (buf));
2048 static RETSIGTYPE alarm_handler (int sig)
2053 /* invoke sendmail in a subshell
2054 path (in) path to program to execute
2055 args (in) arguments to pass to program
2056 msg (in) temp file containing message to send
2057 tempfile (out) if sendmail is put in the background, this points
2058 to the temporary file containing the stdout of the
2059 child process. If it is NULL, stderr and stdout
2060 are not redirected. */
2062 send_msg (const char *path, char **args, const char *msg, char **tempfile)
2068 mutt_block_signals_system ();
2071 /* we also don't want to be stopped right now */
2072 sigaddset (&set, SIGTSTP);
2073 sigprocmask (SIG_BLOCK, &set, NULL);
2075 if (SendmailWait >= 0 && tempfile)
2077 char tmp[_POSIX_PATH_MAX];
2080 *tempfile = safe_strdup (tmp);
2083 if ((pid = fork ()) == 0)
2085 struct sigaction act, oldalrm;
2087 /* save parent's ID before setsid() */
2090 /* we want the delivery to continue even after the main process dies,
2091 * so we put ourselves into another session right away
2095 /* next we close all open files */
2097 #if defined(OPEN_MAX)
2098 for (fd = tempfile ? 1 : 3; fd < OPEN_MAX; fd++)
2100 #elif defined(_POSIX_OPEN_MAX)
2101 for (fd = tempfile ? 1 : 3; fd < _POSIX_OPEN_MAX; fd++)
2111 /* now the second fork() */
2112 if ((pid = fork ()) == 0)
2114 /* "msg" will be opened as stdin */
2115 if (open (msg, O_RDONLY, 0) < 0)
2122 if (SendmailWait >= 0 && tempfile && *tempfile)
2124 /* *tempfile will be opened as stdout */
2125 if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
2127 /* redirect stderr to *tempfile too */
2133 if (open ("/dev/null", O_WRONLY | O_APPEND) < 0) /* stdout */
2135 if (open ("/dev/null", O_RDWR | O_APPEND) < 0) /* stderr */
2139 execvp (path, args);
2146 FREE (tempfile); /* __FREE_CHECKED__ */
2150 /* SendmailWait > 0: interrupt waitpid() after SendmailWait seconds
2151 * SendmailWait = 0: wait forever
2152 * SendmailWait < 0: don't wait
2154 if (SendmailWait > 0)
2157 act.sa_handler = alarm_handler;
2159 /* need to make sure waitpid() is interrupted on SIGALRM */
2160 act.sa_flags = SA_INTERRUPT;
2164 sigemptyset (&act.sa_mask);
2165 sigaction (SIGALRM, &act, &oldalrm);
2166 alarm (SendmailWait);
2168 else if (SendmailWait < 0)
2169 _exit (0xff & EX_OK);
2171 if (waitpid (pid, &st, 0) > 0)
2173 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
2174 if (SendmailWait && st == (0xff & EX_OK) && tempfile && *tempfile)
2176 unlink (*tempfile); /* no longer needed */
2177 FREE (tempfile); /* __FREE_CHECKED__ */
2182 st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
2184 if (SendmailWait > 0 && tempfile && *tempfile)
2187 FREE (tempfile); /* __FREE_CHECKED__ */
2191 /* reset alarm; not really needed, but... */
2193 sigaction (SIGALRM, &oldalrm, NULL);
2195 if (kill (ppid, 0) == -1 && errno == ESRCH && tempfile && *tempfile)
2197 /* the parent is already dead */
2199 FREE (tempfile); /* __FREE_CHECKED__ */
2205 sigprocmask (SIG_UNBLOCK, &set, NULL);
2207 if (pid != -1 && waitpid (pid, &st, 0) > 0)
2208 st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR; /* return child status */
2210 st = S_ERR; /* error */
2212 mutt_unblock_signals_system (1);
2218 add_args (char **args, size_t *argslen, size_t *argsmax, ADDRESS *addr)
2220 for (; addr; addr = addr->next)
2222 /* weed out group mailboxes, since those are for display only */
2223 if (addr->mailbox && !addr->group)
2225 if (*argslen == *argsmax)
2226 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2227 args[(*argslen)++] = addr->mailbox;
2234 add_option (char **args, size_t *argslen, size_t *argsmax, char *s)
2236 if (*argslen == *argsmax)
2237 safe_realloc (&args, (*argsmax += 5) * sizeof (char *));
2238 args[(*argslen)++] = s;
2243 mutt_invoke_sendmail (ADDRESS *from, /* the sender */
2244 ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, /* recips */
2245 const char *msg, /* file containing message */
2246 int eightbit) /* message contains 8bit chars */
2248 char *ps = NULL, *path = NULL, *s = safe_strdup (Sendmail), *childout = NULL;
2250 size_t argslen = 0, argsmax = 0;
2255 while ((ps = strtok (ps, " ")))
2257 if (argslen == argsmax)
2258 safe_realloc (&args, sizeof (char *) * (argsmax += 5));
2261 args[argslen++] = ps;
2264 path = safe_strdup (ps);
2265 ps = strrchr (ps, '/');
2270 args[argslen++] = ps;
2276 if (eightbit && option (OPTUSE8BITMIME))
2277 args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
2279 if (option (OPTENVFROM))
2283 args = add_option (args, &argslen, &argsmax, "-f");
2284 args = add_args (args, &argslen, &argsmax, EnvFrom);
2286 else if (from && !from->next)
2288 args = add_option (args, &argslen, &argsmax, "-f");
2289 args = add_args (args, &argslen, &argsmax, from);
2295 args = add_option (args, &argslen, &argsmax, "-N");
2296 args = add_option (args, &argslen, &argsmax, DsnNotify);
2300 args = add_option (args, &argslen, &argsmax, "-R");
2301 args = add_option (args, &argslen, &argsmax, DsnReturn);
2303 args = add_option (args, &argslen, &argsmax, "--");
2304 args = add_args (args, &argslen, &argsmax, to);
2305 args = add_args (args, &argslen, &argsmax, cc);
2306 args = add_args (args, &argslen, &argsmax, bcc);
2308 if (argslen == argsmax)
2309 safe_realloc (&args, sizeof (char *) * (++argsmax));
2311 args[argslen++] = NULL;
2313 if ((i = send_msg (path, args, msg, option(OPTNOCURSES) ? NULL : &childout)) != (EX_OK & 0xff))
2317 const char *e = mutt_strsysexit (i);
2319 e = mutt_strsysexit (i);
2320 mutt_error (_("Error sending message, child exited %d (%s)."), i, NONULL (e));
2325 if (stat (childout, &st) == 0 && st.st_size > 0)
2326 mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
2338 if (i == (EX_OK & 0xff))
2340 else if (i == S_BKG)
2347 /* For postponing (!final) do the necessary encodings only */
2348 void mutt_prepare_envelope (ENVELOPE *env, int final)
2350 char buffer[LONG_STRING];
2354 if (env->bcc && !(env->to || env->cc))
2356 /* some MTA's will put an Apparently-To: header field showing the Bcc:
2357 * recipients if there is no To: or Cc: field, so attempt to suppress
2358 * it by using an empty To: field.
2360 env->to = rfc822_new_address ();
2362 env->to->next = rfc822_new_address ();
2365 rfc822_cat (buffer, sizeof (buffer), "undisclosed-recipients",
2368 env->to->mailbox = safe_strdup (buffer);
2371 mutt_set_followup_to (env);
2373 if (!env->message_id)
2374 env->message_id = mutt_gen_msgid ();
2377 /* Take care of 8-bit => 7-bit conversion. */
2378 rfc2047_encode_adrlist (env->to, "To");
2379 rfc2047_encode_adrlist (env->cc, "Cc");
2380 rfc2047_encode_adrlist (env->bcc, "Bcc");
2381 rfc2047_encode_adrlist (env->from, "From");
2382 rfc2047_encode_adrlist (env->mail_followup_to, "Mail-Followup-To");
2383 rfc2047_encode_adrlist (env->reply_to, "Reply-To");
2384 rfc2047_encode_string (&env->x_label);
2388 rfc2047_encode_string (&env->subject);
2390 encode_headers (env->userhdrs);
2393 void mutt_unprepare_envelope (ENVELOPE *env)
2397 for (item = env->userhdrs; item; item = item->next)
2398 rfc2047_decode (&item->data);
2400 rfc822_free_address (&env->mail_followup_to);
2402 /* back conversions */
2403 rfc2047_decode_adrlist (env->to);
2404 rfc2047_decode_adrlist (env->cc);
2405 rfc2047_decode_adrlist (env->bcc);
2406 rfc2047_decode_adrlist (env->from);
2407 rfc2047_decode_adrlist (env->reply_to);
2408 rfc2047_decode (&env->subject);
2409 rfc2047_decode (&env->x_label);
2412 static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *resent_from,
2417 char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
2418 MESSAGE *msg = NULL;
2422 /* Try to bounce each message out, aborting if we get any failures. */
2423 for (i=0; i<Context->msgcount; i++)
2424 if (Context->hdrs[i]->tagged)
2425 ret |= _mutt_bounce_message (fp, Context->hdrs[i], to, resent_from, env_from);
2429 /* If we failed to open a message, return with error */
2430 if (!fp && (msg = mx_open_message (Context, h->msgno)) == NULL)
2433 if (!fp) fp = msg->fp;
2435 mutt_mktemp (tempfile);
2436 if ((f = safe_fopen (tempfile, "w")) != NULL)
2438 int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
2441 if (!option (OPTBOUNCEDELIVERED))
2442 ch_flags |= CH_WEED_DELIVERED;
2444 fseeko (fp, h->offset, 0);
2445 fprintf (f, "Resent-From: %s", resent_from);
2446 fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
2447 msgid_str = mutt_gen_msgid();
2448 fprintf (f, "Resent-Message-ID: %s\n", msgid_str);
2449 fputs ("Resent-To: ", f);
2450 mutt_write_address_list (to, f, 11, 0);
2451 mutt_copy_header (fp, h, f, ch_flags, NULL);
2453 mutt_copy_bytes (fp, f, h->content->length);
2459 ret = mutt_smtp_send (env_from, to, NULL, NULL, tempfile,
2460 h->content->encoding == ENC8BIT);
2462 #endif /* USE_SMTP */
2463 ret = mutt_invoke_sendmail (env_from, to, NULL, NULL, tempfile,
2464 h->content->encoding == ENC8BIT);
2468 mx_close_message (&msg);
2473 int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
2476 const char *fqdn = mutt_fqdn (1);
2477 char resent_from[STRING];
2481 resent_from[0] = '\0';
2482 from = mutt_default_from ();
2485 * mutt_default_from() does not use $realname if the real name is not set
2486 * in $from, so we add it here. The reason it is not added in
2487 * mutt_default_from() is that during normal sending, we execute
2488 * send-hooks and set the realname last so that it can be changed based
2489 * upon message criteria.
2491 if (! from->personal)
2492 from->personal = safe_strdup(Realname);
2495 rfc822_qualify (from, fqdn);
2497 rfc2047_encode_adrlist (from, "Resent-From");
2498 if (mutt_addrlist_to_idna (from, &err))
2500 mutt_error (_("Bad IDN %s while preparing resent-from."),
2502 rfc822_free_address (&from);
2505 rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
2507 ret = _mutt_bounce_message (fp, h, to, resent_from, from);
2509 rfc822_free_address (&from);
2515 /* given a list of addresses, return a list of unique addresses */
2516 ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
2518 ADDRESS *top = addr;
2519 ADDRESS **last = ⊤
2525 for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next)
2527 if (tmp->mailbox && addr->mailbox &&
2528 !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
2537 dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
2543 rfc822_free_address(&addr);
2557 static void set_noconv_flags (BODY *b, short flag)
2559 for(; b; b = b->next)
2561 if (b->type == TYPEMESSAGE || b->type == TYPEMULTIPART)
2562 set_noconv_flags (b->parts, flag);
2563 else if (b->type == TYPETEXT && b->noconv)
2566 mutt_set_parameter ("x-mutt-noconv", "yes", &b->parameter);
2568 mutt_delete_parameter ("x-mutt-noconv", &b->parameter);
2573 int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post, char *fcc)
2577 char tempfile[_POSIX_PATH_MAX];
2578 FILE *tempfp = NULL;
2579 int r, need_buffy_cleanup = 0;
2583 set_noconv_flags (hdr->content, 1);
2585 if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
2587 dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
2592 /* We need to add a Content-Length field to avoid problems where a line in
2593 * the message body begins with "From "
2595 if (f.magic == M_MMDF || f.magic == M_MBOX)
2597 mutt_mktemp (tempfile);
2598 if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
2600 mutt_perror (tempfile);
2601 mx_close_mailbox (&f, NULL);
2604 /* remember new mail status before appending message */
2605 need_buffy_cleanup = 1;
2609 hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
2610 if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
2612 mx_close_mailbox (&f, NULL);
2616 /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
2617 * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
2619 mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 0);
2621 /* (postponment) if this was a reply of some sort, <msgid> contians the
2622 * Message-ID: of message replied to. Save it using a special X-Mutt-
2623 * header so it can be picked up if the message is recalled at a later
2624 * point in time. This will allow the message to be marked as replied if
2625 * the same mailbox is still open.
2628 fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
2630 /* (postponment) save the Fcc: using a special X-Mutt- header so that
2631 * it can be picked up when the message is recalled
2634 fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
2635 fprintf (msg->fp, "Status: RO\n");
2639 /* (postponment) if the mail is to be signed or encrypted, save this info */
2640 if ((WithCrypto & APPLICATION_PGP)
2641 && post && (hdr->security & APPLICATION_PGP))
2643 fputs ("X-Mutt-PGP: ", msg->fp);
2644 if (hdr->security & ENCRYPT)
2645 fputc ('E', msg->fp);
2646 if (hdr->security & SIGN)
2648 fputc ('S', msg->fp);
2649 if (PgpSignAs && *PgpSignAs)
2650 fprintf (msg->fp, "<%s>", PgpSignAs);
2652 if (hdr->security & INLINE)
2653 fputc ('I', msg->fp);
2654 fputc ('\n', msg->fp);
2657 /* (postponment) if the mail is to be signed or encrypted, save this info */
2658 if ((WithCrypto & APPLICATION_SMIME)
2659 && post && (hdr->security & APPLICATION_SMIME))
2661 fputs ("X-Mutt-SMIME: ", msg->fp);
2662 if (hdr->security & ENCRYPT) {
2663 fputc ('E', msg->fp);
2664 if (SmimeCryptAlg && *SmimeCryptAlg)
2665 fprintf (msg->fp, "C<%s>", SmimeCryptAlg);
2667 if (hdr->security & SIGN) {
2668 fputc ('S', msg->fp);
2669 if (SmimeDefaultKey && *SmimeDefaultKey)
2670 fprintf (msg->fp, "<%s>", SmimeDefaultKey);
2672 if (hdr->security & INLINE)
2673 fputc ('I', msg->fp);
2674 fputc ('\n', msg->fp);
2678 /* (postponement) if the mail is to be sent through a mixmaster
2679 * chain, save that information
2682 if (post && hdr->chain && hdr->chain)
2686 fputs ("X-Mutt-Mix:", msg->fp);
2687 for (p = hdr->chain; p; p = p->next)
2688 fprintf (msg->fp, " %s", (char *) p->data);
2690 fputc ('\n', msg->fp);
2696 char sasha[LONG_STRING];
2699 mutt_write_mime_body (hdr->content, tempfp);
2701 /* make sure the last line ends with a newline. Emacs doesn't ensure
2702 * this will happen, and it can cause problems parsing the mailbox
2705 fseek (tempfp, -1, 2);
2706 if (fgetc (tempfp) != '\n')
2708 fseek (tempfp, 0, 2);
2709 fputc ('\n', tempfp);
2713 if (ferror (tempfp))
2715 dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
2716 safe_fclose (&tempfp);
2718 mx_commit_message (msg, &f); /* XXX - really? */
2719 mx_close_message (&msg);
2720 mx_close_mailbox (&f, NULL);
2724 /* count the number of lines */
2726 while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
2728 fprintf (msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftello (tempfp));
2729 fprintf (msg->fp, "Lines: %d\n\n", lines);
2731 /* copy the body and clean up */
2733 r = mutt_copy_stream (tempfp, msg->fp);
2734 if (fclose (tempfp) != 0)
2736 /* if there was an error, leave the temp version */
2742 fputc ('\n', msg->fp); /* finish off the header */
2743 r = mutt_write_mime_body (hdr->content, msg->fp);
2746 if (mx_commit_message (msg, &f) != 0)
2748 mx_close_message (&msg);
2749 mx_close_mailbox (&f, NULL);
2751 if (!post && need_buffy_cleanup)
2752 mutt_buffy_cleanup (path, &st);
2755 set_noconv_flags (hdr->content, 0);