/********************************************************************
* wilkinson
* 3.36VMS-2
* 1996/01/28 14:00
* gopher_root1:[gopher.g2.vms2_13.gopherd]gopherdconf.c,v
* Exp
*
* Paul Lindner, University of Minnesota CIS.
*
* Copyright 1991, 1992 by the Regents of the University of Minnesota
* see the file "Copyright" in the distribution for conditions of use.
*********************************************************************
* MODULE: gopherdconf.c
* Routines to parse the gopherd.conf file.
*********************************************************************
* Revision History:
* gopherdconf.c,v
* Revision 3.36VMS-2  1996/01/28 14:00  wilkinson
* Allow OPCOM message console selection configuration to have spaces
*
* Revision 3.36VMS-1  1996/01/24 11:00  wilkinson
* Made OPCOM message console selection configurable
*
* Revision 3.36VMS 1995/09/25 14:31    wilkinson
* Consolodate VMS/Unix source code for server as well as client
*
* Revision 3.36  1995/02/16  22:32:39  lindner
* HTML icon support
*
* Revision 3.35  1995/02/13  19:07:06  lindner
* Add MaxConnection limit for total server
*
* Revision 3.34  1995/02/11  06:21:26  lindner
* remove pid junk
*
* Revision 3.33  1995/02/06  22:14:50  lindner
* remove unused variables
*
* Revision 3.32  1995/02/02  17:13:54  lindner
* Fix memory leaks
*
* Revision 3.31  1994/12/15  17:33:19  lindner
* Add BlockScript: keyword support
*
* Revision 3.30  1994/12/08  05:23:00  lindner
* Fix wording problem in integrity check
*
* Revision 3.29  1994/10/10  18:39:36  lindner
* Change auxconf: to use regular expressions
*
* Revision 3.28  1994/09/29  19:59:16  lindner
* Force people to update their gopherd.conf files
*
* Revision 3.27  1994/08/18  22:28:17  lindner
* Abstract for top level and malloc casts
*
* Revision 3.26  1994/08/03  03:33:25  lindner
* Include files in SERVERDIR
*
* Revision 3.25  1994/07/22  22:56:18  lindner
* More NO_AUTH stuff..
*
* Revision 3.24  1994/07/22  22:26:25  lindner
* NO_AUTHENTICATION mods
*
* Revision 3.23  1994/07/22  16:37:07  lindner
* Add INCLUDE directive for gopherd.conf files
*
* Revision 3.22  1994/07/21  17:23:05  lindner
* Add File Separator code
*
* Revision 3.21  1994/06/29  05:30:08  lindner
* Add code to handle lists of authscripts and authitems
*
* Revision 3.20  1994/03/31  22:46:26  lindner
* Allow whitespace in the gopherd.conf file before tokens
*
* Revision 3.19  1994/03/17  21:18:39  lindner
* Massive reworking of access limits
*
* Revision 3.18  1994/03/17  04:30:17  lindner
* VMS fixes gopherd.h
*
* Revision 3.17  1994/03/08  15:56:01  lindner
* gcc -Wall fixes
*
* Revision 3.16  1994/01/21  03:59:11  lindner
* Add support for timezone variable and fix return type
*
* Revision 3.15  1993/12/09  20:48:16  lindner
* Debug fixes
*
* Revision 3.14  1993/10/27  20:36:59  lindner
* Plug memory leak
*
* Revision 3.13  1993/10/20  03:18:17  lindner
* Code for ignore_patt:
*
* Revision 3.12  1993/10/11  04:39:49  lindner
* Allow quotes in dirname for auxconf
*
* Revision 3.11  1993/10/07  05:19:54  lindner
* Update for gateway items and GDCevalDir
*
* Revision 3.10  1993/10/04  06:48:57  lindner
* mods to support auxconf files
*
* Revision 3.9  1993/09/30  17:05:11  lindner
* start of subconf
*
* Revision 3.8  1993/09/21  04:16:50  lindner
* Move cache settings into gopherd.conf
*
* Revision 3.7  1993/08/23  18:46:17  lindner
* Crude addition of a veronica top-level block
*
* Revision 3.6  1993/08/20  18:03:07  lindner
* Mods to allow gopherd.conf files control ftp gateway access
*
* Revision 3.5  1993/07/27  05:27:49  lindner
* Mondo Debug overhaul from Mitra
*
* Revision 3.4  1993/07/23  03:19:20  lindner
* Mods for using decoder:'s
*
* Revision 3.3  1993/04/15  17:09:22  lindner
* none
*
* Revision 3.2  1993/03/24  20:25:14  lindner
* Addition for secureusers file
*
* Revision 3.1.1.1  1993/02/11  18:02:51  lindner
* Gopher+1.2beta release
*
* Revision 1.3  1993/02/09  22:30:56  lindner
* Additions for multi-languages
*
* Revision 1.2  1993/01/30  23:57:44  lindner
* Gopher+ stuff
*
* Revision 1.1  1992/12/10  23:13:27  lindner
* gopher 1.1 release
*
*
*********************************************************************/

#ifdef VMS_SERVER
#define GSGOPHEROBJ_C
#endif
#include "gopherdconf.h"
#ifdef VMS_SERVER
#undef GSGOPHEROBJ_C
#include <ctype.h>
#include "fileio.h"
#include <opcdef.h>
#endif
#include "Malloc.h"
#include "String.h"
#include <stdio.h>
#include "util.h"
#include "Debug.h"
#include "conf.h"

#ifdef VMS_SERVER
       /*  To avoid compliation errors when #including "gopherd.h" or
           "globals.h" to get these external variables, we'll just
           define them as external here...
       */
extern int vms_OPC$M_num;
extern int vms_OPC$M_NM[];
extern char *vms_OPC$M_TXT[];
#endif

/*********************************************/

