static char rcsid[] = "@(#)$Id: pgp.c,v 1.57 2006/09/20 16:22:21 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.57 $   $State: Exp $
 *
 *  Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *                           (was hurtta+elm@ozone.FMI.FI)
 *
 *  Initially written by: Michael Elkins <elkins@aero.org>, 1995
 *****************************************************************************/

#include "def_elm.h"
#include "s_elm.h"

DEBUG_VAR(Debug,__FILE__,"pgp");

#ifdef USE_PGP
#include <sys/time.h>
#include "menu.h"

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif       

/* the column in which the userid begins in the 'pgp -kv' command output */
#define PGP2_USERID_OFFSET 30
#define PGP5_USERID_OFFSET 5


static char * PGPSelectKey P_((char *n, char **k, int len));

/* 'n' is the key we are looking for.  'k' is the list of possible matches
   which contains 'len' entries.  prompt the user for which key s/he really
   wants to use. */
static char * PGPSelectKey (n, k, len)
     char *n;
     char **k;
     int len;
{
    int i;
    char * buf = NULL;
    menu_t menu;
    
    struct menu_context  *page = new_menu_context();

    buf = elm_message(CATGETS(elm_msg_cat, ElmSet, ElmPgpMultipleKeys,
			 "Multiple keys match '%s':"), 
		 n);
    MenuInit (&menu, buf, catgets(elm_msg_cat, ElmSet, ElmPgpSelectKey,
				  "Select key or 'q' to quit: "),
	      0);
    
    for (i = 0; i < len; i++)
	MenuAdd(&menu, k[i]);
    
    for (;;) {

	switch (MenuLoop(&menu,page)) {
	case 'q':
	    MenuDestroy(&menu);
	    free(buf);

	    erase_menu_context(&page);
	    return(0);
	case '\n':
	case '\r':
	    MenuDestroy(&menu);
	    free(buf);

	    erase_menu_context(&page);
	    return(k[MenuCurrent(menu)]);
	}
    }
    /* not reached */
}



static void close_pipe P_((struct run_state *rs));
static void close_pipe(rs)
     struct run_state *rs;
{
    int *array = rs->ext_init_data;

    close(array[0]);
}

static int GetPGPKey P_((char *name, char *target,
			 int targetsize, enum pgp_version v));

static int GetPGPKey (name, target, targetsize, v)
	char *name;
	char *target;
	int targetsize;
	enum pgp_version v;
{
    /* given "name", return the string to use during the pgp call to specify 
       the key which the user means.  return -1 on error. */
    
    char buf[STRING], address[STRING], *c, **keys=0, *pc, userpart[STRING];
    int i=0, keys_len=0, keys_max=0, return_val=0, start=0;
    pid_t pid;
    FILE *p;
    
    if (!name || !target) {
	DPRINT(Debug,9,(&Debug,  "GetPGPKey=-1\n"));
	return -1;
    }
    DPRINT(Debug,9,(&Debug,  "GetPGPKey: name=%s,v=%d\n",name,v));

    if (index(name, '@') == NULL && index(name, '(') == NULL && 
	index(name, '<') == NULL) {
	/* this is either just a username, or someone's real name.  in either
	   case it only needs to be checked once. */
	
	strfcpy(address, name, sizeof address);
	i = 2;
    }
    else {
	get_address_from (name, address, sizeof address);
	
	i=0;
	while (address[i] && address[i] != '@') {
	    userpart[i] = address[i];
	    i++;
	}
	userpart[i] = '\0';
	
	i = 0;
    }
    c = address;
    
    /* the following loop first checks to see if any keys with the full
       address, or real name, or finally the username exist */

