summaryrefslogtreecommitdiffstats
path: root/sendlib.c
diff options
context:
space:
mode:
authorThomas Roessler <roessler@does-not-exist.org>1998-06-08 09:16:03 +0000
committerThomas Roessler <roessler@does-not-exist.org>1998-06-08 09:16:03 +0000
commit1a5381e07e97fe482c2b3a7c75f99938f0b105d4 (patch)
treeb4fa4088bbbf5fc9217ee6f87ab60034175e6899 /sendlib.c
Initial revision
Diffstat (limited to 'sendlib.c')
-rw-r--r--sendlib.c1788
1 files changed, 1788 insertions, 0 deletions
diff --git a/sendlib.c b/sendlib.c
new file mode 100644
index 00000000..b6eaac64
--- /dev/null
+++ b/sendlib.c
@@ -0,0 +1,1788 @@
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "rfc2047.h"
+#include "mx.h"
+#include "mime.h"
+#include "mailbox.h"
+#include "copy.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <sysexits.h>
+
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif /* _PGPPATH */
+
+
+
+#define DISPOSITION(X) X==DISPATTACH?"attachment":"inline"
+
+const char MimeSpecials[] = "@.,;<>[]\\\"()?/=";
+
+char B64Chars[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '+', '/'
+};
+
+static char MsgIdPfx = 'A';
+
+static void transform_to_7bit (BODY *a, FILE *fpin);
+
+static void encode_quoted (FILE * fin, FILE *fout, int istext)
+{
+ int c, linelen = 0;
+ char line[77], savechar;
+
+ while ((c = fgetc (fin)) != EOF)
+ {
+ /* Escape lines that begin with "the message separator". */
+ if (linelen == 5 && !strncmp ("From ", line, 5))
+ {
+ strfcpy (line, "=46rom ", sizeof (line));
+ linelen = 7;
+ }
+ else if (linelen == 1 && line[0] == '.')
+ {
+ strfcpy (line, "=2E", sizeof (line));
+ linelen = 3;
+ }
+
+ /* Wrap the line if needed. */
+ if (linelen == 76 && ((istext && c != '\n') || !istext))
+ {
+ /* If the last character is "quoted", then be sure to move all three
+ * characters to the next line. Otherwise, just move the last
+ * character...
+ */
+ if (line[linelen-3] == '=')
+ {
+ line[linelen-3] = 0;
+ fputs (line, fout);
+ fputs ("=\n", fout);
+ line[linelen] = 0;
+ line[0] = '=';
+ line[1] = line[linelen-2];
+ line[2] = line[linelen-1];
+ linelen = 3;
+ }
+ else
+ {
+ savechar = line[linelen-1];
+ line[linelen-1] = '=';
+ line[linelen] = 0;
+ fputs (line, fout);
+ fputc ('\n', fout);
+ line[0] = savechar;
+ linelen = 1;
+ }
+ }
+
+ if (c == '\n' && istext)
+ {
+ /* Check to make sure there is no trailing space on this line. */
+ if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
+ {
+ if (linelen < 74)
+ {
+ sprintf (line+linelen-1, "=%2.2X", line[linelen-1]);
+ fputs (line, fout);
+ }
+ else
+ {
+ int savechar = line[linelen-1];
+
+ line[linelen-1] = '=';
+ line[linelen] = 0;
+ fputs (line, fout);
+ fprintf (fout, "\n=%2.2X", savechar);
+ }
+ }
+ else
+ {
+ line[linelen] = 0;
+ fputs (line, fout);
+ }
+ fputc ('\n', fout);
+ linelen = 0;
+ }
+ else if (c != 9 && (c < 32 || c > 126 || c == '='))
+ {
+ /* Check to make sure there is enough room for the quoted character.
+ * If not, wrap to the next line.
+ */
+ if (linelen > 73)
+ {
+ line[linelen++] = '=';
+ line[linelen] = 0;
+ fputs (line, fout);
+ fputc ('\n', fout);
+ linelen = 0;
+ }
+ sprintf (line+linelen,"=%2.2X", c);
+ linelen += 3;
+ }
+ else
+ {
+ /* Don't worry about wrapping the line here. That will happen during
+ * the next iteration when I'll also know what the next character is.
+ */
+ line[linelen++] = c;
+ }
+ }
+
+ /* Take care of anything left in the buffer */
+ if (linelen > 0)
+ {
+ if (line[linelen-1] == ' ' || line[linelen-1] == '\t')
+ {
+ /* take care of trailing whitespace */
+ if (linelen < 74)
+ sprintf (line+linelen-1, "=%2.2X", line[linelen-1]);
+ else
+ {
+ savechar = line[linelen-1];
+ line[linelen-1] = '=';
+ line[linelen] = 0;
+ fputs (line, fout);
+ fputc ('\n', fout);
+ sprintf (line, "=%2.2X", savechar);
+ }
+ }
+ else
+ line[linelen] = 0;
+ fputs (line, fout);
+ }
+}
+
+static void encode_base64 (FILE * fin, FILE *fout, int istext)
+{
+ int c1, c2, c3, ch;
+ int insert_newline = 0;
+ int linelen = 0;
+
+ FOREVER
+ {
+ if (istext)
+ {
+ if (insert_newline)
+ {
+ c1 = '\n';
+ insert_newline = 0;
+
+ c2 = fgetc(fin);
+ if (c2 == '\n')
+ {
+ c2 = '\r';
+ c3 = '\n';
+ }
+ else
+ {
+ c3 = fgetc(fin);
+ if (c3 == '\n')
+ {
+ c3 = '\r';
+ insert_newline = 1;
+ }
+ }
+ }
+ else
+ {
+ c1 = fgetc(fin);
+ if (c1 == '\n')
+ {
+ c1 = '\r';
+ c2 = '\n';
+ c3 = fgetc(fin);
+ if (c3 == '\n')
+ {
+ c3 = '\r';
+ insert_newline = 1;
+ }
+ }
+ else
+ {
+ c2 = fgetc(fin);
+ if (c2 == '\n')
+ {
+ c2 = '\r';
+ c3 = '\n';
+ }
+ else
+ {
+ c3 = fgetc(fin);
+ if (c3 == '\n')
+ {
+ c3 = '\r';
+ insert_newline = 1;
+ }
+ }
+ }
+ }
+ }
+ else /* !istext */
+ {
+ if ((c1 = fgetc(fin)) == EOF)
+ break;
+ c2 = fgetc(fin);
+ c3 = fgetc(fin);
+ }
+
+ if (linelen + 4 >= 76)
+ {
+ fputc('\n', fout);
+ linelen = 0;
+ }
+
+ ch = c1 >> 2;
+ fputc (B64Chars[ch], fout);
+
+ if (c2 != EOF)
+ {
+ ch = ((c1 & 0x3) << 4) | (c2 >> 4);
+ fputc (B64Chars[ch], fout);
+ }
+ else
+ {
+ ch = (c1 & 0x3) << 4;
+ fputc (B64Chars[ch], fout);
+ fputs("==", fout);
+ break;
+ }
+
+ if (c3 != EOF)
+ {
+ ch = ((c2 & 0xf) << 2) | (c3 >> 6);
+ fputc (B64Chars[ch], fout);
+ }
+ else
+ {
+ ch = (c2 & 0xf) << 2;
+ fputc(B64Chars[ch], fout);
+ fputc('=', fout);
+ break;
+ }
+
+ ch = c3 & 0x3f;
+ fputc(B64Chars[ch], fout);
+
+ linelen += 4;
+ }
+
+ fputc('\n', fout);
+}
+
+int mutt_write_mime_header (BODY *a, FILE *f)
+{
+ PARAMETER *p;
+ char buffer[STRING];
+ char *t;
+ char *fn;
+ int len;
+ int tmplen;
+
+ fprintf (f, "Content-Type: %s/%s", TYPE (a->type), a->subtype);
+
+ if (a->parameter)
+ {
+ len = 25 + strlen (a->subtype); /* approximate len. of content-type */
+
+ p = a->parameter;
+ while (p)
+ {
+ fputc (';', f);
+
+ buffer[0] = 0;
+ rfc822_cat (buffer, sizeof (buffer), p->value, MimeSpecials);
+
+ tmplen = strlen (buffer) + strlen (p->attribute) + 1;
+
+ if (len + tmplen + 2 > 76)
+ {
+ fputs ("\n\t", f);
+ len = tmplen + 8;
+ }
+ else
+ {
+ fputc (' ', f);
+ len += tmplen + 1;
+ }
+
+ fprintf (f, "%s=%s", p->attribute, buffer);
+
+ p = p->next;
+ }
+ }
+
+ fputc ('\n', f);
+
+ if (a->encoding != ENC7BIT)
+ fprintf(f, "Content-Transfer-Encoding: %s\n", ENCODING (a->encoding));
+
+ if (a->description)
+ fprintf(f, "Content-Description: %s\n", a->description);
+
+ if (a->use_disp && (a->disposition == DISPATTACH || a->filename || a->d_filename))
+ {
+ fprintf (f, "Content-Disposition: %s", DISPOSITION (a->disposition));
+
+ if(!(fn = a->d_filename))
+ fn = a->filename;
+
+ if (fn)
+ {
+ /* Strip off the leading path... */
+ if ((t = strrchr (fn, '/')))
+ t++;
+ else
+ t = fn;
+
+ buffer[0] = 0;
+ rfc822_cat (buffer, sizeof (buffer), t, MimeSpecials);
+ fprintf (f, "; filename=%s", buffer);
+ }
+
+ fputc ('\n', f);
+ }
+
+ /* Do NOT add the terminator here!!! */
+ return (ferror (f) ? -1 : 0);
+}
+
+int mutt_write_mime_body (BODY *a, FILE *f)
+{
+ char *p, boundary[SHORT_STRING];
+ FILE *fpin;
+ BODY *t;
+
+ if (a->type == TYPEMULTIPART)
+ {
+ /* First, find the boundary to use */
+ if (!(p = mutt_get_parameter ("boundary", a->parameter)))
+ {
+ dprint (1, (debugfile, "mutt_write_mime_body(): no boundary parameter found!\n"));
+ return (-1);
+ }
+ strfcpy (boundary, p, sizeof (boundary));
+
+ for (t = a->parts; t ; t = t->next)
+ {
+ fprintf (f, "\n--%s\n", boundary);
+ if (mutt_write_mime_header (t, f) == -1)
+ return -1;
+ fputc ('\n', f);
+ if (mutt_write_mime_body (t, f) == -1)
+ return -1;
+ }
+ fprintf (f, "\n--%s--\n", boundary);
+ return (ferror (f) ? -1 : 0);
+ }
+
+
+
+#ifdef _PGPPATH
+ /* This is pretty gross, but it's the best solution for now... */
+ if (a->type == TYPEAPPLICATION && strcmp (a->subtype, "pgp-encrypted") == 0)
+ {
+ fputs ("Version: 1\n", f);
+ return 0;
+ }
+#endif /* _PGPPATH */
+
+
+
+ if ((fpin = fopen (a->filename, "r")) == NULL)
+ {
+ dprint(1,(debugfile, "write_mime_body: %s no longer exists!\n",a->filename));
+ return -1;
+ }
+
+ if (a->encoding == ENCQUOTEDPRINTABLE)
+ encode_quoted (fpin, f, mutt_is_text_type (a->type, a->subtype));
+ else if (a->encoding == ENCBASE64)
+ encode_base64 (fpin, f, mutt_is_text_type (a->type, a->subtype));
+ else
+ mutt_copy_stream (fpin, f);
+ fclose (fpin);
+
+ return (ferror (f) ? -1 : 0);
+}
+
+#define BOUNDARYLEN 16
+char *mutt_generate_boundary (void)
+{
+ char *rs = (char *)safe_malloc (BOUNDARYLEN + 1);
+ char *p = rs;
+ int i;
+
+ rs[BOUNDARYLEN] = 0;
+ for (i=0;i<BOUNDARYLEN;i++) *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
+ *p = 0;
+ return (rs);
+}
+
+/* analyze the contents of a file to determine which MIME encoding to use */
+static CONTENT *mutt_get_content_info (const char *fname)
+{
+ CONTENT *info;
+ FILE *fp;
+ int ch, from=0, whitespace=0, dot=0, linelen=0;
+
+ if ((fp = fopen (fname, "r")) == NULL)
+ {
+ dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
+ fname, strerror (errno), errno));
+ return (NULL);
+ }
+
+ info = safe_calloc (1, sizeof (CONTENT));
+ while ((ch = fgetc (fp)) != EOF)
+ {
+ linelen++;
+ if (ch == '\n')
+ {
+ if (whitespace) info->space = 1;
+ if (dot) info->dot = 1;
+ if (linelen > info->linemax) info->linemax = linelen;
+ whitespace = 0;
+ linelen = 0;
+ dot = 0;
+ }
+ else if (ch == '\r')
+ {
+ if ((ch = fgetc (fp)) == EOF)
+ {
+ info->binary = 1;
+ break;
+ }
+ else if (ch != '\n')
+ {
+ info->binary = 1;
+ ungetc (ch, fp);
+ continue;
+ }
+ else
+ {
+ if (whitespace) info->space = 1;
+ if (dot) info->dot = 1;
+ if (linelen > info->linemax) info->linemax = linelen;
+ whitespace = 0;
+ dot = 0;
+ linelen = 0;
+ }
+ }
+ else if (ch & 0x80)
+ info->hibin++;
+ else if (ch == '\t' || ch == '\f')
+ {
+ info->ascii++;
+ whitespace++;
+ }
+ else if (ch < 32 || ch == 127)
+ info->lobin++;
+ else
+ {
+ if (linelen == 1)
+ {
+ if (ch == 'F')
+ from = 1;
+ else
+ from = 0;
+ if (ch == '.')
+ dot = 1;
+ else
+ dot = 0;
+ }
+ else if (from)
+ {
+ if (linelen == 2 && ch != 'r') from = 0;
+ else if (linelen == 3 && ch != 'o') from = 0;
+ else if (linelen == 4 && ch != 'm') from = 0;
+ else if (linelen == 5)
+ {
+ if (ch == ' ') info->from = 1;
+ from = 0;
+ }
+ }
+ if (ch == ' ') whitespace++;
+ info->ascii++;
+ }
+ if (linelen > 1) dot = 0;
+ if (ch != ' ' && ch != '\t') whitespace = 0;
+ }
+ fclose (fp);
+ return (info);
+}
+
+/* Given a file with path ``s'', see if there is a registered MIME type.
+ * returns the major MIME type, and copies the subtype to ``d''. First look
+ * for ~/.mime.types, then look in a system mime.types if we can find one.
+ * The longest match is used so that we can match `ps.gz' when `gz' also
+ * exists.
+ */
+
+static int lookup_mime_type (char *d, const char *s)
+{
+ FILE *f;
+ char *p, *ct,
+ buf[LONG_STRING];
+ int count;
+ int szf, sze, cur_n, cur_sze;
+
+ *d = 0;
+ cur_n = TYPEOTHER;
+ cur_sze = 0;
+ szf = strlen (s);
+
+ for (count = 0 ; count < 2 ; count++)
+ {
+ /*
+ * can't use strtok() because we use it in an inner loop below, so use
+ * a switch statement here instead.
+ */
+ switch (count)
+ {
+ case 0:
+ snprintf (buf, sizeof (buf), "%s/.mime.types", Homedir);
+ break;
+ case 1:
+ strfcpy (buf, SHAREDIR"/mime.types", sizeof (buf));
+ break;
+ default:
+ return (cur_n);
+ }
+
+ if ((f = fopen (buf, "r")) != NULL)
+ {
+ while (fgets (buf, sizeof (buf) - 1, f) != NULL)
+ {
+ /* weed out any comments */
+ if ((p = strchr (buf, '#')))
+ *p = 0;
+
+ /* remove any leading space. */
+ ct = buf;
+ SKIPWS (ct);
+
+ /* position on the next field in this line */
+ if ((p = strpbrk (ct, " \t")) == NULL)
+ continue;
+ *p++ = 0;
+ SKIPWS (p);
+
+ /* cycle through the file extensions */
+ while ((p = strtok (p, " \t\n")))
+ {
+ sze = strlen (p);
+ if ((sze > cur_sze) && (szf >= sze) &&
+ strcasecmp (s + szf - sze, p) == 0 &&
+ (szf == sze || s[szf - sze - 1] == '.'))
+ {
+ char *dc;
+
+ /* get the content-type */
+
+ if ((p = strchr (ct, '/')) == NULL)
+ {
+ /* malformed line, just skip it. */
+ break;
+ }
+ *p++ = 0;
+
+ dc = d;
+ while (*p && !ISSPACE (*p))
+ *dc++ = *p++;
+ *dc = 0;
+
+ cur_n = mutt_check_mime_type (ct);
+ cur_sze = sze;
+ }
+ p = NULL;
+ }
+ }
+ fclose (f);
+ }
+ }
+ return (cur_n);
+}
+
+static char *set_text_charset (CONTENT *info)
+{
+ if (strcasecmp (NONULL (Charset), "us-ascii") == 0)
+ {
+ if (info->hibin != 0)
+ return "unknown-8bit";
+ }
+ else if (info->hibin == 0)
+ return "us-ascii";
+
+ /* if no charset is given, provide a reasonable default */
+ return (Charset ? Charset : "us-ascii");
+}
+
+void mutt_message_to_7bit (BODY *a, FILE *fp)
+{
+ char temp[_POSIX_PATH_MAX];
+ size_t linelen = 0;
+ char *line = NULL;
+ FILE *fpin = NULL;
+ FILE *fpout = NULL;
+ struct stat sb;
+
+ if (!a->filename && fp)
+ fpin = fp;
+ else if (!a->filename || !(fpin = fopen (a->filename, "r")))
+ {
+ mutt_error ("Could not open %s", a->filename ? a->filename : "(null)");
+ return;
+ }
+ else
+ {
+ a->offset = 0;
+ if (stat (a->filename, &sb) == -1)
+ {
+ mutt_perror ("stat");
+ fclose (fpin);
+ }
+ a->length = sb.st_size;
+ }
+
+ mutt_mktemp (temp);
+ if (!(fpout = safe_fopen (temp, "w+")))
+ {
+ mutt_perror ("fopen");
+ goto cleanup;
+ }
+
+ fseek (fpin, a->offset, 0);
+ a->parts = mutt_parse_messageRFC822 (fpin, a);
+
+ transform_to_7bit (a->parts, fpin);
+
+ mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
+ CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
+
+ fputs ("Mime-Version: 1.0\n", fpout);
+ mutt_write_mime_header (a->parts, fpout);
+ fputc ('\n', fpout);
+ mutt_write_mime_body (a->parts, fpout);
+
+ cleanup:
+ safe_free ((void **) &line);
+ linelen = 0;
+
+ if (fpin && !fp)
+ fclose (fpin);
+ if (fpout)
+ fclose (fpout);
+ else
+ return;
+
+ a->encoding = ENC7BIT;
+ a->d_filename = a->filename;
+ if (a->filename && a->unlink)
+ unlink (a->filename);
+ a->filename = safe_strdup (temp);
+ a->unlink = 1;
+ if(stat (a->filename, &sb) == -1)
+ {
+ mutt_perror ("stat");
+ return;
+ }
+ a->length = sb.st_size;
+ mutt_free_body (&a->parts);
+}
+
+static void transform_to_7bit (BODY *a, FILE *fpin)
+{
+ char buff[_POSIX_PATH_MAX];
+ STATE s;
+ struct stat sb;
+
+ memset (&s, 0, sizeof (s));
+ for (; a; a = a->next)
+ {
+ if (a->type == TYPEMULTIPART)
+ {
+ if (a->encoding != ENC7BIT)
+ a->encoding = ENC7BIT;
+
+ transform_to_7bit (a->parts, fpin);
+ }
+ else if (a->type == TYPEMESSAGE && strcasecmp (a->subtype, "delivery-status"))
+ {
+ mutt_message_to_7bit (a, fpin);
+ }
+ else
+ {
+ mutt_mktemp (buff);
+ if ((s.fpout = safe_fopen (buff, "w")) == NULL)
+ {
+ mutt_perror ("fopen");
+ return;
+ }
+ s.fpin = fpin;
+ mutt_decode_attachment (a, &s);
+ fclose (s.fpout);
+ a->d_filename = a->filename;
+ a->filename = safe_strdup (buff);
+ a->unlink = 1;
+ if (stat (a->filename, &sb) == -1)
+ {
+ mutt_perror ("stat");
+ return;
+ }
+ a->length = sb.st_size;
+
+ mutt_update_encoding (a);
+ if (a->encoding == ENC8BIT)
+ a->encoding = ENCQUOTEDPRINTABLE;
+ else if(a->encoding == ENCBINARY)
+ a->encoding = ENCBASE64;
+ }
+ }
+}
+
+/* determine which Content-Transfer-Encoding to use */
+static void mutt_set_encoding (BODY *b, CONTENT *info)
+{
+ if (b->type == TYPETEXT)
+ {
+ if (info->lobin)
+ b->encoding = ENCQUOTEDPRINTABLE;
+ else if (info->hibin)
+ b->encoding = option (OPTALLOW8BIT) ? ENC8BIT : ENCQUOTEDPRINTABLE;
+ else
+ b->encoding = ENC7BIT;
+ }
+ else if (b->type == TYPEMESSAGE)
+ {
+ if (info->lobin || info->hibin)
+ {
+ if (option (OPTALLOW8BIT) && !info->lobin)
+ b->encoding = ENC8BIT;
+ else
+ mutt_message_to_7bit (b, NULL);
+ }
+ else
+ b->encoding = ENC7BIT;
+ }
+ else if (info->lobin || info->hibin || info->binary || info->linemax > 990)
+ {
+ /* Determine which encoding is smaller */
+ if (1.33 * (float)(info->lobin+info->hibin+info->ascii) < 3.0 * (float) (info->lobin + info->hibin) + (float)info->ascii)
+ b->encoding = ENCBASE64;
+ else
+ b->encoding = ENCQUOTEDPRINTABLE;
+ }
+ else
+ b->encoding = ENC7BIT;
+}
+
+/* Assumes called from send mode where BODY->filename points to actual file */
+void mutt_update_encoding (BODY *a)
+{
+ CONTENT *info;
+
+ if ((info = mutt_get_content_info (a->filename)) == NULL)
+ return;
+
+ mutt_set_encoding (a, info);
+
+ if (a->type == TYPETEXT)
+ {
+ /* make sure the charset is valid */
+ if (a->parameter)
+ safe_free ((void **) &a->parameter->value);
+ else
+ {
+ a->parameter = mutt_new_parameter ();
+ a->parameter->attribute = safe_strdup ("charset");
+ }
+ a->parameter->value = safe_strdup (set_text_charset (info));
+ }
+
+
+
+#ifdef _PGPPATH
+ /* save the info in case this message is signed. we will want to do Q-P
+ * encoding if any lines begin with "From " so the signature won't be munged,
+ * for example.
+ */
+ safe_free ((void **) &a->content);
+ a->content = info;
+ info = NULL;
+#endif
+
+
+
+ safe_free ((void **) &info);
+}
+
+BODY *mutt_make_attach (const char *path)
+{
+ BODY *att;
+ CONTENT *info;
+ char buf[SHORT_STRING];
+ int n;
+
+ if ((info = mutt_get_content_info (path)) == NULL)
+ return NULL;
+
+ att = mutt_new_body ();
+ att->filename = safe_strdup (path);
+
+ /* Attempt to determine the appropriate content-type based on the filename
+ * suffix.
+ */
+ if ((n = lookup_mime_type (buf, path)) != TYPEOTHER)
+ {
+ att->type = n;
+ att->subtype = safe_strdup (buf);
+ }
+
+ if (!att->subtype)
+ {
+ if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
+ {
+ /*
+ * Statistically speaking, there should be more than 10% "lobin"
+ * chars if this is really a binary file...
+ */
+ att->type = TYPETEXT;
+ att->subtype = safe_strdup ("plain");
+ att->parameter = mutt_new_parameter ();
+ att->parameter->attribute = safe_strdup ("charset");
+ att->parameter->value = safe_strdup (set_text_charset (info));
+ }
+ else
+ {
+ att->type = TYPEAPPLICATION;
+ att->subtype = safe_strdup ("octet-stream");
+ }
+ }
+
+ mutt_set_encoding (att, info);
+
+
+
+#ifdef _PGPPATH
+ /*
+ * save the info in case this message is signed. we will want to do Q-P
+ * encoding if any lines begin with "From " so the signature won't be munged,
+ * for example.
+ */
+ att->content = info;
+ info = NULL;
+#endif
+
+
+
+ safe_free ((void **) &info);
+
+ return (att);
+}
+
+static int get_toplevel_encoding (BODY *a)
+{
+ int e = ENC7BIT;
+
+ for (; a; a = a->next)
+ {
+ if (a->encoding == ENCBINARY)
+ return (ENCBINARY);
+ else if (a->encoding == ENC8BIT)
+ e = ENC8BIT;
+ }
+
+ return (e);
+}
+
+BODY *mutt_make_multipart (BODY *b)
+{
+ BODY *new;
+
+ new = mutt_new_body ();
+ new->type = TYPEMULTIPART;
+ new->subtype = safe_strdup ("mixed");
+ new->encoding = get_toplevel_encoding (b);
+ new->parameter = mutt_new_parameter ();
+ new->parameter->attribute = safe_strdup ("boundary");
+ new->parameter->value = mutt_generate_boundary ();
+ new->use_disp = 0;
+ new->parts = b;
+
+ return new;
+}
+
+static char *mutt_make_date (char *s)
+{
+ time_t t = time (NULL);
+ struct tm *l = gmtime(&t);
+ int yday = l->tm_yday;
+ int tz = l->tm_hour * 60 + l->tm_min;
+
+ l = localtime(&t);
+ tz = l->tm_hour * 60 + l->tm_min - tz;
+ yday = l->tm_yday - yday;
+
+ if (yday != 0)
+ tz += yday * 24 * 60; /* GMT is next or previous day! */
+
+ sprintf (s, "Date: %s, %d %s %d %02d:%02d:%02d %+03d%02d\n",
+ Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon], l->tm_year+1900,
+ l->tm_hour, l->tm_min, l->tm_sec, tz/60, abs(tz) % 60);
+ return (s);
+}
+
+/* wrapper around mutt_write_address() so we can handle very large
+ recipient lists without needing a huge temporary buffer in memory */
+void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen)
+{
+ ADDRESS *tmp;
+ char buf[LONG_STRING];
+ int count = 0;
+ int len;
+
+ while (adr)
+ {
+ tmp = adr->next;
+ adr->next = NULL;
+ buf[0] = 0;
+ rfc822_write_address (buf, sizeof (buf), adr);
+ len = strlen (buf);
+ if (count && linelen + len > 74)
+ {
+ if (count)
+ {
+ fputs ("\n\t", fp);
+ linelen = len + 8; /* tab is usually about 8 spaces... */
+ }
+ }
+ else
+ {
+ if (count && adr->mailbox)
+ {
+ fputc (' ', fp);
+ linelen++;
+ }
+ linelen += len;
+ }
+ fputs (buf, fp);
+ adr->next = tmp;
+ if (!adr->group && adr->next && adr->next->mailbox)
+ {
+ linelen++;
+ fputc (',', fp);
+ }
+ adr = adr->next;
+ count++;
+ }
+ fputc ('\n', fp);
+}
+
+/* arbitrary number of elements to grow the array by */
+#define REF_INC 16
+
+#define TrimRef 10
+
+/* need to write the list in reverse because they are stored in reverse order
+ * when parsed to speed up threading
+ */
+static void write_references (LIST *r, FILE *f)
+{
+ LIST **ref = NULL;
+ int refcnt = 0, refmax = 0;
+
+ for ( ; (TrimRef == 0 || refcnt < TrimRef) && r ; r = r->next)
+ {
+ if (refcnt == refmax)
+ safe_realloc ((void **) &ref, (refmax += REF_INC) * sizeof (LIST *));
+ ref[refcnt++] = r;
+ }
+
+ while (refcnt-- > 0)
+ {
+ fputc (' ', f);
+ fputs (ref[refcnt]->data, f);
+ }
+
+ safe_free ((void **) &ref);
+}
+
+/* Note: all RFC2047 encoding should be done outside of this routine, except
+ * for the "real name." This will allow this routine to be used more than
+ * once, if necessary.
+ *
+ * mode == 1 => "lite" mode (used for edit_hdrs)
+ * mode == 0 => normal mode. write full header + MIME headers
+ * mode == -1 => write just the envelope info (used for postponing messages)
+ */
+
+int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach, int mode)
+{
+ char buffer[LONG_STRING];
+ LIST *tmp = env->userhdrs;
+
+ if (mode == 0)
+ {
+ if (env->message_id)
+ fprintf (fp, "Message-ID: %s\n", env->message_id);
+ fputs (mutt_make_date (buffer), fp);
+ }
+
+ /* OPTUSEFROM is not consulted here so that we can still write a From:
+ * field if the user sets it with the `my_hdr' command
+ */
+ if (env->from)
+ {
+ buffer[0] = 0;
+ rfc822_write_address (buffer, sizeof (buffer), env->from);
+ fprintf (fp, "From: %s\n", buffer);
+ }
+
+ if (env->to)
+ {
+ fputs ("To: ", fp);
+ mutt_write_address_list (env->to, fp, 4);
+ }
+ else if (mode > 0)
+ fputs ("To: \n", fp);
+
+ if (env->cc)
+ {
+ fputs ("Cc: ", fp);
+ mutt_write_address_list (env->cc, fp, 4);
+ }
+ else if (mode > 0)
+ fputs ("Cc: \n", fp);
+
+ if (env->bcc)
+ {
+ fputs ("Bcc: ", fp);
+ mutt_write_address_list (env->bcc, fp, 5);
+ }
+ else if (mode > 0)
+ fputs ("Bcc: \n", fp);
+
+ if (env->subject)
+ fprintf (fp, "Subject: %s\n", env->subject);
+ else if (mode == 1)
+ fputs ("Subject: \n", fp);
+
+ if (env->reply_to)
+ {
+ fputs ("Reply-To: ", fp);
+ mutt_write_address_list (env->reply_to, fp, 10);
+ }
+ else if (mode > 0)
+ fputs ("Reply-To: \n", fp);
+
+ if (env->mail_followup_to)
+ {
+ fputs ("Mail-Followup-To: ", fp);
+ mutt_write_address_list (env->mail_followup_to, fp, 18);
+ }
+
+ if (mode <= 0)
+ {
+ if (env->references)
+ {
+ fputs ("References:", fp);
+ write_references (env->references, fp);
+ fputc('\n', fp);
+ }
+
+ /* Add the MIME headers */
+ fputs ("Mime-Version: 1.0\n", fp);
+ mutt_write_mime_header (attach, fp);
+ }
+
+#ifndef NO_XMAILER
+ if (mode == 0)
+ {
+ /* Add a vanity header */
+ fprintf (fp, "X-Mailer: Mutt %s\n", VERSION);
+ }
+#endif
+
+ /* Add any user defined headers */
+ for (; tmp; tmp = tmp->next)
+ {
+ fputs (tmp->data, fp);
+ fputc ('\n', fp);
+ }
+
+ return (ferror (fp) == 0 ? 0 : -1);
+}
+
+static void encode_headers (LIST *h)
+{
+ char tmp[LONG_STRING];
+ char *p;
+ size_t len;
+
+ for (; h; h = h->next)
+ {
+ if ((p = strchr (h->data, ':')))
+ {
+ *p++ = 0;
+ SKIPWS (p);
+ snprintf (tmp, sizeof (tmp), "%s: ", h->data);
+ len = strlen (tmp);
+ rfc2047_encode_string (tmp + len, sizeof (tmp) - len, (unsigned char *) p);
+ safe_free ((void **) &h->data);
+ h->data = safe_strdup (tmp);
+ }
+ }
+}
+
+/* rfc2047 encode the content-descriptions */
+static void encode_descriptions (BODY *b)
+{
+ BODY *t;
+ char tmp[LONG_STRING];
+
+ for (t = b; t; t = t->next)
+ {
+ if (t->description)
+ {
+ rfc2047_encode_string (tmp, sizeof (tmp), (unsigned char *) t->description);
+ safe_free ((void **) &t->description);
+ t->description = safe_strdup (tmp);
+ }
+ if (t->parts)
+ encode_descriptions (t->parts);
+ }
+}
+
+char *mutt_gen_msgid (void)
+{
+ char buf[SHORT_STRING];
+ time_t now;
+ struct tm *tm;
+
+ now = time (NULL);
+ tm = localtime (&now);
+ snprintf (buf, sizeof (buf), "<%d%02d%02d%02d%02d%02d.%c%d@%s>",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
+ tm->tm_min, tm->tm_sec, MsgIdPfx, getpid (),
+ Fqdn[0] != '@' ? Fqdn : Hostname);
+ MsgIdPfx = (MsgIdPfx == 'Z') ? 'A' : MsgIdPfx + 1;
+ return (safe_strdup (buf));
+}
+
+static RETSIGTYPE alarm_handler (int sig)
+{
+ Signals |= S_ALARM;
+}
+
+/* invoke sendmail in a subshell
+ path (in) path to program to execute
+ args (in) arguments to pass to program
+ msg (in) temp file containing message to send
+ tempfile (out) if sendmail is put in the background, this points
+ to the temporary file containing the stdout of the
+ child process */
+static int
+send_msg (const char *path, char **args, const char *msg, char **tempfile)
+{
+ int fd, st, w = 0, err = 0;
+ pid_t pid;
+ struct sigaction act, oldint, oldquit, oldalrm;
+
+ memset (&act, 0, sizeof (struct sigaction));
+ sigemptyset (&(act.sa_mask));
+ act.sa_handler = SIG_IGN;
+ sigaction (SIGINT, &act, &oldint);
+ sigaction (SIGQUIT, &act, &oldquit);
+
+ if (SendmailWait)
+ {
+ char tmp[_POSIX_PATH_MAX];
+
+ mutt_mktemp (tmp);
+ *tempfile = safe_strdup (tmp);
+ }
+
+ if ((pid = fork ()) == 0)
+ {
+ /* reset signals for the child */
+ act.sa_handler = SIG_DFL;
+ /* we need SA_RESTART for the open() below */
+#ifdef SA_RESTART
+ act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+#else
+ act.sa_flags = SA_NOCLDSTOP;
+#endif
+ sigaction (SIGCHLD, &act, NULL);
+ act.sa_flags = 0;
+ sigaction (SIGINT, &act, NULL);
+ sigaction (SIGQUIT, &act, NULL);
+
+ /* if it is possible that we will deliver in the background, then we have
+ to detach the child from this process group or it will die when the
+ parent process exists, causing the mail to not get delivered. The
+ problem here is that any error messages will get lost... */
+ if (SendmailWait)
+ setsid ();
+ if ((pid = fork ()) == 0)
+ {
+ fd = open (msg, O_RDONLY, 0);
+ if (fd < 0)
+ _exit (127);
+ dup2 (fd, 0);
+ close (fd);
+ if (SendmailWait)
+ {