Support for both static and dynamically generated CGI pages.
[privoxy.git] / loadcfg.c
index ae33439..18db099 100644 (file)
--- a/loadcfg.c
+++ b/loadcfg.c
@@ -1,4 +1,4 @@
-const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.4 2001/05/22 18:46:04 oes Exp $";
+const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.19 2001/07/15 17:45:16 jongfoster Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/loadcfg.c,v $
@@ -35,6 +35,92 @@ const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.4 2001/05/22 18:46:04 oes Exp $";
  *
  * Revisions   :
  *    $Log: loadcfg.c,v $
+ *    Revision 1.19  2001/07/15 17:45:16  jongfoster
+ *    Removing some unused #includes
+ *
+ *    Revision 1.18  2001/07/13 14:01:14  oes
+ *     - Removed all #ifdef PCRS
+ *     - Removed vim-settings
+ *
+ *    Revision 1.17  2001/06/29 13:31:03  oes
+ *    - Improved comments
+ *    - Fixed (actionsfile) and sorted hashes
+ *    - Introduced admin_address and proxy-info-url
+ *      as config parameters
+ *    - Renamed config->proxy_args_invocation (which didn't have
+ *      the invocation but the options!) to config->proxy_args
+ *    - Various adaptions
+ *    - Removed logentry from cancelled commit
+ *
+ *    Revision 1.16  2001/06/09 10:55:28  jongfoster
+ *    Changing BUFSIZ ==> BUFFER_SIZE
+ *
+ *    Revision 1.15  2001/06/07 23:13:40  jongfoster
+ *    Merging ACL and forward files into config file.
+ *    Cosmetic: Sorting config file options alphabetically.
+ *    Cosmetic: Adding brief syntax comments to config file options.
+ *
+ *    Revision 1.14  2001/06/07 14:46:25  joergs
+ *    Missing make_path() added for re_filterfile.
+ *
+ *    Revision 1.13  2001/06/05 22:33:54  jongfoster
+ *
+ *    Fixed minor memory leak.
+ *    Also now uses make_path to prepend the pathnames.
+ *
+ *    Revision 1.12  2001/06/05 20:04:09  jongfoster
+ *    Now uses _snprintf() in place of snprintf() under Win32.
+ *
+ *    Revision 1.11  2001/06/04 18:31:58  swa
+ *    files are now prefixed with either `confdir' or `logdir'.
+ *    `make redhat-dist' replaces both entries confdir and logdir
+ *    with redhat values
+ *
+ *    Revision 1.10  2001/06/03 19:11:54  oes
+ *    introduced confdir option
+ *
+ *    Revision 1.9  2001/06/01 20:06:24  jongfoster
+ *    Removed support for "tinygif" option - moved to actions file.
+ *
+ *    Revision 1.8  2001/05/31 21:27:13  jongfoster
+ *    Removed many options from the config file and into the
+ *    "actions" file: add_forwarded, suppress_vanilla_wafer,
+ *    wafer, add_header, user_agent, referer, from
+ *    Also globally replaced "permission" with "action".
+ *
+ *    Revision 1.7  2001/05/29 09:50:24  jongfoster
+ *    Unified blocklist/imagelist/permissionslist.
+ *    File format is still under discussion, but the internal changes
+ *    are (mostly) done.
+ *
+ *    Also modified interceptor behaviour:
+ *    - We now intercept all URLs beginning with one of the following
+ *      prefixes (and *only* these prefixes):
+ *        * http://i.j.b/
+ *        * http://ijbswa.sf.net/config/
+ *        * http://ijbswa.sourceforge.net/config/
+ *    - New interceptors "home page" - go to http://i.j.b/ to see it.
+ *    - Internal changes so that intercepted and fast redirect pages
+ *      are not replaced with an image.
+ *    - Interceptors now have the option to send a binary page direct
+ *      to the client. (i.e. ijb-send-banner uses this)
+ *    - Implemented show-url-info interceptor.  (Which is why I needed
+ *      the above interceptors changes - a typical URL is
+ *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
+ *      The previous mechanism would not have intercepted that, and
+ *      if it had been intercepted then it then it would have replaced
+ *      it with an image.)
+ *
+ *    Revision 1.6  2001/05/26 00:28:36  jongfoster
+ *    Automatic reloading of config file.
+ *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
+ *    Most of the global variables have been moved to a new
+ *    struct configuration_spec, accessed through csp->config->globalname
+ *    Most of the globals remaining are used by the Win32 GUI.
+ *
+ *    Revision 1.5  2001/05/25 22:34:30  jongfoster
+ *    Hard tabs->Spaces
+ *
  *    Revision 1.4  2001/05/22 18:46:04  oes
  *
  *    - Enabled filtering banners by size rather than URL
@@ -115,19 +201,16 @@ const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.4 2001/05/22 18:46:04 oes Exp $";
 
 #ifdef _WIN32
 
-# include <sys/timeb.h>
 # include <windows.h>
-# include <io.h>
-# include <process.h>
-# ifdef TOGGLE
-#  include <time.h>
-# endif /* def TOGGLE */
 
 # include "win32.h"
 # ifndef _WIN_CONSOLE
 #  include "w32log.h"
 # endif /* ndef _WIN_CONSOLE */
 
+/* VC++ has "_snprintf", not "snprintf" */
+#define snprintf _snprintf
+
 #else /* ifndef _WIN32 */
 
 # include <unistd.h>
@@ -139,6 +222,7 @@ const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.4 2001/05/22 18:46:04 oes Exp $";
 #endif
 
 #include "loadcfg.h"
+#include "list.h"
 #include "jcc.h"
 #include "filters.h"
 #include "loaders.h"
@@ -149,6 +233,7 @@ const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.4 2001/05/22 18:46:04 oes Exp $";
 #include "errlog.h"
 #include "jbsockets.h"
 #include "gateway.h"
+#include "ssplit.h"
 
 const char loadcfg_h_rcs[] = LOADCFG_H_VERSION;
 
@@ -163,157 +248,136 @@ const char loadcfg_h_rcs[] = LOADCFG_H_VERSION;
 #define ijb_isupper(__X) isupper((int)(unsigned char)(__X))
 #define ijb_tolower(__X) tolower((int)(unsigned char)(__X))
 
-static const char VANILLA_WAFER[] =
-   "NOTICE=TO_WHOM_IT_MAY_CONCERN_"
-   "Do_not_send_me_any_copyrighted_information_other_than_the_"
-   "document_that_I_am_requesting_or_any_of_its_necessary_components._"
-   "In_particular_do_not_send_me_any_cookies_that_"
-   "are_subject_to_a_claim_of_copyright_by_anybody._"
-   "Take_notice_that_I_refuse_to_be_bound_by_any_license_condition_"
-   "(copyright_or_otherwise)_applying_to_any_cookie._";
-
-#ifdef TOGGLE
+#ifdef FEATURE_TOGGLE
 /* by haroon - indicates if ijb is enabled */
 int g_bToggleIJB        = 1;   /* JunkBusters is enabled by default. */
-#endif
-
-int debug               = 0;
-int multi_threaded      = 1;
-
-#if defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST)
-int tinygif             = 0;
-const char *tinygifurl  = NULL;
-#endif /* defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST) */
-
-const char *logfile     = NULL;
+#endif /* def FEATURE_TOGGLE */
 
+/* The filename of the configfile */
 const char *configfile  = NULL;
 
-const char *blockfile   = NULL;
-const char *permissions_file  = NULL;
-const char *forwardfile = NULL;
-
-#ifdef ACL_FILES
-const char *aclfile     = NULL;
-#endif /* def ACL_FILES */
-
-#ifdef USE_IMAGE_LIST
-const char *imagefile   = NULL;
-#endif /* def USE_IMAGE_LIST */
-
 /*
- * Permissions to use for URLs not in the permissions list.
+ * CGI functions will later need access to the invocation args,
+ * so we will make argc and argv global.
  */
-int default_permissions = PERMIT_RE_FILTER;
-
-#ifdef PCRS
-const char *re_filterfile = NULL;
-#endif /* def PCRS */
-
-#ifdef FAST_REDIRECTS
-int fast_redirects = 0;
-#endif /* def FAST_REDIRECTS */
+int Argc = 0;
+const char **Argv = NULL;
 
-#ifdef TRUST_FILES
-const char *trustfile   = NULL;
-#endif /* def TRUST_FILES */
+static struct file_list *current_configfile = NULL;
 
-#ifdef JAR_FILES
-const char *jarfile     = NULL;
-FILE *jar = NULL;
-#endif /* def JAR_FILES */
 
-const char *referrer    = NULL;
-const char *uagent      = NULL;
-const char *from        = NULL;
+/*
+ * This takes the "cryptic" hash of each keyword and aliases them to
+ * something a little more readable.  This also makes changing the
+ * hash values easier if they should change or the hash algorthm changes.
+ * Use the included "hash" program to find out what the hash will be
+ * for any string supplied on the command line.  (Or just put it in the
+ * config file and read the number from the error message in the log).
+ *
+ * Please keep this list sorted alphabetically (but with the Windows
+ * console and GUI specific options last).
+ */
 
-#ifndef SPLIT_PROXY_ARGS
-const char *suppress_message = NULL;
-#endif /* ndef SPLIT_PROXY_ARGS */
+#define hash_actions_file              1196306641ul /* "actionsfile" */
+#define hash_admin_address             4112573064ul /* "admin-address" */
+#define hash_confdir                      1978389ul /* "confdir" */
+#define hash_debug                          78263ul /* "debug" */
+#define hash_deny_access               1227333715ul /* "deny-access" */
+#define hash_forward                      2029845ul /* "forward" */
+#define hash_forward_socks4            3963965521ul /* "forward-socks4" */
+#define hash_forward_socks4a           2639958518ul /* "forward-socks4a" */
+#define hash_jarfile                      2046641ul /* "jarfile" */
+#define hash_listen_address            1255650842ul /* "listen-address" */
+#define hash_logdir                        422889ul /* "logdir" */
+#define hash_logfile                      2114766ul /* "logfile" */
+#define hash_permit_access             3587953268ul /* "permit-access" */
+#define hash_proxy_info_url            3903079059ul /* "proxy-info-url" */
+#define hash_re_filterfile             3877522444ul /* "re_filterfile" */
+#define hash_single_threaded           4250084780ul /* "single-threaded" */
+#define hash_suppress_blocklists       1948693308ul /* "suppress-blocklists" */
+#define hash_toggle                        447966ul /* "toggle" */
+#define hash_trust_info_url             430331967ul /* "trust-info-url" */
+#define hash_trustfile                   56494766ul /* "trustfile" */
+
+#define hash_activity_animation        1817904738ul /* "activity-animation" */
+#define hash_close_button_minimizes    3651284693ul /* "close-button-minimizes" */
+#define hash_hide_console              2048809870ul /* "hide-console" */
+#define hash_log_buffer_size           2918070425ul /* "log-buffer-size" */
+#define hash_log_font_name             2866730124ul /* "log-font-name" */
+#define hash_log_font_size             2866731014ul /* "log-font-size" */
+#define hash_log_highlight_messages    4032101240ul /* "log-highlight-messages" */
+#define hash_log_max_lines             2868344173ul /* "log-max-lines" */
+#define hash_log_messages              2291744899ul /* "log-messages" */
+#define hash_show_on_task_bar           215410365ul /* "show-on-task-bar" */
 
-int suppress_vanilla_wafer = 0;
-int add_forwarded          = 0;
 
-struct list wafer_list[1];
-struct list xtra_list[1];
 
-#ifdef TRUST_FILES
-struct list trust_info[1];
-struct url_spec *trust_list[64];
-#endif /* def TRUST_FILES */
+/*********************************************************************
+ *
+ * Function    :  unload_configfile
+ *
+ * Description :  Free the config structure and all components.
+ *
+ * Parameters  :
+ *          1  :  data: struct configuration_spec to unload
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+void unload_configfile (void * data)
+{
+   struct configuration_spec * config = (struct configuration_spec *)data;
+   struct forward_spec *cur_fwd = config->forward;
+#ifdef FEATURE_ACL
+   struct access_control_list *cur_acl = config->acl;
 
-/*
- * Port and IP to bind to.
- * Defaults to HADDR_DEFAULT:HADDR_PORT == 127.0.0.1:8000
- */
-const char *haddr = NULL;
-int         hport = 0;
+   while (cur_acl != NULL)
+   {
+      struct access_control_list * next_acl = cur_acl->next;
+      free(cur_acl);
+      cur_acl = next_acl;
+   }
+   config->acl = NULL;
+#endif /* def FEATURE_ACL */
 
-#ifndef SPLIT_PROXY_ARGS
-int suppress_blocklists = 0;  /* suppress listing sblock and simage */
-#endif /* ndef SPLIT_PROXY_ARGS */
+   while (cur_fwd != NULL)
+   {
+      struct forward_spec * next_fwd = cur_fwd->next;
+      free_url(cur_fwd->url);
 
-struct proxy_args proxy_args[1];
+      freez(cur_fwd->gateway_host);
+      freez(cur_fwd->forward_host);
+      free(cur_fwd);
+      cur_fwd = next_fwd;
+   }
+   config->forward = NULL;
+   
+#ifdef FEATURE_COOKIE_JAR
+   if ( NULL != config->jar )
+   {
+      fclose( config->jar );
+      config->jar = NULL;
+   }
+#endif /* def FEATURE_COOKIE_JAR */
 
-int configret = 0;
-int config_changed = 0;
+   freez((char *)config->confdir);
+   freez((char *)config->logdir);
 
+   freez((char *)config->haddr);
+   freez((char *)config->logfile);
 
-/*
- * The load_config function is now going to call `init_proxy_args',
- * so it will need argc and argv.  Since load_config will also be
- * a signal handler, we need to have these globally available.
- */
-int Argc = 0;
-const char **Argv = NULL;
+   freez((char *)config->actions_file);
+   freez((char *)config->admin_address);
+   freez((char *)config->proxy_info_url);
+   freez((char *)config->proxy_args);
 
