/* $Id: pgp.cpp,v 1.22 2004/01/02 03:54:01 fesnel Exp $ */
/*******************************************************************************
 *   This program is part of a library used by the Archimedes email client     * 
 *                                                                             *
 *   Copyright : (C) 1995-1998 Gennady B. Sorokopud (gena@NetVision.net.il)    *
 *               (C) 1995 Ugen. J. S. Antsilevich (ugen@latte.worldbank.org)   *
 *               (C) 1998-2004 by the Archimedes Project                       *
 *                   http://sourceforge.net/projects/archimedes                *
 *                                                                             *
 *             --------------------------------------------                    *
 *                                                                             *
 *   This program is free software; you can redistribute it and/or modify      *
 *   it under the terms of the GNU Library General Public License as published *
 *   by the Free Software Foundation; either version 2 of the License, or      *
 *   (at your option) any later version.                                       *
 *                                                                             *
 *   This program is distributed in the hope that it will be useful,           *
 *   but WITHOUT ANY WARRANTY, without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU Library General Public License for more details.                      *
 *                                                                             *
 *   You should have received a copy of the GNU Library General Public License *
 *   along with this program; if not, write to the Free Software               *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA.  *
 *                                                                             *
 ******************************************************************************/

#include <fmail.h>
#include <pgp.h>

#include "addrbook_util.h"

static char *get_pgp263_command_line(int, struct pgpargs *, char *, int);
static char *get_pgp500_command_line(int, struct pgpargs *, char *, int);
static char *get_pgp651_command_line(int, struct pgpargs *, char *, int);
static char *get_gpg_command_line(int, struct pgpargs *, char *, int);

char *phrase = NULL;	/* scrambled passphrase */
static int pgpext;      /* PGP exit status */

void pgp_timer_cb() {
	char *p;

	if(phrase) {
		p = phrase;
		while(*p)
			*p++ = '\0';
		free(phrase);
		phrase = NULL;
	}
}

void pgp_cleanup(struct _proc_info *pinfo) {
	pgpext = pinfo->status;
}

void scramble(char *string) {
	unsigned char *p;

	if(!string)
		return;

	p = (unsigned char *) string;
	while(*p != '\0') {
		if(*p != 0xaa)
			*p = *p ^ 0xaa;
		p++;
	}

	return;
}

void init_pgpargs(struct pgpargs *pargs) {
	pargs->userid = NULL;
	pargs->recp = NULL;
	pargs->passphrase = NULL;
	pargs->msg = NULL;
}

void pgp_init(struct _proc_info *pinfo) {
	putenv("PGPPASSFD=0");  /* see pgp/doc/appnote.doc */
}

int pgp_fetch_key_from_http(char *host, unsigned int keyid) {
	int sock, n, res = 0, keyfound = 0;
	char buf[256], kfile[256], pbuf[32];
	FILE *kfd;

	if((sock = ConMan.host_connect(host, "11371", NULL)) == -1)
		return -1;

	/* HKP (Horowitz Key Protocol) */
	snprintf(buf, sizeof(buf),
			 "GET /pks/lookup?op=get&exact=on&search=0x%X HTTP/1.0\n",
			 keyid);
	strcat(buf, "User-Agent: XFMail (Unix)\n\r\n\r");
	if(send(sock, buf, strlen(buf), 0) == -1)
		return -1;

	strcpy(kfile, get_temp_file("pgphttp"));
	if((kfd = fopen(kfile, "w")) == NULL) {
		display_msg(MSG_WARN, "PGP keyserver (HKP)",
					"Can not create temp. file %-.64s", kfile);
		ConMan.del_cinfo(sock);
		return -1;
	}

	*pbuf = '\0';
	do {
		if((n = my_check_io_forms(sock, 0, SOCKET_TIMEOUT)) < 0) {
			if(n == -2)
				display_msg(MSG_WARN, "PGP keyserver (HKP)",
							"transfer aborted");
			res = -1;
			break;
		}

		if((n = recv(sock, buf, sizeof(buf), 0)) == -1) {
			display_msg(MSG_WARN, "PGP keyserver (HKP)", "recv() failed");
			res = -1;
			break;
		}

		if(n > 0) {
			fwrite(buf, n, 1, kfd);
			buf[n] = '\0';
			if(!keyfound) {
				if(strstr(buf, "BEGIN PGP")) {
					keyfound = 1;
					*pbuf = '\0';
					continue;
				} else {
					strncat(pbuf, buf, 10);
					if(strstr(pbuf, "BEGIN PGP")) {
						keyfound = 1;
						*pbuf = '\0';
						continue;
					}
					strcpy(pbuf, buf + (n > 10 ? n - 10 : n));
				}
			}
		}
	} while(n > 0);

	ConMan.del_cinfo(sock);
	fclose(kfd);

	if((res == 0) && (keyfound == 0)) {
		display_msg(MSG_WARN, "PGP keyserver (HKP)",
					"Key 0x%X was not found in public keyring(s) and on server %s",
					keyid, host);
		unlink(kfile);
		return -1;
	}

	if(res == 0) {
		if(pgp_action(kfile, EXTKEY, NULL) == -1) {
			unlink(kfile);
			return -1;
		}
		display_msg(MSG_MSG, "PGP keyserver (HKP)",
					"Fetched and extracted PGP public key 0x%X from %s",
					keyid, host);
	}

	unlink(kfile);
	return res;
}