GDCobj *
GDCnew()
{
    GDCobj *gdc;

    gdc = (GDCobj *) malloc(sizeof(GDCobj));

    gdc->Extensions   = EXAnew();

#ifndef NO_AUTHENTICATION
    gdc->Sites        = SiteArrayNew();
    gdc->Securityon   = FALSE;

    gdc->Authroutines = AUTHAnew(5);
    gdc->Authitems    = AUTHITEMSnew(5);
    gdc->Serverpw     = STRnew();
#endif

    gdc->RunFromInetd = FALSE;
    gdc->Caching      = TRUE;
    gdc->Cachetime    = 180;  /** Three minutes ***/

    gdc->Logfile      = STRnew();
    gdc->Errorfile    = STRnew();
#ifndef VMS_SERVER
    gdc->Data_Dir     = STRnew();
#else
    gdc->DataDir      = STRnew();
#endif
    gdc->Hostname     = STRnew();
    gdc->Port         = 0;
    gdc->chroot       = TRUE;
    gdc->Defaccess    = ACC_FULL;
    gdc->BummerMsg    = STRnew();

    gdc->Admin        = STRnew();
    gdc->AdminEmail   = STRnew();
    gdc->Abstract     = STRnew();

    gdc->Site         = STRnew();
    gdc->Org          = STRnew();
    gdc->Geog         = STRnew();
    gdc->Loc          = STRnew();
    gdc->Lang         = STRnew();
    gdc->TZ           = 0;
    gdc->VeronicaIndex = TRUE;

    gdc->Tixfile      = STRnew();

    STRset(gdc->Logfile, "");
    STRset(gdc->Errorfile, "");
    STRset(gdc->BummerMsg, "");
    STRset(gdc->Hostname, "");

    gdc->other_dirs = STAnew(2);
    gdc->other_gdcs = STAnew(2);

    gdc->FileSeparators = STAnew(5);

    gdc->BlkScriptBlocks = STAnew(5);
    gdc->BlkScripts      = STAnew(5);

    gdc->MaxConnections  = -1;

#ifdef VMS_SERVER
               /* Override Unix defaults w/ VMS ones, or provide
                  initialization when it's omitted under Unix      */
    gdc->Port         = GOPHER_PORT;
    gdc->chroot       = FALSE;
    gdc->TZ           = -1;
    gdc->TimeZoneText = STRnew();
    gdc->MaxLoad      = 0.0;

    /* New VMS configuration options */
    gdc->IsGplus      = FALSE;
    gdc->IgnoreAll    = FALSE;
    gdc->SortDir      = TRUE;
    gdc->FTPPort      = -1;
    gdc->EXECPort     = -1;
    gdc->SRCHPort     = -1;
    gdc->OVERPort     = -1;
    gdc->OVERSize     = -1;
    gdc->SortShell    = TRUE;
    gdc->SortGREP     = TRUE;
    gdc->SortCMD1     = TRUE;
    gdc->ReadTimeout  = READTIMEOUT;
    gdc->OPCOM        = OPC$M_NM_CENTRL | OPC$M_NM_NTWORK;
    gdc->Hidden_Prefix= STRnew();
    gdc->Link_Prefix  = STRnew();
    gdc->LookAside    = STRnew();
    gdc->DName        = STRnew();
    gdc->DHead        = STRnew();
    gdc->DFoot        = STRnew();
    gdc->Scratch_Dir  = STRnew();
    gdc->Support_Dir  = STRnew();
    gdc->Spawn_Init   = STRnew();
    gdc->RestartLnm   = STRnew();
    gdc->ConfigFile   = STRnew();
    gdc->LogFileTag   = STRnew();
    gdc->rollover     = ROLLOVER_NEVER;

    /* Initialize strings for VMS */
    STRset(gdc->DataDir, DATA_DIRECTORY);
    STRset(gdc->Admin, "");
    STRset(gdc->AdminEmail, "");
    STRset(gdc->Site, "");
    STRset(gdc->Org, "");
    STRset(gdc->Geog, "");
    STRset(gdc->Lang, "");
    STRset(gdc->Loc, "");
    STRset(gdc->TimeZoneText,"");
    STRset(gdc->Hidden_Prefix, "_");
    STRset(gdc->Link_Prefix, ".");
    STRset(gdc->LookAside, ".cap");
    STRset(gdc->DName, "");
    STRset(gdc->DHead, "");
    STRset(gdc->DFoot, "");
    STRset(gdc->Scratch_Dir,"sys$scratch:");
    STRset(gdc->Support_Dir,"sys$disk:");
    STRset(gdc->Spawn_Init, LOGINCOM);
    STRset(gdc->RestartLnm, RESTART);
    STRset(gdc->ConfigFile, CONF_FILE);
    STRset(gdc->LogFileTag, "");
#endif
    return(gdc);
}

void
GDCdestroy(gdc)
 GDCobj *gdc;
{
#ifdef VMS_SERVER           /* Oops... did this get misplaced for unix? */
    EXAdestroy(gdc->Extensions);
#endif
#ifndef NO_AUTHENTICATION
#ifndef VMS_SERVER          /* Oops... did this get misplaced for unix? */
    EXAdestroy(gdc->Extensions);
#endif
    SiteArrDestroy(gdc->Sites);
    AUTHAdestroy(gdc->Authroutines);
    AUTHITEMSdestroy(gdc->Authitems);
    STRdestroy(gdc->Serverpw);
#endif

    STRdestroy(gdc->Logfile);
    STRdestroy(gdc->BummerMsg);
    STRdestroy(gdc->Hostname);
    STRdestroy(gdc->Lang);
    STRdestroy(gdc->Tixfile);
    STRdestroy(gdc->Abstract);

    /** etc..**/

    STAdestroy(gdc->other_dirs);
    STAdestroy(gdc->other_gdcs);
    STAdestroy(gdc->FileSeparators);

    STAdestroy(gdc->BlkScriptBlocks);
    STAdestroy(gdc->BlkScripts);

#ifdef VMS_SERVER
    STRdestroy(gdc->Hidden_Prefix);
    STRdestroy(gdc->Link_Prefix);
    STRdestroy(gdc->LookAside);
    STRdestroy(gdc->DName);
    STRdestroy(gdc->DHead);
    STRdestroy(gdc->DFoot);
    STRdestroy(gdc->Scratch_Dir);
    STRdestroy(gdc->Support_Dir);
    STRdestroy(gdc->Spawn_Init);
    STRdestroy(gdc->RestartLnm);
    STRdestroy(gdc->ConfigFile);
    STRdestroy(gdc->LogFileTag);
                       /* Don't forget these; they're strings, too */
    STRdestroy(gdc->Errorfile);
    STRdestroy(gdc->DataDir);
    STRdestroy(gdc->Admin);
    STRdestroy(gdc->AdminEmail);
    STRdestroy(gdc->Site);
    STRdestroy(gdc->Org);
    STRdestroy(gdc->Loc);
    STRdestroy(gdc->Geog);
    STRdestroy(gdc->TimeZoneText);
#endif
    free(gdc);
}

