diff options
author | 2020-05-25 20:09:04 +0200 | |
---|---|---|
committer | 2020-05-25 20:09:04 +0200 | |
commit | 4440a86cfa359b8e40a484a2cd46d33db5455d8a (patch) | |
tree | f5c0c59aebf0058ae97e7ef8b5fb8017f459a05a /common/send.c | |
download | ircd-4440a86cfa359b8e40a484a2cd46d33db5455d8a.tar.gz |
Initial
Diffstat (limited to 'common/send.c')
-rw-r--r-- | common/send.c | 1555 |
1 files changed, 1555 insertions, 0 deletions
diff --git a/common/send.c b/common/send.c new file mode 100644 index 0000000..c61e3e5 --- /dev/null +++ b/common/send.c @@ -0,0 +1,1555 @@ +/* + * 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.39 1999/07/21 22:57:40 kalt 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 if (IsServer(to) || IsService(to)) + sendto_flag(SCH_ERROR, + "Max SendQ limit exceeded for %s: %d > %d", + get_client_name(to, FALSE), + DBufLength(&to->sendQ), get_sendq(to)); + if (!CBurst(to)) + { + to->exitc = EXITC_SENDQ; + return dead_link(to, "Max Sendq exceeded"); + } +# else /* HUB */ + if (IsService(to) || IsServer(to)) + sendto_flag(SCH_ERROR, + "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, "Max Sendq exceeded"); +# endif /* HUB */ + } + else + { +tryagain: +# 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); + + if (len && (i = dbuf_put(&to->sendQ, msg, len)) < 0) +# else /* ZIP_LINKS */ + 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", 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) && + !mycmp(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, user->host); + 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, "@"); + if (IsUnixSocket(from)) + (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 || !mycmp(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, user->host); + 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, "@"); + if (IsUnixSocket(from)) + (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 + +#ifndef CLIENT_COMPILE +#if ! USE_STDARG +/*VARARGS*/ +void sendto_channel_butone(one, from, chptr, pattern, + p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) +aClient *one, *from; +aChannel *chptr; +char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; +#else +void sendto_channel_butone(aClient *one, aClient *from, aChannel *chptr, 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 && !mycmp(p1, from->name)) + p1 = anon.name; +#endif + lfrm = &anon; + } + + if (one != from && MyConnect(from) && IsRegisteredUser(from)) + { + /* useless junk? */ +#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 */ + 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->members;lp; + lp=lp->next) + { + cptr = lp->value.cptr; + if (user == cptr) + continue; + if (!MyConnect(cptr) || 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 && !mycmp(p1, from->name)) + p1 = anon.name; +#endif + lfrm = &anon; + } + + for (lp = chptr->members; 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: + return (match(mask, one->user->host)==0); + 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; + 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 }, +}; + + +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); +} + +#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 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; +} + +/* + * sendto_flog + * cptr used for firsttime, auth, exitc, send/received M/K + * msg replaces duration if duration is 0 + * duration only used if non 0 + * username can't get it from cptr + * hostname i.e. + */ +void sendto_flog(cptr, msg, duration, username, hostname) +aClient *cptr; +char *msg, *username, *hostname; +time_t duration; +{ + char linebuf[1024]; /* auth reply might be long.. */ + int logfile; + +#ifdef USE_SERVICES + if (duration) + { + (void)sprintf(linebuf, + "%s (%3d:%02d:%02d): %s@%s [%s] %c %lu %luKb %lu %luKb\n", + myctime(cptr->firsttime), + (int) (duration / 3600), + (int) ((duration % 3600) / 60), + (int) (duration % 60), + username, hostname, cptr->auth, + cptr->exitc, cptr->sendM, cptr->sendK, + cptr->receiveM, cptr->receiveK); + check_services_butone(SERVICE_WANT_USERLOG, NULL, &me, + "USERLOG :%s", linebuf); + } + else + { + (void)sprintf(linebuf, + "%s (%s): %s@%s [%s] %c %lu %luKb %lu %luKb\n", + myctime(cptr->firsttime), msg, username, + hostname, cptr->auth, + cptr->exitc, cptr->sendM, cptr->sendK, + cptr->receiveM, cptr->receiveK); + check_services_butone(SERVICE_WANT_CONNLOG, NULL, &me, + "CONNLOG :%s", linebuf); + } +#endif + /* + * This conditional makes the logfile active only after + * it's been created, thus logging can be turned off by + * removing the file. + * + * stop NFS hangs...most systems should be able to + * file in 3 seconds. -avalon (curtesy of wumpus) + */ + (void)alarm(3); + if ( +#ifdef FNAME_USERLOG + (duration && + (logfile = open(FNAME_USERLOG, O_WRONLY|O_APPEND)) != -1) +# ifdef FNAME_CONNLOG + || +# endif +#endif +#ifdef FNAME_CONNLOG + (!duration && + (logfile = open(FNAME_CONNLOG, O_WRONLY|O_APPEND)) != -1) +#else +# ifndef FNAME_USERLOG + 0 +# endif +#endif + ) + { + (void)alarm(0); +#ifndef USE_SERVICES + if (duration) + (void)sprintf(linebuf, + "%s (%3d:%02d:%02d): %s@%s [%s] %c %lu %luKb %lu %luKb\n", + myctime(cptr->firsttime), + (int) (duration / 3600), + (int) ((duration % 3600) / 60), + (int) (duration % 60), + username, hostname, cptr->auth, + cptr->exitc, cptr->sendM, cptr->sendK, + cptr->receiveM, cptr->receiveK); + else + (void)sprintf(linebuf, + "%s (%s): %s@%s [%s] %c %lu %luKb %lu %luKb\n", + myctime(cptr->firsttime), msg, username, + hostname, cptr->auth, + cptr->exitc, cptr->sendM, cptr->sendK, + cptr->receiveM, cptr->receiveK); +#endif + (void)alarm(3); + (void)write(logfile, linebuf, strlen(linebuf)); + (void)alarm(0); + (void)close(logfile); + } + (void)alarm(0); +} +#endif /* CLIENT_COMPILE */ |