Logo Search packages:      
Sourcecode: rageircd version File versions  Download package

s_conf2.c

/*
 * RageIRCd: an advanced Internet Relay Chat daemon (ircd).
 * (C) 2000-2005 the RageIRCd Development Team, all rights reserved.
 *
 * This software is free, licensed under the General Public License.
 * Please refer to doc/LICENSE and doc/README for further details.
 *
 * $Id: s_conf2.c,v 1.287.2.15 2005/07/09 00:39:20 amcwilliam Exp $
 */

#include "struct.h"
#include "common.h"
#include "setup.h"
#include "h.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include "patchlevel.h"
#include "ssl.h"
#include "conf2.h"
#include "config.h"
#include "memory.h"
#include "res.h"
#include "hook.h"
#include "dlink.h"
#include "fd.h"
#include "xmode.h"
#include "modules.h"
#include "user_ban.h"
#include <time.h>
#include <signal.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <utmp.h>
#include <fcntl.h>

#undef CDEBUG

ConfigItem_network NetworkConfig, tmpNetworkConfig;
ConfigItem_masking MaskingConfig, tmpMaskingConfig;
ConfigItem_flood FloodConfig, tmpFloodConfig;
ConfigItem_general GeneralConfig, tmpGeneralConfig;

ConfigItem_servinfo *ServerInfo = NULL;
ConfigItem_admin *AdminInfo = NULL;
ConfigItem_class *DefaultClass = NULL;

dlink_list conf_class_list = DLINK_LIST_INIT;
dlink_list conf_allow_list = DLINK_LIST_INIT;
dlink_list conf_oper_list = DLINK_LIST_INIT;
dlink_list conf_super_list = DLINK_LIST_INIT;
dlink_list conf_link_list = DLINK_LIST_INIT;
dlink_list conf_listen_list = DLINK_LIST_INIT;
dlink_list conf_include_list = DLINK_LIST_INIT;
#ifndef STATIC_MODULES
dlink_list conf_modulepath_list = DLINK_LIST_INIT;
dlink_list conf_modulefile_list = DLINK_LIST_INIT;
#endif

ConfigDirective conf_directives[] = {
      { "servinfo",     parse_servinfo,   test_servinfo,    free_servinfo },
      { "admin",  parse_admin,      NULL,       NULL },
      { "class",  parse_class,      test_class, free_class },
      { "allow",  parse_allow,      test_allow, free_allow },
      { "oper",   parse_oper, test_oper,  free_oper },
      { "super",  parse_super,      NULL,       NULL },
      { "link",   parse_link, test_link,  free_link },
      { "listen", parse_listen,     NULL,       NULL },
      { "restrict",     parse_restrict,   NULL,       NULL },
      { "kill",   parse_kill, NULL,       NULL },
      { "include",      parse_include,    NULL,       NULL },
#ifndef STATIC_MODULES
      { "modules",      parse_modules,    NULL,       NULL },
#endif
      { "network",      parse_network,    test_network,     free_network },
      { "masking",      parse_masking,    test_masking,     free_masking },
      { "flood",  parse_flood,      test_flood, free_flood },
      { "general",      parse_general,    test_general,     free_general },
      { NULL,           NULL,       NULL,       NULL },
};

ConfigOption servinfo_logs[] = {
      { "default",            LOG_DEFAULT },
      { "error",        LOG_ERROR },
      { "kill",         LOG_KILL },
      { "client",       LOG_CLIENT },
      { "server",       LOG_SERVER },
      { "oper",         LOG_OPER },
      { "override",           LOG_OVERRIDE },
      { NULL, 0 }
};

ConfigOption oper_can_perform[] = {
      { "die",          OFLAG_DIE },
      { "restart",            OFLAG_RESTART },
      { "rehash",       OFLAG_REHASH },
      { "kline",        OFLAG_KLINE },
      { "unkline",            OFLAG_UNKLINE },
      { "local_routing",      OFLAG_LROUTE },
      { "global_routing",     OFLAG_GROUTE },
      { "local_kills",  OFLAG_LKILL },
      { "global_kills", OFLAG_GKILL },
      { "local_notices",      OFLAG_LNOTICE },
      { "global_notices",     OFLAG_GNOTICE },
      { NULL, 0 }
};

ConfigOption oper_can_see[] = {
      { "routing",            OFLAG_RSTAFF },
      { "floods",       OFLAG_FNOTICE },
      { "local_cliconn",      OFLAG_LCLICONN },
      { "global_cliconn",     OFLAG_GCLICONN },
      { NULL, 0 }
};

static void conf_add_from(ConfigFrom *from, char *str)
{
      char *p, *userhost = NULL;
      
      if (from->host_count >= MAXCONFHOSTS) {
            return;
      }
      
      ASSERT(!BadPtr(str));
      p = strrchr(str, '@');
      
      if (p == NULL) {
            char tmp[HOSTLEN + 3];
                  
            ircsnprintf(tmp, HOSTLEN + 2, "*@%s", str);
            DupString(userhost, tmp);
      }
      else {
            DupString(userhost, str);
      }
      
      from->hosts[from->host_count++] = userhost;
}

int conf_check_from(ConfigFrom *from, char *str)
{
      int i;

      for (i = 0; i < from->host_count; i++) {
            ASSERT(!BadPtr(from->hosts[i]));
            if (!match(from->hosts[i], str)) {
                  return 1;
            }
      }

      return 0;
}

static inline void conf_clear_from(ConfigFrom *from)
{
      while (from->host_count) {
            cMyFree(from->hosts[from->host_count - 1]);
            from->host_count--;
      }
}

static int conf_set_class(ConfigEntry *e, ConfigItem_class **class)
{
      ConfigItem_class *tmp = NULL, *found = NULL;
      int changed = 0;
      
      ASSERT(e != NULL);
      ASSERT(!BadPtr(e->vardata));
      
      if ((found = find_class(e->vardata)) == NULL) {
            found = DefaultClass;
      }
      
      /* We now have a class to use, but can we use it yet? */
      
      if ((tmp = *class) != NULL) {
            /* Take this one out first! */
            
            tmp->item.refcount--;
            changed = 1; /* class changed */
      }
      
      tmp = found;
      tmp->item.refcount++;
      
      *class = tmp;
      ASSERT(*class != NULL);
      
      return changed;
}

static void add_unknown(ConfigEntry *entry, void *item, short type)
{
      ConfigUnknown *unknown = (ConfigUnknown *)MyMalloc(sizeof(ConfigUnknown));

      unknown->item = item;
      unknown->entry = entry;
      unknown->type = type;

      dlink_add_tail(&conf_unknown_list, unknown);
}

static unsigned int parse_token_list(char *token_list, ConfigOption *option_table)
{
      char *s, *p;
      unsigned int flags = 0;
      ConfigOption *opt;

      for (s = strtoken(&p, token_list, ","); s != NULL; s = strtoken(&p, NULL, ",")) {
            while (IsSpace(*s)) {
                  s++;
            }
            if (*s == '\0') {
                  continue;
            }
            for (opt = option_table; opt->token != NULL; opt++) {
                  if (!mycmp(opt->token, s)) {
                        flags |= opt->flag;
                        break;
                  }
            }
      }

      return flags;
}

void *parse_servinfo(ConfigEntry *entry)
{
      ConfigEntry *e;

      BLOCK_ENTRIES(entry, "servinfo")

      if (ServerInfo == NULL) {
            ServerInfo = (ConfigItem_servinfo *)MyMalloc(sizeof(ConfigItem_servinfo));
      }

      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "servinfo")
            PARAMETER(e, "servinfo")

            if (!mycmp(e->varname, "name")) {
                  cDupString(ServerInfo->name, e->vardata);
            }
            else if (!mycmp(e->varname, "description")) {
                  cDupString(ServerInfo->desc, e->vardata);
            }
            else if (!mycmp(e->varname, "default_bind_ip")) {
                  cDupString(ServerInfo->default_bind_ip, e->vardata);
            }
            else if (!mycmp(e->varname, "identity")) {
                  ServerInfo->identity = abs(atoi(e->vardata));
            }
            else if (!mycmp(e->varname, "hub")) {
                  ServerInfo->hub = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "max_clients")) {
                  ServerInfo->max_clients = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "kline_address")) {
                  cDupString(ServerInfo->kline_address, e->vardata);
            }
            else if (!mycmp(e->varname, "logs")) {
                  ServerInfo->logs |= parse_token_list(e->vardata, servinfo_logs);
            }
