
/***************************************************************************
 *   Copyright (C) 1999 to 2004 by Jonathan Duddington                     *
 *   email: jonsd@users.sourceforge.net                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write see:                           *
 *               <http://www.gnu.org/licenses/>.                           *
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "win.h"
#include "wimp.h"
#include "dbox.h"
#include "werr.h"
#include "bbc.h"
#include "menu.h"

#include "narc.h"
#include "hdrs.h"



extern char *get_user_id(OPTIONS_MAILBOX *user);
extern char *reply_extract_email_addr(char *buf, int control);

int pgp_report(int control);

extern char *path_choices;
extern OPTIONS options;
extern int lists_line_height;
extern int standard_font;
extern BLIST pgplist;
extern char sig_pgpkey[];

extern menu blist_current_menu;
extern int blist_current_menu_line;
extern int blist_current_menu_blist;



typedef struct {
	char *name;
	short n_uid;
	char photo;
} PGP_KEYRING_INDEX;

static char *pgp_keyring=NULL;
static PGP_KEYRING_INDEX *pgp_keyring_index = NULL;

static FILE *save_f;
static int pgp_selected = -1;
static int  pgp_aliases_length = 0;
static char *pgp_aliases = NULL;

static char *pgp_stdout = "<Pluto$tmp>.pgp_st";
static char *pgp_list = "<pluto$tmp>.pgplist";
//static char *pgp_decode_log = "<pluto$tmp>.pgp_decode";

extern char tmp_path[];

static char fname_temp[40];
static char pgp_username[128];
static char sig_data[140];

static int pgp_type = 0;  /* 1=pgp, 2=gpg */
static char *pgp_name = "";
static int pgp_yes = 0;
static int hold_password_flag=0;
static int hold_password_ok = 0;
static char password[64] = {0};
static char hold_password_user[60] = {0};   /* index by user number */
static char password_zeros[64]="                                ";
static char keyid[9];

/* calling GPG */
const char *gpgcall = "gpg --status-file <Pluto$tmp>.pgp_st --no-tty --batch --yes";
const char *gpgpwd  = "gpg --passphrase-file Pipe:$.gpg_pwd --status-file <Pluto$tmp>.pgp_st --no-tty --batch --yes";



#define GPG_GOODSIG    1
#define GPG_BADSIG     2
#define GPG_ERRSIG     3
#define GPG_VALIDSIG   4
#define GPG_SIG_ID     5
#define GPG_ENC_TO     6
#define GPG_NODATA     7
#define GPG_UNEXPECTED 8
#define GPG_USERID_HINT 9

#define GPG_NEED_PASSPHRASE 10
#define GPG_NEED_PASSPHRASE_SYM 11
#define GPG_BAD_PASSPHRASE  12
#define GPG_GOOD_PASSPHRASE 13
#define GPG_MISSING_PASSPHRASE 14
#define GPG_DECRYPTION_FAILED  15
#define GPG_IMPORT_RES      16
#define GPG_DELETE_PROBLEM  17
#define GPG_INV_RECP        18
#define GPG_NO_PUBKEY       19
#define GPG_KEY_REVOKED     20
#define GPG_NO_SECKEY       21
#define GPG_NO_RECP         22


typedef struct {
	char *mnem;
	char value;
} GPG_TAB;

static GPG_TAB gpg_tab[] = {
	"GOODSIG", GPG_GOODSIG,
	"BADSIG",  GPG_BADSIG,
	"ERRSIG",  GPG_ERRSIG,
	"VALIDSIG",GPG_VALIDSIG,
	"SIG_ID",  GPG_SIG_ID,
	"ENC_TO",  GPG_ENC_TO,
	"NODATA",  GPG_NODATA,
	"UNEXPECTED", GPG_UNEXPECTED,
	"USERID_HINT", GPG_USERID_HINT,

	"NEED_PASSPHRASE", GPG_NEED_PASSPHRASE,
	"NEED_PASSPHRASE_SYM", GPG_NEED_PASSPHRASE_SYM,
	"BAD_PASSPHRASE",  GPG_BAD_PASSPHRASE,
	"GOOD_PASSPHRASE", GPG_GOOD_PASSPHRASE,
	"MISSING_PASSPHRASE", GPG_MISSING_PASSPHRASE,
	"DECRYPTION_FAILED", GPG_DECRYPTION_FAILED,
	"IMPORT_RES",     GPG_IMPORT_RES,
	"DELETE_PROBLEM", GPG_DELETE_PROBLEM,
	"NO_PUBKEY",      GPG_NO_PUBKEY,
	"KEY_REVOKED",    GPG_KEY_REVOKED,
	"NO_SECKEY",      GPG_NO_SECKEY,

	"INV_RECP",       GPG_INV_RECP,
	"NO_RECP",        GPG_NO_RECP,
	NULL,0,
};



/* bits 0-3
   bits 4-7  0 no action, 1 don't continue
*/

static MNEM_TAB pgp_tab[] = {
	"key for user id: ",0x03,
	"enter pass phrase", 0x14,
	"warning:  because", 0x00,  /* ignore */
	"warning",      0x11,
	"error",        0x11,
	"cannot find",  0x05,
	"good ",        0x11,
	"signature er", 0x11,
	"no passph",    0x16,
	"you do not",   0x11,
	"file write",   0x11,   /* file write error */
	"keyfile contains", 0x02,
	"no new keys",  0x11,
	"a user id is r", 0x11,
	NULL, 0
};



int pgp_present()
/***************/
/* 1=pgp  2=gpg  0=neither */
{
	char buf[4];

	pgp_type = 0;

	if(read_os_variable("GnuPG$Path",buf,sizeof(buf))!=1)
	{
		/* GPG is present */

		if(read_os_variable("GnuPGUser$Path",buf,sizeof(buf))==1)
		{
			werr(0,"GnuPG User Path is not set");
			return(0);
		}
		pgp_type = 2;
		pgp_name = "GnuPG";
		return(2);
	}

	if(read_os_variable("PGPPATH",buf,sizeof(buf))==1)
	{
		werr(0,"PGP is not present");
		return(0);
	}
	pgp_type = 1;
	pgp_name = "PGPv2";
	return(1);

}   /* end of gpg_not_present */



