home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #6 / amigamamagazinepolishissue1998.iso / varia / pgp / pgpamiga / source / config.c < prev    next >
C/C++ Source or Header  |  1993-12-23  |  14KB  |  574 lines

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