#ifdef USE_OPENSSL
            else if (!mycmp(e->varname, "ssl_certificate")) {
                  cDupString(ServerInfo->ssl_certificate, e->vardata);
            }
            else if (!mycmp(e->varname, "ssl_private_key")) {
                  cDupString(ServerInfo->ssl_private_key, e->vardata);
            }
#endif
            else {
                  add_unknown(e, ServerInfo, CONF_SERVINFO);
            }
      }

      return (void *)ServerInfo;
}

int test_servinfo(ConfigFile *file, void *item)
{
      ConfigItem_servinfo *servinfo = (ConfigItem_servinfo *)item;
      int retval = CONF_SUCCESS;

      if (BadPtr(servinfo->name)) {
            report(1, "%s: ERROR: servinfo::name: missing parameter", file->filename);
            retval = CONF_FAILURE;
      }
      else if (!valid_server_name(servinfo->name)) {
            report(1, "%s: ERROR: servinfo::name: invalid server name", file->filename);
            retval = CONF_FAILURE;
      }
      if (BadPtr(servinfo->desc)) {
            cDupString(servinfo->desc, "IRCers United!");
      }
      if (!BadPtr(servinfo->default_bind_ip) && !valid_ip_addr(servinfo->default_bind_ip, 0)) {
            cMyFree(servinfo->default_bind_ip);
            report(0, "%s: servinfo::default_bind_ip: invalid ip address, using INADDR_ANY",
                  file->filename);
      }
      if (servinfo->identity < 1 || servinfo->identity > 4096) {
            servinfo->identity = 0xFFF;
      }
      if (servinfo->max_clients < 10) {
            servinfo->max_clients = DEFAULT_MAX_CLIENTS;
      }
      if (BadPtr(servinfo->kline_address)) {
            cDupString(servinfo->kline_address, DEFAULT_KLINE_ADDRESS);
      }
      else if (!valid_email_addr(servinfo->kline_address, 1)) {
            cDupString(servinfo->kline_address, DEFAULT_KLINE_ADDRESS);
      }
#ifdef USE_OPENSSL
      if (BadPtr(servinfo->ssl_certificate)) {
            report(1, "%s: ERROR: servinfo::ssl_certificate: missing parameter", file->filename);
            retval = CONF_FAILURE;
      }
      if (BadPtr(servinfo->ssl_private_key)) {
            report(1, "%s: ERROR: servinfo::ssl_private_key: missing parameter", file->filename);
            retval = CONF_FAILURE;
      }
#endif

      return retval;
}

void free_servinfo(void *item)
{
      ConfigItem_servinfo *servinfo = (ConfigItem_servinfo *)item;

      cMyFree(servinfo->name);
      cMyFree(servinfo->desc);
      cMyFree(servinfo->default_bind_ip);
      cMyFree(servinfo->kline_address);
#ifdef USE_OPENSSL
      cMyFree(servinfo->ssl_certificate);
      cMyFree(servinfo->ssl_private_key);
#endif
      cMyFree(servinfo);
}

void *parse_admin(ConfigEntry *entry)
{
      ConfigEntry *e;

      BLOCK_ENTRIES(entry, "admin")

      if (AdminInfo == NULL) {
            AdminInfo = (ConfigItem_admin *)MyMalloc(sizeof(ConfigItem_admin));
      }

      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "admin")
            PARAMETER(e, "admin")

            if (!mycmp(e->varname, "name")) {
                  cDupString(AdminInfo->name, e->vardata);
            }
            else if (!mycmp(e->varname, "description")) {
                  cDupString(AdminInfo->desc, e->vardata);
            }
            else if (!mycmp(e->varname, "email")) {
                  cDupString(AdminInfo->email, e->vardata);
            }
            else {
                  add_unknown(e, AdminInfo, CONF_ADMIN);
            }
      }

      return (void *)AdminInfo;
}

void *parse_class(ConfigEntry *entry)
{
      ConfigEntry *e;
      ConfigItem_class *class;

      BLOCK_NAME(entry, "class", "class name")
      BLOCK_ENTRIES(entry, "class")

      if ((class = find_class(entry->vardata)) == NULL) {
            class = (ConfigItem_class *)MyMalloc(sizeof(ConfigItem_class));
            cDupString(class->name, entry->vardata);
            dlink_add(&conf_class_list, class);
      }
      else {
            /* Clear any possibilities */
            class->item.temp = 0;
      }

      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "class")
            PARAMETER(e, "class")

            if (!mycmp(e->varname, "ping_time")) {
                  class->ping_time = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "max_clients")) {
                  class->max_clients = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "sendq_length")) {
                  class->sendq_length = get_size(e->vardata);
            }
            else {
                  add_unknown(e, class, CONF_CLASS);
            }
      }

      return (void *)class;
}

int test_class(ConfigFile *file, void *item)
{
      ConfigItem_class *class = (ConfigItem_class *)item;

      if (BadPtr(class->name)) {
            report(0, "%s: class: missing name, ignoring block", file->filename);
            dlink_del(&conf_class_list, class, NULL);
            return CONF_FAILURE;
      }
      if (class->ping_time < 30) {
            class->ping_time = DEFAULT_PINGTIME;
      }
      if (class->max_clients < 1) {
            class->max_clients = DEFAULT_MAX_CLIENTS;
      }
      if (class->sendq_length < 1) {
            class->sendq_length = MAXSENDQLENGTH;
      }

      return CONF_SUCCESS;
}

void free_class(void *item)
{
      ConfigItem_class *class = (ConfigItem_class *)item;
      cMyFree(class->name);
      cMyFree(class);
}

void *parse_allow(ConfigEntry *entry)
{
      ConfigEntry *e;
      ConfigItem_allow *allow;
      dlink_node *node;
      char *hostname = NULL, *ipaddr = NULL;

      BLOCK_ENTRIES(entry, "allow")
      
      /* To avoid dupliate blocks, first scan through the entries
       * and pick out the hostname and ip address first.
       */
      
      for (EACH_ENTRY(e, entry)) {
            if (!mycmp(e->varname, "hostname")) {
                  cDupString(hostname, e->vardata);
            }
            else if (!mycmp(e->varname, "ip")) {
                  cDupString(ipaddr, e->vardata);
            }
      }
      
      /* Now scan through and try find it! We want the exact strings,
       * so use irccmp(). Perhaps a little anal, but hey.
       */
      DLINK_FOREACH_PREV_DATA(conf_allow_list.tail, node, allow, ConfigItem_allow) {
            if (!BadPtr(hostname) && !BadPtr(allow->hostname) && !irccmp(hostname, allow->hostname)) {
                  break;
            }
            if (!BadPtr(ipaddr) && !BadPtr(allow->ipaddr) && !irccmp(ipaddr, allow->ipaddr)) {
                  break;
            }
            allow = NULL; /* Reset */
      }
      
      /* If allow is NULL, it's a new block. */
      if (allow == NULL) {
            allow = (ConfigItem_allow *)MyMalloc(sizeof(ConfigItem_allow));
            allow->hostname = hostname;
            allow->ipaddr = ipaddr;
            dlink_add(&conf_allow_list, allow);
      }
      else {
            /* Take it out the list to maintain order of allow{} config */
            node = dlink_del_nofree(&conf_allow_list, NULL, node);
            
            /* We need to zap everything out to make it reload */
            if (allow->auth != NULL) {
                  destroy_auth(allow->auth);
                  allow->auth = NULL;
            }
            
            allow->class->item.refcount--;
            allow->class = NULL; /* Zap it! */
            
            cMyFree(allow->hostname);
            cMyFree(allow->ipaddr);
            cMyFree(allow->spoof_mask);
            cMyFree(allow->redir_serv);
            
            allow->port = 0;
            allow->max_per_ip = 0;
            allow->flags = 0;
            
            allow->hostname = hostname;
            allow->ipaddr = ipaddr;
            
            /* Put the node(allow) back in the list */
            dlink_add_node(&conf_allow_list, node, allow);
            
            allow->item.temp = 0; /* Allow this to be used again */
      }
      
      hostname = ipaddr = NULL; /* This is not a memory leak! */

      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "allow")
            if (!mycmp(e->varname, "auth")) {
                  if (allow->auth != NULL) {
                        destroy_auth(allow->auth);
                  }
                  allow->auth = parse_auth(e, "allow::auth");
                  continue;
            }

            PARAMETER(e, "allow")
            if (!mycmp(e->varname, "hostname")) {
                  cDupString(allow->hostname, e->vardata);
            }
            else if (!mycmp(e->varname, "ip")) {
                  cDupString(allow->ipaddr, e->vardata);
            }
            else if (!mycmp(e->varname, "port")) {
                  allow->port = get_port(e->vardata);
            }
            else if (!mycmp(e->varname, "max_per_ip")) {
                  allow->max_per_ip = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "class")) {
                  if (conf_set_class(e, &allow->class)) {
                        report(0, "%s:%d: allow::class: class has been reassigned",
                              e->file->filename, e->varlinenum);
                  }
            }
            else if (!mycmp(e->varname, "redir_serv")) {
                  cDupString(allow->redir_serv, e->vardata);
            }
            else if (!mycmp(e->varname, "redir_port")) {
                  allow->redir_port = get_port(e->vardata);
            }
            else if (!mycmp(e->varname, "spoof_mask")) {
                  cDupString(allow->spoof_mask, e->vardata);
            }
            else if (!mycmp(e->varname, "kline_exempt")) {
                  allow->flags |= get_option(e->vardata, ALLOW_KLINEEXEMPT);
            }
            else if (!mycmp(e->varname, "no_tilde")) {
                  allow->flags |= get_option(e->vardata, ALLOW_NOTILDE);
            }
            else if (!mycmp(e->varname, "need_identd")) {
                  allow->flags |= get_option(e->vardata, ALLOW_NEEDIDENTD);
            }