static char *get_pgp_command_line(int mode, struct pgpargs *pargs,
								  char *inbuf, int t) {
	switch(Config.getInt("pgpversion", PGP_DEFAULT_VERS)) {
	    case 263:
	        return get_pgp263_command_line(mode, pargs, inbuf, t);
	        break;
	
	    case 500:
	        return get_pgp500_command_line(mode, pargs, inbuf, t);
	        break;
	
	    case 651: 
	        return get_pgp651_command_line(mode, pargs, inbuf, t);
	        break;
	
	    case 95:
	        return get_gpg_command_line(mode, pargs, inbuf, t);
	        break;
	
	    default:
	        display_msg(MSG_WARN, "PGP", "Unknown PGP version: %d",
			Config.getInt("pgpversion", PGP_DEFAULT_VERS)); 
	}

	return NULL;
}

static char *get_pgp500_command_line(int mode, struct pgpargs *pargs,
									 char *inbuf, int t) {
	char args[1024];
	const char pgpparm[] = "+language=en +verbose=1 +clearsig=on";
	char pubring[255] = "";
	char *pgpprg;
	struct _mail_addr *addr, *addr1;

	if(!(t & NORMAL) && (t & KEYRING))
		snprintf(pubring, sizeof(pubring), "+pubring=%s",
				 Config.get("pgpkeyring", "").c_str());
	pgpprg = strdup(Config.get("pgp", _PATH_PGP).c_str());
	if(pargs && (pargs->userid == NULL)) {
		if((mode & (SIGN | SIGNFL)) && pargs->msg
		   && pargs->msg->header->From) {
			addr = pargs->msg->header->From;
			if(addr->pgpid == NULL) {
				if((addr1 = find_addr(addr)) != NULL)
					addr = addr1;
			}
			if(addr->pgpid && *addr->pgpid && strncmp(addr->pgpid, "0x", 2)) 
			  display_msg(MSG_WARN, "PGP", "Invalid PGP Id: %s", addr->pgpid);
			else
			  pargs->userid = addr->pgpid;
		}
		if(pargs->userid == NULL)
			pargs->userid = const_cast<char *>(Config.get("pgpuser", user_n).c_str());
	}
	if((mode & ENCODE) && (mode & SIGN))
		snprintf(args, sizeof(args),
				 "%se %s +batchmode +NoBatchInvalidKeys=off -fat %s -su %s ",
				 pgpprg, pgpparm, pargs->recp, pargs->userid);
	else if(mode & ENCODE)
		snprintf(args, sizeof(args),
				 "%se %s +batchmode +NoBatchInvalidKeys=off -fat %s",
				 pgpprg, pgpparm, pargs->recp);
	else if(mode & SIGN)
		snprintf(args, sizeof(args), "%ss %s +batchmode -fatu %s ", pgpprg,
				 pgpparm, pargs->userid);

	if(mode & SIGNFL) {
		snprintf(args, sizeof(args), "%ss %s +batchmode -fatbu %s", pgpprg,
				 pgpparm, pargs->userid);
		if(pargs->recp == NULL) {
			free(pgpprg);
			return NULL;
		}
	}

	if(mode & (DECODE | VERIFY))
		snprintf(args, sizeof(args), "%sv %s +batchmode %s -f", pgpprg,
				 pgpparm, pubring);

	if(mode & EXTKEY)
		snprintf(args, sizeof(args), "%sk %s +batchmode -a %s", pgpprg,
				 pgpparm, inbuf);

	if(mode & ADDKEY)
		snprintf(args, sizeof(args), "%sk %s -xa %s", pgpprg, pgpparm,
				 pargs->recp);

	if(mode & VRFYFL)
		snprintf(args, sizeof(args), "%sv %s +batchmode %s %s -o %s",
				 pgpprg, pgpparm, pubring, pargs->recp, inbuf);

	free(pgpprg);
	return strdup(args);
}