void pgp_clear_password()
/***********************/
{
	memset(password,0,sizeof(password));
	hold_password_ok = 0;
	hold_password_flag = 0;
}



FILE *pgp_open_list(void)
/***********************/
{
	return(fopen(pgp_list,"w"));
}   /* end of pgp_open_list */



void pgp_view_report()
/********************/
{
	set_filetype(pgp_stdout,0xfff);
	dataopen(pgp_stdout,0xfff);
}   /* end of pgp_view_report */



void pgp_load_aliases()
/*********************/
{
	int  i;
	FILE *f;
	int  length;
	char fname[128];

	sprintf(fname,"%s.PGP_Alias",path_choices);

	if(pgp_aliases != NULL)
	{
		free(pgp_aliases);
		pgp_aliases_length = 0;
	}

	if((f = fopen(fname,"r")) == NULL)
	{
		return;
	}

	length = get_filelength(fname);
	if((pgp_aliases = malloc(length+1)) == NULL)
	{
		malloc_err(45);
		fclose(f);
		return;
	}

	fread(pgp_aliases,1,length,f);
	fclose(f);

	pgp_aliases_length = length;
	pgp_aliases[length] = 0;

	for(i=0; i<length; i++)
	{
		if(pgp_aliases[i] == '\n')
			pgp_aliases[i] = 0;
	}
}   /* end of pgp_load_aliases */



char *pgp_lookup_alias(char *addr)
/********************************/
{
	int  len;
	char *p;
	char *p_end;

	if(pgp_aliases == NULL)
		pgp_load_aliases();

	len = strlen(addr);
	p = pgp_aliases;
	p_end = &pgp_aliases[pgp_aliases_length];

	while(p < p_end)
	{
		if(memcmp_lc2(addr,p,len)==0)
		{
			/* matching address, get the alias */
			while((*p != 0) && !isspace(*p)) p++;
			while(isspace(*p)) p++;
			return(p);
		}
		p = strchr(p,0);
		p++;
	}
	return(NULL);
}   /* end of pgp_lookup_alias */



void pgp_command(char *command, int flags)
/****************************************/
/* flags:  bit 0   include password
           bit 1   include list of users
           bit 2   run in taskwindow
           bit 3   suppress screen output
*/
{
	int  i;
	FILE *f;
	char *p;
	char *p2;
	char buf[1024];
	char user[128];

	if(pgp_type==1)
		flags = flags & ~0xc;  /* taskwindow not for PGP */

	p2 = buf;
	if(flags & 4)
	{
		strcpy(buf,"TaskWindow \"");
		p2 += strlen(buf);
	}

	if((flags & 2) && ((p = strstr(command,"-r "))!=NULL) &&
			((f = fopen_werr(pgp_list,"r",NULL)) != NULL))
	{
		/* replace -r by list of recipients */
		memcpy(p2,command,p-command);

		p2 += (p-command);

		while(!feof(f))
		{
			if(fgets(user,sizeof(user),f) != NULL)
			{
				i = strlen(user);
				user[i-1]=0;      /* remove \n at end of line */
				sprintf(p2,"-r %s ",user);
				p2 += (strlen(user)+4);
			}
		}
		p += 3;
		strcat(p2,p);

		fclose(f);
	}
	else
	{
		strcpy(p2,command);
	}

	if(flags & 4)
		strcat(buf,"\"");

	if(flags & 8)
		strcat(buf," { > <pluto$tmp>.pgp_out }");

#ifdef deleted
	f=fopen("<pluto$tmp>.pgp_cmd","a");
	if(f!=NULL)
	{
		fprintf(f,"%s\n\n",buf);
		fclose(f);
	}
#endif

	for(i=0; i<3; i++)
		os_swi3(0x06,138,0,'\n');  /* OS_Byte, insert character into input buffer */

	if(flags & 1)
	{
		f = fopen_werr("Pipe:$.gpg_pwd","w",NULL);
		fprintf(f, "%s",password);
		fclose(f);
	}

	wimp_starttask(buf);
	os_swi2(0x06,15,1);    /* OS_Byte, flush input buffer */

	memset(buf,0,sizeof(buf));
}   /* end of pgp_command */



void pgp_task_command(char *command)
/**********************************/
{
	FILE *f;
	char fname[128];
	char buf[128];

	sprintf(fname,"%s.pgp4",tmp_path);
	f = fopen(fname,"w");
	if(f==NULL)
		return;

	if(pgp_type==1)
		fprintf(f,"pgp +verbose=0 %s\n",command);
	else
		fprintf(f,"gpg --status-file <Pluto$tmp>.pgp_st %s\n",command);

	fclose(f);
	set_filetype(fname,0xfd7);   /* taskobey */

	sprintf(buf,"filer_run %s",fname);
	wimp_starttask(buf);
}   /* end of pgp_task_command */




int pgplist_sorter(const void *p1, const void *p2)
/*************************************************/
{
	PGP_KEYRING_INDEX *a1;
	PGP_KEYRING_INDEX *a2;

	a1 = (PGP_KEYRING_INDEX *)p1;
	a2 = (PGP_KEYRING_INDEX *)p2;

	return(strcmp(&a1->name[0],&a2->name[0]));
}   /* end of pgplist_sorter */



void pgp_fade_fields()
/********************/
{
	int  i;
	int  fade_flag=0;

	if(pgp_selected == -1)
		fade_flag = wimp_INOSELECT;

	for(i=1; i<5; i++)
	{
		/* fade these icons */
		wimp_set_icon_state(pgplist.w_buttons,i,fade_flag,wimp_INOSELECT);
	}
}   /* end of pgp_fade_fields */




