ftp.nice.ch/pub/next/unix/network/www/apache.1.3a1.NIHS.bs.tar.gz#/apache.1.3a1.NIHS.bs/original-source/src/http_config.c

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

/* ====================================================================
 * Copyright (c) 1995-1997 The Apache Group.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * 4. The names "Apache Server" and "Apache Group" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission.
 *
 * 5. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Group and was originally based
 * on public domain software written at the National Center for
 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
 * For more information on the Apache Group and the Apache HTTP server
 * project, please see <http://www.apache.org/>.
 *
 */

/*
 * http_config.c: once was auxillary functions for reading httpd's config
 * file and converting filenames into a namespace
 *
 * Rob McCool 
 * 
 * Wall-to-wall rewrite for Apache... commands which are part of the
 * server core can now be found next door in "http_core.c".  Now contains
 * general command loop, and functions which do bookkeeping for the new
 * Apache config stuff (modules and configuration vectors).
 *
 * rst
 *
 */

#define CORE_PRIVATE

#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"		/* for errors in parse_htaccess */
#include "http_request.h"	/* for default_handler (see invoke_handler) */
#include "http_conf_globals.h"	/* Sigh... */
#include "explain.h"

DEF_Explain

/****************************************************************
 *
 * We begin with the functions which deal with the linked list
 * of modules which control just about all of the server operation.
 */

/* total_modules is the number of modules linked in.  */
static int total_modules = 0;
module *top_module = NULL;
    
typedef int (*handler_func)(request_rec *);
typedef void *(*dir_maker_func)(pool *, char *);
typedef void *(*merger_func)(pool *, void *, void *);    

/* Dealing with config vectors.  These are associated with per-directory,
 * per-server, and per-request configuration, and have a void* pointer for
 * each modules.  The nature of the structure pointed to is private to the
 * module in question... the core doesn't (and can't) know.  However, there
 * are defined interfaces which allow it to create instances of its private
 * per-directory and per-server structures, and to merge the per-directory
 * structures of a directory and its subdirectory (producing a new one in
 * which the defaults applying to the base directory have been properly
 * overridden).
 */

API_EXPORT(void *) get_module_config (void *conf_vector, module *m)
{
   void **confv = (void**)conf_vector;
   return confv[m->module_index];
}

API_EXPORT(void) set_module_config (void *conf_vector, module *m, void *val)
{
   void **confv = (void**)conf_vector;
   confv[m->module_index] = val;
}

void *
create_empty_config (pool *p)
{
   void **conf_vector = (void **)pcalloc(p, sizeof(void*) * total_modules);
   return (void *)conf_vector;
}

void *
create_default_per_dir_config (pool *p)
{
   void **conf_vector = (void **)pcalloc(p, sizeof(void*) * (total_modules+DYNAMIC_MODULE_LIMIT));
   module *modp;

   for (modp = top_module; modp; modp = modp->next) {
       dir_maker_func df = modp->create_dir_config;

       if (df) conf_vector[modp->module_index] = (*df)(p, NULL);
   }

   return (void*)conf_vector;
}

void *
merge_per_dir_configs (pool *p, void *base, void *new)
{
   void **conf_vector = (void **)palloc(p, sizeof(void*) * total_modules);
   void **base_vector = (void **) base;
   void **new_vector = (void **) new;
   module *modp;

   for (modp = top_module; modp; modp = modp->next) {
       merger_func df = modp->merge_dir_config;
       int i = modp->module_index;

       if (df && new_vector[i])
	   conf_vector[i] = (*df)(p, base_vector[i], new_vector[i]);
       else
	   conf_vector[i] = new_vector[i]? new_vector[i] : base_vector[i];
   }

   return (void*)conf_vector;
}

void *
create_server_config (pool *p, server_rec *s)
{
   void **conf_vector = (void **)pcalloc(p, sizeof(void*) * (total_modules+DYNAMIC_MODULE_LIMIT));
   module *modp;

   for (modp = top_module; modp; modp = modp->next) {
       if (modp->create_server_config)
	   conf_vector[modp->module_index]=(*modp->create_server_config)(p,s);
   }

   return (void*)conf_vector;
}

void merge_server_configs (pool *p, void *base, void *virt)
{
    /* Can reuse the 'virt' vector for the spine of it, since we don't
     * have to deal with the moral equivalent of .htaccess files here...
     */

    void **base_vector = (void **)base;
    void **virt_vector = (void **)virt;
    module *modp;
    
    for (modp = top_module; modp; modp = modp->next) {
	merger_func df = modp->merge_server_config;
	int i = modp->module_index;

	if (!virt_vector[i])
	    virt_vector[i] = base_vector[i];
	else if (df)
	    virt_vector[i] = (*df)(p, base_vector[i], virt_vector[i]);
    }
}
 