static char *get_pgp651_command_line(int mode, struct pgpargs *pgpargs,
									 char *inbuf, int t) {
	char args[1024];
	char pubring[2048] = "";
	const char pgpparm[] = "+language=en +verbose=1 +force +clearsig=on";
	char *pgpprg;
	struct _mail_addr *addr, *addr1;

	if(!(t & NORMAL) && (t & KEYRING))
		snprintf(pubring, sizeof(pubring), "+pubring=%s",
				 Config.get("pgpkeyring", "").c_str());
	pgpprg = strdup(Config.get("pgp", _PATH_PGP).c_str());
	if(pgpargs && (pgpargs->userid == NULL)) {
		if((mode & (SIGN | SIGNFL)) && pgpargs->msg
		   && pgpargs->msg->header->From) {
			addr = pgpargs->msg->header->From;
			if(addr->pgpid == NULL) {
				if((addr1 = find_addr(addr)) != NULL)
					addr = addr1;
			}
			if(addr->pgpid && *addr->pgpid && strncmp(addr->pgpid, "0x", 2)) 
			  display_msg(MSG_WARN, "PGP", "Invalid PGP Id: %s", addr->pgpid);
			else
			  pgpargs->userid = addr->pgpid;
		}
		if(pgpargs->userid == NULL)
			pgpargs->userid = const_cast<char*>(Config.get("pgpuser", user_n).c_str());
	}
	if((mode & ENCODE) && (mode & SIGN))
		snprintf(args, sizeof(args), "%s %s +batchmode -fates %s -u %s",
				 pgpprg, pgpparm, pgpargs->recp, pgpargs->userid);

	else if(mode & ENCODE) /* seems to work with 6.5.8 without +batchmode */ 
		snprintf(args, sizeof(args), "%s %s -fate %s", pgpprg, pgpparm,
				 pgpargs->recp);

	else if(mode & SIGN) 
		snprintf(args, sizeof(args), "%s %s +batchmode -fats -u %s", pgpprg,
				 pgpparm, pgpargs->userid);

	if(mode & SIGNFL) {
		snprintf(args, sizeof(args), "%s %s +batchmode -fatbs -u %s", pgpprg,
				 pgpparm, pgpargs->userid);
		if(pgpargs->recp == NULL) {
			free(pgpprg);
			return NULL;
		}
	}

	if(mode & (DECODE | VERIFY))
		snprintf(args, sizeof(args), "%s %s +batchmode %s -f", pgpprg,
				 pgpparm, pubring);

	if(mode & EXTKEY)
		snprintf(args, sizeof(args), "%s %s +batchmode -fka", pgpprg,
				 pgpparm);

	if(mode & ADDKEY)
		snprintf(args, sizeof(args), "%s %s -f -kxa %s", pgpprg, pgpparm,
				 pgpargs->recp);

	if(mode & VRFYFL)
		snprintf(args, sizeof(args), "%s %s +batchmode %s %s %s",
				 pgpprg, pgpparm, pubring, pgpargs->recp, inbuf);

	free(pgpprg);
	return strdup(args);
}
static char *get_pgp263_command_line(int mode, struct pgpargs *pgpargs,
									 char *inbuf, int t) {
	char args[1024];
	char pubring[255] = "";
	const char pgpparm[] = "+language=en +verbose=1 +clearsig=on";
	char *pgpprg;
	struct _mail_addr *addr, *addr1;

	if(!(t & NORMAL) && (t & KEYRING))
		snprintf(pubring, sizeof(pubring), "+pubring=%s",
				 Config.get("pgpkeyring", "").c_str());
	pgpprg = strdup(Config.get("pgp", _PATH_PGP).c_str());
	if(pgpargs && (pgpargs->userid == NULL)) {
		if((mode & (SIGN | SIGNFL)) && pgpargs->msg
		   && pgpargs->msg->header->From) {
			addr = pgpargs->msg->header->From;
			if(addr->pgpid == NULL) {
				if((addr1 = find_addr(addr)) != NULL)
					addr = addr1;
			}
			if(addr->pgpid && *addr->pgpid && strncmp(addr->pgpid, "0x", 2)) 
			  display_msg(MSG_WARN, "PGP", "Invalid PGP Id: %s", addr->pgpid);
			else
				pgpargs->userid = addr->pgpid;
		}
		if(pgpargs->userid == NULL)
			pgpargs->userid = const_cast<char *>(Config.get("pgpuser", user_n).c_str());
	}
	if((mode & ENCODE) && (mode & SIGN))
		snprintf(args, sizeof(args), "%s %s +batchmode -fate %s -su %s ",
				 pgpprg, pgpparm, pgpargs->recp, pgpargs->userid);
	else if(mode & ENCODE) 
		snprintf(args, sizeof(args), "%s %s -fate %s", pgpprg, pgpparm,
				 pgpargs->recp);	 
	else if(mode & SIGN)
		snprintf(args, sizeof(args), "%s %s +batchmode -fatsu %s ", pgpprg,
				 pgpparm, pgpargs->userid);

	if(mode & SIGNFL) {
		snprintf(args, sizeof(args), "%s %s +batchmode -fatbsu %s", pgpprg,
				 pgpparm, pgpargs->userid);
		if(pgpargs->recp == NULL) {
			free(pgpprg);
			return NULL;
		}
	}

	if(mode & (DECODE | VERIFY))
		snprintf(args, sizeof(args), "%s %s +batchmode %s -f", pgpprg,
				 pgpparm, pubring);

	if(mode & EXTKEY)
		snprintf(args, sizeof(args), "%s %s +batchmode -f -ka", pgpprg,
				 pgpparm);

	if(mode & ADDKEY)
		snprintf(args, sizeof(args), "%s %s -f -kxa %s", pgpprg, pgpparm,
				 pgpargs->recp);

	if(mode & VRFYFL)
		snprintf(args, sizeof(args), "%s %s +batchmode %s %s %s",
				 pgpprg, pgpparm, pubring, pgpargs->recp, inbuf);

	free(pgpprg);
	return strdup(args);
}