#ifdef USE_THROTTLE
            else if (!mycmp(e->varname, "throttle_exempt")) {
                  allow->flags |= get_option(e->vardata, ALLOW_THROTTLEEXEMPT);
            }
#endif
            else {
                  add_unknown(e, allow, CONF_ALLOW);
            }
      }
      
      if (!BadPtr(allow->hostname) && in_str(allow->hostname, '@')) {
            allow->flags |= ALLOW_HOSTNAME_AT;
      }
      if (!BadPtr(allow->ipaddr) && in_str(allow->ipaddr, '@')) {
            allow->flags |= ALLOW_IPADDR_AT;
      }
      
      return (void *)allow;
}

int test_allow(ConfigFile *file, void *item)
{
      ConfigItem_allow *allow = (ConfigItem_allow *)item;

      if (!BadPtr(allow->ipaddr) && !valid_ip_addr(allow->ipaddr, 1)) {
            cMyFree(allow->ipaddr);
      }
      if (allow->class == NULL) {
            allow->class = DefaultClass;
            allow->class->item.refcount++;
      }
      if (BadPtr(allow->redir_serv)) {
            allow->redir_port = 0;
      }
      else if (allow->redir_port == -1) {
            cMyFree(allow->redir_serv);
      }

      return CONF_SUCCESS;
}

void free_allow(void *item)
{
      ConfigItem_allow *allow = (ConfigItem_allow *)item;

      /* This can be NULL if test_allow() were to return CONF_FAILURE */
      if (allow->class != NULL) {
            allow->class->item.refcount--;
      }

      if (allow->auth != NULL) {
            destroy_auth(allow->auth);
      }

      cMyFree(allow->hostname);
      cMyFree(allow->ipaddr);
      cMyFree(allow->spoof_mask);
      cMyFree(allow->redir_serv);
      cMyFree(allow);
}

void *parse_oper(ConfigEntry *entry)
{
      ConfigEntry *e;
      ConfigItem_oper *oper;

      BLOCK_NAME(entry, "oper", "oper name")
      BLOCK_ENTRIES(entry, "oper")

      if ((oper = find_oper(entry->vardata)) == NULL) {
            oper = (ConfigItem_oper *)MyMalloc(sizeof(ConfigItem_oper));
            cDupString(oper->name, entry->vardata);
            dlink_add(&conf_oper_list, oper);
      }
      else {
            /* Clear any possibilities */
            if (oper->auth != NULL) {
                  destroy_auth(oper->auth);
                  oper->auth = NULL;
            }
            
            conf_clear_from(&oper->from);
            
            if (oper->class != NULL) {
                  oper->class->item.refcount--;
                  oper->class = NULL;
            }
            
            cMyFree(oper->join_on_oper);
            
            oper->flags = 0;
            oper->item.temp = 0;
      }

      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "oper")
            if (!mycmp(e->varname, "auth")) {
                  if (oper->auth != NULL) {
                        destroy_auth(oper->auth);
                  }
                  oper->auth = parse_auth(e, "oper::auth");
                  continue;
            }

            PARAMETER(e, "oper")
            if (!mycmp(e->varname, "from")) {
                  conf_add_from(&oper->from, e->vardata);
            }
            else if (!mycmp(e->varname, "class")) {
                  if (conf_set_class(e, &oper->class)) {
                        report(0, "%s:%d: oper::class: class has been reassigned",
                              e->file->filename, e->varlinenum);
                  }
            }
            else if (!mycmp(e->varname, "join_on_oper")) {
                  cDupString(oper->join_on_oper, e->vardata);
            }
            else if (!mycmp(e->varname, "show_oper_motd")) {
                  oper->flags |= get_option(e->vardata, OFLAG_OPERMOTD);
            }
            else if (!mycmp(e->varname, "no_recvq_throttle")) {
                  oper->flags |= get_option(e->vardata, OFLAG_NORECVQTHROTTLE);
            }
            else if (!mycmp(e->varname, "locops")) {
                  oper->flags |= get_option(e->vardata, OFLAG_LOCOPS);
            }
            else if (!mycmp(e->varname, "globops")) {
                  oper->flags |= get_option(e->vardata, OFLAG_GLOBOPS);
            }
            else if (!mycmp(e->varname, "wallops")) {
                  oper->flags |= get_option(e->vardata, OFLAG_WALLOPS);
            }
            else if (!mycmp(e->varname, "netadmin")) {
                  oper->flags |= get_option(e->vardata, OFLAG_NETADMIN);
            }
            else if (!mycmp(e->varname, "admin")) {
                  oper->flags |= get_option(e->vardata, OFLAG_ADMIN);
            }
            else if (!mycmp(e->varname, "sadmin")) {
                  oper->flags |= get_option(e->vardata, OFLAG_SADMIN);
            }
            else if (!mycmp(e->varname, "can_perform")) {
                  oper->flags |= parse_token_list(e->vardata, oper_can_perform);
            }
            else if (!mycmp(e->varname, "can_see")) {
                  oper->flags |= parse_token_list(e->vardata, oper_can_see);
            }
            else {
                  add_unknown(e, oper, CONF_OPER);
            }
      }

      return (void *)oper;
}

int test_oper(ConfigFile *file, void *item)
{
      ConfigItem_oper *oper = (ConfigItem_oper *)item;

      if (BadPtr(oper->name)) {
            report(0, "%s: oper: missing oper name, ignoring block", file->filename);
            dlink_del(&conf_oper_list, oper, NULL);
            return CONF_FAILURE;
      }
      if (oper->auth == NULL) {
            report(0, "%s: oper::auth: missing section, ignoring block", file->filename);
            dlink_del(&conf_oper_list, oper, NULL);
            return CONF_FAILURE;
      }
      if (!oper->from.host_count) {
            report(0, "%s: oper::from: missing parameter, ignoring block", file->filename);
            dlink_del(&conf_oper_list, oper, NULL);
            return CONF_FAILURE;
      }
      if (oper->class == NULL) {
            oper->class = DefaultClass;
            oper->class->item.refcount++;
      }

      return CONF_SUCCESS;
}