#define MAXGDCFLEVEL 10
static FILE *GDCfiles[MAXGDCFLEVEL]; /* can 'include' up to ten levels deep */
static int  GDCfilenum = -1;

static void
GDCfilePush(f)
 FILE *f;
{
    if ((++GDCfilenum) < MAXGDCFLEVEL)
         GDCfiles[GDCfilenum] = f;
    else {
#ifndef VMS_SERVER
         fprintf(stderr, "Nesting level too deep for include directive\n");
         exit(-1);
#else
         VMS$fprintf(stderr, "Nesting level too deep for include directive\n");
         gopherd_exit(-1);
#endif
    }
}

static
char *
GDCfileGetline(inputline, size)
 char *inputline;
 int  size;
{
    char* farg;

    while (NULL == (farg = fgets(inputline, size, GDCfiles[GDCfilenum]))) {
         if (GDCfilenum == 0) {
              fclose(GDCfiles[GDCfilenum]);
              return(farg);
         }

         /** Try popping up to the previous file.. **/
         fclose(GDCfiles[GDCfilenum]);
         GDCfilenum--;
    }

    return(farg);
}


/*
* Parse Gopherd.conf tokens..
*/

static
boolean
GDCtokens(gdc, token, rest)
 GDCobj *gdc;
 char *token;
 char *rest;
{
    boolean success = TRUE;

#ifdef VMS_SERVER
    Debug("GDC: %s: ", token);
    Debug("%s\n", rest);

    if ((strcasecmp(token, "ACCESS") != 0) &&
           (strcasecmp(token, "ABSTRACT") != 0) &&
                   /* Add here any other selections which we don't
                       want to allow a "!" to terminate the line as
                       traditional OpenVMS DCL-style commentary   */
           (strcasecmp(token, "ADMINEMAIL") != 0))
    {
       char *cp;

       if ((cp = strchr(rest, '!')) != NULL) {
           *cp = '\0';         /*  Allow VMS-style "!" commentary as well  */
           for(--cp;isspace(*cp)||!isprint(*cp);cp--)
               *cp = '\0';    /*  Strip trailing non-text                  */

       }
   }
#endif


    if (strcasecmp(token, "ADMIN")==0)
         GDCsetAdmin(gdc, rest);
#ifndef NO_AUTHENTICATION
    else if (strcasecmp(token, "ACCESS") == 0) {
         Accesslevel moo;

         success = SiteProcessLine(gdc->Sites, rest, gdc->Defaccess);
         moo = SiteDefAccess(gdc->Sites);

         if (moo != ACC_UNKNOWN)
              gdc->Defaccess = moo;

         gdc->Securityon = TRUE;
    }
    else if (strcasecmp(token, "AUTHSCRIPT")==0) {
         success = AUTHAprocessLine(gdc->Authroutines, rest);
    }
    else if (strcasecmp(token, "AUTHITEM")==0) {
         success = AUTHITEMSprocessLine(gdc->Authitems, rest);
    }
    else if (strcasecmp(token, "SERVERPW")==0) {
         GDCsetPW(gdc, rest);
    }
#endif
    else if (strcasecmp(token, "ADMINEMAIL")==0)
         GDCsetAdminEmail(gdc, rest);
    else if (strcasecmp(token, "HOSTALIAS")==0)
         GDCsetHostname(gdc, rest);
    else if (strcasecmp(token, "SITE")==0)
         GDCsetSite(gdc, rest);
    else if (strcasecmp(token, "ORG")==0)
         GDCsetOrg(gdc, rest);
    else if (strcasecmp(token, "LOC")==0)
         GDCsetLoc(gdc, rest);
    else if (strcasecmp(token, "LOGFILE")==0)
         GDCsetLogfile(gdc, rest);
    else if (strcasecmp(token, "ERRORFILE")==0)
         GDCsetErrorfile(gdc, rest);
    else if (strcasecmp(token, "GEOG")==0)
         GDCsetGeog(gdc, rest);
    else if (strcasecmp(token, "BUMMERMSG")==0)
         GDCsetBummerMsg(gdc, rest);
    else if (strcasecmp(token, "LANGUAGE")==0) {
         rest = skip_whitespace(rest);
         GDCsetLang(gdc, rest);
    }
#ifndef VMS_SERVER              /*  Do TimeZones differently for OpenVMS */
    else if (strcasecmp(token, "TZ") == 0)
         GDCsetTZ(gdc,atoi(rest));
#endif
    else if (strcasecmp(token, "VIEWEXT")==0) {
         success = EXAprocessLine(gdc->Extensions, EXT_VIEW, rest,
                                  GDCgetLang(gdc));
    }
    else if (strcasecmp(token, "BLOCKEXT")==0) {
         success = EXAprocessLine(gdc->Extensions, EXT_BLOCK, rest, NULL);
    }
    else if (strcasecmp(token, "BLOCKREFEXT")==0) {
         success = EXAprocessLine(gdc->Extensions, EXT_BLOCKREF, rest, NULL);
    }
    else if (strcasecmp(token, "IGNORE")==0) {
         success = EXAprocessLine(gdc->Extensions, EXT_IGNORE, rest, NULL);
    }
    else if (strcasecmp(token, "IGNORE_PATT")==0) {
         success = EXAprocessLine(gdc->Extensions, EXT_IGNOREPAT, rest, NULL);
    }
    else if (strcasecmp(token, "DECODER")==0) {
         success = EXAprocessLine(gdc->Extensions, EXT_DECODER, rest, NULL);
    } else if (strcasecmp(token, "ABSTRACT") == 0) {
         GDCsetAbstract(gdc, rest);
    }
    else if (strcasecmp(token, "VERONICAINDEX")==0) {
         if (strcasecmp(rest, "no")==0)
              GDCsetShouldIndex(gdc, FALSE);
         else
              GDCsetShouldIndex(gdc, TRUE);
    }
    else if (strcasecmp(token, "CACHETIME")==0) {
         GDCsetCachetime(gdc, atoi(rest));
    }
    else if (strcasecmp(token, "FILESEP")==0) {
         GDCaddFileSep(gdc, rest);
    }
    else if (strcasecmp(token, "INCLUDE") == 0) {
         /** Check to see if we can open the file.. **/
         /** Then push it onto the stack.. **/
         FILE *gdcfile;

#ifndef VMS_SERVER
         if (*rest != '/') {
              char tmpstr[256];

              sprintf(tmpstr, "%s/%s", GDCDIR, rest);
              strcpy(rest, tmpstr);
         }
#endif
         if ((gdcfile = fopen(rest, "r")) == (FILE *) NULL) {
#ifndef VMS_SERVER
              fprintf(stderr,"Cannot open file '%s'\n", rest);
              exit(-1);
#else
              VMS$fprintf(stderr,"Cannot open file '%s'\n", rest);
              gopherd_exit(-1);
#endif
         }
         GDCfilePush(gdcfile);

    }
    else if (strcasecmp(token, "BLOCKSCRIPT") == 0) {
         char *cp;

         /** Block name is first argument, script to run is second **/

         cp = strchr(rest, ' ');
         if (cp != NULL) {
              *cp = '\0';
              cp++;

              GDCpushBlockScript(gdc, rest, cp);

         } else {
              success = FALSE;
         }
    }
#ifndef VMS_SERVER
    else if (strcasecmp(token, "AUXCONF")==0) {
         /** Directory is first arg, conf file is second **/
         char *cp;

         if (*rest == '\"') {
              /*** " Dir is in quotes.. ***/
              rest++;
              cp = strchr(rest, '\"'); /* " Fix for hilit19 */
              if (cp != NULL) {
                   *cp = '\0';
                   cp ++;
              }
         } else {
              cp = strchr(rest, ' ');
         }

         if (cp == NULL)
              success = FALSE;
         else {
              *cp = '\0';
              GDCpushOtherGDC(gdc, rest, cp+1);
         }
    }
#endif
    else if (strcasecmp(token, "MAXCONNECTIONS")==0) {
         int numconns = atoi(rest);

         if (numconns > 1)
              GDCsetMaxconns(gdc, atoi(rest));
    }
#ifdef VMS_SERVER
           /*  VMS configuration options follow                        */
           /*  Although these first few options probably ought to be
                   configurable for Unix as well, oughtn't they?       */
    else if (strcasecmp(token, "EXT")==0) {
         success = EXAprocessLine(gdc->Extensions, EXT_MISC, rest, NULL);
    }
    else if (strcasecmp(token, "DataDirectory")==0)
         GDCsetDatadir(gdc, rest);

    else if (strcasecmp(token, "Port")==0)
         GDCsetPort(gdc, atoi(rest));

    else if (strcasecmp(token, "INETD")==0) {
           if (strcasecmp(rest, "True")==0)
               GDCsetInetdActive(gdc,TRUE);
           else
               GDCsetInetdActive(gdc, FALSE);
    }
    else if (strcasecmp(token, "MaxLoad")==0)
         GDCsetMaxLoad(gdc, atof(rest));

    else
    if ((strcasecmp(token, "TimeZone")==0) || (strcasecmp(token, "TZ")==0)) {
         if ( (strncmp(rest, "-", 1)==0) && isdigit(*(rest+1)) )
           GDCsetTZ(gdc, -atoi(rest+1));
         else if ( (strncmp(rest, "+", 1)==0) && isdigit(*(rest+1)) )
               GDCsetTZ(gdc, atoi(rest+1));
         else if (isdigit(*rest))
               GDCsetTZ(gdc, atoi(rest));
           else {  /* Mnemonic Timezone */
               GDCsetTZ(gdc,-1);
               GDCsetTimeZone(gdc,rest);
               return(success);
           }
         if (abs(GDCgetTZ(gdc))<100)
           GDCsetTZ(gdc,GDCgetTZ(gdc)*100);
    }

           /*  Now these remaining options are actually VMS specific   */
    else if (strcasecmp(token, "IsGplus")==0) {
           if (strcasecmp(rest, "True")==0)
               GDCsetIsGplus(gdc,TRUE);
           else
               GDCsetIsGplus(gdc, FALSE);
    }
    else if (strcasecmp(token, "IgnoreAll")==0) {
           if (strcasecmp(rest, "True")==0)
               GDCsetIgnoreAll(gdc,TRUE);
           else
               GDCsetIgnoreAll(gdc, FALSE);
    }
    else if (strcasecmp(token, "SortDir")==0) {
           if (strcasecmp(rest, "True")==0)
               GDCsetSortDir(gdc,TRUE);
           else
               GDCsetSortDir(gdc, FALSE);
    }
    else if (strcasecmp(token, "FTPPort")==0) {
           if (strcasecmp(rest, "None")==0)
               GDCsetFTPPort(gdc,0);
           else
               GDCsetFTPPort(gdc, atoi(rest));
    }
    else if (strcasecmp(token, "EXECPort")==0) {
           if (strcasecmp(rest, "None")==0)
               GDCsetEXECPort(gdc,0);
           else
               GDCsetEXECPort(gdc, atoi(rest));
    }
    else if (strcasecmp(token, "SRCHPort")==0) {
           if (strcasecmp(rest, "None")==0)
               GDCsetSRCHPort(gdc,0);
           else
               GDCsetSRCHPort(gdc, atoi(rest));
    }

    else if (strcasecmp(token, "OVERPort")==0) {
           int  threshold;
           char *cp = strchr(rest,',');

           GDCsetOVERSize(gdc,-1);
           if (strcasecmp(rest, "None")==0)
               GDCsetOVERPort(gdc,0);
           else {
               GDCsetOVERPort(gdc, atoi(rest));
               if (cp) {
                   while (*(++cp)==' ');
                   GDCsetOVERSize(gdc, (threshold=atoi(cp))?threshold:-1);
               }
           }
    }
    else if (strcasecmp(token, "SortShell")==0) {
           if (strcasecmp(rest, "True")==0)
               GDCsetSortShell(gdc,TRUE);
           else
               GDCsetSortShell(gdc, FALSE);
    }
    else if (strcasecmp(token, "SortGREP")==0) {
           if (strcasecmp(rest, "True")==0)
               GDCsetSortGREP(gdc,TRUE);
           else
               GDCsetSortGREP(gdc, FALSE);
    }
    else if (strcasecmp(token, "SortCMD1")==0) {
           if (strcasecmp(rest, "True")==0)
               GDCsetSortCMD1(gdc,TRUE);
           else
               GDCsetSortCMD1(gdc, FALSE);
    }
    else
    if (strcasecmp(token, "ReadTimeout")==0)
           GDCsetReadTimeout(gdc, atoi(rest));

    else if (strcasecmp(token, "Console")==0) {
           int  opc;
           int ox;
           char *cp = strchr(rest,'+');

           GDCsetOPCOM(gdc, opc = 0);
           while (rest) {
               if (cp)
                   *cp = '\0';
               while (*rest==' ')
                   rest++;
               ox = strlen(rest) - 1;
               while (*(rest+ox)==' ')
                   *(rest+ox--) = '\0';
               for (ox=0; ox<vms_OPC$M_num; ox++) {
                   if (strcasecmp(rest, vms_OPC$M_TXT[ox])==0)
                       GDCsetOPCOM(gdc, opc |= vms_OPC$M_NM[ox]);
               }
               if (cp) {
                   rest = cp+1;
                   cp = strchr(rest,'+');
               }
               else
                   rest = NULL;
           }
           if (opc==0)
               GDCsetOPCOM(gdc, opc = (OPC$M_NM_CENTRL | OPC$M_NM_NTWORK));
    }

    else if (strcasecmp(token, "Hidden")==0)
         GDCsetHiddenPrefix(gdc, rest);

    else if (strcasecmp(token, "Link")==0)
         GDCsetLinkPrefix(gdc, rest);

    else if (strcasecmp(token, "LookAside")==0)
         GDCsetLookAside(gdc, rest);

    else if (strcasecmp(token, "DName")==0)
         GDCsetDName(gdc, rest);

    else if (strcasecmp(token, "DHead")==0)
         GDCsetDHead(gdc, rest);

    else if (strcasecmp(token, "DFoot")==0)
         GDCsetDFoot(gdc, rest);

    else if (strcasecmp(token, "ScratchDir")==0)
         GDCsetScratchDir(gdc, rest);

    else if (strcasecmp(token, "SupportDir")==0)
         GDCsetSupportDir(gdc, rest);

    else if (strcasecmp(token, "SpawnInit")==0)
         GDCsetSpawnInit(gdc, rest);

    else if (strcasecmp(token, "Restart")==0)
         GDCsetRestart(gdc, rest);

    else if (strcasecmp(token, "LogTag")==0)
           GDCsetLogTag(gdc, rest);

    else if (strcasecmp(token, "Rollover")==0) {
           if (strcasecmp(rest, "Daily")==0)
               GDCsetRollover(gdc,ROLLOVER_DAILY);
           if (strcasecmp(rest, "Monthly")==0)
               GDCsetRollover(gdc,ROLLOVER_MONTHLY);
           if (strcasecmp(rest, "Hourly")==0)
               GDCsetRollover(gdc,ROLLOVER_HOURLY);
           if (strcasecmp(rest, "Annually")==0)
               GDCsetRollover(gdc,ROLLOVER_ANNUALLY);
           if (strcasecmp(rest, "Weekly")==0)
               GDCsetRollover(gdc,ROLLOVER_WEEKLY);
           else
               GDCsetRollover(gdc, ROLLOVER_NEVER);
    }
    else if (strcasecmp(token, "CACHE")==0) {
           if (strncasecmp(rest, "TRUE", 4)==0)
               GDCsetCaching(gdc,TRUE);
           else
               GDCsetCaching(gdc, FALSE);
         }
    else if (strcasecmp(token, "DO_CHROOT")==0) {
           if (strncasecmp(rest, "TRUE", 4)==0)
               GDCsetchroot(gdc,TRUE);
           else
               GDCsetchroot(gdc, FALSE);
         }
#endif
    else
         success = FALSE;

    return(success);
}


