/************************************************************************
 *   IRC - Internet Relay Chat, src/s_conf.c
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *                      University of Oulu, Computing Center
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 1, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* $Id: s_conf.c,v 1.12 2006/01/07 22:13:26 trystanscott Exp $ */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "inet.h"
#include <signal.h>
#include "h.h"
#include "userban.h"
#include "confparse.h"
#include "memcount.h"

/* This entire file has basically been rewritten from scratch with the
 * exception of lookup_confhost and attach_Iline/attach_iline fucntions
 * Feb04 -epi
 */

extern int  rehashed;
extern int  forked;
extern tConf tconftab[];
extern sConf sconftab[];

/* internally defined functions  */

static int          lookup_confhost(aConnect *);
static int          attach_iline(aClient *, aAllow *, char *, int);

/* externally defined functions  */

extern aClass       *make_class();
extern aOper        *make_oper();
extern aConnect     *make_connect();
extern aAllow       *make_allow();
extern struct Conf_Me   *make_me();
extern aPort        *make_port();
extern void          read_shortmotd(char *);

/* these are our global lists of ACTIVE conf entries */

#define MAXUSERVS 24

aConnect   *connects  = NULL;       /* connects, C/N pairs  */
aAllow     *allows    = NULL;       /* allows  - I lines    */
Conf_Me    *MeLine    = NULL;       /* meline - only one    */
aOper      *opers     = NULL;       /* opers - Olines       */
aPort      *ports     = NULL;       /* ports - P/M lines    */
aClass     *classes   = NULL;      /* classes - Ylines     */
char       *uservers[MAXUSERVS];    /* uservers = Ulines    */
Conf_Modules *modules = NULL;

/* this set of lists is used for loading and rehashing the config file */

aConnect    *new_connects   = NULL;
aAllow      *new_allows     = NULL;
Conf_Me     *new_MeLine     = NULL;
aOper       *new_opers      = NULL;
aPort       *new_ports      = NULL;
aClass      *new_classes    = NULL;
char        *new_uservers[MAXUSERVS+1];    /* null terminated array */
Conf_Modules *new_modules       = NULL;

#ifdef LOCKFILE
extern void do_pending_klines(void);
#endif
extern void confparse_error(char *, int);
extern int klinestore_init(int);
#ifdef TOYS
extern void init_chef();
#endif

/* initclass()
 * initialize the default class
 */

void initclass()
{
    new_classes = make_class();

    DupString(new_classes->name, "default");
    new_classes->connfreq = CONNECTFREQUENCY;
    new_classes->pingfreq = PINGFREQUENCY;
    new_classes->maxlinks = MAXIMUM_LINKS;
    new_classes->maxsendq = MAXSENDQLENGTH;
    new_classes->links = 0;
}

/* init_globals
 * initialize our major globals to the defaults first
 */

void init_globals()
{
    strncpyzt(ProxyMonURL, DEFAULT_WGMON_URL, sizeof(ProxyMonURL));
    strncpyzt(ProxyMonHost, DEFAULT_WGMON_HOST, sizeof(ProxyMonHost));
    strncpyzt(Network_Name, DEFAULT_NETWORK, sizeof(Network_Name));
    strncpyzt(Services_Name, DEFAULT_SERVICES_NAME, sizeof(Services_Name));
    strncpyzt(Stats_Name, DEFAULT_STATS_NAME, sizeof(Stats_Name));
    strncpyzt(NS_Register_URL, DEFAULT_NS_REGISTER_URL,
              sizeof(NS_Register_URL));
    strncpyzt(Network_Kline_Address, DEFAULT_NKLINE_ADDY,
                                            sizeof(Network_Kline_Address));
    strncpyzt(Local_Kline_Address, DEFAULT_LKLINE_ADDY,
                                            sizeof(Local_Kline_Address));
    strncpyzt(Staff_Address, DEFAULT_STAFF_ADDRESS, sizeof(Staff_Address));
    maxchannelsperuser = DEFAULT_MAXCHANNELSPERUSER;
    tsmaxdelta = DEFAULT_TSMAXDELTA;
    tswarndelta = DEFAULT_TSWARNDELTA;
    local_ip_limit = DEFAULT_LOCAL_IP_CLONES;
    local_ip24_limit = DEFAULT_LOCAL_IP24_CLONES;
    global_ip_limit = DEFAULT_GLOBAL_IP_CLONES;
    global_ip24_limit = DEFAULT_GLOBAL_IP24_CLONES;

	strncpyzt(HostPrefix, Network_Name, sizeof(HostPrefix));
	strncpyzt(HostDomain, DEFAULT_HOST_DOMAIN, sizeof(HostDomain));
	strncpyzt(HiddenServName, HIDDEN_SERVER_NAME, sizeof(HiddenServName));
    strncpyzt(HiddenServDesc, HIDDEN_SERVER_DESC, sizeof(HiddenServDesc));
	strncpyzt(HELPCHAN, DEFAULT_HELPCHAN, sizeof(HELPCHAN));
	strncpyzt(WEBSITE, DEFAULT_WEBSITE, sizeof(WEBSITE));
	strncpyzt(AUP, DEFAULT_AUP, sizeof(AUP));
}


/* free_ routines
 * free the requested conf structure
 * feb.04 -epi
 */

void
free_connect(aConnect *ptr)
{
    MyFree(ptr->host);
    MyFree(ptr->apasswd);
    MyFree(ptr->cpasswd);
    MyFree(ptr->source);
    MyFree(ptr->name);
    MyFree(ptr->class_name);
    MyFree(ptr);
    return;
}

void
free_allow(aAllow *ptr)
{
    MyFree(ptr->ipmask);
    MyFree(ptr->passwd);
    MyFree(ptr->hostmask);
    MyFree(ptr->class_name);
    MyFree(ptr);
    return;
}

void
free_oper(aOper *ptr)
{
    int i = 0;
    while(ptr->hosts[i])
    {
        MyFree(ptr->hosts[i]);
        i++;
    }
    MyFree(ptr->passwd);
    MyFree(ptr->nick);
    MyFree(ptr->class_name);
    MyFree(ptr);
    return;
}

void
free_port(aPort *ptr)
{
    MyFree(ptr->allow);
    MyFree(ptr->address);
    MyFree(ptr);
    return;
}

void
free_class(aClass *ptr)
{
    MyFree(ptr->name);
    MyFree(ptr);
    return;
}

void expire_class(aClass *cl)
{
    aClass *ccl, *pcl = NULL;
    if (cl->refs == 0 && cl->maxlinks == -1)
    {
        for (ccl = classes; ccl; ccl = ccl->next)
        {
            if (ccl == cl)
            {
                if (pcl)
                    pcl->next = ccl->next;
                else
                    classes = ccl->next;
                free_class(ccl);
                break;
            }
            pcl = ccl;
        }
    }
}

/* clear_conflinks()
 * remove associated confs from this client
 * and free the conf if it is scheduled to be deleted
 * Feb04 -epi
 */

void
clear_conflinks(aClient *cptr)
{
    if (cptr->class)
    {
        cptr->class->links--;
        cptr->class->refs--;
        expire_class(cptr->class);
    }
    if(IsServer(cptr))
    {
        aConnect *x;
        if((x = cptr->serv->aconn))
        {
            x->acpt = NULL;
            if (x->legal == -1)     /* scheduled for removal? */
            {
                aConnect *aconn = NULL;

                if (x == connects)
                    connects = x->next;
                else
                {
                    for (aconn = connects;
                         aconn != NULL && aconn->next != x;
                         aconn = aconn->next);
                    if (aconn != NULL)
                        aconn->next = x->next;
                    else
                        sendto_realops_lev(DEBUG_LEV, "Deleting scheduled "
                                           "connect, but it isn't in the "
                                           "list?? [%s]", x->name);
                }
                x->class->refs--;
                expire_class(x->class);
                free_connect(x);
            }
            cptr->serv->aconn = NULL;
        }
    }
    else if (cptr->user != NULL)
    {
        aAllow *x;
        aOper *y;
        if((x = cptr->user->allow))
        {
            x->clients--;
            if(x->clients <= 0 && x->legal == -1)
            {
                /* remove this allow now that its empty */
                aAllow *allow = NULL;
                if (allows == x)
                    allows = x->next;
                else
                {
                    for (allow = allows;
                         allow != NULL && allow->next != x;
                         allow = allow->next);
                    if (allow != NULL)
                        allow->next = x->next;
                    else
                        sendto_realops_lev(DEBUG_LEV, "Deleting scheduled "
                                           "allow, but it isn't in the list?? "
                                           "[%s / %s]", x->ipmask,x->hostmask);
                }
                x->class->refs--;
                expire_class(x->class);
                free_allow(x);
            }
            cptr->user->allow = NULL;
        }
        if((y = cptr->user->oper))
        {
            y->opers--;
            if(y->legal == -1 && y->opers <= 0)
            {
                aOper *oper = NULL;
                if (opers == y)
                    opers = y->next;
                else
                {
                    for (oper = opers;
                         oper != NULL && oper->next != y;
                         oper = oper->next);
                    if (oper != NULL)
                        oper->next = y->next;
                    else
                        sendto_realops_lev(DEBUG_LEV, "Deleting scheduled "
                                           "oper, but it isn't in the list?? "
                                           "[%s]", y->nick);
                }
                y->class->refs--;
                expire_class(y->class);
                free_oper(y);
            }
            cptr->user->oper = NULL;
        }
    }
    return;
}