void free_oper(void *item)
{
      ConfigItem_oper *oper = (ConfigItem_oper *)item;

      /* This can be NULL if test_oper() returns CONF_FAILURE */
      if (oper->class != NULL) {
            oper->class->item.refcount--;
      }

      conf_clear_from(&oper->from);

      if (oper->auth != NULL) {
            destroy_auth(oper->auth);
      }

      cMyFree(oper->name);
      cMyFree(oper->join_on_oper);
      cMyFree(oper);
}

void *parse_super(ConfigEntry *entry)
{
      ConfigEntry *e;

      BLOCK_ENTRIES(entry, "super")

      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "super")
            PARAMETER(e, "super")

            if (!mycmp(e->varname, "server")) {
                  char *server = NULL;
                  cDupString(server, e->vardata);
                  dlink_add(&conf_super_list, server);
            }
            else {
                  UNKNOWN_VAR(e, "super");
            }
      }

      return NULL;
}

void *parse_link(ConfigEntry *entry)
{
      ConfigEntry *e;
      ConfigItem_link *linkp;

      BLOCK_NAME(entry, "link", "server name")
      BLOCK_ENTRIES(entry, "link")

      if ((linkp = find_link(entry->vardata, "*")) == NULL) {
            linkp = (ConfigItem_link *)MyMalloc(sizeof(ConfigItem_link));
            cDupString(linkp->servername, entry->vardata);
            dlink_add(&conf_link_list, linkp);
      }
      else {
            /* Clear any possibilities */
            
            if (linkp->auth != NULL) {
                  destroy_auth(linkp->auth);
                  linkp->auth = NULL;
            }
            
            if (linkp->class != NULL) {
                  linkp->class->item.refcount--;
                  linkp->class = NULL;
            }
            
            cMyFree(linkp->host);
            cMyFree(linkp->bind_ip);
            
            linkp->flags = 0;
            linkp->ip.s_addr = 0;
            linkp->port = 0;
            linkp->item.temp = 0;
      }

      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "link")
            if (!mycmp(e->varname, "auth")) {
                  if (linkp->auth != NULL) {
                        destroy_auth(linkp->auth);
                  }
                  linkp->auth = parse_auth(e, "link::auth");
                  continue;
            }

            PARAMETER(e, "link")
            if (!mycmp(e->varname, "host")) {
                  cDupString(linkp->host, e->vardata);
            }
            else if (!mycmp(e->varname, "bind_ip")) {
                  cDupString(linkp->bind_ip, e->vardata);
            }
            else if (!mycmp(e->varname, "port")) {
                  linkp->port = get_port(e->vardata);
            }
            else if (!mycmp(e->varname, "class")) {
                  if (conf_set_class(e, &linkp->class)) {
                        report(0, "%s:%d: link::class: class has been reassigned",
                              e->file->filename, e->varlinenum);
                  }
            }
            else if (!mycmp(e->varname, "auto_connect")) {
                  linkp->flags |= get_option(e->vardata, LINK_AUTOCONNECT);
            }
#ifdef USE_ZLIB
            else if (!mycmp(e->varname, "compressed")) {
                  linkp->flags |= get_option(e->vardata, LINK_COMPRESS);
            }
#endif
#ifdef USE_OPENSSL
            else if (!mycmp(e->varname, "encrypted")) {
                  linkp->flags |= get_option(e->vardata, LINK_SECURE);
            }
#endif
            else {
                  add_unknown(e, linkp, CONF_LINK);
            }
      }

      return (void *)linkp;
}

int test_link(ConfigFile *file, void *item)
{
      ConfigItem_link *linkp = (ConfigItem_link *)item;

      if (BadPtr(linkp->servername)) {
            report(0, "%s: link: missing server name, ignoring block", file->filename);
            dlink_del(&conf_link_list, linkp, NULL);
            return CONF_FAILURE;
      }
      if (linkp->auth == NULL) {
            report(0, "%s: link::auth: missing section, ignoring block", file->filename);
            dlink_del(&conf_link_list, linkp, NULL);
            return CONF_FAILURE;
      }
      if (BadPtr(linkp->host)) {
            cDupString(linkp->host, "*");
      }
      if (!BadPtr(linkp->bind_ip) && !valid_ip_addr(linkp->bind_ip, 0)) {
            cMyFree(linkp->bind_ip);
      }
      if (linkp->port <= 0) {
            linkp->port = DEFAULT_PORTNUM;
      }
      if (linkp->class == NULL) {
            linkp->class = DefaultClass;
            linkp->class->item.refcount++;
      }

      return CONF_SUCCESS;
}

void free_link(void *item)
{
      ConfigItem_link *linkp = (ConfigItem_link *)item;

      /* This can be NULL if test_link() returns CONF_FAILURE */
      if (linkp->class != NULL) {
            linkp->class->item.refcount--;
      }

      if (linkp->auth != NULL) {
            destroy_auth(linkp->auth);
      }

      cMyFree(linkp->servername);
      cMyFree(linkp->host);
      cMyFree(linkp->bind_ip);
      cMyFree(linkp);
}

void *parse_listen(ConfigEntry *entry)
{
      ConfigEntry *e, *ee;
      ConfigItem_listen *listenp;
      char addr[HOSTIPLEN + 1], portbuf[HOSTIPLEN + 20];
      int first, last, i;

      BLOCK_NAME(entry, "listen", "ip address")
      BLOCK_ENTRIES(entry, "listen")

      if (*entry->vardata != '*') {
            strncpyzt(addr, entry->vardata, HOSTIPLEN + 1);
      }
      else {
            *addr = '*';
            *(addr + 1) = '\0';
      }

      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "listen")
            PARAMETER(e, "listen")

            first = last = -1;
            if (!mycmp(e->varname, "port")) {
                  if (!(first = get_port(e->vardata))) {
                        report(0, "%s:%d: listen: invalid port, ignoring entry", e->file->filename,
                              e->varlinenum);
                        continue;
                  }
                  last = first;
            }
            else if (!mycmp(e->varname, "range")) {
                  get_port_range(e->vardata, &first, &last);
                  if (!first || !last) {
                        report(0, "%s:%d: listen: invalid range, ignoring entry", e->file->filename,
                              e->varlinenum);
                        continue;
                  }
            }
            else {
                  UNKNOWN_VAR(e, "listen");
                  continue;
            }

            if (first == -1 || (last == -1)) {
                  continue;
            }

            for (i = first; i <= last; i++) {
                  if ((listenp = find_listen(addr, i)) == NULL) {
                        listenp = (ConfigItem_listen *)MyMalloc(sizeof(ConfigItem_listen));
                        cDupString(listenp->ipaddr, addr);
                        listenp->port = i;
                        dlink_add(&conf_listen_list, listenp);
                  }
                  else {
                        /* Clear any possibilities */
                        listenp->flags = 0;
                        listenp->item.temp = 0;
                  }

                  if (e->entries == NULL) {
                        continue;
                  }

                  ircsprintf(portbuf, "listen::%s::%d", addr, i);
                  for (EACH_ENTRY(ee, e)) {
                        VARIABLE(ee, portbuf)
                        PARAMETER(ee, portbuf)

                        if (!mycmp(ee->varname, "client_only")) {
                              listenp->flags |= get_option(ee->vardata, LISTEN_CLIENTONLY);
                        }
                        else if (!mycmp(ee->varname, "server_only")) {
                              listenp->flags |= get_option(ee->vardata, LISTEN_SERVERONLY);
                        }
#ifdef USE_OPENSSL
                        else if (!mycmp(ee->varname, "secure")) {
                              listenp->flags |= get_option(ee->vardata, LISTEN_SECURE);
                        }
#endif
                        else {
                              UNKNOWN_VAR(ee, portbuf);
                        }
                  }
            }
      }

      return NULL;
}