    for (;;) {
	int fd[2];
	int array[1];       
	CONST char * argv[10];
	int code;
	int stat = -1;

	struct run_state RS;

	while (*c && isspace (*c)) c++; /* move past any leading space! */
	DPRINT(Debug,10,(&Debug,  "GetPGPKey: c=%s\n",c));    

	if (pipe (fd) == -1) {
	    DPRINT(Debug,1,(&Debug,  
			    "GetPGPKey()=-1: ERROR: pipe (errno %d)\n",
			    errno));

	    return -1;
	}

	array[0] = fd[0];
	RS.save_errno    = 0;
	RS.ext_init_data = array;
	RS.ext_init      = close_pipe;
	RS.ext_env       = NULL;

	switch(v) {
	    static char path[1000];
	    int n;

	case pgp2:	
	    n = 0;
	    {
		char * pgp2_path_val = give_dt_estr_as_str(&pgp2_path_e, 
							   "pgp2");
		if (! pgp2_path_val)
		    return -1;
		argv[n++] = pgp2_path_val; 
	    }
	    argv[n++] = "+verbose=0";
	    argv[n++] = "+language=en"; 
	    argv[n++] = "-kv";
	    argv[n++] = c; 
	    argv[n]   = NULL;
	    break;

	case pgp5:
	    n = 0;
	    {
		/* give_dt_estr_as_str adds / to end */
		char * pgp5_dir_val  = give_dt_estr_as_str(&pgp5_dir_e, 
							   "pgp5-dir");
		if (! pgp5_dir_val)
		    return -1;
		elm_sfprintf(path, sizeof path,FRM("%spgpk"),pgp5_dir_val);
	    }
	    argv[n++] = path;
	    argv[n++] = "+verbose=0";
	    argv[n++] = "+language=en"; 
	    argv[n++] = "-l"; 
	    argv[n++] = c;
	    argv[n]   = NULL;
	    break;
	    
	case gpg:
	    n = 0;	    
	    {
		char * gpg_path_val = give_dt_estr_as_str(&gpg_path_e, "gpg");
		if (! gpg_path_val)
		    return -1;
		argv[n++] = gpg_path_val;
	    }
	    argv[n++] = "--list-public-keys";
	    argv[n++] = c;
	    argv[n++] = NULL;
	    break;
	}

	code = start_run(&RS, SY_RUN_STATE_INIT, argv ,
			 -1,fd[1]);
	  
	close (fd[1]);

	if (!code) {
	    DPRINT(Debug,1,(&Debug,  
			    "GetPGPKey()=-1: running pgp/gpg failed\n"));
	    return -1;
	}


	p = fdopen (fd[0], "r");
	if (p == NULL) {
	    int tmp;
	    int err = errno;

	    DPRINT(Debug,1,(&Debug,  
			    "GetPGPKey()=-1: ERROR: fdopen (errno %d)\n", 
			    err));
	    kill(RS.pid,SIGTERM);
	    wait_end(&RS,&tmp);	    

	    return -1;
	}

	code = run_already_done(&RS,&stat);
	if (0 != code) {
	    DPRINT(Debug,5,(&Debug,
			    "now pgp/gpg is ALREADY completing\n"));
	}      

    retry:
	while (fgets(buf, STRING, p) != NULL) {
	    DPRINT(Debug,10,(&Debug, 
			     "GetPGPKey: %s\n",buf));
	    switch(v) {
	    case pgp2:	

		/* see if we've reached the beginning of the key listings... */
		
		if (!start && strncmp(buf, "pub", 3)==0)
		    start=1;
		
		if (start) {
		    
		    /* if we've read all the keys, stop here */
		    if (buf[0] != 'p' && buf[0] != ' ')
			break;
		    
		    if (keys_len == keys_max)
			keys = (char**)DynamicArray((void **)keys, 
						    sizeof(char*), 
						    &keys_max, 5);
		    
		    pc = rindex(buf, '\n');
		    if (!pc) /* this shouldn't happen! */
			continue;
		    *pc = '\0';
 		    pc = buf + PGP2_USERID_OFFSET;
  		    
 		    keys[keys_len] = safe_strdup(pc);
		    ++keys_len;
		}
		break;

	    case gpg: 
		if (0 == strncmp(buf, "pub ",4)) {
		    int pos = 4;
		    char work[SLEN];
		    
		    start=1;
		    
		    /* ID */
		    pos = get_word(buf,pos,work,sizeof work);
		    if (-1 == pos)
			break;
		    
		    /* DATE */
		    pos = get_word(buf,pos,work,sizeof work);
		    if (-1 == pos)
			break;

		    while (isspace(buf[pos]))
			   pos++;

		    if (!buf[pos])
			break;

		    /* Ignore  [expires: -text (language may be different) */
		    if ('[' == buf[pos])
			break;		    
		    
		    if (keys_len == keys_max)
			keys = (char**)DynamicArray((void **)keys, 
						    sizeof(char*), 
						    &keys_max, 5);
 
		    keys[keys_len] = safe_strdup(buf+pos);
		    keys_len++;

		} else if (start && buf[0] == ' ' ||
			   0 == strncmp(buf, "uid ",4)) {

		    int pos = 4;

		    while (isspace(buf[pos]))
			   pos++;

		    if (!buf[pos])
			break;

		    if (keys_len == keys_max)
			keys = (char**)DynamicArray((void **)keys, 
						    sizeof(char*), 
						    &keys_max, 5);

		    keys[keys_len] = safe_strdup(buf+pos);
		    keys_len++;

		} else
		    start = 0;
		    		
		break;

	    case pgp5:	
		if (strncmp(buf, "uid", 3)==0) {

		    if (keys_len == keys_max)
			keys = (char**)DynamicArray((void **)keys, 
						    sizeof(char*), 
						    &keys_max, 5);
		    
		    pc = rindex(buf, '\n');
		    if (!pc) /* this shouldn't happen! */
			continue;
		    *pc = '\0';
		    
		    pc = buf + PGP5_USERID_OFFSET;
		    keys[keys_len] = safe_strdup(pc);
		    ++keys_len;
		}
	    }
	}

	if (ferror(p) && EINTR == errno) {
	    clearerr(p);
	    DPRINT(Debug,5,(&Debug,
			    "Reading of result interrupted (EINTR) -- retrying\n"));

	    if (0 == code) {
		code = run_already_done(&RS,&stat);
		if (0 != code) {
		    DPRINT(Debug,5,(&Debug,
				    "now pgp/gpg is completing\n"));
		}      
	    }

	    goto retry;
	}

	fclose(p);

	if (0 == code)
	    code = wait_end(&RS,&stat);

	if (code < 0) {
	    DPRINT(Debug,5,(&Debug,
			    "pgp/gpg dies on signal %d\n",
			    -code));
	} else {
	    DPRINT(Debug,5,(&Debug,"pgp/gpgp exited with status %d\n",stat));
	}

	if (keys_len > 0 || i > 1)
	    break;
	else {
	    if (i == 0) {
		char * z;
		strfcpy(address,name, sizeof address);
		
		z = strchr(address,'<');   /* Lousy */

		/* if there was no real name, go on to the userpart check */
		if (!z || z == address) {
		    c = userpart;
		    i++;
		} else
		    *z = '\0';
	    } else if (i == 1)
		c = userpart;
	    i++;
	}
    }
    