void pgp_list_keys(int control)
/*****************************/
/* control: 1=bring to front */
{
	int  i;
	int  colons;
	int  count;
	FILE *f;
	int  length;
	char fname[80];
	char buf[80];

	sprintf(fname,"%s.pgp3",tmp_path);

	pgp_present();
	if(pgp_type==1)
		sprintf(buf,"pgp -kv -t 2> null: { > %s }",fname);
	else
		sprintf(buf,"gpg --list-keys --no-show-photos --with-colons { > %s }",fname);
	pgp_command(buf,0);

	/* load list into memory */
	length = get_filelength(fname);

	if(pgp_keyring != NULL)
		free(pgp_keyring);
	if(pgp_keyring_index != NULL)
		free(pgp_keyring_index);

	pgp_keyring = malloc(length);
	if(pgp_keyring == NULL)
	{
		malloc_err_string(46,"for PGP keyring");
		return;
	}

	f = fopen(fname,"r");
	if(f == NULL)
	{
		free(pgp_keyring);
		pgp_keyring = NULL;
		return;
	}

	fread(pgp_keyring,1,length,f);
	fclose(f);
	remove(fname);

	count = 0;
	for(i=0; i<length; i++)
	{
		if(pgp_keyring[i] == '\r')
		{
			pgp_keyring[i] = 0;
			count++;
		}
	}

	/* allocate table of pointers */
	pgp_keyring_index = calloc(count,sizeof(PGP_KEYRING_INDEX));
	if(pgp_keyring_index == NULL)
		return;

	count = -1;
	for(i=0; i<(length-5); i++)
	{
		if(pgp_keyring[i] =='\n')
		{
			while(pgp_keyring[++i] == '\n');

			if(pgp_type==1)
			{
				if(memcmp(&pgp_keyring[i],"pub ",4)==0)
				{
					count++;
					pgp_keyring_index[count].name = &pgp_keyring[i+30];
				}
				else if((count >= 0) && (memcmp(&pgp_keyring[i],"uid ",4)==0))
				{
					pgp_keyring_index[count].n_uid++;
				}
			}
			else
			{
				/* GnuPG */

				if(memcmp(&pgp_keyring[i],"pub:",4)==0)
				{
					count++;

					/* skip 8 more colons to get to the name */
					colons=0;
					while(colons < 9)
					{
						if(pgp_keyring[i++]==':')
							colons++;
					}

					pgp_keyring_index[count].name = &pgp_keyring[i];
					while(pgp_keyring[i] != ':') i++;
					pgp_keyring[i]=0;  /* end of name */
				}
				else if((count >= 0) && (memcmp(&pgp_keyring[i],"uid:",4)==0))
				{
					pgp_keyring_index[count].n_uid++;
				}
				else if(memcmp(&pgp_keyring[i],"uat:",4)==0)
					pgp_keyring_index[count].photo = 1;
			}
		}
	}
	count++;
	pgplist.n_entries = count;
	qsort(pgp_keyring_index,count,sizeof(PGP_KEYRING_INDEX),pgplist_sorter);
	pgp_fade_fields();

	sprintf(buf,"%s Keyring",pgp_name);
	win_settitle(pgplist.w_buttons,buf);
	if(control==1)
		blist_set_extent(&pgplist,3);
	else
		blist_set_extent(&pgplist,7);
}   /* end of pgp_list_keys */





void pgp_import(char *filename, int filetype)
/***************************************/
{
	char buf[256];

	if(pgp_type==1)
	{
		sprintf(buf,"-ka %s",filename);
		pgp_task_command(buf);
	}
	else
	{
		sprintf(buf,"%s --import %s",gpgcall,filename);
		pgp_command(buf,0);
		pgp_list_keys(0);
	}
}   /* end of pgp_import */





void pgp_click_buttons2(BLIST *b, wimp_eventdata *d)
/**************************************************/
{
	int  icon;
	int  click;
	char *name;
	int  reload = 0;
	char buf[200];
	char fname_temp[80];

	icon = d->but.m.i;
	click = d->but.m.bbits;

	if(pgp_selected >= 0)
		name = &pgp_keyring_index[pgp_selected].name[0];
	else
		name = "";

	buf[0] = 0;

	switch(icon)
	{
	case 4:   /* view */
		sprintf(buf,"-kvcv \"%s\"",name);
		break;

	case 1:    /* delete */
		sprintf(buf,"-kr \"%s\"",name);
		reload = 1;
		break;

	case 2:    /* extract */
		sprintf(fname_temp,"%s.pgp2/asc",tmp_path);
		remove(fname_temp);
		sprintf(buf,"pgp +batchmode -kxat \"%s\" %s 2> null: { > %s }",name,fname_temp,pgp_stdout);
		pgp_command(buf,0);
		buf[0]=0;
		dataopen(fname_temp,0xfff);
		break;

	case 3:    /* edit */
		sprintf(buf,"-ke \"%s\"",name);
		break;

	case 7:   /* list */
		sprintf(buf,"-kvv");
		break;

	case 5:    /* create */
		sprintf(buf,"-kg");
		break;

	case 6:   /* PGP alias list */
		sprintf(buf,"%s.PGP_Alias",path_choices);
		file_ensure_exists(buf);
		OLE_start(buf,0xfff,6,NULL,0,NULL);
		buf[0]=0;
		break;

	}
	if(buf[0] != 0)
	{
		pgp_task_command(buf);
	}
	if(reload)
	{
		/*      pgp_list_keys(); */
	}
}   /* end of pgp_click_buttons */




