summaryrefslogtreecommitdiffstats
path: root/rfc2045/makemime.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /rfc2045/makemime.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'rfc2045/makemime.c')
-rw-r--r--rfc2045/makemime.c1111
1 files changed, 1111 insertions, 0 deletions
diff --git a/rfc2045/makemime.c b/rfc2045/makemime.c
new file mode 100644
index 0000000..e535556
--- /dev/null
+++ b/rfc2045/makemime.c
@@ -0,0 +1,1111 @@
+/*
+** Copyright 2000-2010 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "rfc2045_config.h"
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#if HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include "rfc822/encode.h"
+#include "rfc2045.h"
+#include "rfc2045charset.h"
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#include "numlib/numlib.h"
+
+#if HAS_GETHOSTNAME
+#else
+int gethostname(const char *, size_t);
+#endif
+
+
+struct arg_list {
+ struct arg_list *next;
+ char *arg;
+ } ;
+
+/******************************************************************************
+
+Check if a file is a regular file.
+
+******************************************************************************/
+
+static int isreg(int fd)
+{
+ struct stat st;
+ if (fstat(fd, &st))
+ {
+ perror("fstat");
+ exit(1);
+ }
+ return S_ISREG(st.st_mode);
+}
+
+/******************************************************************************
+
+Determine the file descriptor wanted, if any.
+
+******************************************************************************/
+
+static int fd_wanted(const char *filename, const char *mode)
+{
+ if (strcmp(filename, "-") == 0) /* stdin or stdout */
+ return strcmp(mode, "r") ? 1:0;
+ if (*filename == '&')
+ return atoi(filename+1); /* file descriptor */
+ return -1; /* or a file */
+}
+
+/******************************************************************************
+
+Open some file or a pipe for reading and writing.
+
+******************************************************************************/
+
+static FILE *openfile_or_pipe(const char *filename, const char *mode)
+{
+int fd, fd_to_dup = fd_wanted(filename, mode);
+FILE *fp;
+
+ if (fd_to_dup == 0)
+ return stdin;
+
+ if (fd_to_dup >= 0)
+ fd = dup(fd_to_dup);
+ else
+ fd=open(filename, (strcmp(mode, "r") ?
+ O_WRONLY|O_CREAT|O_TRUNC:O_RDONLY), 0666);
+
+ if (fd < 0)
+ {
+ perror(filename);
+ exit(1);
+ }
+ fp=fdopen(fd, mode);
+ if (!fp)
+ {
+ perror("fdopen");
+ exit(1);
+ }
+ return (fp);
+}
+
+/******************************************************************************
+
+Open some file. If we get a pipe, open a temporary file, and drain pipe's
+contents into it.
+
+******************************************************************************/
+
+static FILE *openfile(const char *filename)
+{
+FILE *fp=openfile_or_pipe(filename, "r");
+int fd=fileno(fp);
+
+ if (!isreg(fd)) /* Must be a pipe */
+ {
+ FILE *t=tmpfile();
+ int c;
+
+ if (!t)
+ {
+ perror("tmpfile");
+ exit(1);
+ }
+
+ while ((c=getc(fp)) != EOF)
+ putc(c, t);
+ if (ferror(fp) || fflush(t)
+ || ferror(t) || fseek(t, 0L, SEEK_SET) == -1)
+ {
+ perror("write");
+ exit(1);
+ }
+ fclose(fp);
+ fp=t;
+ }
+ else
+ {
+ off_t orig_pos = lseek(fd, 0L, SEEK_CUR);
+
+ if (orig_pos == -1 ||
+ fseek(fp, orig_pos, SEEK_SET) == -1)
+ {
+ perror("fseek");
+ exit(1);
+ }
+ }
+ return (fp);
+}
+
+/******************************************************************************
+
+Build argv/argc from a file.
+
+******************************************************************************/
+
+static void read_args(int *argcp, char ***argvp, const char *file)
+{
+FILE *fp=openfile_or_pipe(file, "r");
+struct arg_list *argfirst=0, *arglast=0, *argp;
+char buffer[BUFSIZ];
+char *p;
+int c;
+
+ *argcp=0;
+ while (fgets(buffer, sizeof(buffer), fp) != 0)
+ {
+ const char *q;
+
+ if ((p=strchr(buffer, '\n')) != 0)
+ *p=0;
+ else while ((c=getc(fp)) != '\n' && c != EOF)
+ ; /* Just dump the excess */
+
+ /* Skip the filler. */
+
+ q=buffer;
+ while (*q && isspace((int)(unsigned char)*q))
+ ++q;
+ if (!*q) continue;
+ if (*q == '#') continue;
+ if (strcmp(buffer, "-") == 0)
+ {
+ if (isreg(fileno(fp)))
+ {
+ long orig_pos = ftell(fp);
+ if (orig_pos == -1 ||
+ lseek(fileno(fp), orig_pos, SEEK_SET) == -1)
+ {
+ perror("seek");
+ exit(1);
+ }
+ }
+ break;
+ }
+
+ argp=(struct arg_list *)malloc(sizeof(struct arg_list)+1+
+ strlen(q));
+ if (!argp)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ if (arglast)
+ arglast->next=argp;
+ else
+ argfirst=argp;
+ arglast=argp;
+ ++*argcp;
+ argp->next=0;
+ argp->arg=strcpy((char *)(argp+1), q);
+ }
+
+ if ((*argvp=malloc(sizeof (char *) * (*argcp+1))) == 0)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ c=0;
+ for (argp=argfirst; argp; argp=argp->next)
+ {
+ (*argvp)[c]= argp->arg;
+ ++c;
+ }
+ (*argvp)[c]=0;
+}
+
+static void usage()
+{
+ fprintf(stderr,
+"Usage:\n"
+" makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \\\n"
+" [-a \"Header: Contents\"] file\n"
+" -m [ type ] [-o file] [-e encoding] [-a \"Header: Contents\"] file\n"
+" -j [-o file] file1 file2\n"
+" @file\n"
+"\n"
+" file: filename - read or write from filename\n"
+" - - read or write from stdin or stdout\n"
+" &n - read or write from file descriptor n\n"
+" \\( opts \\) - read from child process, that generates [ opts ]\n"
+ "\n");
+
+ fprintf(stderr,
+"Options:\n"
+"\n"
+" -c type - create a new MIME section from \"file\" with this\n"
+" Content-Type: (default is application/octet-stream).\n"
+" -C charset - MIME charset of a new text/plain section.\n"
+ " -N name - MIME content name of the new mime section.\n");
+
+ fprintf(stderr,
+" -m [ type ] - create a multipart mime section from \"file\" of this\n"
+" Content-Type: (default is multipart/mixed).\n"
+" -e encoding - use the given encoding (7bit, 8bit, quoted-printable,\n"
+" or base64), instead of guessing. Omit \"-e\" and use\n"
+" -c auto to set Content-Type: to text/plain or\n"
+ " application/octet-stream based on picked encoding.\n");
+
+ fprintf(stderr,
+" -j file1 file2 - join mime section file2 to multipart section file1.\n"
+" -o file - write ther result to file, instead of stdout (not\n"
+" allowed in child processes).\n"
+" -a header - prepend an additional header to the output.\n"
+"\n"
+" @file - read all of the above options from file, one option or\n"
+" value on each line.\n"
+ );
+ exit (0);
+}
+
+/******************************************************************************
+
+The arguments are parsed into the following structure, as a tree.
+
+******************************************************************************/
+struct mimestruct {
+
+ /*
+ ** One or two input files. We initialize either file or child,
+ ** depending on the source being a file, or a child process.
+ ** Later, we open a file pointer in either case.
+ */
+
+ const char *inputfile1, *inputfile2;
+ struct mimestruct *inputchild1, *inputchild2;
+ FILE *inputfp1, *inputfp2;
+ pid_t child1, child2;
+
+ /* Output file. Defaults to "-", stdout */
+
+ const char *outputfile;
+ FILE *outputfp;
+
+ /* The handler and open functions */
+
+ void (*handler_func)(struct mimestruct *);
+ void (*open_func)(struct mimestruct *);
+
+ /* The new mime type, and encoding (-e) */
+ const char *mimetype;
+ const char *mimeencoding;
+ const char *textplaincharset;
+ const char *contentname;
+
+ /* A list of -a headers */
+ struct arg_list *a_first, *a_last;
+ } ;
+
+static void createsimplemime(struct mimestruct *);
+static void createmultipartmime(struct mimestruct *);
+static void joinmultipart(struct mimestruct *);
+
+static void opencreatesimplemime(struct mimestruct *);
+static void opencreatemultipartmime(struct mimestruct *);
+static void openjoinmultipart(struct mimestruct *);
+
+/******************************************************************************
+
+Recursively build the mimestruct tree.
+
+******************************************************************************/
+
+struct mimestruct *parseargs(int *argcp, char ***argvp)
+{
+struct mimestruct *m=malloc(sizeof(struct mimestruct));
+int argc= *argcp;
+char **argv= *argvp;
+
+ if (!m)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ memset(m, 0, sizeof(*m));
+
+ if (argc == 0 || argv[0][0] != '-') usage();
+
+ if (strncmp(argv[0], "-c", 2) == 0)
+ {
+ m->handler_func= &createsimplemime;
+ m->open_func= &opencreatesimplemime;
+ if (argv[0][2])
+ {
+ m->mimetype=argv[0]+2;
+ --argc;
+ ++argv;
+ }
+ else
+ {
+ --argc;
+ ++argv;
+ if (argc && argv[0][0] != '-' && argv[0][0] != ')')
+ {
+ m->mimetype=argv[0];
+ --argc;
+ ++argv;
+ }
+ else
+ m->mimetype="application/octet-stream";
+ }
+
+ while (isspace((int)(unsigned char)*m->mimetype))
+ ++m->mimetype;
+ }
+ else if (strncmp(argv[0], "-m", 2) == 0)
+ {
+ m->handler_func= &createmultipartmime;
+ m->open_func= &opencreatemultipartmime;
+ if (argv[0][2])
+ {
+ m->mimetype=argv[0]+2;
+ --argc;
+ ++argv;
+ }
+ else
+ {
+ --argc;
+ ++argv;
+ if (argc && argv[0][0] != '-' && argv[0][0] != ')')
+ {
+ m->mimetype=argv[0];
+ --argc;
+ ++argv;
+ }
+ else
+ m->mimetype="multipart/mixed";
+ }
+ while (isspace((int)(unsigned char)*m->mimetype))
+ ++m->mimetype;
+ }
+ else if (strncmp(argv[0], "-j", 2) == 0)
+ {
+ const char *filename;
+
+ m->handler_func= &joinmultipart;
+ m->open_func= &openjoinmultipart;
+ if (argv[0][2])
+ {
+ filename=argv[0]+2;
+ --argc;
+ ++argv;
+ }
+ else
+ {
+ --argc;
+ ++argv;
+ if (argc == 0) usage();
+ filename=argv[0];
+ --argc;
+ ++argv;
+ }
+
+ while (isspace((int)(unsigned char)*filename))
+ ++filename;
+
+ if (strcmp(filename, "(") == 0)
+ {
+ m->inputchild2=parseargs(&argc, &argv);
+ if (argc == 0 || strcmp(argv[0], ")"))
+ usage();
+ --argc;
+ ++argv;
+ }
+ else
+ m->inputfile2=filename;
+ }
+ else
+ usage();
+
+ /* Handle common options */
+
+ while (argc)
+ {
+ if (strncmp(argv[0], "-o", 2) == 0)
+ {
+ const char *f=argv[0]+2;
+
+ ++argv;
+ --argc;
+ if (*f == 0)
+ {
+ if (!argc) usage();
+ f=argv[0];
+ ++argv;
+ --argc;
+ }
+ while (isspace((int)(unsigned char)*f))
+ ++f;
+ m->outputfile=f;
+ continue;
+ }
+
+ if (strncmp(argv[0], "-C", 2) == 0)
+ {
+ char *f=argv[0]+2;
+
+ ++argv;
+ --argc;
+
+
+ if (*f == 0)
+ {
+ if (!argc) usage();
+ f=argv[0];
+ ++argv;
+ --argc;
+ }
+ while (isspace((int)(unsigned char)*f))
+ ++f;
+ m->textplaincharset=f;
+ continue;
+ }
+
+ if (strncmp(argv[0], "-N", 2) == 0)
+ {
+ char *f=argv[0]+2;
+
+ ++argv;
+ --argc;
+
+
+ if (*f == 0)
+ {
+ if (!argc) usage();
+ f=argv[0];
+ ++argv;
+ --argc;
+ }
+ while (isspace((int)(unsigned char)*f))
+ ++f;
+ m->contentname=f;
+ continue;
+ }
+
+ if (strncmp(argv[0], "-e", 2) == 0)
+ {
+ char *f=argv[0]+2, *q;
+
+ ++argv;
+ --argc;
+
+ if (*f == 0)
+ {
+ if (!argc) usage();
+ f=argv[0];
+ ++argv;
+ --argc;
+ }
+
+ for (q=f; *q; q++)
+ *q=tolower((int)(unsigned char)*q);
+
+ while (isspace((int)(unsigned char)*f))
+ ++f;
+
+ if (strcmp(f, "7bit") && strcmp(f, "8bit") &&
+ strcmp(f, "quoted-printable") &&
+ strcmp(f, "base64"))
+ usage();
+
+ m->mimeencoding=f;
+ continue;
+ }
+
+ if (strncmp(argv[0], "-a", 2) == 0)
+ {
+ char *f=argv[0]+2;
+ struct arg_list *a;
+
+ ++argv;
+ --argc;
+
+ if (*f == 0)
+ {
+ if (!argc) usage();
+ f=argv[0];
+ ++argv;
+ --argc;
+ }
+
+ while (isspace((int)(unsigned char)*f))
+ ++f;
+
+ a=malloc(sizeof(struct arg_list));
+ if (!a)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ if (m->a_last)
+ m->a_last->next=a;
+ else m->a_first=a;
+ m->a_last=a;
+ a->arg=f;
+ a->next=0;
+ continue;
+ }
+ break;
+ }
+
+ /* We must now have the input file argument */
+
+ if (!argc) usage();
+
+ if (strcmp(argv[0], "(") == 0)
+ {
+ --argc;
+ ++argv;
+ m->inputchild1=parseargs(&argc, &argv);
+ if (argc == 0 || strcmp(argv[0], ")"))
+ usage();
+ --argc;
+ ++argv;
+ }
+ else
+ {
+ m->inputfile1=argv[0];
+ --argc;
+ ++argv;
+ }
+
+ *argcp=argc;
+ *argvp=argv;
+ return (m);
+}
+
+/******************************************************************************
+
+After we're done, terminate with a zero exit code if all child processes also
+terminated with a zero exit code. Otherwise, terminate with a non-zero exit
+code thus propagating any child's non-zero exit code to parent.
+
+******************************************************************************/
+
+static void goodexit(struct mimestruct *m, int exitcode)
+{
+ if (m->outputfp && (fflush(m->outputfp) || ferror(m->outputfp)))
+ {
+ perror("makemime");
+ exit(1);
+ }
+
+ /*
+ ** Drain any leftover input, so that the child doesn't get
+ ** a SIGPIPE.
+ */
+
+ while (m->inputfp1 && !feof(m->inputfp1) && !ferror(m->inputfp1))
+ getc(m->inputfp1);
+
+ while (m->inputfp2 && !feof(m->inputfp2) && !ferror(m->inputfp2))
+ getc(m->inputfp2);
+
+ if (m->inputfp1)
+ {
+ if (ferror(m->inputfp1))
+ {
+ perror("makemime");
+ exitcode=1;
+ }
+
+ fclose(m->inputfp1);
+ }
+ if (m->inputfp2)
+ {
+ if (ferror(m->inputfp2))
+ {
+ perror("makemime");
+ exitcode=1;
+ }
+
+ fclose(m->inputfp2);
+ }
+
+ while (m->child1 > 0 && m->child2 > 0)
+ {
+ int waitstat;
+ pid_t p=wait(&waitstat);
+
+ if (p <= 0 && errno == ECHILD) break;
+
+ if (p == m->child1)
+ m->child1=0;
+ else if (p == m->child2)
+ m->child2=0;
+ else continue;
+ if (waitstat) exitcode=1;
+ }
+ exit(exitcode);
+}
+
+int main(int argc, char **argv)
+{
+struct mimestruct *m;
+
+ signal(SIGCHLD, SIG_DFL);
+ if (argc > 1 && argv[1][0] == '@')
+ read_args(&argc, &argv, argv[1]+1);
+ else if (argc > 1)
+ {
+ --argc;
+ ++argv;
+ }
+
+ m=parseargs(&argc, &argv);
+ if (argc) usage(); /* Some arguments left */
+
+ (*m->open_func)(m);
+ (*m->handler_func)(m);
+ goodexit(m, 0);
+ return (0);
+}
+
+static int encode_outfp(const char *p, size_t n, void *vp)
+{
+ if (fwrite(p, n, 1, *(FILE **)vp) != 1)
+ return -1;
+ return 0;
+}
+
+static int do_printRfc2231Attr(const char *param,
+ const char *value,
+ void *voidArg)
+{
+ fprintf( ((struct mimestruct *)voidArg)->outputfp,
+ ";\n %s=%s", param, value);
+ return 0;
+}
+
+static void createsimplemime(struct mimestruct *m)
+{
+struct arg_list *a;
+struct libmail_encode_info encode_info;
+const char *orig_charset=m->textplaincharset;
+
+ /* Determine encoding by reading the file, as follows:
+ **
+ ** Default to 7bit. Use 8bit if high-ascii bytes found. Use
+ ** quoted printable if lines more than 200 characters found.
+ ** Use base64 if a null byte is found.
+ */
+
+ if (m->mimeencoding == 0)
+ {
+ long orig_pos=ftell(m->inputfp1);
+ int binaryflag;
+
+ if (orig_pos == -1)
+ {
+ perror("ftell");
+ goodexit(m, 1);
+ }
+
+ m->mimeencoding=libmail_encode_autodetect_fpoff(m->inputfp1,
+ 0,
+ 0, -1,
+ &binaryflag);
+
+ if (ferror(m->inputfp1)
+ || fseek(m->inputfp1, orig_pos, SEEK_SET)<0)
+ {
+ perror("fseek");
+ goodexit(m, 1);
+ }
+
+ if (strcmp(m->mimetype, "auto") == 0)
+ m->mimetype=binaryflag
+ ? (orig_charset=0,
+ "application/octet-stream"):"text/plain";
+ }
+
+ for (a=m->a_first; a; a=a->next)
+ fprintf(m->outputfp, "%s\n", a->arg);
+
+ fprintf(m->outputfp, "Content-Type: %s", m->mimetype);
+ if (orig_charset && *orig_charset)
+ {
+ const char *c;
+
+ fprintf(m->outputfp, "; charset=\"");
+ for (c=orig_charset; *c; c++)
+ {
+ if (*c != '"' && *c != '\\')
+ putc(*c, m->outputfp);
+ }
+ fprintf(m->outputfp, "\"");
+ }
+
+ if (m->contentname && *m->contentname)
+ {
+ const char *chset=m->textplaincharset ? m->textplaincharset
+ : "iso-8859-1";
+
+ rfc2231_attrCreate("name", m->contentname, chset, NULL,
+ do_printRfc2231Attr, m);
+ }
+
+ fprintf(m->outputfp, "\nContent-Transfer-Encoding: %s\n\n",
+ m->mimeencoding);
+
+ libmail_encode_start(&encode_info, m->mimeencoding,
+ &encode_outfp,
+ &m->outputfp);
+ {
+ char input_buf[BUFSIZ];
+ int n;
+
+ while ((n=fread(input_buf, 1, sizeof(input_buf),
+ m->inputfp1)) > 0)
+ {
+ if ( libmail_encode(&encode_info, input_buf, n))
+ break;
+ }
+
+ libmail_encode_end(&encode_info);
+ }
+}
+
+/******************************************************************************
+
+Satisfy paranoia by making sure that the MIME boundary we picked does not
+appear in the contents of the bounded section.
+
+******************************************************************************/
+
+static int tryboundary(struct mimestruct *m, FILE *f, const char *bbuf)
+{
+char buf[BUFSIZ];
+char *p;
+int l=strlen(bbuf);
+int c;
+long orig_pos=ftell(f);
+
+ if (orig_pos == -1)
+ {
+ perror("ftell");
+ goodexit(m, 1);
+ }
+
+ while ((p=fgets(buf, sizeof(buf), f)) != 0)
+ {
+ if (p[0] == '-' && p[1] == '-' &&
+ strncmp(p+2, bbuf, l) == 0)
+ break;
+
+ if ((p=strchr(buf, '\n')) != 0)
+ *p=0;
+ else while ((c=getc(f)) != EOF && c != '\n')
+ ;
+ }
+
+ if (ferror(f) || fseek(f, orig_pos, SEEK_SET)<0)
+ {
+ perror("fseek");
+ goodexit(m, 1);
+ }
+
+ return (p ? 1:0);
+}
+
+/******************************************************************************
+
+Create a MIME boundary for some content.
+
+******************************************************************************/
+
+static const char *mkboundary(struct mimestruct *m, FILE *f)
+{
+pid_t pid=getpid();
+time_t t;
+static unsigned n=0;
+static char bbuf[NUMBUFSIZE*4];
+char buf[NUMBUFSIZE];
+
+ time(&t);
+
+ do
+ {
+ strcpy(bbuf, "=_");
+ strcat(bbuf, libmail_str_size_t(++n, buf));
+ strcat(bbuf, "_");
+ strcat(bbuf, libmail_str_time_t(t, buf));
+ strcat(bbuf, "_");
+ strcat(bbuf, libmail_str_pid_t(pid, buf));
+ } while (tryboundary(m, f, bbuf));
+ return (bbuf);
+}
+
+static void createmultipartmime(struct mimestruct *m)
+{
+const char *b=mkboundary(m, m->inputfp1);
+struct arg_list *a;
+int c;
+
+ if (m->mimeencoding == 0)
+ m->mimeencoding="8bit";
+
+ for (a=m->a_first; a; a=a->next)
+ fprintf(m->outputfp, "%s\n", a->arg);
+ fprintf(m->outputfp, "Content-Type: %s; boundary=\"%s\"\n"
+ "Content-Transfer-Encoding: %s\n\n"
+ RFC2045MIMEMSG
+ "\n--%s\n",
+ m->mimetype, b,
+ m->mimeencoding,
+ b);
+ while ((c=getc(m->inputfp1)) != EOF)
+ putc(c, m->outputfp);
+ fprintf(m->outputfp, "\n--%s--\n", b);
+}
+
+static void joinmultipart(struct mimestruct *m)
+{
+const char *new_boundary;
+char *old_boundary=0;
+int old_boundary_len=0;
+char buffer[BUFSIZ];
+char *p;
+int c;
+
+ do
+ {
+ new_boundary=mkboundary(m, m->inputfp1);
+ } while (tryboundary(m, m->inputfp2, new_boundary));
+
+ /* Copy the header */
+
+ for (;;)
+ {
+ if (fgets(buffer, sizeof(buffer), m->inputfp2) == 0)
+ {
+ buffer[0]=0;
+ break;
+ }
+
+ if (strcmp(buffer, "\r\n") == 0 ||
+ buffer[0] == '\n' || strncmp(buffer, "--", 2) == 0)
+ break;
+
+ if (strncasecmp(buffer, "content-type:", 13))
+ {
+ fprintf(m->outputfp, "%s", buffer);
+ if ((p=strchr(buffer, '\n')) != 0) continue;
+ while ((c=getc(m->inputfp2)) != EOF && c != '\n')
+ putc(c, m->outputfp);
+ continue;
+ }
+
+ if ((p=strchr(buffer, '\n')) == 0)
+ while ((c=getc(m->inputfp2)) != EOF && c != '\n')
+ ;
+
+ p=strchr(buffer+13, ';');
+ if (p) *p=0;
+ fprintf(m->outputfp, "Content-Type:%s; boundary=\"%s\"\n",
+ buffer+13, new_boundary);
+
+ for (;;)
+ {
+ c=getc(m->inputfp2);
+ if (c != EOF) ungetc(c, m->inputfp2);
+ if (c == '\n' || !isspace((int)(unsigned char)c))
+ break;
+ while ((c=getc(m->inputfp2)) != EOF && c != '\n')
+ ;
+ }
+ }
+
+ do
+ {
+ if (strncmp(buffer, "--", 2) == 0)
+ {
+ if (old_boundary == 0)
+ {
+ old_boundary=malloc(strlen(buffer)+1);
+ if (!old_boundary)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ strcpy(old_boundary, buffer);
+ if ((p=strchr(old_boundary, '\n')) != 0)
+ {
+ if (p > old_boundary && p[-1] == '\r')
+ --p;
+ *p=0;
+ }
+ p=old_boundary+strlen(old_boundary);
+ if (p >= old_boundary+4 &&
+ strcmp(p-2, "--") == 0)
+ p[-2]=0;
+ old_boundary_len=strlen(old_boundary);
+ }
+
+
+ if (strncasecmp(buffer, old_boundary,
+ old_boundary_len) == 0)
+ {
+ if ((p=strchr(buffer, '\n')) != 0)
+ *p=0;
+ else while ((c=getc(m->inputfp2)) != '\n'
+ && c != EOF)
+ ;
+
+ c=strlen(buffer);
+ if (c > 0 && buffer[c-1] == '\r')
+ buffer[--c]=0;
+
+ if (c >= 4 && strcmp(buffer+(c-2), "--") == 0)
+ break;
+ fprintf(m->outputfp, "--%s\n",
+ new_boundary);
+ continue;
+ }
+ }
+ fprintf(m->outputfp, "%s", buffer);
+ if ((p=strchr(buffer, '\n')) == 0)
+ while ((c=getc(m->inputfp2)) != '\n' && c != EOF)
+ ;
+ } while (fgets(buffer, sizeof(buffer), m->inputfp2) != 0);
+
+ fprintf(m->outputfp, "--%s\n", new_boundary);
+
+ while ((c=getc(m->inputfp1)) != EOF)
+ putc(c, m->outputfp);
+
+ fprintf(m->outputfp, "\n--%s--\n", new_boundary);
+ goodexit(m, 0);
+}
+
+/******************************************************************************
+
+Open input from a child process
+
+******************************************************************************/
+
+static FILE *openchild(struct mimestruct *parent, struct mimestruct *child,
+ pid_t *pidptr,
+ int usescratch)
+{
+int pipefd[2];
+char buf[NUMBUFSIZE];
+char buf2[NUMBUFSIZE+1];
+FILE *fp;
+
+ if (pipe(pipefd) < 0)
+ {
+ perror("pipe");
+ exit(1);
+ }
+
+ *pidptr=fork();
+
+ if (*pidptr < 0)
+ {
+ perror("fork");
+ exit(1);
+ }
+
+ if (*pidptr == 0)
+ {
+ /* Duplicate pipe on stdout */
+
+ close(pipefd[0]);
+ dup2(pipefd[1], 1);
+ close(pipefd[1]);
+
+ /* Close any input files opened by parent */
+
+ if (parent->inputfp1) fclose(parent->inputfp1);
+ if (parent->inputfp2) fclose(parent->inputfp2);
+
+ /* Open, then execute the child process */
+
+ (*child->open_func)(child);
+ (*child->handler_func)(child);
+ goodexit(child, 0);
+ }
+ close(pipefd[1]);
+
+ /*
+ ** Open the pipe by calling openfile(), automatically creating
+ ** the scratch file, if necessary.
+ */
+
+ buf[0]='&';
+ strcpy(buf+1, libmail_str_size_t(pipefd[0], buf2));
+
+ fp= usescratch ? openfile(buf):openfile_or_pipe(buf, "r");
+ close(pipefd[0]); /* fd was duped by openfile */
+ return (fp);
+}
+
+static void openoutput(struct mimestruct *m)
+{
+ if (!m->outputfile)
+ m->outputfile="-";
+
+ m->outputfp= openfile_or_pipe(m->outputfile, "w");
+}
+
+static void openjoinmultipart(struct mimestruct *m)
+{
+ /* number two is the multipart section */
+ if (m->inputchild2)
+ m->inputfp2=openchild(m, m->inputchild2, &m->child2, 1);
+ else
+ m->inputfp2=openfile(m->inputfile2);
+
+
+ if (m->inputchild1)
+ m->inputfp1=openchild(m, m->inputchild1, &m->child1, 1);
+ else
+ m->inputfp1=openfile(m->inputfile1);
+ openoutput(m);
+}
+
+static void opencreatesimplemime(struct mimestruct *m)
+{
+ if (m->inputchild1)
+ m->inputfp1=openchild(m, m->inputchild1, &m->child1,
+ m->mimeencoding ? 0:1);
+ else
+ m->inputfp1= m->mimeencoding
+ ? openfile_or_pipe(m->inputfile1, "r")
+ : openfile(m->inputfile1);
+ openoutput(m);
+}
+
+static void opencreatemultipartmime(struct mimestruct *m)
+{
+ if (m->inputchild1)
+ m->inputfp1=openchild(m, m->inputchild1, &m->child1, 1);
+ else
+ m->inputfp1=openfile_or_pipe(m->inputfile1, "r");
+ openoutput(m);
+}
+