1 /*      armor.c  - ASCII/binary encoding/decoding based partly on
2    PEM RFC1113 and MIME standards.
3    PGP: Pretty Good(tm) Privacy - public key cryptography for the masses.
4 
5    (c) Copyright 1990-1996 by Philip Zimmermann.  All rights reserved.
6    The author assumes no liability for damages resulting from the use
7    of this software, even if the damage results from defects in this
8    software.  No warranty is expressed or implied.
9 
10    Note that while most PGP source modules bear Philip Zimmermann's
11    copyright notice, many of them have been revised or entirely written
12    by contributors who frequently failed to put their names in their
13    code.  Code that has been incorporated into PGP from other authors
14    was either originally published in the public domain or is used with
15    permission from the various authors.
16 
17    PGP is available for free to the public under certain restrictions.
18    See the PGP User's Guide (included in the release package) for
19    important information about licensing, patent restrictions on
20    certain algorithms, trademarks, copyrights, and export controls.
21  */
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include "mpilib.h"
26 #include "fileio.h"
27 #include "mpiio.h"
28 #include "language.h"
29 #include "pgp.h"
30 #include "charset.h"
31 #include "crypto.h"
32 #include "armor.h"
33 #include "keymgmt.h"
34 #ifdef MACTC5
35 #include "Macutil2.h"
36 #include "Macutil3.h"
37 #endif
38 
39 static int darmor_file(char *infile, char *outfile);
40 static crcword crchware(byte ch, crcword poly, crcword accum);
41 static int armordecode(FILE * in, FILE * out, int *warned);
42 static void mk_crctbl(crcword poly);
43 static boolean is_armorfile(char *infile);
44 
45 /*      Begin ASCII armor routines.
46    This converts a binary file into printable ASCII characters, in a
47    radix-64 form mostly compatible with the MIME format.
48    This makes it easier to send encrypted files over a 7-bit channel.
49  */
50 
51 /* Index this array by a 6 bit value to get the character corresponding
52  * to that value.
53  */
54 static
55 unsigned char bintoasc[] =
56 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
57 
58 /* Index this array by a 7 bit value to get the 6-bit binary field
59  * corresponding to that value.  Any illegal characters return high bit set.
60  */
61 static
62 unsigned char asctobin[] =
63 {
64 #ifdef EBCDIC
65     128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,
66     128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,
67     128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,
68     128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,
69     128,128,128,128,128,128,128,128, 128,128,128,128,128,128, 62,128,
70     128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,
71     128, 63,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,
72     128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,
73     128, 26, 27, 28, 29, 30, 31, 32,  33, 34,128,128,128,128,128,128,
74     128, 35, 36, 37, 38, 39, 40, 41,  42, 43,128,128,128,128,128,128,
75     128,128, 44, 45, 46, 47, 48, 49,  50, 51,128,128,128,128,128,128,
76     128,128,128,128,128,128,128,128, 128,128,128,128,128,128,128,128,
77     128,  0,  1,  2,  3,  4,  5,  6,   7,  8,128,128,128,128,128,128,
78     128,  9, 10, 11, 12, 13, 14, 15,  16, 17,128,128,128,128,128,128,
79     128,128, 18, 19, 20, 21, 22, 23,  24, 25,128,128,128,128,128,128,
80      52, 53, 54, 55, 56, 57, 58, 59,  60, 61,128,128,128,128,128,128
81 #else
82     0200, 0200, 0200, 0200, 0200, 0200, 0200, 0200,
83     0200, 0200, 0200, 0200, 0200, 0200, 0200, 0200,
84     0200, 0200, 0200, 0200, 0200, 0200, 0200, 0200,
85     0200, 0200, 0200, 0200, 0200, 0200, 0200, 0200,
86     0200, 0200, 0200, 0200, 0200, 0200, 0200, 0200,
87     0200, 0200, 0200, 0076, 0200, 0200, 0200, 0077,
88     0064, 0065, 0066, 0067, 0070, 0071, 0072, 0073,
89     0074, 0075, 0200, 0200, 0200, 0200, 0200, 0200,
90     0200, 0000, 0001, 0002, 0003, 0004, 0005, 0006,
91     0007, 0010, 0011, 0012, 0013, 0014, 0015, 0016,
92     0017, 0020, 0021, 0022, 0023, 0024, 0025, 0026,
93     0027, 0030, 0031, 0200, 0200, 0200, 0200, 0200,
94     0200, 0032, 0033, 0034, 0035, 0036, 0037, 0040,
95     0041, 0042, 0043, 0044, 0045, 0046, 0047, 0050,
96     0051, 0052, 0053, 0054, 0055, 0056, 0057, 0060,
97     0061, 0062, 0063, 0200, 0200, 0200, 0200, 0200
98 #endif
99 };
100 
101 /* Current line number for mult decodes */
102 #ifdef MACTC5
103 long	infile_line;		/* Current line number for mult decodes */
104 #else
105 static long infile_line;
106 #endif
107 
108 /************************************************************************/
109 
110 /* CRC Routines. */
111 /*      These CRC functions are derived from code in chapter 19 of the book
112  *    "C Programmer's Guide to Serial Communications", by Joe Campbell.
113  *      Generalized to any CRC width by Philip Zimmermann.
114  */
115 
116 #define byte unsigned char
117 
118 #define CRCBITS 24		/* may be 16, 24, or 32 */
119 /* #define maskcrc(crc) ((crcword)(crc)) *//* if CRCBITS is 16 or 32 */
120 #define maskcrc(crc) ((crc) & 0xffffffL)	/* if CRCBITS is 24 */
121 #define CRCHIBIT ((crcword) (1L<<(CRCBITS-1)))	/* 0x8000 if CRCBITS is 16 */
122 #define CRCSHIFTS (CRCBITS-8)
123 
124 /*
125  * Notes on making a good 24-bit CRC--
126  * The primitive irreducible polynomial of degree 23 over GF(2),
127  * 040435651 (octal), comes from Appendix C of "Error Correcting Codes,
128  * 2nd edition" by Peterson and Weldon, page 490.  This polynomial was
129  * chosen for its uniform density of ones and zeros, which has better
130  * error detection properties than polynomials with a minimal number of
131  * nonzero terms.  Multiplying this primitive degree-23 polynomial by
132  * the polynomial x+1 yields the additional property of detecting any
133  * odd number of bits in error, which means it adds parity.  This
134  * approach was recommended by Neal Glover.
135  *
136  * To multiply the polynomial 040435651 by x+1, shift it left 1 bit and
137  * bitwise add (xor) the unshifted version back in.  Dropping the unused
138  * upper bit (bit 24) produces a CRC-24 generator bitmask of 041446373
139  * octal, or 0x864cfb hex.
140  *
141  * You can detect spurious leading zeros or framing errors in the
142  * message by initializing the CRC accumulator to some agreed-upon
143  * nonzero value, but the value used is a bit nonstandard.
144  */
145 
146 #define CCITTCRC 0x1021		/* CCITT's 16-bit CRC generator polynomial */
147 #define PRZCRC 0x864cfbL	/* PRZ's 24-bit CRC generator polynomial */
148 #define CRCINIT 0xB704CEL	/* Init value for CRC accumulator */
149 
150 static crcword crctable[256];	/* Table for speeding up CRC's */
151 
152 /*
153  * mk_crctbl derives a CRC lookup table from the CRC polynomial.
154  * The table is used later by the crcbytes function given below.
155  * mk_crctbl only needs to be called once at the dawn of time.
156  *
157  * The theory behind mk_crctbl is that table[i] is initialized
158  * with the CRC of i, and this is related to the CRC of i>>1,
159  * so the CRC of i>>1 (pointed to by p) can be used to derive
160  * the CRC of i (pointed to by q).
161  */
162 static void
163 mk_crctbl(crcword poly)
164 {
165     int i;
166     crcword t, *p, *q;
167     p = q = crctable;
168     *q++ = 0;
169     *q++ = poly;
170     for (i = 1; i < 128; i++) {
171 	t = *++p;
172 	if (t & CRCHIBIT) {
173 	    t <<= 1;
174 	    *q++ = t ^ poly;
175 	    *q++ = t;
176 	} else {
177 	    t <<= 1;
178 	    *q++ = t;
179 	    *q++ = t ^ poly;
180 	}
181     }
182 }
183 
184 /*
185  * Accumulate a buffer's worth of bytes into a CRC accumulator,
186  * returning the new CRC value.
187  */
188 crcword
189 crcbytes(byte * buf, unsigned len, register crcword accum)
190 {
191     do {
192 	accum = accum << 8 ^ crctable[(byte) (accum >> CRCSHIFTS) ^ *buf++];
193     } while (--len);
194     return maskcrc(accum);
195 }				/* crcbytes */
196 
197 /* Initialize the CRC table using our codes */
198 void
199 init_crc(void)
200 {
201     mk_crctbl(PRZCRC);
202 }
203 
204 
205 /************************************************************************/
206 
207 
208 /* ENC is the basic 1 character encoding function to make a char printing */
209 #define ENC(c) ((int)bintoasc[((c) & 077)])
210 #define PAD		'='
211 
212 /*
213  * output one group of up to 3 bytes, pointed at by p, on file f.
214  * if fewer than 3 are present, the 1 or two extras must be zeros.
215  */
216 static void
217 outdec(char *p, FILE *f, int count)
218 {
219     int c1, c2, c3, c4;
220 
221     c1 = *p >> 2;
222     c2 = ((*p << 4) & 060) | ((p[1] >> 4) & 017);
223     c3 = ((p[1] << 2) & 074) | ((p[2] >> 6) & 03);
224     c4 = p[2] & 077;
225     putc(ENC(c1), f);
226     putc(ENC(c2), f);
227     if (count == 1) {
228 	putc(PAD, f);
229 	putc(PAD, f);
230     } else {
231 	putc(ENC(c3), f);
232 	if (count == 2)
233 	    putc(PAD, f);
234 	else
235 	    putc(ENC(c4), f);
236     }
237 }				/* outdec */
238 
239 
240 /* Output the CRC value, MSB first per normal CRC conventions */
241 static void
242 outcrc(word32 crc, FILE *outFile)
243 {
244     char crcbuf[4];
245     crcbuf[0] = (crc >> 16) & 0xff;
246     crcbuf[1] = (crc >> 8) & 0xff;
247     crcbuf[2] = (crc >> 0) & 0xff;
248     putc(PAD, outFile);
249     outdec(crcbuf, outFile, 3);
250     putc('\n', outFile);
251 }				/* outcrc */
252 
253 /* Return filename for output (text mode), but replace last letter(s) of
254  * filename with the ascii for num.  It will use the appropriate number
255  * of digits for ofnum when converting num, so if ofnum < 10, use 1 digit,
256  * >= 10 and < 100 use 2 digits, >= 100 and < 1000 use 3 digits.  If its
257  * >= 1000, then we have other problems to worry about, and this might do
258  * weird things.
259  */
260 static char *
261 numFilename(char *fname, int num, int ofnum)
262 {
263     static char fnamenum[MAX_PATH];
264     int len;
265     int offset = 1;
266 
267     strcpy(fnamenum, fname);
268     len = strlen(fnamenum);
269     do {
270 	fnamenum[len - offset] = '0' + (num % 10);
271 	num /= 10;
272 	ofnum /= 10;
273 	offset++;
274     } while (ofnum >= 1 && offset < 4);
275     return fnamenum;
276 }
277 
278 /*
279  * Reads and discards a line from the given file.  Returns -1 on error or
280  * EOF, 0 if the line is blank, and 1 if the line is not blank.
281  */
282 static int
283 skipline(FILE * f)
284 {
285     int state, flag, c;
286 
287     state = 0;
288     flag = 0;
289     for (;;) {
290 	c = getc(f);
291 	if (c == '\n')
292 	    return flag;
293 	if (state) {
294 	    ungetc(c, f);
295 	    return flag;
296 	}
297 	if (c == EOF)
298 	    return -1;
299 	if (c == '\r')
300 	    state = 1;
301 	else if (c != ' ')
302 	    flag = 1;
303     }
304 }				/* skipline */
305 
306 
307 /*
308  * Copies a line from the input file to the output.  Does NOT copy the
309  * trailing newline.  Returns -1 on EOF or error, 0 if the line was terminated
310  * by EOF, and 1 if the line was terminated with a newline sequence.
311  */
312 static int
313 copyline(FILE * in, FILE * out)
314 {
315     int state, flag, c;
316 
317     state = 0;
318     for (;;) {
319 	c = getc(in);
320 	if (c == '\n')
321 	    return 1;
322 	if (state) {
323 	    ungetc(c, in);
324 	    return 1;
325 	}
326 	if (c == EOF)
327 	    return 0;
328 	if (c == '\r')
329 	    state = 1;
330 	else
331 	    putc(c, out);
332     }
333 }				/* copyline */
334 
335 /*
336  * Reads a line from file f, up to the size of the buffer.  The line in the
337  * buffer will NOT include line termination, although any of (CR, LF, CRLF)
338  * is accepted on input.  The return value is -1 on error, 0 if the line
339  * was terminated abnormally (EOF, error, or out of buffer space), and
340  * 1 if the line was terminated normally.
341  *
342  * Passing in a buffer less than 2 characters long is not a terribly bright
343  * idea.
344  */
345 static int
346 get_line(char *buf, int n, FILE * f)
347 {
348     int state;
349     char *p;
350     int c;
351 
352     state = 0;
353     p = buf;
354     for (;;) {
355 	c = getc(f);
356 	if (c == '\n') {
357 	    *p = 0;
358 	    return 1;		/* Line terminated with \n or \r\n */
359 	}
360 	if (state) {
361 	    ungetc(c, f);
362 	    *p = 0;
363 	    return 1;		/* Line terminated with \r */
364 	}
365 	if (c == EOF) {
366 	    *p = 0;
367 	    return (p == buf) ? -1 : 0;		/* Error */
368 	}
369 	if (c == '\r')
370 	    state = 1;
371 	else if (--n > 0) {
372 	    *p++ = c;
373 	} else {
374 	    ungetc(c, f);
375 	    *p = 0;
376 	    return 0;		/* Out of buffer space */
377 	}
378     }				/* for (;;) */
379 }				/* get_line */
380 
381 #if 1
382 /* This limit is advisory only; longer lines are handled properly.
383  * The only requirement is that this be at least as long as the longest
384  * delimiter string used by PGP
385  * (e.g. "-----BEGIN PGP MESSAGE, PART %02d/%02d-----\n")
386  */
387 #define MAX_LINE_SIZE 80
388 #else
389 #ifdef MSDOS			/* limited stack space */
390 #define MAX_LINE_SIZE	256
391 #else
392 #define MAX_LINE_SIZE	1024
393 #endif
394 #endif
395 
396 /*
397  * Read a line from file f, buf must be able to hold at least MAX_LINE_SIZE
398  * characters.  Anything after that is ignored.  Strips trailing spaces and
399  * line terminator, can read LF, CRLF and CR textfiles.  It can't be ASCII
400  * armor anyway.
401  */
402 static char *
403 get_armor_line(char *buf, FILE * f)
404 {
405     int c, n = MAX_LINE_SIZE-1;
406     char *p = buf;
407 
408     do {
409 	c = getc(f);
410 	if (c == '\n' || c == '\r' || c == EOF)
411 	    break;
412 	*p++ = c;
413     } while (--n > 0);
414     if (p == buf && c == EOF) {
415 	*buf = '\0';
416 	return NULL;
417     }
418     /*
419      * Skip to end of line, setting n to non-zero if anything trailing is
420      * not a space (meaning that any trailing whitespace in the buffer is
421      * not trailing whitespace on the line and should not be stripped).
422      */
423     n = 0;
424     while (c != '\n' && c != '\r' && c != EOF) {
425         n |= c ^ ' ';
426 	c = getc(f);
427     }
428     if (c == '\r' && (c = getc(f)) != '\n')
429 	ungetc(c, f);
430     if (!n) {	/* Skip trailing whitespace, as described above */
431 	while (p > buf && p[-1] == ' ')
432 	    --p;
433     }
434     *p = '\0';
435     return buf;
436 }
437 
438 
439 /*
440  * Encode a file in sections.  64 ASCII bytes * 720 lines = 46K,
441  * recommended max.  Usenet message size is 50K so this leaves a nice
442  * margin for .signature.  In the interests of orthogonality and
443  * programmer laziness no check is made for a message containing only
444  * a few lines (or even just an 'end')  after a section break.
445  */
446 #define LINE_LEN	48L
447 int pem_lines = 720;
448 #define BYTES_PER_SECTION	(LINE_LEN * pem_lines)
449 
450 #if defined(VMS) || defined(C370)
451 #define FOPRARMOR	FOPRTXT
452 #else
453 /* armored files are opened in binary mode so that CRLF/LF/CR files
454    can be handled by all systems */
455 #define	FOPRARMOR	FOPRBIN
456 #endif
457 
458 extern boolean verbose;		/* Undocumented command mode in PGP.C */
459 extern boolean filter_mode;
460 
461 /*
462  * Copy from infilename to outfilename, ASCII armoring as you go along,
463  * and with breaks every pem_lines lines.
464  * If clearfilename is non-NULL, first output that file preceded by a
465  * special delimiter line.  filename is the original filename, used
466  * only for debugging.
467  */
468 int
469 armor_file(char *infilename, char *outfilename, char *filename,
470 	char *clearfilename, boolean kv_label)
471 {
472     char buffer[MAX_LINE_SIZE];
473     int i, rc, bytesRead, lines = 0;
474     int noSections, currentSection = 1;
475     long fileLen;
476     crcword crc;
477     FILE *inFile, *outFile, *clearFile;
478     char *tempf;
479     char *blocktype = "MESSAGE";
480 #ifdef MACTC5
481     char curOutFile[256]="";
482 #endif
483 
484     if (verbose)
485 	fprintf(pgpout,
486 "armor_file: infile = %s, outfile = %s, filename = %s, clearname = %s\n",
487 		infilename, outfilename, filename,
488 		clearfilename == NULL ? "" : clearfilename);
489 
490     /* open input file as binary */
491     if ((inFile = fopen(infilename, FOPRBIN)) == NULL)
492 	return 1;
493 
494     if (!outfilename || pem_lines == 0) {
495 	noSections = 1;
496     } else {
497 	/* Evaluate how many parts this file will comprise */
498 	fseek(inFile, 0L, SEEK_END);
499 	fileLen = ftell(inFile);
500 	rewind(inFile);
501 	noSections = (fileLen + BYTES_PER_SECTION - 1) /
502 	    BYTES_PER_SECTION;
503 	if (noSections > 99) {
504 	    pem_lines = ((fileLen + LINE_LEN - 1) / LINE_LEN + 98) / 99;
505 	    noSections = (fileLen + BYTES_PER_SECTION - 1) /
506 		BYTES_PER_SECTION;
507 	    fprintf(pgpout,
508 	    "value for \"armorlines\" is too low, using %d\n", pem_lines);
509 	}
510     }
511 
512     if (clearfilename)
513       tempf = tempfile(TMP_WIPE);
514     else
515       tempf = outfilename;
516 
517     if (outfilename == NULL) {
518 	outFile = stdout;
519     } else {
520 	if (noSections > 1) {
521             do {
522                 char *t;
523                 force_extension(outfilename, ASC_EXTENSION);
524                 strcpy(outfilename, numFilename(outfilename, 1, noSections));
525                 if (!file_exists(outfilename)) break;
526                 t = ck_dup_output(outfilename, TRUE, TRUE);
527                 if (t==NULL) user_error();
528                 strcpy(outfilename,t);
529             } while (TRUE);
530             outFile = fopen(tempf, FOPWTXT);
531 	} else
532 	    outFile = fopen(tempf, FOPWTXT);
533 #ifdef MACTC5
534 		strcpy(curOutFile,outfilename);
535 #endif
536     }
537 
538     if (outFile == NULL) {
539 	fclose(inFile);
540 	return 1;
541     }
542     if (clearfilename) {
543 	if ((clearFile = fopen(clearfilename, FOPRTXT)) == NULL) {
544 	    fclose(inFile);
545 	    if (outFile != stdout)
546 		fclose(outFile);
547 	    return 1;
548 	}
549 	fprintf(outFile, "-----BEGIN PGP SIGNED MESSAGE-----\n\n");
550 	while ((i = get_line(buffer, sizeof buffer, clearFile)) >= 0) {
551 	    /* Quote lines beginning with '-' as per RFC1113;
552 	     * Also quote lines beginning with "From "; this is
553 	     * for Unix mailers which add ">" to such lines.
554 	     */
555 	    if (buffer[0] == '-' || strncmp(buffer, "From ", 5) == 0)
556 		fputs("- ", outFile);
557 	    fputs(buffer, outFile);
558 	    /* If there is more on this line, copy it */
559 	    if (i == 0)
560 		if (copyline(clearFile, outFile) <= 0)
561 		    break;
562 	    fputc('\n', outFile);
563 	}
564 	fclose(clearFile);
565 	putc('\n', outFile);
566 	blocktype = "SIGNATURE";
567     }
568     if (noSections == 1) {
569 	byte ctb = 0;
570         int keycounter = 0;
571         int status;
572 	ctb = getc(inFile);
573 	if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE)) {
574 	    blocktype = "PUBLIC KEY BLOCK";
575             if (kv_label) {
576                 kv_title(outFile);     /* Title line */
577                 rewind(inFile);        /* Back over CTB */
578                 status = kvformat_keypacket(inFile, outFile, TRUE, "", infilename,
579                                             FALSE, FALSE, &keycounter);
580 	        fprintf(outFile, "\n");
581             }
582 	} else if (is_ctb_type(ctb, CTB_CERT_SECKEY_TYPE)) {
583             blocktype = "SECRET KEY BLOCK";
584             if (kv_label) {
585                 kv_title(outFile);     /* Title line */
586                 rewind(inFile);        /* Back over CTB */
587                 status = kvformat_keypacket(inFile, outFile, TRUE, "", infilename,
588                                             FALSE, FALSE, &keycounter);
589 	        fprintf(outFile, "\n");
590             }
591 	}
592 	fprintf(outFile, "-----BEGIN PGP %s-----\n", blocktype);
593 	rewind(inFile);
594     } else {
595 	fprintf(outFile,
596 		"-----BEGIN PGP MESSAGE, PART %02d/%02d-----\n",
597 		1, noSections);
598     }
599     fprintf(outFile, "Version: %s\n", LANG(rel_version));
600     if (clearfilename)
601 	fprintf(outFile, "Charset: %s\n", charset);
602     if (globalCommentString[0])
603 	fprintf(outFile, "Comment: %s\n", globalCommentString);
604     fprintf(outFile, "\n");
605 
606     init_crc();
607     crc = CRCINIT;
608 
609     while ((bytesRead = fread(buffer, 1, LINE_LEN, inFile)) > 0) {
610 	/* Munge up LINE_LEN characters */
611 	if (bytesRead < LINE_LEN)
612 	    fill0(buffer + bytesRead, LINE_LEN - bytesRead);
613 
614 	crc = crcbytes((byte *) buffer, bytesRead, crc);
615 	for (i = 0; i < bytesRead - 3; i += 3)
616 	    outdec(buffer + i, outFile, 3);
617 	outdec(buffer + i, outFile, bytesRead - i);
618 	putc('\n', outFile);
619 #ifdef MACTC5
620 		mac_poll_for_break();
621 #endif
622 
623 	if (++lines == pem_lines && currentSection < noSections) {
624 	    lines = 0;
625 	    outcrc(crc, outFile);
626 	    fprintf(outFile,
627 		    "-----END PGP MESSAGE, PART %02d/%02d-----\n\n",
628 		    currentSection, noSections);
629 	    if (write_error(outFile)) {
630 		fclose(outFile);
631 		return -1;
632  	    }
633 	    fclose(outFile);
634 #ifdef MACTC5
635 		PGPSetFinfo(curOutFile,'TEXT','MPGP');
636 #endif
637 	    outFile = fopen(numFilename(outfilename,
638 					++currentSection,
639 					noSections), FOPWTXT);
640 #ifdef MACTC5
641 		strcpy(curOutFile,numFilename (outfilename,currentSection,noSections));
642 #endif
643 	    if (outFile == NULL) {
644 		fclose(inFile);
645 		return -1;
646 	    }
647 	    fprintf(outFile,
648 		    "-----BEGIN PGP MESSAGE, PART %02d/%02d-----\n",
649 		    currentSection, noSections);
650 	    fprintf(outFile, "\n");
651 	    crc = CRCINIT;
652 	}
653     }
654     outcrc(crc, outFile);
655 
656     if (noSections == 1)
657 	fprintf(outFile, "-----END PGP %s-----\n", blocktype);
658     else
659 	fprintf(outFile, "-----END PGP MESSAGE, PART %02d/%02d-----\n",
660 		noSections, noSections);
661 
662     /* Done */
663     fclose(inFile);
664     rc = write_error(outFile);
665     if (outFile == stdout)
666 	return rc;
667 #ifdef MACTC5
668 	PGPSetFinfo(curOutFile,'TEXT','MPGP');
669 #endif
670     fclose(outFile);
671     if (clearfilename) {
672         remove(outfilename);
673         savetemp(tempf,outfilename);
674     }
675 
676     if (rc)
677 	return -1;
678 
679     if (clearfilename) {
680 	fprintf(pgpout,
681 		LANG("\nClear signature file: %s\n"), outfilename);
682     } else if (noSections == 1) {
683 	fprintf(pgpout,
684 		LANG("\nTransport armor file: %s\n"), outfilename);
685     } else {
686 	fprintf(pgpout, LANG("\nTransport armor files: "));
687 	for (i = 1; i <= noSections; ++i)
688 	    fprintf(pgpout, "%s%s",
689 		    numFilename(outfilename, i, noSections),
690 		    i == noSections ? "\n" : ", ");
691     }
692     return 0;
693 }				/* armor_file */
694 
695 /*      End ASCII armor encode routines. */
696 
697 
698 /*
699  * ASCII armor decode routines.
700  */
701 static int
702 darmor_buffer(char *inbuf, char *outbuf, int *outlength)
703 {
704     unsigned char *bp;
705     int length;
706     unsigned int c1, c2, c3, c4;
707     register int j;
708 
709     length = 0;
710     bp = (unsigned char *) inbuf;
711 
712     /* FOUR input characters go into each THREE output charcters */
713 
714     while (*bp != '\0') {
715 #ifdef EBCDIC
716 	if ((c1 = asctobin[*bp]) & 0x80)
717 	    return -1;
718 	++bp;
719 	if ((c2 = asctobin[*bp]) & 0x80)
720 	    return -1;
721 #else
722 	if (*bp & 0x80 || (c1 = asctobin[*bp]) & 0x80)
723 	    return -1;
724 	++bp;
725 	if (*bp & 0x80 || (c2 = asctobin[*bp]) & 0x80)
726 	    return -1;
727 #endif
728 	if (*++bp == PAD) {
729 	    c3 = c4 = 0;
730 	    length += 1;
731 	    if (c2 & 15)
732 		return -1;
733 	    if (strcmp((char *) bp, "==") == 0)
734 		bp += 1;
735 	    else if (strcmp((char *) bp, "=3D=3D") == 0)
736 		bp += 5;
737 	    else
738 		return -1;
739 #ifdef EBCDIC
740 	} else if ((c3 = asctobin[*bp]) & 0x80) {
741 #else
742 	} else if (*bp & 0x80 || (c3 = asctobin[*bp]) & 0x80) {
743 #endif
744 	    return -1;
745 	} else {
746 	    if (*++bp == PAD) {
747 		c4 = 0;
748 		length += 2;
749 		if (c3 & 3)
750 		    return -1;
751 		if (strcmp((char *) bp, "=") == 0);	/* All is well */
752 		else if (strcmp((char *) bp, "=3D") == 0)
753 		    bp += 2;
754 		else
755 		    return -1;
756 #ifdef EBCDIC
757 	    } else if ((c4 = asctobin[*bp]) & 0x80) {
758 #else
759 	    } else if (*bp & 0x80 || (c4 = asctobin[*bp]) & 0x80) {
760 #endif
761 		return -1;
762 	    } else {
763 		length += 3;
764 	    }
765 	}
766 	++bp;
767 	j = (c1 << 2) | (c2 >> 4);
768 	*outbuf++ = j;
769 	j = (c2 << 4) | (c3 >> 2);
770 	*outbuf++ = j;
771 	j = (c3 << 6) | c4;
772 	*outbuf++ = j;
773     }
774 
775     *outlength = length;
776     return 0;			/* normal return */
777 
778 }				/* darmor_buffer */
779 
780 static char armorfilename[MAX_PATH];
781 /*
782  * try to open the next file of a multi-part armored file
783  * the sequence number is expected at the end of the file name
784  */
785 static FILE *
786 open_next(void)
787 {
788     char *p, *s, c;
789     FILE *fp;
790 
791     p = armorfilename + strlen(armorfilename);
792     while (--p >= armorfilename && isdigit(*p)) {
793 	if (*p != '9') {
794 	    ++*p;
795 	    return fopen(armorfilename, FOPRARMOR);
796 	}
797 	*p = '0';
798     }
799 
800     /* need an extra digit */
801     if (p >= armorfilename) {
802 	/* try replacing character ( .as0 -> .a10 ) */
803 	c = *p;
804 	*p = '1';
805 	if ((fp = fopen(armorfilename, FOPRARMOR)) != NULL)
806 	    return fp;
807 	*p = c;			/* restore original character */
808     }
809     ++p;
810     for (s = p + strlen(p); s >= p; --s)
811 	s[1] = *s;
812     *p = '1';			/* insert digit ( fn0 -> fn10 ) */
813 
814 #if defined(MSDOS) && !defined(BUG)
815     /* if the resulting filename has more than three
816        characters after the first dot, don't even try to open it */
817     s = strchr(armorfilename, '.');
818     if (s != NULL)
819        if (strlen(s) > 3)
820           return NULL;
821 #endif /* MSDOS */
822 
823     return fopen(armorfilename, FOPRARMOR);
824 }
825 
826 /*
827  * Returns -1 if the line given is does not begin as a valid ASCII
828  * armor header line (something of the form "Label: ", where "Label"
829  * must begin with a letter followed by letters, numbers, or hyphens,
830  * followed immediately by a colon and a space), 0 if it is a familiar
831  * label, and the length of the label if it is an unfamiliar label
832  * (E.g. not "Version" or "Comment");
833  */
834 static int
835 isheaderline(char const *buf)
836 {
837 	int i;
838 
839 	if (!isalpha(*buf))
840 		return -1;	/* Not a label */
841 
842 	for (i = 1; isalnum(buf[i]) || i == '-'; i++)
843 		;
844 	if (buf[i] != ':' || buf[i+1] != ' ')
845 		return -1;	/* Not a label */
846 
847 	if (memcmp(buf, "Charset", i) == 0) {
848 		if (use_charset_header) strcpy(charset_header,buf+9);
849 		return 0;
850 	}
851 	if (memcmp(buf, "Version", i) == 0 ||
852 	    memcmp(buf, "Comment", i) == 0)
853 		return 0;	/* Familiar label */
854 	return i;	/* Unfamiliar label */
855 }
856 
857 /*
858  * Skips a bunch of headers, either returning 0, or printing
859  * an error message and returning -1.
860  * If it encounters an unfamiliar label and *warned is not set,
861  * prints a warning and sets *warned.
862  * NOTE that file read errors are NOT printed or reported in the
863  * return code.  It is assumed that the following read will
864  * notice the error and do something appropriate.
865  */
866 static int
867 skipheaders(FILE *in, char *buf, int *warned, int armorfollows)
868 {
869     int label_seen = 0;
870     int i;
871 #ifndef STRICT_ARMOR	/* Allow no space */
872     long fpos;
873     char outbuf[(MAX_LINE_SIZE*3+3)/4];
874     int n;
875 #endif
876 
877     for (;;) {
878 	++infile_line;
879 #ifndef STRICT_ARMOR
880 	fpos = ftell(in);
881 #endif
882 	if (get_armor_line(buf, in) == NULL)	/* Error */
883 	    return 0;	/* See comment above */
884 	if (buf[0] == '\0')	/* Blank line */
885 	    return 0;	/* Success */
886 	if (label_seen && (buf[0] == ' ' || buf[0] == '\t'))
887 	    continue;	/* RFC-822-style continuation line */
888 	i = isheaderline(buf);
889 	if (i < 0) {	/* Not a legal header line */
890 #ifndef STRICT_ARMOR	/* If it's as ASCII armor line, accept it */
891 	    if (armorfollows && darmor_buffer(buf, outbuf, &n) == 0 && n == 48)
892 	    {
893 		fseek(in, fpos, SEEK_SET);
894 		--infile_line;
895 		return 0;	/* Consider this acceptable */
896 	    }
897 #else
898 	    (void)armorfollows;	/* Stop compiler complaints */
899 #endif
900 	    fprintf(pgpout,
901 LANG("Invalid ASCII armor header line: \"%.40s\"\n\
902 ASCII armor corrupted.\n"), buf);
903 	    return -1;
904 	}
905 	if (i > 0 && !*warned) {
906 		fprintf(pgpout,
907 LANG("Warning: Unrecognized ASCII armor header label \"%.*s:\" ignored.\n"),
908 		        i, buf);
909 		*warned = 1;
910 	}
911 	label_seen = 1;	/* Continuation lines are now legal */
912     }
913 }
914 
915 /*
916  * Copy from in to out, decoding as you go, with handling for multiple
917  * 500-line blocks of encoded data.  This function also knows how to
918  * go past the end of one part to the beginning of the next in a multi-part
919  * file.  (As you can see from some ugliness below, this is not the best
920  * place to do it, since the caller is responsible for closing the
921  * "original_in" file.)
922  */
923 static int
924 armordecode(FILE *original_in, FILE *out, int *warned)
925 {
926     char inbuf[MAX_LINE_SIZE];
927     char outbuf[MAX_LINE_SIZE];
928 
929     int i, n, status;
930     int line;
931     int section, currentSection = 1;
932     int noSections = 0;
933     int gotcrc = 0;
934     long crc = CRCINIT, chkcrc = -1;
935     char crcbuf[4];
936     int ret_code = 0;
937     int end_of_message;
938     FILE *in = original_in;
939 
940     init_crc();
941 
942     for (line = 1;; line++) {	/* for each input line */
943 	if (get_armor_line(inbuf, in) == NULL) {
944 	    end_of_message = 1;
945 	} else {
946 	    end_of_message =
947 		(strncmp(inbuf, "-----END PGP MESSAGE,", 21) == 0);
948 	    ++infile_line;
949 	}
950 
951 	if (currentSection != noSections && end_of_message) {
952 	    /* End of this section */
953 	    if (gotcrc) {
954 		if (chkcrc != crc) {
955 		    fprintf(pgpout,
956  LANG("ERROR: Bad ASCII armor checksum in section %d.\n"), currentSection);
957 /* continue with decoding to see if there are other bad parts */
958 		    ret_code = -1;
959 		}
960 	    }
961 	    gotcrc = 0;
962 	    crc = CRCINIT;
963 	    section = 0;
964 
965 	    /* Try and find start of next section */
966 	    do {
967 		if (get_armor_line(inbuf, in) == NULL) {
968 		    if (in != original_in)
969 			fclose(in);
970 		    if ((in = open_next()) != NULL)
971 			continue;	/* Keep working on new in */
972 		    fprintf(pgpout,
973 		    LANG("Can't find section %d.\n"), currentSection + 1);
974 		    return -1;
975 		}
976 		++infile_line;
977 	    }
978 	    while (strncmp(inbuf, "-----BEGIN PGP MESSAGE", 22));
979 
980 	    /* Make sure this section is the correct one */
981 	    if (2 != sscanf(inbuf,
982 			    "-----BEGIN PGP MESSAGE, PART %d/%d",
983 			    &section, &noSections)) {
984 		fprintf(pgpout,
985 			LANG("Badly formed section delimiter, part %d.\n"),
986 			currentSection + 1);
987 		goto error;
988 	    }
989 	    if (section != ++currentSection) {
990 		fprintf(pgpout,
991 LANG("Sections out of order, expected part %d"), currentSection);
992 		if (section)
993 		    fprintf(pgpout,
994 			    LANG(", got part %d\n"), section);
995 		else
996 		    fputc('\n', pgpout);
997 		goto error;
998 	    }
999 	    /* Skip header after BEGIN line */
1000 	    if (skipheaders(in, inbuf, warned, 1) < 0)
1001 		goto error;
1002 	    if (feof(in)) {
1003 		fprintf(pgpout,
1004 		   LANG("ERROR: Hit EOF in header of section %d.\n"),
1005 			currentSection);
1006 		goto error;
1007 	    }
1008 
1009 	    /* Continue decoding */
1010 	    continue;
1011 	}
1012 #ifdef MACTC5
1013 	mac_poll_for_break();
1014 #endif
1015 
1016 /* Quit when hit the -----END PGP MESSAGE----- line or a blank,
1017  * or handle checksum
1018  */
1019 	if (inbuf[0] == PAD) {	/* Checksum lines start
1020 				   with PAD char */
1021 	    /* If the already-armored file is sent through MIME
1022 	     * and gets armored again, '=' will become '=3D'.
1023 	     * To make life easier, we detect and work around this
1024 	     * idiosyncracy.
1025 	     */
1026 	    if (strlen(inbuf) == 7 &&
1027 		inbuf[1] == '3' && inbuf[2] == 'D')
1028 		status = darmor_buffer(inbuf + 3, crcbuf, &n);
1029 	    else
1030 		status = darmor_buffer(inbuf + 1, crcbuf, &n);
1031 	    if (status < 0 || n != 3) {
1032 		fprintf(pgpout,
1033 LANG("ERROR: Badly formed ASCII armor checksum, line %d.\n"), line);
1034                 goto error;
1035 	    }
1036 	    chkcrc = (((long) crcbuf[0] << 16) & 0xff0000L) +
1037 		((crcbuf[1] << 8) & 0xff00L) + (crcbuf[2] & 0xffL);
1038 	    gotcrc = 1;
1039 	    continue;
1040 	}
1041 	if (inbuf[0] == '\0') {
1042 	    fprintf(pgpout,
1043 		    LANG("WARNING: No ASCII armor `END' line.\n"));
1044 	    break;
1045 	}
1046 	if (strncmp(inbuf, "-----END PGP ", 13) == 0)
1047 	    break;
1048 
1049 	status = darmor_buffer(inbuf, outbuf, &n);
1050 
1051 	if (status == -1) {
1052 	    fprintf(pgpout,
1053 	     LANG("ERROR: Bad ASCII armor character, line %d.\n"), line);
1054 	    gotcrc = 1;		/* this will print part number,
1055 				   continue with next part */
1056 	    ret_code = -1;
1057 	}
1058 	if (n > sizeof outbuf) {
1059 	    fprintf(pgpout,
1060 	     LANG("ERROR: Bad ASCII armor line length %d on line %d.\n"),
1061 		    n, line);
1062 	    goto error;
1063 	}
1064 	crc = crcbytes((byte *) outbuf, n, crc);
1065 	if (fwrite(outbuf, 1, n, out) != n) {
1066 	    ret_code = -1;
1067 	    break;
1068 	}
1069     }				/* line */
1070 
1071     if (gotcrc) {
1072 	if (chkcrc != crc) {
1073 	    fprintf(pgpout,
1074 		    LANG("ERROR: Bad ASCII armor checksum"));
1075 	    if (noSections > 0)
1076 		fprintf(pgpout,
1077 			LANG(" in section %d"), noSections);
1078 	    fputc('\n', pgpout);
1079 	    goto error;
1080 	}
1081     } else {
1082 	fprintf(pgpout,
1083 		LANG("Warning: Transport armor lacks a checksum.\n"));
1084     }
1085 
1086     if (in != original_in)
1087 	fclose(in);
1088     return ret_code;		/* normal return */
1089 error:
1090     if (in != original_in)
1091 	fclose(in);
1092     return -1;			/* error return */
1093 }				/* armordecode */
1094 
1095 static boolean
1096 is_armorfile(char *infile)
1097 {
1098     FILE *in;
1099     char inbuf[MAX_LINE_SIZE];
1100     char outbuf[MAX_LINE_SIZE];
1101     int n;
1102     long il;
1103 
1104     in = fopen(infile, FOPRARMOR);
1105     if (in == NULL)
1106 	return FALSE;	/* can't open file */
1107 
1108     /* Read to infile_line before we begin looking */
1109     for (il = 0; il < infile_line; ++il) {
1110 	if (get_armor_line(inbuf, in) == NULL) {
1111 	    fclose(in);
1112 	    return FALSE;
1113 	}
1114     }
1115 
1116     /* search file for delimiter line */
1117     for (;;) {
1118 	if (get_armor_line(inbuf, in) == NULL)
1119 	    break;
1120 	if (strncmp(inbuf, "-----BEGIN PGP ", 15) == 0) {
1121 	    if (strncmp(inbuf,
1122 		    "-----BEGIN PGP SIGNED MESSAGE-----", 34) == 0) {
1123 		fclose(in);
1124 		return TRUE;
1125 	    }
1126 	    n = 1;	/* Don't print warnings yet */
1127 	    if (skipheaders(in, inbuf, &n, 1) < 0 ||
1128 	        get_armor_line(inbuf, in) == NULL ||
1129 	        darmor_buffer(inbuf, outbuf, &n) < 0)
1130 		break;
1131 	    fclose(in);
1132 	    return TRUE;
1133 	}
1134     }
1135 
1136     fclose(in);
1137     return FALSE;
1138 }				/* is_armorfile */
1139 
1140 static int
1141 darmor_file(char *infile, char *outfile)
1142 {
1143     FILE *in, *out;
1144     char buf[MAX_LINE_SIZE];
1145     char outbuf[(MAX_LINE_SIZE*3+3)/4];
1146     int status, n;
1147     long il, fpos;
1148     char *litfile = NULL;
1149     int header_warned = 0;	/* Complained about unknown header */
1150 
1151     if ((in = fopen(infile, FOPRARMOR)) == NULL) {
1152 	fprintf(pgpout, LANG("ERROR: Can't find file %s\n"), infile);
1153 	return 10;
1154     }
1155     strcpy(armorfilename, infile);	/* store filename for multi-parts */
1156 
1157     /* Skip to infile_line */
1158     for (il = 0; il < infile_line; ++il) {
1159 	if (get_armor_line(buf, in) == NULL) {
1160 	    fclose(in);
1161 	    return -1;
1162 	}
1163     }
1164 
1165     /* Loop through file, searching for delimiter.  Decode anything with a
1166        delimiter, complain if there were no delimiter. */
1167 
1168     /* search file for delimiter line */
1169     for (;;) {
1170 	++infile_line;
1171 	if (get_armor_line(buf, in) == NULL) {
1172 	    fprintf(pgpout,
1173 		    LANG("ERROR: No ASCII armor `BEGIN' line!\n"));
1174 	    fclose(in);
1175 	    return 12;
1176 	}
1177 	if (strncmp(buf, "-----BEGIN PGP ", 15) == 0)
1178 	    break;
1179     }
1180     if (strncmp(buf, "-----BEGIN PGP SIGNED MESSAGE-----", 34) == 0) {
1181 	FILE *litout;
1182 	char *p;
1183 	int nline;
1184 
1185 	/*
1186 	 * It would be nice to allow headers here, as we could add
1187 	 * additional information to PGP messages, but it appears to
1188 	 * be too easy to spoof, given standard text viewers.  So,
1189 	 * forbid it outright until we sit down and figure out how to
1190 	 * thwart all the ways of faking an end-of-headers.  The
1191 	 * possibilities are:
1192 	 * - Enough trailing whitespace on a valid-looking line to force a
1193 	 *   line wrap.  The 80 column case is tricky, as the classical
1194 	 *   Big Iron IBM mainframe pads to 80 columns, and some terminal
1195 	 *   and text viewer combinations cause a blank line, while others
1196 	 *   don't.  A line that is exactly 80 columns wide but ends in
1197 	 *   a non-blank would do, too.
1198 	 * - A big pile of whitespace within a line, enough to
1199 	 *   produce something that looks like a blank line between
1200 	 *   the beginning and end parts.
1201 	 * - Various cursor-control sequences.
1202 	 * Basically, it's a nasty problem.  A very strong case can be made
1203 	 * for the argument that it's the text viewer's problem, and outside
1204 	 * PGP's jurisdiction, but that has a few conflicts with reality.
1205 	 */
1206 	charset_header[0] = '\0';
1207 	if (get_armor_line(buf, in) == NULL) {
1208 		fprintf(pgpout,
1209 LANG("ERROR: ASCII armor decode input ended unexpectedly!\n"));
1210 		fclose(in);
1211 		return 12;
1212 	}
1213 	if (buf[0] != '\0') {
1214 		fprintf(pgpout,
1215 LANG("ERROR: Header line added to ASCII armor: \"%s\"\n\
1216 ASCII armor corrupted.\n"), buf);
1217 		fclose(in);
1218 		return -1;
1219 
1220 	}
1221 
1222 	litfile = tempfile(TMP_WIPE | TMP_TMPDIR);
1223 	if ((litout = fopen(litfile, FOPWTXT)) == NULL) {
1224 	    fprintf(pgpout,
1225 LANG("\n\007Unable to write ciphertext output file '%s'.\n"), litfile);
1226 	    fclose(in);
1227 	    return -1;
1228 	}
1229 
1230 	status = 0;
1231 	for (;;) {
1232 	    ++infile_line;
1233 	    nline = status;
1234 	    status = get_line(buf, sizeof buf, in);
1235 	    if (status < 0) {
1236 		fprintf(pgpout,
1237 LANG("ERROR: ASCII armor decode input ended unexpectedly!\n"));
1238 		fclose(in);
1239 		fclose(litout);
1240 		rmtemp(litfile);
1241 		return 12;
1242 	    }
1243 	    if (strncmp(buf, "-----BEGIN PGP ", 15) == 0)
1244 		break;
1245 	    if (nline)
1246 		putc('\n', litout);
1247 	    /* De-quote lines starting with '- ' */
1248 	    fputs(buf + ((buf[0] == '-' && buf[1] == ' ') ? 2 : 0), litout);
1249 	    /* Copy trailing part of line, if any. */
1250 	    if (!status)
1251 		status = copyline(in, litout);
1252 	    /* Ignore error; get_line will discover it again */
1253 	}
1254 	fflush(litout);
1255 	if (ferror(litout)) {
1256 	    fclose(litout);
1257 	    fclose(in);
1258 	    rmtemp(litfile);
1259 	    return -1;
1260 	}
1261 	fclose(litout);
1262     }
1263     /* Skip header after BEGIN line */
1264     if (skipheaders(in, buf, &header_warned, 1) < 0) {
1265 	fclose(in);
1266 	return -1;
1267     }
1268     if (feof(in)) {
1269 	fprintf(pgpout, LANG("ERROR: Hit EOF in header.\n"));
1270 	fclose(in);
1271 	return 13;
1272     }
1273 
1274     if ((out = fopen(outfile, FOPWBIN)) == NULL) {
1275 	fprintf(pgpout,
1276 LANG("\n\007Unable to write ciphertext output file '%s'.\n"), outfile);
1277 	fclose(in);
1278 	return -1;
1279     }
1280     status = armordecode(in, out, &header_warned);
1281 
1282     if (litfile) {
1283 	char *canonfile, hold_charset[16];
1284 	char lit_mode = MODE_TEXT;
1285 	word32 dummystamp = 0;
1286 	FILE *f;
1287 
1288 	/* Convert clearsigned message to internal character set */
1289 	canonfile = tempfile(TMP_WIPE | TMP_TMPDIR);
1290 	strip_spaces = TRUE;
1291         if (charset_header[0]) {
1292             strcpy(hold_charset, charset);
1293             strcpy(charset, charset_header);
1294             init_charset();
1295         }
1296 	make_canonical(litfile, canonfile);
1297 	rmtemp(litfile);
1298 	litfile = canonfile;
1299         if (charset_header[0]) {
1300             strcpy(charset, hold_charset);
1301             init_charset();
1302 	}
1303 	/* Glue the literal file read above to the signature */
1304         f = fopen(litfile, FOPRBIN);
1305 
1306 	write_ctb_len(out, CTB_LITERAL2_TYPE, fsize(f) + 6, FALSE);
1307 	fwrite(&lit_mode, 1, 1, out);	/* write lit_mode */
1308 	fputc('\0', out);	/* No filename */
1309 	fwrite(&dummystamp, 1, sizeof(dummystamp), out);
1310 	/* dummy timestamp */
1311 	copyfile(f, out, -1L);	/* Append literal file */
1312 	fclose(f);
1313 	rmtemp(litfile);
1314     }
1315     if (write_error(out))
1316 	status = -1;
1317     fclose(out);
1318     fclose(in);
1319     return status;
1320 }				/* darmor_file */
1321 
1322 /* Entry points for generic interface names */
1323 
1324 int de_armor_file(char *infile, char *outfile, long *curline)
1325 {
1326     int status;
1327 
1328     if (verbose)
1329 	fprintf(pgpout,
1330 	     "de_armor_file: infile = %s, outfile = %s, curline = %ld\n",
1331 		infile, outfile, *curline);
1332     infile_line = (curline ? *curline : 0);
1333     status = darmor_file(infile, outfile);
1334     if (curline)
1335 	*curline = infile_line;
1336     return status;
1337 }
1338 
1339 boolean
1340 is_armor_file(char *infile, long startline)
1341 {
1342     infile_line = startline;
1343     return is_armorfile(infile);
1344 }
1345