+#ifdef FEATURE_COOKIE_JAR
+   freez((char *)config->jarfile);
+#endif /* def FEATURE_COOKIE_JAR */
 
-/*
- * This takes the "cryptic" hash of each keyword and aliases them to
- * something a little more readable.  This also makes changing the
- * hash values easier if they should change or the hash algorthm changes.
- * Use the included "hash" program to find out what the hash will be
- * for any string supplied on the command line.  (Or just put it in the
- * config file and read the number from the error message in the log).
- */
+   freez((char *)config->re_filterfile);
 
-#define hash_trustfile                 56494766ul
-#define hash_trust_info_url            449869467ul
-#define hash_debug                     78263ul
-#define hash_tinygif                   2227702ul
-#define hash_add_forwarded_header      3191044770ul
-#define hash_single_threaded           4250084780ul
-#define hash_suppress_vanilla_wafer    3121233547ul
-#define hash_wafer                     89669ul
-#define hash_add_header                237434619ul
-#define hash_permissions_file          3825730796lu /* "permissionsfile" */
-#define hash_logfile                   2114766ul
-#define hash_blockfile                 48845391ul
-#define hash_imagefile                 51447891ul
-#define hash_jarfile                   2046641ul
-#define hash_listen_address            1255650842ul
-#define hash_forwardfile               1268669141ul
-#define hash_aclfile                   1908516ul
-#define hash_re_filterfile             3877522444ul
-#define hash_user_agent                283326691ul
-#define hash_referrer                  10883969ul
-#define hash_referer                   2176719ul
-#define hash_from                      16264ul
-#define hash_fast_redirects            464873764lu
-#define hash_hide_console              2048809870ul
-#define hash_include_stats             2174146548ul
-#define hash_suppress_blocklists       1948693308ul
-#define hash_toggle                    447966ul
-
-#define hash_activity_animation        1817904738ul
-#define hash_log_messages              2291744899ul
-#define hash_log_highlight_messages    4032101240ul
-#define hash_log_buffer_size           2918070425ul
-#define hash_log_max_lines             2868344173ul
-#define hash_log_font_name             2866730124ul
-#define hash_log_font_size             2866731014ul
-#define hash_show_on_task_bar          215410365ul
-#define hash_close_button_minimizes    3651284693ul
+}
 
 
 /*********************************************************************
@@ -323,571 +387,900 @@ const char **Argv = NULL;
  * Description :  Load the config file and all parameters.
  *
  * Parameters  :
- *          1  :  signum : this can be the signal SIGHUP or 0 (if from main).
- *                In any case, we just ignore this and reload the config file.
+ *          1  :  csp = Client state (the config member will be 
+ *                filled in by this function).
  *
- * Returns     :  configret : 0 => Ok, everything else is an error.
- *                Note: we use configret since a signal handler cannot
- *                return a value, and this function does double duty.
- *                Ie. Is is called from main and from signal( SIGHUP );
+ * Returns     :  0 => Ok, everything else is an error.
  *
  *********************************************************************/