static char *get_gpg_command_line(int mode, struct pgpargs *pgpargs,
								  char *inbuf, int t) {

	char args[1024];
	const char pgpparm[] =
	"-t --yes --always-trust --quiet --no-greeting --status-fd 2";
	const char gpgsuppl[]= "--passphrase-fd 0";
	/* --passphrase-fd 0 needed only when it's needed */
	char pubring[255] = "";
	char *pgpprg;
	struct _mail_addr *addr, *addr1;

	if(!(t & NORMAL) && (t & KEYRING))
		snprintf(pubring, sizeof(pubring), "--keyring=%s",
				 Config.get("pgpkeyring", "").c_str());
	pgpprg = strdup(Config.get("pgp", _PATH_PGP).c_str());
	if(pgpargs && (pgpargs->userid == NULL)) {
		if((mode & (SIGN | SIGNFL)) && pgpargs->msg
		   && pgpargs->msg->header->From) {
			addr = pgpargs->msg->header->From;
			if(addr->pgpid == NULL) {
				if((addr1 = find_addr(addr)) != NULL)
					addr = addr1;
			}
			if(addr->pgpid && *addr->pgpid && strncmp(addr->pgpid, "0x", 2)) 
			  display_msg(MSG_WARN, "PGP", "Invalid PGP Id: %s", addr->pgpid);
			else
			  pgpargs->userid = addr->pgpid;
		}
		if(pgpargs->userid == NULL)
			pgpargs->userid = const_cast<char *>(Config.get("pgpuser", user_n).c_str());
	}
	/* sign & encode needs passphrase */
	if((mode & ENCODE) && (mode & SIGN))
		snprintf(args, sizeof(args), "%s %s %s --batch -a -se %s -u %s ",
				 pgpprg, pgpparm, gpgsuppl, pgpargs->recp, pgpargs->userid);
	else if(mode & ENCODE)
		snprintf(args, sizeof(args), "%s %s --batch -a -e %s", pgpprg,
				 pgpparm, pgpargs->recp);
	else if(mode & SIGN) /* sign needs passphrase */
		snprintf(args, sizeof(args), "%s %s %s --batch --clearsign -u %s ",
				 pgpprg, pgpparm, gpgsuppl, pgpargs->userid);

	if(mode & SIGNFL) { /* signfl needs passphrase */
		snprintf(args, sizeof(args), "%s %s %s --batch -ab -u %s", pgpprg,
				 pgpparm, gpgsuppl, pgpargs->userid);
		if(pgpargs->recp == NULL) {
			free(pgpprg);
			return NULL;
		}
	}
	if(mode & DECODE) /* decode needs passphrase */
		snprintf(args, sizeof(args), "%s %s %s --batch --decrypt %s", pgpprg,
				 pgpparm, gpgsuppl, pubring);
	if(mode & VERIFY)
	snprintf(args, sizeof(args), "%s %s --batch --decrypt %s", pgpprg,
		pgpparm, pubring);
	if(mode & EXTKEY)
		snprintf(args, sizeof(args), "%s %s --batch --import %s", pgpprg,
				 pgpparm, inbuf);

	if(mode & ADDKEY)
		snprintf(args, sizeof(args), "%s %s --batch -a --export %s",
				 pgpprg, pgpparm, pgpargs->recp);

	if(mode & VRFYFL)
		snprintf(args, sizeof(args), "%s %s --batch --verify %s %s %s", pgpprg,
				 pgpparm, pubring, pgpargs->recp, inbuf);

	free(pgpprg);
	return strdup(args);

}

/*
 *      inbuf: file with text to encode/decode
 *       mode: ENCODE|SIGN or DECODE
 *       recp: if mode&(ENCODE|SIGN), the list of recipient(s)
 * passphrase: if mode==DECODE, or mode==SIGN, the pass phrase
 * see also "pgp/doc/appnote.doc"
 */

