/************************************************************************
 *   IRC - Internet Relay Chat, src/s_serv.c
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *                      University of Oulu, Computing Center
 *
 *   See file AUTHORS in IRC package for additional names of
 *   the programmers.
 *
 *   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_serv.c,v 1.19 2006/06/25 22:53:01 sheik Exp $ */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include "nameser.h"
#include "resolv.h"
#include "dh.h"
#include "zlink.h"
#include "userban.h"

#if defined(AIX) || defined(SVR3)
#include <time.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <utmp.h>
#include "h.h"
#if defined( HAVE_STRING_H )
#include <string.h>
#else
/* older unices don't have strchr/strrchr .. help them out */
#include <strings.h>
#undef strchr
#define strchr index
#endif
#include "fdlist.h"
#include "throttle.h"
#include "clones.h"
#include "memcount.h"

static char buf[BUFSIZE];
extern int  rehashed;
extern int  forked;

/* external variables */

/* external functions */

extern char *smalldate(time_t); /* defined in s_misc.c */
extern void outofmemory(void);  /* defined in list.c */
extern void s_die(void);
extern int  match(char *, char *);      /* defined in match.c */

/* Local function prototypes */

#if 0
static int  isnumber(char *);   /* return 0 if not, else return number */
static char *cluster(char *);
#endif

int         send_motd(aClient *, aClient *, int, char **);
void        read_motd(char *);
void        read_shortmotd(char *);

char        motd_last_changed_date[MAX_DATE_STRING]; /* enough room for date */ 

void fakeserver_list(aClient *);
int fakelinkscontrol(int, char **);
void fakelinkserver_update(char *, char *);
void fakeserver_sendserver(aClient *);

int is_luserslocked();
void send_fake_users(aClient *);
void send_fake_lusers(aClient *);
void fakelusers_sendlock(aClient *);

#ifdef LOCKFILE
/* Shadowfax's lockfile code */
void        do_pending_klines(void);
void        do_pending_glines(void);

struct pkl
{
    char       *comment;        /* Kline Comment */
    char       *kline;          /* Actual Kline */
    struct pkl *next;           /* Next Pending Kline */
} *pending_klines = NULL;

time_t      pending_kline_time = 0;


struct pgl
{
    char       *comment;        /* Kline Comment */
    char       *gline;          /* Actual Kline */
    struct pgl *next;           /* Next Pending Kline */
} *pending_glines = NULL;

gtime_t      pending_gline_time = 0;

#endif /* LOCKFILE */

/*
 * m_functions execute protocol messages on this server: *
 * 
 * cptr: 
 ** always NON-NULL, pointing to a *LOCAL* client
 ** structure (with an open socket connected!). This 
 ** is the physical socket where the message originated (or
 ** which caused the m_function to be executed--some
 ** m_functions may call others...). 
 * 
 * sptr:
 ** the source of the message, defined by the
 ** prefix part of the message if present. If not or
 ** prefix not found, then sptr==cptr. 
 * 
 *      *Always* true (if 'parse' and others are working correct): 
 * 
 *      1)      sptr->from == cptr  (note: cptr->from == cptr) 
 * 
 *      2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr 
 * cannot be a local connection, unless it's actually cptr!). 
 *
 * MyConnect(x) should probably  be defined as (x == x->from) --msa
 * 
 * parc:    
 ** number of variable parameter strings (if zero, 
 ** parv is allowed to be NULL)
 * 
 * parv:    
 ** a NULL terminated list of parameter pointers,
 *** parv[0], sender (prefix string), if not present his points to 
 *** an empty string.
 *
 ** [parc-1]:
 *** pointers to additional parameters 
 *** parv[parc] == NULL, *always* 
 * 
 * note:   it is guaranteed that parv[0]..parv[parc-1] are all
 *         non-NULL pointers.
 */

/*
 * * m_version 
 *      parv[0] = sender prefix 
 *      parv[1] = remote server
 */
int 
m_version(aClient *cptr, aClient *sptr, int parc, char *parv[])
{

    if (hunt_server(cptr, sptr, ":%s VERSION :%s", 1, parc, parv) ==
        HUNTED_ISME)
        send_rplversion(sptr);

    return 0;
}

/*
 * m_squit
 * there are two types of squits: those going downstream (to the target server)
 * and those going back upstream (from the target server).
 * previously, it wasn't necessary to distinguish between these two types of 
 * squits because they neatly echoed back all of the QUIT messages during
 * an squit.  This, however, is no longer practical.
 * 
 * To clarify here, DOWNSTREAM signifies an SQUIT heading towards the target
 * server UPSTREAM signifies an SQUIT which has successfully completed,
 * heading out everywhere.
 *
 * acptr is the server being squitted.
 * a DOWNSTREAM squit is where the notice did not come from acptr->from.
 * an UPSTREAM squit is where the notice DID come from acptr->from.
 *
 *        parv[0] = sender prefix 
 *        parv[1] = server name 
 *        parv[2] = comment
 */
int 
m_squit(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aConnect *aconn;
    char *server;
    aClient *acptr;
    char *comment = (parc > 2) ? parv[2] : sptr->name;

    if (!IsPrivileged(sptr)) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }

    if (parc > 1) 
    {
        server = parv[1];
        /* To accomodate host masking, a squit for a masked server
         * name is expanded if the incoming mask is the same as the
         * server name for that link to the name of link.
         */
        while ((*server == '*') && IsServer(cptr))
        {
            aconn = cptr->serv->aconn;
            if (!aconn)
                break;
            if (!mycmp(server, my_name_for_link(me.name, aconn)))
                server = cptr->name;
            break;                      /* WARNING is normal here */
            /* NOTREACHED */
        }
        /*
         * The following allows wild cards in SQUIT. Only useful when
         * the command is issued by an oper.
         */
        for (acptr = client; (acptr = next_client(acptr, server)); 
             acptr = acptr->next)
            if (IsServer(acptr) || IsMe(acptr))
                break;
        if (acptr && IsMe(acptr)) 
        {
            acptr = cptr;
            server = cptr->sockhost;
        }
    }
    else
    {
        /* This is actually protocol error. But, well, closing the
         * link is very proper answer to that...
         */
        server = cptr->sockhost;
        acptr = cptr;
    }

    if (!acptr) 
    {
        sendto_one(sptr, err_str(ERR_NOSUCHSERVER),
                   me.name, parv[0], server);
        return 0;
    }

    if (MyClient(sptr) && ((!OPCanGRoute(sptr) && !MyConnect(acptr)) || 
                           (!OPCanLRoute(sptr) && MyConnect(acptr)))) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }

    /* If the server is mine, we don't care about upstream or downstream,
       just kill it and do the notice. */
    
    if (MyConnect(acptr)) 
    {
        sendto_gnotice("from %s: Received SQUIT %s from %s (%s)",
                       me.name, acptr->name, get_client_name(sptr, HIDEME),
                       comment);
        sendto_serv_butone(NULL, ":%s GNOTICE :Received SQUIT %s from %s (%s)",
                           me.name, server, get_client_name(sptr, HIDEME),
                           comment);
        
#if defined(USE_SYSLOG) && defined(SYSLOG_SQUIT)
        syslog(LOG_DEBUG, "SQUIT From %s : %s (%s)",
               parv[0], server, comment);
#endif
        /* I am originating this squit! Not cptr! */
        /* ack, but if cptr is squitting itself.. */
        if(cptr == sptr)
        {
            exit_client(&me, acptr, sptr, comment);
            return FLUSH_BUFFER; /* kludge */
        }
        return exit_client(&me, acptr, sptr, comment);
    }
    
    /* the server is not connected to me. Determine whether this is an upstream
       or downstream squit */
    
    if(sptr->from == acptr->from) /* upstream */
    {
        sendto_realops_lev(DEBUG_LEV,
                           "Exiting server %s due to upstream squit by %s [%s]",
                           acptr->name, sptr->name, comment);
        return exit_client(cptr, acptr, sptr, comment);
    }

    /* fallthrough: downstream */

    if(!(IsUnconnect(acptr->from))) /* downstream not unconnect capable */
    {
        sendto_realops_lev(DEBUG_LEV,
                    "Exiting server %s due to non-unconnect server %s [%s]",
                    acptr->name, acptr->from->name, comment);
        return exit_client(&me, acptr, sptr, comment);
    }

    
    sendto_realops_lev(DEBUG_LEV, "Passing along SQUIT for %s by %s [%s]",
                       acptr->name, sptr->name, comment);
    sendto_one(acptr->from, ":%s SQUIT %s :%s", parv[0], acptr->name, comment);

    return 0;
}

/*
 * m_svinfo 
 *       parv[0] = sender prefix 
 *       parv[1] = TS_CURRENT for the server 
 *       parv[2] = TS_MIN for the server 
 *       parv[3] = server is standalone or connected to non-TS only 
 *       parv[4] = server's idea of UTC time
 */
int m_svinfo(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    time_t      deltat, tmptime, theirtime;
    
    if (!IsServer(sptr) || !MyConnect(sptr))
        return 0;

    if(parc == 2 && mycmp(parv[1], "ZIP") == 0)
    {
        SetZipIn(sptr);
        sptr->serv->zip_in = zip_create_input_session();
        sendto_gnotice("from %s: Input from %s is now compressed",
                       me.name, get_client_name(sptr, HIDEME));
        sendto_serv_butone(sptr,
                           ":%s GNOTICE :Input from %s is now compressed",
                           me.name, get_client_name(sptr, HIDEME));
        return ZIP_NEXT_BUFFER;
    }
    
    if(parc < 5 || !DoesTS(sptr))
        return 0;
    
    if (TS_CURRENT < atoi(parv[2]) || atoi(parv[1]) < TS_MIN) 
    {
        /* a server with the wrong TS version connected; since we're
         * TS_ONLY we can't fall back to the non-TS protocol so we drop
         * the link  -orabidoo
         */
        sendto_ops("Link %s dropped, wrong TS protocol version (%s,%s)",
                   get_client_name(sptr, HIDEME), parv[1], parv[2]);
        return exit_client(sptr, sptr, sptr, "Incompatible TS version");
    }
    
    tmptime = time(NULL);
    theirtime = atol(parv[4]);
    deltat = abs(theirtime - tmptime);
    
    if (deltat > tsmaxdelta) 
    {
        sendto_gnotice("from %s: Link %s dropped, excessive TS delta (my "
                       "TS=%d, their TS=%d, delta=%d)",
                       me.name, get_client_name(sptr, HIDEME), tmptime,
                       theirtime, deltat);
        sendto_serv_butone(sptr, ":%s GNOTICE :Link %s dropped, excessive "
                           "TS delta (delta=%d)",
                           me.name, get_client_name(sptr, HIDEME), deltat);
        return exit_client(sptr, sptr, sptr, "Excessive TS delta");
    }

    if (deltat > tswarndelta) 
    {
        sendto_realops("Link %s notable TS delta (my TS=%d, their TS=%d, "
                       "delta=%d)", get_client_name(sptr, HIDEME), tmptime,
                       theirtime, deltat);
    }

    return 0;
}

/* 
 * m_burst
 *      parv[0] = sender prefix
 *      parv[1] = SendQ if an EOB
 */
int 
m_burst(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
{
  
    if (!IsServer(sptr) || sptr != cptr || parc > 2 || !IsBurst(sptr))
        return 0;
    if (parc == 2) { /* This is an EOB */
        sptr->flags &= ~(FLAGS_EOBRECV);
        if (sptr->flags & (FLAGS_SOBSENT|FLAGS_BURST)) return 0;
        
        /* we've already sent our EOB.. we synched first
         * no need to check IsBurst because we shouldn't receive a BURST if 
         * they're not BURST capab
         */
        
        sendto_gnotice("from %s: synch to %s in %d %s at %s sendq", me.name,
                       *parv, (timeofday-sptr->firsttime), 
                       (timeofday-sptr->firsttime)==1?"sec":"secs", parv[1]);
        sendto_serv_butone(NULL,
                           ":%s GNOTICE :synch to %s in %d %s at %s sendq",
                           me.name, sptr->name, (timeofday-sptr->firsttime),
                           (timeofday-sptr->firsttime)==1?"sec":"secs",
                           parv[1]);
        
    }
    else
    {
        sptr->flags |= FLAGS_EOBRECV;
    }
    return 0;
}

/*
 * * m_info 
 *      parv[0] = sender prefix 
 *      parv[1] = servername
 */

int 
m_info(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
   char outstr[241];

    static time_t last_used = 0L;
    if (hunt_server(cptr,sptr,":%s INFO :%s",1,parc,parv) == HUNTED_ISME) 
    {
        sendto_realops_lev(SPY_LEV, "INFO requested by %s (%s@%s) [%s]",
                           sptr->name, sptr->user->username, sptr->user->host,
                           sptr->user->server);
                        
        if (!IsAnOper(sptr)) 
        {
            if (IsSquelch(sptr)) {
                sendto_one(sptr, rpl_str(RPL_ENDOFINFO), me.name, parv[0]);
                return 0;
            }
            if (!MyConnect(sptr))
                return 0;
            if ((last_used + MOTD_WAIT) > NOW) 
                return 0;
            else 
                last_used = NOW;
        }
        
		ircsprintf (outstr, " ");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr,
                " \2+-----------------+ solid-ircd +------------------+\2");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, " ");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, " Welcome to %s", Network_Name,"IRC Network");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, "Official Site:       \2\2 %s", WEBSITE);
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, " Acceptable Use Policy:     \2\2 %s", AUP);
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, "Official Help channel:   \2\2 %s", HELPCHAN);
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, " ");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr,
                " \2+---------------------------------------------------+\2");

    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, " ");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, " %s is running %s", me.name, version);
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, " Build #%s, compiled on %s", generation, creation);
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, " Server been up since %s", myctime (me.firsttime));
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, " ");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr,
                " \2+--------------------------------------------------+\2");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, " ");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr,
                " \2/SOLIDINFO\2               To view the solid-ircd credits");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr,
                " \2/DALINFO\2             To view the dalnet credits ;)");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr, " ");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);

    ircsprintf (outstr,
                " \2+--------------------------------------------------+\2");
    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], outstr);
        
        sendto_one(sptr, rpl_str(RPL_ENDOFINFO), me.name, parv[0]);
    }
 
    return 0;
}


/*
 * * m_dalinfo 
 *      parv[0] = sender prefix 
 *      parv[1] = servername
 */

