Plan 9 from Bell Labs’s /usr/web/sources/contrib/btdn/src/pgp/826228074/pgp.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/* #define TEMP_VERSION / * if defined, temporary experimental
   		            version of PGP */
/* pgp.c -- main module for PGP.
   PGP: Pretty Good(tm) Privacy - public key cryptography for the masses.

   Synopsis:  PGP uses public-key encryption to protect E-mail. 
   Communicate securely with people you've never met, with no secure
   channels needed for prior exchange of keys.  PGP is well featured and
   fast, with sophisticated key management, digital signatures, data
   compression, and good ergonomic design.

   The original PGP version 1.0 was written by Philip Zimmermann, of
   Phil's Pretty Good(tm) Software.  Many parts of later versions of 
   PGP were developed by an international collaborative effort, 
   involving a number of contributors, including major efforts by:
   Branko Lankester <[email protected]>
   Hal Finney <[email protected]>
   Peter Gutmann <[email protected]>
   Other contributors who ported or translated or otherwise helped include:
   Jean-loup Gailly in France
   Hugh Kennedy in Germany
   Lutz Frank in Germany
   Cor Bosman in The Netherlands
   Felipe Rodriquez Svensson in The Netherlands
   Armando Ramos in Spain
   Miguel Angel Gallardo Ortiz in Spain
   Harry Bush and Maris Gabalins in Latvia
   Zygimantas Cepaitis in Lithuania
   Alexander Smishlajev
   Peter Suchkow and Andrew Chernov in Russia
   David Vincenzetti in Italy
   ...and others.


   (c) Copyright 1990-1994 by Philip Zimmermann.  All rights reserved.
   The author assumes no liability for damages resulting from the use
   of this software, even if the damage results from defects in this
   software.  No warranty is expressed or implied.

   Note that while most PGP source modules bear Philip Zimmermann's
   copyright notice, many of them have been revised or entirely written
   by contributors who frequently failed to put their names in their
   code.  Code that has been incorporated into PGP from other authors
   was either originally published in the public domain or is used with
   permission from the various authors.

   PGP is available for free to the public under certain restrictions.
   See the PGP User's Guide (included in the release package) for
   important information about licensing, patent restrictions on
   certain algorithms, trademarks, copyrights, and export controls.


   Philip Zimmermann may be reached at:
   Boulder Software Engineering
   3021 Eleventh Street
   Boulder, Colorado 80304  USA
   (303) 541-0140  (voice or FAX)
   email:  [email protected]


   PGP will run on MSDOS, Sun Unix, VAX/VMS, Ultrix, Atari ST, 
   Commodore Amiga, and OS/2.  Note:  Don't try to do anything with 
   this source code without looking at the PGP User's Guide.

   PGP combines the convenience of the Rivest-Shamir-Adleman (RSA)
   public key cryptosystem with the speed of fast conventional
   cryptographic algorithms, fast message digest algorithms, data
   compression, and sophisticated key management.  And PGP performs 
   the RSA functions faster than most other software implementations.  
   PGP is RSA public key cryptography for the masses.

   Uses RSA Data Security, Inc. MD5 Message Digest Algorithm
   as a hash for signatures.  Uses the ZIP algorithm for compression.
   Uses the ETH IPES/IDEA algorithm for conventional encryption.

   PGP generally zeroes its used stack and memory areas before exiting.
   This avoids leaving sensitive information in RAM where other users
   could find it later.  The RSA library and keygen routines also
   sanitize their own stack areas.  This stack sanitizing has not been
   checked out under all the error exit conditions, when routines exit
   abnormally.  Also, we must find a way to clear the C I/O library
   file buffers, the disk buffers, and and cache buffers.

   Revisions:
   Version 1.0 -  5 Jun 91
   Version 1.4 - 19 Jan 92
   Version 1.5 - 12 Feb 92
   Version 1.6 - 24 Feb 92
   Version 1.7 - 29 Mar 92
   Version 1.8 - 23 May 92
   Version 2.0 -  2 Sep 92
   Version 2.1 -  6 Dec 92
   Version 2.2 -  6 Mar 93
   Version 2.3 - 13 Jun 93
   Version 2.3a-  1 Jul 93
   Version 2.4 -  6 Nov 93
   Version 2.5 -  5 May 94
   Version 2.6 - 22 May 94
   Version 2.6.1 - 29 Aug 94
   Version 2.6.2 - 11 Oct 94

   Modified: 12-Nov-92 HAJK
   Add FDL stuff for VAX/VMS local mode. 
 */


#include <ctype.h>
#ifndef AMIGA
#include <signal.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef __QNX__
#include <sys/stat.h>
#endif

#include "system.h"
#include "mpilib.h"
#include "random.h"
#include "crypto.h"
#include "fileio.h"
#include "keymgmt.h"
#include "language.h"
#include "pgp.h"
#include "exitpgp.h"
#include "charset.h"
#include "getopt.h"
#include "config.h"
#include "keymaint.h"
#include "keyadd.h"
#include "rsaglue.h"
#ifdef  M_XENIX
char *strstr();
long time();
#endif

#ifdef MSDOS
#ifdef __ZTC__			/* Extend stack for Zortech C */
unsigned _stack_ = 24 * 1024;
#endif
#ifdef __TURBOC__
unsigned _stklen = 24 * 1024;
#endif
#endif
#define	STACK_WIPE	4096

/* Global filenames and system-wide file extensions... */
char rel_version[] = _LANG("2.6.2");	/* release version */
static char rel_date[] = "11 Oct 94";	/* release date */
char PGP_EXTENSION[] = ".pgp";
char ASC_EXTENSION[] = ".asc";
char SIG_EXTENSION[] = ".sig";
char BAK_EXTENSION[] = ".bak";
static char HLP_EXTENSION[] = ".hlp";
char CONSOLE_FILENAME[] = "_CONSOLE";
static char HELP_FILENAME[] = "pgp.hlp";

/* These files use the environmental variable PGPPATH as a default path: */
char globalPubringName[MAX_PATH];
char globalSecringName[MAX_PATH];
char globalRandseedName[MAX_PATH];
char globalCommentString[128];

/* Flags which are global across the driver code files */
boolean verbose = FALSE;	/* -l option: display maximum information */
FILE *pgpout;			/* Place for routine messages */

static void usage(void);
static void key_usage(void);
static void arg_error(void);
static void initsigs(void);
static int do_keyopt(char);
static int do_decrypt(char *);
static void do_armorfile(char *);


/* Various compression signatures: PKZIP, Zoo, GIF, Arj, and HPACK.
   Lha(rc) is handled specially in the code; it is missing from the
   compressSig structure intentionally.  If more formats are added,
   put them before lharc to keep the code consistent.
 */
static char *compressSig[] =
{"PK\03\04", "ZOO ", "GIF8", "\352\140",
 "HPAK", "\037\213", "\037\235", "\032\013", "\032HP%"
	/* lharc is special, must be last */ };
static char *compressName[] =
{"PKZIP", "Zoo", "GIF", "Arj",
 "Hpack", "gzip", "compressed", "PAK", "Hyper",
 "LHarc"};
static char *compressExt[] =
{".zip", ".zoo", ".gif", ".arj",
 ".hpk", ".gz", ".Z", ".pak", ".hyp",
 ".lzh"};

/* "\032\0??", "ARC", ".arc" */

/* Returns file signature type from a number of popular compression formats
   or -1 if no match */
int compressSignature(byte * header)
{
    int i;

    for (i = 0; i < sizeof(compressSig) / sizeof(*compressSig); i++)
	if (!strncmp((char *) header, compressSig[i], strlen(compressSig[i])))
	    return i;
    /* Special check for lharc files */
    if (header[2] == '-' && header[3] == 'l' &&
	(header[4] == 'z' || header[4] == 'h') &&
	header[6] == '-')
	return i;
    return -1;
}				/* compressSignature */

/* returns TRUE iff file is likely to be compressible */
static boolean file_compressible(char *filename)
{
    byte header[8];
    get_header_info_from_file(filename, header, 8);
    if (compressSignature(header) >= 0)
	return FALSE;		/* probably not compressible */
    return TRUE;		/* possibly compressible */
}				/* compressible */


/* Possible error exit codes - not all of these are used.  Note that we
   don't use the ANSI EXIT_SUCCESS and EXIT_FAILURE.  To make things
   easier for compilers which don't support enum we use #defines */

#define EXIT_OK				0
#define INVALID_FILE_ERROR		1
#define FILE_NOT_FOUND_ERROR		2
#define UNKNOWN_FILE_ERROR		3
#define NO_BATCH			4
#define BAD_ARG_ERROR			5
#define INTERRUPT			6
#define OUT_OF_MEM			7

/* Keyring errors: Base value = 10 */
#define KEYGEN_ERROR			10
#define NONEXIST_KEY_ERROR		11
#define KEYRING_ADD_ERROR		12
#define KEYRING_EXTRACT_ERROR		13
#define KEYRING_EDIT_ERROR		14
#define KEYRING_VIEW_ERROR		15
#define KEYRING_REMOVE_ERROR		16
#define KEYRING_CHECK_ERROR		17
#define KEY_SIGNATURE_ERROR		18
#define KEYSIG_REMOVE_ERROR		19

/* Encode errors: Base value = 20 */
#define SIGNATURE_ERROR			20
#define RSA_ENCR_ERROR			21
#define ENCR_ERROR			22
#define COMPRESS_ERROR			23

/* Decode errors: Base value = 30 */
#define SIGNATURE_CHECK_ERROR		30
#define RSA_DECR_ERROR			31
#define DECR_ERROR			32
#define DECOMPRESS_ERROR		33


#ifdef SIGINT

/* This function is called if a BREAK signal is sent to the program.  In this
   case we zap the temporary files.
 */
void breakHandler(int sig)
{
#ifdef UNIX
    if (sig == SIGPIPE) {
	signal(SIGPIPE, SIG_IGN);
	exitPGP(INTERRUPT);
    }
    if (sig != SIGINT)
	fprintf(stderr, "\nreceived signal %d\n", sig);
    else
#endif
	fprintf(pgpout, LANG("\nStopped at user request\n"));
    exitPGP(INTERRUPT);
}
#endif

