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); | ||
|  | } | ||
|  | 
 |