void gpg_click_buttons(BLIST *b, wimp_eventdata *d)
/*************************************************/
{
	int  icon;
	int  click;
	char *name;
	int  reload = 0;
	char buf[200];
	char fname_temp[80];

	icon = d->but.m.i;
	click = d->but.m.bbits;

	if(pgp_selected >= 0)
		name = &pgp_keyring_index[pgp_selected].name[0];
	else
		name = "";

	buf[0] = 0;

	switch(icon)
	{
	case 4:   /* view */
		if(dbox_persist())
			sprintf(buf,"--check-sigs \"%s\"",name);
		else
			sprintf(buf,"--list-sigs \"%s\"",name);
		pgp_task_command(buf);
		break;

	case 1:    /* delete */
		sprintf(buf,"Delete GnuGP key?: %s",name);
		if(query(buf,NULL)==0)
			return;
		sprintf(buf,"%s --delete-key \"%s\"",gpgcall,name);
		pgp_command(buf,0);
		if(pgp_report(0)==0x102)
		{
			call_event_process(10);
			beep();
			sprintf(buf,"Secret key \"%s\". Delete it?",name);
			if(query(buf,"DELETE")==0)
				return;

			sprintf(buf,"--delete-secret-key \"%s\"",name);
			pgp_task_command(buf);
		}
		reload = 1;
		break;

	case 2:    /* extract */
		sprintf(fname_temp,"%s.pgp2/asc",tmp_path);
		remove(fname_temp);
		sprintf(buf,"gpg --batch --export \"%s\" { > %s }",name,fname_temp);
		pgp_command(buf,0);
		dataopen(fname_temp,0xfff);
		break;

	case 3:    /* edit */
		sprintf(buf,"--no-greeting --edit-key \"%s\"",name);
		pgp_task_command(buf);
		break;

	case 7:   /* list */
		sprintf(buf,"--list-keys --no-show-photos");
		pgp_task_command(buf);
		break;

	case 5:    /* create */
		sprintf(buf,"--gen-key");
		pgp_task_command(buf);
		break;

	case 6:   /* PGP alias list */
		sprintf(buf,"%s.PGP_Alias",path_choices);
		file_ensure_exists(buf);
		OLE_start(buf,0xfff,6,NULL,0,NULL);
		break;
	}

	if(reload)
	{
		pgp_list_keys(0);
	}
}   /* end of gpg_click_buttons */




void pgp_click_buttons(BLIST *b, wimp_eventdata *d)
/********************************************/
{
	pgp_present();

	if(pgp_type==1)
		pgp_click_buttons2(b,d);
	else
		gpg_click_buttons(b,d);
}   /* end of pgp_click_buttons */


void pgp_click_window(BLIST *b,int linenum,int bbits)
/**********************************************/
{
	char *name;
	char buf[200];

	name = &pgp_keyring_index[linenum].name[0];

	switch(bbits)
	{
	case wimp_BCLICKLEFT:
		pgp_selected = linenum;
		pgp_fade_fields();
		break;

	case wimp_BCLICKRIGHT:
		if(linenum == pgp_selected)
			pgp_selected = -1;
		else
			pgp_selected = linenum;
		pgp_fade_fields();
		break;

	case wimp_BLEFT:
		pgp_selected = -1;
		if(pgp_type == 2)
			sprintf(buf,"--list-sigs \"%s\"",name);
		else
			sprintf(buf,"-kvcv \"%s\"",name);
		pgp_task_command(buf);
		break;

	case wimp_BRIGHT:        /* adjust, 2nd click */
		if(pgp_type == 2)
		{
			sprintf(buf,"--check-sigs \"%s\"",name);
			pgp_task_command(buf);
		}
		break;
	}

	pgp_fade_fields();
	redraw_blist(b);
}   /* end of pgp_click_window */




void pgp_key_window(BLIST *b,wimp_eventdata *d)
/****************************************/
{
	int  c;

	switch(c = d->key.chcode)
	{
	default:
		wimp_processkey(c);
		break;
	}
}   /* end of pgp_key_window */





void pgp_display_window(BLIST *b,wimp_redrawstr *r,int line,int end_line,int x,int y)
/***************************************************************************/
{
	os_regset regs;
	char *name;
	char n_uid[16];

	for(line=line; line<end_line; line++)
	{
		if(line >= b->n_entries)
			break;

		name = pgp_keyring_index[line].name;

		if(line==pgp_selected)
			list_set_colour(0, 0,x,y);
		else
			list_set_colour(0, 1,x,y);

		sprintf(n_uid,"%d",pgp_keyring_index[line].n_uid+1);

		if(standard_font >= 0)
		{
			regs.r[0] = standard_font;
			regs.r[2] = 0x190;    /* bit 4, bit 7, bit 8 */
			regs.r[4] = y;

			if(pgp_keyring_index[line].photo)
				paint_string("*",x+8,y,20);

			paint_string(n_uid,x+28,y,30);

			paint_string(name,x+58,y,800);
		}
		else
		{
			if(pgp_keyring_index[line].photo)
			{
				bbc_move(x+8, y-4);
				printf("*");
			}
			bbc_move(x+28, y-4);
			printf(n_uid);
			bbc_move(x+58, y-4);
			printf(name);
		}

		y -= lists_line_height;
	}
}   /* pgp_display_window */




