solanum-vs-hackint-and-char.../ircd/send.c
Simon Arlott 2d8d5b058b
echo-message should work for clients that aren't in the target channel
Move the echo part to the end of the channel membership loop so that it
works even if the user isn't on the channel.
2016-11-23 21:14:44 +00:00

1449 lines
35 KiB
C

/*
* ircd-ratbox: A slightly useful ircd.
* send.c: Functions for sending messages.
*
* Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
* Copyright (C) 1996-2002 Hybrid Development Team
* Copyright (C) 2002-2005 ircd-ratbox development team
*
* 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
*/
#include "stdinc.h"
#include "send.h"
#include "channel.h"
#include "class.h"
#include "client.h"
#include "match.h"
#include "ircd.h"
#include "numeric.h"
#include "s_serv.h"
#include "s_conf.h"
#include "s_newconf.h"
#include "logger.h"
#include "hook.h"
#include "monitor.h"
#include "msgbuf.h"
/* send the message to the link the target is attached to */
#define send_linebuf(a,b) _send_linebuf((a->from ? a->from : a) ,b)
static void send_queued_write(rb_fde_t *F, void *data);
unsigned long current_serial = 0L;
struct Client *remote_rehash_oper_p;
/* send_linebuf()
*
* inputs - client to send to, linebuf to attach
* outputs -
* side effects - linebuf is attached to client
*/
static int
_send_linebuf(struct Client *to, buf_head_t *linebuf)
{
if(IsMe(to))
{
sendto_realops_snomask(SNO_GENERAL, L_ALL, "Trying to send message to myself!");
return 0;
}
if(!MyConnect(to) || IsIOError(to))
return 0;
if(rb_linebuf_len(&to->localClient->buf_sendq) > get_sendq(to))
{
if(IsServer(to))
{
sendto_realops_snomask(SNO_GENERAL, L_ALL,
"Max SendQ limit exceeded for %s: %u > %lu",
to->name,
rb_linebuf_len(&to->localClient->buf_sendq),
get_sendq(to));
ilog(L_SERVER, "Max SendQ limit exceeded for %s: %u > %lu",
log_client_name(to, SHOW_IP),
rb_linebuf_len(&to->localClient->buf_sendq),
get_sendq(to));
}
dead_link(to, 1);
return -1;
}
else
{
/* just attach the linebuf to the sendq instead of
* generating a new one
*/
rb_linebuf_attach(&to->localClient->buf_sendq, linebuf);
}
/*
** 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->sendM += 1;
me.localClient->sendM += 1;
if(rb_linebuf_len(&to->localClient->buf_sendq) > 0)
send_queued(to);
return 0;
}
/* send_linebuf_remote()
*
* inputs - client to attach to, sender, linebuf
* outputs -
* side effects - client has linebuf attached
*/
static void
send_linebuf_remote(struct Client *to, struct Client *from, buf_head_t *linebuf)
{
if(to->from)
to = to->from;
/* we assume the caller has already tested for fake direction */
_send_linebuf(to, linebuf);
}
/* send_queued_write()
*
* inputs - fd to have queue sent, client we're sending to
* outputs - contents of queue
* side effects - write is rescheduled if queue isnt emptied
*/
void
send_queued(struct Client *to)
{
int retlen;
rb_fde_t *F = to->localClient->F;
if (!F)
return;
/* cant write anything to a dead socket. */
if(IsIOError(to))
return;
/* try to flush later when the write event resets this */
if(IsFlush(to))
return;
if(rb_linebuf_len(&to->localClient->buf_sendq))
{
while ((retlen =
rb_linebuf_flush(F, &to->localClient->buf_sendq)) > 0)
{
/* We have some data written .. update counters */
ClearFlush(to);
to->localClient->sendB += retlen;
me.localClient->sendB += retlen;
if(to->localClient->sendB > 1023)
{
to->localClient->sendK += (to->localClient->sendB >> 10);
to->localClient->sendB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */
}
else if(me.localClient->sendB > 1023)
{
me.localClient->sendK += (me.localClient->sendB >> 10);
me.localClient->sendB &= 0x03ff;
}
}
if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
{
dead_link(to, 0);
return;
}
}
if(rb_linebuf_len(&to->localClient->buf_sendq))
{
SetFlush(to);
rb_setselect(to->localClient->F, RB_SELECT_WRITE,
send_queued_write, to);
}
else
ClearFlush(to);
}
void
send_pop_queue(struct Client *to)
{
if(to->from != NULL)
to = to->from;
if(!MyConnect(to) || IsIOError(to))
return;
if(rb_linebuf_len(&to->localClient->buf_sendq) > 0)
send_queued(to);
}
/* send_queued_write()
*
* inputs - fd to have queue sent, client we're sending to
* outputs - contents of queue
* side effects - write is scheduled if queue isnt emptied
*/
static void
send_queued_write(rb_fde_t *F, void *data)
{
struct Client *to = data;
ClearFlush(to);
send_queued(to);
}
/*
* linebuf_put_msgvbuf
*
* inputs - msgbuf header, linebuf object, capability mask, pattern, arguments
* outputs - none
* side effects - the linebuf object is cleared, then populated using rb_linebuf_putmsg().
*/
static void
linebuf_put_msgvbuf(struct MsgBuf *msgbuf, buf_head_t *linebuf, unsigned int capmask, const char *pattern, va_list *va)
{
char buf[BUFSIZE];
rb_linebuf_newbuf(linebuf);
msgbuf_unparse_prefix(buf, sizeof buf, msgbuf, capmask);
rb_linebuf_putprefix(linebuf, pattern, va, buf);
}
/* linebuf_put_msgbuf
*
* inputs - msgbuf header, linebuf object, capability mask, pattern, arguments
* outputs - none
* side effects - the linebuf object is cleared, then populated using rb_linebuf_putmsg().
*/
static void
linebuf_put_msgbuf(struct MsgBuf *msgbuf, buf_head_t *linebuf, unsigned int capmask, const char *pattern, ...)
{
va_list va;
va_start(va, pattern);
linebuf_put_msgvbuf(msgbuf, linebuf, capmask, pattern, &va);
va_end(va);
}
/* build_msgbuf_from
*
* inputs - msgbuf object, client the message is from
* outputs - none
* side effects - a msgbuf object is populated with an origin and relevant tags
* notes - to make this reentrant, find a solution for `buf` below
*/
static void
build_msgbuf_from(struct MsgBuf *msgbuf, struct Client *from, const char *cmd)
{
static char buf[BUFSIZE];
hook_data hdata;
msgbuf_init(msgbuf);
msgbuf->origin = buf;
msgbuf->cmd = cmd;
if (from != NULL && IsPerson(from))
snprintf(buf, sizeof buf, "%s!%s@%s", from->name, from->username, from->host);
else if (from != NULL)
rb_strlcpy(buf, from->name, sizeof buf);
else
rb_strlcpy(buf, me.name, sizeof buf);
hdata.client = from;
hdata.arg1 = msgbuf;
call_hook(h_outbound_msgbuf, &hdata);
}
/* sendto_one()
*
* inputs - client to send to, va_args
* outputs - client has message put into its queue
* side effects -
*/
void
sendto_one(struct Client *target_p, const char *pattern, ...)
{
va_list args;
buf_head_t linebuf;
/* send remote if to->from non NULL */
if(target_p->from != NULL)
target_p = target_p->from;
if(IsIOError(target_p))
return;
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
va_end(args);
_send_linebuf(target_p, &linebuf);
rb_linebuf_donebuf(&linebuf);
}
/* sendto_one_prefix()
*
* inputs - client to send to, va_args
* outputs - client has message put into its queue
* side effects - source(us)/target is chosen based on TS6 capability
*/
void
sendto_one_prefix(struct Client *target_p, struct Client *source_p,
const char *command, const char *pattern, ...)
{
struct Client *dest_p;
va_list args;
buf_head_t linebuf;
/* send remote if to->from non NULL */
if(target_p->from != NULL)
dest_p = target_p->from;
else
dest_p = target_p;
if(IsIOError(dest_p))
return;
if(IsMe(dest_p))
{
sendto_realops_snomask(SNO_GENERAL, L_ALL, "Trying to send to myself!");
return;
}
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args,
":%s %s %s ",
get_id(source_p, target_p),
command, get_id(target_p, target_p));
va_end(args);
_send_linebuf(dest_p, &linebuf);
rb_linebuf_donebuf(&linebuf);
}
/* sendto_one_notice()
*
* inputs - client to send to, va_args
* outputs - client has a NOTICE put into its queue
* side effects - source(us)/target is chosen based on TS6 capability
*/
void
sendto_one_notice(struct Client *target_p, const char *pattern, ...)
{
struct Client *dest_p;
va_list args;
buf_head_t linebuf;
char *to;
/* send remote if to->from non NULL */
if(target_p->from != NULL)
dest_p = target_p->from;
else
dest_p = target_p;
if(IsIOError(dest_p))
return;
if(IsMe(dest_p))
{
sendto_realops_snomask(SNO_GENERAL, L_ALL, "Trying to send to myself!");
return;
}
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args,
":%s NOTICE %s ",
get_id(&me, target_p), *(to = get_id(target_p, target_p)) != '\0' ? to : "*");
va_end(args);
_send_linebuf(dest_p, &linebuf);
rb_linebuf_donebuf(&linebuf);
}
/* sendto_one_numeric()
*
* inputs - client to send to, va_args
* outputs - client has message put into its queue
* side effects - source/target is chosen based on TS6 capability
*/
void
sendto_one_numeric(struct Client *target_p, int numeric, const char *pattern, ...)
{
struct Client *dest_p;
va_list args;
buf_head_t linebuf;
char *to;
/* send remote if to->from non NULL */
if(target_p->from != NULL)
dest_p = target_p->from;
else
dest_p = target_p;
if(IsIOError(dest_p))
return;
if(IsMe(dest_p))
{
sendto_realops_snomask(SNO_GENERAL, L_ALL, "Trying to send to myself!");
return;
}
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args,
":%s %03d %s ",
get_id(&me, target_p),
numeric, *(to = get_id(target_p, target_p)) != '\0' ? to : "*");
va_end(args);
_send_linebuf(dest_p, &linebuf);
rb_linebuf_donebuf(&linebuf);
}
/*
* sendto_server
*
* inputs - pointer to client to NOT send to
* - caps or'd together which must ALL be present
* - caps or'd together which must ALL NOT be present
* - 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'.
*
* This function was written in an attempt to merge together the other
* billion sendto_*serv*() functions, which sprung up with capabs, uids etc
* -davidt
*/
void
sendto_server(struct Client *one, struct Channel *chptr, unsigned long caps,
unsigned long nocaps, const char *format, ...)
{
va_list args;
struct Client *target_p;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
buf_head_t linebuf;
/* noone to send to.. */
if(rb_dlink_list_length(&serv_list) == 0)
return;
if(chptr != NULL && *chptr->chname != '#')
return;
rb_linebuf_newbuf(&linebuf);
va_start(args, format);
rb_linebuf_putmsg(&linebuf, format, &args, NULL);
va_end(args);
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, serv_list.head)
{
target_p = ptr->data;
/* check against 'one' */
if(one != NULL && (target_p == one->from))
continue;
/* check we have required capabs */
if(!IsCapable(target_p, caps))
continue;
/* check we don't have any forbidden capabs */
if(!NotCapable(target_p, nocaps))
continue;
_send_linebuf(target_p, &linebuf);
}
rb_linebuf_donebuf(&linebuf);
}
/* sendto_channel_flags()
*
* inputs - server not to send to, flags needed, source, channel, va_args
* outputs - message is sent to channel members
* side effects -
*/
void
sendto_channel_flags(struct Client *one, int type, struct Client *source_p,
struct Channel *chptr, const char *pattern, ...)
{
char buf[BUFSIZE];
va_list args;
buf_head_t rb_linebuf_local;
buf_head_t rb_linebuf_id;
struct Client *target_p;
struct membership *msptr;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
int current_capmask = 0;
struct MsgBuf msgbuf;
rb_linebuf_newbuf(&rb_linebuf_local);
rb_linebuf_newbuf(&rb_linebuf_id);
current_serial++;
build_msgbuf_from(&msgbuf, source_p, NULL);
va_start(args, pattern);
vsnprintf(buf, sizeof buf, pattern, args);
va_end(args);
linebuf_put_msgbuf(&msgbuf, &rb_linebuf_local, NOCAPS, "%s", buf);
rb_linebuf_putmsg(&rb_linebuf_id, NULL, NULL, ":%s %s", use_id(source_p), buf);
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->members.head)
{
msptr = ptr->data;
target_p = msptr->client_p;
if(!MyClient(source_p) && (IsIOError(target_p->from) || target_p->from == one))
continue;
if(MyClient(source_p) && target_p == one)
continue;
if(type && ((msptr->flags & type) == 0))
continue;
if(IsDeaf(target_p))
continue;
if(!MyClient(target_p))
{
/* if we've got a specific type, target must support
* CHW.. --fl
*/
if(type && NotCapable(target_p->from, CAP_CHW))
continue;
if(target_p->from->serial != current_serial)
{
send_linebuf_remote(target_p, source_p, &rb_linebuf_id);
target_p->from->serial = current_serial;
}
}
else
{
if (target_p->localClient->caps != current_capmask)
{
/* reset the linebuf */
rb_linebuf_donebuf(&rb_linebuf_local);
rb_linebuf_newbuf(&rb_linebuf_local);
/* render the new linebuf and attach it */
linebuf_put_msgbuf(&msgbuf, &rb_linebuf_local, target_p->localClient->caps, "%s", buf);
current_capmask = target_p->localClient->caps;
}
_send_linebuf(target_p, &rb_linebuf_local);
}
}
/* source client may not be on the channel, send echo separately */
if(MyClient(source_p) && IsCapable(source_p, CLICAP_ECHO_MESSAGE))
{
target_p = one;
if (target_p->localClient->caps != current_capmask)
{
/* reset the linebuf */
rb_linebuf_donebuf(&rb_linebuf_local);
rb_linebuf_newbuf(&rb_linebuf_local);
/* render the new linebuf and attach it */
linebuf_put_msgbuf(&msgbuf, &rb_linebuf_local, target_p->localClient->caps, "%s", buf);
current_capmask = target_p->localClient->caps;
}
_send_linebuf(target_p, &rb_linebuf_local);
}
rb_linebuf_donebuf(&rb_linebuf_local);
rb_linebuf_donebuf(&rb_linebuf_id);
}
/* sendto_channel_flags()
*
* inputs - server not to send to, flags needed, source, channel, va_args
* outputs - message is sent to channel members
* side effects -
*/
void
sendto_channel_opmod(struct Client *one, struct Client *source_p,
struct Channel *chptr, const char *command,
const char *text)
{
buf_head_t rb_linebuf_local;
buf_head_t rb_linebuf_old;
buf_head_t rb_linebuf_new;
struct Client *target_p;
struct membership *msptr;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
rb_linebuf_newbuf(&rb_linebuf_local);
rb_linebuf_newbuf(&rb_linebuf_old);
rb_linebuf_newbuf(&rb_linebuf_new);
current_serial++;
if(IsServer(source_p))
rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL,
":%s %s %s :%s",
source_p->name, command, chptr->chname, text);
else
rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL,
":%s!%s@%s %s %s :%s",
source_p->name, source_p->username,
source_p->host, command, chptr->chname, text);
if (chptr->mode.mode & MODE_MODERATED)
rb_linebuf_putmsg(&rb_linebuf_old, NULL, NULL,
":%s %s %s :%s",
use_id(source_p), command, chptr->chname, text);
else
rb_linebuf_putmsg(&rb_linebuf_old, NULL, NULL,
":%s NOTICE @%s :<%s:%s> %s",
use_id(source_p->servptr), chptr->chname,
source_p->name, chptr->chname, text);
rb_linebuf_putmsg(&rb_linebuf_new, NULL, NULL,
":%s %s =%s :%s",
use_id(source_p), command, chptr->chname, text);
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->members.head)
{
msptr = ptr->data;
target_p = msptr->client_p;
if(!MyClient(source_p) && (IsIOError(target_p->from) || target_p->from == one))
continue;
if(MyClient(source_p) && target_p == one)
continue;
if((msptr->flags & CHFL_CHANOP) == 0)
continue;
if(IsDeaf(target_p))
continue;
if(!MyClient(target_p))
{
/* if we've got a specific type, target must support
* CHW.. --fl
*/
if(NotCapable(target_p->from, CAP_CHW))
continue;
if(target_p->from->serial != current_serial)
{
if (IsCapable(target_p->from, CAP_EOPMOD))
send_linebuf_remote(target_p, source_p, &rb_linebuf_new);
else
send_linebuf_remote(target_p, source_p, &rb_linebuf_old);
target_p->from->serial = current_serial;
}
}
else
_send_linebuf(target_p, &rb_linebuf_local);
}
/* source client may not be on the channel, send echo separately */
if(MyClient(source_p) && IsCapable(source_p, CLICAP_ECHO_MESSAGE))
{
target_p = one;
_send_linebuf(target_p, &rb_linebuf_local);
}
rb_linebuf_donebuf(&rb_linebuf_local);
rb_linebuf_donebuf(&rb_linebuf_old);
rb_linebuf_donebuf(&rb_linebuf_new);
}
/* sendto_channel_local()
*
* inputs - flags to send to, channel to send to, va_args
* outputs - message to local channel members
* side effects -
*/
void
sendto_channel_local(int type, struct Channel *chptr, const char *pattern, ...)
{
va_list args;
buf_head_t linebuf;
struct membership *msptr;
struct Client *target_p;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
va_end(args);
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->locmembers.head)
{
msptr = ptr->data;
target_p = msptr->client_p;
if(IsIOError(target_p))
continue;
if(type == ONLY_OPERS)
{
if (!IsOper(target_p))
continue;
}
else if(type && ((msptr->flags & type) == 0))
continue;
_send_linebuf(target_p, &linebuf);
}
rb_linebuf_donebuf(&linebuf);
}
/*
* _sendto_channel_local_with_capability_butone()
*
* Shared implementation of sendto_channel_local_with_capability and sendto_channel_local_with_capability_butone
*/
static void
_sendto_channel_local_with_capability_butone(struct Client *one, int type, int caps, int negcaps, struct Channel *chptr,
const char *pattern, va_list * args)
{
buf_head_t linebuf;
struct membership *msptr;
struct Client *target_p;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
rb_linebuf_newbuf(&linebuf);
rb_linebuf_putmsg(&linebuf, pattern, args, NULL);
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->locmembers.head)
{
msptr = ptr->data;
target_p = msptr->client_p;
if (target_p == one)
continue;
if(IsIOError(target_p) ||
!IsCapable(target_p, caps) ||
!NotCapable(target_p, negcaps))
continue;
if(type && ((msptr->flags & type) == 0))
continue;
_send_linebuf(target_p, &linebuf);
}
rb_linebuf_donebuf(&linebuf);
}
/* sendto_channel_local_with_capability()
*
* inputs - flags to send to, caps, negate caps, channel to send to, va_args
* outputs - message to local channel members
* side effects -
*/
void
sendto_channel_local_with_capability(int type, int caps, int negcaps, struct Channel *chptr, const char *pattern, ...)
{
va_list args;
va_start(args, pattern);
_sendto_channel_local_with_capability_butone(NULL, type, caps, negcaps, chptr, pattern, &args);
va_end(args);
}
/* sendto_channel_local_with_capability()
*
* inputs - flags to send to, caps, negate caps, channel to send to, va_args
* outputs - message to local channel members
* side effects -
*/
void
sendto_channel_local_with_capability_butone(struct Client *one, int type, int caps, int negcaps, struct Channel *chptr,
const char *pattern, ...)
{
va_list args;
va_start(args, pattern);
_sendto_channel_local_with_capability_butone(one, type, caps, negcaps, chptr, pattern, &args);
va_end(args);
}
/* sendto_channel_local_butone()
*
* inputs - flags to send to, channel to send to, va_args
* - user to ignore when sending
* outputs - message to local channel members
* side effects -
*/
void
sendto_channel_local_butone(struct Client *one, int type, struct Channel *chptr, const char *pattern, ...)
{
va_list args;
buf_head_t linebuf;
struct membership *msptr;
struct Client *target_p;
struct MsgBuf msgbuf;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
rb_linebuf_newbuf(&linebuf);
build_msgbuf_from(&msgbuf, one, NULL);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
va_end(args);
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->locmembers.head)
{
msptr = ptr->data;
target_p = msptr->client_p;
if(target_p == one)
continue;
if(IsIOError(target_p))
continue;
if(type && ((msptr->flags & type) == 0))
continue;
/* attach the present linebuf to the target */
_send_linebuf(target_p, &linebuf);
}
rb_linebuf_donebuf(&linebuf);
}
/*
* sendto_common_channels_local()
*
* inputs - pointer to client
* - capability mask
* - negated capability mask
* - 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 cap, int negcap, const char *pattern, ...)
{
va_list args;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
rb_dlink_node *uptr;
rb_dlink_node *next_uptr;
struct Channel *chptr;
struct Client *target_p;
struct membership *msptr;
struct membership *mscptr;
buf_head_t linebuf;
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
va_end(args);
++current_serial;
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, user->user->channel.head)
{
mscptr = ptr->data;
chptr = mscptr->chptr;
RB_DLINK_FOREACH_SAFE(uptr, next_uptr, chptr->locmembers.head)
{
msptr = uptr->data;
target_p = msptr->client_p;
if(IsIOError(target_p) ||
target_p->serial == current_serial ||
!IsCapable(target_p, cap) ||
!NotCapable(target_p, negcap))
continue;
target_p->serial = current_serial;
send_linebuf(target_p, &linebuf);
}
}
/* this can happen when the user isnt in any channels, but we still
* need to send them the data, ie a nick change
*/
if(MyConnect(user) && (user->serial != current_serial))
send_linebuf(user, &linebuf);
rb_linebuf_donebuf(&linebuf);
}
/*
* sendto_common_channels_local_butone()
*
* inputs - pointer to client
* - capability mask
* - negated capability mask
* - pattern to send
* output - NONE
* side effects - Sends a message to all people on local server who are
* in same channel with user, except for user itself.
*/
void
sendto_common_channels_local_butone(struct Client *user, int cap, int negcap, const char *pattern, ...)
{
va_list args;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
rb_dlink_node *uptr;
rb_dlink_node *next_uptr;
struct Channel *chptr;
struct Client *target_p;
struct membership *msptr;
struct membership *mscptr;
buf_head_t linebuf;
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
va_end(args);
++current_serial;
/* Skip them -- jilles */
user->serial = current_serial;
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, user->user->channel.head)
{
mscptr = ptr->data;
chptr = mscptr->chptr;
RB_DLINK_FOREACH_SAFE(uptr, next_uptr, chptr->locmembers.head)
{
msptr = uptr->data;
target_p = msptr->client_p;
if(IsIOError(target_p) ||
target_p->serial == current_serial ||
!IsCapable(target_p, cap) ||
!NotCapable(target_p, negcap))
continue;
target_p->serial = current_serial;
send_linebuf(target_p, &linebuf);
}
}
rb_linebuf_donebuf(&linebuf);
}
/* sendto_match_butone()
*
* inputs - server not to send to, source, mask, type of mask, va_args
* output -
* side effects - message is sent to matching clients
*/
void
sendto_match_butone(struct Client *one, struct Client *source_p,
const char *mask, int what, const char *pattern, ...)
{
static char buf[BUFSIZE];
va_list args;
struct Client *target_p;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
buf_head_t rb_linebuf_local;
buf_head_t rb_linebuf_id;
rb_linebuf_newbuf(&rb_linebuf_local);
rb_linebuf_newbuf(&rb_linebuf_id);
va_start(args, pattern);
vsnprintf(buf, sizeof(buf), pattern, args);
va_end(args);
if(IsServer(source_p))
rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL,
":%s %s", source_p->name, buf);
else
rb_linebuf_putmsg(&rb_linebuf_local, NULL, NULL,
":%s!%s@%s %s",
source_p->name, source_p->username,
source_p->host, buf);
rb_linebuf_putmsg(&rb_linebuf_id, NULL, NULL, ":%s %s", use_id(source_p), buf);
if(what == MATCH_HOST)
{
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, lclient_list.head)
{
target_p = ptr->data;
if(match(mask, target_p->host))
_send_linebuf(target_p, &rb_linebuf_local);
}
}
/* what = MATCH_SERVER, if it doesnt match us, just send remote */
else if(match(mask, me.name))
{
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, lclient_list.head)
{
target_p = ptr->data;
_send_linebuf(target_p, &rb_linebuf_local);
}
}
RB_DLINK_FOREACH(ptr, serv_list.head)
{
target_p = ptr->data;
if(target_p == one)
continue;
send_linebuf_remote(target_p, source_p, &rb_linebuf_id);
}
rb_linebuf_donebuf(&rb_linebuf_local);
rb_linebuf_donebuf(&rb_linebuf_id);
}
/* sendto_match_servs()
*
* inputs - source, mask to send to, caps needed, va_args
* outputs -
* side effects - message is sent to matching servers with caps.
*/
void
sendto_match_servs(struct Client *source_p, const char *mask, int cap,
int nocap, const char *pattern, ...)
{
static char buf[BUFSIZE];
va_list args;
rb_dlink_node *ptr;
struct Client *target_p;
buf_head_t rb_linebuf_id;
if(EmptyString(mask))
return;
rb_linebuf_newbuf(&rb_linebuf_id);
va_start(args, pattern);
vsnprintf(buf, sizeof(buf), pattern, args);
va_end(args);
rb_linebuf_putmsg(&rb_linebuf_id, NULL, NULL,
":%s %s", use_id(source_p), buf);
current_serial++;
RB_DLINK_FOREACH(ptr, global_serv_list.head)
{
target_p = ptr->data;
/* dont send to ourselves, or back to where it came from.. */
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;
if(cap && !IsCapable(target_p->from, cap))
continue;
if(nocap && !NotCapable(target_p->from, nocap))
continue;
_send_linebuf(target_p->from, &rb_linebuf_id);
}
}
rb_linebuf_donebuf(&rb_linebuf_id);
}
/* sendto_local_clients_with_capability()
*
* inputs - caps needed, pattern, va_args
* outputs -
* side effects - message is sent to matching local clients with caps.
*/
void
sendto_local_clients_with_capability(int cap, const char *pattern, ...)
{
va_list args;
rb_dlink_node *ptr;
struct Client *target_p;
buf_head_t linebuf;
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
va_end(args);
RB_DLINK_FOREACH(ptr, lclient_list.head)
{
target_p = ptr->data;
if(IsIOError(target_p) || !IsCapable(target_p, cap))
continue;
send_linebuf(target_p, &linebuf);
}
rb_linebuf_donebuf(&linebuf);
}
/* sendto_monitor()
*
* inputs - monitor nick to send to, format, va_args
* outputs - message to local users monitoring the given nick
* side effects -
*/
void
sendto_monitor(struct monitor *monptr, const char *pattern, ...)
{
va_list args;
buf_head_t linebuf;
struct Client *target_p;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args, NULL);
va_end(args);
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, monptr->users.head)
{
target_p = ptr->data;
if(IsIOError(target_p))
continue;
_send_linebuf(target_p, &linebuf);
}
rb_linebuf_donebuf(&linebuf);
}
/* sendto_anywhere()
*
* inputs - target, source, va_args
* outputs -
* side effects - client is sent message with correct prefix.
*/
void
sendto_anywhere(struct Client *target_p, struct Client *source_p,
const char *command, const char *pattern, ...)
{
va_list args;
buf_head_t linebuf;
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
if(MyClient(target_p))
{
if(IsServer(source_p))
rb_linebuf_putmsg(&linebuf, pattern, &args, ":%s %s %s ",
source_p->name, command,
target_p->name);
else
{
struct MsgBuf msgbuf;
build_msgbuf_from(&msgbuf, source_p, command);
msgbuf.target = target_p->name;
linebuf_put_msgvbuf(&msgbuf, &linebuf, target_p->localClient->caps, pattern, &args);
}
}
else
rb_linebuf_putmsg(&linebuf, pattern, &args, ":%s %s %s ",
get_id(source_p, target_p), command,
get_id(target_p, target_p));
va_end(args);
if(MyClient(target_p))
_send_linebuf(target_p, &linebuf);
else
send_linebuf_remote(target_p, source_p, &linebuf);
rb_linebuf_donebuf(&linebuf);
}
/* sendto_realops_snomask()
*
* inputs - snomask needed, level (opers/admin), va_args
* output -
* side effects - message is sent to opers with matching snomasks
*/
void
sendto_realops_snomask(int flags, int level, const char *pattern, ...)
{
static char buf[BUFSIZE];
char *snobuf;
struct Client *client_p;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
va_list args;
buf_head_t linebuf;
rb_linebuf_newbuf(&linebuf);
/* Be very sure not to do things like "Trying to send to myself"
* L_NETWIDE, otherwise infinite recursion may result! -- jilles */
if (level & L_NETWIDE && ConfigFileEntry.global_snotices)
{
/* rather a lot of copying around, oh well -- jilles */
va_start(args, pattern);
vsnprintf(buf, sizeof(buf), pattern, args);
va_end(args);
rb_linebuf_putmsg(&linebuf, pattern, NULL,
":%s NOTICE * :*** Notice -- %s", me.name, buf);
snobuf = construct_snobuf(flags);
if (snobuf[1] != '\0')
sendto_server(NULL, NULL, CAP_ENCAP|CAP_TS6, NOCAPS,
":%s ENCAP * SNOTE %c :%s",
me.id, snobuf[1], buf);
}
else if (remote_rehash_oper_p != NULL)
{
/* rather a lot of copying around, oh well -- jilles */
va_start(args, pattern);
vsnprintf(buf, sizeof(buf), pattern, args);
va_end(args);
rb_linebuf_putmsg(&linebuf, pattern, NULL,
":%s NOTICE * :*** Notice -- %s", me.name, buf);
sendto_one_notice(remote_rehash_oper_p, ":*** Notice -- %s", buf);
}
else
{
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args,
":%s NOTICE * :*** Notice -- ", me.name);
va_end(args);
}
level &= ~L_NETWIDE;
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, local_oper_list.head)
{
client_p = ptr->data;
/* 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) && !IsOperAdmin(client_p)) ||
((level == L_OPER) && IsOperAdmin(client_p)))
continue;
if(client_p->snomask & flags)
_send_linebuf(client_p, &linebuf);
}
rb_linebuf_donebuf(&linebuf);
}
/* sendto_realops_snomask_from()
*
* inputs - snomask needed, level (opers/admin), source server, va_args
* output -
* side effects - message is sent to opers with matching snomask
*/
void
sendto_realops_snomask_from(int flags, int level, struct Client *source_p,
const char *pattern, ...)
{
struct Client *client_p;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
va_list args;
buf_head_t linebuf;
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args,
":%s NOTICE * :*** Notice -- ", source_p->name);
va_end(args);
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, local_oper_list.head)
{
client_p = ptr->data;
/* 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) && !IsOperAdmin(client_p)) ||
((level == L_OPER) && IsOperAdmin(client_p)))
continue;
if(client_p->snomask & flags)
_send_linebuf(client_p, &linebuf);
}
rb_linebuf_donebuf(&linebuf);
}
/*
* 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(int flags, struct Client *source_p, const char *pattern, ...)
{
struct Client *client_p;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
va_list args;
buf_head_t linebuf;
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
if(IsPerson(source_p))
rb_linebuf_putmsg(&linebuf, pattern, &args,
":%s!%s@%s WALLOPS :", source_p->name,
source_p->username, source_p->host);
else
rb_linebuf_putmsg(&linebuf, pattern, &args, ":%s WALLOPS :", source_p->name);
va_end(args);
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, IsPerson(source_p) && flags == UMODE_WALLOP ? lclient_list.head : local_oper_list.head)
{
client_p = ptr->data;
if(client_p->umodes & flags)
_send_linebuf(client_p, &linebuf);
}
rb_linebuf_donebuf(&linebuf);
}
/* kill_client()
*
* input - client to send kill to, client to kill, va_args
* output -
* side effects - we issue a kill for the client
*/
void
kill_client(struct Client *target_p, struct Client *diedie, const char *pattern, ...)
{
va_list args;
buf_head_t linebuf;
rb_linebuf_newbuf(&linebuf);
va_start(args, pattern);
rb_linebuf_putmsg(&linebuf, pattern, &args, ":%s KILL %s :",
get_id(&me, target_p), get_id(diedie, target_p));
va_end(args);
send_linebuf(target_p, &linebuf);
rb_linebuf_donebuf(&linebuf);
}
/*
* kill_client_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_serv_butone(struct Client *one, struct Client *target_p, const char *pattern, ...)
{
static char buf[BUFSIZE];
va_list args;
struct Client *client_p;
rb_dlink_node *ptr;
rb_dlink_node *next_ptr;
buf_head_t rb_linebuf_id;
rb_linebuf_newbuf(&rb_linebuf_id);
va_start(args, pattern);
vsnprintf(buf, sizeof(buf), pattern, args);
va_end(args);
rb_linebuf_putmsg(&rb_linebuf_id, NULL, NULL, ":%s KILL %s :%s",
use_id(&me), use_id(target_p), buf);
RB_DLINK_FOREACH_SAFE(ptr, next_ptr, serv_list.head)
{
client_p = ptr->data;
/* ok, if the client we're supposed to not send to has an
* ID, then we still want to issue the kill there..
*/
if(one != NULL && (client_p == one->from) &&
(!has_id(client_p) || !has_id(target_p)))
continue;
_send_linebuf(client_p, &rb_linebuf_id);
}
rb_linebuf_donebuf(&rb_linebuf_id);
}