void *create_connection_config (pool *p) {
    return create_empty_config (p);
}

void *create_request_config (pool *p) {
    return create_empty_config (p);
}

void *create_per_dir_config (pool *p) {
    return create_empty_config (p);
}

#ifdef EXPLAIN

struct
    {
    int offset;
    char *method;
    } aMethods[]=
    {
#define m(meth)	{ XtOffsetOf(module,meth),#meth }
    m(translate_handler),
    m(check_user_id),
    m(auth_checker),
    m(type_checker),
    m(fixer_upper),
    m(logger),
    { -1,"?" },
#undef m
    };

char *ShowMethod(module *modp,int offset)
    {
    int n;
    static char buf[200];

    for(n=0 ; aMethods[n].offset >= 0 ; ++n)
	if(aMethods[n].offset == offset)
	    break;
    ap_snprintf(buf, sizeof(buf), "%s:%s",modp->name,aMethods[n].method);
    return buf;
    }
#else
#define ShowMethod(modp,offset)
#endif

/****************************************************************
 *
 * Dispatch through the modules to find handlers for various phases
 * of request handling.  These are invoked by http_request.c to actually
 * do the dirty work of slogging through the module structures.
 */

/*
 * Optimized run_method routines.  The observation here is that many modules
 * have NULL for most of the methods.  So we build optimized lists of
 * everything.  If you think about it, this is really just like a sparse array
 * implementation to avoid scanning the zero entries.
 */
static const int method_offsets[] = {
    XtOffsetOf (module, translate_handler),
    XtOffsetOf (module, check_user_id),
    XtOffsetOf (module, auth_checker),
    XtOffsetOf (module, access_checker),
    XtOffsetOf (module, type_checker),
    XtOffsetOf (module, fixer_upper),
    XtOffsetOf (module, logger),
    XtOffsetOf (module, header_parser)
};
#define NMETHODS	(sizeof (method_offsets)/sizeof (method_offsets[0]))

static struct {
    int translate_handler;
    int check_user_id;
    int auth_checker;
    int access_checker;
    int type_checker;
    int fixer_upper;
    int logger;
    int header_parser;
} offsets_into_method_ptrs;

/*
 * This is just one big array of method_ptrs.  It's constructed such that,
 * for example, method_ptrs[ offsets_into_method_ptrs.logger ] is the first
 * logger function.  You go one-by-one from there until you hit a NULL.
 * This structure was designed to hopefully maximize cache-coolness.
 */
static handler_func *method_ptrs;

/* routine to reconstruct all these shortcuts... called after every
 * add_module.
 * XXX: this breaks if modules dink with their methods pointers
 */
static void
build_method_shortcuts (void)
{
    module *modp;
    int how_many_ptrs;
    int i;
    int next_ptr;
    handler_func fp;

    if (method_ptrs) {
	/* free up any previous set of method_ptrs */
	free (method_ptrs);
    }

    /* first we count how many functions we have */
    how_many_ptrs = 0;
    for (modp = top_module; modp; modp = modp->next) {
	for (i = 0; i<NMETHODS; ++i) {
	    if (*(handler_func *)(method_offsets[i] + (char *)modp)) {
		++how_many_ptrs;
	    }
	}
    }
    method_ptrs = malloc ((how_many_ptrs+NMETHODS)*sizeof (handler_func));
    next_ptr = 0;
    for (i = 0; i<NMETHODS; ++i) {
	/* XXX: This is an itsy bit presumptuous about the alignment
	 * constraints on offsets_into_method_ptrs.  I can't remember if
	 * ANSI says this has to be true... -djg */
	((int *)&offsets_into_method_ptrs)[i] = next_ptr;
	for (modp = top_module; modp; modp = modp->next) {
	    fp = *(handler_func *)(method_offsets[i] + (char *)modp);
	    if (fp) {
		method_ptrs[next_ptr++] = fp;
	    }
	}
	method_ptrs[next_ptr++] = NULL;
    }
}


static int
run_method (request_rec *r, int offset, int run_all)
{
    int i;

    for (i = offset; method_ptrs[i]; ++i ) {
	handler_func mod_handler = method_ptrs[i];

	if (mod_handler) {
           int result;

	   result = (*mod_handler)(r);

	   if (result != DECLINED && (!run_all || result != OK))
	       return result;
       }
   }

   return run_all ? OK : DECLINED;
}

int translate_name(request_rec *r) {
   return run_method (r, offsets_into_method_ptrs.translate_handler, 0);
}

int check_access(request_rec *r) {
   return run_method (r, offsets_into_method_ptrs.access_checker, 1);
}

int find_types (request_rec *r) {
   return run_method (r, offsets_into_method_ptrs.type_checker, 0);
}

int run_fixups (request_rec *r) {
   return run_method (r, offsets_into_method_ptrs.fixer_upper, 1);
}