/* find the appropriate conf and return it */

aConnect *
find_aConnect(char *name)
{
    aConnect *tmp;
    for(tmp = connects; tmp; tmp = tmp->next)
        if(!match(name, tmp->name))
            break;
    return tmp;
}

static inline aPort *
find_port(int port, char *bind)
{
    aPort *tmp;
    for(tmp = ports; tmp; tmp = tmp->next)
        if (tmp->port == port)
        {
            if (tmp->address == bind)  /* both NULL */
                break;
            if (tmp->address && bind && !mycmp(tmp->address, bind))
                break;
        }
    return tmp;
}

aConnect *
find_aConnect_match(char *name, char *username, char *host)
{
    aConnect *aconn;
    char userhost[USERLEN + HOSTLEN + 3];

    ircsprintf(userhost, "%s@%s", username, host);

    for(aconn = connects; aconn; aconn = aconn->next)
    {
        if (aconn->legal == -1)
            continue;
        if(!mycmp(name, aconn->name) && !match(userhost, aconn->host))
            break;
    }
    return aconn;
}

int
find_aUserver(char *name)
{
    int i;
    for(i = 0; uservers[i]; i++)
    {
        if(!mycmp(name, uservers[i]))
            return 1;
    }
    return 0;
}

aOper *
find_oper(char *name, char *username, char *sockhost, char *hostip)
{
    aOper *aoper;
    char userhost[USERLEN + HOSTLEN + 3];
    char userip[USERLEN + HOSTLEN + 3];
    int i;

    /* sockhost OR hostip must match our host field */


    ircsprintf(userhost, "%s@%s", username, sockhost);
    ircsprintf(userip, "%s@%s", username, hostip);

    for(aoper = opers; aoper; aoper = aoper->next)
    {
        if (aoper->legal == -1)
            continue;

        for(i = 0; aoper->hosts[i]; i++)
        {
            if(!mycmp(name, aoper->nick) && (!match(aoper->hosts[i], userhost) 
                    || !match(aoper->hosts[i], userip)))
                return aoper;
        }
    }
    return NULL;
}

static inline aOper *
find_oper_byname(char *name)
{
    aOper *aoper;
    for(aoper = opers; aoper; aoper = aoper->next)
        if(!mycmp(name, aoper->nick))
            break;
    return aoper;
}

static inline aClass *
find_class(char *name)
{
    aClass *tmp;
    if(!name)
        return find_class("default");
    for(tmp = classes; tmp; tmp = tmp->next)
        if(!mycmp(name, tmp->name))
            break;
    return tmp;
}

/* set_effective_class
 * sets the class for cptr properly
 */

void
set_effective_class(aClient *cptr)
{
    if (cptr->class)
    {
        cptr->class->links--;
        cptr->class->refs--;
        expire_class(cptr->class);
    }
    if(IsServer(cptr))
    {
        if(cptr->serv->aconn->class)
            cptr->class = cptr->serv->aconn->class;
        else
            cptr->class = find_class("default");
    }
    else
    {
        if(cptr->user && cptr->user->oper)
            cptr->class = cptr->user->oper->class;
        else if(cptr->user && cptr->user->allow)
            cptr->class = cptr->user->allow->class;
        else
            cptr->class = find_class("default");
    }
    cptr->class->refs++;
    cptr->class->links++;
    return;
}
    

/* find the first (best) I line to attach.
 * rewritten in feb04 for the overdue death of aConfItem
 * and all the shit that came with it.  -epi
 * Rewritten again in Mar04 to optimize and get rid of deceptive logic.
 * Whoever wrote this originally must have been drunk...  -Quension
 */
int 
attach_Iline(aClient *cptr, struct hostent *hp, char *sockhost)
{
    aAllow *allow;

    static char useriphost[USERLEN + 1 + HOSTLEN + 1];
    static char usernamehost[USERLEN + 1 + HOSTLEN + 1];
    char   *iphost;
    char   *namehost = NULL;    /* squish compiler warning */
    int     len;

    /* user@host in both buffers, plus pointers to host only */
    len = ircsprintf(useriphost, "%s@", cptr->username);
    iphost = useriphost + len;
    strcpy(iphost, sockhost);
    if (hp)
    {
        memcpy(usernamehost, useriphost, USERLEN+2);
        namehost = usernamehost + len;
        len = sizeof(usernamehost) - len;
        strncpy(namehost, hp->h_name, len);
        add_local_domain(namehost, len - strlen(namehost));
    }

    for (allow = allows; allow; allow = allow->next) 
    {
        if(allow->legal == -1)
            continue;

        if (allow->port && (allow->port != cptr->lstn->port))
            continue;

        if (!allow->ipmask || !allow->hostmask)
            return (attach_iline(cptr, allow, iphost, 0));

        /* match hostmask against both resolved name and IP, prefer name */
        if (allow->flags & CONF_FLAGS_I_MATCH_NAME)
        {
            if (allow->flags & CONF_FLAGS_I_NAME_HAS_AT)
            {
                if (hp && !match(allow->hostmask, usernamehost))
                    return (attach_iline(cptr, allow, namehost, 1));
                if (!match(allow->hostmask, useriphost))
                    return (attach_iline(cptr, allow, hp?namehost:iphost, 1));
            }
            else
            {
                if (hp && !match(allow->hostmask, namehost))
                    return (attach_iline(cptr, allow, namehost, 0));
                if (!match(allow->hostmask, iphost))
                    return (attach_iline(cptr, allow, hp?namehost:iphost, 0));
            }
        }

        if (allow->flags & CONF_FLAGS_I_MATCH_HOST)
        {
            if (allow->flags & CONF_FLAGS_I_HOST_HAS_AT)
            {
                if (!match(allow->ipmask, useriphost))
                    return (attach_iline(cptr, allow, iphost, 1));
            }
            else
            {
                if (!match(allow->ipmask, iphost))
                    return (attach_iline(cptr, allow, iphost, 0));
            }
        }
    }
    return -1;          /* no match */
}

/*
 * rewrote to remove the "ONE" lamity *BLEH* I agree with comstud on
 * this one. - Dianora
 */
static int 
attach_iline(aClient *cptr, aAllow *allow, char *uhost, int doid)
{
    if(allow->class->links >= allow->class->maxlinks)
        return -3;

    if (doid)
        cptr->flags |= FLAGS_DOID;
    get_sockhost(cptr, uhost);
    
    cptr->user->allow = allow;
    allow->clients++;

    return 0;
}

/* confadd_ functions
 * add a config item
 * Feb.15/04 -epi
 */
static int oper_access[] =
{
    ~0,            '*',
    OFLAG_LOCAL,   'o',
    OFLAG_GLOBAL,  'O',
    OFLAG_REHASH,  'r',
    OFLAG_DIE,     'D',
    OFLAG_RESTART, 'R',
    OFLAG_GLOBOP,  'h',
    OFLAG_WALLOP,  'w',
    OFLAG_LOCOP,   'l',
    OFLAG_LROUTE,  'c',
    OFLAG_GROUTE,  'C',
    OFLAG_LKILL,   'k',
    OFLAG_GKILL,   'K',
    OFLAG_KLINE,   'b',
    OFLAG_UNKLINE, 'B',
    OFLAG_LNOTICE, 'n',
    OFLAG_GNOTICE, 'N',
    OFLAG_ADMIN,   'A',
    OFLAG_SADMIN,  'a',
    OFLAG_UMODEc,  'u',
    OFLAG_UMODEf,  'f',
    OFLAG_UMODEF,  'F',
	OFLAG_UMODEb,  'W',
    OFLAG_UMODEd,  'd',
    OFLAG_UMODEy,  'y',
    0, 0 };