int 
m_dalinfo(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char      **text = daltext;

    static time_t last_used = 0L;
    if (hunt_server(cptr,sptr,":%s DALINFO :%s",1,parc,parv) == HUNTED_ISME) 
    {
        sendto_realops_lev(SPY_LEV, "DALINFO requested by %s (%s@%s) [%s]",
                           sptr->name, sptr->user->username, sptr->user->host,
                           sptr->user->server);
                        
        if (!IsAnOper(sptr)) 
        {
            if (IsSquelch(sptr)) {
                sendto_one(sptr, rpl_str(RPL_ENDOFINFO), me.name, parv[0]);
                return 0;
            }
            if (!MyConnect(sptr))
                return 0;
            if ((last_used + MOTD_WAIT) > NOW) 
                return 0;
            else 
                last_used = NOW;
        }
        while (*text)
            sendto_one(sptr, rpl_str(RPL_INFO),
                       me.name, parv[0], *text++);
                        
        sendto_one(sptr, rpl_str(RPL_INFO), me.name, parv[0], "");

        /* I am -definately- going to come up with a replacement for this! */
        /* you didnt, so i removed it.. kinda stupid anyway.  -epi */
        
        sendto_one(sptr,
                   ":%s %d %s :Birth Date: %s, compile #%s",
                   me.name, RPL_INFO, parv[0], creation, generation);
        sendto_one(sptr, ":%s %d %s :On-line since %s",
                   me.name, RPL_INFO, parv[0],
                   myctime(me.firsttime));
        sendto_one(sptr, rpl_str(RPL_ENDOFINFO), me.name, parv[0]);
    }
 
    return 0;
}



/*
 * * m_solidinfo 
 *      parv[0] = sender prefix 
 *      parv[1] = servername
 */

int 
m_solidinfo(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char      **text = solidtext;

    static time_t last_used = 0L;
    if (hunt_server(cptr,sptr,":%s SOLIDINFO :%s",1,parc,parv) == HUNTED_ISME) 
    {
        sendto_realops_lev(SPY_LEV, "SOLIDINFO requested by %s (%s@%s) [%s]",
                           sptr->name, sptr->user->username, sptr->user->host,
                           sptr->user->server);
                        
        if (!IsAnOper(sptr)) 
        {
            if (IsSquelch(sptr)) {
                sendto_one(sptr, rpl_str(RPL_ENDOFINFO), me.name, parv[0]);
                return 0;
            }
            if (!MyConnect(sptr))
                return 0;
            if ((last_used + MOTD_WAIT) > NOW) 
                return 0;
            else 
                last_used = NOW;
        }
        while (*text)
            sendto_one(sptr, rpl_str(RPL_INFO),
                       me.name, parv[0], *text++);
                        
        sendto_one(sptr, rpl_str(RPL_INFO), me.name, parv[0], "");

        /* I am -definately- going to come up with a replacement for this! */
        /* you didnt, so i removed it.. kinda stupid anyway.  -epi */
        
        sendto_one(sptr,
                   ":%s %d %s :Birth Date: %s, compile #%s",
                   me.name, RPL_INFO, parv[0], creation, generation);
        sendto_one(sptr, ":%s %d %s :On-line since %s",
                   me.name, RPL_INFO, parv[0],
                   myctime(me.firsttime));
        sendto_one(sptr, rpl_str(RPL_ENDOFINFO), me.name, parv[0]);
    }
 
    return 0;
}

/*
 * * m_links 
 *      parv[0] = sender prefix 
 *      parv[1] = servername mask 
 * or 
 *      parv[0] = sender prefix 
 *      parv[1] = server to query 
 *      parv[2] = servername mask
 */
int 
m_links(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char       *mask;
    aClient    *acptr;
    char        clean_mask[(2 * HOSTLEN) + 1];
    char       *s;
    char       *d;
    int         n;

    /* reject non-local requests */
    if (IsServer(sptr) || (!IsAnOper(sptr) && !MyConnect(sptr)))
        return 0;

    mask = (parc < 2) ? NULL : parv[1];
    
    /*
     * * sigh* Before the kiddies find this new and exciting way of
     * * annoying opers, lets clean up what is sent to all opers
     * * -Dianora
     */

    if (mask) 
    {      /* only necessary if there is a mask */
        s = mask;
        d = clean_mask;
        n = (2 * HOSTLEN) - 2;
        while (*s && n > 0) 
        {
            /* Is it a control character? */
            if ((unsigned char) *s < (unsigned char) ' ') 
            {
                *d++ = '^';
                /* turn it into a printable */
                *d++ = (char) ((unsigned char)*s + 0x40); 
                s++;
                n -= 2;
            }
            else if ((unsigned char) *s > (unsigned char) '~') 
            {
                *d++ = '.';
                s++;
                n--;
            }
            else 
            {
                *d++ = *s++;
                n--;
            }
        }
        *d = '\0';
    }

    if (MyConnect(sptr))
        sendto_realops_lev(SPY_LEV,
                           "LINKS %s requested by %s (%s@%s) [%s]",
                           mask ? clean_mask : "all",
                           sptr->name, sptr->user->username,
                           sptr->user->host, sptr->user->server);

    if(!(confopts & FLAGS_SHOWLINKS) && !IsAnOper(sptr))
        fakeserver_list(sptr);
    else
    for (acptr = client, (void) collapse(mask); acptr; acptr = acptr->next) 
    {
        if (!IsServer(acptr) && !IsMe(acptr))
            continue;
        if (!BadPtr(mask) && match(mask, acptr->name))
            continue;
#ifdef HIDEULINEDSERVS
        if (!IsOper(sptr) && IsULine(acptr))
            continue;
#endif
        sendto_one(sptr, rpl_str(RPL_LINKS),
                   me.name, parv[0], acptr->name, acptr->serv->up,
                   acptr->hopcount, (acptr->info[0] ? acptr->info :
                                     "(Unknown Location)"));
    }
    sendto_one(sptr, rpl_str(RPL_ENDOFLINKS), me.name, parv[0],
               BadPtr(mask) ? "*" : clean_mask);
    return 0;
}

/*
 * * m_users 
 *        parv[0] = sender prefix 
 *        parv[1] = servername
 */
int 
m_users(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    if (hunt_server(cptr, sptr, ":%s USERS :%s", 1, parc, parv) == HUNTED_ISME) 
    {
        if(is_luserslocked())
        {
            send_fake_users(sptr);
            return 0;
        }
        /* No one uses this any more... so lets remap it..   -Taner */
        sendto_one(sptr, rpl_str(RPL_LOCALUSERS), me.name, parv[0],
                   Count.local, Count.max_loc);
        sendto_one(sptr, rpl_str(RPL_GLOBALUSERS), me.name, parv[0],
                   Count.total, Count.max_tot);
    }
    return 0;
}

/*
 * * Note: At least at protocol level ERROR has only one parameter, 
 * although this is called internally from other functions  --msa 
 *
 *      parv[0] = sender prefix 
 *      parv[*] = parameters
 */
int 
m_error(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char   *para;

    para = (parc > 1 && *parv[1] != '\0') ? parv[1] : "<>";

    Debug((DEBUG_ERROR, "Received ERROR message from %s: %s",
           sptr->name, para));
    /*
     * * Ignore error messages generated by normal user clients 
     * (because ill-behaving user clients would flood opers screen
     * otherwise). Pass ERROR's from other sources to the local
     * operator...
     */
    if (IsPerson(cptr) || IsUnknown(cptr))
        return 0;
    if (cptr == sptr)
        sendto_ops("ERROR :from %s -- %s",
                   get_client_name(cptr, HIDEME), para);
    else
        sendto_ops("ERROR :from %s via %s -- %s", sptr->name,
                   get_client_name(cptr, HIDEME), para);
    return 0;
}

/*
 * m_help
 * parv[0] = sender prefix
 * 
 * Forward help requests to HelpServ if defined, and is invoked
 * by non-opers, otherwise sends opers.txt to opers (if present),
 * or sends a big list of commands to non-opers (and opers if
 * opers.txt is not present). -srd
 */
int 
m_help(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int         i;
    aMotd *helpfile_ptr;

    static time_t last_used = 0L;

#ifdef HELP_FORWARD_HS
    if (!IsAnOper(sptr))
    {
       if (parc < 2 || *parv[1] == '\0')
       {
          sendto_one(sptr, ":%s NOTICE %s :For a list of help topics, type "
                     "/%s %s", me.name, sptr->name, HELPSERV, DEF_HELP_CMD);
          return -1;
       }
        return m_aliased(cptr, sptr, parc, parv, &aliastab[AII_HS]);
 
       return 0;
    }
#endif
    
    if (!IsAnOper(sptr))
    {
       /* reject non local requests */
       if ((last_used + MOTD_WAIT) > NOW)
          return 0;   
       else
          last_used = NOW;
    }

    if (!IsAnOper(sptr) || (helpfile == (aMotd *) NULL))
    {
        for (i = 0; msgtab[i].cmd; i++)
            sendto_one(sptr, ":%s NOTICE %s :%s",
                       me.name, parv[0], msgtab[i].cmd);
        return 0;
    }
       
    helpfile_ptr = helpfile;
    while (helpfile_ptr)
    {
        sendto_one(sptr,
                   ":%s NOTICE %s :%s",
                   me.name, parv[0], helpfile_ptr->line);
        helpfile_ptr = helpfile_ptr->next;
    }
       
    return 0;
}

/*
 * parv[0] = sender parv[1] = host/server mask. 
 * parv[2] = server to query
 * 
 * 199970918 JRL hacked to ignore parv[1] completely and require parc > 3
 * to cause a force
 *
 * Now if parv[1] is anything other than *, it forces a recount.
 *    -Quension [May 2005]
 */
int 
m_lusers(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int send_lusers(aClient *, aClient *, int, char **);

    if (parc > 2) 
    {
        if (hunt_server(cptr, sptr, ":%s LUSERS %s :%s", 2, parc, parv) !=
            HUNTED_ISME)
            return 0;
    }

    if(!IsAnOper(sptr) && is_luserslocked())
    {
       send_fake_lusers(sptr);
       return 0;
    }           

    return send_lusers(cptr,sptr,parc,parv);
}

/*
 * send_lusers
 *     parv[0] = sender
 *     parv[1] = anything but "*" to force a recount
 *     parv[2] = server to query
 */
int send_lusers(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
{
    /* forced recount */
    if (IsAnOper(sptr) && (parc > 1) && (*parv[1] != '*')) 
    {
        int s_count = 0;
        int c_count = 0;
        int u_count = 0;
        int i_count = 0;
        int o_count = 0;
        int m_client = 0;
        int m_server = 0;
        int m_ulined = 0;
        aClient *acptr;
        
        for (acptr = client; acptr; acptr = acptr->next) 
        {
            switch (acptr->status) 
            {
            case STAT_SERVER:
                if (MyConnect(acptr))
                {
                    m_server++;
                    if(IsULine(acptr))
                        m_ulined++;
                }
            case STAT_ME:
                s_count++;
                break;
            case STAT_CLIENT:
                if (IsAnOper(acptr))
                    o_count++;
#ifdef  SHOW_INVISIBLE_LUSERS
                if (MyConnect(acptr))
                    m_client++;
                if (!IsInvisible(acptr))
                    c_count++;
                else
                    i_count++;
#else
                if (MyConnect(acptr)) 
                {
                    if (IsInvisible(acptr)) 
                    {
                        if (IsAnOper(sptr))
                            m_client++;
                    }
                    else
                        m_client++;
                }
                if (!IsInvisible(acptr))
                    c_count++;
                else
                    i_count++;
#endif
                break;
            default:
                u_count++;
                break;
            }
        }

        /* sanity check */
        if (m_server != Count.myserver)
        {
            sendto_realops_lev(DEBUG_LEV, "Local server count off by %d",
                               Count.myserver - m_server);
            Count.myserver = m_server;
        }
        if (m_ulined != Count.myulined)
        {
            sendto_realops_lev(DEBUG_LEV, "Local superserver count off by %d",
                               Count.myulined - m_ulined);
            Count.myulined = m_ulined;
        }
        if (s_count != Count.server)
        {
            sendto_realops_lev(DEBUG_LEV, "Server count off by %d",
                               Count.server - s_count);
            Count.server = s_count;
        }
        if (i_count != Count.invisi)
        {
            sendto_realops_lev(DEBUG_LEV, "Invisible client count off by %d",
                               Count.invisi - i_count);
            Count.invisi = i_count;
        }
        if ((c_count + i_count) != Count.total)
        {
            sendto_realops_lev(DEBUG_LEV, "Total client count off by %d",
                               Count.total - (c_count + i_count));
            Count.total = c_count + i_count;
        }
        if (m_client != Count.local)
        {
            sendto_realops_lev(DEBUG_LEV, "Local client count off by %d",
                               Count.local - m_client);
            Count.local = m_client;
        }
        if (o_count != Count.oper)
        {
            sendto_realops_lev(DEBUG_LEV, "Oper count off by %d",
                               Count.oper - o_count);
            Count.oper = o_count;
        }
        if (u_count != Count.unknown)
        {
            sendto_realops_lev(DEBUG_LEV, "Unknown connection count off by %d",
                               Count.unknown - u_count);
            Count.unknown = u_count;
        }
    }   /* Recount loop */

    /* save stats */
    if ((timeofday - last_stat_save) > 3600)
    {
        FILE *fp;
        char tmp[PATH_MAX];
        
        last_stat_save = timeofday;
        ircsprintf(tmp, "%s/.maxclients", dpath);
        fp = fopen(tmp, "w");
        if (fp != NULL)
        {
            fprintf(fp, "%d %d %li %li %li %ld %ld %ld %ld", Count.max_loc,
                    Count.max_tot, Count.weekly, Count.monthly,
                    Count.yearly, Count.start, Count.week, Count.month,
                    Count.year);
            fclose(fp);
            sendto_realops_lev(DEBUG_LEV, "Saved maxclient statistics");
        }
    }
    
    
#ifndef SHOW_INVISIBLE_LUSERS
    if (IsAnOper(sptr) && Count.invisi)
#endif
        sendto_one(sptr, rpl_str(RPL_LUSERCLIENT), me.name, parv[0],
                   Count.total - Count.invisi, Count.invisi, Count.server);
#ifndef SHOW_INVISIBLE_LUSERS
    else
        sendto_one(sptr,
                   ":%s %d %s :There are %d users on %d servers", me.name,
                   RPL_LUSERCLIENT, parv[0], Count.total - Count.invisi,
                   Count.server);
#endif

    if (Count.oper)
        sendto_one(sptr, rpl_str(RPL_LUSEROP), me.name, parv[0], Count.oper);

    if (IsAnOper(sptr) && Count.unknown)
        sendto_one(sptr, rpl_str(RPL_LUSERUNKNOWN), me.name, parv[0],
                   Count.unknown);
    
    /* This should be ok */
    if (Count.chan > 0)
        sendto_one(sptr, rpl_str(RPL_LUSERCHANNELS),
                   me.name, parv[0], Count.chan);
    sendto_one(sptr, rpl_str(RPL_LUSERME),
#ifdef HIDEULINEDSERVS
               me.name, parv[0], Count.local, 
               IsOper(sptr) ? Count.myserver : Count.myserver - Count.myulined);
#else
               me.name, parv[0], Count.local, Count.myserver);
#endif

    sendto_one(sptr, rpl_str(RPL_LOCALUSERS), me.name, parv[0],
                   Count.local, Count.max_loc);
    sendto_one(sptr, rpl_str(RPL_GLOBALUSERS), me.name, parv[0],
               Count.total, Count.max_tot);
    return 0;
}