int pgp_action(char *inbuf, int mode, struct pgpargs *pgpargs) {
	char buf[1024];
	char nfile[64];
	char *args, *p, *q, *r;
	int n, move_result = 0, errfd;
	int t = NORMAL, tries = 0;
	unsigned int keyid = 0;
	struct _proc_info pinfo;
	FILE *file;
	struct stat sb;
	int pgpvers = Config.getInt("pgpversion", PGP_DEFAULT_VERS);
	strcpy(nfile, (mode & SIGNFL) ? pgpargs->recp : get_temp_file("pgp"));
	while(t) {
		n = 0;
		move_result = 1;
		init_pinfo(&pinfo);
		pinfo.handle = pgp_cleanup;
		pinfo.init = pgp_init;
		pinfo.fd_in[0] = pinfo.fd_out[0] = pinfo.fd_err[0] = 0;

		/* init pgp exit status */
		pgpext = -100;

		if(!(t & (NORMAL | KEYRING)) && (t & HTTP)) {
			if(pgp_fetch_key_from_http
			   (const_cast<char *>(Config.get("pgpkeyserver", "wwwkeys.pgp.net").c_str()),
				keyid) == -1) {    /* if we can not find signature during decode - still procede */
				if(mode & DECODE)
					break;
				unlink(nfile);
				return -1;
			}
		}

		if(!(args = get_pgp_command_line(mode, pgpargs, inbuf, t))) {
			if(mode & DECODE)
				break;
			unlink(nfile);
			return -1;
		}

		if(t & NORMAL)
			t &= ~NORMAL;
		else if(t & KEYRING)
			t &= ~KEYRING;
		else if(t & HTTP)
			t &= ~HTTP;
	/* log pgp/gpg args, just for debugging */
	display_msg(MSG_LOG, "PGP_EXEC", "args: %s", args);
		if(exec_child(args, &pinfo) == -1) {
			free(args);
			pgp_cleanup(&pinfo);
			if(pinfo.fd_in[1] > 0)
				close(pinfo.fd_in[1]);
			if(pinfo.fd_out[0] > 0)
				close(pinfo.fd_out[0]);
			if(pinfo.fd_err[0] > 0)
				close(pinfo.fd_err[0]);

			unlink(nfile);
			return -1;
		}
		free(args);
	
		/* first line of stdin must contain passphrase */
		/* endline should be written to stdin only when passphrase */
		/* is written there */
		if(pgpargs && pgpargs->passphrase && (mode & (DECODE | SIGN | SIGNFL))) {
		write(pinfo.fd_in[1], pgpargs->passphrase, strlen(pgpargs->passphrase));
		write(pinfo.fd_in[1], "\n", 1);
	}
		if((mode & EXTKEY) && (pgpvers == 500 || pgpvers == 95 || pgpvers == 651))
			errfd = pinfo.fd_err[0];
		else if(mode & VRFYFL)
			errfd = (pgpvers == 263) ? pinfo.fd_out[0] : pinfo.fd_err[0];
		else {
			if((file = fopen(inbuf, "r")) != NULL) {
				while((n = fread(buf, 1, sizeof(buf), file)) > 0)
					write(pinfo.fd_in[1], buf, n);
				close(pinfo.fd_in[1]);
				pinfo.fd_in[1] = -1;
				fclose(file);
			}

			if((file = fopen(nfile, "w")) != NULL) {
				while(1) {
					if((n =my_check_io_forms(pinfo.fd_out[0], 0, PGP_TIMEOUT)) < 0) {
						if(n == -3) {
							display_msg(MSG_WARN, "PGP Timeout", "Terminating process");
							if(pinfo.pid > 0)
								kill(pinfo.pid, SIGKILL);
						}
						break;
					}
					if((n = read(pinfo.fd_out[0], buf, sizeof(buf))) > 0)
						fwrite(buf, 1, n, file);
					else
						break;
				}
				close(pinfo.fd_out[0]);
				pinfo.fd_out[0] = -1;
				fclose(file);
			}

			errfd = pinfo.fd_err[0];
		}

	usleep(500000);
		while((n = read(errfd, buf, sizeof(buf) - 1)) > 0) {
			buf[n] = '\0'; 
			/* p should be buf for all versions except 263 */
			/* now messy part of the code starts. I don't understand */
			/* any single bit of it :-( */
			if((pgpvers == 500) || (pgpvers == 95) || (pgpvers == 651))
				p = buf;
			else {
				p = strstr(buf, "Current");
				if(p)
					p = strchr(p, '\n');
				if(p && *p)
					p++;
			}

			if(p && *p) {
				while((q = strchr(p, '\n')) != NULL) {
					*q = '\0';
					if(strlen(p))
						display_msg(MSG_LOG, "PGP", p);
					*q = '\n';
					p = q + 1;
				}
				if(*p)
					display_msg(MSG_LOG, "PGP", p);
			}

			if((p = strstr(buf, "Good signature")) ||
			   (p = strstr(buf, "BAD signature")) ||
			   (p = strstr(buf, "VALIDSIG")) ||
			   (p = strstr(buf, "BADSIG")) ||
			   (p = strstr(buf, "VALIDSIG")) ||
			   (p = strstr(buf, "WARNING:"))) {
				if(pgpvers == 95) {
					if(strstr(buf, "gpg:") != NULL)
						p = strstr(buf, "gpg:");
				}
				if(strlen(p) > 300)
					p[300] = '\0';
				if(strstr(p, "Can't find the right public key")) {
				} else if((q = strchr(p, '\n')) != NULL) {
					*q = '\0';
					q++;
					display_msg(MSG_MSG, p, q);
				} else
					display_msg(MSG_MSG,
								"PGP messsage (see Log for more details)",
								p);
			}
			if((p = strstr(buf, "ERROR:")) || (p = strstr(buf, "Error:"))
			   || (p = strstr(buf, "You do not have the secret key"))
			   || (p = strstr(buf, "No encryption keys found"))
			   || (p = strstr(buf, "decryption failed"))
			   || (p = strstr(buf, "Key matching expected"))
			   || (p = strstr(buf, "unknown keyid"))
			   || (p = strstr(buf, "ERRSIG"))
			   || (p = strstr(buf, "SIGEXPIRED"))
			   || (p = strstr(buf, "BADARMOR"))
			   || (p = strstr(buf, "BAD_PASSPHRASE"))
			   || (p = strstr(buf, "NO_PUBKEY"))
			   || (p = strstr(buf, "NO_SECKEY"))
			   || (p = strstr(buf, "CRC"))
			   || (p = strstr(buf, "secret key not available"))
			   || (p = strstr(buf, "public key not found"))
			   || (p = strstr(buf, "malformed user id"))
			   || (p = strstr(buf, "encryption failed"))
			   || (p = strstr(buf, "sign+encrypt failed"))
			   || (p = strstr(buf, "Encryption error"))) {
				/* if we decoding encoded and signed message */
				/* but no public key could be found - still show the results */
				if(!(mode & DECODE) &&
				   (!strstr(buf, "Key matching expected") ||
					!strstr(buf, "unknown keyid")))
					move_result = 0;

				/* can not find encryption key for one of the recipients */
				if((mode & ENCODE) &&
				   (strstr(buf, "No encryption keys found") ||
					strstr(buf, "NO_PUBKEY") ||
					strstr(buf, "public key not found") ||
					strstr(buf, "public key not found") ||
					strstr(buf, "encryption failed") ||
					strstr(buf, "sign+encrypt failed")))
					move_result = 0;
				if(strstr(buf, "Key matching expected") ||
				   strstr(buf, "encryption failed") ||
				   strstr(buf, "sign+encrypt failed") ||
				   strstr(buf, "unknown keyid") ||
		   strstr(buf, "NO_PUBKEY")) {
					char *keystr;
					if(!tries) {
						if(Config.getInt("pgpusekeyring", 0))
							t |= KEYRING;
						if(Config.getInt("pgpusehttp", 0))
							t |= HTTP;
					}
					if((keystr = strstr(p, "0x")) == NULL)
						keystr = (pgpvers == 263) ? p + 29 : p + 15;

					if(pgpvers!=95 && strncmp(keystr, "0x", 2)) {
						buf[0] = '\0';
						if((n = read(errfd, buf, sizeof(buf) - 1)) > 0)
							buf[n] = '\0';
						if((keystr = strstr(buf, "0x")) == NULL)
							keystr = "";
					}

					if((keyid = strtoul(keystr, NULL, 16)) == 0)
						t &= ~HTTP;
					if((tries == 0) && !(t & KEYRING) && !(t & HTTP))
						display_msg(MSG_WARN, "PGP",
									"Can not find key %s in public keyring",
									keystr);
					if(tries > 0)
						display_msg(MSG_WARN, "PGP",
									"Can not find key %s in both public and additional keyrings",
									keystr);
					p = NULL;
				}
				if(p) {
					if((q = strchr(p, '\n')) != NULL) {
						*q++ = '\0';
						if((r = strchr(q, '\n')) != NULL)
							*r = '\0';
						while((*q < 32) && (*q != '\0'))
							q++;
						while((*p < 32) && (*p != '\0'))
							p++;
						if(strlen(q) > 80)
							q[80] = '\0';
						if(strlen(p) > 80)
							p[80] = '\0';
						display_msg(MSG_WARN, p, q);
					} else {
						while((*p < 32) && (*p != '\0'))
							p++;
						if(strlen(p) > 80)
							p[80] = '\0';
						display_msg(MSG_WARN,
									"PGP Error (See Log for more details)",
									p);
					}
				}
			}
			/* forget the pass phrase */
			if((mode & (DECODE | SIGN | SIGNFL)) &&
			   (strstr(buf, "pass phrase") ||
				strstr(buf, "passphrase") ||
				strstr(buf, "BAD_PASSPHRASE")))
				pgp_timer_cb();
		}
		close(pinfo.fd_err[0]);
		if(pinfo.fd_out[0] != -1)
			close(pinfo.fd_out[0]);
		if(pinfo.fd_in[1] != -1)
			close(pinfo.fd_in[1]);

		check_extprocs();
		for(n = 0; (pgpext == -100) && (n < PGP_TIMEOUT); n++) {
			check_extprocs();
			sleep(1);
		}

		if(pgpext == -100) {   /* pgp is still running */
			display_msg(MSG_WARN, "PGP", "Stuck PGP process");
			if(pinfo.pid > 0)
				kill(pinfo.pid, SIGKILL);   /* nuke it */
			move_result = 0;
		} else /* pgp exit status was not 0 and not 1 (??) */
			if((pgpext != 0) && (pgpext != 1)) {
			display_msg(MSG_WARN, "PGP", "PGP terminated with status %d",
						pgpext);
			move_result = 0;
		}

		tries++;
	}

	if(move_result) {
		if(mode & VRFYFL || mode & VERIFY)
			return 0;

		if(mode & SIGNFL)
			return 0;

		if(mode & EXTKEY)
			return 0;

		if(stat(nfile, &sb) == -1)
			return -1;

		if(sb.st_size == 0) {
			display_msg(MSG_WARN, "PGP", "PGP produced no output");
			unlink(nfile);
			return -1;
		}

		if(do_move(nfile, inbuf) != 0) {
			unlink(nfile);
			return -1;
		}

		return 0;
	} else
		unlink(nfile);

	return -1;
}