/* Clears screen and homes the cursor. */
static void clearscreen(void)
{
    fprintf(pgpout, "\n\033[0;0H\033[J\r           \r");  /* ANSI sequence. */
    fflush(pgpout);
}

/* We had to process the config file first to possibly select the 
   foreign language to translate the sign-on line that follows... */
static void signon_msg(void)
{    
    word32 tstamp;
    /* display message only once to allow calling multiple times */
    static boolean printed = FALSE;

    if (quietmode || printed)
	return;
    printed = TRUE;
    fprintf(stderr,
LANG("Pretty Good Privacy(tm) %s - Public-key encryption for the masses.\n"),
	    rel_version);
#ifdef TEMP_VERSION
    fputs(
"Internal development version only - not for general release.\n", stderr);
#endif
    fprintf(stderr,
LANG("(c) 1990-1994 Philip Zimmermann, Phil's Pretty Good Software. %s\n"),
	    rel_date);
    fputs(LANG(signon_legalese), stderr);
    fputs(
LANG("Export of this software may be restricted by the U.S. government.\n"),
	  stderr);

    get_timestamp((byte *) & tstamp);	/* timestamp points to tstamp */
    fprintf(pgpout, "Current time: %s\n", ctdate(&tstamp));
}


#ifdef TEMP_VERSION		/* temporary experimental version of PGP */
#include <time.h>
#define CREATION_DATE 0x2DC079A4ul
				/* CREATION_DATE is
				   Fri Apr 29 03:06:12 1994 UTC */
#define LIFESPAN	((unsigned long) 60L * (unsigned long) 86400L)
				/* LIFESPAN is 60 days */

/* If this is an experimental version of PGP, cut its life short */
void check_expiration_date(void)
{
    if (get_timestamp(NULL) > (CREATION_DATE + LIFESPAN)) {
	fprintf(stderr,
		"\n\007This experimental version of PGP has expired.\n");
	exit(-1);		/* error exit */
    }
}				/* check_expiration_date */
#else				/* no expiration date */
#define check_expiration_date()	/* null statement */
#endif				/* TEMP_VERSION */

/* -f means act as a unix-style filter */
/* -i means internalize extended file attribute information, only supported
 *          between like (or very compatible) operating systems. */
/* -l means show longer more descriptive diagnostic messages */
/* -m means display plaintext output on screen, like unix "more" */
/* -d means decrypt only, leaving inner signature wrapping intact */
/* -t means treat as pure text and convert to canonical text format */

/* Used by getopt function... */
#define OPTIONS "abcdefghiklmo:prstu:vwxz:ABCDEFGHIKLMO:PRSTU:VWX?"
extern int optind;
extern char *optarg;

boolean emit_radix_64 = FALSE;	/* set by config file */
static boolean sign_flag = FALSE;
boolean moreflag = FALSE;
boolean filter_mode = FALSE;
static boolean preserve_filename = FALSE;
static boolean decrypt_only_flag = FALSE;
static boolean de_armor_only = FALSE;
static boolean strip_sig_flag = FALSE;
boolean clear_signatures = TRUE;
boolean strip_spaces;
static boolean c_flag = FALSE;
static boolean u_flag = FALSE;		/* Did I get my_name from -u? */
boolean encrypt_to_self = FALSE; /* should I encrypt messages to myself? */
boolean batchmode = FALSE;	/* if TRUE: don't ask questions */
boolean quietmode = FALSE;
boolean force_flag = FALSE;	/* overwrite existing file without asking */
#ifdef VMS			/* kludge for those stupid VMS variable-length
				   text records */
char literal_mode = MODE_TEXT;	/* MODE_TEXT or MODE_BINARY for literal
				   packet */
#else				/* not VMS */
char literal_mode = MODE_BINARY; /* MODE_TEXT or MODE_BINARY for literal
				    packet */
#endif				/* not VMS */
/* my_name is substring of default userid for secret key to make signatures */
char my_name[256] = "\0";	/* null my_name means take first userid
				   in ring */
boolean keepctx = FALSE;	/* TRUE means keep .ctx file on decrypt */
/* Ask for each key separately if it should be added to the keyring */
boolean interactive_add = FALSE;
boolean compress_enabled = TRUE; /* attempt compression before encryption */
long timeshift = 0L;		/* seconds from GMT timezone */
int version_byte = VERSION_BYTE_NEW;
boolean nomanual = 0;
/* If non-zero, initialize file to this many random bytes */
int makerandom = 0;


static char *outputfile = NULL;
static int errorLvl = EXIT_OK;
static char mcguffin[256];	/* userid search tag */
boolean signature_checked = FALSE;
char plainfile[MAX_PATH];
int myArgc = 2;
char **myArgv;
struct hashedpw *passwds = 0, *keypasswds = 0;
static struct hashedpw **passwdstail = &passwds;