/***********************************************************************
 * m_connect() - Added by Jto 11 Feb 1989
 ***********************************************************************/
/*
 * * m_connect 
 *      parv[0] = sender prefix 
 *      parv[1] = servername 
 *      parv[2] = port number 
 *      parv[3] = remote server
 */
int 
m_connect(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int         port, tmpport, retval;
    aConnect   *aconn;
    aClient    *acptr;

    if (!IsPrivileged(sptr))
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return -1;
    }
    
    if ((MyClient(sptr) && !OPCanGRoute(sptr) && parc > 3) ||
        (MyClient(sptr) && !OPCanLRoute(sptr) && parc <= 3))
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }
    
    if (hunt_server(cptr, sptr, ":%s CONNECT %s %s :%s",
                    3, parc, parv) != HUNTED_ISME)
        return 0;

    if (parc < 2 || *parv[1] == '\0') 
    {
        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
                   me.name, parv[0], "CONNECT");
        return -1;
    }

    if ((acptr = find_server(parv[1], NULL)))
    {
        sendto_one(sptr, ":%s NOTICE %s :Connect: Server %s %s %s.",
                   me.name, parv[0], parv[1], "already exists from",
                   acptr->from->name);
        return 0;
    }

    if (!(aconn = find_aConnect(parv[1])))
    {
        sendto_one(sptr, "NOTICE %s :Connect: No Connect block found for %s.",
                   parv[0], parv[1]);
        return 0;
    }
    /*
     * * Get port number from user, if given. If not specified, use
     * the default form configuration structure. If missing from
     * there, then use the precompiled default.
     */
    tmpport = port = aconn->port;
    if (parc > 2 && !BadPtr(parv[2])) 
    {
        if ((port = atoi(parv[2])) <= 0) 
        {
            sendto_one(sptr,
                       "NOTICE %s :Connect: Illegal port number",
                       parv[0]);
            return 0;
        }
    }
    else if (port <= 0 && (port = PORTNUM) <= 0) 
    {
        sendto_one(sptr, ":%s NOTICE %s :Connect: missing port number",
                   me.name, parv[0]);
        return 0;
    }
    /*
     * * Notify all operators about remote connect requests
     * Let's notify about local connects, too. - lucas
     * sendto_ops_butone -> sendto_serv_butone(), like in df. -mjs
     */
    sendto_gnotice("from %s: %s CONNECT %s %s from %s",
                   me.name,  IsAnOper(cptr) ? "Local" : "Remote", 
                   parv[1], parv[2] ? parv[2] : "",
                   sptr->name);
    sendto_serv_butone(NULL, ":%s GNOTICE :%s CONNECT %s %s from %s", 
                       me.name, IsAnOper(cptr) ? "Local" : "Remote",
                       parv[1], parv[2] ? parv[2] : "",
                       sptr->name);

#if defined(USE_SYSLOG) && defined(SYSLOG_CONNECT)
    syslog(LOG_DEBUG, "CONNECT From %s : %s %s", parv[0], parv[1], 
           parv[2] ? parv[2] : "");
#endif
    
    aconn->port = port;
    switch (retval = connect_server(aconn, sptr, NULL))
    {
        case 0:
            sendto_one(sptr, ":%s NOTICE %s :*** Connecting to %s.",
                       me.name, parv[0], aconn->name);
            break;
        case -1:
            sendto_one(sptr, ":%s NOTICE %s :*** Couldn't connect to %s.",
                       me.name, parv[0], aconn->name);
            break;
        case -2:
            sendto_one(sptr, ":%s NOTICE %s :*** Host %s is unknown.",
                       me.name, parv[0], aconn->name);
            break;
        default:
            sendto_one(sptr, ":%s NOTICE %s :*** Connection to %s failed: %s",
                       me.name, parv[0], aconn->name, strerror(retval));
    }
    aconn->port = tmpport;
    return 0;
}

/*
 * * m_wallops (write to *all* opers currently online) 
 *      parv[0] = sender prefix 
 *      parv[1] = message text
 */
int 
m_wallops(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char       *message = parc > 1 ? parv[1] : NULL;
    
    if (BadPtr(message)) 
    {
        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
                   me.name, parv[0], "WALLOPS");
        return 0;
    }
    
    if (!IsServer(sptr) && MyConnect(sptr) && !OPCanWallOps(sptr)) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return (0);
    }
    
    sendto_wallops_butone(IsServer(cptr) ? cptr : NULL, sptr,
                          ":%s WALLOPS :%s", parv[0], message);
    return 0;
}

/*
 * * m_locops (write to *all* local opers currently online) 
 *      parv[0] = sender prefix 
 *      parv[1] = message text
 */
int 
m_locops(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char       *message = parc > 1 ? parv[1] : NULL;

    if (BadPtr(message)) 
    {
        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
                   me.name, parv[0], "LOCOPS");
        return 0;
    }

    if (!IsServer(sptr) && MyConnect(sptr) && !OPCanLocOps(sptr)) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return (0);
    }
    sendto_locops("from %s: %s", parv[0], message);
    return (0);
}

/*
 * m_goper  (Russell) sort of like wallop, but only to ALL +o clients
 * on every server. 
 *      parv[0] = sender prefix 
 *      parv[1] = message text 
 * Taken from df465, ported to hybrid. -mjs
 */
int 
m_goper(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char       *message = parc > 1 ? parv[1] : NULL;

    if (BadPtr(message)) 
    {
        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
                   me.name, parv[0], "GOPER");
        return 0;
    }
    if (!IsServer(sptr) || !IsULine(sptr)) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }

    sendto_serv_butone_super(cptr, 0, ":%s GOPER :%s", parv[0], message);
    sendto_ops("from %s: %s", parv[0], message);
    return 0;
}

/*
 * m_gnotice  (Russell) sort of like wallop, but only to +g clients on *
 * this server. 
 *      parv[0] = sender prefix 
 *      parv[1] = message text 
 * ported from df465 to hybrid -mjs
 *
 * This function itself doesnt need any changes for the move to +n routing
 * notices, to sendto takes care of it.  Now only sends to +n clients -epi
 */
int 
m_gnotice(aClient *cptr, aClient *sptr, int parc, char *parv[])
{

    char       *message = parc > 1 ? parv[1] : NULL;
    
    if (BadPtr(message)) 
    {
        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
                   me.name, parv[0], "GNOTICE");
        return 0;
    }
    if (!IsServer(sptr) && MyConnect(sptr)) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }

    sendto_serv_butone_super(cptr, 0, ":%s GNOTICE :%s", parv[0], message);
    sendto_gnotice("from %s: %s", parv[0], message);
    return 0;
}

int 
m_globops(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char       *message = parc > 1 ? parv[1] : NULL;

    /* a few changes, servers weren't able to globop -mjs */

    if (BadPtr(message)) 
    {
        if (MyClient(sptr))
            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
                       me.name, parv[0], "GLOBOPS");
        return 0;
    }

    if (MyClient(sptr) && !OPCanGlobOps(sptr)) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }
    if (strlen(message) > TOPICLEN)
        message[TOPICLEN] = '\0';
    sendto_serv_butone_super(cptr, 0, ":%s GLOBOPS :%s", parv[0], message);
    send_globops("from %s: %s", parv[0], message);
    return 0;
}

int 
m_chatops(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char       *message = parc > 1 ? parv[1] : NULL;

    if (BadPtr(message)) 
    {
        if (MyClient(sptr))
            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
                       me.name, parv[0], "CHATOPS");
        return 0;
    }

    if (MyClient(sptr) && (!IsAnOper(sptr) || !SendChatops(sptr))) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }
    
    if (strlen(message) > TOPICLEN)
        message[TOPICLEN] = '\0';
    sendto_serv_butone_super(cptr, 0, ":%s CHATOPS :%s", parv[0], message);
    send_chatops("from %s: %s", parv[0], message);
    return 0;
}


int m_conops(aClient *cptr, aClient *sptr, int parc, char *parv[])
{

    char       *message = parc > 1 ? parv[1] : NULL;
    
    if (check_registered(sptr))
        return 0;

    if (BadPtr(message)) 
    {
        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "CONOPS");
        return 0;
    }
    if (!IsServer(sptr) && MyConnect(sptr)) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }

    sendto_serv_butone(IsServer(cptr) ? cptr : NULL, ":%s CONOPS :%s",
                       parv[0], message);
    sendto_conops("from %s: %s", parv[0], message);
    return 0;
}

/*
 * * m_time 
 *       parv[0] = sender prefix 
 *       parv[1] = servername
 */

int 
m_time(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    if (hunt_server(cptr, sptr, ":%s TIME :%s", 1, parc, parv) == HUNTED_ISME)
        sendto_one(sptr, rpl_str(RPL_TIME), me.name,
                   parv[0], me.name, date((long) 0));
    return 0;
}

/*
 * * m_admin 
 *        parv[0] = sender prefix 
 *        parv[1] = servername
 */
int 
m_admin(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    
    if (hunt_server(cptr, sptr, ":%s ADMIN :%s", 1, parc, parv) != HUNTED_ISME)
        return 0;
    
    if (IsPerson(sptr))
        sendto_realops_lev(SPY_LEV, "ADMIN requested by %s (%s@%s) [%s]",
                           sptr->name, sptr->user->username, sptr->user->host,
                           sptr->user->server);
    
        sendto_one(sptr, rpl_str(RPL_ADMINME),
                   me.name, parv[0], me.name);
        sendto_one(sptr, rpl_str(RPL_ADMINLOC1),
                   me.name, parv[0], MeLine->admin[0] ? MeLine->admin[0] : "");
        sendto_one(sptr, rpl_str(RPL_ADMINLOC2),
                   me.name, parv[0], MeLine->admin[1] ? MeLine->admin[1] : "");
        sendto_one(sptr, rpl_str(RPL_ADMINEMAIL),
                   me.name, parv[0], MeLine->admin[2] ? MeLine->admin[2] : "");
    return 0;
}

/* Shadowfax's server side, anti flood code */
#ifdef FLUD
extern int  flud_num;
extern int  flud_time;
extern int  flud_block;
#endif

#ifdef ANTI_SPAMBOT
extern int  spam_num;
extern int  spam_time;
#endif