int is_pgp(char *file) {
	FILE *pfd;
	char buf[255];

	if((pfd = fopen(file, "r")) == NULL)
		return -1;

	while(fgets(buf, sizeof(buf), pfd)) {
		strip_newline(buf);
		if(!*buf)
			continue;

		if(!strcmp(buf, PGP_ENC_START)) {
			fclose(pfd);
			return 1;
		}

		if(!strcmp(buf, PGP_SIG_START)) {
			fclose(pfd);
			return 2;
		}

		if(!strcmp(buf, PGP_KEY_START)) {
			fclose(pfd);
			return 3;
		}

		fclose(pfd);
		return 0;
	}

	fclose(pfd);
	return 0;
}

int pgp_decode_file(char *file) {
	int res;
	struct pgpargs pgpargs;

	init_pgpargs(&pgpargs);
	pgpargs.passphrase = input_passphrase();

	if((res = pgp_action(file, DECODE, &pgpargs)) != 0)
		display_msg(MSG_WARN, "reply", "Failed to decode PGP message");

	if(pgpargs.passphrase)
		free(pgpargs.passphrase);

	return res;
}

int
compare_pgpkeys(struct _pgp_keylist *entry1, struct _pgp_keylist *entry2) {
	return strcasecmp(entry1->userid, entry2->userid);
}