int log_transaction (request_rec *r) {
   return run_method (r, offsets_into_method_ptrs.logger, 1);
}

int header_parse (request_rec *r) {
    return run_method (r, offsets_into_method_ptrs.header_parser, 1);
}

/* Auth stuff --- anything that defines one of these will presumably
 * want to define something for the other.  Note that check_auth is
 * separate from check_access to make catching some config errors easier.
 */

int check_user_id (request_rec *r) {
   return run_method (r, offsets_into_method_ptrs.check_user_id, 0);
}

int check_auth (request_rec *r) {
   return run_method (r, offsets_into_method_ptrs.auth_checker, 0);
}

int invoke_handler (request_rec *r)
{
   module *modp;
   handler_rec *handp;
   char *content_type = r->content_type ? r->content_type : default_type (r);
   char *handler, *p; 

   if ((p = strchr(content_type, ';')) != NULL) {  /* MIME type arguments */
       while (p > content_type && p[-1] == ' ') --p; /* strip trailing spaces */
       content_type = pstrndup(r->pool, content_type, p - content_type);
   }
   handler = r->handler ? r->handler : content_type;
  
   /* Pass one --- direct matches */
   
   for (modp = top_module; modp; modp = modp->next) 
   {
       if (!modp->handlers) continue;
       
       for (handp = modp->handlers; handp->content_type; ++handp) {
	   if (!strcasecmp (handler, handp->content_type)) {
	       int result = (*handp->handler)(r);

	       if (result != DECLINED) return result;
	   }
       }
   }
   
   /* Pass two --- wildcard matches */
   
   for (modp = top_module; modp; modp = modp->next) 
   {
       if (!modp->handlers) continue;
       
       for (handp = modp->handlers; handp->content_type; ++handp) {
	   char *starp = strchr (handp->content_type, '*');
	   int len;

	   if (!starp) continue;

	   len = starp - handp->content_type;
	   
	   if (!len || !strncasecmp (handler, handp->content_type, len))
	   {
	       int result = (*handp->handler)(r);

	       if (result != DECLINED) return result;
	   }
       }
   }
   
   return NOT_IMPLEMENTED;
}

/* One-time setup for precompiled modules --- NOT to be done on restart */

API_EXPORT(void) add_module (module *m)
{
    /* This could be called from an AddModule httpd.conf command,
     * after the file has been linked and the module structure within it
     * teased out...
     */

    /* At some point, we may want to offer back-compatibility for
     * loading modules that are for older versions of Apache. For now,
     * though, we don't.
     */

    if (m->version != MODULE_MAGIC_NUMBER) {
	fprintf(stderr, "httpd: module \"%s\" is not compatible with this "
		        "version of Apache.\n", m->name);
	fprintf(stderr, "Please contact the author for the correct version.\n");
	exit(1);
    }

    if (m->next == NULL) {
        m->next = top_module;
	top_module = m;
    }
    if (m->module_index == -1) {
	m->module_index = total_modules++;
    }
    
    /* Some C compilers put a complete path into __FILE__, but we want
     * only the filename (e.g. mod_includes.c). So check for path
     * components (Unix and DOS), and remove them.
     */

    if (strrchr(m->name, '/')) m->name = 1 + strrchr(m->name, '/');
    if (strrchr(m->name, '\\')) m->name = 1 + strrchr(m->name, '\\');

    /** XXX: this will be slow if there's lots of add_modules */
    build_method_shortcuts ();
}

void setup_prelinked_modules ()
{
    module **m;

    /* First, set all module indices, and init total_modules.  */
    total_modules = 0;
    for (m = preloaded_modules; *m; ++m, ++total_modules) {
        (*m)->module_index = total_modules;
    }

    for (m = prelinked_modules; *m; ++m) {
        add_module (*m);
    }
}

API_EXPORT(const char *) find_module_name (module *m)
{
    return m->name;
}

API_EXPORT(module *) find_linked_module (const char *name)
{
    module *modp;

    for (modp = top_module; modp; modp = modp->next) {
        if (strcmp(modp->name, name) == 0)
            return modp;
    }
    return NULL;
}

/* Add a named module.  Returns 1 if module found, 0 otherwise.  */
API_EXPORT(int) add_named_module (const char *name)
{
    module *modp;
    int i = 0;

    for (modp = preloaded_modules[i]; modp; modp = preloaded_modules[++i]) {
        if (strcmp(modp->name, name) == 0) {
	    /* Only add modules that are not already enabled.  */
	    if (modp->next == NULL) {
	        add_module(modp);
	    }
	    return 1;
	}
    }

    return 0;
}

/* Clear the internal list of modules, in preparation for starting over. */
API_EXPORT(void) clear_module_list ()
{
    module **m = &top_module;
    module **next_m;

    while (*m) {
	next_m = &((*m)->next);
	*m = NULL;
	m = next_m;
    }

    /* This is required; so we add it always.  */
    add_named_module ("http_core.c");
}