-void load_config( int signum )
+struct configuration_spec * load_config(void)
 {
-   char buf[BUFSIZ];
+   char buf[BUFFER_SIZE];
    char *p, *q;
    FILE *configfp = NULL;
+   struct configuration_spec * config = NULL;
+   struct client_state * fake_csp;
+   struct file_list *fs;
 
-   configret = 0; /* FIXME: This is obsolete, always 0. */
-   config_changed = 1;
+   if (!check_file_changed(current_configfile, configfile, &fs))
+   {
+      /* No need to load */
+      return ((struct configuration_spec *)current_configfile->f);
+   }
+   if (!fs)
+   {
+      log_error(LOG_LEVEL_FATAL, "can't check configuration file '%s':  %E",
+                configfile);
+   }
 
    log_error(LOG_LEVEL_INFO, "loading configuration file '%s':", configfile);
 
-   init_proxy_args(Argc, Argv);
+#ifdef FEATURE_TOGGLE
+   g_bToggleIJB      = 1;
+#endif /* def FEATURE_TOGGLE */
 
+   fs->f = config = (struct configuration_spec *)zalloc(sizeof(*config));
 
-   /* (Waste of memory [not quite a "leak"] here.  The 
-    * last blockfile/permissions file/... etc will not be 
-    * unloaded until we load a new one.  If the 
-    * block/... feature has been disabled in 
-    * the new config file, then we're wasting some 
-    * memory we could otherwise reclaim.
-    */
+   if (config==NULL)
+   {
+      freez(fs->filename);
+      freez(fs);
+      log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
+      /* Never get here - LOG_LEVEL_FATAL causes program exit */
+   }
 
-   /* Disable all loaders. */
-   remove_all_loaders();
+   /*
+    * This is backwards from how it's usually done.
+    * Following the usual pattern, "fs" would be stored in a member 
+    * variable in "csp", and then we'd access "config" from "fs->f",
+    * using a cast.  However, "config" is used so often that a 
+    * cast each time would be very ugly, and the extra indirection
+    * would waste CPU cycles.  Therefore we store "config" in
+    * "csp->config", and "fs" in "csp->config->config_file_list".
+    */
+   config->config_file_list = fs;
 
    /*
-    * Reset to as close to startup state as we can.
-    * But leave changing the logfile until after we're done loading.
+    * Set to defaults
     */
 
-#ifdef JAR_FILES
-   if ( NULL != jar )
+   config->multi_threaded    = 1;
+   config->hport             = HADDR_PORT;
+
+   if ((configfp = fopen(configfile, "r")) == NULL)
    {
-      fclose( jar );
-      jar = NULL;
+      log_error(LOG_LEVEL_FATAL, "can't open configuration file '%s':  %E",
+              configfile);
+      /* Never get here - LOG_LEVEL_FATAL causes program exit */
    }
-#endif /* def JAR_FILES */
 
-   debug             = 0;
-   multi_threaded    = 1;
+   while (read_config_line(buf, sizeof(buf), configfp, fs) != NULL)
+   {
+      char cmd[BUFFER_SIZE];
+      char arg[BUFFER_SIZE];
+      char tmp[BUFFER_SIZE];
+#ifdef FEATURE_ACL
+      struct access_control_list *cur_acl;
+#endif /* def FEATURE_ACL */
+      struct forward_spec *cur_fwd;
+      int vec_count;
+      char *vec[3];
+
+      strcpy(tmp, buf);
+
+      /* Copy command (i.e. up to space or tab) into cmd */
+      p = buf;
+      q = cmd;
+      while (*p && (*p != ' ') && (*p != '\t'))
+      {
+         *q++ = *p++;
+      }
+      *q = '\0';
 
-   default_permissions = PERMIT_RE_FILTER;
+      /* Skip over the whitespace in buf */
+      while (*p && ((*p == ' ') || (*p == '\t')))
+      {
+         p++;
+      }
 
-#if defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST)
-   tinygif           = 0;
-   freez((char *)tinygifurl);
-#endif /* defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST) */
+      /* Copy the argument into arg */
+      strcpy(arg, p);
 
-   suppress_vanilla_wafer = 0;
-   add_forwarded     = 0;
-   hport             = HADDR_PORT;
+      /* Should never happen, but check this anyway */
+      if (*cmd == '\0')
+      {
+         continue;
+      }
 
-#ifdef _WIN_CONSOLE
-   hideConsole       = 0;
-#endif /*def _WIN_CONSOLE*/
+      /* Make sure the command field is lower case */
+      for (p=cmd; *p; p++)
+      {
+         if (ijb_isupper(*p))
+         {
+            *p = ijb_tolower(*p);
+         }
+      }
 
-#ifdef TOGGLE
-   g_bToggleIJB      = 1;
-#endif
+      /* Save the argument for show-proxy-args */
+      savearg(cmd, arg, config);
 
-#ifdef STATISTICS
-   urls_read         = 0;
-   urls_rejected     = 0;
-#endif /* def STATISTICS */
 
-#ifndef SPLIT_PROXY_ARGS
-   suppress_blocklists = 0;
-#endif /* ndef SPLIT_PROXY_ARGS */
+      switch( hash_string( cmd ) )
+      {
+/****************************************************************************
+ * actionsfile actions-file-name
+ * In confdir by default
+ ****************************************************************************/
+         case hash_actions_file :
+            freez((char *)config->actions_file);
+            config->actions_file = make_path(config->confdir, arg);
+            continue;
+
+/****************************************************************************
+ * admin-address email-address
+ ****************************************************************************/
+         case hash_admin_address :
+            freez((char *)config->admin_address);
+            config->admin_address = strdup(arg);
+            continue;       
+
+/****************************************************************************
+ * confdir directory-name
+ ****************************************************************************/
+         case hash_confdir :
+            freez((char *)config->confdir);
+            config->confdir = strdup(arg);
+            continue;            
+
+/****************************************************************************
+ * debug n
+ * Specifies debug level, multiple values are ORed together.
+ ****************************************************************************/
+         case hash_debug :
+            config->debug |= atoi(arg);
+            continue;
 
-   freez((char *)from);
-   freez((char *)haddr);
-   freez((char *)uagent);
-   freez((char *)referrer);
-   freez((char *)logfile);
+/****************************************************************************
+ * deny-access source-ip[/significant-bits] [dest-ip[/significant-bits]]
+ ****************************************************************************/
+#ifdef FEATURE_ACL
+         case hash_deny_access:
+            vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
 
+            if ((vec_count != 1) && (vec_count != 2))
+            {
+               log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
+                     "deny-access directive in configuration file.");
+               config->proxy_args = strsav( config->proxy_args,
+                  "<br>\nWARNING: Wrong number of parameters for "
+                  "deny-access directive in configuration file.<br><br>\n");
+               continue;
+            }
 
-   freez((char *)blockfile);
-   freez((char *)permissions_file);
-   freez((char *)forwardfile);
+            /* allocate a new node */
+            cur_acl = (struct access_control_list *) zalloc(sizeof(*cur_acl));
 
-#ifdef ACL_FILES
-   freez((char *)aclfile);
-#endif /* def ACL_FILES */
+            if (cur_acl == NULL)
+            {
+               log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
+               /* Never get here - LOG_LEVEL_FATAL causes program exit */
+               continue;
+            }
+            cur_acl->action = ACL_DENY;
+
+            if (acl_addr(vec[0], cur_acl->src) < 0)
+            {
+               log_error(LOG_LEVEL_ERROR, "Invalid source IP for deny-access "
+                     "directive in configuration file: \"%s\"", vec[0]);
+               config->proxy_args = strsav( config->proxy_args,
+                  "<br>\nWARNING: Invalid source IP for deny-access directive"
+                  " in configuration file: \"");
+               config->proxy_args = strsav( config->proxy_args,
+                  vec[0]);
+               config->proxy_args = strsav( config->proxy_args,
+                  "\"<br><br>\n");
+               freez(cur_acl);
+               continue;
+            }
+            if (vec_count == 2)
+            {
+               if (acl_addr(vec[1], cur_acl->dst) < 0)
+               {
+                  log_error(LOG_LEVEL_ERROR, "Invalid destination IP for deny-access "
+                        "directive in configuration file: \"%s\"", vec[0]);
+                  config->proxy_args = strsav( config->proxy_args,
+                     "<br>\nWARNING: Invalid destination IP for deny-access directive"
+                     " in configuration file: \"");
+                  config->proxy_args = strsav( config->proxy_args,
+                     vec[0]);
+                  config->proxy_args = strsav( config->proxy_args,
+                     "\"<br><br>\n");
+                  freez(cur_acl);
+                  continue;
+               }
+            }
 
-#ifdef USE_IMAGE_LIST
-   freez((char *)imagefile);
-#endif /* def USE_IMAGE_LIST */
+            /*
+             * Add it to the list.  Note we reverse the list to get the
+             * behaviour the user expects.  With both the ACL and
+             * actions file, the last match wins.  However, the internal
+             * implementations are different:  The actions file is stored
+             * in the same order as the file, and scanned completely.
+             * With the ACL, we reverse the order as we load it, then 
+             * when we scan it we stop as soon as we get a match.
+             */
+            cur_acl->next  = config->acl;
+            config->acl = cur_acl;
 
-#ifdef JAR_FILES
-   freez((char *)jarfile);
-#endif /* def JAR_FILES */
+            continue;
+#endif /* def FEATURE_ACL */
 
-#ifndef SPLIT_PROXY_ARGS
-   freez((char *)suppress_message);
-#endif /* ndef SPLIT_PROXY_ARGS */
+/****************************************************************************
+ * forward url-pattern (.|http-proxy-host[:port])
+ ****************************************************************************/
+         case hash_forward:
+            vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
 
-#ifdef TRUST_FILES
-   freez((char *)trustfile);
-#endif /* def TRUST_FILES */
+            if (vec_count != 2)
+            {
+               log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for forward "
+                     "directive in configuration file.");
+               config->proxy_args = strsav( config->proxy_args,
+                  "<br>\nWARNING: Wrong number of parameters for "
+                  "forward directive in configuration file.");
+               continue;
+            }
 
-#ifdef PCRS
-   freez((char *)re_filterfile);
-#endif /* def PCRS */
+            /* allocate a new node */
+            cur_fwd = zalloc(sizeof(*cur_fwd));
+            if (cur_fwd == NULL)
+            {
+               log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
+               /* Never get here - LOG_LEVEL_FATAL causes program exit */
+               continue;
+            }
 
-#ifdef FAST_REDIRECTS
-   fast_redirects = 0;
-#endif /* def FAST_REDIRECTS */
+            cur_fwd->type = SOCKS_NONE;
 
-   if (NULL != configfile)
-   {
-      if ((configfp = fopen(configfile, "r")) == NULL)
-      {
-         log_error(LOG_LEVEL_FATAL, "can't open configuration file '%s':  %E",
-                 configfile);
-         /* Never get here - LOG_LEVEL_FATAL causes program exit */
-      }
-   }
+            /* Save the URL pattern */
+            if (create_url_spec(cur_fwd->url, vec[0]))
+            {
+               log_error(LOG_LEVEL_ERROR, "Bad URL specifier for forward "
+                     "directive in configuration file.");
+               config->proxy_args = strsav( config->proxy_args,
+                  "<br>\nWARNING: Bad URL specifier for "
+                  "forward directive in configuration file.");
+               continue;
+            }
 
-   if (NULL != configfp)
-   {
-      memset (buf, 'j', sizeof(buf));
-      while (read_config_line(buf, sizeof(buf), configfp, NULL) != NULL)
-      {
-         char cmd[BUFSIZ];
-         char arg[BUFSIZ];
-         char tmp[BUFSIZ];
+            /* Parse the parent HTTP proxy host:port */
+            p = vec[1];
 
-         strcpy(tmp, buf);
+            if (strcmp(p, ".") != 0)
+            {
+               cur_fwd->forward_host = strdup(p);
 
-         /* Copy command (i.e. up to space or tab) into cmd */
-         p = buf;
-         q = cmd;
-         while (*p && (*p != ' ') && (*p != '\t'))
-         {
-            *q++ = *p++;
-         }
-         *q = '\0';
+               if ((p = strchr(cur_fwd->forward_host, ':')))
+               {
+                  *p++ = '\0';
+                  cur_fwd->forward_port = atoi(p);
+               }
 
-         /* Skip over the whitespace in buf */
-         while (*p && ((*p == ' ') || (*p == '\t')))
-         {
-            p++;
-         }
+               if (cur_fwd->forward_port <= 0)
+               {
+                  cur_fwd->forward_port = 8000;
+               }
+            }
 
-         /* Copy the argument into arg */
-         strcpy(arg, p);
+            /* Add to list. */
+            cur_fwd->next = config->forward;
+            config->forward = cur_fwd;
 
-         /* Should never happen, but check this anyway */
-         if (*cmd == '\0')
-         {
             continue;
-         }
 
-         /* Make sure the command field is lower case */
-         for (p=cmd; *p; p++)
-         {
-            if (ijb_isupper(*p))
+/****************************************************************************
+ * forward-socks4 url-pattern socks-proxy[:port] (.|http-proxy[:port])
+ ****************************************************************************/
+         case hash_forward_socks4:
+            vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
+
+            if (vec_count != 3)
             {
-               *p = ijb_tolower(*p);
+               log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
+                     "forward-socks4 directive in configuration file.");
+               config->proxy_args = strsav( config->proxy_args,
+                  "<br>\nWARNING: Wrong number of parameters for "
+                  "forward-socks4 directive in configuration file.");
+               continue;
             }
-         }
 
-         /* Save the argument for show-proxy-args */
-         savearg(cmd, arg);
+            /* allocate a new node */
+            cur_fwd = zalloc(sizeof(*cur_fwd));
+            if (cur_fwd == NULL)
+            {
+               log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
+               /* Never get here - LOG_LEVEL_FATAL causes program exit */
+               continue;
+            }
 
+            cur_fwd->type = SOCKS_4;
 
-         switch( hash_string( cmd ) )
-         {
-#ifdef TRUST_FILES
-            case hash_trustfile :
-               freez((char *)trustfile);
-               trustfile = strdup(arg);
+            /* Save the URL pattern */
+            if (create_url_spec(cur_fwd->url, vec[0]))
+            {
+               log_error(LOG_LEVEL_ERROR, "Bad URL specifier for forward-socks4 "
+                     "directive in configuration file.");
+               config->proxy_args = strsav( config->proxy_args,
+                  "<br>\nWARNING: Bad URL specifier for "
+                  "forward-socks4 directive in configuration file.");
                continue;
+            }
 
-            case hash_trust_info_url :
-               enlist(trust_info, arg);
-               continue;
-#endif /* def TRUST_FILES */
+            /* Parse the SOCKS proxy host[:port] */
+            p = vec[1];
 
-            case hash_debug :
-               debug |= atoi(arg);
-               continue;
+            if (strcmp(p, ".") != 0)
+            {
+               cur_fwd->gateway_host = strdup(p);
 
-#if defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST)
-            case hash_tinygif :
-               freez((char *)tinygifurl);
-               tinygif = atoi(arg);
-               if(3 == tinygif)
+               if ((p = strchr(cur_fwd->gateway_host, ':')))
                {
-                  p = arg;
-                  while((*p >= '0') && (*p <= '9'))
-                  {
-                     p++;
-                  }
-                  while((*p == ' ') || (*p == '\t'))
-                  {
-                     p++;
-                  }
-                  if (*p)
-                  {
-                     q = malloc(strlen(p) + 5);
-                     if (q)
-                     {
-                        strcpy(q, p);
-                        strcat(q, "\r\n\r\n");
-                        tinygifurl = q;
-                     }
-                  }
+                  *p++ = '\0';
+                  cur_fwd->gateway_port = atoi(p);
                }
-               if ((tinygif != 1) && 
-                   (tinygif != 2) && 
-                   ((tinygif != 3) || (tinygifurl==NULL)) )
+               if (cur_fwd->gateway_port <= 0)
                {
-                  log_error(LOG_LEVEL_ERROR, "tinygif setting invalid.");
+                  cur_fwd->gateway_port = 1080;
                }
-               continue;
-#endif /* defined(DETECT_MSIE_IMAGES) || defined(USE_IMAGE_LIST) */
+            }
 
-            case hash_add_forwarded_header :
-               add_forwarded = 1;
-               continue;
+            /* Parse the parent HTTP proxy host[:port] */
+            p = vec[2];
 
-            case hash_single_threaded :
-               multi_threaded = 0;
-               continue;
+            if (strcmp(p, ".") != 0)
+            {
+               cur_fwd->forward_host = strdup(p);
 
-            case hash_suppress_vanilla_wafer :
-               suppress_vanilla_wafer = 1;
-               continue;
+               if ((p = strchr(cur_fwd->forward_host, ':')))
+               {
+                  *p++ = '\0';
+                  cur_fwd->forward_port = atoi(p);
+               }
 
-            case hash_wafer :
-               enlist(wafer_list, arg);
-               continue;
+               if (cur_fwd->forward_port <= 0)
+               {
+                  cur_fwd->forward_port = 8000;
+               }
+            }
 
-            case hash_add_header :
-               enlist(xtra_list, arg);
-               continue;
+            /* Add to list. */
+            cur_fwd->next = config->forward;
+            config->forward = cur_fwd;
+            
+            continue;
 
-            case hash_permissions_file :
-               freez((char *)permissions_file);
-               permissions_file = strdup(arg);
-               continue;
+/****************************************************************************
+ * forward-socks4a url-pattern socks-proxy[:port] (.|http-proxy[:port])
+ ****************************************************************************/
+         case hash_forward_socks4a:
+            vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
 
-            case hash_logfile :
-               freez((char *)logfile);
-               logfile = strdup(arg);
+            if (vec_count != 3)
+            {
+               log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
+                     "forward-socks4a directive in configuration file.");
+               config->proxy_args = strsav( config->proxy_args,
+                  "<br>\nWARNING: Wrong number of parameters for "
+                  "forward-socks4a directive in configuration file.");
                continue;
+            }
 
-            case hash_blockfile :
-               freez((char *)blockfile);
-               blockfile = strdup(arg);
+            /* allocate a new node */
+            cur_fwd = zalloc(sizeof(*cur_fwd));
+            if (cur_fwd == NULL)
+            {
+               log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
+               /* Never get here - LOG_LEVEL_FATAL causes program exit */
                continue;
+            }
 
-#ifdef USE_IMAGE_LIST
-            case hash_imagefile :
-               freez((char *)imagefile);
-               imagefile = strdup(arg);
-               continue;
-#endif /* def USE_IMAGE_LIST */
+            cur_fwd->type = SOCKS_4A;
 
-#ifdef JAR_FILES
-            case hash_jarfile :
-               freez((char *)jarfile);
-               jarfile = strdup(arg);
+            /* Save the URL pattern */
+            if (create_url_spec(cur_fwd->url, vec[0]))
+            {
+               log_error(LOG_LEVEL_ERROR, "Bad URL specifier for forward-socks4a "
+                     "directive in configuration file.");
+               config->proxy_args = strsav( config->proxy_args,
+                  "<br>\nWARNING: Bad URL specifier for "
+                  "forward-socks4a directive in configuration file.");
                continue;
-#endif /* def JAR_FILES */
+            }
 
-            case hash_listen_address :
-               freez((char *)haddr);
-               haddr = strdup(arg);
-               continue;
+            /* Parse the SOCKS proxy host[:port] */
+            p = vec[1];
 
-            case hash_forwardfile :
-               freez((char *)forwardfile);
-               forwardfile = strdup(arg);
-               continue;
+            cur_fwd->gateway_host = strdup(p);
 
-#ifdef ACL_FILES
-            case hash_aclfile :
-               freez((char *)aclfile);
-               aclfile = strdup(arg);
-               continue;
-#endif /* def ACL_FILES */
+            if ((p = strchr(cur_fwd->gateway_host, ':')))
+            {
+               *p++ = '\0';
+               cur_fwd->gateway_port = atoi(p);
+            }
+            if (cur_fwd->gateway_port <= 0)
+            {
+               cur_fwd->gateway_port = 1080;
+            }
 
-#ifdef PCRS
-            case hash_re_filterfile :
-               freez((char *)re_filterfile);
-               re_filterfile = strdup(arg);
-               continue;
-#endif /* def PCRS */
+            /* Parse the parent HTTP proxy host[:port] */
+            p = vec[2];
 
-            case hash_user_agent :
-               freez((char *)uagent);
-               uagent = strdup(arg);
-               continue;
+            if (strcmp(p, ".") != 0)
+            {
+               cur_fwd->forward_host = strdup(p);
 
-               /*
-                * Offer choice of correct spelling according to dictionary,
-                * or the misspelling used in the HTTP spec.
-                */
-            case hash_referrer :
-            case hash_referer :
-               freez((char *)referrer);
-               referrer = strdup(arg);
-               continue;
+               if ((p = strchr(cur_fwd->forward_host, ':')))
+               {
+                  *p++ = '\0';
+                  cur_fwd->forward_port = atoi(p);
+               }
 
-            case hash_from :
-               freez((char *)from);
-               from = strdup(arg);
-               continue;
+               if (cur_fwd->forward_port <= 0)
+               {
+                  cur_fwd->forward_port = 8000;
+               }
+            }
+
+            /* Add to list. */
+            cur_fwd->next = config->forward;
+            config->forward = cur_fwd;
+            
+            continue;
+
+/****************************************************************************
+ * jarfile jar-file-name
+ * In logdir by default
+ ****************************************************************************/
+#ifdef FEATURE_COOKIE_JAR
+         case hash_jarfile :
+            freez((char *)config->jarfile);
+            config->jarfile = make_path(config->logdir, arg);
+            continue;
+#endif /* def FEATURE_COOKIE_JAR */
+
+/****************************************************************************
+ * listen-address [ip][:port]
+ ****************************************************************************/
+         case hash_listen_address :
+            freez((char *)config->haddr);
+            config->haddr = strdup(arg);
+            continue;
+
+/****************************************************************************
+ * logdir directory-name
+ ****************************************************************************/
+         case hash_logdir :
+            freez((char *)config->logdir);
+            config->logdir = strdup(arg);
+            continue;            
+
+/****************************************************************************
+ * logfile log-file-name
+ * In logdir by default
+ ****************************************************************************/
+         case hash_logfile :
+            freez((char *)config->logfile);
+            config->logfile = make_path(config->logdir, arg);
+            continue;
+
+/****************************************************************************
+ * permit-access source-ip[/significant-bits] [dest-ip[/significant-bits]]
+ ****************************************************************************/
+#ifdef FEATURE_ACL
+         case hash_permit_access:
+            vec_count = ssplit(arg, " \t", vec, SZ(vec), 1, 1);
+
+            if ((vec_count != 1) && (vec_count != 2))
+            {
+               log_error(LOG_LEVEL_ERROR, "Wrong number of parameters for "
+                     "permit-access directive in configuration file.");
+               config->proxy_args = strsav( config->proxy_args,
+                  "<br>\nWARNING: Wrong number of parameters for "
+                  "permit-access directive in configuration file.<br><br>\n");
 
-#ifdef FAST_REDIRECTS
-            case hash_fast_redirects :
-               fast_redirects = 1;
                continue;
-#endif /* def FAST_REDIRECTS */
+            }
 
-#ifdef _WIN_CONSOLE
-            case hash_hide_console :
-               hideConsole = 1;
+            /* allocate a new node */
+            cur_acl = (struct access_control_list *) zalloc(sizeof(*cur_acl));
+
+            if (cur_acl == NULL)
+            {
+               log_error(LOG_LEVEL_FATAL, "can't allocate memory for configuration");
+               /* Never get here - LOG_LEVEL_FATAL causes program exit */
                continue;
-#endif /*def _WIN_CONSOLE*/
+            }
+            cur_acl->action = ACL_PERMIT;
 
-#ifndef SPLIT_PROXY_ARGS
-            case hash_suppress_blocklists :
-               if (arg[0] != '\0')
-               {
-                  suppress_message = strdup(arg);
-               }
-               else
+            if (acl_addr(vec[0], cur_acl->src) < 0)
+            {
+               log_error(LOG_LEVEL_ERROR, "Invalid source IP for permit-access "
+                     "directive in configuration file: \"%s\"", vec[0]);
+               config->proxy_args = strsav( config->proxy_args,
+                  "<br>\nWARNING: Invalid source IP for permit-access directive"
+                  " in configuration file: \"");
+               config->proxy_args = strsav( config->proxy_args,
+                  vec[0]);
+               config->proxy_args = strsav( config->proxy_args,
+                  "\"<br><br>\n");
+               freez(cur_acl);
+               continue;
+            }
+            if (vec_count == 2)
+            {
+               if (acl_addr(vec[1], cur_acl->dst) < 0)
                {
-                  /* There will be NO reference in proxy-args. */
-                  suppress_message = NULL;
+                  log_error(LOG_LEVEL_ERROR, "Invalid destination IP for "
+                        "permit-access directive in configuration file: \"%s\"",
+                        vec[0]);
+                  config->proxy_args = strsav( config->proxy_args,
+                     "<br>\nWARNING: Invalid destination IP for permit-access directive"
+                     " in configuration file: \"");
+                  config->proxy_args = strsav( config->proxy_args,
+                     vec[0]);
+                  config->proxy_args = strsav( config->proxy_args,
+                     "\"<br><br>\n");
+                  freez(cur_acl);
+                  continue;
                }
+            }
 
-               suppress_blocklists = 1;
-               continue;
-#endif /* ndef SPLIT_PROXY_ARGS */
+            /*
+             * Add it to the list.  Note we reverse the list to get the
+             * behaviour the user expects.  With both the ACL and
+             * actions file, the last match wins.  However, the internal
+             * implementations are different:  The actions file is stored
+             * in the same order as the file, and scanned completely.
+             * With the ACL, we reverse the order as we load it, then 
+             * when we scan it we stop as soon as we get a match.
+             */
+            cur_acl->next  = config->acl;
+            config->acl = cur_acl;
 
-#ifdef TOGGLE
-            case hash_toggle :
-               g_bToggleIJB = atoi(arg);
-               continue;
-#endif /* def TOGGLE */
+            continue;
+#endif /* def FEATURE_ACL */
+
+/****************************************************************************
+ * proxy-info-url url
+ ****************************************************************************/
+         case hash_proxy_info_url :
+            freez((char *)config->proxy_info_url);
+            config->proxy_info_url = strdup(arg);
+            continue;
+
+/****************************************************************************
+ * re_filterfile file-name
+ * In confdir by default.
+ ****************************************************************************/
+         case hash_re_filterfile :
+            freez((char *)config->re_filterfile);
+            config->re_filterfile = make_path(config->confdir, arg);
+            continue;
+
+/****************************************************************************
+ * single-threaded
+ ****************************************************************************/
+         case hash_single_threaded :
+            config->multi_threaded = 0;
+            continue;
+
+/****************************************************************************
+ * toggle (0|1)
+ ****************************************************************************/
+#ifdef FEATURE_TOGGLE
+         case hash_toggle :
+            g_bToggleIJB = atoi(arg);
+            continue;
+#endif /* def FEATURE_TOGGLE */
+
+/****************************************************************************
+ * trust-info-url url
+ ****************************************************************************/
+#ifdef FEATURE_TRUST
+         case hash_trust_info_url :
+            enlist(config->trust_info, arg);
+            continue;
+#endif /* def FEATURE_TRUST */
+
+/****************************************************************************
+ * trustfile filename
+ * (In confdir by default.)
+ ****************************************************************************/
+#ifdef FEATURE_TRUST
+         case hash_trustfile :
+            freez((char *)config->trustfile);
+            config->trustfile = make_path(config->confdir, arg);
+            continue;
+#endif /* def FEATURE_TRUST */
+
+
+/****************************************************************************
+ * Win32 Console options:
+ ****************************************************************************/
+
+/****************************************************************************
+ * hide-console
+ ****************************************************************************/
+#ifdef _WIN_CONSOLE
+         case hash_hide_console :
+            hideConsole = 1;
+            continue;
+#endif /*def _WIN_CONSOLE*/
+
+
+/****************************************************************************
+ * Win32 GUI options:
+ ****************************************************************************/
 
 #if defined(_WIN32) && ! defined(_WIN_CONSOLE)
-            case hash_activity_animation :
-               g_bShowActivityAnimation = atoi(arg);
-               continue;
+/****************************************************************************
+ * activity-animation (0|1)
+ ****************************************************************************/
+         case hash_activity_animation :
+            g_bShowActivityAnimation = atoi(arg);
+            continue;
 
-            case hash_log_messages :
-               g_bLogMessages = atoi(arg);
-               continue;
+/****************************************************************************
+ *  close-button-minimizes (0|1)
+ ****************************************************************************/
+         case hash_close_button_minimizes :
+            g_bCloseHidesWindow = atoi(arg);
+            continue;
 
-            case hash_log_highlight_messages :
-               g_bHighlightMessages = atoi(arg);
-               continue;
+/****************************************************************************
+ * log-buffer-size (0|1)
+ ****************************************************************************/
+         case hash_log_buffer_size :
+            g_bLimitBufferSize = atoi(arg);
+            continue;
 
-            case hash_log_buffer_size :
-               g_bLimitBufferSize = atoi(arg);
-               continue;
+/****************************************************************************
+ * log-font-name fontnane
+ ****************************************************************************/
+         case hash_log_font_name :
+            strcpy( g_szFontFaceName, arg );
+            continue;
 
-            case hash_log_max_lines :
-               g_nMaxBufferLines = atoi(arg);
-               continue;
+/****************************************************************************
+ * log-font-size n
+ ****************************************************************************/
+         case hash_log_font_size :
+            g_nFontSize = atoi(arg);
+            continue;
 
-            case hash_log_font_name :
-               strcpy( g_szFontFaceName, arg );
-               continue;
+/****************************************************************************
+ * log-highlight-messages (0|1)
+ ****************************************************************************/
+         case hash_log_highlight_messages :
+            g_bHighlightMessages = atoi(arg);
+            continue;
 
-            case hash_log_font_size :
-               g_nFontSize = atoi(arg);
-               continue;
+/****************************************************************************
+ * log-max-lines n
+ ****************************************************************************/
+         case hash_log_max_lines :
+            g_nMaxBufferLines = atoi(arg);
+            continue;
 
-            case hash_show_on_task_bar :
-               g_bShowOnTaskBar = atoi(arg);
-               continue;
+/****************************************************************************
+ * log-messages (0|1)
+ ****************************************************************************/
+         case hash_log_messages :
+            g_bLogMessages = atoi(arg);
+            continue;
+
+/****************************************************************************
+ * show-on-task-bar (0|1)
+ ****************************************************************************/
+         case hash_show_on_task_bar :
+            g_bShowOnTaskBar = atoi(arg);
+            continue;
 
-            case hash_close_button_minimizes :
-               g_bCloseHidesWindow = atoi(arg);
-               continue;
 #endif /* defined(_WIN32) && ! defined(_WIN_CONSOLE) */
 
-            /* Warnings about unsupported features */
-
-#ifndef TRUST_FILES
-            case hash_trustfile :
-            case hash_trust_info_url :
-#endif /* ndef TRUST_FILES */
-#ifndef USE_IMAGE_LIST
-            case hash_imagefile :
-#endif /* ndef USE_IMAGE_LIST */
-#ifndef PCRS
-            case hash_re_filterfile :
-#endif /* ndef PCRS */
-#ifndef TOGGLE
-            case hash_toggle :
-#endif /* ndef TOGGLE */
-#if defined(_WIN_CONSOLE) || ! defined(_WIN32)
-            case hash_activity_animation :
-            case hash_log_messages :
-            case hash_log_highlight_messages :
-            case hash_log_buffer_size :
-            case hash_log_max_lines :
-            case hash_log_font_name :
-            case hash_log_font_size :
-            case hash_show_on_task_bar :
-            case hash_close_button_minimizes :
-#endif /* defined(_WIN_CONSOLE) || ! defined(_WIN32) */
+
+/****************************************************************************/
+/* Warnings about unsupported features                                      */
+/****************************************************************************/
+#ifndef FEATURE_ACL
+         case hash_deny_access:
+#endif /* ndef FEATURE_ACL */
+#ifndef FEATURE_COOKIE_JAR
+         case hash_jarfile :
+#endif /* ndef FEATURE_COOKIE_JAR */
+#ifndef FEATURE_ACL
+         case hash_permit_access:
+#endif /* ndef FEATURE_ACL */
+#ifndef FEATURE_TOGGLE
+         case hash_toggle :
+#endif /* ndef FEATURE_TOGGLE */
+#ifndef FEATURE_TRUST
+         case hash_trustfile :
+         case hash_trust_info_url :
+#endif /* ndef FEATURE_TRUST */
+
 #ifndef _WIN_CONSOLE
-            case hash_hide_console :
+         case hash_hide_console :
 #endif /* ndef _WIN_CONSOLE */
-#if !defined(DETECT_MSIE_IMAGES) && !defined(USE_IMAGE_LIST)
-            case hash_tinygif :
-#endif /* !defined(DETECT_MSIE_IMAGES) && !defined(USE_IMAGE_LIST) */
-#ifndef JAR_FILES
-            case hash_jarfile :
-#endif /* ndef JAR_FILES */
-#ifndef ACL_FILES
-            case hash_aclfile :
-#endif /* ndef ACL_FILES */
-
-#ifdef SPLIT_PROXY_ARGS
-            case hash_suppress_blocklists :
-#endif /* def SPLIT_PROXY_ARGS */
-               log_error(LOG_LEVEL_INFO, "Unsupported directive \"%s\" ignored.", cmd);
-               continue;
-
-            default :
-               /*
-                * I decided that I liked this better as a warning than an
-                * error.  To change back to an error, just change log level
-                * to LOG_LEVEL_FATAL.
-                */
-               log_error(LOG_LEVEL_ERROR, "Unrecognized directive (%lulu) in "
-                     "configuration file: \"%s\"", hash_string( cmd ), buf);
-               p = malloc( BUFSIZ );
-               if (p != NULL)
-               {
-                  sprintf( p, "<br>\nWARNING: unrecognized directive : %s<br><br>\n", buf );
-                  proxy_args->invocation = strsav( proxy_args->invocation, p );
-                  freez( p );
-               }
-               continue;
-         }
-      }
-      fclose(configfp);
-   }
 