/* m_set - set options while running */
int 
m_set(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char       *command;

    if (!MyClient(sptr) || !IsOper(sptr)) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }

    if (parc > 1) 
    {
        command = parv[1];
        if (!strncasecmp(command, "MAX", 3)) 
        {
            if (parc > 2) 
            {
                int new_value = atoi(parv[2]);

                if (new_value > MAX_ACTIVECONN) 
                {
                    sendto_one(sptr,
                               ":%s NOTICE %s :You cannot set MAXCLIENTS "
                               "above the compiled FD limit (%d)", me.name,
                               parv[0], MAX_ACTIVECONN);
                    return 0;
                }
                if (new_value < 0)
                    new_value = 0;
                MAXCLIENTS = new_value;
                sendto_one(sptr, ":%s NOTICE %s :NEW MAXCLIENTS = %d (Current "
                           "= %d)", me.name, parv[0], MAXCLIENTS, Count.local);
                sendto_realops("%s!%s@%s set new MAXCLIENTS to %d "
                               "(%d current)", parv[0], sptr->user->username,
                               sptr->sockhost, MAXCLIENTS, Count.local);
                return 0;
            }
            sendto_one(sptr, ":%s NOTICE %s :MAXCLIENTS is %d (%d online)",
                       me.name, parv[0], MAXCLIENTS, Count.local);
            return 0;
        }
#ifdef FLUD
        else if (!strncasecmp(command, "FLUDNUM", 7)) 
        {
            if (parc > 2) 
            {
                int newval = atoi(parv[2]);
                if (newval <= 0) 
                {
                    sendto_one(sptr, ":%s NOTICE %s :flud NUM must be > 0",
                               me.name, parv[0]);
                    return 0;
                }
                flud_num = newval;
                sendto_ops("%s has changed flud NUM to %i", parv[0], flud_num);
                sendto_one(sptr, ":%s NOTICE %s :flud NUM is now set to %i",
                           me.name, parv[0], flud_num);
                return 0;
            }
            else
            {
                sendto_one(sptr, ":%s NOTICE %s :flud NUM is currently %i",
                           me.name, parv[0], flud_num);
                return 0;
            }
        }
        else if (!strncasecmp(command, "FLUDTIME", 8)) 
        {
            if (parc > 2) 
            {
                int newval = atoi(parv[2]);
                if (newval <= 0) 
                {
                    sendto_one(sptr, ":%s NOTICE %s :flud TIME must be > 0",
                               me.name, parv[0]);
                    return 0;
                }
                flud_time = newval;
                sendto_ops("%s has changed flud TIME to %i", parv[0],
                           flud_time);
                sendto_one(sptr, ":%s NOTICE %s :flud TIME is now set to %i",
                           me.name, parv[0], flud_time);
                return 0;
            }
            else 
            {
                sendto_one(sptr, ":%s NOTICE %s :flud TIME is currently %i",
                           me.name, parv[0], flud_time);
                return 0;
            }
        }
        else if (!strncasecmp(command, "FLUDBLOCK", 9)) 
        {
            if (parc > 2) 
            {
                int newval = atoi(parv[2]);
                if (newval < 0) 
                {
                    sendto_one(sptr, ":%s NOTICE %s :flud BLOCK must be >= 0",
                               me.name, parv[0]);
                    return 0;
                }
                flud_block = newval;
                if (flud_block == 0)
                {
                    sendto_ops("%s has disabled flud detection/protection",
                               parv[0]);
                    sendto_one(sptr, ":%s NOTICE %s :flud detection disabled",
                               me.name, parv[0]);
                }
                else
                {
                    sendto_ops("%s has changed flud BLOCK to %i",
                               parv[0], flud_block);
                    sendto_one(sptr, ":%s NOTICE %s :flud BLOCK is now set "
                               "to %i", me.name, parv[0], flud_block);
                }
                return 0;
            }
            else
            {
                sendto_one(sptr, ":%s NOTICE %s :flud BLOCK is currently %i",
                           me.name, parv[0], flud_block);
                return 0;
            }
        }
#endif
#ifdef ANTI_SPAMBOT
        /* int spam_time = MIN_JOIN_LEAVE_TIME; 
         * int spam_num = MAX_JOIN_LEAVE_COUNT;
         */
        else if (!strncasecmp(command, "SPAMNUM", 7)) 
        {
            if (parc > 2) 
            {
                int newval = atoi(parv[2]);
                if (newval <= 0) 
                {
                    sendto_one(sptr, ":%s NOTICE %s :spam NUM must be > 0",
                               me.name, parv[0]);
                    return 0;
                }
                if (newval < MIN_SPAM_NUM)
                    spam_num = MIN_SPAM_NUM;
                else
                    spam_num = newval;
                sendto_ops("%s has changed spam NUM to %i", parv[0], spam_num);
                sendto_one(sptr, ":%s NOTICE %s :spam NUM is now set to %i",
                           me.name, parv[0], spam_num);
                return 0;
            }
            else 
            {
                sendto_one(sptr, ":%s NOTICE %s :spam NUM is currently %i",
                           me.name, parv[0], spam_num);
                return 0;
            }
        }
        else if (!strncasecmp(command, "SPAMTIME", 8)) 
        {
            if (parc > 2) 
            {
                int newval = atoi(parv[2]);
                if (newval <= 0) 
                {
                    sendto_one(sptr, ":%s NOTICE %s :spam TIME must be > 0",
                               me.name, parv[0]);
                    return 0;
                }
                if (newval < MIN_SPAM_TIME)
                    spam_time = MIN_SPAM_TIME;
                else
                    spam_time = newval;
                sendto_ops("%s has changed spam TIME to %i", parv[0],
                           spam_time);
                sendto_one(sptr, ":%s NOTICE %s :SPAM TIME is now set to %i",
                           me.name, parv[0], spam_time);
                return 0;
            }
            else 
            {
                sendto_one(sptr, ":%s NOTICE %s :spam TIME is currently %i",
                           me.name, parv[0], spam_time);
                return 0;
            }
        }

#endif
#ifdef THROTTLE_ENABLE
        else if (!strncasecmp(command, "THROTTLE", 8))  
        {
           char *changed = NULL;
           char *to = NULL;
           /* several values available:
            * ENABLE [on|off] to enable the code
            * COUNT [n] to set a max count, must be > 1
            * TIME [n] to set a max time before expiry, must be > 5
            * RECORDTIME [n] to set a time for the throttle records to expire
            * HASH [n] to set the size of the hash table, must be bigger than
            *          the default */


           /* only handle individual settings if parc > 3 (they're actually
            * changing stuff) */
           if (parc > 3) {
               if (!strcasecmp(parv[2], "ENABLE"))  {
                  changed = "ENABLE";
                  if (ToLower(*parv[3]) == 'y' || !strcasecmp(parv[3], "on")) {
                     throttle_enable = 1;
                     to = "ON";
                  } else if (ToLower(*parv[3]) == 'n' ||
                           !strcasecmp(parv[3], "off")) {
                     throttle_enable = 0;
                     to = "OFF";
                  }
               } else if (!strcasecmp(parv[2], "COUNT")) {
                  int cnt;
                  changed = "COUNT";
                  cnt = atoi(parv[3]);
                  if (cnt > 1) {
                     throttle_tcount = cnt;
                     to = parv[3];
                  }
               } else if (!strcasecmp(parv[2], "TIME")) {
                  int cnt;
                  changed = "TIME";
                  cnt = atoi(parv[3]);
                  if (cnt >= 5) {
                     throttle_ttime = cnt;
                     to = parv[3];
                  } 
               } else if (!strcasecmp(parv[2], "RECORDTIME")) {
                  int cnt;
                  changed = "RECORDTIME";
                  cnt = atoi(parv[3]);
                  if (cnt >= 30) {
                     throttle_rtime = cnt;
                     to = parv[3];
                  }
               } else if (!strcasecmp(parv[2], "HASH")) {
                  int cnt;
                  changed = "HASH";
                  cnt = atoi(parv[3]);
                  if (cnt >= THROTTLE_HASHSIZE) {
                     throttle_resize(cnt);
                     to = parv[3];
                  }
               }

               if (to != NULL) {
                  sendto_ops("%s has changed throttle %s to %s", parv[0],
                        changed, to);
                  sendto_one(sptr, ":%s NOTICE %s :set throttle %s to %s",
                        me.name, parv[0], changed, to);
               }
           } else {
              /* report various things, we cannot easily get the hash size, so
               * leave that alone. */
              sendto_one(sptr, ":%s NOTICE %s :THROTTLE %s", me.name, parv[0],
                    throttle_enable ? "enabled" : "disabled");
              sendto_one(sptr, ":%s NOTICE %s :THROTTLE COUNT=%d", me.name,
                    parv[0], throttle_tcount);
              sendto_one(sptr, ":%s NOTICE %s :THROTTLE TIME=%d sec", me.name,
                    parv[0], throttle_ttime);
              sendto_one(sptr, ":%s NOTICE %s :THROTTLE RECORDTIME=%d sec", me.name,
                    parv[0], throttle_rtime);
           }
        }
        else if (!strncasecmp(command, "LCLONES", 3))
        {
            if (parc > 3)
            {
                int limit, rval;

                limit = atoi(parv[3]);
                rval = clones_set(parv[2], CLIM_SOFT_LOCAL, limit);

                if (rval < 0)
                    sendto_one(sptr, ":%s NOTICE %s :Invalid IP or limit.",
                               me.name, parv[0]);
                else if (rval > 0 && limit == 0)
                {
                    sendto_ops("%s removed soft local clone limit for %s",
                               parv[0], parv[2]);
                    sendto_one(sptr, ":%s NOTICE %s :removed soft local clone"
                               " limit for %s", me.name, parv[0], parv[2]);
                }
                else if (rval > 0)
                {
                    sendto_ops("%s changed soft local clone limit for %s from"
                               " %d to %d", parv[0], parv[2], rval, limit);
                    sendto_one(sptr, ":%s NOTICE %s :changed soft local clone"
                               " limit for %s from %d to %d", me.name, parv[0],
                               parv[2], rval, limit);
                }
                else if (limit == 0)
                {
                    sendto_one(sptr, ":%s NOTICE %s :no soft local clone limit"
                               " for %s", me.name, parv[0], parv[2]);
                }
                else
                {
                    sendto_ops("%s set soft local clone limit for %s to %d",
                               parv[0], parv[2], limit);
                    sendto_one(sptr, ":%s NOTICE %s :set soft local clone"
                               " limit for %s to %d", me.name, parv[0],
                               parv[2], limit);
                }
            }
            else if (parc > 2)
            {
                int hglimit, sglimit, sllimit;

                clones_get(parv[2], &hglimit, &sglimit, &sllimit);

                if (!sllimit)
                    sendto_one(sptr, ":%s NOTICE %s :no soft local clone limit"
                               " for %s", me.name, parv[0], parv[2]);
                else
                    sendto_one(sptr, ":%s NOTICE %s :soft local clone limit"
                               " for %s is %d", me.name, parv[0], parv[2],
                               sllimit);
            }
            else
                sendto_one(sptr, ":%s NOTICE %s :Usage: LCLONES <ip> [<limit>]",
                           me.name, parv[0]);
        }
        else if (!strncasecmp(command, "GCLONES", 3))
        {
            int hglimit, sglimit, sllimit, limit, rval;

            if (parc > 3)
            {
                limit = atoi(parv[3]);
                clones_get(parv[2], &hglimit, &sglimit, &sllimit);

                if (hglimit && limit > hglimit)
                    sendto_one(sptr, ":%s NOTICE %s :Cannot set soft global"
                               " clone limit for %s above services-set hard"
                               " limit (%d)", me.name, parv[0], parv[2],
                               hglimit);
                else
                {
                    rval = clones_set(parv[2], CLIM_SOFT_GLOBAL, limit);

                    if (rval < 0)
                        sendto_one(sptr, ":%s NOTICE %s :Invalid IP or limit.",
                                   me.name, parv[0]);
                    else if (rval > 0 && limit == 0)
                    {
                        sendto_ops("%s removed soft global clone limit for %s",
                                   parv[0], parv[2]);
                        sendto_one(sptr, ":%s NOTICE %s :removed soft global"
                                   " clone limit for %s", me.name, parv[0],
                                   parv[2]);
                    }
                    else if (rval > 0)
                    {
                        sendto_ops("%s changed soft global clone limit for %s"
                                   " from %d to %d", parv[0], parv[2], rval,
                                   limit);
                        sendto_one(sptr, ":%s NOTICE %s :changed soft global"
                                   " clone limit for %s from %d to %d",
                                   me.name, parv[0], parv[2], rval, limit);
                    }
                    else if (limit == 0)
                    {
                        sendto_one(sptr, ":%s NOTICE %s :no soft global clone"
                                   " limit for %s", me.name, parv[0], parv[2]);
                    }
                    else
                    {
                        sendto_ops("%s set soft global clone limit for %s to"
                                   " %d", parv[0], parv[2], limit);
                        sendto_one(sptr, ":%s NOTICE %s :set soft global clone"
                                   " limit for %s to %d", me.name, parv[0],
                                   parv[2], limit);
                    }
                }
            }
            else if (parc > 2)
            {
                clones_get(parv[2], &hglimit, &sglimit, &sllimit);

                if (sglimit)
                    sendto_one(sptr, ":%s NOTICE %s :soft global clone limit"
                               " for %s is %d", me.name, parv[0], parv[2],
                               sglimit);
                else
                    sendto_one(sptr, ":%s NOTICE %s :no soft global clone"
                               " limit for %s", me.name, parv[0], parv[2]);

                if (hglimit)
                    sendto_one(sptr, ":%s NOTICE %s :hard global clone limit"
                               " for %s is %d", me.name, parv[0], parv[2],
                               hglimit);
            }
            else
                sendto_one(sptr, ":%s NOTICE %s :Usage: GCLONES <ip> [<limit>]",
                           me.name, parv[0]);
        }
        else if (!strncasecmp(command, "DEFLCLONE", 6))
        {
            char *eptr;

            if (parc > 2)
            {
                local_ip_limit = strtol(parv[2], &eptr, 10);
                if (*eptr != 0)
                    local_ip24_limit = atoi(eptr+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;

                sendto_ops("%s set default local clone limit to %d:%d"
                           " (host:site)", parv[0], local_ip_limit,
                           local_ip24_limit);
                sendto_one(sptr, ":%s NOTICE %s :set default local clone limit"
                           " to %d:%d (host:site)", me.name, parv[0],
                           local_ip_limit, local_ip24_limit);
            }
            else
                sendto_one(sptr, ":%s NOTICE %s :default local clone limit is"
                           " %d:%d (host:site)", me.name, parv[0],
                           local_ip_limit, local_ip24_limit);
        }
        else if (!strncasecmp(command, "DEFGCLONE", 6))
        {
            char *eptr;

            if (parc > 2)
            {
                global_ip_limit = strtol(parv[2], &eptr, 10);
                if (*eptr != 0)
                    global_ip24_limit = atoi(eptr+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;

                sendto_ops("%s set default global clone limit to %d:%d"
                           " (host:site)", parv[0], global_ip_limit,
                           global_ip24_limit);
                sendto_one(sptr, ":%s NOTICE %s :set default global clone"
                           " limit to %d:%d (host:site)", me.name, parv[0],
                           global_ip_limit, global_ip24_limit);
            }
            else
                sendto_one(sptr, ":%s NOTICE %s :default global clone limit is"
                           " %d:%d (host:site)", me.name, parv[0],
                           global_ip_limit, global_ip24_limit);
        }
#endif
    }
    else 
    {
        sendto_one(sptr, ":%s NOTICE %s :Options: MAX",
                   me.name, parv[0]);
#ifdef FLUD
        sendto_one(sptr, ":%s NOTICE %s :Options: FLUDNUM, FLUDTIME, "
                   "FLUDBLOCK", me.name, parv[0]);
#endif

#ifdef ANTI_SPAMBOT
        sendto_one(sptr, ":%s NOTICE %s :Options: SPAMNUM, SPAMTIME",
                   me.name, parv[0]);
#endif

#ifdef THROTTLE_ENABLE
        sendto_one(sptr, ":%s NOTICE %s :Options: THROTTLE "
              "<ENABLE|COUNT|TIME|RECORDTIME|HASH> [setting]", me.name, parv[0]);
        sendto_one(sptr, ":%s NOTICE %s :Options: LCLONES, GCLONES, "
                   "DEFLCLONES, DEFGCLONES", me.name, parv[0]);
#endif
    }
    return 0;
}

/*
 * cluster() input            
 * - pointer to a hostname output 
 * pointer to a static of the hostname masked for use in a kline. side 
 * effects - NONE
 * 
 * reworked a tad -Dianora
 */

#if 0
static char *cluster(char *hostname)
{
    static char result[HOSTLEN + 1];    /* result to return */
    char        temphost[HOSTLEN + 1];  /* workplace */
    char       *ipp;            /* used to find if host is ip # only */
    char       *host_mask;      /* used to find host mask portion to '*' */
    /* used to zap last nnn portion of an ip # */
    char       *zap_point = (char *) NULL; 
    char       *tld;            /* Top Level Domain */
    int         is_ip_number;   /* flag if its an IP # */
    int         number_of_dots; /* count # of dots for ip# and domain klines */

    if (!hostname)
        return (char *) NULL;   /* EEK! */

    /*
     * If a '@' is found in the hostname, this is bogus and must have
     * been introduced by server that doesn't check for bogus domains
     * (dns spoof) very well. *sigh* just return it... I could also
     * legitimately return (char *)NULL as above.
     * 
     * -Dianora
     */

    if (strchr(hostname, '@')) 
    {
        strncpyzt(result, hostname, HOSTLEN);
        return (result);
    }

    strncpyzt(temphost, hostname, HOSTLEN);

    is_ip_number = YES;         /* assume its an IP# */
    ipp = temphost;
    number_of_dots = 0;

    while (*ipp) 
    {
        if (*ipp == '.') 
        {
            number_of_dots++;
            if (number_of_dots == 3)
                zap_point = ipp;
            ipp++;
        }
        else if (!IsDigit(*ipp)) 
        {
            is_ip_number = NO;
            break;
        }
        ipp++;
    }

    if (is_ip_number && (number_of_dots == 3)) 
    {
        zap_point++;
        *zap_point++ = '*';     /* turn 111.222.333.444 into ... */
        *zap_point = '\0';      /* 111.222.333.* */
        strncpy(result, temphost, HOSTLEN);
        return (result);
    }
    else 
    {
        tld = strrchr(temphost, '.');
        if (tld) 
        {
            number_of_dots = 2;
            if (tld[3])                 /* its at least a 3 letter tld */
                number_of_dots = 1;
            if (tld != temphost)        /* in these days of dns spoofers ... */
                host_mask = tld - 1;    /* Look for host portion to '*' */
            else
                host_mask = tld;  /* degenerate case hostname is '.com' ect. */
            
            while (host_mask != temphost) 
            {
                if (*host_mask == '.')
                    number_of_dots--;
                if (number_of_dots == 0) 
                {
                    result[0] = '*';
                    strncpy(result + 1, host_mask, HOSTLEN - 1);
                    return (result);
                }
                host_mask--;
            }
            result[0] = '*';    /* foo.com => *foo.com */
            strncpy(result + 1, temphost, HOSTLEN);
        }
        else
        {               /*  no tld found oops. just return it as is */
            strncpy(result, temphost, HOSTLEN);
            return (result);
        }
    }

    return (result);
}

int m_kline(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    struct userBan *ban, *oban;
#if defined (LOCKFILE)
    struct pkl *k;
#else
    int         out;
#endif
    
    char        buffer[1024];

    char       *filename;       /* filename to use for kline */
    char       *user, *host;
    char       *reason;
    char       *current_date;
    aClient    *acptr;
    char        tempuser[USERLEN + 2];
    char        temphost[HOSTLEN + 1];
    int         temporary_kline_time = 0;       /* -Dianora */
    time_t      temporary_kline_time_seconds = 0;
    int         time_specified = 0;
    char       *argv;
    int         i;
    char       fbuf[512];

    if (!MyClient(sptr) || !IsAnOper(sptr) || !OPCanKline(sptr)) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }

    if (parc < 2) 
    {
        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
                   me.name, parv[0], "KLINE");
        return 0;
    }

    argv = parv[1];

    if ((temporary_kline_time = isnumber(argv)) >= 0) 
    {
        if (parc < 3) 
        {
            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
                       me.name, parv[0], "KLINE");
            return 0;
        }
        if (temporary_kline_time > (24 * 60 * 7))
            temporary_kline_time = (24 * 60 * 7);       /*  Max it at 1 week */

        temporary_kline_time_seconds = 
            (time_t) temporary_kline_time *(time_t) 60;
        
        /* turn it into minutes */
        argv = parv[2];
        parc--;
        time_specified = 1;
    }
    else
    {
        temporary_kline_time = 0; /* -1 minute klines are bad... :) - lucas */
    }
    
    if(strchr(argv, ' '))
    {
        sendto_one(sptr, ":%s NOTICE %s :Poorly formatted hostname "
                         "(contains spaces). Be sure you are using the form: "
                         "/quote KLINE [time] <user@host/nick> :<reason>",
                         me.name, parv[0]);
        return 0;
    }
    

    if ((host = strchr(argv, '@')) || *argv == '*') 
    {
        /* Explicit user@host mask given */

        if (host)               /* Found user@host */
        {
            user = argv;                /* here is user part */
            *(host++) = '\0';   /* and now here is host */
        }
        else 
        {
            user = "*";         /* no @ found, assume its *@somehost */
            host = argv;
        }
        
        if (!*host)             /* duh. no host found, assume its '*' host */
            host = "*";
        strncpyzt(tempuser, user, USERLEN + 2); /* allow for '*' in front */
        strncpyzt(temphost, host, HOSTLEN);
        user = tempuser;
        host = temphost;
    }
    else 
    {
        /* Try to find user@host mask from nick */
        
        if (!(acptr = find_chasing(sptr, argv, NULL)))
            return 0;

        if (!acptr->user)
            return 0;

        if (IsServer(acptr)) 
        {
            sendto_one(sptr, ":%s NOTICE %s :Can't KLINE a server, use @'s "
                       "where appropriate", me.name, parv[0]);
            return 0;
        }
        /*
         * turn the "user" bit into "*user", blow away '~' if found in
         * original user name (non-idented)
         */

        tempuser[0] = '*';
        if (*acptr->user->username == '~')
            strcpy(tempuser + 1, (char *) acptr->user->username + 1);
        else
            strcpy(tempuser + 1, acptr->user->username);
        user = tempuser;
        host = cluster(acptr->user->host);
    }

    if (time_specified)
        argv = parv[3];
    else
        argv = parv[2];