int gpg_report(int control)
/*************************/
{
	FILE *f;
	int  result=0;
	int  action=0;
	int  keyword;
	int  reason=0;
	int  sig_error;
	int  redraw_list = 0;
	GPG_TAB *m;
	char *bp;
	int  count_enc_to = 0;
	int  count_no_seckey = 0;
	char buf[150];

	sig_data[0] = 0;

	f = fopen(pgp_stdout,"r");
	if(f==NULL)
		return(0);


	while(!feof(f) && (result==0))
	{
		if(fgets(buf,sizeof(buf),f) == NULL)
			break;

		if(memcmp(buf,"[GNUPG:] ",9)!=0)
			continue;

		/* delete terminating NL */
		buf[strlen(buf)-1] = 0;

		bp = &buf[9];

		for(m = gpg_tab; m->mnem != NULL; m++)
		{
			if(memcmp(bp,m->mnem,strlen(m->mnem))==0)
			{
				action = m->value;
				keyword = m->value;

				result = 0;
				sig_error = 0;

				switch(keyword)
				{
				case GPG_NEED_PASSPHRASE:
					break;

				case GPG_BAD_PASSPHRASE:
					result=4;
					break;

				case GPG_MISSING_PASSPHRASE:
					result=4;
					break;

				case GPG_USERID_HINT:
					/*    strcpy(pgp_username,reply_extract_email_addr(&bp[29],0)); */
					strcpy(pgp_username,&bp[29]);
					break;

				case GPG_GOODSIG:
					strncpy0(&sig_data[19],&bp[25],sizeof(sig_data)-19);
					memcpy(keyid,&bp[16],8);
					keyid[8]=0;
					break;

				case GPG_VALIDSIG:
					sprintf(sig_data,"SIG OK  ");
					memcpy(&sig_data[8],&bp[50],11);   /* sig. date */
					break;

				case GPG_BADSIG:
					sprintf(sig_data,"! BAD SIG ");
					strncpy0(&sig_data[10],&bp[24],sizeof(sig_data)-10);
					sig_error = GPG_BADSIG;
					break;

				case GPG_ERRSIG:
					sprintf(sig_data,"! SIG ERR ");
					strncpy0(&sig_data[10],&bp[24],sizeof(sig_data)-10);
					sig_error = GPG_ERRSIG;
					break;

				case GPG_NO_PUBKEY:
					sprintf(sig_data,"Public key unavailable ");
					sig_error = GPG_NO_PUBKEY;
					break;

				case GPG_ENC_TO:
					count_enc_to++;
					break;

				case GPG_NO_SECKEY:
					count_no_seckey++;
					break;

				case GPG_KEY_REVOKED:
					sprintf(sig_data,"Key revoked ");
					sig_error = GPG_KEY_REVOKED;
					break;

				case GPG_NODATA:
					reason = bp[7];

					switch(reason)
					{
					case '1':
						break;  /* no armoured data, ignore */

					case '2':
						werr(0,"GnuPG: No data, expected a packet");
						break;

					case '3':
						werr(0,"GnuPG: Bad data found");
						break;
					}
					result =1;
					break;

				case GPG_DECRYPTION_FAILED:
					if(count_no_seckey >= count_enc_to)
						werr(0,"Decryption failed, secret key unavailable");
					else
						werr(0,"GnuPG: %s",m->mnem);
					result = 1;
					break;

				case GPG_IMPORT_RES:
					redraw_list = 1;
					break;

				case GPG_DELETE_PROBLEM:
					reason = bp[15];
					if(reason=='2')
						result=0x102;   /* must delete secret key first */
					break;

				case GPG_INV_RECP:
					reason = bp[9];

					if(reason=='0')
						werr(0,"GnuPG: Public key not known for '%s'",&bp[11]);
					else
						werr(0,"GnuPG: Can't encrypt to '%s'. Reason: %c",&bp[11],reason);
					result = 5;
					break;

				case GPG_NO_RECP:
					werr(0,"No recipients");
					result = 5;
					break;
				}
			}
		}
	}
	fclose(f);

	if(sig_error)
	{
		werr(0,sig_data);
	}

	if((redraw_list) && (pgplist.open))
	{
		pgp_list_keys(0);
	}

	return(result);
}   /* end of gpg_report */



int pgp_report(int control)
/*************************/
{
	FILE *f;
	MNEM_TAB *m;
	int  type=0;
	int  action=0;
	int  result=0;
	char *bp;
	char buf[80];
	char buf2[80];

	pgp_username[0] = 0;

	if(pgp_type == 2)
		return(gpg_report(control));

	f = fopen(pgp_stdout,"r");
	if(f==NULL)
		return(0);


	while(!feof(f))
	{
		if(fgets(buf,sizeof(buf),f) == NULL)
			break;

		bp = buf;
		if(bp[0]==0x7)
			bp++;  /* ignore CTRL-G at start of line */

		for(m = pgp_tab; m->mnem != NULL; m++)
		{
			if(memcmp_lc(bp,m->mnem,strlen(m->mnem))==0)
			{
				action = m->value;
				type = m->value & 0xf;

				switch(type)
				{
				case 1:
					werr(0,bp);
					break;

				case 2:
					if(fgets(buf2,sizeof(buf2),f)!=NULL)
					{
						bp[strlen(bp)-2]=0;   /* remove CR LF */
						werr(0,"%s %s",bp,buf2);
					}
					break;

				case 3:
					/* remember user name */
					strncpy(pgp_username,&bp[17],sizeof(pgp_username));
					break;

				case 4:
					/* enter pass phrase */
					if(control == 1)
						action = 0;   /* continue, we've already entered password, it must be wrong */
					break;

				case 5:  /* cannot find the public key */
					werr(0,bp);   /* report but continue looking */
					break;

				case 6:  /* bad pass-phrase */
					werr(0,"Bad passphrase; can't access secret key");
					break;
				}

				if(result==0)
					result = type;

				if(action & 0x10)
				{
					fclose(f);
					return(type);
				}
				break;   /* only consider the first match in the list of strings */
			}
		}
	}
	fclose(f);
	return(result);
}   /* end of pgp_report */




char *pgp_password(char *user, int reason)
/****************************************/
{
	dbox d;
	char buf[200];

	static char *reason_str[] = {
		"make signature",
		"decrypt"
	};

	if(hold_password_ok && (strcmp(hold_password_user,user)==0))
	{
		return(password);   /* remember previous password */
	}

	pgp_clear_password();

	d = dbox_new("Boxp");

	sprintf(buf,"%s Password: %s",pgp_name,reason_str[reason]);
	win_settitle(dbox_syshandle(d),buf);

	sprintf(buf,"Enter Pass phrase for:  %s",user);

	dbox_setfield(d,2,buf);
	dbox_unfadefield(d,3);
	dbox_show(d);
	if(dbox_fillin(d) == 0)
	{
		dbox_getfield(d,1,password,sizeof(password));
		dbox_setfield(d,1,password_zeros);
		hold_password_flag = dbox_getnumeric(d,3);
		dbox_dispose(&d);
		strcpy(hold_password_user,user);
		return(password);
	}
	dbox_dispose(&d);
	return(NULL);
}   /* end of pgp_password */