/*****************************************************************
 *
 * Resource, access, and .htaccess config files now parsed by a common
 * command loop.
 *
 * Let's begin with the basics; parsing the line and
 * invoking the function...
 */

const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms, void *mconfig,
		 const char *args)
{
    char *w, *w2, *w3;
    const char *errmsg;

    if ((parms->override & cmd->req_override) == 0)
        return pstrcat (parms->pool, cmd->name, " not allowed here", NULL);
    
    parms->info = cmd->cmd_data;
    parms->cmd = cmd;
    
    switch (cmd->args_how) {
    case RAW_ARGS:
        return (*cmd->func) (parms, mconfig, args);

    case NO_ARGS:
	if (*args != 0)
	    return pstrcat (parms->pool, cmd->name, " takes no arguments",
			    NULL);

	return (*cmd->func) (parms, mconfig);
	
    case TAKE1:
	w = getword_conf (parms->pool, &args);
	
	if (*w == '\0' || *args != 0) 
	    return pstrcat (parms->pool, cmd->name, " takes one argument",
			    cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);

	return (*cmd->func) (parms, mconfig, w);
	
    case TAKE2:

	w = getword_conf (parms->pool, &args);
	w2 = getword_conf (parms->pool, &args);
	
	if (*w == '\0' || *w2 == '\0' || *args != 0) 
	    return pstrcat (parms->pool, cmd->name, " takes two arguments",
			    cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);

	return (*cmd->func) (parms, mconfig, w, w2);
	
    case TAKE12:

	w = getword_conf (parms->pool, &args);
	w2 = getword_conf (parms->pool, &args);
	
	if (*w == '\0' || *args != 0) 
	    return pstrcat (parms->pool, cmd->name, " takes 1-2 arguments",
			    cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);

	return (*cmd->func) (parms, mconfig, w, *w2 ? w2 : NULL);
	
    case TAKE3:

	w = getword_conf (parms->pool, &args);
	w2 = getword_conf (parms->pool, &args);
	w3 = getword_conf (parms->pool, &args);
	
	if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0) 
	    return pstrcat (parms->pool, cmd->name, " takes three arguments",
			    cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);

	return (*cmd->func) (parms, mconfig, w, w2, w3);
	
    case TAKE23:

	w = getword_conf (parms->pool, &args);
	w2 = getword_conf (parms->pool, &args);
	w3 = *args ? getword_conf (parms->pool, &args) : NULL;
	
	if (*w == '\0' || *w2 == '\0' || *args != 0) 
	    return pstrcat (parms->pool, cmd->name, " takes two or three arguments",
			    cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);

	return (*cmd->func) (parms, mconfig, w, w2, w3);
	
    case TAKE123:

	w = getword_conf (parms->pool, &args);
	w2 = *args ? getword_conf (parms->pool, &args) : NULL;
	w3 = *args ? getword_conf (parms->pool, &args) : NULL;
	
	if (*w == '\0' || *args != 0) 
	    return pstrcat (parms->pool, cmd->name, " takes one, two or three arguments",
			    cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);

	return (*cmd->func) (parms, mconfig, w, w2, w3);
	
    case TAKE13:

	w = getword_conf (parms->pool, &args);
	w2 = *args ? getword_conf (parms->pool, &args) : NULL;
	w3 = *args ? getword_conf (parms->pool, &args) : NULL;
	
	if (*w == '\0' || (*w2 && !w3) || *args != 0) 
	    return pstrcat (parms->pool, cmd->name, " takes one or three arguments",
			    cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);

	return (*cmd->func) (parms, mconfig, w, w2, w3);
	
    case ITERATE:

	while (*(w = getword_conf (parms->pool, &args)) != '\0')
	    if ((errmsg = (*cmd->func) (parms, mconfig, w)))
	        return errmsg;

	return NULL;
	
    case ITERATE2:

	w = getword_conf (parms->pool, &args);
	
	if (*w == '\0' || *args == 0) 
	    return pstrcat(parms->pool, cmd->name,
			   " requires at least two arguments",
			   cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
	  

	while (*(w2 = getword_conf (parms->pool, &args)) != '\0')
	    if ((errmsg = (*cmd->func) (parms, mconfig, w, w2)))
	        return errmsg;

	return NULL;
	
    case FLAG:

	w = getword_conf (parms->pool, &args);

	if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp (w, "off")))
	    return pstrcat (parms->pool, cmd->name, " must be On or Off",
			    NULL);

	return (*cmd->func) (parms, mconfig, strcasecmp (w, "off") != 0);

    default:

	return pstrcat (parms->pool, cmd->name,
			" is improperly configured internally (server bug)",
			NULL);
    }
}