#ifdef DEFAULT_KLINE_TIME
    if (time_specified == 0)
    {
        temporary_kline_time = DEFAULT_KLINE_TIME;
        temporary_kline_time_seconds =
            (time_t) temporary_kline_time *(time_t) 60;
    }
#endif

    if (parc > 2) 
    {
        if (*argv)
            reason = argv;
        else
            reason = "No reason";
    }
    else
        reason = "No reason";

    if (!match(user, "akjhfkahfasfjd") &&
        !match(host, "ldksjfl.kss...kdjfd.jfklsjf")) 
    {
        sendto_one(sptr, ":%s NOTICE %s :Can't K-Line *@*", me.name,
                   parv[0]);
        return 0;
    }

    /* we can put whatever we want in temp K: lines */
    if (temporary_kline_time == 0 && strchr(reason, ':')) 
    {
        sendto_one(sptr,
                   ":%s NOTICE %s :Invalid character ':' in comment",
                   me.name, parv[0]);
        return 0;
    }

    if (temporary_kline_time == 0 && strchr(reason, '#')) 
    {
        sendto_one(sptr,
                   ":%s NOTICE %s :Invalid character '#' in comment",
                   me.name, parv[0]);
        return 0;
    }

    ban = make_hostbased_ban(user, host);
    if(!ban)
    {
        sendto_one(sptr, ":%s NOTICE %s :Malformed ban %s@%s", me.name, parv[0],
                   user, host);
        return 0;
    }

    if ((oban = find_userban_exact(ban, 0)))
    {
        char *ktype = (oban->flags & UBAN_LOCAL) ? 
                      LOCAL_BANNED_NAME : NETWORK_BANNED_NAME;

        sendto_one(sptr, ":%s NOTICE %s :[%s@%s] already %s for %s",
                   me.name, parv[0], user, host, ktype, 
                   oban->reason ? oban->reason : "<No Reason>");

        userban_free(ban);
        return 0;
    }

    current_date = smalldate((time_t) 0);
    ircsprintf(buffer, "%s (%s)", reason, current_date);

    ban->flags |= UBAN_LOCAL;
    ban->reason = (char *) MyMalloc(strlen(buffer) + 1);
    strcpy(ban->reason, buffer);
    
    if (temporary_kline_time) 
    {
        ban->flags |= UBAN_TEMPORARY;
        ban->timeset = timeofday;
        ban->duration = temporary_kline_time_seconds;
    }

    if(user_match_ban(sptr, ban))
    {
        sendto_one(sptr, ":%s NOTICE %s :You attempted to add a ban [%s@%s]"
                         " which would affect yourself. Aborted.",
                   me.name, parv[0], user, host);
        userban_free(ban);
        return 0;
    }

    add_hostbased_userban(ban);

    /* Check local users against it */
    
	userban_sweep(ban);

    host = get_userban_host(ban, fbuf, 512);

    if(temporary_kline_time)
    {   
        sendto_realops("%s added temporary %d min. "LOCAL_BAN_NAME" for"
                       " [%s@%s] [%s]", parv[0], temporary_kline_time, user, 
                       host, reason);
        return 0;
    }

    /* from here on, we're dealing with a perm kline */

    filename = configfile;

    sendto_one(sptr, ":%s NOTICE %s :Added K-Line [%s@%s] to server "
               "configfile", me.name, parv[0], user, host);

    sendto_realops("%s added K-Line for [%s@%s] [%s]",
                   parv[0], user, host, reason);
    
#if defined(LOCKFILE)
    if ((k = (struct pkl *) malloc(sizeof(struct pkl))) == NULL) 
    {
        sendto_one(sptr, ":%s NOTICE %s :Problem allocating memory",
                   me.name, parv[0]);
        return (0);
    }

    ircsprintf(buffer, "/* %s!%s@%s Added kill for: %s@%s\n"
                       " * at %s */\n",
               sptr->name, sptr->user->username,
               sptr->user->host, user, host, current_date);

    if ((k->comment = strdup(buffer)) == NULL) 
    {
        free(k);
        sendto_one(sptr, ":%s NOTICE %s :Problem allocating memory",
                   me.name, parv[0]);
        return (0);
    }

    ircsprintf(buffer, "kill {\n"
                       "    mask \"%s@%s\";\n"
                       "    reason \"%s\";\n"
                       "};\n\n",
                      user, host, reason);

    if ((k->kline = strdup(buffer)) == NULL) 
    {
        free(k->comment);
        free(k);
        sendto_one(sptr, ":%s NOTICE %s :Problem allocating memory",
                   me.name, parv[0]);
        return (0);
    }
    k->next = pending_klines;
    pending_klines = k;

    do_pending_klines();
    return (0);

#else /*  LOCKFILE - MDP */

    if ((out = open(filename, O_RDWR | O_APPEND | O_CREAT)) == -1) 
    {
        sendto_one(sptr, ":%s NOTICE %s :Problem opening %s ",
                   me.name, parv[0], filename);
        return 0;
    }

    ircsprintf(buffer, "/* %s!%s@%s Added kill for: %s@%s\n"
                       " * at %s */\n",
               sptr->name, sptr->user->username,
               sptr->user->host, user, host, current_date);

    if (write(out, buffer, strlen(buffer)) <= 0) 
    {
        sendto_one(sptr, ":%s NOTICE %s :Problem writing to %s",
                   me.name, parv[0], filename);
        close(out);
        return 0;
    }

    ircsprintf(buffer, "kill {\n"
                       "    mask \"%s@%s\";\n"
                       "    reason \"%s\";\n"
                       "};\n\n",
                      user, host, reason);

    if (write(out, buffer, strlen(buffer)) <= 0) 
    {
        sendto_one(sptr, ":%s NOTICE %s :Problem writing to %s",
                   me.name, parv[0], filename);
        close(out);
        return 0;
    }

    close(out);

#ifdef USE_SYSLOG
    syslog(LOG_NOTICE, "%s added K-Line for [%s@%s] [%s]", parv[0],
           user, host, reason);
#endif

    return 0;
#endif /* LOCKFILE */
}
#endif

/*
 * isnumber()
 * 
 * inputs               
 * - pointer to ascii string in output             
 * - 0 if not an integer number, else the number side effects  
 * - none return -1 if not an integer. 
 * (if someone types in maxint, oh well..) - lucas
 */
#if 0
static int isnumber(char *p)
{
    int         result = 0;

    while (*p) 
    {
        if (IsDigit(*p)) 
        {
            result *= 10;
            result += ((*p) & 0xF);
            p++;
        }
        else
            return (-1);
    }
    /*
     * in the degenerate case where oper does a /quote kline 0 user@host
     * :reason i.e. they specifically use 0, I am going to return 1
     * instead as a return value of non-zero is used to flag it as a
     * temporary kline
     */

    /*
     * er, no. we return 0 because 0 means that it's a permanent kline. -lucas
     * oh, and we only do this if DEFAULT_KLINE_TIME is specified.
     */

#ifndef DEFAULT_KLINE_TIME
    if(result == 0)
        result = 1;
#endif
    
    return (result);
}
#endif

#if 0
/*
 * * m_unkline 
 * Added Aug 31, 1997 
 * common (Keith Fralick) fralick@gate.net 
 * 
 *      parv[0] = sender 
 *      parv[1] = address to remove
 * 
 * re-worked and cleanedup for use in hybrid-5 -Dianora
 * 
 */
int m_unkline(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    struct userBan *ban;
    char       *user, *host;

    if (!IsAnOper(sptr) || !OPCanUnKline(sptr))
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }

    if (parc < 2) 
    {
        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
                   me.name, parv[0], "UNKLINE");
        return 0;
    }

    if ((host = strchr(parv[1], '@')) || *parv[1] == '*') 
    {
        /* Explicit user@host mask given */

        if (host)       /* Found user@host */
        {
            user = parv[1];     /* here is user part */
            *(host++) = '\0';   /* and now here is host */
        }
        else 
        {
            user = "*";         /* no @ found, assume its *@somehost */
            host = parv[1];
        }
    }
    else
    {
        sendto_one(sptr, ":%s NOTICE %s :Invalid parameters",
                   me.name, parv[0]);
        return 0;
    }

    if ((user[0] == '*') && (user[1] == '\0') && (host[0] == '*') &&
        (host[1] == '\0')) 
    {
        sendto_one(sptr, ":%s NOTICE %s :Cannot UNK-Line everyone",
                   me.name, parv[0]);
        return 0;
    }

    ban = make_hostbased_ban(user, host);
    if(ban)
    {
        struct userBan *oban;

        ban->flags |= (UBAN_LOCAL|UBAN_TEMPORARY);
        if((oban = find_userban_exact(ban, UBAN_LOCAL|UBAN_TEMPORARY)))
        {
            char tmp[512];

            host = get_userban_host(oban, tmp, 512);

            remove_userban(oban);
            klinestore_remove(oban);
            userban_free(oban);
            userban_free(ban);

            sendto_one(sptr, ":%s NOTICE %s :K-Line for [%s@%s] is removed",
                       me.name, parv[0], user, host);
            sendto_ops("%s has removed the K-Line for: [%s@%s] (%d matches)",
                       parv[0], user, host, 1);

            return 0;
        }
        userban_free(ban);
    }    
    sendto_one(sptr, ":%s NOTICE %s :No Kline matches [%s@%s]",
               me.name, parv[0], user, host);
    return 0;
}

#endif /* UNKLINE */



/* m_rehash */
int 
m_rehash(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    if (!OPCanRehash(sptr)) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }
        
    if (parc > 1) 
    {
        if (mycmp(parv[1], "DNS") == 0) 
        {
            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0], "DNS");
            flush_cache();              /* flush the dns cache */
            res_init();         /* re-read /etc/resolv.conf */
            sendto_ops("%s is rehashing DNS while whistling innocently",
                       parv[0]);
            return 0;
        }
        else if (mycmp(parv[1], "TKLINES") == 0)
        {
            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
                       "temp klines");
            remove_userbans_match_flags(UBAN_LOCAL|UBAN_TEMPORARY, 0);
            sendto_ops("%s is clearing temp klines while whistling innocently",
                       parv[0]);
            return 0;
        }
/* Rehash & remove Temporary Glines - Sheik 03-DEC-2005 */

		else if (mycmp(parv[1], "TGLINES") == 0)
        {
            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
                       "temp glines");
            remove_userbans_match_flags(UBAN_GLINE|UBAN_TEMPORARY, 0);
            sendto_ops("%s is clearing temp glines while whistling innocently",
                       parv[0]);
            return 0;
        }
        else if (mycmp(parv[1], "GC") == 0) 
        {
            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
                       "garbage collecting");
            block_garbage_collect();
            sendto_ops("%s is garbage collecting while whistling innocently",
                       parv[0]);
            return 0;
        }
        else if (mycmp(parv[1], "MOTD") == 0) 
        {
            sendto_ops("%s is forcing re-reading of MOTD file", parv[0]);
            read_motd(MOTD);
        if(confopts & FLAGS_SMOTD)
                read_shortmotd(SHORTMOTD);
            return (0);
        }
        else if(mycmp(parv[1], "AKILLS") == 0) 
        {
            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
                       "akills");
            remove_userbans_match_flags(UBAN_NETWORK, 0);
            sendto_ops("%s is rehashing akills", parv[0]);
            return 0;
        }
        else if(mycmp(parv[1], "THROTTLES") == 0) {
            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
                 "throttles");
            throttle_rehash();
            sendto_ops("%s is rehashing throttles", parv[0]);
            return 0;
        }
        else if(mycmp(parv[1], "SQLINES") == 0) {
            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
                 "sqlines");
            sendto_ops("%s is rehashing sqlines", parv[0]);
            remove_simbans_match_flags(SBAN_NICK|SBAN_NETWORK, 0);
            remove_simbans_match_flags(SBAN_CHAN|SBAN_NETWORK, 0);
            return 0;
        }
        else if(mycmp(parv[1], "SGLINES") == 0) {
            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
                 "sglines");
            sendto_ops("%s is rehashing sglines", parv[0]);
            remove_simbans_match_flags(SBAN_GCOS|SBAN_NETWORK, 0);
            return 0;
        }
        else if(mycmp(parv[1], "TSQGLINES") == 0) {
            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
                 "tsqglines");
            sendto_ops("%s is rehashing temporary sqlines/glines", parv[0]);
            remove_simbans_match_flags(SBAN_GCOS|SBAN_TEMPORARY, 0);
            remove_simbans_match_flags(SBAN_NICK|SBAN_TEMPORARY, 0);
            remove_simbans_match_flags(SBAN_CHAN|SBAN_TEMPORARY, 0);
            return 0;
        }
    }
    else 
    {
        sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0], configfile);
        sendto_ops("%s is rehashing Server config file while whistling "
                   "innocently", parv[0]);