int main(int argc, char *argv[])
{
    int status, opt;
    char *inputfile = NULL;
    char **recipient = NULL;
    char **mcguffins;
    char *workfile, *tempf;
    boolean nestflag = FALSE;
    boolean decrypt_mode = FALSE;
    boolean wipeflag = FALSE;
    boolean armor_flag = FALSE;	/* -a option */
    boolean separate_signature = FALSE;
    boolean keyflag = FALSE;
    boolean encrypt_flag = FALSE;
    boolean conventional_flag = FALSE;
    boolean attempt_compression; /* attempt compression before encryption */
    boolean output_stdout;	/* Output goes to stdout */
    char *clearfile = NULL;
    char *literal_file = NULL;
    char literal_file_name[MAX_PATH];
    char cipherfile[MAX_PATH];
    char keychar = '\0';
    char *p;
    byte ctb;
    struct hashedpw *hpw;

    /* Initial messages to stderr */
    pgpout = stderr;

#ifdef	DEBUG1
    verbose = TRUE;
#endif
    /* The various places one can get passwords from.
     * We accumulate them all into two lists.  One is
     * to try on keys only, and is stored in no particular
     * order, while the other is of unknown purpose so
     * far (they may be used for conventional encryption
     * or decryption as well), and are kept in a specific
     * order.  If any password in the general list is found
     * to decode a key, it is moved to the key list.
     * The general list is not grown after initialization,
     * so the tail pointer is not used after this.
     */

    if ((p = getenv("PGPPASS")) != NULL) {
	hpw = xmalloc(sizeof(struct hashedpw));
	hashpass(p, strlen(p), hpw->hash);
	/* Add to linked list of key passwords */
	hpw->next = keypasswds;
	keypasswds = hpw;
    }
    /* The -z "password" option should be used instead of PGPPASS if
     * the environment can be displayed with the ps command (eg. BSD).
     * If the system does not allow overwriting of the command line
     * argument list but if it has a "hidden" environment, PGPPASS
     * should be used.
     */
    for (opt = 1; opt < argc; ++opt) {
	p = argv[opt];
	if (p[0] != '-' || p[1] != 'z')
	    continue;
	/* Accept either "-zpassword" or "-z password" */
	p += 2;
	if (!*p)
	    p = argv[++opt];
	/* p now points to password */
	if (!p)
	    break;		/* End of arg list - ignore */
	hpw = xmalloc(sizeof(struct hashedpw));
	hashpass(p, strlen(p), hpw->hash);
	/* Wipe password */
	while (*p)
	    *p++ = ' ';
	/* Add to tail of linked list of passwords */
	hpw->next = 0;
	*passwdstail = hpw;
	passwdstail = &hpw->next;
    }
    /*
     * If PGPPASSFD is set in the environment try to read the password
     * from this file descriptor.  If you set PGPPASSFD to 0 pgp will
     * use the first line read from stdin as password.
     */
    if ((p = getenv("PGPPASSFD")) != NULL) {
	int passfd;
	if (*p && (passfd = atoi(p)) >= 0) {
	    char pwbuf[256];
	    p = pwbuf;
	    while (read(passfd, p, 1) == 1 && *p != '\n')
		++p;
	    hpw = xmalloc(sizeof(struct hashedpw));
	    hashpass(pwbuf, p - pwbuf, hpw->hash);
	    memset(pwbuf, 0, p - pwbuf);
	    /* Add to tail of linked list of passwords */
	    hpw->next = 0;
	    *passwdstail = hpw;
	    passwdstail = &hpw->next;
	}
    }
    /* Process the config file.  The following override each other:
       - Hard-coded defualts
       - The system config file
       - Hard-coded defaults for security-critical things
       - The user's config file
       - Environment variables
       - Command-line options.
     */
    opt = 0;			/* Number of config files read */
#ifdef PGP_SYSTEM_DIR
    strcpy(mcguffin, PGP_SYSTEM_DIR);
    strcat(mcguffin, "config.txt");
    if (access(mcguffin, 0) == 0) {
	opt++;
	/*
	 * Note: errors here are NOT fatal, so that people
	 * can use PGP with a corrputed system file.
	 */
	processConfigFile(mcguffin);
    }
#endif

    /*
     * These must be personal; the system config file may not
     * influence them.
     */
    buildfilename(globalPubringName, "pubring.pgp");
    buildfilename(globalSecringName, "secring.pgp");
    buildfilename(globalRandseedName, "randseed.bin");
    my_name[0] = '\0';

    /* Process the config file first.  Any command-line arguments will
       override the config file settings */
#if defined(UNIX) || defined(MSDOS) || defined(OS2)
    /* Try "pgp.ini" on MS-DOS or ".pgprc" on Unix */
#ifdef UNIX
    buildfilename(mcguffin, ".pgprc");
#else
    buildfilename(mcguffin, "pgp.ini");
#endif
    if (access(mcguffin, 0) != 0)
	buildfilename(mcguffin, "config.txt");
#else
    buildfilename(mcguffin, "config.txt");
#endif
    if (access(mcguffin, 0) == 0) {
	opt++;
	if (processConfigFile(mcguffin) < 0)
	    exit(BAD_ARG_ERROR);
    }
    if (!opt)
	fprintf(pgpout, LANG("\007No configuration file found.\n"));

    init_charset();

#ifdef MSDOS			/* only on MSDOS systems */
    if ((p = getenv("TZ")) == NULL || *p == '\0') {
	fprintf(pgpout,LANG("\007WARNING: Environmental variable TZ is not \
defined, so GMT timestamps\n\
may be wrong.  See the PGP User's Guide to properly define TZ\n\
in AUTOEXEC.BAT file.\n"));
    }
#endif				/* MSDOS */

#ifdef VMS
#define TEMP "SYS$SCRATCH"
#else
#define TEMP "TMP"
#endif				/* VMS */
    if ((p = getenv(TEMP)) != NULL && *p != '\0')
	settmpdir(p);

    if ((myArgv = (char **) malloc((argc + 2) * sizeof(char **))) == NULL) {
	fprintf(stderr, LANG("\n\007Out of memory.\n"));
	exitPGP(7);
    }
    myArgv[0] = NULL;
    myArgv[1] = NULL;

    /* Process all the command-line option switches: */
    while (optind < argc) {
	/*
	 * Allow random order of options and arguments (like GNU getopt)
	 * NOTE: this does not work with GNU getopt, use getopt.c from
	 * the PGP distribution.
	 */
	if ((opt = pgp_getopt(argc, argv, OPTIONS)) == EOF) {
	    if (optind == argc)	/* -- at end */
		break;
	    myArgv[myArgc++] = argv[optind++];
	    continue;
	}
	opt = to_lower(opt);
	if (keyflag && (keychar == '\0' || (keychar == 'v' && opt == 'v'))) {
	    if (keychar == 'v')
		keychar = 'V';
	    else
		keychar = opt;
	    continue;
	}
	switch (opt) {
	case 'a':
	    armor_flag = TRUE;
	    emit_radix_64 = 1;
	    break;
	case 'b':
	    separate_signature = strip_sig_flag = TRUE;
	    break;
	case 'c':
	    encrypt_flag = conventional_flag = TRUE;
	    c_flag = TRUE;
	    break;
	case 'd':
	    decrypt_only_flag = TRUE;
	    break;
	case 'e':
	    encrypt_flag = TRUE;
	    break;
	case 'f':
	    filter_mode = TRUE;
	    break;
	case '?':
	case 'h':
	    usage();
	    break;
#ifdef VMS
	case 'i':
	    literal_mode = MODE_LOCAL;
	    break;
#endif				/* VMS */
	case 'k':
	    keyflag = TRUE;
	    break;
	case 'l':
	    verbose = TRUE;
	    break;
	case 'm':
	    moreflag = TRUE;
	    break;
	case 'p':
	    preserve_filename = TRUE;
	    break;
	case 'o':
	    outputfile = optarg;
	    break;
	case 's':
	    sign_flag = TRUE;
	    break;
	case 't':
	    literal_mode = MODE_TEXT;
	    break;
	case 'u':
	    strncpy(my_name, optarg, sizeof(my_name) - 1);
	    CONVERT_TO_CANONICAL_CHARSET(my_name);
	    u_flag = TRUE;
	    break;
	case 'w':
	    wipeflag = TRUE;
	    break;
	case 'z':
	    break;
	    /* '+' special option: does not require - */
	case '+':
	    if (processConfigLine(optarg) == 0)
		break;
	    fprintf(stderr, "\n");
	    /* fallthrough */
	default:
	    arg_error();
	}
    }
    myArgv[myArgc] = NULL;	/* Just to make it NULL terminated */

    if (keyflag && keychar == '\0')
	key_usage();

    signon_msg();
    check_expiration_date();	/* hobble any experimental version */

    /*
     * Write to stdout if explicitly asked to, or in filter mode and
     * no explicit file name was given.
     */
    output_stdout = outputfile ? strcmp(outputfile, "-")  == 0 : filter_mode;

#if 1
    /* At request of Peter Simons, use stderr always. Sounds reasonable. */
    /* JIS: Put this code back in... removing it broke too many things */
    if (!output_stdout)
	pgpout = stdout;
#endif


#if defined(UNIX) || defined(VMS)
    umask(077);			/* Make files default to private */
#endif

    initsigs();			/* Catch signals */
    noise();			/* Start random number generation */

    if (keyflag) {
	status = do_keyopt(keychar);
	if (status < 0)
	    user_error();
	exitPGP(status);
    }
    /* -db means break off signature certificate into separate file */
    if (decrypt_only_flag && strip_sig_flag)
	decrypt_only_flag = FALSE;

    if (decrypt_only_flag && armor_flag)
	decrypt_mode = de_armor_only = TRUE;

    if (outputfile != NULL)
	preserve_filename = FALSE;

    if (!sign_flag && !encrypt_flag && !conventional_flag && !armor_flag) {
	if (wipeflag) {		/* wipe only */
	    if (myArgc != 3)
		arg_error();	/* need one argument */
	    if (wipefile(myArgv[2]) == 0 && remove(myArgv[2]) == 0) {
		fprintf(pgpout,
			LANG("\nFile %s wiped and deleted. "), myArgv[2]);
		fprintf(pgpout, "\n");
		exitPGP(EXIT_OK);
	    }
	    exitPGP(UNKNOWN_FILE_ERROR);
	}
	/* decrypt if none of the -s -e -c -a -w options are specified */
	decrypt_mode = TRUE;
    }
    if (myArgc == 2) {		/* no arguments */
#ifdef UNIX
	if (!filter_mode && !isatty(fileno(stdin))) {
	    /* piping to pgp without arguments and no -f:
	     * switch to filter mode but don't write output to stdout
	     * if it's a tty, use the preserved filename */
	    if (!moreflag)
		pgpout = stderr;
	    filter_mode = TRUE;
	    if (isatty(fileno(stdout)) && !moreflag)
		preserve_filename = TRUE;
	}
#endif
	if (!filter_mode) {
	    if (quietmode) {
		quietmode = FALSE;
		signon_msg();
	    }
	    fprintf(pgpout,
LANG("\nFor details on licensing and distribution, see the PGP User's Guide.\
\nFor other cryptography products and custom development services, contact:\
\nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, \
phone +1 303 541-0140\n"));
	    if (strcmp((p = LANG("@translator@")), "@translator@"))
		fprintf(pgpout, p);
	    fprintf(pgpout, LANG("\nFor a usage summary, type:  pgp -h\n"));
	    exit(BAD_ARG_ERROR);	/* error exit */
	}
    } else {
	if (filter_mode) {
	    recipient = &myArgv[2];
	} else {
	    inputfile = myArgv[2];
	    recipient = &myArgv[3];
	}
    }


    if (filter_mode) {
	inputfile = "stdin";
    } else if (makerandom > 0) {	/* Create the input file */
	/*
	 * +makerandom=<bytes>: Create an input file consisting of <bytes>
	 * cryptographically strong random bytes, before applying the
	 * encryption options of PGP.  This is an advanced option, so
	 * assume the user knows what he's doing and don't bother about
	 * overwriting questions.  E.g.
	 * pgp +makerandom=24 foofile
	 *	Create "foofile" with 24 random bytes in it.
	 * pgp +makerandom=24 -ea foofile recipient
	 *	The same, but also encrypt it to "recipient", creating
	 *	foofile.asc as well.
	 * This feature was created to allow PGP to create and send keys
	 * around for other applications to use.
	 */
	status = cryptRandWriteFile(inputfile, (struct IdeaCfbContext *)0,
	                       (unsigned)makerandom);
	if (status < 0) {
		fprintf(stderr,"Error writing file \"%s\"\n",inputfile);
		exitPGP(INVALID_FILE_ERROR);
	}
	fprintf(pgpout, LANG("File %s created containing %d random bytes.\n"),
		inputfile, makerandom);
	/* If we aren't encrypting, don't bother trying to decrypt this! */
	if (decrypt_mode)
		exitPGP(EXIT_OK);

	/* This is obviously NOT a text file */
	literal_mode = MODE_BINARY;
    } else {
	if (decrypt_mode && no_extension(inputfile)) {
	    strcpy(cipherfile, inputfile);
	    force_extension(cipherfile, ASC_EXTENSION);
	    if (file_exists(cipherfile)) {
		inputfile = cipherfile;
	    } else {
		force_extension(cipherfile, PGP_EXTENSION);
		if (file_exists(cipherfile)) {
		    inputfile = cipherfile;
		} else {
		    force_extension(cipherfile, SIG_EXTENSION);
		    if (file_exists(cipherfile))
			inputfile = cipherfile;
		}
	    }
	}
	if (!file_exists(inputfile)) {
	    fprintf(pgpout,
		    LANG("\007File [%s] does not exist.\n"), inputfile);
	    errorLvl = FILE_NOT_FOUND_ERROR;
	    user_error();
	}
    }

    if (strlen(inputfile) >= (unsigned) MAX_PATH - 4) {
	fprintf(pgpout, 
		LANG("\007Invalid filename: [%s] too long\n"), inputfile);
	errorLvl = INVALID_FILE_ERROR;
	user_error();
    }
    strcpy(plainfile, inputfile);

    if (filter_mode) {
	setoutdir(NULL);	/* NULL means use tmpdir */
    } else {
	if (outputfile)
	    setoutdir(outputfile);
	else
	    setoutdir(inputfile);
    }

    if (filter_mode) {
	workfile = tempfile(TMP_WIPE | TMP_TMPDIR);
	readPhantomInput(workfile);
    } else {
	workfile = inputfile;
    }

    get_header_info_from_file(workfile, &ctb, 1);
    if (decrypt_mode) {
	strip_spaces = FALSE;
	if (!is_ctb(ctb) && is_armor_file(workfile, 0L))
	    do_armorfile(workfile);
	else if (do_decrypt(workfile) < 0)
	    user_error();
	if (batchmode && !signature_checked)
	    exitPGP(1);		/* alternate success, file did not have sig. */
	else
	    exitPGP(EXIT_OK);
    }
    /*
     * See if plaintext input file was actually created by PGP earlier--
     * If it was, maybe we should NOT encapsulate it in a literal packet.
     * (nestflag = TRUE).  Otherwise, always encapsulate it (default).
     * (Why test for filter_mode???)
     */
    if (!batchmode && !filter_mode && legal_ctb(ctb)) {
	/*      Special case--may be a PGP-created packet, so
	   do we inhibit encapsulation in literal packet? */
	fprintf(pgpout,
LANG("\n\007Input file '%s' looks like it may have been created by PGP. "),
		inputfile);
	fprintf(pgpout,
LANG("\nIs it safe to assume that it was created by PGP (y/N)? "));
	nestflag = getyesno('n');
    } else if (force_flag && makerandom == 0 && legal_ctb(ctb)) {
	nestflag = TRUE;
    }

    if (moreflag && makerandom == 0) {
	/* special name to cause printout on decrypt */
	strcpy(literal_file_name, CONSOLE_FILENAME);
	literal_mode = MODE_TEXT;	/* will check for text file later */
    } else {
	strcpy(literal_file_name, file_tail(inputfile));
#ifdef MSDOS
	strlwr(literal_file_name);
#endif
    }
    literal_file = literal_file_name;

    /*      Make sure non-text files are not accidentally converted 
       to canonical text.  This precaution should only be followed 
       for US ASCII text files, since European text files may have 
       8-bit character codes and still be legitimate text files 
       suitable for conversion to canonical (CR/LF-terminated) 
       text format. */
    if (literal_mode == MODE_TEXT && !is_text_file(workfile)) {
	fprintf(pgpout,
LANG("\nNote: '%s' is not a pure text file.\n\
File will be treated as binary data.\n"),
		workfile);
	literal_mode = MODE_BINARY;	/* now expect straight binary */
    }
    if (moreflag && literal_mode == MODE_BINARY) {
	/* For eyes only?  Can't display binary file. */
	fprintf(pgpout,
LANG("\n\007Error: Only text files may be sent as display-only.\n"));
	errorLvl = INVALID_FILE_ERROR;
	user_error();
    }

    /*  
     * See if plainfile looks like it might be incompressible, 
     * by examining its contents for compression headers for 
     * commonly-used compressed file formats like PKZIP, etc.
     * Remember this information for later, when we are deciding
     * whether to attempt compression before encryption.
     *
     * Naturally, don't bother if we are making a separate signature or
     * clear-signed message.  Also, don't bother trying to compress a
     * PGP message, as it's probably already compressed.
     */
    attempt_compression = compress_enabled && !separate_signature &&
                          !nestflag && !clearfile && makerandom == 0 &&
                          file_compressible(plainfile);

    if (sign_flag) {
	if (!filter_mode && !quietmode)
	    fprintf(pgpout,
LANG("\nA secret key is required to make a signature. "));
	if (!quietmode && my_name[0] == '\0') {
	    fprintf(pgpout,
LANG("\nYou specified no user ID to select your secret key,\n\
so the default user ID and key will be the most recently\n\
added key on your secret keyring.\n"));
	}
	strip_spaces = FALSE;
	clearfile = NULL;
	if (literal_mode == MODE_TEXT) {
	    /* Text mode requires becoming canonical */
	    tempf = tempfile(TMP_WIPE | TMP_TMPDIR);
	    /* +clear means output file with signature in the clear,
	       only in combination with -t and -a, not with -e or -b */
	    if (!encrypt_flag && !separate_signature &&
		emit_radix_64 && clear_signatures) {
		clearfile = workfile;
		strip_spaces = TRUE;
	    }
	    make_canonical(workfile, tempf);
	    if (!clearfile)
		rmtemp(workfile);
	    workfile = tempf;
	}
	if (attempt_compression || encrypt_flag || emit_radix_64 ||
	    output_stdout)
	    tempf = tempfile(TMP_WIPE | TMP_TMPDIR);
	else
	    tempf = tempfile(TMP_WIPE);
	/* for clear signatures we create a separate signature */
	status = signfile(nestflag, separate_signature || (clearfile != NULL),
		   my_name, workfile, tempf, literal_mode, literal_file);
	rmtemp(workfile);
	workfile = tempf;

	if (status < 0) {	/* signfile failed */
	    fprintf(pgpout, LANG("\007Signature error\n"));
	    errorLvl = SIGNATURE_ERROR;
	    user_error();
	}
    } else if (!nestflag) {	/* !sign_file */
	/*      Prepend CTB_LITERAL byte to plaintext file.
	   --sure wish this pass could be optimized away. */
	if (attempt_compression || encrypt_flag || emit_radix_64 ||
	    output_stdout)
	    tempf = tempfile(TMP_WIPE | TMP_TMPDIR);
	else
	    tempf = tempfile(TMP_WIPE);
	/* for clear signatures we create a separate signature */
	status = make_literal(workfile, tempf, literal_mode, literal_file);
	rmtemp(workfile);
	workfile = tempf;
    }

    if (encrypt_flag) {
        if (emit_radix_64 || output_stdout)
	    tempf = tempfile(TMP_WIPE | TMP_TMPDIR);
	else
	    tempf = tempfile(TMP_WIPE);
	if (!conventional_flag) {
	    if (!filter_mode && !quietmode)
		fprintf(pgpout,
LANG("\n\nRecipients' public key(s) will be used to encrypt. "));
	    if (recipient == NULL || *recipient == NULL ||
		**recipient == '\0') {
		/* no recipient specified on command line */
		fprintf(pgpout,
LANG("\nA user ID is required to select the recipient's public key. "));
		fprintf(pgpout, LANG("\nEnter the recipient's user ID: "));
		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
		if ((mcguffins = (char **) malloc(2 * sizeof(char *))) == NULL)
		{
		    fprintf(stderr, LANG("\n\007Out of memory.\n"));
		    exitPGP(7);
		}
		mcguffins[0] = mcguffin;
		mcguffins[1] = "";
	    } else {
		/* recipient specified on command line */
		mcguffins = recipient;
	    }
	    for (recipient = mcguffins; *recipient != NULL &&
		 **recipient != '\0'; recipient++) {
		CONVERT_TO_CANONICAL_CHARSET(*recipient);
	    }
	    status = encryptfile(mcguffins, workfile, tempf,
				 attempt_compression);
	} else {
	    status = idea_encryptfile(workfile, tempf, attempt_compression);
	}

	rmtemp(workfile);
	workfile = tempf;

	if (status < 0) {
	    fprintf(pgpout, LANG("\007Encryption error\n"));
	    errorLvl = (conventional_flag ? ENCR_ERROR : RSA_ENCR_ERROR);
	    user_error();
	} 
    } else if (attempt_compression && !separate_signature && !clearfile) {
	/*
	 * PGP used to be parsimonious about compressin; originally, it only
	 * did it for files that were being encrypted (to reduce the
	 * redundancy in the plaintext), but it should really do it for
	 * anything where it's not a bad idea.
	 */
        if (emit_radix_64 || output_stdout)
	    tempf = tempfile(TMP_WIPE | TMP_TMPDIR);
	else
	    tempf = tempfile(TMP_WIPE);
	squish_file(workfile, tempf);
	rmtemp(workfile);
	workfile = tempf;
    }

    /*
     * Write to stdout if explicitly asked to, or in filter mode and
     * no explicit file name was given.
     */
    if (output_stdout) {
	if (emit_radix_64) {
	    /* NULL for outputfile means write to stdout */
	    if (armor_file(workfile, NULL, inputfile, clearfile) != 0) {
		errorLvl = UNKNOWN_FILE_ERROR;
		user_error();
	    }
	    if (clearfile)
		rmtemp(clearfile);
	} else {
	    if (writePhantomOutput(workfile) < 0) {
		errorLvl = UNKNOWN_FILE_ERROR;
		user_error();
	    }
	}
	rmtemp(workfile);
    } else {
	char name[MAX_PATH];
	if (outputfile) {
	    strcpy(name, outputfile);
	} else {
	    strcpy(name, inputfile);
	    drop_extension(name);
	}
	if (no_extension(name)) {
	    if (emit_radix_64)
		force_extension(name, ASC_EXTENSION);
	    else if (sign_flag && separate_signature)
		force_extension(name, SIG_EXTENSION);
	    else
		force_extension(name, PGP_EXTENSION);
	}
	if (emit_radix_64) {
	    if (armor_file(workfile, name, inputfile, clearfile) != 0) {
		errorLvl = UNKNOWN_FILE_ERROR;
		user_error();
	    }
	    if (clearfile)
		rmtemp(clearfile);
	} else {
	    if ((outputfile = savetemp(workfile, name)) == NULL) {
		errorLvl = UNKNOWN_FILE_ERROR;
		user_error();
	    }
	    if (!quietmode) {
		if (encrypt_flag)
		    fprintf(pgpout,
			    LANG("\nCiphertext file: %s\n"), outputfile);
		else if (sign_flag)
		    fprintf(pgpout,
			    LANG("\nSignature file: %s\n"), outputfile);
	    }
	}
    }

    if (wipeflag) {
	/* destroy every trace of plaintext */
	if (wipefile(inputfile) == 0) {
	    remove(inputfile);
	    fprintf(pgpout, LANG("\nFile %s wiped and deleted. "), inputfile);
	    fprintf(pgpout, "\n");
	}
    }
    exitPGP(EXIT_OK);
    return 0;			/* to shut up lint and some compilers */
}				/* main */