    if (keys_len == 1) /* perfect match! */
	get_address_from(keys[0], target, targetsize);
    else if (keys_len > 1) { /* ask the user which, if any, s/he meant */
	c = PGPSelectKey(name, keys, keys_len);
	if (c)
	    get_address_from(c, target, targetsize);
	else {
	    target[0] = '\0';
	    return_val = -1;
	}
    } else {
	target[0] = '\0';
	return_val = -1;
    }

    DestroyDynamicArray((void **)keys);
    
    DPRINT(Debug,9,(&Debug,  "GetPGPKey(name=%s)=%d, target=%s\n",
		    name,return_val,target));
    return return_val;
}

static int pgp_call P_((char *filename,int opts,
			struct mailing_headers *headers,
			enum pgp_version v,
			struct menu_context  *page));

static int pgp_encrypt P_((char *filename, char *ids, char *sig,
			   int opts, int metoo,
			   enum pgp_version v,
			   struct menu_context  *page));

static int pgp_call (filename, opts, headers, v, page)
	char *filename;
	int opts;
	struct mailing_headers *headers;
	enum pgp_version v;
	struct menu_context  *page;
{
    char tobuf[VERY_LONG_STRING] = {0};
    char frombuf[VERY_LONG_STRING] = {0};
    int status;
    int r;
    int LINES, COLUMNS;

    menu_get_sizes(page, &LINES, &COLUMNS);   
    
    do {
	if (opts & PGP_MESSAGE) {
	    struct addr_item *x;
	    /* build up the list of recipients */
	    tobuf[0] = '\0';
	    
	    for (x = headers->to.addrs; 
		 x < headers->to.addrs + headers->to.addrs_len;
		 x++) {
		if (tobuf[0])
		    strfcat(tobuf, ", ", sizeof tobuf);
	    strfcat(tobuf, x->addr, sizeof tobuf);
	    }
	    for (x = headers->cc.addrs; 
		 x < headers->cc.addrs + headers->cc.addrs_len;
		 x++) {
		if (tobuf[0])
		    strfcat(tobuf, ", ", sizeof tobuf);
		strfcat(tobuf, x->addr, sizeof tobuf);
	    }
	    for (x = headers->bcc.addrs; 
		 x < headers->bcc.addrs + headers->bcc.addrs_len;
		 x++) {
		if (tobuf[0])
		    strfcat(tobuf, ", ", sizeof tobuf);
		strfcat(tobuf, x->addr, sizeof tobuf);
	    }
	    
	    /* If the message is to be encrypted, give the user a chance to 
	     * edit the list of ids to encrypt to since the given address may 
	     * not always be correct.
	     */
	redraw:
	    PutLineX (LINES-1-2, 0, CATGETS(elm_msg_cat, ElmSet, ElmPgpTo,
					      "To: "));
	    status = optionally_enter(tobuf, LINES-1-2, 4, 
				      OE_APPEND_CURRENT|OE_REDRAW_MARK|
				      OE_SIG_CHAR /* Ctrl-C */,
				      sizeof tobuf,
				      page);
	    if (REDRAW_MARK == status)
		goto redraw;
	    if (status != 0 || tobuf[0] == '\0')
		return FALSE;
	}

	if (pgp_askpgpsig && (opts & PGP_SIGNED_MESSAGE)) {
	    /* If the message is to be signed, give the user a chance to 
	     * specify with which signature.
	     */
	    strfcat(frombuf, username, sizeof frombuf);
	redraw2:
	    PutLineX (LINES-1-2, 0, CATGETS(elm_msg_cat, ElmSet, ElmPgpFrom,
					      "From: "));
	    status = optionally_enter(frombuf, LINES-1-2, 6,
				      OE_APPEND_CURRENT|OE_REDRAW_MARK|
				      OE_SIG_CHAR /* Ctrl-C */,
				      sizeof frombuf,
				      page);
	    if (REDRAW_MARK == status)
		goto redraw2;
	    if (status != 0 || frombuf[0] == '\0')
		return FALSE;
	}
	r = pgp_encrypt (filename, tobuf, frombuf, opts, auto_copy_sent, v,
			 page);
    } while (r < 0);
    if (r < 0)
	r = 0;
    return (r);
}

static void close_it P_((struct run_state *rs));
static void close_it(rs)
     struct run_state *rs;
{
    int *close_fd = rs->ext_init_data;

    if (*close_fd != -1)
	close(*close_fd);
}