void *parse_restrict(ConfigEntry *entry)
{
      ConfigEntry *e;
      char *mask = NULL, *reason = NULL;
      unsigned int flags = BAN_LOCAL;
      simBan *sban;

      BLOCK_NAME(entry, "restrict", "restrict type")
      BLOCK_ENTRIES(entry, "restrict")

      if (!mycmp(entry->vardata, "nick")) {
            flags |= SBAN_NICK;
      }
      else if (!mycmp(entry->vardata, "gcos")) {
            flags |= SBAN_GCOS;
      }
      else if (!mycmp(entry->vardata, "chan")) {
            flags |= SBAN_CHAN;
      }
      else if (!mycmp(entry->vardata, "file")) {
            flags |= SBAN_FILE;
      }
      else {
            report(0, "%s:%d: restrict: unknown parameter %s, ignoring block", entry->file->filename,
                  entry->varlinenum, entry->vardata);
            return NULL;
      }

      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "restrict")
            PARAMETER(e, "restrict")

            if (!mycmp(e->varname, "mask")) {
                  mask = e->vardata;
            }
            else if (!mycmp(e->varname, "reason")) {
                  reason = e->vardata;
            }
      }

      if ((sban = make_simban(mask, reason, 0, flags)) == NULL) {
            report(0, "%s:%d: restrict: invalid mask, ignoring block", entry->file->filename, entry->varlinenum);
            return NULL;
      }

      add_simban(sban);
      return NULL;
}

void *parse_kill(ConfigEntry *entry)
{
      ConfigEntry *e;
      char *user = NULL, *host = NULL, *reason = NULL;
      userBan *uban;

      BLOCK_ENTRIES(entry, "kill")

      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "kill")
            PARAMETER(e, "kill")

            if (!mycmp(e->varname, "mask")) {
                  if ((host = strchr(e->vardata, '@')) != NULL) {
                        *host++ = '\0';
                        user = e->vardata;
                  }
                  else {
                        host = e->vardata;
                        user = "*";
                  }
            }
            else if (!mycmp(e->varname, "reason")) {
                  reason = e->vardata;
            }
      }

      if ((uban = make_userban(user, host, reason, 0)) == NULL) {
            report(0, "%s:%d: kill: invalid mask, ignoring block", entry->file->filename, entry->varlinenum);
            return NULL;
      }

      uban->flags |= BAN_LOCAL;
      add_userban(uban);

      return NULL;
}

void *parse_include(ConfigEntry *entry)
{
      ConfigItem_include *include;
      dlink_node *node;

      BLOCK_NAME(entry, "include", "file name")

      DLINK_FOREACH_DATA(conf_include_list.head, node, include, ConfigItem_include) {
            if (!mycmp(include->filename, entry->vardata)) {
                  report(0, "%s:%d: include: config file %s already %s", entry->file->filename,
                        entry->varlinenum, (include->loaded) ? "loaded" : "marked for inclusion");
                  return NULL;
            }
      }

      include = (ConfigItem_include *)MyMalloc(sizeof(ConfigItem_include));
      cDupString(include->filename, entry->vardata);

      dlink_add(&conf_include_list, include);
      return (void *)include;
}

#ifndef STATIC_MODULES
void *parse_modules(ConfigEntry *entry)
{
      ConfigEntry *e;
      char *mod;

      BLOCK_ENTRIES(entry, "modules")
      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "modules")
            PARAMETER(e, "modules")

            mod = NULL; /* This is not a memory leak */

            if (!mycmp(e->varname, "file")) {
                  cDupString(mod, e->vardata);
                  dlink_add(&conf_modulefile_list, mod);
            }
            else if (!mycmp(e->varname, "path")) {
                  cDupString(mod, e->vardata);
                  dlink_add(&conf_modulepath_list, mod);
            }
            else {
                  UNKNOWN_VAR(e, "modules");
            }
      }

      return NULL;
};
#endif

void *parse_network(ConfigEntry *entry)
{
      ConfigEntry *e;
      ConfigItem_network *network = &tmpNetworkConfig;
      char *s;

      BLOCK_ENTRIES(entry, "network")
      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "network")
            PARAMETER(e, "network")

            if (!mycmp(e->varname, "name")) {
                  cDupString(network->name, e->vardata);
                  for (s = e->vardata; *s != '\0'; s++) {
                        if (*s == ' ') {
                              *s = '-';
                        }
                  }
                  cDupString(network->name_005, e->vardata);
            }
            else if (!mycmp(e->varname, "kline_address")) {
                  cDupString(network->kline_address, e->vardata);
            }
            else if (!mycmp(e->varname, "services_server")) {
                  cDupString(network->services_server, e->vardata);
            }
            else if (!mycmp(e->varname, "stats_server")) {
                  cDupString(network->stats_server, e->vardata);
            }
            else if (!mycmp(e->varname, "max_link_depth")) {
                  network->max_link_depth = atoi(e->vardata);
            }
            else {
                  add_unknown(e, network, CONF_NETWORK);
            }
      }

      return (void *)network;
}

int test_network(ConfigFile *file, void *item)
{
      ConfigItem_network *network = (ConfigItem_network *)item;
      static int retval = CONF_SUCCESS;

      if (BadPtr(network->name)) {
            if (Internal.rehashing) {
                  report(0, "%s: network::name: missing parameter", file->filename);
                  retval = CONF_FALLBACK;
            }
            else {
                  cDupString(network->name, DEFAULT_NETWORK_NAME);
                  cDupString(network->name_005, DEFAULT_NETWORK_NAME_005);
                  report(0, "%s: network::name: missing parameter, using %s", file->filename,
                        network->name);
            }
      }
      if (BadPtr(network->kline_address) || !valid_email_addr(network->kline_address, 1)) {
            cDupString(network->kline_address, DEFAULT_KLINE_ADDRESS);
      }
      if (network->max_link_depth < 1) {
            network->max_link_depth = DEFAULT_LINK_DEPTH;
      }

      return retval;
}

void free_network(void *item)
{
      ConfigItem_network *network = (ConfigItem_network *)item;
      cMyFree(network->name);
      cMyFree(network->name_005);
      cMyFree(network->kline_address);
      cMyFree(network->services_server);
      cMyFree(network->stats_server);
}

void *parse_masking(ConfigEntry *entry)
{
      ConfigEntry *e;
      ConfigItem_masking *masking = &tmpMaskingConfig;

      BLOCK_ENTRIES(entry, "masking")
      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "masking")
            PARAMETER(e, "masking")

            if (!mycmp(e->varname, "netadmin_mask")) {
                  cDupString(masking->netadmin_mask, e->vardata);
            }
            else if (!mycmp(e->varname, "admin_mask")) {
                  cDupString(masking->admin_mask, e->vardata);
            }
            else if (!mycmp(e->varname, "oper_mask")) {
                  cDupString(masking->oper_mask, e->vardata);
            }
            else if (!mycmp(e->varname, "user_mask_prefix")) {
                  cDupString(masking->user_mask_prefix, e->vardata);
            }
            else if (!mycmp(e->varname, "mask_key1")) {
                  masking->mask_key1 = strtol(e->vardata, NULL, 10);
            }
            else if (!mycmp(e->varname, "mask_key2")) {
                  masking->mask_key2 = strtol(e->vardata, NULL, 10);
            }
            else if (!mycmp(e->varname, "mask_key3")) {
                  masking->mask_key3 = strtol(e->vardata, NULL, 10);
            }
            else {
                  add_unknown(e, masking, CONF_MASKING);
            }
      }

      return (void *)masking;
}

int test_masking(ConfigFile *file, void *item)
{
      ConfigItem_masking *masking = (ConfigItem_masking *)item;
      static int retval = CONF_SUCCESS;

      if (BadPtr(masking->user_mask_prefix)) {
            cDupString(masking->user_mask_prefix, DEFAULT_USER_MASK_PREFIX);
      }
      if (masking->mask_key1 < 10000) {
            if (Internal.rehashing && (retval != CONF_FAILURE)) {
                  report(0, "%s: masking::mask_key1: invalid key", file->filename);
                  retval = CONF_FALLBACK;
            }
            else {
                  report(1, "%s: ERROR: masking::mask_key1: invalid key", file->filename);
                  retval = CONF_FAILURE;
            }
      }
      if (masking->mask_key2 < 10000) {
            if (Internal.rehashing && (retval != CONF_FAILURE)) {
                  report(0, "%s: masking::mask_key2: invalid key", file->filename);
                  retval = CONF_FALLBACK;
            }
            else {
                  report(1, "%s: ERROR: masking::mask_key2: invalid key", file->filename);
                  retval = CONF_FAILURE;
            }
      }
      if (masking->mask_key3 < 10000) {
            if (Internal.rehashing && (retval != CONF_FAILURE)) {
                  report(0, "%s: masking::mask_key3: invalid key", file->filename);
                  retval = CONF_FALLBACK;
            }
            else {
                  report(1, "%s: ERROR: masking::mask_key3: invalid key", file->filename);
                  retval = CONF_FAILURE;
            }
      }

      return retval;
}