#ifdef MSDOS
#include <dos.h>
static char *dos_errlst[] =
{
    "Write protect error",	/* LANG ("Write protect error") */
    "Unknown unit",
    "Drive not ready",		/* LANG ("Drive not ready") */
    "3", "4", "5", "6", "7", "8", "9",
    "Write error",		/* LANG ("Write error") */
    "Read error",		/* LANG ("Read error") */
    "General failure",
};

/* handler for msdos 'harderrors' */
#ifndef OS2
#ifdef __TURBOC__		/* Turbo C 2.0 */
static int dostrap(int errval)
#else
static void dostrap(unsigned deverr, unsigned errval)
#endif
{
    char errbuf[64];
    int i;
    sprintf(errbuf, "\r\nDOS error: %s\r\n", dos_errlst[errval]);
    i = 0;
    do
	bdos(2, (unsigned int) errbuf[i], 0);
    while (errbuf[++i]);
#ifdef __TURBOC__
    return 0;			/* ignore (fopen will return NULL) */
#else
    return;
#endif
}
#endif				/* MSDOS */
#endif

static void initsigs()
{
#ifdef MSDOS
#ifndef OS2
#ifdef __TURBOC__
    harderr(dostrap);
#else				/* MSC */
#ifndef __GNUC__		/* DJGPP's not MSC */
    _harderr(dostrap);
#endif
#endif
#endif
#endif				/* MSDOS */
#ifdef SIGINT
#ifdef ATARI
    signal(SIGINT, (sigfunc_t) breakHandler);
#else
    if (signal(SIGINT, SIG_IGN) != SIG_IGN)
	signal(SIGINT, breakHandler);
#if defined(UNIX) || defined(VMS)
    if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
	signal(SIGHUP, breakHandler);
    if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
	signal(SIGQUIT, breakHandler);
#ifdef UNIX
    signal(SIGPIPE, breakHandler);
#endif
    signal(SIGTERM, breakHandler);
#ifndef DEBUG
# ifdef SIGTRAP
    signal(SIGTRAP, breakHandler);
# endif
# ifdef SIGSEGV
    signal(SIGSEGV, breakHandler);
# endif
# ifdef SIGILL
    signal(SIGILL, breakHandler);
# endif
#ifdef SIGBUS
    signal(SIGBUS, breakHandler);
#endif
#endif				/* DEBUG */
#endif				/* UNIX */
#endif				/* not Atari */
#endif				/* SIGINT */
}				/* initsigs */