int pgp_start_text(FILE **f_out, int action)
/******************************************/
/* Divert text to temp file so we can PGP it */
{
	FILE *f;

	action = action  & (~action >> 4);
	action &= 3;

	pgp_yes = 0;

	if((action==0) || (pgp_present()==0))
		return(0);

	if((action & 2) && (query2("Encrypt message ?","YES","NO")==0))
	{
		return(0);
	}

	save_f = *f_out;

	check_ramfs();
	sprintf(fname_temp,"%s.pgp1",tmp_path);

	f = fopen_werr(fname_temp,"w",NULL);
	if(f==NULL)
	{
		return(0);
	}

	pgp_yes = 1;    /* allow pgp_text_end to operate */

	*f_out = f;
	return(1);
}   /* end of pgp_start_text */



int pgp_end_text(FILE **f_out, int action, OPTIONS_MAILBOX *user)
/***************************************************************/
/* Action  bit 0  sign message
           bit 1  encrypt message
           bit 4  inhibit sign
           bit 5  inhibit encrypt
*/
{
	FILE *f;
	char *user_name;
	char *passwd;
	int  result;
	int  length;
	int  i;
	char *p_alias;
	char *fname2_ptr;
	char fname2[40];
	char fname_log[40];
	char buf[300];

	if(pgp_yes == 0)
		return(0);

	fclose(*f_out);

	if(action == -1)
	{
		*f_out = save_f;
		return(1);
	}

	action = action  & (~action >> 4);
	action &= 3;

	if((action==0) || (pgp_present()==0))
		return(0);


	sprintf(fname2,"%s.pgp2",tmp_path);
	sprintf(fname_log,"%spgp_log",pluto_path);
	remove(fname2);  /* just in case there's anything left over from last time */
	remove(fname_log);



	fname2_ptr = fname2;

	/* perform PGP on the temporary file */
	if(pgp_type==1)
		user_name = user->full_name;
	else
	{
		user_name = user->email_addr;
		if((p_alias = pgp_lookup_alias(user_name)) != NULL)
			user_name = p_alias;
	}
	if(sig_pgpkey[0] != 0)
		user_name = sig_pgpkey;

	if(action & 1)
	{
		/* sign message, we need our password */
		passwd = pgp_password(user_name,0);
		if(passwd == NULL)
		{
			*f_out =save_f;
			remove(fname_temp);
			return(2);
		}

	}

	visdelay2_begin();

	if(pgp_type == 2)
		action += 4;   /* GPG */

	switch(action)
	{
	case 0:
	case 4:
		/* don't do PGP */
		fname2_ptr = fname_temp;  /* so we just copy the input */
		break;

	case 1:   /* PGP sign */
		sprintf(buf,"pgp +verbose=0 -sta %s -o %s -z \"%s\" -u \"%s\" 2> null: { > %s }",
				fname_temp,fname2_ptr,passwd,user_name,pgp_stdout);
		pgp_command(buf,0);
		break;

	case 2:

		/* encrypt with public key of addressees */
		sprintf(buf,"pgp +batchmode +verbose=0 -eat %s -@ %s -o %s 2> null: { > %s }",
				fname_temp,pgp_list,fname2_ptr,pgp_stdout);
		pgp_command(buf,0);
		break;

	case 3:  /* sign and encrypt */
		/* sign and encrypt */
		sprintf(buf,"pgp +batchmode +verbose=0 -east %s -@ %s -o %s -z \"%s\" -u \"%s\" 2> null: { > %s }",
				fname_temp,pgp_list,fname2_ptr,passwd,user_name,pgp_stdout);
		pgp_command(buf,0);
		break;



	case 5:   /* GPG sign */
		sprintf(buf,"%s -o %s -u \"%s\" -sat %s",gpgpwd,fname2_ptr,user_name,fname_temp);
		pgp_command(buf,1);
		break;

	case 6:   /* GPG encrypt with public key */
		sprintf(buf,"%s -r -o %s --always-trust -eat %s",gpgcall,fname2_ptr,fname_temp);
		pgp_command(buf,2);
		break;

	case 7:   /* GPG sign and encrypt */
		sprintf(buf,"%s -r -o %s -u \"%s\" --always-trust -seat %s",gpgpwd,fname2_ptr,user_name,fname_temp);
		pgp_command(buf,3);
		break;
	}

	call_event_process(10);

	/* overwrite the plain-text data after encryption has been used */
	if((action & 3) >= 2)
	{
		length = get_filelength(fname_temp);
		f = fopen(fname_temp,"w");
		if(f != NULL)
		{
			for(i=0; i<length; i++)
			{
				fputc(0,f);
			}
			fflush(f);
			fclose(f);
		}
	}
	remove(fname_temp);

	visdelay2_end();

	/* copy the temporary file to the original output */
	f = fopen(fname2_ptr,"r");
	if(f == NULL)
	{
		switch(pgp_report(1))
		{
		case 0:
			werr(0,"%s failed to process message",pgp_name);
			break;

		case 4:
			pgp_clear_password();
			werr(0,"%s: Bad password",pgp_name);
			break;
		}

		*f_out = save_f;
		return(1);
	}

	if(action != 0)
	{
		result = pgp_report(1);
		if(result == 5)
		{
			/* can't find public key */
			*f_out = save_f;
			fclose(f);
			remove(fname2_ptr);
			return(1);
		}
	}

	while(!feof(f))
	{
		if(fgets(buf,sizeof(buf),f)==NULL)
			break;

		/* correct the sig separator */
		if((action == 1) && (memcmp(buf,"- -- \n",6)==0))
			fputs(&buf[2],save_f);
		else
			fputs(buf,save_f);
	}
	fclose(f);

	remove(fname2_ptr);

	if((action & 1) && (hold_password_flag))
	{
		hold_password_ok = 1;
	}
	if(hold_password_ok==0)
	{
		pgp_clear_password();
	}

	*f_out = save_f;
	return(0);
}   /* end of pgp_end_text */



