1 /* #define TEMP_VERSION /* if defined, temporary experimental
2    		            version of PGP */
3 /* pgp.c -- main module for PGP.
4    PGP: Pretty Good(tm) Privacy - public key cryptography for the masses.
5 
6    Synopsis:  PGP uses public-key encryption to protect E-mail.
7    Communicate securely with people you've never met, with no secure
8    channels needed for prior exchange of keys.  PGP is well featured and
9    fast, with sophisticated key management, digital signatures, data
10    compression, and good ergonomic design.
11 
12    The original PGP version 1.0 was written by Philip Zimmermann, of
13    Phil's Pretty Good(tm) Software.  Many parts of later versions of
14    PGP were developed by an international collaborative effort,
15    involving a number of contributors, including major efforts by:
16    Branko Lankester <branko@hacktic.nl>
17    Hal Finney <74076.1041@compuserve.com>
18    Peter Gutmann <pgut1@cs.aukuni.ac.nz>
19    Other contributors who ported or translated or otherwise helped include:
20    Jean-loup Gailly in France
21    Hugh Kennedy in Germany
22    Lutz Frank in Germany
23    Cor Bosman in The Netherlands
24    Felipe Rodriquez Svensson in The Netherlands
25    Armando Ramos in Spain
26    Miguel Angel Gallardo Ortiz in Spain
27    Harry Bush and Maris Gabalins in Latvia
28    Zygimantas Cepaitis in Lithuania
29    Alexander Smishlajev
30    Peter Suchkow and Andrew Chernov in Russia
31    David Vincenzetti in Italy
32    ...and others.
33 
34 
35    (c) Copyright 1990-1996 by Philip Zimmermann.  All rights reserved.
36    The author assumes no liability for damages resulting from the use
37    of this software, even if the damage results from defects in this
38    software.  No warranty is expressed or implied.
39 
40    Note that while most PGP source modules bear Philip Zimmermann's
41    copyright notice, many of them have been revised or entirely written
42    by contributors who frequently failed to put their names in their
43    code.  Code that has been incorporated into PGP from other authors
44    was either originally published in the public domain or is used with
45    permission from the various authors.
46 
47    PGP is available for free to the public under certain restrictions.
48    See the PGP User's Guide (included in the release package) for
49    important information about licensing, patent restrictions on
50    certain algorithms, trademarks, copyrights, and export controls.
51 
52 
53    Philip Zimmermann may be reached at:
54    Boulder Software Engineering
55    3021 Eleventh Street
56    Boulder, Colorado 80304  USA
57    (303) 541-0140  (voice or FAX)
58    email:  prz@acm.org
59 
60 
61    PGP will run on MSDOS, Sun Unix, VAX/VMS, Ultrix, Atari ST,
62    Commodore Amiga, and OS/2.  Note:  Don't try to do anything with
63    this source code without looking at the PGP User's Guide.
64 
65    PGP combines the convenience of the Rivest-Shamir-Adleman (RSA)
66    public key cryptosystem with the speed of fast conventional
67    cryptographic algorithms, fast message digest algorithms, data
68    compression, and sophisticated key management.  And PGP performs
69    the RSA functions faster than most other software implementations.
70    PGP is RSA public key cryptography for the masses.
71 
72    Uses RSA Data Security, Inc. MD5 Message Digest Algorithm
73    as a hash for signatures.  Uses the ZIP algorithm for compression.
74    Uses the ETH IPES/IDEA algorithm for conventional encryption.
75 
76    PGP generally zeroes its used stack and memory areas before exiting.
77    This avoids leaving sensitive information in RAM where other users
78    could find it later.  The RSA library and keygen routines also
79    sanitize their own stack areas.  This stack sanitizing has not been
80    checked out under all the error exit conditions, when routines exit
81    abnormally.  Also, we must find a way to clear the C I/O library
82    file buffers, the disk buffers, and cache buffers.
83 
84    Revisions:
85    Version 1.0 -  5 Jun 91
86    Version 1.4 - 19 Jan 92
87    Version 1.5 - 12 Feb 92
88    Version 1.6 - 24 Feb 92
89    Version 1.7 - 29 Mar 92
90    Version 1.8 - 23 May 92
91    Version 2.0 -  2 Sep 92
92    Version 2.1 -  6 Dec 92
93    Version 2.2 -  6 Mar 93
94    Version 2.3 - 13 Jun 93
95    Version 2.3a-  1 Jul 93
96    Version 2.4 -  6 Nov 93
97    Version 2.5 -  5 May 94
98    Version 2.6 - 22 May 94
99    Version 2.6.1 - 29 Aug 94
100    Version 2.6.2 - 11 Oct 94
101    Version 2.6.2i - 7 May 95
102    Version 2.6.3(i) - 18 Jan 96
103    Version 2.6.3(i)a - 4 Mar 96
104    Version 2.6.3(i)n -  2 Apr 1997
105    Version 2.6.3(i)n -  3 Apr 1997
106    Version 2.6.3(i)n -  4 Apr 1997
107    Version 2.6.3(i)n - 18 Apr 1997
108    Version 2.6.3(i)n - 12 May 1997
109    Version 2.6.3(i)n - 13 May 1997
110    Version 2.6.3(i)n - 29 May 1997
111    Version 2.6.3(i)n - 29 Jul 1997
112    Version 2.6.3(i)n - 28 Aug 1997
113    Version 2.6.3(i)n -  5 Sep 1997
114    Version 2.6.3(i)n -  6 Oct 1997
115    Version 2.6.3(i)n -  7 Oct 1997
116    Version 2.6.3(i)n -  2 Sep 1999
117    Version 2.6.3(i)n -  6 Oct 2000
118    Version 2.6.3(i)n - 22 Mar 2001
119  */
120 
121 
122 #include <ctype.h>
123 #ifndef AMIGA
124 #include <signal.h>
125 #endif
126 #include <stdio.h>
127 #include <stdlib.h>
128 #include <string.h>
129 
130 #ifdef UNIX
131 #include <sys/types.h>
132 #include <sys/stat.h>
133 #endif
134 
135 #include "system.h"
136 #include "mpilib.h"
137 #include "random.h"
138 #include "crypto.h"
139 #include "fileio.h"
140 #include "keymgmt.h"
141 #include "language.h"
142 #include "pgp.h"
143 #include "exitpgp.h"
144 #include "charset.h"
145 #include "getopt.h"
146 #include "config.h"
147 #include "keymaint.h"
148 #include "keyadd.h"
149 #include "rsaglue.h"
150 #include "noise.h"
151 
152 #ifdef MACTC5
153 #include "Macutil.h"
154 #include "Macutil2.h"
155 #include "Macutil3.h"
156 #include "Macutil4.h"
157 #include "Macbinary.h"
158 #include "Binhex.h"
159 #include "MacPGP.h"
160 #include "mystr.h"
161 void AddOutputFiles(char *filepath);
162 void ReInitKeyMaint(void);
163 extern char appPathName[];
164 void 		ReInitGlobals(void);
165 extern int level,method;
166 extern Boolean explicit_plainfile;
167 extern long infile_line;
168 extern int eofonce;
169 extern boolean savedwashed;
170 extern char *special;
171 char *Outputfile = NULL;
172 void check_expiration_date(void);
173 #define BEST -1
174 #define exit Exit
175 void Exit(int x);
176 #endif
177 
178 #ifdef  M_XENIX
179 char *strstr();
180 long time();
181 #endif
182 
183 #ifdef MSDOS
184 #ifdef __ZTC__			/* Extend stack for Zortech C */
185 unsigned _stack_ = 24 * 1024;
186 #endif
187 #ifdef __TURBOC__
188 unsigned _stklen = 24 * 1024;
189 #endif
190 #endif
191 #define	STACK_WIPE	4096
192 
193 #ifdef AMIGA
194 #ifdef __SASC_60
195 /* Let the compiler allocate us an appropriate stack. */
196 extern long __stack = 32768L;
197 #endif
198 
199  /* Add the appropriate AmigaOS version string, depending on the
200   * compiler flags.
201   */
202 #ifdef USA
203 static const char __DOSVer[] = "$VER: PGP 2.6.3n (2001-03-23)"
204 #  ifdef _M68020
205         " Amiga 68020 version by Rob Knop <rknop@mop.caltech.edu>";
206 #  else
207         " Amiga 68000 version by Rob Knop <rknop@mop.caltech.edu>";
208 #  endif
209 #else
210 static const char __DOSVer[] = "$VER: PGP 2.6.3in (2001-03-23)"
211 #  ifdef _M68020
212         " Amiga 68020 version by Peter Simons <simons@peti.rhein.de>";
213 #  else
214         " Amiga 68000 version by Peter Simons <simons@peti.rhein.de>";
215 #  endif
216 #endif /* USA */
217 #endif /* AMIGA */
218 
219 /* Global filenames and system-wide file extensions... */
220 #ifdef USA
221 char rel_version[] = _LANG("2.6.3n");	/* release version */
222 #else
223 char rel_version[] = _LANG("2.6.3in");	/* release version */
224 #endif
225 char rel_date[] = "2001-03-23";		/* release date */
226 char PGP_EXTENSION[] = ".pgp";
227 char ASC_EXTENSION[] = ".asc";
228 char SIG_EXTENSION[] = ".sig";
229 char BAK_EXTENSION[] = ".bak";
230 static char HLP_EXTENSION[] = ".hlp";
231 char CONSOLE_FILENAME[] = "_CONSOLE";
232 #ifdef MACTC5
233 char HELP_FILENAME[256] = "pgp.hlp";
234 #else
235 static char HELP_FILENAME[] = "pgp.hlp";
236 #endif
237 
238 /* These files use the environmental variable PGPPATH as a default path: */
239 char globalPubringName[MAX_PATH];
240 char globalSecringName[MAX_PATH];
241 char globalRandseedName[MAX_PATH];
242 char globalCommentString[128];
243 
244 /* Flags which are global across the driver code files */
245 boolean verbose = FALSE;	/* -l option: display maximum information */
246 FILE *pgpout;			/* Place for routine messages */
247 
248 static void usage(void);
249 static void key_usage(void);
250 static void arg_error(void);
251 static void initsigs(void);
252 static int do_keyopt(char);
253 static int do_decrypt(char *);
254 static void do_armorfile(char *);
255 char ** ParseRecipients(char **);
256 void hashpass (char *keystring, int keylen, byte *hash);
257 
258 /* Various compression signatures: PKZIP, Zoo, GIF, Arj, and HPACK.
259    Lha(rc) is handled specially in the code; it is missing from the
260    compressSig structure intentionally.  If more formats are added,
261    put them before lharc to keep the code consistent.
262 
263    27-Jun-95 simons@peti.rhein.de (Peter Simons)
264    Added support for lh5 archive as generated by Lha. Unfortunately,
265    lh5 requires special treatment also. I inserted the check right
266    _before_ lharc, because lh5/lha is a special type of an lharc
267    archive.
268  */
269 static char *compressSig[] =
270 {"PK\03\04", "ZOO ", "GIF8", "\x60\xea", "Rar!",
271  "HPAK", "\037\213", "\037\235", "\032\013", "\032HP%"
272 	/* lharc is special, must be last */ };
273 static char *compressName[] =
274 {"PKZIP", "Zoo", "GIF", "Arj", "RAR",
275  "Hpack", "gzip", "compressed", "PAK", "Hyper",
276  "Lha", "Lharc"};
277 static char *compressExt[] =
278 {".zip", ".zoo", ".gif", ".arj", ".rar",
279  ".hpk", ".gz", ".Z", ".pak", ".hyp",
280  ".lha", ".lzh"};
281 
282 /* "\032\0??", "ARC", ".arc" */
283 
284 /* Returns file signature type from a number of popular compression formats
285    or -1 if no match */
compressSignature(byte * header)286 int compressSignature(byte * header)
287 {
288     int i;
289 
290     for (i = 0; i < sizeof(compressSig) / sizeof(*compressSig); i++)
291 	if (!strncmp((char *) header, compressSig[i], strlen(compressSig[i])))
292 	    return i;
293 
294     /* Special check for lha files */
295     if (!strncmp((char *)header+2, "-lh5-", 5))
296       return i;
297 
298     /* Special check for lharc files */
299     if (header[2] == '-' && header[3] == 'l' &&
300 	(header[4] == 'z' || header[4] == 'h') &&
301 	header[6] == '-')
302 	return i+1;
303     return -1;
304 }				/* compressSignature */
305 
306 /* returns TRUE if file is likely to be compressible */
file_compressible(char * filename)307 static boolean file_compressible(char *filename)
308 {
309     byte header[8];
310     get_header_info_from_file(filename, header, 8);
311     if (compressSignature(header) >= 0)
312 	return FALSE;		/* probably not compressible */
313     return TRUE;		/* possibly compressible */
314 }				/* compressible */
315 
316 
317 /* Possible error exit codes - not all of these are used.  Note that we
318    don't use the ANSI EXIT_SUCCESS and EXIT_FAILURE.  To make things
319    easier for compilers which don't support enum we use #defines */
320 
321 #define EXIT_OK				0
322 #define INVALID_FILE_ERROR		1
323 #define FILE_NOT_FOUND_ERROR		2
324 #define UNKNOWN_FILE_ERROR		3
325 #define NO_BATCH			4
326 #define BAD_ARG_ERROR			5
327 #define INTERRUPT			6
328 #define OUT_OF_MEM			7
329 
330 /* Keyring errors: Base value = 10 */
331 #define KEYGEN_ERROR			10
332 #define NONEXIST_KEY_ERROR		11
333 #define KEYRING_ADD_ERROR		12
334 #define KEYRING_EXTRACT_ERROR		13
335 #define KEYRING_EDIT_ERROR		14
336 #define KEYRING_VIEW_ERROR		15
337 #define KEYRING_REMOVE_ERROR		16
338 #define KEYRING_CHECK_ERROR		17
339 #define KEY_SIGNATURE_ERROR		18
340 #define KEYSIG_REMOVE_ERROR		19
341 
342 /* Encode errors: Base value = 20 */
343 #define SIGNATURE_ERROR			20
344 #define RSA_ENCR_ERROR			21
345 #define ENCR_ERROR			22
346 #define COMPRESS_ERROR			23
347 
348 /* Decode errors: Base value = 30 */
349 #define SIGNATURE_CHECK_ERROR		30
350 #define RSA_DECR_ERROR			31
351 #define DECR_ERROR			32
352 #define DECOMPRESS_ERROR		33
353 
354 
355 #ifdef SIGINT
356 
357 /* This function is called if a BREAK signal is sent to the program.  In this
358    case we zap the temporary files.
359  */
breakHandler(int sig)360 void breakHandler(int sig)
361 {
362 #ifdef UNIX
363     if (sig == SIGPIPE) {
364 	signal(SIGPIPE, SIG_IGN);
365 	exitPGP(INTERRUPT);
366     }
367     if (sig != SIGINT)
368 	fprintf(stderr, "\nreceived signal %d\n", sig);
369     else
370 #endif
371 	fprintf(pgpout, LANG("\nStopped at user request\n"));
372     exitPGP(INTERRUPT);
373 }
374 #endif
375 
376 /* Clears screen and homes the cursor. */
clearscreen(void)377 static void clearscreen(void)
378 {
379     fprintf(pgpout, "\n\033[0;0H\033[J\r           \r");  /* ANSI sequence. */
380     fflush(pgpout);
381 }
382 
383 /* We had to process the config file first to possibly select the
384    foreign language to translate the sign-on line that follows... */
signon_msg(void)385 static void signon_msg(void)
386 {
387     word32 tstamp;
388     /* display message only once to allow calling multiple times */
389     static boolean printed = FALSE;
390 
391     if (quietmode || printed)
392 	return;
393     printed = TRUE;
394     fprintf(stderr,
395 LANG("Pretty Good Privacy(tm) %s - Public-key encryption for the masses.\n"),
396 	    rel_version);
397 #ifdef TEMP_VERSION
398     fputs(
399 "Internal development version only - not for general release.\n", stderr);
400 #endif
401     fputs(LANG("(c) 1990-96 Philip Zimmermann, Phil's Pretty Good Software."),
402     stderr);
403     fprintf(stderr, " %s\n",LANG(rel_date));
404 #ifdef USA
405     fputs(LANG(signon_legalese), stderr);
406 #endif
407     fputs(
408 #ifdef USA
409 LANG("Export of this software may be restricted by the U.S. government.\n"),
410 #else
411 LANG("International version - not for use in the USA. Does not use RSAREF.\n"),
412 #endif
413 	  stderr);
414 
415     get_timestamp((byte *) & tstamp);	/* timestamp points to tstamp */
416     fprintf(pgpout, LANG("Current time: %s\n"), ctdate(&tstamp));
417 }
418 
419 
420 #ifdef TEMP_VERSION		/* temporary experimental version of PGP */
421 #include <time.h>
422 #define CREATION_DATE 0x30FE3640ul
423 				/* CREATION_DATE is
424 				   Thu Jan 18, 1996 1200 hours UTC */
425 #define LIFESPAN	((unsigned long) 60L * (unsigned long) 86400L)
426 				/* LIFESPAN is 60 days */
427 
428 /* If this is an experimental version of PGP, cut its life short */
check_expiration_date(void)429 void check_expiration_date(void)
430 {
431     if (get_timestamp(NULL) > (CREATION_DATE + LIFESPAN)) {
432 	fprintf(stderr,
433 		"\n\007This experimental version of PGP has expired.\n");
434 	exit(-1);		/* error exit */
435     }
436 }				/* check_expiration_date */
437 #else				/* no expiration date */
438 #define check_expiration_date()	/* null statement */
439 #endif				/* TEMP_VERSION */
440 
441 /* -f means act as a unix-style filter */
442 /* -i means internalize extended file attribute information, only supported
443  *          between like (or very compatible) operating systems. */
444 /* -l means show longer more descriptive diagnostic messages */
445 /* -m means display plaintext output on screen, like unix "more" */
446 /* -d means decrypt only, leaving inner signature wrapping intact */
447 /* -t means treat as pure text and convert to canonical text format */
448 
449 /* Used by getopt function... */
450 #define OPTIONS "abcdefghiklmo:prstu:vwxz:ABCDEFGHIKLMO:PRSTU:VWX?"
451 extern int optind;
452 extern char *optarg;
453 
454 #define INCLUDE_MARK "-@"
455 #define INCLUDE_MARK_LEN sizeof(INCLUDE_MARK)-1	/* skip the \0 */
456 
457 boolean emit_radix_64 = FALSE;	/* set by config file */
458 static boolean sign_flag = FALSE;
459 boolean moreflag = FALSE;
460 boolean filter_mode = FALSE;
461 static boolean preserve_filename = FALSE;
462 static boolean decrypt_only_flag = FALSE;
463 static boolean de_armor_only = FALSE;
464 static boolean strip_sig_flag = FALSE;
465 boolean clear_signatures = TRUE;
466 boolean strip_spaces;
467 static boolean c_flag = FALSE;
468 static boolean u_flag = FALSE;		/* Did I get my_name from -u? */
469 boolean encrypt_to_self = FALSE; /* should I encrypt messages to myself? */
470 boolean sign_new_userids = TRUE;
471 boolean batchmode = FALSE;	/* if TRUE: don't ask questions */
472 boolean quietmode = FALSE;
473 boolean force_flag = FALSE;	/* overwrite existing file without asking */
474 #ifdef VMS			/* kludge for those stupid VMS variable-length
475 				   text records */
476 char literal_mode = MODE_TEXT;	/* MODE_TEXT or MODE_BINARY for literal
477 				   packet */
478 #else				/* not VMS */
479 char literal_mode = MODE_BINARY; /* MODE_TEXT or MODE_BINARY for literal
480 				    packet */
481 #endif				/* not VMS */
482 /* my_name is substring of default userid for secret key to make signatures */
483 char my_name[256] = "\0";	/* null my_name means take first userid
484 				   in ring */
485 char my_etsid[256] = "\0";
486 boolean keepctx = FALSE;	/* TRUE means keep .ctx file on decrypt */
487 /* Ask for each key separately if it should be added to the keyring */
488 boolean interactive_add = FALSE;
489 boolean compress_enabled = TRUE; /* attempt compression before encryption */
490 long timeshift = 0L;		/* seconds from GMT timezone */
491 int version_byte = VERSION_BYTE_NEW;
492 boolean nomanual = 0;
493 /* Show unknown certs in -kv[c][v], default: no, because it's useless */
494 boolean show_unknown_certs = FALSE;
495 /* If non-zero, initialize file to this many random bytes */
496 int makerandom = 0;
497 
498 
499 static char *outputfile = NULL;
500 #ifndef MACTC5
501 static int errorLvl = EXIT_OK;
502 #else
503 int errorLvl = EXIT_OK;
504 #endif
505 static char mcguffin[256];	/* userid search tag */
506 boolean signature_checked = FALSE;
507 int checksig_pass = 0;
508 boolean use_charset_header;
509 char charset_header[16] = "";
510 char plainfile[MAX_PATH];
511 int myArgc = 2;
512 char **myArgv;
513 struct hashedpw *passwds = 0, *keypasswds = 0;
514 static struct hashedpw **passwdstail = &passwds;
515 
516 #ifdef MACTC5
517 extern unsigned long PGPStart, WNECalls;
518 
ReInitGlobals()519 void ReInitGlobals()
520 {
521 	int i;
522     char scratch[64];
523     WNECalls = 0;
524     if (verbose)
525     	PGPStart = TickCount();
526     else
527     	PGPStart = 0;
528     Abort = FALSE;
529 	BreakCntl = 0;
530 	pgpout = stderr;
531     optind = 1;
532 	errorLvl = EXIT_OK;
533     myArgc = 2;
534     myArgv = nil;
535     emit_radix_64 = FALSE;		/* set by config file */
536     sign_flag = FALSE;
537     moreflag = FALSE;
538     filter_mode = FALSE;
539     preserve_filename = FALSE;
540     decrypt_only_flag = FALSE;
541     de_armor_only = FALSE;
542     strip_sig_flag = FALSE;
543     u_flag = FALSE;
544 	c_flag = FALSE;
545     signature_checked = FALSE;
546 	literal_mode = MODE_BINARY;	/* MODE_TEXT or MODE_BINARY for literal packet */
547     errorLvl = EXIT_OK;
548     outputfile = Outputfile;
549     method = BEST; 	/* one of BEST, DEFLATE (only), or STORE (only) */
550     level = 9;		/* 0=fastest compression, 9=best compression */
551     special = NULL;	/* List of special suffixes */
552     infile_line = 0;
553 	eofonce = 0;
554 	savedwashed = FALSE;
555 	ReInitKeyMaint();
556 	settmpdir(nil);
557 	setoutdir(nil);
558 	makerandom = 0;
559 	if (xcli_opt[0]) {
560     	if (argv[argc] == nil)
561     		argv[argc] = malloc((size_t) 80);
562 		if (argv[argc] == nil) {
563 			BailoutAlert(LANG("Out of memory"));
564 			ExitToShell();
565 		}
566 		strcpy(argv[argc], xcli_opt);
567 		argc++;
568 		fprintf(pgpout, "         %s\n", xcli_opt);
569 	}
570 	for (i = 0; i <= 63; i++)
571 		scratch[i] = to_upper(xcli_opt[i]);
572 	if (strcmp(xcli_opt, "+NOMANUAL=ON")==0) nomanual = true;
573 	else nomanual = false;
574  }
575 
init_pgp()576 int init_pgp()
577 {
578 	int err=0;
579 	pgpout=stderr;
580 	/* Process the config file first.  Any command-line arguments will
581 	   override the config file settings */
582 	buildfilename( mcguffin, "config.txt");
583 	if ( processConfigFile( mcguffin ) < 0 )
584 		err=BAD_ARG_ERROR;
585 	init_charset();
586 	signon_msg();
587 	g_armor_flag=emit_radix_64;
588 	g_text_mode=(literal_mode == MODE_TEXT);
589 	g_clear_signatures=clear_signatures;
590 	PGPSetFinfo(globalRandseedName,'RSed','MPGP');
591 	set_precision(MAX_UNIT_PRECISION);
592 	return err;
593 }
594 
595 
Exit(int x)596 void Exit(int x) {
597 
598 	errorLvl = x;
599 	if (myArgv)
600 		free(myArgv);
601 	if (mcguffins)
602 		free(mcguffins);
603 	mac_cleanup_tmpf();
604 	longjmp(jmp_env,5);
605 }
606 
607 
pgp_dispatch(int argc,char * argv[])608 int pgp_dispatch(int argc, char *argv[])
609 {
610 	int status, opt;
611 	char *inputfile = NULL;
612 	char **recipient = NULL;
613 /*	char **mcguffins;   zigf made global so we can free */
614 	boolean macbin_flag = FALSE;
615 #else
616 
617 int main(int argc, char *argv[])
618 {
619     int status, opt;
620     char *inputfile = NULL;
621     char **recipient = NULL;
622     char **mcguffins;
623 #endif /* MACTC5 */
624     char *workfile, *tempf;
625     boolean nestflag = FALSE;
626     boolean decrypt_mode = FALSE;
627     boolean wipeflag = FALSE;
628     boolean armor_flag = FALSE;	/* -a option */
629     boolean separate_signature = FALSE;
630     boolean keyflag = FALSE;
631     boolean encrypt_flag = FALSE;
632     boolean conventional_flag = FALSE;
633     boolean attempt_compression; /* attempt compression before encryption */
634     boolean output_stdout;	/* Output goes to stdout */
635     char *clearfile = NULL;
636     char *literal_file = NULL;
637     char literal_file_name[MAX_PATH];
638     char cipherfile[MAX_PATH];
639     char keychar = '\0';
640     char *p;
641     byte ctb;
642     struct hashedpw *hpw;
643 
644     /* Initial messages to stderr */
645     pgpout = stderr;
646 
647 #ifdef MACTC5
648 	ReInitGlobals();
649 #endif
650 #ifdef	DEBUG1
651     verbose = TRUE;
652 #endif
653     /* The various places one can get passwords from.
654      * We accumulate them all into two lists.  One is
655      * to try on keys only, and is stored in no particular
656      * order, while the other is of unknown purpose so
657      * far (they may be used for conventional encryption
658      * or decryption as well), and are kept in a specific
659      * order.  If any password in the general list is found
660      * to decode a key, it is moved to the key list.
661      * The general list is not grown after initialization,
662      * so the tail pointer is not used after this.
663      */
664 
665 #ifndef MACTC5
666     if ((p = getenv("PGPPASS")) != NULL) {
667 	hpw = xmalloc(sizeof(struct hashedpw));
668 	hashpass(p, strlen(p), hpw->hash);
669 	/* Add to linked list of key passwords */
670 	hpw->next = keypasswds;
671 	keypasswds = hpw;
672     }
673     /* The -z "password" option should be used instead of PGPPASS if
674      * the environment can be displayed with the ps command (eg. BSD).
675      * If the system does not allow overwriting of the command line
676      * argument list but if it has a "hidden" environment, PGPPASS
677      * should be used.
678      */
679     for (opt = 1; opt < argc; ++opt) {
680 	p = argv[opt];
681 	if (p[0] != '-' || p[1] != 'z')
682 	    continue;
683 	/* Accept either "-zpassword" or "-z password" */
684 	p += 2;
685 	if (!*p)
686 	    p = argv[++opt];
687 	/* p now points to password */
688 	if (!p)
689 	    break;		/* End of arg list - ignore */
690 	hpw = xmalloc(sizeof(struct hashedpw));
691 	hashpass(p, strlen(p), hpw->hash);
692 	/* Wipe password */
693 	while (*p)
694 	    *p++ = ' ';
695 	/* Add to tail of linked list of passwords */
696 	hpw->next = 0;
697 	*passwdstail = hpw;
698 	passwdstail = &hpw->next;
699     }
700     /*
701      * If PGPPASSFD is set in the environment try to read the password
702      * from this file descriptor.  If you set PGPPASSFD to 0 pgp will
703      * use the first line read from stdin as password.
704      */
705     if ((p = getenv("PGPPASSFD")) != NULL) {
706 	int passfd;
707 	if (*p && (passfd = atoi(p)) >= 0) {
708 	    char pwbuf[256];
709 	    p = pwbuf;
710 	    while (read(passfd, p, 1) == 1 && *p != '\n')
711 		++p;
712 	    hpw = xmalloc(sizeof(struct hashedpw));
713 	    hashpass(pwbuf, p - pwbuf, hpw->hash);
714 	    memset(pwbuf, 0, p - pwbuf);
715 	    /* Add to tail of linked list of passwords */
716 	    hpw->next = 0;
717 	    *passwdstail = hpw;
718 	    passwdstail = &hpw->next;
719 	}
720     }
721     /* Process the config file.  The following override each other:
722        - Hard-coded defaults
723        - The system config file
724        - Hard-coded defaults for security-critical things
725        - The user's config file
726        - Environment variables
727        - Command-line options.
728      */
729     opt = 0;			/* Number of config files read */
730 #ifdef PGP_SYSTEM_DIR
731 #ifdef UNIX
732     buildsysfilename(mcguffin, ".pgprc");
733     if (access(mcguffin, 0) != 0)
734 #endif
735     buildsysfilename(mcguffin, "config.txt");
736     if (access(mcguffin, 0) == 0) {
737 	opt++;
738 	/*
739 	 * Note: errors here are NOT fatal, so that people
740 	 * can use PGP with a corrputed system file.
741 	 */
742 	processConfigFile(mcguffin);
743     }
744 #endif
745 
746     /*
747      * These must be personal; the system config file may not
748      * influence them.
749      */
750     buildfilename(globalPubringName, "pubring.pgp");
751     buildfilename(globalSecringName, "secring.pgp");
752     buildfilename(globalRandseedName, "randseed.bin");
753     my_name[0] = '\0';
754 
755     /* Process the config file first.  Any command-line arguments will
756        override the config file settings */
757 #if defined(UNIX) || defined(MSDOS) || defined(OS2) || defined (WIN32)
758     /* Try "pgp.ini" on MS-DOS or ".pgprc" on Unix */
759 #ifdef UNIX
760     buildfilename(mcguffin, ".pgprc");
761 #else
762     buildfilename(mcguffin, "pgp.ini");
763 #endif
764     if (access(mcguffin, 0) != 0)
765 	buildfilename(mcguffin, "config.txt");
766 #else
767     buildfilename(mcguffin, "config.txt");
768 #endif
769     if (access(mcguffin, 0) == 0) {
770 	opt++;
771 	if (processConfigFile(mcguffin) < 0)
772 	    exit(BAD_ARG_ERROR);
773     }
774     if (!opt)
775 	fprintf(pgpout, LANG("\007No configuration file found.\n"));
776 
777     init_charset();
778 #endif /* MACTC5 */
779 
780 #ifdef MSDOS			/* only on MSDOS systems */
781     if ((p = getenv("TZ")) == NULL || *p == '\0') {
782 	fprintf(pgpout,LANG("\007WARNING: Environmental variable TZ is not \
783 defined, so GMT timestamps\n\
784 may be wrong.  See the PGP User's Guide to properly define TZ\n\
785 in AUTOEXEC.BAT file.\n"));
786     }
787 #endif				/* MSDOS */
788 
789 #ifdef VMS
790 #define TEMP "SYS$SCRATCH"
791 #else
792 #define TEMP "TMP"
793 #endif				/* VMS */
794     if ((p = getenv(TEMP)) != NULL && *p != '\0')
795 	settmpdir(p);
796 
797     if ((myArgv = (char **) malloc((argc + 2) * sizeof(char **))) == NULL) {
798 	fprintf(stderr, LANG("\n\007Out of memory.\n"));
799 	exitPGP(7);
800     }
801     myArgv[0] = NULL;
802     myArgv[1] = NULL;
803 
804     /* Process all the command-line option switches: */
805     while (optind < argc) {
806 	/*
807 	 * Allow random order of options and arguments (like GNU getopt)
808 	 * NOTE: this does not work with GNU getopt, use getopt.c from
809 	 * the PGP distribution.
810 	 */
811 	if ((!strncmp(argv[optind], INCLUDE_MARK, INCLUDE_MARK_LEN)) ||
812            ((opt = pgp_getopt(argc, argv, OPTIONS)) == EOF)) {
813 	    if (optind == argc)	/* -- at end */
814 		break;
815 	    myArgv[myArgc++] = argv[optind++];
816 	    continue;
817 	}
818 	opt = to_lower(opt);
819 	if (keyflag && (keychar == '\0' || (keychar == 'v' && opt == 'v'))) {
820 	    if (keychar == 'v')
821 		keychar = 'V';
822 	    else
823 		keychar = opt;
824 	    continue;
825 	}
826 	switch (opt) {
827 	case 'a':
828 	    armor_flag = TRUE;
829 	    emit_radix_64 = 1;
830 	    break;
831 	case 'b':
832 	    separate_signature = strip_sig_flag = TRUE;
833 	    break;
834 	case 'c':
835 	    encrypt_flag = conventional_flag = TRUE;
836 	    c_flag = TRUE;
837 	    break;
838 	case 'd':
839 	    decrypt_only_flag = TRUE;
840 	    break;
841 	case 'e':
842 	    encrypt_flag = TRUE;
843 	    break;
844 #ifdef MACTC5
845 	case 'f':
846 		if (macbin_flag == FALSE) filter_mode = TRUE;
847 		break;
848 #else
849 	case 'f':
850 	    filter_mode = TRUE;
851 	    break;
852 #endif
853 	case '?':
854 	case 'h':
855 	    usage();
856 	    break;
857 #ifdef VMS
858 	case 'i':
859 	    literal_mode = MODE_LOCAL;
860 	    break;
861 #else
862 #ifdef MACTC5
863 	case 'i':
864 		macbin_flag = TRUE;
865 		moreflag = FALSE;
866 		literal_mode = MODE_BINARY;
867 		filter_mode = FALSE;
868 		break;
869 #endif /* MACTC5 */
870 #endif				/* VMS */
871 	case 'k':
872 	    keyflag = TRUE;
873 	    break;
874 	case 'l':
875 	    verbose = TRUE;
876 	    break;
877 #ifdef MACTC5
878 	case 'm':
879 		if( macbin_flag == FALSE )
880 			moreflag = TRUE;
881 		break;
882 #else
883 	case 'm':
884 	    moreflag = TRUE;
885 	    break;
886 #endif
887 	case 'p':
888 	    preserve_filename = TRUE;
889 	    break;
890 	case 'o':
891 	    outputfile = optarg;
892 	    break;
893 	case 's':
894 	    sign_flag = TRUE;
895 	    break;
896 #ifdef MACTC5
897 	case 't':
898 		if( macbin_flag == FALSE )
899 			literal_mode = MODE_TEXT;
900 		break;
901 #else
902 	case 't':
903 	    literal_mode = MODE_TEXT;
904 	    break;
905 #endif
906 	case 'u':
907 	    strncpy(my_name, optarg, sizeof(my_name) - 1);
908 	    CONVERT_TO_CANONICAL_CHARSET(my_name);
909 	    u_flag = TRUE;
910 	    break;
911 	case 'w':
912 	    wipeflag = TRUE;
913 	    break;
914 	case 'z':
915 	    break;
916 	    /* '+' special option: does not require - */
917 	case '+':
918 	    if (processConfigLine(optarg) == 0) {
919                 if (!strncmp(optarg,"CH",2))   /* CHARSET */
920                     init_charset();
921 		break;
922 	    }
923 	    fprintf(stderr, "\n");
924 	    /* fallthrough */
925 	default:
926 	    arg_error();
927 	}
928     }
929     myArgv[myArgc] = NULL;	/* Just to make it NULL terminated */
930 
931     if (keyflag && keychar == '\0')
932 	key_usage();
933 
934     signon_msg();
935     check_expiration_date();	/* hobble any experimental version */
936 
937     /*
938      * Write to stdout if explicitly asked to, or in filter mode and
939      * no explicit file name was given.
940      */
941     output_stdout = outputfile ? strcmp(outputfile, "-")  == 0 : filter_mode;
942 
943 #if 1
944     /* At request of Peter Simons, use stderr always. Sounds reasonable. */
945     /* JIS: Put this code back in... removing it broke too many things */
946     if (!output_stdout)
947 	pgpout = stdout;
948 #endif
949 
950 
951 #if defined(UNIX) || defined(VMS)
952     umask(077);			/* Make files default to private */
953 #endif
954 
955     initsigs();			/* Catch signals */
956     noise();			/* Start random number generation */
957 
958     if (keyflag) {
959 	status = do_keyopt(keychar);
960 	if (status < 0)
961 	    user_error();
962 	exitPGP(status);
963     }
964     /* -db means break off signature certificate into separate file */
965     if (decrypt_only_flag && strip_sig_flag)
966 	decrypt_only_flag = FALSE;
967 
968     if (decrypt_only_flag && armor_flag)
969 	decrypt_mode = de_armor_only = TRUE;
970 
971     if (outputfile != NULL)
972 	preserve_filename = FALSE;
973 
974     if (!sign_flag && !encrypt_flag && !conventional_flag && !armor_flag) {
975 	if (wipeflag) {		/* wipe only */
976 	    if (myArgc != 3)
977 		arg_error();	/* need one argument */
978 	    if (wipefile(myArgv[2]) == 0 && remove(myArgv[2]) == 0) {
979 		fprintf(pgpout,
980 			LANG("\nFile %s wiped and deleted. "), myArgv[2]);
981 		fprintf(pgpout, "\n");
982 		exitPGP(EXIT_OK);
983 	    } else if (file_exists(myArgv[2]))
984 	        fprintf(pgpout,
985 LANG("\n\007Error: Can't wipe out file '%s' - read only, maybe?\n"),
986                         myArgv[2]);
987             else {
988 	        fprintf(pgpout,
989 		        LANG("\n\007File '%s' does not exist.\n"), myArgv[2]);
990 	    }
991 	    exitPGP(UNKNOWN_FILE_ERROR);
992 	}
993 	/* decrypt if none of the -s -e -c -a -w options are specified */
994 	decrypt_mode = TRUE;
995     }
996     if (myArgc == 2) {		/* no arguments */
997 #ifdef UNIX
998 	if (!filter_mode && !isatty(fileno(stdin))) {
999 	    /* piping to pgp without arguments and no -f:
1000 	     * switch to filter mode but don't write output to stdout
1001 	     * if it's a tty, use the preserved filename */
1002 	    if (!moreflag)
1003 		pgpout = stderr;
1004 	    filter_mode = TRUE;
1005 	    if (isatty(fileno(stdout)) && !moreflag)
1006 		preserve_filename = TRUE;
1007 	}
1008 #endif
1009 	if (!filter_mode) {
1010 	    if (quietmode) {
1011 		quietmode = FALSE;
1012 		signon_msg();
1013 	    }
1014 	    fprintf(pgpout,
1015 LANG("\nFor details on licensing and distribution, see the PGP User's Guide.\
1016 \nFor other cryptography products and custom development services, contact:\
1017 \nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, \
1018 phone +1 303 541-0140\n"));
1019 	    if (strcmp((p = LANG("@translator@")), "@translator@"))
1020 		fprintf(pgpout, p);
1021 	    fprintf(pgpout, LANG("\nFor a usage summary, type:  pgp -h\n"));
1022 #ifdef MACTC5
1023 		exitPGP(BAD_ARG_ERROR);
1024 #else
1025 	    exit(BAD_ARG_ERROR);	/* error exit */
1026 #endif
1027 	}
1028     } else {
1029 	if (filter_mode) {
1030 	    recipient = &myArgv[2];
1031 	} else {
1032 	    inputfile = myArgv[2];
1033 	    recipient = &myArgv[3];
1034 	}
1035 	recipient = ParseRecipients(recipient);
1036     }
1037 
1038 
1039     if (filter_mode) {
1040 	inputfile = "stdin";
1041     } else if (makerandom > 0) {	/* Create the input file */
1042 	/*
1043 	 * +makerandom=<bytes>: Create an input file consisting of <bytes>
1044 	 * cryptographically strong random bytes, before applying the
1045 	 * encryption options of PGP.  This is an advanced option, so
1046 	 * assume the user knows what he's doing and don't bother about
1047 	 * overwriting questions.  E.g.
1048 	 * pgp +makerandom=24 foofile
1049 	 *	Create "foofile" with 24 random bytes in it.
1050 	 * pgp +makerandom=24 -ea foofile recipient
1051 	 *	The same, but also encrypt it to "recipient", creating
1052 	 *	foofile.asc as well.
1053 	 * This feature was created to allow PGP to create and send keys
1054 	 * around for other applications to use.
1055 	 */
1056 	status = cryptRandWriteFile(inputfile, (struct IdeaCfbContext *)0,
1057 	                       (unsigned)makerandom);
1058 	if (status < 0) {
1059 		fprintf(stderr,"Error writing file \"%s\"\n",inputfile);
1060 		exitPGP(INVALID_FILE_ERROR);
1061 	}
1062 	fprintf(pgpout, LANG("File %s created containing %d random bytes.\n"),
1063 		inputfile, makerandom);
1064 	/* If we aren't encrypting, don't bother trying to decrypt this! */
1065 	if (decrypt_mode)
1066 		exitPGP(EXIT_OK);
1067 
1068 	/* This is obviously NOT a text file */
1069 	literal_mode = MODE_BINARY;
1070     } else {
1071 	if (decrypt_mode && no_extension(inputfile)) {
1072 	    strcpy(cipherfile, inputfile);
1073 	    force_extension(cipherfile, ASC_EXTENSION);
1074 	    if (file_exists(cipherfile)) {
1075 		inputfile = cipherfile;
1076 	    } else {
1077 		force_extension(cipherfile, PGP_EXTENSION);
1078 		if (file_exists(cipherfile)) {
1079 		    inputfile = cipherfile;
1080 		} else {
1081 		    force_extension(cipherfile, SIG_EXTENSION);
1082 		    if (file_exists(cipherfile))
1083 			inputfile = cipherfile;
1084 		}
1085 	    }
1086 	}
1087 	if (!file_exists(inputfile)) {
1088 	    fprintf(pgpout,
1089 		    LANG("\n\007File '%s' does not exist.\n"), inputfile);
1090 	    errorLvl = FILE_NOT_FOUND_ERROR;
1091 	    user_error();
1092 	}
1093     }
1094 
1095     if (strlen(inputfile) >= (unsigned) MAX_PATH - 4) {
1096 	fprintf(pgpout,
1097 		LANG("\007Invalid filename: '%s' too long\n"), inputfile);
1098 	errorLvl = INVALID_FILE_ERROR;
1099 	user_error();
1100     }
1101     strcpy(plainfile, inputfile);
1102 
1103     if (filter_mode) {
1104 	setoutdir(NULL);	/* NULL means use tmpdir */
1105     } else {
1106 	if (outputfile)
1107 	    setoutdir(outputfile);
1108 	else
1109 	    setoutdir(inputfile);
1110     }
1111 
1112     if (filter_mode) {
1113 	workfile = tempfile(TMP_WIPE | TMP_TMPDIR);
1114 	readPhantomInput(workfile);
1115     } else {
1116 	workfile = inputfile;
1117     }
1118 
1119     get_header_info_from_file(workfile, &ctb, 1);
1120     if (decrypt_mode) {
1121 	strip_spaces = FALSE;
1122 	if (!is_ctb(ctb) && is_armor_file(workfile, 0L))
1123 	    do_armorfile(workfile);
1124 	else if (do_decrypt(workfile) < 0)
1125 	    user_error();
1126 #ifdef MACTC5
1127 	if (verbose) fprintf(stderr, "Final file = %s.\n", plainfile);
1128 	/* Allow for overide of auto-unmacbin : 205b */
1129 	if( (macbin_flag == FALSE) && is_macbin(plainfile) )
1130 		bin2mac(plainfile,TRUE);
1131 	else {
1132 		AddOutputFiles(plainfile);
1133 		PGPSetFinfo(plainfile,FType,FCreator);
1134 	}
1135 	if (use_clipboard) File2Scrap(plainfile);
1136 #endif
1137 	if (batchmode && !signature_checked)
1138 	    exitPGP(1);		/* alternate success, file did not have sig. */
1139 	else
1140 	    exitPGP(EXIT_OK);
1141     }
1142     /*
1143      * See if plaintext input file was actually created by PGP earlier--
1144      * If it was, maybe we should NOT encapsulate it in a literal packet.
1145      * (nestflag = TRUE).  Otherwise, always encapsulate it (default).
1146      * (Why test for filter_mode???)
1147      */
1148     if (!batchmode && !filter_mode && legal_ctb(ctb)) {
1149 	/*      Special case--may be a PGP-created packet, so
1150 	   do we inhibit encapsulation in literal packet? */
1151 	fprintf(pgpout,
1152 LANG("\n\007Input file '%s' looks like it may have been created by PGP. "),
1153 		inputfile);
1154 	fprintf(pgpout,
1155 LANG("\nIs it safe to assume that it was created by PGP (y/N)? "));
1156 	nestflag = getyesno('n');
1157     } else if (force_flag && makerandom == 0 && legal_ctb(ctb)) {
1158 	nestflag = TRUE;
1159     }
1160 
1161     if (moreflag && makerandom == 0) {
1162 	/* special name to cause printout on decrypt */
1163 	strcpy(literal_file_name, CONSOLE_FILENAME);
1164 	literal_mode = MODE_TEXT;	/* will check for text file later */
1165     } else {
1166 	strcpy(literal_file_name, file_tail(inputfile));
1167 #ifdef MSDOS
1168 	strlwr(literal_file_name);
1169 #endif
1170     }
1171     literal_file = literal_file_name;
1172 
1173     /*      Make sure non-text files are not accidentally converted
1174        to canonical text.  This precaution should only be followed
1175        for US ASCII text files, since European text files may have
1176        8-bit character codes and still be legitimate text files
1177        suitable for conversion to canonical (CR/LF-terminated)
1178        text format. */
1179     if (literal_mode == MODE_TEXT && !is_text_file(workfile)) {
1180 	fprintf(pgpout,
1181 LANG("\nNote: '%s' is not a pure text file.\n\
1182 File will be treated as binary data.\n"),
1183 		workfile);
1184 	literal_mode = MODE_BINARY;	/* now expect straight binary */
1185     }
1186     if (moreflag && literal_mode == MODE_BINARY) {
1187 	/* For eyes only?  Can't display binary file. */
1188 	fprintf(pgpout,
1189 LANG("\n\007Error: Only text files may be sent as display-only.\n"));
1190 	errorLvl = INVALID_FILE_ERROR;
1191 	user_error();
1192     }
1193 
1194     /*
1195      * See if plainfile looks like it might be incompressible,
1196      * by examining its contents for compression headers for
1197      * commonly-used compressed file formats like PKZIP, etc.
1198      * Remember this information for later, when we are deciding
1199      * whether to attempt compression before encryption.
1200      *
1201      * Naturally, don't bother if we are making a separate signature or
1202      * clear-signed message.  Also, don't bother trying to compress a
1203      * PGP message, as it's probably already compressed.
1204      */
1205     attempt_compression = compress_enabled && !separate_signature &&
1206                           !nestflag && !clearfile && makerandom == 0 &&
1207                           file_compressible(plainfile);
1208 
1209 #ifdef MACTC5
1210 	if(( macbin_flag == TRUE ) && (nestflag==FALSE)) {
1211 		char *saveworkfile;
1212 		nestflag = false;
1213 		saveworkfile = workfile;
1214 		workfile = tempfile(TMP_WIPE|TMP_TMPDIR);
1215 		if (mac2bin(saveworkfile, workfile)!=0) {
1216 			fprintf(pgpout, LANG("\n\007Error: MacBinary failed!\n"));
1217 			errorLvl = INVALID_FILE_ERROR;
1218 			rmtemp(workfile);
1219 			exitPGP(errorLvl);
1220 		}
1221 	}
1222 #endif
1223     if (sign_flag) {
1224 	if (!filter_mode && !quietmode)
1225 	    fprintf(pgpout,
1226 LANG("\nA secret key is required to make a signature. "));
1227 	if (!quietmode && my_name[0] == '\0') {
1228 	    fprintf(pgpout,
1229 LANG("\nYou specified no user ID to select your secret key,\n\
1230 so the default user ID and key will be the most recently\n\
1231 added key on your secret keyring.\n"));
1232 	}
1233 	strip_spaces = FALSE;
1234 	clearfile = NULL;
1235 	if (literal_mode == MODE_TEXT) {
1236 	    /* Text mode requires becoming canonical */
1237 	    tempf = tempfile(TMP_WIPE | TMP_TMPDIR);
1238 	    /* +clear means output file with signature in the clear,
1239 	       only in combination with -t and -a, not with -e or -b */
1240 	    if (!encrypt_flag && !separate_signature &&
1241 		emit_radix_64 && clear_signatures) {
1242 		clearfile = workfile;
1243 		strip_spaces = TRUE;
1244 	    }
1245 	    make_canonical(workfile, tempf);
1246 	    if (!clearfile)
1247 		rmtemp(workfile);
1248 	    workfile = tempf;
1249 	}
1250 	if (attempt_compression || encrypt_flag || emit_radix_64 ||
1251 	    output_stdout)
1252 	    tempf = tempfile(TMP_WIPE | TMP_TMPDIR);
1253 	else
1254 	    tempf = tempfile(TMP_WIPE);
1255 	/* for clear signatures we create a separate signature */
1256 	status = signfile(nestflag, separate_signature || (clearfile != NULL),
1257 		   my_name, workfile, tempf, literal_mode, literal_file);
1258 	rmtemp(workfile);
1259 	workfile = tempf;
1260 
1261 	if (status < 0) {	/* signfile failed */
1262 	    fprintf(pgpout, LANG("\007Signature error\n"));
1263 	    errorLvl = SIGNATURE_ERROR;
1264 	    user_error();
1265 	}
1266     } else if (!nestflag) {	/* !sign_file */
1267 	/*      Prepend CTB_LITERAL byte to plaintext file.
1268 	   --sure wish this pass could be optimized away. */
1269 	if (attempt_compression || encrypt_flag || emit_radix_64 ||
1270 	    output_stdout)
1271 	    tempf = tempfile(TMP_WIPE | TMP_TMPDIR);
1272 	else
1273 	    tempf = tempfile(TMP_WIPE);
1274 	/* for clear signatures we create a separate signature */
1275 	status = make_literal(workfile, tempf, literal_mode, literal_file);
1276 	rmtemp(workfile);
1277 	workfile = tempf;
1278     }
1279 
1280     if (encrypt_flag) {
1281         if (emit_radix_64 || output_stdout)
1282 	    tempf = tempfile(TMP_WIPE | TMP_TMPDIR);
1283 	else
1284 	    tempf = tempfile(TMP_WIPE);
1285 	if (!conventional_flag) {
1286 	    if (!filter_mode && !quietmode)
1287 		fprintf(pgpout,
1288 LANG("\n\nRecipients' public key(s) will be used to encrypt. "));
1289 	    if (recipient == NULL || *recipient == NULL ||
1290 		**recipient == '\0') {
1291 		/* no recipient specified on command line */
1292 		fprintf(pgpout,
1293 LANG("\nA user ID is required to select the recipient's public key. "));
1294 		fprintf(pgpout, LANG("\nEnter the recipient's user ID: "));
1295 #ifdef AMIGA
1296                 requesterdesc=LANG("\nEnter the recipient's user ID: ");
1297 #endif
1298 		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
1299 		if ((mcguffins = (char **) malloc(2 * sizeof(char *))) == NULL)
1300 		{
1301 		    fprintf(stderr, LANG("\n\007Out of memory.\n"));
1302 		    exitPGP(7);
1303 		}
1304 		mcguffins[0] = mcguffin;
1305 		mcguffins[1] = "";
1306 	    } else {
1307 		/* recipient specified on command line */
1308 		mcguffins = recipient;
1309 	    }
1310 	    for (recipient = mcguffins; *recipient != NULL &&
1311 		 **recipient != '\0'; recipient++) {
1312 		CONVERT_TO_CANONICAL_CHARSET(*recipient);
1313 	    }
1314 	    status = encryptfile(mcguffins, workfile, tempf,
1315 				 attempt_compression);
1316 	} else {
1317 	    status = idea_encryptfile(workfile, tempf, attempt_compression);
1318 	}
1319 
1320 	rmtemp(workfile);
1321 	workfile = tempf;
1322 
1323 	if (status < 0) {
1324 	    fprintf(pgpout, LANG("\007Encryption error\n"));
1325 	    errorLvl = (conventional_flag ? ENCR_ERROR : RSA_ENCR_ERROR);
1326 	    user_error();
1327 	}
1328     } else if (attempt_compression && !separate_signature && !clearfile) {
1329 	/*
1330 	 * PGP used to be parsimonious about compression; originally, it only
1331 	 * did it for files that were being encrypted (to reduce the
1332 	 * redundancy in the plaintext), but it should really do it for
1333 	 * anything where it's not a bad idea.
1334 	 */
1335         if (emit_radix_64 || output_stdout)
1336 	    tempf = tempfile(TMP_WIPE | TMP_TMPDIR);
1337 	else
1338 	    tempf = tempfile(TMP_WIPE);
1339 	squish_file(workfile, tempf);
1340 	rmtemp(workfile);
1341 	workfile = tempf;
1342     }
1343 
1344     /*
1345      * Write to stdout if explicitly asked to, or in filter mode and
1346      * no explicit file name was given.
1347      */
1348     if (output_stdout) {
1349 	if (emit_radix_64) {
1350 	    /* NULL for outputfile means write to stdout */
1351 	    if (armor_file(workfile, NULL, inputfile, clearfile, FALSE) != 0) {
1352 		errorLvl = UNKNOWN_FILE_ERROR;
1353 		user_error();
1354 	    }
1355 	    if (clearfile)
1356 		rmtemp(clearfile);
1357 	} else {
1358 	    if (writePhantomOutput(workfile) < 0) {
1359 		errorLvl = UNKNOWN_FILE_ERROR;
1360 		user_error();
1361 	    }
1362 	}
1363 	rmtemp(workfile);
1364     } else {
1365 	char name[MAX_PATH];
1366         char *t;
1367 	if (outputfile) {
1368 	    strcpy(name, outputfile);
1369 	} else {
1370 	    strcpy(name, inputfile);
1371 	    drop_extension(name);
1372 	}
1373         do {
1374 	    if (!outputfile && no_extension(name)) {
1375 	        if (emit_radix_64)
1376 		    force_extension(name, ASC_EXTENSION);
1377 	        else if (sign_flag && separate_signature)
1378 		    force_extension(name, SIG_EXTENSION);
1379 	        else
1380 		    force_extension(name, PGP_EXTENSION);
1381 #ifdef MACTC5
1382 			if (addresfork) {
1383 				drop_extension(name);
1384 				force_extension(name, ".sdf");
1385 			}
1386 #endif
1387 	    }
1388             if (!file_exists(name)) break;
1389             t=ck_dup_output(name, TRUE, !clearfile);
1390             if (t==NULL) user_error();
1391             if (clearfile && !strcmp(t,name)) break;
1392             strcpy(name,t);
1393         } while (TRUE);
1394 	if (emit_radix_64) {
1395 	    if (armor_file(workfile, name, inputfile, clearfile, FALSE) != 0) {
1396 		errorLvl = UNKNOWN_FILE_ERROR;
1397 		user_error();
1398 	    }
1399 	    if (clearfile)
1400 		rmtemp(clearfile);
1401 	} else {
1402 	    if ((outputfile = savetemp(workfile, name)) == NULL) {
1403 		errorLvl = UNKNOWN_FILE_ERROR;
1404 		user_error();
1405 	    }
1406 	    if (!quietmode) {
1407 		if (encrypt_flag)
1408 		    fprintf(pgpout,
1409 			    LANG("\nCiphertext file: %s\n"), outputfile);
1410 		else if (sign_flag)
1411 		    fprintf(pgpout,
1412 			    LANG("\nSignature file: %s\n"), outputfile);
1413 	    }
1414 	}
1415 #ifdef MACTC5
1416 		AddOutputFiles(name);
1417 		if (addresfork) {
1418 			if(!AddResourceFork(name)) {
1419 				short frefnum,len,i;
1420 				char *p,*q;
1421 				Handle h;
1422 				c2pstr(name);
1423 				q=file_tail(argv[2]);
1424 				len=strlen(q);
1425 				frefnum=OpenResFile((uchar *)name);
1426 				h=NewHandle(len+1);
1427 				HLock(h);
1428 				p=*h;
1429 				*p++=len;
1430 				for (i=0; i<len; i++) *p++=*q++;
1431 				AddResource(h,'STR ',500,(uchar *)"");
1432 				ChangedResource(h);
1433 				WriteResource(h);
1434 				UpdateResFile(frefnum);
1435 				CloseResFile(frefnum);
1436 				p2cstr((uchar *)name);
1437 			} else {
1438 				BailoutAlert("AddResFork failed!");
1439 				exitPGP(-1);
1440 			}
1441 		}
1442 		if (binhex_flag) {
1443 			if (binhex(name)) {
1444 				BailoutAlert("BinHex failed!");
1445 				exitPGP(-1);
1446 			}
1447 			remove(name);
1448 		}
1449 		if (use_clipboard) File2Scrap(name);
1450 #endif /* MACTC5 */
1451     }
1452 
1453     if (wipeflag) {
1454 	/* destroy every trace of plaintext */
1455 	if (wipefile(inputfile) == 0) {
1456 	    remove(inputfile);
1457 	    fprintf(pgpout, LANG("\nFile %s wiped and deleted. "), inputfile);
1458 	    fprintf(pgpout, "\n");
1459 	}
1460     }
1461 
1462 #ifdef MACTC5
1463 	if(!addresfork && !use_clipboard)
1464 		if (!emit_radix_64) PGPSetFinfo(outputfile,'Cryp','MPGP');
1465 #endif
1466 
1467     exitPGP(EXIT_OK);
1468     return 0;			/* to shut up lint and some compilers */
1469 #ifdef MACTC5
1470 }				/* pgp_dispatch */
1471 #else
1472 }				/* main */
1473 #endif
1474 
1475 #ifdef MSDOS
1476 #include <dos.h>
1477 static char *dos_errlst[] =
1478 {
1479     "Write protect error",	/* LANG ("Write protect error") */
1480     "Unknown unit",
1481     "Drive not ready",		/* LANG ("Drive not ready") */
1482     "3", "4", "5", "6", "7", "8", "9",
1483     "Write error",		/* LANG ("Write error") */
1484     "Read error",		/* LANG ("Read error") */
1485     "General failure",
1486 };
1487 
1488 /* handler for msdos 'harderrors' */
1489 #ifndef OS2
1490 #ifdef __TURBOC__		/* Turbo C 2.0 */
dostrap(int errval)1491 static int dostrap(int errval)
1492 #else
1493 static void dostrap(unsigned deverr, unsigned errval)
1494 #endif
1495 {
1496     char errbuf[64];
1497     int i;
1498     sprintf(errbuf, "\r\nDOS error: %s\r\n", dos_errlst[errval]);
1499     i = 0;
1500     do
1501 	bdos(2, (unsigned int) errbuf[i], 0);
1502     while (errbuf[++i]);
1503 #ifdef __TURBOC__
1504     return 0;			/* ignore (fopen will return NULL) */
1505 #else
1506     return;
1507 #endif
1508 }
1509 #endif				/* MSDOS */
1510 #endif
1511 
initsigs()1512 static void initsigs()
1513 {
1514 #ifdef MSDOS
1515 #ifndef OS2
1516 #ifdef __TURBOC__
1517     harderr(dostrap);
1518 #else				/* MSC */
1519 #ifndef __GNUC__		/* DJGPP's not MSC */
1520     _harderr(dostrap);
1521 #endif
1522 #endif
1523 #endif
1524 #endif				/* MSDOS */
1525 #ifdef SIGINT
1526     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1527 	signal(SIGINT, breakHandler);
1528 #if defined(UNIX) || defined(VMS) || defined(ATARI)
1529 #ifndef __PUREC__ /* PureC doesn't recognise all signals */
1530     if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
1531 	signal(SIGHUP, breakHandler);
1532     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
1533 	signal(SIGQUIT, breakHandler);
1534 #endif
1535 #ifdef UNIX
1536     signal(SIGPIPE, breakHandler);
1537 #endif
1538     signal(SIGTERM, breakHandler);
1539 #ifdef MACTC5
1540 	signal(SIGABRT,breakHandler);
1541 	signal(SIGTERM,breakHandler);
1542 #ifndef DEBUG
1543     signal(SIGTRAP, breakHandler);
1544     signal(SIGSEGV, breakHandler);
1545     signal(SIGILL, breakHandler);
1546 #ifdef SIGBUS
1547     signal(SIGBUS, breakHandler);
1548 #endif
1549 #endif				/* DEBUG */
1550 #endif				/* MACTC5 */
1551 #endif				/* UNIX */
1552 #endif				/* SIGINT */
1553 }				/* initsigs */
1554 
1555 
do_armorfile(char * armorfile)1556 static void do_armorfile(char *armorfile)
1557 {
1558     char *tempf;
1559     char cipherfile[MAX_PATH];
1560     long lastpos, linepos = 0;
1561     int status;
1562     int success = 0;
1563 
1564     for (;;) {
1565 	/* Handle transport armor stripping */
1566 	tempf = tempfile(0);
1567 	strip_spaces = FALSE;	/* de_armor_file() sets
1568 				   this for clear signature files */
1569         use_charset_header = TRUE;
1570         lastpos = linepos;
1571 	status = de_armor_file(armorfile, tempf, &linepos);
1572 	if (status) {
1573 	    fprintf(pgpout,
1574 LANG("\n\007Error: Transport armor stripping failed for file %s\n"),
1575 		    armorfile);
1576 	    errorLvl = INVALID_FILE_ERROR;
1577 	    user_error();	/* Bad file */
1578 	}
1579 	if (keepctx || de_armor_only) {
1580 	    if (outputfile && de_armor_only) {
1581 		if (strcmp(outputfile, "-") == 0) {
1582 		    writePhantomOutput(tempf);
1583 		    rmtemp(tempf);
1584 		    return;
1585 		}
1586 		strcpy(cipherfile, outputfile);
1587 	    } else {
1588 		strcpy(cipherfile, file_tail(armorfile));
1589 		force_extension(cipherfile, PGP_EXTENSION);
1590 	    }
1591 	    if ((tempf = savetemp(tempf, cipherfile)) == NULL) {
1592 		errorLvl = UNKNOWN_FILE_ERROR;
1593 		user_error();
1594 	    }
1595 	    if (!quietmode)
1596 		fprintf(pgpout,
1597 LANG("Stripped transport armor from '%s', producing '%s'.\n"),
1598 			armorfile, tempf);
1599 	    /* -da flag: don't decrypt */
1600 	    if (de_armor_only || do_decrypt(tempf) >= 0)
1601 		++success;
1602 	} else {
1603 	    if (charset_header[0])        /* Check signature with charset from Charset: header */
1604 		checksig_pass = 1;
1605 	    if (do_decrypt(tempf) >= 0)
1606 		++success;
1607 	    rmtemp(tempf);
1608 	    if (charset_header[0]) {
1609 		if (checksig_pass == 2) { /* Sigcheck failed: try again with local charset */
1610 		    tempf = tempfile(0);
1611 		    use_charset_header = FALSE;
1612 		    linepos = lastpos;
1613 		    de_armor_file(armorfile, tempf, &linepos);
1614 		    if (do_decrypt(tempf) >= 0)
1615 		        ++success;
1616 		    rmtemp(tempf);
1617 		}
1618 		checksig_pass = 0;
1619 	    }
1620 	}
1621 
1622 	if (!is_armor_file(armorfile, linepos)) {
1623 	    if (!success)	/* print error msg if we didn't
1624 				   decrypt anything */
1625 		user_error();
1626 	    return;
1627 	}
1628 	fprintf(pgpout,
1629 		LANG("\nLooking for next packet in '%s'...\n"), armorfile);
1630     }
1631 }				/* do_armorfile */
1632 
1633 
do_decrypt(char * cipherfile)1634 static int do_decrypt(char *cipherfile)
1635 {
1636     char *outfile = NULL;
1637     int status, i;
1638     boolean nested_info = FALSE;
1639     char ringfile[MAX_PATH];
1640     byte ctb;
1641     byte header[8];		/* used to classify file type at the end. */
1642     char preserved_name[MAX_PATH];
1643     char *newname;
1644 
1645     /* will be set to the original file name after processing a
1646        literal packet */
1647     preserved_name[0] = '\0';
1648 
1649     do {			/* while nested parsable info present */
1650 	if (nested_info) {
1651 	    rmtemp(cipherfile);	/* never executed on first pass */
1652 	    cipherfile = outfile;
1653 	}
1654 	if (get_header_info_from_file(cipherfile, &ctb, 1) < 0) {
1655 	    fprintf(pgpout,
1656 LANG("\n\007Can't open ciphertext file '%s'\n"), cipherfile);
1657 	    errorLvl = FILE_NOT_FOUND_ERROR;
1658 	    return -1;
1659 	}
1660 	if (!is_ctb(ctb))	/* not a real CTB -- complain */
1661 	    break;
1662 
1663 	if (moreflag)
1664 	    outfile = tempfile(TMP_WIPE | TMP_TMPDIR);
1665 	else
1666 	    outfile = tempfile(TMP_WIPE);
1667 
1668 	/* PKE is Public Key Encryption */
1669 	if (is_ctb_type(ctb, CTB_PKE_TYPE)) {
1670 
1671 	    if (!quietmode)
1672 		fprintf(pgpout,
1673 LANG("\nFile is encrypted.  Secret key is required to read it. "));
1674 
1675 	    /* Decrypt to scratch file since we may have a LITERAL2 */
1676 	    status = decryptfile(cipherfile, outfile);
1677 
1678 	    if (status < 0) {	/* error return */
1679 		errorLvl = RSA_DECR_ERROR;
1680 		return -1;
1681 	    }
1682 	    nested_info = (status > 0);
1683 
1684 	} else if (is_ctb_type(ctb, CTB_SKE_TYPE)) {
1685 
1686 	    if (decrypt_only_flag) {
1687 		/* swap file names instead of just copying the file */
1688 		rmtemp(outfile);
1689 		outfile = cipherfile;
1690 		cipherfile = NULL;
1691 		if (!quietmode)
1692 		    fprintf(pgpout,
1693 LANG("\nThis file has a signature, which will be left in place.\n"));
1694 		nested_info = FALSE;
1695 		break;		/* Do no more */
1696 	    }
1697 	    if (!quietmode && checksig_pass<=1)
1698 		fprintf(pgpout,
1699 LANG("\nFile has signature.  Public key is required to check signature.\n"));
1700 
1701 	    status = check_signaturefile(cipherfile, outfile,
1702 					 strip_sig_flag, preserved_name);
1703 
1704 	    if (status < 0) {	/* error return */
1705 		errorLvl = SIGNATURE_CHECK_ERROR;
1706 		return -1;
1707 	    }
1708 	    nested_info = (status > 0);
1709 
1710 	    if (strcmp(preserved_name, "/dev/null") == 0) {
1711 		rmtemp(outfile);
1712 		fprintf(pgpout, "\n");
1713 		return 0;
1714 	    }
1715 	} else if (is_ctb_type(ctb, CTB_CKE_TYPE)) {
1716 
1717 	    /* Conventional Key Encrypted ciphertext. */
1718 	    /* Tell user it's encrypted here, and prompt for
1719 	       password in subroutine. */
1720 	    if (!quietmode)
1721 		fprintf(pgpout, LANG("\nFile is conventionally encrypted.  "));
1722 	    /* Decrypt to scratch file since it may be a LITERAL2 */
1723 	    status = idea_decryptfile(cipherfile, outfile);
1724 	    if (status < 0) {	/* error return */
1725 		errorLvl = DECR_ERROR;
1726 		return -1;	/* error exit status */
1727 	    }
1728 	    nested_info = (status > 0);
1729 
1730 	} else if (is_ctb_type(ctb, CTB_COMPRESSED_TYPE)) {
1731 
1732 	    /* Compressed text. */
1733 	    status = decompress_file(cipherfile, outfile);
1734 	    if (status < 0) {	/* error return */
1735 		errorLvl = DECOMPRESS_ERROR;
1736 		return -1;
1737 	    }
1738 	    /* Always assume nested information... */
1739 	    nested_info = TRUE;
1740 
1741 	} else if (is_ctb_type(ctb, CTB_LITERAL_TYPE) ||
1742 		   is_ctb_type(ctb, CTB_LITERAL2_TYPE)) { /* Raw plaintext.
1743 							     Just copy it.
1744 							     No more nesting.
1745 							   */
1746 
1747 	    /* Strip off CTB_LITERAL prefix byte from file: */
1748 	    /* strip_literal may alter plainfile; will set mode */
1749 	    status = strip_literal(cipherfile, outfile,
1750 				   preserved_name, &literal_mode);
1751 	    if (status < 0) {	/* error return */
1752 		errorLvl = UNKNOWN_FILE_ERROR;
1753 		return -1;
1754 	    }
1755 	    nested_info = FALSE;
1756 	} else if (ctb == CTB_CERT_SECKEY || ctb == CTB_CERT_PUBKEY) {
1757 
1758 	    rmtemp(outfile);
1759 	    if (decrypt_only_flag) {
1760 		/* swap file names instead of just copying the file */
1761 		outfile = cipherfile;
1762 		cipherfile = NULL;
1763 		nested_info = FALSE;	/* No error */
1764 		break;		/* no further processing */
1765 	    }
1766 	    /* Key ring.  View it. */
1767 	    fprintf(pgpout,
1768 LANG("\nFile contains key(s).  Contents follow..."));
1769 	    if (view_keyring(NULL, cipherfile, TRUE, FALSE) < 0) {
1770 		errorLvl = KEYRING_VIEW_ERROR;
1771 		return -1;
1772 	    }
1773 	    /* filter mode explicit requested with -f */
1774 	    if (filter_mode && !preserve_filename)
1775 		return 0;	/*    No output file */
1776 	    if (batchmode)
1777 		return 0;
1778 	    if (ctb == CTB_CERT_SECKEY)
1779 		strcpy(ringfile, globalSecringName);
1780 	    else
1781 		strcpy(ringfile, globalPubringName);
1782 	    /*      Ask if it should be put on key ring */
1783 	    fprintf(pgpout,
1784 LANG("\nDo you want to add this keyfile to keyring '%s' (y/N)? "), ringfile);
1785 	    if (!getyesno('n'))
1786 		return 0;
1787 	    status = addto_keyring(cipherfile, ringfile);
1788 	    if (status < 0) {
1789 		fprintf(pgpout, LANG("\007Keyring add error. "));
1790 		errorLvl = KEYRING_ADD_ERROR;
1791 		return -1;
1792 	    }
1793 	    return 0;		/*    No output file */
1794 
1795 	} else {		/* Unrecognized CTB */
1796 	    break;
1797 	}
1798 
1799     } while (nested_info);
1800     /* No more nested parsable information */
1801 
1802     /* Stopped early due to error */
1803     if (nested_info) {
1804 	fprintf(pgpout,
1805 "\7\nERROR: Nested data has unexpected format.  CTB=0x%02X\n", ctb);
1806 	if (outfile)
1807 	    rmtemp(outfile);
1808 	if (cipherfile)
1809 	    rmtemp(cipherfile);
1810 	errorLvl = UNKNOWN_FILE_ERROR;
1811 	return -1;
1812     }
1813     if (outfile == NULL) {	/* file was not encrypted */
1814 	if (!filter_mode && !moreflag) {
1815 	    fprintf(pgpout,
1816 LANG("\007\nError: '%s' is not a ciphertext, signature, or key file.\n"),
1817 		    cipherfile);
1818 	    errorLvl = UNKNOWN_FILE_ERROR;
1819 	    return -1;
1820 	}
1821 	outfile = cipherfile;
1822     } else {
1823 	if (cipherfile)
1824 	    rmtemp(cipherfile);
1825     }
1826 
1827     if (moreflag || (strcmp(preserved_name, CONSOLE_FILENAME) == 0)) {
1828 	/* blort to screen */
1829 	if (strcmp(preserved_name, CONSOLE_FILENAME) == 0) {
1830 	    fprintf(pgpout,
1831 LANG("\n\nThis message is marked \"For your eyes only\".  Display now \
1832 (Y/n)? "));
1833 	    if (batchmode
1834 #ifdef UNIX
1835             || !isatty(fileno(stdout))	/* stdout is redirected! */
1836 #endif
1837             || filter_mode || !getyesno('y')) {
1838 		/* no -- abort display, and clean up */
1839                 fprintf(pgpout, "\n");
1840 		rmtemp(outfile);
1841 		return 0;
1842 	    }
1843 	}
1844 	if (!quietmode)
1845 	    fprintf(pgpout, LANG("\n\nPlaintext message follows...\n"));
1846 	else
1847 	    putc('\n', pgpout);
1848 	fprintf(pgpout, "------------------------------\n");
1849 	more_file(outfile, strcmp(preserved_name, CONSOLE_FILENAME) == 0);
1850 	/* Disallow saving to disk if outfile is console-only: */
1851 	if (strcmp(preserved_name, CONSOLE_FILENAME) == 0) {
1852 	    clearscreen();	/* remove all evidence */
1853 	} else if (!quietmode && !batchmode) {
1854 	    fprintf(pgpout, LANG("Save this file permanently (y/N)? "));
1855 	    if (getyesno('n')) {
1856 		char moreFilename[256];
1857 		fprintf(pgpout, LANG("Enter filename to save file as: "));
1858 #ifdef AMIGA
1859                 requesterdesc=LANG("Enter filename to save file as: ");
1860 #endif
1861                 if (preserved_name[0]) {
1862 		    fprintf(pgpout, "[%s]: ", file_tail(preserved_name));
1863 #ifdef AMIGA
1864                     strcat(requesterdesc, "[");
1865                     strcat(requesterdesc, file_tail(preserved_name));
1866                     strcat(requesterdesc, "]:");
1867 #endif
1868                 }
1869 #ifdef MACTC5
1870 		if(!GetFilePath(LANG("Enter filename to save file as:"),moreFilename,PUTFILE))
1871 			strcpy(moreFilename,"");
1872 		else
1873 			fprintf(pgpout, "%s\n",moreFilename);
1874 #else
1875 		getstring(moreFilename, 255, TRUE);
1876 #endif
1877 		if (*moreFilename == '\0') {
1878 		    if (*preserved_name != '\0')
1879 			savetemp(outfile, file_tail(preserved_name));
1880 		    else
1881 			rmtemp(outfile);
1882 		} else
1883 		    savetemp(outfile, moreFilename);
1884 		return 0;
1885 	    }
1886 	}
1887 	rmtemp(outfile);
1888 	return 0;
1889     }				/* blort to screen */
1890     if (outputfile) {
1891 	if (!strcmp(outputfile, "/dev/null")) {
1892 	    rmtemp(outfile);
1893 	    return 0;
1894 	}
1895 	filter_mode = (strcmp(outputfile, "-") == 0);
1896 	strcpy(plainfile, outputfile);
1897     } else {
1898 #ifdef VMS
1899 	/* VMS null extension has to be ".", not "" */
1900 	force_extension(plainfile, ".");
1901 #else				/* not VMS */
1902 	drop_extension(plainfile);
1903 #endif				/* not VMS */
1904     }
1905 
1906     if (!preserve_filename && filter_mode) {
1907 	if (writePhantomOutput(outfile) < 0) {
1908 	    errorLvl = UNKNOWN_FILE_ERROR;
1909 	    return -1;
1910 	}
1911 	rmtemp(outfile);
1912 	return 0;
1913     }
1914     if (preserve_filename && preserved_name[0] != '\0')
1915 	strcpy(plainfile, file_tail(preserved_name));
1916 
1917     if (quietmode) {
1918 	if (savetemp(outfile, plainfile) == NULL) {
1919 	    errorLvl = UNKNOWN_FILE_ERROR;
1920 	    return -1;
1921 	}
1922 	return 0;
1923     }
1924     if (!verbose)	       /* if other filename messages were suppressed */
1925 	fprintf(pgpout, LANG("\nPlaintext filename: %s"), plainfile);
1926 
1927 
1928 /*---------------------------------------------------------*/
1929 
1930     /*      One last thing-- let's attempt to classify some of the more
1931        frequently occurring cases of plaintext output files, as an
1932        aid to the user.
1933 
1934        For example, if output file is a public key, it should have
1935        the right extension on the filename.
1936 
1937        Also, it will likely be common to encrypt files created by
1938        various archivers, so they should be renamed with the archiver
1939        extension.
1940      */
1941     get_header_info_from_file(outfile, header, 8);
1942 
1943     newname = NULL;
1944 #ifdef MACTC5
1945 	if (header[0] == CTB_CERT_SECKEY)
1946 		PGPSetFinfo(plainfile,'SKey','MPGP');
1947 #endif
1948     if (header[0] == CTB_CERT_PUBKEY) {
1949 	/* Special case--may be public key, worth renaming */
1950 #ifdef MACTC5
1951 		PGPSetFinfo(plainfile,'PKey','MPGP');
1952 #endif
1953 	fprintf(pgpout,
1954 LANG("\nPlaintext file '%s' looks like it contains a public key."),
1955 		plainfile);
1956 	newname = maybe_force_extension(plainfile, PGP_EXTENSION);
1957     }
1958     /* Possible public key output file */
1959     else if ((i = compressSignature(header)) >= 0) {
1960 	/*      Special case--may be an archived/compressed file,
1961 		worth renaming
1962 	*/
1963 	fprintf(pgpout, LANG("\nPlaintext file '%s' looks like a %s file."),
1964 		plainfile, compressName[i]);
1965 	newname = maybe_force_extension(plainfile, compressExt[i]);
1966     } else if (is_ctb(header[0]) &&
1967 	       (is_ctb_type(header[0], CTB_PKE_TYPE)
1968 		|| is_ctb_type(header[0], CTB_SKE_TYPE)
1969 		|| is_ctb_type(header[0], CTB_CKE_TYPE))) {
1970 	/* Special case--may be another ciphertext file, worth renaming */
1971 	fprintf(pgpout,
1972 LANG("\n\007Output file '%s' may contain more ciphertext or signature."),
1973 		plainfile);
1974 	newname = maybe_force_extension(plainfile, PGP_EXTENSION);
1975     }				/* Possible ciphertext output file */
1976 #ifdef MACTC5
1977 	if( (newname = savetemp(outfile, (newname ? newname : plainfile))) == NULL) {
1978 #else
1979     if (savetemp(outfile, (newname ? newname : plainfile)) == NULL) {
1980 #endif
1981 	errorLvl = UNKNOWN_FILE_ERROR;
1982 	return -1;
1983     }
1984 #ifdef MACTC5
1985 	else if( strcmp(newname, plainfile) != 0 )	/* 203a */
1986 		strcpy(plainfile, newname);
1987 #endif
1988     fprintf(pgpout, "\n");
1989     return 0;
1990 }				/* do_decrypt */
1991 
1992 static int do_keyopt(char keychar)
1993 {
1994     char keyfile[MAX_PATH];
1995     char ringfile[MAX_PATH];
1996     char *workfile;
1997     int status;
1998 
1999     if ((filter_mode || batchmode) && (keychar == 'g' || keychar == 'e')) {
2000 	errorLvl = NO_BATCH;
2001 	arg_error();		/* interactive process, no go in batch mode */
2002     }
2003     /*
2004      * If we're not doing anything that uses stdout, produce output there,
2005      * in case user wants to redirect it.
2006      */
2007     if (!filter_mode)
2008 	pgpout = stdout;
2009 
2010     switch (keychar) {
2011 
2012 /*-------------------------------------------------------*/
2013     case 'g':
2014 	{		/*      Key generation
2015 			   Arguments: bitcount, bitcount, factor1, factor2, mask1, mask2
2016 			 */
2017 	    char keybits[6], ebits[6], *username = NULL;
2018 	    word32 factor1, factor2, mask1, mask2;
2019 
2020 	    /*
2021 	     * Why all this code?
2022 	     *
2023 	     * Some people may object to PGP insisting on finding the
2024 	     * manual somewhere in the neighborhood to generate a key.
2025 	     * They bristle against this seemingly authoritarian
2026 	     * attitude.  Some people have even modified PGP to defeat
2027 	     * this feature, and redistributed their hotwired version to
2028 	     * others.  That creates problems for me (PRZ).
2029 	     *
2030 	     * Here is the problem.  Before I added this feature, there
2031 	     * were maimed versions of the PGP distribution package
2032 	     * floating around that lacked the manual.  One of them was
2033 	     * uploaded to Compuserve, and was distributed to countless
2034 	     * users who called me on the phone to ask me why such a
2035 	     * complicated program had no manual.  It spread out to BBS
2036 	     * systems around the country.  And a freeware distributor got
2037 	     * hold of the package from Compuserve and enshrined it on
2038 	     * CD-ROM, distributing thousands of copies without the
2039 	     * manual.  What a mess.
2040 	     *
2041 	     * Please don't make my life harder by modifying PGP to
2042 	     * disable this feature so that others may redistribute PGP
2043 	     * without the manual.  If you run PGP on a palmtop with no
2044 	     * memory for the manual, is it too much to ask that you type
2045 	     * one little extra word on the command line to do a key
2046 	     * generation, a command that is seldom used by people who
2047 	     * already know how to use PGP?  If you can't stand even this
2048 	     * trivial inconvenience, can you suggest a better method of
2049 	     * reducing PGP's distribution without the manual?
2050 	     *
2051 	     * PLEASE DO NOT DISABLE THIS CHECK IN THE SOURCE CODE
2052 	     * WITHOUT AT LEAST CALLING PHILIP ZIMMERMANN
2053 	     * (+1 303 541-0140, or prz@acm.org) TO DISCUSS IT.
2054 	     */
2055 	    if (!nomanual && manuals_missing()) {
2056 		char const *const *dir;
2057 		fputs(LANG("\a\nError: PGP User's Guide not found.\n\
2058 PGP looked for it in the following directories:\n"), pgpout);
2059 #ifdef MACTC5
2060 		fprintf(pgpout, "\t\"%s\"\n", appPathName);
2061 #else
2062 		for (dir = manual_dirs; *dir; dir++)
2063 		    fprintf(pgpout, "\t\"%s\"\n", *dir);
2064 #endif	/* MACTC5 */
2065 		fputs(
2066 LANG("and the doc subdirectory of each of the above.  Please put a copy of\n\
2067 both volumes of the User's Guide in one of these directories.\n\
2068 \n\
2069 Under NO CIRCUMSTANCES should PGP ever be distributed without the PGP\n\
2070 User's Guide, which is included in the standard distribution package.\n\
2071 If you got a copy of PGP without the manual, please inform whomever you\n\
2072 got it from that this is an incomplete package that should not be\n\
2073 distributed further.\n\
2074 \n\
2075 PGP will not generate a key without finding the User's Guide.\n\
2076 There is a simple way to override this restriction.  See the\n\
2077 PGP User's Guide for details on how to do it.\n\
2078 \n"), pgpout);
2079 		return KEYGEN_ERROR;
2080 	    }
2081 	    if (myArgc > 2)
2082 		strncpy(keybits, myArgv[2], sizeof(keybits) - 1);
2083 	    else
2084 		keybits[0] = '\0';
2085 
2086 	    if (myArgc > 3)
2087 		strncpy(ebits, myArgv[3], sizeof(ebits) - 1);
2088 	    else
2089 		ebits[0] = '\0';
2090 
2091 	    if (myArgc > 4) {
2092 	       sscanf(myArgv[4], "%li", &factor1);
2093 	       factor2 = 1;
2094 	       mask1 = mask2 = 0xffffffffl;
2095 	    } else {
2096 	       factor1 = factor2 = mask1 = mask2 = 1;
2097 	    }
2098 	    if (myArgc > 5) sscanf(myArgv[5], "%li", &factor2);
2099 	    if (myArgc > 6) sscanf(myArgv[6], "%li", &mask1);
2100 	    if (myArgc > 7) sscanf(myArgv[7], "%li", &mask2);
2101 
2102 	    /* If the -u option is given, use that username */
2103 	    if (u_flag && my_name != NULL && *my_name != '\0')
2104 		username = my_name;
2105 
2106 	    /* dokeygen writes the keys out to the key rings... */
2107 	    status = dokeygen(keybits, ebits, factor1, factor2, mask1, mask2, username);
2108 
2109 	    if (status < 0) {
2110 		fprintf(pgpout, LANG("\007Keygen error. "));
2111 		errorLvl = KEYGEN_ERROR;
2112 	    }
2113 #ifdef MACTC5
2114 		else  {
2115 			strcpy(ringfile, globalPubringName );
2116 			PGPSetFinfo(ringfile,'PKey','MPGP');
2117 			strcpy(ringfile, globalSecringName  );
2118 			PGPSetFinfo(ringfile,'SKey','MPGP');
2119 		}
2120 #endif
2121 	    return status;
2122 	}			/* Key generation */
2123 
2124 /*-------------------------------------------------------*/
2125     case 'c':
2126 	{			/*      Key checking
2127 				   Arguments: userid, ringfile
2128 				 */
2129 
2130 	    if (myArgc < 3) {	/* Default to all user ID's */
2131 		mcguffin[0] = '\0';
2132 	    } else {
2133 		strcpy(mcguffin, myArgv[2]);
2134 		if (strcmp(mcguffin, "*") == 0)
2135 		    mcguffin[0] = '\0';
2136 	    }
2137 	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);
2138 
2139 	    if (myArgc < 4)	/* default key ring filename */
2140 		strcpy(ringfile, globalPubringName);
2141 	    else
2142 		strncpy(ringfile, myArgv[3], sizeof(ringfile) - 1);
2143 
2144 	    if ((myArgc < 4 && myArgc > 2)     /* Allow just key file as arg */
2145 		&&has_extension(myArgv[2], PGP_EXTENSION)) {
2146 		strcpy(ringfile, myArgv[2]);
2147 		mcguffin[0] = '\0';
2148 	    }
2149 	    status = dokeycheck(mcguffin, ringfile, CHECK_ALL);
2150 
2151 	    if (status < 0) {
2152 		fprintf(pgpout, LANG("\007Keyring check error.\n"));
2153 		errorLvl = KEYRING_CHECK_ERROR;
2154 	    }
2155 	    if (status >= 0 && mcguffin[0] != '\0')
2156 		return status;	/* just checking a single user,
2157 				   dont do maintenance */
2158 
2159 	    if ((status = maint_check(ringfile, 0)) < 0 && status != -7) {
2160 		fprintf(pgpout, LANG("\007Maintenance pass error. "));
2161 		errorLvl = KEYRING_CHECK_ERROR;
2162 	    }
2163 #ifdef MACTC5
2164 		{
2165 		byte ctb;
2166 		get_header_info_from_file(ringfile, &ctb, 1);
2167 		if (ctb == CTB_CERT_SECKEY)
2168 			PGPSetFinfo(ringfile,'SKey','MPGP');
2169 		else if (ctb == CTB_CERT_PUBKEY)
2170 		PGPSetFinfo(ringfile,'PKey','MPGP');
2171 		}
2172 #endif
2173 	    return status == -7 ? 0 : status;
2174 	}			/* Key check */
2175 
2176 /*-------------------------------------------------------*/
2177     case 'm':
2178 	{			/*      Maintenance pass
2179 				   Arguments: ringfile
2180 				 */
2181 
2182 	    if (myArgc < 3)	/* default key ring filename */
2183 		strcpy(ringfile, globalPubringName);
2184 	    else
2185 		strcpy(ringfile, myArgv[2]);
2186 
2187 #ifdef MSDOS
2188 	    strlwr(ringfile);
2189 #endif
2190 	    if (!file_exists(ringfile))
2191 		default_extension(ringfile, PGP_EXTENSION);
2192 
2193 	    if ((status = maint_check(ringfile,
2194 		      MAINT_VERBOSE | (c_flag ? MAINT_CHECK : 0))) < 0) {
2195 		if (status == -7)
2196 		    fprintf(pgpout,
2197 			    LANG("File '%s' is not a public keyring\n"),
2198 			    ringfile);
2199 		fprintf(pgpout, LANG("\007Maintenance pass error. "));
2200 		errorLvl = KEYRING_CHECK_ERROR;
2201 	    }
2202 #ifdef MACTC5
2203 		PGPSetFinfo(ringfile,'PKey','MPGP');
2204 #endif
2205 	    return status;
2206 	}			/* Maintenance pass */
2207 
2208 /*-------------------------------------------------------*/
2209     case 's':
2210 	{			/*      Key signing
2211 				   Arguments: her_id, keyfile
2212 				 */
2213 
2214 	    if (myArgc >= 4)
2215 		strncpy(keyfile, myArgv[3], sizeof(keyfile) - 1);
2216 	    else
2217 		strcpy(keyfile, globalPubringName);
2218 
2219 	    if (myArgc >= 3) {
2220 		strcpy(mcguffin, myArgv[2]);	/* Userid to sign */
2221 	    } else {
2222 		fprintf(pgpout,
2223 LANG("\nA user ID is required to select the public key you want to sign. "));
2224 		if (batchmode)	/* not interactive, userid
2225 				   must be on command line */
2226 		    return -1;
2227 		fprintf(pgpout, LANG("\nEnter the public key's user ID: "));
2228 #ifdef AMIGA
2229                 requesterdesc=LANG("\nEnter the public key's user ID: ");
2230 #endif
2231 		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
2232 	    }
2233 	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);
2234 
2235 	    if (my_name[0] == '\0') {
2236 		fprintf(pgpout,
2237 LANG("\nA secret key is required to make a signature. "));
2238 		fprintf(pgpout,
2239 LANG("\nYou specified no user ID to select your secret key,\n\
2240 so the default user ID and key will be the most recently\n\
2241 added key on your secret keyring.\n"));
2242 	    }
2243 	    status = signkey(mcguffin, my_name, keyfile);
2244 
2245 	    if (status >= 0) {
2246 		status = maint_update(keyfile, 0);
2247 		if (status == -7) { /* ringfile is a keyfile or
2248 				       secret keyring */
2249 		    fprintf(pgpout,
2250 			    "Warning: '%s' is not a public keyring\n",
2251 			    keyfile);
2252 		    return 0;
2253 		}
2254 		if (status < 0)
2255 		    fprintf(pgpout, LANG("\007Maintenance pass error. "));
2256 	    }
2257 	    if (status < 0) {
2258 		fprintf(pgpout, LANG("\007Key signature error. "));
2259 		errorLvl = KEY_SIGNATURE_ERROR;
2260 	    }
2261 #ifdef MACTC5
2262 		PGPSetFinfo(keyfile,'PKey','MPGP');
2263 #endif
2264 	    return status;
2265 	}			/* Key signing */
2266 
2267 
2268 /*-------------------------------------------------------*/
2269     case 'd':
2270 	{			/*  disable/revoke key/cert
2271 				   Arguments: userid, keyfile
2272 				 */
2273 
2274 	    if (myArgc >= 4)
2275 		strncpy(keyfile, myArgv[3], sizeof(keyfile) - 1);
2276 	    else
2277 		strcpy(keyfile, globalPubringName);
2278 
2279 	    if (myArgc >= 3) {
2280 		strcpy(mcguffin, myArgv[2]);	/* Userid to revoke */
2281 	    } else {
2282 		fprintf(pgpout,
2283 LANG("\nA user ID is required to select the key you want to revoke or \
2284 disable. "));
2285 		fprintf(pgpout, LANG("\nEnter user ID: "));
2286 #ifdef AMIGA
2287                 requesterdesc=LANG("\nEnter user ID: ");
2288 #endif
2289 		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
2290 	    }
2291 	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);
2292 
2293 	    status = sign_flag
2294 	               ? revoke_cert(mcguffin, my_name, keyfile)
2295 	               : disable_key(mcguffin, keyfile);
2296 
2297 	    if (status >= 0) {
2298 		status = maint_update(keyfile, 0);
2299 		if (status == -7) { /* ringfile is a keyfile or
2300 				       secret keyring */
2301 		    fprintf(pgpout, "Warning: '%s' is not a public keyring\n",
2302 			    keyfile);
2303 		    return 0;
2304 		}
2305 		if (status < 0)
2306 		    fprintf(pgpout, LANG("\007Maintenance pass error. "));
2307 	    }
2308 	    if (status < 0)
2309 		errorLvl = KEY_SIGNATURE_ERROR;
2310 #ifdef MACTC5
2311 		PGPSetFinfo(keyfile,'PKey','MPGP');
2312 #endif
2313 	    return status;
2314 	}			/* Key compromise / ID/Cert revocation */
2315 
2316 /*-------------------------------------------------------*/
2317     case 'e':
2318 	{			/*      Key editing
2319 				   Arguments: userid, ringfile
2320 				 */
2321 
2322 	    if (myArgc >= 4)
2323 		strncpy(ringfile, myArgv[3], sizeof(ringfile) - 1);
2324 	    else		/* default key ring filename */
2325 		strcpy(ringfile, globalPubringName);
2326 
2327 	    if (myArgc >= 3) {
2328 		strcpy(mcguffin, myArgv[2]);	/* Userid to edit */
2329 	    } else {
2330 		fprintf(pgpout,
2331 LANG("\nA user ID is required to select the key you want to edit. "));
2332 		fprintf(pgpout, LANG("\nEnter the key's user ID: "));
2333 #ifdef AMIGA
2334                 requesterdesc=LANG("\nEnter the key's user ID: ");
2335 #endif
2336 		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
2337 	    }
2338 	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);
2339 
2340 	    status = dokeyedit(mcguffin, ringfile);
2341 
2342 	    if (status >= 0) {
2343 		status = maint_update(ringfile, 0);
2344 		if (status == -7)
2345 		    status = 0;	/* ignore "not a public keyring" error */
2346 		if (status < 0)
2347 		    fprintf(pgpout, LANG("\007Maintenance pass error. "));
2348 	    }
2349 	    if (status < 0) {
2350 		fprintf(pgpout, LANG("\007Keyring edit error. "));
2351 		errorLvl = KEYRING_EDIT_ERROR;
2352 	    }
2353 #ifdef MACTC5
2354 		{
2355 		byte ctb;
2356 		get_header_info_from_file(ringfile, &ctb, 1);
2357 		if (ctb == CTB_CERT_SECKEY)
2358 			PGPSetFinfo(ringfile,'SKey','MPGP');
2359 		else if (ctb == CTB_CERT_PUBKEY)
2360 		PGPSetFinfo(ringfile,'PKey','MPGP');
2361 		}
2362 #endif
2363 	    return status;
2364 	}			/* Key edit */
2365 
2366 /*-------------------------------------------------------*/
2367     case 'a':
2368 	{			/*      Add key to key ring
2369 				   Arguments: keyfile, ringfile
2370 				 */
2371 
2372 	    if (myArgc < 3 && !filter_mode)
2373 		arg_error();
2374 
2375 	    if (!filter_mode) {	/* Get the keyfile from args */
2376 		strncpy(keyfile, myArgv[2], sizeof(keyfile) - 1);
2377 
2378 #ifdef MSDOS
2379 		strlwr(keyfile);
2380 #endif
2381 		if (!file_exists(keyfile))
2382 		    default_extension(keyfile, PGP_EXTENSION);
2383 
2384 		if (!file_exists(keyfile)) {
2385 		    fprintf(pgpout,
2386 			    LANG("\n\007Key file '%s' does not exist.\n"),
2387 			    keyfile);
2388 		    errorLvl = NONEXIST_KEY_ERROR;
2389 		    return -1;
2390 		}
2391 		workfile = keyfile;
2392 
2393 	    } else {
2394 		workfile = tempfile(TMP_WIPE | TMP_TMPDIR);
2395 		readPhantomInput(workfile);
2396 	    }
2397 
2398 	    if (myArgc < (filter_mode ? 3 : 4)) { /* default key ring
2399 						     filename */
2400 		byte ctb;
2401 		get_header_info_from_file(workfile, &ctb, 1);
2402 		if (ctb == CTB_CERT_SECKEY)
2403 		    strcpy(ringfile, globalSecringName);
2404 		else
2405 		    strcpy(ringfile, globalPubringName);
2406 	    } else {
2407 		strncpy(ringfile, myArgv[(filter_mode ? 2 : 3)],
2408 			sizeof(ringfile) - 1);
2409 		default_extension(ringfile, PGP_EXTENSION);
2410 	    }
2411 #ifdef MSDOS
2412 	    strlwr(ringfile);
2413 #endif
2414 
2415 	    status = addto_keyring(workfile, ringfile);
2416 
2417 	    if (filter_mode)
2418 		rmtemp(workfile);
2419 
2420 	    if (status < 0) {
2421 		fprintf(pgpout, LANG("\007Keyring add error. "));
2422 		errorLvl = KEYRING_ADD_ERROR;
2423 	    }
2424 #ifdef MACTC5
2425 		{
2426 		byte ctb;
2427 		get_header_info_from_file(ringfile, &ctb, 1);
2428 		if (ctb == CTB_CERT_SECKEY)
2429 			PGPSetFinfo(ringfile,'SKey','MPGP');
2430 		else if (ctb == CTB_CERT_PUBKEY)
2431 		PGPSetFinfo(ringfile,'PKey','MPGP');
2432 		}
2433 #endif
2434 	    return status;
2435 	}			/* Add key to key ring */
2436 
2437 /*-------------------------------------------------------*/
2438     case 'x':
2439 	{			/*      Extract key from key ring
2440 				   Arguments: mcguffin, keyfile, ringfile
2441 				 */
2442 
2443 	    if (myArgc >= (filter_mode ? 4 : 5)) /* default key ring
2444 						    filename */
2445 		strncpy(ringfile, myArgv[(filter_mode ? 3 : 4)],
2446 			sizeof(ringfile) - 1);
2447 	    else
2448 		strcpy(ringfile, globalPubringName);
2449 
2450 	    if (myArgc >= (filter_mode ? 2 : 3)) {
2451 		if (myArgv[2])
2452 		    /* Userid to extract */
2453 		    strcpy(mcguffin, myArgv[2]);
2454 		else
2455 		    strcpy(mcguffin, "");
2456 	    } else {
2457 		fprintf(pgpout,
2458 LANG("\nA user ID is required to select the key you want to extract. "));
2459 		if (batchmode)	/* not interactive, userid
2460 				   must be on command line */
2461 		    return -1;
2462 		fprintf(pgpout, LANG("\nEnter the key's user ID: "));
2463 #ifdef AMIGA
2464                 requesterdesc=LANG("\nEnter the key's user ID: ");
2465 #endif
2466 		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
2467 	    }
2468 	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);
2469 
2470 	    if (!filter_mode) {
2471 		if (myArgc >= 4)
2472 		    strncpy(keyfile, myArgv[3], sizeof(keyfile) - 1);
2473 		else
2474 		    keyfile[0] = '\0';
2475 
2476 		workfile = keyfile;
2477 	    } else {
2478 		workfile = tempfile(TMP_WIPE | TMP_TMPDIR);
2479 	    }
2480 
2481 #ifdef MSDOS
2482 	    strlwr(workfile);
2483 	    strlwr(ringfile);
2484 #endif
2485 
2486 	    default_extension(ringfile, PGP_EXTENSION);
2487 
2488 	    status = extract_from_keyring(mcguffin, workfile,
2489 					  ringfile, (filter_mode ? FALSE :
2490 						     emit_radix_64));
2491 
2492 	    if (status < 0) {
2493 		fprintf(pgpout, LANG("\007Keyring extract error. "));
2494 		errorLvl = KEYRING_EXTRACT_ERROR;
2495 		if (filter_mode)
2496 		    rmtemp(workfile);
2497 		return status;
2498 	    }
2499 	    if (filter_mode && !status) {
2500 		if (emit_radix_64) {
2501 		    /* NULL for outputfile means write to stdout */
2502 		    if (armor_file(workfile, NULL, NULL, NULL, FALSE) != 0) {
2503 			errorLvl = UNKNOWN_FILE_ERROR;
2504 			return -1;
2505 		    }
2506 		} else {
2507 		    if (writePhantomOutput(workfile) < 0) {
2508 			errorLvl = UNKNOWN_FILE_ERROR;
2509 			return -1;
2510 		    }
2511 		}
2512 		rmtemp(workfile);
2513 	    }
2514 #ifdef MACTC5
2515 		if (status)
2516 			return KEYRING_EXTRACT_ERROR;
2517 		if ((!emit_radix_64)&&(strlen(keyfile)>0)) {
2518 		byte ctb;
2519 		get_header_info_from_file(keyfile, &ctb, 1);
2520 		if (ctb == CTB_CERT_SECKEY)
2521 			PGPSetFinfo(ringfile,'SKey','MPGP');
2522 		else if (ctb == CTB_CERT_PUBKEY)
2523 			PGPSetFinfo(ringfile,'PKey','MPGP');
2524 		}
2525 #endif
2526 	    return 0;
2527 	}			/* Extract key from key ring */
2528 
2529 /*-------------------------------------------------------*/
2530     case 'r':
2531 	{	/*      Remove keys or selected key signatures from userid keys
2532 			Arguments: userid, ringfile
2533 		 */
2534 
2535 	    if (myArgc >= 4)
2536 		strcpy(ringfile, myArgv[3]);
2537 	    else		/* default key ring filename */
2538 		strcpy(ringfile, globalPubringName);
2539 
2540 	    if (myArgc >= 3) {
2541 		strcpy(mcguffin, myArgv[2]);	/* Userid to work on */
2542 	    } else {
2543 		if (sign_flag) {
2544 		    fprintf(pgpout,
2545 LANG("\nA user ID is required to select the public key you want to\n\
2546 remove certifying signatures from. "));
2547 		} else {
2548 		    fprintf(pgpout,
2549 LANG("\nA user ID is required to select the key you want to remove. "));
2550 		}
2551 		if (batchmode)	/* not interactive, userid must be on
2552 				   command line */
2553 		    return -1;
2554 		fprintf(pgpout, LANG("\nEnter the key's user ID: "));
2555 #ifdef AMIGA
2556                 requesterdesc=LANG("\nEnter the key's user ID: ");
2557 #endif
2558 		getstring(mcguffin, 255, TRUE);		/* echo keyboard */
2559 	    }
2560 	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);
2561 
2562 #ifdef MSDOS
2563 	    strlwr(ringfile);
2564 #endif
2565 	    if (!file_exists(ringfile))
2566 		default_extension(ringfile, PGP_EXTENSION);
2567 
2568 	    if (sign_flag) {	/* Remove signatures */
2569 		if (remove_sigs(mcguffin, ringfile) < 0) {
2570 		    fprintf(pgpout, LANG("\007Key signature remove error. "));
2571 		    errorLvl = KEYSIG_REMOVE_ERROR;
2572 		    return -1;
2573 		}
2574 	    } else {		/* Remove keyring */
2575 #ifdef MACTC5
2576 			if (remove_from_keyring( NULL, mcguffin, ringfile,
2577 					(boolean)!strcmp(ringfile, globalPubringName))) {
2578 #else
2579 		if (remove_from_keyring(NULL, mcguffin, ringfile,
2580 					(boolean) (myArgc < 4)) < 0) {
2581 #endif
2582 		    fprintf(pgpout, LANG("\007Keyring remove error. "));
2583 		    errorLvl = KEYRING_REMOVE_ERROR;
2584 		    return -1;
2585 		}
2586 	    }
2587 #ifdef MACTC5
2588 		{
2589 		byte ctb;
2590 		get_header_info_from_file(ringfile, &ctb, 1);
2591 		if (ctb == CTB_CERT_SECKEY)
2592 			PGPSetFinfo(ringfile,'SKey','MPGP');
2593 		else if (ctb == CTB_CERT_PUBKEY)
2594 		PGPSetFinfo(ringfile,'PKey','MPGP');
2595 		PGPSetFinfo(globalPubringName,'PKey','MPGP');
2596 		}
2597 #endif
2598 	    return 0;
2599 	}			/* remove key signatures from userid */
2600 
2601 /*-------------------------------------------------------*/
2602     case 'v':
2603     case 'V':			/* -kvv */
2604 	{			/* View or remove key ring entries,
2605 				   with userid match
2606 				   Arguments: userid, ringfile
2607 				 */
2608 
2609 	    if (myArgc < 4)	/* default key ring filename */
2610 		strcpy(ringfile, globalPubringName);
2611 	    else
2612 		strcpy(ringfile, myArgv[3]);
2613 
2614 	    if (myArgc > 2) {
2615 		strcpy(mcguffin, myArgv[2]);
2616 		if (strcmp(mcguffin, "*") == 0)
2617 		    mcguffin[0] = '\0';
2618 	    } else {
2619 		*mcguffin = '\0';
2620 	    }
2621 
2622 	    if ((myArgc == 3) && has_extension(myArgv[2], PGP_EXTENSION)) {
2623 		strcpy(ringfile, myArgv[2]);
2624 		mcguffin[0] = '\0';
2625 	    }
2626 	    CONVERT_TO_CANONICAL_CHARSET(mcguffin);
2627 
2628 #ifdef MSDOS
2629 	    strlwr(ringfile);
2630 #endif
2631 	    if (!file_exists(ringfile))
2632 		default_extension(ringfile, PGP_EXTENSION);
2633 
2634 	    /* If a second 'v' (keychar = V), show signatures too */
2635 	    status = view_keyring(mcguffin, ringfile,
2636 				  (boolean) (keychar == 'V'), c_flag);
2637 	    if (status < 0) {
2638 		fprintf(pgpout, LANG("\007Keyring view error. "));
2639 		errorLvl = KEYRING_VIEW_ERROR;
2640 	    }
2641 #ifdef MACTC5
2642 		{
2643 		byte ctb;
2644 		get_header_info_from_file(ringfile, &ctb, 1);
2645 		if (ctb == CTB_CERT_SECKEY)
2646 			PGPSetFinfo(ringfile,'SKey','MPGP');
2647 		else if (ctb == CTB_CERT_PUBKEY)
2648 		PGPSetFinfo(ringfile,'PKey','MPGP');
2649 		}
2650 #endif
2651 	    return status;
2652 	}			/* view key ring entries, with userid match */
2653 
2654     default:
2655 	arg_error();
2656     }
2657     return 0;
2658 }				/* do_keyopt */
2659 
2660 /* comes here if user made a boo-boo. */
2661 void user_error()
2662 {
2663     fprintf(pgpout, LANG("\nFor a usage summary, type:  pgp -h\n"));
2664     fprintf(pgpout,
2665 	    LANG("For more detailed help, consult the PGP User's Guide.\n"));
2666     exitPGP(errorLvl ? errorLvl : 127);		/* error exit */
2667 }
2668 
2669 #if defined(DEBUG) && defined(linux)
2670 #include <malloc.h>
2671 #endif
2672 
2673 /*
2674  * exitPGP: wipes and removes temporary files, also tries to wipe
2675  * the stack.
2676  */
2677 void exitPGP(int returnval)
2678 {
2679     char buf[STACK_WIPE];
2680     struct hashedpw *hpw;
2681 
2682     if (verbose)
2683 	fprintf(pgpout, "exitPGP: exitcode = %d\n", returnval);
2684     for (hpw = passwds; hpw; hpw = hpw->next)
2685 	memset(hpw->hash, 0, sizeof(hpw->hash));
2686     for (hpw = keypasswds; hpw; hpw = hpw->next)
2687 	memset(hpw->hash, 0, sizeof(hpw->hash));
2688 #ifdef MACTC5
2689 	mac_cleanup_tmpf();
2690 #else
2691     cleanup_tmpf();
2692 #endif
2693     /* Merge any entropy we collected into the randseed.bin file */
2694     if (cryptRandOpen((struct IdeaCfbContext *)0) >= 0)
2695 	    cryptRandSave((struct IdeaCfbContext *)0);
2696 #if defined(DEBUG) && defined(linux)
2697     if (verbose) {
2698 	struct mstats mstat;
2699 	mstat = mstats();
2700 	printf("%d chunks used (%d bytes)  %d bytes total\n",
2701 	       mstat.chunks_used, mstat.bytes_used, mstat.bytes_total);
2702     }
2703 #endif
2704     memset(buf, 0, sizeof(buf));	/* wipe stack */
2705 #ifdef VMS
2706 /*
2707  * Fake VMS style error returns with severity in bottom 3 bits
2708  */
2709     if (returnval)
2710 	returnval = (returnval << 3) | 0x10000002;
2711     else
2712 	returnval = 0x10000001;
2713 #endif				/* VMS */
2714     exit(returnval);
2715 }
2716 
2717 static void arg_error()
2718 {
2719     signon_msg();
2720     fprintf(pgpout, LANG("\nInvalid arguments.\n"));
2721     errorLvl = BAD_ARG_ERROR;
2722     user_error();
2723 }
2724 
2725 /*
2726  * Check for language specific help files in PGPPATH, then the system
2727  * directory.  If that fails, check for the default pgp.hlp, again
2728  * first a private copy, then the system-wide one.
2729  *
2730  * System-wide copies currently only exist on Unix.
2731  */
2732 static void build_helpfile(char *helpfile, char const *extra)
2733 {
2734     if (strcmp(language, "en")) {
2735 	buildfilename(helpfile, language);
2736 	strcat(helpfile, extra);
2737 	force_extension(helpfile, HLP_EXTENSION);
2738 	if (file_exists(helpfile))
2739 	    return;
2740 #ifdef PGP_SYSTEM_DIR
2741 	strcpy(helpfile, PGP_SYSTEM_DIR);
2742 	strcat(helpfile, language);
2743 	strcat(helpfile, extra);
2744 	force_extension(helpfile, HLP_EXTENSION);
2745 	if (file_exists(helpfile))
2746 	    return;
2747 #endif
2748     }
2749     buildfilename(helpfile, "pgp");
2750     strcat(helpfile, extra);
2751     force_extension(helpfile, HLP_EXTENSION);
2752 #ifdef PGP_SYSTEM_DIR
2753     if (file_exists(helpfile))
2754 	return;
2755     strcpy(helpfile, PGP_SYSTEM_DIR);
2756     strcat(helpfile, "pgp");
2757     strcat(helpfile, extra);
2758     force_extension(helpfile, HLP_EXTENSION);
2759 #endif
2760 }
2761 
2762 static void usage()
2763 {
2764     char helpfile[MAX_PATH];
2765     char *tmphelp = helpfile;
2766     extern unsigned char *ext_c_ptr;
2767 
2768     signon_msg();
2769     build_helpfile(helpfile, "");
2770 
2771     if (ext_c_ptr) {
2772 	/* conversion to external format necessary */
2773 	tmphelp = tempfile(TMP_TMPDIR);
2774 	CONVERSION = EXT_CONV;
2775 	if (copyfiles_by_name(helpfile, tmphelp) < 0) {
2776 	    rmtemp(tmphelp);
2777 	    tmphelp = helpfile;
2778 	}
2779 	CONVERSION = NO_CONV;
2780     }
2781     /* built-in help if pgp.hlp is not available */
2782     if (more_file(tmphelp, FALSE) < 0)
2783 	fprintf(pgpout, LANG("\nUsage summary:\
2784 \nTo encrypt a plaintext file with recipent's public key, type:\
2785 \n   pgp -e textfile her_userid [other userids] (produces textfile.pgp)\
2786 \nTo sign a plaintext file with your secret key:\
2787 \n   pgp -s textfile [-u your_userid]           (produces textfile.pgp)\
2788 \nTo sign a plaintext file with your secret key, and then encrypt it\
2789 \n   with recipent's public key, producing a .pgp file:\
2790 \n   pgp -es textfile her_userid [other userids] [-u your_userid]\
2791 \nTo encrypt with conventional encryption only:\
2792 \n   pgp -c textfile\
2793 \nTo decrypt or check a signature for a ciphertext (.pgp) file:\
2794 \n   pgp ciphertextfile [-o plaintextfile]\
2795 \nTo produce output in ASCII for email, add the -a option to other options.\
2796 \nTo generate your own unique public/secret key pair:  pgp -kg\
2797 \nFor help on other key management functions, type:   pgp -k\n"));
2798     if (ext_c_ptr)
2799 	rmtemp(tmphelp);
2800     exit(BAD_ARG_ERROR);	/* error exit */
2801 }
2802 
2803 static void key_usage()
2804 {
2805     char helpfile[MAX_PATH];
2806     char *tmphelp = helpfile;
2807     extern unsigned char *ext_c_ptr;
2808 
2809     signon_msg();
2810     build_helpfile(helpfile, "key");
2811 
2812     if (ext_c_ptr) {
2813 	/* conversion to external format necessary */
2814 	tmphelp = tempfile(TMP_TMPDIR);
2815 	CONVERSION = EXT_CONV;
2816 	if (copyfiles_by_name(helpfile, tmphelp) < 0) {
2817 	    rmtemp(tmphelp);
2818 	    tmphelp = helpfile;
2819 	}
2820 	CONVERSION = NO_CONV;
2821     }
2822     /* built-in help if key.hlp is not available */
2823     if (more_file(tmphelp, FALSE) < 0)
2824 	/* only use built-in help if there is no helpfile */
2825 	fprintf(pgpout, LANG("\nKey management functions:\
2826 \nTo generate your own unique public/secret key pair:\
2827 \n   pgp -kg\
2828 \nTo add a key file's contents to your public or secret key ring:\
2829 \n   pgp -ka keyfile [keyring]\
2830 \nTo remove a key or a user ID from your public or secret key ring:\
2831 \n   pgp -kr userid [keyring]\
2832 \nTo edit your user ID or pass phrase:\
2833 \n   pgp -ke your_userid [keyring]\
2834 \nTo extract (copy) a key from your public or secret key ring:\
2835 \n   pgp -kx userid keyfile [keyring]\
2836 \nTo view the contents of your public key ring:\
2837 \n   pgp -kv[v] [userid] [keyring]\
2838 \nTo check signatures on your public key ring:\
2839 \n   pgp -kc [userid] [keyring]\
2840 \nTo sign someone else's public key on your public key ring:\
2841 \n   pgp -ks her_userid [-u your_userid] [keyring]\
2842 \nTo remove selected signatures from a userid on a keyring:\
2843 \n   pgp -krs userid [keyring]\
2844 \n"));
2845     if (ext_c_ptr)
2846 	rmtemp(tmphelp);
2847     exit(BAD_ARG_ERROR);	/* error exit */
2848 }
2849 
2850 char **ParseRecipients(char **recipients)
2851 {
2852 	/*
2853 	 * ParseRecipients() expects an array of pointers to
2854 	 * characters, usually the array returned by the C startup
2855 	 * code. Then it will look for entries beginning with the
2856   	 * string "-@" followed by a filename, which may be appended
2857  	 * directly or seperated by a blank.
2858  	 *
2859  	 * If the file exists and is readable, the routine will load
2860  	 * the contents and insert it into the command line as if the
2861  	 * names had been specified there.
2862  	 *
2863  	 * Each entry in the file consists of one line. The file line
2864  	 * will be treated as one argument, no matter whether it
2865  	 * contains spaces or not. Lines beginning with "#" will be
2866  	 * ignored and treated as comments. Empty lines will be ignored
2867  	 * also. Trailing white spaces will be removed.
2868  	 *
2869  	 * Currently, ParseRecipients() uses one fixed buffer, meaning,
2870  	 * that one single line must not be longer than 255 characters.
2871  	 * The number of included lines is unlimited.
2872  	 *
2873  	 * When any kind of problem occurs, PGP will terminate and do
2874  	 * nothing. No need to test for an error, the result is always
2875  	 * correct.
2876  	 *
2877   	 *             21-Sep-95, Peter Simons <simons@peti.rhein.de>
2878  	 */
2879 
2880 	char **backup = recipients, **new;
2881 	int entrynum;
2882  	int MAX_RECIPIENTS = 128;   /* The name is somewhat wrong. of
2883  				     * course the memory handling is
2884  				     * dynamic.
2885  				     */
2886 
2887  	/* Check whether we need to do something or not. */
2888  	while(*recipients) {
2889  		if (!strncmp(*recipients, INCLUDE_MARK, INCLUDE_MARK_LEN))
2890  		    break;
2891  		recipients++;
2892  	}
2893  	if (!*recipients)
2894  	  return backup;	/* nothin' happened */
2895 
2896  	recipients=backup;
2897  	if (!(new = malloc(MAX_RECIPIENTS * sizeof(char *))))
2898  	  exitPGP(OUT_OF_MEM);
2899  	entrynum = 0;
2900 
2901  	while(*recipients) {
2902  		if (strncmp(*recipients, INCLUDE_MARK, INCLUDE_MARK_LEN))
2903                 {
2904  			new[entrynum++] = *recipients++;
2905  			if (entrynum == MAX_RECIPIENTS) {
2906  				/* Current buffer is too small.
2907  				 * Use realloc() to largen itt.
2908  				 */
2909  				MAX_RECIPIENTS += 128;
2910  				if (!(new = realloc(new,
2911                                  MAX_RECIPIENTS * sizeof(char *))))
2912  				  exitPGP(OUT_OF_MEM);
2913  			}
2914  		}
2915  		else {
2916  			/* We got a hit! Load the file and parse it. */
2917  			FILE *fh;
2918  			char *filename, tempbuf[256];
2919 
2920  			if (strlen(*recipients) == INCLUDE_MARK_LEN)
2921  			  filename = *++recipients;
2922  			else
2923  			  filename = *recipients+INCLUDE_MARK_LEN;
2924  			fprintf(pgpout, LANG("\nIncluding \"%s\"...\n"), filename);
2925  			if (!(fh = fopen(filename, "r"))) {
2926  				perror("PGP");
2927  				exitPGP(UNKNOWN_FILE_ERROR);
2928  			}
2929  			while(fgets(tempbuf, sizeof(tempbuf)-1, fh)) {
2930  				int i = strlen(tempbuf);
2931 
2932  				/* Test for comments or empty lines. */
2933  				if (!i || *tempbuf == '#')
2934  				  continue;
2935 
2936  				/* Remove trailing blanks. */
2937  				while (isspace(tempbuf[i-1]))
2938  				  i--;
2939  				tempbuf[i] = '\0';
2940 
2941  				/* Copy new entry to new */
2942  				if (!(new[entrynum++] = store_str(tempbuf)))
2943  				  exitPGP(OUT_OF_MEM);
2944  				if (entrynum == MAX_RECIPIENTS) {
2945  					/* Current buffer is too small.
2946  					 * Use realloc() to largen itt.
2947  					 */
2948  					MAX_RECIPIENTS += 128;
2949  					if (!(new = realloc(new,
2950                                          MAX_RECIPIENTS * sizeof(char *))))
2951  					  exitPGP(OUT_OF_MEM);
2952  				}
2953  			}
2954  			if (ferror(fh)) {
2955  				perror("PGP");
2956  				exitPGP(UNKNOWN_FILE_ERROR);
2957  			}
2958  			fclose(fh);
2959  			recipients++;
2960  		}
2961  	}
2962 
2963  	/*
2964  	 * We have to write one trailing NULL pointer.
2965  	 * Check array size first.
2966  	 */
2967  	if (entrynum == MAX_RECIPIENTS) {
2968  		if (!(new = realloc(new, (MAX_RECIPIENTS+1) * sizeof(char *))))
2969  		  exitPGP(OUT_OF_MEM);
2970  	}
2971  	new[entrynum] = NULL;
2972  	return new;
2973 }
2974