void
GDCfromFile(gdc, filename)
 GDCobj *gdc;
 char *filename;
{
    FILE *gdcfile;
    char inputline[512];
    char *cp, *linestart, *token, *restofline;
    boolean success;
#ifdef VMS_SERVER
    int bad_cfg=0;
#endif


    if ((gdcfile = fopen(filename, "r")) == (FILE *) NULL) {
#ifndef VMS_SERVER
         printf("Cannot open file '%s'\n", filename);
         exit(-1);
#else
         VMS$fprintf(stderr,"Cannot open file '%s'\n", filename);
         bad_cfg++;
#endif
    }

    GDCfilePush(gdcfile);

#ifdef VMS_SERVER
   if (!GDCfilenum)        /*  Only record the topmost config file */
       GDCsetConfig(gdc, fgetname(gdcfile, inputline));
#endif

    while (GDCfileGetline(inputline, sizeof(inputline))) {
         int inputlen;
         ZapCRLF(inputline);

         inputlen = strlen(inputline);

         while ((inputlen > 1) && inputline[inputlen-1] == GDCcontinue) {
              inputline[inputlen-1] = '\n';
              if (!GDCfileGetline(inputline + inputlen,
                             sizeof(inputline)-inputlen))
                   break;
              ZapCRLF(inputline+inputlen);
              inputlen += strlen(inputline+inputlen);
         }

         /** Eat whitespace **/
         linestart= inputline;
         for ( ; *linestart != '\0' && *linestart <= ' '; linestart++) ;

         if (*linestart == '#' || *linestart == '\0') /** Ignore comments **/
              continue;

         cp = strchr(linestart, ':');
         if (cp == NULL) {
#ifndef VMS_SERVER
              fprintf(stderr, "Bad line '%s'\n", inputline);
              fprintf(stderr, "Line needs a colon\n");
              exit(-1);
#else
              VMS$fprintf(stderr, "Bad line '%s'\nLine needs a colon\n",
                                                   inputline);
              bad_cfg++;
              continue;
#endif
         }
         *cp = '\0';
         token      = linestart;
         restofline = cp+1;
         while (*restofline == ' ' || *restofline == '\t')
              restofline++;

         success = GDCtokens(gdc, token, restofline);
         if (!success) {
#ifndef VMS_SERVER
              fprintf(stderr, "Bad line '%s'\n", inputline);
              if (strcasecmp(inputline, "ext")==0)
                   fprintf(stderr, "please upgrade your gopherd.conf file\n");
              exit(-1);
#else
              VMS$fprintf(stderr, "Bad line '%s'\n", inputline);
              *cp = ':';
              bad_cfg++;
              continue;
#endif
         }
    }
#ifdef VMS_SERVER
    fclose(gdcfile);
    if (bad_cfg) {
       VMS$fprintf(stderr, "\n\t%d configuration errors detected\n",bad_cfg);
       VMS$fprintf(stderr, "\tplease upgrade your configuration file\n");
                               /* exit(-1) from detached server would just */
       if (gdc->RunFromInetd)  /*  loop; so ignore the errors now, have    */
           gopherd_exit(-1);   /*  the user fix them & restart             */

       VMS$fprintf(stderr,
                   "\tand restart this detached server with the new cfg\n");
    }
#endif
}