static int pgp_encrypt (filename, ids, sig, opts, metoo, v, page)
     char *filename, *ids, *sig;
     int opts, metoo;
     enum pgp_version v;
     struct menu_context  *page;
{
    int id_len=0, id_max=0, usepgppass=FALSE, fd[2];
    char
	keyid[STRING], buf[VERY_LONG_STRING], **id_array=0, *c,
	*p;
    CONST char ** joined = NULL;
    int close_fd;  
#define MAX_ARG 1000
    CONST char * argv[MAX_ARG];
    char * argv0[MAX_ARG];
    char * env[2];
    int a;
    int ret = 0;
    int was_redraw = 0;   /* FIXME */
    struct run_state RS;
    int exit_code;
    int LINES, COLUMNS;

    menu_get_sizes(page, &LINES, &COLUMNS);   

    DPRINT(Debug,2,(&Debug, 
		"pgp_encrypt(): ids=\"%s\", signwith=\"%s\", encrypt=%s, sign=%s\n", 
		ids, sig, 
		opts & PGP_MESSAGE ? "TRUE" : "FALSE", 
		opts & PGP_SIGNED_MESSAGE ? "TRUE" : "FALSE"));

    p = ids;
    /* If this message is to be encrypted, look up the keys of all the
       recipients */
    if (opts & PGP_MESSAGE) {
	while ((c = strtok(p, ",")) != NULL) {
	    int Len;
	    
	    while (whitespace(*c))
		c++;
	    if ('\0' == *c)
		goto next_recipient;

	    if (GetPGPKey(c, keyid, sizeof keyid, v) == -1) {
	    redraw1:
		while(1) {
		    int x_coord, y_coord, ans;
		    int def_ans = 'q';
		    MoveCursor (LINES-4, 0);
		    CleartoEOS ();
		    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmPgpNoMatch,
				      "Couldn't find key matching '%s'!"),
			      c);		    
		    print_format_center (LINES-3, 
					 CATGETS(elm_msg_cat, ElmSet, ElmPgpKeyError,
						 "e)dit recipient list, s)kip recipient, q)uit"));
		    menu_PutLineX (page,LINES-1-3, 0, 
				   CATGETS(elm_msg_cat, ElmSet, 
					   ElmPgpSelect,
					   "Select [e/s/q]: "));
		    menu_GetXYLocation(page, &x_coord, &y_coord);
		    menu_PutLineX (page,x_coord, y_coord,FRM("%c"),def_ans);
		    menu_MoveCursor(page, x_coord, y_coord);
		    
		    switch((ans = menu_ReadCh(page,REDRAW_MARK))) {
		    case '\n':
		    case '\r':
			ans = def_ans;
		    case REDRAW_MARK:
			was_redraw = 1;
			menu_ClearScreen(page);
			goto redraw1;
		    }


		    switch(ans) {
		    case 'e':
			Write_to_screen(CATGETS(elm_msg_cat, ElmSet, 
						ElmPgpEditRec,
						"Edit recipients"));
			return -1; /* Indicate KEY error */
		    case 's':
			Write_to_screen(CATGETS(elm_msg_cat, ElmSet, 
						ElmPgpSkipRec,
						"Skip recipients"));
			goto next_recipient;
		    case 'q':
			Write_to_screen(CATGETS(elm_msg_cat, ElmSet, 
						ElmPgpQuit,
						"Quit"));
			return 0;
		    }
		}
	    }
	    if (id_len == id_max)
		id_array = (char**)DynamicArray((void **)id_array, 
						sizeof(char*), &id_max, 25);
	    Len = strlen(keyid) + 1;
	    id_array[id_len] = (char*)safe_malloc(Len);
	    strfcpy(id_array[id_len], keyid, Len);
	    id_len++;
	next_recipient:
	    p=NULL;
	}
	/* If all recpients s)kipped */
	if (id_len < 1 || !id_array[0]) {
	    return 0;
	}
    }
    if (id_len == id_max)
	id_array = (char**)DynamicArray((void **)id_array, 
					sizeof(char*), &id_max, 1);
    id_array[id_len] = NULL;

    if ((opts & PGP_SIGNED_MESSAGE) && pgp_keeppass && 
	pgp_goodPassphrase(v)) {
	usepgppass = TRUE;
	pipe(fd);
    }

    close_fd = -1;
    env[0] = NULL;
    RS.ext_env = env; 
    RS.ext_init_data = &close_fd;
    RS.ext_init      = close_it;

    switch(v) {
    case pgp2:
    case pgp5:
	if (usepgppass) {
	    static char buffer[20];
	    elm_sfprintf(buffer, sizeof buffer,FRM("PGPPASSFD=%d"),
			 fd[0]);
	    env[0] = buffer;
	    close_fd = fd[1];
	}
	env[1] = NULL;
    }

    /* copy the file into it's final destination */
    elm_sfprintf(buf, sizeof buf,
		 FRM("%s.asc"), filename);

    Raw(OFF);
    ClearScreen(0);
    Write_to_screen(CATGETS(elm_msg_cat, ElmSet,ElmRunningPgp,
			      "Running PGP..."));
    Write_to_screen(FRM("\n\n"));

    a = 0;
    switch(v) {
	static char path[1000];
	int i;	
    case pgp2:	
	{
	    char * pgp2_path_val = give_dt_estr_as_str(&pgp2_path_e, "pgp2");

	    if (! pgp2_path_val) {
		Raw(ON);
		return FALSE;
	    }

	    argv0[a++] = pgp2_path_val;
	}
	if (metoo)
	    argv0[a++] = "+encrypttoself=on";
       
	if ((usepgppass || !(opts & PGP_SIGNED_MESSAGE)) &&
	    !pgp_interactive)
	    argv0[a++] = "+batchmode";
	
	if (opts & PGP_SIGNED_MESSAGE)
	    argv0[a++] = "+clearsig=on";
	argv0[a++] = "+verbose=0";
	switch (opts & (PGP_SIGNED_MESSAGE|PGP_MESSAGE)) {
	case PGP_SIGNED_MESSAGE|PGP_MESSAGE:
	    argv0[a++] = "-atwse";
	    break;
	case PGP_SIGNED_MESSAGE:
	    argv0[a++] = "-atws";
	    break;
	case PGP_MESSAGE:
	    argv0[a++] = "-atwe";
	    break;
	}

	if (pgp_askpgpsig && (opts & PGP_SIGNED_MESSAGE)) {
	    argv0[a++] = "-u";
	    argv0[a++] = sig;
	}
	argv0[a++] = filename;
	argv0[a] = NULL;
	
	joined = join_argv(argv0,id_array);

	ret = start_run(&RS,SY_RUN_STATE_ENV|SY_RUN_STATE_INIT|
			SY_ENAB_SIGINT,
			joined,-1,-1);

	break;
    case pgp5:
	{
	    /* give_dt_estr_as_str adds / to end */
	    char * pgp5_dir_val  = give_dt_estr_as_str(&pgp5_dir_e, 
						       "pgp5-dir");

	    if (! pgp5_dir_val) {
		Raw(ON);
		return FALSE;
	    }

	    if (opts & PGP_MESSAGE) 
		elm_sfprintf(path, sizeof path,FRM("%spgpe"),pgp5_dir_val);
	    else
		elm_sfprintf(path, sizeof path,FRM("%spgps"),pgp5_dir_val);
	}
	argv[a++] = path;
	if (metoo)
	    argv[a++] = "+encrypttoself=on";
	if ((usepgppass || !(opts & PGP_SIGNED_MESSAGE)) &&
	    !pgp_interactive)
	    argv[a++] = "+batchmode";
	
	if (opts & PGP_SIGNED_MESSAGE)
	    argv[a++] = "+clearsig=on";
	argv[a++] = "+verbose=0";
	switch (opts & (PGP_SIGNED_MESSAGE|PGP_MESSAGE)) {
	case PGP_SIGNED_MESSAGE|PGP_MESSAGE:
	    argv[a++] = "-ats";
	    break;
	case PGP_SIGNED_MESSAGE:
	    argv[a++] = "-at";
	    break;
	case PGP_MESSAGE:
	    argv[a++] = "-at";
	    break;
	}
	if (pgp_askpgpsig && (opts & PGP_SIGNED_MESSAGE)) {
	    argv[a++] = "-u";
	    argv[a++] = sig;
	}
	argv[a++] = "-o";
	argv[a++] = buf;

	for (i = 0; a < MAX_ARG -5 && id_array[i]; i++) {
	    argv[a++] = "-r";
	    argv[a++] = id_array[i];
	}
	argv[a++] = filename;
	argv[a] = NULL;
	
	ret = start_run(&RS,SY_RUN_STATE_ENV|SY_RUN_STATE_INIT|
			SY_ENAB_SIGINT,
			argv,-1,-1);

	break;
    case gpg: 
	{
	    char * gpg_path_val = give_dt_estr_as_str(&gpg_path_e, "gpg");

	    if (! gpg_path_val) {
		Raw(ON);
		return FALSE;
	    }

	    argv[a++] = gpg_path_val;
	}
	if (usepgppass) {
	    static char buffer[10];
	    argv[a++] = "--passphrase-fd";
	    elm_sfprintf(buffer, sizeof buffer,FRM("%d"),
			 fd[0]);
	    argv[a++] = buffer;
	    close_fd = fd[1];
	}	
	if ((usepgppass || !(opts & PGP_SIGNED_MESSAGE)) &&
	    !pgp_interactive)
	    argv[a++] ="--batch";
	
	if (metoo) {
	    argv[a++] = "--encrypt-to";
	    if (pgp_askpgpsig && (opts & PGP_SIGNED_MESSAGE)) {
		static char buffer[STRING];
		elm_sfprintf(buffer, sizeof buffer,FRM("<%s>"),sig);
		argv[a++] = buffer;
	    }
	    else {
		static char buffer[STRING];
		elm_sfprintf(buffer, sizeof buffer,
			     FRM("<%s@%s>"),username,hostfullname);
		argv[a++] = buffer;
	    }
	}
	argv[a++] = "--no-verbose";
	if (pgp_askpgpsig && (opts & PGP_SIGNED_MESSAGE)) {
	    static char buffer[STRING];
	    argv[a++] = "--local-user";
	    elm_sfprintf(buffer, sizeof buffer,FRM("<%s>"),sig);
	    argv[a++] = buffer;
	}
	argv[a++] = "--output";
	argv[a++] = buf;
	for (i = 0; a < MAX_ARG -5 && id_array[i]; i++) {
	    argv[a++] = "--recipient";
	    argv[a++] = id_array[i];
	}
	switch (opts & (PGP_SIGNED_MESSAGE|PGP_MESSAGE)) {
	case PGP_SIGNED_MESSAGE|PGP_MESSAGE:
	    argv[a++] = "--sign";
	    argv[a++] = "--armor";
	    argv[a++] = "--encrypt";
	    break;
	case PGP_MESSAGE:
	    argv[a++] = "--armor";
	    argv[a++] = "--encrypt";
	    break;
	case PGP_SIGNED_MESSAGE:
	    argv[a++] = "--clearsign";
	    break;
	}
	argv[a++] = filename;
	argv[a] = NULL;
	
	ret = start_run(&RS,SY_RUN_STATE_ENV|SY_RUN_STATE_INIT|
			SY_ENAB_SIGINT,
			argv,-1,-1);

	break;
    }


    if (ret) {
	if (usepgppass) {	
	    write(fd[1], pgp_passphrase[v], strlen(pgp_passphrase[v]));
	    write(fd[1], "\n", 1); /* pgp expects this as a line terminator! */
	    close(fd[1]);
	}
	ret = wait_end(&RS,&exit_code);
	Raw(ON);
    } else {
	Raw(ON);
	if (RS.save_errno)
	    lib_error(CATGETS(elm_msg_cat, ElmSet,ElmFailErrno,
			      "Failed: %.30s: %.40s"),
		      argv[0],error_description(RS.save_errno));
	else
	    lib_error(CATGETS(elm_msg_cat, ElmSet,ElmCantStart,
			      "Can't start %.30s"),
		      argv[0]);
	if (usepgppass) {	
	    close(fd[1]);
	}
	DestroyDynamicArray((void **)id_array); /* don't need this any more... */
	if (joined)
	    free(joined);

	return FALSE;
    }
    
    DestroyDynamicArray((void **)id_array); /* don't need this any more... */
    if (joined)
	free(joined);
    
    
    if (ret < 0) {
	lib_error(CATGETS(elm_msg_cat, ElmSet,ElmFailSignal,
			  "%.30s fail: Signal?"),
		  argv[0]);
	return FALSE;
    } else if (ret > 0) {
	if (RS.save_errno) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet,ElmFailErrno,
			      "Failed: %.30s: %.40s"),
		      argv[0],error_description(RS.save_errno));
	    return FALSE;
	} else if (exit_code) {	
	    lib_error(CATGETS(elm_msg_cat, ElmSet,ElmPgpErrorStatus,
			      "Pgp returned error status %d"),
		      exit_code);
	    if (pgp_keeppass)
		pgp_void_passphrase ();
	    return FALSE;
	} else {
	    if (rename(buf, filename) < 0) {
		lib_error(CATGETS(elm_msg_cat, ElmSet,ElmCantRenameTmpFile,
				  "Could not rename temporary file!"));
		return FALSE;
	    }
	}
    } else {
	lib_error(CATGETS(elm_msg_cat, ElmSet,ElmLostErrno,
			  "%.30s lost: %.40s"),
		  argv[0],error_description(RS.save_errno));
	return TRUE;
    }

    if (was_redraw)   /* FIXME: Should not needed */
	menu_trigger_redraw(page);

    return opts;
}

