1001 lines
20 KiB
C
1001 lines
20 KiB
C
|
/*
|
||
|
** Copyright 2000 Double Precision, Inc. See COPYING for
|
||
|
** distribution information.
|
||
|
*/
|
||
|
|
||
|
#if HAVE_CONFIG_H
|
||
|
#include "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 "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
|
||
|
|
||
|
static const char rcsid[]="$Id$";
|
||
|
|
||
|
struct arg_list {
|
||
|
struct arg_list *next;
|
||
|
char *arg;
|
||
|
} ;
|
||
|
|
||
|
/******************************************************************************
|
||
|
|
||
|
Open some file or a pipe for reading and writing.
|
||
|
|
||
|
******************************************************************************/
|
||
|
|
||
|
static FILE *openfile_or_pipe(const char *filename, const char *mode)
|
||
|
{
|
||
|
int fd;
|
||
|
FILE *fp;
|
||
|
|
||
|
if (strcmp(filename, "-") == 0) /* stdin or stdout */
|
||
|
fd=dup( strcmp(mode, "r") ? 1:0);
|
||
|
else if (*filename == '&')
|
||
|
fd=dup( atoi(filename+1)); /* file descriptor */
|
||
|
else fd=open(filename, (strcmp(mode, "r") ? O_WRONLY|O_CREAT|O_TRUNC:
|
||
|
O_RDONLY), 0666); /* or a file */
|
||
|
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);
|
||
|
off_t orig_pos;
|
||
|
|
||
|
if ((orig_pos=lseek(fd, 0L, SEEK_CUR)) == -1 ||
|
||
|
lseek(fd, 0L, SEEK_END) == -1 ||
|
||
|
lseek(fd, 0L, SEEK_CUR) == -1 ||
|
||
|
lseek(fd, orig_pos, SEEK_SET) == -1) /* 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;
|
||
|
}
|
||
|
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) 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] [-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"
|
||
|
"Options:\n"
|
||
|
"\n"
|
||
|
" -c type - create a new mime section from \"file\" with this\n"
|
||
|
" Content-Type: (default is application/octet-stream).\n"
|
||
|
" -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.\n"
|
||
|
" -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;
|
||
|
|
||
|
/* 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;
|
||
|
}
|
||
|
|
||
|
/* 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], "-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 struct mimestruct *base64m;
|
||
|
|
||
|
static void putbase64(const char *p, size_t l)
|
||
|
{
|
||
|
fwrite(p, 1, l, base64m->outputfp);
|
||
|
}
|
||
|
|
||
|
static void createsimplemime(struct mimestruct *m)
|
||
|
{
|
||
|
int c;
|
||
|
struct arg_list *a;
|
||
|
|
||
|
/* 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)
|
||
|
{
|
||
|
int l=0;
|
||
|
int longline=0;
|
||
|
long orig_pos=ftell(m->inputfp1);
|
||
|
|
||
|
if (orig_pos == -1)
|
||
|
{
|
||
|
perror("ftell");
|
||
|
goodexit(m, 1);
|
||
|
}
|
||
|
|
||
|
m->mimeencoding="7bit";
|
||
|
|
||
|
while ((c=getc(m->inputfp1)) != EOF)
|
||
|
{
|
||
|
unsigned char ch= (unsigned char)c;
|
||
|
|
||
|
if (ch >= 0x80)
|
||
|
m->mimeencoding="8bit";
|
||
|
if (ch == 0)
|
||
|
{
|
||
|
m->mimeencoding="base64";
|
||
|
longline=0;
|
||
|
break;
|
||
|
}
|
||
|
if (ch == '\n') l=0;
|
||
|
else if (++l > 200)
|
||
|
longline=1;
|
||
|
|
||
|
}
|
||
|
if (ferror(m->inputfp1)
|
||
|
|| fseek(m->inputfp1, orig_pos, SEEK_SET)<0)
|
||
|
{
|
||
|
perror("fseek");
|
||
|
goodexit(m, 1);
|
||
|
}
|
||
|
if (longline)
|
||
|
m->mimeencoding="quoted-printable";
|
||
|
}
|
||
|
|
||
|
|
||
|
for (a=m->a_first; a; a=a->next)
|
||
|
fprintf(m->outputfp, "%s\n", a->arg);
|
||
|
|
||
|
fprintf(m->outputfp, "Content-Type: %s\n"
|
||
|
"Content-Transfer-Encoding: %s\n\n",
|
||
|
m->mimetype, m->mimeencoding);
|
||
|
|
||
|
if (strcmp(m->mimeencoding, "quoted-printable") == 0)
|
||
|
{
|
||
|
int l=0;
|
||
|
int c;
|
||
|
|
||
|
while ((c=getc(m->inputfp1)) != EOF)
|
||
|
{
|
||
|
if (l > 72)
|
||
|
{
|
||
|
fprintf(m->outputfp, "=\n");
|
||
|
l=0;
|
||
|
}
|
||
|
if (c == '\n')
|
||
|
l=0;
|
||
|
else if (c < ' ' || c == '=' || c > 0x7F)
|
||
|
{
|
||
|
fprintf(m->outputfp, "=%02X",
|
||
|
(int)(unsigned char)c);
|
||
|
l += 3;
|
||
|
continue;
|
||
|
}
|
||
|
else ++l;
|
||
|
putc(c, m->outputfp);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
if (strcmp(m->mimeencoding, "base64") == 0)
|
||
|
{
|
||
|
char buf[BUFSIZ];
|
||
|
int l;
|
||
|
|
||
|
base64m=m;
|
||
|
rfc2045_base64encode_start( &putbase64 );
|
||
|
while ((l=fread(buf, 1, sizeof(buf), m->inputfp1)) > 0)
|
||
|
rfc2045_base64encode(buf, l);
|
||
|
rfc2045_base64encode_end();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* 7bit or 8bit */
|
||
|
|
||
|
while ((c=getc(m->inputfp1)) != EOF)
|
||
|
putc(c, m->outputfp);
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
|
||
|
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, str_size_t(++n, buf));
|
||
|
strcat(bbuf, "_");
|
||
|
strcat(bbuf, str_time_t(t, buf));
|
||
|
strcat(bbuf, "_");
|
||
|
strcat(bbuf, 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 (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)
|
||
|
*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 >= 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]);
|
||
|
close(1);
|
||
|
dup(pipefd[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, 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);
|
||
|
}
|
||
|
|