char *get_pgp_recp(struct _mail_msg *msg) {
	static char recp[1024];
	struct _mail_addr *ptr = NULL, *addr;
	int i, pgpvers =
	Config.getInt("pgpversion", PGP_DEFAULT_VERS);
	*recp = '\0';

	//for(i = 0; i <= 1; i++) {
	for(i = 0; i < 3; i++) {
        switch(i) {
            case 0:
			    ptr = msg->header->To;
                break;

            case 1:
			    ptr = msg->header->Cc;
                break;

            case 2:
				if(Config.getInt("pgp_encrypt_bcc", 0)) {
			        ptr = msg->header->Bcc;
                } else {
                    ptr = NULL;
                }
                break;

            default:
                ptr = NULL;
        }

		while(ptr) {
			if(!ptr->pgpid || !strlen(ptr->pgpid))
				if((addr = find_addr(ptr)))
					if((addr->pgpid) && strlen(addr->pgpid)) {
						if((strlen(addr->pgpid) + strlen(recp) + 1)
						   >= sizeof(recp))
							break;
						if(pgpvers == 500 || pgpvers == 95)
							strcat(recp, "-r ");
						strcat(recp, addr->pgpid);
						goto nextitem;
					}
			if(ptr->pgpid && strlen(ptr->pgpid)) {
				if((strlen(ptr->pgpid) + strlen(recp) + 1) >=
				   sizeof(recp)) break;
				if(pgpvers == 500 || pgpvers == 95)
					strcat(recp, "-r ");
				strcat(recp, ptr->pgpid);
			} else {
				if((strlen(ptr->addr) + strlen(recp) + 1) >= sizeof(recp))
					break;
				if(pgpvers == 500 || pgpvers == 95)
					strcat(recp, "-r ");
				strcat(recp, ptr->addr);
			}
			nextitem:
			strcat(recp, " ");
			ptr = ptr->next_addr;
		}
	}

	return *recp ? recp : NULL;
}

int pgp_encode_rfc2015(struct _mail_msg *msg, int mode) {
	struct _mime_msg *mime;
	struct _head_field *fld;
	struct pgpargs pgpargs;
	char efile[255], vfile[255], buf[255];
	FILE *efd;

	init_pgpargs(&pgpargs);
	if(msg->flags & PGP_SIGNED) {
		display_msg(MSG_WARN, "encode",
					"Message is already signed by PGP");
		return -1;
	}

	if(msg->flags & PGP_ENCRYPTED) {
		display_msg(MSG_WARN, "encode",
					"Message is already encrypted with PGP");
		return -1;
	}

	if((pgpargs.recp = get_pgp_recp(msg)) == NULL) {
		display_msg(MSG_WARN, "encode",
					"Must specify at least one recipient");
		return -1;
	}

	if(!msg->mime)
		mime_scan(msg);

	mime = msg->mime;
	while(mime) {
		if(mime->flags & MSG_BODY)
			break;
		mime = mime->mime_next;
	}

	if(!mime)
		return -1;

	strcpy(efile, get_temp_file("pgpencr"));
	if((efd = fopen(efile, "w")) == NULL) {
		display_msg(MSG_WARN, "encode", "Can not create temp. file %-.64s",
					efile);
		return -1;
	}

	if((fld = find_field(msg, MIME_C_TYPE)) != NULL)
		print_header_field(fld, efd, 0);
	else
		fprintf(efd, "%s: %s/%s\n", MIME_C_TYPE, mime->mailcap->type_text,
				mime->mailcap->subtype_text);

	if(mime->encoding->c_trans_enc != CE_7BIT) {
		if((fld = find_field(msg, MIME_C_ENCR)) != NULL)
			print_header_field(fld, efd, 0);
		else
			fprintf(efd, "%s: %s\n", MIME_C_ENCR,
					mime->encoding->encoding_name);
	}

	fputc('\n', efd);
	if(msg->print_body(msg, efd) == -1) {
		display_msg(MSG_WARN, "encode", "Failed to write message");
		fclose(efd);
		unlink(efile);
		return -1;
	}
	fclose(efd);

	if(mode & SIGN)
		pgpargs.passphrase = input_passphrase();
	pgpargs.msg = msg;
	if(pgp_action(efile, mode, &pgpargs) != 0) {
		unlink(efile);

		if(pgpargs.passphrase)
			free(pgpargs.passphrase);
		return -1;
	}
	if(pgpargs.passphrase)
		free(pgpargs.passphrase);

	strcpy(vfile, get_temp_file("pgpvers"));
	if((efd = fopen(vfile, "w")) == NULL) {
		display_msg(MSG_WARN, "encode", "Can not create temp. file %-.64s",
					vfile);
		unlink(efile);
		return -1;
	}

	fprintf(efd, "Version: 1\n");
	fclose(efd);

	discard_mime(msg->mime);
	mime = msg->mime = create_mime();
	mime->mailcap = &mailcap[PGPENC_MAILCAP];
	mime->encoding = &supp_encodings[TEXT_ENCODING];
	mime->flags |= ATTACHMENT;
	mime->flags &= ~TEXT_PART;
	mime->src_info = strdup(vfile);
	snprintf(buf, sizeof(buf), "%s/%s", mailcap[PGPENC_MAILCAP].type_text,
			 mailcap[PGPENC_MAILCAP].subtype_text);
	replace_mime_field(mime, MIME_C_TYPE, buf);

	mime->mime_next = create_mime();
	mime = mime->mime_next;
	mime->mailcap = &mailcap[BINARY_MAILCAP];
	mime->encoding = &supp_encodings[TEXT_ENCODING];
	mime->flags |= ATTACHMENT;
	mime->flags &= ~TEXT_PART;
	mime->src_info = strdup(efile);
	snprintf(buf, sizeof(buf), "%s/%s", mailcap[BINARY_MAILCAP].type_text,
			 mailcap[BINARY_MAILCAP].subtype_text);
	replace_mime_field(mime, MIME_C_TYPE, buf);

	if(update_mime(msg) == -1) {
		unlink(vfile);
		unlink(efile);
		return -1;
	}

	unlink(vfile);
	unlink(efile);
	msg->flags |= PGP_ENCRYPTED;

	return 0;
}

