ftp.nice.ch/pub/next/unix/network/www/httpd.1.5-export.NIHS.bs.gnutar.gz#/httpd_1.5-export/src/http_config.c

This is http_config.c in view mode; [Download] [Up]

/************************************************************************
 * NCSA HTTPd Server
 * Software Development Group
 * National Center for Supercomputing Applications
 * University of Illinois at Urbana-Champaign
 * 605 E. Springfield, Champaign, IL 61820
 * httpd@ncsa.uiuc.edu
 *
 * Copyright  (C)  1995, Board of Trustees of the University of Illinois
 *
 ************************************************************************
 *
 * http_config.c: auxillary functions for reading httpd's config file
 * and converting filenames into a namespace
 *
 * Based on NCSA HTTPd 1.3 by Rob McCool 
 *
 * 10/28/94  cvarela
 *      Added config options AgentLog and RefererLog for extra log info
 *
 * 02/19/95  blong
 *	Added config options MaxServers and StartServers for configuration
 *	defined children
 *
 * 03/21/95 cvarela
 *      Added RefererIgnore to ignore certain URIs when logging referers
 *
 * 06/01/95 blong
 *	Added patch by Conrad Damon (damon@netserver.stanford.edu) that
 *	avoids a cfg_getline loop if a user sets a directory as a password
 *	file.
 *
 * 09/02/95 blong
 *	Added patch by Kevin Ruddy (kevin.ruddy@powerdog.com) that should
 *	allow systems which don't recognize a numeric address to gethostbyname
 *	to use a numeric IP in the BindAddress and VirtualHost fields
 *
 * 09/10/95 mshapiro
 *      Added includes for <netinet/in.h> and <arpa/inet.h>
 * 09/18/95 mshapiro
 *      Added LogDirGroupWriteOk, LogDirPublicWriteOk
 *      directives
 * 10/06/95 blong
 *	Added patch by Elf Sternberg (elf@aaden.spry.com) to configure
 *	the process name which SETPROCTITLE uses
 * 10/06/95 blong
 *	Added patch by Nathan Neulinger (nneul@umr.edu) to have separate
 *	security permissions for Redirect in .htaccess files
 */


#include "config.h"
#include "portability.h"

#include <stdio.h>
#ifndef NO_STDLIB_H 
# include <stdlib.h>
#endif /* NO_STDLIB_H */
#ifndef NO_MALLOC_H
# ifdef NEED_SYS_MALLOC_H
#  include <sys/malloc.h>
# else
#  include <malloc.h>
# endif /* NEED_SYS_MALLOC_H */
#endif /* NO_MALLOC_H */
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netdb.h>
#include "constants.h"
#include "fdwrap.h"
#include "http_config.h"
#include "host_config.h"
#include "http_mime.h"
#include "http_access.h"
#include "http_alias.h"
#include "http_log.h"
#include "http_dir.h"
#include "util.h"
#include <netinet/in.h>
#include <arpa/inet.h>


/* Server config globals */
int standalone;
int port;
uid_t user_id;
gid_t group_id;
char server_confname[MAX_STRING_LEN];
int timeout;  
int do_rfc931;
char server_root[MAX_STRING_LEN];
char core_dir[MAX_STRING_LEN];
char pid_fname[MAX_STRING_LEN];
#ifdef SETPROCTITLE
char process_name[MAX_STRING_LEN];
#endif /* SETPROCTITLE */
char access_confname[MAX_STRING_LEN];
char types_confname[MAX_STRING_LEN];
char local_default_type[MAX_STRING_LEN];
char local_default_icon[MAX_STRING_LEN];
int  log_directory_group_write_ok = 0;
int  log_directory_other_write_ok = 0;

/* Access Globals*/
int num_sec;
/* number of security directories in access config file */
int num_sec_config;
security_data sec[MAX_SECURITY];

#ifndef NO_PASS
int max_servers;
int start_servers;
#endif /* NO_PASS */

static int ConfigErrorCritical=TRUE;

void config_error(char *error_msg, char *filename, int lineno, 
			 FILE *errors) 
{
  fprintf(errors,"Syntax error on line %d of %s:\n", lineno, filename);
  fprintf(errors,"%s.\n",error_msg);
  if (ConfigErrorCritical) exit(1);
}

void config_warn(char *warn_msg, char *filename, int lineno, 
		 FILE *errors) 
{
  fprintf(errors,"Syntax warning on line %d of %s:\n", lineno, filename);
  fprintf(errors,"%s.\n",warn_msg);
}

void set_defaults(per_host *host, FILE *errors) 
{
  char tmp[MAX_STRING_LEN];

  standalone = 1;
  port = DEFAULT_PORT;
  user_id = uname2id(DEFAULT_USER);
  group_id = gname2id(DEFAULT_GROUP);
  
#ifdef SETPROCTITLE
  strcpy(process_name, "HTTPd");
#endif /* SETPROCTITLE */

#ifndef NO_PASS
  max_servers = DEFAULT_MAX_DAEMON;
  start_servers = DEFAULT_START_DAEMON;
#endif /* NO_PASS */

  /* ServerRoot set in httpd.c */

  host->address_info.s_addr = htonl(INADDR_ANY);

  set_host_conf_value(host,PH_HTTPD_CONF,HC_LOG_TYPE);
  host->log_opts = LOG_NONE;

  make_full_path(server_root,DEFAULT_ERRORLOG,tmp);
  set_host_conf(host,PH_HTTPD_CONF,HC_ERROR_FNAME,tmp);

  make_full_path(server_root,DEFAULT_XFERLOG,tmp);
  set_host_conf(host,PH_HTTPD_CONF,HC_XFER_FNAME,tmp);
  strcpy(core_dir,server_root);
  
  make_full_path(server_root,DEFAULT_AGENTLOG,tmp);
  set_host_conf(host,PH_HTTPD_CONF,HC_AGENT_FNAME,tmp);
    
  make_full_path(server_root,DEFAULT_REFERERLOG,tmp);
  set_host_conf(host,PH_HTTPD_CONF,HC_REFERER_FNAME,tmp);

  make_full_path(server_root,RESOURCE_CONFIG_FILE,tmp);
  set_host_conf(host,PH_HTTPD_CONF,HC_SRM_CONFNAME,tmp);

  set_host_conf(host,PH_HTTPD_CONF,HC_REFERER_IGNORE,DEFAULT_REFERERIGNORE);
  set_host_conf(host,PH_HTTPD_CONF,HC_SERVER_ADMIN,DEFAULT_ADMIN);

  make_full_path(server_root,ACCESS_CONFIG_FILE,access_confname);
  make_full_path(server_root,TYPES_CONFIG_FILE,types_confname);
  make_full_path(server_root,DEFAULT_PIDLOG,pid_fname);

  tmp[0] = '\0';
  set_host_conf(host,PH_HTTPD_CONF,HC_ANNOT_SERVER,tmp);
  host->dns_mode = DNS_STD;

  /* initialize keep-alive data to defaults */
  keep_alive.bAllowKeepAlive = DEFAULT_ALLOW_KEEPALIVE;
  keep_alive.nMaxRequests = DEFAULT_KEEPALIVE_MAXREQUESTS;
  keep_alive.nTimeOut = DEFAULT_KEEPALIVE_TIMEOUT;

  timeout = DEFAULT_TIMEOUT;
  do_rfc931 = DEFAULT_RFC931;

  /* Necessary with new configuration stuff? */
  
  /* default resource config stuff */
  set_host_conf(host,PH_SRM_CONF,SRM_USER_DIR,DEFAULT_USER_DIR);
  set_host_conf(host,PH_SRM_CONF,SRM_INDEX_NAMES,DEFAULT_INDEX_NAMES);
  set_host_conf(host,PH_SRM_CONF,SRM_ACCESS_NAME,DEFAULT_ACCESS_FNAME);
  set_host_conf(host,PH_SRM_CONF,SRM_DOCUMENT_ROOT,DOCUMENT_LOCATION);
  set_host_conf(host,PH_SRM_CONF,SRM_DEFAULT_TYPE,DEFAULT_TYPE);
  set_host_conf(host,PH_SRM_CONF,SRM_DEFAULT_ICON,"");
}