int pgp_menu (filename, headers, page)
     char *filename;
     struct mailing_headers *headers;
     struct menu_context  *page;
{
    int update = TRUE;
    enum pgp_version v = (enum pgp_version) give_dt_enumerate_as_int(&send_pgp_version);
    enum pgp_version version;
    int x_coord = 0, y_coord = 0;
    int def_ans = 'q';
    int LINES, COLUMNS;

    menu_get_sizes(page, &LINES, &COLUMNS);   

    if (!(version = have_pgp(v)))
	return FALSE;
    
    for (;;) {
	int ans;
	if (update) {
	    MoveCursor (LINES-4, 0);
	    CleartoEOS ();
	    print_format_center(LINES-3, 
				CATGETS(elm_msg_cat, ElmSet, ElmPgpEncSign,
					"e)ncrypt, s)ign, b)oth encrypt and sign or q)uit"));

	    menu_PutLineX (page,
			   LINES-4, 0, CATGETS(elm_msg_cat, ElmSet, ElmPgpP,
					       "pgp: "));
	    menu_GetXYLocation(page,
			       &x_coord, &y_coord);
	    
	    update = FALSE;
	}
	menu_PutLineX (page,
		       x_coord, y_coord,FRM("%c"),def_ans);
	menu_MoveCursor(page,x_coord, y_coord);
	
	switch((ans = menu_ReadCh(page, REDRAW_MARK))) {
	case '\n':
	case '\r':
	    ans = def_ans;
	}
	if (isascii(ans) && isupper(ans))
	    ans = tolower(ans);

	switch(ans) {
	case 'e':
	    menu_Write_to_screen(page,
				 CATGETS(elm_msg_cat, ElmSet, ElmPgpEncrypt,
					 "Encrypt"));
	    return (pgp_call (filename, PGP_MESSAGE, headers, version, page));
	case 's':
	    menu_Write_to_screen(page,
				 CATGETS(elm_msg_cat, ElmSet, ElmPgpSign,
					 "Sign"));
	    return (pgp_call (filename, PGP_SIGNED_MESSAGE, headers,
			      version, page));
	case 'b':
	    menu_Write_to_screen(page,
				 CATGETS(elm_msg_cat, ElmSet, ElmPgpSignEncrypt,
					 "Sign and Encrypt"));
	    return (pgp_call (filename, PGP_MESSAGE | PGP_SIGNED_MESSAGE, 
			      headers, version, page));
	case 'q':
	    menu_Write_to_screen(page,
				 CATGETS(elm_msg_cat, ElmSet, ElmPgpQuit,
					 "Quit"));
	    return FALSE;
	case REDRAW_MARK:
	case ctrl('L'):
	    update = 1;
	}
    }
    /* not reached */
}

