httpd-test-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From grega...@apache.org
Subject cvs commit: httpd-test/specweb99/specweb99-2.0 README config5.m4 mod_specweb99.c mod_specweb99.h
Date Thu, 02 May 2002 20:34:13 GMT
gregames    02/05/02 13:34:13

  Added:       specweb99 LICENSE.txt README httpd.specweb.conf rc.byrd_ap
               specweb99/specweb99-1.3 README STATUS mod_specweb99.c
                        mod_specweb99.h
               specweb99/specweb99-2.0 README config5.m4 mod_specweb99.c
                        mod_specweb99.h
  Log:
  initial version of specweb99.
  
  Revision  Changes    Path
  1.1                  httpd-test/specweb99/LICENSE.txt
  
  Index: LICENSE.txt
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2002 The Apache Software Foundation.  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. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 was contributed by Covalent Technologies Inc, 
   * http://www.covalent.net/ around April 2002.
   */
  
  
  
  1.1                  httpd-test/specweb99/README
  
  Index: README
  ===================================================================
  This is a module for the Apache web server that implements the dynamic
  content requirements for the SPECWeb99 benchmark from the Standard
  Performance Evaluation Corporation <http://www.spec.org/>. 
  
  Use of this module and disclosure of run results are subject to the Run
  Rules of SPECWeb99 which can be found in the SPECWeb99 distribution. 
  
  
  Requirements
  
  -   Apache
  
  -   SPECWeb99 suite
      wafgen99, cadgen99 and upfgen99 installed in docroot.
  
  -   A SPECWeb99 CGI script or program. Examples come with the
      SPECWeb99 distribution.
  
  1.3 1.3 version of the module; more complete
      than 2.0
  
  2.0 2.0 version of the module; only partly
      implemented. 
  
  
  
  
  1.1                  httpd-test/specweb99/httpd.specweb.conf
  
  Index: httpd.specweb.conf
  ===================================================================
  # Sample config file
  # XXXX 	User configurable things at the end.
  #      	This assumes waf/cad/upf-gen and the perl
  #	cgi script to be in docroot.
  #
  #
  LoadModule specweb99_module   mod_specweb99.so
  
  <Location /specweb99get>
          SetHandler specweb99get
  </Location>
  
  <Location /specweb99cadget>
      SetHandler specweb99cadget
  </Location>
  
  <Location /specweb99hk>
      SetHandler specweb99hk
  </Location>
  
  <Location /specweb99post>
      SetHandler specweb99post
  </Location>
  
  Alias	/specweb99cgi /specbweb99_cgi.pl
  
  AddHandler cgi-script	 .pl
  
  <Directory <docroot>/specweb99.pl>
  	Options +ExecCGI
  </Directory>
  
  
  
  
  1.1                  httpd-test/specweb99/rc.byrd_ap
  
  Index: rc.byrd_ap
  ===================================================================
  # Relevant section from the rc.* files for specweb.
  #
  CLIENTS=10.0.44.2[1]{10.0.43.30} 10.0.44.3[1]{10.0.43.30} 10.0.44.4[1]{10.0.43.30} 10.0.44.5[1]{10.0.43.30} 10.0.44.6[1]{10.0.43.30} 10.0.44.7[1]{10.0.43.30} 
  
  SERVER=10.0.44.40
  
  URL_ROOT=http://@@SERVER@@
  
  DYNAMIC_ROOT=http://@@SERVER@@/specweb99cgi
  DYN_GET_SCRIPT=http://@@SERVER@@/specweb99get
  DYN_CAD_SCRIPT=http://@@SERVER@@/specweb99cadget
  DYN_POST_SCRIPT=http://@@SERVER@@/specweb99post
  DYN_CMD_SCRIPT=http://@@SERVER@@/specweb99hk
  DYN_CGI_SCRIPT=http://@@SERVER@@/specweb99cgi
  
  
  
  
  1.1                  httpd-test/specweb99/specweb99-1.3/README
  
  Index: README
  ===================================================================
  
  Build procedure
  
  1.	Make sure apache 1.3 is compiled and installed
  
  2.	Generate a make file with
  
  		apxs -n specweb99 -g
  
  3.	Make, Install the specweb module.
  
  4.	Add something along the lines of
  	the httpd.specweb.conf to your
  	configuration. (Or use the include
  	directive to include it).
  
  5.	Make sure that wafgen99, cadgen99 and upfgen99
  	are in your docroot.
  
  6.	Note that according to the specweb rules
  	a certain percentage must be handled with
  	a CGI. See the end of the .conf file.
  
  
  
  
  
  1.1                  httpd-test/specweb99/specweb99-1.3/STATUS
  
  Index: STATUS
  ===================================================================
  
  April 2002.
  
  	1.00 	First rough release.
  
  Main TODO's
  	-	See code.
  	-	Make things more configurable.
  
  Note on coding style:
  
  	Some of it is distinctly un-apache like - to match that
  	of the specweb run rules examples. Furthermore certain
  	optimizations are out - again according to the run rules.
  
  
  
  1.1                  httpd-test/specweb99/specweb99-1.3/mod_specweb99.c
  
  Index: mod_specweb99.c
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2002 The Apache Software Foundation.  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. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 was contributed by Covalent Technologies Inc, 
   * http://www.covalent.net around April 20002.
   * 
   * mod_specweb99.c -- Apache  specweb99 module
   * sctemme July 2001
   */
  
  #include <stdio.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <sys/mman.h>
  #include <sys/time.h>
  #include <time.h>
  #include <sys/file.h>
  #include <fcntl.h>
  #include <sys/fcntl.h>
  #include <unistd.h>
  #include <errno.h>
  
  #include <mm.h>
  
  #ifdef SOLARIS2
  #include <strings.h>
  #endif
  
  #ifndef LOCK_SH
  /* normally in sys/file.h (in ucbinclude on solaris 2)*/
  #define LOCK_SH         1	/* shared lock */
  #define LOCK_EX         2	/* exclusive lock */
  #define LOCK_NB         4	/* don't block when locking */
  #define LOCK_UN         8	/* unlock */
  #endif
  
  #include "httpd.h"
  #include "http_core.h"
  #include "http_config.h"
  #include "http_log.h"
  #include "http_protocol.h"
  #include "http_main.h"
  #include "util_script.h"
  #include "ap_config.h"
  
  /* Set to augment fstat() based 'reset' checks. As per run rules
   * the file is still stat()-ed.
   */
  #define SEMCNTR
  
  #ifdef SEMCNTR
  #include <sys/ipc.h>
  #include <sys/sem.h>
  #define SEMKEY (5365200)
  typedef union semun {
      int val;
      struct semid_ds *buf;
      unsigned short *array;
  }     semun;
  #endif
  
  /* Note: version must be of the x.yy type - as it is
   * send over the http protocol wire; where x and y
   * are single 0-9 ascii digits :-). The name should
   * be an A-Za-z0-9 string. Which does not start with
   * a number :-) You want to avoid _ but '-' is not too
   * bad usually..
   */
  #define NAME "Specweb"
  #define VERSION "1.00"
  
  #include "mod_specweb99.h"
  
  module MODULE_VAR_EXPORT specweb99_module;
  
  enum ltype {
      SHLOCK, EXLOCK, UNLOCK
  };
  
  /*
   * Global variable: pointer to structure that holds pointers
   * to share memory buffers for user profiles and custom ad data.
   */
  MM *globalBuffer;
  bufs *rec_buffer;
  
  #define  _rlock(s,r,fg,file)	(_dolock(s,r,fg,SHLOCK,file))
  #define  _wlock(s,r,fg,file)	(_dolock(s,r,fg,EXLOCK,file))
  #define _unlock(s,r,fg,file)	(_dolock(s,r,fg,UNLOCK,file))
  
  /* on some OS-es (linux) fwrite(2c)/fwrite(2c) seem
   * to be able to ruturn with EINTR.
   */
  #define _WINTR(x) { while(((x)<0) && (errno == EINTR)) { /* NOP */ }; }
  
  static
  int _lockop(int fd, enum ltype type, int noblock)
  {
      int e;
  #ifdef SOLARIS2
      struct flock lock;
  
      if (type == EXLOCK)
  	lock.l_type = F_WRLCK;
      else if (type == SHLOCK)
  	lock.l_type = F_RDLCK;
      else
  	lock.l_type = F_UNLCK;
  
      lock.l_whence = SEEK_SET;
      lock.l_start = 0;
      lock.l_len = 0;
  
      /*
       * On Solaris and HPUX - fcntl returns -1 on error and just about any
       * other value on success. I.e. not always 0.
       */
      _WINTR(e = (fcntl(fd, (noblock) ? F_SETLK : F_SETLKW, &lock)));
  
      if (e == -1) {
  	if ((errno = EAGAIN) && (noblock))
  	    return -2;
  	return e;
      }
      return 0;
  #else
      int lock_type;
      if (type == SHLOCK)
  	lock_type = LOCK_SH;
      if (type == EXLOCK)
  	lock_type = LOCK_EX;
      else
  	lock_type = LOCK_UN;
  
      if (noblock)
  	lock_type |= LOCK_NB;
  
      _WINTR(e = flock(fd, lock_type));
      if (e) {
  	if ((errno == EWOULDBLOCK) && (noblock))
  	    return -2;
  	else
  	    return -1;
      }
  #endif
      return 0;
  }
  
  /* generic locking
   * - when 'r' is passed - will do a timeout.
   * - when 's' is passed - do appropriate logging.
   * - when 'file' is passed - logging will be more meaningfull
   */
  static
  int _dolock(struct server_rec *s, struct request_rec *r, FILE *f, enum ltype type, char *file)
  {
      int fd = fileno(f);
      int e;
      /*
       * Rather than simply try-lock-and-wait - we first check if a lock would
       * block - and then set a timeout before camping out on the lock.
       */
      if ((e = _lockop(fd, type, 1)) == -2) {
  	if (r)
  	    ap_hard_timeout("specweb lock", r);
  
  	if (s)
  	    ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, s,
  			 "Camping out %s%s for a %s lock",
  			 file ? "on " : "", file,
  			 ((type == EXLOCK) ? "write" : ((type == SHLOCK) ? "read" : "un")));
  
  	e = _lockop(fd, type, 0);
  
  	if (r)
  	    ap_kill_timeout(r);
      }
  
      /* Trap both first/second flock() error. */
      if (e) {
  	if (s)
  	    ap_log_error(APLOG_MARK, APLOG_ERR, s, "Failed to %s" "lock%s%s",
  			 ((type == EXLOCK) ? "write " : ((type == SHLOCK) ? "read " : "un")),
  			 file ? ": " : "", file);
  	return -1;
      }
      return 0;
  }
  
  
  /***********************************************************************
   * returnHTMLPageWithFile                                              *
   ***********************************************************************/
  
  static char *returnHTMLPageHead(request_rec *r, int len)
  {
      char *bp_head;
  
      /* Fill up the boilerplate with info */
      bp_head = ap_psprintf(r->pool, BOILERPLATE_START,
  			  ap_get_server_version(),
  			  ap_get_remote_host(r->connection,
  					     NULL,
  					     REMOTE_NOLOOKUP),
  			  r->uri,
  			  r->args ? r->args : "");
  
      ap_set_content_length(r, strlen(bp_head) + strlen(BOILERPLATE_END) + len);
      r->content_type = "text/html";
      ap_send_http_header(r);
  
      return bp_head;
  };
  
  /***********************************************************************
   * returnHTMLPageWithMessage                                           *
   ***********************************************************************/
  
  static void returnHTMLPageWithMessage(request_rec *r, char *fmt,...)
  {
      va_list args;
      char *m;
  
      va_start(args, fmt);
      m = ap_pvsprintf(r->pool, fmt, args);
      va_end(args);
  
      returnHTMLPageWithBuffer(r, m);
  }				/* returnHTMLPageWithMessage */
  
  
  /***********************************************************************
   * returnHTMLPageWithBuffer                                            *
   ***********************************************************************/
  
  static void returnHTMLPageWithBuffer(request_rec *r, char *buf)
  {
      char *bp_head;
  
      if (buf == NULL) {
  	returnHTMLPageWithMessage(r,
  			      "Error: tried to send buffer but it is NULL");
  	return;
      };
  
      bp_head = returnHTMLPageHead(r, strlen(buf));
  
      if (r->header_only)
  	return;
  
      ap_hard_timeout("send html page with buffer", r);
      ap_rputs(bp_head, r);
      ap_rputs(buf, r);		/* Hope this makes no assumptions on the
  				 * nature of the string it sends: I sure as
  				 * heck have no way of caring! */
      ap_rputs(BOILERPLATE_END, r);
  
      ap_kill_timeout(r);
  }				/* returnHTMLPageWithBuffer */
  
  static void returnHTMLPageWithFile(request_rec *r, FILE *f)
  {
      char *bp_head;
      struct stat s;
  
      /*
       * How big is the file? Use a regular file system call because ap_
       * doesn't provide one.
       */
      if ((fstat(fileno(f), &s))) {
  	returnHTMLPageWithMessage(r, "Error: Failed to stat the file");
  	return;
      }
  
      bp_head = returnHTMLPageHead(r, s.st_size);
  
      if (r->header_only)
  	return;
  
      ap_hard_timeout("send html page with file", r);
      ap_rputs(bp_head, r);
      ap_send_fd(f, r);
      ap_rputs(BOILERPLATE_END, r);
      ap_kill_timeout(r);
  }				/* returnHTMLPageWithFile */
  
  
  /***********************************************************************
   * checkUPFile                                                         *
   ***********************************************************************/
  
  int16_t checkUPFile(struct server_rec *sv, struct request_rec *r, struct specweb99_module_data * _my)
  {
      struct stat s;
      int16_t numrecords, up_uid;
      int e = 0;
      FILE *f;
      char up_record[UPRLENGTH];
  #ifdef SEMCNTR
      int semval;
  #endif
      /* stat it, compare to stored stat */
  
      if (stat(_my->up_path, &s) != 0) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, sv,
  		 "Could not stat User.Personality file '%s'", _my->up_path);
  	return 1;
      };
  
      if (EQMODTIME(s, _my->up_lastmod)) {
  #ifdef SEMCNTR
  	semun semv;
  #if 0
  	int id = semget(SEMKEY, 1, 0666);
  	if (id < 0) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Could not find sem in checkUPfile");
  	    return 1;
  	};
  #endif
  	if ((semctl(SEMKEY, _my->semid, GETVAL, &semv)) < 0) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Could not get sem in checkUPfile");
  	    return 1;
  	};
  #ifdef DEBUG
  	ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, sv,
  		     "Sem cntr %d == %d\n", semv.val, _my->semcntr);
  #endif
  	semval = semv.val;
  	if (semval == _my->semcntr)
  #endif
  	    return 0;
      }
  
      numrecords = s.st_size / 15;
      /*
       * Check buffer array for nullness and bigness, make if necessary.
       */
      if ((_my->up == NULL) || (numrecords > _my->up_count)) {
  	/* User personalities are only 32 bits (sad, really) */
  	ap_clear_pool(_my->up_pool);
  	_my->up = ap_palloc(_my->up_pool, numrecords * sizeof(u_int32_t));
  	_my->up_count = numrecords;
      }
      /*
       * open the file, with memory from the request pool because we will not
       * need it very long.
       */
      if ((f = fopen(_my->up_path, "r")) == NULL) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, sv,
  		 "Could not open User.Personality file '%s'", _my->up_path);
  	return 1;
      }
  
      if (_rlock(sv, r, f, _my->up_path)) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, sv,
  		 "Failed to lock User.Personality file '%s'", _my->up_path);
  	fclose(f);
  	return 1;
      }
  
      /* Read every record, parse, put user demographics in array */
      up_uid = 0;
      while (1) {
  	int id, dem;
  	_WINTR(e = fread((void *) up_record, sizeof(up_record), 1, f));
  	if (e != 1)
  	    break;		/* on error or EOF */
  
  	if (sscanf(up_record, "%d %x", &id, &dem) != 2) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, sv, "corrupted entry in U file");
  	    e = 1;
  	}
  
  	if (up_uid != id) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, sv, "user id out of sync in UP file");
  	    e = 1;
  	}
  	_my->up[up_uid] = dem;
  	up_uid++;
      };
  
      if (ferror(f)) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Failed read from User.Personality file '%s'", _my->up_path);
  	e = 1;
      }
  
      if (_unlock(sv, r, f, _my->up_path)) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Failed to unlock User.Personality file '%s'", _my->up_path);
  	e = 1;
      };
  
      /* Close file */
      fclose(f);
  
      /* Store last modified date assuming no errors. */
      if (!e) {
  	_my->up_lastmod = MODTIME(s);
  #ifdef DEBUG
  	    ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, sv,
  		  "checkUPFile: File re-read. Date=%ld", (long) MODTIME(s));
  #endif
      }
  
      return e;
  }				/* checkUPFile */
  
  
  /***********************************************************************
   * checkCADFile                                                        *
   ***********************************************************************/
  
  int16_t checkCADFile(struct server_rec *sv, struct request_rec *r, struct specweb99_module_data * _my)
  {
      struct stat s;
      size_t numrecords;
      FILE *f;
      char cadline[CADRLENGTH];
      u_int16_t cad_uid;
      int e = 0;
  #ifdef SEMCNTR
      int semval = 0;
  #endif
  
      /* Stat it, compare to stored modification time */
  
      if (stat(_my->cad_path, &s) != 0) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Failed to stat CAD file '%s'", _my->cad_path);
  	return 1;
      };
  
      if (EQMODTIME(s, _my->cad_lastmod)) {
  #ifdef SEMCNTR
  	/* also check sem's */
  	semun semv;
  #if 0
  	int id = semget(SEMKEY, 1, 0666);
  	if (id < 0) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Could not find sem in CADfile");
  	    return 1;
  	};
  #endif
  	if ((semctl(SEMKEY, _my->semid, GETVAL, &semv)) < 0) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Could not get sem in CADfile");
  	    return 1;
  	};
  #ifdef DEBUG
  	ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, sv,
  		     "Sem cntr %d == %d\n", semv.val, _my->semcntr);
  #endif
  	semval = semv.val;
  	if (semval == _my->semcntr)
  #endif
  	    return 0;
      }
  
      /*
       * Need to read file into memory - and re-allocate the array if the size
       * has changed.
       */
      numrecords = s.st_size / CADRLENGTH;
      if (numrecords > _my->cad_count) {
  	ap_clear_pool(_my->cad_pool);
  	_my->cad = ap_palloc(_my->cad_pool, numrecords * sizeof(struct cadrec));
  	_my->cad_count = numrecords;
      }
  
      f = fopen(_my->cad_path, "r");
      if (f == NULL) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Failed to open CAD file '%s'", _my->cad_path);
  	return 1;
      };
  
      if (_rlock(sv, r, f, _my->cad_path)) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Failed to lock CAD file '%s'", _my->cad_path);
  	return 1;
      }
  
      cad_uid = 0;
      while (cad_uid < numrecords) {
  	int l, id, dem, adw, adm, exp;
  	_WINTR(l = fread(cadline, CADRLENGTH, 1, f));
  	if (l != 1)
  	    break;		/* on EOF and on error */
  	/*
  	 * Decode AD file (see specweb page ..)
  	 *
  	 * 0123456789.123456789.123456789.12345678 01234 01234567 01234567 012
  	 * 0123456789n "%5d %8X %8X %3d %10d\n", Ad_id, AdDemographics,
  	 * Weightings, Minimum_Match_Value, Expiration_Time
  	 *
  	 */
  	if (sscanf(cadline, "%d %x %x %d %d", &id, &dem, &adw, &adm, &exp) != 5) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Entry CAD file corrupted");
  	    continue;
  	}
  
  	if (cad_uid != id) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Entry CAD file Id# out of sync");
  	    continue;
  	}
  	_my->cad[cad_uid].addemographics = dem;
  	_my->cad[cad_uid].gen_weightings = (adw & 0x00f0000) >> 16;
  	_my->cad[cad_uid].age_weightings = (adw & 0x000f000) >> 12;
  	_my->cad[cad_uid].reg_weightings = (adw & 0x0000f00) >> 8;
  	_my->cad[cad_uid].int1_weightings = (adw & 0x00000f0) >> 4;
  	_my->cad[cad_uid].int2_weightings = (adw & 0x000000f);
  	_my->cad[cad_uid].minimum_match_value = adm;
  	_my->cad[cad_uid].expiration_time = exp;
  	cad_uid++;
      }
  
      if (ferror(f)) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Failed to read from CAD file '%s'", _my->cad_path);
  	e = 1;
      };
  
      if (_unlock(sv, r, f, _my->cad_path)) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, sv, "Failed to unlock the CAD file '%s'", _my->cad_path);
  	e = 1;
      };
  
      fclose(f);
      if (!e) {
  	_my->cad_lastmod = MODTIME(s);
  #ifdef DEBUG
  	    ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, sv,
  		 "checkCADFile: File re-read. Date=%ld", (long) MODTIME(s));
  #endif
      }
  
      return e;
  }				/* checkCADFile */
  
  
  
  /***********************************************************************
   * specweb99_module_init                                               *
   ***********************************************************************/
  
  static void *specweb99_server_create(pool *p, server_rec *s)
  {
      struct specweb99_module_data *_my;
      _my = (struct specweb99_module_data *) ap_pcalloc(p, sizeof(struct specweb99_module_data));
  
  #ifdef SOLARIS2
      _my->up_lastmod = (time_t) 0L;
      _my->cad_lastmod = (time_t) 0L;
  #else
      _my->up_lastmod.tv_sec = _my->up_lastmod.tv_nsec = 0;
      _my->cad_lastmod.tv_sec = _my->cad_lastmod.tv_nsec = 0;
  #endif
  
      _my->up = NULL;
      _my->cad = NULL;
  
      _my->up_count = 0;
      _my->cad_count = 0;
  
  #ifdef SEMCNTR
      /*
       * XXX not sure if this is correct. The code I stole this from did not
       * fork; i.e. we only get the sem id here; and then store it. That might
       * not be good enough; i.e. we might have to do a semget() each time; or
       * at fork time.
       */
      {
  	semun argument;
  	/* make sure that we have a semaphore. */
  	if ((_my->semid = semget(SEMKEY, 1, 0666 | IPC_CREAT)) < 0) {
  	    ap_log_error(APLOG_MARK, APLOG_CRIT, s, "Failed to create semaphore");
  	    return NULL;
  	};
  
  	_my->semcntr = 0;
  	argument.val = _my->semcntr + 1;	/* make sure they are not the
  						 * same ! */
  	if ((semctl(_my->semid, 0, SETVAL, &argument)) < 0) {
  	    ap_log_error(APLOG_MARK, APLOG_CRIT, s, "Failed to set semaphore");
  	    return NULL;
  	};
      };
  #endif
  
      return (void *) _my;
  }
  
  static void specweb99_module_init(server_rec *s, ap_pool * p)
  {
      char fname[255];
  
      ap_add_version_component(NAME "/" VERSION);
  
      ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s,
        NAME "/" VERSION " module: Compiled on %s at %s", __DATE__, __TIME__);
  
      ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s, "%s",
  	      "$Id: mod_specweb99.c,v 1.1 2002/05/02 20:34:13 gregames Exp $");
  
      /*
       * Make a shared memory buffer to hold all our User Profile and Custom
       * Ads activity. The size of this buffer is hardwired for the maximum
       * number of SPECWeb connections we expect to handle. This is
       * unfortunate, but I don't really want to deal with multiple shared
       * memory buffers at this time.
       */
      sprintf(fname, "/tmp/specweb99buf.%ld", getpid());
      globalBuffer = mm_create(
  			     sizeof(bufs) +
  			     sizeof(u_int32_t) * MAXUSERS +
  			     360 * CADRLENGTH,
  			     fname);
      if (globalBuffer == NULL) {
  	/* What do we do from the init? */
  	ap_log_error(APLOG_MARK, APLOG_EMERG | APLOG_NOERRNO, s,
  		     "Couldn't allocate shared memory: mm error %s",
  		     mm_error());
  	exit(1);
      }
  #ifdef DEBUG
      ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s,
  		 "Allocated shared memory at %x", globalBuffer);
      mm_display_info(globalBuffer);
  #endif
      rec_buffer = mm_malloc(globalBuffer, sizeof(bufs));
      if (rec_buffer == NULL) {
  	ap_log_error(APLOG_MARK, APLOG_EMERG | APLOG_NOERRNO, s,
  	       "Couldn't allocate rec_buffer in shared memory: mm error %s",
  		     mm_error());
  	exit(1);
      }
      rec_buffer->cad_buffer = NULL;
      rec_buffer->up_buffer = NULL;
  
  }				/* specweb99_module_init */
  
  static void specweb99_child_init(server_rec *s, ap_pool * p)
  {
      struct specweb99_module_data *_my =
      ap_get_module_config(s->module_config, &specweb99_module);
      struct request_rec r;
      const char *docroot;
  
      r.server = s;
      docroot = ap_document_root(&r);
  
      _my->up_path = ap_make_full_path(p, docroot, "User.Personality");
      _my->cad_path = ap_make_full_path(p, docroot, "Custom.Ads");
      _my->log_path = ap_make_full_path(p, docroot, "post.log");
      _my->upfgen99 = ap_make_full_path(p, docroot, "upfgen99");
      _my->cadgen99 = ap_make_full_path(p, docroot, "cadgen99");
  
      _my->up_pool = ap_make_sub_pool(p);
      _my->cad_pool = ap_make_sub_pool(p);
  
      if (s->next) {
  	fprintf(stderr,
  		"WARNING- this specweb module currently does not support vhosts/services\n"
  		"See %s:%d for what you need to change. The server will continue and assume\n"
  		"the config of the base server\n", __FILE__, __LINE__ + 2);
  
  	/*
  	 * Right now we assume you are specwebbing a whole server install -
  	 * as opposed to a host:port:protocol instance tied to a virtual
  	 * service.
  	 *
  	 * To support vhosts - the _my module config needs simply to be moved to
  	 * the per server config block (or the per dir block) and the init
  	 * and/or any access to it need to either go through the ->nxt list
  	 * OR carefull overlay merging needs to be done to a sensible default
  	 * for each of the cases. The current simplistic 'docroot' references
  	 * are propably no longer going to work and will need explicit config
  	 * (e.g. think ~user and other redirect cases with clobber the
  	 * concept of a docroot).
  	 */
      };
  
  #ifdef DEBUG
      ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, s,
  		 "Child %ld initialized. MM at %x and rec_buffer at %x.",
  		 getpid(),
  		 globalBuffer,
  		 rec_buffer);
  #endif
  
  }				/* specweb99_module_init */
  
  
  /***********************************************************************
   * call_specweb_util                                                   *
   ***********************************************************************/
  
  static int call_specweb_util(void *rp, child_info * pinfo)
  {
      char **env;
      int child_pid;
      request_rec *r = (request_rec *) rp;
  
      env = (char **) ap_create_environment(r->pool, r->subprocess_env);
      ap_error_log2stderr(r->server);
      ap_cleanup_for_exec();
  
      if (!r->args) {
  	ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO,
  	r->server, "No rargs: %p %p", r->prev, r->prev ? r->prev->prev : 0);
  	r->args = r->prev->args;
      };
      child_pid = ap_call_exec(r, pinfo, r->filename, env, 0);
  
  #ifdef WIN32
      return (child_pid);
  #else
      ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "exec of %s failed", r->filename);
      exit(0);
      /* NOT REACHED */
      return (0);
  #endif
  }				/* call_specweb_util */
  
  
  /***********************************************************************
   * specweb99_hk_handler                                                *
   ***********************************************************************/
  
  static int specweb99_hk_handler(request_rec *r)
  {
      struct specweb99_module_data *_my =
      ap_get_module_config(r->server->module_config, &specweb99_module);
      FILE *f;
      char *data, *key, *val, *line, *expcomma, *newexp = "<unset>";
      table *tab;
      size_t err;
      int16_t maxload, maxthread;
      int32_t pointtime;
      char *exp, *urlroot, *rootdir;
      const char *docroot = ap_document_root(r);
      uri_components *urlrootrec;	/* To parse the urlroot string into */
  
      int result;			/* To keep mm_(un)lock result in */
      char cadline[CADRLENGTH], up_record[UPRLENGTH];
      u_int16_t cad_uid, up_uid;
  
      specweb99_info(r->server,
        ap_psprintf(r->pool, "Housekeeping routine called with %s", r->args));
  
      if (strstr(r->args, "command/Fetch") != NULL) {
  	f = ap_pfopen(r->pool, _my->log_path, "r");
  	if (f == NULL) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Could no open post.log file '%s' for reading", _my->log_path);
  	    returnHTMLPageWithMessage(r, "Error: could not open post.log for reading.");
  	    return OK;
  	}
  	returnHTMLPageWithFile(r, f);
  	ap_pfclose(r->pool, f);
  	return OK;
      }
      else if ((data = strstr(r->args, "command/Reset"))) {
  	/*
  	 * 1 012345678901234
  	 * command/Reset&maxload=[MaxLoad]&pttime=[PointTime]&maxthreads=[
  	 * MaxThreads]&exp=[ExpiredList]&urlroot=[UrlRoot]
  	 */
  	data += 14;		/* position at start of argument string */
  	/* Tokenize argument string */
  	tab = ap_make_table(r->pool, 0);
  
  	while (*data && (val = ap_getword(r->pool,
  					  (const char **) &data,
  					  '&'))) {
  	    key = ap_getword(r->pool, (const char **) &val, '=');
  	    ap_unescape_url(key);
  	    ap_unescape_url(val);
  	    ap_table_set(tab, key, val);
  	}
  	/* Put arguments in variables */
  	maxload = atoi(ap_table_get(tab, "maxload"));
  	pointtime = atol(ap_table_get(tab, "pttime"));
  	/*
  	 * The Run Rules pseudocode is ambivalent about this token name: the
  	 * pseudocode says 'maxthreads' but its test command a couple of
  	 * lines down says 'maxthread'. Aside from the question whether we
  	 * should at all pay attention to the token names, I'm going along
  	 * with what the manager script sends which is 'maxthread'.
  	 */
  	maxthread = atoi(ap_table_get(tab, "maxthread"));
  	exp = ap_psprintf(r->pool, "%s", ap_table_get(tab, "exp"));
  	/*
  	 * OK, this vexes me. Every shred of documentation about SPECWeb
  	 * speaks of a comma-separated list of expired ads, but the cadgen99
  	 * program segfaults if you pass anything but a whitespace- separated
  	 * list. The Run Rules explicitly state that the pseudo code is the
  	 * definitive Reference By Which This Module Shall Be Coded, yet I
  	 * had to yank the following gem from the perl script:
  	 */
  	if ((expcomma = strstr(exp, ",")) != NULL) {
  #if _DEBUG
  	    newexp = ap_psprintf(r->pool, "%d %d", atoi(exp), atoi(expcomma + 1));
  #else
  	    newexp = ap_psprintf(r->pool, "%d %d", atoi(exp), atoi(expcomma + 1));
  #endif
  	};
  
  	urlroot = ap_psprintf(r->pool, "%s", ap_table_get(tab, "urlroot"));
  
  	/*
  	 * Prep: we got a URI from the request. Need to parse that, extract
  	 * the local part and tack that onto docroot.
  	 */
  	urlrootrec = ap_palloc(r->pool, sizeof(uri_components));
  	if (ap_parse_uri_components(r->pool, urlroot, urlrootrec) !=HTTP_OK) {
  	    ap_log_error(APLOG_MARK, APLOG_NOERRNO, r->server, "The URL Root '%s' was invalid", urlroot);
  	    returnHTMLPageWithMessage(r, "The UrlRoot passed was invalid");
  	    return OK;
  	}
  	rootdir = ap_os_escape_path(r->pool,
  				    ap_make_full_path(r->pool,
  						      docroot,
  						      urlrootrec->path),
  				    0);
  
  	/*
  	 * Remove Custom.Ads and User.Personality files before regenerating
  	 * them. This will reveal any problems running the aux programs
  	 */
  	if (unlink(_my->up_path) != 0) {
  	    ap_log_error(APLOG_MARK,
  			 APLOG_WARNING,
  			 r->server,
  			 "Could not delete file %s",
  			 _my->up_path);
  	}
  
  	if (unlink(_my->cad_path) != 0) {
  	    ap_log_error(APLOG_MARK,
  			 APLOG_WARNING,
  			 r->server,
  			 "Could not delete file %s",
  			 _my->cad_path);
  	}
  
  #if 1
  	{
  	    int e;
  	    char *cmd = ap_psprintf(r->pool,
  				    "%s -n %d -t %d -C %s", _my->upfgen99, maxload, maxthread, rootdir);
  
  	    ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, r->server, "Runnng %s", cmd);
  
  	    e = system(cmd);
  
  	    switch (e) {
  	    case 127:
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "couldn't start shell for %s", cmd);
  		returnHTMLPageWithMessage(r, "Couldn't spawn child process upfgen99");
  		return OK;
  		break;
  	    case -1:
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "couldn't fork()/waitpid() for %s", cmd);
  		returnHTMLPageWithMessage(r, "Couldn't spawn child process upfgen99");
  		return OK;
  		break;
  	    default:
  		if ((!(WIFEXITED(e))) || (WEXITSTATUS(e))) {
  		    ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "CMD %s failed", cmd);
  		    returnHTMLPageWithMessage(r, "Couldn't spawn child process upfgen99");
  		    return OK;
  		}
  		break;
  	    }
  	}
  	{
  	    int e;
  	    char *cmd = ap_psprintf(r->pool, "%s -C %s -e %ld -t %d %s",
  	    _my->cadgen99, rootdir, (long int) pointtime, maxthread, newexp);
  
  	    ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, r->server, "Runnng %s", cmd);
  
  	    e = system(cmd);
  
  	    switch (e) {
  	    case 127:
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "couldn't start shell for %s", cmd);
  		returnHTMLPageWithMessage(r, "Couldn't spawn child process cadgen");
  		return OK;
  		break;
  	    case -1:
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "couldn't fork()/waitpid() for %s", cmd);
  		returnHTMLPageWithMessage(r, "Couldn't spawn child process cadgen99");
  		return OK;
  		break;
  	    default:
  		if ((!(WIFEXITED(e))) || (WEXITSTATUS(e))) {
  		    ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "CMD %s failed", cmd);
  		    returnHTMLPageWithMessage(r, "Couldn't spawn child process cadgen99");
  		    return OK;
  		}
  		break;
  	    }
  	}
  #else
  	{
  	    char *saveargs;
  	    /* Call upfgen and cadgen */
  	    r->filename = _my->upfgen99;
  	    /*
  	     * Keep request arguments around, we need them for eventual response
  	     */
  	    saveargs = ap_pstrdup(r->pool, r->args);
  
  	    r->args = ap_psprintf(r->pool,
  				  "-n+%d+-t+%d+-C+%s",
  				  maxload,
  				  maxthread,
  				  rootdir);
  #ifdef DEBUG
  	    specweb99_debug(r->server, ap_psprintf(r->pool, "Calling %s",
  						   r->filename));
  #endif
  	    if (!ap_bspawn_child(r->pool,
  				 call_specweb_util,
  				 (void *) r,
  				 kill_after_timeout,
  				 NULL,
  				 NULL,
  				 NULL)) {
  		ap_log_error(APLOG_MARK,
  			     APLOG_ERR,
  			     r->server,
  			     "couldn't spawn child process: %s",
  			     r->filename);
  		r->args = saveargs;
  		returnHTMLPageWithMessage(r, "Couldn't spawn child process upfgen99");
  		return OK;
  	    }
  
  	    r->filename = _my->cadgen99;
  	    r->args = ap_psprintf(r->pool,
  				  "-C+%s+-e+%ld+-t+%d+%s",
  				  rootdir,
  				  (long int) pointtime,
  				  maxthread,
  				  newexp);
  #ifdef DEBUG
  	    specweb99_debug(r->server, ap_psprintf(r->pool, "Calling %s",
  						   r->filename));
  #endif
  	    if (!ap_bspawn_child(r->pool,
  				 call_specweb_util,
  				 (void *) r,
  				 kill_after_timeout,
  				 NULL,
  				 NULL,
  				 NULL)) {
  		ap_log_error(APLOG_MARK,
  			     APLOG_ERR,
  			     r->server,
  			     "couldn't spawn child process: %s",
  			     r->filename);
  
  		r->args = saveargs;
  		returnHTMLPageWithMessage(r, "Couldn't spawn child process upfgen99");
  		return OK;
  	    }
  	    r->args = saveargs;
  	}
  #endif
  	/*
  	 * We are sleeping at least one second - to make sure that any
  	 * fstat() on mtime will actually yeild different values - no matter
  	 * how closely spaced the Reset's are issued. (in particular the
  	 * spacing between the test reset form the manager and the reset at
  	 * the commencing - which normally can be within a second - thus
  	 * having identical mtime's on platforms with second granuaarity
  	 * (Solaris,Lnux).
  	 */
  	sleep(2);
  
  /*
   * Now that we have run the two auxillary programs, read
   * the results back into our shared memory buffer.
   */
  
  /*
   * We want absolute exclusive access to this buffer during
   * this exercise. Wonder what any other processes do when
   * they try to read the buffer. Do they camp out?
   * Undocumented: mm_lock and mm_unlock return TRUE or FALSE
   * that is 1 or 0: presumably TRUE or 1 means the lock was
   * acquired/released correctly.
   */
  	    result = mm_lock(globalBuffer, MM_LOCK_RW);
  	if (result == 0) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server,
  		       "Locking shared memory segment failed! MM error: %s",
  			 mm_error());
  	    return HTTP_INTERNAL_SERVER_ERROR;
  	}
  
  /*
   * If the Custom Ads buffer does not exist, allocate it. If not,
   * reallocate. Reallocating should not upset things because the size
   * never changes. If the location changes, we might have to stop
   * doing reallocation to avoid fragmentation
   */
  	if (rec_buffer->cad_buffer == NULL) {
  	    rec_buffer->cad_buffer = mm_malloc(globalBuffer, CADRLENGTH * 360);
  	    if (rec_buffer->cad_buffer == NULL) {
  		ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server,
  			     "Allocating CAD buffer in shared memory failed! MM error: %s",
  			     mm_error());
  		return HTTP_INTERNAL_SERVER_ERROR;
  	    }
  	}
  	else {
  	    rec_buffer->cad_buffer = mm_realloc(globalBuffer,
  						rec_buffer->cad_buffer,
  						CADRLENGTH * 360);
  	    if (rec_buffer->cad_buffer == NULL) {
  		ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server,
  			     "Reallocating CAD buffer in shared memory failed! MM error: %s",
  			     mm_error());
  		return HTTP_INTERNAL_SERVER_ERROR;
  	    }
  	}
  
  /*
   * Same for User Profile array. This changes size for practically every
   * run. Gets size parameter from the command/Reset parameter string.
   */
  	if (rec_buffer->up_buffer == NULL) {
  	    rec_buffer->up_buffer = mm_malloc(globalBuffer, maxload * sizeof(u_int32_t));
  	    if (rec_buffer->up_buffer == NULL) {
  		ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server,
  		"Allocating UP buffer in shared memory failed! MM error: %s",
  			     mm_error());
  		return HTTP_INTERNAL_SERVER_ERROR;
  	    }
  	}
  	else {
  	    rec_buffer->up_buffer = mm_realloc(globalBuffer,
  					       rec_buffer->up_buffer,
  					       maxload * sizeof(u_int32_t));
  	    if (rec_buffer->up_buffer == NULL) {
  		ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server,
  			     "Reallocating UP buffer in shared memory failed! MM error: %s",
  			     mm_error());
  		return HTTP_INTERNAL_SERVER_ERROR;
  	    }
  	}
  
  /* Open CAD file, read, parse and stuff into buffer, close. */
  	f = fopen(_my->cad_path, "r");
  	if (f == NULL) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Failed to open CAD file '%s'", _my->cad_path);
  	    return HTTP_INTERNAL_SERVER_ERROR;
  	};
  
  	if (_rlock(r->server, r, f, _my->cad_path)) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Failed to lock CAD file '%s'", _my->cad_path);
  	    return HTTP_INTERNAL_SERVER_ERROR;
  	}
  
  	cad_uid = 0;
  	result = 0;
  	while (cad_uid < NUMCADRECORDS) {
  	    int l, id, dem, adw, adm, exp;
  	    _WINTR(l = fread(cadline, CADRLENGTH, 1, f));
  	    if (l != 1)
  		break;		/* on EOF and on error */
  	    /*
  	     * Decode AD file (see specweb page ..)
  	     *
  	     * 0123456789.123456789.123456789.12345678 01234 01234567 01234567
  	     * 012 0123456789n "%5d %8X %8X %3d %10d\n", Ad_id,
  	     * AdDemographics, Weightings, Minimum_Match_Value,
  	     * Expiration_Time
  	     *
  	     */
  	    if (sscanf(cadline, "%d %x %x %d %d", &id, &dem, &adw, &adm, &exp) != 5) {
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Entry CAD file corrupted");
  		continue;
  	    }
  
  	    if (cad_uid != id) {
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  			     "Entry CAD file Id# out of sync");
  		continue;
  	    }
  	    rec_buffer->cad_buffer[cad_uid].addemographics = dem;
  	    rec_buffer->cad_buffer[cad_uid].gen_weightings =
  		(adw & 0x00f0000) >> 16;
  	    rec_buffer->cad_buffer[cad_uid].age_weightings =
  		(adw & 0x000f000) >> 12;
  	    rec_buffer->cad_buffer[cad_uid].reg_weightings =
  		(adw & 0x0000f00) >> 8;
  	    rec_buffer->cad_buffer[cad_uid].int1_weightings =
  		(adw & 0x00000f0) >> 4;
  	    rec_buffer->cad_buffer[cad_uid].int2_weightings =
  		(adw & 0x000000f);
  	    rec_buffer->cad_buffer[cad_uid].minimum_match_value = adm;
  	    rec_buffer->cad_buffer[cad_uid].expiration_time = exp;
  	    cad_uid++;
  	}
  
  	if (ferror(f)) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Failed to read from CAD file '%s'", _my->cad_path);
  	    result = 1;
  	};
  
  	if (_unlock(r->server, r, f, _my->cad_path)) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Failed to unlock the CAD file '%s'", _my->cad_path);
  	    result = 1;
  	};
  
  	fclose(f);
  	if (result) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  			 "Error occurred during CAD read");
  	}
  
  	_my->cad_count = NUMCADRECORDS;	/* It's constant. Keeping it around
  					 * in a variable is not really
  					 * necessary and might someday be
  					 * elliminated. */
  
  /* Open UP file */
  /* Read lines, parse and stuff into buffer */
  /* Close UP file */
  	/*
  	 * open the file, with memory from the request pool because we will
  	 * not need it very long.
  	 */
  	if ((f = fopen(_my->up_path, "r")) == NULL) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  		 "Could not open User.Personality file '%s'", _my->up_path);
  	    return HTTP_INTERNAL_SERVER_ERROR;
  	}
  
  	if (_rlock(r->server, r, f, _my->up_path)) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  		 "Failed to lock User.Personality file '%s'", _my->up_path);
  	    fclose(f);
  	    return HTTP_INTERNAL_SERVER_ERROR;
  	}
  
  	/* Read every record, parse, put user demographics in array */
  	up_uid = 0;
  	while (1) {
  	    int id, dem;
  	    _WINTR(result = fread((void *) up_record, sizeof(up_record), 1, f));
  	    if (result != 1)
  		break;		/* on error or EOF */
  
  	    if (sscanf(up_record, "%d %x", &id, &dem) != 2) {
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "corrupted entry in U file");
  		result = 1;
  	    }
  
  	    if (up_uid != id) {
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "user id out of sync in UP file");
  		result = 1;
  	    }
  	    rec_buffer->up_buffer[up_uid] = dem;
  	    up_uid++;
  	};
  
  	if (ferror(f)) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Failed read from User.Personality file '%s'", _my->up_path);
  	    result = 1;
  	}
  
  	if (_unlock(r->server, r, f, _my->up_path)) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Failed to unlock User.Personality file '%s'", _my->up_path);
  	    result = 1;
  	};
  
  	/* Close file */
  	fclose(f);
  
  	rec_buffer->up_count = up_uid + 1;	/* User ID is zero-based */
  
  
  /* We're done, let the other children in again. */
  	result = mm_unlock(globalBuffer);
  	if (result == 0) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server,
  		     "Unlocking shared memory segment failed! MM error: %s",
  			 mm_error());
  	    return HTTP_INTERNAL_SERVER_ERROR;
  	}
  
  /* Reset post.log */
  	    f = ap_pfopen(r->pool, _my->log_path, "w");	/* Truncate, open for
  							 * writing */
  	if (f == NULL) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Could not open post.log '%s' for writing", _my->log_path);
  	    returnHTMLPageWithMessage(r, "Error: couldn't open post.log for writing.");
  	    return OK;
  	}
  	line = ap_psprintf(r->pool, "%10d\n", 0);
  	_WINTR(err = fwrite(line, strlen(line), 1, f));
  	if (err == -1) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Could not write to post.log '%s'", _my->log_path);
  	    returnHTMLPageWithMessage(r, "Error: could not write to post.log.");
  	}
  	else {
  	    returnHTMLPageWithMessage(r, "");	/* No news is good news. */
  	}			/* Write  to post.log */
  	ap_pfclose(r->pool, f);
  
  #ifdef SEMCNTR
  	{
  	    struct sembuf ops[1];
  #if 0
  	    /* get our counter. */
  	    int id;
  	    if ((id = semget(SEMKEY, 0, 0666)) < 0) {
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Could not get sem");
  		returnHTMLPageWithMessage(r, "Error: could not get sem");
  	    };
  #endif
  	    /* and increment */
  	    ops[0].sem_num = 0;
  	    ops[0].sem_op = 1;
  	    ops[0].sem_flg = 0;
  	    if ((semop(_my->semid, ops, 1)) < 0) {
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Could not inc sem");
  		returnHTMLPageWithMessage(r, "Error: could not get sem");
  	    };
  	    ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "Inced the sem");
  	}
  #endif
  
  	return OK;
      }				/* Reset Command */
  
      /* Fall through */
      returnHTMLPageWithMessage(r, "Error: unrecognized command '%s'", r->args);
      return OK;
  }
  
  
  /***********************************************************************
   * specweb99_get_handler                                               *
   ***********************************************************************/
  
  static int specweb99_get_handler(request_rec *r)
  {
      char *path;
      FILE *thefile;
      const char *docroot = ap_document_root(r);
      /*
       * This should not be tested for every request. Eventually, this code
       * will find its way to the housekeeping command handler, but that does
       * not exist yet.
       */
  
      /*
       * Construct the path to our file. Note that using ap_document_root() is
       * not senang. I should do this a subrequest but OTOH that would take
       * time and we don't have time.
       */
      path = ap_make_full_path(r->pool, docroot, r->args);
  
      /* use ap_ call to open the file */
      if (!(thefile = ap_pfopen(r->pool, path, "r"))) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  		     "Could not open File '%s' (get handler)", path);
  	returnHTMLPageWithMessage(r, "Could not open '%s': %s",
  				  path, strerror(errno));
  	return HTTP_INTERNAL_SERVER_ERROR;
      };
  
      returnHTMLPageWithFile(r, thefile);
      ap_pfclose(r->pool, thefile);
  
      return OK;
  }				/* specweb99_get_handler */
  
  
  
  /***********************************************************************
   * customadscan                                                        *
   ***********************************************************************/
  
  caddr_t customadscan(request_rec *r, FILE *f, int16_t adid)
  {
      struct stat s;
      int16_t fd, i;
      size_t len;
      char *index, *N, *X, *Y;
      char *buf;
      /* pinpoint the file from the ad ID */
      N = ap_psprintf(r->pool, "%05d", adid / 36);
      X = ap_psprintf(r->pool, "%1d", (adid % 36) / 9);
      Y = ap_psprintf(r->pool, "%1d", adid % 9);
      /* File is already open */
      fd = fileno(f);
      if (fstat(fd, &s) == -1)
  	return NULL;
  
      len = (size_t) s.st_size;
      buf = ap_palloc(r->pool, len + 1);
  
      if (len) {
  	char *bufp = buf;
  	int l, left = len;
  	do {
  	    l = read(fd, bufp, left);
  	    if (l == -1) {
  		if ((errno == EINTR) || (errno == EAGAIN))
  		    continue;
  		return NULL;	/* Error on read */
  	    }
  	    else if (l == 0)
  		return NULL;	/* Premature end */
  	    left -= l;
  	    bufp += l;
  	} while (left);
      }
      buf[len] = '\0';		/* Null-terminate it so that everybody else
  				 * who has to handle this knows how long the
  				 * buffer is. */
      index = buf;
      /*
       * It says in the run rules that we are to scan until the end of the
       * file... what if there are more than one occurrence of the ad (common
       * disease on todays web pages)?
       */
      while ((index = strstr(index, MARKER)) != NULL) {
  	/* This lands us a new index */
  /* <!WEB99CAD><IMG SRC="/file_set/dirNNNNN/classX_Y">
   * 01234567890123456789012345678901234567890123456789
   *           1         2         3         4          */
  	for (i = 0; i < 5; i++) {
  	    *(index + 34 + i) = N[i];
  	}
  	*(index + 45) = *X;
  	*(index + 47) = *Y;
  	index += 50;		/* Put the index past this marker, continue
  				 * scanning */
      }
      return buf;
  
  }				/* customadscan */
  
  
  /***********************************************************************
   * specweb99_cadget_handler                                            *
   ***********************************************************************/
  
  static int specweb99_cadget_handler(request_rec *r)
  {
      struct specweb99_module_data *_my =
      ap_get_module_config(r->server->module_config, &specweb99_module);
      char *cookie_in, *cookie_out, *end;
      const char *docroot = ap_document_root(r);
      char *filename;
      int16_t my_user, last_ad, userindex, adindex, expired = 0;
      u_int32_t userdemographics, combineddemographics;	/* it's a bitmap */
      u_int16_t ad_weight;
      FILE *page;
  
      /*
       * This should not be tested for every request. Eventually, this code
       * will find its way to the housekeeping command handler, but that does
       * not exist yet.
       */
      /* Get the cookie */
      cookie_in = (char *) ap_table_get(r->headers_in, "Cookie");
  #ifdef DEBUG
      specweb99_debug(r->server, ap_psprintf(r->pool,
  			   "Got cadget request, cookie is: %s", cookie_in));
  #endif
  
      /*
       * XXX Again, ap_document_root is deprecated. I should probably find the
       * document root in my init handler and keep it around.
       */
      filename = ap_make_full_path(r->pool, docroot, r->args);
  
  #ifdef DEBUG
      ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r->server,
  		 "Full path is '%s'", filename);
  #endif
  
      if ((page = ap_pfopen(r->pool, filename, "r")) == NULL) {
  	returnHTMLPageWithMessage(r, "File '%s' not found", filename);
  	ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  		     "Could not open File '%s' (cadget handler)", filename);
  	return OK;
      }
      /*
       * Parse Cookie string into MyUser and Last_Ad. The format of the cookie
       * is as follows (the order of keys and values is fixed): 1         2 3 4
       * 5 012345678901234567890123456789012345678901234567890123456789
       * my_cookie=user_id=[MyUser]&last_ad=[Last_ad]
       */
      if (!cookie_in) {
  	ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server,
  		     "Error: expected cookie but found none");
  	returnHTMLPageWithMessage(r, "Error: expected cookie but found none");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }				/* Is there a cookie */
  
      /* Parse the incoming Cookie data */
      /* Format of input: user_id=<x>&last_ad=<y> */
      my_user = strtol(cookie_in + 18, &end, 10);
      last_ad = atoi(end + 9);	/* We trust that there is something behind
  				 * the last_ad value to stop the conversion */
  #ifdef DEBUG
      specweb99_debug(r->server, ap_psprintf(r->pool,
  					"UserID: %d, LastAd: %d, Valid: %s",
  					   my_user,
  					   last_ad,
  					(cookie_in == end) ? "no" : "yes"));
  #endif
  
      /*
       * Calculate UserIndex into User.Personality file UserIndex = MyUser -
       * 10000
       */
      userindex = my_user - 10000;
  
      /*
       * Find User.Personality record using UserIndex
       */
  
  #if 0
      if (checkUPFile(r->server, r, _my)) {
  	ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server,
  		     "User personality check failed.");
  	returnHTMLPageWithMessage(r, "Error: User personality file check failed.");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  #endif
  
      if (userindex < 0 || userindex >= rec_buffer->up_count) {
  	/* Couldn't find it, so let's make our mark and leave */
  #ifdef DEBUG
  	specweb99_debug(r->server, "User record not found");
  #endif
  	returnHTMLPageWithMessage(r, "User Record %d not found (out of my current range %d .. %d)",
  		userindex + 10000, 10000, rec_buffer->up_count + 10000 - 1);
  	ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server,
  	      "User Record %d not found (out of my current range %d .. %d)",
  		userindex + 10000, 10000, rec_buffer->up_count + 10000 - 1);
  	return OK;
      }
  
      /* If case, old behaviour. Else, use shm buffer */
  #if 0
      userdemographics = _my->up[userindex];
  #else
      userdemographics = rec_buffer->up_buffer[userindex];
  #endif
  
  #if 0
      if (checkCADFile(r->server, r, _my)) {
  	returnHTMLPageWithMessage(r, "Error: Ad file check failed.");
  	ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server,
  		     "Ad file check failed");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  #endif
  
      adindex = (last_ad + 1) % 360;
  /*
      Do For Each Ad in Custom.Ads starting where Ad_index == Ad_id
  */
      while (1) {
  #if 0
  	/* This is the old per-process buffer case */
  	/* CombinedDemographics = ( AdDemographics & UserDemographics ) */
  	combineddemographics = (_my->cad[adindex].addemographics) & userdemographics;
  	/* Ad_weight = 0 */
  	ad_weight = 0;
  	if (combineddemographics & GENDER_MASK) {
  	    ad_weight += _my->cad[adindex].gen_weightings;
  	}
  	if (combineddemographics & AGE_GROUP_MASK) {
  	    ad_weight += _my->cad[adindex].age_weightings;
  	}
  	if (combineddemographics & REGION_MASK) {
  	    ad_weight += _my->cad[adindex].reg_weightings;
  	}
  	if (combineddemographics & INTEREST1_MASK) {
  	    ad_weight += _my->cad[adindex].int1_weightings;
  	}
  	if (combineddemographics & INTEREST2_MASK) {
  	    ad_weight += _my->cad[adindex].int2_weightings;
  	}
  	if (ad_weight >= _my->cad[adindex].minimum_match_value) {
  	    break;
  	}
  	adindex = (adindex + 1) % 360;
  	if (adindex == last_ad) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server, "Ad to expire not found");
  	    break;
  	}
      }
      expired = ((time((time_t *) NULL) > _my->cad[adindex].expiration_time)) ? 1 : 0;
  
  #if DEBUG
      ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, r->server,
  		 "Found ad %d : expire %s (%d > %d)",
  		 adindex, expired ? "yes" : "no",
  	    (int) time((time_t *) NULL), _my->cad[adindex].expiration_time);
  #endif
  
  #else
  	/* This is the shared memory case */
  /*        CombinedDemographics = ( AdDemographics & UserDemographics ) */
  	combineddemographics = (rec_buffer->cad_buffer[adindex].addemographics) & userdemographics;
  /*        Ad_weight = 0 */
  	ad_weight = 0;
  	if (combineddemographics & GENDER_MASK) {
  	    ad_weight += rec_buffer->cad_buffer[adindex].gen_weightings;
  	}
  	if (combineddemographics & AGE_GROUP_MASK) {
  	    ad_weight += rec_buffer->cad_buffer[adindex].age_weightings;
  	}
  	if (combineddemographics & REGION_MASK) {
  	    ad_weight += rec_buffer->cad_buffer[adindex].reg_weightings;
  	}
  	if (combineddemographics & INTEREST1_MASK) {
  	    ad_weight += rec_buffer->cad_buffer[adindex].int1_weightings;
  	}
  	if (combineddemographics & INTEREST2_MASK) {
  	    ad_weight += rec_buffer->cad_buffer[adindex].int2_weightings;
  	}
  	if (ad_weight >= rec_buffer->cad_buffer[adindex].minimum_match_value) {
  	    break;
  	}
  	adindex = (adindex + 1) % 360;
  	if (adindex == last_ad) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server, "Ad to expire not found");
  	    break;
  	}
      }
      expired = ((time((time_t *) NULL) > rec_buffer->cad_buffer[adindex].expiration_time)) ? 1 : 0;
  
  #if DEBUG
      ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, r->server,
  		 "Found ad %d : expire %s (%d > %d)",
  		 adindex, expired ? "yes" : "no",
  		 (int) time((time_t *) NULL),
  		 rec_buffer->cad_buffer[adindex].expiration_time);
  #endif
  
  #endif
  
      cookie_out = ap_psprintf(r->pool,
  			     "found_cookie=Ad_id=%d&Ad_weight=%d&Expired=%d",
  			     adindex,
  			     ad_weight,
  			     expired);
      ap_table_setn(r->headers_out, "Set-Cookie", cookie_out);
  
      if ((strstr(filename, "class1") != NULL) ||
  	(strstr(filename, "class2") != NULL)) {
  	caddr_t buf = customadscan(r, page, adindex);
  	returnHTMLPageWithBuffer(r, (char *) buf);
      }
      else {
  	returnHTMLPageWithFile(r, page);
      }
      ap_pfclose(r->pool, page);
      return OK;
  }				/* specweb99_cadget_handler */
  
  
  #if 0
  /***********************************************************************
   * util_parse_cookie                                                   *
   ***********************************************************************/
  
  table *util_parse_cookie(request_rec *r)
  {
      const char *data = ap_table_get(r->headers_in, "Cookie");
      table *cookies;
      const char *pair;
      if (!data)
  	return NULL;
  
      cookies = ap_make_table(r->pool, 2);
      while (*data && (pair = ap_getword(r->pool, &data, ';'))) {
  	const char *name, *value;
  	while (*data == ' ')
  	    ++data;
  	name = ap_getword(r->pool, &pair, '=');
  	while (*pair && (value = ap_getword(r->pool, &pair, '&'))) {
  	    ap_unescape_url((char *) value);
  	    ap_table_add(cookies, name, value);
  	}
      }
  
      return cookies;
  }				/* util_parse_cookies */
  #endif
  
  /***********************************************************************
   * specweb99_post_handler                                              *
   ***********************************************************************/
  static int _log_and_write(
  			      struct request_rec *r,
  			      FILE *f, char *filename,
  			      const char *urlroot, int dirnum, int classnum, int filenum, int clientnum, int uid
  )
  {
      pid_t pid;
      time_t stamp;
      int e;
      u_int32_t recnum;
      char recnumstr[12];		/* ten wide plus return plus \0 */
  
      stamp = time(NULL);
      pid = getpid();
  
  /*  "%10d %10d %10d %5d %2d %2d %10d %-60.60s %10d %10d\n",
   *      RecordNum, TimeStamp, Pid, Dir#, Class#, File#, Client#,
   *      FileName, Pid, MyCookie
   */
  
  
  /*  Increment PostLog RecordCount record and rewrite PostLog*/
      _WINTR(e = fread(recnumstr, (size_t) 1, (size_t) 11, f));
      if (e != 11) {
  	/* Scream and shout */
  	specweb99_error(r->server, "Failed to read recordcount from post.log");
  	returnHTMLPageWithMessage(r, "Failed to read recordcount from post.log");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      recnum = atol(recnumstr) + 1;	/* protected by trailing return */
      fseek(f, 0, SEEK_SET);
      if (fprintf(f, "%10d\n", recnum) != 11) {
  	specweb99_error(r->server, "Failed to write new recordcount  to post.log");
  	returnHTMLPageWithMessage(r, "Failed write new recordcount to post.log");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
      fflush(f);
  
  /*  Append new PostLog Record to end of PostLog*/
      fseek(f, 0, SEEK_END);
      /* write 139 bytes */
      if (fprintf(f, "%10d %10d %10d %5d %2d %2d %10d %-60.60s %10d %10d\n",
  		recnum, (int) stamp, (int) pid, dirnum, classnum, filenum,
  		clientnum, filename, (int) pid, uid) != 139) {
  	specweb99_error(r->server, "Failed to write record to post.log");
  	returnHTMLPageWithMessage(r, "Failed write record to post.log");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  /*    If writing PostLog gets errors*/
  /*        Return HTML Page with Message = error_message*/
  /*    Endif*/
  /*        (refer to Post Log Format section to see required format)*/
  
      fflush(f);
  
      return OK;
  }
  
  static int specweb99_post_handler(request_rec *r)
  {
      struct specweb99_module_data *_my =
      ap_get_module_config(r->server->module_config, &specweb99_module);
      int posterr, e, e2;
      const char *urlroot = "<none>";
      int dirnum = 0, classnum = 0, filenum = 0, clientnum = 0, uid = 0;
      char *filename;
      const char *cookie_in;
      FILE *f;
      char *data = "<none>";
      const char *type, *docroot;
      char argsbuffer[HUGE_STRING_LEN];
      int rsize, len_read, rpos = 0;
      long length = 0;
  
      docroot = ap_document_root(r);
  
  /*Begin:*/
  /*    Make substitutions in HTML return page for the following:*/
  /*        Server_Software*/
  /*        Remote_Addr*/
  /*        Script_Name*/
  /*        QueryString*/
  /* The above is done in the returnHTMLPageWith... functions */
  
  /*    Parse PostInput - a sample format is as follows */
  /*    (keys may be received in any order):*/
  /*        urlroot=[urlroot]&dir=[Dir#]&class=[Class#]&num=[File#]&client=[Client#]*/
  
      type = ap_table_get(r->headers_in, "Content-Type");
      /*
       * Scream in protest if the user uses the broken version of SPECWeb99
       * manager that doesn't send the Content-Type header. Note that this only
       * affects the pre-run tests: the regular client does send the header.
       */
      if ((type == NULL) || (strcasecmp(type, DEFAULT_ENCTYPE) != 0)) {
  	ap_log_error(APLOG_MARK,
  		     APLOG_WARNING | APLOG_NOERRNO,
  		     r->server,
  		     "The client didn't send %s as Content-Type. Version "
  		   "1.02 of the SPECWeb does not do this and thus violates "
  		     "the HTTP specification. Please apply the following "
  		     "patch to your manager script and bitch to SPEC that "
  		 "they fix this:\n%s", DEFAULT_ENCTYPE, SPEC_MANAGER_PATCH);
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      if (ap_setup_client_block(r, REQUEST_CHUNKED_ERROR) != OK) {
  	ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r->server, "Could not setup client block");
  	returnHTMLPageWithMessage(r, "Couldn't set up client block");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      if (!ap_should_client_block(r)) {
  	ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r->server, "No POST data");
  	returnHTMLPageWithMessage(r, "No POST data");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      length = r->remaining;
  
      data = ap_pcalloc(r->pool, length + 1);
  
      ap_hard_timeout("specweb99_post_handler read", r);
  
      while ((len_read =
  	    ap_get_client_block(r, argsbuffer, sizeof(argsbuffer))) > 0) {
  	ap_reset_timeout(r);
  	if ((rpos + len_read) > length) {
  	    rsize = length - rpos;
  	}
  	else {
  	    rsize = len_read;
  	}
  	memcpy((char *) data + rpos, argsbuffer, rsize);
  	rpos += rsize;
      }
      ap_kill_timeout(r);
  
      data[length] = '\0';
  
      posterr = 5;		/* Counter to make sure we get all VARiables
  				 * from the CGI post */
      while (data) {
  	const char *p = data;
  
  	data = index(p, '&' /* 0x26 */ );
  	if (data != NULL)
  	    *data++ = '\0';
  
  	if (strncmp(p, "urlroot=", 8) == 0) {
  	    urlroot = ap_pstrdup(r->pool, p + 8);
  	    posterr--;
  	}
  	else if (strncmp(p, "dir=", 4) == 0) {
  	    dirnum = atoi(p + 4);
  	    posterr--;
  	}
  	else if (strncmp(p, "class=", 6) == 0) {
  	    classnum = atoi(p + 6);
  	    posterr--;
  	}
  	else if (strncmp(p, "num=", 4) == 0) {
  	    filenum = atoi(p + 4);
  	    posterr--;
  	}
  	else if (strncmp(p, "client=", 7) == 0) {
  	    clientnum = atoi(p + 7);
  	    posterr--;
  	}
      }
      if (posterr != 0) {
  	ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR,
  		     r->server, "Did not get all POST arguments");
  	returnHTMLPageWithMessage(r, "Did not get all POST arguments");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
  /*    Parse Cookie string to get MyCookie. The format is as */
  /*    follows (the order of the keys and values is fixed):*/
      /*
       * 1         2       012345678901234567890123
       * my_cookie=user_id=[MyCookie]&last_ad=[IgnoredField]
       */
  
      cookie_in = ap_table_get(r->headers_in, "Cookie");
      if (cookie_in == NULL) {
  	ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, r->server, "No cookie");
  	returnHTMLPageWithMessage(r, "Invalid Cookie");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
      uid = atoi(cookie_in + 18);
  
  /*  Filename = [urlroot]/dir[5-digit Dir#]/class[Class#]_[File#]*/
  /*        (for example, the POST input of */
  /*        urlroot=/specweb99/file_set&dir=00123&class=1&num=1&client=10003 */
  /*        would make Filename = /specweb99/file_set/dir00123/class1_1)*/
  
      filename = ap_make_full_path(r->pool,
  		    docroot, ap_psprintf(r->pool, "%s/dir%05d/class%1d_%1d",
  				       urlroot, dirnum, classnum, filenum));
  
      /* Do_atomically (for example, using a file lock or other mutex): */
  
      f = ap_pfopen(r->pool, _my->log_path, "r+");
      if (f == NULL) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  		"Failed to open post.log '%s' for updating", _my->log_path);
  	returnHTMLPageWithMessage(r, "Failed to open post.log file for updating");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      if ((e = _wlock(r->server, r, f, _my->log_path)) != 0)
  	returnHTMLPageWithMessage(r, "Failed to lock post.log file");
  
      if (e == 0)
  	e = _log_and_write(r, f, filename, urlroot, dirnum, classnum, filenum, clientnum, uid);
  
      if ((e2 = _unlock(r->server, r, f, _my->log_path)) != OK) {
  	if (e == 0) {
  	    e = e2;
  	    returnHTMLPageWithMessage(r, "Failed to lock post.log file");
  	}
      }
  /*    End Do_atomically*/
      ap_pfclose(r->pool, f);
  
      if (e != 0)
  	return HTTP_INTERNAL_SERVER_ERROR;	/* _log_and_write() will have
  						 * displayed a page already */
  
      /*
       * gettimeofday(&afterlock, NULL); lockstr = ap_psprintf(r->pool, "Entire
       * logfile op took: %ld microseconds", (afterlock.tv_sec -
       * beforelock.tv_sec) * 1000000 + afterlock.tv_usec -
       * beforelock.tv_usec); specweb99_info(lockstr);
       */
  /*    Access file 'RootDir/Filename'*/
      f = ap_pfopen(r->pool, filename, "r");
  /*    If error found while accessing file then*/
      if (f == NULL) {
  /*        Return HTML Page with Message = error_message*/
  	ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Could not open File '%s' (post handler)", filename);
  	returnHTMLPageWithMessage(r, "Could not open file");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
  /*        CookieString = "my_cookie=<myCookie>"
   *
   * XXX    seems from code inspection that this really
   *        is not setting again the cookie - but a change
   *        the cookie is to be set to %d of the user number.
   */
      ap_table_setn(r->headers_out, "Set-Cookie", ap_psprintf(r->pool, "my_cookie=%d", uid));
  
  /*        Return HTML Page with File='RootDir/FileName' and Cookie=CookieString */
      returnHTMLPageWithFile(r, f);
      ap_pfclose(r->pool, f);
  
      return OK;
  }				/* specweb99_post_handler */
  
  /*!
    @const specweb99_handlers
    @discussion Dispatch list of handler function pointers to let the Apache
    core know which incantation is handled by what function
   */
  
  static const handler_rec specweb99_handlers[] = {
      {"specweb99hk", specweb99_hk_handler},
      {"specweb99get", specweb99_get_handler},
      {"specweb99cadget", specweb99_cadget_handler},
      {"specweb99post", specweb99_post_handler},
      {NULL, NULL}
  };
  
  /*!
    @const specweb99_module
    @discussion Dispatch list of Apache API hook callbacks
   */
  
  module MODULE_VAR_EXPORT specweb99_module = {
      STANDARD_MODULE_STUFF,
      specweb99_module_init,	/* module initializer                  */
      NULL,			/* create per-dir    config structures */
      NULL,			/* merge  per-dir    config structures */
      specweb99_server_create,	/* create per-server config structures */
      NULL,			/* merge  per-server config structures */
      NULL,			/* table of config file commands       */
      specweb99_handlers,		/* [#8] MIME-typed-dispatched handlers */
      NULL,			/* [#1] URI to filename translation    */
      NULL,			/* [#4] validate user id from request  */
      NULL,			/* [#5] check if the user is ok _here_ */
      NULL,			/* [#3] check access by host address   */
      NULL,			/* [#6] determine MIME type            */
      NULL,			/* [#7] pre-run fixups                 */
      NULL,			/* [#9] log a transaction              */
      NULL,			/* [#2] header parser                  */
      specweb99_child_init,	/* child_init                          */
      NULL,			/* child_exit                          */
      NULL,			/* [#0] post read-request              */
  #ifdef EAPI
      NULL,			/* EAPI: add_module                    */
      NULL,			/* EAPI: remove_module                 */
      NULL,			/* EAPI: rewrite_command               */
      NULL,			/* EAPI: new_connection                */
  #endif
  };
  
  
  
  1.1                  httpd-test/specweb99/specweb99-1.3/mod_specweb99.h
  
  Index: mod_specweb99.h
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2002 The Apache Software Foundation.  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. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 was contributed by Covalent Technologies Inc, 
   * http://www.covalent.net around April 20002.
   * 
   * mod_specweb99.h -- Header file for Apache specweb99 module
   * sctemme July 2001
   */
  
   /*
    * ! @header mod_specweb99 The mod_specweb99 module implements the SPECWeb99
    * dynamic content requests. It should work with Apache 1.3 by enabling the
    * request handlers in the Apache configuration file. Since this module is
    * self-contained and called exclusively through its handlers, this
    * documentation should be seen purely as internal, not to reuse the symbols
    * it describes.
    *
    */
  
  #ifndef MOD_SPECWEB99_H
  #define MOD_SPECWEB99_H
  
  #ifdef SOLARIS2
  #define u_int32_t uint32_t
  #define u_int16_t uint16_t
  #define MODTIME(a) ((a).st_mtime)
  #define EQMODTIME(a,b)  ((a).st_mtime == (b))
  #else
  #ifdef LINUX
  #define MODTIME(a) ((a).st_mtime)
  #define EQMODTIME(a,b)  ((a).st_mtime == (b))
  #else
  #define MODTIME(a) ((a).st_mtimespec)
  #define EQMODTIME(a,b)  (((a).st_mtimespec.tv_nsec == (b).tv_nsec) && ((a).st_mtimespec.tv_sec == (b).tv_sec))
  #endif				/* LINUX */
  #endif				/* SOLARIS2 */
  
  #define BOILERPLATE_START \
  "<html>\n"\
  "<head><title>SPECweb99 Dynamic GET & POST Test</title></head>\n"\
  "<body>\n"\
  "<p>SERVER_SOFTWARE = %s\n"\
  "<p>REMOTE_ADDR = %s\n"\
  "<p>SCRIPT_NAME = %s\n"\
  "<p>QUERY_STRING = %s\n"\
  "<pre>\n"
  
  #define BOILERPLATE_END "\n</pre>\n</body></html>\n"
  
  #define MARKER "<!WEB99CAD><IMG SRC=\"/file_set/dir"
  
  /* Record lengths including newline */
  #define UPRLENGTH  15
  #define CADRLENGTH 39
  
  /* Maximum number of users we make shared memory for. */
  #define MAXUSERS 1500
  /* Number of records in CAD file */
  #define NUMCADRECORDS 360
  
  #define GENDER_MASK	 (  0x3 << 28)
  #define AGE_GROUP_MASK   (  0xf << 24)
  #define REGION_MASK      (  0xf << 20)
  #define INTEREST1_MASK   (0x3ff << 10)
  #define INTEREST2_MASK   (0x3ff <<  0)
  
  #define GENDER_WT_OFF    16
  #define AGE_GROUP_WT_OFF 12
  #define REGION_WT_OFF     8
  #define INTEREST1_WT_OFF  4
  #define INTEREST2_WT_OFF  0
  
  #define DEFAULT_ENCTYPE "application/x-www-form-urlencoded"
  #define SPEC_MANAGER_PATCH "*** manager.orig        Wed Aug 23 16:19:12 2000\n\
  --- manager     Wed Aug 23 16:20:18 2000\n\
  ***************\n\
  *** 1691,1696 ****\n\
  --- 1691,1700 ----\n\
          $req = new HTTP::Request 'POST' => $url . \"f\";\n\
          my($urlroot) = ($url =~ m#\?(.*file_set/)#);\n\
          $req->content(\"class=1&client=2&dir=00004&num=3&urlroot=$urlroot\");\n\
  +\n\
  +       # ddhill added \n\
  +       $req->header('Content-type' => 'application/x-www-form-urlencoded');\n\
  +       # end ddhill added\n\
        }\n\
        else {\n\
          $req = new HTTP::Request 'GET' => $url;\n"
  
  
  /* Structure to hold custom ad data */
  
  typedef struct cadrec {
      u_int32_t addemographics;
      u_int32_t age_weightings;
      u_int32_t gen_weightings;
      u_int32_t reg_weightings;
      u_int32_t int1_weightings;
      u_int32_t int2_weightings;
      u_int16_t minimum_match_value;
      u_int32_t expiration_time;
  }      cadrec;
  
  typedef cadrec *CADPtr;
  
  typedef u_int32_t *UPPtr;
  
  typedef struct bufs {
      CADPtr cad_buffer;
      UPPtr up_buffer;
      u_int16_t up_count;
  }    bufs;
  
  typedef struct specweb99_module_data specweb99_module_data;
  
  /* Module private data space - where we keep some per
   * (virtual?) server precalculated data
   */
  struct specweb99_module_data {
      char *log_path;
      char *cadgen99;
      char *upfgen99;
  
  #ifdef SOLARIS2
      time_t up_lastmod;
      time_t cad_lastmod;
  #else
      struct timespec up_lastmod;
      struct timespec cad_lastmod;
  #endif
      char *up_path;
      u_int32_t *up;
      u_int16_t up_count;
      pool *up_pool;
  
      char *cad_path;
      struct cadrec *cad;
      u_int16_t cad_count;
      pool *cad_pool;
  };
  
  /*!
    @function specweb99_error
    @discussion Wrapper for the ap_log_error function, writes to the error
    log with APLOG_ERR severity with the source file name, line number and
    errno global variable. Note that we lose ap_log_error's sprintf-like
    properties.
    @param mess The message string
   */
  
  #define specweb99_error(s,mess) { if (s) ap_log_error(APLOG_MARK, \
  					   APLOG_ERR, \
  					   s, (mess)); else fprintf(stderr,(mess)); }
  
  /*!
    @function specweb99_info
    @discussion Wrapper for the ap_log_error function, writes to the error
    log with APLOG_INFO severity. Note that we lose ap_log_error's
    sprintf-like properties.
    @param mess The message string
   */
  
  #define specweb99_info(s,mess) { if (s) ap_log_error(APLOG_MARK, \
  					  APLOG_NOERRNO|APLOG_INFO, \
  					  s, (mess)); else fprintf(stderr,(mess)); }
  
  #ifdef DEBUG
  /*!
    @function specweb99_debug
    @discussion Wrapper for the ap_log_error function, writes to the error
    log with APLOG_DEBUG severity. Note that we lose ap_log_error's
    sprintf-like properties.
    @param mess The message string
   */
  
  #define specweb99_debug(s,mess) { if (s) ap_log_error(APLOG_MARK, \
  					   APLOG_NOERRNO|APLOG_DEBUG, \
  					   s, (mess)); \
  				   else \
  					   fprintf(stderr,(mess)); \
  				}
  #endif
  
  /* Housekeeping and internal functions */
  
  /*!
    @function returnHTMLPageWithFile
    @discussion Sends the contents of a file to the client, encapsulated in
   				boilerplate HTML  and adorned with content-length and
    				content-type header
    @param request_rec *r The request we are currently handling
    @param FILE *f Pointer to the file structure. This is the file we need
   					  to return. It has to be open for reading with the file
   					  pointer at the start.
   */
  
  static void returnHTMLPageWithFile(request_rec *r, FILE *f);
  
  /*!
    @function returnHTMLPageWithMessage
    @discussion Sends a message to the client, encapsulated in boilerplate HTML
    				and adorned with content-length and content-type header
    @param r The request we are currently handling
    @param m Format (as in printf) and optional futher parameters.
   */
  
  static void returnHTMLPageWithMessage(request_rec *r, char *fmt,...);
  
  /*!
    @function returnHTMLPageWithBuffer
    @discussion Sends contents of buffer to client, encapsulated in boilerplate
   				HTML and adorned with content-length and content-type header.
    @param r The request we are currently handling
    @param buf The data we're sending. We expect null-terminated string
   */
  
  static void returnHTMLPageWithBuffer(request_rec *r, char *buf);
  
  #if 0
  /*!
    @function checkUPFile
    @discussion Checks if the User.Personality file has been loaded into memory
                and, if not, does so.
    @param r The request we are currently handling
    @result Success (0) or failure (!0).
   */
  int16_t checkUPFile(struct specweb99_module_data * _my);
  
  /*!
   @function checkCADFile
   @discussion Checks if the Custom.Ads file has been changed on disk since the
   	     last time it was loaded into memory and, if so, refreshes the
   	     in-memory copy.
   @param r The request we are currently handling
   @result Succes (0) or failure (!0)
   */
  
  int16_t checkCADFile(struct specweb99_module_data * _my);
  
  /*!
    @function customadscan
    @discussion Reads a payload file into memory, scans it for ad tags, replaces
   				ad data and returns a pointer to the memory buffer
    @param r The request we are currently handling
    @param f The payload file we will scan. We expect the file to be open
    @param adid The number of the ad that we will substitute into
   						   the payload file
    @result Pointer to the buffer that contains the processed payload file data
   */
  #endif
  
  caddr_t customadscan(request_rec *r, FILE *f, int16_t adid);
  
  /*!
    @function call_specweb_util
    @discussion Callback routine for ap_bspawn_child
    @param rp Process data; we are passed the current request_rec,
   					   adapted for our use
    @param pinfo Win32 needs this
    @result On Win32, the pid of the process it spawned, on unix it never returns
   */
  
  static int call_specweb_util(void *rp, child_info * pinfo);
  
  /*!
    @function specweb99_module_init
    @discussion Initialization of module and our own data structures.
    @param s The server_rec of the server process that initializes us
    @param p Pointer to a pool that lives as long as the server
   */
  
  static void specweb99_module_init(server_rec *s, ap_pool * p);
  
  /* Content handlers for the SPECWeb99 dynamic content categories */
  
  /*!
    @function specweb99_hk_handler
    @discussion Implements the SPECWeb99 housekeeping routines
    @param r The request we are currently handling
   */
  
  static int specweb99_hk_handler(request_rec *r);
  
  /*!
    @function specweb99_get_handler
    @discussion Implements the SPECWeb99 GET operation
    @param r The request we are currently handling
    @result OK if it does not die horribly which should not occur because it does
    a lot of its own error handling
   */
  
  static int specweb99_get_handler(request_rec *r);
  
  /*!
    @function specweb99_cadget_handler
    @discussion Handles SPECWeb99 GET request with Custom Ad Rotation
    @param r The request we are currently handling
    @result OK if it does not die horribly which should not occur because it does
    a lot of its own error handling
   */
  
  static int specweb99_cadget_handler(request_rec *r);
  
  /*!
   @function util_parse_cookie
   @discussion  This routine comes straight out of 'Writing Apache Modules with
    Perl and C' by Doug MacEachern and Lincoln Stein, page 599. Parses the cookies
    of a request into a table of key-value pairs.
   @param r The current request
   @result Pointer to the cookie table
   */
  
  table *util_parse_cookie(request_rec *r);
  
  /*!
    @function specweb99_post_handler
    @discussion Implements the SPECWeb99 POST operation.
    @param r The request we are currently handling
    @result OK if it does not die horribly which should not occur because it does
    a lot of its own error handling
   */
  
  static int specweb99_post_handler(request_rec *r);
  
  /*!
   @function specweb99_logger
   @discussion Write the result of a POST transaction to the post.log as
    required by the SPEC Run Rules. On other requests, this does nothing.
    @param r The current request
    @result OK if ok, HTTP_INTERNAL_SERVER_ERROR if something went south.
   */
  
  int specweb99_logger(request_rec *r);
  
  #endif				/* MOD_SPECWEB99_H */
  
  
  
  1.1                  httpd-test/specweb99/specweb99-2.0/README
  
  Index: README
  ===================================================================
  Move into .../httpd-2.0/modules/generators
  
  Rerun buildconf and enable with
  
  	./configure --enable-specweb99
  
  Note: 
  -   lacks some of the timeout/error catching - APR problem
  -   locking different - due to APR
  -   This release should compile against Apache 2.0.35
  
  WORK IN PROCESS
  
  See the file LICENSE.txt for license info.
  
  {sctemme@covalent.net | dirkx|@{ apache.org | covalent.net }}
  
  
  
  
  1.1                  httpd-test/specweb99/specweb99-2.0/config5.m4
  
  Index: config5.m4
  ===================================================================
  dnl modules enabled in this directory by default
  dnl See the file LICENSE.txt for license info.
  dnl { sctemme | dirkx }@{ apache.org | covalent.net }
  
  dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]])
  
  APACHE_MODPATH_INIT(generators)
  
  APACHE_MODULE(status, process/thread monitoring, , , yes)
  APACHE_MODULE(autoindex, directory listing, , , yes)
  APACHE_MODULE(asis, as-is filetypes, , , yes)
  APACHE_MODULE(info, server information, , , most)
  APACHE_MODULE(suexec, set uid and gid for spawned processes, , , no, [
                other_targets=suexec ] )
  
  APR_ADDTO(LT_LDFLAGS,-export-dynamic)
  
  if test "$apache_cv_mpm" = "worker" -o "$apache_cv_mpm" = "threaded" -o "$apache_cv_mpm" = "perchild"; then
  # if we are using a threaded MPM, we will get better performance with
  # mod_cgid, so make it the default.
      APACHE_MODULE(cgid, CGI scripts, , , yes)
      APACHE_MODULE(cgi, CGI scripts, , , no)
  else
  # if we are using a non-threaded MPM, it makes little sense to use
  # mod_cgid, and it just opens up holes we don't need.  Make mod_cgi the
  # default
      APACHE_MODULE(cgi, CGI scripts, , , yes)
      APACHE_MODULE(cgid, CGI scripts, , , no)
  fi
  
  dnl dirkx@covalent.net / oct 2001
  APACHE_MODULE(specweb99, SpecWeb99 benchmarking, , , no)
  
  APACHE_MODPATH_FINISH
  
  
  
  1.1                  httpd-test/specweb99/specweb99-2.0/mod_specweb99.c
  
  Index: mod_specweb99.c
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2002 The Apache Software Foundation.  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. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 was contributed by Covalent Technologies Inc, 
   * http://www.covalent.net around April 20002.
   *
   * mod_specweb99.c -- Apache  specweb99 module
   * sctemme July 2001
   *
   * {sctemme | dirkx }@{ apache.org | covalent.net }
   */
  
  #include "httpd.h"
  
  #include "http_core.h"
  #include "http_config.h"
  #include "http_log.h"
  #include "http_protocol.h"
  #include "http_main.h"
  
  #include "ap_config.h"
  
  #include "apr.h"
  #include "apr_strings.h"
  #include "apr_lib.h"
  #include "apr_file_info.h"
  #include "apr_time.h"
  #include "apr_tables.h"
  #include "apr_buckets.h"
  #include "apr_uri.h"
  
  #include "mod_core.h"
  
  #if APR_HAVE_SYS_TYPES_H
  #include <sys/types.h>
  #endif
  #if APR_HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  
  /* Note: version must be of the x.yy type - as it is
   * send over the http protocol wire; where x and y
   * are single 0-9 ascii digits :-). The name should
   * be an A-Za-z0-9 string. Which does not start with
   * a number :-) You want to avoid _ but '-' is not too
   * bad usually..
   */
  #define NAME "Specweb"
  #define VERSION "2.01"
  
  #include "mod_specweb99.h"
  
  module AP_MODULE_DECLARE_DATA specweb99_module;
  
  typedef struct specweb99_module_data specweb99_module_data;
  
  /* Module private data space - where we keep some per
   * (virtual?) server precalculated data
   */
  struct specweb99_module_data {
      char *log_path;
      char *cadgen99;
      char *upfgen99;
  
      apr_time_t up_lastmod;
      apr_time_t cad_lastmod;
  
      apr_time_t check;
  
      char *up_path;
      apr_pool_t *up_pool;
  
      apr_int32_t *up;
      apr_uint16_t up_count;
  
      char *cad_path;
      apr_pool_t *cad_pool;
  
      struct cadrec *cad;
      apr_uint16_t cad_count;
  };
  
  static const char boilerplate_start[] = BOILERPLATE_START;
  static const int boilerplate_start_len = sizeof(boilerplate_start) - 1;
  
  static const char boilerplate_end[] = BOILERPLATE_END;
  static const int boilerplate_end_len = sizeof(boilerplate_end) - 1;
  
  /* Use interal locking - the main reason for doing
   * so is error trapping and being able to warn/info
   * when we spend a lot of time in camping on a lock.
   */
  #define  _rlock(s,r,fg,file)    (_dolock(s,r,fg,APR_FLOCK_SHARED,file))
  #define  _wlock(s,r,fg,file)    (_dolock(s,r,fg,APR_FLOCK_EXCLUSIVE,file))
  
  /* generic locking
   * - when 'r' is passed - will do a timeout.
   * - when 's' is passed - do appropriate logging.
   * - when 'file' is passed - logging will be more meaningfull
   */
  static int _dolock(struct server_rec *s, struct request_rec *r, apr_file_t * f,
  		       int type, char *file)
  {
      int e;
      /*
       *  Rather than simply try-lock-and-wait - we first check
       * if a lock would block - and then set a timeout before
       * camping out on the lock.
       *
       */
      if ((e = apr_file_lock(f, type | APR_FLOCK_NONBLOCK)) != APR_SUCCESS) {
  	/*
  	 * XXX timeouts removed !  not sure how to do that in 2.0
  	 */
  	if (s)
  	    ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, e, s,
  			 "Camping out %s%s for a %s lock",
  			 file ? "on " : "", file ? file : "",
  			 ((type == APR_FLOCK_SHARED) ? "read" : "write"));
  
  	e = apr_file_lock(f, type);
  	/*
  	 * XXXX clear timeout removed not sure how to do that in 2.0
  	 */
      }
  
      /* Trap both first/second flock() error. */
      if (e) {
  	if (s)
  	    ap_log_error(APLOG_MARK, APLOG_ERR, e, s,
  			 "Failed to %s" "lock%s%s",
  			 ((type == APR_FLOCK_SHARED) ? "read" : "write"),
  			 ((file) ? ": " : ""), ((file) ? file : ""));
  	return -1;
      }
      return 0;
  }
  
  static char *returnHTMLPageHead(request_rec *r, int len)
  {
      char *bp_head;
  
      /* Fill up the boilerplate with info */
      bp_head = apr_psprintf(r->pool, boilerplate_start,
  			   ap_get_server_version(),
  			   ap_get_remote_host(r->connection, NULL,
  					      REMOTE_NOLOOKUP, NULL), r->uri,
  			   r->args ? r->args : "");
  
      ap_set_content_length(r, strlen(bp_head) + boilerplate_end_len + len);
      r->content_type = "text/html";
  
      return bp_head;
  }
  
  
  static void returnHTMLPageWithBuffer(request_rec *r, char *buf)
  {
      char *bp_head;
      conn_rec *c = r->connection;
      apr_bucket_brigade *bb;
      apr_bucket *b1, *b2, *b3;
      apr_status_t rv;
  
      if (buf == NULL)
  	buf = "Error: tried to send buffer but it is NULL";
  
      bp_head = returnHTMLPageHead(r, strlen(buf));
  
      if (r->header_only)
  	return;
  
      bb = apr_brigade_create(r->pool, c->bucket_alloc);
  
      b1 = apr_bucket_transient_create(bp_head, strlen(bp_head), c->bucket_alloc);
      b2 = apr_bucket_transient_create(buf, strlen(buf), c->bucket_alloc);
      b3 = apr_bucket_immortal_create(boilerplate_end,
  				  boilerplate_end_len, c->bucket_alloc);
  
      APR_BRIGADE_INSERT_TAIL(bb, b1);
      APR_BRIGADE_INSERT_TAIL(bb, b2);
      APR_BRIGADE_INSERT_TAIL(bb, b3);
      APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(c->bucket_alloc));
  
      rv = ap_pass_brigade(r->output_filters, bb);
      if (rv != APR_SUCCESS) {
  	ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  		      "mod_specweb: ap_pass_brigade failed for buffer '%s'",
  		      buf);
      }
  }				/* returnHTMLPageWithBuffer */
  
  static void returnHTMLPageWithMessage(request_rec *r, char *fmt,...)
  {
      va_list args;
      char *m;
  
      va_start(args, fmt);
      m = apr_pvsprintf(r->pool, fmt, args);
      va_end(args);
  
      returnHTMLPageWithBuffer(r, m);
  }				/* returnHTMLPageWithMessage */
  
  static void returnHTMLPageWithFile(request_rec *r, char *fname)
  {
      char *bp_head;
      struct apr_finfo_t s;
      apr_file_t *f;
      apr_status_t rv;
      apr_bucket_brigade *bb;
      apr_bucket *b1, *b2, *b3;
      apr_off_t zero = 0;
      conn_rec *c = r->connection;
  
      if ((rv = apr_stat(&s, fname, APR_FINFO_SIZE, r->pool)) != APR_SUCCESS) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
  		     "Could not stat file '%s' for reading", fname);
  	returnHTMLPageWithMessage(r, "Error: Failed to stat the file");
  	return;
      }
  
      bp_head = returnHTMLPageHead(r, s.size);
  
      if (r->header_only) 
  	return;
  
      b1 = apr_bucket_transient_create(bp_head, strlen(bp_head), c->bucket_alloc);
  
      if ((rv =
  	 apr_file_open(&f, fname, APR_READ, APR_OS_DEFAULT,
  		       r->pool)) != APR_SUCCESS) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
  		     "Could not open file '%s' for reading", fname);
  	returnHTMLPageWithMessage(r, "Error: could not open file for reading.");
  	return;
      }
  
      bb = apr_brigade_create(r->pool, c->bucket_alloc);
  #if APR_HAS_LARGE_FILES
      if (s.size > AP_MAX_SENDFILE) {
  	/*
  	 * APR_HAS_LARGE_FILES issue; must split into mutiple buckets, no
  	 * greater than MAX(apr_size_t), and more granular than that in case
  	 * the brigade code/filters attempt to read it directly.
  	 */
  	apr_off_t fsize = s.size;
  	b = apr_bucket_file_create(f, zero, AP_MAX_SENDFILE, r->pool);
  	while (fsize > AP_MAX_SENDFILE) {
  	    APR_BRIGADE_INSERT_TAIL(bb, b);
  	    apr_bucket_copy(b, &b);
  	    b->start += AP_MAX_SENDFILE;
  	    fsize -= AP_MAX_SENDFILE;
  	}
  	b->length = (apr_size_t) fsize;	/* Resize just the last bucket */
      }
      else
  #endif
  	b2 = apr_bucket_file_create(f, zero, (apr_size_t) (s.size),
  				    r->pool, c->bucket_alloc);
      b3 = apr_bucket_immortal_create(boilerplate_end, 
  				    boilerplate_end_len, c->bucket_alloc);
  
      APR_BRIGADE_INSERT_TAIL(bb, b1);
      APR_BRIGADE_INSERT_TAIL(bb, b2);
      APR_BRIGADE_INSERT_TAIL(bb, b3);
      APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_eos_create(c->bucket_alloc));
  
      rv = ap_pass_brigade(r->output_filters, bb);
      if (rv != APR_SUCCESS) {
  	ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  		      "mod_specweb: ap_pass_brigade failed for %s", fname);
      }
  }				/* returnHTMLPageWithFile */
  
  
  
  /***********************************************************************
   * checkUPFile                                                         *
   ***********************************************************************/
  
  static apr_int16_t
         checkUPFile(struct server_rec *sv, struct request_rec *r,
  		          struct specweb99_module_data * _my)
  {
      apr_finfo_t s;
      apr_status_t rv;
      apr_file_t *f;
      apr_int16_t numrecords, up_uid;
      int e = 0;
      char up_record[UPRLENGTH + 1];
      /* stat it, compare to stored stat */
      apr_time_t now;
  
      now = time(NULL);
      if(_my->check == now) 
  	return 0;
      _my->check == now;
  
      if ((rv = apr_stat(&s, _my->up_path,
  		       APR_FINFO_SIZE | APR_FINFO_MTIME,
  		       r->pool)) != APR_SUCCESS) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  		     "Could not stat User.Personality file '%s'",
  		     _my->up_path);
  	return 1;
      };
  
      if (s.mtime == _my->up_lastmod) {
  	return 0;
      }
  
      numrecords = s.size / UPRLENGTH;
      /*
       * Check buffer array for nullness and bigness, make if necessary.
       */
      if ((_my->up == NULL) || (numrecords > _my->up_count)) {
  	/* User personalities are only 32 bits (sad, really) */
  	apr_pool_clear(_my->up_pool);
  	_my->up = apr_palloc(_my->up_pool, numrecords * sizeof(u_int32_t));
  	_my->up_count = numrecords;
      }
      /*
       * open the file, with memory from the request pool because we will
       * not need it very long.
       */
      if ((rv = apr_file_open(&f, _my->up_path,
  			    APR_READ, APR_OS_DEFAULT,
  			    r->pool)) != APR_SUCCESS) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  		     "Could not open User.Personality file '%s'",
  		     _my->up_path);
  	return 1;
      }
  
      if (_rlock(sv, r, f, _my->up_path)) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  		     "Failed to lock User.Personality file '%s'",
  		     _my->up_path);
  	apr_file_close(f);
  	return 1;
      }
  
      /* Read every record, parse, put user demographics in array */
      up_uid = 0;
      while (1) {
  	int id, dem;
  	apr_size_t l;
  
  	if ((rv =
  	     apr_file_read_full(f, up_record, UPRLENGTH, &l)) != APR_SUCCESS)
  	    break;
  
  	up_record[UPRLENGTH] = '\0';
  
  	if (sscanf(up_record, "%d %x", &id, &dem) != 2) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  			 "corrupted entry in UP file");
  	    e = 1;
  	}
  
  	if (up_uid != id) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  			 "user id out of sync in UP file");
  	    e = 1;
  	}
  
  	_my->up[up_uid] = dem;
  	up_uid++;
      }
  
      if ((up_uid != numrecords) && (rv != APR_SUCCESS)) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  		     "Failed read from User.Personality file '%s'",
  		     _my->up_path);
  	e++;
      }
  
      if (apr_file_unlock(f) != APR_SUCCESS) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  		     "Failed to unlock User.Personality file '%s'",
  		     _my->up_path);
  	e = 1;
      };
  
      /* Close file */
      apr_file_close(f);
  
      /* Store last modified date assuming no errors. */
      if (e)
  	_my->up_lastmod = 0;
      else
  	_my->up_lastmod = s.mtime;
  
      return e;
  }
  
  
  /***********************************************************************
   * checkCADFile                                                        *
   ***********************************************************************/
  
  static apr_int16_t
  checkCADFile(struct server_rec *sv, struct request_rec *r,
  	     struct specweb99_module_data * _my)
  {
      apr_finfo_t s;
      apr_status_t rv;
      size_t numrecords;
      apr_file_t *f;
      char cadline[CADRLENGTH];
      apr_uint16_t cad_uid;
      int e = 0;
      apr_time_t now;
  
      now = time(NULL);
      if(_my->check == now) 
  	return 0;
      _my->check == now;
  
      /* Stat it, compare to stored modification time */
  
      if ((rv =
  	 apr_stat(&s, _my->cad_path, APR_FINFO_SIZE | APR_FINFO_MTIME,
  		  r->pool)) != APR_SUCCESS) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  		     "Failed to stat CAD file '%s'", _my->cad_path);
  	return 1;
      };
  
      if (s.mtime == _my->cad_lastmod) {
  	return 0;
      }
  
      /*
       * Need to read file into memory - and re-allocate the array if the
       * size has changed.
       */
      numrecords = s.size / CADRLENGTH;
      if (numrecords > _my->cad_count) {
  	apr_pool_clear(_my->cad_pool);
  	_my->cad =
  	    apr_palloc(_my->cad_pool, numrecords * sizeof(struct cadrec));
  	_my->cad_count = numrecords;
      }
  
      if ((rv = apr_file_open(&f, _my->cad_path,
  			    APR_READ, APR_OS_DEFAULT,
  			    r->pool)) != APR_SUCCESS) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  		     "Failed to open CAD file '%s'", _my->cad_path);
  	return 1;
      };
  
      if (_rlock(sv, r, f, _my->cad_path)) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  		     "Failed to lock CAD file '%s'", _my->cad_path);
  	return 1;
      }
  
      cad_uid = 0;
      while (cad_uid < numrecords) {
  	int id, dem, adw, adm, exp;
  	apr_size_t l;
  	if ((rv =
  	   apr_file_read_full(f, cadline, CADRLENGTH, &l)) != APR_SUCCESS) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  			 "Failed to read from CAD file '%s'", _my->cad_path);
  	    e = 1;
  	    break;
  	};
  	/*
  	 * Decode AD file (see specweb page ..)
  	 *
  	 * 0123456789.123456789.123456789.12345678 01234 01234567 01234567
  	 * 012 0123456789n "%5d %8X %8X %3d %10d\n", Ad_id,
  	 * AdDemographics, Weightings, Minimum_Match_Value,
  	 * Expiration_Time
  	 *
  	 */
  	if (sscanf(cadline, "%d %x %x %d %d", &id, &dem, &adw, &adm, &exp) !=
  	    5) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  			 "Entry CAD file corrupted");
  	    continue;
  	}
  
  	if (cad_uid != id) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  			 "Entry CAD file Id# out of sync");
  	    continue;
  	}
  	_my->cad[cad_uid].addemographics = dem;
  	_my->cad[cad_uid].gen_weightings = (adw & 0x00f0000) >> 16;
  	_my->cad[cad_uid].age_weightings = (adw & 0x000f000) >> 12;
  	_my->cad[cad_uid].reg_weightings = (adw & 0x0000f00) >> 8;
  	_my->cad[cad_uid].int1_weightings = (adw & 0x00000f0) >> 4;
  	_my->cad[cad_uid].int2_weightings = (adw & 0x000000f);
  	_my->cad[cad_uid].minimum_match_value = adm;
  	_my->cad[cad_uid].expiration_time = exp;
  	cad_uid++;
      }
  
      if (apr_file_unlock(f) != APR_SUCCESS) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, sv,
  		     "Failed to unlock the CAD file '%s'", _my->cad_path);
  	e = 1;
      };
  
      apr_file_close(f);
  
      if (e)
  	_my->cad_lastmod = 0;
      else
  	_my->cad_lastmod = s.mtime;
  
      return e;
  }
  
  static void *specweb99_server_create(apr_pool_t * p, server_rec *s)
  {
      struct specweb99_module_data *_my;
      _my = (struct specweb99_module_data *)
  	apr_pcalloc(p, sizeof(struct specweb99_module_data));
  
      _my->up_lastmod = (apr_time_t) 0L;
      _my->cad_lastmod = (apr_time_t) 0L;
  
      _my->up = NULL;
      _my->cad = NULL;
  
      _my->up_count = 0;
      _my->cad_count = 0;
  
      return (void *) _my;
  }
  
  static int specweb99_module_init(apr_pool_t * p,
  		       apr_pool_t * plog, apr_pool_t * ptemp, server_rec *s)
  {
      ap_add_version_component(p, NAME "/" VERSION);
  
      ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, 0, s,
  		 NAME "/" VERSION " module: Compiled on %s at %s", __DATE__,
  		 __TIME__);
  
      ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, 0, s, "%s",
  	    "$Id: mod_specweb99.c,v 1.1 2002/05/02 20:34:13 gregames Exp $");
  
      return 0;
  }				/* specweb99_module_init */
  
  static void specweb99_child_init(apr_pool_t * p, server_rec *s)
  {
      struct specweb99_module_data *_my =
  	ap_get_module_config(s->module_config, &specweb99_module);
      struct request_rec r;
      const char *docroot;
  
      r.server = s;
      docroot = ap_document_root(&r);
  
      _my->check = 0;
      _my->up_path = ap_make_full_path(p, docroot, "User.Personality");
      _my->cad_path = ap_make_full_path(p, docroot, "Custom.Ads");
      _my->log_path = ap_make_full_path(p, docroot, "post.log");
      _my->upfgen99 = ap_make_full_path(p, docroot, "upfgen99");
      _my->cadgen99 = ap_make_full_path(p, docroot, "cadgen99");
  
      if (apr_pool_create(&(_my->up_pool), p) != APR_SUCCESS)
  	exit(APEXIT_CHILDFATAL);
  
      if (apr_pool_create(&(_my->cad_pool), p) != APR_SUCCESS)
  	exit(APEXIT_CHILDFATAL);
  
      if (s->next) {
  	fprintf(stderr,
  		"WARNING- this specweb module currently does not support vhosts/services\n"
  		"See %s:%d for what you need to change. The server will continue and assume\n"
  		"the config of the base server\n", __FILE__, __LINE__ + 2);
  
  	/*
  	 * Right now we assume you are specwebbing a whole server install -
  	 * as opposed to a host:port:protocol instance tied to a virtual
  	 * service.
  	 *
  	 * To support vhosts - the _my module config needs simply to be moved to
  	 * the per server config block (or the per dir block) and the init
  	 * and/or any access to it need to either go through the ->nxt list
  	 * OR carefull overlay merging needs to be done to a sensible default
  	 * for each of the cases. The current simplistic 'docroot' references
  	 * are propably no longer going to work and will need explicit config
  	 * (e.g. think ~user and other redirect cases with clobber the
  	 * concept of a docroot).
  	 */
      };
  }				/* specweb99_module_init */
  
  
  static int specweb99_hk_handler(request_rec *r)
  {
      struct specweb99_module_data *_my =
  	ap_get_module_config(r->server->module_config, &specweb99_module);
      apr_file_t *f;
      char *data, *key, *val, *line  = "<unset>";
      apr_table_t *tab;
      apr_status_t rv;
      const char *maxload, *maxthread, *pointtime, *urlroot;
      char *exp, *rootdir, *saveargs;
      char *cmd1, *cmd2, *c;
      int  cmd1res, cmd2res;
      const char *docroot = ap_document_root(r);
      apr_uri_t urlrootrec;	/* To parse the urlroot string into */
  
      if (strcmp(r->handler, "specweb99hk"))
  	return DECLINED;
  
      if (strstr(r->args, "command/Fetch") != NULL) {
  	returnHTMLPageWithFile(r, _my->log_path);
  	return OK;
      }
      else if ((data = strstr(r->args, "command/Reset"))) {
  	/*
  	 * We are sleeping at least one second - to make sure that any
  	 * fstat() on mtime will actually yeild different values - no matter
  	 * how closely spaced the Reset's are issued. (in particular the
  	 * spacing between the test reset form the manager and the reset at
  	 * the commencing - which normally can be within a second - thus
  	 * having identical mtime's on platforms with second granuaarity
  	 * (Solaris,Lnux).
  	 */
  	apr_sleep(2);
  
  	/*
  	 * 1 012345678901234
  	 * command/Reset&maxload=[MaxLoad]&pttime=[PointTime]&maxthreads=[
  	 * MaxThreads]&exp=[ExpiredList]&urlroot=[UrlRoot]
  	 */
  	data += 14;		/* position at start of argument string */
  	/* Tokenize argument string */
  	tab = apr_table_make(r->pool, 0);
  
  	while (*data && (val = ap_getword(r->pool,
  					  (const char **) &data, '&'))) {
  	    key = ap_getword(r->pool, (const char **) &val, '=');
  	    ap_unescape_url(key);
  	    ap_unescape_url(val);
  	    apr_table_set(tab, key, val);
  	}
  	/* Put arguments in variables */
  	maxload = apr_table_get(tab, "maxload");
  	pointtime = apr_table_get(tab, "pttime");
  	/*
  	 * The Run Rules pseudocode is ambivalent about this token name: the
  	 * pseudocode says 'maxthreads' but its test command a couple of
  	 * lines down says 'maxthread'. Aside from the question whether we
  	 * should at all pay attention to the token names, I'm going along
  	 * with what the manager script sends which is 'maxthread'.
  	 */
  	maxthread = apr_table_get(tab, "maxthread");
  
  	/*
  	 * OK, this vexes me. Every shred of documentation about SPECWeb
  	 * speaks of a comma-separated list of expired ads, but the cadgen99
  	 * program segfaults if you pass anything but a whitespace- separated
  	 * list. The Run Rules explicitly state that the pseudo code is the
  	 * definitive Reference By Which This Module Shall Be Coded, yet I
  	 * had to yank the following gem from the perl script:
  	 */
  	exp = apr_pstrdup(r->pool, apr_table_get(tab, "exp"));
  	for(c=exp;*c;c++)
  	    if(*c == ',') *c = ' ';
  
  	urlroot = apr_table_get(tab, "urlroot");
  
  	/*
  	 * Prep: we got a URI from the request. Need to parse that, extract
  	 * the local part and tack that onto docroot.
  	 */
  	if ((rv =
  	     apr_uri_parse(r->pool, urlroot, &urlrootrec)) != APR_SUCCESS) {
  	    ap_log_error(APLOG_MARK, APLOG_NOERRNO, rv, r->server,
  			 "The URL Root '%s' was invalid", urlroot);
  	    returnHTMLPageWithMessage(r, "The UrlRoot passed was invalid");
  	    return OK;
  	}
          if (!urlrootrec.path) {
              urlrootrec.path = "";
          }
  	rootdir = ap_os_escape_path(r->pool,
  				    ap_make_full_path(r->pool,
  						      docroot,
  						      urlrootrec.path), 0);
  
  	/* Call upfgen and cadgen */
  	/*
  	 * Keep request arguments around, we need them for eventual response
  	 */
  	saveargs = apr_pstrdup(r->pool, r->args);
  
  	cmd1 = apr_psprintf(r->pool, "%s -C %s -n %s -t %s",
  	    _my->upfgen99, docroot, maxload, maxthread);
  
  	if((cmd1res = system(cmd1)) != 0) 
  	    ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,
  		"Call failed %d=%s",cmd1res,cmd1);
  	else
  	    ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server,
  		"Called %s",cmd1);
  
  	cmd2 = apr_psprintf(r->pool, "%s -C %s -e %s -t %s %s",
  	    _my->cadgen99, docroot, pointtime, maxthread, exp);
  
  	cmd2res = system(cmd2);
  
  	if((cmd1res = system(cmd2)) != 0) 
  	    ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,
  		"Call failed %d=%s",cmd2res,cmd2);
  	else
  	    ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server,
  		"Called %s",cmd2);
  
  	r->args = saveargs;
  	/*
  	 * Reset post.log i.e. Truncate, open for writing
  	 */
  	if ((rv =
  	     apr_file_open(&f, _my->log_path, 
  			   APR_WRITE | APR_CREATE | APR_TRUNCATE,
  			   APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
  			 "Could not open post.log '%s' for writing",
  			 _my->log_path);
  	    returnHTMLPageWithMessage(r,
  			      "Error: couldn't open post.log for writing.");
  	    return OK;
  	}
  
  	line = apr_psprintf(r->pool, "%10d\n", 0);
  
  	if (apr_file_write_full(f, line, strlen(line), NULL) != APR_SUCCESS) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
  			 "Could not write to post.log '%s'", _my->log_path);
  	    returnHTMLPageWithMessage(r,
  				      "Error: could not write to post.log.");
  	}
  	else {
  	    returnHTMLPageWithMessage(r, "%s\n%d\n%s\n%d\n",
  		cmd1,cmd1res,cmd2,cmd2res);
  	}
  
  	apr_file_close(f);
  
  	return OK;
      }				/* Reset Command */
  
      /* Fall through */
      returnHTMLPageWithMessage(r, "Error: unrecognized command '%s'", r->args);
      return OK;
  }
  
  
  /***********************************************************************
   * specweb99_get_handler                                               *
   ***********************************************************************/
  
  static int specweb99_get_handler(request_rec *r)
  {
      char *path;
      const char *docroot = ap_document_root(r);
  
      if (strcmp(r->handler, "specweb99get"))
  	return DECLINED;
      /*
       * This should not be tested for every request. Eventually, this code
       * will find its way to the housekeeping command handler, but that does
       * not exist yet.
       */
  
      if(r->args == NULL) {
          returnHTMLPageWithMessage(r,"Error: No file argument provided");
          return OK;
      }
  
      /*
       * Construct the path to our file. Note that using ap_document_root() is
       * not senang. I should do this a subrequest but OTOH that would take
       * time and we don't have time.
       */
      path = ap_make_full_path(r->pool, docroot, r->args);
      returnHTMLPageWithFile(r, path);
  
      return OK;
  }				/* specweb99_get_handler */
  
  
  
  /***********************************************************************
   * customadscan                                                        *
   ***********************************************************************/
  
  static caddr_t customadscan(request_rec *r, char *fname, int16_t adid)
  {
      struct apr_finfo_t s;
      apr_status_t rv;
      apr_int16_t i;
      apr_file_t *f;
      apr_size_t len, l;
      char *buf;
      char *index, *N, *X, *Y;
  
      N = apr_psprintf(r->pool, "%05d", adid / 36);
      X = apr_psprintf(r->pool, "%1d", (adid % 36) / 9);
      Y = apr_psprintf(r->pool, "%1d", adid % 9);
  
      if ((rv = apr_stat(&s, fname, APR_FINFO_SIZE, r->pool)) != APR_SUCCESS)
  	return NULL;
  
      if ((rv =
  	 apr_file_open(&f, fname, APR_READ, APR_OS_DEFAULT,
  		       r->pool)) != APR_SUCCESS)
  	return NULL;
  
      len = s.size;
      buf = apr_palloc(r->pool, len + 1);
  
      if (((rv = apr_file_read_full(f, buf, len, &l)) != APR_SUCCESS)
  	|| (l != len))
  	return NULL;		/* Error on read */
  
      buf[len] = '\0';		/* Null-terminate it so that everybody else
  				 * who has to handle this knows how long the
  				 * buffer is. */
      index = buf;
      /*
       * It says in the run rules that we are to scan until the end of the
       * file... what if there are more than one occurrence of the ad (common
       * disease on todays web pages)?
       */
      while ((index = strstr(index, MARKER)) != NULL) {
  	/* This lands us a new index */
  /* <!WEB99CAD><IMG SRC="/file_set/dirNNNNN/classX_Y">
   * 01234567890123456789012345678901234567890123456789
   *           1         2         3         4          */
  	for (i = 0; i < 5; i++) {
  	    *(index + 34 + i) = N[i];
  	}
  	*(index + 45) = *X;
  	*(index + 47) = *Y;
  	index += 50;		/* Put the index past this marker, continue
  				 * scanning */
      }
      return buf;
  
  }				/* customadscan */
  
  
  /***********************************************************************
   * specweb99_cadget_handler                                            *
   ***********************************************************************/
  
  static int specweb99_cadget_handler(request_rec *r)
  {
      struct specweb99_module_data *_my =
      ap_get_module_config(r->server->module_config, &specweb99_module);
      char *cookie_in, *cookie_out, *end;
      const char *docroot = ap_document_root(r);
      char *filename;
      int16_t my_user, last_ad, userindex, adindex, expired = 0;
      u_int32_t userdemographics, combineddemographics;	/* it's a bitmap */
      u_int16_t ad_weight;
  
      if (strcmp(r->handler, "specweb99cadget"))
  	return DECLINED;
      /*
       * This should not be tested for every request. Eventually, this code
       * will find its way to the housekeeping command handler, but that does
       * not exist yet.
       */
      /* Get the cookie */
      cookie_in = (char *) apr_table_get(r->headers_in, "Cookie");
  #ifdef DEBUG
      specweb99_debug(r->server, ap_psprintf(r->pool,
  					"Got cadget request, cookie is: %s",
  					   cookie_in));
  #endif
  
      /*
       * XXX Again, ap_document_root is deprecated. I should probably find the
       * document root in my init handler and keep it around.
       */
      filename = ap_make_full_path(r->pool, docroot, r->args);
  
  #ifdef DEBUG
      ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, r->server,
  		 "Full path is '%s'", filename);
  #endif
  
      /*
       * Parse Cookie string into MyUser and Last_Ad. The format of the cookie
       * is as follows (the order of keys and values is fixed): 1         2 3 4
       * 5 012345678901234567890123456789012345678901234567890123456789
       * my_cookie=user_id=[MyUser]&last_ad=[Last_ad]
       */
      if (!cookie_in) {
  	ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
  		     "Error: expected cookie but found none");
  	returnHTMLPageWithMessage(r, "Error: expected cookie but found none");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }				/* Is there a cookie */
  
      /* Parse the incoming Cookie data */
      /* Format of input: user_id=<x>&last_ad=<y> */
      my_user = strtol(cookie_in + 18, &end, 10);
      last_ad = atoi(end + 9);	/* We trust that there is something behind
  				 * the last_ad value to stop the conversion */
  #ifdef DEBUG
      specweb99_debug(r->server, ap_psprintf(r->pool,
  					"UserID: %d, LastAd: %d, Valid: %s",
  					   my_user,
  					   last_ad,
  					   (cookie_in ==
  					    end) ? "no" : "yes"));
  #endif
  
      /*
       * Calculate UserIndex into User.Personality file UserIndex = MyUser -
       * 10000
       */
      userindex = my_user - 10000;
  
      /*
       * Find User.Personality record using UserIndex
       */
  
      if (checkUPFile(r->server, r, _my)) {
  	ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
  		     "User personality check failed.");
  	returnHTMLPageWithMessage(r,
  			      "Error: User personality file check failed.");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      if (userindex < 0 || userindex >= _my->up_count) {
  	/* Couldn't find it, so let's make our mark and leave */
  #ifdef DEBUG
  	specweb99_debug(r->server, "User record not found");
  #endif
  	returnHTMLPageWithMessage(r,
  	      "User Record %d not found (out of my current range %d .. %d)",
  				  userindex + 10000, 10000,
  				  _my->up_count + 10000 - 1);
  	ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
  	      "User Record %d not found (out of my current range %d .. %d)",
  		     userindex + 10000, 10000, _my->up_count + 10000 - 1);
  	return OK;
      }
  
      userdemographics = _my->up[userindex];
  
      if (checkCADFile(r->server, r, _my)) {
  	returnHTMLPageWithMessage(r, "Error: Ad file check failed.");
  	ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
  		     "Ad file check failed");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      adindex = (last_ad + 1) % 360;
  /*
      Do For Each Ad in Custom.Ads starting where Ad_index == Ad_id
  */
      while (1) {
  /*        CombinedDemographics = ( AdDemographics & UserDemographics ) */
  	combineddemographics =
  	    (_my->cad[adindex].addemographics) & userdemographics;
  /*        Ad_weight = 0 */
  	ad_weight = 0;
  	if (combineddemographics & GENDER_MASK) {
  	    ad_weight += _my->cad[adindex].gen_weightings;
  	}
  	if (combineddemographics & AGE_GROUP_MASK) {
  	    ad_weight += _my->cad[adindex].age_weightings;
  	}
  	if (combineddemographics & REGION_MASK) {
  	    ad_weight += _my->cad[adindex].reg_weightings;
  	}
  	if (combineddemographics & INTEREST1_MASK) {
  	    ad_weight += _my->cad[adindex].int1_weightings;
  	}
  	if (combineddemographics & INTEREST2_MASK) {
  	    ad_weight += _my->cad[adindex].int2_weightings;
  	}
  	if (ad_weight >= _my->cad[adindex].minimum_match_value) {
  	    break;
  	}
  	adindex = (adindex + 1) % 360;
  	if (adindex == last_ad) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
  			 "Ad to expire not found");
  	    break;
  	}
      }
      expired =
  	((time((time_t *) NULL) > _my->cad[adindex].expiration_time)) ? 1 : 0;
  
      ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, 0, r->server,
  		 "Found ad %d : expire %s (%d > %d)",
  		 adindex, expired ? "yes" : "no",
  		 (int) time((time_t *) NULL),
  		 _my->cad[adindex].expiration_time);
  
      cookie_out = apr_psprintf(r->pool,
  			    "found_cookie=Ad_id=%d&Ad_weight=%d&Expired=%d",
  			      adindex, ad_weight, expired);
      apr_table_setn(r->headers_out, "Set-Cookie", cookie_out);
  
      if ((strstr(filename, "class1") != NULL) ||
  	(strstr(filename, "class2") != NULL)) {
  	caddr_t buf = customadscan(r, filename, adindex);
  	returnHTMLPageWithBuffer(r, (char *) buf);
      }
      else {
  	returnHTMLPageWithFile(r, filename);
      }
      return OK;
  }
  
  
  static char *_log_and_write(struct request_rec *r, apr_file_t * f,
  		            char *filename, const char *urlroot, int dirnum,
  		          int classnum, int filenum, int clientnum, int uid)
  {
      pid_t pid;
      time_t stamp;
      u_int32_t recnum;
      char recnumstr[12];		/* ten wide plus return plus \0 */
      apr_size_t l;
      apr_off_t zero = 0;
      apr_status_t rv;
  
      stamp = time(NULL);
      pid = getpid();
  
      if ((rv = apr_file_read_full(f, recnumstr, 11, &l)) != APR_SUCCESS)
  	return "Failed to read recordcount from post.log";
  
      recnumstr[11] = '\0';
      recnum = atol(recnumstr) + 1;
  
      if ((rv = apr_file_seek(f, APR_SET, &zero)) != APR_SUCCESS)
  	return "Failed to seek 0 to post.log";
  
      if ((rv=(apr_file_printf(f, "%10d", recnum))) < 0) 
  	return "Failed to write num to post.log";
  
      if ((rv = apr_file_seek(f, APR_END, &zero)) != APR_SUCCESS)
  	return "Failed to seek end to post.log";
  
      if ((apr_file_printf
  	 (f, "%10d %10d %10d %5d %2d %2d %10d %-60.60s %10d %10d\n", recnum,
  	  (int) stamp, (int) pid, dirnum, classnum, filenum, clientnum,
  	  filename, (int) pid, uid)) < 0)
  	return "Failed to write record to post.log";
  
      apr_file_flush(f);
  
      return NULL;
  }
  
  static int specweb99_post_handler(request_rec *r)
  {
      struct specweb99_module_data *_my =
      ap_get_module_config(r->server->module_config, &specweb99_module);
      const char *urlroot = "<none>";
      int dirnum = 0, classnum = 0, filenum = 0, clientnum = 0, uid = 0;
      char *filename;
      int posterr;
      const char *cookie_in;
      apr_file_t *f;
      char *data = "<none>";
      const char *type, *docroot;
      char argsbuffer[HUGE_STRING_LEN];
      int rsize, len_read, rpos = 0;
      long length = 0;
      apr_status_t rv, rv2;
  
      if (strcmp(r->handler, "specweb99post"))
  	return DECLINED;
  
      docroot = ap_document_root(r);
  
  /*Begin:*/
  /*    Make substitutions in HTML return page for the following:*/
  /*        Server_Software*/
  /*        Remote_Addr*/
  /*        Script_Name*/
  /*        QueryString*/
  /* The above is done in the returnHTMLPageWith... functions */
  
  /*    Parse PostInput - a sample format is as follows */
  /*    (keys may be received in any order):*/
  /*        urlroot=[urlroot]&dir=[Dir#]&class=[Class#]&num=[File#]&client=[Client#]*/
  
      type = apr_table_get(r->headers_in, "Content-Type");
      /*
       * Scream in protest if the user uses the broken version of SPECWeb99
       * manager that doesn't send the Content-Type header. Note that this only
       * affects the pre-run tests: the regular client does send the header.
       */
      if ((type == NULL) || (strcasecmp(type, DEFAULT_ENCTYPE) != 0)) {
  	ap_log_error(APLOG_MARK,
  		     APLOG_WARNING | APLOG_NOERRNO, 0,
  		     r->server,
  		     "The client didn't send %s as Content-Type. Version "
  		     "1.02 of the SPECWeb does not do this and thus violates "
  		     "the HTTP specification. Please apply the following "
  		     "patch to your manager script and bitch to SPEC that "
  		     "they fix this:\n%s", DEFAULT_ENCTYPE,
  		     SPEC_MANAGER_PATCH);
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      if (ap_setup_client_block(r, REQUEST_CHUNKED_ERROR) != OK) {
  	ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,
  		     "Could not setup client block");
  	returnHTMLPageWithMessage(r, "Couldn't set up client block");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      if (!ap_should_client_block(r)) {
  	ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,
  		     "No POST data");
  	returnHTMLPageWithMessage(r, "No POST data");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      length = r->remaining;
  
      data = apr_pcalloc(r->pool, length + 1);
  
      while ((len_read =
  	    ap_get_client_block(r, argsbuffer, sizeof(argsbuffer))) > 0) {
  	if ((rpos + len_read) > length) {
  	    rsize = length - rpos;
  	}
  	else {
  	    rsize = len_read;
  	}
  	memcpy((char *) data + rpos, argsbuffer, rsize);
  	rpos += rsize;
      }
  
      data[length] = '\0';
  
      posterr = 5;		/* Counter to make sure we get all VARiables
  				 * from the CGI post */
      while (data) {
  	const char *p = data;
  
  	data = index(p, '&' /* 0x26 */ );
  	if (data != NULL)
  	    *data++ = '\0';
  
  	if (strncmp(p, "urlroot=", 8) == 0) {
  	    urlroot = apr_pstrdup(r->pool, p + 8);
  	    posterr--;
  	}
  	else if (strncmp(p, "dir=", 4) == 0) {
  	    dirnum = atoi(p + 4);
  	    posterr--;
  	}
  	else if (strncmp(p, "class=", 6) == 0) {
  	    classnum = atoi(p + 6);
  	    posterr--;
  	}
  	else if (strncmp(p, "num=", 4) == 0) {
  	    filenum = atoi(p + 4);
  	    posterr--;
  	}
  	else if (strncmp(p, "client=", 7) == 0) {
  	    clientnum = atoi(p + 7);
  	    posterr--;
  	}
      }
      if (posterr != 0) {
  	ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0,
  		     r->server, "Did not get all POST arguments");
  	returnHTMLPageWithMessage(r, "Did not get all POST arguments");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
  /*    Parse Cookie string to get MyCookie. The format is as */
  /*    follows (the order of the keys and values is fixed):*/
      /*
       * 1         2       012345678901234567890123
       * my_cookie=user_id=[MyCookie]&last_ad=[IgnoredField]
       */
  
      cookie_in = apr_table_get(r->headers_in, "Cookie");
      if (cookie_in == NULL) {
  	ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r->server,
  		     "No cookie");
  	returnHTMLPageWithMessage(r, "Invalid Cookie");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
      uid = atoi(cookie_in + 18);
  
  /*  Filename = [urlroot]/dir[5-digit Dir#]/class[Class#]_[File#]*/
  /*        (for example, the POST input of */
  /*        urlroot=/specweb99/file_set&dir=00123&class=1&num=1&client=10003 */
  /*        would make Filename = /specweb99/file_set/dir00123/class1_1)*/
  
      filename = ap_make_full_path(r->pool,
  				 docroot, apr_psprintf(r->pool,
  						  "%s/dir%05d/class%1d_%1d",
  						       urlroot, dirnum,
  						       classnum, filenum));
  
      /* Do_atomically (for example, using a file lock or other mutex): */
  
      if ((rv = apr_file_open(&f, _my->log_path, APR_READ | APR_WRITE,
  			    APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
  		     "Failed to open post.log '%s' for updating",
  		     _my->log_path);
  	returnHTMLPageWithMessage(r,
  			       "Failed to open post.log file for updating");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      if ((rv = _wlock(r->server, r, f, _my->log_path)) != APR_SUCCESS)
  	returnHTMLPageWithMessage(r, "Failed to lock post.log file");
      else {
  	char *msg =
  	_log_and_write(r, f, filename, urlroot, dirnum, classnum, filenum,
  		       clientnum, uid);
  	if (msg) {
  	    rv = APR_OS_START_USEERR;
  	    ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, msg);
  	    returnHTMLPageWithMessage(r, msg);
  	}
      }
  
      if ((rv2 = apr_file_unlock(f)) != APR_SUCCESS) {
  	if (rv == APR_SUCCESS) {
  	    rv = rv2;
  	    ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
  			 "Failed to unlock %s", filename ? filename : "");
  	    returnHTMLPageWithMessage(r, "Failed to lock unpost.log file");
  	}
      }
  
      apr_file_close(f);
  
      if (rv != APR_SUCCESS)
  	return HTTP_INTERNAL_SERVER_ERROR;	/* _log_and_write() will have
  						 * displayed a page already */
  
      if ((rv = apr_file_open(&f, filename, APR_READ,
  			    APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
  		     "Could not open File '%s' (post handler)", filename);
  	returnHTMLPageWithMessage(r, "Could not open file");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      /*
       * XXXX FIXME - close and open later cause of APR not having an fstat()
       */
      apr_file_close(f);
  
      /*
       * CookieString = "my_cookie=<myCookie>"
       * 
       * XXX    seems from code inspection that this really is not setting again
       * the cookie - but a change the cookie is to be set to %d of the user
       * number.
       */
      apr_table_setn(r->headers_out, "Set-Cookie",
  		   apr_psprintf(r->pool, "my_cookie=%d", uid));
  
      /* 
       * Return HTML Page with File='RootDir/FileName' and Cookie=CookieString
       */
      returnHTMLPageWithFile(r, filename);
  
      return OK;
  }				/* specweb99_post_handler */
  
  static void register_hooks(apr_pool_t * p)
  {
      ap_hook_post_config(specweb99_module_init, NULL, NULL, APR_HOOK_MIDDLE);
      ap_hook_child_init(specweb99_child_init, NULL, NULL, APR_HOOK_MIDDLE);
  
      ap_hook_handler(specweb99_hk_handler, NULL, NULL, APR_HOOK_MIDDLE);
      ap_hook_handler(specweb99_get_handler, NULL, NULL, APR_HOOK_MIDDLE);
      ap_hook_handler(specweb99_cadget_handler, NULL, NULL, APR_HOOK_MIDDLE);
      ap_hook_handler(specweb99_post_handler, NULL, NULL, APR_HOOK_MIDDLE);
  
  }
  
  module AP_MODULE_DECLARE_DATA specweb99_module = {
      STANDARD20_MODULE_STUFF,
      NULL,			/* dir config creater */
      NULL,			/* dir merger --- default is to override */
      specweb99_server_create,	/* server config */
      NULL,			/* merge server config */
      NULL,			/* command apr_table_t */
      register_hooks		/* register hooks */
  };
  
  
  
  1.1                  httpd-test/specweb99/specweb99-2.0/mod_specweb99.h
  
  Index: mod_specweb99.h
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2002 The Apache Software Foundation.  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. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 was contributed by Covalent Technologies Inc, 
   * http://www.covalent.net around April 20002.
   *
   * mod_specweb99.h -- Header file for Apache specweb99 module
   * sctemme July 2001
   *
   * See the file LICENSE.txt for license info.
   *
   * { sctemme | dirkx }@{ apache.org | covalent.net }
   *
   */
  
   /*
    * ! @header mod_specweb99 The mod_specweb99 module implements the SPECWeb99
    * dynamic content requests. It should work with Apache 2.0 by enabling the
    * request handlers in the Apache configuration file. Since this module is
    * self-contained and called exclusively through its handlers, this
    * documentation should be seen purely as internal, not to reuse the symbols
    * it describes.
    */
  
  #ifndef MOD_SPECWEB99_H
  #define MOD_SPECWEB99_H
  
  #define BOILERPLATE_START \
  	"<html>\n"\
  	"<head><title>SPECweb99 Dynamic GET & POST Test</title></head>\n"\
  	"<body>\n"\
  	"<p>SERVER_SOFTWARE = %s\n"\
  	"<p>REMOTE_ADDR = %s\n"\
  	"<p>SCRIPT_NAME = %s\n"\
  	"<p>QUERY_STRING = %s\n"\
  	"<pre>\n"
  
  #define BOILERPLATE_END \
  	"\n</pre>\n</body></html>\n"
  
  #define MARKER \
  	"<!WEB99CAD><IMG SRC=\"/file_set/dir"
  
  /* Record lengths including newline */
  #define UPRLENGTH  	(15)
  #define CADRLENGTH 	(39)
  
  #define GENDER_MASK	 (  0x3 << 28)
  #define AGE_GROUP_MASK   (  0xf << 24)
  #define REGION_MASK      (  0xf << 20)
  #define INTEREST1_MASK   (0x3ff << 10)
  #define INTEREST2_MASK   (0x3ff <<  0)
  
  #define GENDER_WT_OFF    16
  #define AGE_GROUP_WT_OFF 12
  #define REGION_WT_OFF     8
  #define INTEREST1_WT_OFF  4
  #define INTEREST2_WT_OFF  0
  
  #define DEFAULT_ENCTYPE "application/x-www-form-urlencoded"
  #define SPEC_MANAGER_PATCH "*** manager.orig        Wed Aug 23 16:19:12 2000\n\
  --- manager     Wed Aug 23 16:20:18 2000\n\
  ***************\n\
  *** 1691,1696 ****\n\
  --- 1691,1700 ----\n\
          $req = new HTTP::Request 'POST' => $url . \"f\";\n\
          my($urlroot) = ($url =~ m#\?(.*file_set/)#);\n\
          $req->content(\"class=1&client=2&dir=00004&num=3&urlroot=$urlroot\");\n\
  +\n\
  +       # ddhill added \n\
  +       $req->header('Content-type' => 'application/x-www-form-urlencoded');\n\
  +       # end ddhill added\n\
        }\n\
        else {\n\
          $req = new HTTP::Request 'GET' => $url;\n"
  
  /* Structure to hold custom ad data */
  
  struct cadrec {
      u_int32_t addemographics;
      u_int32_t age_weightings;
      u_int32_t gen_weightings;
      u_int32_t reg_weightings;
      u_int32_t int1_weightings;
      u_int32_t int2_weightings;
      u_int16_t minimum_match_value;
      u_int32_t expiration_time;
  };
  
  #endif
  
  
  

Mime
View raw message