|
|
1.1.1.5 ! root 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-1994 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) ! 56: #include <io.h> ! 57: #include <fcntl.h> ! 58: #endif ! 59: ! 60: #ifndef F_OK ! 61: #define F_OK 0 ! 62: #define X_OK 1 ! 63: #define W_OK 2 ! 64: #define R_OK 4 ! 65: #endif /* !F_OK */ ! 66: ! 67: /* ! 68: * DIRSEPS is a string of possible directory-separation characters ! 69: * The first one is the preferred one, which goes in between ! 70: * PGPPATH and the file name if PGPPATH is not terminated with a ! 71: * directory separator. ! 72: */ ! 73: ! 74: #if defined(MSDOS) || defined(__MSDOS__) ! 75: static char const DIRSEPS[] = "\\/:"; ! 76: #define BSLASH ! 77: ! 78: #elif defined(ATARI) ! 79: static char const DIRSEPS[] = "\\/:"; ! 80: #define BSLASH ! 81: ! 82: #elif defined(UNIX) ! 83: static char const DIRSEPS[] = "/"; ! 84: #define MULTIPLE_DOTS ! 85: ! 86: #elif defined(AMIGA) ! 87: static char const DIRSEPS[] = "/:"; ! 88: #define MULTIPLE_DOTS ! 89: ! 90: #elif defined(VMS) ! 91: static char const DIRSEPS[] = "]:"; /* Any more? */ ! 92: ! 93: #else ! 94: #error Unknown OS ! 95: #endif ! 96: ! 97: ! 98: /* 1st character of temporary file extension */ ! 99: #define TMP_EXT '$' /* extensions are '.$##' */ ! 100: ! 101: /* The PGPPATH environment variable */ ! 102: ! 103: static char PGPPATH[] = "PGPPATH"; ! 104: ! 105: /* Disk buffers, used here and in crypto.c */ ! 106: byte textbuf[DISKBUFSIZE]; ! 107: static unsigned textbuf2[2*DISKBUFSIZE/sizeof(unsigned)]; ! 108: ! 109: boolean file_exists(char *filename) ! 110: /* Returns TRUE iff file exists. */ ! 111: { ! 112: return access(filename, F_OK) == 0; ! 113: } /* file_exists */ ! 114: ! 115: static boolean is_regular_file(char *filename) ! 116: { ! 117: #ifdef S_ISREG ! 118: struct stat st; ! 119: return stat(filename, &st) != -1 && S_ISREG(st.st_mode); ! 120: #else ! 121: return TRUE; ! 122: #endif ! 123: } ! 124: ! 125: ! 126: /* ! 127: * This wipes a file with pseudo-random data. The purpose of this is to ! 128: * make sure no sensitive information is left on the disk. The use ! 129: * of pseudo-random data is to defeat disk compression drivers (such as ! 130: * Stacker and dblspace) so that we are guaranteed that the entire file ! 131: * has been overwritten. ! 132: * ! 133: * Note that the file MUST be open for read/write. ! 134: * ! 135: * It may not work to eliminate everything from non-volatile storage ! 136: * if the OS you're using does its own paging or swapping. Then ! 137: * it's an issue of how the OS's paging device is wiped, and you can ! 138: * only hope that the space will be reused within a few seconds. ! 139: * ! 140: * Also, some file systems (in particular, the Logging File System ! 141: * for Sprite) do not write new data in the same place as old data, ! 142: * defeating this wiping entirely. Fortunately, such systems ! 143: * usually don't need a swap file, and for small temp files, they ! 144: * do write-behind, so if you create and delete a file fast enough, ! 145: * it never gets written to disk at all. ! 146: */ ! 147: ! 148: /* ! 149: * The data is randomly generated with the size of the file as a seed. ! 150: * The data should be random and not leak information. If someone is ! 151: * examining deleted files, presumably they can reconstruct the file size, ! 152: * so that's not a secret. H'm... this wiping algorithm makes it easy to, ! 153: * given a block of data, find the size of the file it came from ! 154: * and the offset of this block within it. That in turn reveals ! 155: * something about the state of the disk's allocation tables when the ! 156: * file was used, possibly making it easier to find other files created ! 157: * at neaby times - such as plaintext files. Is this acceptable? ! 158: */ ! 159: ! 160: /* ! 161: * Theory of operation: We use the additive congruential RNG ! 162: * r[i] = r[i-24] + r[i-55], from Knuth, Vol. 2. This is fast ! 163: * and has a long enough period that there should be no repetitions ! 164: * in even a huge file. It is seeded with r[-55] through r[-1] ! 165: * using another polynomial-based RNG. We seed a linear feedback ! 166: * shift register (CRC generator) with the size of the swap file, ! 167: * and clock in 0 bits. Each 32 bits, the value of the generator is ! 168: * taken as the next integer. This is just to ensure a reasonably ! 169: * even mix of 1's and 0's in the initialization vector. ! 170: */ ! 171: ! 172: /* ! 173: * This is the CRC-32 polynomial, which should be okay for random ! 174: * number generation. ! 175: * 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 ! 176: * = 1 0000 0100 1100 0001 0001 1101 1011 0111 ! 177: * = 0x04c11db7 ! 178: */ ! 179: #define POLY 0x04c11db7 ! 180: ! 181: static int ! 182: wipeout(FILE *f) ! 183: { ! 184: unsigned *p1, *p2, *p3; ! 185: unsigned long len; ! 186: unsigned long t; ! 187: int i; ! 188: ! 189: /* Get the file size */ ! 190: fseek(f, 0L, SEEK_END); ! 191: len = ftell(f); ! 192: rewind(f); ! 193: ! 194: /* Seed of first RNG. Inverted to get more 1 bits */ ! 195: t = ~len; ! 196: ! 197: /* Initialize first 55 words of buf with pseudo-random stuff */ ! 198: p1 = (unsigned *)textbuf2 + 55; ! 199: do { ! 200: for (i = 0; i < 32; i++) ! 201: t = (t & 0x80000000) ? t<<1 ^ POLY : t<<1; ! 202: *--p1 = (unsigned)t; ! 203: } while (p1 > (unsigned *)textbuf2); ! 204: ! 205: while (len) { ! 206: /* Fill buffer with pseudo-random integers */ ! 207: ! 208: p3 = (unsigned *)textbuf2 + 55; ! 209: p2 = (unsigned *)textbuf2 + 24; ! 210: p1 = (unsigned *)textbuf2 + sizeof(textbuf2)/sizeof(*p1); ! 211: do { ! 212: *--p1 = *--p2 + *--p3; ! 213: } while (p2 > (unsigned *)textbuf2); ! 214: ! 215: p2 = (unsigned *)textbuf2 + sizeof(textbuf2)/sizeof(*p1); ! 216: do { ! 217: *--p1 = *--p2 + *--p3; ! 218: } while (p3 > (unsigned *)textbuf2); ! 219: ! 220: p3 = (unsigned *)textbuf2 + sizeof(textbuf2)/sizeof(*p3); ! 221: do { ! 222: *--p1 = *--p2 + *--p3; ! 223: } while (p1 > (unsigned *)textbuf2); ! 224: ! 225: /* Write it out - yes, we're ignoring errors */ ! 226: if (len > sizeof(textbuf2)) { ! 227: fwrite((char const *)textbuf2, sizeof(textbuf2), 1, f); ! 228: len -= sizeof(textbuf2); ! 229: } else { ! 230: fwrite((char const *)textbuf2, len, 1, f); ! 231: len = 0; ! 232: } ! 233: } ! 234: } ! 235: ! 236: ! 237: int wipefile(char *filename) ! 238: /* ! 239: * Completely overwrite and erase file, so that no sensitive ! 240: * information is left on the disk. ! 241: */ ! 242: { ! 243: FILE *f; ! 244: /* open file f for read/write, in binary (not text) mode...*/ ! 245: if ((f = fopen(filename,FOPRWBIN)) == NULL) ! 246: return -1; /* error - file can't be opened */ ! 247: wipeout(f); ! 248: fclose(f); ! 249: return 0; /* normal return */ ! 250: } /* wipefile */ ! 251: ! 252: char *file_tail (char *filename) ! 253: /* ! 254: * Returns the part of a filename after all directory specifiers. ! 255: */ ! 256: { ! 257: char *p; ! 258: char const *s = DIRSEPS; ! 259: ! 260: while (*s) { ! 261: p = strrchr(filename, *s); ! 262: if (p) ! 263: filename = p+1; ! 264: s++; ! 265: } ! 266: ! 267: return filename; ! 268: } ! 269: ! 270: ! 271: boolean has_extension(char *filename, char *extension) ! 272: /* return TRUE if extension matches the end of filename */ ! 273: { ! 274: int lf = strlen(filename); ! 275: int lx = strlen(extension); ! 276: ! 277: if (lf <= lx) ! 278: return FALSE; ! 279: return !strcmp(filename + lf - lx, extension); ! 280: } ! 281: ! 282: boolean is_tempfile(char *path) ! 283: /* return TRUE if path is a filename created by tempfile() */ ! 284: /* Filename matches "*.$[0-9][0-9]" */ ! 285: { ! 286: char *p = strrchr(path, '.'); ! 287: ! 288: return p != NULL && p[1] == TMP_EXT && ! 289: isdigit(p[2]) && isdigit(p[3]) && p[4] == '\0'; ! 290: } ! 291: ! 292: boolean no_extension(char *filename) ! 293: /* ! 294: * Returns TRUE if user left off file extension, allowing default. ! 295: * Note that the name is misleading if multiple dots are allowed. ! 296: * not_pgp_extension or something would be better. ! 297: */ ! 298: { ! 299: #ifdef MULTIPLE_DOTS /* filename can have more than one dot */ ! 300: if (has_extension(filename, ASC_EXTENSION) || ! 301: has_extension(filename, PGP_EXTENSION) || ! 302: has_extension(filename, SIG_EXTENSION) || ! 303: is_tempfile(filename)) ! 304: return FALSE; ! 305: else ! 306: return TRUE; ! 307: #else ! 308: filename = file_tail(filename); ! 309: ! 310: return strrchr(filename, '.') == NULL; ! 311: #endif ! 312: } /* no_extension */ ! 313: ! 314: ! 315: void drop_extension(char *filename) ! 316: /* deletes trailing ".xxx" file extension after the period. */ ! 317: { ! 318: if (!no_extension(filename)) ! 319: *strrchr(filename,'.') = '\0'; ! 320: } /* drop_extension */ ! 321: ! 322: ! 323: void default_extension(char *filename, char *extension) ! 324: /* append filename extension if there isn't one already. */ ! 325: { ! 326: if (no_extension(filename)) ! 327: strcat(filename,extension); ! 328: } /* default_extension */ ! 329: ! 330: #ifndef MAX_NAMELEN ! 331: #if defined(AMIGA) || defined(NeXT) || (defined(BSD) && BSD > 41) || (defined(sun) && defined(i386)) ! 332: #define MAX_NAMELEN 255 ! 333: #else ! 334: #include <limits.h> ! 335: #endif ! 336: #endif ! 337: ! 338: static void truncate_name(char *path, int ext_len) ! 339: /* truncate the filename so that an extension can be tacked on. */ ! 340: { ! 341: #ifdef UNIX /* for other systems this is a no-op */ ! 342: char *p; ! 343: #ifdef MAX_NAMELEN /* overrides the use of pathconf() */ ! 344: int namemax = MAX_NAMELEN; ! 345: #else ! 346: int namemax; ! 347: #ifdef _PC_NAME_MAX ! 348: char dir[MAX_PATH]; ! 349: ! 350: strcpy(dir, path); ! 351: if ((p = strrchr(dir, '/')) == NULL) { ! 352: strcpy(dir, "."); ! 353: } else { ! 354: if (p == dir) ! 355: ++p; ! 356: *p = '\0'; ! 357: } ! 358: if ((namemax = pathconf(dir, _PC_NAME_MAX)) <= ext_len) ! 359: return; ! 360: #else ! 361: #ifdef NAME_MAX ! 362: namemax = NAME_MAX; ! 363: #else ! 364: namemax = 14; ! 365: #endif /* NAME_MAX */ ! 366: #endif /* _PC_NAME_MAX */ ! 367: #endif /* MAX_NAMELEN */ ! 368: ! 369: if ((p = strrchr(path, '/')) == NULL) ! 370: p = path; ! 371: else ! 372: ++p; ! 373: if (strlen(p) > namemax - ext_len) { ! 374: if (verbose) ! 375: fprintf(pgpout, "Truncating filename '%s' ", path); ! 376: p[namemax - ext_len] = '\0'; ! 377: if (verbose) ! 378: fprintf(pgpout, "to '%s'\n", path); ! 379: } ! 380: #endif /* UNIX */ ! 381: } ! 382: ! 383: void force_extension(char *filename, char *extension) ! 384: /* change the filename extension. */ ! 385: { ! 386: drop_extension(filename); /* out with the old */ ! 387: truncate_name(filename, strlen(extension)); ! 388: strcat(filename,extension); /* in with the new */ ! 389: } /* force_extension */ ! 390: ! 391: ! 392: boolean getyesno(char default_answer) ! 393: /* ! 394: * Get yes/no answer from user, returns TRUE for yes, FALSE for no. ! 395: * First the translations are checked, if they don't match 'y' and 'n' ! 396: * are tried. ! 397: */ ! 398: { ! 399: char buf[8]; ! 400: static char yes[8], no[8]; ! 401: ! 402: if (yes[0] == '\0') { ! 403: strncpy(yes, LANG("y"), 7); ! 404: strncpy(no, LANG("n"), 7); ! 405: } ! 406: if (!batchmode) { /* return default answer in batchmode */ ! 407: getstring(buf,6,TRUE); /* echo keyboard input */ ! 408: strlwr(buf); ! 409: if (!strncmp(buf, no, strlen(no))) ! 410: return FALSE; ! 411: if (!strncmp(buf, yes, strlen(yes))) ! 412: return TRUE; ! 413: if (buf[0] == 'n') ! 414: return FALSE; ! 415: if (buf[0] == 'y') ! 416: return TRUE; ! 417: } ! 418: return default_answer == 'y' ? TRUE : FALSE; ! 419: } /* getyesno */ ! 420: ! 421: ! 422: ! 423: char *maybe_force_extension(char *filename, char *extension) ! 424: /* if user consents to it, change the filename extension. */ ! 425: { ! 426: static char newname[MAX_PATH]; ! 427: if (!has_extension(filename,extension)) { ! 428: strcpy(newname,filename); ! 429: force_extension(newname,extension); ! 430: if (!file_exists(newname)) { ! 431: fprintf(pgpout,LANG("\nShould '%s' be renamed to '%s' [Y/n]? "), ! 432: filename,newname); ! 433: if (getyesno('y')) ! 434: return newname; ! 435: } ! 436: } ! 437: return NULL; ! 438: } /* maybe_force_extension */ ! 439: ! 440: /* ! 441: * Add a trailing directory separator to a name, if absent. ! 442: */ ! 443: static void ! 444: addslash(char *name) ! 445: { ! 446: int i = strlen(name); ! 447: ! 448: if (i != 0 && !strchr(DIRSEPS, name[i-1])) { ! 449: name[i] = DIRSEPS[0]; ! 450: name[i+1] = '\0'; ! 451: } ! 452: } ! 453: ! 454: char *buildfilename(char *result, char *fname) ! 455: /* ! 456: * Builds a filename with a complete path specifier from the environmental ! 457: * variable PGPPATH. ! 458: */ ! 459: { ! 460: char const *s = getenv(PGPPATH); ! 461: ! 462: result[0] = '\0'; ! 463: ! 464: if (s && strlen(s) <= 50) { ! 465: strcpy(result, s); ! 466: } ! 467: #ifdef UNIX ! 468: /* On Unix, default to $HOME/.pgp, otherwise, current directory. */ ! 469: else { ! 470: s = getenv("HOME"); ! 471: if (s && strlen(s) <= 50) { ! 472: strcpy(result, s); ! 473: addslash(result); ! 474: strcat(result, ".pgp"); ! 475: } ! 476: } ! 477: #endif /* UNIX */ ! 478: ! 479: addslash(result); ! 480: strcat(result,fname); ! 481: return result; ! 482: } /* buildfilename */ ! 483: ! 484: char *buildsysfilename(char *result, char *fname) ! 485: { ! 486: buildfilename(result, fname); ! 487: #ifdef PGP_SYSTEM_DIR ! 488: if (file_exists(result)) ! 489: return result; ! 490: strcpy(result, PGP_SYSTEM_DIR); ! 491: strcat(result, fname); ! 492: if (file_exists(result)) ! 493: return result; ! 494: buildfilename(result, fname); /* Put name back for error */ ! 495: #endif ! 496: return result; ! 497: } ! 498: ! 499: ! 500: void file_to_canon(char *filename) ! 501: /* Convert filename to canonical form, with slashes as separators */ ! 502: { ! 503: #ifdef BSLASH ! 504: while (*filename) { ! 505: if (*filename == '\\') ! 506: *filename = '/'; ! 507: ++filename; ! 508: } ! 509: #endif ! 510: } ! 511: ! 512: ! 513: int write_error(FILE *f) ! 514: { ! 515: fflush(f); ! 516: if (ferror(f)) { ! 517: #ifdef ENOSPC ! 518: if (errno == ENOSPC) ! 519: fprintf(pgpout, LANG("\nDisk full.\n")); ! 520: else ! 521: #endif ! 522: fprintf(pgpout, LANG("\nFile write error.\n")); ! 523: return -1; ! 524: } ! 525: return 0; ! 526: } ! 527: ! 528: int copyfile(FILE *f, FILE *g, word32 longcount) ! 529: /* copy file f to file g, for longcount bytes */ ! 530: { ! 531: int count, status = 0; ! 532: do { /* read and write the whole file... */ ! 533: if (longcount < (word32) DISKBUFSIZE) ! 534: count = (int)longcount; ! 535: else ! 536: count = DISKBUFSIZE; ! 537: count = fread(textbuf,1,count,f); ! 538: if (count>0) { ! 539: if (CONVERSION != NO_CONV) { ! 540: int i; ! 541: for (i = 0; i < count; i++) ! 542: textbuf[i] = (CONVERSION == EXT_CONV) ? ! 543: EXT_C(textbuf[i]) : ! 544: INT_C(textbuf[i]); ! 545: } ! 546: if (fwrite(textbuf,1,count,g) != count ) { ! 547: /* Problem: return error value */ ! 548: status = -1; ! 549: break; ! 550: } ! 551: longcount -= count; ! 552: } ! 553: /* if text block was short, exit loop */ ! 554: } while (count==DISKBUFSIZE); ! 555: burn(textbuf); /* burn sensitive data on stack */ ! 556: return status; ! 557: } /* copyfile */ ! 558: ! 559: int copyfilepos (FILE *f, FILE *g, word32 longcount, word32 fpos) ! 560: /* ! 561: * Like copyfile, but takes a position for file f. Returns with ! 562: * f and g pointing just past the copied data. ! 563: */ ! 564: { ! 565: fseek (f, fpos, SEEK_SET); ! 566: return copyfile (f, g, longcount); ! 567: } ! 568: ! 569: ! 570: int copyfile_to_canon (FILE *f, FILE *g, word32 longcount) ! 571: /* copy file f to file g, for longcount bytes. Convert to ! 572: * canonical form as we go. f is open in text mode. Canonical ! 573: * form uses crlf's as line separators. ! 574: */ ! 575: { ! 576: int count, status = 0; ! 577: byte c, *tb1, *tb2; ! 578: int i, nbytes; ! 579: int nspaces = 0; ! 580: do { /* read and write the whole file... */ ! 581: if (longcount < (word32) DISKBUFSIZE) ! 582: count = (int)longcount; ! 583: else ! 584: count = DISKBUFSIZE; ! 585: count = fread(textbuf,1,count,f); ! 586: if (count>0) { ! 587: /* Convert by adding CR before LF */ ! 588: tb1 = textbuf; ! 589: tb2 = (byte *)textbuf2; ! 590: for (i=0; i<count; ++i) { ! 591: switch (CONVERSION) { ! 592: case EXT_CONV: ! 593: c = EXT_C(*tb1++); ! 594: break; ! 595: case INT_CONV: ! 596: c = INT_C(*tb1++); ! 597: break; ! 598: default: ! 599: c = *tb1++; ! 600: } ! 601: if (strip_spaces) { ! 602: if (c == ' ') { ! 603: /* Don't output spaces yet */ ! 604: nspaces += 1; ! 605: } else { ! 606: if (c == '\n') { ! 607: *tb2++ = '\r'; ! 608: nspaces = 0; /* Delete trailing spaces */ ! 609: } ! 610: if (nspaces) { ! 611: /* Put out spaces now */ ! 612: do ! 613: *tb2++ = ' '; ! 614: while (--nspaces); ! 615: } ! 616: *tb2++ = c; ! 617: } ! 618: } else { ! 619: if (c == '\n') ! 620: *tb2++ = '\r'; ! 621: *tb2++ = c; ! 622: } ! 623: } ! 624: nbytes = tb2 - (byte *)textbuf2; ! 625: if (fwrite(textbuf2,1,nbytes,g) != nbytes ) { ! 626: /* Problem: return error value */ ! 627: status = -1; ! 628: break; ! 629: } ! 630: longcount -= count; ! 631: } ! 632: /* if text block was short, exit loop */ ! 633: } while (count==DISKBUFSIZE); ! 634: burn(textbuf); /* burn sensitive data on stack */ ! 635: burn(textbuf2); ! 636: return status; ! 637: } /* copyfile_to_canon */ ! 638: ! 639: ! 640: int copyfile_from_canon (FILE *f, FILE *g, word32 longcount) ! 641: /* copy file f to file g, for longcount bytes. Convert from ! 642: * canonical to local form as we go. g is open in text mode. Canonical ! 643: * form uses crlf's as line separators. ! 644: */ ! 645: { ! 646: int count, status = 0; ! 647: byte c, *tb1, *tb2; ! 648: int i, nbytes; ! 649: do { /* read and write the whole file... */ ! 650: if (longcount < (word32) DISKBUFSIZE) ! 651: count = (int)longcount; ! 652: else ! 653: count = DISKBUFSIZE; ! 654: count = fread(textbuf,1,count,f); ! 655: if (count>0) { ! 656: /* Convert by removing CR's */ ! 657: tb1 = textbuf; ! 658: tb2 = (byte *)textbuf2; ! 659: for (i=0; i<count; ++i) { ! 660: switch (CONVERSION) { ! 661: case EXT_CONV: ! 662: c = EXT_C(*tb1++); ! 663: break; ! 664: case INT_CONV: ! 665: c = INT_C(*tb1++); ! 666: break; ! 667: default: ! 668: c = *tb1++; ! 669: } ! 670: if (c != '\r') ! 671: *tb2++ = c; ! 672: } ! 673: nbytes = tb2 - (byte *)textbuf2; ! 674: if (fwrite(textbuf2,1,nbytes,g) != nbytes ) { ! 675: /* Problem: return error value */ ! 676: status = -1; ! 677: break; ! 678: } ! 679: longcount -= count; ! 680: } ! 681: /* if text block was short, exit loop */ ! 682: } while (count==DISKBUFSIZE); ! 683: burn(textbuf); /* burn sensitive data on stack */ ! 684: burn(textbuf2); ! 685: return status; ! 686: } /* copyfile_from_canon */ ! 687: ! 688: ! 689: int copyfiles_by_name(char *srcFile, char *destFile) ! 690: /* Copy srcFile to destFile */ ! 691: { ! 692: FILE *f, *g; ! 693: int status = 0; ! 694: long fileLength; ! 695: ! 696: f = fopen(srcFile, FOPRBIN); ! 697: if (f == NULL) ! 698: return -1; ! 699: g = fopen(destFile, FOPWBIN); ! 700: if (g == NULL) { ! 701: fclose(f); ! 702: return -1; ! 703: } ! 704: ! 705: /* Get file length and copy it */ ! 706: fseek(f,0L,SEEK_END); ! 707: fileLength = ftell(f); ! 708: rewind(f); ! 709: status = copyfile(f,g,fileLength); ! 710: fclose(f); ! 711: if (write_error(g)) ! 712: status = -1; ! 713: fclose(g); ! 714: return status; ! 715: } /* copyfiles_by_name */ ! 716: ! 717: ! 718: int make_canonical(char *srcFile, char *destFile) ! 719: /* Copy srcFile to destFile, converting to canonical text form */ ! 720: { ! 721: FILE *f, *g; ! 722: int status = 0; ! 723: long fileLength; ! 724: ! 725: if (((f=fopen(srcFile,FOPRTXT)) == NULL) || ! 726: ((g=fopen(destFile,FOPWBIN)) == NULL)) ! 727: /* Can't open files */ ! 728: return -1; ! 729: ! 730: /* Get file length and copy it */ ! 731: fseek(f,0L,SEEK_END); ! 732: fileLength = ftell(f); ! 733: rewind(f); ! 734: CONVERSION = INT_CONV; ! 735: status = copyfile_to_canon(f,g,fileLength); ! 736: CONVERSION = NO_CONV; ! 737: fclose(f); ! 738: if (write_error(g)) ! 739: status = -1; ! 740: fclose(g); ! 741: return status; ! 742: } /* make_canonical */ ! 743: ! 744: ! 745: int rename2(char *srcFile, char *destFile) ! 746: /* ! 747: * Like rename() but will try to copy the file if the rename fails. ! 748: * This is because under OS's with multiple physical volumes if the ! 749: * source and destination are on different volumes the rename will fail ! 750: */ ! 751: { ! 752: FILE *f, *g; ! 753: int status = 0; ! 754: long fileLength; ! 755: ! 756: #ifdef VMS ! 757: if (rename(srcFile,destFile) != 0) ! 758: #else ! 759: if (rename(srcFile,destFile) == -1) ! 760: #endif ! 761: { ! 762: /* Rename failed, try a copy */ ! 763: if (((f=fopen(srcFile,FOPRBIN)) == NULL) || ! 764: ((g=fopen(destFile,FOPWBIN)) == NULL)) ! 765: /* Can't open files */ ! 766: return -1; ! 767: ! 768: /* Get file length and copy it */ ! 769: fseek(f,0L,SEEK_END); ! 770: fileLength = ftell(f); ! 771: rewind(f); ! 772: status = copyfile(f,g,fileLength); ! 773: if (write_error(g)) ! 774: status = -1; ! 775: ! 776: /* Zap source file if the copy went OK, otherwise zap the (possibly ! 777: incomplete) destination file */ ! 778: if (status >= 0) { ! 779: wipeout(f); /* Zap source file */ ! 780: fclose(f); ! 781: remove(srcFile); ! 782: fclose(g); ! 783: } else { ! 784: if (is_regular_file(destFile)) { ! 785: wipeout(g); /* Zap destination file */ ! 786: fclose(g); ! 787: remove(destFile); ! 788: } else { ! 789: fclose(g); ! 790: } ! 791: fclose(f); ! 792: } ! 793: } ! 794: return status; ! 795: } ! 796: ! 797: ! 798: int readPhantomInput(char *filename) ! 799: /* read the data from stdin to the phantom input file */ ! 800: { ! 801: FILE *outFilePtr; ! 802: byte buffer[ 512 ]; ! 803: int bytesRead, status = 0; ! 804: ! 805: if (verbose) ! 806: fprintf(pgpout, "writing stdin to file %s\n", filename); ! 807: if ((outFilePtr = fopen(filename,FOPWBIN)) == NULL) ! 808: return -1; ! 809: ! 810: #if defined(MSDOS) || defined(OS2) ! 811: /* Under DOS must set input stream to binary mode to avoid data mangling */ ! 812: setmode(fileno(stdin), O_BINARY); ! 813: #endif /* MSDOS || OS2 */ ! 814: while ((bytesRead = fread (buffer, 1, 512, stdin)) > 0) ! 815: if (fwrite (buffer, 1, bytesRead, outFilePtr) != bytesRead) { ! 816: status = -1; ! 817: break; ! 818: } ! 819: if (write_error(outFilePtr)) ! 820: status = -1; ! 821: fclose (outFilePtr); ! 822: #if defined(MSDOS) || defined(OS2) ! 823: setmode(fileno(stdin), O_TEXT); /* Reset stream */ ! 824: #endif /* MSDOS || OS2 */ ! 825: return status; ! 826: } ! 827: ! 828: ! 829: int writePhantomOutput(char *filename) ! 830: /* write the data from the phantom output file to stdout */ ! 831: { FILE *outFilePtr; ! 832: byte buffer[ 512 ]; ! 833: int bytesRead, status = 0; ! 834: ! 835: if (verbose) ! 836: fprintf(pgpout, "writing file %s to stdout\n", filename); ! 837: /* this can't fail since we just created the file */ ! 838: outFilePtr = fopen(filename,FOPRBIN); ! 839: ! 840: #if defined(MSDOS) || defined(OS2) ! 841: setmode(fileno(stdout), O_BINARY); ! 842: #endif /* MSDOS || OS2 */ ! 843: while ((bytesRead = fread (buffer, 1, 512, outFilePtr)) > 0) ! 844: if (fwrite (buffer, 1, bytesRead, stdout) != bytesRead) { ! 845: status = -1; ! 846: break; ! 847: } ! 848: fclose (outFilePtr); ! 849: fflush(stdout); ! 850: if (ferror(stdout)) { ! 851: status = -1; ! 852: fprintf(pgpout, LANG("\007Write error on stdout.\n")); ! 853: } ! 854: #if defined(MSDOS) || defined(OS2) ! 855: setmode(fileno(stdout), O_TEXT); ! 856: #endif /* MSDOS || OS2 */ ! 857: ! 858: return status; ! 859: } ! 860: ! 861: /* Return the size from the current position of file f to the end */ ! 862: word32 fsize (FILE *f) ! 863: { ! 864: long fpos = ftell (f); ! 865: long fpos2; ! 866: ! 867: fseek (f, 0L, SEEK_END); ! 868: fpos2 = ftell (f); ! 869: fseek (f, fpos, SEEK_SET); ! 870: return (word32)(fpos2 - fpos); ! 871: } ! 872: ! 873: /* Return TRUE if file filename looks like a pure text file */ ! 874: int is_text_file (char *filename) ! 875: { ! 876: FILE *f = fopen(filename,"r"); /* FOPRBIN gives problem with VMS */ ! 877: int i, n, bit8 = 0; ! 878: unsigned char buf[512]; ! 879: unsigned char *bufptr = buf; ! 880: unsigned char c; ! 881: ! 882: if (!f) ! 883: return FALSE; /* error opening it, so not a text file */ ! 884: i = n = fread (buf, 1, sizeof(buf), f); ! 885: fclose(f); ! 886: if (n <= 0) ! 887: return FALSE; /* empty file or error, not a text file */ ! 888: if (compressSignature(buf) >= 0) ! 889: return FALSE; ! 890: while (i--) { ! 891: c = *bufptr++; ! 892: if (c & 0x80) ! 893: ++bit8; ! 894: else /* allow BEL BS HT LF VT FF CR EOF control characters */ ! 895: if (c < '\007' || (c > '\r' && c < ' ' && c != '\032')) ! 896: return FALSE; /* not a text file */ ! 897: } ! 898: if (strcmp(language, "ru") == 0) ! 899: return TRUE; ! 900: /* assume binary if more than 1/4 bytes have 8th bit set */ ! 901: return bit8 < n/4; ! 902: } /* is_text_file */ ! 903: ! 904: VOID *xmalloc(unsigned size) ! 905: { VOID *p; ! 906: if (size == 0) ! 907: ++size; ! 908: p = malloc(size); ! 909: if (p == NULL) { ! 910: fprintf(stderr, LANG("\n\007Out of memory.\n")); ! 911: exitPGP(1); ! 912: } ! 913: return p; ! 914: } ! 915: ! 916: /*---------------------------------------------------------------------- ! 917: * temporary file routines ! 918: */ ! 919: ! 920: ! 921: #define MAXTMPF 8 ! 922: ! 923: #define TMP_INUSE 2 ! 924: ! 925: static struct ! 926: { char path[MAX_PATH]; ! 927: int flags; ! 928: int num; ! 929: } tmpf[MAXTMPF]; ! 930: ! 931: static char tmpdir[256]; /* temporary file directory */ ! 932: static char outdir[256]; /* output directory */ ! 933: static char tmpbasename[64] = "pgptemp"; /* basename for temporary files */ ! 934: ! 935: ! 936: /* ! 937: * set directory for temporary files. path will be stored in ! 938: * tmpdir[] with an appropriate trailing path separator. ! 939: */ ! 940: void settmpdir(char *path) ! 941: { ! 942: char *p; ! 943: ! 944: if (path == NULL || *path == '\0') { ! 945: tmpdir[0] = '\0'; ! 946: return; ! 947: } ! 948: strcpy(tmpdir, path); ! 949: p = tmpdir + strlen(tmpdir)-1; ! 950: if (*p != '/' && *p != '\\' && *p != ']' && *p != ':') { ! 951: /* append path separator, either / or \ */ ! 952: if ((p = strchr(tmpdir, '/')) == NULL && ! 953: (p = strchr(tmpdir, '\\')) == NULL) ! 954: p = "/"; /* path did not contain / or \, use / */ ! 955: strncat(tmpdir, p, 1); ! 956: } ! 957: } ! 958: ! 959: /* ! 960: * set output directory to avoid a file copy when temp file is renamed to ! 961: * output file. the argument filename must be a valid path for a file, not ! 962: * a directory. ! 963: */ ! 964: void setoutdir(char *filename) ! 965: { ! 966: char *p; ! 967: ! 968: if (filename == NULL) { ! 969: strcpy(outdir, tmpdir); ! 970: return; ! 971: } ! 972: strcpy(outdir, filename); ! 973: p = file_tail(outdir); ! 974: strcpy(tmpbasename, p); ! 975: *p = '\0'; ! 976: drop_extension(tmpbasename); ! 977: #if !defined(BSD42) && !defined(BSD43) && !defined(sun) ! 978: /* ! 979: * we don't depend on pathconf here, if it returns an incorrect value ! 980: * for NAME_MAX (like Linux 0.97 with minix FS) finding a unique name ! 981: * for temp files can fail. ! 982: */ ! 983: tmpbasename[10] = '\0'; /* 14 char limit */ ! 984: #endif ! 985: } ! 986: ! 987: /* ! 988: * return a unique temporary file name ! 989: */ ! 990: char *tempfile(int flags) ! 991: { ! 992: int i, j; ! 993: int num; ! 994: int fd; ! 995: #ifndef UNIX ! 996: FILE *fp; ! 997: #endif ! 998: ! 999: for (i = 0; i < MAXTMPF; ++i) ! 1000: if (tmpf[i].flags == 0) ! 1001: break; ! 1002: ! 1003: if (i == MAXTMPF) { ! 1004: /* message only for debugging, no need for LANG */ ! 1005: fprintf(stderr, "\n\007Out of temporary files\n"); ! 1006: return NULL; ! 1007: } ! 1008: ! 1009: again: ! 1010: num = 0; ! 1011: do { ! 1012: for (j = 0; j < MAXTMPF; ++j) ! 1013: if (tmpf[j].flags && tmpf[j].num == num) ! 1014: break; ! 1015: if (j < MAXTMPF) ! 1016: continue; /* sequence number already in use */ ! 1017: sprintf(tmpf[i].path, "%s%s.%c%02d", ! 1018: ((flags & TMP_TMPDIR) && *tmpdir ? tmpdir : outdir), ! 1019: tmpbasename, TMP_EXT, num); ! 1020: if (!file_exists(tmpf[i].path)) ! 1021: break; ! 1022: } ! 1023: while (++num < 100); ! 1024: ! 1025: if (num == 100) { ! 1026: fprintf(pgpout, "\n\007tempfile: cannot find unique name\n"); ! 1027: return NULL; ! 1028: } ! 1029: ! 1030: #if defined(UNIX) || defined(VMS) ! 1031: if ((fd = open(tmpf[i].path, O_EXCL|O_RDWR|O_CREAT, 0600)) != -1) ! 1032: close(fd); ! 1033: #else ! 1034: if ((fp = fopen(tmpf[i].path, "w")) != NULL) ! 1035: fclose(fp); ! 1036: fd = (fp == NULL ? -1 : 0); ! 1037: #endif ! 1038: ! 1039: if (fd == -1) { ! 1040: if (!(flags & TMP_TMPDIR)) { ! 1041: flags |= TMP_TMPDIR; ! 1042: goto again; ! 1043: } ! 1044: #ifdef UNIX ! 1045: else if (tmpdir[0] == '\0') { ! 1046: strcpy(tmpdir, "/tmp/"); ! 1047: goto again; ! 1048: } ! 1049: #endif ! 1050: } ! 1051: if (fd == -1) ! 1052: { fprintf(pgpout, LANG("\n\007Cannot create temporary file '%s'\n"), tmpf[i].path); ! 1053: user_error(); ! 1054: } ! 1055: #ifdef VMS ! 1056: remove(tmpf[i].path); ! 1057: #endif ! 1058: ! 1059: tmpf[i].num = num; ! 1060: tmpf[i].flags = flags | TMP_INUSE; ! 1061: if (verbose) ! 1062: fprintf(pgpout, "tempfile: created '%s'\n", tmpf[i].path); ! 1063: return tmpf[i].path; ! 1064: } /* tempfile */ ! 1065: ! 1066: /* ! 1067: * remove temporary file, wipe if necessary. ! 1068: */ ! 1069: void rmtemp(char *name) ! 1070: { ! 1071: int i; ! 1072: ! 1073: for (i = 0; i < MAXTMPF; ++i) ! 1074: if (tmpf[i].flags && strcmp(tmpf[i].path, name) == 0) ! 1075: break; ! 1076: ! 1077: if (i < MAXTMPF) { ! 1078: if (strlen(name) > 3 && name[strlen(name)-3] == TMP_EXT) { ! 1079: /* only remove file if name hasn't changed */ ! 1080: if (verbose) ! 1081: fprintf(pgpout, "rmtemp: removing '%s'\n", name); ! 1082: if (tmpf[i].flags & TMP_WIPE) ! 1083: wipefile(name); ! 1084: if (!remove(name)) { ! 1085: tmpf[i].flags = 0; ! 1086: } else if (verbose) { ! 1087: fprintf(stderr,"\nrmtemp: Failed to remove %s",name); ! 1088: perror ("\nError"); ! 1089: } ! 1090: } else if (verbose) ! 1091: fprintf(pgpout, "rmtemp: not removing '%s'\n", name); ! 1092: } ! 1093: } /* rmtemp */ ! 1094: ! 1095: /* ! 1096: * make temporary file permanent, returns the new name. ! 1097: */ ! 1098: char *savetemp(char *name, char *newname) ! 1099: { ! 1100: int i, overwrite; ! 1101: static char buf[MAX_PATH]; ! 1102: ! 1103: if (strcmp(name, newname) == 0) ! 1104: return name; ! 1105: ! 1106: for (i = 0; i < MAXTMPF; ++i) ! 1107: if (tmpf[i].flags && strcmp(tmpf[i].path, name) == 0) ! 1108: break; ! 1109: ! 1110: if (i < MAXTMPF) { ! 1111: if (strlen(name) < 4 || name[strlen(name)-3] != TMP_EXT) { ! 1112: if (verbose) ! 1113: fprintf(pgpout, "savetemp: not renaming '%s' to '%s'\n", ! 1114: name, newname); ! 1115: return name; /* return original file name */ ! 1116: } ! 1117: } ! 1118: ! 1119: while (file_exists(newname)) { ! 1120: if (batchmode && !force_flag) { ! 1121: fprintf(pgpout,LANG("\n\007Output file '%s' already exists.\n"),newname); ! 1122: return NULL; ! 1123: } ! 1124: if (is_regular_file(newname)) { ! 1125: if (force_flag) { ! 1126: /* remove without asking */ ! 1127: remove(newname); ! 1128: break; ! 1129: } ! 1130: fprintf(pgpout,LANG("\n\007Output file '%s' already exists. Overwrite (y/N)? "), ! 1131: newname); ! 1132: overwrite = getyesno('n'); ! 1133: } else { ! 1134: fprintf(pgpout,LANG("\n\007Output file '%s' already exists.\n"),newname); ! 1135: if (force_flag) /* never remove special file */ ! 1136: return NULL; ! 1137: overwrite = FALSE; ! 1138: } ! 1139: ! 1140: if (!overwrite) { ! 1141: fprintf(pgpout, LANG("\nEnter new file name: ")); ! 1142: getstring(buf, MAX_PATH - 1, TRUE); ! 1143: if (buf[0] == '\0') ! 1144: return NULL; ! 1145: newname = buf; ! 1146: } else { ! 1147: remove(newname); ! 1148: } ! 1149: } ! 1150: if (verbose) ! 1151: fprintf(pgpout, "savetemp: renaming '%s' to '%s'\n", name, newname); ! 1152: if (rename2(name, newname) < 0) { ! 1153: /* errorLvl = UNKNOWN_FILE_ERROR; */ ! 1154: fprintf(pgpout, LANG("Can't create output file '%s'\n"), newname); ! 1155: return NULL; ! 1156: } ! 1157: if (i < MAXTMPF) ! 1158: tmpf[i].flags = 0; ! 1159: return newname; ! 1160: } /* savetemp */ ! 1161: ! 1162: /* ! 1163: * like savetemp(), only make backup of destname if it exists ! 1164: */ ! 1165: int savetempbak(char *tmpname, char *destname) ! 1166: { ! 1167: char bakpath[MAX_PATH]; ! 1168: #ifdef UNIX ! 1169: int mode = -1; ! 1170: #endif ! 1171: ! 1172: if (is_tempfile(destname)) { ! 1173: remove(destname); ! 1174: } else { ! 1175: if (file_exists(destname)) { ! 1176: #ifdef UNIX ! 1177: struct stat st; ! 1178: if (stat(destname, &st) != -1) ! 1179: mode = st.st_mode & 07777; ! 1180: #endif ! 1181: strcpy(bakpath, destname); ! 1182: force_extension(bakpath, BAK_EXTENSION); ! 1183: remove(bakpath); ! 1184: #ifdef VMS ! 1185: if (rename(destname, bakpath) != 0) ! 1186: #else ! 1187: if (rename(destname, bakpath) == -1) ! 1188: #endif ! 1189: return -1; ! 1190: } ! 1191: } ! 1192: if (savetemp(tmpname, destname) == NULL) ! 1193: return -1; ! 1194: #ifdef UNIX ! 1195: if (mode != -1) ! 1196: chmod(destname, mode); ! 1197: #endif ! 1198: return 0; ! 1199: } ! 1200: ! 1201: /* ! 1202: * remove all temporary files and wipe them if necessary ! 1203: */ ! 1204: void cleanup_tmpf(void) ! 1205: { ! 1206: int i; ! 1207: ! 1208: for (i = 0; i < MAXTMPF; ++i) ! 1209: if (tmpf[i].flags) ! 1210: rmtemp(tmpf[i].path); ! 1211: } /* cleanup_tmpf */ ! 1212: ! 1213: /* ! 1214: * Routines to search for the manuals. ! 1215: * ! 1216: * Why all this code? ! 1217: * ! 1218: * Some dimwits have been distributing versions of PGP (especially on MS-DOS) ! 1219: * without the manuals. This frustrates users (who call Philip Zimmermann ! 1220: * at all hours of the day and night), and in addition to depriving them ! 1221: * of instructions on how to operate the program, also deprives them of ! 1222: * IMPORTANT legal notices. This is a Bad Thing, so we've gone to the ! 1223: * trouble of being fascist and *forcing* the manuals to be there. ! 1224: */ ! 1225: ! 1226: static unsigned ! 1227: ext_missing(char *prefix) ! 1228: { ! 1229: static char const * const extensions[] = { ! 1230: #ifdef VMS ! 1231: ".doc", ".txt", ".man", ".tex", ".", 0}; ! 1232: #else ! 1233: ".doc", ".txt", ".man", ".tex", "", 0}; ! 1234: #endif ! 1235: char const * const *p; ! 1236: char *end = prefix + strlen(prefix); ! 1237: ! 1238: for (p = extensions; *p; p++) { ! 1239: strcpy(end, *p); ! 1240: #if 0 /* Debugging code */ ! 1241: fprintf(pgpout, "Looking for \"%s\"\n", prefix); ! 1242: #endif ! 1243: if (file_exists(prefix)) ! 1244: return 0; ! 1245: } ! 1246: return 1; ! 1247: } ! 1248: ! 1249: /* ! 1250: * Returns mask of files missing ! 1251: */ ! 1252: static unsigned ! 1253: files_missing(char *prefix) ! 1254: { ! 1255: static char const * const names[] = {"pgpdoc1", "pgpdoc2", 0}; ! 1256: char const * const *p; ! 1257: unsigned bit, mask = 3; ! 1258: int len = strlen(prefix); ! 1259: ! 1260: if (prefix[0] && !file_exists(prefix)) /* Directory doesn't exist? */ ! 1261: return mask; ! 1262: if (len && strchr(DIRSEPS, prefix[len-1]) == 0) ! 1263: prefix[len++] = DIRSEPS[0]; ! 1264: for (p = names, bit = 1; *p; p++, bit <<= 1) { ! 1265: strcpy(prefix+len, *p); ! 1266: if (!ext_missing(prefix)) ! 1267: mask &= ~bit; ! 1268: } ! 1269: ! 1270: return mask; /* Bitmask of which files exist */ ! 1271: } ! 1272: ! 1273: /* ! 1274: * Search prefix directory and doc subdirectory. ! 1275: */ ! 1276: static unsigned ! 1277: doc_missing(char *prefix) ! 1278: { ! 1279: unsigned mask; ! 1280: int len = strlen(prefix); ! 1281: ! 1282: mask = files_missing(prefix); ! 1283: if (!mask) ! 1284: return 0; ! 1285: #ifdef VMS ! 1286: if (len && prefix[len-1] == ']') { ! 1287: strcpy(prefix+len-1, ".doc]"); ! 1288: } else { ! 1289: assert(!len || prefix[len-1] == ':'); ! 1290: strcpy(prefix+len, "[doc]"); ! 1291: } ! 1292: #else ! 1293: if (len && prefix[len-1] != DIRSEPS[0]) ! 1294: prefix[len++] = DIRSEPS[0]; ! 1295: strcpy(prefix+len, "doc"); ! 1296: #endif ! 1297: ! 1298: mask &= files_missing(prefix); ! 1299: ! 1300: prefix[len] = '\0'; ! 1301: return mask; ! 1302: } ! 1303: ! 1304: /* ! 1305: * Expands a leading environment variable. Returns 0 on success; ! 1306: * <0 if there is an error. ! 1307: */ ! 1308: static int ! 1309: expand_env(char const *src, char *dest) ! 1310: { ! 1311: char const *var, *suffix; ! 1312: unsigned len; ! 1313: ! 1314: if (*src != '$') { ! 1315: strcpy(dest, src); ! 1316: return 0; ! 1317: } ! 1318: ! 1319: /* Find end of variable */ ! 1320: if (src[1] == '{') { /* ${FOO} form */ ! 1321: var = src+2; ! 1322: len = strchr(var, '}') - var; ! 1323: suffix = src+2+len+1; ! 1324: } else { /* $FOO form - allow $ for VMS */ ! 1325: var = src+1; ! 1326: len = strspn(var, "ABCDEFGHIJKLMNOPQRSTUVWXYZ$_"); ! 1327: suffix = src+1+len; ! 1328: } ! 1329: ! 1330: memcpy(dest, var, len); /* Copy name */ ! 1331: dest[len] = '\0'; /* Null-terminate */ ! 1332: ! 1333: var = getenv(dest); ! 1334: if (!var || !*var) ! 1335: return -1; /* No env variable */ ! 1336: ! 1337: /* Copy expanded form to destination */ ! 1338: strcpy(dest, var); ! 1339: ! 1340: /* Add tail */ ! 1341: strcat(dest, suffix); ! 1342: ! 1343: return 0; ! 1344: } ! 1345: ! 1346: /* Don't forget to change 'pgp25' whenever you update rel_version past 2.5 */ ! 1347: char const * const manual_dirs[] = { ! 1348: #if defined(VMS) ! 1349: "$PGPPATH", "", "[pgp]", "[pgp25]", ! 1350: PGP_SYSTEM_DIR, "SYS$LOGIN:", "SYS$LOGIN:[pgp]", ! 1351: "SYS$LOGIN:[pgp25]", "[-]", ! 1352: #elif defined(UNIX) ! 1353: "$PGPPATH", "", "pgp", "pgp25", PGP_SYSTEM_DIR, ! 1354: "$HOME/.pgp", "$HOME", "$HOME/pgp", "$HOME/pgp25", "..", ! 1355: #elif defined(AMIGA) ! 1356: "$PGPPATH", "", "pgp", "pgp25", ":pgp", ":pgp25", ":", "/", ! 1357: #else /* MSDOS or ATARI */ ! 1358: "$PGPPATH", "", "pgp", "pgp25", "\\pgp", "\\pgp25", "\\", "..", ! 1359: "c:\\pgp", "c:\\pgp25", ! 1360: #endif ! 1361: 0 }; ! 1362: ! 1363: unsigned ! 1364: manuals_missing(void) ! 1365: { ! 1366: char buf[256]; ! 1367: unsigned mask = ~0u; ! 1368: char const * const *p; ! 1369: ! 1370: for (p = manual_dirs; *p; p++) { ! 1371: if (expand_env(*p, buf) < 0) ! 1372: continue; /* Ignore */ ! 1373: mask &= doc_missing(buf); ! 1374: if (!mask) ! 1375: break; ! 1376: } ! 1377: ! 1378: return mask; ! 1379: } ! 1380: ! 1381: /* ! 1382: * Why all this code? ! 1383: * ! 1384: * Some dimwits have been distributing versions of PGP (especially on MS-DOS) ! 1385: * without the manuals. This frustrates users (who call Philip Zimmermann ! 1386: * at all hours of the day and night), and in addition to depriving them ! 1387: * of instructions on how to operate the program, also deprives them of ! 1388: * IMPORTANT legal notices. This is a Bad Thing, so we've gone to the ! 1389: * trouble of being fascist and *forcing* the manuals to be there. ! 1390: */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.