-   init_error_log(Argv[0], logfile, debug);
+#if defined(_WIN_CONSOLE) || ! defined(_WIN32)
+         case hash_activity_animation :
+         case hash_close_button_minimizes :
+         case hash_log_buffer_size :
+         case hash_log_font_name :
+         case hash_log_font_size :
+         case hash_log_highlight_messages :
+         case hash_log_max_lines :
+         case hash_log_messages :
+         case hash_show_on_task_bar :
+#endif /* defined(_WIN_CONSOLE) || ! defined(_WIN32) */
+            /* These warnings are annoying - so hide them. -- Jon */
+            /* log_error(LOG_LEVEL_INFO, "Unsupported directive \"%s\" ignored.", cmd); */
+            continue;
 
-   if (permissions_file)
-   {
-      add_loader(load_permissions_file);
-   }
+/****************************************************************************/
+         default :
+/****************************************************************************/
+            /*
+             * I decided that I liked this better as a warning than an
+             * error.  To change back to an error, just change log level
+             * to LOG_LEVEL_FATAL.
+             */
+            log_error(LOG_LEVEL_ERROR, "Unrecognized directive (%luul) in "
+                  "configuration file: \"%s\"", hash_string( cmd ), buf);
+            config->proxy_args = strsav( config->proxy_args, "<br>\nWARNING: unrecognized directive : ");
+            config->proxy_args = strsav( config->proxy_args, buf);
+            config->proxy_args = strsav( config->proxy_args, "<br><br>\n");
+            continue;
 