const command_rec *find_command (const char *name, const command_rec *cmds)
{
    while (cmds->name) 
        if (!strcasecmp (name, cmds->name))
	    return cmds;
	else
	    ++cmds;

    return NULL;
}
    
const command_rec *find_command_in_modules (const char *cmd_name, module **mod)
{
   const command_rec *cmdp;
   module *modp;

   for (modp = *mod; modp; modp = modp->next) 
       if (modp->cmds && (cmdp = find_command (cmd_name, modp->cmds))) {
	   *mod = modp;
	   return cmdp;
       }

   return NULL;
}

const char *handle_command (cmd_parms *parms, void *config, const char *l)
{
    const char *args, *cmd_name, *retval;
    const command_rec *cmd;
    module *mod = top_module;

    ++parms->config_line;
    if((l[0] == '#') || (!l[0])) return NULL;
	
    args = l;
    cmd_name = getword_conf (parms->temp_pool, &args);
    if (*cmd_name == '\0') return NULL;
	
    do {
	if (!(cmd = find_command_in_modules (cmd_name, &mod))) {
	    return pstrcat (parms->pool, "Invalid command ", cmd_name, NULL);
	}
	else {
	    void *mconfig = get_module_config (config, mod);
	    void *sconfig =
		get_module_config (parms->server->module_config, mod);
	    
	    if (!mconfig && mod->create_dir_config) {
		mconfig = (*mod->create_dir_config) (parms->pool, parms->path);
		set_module_config (config, mod, mconfig);
	    }
	    
	    if (!sconfig && mod->create_server_config) {
		sconfig =
		    (*mod->create_server_config)(parms->pool, parms->server);
		set_module_config (parms->server->module_config, mod, sconfig);
	    }
	    
	    retval = invoke_cmd (cmd, parms, mconfig, args);
	    mod = mod->next;	/* Next time around, skip this one */
	}
    } while (retval && !strcmp(retval, DECLINE_CMD));

    return retval;
}

const char *srm_command_loop (cmd_parms *parms, void *config)
{
    char l[MAX_STRING_LEN];
    
    while (!(cfg_getline (l, MAX_STRING_LEN, parms->infile))) {
	const char *errmsg = handle_command (parms, config, l);
	if (errmsg) return errmsg;
    }

    return NULL;
}

/*
 * Generic command functions...
 */

API_EXPORT_NONSTD(const char *) set_string_slot (cmd_parms *cmd,
						 char *struct_ptr, char *arg)
{
    /* This one's pretty generic... */
  
    int offset = (int)cmd->info; 
    *(char **)(struct_ptr + offset) = pstrdup (cmd->pool, arg);
    return NULL;
}

API_EXPORT_NONSTD(const char *) set_flag_slot (cmd_parms *cmd,
					       char *struct_ptr, int arg)
{
    /* This one's pretty generic too... */
  
    int offset = (int)cmd->info; 
    *(int *)(struct_ptr + offset) = arg ? 1 : 0;
    return NULL;
}

/*****************************************************************
 *
 * Reading whole config files...
 */

cmd_parms default_parms = { NULL, 0, -1, NULL, 0, NULL, NULL, NULL, NULL };

API_EXPORT(char *)server_root_relative (pool *p, char *file)
{
#ifdef __EMX__
    /* Add support for OS/2 drive names */
    if ((file[0] == '/') || (file[1] == ':')) return file;
#else
    if (file[0] == '/') return file;
#endif    
    return make_full_path (p, server_root, file);
}

void process_resource_config(server_rec *s, char *fname, pool *p, pool *ptemp)
{
    FILE *cfg;
    const char *errmsg;
    cmd_parms parms;
    struct stat finfo;
    
    fname = server_root_relative (p, fname);

    if (!(strcmp(fname, server_root_relative(p, RESOURCE_CONFIG_FILE))) ||
	!(strcmp(fname, server_root_relative(p, ACCESS_CONFIG_FILE)))) {
	if (stat(fname, &finfo) == -1)
	    return;
    }
    
    /* GCC's initialization extensions are soooo nice here... */
    
    parms = default_parms;
    parms.config_file = fname;
    parms.pool = p;
    parms.temp_pool = ptemp;
    parms.server = s;
    parms.override = (RSRC_CONF|OR_ALL)&~(OR_AUTHCFG|OR_LIMIT);
    
    if(!(cfg = fopen(fname, "r"))) {
        perror("fopen");
        fprintf(stderr,"httpd: could not open document config file %s\n",
                fname);
        exit(1);
    } 

    parms.infile = cfg;
    
    errmsg = srm_command_loop (&parms, s->lookup_defaults);
    
    if (errmsg) {
        fprintf (stderr, "Syntax error on line %d of %s:\n",
		 parms.config_line, fname);
	fprintf (stderr, "%s\n", errmsg);
	exit(1);
    }
    
    fclose(cfg);
}