int
confadd_oper(cVar *vars[], int lnum)
{
    cVar *tmp;
    aOper *x = make_oper();
    int *i, flag, c = 0, hc = 0;
    char *m = "*";

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if(tmp->type && (tmp->type->flag & SCONFF_NAME))
        {
            if(x->nick)
            {
                confparse_error("Multiple name definitions", lnum);
                free_oper(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->nick, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_HOST))
        {
            if((hc+1) > MAXHOSTS)
            {
                confparse_error("Excessive host definitions", lnum);
                free_oper(x);
                return -1;
            }
            tmp->type = NULL;
            if (!strchr(tmp->value, '@') && *tmp->value != '/')
            {
                char       *newhost;
                int         len = 3;
                len += strlen(tmp->value);
                newhost = (char *) MyMalloc(len);
                ircsprintf(newhost, "*@%s", tmp->value);
                x->hosts[hc] = newhost;
            }
            else
                DupString(x->hosts[hc], tmp->value);
            hc++;
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_PASSWD))
        {
            if(x->passwd)
            {
                confparse_error("Multiple password definitions", lnum);
                free_oper(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->passwd, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_ACCESS))
        {
            if(x->flags > 0)
            {
                confparse_error("Multiple access definitions", lnum);
                free_oper(x);
                return -1;
            }
            for (m=(*tmp->value) ? tmp->value : m; *m; m++)
            {
                for (i=oper_access; (flag = *i); i+=2)
                    if (*m==(char)(*(i+1)))
                    {
                        x->flags |= flag;
                        break;
                    }
            }
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_CLASS))
        {
            if(x->class_name)
            {
                confparse_error("Multiple class definitions", lnum);
                free_oper(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->class_name, tmp->value);
        }
    }
    if(!x->nick)
    {
        confparse_error("Lacking name in oper block", lnum);
        free_oper(x);
        return -1;
    }
    if(!x->hosts[0])
    {
        confparse_error("Lacking host in oper block", lnum);
        free_oper(x);
        return -1;
    }
    if(!x->passwd)
    {
        confparse_error("Lacking passwd in oper block", lnum);
        free_oper(x);
        return -1;
    }
    if(x->flags == 0)
    {
        confparse_error("Lacking access in oper block", lnum);
        free_oper(x);
        return -1;
    }
    x->next = new_opers;
    new_opers = x;
    return lnum;
}

static int server_info[] =
{
    CONN_ZIP, 'Z',
    CONN_DKEY, 'E',
    CONN_HUB, 'H',
    0, 0
};