void pgp_mail_public_key (mailbox, aview, page, prompt_area)
     struct MailboxView *mailbox;
     struct AliasView *aview;
     struct menu_context  *page;     
     struct menu_context  *prompt_area;     
{
    /* If redraw is needed use
          menu_trigger_redraw(page)
    */

    int ret;
    struct run_state RS;
    CONST char * argv[20];
    int argc = 0;
    char userid[SLEN], pgpkey[SLEN], tmpfil[STRING], subj[STRING];
    int status;
    int need_redraw = FALSE;
    enum pgp_version v = (enum pgp_version) give_dt_enumerate_as_int(&send_pgp_version);
    enum pgp_version version;
    int LINES, COLUMNS;
    char *tmp;

    tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
    if (!tmp)
	return;

    menu_get_sizes(page, &LINES, &COLUMNS);   

    userid[0] = '\0';
    pgpkey[0] = '\0';

    if (!(version = have_pgp(v)))
	return;
    
    PutLineX(LINES-3, 0, 
	     CATGETS(elm_msg_cat, ElmSet, ElmPgpEntUser,
		     "Enter userid of public key: "));
    CleartoEOS ();
    status = optionally_enter(userid, LINES-3, 28, OE_REDRAW_MARK|
			      OE_SIG_CHAR /* Ctrl-C */,
			      sizeof userid, page);
    while(REDRAW_MARK == status) {
	PutLineX(LINES-1-2, 0,   CATGETS(elm_msg_cat, ElmSet, ElmPgpEntUser,
					   "Enter userid of public key: "));
	status = optionally_enter(userid, LINES-3, 28, 
				  OE_REDRAW_MARK|OE_APPEND_CURRENT|
				  OE_SIG_CHAR /* Ctrl-C */,
				  sizeof userid, page);
	need_redraw = TRUE;
    }