int parse_htaccess(void **result, request_rec *r, int override,
		   char *d, const char *access_name)
{
    FILE *f = NULL;
    cmd_parms parms;
    const char *errmsg;
    char *filename = NULL;
    const struct htaccess_result *cache;
    struct htaccess_result *new;
    void *dc;

/* firstly, search cache */
    for (cache=r->htaccess; cache != NULL; cache=cache->next)
	if (cache->override == override && strcmp(cache->dir, d) == 0)
	{
	    if (cache->htaccess != NULL) *result = cache->htaccess;
	    return OK;
	}

    parms = default_parms;
    parms.override = override;
    parms.pool = r->pool;
    parms.temp_pool = r->pool;
    parms.server = r->server;
    parms.path = d;

    if (access_name) {
	while (!f && access_name[0]) {
	    char *w = getword_conf(r->pool, &access_name);
	    filename = make_full_path(r->pool, d, w);
	    f=pfopen(r->pool, filename, "r");
	}
    }
    else {
	filename = make_full_path(r->pool, d, 0);
	f=pfopen(r->pool, filename, "r");
    }
    if(f) {
        dc = create_per_dir_config (r->pool);
	
        parms.infile = f;
	parms.config_file = filename;

	errmsg = srm_command_loop (&parms, dc);
	
        pfclose(r->pool, f);

	if (errmsg) {
	    log_reason (errmsg, filename, r);
	    return SERVER_ERROR;
	}
	
	*result = dc;
    } else
	dc = NULL;

/* cache it */
    new = palloc(r->pool, sizeof(struct htaccess_result));
    new->dir = pstrdup(r->pool, d);
    new->override = override;
    new->htaccess = dc;
/* add to head of list */
    new->next = r->htaccess;
    r->htaccess = new;

    return OK;
}

/*****************************************************************
 *
 * Virtual host stuff; note that the commands that invoke this stuff
 * are with the command table in http_core.c.
 */

/*
 * Parses a host of the form <address>[:port]
 * paddr is used to create a list in the order of input
 * **paddr is the ->next pointer of the last entry (or s->addrs)
 * *paddr is the variable used to keep track of **paddr between calls
 * port is the default port to assume
 */
static void get_addresses (pool *p, char *w, server_addr_rec ***paddr, unsigned port)
{
    struct hostent *hep;
    unsigned long my_addr;
    server_addr_rec *sar;
    char *t;
    int i, is_an_ip_addr;

    if( *w == 0 ) return;

    t = strchr(w, ':');
    if (t) {
	if( strcmp(t+1,"*") == 0 ) {
	    port = 0;
	} else if( (i = atoi(t+1)) ) {
	    port = i;
	} else {
	    fprintf( stderr, "Port must be numeric\n" );
	}
	*t = 0;
    }

    is_an_ip_addr = 0;
    if (strcmp(w, "*") == 0) {
	my_addr = htonl(INADDR_ANY);
	is_an_ip_addr = 1;
    } else if( strcmp(w, "_default_") == 0
	    || strcmp(w, "255.255.255.255") == 0 ) {
	my_addr = DEFAULT_VHOST_ADDR;
	is_an_ip_addr = 1;
    } else if(
#ifdef DGUX
	    ( my_addr = inet_network(w) )
#else
	    ( my_addr = inet_addr(w) )
#endif
	    != INADDR_NONE ) {
	is_an_ip_addr = 1;
    }
    if( is_an_ip_addr ) {
	sar = pcalloc( p, sizeof( server_addr_rec ) );
	**paddr = sar;
	*paddr = &sar->next;
	sar->host_addr.s_addr = my_addr;
	sar->host_port = port;
	sar->virthost = pstrdup(p, w);
	if (t != NULL) *t = ':';
	return;
    }

    hep = gethostbyname(w);

    if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
	fprintf (stderr, "Cannot resolve host name %s --- ignoring!\n", w);
	if (t != NULL) *t = ':';
	return;
    }

    for( i = 0; hep->h_addr_list[i]; ++i ) {
	sar = pcalloc( p, sizeof( server_addr_rec ) );
	**paddr = sar;
	*paddr = &sar->next;
	sar->host_addr = *(struct in_addr *)hep->h_addr_list[i];
	sar->host_port = port;
	sar->virthost = pstrdup(p, w);
    }

    if (t != NULL) *t = ':';
}