-   if (blockfile)
-   {
-      add_loader(load_blockfile);
-   }
+/****************************************************************************/
+      } /* end switch( hash_string(cmd) ) */
+   } /* end while ( read_config_line(...) ) */
 
-#ifdef USE_IMAGE_LIST
-   if (imagefile)
-   {
-      add_loader(load_imagefile);
-   }
-#endif /* def USE_IMAGE_LIST */
+   fclose(configfp);
 
-#ifdef TRUST_FILES
-   if (trustfile)
-   {
-      add_loader(load_trustfile);
-   }
-#endif /* def TRUST_FILES */
+   init_error_log(Argv[0], config->logfile, config->debug);
 
-   if (forwardfile)
+   if (config->actions_file)
    {
-      add_loader(load_forwardfile);
+      add_loader(load_actions_file, config);
    }
 
-#ifdef ACL_FILES
-   if (aclfile)
+   if (config->re_filterfile)
    {
-      add_loader(load_aclfile);
+      add_loader(load_re_filterfile, config);
    }
-#endif /* def ACL_FILES */
 
-#ifdef PCRS
-   if (re_filterfile)
+#ifdef FEATURE_TRUST
+   if (config->trustfile)
    {
-      add_loader(load_re_filterfile);
+      add_loader(load_trustfile, config);
    }