    if ( status != 0) {
	if (need_redraw)
	    menu_trigger_redraw(page);
	
	menu_trigger_redraw(prompt_area);			
	return;
    }

    if (GetPGPKey(userid, pgpkey, sizeof pgpkey, version) < 0) {
	print_format_center(LINES-1, 
			    CATGETS(elm_msg_cat, ElmSet, ElmPgpSorryUserId,
				    "Sorry, couldn't find that userid."));
	ClearLine(LINES-3);

	menu_trigger_redraw(page);
	return;
    }
    elm_sfprintf(tmpfil, sizeof tmpfil,
		 FRM("%selm.%d.asc"), tmp, getpid());
    
    switch(version) {
	static char path[1000];
	static char buffer[200];
    case pgp2:	
	{
	    char * pgp2_path_val = give_dt_estr_as_str(&pgp2_path_e, "pgp2");
	    if (! pgp2_path_val)
		return;
	    argv[argc++] = pgp2_path_val;
	}
	argv[argc++] = "+verbose=0";
	argv[argc++] = "-kxa";
	argv[argc++] = pgpkey;
	argv[argc++] = tmpfil;

	break;
    case pgp5: 
	{
	    /* give_dt_estr_as_str adds / to end */
	    char * pgp5_dir_val  = give_dt_estr_as_str(&pgp5_dir_e, 
						       "pgp5-dir");
	    if (! pgp5_dir_val)
		return;
	    elm_sfprintf(path, sizeof path,FRM("%spgpk"),pgp5_dir_val);
	}
	argv[argc++] = path;
	argv[argc++] = "+verbose=0";
	argv[argc++] = "-x";
	argv[argc++] = pgpkey;
	argv[argc++] = "-o";
	argv[argc++] = tmpfil;
	break;
    case gpg:	
	{
	    char * gpg_path_val = give_dt_estr_as_str(&gpg_path_e, "gpg");
	    if (! gpg_path_val) 
		return;	    
	    argv[argc++] = gpg_path_val;
	}

	argv[argc++] = "--no-verbose";
	argv[argc++] = "--armor";
	argv[argc++] = "--output";
	argv[argc++] = tmpfil;
	argv[argc++] = "--export";
	elm_sfprintf(buffer, sizeof buffer,FRM("<%s>"),pgpkey);
	argv[argc++] = buffer;
	break;
    }
    argv[argc++] = NULL;
    
    ret = start_run(&RS,SY_NOTTY,argv,-1,-1);
    