static void do_armorfile(char *armorfile)
{
    char *tempf;
    char cipherfile[MAX_PATH];
    long linepos = 0;
    int status;
    int success = 0;

    for (;;) {
	/* Handle transport armor stripping */
	tempf = tempfile(0);
	strip_spaces = FALSE;	/* de_armor_file() sets
				   this for clear signature files */
	status = de_armor_file(armorfile, tempf, &linepos);
	if (status) {
	    fprintf(pgpout,
LANG("\n\007Error: Transport armor stripping failed for file %s\n"),
		    armorfile);
	    errorLvl = INVALID_FILE_ERROR;
	    user_error();	/* Bad file */
	}
	if (keepctx || de_armor_only) {
	    if (outputfile && de_armor_only) {
		if (strcmp(outputfile, "-") == 0) {
		    writePhantomOutput(tempf);
		    rmtemp(tempf);
		    return;
		}
		strcpy(cipherfile, outputfile);
	    } else {
		strcpy(cipherfile, file_tail(armorfile));
		force_extension(cipherfile, PGP_EXTENSION);
	    }
	    if ((tempf = savetemp(tempf, cipherfile)) == NULL) {
		errorLvl = UNKNOWN_FILE_ERROR;
		user_error();
	    }
	    if (!quietmode)
		fprintf(pgpout,
LANG("Stripped transport armor from '%s', producing '%s'.\n"),
			armorfile, tempf);
	    /* -da flag: don't decrypt */
	    if (de_armor_only || do_decrypt(tempf) >= 0)
		++success;
	} else {
	    if (do_decrypt(tempf) >= 0)
		++success;
	    rmtemp(tempf);
	}

	if (!is_armor_file(armorfile, linepos)) {
	    if (!success)	/* print error msg if we didn't
				   decrypt anything */
		user_error();
	    return;
	}
	fprintf(pgpout,
		LANG("\nLooking for next packet in '%s'...\n"), armorfile);
    }
}				/* do_armorfile */


static int do_decrypt(char *cipherfile)
{
    char *outfile = NULL;
    int status, i;
    boolean nested_info = FALSE;
    char ringfile[MAX_PATH];
    byte ctb;
    byte header[8];		/* used to classify file type at the end. */
    char preserved_name[MAX_PATH];
    char *newname;

    /* will be set to the original file name after processing a
       literal packet */
    preserved_name[0] = '\0';

    do {			/* while nested parsable info present */
	if (nested_info) {
	    rmtemp(cipherfile);	/* never executed on first pass */
	    cipherfile = outfile;
	}
	if (get_header_info_from_file(cipherfile, &ctb, 1) < 0) {
	    fprintf(pgpout,
LANG("\n\007Can't open ciphertext file '%s'\n"), cipherfile);
	    errorLvl = FILE_NOT_FOUND_ERROR;
	    return -1;
	}
	if (!is_ctb(ctb))	/* not a real CTB -- complain */
	    break;

	if (moreflag)
	    outfile = tempfile(TMP_WIPE | TMP_TMPDIR);
	else
	    outfile = tempfile(TMP_WIPE);

	/* PKE is Public Key Encryption */
	if (is_ctb_type(ctb, CTB_PKE_TYPE)) {

	    if (!quietmode)
		fprintf(pgpout,
LANG("\nFile is encrypted.  Secret key is required to read it. "));

	    /* Decrypt to scratch file since we may have a LITERAL2 */
	    status = decryptfile(cipherfile, outfile);

	    if (status < 0) {	/* error return */
		errorLvl = RSA_DECR_ERROR;
		return -1;
	    }
	    nested_info = (status > 0);

	} else if (is_ctb_type(ctb, CTB_SKE_TYPE)) {

	    if (decrypt_only_flag) {
		/* swap file names instead of just copying the file */
		rmtemp(outfile);
		outfile = cipherfile;
		cipherfile = NULL;
		if (!quietmode)
		    fprintf(pgpout,
LANG("\nThis file has a signature, which will be left in place.\n"));
		nested_info = FALSE;
		break;		/* Do no more */
	    }
	    if (!quietmode)
		fprintf(pgpout,
LANG("\nFile has signature.  Public key is required to check signature. "));

	    status = check_signaturefile(cipherfile, outfile,
					 strip_sig_flag, preserved_name);

	    if (status < 0) {	/* error return */
		errorLvl = SIGNATURE_CHECK_ERROR;
		return -1;
	    }
	    nested_info = (status > 0);

	    if (strcmp(preserved_name, "/dev/null") == 0) {
		rmtemp(outfile);
		fprintf(pgpout, "\n");
		return 0;
	    }
	} else if (is_ctb_type(ctb, CTB_CKE_TYPE)) {

	    /* Conventional Key Encrypted ciphertext. */
	    /* Tell user it's encrypted here, and prompt for
	       password in subroutine. */
	    if (!quietmode)
		fprintf(pgpout, LANG("\nFile is conventionally encrypted.  "));
	    /* Decrypt to scratch file since it may be a LITERAL2 */
	    status = idea_decryptfile(cipherfile, outfile);
	    if (status < 0) {	/* error return */
		errorLvl = DECR_ERROR;
		return -1;	/* error exit status */
	    }
	    nested_info = (status > 0);

	} else if (is_ctb_type(ctb, CTB_COMPRESSED_TYPE)) {

	    /* Compressed text. */
	    status = decompress_file(cipherfile, outfile);
	    if (status < 0) {	/* error return */
		errorLvl = DECOMPRESS_ERROR;
		return -1;
	    }
	    /* Always assume nested information... */
	    nested_info = TRUE;

	} else if (is_ctb_type(ctb, CTB_LITERAL_TYPE) ||
		   is_ctb_type(ctb, CTB_LITERAL2_TYPE)) { /* Raw plaintext.
							     Just copy it.
							     No more nesting.
							   */

	    /* Strip off CTB_LITERAL prefix byte from file: */
	    /* strip_literal may alter plainfile; will set mode */
	    status = strip_literal(cipherfile, outfile,
				   preserved_name, &literal_mode);
	    if (status < 0) {	/* error return */
		errorLvl = UNKNOWN_FILE_ERROR;
		return -1;
	    }
	    nested_info = FALSE;
	} else if (ctb == CTB_CERT_SECKEY || ctb == CTB_CERT_PUBKEY) {

	    rmtemp(outfile);
	    if (decrypt_only_flag) {
		/* swap file names instead of just copying the file */
		outfile = cipherfile;
		cipherfile = NULL;
		nested_info = FALSE;	/* No error */
		break;		/* no further processing */
	    }
	    /* Key ring.  View it. */
	    fprintf(pgpout,
LANG("\nFile contains key(s).  Contents follow..."));
	    if (view_keyring(NULL, cipherfile, TRUE, FALSE) < 0) {
		errorLvl = KEYRING_VIEW_ERROR;
		return -1;
	    }
	    /* filter mode explicit requested with -f */
	    if (filter_mode && !preserve_filename)
		return 0;	/*    No output file */
	    if (batchmode)
		return 0;
	    if (ctb == CTB_CERT_SECKEY)
		strcpy(ringfile, globalSecringName);
	    else
		strcpy(ringfile, globalPubringName);
	    /*      Ask if it should be put on key ring */
	    fprintf(pgpout,
LANG("\nDo you want to add this keyfile to keyring '%s' (y/N)? "), ringfile);
	    if (!getyesno('n'))
		return 0;
	    status = addto_keyring(cipherfile, ringfile);
	    if (status < 0) {
		fprintf(pgpout, LANG("\007Keyring add error. "));
		errorLvl = KEYRING_ADD_ERROR;
		return -1;
	    }
	    return 0;		/*    No output file */

	} else {		/* Unrecognized CTB */
	    break;
	}

    } while (nested_info);
    /* No more nested parsable information */

    /* Stopped early due to error */
    if (nested_info) {
	fprintf(pgpout,
"\7\nERROR: Nested data has unexpected format.  CTB=0x%02X\n", ctb);
	if (outfile)
	    rmtemp(outfile);
	if (cipherfile)
	    rmtemp(cipherfile);
	errorLvl = UNKNOWN_FILE_ERROR;
	return -1;
    }
    if (outfile == NULL) {	/* file was not encrypted */
	if (!filter_mode && !moreflag) {
	    fprintf(pgpout,
LANG("\007\nError: '%s' is not a ciphertext, signature, or key file.\n"),
		    cipherfile);
	    errorLvl = UNKNOWN_FILE_ERROR;
	    return -1;
	}
	outfile = cipherfile;
    } else {
	if (cipherfile)
	    rmtemp(cipherfile);
    }

    if (moreflag || (strcmp(preserved_name, CONSOLE_FILENAME) == 0)) {
	/* blort to screen */
	if (strcmp(preserved_name, CONSOLE_FILENAME) == 0) {
	    fprintf(pgpout,
LANG("\n\nThis message is marked \"For your eyes only\".  Display now \
(Y/n)? "));
	    if (batchmode || !getyesno('y')) {
		/* no -- abort display, and clean up */
		rmtemp(outfile);
		return 0;
	    }
	}
	if (!quietmode)
	    fprintf(pgpout, LANG("\n\nPlaintext message follows...\n"));
	else
	    putc('\n', pgpout);
	fprintf(pgpout, "------------------------------\n");
	more_file(outfile);
	/* Disallow saving to disk if outfile is console-only: */
	if (strcmp(preserved_name, CONSOLE_FILENAME) == 0) {
	    clearscreen();	/* remove all evidence */
	} else if (!quietmode && !batchmode) {
	    fprintf(pgpout, LANG("Save this file permanently (y/N)? "));
	    if (getyesno('n')) {
		char moreFilename[256];
		fprintf(pgpout, LANG("Enter filename to save file as: "));
		if (preserved_name[0])
		    fprintf(pgpout, "[%s]: ", file_tail(preserved_name));
		getstring(moreFilename, 255, TRUE);
		if (*moreFilename == '\0') {
		    if (*preserved_name != '\0')
			savetemp(outfile, file_tail(preserved_name));
		    else
			rmtemp(outfile);
		} else
		    savetemp(outfile, moreFilename);
		return 0;
	    }
	}
	rmtemp(outfile);
	return 0;
    }				/* blort to screen */
    if (outputfile) {
	if (!strcmp(outputfile, "/dev/null")) {
	    rmtemp(outfile);
	    return 0;
	}
	filter_mode = (strcmp(outputfile, "-") == 0);
	strcpy(plainfile, outputfile);
    } else {
#ifdef VMS
	/* VMS null extension has to be ".", not "" */
	force_extension(plainfile, ".");
#else				/* not VMS */
	drop_extension(plainfile);
#endif				/* not VMS */
    }

    if (!preserve_filename && filter_mode) {
	if (writePhantomOutput(outfile) < 0) {
	    errorLvl = UNKNOWN_FILE_ERROR;
	    return -1;
	}
	rmtemp(outfile);
	return 0;
    }
    if (preserve_filename && preserved_name[0] != '\0')
	strcpy(plainfile, file_tail(preserved_name));

    if (quietmode) {
	if (savetemp(outfile, plainfile) == NULL) {
	    errorLvl = UNKNOWN_FILE_ERROR;
	    return -1;
	}
	return 0;
    }
    if (!verbose)	       /* if other filename messages were suppressed */
	fprintf(pgpout, LANG("\nPlaintext filename: %s"), plainfile);


/*---------------------------------------------------------*/

    /*      One last thing-- let's attempt to classify some of the more
       frequently occurring cases of plaintext output files, as an
       aid to the user.

       For example, if output file is a public key, it should have
       the right extension on the filename.

       Also, it will likely be common to encrypt files created by
       various archivers, so they should be renamed with the archiver
       extension.
     */
    get_header_info_from_file(outfile, header, 8);

    newname = NULL;
    if (header[0] == CTB_CERT_PUBKEY) {
	/* Special case--may be public key, worth renaming */
	fprintf(pgpout,
LANG("\nPlaintext file '%s' looks like it contains a public key."),
		plainfile);
	newname = maybe_force_extension(plainfile, PGP_EXTENSION);
    }
    /* Possible public key output file */ 
    else if ((i = compressSignature(header)) >= 0) {
	/*      Special case--may be an archived/compressed file,
		worth renaming
	*/
	fprintf(pgpout, LANG("\nPlaintext file '%s' looks like a %s file."),
		plainfile, compressName[i]);
	newname = maybe_force_extension(plainfile, compressExt[i]);
    } else if (is_ctb(header[0]) &&
	       (is_ctb_type(header[0], CTB_PKE_TYPE)
		|| is_ctb_type(header[0], CTB_SKE_TYPE)
		|| is_ctb_type(header[0], CTB_CKE_TYPE))) {
	/* Special case--may be another ciphertext file, worth renaming */
	fprintf(pgpout,
LANG("\n\007Output file '%s' may contain more ciphertext or signature."),
		plainfile);
	newname = maybe_force_extension(plainfile, PGP_EXTENSION);
    }				/* Possible ciphertext output file */
    if (savetemp(outfile, (newname ? newname : plainfile)) == NULL) {
	errorLvl = UNKNOWN_FILE_ERROR;
	return -1;
    }
    fprintf(pgpout, "\n");
    return 0;
}				/* do_decrypt */

