--- pgp/src/armor.c 2018/04/24 16:39:37 1.1.1.3 +++ pgp/src/armor.c 2018/04/24 16:40:28 1.1.1.4 @@ -117,53 +117,49 @@ static long infile_line; /* Current lin static crcword crctable[256]; /* Table for speeding up CRC's */ -/* crchware simulates CRC hardware circuit. Generates true CRC - directly, without requiring extra NULL bytes to be appended - to the message. - Returns new updated CRC accumulator. -*/ -static -crcword crchware(byte ch, crcword poly, crcword accum) -{ int i; - crcword data; - data = ch; - data <<= CRCSHIFTS; /* shift data to line up with MSB of accum */ - i = 8; /* counts 8 bits of data */ - do - { /* if MSB of (data XOR accum) is TRUE, shift and subtract poly */ - if ((data ^ accum) & CRCHIBIT) - accum = (accum<<1) ^ poly; - else - accum <<= 1; - data <<= 1; - } while (--i); /* counts 8 bits of data */ - return (maskcrc(accum)); -} /* crchware */ - - /* mk_crctbl derives a CRC lookup table from the CRC polynomial. - The table is used later by crcupdate function given below. + The table is used later by the crcbytes function given below. mk_crctbl only needs to be called once at the dawn of time. + + The theory behind mk_crctbl is that table[i] is initialized + with the CRC of i, and this is related to the CRC of i>>1, + so the CRC of i>>1 (pointed to by p) can be used to derive + the CRC of i (pointed to by q). */ static void mk_crctbl(crcword poly) { int i; - for (i=0; i<256; i++) - crctable[i] = crchware((byte) i, poly, 0); -} /* mk_crctbl */ - - -/* crcupdate calculates a CRC using the fast table-lookup method. - Returns new updated CRC accumulator. -*/ -crcword crcupdate(byte data, register crcword accum) -{ byte combined_value; + crcword t, *p, *q; + p = q = crctable; + *q++ = 0; + *q++ = poly; + for (i = 1; i < 128; i++) + { t = *++p; + if (t & CRCHIBIT) + { t <<= 1; + *q++ = t ^ poly; + *q++ = t; + } + else + { t <<= 1; + *q++ = t; + *q++ = t ^ poly; + } + } +} - /* XOR the MSByte of the accum with the data byte */ - combined_value = (accum >> CRCSHIFTS) ^ data; - accum = (accum << 8) ^ crctable[combined_value]; - return (maskcrc(accum)); -} /* crcupdate */ +/* + * Accumulate a buffer's worth of bytes into a CRC accumulator, + * returning the new CRC value. + */ +crcword +crcbytes(byte *buf, unsigned len, register crcword accum) +{ + do { + accum = accum<<8 ^ crctable[(byte)(accum>>CRCSHIFTS) ^ *buf++]; + } while (--len); + return maskcrc(accum); +} /* crcbytes */ /* Initialize the CRC table using our codes */ void init_crc(void) @@ -218,27 +214,137 @@ static void outcrc (word32 crc, FILE *ou putc('\n',outFile); } /* outcrc */ -/* Return filename for output (text mode), but replace last letter of - * filename with the ascii for num (last two letters if num > 10). +/* Return filename for output (text mode), but replace last letter(s) of + * filename with the ascii for num. It will use the appropriate number + * of digits for ofnum when converting num, so if ofnum < 10, use 1 digit, + * >= 10 and < 100 use 2 digits, >= 100 and < 1000 use 3 digits. If its + * >= 1000, then we have other problems to worry about, and this might do + * weird things. */ -static char *numFilename( char *fname, int num) +static char *numFilename( char *fname, int num, int ofnum) { static char fnamenum[MAX_PATH]; int len; + int offset = 1; strcpy (fnamenum, fname); len = strlen (fnamenum); - if (num < 10) - fnamenum[len-1] = '0' + num; - else /* If num > 100, this will be slightly screwy */ - { fnamenum[len-2] = '0' + (num / 10); - fnamenum[len-1] = '0' + (num % 10); - } + do { + fnamenum[len-offset] = '0' + (num%10); + num /= 10; + ofnum /= 10; + offset++; + } while (ofnum >= 1 && offset < 4); return(fnamenum); } /* + * Reads and discards a line from the given file. Returns -1 on error or + * EOF, 0 if the line is blank, and 1 if the line is not blank. + */ +static int +skipline(FILE *f) +{ + int state, flag, c; + + state = 0; + flag = 0; + for (;;) { + c = getc(f); + if (c == '\n') + return flag; + if (state) + { ungetc(c, f); + return flag; + } + if (c == EOF) + return -1; + if (c == '\r') + state = 1; + else if (c != ' ') + flag = 1; + } +} /* skipline */ + +/* + * Copies a line from the input file to the output. Does NOT copy the + * trailing newline. Returns -1 on EOF or error, 0 if the line was terminated + * by EOF, and 1 if the line was terminated with a newline sequence. + */ +static int +copyline(FILE *in, FILE *out) +{ + int state, flag, c; + + state = 0; + for (;;) { + c = getc(in); + if (c == '\n') + return 1; + if (state) + { ungetc(c, in); + return 1; + } + if (c == EOF) + return 0; + if (c == '\r') + state = 1; + else + putc(c, out); + } +} /* copyline */ + +/* + * Reads a line from file f, up to the size of the buffer. The line in the + * buffer will NOT include line termination, although any of (CR, LF, CRLF) + * is accepted on input. The return value is -1 on error, 0 if the line + * was terminated abnormally (EOF, error, or out of buffer space), and + * 1 if the line was terminated normally. + * + * Passing in a buffer less than 2 characters long is not a terribly bright + * idea. + */ +static int +getline(char *buf, int n, FILE *f) +{ + int state; + char *p; + int c; + + state = 0; + p = buf; + for (;;) + { c = getc(f); + if (c == '\n') + { *p = 0; + return 1; /* Line terminated with \n or \r\n */ + } + if (state) + { ungetc(c, f); + *p = 0; + return 1; /* Line terminated with \r */ + } + if (c == EOF) + { *p = 0; + return (p == buf) ? -1 : 0; /* Error */ + } + if (c == '\r') + state = 1; + else if (--n > 0) + *p++ = c; + else + { + ungetc(c, f); + *p = 0; + return 0; /* Out of buffer space */ + } + } /* for (;;) */ +} /* getline */ + +/* * Read a line from file f, buf must be able to hold at least 80 characters. - * Strips trailing spaces, can read LF, CRLF and CR textfiles. + * Strips trailing spaces and line terminator, can read LF, CRLF and CR + * textfiles. Anything after 80 characters is ignored. It can't be ASCII + * armor anyway. */ static char * get_armor_line(char *buf, FILE *f) @@ -268,21 +374,39 @@ get_armor_line(char *buf, FILE *f) } -/* Encode a file in sections. 64 ASCII bytes * 720 lines = 46K, - recommended max. Usenet message size is 50K so this leaves a nice - margin for .signature. In the interests of orthogonality and - programmer laziness no check is made for a message containing only - a few lines (or even just an 'end') after a section break. -*/ +/* + * Encode a file in sections. 64 ASCII bytes * 720 lines = 46K, + * recommended max. Usenet message size is 50K so this leaves a nice + * margin for .signature. In the interests of orthogonality and + * programmer laziness no check is made for a message containing only + * a few lines (or even just an 'end') after a section break. + */ #define LINE_LEN 48L int pem_lines = 720; #define BYTES_PER_SECTION (LINE_LEN * pem_lines) +#if 1 +/* This limit is advisory only; longer lines are handled properly. + * The only requirement is that this be at least as long as the longest + * delimiter string used by PGP + * (e.g. "-----BEGIN PGP MESSAGE, PART %02d/%02d-----\n") + */ +#define MAX_LINE_SIZE 80 +#else #ifdef MSDOS /* limited stack space */ #define MAX_LINE_SIZE 256 #else #define MAX_LINE_SIZE 1024 #endif +#endif + +#ifndef VMS +/* armored files are opened in binary mode so that CRLF/LF/CR files + can be handled by all systems */ +#define FOPRPEM FOPRBIN +#else +#define FOPRPEM FOPRTXT +#endif extern boolean verbose; /* Undocumented command mode in PGP.C */ extern boolean filter_mode; @@ -298,7 +422,7 @@ static int pem_file(char *infilename, char *outfilename, char *clearfilename) { char buffer[MAX_LINE_SIZE]; - int i,rc,bytesRead,lines = 0; + int i, rc, bytesRead, lines = 0; int noSections, currentSection = 1; long fileLen; crcword crc; @@ -332,7 +456,7 @@ int pem_file(char *infilename, char *out else { if (noSections > 1) { force_extension(outfilename, ASC_EXTENSION); - outFile = fopen (numFilename (outfilename, 1), FOPWTXT); + outFile = fopen (numFilename (outfilename, 1, noSections), FOPWTXT); } else outFile = fopen(outfilename,FOPWTXT); @@ -351,15 +475,20 @@ int pem_file(char *infilename, char *out return(1); } fprintf (outFile, "-----BEGIN PGP SIGNED MESSAGE-----\n\n"); - while (fgets(buffer, sizeof(buffer), clearFile) != NULL) + while ((i = getline(buffer, sizeof buffer, clearFile)) >= 0) { /* Quote lines beginning with '-' as per RFC1113; - * Also quote lines beginning with "From "; - * this is for Unix systems which add ">" to such lines. + * Also quote lines beginning with "From "; this is + * for Unix mailers which add ">" to such lines. */ - if (buffer[0] == '-' || (strncmp(buffer, "From ", 5)==0)) + if (buffer[0] == '-' || strncmp(buffer, "From ", 5)==0) fputs("- ", outFile); fputs(buffer, outFile); + /* If there is more on this line, copy it */ + if (i == 0) + if (copyline(clearFile, outFile) <= 0) + break; + fputc('\n', outFile); } fclose (clearFile); putc('\n', outFile); @@ -390,18 +519,10 @@ int pem_file(char *infilename, char *out if (bytesRead < LINE_LEN) fill0 (buffer+bytesRead, LINE_LEN-bytesRead); - for (i=0; i .a10 ) */ c = *p; *p = '1'; - if ((fp = fopen(pemfilename, FOPRTXT)) != NULL) + if ((fp = fopen(pemfilename, FOPRPEM)) != NULL) return(fp); *p = c; /* restore original character */ } @@ -550,7 +685,7 @@ open_next(void) s[1] = *s; *p = '1'; /* insert digit ( fn0 -> fn10 ) */ - return(fopen(pemfilename, FOPRTXT)); + return(fopen(pemfilename, FOPRPEM)); } /* @@ -647,8 +782,18 @@ int end_of_message; /* Quit when hit the -----END PGP MESSAGE----- line or a blank, or handle checksum */ if (inbuf[0] == PAD) /* Checksum lines start with PAD char */ - { status = dpem_buffer (inbuf+1,crcbuf,&n); - if (status==-1 || n!=3) + { + /* If the already-armored file is sent through MIME + * and gets armored again, '=' will become '=3D'. + * To make life easier, we detect and work around this + * idiosyncracy. + */ + if (strlen(inbuf) == 7 && + inbuf[1] == '3' && inbuf[2] == 'D') + status = dpem_buffer(inbuf+3, crcbuf, &n); + else + status = dpem_buffer(inbuf+1, crcbuf, &n); + if ( status < 0 || n != 3 ) { fprintf(pgpout,PSTR("ERROR: Badly formed ASCII armor checksum, line %d.\n"),line); return -1; } @@ -678,8 +823,7 @@ int end_of_message; return -1; } - for (i=0; i= buf && *p == '\n') - { nline = TRUE; - /* remove \n, original file may have ended in unterminated line */ - *p = '\0'; - } - /* De-quote lines starting with '- ' */ - fputs(buf + ((buf[0]=='-'&&buf[1]==' ')?2:0), litout); - } + + if (strncmp(buf,"-----BEGIN PGP ", 15) == 0) + break; + if (nline) + putc('\n', litout); + /* De-quote lines starting with '- ' */ + fputs(buf + ((buf[0]=='-'&&buf[1]==' ')?2:0), litout); + /* Copy trailing part of line, if any. */ + if (!status) + status = copyline(in, litout); + /* Ignore error; getline will discover it again */ } fflush (litout); if (ferror(litout))