int
confadd_connect(cVar *vars[], int lnum)
{
    cVar *tmp;
    aConnect *x = make_connect();
    int *i, flag, c = 0;
    char *m = "*";

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if(tmp->type && (tmp->type->flag & SCONFF_NAME))
        {
            if(x->name)
            {
                confparse_error("Multiple name definitions", lnum);
                free_connect(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->name, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_HOST))
        {
            if(x->host)
            {
                confparse_error("Multiple host definitions", lnum);
                free_connect(x);
                return -1;
            }
            tmp->type = NULL;
            if (!strchr(tmp->value, '@') && *tmp->value != '/')
            {
                char       *newhost;
                int         len = 3;
                len += strlen(tmp->value);
                newhost = (char *) MyMalloc(len);
                ircsprintf(newhost, "*@%s", tmp->value);
                x->host = newhost;
            }
            else
                DupString(x->host, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_APASSWD))
        {
            if(x->apasswd)
            {
                confparse_error("Multiple apasswd definitions", lnum);
                free_connect(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->apasswd, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_CPASSWD))
        {
            if(x->cpasswd)
            {
                confparse_error("Multiple cpasswd definitions", lnum);
                free_connect(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->cpasswd, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_FLAGS))
        {
            if(x->flags > 0)
            {
                confparse_error("Multiple flag definitions", lnum);
                free_connect(x);
                return -1;
            }
            tmp->type = NULL;
            x->flags = 0;
            for (m=(*tmp->value) ? tmp->value : m; *m; m++)
            {
                for (i=server_info; (flag = *i); i+=2)
                if (*m==(char)(*(i+1)))
                {
                    x->flags |= flag;
                    break;
                }
            }
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_PORT))
        {
            if(x->port > 0)
            {
                confparse_error("Multiple port definitions", lnum);
                free_connect(x);
                return -1;
            }
            tmp->type = NULL;
            x->port = atoi(tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_BIND))
        {
            if(x->source)
            {
                confparse_error("Multiple source definitions", lnum);
                free_connect(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->source, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_CLASS))
        {
            if(x->class_name)
            {
                confparse_error("Multiple class definitions", lnum);
                free_connect(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->class_name, tmp->value);
        }
    }
    if(!x->name)
    {
        confparse_error("Lacking name in connect block", lnum);
        free_connect(x);
        return -1;
    }
    if(!x->apasswd)
    {
        confparse_error("Lacking apasswd in connect block", lnum);
        free_connect(x);
        return -1;
    }
    if(!x->cpasswd)
    {
        confparse_error("Lacking cpasswd in connect block", lnum);
        free_connect(x);
        return -1;
    }
    if(!x->host)
    {
        confparse_error("Lacking host in connect block", lnum);
        free_connect(x);
        return -1;
    }
    x->next = new_connects;
    new_connects = x;
    return lnum;
}

int
confadd_options(cVar *vars[], int lnum)
{
    cVar *tmp;
    int c = 0;
    char *s;

    /* here, because none of the option peice are interdependent
     * all the items are added immediately.   Makes life easier
     * ...except the option flags, which are handled specially -Quension
     */

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if(tmp->type && (tmp->type->flag & OPTF_NETNAME))
        {
            tmp->type = NULL;
            strncpyzt(Network_Name, tmp->value, sizeof(Network_Name));
        }
        else if(tmp->type && (tmp->type->flag & OPTF_SERVNAME))
        {
            tmp->type = NULL;
            strncpyzt(Services_Name, tmp->value, sizeof(Services_Name));
        }
        else if(tmp->type && (tmp->type->flag & OPTF_STATSNAME))
        {
            tmp->type = NULL;
            strncpyzt(Stats_Name, tmp->value, sizeof(Stats_Name));
        }
        else if(tmp->type && (tmp->type->flag & OPTF_WGMONHOST))
        {
            tmp->type = NULL;
            new_confopts |= FLAGS_WGMONHOST;
            strncpyzt(ProxyMonHost, tmp->value, sizeof(ProxyMonHost));
        }
        else if(tmp->type && (tmp->type->flag & OPTF_WGMONURL))
        {
            tmp->type = NULL;
            new_confopts |= FLAGS_WGMONURL;
            strncpyzt(ProxyMonURL, tmp->value, sizeof(ProxyMonURL));
        }
        else if(tmp->type && (tmp->type->flag & OPTF_NSREGURL))
        {
            tmp->type = NULL;
            strncpyzt(NS_Register_URL, tmp->value, sizeof(NS_Register_URL));
        }
        else if(tmp->type && (tmp->type->flag & OPTF_MAXCHAN))
        {
            tmp->type = NULL;
            maxchannelsperuser = atoi(tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & OPTF_SERVTYPE))
        {
            tmp->type = NULL;
            if(!mycmp("HUB", tmp->value))
            {
                new_confopts |= FLAGS_HUB;
                new_confopts &= ~FLAGS_SERVHUB;
            }
            else if(!mycmp("SERVICESHUB", tmp->value))
            {
                new_confopts |= FLAGS_SERVHUB;
                new_confopts |= FLAGS_HUB;
            }
            else if(!mycmp("CLIENT", tmp->value))
                new_confopts &= ~(FLAGS_HUB|FLAGS_SERVHUB);
            else
            {
                confparse_error("Unknown servtype in option block", lnum);
                return -1;
            }
        }
        else if(tmp->type && (tmp->type->flag & OPTF_NKLINEADDY))
        {
            tmp->type = NULL;
            strncpyzt(Network_Kline_Address, tmp->value,
                                    sizeof(Network_Kline_Address));
        }
        else if(tmp->type && (tmp->type->flag & OPTF_LKLINEADDY))
        {
            tmp->type = NULL;
            strncpyzt(Local_Kline_Address, tmp->value,
                                    sizeof(Local_Kline_Address));
        }
        else if(tmp->type && (tmp->type->flag & OPTF_STAFFADDY))
        {
            tmp->type = NULL;
            strncpyzt(Staff_Address, tmp->value, sizeof(Staff_Address));
        }
        else if(tmp->type && (tmp->type->flag & OPTF_LCLONES))
        {
            tmp->type = NULL;
            local_ip_limit = strtol(tmp->value, &s, 10);
            if (*s == ':')
                local_ip24_limit = atoi(s+1);
            if (local_ip_limit < 1)
                local_ip_limit = DEFAULT_LOCAL_IP_CLONES;
            if (local_ip24_limit < 1)
                local_ip24_limit = DEFAULT_LOCAL_IP24_CLONES;
        }
        else if(tmp->type && (tmp->type->flag & OPTF_GCLONES))
        {
            tmp->type = NULL;
            global_ip_limit = strtol(tmp->value, &s, 10);
            if (*s == ':')
                global_ip24_limit = atoi(s+1);
            if (global_ip_limit < 1)
                global_ip_limit = DEFAULT_GLOBAL_IP_CLONES;
            if (global_ip24_limit < 1)
                global_ip24_limit = DEFAULT_GLOBAL_IP24_CLONES;
        }
        else if(tmp->type && (tmp->type->flag & OPTF_SMOTD))
        {
            tmp->type = NULL;
            new_confopts |= FLAGS_SMOTD;
        }
        else if(tmp->type && (tmp->type->flag & OPTF_SMOTD))
        {
            tmp->type = NULL;
            new_confopts |= FLAGS_SMOTD;
        }
        else if(tmp->type && (tmp->type->flag & OPTF_CRYPTPASS))
        {
            tmp->type = NULL;
            new_confopts |= FLAGS_CRYPTPASS;
        }
        else if(tmp->type && (tmp->type->flag & OPTF_SHOWLINKS))
        {
            tmp->type = NULL;
            new_confopts |= FLAGS_SHOWLINKS;
        }
        else if(tmp->type && (tmp->type->flag & OPTF_SPLITOPOK))
        {
            tmp->type = NULL;
            new_confopts |= FLAGS_SPLITOPOK;
        }
        else if(tmp->type && (tmp->type->flag & OPTF_TSMAXDELTA))
        {
            tmp->type = NULL;
            tsmaxdelta = atoi(tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & OPTF_TSWARNDELTA))
        {
            tmp->type = NULL;
            tswarndelta = atoi(tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & OPTF_RQUIT))
        {
            tmp->type = NULL;
            new_confopts |= FLAGS_RQUIT;
        }
		else if(tmp->type && (tmp->type->flag & OPTF_HOSTPREFIX))
		{
			tmp->type = NULL;
			strncpyzt(HostPrefix, tmp->value,
				sizeof(HostPrefix));
		}
		else if(tmp->type && (tmp->type->flag & OPTF_DOMAIN))
		{
			tmp->type = NULL;
			strncpyzt(HostDomain, tmp->value,
				sizeof(HostDomain));
		}

		else if(tmp->type && (tmp->type->flag & OPTF_AUTOUMODEV))
		{
			tmp->type = NULL;
			new_confopts |= FLAGS_AUTOUMODE_v;
		}
		else if(tmp->type && (tmp->type->flag & OPTF_AUTOUMODER))
		{
			tmp->type = NULL;
			new_confopts |= FLAGS_AUTOUMODE_R;
		}
		else if (tmp->type && (tmp->type->flag & OPTF_HIDDENSERVNAME))
        {
            tmp->type = NULL;
            strncpyzt(HiddenServName, tmp->value, sizeof(HiddenServName));
        }
        else if (tmp->type && (tmp->type->flag & OPTF_HIDDENSERVDESC))
        {
            tmp->type = NULL;
            strncpyzt(HiddenServDesc, tmp->value, sizeof(HiddenServDesc));
        }
		 else if(tmp->type && (tmp->type->flag & OPTF_BOTCLASS))
	  	  	{
 	  	  	tmp->type = NULL;
	  	  	DupString(bot_class, tmp->value);
	     }
		  else if (tmp->type && (tmp->type->flag & OPTF_HELPCHAN))
        {
            tmp->type = NULL;
            strncpyzt(HELPCHAN, tmp->value, sizeof(HELPCHAN));
        }
		  else if (tmp->type && (tmp->type->flag & OPTF_WEBSITE))
        {
            tmp->type = NULL;
            strncpyzt(WEBSITE, tmp->value, sizeof(WEBSITE));
        }
		  else if (tmp->type && (tmp->type->flag & OPTF_AUP))
        {
            tmp->type = NULL;
            strncpyzt(AUP, tmp->value, sizeof(AUP));
        }

       

    }
    return lnum;
}

int
confadd_allow(cVar *vars[], int lnum)
{
    cVar *tmp;
    aAllow *x = make_allow();
    int c = 0;
    /* Currently, Allows are the only config types without
     * easy identifiers - so we dont worry about duplicate types.
     * -epi
     */

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if(tmp->type && (tmp->type->flag & SCONFF_IPMASK))
        {
            if(x->ipmask)
            {
                confparse_error("Multiple ipmask definitions", lnum);
                free_allow(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->ipmask, tmp->value);
            if(strchr(x->ipmask, '@'))
                x->flags |= CONF_FLAGS_I_HOST_HAS_AT;
            x->flags |= CONF_FLAGS_I_MATCH_HOST;
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_HOST))
        {
            if(x->hostmask)
            {
                confparse_error("Multiple host definitions", lnum);
                free_allow(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->hostmask, tmp->value);
            if(strchr(x->hostmask, '@'))
                x->flags |= CONF_FLAGS_I_NAME_HAS_AT;
            x->flags |= CONF_FLAGS_I_MATCH_NAME;
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_PASSWD))
        {
            if(x->passwd)
            {
                confparse_error("Multiple passwd definitions", lnum);
                free_allow(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->passwd, tmp->value);
#if (RIDICULOUS_PARANOIA_LEVEL>=1)
            if(myncmp(x->passwd, "oper", 4) == 0)
            {
                if((x->passwd[4] == '.') || (x->passwd[4] == '\0'))
                {
                    char *tmpd = x->passwd;
                    char *tmp = x->passwd + 4;

                    x->flags |= CONF_FLAGS_I_OPERPORT;
                    if(*tmp)
                        tmp++;
                    DupString(x->passwd, tmp);
                    MyFree(tmpd);
                }
            }
#endif
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_PORT))
        {
            if(x->port > 0)
            {
                confparse_error("Multiple host definitions", lnum);
                free_allow(x);
                return -1;
            }
            tmp->type = NULL;
            x->port = atoi(tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_CLASS))
        {
            if(x->class_name)
            {
                confparse_error("Multiple class definitions", lnum);
                free_allow(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->class_name, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_FLAGS))
        {
            char *s = tmp->value;

            while (*s)
                switch (*s++)
                {
                    case 'm': x->flags |= CONF_FLAGS_I_OPERPORT; break;
                    case 'T': x->flags |= CONF_FLAGS_NOTHROTTLE; break;
                    case 'F': x->flags |= CONF_FLAGS_FORCEFLOOD; break;
                    case 'C': x->flags |= CONF_FLAGS_SKIPCLONES; break;
                    default:
                        confparse_error("Unknown flag", lnum);
                        free_allow(x);
                        return -1;
                }

            tmp->type = NULL;
        }
    }
    if(!x->ipmask && !x->hostmask)
    {
        confparse_error("Lacking both ipmask and host for allow", lnum);
        free_allow(x);
        return -1;
    }
    if(!x->ipmask)
        DupString(x->ipmask, "-");
    if(!x->hostmask)
        DupString(x->hostmask, "-");
    x->next = new_allows;
    new_allows = x;
    return lnum;
}

int
confadd_port(cVar *vars[], int lnum)
{
    cVar *tmp;
    aPort *x;
    int    c = 0;

    x = make_port();
    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if(tmp->type && (tmp->type->flag & SCONFF_IPMASK))
        {
            if(x->allow)
            {
                confparse_error("Multiple ipmask definitions", lnum);
                free_port(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->allow, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_BIND))
        {
            if(x->address)
            {
                confparse_error("Multiple bind definitions", lnum);
                free_port(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->address, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_PORT))
        {
            if(x->port > 0)
            {
                confparse_error("Multiple port definitions", lnum);
                free_port(x);
                return -1;
            }
            tmp->type = NULL;
            x->port = atoi(tmp->value);
        }

#ifdef HAVE_SSL
        else if(tmp->type && (tmp->type->flag & SCONFF_TYPE))
        {

            x->ptype = NULL;
            if(!mycmp("SSL", tmp->value))
                x->ptype = "SSL";
            else
            {
                confparse_error("Unknown type in port block", lnum);
                return -1;
            }
        }
#endif


    }
    if(!(x->port > 0))
    {
        confparse_error("Lacking port in port block", lnum);
        free_port(x);
        return -1;
    }
    x->next = new_ports;
    new_ports = x;
    return lnum;
}

int
confadd_global(cVar *vars[], int lnum)
{
    cVar *tmp;
    Conf_Me *x = new_MeLine;
    int c = 0;

    /* note:
     * we dont free this here because we'll do that if we pull out
     */

    if(!x)
    {
        x = make_me();
        new_MeLine = x;
    }

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if(tmp->type && (tmp->type->flag & SCONFF_NAME))
        {
            unsigned char *s;
            int valid = 0;
            if(x->servername)
            {
                confparse_error("Multiple name definitions", lnum);
                return -1;
            }
            /* validate server name, based on m_server() */
            for (s = (unsigned char *) tmp->value; *s; s++)
            {
                if (*s < ' ' || *s > '~')
                {
                    valid = 0;
                    break;
                }
                if (*s == '.')
                    valid = 1;
            }
            if (!valid)
            {
                confparse_error("Invalid server name", lnum);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->servername, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_INFO))
        {
            if(x->info)
            {
                confparse_error("Multiple info definitions", lnum);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->info, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_DPASS))
        {
            if(x->diepass)
            {
                confparse_error("Multiple dpass definitions", lnum);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->diepass, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_RPASS))
        {
            if(x->restartpass)
            {
                confparse_error("Multiple rpass definitions", lnum);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->restartpass, tmp->value);
        }
    }
    if(!x->servername)
    {
        confparse_error("Lacking name definition in global block", lnum);
        return -1;
    }
    if(!x->info)
    {
        confparse_error("Lacking info definition in global block", lnum);
        return -1;
    }
    return lnum;
}

int
confadd_admin(cVar *vars[], int lnum)
{
    cVar *tmp;
    Conf_Me *x = new_MeLine;
    int c = 0;

    if(!x)
    {
        x = make_me();
        new_MeLine = x;
    }

    if (x->admin[0])
    {
        confparse_error("Multiple admin blocks", lnum);
        return -1;
    }

    for(tmp = vars[c]; tmp && (c != 3); tmp = vars[++c])
        DupString(x->admin[c], tmp->value);

    return lnum;
}

int
confadd_class(cVar *vars[], int lnum)
{
    cVar *tmp;
    aClass *x = make_class();
    int c = 0;
    char *s;

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if(tmp->type && (tmp->type->flag & SCONFF_NAME))
        {
            if(x->name)
            {
                confparse_error("Multiple name definitions", lnum);
                free_class(x);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->name, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_PINGFREQ))
        {
            if(x->pingfreq > 0)
            {
                confparse_error("Multiple pingfreq definitions", lnum);
                free_class(x);
                return -1;
            }
            tmp->type = NULL;
            x->pingfreq = atoi(tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_CONNFREQ))
        {
            if(x->connfreq > 0)
            {
                confparse_error("Multiple maxclones/connfreq definitions",
                                lnum);
                free_class(x);
                return -1;
            }
            tmp->type = NULL;
            x->connfreq = strtol(tmp->value, &s, 10);
            if (*s == ':')
                x->ip24clones = atoi(s+1);
            if (x->connfreq < 1)
                x->connfreq = 0;
            if (x->ip24clones < 1)
                x->ip24clones = 0;
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_MAXUSERS))
        {
            if(x->maxlinks > 0)
            {
                confparse_error("Multiple maxusers/maxlinks definitions"
                " (you can only have one or the other)",
                                lnum);
                free_class(x);
                return -1;
            }
            tmp->type = NULL;
            x->maxlinks = atoi(tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_MAXSENDQ))
        {
            if(x->maxsendq > 0)
            {
                confparse_error("Multiple maxsendq definitions", lnum);
                free_class(x);
                return -1;
            }
            tmp->type = NULL;
            x->maxsendq = atoi(tmp->value);
        }
    }
    if(!x->name)
    {
        confparse_error("Lacking name definition", lnum);
        free_class(x);
        return -1;
    }
    if(!(x->maxsendq > 0))
    {
        confparse_error("Lacking maxsendq definition", lnum);
        free_class(x);
        return -1;
    }
    x->next = new_classes;
    new_classes = x;
    return lnum;
}