server_rec *init_virtual_host (pool *p, const char *hostname,
				server_rec *main_server)
{
    server_rec *s = (server_rec *)pcalloc (p, sizeof (server_rec));
    server_addr_rec **addrs;

#ifdef RLIMIT_NOFILE
    struct rlimit limits;

    getrlimit ( RLIMIT_NOFILE, &limits );
    if ( limits.rlim_cur < limits.rlim_max ) {
      limits.rlim_cur += 2;
      if ( setrlimit ( RLIMIT_NOFILE, &limits ) < 0 ) {
	perror ("setrlimit(RLIMIT_NOFILE)");
	fprintf (stderr, "Cannot exceed hard limit for open files");
      }
    }
#endif

    s->server_admin = NULL;
    s->server_hostname = NULL; 
    s->error_fname = NULL;
    s->srm_confname = NULL;
    s->access_confname = NULL;
    s->timeout = 0;
    s->keep_alive_timeout = 0;
    s->keep_alive = -1;
    s->keep_alive_max = -1;
    s->error_log = main_server->error_log;
    /* start the list of addreses */
    addrs = &s->addrs;
    while( hostname[0] ) {
	get_addresses( p, getword_conf( p, &hostname ), &addrs,
	    main_server->port );
    }
    /* terminate the list */
    *addrs = NULL;
    if( s->addrs ) {
	if (s->addrs->host_port) {
	    s->port = s->addrs->host_port;  /* set them the same, by default */
	} else {
	    /* otherwise we get a port of 0 on redirects */
	    s->port = main_server->port;
	}
    }
    s->next = NULL;

    s->is_virtual = 1;
    s->names = NULL;

    s->module_config = create_empty_config (p);
    s->lookup_defaults = create_per_dir_config (p);
    
    s->server_uid = user_id;
    s->server_gid = group_id;

    return s;
}

int is_virtual_server (server_rec *s)
{
    return s->is_virtual;
}

void fixup_virtual_hosts (pool *p, server_rec *main_server)
{
    server_rec *virt;

    for (virt = main_server->next; virt; virt = virt->next) {
	merge_server_configs (p, main_server->module_config,
			      virt->module_config);
	
	virt->lookup_defaults =
	    merge_per_dir_configs (p, main_server->lookup_defaults,
				   virt->lookup_defaults);

	if (virt->server_admin == NULL)
	    virt->server_admin = main_server->server_admin;

	if (virt->srm_confname == NULL)
	    virt->srm_confname = main_server->srm_confname;

	if (virt->access_confname == NULL)
	    virt->access_confname = main_server->access_confname;

	if (virt->timeout == 0)
	    virt->timeout = main_server->timeout;

	if (virt->keep_alive_timeout == 0)
	    virt->keep_alive_timeout = main_server->keep_alive_timeout;

	if (virt->keep_alive == -1)
	    virt->keep_alive = main_server->keep_alive;

	if (virt->keep_alive_max == -1)
	    virt->keep_alive_max = main_server->keep_alive_max;

	if (virt->send_buffer_size == 0)
		virt->send_buffer_size = main_server->send_buffer_size;
    }
}

/*****************************************************************
 *
 * Getting *everything* configured... 
 */

void init_config_globals (pool *p)
{
    /* ServerRoot, server_confname set in httpd.c */
    
    standalone = 1;
    user_name = DEFAULT_USER;
    user_id = uname2id(DEFAULT_USER);
    group_id = gname2id(DEFAULT_GROUP);
    daemons_to_start = DEFAULT_START_DAEMON;
    daemons_min_free = DEFAULT_MIN_FREE_DAEMON;
    daemons_max_free = DEFAULT_MAX_FREE_DAEMON;
    daemons_limit = HARD_SERVER_LIMIT;
    pid_fname = DEFAULT_PIDLOG;
    scoreboard_fname = DEFAULT_SCOREBOARD;
    lock_fname = DEFAULT_LOCKFILE;
    max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
    bind_address.s_addr = htonl(INADDR_ANY);
    listeners = NULL;
    listenbacklog = DEFAULT_LISTENBACKLOG;
}

server_rec *init_server_config(pool *p)
{
    server_rec *s = (server_rec *)pcalloc (p, sizeof (server_rec));

    s->port = DEFAULT_PORT;
    s->server_admin = DEFAULT_ADMIN;
    s->server_hostname = NULL; 
    s->error_fname = DEFAULT_ERRORLOG;
    s->error_log = stderr;
    s->srm_confname = RESOURCE_CONFIG_FILE;
    s->access_confname = ACCESS_CONFIG_FILE;
    s->timeout = DEFAULT_TIMEOUT;
    s->keep_alive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
    s->keep_alive_max = DEFAULT_KEEPALIVE;
    s->keep_alive = 1;
    s->next = NULL;
    s->addrs = pcalloc(p, sizeof (server_addr_rec));
    s->addrs->host_addr.s_addr = htonl (INADDR_ANY); /* NOT virtual host;
					       * don't match any real network
					       * interface.
					       */
    s->addrs->host_port = 0; /* matches any port */

    s->module_config = create_server_config (p, s);
    s->lookup_defaults = create_default_per_dir_config (p);

    return s;
}