-#endif /* def PCRS */
+#endif /* def FEATURE_TRUST */
 
-#ifdef JAR_FILES
-   if ( NULL != jarfile )
+#ifdef FEATURE_COOKIE_JAR
+   if ( NULL != config->jarfile )
    {
-      if ( NULL == (jar = fopen(jarfile, "a")) )
+      if ( NULL == (config->jar = fopen(config->jarfile, "a")) )
       {
-         log_error(LOG_LEVEL_FATAL, "can't open jarfile '%s': %E", jarfile);
+         log_error(LOG_LEVEL_FATAL, "can't open jarfile '%s': %E", config->jarfile);
          /* Never get here - LOG_LEVEL_FATAL causes program exit */
       }
-      setbuf(jar, NULL);
+      setbuf(config->jar, NULL);
    }
-#endif /* def JAR_FILES */
+#endif /* def FEATURE_COOKIE_JAR */
 
-   if ( NULL == haddr )
+   if ( NULL == config->haddr )
    {
-      haddr = strdup( HADDR_DEFAULT );
+      config->haddr = strdup( HADDR_DEFAULT );
    }
 
-   if ( NULL != haddr )
+   if ( NULL != config->haddr )
    {
-      if ((p = strchr(haddr, ':')))
+      if ((p = strchr(config->haddr, ':')))
       {
          *p++ = '\0';
          if (*p)
          {
-            hport = atoi(p);
+            config->hport = atoi(p);
          }
       }
 
-      if (hport <= 0)
+      if (config->hport <= 0)
       {
          *--p = ':';
-         log_error(LOG_LEVEL_FATAL, "invalid bind port spec %s", haddr);
+         log_error(LOG_LEVEL_FATAL, "invalid bind port spec %s", config->haddr);
          /* Never get here - LOG_LEVEL_FATAL causes program exit */
       }
-      if (*haddr == '\0')
+      if (*config->haddr == '\0')
       {
-         haddr = NULL;
+         config->haddr = NULL;
       }
    }
 