    if (ret) {
	int exit_code;
	ret = run_already_done(&RS,&exit_code);
	if (0 == ret) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet,ElmRunningPgp,
			      "Running PGP..."));	    
	    ret = wait_end(&RS,&exit_code);
	}
	if (ret < 0) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet,ElmFailSignal,
			      "%.30s fail: Signal?"),
		      argv[0]);
	    menu_trigger_redraw(page);
	    return;
	} else if (ret > 0) {
	    if (RS.save_errno) {
		lib_error(CATGETS(elm_msg_cat, ElmSet,ElmFailErrno,
				  "Failed: %.30s: %.40s"),
			  argv[0],error_description(RS.save_errno));

		menu_trigger_redraw(page);
		return;
	    } else if (exit_code) {	
		lib_error(CATGETS(elm_msg_cat, ElmSet,ElmPgpErrorStatus,
				  "Pgp returned error status %d"),
			  exit_code);
		menu_trigger_redraw(page);
		return;
	    } else {
		lib_error(CATGETS(elm_msg_cat, ElmSet,ElmPgpDone,
				  "Running PGP... Done."));
		strfcpy(included_file,tmpfil,sizeof included_file);
	    }
	} else {
	    lib_error(CATGETS(elm_msg_cat, ElmSet,ElmLostErrno,
			      "%.30s lost: %.40s"),
		      argv[0],error_description(RS.save_errno));
	    menu_trigger_redraw(page);
	    return;
	}	
    } else {
	if (RS.save_errno)
	    lib_error(CATGETS(elm_msg_cat, ElmSet,ElmFailErrno,
			      "Failed: %.30s: %.40s"),
		      argv[0],error_description(RS.save_errno));
      else
	  lib_error(CATGETS(elm_msg_cat, ElmSet,ElmCantStart,
			    "Can't start %.30s"),
		    argv[0]);
	menu_trigger_redraw(page);
	return;
    }
    
    /* set the default subject for this message */
    elm_sfprintf(subj, sizeof subj,
		 CATGETS(elm_msg_cat, ElmSet, ElmPgpPublicKey,
		       "PGP public key for %s"), 
		 pgpkey);
    
    pgp_status = PGP_PUBLIC_KEY;
    
    /* Now send the message off! */
    send_msg_l(-1,NULL, NULL, subj, 0, 0,  mailbox, aview,
	       page, 
	       prompt_area /* XXX Redraw will be triggered anyway to page*/);
    
    unlink (included_file); /* make sure to clean up. */
    included_file[0] = '\0';
    pgp_status = 0; /* reset */

    menu_trigger_redraw(page);
    return;
}

void pgp_extract_public_key (hdr, infile, page)
     struct header_rec  *hdr;
     FILE *infile;
     struct menu_context *page;
{
    /* If redraw is needed, use
           menu_trigger_redraw(page)
    */

    char tempfile[STRING];
    struct run_state RS;
    CONST char *argv[20];
    int argc = 0;
    FILE *fpout;
    int res;
    enum pgp_version v = (enum pgp_version) give_dt_enumerate_as_int(&send_pgp_version);
    enum pgp_version version;
    char *    tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");

    if (!tmp)
	return;

    if (!hdr)
	return;
    
    if (!(version = have_pgp(v)))
	return;
    
    elm_sfprintf(tempfile,sizeof tempfile,
		 FRM("%selm.%d"),tmp,getpid());
    fpout=safeopen_rdwr(tempfile);
    if (!fpout) {
	lib_error(CATGETS(elm_msg_cat, ElmSet,ElmOpenTmpWriting,
			  "Could not open temp file %.30s for writing!"), 
		  tempfile);
	return;
    }
    if (!copy_message_f(infile,hdr,
			"",fpout,0,NULL)) {
	fclose(fpout);
	return;
    }
    fclose(fpout);
    
    switch(version) {
	static char path[1000];
    case pgp2:	
	{
	    char * pgp2_path_val = give_dt_estr_as_str(&pgp2_path_e, "pgp2");
	    if (! pgp2_path_val)
		return;

	    argv[argc++] = pgp2_path_val;
	}
	argv[argc++] = "+verbose=0";
	argv[argc++] = "-ka";
	argv[argc++] = tempfile;
	break;
    case pgp5:	
	{
	    /* give_dt_estr_as_str adds / to end */
	    char * pgp5_dir_val  = give_dt_estr_as_str(&pgp5_dir_e, 
						       "pgp5-dir");

	    if (!pgp5_dir_val)
		return;

	    elm_sfprintf(path, sizeof path,FRM("%spgpk"),pgp5_dir_val);
	}
	argv[argc++] = path;
	argv[argc++] = "+verbose=0";
	argv[argc++] = "-a";
	argv[argc++] = tempfile;
	break;
    case gpg:
	{
	    char * gpg_path_val = give_dt_estr_as_str(&gpg_path_e, "gpg");
	    if (! gpg_path_val) 
		return;
	    
	    argv[argc++] = gpg_path_val;

	}
	argv[argc++] = "--no-verbose";
	argv[argc++] = "--import";
	argv[argc++] = tempfile;
	break;
    }
    argv[argc++] = NULL;
    
    res=start_run(&RS,SY_CLRWAIT,argv,-1,-1);
    if (res) {
	int exit_code;
	wait_end(&RS,&exit_code);
    }
    unlink(tempfile);

    menu_trigger_redraw(page);
    return;
}

#endif /* USE_PGP */


/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1