# ifdef USE_SYSLOG
        syslog(LOG_INFO, "REHASH From %s\n", get_client_name(sptr, FALSE));
# endif
        return rehash(cptr, sptr, 
                      (parc > 1) ? ((*parv[1] == 'q') ? 2 : 0) : 0);
    }
    return 0;                   /* shouldn't ever get here */
}

/* m_restart */
int 
m_restart(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    char       *pass = NULL;
    
    if (!OPCanRestart(sptr))
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }
    /*
     * m_restart is now password protected as in df465 only change --
     * this one doesn't allow a reason to be specified. future changes:
     * crypt()ing of password, reason to be re-added -mjs
     */
    if ((pass = MeLine->restartpass)) 
    {
        if (parc < 2) 
        {
            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
                       "RESTART");
            return 0;
        }
        if (strcmp(pass, parv[1])) 
        {
            sendto_one(sptr, err_str(ERR_PASSWDMISMATCH), me.name, parv[0]);
            return 0;
        }
    }
    
#ifdef USE_SYSLOG
    syslog(LOG_WARNING, "Server RESTART by %s\n",
           get_client_name(sptr, FALSE));
#endif
    sprintf(buf, "Server RESTART by %s", get_client_name(sptr, FALSE));
    restart(buf);
    return 0;                   /* NOT REACHED */
}

/*
 * * m_trace 
 *        parv[0] = sender prefix 
 *        parv[1] = servername
 */
int 
m_trace(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int      i;
    aClient *acptr=NULL;
    aClass      *cltmp;
    char        *tname;
    int          doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
    int          wilds = 0, dow = 0;
        
    tname = (parc > 1) ? parv[1] : me.name;

#ifdef NO_USER_TRACE
    if(!IsAnOper(sptr))
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }
#endif

#ifdef HIDEULINEDSERVS
    if((acptr = next_client_double(client, tname)))
    {
        if (!(IsAnOper(sptr)) && IsULine(acptr))
        {
            sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
            return 0;
        }
        acptr = NULL; /* shrug, we borrowed it, reset it just in case */
    }
#endif
    
    if (parc > 2)
        if (hunt_server(cptr, sptr, ":%s TRACE %s :%s", 2, parc, parv))
            return 0;

    switch (hunt_server(cptr, sptr, ":%s TRACE :%s", 1, parc, parv)) 
    {
        case HUNTED_PASS:   /*  note: gets here only if parv[1] exists */
        {
            aClient    *ac2ptr = next_client_double(client, tname);
            if (ac2ptr)
                sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
                           version, debugmode, tname, 
                           ac2ptr->from->name);
            else
                sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
                           version, debugmode, tname, 
                           "ac2ptr_is_NULL!!");
            return 0;
        }
        case HUNTED_ISME:
            break;
        default:
            return 0;
    }
    if(!IsAnOper(sptr)) 
    {
        if (parv[1] && !strchr(parv[1],'.') && 
            (strchr(parv[1], '*') || strchr(parv[1], '?'))) 
            /* bzzzt, no wildcard nicks for nonopers */
        {
            sendto_one(sptr, rpl_str(RPL_ENDOFTRACE),me.name,
                       parv[0], parv[1]);
            return 0;        
        }
    }
    sendto_realops_lev(SPY_LEV, "TRACE requested by %s (%s@%s) [%s]",
                       sptr->name, sptr->user->username, sptr->user->host,
                       sptr->user->server);
        
    doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : TRUE;
    wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
    dow = wilds || doall;
    if(!IsAnOper(sptr) || !dow) /* non-oper traces must be full nicks */
        /* lets also do this for opers tracing nicks */
    {
        char      *name, *class;
        acptr = hash_find_client(tname,(aClient *)NULL);
        if(!acptr || !IsPerson(acptr)) 
        {
            /* this should only be reached if the matching
               target is this server */
            sendto_one(sptr, rpl_str(RPL_ENDOFTRACE),me.name,
                       parv[0], tname);
            return 0;
                          
        }
        if(acptr->class)
            class = acptr->class->name;
        else
            class = "NONE";
        name = get_client_name(acptr,FALSE);
        if (IsAnOper(acptr)) 
        {
            sendto_one(sptr, rpl_str(RPL_TRACEOPERATOR),
                       me.name, parv[0], class, name,
                       timeofday - acptr->lasttime);
        }
        else
        {
            sendto_one(sptr,rpl_str(RPL_TRACEUSER),
                       me.name, parv[0], class, name,
                       timeofday - acptr->lasttime);
        }
        sendto_one(sptr, rpl_str(RPL_ENDOFTRACE),me.name,
                   parv[0], tname);
        return 0;        
    }

    memset((char *) link_s, '\0', sizeof(link_s));
    memset((char *) link_u, '\0', sizeof(link_u));
    /* Count up all the servers and clients in a downlink. */
    if (doall)
        for (acptr = client; acptr; acptr = acptr->next) 
        {
            if (IsPerson(acptr) && (!IsInvisible(acptr) || IsAnOper(sptr)))
                link_u[acptr->from->fd]++;
            else if (IsServer(acptr))
#ifdef HIDEULINEDSERVS
                if (IsOper(sptr) || !IsULine(acptr))
#endif
                    link_s[acptr->from->fd]++;
        }
                
        
    /* report all direct connections */
        
    for (i = 0; i <= highest_fd; i++) 
    {
        char       *name, *class;
                
        if (!(acptr = local[i]))        /* Local Connection? */
            continue;
#ifdef HIDEULINEDSERVS
        if (!IsOper(sptr) && IsULine(acptr))
            continue;
#endif
        if (IsInvisible(acptr) && dow &&
            !(MyConnect(sptr) && IsAnOper(sptr)) &&
            !IsAnOper(acptr) && (acptr != sptr))
            continue;
        if (!doall && wilds && match(tname, acptr->name))
            continue;
        if (!dow && mycmp(tname, acptr->name))
            continue;
        /* only show IPs of unknowns or clients to opers */
        if (IsAnOper(sptr) && 
                (acptr->status == STAT_CLIENT || acptr->status == STAT_UNKNOWN))
            name = get_client_name(acptr, FALSE);
        else
            name = get_client_name(acptr, HIDEME);
        if(acptr->class)
            class = acptr->class->name;
        else
            class = "NONE";
                
        switch (acptr->status) 
        {
            case STAT_CONNECTING:
                sendto_one(sptr, rpl_str(RPL_TRACECONNECTING), me.name,
                           parv[0], class, name);
                break;
            case STAT_HANDSHAKE:
                sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE), me.name,
                           parv[0], class, name);
                break;
            case STAT_ME:
                break;
            case STAT_UNKNOWN:
                /* added time -Taner */
                sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
                          me.name, parv[0], class, name,
                          acptr->firsttime ? timeofday - acptr->firsttime : -1);
                break;
            case STAT_CLIENT:
                /*
                 * Only opers see users if there is a wildcard but
                 * anyone can see all the opers.
                 */
                if (((IsAnOper(sptr) && (MyClient(sptr))) 
                        || !(dow && IsInvisible(acptr))) || !dow || 
                    IsAnOper(acptr)) 
                {
                    if (IsAnOper(acptr))
                        sendto_one(sptr, rpl_str(RPL_TRACEOPERATOR),
                               me.name, parv[0], class, name,
                               timeofday - acptr->lasttime);
                }
                break;
            case STAT_SERVER:
                sendto_one(sptr, rpl_str(RPL_TRACESERVER),
                           me.name, parv[0], class, link_s[i],
                           link_u[i], name, 
                           *(acptr->serv->bynick) ? acptr->serv->bynick : "*", 
                           *(acptr->serv->byuser) ? acptr->serv->byuser : "*", 
                           *(acptr->serv->byhost) ? acptr->serv->byhost : 
                           me.name);
                break;
            case STAT_LOG:
                sendto_one(sptr, rpl_str(RPL_TRACELOG), me.name,
                           parv[0], LOGFILE, acptr->port);
                break;
            default:                /* ...we actually shouldn't come here... */
                sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE), me.name,
                           parv[0], name);
                break;
        }
    }

    /* let the user have some idea that its at the end of the trace */
    sendto_one(sptr, rpl_str(RPL_TRACESERVER),
                me.name, parv[0], "NONE", link_s[me.fd],
                link_u[me.fd], me.name, "*", "*", me.name,
                acptr ? timeofday - acptr->lasttime : 0);
#ifdef HIDEULINEDSERVS
    if (IsOper(sptr))
#endif
        for (cltmp = classes; doall && cltmp; cltmp = cltmp->next)
            if (cltmp->links > 0)
                sendto_one(sptr, rpl_str(RPL_TRACECLASS), me.name,
                           parv[0], cltmp->name, cltmp->links);
        
    sendto_one(sptr, rpl_str(RPL_ENDOFTRACE), me.name, parv[0], tname);
    return 0;
}

/*
 * * m_motd 
 *       parv[0] = sender prefix 
 *       parv[1] = servername
 */
int 
m_motd(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    static time_t last_used = 0L;
    if (hunt_server(cptr, sptr, ":%s MOTD :%s", 1, parc, parv) != HUNTED_ISME)
        return 0;
    if(!IsAnOper(sptr)) 
    {
        if (IsSquelch(sptr)) 
        {
            sendto_one(sptr, rpl_str(RPL_ENDOFMOTD), me.name, parv[0]);
            return 0;
        }
        if ((last_used + MOTD_WAIT) > NOW)
            return 0;
        else
            last_used = NOW;

    }
    sendto_realops_lev(SPY_LEV, "MOTD requested by %s (%s@%s) [%s]",
                       sptr->name, sptr->user->username, sptr->user->host,
                       sptr->user->server);
    send_motd(cptr, sptr, parc, parv);
    return 0;
}

/*
** send_motd
**  parv[0] = sender prefix
**  parv[1] = servername
**
** This function split off so a server notice could be generated on a
** user requested motd, but not on each connecting client.
** -Dianora
*/
int 
send_motd(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aMotd *temp;
    
    if (motd == (aMotd *) NULL) 
    {
        sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv[0]);
        return 0;
    }
    sendto_one(sptr, rpl_str(RPL_MOTDSTART), me.name, parv[0], me.name);

    sendto_one(sptr, ":%s %d %s :-%s", me.name, RPL_MOTD, parv[0], 
               motd_last_changed_date);

    temp = motd;
    while (temp) 
    {
        sendto_one(sptr, rpl_str(RPL_MOTD),  me.name, parv[0], temp->line);
        temp = temp->next;
    }
    sendto_one(sptr, rpl_str(RPL_ENDOFMOTD), me.name, parv[0]);
    return 0;
}

/*
 * read_motd() - From CoMSTuD, added Aug 29, 1996
 */
void 
read_motd(char *filename)
{
    aMotd *temp, *last;
    struct tm *motd_tm;
    struct stat sb;
    char        buffer[MOTDLINELEN], *tmp;
    int         fd;

    /* Clear out the old MOTD */

    while (motd) 
    {
        temp = motd->next;
        MyFree(motd);
        motd = temp;
    }
    fd = open(filename, O_RDONLY);
    if (fd == -1)
    {
        if(!forked)
                printf("WARNING:  MOTD file %s could not be found.  "
                       "Skipping MOTD load.\n", filename);
        return;
    }
    fstat(fd, &sb);
    motd_tm = localtime(&sb.st_mtime);
    last = (aMotd *) NULL;

    while (dgets(fd, buffer, MOTDLINELEN - 1) > 0) 
    {
        if ((tmp = (char *) strchr(buffer, '\n')))
            *tmp = '\0';
        if ((tmp = (char *) strchr(buffer, '\r')))
            *tmp = '\0';
        temp = (aMotd *) MyMalloc(sizeof(aMotd));

        strncpyzt(temp->line, buffer, MOTDLINELEN);
        temp->next = (aMotd *) NULL;
        if (!motd)
            motd = temp;
        else
            last->next = temp;
        last = temp;
    }
    close(fd);

    sprintf(motd_last_changed_date, "%d/%d/%d %d:%02d", motd_tm->tm_mday,
            motd_tm->tm_mon + 1, 1900 + motd_tm->tm_year, motd_tm->tm_hour,
            motd_tm->tm_min);
}

void 
read_shortmotd(char *filename)
{
    aMotd *temp, *last;
    char        buffer[MOTDLINELEN], *tmp;
    int         fd;

    /* Clear out the old MOTD */

    while (shortmotd)
    {
        temp = shortmotd->next;
        MyFree(shortmotd);
        shortmotd = temp;
    }
    fd = open(filename, O_RDONLY);
    if (fd == -1)
    {
        if(!forked)
                printf("WARNING:  sMOTD file %s could not be found.  "
                       "Skipping sMOTD load.\n", filename);
        return;
    }
    
    last = (aMotd *) NULL;

    while (dgets(fd, buffer, MOTDLINELEN - 1) > 0) 
    {
        if ((tmp = (char *) strchr(buffer, '\n')))
            *tmp = '\0';
        if ((tmp = (char *) strchr(buffer, '\r')))
            *tmp = '\0';
        temp = (aMotd *) MyMalloc(sizeof(aMotd));

        strncpyzt(temp->line, buffer, MOTDLINELEN);
        temp->next = (aMotd *) NULL;
        if (!shortmotd)
            shortmotd = temp;
        else
            last->next = temp;
        last = temp;
    }
    close(fd);
}

/*
 * read_help() - modified from from CoMSTuD's read_motd added Aug 29,
 * 1996 modifed  Aug 31 1997 - Dianora
 * 
 * Use the same idea for the oper helpfile
 */
void 
read_help(char *filename)
{
    aMotd *temp, *last;
    char        buffer[MOTDLINELEN], *tmp;
    int         fd;

    /* Clear out the old HELPFILE */

    while (helpfile) 
    {
        temp = helpfile->next;
        MyFree(helpfile);
        helpfile = temp;
    }

    fd = open(filename, O_RDONLY);
    if (fd == -1)
    {
        if(!forked)
                printf("WARNING:  Help file %s could not be found.  "
                       "Skipping Help file load.\n", filename);
        return;
    }

    last = (aMotd *) NULL;

    while (dgets(fd, buffer, MOTDLINELEN - 1) > 0) 
    {
        if ((tmp = (char *) strchr(buffer, '\n')))
            *tmp = '\0';
        if ((tmp = (char *) strchr(buffer, '\r')))
            *tmp = '\0';
        temp = (aMotd *) MyMalloc(sizeof(aMotd));

        strncpyzt(temp->line, buffer, MOTDLINELEN);
        temp->next = (aMotd *) NULL;
        if (!helpfile)
            helpfile = temp;
        else
            last->next = temp;
        last = temp;
    }
    close(fd);
}

