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

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

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include <stdio.h>
#include "numeric.h"
#include "dh.h"
#include "zlink.h"
#include "fds.h"
#include "memcount.h"

/*
 * STOP_SENDING_ON_SHORT_SEND:
 * Treat a short send as a blocked socket
 * Might not always be a good idea, esp. if using something that's
 * Edge triggered (ie, kqueue, epoll, etc)
 */
#undef STOP_SENDING_ON_SHORT_SEND

#ifdef ALWAYS_SEND_DURING_SPLIT
extern int currently_processing_netsplit;
#endif

static char sendbuf[2048];
static char remotebuf[2048];
static char selfbuf[256];
static int  send_message(aClient *, char *, int, void*);

#ifdef HAVE_ENCRYPTION_ON
/*
 * WARNING:
 * Please be aware that if you are using both encryption
 * and ziplinks, rc4buf in send.c MUST be the same size
 * as zipOutBuf in zlink.c!
 */
static char rc4buf[16384];
#endif

static int  sentalong[MAXCONNECTIONS];
static int  sent_serial;

void init_send()
{
   memset(sentalong, 0, sizeof(int) * MAXCONNECTIONS);
   sent_serial = 0;
}

/* This routine increments our serial number so it will
 * be unique from anything in sentalong, no need for a memset
 * except for every MAXINT calls - lucas
 */

/* This should work on any OS where an int is 32 bit, I hope.. */

#define HIGHEST_SERIAL INT_MAX

#define INC_SERIAL if(sent_serial == HIGHEST_SERIAL) \
   { memset(sentalong, 0, sizeof(sentalong)); sent_serial = 0; } \
   sent_serial++;


/*
 * dead_link
 *
 * somewhere along the lines of sending out, there was an error.
 * we can't close it from the send loop, so mark it as dead
 * and close it from the main loop.
 *
 * if this link is a server, tell routing people.
 */

static int dead_link(aClient *to, char *notice, int sockerr) 
{
    int errtmp = errno;  /* so we don't munge this later */
    
    to->sockerr = sockerr;
    to->flags |= FLAGS_DEADSOCKET;
    /*
     * If because of BUFFERPOOL problem then clean dbuf's now so that
     * notices don't hurt operators below.
     */
    SBufClear(&to->recvQ);
    SBufClear(&to->sendQ);
    /* Ok, if the link we're dropping is a server, send a routing
     * notice..
     */
    if (IsServer(to) && !(to->flags & FLAGS_CLOSING))
    {
        char fbuf[512];

        ircsprintf(fbuf, "from %s: %s", me.name, notice);
        sendto_gnotice(fbuf, get_client_name(to, HIDEME), strerror(errtmp));
        ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, notice);
        sendto_serv_butone(to, fbuf, get_client_name(to, HIDEME),
                           strerror(errtmp));
    }  
 
    return -1;
}

/*
 * send_message 
 * Internal utility which delivers one message buffer to the 
 * socket. Takes care of the error handling and buffering, ifneeded.
 */
static int send_message(aClient *to, char *msg, int len, void* sbuf) 
{
    static int  SQinK;
    int flag;
    
#ifdef DUMP_DEBUG
    fprintf(dumpfp, "-> %s: %s\n", (to->name ? to->name : "*"), msg);
#endif

    if (to->from)
        to = to->from;

    flag = (!sbuf || ZipOut(to) || IsRC4OUT(to)) ? 1 : 0;

    if (flag == 1)
    {
        if(IsServer(to) || IsNegoServer(to))
        {
            if(len>510) 
            {
                msg[511]='\n';
                msg[512]='\0';
                len=512;
            }
            else 
            {
                msg[len] = '\n';
                msg[len+1] = '\0';
                len++;
            }   
        }
        else
        {
            if(len>509) 
            {
                msg[510]='\r';
                msg[511]='\n';
                msg[512]='\0';
                len=512;
            }
            else 
            {
                msg[len] = '\r';
                msg[len+1] = '\n';
                msg[len+2] = '\0';
                len+=2;
            }   
        }
    }

    if (IsMe(to)) 
    {
        strncpyzt(selfbuf, msg, sizeof(selfbuf));
        sendto_ops("Trying to send to myself! [%s]", selfbuf);
        return 0;
    }
   
    if (IsDead(to))
        return 0;

    if (to->class && (SBufLength(&to->sendQ) > to->class->maxsendq))
    {
        /* this would be a duplicate notice, but it contains some useful 
         * information thatwould be spamming the rest of the network.
         * Kept in. - lucas
         */
        if (IsServer(to)) 
            sendto_ops("Max SendQ limit exceeded for %s: %d > %d",
                       get_client_name(to, HIDEME), SBufLength(&to->sendQ),
                       to->class->maxsendq);
        to->flags |= FLAGS_SENDQEX;
        return dead_link(to, "Max Sendq exceeded for %s, closing link", 0);
    }
    
    /*
     * Update statistics. The following is slightly incorrect
     * because it counts messages even if queued, but bytes only
     * really sent. Queued bytes get updated in SendQueued.
     */
    to->sendM += 1;
    me.sendM += 1;
    if (to->lstn)
        to->lstn->sendM += 1;

    if(ZipOut(to))
    {
        int ldata = (to->flags & FLAGS_BURST);

        msg = zip_output(to->serv->zip_out, msg, &len, 0, &ldata);
        if(len == -1)
        {
            sendto_realops("Zipout error for %s: (%d) %s\n", to->name, ldata,
                           msg);
            return dead_link(to, "Zip output error for %s", IRCERR_ZIP);
        }
        
        if(len == 0)
            return 0;
    }

#ifdef HAVE_ENCRYPTION_ON
    if(IsRC4OUT(to))
    {
        /* don't destroy the data in 'msg' */
        rc4_process_stream_to_buf(to->serv->rc4_out, (const unsigned char *) msg, (unsigned char *) rc4buf, len);
        msg = rc4buf;
    }
#endif

    if (!sbuf || flag)
    {
        if (sbuf_put(&to->sendQ, msg, len) < 0)
            return dead_link(to, "Buffer allocation error for %s,"
                                 " closing link", IRCERR_BUFALLOC);
    }
    else
    {
        if (sbuf_put_share(&to->sendQ, sbuf) < 0)
            return dead_link(to, "Buffer allocation error for %s,"
                                 " closing link", IRCERR_BUFALLOC);
    }

    /*
     * This little bit is to stop the sendQ from growing too large
     * when there is no need for it to. Thus we call send_queued()
     * every time 2k has been added to the queue since the last
     * non-fatal write. Also stops us from deliberately building a
     * large sendQ and then trying to flood that link with data
     * (possible during the net relinking done by servers with a large
     * load).
     */
    /*
     * Well, let's try every 4k for clients, and immediately for servers
     * -Taner
     */
    /*
     * Let's not waste time trying this on anyone who has a blocking socket.
     * Also, let's send every 8k for servers, since there's lots of traffic
     * there and we'd like to make it more efficient. - lucas
     */

    if(to->flags & FLAGS_BLOCKED)
       return 0;

#ifdef ALWAYS_SEND_DURING_SPLIT
    if (currently_processing_netsplit)
    {
        send_queued(to);
        return 0;
    }
#endif

    SQinK = (SBufLength(&to->sendQ) >> 10);
    if (IsServer(to)) 
    {
        if (SQinK > (to->lastsq + 8))
            send_queued(to);
    }
    else 
    {
        if (SQinK > (to->lastsq + 4))
            send_queued(to);
    }
    return 0;
}