static void default_listeners(pool *p, server_rec *s)
{
    listen_rec *new;

    if (listeners != NULL) {
	return;
    }
    /* allocate a default listener */
    new = pcalloc(p, sizeof(listen_rec));
    new->local_addr.sin_family = AF_INET;
    new->local_addr.sin_addr = bind_address;
    new->local_addr.sin_port = htons(s->port);
    new->fd = -1;
    new->used = 0;
    new->next = NULL;
    listeners = new;
}


server_rec *read_config(pool *p, pool *ptemp, char *confname)
{
    server_rec *s = init_server_config(p);
    
    init_config_globals(p);
    
    /* All server-wide config files now have the SAME syntax... */
    
    process_resource_config (s, confname, p, ptemp);
    process_resource_config (s, s->srm_confname, p, ptemp);
    process_resource_config (s, s->access_confname, p, ptemp);
    
    fixup_virtual_hosts (p, s);
    default_listeners (p, s);

    return s;
}
    

void init_modules(pool *p, server_rec *s)
{
    module *m;

    for (m = top_module; m; m = m->next)
        if (m->init)
	    (*m->init) (s, p);
}

void child_init_modules(pool *p, server_rec *s)
{
    module *m;

    for (m = top_module; m; m = m->next)
        if (m->child_init)
	    (*m->child_init) (s, p);
}

/********************************************************************
 * Configuration directives are restricted in terms of where they may
 * appear in the main configuration files and/or .htaccess files according
 * to the bitmask req_override in the command_rec structure.
 * If any of the overrides set in req_override are also allowed in the
 * context in which the command is read, then the command is allowed.
 * The context is determined as follows:
 *
 *    inside *.conf --> override = (RSRC_CONF|OR_ALL)&~(OR_AUTHCFG|OR_LIMIT);
 *    within <Directory> or <Location> --> override = OR_ALL|ACCESS_CONF;
 *    within .htaccess --> override = AllowOverride for current directory;
 *
 * the result is, well, a rather confusing set of possibilities for when
 * a particular directive is allowed to be used.  This procedure prints
 * in English where the given (pc) directive can be used.
 */
void show_overrides(command_rec *pc, module *pm)
{
    int n = 0;
    
    printf("\tAllowed in *.conf ");
    if ((pc->req_override & (OR_OPTIONS|OR_FILEINFO|OR_INDEXES)) ||
        ((pc->req_override & RSRC_CONF) &&
         ((pc->req_override & (ACCESS_CONF|OR_AUTHCFG|OR_LIMIT)))))
        printf("anywhere");
    else if (pc->req_override & RSRC_CONF)
        printf("only outside <Directory> or <Location>");
    else 
        printf("only inside <Directory> or <Location>");

    /* Warn if the directive is allowed inside <Directory> or .htaccess
     * but module doesn't support per-dir configuration */

    if ((pc->req_override & (OR_ALL|ACCESS_CONF)) && !pm->create_dir_config)
        printf(" [no per-dir config]");

    if (pc->req_override & OR_ALL) {
        printf(" and in .htaccess\n\twhen AllowOverride");

        if ((pc->req_override & OR_ALL) == OR_ALL)
            printf(" isn't None");
        else {
            printf(" includes ");

            if (pc->req_override & OR_AUTHCFG) {
                if (n++) printf(" or ");
                printf("AuthConfig");
            }
            if (pc->req_override & OR_LIMIT) {
                if (n++) printf(" or ");
                printf("Limit");
            }
            if (pc->req_override & OR_OPTIONS) {
                if (n++) printf(" or ");
                printf("Options");
            }
            if (pc->req_override & OR_FILEINFO) {
                if (n++) printf(" or ");
                printf("FileInfo");
            }
            if (pc->req_override & OR_INDEXES) {
                if (n++) printf(" or ");
                printf("Indexes");
            }
        }
    }
    printf("\n");
}

/* Show the preloaded configuration directives, the help string explaining
 * the directive arguments, in what module they are handled, and in
 * what parts of the configuration they are allowed.  Used for httpd -h.
 */
void show_directives()
{
    command_rec *pc;
    int n;
    
    for (n = 0; preloaded_modules[n]; ++n)
        for (pc = preloaded_modules[n]->cmds; pc && pc->name; ++pc) {
            printf("%s\n", pc->name);
            if (pc->errmsg)
                printf("\t%s\n", pc->errmsg);
            printf("\t%s\n", preloaded_modules[n]->name);
            show_overrides(pc, preloaded_modules[n]);
        }
}

/* Show the preloaded module names.  Used for httpd -l. */
void show_modules()
{
    int n;
 
    printf ("Compiled-in modules:\n");
    for (n = 0; preloaded_modules[n]; ++n)
        printf ("  %s\n", preloaded_modules[n]->name);
}

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