int
confadd_kill(cVar *vars[], int lnum)
{
    cVar *tmp;
    struct userBan *ban;
    int c = 0;
    char *ub_u = NULL, *ub_r = NULL, *host = NULL;

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if(tmp->type && (tmp->type->flag & SCONFF_MASK))
        {
            if(host)
            {
                confparse_error("Multiple mask definitions", lnum);
                return -1;
            }
            tmp->type = NULL;
            if((host = strchr(tmp->value, '@')))
            {
                *host = '\0';
                host++;
                ub_u = tmp->value;
            }
            else
                host = tmp->value;
        }
        if(tmp->type && (tmp->type->flag & SCONFF_REASON))
        {
            if(ub_r)
            {
                confparse_error("Multiple reason definitions", lnum);
                return -1;
            }
            tmp->type = NULL;
            ub_r = tmp->value;
            break;
        }
    }
    if(!host)
    {
        confparse_error("Lacking mask definition", lnum);
        return -1;
    }
    ub_u = BadPtr(ub_u) ? "*" : ub_u;
    ub_r = BadPtr(ub_r) ? "<No Reason>" : ub_r;

    ban = make_hostbased_ban(ub_u, host);
    if(!ban)
        return lnum;    /* this isnt a parser problem - dont pull out */

    ban->flags |= (UBAN_LOCAL|UBAN_CONF);
    DupString(ban->reason, ub_r);
    ban->timeset = NOW;

    add_hostbased_userban(ban);
    userban_sweep(ban);

    return lnum;
}

int
confadd_super(cVar *vars[], int lnum)
{
    cVar *tmp;
    int c = 0;
    int i;

    /* If multiple super blocks are specified, set up to append */
    for (i = 0; new_uservers[i]; i++)
        ;

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if (i == MAXUSERVS)
        {
            confparse_error("Excessive super server definitions", lnum);
            return -1;
        }
        DupString(new_uservers[i], tmp->value);
        i++;
    }
    new_uservers[i] = NULL;
    return lnum;
}

int
confadd_restrict(cVar *vars[], int lnum)
{
    cVar *tmp;
    int c = 0, type = 0;
    char *mask = NULL, *reason = NULL;
    struct simBan *ban;

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if(tmp->type && (tmp->type->flag & SCONFF_TYPE))
        {
            if(type > 0)
            {
                confparse_error("Multiple type definitions", lnum);
                return -1;
            }
            tmp->type = NULL;
            if(!mycmp("CHAN", tmp->value))
                type = SBAN_CHAN;
            else if(!mycmp("NICK", tmp->value))
                type = SBAN_NICK;
            else if(!mycmp("GCOS", tmp->value))
                type = SBAN_GCOS;
            else
            {
                confparse_error("Unknown type in restrict block", lnum);
                return -1;
            }
            type |= SBAN_LOCAL;
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_MASK))
        {
            if(mask)
            {
                confparse_error("Mutliple mask definitions", lnum);
                return -1;
            }
            tmp->type = NULL;
            mask = tmp->value;
        }
        else if(tmp->type && (tmp->type->flag & SCONFF_REASON))
        {
            if(reason)
            {
                confparse_error("Multiple reason definitions", lnum);
                return -1;
            }
            tmp->type = NULL;
            reason = tmp->value;
        }
    }
    if(!mask)
    {
        confparse_error("Missing mask in restrict block", lnum);
        return -1;
    }
    if(!(type > 0))
    {
        confparse_error("Missing type in restrict block", lnum);
        return -1;
    }
    ban = make_simpleban(type, mask);
    if(!ban)
        return lnum;
    if(find_simban_exact(ban) != NULL)  /* dont add duplicates */
    {
        simban_free(ban);
        return lnum;
    }
    if(!reason)
    {
        if(type & SBAN_CHAN)
            reason = "Reserved Channel";
        else if(type & SBAN_NICK)
            reason = "Reserved Nick";
        else if(type & SBAN_GCOS)
            reason = "Bad GCOS";
    }
    DupString(ban->reason, reason);
    ban->timeset = NOW;

    add_simban(ban);
    return lnum;
}


