/src/send.c
C | 1221 lines | 731 code | 148 blank | 342 comment | 177 complexity | 963502739f881782cfe56accad8cc456 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
- /*
- * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
- * send.c: Functions for sending messages.
- *
- * Copyright (C) 2002 by the past and present ircd coders, and others.
- *
- * 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- *
- * $Id: send.c 493 2007-07-07 17:45:14Z jon $
- */
- #include "stdinc.h"
- #include "tools.h"
- #include "send.h"
- #include "channel.h"
- #include "client.h"
- #include "common.h"
- #include "dbuf.h"
- #include "irc_string.h"
- #include "ircd.h"
- #include "handlers.h"
- #include "numeric.h"
- #include "fdlist.h"
- #include "s_bsd.h"
- #include "s_serv.h"
- #include "sprintf_irc.h"
- #include "s_conf.h"
- #include "list.h"
- #include "s_log.h"
- #include "memory.h"
- #include "hook.h"
- #include "irc_getnameinfo.h"
- #include "packet.h"
- #define LOG_BUFSIZE 2048
- struct Callback *iosend_cb = NULL;
- struct Callback *iosendctrl_cb = NULL;
- static void send_message(struct Client *, char *, int);
- static void send_message_remote(struct Client *, struct Client *, char *, int);
- static unsigned long current_serial = 0L;
- /* send_format()
- *
- * inputs - buffer to format into
- * - size of the buffer
- * - format pattern to use
- * - var args
- * output - number of bytes formatted output
- * side effects - modifies sendbuf
- */
- static inline int
- send_format(char *lsendbuf, int bufsize, const char *pattern, va_list args)
- {
- int len;
- /*
- * from rfc1459
- *
- * IRC messages are always lines of characters terminated with a CR-LF
- * (Carriage Return - Line Feed) pair, and these messages shall not
- * exceed 512 characters in length, counting all characters
- * including the trailing CR-LF.
- * Thus, there are 510 characters maximum allowed
- * for the command and its parameters. There is no provision for
- * continuation message lines. See section 7 for more details about
- * current implementations.
- */
- len = vsnprintf(lsendbuf, bufsize - 1, pattern, args);
- if(len > bufsize - 2)
- len = bufsize - 2; /* required by some versions of vsnprintf */
- lsendbuf[len++] = '\r';
- lsendbuf[len++] = '\n';
- return (len);
- }
- /*
- * iosend_default - append a packet to the client's sendq.
- */
- void *
- iosend_default(va_list args)
- {
- struct Client *to = va_arg(args, struct Client *);
- int length = va_arg(args, int);
- char *buf = va_arg(args, char *);
- dbuf_put(&to->localClient->buf_sendq, buf, length);
- return NULL;
- }
- /*
- ** send_message
- ** Internal utility which appends given buffer to the sockets
- ** sendq.
- */
- static void
- send_message(struct Client *to, char *buf, int len)
- {
- assert(!IsMe(to));
- assert(to != &me);
- if(dbuf_length(&to->localClient->buf_sendq) + len > get_sendq(to))
- {
- if(IsServer(to))
- sendto_realops_flags(UMODE_ALL, L_ALL,
- "Max SendQ limit exceeded for %s: %lu > %lu",
- get_client_name(to, HIDE_IP),
- (unsigned
- long) (dbuf_length(&to->localClient->buf_sendq) +
- len), get_sendq(to));
- if(IsClient(to))
- SetSendQExceeded(to);
- dead_link_on_write(to, 0);
- return;
- }
- execute_callback(iosend_cb, to, len, buf);
- /*
- ** 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->localClient->send.messages;
- ++me.localClient->send.messages;
- if(dbuf_length(&to->localClient->buf_sendq) >
- (IsServer(to) ? (unsigned int) 1024 : (unsigned int) 4096))
- send_queued_write(to);
- }
- /* send_message_remote()
- *
- * inputs - pointer to client from message is being sent
- * - pointer to client to send to
- * - pointer to preformatted buffer
- * - length of input buffer
- * output - none
- * side effects - Despite the function name, this only sends to directly
- * connected clients.
- *
- */
- static void
- send_message_remote(struct Client *to, struct Client *from, char *buf, int len)
- {
- if(!MyConnect(to))
- {
- sendto_realops_flags(UMODE_ALL, L_ALL,
- "server send message to %s [%s] dropped from %s(Not local server)",
- to->name, to->from->name, from->name);
- return;
- }
- if(ServerInfo.hub && IsCapable(to, CAP_LL))
- {
- if(((from->lazyLinkClientExists & to->localClient->serverMask) == 0))
- client_burst_if_needed(to, from);
- }
- /* Optimize by checking if (from && to) before everything */
- /* we set to->from up there.. */
- if(!MyClient(from) && IsClient(to) && (to == from->from))
- {
- if(IsServer(from))
- {
- sendto_realops_flags(UMODE_ALL, L_ALL,
- "Send message to %s [%s] dropped from %s(Fake Dir)",
- to->name, to->from->name, from->name);
- return;
- }
- sendto_realops_flags(UMODE_ALL, L_ALL,
- "Ghosted: %s[%s@%s] from %s[%s@%s] (%s)",
- to->name, to->username, to->host,
- from->name, from->username, from->host, to->from->name);
- sendto_server(NULL, to, NULL, CAP_TS6, NOCAPS, NOFLAGS,
- ":%s KILL %s :%s (%s[%s@%s] Ghosted %s)",
- me.id, to->name, me.name, to->name,
- to->username, to->host, to->from->name);
- sendto_server(NULL, to, NULL, NOCAPS, CAP_TS6, NOFLAGS,
- ":%s KILL %s :%s (%s[%s@%s] Ghosted %s)",
- me.name, to->name, me.name, to->name,
- to->username, to->host, to->from->name);
- SetKilled(to);
- if(IsClient(from))
- sendto_one(from, form_str(ERR_GHOSTEDCLIENT),
- me.name, from->name, to->name, to->username, to->host, to->from);
- exit_client(to, &me, "Ghosted client");
- return;
- }
- send_message(to, buf, len);
- }
- /*
- ** sendq_unblocked
- ** Called when a socket is ready for writing.
- */
- void
- sendq_unblocked(fde_t * fd, struct Client *client_p)
- {
- ClearSendqBlocked(client_p);
- /* let send_queued_write be executed by send_queued_all */
- #ifdef HAVE_LIBCRYPTO
- if(fd->flags.pending_read)
- {
- fd->flags.pending_read = 0;
- read_packet(fd, client_p);
- }
- #endif
- }
- /*
- ** slinkq_unblocked
- ** Called when a server control socket is ready for writing.
- */
- static void
- slinkq_unblocked(fde_t * fd, struct Client *client_p)
- {
- ClearSlinkqBlocked(client_p);
- send_queued_slink_write(client_p);
- }
- /*
- ** send_queued_write
- ** This is called when there is a chance that some output would
- ** be possible. This attempts to empty the send queue as far as
- ** possible, and then if any data is left, a write is rescheduled.
- */
- void
- send_queued_write(struct Client *to)
- {
- int retlen;
- struct dbuf_block *first;
- /*
- ** Once socket is marked dead, we cannot start writing to it,
- ** even if the error is removed...
- */
- if(IsDead(to) || IsSendqBlocked(to))
- return; /* no use calling send() now */
- /* Next, lets try to write some data */
- if(dbuf_length(&to->localClient->buf_sendq))
- {
- do
- {
- first = to->localClient->buf_sendq.blocks.head->data;
- #ifdef HAVE_LIBCRYPTO
- if(to->localClient->fd.ssl)
- {
- retlen = SSL_write(to->localClient->fd.ssl, first->data,
- first->size);
- /* translate openssl error codes, sigh */
- if(retlen < 0)
- switch (SSL_get_error(to->localClient->fd.ssl, retlen))
- {
- case SSL_ERROR_WANT_READ:
- return; /* retry later, don't register for write events */
- case SSL_ERROR_WANT_WRITE:
- errno = EWOULDBLOCK;
- case SSL_ERROR_SYSCALL:
- break;
- case SSL_ERROR_SSL:
- if(errno == EAGAIN)
- break;
- default:
- retlen = errno = 0; /* either an SSL-specific error or EOF */
- }
- }
- else
- #endif
- retlen = send(to->localClient->fd.fd, first->data, first->size, 0);
- if(retlen <= 0)
- {
- #ifdef _WIN32
- errno = WSAGetLastError();
- #endif
- break;
- }
- dbuf_delete(&to->localClient->buf_sendq, retlen);
- /* We have some data written .. update counters */
- to->localClient->send.bytes += retlen;
- me.localClient->send.bytes += retlen;
- }
- while(dbuf_length(&to->localClient->buf_sendq));
- if((retlen < 0) && (ignoreErrno(errno)))
- {
- /* we have a non-fatal error, reschedule a write */
- SetSendqBlocked(to);
- comm_setselect(&to->localClient->fd, COMM_SELECT_WRITE,
- (PF *) sendq_unblocked, (void *) to, 0);
- }
- else if(retlen <= 0)
- {
- dead_link_on_write(to, errno);
- return;
- }
- }
- }
- /*
- ** send_queued_slink_write
- ** This is called when there is a chance the some output would
- ** be possible. This attempts to empty the send queue as far as
- ** possible, and then if any data is left, a write is rescheduled.
- */
- void
- send_queued_slink_write(struct Client *to)
- {
- int retlen;
- /*
- ** Once socket is marked dead, we cannot start writing to it,
- ** even if the error is removed...
- */
- if(IsDead(to) || IsSlinkqBlocked(to))
- return; /* no use calling send() now */
- /* Next, lets try to write some data */
- if(to->localClient->slinkq != NULL)
- {
- retlen = send(to->localClient->ctrlfd.fd,
- to->localClient->slinkq + to->localClient->slinkq_ofs,
- to->localClient->slinkq_len, 0);
- if(retlen < 0)
- {
- /* If we have a fatal error */
- if(!ignoreErrno(errno))
- {
- dead_link_on_write(to, errno);
- return;
- }
- }
- else if(retlen == 0)
- {
- /* 0 bytes is an EOF .. */
- dead_link_on_write(to, 0);
- return;
- }
- else
- {
- execute_callback(iosendctrl_cb, to, retlen,
- to->localClient->slinkq + to->localClient->slinkq_ofs);
- to->localClient->slinkq_len -= retlen;
- assert(to->localClient->slinkq_len >= 0);
- if(to->localClient->slinkq_len)
- to->localClient->slinkq_ofs += retlen;
- else
- {
- to->localClient->slinkq_ofs = 0;
- MyFree(to->localClient->slinkq);
- to->localClient->slinkq = NULL;
- }
- }
- /* Finally, if we have any more data, reschedule a write */
- if(to->localClient->slinkq_len)
- {
- SetSlinkqBlocked(to);
- comm_setselect(&to->localClient->ctrlfd, COMM_SELECT_WRITE,
- (PF *) slinkq_unblocked, (void *) to, 0);
- }
- }
- }
- /* send_queued_all()
- *
- * input - NONE
- * output - NONE
- * side effects - try to flush sendq of each client
- */
- void
- send_queued_all(void)
- {
- dlink_node *ptr;
- /* Servers are processed first, mainly because this can generate
- * a notice to opers, which is to be delivered by this function.
- */
- DLINK_FOREACH(ptr, serv_list.head) send_queued_write((struct Client *) ptr->data);
- DLINK_FOREACH(ptr, unknown_list.head) send_queued_write((struct Client *) ptr->data);
- DLINK_FOREACH(ptr, local_client_list.head) send_queued_write((struct Client *) ptr->data);
- /* NOTE: This can still put clients on aborted_list; unfortunately,
- * exit_aborted_clients takes precedence over send_queued_all,
- * because exiting clients can generate server/oper traffic.
- * The easiest approach is dealing with aborted clients in the next I/O lap.
- * -adx
- */
- }
- /* sendto_one()
- *
- * inputs - pointer to destination client
- * - var args message
- * output - NONE
- * side effects - send message to single client
- */
- void
- sendto_one(struct Client *to, const char *pattern, ...)
- {
- va_list args;
- char buffer[IRCD_BUFSIZE];
- int len;
- if(to->from != NULL)
- to = to->from;
- if(IsDead(to))
- return; /* This socket has already been marked as dead */
- va_start(args, pattern);
- len = send_format(buffer, IRCD_BUFSIZE, pattern, args);
- va_end(args);
- send_message(to, buffer, len);
- }
- /* sendto_channel_butone()
- *
- * inputs - pointer to client(server) to NOT send message to
- * - pointer to client that is sending this message
- * - pointer to channel being sent to
- * - vargs message
- * output - NONE
- * side effects - message as given is sent to given channel members.
- *
- * WARNING - +D clients are ignored
- */
- void
- sendto_channel_butone(struct Client *one, struct Client *from,
- struct Channel *chptr, const char *command, const char *pattern, ...)
- {
- va_list alocal, aremote, auid;
- char local_buf[IRCD_BUFSIZE];
- char remote_buf[IRCD_BUFSIZE];
- char uid_buf[IRCD_BUFSIZE];
- int local_len, remote_len, uid_len;
- dlink_node *ptr;
- dlink_node *ptr_next;
- struct Client *target_p;
- int is_ctcp = NO;
- const char *message;
- if(IsServer(from))
- local_len = ircsprintf(local_buf, ":%s %s %s ", from->name, command, chptr->chname);
- else
- local_len = ircsprintf(local_buf, ":%s!%s@%s %s %s ",
- from->name, from->username, from->host,
- command, chptr->chname);
- remote_len = ircsprintf(remote_buf, ":%s %s %s ", from->name, command, chptr->chname);
- uid_len = ircsprintf(uid_buf, ":%s %s %s ", ID(from), command, chptr->chname);
- va_start(alocal, pattern);
- va_start(aremote, pattern);
- va_start(auid, pattern);
- message = &local_buf[local_len] + 1;
- local_len += send_format(&local_buf[local_len], IRCD_BUFSIZE - local_len, pattern, alocal);
- remote_len += send_format(&remote_buf[remote_len], IRCD_BUFSIZE - remote_len,
- pattern, aremote);
- uid_len += send_format(&uid_buf[uid_len], IRCD_BUFSIZE - uid_len, pattern, auid);
- va_end(auid);
- va_end(aremote);
- va_end(alocal);
- ++current_serial;
- if(msg_is_ctcp(message))
- is_ctcp = YES;
- DLINK_FOREACH_SAFE(ptr, ptr_next, chptr->members.head)
- {
- target_p = ((struct Membership *) ptr->data)->client_p;
- assert(target_p != NULL);
- if(IsDefunct(target_p) || IsDeaf(target_p) || is_bwsave(chptr, target_p) ||
- (is_ctcp && is_noctcp(from, target_p)) || target_p->from == one)
- continue;
- if(MyClient(target_p))
- {
- if(target_p->serial != current_serial)
- {
- send_message(target_p, local_buf, local_len);
- target_p->serial = current_serial;
- }
- }
- else
- {
- /* Now check whether a message has been sent to this
- * remote link already
- */
- if(target_p->from->serial != current_serial)
- {
- if(IsCapable(target_p->from, CAP_TS6))
- send_message_remote(target_p->from, from, uid_buf, uid_len);
- else
- send_message_remote(target_p->from, from, remote_buf,
- remote_len);
- target_p->from->serial = current_serial;
- }
- }
- }
- }
- /* sendto_server()
- *
- * inputs - pointer to client to NOT send to
- * - pointer to source client required by LL (if any)
- * - pointer to channel required by LL (if any)
- * - caps or'd together which must ALL be present
- * - caps or'd together which must ALL NOT be present
- * - LL flags: LL_ICLIENT | LL_ICHAN
- * - printf style format string
- * - args to format string
- * output - NONE
- * side effects - Send a message to all connected servers, except the
- * client 'one' (if non-NULL), as long as the servers
- * support ALL capabs in 'caps', and NO capabs in 'nocaps'.
- * If the server is a lazylink client, then it must know
- * about source_p if non-NULL (unless LL_ICLIENT is specified,
- * when source_p will be introduced where required) and
- * chptr if non-NULL (unless LL_ICHANNEL is specified, when
- * chptr will be introduced where required).
- * Note: nothing will be introduced to a LazyLeaf unless
- * the message is actually sent.
- *
- * This function was written in an attempt to merge together the other
- * billion sendto_*serv*() functions, which sprung up with capabs,
- * lazylinks, uids, etc.
- * -davidt
- */
- void
- sendto_server(struct Client *one, struct Client *source_p,
- struct Channel *chptr, unsigned long caps,
- unsigned long nocaps, unsigned long llflags, const char *format, ...)
- {
- va_list args;
- struct Client *client_p;
- dlink_node *ptr;
- char buffer[IRCD_BUFSIZE];
- int len;
- if(chptr != NULL)
- {
- if(chptr->chname[0] != '#')
- return;
- }
- va_start(args, format);
- len = send_format(buffer, IRCD_BUFSIZE, format, args);
- va_end(args);
- DLINK_FOREACH(ptr, serv_list.head)
- {
- client_p = ptr->data;
- /* If dead already skip */
- if(IsDead(client_p))
- continue;
- /* check against 'one' */
- if(one != NULL && (client_p == one->from))
- continue;
- /* check we have required capabs */
- if((client_p->localClient->caps & caps) != caps)
- continue;
- /* check we don't have any forbidden capabs */
- if((client_p->localClient->caps & nocaps) != 0)
- continue;
- if(ServerInfo.hub && IsCapable(client_p, CAP_LL))
- {
- /* check LL channel */
- if(chptr != NULL &&
- ((chptr->lazyLinkChannelExists &
- client_p->localClient->serverMask) == 0))
- {
- /* Only introduce the channel if we really will send this message */
- if(!(llflags & LL_ICLIENT) && source_p &&
- ((source_p->lazyLinkClientExists &
- client_p->localClient->serverMask) == 0))
- continue; /* we can't introduce the unknown source_p, skip */
- if(llflags & LL_ICHAN)
- burst_channel(client_p, chptr);
- else
- continue; /* we can't introduce the unknown chptr, skip */
- }
- /* check LL client */
- if(source_p &&
- ((source_p->lazyLinkClientExists &
- client_p->localClient->serverMask) == 0))
- {
- if(llflags & LL_ICLIENT)
- client_burst_if_needed(client_p, source_p);
- else
- continue; /* we can't introduce the unknown source_p, skip */
- }
- }
- send_message(client_p, buffer, len);
- }
- }
- /* sendto_common_channels_local()
- *
- * inputs - pointer to client
- * - pattern to send
- * output - NONE
- * side effects - Sends a message to all people on local server who are
- * in same channel with user.
- * used by m_nick.c and exit_one_client.
- */
- void
- sendto_common_channels_local(struct Client *user, int touser, const char *pattern, ...)
- {
- va_list args;
- dlink_node *uptr;
- dlink_node *cptr;
- struct Channel *chptr;
- struct Membership *ms;
- struct Client *target_p;
- char buffer[IRCD_BUFSIZE];
- int len;
- va_start(args, pattern);
- len = send_format(buffer, IRCD_BUFSIZE, pattern, args);
- va_end(args);
- ++current_serial;
- DLINK_FOREACH(cptr, user->channel.head)
- {
- chptr = ((struct Membership *) cptr->data)->chptr;
- assert(chptr != NULL);
- DLINK_FOREACH(uptr, chptr->members.head)
- {
- ms = uptr->data;
- target_p = ms->client_p;
- assert(target_p != NULL);
- if(!MyConnect(target_p) || target_p == user || IsDefunct(target_p) ||
- target_p->serial == current_serial)
- continue;
- target_p->serial = current_serial;
- send_message(target_p, buffer, len);
- }
- }
- if(touser && MyConnect(user) && !IsDead(user) && user->serial != current_serial)
- send_message(user, buffer, len);
- }
- /* sendto_channel_local()
- *
- * inputs - member status mask, e.g. CHFL_CHANOP | CHFL_VOICE
- * - whether to ignore +D clients (YES/NO)
- * - pointer to channel to send to
- * - var args pattern
- * output - NONE
- * side effects - Send a message to all members of a channel that are
- * locally connected to this server.
- */
- void
- sendto_channel_local(int type, int nodeaf, struct Channel *chptr, const char *pattern, ...)
- {
- va_list args;
- char buffer[IRCD_BUFSIZE];
- int len;
- dlink_node *ptr;
- struct Membership *ms;
- struct Client *target_p;
- va_start(args, pattern);
- len = send_format(buffer, IRCD_BUFSIZE, pattern, args);
- va_end(args);
- DLINK_FOREACH(ptr, chptr->members.head)
- {
- ms = ptr->data;
- target_p = ms->client_p;
- if(type != 0 && (ms->flags & type) == 0)
- continue;
- if(!MyConnect(target_p) || IsDefunct(target_p) ||
- (nodeaf && (IsDeaf(target_p) || is_bwsave(chptr, target_p))))
- continue;
- send_message(target_p, buffer, len);
- }
- }
- /* sendto_channel_local_butone()
- *
- * inputs - pointer to client to NOT send message to
- * - member status mask, e.g. CHFL_CHANOP | CHFL_VOICE
- * - whether to ignore +D clients (YES/NO)
- * - pointer to channel to send to
- * - var args pattern
- * output - NONE
- * side effects - Send a message to all members of a channel that are
- * locally connected to this server except one.
- *
- */
- void
- sendto_channel_local_butone(struct Client *one, int type, int nodeaf,
- struct Channel *chptr, const char *pattern, ...)
- {
- va_list args;
- char buffer[IRCD_BUFSIZE];
- int len;
- struct Client *target_p;
- struct Membership *ms;
- dlink_node *ptr;
- va_start(args, pattern);
- len = send_format(buffer, IRCD_BUFSIZE, pattern, args);
- va_end(args);
- DLINK_FOREACH(ptr, chptr->members.head)
- {
- ms = ptr->data;
- target_p = ms->client_p;
- if(type != 0 && (ms->flags & type) == 0)
- continue;
- if(!MyConnect(target_p) || target_p == one || IsDefunct(target_p)
- || (nodeaf && (IsDeaf(target_p) || is_bwsave(chptr, target_p))))
- continue;
- send_message(target_p, buffer, len);
- }
- }
- /* sendto_channel_remote()
- *
- * inputs - Client not to send towards
- * - Client from whom message is from
- * - member status mask, e.g. CHFL_CHANOP | CHFL_VOICE
- * - pointer to channel to send to
- * - var args pattern
- * output - NONE
- * side effects - Send a message to all members of a channel that are
- * remote to this server.
- */
- void
- sendto_channel_remote(struct Client *one, struct Client *from, int type, int caps,
- int nocaps, struct Channel *chptr, const char *pattern, ...)
- {
- va_list args;
- char buffer[IRCD_BUFSIZE];
- int len;
- dlink_node *ptr;
- struct Client *target_p;
- struct Membership *ms;
- va_start(args, pattern);
- len = send_format(buffer, IRCD_BUFSIZE, pattern, args);
- va_end(args);
- ++current_serial;
- DLINK_FOREACH(ptr, chptr->members.head)
- {
- ms = ptr->data;
- target_p = ms->client_p;
- if(type != 0 && (ms->flags & type) == 0)
- continue;
- if(MyConnect(target_p))
- continue;
- target_p = target_p->from;
- if(target_p == one->from ||
- ((target_p->from->localClient->caps & caps) != caps) ||
- ((target_p->from->localClient->caps & nocaps) != 0))
- continue;
- if(target_p->from->serial != current_serial)
- {
- send_message(target_p, buffer, len);
- target_p->from->serial = current_serial;
- }
- }
- }
- /*
- ** match_it() and sendto_match_butone() ARE only used
- ** to send a msg to all ppl on servers/hosts that match a specified mask
- ** (used for enhanced PRIVMSGs) for opers
- **
- ** addition -- Armin, 8jun90 (gruner@informatik.tu-muenchen.de)
- **
- */
- /* match_it()
- *
- * inputs - client pointer to match on
- * - actual mask to match
- * - what to match on, HOST or SERVER
- * output - 1 or 0 if match or not
- * side effects - NONE
- */
- static int
- match_it(const struct Client *one, const char *mask, int what)
- {
- if(what == MATCH_HOST)
- return (match(mask, one->host));
- return (match(mask, one->servptr->name));
- }
- /* sendto_match_butone()
- *
- * Send to all clients which match the mask in a way defined on 'what';
- * either by user hostname or user servername.
- *
- * ugh. ONLY used by m_message.c to send an "oper magic" message. ugh.
- */
- void
- sendto_match_butone(struct Client *one, struct Client *from, char *mask,
- int what, const char *pattern, ...)
- {
- va_list alocal, aremote;
- struct Client *client_p;
- dlink_node *ptr, *ptr_next;
- char local_buf[IRCD_BUFSIZE], remote_buf[IRCD_BUFSIZE];
- int local_len = ircsprintf(local_buf, ":%s!%s@%s ", from->name,
- from->username, from->host);
- int remote_len = ircsprintf(remote_buf, ":%s ", from->name);
- va_start(alocal, pattern);
- va_start(aremote, pattern);
- local_len += send_format(&local_buf[local_len], IRCD_BUFSIZE - local_len, pattern, alocal);
- remote_len += send_format(&remote_buf[remote_len], IRCD_BUFSIZE - remote_len,
- pattern, aremote);
- va_end(aremote);
- va_end(alocal);
- /* scan the local clients */
- DLINK_FOREACH(ptr, local_client_list.head)
- {
- client_p = ptr->data;
- if(client_p != one && !IsDefunct(client_p) && match_it(client_p, mask, what))
- send_message(client_p, local_buf, local_len);
- }
- /* Now scan servers */
- DLINK_FOREACH_SAFE(ptr, ptr_next, serv_list.head)
- {
- client_p = ptr->data;
- /*
- * The old code looped through every client on the
- * network for each server to check if the
- * server (client_p) has at least 1 client matching
- * the mask, using something like:
- *
- * for (target_p = GlobalClientList; target_p; target_p = target_p->next)
- * if (IsRegisteredUser(target_p) &&
- * match_it(target_p, mask, what) &&
- * (target_p->from == client_p))
- * vsendto_prefix_one(client_p, from, pattern, args);
- *
- * That way, we wouldn't send the message to
- * a server who didn't have a matching client.
- * However, on a network such as EFNet, that
- * code would have looped through about 50
- * servers, and in each loop, loop through
- * about 50k clients as well, calling match()
- * in each nested loop. That is a very bad
- * thing cpu wise - just send the message
- * to every connected server and let that
- * server deal with it.
- * -wnder
- */
- if(client_p != one && !IsDefunct(client_p))
- send_message_remote(client_p, from, remote_buf, remote_len);
- }
- }
- /* sendto_match_servs()
- *
- * inputs - source client
- * - mask to send to
- * - capab needed
- * - data
- * outputs - none
- * side effects - data sent to servers matching with capab
- */
- void
- sendto_match_servs(struct Client *source_p, const char *mask, int cap, const char *pattern, ...)
- {
- va_list args;
- struct Client *target_p;
- dlink_node *ptr;
- char buffer[IRCD_BUFSIZE];
- int found = 0;
- va_start(args, pattern);
- vsnprintf(buffer, sizeof(buffer), pattern, args);
- va_end(args);
- current_serial++;
- DLINK_FOREACH(ptr, global_serv_list.head)
- {
- target_p = ptr->data;
- /* Do not attempt to send to ourselves, or the source */
- if(IsMe(target_p) || target_p->from == source_p->from)
- continue;
- if(target_p->from->serial == current_serial)
- continue;
- if(match(mask, target_p->name))
- {
- /*
- * if we set the serial here, then we'll never do a
- * match() again, if !IsCapable()
- */
- target_p->from->serial = current_serial;
- found++;
- if(!IsCapable(target_p->from, cap))
- continue;
- sendto_anywhere(target_p, source_p, "%s", buffer);
- }
- }
- }
- /* sendto_anywhere()
- *
- * inputs - pointer to dest client
- * - pointer to from client
- * - varags
- * output - NONE
- * side effects - less efficient than sendto_remote and sendto_one
- * but useful when one does not know where target "lives"
- */
- void
- sendto_anywhere(struct Client *to, struct Client *from, const char *pattern, ...)
- {
- va_list args;
- char buffer[IRCD_BUFSIZE];
- int len;
- struct Client *send_to = (to->from != NULL ? to->from : to);
- if(IsDead(send_to))
- return;
- if(MyClient(to))
- {
- if(IsServer(from))
- {
- if(IsCapable(to, CAP_TS6) && HasID(from))
- len = ircsprintf(buffer, ":%s ", from->id);
- else
- len = ircsprintf(buffer, ":%s ", from->name);
- }
- else
- len = ircsprintf(buffer, ":%s!%s@%s ",
- from->name, from->username, from->host);
- }
- else
- len = ircsprintf(buffer, ":%s ", ID_or_name(from, send_to));
- va_start(args, pattern);
- len += send_format(&buffer[len], IRCD_BUFSIZE - len, pattern, args);
- va_end(args);
- if(MyClient(to))
- send_message(send_to, buffer, len);
- else
- send_message_remote(send_to, from, buffer, len);
- }
- /* sendto_realops_flags()
- *
- * inputs - flag types of messages to show to real opers
- * - flag indicating opers/admins
- * - var args input message
- * output - NONE
- * side effects - Send to *local* ops only but NOT +s nonopers.
- */
- void
- sendto_realops_flags(unsigned int flags, int level, const char *pattern, ...)
- {
- struct Client *client_p;
- char nbuf[IRCD_BUFSIZE];
- dlink_node *ptr;
- va_list args;
- va_start(args, pattern);
- vsnprintf(nbuf, IRCD_BUFSIZE, pattern, args);
- va_end(args);
- DLINK_FOREACH(ptr, oper_list.head)
- {
- client_p = ptr->data;
- assert(client_p->umodes & UMODE_OPER);
- /* If we're sending it to opers and theyre an admin, skip.
- * If we're sending it to admins, and theyre not, skip.
- */
- if(((level == L_ADMIN) && !IsAdmin(client_p)) ||
- ((level == L_OPER) && IsAdmin(client_p)))
- continue;
- if(client_p->umodes & flags)
- sendto_one(client_p, ":%s NOTICE %s :*** Notice -- %s",
- me.name, client_p->name, nbuf);
- }
- }
- /* sendto_wallops_flags()
- *
- * inputs - flag types of messages to show to real opers
- * - client sending request
- * - var args input message
- * output - NONE
- * side effects - Send a wallops to local opers
- */
- void
- sendto_wallops_flags(unsigned int flags, struct Client *source_p, const char *pattern, ...)
- {
- struct Client *client_p;
- dlink_node *ptr;
- va_list args;
- char buffer[IRCD_BUFSIZE];
- int len;
- if(IsClient(source_p))
- len = ircsprintf(buffer, ":%s!%s@%s WALLOPS :",
- source_p->name, source_p->username, source_p->host);
- else
- len = ircsprintf(buffer, ":%s WALLOPS :", source_p->name);
- va_start(args, pattern);
- len += send_format(&buffer[len], IRCD_BUFSIZE - len, pattern, args);
- va_end(args);
- DLINK_FOREACH(ptr, oper_list.head)
- {
- client_p = ptr->data;
- assert(client_p->umodes & UMODE_OPER);
- if((client_p->umodes & flags) && !IsDefunct(client_p))
- send_message(client_p, buffer, len);
- }
- }
- /* ts_warn()
- *
- * inputs - var args message
- * output - NONE
- * side effects - Call sendto_realops_flags, with some flood checking
- * (at most 5 warnings every 5 seconds)
- */
- void
- ts_warn(const char *pattern, ...)
- {
- va_list args;
- char buffer[LOG_BUFSIZE];
- static time_t last = 0;
- static int warnings = 0;
- /*
- ** 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
- */
- if(CurrentTime - last < 5)
- {
- if(++warnings > 5)
- return;
- }
- else
- {
- last = CurrentTime;
- warnings = 0;
- }
- va_start(args, pattern);
- vsprintf_irc(buffer, pattern, args);
- va_end(args);
- sendto_realops_flags(UMODE_ALL, L_ALL, "%s", buffer);
- ilog(L_CRIT, "%s", buffer);
- }
- /* kill_client()
- *
- * inputs - client to send kill towards
- * - pointer to client to kill
- * - reason for kill
- * output - NONE
- * side effects - NONE
- */
- void
- kill_client(struct Client *client_p, struct Client *diedie, const char *pattern, ...)
- {
- va_list args;
- char buffer[IRCD_BUFSIZE];
- int len;
- if(client_p->from != NULL)
- client_p = client_p->from;
- if(IsDead(client_p))
- return;
- len = ircsprintf(buffer, ":%s KILL %s :", ID_or_name(&me, client_p->from),
- ID_or_name(diedie, client_p));
- va_start(args, pattern);
- len += send_format(&buffer[len], IRCD_BUFSIZE - len, pattern, args);
- va_end(args);
- send_message(client_p, buffer, len);
- }
- /* kill_client_ll_serv_butone()
- *
- * inputs - pointer to client to not send to
- * - pointer to client to kill
- * output - NONE
- * side effects - Send a KILL for the given client
- * message to all connected servers
- * except the client 'one'. Also deal with
- * client being unknown to leaf, as in lazylink...
- */
- void
- kill_client_ll_serv_butone(struct Client *one, struct Client *source_p, const char *pattern, ...)
- {
- va_list args;
- int have_uid = 0;
- struct Client *client_p;
- dlink_node *ptr;
- char buf_uid[IRCD_BUFSIZE], buf_nick[IRCD_BUFSIZE];
- int len_uid = 0, len_nick;
- if(HasID(source_p) && (me.id[0] != '\0'))
- {
- have_uid = 1;
- va_start(args, pattern);
- len_uid = ircsprintf(buf_uid, ":%s KILL %s :", me.id, ID(source_p));
- len_uid += send_format(&buf_uid[len_uid], IRCD_BUFSIZE - len_uid, pattern, args);
- va_end(args);
- }
- va_start(args, pattern);
- len_nick = ircsprintf(buf_nick, ":%s KILL %s :", me.name, source_p->name);
- len_nick += send_format(&buf_nick[len_nick], IRCD_BUFSIZE - len_nick, pattern, args);
- va_end(args);
- DLINK_FOREACH(ptr, serv_list.head)
- {
- client_p = ptr->data;
- if(one != NULL && (client_p == one->from))
- continue;
- if(IsDefunct(client_p))
- continue;
- /* XXX perhaps IsCapable should test for localClient itself ? -db */
- if(client_p->localClient == NULL || !IsCapable(client_p, CAP_LL) ||
- !ServerInfo.hub ||
- (source_p->lazyLinkClientExists & client_p->localClient->serverMask))
- {
- if(have_uid && IsCapable(client_p, CAP_TS6))
- send_message(client_p, buf_uid, len_uid);
- else
- send_message(client_p, buf_nick, len_nick);
- }
- }
- }