static int do_keyopt(char keychar)
{
    char keyfile[MAX_PATH];
    char ringfile[MAX_PATH];
    char *workfile;
    int status;

    if ((filter_mode || batchmode)
	&& (keychar == 'g' || keychar == 'e' || keychar == 'd'
	    || (keychar == 'r' && sign_flag))) {
	errorLvl = NO_BATCH;
	arg_error();		/* interactive process, no go in batch mode */
    }
    /*
     * If we're not doing anything that uses stdout, produce output there,
     * in case user wants to redirect it.
     */
    if (!filter_mode)
	pgpout = stdout;

    switch (keychar) {

/*-------------------------------------------------------*/
    case 'g':
	{		/*      Key generation
			   Arguments: bitcount, bitcount
			 */
	    char keybits[6], ebits[6], *username = NULL;

	    /* 
	     * Why all this code?
	     * 
	     * Some people may object to PGP insisting on finding the
	     * manual somewhere in the neighborhood to generate a key.
	     * They bristle against this seemingly authoritarian
	     * attitude.  Some people have even modified PGP to defeat
	     * this feature, and redistributed their hotwired version to
	     * others.  That creates problems for me (PRZ).
	     * 
	     * Here is the problem.  Before I added this feature, there
	     * were maimed versions of the PGP distribution package
	     * floating around that lacked the manual.  One of them was
	     * uploaded to Compuserve, and was distributed to countless
	     * users who called me on the phone to ask me why such a
	     * complicated program had no manual.  It spread out to BBS
	     * systems around the country.  And a freeware distributor got
	     * hold of the package from Compuserve and enshrined it on
	     * CD-ROM, distributing thousands of copies without the
	     * manual.  What a mess.
	     * 
	     * Please don't make my life harder by modifying PGP to
	     * disable this feature so that others may redistribute PGP
	     * without the manual.  If you run PGP on a palmtop with no
	     * memory for the manual, is it too much to ask that you type
	     * one little extra word on the command line to do a key
	     * generation, a command that is seldom used by people who
	     * already know how to use PGP?  If you can't stand even this
	     * trivial inconvenience, can you suggest a better method of
	     * reducing PGP's distribution without the manual?
	     * 
	     * PLEASE DO NOT DISABLE THIS CHECK IN THE SOURCE CODE
	     * WITHOUT AT LEAST CALLING PHILIP ZIMMERMANN 
	     * (+1 303 541-0140, or [email protected]) TO DISCUSS IT. 
	     */
	    if (!nomanual && manuals_missing()) {
		char const *const *dir;
		fputs(LANG("\a\nError: PGP User's Guide not found.\n\
PGP looked for it in the following directories:\n"), pgpout);
		for (dir = manual_dirs; *dir; dir++)
		    fprintf(pgpout, "\t\"%s\"\n", *dir);
		fputs(
LANG("and the doc subdirectory of each of the above.  Please put a copy of\n\
both volumes of the User's Guide in one of these directories.\n\
\n\
Under NO CIRCUMSTANCES should PGP ever be distributed without the PGP\n\
User's Guide, which is included in the standard distribution package.\n\
If you got a copy of PGP without the manual, please inform whomever you\n\
got it from that this is an incomplete package that should not be\n\
distributed further.\n\
\n\
PGP will not generate a key without finding the User's Guide.\n\
There is a simple way to override this restriction.  See the\n\
PGP User's Guide for details on how to do it.\n\
\n"), pgpout);
		return KEYGEN_ERROR;
	    }
	    if (myArgc > 2)
		strncpy(keybits, myArgv[2], sizeof(keybits) - 1);
	    else
		keybits[0] = '\0';

	    if (myArgc > 3)
		strncpy(ebits, myArgv[3], sizeof(ebits) - 1);
	    else
		ebits[0] = '\0';

	    /* If the -u option is given, use that username */
	    if (u_flag && my_name != NULL && *my_name != '\0')
		username = my_name;

	    /* dokeygen writes the keys out to the key rings... */
	    status = dokeygen(keybits, ebits, username);

	    if (status < 0) {
		fprintf(pgpout, LANG("\007Keygen error. "));
		errorLvl = KEYGEN_ERROR;
	    }
	    return status;
	}			/* Key generation */

/*-------------------------------------------------------*/
    case 'c':
	{			/*      Key checking
				   Arguments: userid, ringfile
				 */

	    if (myArgc < 3) {	/* Default to all user ID's */
		mcguffin[0] = '\0';
	    } else {
		strcpy(mcguffin, myArgv[2]);
		if (strcmp(mcguffin, "*") == 0)
		    mcguffin[0] = '\0';
	    }
	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);

	    if (myArgc < 4)	/* default key ring filename */
		strcpy(ringfile, globalPubringName);
	    else
		strncpy(ringfile, myArgv[3], sizeof(ringfile) - 1);

	    if ((myArgc < 4 && myArgc > 2)     /* Allow just key file as arg */
		&&has_extension(myArgv[2], PGP_EXTENSION)) {
		strcpy(ringfile, myArgv[2]);
		mcguffin[0] = '\0';
	    }
	    status = dokeycheck(mcguffin, ringfile, CHECK_ALL);

	    if (status < 0) {
		fprintf(pgpout, LANG("\007Keyring check error.\n"));
		errorLvl = KEYRING_CHECK_ERROR;
	    }
	    if (status >= 0 && mcguffin[0] != '\0')
		return status;	/* just checking a single user,
				   dont do maintenance */

	    if ((status = maint_check(ringfile, 0)) < 0 && status != -7) {
		fprintf(pgpout, LANG("\007Maintenance pass error. "));
		errorLvl = KEYRING_CHECK_ERROR;
	    }
	    return status == -7 ? 0 : status;
	}			/* Key check */

/*-------------------------------------------------------*/
    case 'm':
	{			/*      Maintenance pass
				   Arguments: ringfile
				 */

	    if (myArgc < 3)	/* default key ring filename */
		strcpy(ringfile, globalPubringName);
	    else
		strcpy(ringfile, myArgv[2]);

#ifdef MSDOS
	    strlwr(ringfile);
#endif
	    if (!file_exists(ringfile))
		default_extension(ringfile, PGP_EXTENSION);

	    if ((status = maint_check(ringfile,
		      MAINT_VERBOSE | (c_flag ? MAINT_CHECK : 0))) < 0) {
		if (status == -7)
		    fprintf(pgpout,
			    LANG("File '%s' is not a public keyring\n"),
			    ringfile);
		fprintf(pgpout, LANG("\007Maintenance pass error. "));
		errorLvl = KEYRING_CHECK_ERROR;
	    }
	    return status;
	}			/* Maintenance pass */

/*-------------------------------------------------------*/
    case 's':
	{			/*      Key signing
				   Arguments: her_id, keyfile
				 */

	    if (myArgc >= 4)
		strncpy(keyfile, myArgv[3], sizeof(keyfile) - 1);
	    else
		strcpy(keyfile, globalPubringName);

	    if (myArgc >= 3) {
		strcpy(mcguffin, myArgv[2]);	/* Userid to sign */
	    } else {
		fprintf(pgpout,
LANG("\nA user ID is required to select the public key you want to sign. "));
		if (batchmode)	/* not interactive, userid
				   must be on command line */
		    return -1;
		fprintf(pgpout, LANG("\nEnter the public key's user ID: "));
		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
	    }
	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);

	    if (my_name[0] == '\0') {
		fprintf(pgpout,
LANG("\nA secret key is required to make a signature. "));
		fprintf(pgpout,
LANG("\nYou specified no user ID to select your secret key,\n\
so the default user ID and key will be the most recently\n\
added key on your secret keyring.\n"));
	    }
	    status = signkey(mcguffin, my_name, keyfile);

	    if (status >= 0) {
		status = maint_update(keyfile, 0);
		if (status == -7) { /* ringfile is a keyfile or
				       secret keyring */
		    fprintf(pgpout,
			    "Warning: '%s' is not a public keyring\n",
			    keyfile);
		    return 0;
		}
		if (status < 0)
		    fprintf(pgpout, LANG("\007Maintenance pass error. "));
	    }
	    if (status < 0) {
		fprintf(pgpout, LANG("\007Key signature error. "));
		errorLvl = KEY_SIGNATURE_ERROR;
	    }
	    return status;
	}			/* Key signing */