int confadd_ssl(cVar *vars[], int lnum)
{
    cVar *tmp;
    int c = 0;

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if (tmp->type && (tmp->type->flag & SSTF_CERTIFICATE)) {
            tmp->type = NULL;
            strncpyzt(SSL_Certificate, tmp->value, sizeof(SSL_Certificate));
        } else if (tmp->type && (tmp->type->flag & SSTF_KEYFILE)) {
            tmp->type = NULL;
            strncpyzt(SSL_Keyfile, tmp->value, sizeof(SSL_Keyfile));
        } else if (tmp->type && (tmp->type->flag & SSTF_UMODEZ)) {
            tmp->type = NULL;
            new_confopts |= FLAGS_LETUMODE_z;
        }
    }

    return lnum;
}

#ifdef TOYS
int confadd_elmer(cVar *vars[], int lnum)
{
    cVar *tmp;
    int c = 0;
    static char *last_word;

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if (tmp->type && (tmp->type->flag & EETF_STRING))
        {
            tmp->type = NULL;
            DupString(last_word, tmp->value);
        }
        if (tmp->type && (tmp->type->flag & EETF_TRANSLATE))
        {
            tmp->type = NULL;
            if (last_word)
            {
                add_translate(last_word, tmp->value);
                MyFree(last_word);
            }
        }
    }
    return lnum;
}

#endif

int
confadd_modules(cVar *vars[], int lnum)
{
    cVar *tmp;
    Conf_Modules *x = new_modules;
    int c = 0, ac = 0, oc = 0;

    /* this is like the global block - we dont free here because we do
     * it if we fail
     */

    if(!x)
    {
        x = (Conf_Modules *) MyMalloc(sizeof(Conf_Modules));
        memset((char *) x, '\0', sizeof(Conf_Modules));
        new_modules = x;
    }
    else
    {
        confparse_error("Multiple module blocks in config file", lnum);
        return -1;
    }

    for(tmp = vars[c]; tmp; tmp = vars[++c])
    {
        if(tmp->type && (tmp->type->flag & MBTF_PATH))
        {
            if(x->module_path)
            {
                confparse_error("Multiple module paths defined", lnum);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->module_path, tmp->value);
        }
        else if(tmp->type && (tmp->type->flag & MBTF_AUTOLOAD))
        {
            if((ac+1) > 128)
            {
                confparse_error("Excessive autoloading modules (max 128)",
                                 lnum);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->autoload[ac], tmp->value);
            ac++;
        }
        else if(tmp->type && (tmp->type->flag & MBTF_OPTLOAD))
        {
            if((oc+1) > 128)
            {
                confparse_error("Excessive optional modules (max 128)", lnum);
                return -1;
            }
            tmp->type = NULL;
            DupString(x->optload[oc], tmp->value);
            oc++;
        }
    }
    if(!x->autoload[0] && !x->optload[0])
    {
        confparse_error("No modules defined in module block", lnum);
        return -1;
    }
    return lnum;
}
    

/* set_classes
 * after loading the config into temporary lists, we must
 * set the appropriate classes for each conf.  If we run into
 * problems, then back out.
 */

static inline aClass *
find_new_class(char *name)
{
    aClass *tmp;
    if(!name)
        return find_new_class("default");
    for(tmp = new_classes; tmp; tmp = tmp->next)
        if(!mycmp(name, tmp->name))
            break;
    return tmp;
}

char *
set_classes(void)
{
    aConnect *aconn;
    aAllow   *allow;
    aOper    *aoper;

    /* Note:
     * You may be wondering why we're doing this here and appearently
     * again in our merge routines!  well, this is for sanity.  if
     * for whatever reason we dont have a class for each definition here,
     * back out of the conf load immediately and we wont have distroyed 
     * or overwritten any of our active data.
     * After we run our merge_classes() routine at the start of our
     * merge, then some of these classes will update currently active
     * classes and be free()'d - meaning some of these references are useless.
     * That is why we run it again inside the merge routines.
     * -epi
     */

    for(aconn = new_connects; aconn; aconn = aconn->next)
        if(!(aconn->class = find_new_class(aconn->class_name)))
            return aconn->class_name;
    for(allow = new_allows; allow; allow = allow->next)
        if(!(allow->class = find_new_class(allow->class_name)))
            return allow->class_name;
    for(aoper = new_opers; aoper; aoper = aoper->next)
        if(!(aoper->class = find_new_class(aoper->class_name)))
            return aoper->class_name;
    return NULL;
}


/* merge routines.  used to mirge together new lists and old lists
 * after a rehash. Feb27/04 -epi
 */

static void
merge_me()
{
    if(MeLine)
    {
        MyFree(MeLine->info);
        MyFree(MeLine->diepass);
        MyFree(MeLine->restartpass);
        MyFree(MeLine->admin[0]);
        MyFree(MeLine->admin[1]);
        MyFree(MeLine->admin[2]);
        /* MeLine->info is guaranteed to be replaced */
        MeLine->diepass = NULL;
        MeLine->restartpass = NULL;
        MeLine->admin[0] = NULL;
        MeLine->admin[1] = NULL;
        MeLine->admin[2] = NULL;
    }
    else
    {
        MeLine = new_MeLine;
        strncpyzt(me.name, MeLine->servername, sizeof(me.name));
        strncpyzt(me.info, MeLine->info, sizeof(me.info));
        new_MeLine = NULL;
        return;
    }
    DupString(MeLine->info, new_MeLine->info);
    strncpyzt(me.info, MeLine->info, sizeof(me.info));
    if(new_MeLine->diepass)
        DupString(MeLine->diepass, new_MeLine->diepass);
    if(new_MeLine->restartpass)
        DupString(MeLine->restartpass, new_MeLine->restartpass);
    if(new_MeLine->admin[0])
        DupString(MeLine->admin[0], new_MeLine->admin[0]);
    if(new_MeLine->admin[1])
        DupString(MeLine->admin[1], new_MeLine->admin[1]);
    if(new_MeLine->admin[2])
        DupString(MeLine->admin[2], new_MeLine->admin[2]);
    MyFree(new_MeLine->servername);
    MyFree(new_MeLine->info);
    MyFree(new_MeLine->diepass);
    MyFree(new_MeLine->restartpass);
    MyFree(new_MeLine->admin[0]);
    MyFree(new_MeLine->admin[1]);
    MyFree(new_MeLine->admin[2]);
    MyFree(new_MeLine);
    new_MeLine = NULL;
    return;
}

static void
merge_connects()
{
    aConnect    *aconn, *old_aconn, *ptr = NULL, *ptrn;

    /* first merge the list, then prune the list */

    /* set old as deletable */
    for(old_aconn = connects; old_aconn; old_aconn = old_aconn->next)
        old_aconn->legal = -1;
    /* update or add new */
    for (aconn = new_connects; aconn; aconn = ptrn)
    {
        ptrn = aconn->next;
        if ((old_aconn = find_aConnect(aconn->name)))
        {
            MyFree(old_aconn->host);
            MyFree(old_aconn->apasswd);
            MyFree(old_aconn->cpasswd);
            MyFree(old_aconn->source);
            MyFree(old_aconn->class_name);
            old_aconn->class->refs--;
            expire_class(old_aconn->class);

            old_aconn->host = aconn->host;
            old_aconn->apasswd = aconn->apasswd;
            old_aconn->cpasswd = aconn->cpasswd;
            old_aconn->source = aconn->source;
            old_aconn->class_name = aconn->class_name;
            old_aconn->port = aconn->port;
            old_aconn->flags = aconn->flags;
            old_aconn->class = find_class(aconn->class_name);
            old_aconn->class->refs++;
            old_aconn->legal = 1;
            lookup_confhost(old_aconn);

            MyFree(aconn->name);
            MyFree(aconn);
        }
        else
        {
            aconn->class = find_class(aconn->class_name);
            aconn->class->refs++;
            aconn->legal = 1;
            lookup_confhost(aconn);
            aconn->next = connects;
            connects = aconn;
        }
    }
    new_connects = NULL;

    ptr = NULL;
    /* and prune the active list */
    aconn = connects;
    while(aconn)
    {
        ptrn = aconn->next;
        if((aconn->legal == -1) && !aconn->acpt)
        {
            if(ptr)
                ptr->next = aconn->next;
            else
                connects = aconn->next;
            aconn->class->refs--;
            expire_class(aconn->class);
            free_connect(aconn);
        }
        else
            ptr = aconn;
        aconn = ptrn;
    }
    return;
}