-   if (run_loader(NULL))
+   /*
+    * Want to run all the loaders once now.
+    *
+    * Need to set up a fake csp, so they can get to the config.
+    */
+   fake_csp = (struct client_state *) zalloc (sizeof(*fake_csp));
+   fake_csp->config = config;
+
+   if (run_loader(fake_csp))
    {
+      freez(fake_csp);
       log_error(LOG_LEVEL_FATAL, "A loader failed while loading config file. Exiting.");
       /* Never get here - LOG_LEVEL_FATAL causes program exit */
    }
+   freez(fake_csp);
 
-#ifdef JAR_FILES
-   /*
-    * If we're logging cookies in a cookie jar, and the user has not
-    * supplied any wafers, and the user has not told us to suppress the
-    * vanilla wafer, then send the vanilla wafer.
-    */
-   if ((jarfile != NULL)
-       && (wafer_list->next == NULL)
-       && (suppress_vanilla_wafer == 0))
+/* FIXME: this is a kludge for win32 */
+#if defined(_WIN32) && !defined (_WIN_CONSOLE)
+
+   g_actions_file     = config->actions_file;
+   g_re_filterfile    = config->re_filterfile;
+
+#ifdef FEATURE_TRUST
+   g_trustfile        = config->trustfile;
+#endif /* def FEATURE_TRUST */
+   
+
+#endif /* defined(_WIN32) && !defined (_WIN_CONSOLE) */
+/* FIXME: end kludge */
+
+
+   config->need_bind = 1;
+
+   if (current_configfile)
    {
-      enlist(wafer_list, VANILLA_WAFER);
+      struct configuration_spec * oldcfg = (struct configuration_spec *)
+                                           current_configfile->f;
+      /*
+       * Check if config->haddr,hport == oldcfg->haddr,hport
+       *
+       * The following could be written more compactly as a single,
+       * (unreadably long) if statement.
+       */
+      config->need_bind = 0;
+      if (config->hport != oldcfg->hport)
+      {
+         config->need_bind = 1;
+      }
+      else if (config->haddr == NULL)
+      {
+         if (oldcfg->haddr != NULL)
+         {
+            config->need_bind = 1;
+         }
+      }
+      else if (oldcfg->haddr == NULL)
+      {
+         config->need_bind = 1;
+      }
+      else if (0 != strcmp(config->haddr, oldcfg->haddr))
+      {
+         config->need_bind = 1;
+      }
+
+      current_configfile->unloader = unload_configfile;
    }
-#endif /* def JAR_FILES */
 
-   end_proxy_args();
+   fs->next = files->next;
+   files->next = fs;
+
+   current_configfile = fs;
 
+   return (config);
 }