/*
 * send_queued 
 * This function is called from the main select-loop (or whatever) 
 * when there is a chance the some output would be possible. This 
 * attempts to empty the send queue as far as possible...
 */
int send_queued(aClient *to)
{
    char       *msg;
    int         len, rlen;
    int more_data = 0; /* the hybrid approach.. */
#ifdef WRITEV_IOV
    struct iovec iov[WRITEV_IOV];
#endif
        
    /*
     * Once socket is marked dead, we cannot start writing to it,
     * even if the error is removed...
     * this should never happen.
     */
    if (IsDead(to)) 
        return -1;

    if(ZipOut(to) && zip_is_data_out(to->serv->zip_out))
    {
        if(SBufLength(&to->sendQ))
            more_data = 1;
        else
        {
            int ldata = (to->flags & FLAGS_BURST);

            msg = zip_output(to->serv->zip_out, NULL, &len, 1, &ldata);
            if(len == -1)
            {
                sendto_realops("Zipout error for %s: (%d) %s\n", to->name,
                               ldata, msg);
                return dead_link(to, "Zip output error for %s", IRCERR_ZIP);
            }

#ifdef HAVE_ENCRYPTION_ON
            if(IsRC4OUT(to))
                rc4_process_stream(to->serv->rc4_out, (unsigned char *) msg, len);
#endif
            /* silently stick this on the sendq... */
            if (!sbuf_put(&to->sendQ, msg, len))
                return dead_link(to, "Buffer allocation error for %s",
                                 IRCERR_BUFALLOC);
        }
    }
   
    while (SBufLength(&to->sendQ) > 0) 
    {
#ifdef WRITEV_IOV
        len = sbuf_mapiov(&to->sendQ, iov);
        if ((rlen = deliver_it(to, iov, len)) < 0)
#else
        msg = sbuf_map(&to->sendQ, &len);
        if ((rlen = deliver_it(to, msg, len)) < 0)
#endif
            return dead_link(to, "Write error to %s, closing link (%s)", errno);
        sbuf_delete(&to->sendQ, rlen);
        to->lastsq = (SBufLength(&to->sendQ) >> 10);

#ifdef STOP_SENDING_ON_SHORT_SEND
        if (rlen < len)
        {
            /* Treat this socket as blocking */
            to->flags |= FLAGS_BLOCKED;
            set_fd_flags(to->fd, FDF_WANTWRITE);
#else
        if (rlen == 0)
        {
            /* Socket is blocking... */
#endif
            break;
        }

        if(more_data && SBufLength(&to->sendQ) == 0)
        {
            int ldata = (to->flags & FLAGS_BURST);
            
            more_data = 0;
            
            msg = zip_output(to->serv->zip_out, NULL, &len, 1, &ldata);
            if(len == -1)
            {
                sendto_realops("Zipout error for %s: (%d) %s\n", to->name,
                               ldata, msg);
                return dead_link(to, "Zip output error for %s", IRCERR_ZIP);
            }
            
#ifdef HAVE_ENCRYPTION_ON
            if(IsRC4OUT(to))
                rc4_process_stream(to->serv->rc4_out, (unsigned char *) msg, len);
#endif
            /* silently stick this on the sendq... */
            if (!sbuf_put(&to->sendQ, msg, len))
                return dead_link(to, "Buffer allocation error for %s",
                                 IRCERR_BUFALLOC);        
        }
    }
    
    if ((to->flags & FLAGS_SOBSENT) && IsBurst(to)
         && SBufLength(&to->sendQ) < 20480) 
    {
        if (!(to->flags & FLAGS_BURST))
        {
            to->flags &= (~FLAGS_SOBSENT);
            sendto_one(to, "BURST %d", SBufLength(&to->sendQ));
        }
    }
    return (IsDead(to)) ? -1 : 0;
}

/* send message to single client */
void sendto_one(aClient *to, char *pattern, ...) 
{
    va_list vl;
    int len;            /* used for the length of the current message */
    
    va_start(vl, pattern);
    len = ircvsprintf(sendbuf, pattern, vl);
   
    if (to->from)
        to = to->from;
    if (IsMe(to)) 
    {
        strncpyzt(selfbuf, sendbuf, sizeof(selfbuf));
        sendto_ops("Trying to send [%s] to myself!", selfbuf);
        return;
    }
    send_message(to, sendbuf, len, NULL);
    va_end(vl);
}

/* send to an aliased super target */
void sendto_alias(AliasInfo *ai, aClient *from, char *pattern, ...)
{
    aClient *to;
    va_list  vl;
    int      len;

    va_start(vl, pattern);
    to = ai->client->from;

    /* use shortforms only for non-super or capable super servers */
    if (!IsULine(to) || ((confopts & FLAGS_SERVHUB)
                         && (to->serv->uflags & ULF_SFDIRECT)))
        len = ircsprintf(sendbuf, ":%s %s :", from->name, ai->shortform);
    else
#ifdef PASS_SERVICES_MSGS
        /* target distinguishes between nick@server and nick */
        len = ircsprintf(sendbuf, ":%s PRIVMSG %s@%s :", from->name, ai->nick,
                         ai->server);
#else
        len = ircsprintf(sendbuf, ":%s PRIVMSG %s :", from->name, ai->nick);
#endif

    len += ircvsprintf(sendbuf+len, pattern, vl);
    send_message(to, sendbuf, len, NULL);
    va_end(vl);
}

void vsendto_one(aClient *to, char *pattern, va_list vl) 
{
    int len;            /* used for the length of the current message */
   
    len = ircvsprintf(sendbuf, pattern, vl);
   
    if (to->from)
        to = to->from;
    if (IsMe(to) && to->fd >= 0) 
    {
        strncpyzt(selfbuf, sendbuf, sizeof(selfbuf));
        sendto_ops("Trying to send [%s] to myself!", selfbuf);
        return;
    }
    send_message(to, sendbuf, len, NULL);
}

/* prefix_buffer
 *
 * take varargs and dump prefixed message into a buffer
 * remote: 1 if client is remote, 0 if local
 * from: the client sending the message
 * prefix: the prefix as specified (parv[0] usually)
 * buffer: the buffer to dump this into (NO BOUNDS CHECKING!)
 * pattern: varargs pattern
 * vl: varargs variable list with one arg taken already
 */
static inline int prefix_buffer(int remote, aClient *from, char *prefix,
                                char *buffer, char *pattern, va_list vl)
{
    char *p;      /* temp pointer */
    int msglen;   /* the length of the message we end up with */
    int sidx = 1; /* start at offset 1 */

    *buffer = ':';

    if(!remote && IsPerson(from))
    {
        int flag = 0;
        anUser *user = from->user;

        for(p = from->name; *p; p++)
                buffer[sidx++] = *p;

        if (user)
        {
            if (*user->username) 
            {
                buffer[sidx++] = '!';
                for(p = user->username; *p; p++)
                    buffer[sidx++] = *p;
            }
            if (*user->host && !MyConnect(from)) 
            {
                buffer[sidx++] = '@';
                for(p = user->host; *p; p++)
                    buffer[sidx++] = *p;
                flag = 1;
            }
        }
       if (!flag && MyConnect(from) && *user->host) 
        {
            buffer[sidx++] = '@';
            for(p = user->host; *p; p++)
                buffer[sidx++] = *p;
        }
    }
    else
    {
            for(p = prefix; *p; p++)
                buffer[sidx++] = *p;
    }

    msglen = ircvsprintf(&buffer[sidx], pattern + 3, vl);
    msglen += sidx;

    return msglen;
}

static inline int check_fake_direction(aClient *from, aClient *to)
{
    if (!MyClient(from) && IsPerson(to) && (to->from == from->from)) 
    {
        if (IsServer(from)) 
        {
            sendto_ops("Message to %s[%s] dropped from %s (Fake Direction)",
                       to->name, to->from->name, from->name);
            return -1;
        }

        sendto_ops("Ghosted: %s[%s@%s] from %s[%s@%s] (%s)", to->name,
                   to->user->username, to->user->host, from->name,
                   from->user->username, from->user->host, to->from->name);
        sendto_serv_butone(NULL, ":%s KILL %s :%s (%s[%s@%s] Ghosted %s)",
                           me.name, to->name, me.name, to->name,
                           to->user->username, to->user->host, to->from->name);
        
        to->flags |= FLAGS_KILLED;
        exit_client(NULL, to, &me, "Ghosted client");

        if (IsPerson(from))
            sendto_one(from, err_str(ERR_GHOSTEDCLIENT), me.name, from->name,
                       to->name, to->user->username, to->user->host, to->from);
        return -1;
    }

    return 0;
}


void sendto_channel_butone(aClient *one, aClient *from, aChannel *chptr,
                           char *pattern, ...) 
{
    chanMember *cm;
    aClient *acptr;
    int i;
    int didlocal = 0, didremote = 0;
    va_list vl;
    char *pfix;
    void *share_bufs[2] = { 0, 0 };
   
    va_start(vl, pattern);

    pfix = va_arg(vl, char *);

    INC_SERIAL
    for (cm = chptr->members; cm; cm = cm->next) 
    {
        acptr = cm->cptr;
        if (acptr->from == one)
           {
            continue; /* ...was the one I should skip */
}
if (IsDeaf(acptr)) /* Deaf mode, imported from UltimateIRCD. -Sheik */
    {
      continue;                 /* Skip deaf clients */
    }


        if((confopts & FLAGS_SERVHUB) && IsULine(acptr))
            continue;

        i = acptr->from->fd;
        if (MyClient(acptr)) 
        {
            if(!didlocal)
            {
                didlocal = prefix_buffer(0, from, pfix, sendbuf, 
                                         pattern, vl);
                sbuf_begin_share(sendbuf, didlocal, &share_bufs[0]);
            }
            if(check_fake_direction(from, acptr))
                    continue;
            
            send_message(acptr, sendbuf, didlocal, share_bufs[0]);
            sentalong[i] = sent_serial;
        }
        else 
        {
            /*
             * Now check whether a message has been sent to this remote
             * link already
             */
            if(!didremote)
            {
                didremote = prefix_buffer(1, from, pfix, remotebuf, 
                                          pattern, vl);
                sbuf_begin_share(remotebuf, didremote, &share_bufs[1]);
            }
            
            if(check_fake_direction(from, acptr))
                    continue;
            
            if (sentalong[i] != sent_serial) 
            {
                send_message(acptr, remotebuf, didremote, share_bufs[1]);
                sentalong[i] = sent_serial;
            }
        }
    }
    
    sbuf_end_share(share_bufs, 2);    
    va_end(vl);
    return;
}

/*
 * Like sendto_channel_butone, but sends to all servers but 'one'
 * that have clients in this channel.
 */
void sendto_channel_remote_butone(aClient *one, aClient *from, aChannel *chptr,
                                  char *pattern, ...) 
{
    chanMember *cm;
    aClient *acptr;
    int i;
    int didremote = 0;
    va_list vl;
    char *pfix;
    void *share_buf = NULL;
   
    va_start(vl, pattern);

    pfix = va_arg(vl, char *);

    INC_SERIAL
    for (cm = chptr->members; cm; cm = cm->next) 
    {
        acptr = cm->cptr;
        if (acptr->from == one)
            continue; /* ...was the one I should skip */

        if((confopts & FLAGS_SERVHUB) && IsULine(acptr))
            continue;

        i = acptr->from->fd;
        if (!MyClient(acptr)) 
        {
            /*
             * Now check whether a message has been sent to this remote
             * link already
             */
            if(!didremote)
            {
                didremote = prefix_buffer(1, from, pfix, remotebuf,
                                          pattern, vl);
                sbuf_begin_share(remotebuf, didremote, &share_buf);
            }
            
            if(check_fake_direction(from, acptr))
                    continue;
            
            if (sentalong[i] != sent_serial) 
            {
                send_message(acptr, remotebuf, didremote, share_buf);
                sentalong[i] = sent_serial;
            }
        }
    }
    
    sbuf_end_share(&share_buf, 1);
    
    va_end(vl);
    return;
}

/*
 * sendto_server_butone_services
 * 
 * Send a message to all connected servers except the client 'one' and super
 * servers with the specified flag (if in SERVHUB mode).
 */
void sendto_serv_butone_super(aClient *one, int flag, char *pattern, ...) 
{
    aClient *cptr;
    int k = 0;
    fdlist send_fdlist;
    va_list vl;
    DLink *lp;
    

    va_start(vl, pattern);
    for (lp = server_list; lp; lp = lp->next)
    {
        cptr = lp->value.cptr;

        if ((confopts & FLAGS_SERVHUB) && IsULine(cptr)
            && (!flag || (cptr->serv->uflags & flag)))
            continue;

        if (one && cptr == one->from)
            continue;
        send_fdlist.entry[++k] = cptr->fd;
    }
    send_fdlist.last_entry = k;
    if (k)
        vsendto_fdlist(&send_fdlist, pattern, vl);
    va_end(vl);
    return;
}

#ifdef NOQUIT
/*
 * sendto_non_noquit_servs_butone
 *
 * Send a message to all non-noquit servs
 */
void sendto_non_noquit_servs_butone(aClient *one, char *pattern, ...)
{
    aClient *cptr;
    int k = 0;
    fdlist send_fdlist;
    va_list vl;
    DLink *lp;

    va_start(vl, pattern);
    for(lp = server_list; lp; lp = lp->next)
    {
        cptr = lp->value.cptr;

        if (IsNoquit(cptr) || (one == cptr))
            continue;

        send_fdlist.entry[++k] = cptr->fd;
    }
    send_fdlist.last_entry = k;
    if (k)
        vsendto_fdlist(&send_fdlist, pattern, vl);
    va_end(vl);
    return;
}
#endif

/*
 * sendto_server_butone
 * 
 * Send a message to all connected servers except the client 'one'.
 */
void sendto_serv_butone(aClient *one, char *pattern, ...) 
{
    aClient *cptr;
    int k = 0;
    fdlist send_fdlist;
    va_list vl;
    DLink *lp;
        
    va_start(vl, pattern);
    for(lp = server_list; lp; lp = lp->next)
    {
        cptr = lp->value.cptr;
        if (one && cptr == one->from)
            continue;
        send_fdlist.entry[++k] = cptr->fd;
    }
    send_fdlist.last_entry = k;
    if (k)
        vsendto_fdlist(&send_fdlist, pattern, vl);
    va_end(vl);
    return;
}

/*
 * sendto_common_channels()
 * 
 * Sends a message to all people (inclusing user) on local server who are
 * in same channel with user.
 */
void sendto_common_channels(aClient *from, char *pattern, ...)
{
    Link *channels;
    chanMember *users;
    aClient *cptr;
    va_list vl;
    char *pfix;
    int msglen = 0;
    void *share_buf = NULL;

    va_start(vl, pattern);

    pfix = va_arg(vl, char *);

    INC_SERIAL

    if(from->fd >= 0)
        sentalong[from->fd] = sent_serial;
    
    if (from->user)
    {
        for (channels = from->user->channel; channels; 
             channels = channels->next)
        {
            for (users = channels->value.chptr->members; users; 
                 users = users->next) 
            {
                cptr = users->cptr;
        
                if (!MyConnect(cptr) || sentalong[cptr->fd] == sent_serial)
                        continue;
            
                sentalong[cptr->fd] = sent_serial;
                if (!msglen)
                {
                    msglen = prefix_buffer(0, from, pfix, sendbuf,
                                           pattern, vl);
                    sbuf_begin_share(sendbuf, msglen, &share_buf);
                }
                if (check_fake_direction(from, cptr))
                    continue;
                send_message(cptr, sendbuf, msglen, share_buf);
            }
        }
    }
    
    if(MyConnect(from))
    {
        if(!msglen)
            msglen = prefix_buffer(0, from, pfix, sendbuf, pattern, vl);
        /* send the share buf if others are using it too */
        send_message(from, sendbuf, msglen, share_buf);
    }
    
    sbuf_end_share(&share_buf, 1);

    va_end(vl);
    return;
}

/*
 * send_quit_to_common_channels()
 * 
 * Sends a message to all people (inclusing user) on local server who are
 * in same channel with user if the user can send to this channel.
 */
void send_quit_to_common_channels(aClient *from, char *reason)
{
    Link *channels;
    chanMember *users;
    aClient *cptr;
    int msglen;
    void *share_buf = NULL;

    INC_SERIAL
    
    msglen=sprintf(sendbuf,":%s!%s@%s QUIT :%s", from->name,
                   from->user->username,from->user->host, reason);      
    sbuf_begin_share(sendbuf, msglen, &share_buf);
   
    if(from->fd >= 0)
        sentalong[from->fd] = sent_serial;    
    for (channels = from->user->channel; channels; channels = channels->next)
    {
        if (!can_send(from, channels->value.chptr, reason)) 
        {
            for (users = channels->value.chptr->members; users; 
                 users = users->next) 
            {
                cptr = users->cptr;
                if (!MyConnect(cptr) || sentalong[cptr->fd] == sent_serial)
                    continue;
                sentalong[cptr->fd] = sent_serial;
                if (check_fake_direction(from, cptr))
                    continue;
                send_message(cptr, sendbuf, msglen, share_buf);
            }
        }
    }
    sbuf_end_share(&share_buf, 1);
}

/*
 * send_part_to_common_channels()
 * 
 * Sends a message to all people (inclusing user) on local server who are
 * in same channel with user if the user cannot send to the channel.
 */
void send_part_to_common_channels(aClient *from, char *reason)
{
    Link *channels;
    chanMember *users;
    aClient *cptr;
    int msglen = 0;
    void *share_buf = NULL;
    
    for (channels = from->user->channel; channels; channels = channels->next)
    {
        if (can_send(from, channels->value.chptr, reason)) 
        {
            msglen = sprintf(sendbuf,":%s!%s@%s PART %s",
                             from->name,from->user->username,from->user->host,
                             channels->value.chptr->chname);
            sbuf_begin_share(sendbuf, msglen, &share_buf);

            INC_SERIAL

            if (from->fd >= 0)
                sentalong[from->fd] = sent_serial;

            for (users = channels->value.chptr->members; users; 
                 users = users->next) 
            {
                cptr = users->cptr;
              
                if (!MyConnect(cptr) || sentalong[cptr->fd] == sent_serial)
                    continue;
                
                sentalong[cptr->fd] = sent_serial;
                if (check_fake_direction(from, cptr))
                    continue;
                send_message(cptr, sendbuf, msglen, share_buf);
            }
            sbuf_end_share(&share_buf, 1);
        }
    }
}

#ifdef FLUD
void sendto_channel_butlocal(aClient *one, aClient *from, aChannel *chptr,
                             char *pattern, ...)
{
    chanMember *cm;
    aClient *acptr;
    int i;
    va_list vl;
          
    va_start(vl, pattern);

    INC_SERIAL
    
    for (cm = chptr->members; cm; cm = cm->next) 
    {
        acptr = cm->cptr;
        if (acptr->from == one)
            continue;           /* ...was the one I should skip */
        i = acptr->from->fd;
        if (!MyFludConnect(acptr)) 
        {
            /*
             * Now check whether a message has been sent to this remote
             * link already
             */
            if (sentalong[i] != sent_serial) 
            {
                vsendto_prefix_one(acptr, from, pattern, vl);
                sentalong[i] = sent_serial;
            }
        }
    }
    va_end(vl);
    return;
}
#endif /* FLUD */

/*
 * sendto_channel_butserv
 * 
 * Send a message to all members of a channel that are connected to this
 * server.
 */
void sendto_channel_butserv(aChannel *chptr, aClient *from, char *pattern, ...)
{
    chanMember  *cm;
    aClient *acptr;
    va_list vl;
    int didlocal = 0;
    char *pfix;
    void *share_buf = NULL;

    va_start(vl, pattern);
    
    pfix = va_arg(vl, char *);

    for (cm = chptr->members; cm; cm = cm->next)
    {
        if (MyConnect(acptr = cm->cptr))
        {
            if(!didlocal)
            {
                didlocal = prefix_buffer(0, from, pfix, sendbuf, pattern, vl);
                sbuf_begin_share(sendbuf, didlocal, &share_buf);
            }
            
            if (check_fake_direction(from, acptr))
                continue;

            send_message(acptr, sendbuf, didlocal, share_buf);

            /* vsendto_prefix_one(acptr, from, pattern, vl); */
        }
    }
    sbuf_end_share(&share_buf, 1);
    va_end(vl);
}

/*
 * sendto_channel_butserv_me
 * 
 * Send a message to all members of a channel that are connected to this
 * server. Possibly hide the origin, if it's a server, with me.name if certain paranoia is on.
 */
void sendto_channel_butserv_me(aChannel *chptr, aClient *from, char *pattern, ...)
{
    chanMember  *cm;
    aClient *acptr;
    va_list vl;
    int didlocal = 0;
    char *pfix;
    void *share_buf = NULL;

    va_start(vl, pattern);
    
    pfix = va_arg(vl, char *);

#ifdef HIDE_SERVERMODE_ORIGINS
    if(IsServer(from) && !IsULine(from))
    {
       from = &me;
       pfix = me.name;
    }
#endif

    for (cm = chptr->members; cm; cm = cm->next)
    {
        if (MyConnect(acptr = cm->cptr))
        {
            if (!didlocal)
            {
                didlocal = prefix_buffer(0, from, pfix, sendbuf, pattern, vl);
                sbuf_begin_share(sendbuf, didlocal, &share_buf);
            }
            
            if(check_fake_direction(from, acptr))
                continue;

            send_message(acptr, sendbuf, didlocal, share_buf);

        }
    }
    sbuf_end_share(&share_buf, 1);
    va_end(vl);
}

/*
 * sendto_all_butone.
 * 
 * Send a message to all connections except 'one'. The basic wall type
 * message generator.
 */
void sendto_all_butone(aClient *one, aClient *from, char *pattern, ...)
{
    int     i;
    aClient *cptr;
    va_list vl;
        
    va_start(vl, pattern);
    for (i = 0; i <= highest_fd; i++)
        if ((cptr = local[i]) && !IsMe(cptr) && one != cptr)
            vsendto_prefix_one(cptr, from, pattern, vl);
    va_end(vl);
    return;
}

/*
 * sendto_all_servmask
 *
 * Send to all servers that match the specified mask, and to all local
 * clients if I match the mask.  Replaces sendto_match_butone().
 *   -Quension [Jul 2004]
 */
void sendto_all_servmask(aClient *from, char *mask, char *pattern, ...)
{
    fdlist   send_fdlist;
    void    *share_buf;
    char    *pfix;
    aClient *cptr;
    DLink   *lp;
    int      i;
    int      k;
    va_list  vl;

    va_start(vl, pattern);

    /* send to matching servers */
    k = 0;
    for (lp = server_list; lp; lp = lp->next)
    {
        cptr = lp->value.cptr;
        if (cptr == from->from)
            continue;
        if (!match(mask, cptr->name))
            send_fdlist.entry[++k] = cptr->fd;
    }
    if (k)
    {
        send_fdlist.last_entry = k;
        vsendto_fdlist(&send_fdlist, pattern, vl);
    }

    /* send to my clients if I match */
    if (!match(mask, me.name))
    {
        pfix = va_arg(vl, char *);
        k = prefix_buffer(0, from, pfix, sendbuf, pattern, vl);
        sbuf_begin_share(sendbuf, k, &share_buf);
        for (i = 0; i < highest_fd; i++)
        {
            if (!(cptr = local[i]))
                continue;
            if (!IsClient(cptr))
                continue;
            send_message(cptr, sendbuf, k, share_buf);
        }
        sbuf_end_share(&share_buf, 1);
    }
}

/*
 * sendto_ops_lev
 * 
 * Send to *local* ops only at a certain level... 0 = normal +s 1 = client
 * connect/disconnect   (+c) [IRCOPS ONLY] 2 = bot rejection
 * (+r) 3 = server kills                      (+k)
 */
void sendto_ops_lev(int lev, char *pattern, ...)
{
    aClient *cptr;
    int     i;
    char        nbuf[1024];
    va_list vl;
    char *tmsg;

#ifdef NICER_UMODENOTICE_SEPARATION
    switch(lev)
    {
       case CCONN_LEV:
          tmsg = "Client";
          break;

       case DEBUG_LEV:
          tmsg = "Debug";
          break;

       case SPY_LEV:
          tmsg = "Spy";
          break;

       case SPAM_LEV:
          tmsg = "Spam";
          break;

       case FLOOD_LEV:
          tmsg = "Flood";
          break;

#ifdef DCCALLOW
       case DCCSEND_LEV:
          tmsg = "DCCAllow";
          break;

#endif
	case ADMIN_LEV:
          tmsg = "Admin";
          break;

       default:
          tmsg = "Notice";
    }
#else
    tmsg = "Notice";
#endif
        
    va_start(vl,pattern);
    for (i = 0; i <= highest_fd; i++)
    if ((cptr = local[i]) && !IsServer(cptr) && !IsMe(cptr)) 
    {
        switch (lev) 
        {
            case CCONN_LEV:
                if (!SendCConnNotice(cptr) || !IsAnOper(cptr))
                    continue;
                break;
            case REJ_LEV:
                if (!SendRejNotice(cptr) || !IsAnOper(cptr))
                    continue;
                break;
            case SKILL_LEV:
                if (!SendSkillNotice(cptr))
                    continue;
                break;
            case USKILL_LEV:
                if (!SendSUkillNotice(cptr) || !IsAnOper(cptr))
                    continue;
                break;
            case SPY_LEV:
                if (!SendSpyNotice(cptr) || !IsAnOper(cptr))
                    continue;
                break;
#ifdef DCCALLOW
            case DCCSEND_LEV:
                if (!SendDCCNotice(cptr) || !IsAnOper(cptr))
                    continue;
                break;
#endif
            case FLOOD_LEV:
                if (!SendFloodNotice(cptr) || !IsAnOper(cptr))
                    continue;
                break;
            case SPAM_LEV:
                if (!SendSpamNotice(cptr) || !IsAnOper(cptr))
                    continue;
                break;
            case DEBUG_LEV:
                if (!SendDebugNotice(cptr) || !IsAnOper(cptr))
                    continue;
                break;

				 case ADMIN_LEV:
                if (!IsAdmin(cptr) || !SendServNotice(cptr))
                    continue;
                          
            default:            /* this is stupid, but oh well */
                if (!SendServNotice(cptr))
                    continue;
        }
        ircsprintf(nbuf, ":%s NOTICE %s :*** %s -- ", me.name, 
                   cptr->name, tmsg);
        strncat(nbuf, pattern, sizeof(nbuf) - strlen(nbuf));
        vsendto_one(cptr, nbuf, vl);
    }
    va_end(vl);
    return;
}                               

/*
 * sendto_ops
 * 
 * Send to *local* ops only.
 */
void sendto_ops(char *pattern, ...)
{
    va_list vl;
        
    va_start(vl, pattern);
    vsendto_realops(pattern, vl);
    va_end(vl);
    return;
}

/*
 * sendto_ops_butone 
 * Send message to all operators. 
 * one - client not to send message to 
 * from- client which message is from *NEVER* NULL!!
 */
void sendto_ops_butone(aClient *one, aClient *from, char *pattern, ...)
{
    int     i;
    aClient *cptr;
    va_list vl;
           
    va_start(vl, pattern);

    INC_SERIAL

    for (cptr = client; cptr; cptr = cptr->next)
    {
        if (!SendWallops(cptr))
            continue;
        /*
         * we want wallops if (MyClient(cptr) && !(IsServer(from) ||
         * IsMe(from))) continue;
         */
        i = cptr->from->fd;     /* find connection oper is on */
        if (sentalong[i] == sent_serial) /* sent message along it already ? */
            continue;
        if (cptr->from == one)
            continue;           /* ...was the one I should skip */
        sentalong[i] = sent_serial;
        vsendto_prefix_one(cptr->from, from, pattern, vl);
    }
    va_end(vl);
    return;
}

/*
 * * sendto_wallops_butone *      Send message to all operators. * one
 * - client not to send message to * from- client which message is from
 * *NEVER* NULL!!
 */
void sendto_wallops_butone(aClient *one, aClient *from, char *pattern, ...)
{
    int     i;
    aClient *cptr;
    va_list vl;
        
    va_start(vl, pattern);
    for(i=0;i<=highest_fd;i++)
    {
        if((cptr=local[i])!=NULL)
        {
            if(!(IsRegistered(cptr) && (SendWallops(cptr) ||
                                        IsServer(cptr))) || cptr==one)
                continue;
            vsendto_prefix_one(cptr, from, pattern, vl);
        }
    }
    va_end(vl);
    return;
}

void send_globops(char *pattern, ...)
{
    aClient *cptr;
    char nbuf[1024];
    va_list vl;
    DLink *lp;
          
    va_start(vl, pattern);
    for (lp = oper_list; lp; lp = lp->next)
    {
        cptr = lp->value.cptr;
        if (!SendGlobops(cptr) || !IsAnOper(cptr))
            continue;

        if (IsAnOper(cptr))
        {
            ircsprintf(nbuf, ":%s NOTICE %s :*** Global -- %s",
                       me.name, cptr->name, pattern);
            vsendto_one(cptr, nbuf, vl);
        }
    }
    va_end(vl);
    return;
}

void send_chatops(char *pattern, ...)
{
    aClient *cptr;
    char nbuf[1024];
    va_list vl;
    DLink *lp;
          
    va_start(vl, pattern);
    for (lp = oper_list; lp; lp = lp->next)
    {
        cptr = lp->value.cptr;
        if (!SendChatops(cptr) || !IsAnOper(cptr))
            continue;

        if (IsAnOper(cptr))
        {
            ircsprintf(nbuf, ":%s NOTICE %s :*** ChatOps -- %s",
                       me.name, cptr->name, pattern);
            vsendto_one(cptr, nbuf, vl);
        }
    }
    va_end(vl);
    return;
}

/*
 * to - destination client from - client which message is from
 * 
 * NOTE: NEITHER OF THESE SHOULD *EVER* BE NULL!! -avalon
 */
void sendto_prefix_one(aClient *to, aClient *from, char *pattern, ...)
{
    static char sender[HOSTLEN + NICKLEN + USERLEN + 5];
    static char temp[1024];
    anUser *user;
    char *idx;
    char *par;
    int flag = 0, sidx = 0;
    va_list vl, vl2;

    va_start(vl, pattern);
    VA_COPY(vl2, vl);

    par = va_arg(vl, char *);
    /*
     * Optimize by checking if (from && to) before everything 
     * uhh, there's _always_ going to be a to!
     */
    if (from) 
    {
        if (!MyClient(from) && IsPerson(to) && (to->from == from->from)) 
        {
            if (IsServer(from)) 
            {
                ircvsprintf(temp, pattern, vl2);
                sendto_ops("Send message (%s) to %s[%s] dropped from "
                           "%s(Fake Dir)", temp, to->name, to->from->name,
                           from->name);
                va_end(vl);
                return;
            }
            
            sendto_ops("Ghosted: %s[%s@%s] from %s[%s@%s] (%s)", to->name,
                       to->user->username, to->user->host, from->name,
                       from->user->username, from->user->host, to->from->name);
            sendto_serv_butone(NULL, ":%s KILL %s :%s (%s[%s@%s] Ghosted %s)",
                               me.name, to->name, me.name, to->name,
                               to->user->username, to->user->host,
                               to->from->name);

            to->flags |= FLAGS_KILLED;
            exit_client(NULL, to, &me, "Ghosted client");
            if (IsPerson(from))
                sendto_one(from, err_str(ERR_GHOSTEDCLIENT), me.name,
                           from->name, to->name, to->user->username,
                           to->user->host, to->from);
            va_end(vl);
            return;
        }

        if (MyClient(to) && IsPerson(from) && !mycmp(par, from->name)) 
        {
            user = from->user;

            for(idx = from->name; *idx; idx++)
                sender[sidx++] = *idx;

            if (user)
            {
                if (*user->username) 
                {
                    sender[sidx++] = '!';
                    for(idx = user->username; *idx; idx++)
                        sender[sidx++] = *idx;
                }
                if (*user->host && !MyConnect(from)) 
                {
                    sender[sidx++] = '@';
                    for(idx = user->host; *idx; idx++)
                        sender[sidx++] = *idx;
                    flag = 1;
                }
            }

            /*
             * flag is used instead of index(sender, '@') for speed and
             * also since username/nick may have had a '@' in them.
             * -avalon
             */

          /*
             * flag is used instead of index(sender, '@') for speed and
             * also since username/nick may have had a '@' in them.
             * -avalon
             */

            if (!flag && MyConnect(from) && *user->host) 
            {
                sender[sidx++] = '@';
                for(idx = user->host; *idx; idx++)
                    sender[sidx++] = *idx;
            }

            sender[sidx] = '\0';
            par = sender;
        }
    }

    temp[0] = ':';
    sidx = 1;

    /*
     * okay, we more or less know that our sendto_prefix crap is going 
     * to be :%s <blah>, so it's easy to fix these lame problems...joy 
     */

    for(idx = par; *idx; idx++)
        temp[sidx++] = *idx;
    for(idx = (pattern + 3); *idx; idx++)
        temp[sidx++] = *idx;

    temp[sidx] = '\0'; 

    vsendto_one(to, temp, vl);
    va_end(vl);
}

/* this is an incredibly expensive function. 
 * removed all strcat() calls. - lucas */
void vsendto_prefix_one(aClient *to, aClient *from, char *pattern, va_list vl)
{
    static char sender[HOSTLEN + NICKLEN + USERLEN + 5];
    static char temp[1024];
    anUser *user;
    char *idx;
    char *par;
    int flag = 0, sidx = 0;
    va_list vl2;
    VA_COPY(vl2, vl);
        
    par = va_arg(vl2, char *);
    /*
     * Optimize by checking if (from && to) before everything 
     * uhh, there's _always_ going to be a to!
     */
    if (from) 
    {
        if (!MyClient(from) && IsPerson(to) && (to->from == from->from)) 
        {
            if (IsServer(from)) 
            {
                ircvsprintf(temp, pattern, vl);
                sendto_ops("Send message (%s) to %s[%s] dropped from "
                           "%s(Fake Dir)", temp,
                           to->name, to->from->name, from->name);
                return;
            }

            sendto_ops("Ghosted: %s[%s@%s] from %s[%s@%s] (%s)", to->name,
                       to->user->username, to->user->host, from->name,
                       from->user->username, from->user->host, to->from->name);
            sendto_serv_butone(NULL, ":%s KILL %s :%s (%s[%s@%s] Ghosted %s)",
                               me.name, to->name, me.name, to->name,
                               to->user->username, to->user->host,
                               to->from->name);

            to->flags |= FLAGS_KILLED;
            exit_client(NULL, to, &me, "Ghosted client");
            if (IsPerson(from))
                sendto_one(from, err_str(ERR_GHOSTEDCLIENT), me.name,
                           from->name, to->name, to->user->username,
                           to->user->host, to->from);
            return;
        }

        if (MyClient(to) && IsPerson(from) && !mycmp(par, from->name)) 
        {
            user = from->user;

            for(idx = from->name; *idx; idx++)
                sender[sidx++] = *idx;

            if (user)
            {
                if (*user->username) 
                {
                    sender[sidx++] = '!';
                    for(idx = user->username; *idx; idx++)
                        sender[sidx++] = *idx;
                }
                if (*user->host && !MyConnect(from)) 
                {
                    sender[sidx++] = '@';
                    for(idx = user->host; *idx; idx++)
                        sender[sidx++] = *idx;
                    flag = 1;
                }
            }

            /*
             * flag is used instead of index(sender, '@') for speed and
             * also since username/nick may have had a '@' in them.
             * -avalon
             */

            if (!flag && MyConnect(from) && *user->host) 
            {
                sender[sidx++] = '@';
                for(idx = user->host; *idx; idx++)
                    sender[sidx++] = *idx;
            }

            sender[sidx] = '\0';
            par = sender;

        }
    }

    temp[0] = ':';
    sidx = 1;

    /* 
     * okay, we more or less know that our sendto_prefix crap is 
     * going to be :%s <blah>, so it's easy to fix these lame problems...joy
     */

    for(idx = par; *idx; idx++)
        temp[sidx++] = *idx;
    for(idx = (pattern + 3); *idx; idx++)
        temp[sidx++] = *idx;

    temp[sidx] = '\0'; 

    vsendto_one(to, temp, vl2);
}

void sendto_fdlist(fdlist *listp, char *pattern, ...)
{
    int len, j, fd;
    va_list vl;
    void *share_buf = NULL;
    
    va_start(vl, pattern);
    len = ircvsprintf(sendbuf, pattern, vl);
    sbuf_begin_share(sendbuf, len, &share_buf);
        
    for (fd = listp->entry[j = 1]; j <= listp->last_entry;
         fd = listp->entry[++j])
        send_message(local[fd], sendbuf, len, share_buf);
    sbuf_end_share(&share_buf, 1);
    va_end(vl);
}

void vsendto_fdlist(fdlist *listp, char *pattern, va_list vl)
{
    int len, j, fd;
    void *share_buf = NULL;
    len = ircvsprintf(sendbuf, pattern, vl);
    sbuf_begin_share(sendbuf, len, &share_buf);
        
    for (fd = listp->entry[j = 1]; j <= listp->last_entry;
         fd = listp->entry[++j])
        send_message(local[fd], sendbuf, len, share_buf);
    sbuf_end_share(&share_buf, 1);
}


void vsendto_realops(char *pattern, va_list vl)
{
    aClient *cptr;
    char nbuf[1024];
    DLink *lp;

    for (lp = oper_list; lp; lp = lp->next)
    {
        cptr = lp->value.cptr;
        if (IsAnOper(cptr))
        {
            ircsprintf(nbuf, ":%s NOTICE %s :*** Notice -- %s",
                       me.name, cptr->name, pattern);
            vsendto_one(cptr, nbuf, vl);
        }
    }
    return;
}

/*
 * sendto_realops
 * 
 * Send to *local* ops only but NOT +s nonopers.
 * If it's to local ops only and not +s nonopers, then SendServNotice is
 * wrong. Changed to IsAnOper. -mjs
 */
void sendto_realops(char *pattern, ...)
{
    va_list vl;
          
    va_start(vl, pattern);
    vsendto_realops(pattern, vl);
    va_end(vl);
}

/*
 * sendto_realops_lev
 * 
 * Send to *local* ops only but NOT +s nonopers at a certain level
 */
void sendto_realops_lev(int lev, char *pattern, ...)
{
    aClient *cptr;
    char nbuf[1024];
    va_list vl;
    DLink *lp;
    char *tmsg;

    va_start(vl, pattern);

#ifdef NICER_UMODENOTICE_SEPARATION
    switch(lev)
    {
       case CCONN_LEV:
          tmsg = "Client";
          break;

       case DEBUG_LEV:
          tmsg = "Debug";
          break;

       case SPY_LEV:
          tmsg = "Spy";
          break;

       case SPAM_LEV:
          tmsg = "Spam";
          break;

       case FLOOD_LEV:
          tmsg = "Flood";
          break;

#ifdef DCCALLOW
       case DCCSEND_LEV:
          tmsg = "DCCAllow";
          break;
#endif

	 case ADMIN_LEV:
          tmsg = "Admin";
          break;

       default:
          tmsg = "Notice";
    }
#else
    tmsg = "Notice";
#endif

    for (lp = oper_list; lp; lp = lp->next)
    {
        cptr = lp->value.cptr;
        switch (lev)
        {
            case CCONN_LEV:
                if (!SendCConnNotice(cptr))
                    continue;
                break;
            case REJ_LEV:
                if (!SendRejNotice(cptr))
                    continue;
                break;
            case SKILL_LEV:
                if (!SendSkillNotice(cptr))
                    continue;
                break;
            case USKILL_LEV:
                if (!SendSUkillNotice(cptr))
                    continue;
                break;
            case SPY_LEV:
                if (!SendSpyNotice(cptr))
                    continue;
                break;
#ifdef DCCALLOW
            case DCCSEND_LEV:
                if (!SendDCCNotice(cptr))
                    continue;
                break;
#endif
            case FLOOD_LEV:
                if (!SendFloodNotice(cptr))
                    continue;
                break;
            case SPAM_LEV:
                if (!SendSpamNotice(cptr))
                    continue;
                break;
            case DEBUG_LEV:
                if (!SendDebugNotice(cptr))
                    continue;
                break;

				case ADMIN_LEV:
                if (!IsAdmin(cptr))
                    continue;
                break;
         
        }
        ircsnprintf(nbuf, 1024, ":%s NOTICE %s :*** %s -- %s",
                    me.name, cptr->name, tmsg, pattern);
        vsendto_one(cptr, nbuf, vl);
    }
    va_end(vl);
    return;
}

/*
 * ts_warn
 * Call sendto_ops, with some flood checking (at most 5 warnings 
 * every 5 seconds)
 */

void ts_warn(char * pattern, ...)
{
    static ts_val last = 0;
    static int  warnings = 0;
    ts_val now;
    va_list vl;
        
    va_start(vl, pattern);
    /*
     * if we're running with TS_WARNINGS enabled and someone does
     * something silly like (remotely) connecting a nonTS server,
     * we'll get a ton of warnings, so we make sure we don't send more
     * than 5 every 5 seconds.  -orabidoo
     */
    now = time(NULL);
    if (now - last < 5)
    {
        if (++warnings > 5)
            return;
    }
    else
    {
        last = now;
        warnings = 0;
    }

    vsendto_realops(pattern, vl);
    va_end(vl);
    return;
}

/*
 * sendto_locops
 */
void sendto_locops(char *pattern, ...)
{
    aClient *cptr;
    char nbuf[1024];
    va_list vl;
    DLink *lp;

    va_start(vl, pattern);

    for (lp = oper_list; lp; lp = lp->next)
    {
        cptr = lp->value.cptr;

        if (SendGlobops(cptr))
        {
            ircsprintf(nbuf, ":%s NOTICE %s :*** LocOps -- %s",
                       me.name, cptr->name, pattern);
            vsendto_one(cptr, nbuf, vl);
        }
    }
    va_end(vl);
    return;
}

/* sendto_gnotice - send a routing notice to all local +n users. */
void sendto_gnotice(char *pattern, ...)
{
    aClient *cptr;
    char nbuf[1024];
    va_list vl;
    DLink *lp;
        
    va_start(vl, pattern);

    for (lp = oper_list; lp; lp = lp->next)
    {
        cptr = lp->value.cptr;
        if (SendRnotice(cptr)) 
        {
            ircsprintf(nbuf, ":%s NOTICE %s :*** Routing -- %s",
                       me.name, cptr->name, pattern);
            vsendto_one(cptr, nbuf, vl);
        }
    }
    va_end(vl);
    return;
}



/* sendto_conops - send a connection notice to all local +C users. */
void sendto_conops(char *pattern, ...)
{
    aClient *cptr;
    char nbuf[1024];
    va_list vl;
    DLink *lp;
        
    va_start(vl, pattern);

    for (lp = oper_list; lp; lp = lp->next)
    {
        cptr = lp->value.cptr;
        if (IsUmodeC(cptr)) 
        {
            ircsprintf(nbuf, ":%s NOTICE %s :*** Global Connections -- %s",
                       me.name, cptr->name, pattern);
            vsendto_one(cptr, nbuf, vl);
        }
    }
    va_end(vl);
    return;
}


/*
 * sendto_channelflags_butone
 *  Send a message to all channel members with the specified flags, both
 *  local and remote.
 */
void sendto_channelflags_butone(aClient *one, aClient *from, aChannel *chptr,
                                int flags, char *pattern, ...)
{
    chanMember *cm;
    aClient *acptr;
    int fd;
    char *pfix;
    va_list vl;
    int didlocal = 0;
    int didremote = 0;
    void *share_buf[2] = {0};

    va_start(vl, pattern);
    pfix = va_arg(vl, char *);

    INC_SERIAL

    for (cm = chptr->members; cm; cm = cm->next)
    {
        acptr = cm->cptr;

        if (acptr->from == one || !(cm->flags & flags))
            continue;

        if ((confopts & FLAGS_SERVHUB) && IsULine(acptr))
            continue;

        if (MyConnect(acptr))
        {
            if (!didlocal)
            {
                didlocal = prefix_buffer(0, from, pfix, sendbuf, pattern, vl);
                sbuf_begin_share(sendbuf, didlocal, &share_buf[0]);
            }
            send_message(acptr, sendbuf, didlocal, share_buf[0]);
        }
        else
        {
            fd = acptr->from->fd;

            if (sentalong[fd] == sent_serial)
                continue;

            if (!didremote)
            {
                didremote = prefix_buffer(1, from, pfix, remotebuf, pattern,
                                          vl);
                sbuf_begin_share(remotebuf, didremote, &share_buf[1]);
            }
            send_message(acptr, remotebuf, didremote, share_buf[1]);
            sentalong[fd] = sent_serial;
        }
    }

    sbuf_end_share(share_buf, 2);
}


/*******************************************
 * Flushing functions (empty queues)
 *******************************************/

/*
 * flush_connections
 * Empty only buffers for clients without FLAGS_BLOCKED
 * dump_connections 
 * Unintelligently try to empty all buffers.
 */
void flush_connections(int fd) 
{
    int     i;
    aClient *cptr;
    
    if (fd == me.fd) 
    {
        for (i = highest_fd; i >= 0; i--)
        {
            if (!(cptr = local[i]))
               continue;
            if(!(cptr->flags & FLAGS_BLOCKED) &&
                (SBufLength(&cptr->sendQ) > 0 ||
                (ZipOut(cptr) && zip_is_data_out(cptr->serv->zip_out))))
                send_queued(cptr);
        }
    }
    else if (fd >= 0 && (cptr = local[fd]) &&
             !(cptr->flags & FLAGS_BLOCKED) && 
             (SBufLength(&cptr->sendQ) > 0 || 
             (ZipOut(cptr) && zip_is_data_out(cptr->serv->zip_out))))
        send_queued(cptr);
}

void dump_connections(int fd) 
{
    int     i;
    aClient *cptr;
    
    if (fd == me.fd) 
    {
        for (i = highest_fd; i >= 0; i--)
            if ((cptr = local[i]) && 
                (SBufLength(&cptr->sendQ) > 0 || 
                (ZipOut(cptr) && zip_is_data_out(cptr->serv->zip_out))))
                send_queued(cptr);
    }
    else if (fd >= 0 && (cptr = local[fd]) && 
        (SBufLength(&cptr->sendQ) > 0 || 
        (ZipOut(cptr) && zip_is_data_out(cptr->serv->zip_out))))
        send_queued(cptr);
}

/* flush an fdlist intelligently */
void flush_fdlist_connections(fdlist *listp)
{
    int i, fd;
    aClient *cptr;
        
    for (fd = listp->entry[i = 1]; i <= listp->last_entry;
         fd = listp->entry[++i])
        if ((cptr = local[fd]) && !(cptr->flags & FLAGS_BLOCKED) &&
            (SBufLength(&cptr->sendQ) > 0 ||
            (ZipOut(cptr) && zip_is_data_out(cptr->serv->zip_out))))
            send_queued(cptr);
}


u_long
memcount_send(MCsend *mc)
{
    mc->file = __FILE__;

    mc->s_bufs.c++;
    mc->s_bufs.m += sizeof(sendbuf);
    mc->s_bufs.c++;
    mc->s_bufs.m += sizeof(remotebuf);
    mc->s_bufs.c++;
    mc->s_bufs.m += sizeof(selfbuf);
    mc->s_bufs.c++;
#ifdef HAVE_ENCRYPTION_ON
    mc->s_bufs.m += sizeof(rc4buf);
#endif
    return 0;
}




syntax highlighted by Code2HTML, v. 0.9.1