void process_server_config(per_host *host, FILE *cfg, FILE *errors, 
			   int virtual) 
{
  char l[MAX_STRING_LEN],w[MAX_STRING_LEN];
  char tmp[MAX_STRING_LEN];
  static int n;
  int doneSRM = FALSE;
  
  if (!virtual) n=0;
  
  /* Parse server config file. Remind me to learn yacc. */
  while(!(cfg_getline(l,MAX_STRING_LEN,cfg))) {
    ++n;
    if((l[0] != '#') && (l[0] != '\0')) {
      cfg_getword(w,l);
      
      if(!strcasecmp(w,"ServerType") && !virtual) {
	if(!strcasecmp(l,"inetd")) standalone=0;
	else if(!strcasecmp(l,"standalone")) standalone=1;
	else config_error("ServerType is either inetd or standalone",
		    server_confname,n,errors);
      }
      else if(!strcasecmp(w,"LogDirGroupWriteOk")) {
	if(virtual) {
	  config_warn("LogDirGroupWriteOk directive in <VirtualHost> ignored",
		      server_confname,n,errors);
	}
	else {
	    log_directory_group_write_ok = 1;
	}
      }
      else if(!strcasecmp(w,"LogDirOtherWriteOk")) {
	if(virtual) {
	  config_warn("LogDirOtherWriteOk directive in <VirtualHost> ignored",
		      server_confname,n,errors);
	}
	else {
	    log_directory_other_write_ok = 1;
	}
      }
      else if(!strcasecmp(w,"CoreDirectory")) {
        cfg_getword(w,l);
	if (w[0] != '/')
	  make_full_path(server_root,w,l);
         else strcpy(l,w);
	if(!is_directory(l)) {
	  sprintf(tmp,"%s is not a valid directory",l);
	  config_error(tmp,server_confname,n,errors);
	}
	strcpy(core_dir,l);
      }
      else if(!strcasecmp(w,"Port") && !virtual) {
	cfg_getword(w,l);
	port = atoi(w);
      }
      else if(!strcasecmp(w,"BindAddress") && !virtual) {
	struct hostent *hep;
	unsigned long ina;
	cfg_getword(w,l);
	if (!strcmp(w,"*")) {
	  host->address_info.s_addr = htonl(INADDR_ANY);
	} else {
	  hep = gethostbyname(w);
	  if (hep && hep->h_addrtype == AF_INET &&
	      hep->h_addr_list[0] && !hep->h_addr_list[1])
	    {
	      memcpy(&(host->address_info),hep->h_addr_list[0],
		     sizeof(struct in_addr));
	    } else if (!hep && (ina = inet_addr(w)) != -1) {
	      host->address_info.s_addr = ina;
	    } else {
	      config_error("BindAddress must be * or a numeric IP or a name that maps to exactly one address",server_confname,n,errors);
	    }
	}
      }
#ifdef SETPROCTITLE
      else if(!strcasecmp(w,"ProcessName") && !virtual) {
	cfg_getword(w,l);
	strcpy(process_name,w);
      }
#endif /* SETPROCTITLE */
      else if(!strcasecmp(w,"User") && !virtual) {
	cfg_getword(w,l);
	user_id = uname2id(w);
      } 
      else if(!strcasecmp(w,"Group")) {
	cfg_getword(w,l);
	group_id = gname2id(w);
      }
      else if(!strcasecmp(w,"ServerAdmin")) {
	cfg_getword(w,l);
	set_host_conf(host,PH_HTTPD_CONF,HC_SERVER_ADMIN,w);
      }
      /* SSG-4/4/95 read annotation server directive */
      else if(!strcasecmp(w,"Annotation-Server")) {
	cfg_getword(w,l);
	set_host_conf(host,PH_HTTPD_CONF,HC_ANNOT_SERVER,w);
      } 
      else if(!strcasecmp(w,"ServerName")) {
	cfg_getword(w,l);
	set_host_conf(host,PH_HTTPD_CONF,HC_SERVER_HOSTNAME,w);
      }
      else if(!strcasecmp(w,"ServerRoot")) {
	cfg_getword(w,l);
	if(!is_directory(w)) {
	  sprintf(tmp,"%s is not a valid directory",w);
	  config_error(tmp,server_confname,n,errors);
	}
	strcpy(server_root,w);
	strcpy(core_dir,w);
	make_full_path(server_root,DEFAULT_ERRORLOG,tmp);
	set_host_conf(host,PH_HTTPD_CONF,HC_ERROR_FNAME,tmp);
	make_full_path(server_root,DEFAULT_XFERLOG,tmp);
	set_host_conf(host,PH_HTTPD_CONF,HC_XFER_FNAME,tmp);
	make_full_path(server_root,DEFAULT_AGENTLOG,tmp);
	set_host_conf(host,PH_HTTPD_CONF,HC_AGENT_FNAME,tmp);
	make_full_path(server_root,DEFAULT_REFERERLOG,tmp);
	set_host_conf(host,PH_HTTPD_CONF,HC_REFERER_FNAME,tmp);
	make_full_path(server_root,RESOURCE_CONFIG_FILE,tmp);
	set_host_conf(host,PH_HTTPD_CONF,HC_SRM_CONFNAME,tmp);

	if (!virtual) {
	  make_full_path(server_root,DEFAULT_PIDLOG,pid_fname);
	  make_full_path(server_root,ACCESS_CONFIG_FILE,access_confname);
	  make_full_path(server_root,TYPES_CONFIG_FILE,types_confname);
	}
      }
      else if(!strcasecmp(w,"ErrorLog")) {
	cfg_getword(w,l);
	if(w[0] != '/')
	  make_full_path(server_root,w,tmp);
	else strcpy(tmp,w);
	set_host_conf(host,PH_HTTPD_CONF,HC_ERROR_FNAME,tmp);
      } 
      else if(!strcasecmp(w,"TransferLog")) {
	cfg_getword(w,l);
	if(w[0] != '/')
	  make_full_path(server_root,w,tmp);
	else strcpy(tmp,w);
	set_host_conf(host,PH_HTTPD_CONF,HC_XFER_FNAME,tmp);
      }
      else if(!strcasecmp(w,"AgentLog")) {
	cfg_getword(w,l);
	if(w[0] != '/')
	  make_full_path(server_root,w,tmp);
	else strcpy(tmp,w);
	set_host_conf(host,PH_HTTPD_CONF,HC_AGENT_FNAME,tmp);
      }
      else if(!strcasecmp(w,"RefererLog")) {
	cfg_getword(w,l);
	if(w[0] != '/')
	  make_full_path(server_root,w,tmp);
	else strcpy(tmp,w);
	set_host_conf(host,PH_HTTPD_CONF,HC_REFERER_FNAME,tmp);
      }
      else if(!strcasecmp(w,"RefererIgnore")) {
	  /*cfg_getword(w,l);*/
	  sprintf (tmp, " %s ", l);
	  /*strcpy(tmp,w);*/
	  set_host_conf(host,PH_HTTPD_CONF,HC_REFERER_IGNORE,tmp);
      }
      else if(!strcasecmp(w,"PidFile")) {
	cfg_getword(w,l);
	if(w[0] != '/')
	  make_full_path(server_root,w,pid_fname);
	else strcpy(pid_fname,w);
      }
      else if(!strcasecmp(w,"DNSMode")) {
        if(!strcasecmp(l,"Maximum")) host->dns_mode = DNS_MAX;
	else if(!strcasecmp(l,"Standard")) host->dns_mode = DNS_STD;
        else if(!strcasecmp(l,"Minimum")) host->dns_mode = DNS_MIN;
	else if(!strcasecmp(l,"None")) host->dns_mode = DNS_NONE;
        else 
	  config_error("DNSMode is either Maximum, Standard, Minimum, or None",
                    server_confname,n,errors);
      }
      else if(!strcasecmp(w,"LogOptions")) {
	while (l[0]) {
  	  cfg_getword(w,l);
	  if (!strcasecmp(w,"None")) {
	    set_host_conf_value(host,PH_HTTPD_CONF,HC_LOG_TYPE);
	    host->log_opts &= LOG_NONE;
	  }
	  if (!strcasecmp(w,"Combined")) {
	    set_host_conf_value(host,PH_HTTPD_CONF,HC_LOG_TYPE);
	    host->log_opts |= LOG_COMBINED;
          } else if (!strcasecmp(w,"Separate")) {
	    set_host_conf_value(host,PH_HTTPD_CONF,HC_LOG_TYPE);
	    host->log_opts &= LOG_SEPARATE;
          } else if (!strcasecmp(w,"ServerName")) {
            set_host_conf_value(host,PH_HTTPD_CONF,HC_LOG_TYPE);
            host->log_opts |= LOG_SERVERNAME;
	  } else if (!strcasecmp(w,"Date")) {
	    set_host_conf_value(host,PH_HTTPD_CONF,HC_LOG_TYPE);
	    host->log_opts |= LOG_DATE;
	  } else {
	    config_error("Valid LogOptions are Combined or Separate, ServerName",
			  server_confname,n,errors);	
          }
	}
      }
      else if(!strcasecmp(w,"AccessConfig") && !virtual) {
	cfg_getword(w,l);
	if(w[0] != '/')
	  make_full_path(server_root,w,access_confname);
	else strcpy(access_confname,w);
      }
      else if(!strcasecmp(w,"ResourceConfig")) {
	cfg_getword(w,l);
	if(w[0] != '/')
	  make_full_path(server_root,w,tmp);
	else strcpy(tmp,w);
	set_host_conf(host,PH_HTTPD_CONF,HC_SRM_CONFNAME,tmp);
	if (!virtual) doneSRM = TRUE;
	process_resource_config(host,NULL,errors,virtual);
      }
      else if(!strcasecmp(w,"TypesConfig")) {
	cfg_getword(w,l);
	if(w[0] != '/')
	  make_full_path(server_root,w,types_confname);
	else strcpy(types_confname,w);
      }
      else if(!strcasecmp(w,"DocumentRoot")) {
        cfg_getword(w,l);
        if(!is_directory(w)) {
          sprintf(tmp,"%s is not a valid directory",w);
          config_error(tmp,server_confname,n,errors);
        }
        /* strip ending / for backward compatibility */
        if ((strlen(w) > 1) && w[strlen(w) - 1] == '/') w[strlen(w) - 1] = '\0';
        set_host_conf(host,PH_SRM_CONF,SRM_DOCUMENT_ROOT,w);
      }
      else if(!strcasecmp(w,"Timeout"))
	timeout = atoi(l);
      else if(!strcasecmp(w,"IdentityCheck")) {
	cfg_getword(w,l);
	if(!strcasecmp(w,"on"))
	  do_rfc931 = TRUE;
	else if(!strcasecmp(w,"off"))
	  do_rfc931 = FALSE;
	else {
	  config_warn("IdentityCheck must be on or off",
		      server_confname,n,errors);
	}
      }
      else if(!strcasecmp(w,"KeepAlive")) {
	cfg_getword(w,l);
	if(!strcasecmp(w,"on"))
	  keep_alive.bAllowKeepAlive = 1;
	else if(!strcasecmp(w,"off"))
	  keep_alive.bAllowKeepAlive = 0;
	else {
	  config_warn("KeepAlive must be on or off",
		      server_confname,n,errors);
	}
      }
      else if(!strcasecmp(w,"KeepAliveTimeout")) {
	cfg_getword(w,l);
	keep_alive.nTimeOut = atoi(w);
      }
      else if(!strcasecmp(w,"MaxKeepAliveRequests")) {
	cfg_getword(w,l);
	keep_alive.nMaxRequests = atoi(w);
      }
      else if(!strcasecmp(w,"MaxServers")) {
#ifndef NO_PASS
	max_servers = atoi(l);
#else
	config_warn("MaxServers unsupported with NO_PASS compile",
		    server_confname,n,errors);
#endif /* NO_PASS */
      }
      else if(!strcasecmp(w,"StartServers")) {
#ifndef NO_PASS
	start_servers = atoi(l);
#else		
	config_warn("StartServers unsupported with NO_PASS compile",
		    server_confname,n,errors);
#endif /* NO_PASS */
      }
#ifdef DIGEST_AUTH
      else if(!strcasecmp(w,"AssumeDigestSupport")) {
	cfg_getword(w,l);
	if(!strcmp(w,"on"))
	  assume_digest_support = TRUE;
	else
	  assume_digest_support = FALSE;
      }
#endif /* DIGEST_AUTH */
      else if(((!strcasecmp(w,"<VirtualHost")) || (!strcasecmp(w,"<Host")))
	      && !virtual) {
	struct hostent *hep;
	unsigned long ina;
	per_host *newHost;
	int virtualhost = FALSE;
	char name[MAX_STRING_LEN];
	
	if (!strcasecmp(w,"<VirtualHost")) virtualhost = TRUE;

	if (!doneSRM) {
	  process_resource_config(host,NULL,errors,FALSE);
	  doneSRM = TRUE;
	}
	newHost = create_host_conf(host,virtualhost);
	getword(w,l,'>');
	getword(name,w,' ');
	if (!strcasecmp(w,"Optional")) {
	  ConfigErrorCritical = FALSE;
        } else if (!strcasecmp(w,"Required")) {
	  ConfigErrorCritical = TRUE;
   	}
	hep = gethostbyname(name);
	if (hep && hep->h_addrtype == AF_INET &&
	    hep->h_addr_list[0] && !hep->h_addr_list[1])
	{
	    memcpy(&newHost->address_info, hep->h_addr_list[0],
		   sizeof(struct in_addr));
        } else if (!hep && (ina = inet_addr(name)) != -1) {
	    newHost->address_info.s_addr = ina;
	} else {
	    config_error("Argument for VirtualHost must be a numeric IP address, or a name that maps to exactly one address",server_confname,n,errors);
	}
	if (!virtualhost) newHost->called_hostname = strdup(name);
	process_server_config(newHost,cfg,errors,TRUE);
      }
      else if(((!strcasecmp(w,"</VirtualHost>")) || (!strcasecmp(w,"</Host>")))
               && virtual) {
	ConfigErrorCritical = 1;
	return;
     }
     else if(!strcasecmp(w,"<SRMOptions>")) {
       process_resource_config(host,cfg,errors,virtual);
     }
     else {
	sprintf(tmp,"Unknown keyword %s",w);
	config_error(tmp,server_confname,n,errors);
      }
    }
  }
  if (!doneSRM) process_resource_config(host,NULL,errors,FALSE);
}

