/*
* IRC - Internet Relay Chat, common/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.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: send.c,v 1.9 2003/12/09 17:25:09 gvs Exp $";
#endif
#include "os.h"
#ifndef CLIENT_COMPILE
# include "s_defines.h"
#else
# include "c_defines.h"
#endif
#define SEND_C
#ifndef CLIENT_COMPILE
# include "s_externs.h"
#else
# include "c_externs.h"
#endif
#undef SEND_C
static char sendbuf[2048];
static int send_message __P((aClient *, char *, int));
#if USE_STDARG
static void vsendto_prefix_one(aClient *, aClient *, char *, va_list);
#endif
#ifndef CLIENT_COMPILE
static char psendbuf[2048];
static int sentalong[MAXCONNECTIONS];
#endif
/*
** dead_link
** An error has been detected. The link *must* be closed,
** but *cannot* call ExitClient (m_bye) from here.
** Instead, mark it with FLAGS_DEADSOCKET. This should
** generate ExitClient from the main loop.
**
** If 'notice' is not NULL, it is assumed to be a format
** for a message to local opers. It can contain only one
** '%s', which will be replaced by the sockhost field of
** the failing link.
**
** Also, the notice is skipped for "uninteresting" cases,
** like Persons and yet unknown connections...
*/
static int dead_link(to, notice)
aClient *to;
char *notice;
{
if (IsHeld(to))
return -1;
to->flags |= FLAGS_DEADSOCKET;
/*
* If because of BUFFERPOOL problem then clean dbufs now so that
* notices don't hurt operators below.
*/
DBufClear(&to->recvQ);
DBufClear(&to->sendQ);
#ifndef CLIENT_COMPILE
if (!IsPerson(to) && !IsUnknown(to) && !(to->flags & FLAGS_CLOSING))
sendto_flag(SCH_ERROR, notice, get_client_name(to, FALSE));
Debug((DEBUG_ERROR, notice, get_client_name(to, FALSE)));
#endif
return -1;
}
#ifndef CLIENT_COMPILE
/*
** flush_fdary
** Used to empty all output buffers for connections in fdary.
*/
void flush_fdary(fdp)
FdAry *fdp;
{
int i;
aClient *cptr;
for (i = 0; i <= fdp->highest; i++)
{
if (!(cptr = local[fdp->fd[i]]))
continue;
if (!IsRegistered(cptr)) /* is this needed?? -kalt */
continue;
if (DBufLength(&cptr->sendQ) > 0)
(void)send_queued(cptr);
}
}
/*
** flush_connections
** Used to empty all output buffers for all connections. Should only
** be called once per scan of connections. There should be a select in
** here perhaps but that means either forcing a timeout or doing a poll.
** When flushing, all we do is empty the obuffer array for each local
** client and try to send it. if we can't send it, it goes into the sendQ
** -avalon
*/
void flush_connections(fd)
int fd;
{
Reg int i;
Reg aClient *cptr;
if (fd == me.fd)
{
for (i = highest_fd; i >= 0; i--)
if ((cptr = local[i]) && DBufLength(&cptr->sendQ) > 0)
(void)send_queued(cptr);
}
else if (fd >= 0 && (cptr = local[fd]) && DBufLength(&cptr->sendQ) > 0)
(void)send_queued(cptr);
}
#endif
/*
** send_message
** Internal utility which delivers one message buffer to the
** socket. Takes care of the error handling and buffering, if
** needed.
** if ZIP_LINKS is defined, the message will eventually be compressed,
** anything stored in the sendQ is compressed.
*/
static int send_message(to, msg, len)
aClient *to;
char *msg; /* if msg is a null pointer, we are flushing connection */
int len;
#if !defined(CLIENT_COMPILE)
{
int i;
Debug((DEBUG_SEND,"Sending %s %d [%s] ", to->name, to->fd, msg));
if (to->from)
to = to->from;
if (to->fd < 0)
{
Debug((DEBUG_ERROR,
"Local socket %s with negative fd... AARGH!",
to->name));
}
if (IsMe(to))
{
sendto_flag(SCH_ERROR, "Trying to send to myself! [%s]", msg);
return 0;
}
if (IsDead(to))
return 0; /* This socket has already been marked as dead */
if (DBufLength(&to->sendQ) > get_sendq(to))
{
# ifdef HUB
if (CBurst(to))
{
aConfItem *aconf = to->serv->nline;
poolsize -= MaxSendq(aconf->class) >> 1;
IncSendq(aconf->class);
poolsize += MaxSendq(aconf->class) >> 1;
sendto_flag(SCH_NOTICE,
"New poolsize %d. (sendq adjusted)",
poolsize);
istat.is_dbufmore++;
}
else
# endif
{
char ebuf[BUFSIZE];
ebuf[0] = '\0';
if (IsService(to) || IsServer(to))
{
SPRINTF(ebuf,
"Max SendQ limit exceeded for %s: %d > %d",
get_client_name(to, FALSE),
DBufLength(&to->sendQ), get_sendq(to));
}
to->exitc = EXITC_SENDQ;
return dead_link(to, ebuf[0] ? ebuf :
"Max Sendq exceeded");
}
}
# ifdef ZIP_LINKS
/*
** data is first stored in to->zip->outbuf until
** it's big enough to be compressed and stored in the sendq.
** send_queued is then responsible to never let the sendQ
** be empty and to->zip->outbuf not empty.
*/
if (to->flags & FLAGS_ZIP)
msg = zip_buffer(to, msg, &len, 0);
tryagain:
if (len && (i = dbuf_put(&to->sendQ, msg, len)) < 0)
# else /* ZIP_LINKS */
tryagain:
if ((i = dbuf_put(&to->sendQ, msg, len)) < 0)
# endif /* ZIP_LINKS */
{
if (i == -2 && CBurst(to))
{ /* poolsize was exceeded while connect burst */
aConfItem *aconf = to->serv->nline;
poolsize -= MaxSendq(aconf->class) >> 1;
IncSendq(aconf->class);
poolsize += MaxSendq(aconf->class) >> 1;
sendto_flag(SCH_NOTICE,
"New poolsize %d. (reached)",
poolsize);
istat.is_dbufmore++;
goto tryagain;
}
else
{
to->exitc = EXITC_MBUF;
return dead_link(to,
"Buffer allocation error for %s");
}
}
/*
** 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->acpt != &me)
to->acpt->sendM += 1;
/*
** 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).
*/
if (DBufLength(&to->sendQ)/1024 > to->lastsq)
send_queued(to);
return 0;
}
#else /* CLIENT_COMPILE */
{
int rlen = 0, i;
Debug((DEBUG_SEND,"Sending %s %d [%s] ", to->name, to->fd, msg));
if (to->from)
to = to->from;
if (to->fd < 0)
{
Debug((DEBUG_ERROR,
"Local socket %s with negative fd... AARGH!",
to->name));
}
if (IsDead(to))
return 0; /* This socket has already been marked as dead */
if ((rlen = deliver_it(to, msg, len)) < 0 && rlen < len)
return dead_link(to,"Write error to %s, closing link");
/*
** 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->acpt != &me)
to->acpt->sendM += 1;
return 0;
}
#endif
/*
** 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(to)
aClient *to;
{
char *msg;
int len, rlen, more = 0;
/*
** Once socket is marked dead, we cannot start writing to it,
** even if the error is removed...
*/
if (IsDead(to))
{
/*
** Actually, we should *NEVER* get here--something is
** not working correct if send_queued is called for a
** dead socket... --msa
*/
return -1;
}
#ifdef ZIP_LINKS
/*
** Here, we must make sure than nothing will be left in to->zip->outbuf
** This buffer needs to be compressed and sent if all the sendQ is sent
*/
if ((to->flags & FLAGS_ZIP) && to->zip->outcount)
{
if (DBufLength(&to->sendQ) > 0)
more = 1;
else
{
msg = zip_buffer(to, NULL, &len, 1);
if (len == -1)
return dead_link(to,
"fatal error in zip_buffer()");
if (dbuf_put(&to->sendQ, msg, len) < 0)
{
to->exitc = EXITC_MBUF;
return dead_link(to,
"Buffer allocation error for %s");
}
}
}
#endif
while (DBufLength(&to->sendQ) > 0 || more)
{
msg = dbuf_map(&to->sendQ, &len);
/* Returns always len > 0 */
if ((rlen = deliver_it(to, msg, len)) < 0)
return dead_link(to,"Write error to %s, closing link");
(void)dbuf_delete(&to->sendQ, rlen);
to->lastsq = DBufLength(&to->sendQ)/1024;
if (rlen < len) /* ..or should I continue until rlen==0? */
break;
#ifdef ZIP_LINKS
if (DBufLength(&to->sendQ) == 0 && more)
{
/*
** The sendQ is now empty, compress what's left
** uncompressed and try to send it too
*/
more = 0;
msg = zip_buffer(to, NULL, &len, 1);
if (len == -1)
return dead_link(to,
"fatal error in zip_buffer()");
if (dbuf_put(&to->sendQ, msg, len) < 0)
{
to->exitc = EXITC_MBUF;
return dead_link(to,
"Buffer allocation error for %s");
}
}
#endif
}
return (IsDead(to)) ? -1 : 0;
}
#ifndef CLIENT_COMPILE
static anUser ausr = { NULL, NULL, NULL, NULL, 0, 0, 0, 0, NULL,
NULL, "anonymous", "anonymous.", "anonymous."};
static aClient anon = { NULL, NULL, NULL, &ausr, NULL, NULL, 0, 0,/*flags*/
&anon, -2, 0, STAT_CLIENT, "anonymous", "anonymous",
"anonymous identity hider", "anonymous.in.IRC", 0, "",
# ifdef ZIP_LINKS
NULL,
# endif
0, {0, 0, NULL }, {0, 0, NULL },
0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, 0, NULL, 0
# if defined(__STDC__) /* hack around union{} initialization -Vesa */
,{0}, NULL, ""
# endif
};
#endif
/*
*
*/
#if ! USE_STDARG
static int sendprep(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
static int vsendprep(char *pattern, va_list va)
#endif
{
int len;
Debug((DEBUG_L10, "sendprep(%s)", pattern));
#if ! USE_STDARG
len = irc_sprintf(sendbuf, pattern, p1, p2, p3, p4, p5, p6,
p7, p8, p9, p10, p11);
if (len == -1)
len = strlen(sendbuf);
#else
len = vsprintf(sendbuf, pattern, va);
#endif
if (len > 510)
#ifdef IRCII_KLUDGE
len = 511;
#else
len = 510;
sendbuf[len++] = '\r';
#endif
sendbuf[len++] = '\n';
sendbuf[len] = '\0';
return len;
}
#ifndef CLIENT_COMPILE
#if ! USE_STDARG
static int sendpreprep(to, from, pattern,
p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
aClient *to, *from;
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
{
static char sender[HOSTLEN+NICKLEN+USERLEN+5];
Reg anUser *user;
char *par;
int flag = 0, len;
Debug((DEBUG_L10, "sendpreprep(%#x(%s),%#x(%s),%s)",
to, to->name, from, from->name, pattern));
par = p1;
if (to && from && MyClient(to) && IsPerson(from) &&
!strcasecmp(par, from->name))
{
user = from->user;
(void)strcpy(sender, from->name);
if (user)
{
if (*user->username)
{
(void)strcat(sender, "!");
(void)strcat(sender, user->username);
}
if (*user->host && !MyConnect(from))
{
(void)strcat(sender, "@");
(void)strcat(sender,
#ifdef RUSNET_IRCD
(user->flags & FLAGS_VHOST) ?
#endif
user->host
#ifdef RUSNET_IRCD
: from->sockhost
#endif
);
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)
{
(void)strcat(sender, "@");
#ifdef RUSNET_IRCD
if (user->flags & FLAGS_VHOST || IsUnixSocket(from))
#else
if (IsUnixSocket(from))
#endif
(void)strcat(sender, user->host);
else
(void)strcat(sender, from->sockhost);
}
par = sender;
}
len = irc_sprintf(psendbuf, pattern, par, p2, p3, p4, p5, p6,
p7, p8, p9, p10, p11);
if (len == -1)
len = strlen(psendbuf);
if (len > 510)
#ifdef IRCII_KLUDGE
len = 511;
#else
len = 510;
psendbuf[len++] = '\r';
#endif
psendbuf[len++] = '\n';
psendbuf[len] = '\0';
return len;
}
#else /* USE_STDARG */
static int vsendpreprep(aClient *to, aClient *from, char *pattern, va_list va)
{
Reg anUser *user;
int flag = 0, len;
Debug((DEBUG_L10, "sendpreprep(%#x(%s),%#x(%s),%s)",
to, to->name, from, from->name, pattern));
if (to && from && MyClient(to) && IsPerson(from) &&
!strncmp(pattern, ":%s", 3))
{
char *par = va_arg(va, char *);
if (from == &anon || !strcasecmp(par, from->name))
{
user = from->user;
(void)strcpy(psendbuf, ":");
(void)strcat(psendbuf, from->name);
if (user)
{
if (*user->username)
{
(void)strcat(psendbuf, "!");
(void)strcat(psendbuf, user->username);
}
if (*user->host && !MyConnect(from))
{
(void)strcat(psendbuf, "@");
(void)strcat(psendbuf,
#ifdef RUSNET_IRCD
(user->flags & FLAGS_VHOST) ?
#endif
user->host
#ifdef RUSNET_IRCD
: from->sockhost
#endif
);
flag = 1;
}
}
/*
** flag is used instead of index(newpat, '@') for speed and
** also since username/nick may have had a '@' in them. -avalon
*/
if (!flag && MyConnect(from) && *user->host)
{
(void)strcat(psendbuf, "@");
#ifdef RUSNET_IRCD
if (user->flags & FLAGS_VHOST || IsUnixSocket(from))
#else
if (IsUnixSocket(from))
#endif
(void)strcat(psendbuf, user->host);
else
(void)strcat(psendbuf, from->sockhost);
}
}
else
{
(void)strcpy(psendbuf, ":");
(void)strcat(psendbuf, par);
}
len = strlen(psendbuf);
len += vsprintf(psendbuf+len, pattern+3, va);
}
else
len = vsprintf(psendbuf, pattern, va);
if (len > 510)
#ifdef IRCII_KLUDGE
len = 511;
#else
len = 510;
psendbuf[len++] = '\r';
#endif
psendbuf[len++] = '\n';
psendbuf[len] = '\0';
return len;
}
#endif /* USE_STDARG */
#endif /* CLIENT_COMPILE */
/*
** send message to single client
*/
#if ! USE_STDARG
int sendto_one(to, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
aClient *to;
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
int vsendto_one(aClient *to, char *pattern, va_list va)
#endif
{
int len;
#if ! USE_STDARG
len = sendprep(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11);
#else
len = vsendprep(pattern, va);
#endif
(void)send_message(to, sendbuf, len);
return len;
}
#if USE_STDARG
int sendto_one(aClient *to, char *pattern, ...)
{
int len;
va_list va;
va_start(va, pattern);
len = vsendto_one(to, pattern, va);
va_end(va);
return len;
}
#endif
/*
* sendto_channel_butone
*
* Send a message to all members of a channel that are connected to this
* server except client 'one'.
*/
#ifndef CLIENT_COMPILE
#if ! USE_STDARG
/*VARARGS*/
void sendto_channel_butone(one, from, chptr, ops, pattern,
p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
aClient *one, *from;
aChannel *chptr;
int ops;
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
void sendto_channel_butone(aClient *one, aClient *from, aChannel *chptr,
int ops, char *pattern, ...)
#endif
{
Reg Link *lp;
Reg aClient *acptr, *lfrm = from;
int len1, len2 = 0;
if (IsAnonymous(chptr) && IsClient(from))
{
#if ! USE_STDARG
if (p1 && *p1 && !strcasecmp(p1, from->name))
p1 = anon.name;
#endif
lfrm = &anon;
}
if (one != from && MyConnect(from) && IsRegisteredUser(from))
{
/* useless junk? */ /* who said that and why? --B. */
#if ! USE_STDARG
sendto_prefix_one(from, from, pattern, p1, p2, p3, p4,
p5, p6, p7, p8, p9, p10, p11);
#else
va_list va;
va_start(va, pattern);
vsendto_prefix_one(from, from, pattern, va);
va_end(va);
#endif
}
#if ! USE_STDARG
len1 = sendprep(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11);
#else
{
va_list va;
va_start(va, pattern);
len1 = vsendprep(pattern, va);
va_end(va);
}
#endif
for (lp = chptr->clist; lp; lp = lp->next)
{
acptr = lp->value.cptr;
if (acptr->from == one || IsMe(acptr))
continue; /* ...was the one I should skip */
/* for ops only: get user flags and check it */
if (ops && !IsServer(acptr))
{
Reg Link *tmp = find_user_link(chptr->members, acptr);
if (!tmp) /* this should NOT happen but... */
continue;
if (!(tmp->flags & CHFL_CHANOP))
continue;
}
if (MyConnect(acptr) && IsRegisteredUser(acptr))
{
if (!len2)
{
#if ! USE_STDARG
len2 = sendpreprep(acptr, lfrm, pattern, p1,
p2, p3, p4, p5, p6, p7, p8,
p9, p10, p11);
#else
va_list va;
va_start(va, pattern);
len2 = vsendpreprep(acptr, lfrm, pattern, va);
va_end(va);
#endif
}
if (acptr != from)
(void)send_message(acptr, psendbuf, len2);
}
else
(void)send_message(acptr, sendbuf, len1);
}
return;
}
/*
* sendto_server_butone
*
* Send a message to all connected servers except the client 'one'.
*/
#if ! USE_STDARG
/*VARARGS*/
void sendto_serv_butone(one, pattern, p1, p2, p3, p4,p5,p6,p7,p8,p9,p10,p11)
aClient *one;
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
void sendto_serv_butone(aClient *one, char *pattern, ...)
#endif
{
Reg int i, len=0;
Reg aClient *cptr;
for (i = fdas.highest; i >= 0; i--)
if ((cptr = local[fdas.fd[i]]) &&
(!one || cptr != one->from) && !IsMe(cptr)) {
if (!len)
{
#if ! USE_STDARG
len = sendprep(pattern, p1, p2, p3, p4, p5,
p6, p7, p8, p9, p10, p11);
#else
va_list va;
va_start(va, pattern);
len = vsendprep(pattern, va);
va_end(va);
#endif
}
(void)send_message(cptr, sendbuf, len);
}
return;
}
#if ! USE_STDARG
int
sendto_serv_v(one, ver, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
aClient *one;
int ver;
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
int
sendto_serv_v(aClient *one, int ver, char *pattern, ...)
#endif
{
Reg int i, len=0, rc=0;
Reg aClient *cptr;
for (i = fdas.highest; i >= 0; i--)
if ((cptr = local[fdas.fd[i]]) &&
(!one || cptr != one->from) && !IsMe(cptr))
if (cptr->serv->version & ver)
{
if (!len)
{
#if ! USE_STDARG
len = sendprep(pattern, p1, p2, p3, p4,
p5, p6, p7, p8, p9, p10,
p11);
#else
va_list va;
va_start(va, pattern);
len = vsendprep(pattern, va);
va_end(va);
#endif
}
(void)send_message(cptr, sendbuf, len);
}
else
rc = 1;
return rc;
}
#if ! USE_STDARG
int
sendto_serv_notv(one, ver, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9,p10,p11)
aClient *one;
int ver;
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
int
sendto_serv_notv(aClient *one, int ver, char *pattern, ...)
#endif
{
Reg int i, len=0, rc=0;
Reg aClient *cptr;
for (i = fdas.highest; i >= 0; i--)
if ((cptr = local[fdas.fd[i]]) &&
(!one || cptr != one->from) && !IsMe(cptr))
if ((cptr->serv->version & ver) == 0)
{
if (!len)
{
#if ! USE_STDARG
len = sendprep(pattern, p1, p2, p3, p4,
p5, p6, p7, p8, p9, p10,
p11);
#else
va_list va;
va_start(va, pattern);
len = vsendprep(pattern, va);
va_end(va);
#endif
}
(void)send_message(cptr, sendbuf, len);
}
else
rc = 1;
return rc;
}
/*
* sendto_common_channels()
*
* Sends a message to all people (inclusing user) on local server who are
* in same channel with user, except for channels set Quiet or Anonymous
* The calling procedure must take the necessary steps for such channels.
*/
#if ! USE_STDARG
/*VARARGS*/
void sendto_common_channels(user,pattern,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11)
aClient *user;
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
void sendto_common_channels(aClient *user, char *pattern, ...)
#endif
{
Reg int i;
Reg aClient *cptr;
Reg Link *channels, *lp;
int len = 0;
/* This is kind of funky, but should work. The first part below
is optimized for HUB servers or servers with few clients on
them. The second part is optimized for bigger client servers
where looping through the whole client list is bad. I'm not
really certain of the point at which each function equals
out...but I do know the 2nd part will help big client servers
fairly well... - Comstud 97/04/24
*/
if (highest_fd < 50) /* This part optimized for HUB servers... */
{
if (MyConnect(user))
{
#if ! USE_STDARG
len = sendpreprep(user, user, pattern,
p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,p11);
#else
va_list va;
va_start(va, pattern);
len = vsendpreprep(user, user, pattern, va);
va_end(va);
#endif
(void)send_message(user, psendbuf, len);
}
for (i = 0; i <= highest_fd; i++)
{
if (!(cptr = local[i]) || IsServer(cptr) ||
user == cptr || !user->user)
continue;
for (lp = user->user->channel; lp; lp = lp->next)
{
if (!IsMember(cptr, lp->value.chptr))
continue;
if (IsAnonymous(lp->value.chptr))
continue;
if (!IsQuiet(lp->value.chptr))
{
#ifndef DEBUGMODE
if (!len) /* This saves little cpu,
but breaks the debug code.. */
#endif
{
#if ! USE_STDARG
len = sendpreprep(cptr, user, pattern,
p1, p2, p3, p4, p5,
p6, p7, p8, p9, p10,
p11);
#else
va_list va;
va_start(va, pattern);
len = vsendpreprep(cptr, user, pattern, va);
va_end(va);
#endif
}
(void)send_message(cptr, psendbuf,
len);
break;
}
}
}
}
else
{
/* This part optimized for client servers */
bzero((char *)&sentalong[0], sizeof(int) * MAXCONNECTIONS);
if (MyConnect(user))
{
#if ! USE_STDARG
len = sendpreprep(user, user, pattern, p1, p2, p3, p4,
p5, p6, p7, p8, p9, p10, p11);
#else
va_list va;
va_start(va, pattern);
len = vsendpreprep(user, user, pattern, va);
va_end(va);
#endif
(void)send_message(user, psendbuf, len);
sentalong[user->fd] = 1;
}
if (!user->user)
return;
for (channels=user->user->channel; channels;
channels=channels->next)
{
if (IsQuiet(channels->value.chptr))
continue;
if (IsAnonymous(channels->value.chptr))
continue;
for (lp=channels->value.chptr->clist;lp;
lp=lp->next)
{
cptr = lp->value.cptr;
if (user == cptr)
continue;
if (!cptr->user || sentalong[cptr->fd])
continue;
sentalong[cptr->fd]++;
#ifndef DEBUGMODE
if (!len) /* This saves little cpu,
but breaks the debug code.. */
#endif
{
#if ! USE_STDARG
len = sendpreprep(cptr, user, pattern,
p1, p2, p3, p4, p5,
p6, p7, p8, p9, p10,
p11);
#else
va_list va;
va_start(va, pattern);
len = vsendpreprep(cptr, user, pattern, va);
va_end(va);
#endif
}
(void)send_message(cptr, psendbuf, len);
}
}
}
return;
}
/*
* sendto_channel_butserv
*
* Send a message to all members of a channel that are connected to this
* server.
*/
#if ! USE_STDARG
/*VARARGS*/
void sendto_channel_butserv(chptr, from, pattern, p1, p2, p3,
p4, p5, p6, p7, p8, p9, p10, p11)
aChannel *chptr;
aClient *from;
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
void sendto_channel_butserv(aChannel *chptr, aClient *from, char *pattern, ...)
#endif
{
Reg Link *lp;
Reg aClient *acptr, *lfrm = from;
int len = 0;
if (MyClient(from))
{ /* Always send to the client itself */
#if ! USE_STDARG
sendto_prefix_one(from, from, pattern, p1, p2, p3, p4,
p5, p6, p7, p8, p9, p10, p11);
#else
va_list va;
va_start(va, pattern);
vsendto_prefix_one(from, from, pattern, va);
va_end(va);
#endif
if (IsQuiet(chptr)) /* Really shut up.. */
return;
}
if (IsAnonymous(chptr) && IsClient(from))
{
#if ! USE_STDARG
if (p1 && *p1 && !strcasecmp(p1, from->name))
p1 = anon.name;
#endif
lfrm = &anon;
}
for (lp = chptr->clist; lp; lp = lp->next)
if (MyClient(acptr = lp->value.cptr) && acptr != from)
{
if (!len)
{
#if ! USE_STDARG
len = sendpreprep(acptr, lfrm, pattern, p1, p2,
p3, p4, p5, p6, p7, p8, p9,
p10, p11);
#else
va_list va;
va_start(va, pattern);
len = vsendpreprep(acptr, lfrm, pattern, va);
va_end(va);
#endif
}
(void)send_message(acptr, psendbuf, len);
}
return;
}
/*
** send a msg to all ppl on servers/hosts that match a specified mask
** (used for enhanced PRIVMSGs)
**
** addition -- Armin, 8jun90 (gruner@informatik.tu-muenchen.de)
*/
static int match_it(one, mask, what)
aClient *one;
char *mask;
int what;
{
switch (what)
{
case MATCH_HOST:
#ifdef RUSNET_IRCD
return (match(mask, one->sockhost) == 0);
#else
return (match(mask, one->user->host) == 0);
#endif
case MATCH_SERVER:
default:
return (match(mask, one->user->server)==0);
}
}
/*
* sendto_match_servs
*
* send to all servers which match the mask at the end of a channel name
* (if there is a mask present) or to all if no mask.
*/
#if ! USE_STDARG
/*VARARGS*/
void sendto_match_servs(chptr, from, format, p1, p2, p3, p4, p5, p6, p7,
p8, p9, p10, p11)
aChannel *chptr;
aClient *from;
char *format, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
void sendto_match_servs(aChannel *chptr, aClient *from, char *format, ...)
#endif
{
Reg int i, len=0;
Reg aClient *cptr;
char *mask;
if (chptr)
{
if (*chptr->chname == '&')
return;
if ((mask = (char *)rindex(chptr->chname, ':')))
mask++;
}
else
mask = (char *)NULL;
for (i = fdas.highest; i >= 0; i--)
{
if (!(cptr = local[fdas.fd[i]]) || (cptr == from) ||
IsMe(cptr))
continue;
if (!BadPtr(mask) && match(mask, cptr->name))
continue;
if (chptr &&
*chptr->chname == '!' && !(cptr->serv->version & SV_NJOIN))
continue;
if (!len)
{
#if ! USE_STDARG
len = sendprep(format, p1, p2, p3, p4, p5, p6, p7,
p8, p9, p10, p11);
#else
va_list va;
va_start(va, format);
len = vsendprep(format, va);
va_end(va);
#endif
}
(void)send_message(cptr, sendbuf, len);
}
}
#if ! USE_STDARG
/*VARARGS*/
int
sendto_match_servs_v(chptr, from, ver, format, p1, p2, p3, p4, p5, p6,
p7, p8, p9, p10, p11)
aChannel *chptr;
aClient *from;
char *format, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
int ver;
#else
int
sendto_match_servs_v(aChannel *chptr, aClient *from, int ver,
char *format, ...)
#endif
{
Reg int i, len=0, rc=0;
Reg aClient *cptr;
char *mask;
if (chptr)
{
if (*chptr->chname == '&')
return 0;
if ((mask = (char *)rindex(chptr->chname, ':')))
mask++;
}
else
mask = (char *)NULL;
for (i = fdas.highest; i >= 0; i--)
{
if (!(cptr = local[fdas.fd[i]]) || (cptr == from) ||
IsMe(cptr))
continue;
if (!BadPtr(mask) && match(mask, cptr->name))
continue;
if (chptr &&
*chptr->chname == '!' && !(cptr->serv->version & SV_NJOIN))
continue;
if ((ver & cptr->serv->version) == 0)
{
rc = 1;
continue;
}
if (!len)
{
#if ! USE_STDARG
len = sendprep(format, p1, p2, p3, p4, p5, p6, p7,
p8, p9, p10, p11);
#else
va_list va;
va_start(va, format);
len = vsendprep(format, va);
va_end(va);
#endif
}
(void)send_message(cptr, sendbuf, len);
}
return rc;
}
#if ! USE_STDARG
/*VARARGS*/
int
sendto_match_servs_notv(chptr, from, ver, format, p1, p2, p3, p4, p5,
p6, p7, p8, p9, p10, p11)
aChannel *chptr;
aClient *from;
char *format, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
int ver;
#else
int
sendto_match_servs_notv(aChannel *chptr, aClient *from, int ver,
char *format, ...)
#endif
{
Reg int i, len=0, rc=0;
Reg aClient *cptr;
char *mask;
if (chptr)
{
if (*chptr->chname == '&')
return 0;
if ((mask = (char *)rindex(chptr->chname, ':')))
mask++;
}
else
mask = (char *)NULL;
for (i = fdas.highest; i >= 0; i--)
{
if (!(cptr = local[fdas.fd[i]]) || (cptr == from) ||
IsMe(cptr))
continue;
if (!BadPtr(mask) && match(mask, cptr->name))
continue;
if (chptr &&
*chptr->chname == '!' && !(cptr->serv->version & SV_NJOIN))
continue;
if ((ver & cptr->serv->version) != 0)
{
rc = 1;
continue;
}
if (!len)
{
#if ! USE_STDARG
len = sendprep(format, p1, p2, p3, p4, p5, p6, p7,
p8, p9, p10, p11);
#else
va_list va;
va_start(va, format);
len = vsendprep(format, va);
va_end(va);
#endif
}
(void)send_message(cptr, sendbuf, len);
}
return rc;
}
/*
* sendto_match_butone
*
* Send to all clients which match the mask in a way defined on 'what';
* either by user hostname or user servername.
*/
/*VARARGS*/
#if ! USE_STDARG
void sendto_match_butone(one, from, mask, what, pattern,
p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
aClient *one, *from;
int what;
char *mask, *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9,*p10,*p11;
#else
void sendto_match_butone(aClient *one, aClient *from, char *mask, int what, char *pattern, ...)
#endif
{
int i;
aClient *cptr,
*srch;
for (i = 0; i <= highest_fd; i++)
{
if (!(cptr = local[i]))
continue; /* that clients are not mine */
if (cptr == one) /* must skip the origin !! */
continue;
if (IsServer(cptr))
{
/*
** we can save some CPU here by not searching the
** entire list of users since it is ordered!
** original idea/code from pht.
** it could be made better by looping on the list of
** servers to avoid non matching blocks in the list
** (srch->from != cptr), but then again I never
** bothered to worry or optimize this routine -kalt
*/
for (srch = cptr->prev; srch; srch = srch->prev)
{
if (!IsRegisteredUser(srch))
continue;
if (srch->from == cptr &&
match_it(srch, mask, what))
break;
}
if (srch == NULL)
continue;
}
/* my client, does he match ? */
else if (!(IsRegisteredUser(cptr) &&
match_it(cptr, mask, what)))
continue;
#if ! USE_STDARG
sendto_prefix_one(cptr, from, pattern,
p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,p11);
#else
{
va_list va;
va_start(va, pattern);
vsendto_prefix_one(cptr, from, pattern, va);
va_end(va);
}
#endif
}
return;
}
/*
** sendto_ops_butone
** Send message to all operators.
** one - client not to send message to
** from- client which message is from *NEVER* NULL!!
*/
#if ! USE_STDARG
/*VARARGS*/
void sendto_ops_butone(one, from, pattern,
p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
aClient *one, *from;
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
void sendto_ops_butone(aClient *one, aClient *from, char *pattern, ...)
#endif
{
Reg int i;
Reg aClient *cptr;
bzero((char *)&sentalong[0], sizeof(int) * MAXCONNECTIONS);
for (cptr = client; cptr; cptr = cptr->next)
{
if (IsService(cptr) || !IsRegistered(cptr))
continue;
if ((IsPerson(cptr) && !SendWallops(cptr)) || IsMe(cptr))
continue;
#ifdef NOWALLOPS_TO_USERS
if (IsPerson(cptr) && !IsAnOper(cptr))
continue;
#endif
if (MyClient(cptr) && !(IsServer(from) || IsMe(from)))
continue;
i = cptr->from->fd; /* find connection oper is on */
if (sentalong[i]) /* sent message along it already ? */
continue;
if (cptr->from == one)
continue; /* ...was the one I should skip */
sentalong[i] = 1;
#if ! USE_STDARG
sendto_prefix_one(cptr->from, from, pattern,
p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,p11);
#else
{
va_list va;
va_start(va, pattern);
vsendto_prefix_one(cptr->from, from, pattern, va);
va_end(va);
}
#endif
}
return;
}
/*
* to - destination client
* from - client which message is from
*
* NOTE: NEITHER OF THESE SHOULD *EVER* BE NULL!!
* -avalon
*/
#if ! USE_STDARG
/*VARARGS*/
void sendto_prefix_one(to, from, pattern,
p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
Reg aClient *to;
Reg aClient *from;
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
void sendto_prefix_one(aClient *to, aClient *from, char *pattern, ...)
#endif
{
int len;
#if ! USE_STDARG
len = sendpreprep(to, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8,
p9, p10, p11);
#else
va_list va;
va_start(va, pattern);
len = vsendpreprep(to, from, pattern, va);
va_end(va);
#endif
send_message(to, psendbuf, len);
return;
}
#if USE_STDARG
static void vsendto_prefix_one(aClient *to, aClient *from, char *pattern, va_list va)
{
int len;
len = vsendpreprep(to, from, pattern, va);
send_message(to, psendbuf, len);
return;
}
#endif
/*
* sends a message to a server-owned channel
*/
static SChan svchans[SCH_MAX] = {
{ SCH_ERROR, "&ERRORS", NULL },
{ SCH_NOTICE, "&NOTICES", NULL },
{ SCH_KILL, "&KILLS", NULL },
{ SCH_CHAN, "&CHANNEL", NULL },
{ SCH_NUM, "&NUMERICS", NULL },
{ SCH_SERVER, "&SERVERS", NULL },
{ SCH_HASH, "&HASH", NULL },
{ SCH_LOCAL, "&LOCAL", NULL },
{ SCH_SERVICE, "&SERVICES", NULL },
{ SCH_DEBUG, "&DEBUG", NULL },
{ SCH_AUTH, "&AUTH", NULL },
#ifdef EXTRA_NOTICES
{ SCH_OPER, "&OPER", NULL },
#endif
};
void setup_svchans()
{
int i;
SChan *shptr;
for (i = SCH_MAX, shptr = svchans + (i - 1); i > 0; i--, shptr--)
shptr->svc_ptr = find_channel(shptr->svc_chname, NULL);
}
#ifdef LOG_EVENTS
static int killlog;
static int userlog;
static int connlog;
static int locallog;
static int authlog;
static int noticelog;
static int errorlog; /* last resort */
#endif
/*
* sendto_log
* type message type/syslog severity
* source message source
* channel message channel (e.g. &ERRORS)
* msg message itself
*/
void sendto_log(type, source, channel, msg)
int type;
const char *source, *channel, *msg;
{
#ifdef LOG_EVENTS
int l
#ifdef USE_SYSLOG
, s = LOG_INFO
#endif
;
switch (type) {
#ifdef SVLOG_KILL
case SVLOG_KILL: l = killlog;
#ifdef SYSLOG_KILL
s = SYSLOG_KILL;
#endif
break;
#endif
#ifdef SVLOG_NOTICES
case SVLOG_NOTICES: l = noticelog;
#ifdef SYSLOG_NOTICES
s = SYSLOG_NOTICES;
#endif
break;
#endif
#ifdef SVLOG_LOCAL
case SVLOG_LOCAL: l = locallog;
#ifdef SYSLOG_LOCAL
s = SYSLOG_LOCAL;
#endif
break;
#endif
#ifdef SVLOG_AUTH
case SVLOG_AUTH: l = userlog;
#ifdef SYSLOG_AUTH
s = SYSLOG_AUTH;
#endif
break;
#endif
#ifdef SVLOG_ERRS
case SVLOG_ERRS:
#endif
default: l = errorlog;
#ifdef SYSLOG_ERRS
s = SYSLOG_ERRS;
#endif
}
#ifdef USE_SYSLOG
syslog(s, "%s: %s: %s", source, channel, msg);
#endif
if (l != -1)
{
char *buf = MyMalloc(strlen(source) + strlen(channel) +
strlen(msg) + 26);
time_t t = time(NULL);
int i = strftime(buf, 20, "%b %e %T ", localtime( &t ) );
i += sprintf(buf + i, "%s: %s: %s\n", source, channel, msg);
(void)write(l, buf, i);
MyFree(buf);
}
#endif /* LOG_EVENTS */
}
#if ! USE_STDARG
void sendto_flag(chan, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,p11)
u_int chan;
char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
#else
void sendto_flag(u_int chan, char *pattern, ...)
#endif
{
Reg aChannel *chptr = NULL;
SChan *shptr;
char nbuf[1024];
if (chan < 1 || chan > SCH_MAX)
chan = SCH_NOTICE;
shptr = svchans + (chan - 1);
if ((chptr = shptr->svc_ptr))
{
#if ! USE_STDARG
(void)sprintf(nbuf, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11);
#else
{
va_list va;
va_start(va, pattern);
(void)vsprintf(nbuf, pattern, va);
va_end(va);
}
#endif
sendto_channel_butserv(chptr, &me, ":%s NOTICE %s :%s", ME, chptr->chname, nbuf);
#ifdef LOG_EVENTS
switch (chan) {
#ifdef SVLOG_ERRS
case SCH_ERROR:
sendto_log(SVLOG_ERRS, ME, chptr->chname, nbuf);
break;
#endif
#ifdef SVLOG_NOTICES
case SCH_NOTICE:
sendto_log(SVLOG_NOTICES, ME, chptr->chname, nbuf);
break;
#endif
#ifdef SVLOG_KILL
case SCH_KILL:
sendto_log(SVLOG_KILL, ME, chptr->chname, nbuf);
break;
#endif
#ifdef SVLOG_LOCAL
case SCH_LOCAL:
sendto_log(SVLOG_LOCAL, ME, chptr->chname, nbuf);
break;
#endif
#ifdef SVLOG_AUTH
case SCH_AUTH:
sendto_log(SVLOG_AUTH, ME, chptr->chname, nbuf);
break;
#endif
}
#endif /* LOG_EVENTS */
#ifdef USE_SERVICES
switch (chan)
{
case SCH_ERROR:
check_services_butone(SERVICE_WANT_ERRORS, NULL, &me,
"&ERRORS :%s", nbuf);
break;
case SCH_NOTICE:
check_services_butone(SERVICE_WANT_NOTICES, NULL, &me,
"&NOTICES :%s", nbuf);
break;
case SCH_LOCAL:
check_services_butone(SERVICE_WANT_LOCAL, NULL, &me,
"&LOCAL :%s", nbuf);
break;
case SCH_NUM:
check_services_butone(SERVICE_WANT_NUMERICS, NULL, &me,
"&NUMERICS :%s", nbuf);
break;
}
#endif
}
return;
}
void
logfiles_open()
{
#ifdef LOG_EVENTS
#ifdef FNAME_KILLLOG
killlog = open(LOG_DIR "/" FNAME_KILLLOG, O_WRONLY|O_APPEND|O_NDELAY);
#else
killlog = -1;
#endif
#ifdef FNAME_USERLOG
userlog = open(LOG_DIR "/" FNAME_USERLOG, O_WRONLY|O_APPEND|O_NDELAY);
#else
userlog = -1;
#endif
#ifdef FNAME_CONNLOG
connlog = open(LOG_DIR "/" FNAME_CONNLOG, O_WRONLY|O_APPEND|O_NDELAY);
#else
connlog = -1;
#endif
#ifdef FNAME_LOCALLOG
locallog = open(LOG_DIR "/" FNAME_LOCALLOG, O_WRONLY|O_APPEND|O_NDELAY);
#else
locallog = -1;
#endif
#ifdef FNAME_ERRORLOG
errorlog = open(LOG_DIR "/" FNAME_ERRORLOG, O_WRONLY|O_APPEND|O_NDELAY);
#else
errorlog = -1;
#endif
#ifdef FNAME_NOTICELOG
noticelog = open(LOG_DIR "/" FNAME_NOTICELOG, O_WRONLY|O_APPEND|O_NDELAY);
#else
noticelog = -1;
#endif
#endif
}
void
logfiles_close()
{
#ifdef LOG_EVENTS
#ifdef FNAME_KILLLOG
if (killlog != -1)
(void)close(killlog);
#endif
#ifdef FNAME_USERLOG
if (userlog != -1)
(void)close(userlog);
#endif
#ifdef FNAME_CONNLOG
if (connlog != -1)
(void)close(connlog);
#endif
#ifdef FNAME_LOCALLOG
if (locallog != -1)
(void)close(locallog);
#endif
#ifdef FNAME_ERRORLOG
if (errorlog != -1)
(void)close(errorlog);
#endif
#ifdef FNAME_NOTICELOG
if (noticelog != -1)
(void)close(noticelog);
#endif
#endif
}
/*
* sendto_flog
* cptr used for firsttime, auth, exitc, send/received M/K
* msg if this contains a message, it means rejected client,
* hence we will log it in connlog; otherwise it's client
* quitting, so it ends up in userlog --Beeth
* username can't get it from cptr
* hostname i.e.
*/
void sendto_flog(cptr, msg, username, hostname)
aClient *cptr;
char *msg, *username, *hostname;
{
#ifdef LOG_EVENTS
/*
** One day we will rewrite linebuf to malloc()s, but for now
** we are lazy. The longest linebuf I saw during last year
** was 216. Max auth reply can be 1024, see rfc931_work() and
** if iauth is disabled, read_authports() makes it max 513.
** And the rest... just count, I got 154 --Beeth
*/
char linebuf[1500];
/*
** This is a potential buffer overflow.
** I mean, when you manage to keep ircd
** running for almost 12 years ;-) --B.
*/
char buf[12], *s;
int logfile;
logfile = msg ? connlog : userlog;
#if !defined(USE_SERVICES) && !( defined(USE_SYSLOG) && \
(defined(SVLOG_USERS) || defined(SVLOG_CONN)) )
if (logfile == -1)
return;
#endif
if (!msg)
{
time_t duration;
duration = timeofday - cptr->firsttime + 1;
(void)sprintf(buf, "%3d:%02d:%02d",
(int) (duration / 3600),
(int) ((duration % 3600) / 60),
(int) (duration % 60));
}
/*
** Aha, cptr->firsttime is time when user connected,
** so we syslog() it anyway. I'm waving big
** "rewrite log format" flag --Beeth.
*/
(void)sprintf(linebuf,
"%s (%s): %s@%s [%s] %c %lu %luKb %lu %luKb\n",
myctime(cptr->firsttime), msg ? msg : buf,
username, hostname, cptr->auth,
cptr->exitc, cptr->sendM, cptr->sendK,
cptr->receiveM, cptr->receiveK);
#if defined(USE_SYSLOG) && (defined(SVLOG_USERS) || defined(SVLOG_CONN))
for (s = linebuf; *s != '('; s++);
syslog(LOG_NOTICE, "%s", s);
#endif
#ifdef USE_SERVICES
if (!msg)
check_services_butone(SERVICE_WANT_USERLOG, NULL, &me,
"USERLOG :%s", linebuf);
else
check_services_butone(SERVICE_WANT_CONNLOG, NULL, &me,
"CONNLOG :%s", linebuf);
#endif
if (logfile != -1)
(void)write(logfile, linebuf, strlen(linebuf));
#endif /* LOG_EVENTS */
}
#endif /* CLIENT_COMPILE */
syntax highlighted by Code2HTML, v. 0.9.1