|
|
1.1.1.2 root 1: /* config.c - config file parser by Peter Gutmann
2: Parses config file for PGP
3:
4: Modified 24 Jun 92 - HAJK
5: Misc fixes for VAX C restrictions.
6:
7: */
8:
9: #include <ctype.h>
10: #include <string.h>
11: #include <stdio.h>
12: #include <stdlib.h>
13: #include "usuals.h"
14: #include "fileio.h"
15: #include "pgp.h"
1.1.1.3 root 16: #include "config.h"
17: #include "charset.h"
18:
19: static int lookup( char *key, int keyLength, char *keyWords[], int range );
20: static int extractToken( char *buffer, int *endIndex, int *length );
21: static int getaString( char *buffer, int *endIndex );
22: static int getAssignment( char *buffer, int *endIndex, INPUT_TYPE settingType );
23: static void processAssignment( int intrinsicIndex );
24:
1.1.1.2 root 25: /* The external config variables we can set here are referenced in pgp.h */
26:
27: /* Return values */
28:
29: #define ERROR -1
30: #define OK 0
31:
32: /* The types of error we check for */
33:
34: enum { NO_ERROR, ILLEGAL_CHAR_ERROR, LINELENGTH_ERROR };
35:
36: #define CPM_EOF 0x1A /* ^Z = CPM EOF char */
37:
38: #define MAX_ERRORS 3 /* Max.no.errors before we give up */
39:
40: #define LINEBUF_SIZE 100 /* Size of input buffer */
41:
1.1.1.3 root 42: static int line; /* The line on which an error occurred */
43: static int errCount; /* Total error count */
44: static boolean hasError; /* Whether this line has an error in it */
1.1.1.2 root 45:
46: /* The settings parsed out by getAssignment() */
47:
1.1.1.3 root 48: static char str[ LINEBUF_SIZE ];
49: static int value;
50: static boolean flag;
51: static char *errtag; /* prefix for printing error messages */
52: static char optstr[100]; /* option being processed */
1.1.1.2 root 53:
54: /* A .CFG file roughly follows the format used in the world-famous HPACK
55: archiver and is as follows:
56:
57: - Leading spaces/tabs (whitespace) are ignored.
58:
59: - Lines with a '#' as the first non-whitespace character are treated as
60: comment lines.
61:
62: - All other lines are treated as config options for the program.
63:
64: - Lines may be terminated by either linefeeds, carriage returns, or
1.1.1.4 ! root 65: carriage return/linefeed pairs (the latter being the DOS default
! 66: method of storing text files).
1.1.1.2 root 67:
68: - Config options have the form:
69:
70: <option> '=' <setting>
71:
72: where <setting> may be 'on', 'off', a numeric value, or a string
73: value.
74:
75: - If strings have spaces or the '#' character inside them they must be
76: surrounded by quote marks '"' */
77:
78: /* Intrinsic variables */
79:
80: #define NO_INTRINSICS (sizeof(intrinsics) / sizeof(intrinsics[0]))
81:
82: enum
83: { ARMOR, COMPRESS, SHOWPASS, KEEPBINARY, LANGUAGE,
84: MYNAME, TEXTMODE, TMP, TZFIX, VERBOSE, BAKRING,
85: ARMORLINES, COMPLETES_NEEDED, MARGINALS_NEEDED, PAGER,
1.1.1.3 root 86: CERT_DEPTH, CHARSET, CLEAR, SELF_ENCRYPT,
1.1.1.4 ! root 87: INTERACTIVE, PKCS_COMPAT,
1.1.1.3 root 88: /* options below this line can only be used as command line
89: * "long" options */
90: #define CONFIG_INTRINSICS BATCHMODE
91: BATCHMODE, FORCE
1.1.1.2 root 92: };
93:
1.1.1.3 root 94: static char *intrinsics[] =
1.1.1.2 root 95: { "ARMOR", "COMPRESS", "SHOWPASS", "KEEPBINARY", "LANGUAGE",
96: "MYNAME", "TEXTMODE", "TMP", "TZFIX", "VERBOSE", "BAKRING",
97: "ARMORLINES", "COMPLETES_NEEDED", "MARGINALS_NEEDED", "PAGER",
1.1.1.3 root 98: "CERT_DEPTH", "CHARSET", "CLEARSIG", "ENCRYPTTOSELF",
1.1.1.4 ! root 99: "INTERACTIVE", "PKCS_COMPAT",
1.1.1.3 root 100: /* command line only */
101: "BATCHMODE", "FORCE",
1.1.1.2 root 102: };
103:
1.1.1.3 root 104: static INPUT_TYPE intrinsicType[] =
1.1.1.2 root 105: { BOOL, BOOL, BOOL, BOOL, STRING,
1.1.1.3 root 106: STRING, BOOL, STRING, NUMERIC, NUMERIC, STRING,
1.1.1.2 root 107: NUMERIC, NUMERIC, NUMERIC, STRING,
1.1.1.3 root 108: NUMERIC, STRING, BOOL, BOOL,
1.1.1.4 ! root 109: BOOL, NUMERIC,
1.1.1.3 root 110: /* command line only */
111: BOOL, BOOL,
1.1.1.2 root 112: };
113:
114: /* Possible settings for variables */
115:
116: #define NO_SETTINGS 2
117:
1.1.1.3 root 118: static char *settings[] = { "OFF", "ON" };
1.1.1.2 root 119:
120:
121: /* Search a list of keywords for a match */
122:
1.1.1.3 root 123: static int lookup( char *key, int keyLength, char *keyWords[], int range )
1.1.1.2 root 124: {
1.1.1.3 root 125: int indx, pos = 0, matches = 0;
1.1.1.2 root 126:
127: strncpy(optstr, key, keyLength);
128: optstr[keyLength] = '\0';
129: /* Make the search case insensitive */
1.1.1.3 root 130: for( indx = 0; indx < keyLength; indx++ )
131: key[ indx ] = to_upper( key[ indx ] );
1.1.1.2 root 132:
1.1.1.3 root 133: for( indx = 0; indx < range; indx++ )
134: if( !strncmp( key, keyWords[ indx ], keyLength ) )
135: { if (strlen(keyWords[indx]) == keyLength)
136: return indx; /* exact match */
137: pos = indx;
1.1.1.2 root 138: ++matches;
139: }
140:
141: switch (matches)
142: { case 0: fprintf(stderr, "%s: unknown keyword: \"%s\"\n", errtag, optstr); break;
143: case 1: return pos;
144: default: fprintf(stderr, "%s: \"%s\" is ambiguous\n", errtag, optstr);
145: }
146: return ERROR;
147: }
148:
149: /* Extract a token from a buffer */
1.1.1.3 root 150: static int extractToken( char *buffer, int *endIndex, int *length )
1.1.1.2 root 151: {
1.1.1.3 root 152: int indx = 0, tokenStart;
1.1.1.2 root 153: char ch;
154:
155: /* Skip whitespace */
1.1.1.3 root 156: for( ch = buffer[ indx ]; ch && ( ch == ' ' || ch == '\t' ); ch = buffer[ indx ] )
157: indx++;
158: tokenStart = indx;
1.1.1.2 root 159:
160: /* Find end of setting */
1.1.1.3 root 161: while( indx < LINEBUF_SIZE && ( ch = buffer[ indx ] ) != '\0' && ch != ' ' && ch != '\t' )
162: indx++;
163: *endIndex += indx;
164: *length = indx - tokenStart;
1.1.1.2 root 165:
166: /* Return start position of token in buffer */
167: return( tokenStart );
168: }
169:
170:
171: /* Get a string constant */
1.1.1.3 root 172: static int getaString( char *buffer, int *endIndex )
1.1.1.2 root 173: {
174: boolean noQuote = FALSE;
175: int stringIndex = 0, bufIndex = 1;
176: char ch = *buffer;
177:
178: /* Skip whitespace */
179: while( ch && ( ch == ' ' || ch == '\t' ) )
180: ch = buffer[ bufIndex++ ];
181:
182: /* Check for non-string */
183: if( ch != '\"' )
184: {
185: *endIndex += bufIndex;
186:
187: /* Check for special case of null string */
188: if( !ch )
189: {
190: *str = '\0';
191: return( OK );
192: }
193:
194: /* Use nasty non-rigorous string format */
195: noQuote = TRUE;
196: }
197:
198: /* Get first char of string */
199: if( !noQuote )
200: ch = buffer[ bufIndex++ ];
201:
202: /* Get string into string */
203: while( ch && ch != '\"' )
204: {
205: /* Exit on '#' if using non-rigorous format */
206: if( noQuote && ch == '#' )
207: break;
208:
209: str[ stringIndex++ ] = ch;
210: ch = buffer[ bufIndex++ ];
211: }
212:
213: /* If using the non-rigorous format, stomp trailing spaces */
214: if( noQuote )
215: while( stringIndex > 0 && str[ stringIndex - 1 ] == ' ' )
216: stringIndex--;
217:
218: str[ stringIndex++ ] = '\0';
219: *endIndex += bufIndex;
220:
221: /* Check for missing string terminator */
222: if( ch != '\"' && !noQuote )
223: {
224: if (line)
225: fprintf(stderr, "%s: unterminated string in line %d\n", errtag, line );
226: else
227: fprintf(stderr, "unterminated string: '\"%s'\n", str );
228: hasError = TRUE;
229: errCount++;
230: return( ERROR );
231: }
232:
233: return( OK );
234: }
235:
236: /* Get an assignment to an intrinsic */
1.1.1.3 root 237: static int getAssignment( char *buffer, int *endIndex, INPUT_TYPE settingType )
1.1.1.2 root 238: {
239: int settingIndex = 0, length;
240:
241: buffer += extractToken( buffer, endIndex, &length );
242:
243: /* Check for an assignment operator */
244: if ( *buffer != '=' )
245: {
246: if (line)
247: fprintf(stderr, "%s: expected '=' in line %d\n", errtag, line );
248: else
249: fprintf(stderr, "%s: expected '=' after \"%s\"\n", errtag, optstr);
250: hasError = TRUE;
251: errCount++;
252: return( ERROR );
253: }
254: buffer++; /* Skip '=' */
255:
256: buffer += extractToken( buffer, endIndex, &length );
257:
258: switch( settingType )
259: {
260: case BOOL:
261: /* Check for known intrinsic - really more general than just
262: checking for TRUE or FALSE */
263: if( ( settingIndex = lookup( buffer, length, settings, NO_SETTINGS ) ) == ERROR )
264: {
265: hasError = TRUE;
266: errCount++;
267: return( ERROR );
268: }
269:
270: flag = ( settingIndex == 0 ) ? FALSE : TRUE;
271: break;
272:
273: case STRING:
274: /* Get a string */
275: getaString( buffer, &length );
276: break;
277:
278: case NUMERIC:
279: /* Get numeric input. Error checking is a pain since atoi()
280: has no real equivalent of NAN */
281: value = atoi( buffer );
282: break;
283: }
284:
285: return( settingIndex );
286: }
287:
288: /* Process an assignment */
289:
1.1.1.3 root 290: static void processAssignment( int intrinsicIndex )
1.1.1.2 root 291: {
292: if( !hasError )
293: switch( intrinsicIndex )
294: {
295: case ARMOR:
296: emit_radix_64 = flag;
297: break;
298:
299: case COMPRESS:
300: compress_enabled = flag;
301: break;
302:
303: case SHOWPASS:
304: showpass = flag;
305: break;
306:
307: case KEEPBINARY:
308: keepctx = flag;
309: break;
310:
311: case LANGUAGE:
312: strncpy(language, str, 15);
313: break;
314:
315: case BAKRING:
316: strcpy(floppyring, str);
317: break;
318:
319: case MYNAME:
320: strcpy(my_name, str);
321: break;
322:
323: case TEXTMODE:
324: if( flag )
1.1.1.3 root 325: literal_mode = MODE_TEXT;
1.1.1.4 ! root 326: else
! 327: literal_mode = MODE_BINARY;
1.1.1.2 root 328: break;
329:
330: case TMP:
331: /* directory pathname to store temp files */
332: settmpdir(str);
333: break;
334:
335: case TZFIX:
336: /* How many hours to add to time() to get GMT. */
337: /* Compute seconds from hours to shift to GMT: */
338: timeshift = 3600L * (long) value;
339: break;
340:
341: case VERBOSE:
1.1.1.3 root 342: switch (value)
343: {
344: case 0: quietmode = TRUE; verbose = FALSE; break;
345: case 1: quietmode = FALSE; verbose = FALSE; break;
346: case 2: quietmode = FALSE; verbose = TRUE; break;
347: default: quietmode = FALSE; verbose = FALSE;
348: }
1.1.1.2 root 349: break;
350:
351: case ARMORLINES:
352: pem_lines = value;
353: break;
354:
355: case MARGINALS_NEEDED:
356: marg_min = value;
357: if (marg_min < 1)
358: marg_min = 1;
359: break;
360:
361: case COMPLETES_NEEDED:
362: compl_min = value;
363: if (compl_min < 1)
364: compl_min = 1;
365: if (compl_min > 4)
366: compl_min = 4;
367: break;
368:
369: case CERT_DEPTH:
370: max_cert_depth = value;
371: if (max_cert_depth < 0)
372: max_cert_depth = 0;
373: if (max_cert_depth > 8)
374: max_cert_depth = 8;
375: break;
376:
377: case PAGER:
378: strcpy(pager, str);
379: break;
380:
381: case CHARSET:
382: strcpy(charset, str);
383: break;
384:
385: case CLEAR:
386: clear_signatures = flag;
387: break;
1.1.1.3 root 388:
389: case SELF_ENCRYPT:
390: encrypt_to_self = flag;
391: break;
392:
393: case INTERACTIVE:
394: interactive_add = flag;
395: break;
396:
397: case BATCHMODE: batchmode = flag; break;
398: case FORCE: force_flag = flag; break;
1.1.1.4 ! root 399: case PKCS_COMPAT: pkcs_compat = value; break;
1.1.1.2 root 400: }
401: }
402:
403: /* Process an option on a line by itself. This expects options which are
404: taken from the command-line, and is less finicky about errors than the
405: config-file version */
406:
407: int processConfigLine( char *option )
408: {
1.1.1.3 root 409: int indx, intrinsicIndex;
1.1.1.2 root 410: char ch;
411:
412: /* Give it a pseudo-linenumber of 0 */
413: line = 0;
414:
415: errtag = "pgp";
416: errCount = 0;
1.1.1.3 root 417: for( indx = 0;
418: indx < LINEBUF_SIZE && ( ch = option[ indx ] ) != '\0' &&
1.1.1.2 root 419: ch != ' ' && ch != '\t' && ch != '=';
1.1.1.3 root 420: indx++ );
421: if( ( intrinsicIndex = lookup( ( char * ) option, indx, intrinsics, NO_INTRINSICS ) ) == ERROR )
1.1.1.2 root 422: return -1;
1.1.1.3 root 423: if (option[indx] == '\0' && intrinsicType[intrinsicIndex] == BOOL)
1.1.1.2 root 424: { /* boolean option, no '=' means TRUE */
425: flag = TRUE;
426: processAssignment(intrinsicIndex);
427: }
428: else /* Get the value to set to, either as a string, a
429: numeric value, or a boolean flag */
1.1.1.3 root 430: if (getAssignment( ( char * ) option + indx, &indx, intrinsicType[ intrinsicIndex ] ) != ERROR)
1.1.1.2 root 431: processAssignment( intrinsicIndex );
432: return(errCount ? -1 : 0);
433: }
434:
435: /* Process a config file */
436: int processConfigFile( char *configFileName )
437: {
438: FILE *configFilePtr;
439: int ch = 0, theCh;
440: int errType, errPos = 0, lineBufCount, intrinsicIndex;
1.1.1.3 root 441: int indx;
1.1.1.2 root 442: char inBuffer[ LINEBUF_SIZE ];
443:
444: line = 1;
445: errCount = 0;
446: errtag = "config.txt";
447:
448: if( ( configFilePtr = fopen( configFileName, FOPRTXT ) ) == NULL )
449: {
450: fprintf(stderr, "Cannot open configuration file %s\n", configFileName );
451: return( OK ); /* treat like empty config file */
452: }
453:
454: /* Process each line in the configFile */
455: while( ch != EOF )
456: {
457: /* Skip whitespace */
1.1.1.3 root 458: while( ( ( ch = getc( configFilePtr ) ) == ' ' || ch == '\t' ) && ch != EOF )
459: ;
1.1.1.2 root 460:
461: /* Get a line into the inBuffer */
462: hasError = FALSE;
463: lineBufCount = 0;
464: errType = NO_ERROR;
465: while( ch != '\r' && ch != '\n' && ch != CPM_EOF && ch != EOF )
466: {
467: /* Check for an illegal char in the data */
468: if( ( ch < ' ' || ch > '~' ) && ch != '\r' && ch != '\n' &&
469: ch != ' ' && ch != '\t' && ch != CPM_EOF && ch != EOF )
470: {
471: if( errType == NO_ERROR )
472: /* Save position of first illegal char */
473: errPos = lineBufCount;
474: errType = ILLEGAL_CHAR_ERROR;
475: }
476:
477: /* Make sure the path is of the correct length. Note that the
478: code is ordered so that a LINELENGTH_ERROR takes precedence over
479: an ILLEGAL_CHAR_ERROR */
480: if( lineBufCount > LINEBUF_SIZE )
481: errType = LINELENGTH_ERROR;
482: else
483: inBuffer[ lineBufCount++ ] = ch;
484:
485: if( ( ch = getc( configFilePtr ) ) == '#' )
486: {
487: /* Skip comment section and trailing whitespace */
488: while( ch != '\r' && ch != '\n' && ch != CPM_EOF && ch != EOF )
489: ch = getc( configFilePtr );
490: break;
491: }
492: }
493:
494: /* Skip trailing whitespace and add der terminador */
495: while(lineBufCount && (( theCh = inBuffer[ lineBufCount - 1 ] ) == ' ' || theCh == '\t' ))
496: lineBufCount--;
497:
498: inBuffer[ lineBufCount ] = '\0';
499:
500: /* Process the line unless its a blank or comment line */
501: if( lineBufCount && *inBuffer != '#' )
502: {
503: switch( errType )
504: {
505: case LINELENGTH_ERROR:
506: fprintf(stderr, "%s: line '%.30s...' too long\n", errtag, inBuffer );
507: errCount++;
508: break;
509:
510: case ILLEGAL_CHAR_ERROR:
511: fprintf(stderr, "> %s\n ", inBuffer );
512: fprintf(stderr, "%*s^\n", errPos, "");
513: fprintf(stderr, "%s: bad character in command on line %d\n", errtag, line );
514: errCount++;
515: break;
516:
517: default:
1.1.1.3 root 518: for( indx = 0;
519: indx < LINEBUF_SIZE && ( ch = inBuffer[ indx ] ) != '\0'
1.1.1.2 root 520: && ch != ' ' && ch != '\t' && ch != '=';
1.1.1.3 root 521: indx++ )
522: ;
523: if( ( intrinsicIndex = lookup( inBuffer, indx, intrinsics, CONFIG_INTRINSICS ) ) == ERROR )
1.1.1.2 root 524: {
525: errCount++;
526: }
527: else
528: {
529: /* Get the value to set to, either as a string, a
530: numeric value, or a boolean flag */
1.1.1.3 root 531: getAssignment( inBuffer + indx, &indx, intrinsicType[ intrinsicIndex ] );
1.1.1.2 root 532: processAssignment( intrinsicIndex );
533: }
534: }
535: }
536:
537: /* Handle special-case of ^Z if configFile came off an MSDOS system */
538: if( ch == CPM_EOF )
539: ch = EOF;
540:
541: /* Exit if there are too many errors */
542: if( errCount >= MAX_ERRORS )
543: break;
544:
545: line++;
546: }
547:
548: fclose( configFilePtr );
549:
550: /* Exit if there were errors */
551: if( errCount )
552: {
553: fprintf(stderr, "%s: %s%d error(s) detected\n\n", configFileName, ( errCount >= MAX_ERRORS ) ?
554: "Maximum level of " : "", errCount );
555: return( ERROR );
556: }
557:
558: return( OK );
559: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.