void process_resource_config(per_host *host, FILE *open, FILE *errors, 
			     int virtual) 
{
  FILE *cfg;
  char l[MAX_STRING_LEN],w[MAX_STRING_LEN];
  char w2[MAX_STRING_LEN];
  char tmp[MAX_STRING_LEN];
  int n=0;
  per_request fakeit;
  
  fakeit.out = errors;
    
  if (!virtual) add_opts_int(&fakeit,0,"/");
 
  if (open == NULL) {
    if(!(cfg = fopen(host->srm_confname,"r"))) {
      fprintf(errors,"HTTPd: could not open document config file %s\n",
	      host->srm_confname);
      perror("fopen");
      if (ConfigErrorCritical) exit(1);
	else return;
    }
  } else cfg = open;
  while(!(cfg_getline(l,MAX_STRING_LEN,cfg))) {
    ++n;
    if((l[0] != '#') && (l[0] != '\0')) {
      cfg_getword(w,l);
      if(!strcasecmp(w,"ScriptAlias")) {
	cfg_getword(w,l);
	cfg_getword(w2,l);
	if((w[0] == '\0') || (w2[0] == '\0')) {
	  config_error(
"ScriptAlias must be followed by a fakename, one space, then a realname",
host->srm_confname,n,errors);
	}                
	if (!set_host_conf_value(host,PH_SRM_CONF,SRM_TRANSLATIONS)) {
	  host->translations = NULL;
	}
	add_alias(host,w,w2,A_SCRIPT_CGI);
      }
      else if(!strcasecmp(w,"OldScriptAlias")) {
	config_warn("OldScriptAlias directive obsolete, ignored",
		    host->srm_confname,n,errors);
      }
      else if(!strcasecmp(w,"UserDir")) {
	cfg_getword(w,l);
	if(!strcmp(w,"DISABLED"))
	  tmp[0] = '\0';
	else
	  strcpy(tmp,w);
	set_host_conf(host,PH_SRM_CONF,SRM_USER_DIR,tmp);
      }
      else if(!strcasecmp(w,"DirectoryIndex")) {
	set_host_conf(host,PH_SRM_CONF,SRM_INDEX_NAMES,l);
      } 
      else if(!strcasecmp(w,"DefaultType")) {
	cfg_getword(w,l);
	set_host_conf(host,PH_SRM_CONF,SRM_DEFAULT_TYPE,w);
      }
      else if(!strcasecmp(w,"AccessFileName")) {
	cfg_getword(w,l);
	set_host_conf(host,PH_SRM_CONF,SRM_ACCESS_NAME,w);
      } 
      else if(!strcasecmp(w,"DocumentRoot")) {
	cfg_getword(w,l);
	if(!is_directory(w)) {
	  sprintf(tmp,"%s is not a valid directory",w);
	  config_error(tmp,host->srm_confname,n,errors);
	}
	/* strip ending / for backward compatibility */
	if (w[strlen(w) - 1] == '/') w[strlen(w) - 1] = '\0';
	set_host_conf(host,PH_SRM_CONF,SRM_DOCUMENT_ROOT,w);
      } 
      else if(!strcasecmp(w,"Alias")) {
	cfg_getword(w,l);
	cfg_getword(w2,l);
	if((w[0] == '\0') || (w2[0] == '\0')) {
	  config_error(
"Alias must be followed by a fakename, one space, then a realname",
host->srm_confname,n,errors);
	}                
	if (!set_host_conf_value(host,PH_SRM_CONF,SRM_TRANSLATIONS)) {
	  host->translations = NULL;
	}
	add_alias(host,w,w2,A_STD_DOCUMENT);
      }
      else if(!strcasecmp(w,"AddType")) {
	cfg_getword(w,l);
	cfg_getword(w2,l);
	if((w[0] == '\0') || (w2[0] == '\0')) {
	  config_error(
"AddType must be followed by a type, one space, then a file or extension",
host->srm_confname,n,errors);
	}
	add_type(&fakeit,w2,w);
      }
      else if(!strcasecmp(w,"AddEncoding")) {
	cfg_getword(w,l);
	cfg_getword(w2,l);
	if((w[0] == '\0') || (w2[0] == '\0')) {
	  config_error(
"AddEncoding must be followed by a type, one space, then a file or extension",
host->srm_confname,n,errors);
	}
	add_encoding(&fakeit,w2,w);
      }
      else if(!strcasecmp(w,"Redirect") || !strcasecmp(w,"RedirectTemp")) {
	cfg_getword(w,l);
	cfg_getword(w2,l);
	if((w[0] == '\0') || (w2[0] == '\0') || (!is_url(w2))) {
	  config_error(
"Redirect must be followed by a document, one space, then a URL",
host->srm_confname,n,errors);
	}
	if (!set_host_conf_value(host,PH_SRM_CONF,SRM_TRANSLATIONS)) {
	  host->translations = NULL;
	}
	add_redirect(host,w,w2,A_REDIRECT_TEMP);
      }
      else if(!strcasecmp(w,"RedirectPermanent")) {
	cfg_getword(w,l);
	cfg_getword(w2,l);
	if((w[0] == '\0') || (w2[0] == '\0') || (!is_url(w2))) {
	  config_error(
"RedirectPermanent must be followed by a document, one space, then a URL",
host->srm_confname,n,errors);
	}
	if (!set_host_conf_value(host,PH_SRM_CONF,SRM_TRANSLATIONS)) {
	  host->translations = NULL;
	}
	add_redirect(host,w,w2,A_REDIRECT_PERM);
      }
      else if(!strcasecmp(w,"FancyIndexing")) {
	cfg_getword(w,l);
	if(!strcasecmp(w,"on"))
	  add_opts_int(&fakeit,FANCY_INDEXING,"/");
	else if(!strcasecmp(w,"off"))
	  add_opts_int(&fakeit,0,"/");
	else {
	  config_error("FancyIndexing must be on or off",
		       host->srm_confname,n,errors);
	}
      }
      else if(!strcasecmp(w,"AddDescription")) {
	char desc[MAX_STRING_LEN];
	int fq;
	if((fq = ind(l,'\"')) == -1) {
	  config_error(
"AddDescription must have quotes around the description",
host->srm_confname,n,errors);
	}
	else {
	  getword(desc,&l[++fq],'\"');
	  cfg_getword(w,&l[fq]);
	  add_desc(&fakeit,BY_PATH,desc,w,"/");
	}
      }
      else if(!strcasecmp(w,"IndexIgnore")) {
	while(l[0]) {
	  cfg_getword(w,l);
	  add_ignore(&fakeit,w,"/");
	}
      }
      else if(!strcasecmp(w,"AddIcon")) {
	cfg_getword(w2,l);
	while(l[0]) {
	  cfg_getword(w,l);
	  add_icon(&fakeit,BY_PATH,w2,w,"/");
	}
      }
      else if(!strcasecmp(w,"AddIconByType")) {
	cfg_getword(w2,l);
	while(l[0]) {
	  cfg_getword(w,l);
	  add_icon(&fakeit,BY_TYPE,w2,w,"/");
	}
      }
      else if(!strcasecmp(w,"AddIconByEncoding")) {
	cfg_getword(w2,l);
	while(l[0]) {
	  cfg_getword(w,l);
	  add_icon(&fakeit,BY_ENCODING,w2,w,"/");
	}
      }
      else if(!strcasecmp(w,"AddAlt")) {
	cfg_getword(w2,l);
	while(l[0]) {
	  cfg_getword(w,l);
	  add_alt(&fakeit,BY_PATH,w2,w,"/");
	}
      }
      else if(!strcasecmp(w,"AddAltByType")) {
	cfg_getword(w2,l);
	while(l[0]) {
	  cfg_getword(w,l);
	  add_alt(&fakeit,BY_TYPE,w2,w,"/");
	}
      }
      else if(!strcasecmp(w,"AddAltByEncoding")) {
	cfg_getword(w2,l);
	while(l[0]) {
	  cfg_getword(w,l);
	  add_alt(&fakeit,BY_ENCODING,w2,w,"/");
	}
      }
      else if(!strcasecmp(w,"DefaultIcon")) {
	cfg_getword(w,l);
	set_host_conf(host,PH_SRM_CONF,SRM_DEFAULT_ICON,w);
      }
      else if(!strcasecmp(w,"ReadmeName")) {
	cfg_getword(w,l);
	add_readme(&fakeit,w,"/");
      }
      else if(!strcasecmp(w,"HeaderName")) {
	cfg_getword(w,l);
	add_header(&fakeit,w,"/");
      }
      else if(!strcasecmp(w,"IndexOptions"))
	add_opts(&fakeit,l,"/");
      else if (!strcasecmp(w,"ErrorDocument")) {
        if (!set_host_conf_value(host,PH_SRM_CONF,SRM_DOCERRORS)) {
	  host->doc_errors = NULL;
	  host->num_doc_errors = 0;
	}
	cfg_getword(w,l); /* Get errornum */
	cfg_getword(w2,l); /* Get filename */

	add_doc_error(host,w,w2);
      }
      else if (!strcasecmp(w,"</SRMOptions>")) {
	Saved_Forced = forced_types;
        Saved_Encoding = encoding_types;
	return;
      }
      else {
	sprintf(tmp,"Unknown keyword %s",w);
	config_error(tmp,host->srm_confname,n,errors);
      }
    }
  }
  fclose(cfg);
  
  Saved_Forced = forced_types;
  Saved_Encoding = encoding_types;

}