void
GDCpushOtherGDC(gdc, dir, conffile)
 GDCobj *gdc;
 char *dir;
 char *conffile;
{
    String *temp;

    temp = STRnew();
    STRset(temp, dir);
    STApush(gdc->other_dirs, temp);

    STRset(temp, conffile);
    STApush(gdc->other_gdcs, temp);

    STRdestroy(temp);
}


void
GDCpushBlockScript(gdc, blockname, scriptname)
 GDCobj *gdc;
 char   *blockname;
 char   *scriptname;
{
    String *temp;

    temp = STRnew();
    STRset(temp, blockname);
    STApush(gdc->BlkScriptBlocks, temp);

    STRset(temp, scriptname);
    STApush(gdc->BlkScripts, temp);

    STRdestroy(temp);
}
/*
* Search for otherdir gdcs
*/

char *
GDCfindOtherGDC(gdc, dir)
 GDCobj *gdc;
 char *dir;
{
    int  i;

    for (i=0; i<STAgetTop(gdc->other_dirs); i++)

         if (strcmp(STAgetText(gdc->other_dirs, i), dir))
              return(STAgetText(gdc->other_gdcs, i));

    return(NULL);
}


boolean
GDCevalDir(gdc, dir)
 GDCobj *gdc;
 char   *dir;
{
    int  i, gdcdirlen;
    char *gdcdir, *conf;
    int dirlen;
    if (dir == NULL)
         return(FALSE);

    dirlen = strlen(dir);

    for (i=0; i<STAgetTop(gdc->other_dirs); i++) {
         gdcdir = STAgetText(gdc->other_dirs, i);
         gdcdirlen = strlen(gdcdir);

/*        if (gdcdirlen <= dirlen && strncmp(dir, gdcdir, gdcdirlen)==0) {*/
         if (re_comp(gdcdir))
              return(TRUE);
         if (re_exec(dir)) {
              conf = STAgetText(gdc->other_gdcs, i);
              GDCfromFile(gdc, conf);
              Debug("Loading up aux gopherd.conf file %s", conf);
              Debug("For item %s\n", gdcdir);
         }
    }
    return(TRUE);
}