void free_masking(void *item)
{
      ConfigItem_masking *masking = (ConfigItem_masking *)item;
      cMyFree(masking->netadmin_mask);
      cMyFree(masking->admin_mask);
      cMyFree(masking->oper_mask);
      cMyFree(masking->user_mask_prefix);
}

void *parse_flood(ConfigEntry *entry)
{
      ConfigEntry *e;
      ConfigItem_flood *flood = &tmpFloodConfig;

      BLOCK_ENTRIES(entry, "flood")
      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "flood")
            PARAMETER(e, "flood")

            if (!mycmp(e->varname, "knock_delay")) {
                  flood->knock_delay = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "accept_notice_time")) {
                  flood->accept_notice_time = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "antispam_quit_msg_time")) {
                  flood->antispam_quit_msg_time = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "pace_wait_simple")) {
                  flood->pace_wait_simple = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "pace_wait_intense")) {
                  flood->pace_wait_intense = atime(e->vardata);
            }
#ifdef USE_THROTTLE
            else if (!mycmp(e->varname, "max_connect_count")) {
                  flood->max_connect_count = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "min_connect_time")) {
                  flood->min_connect_time = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "throttle_rejected_clients")) {
                  flood->throttle_rejected_clients = get_option(e->vardata, 1);
            }
#endif
            else if (!mycmp(e->varname, "anti_nick_flood")) {
                  flood->anti_nick_flood = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "anti_away_flood")) {
                  flood->anti_away_flood = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "user_recvq_limit")) {
                  flood->user_recvq_limit = get_size(e->vardata);
            }
            else if (!mycmp(e->varname, "min_join_part_time")) {
                  flood->min_join_part_time = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "max_join_part_count")) {
                  flood->max_join_part_count = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "spambot_squelch_time")) {
                  flood->spambot_squelch_time = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "increase_oper_recvq")) {
                  flood->increase_oper_recvq = get_option(e->vardata, 1);
            }
            else {
                  add_unknown(e, flood, CONF_FLOOD);
            }
      }

      return (void *)flood;
}

int test_flood(ConfigFile *file, void *item)
{
      ConfigItem_flood *flood = (ConfigItem_flood *)item;

      if (flood->user_recvq_limit < 512) {
            flood->user_recvq_limit = DEFAULT_USER_RECVQ_LIMIT;
      }

      if (flood->min_join_part_time < 60) {
            flood->min_join_part_time = DEFAULT_JOIN_PART_TIME;
      }
      if (flood->max_join_part_count < 5) {
            flood->max_join_part_count = DEFAULT_JOIN_PART_COUNT;
      }

      return CONF_SUCCESS;
}

void free_flood(void *item)
{
      /* Empty */
}

void *parse_general(ConfigEntry *entry)
{
      ConfigEntry *e;
      ConfigItem_general *general = &tmpGeneralConfig;
      char *s;
      int mindex;
      xMode *t;

      BLOCK_ENTRIES(entry, "general")
      for (EACH_ENTRY(e, entry)) {
            VARIABLE(e, "general")
            PARAMETER(e, "general")

            if (!mycmp(e->varname, "max_chans_per_user")) {
                  general->max_chans_per_user = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "connauth_timeout")) {
                  general->connauth_timeout = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "statsfile_save_freq")) {
                  general->statsfile_save_freq = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "modes_on_connect")) {
                  for (s = e->vardata; *s != '\0'; s++) {
                        if ((mindex = usermodes->map[(unsigned char)*s]) == -1) {
                              continue;
                        }

                        t = &usermodes->table[mindex];

                        if (!(NEEDOPER_UMODES & t->mode || (t->mode == UMODE_REGNICK) || (t->mode == UMODE_DEAF))) {
                              general->modes_on_connect |= t->mode;
                        }
                  }
            }
            else if (!mycmp(e->varname, "join_on_connect")) {
                  cDupString(general->join_on_connect, e->vardata);
            }
            else if (!mycmp(e->varname, "compression_level")) {
                  general->compression_level = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "auto_connect_freq")) {
                  general->auto_connect_freq = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "ts_delta_warn")) {
                  general->ts_delta_warn = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "ts_delta_max")) {
                  general->ts_delta_max = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "short_motd")) {
                  general->short_motd = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "hide_super_servers")) {
                  general->hide_super_servers = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "check_identd")) {
                  general->check_identd = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "resolve_hostnames")) {
                  general->resolve_hostnames = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "show_headers")) {
                  general->show_headers = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "failed_oper_notice")) {
                  general->failed_oper_notice = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "custom_channels")) {
                  general->custom_channels = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "enable_knock")) {
                  general->enable_knock = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "custom_quit_msgs")) {
                  general->custom_quit_msgs = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "show_cliconn_quit_msgs")) {
                  general->show_cliconn_quit_msgs = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "show_invisible_lusers")) {
                  general->show_invisible_lusers = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "flatten_links")) {
                  general->flatten_links = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "enable_map")) {
                  general->enable_map = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "spy_notices")) {
                  general->spy_notices = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "no_oper_accept")) {
                  general->no_oper_accept = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "enable_netadmins")) {
                  general->enable_netadmins = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "restrict_chan_override")) {
                  general->restrict_chan_override = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "allow_fake_channels")) {
                  general->allow_fake_channels = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "max_accept")) {
                  general->max_accept = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "max_dccallow")) {
                  general->max_dccallow = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "ignore_remote_motd")) {
                  general->ignore_remote_motd = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "ignore_remote_rules")) {
                  general->ignore_remote_rules = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "max_bans")) {
                  general->max_bans = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "default_kline_time")) {
                  general->default_kline_time = atime(e->vardata);
            }
            else if (!mycmp(e->varname, "max_watch")) {
                  general->max_watch = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "max_kills")) {
                  general->max_kills = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "ignore_remote_stats")) {
                  general->ignore_remote_stats = get_option(e->vardata, 1);
            }
            else if (!mycmp(e->varname, "max_targets")) {
                  general->max_targets = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "max_who_replies")) {
                  general->max_who_replies = atoi(e->vardata);
            }
            else if (!mycmp(e->varname, "no_mixed_versions")) {
                  general->no_mixed_versions = get_option(e->vardata, 1);
            }
            else {
                  add_unknown(e, general, CONF_GENERAL);
            }
      }

      return (void *)general;
}

int test_general(ConfigFile *file, void *item)
{
      ConfigItem_general *general = (ConfigItem_general *)item;

      if (general->max_chans_per_user < 1) {
            general->max_chans_per_user = DEFAULT_CHANS_PER_USER;
      }
      if (general->connauth_timeout < 0) {
            general->connauth_timeout = DEFAULT_CONNAUTH_TIMEOUT;
      }
      if (general->compression_level < 0 || general->compression_level > 9) {
            general->compression_level = DEFAULT_COMPRESSION_LEVEL;
      }
      if (general->ts_delta_max <= 0) {
            general->ts_delta_max = DEFAULT_TS_DELTA_MAX;
      }
      if (general->ts_delta_warn <= 0) {
            general->ts_delta_warn = DEFAULT_TS_DELTA_WARN;
      }
      if (general->max_accept < 0) {
            general->max_accept = DEFAULT_MAX_ACCEPT;
      }
      if (general->max_dccallow < 0) {
            general->max_dccallow = DEFAULT_MAX_DCCALLOW;
      }
      if (general->max_bans < 10) {
            general->max_bans = DEFAULT_MAX_BANS;
      }
      if (general->default_kline_time && (general->default_kline_time < 60)) {
            general->default_kline_time = DEFAULT_KLINE_TIME;
      }
      if (general->max_watch < 10) {
            general->max_watch = DEFAULT_MAX_WATCH;
      }
      if (general->max_kills < 1) {
            general->max_kills = DEFAULT_MAX_KILLS;
      }
      if (general->max_targets < 1 || general->max_targets >= HARD_TARGET_LIMIT) {
            general->max_targets = DEFAULT_MAX_TARGETS;
      }

      return CONF_SUCCESS;
}

