1 /*      fileio.c  - I/O routines for PGP.
2    PGP: Pretty Good(tm) Privacy - public key cryptography for the masses.
3 
4    (c) Copyright 1990-1996 by Philip Zimmermann.  All rights reserved.
5    The author assumes no liability for damages resulting from the use
6    of this software, even if the damage results from defects in this
7    software.  No warranty is expressed or implied.
8 
9    Note that while most PGP source modules bear Philip Zimmermann's
10    copyright notice, many of them have been revised or entirely written
11    by contributors who frequently failed to put their names in their
12    code.  Code that has been incorporated into PGP from other authors
13    was either originally published in the public domain or is used with
14    permission from the various authors.
15 
16    PGP is available for free to the public under certain restrictions.
17    See the PGP User's Guide (included in the release package) for
18    important information about licensing, patent restrictions on
19    certain algorithms, trademarks, copyrights, and export controls.
20 
21    Modified 16 Apr 92 - HAJK
22    Mods for support of VAX/VMS file system
23 
24    Modified 17 Nov 92 - HAJK
25    Change to temp file stuff for VMS.
26  */
27 
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #ifdef UNIX
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #ifdef _BSD
38 #include <sys/param.h>
39 #endif
40 extern int errno;
41 #endif				/* UNIX */
42 #ifdef VMS
43 #include <file.h>
44 #include <assert.h>
45 #endif
46 #include "random.h"
47 #include "mpilib.h"
48 #include "mpiio.h"
49 #include "fileio.h"
50 #include "language.h"
51 #include "pgp.h"
52 #include "exitpgp.h"
53 #include "charset.h"
54 #include "system.h"
55 #if defined(MSDOS) || defined(OS2) || defined (WIN32)
56 #include <io.h>
57 #include <fcntl.h>
58 #endif
59 #ifdef MACTC5
60 #include "crypto.h" 	/* for get_header_info_from_file() */
61 #include "AEStuff.h"
62 #include "AppGlobals.h"
63 #include "MacPGP.h"
64 #include "Macutil2.h"
65 #include "Macutil3.h"
66 #define MULTIPLE_DOTS
67 extern Boolean AEProcessing;
68 pascal Boolean idleProc(EventRecord * eventIn, long *sleep, RgnHandle * mouseRgn);
69 #endif
70 
71 char *ck_dup_output(char *, boolean, boolean);
72 
73 #ifndef F_OK
74 #define F_OK	0
75 #define X_OK	1
76 #define W_OK	2
77 #define R_OK	4
78 #endif				/* !F_OK */
79 
80 /*
81  * DIRSEPS is a string of possible directory-separation characters
82  * The first one is the preferred one, which goes in between
83  * PGPPATH and the file name if PGPPATH is not terminated with a
84  * directory separator.
85  */
86 
87 #if defined(MSDOS) || defined(__MSDOS__) || defined(OS2) || defined (WIN32)
88 static char const DIRSEPS[] = "\\/:";
89 #define BSLASH
90 
91 #elif defined(ATARI)
92 static char const DIRSEPS[] = "\\/:";
93 #define BSLASH
94 
95 #elif defined(UNIX)
96 static char const DIRSEPS[] = "/";
97 #define MULTIPLE_DOTS
98 
99 #elif defined(AMIGA)
100 static char const DIRSEPS[] = "/:";
101 #define MULTIPLE_DOTS
102 
103 #elif defined(VMS)
104 static char const DIRSEPS[] = "]:";
105 
106 #elif defined(EBCDIC)
107 static char const DIRSEPS[] = "("; 	/* Any more? */
108 #define MULTIPLE_DOTS
109 
110 #elif defined(MACTC5)
111 #define MULTIPLE_DOTS
112 static char const DIRSEPS[] = ":";
113 
114 #else
115 /* #error is not portable, this has the same effect */
116 #include "Unknown OS"
117 #endif
118 
119 #ifdef __PUREC__
120 #include <ext.h>
access(const char * name,int flag)121 int access(const char *name,int flag)
122 {
123 struct ffblk dummy;
124 	return findfirst(name,&dummy,-1);
125 }
126 #endif
127 
128 /* 1st character of temporary file extension */
129 #define	TMP_EXT	'$'		/* extensions are '.$##' */
130 
131 /* The PGPPATH environment variable */
132 
133 static char PGPPATH[] = "PGPPATH";
134 
135 /* Disk buffers, used here and in crypto.c */
136 byte textbuf[DISKBUFSIZE];
137 static unsigned textbuf2[2 * DISKBUFSIZE / sizeof(unsigned)];
138 
file_exists(char * filename)139 boolean file_exists(char *filename)
140 /*      Returns TRUE iff file exists. */
141 {
142 #ifdef MACTC5
143 	FILE *f;
144 	/* open file f for read, in binary (not text) mode...*/
145 	if ((f = fopen(filename,FOPRBIN)) == NULL)
146 		return(FALSE);
147 	fclose(f);
148 	return(TRUE);
149 #else
150     return access(filename, F_OK) == 0;
151 #endif
152 }				/* file_exists */
153 
is_regular_file(char * filename)154 static boolean is_regular_file(char *filename)
155 {
156 #ifdef S_ISREG
157     struct stat st;
158     return stat(filename, &st) != -1 && S_ISREG(st.st_mode);
159 #else
160     return TRUE;
161 #endif
162 }
163 
164 
165 /*
166  * This wipes a file with pseudo-random data.  The purpose of this is to
167  * make sure no sensitive information is left on the disk.  The use
168  * of pseudo-random data is to defeat disk compression drivers (such as
169  * Stacker and dblspace) so that we are guaranteed that the entire file
170  * has been overwritten.
171  *
172  * Note that the file MUST be open for read/write.
173  *
174  * It may not work to eliminate everything from non-volatile storage
175  * if the OS you're using does its own paging or swapping.  Then
176  * it's an issue of how the OS's paging device is wiped, and you can
177  * only hope that the space will be reused within a few seconds.
178  *
179  * Also, some file systems (in particular, the Logging File System
180  * for Sprite) do not write new data in the same place as old data,
181  * defeating this wiping entirely.  Fortunately, such systems
182  * usually don't need a swap file, and for small temp files, they
183  * do write-behind, so if you create and delete a file fast enough,
184  * it never gets written to disk at all.
185  */
186 
187 /*
188  * The data is randomly generated with the size of the file as a seed.
189  * The data should be random and not leak information.  If someone is
190  * examining deleted files, presumably they can reconstruct the file size,
191  * so that's not a secret.  H'm... this wiping algorithm makes it easy to,
192  * given a block of data, find the size of the file it came from
193  * and the offset of this block within it.  That in turn reveals
194  * something about the state of the disk's allocation tables when the
195  * file was used, possibly making it easier to find other files created
196  * at neaby times - such as plaintext files.  Is this acceptable?
197  */
198 
199 /*
200  * Theory of operation: We use the additive congruential RNG
201  * r[i] = r[i-24] + r[i-55], from Knuth, Vol. 2.  This is fast
202  * and has a long enough period that there should be no repetitions
203  * in even a huge file.  It is seeded with r[-55] through r[-1]
204  * using another polynomial-based RNG.  We seed a linear feedback
205  * shift register (CRC generator) with the size of the swap file,
206  * and clock in 0 bits.  Each 32 bits, the value of the generator is
207  * taken as the next integer.  This is just to ensure a reasonably
208  * even mix of 1's and 0's in the initialization vector.
209  */
210 
211 /*
212  * This is the CRC-32 polynomial, which should be okay for random
213  * number generation.
214  * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1
215  * = 1 0000 0100 1100 0001 0001 1101 1011 0111
216  * = 0x04c11db7
217  */
218 #define POLY 0x04c11db7
219 
wipeout(FILE * f)220 static void wipeout(FILE * f)
221 {
222     unsigned *p1, *p2, *p3;
223     unsigned long len;
224     unsigned long t;
225     int i;
226 
227     /* Get the file size */
228     fseek(f, 0L, SEEK_END);
229     len = ftell(f);
230 #ifdef MACTC5
231 	len = len + 4096 - (len % 4096);
232 #endif
233     rewind(f);
234 
235     /* Seed of first RNG.  Inverted to get more 1 bits */
236     t = ~len;
237 
238     /* Initialize first 55 words of buf with pseudo-random stuff */
239     p1 = (unsigned *) textbuf2 + 55;
240     do {
241 	for (i = 0; i < 32; i++)
242 	    t = (t & 0x80000000) ? t << 1 ^ POLY : t << 1;
243 	*--p1 = (unsigned) t;
244     } while (p1 > (unsigned *) textbuf2);
245 
246     while (len) {
247 	/* Fill buffer with pseudo-random integers */
248 
249 	p3 = (unsigned *) textbuf2 + 55;
250 	p2 = (unsigned *) textbuf2 + 24;
251 	p1 = (unsigned *) textbuf2 + sizeof(textbuf2) / sizeof(*p1);
252 	do {
253 	    *--p1 = *--p2 + *--p3;
254 	} while (p2 > (unsigned *) textbuf2);
255 
256 	p2 = (unsigned *) textbuf2 + sizeof(textbuf2) / sizeof(*p1);
257 	do {
258 	    *--p1 = *--p2 + *--p3;
259 	} while (p3 > (unsigned *) textbuf2);
260 
261 	p3 = (unsigned *) textbuf2 + sizeof(textbuf2) / sizeof(*p3);
262 	do {
263 	    *--p1 = *--p2 + *--p3;
264 	} while (p1 > (unsigned *) textbuf2);
265 
266 	/* Write it out - yes, we're ignoring errors */
267 	if (len > sizeof(textbuf2)) {
268 	    fwrite((char const *) textbuf2, sizeof(textbuf2), 1, f);
269 	    len -= sizeof(textbuf2);
270 #ifdef MACTC5
271 		mac_poll_for_break();
272 #endif
273 	} else {
274 	    fwrite((char const *) textbuf2, len, 1, f);
275 	    len = 0;
276 	}
277     }
278 }
279 
280 
281 /*
282  * Completely overwrite and erase file, so that no sensitive
283  * information is left on the disk.
284  */
wipefile(char * filename)285 int wipefile(char *filename)
286 {
287     FILE *f;
288     /* open file f for read/write, in binary (not text) mode... */
289     if ((f = fopen(filename, FOPRWBIN)) == NULL)
290 	return -1;		/* error - file can't be opened */
291     wipeout(f);
292     fclose(f);
293     return 0;			/* normal return */
294 }				/* wipefile */
295 
296 /*
297  * Returns the part of a filename after all directory specifiers.
298  */
file_tail(char * filename)299 char *file_tail(char *filename)
300 {
301     char *p;
302     char const *s = DIRSEPS;
303 
304     while (*s) {
305 	p = strrchr(filename, *s);
306 	if (p)
307 	    filename = p + 1;
308 	s++;
309     }
310 
311     return filename;
312 }
313 
314 
315 /* return TRUE if extension matches the end of filename */
has_extension(char * filename,char * extension)316 boolean has_extension(char *filename, char *extension)
317 {
318     int lf = strlen(filename);
319     int lx = strlen(extension);
320 
321     if (lf <= lx)
322 	return FALSE;
323     return !strcmp(filename + lf - lx, extension);
324 }
325 
326 /* return TRUE if path is a filename created by tempfile() */
327 /* Filename matches "*.$[0-9][0-9]" */
is_tempfile(char * path)328 boolean is_tempfile(char *path)
329 {
330     char *p = strrchr(path, '.');
331 
332     return p != NULL && p[1] == TMP_EXT &&
333 	isdigit(p[2]) && isdigit(p[3]) && p[4] == '\0';
334 }
335 
336 /*
337  * Returns TRUE if user left off file extension, allowing default.
338  * Note that the name is misleading if multiple dots are allowed.
339  * not_pgp_extension or something would be better.
340  */
no_extension(char * filename)341 boolean no_extension(char *filename)
342 {
343 #ifdef MULTIPLE_DOTS		/* filename can have more than one dot */
344     if (has_extension(filename, ASC_EXTENSION) ||
345 	has_extension(filename, PGP_EXTENSION) ||
346 	has_extension(filename, SIG_EXTENSION) ||
347 #ifdef MACTC5
348 		has_extension(filename,".tmp") ||
349 #endif
350 	is_tempfile(filename))
351 	return FALSE;
352     else
353 	return TRUE;
354 #else
355     filename = file_tail(filename);
356 
357     return strrchr(filename, '.') == NULL;
358 #endif
359 }				/* no_extension */
360 
361 
362 /* deletes trailing ".xxx" file extension after the period. */
drop_extension(char * filename)363 void drop_extension(char *filename)
364 {
365     if (!no_extension(filename))
366 	*strrchr(filename, '.') = '\0';
367 }				/* drop_extension */
368 
369 
370 /* append filename extension if there isn't one already. */
default_extension(char * filename,char * extension)371 void default_extension(char *filename, char *extension)
372 {
373     if (no_extension(filename))
374 	strcat(filename, extension);
375 }				/* default_extension */
376 
377 #ifndef MAX_NAMELEN
378 #if defined(AMIGA) || defined(NeXT) || (defined(BSD) && BSD > 41) || (defined(sun) && defined(i386))
379 #define	MAX_NAMELEN	255
380 #else
381 #ifdef MACTC5
382 #define MAX_NAMELEN 31
383 #else
384 #include <limits.h>
385 #endif
386 #endif
387 #endif
388 
389 /* truncate the filename so that an extension can be tacked on. */
truncate_name(char * path,int ext_len)390 static void truncate_name(char *path, int ext_len)
391 {
392 #ifdef UNIX			/* for other systems this is a no-op */
393     char *p;
394 #ifdef MAX_NAMELEN		/* overrides the use of pathconf() */
395     int namemax = MAX_NAMELEN;
396 #else
397     int namemax;
398 #ifdef _PC_NAME_MAX
399     char dir[MAX_PATH];
400 
401     strcpy(dir, path);
402     if ((p = strrchr(dir, '/')) == NULL) {
403 	strcpy(dir, ".");
404     } else {
405 	if (p == dir)
406 	    ++p;
407 	*p = '\0';
408     }
409     if ((namemax = pathconf(dir, _PC_NAME_MAX)) <= ext_len)
410 	return;
411 #else
412 #ifdef NAME_MAX
413     namemax = NAME_MAX;
414 #else
415     namemax = 14;
416 #endif				/* NAME_MAX */
417 #endif				/* _PC_NAME_MAX */
418 #endif				/* MAX_NAMELEN */
419 
420     if ((p = strrchr(path, '/')) == NULL)
421 	p = path;
422     else
423 	++p;
424     if (strlen(p) > namemax - ext_len) {
425 	if (verbose)
426 	    fprintf(pgpout, "Truncating filename '%s' ", path);
427 	p[namemax - ext_len] = '\0';
428 	if (verbose)
429 	    fprintf(pgpout, "to '%s'\n", path);
430     }
431 #else
432 #ifdef MACTC5
433 	char *p;
434 	p = file_tail(path);
435 	if (verbose)
436 		fprintf(pgpout, LANG("Truncating filename '%s' "), path);
437 	if (strlen(p) + ext_len > MAX_NAMELEN) p[MAX_NAMELEN - ext_len] = '\0';
438 #endif  /* MACTC5 */
439 #endif				/* UNIX */
440 }
441 
442 /* change the filename extension. */
force_extension(char * filename,char * extension)443 void force_extension(char *filename, char *extension)
444 {
445     drop_extension(filename);	/* out with the old */
446     truncate_name(filename, strlen(extension));
447     strcat(filename, extension);	/* in with the new */
448 }				/* force_extension */
449 
450 
451 /*
452  * Get yes/no answer from user, returns TRUE for yes, FALSE for no.
453  * First the translations are checked, if they don't match 'y' and 'n'
454  * are tried.
455  */
456 #ifdef MACTC5
457 
getyesno(char default_answer)458 boolean getyesno(char default_answer)
459 {
460   extern FILE *logfile;
461   short  alertid,i,large,err;
462   char dfault[8], ndfault[8];
463   ProcessSerialNumber psn;
464   if (batchmode)
465   	return(default_answer == 'y' ? TRUE : FALSE);
466   if (strlen(Yes_No_Message)<72) large=0;
467   else large=100;
468   strcpy(dfault,default_answer == 'y' ? LANG("y") : LANG("n"));
469   strcpy(ndfault,default_answer == 'n' ? LANG("y") : LANG("n"));
470   for(i=0;i<strlen(Yes_No_Message);i++)
471   if (Yes_No_Message[i]<' ' && Yes_No_Message[i]>=0) Yes_No_Message[i]=' ';	/* It's a signed char! */
472   InitCursor();
473   alertid=(default_answer == 'n' ? 211+large : 212+large);
474   c2pstr(Yes_No_Message);
475   ParamText((uchar *)Yes_No_Message,(uchar *)"", \
476   		(uchar *)"",(uchar *)"");
477   if (AEProcessing) {
478   	if (gHasProcessManager)
479   		GetFrontProcess(&psn);
480   	if(MyInteractWithUser())
481   		return default_answer;
482   	if (gHasProcessManager)
483     	SetFrontProcess(&psn);
484   }
485   if (CautionAlert(alertid,nil)==1){
486    p2cstr((uchar *)Yes_No_Message);
487    fputs(strcat(Yes_No_Message,dfault),stderr);
488    fputc('\n',stderr);
489    fflush(stderr);
490    return(default_answer == 'y' ? TRUE : FALSE);
491    }
492   p2cstr((uchar *)Yes_No_Message);
493   fputs(strcat(Yes_No_Message,ndfault),stderr);
494   fputc('\n',stderr);
495   fflush(stderr);
496   return(default_answer == 'n' ? TRUE : FALSE);
497   }
498 #else
499 
getyesno(char default_answer)500 boolean getyesno(char default_answer)
501 {
502     char buf[8];
503     static char yes[8], no[8];
504 
505     if (yes[0] == '\0') {
506 	strncpy(yes, LANG("y"), 7);
507 	strncpy(no, LANG("n"), 7);
508     }
509     if (!batchmode) {		/* return default answer in batchmode */
510 	getstring(buf, 6, TRUE);	/* echo keyboard input */
511 	strlwr(buf);
512 	if (!strncmp(buf, no, strlen(no)))
513 	    return FALSE;
514 	if (!strncmp(buf, yes, strlen(yes)))
515 	    return TRUE;
516 	if (buf[0] == 'n')
517 	    return FALSE;
518 	if (buf[0] == 'y')
519 	    return TRUE;
520     }
521     return default_answer == 'y' ? TRUE : FALSE;
522 }				/* getyesno */
523 #endif
524 
525 /* if user consents to it, change the filename extension. */
maybe_force_extension(char * filename,char * extension)526 char *maybe_force_extension(char *filename, char *extension)
527 {
528     static char newname[MAX_PATH];
529     if (!batchmode && !has_extension(filename, extension)) {
530 	strcpy(newname, filename);
531 	force_extension(newname, extension);
532 	if (!file_exists(newname)) {
533 	    fprintf(pgpout, LANG("\nShould '%s' be renamed to '%s' (Y/n)? "),
534 		    filename, newname);
535 	    if (getyesno('y'))
536 		return newname;
537 	}
538     }
539     return NULL;
540 }				/* maybe_force_extension */
541 
542 /*
543  * Add a trailing directory separator to a name, if absent.
544  */
addslash(char * name)545 static void addslash(char *name)
546 {
547     int i = strlen(name);
548 
549     if (i != 0 && !strchr(DIRSEPS, name[i - 1])) {
550 	name[i] = DIRSEPS[0];
551 	name[i + 1] = '\0';
552     }
553 }
554 
555 /*
556  * Builds a filename with a complete path specifier from the environmental
557  * variable PGPPATH.
558  */
buildfilename(char * result,char * fname)559 char *buildfilename(char *result, char *fname)
560 {
561 #ifdef MACTC5
562 	char const *s;
563 #else
564     char const *s = getenv(PGPPATH);
565 #endif
566     result[0] = '\0';
567 #ifdef MACTC5
568     return(strcpy(result,fname));
569 #endif
570 
571     if (s && strlen(s) <= 50) {
572 	strcpy(result, s);
573     }
574 #ifdef UNIX
575     /* On Unix, default to $HOME/.pgp, otherwise, current directory. */
576     else {
577 	s = getenv("HOME");
578 	if (s && strlen(s) <= 50) {
579 	    strcpy(result, s);
580 	    addslash(result);
581 	    strcat(result, ".pgp");
582 	}
583     }
584 #endif				/* UNIX */
585 
586     addslash(result);
587     strcat(result, fname);
588     return result;
589 }				/* buildfilename */
590 
buildsysfilename(char * result,char * fname)591 char *buildsysfilename(char *result, char *fname)
592 {
593 #ifdef PGP_SYSTEM_DIR
594     strcpy(result, PGP_SYSTEM_DIR);
595     strcat(result, fname);
596     if (file_exists(result))
597 	return result;
598 #endif
599     buildfilename(result, fname);	/* Put name back for error */
600     return result;
601 }
602 
603 
604 /* Convert filename to canonical form, with slashes as separators */
file_to_canon(char * filename)605 void file_to_canon(char *filename)
606 {
607 #ifdef EBCDIC
608     CONVERT_TO_CANONICAL_CHARSET(filename);
609 #endif
610 #ifdef BSLASH
611     while (*filename) {
612 	if (*filename == '\\')
613 	    *filename = '/';
614 	++filename;
615     }
616 #else	/* 203a */
617 #ifdef MACTC5
618 	while (*filename) {
619 		if (*filename == ':')
620 			*filename = '/';
621 		++filename;
622 		}
623 #endif
624 #endif
625 }
626 
627 #ifdef EBCDIC
628 /* Convert filename from canonical form */
file_from_canon(char * filename)629 void file_from_canon(char *filename)
630 {
631    strcpy( filename, LOCAL_CHARSET(filename) );
632 }
633 #endif /* EBCDIC */
634 
635 
write_error(FILE * f)636 int write_error(FILE * f)
637 {
638     fflush(f);
639     if (ferror(f)) {
640 #ifdef ENOSPC
641 	if (errno == ENOSPC)
642 	    fprintf(pgpout, LANG("\nDisk full.\n"));
643 	else
644 #endif
645 	    fprintf(pgpout, LANG("\nFile write error.\n"));
646 	return -1;
647     }
648     return 0;
649 }
650 
651 /* copy file f to file g, for longcount bytes */
copyfile(FILE * f,FILE * g,word32 longcount)652 int copyfile(FILE * f, FILE * g, word32 longcount)
653 {
654     int count, status = 0;
655     do {			/* read and write the whole file... */
656 	if (longcount < (word32) DISKBUFSIZE)
657 	    count = (int) longcount;
658 	else
659 	    count = DISKBUFSIZE;
660 	count = fread(textbuf, 1, count, f);
661 	if (count > 0) {
662 	    if (CONVERSION != NO_CONV) {
663 		int i;
664 		for (i = 0; i < count; i++)
665 		    textbuf[i] = (CONVERSION == EXT_CONV) ?
666 			EXT_C(textbuf[i]) :
667 			INT_C(textbuf[i]);
668 	    }
669 	    if (fwrite(textbuf, 1, count, g) != count) {
670 		/* Problem: return error value */
671 		status = -1;
672 		break;
673 	    }
674 	    longcount -= count;
675 #ifdef MACTC5
676 			mac_poll_for_break();
677 #endif
678 	}
679 	/* if text block was short, exit loop */
680     } while (count == DISKBUFSIZE);
681     burn(textbuf);		/* burn sensitive data on stack */
682     return status;
683 }				/* copyfile */
684 
685 /*
686  * Like copyfile, but takes a position for file f.  Returns with
687  * f and g pointing just past the copied data.
688  */
copyfilepos(FILE * f,FILE * g,word32 longcount,word32 fpos)689 int copyfilepos(FILE * f, FILE * g, word32 longcount, word32 fpos)
690 {
691     fseek(f, fpos, SEEK_SET);
692     return copyfile(f, g, longcount);
693 }
694 
695 
696 /* copy file f to file g, for longcount bytes.  Convert to
697  * canonical form as we go.  f is open in text mode.  Canonical
698  * form uses crlf's as line separators.
699  */
copyfile_to_canon(FILE * f,FILE * g,word32 longcount)700 int copyfile_to_canon(FILE * f, FILE * g, word32 longcount)
701 {
702     int count, status = 0;
703     byte c, *tb1, *tb2;
704     int i, nbytes;
705     int nspaces = 0;
706 #ifdef MACTC5
707     Boolean warning = true; /* MACTC5 */
708 #endif
709     do {			/* read and write the whole file... */
710 	if (longcount < (word32) DISKBUFSIZE)
711 	    count = (int) longcount;
712 	else
713 	    count = DISKBUFSIZE;
714 	count = fread(textbuf, 1, count, f);
715 	if (count > 0) {
716 	    /* Convert by adding CR before LF */
717 	    tb1 = textbuf;
718 	    tb2 = (byte *) textbuf2;
719 	    for (i = 0; i < count; ++i) {
720 		switch (CONVERSION) {
721 		case EXT_CONV:
722 		    c = EXT_C(*tb1++);
723 		    break;
724 		case INT_CONV:
725 		    c = INT_C(*tb1++);
726 		    break;
727 		default:
728 		    c = *tb1++;
729 		}
730 #ifdef MACTC5
731 		if ( (((uchar) c) < ' ') && (c != '\n') && (c != '\t') && warning) {
732 			warning = false;
733 			fprintf(stdout, "\aWarning text file contains control characters!\n");
734 		}
735 #endif
736 		if (strip_spaces) {
737 		    if (c == ' ') {
738 			/* Don't output spaces yet */
739 			nspaces += 1;
740 		    } else {
741 			if (c == '\n') {
742 			    *tb2++ = '\r';
743 			    nspaces = 0;	/* Delete trailing spaces */
744 			}
745 			if (nspaces) {
746 			    /* Put out spaces now */
747 			    do
748 				*tb2++ = ' ';
749 			    while (--nspaces);
750 			}
751 			*tb2++ = c;
752 		    }
753 		} else {
754 		    if (c == '\n')
755 			*tb2++ = '\r';
756 		    *tb2++ = c;
757 		}
758 	    }
759 	    nbytes = tb2 - (byte *) textbuf2;
760 	    if (fwrite(textbuf2, 1, nbytes, g) != nbytes) {
761 		/* Problem: return error value */
762 		status = -1;
763 		break;
764 	    }
765 	    longcount -= count;
766 	}
767 	/* if text block was short, exit loop */
768     } while (count == DISKBUFSIZE);
769     burn(textbuf);		/* burn sensitive data on stack */
770     burn(textbuf2);
771     return status;
772 }				/* copyfile_to_canon */
773 
774 
775 /* copy file f to file g, for longcount bytes.  Convert from
776  * canonical to local form as we go.  g is open in text mode.  Canonical
777  * form uses crlf's as line separators.
778  */
copyfile_from_canon(FILE * f,FILE * g,word32 longcount)779 int copyfile_from_canon(FILE * f, FILE * g, word32 longcount)
780 {
781     int count, status = 0;
782     byte c, *tb1, *tb2;
783     int i, nbytes;
784     do {			/* read and write the whole file... */
785 	if (longcount < (word32) DISKBUFSIZE)
786 	    count = (int) longcount;
787 	else
788 	    count = DISKBUFSIZE;
789 	count = fread(textbuf, 1, count, f);
790 	if (count > 0) {
791 	    /* Convert by removing CR's */
792 	    tb1 = textbuf;
793 	    tb2 = (byte *) textbuf2;
794 	    for (i = 0; i < count; ++i) {
795 		switch (CONVERSION) {
796 		case EXT_CONV:
797 		    c = EXT_C(*tb1++);
798 		    break;
799 		case INT_CONV:
800 		    c = INT_C(*tb1++);
801 		    break;
802 		default:
803 		    c = *tb1++;
804 		}
805 		if (c != '\r')
806 		    *tb2++ = c;
807 	    }
808 	    nbytes = tb2 - (byte *) textbuf2;
809 	    if (fwrite(textbuf2, 1, nbytes, g) != nbytes) {
810 		/* Problem: return error value */
811 		status = -1;
812 		break;
813 	    }
814 	    longcount -= count;
815 	}
816 	/* if text block was short, exit loop */
817     } while (count == DISKBUFSIZE);
818     burn(textbuf);		/* burn sensitive data on stack */
819     burn(textbuf2);
820     return status;
821 }				/* copyfile_from_canon */
822 
823 /*      Copy srcFile to destFile  */
copyfiles_by_name(char * srcFile,char * destFile)824 int copyfiles_by_name(char *srcFile, char *destFile)
825 {
826     FILE *f, *g;
827     int status = 0;
828     long fileLength;
829 
830     f = fopen(srcFile, FOPRBIN);
831     if (f == NULL)
832 	return -1;
833     g = fopen(destFile, FOPWBIN);
834     if (g == NULL) {
835 	fclose(f);
836 	return -1;
837     }
838     /* Get file length and copy it */
839     fseek(f, 0L, SEEK_END);
840     fileLength = ftell(f);
841     rewind(f);
842     status = copyfile(f, g, fileLength);
843     fclose(f);
844     if (write_error(g))
845 	status = -1;
846     fclose(g);
847     return status;
848 }				/* copyfiles_by_name */
849 
850 /* Copy srcFile to destFile, converting to canonical text form  */
make_canonical(char * srcFile,char * destFile)851 int make_canonical(char *srcFile, char *destFile)
852 {
853     FILE *f, *g;
854     int status = 0;
855     long fileLength;
856 
857     if (((f = fopen(srcFile, FOPRTXT)) == NULL) ||
858 	((g = fopen(destFile, FOPWBIN)) == NULL))
859 	/* Can't open files */
860 	return -1;
861 
862     /* Get file length and copy it */
863     fseek(f, 0L, SEEK_END);
864     fileLength = ftell(f);
865     rewind(f);
866     CONVERSION = INT_CONV;
867     status = copyfile_to_canon(f, g, fileLength);
868     CONVERSION = NO_CONV;
869     fclose(f);
870     if (write_error(g))
871 	status = -1;
872     fclose(g);
873     return status;
874 }				/* make_canonical */
875 
876 /*
877  * Like rename() but will try to copy the file if the rename fails.
878  * This is because under OS's with multiple physical volumes if the
879  * source and destination are on different volumes the rename will fail
880  */
rename2(char * srcFile,char * destFile)881 int rename2(char *srcFile, char *destFile)
882 {
883     FILE *f, *g;
884 #ifdef MACTC5
885 	int copy=-1;
886 #endif
887     int status = 0;
888     long fileLength;
889 
890 #ifdef MACTC5
891 	copy=MoveRename(srcFile,destFile);
892 if (copy)
893 #else
894 #if defined(VMS) || defined(C370)
895     if (rename(srcFile, destFile) != 0)
896 #else
897     if (rename(srcFile, destFile) == -1)
898 #endif
899 #endif
900     {
901 	/* Rename failed, try a copy */
902 	if (((f = fopen(srcFile, FOPRBIN)) == NULL) ||
903 	    ((g = fopen(destFile, FOPWBIN)) == NULL))
904 	    /* Can't open files */
905 	    return -1;
906 
907 #ifdef MACTC5
908 		{
909 		FInfo finderInfo;
910 		c2pstr(srcFile);
911 		c2pstr(destFile);
912 		if(GetFInfo((uchar *)srcFile,0,&finderInfo)==0)
913 			SetFInfo((uchar *)destFile,0,&finderInfo);
914 		p2cstr((uchar *)srcFile);
915 		p2cstr((uchar *)destFile);
916 		}
917 #endif
918 
919 	/* Get file length and copy it */
920 	fseek(f, 0L, SEEK_END);
921 	fileLength = ftell(f);
922 	rewind(f);
923 	status = copyfile(f, g, fileLength);
924 	if (write_error(g))
925 	    status = -1;
926 
927 	/* Zap source file if the copy went OK, otherwise zap the (possibly
928 	   incomplete) destination file */
929 	if (status >= 0) {
930 	    wipeout(f);		/* Zap source file */
931 	    fclose(f);
932 	    remove(srcFile);
933 	    fclose(g);
934 	} else {
935 	    if (is_regular_file(destFile)) {
936 		wipeout(g);	/* Zap destination file */
937 		fclose(g);
938 		remove(destFile);
939 	    } else {
940 		fclose(g);
941 	    }
942 	    fclose(f);
943 	}
944     }
945     return status;
946 }
947 
948 /* read the data from stdin to the phantom input file */
readPhantomInput(char * filename)949 int readPhantomInput(char *filename)
950 {
951     FILE *outFilePtr;
952     byte buffer[512];
953     int bytesRead, status = 0;
954 
955     if (verbose)
956 	fprintf(pgpout, "writing stdin to file %s\n", filename);
957     if ((outFilePtr = fopen(filename, FOPWBIN)) == NULL)
958 	return -1;
959 
960 #if defined(MSDOS) || defined(OS2) || defined (WIN32)
961     /* Under DOS must set input stream to binary mode to avoid data mangling */
962     setmode(fileno(stdin), O_BINARY);
963 #endif				/* MSDOS || OS2 */
964     while ((bytesRead = fread(buffer, 1, 512, stdin)) > 0)
965 	if (fwrite(buffer, 1, bytesRead, outFilePtr) != bytesRead) {
966 	    status = -1;
967 	    break;
968 	}
969     if (write_error(outFilePtr))
970 	status = -1;
971     fclose(outFilePtr);
972 #if defined(MSDOS) || defined(OS2) || defined (WIN32)
973     setmode(fileno(stdin), O_TEXT);	/* Reset stream */
974 #endif				/* MSDOS || OS2 */
975     return status;
976 }
977 
978 /* write the data from the phantom output file to stdout */
writePhantomOutput(char * filename)979 int writePhantomOutput(char *filename)
980 {
981     FILE *outFilePtr;
982     byte buffer[512];
983     int bytesRead, status = 0;
984 
985     if (verbose)
986 	fprintf(pgpout, "writing file %s to stdout\n", filename);
987     /* this can't fail since we just created the file */
988     outFilePtr = fopen(filename, FOPRBIN);
989 
990 #if defined(MSDOS) || defined(OS2) || defined (WIN32)
991     setmode(fileno(stdout), O_BINARY);
992 #endif				/* MSDOS || OS2 */
993     while ((bytesRead = fread(buffer, 1, 512, outFilePtr)) > 0)
994 	if (fwrite(buffer, 1, bytesRead, stdout) != bytesRead) {
995 	    status = -1;
996 	    break;
997 	}
998     fclose(outFilePtr);
999     fflush(stdout);
1000     if (ferror(stdout)) {
1001 	status = -1;
1002 	fprintf(pgpout, LANG("\007Write error on stdout.\n"));
1003     }
1004 #if defined(MSDOS) || defined(OS2) || defined (WIN32)
1005     setmode(fileno(stdout), O_TEXT);
1006 #endif				/* MSDOS || OS2 */
1007 
1008     return status;
1009 }
1010 
1011 /* Return the size from the current position of file f to the end */
fsize(FILE * f)1012 word32 fsize(FILE * f)
1013 {
1014     long fpos = ftell(f);
1015     long fpos2;
1016 
1017     fseek(f, 0L, SEEK_END);
1018     fpos2 = ftell(f);
1019     fseek(f, fpos, SEEK_SET);
1020     return (word32) (fpos2 - fpos);
1021 }
1022 
1023 /* Return TRUE if file filename looks like a pure text file */
is_text_file(char * filename)1024 int is_text_file (char *filename) /* EWS */
1025 {
1026     FILE *f = fopen(filename,"r");      /* FOPRBIN gives problem with VMS */
1027     int i, n, lfctr = 0;
1028     unsigned char buf[512];
1029     unsigned char *bufptr = buf;
1030     unsigned char c;
1031 
1032     if (!f)
1033         return FALSE;          /* error opening it, so not a text file */
1034     i = n = fread (buf, 1, sizeof(buf), f);
1035     fclose(f);
1036     if (n <= 0)
1037         return FALSE;          /* empty file or error, not a text file */
1038     if (compressSignature(buf) >= 0)
1039         return FALSE;
1040     while (i--) {
1041         c = *bufptr++;
1042         if (c == '\n' || c == '\r')
1043             lfctr=0;
1044         else /* allow BEL BS HT LF VT FF CR EOF ESC control characters */
1045         {
1046 #ifdef EBCDIC
1047 	    if (iscntrl(c) && c!=BEL && c!=BS && c!=HT && c!=LF && c!=VT && c!=FF && c!=CR && c!=EOF && c!=ESC)
1048 #else
1049             if (c < '\007' || (c > '\r' && c < ' ' && c != '\032' && c != '\033'))
1050 #endif
1051                 return FALSE;  /* not a text file */
1052             lfctr++;
1053         }
1054     }
1055     return TRUE;
1056 }                               /* is_text_file */
1057 
xmalloc(unsigned size)1058 VOID *xmalloc(unsigned size)
1059 {
1060     VOID *p;
1061     if (size == 0)
1062 	++size;
1063     p = malloc(size);
1064     if (p == NULL) {
1065 	fprintf(stderr, LANG("\n\007Out of memory.\n"));
1066 	exitPGP(1);
1067     }
1068     return p;
1069 }
1070 
1071 /*----------------------------------------------------------------------
1072  *	temporary file routines
1073  */
1074 
1075 
1076 #define MAXTMPF 8
1077 
1078 #define	TMP_INUSE	2
1079 
1080 static struct {
1081     char path[MAX_PATH];
1082     int flags;
1083     int num;
1084 } tmpf[MAXTMPF];
1085 
1086 static char tmpdir[256];	/* temporary file directory */
1087 static char outdir[256];	/* output directory */
1088 static char tmpbasename[64] = "pgptemp";	/* basename for
1089 						   temporary files */
1090 
1091 
1092 /*
1093  * set directory for temporary files.  path will be stored in
1094  * tmpdir[] with an appropriate trailing path separator.
1095  */
settmpdir(char * path)1096 void settmpdir(char *path)
1097 {
1098     char *p;
1099 
1100     if (path == NULL || *path == '\0') {
1101 	tmpdir[0] = '\0';
1102 	return;
1103     }
1104     strcpy(tmpdir, path);
1105     p = tmpdir + strlen(tmpdir) - 1;
1106 #ifdef MACTC5
1107 	if (*p != '/' && *p != '\\' && *p != ']' && *p != ':')
1108 	{	/* append path separator, either / or \ */
1109 		if ((p = strchr(tmpdir, '/')) == NULL &&
1110 			(p = strchr(tmpdir, '\\')) == NULL &&
1111 			(p = strchr(tmpdir, ':')) == NULL)
1112 			p = ":";	/* path did not contain / or \ or :, use : */
1113 		strncat(tmpdir, p, 1);
1114 #else
1115     if (*p != '/' && *p != '\\' && *p != ']' && *p != ':') {
1116 	/* append path separator, either / or \ */
1117 	if ((p = strchr(tmpdir, '/')) == NULL &&
1118 	    (p = strchr(tmpdir, '\\')) == NULL)
1119 	    p = "/";		/* path did not contain / or \, use / */
1120 	strncat(tmpdir, p, 1);
1121 #endif
1122     }
1123 }
1124 
1125 /*
1126  * set output directory to avoid a file copy when temp file is renamed to
1127  * output file.  the argument filename must be a valid path for a file, not
1128  * a directory.
1129  */
1130 void setoutdir(char *filename)
1131 {
1132     char *p;
1133 
1134     if (filename == NULL) {
1135 	strcpy(outdir, tmpdir);
1136 	return;
1137     }
1138     strcpy(outdir, filename);
1139     p = file_tail(outdir);
1140     strcpy(tmpbasename, p);
1141     *p = '\0';
1142     drop_extension(tmpbasename);
1143 #if !defined(BSD42) && !defined(BSD43) && !defined(sun)
1144     /*
1145      *  we don't depend on pathconf here, if it returns an incorrect value
1146      * for NAME_MAX (like Linux 0.97 with minix FS) finding a unique name
1147      * for temp files can fail.
1148      */
1149     tmpbasename[10] = '\0';	/* 14 char limit */
1150 #endif
1151 }
1152 
1153 /*
1154  * return a unique temporary file name
1155  */
1156 char *tempfile(int flags)
1157 {
1158     int i, j;
1159     int num;
1160     int fd;
1161 #ifndef UNIX
1162     FILE *fp;
1163 #endif
1164 
1165     for (i = 0; i < MAXTMPF; ++i)
1166 	if (tmpf[i].flags == 0)
1167 	    break;
1168 
1169     if (i == MAXTMPF) {
1170 	/* message only for debugging, no need for LANG */
1171 	fprintf(stderr, "\n\007Out of temporary files\n");
1172 	return NULL;
1173     }
1174   again:
1175     num = 0;
1176     do {
1177 	for (j = 0; j < MAXTMPF; ++j)
1178 	    if (tmpf[j].flags && tmpf[j].num == num)
1179 		break;
1180 	if (j < MAXTMPF)
1181 	    continue;		/* sequence number already in use */
1182 	sprintf(tmpf[i].path, "%s%s.%c%02d",
1183 		((flags & TMP_TMPDIR) && *tmpdir ? tmpdir : outdir),
1184 		tmpbasename, TMP_EXT, num);
1185 	if (!file_exists(tmpf[i].path))
1186 	    break;
1187     }
1188     while (++num < 100);
1189 
1190     if (num == 100) {
1191 	fprintf(pgpout, "\n\007tempfile: cannot find unique name\n");
1192 	return NULL;
1193     }
1194 #if defined(UNIX) || defined(VMS)
1195     if ((fd = open(tmpf[i].path, O_EXCL | O_RDWR | O_CREAT, 0600)) != -1)
1196 	close(fd);
1197 #else
1198     if ((fp = fopen(tmpf[i].path, "w")) != NULL)
1199 	fclose(fp);
1200     fd = (fp == NULL ? -1 : 0);
1201 #endif
1202 
1203     if (fd == -1) {
1204 	if (!(flags & TMP_TMPDIR)) {
1205 	    flags |= TMP_TMPDIR;
1206 	    goto again;
1207 	}
1208 #ifdef UNIX
1209 	else if (tmpdir[0] == '\0') {
1210 	    strcpy(tmpdir, "/tmp/");
1211 	    goto again;
1212 	}
1213 #endif
1214     }
1215     if (fd == -1) {
1216 	fprintf(pgpout, LANG("\n\007Cannot create temporary file '%s'\n"),
1217 		tmpf[i].path);
1218 	user_error();
1219     }
1220 #if defined(VMS) || defined(C370)
1221     remove(tmpf[i].path);
1222 #endif
1223 
1224     tmpf[i].num = num;
1225     tmpf[i].flags = flags | TMP_INUSE;
1226     if (verbose)
1227 	fprintf(pgpout, "tempfile: created '%s'\n", tmpf[i].path);
1228     return tmpf[i].path;
1229 }				/* tempfile */
1230 
1231 /*
1232  * remove temporary file, wipe if necessary.
1233  */
1234 void rmtemp(char *name)
1235 {
1236     int i;
1237 
1238     for (i = 0; i < MAXTMPF; ++i)
1239 	if (tmpf[i].flags && strcmp(tmpf[i].path, name) == 0)
1240 	    break;
1241 
1242     if (i < MAXTMPF) {
1243 	if (strlen(name) > 3 && name[strlen(name) - 3] == TMP_EXT) {
1244 	    /* only remove file if name hasn't changed */
1245 	    if (verbose)
1246 		fprintf(pgpout, "rmtemp: removing '%s'\n", name);
1247 	    if (tmpf[i].flags & TMP_WIPE)
1248 		wipefile(name);
1249 	    if (!remove(name)) {
1250 		tmpf[i].flags = 0;
1251 	    } else if (verbose) {
1252 		fprintf(stderr, "\nrmtemp: Failed to remove %s", name);
1253 		perror("\nError");
1254 	    }
1255 	} else if (verbose)
1256 	    fprintf(pgpout, "rmtemp: not removing '%s'\n", name);
1257     }
1258 }				/* rmtemp */
1259 
1260 /*
1261  * make temporary file permanent, returns the new name.
1262  */
1263 char *savetemp(char *name, char *newname)
1264 {
1265     int i, overwrite;
1266 
1267     if (strcmp(name, newname) == 0)
1268 	return name;
1269 
1270     for (i = 0; i < MAXTMPF; ++i)
1271 	if (tmpf[i].flags && strcmp(tmpf[i].path, name) == 0)
1272 	    break;
1273 
1274     if (i < MAXTMPF) {
1275 	if (strlen(name) < 4 || name[strlen(name) - 3] != TMP_EXT) {
1276 	    if (verbose)
1277 		fprintf(pgpout, "savetemp: not renaming '%s' to '%s'\n",
1278 			name, newname);
1279 	    return name;	/* return original file name */
1280 	}
1281     }
1282 
1283     newname = ck_dup_output(newname, FALSE, TRUE);
1284     if (newname==NULL)
1285         return(NULL);
1286 
1287     if (verbose)
1288 	fprintf(pgpout, "savetemp: renaming '%s' to '%s'\n", name, newname);
1289     if (rename2(name, newname) < 0) {
1290 	/* errorLvl = UNKNOWN_FILE_ERROR; */
1291 	fprintf(pgpout, LANG("Can't create output file '%s'\n"), newname);
1292 	return NULL;
1293     }
1294     if (i < MAXTMPF)
1295 	tmpf[i].flags = 0;
1296     return newname;
1297 } /* savetemp */
1298 
1299 char *ck_dup_output(char *newname, boolean notest, boolean delete_dup)
1300 {
1301     int overwrite;
1302     static char buf[MAX_PATH];
1303 
1304     while (file_exists(newname)) {
1305 	if (batchmode && !force_flag) {
1306             fprintf(pgpout,LANG("\n\007Output file '%s' already exists.\n"),
1307                     newname);
1308 	    return NULL;
1309 	}
1310 	if (is_regular_file(newname)) {
1311 	    if (force_flag) {
1312 		/* remove without asking */
1313 		if (delete_dup) remove(newname);
1314 		break;
1315 	    }
1316 	    fprintf(pgpout,
1317 	    LANG("\n\007Output file '%s' already exists.  Overwrite (y/N)? "),
1318 		 newname);
1319 	    overwrite = getyesno('n');
1320 	} else {
1321 	    fprintf(pgpout,
1322 		    LANG("\n\007Output file '%s' already exists.\n"),newname);
1323 	    if (force_flag)	/* never remove special file */
1324 		return NULL;
1325 	    overwrite = FALSE;
1326 	}
1327 
1328 	if (!overwrite) {
1329 	    fprintf(pgpout, "\n");
1330 	    fprintf(pgpout, LANG("Enter new file name:"));
1331 	    fprintf(pgpout, " ");
1332 #ifdef MACTC5
1333 			if (!GetFilePath(LANG("Enter new file name:"), buf, PUTFILE))
1334 				return(NULL);
1335 			strcpy(newname, buf);
1336 			fprintf(pgpout, "%s\n",buf);
1337 #else
1338 	    getstring(buf, MAX_PATH - 1, TRUE);
1339 	    if (buf[0] == '\0')
1340 		return(NULL);
1341 	    newname = buf;
1342 #endif
1343 	} else if (delete_dup)
1344             remove(newname);
1345         else
1346             break;
1347 
1348         if (notest) break;
1349     }
1350     return(newname);
1351 } /* ck_dup_output */
1352 
1353 /*
1354  * like savetemp(), only make backup of destname if it exists
1355  */
1356 int savetempbak(char *tmpname, char *destname)
1357 {
1358     char bakpath[MAX_PATH];
1359 #ifdef MACTC5
1360 	byte header[8];
1361 #endif
1362 #ifdef UNIX
1363     int mode = -1;
1364 #endif
1365 
1366     if (is_tempfile(destname)) {
1367 	remove(destname);
1368     } else {
1369 	if (file_exists(destname)) {
1370 #ifdef UNIX
1371 	    struct stat st;
1372 	    if (stat(destname, &st) != -1)
1373 		mode = st.st_mode & 07777;
1374 #endif
1375 	    strcpy(bakpath, destname);
1376 	    force_extension(bakpath, BAK_EXTENSION);
1377 	    remove(bakpath);
1378 #if defined(VMS) || defined(C370)
1379 	    if (rename(destname, bakpath) != 0)
1380 #else
1381 	    if (rename(destname, bakpath) == -1)
1382 #endif
1383 		return -1;
1384 #ifdef MACTC5
1385 		get_header_info_from_file(bakpath, header, 8 );
1386 		if (header[0] == CTB_CERT_SECKEY)
1387 			PGPSetFinfo(bakpath,'SKey','MPGP');
1388 		if (header[0] == CTB_CERT_PUBKEY)
1389 			PGPSetFinfo(bakpath,'PKey','MPGP');
1390 #endif
1391 	}
1392     }
1393     if (savetemp(tmpname, destname) == NULL)
1394 	return -1;
1395 #if defined(UNIX)
1396     if (mode != -1)
1397 	chmod(destname, mode);
1398 #elif defined(MACTC5)
1399 	get_header_info_from_file(destname, header, 8 );
1400 	if (header[0] == CTB_CERT_SECKEY)
1401 		PGPSetFinfo(destname,'SKey','MPGP');
1402 	if (header[0] == CTB_CERT_PUBKEY)
1403 		PGPSetFinfo(destname,'PKey','MPGP');
1404 #endif
1405     return 0;
1406 }
1407 
1408 /*
1409  * remove all temporary files and wipe them if necessary
1410  */
1411 void cleanup_tmpf(void)
1412 {
1413     int i;
1414 
1415     for (i = 0; i < MAXTMPF; ++i)
1416 	if (tmpf[i].flags)
1417 	    rmtemp(tmpf[i].path);
1418 }				/* cleanup_tmpf */
1419 
1420 #ifdef MACTC5
1421 void mac_cleanup_tmpf(void)
1422 {
1423 	int i,err;
1424     HFileParam pb;
1425     char fname[256];
1426 	for (i = 0; i < MAXTMPF; ++i)
1427 		if (tmpf[i].flags)
1428 	       {
1429 	        strcpy(fname,tmpf[i].path);
1430 	        pb.ioCompletion=nil;
1431 	        c2pstr(fname);
1432 	        pb.ioNamePtr=(uchar *)fname;
1433 	        pb.ioVRefNum=0;
1434 	        pb.ioFDirIndex=0;
1435 	        pb.ioFRefNum=0;
1436 	        pb.ioDirID=0;
1437 	        err=PBHGetFInfo((HParmBlkPtr)&pb,false);
1438 	        if (pb.ioFRefNum!=0){
1439 	            strcpy(fname,tmpf[i].path);
1440 	            pb.ioCompletion=nil;
1441 	            c2pstr(fname);
1442 	            pb.ioNamePtr=(uchar *)fname;
1443 	            pb.ioVRefNum=0;
1444 	            pb.ioDirID=0;
1445 	            err=PBClose((ParmBlkPtr)&pb,false);
1446 	            }
1447 			rmtemp(tmpf[i].path);
1448 			}
1449 }	/* mac_cleanup_tmpf */
1450 #endif
1451 
1452 /*
1453  * Routines to search for the manuals.
1454  *
1455  * Why all this code?
1456  *
1457  * Some people may object to PGP insisting on finding the manual somewhere
1458  * in the neighborhood to generate a key.  They bristle against this
1459  * seemingly authoritarian attitude.  Some people have even modified PGP
1460  * to defeat this feature, and redistributed their hotwired version to
1461  * others.  That creates problems for me (PRZ).
1462  *
1463  * Here is the problem.  Before I added this feature, there were maimed
1464  * versions of the PGP distribution package floating around that lacked
1465  * the manual.  One of them was uploaded to Compuserve, and was
1466  * distributed to countless users who called me on the phone to ask me why
1467  * such a complicated program had no manual.  It spread out to BBS systems
1468  * around the country.  And a freeware distributor got hold of the package
1469  * from Compuserve and enshrined it on CD-ROM, distributing thousands of
1470  * copies without the manual.  What a mess.
1471  *
1472  * Please don't make my life harder by modifying PGP to disable this
1473  * feature so that others may redistribute PGP without the manual.  If you
1474  * run PGP on a palmtop with no memory for the manual, is it too much to
1475  * ask that you type one little extra word on the command line to do a key
1476  * generation, a command that is seldom used by people who already know
1477  * how to use PGP?  If you can't stand even this trivial inconvenience,
1478  * can you suggest a better method of reducing PGP's distribution without
1479  * the manual?
1480  */
1481 
1482 static unsigned ext_missing(char *prefix)
1483 {
1484     static char const *const extensions[] =
1485 #ifdef VMS
1486 	{ ".doc", ".txt", ".man", ".tex", ".", 0 };
1487 #else
1488 	{ ".doc", ".txt", ".man", ".tex", "", 0 };
1489 #endif
1490     char const *const *p;
1491     char *end = prefix + strlen(prefix);
1492 
1493     for (p = extensions; *p; p++) {
1494 	strcpy(end, *p);
1495 #if 0				/* Debugging code */
1496 	fprintf(pgpout, "Looking for \"%s\"\n", prefix);
1497 #endif
1498 	if (file_exists(prefix))
1499 	    return 0;
1500     }
1501     return 1;
1502 }
1503 
1504 /*
1505  * Returns mask of files missing
1506  */
1507 static unsigned files_missing(char *prefix)
1508 {
1509 /* This changed to incorporate the changes in the Documentation subdirectory */
1510 #ifdef MACTC5
1511 	static char const *const names[] =
1512 	{"Volume I", "Volume II", 0};
1513 #else
1514     static char const *const names[] =
1515     {"pgpdoc1", "pgpdoc2", 0};
1516 #endif
1517     char const *const *p;
1518     unsigned bit, mask = 3;
1519     int len = strlen(prefix);
1520 
1521 #ifndef MACTC5
1522 	/* Cannot do this on the macintosh because file_exists returns false on
1523 	   directories */
1524 #ifndef VMS
1525     /*
1526      * Optimization: if directory doesn't exist, stop.  But access()
1527      * (used internally by file_exists()) doesn't work on dirs under VMS.
1528      */
1529     if (prefix[0] && !file_exists(prefix))	/* Directory doesn't exist? */
1530 	return mask;
1531 #endif /* VMS */
1532 #endif /* MACTC5 */
1533     if (len && strchr(DIRSEPS, prefix[len - 1]) == 0)
1534 	prefix[len++] = DIRSEPS[0];
1535     for (p = names, bit = 1; *p; p++, bit <<= 1) {
1536 	strcpy(prefix + len, *p);
1537 	if (!ext_missing(prefix))
1538 	    mask &= ~bit;
1539     }
1540 
1541     return mask;		/* Bitmask of which files exist */
1542 }
1543 
1544 /*
1545  * Search prefix directory and doc subdirectory.
1546  */
1547 static unsigned doc_missing(char *prefix)
1548 {
1549     unsigned mask;
1550     int len = strlen(prefix);
1551 
1552     mask = files_missing(prefix);
1553     if (!mask)
1554 	return 0;
1555 #if defined(VMS)
1556     if (len && prefix[len - 1] == ']') {
1557 	strcpy(prefix + len - 1, ".doc]");
1558     } else {
1559 	assert(!len || prefix[len - 1] == ':');
1560 	strcpy(prefix + len, "[doc]");
1561     }
1562 #elif defined(MACTC5)
1563 	/* on the macintosh we must look for the documents in
1564 	   Documentation:PGP User's Guide: folder  */
1565 	if (len && prefix[len - 1] != DIRSEPS[0])
1566 		prefix[len++] = DIRSEPS[0];
1567 	strcpy(prefix + len, "Documentation");
1568 	len = strlen(prefix);
1569 	mask &= files_missing(prefix);
1570 	if (!mask)
1571 		return 0;
1572 	if (len && prefix[len - 1] != DIRSEPS[0])
1573 		prefix[len++] = DIRSEPS[0];
1574 	strcpy(prefix + len, "PGP User's Guide");
1575 	mask &= files_missing(prefix);
1576 	if (!mask)
1577 		return 0;
1578 #else
1579     if (len && prefix[len - 1] != DIRSEPS[0])
1580 	prefix[len++] = DIRSEPS[0];
1581     strcpy(prefix + len, "doc");
1582 #endif
1583 
1584     mask &= files_missing(prefix);
1585 
1586     prefix[len] = '\0';
1587     return mask;
1588 }
1589 
1590 /*
1591  * Expands a leading environment variable.  Returns 0 on success;
1592  * <0 if there is an error.
1593  */
1594 static int expand_env(char const *src, char *dest)
1595 {
1596     char const *var, *suffix;
1597     unsigned len;
1598 
1599     if (*src != '$') {
1600 	strcpy(dest, src);
1601 	return 0;
1602     }
1603     /* Find end of variable */
1604     if (src[1] == '{') {	/* ${FOO} form */
1605 	var = src + 2;
1606 	len = strchr(var, '}') - (char*) var;
1607 	suffix = src + 2 + len + 1;
1608     } else {			/* $FOO form - allow $ for VMS */
1609 	var = src + 1;
1610 	len = strspn(var, "ABCDEFGHIJKLMNOPQRSTUVWXYZ$_");
1611 	suffix = src + 1 + len;
1612     }
1613 
1614     memcpy(dest, var, len);	/* Copy name */
1615     dest[len] = '\0';		/* Null-terminate */
1616 
1617     var = getenv(dest);
1618     if (!var || !*var)
1619 	return -1;		/* No env variable */
1620 
1621     /* Copy expanded form to destination */
1622     strcpy(dest, var);
1623 
1624     /* Add tail */
1625     strcat(dest, suffix);
1626 
1627     return 0;
1628 }
1629 
1630 /* Don't forget to change 'pgp26' whenever you update rel_version past 2.6 */
1631 char const *const manual_dirs[] =
1632 {
1633 #if defined(VMS)
1634     "$PGPPATH", "", "[pgp]", "[pgp26]", "[pgp263]",
1635     PGP_SYSTEM_DIR, "SYS$LOGIN:", "SYS$LOGIN:[pgp]",
1636     "SYS$LOGIN:[pgp26]", "SYS$LOGIN:[pgp263]", "[-]",
1637 #elif defined(UNIX)
1638     "$PGPPATH", "", "pgp", "pgp26", "pgp263", PGP_SYSTEM_DIR,
1639     "$HOME/.pgp", "$HOME", "$HOME/pgp", "$HOME/pgp26", "..",
1640 #elif defined(AMIGA)
1641     "$PGPPATH", "", "pgp", "pgp26", ":pgp", ":pgp26", ":pgp263",
1642     ":", "/",
1643 #else				/* MSDOS or ATARI */
1644     "$PGPPATH", "", "pgp", "pgp26", "\\pgp", "\\pgp26", "\\pgp263",
1645     "\\", "..", "c:\\pgp", "c:\\pgp26",
1646 #endif
1647     0};
1648 
1649 #ifdef MACTC5
1650 extern char appPathName[];
1651 #endif
1652 
1653 unsigned manuals_missing(void)
1654 {
1655     char buf[256];
1656     unsigned mask = ~((unsigned)0);
1657     char const *const *p;
1658 
1659 #ifdef MACTC5
1660 	strcpy(buf, appPathName);
1661 	mask &= doc_missing(buf);
1662 	return mask;
1663 #endif /* MACTC5 */
1664     for (p = manual_dirs; *p; p++) {
1665 	if (expand_env(*p, buf) < 0)
1666 	    continue;		/* Ignore */
1667 	mask &= doc_missing(buf);
1668 	if (!mask)
1669 	    break;
1670     }
1671 
1672     return mask;
1673 }
1674 
1675 /*
1676  * Why all this code?
1677  *
1678  * See block of comments above.
1679  */
1680