#ifndef NO_AUTHENTICATION
AccessResult
GDCCanSearch(gdc, hostname, ipnum, sessions)
 GDCobj *gdc;
 char   *hostname, *ipnum;
 int     sessions;
{
    AccessResult test;

    Debug("Testing %s/", hostname);
    Debug("%s for searching\n", ipnum);

    if (gdc->Securityon == FALSE)
         return(SITE_OK);

    test = SiteAccess(gdc->Sites, hostname, ipnum, ACC_SEARCH, sessions);

    return(test);
}



AccessResult
GDCCanRead(gdc, hostname, ipnum, sessions)
 GDCobj *gdc;
 char   *hostname, *ipnum;
 int     sessions;
{
    AccessResult test;

    Debug("Testing %s/", hostname);
    Debug("%s for reading\n", ipnum);

    if (gdc->Securityon == FALSE)
         return(SITE_OK);

    test = SiteAccess(gdc->Sites, hostname, ipnum, ACC_READ, sessions);

    return(test);
}


AccessResult
GDCCanBrowse(gdc, hostname, ipnum, sessions)
 GDCobj *gdc;
 char   *hostname, *ipnum;
 int    sessions;
{
    AccessResult test;

    Debug("Testing %s/", hostname);
    Debug("%s for browsing\n", ipnum);

    if (gdc->Securityon == FALSE)
         return(SITE_OK);


    test = SiteAccess(gdc->Sites, hostname, ipnum, ACC_BROWSE, sessions);

    return(test);

}


AccessResult
GDCCanFTP(gdc, hostname, ipnum, sessions)
 GDCobj *gdc;
 char   *hostname, *ipnum;
 int     sessions;
{
    AccessResult test;

    Debug("Testing %s/", hostname);
    Debug("%s for ftping\n", ipnum);

    if (gdc->Securityon == FALSE)
         return(SITE_OK);

    test = SiteAccess(gdc->Sites, hostname, ipnum, ACC_FTP, sessions);

    return(test);

}

#ifdef VMS_SERVER

AccessResult
GDCCanEXEC(gdc, hostname, ipnum, sessions)
 GDCobj *gdc;
 char   *hostname, *ipnum;
 int     sessions;
{
    AccessResult test;

    Debug("Testing %s/", hostname);
    Debug("%s for exec\n", ipnum);

    if (gdc->Securityon == FALSE)
         return(SITE_OK);

    test = SiteAccess(gdc->Sites, hostname, ipnum, ACC_EXEC, sessions);

    return(test);

}

AccessResult
GDCcanAccess(gdc, hostname, ipnum, sessions, access)
 GDCobj *gdc;
 char *hostname, *ipnum;
 int sessions, access;
{

    if (gdc->Securityon == FALSE)
         return(SITE_OK);

        switch(access) {
    case ACC_READ:   return(GDCCanRead(gdc, hostname, ipnum, sessions));
    case ACC_BROWSE: return(GDCCanBrowse(gdc, hostname, ipnum, sessions));
    case ACC_SEARCH: return(GDCCanSearch(gdc, hostname, ipnum, sessions));
    case ACC_FTP:    return(GDCCanFTP(gdc, hostname, ipnum, sessions));
    case ACC_EXEC:   return(GDCCanEXEC(gdc, hostname, ipnum, sessions));
    default:         return(SITE_OK);
        }
}
#endif