void free_general(void *item)
{
      ConfigItem_general *general = (ConfigItem_general *)item;
      cMyFree(general->join_on_connect);
}

ConfigItem_class *find_class(char *name)
{
      dlink_node *node;
      ConfigItem_class *class;

      DLINK_FOREACH_DATA(conf_class_list.head, node, class, ConfigItem_class) {
            if (!mycmp(class->name, name)) {
                  return class;
            }
      }

      return NULL;
}

ConfigItem_oper *find_oper(char *name)
{
      dlink_node *node;
      ConfigItem_oper *oper;

      DLINK_FOREACH_DATA(conf_oper_list.head, node, oper, ConfigItem_oper) {
            if (!mycmp(oper->name, name)) {
                  return oper;
            }
      }

      return NULL;
}

char *find_super(char *servername)
{
      dlink_node *node;
      char *super;

      DLINK_FOREACH_DATA(conf_super_list.head, node, super, char) {
            if (!match(super, servername)) {
                  return super;
            }
      }

      return NULL;
}

ConfigItem_link *find_link(char *servername, char *host)
{
      dlink_node *node;
      ConfigItem_link *linkp;

      DLINK_FOREACH_DATA(conf_link_list.head, node, linkp, ConfigItem_link) {
            if (match(linkp->servername, servername) && match(servername, linkp->servername)) {
                  continue;
            }
            if (!match(linkp->host, host) || !match(host, linkp->host)) {
                  return linkp;
            }
      }

      return NULL;
}

ConfigItem_listen *find_listen(char *ipaddr, int port)
{
      dlink_node *node;
      ConfigItem_listen *listenp;

      DLINK_FOREACH_DATA(conf_listen_list.head, node, listenp, ConfigItem_listen) {
            if (listenp->port != port) {
                  continue;
            }
            if (!irccmp(listenp->ipaddr, ipaddr)) {
                  return listenp;
            }
      }

      return NULL;
}

void conf_rehash()
{
      dlink_node *node, *next = NULL;
      ConfigItem_class *class;
      ConfigItem_allow *allow;
      ConfigItem_oper *oper;
      ConfigItem_link *linkp;
      ConfigItem_listen *listenp;
      char *cp;

      Internal.rehashing = 1;

      if (ServerInfo != NULL) {
            free_servinfo(ServerInfo);
            ServerInfo = NULL;
      }
      if (AdminInfo != NULL) {
            cMyFree(AdminInfo->name);
            cMyFree(AdminInfo->desc);
            cMyFree(AdminInfo->email);
            cMyFree(AdminInfo);
      }

      /* WARNING! Items with class references _must_ be cleared before conf_class_list
       * in order to allow any class references to be removed.
       */

      DLINK_FOREACH_SAFE_DATA(conf_allow_list.head, node, next, allow, ConfigItem_allow) {
            if (allow->item.refcount) {
                  allow->item.temp = 1;
            }
            else {
                  dlink_del(&conf_allow_list, NULL, node);
                  free_allow(allow);
            }
      }
      DLINK_FOREACH_SAFE_DATA(conf_oper_list.head, node, next, oper, ConfigItem_oper) {
            if (oper->item.refcount) {
                  oper->item.temp = 1;
            }
            else {
                  dlink_del(&conf_oper_list, NULL, node);
                  free_oper(oper);
            }
      }
      DLINK_FOREACH_SAFE_DATA(conf_link_list.head, node, next, linkp, ConfigItem_link) {
            if (linkp->item.refcount || linkp->servers) {
                  linkp->item.temp = 1;
            }
            else {
                  dlink_del(&conf_link_list, NULL, node);
                  free_link(linkp);
            }
      }

      DLINK_FOREACH_SAFE_DATA(conf_class_list.head, node, next, class, ConfigItem_class) {
            if (class->item.perm) {
                  continue;
            }
            if (class->item.refcount || class->clients) {
                  class->item.temp = 1;
            }
            else {
                  dlink_del(&conf_class_list, NULL, node);
                  free_class(class);
            }
      }

      DLINK_FOREACH_SAFE_DATA(conf_listen_list.head, node, next, listenp, ConfigItem_listen) {
            if ((listenp->l != NULL) && listenp->l->clients) {
                  listenp->item.temp = 1;
            }
            else {
                  if (listenp->l != NULL) {
                        close_one_listener(listenp->l, NULL);
                  }
                  dlink_del(&conf_listen_list, NULL, node);
                  cMyFree(listenp->ipaddr);
                  cMyFree(listenp);
            }
      }

      DLINK_FOREACH_SAFE_DATA(conf_super_list.head, node, next, cp, char) {
            dlink_del(&conf_super_list, NULL, node);
            cMyFree(cp);
      }

#ifndef STATIC_MODULES
      DLINK_FOREACH_SAFE_DATA(conf_modulefile_list.head, node, next, cp, char) {
            dlink_del(&conf_modulefile_list, NULL, node);
            cMyFree(cp);
      }
      DLINK_FOREACH_SAFE_DATA(conf_modulepath_list.head, node, next, cp, char) {
            dlink_del(&conf_modulepath_list, NULL, node);
            cMyFree(cp);
      }
#endif

      remove_userbans_match_flags(BAN_LOCAL, BAN_TEMPORARY);
      remove_simbans_match_flags(SBAN_NICK|BAN_LOCAL, BAN_TEMPORARY);
      remove_simbans_match_flags(SBAN_CHAN|BAN_LOCAL, BAN_TEMPORARY);
      remove_simbans_match_flags(SBAN_GCOS|BAN_LOCAL, BAN_TEMPORARY);
      remove_simbans_match_flags(SBAN_FILE|BAN_LOCAL, BAN_TEMPORARY);

      hook_run(h_conf_rehash, NULL);

      init_conf();

      /* Reinitialise my own socket stuffs. */
      close_listeners();
      restart_resolver();
      open_listeners();

      /* Rebuild the isupport list. */
      build_isupport();
      
      /* Re-read any message files. */
      rehash_message_files();
      
      /* And reset the autoconnect event */
      del_event_byfunc(try_connections, NULL);
      try_connections(&me);

      Internal.rehashing = 0;
}