int pgp_display_output(TEXTR *t, int start)
/*****************************************/
/* Write contents of pgp_out at start of text article.
   Call with t=NULL to count size of text to be added
   Call with t set  to add the text */
{
	int  len;
	int  size=0;
	FILE *f;
	char buf[256];

	f = fopen("<pluto$tmp>.pgp_out","r");
	if(f==NULL)
		return(0);

	for(;;)
	{
		if(fgets(buf,sizeof(buf),f)==NULL)
			break;

		/* discard certain lines that we don't want to see */
		if(memcmp(&buf[24],"remove temp",11)==0)
			continue;

		if((buf[26]=='[') && (strstr(&buf[27],"image")!=NULL))
			continue;

		len = strlen(buf);
		if(buf[len-2] =='\r')
		{
			buf[len-2] = '\n';  /* replace CR LF by LF */
			len--;
		}

		if(size==0)
		{
			if(t != NULL)
			{
				memcpy(&t->text_base[start],"-----BEGIN PGP STATUS-----\n",27);
			}
			size = 27;
		}

		if(t != NULL)
		{
			memcpy(&t->text_base[start + size],buf,len);
		}
		size += len;
	}
	fclose(f);

	if(size == 0)
		return(0);

	if(t != NULL)
	{
		memcpy(&t->text_base[start+size],"-----END PGP STATUS-----\n\n",26);
	}

	return(size+26);

}   /* end of pgp_display_output */



void pgp_decode(TEXTR *t)
/***********************/
{
	FILE *f;
	int  i;
	int  c;
	int  prev_c;
	int  length;
	int  new_length;
	int  diff;
	int  sig;
	int  display;
	int  start;
	int  result;
	int  report_length;
	int  multipart=0;
	int  report_written;
	char *p;
	char *pgp_prog;
	char *passwrd;
	char fname[40];
	char fname2[40];
	char command[320];

	static char *path_photo = "<pluto$tmp>.Photo_id";
	static char *movephoto = " --photo-viewer \"copy %i <pluto$tmp>.Photo_id ~CDF~V\"";

	if(pgp_present()==0)
		return;
	pgp_prog = "pgp";
	keyid[0] = 0;

	start = t->internet_header;
	length = t->text_length - start;

	check_ramfs();
	sprintf(fname,"%s.pgp1",tmp_path);
	sprintf(fname2,"%s.pgp2",tmp_path);

	f = fopen(fname,"w");
	if(f == NULL)
	{
		return;
	}

	visdelay2_begin();

	/* write out text to temp file */
	fwrite(&t->text_base[start],1,length,f);
	fclose(f);

	/* check for public key block */
	p = &t->text_base[t->text_start];
	for(i=0; i<t->n_lines; i++)
	{
		if(memcmp(p,"-----BEGIN PGP ",15)==0)
		{
			if(memcmp(&p[15],"PUBLIC KEY BLOCK-----",21)==0)
			{
				/* add public key to keyring */
				if(pgp_type==1)
				{
					sprintf(command,"%s +batchmode -ka %s 2> null: { > %s }",pgp_prog,fname,pgp_stdout);
				}
				else
				{
					sprintf(command,"%s -a --import %s",gpgcall,fname);
				}
				pgp_command(command,0);
				pgp_report(0);
				remove(fname);
				visdelay2_end();
				return;
			}
			break;
		}
		p += t->line_tab[i];
	}

	if(pgp_type==1)
	{
		sprintf(command,"%s +verbose=0 %s -o %s 2> null: { > %s }",pgp_prog,fname,fname2,pgp_stdout);
	}
	else
	{
		remove(path_photo);

		if(hold_password_ok)
			sprintf(command,"%s%s -o %s -d %s",gpgpwd,movephoto,fname2,fname);
		else
			sprintf(command,"%s%s -o %s -d %s",gpgcall,movephoto,fname2,fname);

	}

	pgp_command(command,9);
	display = 1;

	if(pgp_report(0) == 4)
	{
		/* enter pass phrase */
		passwrd = pgp_password(pgp_username,1);
		if(passwrd == NULL)
			display = 0;
		else
		{
			if(pgp_type==1)
			{
				sprintf(command,"%s +verbose=0 %s -o %s -z \"%s\" 2> null: { > %s }",pgp_prog,fname,fname2,passwrd,pgp_stdout);
			}
			else
			{
				sprintf(command,"%s%s -o %s -d %s",gpgpwd,movephoto,fname2,fname);
			}
			pgp_command(command,9);
			result = pgp_report(1);

			if(result == 4)
			{
				pgp_clear_password();
				werr(0,"%s: Bad password",pgp_name);
				display = 0;
			}
		}
	}

	if(pgp_type == 2)
	{
		set_filetype("<pluto$tmp>.pgp_out",0xfff);
	}
	else
	{
		file_copy("<pluto$tmp>.pgp_st","<pluto$tmp>.pgp_out");
	}

	if((pgp_type==2) && (display))
	{
		sprintf(command,"JPEGview -d -n %s %s",keyid,path_photo);
		if(get_filelength(path_photo) > 0)
			wimp_starttask(command);

//      wimp_starttask("type <pluto$tmp>.pgp_out");
	}



	remove(fname);

	if(hold_password_flag)
	{
		/* remember password */
		hold_password_ok = 1;
	}
	if(hold_password_ok==0)
	{
		memset(password,0,sizeof(password));
	}


	if(sig_data[0] != 0)
	{
		sig_data[69] = 0;   /* Title length in Article Viewer */
		win_settitle(t->w_card,sig_data);
	}

	f = fopen(fname2,"r");
	if(f == NULL)
	{
		/* no output from decode - it's failed */
		visdelay2_end();
		wimp_starttask("type <pluto$tmp>.pgp_out");
		return;
	}

	fread(command,1,23,f);
	rewind(f);

	if(memcmp_lc(command,"content-type: multipart",23)==0)
		multipart = 1;

	if(multipart == 1)
	{
		report_length = 0;  /* don't add gpg report if multipart decode */
	}
	else
	{
		report_length = pgp_display_output(NULL,0);
	}

	new_length = get_filelength(fname2) + report_length;
	if(new_length == 0)
	{
		visdelay2_end();
		fclose(f);
		return;
	}

	sig = t->sig_start;
	diff = new_length - length;

	if(diff > 0)
	{
		move_down(t,t->text_length,diff);
		diff = 0;
	}

	/* write PGP report at start of text */
	if(multipart == 0)
	{
		pgp_display_output(t,start);
		start += report_length;
	}

	/* read decrypted text, replace CR LF by LF */
	p = &t->text_base[start];

	prev_c = 0;
	for(i=0; i<new_length; i++)
	{
		c = fgetc(f);

		if((c == '\n') && (prev_c == '\r'))
		{
			p--;          /* remove CR before NL */
			diff--;
		}
		*p++ = prev_c = c;
	}
	fclose(f);

	if(multipart)
	{
		/* look whether first part is text/plain and if so, put pgp report there */
		report_written = 0;
		for(i=start+23; i < (t->text_length-25); i++)
		{
			if(memcmp_lc(&t->text_base[i],"\ncontent-type: text/plain",25)==0)
			{
				i+=24;
				while((i < t->text_length) && (memcmp(&t->text_base[i],"\n\n",2)!=0))
					i++;
				i+=2;

				move_down(t,i,pgp_display_output(NULL,0));
				pgp_display_output(t,i);
				report_written = 1;
				break;
			}
		}

		if(!report_written)
		{
			wimp_starttask("type <pluto$tmp>.pgp_out");
		}
	}

	if(diff < 0)
	{
		move_up(t,t->text_length,-diff);
	}

	sig = sig - 54 + report_length;

	if((sig > 0) && (sig < t->text_length))
	{
		/* adjust the position of the signature */
		for(i=0; i<19; i++)
		{
			if(memcmp(&t->text_base[sig+i],"\n--\n",4)==0)
			{
				sig = sig + i + 1;
				/* re-insert the space at the end of the sig separator that PGP has stripped */
				move_down(t,sig+2,1);
				t->text_base[sig+2]=' ';
				break;
			}
		}
		if(i==5)
			sig += 2;
		t->sig_start = sig;
	}
	else
	{
		/* no plain signature present in original, look for it in decrypted text */

		i = t->text_length - 500;
		if(i < t->internet_header)
			i = t->internet_header;

		for(sig=t->text_length-4; sig>i; sig--)
		{
			if(memcmp(&t->text_base[sig],"\n--\n",4)==0)
			{
				move_down(t,sig+3,1);
				t->text_base[sig+3]=' ';
				t->sig_start = sig;
				break;
			}
		}
	}

	if(t->multi_encrypted == 1)
	{
		/* find and delete multipart/encrypted line from message header */
		for(i=0; i<t->internet_header; i++)
		{
			if((t->text_base[i] == '\n') && (memcmp_lc(&t->text_base[i+1],"content-type: multipart/encrypted",33)==0))
			{
				start = i;

				i+=33;
				while((t->text_base[i] != '\n') || (isspace(t->text_base[i+1])))
					i++;

				move_up(t,i,i-start);
			}
		}

		if(multipart)
		{
			move_up(t,t->internet_header,1);
		}
	}

	calc_line_tab(t);
	redraw_text_lines(t,0,-1);
	text_reopen(t);

	remove(fname2);

	visdelay2_end();
}   /* end of pgp_decode */