void access_syntax_error(per_request *reqInfo, int n, char *err, FILE *fp, 
			 char *file) 
{
    if(!file) {
        fprintf(reqInfo->out,
		"Syntax error on line %d of access config file.\n",n);
        fprintf(reqInfo->out,"%s\n",err);
	fclose(fp);
        exit(1);
    }
    else {
        char e[MAX_STRING_LEN];
	FClose(fp);
        sprintf(e,"httpd: syntax error or override violation in access control file %s, reason: %s",file,err);
        die(reqInfo,SC_SERVER_ERROR,e);
    }
}

int parse_access_dir(per_request *reqInfo, FILE *f, int line, char or, 
		     char *dir, char *file) 
{
    char l[MAX_STRING_LEN];
    char w[MAX_STRING_LEN];
    char w2[MAX_STRING_LEN];
    int n=line;
    register int x,i;

    x = num_sec;

    sec[x].opts=OPT_UNSET;
    sec[x].override = or;
    sec[x].bSatisfy = 0;

    if(is_matchexp(dir))
        strcpy(sec[x].d,dir);
    else
        strcpy_dir(sec[x].d,dir);

    sec[x].auth_type[0] = '\0';
    sec[x].auth_name[0] = '\0';
    sec[x].auth_pwfile[0] = '\0';
#ifdef DIGEST_AUTH
    sec[x].auth_digestfile[0] = '\0';
#endif /* DIGEST_AUTH */
    sec[x].auth_grpfile[0] = '\0';
    for(i=0;i<METHODS;i++) {
        sec[x].order[i] = DENY_THEN_ALLOW;
        sec[x].num_allow[i]=0;
        sec[x].num_deny[i]=0;
        sec[x].num_auth[i] = 0;
    }

    while(!(cfg_getline(l,MAX_STRING_LEN,f))) {
        ++n;
        if((l[0] == '#') || (!l[0])) continue;
        cfg_getword(w,l);

        if(!strcasecmp(w,"AllowOverride")) {
            if(file)
                access_syntax_error(reqInfo,n,"override violation",f,file);
            sec[x].override = OR_NONE;
            while(l[0]) {
                cfg_getword(w,l);
                if(!strcasecmp(w,"Limit"))
                    sec[x].override |= OR_LIMIT;
                else if(!strcasecmp(w,"Options"))
                    sec[x].override |= OR_OPTIONS;
                else if(!strcasecmp(w,"FileInfo"))
                    sec[x].override |= OR_FILEINFO;
                else if(!strcasecmp(w,"AuthConfig"))
                    sec[x].override |= OR_AUTHCFG;
                else if(!strcasecmp(w,"Indexes"))
                    sec[x].override |= OR_INDEXES;
		else if(!strcasecmp(w,"Redirect"))
		    sec[x].override |= OR_REDIRECT;
                else if(!strcasecmp(w,"None"))
                    sec[x].override = OR_NONE;
                else if(!strcasecmp(w,"All")) 
                    sec[x].override = OR_ALL;
                else {
                    access_syntax_error(reqInfo,n,
"Unknown keyword in AllowOverride directive.",f,file);
                }
            }
        } 
        else if(!strcasecmp(w,"Options")) {
            if(!(or & OR_OPTIONS))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            sec[x].opts = OPT_NONE;
            while(l[0]) {
                cfg_getword(w,l);
                if(!strcasecmp(w,"Indexes"))
                    sec[x].opts |= OPT_INDEXES;
                else if(!strcasecmp(w,"Includes"))
                    sec[x].opts |= OPT_INCLUDES;
                else if(!strcasecmp(w,"IncludesNOEXEC"))
                    sec[x].opts |= (OPT_INCLUDES | OPT_INCNOEXEC);
                else if(!strcasecmp(w,"FollowSymLinks"))
                    sec[x].opts |= OPT_SYM_LINKS;
                else if(!strcasecmp(w,"SymLinksIfOwnerMatch"))
                    sec[x].opts |= OPT_SYM_OWNER;
                else if(!strcasecmp(w,"execCGI"))
                    sec[x].opts |= OPT_EXECCGI;
                else if(!strcasecmp(w,"None")) 
                    sec[x].opts = OPT_NONE;
                else if(!strcasecmp(w,"All")) 
                    sec[x].opts = OPT_ALL;
                else {
                    access_syntax_error(reqInfo,n,
"Unknown keyword in Options directive.",f,file);
                }
            }
        }
        else if(!strcasecmp(w,"AuthName")) {
            if(!(or & OR_AUTHCFG))
                access_syntax_error(reqInfo,n,"override violation",f,file);
	    strcpy(sec[x].auth_name,l);
        }
        else if(!strcasecmp(w,"AuthType")) {
            if(!(or & OR_AUTHCFG))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
	    strcpy(sec[x].auth_type, w);
        }
        else if(!strcasecmp(w,"AuthUserFile")) {
            if(!(or & OR_AUTHCFG))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
	    strcpy(sec[x].auth_pwfile, w);
	    cfg_getword(w,l);
#ifdef DBM_SUPPORT
	    if (!strcasecmp (w, "dbm"))
		sec[x].auth_pwfile_type = AUTHFILETYPE_DBM;
	    else 
#endif /* DBM_SUPPORT */
#ifdef NIS_SUPPORT
          if (!strcasecmp (w, "nis"))
              sec[x].auth_pwfile_type = AUTHFILETYPE_NIS;
          else 
#endif /* NIS_SUPPORT */
		sec[x].auth_pwfile_type = AUTHFILETYPE_STANDARD;
        }
#ifdef DIGEST_AUTH
        else if(!strcasecmp(w,"AuthDigestFile")) {
            if(!(or & OR_AUTHCFG))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
	    strcpy(sec[x].auth_digestfile, w);
	    cfg_getword(w,l);
#ifdef DBM_SUPPORT
	    if (!strcasecmp (w, "dbm"))
		sec[x].auth_digestfile_type = AUTHFILETYPE_DBM;
	    else 
#endif /* DBM_SUPPORT */
#ifdef NIS_SUPPORT
          if (!strcasecmp (w, "nis"))
              sec[x].auth_digestfile_type = AUTHFILETYPE_NIS;
          else 
#endif /* NIS_SUPPORT */
		sec[x].auth_digestfile_type = AUTHFILETYPE_STANDARD;
        }
#endif /* DIGEST_AUTH */
        else if(!strcasecmp(w,"AuthGroupFile")) {
            if(!(or & OR_AUTHCFG))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
	    strcpy(sec[x].auth_grpfile, w);
	    cfg_getword(w,l);
#ifdef DBM_SUPPORT
	    if (!strcasecmp (w, "dbm"))
		sec[x].auth_grpfile_type = AUTHFILETYPE_DBM;
	    else 
#endif /* DBM_SUPPORT */
#ifdef NIS_SUPPORT
          if (!strcasecmp (w, "nis"))
              sec[x].auth_grpfile_type = AUTHFILETYPE_NIS;
          else 
#endif /* NIS_SUPPORT */
		sec[x].auth_grpfile_type = AUTHFILETYPE_STANDARD;
        }
        else if(!strcasecmp(w,"AddType")) {
            if(!(or & OR_FILEINFO))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
            cfg_getword(w2,l);
            if((w[0] == '\0') || (w2[0] == '\0')) {
                access_syntax_error(reqInfo,n,
"AddType must be followed by a type, one space, then a file or extension.",
                                    f,file);
            }
            add_type(reqInfo,w2,w);
        }
        else if(!strcasecmp(w,"DefaultType")) {
            if(!(or & OR_FILEINFO))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
            strcpy(local_default_type,w);
        }
        else if(!strcasecmp(w,"AddEncoding")) {
            if(!(or & OR_FILEINFO))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
            cfg_getword(w2,l);
            if((w[0] == '\0') || (w2[0] == '\0')) {
                access_syntax_error(reqInfo,n,
"AddEncoding must be followed by a type, one space, then a file or extension.",
                                    f,file);
            }
            add_encoding(reqInfo,w2,w);
        }
        else if(!strcasecmp(w,"DefaultIcon")) {
            if(!(or & OR_INDEXES))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
            strcpy(local_default_icon,w);
        }
        else if(!strcasecmp(w,"AddDescription")) {
            char desc[MAX_STRING_LEN];
            int fq;
            
            if(!(or & OR_INDEXES))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            if((fq = ind(l,'\"')) == -1)
                access_syntax_error(reqInfo,n,"AddDescription must have quotes",
                                    f,file);
            else {
                getword(desc,&l[++fq],'\"');
                cfg_getword(w,&l[fq]);
                add_desc(reqInfo,BY_PATH,desc,w,sec[x].d);
            }
        }
        else if(!strcasecmp(w,"IndexIgnore")) {
            if(!(or & OR_INDEXES))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            while(l[0]) {
                cfg_getword(w,l);
                add_ignore(reqInfo,w,sec[x].d);
            }
        }
        else if(!strcasecmp(w,"AddIcon")) {
            char w2[MAX_STRING_LEN];
            
            if(!(or & OR_INDEXES))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w2,l);
            while(l[0]) {
                cfg_getword(w,l);
                add_icon(reqInfo,BY_PATH,w2,w,sec[x].d);
            }
        }
        else if(!strcasecmp(w,"AddIconByType")) {
            char w2[MAX_STRING_LEN];
            
            if(!(or & OR_INDEXES))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w2,l);
            while(l[0]) {
                cfg_getword(w,l);
                add_icon(reqInfo,BY_TYPE,w2,w,sec[x].d);
            }
        }
        else if(!strcasecmp(w,"AddIconByEncoding")) {
            char w2[MAX_STRING_LEN];
            
            if(!(or & OR_INDEXES))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w2,l);
            while(l[0]) {
                cfg_getword(w,l);
                add_icon(reqInfo,BY_ENCODING,w2,w,sec[x].d);
            }
        }
        else if(!strcasecmp(w,"ReadmeName")) {
            if(!(or & OR_INDEXES))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
            add_readme(reqInfo,w,sec[x].d);
        }
        else if(!strcasecmp(w,"HeaderName")) {
            if(!(or & OR_INDEXES))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
            add_header(reqInfo,w,sec[x].d);
        }
        else if(!strcasecmp(w,"IndexOptions")) {
            if(!(or & OR_INDEXES))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            add_opts(reqInfo,l,sec[x].d);
        }
        else if(!strcasecmp(w,"Redirect") || !strcasecmp(w,"RedirectTemp")) {
            if(!(or & OR_REDIRECT))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
            cfg_getword(w2,l);
            if((w[0] == '\0') || (w2[0] == '\0') || (!is_url(w2))) {
                access_syntax_error(reqInfo,n,
"Redirect must be followed by a document, one space, then a URL.",f,file);
            }
            if(!file) {
	      if (!set_host_conf_value(gConfiguration,PH_SRM_CONF,SRM_TRANSLATIONS)) {
		gConfiguration->translations = NULL;
	      }
	      add_redirect(gConfiguration,w,w2,A_REDIRECT_TEMP);
	    }
            else {
		if (!strcmp(reqInfo->url,w)) {
		  FClose(f);
		  die(reqInfo,SC_REDIRECT_TEMP,w2);
		}
	    }
        }
        else if(!strcasecmp(w,"RedirectPermanent")) {
            if(!(or & OR_FILEINFO))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            cfg_getword(w,l);
            cfg_getword(w2,l);
            if((w[0] == '\0') || (w2[0] == '\0') || (!is_url(w2))) {
                access_syntax_error(reqInfo,n,
"Redirect must be followed by a document, one space, then a URL.",f,file);
            }
            if(!file) {
	      if (!set_host_conf_value(gConfiguration,PH_SRM_CONF,SRM_TRANSLATIONS)) {
		gConfiguration->translations = NULL;
	      }
	      add_redirect(gConfiguration,w,w2,A_REDIRECT_PERM);
	    }
            else {
                if (!strcmp(reqInfo->url,w)) {
                  FClose(f);
                  die(reqInfo,SC_REDIRECT_PERM,w2);
                }
            }
	}
        else if(!strcasecmp(w,"<Limit")) {
            int m[METHODS];

            if(!(or & OR_LIMIT))
                access_syntax_error(reqInfo,n,"override violation",f,file);
            for(i=0;i<METHODS;i++) m[i] = 0;
            getword(w2,l,'>');
            while(w2[0]) {
                cfg_getword(w,w2);
                if(!strcasecmp(w,"GET")) m[M_GET]=1;
                else if(!strcasecmp(w,"PUT")) m[M_PUT]=1;
                else if(!strcasecmp(w,"POST")) m[M_POST]=1;
                else if(!strcasecmp(w,"DELETE")) m[M_DELETE]=1;
            }
            while(1) {
                if(cfg_getline(l,MAX_STRING_LEN,f))
                    access_syntax_error(reqInfo,n,"Limit missing /Limit",f,file);
                n++;
                if((l[0] == '#') || (!l[0])) continue;

                if(!strcasecmp(l,"</Limit>"))
                    break;
                cfg_getword(w,l);
                if(!strcasecmp(w,"order")) {
                    if(!strcasecmp(l,"allow,deny")) {
                        for(i=0;i<METHODS;i++)
                            if(m[i])
                                sec[x].order[i] = ALLOW_THEN_DENY;
                    }
                    else if(!strcasecmp(l,"deny,allow")) {
                        for(i=0;i<METHODS;i++)
                            if(m[i]) 
                                sec[x].order[i] = DENY_THEN_ALLOW;
                    }
                    else if(!strcasecmp(l,"mutual-failure")) {
                        for(i=0;i<METHODS;i++)
                            if(m[i]) 
                                sec[x].order[i] = MUTUAL_FAILURE;
                    }
                    else
                        access_syntax_error(reqInfo,n,"Unknown order.",f,file);
                } 
                else if((!strcasecmp(w,"allow"))) {
                    cfg_getword(w,l);
                    if(strcmp(w,"from"))
                        access_syntax_error(reqInfo,n,
                                            "allow must be followed by from.",
                                            f,file);
                    while(1) {
                        cfg_getword(w,l);
                        if(!w[0]) break;
                        for(i=0;i<METHODS;i++)
                            if(m[i]) {
                                int q=sec[x].num_allow[i]++;
                                if(!(sec[x].allow[i][q] = strdup(w)))
                                    die(reqInfo,SC_NO_MEMORY,"parse_access_dir");
                            }
                    }
                }
                else if(!strcasecmp(w,"require")) {
                    for(i=0;i<METHODS;i++)
                         if(m[i]) {
                            int q=sec[x].num_auth[i]++;
                            if(!(sec[x].auth[i][q] = strdup(l)))
                                die(reqInfo,SC_NO_MEMORY,"parse_access_dir");
                        }
                }
                else if((!strcasecmp(w,"deny"))) {
                    cfg_getword(w,l);
                    if(strcmp(w,"from"))
                        access_syntax_error(reqInfo,n,
                                            "deny must be followed by from.",
                                            f,file);
                    while(1) {
                        cfg_getword(w,l);
                        if(!w[0]) break;
                        for(i=0;i<METHODS;i++)
                            if(m[i]) {
                                int q=sec[x].num_deny[i]++;
                                if(!(sec[x].deny[i][q] = strdup(w)))
                                    die(reqInfo,SC_NO_MEMORY,"parse_access_dir");
                            }
                    }
                }
		else if((!strcasecmp(w, "satisfy"))){
		    sec[x].bSatisfy = SATISFY_ALL;       /*default 0 for all*/
		    cfg_getword(w,l);
		    if(!w[0]) break;   /*if not specified, assume satisfy all*/
		    if ((!strcasecmp(w, "any")))
			sec[x].bSatisfy = SATISFY_ANY;
		    else if ((strcasecmp(w, "all")))/* unknow word in satisfy*/
			access_syntax_error(reqInfo,n, 
					    "Satisfy either any or all.", 
					    f,file);
		}
                else
                    access_syntax_error(reqInfo,n,
					"Unknown keyword in Limit region.",
                                        f,file);
            }
        }
        else if(!strcasecmp(w,"</Directory>"))
            break;
        else {
            char errstr[MAX_STRING_LEN];
            sprintf(errstr,"Unknown method %s",w);
            access_syntax_error(reqInfo,n,errstr,f,file);
            return -1;
        }
    }
    ++num_sec;
    return n;
}