/* m_close - added by Darren Reed Jul 13 1992. */
int 
m_close(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aClient *acptr;
    int     i;
    int         closed = 0;

    if (!MyOper(sptr)) 
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }

    for (i = highest_fd; i; i--) 
    {
        if (!(acptr = local[i]))
            continue;
        if (!IsUnknown(acptr) && !IsConnecting(acptr) &&
            !IsHandshake(acptr))
            continue;
        sendto_one(sptr, rpl_str(RPL_CLOSING), me.name, parv[0],
                   get_client_name(acptr, TRUE), acptr->status);
        exit_client(acptr, acptr, acptr, "Oper Closing");
        closed++;
    }
    sendto_one(sptr, rpl_str(RPL_CLOSEEND), me.name, parv[0], closed);
    return 0;
}

int 
m_die(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aClient *acptr;
    int     i;
    char       *pass = NULL;

    if (!OPCanDie(sptr))
    {
        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
        return 0;
    }
    /* X line -mjs */

    if ((pass = MeLine->diepass))
    {
        if (parc < 2) 
        {
            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name,
                       parv[0], "DIE");
            return 0;
        }
        if (strcmp(pass, parv[1])) 
        {
            sendto_one(sptr, err_str(ERR_PASSWDMISMATCH), me.name, parv[0]);
            return 0;
        }
    }

    for (i = 0; i <= highest_fd; i++) 
    {
        if (!(acptr = local[i]))
            continue;
        if (IsClient(acptr))
            sendto_one(acptr,
                       ":%s NOTICE %s :Server Terminating. %s",
                       me.name, acptr->name,
                       get_client_name(sptr, FALSE));
        else if (IsServer(acptr))
            sendto_one(acptr, ":%s ERROR :Terminated by %s",
                       me.name, get_client_name(sptr, TRUE));
    }
    s_die();
    return 0;
}

/*
 * m_capab 
 * Communicate what I can do to another server 
 * This has to be able to be sent and understood while
 * the client is UNREGISTERED. Therefore, we
 * absolutely positively must not check to see if
 * this is a server or a client. It's probably an unknown!
 */
int 
m_capab(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    int         i;

    /* If it's not local, or it has already set capabilities,
     * silently ignore it.
     * Dont ignore clients where we have set some capabilities already
     * that would suck for connecting TO servers.
     */
    
    if(cptr != sptr)
        return 0;

    for (i = 1; i < parc; i++) 
    {
        if (strcmp(parv[i], "BURST") == 0)
            SetBurst(sptr);
        else if (strcmp(parv[i], "UNCONNECT") == 0)
            SetUnconnect(cptr);
        else if (strcmp(parv[i], "DKEY") == 0)
            SetDKEY(cptr);
        else if (strcmp(parv[i], "ZIP") == 0)
            SetZipCapable(cptr);
#ifdef NOQUIT
        else if (strcmp(parv[i], "NOQUIT") == 0)
            SetNoquit(cptr);
#endif
    }

    return 0;
}

/* Shadowfax's LOCKFILE code */
#ifdef LOCKFILE