static void
merge_allows()
{
    aAllow *allow, *ptr = NULL, *ptrn;

    for(allow = allows; allow; allow = allow->next)
        allow->legal = -1;
    allow = new_allows;
    while(allow)
    {
        allow->class = find_class(allow->class_name);
        allow->class->refs++;
        /* we dont really have to merge anything here.. */
        /* ..but we should avoid duplicates anyway */
        for (ptr = allows; ptr; ptr = ptr->next)
        {
            if (ptr->class != allow->class)
                continue;
            if (ptr->port != allow->port)
                continue;
            if (ptr->flags != allow->flags)
                continue;
            if (mycmp(ptr->ipmask, allow->ipmask))
                continue;
            if (mycmp(ptr->hostmask, allow->hostmask))
                continue;
            /* inverted logic below */
            if (ptr->passwd && allow->passwd
                && !mycmp(ptr->passwd, allow->passwd))
                break;
            if (ptr->passwd == allow->passwd)
                break;
        }
        /* if duplicate, mark for deletion but add anyway */
        if (ptr)
        {
            ptr->legal = 1;
            allow->legal = -1;
        }
        ptr = allow->next;
        allow->next = allows;
        allows = allow;
        allow = ptr;
    }
    new_allows = NULL;
    ptr = NULL;
    allow = allows;
    while(allow)
    {
        ptrn = allow->next;
        if((allow->legal == -1) && (allow->clients <= 0))
        {
            if(ptr)
                ptr->next = allow->next;
            else
                allows = allow->next;
            allow->class->refs--;
            expire_class(allow->class);
            free_allow(allow);
        }
        else
            ptr = allow;
        allow = ptrn;
    }
    return;     /* this one is easy */
}
    
static void
merge_opers()
{
    aOper *aoper, *old_oper, *ptrn = NULL, *ptr = NULL;

    for(old_oper = opers; old_oper; old_oper = old_oper->next)
        old_oper->legal = -1;

    /* add or merge and del new ones */
    for (aoper = new_opers; aoper; aoper = ptrn)
    {
        ptrn = aoper->next;
        if ((old_oper = find_oper_byname(aoper->nick)))
        {
            int i;

            for (i = 0; old_oper->hosts[i]; i++)
                MyFree(old_oper->hosts[i]);
            MyFree(old_oper->passwd);
            MyFree(old_oper->class_name);
            old_oper->class->refs--;
            expire_class(old_oper->class);

            for (i = 0; aoper->hosts[i]; i++)
                old_oper->hosts[i] = aoper->hosts[i];
            old_oper->hosts[i] = NULL;
            old_oper->passwd = aoper->passwd;
            old_oper->class_name = aoper->class_name;
            old_oper->class = find_class(aoper->class_name);
            old_oper->class->refs++;
            old_oper->flags = aoper->flags;
            old_oper->legal = 1;

            MyFree(aoper->nick);
            MyFree(aoper);
        }
        else
        {
            aoper->class = find_class(aoper->class_name);
            aoper->class->refs++;
            aoper->legal = 1;
            aoper->next = opers;
            opers = aoper;
        }
    }
    new_opers = NULL;

    /* del old ones */
    ptr = NULL;
    aoper = opers;
    while(aoper)
    {
        ptrn = aoper->next;
        if((aoper->legal == -1) && (aoper->opers <= 0))
        {
            if(ptr)
                ptr->next = aoper->next;
            else
                opers = aoper->next;
            free_oper(aoper);
        }
        else
            ptr = aoper;
        aoper = ptrn;
    }
    return;
}

static void
merge_ports()
{
    aPort *aport, *old_port, *ptrn;
    
    if(forked)
        close_listeners();      /* marks ports for deletion */

    /* add or merge and del new ones */
    for (aport = new_ports; aport; aport = ptrn)
    {
        ptrn = aport->next;
        if ((old_port = find_port(aport->port, aport->address)))
        {
            MyFree(old_port->allow);
            old_port->allow = aport->allow;
            old_port->legal = 1;
            MyFree(aport->address);
            MyFree(aport);
        }
        else
        {
            aport->next = ports;
            ports = aport;
        }
    }
    new_ports = NULL;

    if(forked)
        open_listeners();
    return;
}

static void
merge_classes()
{
    aClass  *class, *old_class, *ptr;

    for(old_class = classes; old_class; old_class = old_class->next)
        old_class->maxlinks = -1;

    for (class = new_classes; class; class = class->next)
    {
        if((old_class = find_class(class->name)))
        {
            old_class->connfreq = class->connfreq;
            old_class->pingfreq = class->pingfreq;
            old_class->maxlinks = class->maxlinks;
            old_class->maxsendq = class->maxsendq;
            old_class->ip24clones = class->ip24clones;
            class->maxlinks = -1;
        }
    }

    /* add classes from new_classes that are not maxlinks = -1 */
    for (class = new_classes; class; class = old_class)
    {
        old_class = class->next;
        if (class->maxlinks == -1)
            free_class(class);
        else
        {
            class->next = classes;
            classes = class;
        }
    }
    new_classes = NULL;

    /* now remove any classes from the list marked and w/o refs */
    for (class = classes; class; class = ptr)
    {
        ptr = class->next;
        expire_class(class);
    }
    return;
}

void
merge_options(void)
{
    if (forked && !(confopts & FLAGS_SMOTD) && (new_confopts & FLAGS_SMOTD))
        read_shortmotd(SHORTMOTD);
    confopts = new_confopts;
}

void
merge_confs()
{
    int i;

    merge_classes();        /* this should always be done first */
    merge_me();
    merge_connects();
    merge_allows();
    merge_opers();
    merge_ports();
    merge_options();
    for(i = 0; uservers[i]; i++)
        MyFree(uservers[i]);
    for(i = 0; new_uservers[i]; i++)
    {
        DupString(uservers[i], new_uservers[i]);
        MyFree(new_uservers[i]);
    }
    new_uservers[0] = NULL;
    /* dont worry about accually merging module data - its fairly
     * inactive and static data.  Just replace it.
     */
    if(modules)
    {
        MyFree(modules->module_path);
        for(i = 0; modules->autoload[i]; i++)
            MyFree(modules->autoload[i]);
        for(i = 0; modules->optload[i]; i++)
            MyFree(modules->optload[i]);
        MyFree(modules);
    }
    modules = new_modules;
    new_modules = NULL;
    return;
}

static void
clear_newconfs()
{
    aConnect *aconn = new_connects, *aconn_p;
    aClass   *class = new_classes, *class_p;
    aOper    *aoper = new_opers, *aoper_p;
    aPort    *aport = new_ports, *aport_p;
    aAllow   *allow = new_allows, *allow_p;
    int i = 0;

    while(aconn)
    {
        aconn_p = aconn->next;
        free_connect(aconn);
        aconn = aconn_p;
    }
    new_connects = NULL;
    while(class)
    {
        class_p = class->next;
        free_class(class);
        class = class_p;
    }
    new_classes = NULL;
    while(aoper)
    {
        aoper_p = aoper->next;
        free_oper(aoper);
        aoper = aoper_p;
    }
    new_opers = NULL;
    while(aport)
    {
        aport_p = aport->next;
        free_port(aport);
        aport = aport_p;
    }
    new_ports = NULL;
    while(allow)
    {
        allow_p = allow->next;
        free_allow(allow);
        allow = allow_p;
    }
    new_allows = NULL;
    if(new_MeLine)
    {
        MyFree(new_MeLine->servername);
        MyFree(new_MeLine->info);
        MyFree(new_MeLine->diepass);
        MyFree(new_MeLine->restartpass);
        MyFree(new_MeLine->admin[0]);
        MyFree(new_MeLine->admin[1]);
        MyFree(new_MeLine->admin[2]);
        MyFree(new_MeLine);
        new_MeLine = NULL;
    }
    while(new_uservers[i])
    {
        DupString(uservers[i], new_uservers[i]);
        MyFree(new_uservers[i]);
        i++;
    }
    new_uservers[0] = NULL;
    if(new_modules)
    {
        for(i = 0; new_modules->autoload[i]; i++)
            MyFree(new_modules->autoload[i]);
        for(i = 0; new_modules->optload[i]; i++)
            MyFree(new_modules->optload[i]);
        MyFree(new_modules->module_path);
        MyFree(new_modules);
        new_modules = NULL;
    }
    return;
}

/*
 * rehash
 * 
 * Actual REHASH service routine. Called with sig == 0 if it has been
 * called as a result of an operator issuing this command, else assume
 * it has been called as a result of the server receiving a HUP signal.
 */
