diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 | 
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 | 
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /gpglib/gpg.c | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-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 'gpglib/gpg.c')
| -rw-r--r-- | gpglib/gpg.c | 2235 | 
1 files changed, 2235 insertions, 0 deletions
| diff --git a/gpglib/gpg.c b/gpglib/gpg.c new file mode 100644 index 0000000..b63214c --- /dev/null +++ b/gpglib/gpg.c @@ -0,0 +1,2235 @@ +/* +** Copyright 2001-2011 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#if	HAVE_FCNTL_H +#include <fcntl.h> +#endif +#include <sys/types.h> +#if	HAVE_SYS_TIME_H +#include	<sys/time.h> +#endif +#if HAVE_SYS_WAIT_H +#include	<sys/wait.h> +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +#include "mimegpgheader.h" +#include "mimegpgstack.h" +#include "mimegpgfork.h" +#include "tempname.h" +#include "gpglib.h" +#include "rfc822/encode.h" +#include "rfc2045/rfc2045.h" + +static int my_rewind(FILE *fp) +{ +	if (fflush(fp) || ferror(fp) || fseek(fp, 0L, SEEK_SET)) +		return (-1); +	clearerr(fp); +	return (0); +} + +int libmail_gpg_inputfunc_readfp(char *buf, size_t cnt, void *vp) +{ +	FILE *fp=(FILE *)vp; +	size_t i; +	int c; + +	if (cnt == 0) +		return -1; + +	--cnt; + +	for (i=0; i<cnt; i++) +	{ +		if ((c=getc(fp)) == EOF) +		{ +			if (i == 0) +				return -1; +			break; +		} +		buf[i]=c; + +		if (c == '\n') +		{ +			++i; +			break; +		} +	} +	buf[i]=0; +	return 0; +} + +void libmail_gpg_noexec(int fd) +{ +#ifdef FD_CLOEXEC +	fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif +} + +/* +** Check if the line just read is a MIME boundary line.  Search the +** current MIME stack for a matching MIME boundary delimiter. +*/ + +static struct mimestack *is_boundary(struct mimestack *s, const char *line, +				     int *isclosing) +{ +	struct mimestack *b; + +	if (line[0] != '-' || line[1] != '-' || +	    (b=libmail_mimestack_search(s, line+2)) == 0) +		return (NULL); + + +	*isclosing=strncmp(line+2+strlen(b->boundary), "--", 2) == 0; +	return (b); +} + +static const char *get_boundary(struct mimestack *, +				const char *, +				FILE *); + +/* +** Skip until EOF or a MIME boundary delimiter other than a closing MIME +** boundary delimiter.  After returning from bind_boundary we expect to +** see MIME headers.  Copy any intermediate lines to fpout. +*/ + +static void find_boundary(struct mimestack **stack, int *iseof, +			  int (*input_func)(char *, size_t, void *vp), +			  void *input_func_arg, +			  void (*output_func)(const char *, +					      size_t, +					      void *), +			  void *output_func_arg, +			  int doappend) +{ +	char buf[BUFSIZ]; + +	for (;;) +	{ +		int is_closing; +		struct mimestack *b; + +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			return; +		} + +		if (!(b=is_boundary(*stack, buf, &is_closing))) +		{ +			if (output_func) +				(*output_func)(buf, strlen(buf), +					       output_func_arg); + +			while (strchr(buf, '\n') == 0) +			{ +				if ( (*input_func)(buf, sizeof(buf), +						   input_func_arg)) +				{ +					*iseof=1; +					return; +				} +				if (output_func) +					(*output_func)(buf, strlen(buf), +						       output_func_arg); +			} +			continue; +		} + +		if (output_func) +		{ +			(*output_func)("--", 2, output_func_arg); +			(*output_func)(b->boundary, strlen(b->boundary), +				       output_func_arg); + +			if (is_closing) +				(*output_func)("--", 2, output_func_arg); + +			(*output_func)("\n", 1, output_func_arg); +		} + +		if (is_closing) +		{ +			libmail_mimestack_pop_to(stack, b); +			continue; +		} +		break; +	} +} + + +/* +** Read a set of headers. +*/ + +static struct header *read_headers(struct mimestack **stack, int *iseof, +				   int (*input_func)(char *, size_t, void *vp), +				   void *input_func_arg, +				   void (*output_func)(const char *, +						       size_t, +						       void *), +				   void *output_func_arg, +				   int doappend, +				   int *errflag) +{ +	char buf[BUFSIZ]; +	struct read_header_context rhc; +	struct header *h; + +	*errflag=0; +	libmail_readheader_init(&rhc); + +	while (!*iseof) +	{ +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} + +		if (READ_START_OF_LINE(rhc)) +		{ +			struct mimestack *b; +			int is_closing; + +			if (strcmp(buf, "\n") == 0 +			    || strcmp(buf, "\r\n") == 0) +				break; + +			b=is_boundary(*stack, buf, &is_closing); + +			if (b) +			{ +				/* +				** Corrupted MIME message.  We should NOT +				** see a MIME boundary in the middle of the +				** headers! +				** +				** Ignore this damage. +				*/ + +				struct header *p; + +				h=libmail_readheader_finish(&rhc); + +				for (p=h; p; p=p->next) +					(*output_func)(p->header, +						       strlen(p->header), +						       output_func_arg); + +				(*output_func)("--", 2, output_func_arg); +				(*output_func)(b->boundary, +					       strlen(b->boundary), +					       output_func_arg); + +				if (is_closing) +					(*output_func)("--", 2, +						       output_func_arg); + +				(*output_func)("\n", 1, output_func_arg); + +				if (is_closing) +				{ +					libmail_mimestack_pop_to(stack, b); +					find_boundary(stack, iseof, +						      input_func, +						      input_func_arg, +						      output_func, +						      output_func_arg, +						      doappend); +				} +				libmail_header_free(h); + +				libmail_readheader_init(&rhc); +				continue; /* From the top */ +			} +		} +		if (libmail_readheader(&rhc, buf) < 0) +		{ +			libmail_header_free(libmail_readheader_finish(&rhc)); +			*errflag= -1; +			return NULL; +		} +	} + +	return (libmail_readheader_finish(&rhc)); +} + +/* +** Here we do actual signing/encoding +*/ + +static int encode_header(const char *h) +{ +	if (strncasecmp(h, "content-", 8) == 0) +		return (1); +	return (0); +} + +struct gpg_fork_output_info { +	void (*output_func)(const char *, size_t, void *); +	void *output_func_arg; +	struct gpgmime_forkinfo *gpgptr; +}; + +static int gpg_fork_output(const char *p, size_t n, void *dummy) +{ +	struct gpg_fork_output_info *info=(struct gpg_fork_output_info *)dummy; + +	(*info->output_func)(p, n, info->output_func_arg); +	return 0; +} + + +static int dogpgencrypt(const char *gpghome, +			const char *passphrase_fd, +			struct mimestack **stack, +			struct header *h, int *iseof, +			int (*input_func)(char *, size_t, void *vp), +			void *input_func_arg, +			void (*output_func)(const char *, +					    size_t, +					    void *), +			void *output_func_arg, +			int argc, +			char **argv, +			int dosign, +			void (*errhandler)(const char *, void *), +			void *errhandler_arg) +{ +	struct header *hp; +	char buf[BUFSIZ]; +	struct gpgmime_forkinfo gpg; +	int clos_flag=0; +	struct mimestack *b=0; +	int rc; +	const char *boundary; +	int need_crlf; +	struct gpg_fork_output_info gfoi; + +	boundary=get_boundary(*stack, "", NULL); + +	gfoi.output_func=output_func; +	gfoi.output_func_arg=output_func_arg; + +	if (libmail_gpgmime_forksignencrypt(gpghome, passphrase_fd, +					    (dosign ? GPG_SE_SIGN:0) +					    | GPG_SE_ENCRYPT, +					    argc, argv, +					    &gpg_fork_output, &gfoi, +					    &gpg)) +	{ +		return -1; +	} + +	for (hp=h; hp; hp=hp->next) +	{ +		if (encode_header(hp->header)) +			continue; + +		(*output_func)(hp->header, strlen(hp->header), +			       output_func_arg); +	} + +#define C(s) (*output_func)( s, sizeof(s)-1, output_func_arg) +#define S(s) (*output_func)( s, strlen(s), output_func_arg) + + +	C("Content-Type: multipart/encrypted;\n" +	  "    boundary=\""); + +	S(boundary); + +	C("\";\n" +	  "    protocol=\"application/pgp-encrypted\"\n" +	  "\n" +	  "This is a MIME GnuPG-encrypted message.  If you see this text, it means\n" +	  "that your E-mail or Usenet software does not support MIME encrypted messages.\n" +	  "The Internet standard for MIME PGP messages, RFC 2015, was published in 1996.\n" +	  "To open this message correctly you will need to install E-mail or Usenet\n" +	  "software that supports modern Internet standards.\n" +	  "\n--"); + +	S(boundary); + +	C("\n" +	  "Content-Type: application/pgp-encrypted\n" +	  "Content-Transfer-Encoding: 7bit\n" +	  "\n" +	  "Version: 1\n" +	  "\n--"); + +	S(boundary); + +	C("\n" +	  "Content-Type: application/octet-stream\n" +	  "Content-Transfer-Encoding: 7bit\n\n"); + +#undef C +#undef S + +	/* For Eudora compatiblity */ +        libmail_gpgmime_write(&gpg, "Mime-Version: 1.0\r\n", 19); + +	for (hp=h; hp; hp=hp->next) +	{ +		const char *p; + +		if (!encode_header(hp->header)) +			continue; + +		for (p=hp->header; *p; p++) +		{ +			if (*p == '\r') +				continue; + +			if (*p == '\n') +				libmail_gpgmime_write(&gpg, "\r\n", 2); +			else +				libmail_gpgmime_write(&gpg, p, 1); +		} +	} + +	/* +	** Chew the content until the next MIME boundary. +	*/ +	need_crlf=1; + +	while (!*iseof) +	{ +		const char *p; + +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} + +		if (need_crlf) +		{ +			if ((b=is_boundary(*stack, buf, &clos_flag)) != NULL) +				break; + +			libmail_gpgmime_write(&gpg, "\r\n", 2); +		} + +		need_crlf=0; +		for (;;) +		{ +			for (p=buf; *p; p++) +			{ +				if (*p == '\r') +					continue; +				if (*p == '\n') +				{ +					need_crlf=1; +					break; +				} + +				libmail_gpgmime_write(&gpg, p, 1); +			} +			if (*p == '\n') +				break; + +			if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +			{ +				*iseof=1; +				break; +			} +		} +	} + +	/* +	** This needs some 'splainin.  Note that we spit out a newline at +	** the BEGINNING of each line, above.  This generates the blank +	** header->body separator line.  Now, if we're NOT doing multiline +	** content, we need to follow the last line of the content with a +	** newline.  If we're already doing multiline content, that extra +	** newline (if it exists) is already there. +	*/ + +	if (!*stack) +	{ +		libmail_gpgmime_write(&gpg, "\r\n", 2); +	} + +	rc=libmail_gpgmime_finish(&gpg); + +	if (rc) +	{ +		(*errhandler)(libmail_gpgmime_getoutput(&gpg), errhandler_arg); +		return (-1); +	} + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(boundary, strlen(boundary), output_func_arg); +	(*output_func)("--\n", 3, output_func_arg); + +	if (*iseof) +		return 0; + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(b->boundary, strlen(b->boundary), output_func_arg); +	if (clos_flag) +		(*output_func)("--", 2, output_func_arg); +	(*output_func)("\n", 1, output_func_arg); + +	if (clos_flag) +	{ +		libmail_mimestack_pop_to(stack, b); +		find_boundary(stack, iseof, input_func, +			      input_func_arg, output_func, +			      output_func_arg, 1); +	} + +	return 0; +} + +static int dogpgsign(const char *gpghome, const char *passphrase_fd, +		     struct mimestack **stack, struct header *h, int *iseof, +		     int (*input_func)(char *, size_t, void *vp), +		     void *input_func_arg, +		     void (*output_func)(const char *, +					 size_t, +					 void *), +		     void *output_func_arg, +		     int argc, +		     char **argv, +		     void (*errhandler)(const char *, void *), +		     void *errhandler_arg) +{ +	struct header *hp; +	char buf[8192]; +	struct gpgmime_forkinfo gpg; +	int clos_flag=0; +	struct mimestack *b=0; +	int rc=0; +	char signed_content_name[TEMPNAMEBUFSIZE]; +	int signed_content; +	FILE *signed_content_fp; +	const char *boundary; +	int need_crlf; +	struct gpg_fork_output_info gfoi; + +	for (hp=h; hp; hp=hp->next) +	{ +		if (encode_header(hp->header)) +			continue; +		(*output_func)(hp->header, strlen(hp->header), +			       output_func_arg); +	} + +	signed_content=libmail_tempfile(signed_content_name); +	if (signed_content < 0 || +	    (signed_content_fp=fdopen(signed_content, "w+")) == NULL) +	{ +		if (signed_content >= 0) +		{ +			close(signed_content); +			unlink(signed_content_name); +		} +		return -1; + +	} +	libmail_gpg_noexec(fileno(signed_content_fp)); +	unlink(signed_content_name);	/* UNIX semantics */ + +	for (hp=h; hp; hp=hp->next) +	{ +		const char *p; + +		if (!encode_header(hp->header)) +			continue; + +		for (p=hp->header; *p; p++) +		{ +			if (*p == '\r') +				continue; + +			if (*p == '\n') +				putc('\r', signed_content_fp); +			putc(*p, signed_content_fp); +		} +	} + +	/* +	** Chew the content until the next MIME boundary. +	*/ +	need_crlf=1; +	while (!*iseof) +	{ +		const char *p; + +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} + +		if (need_crlf) +		{ +			if ((b=is_boundary(*stack, buf, &clos_flag)) != NULL) +				break; + +			fprintf(signed_content_fp, "\r\n"); +		} + +		need_crlf=0; +		for (;;) +		{ +			for (p=buf; *p; p++) +			{ +				if (*p == '\r') +					continue; +				if (*p == '\n') +				{ +					need_crlf=1; +					break; +				} + +				putc(*p, signed_content_fp); +			} +			if (*p == '\n') +				break; + +			if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +			{ +				*iseof=1; +				break; +			} +		} +	} + +	/* +	** This needs some 'splainin.  Note that we spit out a newline at +	** the BEGINNING of each line, above.  This generates the blank +	** header->body separator line.  Now, if we're NOT doing multiline +	** content, we need to follow the last line of the content with a +	** newline.  If we're already doing multiline content, that extra +	** newline (if it exists) is already there. +	*/ + +	if (!*stack) +	{ +		fprintf(signed_content_fp, "\r\n"); +	} + +	if (fflush(signed_content_fp) < 0 || ferror(signed_content_fp)) +	{ +		fclose(signed_content_fp); +		return (-1); +	} + +	boundary=get_boundary(*stack, "", signed_content_fp); + +	if (my_rewind(signed_content_fp) < 0) +	{ +		fclose(signed_content_fp); +		return (-1); +	} + +#define C(s) (*output_func)( s, sizeof(s)-1, output_func_arg) +#define S(s) (*output_func)( s, strlen(s), output_func_arg) + +	C("Content-Type: multipart/signed;\n" +	  "    boundary=\""); +	S(boundary); +	C("\";\n" +	  "    micalg=pgp-sha1;" +	  " protocol=\"application/pgp-signature\"\n" +	  "\n" +	  "This is a MIME GnuPG-signed message.  If you see this text, it means that\n" +	  "your E-mail or Usenet software does not support MIME signed messages.\n" +	  "The Internet standard for MIME PGP messages, RFC 2015, was published in 1996.\n" +	  "To open this message correctly you will need to install E-mail or Usenet\n" +	  "software that supports modern Internet standards.\n" +	  "\n--"); +	S(boundary); +	C("\n"); + + +	gfoi.output_func=output_func; +	gfoi.output_func_arg=output_func_arg; +	gfoi.gpgptr= &gpg; + +	if (libmail_gpgmime_forksignencrypt(gpghome, passphrase_fd, +					    GPG_SE_SIGN, +					    argc, argv, +					    &gpg_fork_output, &gfoi, +					    &gpg)) +	{ +		fclose(signed_content_fp); +		return (-1); +	} + +	while (fgets(buf, sizeof(buf), signed_content_fp) != NULL) +	{ +		char *p; +		size_t j, k; + +		libmail_gpgmime_write(&gpg, buf, strlen(buf)); + +		p=buf; +		for (j=k=0; p[j]; j++) +			if (p[j] != '\r') +				p[k++]=p[j]; + +		if (k) +			(*output_func)(p, k, output_func_arg); +	} + +	C("\n--"); +	S(boundary); + +	C("\n" +	  "Content-Type: application/pgp-signature\n" +	  "Content-Transfer-Encoding: 7bit\n\n"); + +#undef C +#undef S + +	if (libmail_gpgmime_finish(&gpg)) +		rc= -1; /* TODO */ + +	if (rc) +	{ +		(*errhandler)(libmail_gpgmime_getoutput(&gpg), +			      errhandler_arg); +		fclose(signed_content_fp); +		return -1; +	} + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(boundary, strlen(boundary), output_func_arg); +	(*output_func)("--\n", 3, output_func_arg); + +	fclose(signed_content_fp); +	if (*iseof) +		return 0; + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(b->boundary, strlen(b->boundary), output_func_arg); +	if (clos_flag) +		(*output_func)("--", 2, output_func_arg); +	(*output_func)("\n", 1, output_func_arg); + +	if (clos_flag) +	{ +		libmail_mimestack_pop_to(stack, b); +		find_boundary(stack, iseof, input_func, input_func_arg, +			      output_func, output_func_arg, 1); +	} +	return 0; +} + +static int isgpg(struct mime_header *); +static int checksign(const char *gpghome, +		     const char *passphrase_fd, +		     struct mimestack **, int *, struct header *, +		     int (*input_func)(char *, size_t, void *vp), +		     void *input_func_arg, +		     void (*)(const char *, size_t, void *), +		     void *, +		     int, char **, +		     int *); +static int decrypt(const char *gpghome, +		   const char *passphrase_fd, +		   struct mimestack **, int *, struct header *, +		   int (*input_func)(char *, size_t, void *vp), +		   void *input_func_arg, +		   void (*)(const char *, size_t, void *), +		   void *, +		   int, char **, +		   int *); + +static void print_noncontent_headers(struct header *h, +				     void (*output_func)(const char *, +							 size_t, +							 void *), +				     void *output_func_arg) +{ +	struct header *p; + +	for (p=h; p; p=p->next) +	{ +		if (strncasecmp(p->header, "content-", 8) == 0) +			continue; +		(*output_func)(p->header, strlen(p->header), output_func_arg); +	} +} + +static int dosignencode2(int dosign, int doencode, int dodecode, +			 const char *gpghome, +			 const char *passphrase_fd, +			 int (*input_func)(char *, size_t, void *vp), +			 void *input_func_arg, +			 void (*output_func)(const char *, +					     size_t, +					     void *), +			 void *output_func_arg, +			 void (*errhandler_func)(const char *, void *), +			 void *errhandler_arg, +			 int argc, char **argv, +			 int *status) +{ +	struct mimestack *boundary_stack=0; +	int iseof=0; + +	*status=0; + +	while (!iseof) +	{ +		int errflag; + +		static const char ct_s[]="content-type:"; +		struct header *h=read_headers(&boundary_stack, &iseof, +					      input_func, input_func_arg, +					      output_func, +					      output_func_arg, +					      dodecode ? 0:1, +					      &errflag), +			*hct; + +		if (errflag) +			return 1; + +		if (iseof && !h) +			continue;	/* Artifact */ + +		hct=libmail_header_find(h, ct_s); + +		/* +		** If this is a multipart MIME section, we can keep on +		** truckin'. +		** +		*/ + +		if (hct) +		{ +			struct mime_header *mh= +				libmail_mimeheader_parse(hct->header+ +							 (sizeof(ct_s)-1)); +			const char *bv; + +			if (!mh) +			{ +				libmail_header_free(h); +				return (-1); +			} + +			if (strcasecmp(mh->header_name, "multipart/x-mimegpg") +			    == 0) +			{ +				/* Punt */ + +				char *buf=malloc(strlen(hct->header)+100); +				const char *p; + +				if (!buf) +				{ +					libmail_mimeheader_free(mh); +					libmail_header_free(h); +					return (-1); +				} +				strcpy(buf, "Content-Type: multipart/mixed"); +				p=strchr(hct->header, ';'); +				strcat(buf, p ? p:""); +				free(hct->header); +				hct->header=buf; + +				libmail_mimeheader_free(mh); +				mh=libmail_mimeheader_parse(hct->header+ +							    sizeof(ct_s)-1); +				if (!mh) +				{ +					libmail_header_free(h); +					return (-1); +				} +			} + +			if (strncasecmp(mh->header_name, "multipart/", 10)==0 +			    && (bv=libmail_mimeheader_getattr(mh, "boundary")) != 0 + +			    && (doencode & LIBMAIL_GPG_ENCAPSULATE) == 0 + +			    && !dosign +			    ) +			{ +				struct header *p; + +				if (libmail_mimestack_push(&boundary_stack, +							   bv) < 0) +				{ +					libmail_header_free(h); +					return (-1); +				} + +				if (dodecode) +				{ +					int rc; + +					if (strcasecmp(mh->header_name, +						       "multipart/signed")==0 +					    && (dodecode & LIBMAIL_GPG_CHECKSIGN) +					    && isgpg(mh)) +					{ +						int errflag; + +						print_noncontent_headers(h, +									 output_func, +									 output_func_arg +									 ); +						libmail_mimeheader_free(mh); +						rc=checksign(gpghome, +							     passphrase_fd, +							     &boundary_stack, +							     &iseof, +							     h, +							     input_func, +							     input_func_arg, +							     output_func, +							     output_func_arg, +							     argc, argv, +							     &errflag); +						libmail_header_free(h); + +						if (errflag) +							*status |= +								LIBMAIL_ERR_VERIFYSIG; +						if (rc) +							return -1; + +						continue; +					} + +					if (strcasecmp(mh->header_name, +						       "multipart/encrypted") +					    ==0 +					    && (dodecode & LIBMAIL_GPG_UNENCRYPT) +					    && isgpg(mh)) +					{ +						int errflag; + +						print_noncontent_headers(h, +									 output_func, +									 output_func_arg +									 ); +						libmail_mimeheader_free(mh); +						rc=decrypt(gpghome, +							   passphrase_fd, +							   &boundary_stack, +							   &iseof, +							   h, +							   input_func, +							   input_func_arg, +							   output_func, +							   output_func_arg, +							   argc, argv, +							   &errflag); +						libmail_header_free(h); + +						if (errflag) +							*status |= +								LIBMAIL_ERR_DECRYPT; +						if (rc) +							return -1; +						continue; +					} +				} + +				for (p=h; p; p=p->next) +				{ +					(*output_func)(p->header, +						       strlen(p->header), +						       output_func_arg); +				} + +				(*output_func)("\n", 1, output_func_arg); +				libmail_header_free(h); +				libmail_mimeheader_free(mh); + +				find_boundary(&boundary_stack, &iseof, +					      input_func, +					      input_func_arg, +					      output_func, +					      output_func_arg, dodecode ? 0:1); +				continue; +			} +			libmail_mimeheader_free(mh); +		} + +		if (dodecode) +		{ +			struct header *p; +			int is_message_rfc822=0; + +			for (p=h; p; p=p->next) +			{ +				(*output_func)(p->header, +					       strlen(p->header), +					       output_func_arg); +			} +			(*output_func)("\n", 1, output_func_arg); + +			/* +			** If this is a message/rfc822 attachment, we can +			** resume reading the next set of headers. +			*/ + +			hct=libmail_header_find(h, ct_s); +			if (hct) +			{ +				struct mime_header *mh= +					libmail_mimeheader_parse(hct->header+ +								 (sizeof(ct_s) +								  -1)); +				if (!mh) +				{ +					libmail_header_free(h); +					return (-1); +				} + +				if (strcasecmp(mh->header_name, +					       "message/rfc822") == 0) +					is_message_rfc822=1; +				libmail_mimeheader_free(mh); +			} +			libmail_header_free(h); + +			if (!is_message_rfc822) +				find_boundary(&boundary_stack, &iseof, +					      input_func, +					      input_func_arg, +					      output_func, +					      output_func_arg, 0); +			continue; +		} + +		if (doencode ? +		    dogpgencrypt(gpghome, +				 passphrase_fd, +				 &boundary_stack, h, &iseof, +				 input_func, +				 input_func_arg, +				 output_func, +				 output_func_arg, +				 argc, argv, dosign, +				 errhandler_func, errhandler_arg) +		    : +		    dogpgsign(gpghome, +			      passphrase_fd, +			      &boundary_stack, h, &iseof, +			      input_func, +			      input_func_arg, +			      output_func, +			      output_func_arg, +			      argc, argv, +			      errhandler_func, errhandler_arg)) +		{ +			libmail_header_free(h); +			return 1; +		} + +		libmail_header_free(h); +	} + +	return (0); +} + + +static int isgpg(struct mime_header *mh) +{ +	const char *attr; + +	attr=libmail_mimeheader_getattr(mh, "protocol"); + +	if (!attr) +		return (0); + +	if (strcasecmp(attr, "application/pgp-encrypted") == 0) +		return (1); + +	if (strcasecmp(attr, "application/pgp-signature") == 0) +	{ +		return (1); +	} +	return (0); +} + +static int nybble(char c) +{ +	static const char x[]="0123456789ABCDEFabcdef"; + +	const char *p=strchr(x, c); +	int n; + +	if (!p) p=x; + +	n= p-x; +	if (n >= 16) +		n -= 6; +	return (n); +} + +/* +** Check signature +*/ + +static int dochecksign(const char *, const char *, +		       struct mimestack *, +		       FILE *, +		       void (*output_func)(const char *, +					   size_t, +					   void *), +		       void *output_func_arg, +		       const char *, +		       const char *, +		       int, char **, int *); + +static int checksign(const char *gpghome, +		     const char *passphrase_fd, +		     struct mimestack **stack, int *iseof, +		     struct header *h, +		     int (*input_func)(char *, size_t, void *vp), +		     void *input_func_arg, +		     void (*output_func)(const char *, +					 size_t, +					 void *), +		     void *output_func_arg, +		     int argc, char **argv, int *errptr) +{ +	char buf[BUFSIZ]; +	struct header *h2; + +	char signed_content[TEMPNAMEBUFSIZE]; +	char signature[TEMPNAMEBUFSIZE]; +	int signed_file, signature_file; +	FILE *signed_file_fp, *signature_file_fp; +	int clos_flag; +	int need_nl, check_boundary; +	struct mimestack *b=0; +	struct mime_header *mh; +	int qpdecode=0; +	int errflag; + +	*errptr=0; + +	signed_file=libmail_tempfile(signed_content); + +	if (signed_file < 0 || (signed_file_fp=fdopen(signed_file, "w+")) == 0) +	{ +		if (signed_file > 0) +		{ +			close(signed_file); +			unlink(signed_content); +		} +		return -1; +	} +	libmail_gpg_noexec(fileno(signed_file_fp)); + +	find_boundary(stack, iseof, input_func, +		      input_func_arg, NULL, NULL, 0); +	if (*iseof) +		return 0; + +	need_nl=0; +	check_boundary=1; + +	while (!*iseof) +	{ +		const char *p; + +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			continue; +		} + +		if (check_boundary +		    && (b=is_boundary(*stack, buf, &clos_flag)) != 0) +			break; +		if (need_nl) +			fprintf(signed_file_fp, "\r\n"); + +		for (p=buf; *p && *p != '\n'; p++) +			putc(*p, signed_file_fp); +		need_nl=check_boundary= *p != 0; +	} + +	if (my_rewind(signed_file_fp) < 0) +	{ +		fclose(signed_file_fp); +		unlink(signed_content); +		return -1; +	} + +	if (clos_flag) +	{ +		fclose(signed_file_fp); +		unlink(signed_content); +		if (b) +			libmail_mimestack_pop_to(stack, b); +		find_boundary(stack, iseof, input_func, input_func_arg, +			      output_func, output_func_arg, 1); +		return 0; +	} + +	h=read_headers(stack, iseof, input_func, input_func_arg, +		       output_func, output_func_arg, 0, &errflag); + +	if (errflag) +	{ +		fclose(signed_file_fp); +		unlink(signed_content); + +		return (-1); +	} + +	if (!h || !(h2=libmail_header_find(h, "content-type:"))) +	{ +		if (h) +			libmail_header_free(h); +		fclose(signed_file_fp); +		unlink(signed_content); +		if (!*iseof) +			find_boundary(stack, iseof, input_func, input_func_arg, +				      output_func, output_func_arg, 1); +		return 0; +	} + +	mh=libmail_mimeheader_parse(h2->header+sizeof("content-type:")-1); + +	if (!mh) +	{ +		libmail_header_free(h); +		fclose(signed_file_fp); +		unlink(signed_content); +		return (-1); +	} + +	if (strcasecmp(mh->header_name, "application/pgp-signature")) +	{ +		libmail_mimeheader_free(mh); +		libmail_header_free(h); +		fclose(signed_file_fp); +		unlink(signed_content); +		if (!*iseof) +			find_boundary(stack, iseof, input_func, input_func_arg, +				      output_func, output_func_arg, 1); +		return (0); +	} +	libmail_mimeheader_free(mh); + +	/* +	** In rare instances, the signature is qp-encoded. +	*/ + +	if ((h2=libmail_header_find(h, "content-transfer-encoding:")) != NULL) +	{ +		mh=libmail_mimeheader_parse +			(h2->header+sizeof("content-transfer-encoding:")-1); + +		if (!mh) +		{ +			libmail_header_free(h); +			fclose(signed_file_fp); +			unlink(signed_content); +			return -1; +		} + +		if (strcasecmp(mh->header_name, +			       "quoted-printable") == 0) +			qpdecode=1; +		libmail_mimeheader_free(mh); +	} +	libmail_header_free(h); + +	signature_file=libmail_tempfile(signature); + +	if (signature_file < 0 +	    || (signature_file_fp=fdopen(signature_file, "w+")) == 0) +	{ +		if (signature_file > 0) +		{ +			close(signature_file); +			unlink(signature); +		} +		unlink(signed_content); +		return (-1); +	} + +	while (!*iseof) +	{ +		const char *p; + +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			continue; +		} + +		if ((b=is_boundary(*stack, buf, &clos_flag)) != 0) +			break; + +		for (p=buf; *p; p++) +		{ +			int n; + +			if (!qpdecode) +			{ +				putc(*p, signature_file_fp); +				continue; +			} + +			if (*p == '=' && p[1] == '\n') +				break; + +			if (*p == '=' && p[1] && p[2]) +			{ +				n=nybble(p[1]) * 16 + nybble(p[2]); +				if ( (char)n ) +				{ +					putc((char)n, signature_file_fp); +					p += 2; +				} +				p += 2; +				continue; +			} +			putc(*p, signature_file_fp); + +			/* If some spits out qp-lines > BUFSIZ, they deserve +			** this crap. +			*/ +		} +	} + +	fflush(signature_file_fp); +	if (ferror(signature_file_fp)) +	{ +		unlink(signature); +		fclose(signed_file_fp); +		unlink(signed_content); +		return -1; +	} +	if (fclose(signature_file_fp)) +	{ +		unlink(signature); +		unlink(signed_content); +		return -1; +	} + +	errflag=dochecksign(gpghome, +			    passphrase_fd, +			    *stack, signed_file_fp, +			    output_func, output_func_arg, signed_content, +			    signature, +			    argc, argv, errptr); + +	fclose(signed_file_fp); +	unlink(signature); +	unlink(signed_content); + +	if (errflag) +		return -1; + +	while (!clos_flag) +	{ +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} +		if (!(b=is_boundary(*stack, buf, &clos_flag))) +			clos_flag=0; +	} +	if (b) +		libmail_mimestack_pop_to(stack, b); + +	return 0; +} + +static const char *newboundary() +{ +	static char buffer[256]; +	static unsigned counter=0; +	time_t t; +	char hostnamebuf[256]; + +	time(&t); +	hostnamebuf[sizeof(hostnamebuf)-1]=0; +	if (gethostname(hostnamebuf, sizeof(hostnamebuf)-1) < 0) +		hostnamebuf[0]=0; + +	sprintf(buffer, "=_%-1.30s-%u-%u-%04u", +		hostnamebuf, (unsigned)getpid(), +		(unsigned)t, ++counter); +	return (buffer); +} + +static int good_boundary(const char *boundary, +			 struct mimestack *m, const char *errmsg, FILE *fp) +{ +	int dummy; +	int l=strlen(boundary); +	const char *p; +	char buf[BUFSIZ]; + +	if (is_boundary(m, boundary, &dummy)) +		return (0); + +	for (p=errmsg; *p; ) +	{ +		if (*p == '-' && p[1] == '-' && strncasecmp(p+2, boundary, l) +		    == 0) +			return (0); + +		while (*p) +			if (*p++ == '\n') +				break; +	} + +	if (fp) +	{ +		if (my_rewind(fp) < 0) +			return 0; + +		while (fgets(buf, sizeof(buf), fp)) +		{ +			if (buf[0] == '-' && buf[1] == '-' && +			    strncasecmp(buf+2, boundary, l) == 0) +				return (0); +		} +	} +	return (1); +} + +static const char *get_boundary(struct mimestack *m, +				const char *errmsg, +				FILE *fp) +{ +	const char *p; + +	do +	{ +		p=newboundary(); +	} while (!good_boundary(p, m, errmsg, fp)); +	return (p); +} + +static const char *encoding_str(const char *p) +{ +	while (*p) +	{ +		if (*p <= 0 || *p >= 0x7F) +			return ("8bit"); +		++p; +	} +	return ("7bit"); +} + + +static int copyfp(FILE *t, +		  void (*output_func)(const char *, +				      size_t, +				      void *), +		  void *output_func_arg, +		  int stripcr) +{ +	char buf[BUFSIZ]; +	int rc=0; + +	while ((rc=fread(buf, 1, sizeof(buf), t)) > 0) +	{ +		if (stripcr) +		{ +			int i, j; + +			for (i=j=0; i<rc; ++i) +			{ +				if (buf[i] != '\r') +					buf[j++]=buf[i]; +			} +			rc=j; +		} +		(*output_func)(buf, rc, output_func_arg); +	} + +	return rc; +} + +static void open_result_multipart(void (*)(const char *, size_t, void *), +				  void *, +				  int, const char *, const char *, +				  const char *); + +static int dochecksign(const char *gpghome, +		       const char *passphrase_fd, +		       struct mimestack *stack, +		       FILE *content_fp, +		       void (*output_func)(const char *, +					   size_t, +					   void *), +		       void *output_func_arg, +		       const char *content_filename, +		       const char *signature_filename, +		       int argc, +		       char **argv, int *errptr) +{ +	struct gpgmime_forkinfo gpg; +	int i; +	const char *new_boundary; +	const char *output; + +	if (libmail_gpgmime_forkchecksign(gpghome, passphrase_fd, +				  content_filename, +				  signature_filename, +				  argc, argv, +				  &gpg)) +	{ +		return -1; +	} + +	*errptr=i=libmail_gpgmime_finish(&gpg); + +	output=libmail_gpgmime_getoutput(&gpg); + +	new_boundary=get_boundary(stack, output, content_fp); + +	open_result_multipart(output_func, output_func_arg, +			      i, new_boundary, output, +			      libmail_gpgmime_getcharset(&gpg)); + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(new_boundary, strlen(new_boundary), output_func_arg); +	(*output_func)("\n", 1, output_func_arg); + +	if (my_rewind(content_fp) < 0) +	{ +		return -1; +	} + +	if (copyfp(content_fp, output_func, output_func_arg, 1)) +		return -1; + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(new_boundary, strlen(new_boundary), output_func_arg); +	(*output_func)("--\n", 3, output_func_arg); +	return 0; +} + +static void open_result_multipart(void (*output_func)(const char *, +						      size_t, +						      void *), +				  void *output_func_arg, +				  int rc, +				  const char *new_boundary, +				  const char *err_str, +				  const char *err_charset) +{ +#define C(s) (*output_func)( s, sizeof(s)-1, output_func_arg) +#define S(s) (*output_func)( s, strlen(s), output_func_arg) + +	char n[10]; +	const char *p; + +	sprintf(n, "%d", rc); + +	C("Content-Type: multipart/x-mimegpg; xpgpstatus="); + +	S(n); + +	C("; boundary=\""); + +	S(new_boundary); + +	C("\"\n" +	  "\nThis is a MIME GnuPG-processed message.  If you see this text, it means\n" +	  "that your E-mail or Usenetsoftware does not support MIME-formatted messages.\n\n" +	  "--"); + +	S(new_boundary); +	C("\nContent-Type: text/x-gpg-output; charset="); +	S(err_charset); +	C("\nContent-Transfer-Encoding: "); + +	p=encoding_str(err_str); +	S(p); +	C("\n\n"); +	S(err_str); +#undef C +#undef S +} + +static void close_mime(struct mimestack **stack, int *iseof, +		       int (*input_func)(char *, size_t, void *vp), +		       void *input_func_arg, +		       void (*output_func)(const char *, +					   size_t, +					   void *), +		       void *output_func_arg) +{ +	char buf[BUFSIZ]; +	int is_closing; +	struct mimestack *b; + +	for (;;) +	{ +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} + +		(*output_func)(buf, strlen(buf), output_func_arg); +		if (!(b=is_boundary(*stack, buf, &is_closing))) +			continue; +		if (!is_closing) +			continue; + +		libmail_mimestack_pop_to(stack, b); +		break; +	} +} + +static int dodecrypt(const char *, const char *, +		     struct mimestack **, int *, +		     int (*input_func)(char *, size_t, void *vp), +		     void *input_func_arg, +		     FILE *, int, char **, const char *, +		     void (*)(const char *, +			      size_t, +			      void *), +		     void *, +		     int *); + +static void write_temp_fp(const char *p, size_t n, void *vp) +{ +	if (fwrite(p, n, 1, (FILE *)vp) != 1) +		; /* ignored */ +} + +static int decrypt(const char *gpghome, +		   const char *passphrase_fd, +		   struct mimestack **stack, int *iseof, +		   struct header *h, +		   int (*input_func)(char *, size_t, void *vp), +		   void *input_func_arg, +		   void (*output_func)(const char *, +				       size_t, +				       void *), +		   void *output_func_arg, +		   int argc, char **argv, +		   int *errptr) +{ +	struct header *p, *q; +	char temp_file[TEMPNAMEBUFSIZE]; +	int temp_fd; +	FILE *temp_fp; +	struct mime_header *mh; +	int flag; +	int errflag; + +	*errptr=0; +	temp_fd=libmail_tempfile(temp_file); +	if (temp_fd < 0 || (temp_fp=fdopen(temp_fd, "w+")) == 0) +	{ +		if (temp_fd >= 0) +			close(temp_fd); +		return -1; +	} + +	for (p=h; p; p=p->next) +	{ +		fprintf(temp_fp, "%s", p->header); +	} +	putc('\n', temp_fp); + +	find_boundary(stack, iseof, input_func, input_func_arg, +		      write_temp_fp, temp_fp, 0); +	if (*iseof) +	{ +		fclose(temp_fp); +		unlink(temp_file); +		return (0); +	} + +	p=read_headers(stack, iseof, input_func, input_func_arg, write_temp_fp, +		       temp_fp, 0, &errflag); + +	if (*iseof || errflag) +	{ +		libmail_header_free(p); +		fclose(temp_fp); +		unlink(temp_file); + +		if (errflag) +			return -1; +		return 0; +	} + +	q=libmail_header_find(p, "content-type:"); + +	flag=0; + +	if (q) +	{ +		mh=libmail_mimeheader_parse(q->header+13); +		if (!mh) +		{ +			libmail_header_free(p); +			fclose(temp_fp); +			unlink(temp_file); +			return -1; +		} + +		if (strcasecmp(mh->header_name, "application/pgp-encrypted") +		    == 0) +			flag=1; +		libmail_mimeheader_free(mh); +	} + +	for (q=p; q; q=q->next) +	{ +		fprintf(temp_fp, "%s", q->header); +	} +	libmail_header_free(p); +	putc('\n', temp_fp); + +	p=read_headers(stack, iseof, input_func, input_func_arg, +		       write_temp_fp, temp_fp, 0, +		       &errflag); + +	if (*iseof || errflag) +	{ +		libmail_header_free(p); +		fclose(temp_fp); +		unlink(temp_file); + +		if (errflag) +			return -1; + +		return 0; +	} + +	q=libmail_header_find(p, "version:"); + +	if (flag) +	{ +		if (!q || atoi(p->header + 8) != 1) +			flag=0; +	} +	for (q=p; q; q=q->next) +	{ +		fprintf(temp_fp, "%s", q->header); +	} +	libmail_header_free(p); +	putc('\n', temp_fp); + +	find_boundary(stack, iseof, input_func, input_func_arg, +		      write_temp_fp, temp_fp, 0); + +	if (*iseof) +	{ +		fclose(temp_fp); +		unlink(temp_file); +		return 0; +	} + +	p=read_headers(stack, iseof, input_func, input_func_arg, write_temp_fp, +		       temp_fp, 0, &errflag); + +	if (*iseof || errflag) +	{ +		libmail_header_free(p); +		fclose(temp_fp); +		unlink(temp_file); + +		if (errflag) +			return -1; + +		return 0; +	} + +	q=libmail_header_find(p, "content-type:"); + +	if (q && flag) +	{ +		flag=0; +		mh=libmail_mimeheader_parse(q->header+13); +		if (!mh) +		{ +			libmail_header_free(p); +			fclose(temp_fp); +			unlink(temp_file); +			return -1; +		} + +		if (strcasecmp(mh->header_name, "application/octet-stream") +		    == 0) +			flag=1; +		libmail_mimeheader_free(mh); + +		q=libmail_header_find(p, "content-transfer-encoding:"); +		if (q && flag) +		{ +			flag=0; +			mh=libmail_mimeheader_parse(strchr(q->header, ':')+1); +			if (!mh) +			{ +				libmail_header_free(p); +				fclose(temp_fp); +				unlink(temp_file); +				return -1; +			} + +			if (strcasecmp(mh->header_name, "7bit") == 0 || +			    strcasecmp(mh->header_name, "8bit") == 0) +				flag=1; +			libmail_mimeheader_free(mh); +		} +	} + +	for (q=p; q; q=q->next) +	{ +		fprintf(temp_fp, "%s", q->header); +	} +	libmail_header_free(p); +	putc('\n', temp_fp); + +	if (fflush(temp_fp) || ferror(temp_fp) || my_rewind(temp_fp) < 0) +	{ +		fclose(temp_fp); +		unlink(temp_file); +		return -1; +	} + +	if (!flag) +	{ +		int c=copyfp(temp_fp, output_func, output_func_arg, 0); + +		fclose(temp_fp); +		unlink(temp_file); +		close_mime(stack, iseof, input_func, input_func_arg, +			   output_func, output_func_arg); +		return (c); +	} + +	fclose(temp_fp); +	if ((temp_fp=fopen(temp_file, "w+")) == NULL) +	{ +		unlink(temp_file); +		return (-1); +	} +	libmail_gpg_noexec(fileno(temp_fp)); +	errflag=dodecrypt(gpghome, passphrase_fd, +			  stack, iseof, input_func, input_func_arg, +			  temp_fp, argc, argv, temp_file, +			  output_func, output_func_arg, errptr); +	fclose(temp_fp); +	unlink(temp_file); +	return errflag; +} + +static int dumpdecrypt(const char *c, size_t n, void *vp) +{ +	FILE *fp=(FILE *)vp; + +	if (n == 0) +		return 0; + +	if (fwrite(c, n, 1, fp) != 1) +		return -1; +	return (0); +} + +static int dodecrypt(const char *gpghome, +		     const char *passphrase_fd, +		     struct mimestack **stack, int *iseof, +		     int (*input_func)(char *, size_t, void *vp), +		     void *input_func_arg, +		     FILE *fpout, int argc, char **argv, +		     const char *temp_file, +		     void (*output_func)(const char *, +			      size_t, +			      void *), +		     void *output_func_arg, +		     int *errptr) +{ +	struct gpgmime_forkinfo gpg; +	char buf[BUFSIZ]; +	int is_closing; +	struct mimestack *b=NULL; +	int dowrite=1; +	int rc; +	const char *new_boundary; +	const char *output; + +	if (libmail_gpgmime_forkdecrypt(gpghome, +				passphrase_fd, +				argc, argv, &dumpdecrypt, fpout, &gpg)) +		return -1; + +	for (;;) +	{ +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} + +		if (dowrite) +			libmail_gpgmime_write(&gpg, buf, strlen(buf)); + +		if (!(b=is_boundary(*stack, buf, &is_closing))) +			continue; +		dowrite=0; +		if (!is_closing) +			continue; +		break; +	} + +	rc=libmail_gpgmime_finish(&gpg); +	if (fflush(fpout) || ferror(fpout) || my_rewind(fpout) < 0) +	{ +		fclose(fpout); +		unlink(temp_file); +		return -1; +	} + +	if (*iseof) +		return 0; + +	output=libmail_gpgmime_getoutput(&gpg), + +	new_boundary=get_boundary(*stack, output, rc ? NULL:fpout); + +	open_result_multipart(output_func, +			      output_func_arg, rc, new_boundary, +			      output, +			      libmail_gpgmime_getcharset(&gpg)); + +	*errptr=rc; + +#if 0 + +	/* +	** gnupg returns non-zero exit even if succesfully unencrypted, when +	** just the signature is bad. +	*/ +	if (rc == 0) +#endif +	{ +		if (fseek(fpout, 0L, SEEK_SET) < 0) +		{ +			fclose(fpout); +			unlink(temp_file); +			return -1; +		} + +		(*output_func)("\n--", 3, output_func_arg); +		(*output_func)(new_boundary, strlen(new_boundary), +			       output_func_arg); +		(*output_func)("\n", 1, output_func_arg); + +		if (copyfp(fpout, output_func, output_func_arg, 1)) +		    return -1; +	} + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(new_boundary, strlen(new_boundary), +		       output_func_arg); +	(*output_func)("--\n", 3, output_func_arg); + +	libmail_mimestack_pop_to(stack, b); +	return 0; +} + +struct libmail_gpg_errhandler { + +	struct libmail_gpg_info *options; +	int err_flag; +}; + +static void libmail_gpg_errfunc(const char *errmsg, void *vp) +{ +	struct libmail_gpg_errhandler *eh=(struct libmail_gpg_errhandler *)vp; + +	if (!eh->err_flag) +	{ +		eh->err_flag=1; + +		(*eh->options->errhandler_func)(errmsg, +						eh->options->errhandler_arg); +	} +} +  +static int input_func_from_fp(char *buf, size_t cnt, void *vp) +{ +	if (fgets(buf, cnt, (FILE *)vp) == NULL) +		return (-1); +	return (0); +} + +/* +** When signing, but not encoding, signed text must be 7bit, as per RFC. +** +** Use rfc2045's rewriter to do this. +*/ + +static int dosignencode(int dosign, int doencode, int dodecode, +			const char *gpghome, +			const char *passphrase_fd, +			int (*input_func)(char *, size_t, void *vp), +			void *input_func_arg, +			void (*output_func)(const char *, +					    size_t, +					    void *), +			void *output_func_arg, +			void (*errhandler_func)(const char *, void *), +			void *errhandler_arg, +			int argc, char **argv, +			int *status) +{ +	char temp_decode_name[TEMPNAMEBUFSIZE]; +	int fdin; +	int fdout; +	FILE *fdin_fp; +	char buffer[8192]; +	struct rfc2045src *src; +	struct rfc2045 *rfcp; +	int rc; + +	if (!dosign || doencode) +		return dosignencode2(dosign, doencode, dodecode, +				     gpghome, +				     passphrase_fd, +				     input_func, +				     input_func_arg, +				     output_func, +				     output_func_arg, +				     errhandler_func, +				     errhandler_arg, +				     argc, argv, status); + +	/* Save the message into a temp file, first */ + +	fdin=libmail_tempfile(temp_decode_name); + +	if (fdin < 0 || +	    (fdin_fp=fdopen(fdin, "w+")) == NULL) +	{ +		if (fdin >= 0) +			close(fdin); + +		(*errhandler_func)("Cannot create temporary file", +				   errhandler_arg); +		return (-1); +	} + +	unlink(temp_decode_name); + +	if (!(rfcp=rfc2045_alloc_ac())) +	{ +		(*errhandler_func)(strerror(errno), errhandler_arg); +		fclose(fdin_fp); +		return (-1); +	} + +	while ( (*input_func)(buffer, sizeof(buffer), input_func_arg) == 0) +	{ +		size_t l=strlen(buffer); + +		if (fwrite(buffer, l, 1, fdin_fp) != 1) +		{ +			(*errhandler_func)(strerror(errno), errhandler_arg); +			fclose(fdin_fp); +			rfc2045_free(rfcp); +			return (-1); +		} + +		/* Parse the message at the same time it's being saved */ + +		rfc2045_parse(rfcp, buffer, l); +	} + +	if (fseek(fdin_fp, 0L, SEEK_SET) < 0) +	{ +		(*errhandler_func)(strerror(errno), errhandler_arg); +		fclose(fdin_fp); +		rfc2045_free(rfcp); +		return (-1); +	} + +	if (!rfc2045_ac_check(rfcp, RFC2045_RW_7BIT)) +	{ +		rfc2045_free(rfcp); + +		/* No need to rewrite, just do this */ + +		rc=dosignencode2(dosign, doencode, dodecode, +				     gpghome, +				     passphrase_fd, +				     input_func_from_fp, +				     fdin_fp, +				     output_func, +				     output_func_arg, +				     errhandler_func, +				     errhandler_arg, +				     argc, argv, status); + +		fclose(fdin_fp); + +		return rc; +	} + +	/* Rewrite the message into another temp file */ + +	fdout=libmail_tempfile(temp_decode_name); + +	src=rfc2045src_init_fd(fileno(fdin_fp)); + +	if (fdout < 0 || src == NULL || +	    rfc2045_rewrite(rfcp, src, fdout, "mimegpg") < 0 || +	    lseek(fdout, 0L, SEEK_SET) < 0) +	{ +		if (fdout >= 0) +			close(fdout); +		if (src) +			rfc2045src_deinit(src); + +		(*errhandler_func)(strerror(errno), errhandler_arg); +		rfc2045_free(rfcp); +		fclose(fdin_fp); +		return (-1); +	} +	fclose(fdin_fp); +	rfc2045_free(rfcp); +	rfc2045src_deinit(src); + +	/* Now, read the converted message, from the temp file */ + +	if ((fdin_fp=fdopen(fdout, "w+")) == NULL) +	{ +		close(fdout); + +		(*errhandler_func)("Cannot create temporary file", +				   errhandler_arg); +		return (-1); +	} + +	rc=dosignencode2(dosign, doencode, dodecode, +			 gpghome, +			 passphrase_fd, +			 input_func_from_fp, +			 fdin_fp, +			 output_func, +			 output_func_arg, +			 errhandler_func, +			 errhandler_arg, +			 argc, argv, status); +	fclose(fdin_fp); +	return rc; +} + +int libmail_gpg_signencode(int dosign, +			   int doencode, +			   /* +			   ** One of LIBMAIL_GPG_INDIVIDUAL or +			   ** LIBMAIL_GPG_ENCAPSULATE +			   */ +			   struct libmail_gpg_info *options) +{ +	int rc; +	struct libmail_gpg_errhandler eh; + +	eh.options=options; +	eh.err_flag=0; + +	if (doencode != LIBMAIL_GPG_INDIVIDUAL && +	    doencode != LIBMAIL_GPG_ENCAPSULATE) +		doencode=0; + +	if (!dosign && !doencode) +	{ +		(*options->errhandler_func)("Invalid arguments to" +					    " libmail_gpg_signencode", +					    options->errhandler_arg); +		return -1; +	} + +	rc=dosignencode(dosign, doencode, 0, +			options->gnupghome, +			options->passphrase_fd, +			options->input_func, +			options->input_func_arg, +			options->output_func, +			options->output_func_arg, +			&libmail_gpg_errfunc, +			&eh, +			options->argc, +			options->argv, +			&options->errstatus); + +	if (rc && !eh.err_flag) +		(*options->errhandler_func)(strerror(errno), +					    options->errhandler_arg); +	return rc; +} + +int libmail_gpg_decode(int mode, +		       /* +		       ** LIBMAIL_GPG_UNENCRYPT OR LIBMAIL_GPG_CHECKSIGN +		       */ +		       struct libmail_gpg_info *options) +{ +	int rc; +	struct libmail_gpg_errhandler eh; + +	eh.options=options; +	eh.err_flag=0; + +	if ((mode & (LIBMAIL_GPG_UNENCRYPT|LIBMAIL_GPG_CHECKSIGN)) == 0) +	{ +		(*options->errhandler_func)("Invalid arguments to" +					    " libmail_gpg_decode", +					    options->errhandler_arg); +		return -1; +	} + +	rc=dosignencode(0, 0, mode, +			options->gnupghome, +			options->passphrase_fd, +			options->input_func, +			options->input_func_arg, +			options->output_func, +			options->output_func_arg, +			&libmail_gpg_errfunc, +			&eh, +			options->argc, +			options->argv, +			&options->errstatus); + +	if (rc && !eh.err_flag) +		(*options->errhandler_func)(strerror(errno), +					    options->errhandler_arg); +	return rc; +} | 