int 
lock_kline_file()
{
    int         fd;

    /* Create Lockfile */

    if ((fd = open(LOCKFILE, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0) 
    {
        sendto_realops("%s is locked, klines pending", configfile);
        pending_kline_time = time(NULL);
        return (-1);
    }
    close(fd);
    return 1;
}

void 
do_pending_klines()
{
    int         fd;
    char        s[20];
    struct pkl *k, *ok;

    if (!pending_klines)
        return;

    /* Create Lockfile */
    if ((fd = open(LOCKFILE, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0) 
    {
        sendto_realops("%s is locked, klines pending", configfile);
        pending_kline_time = time(NULL);
        return;
    }
    ircsprintf(s, "%d\n", getpid());
    write(fd, s, strlen(s));
    close(fd);

    /* Open klinefile */
    if ((fd = open(configfile, O_WRONLY | O_APPEND)) == -1) 
    {
        sendto_realops("Pending klines cannot be written, cannot open %s",
                       configfile);
        unlink(LOCKFILE);
        return;
    }

    /* Add the Pending Klines */

    k = pending_klines;
    while (k) 
    {
        write(fd, k->comment, strlen(k->comment));
        write(fd, k->kline, strlen(k->kline));
        free(k->comment);
        free(k->kline);
        ok = k;
        k = k->next;
        free(ok);
    }
    pending_klines = NULL;
    pending_kline_time = 0;

    close(fd);

    /* Delete the Lockfile */
    unlink(LOCKFILE);
}
#endif
         
/* m_svskill - Just about the same as outta df
 *  - Raistlin
 * parv[0] = servername
 * parv[1] = client
 * parv[2] = nick stamp
 * parv[3] = kill message
 */
         
int 
m_svskill(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
{
    aClient *acptr;
    char *comment;
    char reason[TOPICLEN + 1];
    ts_val ts = 0;

    if (parc < 2)
        return 0;

    if (parc > 3) 
    {
        comment = parv[3] ? parv[3] : parv[0];
        ts = atol(parv[2]);
    }
    else
        comment = (parc > 2 && parv[2]) ? parv[2] : parv[0];
      
    if(!IsULine(sptr)) return -1;
    if((acptr = find_client(parv[1], NULL)) && (!ts || ts == acptr->tsinfo))
    {
        if(MyClient(acptr))
        {
            strcpy(reason, "SVSKilled: ");
            strncpy(reason + 11, comment, TOPICLEN - 11);
            reason[TOPICLEN] = '\0';
            exit_client(acptr, acptr, sptr, reason);
            return (acptr == cptr) ? FLUSH_BUFFER : 0;
        }
        if(acptr->from == cptr)
        {
            sendto_realops_lev(DEBUG_LEV, "Received wrong-direction SVSKILL"
                               " for %s (behind %s) from %s", 
                               acptr->name, cptr->name,
                               get_client_name(sptr, HIDEME));
            return 0;
        }
        else if(ts == 0) 
            sendto_one(acptr->from, ":%s SVSKILL %s :%s", parv[0], parv[1],
                               comment);
        else
            sendto_one(acptr->from, ":%s SVSKILL %s %ld :%s", parv[0], parv[1],
                               ts, comment);
    }
    return 0;
}
         
/* m_akill -
 * Parse AKILL command
 * parv[1]=host 
 * parv[2]=user
 * parv[3]=length
 * parv[4]=akiller
 * parv[5]=time set
 * parv[6]=reason
 */      
int 
m_akill(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
{
    aClient *acptr;
    char *user, *host, *reason, *akiller, buffer[1024], *current_date, 
        fbuf[512];
    time_t length=0, timeset=0;
    int i;
    struct userBan *ban, *oban;

    if(!IsServer(sptr) || (parc < 6))
        return 0;
        
    if(!IsULine(sptr)) 
    {
        sendto_serv_butone(&me,":%s GLOBOPS :Non-ULined server %s trying to "
                           "AKILL!", me.name, sptr->name);
        send_globops("From %s: Non-ULined server %s trying to AKILL!", me.name,
                     sptr->name);
        return 0;
    }
        
    host=parv[1];
    user=parv[2];
    akiller=parv[4];
    length=atoi(parv[3]);
    timeset=atoi(parv[5]);
    reason=(parv[6] ? parv[6] : "<no reason>");

    if(length == 0) /* a "permanent" akill? */
       length = (86400 * 7); /* hold it for a week */

    /* is this an old bogus akill? */
    if(timeset + length <= NOW)
       return 0;

    current_date=smalldate((time_t)timeset);
    /* cut reason down a little, eh? */
    /* 250 chars max */
    if(strlen(reason)>250)
        reason[251]=0;

    ban = make_hostbased_ban(user, host);
    if(!ban)
    {
       sendto_realops_lev(DEBUG_LEV, "make_hostbased_ban(%s, %s) failed"
                                     " on akill", user, host);
       return 0;
    }

    /* if it already exists, pass it on */
    oban = find_userban_exact(ban, 0);
    if(oban)
    {
        /* pass along the akill anyways */
        sendto_serv_butone(cptr, ":%s AKILL %s %s %d %s %d :%s",
                           sptr->name, host, user, length, akiller,
                           timeset, reason);
       userban_free(ban);
       return 0;
    }
        
    ircsprintf(buffer, "%s (%s)", reason, current_date);
    ban->flags |= (UBAN_NETWORK|UBAN_TEMPORARY);
    ban->reason = (char *) MyMalloc(strlen(buffer) + 1);
    strcpy(ban->reason, buffer);
    ban->timeset = timeset;
    ban->duration = length;

    add_hostbased_userban(ban);

    /* send it off to any other servers! */
    sendto_serv_butone(cptr, ":%s AKILL %s %s %d %s %d :%s",
                       sptr->name, host, user, length, akiller,
                       timeset, reason);

    /* Check local users against it */
    for (i = 0; i <= highest_fd; i++)
    {
        if (!(acptr = local[i]) || IsMe(acptr) || IsLog(acptr))
            continue;
        if (IsPerson(acptr) && user_match_ban(acptr, ban))
        {
            sendto_ops(NETWORK_BAN_NAME" active for %s",
                       get_client_name(acptr, FALSE));
            ircsprintf(fbuf, NETWORK_BANNED_NAME": %s", reason);
            exit_client(acptr, acptr, &me, fbuf);
            i--;
        }
    }
        
    return 0;
}
  
int 
m_rakill(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
{
    struct userBan *ban, *oban;

    if(!IsServer(sptr))
        return 0;

    /* just quickly find the akill and be rid of it! */
    if(parc<3) 
    {
        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
                   "RAKILL");
        return 0;
    }

    if(!IsULine(sptr)) 
    {
        sendto_serv_butone(&me, ":%s GLOBOPS :Non-ULined server %s trying to "
                           "RAKILL!",  me.name, sptr->name);
        send_globops("From %s: Non-ULined server %s trying to RAKILL!",
                     me.name,
                     sptr->name);
        return 0;
    }

    ban = make_hostbased_ban(parv[2], parv[1]);
    if(!ban)
       return 0;

    ban->flags |= UBAN_NETWORK;
    oban = find_userban_exact(ban, UBAN_NETWORK);
    if(oban)
    {
       remove_userban(oban);
       userban_free(oban);
    }

    userban_free(ban);

    sendto_serv_butone(cptr, ":%s RAKILL %s %s", sptr->name, parv[1], parv[2]);
    return 0;
}

        
/*
 * RPL_NOWON   - Online at the moment (Succesfully added to WATCH-list)
 * RPL_NOWOFF  - Offline at the moement (Succesfully added to WATCH-list)
 * RPL_WATCHOFF   - Succesfully removed from WATCH-list.
 * ERR_TOOMANYWATCH - Take a guess :>  Too many WATCH entries.
 */
static void 
show_watch(aClient *cptr, char *name, int rpl1, int rpl2) 
{
    aClient *acptr;     
    
    if ((acptr = find_person(name, NULL)))
        sendto_one(cptr, rpl_str(rpl1), me.name, cptr->name,
                   acptr->name, acptr->user->username,
                   acptr->user->host, acptr->lasttime);
    else
        sendto_one(cptr, rpl_str(rpl2), me.name, cptr->name,
                   name, "*", "*", 0);
}
        
/* m_watch */
int 
m_watch(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
{
    aClient  *acptr;
    char  *s, *p, *user;
    char def[2] = "l";
        
    if (parc < 2) 
    {
        /* Default to 'l' - list who's currently online */
        parc = 2;
        parv[1] = def;
    }
    
    for (p = NULL, s = strtoken(&p, parv[1], ", "); s;
         s = strtoken(&p, NULL, ", ")) 
    {
        if ((user = (char *)strchr(s, '!')))
            *user++ = '\0'; /* Not used */
                
        /*
         * Prefix of "+", they want to add a name to their WATCH
         * list. 
         */
        if (*s == '+') 
        {
            if (*(s+1)) 
            {
                if ((sptr->watches >= MAXWATCH) && !IsAnOper(sptr))
                {
                    sendto_one(sptr, err_str(ERR_TOOMANYWATCH),
                               me.name, cptr->name, s+1);                                       
                    continue;
                }                               
                add_to_watch_hash_table(s+1, sptr);
            }
            show_watch(sptr, s+1, RPL_NOWON, RPL_NOWOFF);
            continue;
        }
        
        /*
         * Prefix of "-", coward wants to remove somebody from their
         * WATCH list.  So do it. :-)
         */
        if (*s == '-') 
        {
            del_from_watch_hash_table(s+1, sptr);
            show_watch(sptr, s+1, RPL_WATCHOFF, RPL_WATCHOFF);
            continue;
        }
                                        
        /*
         * Fancy "C" or "c", they want to nuke their WATCH list and start
         * over, so be it.
         */
        if (*s == 'C' || *s == 'c') 
        {
            hash_del_watch_list(sptr);
            continue;
        }
                
        /*
         * Now comes the fun stuff, "S" or "s" returns a status report of
         * their WATCH list.  I imagine this could be CPU intensive if its
         * done alot, perhaps an auto-lag on this?
         */
        if (*s == 'S' || *s == 's') 
        {
            Link *lp;
            aWatch *anptr;
            int  count = 0;
                                                        
            /*
             * Send a list of how many users they have on their WATCH list
             * and how many WATCH lists they are on.
             */
            anptr = hash_get_watch(sptr->name);
            if (anptr)
                for (lp = anptr->watch, count = 1; (lp = lp->next); count++);
            sendto_one(sptr, rpl_str(RPL_WATCHSTAT), me.name, parv[0],
                       sptr->watches, count);
                        
            /*
             * Send a list of everybody in their WATCH list. Be careful
             * not to buffer overflow.
             */
            if ((lp = sptr->watch) == NULL) 
            {
                sendto_one(sptr, rpl_str(RPL_ENDOFWATCHLIST), me.name, parv[0],
                           *s);
                continue;
            }
            *buf = '\0';
            strcpy(buf, lp->value.wptr->nick);
            count = strlen(parv[0])+strlen(me.name)+10+strlen(buf);
            while ((lp = lp->next)) 
            {
                if (count+strlen(lp->value.wptr->nick)+1 > BUFSIZE - 2) 
                {
                    sendto_one(sptr, rpl_str(RPL_WATCHLIST), me.name,
                               parv[0], buf);
                    *buf = '\0';
                    count = strlen(parv[0])+strlen(me.name)+10;
                }
                strcat(buf, " ");
                strcat(buf, lp->value.wptr->nick);
                count += (strlen(lp->value.wptr->nick)+1);
            }
            sendto_one(sptr, rpl_str(RPL_WATCHLIST), me.name, parv[0], buf);
            sendto_one(sptr, rpl_str(RPL_ENDOFWATCHLIST), me.name, parv[0],
                       *s);
            continue;
        }
                
        /*
         * Well that was fun, NOT.  Now they want a list of everybody in
         * their WATCH list AND if they are online or offline? Sheesh,
         * greedy arn't we?
         */
        if (*s == 'L' || *s == 'l') 
        {
            Link *lp = sptr->watch;
                        
            while (lp) 
            {
                if ((acptr = find_person(lp->value.wptr->nick, NULL)))
                    sendto_one(sptr, rpl_str(RPL_NOWON), me.name, parv[0],
                               acptr->name, acptr->user->username,
                               acptr->user->host, acptr->tsinfo);
                /*
                 * But actually, only show them offline if its a capital
                 * 'L' (full list wanted).
                 */
                else if (IsUpper(*s))
                    sendto_one(sptr, rpl_str(RPL_NOWOFF), me.name, parv[0],
                               lp->value.wptr->nick, "*", "*",
                               lp->value.wptr->lasttime);
                lp = lp->next;
            }
                        
            sendto_one(sptr, rpl_str(RPL_ENDOFWATCHLIST), me.name, parv[0],
                       *s);
            continue;
        }
        /* Hmm.. unknown prefix character.. Ignore it. :-) */
    }
        
    return 0;
}

int 
m_sqline(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
{
    struct simBan *ban;
    unsigned int flags;
    char *reason;

    if(!(IsServer(sptr) || IsULine(sptr)))
        return 0;

    if(parc < 2) 
    {
        /* we should not get malformed sqlines.  complain loud */
        sendto_realops("%s attempted to add sqline with insufficient params!"
                       " (ignored) Contact coders!", sptr->name);
        return 0;
    }
        
    /* if we have any Q:lines (SQ or Q) that match
     * this Q:line, just return (no need to waste cpu */

    flags = SBAN_NETWORK;
    if(parv[1][0] == '#')
       flags |= SBAN_CHAN;
    else
       flags |= SBAN_NICK;
    ban = make_simpleban(flags, parv[1]);
    if(!ban)
    {
        sendto_realops("make_simpleban(%s) failed on sqline!", parv[1]);
        return 0;
    }

    reason = BadPtr(parv[2]) ? "Reserved" : parv[2];
    ban->reason = NULL;

    if (find_simban_exact(ban) == NULL)
    {
        ban->reason = (char *) MyMalloc(strlen(reason) + 1);
        strcpy(ban->reason, reason);
        ban->timeset = NOW;
        add_simban(ban);
    }
    else
        simban_free(ban);

    sendto_serv_butone(cptr, ":%s SQLINE %s :%s", sptr->name, parv[1],
                       reason);
    return 0;
}
        
int 
m_unsqline(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
{
    int matchit = 0;
    char *mask;
    
    if(!(IsServer(sptr) || IsULine(sptr)))
        return 0;
    
    if(parc < 2) 
    {
        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
                   "UNSQLINE");
        return 0;
    }
    
    if (parc == 3) 
    {
        matchit = atoi(parv[1]);
        mask = parv[2];
    }
    else
        mask = parv[1];

    /* special case for "UNSQLINE 1 :*" */
    if(mycmp(mask, "*") == 0 && matchit)
    {
       remove_simbans_match_mask(SBAN_CHAN|SBAN_NETWORK, mask, 1);
       remove_simbans_match_mask(SBAN_NICK|SBAN_NETWORK, mask, 1);
    }
    else if(mask[0] == '#')
       remove_simbans_match_mask(SBAN_CHAN|SBAN_NETWORK, mask, matchit);
    else
       remove_simbans_match_mask(SBAN_NICK|SBAN_NETWORK, mask, matchit);

    if (parc == 3) 
        sendto_serv_butone(cptr, ":%s UNSQLINE %d :%s", sptr->name, matchit,
                           mask);
    else
        sendto_serv_butone(cptr, ":%s UNSQLINE :%s", sptr->name, mask);
    return 0;
}

int m_sgline(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
{
    struct simBan *ban;
    int len;
    unsigned int flags;
    char *mask, *reason;
    
    if(!(IsServer(sptr) || IsULine(sptr)))
        return 0;

    if(parc<3) 
    {
        sendto_realops("%s attempted to add sgline with insufficient params!"
                       " (ignored) Contact coders!", sptr->name);
        return 0;
    }
        
    len=atoi(parv[1]);
    mask=parv[2];
    if ((strlen(mask) > len) && (mask[len])==':') 
    {
        mask[len] = '\0';
        reason = mask+len+1;
    } 
    else 
    { /* Bogus */
        return 0;
    }
    
    /* if we have any G:lines (SG or G) that match
     * this G:line, just return (no need to waste cpu */

    flags = SBAN_NETWORK|SBAN_GCOS;
    ban = make_simpleban(flags, mask);
    if(!ban)
    {
        sendto_realops("make_simpleban(%s) failed on sgline!", parv[1]);
        return 0;
    }

    if(BadPtr(reason))
       reason = "Reserved";
    ban->reason = NULL;

    if (find_simban_exact(ban) == NULL)
    {
        ban->reason = (char *) MyMalloc(strlen(reason) + 1);
        strcpy(ban->reason, reason);
        ban->timeset = NOW;
        add_simban(ban);
    }
    else
        simban_free(ban);

    sendto_serv_butone(cptr, ":%s SGLINE %d :%s:%s", sptr->name, len,
                       mask, reason);
    return 0;
}
        
int 
m_unsgline(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
{
    int matchit=0;
    char *mask;
   
    if(!(IsServer(sptr) || IsULine(sptr)))
        return 0;
   
    if(parc<2) 
    {
        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
                   "UNSGLINE");
        return 0;
    }
   

    if (parc==3) 
    {
        matchit=atoi(parv[1]);
        mask=parv[2];
    }
    else
        mask=parv[1];
    
    remove_simbans_match_mask(SBAN_GCOS|SBAN_NETWORK, mask, matchit);
    
    if (parc==3)
        sendto_serv_butone(cptr, ":%s UNSGLINE %d :%s", sptr->name, matchit,
                           mask);
    else
        sendto_serv_butone(cptr, ":%s UNSGLINE :%s",sptr->name,mask);
    return 0;
}

/* svident and setident are based on the code by Mouse
 *  Added on 04/22/05 -Sheik
 * m_svident
 *  parv[0] = sender
 *  parv[1] = nickname
 *  parv[2] = identname
 */


int m_svident(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aClient *acptr;

    if (check_registered(sptr))
	return 0;
    if (MyClient(sptr))
    {
	if (!IsAnOper(sptr))
	{
	    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
	    return 0;
	}
    }
    if (parc != 3)
	return 0;
    if (strlen(parv[2]) < 1)
    {
	sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SVIDENT");
	return 0;
    }
    if (strlen(parv[2]) > (USERLEN - 1))
    {
	sendto_one(sptr, ":%s NOTICE %s :*** Notice -- Idents are limited to %i characters.", me.name, sptr->name, (USERLEN-1));
	return 0;
    }



    if ((acptr = find_person(parv[1], NULL))) {

        sendto_ops("%s changed the virtual ident of %s (%s@%s) to be %s", 
        sptr->name, acptr->name, acptr->user->username,
        acptr->user->host, parv[2]);
	strcpy(acptr->user->username, parv[2]);

	sendto_serv_butone(cptr, ":%s SVIDENT %s :%s", sptr->name, acptr->name, parv[2]);

        sendto_one(sptr, ":%s NOTICE %s :*** Notice -- Your Ident is now (%s)", me.name,
        acptr->user->host, parv[2]);

	return 0;
    }
    else
    {
	sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, sptr->name, parv[1]);
	return 0;
    }
    return 0;
}


/*
 * m_setident
 *  
 *  
 *  
 */

int m_setident(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aClient *acptr;

    if (check_registered(sptr))
	return 0;
    if (MyClient(sptr))
    {
	if (!IsAnOper(sptr))
	{
	    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
	    return 0;
	}
    }
    if (parc != 3)
	return 0;
    if (strlen(parv[2]) < 1)
    {
	sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SVIDENT");
	return 0;
    }
    if (strlen(parv[2]) > (USERLEN - 1))
    {
	sendto_one(sptr, ":%s NOTICE %s :*** Notice -- Idents are limited to %i characters.", me.name, sptr->name, (USERLEN-1));
	return 0;
    }



    if ((acptr = find_person(parv[1], NULL))) {

        sendto_ops("%s changed the virtual ident of %s (%s@%s) to be %s", 
        sptr->name, acptr->name, acptr->user->username,
        acptr->user->host, parv[2]);
	strcpy(acptr->user->username, parv[2]);

	sendto_serv_butone(cptr, ":%s SVIDENT %s :%s", sptr->name, acptr->name, parv[2]);

        sendto_one(sptr, ":%s NOTICE %s :*** Notice -- Your Ident is now (%s)", me.name,
        acptr->user->host, parv[2]);

	return 0;
    }
    else
    {
	sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, sptr->name, parv[1]);
	return 0;
    }
    return 0;
}

/*
** m_ircops
** Based on m_ircops by HAMLET
** Modified for solid-ircd  by The_Sphere 
**
*/

#ifdef IRCOP_LIST

int
m_ircops (aClient *cptr, aClient *sptr, int parc, char *parv[])
{
	aClient *acptr;
	char buf[BUFSIZE];
	int locals = 0;
	int globals = 0;
	if (!MyClient(sptr))
		return 0;
	strcpy (buf, "+-------------------------+ Online IRC Operators +-------------------------------+");
	sendto_one (sptr, rpl_str (RPL_IRCOPS), me.name, parv[0], buf);
	strcpy (buf, "\2Nick                         Status                Server\2");
	sendto_one (sptr, rpl_str (RPL_IRCOPS), me.name, parv[0], buf);
	strcpy (buf, "+--------------------------------------------------------------------------------+");
	sendto_one (sptr, rpl_str (RPL_IRCOPS), me.name, parv[0], buf);

	for (acptr = client; acptr; acptr = acptr->next)
	{
		if (!(IsULine (acptr)) && (IsAnOper(acptr) && !IsUmodeH (acptr)))
		{
			if (!acptr->user)
				continue;
		
		 ircsprintf (buf, "\2%-30s\2  %s  %-8s  %s",
                  acptr->name ? acptr->name : "<Unknown>",
                  IsOper (acptr) ? "Global" : "Local",
                  acptr->user->away ? "(AWAY)" : "", acptr->user->server);

			sendto_one (sptr, rpl_str (RPL_IRCOPS), me.name, parv[0], buf);
			sendto_one (sptr, rpl_str (RPL_IRCOPS), me.name, parv[0], "-");
			
			if (IsOper (acptr))
				globals++;
			else
				locals++;
		}
	}
	
	ircsprintf (buf,
              "Total: \2%d\2 IRCOp%s connected - \2%d\2 Globa%s, \2%d\2 Loca%s",
              globals + locals, (globals + locals) > 1 ? "s" : "", globals,
              globals > 1 ? "ls" : "l", locals, locals > 1 ? "ls" : "l");
	sendto_one (sptr, rpl_str (RPL_IRCOPS), me.name, parv[0], buf);
	strcpy (buf, "+--------------------------------------------------------------------------------+");
	sendto_one (sptr, rpl_str (RPL_IRCOPS), me.name, parv[0], buf);
	sendto_one (sptr, rpl_str (RPL_ENDOFIRCOPS), me.name, parv[0]);

	return 0;
}

#endif

/*
* dump_map (used by m_map)
*/

void dump_map(aClient * cptr, aClient * server, char *mask, int prompt_length, int length)
{
	static char prompt[64];
	char *p = &prompt[prompt_length];
	int cnt = 0, local = 0;
	aClient *acptr;

	*p = '\0';

	if (prompt_length > 60)
		sendto_one(cptr, rpl_str(RPL_MAPMORE), me.name, cptr->name, prompt, server->name);
	else
	{
		for (acptr = client; acptr; acptr = acptr->next)
		{
			if (IsPerson(acptr))
			{
				++cnt;
				if (!strcmp(acptr->user->server, server->name))
					++local;
			}
		}
		sendto_one(cptr, rpl_str(RPL_MAP), me.name, 
			cptr->name, prompt, length, server->name, 
			local, (local * 100) / cnt);
		cnt = 0;
	}
	if (prompt_length > 0)
	{
		p[-1] = ' ';
		if (p[-2] == '`')
			p[-2] = ' ';
	}
	if (prompt_length > 60)
		return;
	strcpy (p, "|-");
	for (acptr = client; acptr; acptr = acptr->next)
	{
		if (!IsServer(acptr) || strcmp(acptr->serv->up, server->name))
			continue;
		if (match(mask, acptr->name))
			acptr->flags &= ~FLAGS_MAP;
		else
		{
			acptr->flags |= FLAGS_MAP;
			cnt++;
		}
	}
	for (acptr = client; acptr; acptr = acptr->next)
	{
		if (IsULine(acptr) && !IsAnOper(cptr))
			continue;
		if (!(acptr->flags & FLAGS_MAP) || !IsServer(acptr) || strcmp(acptr->serv->up, server->name))
			continue;
		if (--cnt == 0)
			*p = '`';
		dump_map(cptr, acptr, mask, prompt_length + 2, length - 2);
	}
	if (prompt_length > 0)
		p[-1] = '-';
}

/*
* m_map()
*  parv[0] = sender prefix
*  parv[1] = server mask
*/
int m_map(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
	aClient *acptr;
	int longest = strlen(me.name);

	if (check_registered (sptr))
		return 0;

	if (!IsAnOper(cptr))
	{
		sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
		return 0;
	}

	if (parc < 2)
		parv[1] = "*";

	for (acptr = client; acptr; acptr = acptr->next)
		if (IsServer(acptr) && (strlen(acptr->name) + acptr->hopcount * 2) > longest)
			longest = strlen(acptr->name) + acptr->hopcount * 2;

	if (longest > 60)
		longest = 60;

	longest += 2;
	dump_map(sptr, &me, parv[1], 0, longest);
	sendto_one(sptr, rpl_str(RPL_MAPEND), me.name, parv[0]);
	return 0;
}



u_long
memcount_s_serv(MCs_serv *mc)
{
    aMotd *m;

    mc->file = __FILE__;

    for (m = motd; m; m = m->next)
    {
        mc->motd.c++;
        mc->motd.m += sizeof(*m);
    }
    mc->total.c += mc->motd.c;
    mc->total.m += mc->motd.m;

    for (m = shortmotd; m; m = m->next)
    {
        mc->shortmotd.c++;
        mc->shortmotd.m += sizeof(*m);
    }
    mc->total.c += mc->shortmotd.c;
    mc->total.m += mc->shortmotd.m;

    for (m = helpfile; m; m = m->next)
    {
        mc->help.c++;
        mc->help.m += sizeof(*m);
    }
    mc->total.c += mc->help.c;
    mc->total.m += mc->help.m;

    return mc->total.m;
}



syntax highlighted by Code2HTML, v. 0.9.1