int pgp_sign_rfc2015(struct _mail_msg *msg) {
	struct _mime_msg *mime, *mime1, *mbody;
	struct _head_field *fld;
	char efile[255], vfile[255], buf[255];
	FILE *efd;
	struct pgpargs pgpargs;

	init_pgpargs(&pgpargs);
	if(msg->flags & PGP_SIGNED) {
		display_msg(MSG_WARN, "sign", "Message is already signed by PGP");
		return -1;
	}

	if(msg->flags & PGP_ENCRYPTED) {
		display_msg(MSG_WARN, "sign",
					"Message is already encrypted with PGP");
		return -1;
	}

	if(!msg->mime)
		mime_scan(msg);

	mbody = msg->mime;
	while(mbody) {
		if(mbody->flags & MSG_BODY)
			break;
		mbody = mbody->mime_next;
	}

	if(!mbody)
		return -1;

	strcpy(efile, get_temp_file("pgpsign"));
	if((efd = fopen(efile, "w")) == NULL) {
		display_msg(MSG_WARN, "sign", "Can not create temp. file %-.64s",
					efile);
		return -1;
	}

	if((fld = find_field(msg, MIME_C_TYPE)) != NULL)
		print_header_field(fld, efd, 0);
	else
		fprintf(efd, "%s: %s/%s\n", MIME_C_TYPE, mbody->mailcap->type_text,
				mbody->mailcap->subtype_text);

	if(mbody->encoding->c_trans_enc !=
	   supp_encodings[DEFAULT_ENCODING].c_trans_enc) {
		if((fld = find_field(msg, MIME_C_ENCR)) != NULL)
			print_header_field(fld, efd, 0);
		else
			fprintf(efd, "%s: %s\n", MIME_C_ENCR,
					mbody->encoding->encoding_name);
	}

	fputc('\n', efd);
	if(msg->print_body(msg, efd) == -1) {
		display_msg(MSG_WARN, "sign", "Failed to write message");
		fclose(efd);
		unlink(efile);
		return -1;
	}
	fclose(efd);

	strcpy(vfile, get_temp_file("pgps"));
	pgpargs.passphrase = input_passphrase();
	pgpargs.recp = vfile;
	pgpargs.msg = msg;
	if(pgp_action(efile, SIGNFL, &pgpargs) != 0) {
		unlink(efile);
		unlink(vfile);
		if(pgpargs.passphrase)
			free(pgpargs.passphrase);
		return -1;
	}
	if(pgpargs.passphrase)
		free(pgpargs.passphrase);

	if((efd = fopen(efile, "w")) == NULL) {
		display_msg(MSG_WARN, "sign", "Can not create temp. file %-.64s",
					efile);
		unlink(efile);
		unlink(vfile);
		return -1;
	}

	if(msg->print_body(msg, efd) == -1) {
		display_msg(MSG_WARN, "sign", "Failed to write message");
		fclose(efd);
		unlink(efile);
		unlink(vfile);
		return -1;
	}
	fclose(efd);

	mime = create_mime();
	mime->mime_next = NULL;
	mime->mailcap = &mailcap[PGPSIG_MAILCAP];
	mime->encoding = &supp_encodings[NO_ENCODING];
	mime->flags |= ATTACHMENT;
	mime->flags &= ~TEXT_PART;
	mime->src_info = strdup(vfile);
	snprintf(buf, sizeof(buf), "%s/%s", mailcap[PGPSIG_MAILCAP].type_text,
			 mailcap[PGPSIG_MAILCAP].subtype_text);
	replace_mime_field(mime, MIME_C_TYPE, buf);

	mime1 = create_mime();
	mime1->mime_next = mime;
	mime1->mailcap = find_mailcap(mbody->mailcap->type_text,
								  mbody->mailcap->subtype_text, 1);
	mime1->encoding = &supp_encodings[NO_ENCODING];
	mime1->flags |= ATTACHMENT;
	mime1->flags &= ~TEXT_PART;
	mime1->src_info = strdup(efile);
	if((fld = find_field(msg, MIME_C_TYPE)) != NULL)
		replace_mime_field(mime1, MIME_C_TYPE, fld->f_line);
	else {
		snprintf(buf, sizeof(buf), "%s: %s/%s", MIME_C_TYPE,
				 mbody->mailcap->type_text, mbody->mailcap->subtype_text);
		replace_mime_field(mime1, MIME_C_TYPE, buf);
	}

	if(mbody->encoding->c_trans_enc !=
	   supp_encodings[DEFAULT_ENCODING].c_trans_enc) {
		if((fld = find_field(msg, MIME_C_ENCR)) != NULL)
			replace_mime_field(mime1, MIME_C_ENCR, fld->f_line);
		else {
			snprintf(buf, sizeof(buf), "%s: %s\n", MIME_C_ENCR,
					 mbody->encoding->encoding_name);
			replace_mime_field(mime1, MIME_C_ENCR, buf);
		}
	}

	discard_mime(msg->mime);
	msg->mime = mime1;

	if(update_mime(msg) == -1) {
		unlink(vfile);
		unlink(efile);
		return -1;
	}

	unlink(vfile);
	unlink(efile);
	msg->flags |= PGP_SIGNED;

	return 0;
}