void parse_htaccess(per_request *reqInfo, char *path, char override) 
{
    struct stat buf;
    FILE *f;
    char t[MAX_STRING_LEN];
    char d[MAX_STRING_LEN];

    strcpy(d,path);
    make_full_path(d,reqInfo->hostInfo->access_name,t);

    if((stat(t, &buf) != -1) && (f=FOpen(t,"r"))) {
        parse_access_dir(reqInfo,f,-1,override,d,t);
        FClose(f);
    }
}


void process_access_config(FILE *errors) 
{
    FILE *f;
    char l[MAX_STRING_LEN];
    char w[MAX_STRING_LEN];
    int n;
    per_request reqInfo;

    reqInfo.out = errors;
    num_sec = 0;
    n=0;
    if(!(f=fopen(access_confname,"r"))) {
        fprintf(errors,"httpd: could not open access configuration file %s.\n",
                access_confname);
        perror("fopen");
        exit(1);
    }
    while(!(cfg_getline(l,MAX_STRING_LEN,f))) {
        ++n;
        if((l[0] == '#') || (!l[0])) continue;
	cfg_getword(w,l);
	if(strcasecmp(w,"<Directory")) {
	    fprintf(errors,
		    "Syntax error on line %d of access config. file.\n",n);
	    fprintf(errors,"Unknown directive %s.\n",w);
	    exit(1);
	}
	getword(w,l,'>');
	n=parse_access_dir(&reqInfo,f,n,OR_ALL,w,NULL);
    }
    fclose(f);
    num_sec_config = num_sec;
}

void read_config(FILE *errors) 
{
  FILE *cfg;
  per_host *host;

  host = create_host_conf(NULL,FALSE);
  set_defaults(host,errors);

  if(!(cfg = fopen(server_confname,"r"))) {
    fprintf(errors,
	    "httpd: could not open server config. file %s\n",
	    server_confname);
    perror("fopen");
    exit(1);
  }
  init_indexing();
  process_server_config(host,cfg,errors,FALSE);
  fclose(cfg);
  init_mime();
  /*  process_resource_config(errors); */
  process_access_config(errors);
  open_all_logs();
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.