/*-------------------------------------------------------*/
    case 'd':
	{			/*      disable/revoke key
				   Arguments: userid, keyfile
				 */

	    if (myArgc >= 4)
		strncpy(keyfile, myArgv[3], sizeof(keyfile) - 1);
	    else
		strcpy(keyfile, globalPubringName);

	    if (myArgc >= 3) {
		strcpy(mcguffin, myArgv[2]);	/* Userid to sign */
	    } else {
		fprintf(pgpout,
LANG("\nA user ID is required to select the key you want to revoke or \
disable. "));
		fprintf(pgpout, LANG("\nEnter user ID: "));
		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
	    }
	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);

	    status = disable_key(mcguffin, keyfile);

	    if (status >= 0) {
		status = maint_update(keyfile, 0);
		if (status == -7) { /* ringfile is a keyfile or
				       secret keyring */
		    fprintf(pgpout, "Warning: '%s' is not a public keyring\n",
			    keyfile);
		    return 0;
		}
		if (status < 0)
		    fprintf(pgpout, LANG("\007Maintenance pass error. "));
	    }
	    if (status < 0)
		errorLvl = KEY_SIGNATURE_ERROR;
	    return status;
	}			/* Key compromise */

/*-------------------------------------------------------*/
    case 'e':
	{			/*      Key editing
				   Arguments: userid, ringfile
				 */

	    if (myArgc >= 4)
		strncpy(ringfile, myArgv[3], sizeof(ringfile) - 1);
	    else		/* default key ring filename */
		strcpy(ringfile, globalPubringName);

	    if (myArgc >= 3) {
		strcpy(mcguffin, myArgv[2]);	/* Userid to edit */
	    } else {
		fprintf(pgpout,
LANG("\nA user ID is required to select the key you want to edit. "));
		fprintf(pgpout, LANG("\nEnter the key's user ID: "));
		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
	    }
	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);

	    status = dokeyedit(mcguffin, ringfile);

	    if (status >= 0) {
		status = maint_update(ringfile, 0);
		if (status == -7)
		    status = 0;	/* ignore "not a public keyring" error */
		if (status < 0)
		    fprintf(pgpout, LANG("\007Maintenance pass error. "));
	    }
	    if (status < 0) {
		fprintf(pgpout, LANG("\007Keyring edit error. "));
		errorLvl = KEYRING_EDIT_ERROR;
	    }
	    return status;
	}			/* Key edit */

/*-------------------------------------------------------*/
    case 'a':
	{			/*      Add key to key ring
				   Arguments: keyfile, ringfile
				 */

	    if (myArgc < 3 && !filter_mode)
		arg_error();

	    if (!filter_mode) {	/* Get the keyfile from args */
		strncpy(keyfile, myArgv[2], sizeof(keyfile) - 1);

#ifdef MSDOS
		strlwr(keyfile);
#endif
		if (!file_exists(keyfile))
		    default_extension(keyfile, PGP_EXTENSION);

		if (!file_exists(keyfile)) {
		    fprintf(pgpout,
			    LANG("\n\007Key file '%s' does not exist.\n"),
			    keyfile);
		    errorLvl = NONEXIST_KEY_ERROR;
		    return -1;
		}
		workfile = keyfile;

	    } else {
		workfile = tempfile(TMP_WIPE | TMP_TMPDIR);
		readPhantomInput(workfile);
	    }

	    if (myArgc < (filter_mode ? 3 : 4)) { /* default key ring
						     filename */
		byte ctb;
		get_header_info_from_file(workfile, &ctb, 1);
		if (ctb == CTB_CERT_SECKEY)
		    strcpy(ringfile, globalSecringName);
		else
		    strcpy(ringfile, globalPubringName);
	    } else {
		strncpy(ringfile, myArgv[(filter_mode ? 2 : 3)],
			sizeof(ringfile) - 1);
		default_extension(ringfile, PGP_EXTENSION);
	    }
#ifdef MSDOS
	    strlwr(ringfile);
#endif

	    status = addto_keyring(workfile, ringfile);

	    if (filter_mode)
		rmtemp(workfile);

	    if (status < 0) {
		fprintf(pgpout, LANG("\007Keyring add error. "));
		errorLvl = KEYRING_ADD_ERROR;
	    }
	    return status;
	}			/* Add key to key ring */

/*-------------------------------------------------------*/
    case 'x':
	{			/*      Extract key from key ring
				   Arguments: mcguffin, keyfile, ringfile
				 */

	    if (myArgc >= (filter_mode ? 4 : 5)) /* default key ring
						    filename */
		strncpy(ringfile, myArgv[(filter_mode ? 3 : 4)],
			sizeof(ringfile) - 1);
	    else
		strcpy(ringfile, globalPubringName);

	    if (myArgc >= (filter_mode ? 2 : 3)) {
		if (myArgv[2])
		    /* Userid to extract */
		    strcpy(mcguffin, myArgv[2]);
		else
		    strcpy(mcguffin, "");
	    } else {
		fprintf(pgpout,
LANG("\nA user ID is required to select the key you want to extract. "));
		if (batchmode)	/* not interactive, userid
				   must be on command line */
		    return -1;
		fprintf(pgpout, LANG("\nEnter the key's user ID: "));
		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
	    }
	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);

	    if (!filter_mode) {
		if (myArgc >= 4)
		    strncpy(keyfile, myArgv[3], sizeof(keyfile) - 1);
		else
		    keyfile[0] = '\0';

		workfile = keyfile;
	    } else {
		workfile = tempfile(TMP_WIPE | TMP_TMPDIR);
	    }

#ifdef MSDOS
	    strlwr(workfile);
	    strlwr(ringfile);
#endif

	    default_extension(ringfile, PGP_EXTENSION);

	    status = extract_from_keyring(mcguffin, workfile,
					  ringfile, (filter_mode ? FALSE :
						     emit_radix_64));

	    if (status < 0) {
		fprintf(pgpout, LANG("\007Keyring extract error. "));
		errorLvl = KEYRING_EXTRACT_ERROR;
		if (filter_mode)
		    rmtemp(workfile);
		return status;
	    }
	    if (filter_mode && !status) {
		if (emit_radix_64) {
		    /* NULL for outputfile means write to stdout */
		    if (armor_file(workfile, NULL, NULL, NULL) != 0) {
			errorLvl = UNKNOWN_FILE_ERROR;
			return -1;
		    }
		} else {
		    if (writePhantomOutput(workfile) < 0) {
			errorLvl = UNKNOWN_FILE_ERROR;
			return -1;
		    }
		}
		rmtemp(workfile);
	    }
	    return 0;
	}			/* Extract key from key ring */