int attach_allow(aClient *cptr, ConfigItem_allow *allow)
{
      IPEntry *ipe;

      ASSERT(cptr != NULL);
      ASSERT(cptr->localClient != NULL);
      ASSERT(cptr->user != NULL);
      ASSERT(cptr->localUser != NULL);
      ASSERT(allow != NULL);

      ipe = find_or_add_ip(&cptr->ip);
      ipe->count++;
      SetHashedIP(cptr);

      if (allow->max_per_ip > 0 && (ipe->count > allow->max_per_ip)) {
            return CLIENTAUTH_TOOMANYIPS;
      }
      if ((allow->class->clients + 1) > allow->class->max_clients) {
            return CLIENTAUTH_CLASSFULL;
      }
      if ((allow->auth != NULL) && !BadPtr(cptr->localClient->passwd)) {
            if (!check_auth(allow->auth, cptr->localClient->passwd)) {
                  return CLIENTAUTH_INVALIDPW;
            }
            memset(cptr->localClient->passwd, '\0', PASSWDLEN + 1);
      }

      if (!BadPtr(allow->spoof_mask)) {
            char *p;

            SetSpoofed(cptr);

            if ((p = strchr(allow->spoof_mask, '@')) != NULL) {
                  SetSpoofedUn(cptr);

                  *p = '\0';
                  strncpyzt(cptr->username, allow->spoof_mask, USERLEN + 1);
                  strncpyzt(cptr->host, (p + 1), HOSTLEN + 1);
                  *p = '@';
            }
            else {
                  strncpyzt(cptr->host, allow->spoof_mask, HOSTLEN + 1);
            }

#ifdef HIDE_SPOOFED_IPS
            cptr->ip.s_addr = 0;
            strncpyzt(cptr->hostip, HIDDEN_IP, HOSTIPLEN + 1);
            strncpyzt(cptr->localClient->sockhost, HIDDEN_IP, HOSTIPLEN + 1);
#endif

            if (IsSpoofedUn(cptr)) {
                  sendto_realops_lev(SPY_LEV, "%s spoofing userhost to %s@%s [%s]",
                        cptr->name, cptr->username, cptr->host, cptr->hostip);
            }
            else {
                  sendto_realops_lev(SPY_LEV, "%s spoofing host to %s [%s]", cptr->name,
                        cptr->host, cptr->hostip);
            }
      }

      if (allow->flags & ALLOW_KLINEEXEMPT) {
            SetKlineExempt(cptr);
      }
#ifdef USE_THROTTLE
      if (allow->flags & ALLOW_THROTTLEEXEMPT) {
            SetThrottleExempt(cptr);
            throttle_remove(cptr->hostip);
      }
#endif

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "Attached allow %x to client %s(%x)", allow, cptr->name, cptr));
#endif

      cptr->localUser->allow = allow;
      allow->item.refcount++;

      return CLIENTAUTH_MATCHED;
}

void detach_allow(aClient *cptr)
{
      ConfigItem_allow *allow;

      ASSERT(cptr != NULL);
      ASSERT(cptr->localUser != NULL);

      if (cptr->localUser->allow == NULL) {
            return;
      }

      allow = cptr->localUser->allow;
      cptr->localUser->allow = NULL;
      allow->item.refcount--;

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "Detached allow %x from client %s(%x)", allow, cptr->name, cptr));
#endif

      if (!allow->item.refcount && allow->item.temp) {
#ifdef CDEBUG
            Debug((DEBUG_DEBUG, "Freeing allow conf"));
#endif
            dlink_del(&conf_allow_list, allow, NULL);
            free_allow(allow);
      }
}

void attach_class(aClient *cptr)
{
      ConfigItem_class *class = NULL;

      ASSERT(cptr != NULL);
      ASSERT(cptr->localClient != NULL);

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "Attempting to attach class to client %s(%x)", cptr->name, cptr));
#endif

      if (cptr->localClient->class != NULL) {
#ifdef CDEBUG
            Debug((DEBUG_DEBUG, "-> first removing client from old class %s(%x)",
                  cptr->localClient->class->name, cptr->localClient->class));
#endif
            detach_class(cptr);
      }

      if (IsServer(cptr)) {
            if (cptr->serv->conf->class != NULL) {
                  class = cptr->serv->conf->class;
            }
      }
      else if (cptr->localUser != NULL) {
            if (cptr->localUser->oper != NULL) {
                  class = cptr->localUser->oper->class;
            }
            else if (cptr->localUser->allow != NULL) {
                  class = cptr->localUser->allow->class;
            }
      }

      if (class == NULL) {
            class = DefaultClass;
      }
      ASSERT(class != NULL);
      
#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "attach_class(%s): I think the best class is [%s]",
            cptr->name, class->name));
#endif

      cptr->localClient->class = class;
      class->item.refcount++;
      class->clients++;

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "Attached class %s(%x) to client %s(%x)", class->name, class, cptr->name, cptr));
#endif
}

void detach_class(aClient *cptr)
{
      ConfigItem_class *class;

      ASSERT(cptr != NULL);
      ASSERT(cptr->localClient != NULL);

      if (cptr->localClient->class == NULL) {
            return;
      }

      class = cptr->localClient->class;
      cptr->localClient->class = NULL;
      class->item.refcount--;
      class->clients--;

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "Detached class %s(%x) from client %s(%x)", class->name, class,
            cptr->name, cptr));
#endif

      if (!class->clients && !class->item.refcount && class->item.temp && !class->item.perm) {
#ifdef CDEBUG
            Debug((DEBUG_DEBUG, "Freeing class conf"));
#endif
            dlink_del(&conf_class_list, class, NULL);
            free_class(class);
      }
}

void attach_oper(aClient *cptr, ConfigItem_oper *oper)
{
      ASSERT(cptr != NULL);
      ASSERT(cptr->localUser != NULL);
      ASSERT(oper != NULL);

      cptr->localUser->oper = oper;
      oper->item.refcount++;

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "Attached oper %s(%x) to client %s(%x)", oper->name, oper, cptr->name, cptr));
#endif
}

void detach_oper(aClient *cptr)
{
      ConfigItem_oper *oper;

      ASSERT(cptr != NULL);
      ASSERT(cptr->localUser != NULL);

      if (cptr->localUser->oper == NULL) {
            return;
      }

      oper = cptr->localUser->oper;
      cptr->localUser->oper = NULL;
      oper->item.refcount--;

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "Detached oper %s(%x) from client %s(%x)", oper->name, oper, cptr->name, cptr));
#endif

      if (!oper->item.refcount && oper->item.temp) {
#ifdef CDEBUG
            Debug((DEBUG_DEBUG, "Freeing oper conf"));
#endif
            dlink_del(&conf_oper_list, oper, NULL);
            free_oper(oper);
      }
}

void attach_link(aClient *cptr, ConfigItem_link *conf)
{
      ASSERT(cptr != NULL);
      ASSERT(cptr->serv != NULL);
      ASSERT(conf != NULL);

      cptr->serv->conf = conf;
      conf->item.refcount++;
      conf->servers++;

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "Attached link %s(%x) to server %s(%x)", conf->servername, conf, cptr->name, cptr));
#endif
}

void detach_link(aClient *cptr)
{
      ConfigItem_link *conf;

      ASSERT(cptr != NULL);
      ASSERT(cptr->serv != NULL);

      if (cptr->serv->conf == NULL) {
            return;
      }

      conf = cptr->serv->conf;
      cptr->serv->conf = NULL;
      conf->item.refcount--;
      conf->servers--;

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "Detached link %s(%x) from server %s(%x)", conf->servername, conf, cptr->name, cptr));
#endif

      if (!conf->item.refcount && !conf->servers && conf->item.temp) {
#ifdef CDEBUG
            Debug((DEBUG_DEBUG, "Freeing link conf"));
#endif
            dlink_del(&conf_link_list, conf, NULL);
            free_link(conf);
      }
}

void attach_listen(Listener *l, ConfigItem_listen *conf)
{
      ASSERT(l != NULL);
      ASSERT(conf != NULL);

      l->conf = conf;
      conf->item.refcount++;

      conf->l = l;
      l->item.refcount++;

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "Attached listen %x to listener %s/%d(%x)", conf, l->ipaddr, l->port, l));
#endif
}

void detach_listen(Listener *l)
{
      ConfigItem_listen *conf;

      ASSERT(l != NULL);
      ASSERT(l->conf != NULL);

      conf = l->conf;
      l->conf = NULL;
      conf->item.refcount--;

      conf->l = NULL;
      l->item.refcount--;

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "Detached listen %x from listener %s/%d(%x)", conf, l->ipaddr, l->port, l));
#endif

      if (!conf->item.refcount && conf->item.temp) {
#ifdef CDEBUG
            Debug((DEBUG_DEBUG, "Freeing listen conf"));
#endif
            dlink_del(&conf_listen_list, conf, NULL);
            cMyFree(conf->ipaddr);
            cMyFree(conf);
      }
}

void detach_confs(aClient *cptr)
{
      ASSERT(cptr != NULL);

#ifdef CDEBUG
      Debug((DEBUG_DEBUG, "detach_confs() for client %s (%x)", cptr->name, cptr));
#endif

      if (IsServer(cptr)) {
            detach_link(cptr);
      }
      else if (cptr->user != NULL) {
            detach_allow(cptr);
            detach_oper(cptr);
      }

      detach_class(cptr);
}

Generated by  Doxygen 1.6.0   Back to index