char *
GDCauthType(gdc, item)
 GDCobj *gdc;
 char         *item;
{
    if (item == NULL)
         return(NULL);

    return(AUTHITEMSfindType(gdc->Authitems, item));
}

AUTHresult
GDCvalidate(gdc, type, username, password, hostname, hostip)
 GDCobj *gdc;
 char *type;
 char *username, *password, *hostname, *hostip;
{
    if (type == NULL)
         return(AUTHRES_OK);   /* If no auth method then it's okay. */

    return(AUTHAvalidate(gdc->Authroutines,type, username, password,
                         hostname, hostip));
}

/*
* Find the ask block for an authenticated item..
*/

char **
GDCauthAsk(gdc, path)
 GDCobj *gdc;
 char   *path;
{
    AUTH *auth;

    Debug("Looking for %s\n", path);

    auth = AUTHITEMSfindAUTH(gdc->Authitems, gdc->Authroutines, path);

    if (auth == NULL) {
         Debug("Couldn't find %s\n", path);
         return(NULL);
    }
    return(AUTHgetAskFcn(auth)(path, "", ""));
}

#endif

/** Is this file ignored? **/
boolean
GDCignore(gdc, fname)
 GDCobj *gdc;
 char *fname;
{
    Extobj *ext;

    ext = EXnew();

    if (EXAsearch(gdc->Extensions, ext, fname, EXT_IGNORE)) {
         EXdestroy(ext);
         return(TRUE);
    } else {
         EXdestroy(ext);
         return(FALSE);
    }
}


boolean
GDCViewExtension(gdc, fext, extin)
 GDCobj *gdc;
 char *fext;
 Extobj **extin;
{
    Extobj *ext;

    ext = EXnew();

    if (EXAsearch(gdc->Extensions, ext, fext, EXT_VIEW)) {
         *extin = ext;
         return(TRUE);
    }
    else {
         EXdestroy(ext);
         return(FALSE);
    }

}

boolean
GDCBlockExtension(gdc, fext, ext)
 GDCobj *gdc;
 char *fext;
 Extobj *ext;
{

    if (EXAsearch(gdc->Extensions, ext, fext, EXT_BLOCK)) {
         return(TRUE);
    }
    else
         return(FALSE);
}



void
GDCaddFileSep(gdc, expr)
 GDCobj *gdc;
 char   *expr;
{
    String *temp;

    temp = STRnew();
    if (temp == NULL)
         return;

    STRset(temp, expr);
    STApush(gdc->FileSeparators, temp);
    STRdestroy(temp);
}

static boolean
GDCisBlank(string)
 char *string;
{
    if (string == NULL ||
        strncmp(string, "blank", 5) == 0)
         return TRUE;
    else
         return(FALSE);
}

void
GDCintegrityCheck(gdc)
 GDCobj *gdc;
{
#ifdef VMS_SERVER
#ifdef fprintf
#define hold_fprintf fprintf
#undef fprintf
#endif
#define fprintf VMS$fprintf
#endif
    if (GDCisBlank(GDCgetSite(gdc)))
         fprintf(stderr, "** Please set the Site: line in your configuration file!\n");

    if (GDCisBlank(GDCgetOrg(gdc)))
         fprintf(stderr, "** Please set the Org: line in your configuration file!\n");

    if (GDCisBlank(GDCgetLoc(gdc)))
         fprintf(stderr, "** Please set the Loc: line in your configuration file!\n");

    if (GDCisBlank(GDCgetAbstract(gdc)))
         fprintf(stderr, "** Please set the Abstract: line in your configuration file!\n");

    if (GDCisBlank(GDCgetAdmin(gdc)))
         fprintf(stderr, "** Please set the Admin: line in your configuration file!\n");

    if (GDCisBlank(GDCgetAdminEmail(gdc)))
         fprintf(stderr, "** Please set the AdminEmail: line in configuration file!\n");
#ifdef VMS_SERVER
#ifdef hold_fprintf
#define fprintf hold_fprintf
#undef hold_fprintf
#endif
#endif

}


#ifdef VMS_SERVER
           /** VMS version supports logfile rollover, so we can't just pick
               up a string and return it like the UNIX macro does -- we need
               real code which builds the current timestamped value and
               returns it. **/
char
*GDCgetLogfile(gdc)
   GDCobj *gdc;
{
   static
       char    rollover[256];
   char    insert[11];
   time_t  Now;
   char    *mx;
   int     i;
   char    cdate[26];
   extern
     char  log_alq[10];
   extern
     char  log_deq[10];
   static
       char *DaysofWeek="SunMonTueWedThuFriSat";
   static
       char *Months = "JanFebMarAprMayJunJulAugSepOctNovDec";
#define DOW     (cdate+0)
#define MMM     (cdate+4)
#define DD      (cdate+8)
#define HH      (cdate+11)
#define YYYY    (cdate+20)

   if (!gdc)
       return(NULL);
   if (!(mx=STRget(gdc->Logfile)))
       return(NULL);
   if (!strlen(mx))
       return(NULL);
   time(&Now);
   strcpy(cdate,(char *)ctime(&Now));
   cdate[3] = cdate[7]= cdate[10]= cdate[13] = cdate[24]= '\0';
   if (GDCgetRollover(gdc)==ROLLOVER_WEEKLY) {
       mx=strstr(DaysofWeek,DOW);
       if (i = ((mx - DaysofWeek)/3)) {
           Now -= (i * (24 * 60 * 60));
           strcpy(cdate,(char *)ctime(&Now));
           cdate[3] = cdate[7]= cdate[10]= cdate[13] = cdate[24]= '\0';
       }
   }
   if (cdate[8]==' ')
       cdate[8] = '0';
   mx=strstr(Months,MMM);
   i = mx - Months;
   sprintf(insert,"%s%02d%s%s",YYYY,(i/3)+1,DD,HH);
   switch(GDCgetRollover(gdc))
   {
   case ROLLOVER_ANNUALLY:     insert[4] = '\0';   i = ALQ_ANNUAL; break;
   case ROLLOVER_MONTHLY:      insert[6] = '\0';   i = ALQ_MONTH;  break;
   case ROLLOVER_WEEKLY:       insert[8] = '\0';   i = ALQ_WEEK;   break;
   case ROLLOVER_DAILY:        insert[8] = '\0';   i = ALQ_DAY;    break;
   case ROLLOVER_HOURLY:       insert[10]= '\0';   i = ALQ_HOUR;   break;
   case ROLLOVER_NEVER:
   default:                    insert[0] = '\0';   i = ALQ_NEVER;
   }
   sprintf(log_alq,"alq=%d",i);
   sprintf(log_deq,"deq=%d",i);
   strcpy(rollover,STRget(gdc->Logfile));
   strcat(rollover,insert);
   return(rollover);
}