int pgp_encrypt(TEXTR *t)
/***********************/
/* Encrypt a stored article with user's own public key */
{
	FILE *f;
	int  i;
	int  length1;
	int  length2;
	int  diff;
	int  start;
	int  user;
	char *user_name = "";
	static char *fname1 = "<pluto$tmp>.tmp1";
	static char *fname2 = "<pluto$tmp>.tmp2";
	char command[128];

	if(pgp_present() == 0)
	{
		beep();
		return(1);
	}

	if(memcmp(&t->text_base[t->internet_header],"-----BEGIN PGP MESSAGE-----",27)==0)
	{
		if(query("Encrypt? Already encrypted.",NULL)==0)
			return(1);
	}

	f = fopen_werr(fname1,"w",NULL);
	if(f == NULL)
		return(1);

	user = (t->cptr->user & 0x1f) - 1;
	if(user >= 0)
		user_name = options.mailbox[user].email_addr;


	visdelay2_begin();

	start = 0;   /* or t->internet_header  ?? */

	length1 = t->text_length - start;
	for(i=start; i < t->text_length; i++)
	{
		fputc(t->text_base[i],f);
	}
	fclose(f);

	if(pgp_type == 1)
		sprintf(command,"pgp +batchmode +verbose=0 -eat %s \"%s\" -o %s 2> null: { > %s }",
				fname1,user_name,fname2,pgp_stdout);
	else
		sprintf(command,"%s --default-recipient-self -o %s -eat %s",gpgcall,fname2,fname1);

	remove(fname2);
	pgp_command(command,8);
	remove(fname1);

	if(pgp_report(0) != 0)
	{
		visdelay2_end();
		return(1);
	}

	length2 = get_filelength(fname2);
	if(length2 > 54)
	{
		f = fopen_werr(fname2,"r",NULL);
		if(f != NULL)
		{
			diff = length2 - length1;
			if(diff > 0)
				move_down(t,t->text_length-1,diff);
			else if(diff < 0)
				move_up(t,t->text_length-1,-diff);

			for(i=0; i<length2; i++)
			{
				t->text_base[start+i] = fgetc(f);
			}
			fclose(f);
		}
	}

	visdelay2_end();
	return(0);
}   /* end of pgp_encrypt */