int rehash(aClient *cptr, aClient *sptr, int sig)
{
    aClient    *acptr;
    int         i;
    char       *conferr;

    if (sig == SIGHUP) 
    {
        sendto_ops("Got signal SIGHUP, reloading ircd conf. file");
        remove_userbans_match_flags(UBAN_NETWORK, 0);
        /* remove all but kill {} blocks from conf */
        remove_userbans_match_flags(UBAN_LOCAL, UBAN_CONF);
        remove_simbans_match_flags(SBAN_NICK|SBAN_LOCAL|SBAN_TEMPORARY, 0);
        remove_simbans_match_flags(SBAN_CHAN|SBAN_LOCAL|SBAN_TEMPORARY, 0);
        remove_simbans_match_flags(SBAN_GCOS|SBAN_LOCAL|SBAN_TEMPORARY, 0);

    }

    /* Shadowfax's LOCKFILE code */
#ifdef LOCKFILE
    do_pending_klines();
#endif

    for (i = 0; i <= highest_fd; i++)
        if ((acptr = local[i]) && !IsMe(acptr)) 
        {
            /*
             * Nullify any references from client structures to this host
             * structure which is about to be freed. Could always keep
             * reference counts instead of this....-avalon
             */
            acptr->hostp = NULL;
        }

    if (sig != SIGINT)
        flush_cache();      /* Flush DNS cache */

     /* remove kill {} blocks */
    remove_userbans_match_flags(UBAN_LOCAL|UBAN_CONF, 0);
    remove_simbans_match_flags(SBAN_NICK|SBAN_LOCAL, SBAN_TEMPORARY);
    remove_simbans_match_flags(SBAN_CHAN|SBAN_LOCAL, SBAN_TEMPORARY);
    remove_simbans_match_flags(SBAN_GCOS|SBAN_LOCAL, SBAN_TEMPORARY);


    initclass();
    new_confopts = 0;
#ifdef TOYS
    init_chef(); /* For now */
#endif
    if(initconf(configfile) == -1)
    {
        sendto_realops("Rehash Aborted");
        clear_newconfs();
        return 1;
    }

    conferr = finishconf();
    if (conferr)
    {
        sendto_realops("Rehash Aborted: %s", conferr);
        clear_newconfs();
        return 1;
    }
    
    merge_confs();
    build_rplcache();
	nextconnect = 1;    /* reset autoconnects */

    /* replay journal if necessary */
    klinestore_init( (sig == SIGHUP) ? 0 : 1 );

    rehashed = 1;

    return 1;
}

/*
 * lookup_confhost Do (start) DNS lookups of all hostnames in the conf
 * line and convert an IP addresses in a.b.c.d number for to IP#s.
 * 
 * cleaned up Aug 3'97 - Dianora
 * rewritten to kill aConfItem, Feb/04 - epi
 */
static int lookup_confhost(aConnect *aconn)
{
    char   *s;
    struct hostent *hp;
    Link        ln;
    if (BadPtr(aconn->host) || BadPtr(aconn->name)) 
    {
    if (aconn->ipnum.s_addr == -1)
        memset((char *) &aconn->ipnum, '\0', sizeof(struct in_addr));

    Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
           aconn->host, aconn->name));
    return -1;
    }
    if ((s = strchr(aconn->host, '@')))
    s++;
    else
    s = aconn->host;
    /*
     * Do name lookup now on hostnames given and store the ip
     * numbers in conf structure.
     */
    if (!IsAlpha(*s) && !IsDigit(*s)) 
    {
    if (aconn->ipnum.s_addr == -1)
        memset((char *) &aconn->ipnum, '\0', sizeof(struct in_addr));

    Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
           aconn->host, aconn->name));
    return -1;
    }
    /*
     * Prepare structure in case we have to wait for a reply which
     * we get later and store away.
     */
    ln.value.aconn = aconn;
    ln.flags = ASYNC_CONF;
    
    if (IsDigit(*s))
    aconn->ipnum.s_addr = inet_addr(s);
    else if ((hp = gethost_byname(s, &ln)))
    memcpy((char *) &(aconn->ipnum), hp->h_addr,
           sizeof(struct in_addr));

    if (aconn->ipnum.s_addr == -1)
    memset((char *) &aconn->ipnum, '\0', sizeof(struct in_addr));
    {
    Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
           aconn->host, aconn->name));
    return -1;
    }
    /* NOTREACHED */
    return 0;
}

u_long
memcount_s_conf(MCs_conf *mc)
{
    aConnect    *conn;
    aAllow      *allow;
    aOper       *oper;
    aPort       *port;
    aClass      *class;
    int          i;

    mc->file = __FILE__;

    for (conn = connects; conn; conn = conn->next)
    {
        mc->connects.c++;
        mc->connects.m += sizeof(*conn);
        if (conn->host)
            mc->connects.m += strlen(conn->host) + 1;
        if (conn->apasswd)
            mc->connects.m += strlen(conn->apasswd) + 1;
        if (conn->cpasswd)
            mc->connects.m += strlen(conn->cpasswd) + 1;
        if (conn->name)
            mc->connects.m += strlen(conn->name) + 1;
        if (conn->source)
            mc->connects.m += strlen(conn->source) + 1;
        if (conn->class_name)
            mc->connects.m += strlen(conn->class_name) + 1;
    }
    mc->total.c += mc->connects.c;
    mc->total.m += mc->connects.m;

    for (allow = allows; allow; allow = allow->next)
    {
        mc->allows.c++;
        mc->allows.m += sizeof(*allow);
        if (allow->ipmask)
            mc->allows.m += strlen(allow->ipmask) + 1;
        if (allow->passwd)
            mc->allows.m += strlen(allow->passwd) + 1;
        if (allow->hostmask)
            mc->allows.m += strlen(allow->hostmask) + 1;
        if (allow->class_name)
            mc->allows.m += strlen(allow->class_name) + 1;
    }
    mc->total.c += mc->allows.c;
    mc->total.m += mc->allows.m;

    for (oper = opers; oper; oper = oper->next)
    {
        mc->opers.c++;
        mc->opers.m += sizeof(*oper);
        if (oper->passwd)
            mc->opers.m += strlen(oper->passwd) + 1;
        if (oper->nick)
            mc->opers.m += strlen(oper->nick) + 1;
        if (oper->class_name)
            mc->opers.m += strlen(oper->class_name) + 1;
        for (i = 0; oper->hosts[i]; i++)
            mc->opers.m += strlen(oper->hosts[i]) + 1;
    }
    mc->total.c += mc->opers.c;
    mc->total.m += mc->opers.m;

    for (port = ports; port; port = port->next)
    {
        mc->ports.c++;
        mc->ports.m += sizeof(*port);
        if (port->allow)
            mc->ports.m += strlen(port->allow) + 1;
        if (port->address)
            mc->ports.m += strlen(port->address) + 1;
    }
    mc->total.c += mc->ports.c;
    mc->total.m += mc->ports.m;

    for (class = classes; class; class = class->next)
    {
        mc->classes.c++;
        mc->classes.m += sizeof(*class);
        if (class->name)
            mc->classes.m += strlen(class->name) + 1;
    }
    mc->total.c += mc->classes.c;
    mc->total.m += mc->classes.m;

    for (i = 0; uservers[i]; i++)
    {
        mc->uservers.c++;
        mc->uservers.m += strlen(uservers[i]) + 1;
    }
    mc->total.c += mc->uservers.c;
    mc->total.m += mc->uservers.m;

    if (modules)
    {
        mc->modules.c = 1;
        mc->modules.m = sizeof(*modules);
        if (modules->module_path)
            mc->modules.m += strlen(modules->module_path) + 1;
        for (i = 0; modules->autoload[i]; i++)
            mc->modules.m += strlen(modules->autoload[i]) + 1;
        for (i = 0; modules->optload[i]; i++)
            mc->modules.m += strlen(modules->optload[i]) + 1;
    }
    mc->total.c += mc->modules.c;
    mc->total.m += mc->modules.m;

    if (MeLine)
    {
        mc->me.c = 1;
        mc->me.m += sizeof(*MeLine);
        if (MeLine->servername)
            mc->me.m += strlen(MeLine->servername) + 1;
        if (MeLine->info)
            mc->me.m += strlen(MeLine->info) + 1;
        if (MeLine->diepass)
            mc->me.m += strlen(MeLine->diepass) + 1;
        if (MeLine->restartpass)
            mc->me.m += strlen(MeLine->restartpass) + 1;
        if (MeLine->admin[0])
            mc->me.m += strlen(MeLine->admin[0]) + 1;
        if (MeLine->admin[1])
            mc->me.m += strlen(MeLine->admin[1]) + 1;
        if (MeLine->admin[2])
            mc->me.m += strlen(MeLine->admin[2]) + 1;
    }
    mc->total.c += mc->me.c;
    mc->total.m += mc->me.m;

    return mc->total.m;
	}


syntax highlighted by Code2HTML, v. 0.9.1