void
GDCfromLink(gdc, fio, sep)
 GDCobj *gdc;
 FileIO *fio;
 char sep;
{
    char inputline[512];
    char *cp, *linestart, *token, *restofline;
    int inputlen;
    boolean success;
    int position;

    position = FIOtell(fio);
    while (inputlen = FIOreadlinezap(fio, inputline, sizeof(inputline))) {
         while ((inputlen > 1) && inputline[inputlen-1] == GDCcontinue) {
              inputline[inputlen-1] = '\n';
              if (inputlen += FIOreadlinezap(fio, inputline + inputlen,
                                         sizeof(inputline)-inputlen))
                   break;
         }
         ZapCRLF(inputline);
         inputlen = strlen(inputline);

         /** Eat whitespace **/
         linestart= inputline;
         for ( ; *linestart != '\0' && *linestart <= ' '; linestart++) ;

         if (*linestart == '#' || *linestart == '\0') /** Ignore comments **/
              continue;

         if (!(cp = strchr(linestart, sep))) {
             FIOseek(fio, position);   /*  Back up to beginning of this line */
             return;
         }
         *cp = '\0';
         token      = linestart;
         restofline = cp+1;
         while (*restofline == ' ' || *restofline == '\t')
              restofline++;

         success = GDCtokens(gdc, token, restofline);
         if (!success) {
             FIOseek(fio, position);   /*  Back up to beginning of this line */
             return;
         }
         position = FIOtell(fio);
    }
}

GDCobj *
GDCcpy(orig)
 GDCobj *orig;
{
    GDCobj *dest = GDCnew();

    EXAcpy(dest->Extensions, orig->Extensions);

#ifndef NO_AUTHENTICATION
    SiteArrayCopy(dest->Sites, orig->Sites);
    dest->Securityon   = orig->Securityon;

    AUTHAcpy(dest->Authroutines, orig->Authroutines);
    AUTHITEMScpy(dest->Authitems, orig->Authitems);
    STRcpy(dest->Serverpw, orig->Serverpw);
#endif

    dest->RunFromInetd = orig->RunFromInetd;
    dest->Caching      = orig->Caching;
    dest->Cachetime    = orig->Cachetime;

    STRcpy(dest->Logfile, orig->Logfile);
    STRcpy(dest->Errorfile, orig->Errorfile);
    STRcpy(dest->DataDir, orig->DataDir);
    STRcpy(dest->Hostname, orig->Hostname);
    dest->Port         = orig->Port;
    dest->chroot       = orig->chroot;
    dest->Defaccess    = orig->Defaccess;
    STRcpy(dest->BummerMsg, orig->BummerMsg);

    STRcpy(dest->Admin, orig->Admin);
    STRcpy(dest->AdminEmail, orig->AdminEmail);
    STRcpy(dest->Abstract, orig->Abstract);

    STRcpy(dest->Site, orig->Site);
    STRcpy(dest->Org, orig->Org);
    STRcpy(dest->Geog, orig->Geog);
    STRcpy(dest->Loc, orig->Loc);
    STRcpy(dest->Lang, orig->Lang);
    dest->TZ           = orig->TZ;
    dest->VeronicaIndex = orig->VeronicaIndex;

    STRcpy(dest->Tixfile, orig->Tixfile);

    STAcpy(dest->other_dirs, orig->other_dirs);
    STAcpy(dest->other_gdcs, orig->other_gdcs);

    STAcpy(dest->FileSeparators, orig->FileSeparators);

    STAcpy(dest->BlkScriptBlocks, orig->BlkScriptBlocks);
    STAcpy(dest->BlkScripts, orig->BlkScripts);

    dest->MaxConnections  = orig->MaxConnections;

    STRcpy(dest->TimeZoneText, orig->TimeZoneText);
    dest->MaxLoad      = orig->MaxLoad;

    /* New VMS configuration options */
    dest->IsGplus      = orig->IsGplus;
    dest->IgnoreAll    = orig->IgnoreAll;
    dest->SortDir      = orig->SortDir;
    dest->FTPPort      = orig->FTPPort;
    dest->EXECPort     = orig->EXECPort;
    dest->SRCHPort     = orig->SRCHPort;
    dest->OVERPort     = orig->OVERPort;
    dest->OVERSize     = orig->OVERSize;
    dest->SortShell    = orig->SortShell;
    dest->SortGREP     = orig->SortGREP;
    dest->SortCMD1     = orig->SortCMD1;
    dest->ReadTimeout  = orig->ReadTimeout;
    dest->OPCOM        = orig->OPCOM;
    STRcpy(dest->Hidden_Prefix, orig->Hidden_Prefix);
    STRcpy(dest->Link_Prefix, orig->Link_Prefix);
    STRcpy(dest->LookAside, orig->LookAside);
    STRcpy(dest->DName, orig->DName);
    STRcpy(dest->DHead, orig->DHead);
    STRcpy(dest->DFoot, orig->DFoot);
    STRcpy(dest->Scratch_Dir, orig->Scratch_Dir);
    STRcpy(dest->Support_Dir, orig->Support_Dir);
    STRcpy(dest->Spawn_Init, orig->Spawn_Init);
    STRcpy(dest->RestartLnm, orig->RestartLnm);
    STRcpy(dest->ConfigFile, orig->ConfigFile);
    STRcpy(dest->LogFileTag, orig->LogFileTag);
    dest->rollover     = orig->rollover;

    return(dest);
}
#endif