/*-------------------------------------------------------*/
    case 'r':
	{	/*      Remove keys or selected key signatures from userid keys
			Arguments: userid, ringfile
		 */

	    if (myArgc >= 4)
		strcpy(ringfile, myArgv[3]);
	    else		/* default key ring filename */
		strcpy(ringfile, globalPubringName);

	    if (myArgc >= 3) {
		strcpy(mcguffin, myArgv[2]);	/* Userid to work on */
	    } else {
		if (sign_flag) {
		    fprintf(pgpout,
LANG("\nA user ID is required to select the public key you want to\n\
remove certifying signatures from. "));
		} else {
		    fprintf(pgpout,
LANG("\nA user ID is required to select the key you want to remove. "));
		}
		if (batchmode)	/* not interactive, userid must be on
				   command line */
		    return -1;
		fprintf(pgpout, LANG("\nEnter the key's user ID: "));
		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
	    }
	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);

#ifdef MSDOS
	    strlwr(ringfile);
#endif
	    if (!file_exists(ringfile))
		default_extension(ringfile, PGP_EXTENSION);

	    if (sign_flag) {	/* Remove signatures */
		if (remove_sigs(mcguffin, ringfile) < 0) {
		    fprintf(pgpout, LANG("\007Key signature remove error. "));
		    errorLvl = KEYSIG_REMOVE_ERROR;
		    return -1;
		}
	    } else {		/* Remove keyring */
		if (remove_from_keyring(NULL, mcguffin, ringfile,
					(boolean) (myArgc < 4)) < 0) {
		    fprintf(pgpout, LANG("\007Keyring remove error. "));
		    errorLvl = KEYRING_REMOVE_ERROR;
		    return -1;
		}
	    }
	    return 0;
	}			/* remove key signatures from userid */

/*-------------------------------------------------------*/
    case 'v':
    case 'V':			/* -kvv */
	{			/* View or remove key ring entries,
				   with userid match
				   Arguments: userid, ringfile
				 */

	    if (myArgc < 4)	/* default key ring filename */
		strcpy(ringfile, globalPubringName);
	    else
		strcpy(ringfile, myArgv[3]);

	    if (myArgc > 2) {
		strcpy(mcguffin, myArgv[2]);
		if (strcmp(mcguffin, "*") == 0)
		    mcguffin[0] = '\0';
	    } else {
		*mcguffin = '\0';
	    }

	    if ((myArgc == 3) && has_extension(myArgv[2], PGP_EXTENSION)) {
		strcpy(ringfile, myArgv[2]);
		mcguffin[0] = '\0';
	    }
	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);

#ifdef MSDOS
	    strlwr(ringfile);
#endif
	    if (!file_exists(ringfile))
		default_extension(ringfile, PGP_EXTENSION);

	    /* If a second 'v' (keychar = V), show signatures too */
	    status = view_keyring(mcguffin, ringfile,
				  (boolean) (keychar == 'V'), c_flag);
	    if (status < 0) {
		fprintf(pgpout, LANG("\007Keyring view error. "));
		errorLvl = KEYRING_VIEW_ERROR;
	    }
	    return status;
	}			/* view key ring entries, with userid match */

    default:
	arg_error();
    }
    return 0;
}				/* do_keyopt */

/* comes here if user made a boo-boo. */
void user_error()
{
    fprintf(pgpout, LANG("\nFor a usage summary, type:  pgp -h\n"));
    fprintf(pgpout,
	    LANG("For more detailed help, consult the PGP User's Guide.\n"));
    exitPGP(errorLvl ? errorLvl : 127);		/* error exit */
}

#if defined(DEBUG) && defined(linux)
#include <malloc.h>
#endif

/*
 * exitPGP: wipes and removes temporary files, also tries to wipe
 * the stack.
 */
void exitPGP(int returnval)
{
    char buf[STACK_WIPE];
    struct hashedpw *hpw;

    if (verbose)
	fprintf(pgpout, "exitPGP: exitcode = %d\n", returnval);
    for (hpw = passwds; hpw; hpw = hpw->next)
	memset(hpw->hash, 0, sizeof(hpw->hash));
    for (hpw = keypasswds; hpw; hpw = hpw->next)
	memset(hpw->hash, 0, sizeof(hpw->hash));
    cleanup_tmpf();
    /* Merge any entropy we collected into the randseed.bin file */
    if (cryptRandOpen((struct IdeaCfbContext *)0) >= 0)
	    cryptRandSave((struct IdeaCfbContext *)0);
#if defined(DEBUG) && defined(linux)
    if (verbose) {
	struct mstats mstat;
	mstat = mstats();
	printf("%d chunks used (%d bytes)  %d bytes total\n",
	       mstat.chunks_used, mstat.bytes_used, mstat.bytes_total);
    }
#endif
    memset(buf, 0, sizeof(buf));	/* wipe stack */
#ifdef VMS
/*
 * Fake VMS style error returns with severity in bottom 3 bits
 */
    if (returnval)
	returnval = (returnval << 3) | 0x10000002;
    else
	returnval = 0x10000001;
#endif				/* VMS */
    exit(returnval);
}

static void arg_error()
{
    signon_msg();
    fprintf(pgpout, LANG("\nInvalid arguments.\n"));
    errorLvl = BAD_ARG_ERROR;
    user_error();
}

/*
 * Check for language specific help files in PGPPATH, then the system
 * directory.  If that fails, check for the default pgp.hlp, again
 * firat a private copy, then the system-wide one.
 *
 * System-wide copies currently only exist on Unix.
 */
static void build_helpfile(char *helpfile, char const *extra)
{
    if (strcmp(language, "en")) {
	buildfilename(helpfile, language);
	strcat(helpfile, extra);
	force_extension(helpfile, HLP_EXTENSION);
	if (file_exists(helpfile))
	    return;
#ifdef PGP_SYSTEM_DIR
	strcpy(helpfile, PGP_SYSTEM_DIR);
	strcat(helpfile, language);
	strcat(helpfile, extra);
	force_extension(helpfile, HLP_EXTENSION);
	if (file_exists(helpfile))
	    return;
#endif
    }
    buildfilename(helpfile, "pgp");
    strcat(helpfile, extra);
    force_extension(helpfile, HLP_EXTENSION);
#ifdef PGP_SYSTEM_DIR
    if (file_exists(helpfile))
	return;
    strcpy(helpfile, PGP_SYSTEM_DIR);
    strcat(helpfile, "pgp");
    strcat(helpfile, extra);
    force_extension(helpfile, HLP_EXTENSION);
#endif
}

static void usage()
{
    char helpfile[MAX_PATH];
    char *tmphelp = helpfile;
    extern unsigned char *ext_c_ptr;

    signon_msg();
    build_helpfile(helpfile, "");

    if (ext_c_ptr) {
	/* conversion to external format necessary */
	tmphelp = tempfile(TMP_TMPDIR);
	CONVERSION = EXT_CONV;
	if (copyfiles_by_name(helpfile, tmphelp) < 0) {
	    rmtemp(tmphelp);
	    tmphelp = helpfile;
	}
	CONVERSION = NO_CONV;
    }
    /* built-in help if pgp.hlp is not available */
    if (more_file(tmphelp) < 0)
	fprintf(pgpout, LANG("\nUsage summary:\
\nTo encrypt a plaintext file with recipent's public key, type:\
\n   pgp -e textfile her_userid [other userids] (produces textfile.pgp)\
\nTo sign a plaintext file with your secret key:\
\n   pgp -s textfile [-u your_userid]           (produces textfile.pgp)\
\nTo sign a plaintext file with your secret key, and then encrypt it\
\n   with recipent's public key, producing a .pgp file:\
\n   pgp -es textfile her_userid [other userids] [-u your_userid]\
\nTo encrypt with conventional encryption only:\
\n   pgp -c textfile\
\nTo decrypt or check a signature for a ciphertext (.pgp) file:\
\n   pgp ciphertextfile [plaintextfile]\
\nTo produce output in ASCII for email, add the -a option to other options.\
\nTo generate your own unique public/secret key pair:  pgp -kg\
\nFor help on other key management functions, type:   pgp -k\n"));
    if (ext_c_ptr)
	rmtemp(tmphelp);
    exit(BAD_ARG_ERROR);	/* error exit */
}

static void key_usage()
{
    char helpfile[MAX_PATH];
    char *tmphelp = helpfile;
    extern unsigned char *ext_c_ptr;

    signon_msg();
    build_helpfile(helpfile, "key");

    if (ext_c_ptr) {
	/* conversion to external format necessary */
	tmphelp = tempfile(TMP_TMPDIR);
	CONVERSION = EXT_CONV;
	if (copyfiles_by_name(helpfile, tmphelp) < 0) {
	    rmtemp(tmphelp);
	    tmphelp = helpfile;
	}
	CONVERSION = NO_CONV;
    }
    /* built-in help if pgp.hlp is not available */
    if (more_file(tmphelp) < 0)
	/* only use built-in help if there is no helpfile */
	fprintf(pgpout, LANG("\nKey management functions:\
\nTo generate your own unique public/secret key pair:\
\n   pgp -kg\
\nTo add a key file's contents to your public or secret key ring:\
\n   pgp -ka keyfile [keyring]\
\nTo remove a key or a user ID from your public or secret key ring:\
\n   pgp -kr userid [keyring]\
\nTo edit your user ID or pass phrase:\
\n   pgp -ke your_userid [keyring]\
\nTo extract (copy) a key from your public or secret key ring:\
\n   pgp -kx userid keyfile [keyring]\
\nTo view the contents of your public key ring:\
\n   pgp -kv[v] [userid] [keyring]\
\nTo check signatures on your public key ring:\
\n   pgp -kc [userid] [keyring]\
\nTo sign someone else's public key on your public key ring:\
\n   pgp -ks her_userid [-u your_userid] [keyring]\
\nTo remove selected signatures from a userid on a keyring:\
\n   pgp -krs userid [keyring]\
\n"));

    exit(BAD_ARG_ERROR);	/* error exit */
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].